feat(notice): 新增公告阅读记录管理功能

- 重构公告阅读状态接口为公告阅读记录列表接口
- 新增公告阅读记录模块,包含完整的CRUD操作
- 在公告列表页面集成阅读记录查看功能
- 实现公告阅读状态的表格展示和筛选功能
- 新增公告阅读记录详情页面和回复功能
- 添加公告阅读记录相关的命名空间定义
master
wangyan 2026-02-25 14:55:47 +08:00
parent 363edfed2c
commit dbce939fc7
7 changed files with 264 additions and 13 deletions

View File

@ -24,7 +24,7 @@ export const noticeInfo = declareRequest(
"noticeLoading",
"Get > /appmenu/notice/{id}",
);
export const noticeReadStatus = declareRequest(
export const noticeReadRecordList = declareRequest(
"noticeLoading",
"Post > @/appmenu/notice/noticeReadStatus",
"Post > @/appmenu/notice/noticeReadRecordList",
);

View File

@ -0,0 +1,35 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const noticeReadRecordList = declareRequest(
"noticeReadRecordLoading",
"Post > @/appmenu/noticeReadRecord/list",
);
export const noticeReadRecordAdd = declareRequest(
"noticeReadRecordLoading",
"Post > @/appmenu/noticeReadRecord/save",
);
export const noticeReadRecordEdit = declareRequest(
"noticeReadRecordLoading",
"Put > @/appmenu/noticeReadRecord/edit",
);
export const noticeReadRecordDelete = declareRequest(
"noticeReadRecordLoading",
"Delete > @/appmenu/noticeReadRecord/{id}",
);
export const noticeReadRecordBatchDelete = declareRequest(
"noticeReadRecordLoading",
"Delete > @/appmenu/noticeReadRecord/ids/{ids}",
);
export const noticeReadRecordInfo = declareRequest(
"noticeReadRecordLoading",
"Get > /appmenu/noticeReadRecord/{id}",
);
export const noticeInfo = declareRequest(
"noticeReadRecordLoading",
"Get > /appmenu/notice/noticeContentInfo/{id}",
);
export const noticeReadRecordReply = declareRequest(
"noticeReadRecordLoading",
"Put > @/appmenu/noticeReadRecord/reply",
);

View File

@ -7,4 +7,5 @@ import { defineNamespace } from "@cqsjjb/jjb-dva-runtime";
export const NS_GLOBAL = defineNamespace("global");
export const NS_APP_MENU = defineNamespace("appMenu");
export const NS_NOTICE = defineNamespace("Notice");
export const NS_NoticeReadRecord = defineNamespace("NoticeReadRecord");
export const NS_CORPINFO = defineNamespace("corpInfo");

View File

@ -82,7 +82,7 @@ function List(props) {
<span
style={{ color: "#00BCD4", cursor: "pointer" }}
onClick={() => {
setCurrentId(record.id);
setCurrentId(record.noticeId);
setViewModalVisible(true);
}}
>
@ -145,7 +145,7 @@ function List(props) {
loading={props.noticeLoading}
getData={getData}
currentId={currentId}
noticeReadStatus={props["noticeReadStatus"]}
noticeReadRecordList={props["noticeReadRecordList"]}
onCancel={() => {
setViewModalVisible(false);
setCurrentId("");
@ -157,8 +157,12 @@ function List(props) {
}
function ViewModalComponent(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["noticeReadStatus"], {
const { tableProps, getData } = useTable(props["noticeReadRecordList"], {
form,
params: {
noticeId: props.currentId,
pageSize: 10,
},
});
const onCancel = () => {
@ -170,26 +174,40 @@ function ViewModalComponent(props) {
maskClosable={false}
open={props.open}
title="查看"
width={800}
onOk={form.submit}
width={1000}
footer={null}
onCancel={onCancel}
loading={props.loading}
>
<Search
labelCol={{ span: 2 }}
labelCol={{ span: 8 }}
form={form}
onFinish={getData}
options={[
{ name: "companyName", label: "企业名称" },
{ name: "deptId", label: "是否阅读" },
{ name: "corpinfoName", label: "企业名称" },
{
name: "readStatus",
label: "是否阅读",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: [{ bianma: "0", name: "未读" }, { bianma: "1", name: "已读" }],
},
]}
/>
<Table
rowKey="userId"
columns={[
{ dataIndex: "companyName", title: "企业名称" },
{ dataIndex: "corpinfoName", title: "企业名称" },
{ dataIndex: "userName", title: "姓名" },
{ dataIndex: "readStatus", title: "阅读状态" },
{
dataIndex: "readStatus",
title: "阅读状态",
render: (_, record) => {
if (!record.readStatus)
return "";
const scopeMap = { 0: "未读", 1: "已读" };
return scopeMap[record.readStatus];
},
},
{ dataIndex: "readTime", title: "阅读时间" },
{ dataIndex: "replyContent", title: "回复内容" },
]}

View File

@ -0,0 +1,61 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Space } from "antd";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search/index";
import Table from "zy-react-library/components/Table/index";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import useTable from "zy-react-library/hooks/useTable";
import { NS_NoticeReadRecord } from "~/enumerate/namespace";
function NoticeReadRecord(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["noticeReadRecordList"], {
form,
});
return (
<Page isShowAllAction={false}>
<Search
labelCol={{ span: 6 }}
form={form}
onFinish={getData}
options={[
{ name: "title", label: "公告名称" },
{
name: "readStatus",
label: "是否阅读",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: [{ bianma: "0", name: "未读" }, { bianma: "1", name: "已读" }],
},
]}
/>
<Table
rowKey="noticeId"
columns={[
{ dataIndex: "title", title: "公告名称" },
{ dataIndex: "publishTime", title: "发布时间" },
{ title: "是否阅读", dataIndex: "readStatus", render: (_, record) => (
<div>{record.readStatus === "1" ? <span style={{ color: "green" }}></span> : record.readStatus === "0" ? <span style={{ color: "red" }}></span> : <span style={{ color: "red" }}></span>}</div>
) },
{
title: "操作",
align: "center",
width: 200,
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./View?id=${record.noticeId}`)}
>
查看
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_NoticeReadRecord], true)(NoticeReadRecord);

View File

@ -0,0 +1,131 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import {
Button,
Form,
message,
Modal,
Typography,
} from "antd";
import { useEffect, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
import Page from "zy-react-library/components/Page";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import { NS_NoticeReadRecord } from "~/enumerate/namespace";
function NoticeReadRecordView(props) {
const { Title, Text } = Typography;
const queryParams = useGetUrlQuery();
const [noticeReadRecordInfo, setNoticeReadRecordInfo] = useState({});
const [replyModalVisible, setReplyModalVisible] = useState(false);
const [form] = Form.useForm();
const getData = async () => {
const { data } = await props["noticeInfo"]({ id: queryParams.id });
setNoticeReadRecordInfo(data);
// 如果未查看,则插入查看记录
if (data.readStatus !== "1") {
const res = await props["noticeReadRecordAdd"]({ noticeId: queryParams.id });
if (!res.success) {
message.error("记录阅读状态失败,请重新阅读");
}
else {
setNoticeReadRecordInfo(prev => ({
...prev,
noticeReadId: res.data,
}));
}
}
};
useEffect(() => {
getData();
}, []);
const handleReply = () => {
setReplyModalVisible(true);
};
const handleReplySubmit = async (values) => {
const { success } = await props["noticeReadRecordReply"]({
noticeReadId: noticeReadRecordInfo.noticeReadId,
replyContent: values.replyContent,
});
if (success) {
message.success("回复成功");
setReplyModalVisible(false);
form.resetFields();
window.history.back();
}
};
return (
<Page
headerTitle="查看"
extraActionButtons={
noticeReadRecordInfo.requireReply && !noticeReadRecordInfo.replyContent && (
<Button
type="primary"
onClick={handleReply}
>
回复
</Button>
)
}
>
<div style={{ maxWidth: 1200, margin: "0 auto", padding: "20px" }}>
<Title level={2} style={{ textAlign: "center", marginBottom: "24px" }}>
{noticeReadRecordInfo.title}
</Title>
<div style={{ textAlign: "center", marginBottom: "24px" }}>
<Text type="secondary">
发布时间
{noticeReadRecordInfo.publishTime}
</Text>
</div>
<div
style={{
minHeight: "200px",
padding: "20px",
marginBottom: "24px",
}}
>
<div dangerouslySetInnerHTML={{ __html: noticeReadRecordInfo.content }} />
</div>
</div>
<Modal
title="回复公告"
width={600}
open={replyModalVisible}
maskClosable={false}
onCancel={() => {
setReplyModalVisible(false);
form.resetFields();
}}
onOk={() => form.submit()}
confirmLoading={props.noticeReadRecordReplyLoading}
>
<FormBuilder
form={form}
span={24}
labelCol={{ span: 10 }}
showActionButtons={false}
onFinish={handleReplySubmit}
loading={props.noticeReadRecordReplyLoading}
options={[
{
name: "replyContent",
label: "回复内容",
required: true,
render: FORM_ITEM_RENDER_ENUM.TEXTAREA,
},
]}
/>
</Modal>
</Page>
);
}
export default Connect([NS_NoticeReadRecord], true)(NoticeReadRecordView);

View File

@ -0,0 +1,5 @@
function AppMenu(props) {
return props.children;
}
export default AppMenu;