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

1741 lines
43 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);