主界面:
// 罐区详情
import React, { useState, useRef, } from 'react';
import { Card, Layout, Form } from 'are-common';
import ReactEcharts from 'echarts-for-react';
import { connect } from "dva";
import { GridContent } from '@ant-design/pro-layout';
import TopCard from "./components/TopCard";
import TankCard from "./components/TankDetails";
import OrderTable from "./components/OrderTable";
import './index.less'
import { injectIntl } from 'react-intl';
import { TankAreaDetailVo } from "./data";
import { ActionType } from '@ant-design/pro-table';
import { findEnumData } from 'src/utils/utils';
import { TankVO } from '../tank/tankInterface';
interface tankAreaDetailsProps {
dispatch: any
location: any
}
const TankAreaDetails: React.FC<tankAreaDetailsProps> = (props) => {
const { dispatch } = props;
const [tankAreaDetailVo, setTankAreaDetailVo] = useState<TankAreaDetailVo>();
const [tankType, setTankType] = useState<string | undefined>();
const [materialName, setMaterialType] = useState<string | undefined>();
const [tankTypesEnum, setTankTypesEnum] = useState();
const actionRef = useRef<ActionType>();
const [tanks, setTanks] = useState<TankVO[]>();
const [tankTitle, setTankTitle] = useState<string>("");
var tankAreaId = 1
if (props.location.search.split("=")[1]) {
tankAreaId = props.location.search.split("=")[1]
}
if (!tankAreaDetailVo) {
dispatch({
type: "tankAreaDetails/findDetailData",
payload: tankAreaId
}).then((res: TankAreaDetailVo) => {
setTankAreaDetailVo(res)
})
}
if (!tankTypesEnum) {
findEnumData("罐类型").then((res: any) => {
setTankTypesEnum(res)
})
}
return (
<Layout>
<GridContent className="sup-are-layout-ref__full" style={{ overflow: 'auto', height: '100vh', padding: '10px 0 0 10px' }}>
<React.Fragment>
<div id="top-header">
<div className="center-title">{tankAreaDetailVo?.tankAreaName}</div>
</div>
<TopCard
tankAreaDetailVo={tankAreaDetailVo}
actionRef={actionRef}
setTankType={setTankType}
tankAreaId={tankAreaId}
setTankTitle={setTankTitle}
setMaterialType={setMaterialType}
>
</TopCard>
<TankCard
tankType={tankType}
tankTypesEnum={tankTypesEnum}
tankAreaId={tankAreaId}
tankAreaDetailVo={tankAreaDetailVo}
setTankType={setTankType}
setTanks={setTanks}
tanks={tanks}
tankTitle={tankTitle}
materialName={materialName}
>
</TankCard>
<OrderTable
tankAreaId={tankAreaId}
>
</OrderTable>
</React.Fragment>
</GridContent>
</Layout>
)
};
export default injectIntl(connect()(TankAreaDetails));
topCard:
import { Card, Col, DatePicker, Row, Tabs, Button, Upload, Icon, message } from 'are-common';
import React, { useState, useRef } from 'react';
import '../index.less'
import ReactEcharts from 'echarts-for-react';
import { TankAreaDetailVo, TankAttr } from '../data';
import { TankVO } from "../../tank/tankInterface";
import { connect } from 'dva';
import { injectIntl } from 'react-intl';
import { excelExport, getIp } from '../../../services/tankAreaDetails';
import tank from 'src/views/tank';
interface TopCardProps {
tankAreaDetailVo: TankAreaDetailVo
counts: Array<any>
dispatch: any
actionRef: any
setTankType: (tankType: string | undefined) => void
setTanks: (tanks: TankVO[]) => void
tankAreaId: string
setTankTitle: (title: string) => void
setMaterialType: (materialName: string | undefined) => void
}
const TopCard: React.FC<any> = (props: TopCardProps) => {
const { tankAreaDetailVo, counts, dispatch, actionRef, setTankType, setMaterialType, tankAreaId, setTankTitle } = props
const fileInput = React.createRef()
const countsData = tankAreaDetailVo?.deviceCounts
const [fileList, setFileList] = useState([]);
//罐类型占比饼图
const tankTypeOption = {
title: {
text: '各类型罐占比统计',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: tankAreaDetailVo?.tankTypes?.map((item: any) => {
return item.name
})
},
series: [
{
name: '储罐类型',
type: 'pie',
radius: '80%',
center: ['50%', '55%'],
data: tankAreaDetailVo?.tankTypes,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
//罐区物料占比饼图
const materialNameOption = {
title: {
text: '各物料占比统计',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: tankAreaDetailVo?.materials?.map((item: any) => {
return item.name
})
},
series: [
{
name: '物料类型',
type: 'pie',
radius: '80%',
center: ['50%', '55%'],
data: tankAreaDetailVo?.materials,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
const tankTypeEvent = {
'click': (value: any) => {
setTankType(value.data?.typeCode);
setMaterialType(undefined)
actionRef.current?.reloadAndRest();
setTankTitle(value.data.name);
}
}
const materialEvent = {
'click': (value: any) => {
setMaterialType(value.data?.name);
setTankType(undefined)
actionRef.current?.reloadAndRest();
setTankTitle(value.data.name)
}
}
//设备数量排序
var countArray = new Array
const sortArray = () => {
countArray.push({ "deviceName": "其他装置", "count": countsData?.otherDeviceCount })
countArray.push({ "deviceName": "泵", "count": countsData?.pumpCount })
countArray.push({ "deviceName": "储罐", "count": countsData?.tankCount })
countArray.push({ "deviceName": "阀", "count": countsData?.valveCount })
for (let i = 0; i < countArray.length; i++) {
for (let j = i + 1; j < countArray.length; j++) {
if (parseInt(countArray[i].count) <= parseInt(countArray[j].count)) {
var temp = countArray[i]
countArray[i] = countArray[j]
countArray[j] = temp
}
}
}
}
//上传
const uploadExcel = () => {
// dispatch({
// type: "tankDetails/excelExport",
// payload: tankAreaId
// })
}
//导入
const fileProps = {
name: 'file',
action: '/are/oms/bpmnFlowChart/updateDevice',
headers: {
authorization: 'authorization-text',
},
showUploadList: false,
multiple: false,
fileList: fileList,
onChange(info: any) {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
setFileList(fileList)
if (info.file.response?.code === 0) {
message.success(`${info.file.name} 文件导入成功`);
} else if (info.file.response?.code === 1000) {
message.error(`${info.file.name} ` + info.file.response?.msg);
}
},
};
//导出
const DownLoadExcel = async () => {
await getIp().then((res: any) => {
const ip = res.result?.data
window.open("http://" + ip + ":18276/oms/bpmnFlowChart/export?tankAreaId=" + tankAreaId)
})
}
if (tankAreaDetailVo) {
sortArray();
return (
<Card bordered={false} bodyStyle={{ padding: 0 }}>
<div className={"topCard"}>
<Row >
<Col xl={7} lg={12} md={14} sm={20} xs={24}>
<div id="ranking-board">
<div className="ranking-board-title">罐区主要设备统计
<Upload {...fileProps}>
<Button style={{ paddingTop: "0%" }} icon={"upload"} onClick={uploadExcel}>
导入</Button>
</Upload>
<Button style={{ paddingTop: "0%" }} icon={"download"} onClick={DownLoadExcel}>导出</Button></div>
<ul className={"rankingList"}>
{
countArray?.map((item: any, i: number) => (
<li key={item.deviceName}>
<span className={`${"rankingItemNumber"} ${i < 3 ? "active" : ''}`}>
{i + 1}
</span>
<span className={"rankingItemTitle"} title={item.deviceName}>
{item.deviceName}
</span>
<span> {item.count}</span>
</li>
))}
</ul>
</div>
</Col>
<Col xl={8} lg={8} md={10} sm={14} xs={24}>
<ReactEcharts className="materialPie"
option={tankTypeOption}
onEvents={tankTypeEvent} />
</Col>
<Col xl={8} lg={8} md={10} sm={14} xs={24}>
<ReactEcharts className="materialPie"
option={materialNameOption}
onEvents={materialEvent}
/>
</Col>
</Row>
</div>
</Card>
)
}
else {
return (
<></>
)
}
}
export default injectIntl<any>(connect()(TopCard));
TankDetails.tsx:
import { Card, Col, DatePicker, Row, Tabs } from 'are-common';
import React, { useState, useRef, Suspense, useEffect } from 'react';
import '../index.less'
import ReactEcharts from 'echarts-for-react';
import { TankAreaDetailVo, TankAttr } from '../data';
import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table';
import { connect } from 'dva';
import { injectIntl } from 'react-intl';
import { findOilStorage } from "../../../services/tankAreaDetails";
import { TankVO } from 'src/views/tank/tankInterface';
interface TankCardProps {
tankAreaDetailVo: TankAreaDetailVo
counts: Array<any>
dispatch: any
tankType: string
tankTypesEnum: Array<any>
location: any
tankAreaId: string
setTanks: (tanks: TankVO[]) => void
tanks: TankVO[]
tankTitle: string
materialName: string | undefined
}
var tanks1: TankVO[];
const TankCard: React.FC<any> = (props: TankCardProps) => {
const { dispatch, tankType, tankTypesEnum, tankAreaId, tankAreaDetailVo, tanks, setTanks, tankTitle, materialName } = props
const actionRef = useRef<ActionType>();
const [materialDatas, setMaterialDatas] = useState();
const [intervalId, setIntervalId] = useState<any>();
// const [tanks, setTanks] = useState<TankVO[]>();
const columns: ProColumns<TankAttr>[] = [
{
title: '罐编号',
dataIndex: 'tankCode',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
renderText: (tankCode: string) => {
return <a onClick={() => {
window.open("/tankInfoManage/tankAreaView?tankCode=" + tankCode)
}}>{tankCode}</a>
}
}, {
title: '罐名称',
dataIndex: 'tankName',
hideInForm: true,
ellipsis: true,
hideInSearch: true
}, {
title: '罐类型',
dataIndex: 'tankType',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
renderText: (typeCode: string) => {
let typeName: string = typeCode
tankTypesEnum?.map((item: any) => {
if (item.code === typeCode) {
typeName = item.text;
}
})
return typeName
}
}, {
title: '物料名称',
dataIndex: 'material.materialName',
hideInForm: true,
ellipsis: true,
hideInSearch: true
}, {
title: '是否起始装置',
dataIndex: 'startDevice',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
filters: undefined,
valueEnum: {
false: { text: "否" },
true: { text: "是" }
}
}, {
title: '是否终止装置',
dataIndex: 'endDevice',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
filters: undefined,
valueEnum: {
false: { text: "否" },
true: { text: "是" }
}
},
]
//查找罐类别信息
const findTanks = (params?: any) => {
clearInterval(intervalId)
if (tankType) {
params.tankType = tankType
}
if (materialName) {
params.materialName = materialName
}
params.tankAreaId = tankAreaId
params.currentPage = params.current
var back = dispatch({
type: 'tank/queryTankPage',
payload: params
}).then((res: any) => {
res.current = res.pagination.current
res.pageSize = res.pagination.pageSize
res.total = Number.parseInt(res.pagination.total)
res.data = res.list
tanks1 = res.list
if (res.data != undefined) {
setIntervalId(setInterval(() => {
dispatch({
type: 'tankAreaDetails/findOilStorage',
payload: res.data?.map((item: TankVO) => {
return item.tankCode
})
}).then((res: any) => {
setMaterialDatas(res)
})
}, 2000))
}
setTanks(res.data)
return res;
})
return Promise.resolve(back)
}
const tankMaterialOption = {
color: ['#3398DB'],
title: {
text: '各罐内实时储油量',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
left: '3%',
right: '10%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: tanks?.map((item: TankVO) => {
return item.tankCode
}),
axisTick: {
alignWithLabel: true
},
name: '储罐编号',
axisLabel: {
interval: 0,
rotate: 40
}
}
],
yAxis: [
{
type: 'value',
name: '储油量'
}
],
series: [
{
name: "储油量",
type: 'bar',
barWidth: '60%',
data: materialDatas
}
]
};
return (
<div id="tank-board">
<Row className="bar-row" style={{ paddingTop: 20 }}>
<Col xl={10} lg={8} md={8} sm={14} xs={24}>
<div className="ranking-board-title">{tankTitle}罐列表</div>
<ProTable
rowKey="id"
// headerTitle="罐列表"
options={false}
search={false}
actionRef={actionRef}
pagination={{
pageSize: 10,
showTotal: total => { return <span>共 <span className="pagination">{total}</span> 条</span> },
}}
columns={columns}
scroll={{
x: 500,
y: `calc(100vh - 700px)`
}}
params={{ tankType: tankType, materialName: materialName }}
request={((params, sorter, filter) => findTanks({ ...params, sorter, filter }))}
>
</ProTable>
</Col>
<Col xl={10} lg={8} md={8} sm={14} xs={16}>
<ReactEcharts
className="tankMaterialBar"
option={tankMaterialOption}
>
</ReactEcharts>
</Col>
</Row>
</div>
)
}
export default injectIntl<any>(connect()(TankCard));
orderTable.tsx:
import { Card, Col, DatePicker, Row, Tabs, Divider } from 'are-common';
import React, { useState, useRef, Suspense, useEffect } from 'react';
import '../index.less'
import ReactEcharts from 'echarts-for-react';
import { TankAreaDetailVo, TankAttr } from '../data';
import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table';
import { connect } from 'dva';
import { injectIntl } from 'react-intl';
import { findOilStorage, getIp } from "../../../services/tankAreaDetails";
interface TankCardProps {
dispatch: any
tankAreaId: string
}
const OrderTable: React.FC<any> = (props: TankCardProps) => {
const { dispatch, tankAreaId } = props
const actionRef = useRef<ActionType>();
const [materialDatas, setMaterialDatas] = useState();
const [intervalId, setIntervalId] = useState<any>();
// const [tanks, setTanks] = useState<TankVO[]>();
const columns: ProColumns<any>[] = [
{
title: '订单编号',
dataIndex: 'orderCode',
hideInForm: true,
ellipsis: true,
hideInSearch: true
}, {
title: '业务类型',
dataIndex: 'businessTypeName',
hideInForm: true,
ellipsis: true,
hideInSearch: true
}, {
title: '传输物料',
dataIndex: 'materialName',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
}, {
title: '传输方式',
dataIndex: 'transferPatternName',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
filters: undefined,
}, {
title: '传输总量',
dataIndex: 'transferTotal',
hideInForm: true,
ellipsis: true,
hideInSearch: true,
filters: undefined,
renderText: (transferTotal: string, order: any) => {
if (order.unitName && transferTotal) {
return transferTotal + order.unitName
}
else if (transferTotal) {
return transferTotal
}
else {
return undefined
}
}
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => (
<>
<a onClick={async () => {
await getIp().then((res: any) => {
window.open("http://" + res?.result?.data + "/are-oms-order-web/#/order?orderCode=" + record.orderCode)
})
}}>详情
</a>
</>
),
},
]
//查找订单信息
const findRunningOrder = (params?: any) => {
params.orderStatusCode = 3
params.tankAreaId = tankAreaId
var back = dispatch({
type: 'tankAreaDetails/findRunningOrder',
payload: params
}).then((res: any) => {
res.current = res.pagination.current
res.pageSize = res.pagination.pageSize
res.total = Number.parseInt(res.pagination.total)
res.data = res.list
return res;
})
return Promise.resolve(back)
}
return (
<div id="order-board">
<div className="ranking-board-title">正在执行的订单</div>
<ProTable
rowKey="orderCode"
// headerTitle="罐列表"
options={false}
search={false}
actionRef={actionRef}
pagination={{
pageSize: 10,
showTotal: total => { return <span>共 <span className="pagination">{total}</span> 条</span> },
}}
columns={columns}
scroll={{
x: 500,
y: `calc(100vh - 700px)`
}}
request={((params, sorter, filter) => findRunningOrder({ ...params, sorter, filter }))}
className="my-ant-pro-table"
>
</ProTable>
</div>
)
}
export default injectIntl<any>(connect()(OrderTable));
index.less:
#data-view {
width: 100%;
height: 100%;
// background-color: #030409;
// color: #fff;
#dv-full-screen-container {
// background-image: url('./img/bg.png');
background-size: 100% 100%;
box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
}
.block-left-right-content {
flex: 1;
display: flex;
margin-top: 20px;
}
.block-top-bottom-content {
flex: 1;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-left: 20px;
}
.block-top-content {
height: 55%;
display: flex;
flex-grow: 0;
box-sizing: border-box;
padding-bottom: 20px;
}
}
#ranking-board {
width: 75%;
// box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
// background-color: rgba(6, 30, 93, 0.5);
// border-top: 2px solid rgba(1, 153, 209, 0.5);
box-sizing: border-box;
padding: 0px 30px;
padding-bottom: 30%;
.ranking-board-title {
font-weight: bold;
height: 50px;
display: flex;
align-items: center;
font-size: 20px;
}
}
#tank-board {
width: 100%;
height: 100%;
// box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
// background-color: rgba(6, 30, 93, 0.5);
// border-top: 2px solid rgba(1, 153, 209, 0.5);
box-sizing: border-box;
padding: 0px 30px;
padding-bottom: 2%;
.ranking-board-title {
font-weight: bold;
height: 50px;
display: flex;
align-items: center;
font-size: 20px;
}
}
#order-board {
width: 100%;
height: 100%;
// box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
// background-color: rgba(6, 30, 93, 0.5);
border-top: 2px solid rgba(1, 153, 209, 0.5);
box-sizing: border-box;
padding: 0px 30px;
padding-bottom: 2%;
.ranking-board-title {
font-weight: bold;
height: 50px;
display: flex;
align-items: center;
font-size: 20px;
}
}
.bar-row{
height: 50%;
}
#top-header {
position: relative;
width: 100%;
height: 100px;
display: flex;
justify-content: space-between;
flex-shrink: 0;
border-bottom: 2px solid rgba(1, 153, 209, 0.5);
.header-center-decoration {
width: 40%;
height: 60px;
margin-top: 30px;
}
.header-left-decoration,
.header-right-decoration {
width: 25%;
height: 60px;
}
.center-title {
position: absolute;
font-size: 30px;
font-weight: bold;
left: 4%;
top: 15px;
transform: translateX(-50%);
}
}
@import '~antd/es/style/themes/default.less';
.rankingList {
margin: 0 0 0;
padding: 0;
list-style: none;
li {
display: flex;
align-items: center;
margin-top: 16px;
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
span {
color: @text-color;
font-size: 14px;
line-height: 22px;
}
.rankingItemNumber {
display: inline-block;
width: 20px;
height: 20px;
margin-top: 1.5px;
margin-right: 16px;
font-weight: 600;
font-size: 12px;
line-height: 20px;
text-align: center;
background-color: @tag-default-bg;
border-radius: 20px;
&.active {
color: #fff;
background-color: #314659;
}
}
.rankingItemTitle {
flex: 1;
margin-right: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.topCard {
padding-top: 2%;
border-bottom: 2px solid rgba(1, 153, 209, 0.5);
.salesBar {
padding: 0 0 32px 32px;
}
.salesRank {
padding: 0 32px 32px 72px;
}
:global {
.ant-tabs-bar,
.ant-tabs-nav-wrap {
padding-left: 16px;
.ant-tabs-nav .ant-tabs-tab {
padding-top: 16px;
padding-bottom: 14px;
line-height: 24px;
}
}
.ant-tabs-extra-content {
padding-right: 24px;
line-height: 55px;
}
.ant-card-head {
position: relative;
}
.ant-card-head-title {
align-items: normal;
}
}
}
.topCardExtra {
height: inherit;
}
@media screen and (max-width: @screen-lg) {
.salesExtra {
display: none;
}
.rankingList {
li {
span:first-child {
margin-right: 8px;
}
}
}
}
@media screen and (max-width: @screen-md) {
.rankingTitle {
margin-top: 16px;
}
.topCard .salesBar {
padding: 16px;
}
}
@media screen and (max-width: @screen-sm) {
.salesExtraWrap {
display: none;
}
.topCard {
:global {
.ant-tabs-content {
padding-top: 30px;
}
}
}
}
.materialPie{
position: flex;
left: 0;
top: 0px;
width: 100%;
height: 30%;
user-select: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
padding: 0px;
margin: 0px;
border-width: 0px;
}
.tankMaterialBar{
position: relative!important;
left: 16%;
top: 0px;
width: 100%;
height: 42vh!important;
user-select: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
padding: 0px;
margin: 0px;
border-width: 10px;
}
.ant-pro-table-toolbar{
height: 0!important;
}
.my-ant-pro-table{
width:90%
}