이전글
https://greed-yb.tistory.com/281
html
<style>
tr.selected {
background-color: #f1f1f1; /* 선택된 행의 배경색 변경 */
}
/* 네비 창 */
.sb-customizer {
z-index: 1050;
position: fixed;
top: 4.5rem;
width: 34rem;
height: 90vh;
text-align: left;
background: #fff;
transition: right .5s;
box-shadow: -0.15rem 0 1.75rem 0 rgb(34 39 46 / 15%);
}
.sb-customizer.sb-customizer-open {
right: 0;
}
.sb-customizer.sb-customizer-closed {
right: -34rem;
}
.sb-customizer .sb-customizer-heading {
padding: 1.0rem;
font-size: 1.2rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: .05em;
color: #a7aeb8;
}
.sb-customizer .sb-customizer-content {
position: relative;
height: calc(100% - 8.5rem);
/*height: 100%;*/
overflow-y: scroll;
padding: 0.5rem;
}
/*.sb-customizer .sb-customizer-toggler {*/
/* position: absolute;*/
/* !*top: 4.625rem;*!*/
/* display: flex;*/
/* align-items: center;*/
/* justify-content: center;*/
/* left: -3rem;*/
/* width: 3rem;*/
/* height: 3rem;*/
/* border-top-left-radius: 0.35rem;*/
/* border-bottom-left-radius: 0.35rem;*/
/* border-top-right-radius: 0;*/
/* border-bottom-right-radius: 0;*/
/* border: 0;*/
/* box-shadow: 0 0.15rem 1.75rem 0 rgb(34 39 46 / 15%);*/
/* color: #001500;*/
/* background-color: #fff;*/
/*}*/
#messenger_dataTable {
width: 100% !important;
table-layout: fixed;
}
#roomList_dataTable {
width: 100% !important;
table-layout: fixed;
}
.text-red-200 {
color: #d62516 !important;
}
/*#messenger_dataTable td {*/
/* word-wrap: break-word;*/
/*}*/
</style>
<!-- 메신저 창 -->
<div config-right id="config-switcher" class="sb-customizer sb-customizer-closed">
<div config-right class="sb-customizer-heading shadow d-flex justify-content-end row m-0 p-1">
<div id="add-switcher-title" class="col-md-6">
<h6 class="mt-2 font-weight-bold text-primary float-left">메신저</h6>
</div>
<div class="col-md-6">
<button onclick="cancelForm()" class="btn btn-danger btn-sm mx-1 float-right">닫기
</button>
</div>
</div>
<div id="messengerDiv" class="card-body mb-4" style="display: block">
<div class="table-responsive">
<table class="table table-bordered" id="messenger_dataTable">
</table>
</div>
<div>
<button onclick="openRoomList()" class="btn btn-dark btn-sm mx-1 float-right">대화방 목록
</button>
</div>
</div>
<div id="roomDiv" class="card-body mb-4" style="display: none">
<div class="table-responsive">
<table class="table table-bordered" id="roomList_dataTable">
</table>
</div>
<div>
<button onclick="backChatRoom()" class="btn btn-dark btn-sm mx-1 float-left">이전
</button>
</div>
</div>
<div id="chatRoomDiv" class="card-body mb-4" style="display: none">
<div class="card-header py-3" style="text-align: left">
<button class="btn btn-info btn-sm mx-1 float-left" onclick="openRoomList()()">이전</button>
<h5 class="m-0 font-weight-bold text-dark text-center" id="chatRoomTitle"></h5>
</div>
<div class="card shadow mb-4">
<div id="msgDiv"class="card-body" style="height: 600px; overflow: auto;">
<div id="msgArea" class="row">
</div>
</div>
<div>
<div class="input-group mb-3">
<input type="text" id="msg" class="form-control" aria-label="Recipient's username" aria-describedby="button-addon2">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" id="send" onclick="sendMsg()">전송</button>
</div>
</div>
</div>
</div>
</div>
</div>
구현 화면에 있는 전체 코드가 아닌 메신저 부분 코드만 올렸다
widgetWebSocket 으로 변경되는 top 부분 메시지 알람 및 사내 메신저 부분은 빠져 있으며 js 만 올리려 한다
style 의 sb-customizer 설정은 메신저 div 가 오른쪽에서 나오는 효과를 주고 있으며,
messenger_dataTable 과 roomList_dataTable 은 DataTable 크기를 고정하기 위함이다
Bootstrap 을 사용하지 않거나 또는 다른 버전을 사용하는 경우,
본인의 프로젝트에 맞게 style을 바꾸길 바란다
Javascript
// 메신저
let messenger_Info = [];
let messenger_dataTable;
// 방 목록
let roomList_Info = [];
let roomList_dataTable;
// 방 id
let roomId = null;
// 상대방 id
let targetId = null;
// 채팅방 웹소켓
let ws;
// widget 웹소켓
let ws_widget;
let ws_targetUser;
/**
* 읽지 않은 메신저 글 수
*/
function messengerReadCount(){
$.ajax({
url: "/messengerReadCount",
type: "GET",
success: function (data) {
if (data != null) {
if(data > 0){
// 사내 메신저 카운트 및 이모티콘 색상 변경
$(".fa-comments").removeClass('text-gray-300')
$(".fa-comments").removeClass('text-red-200')
$(".fa-comments").addClass('text-red-200')
$("#alertSpan").remove();
// top.html 알람 표시
if(data > 3){
$("#alertsDropdown").append('<span id="alertSpan" class="badge badge-danger badge-counter">3+</span>');
}else{
$("#alertsDropdown").append('<span id="alertSpan" class="badge badge-danger badge-counter">' + data +'</span>');
}
}else {
$("#alertSpan").remove();
// 사내 메신저 카운트 및 이모티콘 색상 변경
$(".fa-comments").removeClass('text-red-200')
$(".fa-comments").removeClass('text-gray-300')
$(".fa-comments").addClass('text-gray-300')
// top.html 알람 표시
$("#alertsDropdown").append('<span id="alertSpan"></span>');
}
// 사내 메신저 카운트 및 이모티콘 색상 변경
$("#readCount").text(data);
}
},
error(e) {
console.log("error : " + e);
}
});
}
/**
* 메신저 열고 닫기
*/
function openForm() {
$('#config-switcher').removeClass('sb-customizer-closed');
$('#config-switcher').addClass('sb-customizer-open');
$("#messengerDiv").css("display","block");
$("#chatRoomDiv").css("display","none");
$("#roomDiv").css("display","none");
// 유저 정보 가져오기
messenger();
}
function cancelForm() {
// 채팅방 id 초기화
roomId = null;
// 웹소켓 연결해제
if(ws != null){
ws.close();
}
$('#config-switcher').addClass('sb-customizer-closed');
$('#config-switcher').removeClass('sb-customizer-open');
}
function messenger(){
// 모든 유저
$.ajax({
url: "/messengerInfo",
type: "GET",
success: function (data) {
if (data != null) {
messenger_Info = data;
messenger_Init(messenger_Info);
}
},
error(e) {
console.log("error : " + e);
}
});
}
/**
* 메신저 유저 정보 DataTables 출력
* @param data
*/
function messenger_Init(data){
if ($.fn.DataTable.isDataTable('#messenger_dataTable')) {
$('#messenger_dataTable').DataTable().destroy(); // 기존 인스턴스 제거
// role_dataTable.destroy();
}
messenger_dataTable = $("#messenger_dataTable").DataTable({
data: data, // 가져온 데이터
"paging": false, // 페이징
"searching": true, // 검색 기능
"info": false, // 정보 표시
"ordering": false, // 정렬 기능
"scrollCollapse": true,
"scrollY": '600px',
"scrollX": "100%",
"scrollXInner": "100px",
"bAutoWidth": false,
"language": { // 언어 설정
decimal: "",
emptyTable: "검색된 데이터가 없습니다.",
info: "Showing _START_ to _END_ of _TOTAL_ entries",
infoEmpty: "Showing 0 to 0 of 0 entries",
infoFiltered: "(filtered from _MAX_ total entries)",
infoPostFix: "",
thousands: ",",
lengthMenu: " _MENU_ ",
loadingRecords: "Loading...",
processing: "Processing...",
search: "Search",
zeroRecords: "항목이 존재하지 않습니다",
paginate: {
"first": "처음",
"last": "마지막",
"next": "다음",
"previous": "이전"
},
aria: {
sortAscending: ": activate to sort column ascending",
sortDescending: ": activate to sort column descending"
}
},
"columnDefs": [
{ width: "95%", targets : [0] }
],
"columns": [
{
data: null,
render: function (data, type, row) {
if(row.profile == null || row.profile == ''){
return '<img src="/uploadPath/image/topFile_cc49d4f3-5119-43b0-931e-5e7bb5aaa8df.jpg" id=" '+ data.id + '"/>'+data.name
}else{
return '<img src="/uploadPath/image/topFile_' + row.profile + '"' + 'id=" '+ data.id + '"/>'+data.name
}
}
}
]
});
/* 선택된 tr 행 색상 변경 및 권한에 해당하는 메뉴 정보 가져오기*/
$('#messenger_dataTable tbody').on('click', 'tr', function () {
let tr = $(this).closest('tr');
let row = messenger_dataTable.row(tr);
if (tr.hasClass('selected')) {
tr.removeClass('selected');
} else {
messenger_dataTable.$('tr.selected').removeClass('selected');//테이블에서 선택된 것 삭제
tr.addClass('selected');
}
});
// 리스너를 제거하고 다시 등록하는 과정으로 off 와 on 을 두어서 dbclick 할때 tr 행이 중복으로 눌리는 것을 방지한다
$('#messenger_dataTable tbody').off('dblclick', 'tr').on('dblclick', 'tr', function () {
let tr = $(this).closest('tr');
let row = messenger_dataTable.row(tr);
targetId = row.data().id;
$.ajax({
url: "/chatRoomInfo",
type: "GET",
data: {"targetId" : targetId , "userId" : user_info.id},
success: function (data) {
if (data != "") {
$("#msgArea").empty();
$("#chatRoomTitle").text(row.data().name);
$("#messengerDiv").css("display","none");
$("#chatRoomDiv").css("display","block");
roomId = data[0].roomId;
for (let i = 0; i < data.length; i++) {
if(data[i].sendUserId == user_info.id){
let str = "<div clas='col-12' style='width:100%'><div class='col-10' style='float: right'>";
str += "<div style='text-align: right; background-color: #f8f9fc;'>";
str += "<b>" + data[i].roomDetail + "</b>";
str += "</div></div></div>";
$("#msgArea").append(str);
}else{
let str = "<div class='col-12' style='width:100%'><div class='col-10' style='float: left'>";
str += "<div style='text-align: left'>";
str += "<b>" + data[i].roomDetail + "</b>";
str += "</div></div></div>";
$("#msgArea").append(str);
}
}
$('#msgDiv').scrollTop($('#msgDiv')[0].scrollHeight)
websocketOpen(roomId);
}else{
roomId = null;
$("#msgArea").empty();
$("#chatRoomTitle").text(row.data().name);
$("#messengerDiv").css("display","none");
$("#chatRoomDiv").css("display","block");
}
},
error(e) {
console.log("error : " + e);
},
complete: function (){
messengerReadCount();
}
});
});
}
/**
* 채팅방 목록
*/
function roomList(){
$.ajax({
url: "/roomList",
data: {"id" : user_info.id},
type: "GET",
success: function (data) {
if (data != null) {
roomList_Info = data;
roomList_Init(roomList_Info);
}
},
error(e) {
console.log("error : " + e);
}
});
}
/**
* 채팅방 목록
* @param data
*/
function roomList_Init(data){
if ($.fn.DataTable.isDataTable('#roomList_dataTable')) {
$('#roomList_dataTable').DataTable().destroy(); // 기존 인스턴스 제거
}
roomList_dataTable = $("#roomList_dataTable").DataTable({
data: data, // 가져온 데이터
"paging": false, // 페이징
"searching": true, // 검색 기능
"info": false, // 정보 표시
"ordering": false, // 정렬 기능
"scrollCollapse": true,
"scrollY": '600px',
"scrollX": "100%",
"scrollXInner": "100px",
"bAutoWidth": false,
"language": { // 언어 설정
decimal: "",
emptyTable: "대화중인 방이 없습니다.",
info: "Showing _START_ to _END_ of _TOTAL_ entries",
infoEmpty: "Showing 0 to 0 of 0 entries",
infoFiltered: "(filtered from _MAX_ total entries)",
infoPostFix: "",
thousands: ",",
lengthMenu: " _MENU_ ",
loadingRecords: "Loading...",
processing: "Processing...",
search: "Search",
zeroRecords: "항목이 존재하지 않습니다",
paginate: {
"first": "처음",
"last": "마지막",
"next": "다음",
"previous": "이전"
},
aria: {
sortAscending: ": activate to sort column ascending",
sortDescending: ": activate to sort column descending"
}
},
"columnDefs": [
{ width: "95%", targets : [0] }
],
"columns": [
{
data: null,
render: function (data, type, row) {
if(row.profile == null || row.profile == ''){
let str ="";
if(data.unreadCount == 0){
str = "<div clas='row' style='width:100%'><div class='col-4' style='float: left'>";
str += "<img src='/uploadPath/image/topFile_cc49d4f3-5119-43b0-931e-5e7bb5aaa8df.jpg' id=' " + data.roomId + "'/>" + data.roomTitle + "</div>";
str += "<div class='col-7' style='float: left'>"+ data.roomDetail + "</div></div>";
}else{
str = "<div clas='row' style='width:100%'><div class='col-4' style='float: left'>";
str += "<img src='/uploadPath/image/topFile_cc49d4f3-5119-43b0-931e-5e7bb5aaa8df.jpg' id=' " + data.roomId + "'/>" + data.roomTitle + "</div>";
str += "<div class='col-7' style='float: left'>"+ data.roomDetail + "</div>";
str += "<div class='col-1' style='float: right'>"+ "<b style='color:red'>" + data.unreadCount + "</b>"+"</div></div>";
}
return str;
}else{
let str ="";
if(data.unreadCount == 0){
str = "<div clas='row' style='width:100%'><div class='col-4' style='float: left'>";
str += "<img src='/uploadPath/image/topFile_" + row.profile + "'" + "id=' " + data.roomId + "'/>" + data.roomTitle + "</div>";
str += "<div class='col-7' style='float: left'>"+ data.roomDetail + "</div></div>";
}else{
str = "<div clas='row' style='width:100%'><div class='col-4' style='float: left'>";
str += "<img src='/uploadPath/image/topFile_" + row.profile + "'" + "id=' " + data.roomId + "'/>" + data.roomTitle + "</div>";
str += "<div class='col-7' style='float: left'>"+ data.roomDetail + "</div>";
str += "<div class='col-1' style='float: right'>"+"<b style='color:red'>" + data.unreadCount + "</b>"+"</div></div>";
}
return str;
}
}
}
]
});
/* 선택된 tr 행 색상 변경 및 권한에 해당하는 메뉴 정보 가져오기*/
$('#roomList_dataTable tbody').on('click', 'tr', function () {
let tr = $(this).closest('tr');
let row = roomList_dataTable.row(tr);
if (tr.hasClass('selected')) {
tr.removeClass('selected');
} else {
roomList_dataTable.$('tr.selected').removeClass('selected');//테이블에서 선택된 것 삭제
tr.addClass('selected');
}
});
// 리스너를 제거하고 다시 등록하는 과정으로 off 와 on 을 두어서 dbclick 할때 tr 행이 중복으로 눌리는 것을 방지한다
$('#roomList_dataTable tbody').off('dblclick', 'tr').on('dblclick', 'tr', function () {
let tr = $(this).closest('tr');
let row = roomList_dataTable.row(tr);
$.ajax({
url: "/chatRoomEnter",
type: "GET",
data: {"id" : row.data().roomId},
success: function (data) {
if (data != null) {
// append 태그 삭제
$("#msgArea").empty();
// 채티방 타이틀 변경
$("#chatRoomTitle").text(row.data().roomTitle);
// 유저 목록 display none
$("#messengerDiv").css("display","none");
// 채팅방 display block
$("#chatRoomDiv").css("display","block");
// 채팅방 목록 display none
$("#roomDiv").css("display","none");
// 채팅방 id
roomId = row.data().roomId;
for (let i = 0; i < data.length; i++) {
if(data[i].sendUserId == user_info.id){
let str = "<div clas='col-12' style='width:100%'><div class='col-10' style='float: right'>";
str += "<div style='text-align: right; background-color: #f8f9fc;'>";
str += "<b>" + data[i].roomDetail + "</b>";
str += "</div></div></div>";
$("#msgArea").append(str);
}else{
let str = "<div class='col-12' style='width:100%'><div class='col-10' style='float: left'>";
str += "<div style='text-align: left'>";
str += "<b>" + data[i].roomDetail + "</b>";
str += "</div></div></div>";
$("#msgArea").append(str);
}
}
// 스크롤 제일 하단으로 이동
$('#msgDiv').scrollTop($('#msgDiv')[0].scrollHeight)
// 채팅방 웹소켓 연결
websocketOpen(roomId);
//
}
},
error(e) {
console.log("error : " + e);
},
complete: function (){
// 메세지를 읽음 처리
messengerReadCount();
}
});
});
}
// 이전 버튼 - 유저 목록으로 이동
function backChatRoom(){
roomId = null;
$("#messengerDiv").css("display","block");
$("#chatRoomDiv").css("display","none");
$("#roomDiv").css("display","none");
$("#chatRoomTitle").text("");
messenger();
}
// 메시지 전송
function sendMsg(){
// 입력한 메시지 변수
let sendMsg = $("#msg").val();
// 입력한 데이터가 없다면 return
if(sendMsg == ''){
return;
}
// user_info.id 는 top.js 에서 계속 가지고 다니는 현재 사용자의 id
$.ajax({
url: "/sendMsg",
type: "POST",
data: {"id" : roomId , "roomDetail" : sendMsg, "targetId" : targetId , "userId" : user_info.id},
success: function (data) {
if (data.msg == "success") {
// 채팅방 id
roomId = data.roomId;
// 메시지를 받는 상대방 id
ws_targetUser = data.userId;
// 웹소켓이 연결되어있지않으면 연결
if(ws == null){
websocketOpen(roomId);
}
let sendData = new Object();
sendData.roomId = roomId;
sendData.roomDetail = sendMsg;
// user_info.id 는 top.js 에서 계속 가지고 다니는 현재 사용자의 id
sendData.sendUserId = user_info.id;
// 채팅방 웹소켓에 전송
ws.send(JSON.stringify(sendData));
$("#msg").val("");
}else {
alert("서버에 오류가 발생하였습니다.");
}
},
error(e) {
console.log("error : " + e);
},
complete: function (){
// widget 웹소켓이 연결되어있지않으면 연결
if(ws_widget == null){
// user_info.id 는 top.js 에서 계속 가지고 다니는 현재 사용자의 id
widgetWebsocket(user_info.id);
}
let widgetData = new Object();
widgetData.userId = ws_targetUser;
// 메시지를 받는 상대방 id를 넣고 메시지 전달 -> 채팅방이 아닌 widget 용임
ws_widget.send(JSON.stringify(widgetData));
ws_targetUser = null;
}
});
}
// 이전 버튼 - 채팅방 목록으로 이동
function openRoomList(){
$("#messengerDiv").css("display","none");
$("#chatRoomDiv").css("display","none");
$("#roomDiv").css("display","block");
$("#chatRoomTitle").text("");
if(ws != null){
ws.close();
}
roomList();
}
/**
* 웹소켓 연결
* @param roomId
*/
function websocketOpen(roomId){
// 본인의 ip와 port 입력
ws = new WebSocket("ws://xxx.xxx.xxx.xxx:xxxx/ws/chat/"+roomId);
// 웹소켓 연결 시 동작
ws.onopen = function (data) {
}
// 웹소켓으로 메시지가 왔을때 동작
ws.onmessage = function (data) {
let messageJson = new Object();
// 받은 데이터를 JSON 으로 변경
messageJson = JSON.parse(data.data)
if(messageJson.roomDetail != null || messageJson.roomDetail != ''){
let msg = messageJson.roomDetail;
if(messageJson.sendUserId == user_info.id){
if (msg != null && msg.trim() != '') {
let str = "<div clas='col-12' style='width:100%'><div class='col-10' style='float: right'>";
str += "<div style='text-align: right; background-color: #f8f9fc;'>";
str += "<b>" + msg + "</b>";
str += "</div></div></div>";
$("#msgArea").append(str);
}
}else{
if (msg != null && msg.trim() != '') {
let str = "<div class='col-12' style='width:100%'><div class='col-10' style='float: left'>";
str += "<div style='text-align: left'>";
str += "<b>" + msg + "</b>";
str += "</div></div></div>";
$("#msgArea").append(str);
}
}
$('#msgDiv').scrollTop($('#msgDiv')[0].scrollHeight)
}
}
}
/**
* widget 웹소켓 연결
* @param username
*/
function widgetWebsocket(username){
ws_widget = new WebSocket("ws://xxx.xxx.xxx.xxx:xxxx/ws/widget/"+username);
ws_widget.onopen = function (data) {
}
ws_widget.onmessage = function (data) {
let messageJson = new Object();
messageJson = JSON.parse(data.data)
console.log("messageJson : " + messageJson);
// 사내 메시지 및 top 부분 메시지 알림
if(messageJson.userId == user_info.id){
messengerReadCount();
}
// 메신저 채팅방 목록이 열려 있는 경우
if($("#roomDiv").css("display") == 'block'){
roomList();
}
}
}
// 메시지 전송 버튼 enter 이벤트
$("#msg").on("keyup", function (event){
if (event.key === 'Enter' || event.keyCode === 13) {
sendMsg();
}
})
$(document).ready(function () {
// 읽지 않은 메시지 수 확인
messengerReadCount();
// widget 웹소켓 연결
widgetWebsocket(user_info.id);
});
길어서 나중에 찾아볼때 애먹을 거 같다...
'개발 > Spring' 카테고리의 다른 글
[SpringBoot] WebSocket 채팅방 구현(5) - service (0) | 2024.08.18 |
---|---|
[SpringBoot] WebSocket 채팅방 구현(4) - controller (0) | 2024.08.18 |
[SpringBoot] WebSocket 채팅방 구현(2) - WebSocketConfig (0) | 2024.08.17 |
[SpringBoot] WebSocket 채팅방 구현(1) - 구현 화면 (0) | 2024.08.17 |
[SpringBoot] 메뉴 계층형 구조 만들기(CheckBox, 전체 선택/해제) (0) | 2024.07.31 |
댓글