1741 lines
43 KiB
JavaScript
1741 lines
43 KiB
JavaScript
|
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);
|