Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lakernote
EasyAdmin
提交
bf289c00
E
EasyAdmin
项目概览
lakernote
/
EasyAdmin
9 个月 前同步成功
通知
14
Star
3
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
EasyAdmin
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
bf289c00
编写于
8月 31, 2021
作者:
lakernote
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
(新增)[整体](基于stomp技术的用户上下线通知)
上级
948a68a7
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
536 addition
and
14 deletion
+536
-14
src/main/java/com/laker/admin/config/WebSocketStompConfig.java
...ain/java/com/laker/admin/config/WebSocketStompConfig.java
+31
-7
src/main/java/com/laker/admin/framework/ext/stomp/EasyChannelInterceptor.java
...ker/admin/framework/ext/stomp/EasyChannelInterceptor.java
+132
-0
src/main/java/com/laker/admin/framework/ext/stomp/EasyChatMessage.java
.../com/laker/admin/framework/ext/stomp/EasyChatMessage.java
+28
-0
src/main/java/com/laker/admin/framework/ext/stomp/EasyHandShakeInterceptor.java
...r/admin/framework/ext/stomp/EasyHandShakeInterceptor.java
+31
-0
src/main/java/com/laker/admin/framework/ext/stomp/EasyPrincipal.java
...va/com/laker/admin/framework/ext/stomp/EasyPrincipal.java
+40
-0
src/main/java/com/laker/admin/framework/ext/stomp/EasyPrincipalHandshakeHandler.java
...in/framework/ext/stomp/EasyPrincipalHandshakeHandler.java
+47
-0
src/main/java/com/laker/admin/framework/ext/stomp/WebSocketEventListener.java
...ker/admin/framework/ext/stomp/WebSocketEventListener.java
+72
-0
src/main/java/com/laker/admin/module/chat/StompConstant.java
src/main/java/com/laker/admin/module/chat/StompConstant.java
+43
-0
src/main/java/com/laker/admin/module/chat/StompMessageService.java
...java/com/laker/admin/module/chat/StompMessageService.java
+24
-0
src/main/java/com/laker/admin/module/chat/WebSocketCharConroller.java
...a/com/laker/admin/module/chat/WebSocketCharConroller.java
+51
-0
web/admin/component/pear/css/module/notice.css
web/admin/component/pear/css/module/notice.css
+1
-1
web/admin/component/pear/module/notice.js
web/admin/component/pear/module/notice.js
+1
-1
web/admin/index.html
web/admin/index.html
+35
-5
未找到文件。
src/main/java/com/laker/admin/config/WebSocketStompConfig.java
浏览文件 @
bf289c00
package
com.laker.admin.config
;
import
com.laker.admin.framework.ext.stomp.EasyChannelInterceptor
;
import
com.laker.admin.framework.ext.stomp.EasyHandShakeInterceptor
;
import
com.laker.admin.framework.ext.stomp.EasyPrincipalHandshakeHandler
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.messaging.simp.config.ChannelRegistration
;
import
org.springframework.messaging.simp.config.MessageBrokerRegistry
;
import
org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer
;
import
org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
;
import
org.springframework.web.socket.config.annotation.StompEndpointRegistry
;
import
org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
;
@Configuration
@EnableWebSocketMessageBroker
public
class
WebSocketStompConfig
extends
AbstractWebSocketMessageBrokerConfigurer
{
public
class
WebSocketStompConfig
implements
WebSocketMessageBrokerConfigurer
{
@Override
public
void
registerStompEndpoints
(
StompEndpointRegistry
registry
)
{
//为/chatroom路径启用SockJS功能
registry
.
addEndpoint
(
"/chatroom"
).
setAllowedOrigins
(
"*"
).
withSockJS
();
registry
.
addEndpoint
(
"/chatroom"
)
.
setAllowedOrigins
(
"*"
)
// 握手处理,主要是连接的时候认证获取其他数据验证等
.
setHandshakeHandler
(
new
EasyPrincipalHandshakeHandler
())
// 拦截处理,和http拦截类似
.
addInterceptors
(
new
EasyHandShakeInterceptor
())
.
withSockJS
();
}
@Override
public
void
configureMessageBroker
(
MessageBrokerRegistry
registry
)
{
//表明在topic、
queue、users这三个域上可以向客户端发消息。 服务端-->客户端
registry
.
enableSimpleBroker
(
"/topic"
,
"/
queue"
,
"/users
"
);
//表明在topic、
user域上可以向客户端发消息。 服务端-->客户端 广播式使用/topic,点对点式使用/user
registry
.
enableSimpleBroker
(
"/topic"
,
"/
user
"
);
//客户端向服务端发起请求时,需要以/app为前缀。 客户端-->服务端
registry
.
setApplicationDestinationPrefixes
(
"/app"
);
//给指定用户发送一对一的消息前缀是/users/。
registry
.
setUserDestinationPrefix
(
"/user
s
/"
);
//给指定用户发送一对一的消息前缀是/users/
,默认/user/
。
registry
.
setUserDestinationPrefix
(
"/user/"
);
}
// /**
// * 设置输入消息通道的线程数,默认线程为1,可以自己自定义线程数,最大线程数,线程存活时间
// *
// * @param registration
// */
// @Override
// public void configureClientInboundChannel(ChannelRegistration registration) {
// // 注册消息拦截器
// registration.interceptors(new EasyChannelInterceptor());
// }
}
src/main/java/com/laker/admin/framework/ext/stomp/EasyChannelInterceptor.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.framework.ext.stomp
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.messaging.Message
;
import
org.springframework.messaging.MessageChannel
;
import
org.springframework.messaging.simp.SimpMessagingTemplate
;
import
org.springframework.messaging.simp.stomp.StompHeaderAccessor
;
import
org.springframework.messaging.support.ChannelInterceptor
;
import
org.springframework.stereotype.Component
;
/**
* websocket消息监听,用于监听websocket用户连接情况
*/
@Slf4j
@Component
public
class
EasyChannelInterceptor
implements
ChannelInterceptor
{
@Autowired
SimpMessagingTemplate
simpMessagingTemplate
;
/**
* 在消息实际发送到通道之前调用。这允许在必要时修改消息。如果此方法返回null,则实际的发送调用不会发生。
*
* @param message
* @param messageChannel
* @return
*/
@Override
public
Message
<?>
preSend
(
Message
<?>
message
,
MessageChannel
messageChannel
)
{
return
message
;
}
/**
* 在消息发送后立刻调用,boolean值参数表示该调用的返回值
*
* @param message
* @param messageChannel
* @param sent
*/
@Override
public
void
postSend
(
Message
<?>
message
,
MessageChannel
messageChannel
,
boolean
sent
)
{
StompHeaderAccessor
accessor
=
StompHeaderAccessor
.
wrap
(
message
);
EasyPrincipal
user
=
(
EasyPrincipal
)
accessor
.
getUser
();
// 这里只是单纯的打印,可以根据项目的实际情况做业务处理
log
.
info
(
"postSend 中获取httpSession key:"
+
user
.
getName
());
// 忽略心跳消息等非STOMP消息
if
(
accessor
.
getCommand
()
==
null
)
{
return
;
}
// 根据连接状态做处理,这里也只是打印了下,可以根据实际场景,对上线,下线,首次成功连接做处理
System
.
out
.
println
(
accessor
.
getCommand
());
switch
(
accessor
.
getCommand
())
{
// 首次连接
case
CONNECT:
log
.
info
(
"httpSession key:"
+
user
.
getName
()
+
" 首次连接"
);
messageChannel
.
send
(
message
);
break
;
// 连接中
case
CONNECTED:
break
;
// 下线
case
DISCONNECT:
log
.
info
(
"httpSession key:"
+
user
.
getName
()
+
" 下线"
);
break
;
default
:
break
;
}
}
/**
* 1. 在消息发送完成后调用,而不管消息发送是否产生异常,在次方法中,我们可以做一些资源释放清理的工作
* 2. 此方法的触发必须是preSend方法执行成功,且返回值不为null,发生了实际的消息推送,才会触发
*
* @param message
* @param messageChannel
* @param b
* @param e
*/
@Override
public
void
afterSendCompletion
(
Message
<?>
message
,
MessageChannel
messageChannel
,
boolean
b
,
Exception
e
)
{
log
.
info
(
"afterSendCompletion"
);
}
/**
* 1. 在消息被实际检索之前调用,如果返回false,则不会对检索任何消息,只适用于(PollableChannels),
* 2. 在websocket的场景中用不到
*
* @param messageChannel
* @return
*/
@Override
public
boolean
preReceive
(
MessageChannel
messageChannel
)
{
log
.
info
(
"preReceive"
);
return
true
;
}
/**
* 1. 在检索到消息之后,返回调用方之前调用,可以进行信息修改,如果返回null,就不会进行下一步操作
* 2. 适用于PollableChannels,轮询场景
*
* @param message
* @param messageChannel
* @return
*/
@Override
public
Message
<?>
postReceive
(
Message
<?>
message
,
MessageChannel
messageChannel
)
{
log
.
info
(
"postReceive"
);
return
message
;
}
/**
* 1. 在消息接收完成之后调用,不管发生什么异常,可以用于消息发送后的资源清理
* 2. 只有当preReceive 执行成功,并返回true才会调用此方法
* 2. 适用于PollableChannels,轮询场景
*
* @param message
* @param messageChannel
* @param e
*/
@Override
public
void
afterReceiveCompletion
(
Message
<?>
message
,
MessageChannel
messageChannel
,
Exception
e
)
{
log
.
info
(
"afterReceiveCompletion"
);
}
}
src/main/java/com/laker/admin/framework/ext/stomp/EasyChatMessage.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.framework.ext.stomp
;
import
lombok.AllArgsConstructor
;
import
lombok.Builder
;
import
lombok.Data
;
import
java.util.Date
;
@Data
@Builder
public
class
EasyChatMessage
{
/**
* 消息内容
*/
private
String
content
;
/**
* 消息发送人
*/
private
String
from
;
/**
* 消息接收人
*/
private
String
to
;
/**
* 消息时间
*/
private
Date
createTime
;
}
\ No newline at end of file
src/main/java/com/laker/admin/framework/ext/stomp/EasyHandShakeInterceptor.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.framework.ext.stomp
;
import
cn.dev33.satoken.stp.StpUtil
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.http.server.ServerHttpRequest
;
import
org.springframework.http.server.ServerHttpResponse
;
import
org.springframework.web.socket.WebSocketHandler
;
import
org.springframework.web.socket.server.HandshakeInterceptor
;
import
java.util.Map
;
/**
* 拦截器
*/
@Slf4j
public
class
EasyHandShakeInterceptor
implements
HandshakeInterceptor
{
@Override
public
boolean
beforeHandshake
(
ServerHttpRequest
request
,
ServerHttpResponse
response
,
WebSocketHandler
wsHandler
,
Map
<
String
,
Object
>
attributes
)
throws
Exception
{
Object
loginId
=
StpUtil
.
getLoginId
();
log
.
info
(
"握手前..."
+
loginId
);
return
true
;
}
@Override
public
void
afterHandshake
(
ServerHttpRequest
request
,
ServerHttpResponse
response
,
WebSocketHandler
wsHandler
,
Exception
ex
)
{
log
.
info
(
"握手成功后..."
+
StpUtil
.
getLoginId
());
}
}
\ No newline at end of file
src/main/java/com/laker/admin/framework/ext/stomp/EasyPrincipal.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.framework.ext.stomp
;
import
lombok.Builder
;
import
lombok.Data
;
import
java.security.Principal
;
/**
* 定义一个自己的权限验证类
*/
@Data
@Builder
public
class
EasyPrincipal
implements
Principal
{
/**
* 用户id
*/
private
String
userId
;
/**
* 用户昵称
*/
private
String
nickName
;
/**
* 地址
*/
private
String
address
;
/**
* 头像
*/
private
String
avatar
;
/**
* 用户状态
*/
private
int
status
;
@Override
public
String
getName
()
{
return
userId
;
}
}
src/main/java/com/laker/admin/framework/ext/stomp/EasyPrincipalHandshakeHandler.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.framework.ext.stomp
;
import
cn.dev33.satoken.stp.StpUtil
;
import
cn.hutool.core.util.StrUtil
;
import
com.laker.admin.framework.EasyAdminSecurityUtils
;
import
com.laker.admin.utils.IP2CityUtil
;
import
com.laker.admin.utils.http.HttpServletRequestUtil
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.http.server.ServerHttpRequest
;
import
org.springframework.http.server.ServletServerHttpRequest
;
import
org.springframework.web.socket.WebSocketHandler
;
import
org.springframework.web.socket.server.support.DefaultHandshakeHandler
;
import
java.security.Principal
;
import
java.util.Map
;
/**
* 设置认证用户信息的握手拦截器
* 这一步很关键,后面该链接绑定的会话都用这个,这里相当于是从http环境取出用户,后面拦截器、接口等处都不是http环境了,获取不到request等信息
*/
@Slf4j
public
class
EasyPrincipalHandshakeHandler
extends
DefaultHandshakeHandler
{
@Override
protected
Principal
determineUser
(
ServerHttpRequest
request
,
WebSocketHandler
wsHandler
,
Map
<
String
,
Object
>
attributes
)
{
log
.
info
(
"确定用户"
);
//握手成功后调用,可以在这里保存用户id
String
userName
=
((
ServletServerHttpRequest
)
request
).
getServletRequest
().
getParameter
(
"userName"
);
String
remoteIP
=
HttpServletRequestUtil
.
getRemoteIP
();
String
address
;
if
(!
StrUtil
.
equals
(
remoteIP
,
"127.0.0.1"
))
{
String
cityInfo
=
IP2CityUtil
.
getCityInfo
(
remoteIP
);
String
[]
split
=
cityInfo
.
split
(
"\\|"
);
address
=
StrUtil
.
format
(
"{}.{}.{}"
,
split
[
2
],
split
[
3
],
split
[
4
]);
}
else
{
address
=
"开发者"
;
}
EasyPrincipal
easyPrincipal
=
EasyPrincipal
.
builder
()
.
userId
(
StpUtil
.
getLoginIdAsString
())
.
nickName
(
EasyAdminSecurityUtils
.
getCurrentUserInfo
().
getNickName
())
.
address
(
address
)
.
build
();
return
easyPrincipal
;
}
}
src/main/java/com/laker/admin/framework/ext/stomp/WebSocketEventListener.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.framework.ext.stomp
;
import
cn.hutool.core.thread.ThreadUtil
;
import
com.laker.admin.module.chat.StompConstant
;
import
com.laker.admin.module.chat.StompMessageService
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.event.EventListener
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.socket.messaging.SessionConnectedEvent
;
import
org.springframework.web.socket.messaging.SessionDisconnectEvent
;
import
java.util.Date
;
import
java.util.concurrent.TimeUnit
;
/**
* websocket事件监听 websocket消息监听,用于监听websocket用户连接情况
*/
@Slf4j
@Component
public
class
WebSocketEventListener
{
@Autowired
StompMessageService
stompMessageService
;
/**
* 建立连接监听
*
* @param sessionConnectedEvent
*/
@EventListener
public
void
handleConnectListener
(
SessionConnectedEvent
sessionConnectedEvent
)
{
EasyPrincipal
user
=
(
EasyPrincipal
)
sessionConnectedEvent
.
getUser
();
log
.
info
(
"建立连接 {} -> {}"
,
user
,
sessionConnectedEvent
);
// 加入在线用户列表
ThreadUtil
.
execute
(()
->
{
// 缓几秒钟再广播消息防止,接收不到
try
{
TimeUnit
.
SECONDS
.
sleep
(
2
);
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
// 广播上线消息
stompMessageService
.
sendMessage
(
StompConstant
.
SUB_STATUS
,
EasyChatMessage
.
builder
()
.
content
(
"欢迎【"
+
user
.
getAddress
()
+
"】老弟体验"
)
.
createTime
(
new
Date
()).
build
());
log
.
info
(
"广播消息喽"
);
});
}
/**
* 断开连接监听
*
* @param sessionDisconnectEvent
*/
@EventListener
public
void
handleDisconnectListener
(
SessionDisconnectEvent
sessionDisconnectEvent
)
{
EasyPrincipal
user
=
(
EasyPrincipal
)
sessionDisconnectEvent
.
getUser
();
log
.
debug
(
"断开连接 -> {}"
,
sessionDisconnectEvent
);
// 剔除在线用户列表
// 广播下线消息
stompMessageService
.
sendMessage
(
StompConstant
.
SUB_STATUS
,
EasyChatMessage
.
builder
()
.
content
(
"期待【"
+
user
.
getAddress
()
+
"】老弟再来"
)
.
createTime
(
new
Date
()).
build
());
log
.
info
(
"广播消息喽"
);
}
}
\ No newline at end of file
src/main/java/com/laker/admin/module/chat/StompConstant.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.module.chat
;
public
interface
StompConstant
{
/**
* 广播式
*/
String
STOMP_TOPIC
=
"/topic"
;
/**
* 一对一式
*/
String
STOMP_USER
=
"/user"
;
/**
* 单用户消息订阅地址
*/
String
SUB_USER
=
"/chat"
;
/**
* 私聊聊天
* 目的地是“/app/chat”。(“/app”前缀是隐含 的,因为我们将其配置为应用的目的地前缀)
*/
String
PUB_USER
=
"/chat"
;
/**
* 聊天室聊天
* 目的地是“/app/chatRoom”。(“/app”前缀是隐含 的,因为我们将其配置为应用的目的地前缀)
*/
String
PUB_CHAT_ROOM
=
"/chatRoom"
;
/**
* 聊天室消息订阅地址
*/
String
SUB_CHAT_ROOM
=
"/topic/chatRoom"
;
/**
* 异常消息订阅地址
*/
String
SUB_ERROR
=
"/error"
;
/**
* 用户上下线状态消息订阅地址
*/
String
SUB_STATUS
=
"/topic/status"
;
/**
* 聊天室消息撤消
*/
String
PUB_CHAT_ROOM_REVOKE
=
"/chatRoom/revoke"
;
}
src/main/java/com/laker/admin/module/chat/StompMessageService.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.module.chat
;
import
com.laker.admin.framework.ext.stomp.EasyChatMessage
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.messaging.simp.SimpMessagingTemplate
;
import
org.springframework.stereotype.Service
;
@Service
public
class
StompMessageService
{
@Autowired
SimpMessagingTemplate
simpMessagingTemplate
;
public
void
sendMessage
(
String
destination
,
EasyChatMessage
message
)
{
simpMessagingTemplate
.
convertAndSend
(
destination
,
message
);
}
public
void
sendMessageToUser
(
EasyChatMessage
message
,
String
...
receiver
)
{
for
(
int
i
=
0
,
len
=
receiver
.
length
;
i
<
len
;
i
++)
{
// 将消息发送到指定用户 参数说明:1.消息接收者 2.消息订阅地址 3.消息内容
// 发给单点某个人,私聊,这里的前缀必须是跟 WebSocketStompConfig registry.setUserDestinationPrefix("/user/")中呼应
simpMessagingTemplate
.
convertAndSendToUser
(
receiver
[
i
],
StompConstant
.
SUB_USER
,
message
);
}
}
}
src/main/java/com/laker/admin/module/chat/WebSocketCharConroller.java
0 → 100644
浏览文件 @
bf289c00
package
com.laker.admin.module.chat
;
import
com.laker.admin.framework.ext.stomp.EasyChatMessage
;
import
com.laker.admin.framework.ext.stomp.EasyPrincipal
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.messaging.handler.annotation.MessageExceptionHandler
;
import
org.springframework.messaging.handler.annotation.MessageMapping
;
import
org.springframework.stereotype.Controller
;
@Controller
@Slf4j
public
class
WebSocketCharConroller
{
@Autowired
StompMessageService
stompMessageService
;
/**
* 聊天室聊天
*/
@MessageMapping
(
StompConstant
.
PUB_CHAT_ROOM
)
public
void
chatRoom
(
EasyChatMessage
chatMessage
,
EasyPrincipal
user
)
throws
Exception
{
stompMessageService
.
sendMessage
(
StompConstant
.
SUB_CHAT_ROOM
,
chatMessage
);
}
/**
* 私聊聊天
* // 本地订阅
* stompClient.subscribe('/user/' + uid + '/chat', function (data) {
* handleMessage(getData(data.body));
* });
* @param user 发送消息的用户对象
* @throws Exception
*/
@MessageMapping
(
StompConstant
.
PUB_USER
)
public
void
sendToUser
(
EasyChatMessage
chatMessage
,
EasyPrincipal
user
)
throws
Exception
{
stompMessageService
.
sendMessageToUser
(
chatMessage
,
chatMessage
.
getTo
());
}
/**
* 消息异常处理
*
* @param e 异常对象
* @param user 发送消息的用户对象
*/
@MessageExceptionHandler
(
Exception
.
class
)
public
void
handleExceptions
(
Exception
e
,
EasyPrincipal
user
)
{
log
.
error
(
"error:"
,
e
);
}
}
web/admin/component/pear/css/module/notice.css
浏览文件 @
bf289c00
...
...
@@ -77,7 +77,7 @@ button.toast-close-button {
left
:
12px
;
}
.toast-top-right
{
top
:
1
2
px
;
top
:
1
00
px
;
right
:
12px
;
}
.toast-bottom-right
{
...
...
web/admin/component/pear/module/notice.js
浏览文件 @
bf289c00
...
...
@@ -13,7 +13,7 @@
};
var
cssStyle
=
$
(
'
<style type="text/css"> .toast-title{font-weight:bold}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-0.3em;top:-0.3em;float:right;font-size:20px;font-weight:bold;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:alpha(opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-0.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:1
2
px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:alpha(opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")!important}#toast-container>.toast-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")!important}#toast-container>.toast-success{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")!important}#toast-container>.toast-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")!important}#toast-container.toast-top-center>div,#toast-container.toast-bottom-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:alpha(opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-0.2em;top:-0.2em}#toast-container .rtl .toast-close-button{left:-0.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-0.2em;top:-0.2em}#toast-container .rtl .toast-close-button{left:-0.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}}</style>
'
'
<style type="text/css"> .toast-title{font-weight:bold}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-0.3em;top:-0.3em;float:right;font-size:20px;font-weight:bold;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:alpha(opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-0.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:1
10
px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:alpha(opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:alpha(opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")!important}#toast-container>.toast-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")!important}#toast-container>.toast-success{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")!important}#toast-container>.toast-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")!important}#toast-container.toast-top-center>div,#toast-container.toast-bottom-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:alpha(opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-0.2em;top:-0.2em}#toast-container .rtl .toast-close-button{left:-0.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-0.2em;top:-0.2em}#toast-container .rtl .toast-close-button{left:-0.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}}</style>
'
);
$
(
"
body
"
).
append
(
cssStyle
);
...
...
web/admin/index.html
浏览文件 @
bf289c00
...
...
@@ -27,8 +27,8 @@
<ul
class=
"layui-nav layui-layout-right"
>
<li
class=
"layui-nav-item layui-hide-xs"
><a
href=
"#"
class=
"fullScreen layui-icon layui-icon-screen-full"
></a></li>
<!-- <li class="layui-nav-item layui-hide-xs"><a href="http://www.pearadmin.com"-->
<!-- class="layui-icon layui-icon-website"></a></li>-->
<!-- <li class="layui-nav-item layui-hide-xs"><a href="http://www.pearadmin.com"-->
<!-- class="layui-icon layui-icon-website"></a></li>-->
<li
class=
"layui-nav-item layui-hide-xs message"
></li>
<li
class=
"layui-nav-item user"
>
<!-- 头 像 -->
...
...
@@ -37,7 +37,8 @@
</a>
<!-- 功 能 菜 单 -->
<dl
class=
"layui-nav-child"
>
<dd><a
user-menu-url=
"view/system/user/center.html"
user-menu-id=
"5555"
user-menu-title=
"基本资料"
>
基本资料
</a>
<dd><a
user-menu-url=
"view/system/user/center.html"
user-menu-id=
"5555"
user-menu-title=
"基本资料"
>
基本资料
</a>
</dd>
<dd><a
user-menu-url=
"view/system/user/password.html"
user-menu-id=
"6666"
user-menu-title=
"修改密码"
>
修改密码
</a>
</dd>
...
...
@@ -81,12 +82,14 @@
<!-- 依 赖 脚 本 -->
<script
src=
"component/layui/layui.js"
></script>
<script
src=
"component/pear/pear.js"
></script>
<script
src=
"https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"
></script>
<script
src=
"https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"
></script>
<!-- 框 架 初 始 化 -->
<script>
layui
.
use
([
'
admin
'
,
'
jquery
'
,
'
convert
'
,
'
popup
'
,
'
easyAdmin
'
],
function
()
{
layui
.
use
([
'
admin
'
,
'
jquery
'
,
'
notice
'
,
'
popup
'
,
'
easyAdmin
'
],
function
()
{
var
admin
=
layui
.
admin
;
var
$
=
layui
.
jquery
;
var
convert
=
layui
.
convert
;
var
notice
=
layui
.
notice
;
var
popup
=
layui
.
popup
;
var
easyAdmin
=
layui
.
easyAdmin
;
...
...
@@ -146,6 +149,33 @@
// 实现消息回调 [消息列表点击事件]
// admin.message(function(id, title, context, form) {});
// 初始化配置,同一样式只需要配置一次,非必须初始化,有默认配置
notice
.
options
=
{
closeButton
:
true
,
//显示关闭按钮
debug
:
false
,
//启用debug
positionClass
:
"
toast-top-right
"
,
//弹出的位置,
showDuration
:
"
1000
"
,
//显示的时间
hideDuration
:
"
1000
"
,
//消失的时间
timeOut
:
"
10000
"
,
//停留的时间
extendedTimeOut
:
"
1000
"
,
//控制时间
showEasing
:
"
swing
"
,
//显示时的动画缓冲方式
hideEasing
:
"
linear
"
,
//消失时的动画缓冲方式
iconClass
:
'
toast-info
'
,
// 自定义图标,有内置,如不需要则传空 支持layui内置图标/自定义iconfont类名
onclick
:
null
,
// 点击关闭回调
};
var
url
=
easyAdmin
.
GetAdminServerUrl
()
+
'
/chatroom?
'
+
easyAdmin
.
GetTokenQueryString
();
var
sock
=
new
SockJS
(
url
);
//创建SockJS连接。
var
stomp
=
Stomp
.
over
(
sock
);
//创建STOMP客户端实例。实际上封装了SockJS,这样就能在WebSocket连接上发送STOMP消息。
stomp
.
connect
({},
function
(
frame
)
{
stomp
.
subscribe
(
'
/topic/status
'
,
function
(
message
)
{
notice
.
info
(
JSON
.
parse
(
message
.
body
).
content
);
});
},
function
(
frame
)
{
notice
.
error
(
frame
);
});
})
</script>
</body>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录