计划管理-生产计划管理-收油生产计划

main
jiangxucong 1 month ago
parent 63bd22df44
commit 2c483417ac

@ -0,0 +1,92 @@
import React, { useState } from 'react';
import { Select } from 'antd';
import styles from './ProductionPlanManagement.less';
import OilProductionPlan from './second_oil_components/OilProductionPlan';
import HairOilProductionPlan from './second_oil_components/HairOilProductionPlan';
import BackflowProductionPlan from './second_oil_components/BackflowProductionPlan';
import MixingProductionPlan from './second_oil_components/MixingProductionPlan';
const ProductionPlanManagement = () => {
// 顶部一行选择:第一个为“基本信息/理化性质”,其余占位
const [firstMenu, setFirstMenu] = useState('oilProductionPlan');
const [secondMenu, setSecondMenu] = useState(undefined);
const [thirdMenu, setThirdMenu] = useState(undefined);
const [fourthMenu, setFourthMenu] = useState(undefined);
const [fifthMenu, setFifthMenu] = useState(undefined);
const firstMenuLabelMap = {
oilProductionPlan: '收油生产计划',
hairOilProductionPlan: '发油生产计划',
backflowProductionPlan: '倒灌生产计划',
mixingProductionPlan: '搅拌生产计划',
};
const renderFirstMenuContent = () => {
switch (firstMenu) {
case 'oilProductionPlan':
return <OilProductionPlan />;
case 'hairOilProductionPlan':
return <HairOilProductionPlan />;
case 'backflowProductionPlan':
return <BackflowProductionPlan />;
case 'mixingProductionPlan':
return <MixingProductionPlan />;
default:
return null;
}
};
return (
<div className={styles.container}>
<div className={styles.topBar}>
<div
className={styles.currentLabel}
>
当前{firstMenuLabelMap[firstMenu] || '未选择'}
</div>
<div className={styles.selectsWrapper}>
<Select
className={styles.topSelect}
value={firstMenu}
onChange={setFirstMenu}
options={[
{ label: '收油生产计划', value: 'oilProductionPlan' },
{ label: '发油生产计划', value: 'hairOilProductionPlan' },
{ label: '倒灌生产计划', value: 'backflowProductionPlan' },
{ label: '搅拌生产计划', value: 'mixingProductionPlan' },
]}
/>
<Select
className={styles.topSelect}
value={secondMenu}
onChange={setSecondMenu}
placeholder="发油生产计划"
options={[]}
disabled
/>
<Select
className={styles.topSelect}
value={thirdMenu}
onChange={setThirdMenu}
placeholder="倒灌生产计划"
options={[]}
disabled
/>
<Select
className={styles.topSelect}
value={fourthMenu}
onChange={setFourthMenu}
placeholder="搅拌生产计划"
options={[]}
disabled
/>
</div>
</div>
{renderFirstMenuContent()}
</div>
);
};
export default ProductionPlanManagement;

