Browse Source

ui美化

master
xsx 2 years ago
parent
commit
de6e5a9ac7
  1. 66
      im-ui/src/api/date.js
  2. 10
      im-ui/src/api/wssocket.js
  3. 177
      im-ui/src/components/chat/ChatItem.vue
  4. 20
      im-ui/src/components/chat/ChatMessageItem.vue
  5. 43
      im-ui/src/components/chat/ChatTime.vue
  6. 2
      im-ui/src/components/common/HeadImage.vue
  7. 42
      im-ui/src/components/common/RightMenu.vue
  8. 108
      im-ui/src/components/friend/FriendItem.vue
  9. 31
      im-ui/src/components/group/GroupItem.vue
  10. 3
      im-ui/src/main.js
  11. 6
      im-ui/src/store/chatStore.js
  12. 12
      im-ui/src/view/Chat.vue
  13. 62
      im-ui/src/view/Friend.vue
  14. 50
      im-ui/src/view/Group.vue
  15. 66
      im-uniapp/common/date.js
  16. 53
      im-uniapp/components/chat-item/chat-item.vue
  17. 12
      im-uniapp/components/chat-message-item/chat-message-item.vue
  18. 45
      im-uniapp/components/chat-time/chat-time.vue
  19. 39
      im-uniapp/components/friend-item/friend-item.vue
  20. 21
      im-uniapp/components/group-item/group-item.vue
  21. 14
      im-uniapp/components/pop-menu/pop-menu.vue
  22. 2
      im-uniapp/main.js
  23. 33
      im-uniapp/pages/group/group-info.vue

66
im-ui/src/api/date.js

@ -0,0 +1,66 @@
let toTimeText = (timeStamp, simple) => {
var dateTime = new Date(timeStamp)
var currentTime = Date.parse(new Date()); //当前时间
var timeDiff = currentTime - dateTime; //与当前时间误差
var timeText = '';
if (timeDiff <= 60000) { //一分钟内
timeText = '刚刚';
} else if (timeDiff > 60000 && timeDiff < 3600000) {
//1小时内
timeText = Math.floor(timeDiff / 60000) + '分钟前';
} else if (timeDiff >= 3600000 && timeDiff < 86400000 && !isYestday(dateTime)) {
//今日
timeText = formatDateTime(dateTime).substr(11, 5);
} else if (isYestday(dateTime)) {
//昨天
timeText = '昨天' + formatDateTime(dateTime).substr(11, 5);
} else if (isYear(dateTime)) {
//今年
timeText = formatDateTime(dateTime).substr(5, simple ? 5 : 14);
} else {
//不属于今年
timeText = formatDateTime(dateTime);
if(simple){
timeText = timeText.substring(2,5);
}
}
return timeText;
}
let isYestday = (date) => {
var yesterday = new Date(new Date() - 1000 * 60 * 60 * 24);
return yesterday.getYear() === date.getYear() &&
yesterday.getMonth() === date.getMonth() &&
yesterday.getDate() === date.getDate();
}
let isYear = (date) => {
return date.getYear() === new Date().getYear();
}
let formatDateTime = (date) => {
if (date === '' || !date) {
return ''
}
var dateObject = new Date(date)
var y = dateObject.getFullYear()
var m = dateObject.getMonth() + 1
m = m < 10 ? ('0' + m) : m
var d = dateObject.getDate()
d = d < 10 ? ('0' + d) : d
var h = dateObject.getHours()
h = h < 10 ? ('0' + h) : h
var minute = dateObject.getMinutes()
minute = minute < 10 ? ('0' + minute) : minute
var second = dateObject.getSeconds()
second = second < 10 ? ('0' + second) : second
return y + '/' + m + '/' + d + ' ' + h + ':' + minute + ':' + second
}
export{
toTimeText,
isYestday,
isYear,
formatDateTime
}

10
im-ui/src/api/wssocket.js

