rtgk-screen-web/pages/screen/planDailyBoard/index.js

1741 lines
43 KiB
JavaScript
Raw Permalink Normal View History

2024-06-20 11:26:44 +08:00
import React, {
useRef,
useEffect,
useState,
useCallback,
useMemo,
} from "react";
import moment from "moment";
import _ from "lodash";
import Footer from "../../../components/screen/Footer";
import Header from "../../../components/screen/Header";
import Row from "../../../components/screen/Row";
import Card from "../../../components/screen/Card";
import Chart from "../../../components/screen/Chart";
import io from "../../../utils/socket.io.js";
import { getDailyBoard } from "../../../reportUtils/getQueryData";
import { parse } from "postcss";
const gutter = 20;
const bottom = 20;
const socketEmit = "timePerformanceGoodProduct";
const socketScreenType = "DailyPlanType";
// 调试方式: local 本地完整数据调试, server 接口调试, dev 开发期调试
const dataType = "server";
function PlanDailyBoard(props) {
const { config: { ioSocketUrl, planDailyConfig = {} } = {} } = global;
const { reloadTime = 5000, isSocket = true } = planDailyConfig;
// 月份
const [titlePrev, setTitlePrev] = useState("");
// 页面数据
const [pageData, setPageData] = useState(dataType === "dev" ? devData : {});
const socket = useRef();
// 当前时间
const curDateOpt = useMemo(() => {
const { curDate } = pageData;
if (curDate) {
setTitlePrev(`${moment(curDate).format("M")}`);
return moment(curDate).format("YYYY-MM-DD");
} else return "";
}, [pageData.curDate]);
const materialInventoryAvailableDaysOpt = useMemo(() => {
const { materialInventoryAvailableDays } = pageData;
return materialInventoryAvailableDays;
}, [pageData.materialInventoryAvailableDays]);
// 当前时间
const goalAchievementRateDateOpt = useMemo(() => {
const { goalAchievementRateDate } = pageData;
if (!goalAchievementRateDate) return curDateOpt;
return goalAchievementRateDate;
}, [pageData.goalAchievementRateDate]);
// 上个月
const lastYearMonthOpt = useMemo(() => {
const { lastYearMonth } = pageData;
if (lastYearMonth) {
return lastYearMonth;
} else return "";
}, [pageData.lastYearMonth]);
// 月总产出
const mouthProdChart = useRef();
const mouthProdOpt = useMemo(
() => chartConfig("mouthProd").init(pageData.mouthProd),
[pageData]
);
// 月生产目标达成
const mouthAchieveChart = useRef();
const mouthAchieveOpt = useMemo(
() => chartConfig("mouthAchieve").init(pageData.mouthAchieve),
[pageData]
);
// 出货当月
const mouthOutputMemo = useMemo(
() =>
pageData.mouthOutput || {
total: 0,
totalRate: 0,
data: [],
names: [],
rates: [],
},
[pageData]
);
// 出货汇总
const totalOutputChart = useRef();
const totalOutputOpt = useMemo(
() => chartConfig("totalOutput").init(pageData.totalOutput),
[pageData]
);
// 生产目标达成
const prodPlanAchieveChart = useRef();
const prodPlanAchieveOpt = useMemo(
() => chartConfig("prodPlanAchieve").init(pageData.prodPlanAchieve),
[pageData]
);
// 原材料库存
const rawMaterialInventoryChart = useRef();
const rawMaterialInventoryOpt = useMemo(
() =>
chartConfig("rawMaterialInventory").init(pageData.rawMaterialInventory),
[pageData]
);
// 产品库存
const prodInventory = useRef();
const prodInventoryOpt = useMemo(() => {
let option = chartConfig("prodInventory").init(pageData.prodInventory);
option && (option.getRes = () => pageData.prodInventory);
return option;
}, [pageData]);
const prodInventoryTableOpt = useMemo(() => {
let { category = [], data = {} } = prodInventoryOpt?.getRes() || {};
let res = { cloumns: [...category, "合计"], data: [] };
let rowData = [];
let total = 0;
// Object.keys(data).forEach((key) => {
// data[key].forEach((val, index) => {
// if (rowData[index] === undefined || isNaN(rowData[index]))
// rowData[index] = 0;
// rowData[index] += val === undefined ? 0 : val;
// let ccval = parseFloat(parseFloat(rowData[index]).toFixed(0));
// rowData[index] = isNaN(ccval) ? 0 : ccval;
// total += val === undefined ? 0 : val;
// let tptalccval = parseFloat(parseFloat(total).toFixed(0));
// total = isNaN(tptalccval) ? 0 : tptalccval;
// });
// });
pageData.productInventoryTable?.forEach((item, index) => {
if (rowData[index] === undefined || isNaN(rowData[index])) {
rowData[index] = 0;
}
rowData[index] = parseFloat(parseFloat(item.inventoryQty).toFixed(0));
total += item.inventoryQty;
});
total = parseFloat(parseFloat(total).toFixed(0));
rowData.push(<span className="c-red">{total}</span>);
res.data[0] = rowData;
return res;
}, [prodInventoryOpt]);
// 历史库存
const historyInventory = useRef();
const historyInventoryOpt = useMemo(
() => chartConfig("historyInventory").init(pageData.historyInventory),
[pageData]
);
// 数据更新
useEffect(() => {
// 月总产出
mouthProdOpt && mouthProdChart.current.setOption(mouthProdOpt);
// 月目标达成
mouthAchieveOpt && mouthAchieveChart.current.setOption(mouthAchieveOpt);
// 出货汇总
totalOutputOpt && totalOutputChart.current.setOption(totalOutputOpt);
// 生产目标达成
prodPlanAchieveOpt &&
prodPlanAchieveChart.current.setOption(prodPlanAchieveOpt);
// 原材料库存
rawMaterialInventoryOpt &&
rawMaterialInventoryChart.current.setOption(rawMaterialInventoryOpt);
// 产品库存
prodInventoryOpt && prodInventory.current.setOption(prodInventoryOpt);
// 历史库存
historyInventoryOpt &&
historyInventory.current.setOption(historyInventoryOpt);
}, [
mouthProdOpt,
mouthAchieveOpt,
totalOutputOpt,
prodPlanAchieveOpt,
rawMaterialInventoryOpt,
prodInventoryOpt,
historyInventoryOpt,
]);
// 收到数据
const update = useCallback((data) => {
if (dataType === "local") data = localDataParse;
console.log("data", data);
let consoleError = (e, name, key) => {
console.log("--------------", name, " error", e);
console.log("localData: --------------", localDataParse[key]);
console.log("requestData: --------------", data[key]);
};
// 月总产出
let mouthProd;
try {
let res = {
category: [],
data: { prod: [], pect: [] },
};
let { goalAchievementDiagram = [] } = data;
goalAchievementDiagram
.sort((a, b) => b.productName.localeCompare(a.productName))
.forEach(({ productName, achievingRate, actualQty }) => {
res.category.push(productName);
res.data.prod.push(actualQty);
res.data.pect.push(achievingRate);
});
mouthProd = res;
} catch (e) {
consoleError(e, "月总产出", "goalAchievementDiagram");
}
// 月生产目标达成
let mouthAchieve;
try {
let res = {
category: [],
data: { plan: [], infect: [], achievement: [] },
};
let { goalAchievementHistogram = [] } = data;
var goalAchievementHistogramtmp = goalAchievementHistogram;
var llist = goalAchievementHistogramtmp.filter(
(x) => x.workLineName.substring(0, 1) === "L"
);
var otherlist = goalAchievementHistogramtmp.filter(
(x) => x.workLineName.substring(0, 1) !== "L"
);
llist.push(...otherlist);
var finallist = llist;
finallist.forEach(
({ workLineName, goalQty, actualQty, monthGoalQty }) => {
res.category.push(workLineName);
res.data.infect.push(actualQty);
res.data.plan.push(monthGoalQty);
res.data.achievement.push(goalQty === 0 ? 0 : actualQty / goalQty);
}
);
mouthAchieve = res;
} catch (e) {
consoleError(e, "月生产目标达成", "goalAchievementHistogram");
}
// 出货当月
let mouthOutput;
try {
let res = {
total: 0,
totalRate: 0,
data: [],
names: [],
rates: [],
};
let { shipmentDashBoard = {} } = data;
let { summary = {} } = shipmentDashBoard;
let { totalShipmentQty = 0, dtl = [], totalAchievingRate } = summary;
res.total = totalShipmentQty.toFixed(0);
res.totalRate = totalAchievingRate;
dtl
.sort((a, b) => b.customerName.localeCompare(a.customerName))
.forEach(({ customerName, shipmentQty, achievingRate, goalQty }) => {
res.names.push(customerName);
res.data.push(shipmentQty.toFixed(0));
res.rates.push(achievingRate);
});
mouthOutput = res;
// console.log("mouthOutput", mouthOutput, summary);
} catch (e) {
consoleError(e, "出货当月", "shipmentDashBoard");
}
// 出货汇总
let totalOutput;
try {
let { shipmentDashBoard = {} } = data;
let { shipmentPie = [] } = shipmentDashBoard;
totalOutput = shipmentPie.map(({ customerName: name, rate: value }) => ({
name,
value,
}));
//演示代码,后面要删除
// totalOutput.forEach((item) => {
// if (item.name === "C客户") {
// item.value = 0.78;
// } else if (item.name === "非C客户") {
// item.value = 0.9;
// }
// });
console.log("-----------totalOutput", totalOutput);
} catch (e) {
consoleError(e, "出货汇总", "shipmentPie");
}
// 生产目标达成
let prodPlanAchieve;
try {
let res = {
category: [],
data: { reach: [] },
};
let { goalAchievementRate = [] } = data;
goalAchievementRate.forEach(({ workCenterName, achievingRate }) => {
res.category.push(workCenterName);
res.data.reach.push(achievingRate);
});
prodPlanAchieve = res;
} catch (e) {
consoleError(e, "生产目标达成", "goalAchievementRate");
}
// 原材料库存
let rawMaterialInventory;
try {
let res = {
category: [],
names: [],
data: [],
};
let { materialInventoryDiagram = [] } = data;
let [data0, data1] = materialInventoryDiagram;
let dates = _.cloneDeep(data0.inventoryDate);
res.category = dates || [];
materialInventoryDiagram.forEach((item) => {
res.names.push(item.materialName);
res.data.push(item.inventoryQty);
});
rawMaterialInventory = res;
// }
} catch (e) {
consoleError(e, "原材料库存", "materialInventoryDiagram");
}
// 产品库存
let prodInventory;
try {
let res = {
category: [],
data: { qualified: [], review: [], inspecting: [], inspected: [] },
};
let { productInventoryHistogram = [] } = data;
productInventoryHistogram.forEach(
({
productName,
okQty,
reviewQty,
pendingInspectionQty,
inspectionQty,
}) => {
res.category.push(productName);
res.data.qualified.push(okQty);
res.data.review.push(reviewQty);
res.data.inspecting.push(pendingInspectionQty);
res.data.inspected.push(inspectionQty);
}
);
prodInventory = res;
} catch (e) {
consoleError(e, "产品库存", "productInventoryHistogram");
}
// 历史库存
let historyInventory;
try {
let res = {
category: [],
data: [],
};
let { historyInventory: history = [] } = data;
history.forEach(({ inventoryDate, inventoryQty },index) => {
//只保留8天的数据
//只保留8天所以计算下
let ca=history.length- 8 ;
if (index<ca) {
return;
}
res.category.push(inventoryDate);
res.data.push(inventoryQty);
});
historyInventory = res;
} catch (e) {
consoleError(e, "历史库存", "historyInventory");
}
let resData = {
curDate: data.curDate,
goalAchievementRateDate: data?.goalAchievementRateDate,
productInventoryTable: data?.productInventoryTable,
lastYearMonth: data?.lastYearMonth,
materialInventoryAvailableDays: data?.materialInventoryAvailableDays,
mouthProd,
mouthAchieve,
mouthOutput,
totalOutput,
prodPlanAchieve,
rawMaterialInventory,
prodInventory,
historyInventory,
};
console.log("resData", resData);
setPageData(resData);
}, []);
// 获取数据
const timeoutRef = useRef();
const getData = useCallback(() => {
// let workCenterCode = getQueryVariable("workCenterCode");
let date = getQueryVariable("date");
// if (!workCenterCode) return;
let query = {
// workCenterCode,
screenType: socketScreenType,
};
if (date) query.date = date;
console.log("fetch", socketEmit, { query });
if (dataType === "server") {
if (isSocket) {
socket.current.emit(socketEmit, "channel", { query });
} else {
getDailyBoard(date)
.then((res) => {
update(JSON.stringify(res));
})
.catch((err) => console.log(err));
}
} else if (dataType === "local") {
update();
}
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(getData, reloadTime);
}, []);
// 初始化加载
useEffect(() => {
if (dataType === "server") {
socket.current = io.connect(ioSocketUrl, {transports:['websocket']});
socket.current.on("connect", function () {
console.log("msg页面连接成功");
getData();
});
if (isSocket)
socket.current.on(socketEmit, function (data) {
try {
data = JSON.parse(data);
} catch (e) {
console.log(e);
}
update(data.data);
});
} else if (dataType === "local") {
setTimeout(getData, 1000);
}
}, [isSocket]);
useEffect(() => {
return () => {
clearTimeout(timeoutRef.current);
};
}, []);
return (
<div className="screen-container">
<Header title={"计划日报看板"} time={curDateOpt} />
<div className="screen-container-content">
<Row style={{ height: "100%" }} gutter={gutter} bottom={bottom}>
<Row.Col style={{ width: "504px" }}>
<Row>
<Row.Col style={{ height: "268px", marginBottom: `${bottom}px` }}>
<Card
title={`${titlePrev}总产出(t)`}
titleSpace={false}
overVisible={true}
>
<Chart ref={mouthProdChart} />
</Card>
</Row.Col>
<Row.Col style={{ height: "604px" }}>
<Card
title={`${titlePrev}生产目标达成`}
titleSpace={false}
overVisible={true}
>
<Chart ref={mouthAchieveChart} />
</Card>
</Row.Col>
</Row>
</Row.Col>
<Row.Col style={{ width: "892px" }}>
<Row>
<Row.Col style={{ height: "210px", marginBottom: `${bottom}px` }}>
<Card
title={`${titlePrev}总出货t`}
titleSpace={true}
overVisible={true}
withBorder={false}
withBg={false}
>
<Row style={{ height: "100%" }}>
<Row.Col className="left" style={{ width: "60%" }}>
<div className="title">当月总交付情况</div>
<Row>
<Row.Col span={12}>
<NumCard data={mouthOutputMemo.total} />
<div className="monthInfo-total">
<div className="box">
进度达成
{parseFloat(
(mouthOutputMemo.totalRate * 100).toFixed(1)
)}
%
</div>
</div>
</Row.Col>
<Row.Col span={12}>
<div className="monthInfo">
<div className="item">
<span className="title">
{mouthOutputMemo.names[0]}交货
</span>
<span className="num c-red">
{mouthOutputMemo.data[0]}
</span>
</div>
<div className="item m-b">
<span className="title">进度达成</span>
<span className="num">
{parseFloat(
(mouthOutputMemo.rates[0] * 100).toFixed(1)
)}
%
</span>
</div>
<div className="item">
<span className="title">
{mouthOutputMemo.names[1]}交货
</span>
<span className="num">
{mouthOutputMemo.data[1]}
</span>
</div>
<div className="item">
<span className="title">进度达成</span>
<span className="num">
{parseFloat(
(mouthOutputMemo.rates[1] * 100).toFixed(1)
)}
%
</span>
</div>
</div>
</Row.Col>
</Row>
</Row.Col>
<Row.Col className="right" style={{ width: "40%" }}>
<div className="title pl-[42px] pr-[25px] text-center">
1-{lastYearMonthOpt}月汇总
</div>
<Chart ref={totalOutputChart} />
</Row.Col>
</Row>
</Card>
</Row.Col>
<Row.Col style={{ height: "304px", marginBottom: `${bottom}px` }}>
<Card
title={goalAchievementRateDateOpt + " 生产目标达成"}
titleSpace={true}
overVisible={true}
>
<Chart ref={prodPlanAchieveChart} />
</Card>
</Row.Col>
<Row.Col style={{ height: "338px" }}>
<Card
title="原材料库存情况(t)"
titleSpace={false}
overVisible={true}
>
<div className=" ml-44 text-lg absolute text-white">
{materialInventoryAvailableDaysOpt !== undefined ? (
Object.keys(materialInventoryAvailableDaysOpt).map(
(item, index) => {
console.log(item);
return (
<span>
{item}可用
<span className="m-2 text-xl text-red-600">
{materialInventoryAvailableDaysOpt[item]}
</span>
{index === 0 ? "" : ""}
</span>
);
}
)
) : (
<></>
)}
</div>
<Chart ref={rawMaterialInventoryChart} />
</Card>
</Row.Col>
</Row>
</Row.Col>
<Row.Col style={{ width: "504px" }}>
<Row>
<Row.Col style={{ height: "436px", marginBottom: `${bottom}px` }}>
<Card title="产品库存情况" titleSpace={true} overVisible={true}>
<div style={{ height: "20%", padding: "16px 20px 0" }}>
<SimpleTable {...prodInventoryTableOpt} />
</div>
<div style={{ height: "80%" }}>
<Chart ref={prodInventory} />
</div>
</Card>
</Row.Col>
<Row.Col style={{ height: "436px" }}>
<Card title="历史库存情况" titleSpace={true} overVisible={true}>
<Chart ref={historyInventory} />
</Card>
</Row.Col>
</Row>
</Row.Col>
</Row>
</div>
{/* <Footer withTime={false} showDate={curDateOpt} /> */}
<style jsx>{`
.screen-container {
height: 100vh;
min-height: 1080px;
min-width: 1920px;
overflow: auto;
background: url("/img/bg.png") center center;
position: relative;
padding-top: 96px;
padding-bottom: 82px;
overflow: hidden;
}
.screen-container-content {
height: 100%;
padding: 0 ${gutter}px;
}
.title {
font-size: 14px;
color: #fff;
line-height: 22px;
padding-top: 16px;
margin-bottom: 6px;
}
.monthInfo-total {
font-size: 18px;
font-weight: bold;
line-height: 28px;
text-align: center;
padding: 8px 4px 0;
}
.monthInfo-total .box {
background: rgba(36, 53, 184, 0.5);
color: #00f7fb;
}
.monthInfo {
border-right: 2px solid rgba(255, 255, 255, 0.2);
}
.monthInfo .item.m-b {
margin-bottom: 14px;
}
.monthInfo .num,
.monthInfo .title {
line-height: 30px;
font-size: 18px;
display: inline-block;
vertical-align: middle;
}
.monthInfo .title {
text-align: right;
width: 130px;
padding: 0;
margin: 0;
}
.monthInfo .num {
font-weight: bold;
margin-left: 16px;
}
.monthInfo .num:not(.c-red) {
color: #00f7fb;
}
`}</style>
<style jsx global>{`
.c-red {
color: #ff005a;
}
`}</style>
</div>
);
}
function NumCard(props) {
let { data = 0 } = props;
const dataArr = useMemo(() => {
let res = data + "";
res = res.split("");
while (res.length < 4) res.unshift("");
return res;
}, [data]);
return (
<div className="num-card">
{dataArr.map((num, index) => (
<div className="num-card-item" key={index}>
<div className="text">{num}</div>
</div>
))}
<style jsx>{`
.num-card {
height: 100px;
line-height: 100px;
text-align: center;
color: #00f7fb;
font-size: 44px;
font-weight: bold;
}
.num-card-item {
float: left;
height: 100%;
width: ${100 / dataArr.length}%;
position: relative;
padding: 0 4px;
}
.num-card-item::before {
content: "";
display: block;
background: linear-gradient(
to bottom,
rgba(0, 76, 167, 0.2),
rgba(36, 53, 184, 0.5)
);
height: 100%;
width: 100%;
}
.text {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
`}</style>
</div>
);
}
function SimpleTable(props) {
let { cloumns = [], data = [] } = props;
return (
<div className="simple-table">
<div className="simple-table-header row">
{cloumns.map((text) => {
return (
<div key={text} className="item">
{text}
</div>
);
})}
</div>
<div className="simple-table-body">
{data.map((rowData, index) => {
return (
<div key={index} className="row">
{rowData.map((text) => {
return (
<div key={text} className="item">
{text}
</div>
);
})}
</div>
);
})}
</div>
<style jsx>{`
.simple-table {
line-height: 32px;
text-align: center;
font-size: 14px;
}
.simple-table-header {
overflow: hidden;
background: rgba(40, 163, 255, 25%);
color: rgba(255, 255, 255, 85%);
}
.simple-table-body {
overflow: hidden;
background: rgba(40, 163, 255, 10%);
color: #fff;
}
.simple-table .item {
width: ${100 / (cloumns.length || 1)}%;
float: left;
}
.simple-table .row:not(:last-child) {
border-bottom: 1px solid;
}
.simple-table .item:not(:last-child) {
border-right: 1px solid;
}
.simple-table .row:not(:last-child),
.simple-table .item:not(:last-child) {
border-color: #09213d;
}
`}</style>
</div>
);
}
const getQueryVariable = (variable) => {
let query = window.location.search.substring(1);
let vars = query.split("&");
for (let i = 0; i < vars.length; i++) {
let pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return false;
};
export default PlanDailyBoard;
// 月总产出
let mouthProd = {
grid: {
bottom: 30,
top: 60,
left: 60,
right: 70,
},
legend: {
top: 15,
right: 10,
textStyle: {
fontSize: 14,
},
},
xAxis: {
type: "category",
data: [],
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisTick: {
show: true,
alignWithLabel: true,
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
yAxis: [
{
type: "value",
min: 0,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
{
type: "value",
min: 0,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
formatter: (value) => `${100 * value}%`,
},
},
],
series: [
{
data: [],
type: "bar",
barWidth: 28,
name: "实际",
itemStyle: {
color: "#2D99FF",
},
label: {
show: true,
align: "center",
width: 200,
position: ["50%", "50%"],
textBorderColor: "transparent",
color: "#fff",
},
},
{
data: [],
yAxisIndex: 1,
type: "line",
name: "达成率",
itemStyle: {
color: "#F5A623",
},
label: {
show: true,
position: "top",
textBorderColor: "transparent",
color: "#F5A623",
formatter: (data) => `${parseInt(100 * data.value)}%`,
},
},
],
};
mouthProd.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(mouthProd);
let { category, data } = res;
let { pect, prod } = data;
prod = prod.map((i) => parseInt(i));
pect = pect.map((i) => i);
// 分类
opt.xAxis.data = category;
// 实际生产数据
opt.series[0].data = prod;
// 达成率
opt.series[1].data = pect;
return opt;
};
// 月生产目标达成
let mouthAchieve = {
legend: {
top: 15,
right: 10,
textStyle: {
fontSize: 14,
},
},
barWidth: 10,
barGap: "40%",
grid: {
bottom: 40,
top: 60,
left: 60,
right: 70,
},
xAxis: {
type: "value",
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
yAxis: {
type: "category",
inverse: true,
axisTick: {
show: true,
alignWithLabel: true,
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
data: [],
},
series: [
{
name: "目标",
type: "bar",
data: [],
label: {
show: true,
position: "right",
textBorderColor: "transparent",
color: "#fff",
},
itemStyle: {
color: "#2D99FF",
},
},
{
name: "实际",
type: "bar",
data: [],
label: {
show: true,
position: "right",
textBorderColor: "transparent",
color: "#fff",
},
itemStyle: {
color: "#6FE621",
},
},
{
name: "达成率",
type: "line",
data: [],
symbolSize: 0,
lineStyle: {
width: 0,
},
label: {
show: true,
position: "right",
textBorderColor: "transparent",
offset: [30, 0],
color: "#F6BD16",
},
itemStyle: {
color: "#F6BD16",
},
},
],
};
mouthAchieve.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(mouthAchieve);
let { category, data } = res;
let { achievement, infect, plan } = data;
// 分类
opt.yAxis.data = category;
// 目标 数据
opt.series[0].data = plan.map((i) => parseFloat(i));
// 实际 数据
opt.series[1].data = infect.map((i) => parseFloat(i));
// 达成率 数据
opt.series[2].data = plan
.map((planVal, index) => Math.max(planVal, infect[index]))
.map((i) => parseFloat(i));
opt.series[2].label.formatter = ({ dataIndex }) =>
` ${(100 * achievement[dataIndex]).toFixed(0)}%`;
return opt;
};
// 出货汇总
let totalOutputBase = {
type: "gauge",
startAngle: 90,
radius: "70%",
max: 1,
endAngle: -270,
pointer: {
show: false,
},
axisLine: {
lineStyle: {
width: 16,
color: [[1, "#28A3FF"]],
opacity: 0.15,
},
},
splitLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
progress: {
show: true,
overlap: false,
clip: true,
itemStyle: {
color: "#2D99FF",
},
},
title: {
fontSize: 14,
offsetCenter: [0, -14],
color: "#fff",
},
detail: {
width: 50,
height: 14,
fontSize: 20,
formatter: (val) => `${parseInt(100 * val)}%`,
valueAnimation: true,
offsetCenter: [0, 10],
color: "#fff",
},
};
let totalOutput = {
series: [
Object.assign({ center: ["30%", "36%"] }, totalOutputBase),
Object.assign({ center: ["75%", "36%"] }, totalOutputBase),
],
};
totalOutput.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(totalOutput);
let [res0, res1] = res;
opt.series[0].data = [res0];
opt.series[1].data = [res1];
return opt;
};
// 生产目标达成
let prodPlanAchieve = {
grid: {
bottom: 40,
top: 30,
left: 70,
right: 40,
},
xAxis: {
type: "category",
data: [],
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisTick: {
show: true,
alignWithLabel: true,
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
yAxis: {
type: "value",
max: 1,
axisLabel: {
color: "rgba(255,255,255,0.85)",
formatter: (value) => `${parseInt(100 * value)}%`,
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
},
series: {
data: [],
type: "bar",
barWidth: 28,
showBackground: true,
itemStyle: {
color: "#6FE621",
},
label: {
show: true,
position: "top",
textBorderColor: "transparent",
color: "#fff",
formatter: (data) => `${parseInt(100 * data.value)}%`,
},
backgroundStyle: {
color: "rgba(45, 153, 255, 0.3)",
},
},
};
prodPlanAchieve.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(prodPlanAchieve);
let { category, data } = res;
let { reach } = data;
opt.xAxis.data = category;
opt.series.data = reach.map((i) => parseFloat(i.toFixed(2)));
return opt;
};
// 原材料库存
let rawMaterialInventory = {
grid: {
bottom: 40,
top: 80,
left: 70,
right: 70,
},
legend: {
top: 15,
right: 10,
textStyle: {
fontSize: 14,
},
},
xAxis: {
type: "category",
data: [],
axisTick: {
show: true,
alignWithLabel: true,
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
interval: 0,
// rotate:45,
formatter: function (value) {
//x轴的文字改为竖版显示
// var str = value.split("");
return value.replace("-", "/");
},
},
},
yAxis: [
{
type: "value",
// splitNumber: 3,
axisTick: {
show: false,
},
axisLine: {
show: false,
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
formatter: (value) => parseFloat(value.toFixed(1)),
},
},
{
type: "value",
// splitNumber: 3,
axisTick: {
show: false,
},
axisLine: {
show: false,
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
formatter: (value) => parseFloat(value.toFixed(1)),
},
},
],
series: [
{
name: "",
type: "line",
symbol: "rect",
symbolSize: 0,
smooth: 0.5,
lineStyle: {
width: 0,
},
areaStyle: {
opacity: 0.6,
color: "#2D99FF",
},
data: [],
label: {
show: true,
position: "insideTop",
textBorderColor: "transparent",
color: "#fff",
// padding:[100,0,0,0], //调整上的位置
},
},
{
name: "",
type: "line",
yAxisIndex: 1,
label: {
show: true,
position: "top",
textBorderColor: "transparent",
color: "#F5A623",
},
data: [],
},
],
};
rawMaterialInventory.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(rawMaterialInventory);
let { category, names, data } = res;
opt.xAxis.data = category.map((date) => moment(date).format("M-D"));
opt.series[0].data = data[0].map((i) => {
if (i === null) return i;
return parseFloat(i.toFixed(1));
});
opt.series[0].name = names[0] + " 库存量";
data[1] &&
(opt.series[1].data = data[1].map((i) => {
if (i === null) return i;
return parseFloat(i.toFixed(1));
}));
names[1] && (opt.series[1].name = names[1] + " 库存量");
return opt;
};
// 产品库存
let prodInventory = {
grid: {
bottom: 40,
top: 60,
left: 70,
right: 30,
},
barWidth: 32,
legend: {
top: 15,
right: 10,
textStyle: {
fontSize: 14,
},
},
xAxis: {
type: "category",
data: [],
axisTick: {
show: true,
alignWithLabel: true,
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
yAxis: {
type: "value",
axisTick: {
show: true,
alignWithLabel: true,
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
series: [
{
name: "合格数量",
type: "bar",
stack: "Ad",
data: [],
itemStyle: {
color: "#6FE621",
},
label: {
show: true,
position: "inside",
textBorderColor: "transparent",
color: "#fff",
},
},
{
name: "待评审数量",
type: "bar",
stack: "Ad",
data: [],
itemStyle: {
color: "#2D99FF",
},
label: {
show: true,
position: "inside",
textBorderColor: "transparent",
color: "#fff",
},
},
{
name: "在检未出数据",
type: "bar",
stack: "Ad",
data: [],
itemStyle: {
color: "#F6BD16",
},
label: {
show: true,
position: "inside",
textBorderColor: "transparent",
color: "#fff",
},
},
{
name: "已评审待处理",
type: "bar",
stack: "Ad",
data: [],
itemStyle: {
color: "#95D8B9",
},
label: {
show: true,
position: "inside",
textBorderColor: "transparent",
color: "#fff",
},
},
],
};
prodInventory.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(prodInventory);
let { category, data } = res;
let { qualified, review, inspecting, inspected } = data;
opt.xAxis.data = category;
opt.series[0].data = qualified;
opt.series[1].data = review;
opt.series[2].data = inspecting;
opt.series[3].data = inspected;
return opt;
};
// 历史库存
let historyInventory = {
grid: {
bottom: 40,
top: 30,
left: 70,
right: 30,
},
barWidth: 16,
xAxis: {
type: "category",
data: [],
axisTick: {
show: true,
alignWithLabel: true,
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
yAxis: {
type: "value",
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.65)",
},
},
axisLabel: {
color: "rgba(255,255,255,0.85)",
},
},
series: [
{
name: "碳酸铁 库存量",
type: "bar",
data: [],
itemStyle: {
color: "#F6BD16",
},
label: {
show: true,
position: "top",
textBorderColor: "transparent",
color: "#fff",
},
},
],
};
historyInventory.init = (res) => {
if (!res) return;
let opt = _.cloneDeep(historyInventory);
let { category, data } = res;
opt.xAxis.data = category.map((date) => moment(date).format("M/D"));
opt.series[0].data = data.map((i) => parseFloat(i.toFixed(0)));
return opt;
};
let chartConfig = (() => {
let chartOpt = {
mouthProd,
mouthAchieve,
totalOutput,
prodPlanAchieve,
rawMaterialInventory,
prodInventory,
historyInventory,
};
let getOpt = (type) => chartOpt[type];
return getOpt;
})();
// dev 数据
let dates1 = [];
for (let i = 0; i < 20; i++) dates1.push(`7-${i + 1}`);
let dates2 = [];
for (let i = 0; i < 10; i++) dates2.push(`7-${i + 1}`);
const devData = {
curDate: "2022-08-03",
mouthProd: {
category: ["磷酸锂铁", "磷酸铁", "高镍三元"],
data: {
prod: [1269, 452, 852],
pect: [0.89, 0.56, 0.763],
},
},
mouthAchieve: {
category: [
"L1-A",
"L1-B",
"L2-A",
"L2-B",
"L3-B",
"L3-B",
"L4-A",
"L4-B",
"L5-A",
"L5-B",
"L6-B",
"L6-B",
"F1",
"F2",
"F3",
"N1",
],
data: {
plan: [
890, 600, 580, 720, 580, 580, 580, 890, 600, 580, 720, 580, 580, 890,
600, 580,
],
infect: [
600, 200, 600, 384, 799, 600, 600, 600, 200, 600, 384, 799, 600, 600,
200, 600,
],
achievement: [
0.6123, 0.6356, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6,
0.6, 0.6, 0.6,
],
},
},
mouthOutput: {
total: 319 + 271,
totalRate: 0.12,
data: [319, 271],
names: ["C公司", "非C公司"],
rates: [0.12, 0.12],
},
totalOutput: [
{ value: 0.234, name: "C公司" },
{ value: 0.234, name: "非C公司" },
],
prodPlanAchieve: {
category: ["L1", "L2", "L3", "L4", "L5", "L6", "F1", "F2", "F3", "N1"],
data: {
reach: [0.79, 0.4, 1.09, 0.33, 0.4, 0.56, 0.4, 0.86, 0.49, 0.79],
},
},
rawMaterialInventory: {
category: dates1,
names: ["碳酸铁", "碳酸锂"],
data: [
[
3900,
4900,
null,
4900,
4300,
3900,
null,
5100,
null,
4300,
3900,
4900,
5100,
4900,
4300,
4900,
5100,
null,
4300,
4300,
],
[
5100,
6200,
6400,
5500,
null,
6200,
6400,
5500,
5100,
null,
6400,
null,
5100,
6200,
6400,
5500,
5100,
null,
6400,
6200,
],
],
},
prodInventory: {
category: ["E80", "E80A", "E70", "E90", "W80S"],
data: {
qualified: [3900, 4900, 5100, 4900, 4300],
review: [5100, 6200, 6400, 5500, 5100],
inspecting: [5100, 6200, 6400, 5500, 5100],
inspected: [5100, 6200, 6400, 5500, 5100],
},
},
historyInventory: {
category: dates2,
data: [3900, 4900, 5100, 4900, 4300, 5100, 4900, 4300, 4900, 4300],
},
};
// local 数据
const localDataParse = {
// #当前日期
curDate: "2022-06-03",
// #8月产出
goalAchievementDiagram: [
{
productName: "磷酸铁锂",
achievingRate: 0.57,
actualQty: 1268,
},
],
// #8月份生产目标达成
goalAchievementHistogram: [
{
workLineName: "L1-A",
goalQty: 890,
actualQty: 600,
},
],
// #生产目标达成
goalAchievementRate: [
{
workCenterName: "L1",
achievingRate: 0.57,
},
],
// #产品库存情况
productInventoryTable: [
{
productName: "E80",
inventoryQty: 1000,
},
],
// #产品库存柱状图
productInventoryHistogram: [
{
productName: "E80",
okQty: 600,
reviewQty: 300,
pendingInspectionQty: 100,
},
],
// #原材料库存情况
materialInventoryDiagram: [
{
inventoryDate: ["2022-08-02"],
materialName: "磷酸铁锂",
inventoryQty: [3532.45],
},
{
inventoryDate: ["2022-08-02"],
materialName: "碳酸锂",
inventoryQty: [3932.45],
},
],
// #8月份出货
shipmentDashBoard: {
// #1-7月汇总
shipmentPie: [
{
customerName: "C公司",
rate: 0.76,
},
{
customerName: "非C公司",
rate: 1.76,
},
],
// #当月情况
summary: {
totalShipmentQty: 7339,
totalAchievingRate: 1.11, //进度达成
dtl: [
{
achievingRate: 1.11, //进度达成
customerName: "C公司",
shipmentQty: 4132,
goalQty: 4500.0,
},
{
achievingRate: 1.01, //进度达成
customerName: "非C公司",
shipmentQty: 4132,
goalQty: 4400.0,
},
],
},
},
// #历史库存情况
historyInventory: [
{
inventoryDate: "5/31",
inventoryQty: 200,
},
],
};
const localData = JSON.stringify(localDataParse);