@ -0,0 +1,138 @@
.container {
padding: 15px 0px;
min-height: 100%;
.topBar {
display: flex;
height: 46px;
margin-bottom: 16px;
.currentLabel {
border-top-left-radius: 20px;
height: 100%;
width: 230px;
display: flex;
align-items: center;
padding-left: 22px;
color: #fff;
font-size: 17px;
font-weight: 450;
background: pink;
background-image: url('@/assets/business_basic/bgOil.png');
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center center;
}
.selectsWrapper {
height: 100%;
// width: 230px;
background-color: #fff;
border: 1px solid transparent;
border-image-source: linear-gradient(96.54deg, #FFFFFF -0.94%, rgba(255, 255, 255, 0) 25.28%, rgba(167, 229, 228, 0) 59.69%, #A7E5E4 79.76%);
border-image-slice: 1;
display: flex;
align-items: center;
gap: 12px;
flex: 1;
}
.topSelect {
width: 130px;
display: flex;
align-items: center;
text-align: center;
box-shadow: none;
:global(.ant-select-selector) {
border: transparent !important;
background-image: url('@/assets/business_basic/Union.svg');
background-repeat: no-repeat;
background-position: center center;
font-size: 12px;
color: rgba(0, 102, 101, 1);
background-size: 100% 100%;
box-shadow: none !important;
}
:global(.ant-select-focused .ant-select-selector),
:global(.ant-select-selector:hover) {
border: transparent !important;
box-shadow: none !important;
}
:global(.ant-select-arrow) {
right: 25px;
color: rgba(0, 102, 101, 1);
}
}
}
.section {
margin-bottom: 16px;
.blockTitle {
font-size: 16px;
font-weight: 600;
color: #2f4f4f;
margin-bottom: 12px;
}
.formGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 16px;
grid-row-gap: 12px;
.formItem {
display: flex;
flex-direction: column;
.label {
color: #666;
font-size: 12px;
margin-bottom: 6px;
}
}
.span2 {
grid-column: 1 / span 2;
}
}
.actionsRight {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
.listHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.filterRow {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 12px;
.filterItem {
display: flex;
align-items: center;
gap: 8px;
.filterLabel {
color: #666;
font-size: 12px;
white-space: nowrap;
}
}
}
}
}

@ -0,0 +1,281 @@
import React, { useMemo, useState } from 'react';
import { Select, Space, Button, Switch, message, Input, Modal, InputNumber } from 'antd';
import { PlusOutlined, SearchOutlined, ReloadOutlined, ExportOutlined } from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './OilProductionPlan.less';
const OilProductionPlan = () => {
const [searchKeyword, setSearchKeyword] = useState('');
// 列表筛选与数据(演示数据,可替换为接口)
const [filters, setFilters] = useState({
listCategory: undefined,
listStatus: undefined,
});
const columns = useMemo(() => {
return [
{ title: '计划编号', dataIndex: 'planNo', key: 'planNo', width: 100 },
{ title: '计划名称', dataIndex: 'planName', key: 'planName', width: 220 },
{ title: '计划类型', dataIndex: 'planType', key: 'planType', width: 100 },
{ title: '计划时间', dataIndex: 'planTime', key: 'planTime', width: 160 },
{ title: '数量(吨)', dataIndex: 'qty', key: 'qty', width: 100 },
{ title: '来源/目标', dataIndex: 'sourceTarget', key: 'sourceTarget', width: 220 },
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
{ title: '进度', dataIndex: 'progress', key: 'progress', width: 100 },
{ title: '创建人', dataIndex: 'creator', key: 'creator', width: 100 },
{
title: '操作',
key: 'action',
fixed: 'right',
width: 140,
render: (_, record) => (
<Space size={12}>
<Button
type="link"
size="small"
className={styles.primaryActionBtn}
onClick={() => handlePrimaryAction(record)}
>
{record.actionText}
</Button>
</Space>
),
},
];
}, []);
const [tableData, setTableData] = useState([
{
key: 'RPO01',
planNo: 'RPO01',
planName: '5月的92#汽油管道收油计划',
planType: '管道收油',
planTime: '2024-05-01 14:00',
qty: 600,
sourceTarget: '来源: 炼油厂 目标: 1号储罐',
status: '执行中',
progress: '45%',
creator: '郑双',
actionText: '更新进度',
},
{
key: 'RPO02',
planNo: 'RPO02',
planName: '5月的95#汽油管道收油计划',
planType: '管道收油',
planTime: '2024-05-01 14:00',
qty: 500,
sourceTarget: '来源: 炼油厂 目标: 2号储罐',
status: '执行中',
progress: '40%',
creator: '钱亮丽',
actionText: '跟踪进度',
},
{
key: 'RPO03',
planNo: 'RPO03',
planName: '5月的0#柴油铁路收油计划',
planType: '铁路收油',
planTime: '2024-05-01 15:00',
qty: 300,
sourceTarget: '来源: 炼油厂 目标: 3号储罐',
status: '执行中',
progress: '0%',
creator: '郑璞',
actionText: '开始执行',
},
{
key: 'RPO04',
planNo: 'RPO04',
planName: '5月的航煤管道公路收油计划',
planType: '公路收油',
planTime: '2024-05-01 12:00',
qty: 200,
sourceTarget: '来源: 炼油厂 目标: 4号储罐',
status: '待执行',
progress: '50%',
creator: '孙莲艳',
actionText: '开始执行',
},
{
key: 'RPO05',
planNo: 'RPO05',
planName: '4月的92#汽油管道收油计划',
planType: '管道收油',
planTime: '2024-05-01 11:00',
qty: 100,
sourceTarget: '来源: 炼油厂 目标: 5号储罐',
status: '审核中',
progress: '90%',
creator: '李莉娜',
actionText: '更新进度',
},
{
key: 'RPO06',
planNo: 'RPO06',
planName: '4月的95#汽油管道收油计划',
planType: '管道收油',
planTime: '2024-05-01 10:00',
qty: 150,
sourceTarget: '来源: 炼油厂 目标: 6号储罐',
status: '已完成',
progress: '30%',
creator: '李钰',
actionText: '查看详情',
},
]);
const [progressState, setProgressState] = useState({ open: false, value: 0, recordKey: null });
const handlePrimaryAction = (record) => {
const action = record.actionText;
if (action === '更新进度') {
const current = parseInt(String(record.progress).replace('%', ''), 10) || 0;
setProgressState({ open: true, value: current, recordKey: record.key });
} else if (action === '跟踪进度') {
message.info(`当前进度:${record.progress}`);
} else if (action === '开始执行') {
Modal.confirm({
title: '确认开始执行?',
onOk: () => {
setTableData((prev) => prev.map((item) =>
item.key === record.key
? { ...item, status: '执行中', progress: item.progress || '0%', actionText: '更新进度' }
: item
));
message.success('已开始执行');
},
});
} else if (action === '查看详情') {
Modal.info({
title: '计划详情',
content: (
<div>
<div>计划编号{record.planNo}</div>
<div>计划名称{record.planName}</div>
<div>计划类型{record.planType}</div>
<div>计划时间{record.planTime}</div>
<div>数量{record.qty}</div>
<div>来源/目标{record.sourceTarget}</div>
<div>状态{record.status}</div>
<div>进度{record.progress}</div>
<div>创建人{record.creator}</div>
</div>
),
});
}
};
return (
<div className={styles.container}>
<div className={styles.searchSection}>
<div className={styles.sectionHeader}>
<span className={styles.sectionBar} />
<span className={styles.sectionTitle}>查询条件</span>
</div>
<div className={styles.filterContent}>
<div className={styles.filterItem}>
<Input.Search
placeholder="搜索关键词"
allowClear
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
onSearch={(val) => setSearchKeyword(val)}
style={{ minWidth: 150 }}
/>
</div>
<div className={styles.filterItem}>
<span className={styles.filterLabel}>状态:</span>
<Select
value={filters.listStatus}
onChange={(v) => setFilters({ ...filters, listStatus: v })}
placeholder="全部"
options={[
{ label: '全部', value: undefined },
{ label: '启用', value: '启用' },
{ label: '停用', value: '停用' },
]}
allowClear
/>
</div>
<Space className={styles.filterButtons}>
<Button
type="primary"
icon={<SearchOutlined />}
className={styles.queryBtn}
>
查询
</Button>
<Button
icon={<ReloadOutlined />}
className={styles.resetBtn}
>
重置
</Button>
</Space>
</div>
</div>
<div className={styles.searchSection}>
<div className={styles.sectionHeader}>
<span className={styles.sectionBar} />
<span className={styles.sectionTitle}>收油生产计划列表</span>
<Space className={styles.headerActions}>
<Button
type="primary"
icon={<PlusOutlined />}
className={styles.addBtn}
>
新增计划
</Button>
</Space>
</div>
<div className={styles.tableWrapper}>
<StandardTable
rowKey="key"
columns={columns}
data={{
list: tableData,
pagination: {
total: tableData.length,
pageSize: 10,
current: 1,
showTotal: (total) => `${total}`,
},
}}
// selectionType="checkbox"
/>
</div>
<Modal
title="更新进度"
open={progressState.open}
onOk={() => {
setTableData((prev) => prev.map((item) =>
item.key === progressState.recordKey
? { ...item, progress: `${progressState.value}%` }
: item
));
setProgressState({ open: false, value: 0, recordKey: null });
message.success('进度已更新');
}}
onCancel={() => setProgressState({ open: false, value: 0, recordKey: null })}
>
<Space>
<span>当前进度%</span>
<InputNumber
min={0}
max={100}
value={progressState.value}
onChange={(v) => setProgressState((s) => ({ ...s, value: Number(v) || 0 }))}
/>
</Space>
</Modal>
</div>
</div>
);
};
export default OilProductionPlan;

@ -0,0 +1,233 @@
@section-border: rgba(118, 194, 181, 1);
@section-bg: rgba(243, 249, 248, 1);
.container {
margin: 0;
padding: 0;
background: #fff;
display: flex;
flex-direction: column;
gap: 15px;
width: 100%;
height: 100%;
// 底部区域 - 油品列表
.searchSection {
flex: 1;
display: flex;
flex-direction: column;
// border: 1px solid @section-border;
background: #fff;
padding: 30px 20px 0 20px;
// 区域头部
.sectionHeader {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
// 将右侧动作区推到最右侧
.headerActions {
display: flex;
gap: 12px;
margin-left: auto;
.addBtn {
background-image: url('@/assets/business_basic/Bt_bg1.png');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border: none;
}
}
// 装饰条
.sectionBar {
width: 2px;
height: 16px;
background: rgba(0, 102, 101, 1);
border-radius: 1px;
}
// 标题
.sectionTitle {
font-size: 18px;
font-weight: 600;
color: #333;
}
}
// 筛选内容区域
.filterContent {
display: flex;
align-items: center;
gap: 20px;
// margin-bottom: 20px;
flex-wrap: wrap;
// justify-content: space-between;
// 筛选项
.filterItem {
display: flex;
align-items: center;
gap: 12px;
// 筛选标签
.filterLabel {
color: #333;
font-size: 14px;
}
:global {
// 统一 Input.Search 边框颜色为绿主题
.ant-input-affix-wrapper {
border-radius: 0px !important;
border-color: rgba(44, 158, 157, 1) !important;
}
.ant-input-search-button {
border-radius: 0px !important;
border-color: rgba(44, 158, 157, 1) !important;
}
.ant-select {
min-width: 140px;
// 选择框样式
.ant-select-selector {
border-color: rgba(44, 158, 157, 1) !important;
border-radius: 2px !important;
}
&:hover .ant-select-selector {
border-color: rgba(44, 158, 157, 1) !important;
}
&.ant-select-focused .ant-select-selector {
border-color: rgba(44, 158, 157, 1) !important;
box-shadow: 0 0 0 2px rgba(44, 158, 157, 0.2) !important;
}
}
}
}
// 筛选按钮组
.filterButtons {
display: flex;
gap: 12px;
.queryBtn {
background-image: url('@/assets/business_basic/Bt_bg1.png');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border: none;
}
.resetBtn {
background-image: url('@/assets/business_basic/Bt_bg2.png');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border: none;
color: #006665;
}
}
}
// 表格包装器
.tableWrapper {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
.primaryActionBtn {
color: #2D9E9D;
}
:global(.ant-table-wrapper) {
flex: 1;
display: flex;
flex-direction: column;
}
// 表头样式
:global {
.ant-table-thead>tr>th {
color: rgba(51, 51, 51, 1) !important;
font-weight: 450 !important;
background-color: rgba(240, 247, 247, 1) !important;
text-align: center !important;
}
// 表体样式
.ant-table-tbody>tr>td {
color: rgba(78, 88, 86, 1) !important;
font-weight: 400 !important;
text-align: center !important;
}
// 操作列按钮样式
.viewDetailBtn {
color: rgba(0, 102, 101, 1) !important;
&:hover {
color: rgba(0, 102, 101, 1) !important;
}
}
.editBtn {
color: rgba(45, 158, 157, 1) !important;
&:hover {
color: rgba(45, 158, 157, 1) !important;
}
}
.deleteBtn {
color: rgba(255, 130, 109, 1) !important;
&:hover {
color: rgba(255, 130, 109, 1) !important;
}
}
// 状态列 Switch 样式
.statusSwitch {
// 启用状态背景色
&.ant-switch-checked {
background-color: rgba(20, 106, 89, 1) !important;
}
// 停用状态背景色
&:not(.ant-switch-checked) {
background-color: rgba(153, 153, 153, 1) !important;
}
}
// 复选框样式
.ant-checkbox-inner {
border-color: rgba(0, 102, 101, 1) !important;
&::after {
background-color: rgba(0, 102, 101, 1) !important;
}
}
.ant-checkbox-wrapper:hover .ant-checkbox-inner,
.ant-checkbox:hover .ant-checkbox-inner {
border-color: rgba(0, 102, 101, 1) !important;
}
.ant-checkbox-checked .ant-checkbox-inner {
background-color: rgba(0, 102, 101, 1) !important;
border-color: rgba(0, 102, 101, 1) !important;
}
}
}
}
}
Loading…
Cancel
Save