对接通知中心与政策法规,包括多语言维护

This commit is contained in:
linxd
2025-06-30 19:40:25 +08:00
parent 73fc2bfa7b
commit 60473ed8ae
18 changed files with 632 additions and 514 deletions

View File

@ -7,12 +7,12 @@ export default {
// }, // },
'/api': { '/api': {
// target: 'http://10.242.37.148:18022',//连接天宫的ng // target: 'http://10.242.37.148:18022',//连接天宫的ng
target: 'http://10.0.0.125:18012',//连接天宫的ng target: 'http://10.0.0.14:18013',//连接天宫的ng
changeOrigin: true, changeOrigin: true,
pathRewrite: { '^/api': '' }, pathRewrite: { '^/api': '' },
}, },
'/upload': { '/upload': {
target: 'http://10.0.0.125:18012',// target: 'http://10.0.0.14:18013',//
changeOrigin: true, changeOrigin: true,
pathRewrite: { '^/upload': '' }, pathRewrite: { '^/upload': '' },
}, },

View File

@ -1,5 +1,7 @@
import help from './en-US/help'; import help from './en-US/help';
import policy from './en-US/policy'; import policy from './en-US/policy';
import download from './en-US/download';
import notice from './en-US/notice';
export default { export default {
'menu.首页': 'Home', 'menu.首页': 'Home',
@ -104,4 +106,10 @@ export default {
// Policy and Regulations module // Policy and Regulations module
...policy, ...policy,
// Download Center module
...download,
// Notice Center module
...notice,
}; };

View File

@ -0,0 +1,15 @@
export default {
// Download center page
'download.title': 'Download Center',
'download.menu.template': 'Templates',
'download.menu.manual': 'Manuals',
'download.card.date': 'Published: {publishTime}',
'download.button': 'Download',
'download.empty': 'No files available for download',
'download.list.total': 'Total {total} Records',
// Download center messages
'download.message.downloadFailed': 'Download failed, please try again later',
'download.message.getListFailed': 'Failed to get download list',
'download.message.getListError': 'Error getting download list, please try again later',
};

View File

@ -0,0 +1,29 @@
export default {
// Notice center list page
'notice.title': 'Notifications',
'notice.menu.system': 'System Updates',
'notice.menu.other': 'Other Notifications',
'notice.list.column.index': 'No.',
'notice.list.column.title': 'Title',
'notice.list.column.publishTime': 'Publish Time',
'notice.list.empty': 'No notifications',
'notice.list.total': 'Total {total} Records',
// Notice center detail page
'notice.detail.back': 'Back to List',
'notice.detail.publishTime': 'Publish Time',
'notice.detail.publisher': 'Publisher',
'notice.detail.type': 'Notice Type',
'notice.detail.type.notice': 'Announcement',
'notice.detail.type.system': 'System Notification',
'notice.detail.type.other': 'Other Notification',
'notice.detail.notFound': 'Notice Not Found',
'notice.detail.loading': 'Loading...',
// Notice center messages
'notice.message.getListFailed': 'Failed to get notification list',
'notice.message.getListError': 'Error getting notification list, please try again later',
'notice.message.getDetailFailed': 'Failed to get notification detail',
'notice.message.getDetailError': 'Error getting notification detail, please try again later',
'notice.message.idNotExist': 'Notice ID cannot be empty',
};

View File

@ -1,5 +1,7 @@
import help from './zh-CN/help'; import help from './zh-CN/help';
import policy from './zh-CN/policy'; import policy from './zh-CN/policy';
import download from './zh-CN/download';
import notice from './zh-CN/notice';
export default { export default {
'menu.首页': '首页', 'menu.首页': '首页',
@ -104,4 +106,10 @@ export default {
// 政策法规模块 // 政策法规模块
...policy, ...policy,
// 下载中心模块
...download,
// 通知中心模块
...notice,
}; };

View File

@ -0,0 +1,15 @@
export default {
// 下载中心页面
'download.title': '下载中心',
'download.menu.template': '模板文件',
'download.menu.manual': '操作手册',
'download.card.date': '发布时间: {publishTime}',
'download.button': '下载',
'download.empty': '暂无可下载的文件',
'download.list.total': '共 {total} 条记录',
// 下载中心消息
'download.message.downloadFailed': '下载失败,请稍后重试',
'download.message.getListFailed': '获取下载列表失败',
'download.message.getListError': '获取下载列表出错,请稍后重试',
};

View File

@ -0,0 +1,29 @@
export default {
// 通知中心列表页面
'notice.title': '通知中心',
'notice.menu.system': '系统更新通知',
'notice.menu.other': '其他通知',
'notice.list.column.index': '序号',
'notice.list.column.title': '标题',
'notice.list.column.publishTime': '发布时间',
'notice.list.empty': '暂无通知',
'notice.list.total': '共 {total} 条记录',
// 通知中心详情页面
'notice.detail.back': '返回列表',
'notice.detail.publishTime': '发布时间',
'notice.detail.publisher': '发布人',
'notice.detail.type': '通知类型',
'notice.detail.type.notice': '通知公告',
'notice.detail.type.system': '系统通知',
'notice.detail.type.other': '其他通知',
'notice.detail.notFound': '未找到相关通知',
'notice.detail.loading': '加载中...',
// 通知中心消息
'notice.message.getListFailed': '获取通知列表失败',
'notice.message.getListError': '获取通知列表出错,请稍后重试',
'notice.message.getDetailFailed': '获取通知详情失败',
'notice.message.getDetailError': '获取通知详情出错,请稍后重试',
'notice.message.idNotExist': '通知ID不能为空',
};

View File

@ -1,121 +1,22 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Card, Menu, Row, Col, Pagination, Button, Typography, Space, message } from 'antd'; import { Card, Menu, Row, Col, Pagination, Button, Typography, Space, message } from 'antd';
import { FileOutlined, BookOutlined, DownloadOutlined } from '@ant-design/icons'; import { FileOutlined, BookOutlined, DownloadOutlined } from '@ant-design/icons';
import { useIntl, FormattedMessage } from 'umi';
import styles from './download.less'; import styles from './download.less';
import { downloadFile } from './service'; import { downloadFile, getDownloadList, DownloadRecord } from '@/servers/api/download';
const { Title, Text } = Typography; const { Title, Text } = Typography;
// 模拟模板文件数据 // 模拟模板文件数据 - 后期将通过API获取分类
const mockTemplateFiles = [ const mockCategories: { [key: string]: string } = {
{ template: '模板文件',
id: '1', manual: '操作手册'
title: '公开招标操作手册', };
description: '公开招标各角色操作手册',
publishDate: '2025年2月3日',
type: 'template',
icon: <FileOutlined />,
fileName: '公开招标操作手册.pdf',
},
{
id: '2',
title: '询价采购模板',
description: '标准询价采购流程文档模板',
publishDate: '2025年1月15日',
type: 'template',
icon: <FileOutlined />,
fileName: '询价采购模板.docx',
},
{
id: '3',
title: '竞争性谈判模板',
description: '竞争性谈判标准文档模板',
publishDate: '2024年12月20日',
type: 'template',
icon: <FileOutlined />,
fileName: '竞争性谈判模板.docx',
},
{
id: '4',
title: '单一来源采购模板',
description: '单一来源采购申请及实施模板',
publishDate: '2024年12月5日',
type: 'template',
icon: <FileOutlined />,
fileName: '单一来源采购模板.docx',
},
{
id: '5',
title: '供应商评估表格',
description: '供应商资质与能力评估标准表格',
publishDate: '2024年11月18日',
type: 'template',
icon: <FileOutlined />,
fileName: '供应商评估表格.xlsx',
},
{
id: '6',
title: '合同范本模板',
description: '标准采购合同范本',
publishDate: '2024年11月1日',
type: 'template',
icon: <FileOutlined />,
fileName: '合同范本模板.docx',
},
];
// 模拟操作手册数据
const mockManuals = [
{
id: '7',
title: '供应商注册指南',
description: '供应商平台注册及资质提交操作指南',
publishDate: '2025年1月25日',
type: 'manual',
icon: <BookOutlined />,
fileName: '供应商注册指南.pdf',
},
{
id: '8',
title: '投标操作指南',
description: '电子投标全流程操作手册',
publishDate: '2025年1月10日',
type: 'manual',
icon: <BookOutlined />,
fileName: '投标操作指南.pdf',
},
{
id: '9',
title: '在线开标指南',
description: '在线开标会议参与指南',
publishDate: '2024年12月15日',
type: 'manual',
icon: <BookOutlined />,
fileName: '在线开标指南.pdf',
},
{
id: '10',
title: '评标专家操作手册',
description: '评标专家系统使用指南',
publishDate: '2024年11月28日',
type: 'manual',
icon: <BookOutlined />,
fileName: '评标专家操作手册.pdf',
},
{
id: '11',
title: '合同签署指南',
description: '电子合同签署流程指南',
publishDate: '2024年11月10日',
type: 'manual',
icon: <BookOutlined />,
fileName: '合同签署指南.pdf',
},
];
const DownloadPage: React.FC = () => { const DownloadPage: React.FC = () => {
const intl = useIntl();
const [activeMenu, setActiveMenu] = useState<string>('template'); const [activeMenu, setActiveMenu] = useState<string>('template');
const [downloadData, setDownloadData] = useState<any[]>([]); const [downloadData, setDownloadData] = useState<DownloadRecord[]>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
@ -123,51 +24,75 @@ const DownloadPage: React.FC = () => {
total: 0, total: 0,
}); });
// 根据当前选中的菜单加载对应的下载数据 // 加载下载数据
useEffect(() => { const loadDownloadData = async (page: number, pageSize: number) => {
setLoading(true); setLoading(true);
// 模拟API请求 try {
setTimeout(() => { const response = await getDownloadList({
const data = activeMenu === 'template' ? mockTemplateFiles : mockManuals; pageNo: page.toString(),
pageSize: pageSize.toString()
});
if (response.code === 200 && response.success) {
// 根据当前选中的菜单过滤数据
// 目前没有分类的API暂时都显示后期可以根据columnType进行过滤
const data = response.data.records;
setDownloadData(data); setDownloadData(data);
setPagination((prevPagination) => ({ setPagination({
...prevPagination, current: response.data.current,
total: data.length, pageSize: response.data.size,
current: 1, total: response.data.total,
})); });
} else {
message.error(response.message || intl.formatMessage({ id: 'download.message.getListFailed' }));
}
} catch (error) {
console.error('获取下载列表出错:', error);
message.error(intl.formatMessage({ id: 'download.message.getListError' }));
} finally {
setLoading(false); setLoading(false);
}, 500); }
}, [activeMenu]); };
// 初始加载和分页变化时加载数据
useEffect(() => {
loadDownloadData(pagination.current, pagination.pageSize);
}, [pagination.current, activeMenu]);
// 处理菜单切换 // 处理菜单切换
const handleMenuClick = (e: any) => { const handleMenuClick = (e: any) => {
setActiveMenu(e.key); setActiveMenu(e.key);
setPagination({
...pagination,
current: 1, // 切换分类时重置页码
});
}; };
// 处理分页变化 // 处理分页变化
const handlePageChange = (page: number, pageSize?: number) => { const handlePageChange = (page: number, pageSize?: number) => {
const newPageSize = pageSize || pagination.pageSize;
setPagination({ setPagination({
...pagination, ...pagination,
current: page, current: page,
pageSize: pageSize || pagination.pageSize, pageSize: newPageSize,
}); });
}; };
// 处理下载按钮点击 // 处理下载按钮点击
const handleDownload = async (id: string, fileName: string) => { const handleDownload = async (item: DownloadRecord) => {
try { try {
await downloadFile(id, fileName); window.open(item.fileUrl, '_blank');
// await downloadFile(id, fileName);
} catch (error) { } catch (error) {
message.error('下载失败,请稍后重试'); message.error(intl.formatMessage({ id: 'download.message.downloadFailed' }));
} }
}; };
// 计算当前页显示的数据 // 获取文件图标
const getCurrentPageData = () => { const getFileIcon = (fileType: string) => {
const { current, pageSize } = pagination; // 根据文件类型返回不同图标,可以进一步完善
const startIndex = (current - 1) * pageSize; return <FileOutlined />;
const endIndex = startIndex + pageSize;
return downloadData.slice(startIndex, endIndex);
}; };
return ( return (
@ -181,55 +106,78 @@ const DownloadPage: React.FC = () => {
className={styles.downloadMenu} className={styles.downloadMenu}
> >
<Menu.Item key="template" icon={<FileOutlined />}> <Menu.Item key="template" icon={<FileOutlined />}>
<FormattedMessage id="download.menu.template" />
</Menu.Item> </Menu.Item>
<Menu.Item key="manual" icon={<BookOutlined />}> <Menu.Item key="manual" icon={<BookOutlined />}>
<FormattedMessage id="download.menu.manual" />
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Col> </Col>
<Col span={20}> <Col span={20}>
<div className={styles.downloadContent}> <div className={styles.downloadContent}>
<Title level={4}>{activeMenu === 'template' ? '模板文件' : '操作手册'}</Title> <Title level={4}>
<FormattedMessage id={`download.menu.${activeMenu}`} defaultMessage={intl.formatMessage({ id: 'download.title' })} />
</Title>
<Row gutter={[16, 16]} className={styles.downloadCardList}> <Row gutter={[16, 16]} className={styles.downloadCardList}>
{getCurrentPageData().map((item) => ( {loading ? (
Array(6).fill(null).map((_, index) => (
<Col xs={24} sm={12} md={8} key={`loading-${index}`}>
<Card loading={true} className={styles.downloadCard}></Card>
</Col>
))
) : downloadData.length > 0 ? (
downloadData.map((item) => (
<Col xs={24} sm={12} md={8} key={item.id}> <Col xs={24} sm={12} md={8} key={item.id}>
<Card <Card
hoverable hoverable
className={styles.downloadCard} className={styles.downloadCard}
loading={loading}
> >
<Space direction="vertical" size="small" style={{ width: '100%' }}> <Space direction="vertical" size="small" style={{ width: '100%' }}>
<div className={styles.cardTitle}> <div className={styles.cardTitle}>
<span className={styles.downloadIcon}>{item.icon}</span> <span className={styles.downloadIcon}>{getFileIcon(item.fileType)}</span>
{item.title} {item.name}
</div> </div>
<div className={styles.cardDate}>{item.publishDate}</div> <div className={styles.cardDate}>
<Text type="secondary" ellipsis={{ tooltip: true }}>{item.description}</Text> {intl.formatMessage(
{ id: 'download.card.date' },
{ publishTime: item.publishTime || '' }
)}
</div>
<Text type="secondary" ellipsis={{ tooltip: true }}>{item.keywords || ''}</Text>
<Button <Button
type="primary" type="primary"
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
className={styles.downloadButton} className={styles.downloadButton}
onClick={() => handleDownload(item.id, item.fileName)} onClick={() => handleDownload(item)}
> >
<FormattedMessage id="download.button" />
</Button> </Button>
</Space> </Space>
</Card> </Card>
</Col> </Col>
))} ))
) : (
<Col span={24} style={{ textAlign: 'center', margin: '30px 0' }}>
<Text type="secondary"><FormattedMessage id="download.empty" /></Text>
</Col>
)}
</Row> </Row>
{downloadData.length > 0 && (
<Pagination <Pagination
current={pagination.current} current={pagination.current}
pageSize={pagination.pageSize} pageSize={pagination.pageSize}
total={pagination.total} total={pagination.total}
onChange={handlePageChange} onChange={handlePageChange}
showTotal={(total) => `${total} 条记录`} showTotal={(total) => intl.formatMessage(
{ id: 'download.list.total' },
{ total }
)}
style={{ marginTop: 24, textAlign: 'right' }} style={{ marginTop: 24, textAlign: 'right' }}
/> />
)}
</div> </div>
</Col> </Col>
</Row> </Row>

View File

@ -1,75 +0,0 @@
import { message } from 'antd';
import request from '@/utils/request';
/**
* 获取下载中心文件列表
* @param params 查询参数
*/
export async function getDownloadList(params: {
type: string;
current: number;
pageSize: number;
}) {
try {
// 实际项目中应该通过API获取数据
// return request('/api/download/list', {
// method: 'GET',
// params,
// });
// 模拟API请求返回数据
return Promise.resolve({
success: true,
data: {
list: [],
total: 0,
},
});
} catch (error) {
message.error('获取下载列表失败');
return {
success: false,
data: {
list: [],
total: 0,
},
};
}
}
/**
* 下载文件
* @param fileId 文件ID
* @param fileName 文件名称
*/
export async function downloadFile(fileId: string, fileName: string) {
try {
// 实际项目中应该通过API下载文件
// const response = await request(`/api/download/file/${fileId}`, {
// method: 'GET',
// responseType: 'blob',
// });
// 创建下载链接
// const blob = new Blob([response]);
// const url = window.URL.createObjectURL(blob);
// const link = document.createElement('a');
// link.href = url;
// link.download = fileName;
// document.body.appendChild(link);
// link.click();
// window.URL.revokeObjectURL(url);
// document.body.removeChild(link);
// 模拟下载成功
message.success(`文件"${fileName}"开始下载`);
return {
success: true,
};
} catch (error) {
message.error(`下载文件"${fileName}"失败`);
return {
success: false,
};
}
}

View File

@ -4,7 +4,7 @@ import { ArrowLeftOutlined } from '@ant-design/icons';
import { history, useIntl } from 'umi'; import { history, useIntl } from 'umi';
import WangEditor from 'wangeditor'; import WangEditor from 'wangeditor';
import { QUESTION_TYPES } from '@/dicts/help'; import { QUESTION_TYPES } from '@/dicts/help';
import { addHelpQuestion } from '@/servers/api/help'; import { addHelpCenterQuestion } from '@/servers/api/help';
import styles from './helpQuestion.less'; import styles from './helpQuestion.less';
const { Title } = Typography; const { Title } = Typography;
@ -70,9 +70,13 @@ const HelpQuestionPage: React.FC = () => {
setSubmitting(true); setSubmitting(true);
try { try {
const response = await addHelpQuestion({ const response = await addHelpCenterQuestion({
...values, ...values,
content: content, content: content,
companyName: values.company,
contactDetails: values.email,
fullName: values.name,
userName: values.account,
}); });
if (response.success) { if (response.success) {

View File

@ -1,9 +1,10 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { Card, Row, Col, Tabs, Table } from 'antd'; import { Card, Row, Col, Tabs, Table } from 'antd';
import { useIntl, Link } from 'umi'; import { useIntl, Link } from 'umi';
import './index.less'; import './index.less';
import IconFont from '@/components/IconFont/IconFont'; import IconFont from '@/components/IconFont/IconFont';
import LinkComponent from './Link'; import LinkComponent from './Link';
import { getCoscoPortalsLinksClassification } from '@/servers/api';
const IndexPage: React.FC = () => { const IndexPage: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const [noticeLoading, setNoticeLoading] = useState(false); const [noticeLoading, setNoticeLoading] = useState(false);
@ -152,6 +153,12 @@ const IndexPage: React.FC = () => {
}, },
]; ];
useEffect(() => {
getCoscoPortalsLinksClassification().then((res) => {
console.log(res);
});
}, []);
return ( return (
<div> <div>
{/* 通知列表 */} {/* 通知列表 */}

View File

@ -45,7 +45,7 @@ const LoginPage: React.FC = () => {
// 渲染注册链接只在供应商和专家Tab下显示 // 渲染注册链接只在供应商和专家Tab下显示
const renderRegisterLink = () => { const renderRegisterLink = () => {
if (activeKey === 'agent') { if (activeKey === 'agent' || activeKey === 'expert') {
return null; // 招标代理不显示注册链接 return null; // 招标代理不显示注册链接
} }

View File

@ -1,80 +1,20 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Table, Menu, Row, Col } from 'antd'; import { Table, Menu, Row, Col, message } from 'antd';
import { history } from 'umi'; import { history, useIntl, FormattedMessage } from 'umi';
import { BellOutlined, AppstoreOutlined } from '@ant-design/icons'; import { BellOutlined, AppstoreOutlined } from '@ant-design/icons';
import styles from './notice.less'; import styles from './notice.less';
import { getNoticeList, NoticeRecord } from '@/servers/api/notice';
// 模拟系统更新通知数据 // 通知类型映射
const mockSystemNotices = [ const noticeTypes: { [key: string]: string } = {
{ system: '系统更新通知',
id: '1', other: '其他通知'
title: '系统将于2023年9月15日进行版本升级', };
publishDate: '2023-09-10',
type: 'system',
},
{
id: '2',
title: '招标模块功能优化更新通知',
publishDate: '2023-08-25',
type: 'system',
},
{
id: '3',
title: '系统安全性能升级维护通知',
publishDate: '2023-08-10',
type: 'system',
},
{
id: '4',
title: '移动端应用同步更新通知',
publishDate: '2023-07-28',
type: 'system',
},
{
id: '5',
title: '供应商管理模块功能升级通知',
publishDate: '2023-07-15',
type: 'system',
},
];
// 模拟其他通知数据
const mockOtherNotices = [
{
id: '6',
title: '关于开展2023年度供应商评估工作的通知',
publishDate: '2023-09-05',
type: 'other',
},
{
id: '7',
title: '关于调整采购审批流程的通知',
publishDate: '2023-08-20',
type: 'other',
},
{
id: '8',
title: '关于加强信息安全管理的通知',
publishDate: '2023-08-05',
type: 'other',
},
{
id: '9',
title: '关于开展采购人员培训的通知',
publishDate: '2023-07-25',
type: 'other',
},
{
id: '10',
title: '关于优化供应商准入机制的通知',
publishDate: '2023-07-10',
type: 'other',
},
];
const NoticePage: React.FC = () => { const NoticePage: React.FC = () => {
const intl = useIntl();
const [activeMenu, setActiveMenu] = useState<string>('system'); const [activeMenu, setActiveMenu] = useState<string>('system');
const [noticeData, setNoticeData] = useState<any[]>([]); const [noticeData, setNoticeData] = useState<NoticeRecord[]>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
@ -82,33 +22,58 @@ const NoticePage: React.FC = () => {
total: 0, total: 0,
}); });
// 根据当前选中的菜单加载对应的通知数据 // 加载通知数据
useEffect(() => { const loadNoticeData = async (page: number, pageSize: number) => {
setLoading(true); setLoading(true);
// 模拟API请求 try {
setTimeout(() => { const response = await getNoticeList({
const data = activeMenu === 'system' ? mockSystemNotices : mockOtherNotices; pageNo: page.toString(),
pageSize: pageSize.toString()
});
if (response.code === 200 && response.success) {
// 根据当前选中的菜单过滤数据
// 目前没有分类的API暂时都显示后期可以根据不同类型进行过滤
const data = response.data.records;
setNoticeData(data); setNoticeData(data);
setPagination((prevPagination) => ({ setPagination({
...prevPagination, current: response.data.current,
total: data.length, pageSize: response.data.size,
})); total: response.data.total,
});
} else {
message.error(response.message || intl.formatMessage({ id: 'notice.message.getListFailed' }));
}
} catch (error) {
console.error('获取通知列表出错:', error);
message.error(intl.formatMessage({ id: 'notice.message.getListError' }));
} finally {
setLoading(false); setLoading(false);
}, 500); }
}, [activeMenu]); };
// 初始加载和分页变化时加载数据
useEffect(() => {
loadNoticeData(pagination.current, pagination.pageSize);
}, [pagination.current, activeMenu]);
// 处理菜单切换 // 处理菜单切换
const handleMenuClick = (e: any) => { const handleMenuClick = (e: any) => {
setActiveMenu(e.key); setActiveMenu(e.key);
setPagination({ setPagination({
...pagination, ...pagination,
current: 1, current: 1, // 切换分类时重置页码
}); });
}; };
// 处理表格分页变化 // 处理表格分页变化
const handleTableChange = (newPagination: any) => { const handleTableChange = (newPagination: any) => {
setPagination(newPagination); setPagination({
...pagination,
current: newPagination.current,
pageSize: newPagination.pageSize,
});
}; };
// 处理点击通知标题 // 处理点击通知标题
@ -119,7 +84,7 @@ const NoticePage: React.FC = () => {
// 定义表格列 // 定义表格列
const columns = [ const columns = [
{ {
title: '序号', title: intl.formatMessage({ id: 'notice.list.column.index' }),
dataIndex: 'index', dataIndex: 'index',
key: 'index', key: 'index',
width: 80, width: 80,
@ -128,19 +93,19 @@ const NoticePage: React.FC = () => {
}, },
}, },
{ {
title: '标题', title: intl.formatMessage({ id: 'notice.list.column.title' }),
dataIndex: 'title', dataIndex: 'title',
key: 'title', key: 'title',
render: (text: string, record: any) => ( render: (text: string, record: NoticeRecord) => (
<a onClick={() => handleNoticeClick(record.id)} className={styles.noticeTitle}> <a onClick={() => handleNoticeClick(record.id)} className={styles.noticeTitle}>
{text} {text}
</a> </a>
), ),
}, },
{ {
title: '发布时间', title: intl.formatMessage({ id: 'notice.list.column.publishTime' }),
dataIndex: 'publishDate', dataIndex: 'publishTime',
key: 'publishDate', key: 'publishTime',
width: 150, width: 150,
align: 'center' as 'center', align: 'center' as 'center',
}, },
@ -157,10 +122,10 @@ const NoticePage: React.FC = () => {
className={styles.noticeMenu} className={styles.noticeMenu}
> >
<Menu.Item key="system" icon={<AppstoreOutlined />}> <Menu.Item key="system" icon={<AppstoreOutlined />}>
<FormattedMessage id="notice.menu.system" />
</Menu.Item> </Menu.Item>
<Menu.Item key="other" icon={<BellOutlined />}> <Menu.Item key="other" icon={<BellOutlined />}>
<FormattedMessage id="notice.menu.other" />
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Col> </Col>
@ -173,13 +138,19 @@ const NoticePage: React.FC = () => {
rowKey="id" rowKey="id"
pagination={{ pagination={{
...pagination, ...pagination,
showTotal: (total) => `${total} 条记录`, showTotal: (total) => intl.formatMessage(
{ id: 'notice.list.total' },
{ total }
),
showSizeChanger: true, showSizeChanger: true,
showQuickJumper: true, showQuickJumper: true,
}} }}
loading={loading} loading={loading}
onChange={handleTableChange} onChange={handleTableChange}
bordered bordered
locale={{
emptyText: intl.formatMessage({ id: 'notice.list.empty' })
}}
/> />
</div> </div>
</Col> </Col>

View File

@ -1,182 +1,45 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useLocation } from 'umi'; import { useLocation, useIntl, FormattedMessage } from 'umi';
import { Typography, Button, Divider, Spin, message } from 'antd'; import { Typography, Button, Divider, Spin, message } from 'antd';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined } from '@ant-design/icons';
import styles from './noticeInfo.less'; import styles from './noticeInfo.less';
import { getNoticeDetail, NoticeRecord } from '@/servers/api/notice';
const { Title, Text } = Typography; const { Title, Text } = Typography;
interface NoticeDetail { const NoticeInfo: React.FC = () => {
id: string; const intl = useIntl();
title: string; const location = useLocation();
content: string; const id = new URLSearchParams(location.search).get("id");
publishDate: string; const [noticeDetail, setNoticeDetail] = useState<NoticeRecord | null>(null);
publisher: string; const [loading, setLoading] = useState<boolean>(true);
type: string;
// 获取通知详情数据
useEffect(() => {
const fetchNoticeDetail = async () => {
if (!id) {
message.error(intl.formatMessage({ id: 'notice.message.idNotExist' }));
setLoading(false);
return;
} }
interface NoticeDetails { try {
[key: string]: NoticeDetail; const response = await getNoticeDetail(id);
if (response.code === 200 && response.success) {
setNoticeDetail(response.data);
} else {
message.error(response.message || intl.formatMessage({ id: 'notice.message.getDetailFailed' }));
} }
} catch (error) {
// 模拟通知详情数据 console.error('获取通知详情出错:', error);
const mockNoticeDetails: NoticeDetails = { message.error(intl.formatMessage({ id: 'notice.message.getDetailError' }));
'1': { } finally {
id: '1', setLoading(false);
title: '系统将于2023年9月15日进行版本升级',
publishDate: '2023-09-10',
publisher: '系统管理员',
type: 'system',
content: `
<p>尊敬的用户:</p>
<p>为了提供更好的用户体验和系统功能我们计划于2023年9月15日晚上22:00至次日凌晨2:00进行系统版本升级。在此期间系统将暂停服务请您提前做好相关工作安排。</p>
<p><strong>本次升级内容:</strong></p>
<ol>
<li>优化招标采购流程,提高操作效率</li>
<li>增加供应商评价功能,完善评价体系</li>
<li>优化系统响应速度,提升用户体验</li>
<li>修复已知问题,提高系统稳定性</li>
<li>增强系统安全性,保障数据安全</li>
</ol>
<p><strong>升级注意事项:</strong></p>
<ol>
<li>请您在系统升级前保存并提交所有未完成的工作</li>
<li>系统升级后,建议清除浏览器缓存再登录系统</li>
<li>如遇到任何问题,请联系系统管理员</li>
</ol>
<p>感谢您的理解与支持!</p>
<p>系统管理团队</p>
<p>2023年9月10日</p>
`
},
'2': {
id: '2',
title: '招标模块功能优化更新通知',
publishDate: '2023-08-25',
publisher: '系统管理员',
type: 'system',
content: `
<p>尊敬的用户:</p>
<p>我们很高兴地通知您,招标模块已完成功能优化升级,现已上线。本次更新主要针对招标流程进行了优化,提高了操作效率和用户体验。</p>
<p><strong>主要更新内容:</strong></p>
<ol>
<li>简化招标发布流程,减少操作步骤</li>
<li>优化投标文件管理功能,支持批量上传和下载</li>
<li>增加招标项目进度跟踪功能,实时掌握项目状态</li>
<li>完善评标功能,支持多种评标方法</li>
<li>增加中标结果自动生成功能,提高工作效率</li>
</ol>
<p><strong>使用建议:</strong></p>
<ol>
<li>请参考系统内置的操作指南熟悉新功能</li>
<li>如有任何问题或建议,请通过系统反馈功能提交</li>
</ol>
<p>感谢您对我们工作的支持!</p>
<p>系统管理团队</p>
<p>2023年8月25日</p>
`
},
'6': {
id: '6',
title: '关于开展2023年度供应商评估工作的通知',
publishDate: '2023-09-05',
publisher: '采购管理部',
type: 'other',
content: `
<p>各相关部门:</p>
<p>为加强供应商管理提高采购质量根据《中远海运集团供应商管理实施细则》的要求现决定开展2023年度供应商评估工作具体事项通知如下</p>
<p><strong>一、评估对象</strong></p>
<p>2023年度与我集团有业务往来的所有供应商。</p>
<p><strong>二、评估时间</strong></p>
<p>2023年9月15日至10月15日。</p>
<p><strong>三、评估内容</strong></p>
<ol>
<li>供应商资质情况</li>
<li>产品质量及服务水平</li>
<li>价格合理性</li>
<li>交货及时性</li>
<li>售后服务响应速度</li>
<li>合同履约情况</li>
<li>社会责任履行情况</li>
</ol>
<p><strong>四、评估方法</strong></p>
<p>采用百分制评分根据评分结果将供应商分为A、B、C、D四个等级。</p>
<p><strong>五、工作要求</strong></p>
<ol>
<li>各部门应指定专人负责本部门供应商评估工作</li>
<li>评估过程应客观公正,实事求是</li>
<li>评估结果应于10月20日前报送采购管理部汇总</li>
</ol>
<p>特此通知。</p>
<p>采购管理部</p>
<p>2023年9月5日</p>
`
},
'7': {
id: '7',
title: '关于调整采购审批流程的通知',
publishDate: '2023-08-20',
publisher: '采购管理部',
type: 'other',
content: `
<p>各相关部门:</p>
<p>为提高采购效率,优化业务流程,经公司研究决定,对采购审批流程进行调整,现将有关事项通知如下:</p>
<p><strong>一、调整内容</strong></p>
<ol>
<li>采购金额50万元以下的项目由部门经理审批后直接提交采购部执行无需总经理审批</li>
<li>采购金额50万元至200万元的项目由部门经理和分管副总经理审批后提交采购部执行</li>
<li>采购金额200万元以上的项目仍按原流程执行需经总经理审批</li>
</ol>
<p><strong>二、实施时间</strong></p>
<p>本通知自2023年9月1日起实施。</p>
<p><strong>三、注意事项</strong></p>
<ol>
<li>各部门应严格按照新的审批流程执行,不得擅自变更</li>
<li>采购部应加强对采购项目的监督管理,确保采购质量</li>
<li>审批人应认真履行审批职责,对采购项目的必要性、合理性进行审核</li>
</ol>
<p>特此通知。</p>
<p>采购管理部</p>
<p>2023年8月20日</p>
`
} }
}; };
const NoticeInfo: React.FC = () => { fetchNoticeDetail();
const location = useLocation(); }, [id, intl]);
const id = new URLSearchParams(location.search).get("id");
const [noticeDetail, setNoticeDetail] = useState<NoticeDetail | null>(null);
const [loading, setLoading] = useState<boolean>(true);
// 模拟获取通知详情数据
useEffect(() => {
// 实际项目中应该通过API获取数据
setTimeout(() => {
if (id && mockNoticeDetails[id]) {
setNoticeDetail(mockNoticeDetails[id]);
} else {
// 处理ID不存在的情况
message.error('通知不存在');
}
setLoading(false);
}, 500);
}, [id]);
// 处理返回列表 // 处理返回列表
const handleBack = () => { const handleBack = () => {
@ -186,7 +49,7 @@ const NoticeInfo: React.FC = () => {
if (loading) { if (loading) {
return ( return (
<div className={styles.loadingContainer}> <div className={styles.loadingContainer}>
<Spin size="large" /> <Spin size="large" tip={intl.formatMessage({ id: 'notice.detail.loading' })} />
</div> </div>
); );
} }
@ -194,12 +57,27 @@ const NoticeInfo: React.FC = () => {
if (!noticeDetail) { if (!noticeDetail) {
return ( return (
<div className={styles.notFoundContainer}> <div className={styles.notFoundContainer}>
<Title level={4}></Title> <Title level={4}><FormattedMessage id="notice.detail.notFound" /></Title>
<Button type="primary" onClick={handleBack}></Button> <Button type="primary" onClick={handleBack}>
<FormattedMessage id="notice.detail.back" />
</Button>
</div> </div>
); );
} }
// 获取通知类型本地化文本
const getNoticeTypeText = (columnType: string) => {
if (!columnType) return intl.formatMessage({ id: 'notice.detail.type.notice' });
if (columnType === 'system') {
return intl.formatMessage({ id: 'notice.detail.type.system' });
} else if (columnType === 'other') {
return intl.formatMessage({ id: 'notice.detail.type.other' });
}
return intl.formatMessage({ id: 'notice.detail.type.notice' });
};
return ( return (
<div className={styles.noticeInfoContainer}> <div className={styles.noticeInfoContainer}>
<div className={styles.noticeInfoHeader}> <div className={styles.noticeInfoHeader}>
@ -209,7 +87,7 @@ const NoticeInfo: React.FC = () => {
onClick={handleBack} onClick={handleBack}
className={styles.backButton} className={styles.backButton}
> >
<FormattedMessage id="notice.detail.back" />
</Button> </Button>
</div> </div>
@ -222,14 +100,18 @@ const NoticeInfo: React.FC = () => {
<div className={styles.metaInfo}> <div className={styles.metaInfo}>
<div className={styles.metaLeft}> <div className={styles.metaLeft}>
<Text type="secondary">: {noticeDetail.publishDate}</Text> <Text type="secondary">
<FormattedMessage id="notice.detail.publishTime" />: {noticeDetail.publishTime}
</Text>
</div> </div>
<div className={styles.metaCenter}> <div className={styles.metaCenter}>
<Text type="secondary">: {noticeDetail.publisher}</Text> <Text type="secondary">
<FormattedMessage id="notice.detail.publisher" />: {noticeDetail.publishBy}
</Text>
</div> </div>
<div className={styles.metaRight}> <div className={styles.metaRight}>
<Text type="secondary"> <Text type="secondary">
: {noticeDetail.type === 'system' ? '系统更新通知' : '其他通知'} <FormattedMessage id="notice.detail.type" />: {getNoticeTypeText(noticeDetail.columnType)}
</Text> </Text>
</div> </div>
</div> </div>

103
src/servers/api/download.ts Normal file
View File

@ -0,0 +1,103 @@
import request from '@/utils/request';
// 下载中心列表请求参数类型
export type DownloadListParams = {
pageNo: string;
pageSize: string;
};
// 下载项记录类型
export type DownloadRecord = {
pageNo: null;
pageSize: null;
createBy: string;
createTime: string;
updateBy: string;
updateTime: string;
remark: null;
lastUpdateTime: string;
id: string;
columnType: string;
name: string;
isBold: string;
keywords: string;
thumbnail: string;
fileName: string;
fileType: string;
fileSize: string;
filePath: string;
fileUrl: string;
isTop: string;
status: string;
publishBy: string;
publishTime: string;
delFlag: string;
statusText: string;
};
// 下载中心列表响应类型
export type DownloadListResponse = {
records: DownloadRecord[];
total: number;
size: number;
current: number;
orders: string[];
optimizeCountSql: boolean;
hitCount: boolean;
countId: null;
maxLimit: null;
searchCount: boolean;
pages: number;
};
// 下载中心详情请求参数类型
export type DownloadDetailParams = {
id: string;
};
/**
* 获取下载中心列表
* @param params 分页参数
* @returns 下载中心列表数据
*/
export async function getDownloadList(params: DownloadListParams) {
return request<API.APIResponse<DownloadListResponse>>('/homePage/getDownloadsPage', {
method: 'POST',
data: params,
});
}
/**
* 获取下载中心详情
* @param id 下载项ID
* @returns 下载中心详情数据
*/
export async function getDownloadDetail(id: string) {
return request<API.APIResponse<DownloadRecord>>(`/homePage/getDownloadsInfo/${id}`, {
method: 'GET',
});
}
/**
* 下载文件
* @param id 文件ID
* @param fileName 文件名称
* @returns 文件下载结果
*/
export async function downloadFile(id: string, fileName: string) {
const response = await request(`/homePage/downloadFile/${id}`, {
method: 'GET',
responseType: 'blob',
});
// 创建下载链接
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return response;
}

8
src/servers/api/index.ts Normal file
View File

@ -0,0 +1,8 @@
// 首页api
import request from '@/utils/request';
export async function getCoscoPortalsLinksClassification() {
return request<API.APIResponse<API.HelpCenterListResponse>>('/homePage/coscoPortalsLinksClassification/getAll', {
method: 'get',
});
}

75
src/servers/api/notice.ts Normal file
View File

@ -0,0 +1,75 @@
import request from '@/utils/request';
// 通知中心列表请求参数类型
export type NoticeListParams = {
pageNo: string;
pageSize: string;
};
// 通知中心记录类型
export type NoticeRecord = {
pageNo: null;
pageSize: null;
createBy: string;
createTime: string;
updateBy: string;
updateTime: string;
remark: null;
lastUpdateTime: string;
id: string;
title: string;
titleEn: string;
content: string;
contentEn: string;
settingEn: string;
columnType: string;
publishBy: string;
publishTime: string;
isTop: string;
status: string;
delFlag: string;
statusText: string;
};
// 通知中心列表响应类型
export type NoticeListResponse = {
records: NoticeRecord[];
total: number;
size: number;
current: number;
orders: string[];
optimizeCountSql: boolean;
hitCount: boolean;
countId: null;
maxLimit: null;
searchCount: boolean;
pages: number;
};
// 通知中心详情请求参数类型
export type NoticeDetailParams = {
id: string;
};
/**
* 获取通知中心列表
* @param params 分页参数
* @returns 通知中心列表数据
*/
export async function getNoticeList(params: NoticeListParams) {
return request<API.APIResponse<NoticeListResponse>>('/homePage/getNoticePage', {
method: 'POST',
data: params,
});
}
/**
* 获取通知中心详情
* @param id 通知ID
* @returns 通知中心详情数据
*/
export async function getNoticeDetail(id: string) {
return request<API.APIResponse<NoticeRecord>>(`/homePage/getNoticeInfo/${id}`, {
method: 'GET',
});
}

View File

@ -6,6 +6,98 @@ declare namespace API {
data: T; data: T;
} }
// 下载中心相关类型
export type DownloadListParams = {
pageNo: string;
pageSize: string;
}
export type DownloadRecord = {
pageNo: null;
pageSize: null;
createBy: string;
createTime: string;
updateBy: string;
updateTime: string;
remark: null;
lastUpdateTime: string;
id: string;
columnType: string;
name: string;
isBold: string;
keywords: string;
thumbnail: string;
fileName: string;
fileType: string;
fileSize: string;
filePath: string;
fileUrl: string;
isTop: string;
status: string;
publishBy: string;
publishTime: string;
delFlag: string;
statusText: string;
}
export type DownloadListResponse = {
records: DownloadRecord[];
total: number;
size: number;
current: number;
orders: string[];
optimizeCountSql: boolean;
hitCount: boolean;
countId: null;
maxLimit: null;
searchCount: boolean;
pages: number;
}
// 通知中心相关类型
export type NoticeListParams = {
pageNo: string;
pageSize: string;
}
export type NoticeRecord = {
pageNo: null;
pageSize: null;
createBy: string;
createTime: string;
updateBy: string;
updateTime: string;
remark: null;
lastUpdateTime: string;
id: string;
title: string;
titleEn: string;
content: string;
contentEn: string;
settingEn: string;
columnType: string;
publishBy: string;
publishTime: string;
isTop: string;
status: string;
delFlag: string;
statusText: string;
}
export type NoticeListResponse = {
records: NoticeRecord[];
total: number;
size: number;
current: number;
orders: string[];
optimizeCountSql: boolean;
hitCount: boolean;
countId: null;
maxLimit: null;
searchCount: boolean;
pages: number;
}
export type RegisterRequest = { export type RegisterRequest = {
coscoSupplierBank: CoscoSupplierBank[]; coscoSupplierBank: CoscoSupplierBank[];
coscoSupplierBase: CoscoSupplierBase; coscoSupplierBase: CoscoSupplierBase;
@ -218,7 +310,6 @@ declare namespace API {
title: string; title: string;
type: string; type: string;
userName: string; userName: string;
[property: string]: any;
} }
// 关于我们响应数据类型 // 关于我们响应数据类型