右上角铃铛消息
This commit is contained in:
@ -3,6 +3,8 @@ package com.ruoyi.web.controller.system;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.mysql.cj.protocol.x.Notice;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@ -41,7 +43,12 @@ public class SysNoticeController extends BaseController
|
||||
{
|
||||
return prefix + "/notice";
|
||||
}
|
||||
|
||||
@RequiresPermissions("system:notice:view")
|
||||
@GetMapping("/notice2")
|
||||
public String notice2()
|
||||
{
|
||||
return prefix + "/notice2";
|
||||
}
|
||||
/**
|
||||
* 查询公告列表
|
||||
*/
|
||||
@ -65,6 +72,9 @@ public class SysNoticeController extends BaseController
|
||||
public String detail(@PathVariable Long noticeId, ModelMap mmap)
|
||||
{
|
||||
SysNotice notice = noticeService.selectNoticeById(noticeId);
|
||||
// 阅读状态
|
||||
notice.setReadStatus("1");
|
||||
noticeService.updateNotice(notice);
|
||||
mmap.put("notice", notice);
|
||||
return prefix + "/detail";
|
||||
}
|
||||
@ -127,4 +137,15 @@ public class SysNoticeController extends BaseController
|
||||
{
|
||||
return toAjax(noticeService.deleteNoticeByIds(ids));
|
||||
}
|
||||
|
||||
@GetMapping("/unreadCount")
|
||||
@ResponseBody
|
||||
public int getUnreadNoticeCount() {
|
||||
SysNotice notice = new SysNotice();
|
||||
notice.setUserId(getSysUser().getUserId());
|
||||
notice.setReadStatus("0");
|
||||
notice.setNoticeType("1");
|
||||
List<SysNotice> list = noticeService.selectNoticeList(notice);
|
||||
return list.size();
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,49 @@
|
||||
<link th:href="@{/css/style.min.css}" rel="stylesheet"/>
|
||||
<link th:href="@{/css/skins.css}" rel="stylesheet"/>
|
||||
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.7.4}" rel="stylesheet"/>
|
||||
<style>
|
||||
.notice-tip {
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
padding: 10px;
|
||||
z-index: 1000;
|
||||
width: 180px;
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.notice-tip:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 16%;
|
||||
margin-left: -8px;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-bottom: 8px solid #ddd;
|
||||
}
|
||||
|
||||
.notice-tip:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 16%;
|
||||
margin-left: -7px;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #fff;
|
||||
}
|
||||
|
||||
.label-danger {
|
||||
background-color: #d9534f;
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="fixed-sidebar full-height-layout gray-bg" th:classappend="${isMobile} ? 'canvas-menu'" style="overflow: hidden">
|
||||
<div id="wrapper">
|
||||
@ -193,6 +236,15 @@
|
||||
</a>
|
||||
</div>
|
||||
<ul class="nav navbar-top-links navbar-right welcome-message">
|
||||
<li id="notice">
|
||||
<a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" href="javascript:void(0);">
|
||||
<i class="fa fa-bell-o"></i> 消息
|
||||
<span class="label label-danger" id="noticeCount" style="position: absolute; top: 10px; right: 5px; display: none;">0</span>
|
||||
</a>
|
||||
<div id="noticeTip" class="notice-tip" style="display: none; position: absolute; background: #fff; border: 1px solid #ccc; padding: 10px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); z-index: 1000; width: 200px; text-align: center; margin-top: 5px;">
|
||||
您有 <span id="unreadCount">0</span> 条未读消息
|
||||
</div>
|
||||
</li>
|
||||
<li><a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="锁定屏幕" href="javascript:;" id="lockScreen"><i class="fa fa-lock"></i> 锁屏</a></li>
|
||||
<li><a data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="全屏显示" href="javascript:;" id="fullScreen"><i class="fa fa-arrows-alt"></i> 全屏</a></li>
|
||||
<li class="dropdown user-menu">
|
||||
@ -382,7 +434,88 @@ $(function() {
|
||||
});
|
||||
}
|
||||
$("[data-toggle='tooltip']").tooltip();
|
||||
|
||||
initNoticeFunction();
|
||||
});
|
||||
|
||||
|
||||
// 初始化消息提醒功能
|
||||
function initNoticeFunction() {
|
||||
var hideTimeout; // 用于存储延迟隐藏的定时器ID
|
||||
|
||||
// 绑定鼠标悬停事件
|
||||
$('#notice').hover(
|
||||
function() {
|
||||
// 鼠标移入时清除可能存在的隐藏定时器
|
||||
clearTimeout(hideTimeout);
|
||||
// 显示未读消息数量和提示
|
||||
loadUnreadNoticeCount();
|
||||
$('#noticeTip').show();
|
||||
},
|
||||
function() {
|
||||
// 鼠标移出时,1秒后隐藏提示
|
||||
hideTimeout = setTimeout(function() {
|
||||
$('#noticeTip').hide();
|
||||
}, 1000); // 延迟1秒隐藏
|
||||
}
|
||||
);
|
||||
|
||||
// 鼠标悬停在提示框上时也清除隐藏定时器
|
||||
$('#noticeTip').hover(
|
||||
function() {
|
||||
clearTimeout(hideTimeout);
|
||||
},
|
||||
function() {
|
||||
hideTimeout = setTimeout(function() {
|
||||
$('#noticeTip').hide();
|
||||
}, 1000); // 延迟1秒隐藏
|
||||
}
|
||||
);
|
||||
|
||||
// 绑定点击事件,跳转到消息列表页面
|
||||
$('#notice').on('click', function(e) {
|
||||
// 阻止默认行为
|
||||
e.preventDefault();
|
||||
// 打开消息列表页面
|
||||
noticeList();
|
||||
});
|
||||
|
||||
// 初始化加载一次未读消息数量
|
||||
loadUnreadNoticeCount();
|
||||
|
||||
// 设置定时刷新未读消息数量
|
||||
setInterval(loadUnreadNoticeCount, 60000); // 每分钟刷新一次
|
||||
}
|
||||
|
||||
// 加载未读消息数量
|
||||
function loadUnreadNoticeCount() {
|
||||
$.get(ctx + "/system/notice/unreadCount", function(data) {
|
||||
if (data > 0) {
|
||||
$("#noticeCount").text(data).show();
|
||||
$("#unreadCount").text(data);
|
||||
} else {
|
||||
$("#noticeCount").hide();
|
||||
$("#unreadCount").text(0);
|
||||
}
|
||||
}).fail(function() {
|
||||
// 请求失败时隐藏提示
|
||||
$("#noticeCount").hide();
|
||||
$("#unreadCount").text(0);
|
||||
});
|
||||
}
|
||||
|
||||
// 点击消息提醒区域时跳转到消息列表页面
|
||||
function noticeList(){
|
||||
var url = ctx + "/system/notice/notice2";
|
||||
// $.modal.open("消息", url, 1200, 800);
|
||||
layer.open({
|
||||
type : 2,
|
||||
shadeClose : true,
|
||||
title : "消息",
|
||||
area : ["1200px", "800px"],
|
||||
content : [ctx + "/system/notice/notice2", 'no']
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -9,35 +9,23 @@
|
||||
<form class="form-horizontal m" id="form-notice-edit" th:object="${notice}">
|
||||
<input id="noticeId" name="noticeId" th:field="*{noticeId}" type="hidden">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label is-required">公告标题:</label>
|
||||
<label class="col-sm-2 control-label ">标题:</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="noticeTitle" name="noticeTitle" th:field="*{noticeTitle}" class="form-control" type="text" required readonly="true">
|
||||
<div class="form-control-static" th:text="*{noticeTitle}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">公告类型:</label>
|
||||
<label class="col-sm-2 control-label">类型:</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="noticeType" class="form-control m-b" th:with="type=${@dict.getType('sys_notice_type')}" readonly="true">
|
||||
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{noticeType}"></option>
|
||||
</select>
|
||||
<div class="form-control-static" th:text="${@dict.getLabel('sys_notice_type', notice.noticeType)}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">公告内容:</label>
|
||||
<label class="col-sm-2 control-label">内容:</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="noticeContent" name="noticeContent" th:field="*{noticeContent}" type="hidden" readonly="true">
|
||||
<div id="editor" ></div>
|
||||
<div class="form-control-static" th:text="*{noticeContent}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">公告状态:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="radio-box" th:each="dict : ${@dict.getType('sys_notice_status')}">
|
||||
<input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:field="*{status}" readonly="true">
|
||||
<label th:for="${dict.dictCode}" th:text="${dict.dictLabel}"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
@ -46,58 +34,14 @@
|
||||
var prefix = ctx + "system/notice";
|
||||
|
||||
$(function() {
|
||||
$('.summernote').summernote({
|
||||
placeholder: '请输入公告内容',
|
||||
height : 192,
|
||||
lang : 'zh-CN',
|
||||
followingToolbar: false,
|
||||
dialogsInBody: true,
|
||||
callbacks: {
|
||||
onImageUpload: function (files) {
|
||||
sendFile(files[0], this);
|
||||
}
|
||||
}
|
||||
});
|
||||
var content = $("#noticeContent").val();
|
||||
$('#editor').summernote('code', content);
|
||||
|
||||
});
|
||||
|
||||
// 上传文件
|
||||
function sendFile(file, obj) {
|
||||
var data = new FormData();
|
||||
data.append("file", file);
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: ctx + "common/upload",
|
||||
data: data,
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
dataType: 'json',
|
||||
success: function(result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$(obj).summernote('editor.insertImage', result.url, result.fileName);
|
||||
} else {
|
||||
$.modal.alertError(result.msg);
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
$.modal.alertWarning("图片上传失败。");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$("#form-notice-edit").validate({
|
||||
focusCleanup: true
|
||||
});
|
||||
|
||||
function submitHandler() {
|
||||
if ($.validate.form()) {
|
||||
var sHTML = $('.summernote').summernote('code');
|
||||
$("#noticeContent").val(sHTML);
|
||||
$.operate.save(prefix + "/edit", $('#form-notice-edit').serialize());
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
|
||||
<head>
|
||||
<th:block th:include="include :: header('通知公告列表')" />
|
||||
</head>
|
||||
<body class="gray-bg">
|
||||
<div class="container-div">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 search-collapse">
|
||||
<form id="notice-form">
|
||||
<div class="select-list">
|
||||
<input type="hidden" class="form-control" name="status" value="0" >
|
||||
<ul>
|
||||
<li>
|
||||
类型:<select name="noticeType" th:with="type=${@dict.getType('sys_notice_type')}">
|
||||
<option value="">所有</option>
|
||||
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
状态:<select name="readStatus" th:with="type=${@dict.getType('sys_read_status')}">
|
||||
<option value="">所有</option>
|
||||
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i> 搜索</a>
|
||||
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="bootstrap-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<script th:inline="javascript">
|
||||
var editFlag = [[${@permission.hasPermi('system:notice:edit')}]];
|
||||
var removeFlag = [[${@permission.hasPermi('system:notice:remove')}]];
|
||||
var types = [[${@dict.getType('sys_notice_type')}]];
|
||||
var datas = [[${@dict.getType('sys_read_status')}]];
|
||||
var prefix = ctx + "system/notice";
|
||||
|
||||
$(function() {
|
||||
var options = {
|
||||
url: prefix + "/list",
|
||||
updateUrl: prefix + "/edit/{id}",
|
||||
detailUrl: prefix + "/detail/{id}",
|
||||
modalName: "消息",
|
||||
columns: [{
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field : 'noticeId',
|
||||
title : '序号'
|
||||
},
|
||||
{
|
||||
field : 'noticeTitle',
|
||||
title : '消息标题',
|
||||
width: 200,
|
||||
formatter: function(value, row, index) {
|
||||
return '<a href="javascript:void(0)" onclick="$.operate.detail(\'' + row.noticeId + '\', 900, 700)">' + value + '</a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field : 'noticeContent',
|
||||
title : '消息内容',
|
||||
width : 600
|
||||
},
|
||||
{
|
||||
field: 'readStatus',
|
||||
title: '状态',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.selectDictLabel(datas, value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '消息时间',
|
||||
width: 150,
|
||||
sortable: true
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
});
|
||||
|
||||
function detail(noticeId){
|
||||
var url = prefix + "/detail/"+noticeId;
|
||||
$.modal.openOptions("通知公告",url);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -31,6 +31,10 @@ public class SysNotice extends BaseEntity
|
||||
/** 公告状态(0正常 1关闭) */
|
||||
private String status;
|
||||
|
||||
private String readStatus;
|
||||
|
||||
private Long userId;
|
||||
|
||||
public Long getNoticeId()
|
||||
{
|
||||
return noticeId;
|
||||
@ -84,6 +88,22 @@ public class SysNotice extends BaseEntity
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getReadStatus() {
|
||||
return readStatus;
|
||||
}
|
||||
|
||||
public void setReadStatus(String readStatus) {
|
||||
this.readStatus = readStatus;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
|
||||
@ -10,6 +10,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="noticeType" column="notice_type" />
|
||||
<result property="noticeContent" column="notice_content" />
|
||||
<result property="status" column="status" />
|
||||
<result property="readStatus" column="read_status" />
|
||||
<result property="userId" column="user_id" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
@ -18,7 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectNoticeVo">
|
||||
select notice_id, notice_title, notice_type, notice_content, status, create_by, create_time, update_by, update_time, remark
|
||||
select notice_id, notice_title, notice_type, notice_content, status, read_status,user_id, create_by, create_time, update_by, update_time, remark
|
||||
from sys_notice
|
||||
</sql>
|
||||
|
||||
@ -36,6 +38,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="noticeType != null and noticeType != ''">
|
||||
AND notice_type = #{noticeType}
|
||||
</if>
|
||||
<if test="readStatus != null and readStatus != ''">
|
||||
AND read_status = #{readStatus}
|
||||
</if>
|
||||
<if test="userId != null">
|
||||
AND user_id = #{userId}
|
||||
</if>
|
||||
<if test="createBy != null and createBy != ''">
|
||||
AND create_by like concat('%', #{createBy}, '%')
|
||||
</if>
|
||||
@ -74,6 +82,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="noticeType != null and noticeType != '' ">notice_type, </if>
|
||||
<if test="noticeContent != null and noticeContent != '' ">notice_content, </if>
|
||||
<if test="status != null and status != '' ">status, </if>
|
||||
<if test="userId != null">user_id, </if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time,
|
||||
@ -83,6 +92,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
|
||||
<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if>
|
||||
<if test="status != null and status != ''">#{status}, </if>
|
||||
<if test="userId != null ">#{userId}, </if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate(),
|
||||
@ -97,6 +107,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if>
|
||||
<if test="noticeContent != null">notice_content = #{noticeContent}, </if>
|
||||
<if test="status != null and status != ''">status = #{status}, </if>
|
||||
<if test="readStatus != null and readStatus != ''">read_status = #{readStatus}, </if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
|
||||
Reference in New Issue
Block a user