Browse Source

群发功能优化

master
xie.bx 3 years ago
parent
commit
c838a69bd0
  1. 6
      im-ui/src/components/chat/ChatGroup.vue
  2. 205
      im-ui/src/components/chat/ChatGroupSide.vue
  3. 4
      im-ui/src/components/chat/ChatItem.vue
  4. 35
      im-ui/src/components/common/FullImage.vue
  5. 20
      im-ui/src/components/common/HeadImage.vue
  6. 139
      im-ui/src/components/common/UserInfo.vue
  7. 4
      im-ui/src/components/group/GroupMember.vue
  8. 7
      im-ui/src/store/index.js
  9. 39
      im-ui/src/store/uiStore.js
  10. 9
      im-ui/src/view/Friend.vue
  11. 42
      im-ui/src/view/Home.vue

6
im-ui/src/components/chat/ChatGroup.vue

@ -284,4 +284,10 @@
font-size: 30px;
}
}
.chat-group-side-box{
border: #dddddd solid 1px;
animation: rtl-drawer-in .3s 1ms;
}
</style>

205
im-ui/src/components/chat/ChatGroupSide.vue

@ -0,0 +1,205 @@
<template>
<div class="chat-group-side">
<div class="group-side-search">
<el-input placeholder="搜索群成员" v-model="searchText">
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-scrollbar class="group-side-scrollbar">
<div class="group-side-member-list">
<div class="group-side-invite">
<div class="invite-member-btn" title="邀请好友进群聊" @click="showAddGroupMember=true">
<i class="el-icon-plus"></i>
</div>
<div class="invite-member-text">邀请</div>
<add-group-member :visible="showAddGroupMember" :groupId="group.id" :members="groupMembers" @reload="$emit('reload')"
@close="showAddGroupMember=false"></add-group-member>
</div>
<div v-for="(member) in groupMembers" :key="member.id">
<group-member class="group-side-member" v-show="!member.quit && member.aliasName.startsWith(searchText)" :member="member"
:showDel="false"></group-member>
</div>
</div>
<el-divider content-position="center"></el-divider>
<el-form labelPosition="top" class="group-side-form" :model="group">
<el-form-item label="群聊名称">
<el-input v-model="group.name" disabled maxlength="20"></el-input>
</el-form-item>
<el-form-item label="群主">
<el-input :value="ownerName" disabled></el-input>
</el-form-item>
<el-form-item label="群公告">
<el-input v-model="group.notice" disabled type="textarea" maxlength="1024" placeholder="群主未设置"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="group.remark" :disabled="!editing" placeholder="群聊的备注仅自己可见" maxlength="20"></el-input>
</el-form-item>
<el-form-item label="我在本群的昵称">
<el-input v-model="group.aliasName" :disabled="!editing" placeholder="xx" maxlength="20"></el-input>
</el-form-item>
<div class="btn-group">
<el-button v-show="editing" type="success" @click="handleSaveGroup()">提交</el-button>
<el-button v-show="!editing" type="primary" @click="editing=!editing">编辑</el-button>
<el-button type="danger" v-show="!isOwner" @click="handleQuit()">退出群聊</el-button>
</div>
</el-form>
</el-scrollbar>
</div>
</template>
<script>
import AddGroupMember from '../group/AddGroupMember.vue';
import GroupMember from '../group/GroupMember.vue';
export default {
name: "chatGroupSide",
components: {
AddGroupMember,
GroupMember
},
data() {
return {
searchText: "",
editing: false,
showAddGroupMember: false
}
},
props: {
group: {
type: Object
},
groupMembers: {
type: Array
}
},
methods: {
handleClose() {
this.$emit('close');
},
loadGroupMembers() {
this.$http({
url: `/api/group/members/${this.group.id}`,
method: "get"
}).then((members) => {
this.groupMembers = members;
})
},
handleSaveGroup() {
let vo = this.group;
this.$http({
url: "/api/group/modify",
method: "put",
data: vo
}).then((group) => {
this.$store.commit("updateGroup", group);
this.$emit('reload');
this.$message.success("修改成功");
})
},
handleQuit() {
this.$confirm('退出群聊后将不再接受群里的消息,确认退出吗?', '确认退出?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: `/api/group/quit/${this.group.id}`,
method: 'delete'
}).then(() => {
this.$store.commit("removeGroup", this.group.id);
this.$store.commit("activeGroup", -1);
this.$store.commit("removeGroupChat", this.group.id);
});
})
},
},
computed: {
ownerName() {
let member = this.groupMembers.find((m) => m.userId == this.group.ownerId);
return member && member.aliasName;
},
isOwner() {
return this.group.ownerId == this.$store.state.userStore.userInfo.id;
}
}
}
</script>
<style lang="scss">
.chat-group-side {
position: relative;
.group-side-member-list {
padding: 10px;
display: flex;
align-items: center;
flex-wrap: wrap;
font-size: 16px;
text-align: center;
.group-side-member {
margin-left: 15px;
}
.group-side-invite {
display: flex;
flex-direction: column;
align-items: center;
width: 50px;
margin-left: 15px;
.invite-member-btn {
width: 100%;
height: 50px;
line-height: 50px;
border: #cccccc solid 1px;
font-size: 25px;
cursor: pointer;
box-sizing: border-box;
&:hover {
border: #aaaaaa solid 1px;
}
}
.invite-member-text {
font-size: 16px;
text-align: center;
width: 100%;
height: 30px;
line-height: 30px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden
}
}
}
.group-side-form {
text-align: left;
padding: 10px;
height: 30%;
.el-form-item {
margin-bottom: 12px;
.el-form-item__label {
padding: 0;
line-height: 30px;
}
.el-textarea__inner {
min-height: 100px !important;
}
}
.btn-group {
text-align: center;
}
}
}
</style>

