From c11671c199f29281e3513d51eeb1cd915fef71c4 Mon Sep 17 00:00:00 2001 From: lix Date: Fri, 25 Jul 2025 11:25:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E5=91=8A=E4=B8=AD=E5=BF=83=E5=92=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/router.config.ts | 11 + src/components/richText/wang/index.tsx | 4 +- .../components/AnnouncementAdd.tsx | 284 ++++++++++++++++++ .../components/AnnouncementList.tsx | 260 ++++++++++++++++ .../components/style.module.less | 17 ++ src/pages/AnnouncementCenter/index.tsx | 12 + src/pages/AnnouncementCenter/service.ts | 175 +++++++++++ src/pages/AnnouncementCenter/types.ts | 15 + 8 files changed, 776 insertions(+), 2 deletions(-) create mode 100644 src/pages/AnnouncementCenter/components/AnnouncementAdd.tsx create mode 100644 src/pages/AnnouncementCenter/components/AnnouncementList.tsx create mode 100644 src/pages/AnnouncementCenter/components/style.module.less create mode 100644 src/pages/AnnouncementCenter/index.tsx create mode 100644 src/pages/AnnouncementCenter/service.ts create mode 100644 src/pages/AnnouncementCenter/types.ts diff --git a/config/router.config.ts b/config/router.config.ts index d65c8b1..d66c0a2 100644 --- a/config/router.config.ts +++ b/config/router.config.ts @@ -203,6 +203,17 @@ export default [ { name: 'noticeManage', path: '/notice/noticeManage', component: './notice/noticeManage/components/NoticeManage' },//通知公告管理-系统管理员 ] }, + {//公告中心 + name: 'announcementCenter', + icon: 'notification', + path: '/AnnouncementCenter', + routes: [ + { name: 'announcementList', path: '/AnnouncementCenter', component: './AnnouncementCenter' },//公告中心列表 + { name: 'announcementAdd', path: '/AnnouncementCenter/add', component: './AnnouncementCenter/components/AnnouncementAdd' },//新增公告 + { name: 'announcementEdit', path: '/AnnouncementCenter/edit', component: './AnnouncementCenter/components/AnnouncementAdd' },//编辑公告 + { name: 'announcementView', path: '/AnnouncementCenter/view', component: './AnnouncementCenter/components/AnnouncementAdd' },//查看公告 + ] + }, {//委托 name: 'entrust', icon: 'form', diff --git a/src/components/richText/wang/index.tsx b/src/components/richText/wang/index.tsx index ec07770..98619aa 100644 --- a/src/components/richText/wang/index.tsx +++ b/src/components/richText/wang/index.tsx @@ -55,7 +55,7 @@ const BraftText: React.FC = (props) => { 'list', //列表 'undo', //撤销 'redo', //恢复 - 'fullscreen', //全屏 + // 'fullscreen', //全屏 // 'image',//图片 // 'emoticon',//表情 @@ -75,7 +75,7 @@ const BraftText: React.FC = (props) => { //工具栏 editor.config.menus = tools; //提示 - editor.config.placeholder = '为了能顺利发布,建议您内容去掉下划线等格式,尽量以纯文本形式发布。'; + editor.config.placeholder = ''; // 配置 onchange 回调函数 editor.config.onchange = editorOnChange; // 注册菜单 diff --git a/src/pages/AnnouncementCenter/components/AnnouncementAdd.tsx b/src/pages/AnnouncementCenter/components/AnnouncementAdd.tsx new file mode 100644 index 0000000..f7efb49 --- /dev/null +++ b/src/pages/AnnouncementCenter/components/AnnouncementAdd.tsx @@ -0,0 +1,284 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { + Button, + Card, + Form, + Input, + Radio, + DatePicker, + Checkbox, + message, + Row, + Col, + Spin, + Space +} from 'antd'; +import type { CheckboxChangeEvent } from 'antd/es/checkbox'; +import type { CheckboxValueType } from 'antd/es/checkbox/Group'; +import { history, useLocation, useSearchParams } from '@umijs/max'; +import moment from 'moment'; +import BraftText from '@/components/richText/wang'; + +import { + getAnnouncementTypeDict, + createAnnouncement, + updateAnnouncement, + getAnnouncementDetail +} from '../service'; + +const { RangePicker } = DatePicker; + +// 表单布局配置 +const formItemLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 16 }, +}; + +// 公告发布网站 +const PUBLISH_WEBSITE_OPTIONS = [ + { + label: '中远海运集团采购信息网', + value: '1', + }, + { + label: '中国招标投标公共服务平台', + value: '2', + }, +]; + +const titleMap = { + add: '新增公告', + edit: '编辑公告', + view: '查看公告', +} + +const AnnouncementAdd: React.FC<{}> = () => { + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [publishWebsites, setPublishWebsites] = useState([]); + const [publishWebsitesIndeterminate, setPublishWebsitesIndeterminate] = useState(false); + const [publishWebsitesCheckAll, setPublishWebsitesCheckAll] = useState(false); + const [announcementTypeDict, setAnnouncementTypeDict] = useState([]); + const { pathname } = useLocation(); + const [searchParams] = useSearchParams(); + // 对 pathname 进行分割 取最后一个 判断页面状态 + const pathnameArr = pathname.split('/'); + const pageStatus = pathnameArr[pathnameArr.length - 1] as 'add' | 'edit' | 'view'; + const announcementId = searchParams.get('id'); + const richTextRef = useRef(null); + + // 初始化表单数据 + useEffect(() => { + if (pageStatus !== 'add' && announcementId) { + loadAnnouncementDetail(announcementId); + } + }, [pageStatus, announcementId]); + + // 富文本编辑器回显内容 + const [richTextContent, setRichTextContent] = useState(''); + + useEffect(() => { + getAnnouncementTypeDict().then(res => { + setAnnouncementTypeDict(res.data); + if (pageStatus === 'add') { + form.setFieldsValue({ + announcementType: res.data?.[0]?.value, + }); + } + }); + }, [pageStatus]); + + // 加载公告详情 + const loadAnnouncementDetail = async (id: string) => { + setLoading(true); + try { + const res = await getAnnouncementDetail(id); + if (res.code === 200) { + const data = res.data; + form.setFieldsValue({ + announcementType: data.announcementType, + announcementName: data.announcementName, + publishTime: [ + moment(data.publishStartTime), + moment(data.publishEndTime) + ], + }); + setPublishWebsites(data.publishWebsites || []); + setRichTextContent(data.content || ''); + } + } catch (error) { + message.error('加载公告详情失败'); + } + setLoading(false); + }; + + // 保存功能 + const handleSave = async () => { + setSubmitting(true); + form.validateFields().then(async values => { + const content = richTextRef.current?.getHtml1() || ''; + const formData = { + announcementType: values.announcementType, + announcementName: values.announcementName, + publishStartTime: values.publishTime[0].format('YYYY-MM-DD HH:mm:ss'), + publishEndTime: values.publishTime[1].format('YYYY-MM-DD HH:mm:ss'), + publishWebsites, + content, + }; + + let res; + if (pageStatus === 'edit' && announcementId) { + res = await updateAnnouncement({ + ...formData, + id: announcementId, + }); + } else { + res = await createAnnouncement(formData); + } + + if (res.code === 200) { + message.success(pageStatus === 'edit' ? '更新成功!' : '保存成功!'); + // history.push('/AnnouncementCenter'); + } + }).finally(() => { + setSubmitting(false); + }) + }; + + // 取消功能 + const handleCancel = () => { + history.replace('/AnnouncementCenter'); + }; + + const onPublishWebsitesCheckAll = (e: CheckboxChangeEvent) => { + setPublishWebsites(e.target.checked ? PUBLISH_WEBSITE_OPTIONS.map(item => item.value) : []); + setPublishWebsitesIndeterminate(false); + setPublishWebsitesCheckAll(e.target.checked); + } + const onPublishWebsitesChange = (list: CheckboxValueType[]) => { + setPublishWebsites(list); + setPublishWebsitesIndeterminate(!!list.length && list.length < PUBLISH_WEBSITE_OPTIONS.length); + setPublishWebsitesCheckAll(list.length === PUBLISH_WEBSITE_OPTIONS.length); + }; + + return ( + + + {titleMap[pageStatus]} + + + + + + )}> +
+ + +

基本信息

+ +
+ + + + + {announcementTypeDict.map((option) => ( + + + {option.label} + + + ))} + + + + + + + + + + trigger.parentElement || document.body} + popupStyle={{ zIndex: 9999 }} + /> + + + + + + + 全选 + + + + + + {PUBLISH_WEBSITE_OPTIONS.map((option) => ( + + + {option.label} + + + ))} + + + + + + + + +

