diff --git a/pom.xml b/pom.xml index 5e76382..9cc2438 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,13 @@ 5.7.2 + + com.belerweb + pinyin4j + 2.5.0 + + + com.alibaba fastjson diff --git a/src/main/java/com/dd/admin/business/api/AuthChatApi.java b/src/main/java/com/dd/admin/business/api/AuthChatApi.java index 66668c8..adb92e5 100644 --- a/src/main/java/com/dd/admin/business/api/AuthChatApi.java +++ b/src/main/java/com/dd/admin/business/api/AuthChatApi.java @@ -55,7 +55,7 @@ public class AuthChatApi { @OperLog(operModule = "获取消息列表",operType = OperType.QUERY,operDesc = "获取消息列表") public ResultBean> getMessageList(ChatDto chatDto) { String followId = String.valueOf(request.getAttribute("authorId")); - List chatVos = chatService.selectChatList(followId); + List chatVos = chatService.getMessageList(followId); return ResultBean.success(chatVos); } diff --git a/src/main/java/com/dd/admin/business/chat/controller/ChatController.java b/src/main/java/com/dd/admin/business/chat/controller/ChatController.java index a888b67..04ef01e 100644 --- a/src/main/java/com/dd/admin/business/chat/controller/ChatController.java +++ b/src/main/java/com/dd/admin/business/chat/controller/ChatController.java @@ -1,6 +1,8 @@ package com.dd.admin.business.chat.controller; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.extra.pinyin.PinyinUtil; +import com.dd.admin.business.chat.domain.AuthorChat; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.Api; @@ -35,6 +37,20 @@ public class ChatController { @Autowired ChatService chatService; + + @ApiOperation(value = "作者列表") + @ApiOperationSupport(order = 2) + @GetMapping("/admin/chat/authorList") + public ResultBean> authorList() { + List authorChats = chatService.selectAuthorChatList(); + authorChats.stream().forEach(authorChat -> { + authorChat.setIndex(String.valueOf(PinyinUtil.getFirstLetter(authorChat.getIndex().charAt(0)))); + }); + return ResultBean.success(authorChats); + } + + + @ApiOperation(value = "-分页列表") @ApiOperationSupport(order = 1) @GetMapping("/admin/chat/page") diff --git a/src/main/java/com/dd/admin/business/chat/domain/AuthorChat.java b/src/main/java/com/dd/admin/business/chat/domain/AuthorChat.java new file mode 100644 index 0000000..f432043 --- /dev/null +++ b/src/main/java/com/dd/admin/business/chat/domain/AuthorChat.java @@ -0,0 +1,39 @@ +package com.dd.admin.business.chat.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AuthorChat { + // 消息的唯一标识id + @ApiModelProperty(value = "用户id") + private String id; + + // 显示名称,例如聊天对象的昵称等 + @ApiModelProperty(value = "显示名称") + private String displayName; + + // 头像的网络地址,用于展示聊天对象的头像图片 + @ApiModelProperty(value = "头像") + private String avatar; + + // 索引字段,可能用于排序、分组等功能,具体含义依业务而定 + @ApiModelProperty(value = "索引") + private String index; + + // 未读消息的数量 + @ApiModelProperty(value = "未读消息数量") + private Integer unread; + + // 最近一条消息的内容,经过相应的渲染处理(如表情替换等) + @ApiModelProperty(value = "最近一条消息内容") + private String lastContent; + + // 最近一条消息的发送时间,通常是时间戳形式(单位可能是毫秒) + @ApiModelProperty(value = "最近一条消息发送时间") + private Long lastSendTime; +} diff --git a/src/main/java/com/dd/admin/business/chat/domain/MessageBean.java b/src/main/java/com/dd/admin/business/chat/domain/MessageBean.java new file mode 100644 index 0000000..b0ca7cb --- /dev/null +++ b/src/main/java/com/dd/admin/business/chat/domain/MessageBean.java @@ -0,0 +1,40 @@ +package com.dd.admin.business.chat.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MessageBean { + // 发送方用户信息 + private FromUser fromUser; + // 消息处理类型,这里对应数字6,具体含义需根据业务确定 + private int handlerType; + // 接收方联系人ID,这里是一个字符串形式的ID,具体格式由业务定义 + private String toContactId; + // 消息的唯一标识ID,UUID格式,具体使用方式依业务而定 + private String id; + // 消息类型,这里为text表示文本消息,可能还有其他类型如image、audio等 + private String type; + // 消息内容,此处为文本内容“111”,根据不同消息类型会有不同格式 + private String content; + // 消息状态,这里是going,具体状态值及含义需结合业务场景明确 + private String status; + // 消息发送时间,这里是一个时间戳形式(可能是毫秒级时间戳,需根据业务确认) + private long sendTime; + + // 内部类,用于表示发送方用户信息 + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class FromUser { + // 显示名称,例如用户的昵称等 + private String displayName; + // 用户的唯一标识ID + private String id; + // 用户头像的URL或者其他相关标识,这里为空字符串,具体使用方式由业务决定 + private String avatar; + } +} diff --git a/src/main/java/com/dd/admin/business/chat/mapper/ChatMapper.java b/src/main/java/com/dd/admin/business/chat/mapper/ChatMapper.java index 0621ccf..1c1f065 100644 --- a/src/main/java/com/dd/admin/business/chat/mapper/ChatMapper.java +++ b/src/main/java/com/dd/admin/business/chat/mapper/ChatMapper.java @@ -1,6 +1,7 @@ package com.dd.admin.business.chat.mapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.dd.admin.business.chat.domain.AuthorChat; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -26,5 +27,10 @@ public interface ChatMapper extends BaseMapper { ChatVo selectChat(@Param("chatId") String chatId); List selectChatDetail(@Param("chatDto") ChatDto chatDto); //查询我的聊天记录列表 当我作为收发方都需要考虑 - List selectChatList(@Param("authorId")String authorId); + List getMessageList(@Param("authorId")String authorId); + + + //admin下面是后台使用的接口 + //查询客服聊天列表 当我作为收发方都需要查询最后一条 + List selectAuthorChatList(@Param("authorId")String authorId); } diff --git a/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml b/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml index 9b17f30..0633da9 100644 --- a/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml +++ b/src/main/java/com/dd/admin/business/chat/mapper/xml/ChatMapper.xml @@ -58,7 +58,7 @@ limit 1 - select * from ( SELECT @@ -83,7 +83,11 @@ business_author b ON a.FROM_ID = b.AUTHOR_ID WHERE a.TO_ID = #{authorId} + + UNION ALL + + SELECT a.TO_ID AS authorId, a.TO_NAME AS authorName, @@ -99,9 +103,66 @@ a.FROM_ID = #{authorId} ORDER BY create_time DESC + ) a1 GROUP BY a1.authorId ORDER BY create_time DESC + + + + + SELECT + wa.AUTHOR_ID id, + wa.AUTHOR_NAME displayName, + wa.AVATAR_URL avatar, + wa.AUTHOR_NAME AS 'index', + wb.unReadCount unRead, + UNIX_TIMESTAMP(CONVERT_TZ(wb.CREATE_TIME, '+08:00', '+00:00')) lastSendTime + FROM + business_author wa + LEFT JOIN ( + SELECT + a.FROM_ID AS authorId, + a.FROM_NAME AS authorName, + b.AVATAR_URL AS authorAvatar, + a.content, + a.create_time, + ( + SELECT + count(1) + FROM + business_chat ca + WHERE + ca.FROM_ID = a.FROM_ID + AND ca.to_id = #{authorId} + AND ca.MESSAGE_STATUS = 0 + ) AS unReadCount + FROM + business_chat a + LEFT JOIN business_author b ON a.FROM_ID = b.AUTHOR_ID + WHERE + a.TO_ID = #{authorId} + UNION ALL + SELECT + a.TO_ID AS authorId, + a.TO_NAME AS authorName, + b.AVATAR_URL AS authorAvatar, + a.content, + a.create_time, + 0 AS unReadCount + FROM + business_chat a + LEFT JOIN business_author b ON a.TO_ID = b.AUTHOR_ID + WHERE + a.FROM_ID = #{authorId} + ORDER BY + create_time DESC + ) wb ON wa.author_id = wb.authorId + GROUP BY + wa.author_id + ORDER BY + wb.create_time DESC + diff --git a/src/main/java/com/dd/admin/business/chat/service/ChatService.java b/src/main/java/com/dd/admin/business/chat/service/ChatService.java index c345d5c..0de799a 100644 --- a/src/main/java/com/dd/admin/business/chat/service/ChatService.java +++ b/src/main/java/com/dd/admin/business/chat/service/ChatService.java @@ -1,6 +1,7 @@ package com.dd.admin.business.chat.service; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.dd.admin.business.chat.domain.AuthorChat; import com.dd.admin.business.chat.entity.Chat; import com.baomidou.mybatisplus.extension.service.IService; import com.dd.admin.business.chat.domain.ChatVo; @@ -24,9 +25,13 @@ public interface ChatService extends IService { ChatVo selectChat(String chatId); //-列表 List selectChatDetail(ChatDto chatDto); - List selectChatList(String authorId); + List getMessageList(String authorId); void readMessage(String authorId,String loginId); //未读聊天消息的数量 Integer selectUnReadCount(String authorId); + + //admin + List selectAuthorChatList(String authorId); + } diff --git a/src/main/java/com/dd/admin/business/chat/service/impl/ChatServiceImpl.java b/src/main/java/com/dd/admin/business/chat/service/impl/ChatServiceImpl.java index 4ec65bd..06a4b6a 100644 --- a/src/main/java/com/dd/admin/business/chat/service/impl/ChatServiceImpl.java +++ b/src/main/java/com/dd/admin/business/chat/service/impl/ChatServiceImpl.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.dd.admin.business.chat.domain.AuthorChat; import com.dd.admin.common.model.PageFactory; import com.dd.admin.business.chat.entity.Chat; import com.dd.admin.business.chat.mapper.ChatMapper; @@ -43,8 +44,8 @@ public class ChatServiceImpl extends ServiceImpl implements Ch } @Override - public List selectChatList(String authorId) { - return baseMapper.selectChatList(authorId); + public List getMessageList(String authorId) { + return baseMapper.getMessageList(authorId); } @Override @@ -64,4 +65,9 @@ public class ChatServiceImpl extends ServiceImpl implements Ch queryWrapper.eq(Chat::getMessageStatus,0); return baseMapper.selectCount(queryWrapper); } + + @Override + public List selectAuthorChatList(String authorId) { + return baseMapper.selectAuthorChatList(authorId); + } } diff --git a/src/main/java/com/dd/admin/business/webSocket/MyWebSocketMsgHandler.java b/src/main/java/com/dd/admin/business/webSocket/MyWebSocketMsgHandler.java index 2f7bc51..363fdd8 100644 --- a/src/main/java/com/dd/admin/business/webSocket/MyWebSocketMsgHandler.java +++ b/src/main/java/com/dd/admin/business/webSocket/MyWebSocketMsgHandler.java @@ -1 +1 @@ -package com.dd.admin.business.webSocket; import com.alibaba.fastjson.JSON; import com.dd.admin.common.utils.AddressUtils; import com.dd.admin.common.utils.IPUtils; import com.dd.admin.common.utils.StringUtil; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import org.tio.core.ChannelContext; import org.tio.core.Tio; import org.tio.http.common.HttpRequest; import org.tio.http.common.HttpResponse; import org.tio.utils.lock.SetWithLock; import org.tio.websocket.common.WsRequest; import org.tio.websocket.server.handler.IWsMsgHandler; import org.tio.websocket.starter.TioWebSocketServerBootstrap; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.Set; @Configuration @Slf4j public class MyWebSocketMsgHandler implements IWsMsgHandler { @Autowired Map handlerInterfaceMap; @Override public HttpResponse handshake(HttpRequest request, HttpResponse httpResponse, ChannelContext channelContext) throws Exception { String authorId = request.getParam("authorId"); String authorName = request.getParam("authorName"); Tio.bindUser(channelContext,authorId); String ipAddr = request.getClientIp(); String realAddress = AddressUtils.getRealAddress(ipAddr); System.out.println(authorId+":进入了Tio id:"+authorId+" ip:"+ ipAddr); SetWithLock channelContexts = Tio.getAllChannelContexts(channelContext.getGroupContext()); Set contextList = channelContexts.getObj(); System.out.println("当前在线用户:"); for(ChannelContext context:contextList){ System.out.println(context.userid+"\t"); } Integer count = channelContexts.size(); System.out.println(count); return httpResponse; } @Override public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception { // System.out.println("握手成功进入群组"); } @Override public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { System.out.println("接收到bytes消息"); return null; } @Override public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { return null; } @Override public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception { if(text.equals("ping")) return null; System.out.println("接收到文本消息:"+text); Map map = JSON.parseObject(text,Map.class); String handlerType =(String)map.get("handlerType"); if(!StringUtil.isEmpty(handlerType)){ MsgHandlerInterface msgHandler = (MsgHandlerInterface) handlerInterfaceMap.get(handlerType); if(msgHandler!=null){ msgHandler.handler(map,channelContext); }else{ log.debug("非法请求..."); } }else{ log.debug("非法请求..."); } System.out.println(map); return null; } } \ No newline at end of file +package com.dd.admin.business.webSocket; import com.alibaba.fastjson.JSON; import com.dd.admin.common.utils.AddressUtils; import com.dd.admin.common.utils.IPUtils; import com.dd.admin.common.utils.StringUtil; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import org.tio.core.ChannelContext; import org.tio.core.Tio; import org.tio.http.common.HttpRequest; import org.tio.http.common.HttpResponse; import org.tio.utils.lock.SetWithLock; import org.tio.websocket.common.WsRequest; import org.tio.websocket.server.handler.IWsMsgHandler; import org.tio.websocket.starter.TioWebSocketServerBootstrap; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.Set; @Configuration @Slf4j public class MyWebSocketMsgHandler implements IWsMsgHandler { @Autowired Map handlerInterfaceMap; @Override public HttpResponse handshake(HttpRequest request, HttpResponse httpResponse, ChannelContext channelContext) throws Exception { String authorId = request.getParam("authorId"); String authorName = request.getParam("authorName"); Tio.bindUser(channelContext,authorId); String ipAddr = request.getClientIp(); String realAddress = AddressUtils.getRealAddress(ipAddr); System.out.println(authorId+":进入了Tio id:"+authorId+" ip:"+ ipAddr); SetWithLock channelContexts = Tio.getAllChannelContexts(channelContext.getGroupContext()); Set contextList = channelContexts.getObj(); System.out.println("当前在线用户:"); for(ChannelContext context:contextList){ System.out.println(context.userid+"\t"); } Integer count = channelContexts.size(); System.out.println("当前共"+count+"个用户"); return httpResponse; } @Override public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception { // System.out.println("握手成功进入群组"); } @Override public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { System.out.println("接收到bytes消息"); return null; } @Override public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { return null; } @Override public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception { if(text.equals("ping")) return "pong"; System.out.println("接收到文本消息:"+text); Map map = JSON.parseObject(text,Map.class); String handlerType =(String)map.get("handlerType"); if(!StringUtil.isEmpty(handlerType)){ MsgHandlerInterface msgHandler = (MsgHandlerInterface) handlerInterfaceMap.get(handlerType); if(msgHandler!=null){ msgHandler.handler(map,channelContext); }else{ log.debug("非法请求..."); } }else{ log.debug("非法请求..."); } System.out.println(map); return null; } } \ No newline at end of file diff --git a/src/main/java/com/dd/admin/business/webSocket/handler/ServiceMessageHandler.java b/src/main/java/com/dd/admin/business/webSocket/handler/ServiceMessageHandler.java new file mode 100644 index 0000000..fde3442 --- /dev/null +++ b/src/main/java/com/dd/admin/business/webSocket/handler/ServiceMessageHandler.java @@ -0,0 +1 @@ +package com.dd.admin.business.webSocket.handler; import cn.hutool.core.bean.BeanUtil; import com.dd.admin.business.chat.domain.ChatVo; import com.dd.admin.business.chat.domain.MessageBean; import com.dd.admin.business.chat.entity.Chat; import com.dd.admin.business.chat.service.ChatService; import com.dd.admin.business.webSocket.MsgHandlerInterface; import com.dd.admin.business.webSocket.util.TioUtil; import com.dd.admin.common.utils.AddressUtils; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.units.qual.C; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.tio.core.ChannelContext; import java.util.Map; @Component @Slf4j @Service("6") public class ServiceMessageHandler implements MsgHandlerInterface { public static ServiceMessageHandler handler; @Autowired ChatService chatService; @Override public Object handler(Map map, ChannelContext context ){ System.out.println(map); MessageBean messageBean = BeanUtil.toBean(map, MessageBean.class); //xx发送人 MessageBean.FromUser fromUser = messageBean.getFromUser(); Chat chat = new Chat(); chat.setFromId(fromUser.getId()); chat.setFromName(fromUser.getDisplayName()); chat.setToId(messageBean.getToContactId()); chat.setContent(messageBean.getContent()); chat.setIpAddress(context.getClientNode().getIp()); chat.setIpRealAddress(AddressUtils.getRealAddress(chat.getIpAddress())); //ip真实地址 chatService.save(chat); ChatVo chatVo = chatService.selectChat(chat.getChatId()); //如果对方是移动端则按照移动端消息格式推送 TioUtil.sendChatMessageToUser(context.getGroupContext(),chat.getToId(),"5",chatVo); //还需要推送给客服自己 TioUtil.sendChatMessageToUser(context.getGroupContext(),chat.getFromId(),"5",messageBean); return null; }} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 10ad328..5c9fa9d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -63,7 +63,7 @@ tio: websocket: server: port: 9326 - heartbeat-timeout: 10000 + heartbeat-timeout: 20000 # 集群配置 默认关闭 cluster: enabled: false diff --git a/web/src/api/websocket.js b/web/src/api/websocket.js new file mode 100644 index 0000000..ca27a83 --- /dev/null +++ b/web/src/api/websocket.js @@ -0,0 +1,108 @@ +import { Message } from 'element-ui' +let socket = null;//实例对象 +let socketLeaveFlag = false +let socketReconnectTimer = null // 计时器对象——重连 +let socketReconnectLock = false // WebSocket重连的锁 +const heartCheck = { + vueThis: this, // vue实例 + timeout: 6000, // 超时时间 + timeoutObj: null, // 计时器对象——向后端发送心跳检测 + serverTimeoutObj: null, // 计时器对象——等待后端心跳检测的回复 + // 心跳检测重置 + reset: function () { + clearTimeout(this.timeoutObj); + clearTimeout(this.serverTimeoutObj); + return this; + }, + // 心跳检测启动 + start: function () { + this.timeoutObj && clearTimeout(this.timeoutObj); + this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj); + this.timeoutObj = setTimeout(() => { + // 这里向后端发送一个心跳检测,后端收到后,会返回一个心跳回复 + socket.send("ping"); + console.log("发送心跳检测"); + this.serverTimeoutObj = setTimeout(() => { + // 如果超过一定时间还没重置计时器,说明websocket与后端断开了 + console.log("未收到心跳检测回复"); + // 关闭WebSocket + socket.close(); + }, this.timeout); + }, this.timeout); + }, +} +const initWebSocket = (wsUrl) => { + return new Promise(function (resolve, reject) { + if ("WebSocket" in window) { + socket = new WebSocket(wsUrl); + socket.onerror = webSocketOnError; + socket.onmessage = webSocketOnMessage; + socket.onclose = webSocketOnClose; + socket.onopen = () => { + console.log('连接成功'); + heartCheck.reset().start(); + resolve(socket) + }; + } else { + Message({ + message: "您的浏览器不支持websocket,请更换Chrome或者Firefox", + type: 'error', + }) + return reject(new Error('浏览器不支持websocket')); + } + }); +} +const webSocketOnError = (e) => { + console.log("WebSocket:发生错误"); + socketReconnect(e.target.url) + window.dispatchEvent( + new CustomEvent("WSOnError", e) + ); +} +//服务器返回的数据 +const webSocketOnMessage = (e) => { + if (e.data === 'pong') { + console.log('收到心跳回复'); + heartCheck.reset().start(); + } else { + console.log('服务器返回的数据') + console.log(e) + } +} +const webSocketOnClose = (e) => { + console.log("WebSocket:已关闭"); + // 清除心跳定时器 + heartCheck.reset(); + if (!socketLeaveFlag) { + // websocket重连 + socketReconnect(e.target.url) + } +} +const sendMessage = (data) =>{ + if (socket) { + socket.send(data); + console.log('消息已发送:', data); + } +} +const socketReconnect = (url) => { + if (socketReconnectLock) { + return; + } + socketReconnectLock = true; + socketReconnectTimer && clearTimeout(socketReconnectTimer); + socketReconnectTimer = setTimeout(() => { + console.log("WebSocket:重连中..."); + socketReconnectLock = false; + // websocket启动 + initWebSocket(url); + }, 4000); +} +const closeWebSocket = () => { + socketLeaveFlag = true + if (socket) { + console.log('断开连接'); + socket.close(); + } +} +//具体问题具体分析,把需要用到的方法暴露出去 +export default { closeWebSocket, socket, initWebSocket,sendMessage }; diff --git a/web/src/assets/sdz.jpg b/web/src/assets/sdz.jpg new file mode 100644 index 0000000..c6e912d Binary files /dev/null and b/web/src/assets/sdz.jpg differ diff --git a/web/src/layout/components/Navbar.vue b/web/src/layout/components/Navbar.vue index 96e1dd3..0bb8386 100644 --- a/web/src/layout/components/Navbar.vue +++ b/web/src/layout/components/Navbar.vue @@ -4,10 +4,13 @@ + + + - + {{user.username}}({{user.deptName}}) @@ -32,7 +35,18 @@ + + + + + + + + + + + @@ -41,12 +55,14 @@ import { mapGetters } from 'vuex' import Breadcrumb from '@/components/Breadcrumb' import Hamburger from '@/components/Hamburger' import UpdatePassword from '@/views/common/system/UpdatePassword' +import Im from "@/views/common/Im"; export default { components: { UpdatePassword, Breadcrumb, - Hamburger + Hamburger, + Im }, computed: { ...mapGetters([ @@ -59,6 +75,9 @@ export default { updatePassword(){ this.$refs.updatePass.open() }, + openIm(){ + this.$refs.im.open() + }, toggleSideBar() { this.$store.dispatch('app/toggleSideBar') }, @@ -95,10 +114,36 @@ export default { } } + .top-icon{ + text-align: center; + height: 50px; + width: 50px; + line-height: 50px; + cursor: pointer; + color: #606266; + font-size: 18px; + &:hover { + background: rgba(0, 0, 0, .025) + } + } + .breadcrumb-container { float: left; } + .top-menu{ + margin-right: 25px; + font-size: 14px; + } + + //修改徽章位置 + .el-badge { + ::v-deep .el-badge__content + { + margin-top:12px; + } + } + .right-menu { float: right; height: 100%; diff --git a/web/src/views/common/Im.vue b/web/src/views/common/Im.vue new file mode 100644 index 0000000..926231e --- /dev/null +++ b/web/src/views/common/Im.vue @@ -0,0 +1,128 @@ + + + + + + + + + + + + diff --git a/web/src/views/dashboard/components/BarChart.vue b/web/src/views/dashboard/components/BarChart.vue deleted file mode 100644 index be0af34..0000000 --- a/web/src/views/dashboard/components/BarChart.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - - - diff --git a/web/src/views/dashboard/components/PieChart.vue b/web/src/views/dashboard/components/PieChart.vue deleted file mode 100644 index c556d49..0000000 --- a/web/src/views/dashboard/components/PieChart.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/web/src/views/dashboard/components/RaddarChart.vue b/web/src/views/dashboard/components/RaddarChart.vue deleted file mode 100644 index 9c10333..0000000 --- a/web/src/views/dashboard/components/RaddarChart.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - diff --git a/web/src/views/dashboard/components/mixins/resize.js b/web/src/views/dashboard/components/mixins/resize.js deleted file mode 100644 index 234953b..0000000 --- a/web/src/views/dashboard/components/mixins/resize.js +++ /dev/null @@ -1,55 +0,0 @@ -import { debounce } from '@/utils' - -export default { - data() { - return { - $_sidebarElm: null, - $_resizeHandler: null - } - }, - mounted() { - this.$_resizeHandler = debounce(() => { - if (this.chart) { - this.chart.resize() - } - }, 100) - this.$_initResizeEvent() - this.$_initSidebarResizeEvent() - }, - beforeDestroy() { - this.$_destroyResizeEvent() - this.$_destroySidebarResizeEvent() - }, - // to fixed bug when cached by keep-alive - // https://github.com/PanJiaChen/vue-element-admin/issues/2116 - activated() { - this.$_initResizeEvent() - this.$_initSidebarResizeEvent() - }, - deactivated() { - this.$_destroyResizeEvent() - this.$_destroySidebarResizeEvent() - }, - methods: { - // use $_ for mixins properties - // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential - $_initResizeEvent() { - window.addEventListener('resize', this.$_resizeHandler) - }, - $_destroyResizeEvent() { - window.removeEventListener('resize', this.$_resizeHandler) - }, - $_sidebarResizeHandler(e) { - if (e.propertyName === 'width') { - this.$_resizeHandler() - } - }, - $_initSidebarResizeEvent() { - this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] - this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) - }, - $_destroySidebarResizeEvent() { - this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) - } - } -} diff --git a/web/src/views/dashboard/index.vue b/web/src/views/dashboard/index.vue index c270e87..72db72a 100644 --- a/web/src/views/dashboard/index.vue +++ b/web/src/views/dashboard/index.vue @@ -1,419 +1,58 @@ - - - - - - - - - - - - - - - - - - - - - - 用户总数 - - 102400 - - - - - - - - - - - 文章总数 - - 81212 - - - - - - - - - - - 评论总数 - - 9280 - - - - - - - - - - - 阅读总数 - - 13600 - - - - - - - - - - - - - - - - - - - - - - - - - - - - 阅读量排行 - - - - - - - - - - - - 收益排行 - - - - Vue - - - - JavaScript - - - - CSS - - - - PHP - - - - Hyperf - - - - ESLint - - - - - - - - - 待办事项 - - - - - {{ activity.content }} - - - - - - - -