更改发送消息模式为promise

This commit is contained in:
2022-02-28 13:45:17 +08:00
parent bf94cca533
commit 39b8f158c0
9 changed files with 647 additions and 466 deletions

View File

@@ -0,0 +1,183 @@
<template xlang="wxml" minapp="mpvue">
<view>
</view>
</template>
<script>
export default {
name: 'tki-file-manager',
props: {},
data() {
return {
}
},
methods: {
_openFile() {
// #ifdef APP-PLUS
if (plus.os.name.toLowerCase() != "android") {
uni.showModal({
title: '提示',
content: '仅支持Android平台',
success: function (res) {}
});
return false
}
let that = this
// java 代码来自 http://www.cnblogs.com/panhouye/archive/2017/04/23/6751710.html
let main = plus.android.runtimeMainActivity();
let Intent = plus.android.importClass("android.content.Intent");
//
let fileIntent = new Intent(Intent.ACTION_GET_CONTENT)
//fileIntent.setType(“image/*”);//选择图片
//fileIntent.setType(“audio/*”); //选择音频
//fileIntent.setType(“video/*”); //选择视频 mp4 3gp 是android支持的视频格式
//fileIntent.setType(“video/*;image/*”);//同时选择视频和图片
fileIntent.setType("*/*"); //无类型限制
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
main.startActivityForResult(fileIntent, 1);
// 获取回调
main.onActivityResult = function (requestCode, resultCode, data) {
let Activity = plus.android.importClass("android.app.Activity");
let ContentUris = plus.android.importClass("android.content.ContentUris");
let Cursor = plus.android.importClass("android.database.Cursor");
let Uri = plus.android.importClass("android.net.Uri");
let Build = plus.android.importClass("android.os.Build");
let Environment = plus.android.importClass("android.os.Environment");
let DocumentsContract = plus.android.importClass("android.provider.DocumentsContract");
let MediaStore = plus.android.importClass("android.provider.MediaStore");
// 给系统导入 contentResolver
let contentResolver = main.getContentResolver()
plus.android.importClass(contentResolver);
// 返回路径
let path = '';
if (resultCode == Activity.RESULT_OK) {
let uri = data.getData()
if ("file" == uri.getScheme().toLowerCase()) { //使用第三方应用打开
path = uri.getPath();
return;
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { //4.4以后
path = getPath(this, uri);
} else { //4.4以下下系统调用方法
path = getRealPathFromURI(uri)
}
// 回调
that.$emit('result', path)
}
// 4.4 以上 从Uri 获取文件绝对路径
function getPath(context, uri) {
let isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
let scheme = uri.getScheme().toLowerCase()
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
let docId = DocumentsContract.getDocumentId(uri);
let split = docId.split(":");
let type = split[0];
// 如果是手机内部存储
if ("primary" == type.toLowerCase()) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
} else {
return '/storage/' + type + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
let id = DocumentsContract.getDocumentId(uri);
let split = id.split(":");
return split[1]
// console.log(id)
// let contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), id);
// return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
let docId = DocumentsContract.getDocumentId(uri);
let split = docId.split(":");
let type = split[0];
let contentUri = null;
if ("image" == type.toLowerCase()) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video" == type.toLowerCase()) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio" == type.toLowerCase()) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
let selection = "_id=?";
let selectionArgs = [split[1]];
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content" == scheme) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file" == scheme) {
return uri.getPath();
}
}
// 4.4 以下 获取 绝对路径
function getRealPathFromURI(uri) {
let res = null
let proj = [MediaStore.Images.Media.DATA]
let cursor = contentResolver.query(uri, proj, null, null, null);
if (null != cursor && cursor.moveToFirst()) { ;
let column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
cursor.close();
}
return res;
}
// 通过uri 查找出绝对路径
function getDataColumn(context, uri, selection, selectionArgs) {
let cursor = null;
let column = "_data";
let projection = [column];
// let contentResolver = context.getContentResolver()
// plus.android.importClass(contentResolver);
cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
let column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
}
function isExternalStorageDocument(uri) {
return "com.android.externalstorage.documents" == uri.getAuthority() ? true : false
}
function isDownloadsDocument(uri) {
return "com.android.providers.downloads.documents" == uri.getAuthority() ? true : false
}
function isMediaDocument(uri) {
return "com.android.providers.media.documents" == uri.getAuthority() ? true : false
}
}
// #endif
// #ifndef APP-PLUS
uni.showModal({
title: '提示',
content: '仅支持Android平台',
success: function (res) {
}
});
// #endif
},
},
onLoad() {
// plus.io.resolveLocalFileSystemURL( '/storage/emulated/0', function(fs) {
// var directoryReader = fs.createReader();
// directoryReader.readEntries(function(entries) {
// var i;
// for (i = 0; i < entries.length; i++) {
// console.log(entries[i].name);
// }
// }, function(e) {
// console.log("Read entries failed: " + e.message);
// });
// }, function(e) {
// console.log("Request file system failed: " + e.message);
// });
}
}
</script>

