Commit ef808ca3 ef808ca31dd105637fd549812f6ac2f6fd74822c by yangjun@hikoon.cn

更新

1 parent 2c6e46e5
......@@ -9,10 +9,12 @@
"lint": "node node_modules/@vue/cli-service/bin/vue-cli-service.js lint src --ext .vue,.js --fix"
},
"dependencies": {
"add": "^2.0.6",
"axios": "^0.21.0",
"core-js": "^2.6.11",
"cos-js-sdk-v5": "^0.5.22",
"element-ui": "^2.13.0",
"loadsh": "^0.0.4",
"md5": "^2.3.0",
"mta-h5-analysis": "^2.0.15",
"tim-js-sdk": "^2.10.2",
......@@ -23,7 +25,8 @@
"vue": "^2.6.11",
"vue-clipboard2": "^0.3.1",
"vue-infinite-scroll": "^2.0.2",
"vuex": "^3.1.2"
"vuex": "^3.1.2",
"yarn": "^1.22.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.12.1",
......
/*eslint-disable*/
/*
* Module: GenerateTestUserSig
*
......@@ -17,38 +18,38 @@
* Reference:https://cloud.tencent.com/document/product/647/17275#Server
*/
function genTestUserSig(userID) {
/**
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
*
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
* 它是腾讯云用于区分客户的唯一标识。
*/
var SDKAPPID = 1400514950;
/**
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
*
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
* 它是腾讯云用于区分客户的唯一标识。
*/
var SDKAPPID = 0
/**
* 签名过期时间,建议不要设置的过短
* <p>
* 时间单位:秒
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
*/
var EXPIRETIME = 604800;
/**
* 签名过期时间,建议不要设置的过短
* <p>
* 时间单位:秒
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
*/
var EXPIRETIME = 604800
/**
* 计算签名用的加密密钥,获取步骤如下:
*
* step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
* step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
* step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
*
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
*/
var SECRETKEY = 'eb219c4b42bdbbf5dca38f21f5be26ab38f1c157d0ba4277d5acce6507f2d727';
/**
* 计算签名用的加密密钥,获取步骤如下:
*
* step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
* step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
* step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
*
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
*/
var SECRETKEY = ''
var generator = new window.LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
var userSig = generator.genTestUserSig(userID);
return {
SDKAppID: SDKAPPID,
userSig: userSig
};
}
\ No newline at end of file
var generator = new window.LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME)
var userSig = generator.genTestUserSig(userID)
return {
SDKAppID: SDKAPPID,
userSig: userSig
}
}
......
......@@ -37,11 +37,7 @@
<div class="summary">
<div v-if="conversation.lastMessage" class="text-ellipsis">
<span class="remind" style="color:red;" v-if="hasMessageAtMe">[有人提到我]</span>
<span class="text" title="图片"
v-if="conversation.lastMessage.type === 'TIMCustomElem' && conversation.lastMessage.payload.data === 'image'">
[图片]
</span>
<span v-else class="text" :title="conversation.lastMessage.messageForShow">
<span class="text" :title="conversation.lastMessage.messageForShow">
{{ messageForShow }}
</span>
</div>
......@@ -58,6 +54,7 @@
<script>
import {mapGetters, mapState} from 'vuex'
import _ from 'loadsh'
import {getDate, getTime, isToday} from '../../utils/date'
export default {
......@@ -134,8 +131,16 @@ export default {
return '对方撤回了一条消息'
}
return `${this.conversation.lastMessage.fromAccount}撤回了一条消息`
} else {
switch (_.get(this.conversation.lastMessage, 'payload.data', 'other')) {
case 'image':
return '[图片]'
case 'video':
return '[视频]'
default:
return this.conversation.lastMessage.messageForShow
}
}
return this.conversation.lastMessage.messageForShow
},
...mapState({
currentConversation: state => state.conversation.currentConversation,
......
......@@ -22,14 +22,21 @@ import {mapState} from 'vuex'
import Helper from '../../../utils/helper'
export default {
props: {
group: {
type: String, required: true
}
},
computed: {
...mapState({
users: state => state.friend.userList,
currentConversation: state => state.conversation.currentConversation,
currentMemberList: state => state.group.currentMemberList
currentMemberList: state => state.group.currentMemberList,
currentUnMemberList: state => state.group.currentUnMemberList
}),
currentUnMemberList() {
return this.users.filter(item => this.currentMemberList.filter(data => data.userID === item.esm_id).length === 0)
},
data() {
return {
list: []
}
},
methods: {
......@@ -67,6 +74,9 @@ export default {
// this.$store.commit('showMessage', {type: 'error', message: error.message})
// })
}
},
mounted() {
console.log(12312321)
}
}
</script>
......
......@@ -6,9 +6,9 @@
<el-button v-if="showCancelBan" type="text" @click="cancelMute">取消禁言</el-button>
<el-popover title="禁言" v-model="popoverVisible" v-show="showBan">
<el-input
v-model="muteTime"
placeholder="请输入禁言时间"
@keydown.enter.native="setGroupMemberMuteTime"
v-model="muteTime"
placeholder="请输入禁言时间"
@keydown.enter.native="setGroupMemberMuteTime"
/>
<el-button slot="reference" type="text" style="color:red;">禁言</el-button>
</el-popover>
......@@ -22,15 +22,15 @@
{{ member.nameCard || '暂无' }}
<el-popover title="修改群名片" v-model="nameCardPopoverVisible" v-show="showEditNameCard">
<el-input
v-model="nameCard"
placeholder="请输入群名片"
@keydown.enter.native="setGroupMemberNameCard"
v-model="nameCard"
placeholder="请输入群名片"
@keydown.enter.native="setGroupMemberNameCard"
/>
<i
class="el-icon-edit"
title="修改群名片"
slot="reference"
style="cursor:pointer; font-size:1.6rem;"
class="el-icon-edit"
title="修改群名片"
slot="reference"
style="cursor:pointer; font-size:1.6rem;"
></i>
</el-popover>
</div>
......@@ -44,7 +44,7 @@
</div>
<el-button type="text" v-if="canChangeRole" @click="changeMemberRole">
{{
member.role === 'Admin' ? '取消管理员' : '设为管理员'
member.role === 'Admin' ? '取消管理员' : '设为管理员'
}}
</el-button>
<el-button type="text" v-if="showKickout" style="color:red;" @click="kickoutGroupMember">踢出群组</el-button>
......@@ -52,9 +52,10 @@
</template>
<script>
import { mapState } from 'vuex'
import { Popover } from 'element-ui'
import { getFullDate } from '../../../utils/date'
import {mapState} from 'vuex'
import {Popover} from 'element-ui'
import {getFullDate} from '../../../utils/date'
export default {
components: {
ElPopover: Popover
......@@ -89,8 +90,8 @@ export default {
},
canChangeRole() {
return (
this.isOwner &&
['ChatRoom', 'Public'].includes(this.currentConversation.subType)
this.isOwner &&
['ChatRoom', 'Public'].includes(this.currentConversation.subType)
)
},
changeRoleTitle() {
......@@ -98,8 +99,8 @@ export default {
return ''
}
return this.isOwner && this.member.role === 'Admin'
? '设为:Member'
: '设为:Admin'
? '设为:Member'
: '设为:Admin'
},
// 是否显示禁言时间
showMuteUntil() {
......@@ -109,9 +110,9 @@ export default {
// 是否显示取消禁言按钮
showCancelBan() {
if (
this.showMuteUntil &&
this.currentConversation.type === this.TIM.TYPES.CONV_GROUP &&
!this.isMine
this.showMuteUntil &&
this.currentConversation.type === this.TIM.TYPES.CONV_GROUP &&
!this.isMine
) {
return this.isOwner || this.isAdmin
}
......@@ -136,20 +137,20 @@ export default {
methods: {
kickoutGroupMember() {
this.tim
.deleteGroupMember({
groupID: this.currentConversation.groupProfile.groupID,
reason: '我要踢你出群',
userIDList: [this.member.userID]
})
.then(() => {
this.$store.commit('deleteGroupMemeber', this.member.userID)
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
.deleteGroupMember({
groupID: this.currentConversation.groupProfile.groupID,
reason: '我要踢你出群',
userIDList: [this.member.userID]
})
.then(() => {
this.$store.commit('deleteGroupMember', this.member.userID)
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
})
})
})
},
changeMemberRole() {
if (!this.canChangeRole) {
......@@ -157,53 +158,53 @@ export default {
}
let currentRole = this.member.role
this.tim
.setGroupMemberRole({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
role: currentRole === 'Admin' ? 'Member' : 'Admin'
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
.setGroupMemberRole({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
role: currentRole === 'Admin' ? 'Member' : 'Admin'
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
})
})
})
},
setGroupMemberMuteTime() {
this.tim
.setGroupMemberMuteTime({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
muteTime: Number(this.muteTime)
})
.then(() => {
this.muteTime = ''
this.popoverVisible = false
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
.setGroupMemberMuteTime({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
muteTime: Number(this.muteTime)
})
.then(() => {
this.muteTime = ''
this.popoverVisible = false
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
})
})
})
},
// 取消禁言
cancelMute() {
this.tim
.setGroupMemberMuteTime({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
muteTime: 0
})
.then(() => {
this.muteTime = ''
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
.setGroupMemberMuteTime({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
muteTime: 0
})
.then(() => {
this.muteTime = ''
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
})
})
})
},
setGroupMemberNameCard() {
if (this.nameCard.trim().length === 0) {
......@@ -214,23 +215,23 @@ export default {
return
}
this.tim
.setGroupMemberNameCard({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
nameCard: this.nameCard
})
.then(() => {
this.nameCardPopoverVisible = false
this.$store.commit('showMessage', {
message: '修改成功'
.setGroupMemberNameCard({
groupID: this.currentConversation.groupProfile.groupID,
userID: this.member.userID,
nameCard: this.nameCard
})
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
.then(() => {
this.nameCardPopoverVisible = false
this.$store.commit('showMessage', {
message: '修改成功'
})
})
.catch(error => {
this.$store.commit('showMessage', {
type: 'error',
message: error.message
})
})
})
}
}
}
......
......@@ -3,7 +3,7 @@
<div class="header">
<span class="member-count text-ellipsis">群成员:{{ currentConversation.groupProfile.memberCount }}</span>
<popover v-model="addGroupMemberVisible">
<add-group-member style="width: 200px;max-height: 70vh;overflow: auto"></add-group-member>
<add-group-member class="add-member-list" :group="currentConversation.groupProfile.groupID"/>
<div slot="reference" class="btn-add-member" title="添加群成员">
<span class="tim-icon-friend-add"></span>
</div>
......@@ -42,6 +42,7 @@ export default {
data() {
return {
addGroupMemberVisible: false,
addGroupMemberList: [],
currentMemberID: '',
count: 30 // 显示的群成员数量
}
......@@ -76,12 +77,11 @@ export default {
}
},
loadMore() {
this.$store
.dispatch('getGroupMemberList', this.groupProfile.groupID)
this.$store.dispatch('getGroupMemberList', this.groupProfile.groupID)
.then(() => {
this.count += 30
})
}
},
}
}
</script>
......@@ -93,6 +93,11 @@ export default {
padding 10px 16px 10px 20px
border-bottom 1px solid $border-base
>>> .add-member-list
width: 200px;
max-height: 70vh;
overflow: auto
.member-count
display inline-block
max-width 130px
......
......@@ -203,15 +203,15 @@
@keydown.enter.native="editNameCard"
/>
</div>
<div class="info-item">
<div class="label" :class="{'active' : active}">全体禁言</div>
<el-switch
v-model="muteAllMembers"
active-color="#409eff"
inactive-color="#dcdfe6"
@change='changeMuteStatus'>
</el-switch>
</div>
<!-- <div class="info-item">-->
<!-- <div class="label" :class="{'active' : active}">全体禁言</div>-->
<!-- <el-switch-->
<!-- v-model="muteAllMembers"-->
<!-- active-color="#409eff"-->
<!-- inactive-color="#dcdfe6"-->
<!-- @change='changeMuteStatus'>-->
<!-- </el-switch>-->
<!-- </div>-->
<div v-if="isOwner">
<el-button type="text" @click="showChangeGroupOwner = true">转让群组</el-button>
<el-input
......
......@@ -28,7 +28,6 @@ export default {
},
computed: {
...mapState({
friendList: state => state.friend.friendList,
adminList(state) {
const list = state.friend.adminList || []
return this.select.length === 0 ? list : list.filter(item => item.name.indexOf(this.select) > -1)
......
......@@ -3,16 +3,20 @@
<div class="group-item">
<avatar src=""/>
<div class="group-name text-ellipsis">{{ group.title }}</div>
<el-tag v-if="group.is_stated===1" effect="dark" type="danger" size="mini">已结算</el-tag>
</div>
</div>
</template>
<script>
import {MessageBox} from 'element-ui'
import {MessageBox, Tag} from 'element-ui'
import Helper from '../../utils/helper'
export default {
props: ['group'],
components: {
ElTag: Tag
},
data() {
return {
visible: false,
......
<template>
<message-bubble :isMine=isMine :message=message>
<div class="custom-element-wrapper">
<div class="survey" v-if="this.payload.data === 'survey'">
<div class="title">对IM DEMO的评分和建议</div>
<el-rate
v-model="rate"
disabled
show-score
text-color="#ff9900"
score-template="{value}">
</el-rate>
<div class="suggestion">{{ this.payload.extension }}</div>
</div>
<span class="text" title="您可以自行解析自定义消息" v-else>
<template v-if="text.isFromGroupLive && text.isFromGroupLive === 1">
<message-group-live-status :liveInfo='text'/>
</template>
<template v-else-if="this.payload.data === 'image'">
<el-image :src="this.payload.description" :preview-src-list="[this.payload.description]"/>
<!-- <img alt="image" class="image-element" :src="this.payload.description" @load="onImageLoaded"-->
<!-- @click="handlePreview"/>-->
</template>
<template v-else>{{ text }}</template>
</span>
</div>
</message-bubble>
<div class="custom-element-wrapper">
<image-element
v-if="this.payload.data === 'image'"
:isMine=isMine
:payload="Object.assign(message.payload,{ imageInfoArray:[ { url: message.payload.description } ] })"
:message="message"/>
<video-element
v-else-if="this.payload.data === 'video'"
:isMine="isMine"
:payload="Object.assign(message.payload,{ videoUrl: message.payload.description })"
:message="message"
/>
<template v-else>
<message-bubble :isMine=isMine :message=message>
<message-group-live-status v-if="text.isFromGroupLive && text.isFromGroupLive === 1" :liveInfo='text'/>
<template v-else>{{ text }}</template>
</message-bubble>
</template>
</div>
</template>
<script>
import {mapState} from 'vuex'
import MessageBubble from '../message-bubble'
import {Rate} from 'element-ui'
import MessageGroupLiveStatus from '../message-group-live-status'
import ImageElement from '../message-elements/image-element.vue'
import VideoElement from './video-element'
export default {
name: 'CustomElement',
......@@ -50,8 +43,8 @@ export default {
}
},
components: {
VideoElement,
MessageBubble,
ElRate: Rate,
MessageGroupLiveStatus,
ImageElement,
},
......@@ -94,14 +87,6 @@ export default {
return '[自定义消息]'
}
},
onImageLoaded(event) {
this.$bus.$emit('image-loaded', event)
},
handlePreview() {
this.$bus.$emit('image-preview', {
url: this.payload.description
})
}
}
}
</script>
......
......@@ -55,9 +55,7 @@ export default {
this.$bus.$emit('image-loaded', event)
},
handlePreview() {
this.$bus.$emit('image-preview', {
url: this.payload.imageInfoArray[0].url
})
this.$bus.$emit('image-preview', {url: this.payload.imageInfoArray[0].url})
}
}
}
......
<template>
<message-bubble :isMine=isMine :message=message>
<video
:src="payload.videoUrl"
controls
class="video"
@error="videoError"
:src="payload.videoUrl"
controls
class="video"
@error="videoError"
></video>
<el-progress
v-if="showProgressBar"
:percentage="percentage"
:color="percentage => (percentage === 100 ? '#67c23a' : '#409eff')"
v-if="showProgressBar"
:percentage="percentage"
:color="percentage => (percentage === 100 ? '#67c23a' : '#409eff')"
/>
</message-bubble>
</template>
<script>
import MessageBubble from '../message-bubble'
import { Progress } from 'element-ui'
import {Progress} from 'element-ui'
export default {
name: 'VideoElement',
components: {
......@@ -47,7 +48,7 @@ export default {
},
methods: {
videoError(e) {
this.$store.commit('showMessage', { type: 'error', message: '视频出错,错误原因:' + e.target.error.message })
this.$store.commit('showMessage', {type: 'error', message: '视频出错,错误原因:' + e.target.error.message})
},
}
}
......
......@@ -90,7 +90,7 @@
<avatar class="group-member-avatar" :src="avatar" @click.native="showGroupMemberProfile"/>
</div>
<div class="col-2">
<!-- 消息主体 -->
<!-- 消息主体 -->
<message-header v-if="showMessageHeader" :message="message"/>
<div class="content-wrapper">
<message-status-icon v-if="isMine" :message="message"/>
......
<template>
<div class="login-wrapper">
<img class="logo" :src="logo" />
<img class="logo" :src="logo"/>
<el-form
ref="login"
:rules="rules"
:model="form"
label-width="0"
style="width:100%;"
@keydown.enter.native="submit"
ref="login"
:rules="rules"
:model="form"
label-width="0"
style="width:100%;"
@keydown.enter.native="submit"
>
<!-- Github登录方式 -->
<el-form-item prop="userID">
<el-select v-model="form.userID" class="user-selector">
<el-option
v-for="index in 30"
:key="index"
:label="`user${index-1}`"
:value="`user${index-1}`"
v-for="index in 30"
:key="index"
:label="`user${index-1}`"
:value="`user${index-1}`"
></el-option>
</el-select>
</el-form-item>
......@@ -35,19 +35,20 @@
</el-form-item>-->
</el-form>
<el-button
type="primary"
@click="submit"
style="width:100%; margin-top: 6px;"
:loading="loading"
>登录</el-button>
type="primary"
@click="submit"
style="width:100%; margin-top: 6px;"
:loading="loading"
>登录
</el-button>
</div>
</template>
<script>
import { Form, FormItem, Select, Option } from 'element-ui'
import {Form, FormItem, Option, Select} from 'element-ui'
import logo from '../../assets/image/logo.png'
import { errorMap } from '../../utils/common'
import md5 from 'md5'
import Helper from '../../utils/helper'
export default {
name: 'Login',
components: {
......@@ -72,10 +73,10 @@ export default {
},
rules: {
userID: [
{ required: true, message: '请输入 userID', trigger: 'blur' },
{ validator: checkUserID, trigger: 'blur' }
{required: true, message: '请输入 userID', trigger: 'blur'},
{validator: checkUserID, trigger: 'blur'}
],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
password: [{required: true, message: '请输入密码', trigger: 'blur'}]
},
logo: logo,
registerVisible: false,
......@@ -93,33 +94,33 @@ export default {
login() {
this.loading = true
this.tim
.login({
userID: this.form.userID,
userSig: window.genTestUserSig(this.form.userID).userSig
})
.then(() => {
this.loading = false
this.$store.commit('toggleIsLogin', true)
this.$store.commit('startComputeCurrent')
this.$store.commit('showMessage', { type: 'success', message: '登录成功' })
this.$store.commit({
type: 'GET_USER_INFO',
.login({
userID: this.form.userID,
userSig: window.genTestUserSig(this.form.userID).userSig,
sdkAppID: window.genTestUserSig('').SDKAppID
userSig: Helper.getSignature(this.form.userID)
})
this.$store.commit('showMessage', {
type: 'success',
message: '登录成功'
.then(() => {
this.loading = false
this.$store.commit('toggleIsLogin', true)
this.$store.commit('startComputeCurrent')
this.$store.commit('showMessage', {type: 'success', message: '登录成功'})
this.$store.commit({
type: 'GET_USER_INFO',
userID: this.form.userID,
userSig: Helper.getSignature(this.form.userID),
sdkAppID: process.env.VUE_APP_API_KEY
})
this.$store.commit('showMessage', {
type: 'success',
message: '登录成功'
})
})
})
.catch(error => {
this.loading = false
this.$store.commit('showMessage', {
message: '登录失败:' + error.message,
type: 'error'
.catch(error => {
this.loading = false
this.$store.commit('showMessage', {
message: '登录失败:' + error.message,
type: 'error'
})
})
})
},
}
}
......@@ -134,27 +135,34 @@ export default {
background $white
color $black
border-radius 5px
box-shadow: 0 11px 20px 0 rgba(0,0,0,0.3)
box-shadow: 0 11px 20px 0 rgba(0, 0, 0, 0.3)
.row-div
display flex
justify-content center
align-items center
flex-direction row
.logo
width 110px
height 110px
.loginBox
width 320px
margin 0 0 20px 0
.send-code
width 112px
.login-im-btn
width 100%
.loginFooter
color: #8c8a8ac7
text-align: center
padding: 0 0 20px 0
cursor: pointer
.login-wrapper {
display: flex;
align-items: center;
......
......@@ -71,14 +71,11 @@ export default {
this.loading = false
this.$store.commit('toggleIsLogin', true)
this.$store.commit('startComputeCurrent')
this.$store.commit({
type: 'GET_USER_INFO',
this.$store.commit('GET_USER_INFO', {
userID: user.id,
userSig: user.sign,
sdkAppID: window.genTestUserSig('').SDKAppID
sdkAppID: process.env.VUE_APP_API_KEY
})
Helper.contactList().then(res => this.$store.commit('updateFriendList', res.data))
// Helper.groupList({size: 20}).then(res => this.$store.commit('updateGroupList', res.data))
// this.$store.commit('showMessage', {type: 'success', message: '登录成功'})
})
.catch(error => {
......@@ -142,15 +139,14 @@ export default {
if (isSDKReady) {
this.tim.getMyProfile()
.then(({data}) => {
this.$store.commit('updateCurrentUserProfile', data)
})
.catch(error => {
this.$store.commit('showMessage', {type: 'error', message: error.message})
})
.then(({data}) => this.$store.commit('updateCurrentUserProfile', data))
.catch(error => this.$store.commit('showMessage', {type: 'error', message: error.message}))
this.$store.dispatch('getBlacklist')
Helper.contactList({type: 'admin'}).then(res => this.$store.commit('updateAdminList', res.data))
Helper.contactList({type: 'scheme'}).then(res => this.$store.commit('updateSchemeList', res.data))
Helper.contactList({type: 'user'}).then(res => this.$store.commit('updateUserList', res.data))
// 登录trtc calling
this.trtcCalling.login({sdkAppID: this.sdkAppID, userID: this.userID, userSig: this.userSig})
// this.trtcCalling.login({sdkAppID: this.sdkAppID, userID: this.userID, userSig: this.userSig})
}
},
kickedOutReason(type) {
......
import tim from 'tim'
import TIM from 'tim-js-sdk'
import _ from 'loadsh'
import store from '..'
import { titleNotify } from '../../utils'
import { filterCallingMessage } from '../../utils/common'
import {titleNotify} from '../../utils'
import {filterCallingMessage} from '../../utils/common'
const conversationModules = {
state: {
currentConversation: {},
currentMessageList: [],
nextReqMessageID: '',
isCompleted: false, // 当前会话消息列表是否已经拉完了所有消息
conversationList: [],
callingInfo: {
memberList: [],
type: 'C2C', //C2C,GROUP
},
audioCall: false
},
getters: {
toAccount: state => {
if (!state.currentConversation || !state.currentConversation.conversationID) {
return ''
}
switch (state.currentConversation.type) {
case 'C2C':
return state.currentConversation.conversationID.replace('C2C', '')
case 'GROUP':
return state.currentConversation.conversationID.replace('GROUP', '')
default:
return state.currentConversation.conversationID
}
},
currentConversationType: state => {
if (!state.currentConversation || !state.currentConversation.type) {
return ''
}
return state.currentConversation.type
state: {
currentConversation: {},
currentMessageList: [],
nextReqMessageID: '',
isCompleted: false, // 当前会话消息列表是否已经拉完了所有消息
conversationList: [],
callingInfo: {
memberList: [],
type: 'C2C', //C2C,GROUP
},
audioCall: false
},
totalUnreadCount: state => {
const result = state.conversationList.reduce((count, conversation) => {
// 当前会话不计算总未读
if (!store.getters.hidden && state.currentConversation.conversationID === conversation.conversationID) {
return count
getters: {
toAccount: state => {
if (!state.currentConversation || !state.currentConversation.conversationID) {
return ''
}
switch (state.currentConversation.type) {
case 'C2C':
return state.currentConversation.conversationID.replace('C2C', '')
case 'GROUP':
return state.currentConversation.conversationID.replace('GROUP', '')
default:
return state.currentConversation.conversationID
}
},
currentConversationType: state => {
if (!state.currentConversation || !state.currentConversation.type) {
return ''
}
return state.currentConversation.type
},
totalUnreadCount: state => {
const result = state.conversationList.reduce((count, conversation) => {
// 当前会话不计算总未读
if (!store.getters.hidden && state.currentConversation.conversationID === conversation.conversationID) {
return count
}
return count + conversation.unreadCount
}, 0)
titleNotify(result)
return result
},
// 用于当前会话的图片预览
imgUrlList: state => {
return state.currentMessageList
.filter(message => (message.type === TIM.TYPES.MSG_IMAGE || (message.type === TIM.TYPES.MSG_CUSTOM && _.get(message, 'payload.data', 'custom') === 'image')) && !message.isRevoked) // 筛选出没有撤回并且类型是图片类型的消息
.map(message => message.payload.imageInfoArray[0].url || message.payload.description)
}
return count + conversation.unreadCount
}, 0)
titleNotify(result)
return result
},
// 用于当前会话的图片预览
imgUrlList: state => {
return state.currentMessageList
.filter(message => message.type === TIM.TYPES.MSG_IMAGE && !message.isRevoked) // 筛选出没有撤回并且类型是图片类型的消息
.map(message => message.payload.imageInfoArray[0].url)
}
},
mutations: {
/**
* 显示trtcCalling 群通话成员列表
* @param {Object} state
* @param {Conversation} setCallingList
*/
mutations: {
/**
* 显示trtcCalling 群通话成员列表
* @param {Object} state
* @param {Conversation} setCallingList
*/
setCallingList(state, value) {
state.callingInfo.memberList = value.memberList
state.callingInfo.type = value.type
},
setCallingList(state, value) {
state.callingInfo.memberList = value.memberList
state.callingInfo.type = value.type
},
/**
* 显示trtcCalling 语音通话
* @param {Object} state
* @param {Conversation} showAudioCall
*/
/**
* 显示trtcCalling 语音通话
* @param {Object} state
* @param {Conversation} showAudioCall
*/
showAudioCall(state, value) {
state.audioCall = value
},
showAudioCall(state, value) {
state.audioCall = value
},
/**
* 更新当前会话
* 调用时机: 切换会话时
* @param {Object} state
* @param {Conversation} conversation
*/
updateCurrentConversation(state, conversation) {
state.currentConversation = conversation
state.currentMessageList = []
state.nextReqMessageID = ''
state.isCompleted = false
},
/**
* 更新会话列表
* 调用时机:触发会话列表更新事件时。CONVERSATION_LIST_UPDATED
* @param {Object} state
* @param {Conversation[]} conversationList
*/
updateConversationList(state, conversationList) {
state.conversationList = conversationList
},
/**
* 重置当前会话
* 调用时机:需要重置当前会话时,例如:当前会话是一个群组,正好被踢出群时(被踢群事件触发),重置当前会话
* @param {Object} state
*/
resetCurrentConversation(state) {
state.currentConversation = {}
},
/**
* 将消息插入当前会话列表
* 调用时机:收/发消息事件触发时
* @param {Object} state
* @param {Message[]|Message} data
* @returns
*/
pushCurrentMessageList(state, data) {
// 还没当前会话,则跳过
if (!state.currentConversation.conversationID) {
return
}
if (Array.isArray(data)) {
// 筛选出当前会话的消息
const result = data.filter(item => item.conversationID === state.currentConversation.conversationID)
state.currentMessageList = [...state.currentMessageList, ...result]
filterCallingMessage(state.currentMessageList)
} else if (data.conversationID === state.currentConversation.conversationID) {
state.currentMessageList = [...state.currentMessageList, data]
filterCallingMessage(state.currentMessageList)
}
},
/**
* 从当前消息列表中删除某条消息
* @param {Object} state
* @param {Message} message
*/
removeMessage(state, message) {
const index = state.currentMessageList.findIndex(({ ID }) => ID === message.ID)
if (index >= 0) {
state.currentMessageList.splice(index, 1)
}
/**
* 更新当前会话
* 调用时机: 切换会话时
* @param {Object} state
* @param {Conversation} conversation
*/
updateCurrentConversation(state, conversation) {
state.currentConversation = conversation
state.currentMessageList = []
state.nextReqMessageID = ''
state.isCompleted = false
},
/**
* 更新会话列表
* 调用时机:触发会话列表更新事件时。CONVERSATION_LIST_UPDATED
* @param {Object} state
* @param {Conversation[]} conversationList
*/
updateConversationList(state, conversationList) {
state.conversationList = conversationList
},
/**
* 重置当前会话
* 调用时机:需要重置当前会话时,例如:当前会话是一个群组,正好被踢出群时(被踢群事件触发),重置当前会话
* @param {Object} state
*/
resetCurrentConversation(state) {
state.currentConversation = {}
},
/**
* 将消息插入当前会话列表
* 调用时机:收/发消息事件触发时
* @param {Object} state
* @param {Message[]|Message} data
* @returns
*/
pushCurrentMessageList(state, data) {
// 还没当前会话,则跳过
if (!state.currentConversation.conversationID) {
return
}
if (Array.isArray(data)) {
// 筛选出当前会话的消息
const result = data.filter(item => item.conversationID === state.currentConversation.conversationID)
state.currentMessageList = [...state.currentMessageList, ...result]
filterCallingMessage(state.currentMessageList)
} else if (data.conversationID === state.currentConversation.conversationID) {
state.currentMessageList = [...state.currentMessageList, data]
filterCallingMessage(state.currentMessageList)
}
},
/**
* 从当前消息列表中删除某条消息
* @param {Object} state
* @param {Message} message
*/
removeMessage(state, message) {
const index = state.currentMessageList.findIndex(({ID}) => ID === message.ID)
if (index >= 0) {
state.currentMessageList.splice(index, 1)
}
},
reset(state) {
Object.assign(state, {
currentConversation: {},
currentMessageList: [],
nextReqMessageID: '',
isCompleted: false, // 当前会话消息列表是否已经拉完了所有消息
conversationList: []
})
}
},
reset(state) {
Object.assign(state, {
currentConversation: {},
currentMessageList: [],
nextReqMessageID: '',
isCompleted: false, // 当前会话消息列表是否已经拉完了所有消息
conversationList: []
})
}
},
actions: {
/**
* 获取消息列表
* 调用时机:打开某一会话时或下拉获取历史消息时
* @param {Object} context
* @param {String} conversationID
*/
getMessageList(context, conversationID) {
if (context.state.isCompleted) {
context.commit('showMessage', {
message: '已经没有更多的历史消息了哦',
type: 'info'
})
return
}
const { nextReqMessageID, currentMessageList } = context.state
tim.getMessageList({ conversationID, nextReqMessageID, count: 15 }).then(imReponse => {
// 更新messageID,续拉时要用到
context.state.nextReqMessageID = imReponse.data.nextReqMessageID
context.state.isCompleted = imReponse.data.isCompleted
// 更新当前消息列表,从头部插入
context.state.currentMessageList = [...imReponse.data.messageList, ...currentMessageList]
filterCallingMessage(context.state.currentMessageList)
actions: {
/**
* 获取消息列表
* 调用时机:打开某一会话时或下拉获取历史消息时
* @param {Object} context
* @param {String} conversationID
*/
getMessageList(context, conversationID) {
if (context.state.isCompleted) {
context.commit('showMessage', {
message: '已经没有更多的历史消息了哦',
type: 'info'
})
return
}
const {nextReqMessageID, currentMessageList} = context.state
tim.getMessageList({conversationID, nextReqMessageID, count: 15}).then(imReponse => {
// 更新messageID,续拉时要用到
context.state.nextReqMessageID = imReponse.data.nextReqMessageID
context.state.isCompleted = imReponse.data.isCompleted
// 更新当前消息列表,从头部插入
context.state.currentMessageList = [...imReponse.data.messageList, ...currentMessageList]
filterCallingMessage(context.state.currentMessageList)
})
},
/**
* 切换会话
* 调用时机:切换会话时
* @param {Object} context
* @param {String} conversationID
*/
checkoutConversation(context, conversationID) {
context.commit('resetCurrentMemberList')
// 1.切换会话前,将切换前的会话进行已读上报
if (context.state.currentConversation.conversationID) {
const prevConversationID = context.state.currentConversation.conversationID
tim.setMessageRead({ conversationID: prevConversationID })
}
// 2.待切换的会话也进行已读上报
tim.setMessageRead({ conversationID })
// 3. 获取会话信息
return tim.getConversationProfile(conversationID).then(({ data }) => {
// 3.1 更新当前会话
context.commit('updateCurrentConversation', data.conversation)
// 3.2 获取消息列表
context.dispatch('getMessageList', conversationID)
// 3.3 拉取第一页群成员列表
if (data.conversation.type === TIM.TYPES.CONV_GROUP) {
return context.dispatch('getGroupMemberList', data.conversation.groupProfile.groupID)
})
},
/**
* 切换会话
* 调用时机:切换会话时
* @param {Object} context
* @param {String} conversationID
*/
checkoutConversation(context, conversationID) {
context.commit('resetCurrentMemberList')
// 1.切换会话前,将切换前的会话进行已读上报
if (context.state.currentConversation.conversationID) {
const prevConversationID = context.state.currentConversation.conversationID
tim.setMessageRead({conversationID: prevConversationID})
}
// 2.待切换的会话也进行已读上报
tim.setMessageRead({conversationID})
// 3. 获取会话信息
return tim.getConversationProfile(conversationID).then(({data}) => {
// 3.1 更新当前会话
context.commit('updateCurrentConversation', data.conversation)
// 3.2 获取消息列表
context.dispatch('getMessageList', conversationID)
// 3.3 拉取第一页群成员列表
if (data.conversation.type === TIM.TYPES.CONV_GROUP) {
context.dispatch('getGroupUnMemberList', data.conversation.groupProfile.groupID)
return context.dispatch('getGroupMemberList', data.conversation.groupProfile.groupID)
}
return Promise.resolve()
})
}
return Promise.resolve()
})
}
}
}
export default conversationModules
......
......@@ -9,10 +9,16 @@ const friendModules = {
getters: {},
mutations: {
updateFriendList(state, friendList) {
// state.friendList = friendList
state.userList = friendList.user
state.adminList = friendList.admin
state.schemeList = friendList.scheme
state.friendList = friendList
},
updateAdminList(state, friendList) {
state.adminList = friendList
},
updateSchemeList(state, friendList) {
state.schemeList = friendList
},
updateUserList(state, friendList) {
state.userList = friendList
},
reset(state) {
Object.assign(state, {friendList: [], createGroupModelVisible: false})
......
import tim from 'tim'
import Helper from '../../utils/helper'
const groupModules = {
state: {
groupList: [],
currentMemberList: [],
createGroupModelVisible: false
},
getters: {
hasGroupList: state => state.groupList.length > 0
},
mutations: {
updateGroupList(state, groupList) {
state.groupList = groupList
},
updateCreateGroupModelVisible(state, visible) {
state.createGroupModelVisible = visible
},
updateCurrentMemberList(state, memberList) {
state.currentMemberList = [...state.currentMemberList, ...memberList]
},
deleteGroupMemeber(state, userID) {
state.currentMemberList = state.currentMemberList.filter((member) => member.userID !== userID)
},
deleteGroupMemberList(state, userIDList) {
state.currentMemberList = state.currentMemberList.filter((member) => !userIDList.includes(member.userID))
},
resetCurrentMemberList(state) {
state.currentMemberList = []
},
reset(state) {
Object.assign(state, {
state: {
groupList: [],
currentMemberList: [],
currentUnMemberList: [],
currentUnMemberPage: 1,
createGroupModelVisible: false
})
}
},
actions: {
updateGroupList(context, groupList) {
context.commit('updateGroupList', groupList)
},
getGroupMemberList(context, groupID) {
return tim.getGroupMemberList({
groupID: groupID,
offset: context.state.currentMemberList.length,
count: 30
}).then((imResponse) => {
context.commit('updateCurrentMemberList', imResponse.data.memberList)
return imResponse
})
getters: {
hasGroupList: state => state.groupList.length > 0
},
mutations: {
updateGroupList(state, groupList) {
state.groupList = groupList
},
updateCreateGroupModelVisible(state, visible) {
state.createGroupModelVisible = visible
},
updateCurrentMemberList(state, memberList) {
state.currentMemberList = [...state.currentMemberList, ...memberList]
},
updateCurrentUnMemberList(state, memberList) {
state.currentUnMemberList = [...state.currentUnMemberList, ...memberList]
state.currentUnMemberPage = state.currentUnMemberPage + 1
},
deleteGroupMember(state, userID) {
state.currentMemberList = state.currentMemberList.filter((member) => member.userID !== userID)
},
deleteGroupMemberList(state, userIDList) {
state.currentMemberList = state.currentMemberList.filter((member) => !userIDList.includes(member.userID))
},
resetCurrentMemberList(state) {
state.currentMemberList = []
state.currentUnMemberList = []
state.currentUnMemberPage = 1
},
reset(state) {
Object.assign(state, {
groupList: [],
currentMemberList: [],
createGroupModelVisible: false
})
}
},
actions: {
updateGroupList(context, groupList) {
context.commit('updateGroupList', groupList)
},
getGroupMemberList(context, groupID) {
return tim.getGroupMemberList({
groupID: groupID,
offset: context.state.currentMemberList.length,
count: 30
}).then((imResponse) => {
context.commit('updateCurrentMemberList', imResponse.data.memberList)
return imResponse
})
},
getGroupUnMemberList(context, groupID) {
Helper.groupMemberList(groupID, {page: context.state.currentUnMemberPage, size: 20}).then(res => {
context.commit('updateCurrentUnMemberList', res.data)
})
}
}
}
}
export default groupModules
......
import tim from '../../tim'
import Helper from '../../utils/helper'
const user = {
state: {
......@@ -36,7 +37,7 @@ const user = {
login(context, userID) {
tim.login({
userID,
userSig: window.genTestUserSig(userID).userSig
userSig: Helper.getSignature(userID)
}).then(() => {
context.commit('toggleIsLogin', true)
context.commit('startComputeCurrent')
......
......@@ -2,9 +2,7 @@ import TIM from 'tim-js-sdk'
import COSSDK from 'cos-js-sdk-v5'
// 初始化 SDK 实例
const tim = TIM.create({
SDKAppID: window.genTestUserSig('').SDKAppID
})
const tim = TIM.create({SDKAppID: parseInt(process.env.VUE_APP_API_KEY)})
window.setLogLevel = tim.setLogLevel
......
import TRTCCalling from 'trtc-calling-js'
let options = {
SDKAppID: window.genTestUserSig('').SDKAppID // 接入时需要将0替换为您的云通信应用的 SDKAppID
}
let options = {SDKAppID: parseInt(process.env.VUE_APP_API_KEY)}
const trtcCalling = new TRTCCalling(options)
......
import request from './request'
export default class Helper {
constructor() {
static getKey() {
return parseInt(process.env.VUE_APP_API_KEY)
}
static getSecret() {
return process.env.VUE_APP_API_SECRET
}
static getExpire() {
return parseInt(process.env.VUE_APP_API_EXPIRETIME)
}
static getUrlKey(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [, ''])[1].replace(/\+/g, '%20')) || null
}
......@@ -12,6 +23,11 @@ export default class Helper {
return this.getUrlKey('token')
}
static getSignature(userID) {
const generator = new window.LibGenerateTestUserSig(this.getKey(), this.getSecret(), this.getExpire())
return generator.genTestUserSig(userID)
}
/**
*
* @returns {Promise<never>|Promise<{sign, id}>}
......@@ -19,18 +35,22 @@ export default class Helper {
static verifyToken() {
return request.get('auth').then(res => {
const {esm_id} = res.data
return Promise.resolve({id: esm_id, sign: window.genTestUserSig(esm_id).userSig})
return Promise.resolve({id: esm_id, sign: this.getSignature(esm_id)})
})
}
static contactList() {
return request.get('im/contacts')
static contactList(params = {}) {
return request.get('im/contacts', {params})
}
static groupList(params = {}) {
return request.get('im/groups', {params})
}
static groupMemberList(group, params = {}) {
return request.get(`im/groups/${group}/members`, {params})
}
static joinGroup(id, member) {
return request.post(`im/groups/${id}/members`, {member})
}
......
......@@ -5,7 +5,7 @@ import tim from '../tim'
const service = axios.create({
baseURL: 'http://spread_dev.hikoon.com/mapi',
baseURL: process.env.VUE_APP_API_URL,
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
......
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
return path.join(__dirname, dir)
}
module.exports = {
publicPath: './',
assetsDir: './',
productionSourceMap: false,
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
.set('tim', resolve('src/tim.js'))
// 删除预加载
config.plugins.delete('preload')
config.plugins.delete('prefetch')
// 压缩代码
config.optimization.minimize(true)
// 分割代码
config.optimization.splitChunks({
chunks: 'all'
})
},
css: {
extract: true,
sourceMap: false,
loaderOptions: {
stylus: {
'resolve url': true,
// 自定义主题场景
import: [path.resolve(__dirname, './src/assets/css/base.styl')]
}
publicPath: './',
assetsDir: './',
productionSourceMap: false,
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
.set('tim', resolve('src/tim.js'))
// 删除预加载
config.plugins.delete('preload')
config.plugins.delete('prefetch')
// 压缩代码
config.optimization.minimize(true)
// 分割代码
config.optimization.splitChunks({
chunks: 'all'
})
},
css: {
extract: true,
sourceMap: false,
loaderOptions: {
stylus: {
'resolve url': true,
// 自定义主题场景
import: [path.resolve(__dirname, './src/assets/css/base.styl')]
}
}
}
}
}
......
......@@ -1249,6 +1249,11 @@ acorn@^7.1.1:
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
add@^2.0.6:
version "2.0.6"
resolved "https://registry.npmjs.org/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235"
integrity sha1-JI8Kn25aUo7yKV2+7DBTITCuIjU=
address@^1.0.3:
version "1.1.2"
resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
......@@ -5100,6 +5105,11 @@ loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4
emojis-list "^3.0.0"
json5 "^1.0.1"
loadsh@^0.0.4:
version "0.0.4"
resolved "https://registry.npmjs.org/loadsh/-/loadsh-0.0.4.tgz#5314babd12bb13315dde024a4ca70758c5489d2d"
integrity sha512-U+wLL8InpfRalWrr+0SuhWgGt10M4OyAk6G8xCYo2rwpiHtxZkWiFpjei0vO463ghW8LPCdhqQxXlMy2qicAEw==
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
......@@ -8689,6 +8699,11 @@ yargs@^16.0.0:
y18n "^5.0.5"
yargs-parser "^20.2.2"
yarn@^1.22.10:
version "1.22.10"
resolved "https://registry.npmjs.org/yarn/-/yarn-1.22.10.tgz#c99daa06257c80f8fa2c3f1490724e394c26b18c"
integrity sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA==
yorkie@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz#92411912d435214e12c51c2ae1093e54b6bb83d9"
......