This article is based on web platform, some experiences and lessons of tencent IM docking with no experience in IM IM project for a friend, or just contact to see so many optional platform, so rich interface and seemingly so big project, your heart may be mess, but, when you see this article, you should not panic, Because there are a lot of problems with the basic process of running through the whole demo on the Web side, without further ado, continue to read.
Tencent IM provides online demo and local demo. Online demo can view the most complete functions of official cases. Local demo is used for local development and debugging. If is the Web find the H5 (pass) platform/dist/debug/GenerateTestUserSig js directory, change SDKAPPID and SECRETKEY term in the file to your own, in the project directory, the installation will depend on the NPM install, NPM start is not recommended to go to http://localhost:8080, but to open the index.html file in the dist directory of the project. In this case, you can use user0-user29 to log in to any directory. Log in to one more account, and the two accounts will be able to communicate instantly, so the official demo is running locally. So, how do we integrate the SDK into our own Web projects?
Currently, many front-end Web projects use the MV* framework, such as the official Demo using Vue+ElementUI technology, so install the SDK dependencies first
// IM Web SDK NPM install tim-js-sdk --save // COS SDK NPM install cos-js-sdK-v5 --saveCopy the code
To achieve modularity, we created a tim.js file as a separate Tim module, imported the corresponding package, and exported Tim
import TIM from 'tim-js-sdk';
import COS from "cos-js-sdk-v5";
let options = {
SDKAppID: 0 // Replace 0 with the SDKAppID of your IM application
};
// To create an SDK instance, the 'tim.create ()' method only returns the same instance for the same 'SDKAppID'
let tim = TIM.create(options); // SDK instances are usually represented by Tim
// Set the SDK log output level. For details, see the description of the setLogLevel interface
tim.setLogLevel(0); // This parameter is a common level with a large amount of logs. Therefore, this parameter is recommended for access
// tim.setLogLevel(1); // Release level, SDK output key information, recommended for production environment
// Register the COS SDK plugin
tim.registerPlugin({'cos-js-sdk': COS});
export default tim
Copy the code
In order to log in to the account locally, you need to use the client to calculate the UserSig to generate the signature, and then add the userID, you can log in to the IM system. Generatetestusersig.js and lib-generate-test-usersig.min.js are used to generate the signature. Generatetestusersig.js needs to be modified due to modular development
// First import lib-generate-test-usersig.min.js
import LibGenerateTestUserSig from './lib-generate-test-usersig.min'
/ /...
/ / modify lib calls the method, the official case is inject new window in the window. The LibGenerateTestUserSig (...).
var generator = new LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
/ /...
/ / export
export {
genTestUserSig
}
Copy the code
For future use, we can inject Tim globally in Windows and Vue
//main.js
import tim from './tim'
import TIM from 'tim-js-sdk'
window.tim = tim
window.TIM = TIM
Vue.prototype.tim = tim
Vue.prototype.TIM = TIM
Copy the code
Next, you need to add event listeners, view all the event bindings, perform the login operation, and mention the exit operation
/ / login
tim.login({userID: 'your userID'.userSig: 'your userSig'}).then((imRespone) = >{
console.log(imResponse.data); // Login succeeded
}).catch((imError) = >{
console.warn('login error:', imError); // Information about the login failure
})
/ / exit
tim.logout().then((imResponse) = >{
console.log(imResponse.data); // Exit successfully
}).catch((imError) = >{
console.warn('logout error:', imError);
});
Copy the code
After logging in, we can obtain the session list and the message list under each session. The data state and detailed operation involved are quite complex. Let’s take a look at the expression processing in the text and briefly explain the idea here.
//emojiMap.js
export const emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/'
export const emojiMap = {
'[bad]': '[email protected]'.'[show]': '[email protected]'
}
export const emojiName = [
'[show]'.'[bad]'
]
Copy the code
According to the official demo, all the expressions are image mapping, such as [smiles] (https://imgcache.qq.com/open/qcloud/tim/assets/emoji/[email protected], only need to change the ending, patchwork pictures link, The img tag traverses the display for selection. When sending a message, we select an emoticon, simply append the emojiName to the text message, such as ‘[smile]’. Next is the problem of parsing text messages with emoticons. The official code for parsing is provided. You only need to import the emoji mapping and export the parsing function.
Then it is to send file information, obtain the corresponding file DOM, call the interface to send file message, display only need to display file name and file size, and then click download, you can refer to the following three functions
size() { const size = this.payload.fileSize if (size > 1024) { if (size / 1024 > 1024) { return `${this.toFixed(size / 1024 / 1024)} Mb` } return `${this.toFixed(size / 1024)} Kb` } return `${this.toFixed(size)}B` }, toFixed(number, {return number.tofixed (precision)} downloadFile() {// browser support fetch, use blob download, avoid browser click a TAB, If (window.fetch) {fetch(this.fileurl).then(res => res.blob()).then(blob => {let a = document.createElement('a') let url = window.URL.createObjectURL(blob) a.href = url a.download = this.fileName a.click() }) } else { let a = document.createElement('a') a.href = this.fileUrl a.target = '_blank' a.download = this.filename a.click() } }Copy the code
The picture message and the file message are almost the same. It should be noted that the picture message here can be zoomed in and zoomed out and rotated to view. The picture operation written in the official demo is attached below
<template>
<div class="image-previewer-wrapper" v-show="showPreviewer" @mousewheel="handleMouseWheel">
<div class="image-wrapper">
<img
class="image-preview"
:style="{transform: `scale(${zoom}) rotate(${rotate}deg)`}"
:src="previewUrl"
@click="close"
/>
</div>
<Icon type="md-close" class="close-button" @click="close" />
<Icon type="md-arrow-back" class="prev-button" @click="goPrev" />
<Icon type="md-arrow-forward" class="next-button" @click="goNext"></Icon>
<div class="actions-bar">
<Icon type="ios-remove-circle-outline" @click="zoomOut"></Icon>
<Icon type="ios-add-circle-outline" @click="zoomIn"></Icon>
<Icon type="md-undo" @click="rotateLeft"></Icon>
<Icon type="md-redo" @click="rotateRight"></Icon>
<span class="image-counter">{{index+1}} / {{imgUrlList.length}}</span>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'ImagePreviewer'.data() {
return {
url: ' ',
index: 0,
visible: false, zoom: 1, zoom: 0, minZoom: 0.1}}, computed: {... mapGetters(['imgUrlList']),
showPreviewer() {
return this.url.length > 0 && this.visible
},
imageStyle() {
return {
transform: `scale(${this.zoom}); `}},previewUrl() {
return this.formatUrl(this.imgUrlList[this.index])
}
},
mounted() {
this.$bus.$on('image-preview', this.handlePreview)
},
methods: {
handlePreview({ url }) {
this.url = url
this.index = this.imgUrlList.findIndex(item => item === url)
this.visible = true
},
handleMouseWheel(event) {
if (event.wheelDelta > 0) {
this.zoomIn()
} else {
this.zoomOut()
}
},
zoomIn() {this.zoom += 0.1},zoomOut() {this.zoom = this.zoom - 0.1 > this.minZoom? This. zoom -0.1: this.minZoom},close() {
Object.assign(this, { zoom: 1 })
this.visible = false
},
rotateLeft() {
this.rotate -= 90
},
rotateRight() {
this.rotate += 90
},
goNext() {
this.index = (this.index + 1) % this.imgUrlList.length
},
goPrev() {
this.index =
this.index - 1 >= 0 ? this.index - 1 : this.imgUrlList.length - 1
},
formatUrl(url) {
if(! url) {return ' '
}
return url.slice(0, 2) === '/ /' ? `https:${url}` : url } } } </script> <style scoped> .image-previewer-wrapper { position: fixed; width: 100%; left: 0; top: 0; height: 100%; display: flex; justify-content: center; align-items: flex-start; Background: RGBA (14, 12, 12, 0.7); z-index: 2000; cursor: zoom-out; } .close-button { cursor: pointer; font-size: 28px; color:# 000;position: fixed; top: 50px; right: 50px; Background: RGBA (255, 255, 255, 0.8); border-radius: 50%; padding: 6px; } .image-wrapper { position: relative; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; }. Image-preview {transition: transform 0.1s ease 0s; } .actions-bar { display: flex; justify-content: space-around; align-items: center; position: fixed; bottom: 50px; left: 50%; margin-left: -100px; padding: 12px; border-radius: 6px; Background: RGBA (255, 255, 255, 0.8); } .actions-bar i { font-size: 24px; cursor: pointer; margin: 0 6px; } .prev-button, .next-button { position: fixed; cursor: pointer; Background: RGBA (255, 255, 255, 0.8); border-radius: 50%; font-size: 24px; padding: 12px; } .prev-button { left: 0; top: 50%; } .next-button { right: 0; top: 50%; }. Image-counter {background: rgba(20, 18, 20, 0.53); padding: 3px; border-radius: 3px; color:#fff;
}
</style>
Copy the code