博客
关于我
一套简单的web即时通讯——第一版
阅读量:691 次
发布时间:2019-03-17

本文共 58533 字,大约阅读时间需要 195 分钟。

  前言

  我们之前已经实现了 ,后面我们模仿layer弹窗,封装了一个自己的web弹窗 ,再后来就产生了将两者结合起来的想法,加上我们之前实现了一套自动生成代码的jpa究极进化版 ,于是去网上搜索,参考即时通讯系统的消息存储如何建表,看下能不能把我们之前的东西稍微整合一下,将之前的写的东西应用起来学以致用,实现一套简单的web即时通讯,一版一版的升级完善。

  第一版功能

  1、实现简单的登录/注册

  2、将之前的页面改成自定义web弹窗的形式,并且完善群聊、私聊功能

 

  代码编写

  目前建了三个表,SQL如下:

  

/* Navicat Premium Data Transfer Source Server         : localhost Source Server Type    : MySQL Source Server Version : 50528 Source Host           : localhost:3306 Source Schema         : test Target Server Type    : MySQL Target Server Version : 50528 File Encoding         : 65001 Date: 09/05/2019 10:09:11*/SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for ims_friend-- ----------------------------DROP TABLE IF EXISTS `ims_friend`;CREATE TABLE `ims_friend`  (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',  `user_id` int(11) NULL DEFAULT NULL COMMENT '用户id',  `friend_id` int(11) NULL DEFAULT NULL COMMENT '好友id',  `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',  `updata_time` datetime NULL DEFAULT NULL COMMENT '更新时间',  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '好友表' ROW_FORMAT = Compact;-- ------------------------------ Table structure for ims_friend_message-- ----------------------------DROP TABLE IF EXISTS `ims_friend_message`;CREATE TABLE `ims_friend_message`  (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',  `from_user_id` int(11) NULL DEFAULT NULL COMMENT '发消息的人的id',  `to_user_id` int(11) NULL DEFAULT NULL COMMENT '收消息的人的id',  `content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '消息内容',  `is_read` int(11) NULL DEFAULT NULL COMMENT '是否已读,1是0否',  `is_back` int(11) NULL DEFAULT NULL COMMENT '是否撤回,1是0否',  `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',  `updata_time` datetime NULL DEFAULT NULL COMMENT '更新时间',  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '好友消息表' ROW_FORMAT = Compact;-- ------------------------------ Table structure for ims_user-- ----------------------------DROP TABLE IF EXISTS `ims_user`;CREATE TABLE `ims_user`  (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '帐号',  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',  `nick_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',  `gender` int(11) NULL DEFAULT 0 COMMENT '性别:0为男,1为女',  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像',  `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电子邮箱',  `phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号码',  `sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '个性签名',  `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',  `updata_time` datetime NULL DEFAULT NULL COMMENT '更新时间',  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户信息表' ROW_FORMAT = Compact;SET FOREIGN_KEY_CHECKS = 1;
View Code

 

  自动生成代码,运行main方法执行

package cn.huanzi.ims.util;import java.io.File;import java.io.FileWriter;import java.io.PrintWriter;import java.sql.*;import java.util.ArrayList;import java.util.List;/** * 自动生成代码 */public class CodeDOM {    /**     * 构造参数,出入表名     */    private CodeDOM(String tableName) {        this.tableName = tableName;        basePackage_ = "cn\\huanzi\\ims\\";        package_ = basePackage_ + StringUtil.camelCaseName(tableName).toLowerCase() + "\\";        //System.getProperty("user.dir") 获取的是项目所在路径        basePath = System.getProperty("user.dir") + "\\src\\main\\java\\" + package_;    }    /**     * 数据连接相关     */    private static final String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8";    private static final String USERNAME = "root";    private static final String PASSWORD = "123456";    private static final String DRIVERCLASSNAME = "com.mysql.jdbc.Driver";    /**     * 表名     */    private String tableName;    /**     * 基础路径     */    private String basePackage_;    private String package_;    private String basePath;    /**     * 创建pojo实体类     */    private void createPojo(List
tableInfos) { File file = FileUtil.createFile(basePath + "pojo\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ".java"); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append( "package " + package_.replaceAll("\\\\", ".") + "pojo;\n" + "\n" + "import lombok.Data;\n" + "import javax.persistence.*;\n" + "import java.io.Serializable;\n" + "import java.util.Date;\n" + "\n" + "@Entity\n" + "@Table(name = \"" + tableName + "\")\n" + "@Data\n" + "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {\n" ); //遍历设置属性 for (TableInfo tableInfo : tableInfos) { //主键 if ("PRI".equals(tableInfo.getColumnKey())) { stringBuffer.append(" @Id\n"); } //自增 if ("auto_increment".equals(tableInfo.getExtra())) { stringBuffer.append(" @GeneratedValue(strategy= GenerationType.IDENTITY)\n"); } stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "\n\n"); } stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); } /** * 创建vo类 */ private void createVo(List
tableInfos) { File file = FileUtil.createFile(basePath + "vo\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo.java"); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append( "package " + package_.replaceAll("\\\\", ".") + "vo;\n" + "\n" + "import lombok.Data;\n" + "import java.io.Serializable;\n" + "import java.util.Date;\n" + "\n" + "@Data\n" + "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo implements Serializable {\n" ); //遍历设置属性 for (TableInfo tableInfo : tableInfos) { stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "\n\n"); } stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); } /** * 创建repository类 */ private void createRepository(List
tableInfos) { File file = FileUtil.createFile(basePath + "repository\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository.java"); StringBuffer stringBuffer = new StringBuffer(); String t = "String"; //遍历属性 for (TableInfo tableInfo : tableInfos) { //主键 if ("PRI".equals(tableInfo.getColumnKey())) { t = StringUtil.typeMapping(tableInfo.getDataType()); } } stringBuffer.append( "package " + package_.replaceAll("\\\\", ".") + "repository;\n" + "\n" + "import " + basePackage_.replaceAll("\\\\", ".") + "common.repository.*;\n" + "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" + "import org.springframework.stereotype.Repository;\n" + "\n" + "@Repository\n" + "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository extends CommonRepository<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {" ); stringBuffer.append("\n"); stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); } /** * 创建service类 */ private void createService(List
tableInfos) { File file = FileUtil.createFile(basePath + "service\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service.java"); StringBuffer stringBuffer = new StringBuffer(); String t = "String"; //遍历属性 for (TableInfo tableInfo : tableInfos) { //主键 if ("PRI".equals(tableInfo.getColumnKey())) { t = StringUtil.typeMapping(tableInfo.getDataType()); } } stringBuffer.append( "package " + package_.replaceAll("\\\\", ".") + "service;\n" + "\n" + "import " + basePackage_.replaceAll("\\\\", ".") + "common.service.*;\n" + "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" + "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" + "\n" + "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service extends CommonService<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {" ); stringBuffer.append("\n"); stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); //Impl File file1 = FileUtil.createFile(basePath + "service\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl.java"); StringBuffer stringBuffer1 = new StringBuffer(); stringBuffer1.append( "package " + package_.replaceAll("\\\\", ".") + "service;\n" + "\n" + "import " + basePackage_.replaceAll("\\\\", ".") + "common.service.*;\n" + "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" + "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" + "import " + package_.replaceAll("\\\\", ".") + "repository." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository;\n" + "import org.springframework.beans.factory.annotation.Autowired;\n" + "import org.springframework.stereotype.Service;\n" + "import org.springframework.transaction.annotation.Transactional;\n" + "import javax.persistence.EntityManager;\n" + "import javax.persistence.PersistenceContext;\n" + "\n" + "@Service\n" + "@Transactional\n" + "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl extends CommonServiceImpl<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> implements " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service{" ); stringBuffer1.append("\n\n"); stringBuffer1.append( " @PersistenceContext\n" + " private EntityManager em;\n"); stringBuffer1.append("" + " @Autowired\n" + " private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository " + StringUtil.camelCaseName(tableName) + "Repository;\n"); stringBuffer1.append("}"); FileUtil.fileWriter(file1, stringBuffer1); } /** * 创建controller类 */ private void createController(List
tableInfos) { File file = FileUtil.createFile(basePath + "controller\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller.java"); StringBuffer stringBuffer = new StringBuffer(); String t = "String"; //遍历属性 for (TableInfo tableInfo : tableInfos) { //主键 if ("PRI".equals(tableInfo.getColumnKey())) { t = StringUtil.typeMapping(tableInfo.getDataType()); } } stringBuffer.append( "package " + package_.replaceAll("\\\\", ".") + "controller;\n" + "\n" + "import " + basePackage_.replaceAll("\\\\", ".") + "common.controller.*;\n" + "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" + "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" + "import " + package_.replaceAll("\\\\", ".") + "service." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service;\n" + "import org.springframework.beans.factory.annotation.Autowired;\n" + "import org.springframework.web.bind.annotation.*;\n" + "\n" + "@RestController\n" + "@RequestMapping(\"/" + StringUtil.camelCaseName(tableName) + "/\")\n" + "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller extends CommonController<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {" ); stringBuffer.append("\n"); stringBuffer.append("" + " @Autowired\n" + " private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service " + StringUtil.camelCaseName(tableName) + "Service;\n"); stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); } /** * 获取表结构信息 * * @return list */ private List
getTableInfo() { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; ArrayList
list = new ArrayList<>(); try { conn = DBConnectionUtil.getConnection(); String sql = "select column_name,data_type,column_comment,column_key,extra from information_schema.columns where table_name=?"; ps = conn.prepareStatement(sql); ps.setString(1, tableName); rs = ps.executeQuery(); while (rs.next()) { TableInfo tableInfo = new TableInfo(); //列名,全部转为小写 tableInfo.setColumnName(rs.getString("column_name").toLowerCase()); //列类型 tableInfo.setDataType(rs.getString("data_type")); //列注释 tableInfo.setColumnComment(rs.getString("column_comment")); //主键 tableInfo.setColumnKey(rs.getString("column_key")); //主键类型 tableInfo.setExtra(rs.getString("extra")); list.add(tableInfo); } } catch (SQLException e) { e.printStackTrace(); } finally { assert rs != null; DBConnectionUtil.close(conn, ps, rs); } return list; } /** * file工具类 */ private static class FileUtil { /** * 创建文件 * * @param pathNameAndFileName 路径跟文件名 * @return File对象 */ private static File createFile(String pathNameAndFileName) { File file = new File(pathNameAndFileName); try { //获取父目录 File fileParent = file.getParentFile(); if (!fileParent.exists()) { fileParent.mkdirs(); } //创建文件 if (!file.exists()) { file.createNewFile(); } } catch (Exception e) { file = null; System.err.println("新建文件操作出错"); e.printStackTrace(); } return file; } /** * 字符流写入文件 * * @param file file对象 * @param stringBuffer 要写入的数据 */ private static void fileWriter(File file, StringBuffer stringBuffer) { //字符流 try { FileWriter resultFile = new FileWriter(file, true);//true,则追加写入 false,则覆盖写入 PrintWriter myFile = new PrintWriter(resultFile); //写入 myFile.println(stringBuffer.toString()); myFile.close(); resultFile.close(); } catch (Exception e) { System.err.println("写入操作出错"); e.printStackTrace(); } } } /** * 字符串处理工具类 */ private static class StringUtil { /** * 数据库类型->JAVA类型 * * @param dbType 数据库类型 * @return JAVA类型 */ private static String typeMapping(String dbType) { String javaType = ""; if ("int|integer".contains(dbType)) { javaType = "Integer"; } else if ("float|double|decimal|real".contains(dbType)) { javaType = "Double"; } else if ("date|time|datetime|timestamp".contains(dbType)) { javaType = "Date"; } else { javaType = "String"; } return javaType; } /** * 驼峰转换为下划线 */ public static String underscoreName(String camelCaseName) { StringBuilder result = new StringBuilder(); if (camelCaseName != null && camelCaseName.length() > 0) { result.append(camelCaseName.substring(0, 1).toLowerCase()); for (int i = 1; i < camelCaseName.length(); i++) { char ch = camelCaseName.charAt(i); if (Character.isUpperCase(ch)) { result.append("_"); result.append(Character.toLowerCase(ch)); } else { result.append(ch); } } } return result.toString(); } /** * 首字母大写 */ public static String captureName(String name) { char[] cs = name.toCharArray(); cs[0] -= 32; return String.valueOf(cs); } /** * 下划线转换为驼峰 */ public static String camelCaseName(String underscoreName) { StringBuilder result = new StringBuilder(); if (underscoreName != null && underscoreName.length() > 0) { boolean flag = false; for (int i = 0; i < underscoreName.length(); i++) { char ch = underscoreName.charAt(i); if ("_".charAt(0) == ch) { flag = true; } else { if (flag) { result.append(Character.toUpperCase(ch)); flag = false; } else { result.append(ch); } } } } return result.toString(); } } /** * JDBC连接数据库工具类 */ private static class DBConnectionUtil { { // 1、加载驱动 try { Class.forName(DRIVERCLASSNAME); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 返回一个Connection连接 * * @return */ public static Connection getConnection() { Connection conn = null; // 2、连接数据库 try { conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); } catch (SQLException e) { e.printStackTrace(); } return conn; } /** * 关闭Connection,Statement连接 * * @param conn * @param stmt */ public static void close(Connection conn, Statement stmt) { try { conn.close(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } /** * 关闭Connection,Statement,ResultSet连接 * * @param conn * @param stmt * @param rs */ public static void close(Connection conn, Statement stmt, ResultSet rs) { try { close(conn, stmt); rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 表结构行信息实体类 */ private class TableInfo { private String columnName; private String dataType; private String columnComment; private String columnKey; private String extra; TableInfo() { } String getColumnName() { return columnName; } void setColumnName(String columnName) { this.columnName = columnName; } String getDataType() { return dataType; } void setDataType(String dataType) { this.dataType = dataType; } String getColumnComment() { return columnComment; } void setColumnComment(String columnComment) { this.columnComment = columnComment; } String getColumnKey() { return columnKey; } void setColumnKey(String columnKey) { this.columnKey = columnKey; } String getExtra() { return extra; } void setExtra(String extra) { this.extra = extra; } } /** * 快速创建,供外部调用,调用之前先设置一下项目的基础路径 */ private String create() { List
tableInfo = getTableInfo(); createPojo(tableInfo); createVo(tableInfo); createRepository(tableInfo); createService(tableInfo); createController(tableInfo); return tableName + " 后台代码生成完毕!"; } public static void main(String[] args) { String[] tables = {"ims_user", "ims_friend", "ims_friend_message"}; for (int i = 0; i < tables.length; i++) { String msg = new CodeDOM(tables[i]).create(); System.out.println(msg); } }}
CodeDOM.java

 

  工程结构

  工程是一个springboot项目,跟我们之前一样使用lombok、thymeleaf等

  tip.js、tip.css放在static的js、css里面

 

  我们在ims_user表生成的controller、service层新增登录、登出等几个接口

@RestController@RequestMapping("/imsUser/")public class ImsUserController extends CommonController
{ @Autowired private ImsUserService imsUserService; /** * 跳转登录、注册页面 */ @RequestMapping("loginPage.html") public ModelAndView loginPage() { return new ModelAndView("login.html"); } /** * 跳转聊天页面 */ @RequestMapping("socketChart/{username}.html") public ModelAndView socketChartPage(@PathVariable String username) { return new ModelAndView("socketChart.html","username",username); } /** * 登录 */ @PostMapping("login") public Result
login(ImsUserVo userVo) { //加密后再去对比密文 userVo.setPassword(MD5Util.getMD5(userVo.getPassword())); Result
> result = list(userVo); if(result.isFlag() && result.getData().size() > 0){ ImsUserVo imsUserVo = result.getData().get(0); //置空隐私信息 imsUserVo.setPassword(null); //add WebSocketServer.loginList WebSocketServer.loginList.add(imsUserVo.getUserName()); return Result.of(imsUserVo); }else{ return Result.of(null,false,"账号或密码错误!"); } } /** * 登出 */ @RequestMapping("logout/{username}") public String loginOut(HttpServletRequest request, @PathVariable String username) { new WebSocketServer().deleteUserByUsername(username); return "退出成功!"; } /** * 获取在线用户 */ @PostMapping("getOnlineList") private List
getOnlineList(String username) { List
list = new ArrayList
(); //遍历webSocketMap for (Map.Entry
entry : WebSocketServer.getSessionMap().entrySet()) { if (!entry.getKey().equals(username)) { list.add(entry.getKey()); } } return list; }}
ImsUserController.java
@Service@Transactionalpublic class ImsUserServiceImpl extends CommonServiceImpl
implements ImsUserService{ @PersistenceContext private EntityManager em; @Autowired private ImsUserRepository imsUserRepository; @Override public Result
save(ImsUserVo entityVo) { //先查询是否已经存在相同账号 ImsUserVo imsUserVo = new ImsUserVo(); imsUserVo.setUserName(entityVo.getUserName()); if(list(imsUserVo).getData().size() > 0){ return Result.of(null,false,"账号已存在!"); } //存储密文 entityVo.setPassword(MD5Util.getMD5(entityVo.getPassword())); return super.save(entityVo); }}
ImsUserServiceImpl.java

 

  WebSocketServer也有优化调整

package cn.huanzi.ims.socket;import cn.huanzi.ims.imsuser.service.ImsUserService;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Map.Entry;/** * WebSocket服务 */@RestController@RequestMapping("/websocket")@ServerEndpoint(value = "/websocket/{username}", configurator = MyEndpointConfigure.class)public class WebSocketServer {    /**     * 在线人数     */    private static int onlineCount = 0;    /**     * 在线用户的Map集合,key:用户名,value:Session对象     */    private static Map
sessionMap = new HashMap
(); /** * 登录用户集合 */ public static List
loginList = new ArrayList<>(); public static Map
getSessionMap(){ return sessionMap; } /** * 注入其他类(换成自己想注入的对象) */ private ImsUserService imsUserService; /** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("username") String username) { //在webSocketMap新增上线用户 sessionMap.put(username, session); //在线人数加加 WebSocketServer.onlineCount++; //通知除了自己之外的所有人 sendOnlineCount(username, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + username + "'}"); } /** * 连接关闭调用的方法 */ @OnClose public void onClose(Session session) { //下线用户名 String logoutUserName = ""; //从webSocketMap删除下线用户 for (Entry
entry : sessionMap.entrySet()) { if (entry.getValue() == session) { sessionMap.remove(entry.getKey()); logoutUserName = entry.getKey(); break; } } deleteUserByUsername(logoutUserName); } /** * 服务器接收到客户端消息时调用的方法 */ @OnMessage public void onMessage(String message, Session session) { try { //JSON字符串转 HashMap HashMap hashMap = new ObjectMapper().readValue(message, HashMap.class); //消息类型 String type = (String) hashMap.get("type"); //来源用户 Map srcUser = (Map) hashMap.get("srcUser"); //目标用户 Map tarUser = (Map) hashMap.get("tarUser"); //如果点击的是自己,那就是群聊 if (srcUser.get("username").equals(tarUser.get("username"))) { //群聊 groupChat(session,hashMap); } else { //私聊 privateChat(session, tarUser, hashMap); } //后期要做消息持久化 } catch (IOException e) { e.printStackTrace(); } } /** * 发生错误时调用 */ @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } /** * 通知除了自己之外的所有人 */ private void sendOnlineCount(String username, String message) { for (Entry
entry : sessionMap.entrySet()) { try { if (entry.getKey() != username) { entry.getValue().getBasicRemote().sendText(message); } } catch (IOException e) { e.printStackTrace(); } } } /** * 私聊 */ private void privateChat(Session session, Map tarUser, HashMap hashMap) throws IOException { //获取目标用户的session Session tarUserSession = sessionMap.get(tarUser.get("username")); //如果不在线则发送“对方不在线”回来源用户 if (tarUserSession == null) { session.getBasicRemote().sendText("{\"type\":\"0\",\"message\":\"对方不在线\"}"); } else { hashMap.put("type", "1"); tarUserSession.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(hashMap)); } } /** * 群聊 */ private void groupChat(Session session, HashMap hashMap) throws IOException { for (Entry
entry : sessionMap.entrySet()) { //自己就不用再发送消息了 if (entry.getValue() != session) { hashMap.put("type", "2"); entry.getValue().getBasicRemote().sendText(new ObjectMapper().writeValueAsString(hashMap)); } } } /** 删除用户 */ public void deleteUserByUsername(String username){ //在线人数减减 WebSocketServer.onlineCount--; WebSocketServer.loginList.remove(username); //通知除了自己之外的所有人 sendOnlineCount(username, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + username + "'}"); }}
View Code

 

  先看一下我们的自定义web弹窗的js、css,跟之前相比有一点小升级

/* web弹窗 */.tip-msg {    background-color: rgba(61, 61, 61, 0.93);    color: #ffffff;    opacity: 0;    max-width: 200px;    position: fixed;    text-align: center;    line-height: 25px;    border-radius: 30px;    padding: 5px 15px;    display: inline-block;    z-index: 10000;}.tip-shade {    z-index: 9999;    background-color: rgb(0, 0, 0);    opacity: 0.6;    position: fixed;    top: 0;    left: 0;    width: 100%;    height: 100%;}.tip-dialog {    z-index: 9999;    position: fixed;    display: block;    background: #e9e9e9;    border-radius: 5px;    opacity: 0;    border: 1px solid #dad8d8;    box-shadow: 0px 1px 20px 2px rgb(255, 221, 221);}.tip-title {    cursor: move;    padding: 5px;    position: relative;    height: 25px;    border-bottom: 1px solid #dad8d8;    user-select: none;}.tip-title-text {    margin: 0;    padding: 0;    font-size: 15px;}.tip-title-btn {    position: absolute;    top: 5px;    right: 5px;}.tip-content {    padding: 8px;    position: relative;    word-break: break-all;    font-size: 14px;    overflow-x: hidden;    overflow-y: auto;}.tip-resize {    position: absolute;    width: 15px;    height: 15px;    right: 0;    bottom: 0;    cursor: se-resize;}
tip.css
/** * 自定义web弹窗/层:简易风格的msg与可拖放的dialog * 依赖jquery */var tip = {    /**     * 初始化     */    init: function () {        var titleDiv = null;//标题元素        var dialogDiv = null;//窗口元素        var titleDown = false;//是否在标题元素按下鼠标        var resizeDown = false;//是否在缩放元素按下鼠标        var offset = {x: 0, y: 0};//鼠标按下时的坐标系/计算后的坐标        /*            使用 on() 方法添加的事件处理程序适用于当前及未来的元素(比如由脚本创建的新元素)。            问题:事件绑定在div上出现div移动速度跟不上鼠标速度,导致鼠标移动太快时会脱离div,从而无法触发事件。            解决:把事件绑定在document文档上,无论鼠标在怎么移动,始终是在文档范围之内。        */        //鼠标在标题元素按下        $(document).on("mousedown", ".tip-title", function (e) {            var event1 = e || window.event;            titleDiv = $(this);            dialogDiv = titleDiv.parent();            titleDown = true;            offset.x = e.clientX - parseFloat(dialogDiv.css("left"));            offset.y = e.clientY - parseFloat(dialogDiv.css("top"));        });        //鼠标移动        $(document).on("mousemove", function (e) {            var event2 = e || window.event;            var eveX = event2.clientX;             // 获取鼠标相对于浏览器x轴的位置            var eveY = event2.clientY;             // 获取鼠标相对于浏览器Y轴的位置            // var height = document.body.clientHeight;//表示HTML文档所在窗口的当前高度;            // var width = document.body.clientWidth;//表示HTML文档所在窗口的当前宽度;            var height = window.innerHeight;//浏览器窗口的内部高度;            var width = window.innerWidth;//浏览器窗口的内部宽度;            //在标题元素按下            if (titleDown) {                //处理滚动条                if (tip.hasXScrollbar()) {                    height = height - tip.getScrollbarWidth();                }                if (tip.hasYScrollbar()) {                    width = width - tip.getScrollbarWidth();                }                //上边                var top = (eveY - offset.y);                if (top <= 0) {                    top = 0;                }                if (top >= (height - dialogDiv.height())) {                    top = height - dialogDiv.height() - 5;                }                //左边                var left = (eveX - offset.x);                if (left <= 0) {                    left = 0;                }                if (left >= (width - dialogDiv.width())) {                    left = width - dialogDiv.width() - 5;                }                dialogDiv.css({                    "top": top + "px",                    "left": left + "px"                });            }            //在缩放元素按下            if (resizeDown) {                var newWidth = (dialogDiv.resize.width + (eveX - offset.x));                if (dialogDiv.resize.initWidth >= newWidth) {                    newWidth = dialogDiv.resize.initWidth;                }                var newHeight = (dialogDiv.resize.height + (eveY - offset.y));                if (dialogDiv.resize.initHeight >= newHeight) {                    newHeight = dialogDiv.resize.initHeight;                }                dialogDiv.css("width", newWidth + "px");                dialogDiv.find(".tip-content").css("height", newHeight + "px");            }        });        //鼠标弹起        $(document).on("mouseup", function (e) {            //清空对象            titleDown = false;            resizeDown = false;            titleDiv = null;            dialogDiv = null;            offset = {x: 0, y: 0};        });        //阻止按钮事件冒泡        $(document).on("mousedown", ".tip-title-min,.tip-title-max,.tip-title-close", function (e) {            e.stopPropagation();//阻止事件冒泡        });        //最小化        $(document).on("click", ".tip-title-min", function (e) {            // var height = document.body.clientHeight;//表示HTML文档所在窗口的当前高度;            // var width = document.body.clientWidth;//表示HTML文档所在窗口的当前宽度;            var height = window.innerHeight;//浏览器窗口的内部高度;            var width = window.innerWidth;//浏览器窗口的内部宽度;            var $parent = $(this).parents(".tip-dialog");            //显示浏览器滚动条            document.body.parentNode.style.overflowY = "auto";            //当前是否为最大化            if ($parent[0].isMax) {                $parent[0].isMax = false;                $parent.css({                    "top": $parent[0].topMin,                    "left": $parent[0].leftMin,                    "height": $parent[0].heightMin,                    "width": $parent[0].widthMin                });            }            //当前是否为最小化            if (!$parent[0].isMin) {                $parent[0].isMin = true;                $parent[0].bottomMin = $parent.css("bottom");                $parent[0].leftMin = $parent.css("left");                $parent[0].heightMin = $parent.css("height");                $parent[0].widthMin = $parent.css("width");                $parent.css({                    "top": "",                    "bottom": "5px",                    "left": 0,                    "height": "30px",                    "width": "95px"                });                $parent.find(".tip-title-text").css("display", "none");                $parent.find(".tip-content").css("display", "none");            } else {                $parent[0].isMin = false;                $parent.css({                    "top": $parent[0].topMin,                    "bottom": $parent[0].bottomMin,                    "left": $parent[0].leftMin,                    "height": $parent[0].heightMin,                    "width": $parent[0].widthMin                });                $parent.find(".tip-title-text").css("display", "block");                $parent.find(".tip-content").css("display", "block");            }        });        //最大化        $(document).on("click", ".tip-title-max", function (e) {            // var height = document.body.clientHeight;//表示HTML文档所在窗口的当前高度;            // var width = document.body.clientWidth;//表示HTML文档所在窗口的当前宽度;            var height = window.innerHeight;//浏览器窗口的内部高度;            var width = window.innerWidth;//浏览器窗口的内部宽度;            var $parent = $(this).parents(".tip-dialog");            //当前是否为最小化            if ($parent[0].isMin) {                $parent[0].isMin = false;                $parent.css({                    "top": $parent[0].topMin,                    "bottom": $parent[0].bottomMin,                    "left": $parent[0].leftMin,                    "height": $parent[0].heightMin,                    "width": $parent[0].widthMin                });                $parent.find(".tip-title h2").css("display", "block");            }            //当前是否为最大化            if (!$parent[0].isMax) {                //隐藏浏览器滚动条                document.body.parentNode.style.overflowY = "hidden";                $parent[0].isMax = true;                $parent[0].topMin = $parent.css("top");                $parent[0].leftMin = $parent.css("left");                $parent[0].heightMin = $parent.css("height");                $parent[0].widthMin = $parent.css("width");                $parent.css({                    "top": 0,                    "left": 0,                    "height": height - 5 + "px",                    "width": width - 5 + "px"                });            } else {                //显示浏览器滚动条                document.body.parentNode.style.overflowY = "auto";                $parent[0].isMax = false;                $parent.css({                    "top": $parent[0].topMin,                    "left": $parent[0].leftMin,                    "height": $parent[0].heightMin,                    "width": $parent[0].widthMin                });            }        });        //缩放        $(document).on("mousedown", ".tip-resize", function (e) {            var event1 = e || window.event;            dialogDiv = $(this).parent();            resizeDown = true;            offset.x = e.clientX;            offset.y = e.clientY;            //点击时的宽高            dialogDiv.resize.width = dialogDiv.width();            dialogDiv.resize.height = dialogDiv.find(".tip-content").height();        });        //关闭        $(document).on("click", ".tip-title-close", function (e) {            $(this).parents(".tip-dialog").parent().remove();            //显示浏览器滚动条            document.body.parentNode.style.overflowY = "auto";        });        //点击窗口优先显示        $(document).on("click", ".tip-dialog", function (e) {            $(".tip-dialog").css("z-index","9999");            $(this).css("z-index","10000");        });    },    /**     * 是否存在X轴方向滚动条     */    hasXScrollbar: function () {        return document.body.scrollWidth > (window.innerWidth || document.documentElement.clientWidth);    },    /**     * 是否存在Y轴方向滚动条     */    hasYScrollbar: function () {        return document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);    },    /**     * 计算滚动条的宽度     */    getScrollbarWidth: function () {        /*            思路:生成一个带滚动条的div,分析得到滚动条长度,然后过河拆桥         */        var scrollDiv = document.createElement("div");        scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';        document.body.appendChild(scrollDiv);        var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;        document.body.removeChild(scrollDiv);        return scrollbarWidth;    },    /**     * tip提示     * tip.msg("哈哈哈哈哈");     * tip.msg({text:"哈哈哈哈哈",time:5000});     */    msg: function (setting) {        var time = setting.time || 2000; // 显示时间(毫秒) 默认延迟2秒关闭        var text = setting.text || setting; // 文本内容        //组装HTML        var tip = "
" + text + "
"; //删除旧tip $(".tip-msg").remove(); //添加到body $("body").append(tip); //获取jq对象 var $tip = $(".tip-msg"); //动画过渡 $tip.animate({opacity: 1}, 500); //计算位置浏览器窗口上下、左右居中 // var height = document.body.clientHeight;//表示HTML文档所在窗口的当前高度; var width = document.body.clientWidth;//表示HTML文档所在窗口的当前宽度; var height = window.innerHeight;//浏览器窗口的内部高度; // var width = window.innerWidth;//浏览器窗口的内部宽度; width = ((width / 2) - ($tip.css("width").replace("px", "") / 2)) / width; height = ((height / 2) - ($tip.css("height").replace("px", "") / 2)) / height; $tip.css({ "top": (height * 100) + "%", "left": (width * 100) + "%" }); //延迟删除 setTimeout(function () { //动画过渡 $tip.animate({opacity: 0}, 500, function () { $tip.remove(); }); }, time); }, /** * 可拖放窗口 * tip.dialog({title:"测试弹窗标题",content:"测试弹窗内容"}); * tip.dialog({title:"测试弹窗标题",class:"myClassName",content:"

测试弹窗内容

",offset: ['100px', '50px'],area:["200px","100px"],shade:0,closeCallBack:function(){console.log('你点击了关闭按钮')}}); */ dialog: function (setting) { var title = setting.title || "这里是标题"; // 标题 var clazz = setting.class || ""; // class var content = setting.content || "这里是内容"; // 内容 var area = setting.area; // 宽高 var offset = setting.offset || "auto"; // 位置 上、左 var shade = setting.shade !== undefined ? setting.shade : 0.7;//遮阴 为0时无遮阴对象 //组装HTML var tip = "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "

\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
"; var $tip = $(tip); //添加到body $("body").append($tip); //设置遮阴 $tip.find(".tip-shade").css("opacity", shade); if (shade === 0) { $tip.find(".tip-shade").css({ "width": "0", "height": "0" }); } //获取dialog对象 $tip = $tip.find(".tip-dialog"); //标题 $tip.find(".tip-title-text").html(title); //内容 $tip.find(".tip-content").append(content); //设置初始宽高 if (area) { $tip.css({ "width": area[0], }); $tip.find(".tip-content").css({ "height": area[1] }); } //动画过渡 $tip.animate({opacity: 1}, 500); //计算位置浏览器窗口上下、左右居中 if (offset === "auto") { // var height = document.body.clientHeight;//表示HTML文档所在窗口的当前高度; var width = document.body.clientWidth;//表示HTML文档所在窗口的当前宽度; var height = window.innerHeight;//浏览器窗口的内部高度; // var width = window.innerWidth;//浏览器窗口的内部宽度; width = ((width / 2) - ($tip.css("width").replace("px", "") / 2)) / width; height = ((height / 2) - ($tip.css("height").replace("px", "") / 2)) / height; $tip.css({ "top": (height * 100) + "%", "left": (width * 100) + "%" }); } else if (Array.isArray(offset)) { $tip.css({ "top": offset[0], "left": offset[1] }); } //初始值宽高 $tip.resize.initWidth = $tip.width(); $tip.resize.initHeight = $tip.find(".tip-content").height(); //绑定关闭回调 if(setting.closeCallBack){ $(".tip-title-close").click(function (e) { setting.closeCallBack(); }); } }};//初始化tip.init();
tip.js

 

  接下来就是登录/注册页面跟聊天页面的HTML、JS的修改,我们先定义一个head.html作为一个公用head在其他地方引入

head.html

 

    登录/注册    
login.html
h3 {    margin: 0 0 5px 0;    text-align: center;}.login {    width: 250px;    background: #e9e9e9;    border-radius: 5px;    margin: 0 auto;    border: 1px solid #e9e9e9;    padding: 10px;}.login > form {    margin: 0;}.login:focus-within {    border: 1px solid #10b7f3;    background: #caefff;}.register {    width: 350px;    background: #e9e9e9;    border-radius: 5px;    margin: 0 auto;    border: 1px solid #e9e9e9;    padding: 10px;    display: none;}.register > form {    margin: 0;}.register > table,.login > table {    margin: 0 auto;}.register:focus-within {    border: 1px solid #10b7f3;    background: #caefff;}
login.css
let $login = "
\n" + "

登录

\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
账号:
密码:
注册 登录
\n" + "
\n" + "
";let $register = "
\n" + "
\n" + "

注册

\n" + "\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + " \n" + " \n" + "
\n" + "
\n" + "
\n" + "
\n" + "
账号:
密码:
昵称:
性别: \n" + " 男\n" + " 女\n" + "
头像:
电子邮箱:
手机号码:
个性签名:
登录 注册
\n" + "
\n" + "
";tip.dialog({title: "登录/注册", content: $login + $register, shade: 0});//切换登录、注册页面function switchover() { if ($(".login").css("display") === "none") { $(".login").show(); $(".register").hide(); } else { $(".register").show(); $(".login").hide(); }}//提交注册function register() { let newTime = commonUtil.getNowTime(); $("#createdTime").val(newTime); $("#updataTime").val(newTime); $("#avatar").val("/image/logo.png"); console.log($("#registerForm").serializeObject()); $.post(ctx + "/imsUser/save", $("#registerForm").serializeObject(), function (data) { if (data.flag) { switchover(); } // tip提示 tip.msg(data.msg); });}//提交登录function login() { console.log($("#loginForm").serializeObject()); $.post(ctx + "/imsUser/login", $("#loginForm").serializeObject(), function (data) { if (data.flag) { window.location.href = ctx + "/imsUser/socketChart/" + data.data.userName + ".html" } else { // tip提示 tip.msg(data.msg); } }); return false;}
login.js

 

聊天页面
socketChart.html
//追加tip页面let hzGroup = "" +    "
\n" + " 在线人数:
0\n" + "
\n" + "
\n" + "\n" + "
\n" + "
";tip.dialog({title: "" + username + "", content: hzGroup, offset: ["10%", "80%"], shade: 0, closeCallBack: function () { console.log("dfasdfasd") window.location.href = ctx + "/imsUser/logout/" + username; }});//聊天页面let hzMessage = "" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "\n" + "
\n" + "
";//消息对象数组var msgObjArr = [];//websocket对象var websocket = null;//判断当前浏览器是否支持WebSocketif ('WebSocket' in window) { websocket = new WebSocket("ws://localhost:10086/websocket/" + username);} else { console.error("不支持WebSocket");}//连接发生错误的回调方法websocket.onerror = function (e) { console.error("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function () { //获取所有在线用户 $.ajax({ type: 'post', url: ctx + "/imsUser/getOnlineList", contentType: 'application/json;charset=utf-8', dataType: 'json', data: {username: username}, success: function (data) { if (data.length) { //列表 for (let i = 0; i < data.length; i++) { var userName = data[i]; var text = "
" + userName + "
[在线]
0
"; //把自己排在第一个 if (username === userName) { $("#hz-group-body").prepend(text); } else { $("#hz-group-body").append(text); } } //在线人数 $("#onlineCount").text(data.length); } }, error: function (xhr, status, error) { console.log("ajax错误!"); } });};//接收到消息的回调方法websocket.onmessage = function (event) { var messageJson = eval("(" + event.data + ")"); //普通消息(私聊) if (messageJson.type === "1") { //来源用户 var srcUser = messageJson.srcUser; //目标用户 var tarUser = messageJson.tarUser; //消息 var message = messageJson.message; //最加聊天数据 setMessageInnerHTML(srcUser.username, srcUser.username, message); } //普通消息(群聊) if (messageJson.type === "2") { //来源用户 var srcUser = messageJson.srcUser; //目标用户 var tarUser = messageJson.tarUser; //消息 var message = messageJson.message; //最加聊天数据 setMessageInnerHTML(username, tarUser.username, message); } //对方不在线 if (messageJson.type === "0") { //消息 var message = messageJson.message; $("#hz-message-body").append( "
" + "
" + "
" + message + "" + "
" + "
"); } //在线人数 if (messageJson.type === "onlineCount") { //取出username var onlineCount = messageJson.onlineCount; var userName = messageJson.username; var oldOnlineCount = $("#onlineCount").text(); //新旧在线人数对比 if (oldOnlineCount < onlineCount) { if ($("#" + userName + "-status").length > 0) { $("#" + userName + "-status").text("[在线]"); $("#" + userName + "-status").css("color", "#497b0f"); } else { $("#hz-group-body").append("
" + userName + "
[在线]
0
"); } } else { //有人下线 $("#" + userName + "-status").text("[离线]"); $("#" + userName + "-status").css("color", "#9c0c0c"); } $("#onlineCount").text(onlineCount); }};//连接关闭的回调方法websocket.onclose = function () { //alert("WebSocket连接关闭");};//将消息显示在对应聊天窗口 对于接收消息来说这里的toUserName就是来源用户,对于发送来说则相反function setMessageInnerHTML(srcUserName, msgUserName, message) { //判断 var childrens = $("#hz-group-body").children(".hz-group-list"); var isExist = false; for (var i = 0; i < childrens.length; i++) { var text = $(childrens[i]).find(".hz-group-list-username").text(); if (text == srcUserName) { isExist = true; break; } } if (!isExist) { //追加聊天对象 msgObjArr.push({ toUserName: srcUserName, message: [{username: msgUserName, message: message, date: nowTime()}]//封装数据 }); $("#hz-group-body").append("
" + srcUserName + "
[在线]
0
"); } else { //取出对象 var isExist = false; for (var i = 0; i < msgObjArr.length; i++) { var obj = msgObjArr[i]; if (obj.toUserName == srcUserName) { //保存最新数据 obj.message.push({username: msgUserName, message: message, date: nowTime()}); isExist = true; break; } } if (!isExist) { //追加聊天对象 msgObjArr.push({ toUserName: srcUserName, message: [{username: msgUserName, message: message, date: nowTime()}]//封装数据 }); } } //刚好有打开的是对应的聊天页面 if ($(".tip" + srcUserName).length > 0) { $(".tip" + srcUserName + " #hz-message-body").append( "
" + "

" + msgUserName + "

" + "
" + "
" + "
" + message + "" + "
" + "
" + "
"); } else { //小圆点++ var conut = $("#hz-badge-" + srcUserName).text(); $("#hz-badge-" + srcUserName).text(parseInt(conut) + 1); $("#hz-badge-" + srcUserName).css("opacity", "1"); }}//发送消息function send(but) { //目标用户名 var tarUserName = $(but).parents(".tip-dialog").find("#toUserName").text(); //登录用户名 var srcUserName = $("#talks").text(); //消息 var message = $(but).parents(".tip-dialog").find("#hz-message-input").html(); websocket.send(JSON.stringify({ "type": "1", "tarUser": {"username": tarUserName}, "srcUser": {"username": srcUserName}, "message": message })); $(".tip" + tarUserName + " #hz-message-body").append( "
" + "
" + "
" + "
" + message + "" + "
" + "
"); $(".tip" + tarUserName + " #hz-message-input").html(""); //取出对象 if (msgObjArr.length > 0) { var isExist = false; for (var i = 0; i < msgObjArr.length; i++) { var obj = msgObjArr[i]; if (obj.toUserName == tarUserName) { //保存最新数据 obj.message.push({username: srcUserName, message: message, date: nowTime()}); isExist = true; break; } } if (!isExist) { //追加聊天对象 msgObjArr.push({ toUserName: tarUserName, message: [{username: srcUserName, message: message, date: nowTime()}]//封装数据[{username:huanzi,message:"你好,我是欢子!",date:2018-04-29 22:48:00}] }); } } else { //追加聊天对象 msgObjArr.push({ toUserName: tarUserName, message: [{username: srcUserName, message: message, date: nowTime()}]//封装数据[{username:huanzi,message:"你好,我是欢子!",date:2018-04-29 22:48:00}] }); }}//监听点击用户$("body").on("click", ".hz-group-list", function () { var toUserName = $(this).find(".hz-group-list-username").text(); //弹出聊天页面 tip.dialog({ title: "正在与 聊天", class: "tip" + toUserName, content: hzMessage, shade: 0 }); // $(".hz-group-list").css("background-color", ""); // $(this).css("background-color", "whitesmoke"); $(".tip" + toUserName + " #toUserName").text(toUserName); //清空小圆点 $("#hz-badge-" + toUserName).text("0"); $("#hz-badge-" + toUserName).css("opacity", "0"); if (msgObjArr.length > 0) { for (var i = 0; i < msgObjArr.length; i++) { var obj = msgObjArr[i]; if (obj.toUserName === toUserName) { //追加数据 var messageArr = obj.message; if (messageArr.length > 0) { for (var j = 0; j < messageArr.length; j++) { var msgObj = messageArr[j]; var leftOrRight = "right"; var message = msgObj.message; var msgUserName = msgObj.username; //当聊天窗口与msgUserName的人相同,文字在左边(对方/其他人),否则在右边(自己) if (msgUserName === toUserName) { leftOrRight = "left"; } //但是如果点击的是自己,群聊的逻辑就不太一样了 if (username === toUserName && msgUserName !== toUserName) { leftOrRight = "left"; } if (username === toUserName && msgUserName === toUserName) { leftOrRight = "right"; } var magUserName = leftOrRight === "left" ? "

" + msgUserName + "

" : ""; $(".tip" + toUserName + " #hz-message-body").append( "
" + magUserName + "
" + "
" + "
" + message + "" + "
" + "
" + "
"); } } break; } } }});//获取当前时间function nowTime() { var time = new Date(); var year = time.getFullYear();//获取年 var month = time.getMonth() + 1;//或者月 var day = time.getDate();//或者天 var hour = time.getHours();//获取小时 var minu = time.getMinutes();//获取分钟 var second = time.getSeconds();//或者秒 var data = year + "-"; if (month < 10) { data += "0"; } data += month + "-"; if (day < 10) { data += "0" } data += day + " "; if (hour < 10) { data += "0" } data += hour + ":"; if (minu < 10) { data += "0" } data += minu + ":"; if (second < 10) { data += "0" } data += second; return data;}
socketChart.js
#hz-main {    width: 700px;    height: 500px;    background-color: red;    margin: 0 auto;}#hz-message {    width: 500px;    float: left;    background-color: #B5B5B5;}#hz-message-body {    width: 460px;    height: 340px;    background-color: #E0C4DA;    padding: 10px 20px;    overflow: auto;}#hz-message-input {    width: 500px;    height: 99px;    background-color: white;    overflow: auto;}#hz-group {    width: 200px;    height: 500px;    background-color: rosybrown;    float: right;}.hz-message-list {    min-height: 30px;    margin: 10px 0;}.hz-message-list-text {    padding: 7px 13px;    border-radius: 15px;    width: auto;    max-width: 85%;    display: inline-block;}.hz-message-list-username {    margin: 0 0 0 25px;}.hz-group-body {    overflow: auto;}.hz-group-list {    padding: 10px;    line-height: 23px;}.hz-group-list:hover{    background-color: whitesmoke;}.left {    float: left;    color: #595a5a;    background-color: #ebebeb;}.right {    float: right;    color: #f7f8f8;    background-color: #919292;}.hz-badge {    width: 20px;    height: 20px;    background-color: #FF5722;    border-radius: 50%;    float: right;    color: white;    text-align: center;    line-height: 20px;    font-weight: bold;    opacity: 0;}
socketChart.css

 

  演示效果

  注册、登录、登出

 

  登录三个账号,上下线提示功能

  

  模拟私聊

  

  模拟群聊(目前点击自己头像是群聊频道)

 

  总结

  第一版先实现到这里。第二版预计实现消息存储、离线推送,好友分组,好友搜索、添加,以及一些其他优化;欢迎大家指出不足之处!

转载地址:http://wlpez.baihongyu.com/

你可能感兴趣的文章
遇到问题之-httpd服务启动报错182行错误
查看>>
pycharm如何设置(错误、警告类的标准提醒)
查看>>
Python3运行的时候错误:ModuleNotFoundError: No module named 'PIL'
查看>>
PHP是世界上最好的语言?Phython第一个不服
查看>>
Bugku CTF-web6
查看>>
Bugku CTF-web10 头等舱
查看>>
UML-配置图
查看>>
JS高级面向对象(二)-构造函数和原型
查看>>
python入门到秃顶(10):异常
查看>>
ES6_变量生明
查看>>
考研复试英语问答
查看>>
百度背景换肤案例
查看>>
修改ng-zorro中table对齐及宽度等细节
查看>>
输出对象的值——踩坑
查看>>
angular2项目里使用排他思想
查看>>
折线图上放面积并隐藏XY轴的线
查看>>
failed to push some refs to git
查看>>
在苹果Mac上如何更改AirDrop名称?
查看>>
1110 Complete Binary Tree (25 point(s))
查看>>
541【毕设课设】基于单片机电阻电感电容RLC测量仪系统
查看>>