Commit 6425bbce 6425bbcee7a7fe9dd058ffda69cda23d46fc50a7 by huhai

im相关改造

1 parent 2603d85b
......@@ -7,3 +7,5 @@ VUE_APP_API_SECRET='eb219c4b42bdbbf5dca38f21f5be26ab38f1c157d0ba4277d5acce6507f2
#VUE_APP_API_KEY=1400514886
#VUE_APP_API_SECRET='9c0cbbb0f08e4087e1b06295cd32fc1f9f368c011539123517f769c59274147a'
VUE_APP_API_EXPIRETIME=604800
VUE_APP_API_UNI_URL='https://spread-dev.hikoon.com/api'
......
......@@ -5,3 +5,4 @@ VUE_APP_API_URL='http://spread_dev.hikoon.com/mapi'
VUE_APP_API_KEY=1400514950
VUE_APP_API_SECRET='eb219c4b42bdbbf5dca38f21f5be26ab38f1c157d0ba4277d5acce6507f2d727'
VUE_APP_API_EXPIRETIME=604800
VUE_APP_API_UNI_URL='https://spread.hikoon.com/api'
\ No newline at end of file
......
......@@ -9,8 +9,8 @@
],
"rules": {
"no-console": 'off',
"quotes": ["error", "single"],
"semi": ["error","never"],
// "quotes": ["error", "single"],
// "semi": ["error","never"],
"space-before-blocks": "error",
"space-unary-ops": "error"
},
......
<template>
<div class="group-member-list-wrapper">
<div class="header">
<span class="member-count text-ellipsis">群成员:{{ currentConversation.groupProfile.memberCount }}</span>
<popover v-show="currentUnMemberList.length !== 0" v-model="addGroupMemberVisible">
<add-group-member :group="currentConversation.groupProfile.groupID"/>
<span class="member-count text-ellipsis"
>群成员:{{ currentConversation.groupProfile.memberCount }}</span
>
<popover
v-show="currentUnMemberList.length !== 0"
v-model="addGroupMemberVisible"
>
<add-group-member :group="currentConversation.groupProfile.groupID" />
<div slot="reference" class="btn-add-member" title="添加群成员">
<span class="tim-icon-friend-add"></span>
</div>
......@@ -13,13 +18,24 @@
<div class="group-member-list">
<div v-for="member in members" :key="member.userID">
<popover placement="right" :key="member.userID">
<group-member-info :member="member"/>
<div slot="reference" class="group-member" @click="currentMemberID = member.userID">
<avatar :title=getGroupMemberAvatarText(member.role) :src="member.avatar"/>
<group-member-info :member="member" />
<div
slot="reference"
class="group-member"
@click="currentMemberID = member.userID"
>
<avatar
:title="getGroupMemberAvatarText(member.role)"
:src="member.avatar"
/>
<div class="member-name text-ellipsis">
<span v-if="member.nameCard" :title=member.nameCard>{{ member.nameCard }}</span>
<span v-else-if="member.nick" :title=member.nick>{{ member.nick }}</span>
<span v-else :title=member.userID>{{ member.userID }}</span>
<span v-if="member.nameCard" :title="member.nameCard">{{
member.nameCard
}}</span>
<span v-else-if="member.nick" :title="member.nick">{{
member.nick
}}</span>
<span v-else :title="member.userID">{{ member.userID }}</span>
</div>
</div>
</popover>
......@@ -27,66 +43,71 @@
</div>
</div>
<div class="more">
<el-button v-if="showLoadMore" type="text"
@click="loadMore">查看更多
<el-button v-if="showLoadMore" type="text" @click="loadMore"
>查看更多
</el-button>
</div>
</div>
</template>
<script>
import {Popover} from 'element-ui'
import {mapState} from 'vuex'
import AddGroupMember from './add-group-member.vue'
import GroupMemberInfo from './group-member-info.vue'
import { Popover } from "element-ui";
import { mapState } from "vuex";
import AddGroupMember from "./add-group-member.vue";
import GroupMemberInfo from "./group-member-info.vue";
export default {
data() {
return {
addGroupMemberVisible: false,
addGroupMemberList: [],
currentMemberID: '',
count: 30 // 显示的群成员数量
}
currentMemberID: "",
count: 30, // 显示的群成员数量
};
},
props: ['groupProfile'],
props: ["groupProfile"],
components: {
Popover,
AddGroupMember,
GroupMemberInfo
GroupMemberInfo,
},
computed: {
...mapState({
currentConversation: state => state.conversation.currentConversation,
currentMemberList: state => state.group.currentMemberList,
currentUnMemberList: state => state.group.currentUnMemberList
currentConversation: (state) => state.conversation.currentConversation,
currentMemberList: (state) => state.group.currentMemberList,
currentUnMemberList: (state) => state.group.currentUnMemberList,
}),
showLoadMore() {
return this.members.length < this.groupProfile.memberCount
return this.members.length < this.groupProfile.memberCount;
},
members() {
return this.currentMemberList.slice(0, this.count)
}
console.log(
"currentMemberList is ",
this.currentMemberList.slice(0, this.count)
);
return this.currentMemberList.slice(0, this.count);
},
},
methods: {
getGroupMemberAvatarText(role) {
switch (role) {
case 'Owner':
return '群主'
case 'Admin':
return '管理员'
case "Owner":
return "群主";
case "Admin":
return "管理员";
default:
return '群成员'
return "群成员";
}
},
loadMore() {
this.$store.dispatch('getGroupMemberList', this.groupProfile.groupID)
this.$store
.dispatch("getGroupMemberList", this.groupProfile.groupID)
.then(() => {
this.count += 30
})
this.count += 30;
});
},
}
}
},
};
</script>
<style lang="stylus" scoped>
......@@ -167,6 +188,4 @@ export default {
// text-align: center;
// line-height: 30px;
// }
</style>
......
<template>
<div class="chat-bubble" @mousedown.stop @contextmenu.prevent>
<el-dropdown trigger="" ref="dropdown" v-if="!message.isRevoked" @command="handleCommand">
<el-dropdown
trigger=""
ref="dropdown"
v-if="!message.isRevoked"
@command="handleCommand"
>
<div style="display: flex">
<div v-if="isMine && messageReadByPeer" class="message-status">
<span>{{messageReadByPeer}}</span>
<span>{{ messageReadByPeer }}</span>
</div>
<div class="message-content" :class="bubbleStyle">
<slot></slot>
......@@ -15,136 +20,165 @@
</el-dropdown-menu>
</el-dropdown>
<div class="group-tip-element-wrapper" v-if="message.isRevoked">
{{text}}
<el-button type="text" size="mini" class="edit-button" v-show="isEdit" @click="reEdit">&nbsp;重新编辑</el-button>
{{ text }}
<el-button
type="text"
size="mini"
class="edit-button"
v-show="isEdit"
@click="reEdit"
>&nbsp;重新编辑</el-button
>
</div>
</div>
</template>
<script>
export default {
name: 'MessageBubble',
name: "MessageBubble",
data() {
return {
isTimeout: false
}
isTimeout: false,
};
},
props: {
isMine: {
type: Boolean
type: Boolean,
},
isNew: {
type: Boolean
type: Boolean,
},
message: {
type: Object,
required: true
}
required: true,
},
},
created() {
this.isTimeoutHandler()
this.isTimeoutHandler();
},
mounted() {
if (this.$refs.dropdown && this.$refs.dropdown.$el) {
this.$refs.dropdown.$el.addEventListener('mousedown', this.handleDropDownMousedown)
this.$refs.dropdown.$el.addEventListener(
"mousedown",
this.handleDropDownMousedown
);
}
},
beforeDestroy() {
if (this.$refs.dropdown && this.$refs.dropdown.$el) {
this.$refs.dropdown.$el.removeEventListener('mousedown', this.handleDropDownMousedown)
this.$refs.dropdown.$el.removeEventListener(
"mousedown",
this.handleDropDownMousedown
);
}
},
computed: {
bubbleStyle() {
let classString = ''
let classString = "";
if (this.isMine) {
classString += 'message-send'
classString += "message-send";
} else {
classString += 'message-received'
classString += "message-received";
}
if (this.isNew) {
classString += 'new'
classString += "new";
}
return classString
return classString;
},
text() {
if (this.message.conversationType === this.TIM.TYPES.CONV_C2C && !this.isMine) {
return '对方撤回了一条消息'
if (
this.message.conversationType === this.TIM.TYPES.CONV_C2C &&
!this.isMine
) {
return "对方撤回了一条消息";
}
if (this.message.conversationType === this.TIM.TYPES.CONV_GROUP && !this.isMine) {
return `${this.message.from}撤回了一条消息`
if (
this.message.conversationType === this.TIM.TYPES.CONV_GROUP &&
!this.isMine
) {
return `${this.message.nick || this.message.from}撤回了一条消息`;
}
return '你撤回了一条消息'
return "你撤回了一条消息";
},
messageReadByPeer() {
if (this.message.status !== 'success') {
return false
if (this.message.status !== "success") {
return false;
}
if (this.message.conversationType === this.TIM.TYPES.CONV_C2C && this.message.isPeerRead) {
return '已读'
if (
this.message.conversationType === this.TIM.TYPES.CONV_C2C &&
this.message.isPeerRead
) {
return "已读";
}
if (this.message.conversationType === this.TIM.TYPES.CONV_C2C && !this.message.isPeerRead) {
return '未读'
if (
this.message.conversationType === this.TIM.TYPES.CONV_C2C &&
!this.message.isPeerRead
) {
return "未读";
}
return ''
return "";
},
isEdit() {
if (!this.isMine) {
return false
return false;
}
if (this.message.type !== this.TIM.TYPES.MSG_TEXT) {
return false
return false;
}
if (this.isTimeout) {
return false
return false;
}
return true
return true;
},
},
methods: {
handleDropDownMousedown(e) {
if (!this.isMine || this.isTimeout) {
return
return;
}
if (e.buttons === 2) {
if (this.$refs.dropdown.visible) {
this.$refs.dropdown.hide()
this.$refs.dropdown.hide();
} else {
this.$refs.dropdown.show()
this.$refs.dropdown.show();
}
}
},
handleCommand(command) {
switch (command) {
case 'revoke':
this.tim.revokeMessage(this.message).then(() => {
this.isTimeoutHandler()
}).catch((err) => {
this.$store.commit('showMessage', {
message: err,
type: 'warning'
})
case "revoke":
this.tim
.revokeMessage(this.message)
.then(() => {
this.isTimeoutHandler();
})
break
case 'delete':
break
.catch((err) => {
this.$store.commit("showMessage", {
message: err,
type: "warning",
});
});
break;
case "delete":
break;
default:
break
break;
}
},
isTimeoutHandler() { // 从发送消息时间开始算起,两分钟内可以编辑
let now = new Date()
isTimeoutHandler() {
// 从发送消息时间开始算起,两分钟内可以编辑
let now = new Date();
if (parseInt(now.getTime() / 1000) - this.message.time > 2 * 60) {
this.isTimeout = true
return
this.isTimeout = true;
return;
}
setTimeout(this.isTimeoutHandler, 1000)
setTimeout(this.isTimeoutHandler, 1000);
},
reEdit() {
this.$bus.$emit('reEditMessage', this.message.payload.text)
}
}
}
this.$bus.$emit("reEditMessage", this.message.payload.text);
},
},
};
</script>
<style lang="stylus" scoped>
......
<template>
<message-bubble :isMine="isMine" :message="message">
<div class="m-e-content" @click="goAudioDetail">
<img :src="coverUrl" class="covor" />
<span class="title" v-if="name">{{ name }}</span>
<span class="title" v-else="name">未命名音乐文件</span>
<!-- <div style="position: absolute; right: 15px">
<u-icon name="list" color="#fff" size="32"></u-icon>
</div> -->
</div>
</message-bubble>
<!-- <div class="chat-bubble">
<div class="message-content" :class="isMine ? 'message-send' : 'message-received'">
<template v-for="(item, index) in contentList">
<span :key="index" v-if="item.name === 'text'">{{ item.text }}</span>
<img v-else-if="item.name === 'img'" :src="item.src" width="20px" height="20px" :key="index"/>
</template>
</div>
</div> -->
</template>
<script>
import MessageBubble from "../message-bubble";
export default {
name: "AudioElement",
components: {
MessageBubble,
},
props: {
payload: {
type: Object,
required: true,
},
message: {
type: Object,
required: true,
},
isMine: {
type: Boolean,
},
},
methods: {
goAudioDetail() {
console.log("in goAudioDetail");
// this.$emit("goAudioDetail");
},
previewImage() {
uni.previewImage({
current: 0,
urls: [this.imagePath],
});
},
},
computed: {
audioUrl() {
return this.payload.description;
},
coverUrl() {
try {
let res = JSON.parse(this.payload.extension);
return res.cover_url;
} catch (e) {}
return "";
},
name() {
try {
let res = JSON.parse(this.payload.extension);
return res.name;
} catch (e) {}
return "";
},
},
mounted() {
console.log("this.message is ", this.message);
},
};
</script>
<style lang="stylus" scoped>
.covor{
width: 80px;
height: 80px;
// border-top-left-radius: 10px;
// border-bottom-left-radius: 10px;
border-radius:6px;
}
.m-e-content{
display: flex;
flex-direction: row;
align-items: center;
width:250px;
height: 80px
// background-color: #2D2F39;
position: relative;
}
.title{
width: 150px;
// color: #E9E9EA;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
/* #ifndef APP-PLUS-NVUE */
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* #endif */
/* #ifdef APP-PLUS-NVUE */
lines: 2;
text-overflow:ellipsis;
/* #endif */
margin-left: 15px
}
</style>
......@@ -2,18 +2,36 @@
<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"/>
: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 })"
:payload="
Object.assign(message.payload, {
videoUrl: message.payload.description,
})
"
:message="message"
/>
<audio-element
v-else-if="this.payload.data === 'audio'"
:isMine="isMine"
:payload="message.payload"
:message="message"
/>
<template v-else>
<message-bubble :isMine=isMine :message=message>
<message-group-live-status v-if="text.isFromGroupLive && text.isFromGroupLive === 1" :liveInfo='text'/>
<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>
......@@ -21,74 +39,79 @@
</template>
<script>
import {mapState} from 'vuex'
import MessageBubble from '../message-bubble'
import MessageGroupLiveStatus from '../message-group-live-status'
import ImageElement from '../message-elements/image-element.vue'
import VideoElement from './video-element'
import { mapState } from "vuex";
import MessageBubble from "../message-bubble";
import MessageGroupLiveStatus from "../message-group-live-status";
import ImageElement from "../message-elements/image-element.vue";
import VideoElement from "./video-element";
import AudioElement from "./audio-element.vue";
export default {
name: 'CustomElement',
name: "CustomElement",
props: {
payload: {
type: Object,
required: true
required: true,
},
message: {
type: Object,
required: true
required: true,
},
isMine: {
type: Boolean
}
type: Boolean,
},
},
components: {
VideoElement,
MessageBubble,
MessageGroupLiveStatus,
ImageElement,
AudioElement,
},
computed: {
...mapState({
currentUserProfile: state => state.user.currentUserProfile
currentUserProfile: (state) => state.user.currentUserProfile,
}),
text() {
return this.translateCustomMessage(this.payload)
return this.translateCustomMessage(this.payload);
},
rate() {
return parseInt(this.payload.description)
}
return parseInt(this.payload.description);
},
},
methods: {
guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
})
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
/[xy]/g,
function (c) {
let r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
}
);
},
translateCustomMessage(payload) {
let videoPayload = {}
let videoPayload = {};
try {
videoPayload = JSON.parse(payload.data)
videoPayload = JSON.parse(payload.data);
} catch (e) {
videoPayload = {}
videoPayload = {};
}
if (payload.data === 'group_create') {
return `${payload.extension}`
if (payload.data === "group_create") {
return `${payload.extension}`;
}
if (videoPayload.roomId) {
videoPayload.roomId = videoPayload.roomId.toString()
videoPayload.isFromGroupLive = 1
return videoPayload
videoPayload.roomId = videoPayload.roomId.toString();
videoPayload.isFromGroupLive = 1;
return videoPayload;
}
if (payload.text) {
return payload.text
return payload.text;
} else {
return '[自定义消息]'
return "[自定义消息]";
}
},
}
}
},
};
</script>
<style lang="stylus" scoped>
......
import request from './request'
import request from "./request";
export default class Helper {
static getKey() {
return parseInt(process.env.VUE_APP_API_KEY)
return parseInt(process.env.VUE_APP_API_KEY);
}
static getSecret() {
return process.env.VUE_APP_API_SECRET
return process.env.VUE_APP_API_SECRET;
}
static getExpire() {
return parseInt(process.env.VUE_APP_API_EXPIRETIME)
return parseInt(process.env.VUE_APP_API_EXPIRETIME);
}
static getUrlKey(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [, ''])[1].replace(/\+/g, '%20')) || null
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
location.href
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
static getToken() {
return this.getUrlKey('token')
return this.getUrlKey("token");
}
static getSignature(userID) {
const generator = new window.LibGenerateTestUserSig(this.getKey(), this.getSecret(), this.getExpire())
return generator.genTestUserSig(userID)
const generator = new window.LibGenerateTestUserSig(
this.getKey(),
this.getSecret(),
this.getExpire()
);
return generator.genTestUserSig(userID);
}
/**
......@@ -33,29 +41,29 @@ export default class Helper {
* @returns {Promise<never>|Promise<{sign, id}>}
*/
static verifyToken() {
return request.get('auth').then(res => {
const {esm_id} = res.data
return Promise.resolve({id: esm_id, sign: this.getSignature(esm_id)})
})
return request.get("auth").then((res) => {
const { esm_id } = res.data;
return Promise.resolve({ id: esm_id, sign: this.getSignature(esm_id) });
});
}
static contactList(params = {}) {
return request.get('im/contacts', {params})
return request.get("im/contacts", { params });
}
static groupList(params = {}) {
return request.get('im/groups', {params})
return request.get("im/groups", { params });
}
static groupMemberList(group, params = {}) {
return request.get(`im/groups/${group}/members`, {params})
return request.get(`im/groups/${group}/members`, { params });
}
static groupMemberDelete(group, member) {
return request.delete(`im/groups/${group}/members/${member}`)
return request.delete(`im/groups/${group}/members/${member}`);
}
static joinGroup(id, member) {
return request.post(`im/groups/${id}/members`, {member})
return request.post(`im/groups/${id}/members`, { member });
}
}
......
import axios from "axios";
const hservice = axios.create({
baseURL: process.env.VUE_APP_API_UNI_URL,
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000, // request timeout
});
export default hservice;
import axios from 'axios'
import Helper from './helper'
import Store from '../store'
import tim from '../tim'
import axios from "axios";
import Helper from "./helper";
import Store from "../store";
import tim from "../tim";
const service = axios.create({
baseURL: process.env.VUE_APP_API_URL,
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
timeout: 5000, // request timeout
});
service.interceptors.request.use(config => {
config.headers['Authorization'] = 'Bearer ' + Helper.getToken()
config.headers['Accept'] = 'application/json'
return config
}, error => {
return Promise.reject(error)
})
service.interceptors.request.use(
(config) => {
config.headers["Authorization"] = "Bearer " + Helper.getToken();
config.headers["Accept"] = "application/json";
return config;
},
(error) => {
return Promise.reject(error);
}
);
service.interceptors.response.use(response => {
service.interceptors.response.use(
(response) => {
switch (response.data.code) {
case 200:
return response.data
return response.data;
case 401:
Store.commit('showMessage', {type: 'error', message: '身份信息已失效,请重新登录'})
Store.commit("showMessage", {
type: "error",
message: "身份信息已失效,请重新登录",
});
tim.logout().then(() => {
Store.commit('user/toggleIsLogin')
Store.commit('user/stopComputeCurrent')
Store.commit('user/reset')
})
break
Store.commit("user/toggleIsLogin");
Store.commit("user/stopComputeCurrent");
Store.commit("user/reset");
});
break;
default:
return Promise.reject(response.data)
return Promise.reject(response.data);
}
},
(error) => {
return Promise.reject(error);
}
}, error => {
return Promise.reject(error)
})
);
export default service
export default service;
......