...
 
Commits (9)
    https://gitcode.net/dcloud/uni-im/-/commit/22d1c411d39199712ced2577493907254be0c880 更新 修复conversation_grade为100且customer_service_uids未配置时,未拦截非好友通讯 2024-11-07T18:02:46+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/5628941c82a79a9254b8a14c97fda618878b33f2 Update index.vue 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/5a7c2ff24f17b180682d22c3d94eea577f3b7f01 Update video.vue 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/0fe5bd2bd385a3e5cc9939afe21b983f1ab3d2cb 更新 新增配置check_mobile,用于配置在发起会话和申请加入群聊时,是否校验手机号码 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/7ea67d3c8824a5dcf07790a2dcf4e463c6914ffe 更新 修复部分情况下没有走__afterFind的问题 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/4fee4e75c6302861e604d7b79d80fe661827f3cf 更新 Conversation. __get 参数支持传param.id表示param.conversation_id 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/0e7827965ee22c163be263c30237da6de536a7f6 更新 修复web端在部分情况下,本地创建的单聊临时会话相关页面,打开后直接刷新会出错的问题 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/02645be07e12884763044cccb1dd716101fc1a64 更新 修复会话的最后一条消息带 action 的情况下,界面刷新/重启后会重复触发的问题 2024-11-08T20:12:37+08:00 linju linju@dcloud.io https://gitcode.net/dcloud/uni-im/-/commit/533420c903ad03ca8aea06ec9fdcc6bf5997c100 更新 优化文件消息的样式 2024-11-08T20:43:05+08:00 linju linju@dcloud.io
