公告公示及详情页面
This commit is contained in:
@ -1,7 +0,0 @@
|
||||
@import '../../baseStyle.less';
|
||||
.spaceBlock {
|
||||
height: 10px;
|
||||
margin: 0 -15px;
|
||||
margin-top: 20px;
|
||||
background: rgba(@gray, 0.3);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
import './SpaceBlock.less';
|
||||
const SpaceBlock: React.FC = () => {
|
||||
return <div className="spaceBlock" />;
|
||||
};
|
||||
|
||||
export default SpaceBlock;
|
@ -15,8 +15,8 @@ body,
|
||||
.layout-content-main {
|
||||
width: @width;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
padding: 15px;
|
||||
// background: #fff;
|
||||
// padding: 15px;
|
||||
}
|
||||
}
|
||||
.colorWeak {
|
||||
|
207
src/pages/announce/announce.less
Normal file
207
src/pages/announce/announce.less
Normal file
@ -0,0 +1,207 @@
|
||||
.announcePage {
|
||||
// padding: 20px;
|
||||
// background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.searchSection {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
// border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
// box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.searchRow {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.searchLabel {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-right: 20px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.filterRow {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.filterLabel {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-right: 20px;
|
||||
min-width: 70px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.filterOptions {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.locationSelector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.provinceSelector {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
|
||||
.provinceRow {
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* 覆盖antd样式 */
|
||||
:global {
|
||||
.ant-radio-button-wrapper {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 0;
|
||||
height: 32px;
|
||||
line-height: 30px;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper:first-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper:last-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper-checked {
|
||||
background-color: rgb(0, 79, 142);
|
||||
border-color: rgb(0, 79, 142);
|
||||
color: #fff;
|
||||
&:hover {
|
||||
background-color: rgb(0, 79, 142);
|
||||
border-color: rgb(0, 79, 142);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn-primary {
|
||||
background-color: rgb(0, 79, 142);
|
||||
border-color: rgb(0, 79, 142);
|
||||
border-radius: 0;
|
||||
&:hover, &:focus {
|
||||
background-color: rgba(0, 79, 142, 0.8);
|
||||
border-color: rgba(0, 79, 142, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-selector {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.ant-input {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ant-input-affix-wrapper {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ant-list-pagination {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ant-pagination-item {
|
||||
border-radius: 0;
|
||||
|
||||
&-active {
|
||||
border-color: rgb(0, 79, 142);
|
||||
|
||||
a {
|
||||
color: rgb(0, 79, 142);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-pagination-item-link {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ant-pagination-options {
|
||||
.ant-select-selector {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tag {
|
||||
margin-right: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.announceList {
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.listItem {
|
||||
padding: 16px 10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
.itemContent {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.itemHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.typeTag {
|
||||
margin-right: 10px;
|
||||
min-width: 90px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.itemTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
|
||||
&:hover {
|
||||
color: rgb(0, 79, 142);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.itemFooter {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.company {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.date {
|
||||
min-width: 180px;
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -1,5 +1,369 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Input, Button, Select, Radio, DatePicker, List, Tag } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { history } from 'umi';
|
||||
import styles from './announce.less';
|
||||
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 模拟公告数据
|
||||
const mockAnnounceData = [
|
||||
{
|
||||
id: '1',
|
||||
type: '招标采购公告',
|
||||
title: '中远海运集团2023年度办公设备采购项目招标公告',
|
||||
company: '中远海运集团有限公司',
|
||||
publishDate: '2023-06-15',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: '中标结果公示',
|
||||
title: '中远海运物流有限公司信息系统建设项目中标结果公示',
|
||||
company: '中远海运物流有限公司',
|
||||
publishDate: '2023-06-10',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: '采购需求公示',
|
||||
title: '中远海运集装箱运输有限公司集装箱采购需求公示',
|
||||
company: '中远海运集装箱运输有限公司',
|
||||
publishDate: '2023-06-08',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: '变更公告',
|
||||
title: '关于"中远海运能源运输股份有限公司船舶维修项目"的变更公告',
|
||||
company: '中远海运能源运输股份有限公司',
|
||||
publishDate: '2023-06-05',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
type: '招标采购公告',
|
||||
title: '中远海运重工有限公司船舶设备采购项目招标公告',
|
||||
company: '中远海运重工有限公司',
|
||||
publishDate: '2023-06-01',
|
||||
},
|
||||
];
|
||||
|
||||
const AnnouncePage: React.FC = () => {
|
||||
return <>announcePage</>;
|
||||
// 状态管理
|
||||
const [searchKeyword, setSearchKeyword] = useState<string>('');
|
||||
const [announceType, setAnnounceType] = useState<string>('全部');
|
||||
const [projectUnit, setProjectUnit] = useState<string>('全选');
|
||||
const [projectLocation, setProjectLocation] = useState<string>('中国');
|
||||
const [publishTime, setPublishTime] = useState<string>('不限');
|
||||
const [projectType, setProjectType] = useState<string>('全部');
|
||||
const [currentProvince, setCurrentProvince] = useState<string>('全部');
|
||||
|
||||
// 分页状态
|
||||
const [current, setCurrent] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [total, setTotal] = useState(100);
|
||||
|
||||
// 公告类型选项
|
||||
const announceTypeOptions = [
|
||||
{ label: '全部', value: '全部' },
|
||||
{ label: '采购需求公示', value: '采购需求公示' },
|
||||
{ label: '招标采购公告', value: '招标采购公告' },
|
||||
{ label: '非招标采购公告', value: '非招标采购公告' },
|
||||
{ label: '资格预审公告', value: '资格预审公告' },
|
||||
{ label: '招募公告', value: '招募公告' },
|
||||
{ label: '变更公告', value: '变更公告' },
|
||||
{ label: '中标(中选)候选人公示', value: '中标(中选)候选人公示' },
|
||||
{ label: '中标(中选)结果公示', value: '中标(中选)结果公示' },
|
||||
{ label: '采购失败(流标)公告', value: '采购失败(流标)公告' },
|
||||
];
|
||||
|
||||
// 省份选项 - 按照图片中的顺序排列
|
||||
const provinceOptions = [
|
||||
{ label: '全部', value: '全部' },
|
||||
{ label: '北京', value: '北京' },
|
||||
{ label: '天津', value: '天津' },
|
||||
{ label: '河北', value: '河北' },
|
||||
{ label: '山西', value: '山西' },
|
||||
{ label: '内蒙古', value: '内蒙古' },
|
||||
{ label: '辽宁', value: '辽宁' },
|
||||
{ label: '吉林', value: '吉林' },
|
||||
{ label: '黑龙江', value: '黑龙江' },
|
||||
{ label: '上海', value: '上海' },
|
||||
{ label: '江苏', value: '江苏' },
|
||||
{ label: '浙江', value: '浙江' },
|
||||
{ label: '安徽', value: '安徽' },
|
||||
{ label: '福建', value: '福建' },
|
||||
{ label: '江西', value: '江西' },
|
||||
{ label: '山东', value: '山东' },
|
||||
{ label: '河南', value: '河南' },
|
||||
{ label: '湖北', value: '湖北' },
|
||||
{ label: '湖南', value: '湖南' },
|
||||
{ label: '广东', value: '广东' },
|
||||
{ label: '广西', value: '广西' },
|
||||
{ label: '海南', value: '海南' },
|
||||
{ label: '重庆', value: '重庆' },
|
||||
{ label: '四川', value: '四川' },
|
||||
{ label: '贵州', value: '贵州' },
|
||||
{ label: '云南', value: '云南' },
|
||||
{ label: '西藏', value: '西藏' },
|
||||
{ label: '陕西', value: '陕西' },
|
||||
{ label: '甘肃', value: '甘肃' },
|
||||
{ label: '青海', value: '青海' },
|
||||
{ label: '宁夏', value: '宁夏' },
|
||||
{ label: '新疆', value: '新疆' },
|
||||
{ label: '香港', value: '香港' },
|
||||
{ label: '澳门', value: '澳门' },
|
||||
{ label: '台湾', value: '台湾' },
|
||||
];
|
||||
|
||||
// 第一行省份
|
||||
const firstRowProvinces = ['全部', '北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江', '上海', '江苏', '浙江', '安徽', '福建', '江西'];
|
||||
// 第二行省份
|
||||
const secondRowProvinces = ['山东', '河南', '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州', '云南', '西藏', '陕西'];
|
||||
// 第三行省份
|
||||
const thirdRowProvinces = ['甘肃', '青海', '宁夏', '新疆', '香港', '澳门', '台湾'];
|
||||
|
||||
// 发布时间选项
|
||||
const publishTimeOptions = [
|
||||
{ label: '不限', value: '不限' },
|
||||
{ label: '今天', value: '今天' },
|
||||
{ label: '最近三天', value: '最近三天' },
|
||||
{ label: '最近一周', value: '最近一周' },
|
||||
{ label: '最近一月', value: '最近一月' },
|
||||
{ label: '自定义', value: '自定义' },
|
||||
];
|
||||
|
||||
// 项目类型选项
|
||||
const projectTypeOptions = [
|
||||
{ label: '全部', value: '全部' },
|
||||
{ label: '货物', value: '货物' },
|
||||
{ label: '工程', value: '工程' },
|
||||
{ label: '服务', value: '服务' },
|
||||
{ label: '其他', value: '其他' },
|
||||
];
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
console.log('搜索条件:', {
|
||||
searchKeyword,
|
||||
announceType,
|
||||
projectUnit,
|
||||
projectLocation,
|
||||
publishTime,
|
||||
projectType,
|
||||
});
|
||||
// 这里添加搜索逻辑
|
||||
};
|
||||
|
||||
// 处理分页变化
|
||||
const handlePageChange = (page: number, pageSizeValue?: number) => {
|
||||
setCurrent(page);
|
||||
if (pageSizeValue) setPageSize(pageSizeValue);
|
||||
};
|
||||
|
||||
// 获取公告类型对应的颜色
|
||||
const getTypeColor = (type: string) => {
|
||||
switch (type) {
|
||||
case '招标采购公告':
|
||||
return 'blue';
|
||||
case '中标结果公示':
|
||||
return 'green';
|
||||
case '采购需求公示':
|
||||
return 'orange';
|
||||
case '变更公告':
|
||||
return 'red';
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
};
|
||||
|
||||
// 处理点击公告标题
|
||||
const handleAnnounceClick = (id: string) => {
|
||||
console.log('点击了公告ID:', id);
|
||||
history.push({
|
||||
pathname: '/announce/announceInfo',
|
||||
query: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.announcePage}>
|
||||
<div className={styles.searchSection}>
|
||||
{/* 公告搜索 */}
|
||||
<div className={styles.searchRow}>
|
||||
<div className={styles.searchLabel}>公告搜索</div>
|
||||
<Input
|
||||
placeholder="请输入公告关键词"
|
||||
value={searchKeyword}
|
||||
onChange={(e) => setSearchKeyword(e.target.value)}
|
||||
style={{ width: '80%', maxWidth: 1000 }}
|
||||
suffix={
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
|
||||
搜 索
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 公告类型 */}
|
||||
<div className={styles.filterRow}>
|
||||
<div className={styles.filterLabel}>公告类型</div>
|
||||
<div className={styles.filterOptions}>
|
||||
<Radio.Group
|
||||
value={announceType}
|
||||
onChange={(e) => setAnnounceType(e.target.value)}
|
||||
buttonStyle="solid"
|
||||
>
|
||||
{announceTypeOptions.map((option) => (
|
||||
<Radio.Button key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 项目所属单位 */}
|
||||
<div className={styles.filterRow}>
|
||||
<div className={styles.filterLabel}>项目所属单位</div>
|
||||
<Select
|
||||
style={{ width: 300 }}
|
||||
value={projectUnit}
|
||||
onChange={(value) => setProjectUnit(value)}
|
||||
>
|
||||
<Option value="全选">全选</Option>
|
||||
<Option value="中远海运集团">中远海运集团</Option>
|
||||
<Option value="中远海运物流">中远海运物流</Option>
|
||||
<Option value="中远海运集装箱">中远海运集装箱</Option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 项目所在地 */}
|
||||
<div className={styles.filterRow}>
|
||||
<div className={styles.filterLabel}>项目所在地</div>
|
||||
<div className={styles.locationSelector}>
|
||||
<Select
|
||||
style={{ width: 150, marginRight: 10 }}
|
||||
value={projectLocation}
|
||||
onChange={(value) => setProjectLocation(value)}
|
||||
>
|
||||
<Option value="中国">中国</Option>
|
||||
<Option value="国外">国外</Option>
|
||||
</Select>
|
||||
|
||||
<div className={styles.provinceSelector}>
|
||||
<Radio.Group
|
||||
value={currentProvince}
|
||||
onChange={(e) => setCurrentProvince(e.target.value)}
|
||||
buttonStyle="solid"
|
||||
>
|
||||
<div className={styles.provinceRow}>
|
||||
{firstRowProvinces.map((province) => (
|
||||
<Radio.Button key={province} value={province}>
|
||||
{province}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.provinceRow}>
|
||||
{secondRowProvinces.map((province) => (
|
||||
<Radio.Button key={province} value={province}>
|
||||
{province}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.provinceRow}>
|
||||
{thirdRowProvinces.map((province) => (
|
||||
<Radio.Button key={province} value={province}>
|
||||
{province}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</div>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 发布时间 */}
|
||||
<div className={styles.filterRow}>
|
||||
<div className={styles.filterLabel}>发布时间</div>
|
||||
<div className={styles.filterOptions}>
|
||||
<Radio.Group
|
||||
value={publishTime}
|
||||
onChange={(e) => setPublishTime(e.target.value)}
|
||||
buttonStyle="solid"
|
||||
>
|
||||
{publishTimeOptions.map((option) => (
|
||||
<Radio.Button key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
{publishTime === '自定义' && (
|
||||
<RangePicker style={{ marginLeft: 10 }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 项目类型 */}
|
||||
<div className={styles.filterRow}>
|
||||
<div className={styles.filterLabel}>项目类型</div>
|
||||
<div className={styles.filterOptions}>
|
||||
<Radio.Group
|
||||
value={projectType}
|
||||
onChange={(e) => setProjectType(e.target.value)}
|
||||
buttonStyle="solid"
|
||||
>
|
||||
{projectTypeOptions.map((option) => (
|
||||
<Radio.Button key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 公告列表部分 */}
|
||||
<div className={styles.announceList}>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={mockAnnounceData}
|
||||
pagination={{
|
||||
onChange: handlePageChange,
|
||||
current,
|
||||
pageSize,
|
||||
total,
|
||||
showTotal: (totalItems) => `共 ${totalItems} 条记录`,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
key={item.id}
|
||||
className={styles.listItem}
|
||||
>
|
||||
<div className={styles.itemContent}>
|
||||
<div className={styles.itemHeader}>
|
||||
<Tag color={getTypeColor(item.type)} className={styles.typeTag}>
|
||||
{item.type}
|
||||
</Tag>
|
||||
<div
|
||||
className={styles.itemTitle}
|
||||
onClick={() => handleAnnounceClick(item.id)}
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.itemFooter}>
|
||||
<div className={styles.company}>招标人:{item.company}</div>
|
||||
<div className={styles.date}>发布日期:{item.publishDate}</div>
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnnouncePage;
|
||||
|
144
src/pages/announce/announceInfo.less
Normal file
144
src/pages/announce/announceInfo.less
Normal file
@ -0,0 +1,144 @@
|
||||
.announceInfoContainer {
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.loadingContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.announceInfoHeader {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.backButton {
|
||||
padding-left: 0;
|
||||
font-size: 16px;
|
||||
color: rgb(0, 79, 142);
|
||||
|
||||
&:hover, &:focus {
|
||||
color: rgba(0, 79, 142, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.announceInfoContent {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold !important;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.metaInfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
|
||||
> div {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metaLeft {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.metaCenter {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.metaRight {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 20px 0 !important;
|
||||
border-top-color: #e8e8e8;
|
||||
}
|
||||
|
||||
.contentBody {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
|
||||
p {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.attachmentsSection {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.attachmentsTitle {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.attachmentsList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.attachmentButton {
|
||||
margin-right: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
:global {
|
||||
.ant-btn-primary {
|
||||
background-color: rgb(0, 79, 142);
|
||||
border-color: rgb(0, 79, 142);
|
||||
border-radius: 0;
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: rgba(0, 79, 142, 0.8);
|
||||
border-color: rgba(0, 79, 142, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn-primary[disabled] {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-color: #d9d9d9;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.ant-btn-background-ghost.ant-btn-primary {
|
||||
color: rgb(0, 79, 142);
|
||||
border-color: rgb(0, 79, 142);
|
||||
border-radius: 0;
|
||||
|
||||
&:hover, &:focus {
|
||||
color: rgba(0, 79, 142, 0.8);
|
||||
border-color: rgba(0, 79, 142, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
border-top-color: #e8e8e8;
|
||||
}
|
||||
}
|
@ -1,8 +1,186 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'umi';
|
||||
import { Typography, Button, Space, Divider, Row, Col, Spin, message } from 'antd';
|
||||
import { DownloadOutlined, ArrowLeftOutlined, FilePdfOutlined, FileWordOutlined, FileExcelOutlined } from '@ant-design/icons';
|
||||
import styles from './announceInfo.less';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
// 模拟公告详情数据
|
||||
const mockAnnounceDetail = {
|
||||
id: '1',
|
||||
title: '中远海运集团2023年度办公设备采购项目招标公告',
|
||||
type: '招标采购公告',
|
||||
publishDate: '2023-06-15',
|
||||
publisher: '中远海运集团采购中心',
|
||||
content: `
|
||||
<p>一、项目基本情况</p>
|
||||
<p>1. 项目名称:中远海运集团2023年度办公设备采购项目</p>
|
||||
<p>2. 采购需求:台式电脑300台、笔记本电脑150台、打印机50台、投影仪10台等办公设备</p>
|
||||
<p>3. 项目预算:人民币800万元</p>
|
||||
<p>4. 最高限价:人民币800万元</p>
|
||||
<p>5. 采购方式:公开招标</p>
|
||||
<p>6. 交付地点:中远海运集团总部及各分支机构</p>
|
||||
<p>7. 交付期限:合同签订后45个工作日内</p>
|
||||
<p>8. 本项目是否接受联合体投标:否</p>
|
||||
|
||||
<p>二、申请人的资格要求</p>
|
||||
<p>1. 满足《中华人民共和国政府采购法》第二十二条规定;</p>
|
||||
<p>2. 落实政府采购政策需满足的资格要求:本项目支持小微企业,监狱企业,残疾人福利性单位发展等政府采购政策。</p>
|
||||
<p>3. 本项目的特定资格要求:</p>
|
||||
<p>(1)投标人须具有电子产品销售相关资质;</p>
|
||||
<p>(2)投标人须提供所投产品的厂家授权;</p>
|
||||
<p>(3)投标人须具有近三年同类项目业绩不少于3个。</p>
|
||||
|
||||
<p>三、获取招标文件</p>
|
||||
<p>1. 时间:2023年6月15日至2023年6月22日,每天9:00至17:00(北京时间,法定节假日除外)</p>
|
||||
<p>2. 地点:中远海运集团采购中心官网</p>
|
||||
<p>3. 方式:网上下载</p>
|
||||
<p>4. 售价:免费</p>
|
||||
|
||||
<p>四、提交投标文件截止时间、开标时间和地点</p>
|
||||
<p>1. 提交投标文件截止时间:2023年7月6日14:30(北京时间)</p>
|
||||
<p>2. 开标时间:2023年7月6日15:00(北京时间)</p>
|
||||
<p>3. 地点:中远海运集团总部1号会议室</p>
|
||||
|
||||
<p>五、公告期限</p>
|
||||
<p>自本公告发布之日起5个工作日。</p>
|
||||
|
||||
<p>六、其他补充事宜</p>
|
||||
<p>1. 本项目需要落实的政府采购政策:节能环保、中小微企业扶持、促进残疾人就业等。</p>
|
||||
<p>2. 投标保证金:人民币10万元。</p>
|
||||
|
||||
<p>七、对本次招标提出询问,请按以下方式联系</p>
|
||||
<p>1. 采购人信息</p>
|
||||
<p>名称:中远海运集团有限公司</p>
|
||||
<p>地址:上海市东大名路xxx号</p>
|
||||
<p>联系方式:021-xxxxxxxx</p>
|
||||
<p>2. 采购代理机构信息</p>
|
||||
<p>名称:上海xxx招标代理有限公司</p>
|
||||
<p>地址:上海市浦东新区xxx路xx号</p>
|
||||
<p>联系方式:021-xxxxxxxx</p>
|
||||
<p>3. 项目联系方式</p>
|
||||
<p>项目联系人:张先生</p>
|
||||
<p>电话:021-xxxxxxxx</p>
|
||||
`,
|
||||
attachments: [
|
||||
{ name: '招标文件.pdf', url: '/files/招标文件.pdf', type: 'pdf' },
|
||||
{ name: '投标须知.docx', url: '/files/投标须知.docx', type: 'word' },
|
||||
{ name: '技术需求说明.xlsx', url: '/files/技术需求说明.xlsx', type: 'excel' }
|
||||
]
|
||||
};
|
||||
|
||||
const AnnounceInfo: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const id = new URLSearchParams(location.search).get("id")
|
||||
return <>AnnounceInfo -------- {id}</>;
|
||||
const id = new URLSearchParams(location.search).get("id");
|
||||
const [announceDetail, setAnnounceDetail] = useState<any>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
// 模拟获取公告详情数据
|
||||
useEffect(() => {
|
||||
// 实际项目中应该通过API获取数据
|
||||
setTimeout(() => {
|
||||
setAnnounceDetail(mockAnnounceDetail);
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
}, [id]);
|
||||
|
||||
// 处理返回列表
|
||||
const handleBack = () => {
|
||||
window.history.back();
|
||||
};
|
||||
|
||||
// 处理下载附件
|
||||
const handleDownload = (url: string, name: string) => {
|
||||
message.success(`开始下载: ${name}`);
|
||||
console.log('下载附件:', url, name);
|
||||
// 实际项目中应该调用下载API
|
||||
// window.open(url);
|
||||
};
|
||||
|
||||
// 根据文件类型获取图标
|
||||
const getFileIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case 'pdf':
|
||||
return <FilePdfOutlined />;
|
||||
case 'word':
|
||||
return <FileWordOutlined />;
|
||||
case 'excel':
|
||||
return <FileExcelOutlined />;
|
||||
default:
|
||||
return <DownloadOutlined />;
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className={styles.loadingContainer}>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.announceInfoContainer}>
|
||||
<div className={styles.announceInfoHeader}>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={handleBack}
|
||||
className={styles.backButton}
|
||||
>
|
||||
返回列表
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className={styles.announceInfoContent}>
|
||||
<div className={styles.titleContainer}>
|
||||
<Title level={2} className={styles.title}>
|
||||
{announceDetail.title}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
<div className={styles.metaInfo}>
|
||||
<div className={styles.metaLeft}>
|
||||
<Text type="secondary">发布时间: {announceDetail.publishDate}</Text>
|
||||
</div>
|
||||
<div className={styles.metaCenter}>
|
||||
<Text type="secondary">发布人: {announceDetail.publisher}</Text>
|
||||
</div>
|
||||
<div className={styles.metaRight}>
|
||||
<Text type="secondary">公告类型: {announceDetail.type}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider className={styles.divider} />
|
||||
|
||||
<div className={styles.contentBody}>
|
||||
<div dangerouslySetInnerHTML={{ __html: announceDetail.content }} />
|
||||
</div>
|
||||
|
||||
{announceDetail.attachments.length > 0 && (
|
||||
<div className={styles.attachmentsSection}>
|
||||
<Divider className={styles.divider} />
|
||||
<div className={styles.attachmentsTitle}>附件下载</div>
|
||||
<div className={styles.attachmentsList}>
|
||||
{announceDetail.attachments.map((attachment: any, index: number) => (
|
||||
<Button
|
||||
key={index}
|
||||
type="primary"
|
||||
ghost
|
||||
icon={getFileIcon(attachment.type)}
|
||||
onClick={() => handleDownload(attachment.url, attachment.name)}
|
||||
className={styles.attachmentButton}
|
||||
>
|
||||
{attachment.name}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnnounceInfo;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card, Row, Col, Tabs, Table } from 'antd';
|
||||
import { useIntl, Link } from 'umi';
|
||||
import SpaceBlock from '@/components/SpaceBlock/SpaceBlock';
|
||||
import './index.less';
|
||||
import IconFont from '@/components/IconFont/IconFont';
|
||||
import LinkComponent from './Link';
|
||||
@ -181,7 +180,6 @@ const IndexPage: React.FC = () => {
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<SpaceBlock />
|
||||
<Tabs onChange={tabChange}>
|
||||
{tabList.map((item) => (
|
||||
<Tabs.TabPane tab={item.label} key={item.key} />
|
||||
@ -200,7 +198,7 @@ const IndexPage: React.FC = () => {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Row style={{ marginTop: '20px' }}>
|
||||
<Row style={{ marginTop: '20px', backgroundColor: '#fff' }}>
|
||||
<Col span={12}>
|
||||
<div className="blockTitle">紧急问题咨询</div>
|
||||
<img src="" alt="" />
|
||||
@ -235,7 +233,6 @@ const IndexPage: React.FC = () => {
|
||||
<p>客服1: 400-300-9989</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<SpaceBlock />
|
||||
|
||||
<div>
|
||||
<LinkComponent />
|
||||
|
Reference in New Issue
Block a user