View File

@@ -31,7 +31,7 @@ Vue.use(router)
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.prototype.$store = store Vue.prototype.$store = store
usqlite.connect({ uni.$sql = usqlite.connect({
name: 'zh-health', // 数据库名称 name: 'zh-health', // 数据库名称
path: '_doc/health.db', // 路径 path: '_doc/health.db', // 路径
}, (err, res) => { }, (err, res) => {

View File

@@ -2,7 +2,9 @@
<view class="emoji--lay" v-show="show"> <view class="emoji--lay" v-show="show">
<scroll-view class="scroll" :scroll-y="true"> <scroll-view class="scroll" :scroll-y="true">
<view class="emoji-flex"> <view class="emoji-flex">
<view class="item" v-for="(item, index) in emojiArr" :key="index" @click="$emit('onEmoji', item)">{{item.emoji}}</view> <view class="item" v-for="(item, index) in emojiArr" :key="index" @click="$emit('onEmoji', item)">
{{item.emoji}}
</view>
</view> </view>
</scroll-view> </scroll-view>
<view class="tool"> <view class="tool">
@@ -35,12 +37,15 @@
.emoji--lay { .emoji--lay {
height: 600rpx; height: 600rpx;
position: relative; position: relative;
.scroll { .scroll {
height: 600rpx; height: 600rpx;
.emoji-flex { .emoji-flex {
padding: 15rpx 15rpx 120rpx; padding: 15rpx 15rpx 120rpx;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
.item { .item {
margin: 10rpx 15rpx; margin: 10rpx 15rpx;
font-size: 52rpx; font-size: 52rpx;
@@ -49,6 +54,7 @@
} }
} }
} }
.tool { .tool {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@@ -58,6 +64,7 @@
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
padding: 20rpx 30rpx 30rpx; padding: 20rpx 30rpx 30rpx;
.item { .item {
line-height: 70rpx; line-height: 70rpx;
border-radius: 35rpx; border-radius: 35rpx;
@@ -68,6 +75,7 @@
background: white; background: white;
border: solid 1rpx #f5f5f5; border: solid 1rpx #f5f5f5;
box-sizing: border-box; box-sizing: border-box;
&.sent { &.sent {
border-color: #34CE98; border-color: #34CE98;
background: #34CE98; background: #34CE98;

View File

@@ -25,6 +25,8 @@
<text class="text">文件</text> <text class="text">文件</text>
</view> </view>
<tki-file-manager ref="filemanager" @result="resultPath"></tki-file-manager>
<u-action-sheet :actions="callActions" cancelText="取消" @close="callShow = false" @select="singleCall" <u-action-sheet :actions="callActions" cancelText="取消" @close="callShow = false" @select="singleCall"
:show="callShow"> :show="callShow">
</u-action-sheet> </u-action-sheet>
@@ -34,11 +36,15 @@
<script> <script>
import im from '@/utils/im/index.js' import im from '@/utils/im/index.js'
import imBase from '../../mixins/imBase.js' import imBase from '../../mixins/imBase.js'
import tkiFileManager from "@/components/tki-file-manager/tki-file-manager.vue"
export default { export default {
mixins: [ mixins: [
imBase imBase
], ],
components: [
tkiFileManager
],
data() { data() {
return { return {
callActions: [{ callActions: [{
@@ -68,6 +74,11 @@
} }
}, },
methods: { methods: {
resultPath(path) {
im.sentFile(this.conversationType, this.targetId, path).then(res => {
console.log('发送文件', res);
})
},
singleCall(e) { singleCall(e) {
uni.navigateTo({ uni.navigateTo({
url: '/pages/im/private/call?targetId=' + this.targetId + '&mediaType=' + e.type + url: '/pages/im/private/call?targetId=' + this.targetId + '&mediaType=' + e.type +
@@ -81,8 +92,13 @@
count: 9, count: 9,
sourceType: ['album'], sourceType: ['album'],
success: res => { success: res => {
im.sentImage(this.conversationType, this.targetId, res.tempFilePaths[0], let funs = []
this.sender, (res) => { res.tempFilePaths.map((item, index) => {
funs[index] = im.sentImage(this.conversationType, this
.targetId, item)
})
Promise.all(funs).then(res => {
this.success() this.success()
}) })
} }
@@ -92,8 +108,8 @@
uni.chooseImage({ uni.chooseImage({
sourceType: ['camera'], sourceType: ['camera'],
success: res => { success: res => {
im.sentImage(this.conversationType, this.targetId, res.tempFilePaths[0], im.sentImage(this.conversationType, this.targetId, res.tempFilePaths[0])
this.sender, (res) => { .then((res) => {
this.success() this.success()
}) })
} }
@@ -116,10 +132,12 @@
}) })
break; break;
case 'file': case 'file':
uni.showToast({ this.$refs.filemanager._openFile()
icon: 'none',
title: '功能正在开发中' // uni.showToast({
}) // icon: 'none',
// title: '功能正在开发中'
// })
break; break;
} }
}, },

View File

@@ -1,17 +1,7 @@
<template> <template>
<view class="sent--text"> <view class="sent--text">
<input <input class="input" type="text" :auto-blur="true" :focus="focusState" v-model="inputTxt" confirm-type="send"
class="input" cursor-spacing="10" @focus="focus" @blur="blur" @confirm="sent" />
type="text"
:auto-blur="true"
:focus="focusState"
v-model="inputTxt"
confirm-type="send"
cursor-spacing="10"
@focus="focus"
@blur="blur"
@confirm="sent"
/>
</view> </view>
</template> </template>
@@ -65,7 +55,7 @@
methods: { methods: {
sent() { sent() {
if (!this.disabled) { if (!this.disabled) {
im.sentText(this.conversationType, this.targetId, this.inputTxt, this.sender, () => { im.sentText(this.conversationType, this.targetId, this.inputTxt).then(() => {
RongIMLib.clearTextMessageDraft(this.conversationType, this.targetId) RongIMLib.clearTextMessageDraft(this.conversationType, this.targetId)
this.$emit('success') this.$emit('success')
this.inputTxt = '' this.inputTxt = ''
@@ -91,6 +81,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
.input { .input {
background: #F3F6FB; background: #F3F6FB;
height: 70rpx; height: 70rpx;

View File

@@ -1,6 +1,7 @@
<template> <template>
<view class="send--voice"> <view class="send--voice">
<view class="voice" hover-class="chat-hover" @touchstart="startRecord" @touchend="stopRecord" @touchmove="touchmove"> <view class="voice" hover-class="chat-hover" @touchstart="startRecord" @touchend="stopRecord"
@touchmove="touchmove">
<text class="button">按住 说话</text> <text class="button">按住 说话</text>
</view> </view>
<!-- 录音层 --> <!-- 录音层 -->
@@ -132,7 +133,7 @@
return return
} }
im.sentVoice(this.conversationType, this.targetId, this.mp3AudioSrc, (this.maxRecordTime - im.sentVoice(this.conversationType, this.targetId, this.mp3AudioSrc, (this.maxRecordTime -
this.recordTime), this.sender, () => { this.recordTime)).then(() => {
this.recordTime = this.maxRecordTime this.recordTime = this.maxRecordTime
this.mp3AudioSrc = null this.mp3AudioSrc = null
setTimeout(() => { setTimeout(() => {
@@ -227,11 +228,13 @@
margin-right: 15rpx; margin-right: 15rpx;
padding: 0 20rpx; padding: 0 20rpx;
font-weight: bold; font-weight: bold;
.button { .button {
font-size: 30rpx; font-size: 30rpx;
color: #333; color: #333;
} }
} }
.lay { .lay {
position: absolute; position: absolute;
left: 0; left: 0;
@@ -241,11 +244,13 @@
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
box-sizing: border-box; box-sizing: border-box;
.lay-header { .lay-header {
height: calc(100vh - 140px); height: calc(100vh - 140px);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
.modal { .modal {
display: flex; display: flex;
background: rgba(0, 0, 0, .6); background: rgba(0, 0, 0, .6);
@@ -257,6 +262,7 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.icon { .icon {
width: 88rpx; width: 88rpx;
height: 88rpx; height: 88rpx;
@@ -268,11 +274,13 @@
} }
} }
} }
.lay-toast { .lay-toast {
text-align: center; text-align: center;
color: rgba($color: #fff, $alpha: .8); color: rgba($color: #fff, $alpha: .8);
font-size: 28rpx; font-size: 28rpx;
} }
.lay-footer { .lay-footer {
background-color: white; background-color: white;
height: 100px; height: 100px;
@@ -286,10 +294,12 @@
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
.lay-footer-icon { .lay-footer-icon {
width: 58rpx; width: 58rpx;
height: 58rpx; height: 58rpx;
} }
.lay-footer-text { .lay-footer-text {
line-height: 40rpx; line-height: 40rpx;
color: gray; color: gray;

View File

@@ -1,6 +1,7 @@
<template> <template>
<view class="chat"> <view class="chat">
<sent-message-bar id="msgbar" :conversationType="conversationType" :targetId="targetId" @onSuccess="getNewMessage()" /> <sent-message-bar id="msgbar" :conversationType="conversationType" :targetId="targetId"
@onSuccess="getNewMessage()" />
<view class="shade" @click="hidePop" v-show="showPop"> <view class="shade" @click="hidePop" v-show="showPop">
<view class="pop" :style="popStyle" :class="{'show':showPop}"> <view class="pop" :style="popStyle" :class="{'show':showPop}">
@@ -175,12 +176,9 @@
20, 20,
true, true,
(messages) => { (messages) => {
console.log('获取消息列表', messages);
const msgs = messages.filter(item => item.receivedStatus == 0) const msgs = messages.filter(item => item.receivedStatus == 0)
console.log('未读消息', msgs);
if (msgs.length) { if (msgs.length) {
RongIMLib.sendReadReceiptResponse(3, this.targetId, msgs, (res) => { RongIMLib.sendReadReceiptResponse(3, this.targetId, msgs, (res) => {
console.error('发送群聊已读回执成功', res);
msgs.map(item => { msgs.map(item => {
RongIMLib.setMessageReceivedStatus(item.messageId, 1) RongIMLib.setMessageReceivedStatus(item.messageId, 1)
}) })

View File

@@ -80,14 +80,15 @@ const getGroupPendinglist = (targetId, callback) => {
* @param {string} content 消息内容 * @param {string} content 消息内容
* @param {function} callback 回调函数 * @param {function} callback 回调函数
*/ */
const sentText = (conversationType, targetId, content, user, callback) => { const sentText = (conversationType, targetId, content) => {
return new Promise((resolve, reject) => {
const msg = { const msg = {
conversationType: conversationType, conversationType: conversationType,
targetId: String(targetId), targetId: String(targetId),
content: { content: {
objectName: 'RC:TxtMsg', objectName: 'RC:TxtMsg',
content: content, content: content,
userInfo: user userInfo: store.getters.sender
} }
} }
@@ -101,15 +102,16 @@ const sentText = (conversationType, targetId, content, user, callback) => {
console.log('发送回执请求', res); console.log('发送回执请求', res);
}) })
} }
callback(messageId) resolve(messageId)
} else { } else {
console.log('发送失败', msg);
uni.showToast({ uni.showToast({
icon: 'none', icon: 'none',
title: '发送失败' + code title: '发送失败' + code
}) })
reject(code)
} }
}) })
})
} }
/** /**
@@ -118,113 +120,85 @@ const sentText = (conversationType, targetId, content, user, callback) => {
* @param {string} targetId 会话id * @param {string} targetId 会话id
* @param {string} voiceUrl 录音的本地路径 * @param {string} voiceUrl 录音的本地路径
* @param {integer} time 录音时长 * @param {integer} time 录音时长
* @param {function} callback 录音时长 * @param {function} user 本人信息
*/ */
const sentVoice = (conversationType, targetId, voiceUrl, time, user, callback) => { const sentVoice = (conversationType, targetId, voiceUrl, time) => {
return new Promise((resolve, reject) => {
const msg = { const msg = {
conversationType: conversationType, conversationType: conversationType,
targetId: String(targetId), targetId: String(targetId),
content: { content: {
objectName: 'RC:HQVCMsg', objectName: 'RC:HQVCMsg',
local: 'file:///' + plus.io.convertLocalFileSystemURL(voiceUrl), local: 'file://' + plus.io.convertLocalFileSystemURL(voiceUrl),
duration: time == 0 ? 1 : time, duration: time == 0 ? 1 : time,
userInfo: user userInfo: store.getters.sender
} }
} }
RongIMLib.sendMediaMessage(msg, { RongIMLib.sendMediaMessage(msg, {
success: (messageId) => { success: (messageId) => {
callback(messageId); resolve(messageId);
},
progress: (progress, messageId) => {
console.log(messageId);
},
cancel: (messageId) => {
// 发送取消回调
}, },
progress: (progress, messageId) => {},
cancel: (messageId) => {},
error: (errorCode, messageId) => { error: (errorCode, messageId) => {
console.log(errorCode, messageId); reject(errorCode);
} }
}) })
})
} }
const sentImage = (conversationType, targetId, imageUrl, user, callback) => { // 发送的图片可能会涉及到一次多张用Promise来确保前端一次性清算
const sentImage = (conversationType, targetId, imageUrl) => {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: imageUrl,
success: (imgInfo) => {
const msg = { const msg = {
conversationType: conversationType, conversationType: conversationType,
targetId: String(targetId), targetId: String(targetId),
content: { content: {
objectName: 'RC:ImgMsg', objectName: 'RC:ImgMsg',
local: 'file:///' + plus.io.convertLocalFileSystemURL(imageUrl), local: imgInfo.path,
userInfo: user userInfo: store.getters.sender
} }
} }
RongIMLib.sendMediaMessage(msg, { RongIMLib.sendMediaMessage(msg, {
success: (messageId) => { success: (messageId) => {
callback(messageId); resolve(messageId)
},
progress: (progress, messageId) => {
console.log(messageId);
},
cancel: (messageId) => {
// 发送取消回调
}, },
progress: (progress, messageId) => {},
cancel: (messageId) => {},
error: (errorCode, messageId) => { error: (errorCode, messageId) => {
console.log(errorCode, messageId); reject(errorCode)
} }
}) })
} }
})
const sentGif = (conversationType, targetId, gifUrl, time, user, callback) => {
const msg = {
conversationType: conversationType,
targetId: String(targetId),
content: {
objectName: 'RC:GIFMsg',
local: 'file:///' + plus.io.convertLocalFileSystemURL(gifUrl),
duration: time,
userInfo: user
}
}
RongIMLib.sendMediaMessage(msg, {
success: (messageId) => {
callback(messageId);
},
progress: (progress, messageId) => {
console.log(messageId);
},
cancel: (messageId) => {
// 发送取消回调
},
error: (errorCode, messageId) => {
console.log(errorCode, messageId);
}
}) })
} }
const sendFile = (conversationType, targetId, fileUrl, time, user, callback) => { const sentFile = (conversationType, targetId, fileUrl) => {
return new Promise((resolve, reject) => {
const msg = { const msg = {
conversationType: conversationType, conversationType: conversationType,
targetId: String(targetId), targetId: String(targetId),
content: { content: {
objectName: 'RC:FileMsg', objectName: 'RC:FileMsg',
local: 'file:///' + plus.io.convertLocalFileSystemURL(fileUrl), local: 'file:///' + plus.io.convertLocalFileSystemURL(fileUrl),
duration: time, userInfo: store.getters.sender
userInfo: user
} }
} }
RongIMLib.sendMediaMessage(msg, { RongIMLib.sendMediaMessage(msg, {
success: (messageId) => { success: (messageId) => {
callback(messageId); resolve(messageId)
},
progress: (progress, messageId) => {
console.log(messageId);
},
cancel: (messageId) => {
// 发送取消回调
}, },
progress: (progress, messageId) => {},
cancel: (messageId) => {},
error: (errorCode, messageId) => { error: (errorCode, messageId) => {
console.log(errorCode, messageId); reject(errorCode)
} }
}) })
})
} }
export default { export default {
@@ -234,6 +208,5 @@ export default {
sentText, sentText,
sentVoice, sentVoice,
sentImage, sentImage,
sentGif, sentFile
sendFile
} }