4
im-ui/src/components/chat/ChatItem.vue

@ -2,9 +2,7 @@
<div class="chat-item" :class="active ? 'active' : ''">
<div class="left">
<head-image :url="chat.headImage" :size="40">
</head-image>
<head-image :url="chat.headImage" :size="40" :id="chat.type=='PRIVATE'?chat.targetId:0"></head-image>
<div v-show="chat.unreadCount>0" class="unread-text">{{chat.unreadCount}}</div>
</div>
<div class="mid">

35
im-ui/src/components/common/FullImage.vue

@ -0,0 +1,35 @@
<template>
<el-dialog width="30%" :visible.sync="visible" :before-close="handleClose">
<img class="full-img" :src="url" />
</el-dialog>
</template>
<script>
export default {
name: "fullImage",
data() {
return {
}
},
methods: {
handleClose() {
this.$emit("close");
}
},
props: {
visible: {
type: Boolean
},
url: {
type: String
}
}
}
</script>
<style>
.full-img {
width: 100%;
height: 100%;
}
</style>

20
im-ui/src/components/common/HeadImage.vue

@ -1,18 +1,21 @@
<template>
<div class="head-image">
<div class="head-image" @click="showUserInfo($event)">
<img :src="url" :style="{width: size+'px',height: size+'px',cursor: 'pointer'}" />
<slot></slot>
</div>
</template>
<script>
export default {
name: "headImage",
data() {
return {}
},
methods: {},
props: {
id:{
type: Number
},
size: {
type: Number,
default: 50
@ -20,6 +23,19 @@
url: {
type: String
}
},
methods:{
showUserInfo(e){
if(this.id && this.id>0){
this.$http({
url: `/api/user/find/${this.id}`,
method: 'get'
}).then((user) => {
this.$store.commit("setUserInfoBoxPos",e);
this.$store.commit("showUserInfoBox",user);
})
}
}
}
}
</script>

139
im-ui/src/components/common/UserInfo.vue

@ -0,0 +1,139 @@
<template>
<div class="user-info-mask" @click="$emit('close')">
<div class="user-info" :style="{left: pos.x+'px',top: pos.y+'px'}" @click.stop>
<div class="user-info-box">
<div class="avatar">
<head-image :url="user.headImageThumb" :size="60" @click.native="showFullImage()"> </head-image>
</div>
<div>
<el-descriptions :column="1" :title="user.userName" class="user-info-items">
<el-descriptions-item label="昵称">{{ user.nickName }}
</el-descriptions-item>
<el-descriptions-item label="签名">{{ user.signature }}
</el-descriptions-item>
</el-descriptions>
</div>
</div>
<el-divider content-position="center"></el-divider>
<div class="user-btn-group">
<el-button v-show="isFriend" type="primary" @click="handleSendMessage()">发消息</el-button>
<el-button v-show="!isFriend" type="primary" @click="handleAddFriend()">加为好友</el-button>
</div>
</div>
</div>
</template>
<script>
import HeadImage from './HeadImage.vue'
export default {
name: "userInfo",
components: {
HeadImage
},
data() {
return {
}
},
props: {
user: {
type: Object
},
pos: {
type: Object
}
},
methods: {
handleSendMessage() {
let user = this.user;
let chat = {
type: 'PRIVATE',
targetId: user.id,
showName: user.nickName,
headImage: user.headImage,
};
this.$store.commit("openChat", chat);
this.$store.commit("activeChat", 0);
if (this.$route.path != "/home/chat") {
this.$router.push("/home/chat");
}
this.$emit("close");
},
handleAddFriend() {
this.$http({
url: "/api/friend/add",
method: "post",
params: {
friendId: this.user.id
}
}).then((data) => {
this.$message.success("添加成功,对方已成为您的好友");
let friend = {
id: this.user.id,
nickName: this.user.nickName,
headImage: this.user.headImageThumb,
online: this.user.online
}
this.$store.commit("addFriend", friend);
})
},
showFullImage(){
if(this.user.headImage){
this.$store.commit("showFullImageBox",this.user.headImage);
}
}
},
computed: {
isFriend() {
let friends = this.$store.state.friendStore.friends;
let friend = friends.find((f) => f.id == this.user.id);
return friend != undefined;
}
}
}
</script>
<style lang="scss">
.user-info-mask {
background-color: rgba($color: #000000, $alpha: 0);
position: absolute;
width: 100%;
height: 100%;
}
.user-info {
position: absolute;
width: 300px;
background-color: white;
border: #dddddd solid 1px;
border-radius: 5px;
padding: 15px;
.user-info-box {
display: flex;
.user-info-items {
margin-left: 10px;
white-space: nowrap;
overflow: hidden;
.el-descriptions__header {
margin-bottom: 5px;
}
.el-descriptions__title {
font-size: 20px;
}
.el-descriptions-item__cell {
padding-bottom: 1px;
}
}
}
.user-btn-group {
text-align: center;
}
}
</style>

4
im-ui/src/components/group/GroupMember.vue

@ -1,15 +1,15 @@
<template>
<div class="group-member">
<head-image :url="member.headImage" :size="50" class="">
<head-image :url="member.headImage" :size="50" :id="member.userId">
<div v-if="showDel" @click.stop="handleDelete()" class="btn-kick el-icon-error"></div>
</head-image>
<div class="member-name">{{member.aliasName}}</div>
</div>
</template>
<script>
import HeadImage from "../common/HeadImage.vue";
export default{
name: "groupMember",
components:{HeadImage},

7
im-ui/src/store/index.js

@ -4,6 +4,7 @@ import chatStore from './chatStore.js';
import friendStore from './friendStore.js';
import userStore from './userStore.js';
import groupStore from './groupStore.js';
import uiStore from './uiStore.js';
import VuexPersistence from 'vuex-persist'
@ -15,10 +16,8 @@ const vuexLocal = new VuexPersistence({
Vue.use(Vuex)
export default new Vuex.Store({
modules: {chatStore,friendStore,userStore,groupStore},
state: {
userInfo: {}
},
modules: {chatStore,friendStore,userStore,groupStore,uiStore},
state: {},
plugins: [vuexLocal.plugin],
mutations: {
initStore(state){

39
im-ui/src/store/uiStore.js

@ -0,0 +1,39 @@
export default {
state: {
userInfo: { // 用户信息窗口
show: false,
user: {},
pos:{
x:0,
y:0
}
},
fullImage: { // 全屏大图
show: false,
url: ""
}
},
mutations: {
showUserInfoBox(state,user){
state.userInfo.show = true;
state.userInfo.user = user;
},
setUserInfoBoxPos(state,pos){
let w = document.documentElement.clientWidth;
let h = document.documentElement.clientHeight;
state.userInfo.pos.x = Math.min(pos.x,w-350);
state.userInfo.pos.y = Math.min(pos.y,h-200);
},
closeUserInfoBox(state){
state.userInfo.show = false;
},
showFullImageBox(state,url){
state.fullImage.show = true;
state.fullImage.url = url;
},
closeFullImageBox(state){
state.fullImage.show = false;
}
},
}

9
im-ui/src/view/Friend.vue

@ -26,7 +26,8 @@
</div>
<div v-show="userInfo.id">
<div class="user-detail">
<head-image class="detail-head-image" :size="200" :url="userInfo.headImage"></head-image>
<head-image class="detail-head-image" :size="200"
:url="userInfo.headImage" @click.native="showFullImage()"></head-image>
<div class="info-item">
<el-descriptions title="好友信息" class="description" :column="1">
<el-descriptions-item label="用户名">{{ userInfo.userName }}
@ -104,6 +105,12 @@
this.$store.commit("activeChat", 0);
this.$router.push("/home/chat");
},
showFullImage(){
if(this.userInfo.headImage){
this.$store.commit('showFullImageBox',this.userInfo.headImage);
}
},
updateFriendInfo(friend, user, index) {
// storestore
friend = JSON.parse(JSON.stringify(friend));

42
im-ui/src/view/Home.vue

@ -1,8 +1,9 @@
<template>
<el-container >
<el-aside width="80px" class="navi-bar">
<div class="user-head-image" @click="onClickHeadImage">
<head-image :url="$store.state.userStore.userInfo.headImageThumb" > </head-image>
<div class="user-head-image">
<head-image :url="$store.state.userStore.userInfo.headImageThumb" :size="60"
@click.native="showSettingDialog=true"> </head-image>
</div>
<el-menu background-color="#333333" text-color="#ddd" style="margin-top: 30px;">
@ -34,15 +35,30 @@
<router-view></router-view>
</el-main>
<setting :visible="showSettingDialog" @close="onCloseSetting()"></setting>
<user-info v-show="uiStore.userInfo.show"
:pos="uiStore.userInfo.pos"
:user="uiStore.userInfo.user"
@close="$store.commit('closeUserInfoBox')"></user-info>
<full-image :visible="uiStore.fullImage.show"
:url="uiStore.fullImage.url"
@close="$store.commit('closeFullImageBox')"
></full-image>
</el-container>
</template>
<script>
import HeadImage from '../components/common/HeadImage.vue';
import Setting from '../components/setting/Setting.vue';
import UserInfo from '../components/common/UserInfo.vue';
import FullImage from '../components/common/FullImage.vue';
export default {
components:{HeadImage,Setting},
components: {
HeadImage,
Setting,
UserInfo,
FullImage
},
data() {
return {
showSettingDialog: false
@ -52,14 +68,11 @@
init(userInfo) {
this.$store.commit("setUserInfo", userInfo);
this.$store.commit("initStore");
console.log("socket");
this.$wsApi.createWebSocket("ws://localhost:8878/im", this.$store);
this.$wsApi.onopen(() => {
console.log("pullUnreadMessage")
this.pullUnreadMessage();
});
this.$wsApi.onmessage((e) => {
console.log(e);
if (e.cmd == 2) {
// 线
this.$message.error("您已在其他地方登陆,将被强制下线");
@ -67,8 +80,7 @@
location.href = "/";
}, 1000)
}
else if(e.cmd==3){
} else if (e.cmd == 3) {
//
this.handlePrivateMessage(e.data);
} else if (e.cmd == 4) {
@ -104,8 +116,6 @@
this.insertPrivateMessage(friend, msg);
this.$store.commit("addFriend", friend);
})
},
insertPrivateMessage(friend, msg) {
let chatInfo = {
@ -156,9 +166,6 @@
location.href = "/";
})
},
onClickHeadImage(){
this.$message.success(JSON.stringify(this.$store.state.userStore.userInfo));
},
onClickSetting() {
this.showSettingDialog = true;
},
@ -166,6 +173,11 @@
this.showSettingDialog = false;
}
},
computed:{
uiStore(){
return this.$store.state.uiStore;
}
},
mounted() {
this.$http({
url: "/api/user/self",
@ -195,8 +207,10 @@
.el-menu {
border: none;
flex: 1;
.el-menu-item {
margin-top: 20px;
.router-link-exact-active span {
color: white !important;
}
@ -220,6 +234,7 @@
font-size: 24px;
text-align: center;
cursor: pointer;
&:hover {
color: white !important;
}
@ -233,5 +248,4 @@
text-align: center;
}
</style>

Loading…
Cancel
Save