@ -86,7 +86,7 @@ let close = () => {
//心跳设置
var heartCheck = {
let heartCheck = {
timeout: 5000, //每段时间发送一次心跳包 这里设置为20s
timeoutObj: null, //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
start: function() {
@ -112,7 +112,7 @@ var heartCheck = {
// 实际调用的方法
function sendMessage(agentData) {
let sendMessage = (agentData) => {
// console.log(globalCallback)
if (websock.readyState === websock.OPEN) {
// 若是ws开启状态
@ -131,16 +131,16 @@ function sendMessage(agentData) {
}
function onMessage(callback) {
let onMessage = (callback) => {
messageCallBack = callback;
}
function onOpen(callback) {
let onOpen = (callback) => {
openCallBack = callback;
}
function onClose(callback) {
let onClose = (callback) => {
closeCallBack = callback;
}
// 将方法暴露出去

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

@ -1,36 +1,53 @@
<template>
<div class="chat-item" :class="active ? 'active' : ''">
<div class="left">
<head-image :url="chat.headImage" :size="40" :id="chat.type=='PRIVATE'?chat.targetId:0"></head-image>
<div class="chat-item" :class="active ? 'active' : ''" @contextmenu.prevent="showRightMenu($event)">
<div class="chat-left">
<head-image :url="chat.headImage" :size="50" :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">
<div>{{ chat.showName}}</div>
<div class="msg-text" v-html="$emo.transform(chat.lastContent)"></div>
</div>
<div class="right ">
<div @click.stop="onClickClose()"><i class="el-icon-close close" style="border: none; font-size: 20px;color: black;" title="关闭"></i></div>
<div class="msg-time">
<chat-time :time="chat.lastSendTime"></chat-time>
<div class="chat-right">
<div class="chat-name">
{{ chat.showName}}
</div>
<div class="chat-content">
<div class="chat-content-text" v-html="$emo.transform(chat.lastContent)"></div>
<div class="chat-time">{{showTime}}</div>
</div>
</div>
<right-menu v-show="rightMenu.show" :pos="rightMenu.pos" :items="rightMenu.items"
@close="rightMenu.show=false" @select="handleSelectMenu"></right-menu>
</div>
</template>
<script>
import ChatTime from "./ChatTime.vue";
import HeadImage from '../common/HeadImage.vue';
import RightMenu from '../common/RightMenu.vue';
export default {
name: "chatItem",
components: {
ChatTime,
HeadImage
HeadImage,
RightMenu
},
data() {
return {}
return {
rightMenu: {
show: false,
pos: {
x: 0,
y: 0
},
items: [{
key: 'TOP',
name: '置顶',
icon: 'el-icon-top'
}, {
key: 'DELETE',
name: '删除',
icon: 'el-icon-delete'
}]
}
}
},
props: {
chat: {
@ -44,55 +61,61 @@
}
},
methods: {
onClickClose(){
this.$emit("del");
showRightMenu(e) {
this.rightMenu.pos = {
x: e.x,
y: e.y
};
this.rightMenu.show = "true";
},
handleSelectMenu(item) {
this.$emit(item.key.toLowerCase(), this.msgInfo);
}
},
computed: {
showTime() {
return this.$date.toTimeText(this.chat.lastSendTime, true)
}
}
}
</script>
<style lang="scss">
<style lang="scss">
.chat-item {
height: 65px;
height: 65px;
display: flex;
margin-bottom: 1px;
position: relative;
padding-left: 15px;
padding-left: 10px;
align-items: center;
padding-right: 5px;
background-color: #fafafa;
white-space: nowrap;
color: black;
cursor: pointer;
&:hover {
background-color: #eeeeee;
}
&.active {
background-color: #dddddd;
background-color: #e8e8f0;
}
&:hover {
.close {
display: block !important;
}
}
.left {
.chat-left {
position: relative;
display: flex;
width: 45px;
height: 45px;
width: 50px;
height: 50px;
.unread-text {
position: absolute;
background-color: #f56c6c;
right: -8px;
top: -8px;
right: -5px;
top: -5px;
color: white;
border-radius: 30px;
padding: 0 5px;
padding: 1px 5px;
font-size: 10px;
text-align: center;
white-space: nowrap;
@ -101,65 +124,45 @@
}
.mid {
margin-left: 15px;
flex: 2;
.chat-right {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
flex-shrink: 0;
overflow: hidden;
padding-left: 10px;
text-align: left;
&>div {
display: flex;
justify-content: flex-start;
align-items: center;
flex: 1;
}
.msg-text {
.chat-name {
font-size: 16px;
color: #888888;
font-weight: 600;
line-height: 30px;
white-space: nowrap;
img{
width: 30px !important;
height: 30px !important;
}
overflow: hidden;
}
}
.right {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-end;
height: 100%;
flex-shrink: 0;
overflow: hidden;
&>div {
.chat-content {
display: flex;
justify-content: flex-start;
align-items: center;
flex: 1;
}
.close {
width: 1.5rem;
height: 1.5rem;
right: 0;
top: 1rem;
cursor: pointer;
display: none;
}
.chat-content-text {
flex: 2;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
.msg-time {
font-size: 14px;
color: #888888;
white-space: nowrap;
img {
width: 30px !important;
height: 30px !important;
}
}
.chat-time {
flex: 1;
font-size: 13px;
text-align: right;
color: #888888;
white-space: nowrap;
overflow: hidden;
}
}
}
}
</style>
</style>

20
im-ui/src/components/chat/ChatMessageItem.vue

@ -8,7 +8,7 @@
<div class="chat-msg-content">
<div class="chat-msg-top">
<span>{{showName}}</span>
<chat-time :time="msgInfo.sendTime"></chat-time>
<span>{{$date.toTimeText(msgInfo.sendTime)}}</span>
</div>
<div class="chat-msg-bottom" @contextmenu.prevent="showRightMenu($event)">
<span class="chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT"
@ -50,14 +50,12 @@
</template>
<script>
import ChatTime from "./ChatTime.vue";
import HeadImage from "../common/HeadImage.vue";
import RightMenu from '../common/RightMenu.vue';
export default {
name: "messageItem",
components: {
ChatTime,
HeadImage,
RightMenu
},
@ -214,10 +212,10 @@
position: relative;
line-height: 35px;
margin-top: 10px;
padding: 10px;
background-color: #eeeeee;
border-radius: 3px;
color: #333;
padding: 14px;
background-color: rgb(235,235,245);
border-radius: 10px;
color: black;
display: block;
font-size: 17px;
text-align: left;
@ -232,9 +230,9 @@
width: 0;
height: 0;
border-style: solid dashed dashed;
border-color: #eeeeee transparent transparent;
border-color: rgb(235,235,245) transparent transparent;
overflow: hidden;
border-width: 10px;
border-width: 13px;
}
}
@ -348,14 +346,14 @@
.chat-msg-text {
margin-left: 10px;
background-color: #5fb878;
background-color: rgb(88, 127, 240);
color: #fff;
vertical-align: top;
&:after {
left: auto;
right: -10px;
border-top-color: #5fb878;
border-top-color: rgb(88, 127, 240);
}
}

43
im-ui/src/components/chat/ChatTime.vue

@ -1,43 +0,0 @@
<template>
<span>{{formatDate}}</span>
</template>
<script>
export default {
name: "chatTime",
data() {
return {}
},
props: {
time: {
type: Number
}
},
computed:{
formatDate(){
let time = new Date(this.time);
let strtime = "";
let todayTime = new Date();
todayTime.setHours(0,0,0,0)
let dayDiff = Math.floor((todayTime.getTime() - time.getTime())/(24*3600*1000)) ;
if (time.getTime() > todayTime.getTime()) {
strtime = time.getHours() <= 9 ? "0" + time.getHours() : time.getHours();
strtime += ":"
strtime += time.getMinutes() <= 9 ? "0" + time.getMinutes() : time.getMinutes();
} else if (dayDiff < 1 ) {
strtime = "昨天";
} else if (dayDiff < 7) {
strtime = `${dayDiff+1}天前`;
} else {
strtime = time.getMonth()+1+"月"+time.getDate()+"日";
}
return strtime;
}
}
}
</script>
<style>
</style>

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

@ -46,7 +46,7 @@
img {
position: relative;
overflow: hidden;
border-radius: 5%;
border-radius: 10%;
}
img:before {

42
im-ui/src/components/common/RightMenu.vue

@ -1,10 +1,12 @@
<template>
<div class="right-menu-mask" @click="close()" @contextmenu.prevent="close()">
<div class="right-menu" :style="{'left':pos.x+'px','top':pos.y+'px'}">
<el-menu background-color="#f5f5f5" text-color="#333333">
<el-menu-item v-for="(item) in items" :key="item.key" :title="item.name" @click="handleSelectMenu(item)">
<i :class="item.icon"></i>
<div class="right-menu" :style="{'left':pos.x+'px','top':pos.y+'px'}">
<el-menu text-color="#333333">
<el-menu-item v-for="(item) in items" :key="item.key" :title="item.name"
@click="handleSelectMenu(item)">
<span :class="item.icon"></span>
<span>{{item.name}}</span>
</el-menu-item>
</el-menu>
</div>
@ -21,16 +23,16 @@
pos: {
type: Object
},
items:{
items: {
type: Array
}
},
methods:{
close(){
methods: {
close() {
this.$emit("close");
},
handleSelectMenu(item){
this.$emit("select",item);
handleSelectMenu(item) {
this.$emit("select", item);
}
}
}
@ -50,14 +52,22 @@
.right-menu {
position: fixed;
box-shadow: 0px 0px 10px #ccc;
.el-menu {
border: 1px solid #b4b4b4;
border-radius: 7px;
overflow: hidden;
.el-menu-item {
height: 40px;
line-height: 40px;
i {
color: #333333;
.el-menu-item {
height: 40px;
line-height: 40px;
span {
font-weight: 600;
}
border-bottom: 1px solid #d0d0d0;
}
}
}
</style>
</style>

108
im-ui/src/components/friend/FriendItem.vue

@ -1,34 +1,58 @@
<template>
<div class="friend-item" :class="active ? 'active' : ''">
<div class="avatar">
<div class="friend-item" :class="active ? 'active' : ''" @contextmenu.prevent="showRightMenu($event)">
<div class="friend-avatar">
<head-image :url="friend.headImage"> </head-image>
</div>
<div class="text">
<div>{{ friend.nickName}}</div>
<div :class="online ? 'online-status online':'online-status'">{{ online?"[在线]":"[离线]"}}</div>
</div>
<div v-if="showDelete" class="close" @click.stop="handleDel()">
<i class="el-icon-close" style="border: none; font-size: 20px;color: black;" title="添加好友"></i>
<div class="friend-info">
<div class="friend-name">{{ friend.nickName}}</div>
<div class="friend-online" :class="friend.online ? 'online':''">{{ friend.online?"[在线]":"[离线]"}}</div>
</div>
<right-menu v-show="menu && rightMenu.show" :pos="rightMenu.pos" :items="rightMenu.items"
@close="rightMenu.show=false" @select="handleSelectMenu"></right-menu>
<slot></slot>
</div>
</template>
<script>
import HeadImage from '../common/HeadImage.vue';
import RightMenu from "../common/RightMenu.vue";
export default {
name: "frinedItem",
components: {
HeadImage
HeadImage,
RightMenu
},
data() {
return {}
return {
rightMenu: {
show: false,
pos: {
x: 0,
y: 0
},
items: [{
key: 'CHAT',
name: '发送消息',
icon: 'el-icon-chat-dot-round'
}, {
key: 'DELETE',
name: '删除好友',
icon: 'el-icon-delete'
}]
}
}
},
methods:{
handleDel(){
console.log("11111111111111111111")
this.$emit('del',this.friend,this.index)
methods: {
showRightMenu(e) {
this.rightMenu.pos = {
x: e.x,
y: e.y
};
this.rightMenu.show = "true";
},
handleSelectMenu(item) {
this.$emit(item.key.toLowerCase(), this.msgInfo);
}
},
props: {
@ -41,16 +65,12 @@
index: {
type: Number
},
showDelete:{
menu: {
type: Boolean,
default: true
}
},
computed: {
online() {
return this.$store.state.friendStore.friends[this.index].online;
}
}
}
</script>
@ -60,11 +80,12 @@
display: flex;
margin-bottom: 1px;
position: relative;
padding-left: 15px;
padding-left: 10px;
align-items: center;
padding-right: 5px;
background-color: #fafafa;
white-space: nowrap;
&:hover {
background-color: #eeeeee;
}
@ -73,48 +94,31 @@
background-color: #dddddd;
}
.close {
width: 1.5rem;
height: 1.5rem;
right: 10px;
top: 1.825rem;
cursor: pointer;
display: none;
}
&:hover {
.close {
display: block;
}
}
.avatar {
.friend-avatar {
display: flex;
justify-content: center;
align-items: center;
width: 45px;
height: 45px;
width: 50px;
height: 50px;
}
.text {
margin-left: 15px;
.friend-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
flex-shrink: 0;
overflow: hidden;
padding-left: 10px;
text-align: left;
&>div {
display: flex;
justify-content: flex-start;
.friend-name {
font-size: 16px;
font-weight: 600;
line-height: 30px;
white-space: nowrap;
overflow: hidden;
}
.online-status {
.friend-online {
font-size: 12px;
font-weight: 600;
&.online {
color: #5fb878;
@ -122,4 +126,4 @@
}
}
}
</style>
</style>

31
im-ui/src/components/group/GroupItem.vue

@ -1,9 +1,9 @@
<template>
<div class="item" :class="active ? 'active' : ''">
<div class="avatar">
<div class="group-item" :class="active ? 'active' : ''">
<div class="group-avatar">
<head-image :url="group.headImage"> </head-image>
</div>
<div class="text">
<div class="group-name">
<div>{{group.remark}}</div>
</div>
</div>
@ -33,12 +33,12 @@
</script>
<style lang="scss" >
.item {
.group-item {
height: 65px;
display: flex;
margin-bottom: 1px;
position: relative;
padding-left: 15px;
padding-left: 10px;
align-items: center;
padding-right: 5px;
background-color: #fafafa;
@ -51,21 +51,20 @@
background-color: #dddddd;
}
.avatar {
width: 45px;
height: 45px;
.group-avatar {
width: 50px;
height: 50px;
}
.text {
display: flex;
flex-direction: column;
justify-content: space-around;
flex: 1;
margin-left: 15px;
.group-name {
padding-left: 10px;
height: 100%;
flex-shrink: 0;
overflow: hidden;
text-align: left;
line-height: 65px;
white-space: nowrap;
overflow: hidden;
font-size: 16px;
font-weight: 600;
}
}
</style>

3
im-ui/src/main.js

@ -10,12 +10,13 @@ import emotion from './api/emotion.js';
import element from './api/element.js';
import store from './store';
import * as enums from './api/enums.js';
import * as date from './api/date.js';
import './utils/directive/dialogDrag';
Vue.use(ElementUI);
// 挂载全局
Vue.prototype.$wsApi = socketApi;
Vue.prototype.$date = date;
Vue.prototype.$http = httpRequest // http请求方法
Vue.prototype.$emo = emotion; // emo表情
Vue.prototype.$elm = element; // 元素操作

6
im-ui/src/store/chatStore.js

@ -57,6 +57,12 @@ export default {
state.activeIndex = state.chats.length - 1;
}
},
moveTop(state,idx){
let chat = state.chats[idx];
// 放置头部
state.chats.splice(idx, 1);
state.chats.unshift(chat);
},
removeGroupChat(state, groupId) {
for (let idx in state.chats) {
if (state.chats[idx].type == 'GROUP' &&

12
im-ui/src/view/Chat.vue

@ -8,7 +8,10 @@
</div>
<el-scrollbar class="l-chat-list" >
<div v-for="(chat,index) in chatStore.chats" :key="index">
<chat-item :chat="chat" :index="index" @click.native="handleActiveItem(index)" @del="handleDelItem(chat,index)"
<chat-item :chat="chat" :index="index"
@click.native="handleActiveItem(index)"
@delete="handleDelItem(index)"
@top="handleTop(index)"
:active="index === chatStore.activeIndex"></chat-item>
</div>
</el-scrollbar>
@ -41,9 +44,12 @@
handleActiveItem(index) {
this.$store.commit("activeChat", index);
},
handleDelItem(chat, index) {
handleDelItem(index) {
this.$store.commit("removeChat", index);
}
},
handleTop(chatIdx) {
this.$store.commit("moveTop", chatIdx);
},
},
computed: {
chatStore() {

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

@ -7,15 +7,16 @@
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-button plain icon="el-icon-plus" style="border: none; padding:12px; font-size: 20px;color: black;" title="添加好友"
@click="handleShowAddFriend()"></el-button>
<el-button plain icon="el-icon-plus" style="border: none; padding:12px; font-size: 20px;color: black;"
title="添加好友" @click="handleShowAddFriend()"></el-button>
<add-friend :dialogVisible="showAddFriend" @close="handleCloseAddFriend">
</add-friend>
</div>
<el-scrollbar class="l-friend-list" >
<el-scrollbar class="l-friend-list">
<div v-for="(friend,index) in $store.state.friendStore.friends" :key="index">
<friend-item v-show="friend.nickName.startsWith(searchText)" :friend="friend" :index="index" :active="index === $store.state.friendStore.activeIndex"
@del="handleDelItem(friend,index)" @click.native="handleActiveItem(friend,index)">
<friend-item v-show="friend.nickName.startsWith(searchText)" :friend="friend" :index="index"
:active="index === $store.state.friendStore.activeIndex" @chat="handleSendMessage(friend)"
@delete="handleDelItem(friend,index)" @click.native="handleActiveItem(friend,index)">
</friend-item>
</div>
</el-scrollbar>
@ -26,8 +27,8 @@
</div>
<div v-show="userInfo.id">
<div class="user-detail">
<head-image class="detail-head-image" :size="200"
:url="userInfo.headImage" @click.native="showFullImage()"></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 }}
@ -40,7 +41,7 @@
</div>
</div>
<div class="btn-group">
<el-button class="send-btn" @click="handleSendMessage()">发消息</el-button>
<el-button class="send-btn" @click="handleSendMessage(userInfo)">消息</el-button>
</div>
</div>
</el-container>
@ -58,6 +59,7 @@
FriendItem,
AddFriend,
HeadImage
},
data() {
return {
@ -75,7 +77,7 @@
},
handleActiveItem(friend, index) {
this.$store.commit("activeFriend", index);
this.loadUserInfo(friend,index);
this.loadUserInfo(friend, index);
},
handleDelItem(friend, index) {
this.$confirm(`确认要解除与 '${friend.nickName}'的好友关系吗?`, '确认解除?', {
@ -93,8 +95,7 @@
})
})
},
handleSendMessage() {
let user = this.userInfo;
handleSendMessage(user) {
let chat = {
type: 'PRIVATE',
targetId: user.id,
@ -105,11 +106,11 @@
this.$store.commit("activeChat", 0);
this.$router.push("/home/chat");
},
showFullImage(){
if(this.userInfo.headImage){
this.$store.commit('showFullImageBox',this.userInfo.headImage);
showFullImage() {
if (this.userInfo.headImage) {
this.$store.commit('showFullImageBox', this.userInfo.headImage);
}
},
updateFriendInfo(friend, user, index) {
// storestore
@ -125,7 +126,7 @@
this.$store.commit("updateChatFromFriend", user);
})
},
loadUserInfo(friend,index){
loadUserInfo(friend, index) {
this.$http({
url: `/user/find/${friend.id}`,
method: 'get'
@ -139,15 +140,15 @@
})
}
},
computed:{
friendStore(){
computed: {
friendStore() {
return this.$store.state.friendStore;
}
},
mounted() {
if(this.friendStore.activeIndex>=0){
if (this.friendStore.activeIndex >= 0) {
let friend = this.friendStore.friends[this.friendStore.activeIndex];
this.loadUserInfo(friend,this.friendStore.activeIndex);
this.loadUserInfo(friend, this.friendStore.activeIndex);
}
}
@ -161,6 +162,7 @@
flex-direction: column;
border: #dddddd solid 1px;
background: white;
.l-friend-header {
height: 50px;
display: flex;
@ -172,8 +174,8 @@
flex: 1;
}
}
.l-friend-ist{
.l-friend-ist {
flex: 1;
}
}
@ -182,6 +184,7 @@
display: flex;
flex-direction: column;
border: #dddddd solid 1px;
.r-friend-header {
width: 100%;
height: 50px;
@ -194,27 +197,26 @@
background-color: white;
border: #dddddd solid 1px;
}
.user-detail {
width: 100%;
display: flex;
padding: 50px 10px 10px 50px;
padding: 50px 80px 20px 80px;
text-align: center;
.info-item {
width: 400px;
height: 200px;
margin-left: 20px;
background-color: #ffffff;
}
.description {
padding: 20px 20px 0px 20px;
}
}
.btn-group {
text-align: left !important;
padding-left: 100px;
padding-left: 120px;
}
}
}
</style>
</style>

50
im-ui/src/view/Group.vue

@ -7,13 +7,13 @@
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
<el-button plain icon="el-icon-plus" style="border: none; padding: 12px; font-size: 20px;color: black;" title="创建群聊"
@click="handleCreateGroup()"></el-button>
<el-button plain icon="el-icon-plus" style="border: none; padding: 12px; font-size: 20px;color: black;"
title="创建群聊" @click="handleCreateGroup()"></el-button>
</div>
<el-scrollbar class="l-group-list">
<div v-for="(group,index) in groupStore.groups" :key="index">
<group-item v-show="group.remark.startsWith(searchText)" :group="group" :active="index === groupStore.activeIndex"
@click.native="handleActiveItem(group,index)">
<group-item v-show="group.remark.startsWith(searchText)" :group="group"
:active="index === groupStore.activeIndex" @click.native="handleActiveItem(group,index)">
</group-item>
</div>
</el-scrollbar>
@ -26,14 +26,16 @@
<div v-show="activeGroup.id">
<div class="r-group-info">
<div>
<file-upload class="avatar-uploader" :action="imageAction" :disabled="!isOwner" :showLoading="true"
:maxSize="maxSize" @success="handleUploadSuccess" :fileTypes="['image/jpeg', 'image/png', 'image/jpg','image/webp']">
<file-upload class="avatar-uploader" :action="imageAction" :disabled="!isOwner"
:showLoading="true" :maxSize="maxSize" @success="handleUploadSuccess"
:fileTypes="['image/jpeg', 'image/png', 'image/jpg','image/webp']">
<img v-if="activeGroup.headImage" :src="activeGroup.headImage" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</file-upload>
<el-button class="send-btn" @click="handleSendMessage()">发送消息</el-button>
</div>
<el-form class="r-group-form" label-width="130px" :model="activeGroup" :rules="rules" ref="groupForm">
<el-form class="r-group-form" label-width="130px" :model="activeGroup" :rules="rules"
ref="groupForm">
<el-form-item label="群聊名称" prop="name">
<el-input v-model="activeGroup.name" :disabled="!isOwner" maxlength="20"></el-input>
</el-form-item>
@ -41,13 +43,15 @@
<el-input :value="ownerName" disabled></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="activeGroup.remark" placeholder="群聊的备注仅自己可见" maxlength="20"></el-input>
<el-input v-model="activeGroup.remark" placeholder="群聊的备注仅自己可见"
maxlength="20"></el-input>
</el-form-item>
<el-form-item label="我在本群的昵称">
<el-input v-model="activeGroup.aliasName" placeholder="" maxlength="20"></el-input>
</el-form-item>
<el-form-item label="群公告">
<el-input v-model="activeGroup.notice" :disabled="!isOwner" type="textarea" maxlength="1024" placeholder="群主未设置"></el-input>
<el-input v-model="activeGroup.notice" :disabled="!isOwner" type="textarea"
maxlength="1024" placeholder="群主未设置"></el-input>
</el-form-item>
<div class="btn-group">
<el-button type="success" @click="handleSaveGroup()">提交</el-button>
@ -60,16 +64,18 @@
<el-scrollbar style="height:400px;">
<div class="r-group-member-list">
<div v-for="(member) in groupMembers" :key="member.id">
<group-member v-show="!member.quit" class="r-group-member" :member="member" :showDel="isOwner&&member.userId!=activeGroup.ownerId"
@del="handleKick"></group-member>
<group-member v-show="!member.quit" class="r-group-member" :member="member"
:showDel="isOwner&&member.userId!=activeGroup.ownerId"
@del="handleKick"></group-member>
</div>
<div class="r-group-invite">
<div class="invite-member-btn" title="邀请好友进群聊" @click="handleInviteMember()">
<i class="el-icon-plus"></i>
</div>
<div class="invite-member-text">邀请</div>
<add-group-member :visible="showAddGroupMember" :groupId="activeGroup.id" :members="groupMembers" @reload="loadGroupMembers"
@close="handleCloseAddGroupMember"></add-group-member>
<add-group-member :visible="showAddGroupMember" :groupId="activeGroup.id"
:members="groupMembers" @reload="loadGroupMembers"
@close="handleCloseAddGroupMember"></add-group-member>
</div>
</div>
</el-scrollbar>
@ -119,12 +125,12 @@
inputErrorMessage: '请输入群聊名称'
}).then((o) => {
let userInfo = this.$store.state.userStore.userInfo;
let data= {
let data = {
name: o.value,
remark: o.value,
aliasName: userInfo.name,
headImage: userInfo.headImage,
headImageThumb: userInfo.headImageThumb,
headImageThumb: userInfo.headImageThumb,
ownerId: userInfo.id
}
this.$http({
@ -182,7 +188,7 @@
this.$store.commit("removeGroup", this.activeGroup.id);
this.$store.commit("activeGroup", -1);
this.$store.commit("removeGroupChat", this.activeGroup.id);
this.activeGroup= {};
this.activeGroup = {};
});
})
@ -254,7 +260,7 @@
isOwner() {
return this.activeGroup.ownerId == this.$store.state.userStore.userInfo.id;
},
imageAction(){
imageAction() {
return `${process.env.VUE_APP_BASE_API}/image/upload`;
}
},
@ -289,8 +295,8 @@
flex: 1;
}
}
.l-group-ist{
.l-group-ist {
flex: 1;
}
}
@ -314,7 +320,7 @@
}
.r-group-container {
padding: 20px;
padding: 50px;
.r-group-info {
display: flex;
@ -358,7 +364,7 @@
}
.send-btn {
margin-top: 10px;
margin-top: 20px;
}
}
@ -410,4 +416,4 @@
}
}
}
</style>
</style>

66
im-uniapp/common/date.js

@ -0,0 +1,66 @@
let toTimeText = (timeStamp, simple) => {
var dateTime = new Date(timeStamp)
var currentTime = Date.parse(new Date()); //当前时间
var timeDiff = currentTime - dateTime; //与当前时间误差
var timeText = '';
if (timeDiff <= 60000) { //一分钟内
timeText = '刚刚';
} else if (timeDiff > 60000 && timeDiff < 3600000) {
//1小时内
timeText = Math.floor(timeDiff / 60000) + '分钟前';
} else if (timeDiff >= 3600000 && timeDiff < 86400000 && !isYestday(dateTime)) {
//今日
timeText = formatDateTime(dateTime).substr(11, 5);
} else if (isYestday(dateTime)) {
//昨天
timeText = '昨天' + formatDateTime(dateTime).substr(11, 5);
} else if (isYear(dateTime)) {
//今年
timeText = formatDateTime(dateTime).substr(5, simple ? 5 : 14);
} else {
//不属于今年
timeText = formatDateTime(dateTime);
if(simple){
timeText = timeText.substring(2,5);
}
}
return timeText;
}
let isYestday = (date) => {
var yesterday = new Date(new Date() - 1000 * 60 * 60 * 24);
return yesterday.getYear() === date.getYear() &&
yesterday.getMonth() === date.getMonth() &&
yesterday.getDate() === date.getDate();
}
let isYear = (date) => {
return date.getYear() === new Date().getYear();
}
let formatDateTime = (date) => {
if (date === '' || !date) {
return ''
}
var dateObject = new Date(date)
var y = dateObject.getFullYear()
var m = dateObject.getMonth() + 1
m = m < 10 ? ('0' + m) : m
var d = dateObject.getDate()
d = d < 10 ? ('0' + d) : d
var h = dateObject.getHours()
h = h < 10 ? ('0' + h) : h
var minute = dateObject.getMinutes()
minute = minute < 10 ? ('0' + minute) : minute
var second = dateObject.getSeconds()
second = second < 10 ? ('0' + second) : second
return y + '/' + m + '/' + d + ' ' + h + ':' + minute + ':' + second
}
export{
toTimeText,
isYestday,
isYear,
formatDateTime
}

53
im-uniapp/components/chat-item/chat-item.vue

@ -4,15 +4,14 @@
<image class="head-image" :src="chat.headImage" mode="aspectFill" lazy-load="true" ></image>
<view v-show="chat.unreadCount>0" class="unread-text">{{chat.unreadCount}}</view>
</view>
<view class="mid">
<view class="show-name">{{ chat.showName}}</view>
<view class="msg-text" v-html="$emo.transform(chat.lastContent)"></view>
</view>
<view class="right ">
<view class="msg-time">
<chat-time :time="chat.lastSendTime"></chat-time>
<view class="chat-right">
<view class="chat-name">
{{ chat.showName}}
</view>
<view class="chat-content">
<view class="chat-content-text" v-html="$emo.transform(chat.lastContent)"></view>
<view class="chat-time">{{$date.toTimeText(chat.lastSendTime)}}</view>
</view>
<view></view>
</view>
</view>
</template>
@ -41,7 +40,7 @@
}
</script>
<style lang="scss" scode>
<style scoped lang="scss">
.chat-item {
height: 120rpx;
display: flex;
@ -85,6 +84,42 @@
}
}
.chat-right {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 20rpx;
text-align: left;
.chat-name {
font-size: 30rpx;
font-weight: 600;
line-height: 60rpx;
white-space: nowrap;
overflow: hidden;
}
.chat-content {
display: flex;
.chat-content-text {
flex: 2;
font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
line-height: 50rpx;
}
.chat-time {
flex: 1;
font-size: 26rpx;
text-align: right;
color: #888888;
white-space: nowrap;
overflow: hidden;
}
}
}
.mid {
margin-left: 20rpx;

12
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -11,7 +11,7 @@
<view class="chat-msg-content" @longpress="onShowMenu($event)">
<view class="chat-msg-top">
<text>{{showName}}</text>
<chat-time :time="msgInfo.sendTime"></chat-time>
<text>{{$date.toTimeText(msgInfo.sendTime)}}</text>
</view>
<view class="chat-msg-bottom">
@ -216,7 +216,7 @@
.head-image {
width: 100%;
height: 100%;
border-radius: 5%;
border-radius: 10%;
}
}
@ -245,7 +245,7 @@
line-height: 30px;
margin-top: 10px;
padding: 10px;
background-color: #eeeeee;
background-color: rgb(235,235,245);
border-radius: 3px;
color: #333;
font-size: 16px;
@ -262,7 +262,7 @@
width: 0;
height: 0;
border-style: solid dashed dashed;
border-color: #eeeeee transparent transparent;
border-color: rgb(235,235,245) transparent transparent;
overflow: hidden;
border-width: 10px;
}
@ -384,13 +384,13 @@
padding-right: 0;
.chat-msg-text {
margin-left: 10px;
background-color: #45ab62;
background-color: rgb(88, 127, 240);
color: #fff;
&:after {
left: auto;
right: -10px;
border-top-color: #45ab62;
border-top-color: rgb(88, 127, 240);
}
}

45
im-uniapp/components/chat-time/chat-time.vue

@ -1,45 +0,0 @@
<template>
<view>
<text>{{formatDate}}</text>
</view>
</template>
<script>
export default {
name: "chat-time",
data() {
return {}
},
props: {
time: {
type: Number
}
},
computed: {
formatDate() {
let time = new Date(this.time);
let strtime = "";
let todayTime = new Date();
todayTime.setHours(0, 0, 0, 0)
let dayDiff = Math.floor((todayTime.getTime() - time.getTime()) / (24 * 3600 * 1000));
if (time.getTime() > todayTime.getTime()) {
strtime = time.getHours() <= 9 ? "0" + time.getHours() : time.getHours();
strtime += ":"
strtime += time.getMinutes() <= 9 ? "0" + time.getMinutes() : time.getMinutes();
} else if (dayDiff < 1) {
strtime = "昨天";
} else if (dayDiff < 7) {
strtime = `${dayDiff+1}天前`;
} else {
strtime = time.getMonth() + 1 + "月" + time.getDate() + "日";
}
return strtime;
}
}
}
</script>
<style>
</style>

39
im-uniapp/components/friend-item/friend-item.vue

@ -1,11 +1,14 @@
<template>
<view class="friend-item" @click="showFriendInfo()">
<view class="avatar">
<view class="friend-avatar">
<image class="head-image" :src="friend.headImage" lazy-load="true" mode="aspectFill"></image>
</view>
<view class="text">
<view>{{ friend.nickName}}</view>
<view :class="friend.online ? 'online-status online':'online-status'">{{ friend.online?"[在线]":"[离线]"}}</view>
<view class="friend-info">
<view class="friend-name">{{ friend.nickName}}</view>
<view class="friend-online"
:class="friend.online ? 'online':''">
{{ friend.online?"[在线]":"[离线]"}}
</view>
</view>
</view>
</template>
@ -46,7 +49,7 @@
background-color: #eeeeee;
}
.avatar {
.friend-avatar {
display: flex;
justify-content: center;
align-items: center;
@ -61,22 +64,24 @@
}
}
.text {
font-size: 36rpx;
margin-left: 30rpx;
.friend-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
flex-shrink: 0;
overflow: hidden;
.online-status {
font-size: 28rpx;
padding-left: 20rpx;
text-align: left;
.friend-name {
font-size: 30rpx;
font-weight: 600;
line-height: 60rpx;
white-space: nowrap;
overflow: hidden;
}
.friend-online {
font-size: 28rpx;
&.online {
color: #5fb878;
}

21
im-uniapp/components/group-item/group-item.vue

@ -1,9 +1,9 @@
<template>
<view class="group-item" @click="showGroupInfo()">
<view class="avatar">
<view class="group-avatar">
<image class="head-image" :src="group.headImage" lazy-load="true" mode="aspectFill"></image>
</view>
<view class="text">
<view class="group-name">
<view>{{ group.remark}}</view>
</view>
</view>
@ -45,7 +45,7 @@
background-color: #eeeeee;
}
.avatar {
.group-avatar {
display: flex;
justify-content: center;
align-items: center;
@ -60,15 +60,12 @@
}
}
.text {
font-size: 36rpx;
margin-left: 30rpx;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
flex-shrink: 0;
.group-name {
font-size: 32rpx;
padding-left: 20rpx;
font-weight: 600;
text-align: left;
white-space: nowrap;
overflow: hidden;
}
}

14
im-uniapp/components/pop-menu/pop-menu.vue

@ -49,18 +49,20 @@
.menu {
position: fixed;
border: 1px solid #d0d0d0;
box-shadow: 0 0 20rpx gray;
border: 1px solid #b4b4b4;
border-radius: 7px;
overflow: hidden;
box-shadow: 0px 0px 10px #ccc;
background-color: #eeeeee;
.menu-item {
height: 30px;
line-height: 30px;
font-size: 20px;
height: 25px;
line-height: 25px;
font-size: 18px;
display: flex;
padding: 10px;
align-items: center;
border-bottom: 1px solid #d0d0d0;
border-bottom: 1px solid #d0d0d8;
}
}
</style>

2
im-uniapp/main.js

@ -2,6 +2,7 @@ import App from './App'
import request from './common/request';
import emotion from './common/emotion.js';
import * as enums from './common/enums.js';
import * as date from './common/date';
import * as socketApi from './common/wssocket';
import store from './store';
@ -14,6 +15,7 @@ export function createApp() {
app.config.globalProperties.$wsApi = socketApi;
app.config.globalProperties.$emo = emotion;
app.config.globalProperties.$enums = enums;
app.config.globalProperties.$date = date;
return {
app
}

33
im-uniapp/pages/group/group-info.vue

@ -4,10 +4,10 @@
<view class="member-items">
<view v-for="(member,idx) in groupMembers" :key="idx">
<view class="member-item" v-if="idx<9" @click="onShowUserInfo(member.userId)">
<view class="avatar">
<view class="member-avatar">
<image class="head-image" :src="member.headImage" mode="aspectFill"> </image>
</view>
<view class="text">
<view class="member-name">
<text>{{member.aliasName}}</text>
</view>
</view>
@ -19,28 +19,28 @@
<view class="member-more" @click="onShowMoreMmeber()">查看更多群成员 ></view>
</view>
<view class="group-detail">
<uni-section title="群聊名称:" titleFontSize="12px">
<uni-section title="群聊名称:" titleFontSize="14px">
<template v-slot:right>
<text>{{group.name}}</text>
<text class="detail-text">{{group.name}}</text>
</template>
</uni-section>
<uni-section title="群主:" titleFontSize="12px">
<uni-section title="群主:" titleFontSize="14px">
<template v-slot:right>
<text>{{ownerName}}</text>
<text class="detail-text">{{ownerName}}</text>
</template>
</uni-section>
<uni-section title="群聊备注:" titleFontSize="12px">
<uni-section title="群聊备注:" titleFontSize="14px">
<template v-slot:right>
<text> {{group.remark}}</text>
<text class="detail-text"> {{group.remark}}</text>
</template>
</uni-section>
<uni-section title="我在本群的昵称:" titleFontSize="12px">
<uni-section title="我在本群的昵称:" titleFontSize="14px">
<template v-slot:right>
<text> {{group.aliasName}}</text>
<text class="detail-text"> {{group.aliasName}}</text>
</template>
</uni-section>
<uni-section title="群公告:" titleFontSize="12px">
<uni-section title="群公告:" titleFontSize="14px">
<uni-notice-bar :text="group.notice" />
</uni-section>
<view class="group-edit" @click="onEditGroup()">修改群聊资料 > </view>
@ -221,7 +221,7 @@
background-color: #fafafa;
white-space: nowrap;
.avatar {
.member-avatar {
display: flex;
justify-content: center;
align-items: center;
@ -236,7 +236,7 @@
}
}
.text {
.member-name {
width: 100%;
flex: 1;
font-size: 14px;
@ -271,11 +271,14 @@
padding: 30rpx;
background: white;
.detail-text{
font-size: 28rpx;
font-weight: 600;
}
.group-edit {
padding: 20rpx;
text-align: center;
font-size: 16px;
font-size: 30rpx;
}
}

Loading…
Cancel
Save