...@@ -95,10 +95,11 @@ ...@@ -95,10 +95,11 @@
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
.file-msg-info { .file-msg-info {
flex: 1;
flex-direction: column; flex-direction: column;
justify-content: space-around; justify-content: space-around;
.name { .name {
word-wrap: break-word; word-break: break-all;
font-size: 16px; font-size: 16px;
} }
.size { .size {
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
qiniu: 'vframe/jpg/offset/0/w/200' qiniu: 'vframe/jpg/offset/0/w/200'
} }
this.videoPoster += paramObj[config.cloudFile.provider] this.videoPoster += paramObj[config.cloudFile.provider]
console.log('this.videoPoster',this.videoPoster); // console.log('this.videoPoster',this.videoPoster);
} }
}, },
deep: true, deep: true,
......
...@@ -412,6 +412,18 @@ ...@@ -412,6 +412,18 @@
this.$refs["chat-input"]?.focus() this.$refs["chat-input"]?.focus()
}, 100) }, 100)
} }
const {is_temp,friend_uid} = this.conversation
if(is_temp && friend_uid){
// 为了避免在web端刷新页面之后,本地单聊临时会话丢失,将地址栏中的会话id参数替换为好友的uid,以此来确保在刷新页面之后,本地能够再次创建临时会话。
const {route:path,options} = getCurrentPages().pop()
delete options.conversation_id
options.user_id = friend_uid
let paramSrt
for (const key in options) {
paramSrt = `${key}=${options[key]}`
}
history.replaceState(null, '', `/#/${path}?${paramSrt}`)
}
// #endif // #endif
}, },
onclickMsgList(){ onclickMsgList(){
......
...@@ -97,23 +97,9 @@ export default { ...@@ -97,23 +97,9 @@ export default {
methods: { methods: {
async load(options) { async load(options) {
console.log('options',options); console.log('options',options);
let conversation_id = options.conversation_id || options.id const {user_id,id} = options
// 如果只传了user_id,需要先获取conversation_id this.conversation = await uniIm.conversation.get(user_id ? {user_id} : id)
if(!conversation_id){ this.friend_uid = this.conversation.friend_uid
if(!options.user_id){
console.error('参数错误')
return uni.showToast({
title: '参数错误',
icon: 'none'
});
}
conversation_id = await uniIm.utils.getConversationId(options.user_id)
console.log('conversation_id',conversation_id);
}
let conversation = await uniIm.conversation.get(conversation_id)
this.conversation = conversation
this.friend_uid = conversation.friend_uid
let field = '_id,nickname,avatar_file' let field = '_id,nickname,avatar_file'
if (this.uniIDHasRole('staff')) { if (this.uniIDHasRole('staff')) {
field += ',email' field += ',email'
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
ref="uni-im-filtered-conversation-list" ref="uni-im-filtered-conversation-list"
id="conversation-list-box" id="conversation-list-box"
:keyword="keyword" :keyword="keyword"
@to-chat="toChat($event)" @to-chat="toChat"
@to-chat-filtered="toChatFiltered($event)" @to-chat-filtered="toChatFiltered($event)"
></uni-im-filtered-conversation-list> ></uni-im-filtered-conversation-list>
</template> </template>
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
<uni-icons @click="showChatInfo" class="more" type="more-filled" size="20"></uni-icons> <uni-icons @click="showChatInfo" class="more" type="more-filled" size="20"></uni-icons>
</view> </view>
<view class="content"> <view class="content">
<chat-filtered v-if="filteredConversationId" ref="chat-filtered" @to-chat="toChat($event)"/> <chat-filtered v-if="filteredConversationId" ref="chat-filtered" @to-chat="toChat"/>
<chat-view v-else ref="chat-view"></chat-view> <chat-view v-else ref="chat-view"></chat-view>
<view v-if="chatInfoIsShow" class="chatInfoBox" @click.stop="chatInfoIsShow = false"> <view v-if="chatInfoIsShow" class="chatInfoBox" @click.stop="chatInfoIsShow = false">
<view @click.stop class="group-info-parent"> <view @click.stop class="group-info-parent">
...@@ -473,35 +473,19 @@ ...@@ -473,35 +473,19 @@
// goods, // goods,
// user_id // user_id
// }); // });
if(user_id || conversation_id){
this.toChat(conversation_id || {user_id})
}
// 如果初始化时,指定了要访问的会话。将指定要访问的会话排序位置置顶,方便看到
setTimeout(()=> this.currentConversation.customIndex = Date.now(), 0);
// 如果列表小于30个会话,尝试着从云端拉取一次 // 如果列表小于30个会话,尝试着从云端拉取一次
if( this.conversationList.length < 30 ){ if( this.conversationList.length < 30 ){
await this.$nextTick() await this.$nextTick()
await this.$refs['uni-im-conversation-list'].loadMore() await this.$refs['uni-im-conversation-list'].loadMore()
}else{ }else{
console.log('会话列表已满一页,需要用户自己滚动到底,再拉取更多'); console.log('会话列表已满一页,需要用户自己滚动到底,再拉取更多');
} }
// console.log('this.conversationList.length',this.conversationList.length);
if (conversation_id) {
console.log('conversation_id', conversation_id);
this.toChat(conversation_id)
} else if (user_id) {
//创建会话
const currentConversation = await uniIm.conversation.get({
friend_uid: user_id
})
// console.log('currentConversation', currentConversation);
// 当前用户给对方发个消息
this.toChat(currentConversation.id)
}
if(user_id){
// 如果初始化时,指定了要访问的user会话。将指定要访问的会话排序位置置顶,方便看到
// 场景:插件市场,点击联系作者。自动将此会话放到首个
setTimeout(()=> {
this.currentConversation.customIndex = Date.now()
}, 0);
}
// 传递参数goods(对象格式,包含:商品名称name,链接url。自动设置对话框默认内容 // 传递参数goods(对象格式,包含:商品名称name,链接url。自动设置对话框默认内容
if (this.isWidescreen && goods) { if (this.isWidescreen && goods) {
...@@ -527,7 +511,7 @@ ...@@ -527,7 +511,7 @@
if(joinGroup){ if(joinGroup){
// #ifdef H5 // #ifdef H5
//删除URL后面的参数(主要是删除joinGroup=xxx),且不刷新页面 //删除URL后面的参数(主要是删除joinGroup=xxx),且不刷新页面
history.pushState({}, '', '/#/'); history.replaceState(null, '', '/#/')
// #endif // #endif
this.joinGroup(joinGroup) this.joinGroup(joinGroup)
}; };
...@@ -598,45 +582,26 @@ ...@@ -598,45 +582,26 @@
}) })
} }
}, },
async toChat(param) { async toChat(param) {
this.chatInfoIsShow = false; this.chatInfoIsShow = false;
this.keyword = '' this.keyword = ''
this.filteredConversationId = false this.filteredConversationId = false
const {id} = await uniIm.conversation.get(param)
const conversation_id = await getConversationId(param) uniIm.currentConversationId = id
// console.log('toChat', conversation_id)
uniIm.currentConversationId = conversation_id
if (this.isWidescreen) { // 若为宽屏,则切换右侧的组件 if (this.isWidescreen) { // 若为宽屏,则切换右侧的组件
this.$nextTick(() => { this.$nextTick(() => {
let chatViewRef = this.$refs['chat-view'] let chatViewRef = this.$refs['chat-view']
if (chatViewRef) { if (chatViewRef) {
chatViewRef.load(conversation_id) chatViewRef.load(id)
} }
}) })
} else { // 若为窄屏,则打开新窗体 } else { // 若为窄屏,则打开新窗体
// param 转成字符串
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-im/pages/chat/chat?conversation_id=' + conversation_id, url: '/uni_modules/uni-im/pages/chat/chat?conversation_id=' + id,
animationDuration: 300 animationDuration: 300
}) })
} }
async function getConversationId(param){
if (typeof param == 'string') {
return param
} else {
if (param.conversation_id) {
return param.conversation_id
} else if (param.group_id) {
return 'group_' + param.group_id
} else if (param.user_id || param.friend_uid) {
// 获取会话id(如果此单聊会话未创建会自动创建)
return (await uniIm.conversation.get(param)).id
} else {
throw new Error("toChat param is error")
}
}
}
}, },
showChatInfo() { showChatInfo() {
this.chatInfoIsShow = !this.chatInfoIsShow this.chatInfoIsShow = !this.chatInfoIsShow
......
...@@ -75,21 +75,14 @@ import uniIm from '@/uni_modules/uni-im/sdk/index.js'; ...@@ -75,21 +75,14 @@ import uniIm from '@/uni_modules/uni-im/sdk/index.js';
'more'; // oading 的状态,可选值:more(loading前)、loading(loading中)、noMore(没有更多了) 'more'; // oading 的状态,可选值:more(loading前)、loading(loading中)、noMore(没有更多了)
}, },
async toChat(user_id) { async toChat(user_id) {
//拿到会话(如果没有自动创建)
const currentConversation = await uniIm.conversation.get({
friend_uid:user_id
});
console.log('currentConversation', currentConversation);
// 当前用户给对方发个消息 // 当前用户给对方发个消息
if (this.isWidescreen) { if (this.isWidescreen) {
//若为宽屏,则触发右侧详情页的自定义事件,通知右侧窗体刷新详情 //若为宽屏,则触发右侧详情页的自定义事件,通知右侧窗体刷新详情
uni.navigateTo({ location.href = '/#/uni_modules/uni-im/pages/index/index?user_id=' + user_id
url: '/uni_modules/uni-im/pages/index/index?conversation_id=' + currentConversation.id
});
} else { } else {
// 若为窄屏,则打开新窗体,在新窗体打开详情页面 // 若为窄屏,则打开新窗体,在新窗体打开详情页面
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-im/pages/chat/chat?conversation_id=' + currentConversation.id url: '/uni_modules/uni-im/pages/chat/chat?user_id=' + user_id
}); });
} }
}, },
......
...@@ -90,49 +90,25 @@ export default class CloudData { ...@@ -90,49 +90,25 @@ export default class CloudData {
// console.time('find dataMap.get' + this.constructor.name) // console.time('find dataMap.get' + this.constructor.name)
res = this.dataMap.get(mapKey) res = this.dataMap.get(mapKey)
// console.timeEnd('find dataMap.get' + this.constructor.name) // console.timeEnd('find dataMap.get' + this.constructor.name)
return res
} else { } else {
console.log('注意:此次查找未走索引',param,this.indexKey,this.constructor.name) console.log('注意:此次查找未走索引',param,this.indexKey,this.constructor.name)
} }
} }
console.time('find-no-index-' + this.constructor.name) if(!res) {
// 默认查找条件为_id console.time('find-no-index-' + this.constructor.name)
if(typeof param == 'string'){ // 默认查找条件为_id
res = dataList.find(item => item._id == param) if(typeof param == 'string'){
}else if(typeof param == 'object'){ res = dataList.find(item => item._id == param)
// console.log('param',param) }else if(typeof param == 'object'){
function isEq(a, b) { // console.log('param',param)
if (Array.isArray(a) || Array.isArray(b)) { res = dataList.find(item => isEq(item,param))
console.error('不支持数组比较') }else{
return a === b; throw new Error('错误的查找条件')
}
let result = true;
for (let key in b) {
const valueA = a[key];
const valueB = b[key];
if (typeof valueB === 'object'
&& valueB !== null
&& typeof valueA === 'object'
&& valueA !== null
) {
return isEq(valueA, valueB);
}
result = valueA === valueB;
if (!result) {
break;
}
}
return result;
} }
res = dataList.find(item => isEq(item,param)) console.timeEnd('find-no-index-' + this.constructor.name)
}else{
throw new Error('错误的查找条件')
}
if(this.__afterFind){
res = this.__afterFind({param,res})
} }
console.timeEnd('find-no-index-' + this.constructor.name) this.__afterFind?.({param,res})
return res return res
} }
/** @description 查询数据,先从本地查找,如果没有则从云端拉取 /** @description 查询数据,先从本地查找,如果没有则从云端拉取
...@@ -327,4 +303,28 @@ function convertObjToString(obj) { ...@@ -327,4 +303,28 @@ function convertObjToString(obj) {
} }
recursiveConvert(obj, ''); recursiveConvert(obj, '');
return result; return result;
}
function isEq(a, b) {
if (Array.isArray(a) || Array.isArray(b)) {
console.error('不支持数组比较')
return a === b;
}
let result = true;
for (let key in b) {
const valueA = a[key];
const valueB = b[key];
if (typeof valueB === 'object'
&& valueB !== null
&& typeof valueA === 'object'
&& valueA !== null
) {
return isEq(valueA, valueB);
}
result = valueA === valueB;
if (!result) {
break;
}
}
return result;
} }
\ No newline at end of file
...@@ -53,7 +53,7 @@ let $users = { ...@@ -53,7 +53,7 @@ let $users = {
} }
return Array.isArray(param) ? userInfoList : userInfoList[0] return Array.isArray(param) ? userInfoList : userInfoList[0]
}, },
getNickname(user_id){ getNickname(user_id,tmpNickname = '[昵称加载中...]'){
const nickname = $state.users[user_id]?.nickname const nickname = $state.users[user_id]?.nickname
if(nickname){ if(nickname){
return nickname return nickname
...@@ -62,7 +62,7 @@ let $users = { ...@@ -62,7 +62,7 @@ let $users = {
$users.loadTask.add(user_id) $users.loadTask.add(user_id)
$state.users[user_id] = { $state.users[user_id] = {
nickname:'[昵称加载中...]', nickname:tmpNickname,
_id:user_id, _id:user_id,
__loading:true __loading:true
} }
......
...@@ -50,6 +50,14 @@ export default class Conversation extends CloudData { ...@@ -50,6 +50,14 @@ export default class Conversation extends CloudData {
// 设置为默认按id查找会话,而不是按_id查找 // 设置为默认按id查找会话,而不是按_id查找
return {id:param} return {id:param}
}else if(typeof param === 'object' && param !== null && !Array.isArray(param)){ }else if(typeof param === 'object' && param !== null && !Array.isArray(param)){
const {user_id,friend_uid,conversation_id:id} = param
if(id){
param.id = id
delete param.conversation_id
}else if(user_id){
param.friend_uid = friend_uid || user_id
delete param.user_id
}
if('source' in param){ if('source' in param){
// 本地查找不需要source字段 // 本地查找不需要source字段
const source = param.source const source = param.source
...@@ -67,13 +75,6 @@ export default class Conversation extends CloudData { ...@@ -67,13 +75,6 @@ export default class Conversation extends CloudData {
} }
},0) },0)
} }
if('user_id' in param){
// param = {friend_uid:param.user_id}
param.friend_uid = param.user_id
delete param.user_id
}else if('conversation_id' in param){
param = {id:param.conversation_id}
}
return param return param
} }
} }
...@@ -91,7 +92,9 @@ export default class Conversation extends CloudData { ...@@ -91,7 +92,9 @@ export default class Conversation extends CloudData {
"type": param.friend_uid ? 1 : 2, "type": param.friend_uid ? 1 : 2,
"msgList": [], "msgList": [],
"update_time": Date.now(), "update_time": Date.now(),
"customSortTime": Date.now() "customSortTime": Date.now(),
// 是本地临时会话数据
"is_temp": true
} }
const conversation = this.add(conversationData) const conversation = this.add(conversationData)
$users.get(friend_uid).then(res => { $users.get(friend_uid).then(res => {
...@@ -187,7 +190,7 @@ export default class Conversation extends CloudData { ...@@ -187,7 +190,7 @@ export default class Conversation extends CloudData {
const loadMoreType = this.loadMore?.type || 'all' const loadMoreType = this.loadMore?.type || 'all'
let conversation_id = param let conversation_id = param
if (typeof param === "object"){ if (typeof param === "object"){
conversation_id = param.conversation_id conversation_id = param.id || param.conversation_id
} }
const uniImCo = uniCloud.importObject("uni-im-co",{customUI: true}) const uniImCo = uniCloud.importObject("uni-im-co",{customUI: true})
const limit = this.loadLimit const limit = this.loadLimit
......
...@@ -76,7 +76,7 @@ export default class Msg extends CloudData { ...@@ -76,7 +76,7 @@ export default class Msg extends CloudData {
// console.log('beforeAdd',data); // console.log('beforeAdd',data);
// 如果此消息,创建时间在当前设备创建此会话之前,则无需执行 // 如果此消息,创建时间在当前设备创建此会话之前,则无需执行
const conversation_time = currentConversation.update_time || currentConversation.client_create_time || 0 const conversation_time = currentConversation.update_time || currentConversation.client_create_time || 0
if (data.create_time < conversation_time) { if (data.create_time <= conversation_time) {
return //console.log('消息创建时间早于会话最新时间,不执行',data.create_time,conversation_time); return //console.log('消息创建时间早于会话最新时间,不执行',data.create_time,conversation_time);
} }
// 调用扩展程序,使扩展程序可以在消息插入之前执行一些操作 // 调用扩展程序,使扩展程序可以在消息插入之前执行一些操作
......
...@@ -5,6 +5,15 @@ const dbCmd = db.command ...@@ -5,6 +5,15 @@ const dbCmd = db.command
const $ = dbCmd.aggregate const $ = dbCmd.aggregate
const md5 = str => crypto.createHash('md5').update(str).digest('hex') const md5 = str => crypto.createHash('md5').update(str).digest('hex')
function getConfig(key) {
// 获取 uni-im 配置
const createConfig = require("uni-config-center");
const uniImConfig = createConfig({
pluginId: 'uni-im', // 插件 id
})
return uniImConfig.config(key)
}
function getConversationId({ function getConversationId({
group_id, group_id,
from_uid, from_uid,
...@@ -77,5 +86,6 @@ module.exports = { ...@@ -77,5 +86,6 @@ module.exports = {
hideUsernameStr, hideUsernameStr,
hideEmailStr, hideEmailStr,
hideMobileStr, hideMobileStr,
checkParam checkParam,
getConfig
} }
\ No newline at end of file
...@@ -290,16 +290,14 @@ async function _checkConversationGrade({ ...@@ -290,16 +290,14 @@ async function _checkConversationGrade({
}) { }) {
// 客服模式下,如果配置的客服 id。则只能向客服发起会话 // 客服模式下,如果配置的客服 id。则只能向客服发起会话
let chatToCustomerService = async () => { let chatToCustomerService = async () => {
const customer_service_uids = uniImConfig.config('customer_service_uids_uids') const customer_service_uids = uniImConfig.config('customer_service_uids') || []
if (customer_service_uids) { if (typeof customer_service_uids == 'string') {
if (typeof customer_service_uids == 'string') { customer_service_uids = [customer_service_uids]
customer_service_uids = [customer_service_uids] }
} if (
if ( !(customer_service_uids.includes(from_uid) || customer_service_uids.includes(to_uid))
!(customer_service_uids.includes(from_uid) || customer_service_uids.includes(to_uid)) ) {
) { throw new Error('非法通讯,会话双方用户 id,均不属于 uni-im-co 中配置的 customer_service_uids')
throw new Error('非法通讯,会话双方用户 id,均不属于 uni-im-co 中配置的 customer_service_uids')
}
} }
return true return true
} }
...@@ -441,7 +439,9 @@ async function _createOrUpdateConversation(conversation_id, msgData, chat_source ...@@ -441,7 +439,9 @@ async function _createOrUpdateConversation(conversation_id, msgData, chat_source
// 不存在,需要先创建会话记录 // 不存在,需要先创建会话记录
else if (!senderConversation) { else if (!senderConversation) {
// 除了云函数之间(包括触发器)调用 和 特殊角色用户,需验证是否绑定了手机号码 // 除了云函数之间(包括触发器)调用 和 特殊角色用户,需验证是否绑定了手机号码
const check_mobile = uniImConfig.config('check_mobile')
if ( if (
check_mobile &&
this.getClientInfo().source != 'function' && this.getClientInfo().source != 'function' &&
!this.current_user_role.includes('uni-im-admin') && !this.current_user_role.includes('uni-im-admin') &&
!this.current_user_role.includes('staff') !this.current_user_role.includes('staff')
......
// schema扩展相关文档请参阅:https://uniapp.dcloud.net.cn/uniCloud/jql-schema-ext.html // schema扩展相关文档请参阅:https://uniapp.dcloud.net.cn/uniCloud/jql-schema-ext.html
const db = uniCloud.database() const db = uniCloud.database()
let t = Date.now() + parseInt(Math.random() * 999999) const utils = require('uni-im-utils')
module.exports = { module.exports = {
trigger: { trigger: {
async beforeCreate({ async beforeCreate({
...@@ -56,8 +55,8 @@ module.exports = { ...@@ -56,8 +55,8 @@ module.exports = {
throw new Error('你已被拉黑,不能加入此群') throw new Error('你已被拉黑,不能加入此群')
} }
// 4. 判断用户是否已经绑定手机号,系统管理员除外 // 4. 判断用户是否已经绑定手机号,系统管理员除外
console.log('userInfo',userInfo); const check_mobile = utils.getConfig('check_mobile')
if(!userInfo.role.includes('uni-im-admin') && !userInfo.role.includes('staff')){ if(check_mobile && !userInfo.role.includes('uni-im-admin') && !userInfo.role.includes('staff')){
let res3 = await db.collection('uni-id-users').where({_id:userInfo.uid}).get() let res3 = await db.collection('uni-id-users').where({_id:userInfo.uid}).get()
if(!res3.data[0].mobile_confirmed){ if(!res3.data[0].mobile_confirmed){
throw new Error('账号未绑定手机号无法加入群聊。请完成绑定后退出本系统并重新登录,再尝试加入群聊。') throw new Error('账号未绑定手机号无法加入群聊。请完成绑定后退出本系统并重新登录,再尝试加入群聊。')
......