From 7a4f61cf5e8d42d24907ccc3d7345d25012d3a55 Mon Sep 17 00:00:00 2001
From: RuoYi <yzz_ivy@163.com>
Date: Tue, 14 Apr 2026 16:43:50 +0800
Subject: [PATCH] 通知公告新增阅读用户&详细
---
ruoyi-ui/src/views/system/notice/ReadUsers.vue | 109 +++++++++
ruoyi-ui/src/layout/components/HeaderNotice/DetailView.vue | 362 ++++++++++++++++++++++++++++++++
ruoyi-ui/src/layout/components/HeaderNotice/index.vue | 60 ----
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReadService.java | 10
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java | 14 +
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java | 2
ruoyi-ui/src/api/system/notice.js | 9
ruoyi-ui/src/views/system/notice/index.vue | 32 ++
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReadMapper.java | 12 +
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java | 12 +
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml | 22 ++
11 files changed, 580 insertions(+), 64 deletions(-)
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java
index c0f6635..3ae141a 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java
@@ -54,7 +54,6 @@
/**
* 根据通知公告编号获取详细信息
*/
- @RequiresPermissions("system:notice:query")
@GetMapping(value = "/{noticeId}")
public AjaxResult getInfo(@PathVariable Long noticeId)
{
@@ -126,6 +125,19 @@
}
/**
+ * 已读用户列表数据
+ */
+ @RequiresPermissions("system:notice:list")
+ @GetMapping("/readUsers/list")
+ @ResponseBody
+ public TableDataInfo readUsersList(Long noticeId, String searchValue)
+ {
+ startPage();
+ List<?> list = noticeReadService.selectReadUsersByNoticeId(noticeId, searchValue);
+ return getDataTable(list);
+ }
+
+ /**
* 删除通知公告
*/
@RequiresPermissions("system:notice:remove")
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReadMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReadMapper.java
index 173e6cf..529b8e1 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReadMapper.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReadMapper.java
@@ -1,9 +1,10 @@
package com.ruoyi.system.mapper;
import java.util.List;
+import java.util.Map;
import org.apache.ibatis.annotations.Param;
-import com.ruoyi.system.domain.SysNoticeRead;
import com.ruoyi.system.domain.SysNotice;
+import com.ruoyi.system.domain.SysNoticeRead;
/**
* 公告已读记录 数据层
@@ -56,6 +57,15 @@
public List<SysNotice> selectNoticeListWithReadStatus(@Param("userId") Long userId, @Param("limit") int limit);
/**
+ * 查询已阅读某公告的用户列表
+ *
+ * @param noticeId 公告ID
+ * @param searchValue 搜索值
+ * @return 已读用户列表
+ */
+ public List<Map<String, Object>> selectReadUsersByNoticeId(@Param("noticeId") Long noticeId, @Param("searchValue") String searchValue);
+
+ /**
* 公告删除时清理对应已读记录
*
* @param noticeIds 公告ID数组
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReadService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReadService.java
index 8b69027..56e958a 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReadService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReadService.java
@@ -1,6 +1,7 @@
package com.ruoyi.system.service;
import java.util.List;
+import java.util.Map;
import com.ruoyi.system.domain.SysNotice;
/**
@@ -44,6 +45,15 @@
public void markReadBatch(Long userId, Long[] noticeIds);
/**
+ * 查询已阅读某公告的用户列表
+ *
+ * @param noticeId 公告ID
+ * @param searchValue 搜索值
+ * @return 已读用户列表
+ */
+ public List<Map<String, Object>> selectReadUsersByNoticeId(Long noticeId, String searchValue);
+
+ /**
* 删除公告时清理对应已读记录
*
* @param noticeIds 公告ID数组
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
index fb1e420..7252dc2 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
@@ -49,7 +49,7 @@
* @return 结果
*/
public int deleteNoticeById(Long noticeId);
-
+
/**
* 批量删除公告信息
*
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java
index 7c6c00a..f8e63a6 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java
@@ -1,10 +1,11 @@
package com.ruoyi.system.service.impl;
import java.util.List;
+import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import com.ruoyi.system.domain.SysNoticeRead;
import com.ruoyi.system.domain.SysNotice;
+import com.ruoyi.system.domain.SysNoticeRead;
import com.ruoyi.system.mapper.SysNoticeReadMapper;
import com.ruoyi.system.service.ISysNoticeReadService;
@@ -63,6 +64,15 @@
}
/**
+ * 查询已阅读某公告的用户列表
+ */
+ @Override
+ public List<Map<String, Object>> selectReadUsersByNoticeId(Long noticeId, String searchValue)
+ {
+ return noticeReadMapper.selectReadUsersByNoticeId(noticeId, searchValue);
+ }
+
+ /**
* 删除公告时清理对应已读记录
*/
@Override
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml
index 3af8092..86e5136 100644
--- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml
@@ -63,4 +63,26 @@
</foreach>
</delete>
+ <!-- 查询已阅读某公告的用户列表,支持按登录名/用户名模糊筛选 -->
+ <select id="selectReadUsersByNoticeId" resultType="java.util.Map">
+ select
+ u.user_id as userId,
+ u.user_name as userName,
+ u.nick_name as nickName,
+ d.dept_name as deptName,
+ u.phonenumber as phonenumber,
+ r.read_time as readTime
+ from sys_notice_read r
+ inner join sys_user u on u.user_id = r.user_id and u.del_flag = '0'
+ left join sys_dept d on d.dept_id = u.dept_id
+ where r.notice_id = #{noticeId}
+ <if test="searchValue != null and searchValue != ''">
+ and (
+ u.user_name like concat('%', #{searchValue}, '%')
+ or u.nick_name like concat('%', #{searchValue}, '%')
+ )
+ </if>
+ order by r.read_time desc
+ </select>
+
</mapper>
diff --git a/ruoyi-ui/src/api/system/notice.js b/ruoyi-ui/src/api/system/notice.js
index c620e47..6f04807 100644
--- a/ruoyi-ui/src/api/system/notice.js
+++ b/ruoyi-ui/src/api/system/notice.js
@@ -68,3 +68,12 @@
params: { ids }
})
}
+
+// 查询公告已读用户列表
+export function listNoticeReadUsers(query) {
+ return request({
+ url: '/system/notice/readUsers/list',
+ method: 'get',
+ params: query
+ })
+}
diff --git a/ruoyi-ui/src/layout/components/HeaderNotice/DetailView.vue b/ruoyi-ui/src/layout/components/HeaderNotice/DetailView.vue
new file mode 100644
index 0000000..e547a96
--- /dev/null
+++ b/ruoyi-ui/src/layout/components/HeaderNotice/DetailView.vue
@@ -0,0 +1,362 @@
+<template>
+ <el-drawer title="公告详情" :visible.sync="visible" direction="rtl" size="50%" append-to-body :before-close="handleClose" custom-class="notice-detail-drawer">
+ <div v-loading="loading" class="notice-detail-drawer__body">
+ <div v-if="!detail" class="notice-empty">
+ <i class="el-icon-document"></i>
+ <span>暂无数据</span>
+ </div>
+ <div v-else class="notice-page">
+ <div class="notice-type-wrap">
+ <span v-if="detail.noticeType === '1'" class="notice-type-tag type-notify">
+ <i class="el-icon-bell"></i> 通知
+ </span>
+ <span v-else-if="detail.noticeType === '2'" class="notice-type-tag type-announce">
+ <i class="el-icon-message"></i> 公告
+ </span>
+ <span v-else class="notice-type-tag type-notify">
+ <i class="el-icon-document"></i> 消息
+ </span>
+ </div>
+
+ <h1 class="notice-title">{{ detail.noticeTitle }}</h1>
+
+ <div class="notice-meta">
+ <span class="meta-item">
+ <i class="el-icon-user"></i>
+ <span>{{ detail.createBy || '—' }}</span>
+ </span>
+ <span class="meta-item">
+ <i class="el-icon-time"></i>
+ <span>{{ detail.createTime || '—' }}</span>
+ </span>
+ <span class="meta-item">
+ <span :class="['status-dot', isStatusNormal ? 'status-ok' : 'status-off']"></span>
+ <span>{{ isStatusNormal ? '正常' : '已关闭' }}</span>
+ </span>
+ </div>
+
+ <div class="notice-divider">
+ <span class="notice-divider-dot"></span>
+ <span class="notice-divider-dot"></span>
+ <span class="notice-divider-dot"></span>
+ </div>
+
+ <div class="notice-body">
+ <div v-if="hasContent" class="notice-content" v-html="detail.noticeContent" />
+ <div v-else class="notice-empty notice-empty--inner">
+ <i class="el-icon-document"></i> 暂无内容
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-drawer>
+</template>
+
+<script>
+import { getNotice } from '@/api/system/notice'
+
+export default {
+ name: 'NoticeDetailView',
+ data() {
+ return {
+ visible: false,
+ loading: false,
+ detail: null
+ }
+ },
+ computed: {
+ isStatusNormal() {
+ const s = this.detail && this.detail.status
+ return s === '0' || s === 0
+ },
+ hasContent() {
+ const c = this.detail && this.detail.noticeContent
+ return c != null && String(c).trim() !== ''
+ }
+ },
+ methods: {
+ open(payload) {
+ let id = null
+ let preset = null
+ if (payload != null && typeof payload === 'object') {
+ id = payload.noticeId
+ if (payload.noticeContent != null) {
+ preset = payload
+ }
+ } else {
+ id = payload
+ }
+ this.visible = true
+ if (preset) {
+ this.detail = preset
+ return
+ }
+ if (id == null || id === '') {
+ this.detail = null
+ return
+ }
+ this.loading = true
+ this.detail = null
+ getNotice(id).then(res => {
+ this.detail = res.data
+ }).catch(() => {
+ this.detail = null
+ }).finally(() => {
+ this.loading = false
+ })
+ },
+ handleClose() {
+ this.visible = false
+ this.detail = null
+ this.loading = false
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.notice-page {
+ max-width: 760px;
+ margin: 0 auto;
+ padding: 8px 8px 20px;
+ animation: notice-fade-up 0.28s ease both;
+}
+
+@keyframes notice-fade-up {
+ from {
+ opacity: 0;
+ transform: translateY(14px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.notice-type-tag {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 3px 12px;
+ border-radius: 2px;
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ margin-bottom: 14px;
+}
+
+.type-notify {
+ background: #fff8e6;
+ color: #b7791f;
+ border-left: 3px solid #d97706;
+}
+
+.type-announce {
+ background: #e8f5e9;
+ color: #276749;
+ border-left: 3px solid #38a169;
+}
+
+.notice-title {
+ font-size: 22px;
+ font-weight: 700;
+ color: #1a202c;
+ line-height: 1.45;
+ margin: 0 0 16px;
+ letter-spacing: -0.2px;
+}
+
+.notice-meta {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 16px;
+ padding: 12px 0;
+ border-top: 1px solid #e9ecef;
+ border-bottom: 1px solid #e9ecef;
+ margin-bottom: 28px;
+}
+
+.meta-item {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 12px;
+ color: #718096;
+}
+
+.meta-item i {
+ font-size: 12px;
+ color: #a0aec0;
+}
+
+.status-dot {
+ display: inline-block;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ margin-right: 4px;
+}
+
+.status-ok {
+ background: #38a169;
+}
+
+.status-off {
+ background: #e53e3e;
+}
+
+.notice-divider {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 24px;
+}
+
+.notice-divider::before,
+.notice-divider::after {
+ content: '';
+ flex: 1;
+ height: 1px;
+ background: linear-gradient(to right, transparent, #dee2e6, transparent);
+}
+
+.notice-divider-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #cbd5e0;
+}
+
+.notice-body {
+ background: #fff;
+ border-radius: 6px;
+ padding: 28px 32px;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);
+ min-height: 120px;
+}
+
+.notice-content {
+ font-size: 14px;
+ line-height: 1.85;
+ color: #2d3748;
+ word-break: break-word;
+}
+
+.notice-content ::v-deep p {
+ margin: 0 0 1em;
+}
+
+.notice-content ::v-deep h1,
+.notice-content ::v-deep h2,
+.notice-content ::v-deep h3 {
+ font-weight: 700;
+ color: #1a202c;
+ margin: 1.4em 0 0.6em;
+}
+
+.notice-content ::v-deep h1 {
+ font-size: 18px;
+}
+
+.notice-content ::v-deep h2 {
+ font-size: 16px;
+}
+
+.notice-content ::v-deep h3 {
+ font-size: 14px;
+}
+
+.notice-content ::v-deep a {
+ color: #3182ce;
+ text-decoration: underline;
+}
+
+.notice-content ::v-deep a:hover {
+ color: #2b6cb0;
+}
+
+.notice-content ::v-deep img {
+ max-width: 100%;
+ border-radius: 4px;
+ margin: 8px 0;
+}
+
+.notice-content ::v-deep ul,
+.notice-content ::v-deep ol {
+ padding-left: 20px;
+ margin: 0 0 1em;
+}
+
+.notice-content ::v-deep li {
+ margin-bottom: 4px;
+}
+
+.notice-content ::v-deep blockquote {
+ border-left: 3px solid #cbd5e0;
+ margin: 1em 0;
+ padding: 6px 16px;
+ color: #718096;
+ background: #f7fafc;
+}
+
+.notice-content ::v-deep table {
+ border-collapse: collapse;
+ width: 100%;
+ margin: 1em 0;
+ font-size: 13px;
+}
+
+.notice-content ::v-deep table th,
+.notice-content ::v-deep table td {
+ border: 1px solid #e2e8f0;
+ padding: 7px 12px;
+}
+
+.notice-content ::v-deep table th {
+ background: #f7fafc;
+ font-weight: 600;
+}
+
+.notice-empty {
+ text-align: center;
+ padding: 40px 0;
+ color: #a0aec0;
+ font-size: 13px;
+}
+
+.notice-empty i {
+ font-size: 28px;
+ display: block;
+ margin-bottom: 10px;
+}
+
+.notice-empty--inner {
+ padding: 32px 0;
+}
+
+.notice-empty--inner i {
+ font-size: 28px;
+}
+
+::v-deep .notice-detail-drawer {
+ .el-drawer__header {
+ margin-bottom: 0;
+ padding: 16px 20px;
+ border-bottom: 1px solid #ebeef5;
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ }
+ .el-drawer__body {
+ background: #f5f6f8;
+ }
+}
+
+.notice-detail-drawer__body {
+ height: 100%;
+ overflow: auto;
+ padding: 10px 16px 22px;
+}
+</style>
diff --git a/ruoyi-ui/src/layout/components/HeaderNotice/index.vue b/ruoyi-ui/src/layout/components/HeaderNotice/index.vue
index 6ee9273..161d91e 100644
--- a/ruoyi-ui/src/layout/components/HeaderNotice/index.vue
+++ b/ruoyi-ui/src/layout/components/HeaderNotice/index.vue
@@ -23,38 +23,24 @@
<span v-if="unreadCount > 0" class="notice-badge">{{ unreadCount }}</span>
</div>
- <el-dialog :title="previewTitle" :visible.sync="previewVisible" width="680px" append-to-body custom-class="notice-preview-dialog">
- <div class="notice-preview-meta">
- <el-tag size="small" :type="previewNoticeType === '1' ? 'warning' : 'success'">
- {{ previewNoticeType === '1' ? '通知' : '公告' }}
- </el-tag>
- <span class="notice-preview-info"><i class="el-icon-user"></i> {{ previewCreateBy }}</span>
- <span class="notice-preview-info"><i class="el-icon-time"></i> {{ previewCreateTime }}</span>
- </div>
- <div class="notice-preview-divider"></div>
- <div class="notice-preview-content" v-html="previewContent"></div>
- </el-dialog>
+ <notice-detail-view ref="noticeViewRef" />
</div>
</template>
<script>
-import { listNoticeTop, markNoticeRead, markNoticeReadAll, getNotice } from '@/api/system/notice'
+import NoticeDetailView from './DetailView'
+import { listNoticeTop, markNoticeRead, markNoticeReadAll } from '@/api/system/notice'
export default {
name: 'HeaderNotice',
+ components: { NoticeDetailView },
data() {
return {
noticeList: [], // 通知列表
unreadCount: 0, // 未读数量
noticeLoading: false, // 加载状态
noticeVisible: false, // 弹出层显示状态
- noticeLeaveTimer: null, // 鼠标离开计时器
- previewVisible: false, // 预览弹窗显示状态
- previewTitle: '', // 预览弹窗标题
- previewContent: '', // 预览弹窗内容
- previewNoticeType: '', // 预览弹窗类型
- previewCreateBy: '', // 预览弹窗创建人
- previewCreateTime: '' // 预览弹窗创建时间
+ noticeLeaveTimer: null // 鼠标离开计时器
}
},
mounted() {
@@ -99,15 +85,7 @@
if (idx !== -1) this.$set(this.noticeList, idx, { ...item, isRead: true })
this.unreadCount = Math.max(0, this.unreadCount - 1)
}
- getNotice(item.noticeId).then(res => {
- const notice = res.data
- this.previewTitle = notice.noticeTitle
- this.previewContent = notice.noticeContent
- this.previewNoticeType = notice.noticeType
- this.previewCreateBy = notice.createBy
- this.previewCreateTime = notice.createTime
- this.previewVisible = true
- })
+ this.$refs.noticeViewRef.open(item.noticeId)
},
// 全部已读
markAllRead() {
@@ -199,31 +177,5 @@
flex-shrink: 0;
font-size: 11px;
color: #bbb;
-}
-::v-deep .notice-preview-dialog {
- .el-dialog__body { padding: 0 20px 20px; }
- .notice-preview-meta {
- display: flex;
- align-items: center;
- gap: 14px;
- padding: 12px 0;
- font-size: 12px;
- color: #888;
- .notice-preview-info { display: flex; align-items: center; gap: 4px; }
- }
- .notice-preview-divider {
- height: 1px;
- background: linear-gradient(to right, transparent, #e2e8f0, transparent);
- margin-bottom: 16px;
- }
- .notice-preview-content {
- font-size: 14px;
- line-height: 1.85;
- color: #2d3748;
- word-break: break-word;
- img { max-width: 100%; border-radius: 4px; }
- p { margin: 0 0 1em; }
- a { color: #409EFF; text-decoration: underline; }
- }
}
</style>
diff --git a/ruoyi-ui/src/views/system/notice/ReadUsers.vue b/ruoyi-ui/src/views/system/notice/ReadUsers.vue
new file mode 100644
index 0000000..663f350
--- /dev/null
+++ b/ruoyi-ui/src/views/system/notice/ReadUsers.vue
@@ -0,0 +1,109 @@
+<template>
+ <el-dialog :title="`「${noticeTitle}」已读用户`" :visible.sync="visible" width="760px" top="6vh" append-to-body @close="handleClose">
+ <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" style="margin-bottom: 4px;">
+ <el-form-item prop="searchValue">
+ <el-input
+ v-model="queryParams.searchValue"
+ placeholder="登录名称 / 用户名称"
+ clearable
+ prefix-icon="el-icon-search"
+ style="width: 220px;"
+ @keyup.enter.native="handleQuery"
+ @clear="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+ <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ <el-form-item style="float: right; margin-right: 0;">
+ <span class="read-stat">
+ 共 <strong>{{ total }}</strong> 人已读
+ </span>
+ </el-form-item>
+ </el-form>
+ <el-table v-loading="loading" :data="userList" size="small" stripe height="340px">
+ <el-table-column type="index" label="序号" width="55" align="center" />
+ <el-table-column label="登录名称" prop="userName" align="center" :show-overflow-tooltip="true" />
+ <el-table-column label="用户名称" prop="nickName" align="center" :show-overflow-tooltip="true" />
+ <el-table-column label="所属部门" prop="deptName" align="center" :show-overflow-tooltip="true" />
+ <el-table-column label="手机号码" prop="phonenumber" align="center" width="120" />
+ <el-table-column label="阅读时间" prop="readTime" align="center" width="160">
+ <template slot-scope="scope">
+ <span>{{ parseTime(scope.row.readTime) }}</span>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" style="padding: 6px 0px;"/>
+ </el-dialog>
+</template>
+
+<script>
+import { listNoticeReadUsers } from "@/api/system/notice"
+
+export default {
+ name: "ReadUsers",
+ data() {
+ return {
+ visible: false,
+ loading: false,
+ noticeId: undefined,
+ noticeTitle: "",
+ total: 0,
+ userList: [],
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ noticeId: undefined,
+ searchValue: undefined
+ }
+ }
+ },
+ methods: {
+ open(row) {
+ this.noticeId = row.noticeId
+ this.noticeTitle = row.noticeTitle
+ this.queryParams.noticeId = row.noticeId
+ this.queryParams.searchValue = undefined
+ this.queryParams.pageNum = 1
+ this.visible = true
+ this.getList()
+ },
+ getList() {
+ this.loading = true
+ listNoticeReadUsers(this.queryParams).then(res => {
+ this.userList = res.rows
+ this.total = res.total
+ }).finally(() => {
+ this.loading = false
+ })
+ },
+ handleQuery() {
+ this.queryParams.pageNum = 1
+ this.getList()
+ },
+ resetQuery() {
+ this.resetForm("queryForm")
+ this.handleQuery()
+ },
+ handleClose() {
+ this.userList = []
+ this.total = 0
+ this.queryParams.searchValue = undefined
+ }
+ }
+}
+</script>
+
+<style scoped>
+.read-stat {
+ font-size: 13px;
+ color: #606266;
+ line-height: 28px;
+}
+.read-stat strong {
+ color: #409eff;
+ font-size: 15px;
+ margin: 0 2px;
+}
+</style>
diff --git a/ruoyi-ui/src/views/system/notice/index.vue b/ruoyi-ui/src/views/system/notice/index.vue
index 8d5f321..5b0f0b6 100644
--- a/ruoyi-ui/src/views/system/notice/index.vue
+++ b/ruoyi-ui/src/views/system/notice/index.vue
@@ -72,12 +72,11 @@
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="noticeId" width="100" />
- <el-table-column
- label="公告标题"
- align="center"
- prop="noticeTitle"
- :show-overflow-tooltip="true"
- />
+ <el-table-column label="公告标题" align="center" :show-overflow-tooltip="true">
+ <template slot-scope="scope">
+ <a class="link-type" style="cursor:pointer" @click="handleViewData(scope.row)">{{ scope.row.noticeTitle }}</a>
+ </template>
+ </el-table-column>
<el-table-column label="公告类型" align="center" prop="noticeType" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_notice_type" :value="scope.row.noticeType"/>
@@ -96,6 +95,13 @@
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-user"
+ @click="handleReadUsers(scope.row)"
+ v-hasPermi="['system:notice:list']"
+ >阅读用户</el-button>
<el-button
size="mini"
type="text"
@@ -166,14 +172,20 @@
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
+
+ <notice-detail-view ref="noticeViewRef" />
+ <read-users-dialog ref="readUsersRef" />
</div>
</template>
<script>
+import NoticeDetailView from "@/layout/components/HeaderNotice/DetailView"
+import ReadUsersDialog from "./ReadUsers"
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice"
export default {
name: "Notice",
+ components: { NoticeDetailView, ReadUsersDialog },
dicts: ['sys_notice_status', 'sys_notice_type'],
data() {
return {
@@ -297,6 +309,14 @@
}
})
},
+ /** 查看公告详情 */
+ handleViewData(row) {
+ this.$refs.noticeViewRef.open(row)
+ },
+ /** 查看已读用户 */
+ handleReadUsers(row) {
+ this.$refs.readUsersRef.open(row)
+ },
/** 删除按钮操作 */
handleDelete(row) {
const noticeIds = row.noticeId || this.ids
--
Gitblit v1.9.3