公告正文

+ +
+ +
+ +
+
+
+
+
+ ); +}; + +export default AnnouncementAdd; \ No newline at end of file diff --git a/src/pages/AnnouncementCenter/components/AnnouncementList.tsx b/src/pages/AnnouncementCenter/components/AnnouncementList.tsx new file mode 100644 index 0000000..0848074 --- /dev/null +++ b/src/pages/AnnouncementCenter/components/AnnouncementList.tsx @@ -0,0 +1,260 @@ +import React, { useRef, useState, useEffect } from 'react'; +import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; +import tableProps from '@/utils/tableProps'; +import { Button, Card, message, Popconfirm, Space } from 'antd'; +import { history } from '@umijs/max'; +import styles from './style.module.less'; + +import type { DataType, AnnouncementSearchParams } from '../types'; +import { + getAnnouncementList, + getAnnouncementTypeDict, + deleteAnnouncement, + publishAnnouncement +} from '../service'; + +const AnnouncementList: React.FC<{}> = () => { + const actionRef = useRef(); + const [loading, setLoading] = useState(false); + const [announcementTypeDict, setAnnouncementTypeDict] = useState([]); + + // 表格列定义 + const columns: ProColumns[] = [ + { + title: '序号', + valueType: 'index', + width: 80, + search: false + }, + { + title: '公告编号', + dataIndex: 'announcementNo', + search: false + }, + { + title: '公告名称', + dataIndex: 'announcementName', + }, + { + title: '公告类型', + dataIndex: 'announcementType', + valueType: 'select', + valueEnum: announcementTypeDict.reduce((acc, item) => { + acc[item.value] = item.label; + return acc; + }, {} as Record), + }, + { + title: '发布时间', + dataIndex: 'publishTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + publishTimeStart: value[0], + publishTimeEnd: value[1], + }; + }, + }, + render: (_, record) => `${record.publishTimeStart} - ${record.publishTimeEnd}`, + }, + { + title: '状态', + dataIndex: 'status', + search: false, + valueEnum: { + '1': '编辑中', + '2': '已保存', + '3': '已发布', + }, + }, + { + title: '操作', + search: false, + width: 80, + render: (_, record) => { + // 已发布状态:只有查看按钮 + if (record.status === '3') { + return ( + + ) + } + + // 已保存状态:有发布、编辑、删除三个按钮 + if (record.status === '2') { + return ( + + handlePublish(record.id)} + okText="确定" + cancelText="取消" + > + + + + handleDelete(record.id)} + okText="确定" + cancelText="取消" + > + + + + ) + } + + // 编辑中状态:有起草、删除两个按钮 + if (record.status === '1') { + return ( + + + handleDelete(record.id)} + okText="确定" + cancelText="取消" + > + + + + ) + } + + return []; + }, + }, + ]; + + // 查询功能 + const handleSearch = async (params: AnnouncementSearchParams ) => { + setLoading(true); + const requestParams = { + ...params, + pageNo: params.current, + } + const res = await getAnnouncementList(requestParams); + setLoading(false); + return { + data: res.data.records, + total: res.data.total, + success: res.success, + }; + }; + + // 新增公告导航 + const handleAdd = () => { + history.push('/AnnouncementCenter/add'); + }; + + // 编辑功能 + const handleEdit = (record: DataType) => { + history.push(`/AnnouncementCenter/edit?id=${record.id}`); + }; + + // 查看功能 + const handleView = (record: DataType) => { + history.push(`/AnnouncementCenter/view?id=${record.id}`); + }; + + // 删除功能 + const handleDelete = async (id: string) => { + setLoading(true); + try { + const res = await deleteAnnouncement(id); + if (res.code === 200) { + message.success('删除成功!'); + actionRef.current?.reload(); + } else { + message.error('删除失败,请重试'); + } + } catch (error) { + message.error('删除失败,请重试'); + } + setLoading(false); + }; + + // 发布功能 + const handlePublish = async (id: string) => { + setLoading(true); + try { + const res = await publishAnnouncement(id); + if (res.code === 200) { + message.success('发布成功!'); + actionRef.current?.reload(); + } else { + message.error('发布失败,请重试'); + } + } catch (error) { + message.error('发布失败,请重试'); + } + setLoading(false); + }; + + useEffect(() => { + getAnnouncementTypeDict().then(res => { + setAnnouncementTypeDict(res.data); + }); + }, []); + + return ( + + + actionRef={actionRef} + loading={loading} + className={styles['announcement-list']} + columns={columns} + headerTitle="我发布的公告" + toolBarRender={() => [ + , + ]} + request={handleSearch} + {...tableProps} + options={false} + rowKey="id" + /> + + ); +}; + +export default AnnouncementList; \ No newline at end of file diff --git a/src/pages/AnnouncementCenter/components/style.module.less b/src/pages/AnnouncementCenter/components/style.module.less new file mode 100644 index 0000000..963a009 --- /dev/null +++ b/src/pages/AnnouncementCenter/components/style.module.less @@ -0,0 +1,17 @@ +.announcement-list { + :global { + .ant-pro-table-search-query-filter { + padding-left: 6px; + padding-right: 6px; + } + .ant-card { + .ant-card-body { + padding: 0; + .ant-pro-table-list-toolbar-container { + padding: 12px 6px; + background: #F2F2F2; + } + } + } + } +} \ No newline at end of file diff --git a/src/pages/AnnouncementCenter/index.tsx b/src/pages/AnnouncementCenter/index.tsx new file mode 100644 index 0000000..56b5193 --- /dev/null +++ b/src/pages/AnnouncementCenter/index.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import AnnouncementList from './components/AnnouncementList'; + +const Index: React.FC = () => { + return ( + <> + + + ); +}; + +export default Index; \ No newline at end of file diff --git a/src/pages/AnnouncementCenter/service.ts b/src/pages/AnnouncementCenter/service.ts new file mode 100644 index 0000000..96724a8 --- /dev/null +++ b/src/pages/AnnouncementCenter/service.ts @@ -0,0 +1,175 @@ +import request from '@/utils/request'; +import { AnnouncementSearchParams } from './types'; + +// 获取公告列表,支持分页和搜索 +export async function getAnnouncementList(params: AnnouncementSearchParams) { + console.log('getAnnouncementList params', params); + + await new Promise(resolve => setTimeout(resolve, 1000)); + return { + data: { + records: [ + { + id: '1', + announcementNo: '123456', + announcementName: '测试公告', + announcementType: '1', + publishTimeStart: '2021-01-01', + publishTimeEnd: '2021-01-02', + status: '1', + }, + + { + id: '2', + announcementNo: '123457', + announcementName: '测试公告2', + announcementType: '2', + publishTimeStart: '2021-01-02', + publishTimeEnd: '2021-01-03', + status: '2', + }, + { + id: '3', + announcementNo: '123458', + announcementName: '测试公告3', + announcementType: '3', + publishTimeStart: '2021-01-03', + publishTimeEnd: '2021-01-04', + status: '3', + }, + ], + total: 3, + }, + success: true, + } + // return request('', { + // method: 'GET', + // data: params, + // }); +} + +// 获取公告类型字典 +export async function getAnnouncementTypeDict() { + await new Promise(resolve => setTimeout(resolve, 1000)); + return { + data: [ + { + label: '需求公示', + value: '1', + }, + + { + label: '项目公告', + value: '2', + }, + { + label: '邀请函内容披露', + value: '3', + }, + { + label: '中标候选人公示', + value: '4', + }, + { + label: '中标结果公示', + value: '5', + }, + { + label: '失败公告', + value: '6', + }, + { + label: '变更公告', + value: '7', + }, + { + label: '其他', + value: '8', + }, + ], + success: true, + } + // return request('', { + // method: 'GET', + // }); +} + +// 发布公告 +export async function publishAnnouncement(id: string) { + await new Promise(resolve => setTimeout(resolve, 1000)); + return { + data: {}, + code: 200, + success: true, + } + // return request(`/api/v1//announcement/publish/${id}`, { + // method: '', + // }); +} + +// 删除公告 +export async function deleteAnnouncement(id: string) { + await new Promise(resolve => setTimeout(resolve, 1000)); + return { + data: {}, + code: 200, + success: true, + } + // return request(`/api//v1/announcement/delete/${id}`, { + // method: 'DELETE', + // }); +} + +// 创建新公告 +export async function createAnnouncement(data: any) { + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log('createAnnouncement data', data); + return { + data: {}, + code: 200, + success: true, + } + // return request('/api/v1/announcement/create', { + // method: 'POST', + // data, + // }); +} + +// 更新公告 +export async function updateAnnouncement(data: any) { + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log('updateAnnouncement data', data); + return { + data: {}, + code: 200, + success: true, + } + // return request(`/api/v1/announcement/update/${id}`, { + // method: 'PUT', + // data, + // }); +} + + + +// 获取公告详情 +export async function getAnnouncementDetail(id: string) { + await new Promise(resolve => setTimeout(resolve, 1000)); + return { + data: { + id: '1', + announcementNo: '123456', + announcementName: '测试公告', + announcementType: '3', + publishStartTime: '2021-01-01', + publishEndTime: '2021-01-02', + publishWebsites: ['1'], + content: '

测试公告内容

', + }, + code: 200, + success: true, + } + // return request(`/api//v1/announcement/detail/${id}`, { + // method: 'GET', + // }); +} \ No newline at end of file diff --git a/src/pages/AnnouncementCenter/types.ts b/src/pages/AnnouncementCenter/types.ts new file mode 100644 index 0000000..d497d4e --- /dev/null +++ b/src/pages/AnnouncementCenter/types.ts @@ -0,0 +1,15 @@ +export interface AnnouncementSearchParams { + announcementName?: string; + publishTimeStart?: string; + publishTimeEnd?: string; + announcementType?: string; + pageSize?: number; + current?: number; +} + +export interface DataType extends AnnouncementSearchParams { + id: string; + announcementNo: string; + status: string; + content?: string; +}