Commit 4bef8f84 by 杨铁龙

add chat

parent ab0ea9c3
......@@ -28,7 +28,8 @@
"vue-router": "^3.2.0",
"vue-seamless-scroll": "^1.1.23",
"vuex": "^3.4.0",
"wangeditor": "^4.5.1"
"wangeditor": "^4.5.1",
"xchat-client": "^2.2.4"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
......
export const config = {
appId: 'tecsun',
password: 'ds123456',
eid: process.env.VUE_APP_EID || 10,
chatApi: 'http://xchat-ds.jinsehuaqin.com:8020/',
SocketUrl: 'ws://xchat-ds.jinsehuaqin.com:8010/ws',
}
export const cache = {
chatApiOrgId: 'chatApiOrgId',
chatApiToken: 'chatApiToken',
}
import axios from 'axios'
import { config, cache } from './config'
import ximInstance from './xim'
export function buildHeaders() {
return {
headers: {
Authorization: 'Bearer ' + localStorage.getItem(cache.chatApiToken),
},
}
}
export function getImTokenAndInitChat() {
return axios
.post(`${config.chatApi}/inside/api/get_auth_token`, {
app_id: config.appId,
password: config.password,
eid: config.eid,
})
.then((r) => {
const token = r.data.data.token
const orgId = r.data.data.org_id
localStorage.setItem(cache.chatApiToken, token)
localStorage.setItem(cache.chatApiOrgId, orgId)
return initChat(token)
})
}
export function initChat(token) {
if (ximInstance.isConnected()) {
return Promise.resolve()
}
return ximInstance.open(config.SocketUrl, token)
}
import Vue from 'vue'
import { wampDebug, XChatClient } from 'xchat-client'
import { STATUS } from 'xchat-client/dist/xchat'
wampDebug(false)
const DefaultMsgPageSize = 20
function emptyFunc() {
return null
}
// export type MsgListener = (msg: Message) => void;
// export type ChatNotifyListener = (msg: NotifyMessage) => void;
// export type StatusChangeListener = (status: any, details: any) => void;
export const Events = {
Msg: 'msg',
Status: 'status',
}
// export enum Kind {
// Chat = "chat",
// ChatNotify = "chat_notify",
// UserNotify = "user_notify",
// }
const chatType = 'group'
export class Xim {
eventBus = new Vue();
client;
paramsForReconnection = {
url: '',
token: ''
};
close() {
if (this.client) {
if (this.client.connected) {
this.client.close()
}
if (this.client) {
this.client.onconnected = emptyFunc
this.client.onmsg = emptyFunc
this.client = undefined
}
}
}
connectionPending = false;
async open(url, token) {
this.connectionPending = true
await new Promise((resolve, reject) => {
this.paramsForReconnection = { url, token }
this.close()
console.log({ token: token })
if (!token) return
const client = new XChatClient(url, this.trimToken(token))
this.client = client
client.onstatuschange = (status, details) => {
this.onStatusChange(status, details)
if (
status === STATUS.DISCONNECTED ||
status === STATUS.CLOSED
) {
reject(status)
}
}
client.onconnected = () => {
this.onConnected.apply(this)
resolve()
}
client.onmsg = this.handleMsg.bind(this)
client.open()
}).finally(() => (this.connectionPending = false))
}
trimToken(token) {
return token.replace(/^Bearer\s/, '')
}
/**
* token过期或者切换用户登录时,需要设置新的token
*/
async setToken(token) {
const client = this.client
if (client == null) return
client.close()
client.setToken(this.trimToken(await token()))
client.open()
}
fetchMsgInBox(chatId, msgId) {
if (this.client == null) return Promise.reject()
return this.client.fetchMsgInBox(chatType, chatId, msgId)
}
fetchChatList() {
if (this.client == null) return Promise.reject()
return this.client.fetchChatList({})
}
fetchChatListAfter(lastMsgTs) {
if (this.client == null) return Promise.reject()
return this.client.fetchChatList({ last_msg_ts: lastMsgTs })
}
fetchChat(chat_id) {
if (this.client == null) return Promise.reject()
return this.client.fetchChat(chat_id)
}
/**
* 发送消息
*/
sendMsg(
chatType,
chatId,
msgType,
msg
) {
this.checkConnected()
if (this.client == null) return Promise.reject()
return this.client.sendMsg(chatType, chatId, msgType, msg, '', {})
}
inputing(chatId) {
this.checkConnected()
if (this.client == null) return Promise.reject()
return this.client.userInput(chatType, chatId)
}
/*
* 查询会话
*/
fetchChatMembers(chat_id) {
this.checkConnected()
if (this.client == null) return Promise.reject()
return this.client.fetchChatMembers(chat_id)
}
/**
* 查询消息
*/
async queryMsgs(
chatType,
chatId,
lid = 0,
rid = 0,
limit = DefaultMsgPageSize,
// = 0 正序(最新的消息在最下面),=1 倒序(最新的消息在最上面)
desc,
p = { isMember: true, model: '', obj: '' }
) {
this.checkConnected()
if (this.client == null) {
throw new Error("client shouldn't undefined")
}
// if (p && !p.isMember && p.model && p.obj) {
// return this.queryMessageWhenIsNotMember(p, limit, lid, rid, desc);
// }
const res = await this.client.fetchChatMsgs(chatType, chatId, {
lid,
rid,
limit,
desc,
})
return res.args[0]
}
// queryMessageWhenIsNotMember(
// p: { isMember: boolean; model: string; obj: string },
// limit: number,
// lid: number,
// rid: number,
// desc: boolean
// ) {
// return chat
// .getSdk()
// .getAxios()
// .get<any, Message[]>(
// `/general/xim/model/${p.model}/${
// p.obj
// }/msgs?lid=${lid}&rid=${rid}&limit=${limit}&desc=${
// desc ? 1 : 0
// }`
// );
// }
setMessagesRead(chatId, msg) {
if (!msg.length) {
return this.setRead(chatId, 1, 1)
}
return this.setRead(chatId, msg[0].id, msg[msg.length - 1].id)
}
/** 查询最后一页消息 */
async queryLastPageMsg(
chatType,
chatId,
limit,
p = { isMember: true, model: '', obj: '' }
) {
const data = await this.queryMsgs(
chatType,
chatId,
0,
0,
limit,
true,
p
)
if (p && p.isMember) {
this.setMessagesRead(chatId, data)
}
return data
}
/** 查询上一页消息 */
async queryPrevPageMsg(
chatType,
chatId,
msgId,
limit,
p = { isMember: true, model: '', obj: '' }
) {
const data = await this.queryMsgs(
chatType,
chatId,
0,
msgId,
limit,
true,
p
)
if (p && p.isMember) {
this.setMessagesRead(chatId, data)
}
return data
}
/** 查询下一页消息 */
async queryNextPageMsg(
chatType,
chatId,
msgId,
limit,
p = { isMember: true, model: '', obj: '' }
) {
const data = await this.queryMsgs(
chatType,
chatId,
msgId,
0,
limit,
false,
p
)
if (p && p.isMember) {
this.setMessagesRead(chatId, data)
}
return data
}
// on(event: "msg", chatId: number, listener: MsgListener): this;
// on(event: "msg", listener: MsgListener): this;
// on(
// event: "chat_notify",
// kind: "chat_change",
// listener: ChatNotifyListener
// ): this;
// on(
// event: "chat_notify",
// kind: string,
// listener: ChatNotifyListener
// ): this;
// on(event: "chat_notify", listener: ChatNotifyListener): this;
// on(event: "status", listener: StatusChangeListener): this;
on(...args) {
this.eventBus.$on(...this.parseEventListener(...args))
return this
}
// off(event: "msg", chatId: number, listener: MsgListener): this;
// off(event: "msg", listener: MsgListener): this;
// off(
// event: "chat_notify",
// kind: "chat_change",
// listener: ChatNotifyListener
// ): this;
// off(
// event: "chat_notify",
// kind: string,
// listener: ChatNotifyListener
// ): this;
// off(event: "chat_notify", listener: ChatNotifyListener): this;
// off(event: "status", listener: StatusChangeListener): this;
off(...args) {
this.eventBus.$off(...this.parseEventListener(...args))
return this
}
once(...args) {
this.eventBus.$once(...this.parseEventListener(...args))
return this
}
emit(event, ...args) {
this.eventBus.$emit(event, ...args)
return this
}
async withdraw(chat, msg) {
this.checkConnected()
if (this.client == null) {
throw new Error("client shouldn't undefined")
}
return this.client
.withdrawMsg(chatType, chat, msg)
.then((r) => r.args[0])
}
/**
* 移除会话(用户端/移动端使用)
*/
async closeChat(chatId) {
return this.client?.setChat(chatId, { is_remove: true })
}
isConnected() {
return this.client?.connected
}
setRead(chatId, start_msg_id, end_msg_id) {
return this.client?.syncReadMsg(chatId, start_msg_id, end_msg_id)
}
// xchat-client 2.2.2新增
setUnRead(chatId) {
return this.client.unreadChat(chatId)
}
parseEventListener(...args) {
if (args.length < 2) {
throw new Error('参数个数不正确')
}
const listener = args[args.length - 1]
return [args.slice(0, -1).join('.'), listener]
}
onConnected() {
if (this.client == null) return
// 连接成功后,需要调用pubUserInfo, 否则服务端会认为此连接无效
// TODO
this.client.pubUserInfo(JSON.stringify({ org_id: 2 }))
this.debug('xim connected')
}
onStatusChange(status, details) {
this.debug('onstatuschange', status, details)
this.emit(Events.Status, status, details)
if (status === STATUS.DISCONNECTED || status === STATUS.CLOSED) {
this.hanldeOffline()
}
}
hanldeOffline() {
this.debug('开始重连')
this.reOpen()
}
reOpen() {
if (this.connectionPending) return
if (this.paramsForReconnection == null) return
this.open(
this.paramsForReconnection.url,
this.paramsForReconnection.token
)
}
handleMsg(kind, msg) {
this.debug(`收到消息 ${new Date().getTime()}`, kind, msg)
switch (kind) {
case 'chat':
this.emit(`msg`, msg)
this.emit(`msg.${msg.chat_id}`, msg)
break
case 'chat_notify':
this.emit(`chat_notify`, msg)
this.emit(`chat_notify.${msg.msg_type}`, msg)
break
default:
this.emit(kind, msg)
}
}
checkConnected() {
if (this.client == null) return
if (!this.client.connected) {
try {
this.client?.open()
} catch (e) {
// eslint-disable-next-line no-console
console.error('checkConnected', e)
this.reOpen()
}
}
}
debug(message, ...params) {
console.log(message, ...params)
}
registerOnMessage(vue, action) {
this.on('msg', action)
vue.$once('hook:beforeDestroy', () => this.off('msg', action))
}
withDrawMsgHandle(e) {
return e.ref_id
? [e.ref_id]
: e.msg.startsWith('[')
? JSON.parse(e.msg)
: [+e.msg]
}
}
const ximInstance = new Xim()
export default ximInstance
......@@ -141,8 +141,18 @@
</el-form>
</el-drawer>
<!-- 消息详情 -->
<el-drawer custom-class="drawer-details" title="消息详情" size="40%" :wrapper-closable="false" :visible.sync="showMsgDrawer">
<span>消息详情</span>
<el-drawer
custom-class="drawer-details"
title="消息详情"
size="40%"
:wrapper-closable="false"
:visible.sync="showMsgDrawer"
>
<div style="font-size:10px">
<div v-for="item in msgList" :key="item.id">{{ formatMsg(item) }}</div>
</div>
<el-input v-model="msgInput" clearable placeholder="请输入" />
<el-button @click="sendMsg">发送</el-button>
</el-drawer>
</div>
</template>
......@@ -151,6 +161,8 @@
import Pagination from '@/components/Pagination'
import { getAreaDictionary } from '@/api/user'
import { getSendMsg, getMsgDetails } from '@/api/Message'
import { getImTokenAndInitChat } from '@/im/index'
import ximInstance from '@/im/xim'
export default {
name: 'PersonaPortrait',
components: { Pagination },
......@@ -241,7 +253,11 @@ export default {
showMsgDrawer: false, // 抽屉:消息详情
taskList: [ // 任务列表
]
],
currentChatId: 0,
msgInput: '',
chatList: [],
msgList: []
}
},
computed: {
......@@ -268,8 +284,52 @@ export default {
},
created() {
// this.initPage()
getImTokenAndInitChat().then(() => {
this.register()
return this.getChatList()
})
},
methods: {
// 注册消息刷新事件
register() {
ximInstance.off('msg', () => { })
ximInstance.on('msg', (msg) => {
console.log('收到新消息', msg)
this.getChatList()
this.openChat(this.currentChatId)
})
},
getChatList() {
ximInstance.fetchChatList().then((data) => {
this.chatList = data.args[0]
console.log('chatList', this.chatList)
})
},
openChat(id) {
ximInstance.queryLastPageMsg('users', id, 20, {
model: '',
obj: '',
isMember: true
}).then((data) => {
this.msgList = data
console.log('msgList', data)
})
},
sendMsg() {
ximInstance.sendMsg('users', this.currentChatId, 'text', JSON.stringify({
text: this.msgInput
})).then(() => {
this.msgInput = ''
this.openChat(this.currentChatId)
})
},
formatMsg(msg) {
try {
return JSON.parse(msg.msg).text
} catch {
return ''
}
},
// 初始化
initPage() {
this.searchForm.areaCode = this.curAreaID
......@@ -291,6 +351,8 @@ export default {
},
// 获取消息详情
async getMsgDetails(id) {
this.currentChatId = 2
this.openChat(this.currentChatId)
const { data } = await getMsgDetails(id)
if (data) {
console.log(data, '消息详情')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment