main
wangyunfei888 1 month ago
parent adcc67002e
commit ddeafd1288

@ -89,7 +89,7 @@ const TemplateCreate = () => {
<div className={styles.rightPanel}> <div className={styles.rightPanel}>
<div className={styles.rightHeader}> <div className={styles.rightHeader}>
<span>样式</span> <span>样式</span>
<Button type="primary">生成报表</Button> <Button type="primary" className={styles.reportBtn}>生成报表</Button>
</div> </div>
<div className={styles.formSection}> <div className={styles.formSection}>

@ -219,6 +219,20 @@
color: #333; color: #333;
} }
.reportBtn {
background: rgba(22, 22, 22, 1);
color: #fff;
border-color: rgba(22, 22, 22, 1);
border-radius: 4px;
&:hover,
&:focus {
background: rgba(22, 22, 22, 1);
color: #fff;
border-color: rgba(22, 22, 22, 1);
}
}
.formSection { .formSection {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

@ -1,5 +1,5 @@
import React, {useMemo, useState} from 'react'; import React, {useMemo, useState} from 'react';
import {Badge, Button, DatePicker, Form, Row, Col, Select, Space} from 'antd'; import {Badge, Button, DatePicker, Select, Space} from 'antd';
import ReactECharts from 'echarts-for-react'; import ReactECharts from 'echarts-for-react';
import StandardTable from '@/components/StandardTable'; import StandardTable from '@/components/StandardTable';
import './LedgerCenter.less'; import './LedgerCenter.less';
@ -34,26 +34,15 @@ const LedgerCenter = () => {
dataIndex: 'sourceType', dataIndex: 'sourceType',
width: 140, width: 140,
render: type => ( render: type => (
<Button size="small" ghost={type !== '在线监测设备'} type={type === '在线监测设备' ? 'primary' : 'dashed'}> <Button
size="small"
type="default"
className={`source-tag ${type === '在线监测设备' ? 'source-online' : 'source-manual'}`}
>
{type} {type}
</Button> </Button>
), ),
}, },
{
title: '操作',
dataIndex: 'actions',
align: 'right',
render: () => (
<Space size={8}>
<Button size="small" type="link">
详情
</Button>
<Button size="small" type="link">
查看报表
</Button>
</Space>
),
},
], ],
data: [ data: [
{ {
@ -233,27 +222,32 @@ const LedgerCenter = () => {
return ( return (
<div className="ledger-center"> <div className="ledger-center">
<div className="ledger-block filter-block"> <div className="ledger-block filter-block">
<Form layout="vertical"> <div className="filter-line first-line">
<Row gutter={16} className="filter-row"> <div className="filter-item">
<Col span={8}> <span className="filter-label">模版名称</span>
<Form.Item label="模版名称"> <Select
<Select placeholder="请选择模版" options={[{label: '污染源排放台账', value: 'ledger1'}, {label: '废水排放台账', value: 'ledger2'}]} /> placeholder="请选择模版"
</Form.Item> options={[{label: '污染源排放台账', value: 'ledger1'}, {label: '废水排放台账', value: 'ledger2'}]}
</Col> className="filter-control"
<Col span={10}> />
<Form.Item label="起止时间"> </div>
<RangePicker style={{width: '100%'}} /> <div className="filter-item">
</Form.Item> <span className="filter-label">起止时间</span>
</Col> <RangePicker className="filter-control" />
<Col span={6}> </div>
<Form.Item label="所属区域"> <div className="filter-item">
<Select placeholder="请选择区域" options={[{label: '全部区域', value: 'all'}, {label: '能源车间', value: 'energy'}, {label: '污水处理站', value: 'water'}]} /> <span className="filter-label">所属区域</span>
</Form.Item> <Select
</Col> placeholder="请选择区域"
</Row> options={[{label: '全部区域', value: 'all'}, {label: '能源车间', value: 'energy'}, {label: '污水处理站', value: 'water'}]}
<Row gutter={16} className="filter-row"> className="filter-control"
<Col span={16}> />
<Form.Item label="填报单位"> </div>
</div>
<div className="filter-line second-line">
<div className="filter-item wide">
<span className="filter-label">填报单位</span>
<Select <Select
mode="multiple" mode="multiple"
allowClear allowClear
@ -264,17 +258,17 @@ const LedgerCenter = () => {
{label: '单位3', value: 'unit3'}, {label: '单位3', value: 'unit3'},
{label: '单位4', value: 'unit4'}, {label: '单位4', value: 'unit4'},
]} ]}
className="filter-control"
/> />
</Form.Item>
</Col>
</Row>
<div className="filter-actions">
<Space size={10}>
<Button>重新配置</Button>
<Button type="primary">生成台账</Button>
</Space>
</div> </div>
</Form> </div>
<div className="filter-line action-line">
<div className="action-buttons">
<Button className="reset-btn">重新配置</Button>
<Button type="primary" className="generate-btn">生成台账</Button>
</div>
</div>
</div> </div>
<div className="ledger-block table-block"> <div className="ledger-block table-block">
@ -291,9 +285,9 @@ const LedgerCenter = () => {
</Button> </Button>
</Space> </Space>
<Space size={10}> <Space size={10}>
<Button type="link">Excel 导出</Button> <Button type="link" className="excel-btn">Excel 导出</Button>
<Button>保存为快照</Button> <Button>保存为快照</Button>
<Button type="primary">发起报告</Button> <Button>发起报告</Button>
</Space> </Space>
</div> </div>

@ -9,20 +9,104 @@
.ledger-block { .ledger-block {
background: #fff; background: #fff;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); border: 1px solid #f0f0f0;
padding: 16px; box-shadow: none;
padding: 14px 16px;
}
.filter-block {
display: flex;
flex-direction: column;
gap: 12px;
} }
.filter-row { .filter-line {
margin-bottom: 8px; display: flex;
align-items: center;
gap: 16px;
} }
.filter-actions { .first-line .filter-item {
padding-top: 4px; flex: 1;
max-width: 33%;
}
.second-line .filter-item {
flex: 1;
}
.action-line {
justify-content: center;
align-items: center;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
}
.filter-label {
min-width: 72px;
color: #333;
font-size: 14px;
}
.filter-control {
min-width: 220px;
}
.first-line .filter-item:nth-child(2) .filter-control {
min-width: 220px;
}
.first-line .filter-item:nth-child(3) .filter-control {
min-width: 200px;
}
.filter-control .ant-select-selector,
.filter-control.ant-picker,
.filter-control.ant-select {
width: 100%;
}
.action-buttons {
display: flex;
gap: 12px;
align-items: center;
}
.action-buttons .ant-btn {
min-width: 120px;
// height: 28px;
line-height: 28px;
border-radius: 4px;
}
.reset-btn {
background: #f5f5f5;
color: #666;
border-color: #e5e5e5;
&:hover,
&:focus {
background: #f5f5f5;
color: #666;
border-color: #e5e5e5;
} }
}
.generate-btn {
background: rgba(0, 200, 122, 1);
border-color: rgba(0, 200, 122, 1);
color: #fff;
.filter-actions .ant-btn { &:hover,
min-width: 104px; &:focus {
background: rgba(0, 200, 122, 1);
border-color: rgba(0, 200, 122, 1);
color: #fff;
}
} }
.table-toolbar { .table-toolbar {
@ -37,6 +121,81 @@
margin-top: 12px; margin-top: 12px;
} }
.table-block {
.ant-table-wrapper {
border: 1px solid #f0f0f0;
border-radius: 6px;
}
.ant-table-container {
border-radius: 6px;
}
.ant-table-thead > tr > th {
background: #fafafa;
color: #333;
font-weight: 500;
height: 42px;
border-color: #f0f0f0;
}
.ant-table-tbody > tr > td {
height: 46px;
border-color: #f0f0f0;
color: #333;
}
.ant-table-tbody > tr:hover > td {
background: #fafafa;
}
.ant-pagination {
display: flex;
justify-content: center;
margin-top: 16px;
}
.ant-pagination-item-active {
border-color: rgba(0, 200, 122, 1);
background: rgba(0, 200, 122, 1);
a {
color: #fff;
}
}
}
.source-tag {
padding: 0 10px;
height: 24px;
line-height: 22px;
border-radius: 4px;
border: 1px solid transparent;
box-shadow: none;
font-size: 12px;
}
.source-online {
background: rgba(3, 197, 152, 1);
border-color: rgba(3, 197, 152, 1);
color: #fff;
}
.source-manual {
background: #fff9e6;
border-color: #f2d28b;
color: #d48806;
}
.excel-btn {
color: rgba(3, 197, 152, 1);
&:hover,
&:focus {
color: rgba(3, 197, 152, 1);
}
}
.chart-wrapper { .chart-wrapper {
padding: 8px 0 0; padding: 8px 0 0;
} }

@ -9,8 +9,6 @@ const { RangePicker } = DatePicker;
const ReportTask = () => { const ReportTask = () => {
const [status, setStatus] = useState(); const [status, setStatus] = useState();
const [template, setTemplate] = useState();
const [type, setType] = useState();
const [period, setPeriod] = useState(null); const [period, setPeriod] = useState(null);
const [createdAt, setCreatedAt] = useState(null); const [createdAt, setCreatedAt] = useState(null);
const [page, setPage] = useState(6); const [page, setPage] = useState(6);
@ -18,10 +16,16 @@ const ReportTask = () => {
const [detailItem, setDetailItem] = useState(null); const [detailItem, setDetailItem] = useState(null);
const statusMeta = { const statusMeta = {
审核失败: { color: 'orange', text: '审核失败' }, 审核失败: { dot: '#f85c3d', text: '审核失败' },
待提交: { color: 'gold', text: '待提交' }, 待提交: { dot: '#f8c349', text: '待提交' },
已提交: { color: 'green', text: '已提交' }, 已提交: { dot: '#48c774', text: '已提交' },
校验失败: { color: 'red', text: '校验失败' }, 校验失败: { dot: '#e14c49', text: '校验失败' },
};
const actionMeta = {
提交: { color: '#ffac46' },
催办: { color: '#e14c49' },
下载: { color: '#8c8c8c' },
}; };
const dataSource = useMemo( const dataSource = useMemo(
@ -70,25 +74,32 @@ const ReportTask = () => {
[], [],
); );
const renderHeader = (label) => (
<span className="table-header-title">
{label}
<SearchOutlined className="header-search-icon" />
</span>
);
const columns = [ const columns = [
{ {
title: '序号', title: renderHeader('序号'),
dataIndex: 'id', dataIndex: 'id',
width: 70, width: 70,
align: 'center', align: 'center',
}, },
{ {
title: '任务名称', title: renderHeader('任务名称'),
dataIndex: 'name', dataIndex: 'name',
ellipsis: true, ellipsis: true,
}, },
{ {
title: '报送模板', title: renderHeader('报送模板'),
dataIndex: 'template', dataIndex: 'template',
ellipsis: true, ellipsis: true,
}, },
{ {
title: '类型', title: renderHeader('类型'),
dataIndex: 'type', dataIndex: 'type',
width: 90, width: 90,
align: 'center', align: 'center',
@ -97,44 +108,45 @@ const ReportTask = () => {
), ),
}, },
{ {
title: '上次报送时间', title: renderHeader('上次报送时间'),
dataIndex: 'lastReport', dataIndex: 'lastReport',
width: 140, width: 140,
align: 'center', align: 'center',
}, },
{ {
title: '下次报送时间', title: renderHeader('下次报送时间'),
dataIndex: 'nextReport', dataIndex: 'nextReport',
width: 140, width: 140,
align: 'center', align: 'center',
}, },
{ {
title: '状态', title: renderHeader('状态'),
dataIndex: 'status', dataIndex: 'status',
width: 120, width: 120,
align: 'center', align: 'center',
render: (value) => { render: (value) => {
const meta = statusMeta[value] || {}; const meta = statusMeta[value] || {};
return ( return (
<Tag color={meta.color} className="status-tag"> <Tag className="status-tag">
<span className="status-dot" /> <span className="status-dot" style={{ background: meta.dot }} />
{meta.text} <span className="status-text">{meta.text}</span>
</Tag> </Tag>
); );
}, },
}, },
{ {
title: '操作', title: renderHeader('操作'),
dataIndex: 'action', dataIndex: 'action',
width: 160, width: 160,
align: 'center', align: 'center',
render: (action, record) => { render: (action, record) => {
const isUrge = action === '催办'; const meta = actionMeta[action] || {};
return ( return (
<div className="action-links"> <div className="action-links">
<Button <Button
type="link" type="link"
size="small" size="small"
className="link-detail"
onClick={() => { onClick={() => {
setDetailItem(record); setDetailItem(record);
setDetailVisible(true); setDetailVisible(true);
@ -142,7 +154,7 @@ const ReportTask = () => {
> >
详情 详情
</Button> </Button>
<Button type="link" size="small" danger={isUrge}> <Button type="link" size="small" className="link-action" style={{ color: meta.color }}>
{action} {action}
</Button> </Button>
</div> </div>
@ -152,7 +164,7 @@ const ReportTask = () => {
]; ];
const handleSearch = () => { const handleSearch = () => {
console.log('查找', { status, template, type, period, createdAt }); console.log('查找', { status, period, createdAt });
}; };
const handleCreate = () => { const handleCreate = () => {
@ -188,36 +200,6 @@ const ReportTask = () => {
</Select> </Select>
</div> </div>
<div className="filter-item">
<span className="filter-label">报送模板</span>
<Select
placeholder="请选择"
allowClear
value={template}
onChange={setTemplate}
className="filter-control"
>
<Option value="污染物排放台账">污染物排放台账</Option>
<Option value="危险废物管理台账">危险废物管理台账</Option>
<Option value="运行记录">运行记录</Option>
<Option value="排污许可证">排污许可证</Option>
</Select>
</div>
<div className="filter-item">
<span className="filter-label">类型</span>
<Select
placeholder="请选择"
allowClear
value={type}
onChange={setType}
className="filter-control"
>
<Option value="月度">月度</Option>
<Option value="年度">年度</Option>
</Select>
</div>
<div className="filter-item"> <div className="filter-item">
<span className="filter-label">报送周期</span> <span className="filter-label">报送周期</span>
<RangePicker <RangePicker
@ -246,10 +228,24 @@ const ReportTask = () => {
<div className="table-wrapper"> <div className="table-wrapper">
<Table <Table
bordered
columns={columns} columns={columns}
dataSource={dataSource} dataSource={dataSource}
rowKey="id" rowKey="id"
pagination={{ current: page, pageSize: 5, total: 85, onChange: setPage }} className="report-task-table"
size="middle"
tableLayout="fixed"
pagination={{
current: page,
pageSize: 5,
total: 85,
showSizeChanger: true,
pageSizeOptions: ['5', '10', '20'],
showQuickJumper: false,
showTotal: (total) => `${total}`,
onChange: setPage,
}}
rowClassName={() => 'report-task-row'}
/> />
</div> </div>

@ -7,13 +7,18 @@
.toolbar { .toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: flex-start;
gap: 12px;
background: #fff; background: #fff;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
border-radius: 4px; border-radius: 4px;
padding: 12px 16px; padding: 12px 16px;
.create-btn { .create-btn {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
background: rgba(0, 212, 138, 1); background: rgba(0, 212, 138, 1);
border-color: rgba(0, 212, 138, 1); border-color: rgba(0, 212, 138, 1);
color: #fff; color: #fff;
@ -29,9 +34,12 @@
.filter-group { .filter-group {
display: flex; display: flex;
flex-wrap: wrap; flex: 1;
flex-wrap: nowrap;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
overflow-x: auto;
min-height: 32px;
.filter-item { .filter-item {
display: inline-flex; display: inline-flex;
@ -45,11 +53,17 @@
} }
.filter-control { .filter-control {
width: 180px; width: 120px !important;
min-width: 120px;
}
.filter-control.ant-select .ant-select-selector {
width: 120px;
} }
.ant-picker-range { .filter-control.ant-picker-range {
width: 260px; width: 120px !important;
min-width: 120px;
} }
} }
@ -73,16 +87,53 @@
padding: 12px 16px 16px; padding: 12px 16px 16px;
overflow: hidden; overflow: hidden;
.table-header-title {
display: inline-flex;
align-items: center;
gap: 6px;
color: #1f1f1f;
font-weight: 500;
}
.header-search-icon {
color: #c6c6c6;
font-size: 12px;
}
.report-task-table {
.ant-table-thead > tr > th {
background: #f5f7fa;
color: #1f1f1f;
font-weight: 500;
border-bottom: 1px solid #e9e9e9;
padding: 12px 10px;
}
.ant-table-tbody > tr > td {
border-bottom: 1px solid #f0f0f0;
padding: 12px 10px;
}
.ant-table-tbody > tr:hover > td {
background: #f8fbff;
}
.ant-table-pagination-right {
justify-content: flex-end;
}
}
.ant-table { .ant-table {
.status-tag { .status-tag {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
border: none; border: 1px solid #f0f0f0;
border-radius: 2px; border-radius: 2px;
height: 26px; height: 26px;
padding: 0 10px; padding: 0 10px;
font-size: 12px; font-size: 12px;
background: #fff;
} }
.status-dot { .status-dot {
@ -98,6 +149,10 @@
border-radius: 2px; border-radius: 2px;
padding: 2px 10px; padding: 2px 10px;
font-size: 12px; font-size: 12px;
height: 26px;
display: inline-flex;
align-items: center;
line-height: 1;
} }
.type-monthly { .type-monthly {
@ -118,6 +173,14 @@
.ant-btn-link { .ant-btn-link {
padding: 0 6px; padding: 0 6px;
} }
.link-detail {
color: #25b864;
}
.link-action {
font-weight: 500;
}
} }
} }
} }

@ -29,6 +29,10 @@ const menuItem = [
label: '环保活动管理', label: '环保活动管理',
key: '/topnavbar00/business/environmentalActivities', key: '/topnavbar00/business/environmentalActivities',
}, },
{
label: '环保月报管理-模板库新建',
key: '/topnavbar00/business/envInformationMonthly/template-create',
},
] ]
const TopNavBar = (props) => { const TopNavBar = (props) => {

Loading…
Cancel
Save