first commit
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals",
|
||||||
|
"rules": {
|
||||||
|
"react-hooks/exhaustive-deps": "off",
|
||||||
|
"react/display-name": "off",
|
||||||
|
"react/jsx-key": "off",
|
||||||
|
"@next/next/no-sync-scripts": "off",
|
||||||
|
"react/no-unknown-property": [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
"ignore": [
|
||||||
|
"jsx",
|
||||||
|
"global"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
|
@ -0,0 +1,34 @@
|
||||||
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||||
|
|
||||||
|
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
|
@ -0,0 +1,124 @@
|
||||||
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { Spin } from "antd";
|
||||||
|
import { LoadingOutlined } from "@ant-design/icons";
|
||||||
|
import { useRule } from "./PackagingOperation/hooks"
|
||||||
|
|
||||||
|
const btnTyps = [
|
||||||
|
{ type: "primary", color: "#0185FF" },
|
||||||
|
{ type: "danger", color: "#F5222D" },
|
||||||
|
{ type: "warning", color: "#FAAB0C" },
|
||||||
|
{ type: "success", color: "#85C700" },
|
||||||
|
{ type: "info", color: "#26c1e1" },
|
||||||
|
{ type: "default", color: "rgba(0,0,0,.85)" },
|
||||||
|
];
|
||||||
|
export const Button = React.memo((props) => {
|
||||||
|
const {
|
||||||
|
visible = true,
|
||||||
|
enable = true,
|
||||||
|
title,
|
||||||
|
type = "primary",
|
||||||
|
ghost = false,
|
||||||
|
size,
|
||||||
|
onClick = () => { },
|
||||||
|
ruleCode,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const rule = useRule(ruleCode);
|
||||||
|
// useEffect(() => {
|
||||||
|
// console.log("rule", rule)
|
||||||
|
// }, [rule])
|
||||||
|
|
||||||
|
const types = useRef(btnTyps.map(({ type }) => type));
|
||||||
|
|
||||||
|
const sizes = useRef(["large"]);
|
||||||
|
const className = useMemo(() => {
|
||||||
|
let res = "client-btn";
|
||||||
|
if (type && types.current.includes(type)) res += ` btn-${type}`;
|
||||||
|
if (ghost === true) res += ` btn-ghost`;
|
||||||
|
if (size && sizes.current.includes(size)) res += ` btn-${size}`;
|
||||||
|
if (enable === false) res += ` btn-disabled`;
|
||||||
|
return res;
|
||||||
|
}, [type, ghost, size, enable]);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (enable && !loading) {
|
||||||
|
let res = onClick();
|
||||||
|
if (res && typeof res.then == "function" && typeof res.finally == "function") {
|
||||||
|
setLoading(true);
|
||||||
|
res.finally(() => setLoading(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [enable, onClick, loading]);
|
||||||
|
|
||||||
|
if (!rule) return null;
|
||||||
|
if (!visible) return null;
|
||||||
|
return (
|
||||||
|
<div className={className} onClick={() => enable && handleClick()}>
|
||||||
|
{title}
|
||||||
|
<Spin
|
||||||
|
spinning={loading}
|
||||||
|
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
|
||||||
|
/>
|
||||||
|
<style jsx>{`
|
||||||
|
.client-btn {
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
line-height: 44px;
|
||||||
|
padding: 0 24px;
|
||||||
|
font-size: 20px;
|
||||||
|
user-select: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.client-btn.btn-large {
|
||||||
|
line-height: 56px;
|
||||||
|
font-size: 24px;
|
||||||
|
padding: 0 36px;
|
||||||
|
}
|
||||||
|
.client-btn.btn-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
${btnTyps
|
||||||
|
.map((item) => {
|
||||||
|
let { type, color } = item;
|
||||||
|
return `
|
||||||
|
.client-btn.btn-${type} {
|
||||||
|
background: ${color};
|
||||||
|
}
|
||||||
|
.client-btn.btn-ghost.btn-${type} {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid;
|
||||||
|
color: ${color};
|
||||||
|
border-color: ${color};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join("") + "\n"}
|
||||||
|
.client-btn.btn-ghost {
|
||||||
|
}
|
||||||
|
.client-btn.btn-ghost.btn-default {
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<style jsx global>{`
|
||||||
|
.client-btn ~ .client-btn {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
.client-btn .ant-spin {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
background: #ffffff87;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
.client-btn .ant-spin-dot {
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,148 @@
|
||||||
|
import React, {
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
export const FormItem = React.memo((props) => {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
value = "",
|
||||||
|
readonly = true,
|
||||||
|
horizontal = true,
|
||||||
|
type,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const valueMemo = useMemo(() => {
|
||||||
|
if (readonly) {
|
||||||
|
if (["", undefined, null].includes(value)) return "-";
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (type === "number") {
|
||||||
|
return <FormItemNumber value={value + ""} {...otherProps} />;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}, [readonly, value, type, otherProps]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"form-item " + (horizontal ? "horizontal" : "vertical")}>
|
||||||
|
<div className="label">{title}</div>
|
||||||
|
<div className="value">{valueMemo}</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.form-item {
|
||||||
|
line-height: 36px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.form-item.horizontal {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
.form-item.horizontal .label,
|
||||||
|
.form-item.horizontal .value {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.form-item.horizontal .label {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
.form-item.vertical {
|
||||||
|
height: 88px;
|
||||||
|
}
|
||||||
|
.form-item.vertical .label,
|
||||||
|
.form-item.vertical .value {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.form-item.vertical .label {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.form-item .label {
|
||||||
|
font-size: 20px;
|
||||||
|
color: rgb(0 0 0 / 45%);
|
||||||
|
}
|
||||||
|
.form-item .value {
|
||||||
|
font-size: 24px;
|
||||||
|
color: rgb(0 0 0 / 85%);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const FormItemNumber = React.memo((props) => {
|
||||||
|
const { value, onChange = () => {}, min } = props;
|
||||||
|
const onInputChange = (e) => {
|
||||||
|
onChange(e.target.value);
|
||||||
|
};
|
||||||
|
const onInputBlur = (e) => {
|
||||||
|
let res = parseFloat(e.target.value);
|
||||||
|
if (isNaN(res)) res = "-";
|
||||||
|
onChange(res + "");
|
||||||
|
};
|
||||||
|
const onBtnClick = (add) => {
|
||||||
|
let res = parseFloat(value);
|
||||||
|
if (isNaN(res)) res = add;
|
||||||
|
res = res + add;
|
||||||
|
if (min !== undefined && res < min) res = min;
|
||||||
|
onChange(res + "");
|
||||||
|
};
|
||||||
|
const btnDisabled = useMemo(() => [value <= min], [value, min]);
|
||||||
|
return (
|
||||||
|
<div className="input-number">
|
||||||
|
<div
|
||||||
|
className={"btn" + (btnDisabled[0] ? " disabled" : "")}
|
||||||
|
onClick={() => value > min && onBtnClick(-1)}
|
||||||
|
>
|
||||||
|
-
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
className="input-number-input"
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
onChange={onInputChange}
|
||||||
|
onBlur={onInputBlur}
|
||||||
|
/>
|
||||||
|
<div className="btn" onClick={() => onBtnClick(1)}>
|
||||||
|
+
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.btn,
|
||||||
|
.input-number-input {
|
||||||
|
height: 36px;
|
||||||
|
line-height: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
.input-number-input {
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
font-size: 24px;
|
||||||
|
padding: 0 12px;
|
||||||
|
width: 120px;
|
||||||
|
margin: 0 3px;
|
||||||
|
}
|
||||||
|
.input-number-input:focus {
|
||||||
|
border-color: #0285ff;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
width: 36px;
|
||||||
|
font-size: 32px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.btn.disabled {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,555 @@
|
||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { Modal, Row, Col, Form, Input, Select, message } from "antd";
|
||||||
|
import { Button } from "../Button";
|
||||||
|
import { request } from "../utils";
|
||||||
|
|
||||||
|
const ModalClient = React.memo((props) => {
|
||||||
|
const {
|
||||||
|
visible = false,
|
||||||
|
title,
|
||||||
|
onOk = () => {},
|
||||||
|
onCancel = () => {},
|
||||||
|
children,
|
||||||
|
} = props;
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="modalClient"
|
||||||
|
destroyOnClose
|
||||||
|
visible={visible}
|
||||||
|
closable={false}
|
||||||
|
footer={null}
|
||||||
|
width={536}
|
||||||
|
bodyStyle={{ padding: "32px" }}
|
||||||
|
>
|
||||||
|
<div className="modalClient-header">{title}</div>
|
||||||
|
<div className="modalClient-body">{children}</div>
|
||||||
|
<div className="modalClient-footer">
|
||||||
|
<Button title="确认" size="large" onClick={onOk} />
|
||||||
|
<Button
|
||||||
|
title="取消"
|
||||||
|
type="default"
|
||||||
|
size="large"
|
||||||
|
ghost
|
||||||
|
onClick={onCancel}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.modalClient-header {
|
||||||
|
height: 32px;
|
||||||
|
font-size: 28px;
|
||||||
|
color: rgb(0 0 0/0.85);
|
||||||
|
letter-spacing: 0px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin-bottom: 42px;
|
||||||
|
}
|
||||||
|
.modalClient-footer {
|
||||||
|
text-align: right;
|
||||||
|
padding-top: 32px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<style jsx global>{`
|
||||||
|
.modalClient .ant-form-item-label {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.modalClient .ant-form-item-label > label {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.modalClient .ant-form-large .ant-form-item-control-input {
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
.modalClient
|
||||||
|
.ant-select-single.ant-select-lg:not(.ant-select-customize-input)
|
||||||
|
.ant-select-selector::after,
|
||||||
|
.modalClient
|
||||||
|
.ant-select-single.ant-select-lg:not(.ant-select-customize-input)
|
||||||
|
.ant-select-selector
|
||||||
|
.ant-select-selection-item,
|
||||||
|
.modalClient
|
||||||
|
.ant-select-single.ant-select-lg:not(.ant-select-customize-input)
|
||||||
|
.ant-select-selector
|
||||||
|
.ant-select-selection-placeholder {
|
||||||
|
line-height: 56px;
|
||||||
|
}
|
||||||
|
.modalClient .ant-form-large .ant-form-item-label > label,
|
||||||
|
.modalClient
|
||||||
|
.ant-select-single.ant-select-lg:not(.ant-select-customize-input)
|
||||||
|
.ant-select-selector {
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
.modalClient .ant-input-lg {
|
||||||
|
line-height: 54px;
|
||||||
|
padding: 0 11px;
|
||||||
|
}
|
||||||
|
.modalClient p,
|
||||||
|
.modalClient .ant-input-lg,
|
||||||
|
.modalClient .ant-select-lg {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.modalClient .item-line {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 56px;
|
||||||
|
}
|
||||||
|
.modalClient.ant-select-item {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置
|
||||||
|
export const ModelSetting = React.forwardRef((props, ref) => {
|
||||||
|
const {} = props;
|
||||||
|
const callbackRef = useRef(() => {});
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
const handleCancel = useCallback(() => setVisible(false), []);
|
||||||
|
|
||||||
|
const [equipList, setEquipList] = useState([]);
|
||||||
|
const [operationList, setOperationList] = useState([]);
|
||||||
|
const [workShopList, setWorkShopList] = useState([]);
|
||||||
|
|
||||||
|
// 查询包装机
|
||||||
|
const getEquip = useCallback((gid) => {
|
||||||
|
if (gid) {
|
||||||
|
request({
|
||||||
|
method: "POST",
|
||||||
|
url: `${window.config.mesUrl}/mdgeneric/md/bmWorkCenter/getEquipByOperationGid?operationGid=${gid}`,
|
||||||
|
}).then(({ data, success } = {}) => {
|
||||||
|
if (!success) data = [];
|
||||||
|
setEquipList(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 工序变化
|
||||||
|
const onOperationChange = useCallback(
|
||||||
|
(code, a, data) => {
|
||||||
|
if (!data) data = operationList;
|
||||||
|
// console.log("val", key, value, data);
|
||||||
|
let {gid: operationGid, name} = data.find((item) => item.code === code) || {};
|
||||||
|
getEquip(operationGid);
|
||||||
|
form.setFieldsValue({
|
||||||
|
operationName: name,
|
||||||
|
operationCode: code,
|
||||||
|
equipName: undefined,
|
||||||
|
equipCode: undefined,
|
||||||
|
});
|
||||||
|
setEquipList([]);
|
||||||
|
},
|
||||||
|
[operationList, form, getEquip]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 查询工序
|
||||||
|
const getOperation = useCallback(
|
||||||
|
(gid, callback) => {
|
||||||
|
if (gid) {
|
||||||
|
request({
|
||||||
|
method: "POST",
|
||||||
|
url: `${window.config.mesUrl}/mdgeneric/md/bmWorkCenter/getOpByWorkCenter?workCenterGid=${gid}`,
|
||||||
|
}).then(({ data, success } = {}) => {
|
||||||
|
if (!success) data = [];
|
||||||
|
setOperationList(data);
|
||||||
|
if (callback) {
|
||||||
|
callback(data);
|
||||||
|
} else if (data.length) {
|
||||||
|
let { name, code } = data[0];
|
||||||
|
form.setFieldsValue({
|
||||||
|
operationName: name,
|
||||||
|
equipName: code,
|
||||||
|
});
|
||||||
|
onOperationChange(code,undefined, data)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else setOperationList([]);
|
||||||
|
},
|
||||||
|
[onOperationChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 车间变化
|
||||||
|
const onWorkShopChange = useCallback(
|
||||||
|
(key = {}) => {
|
||||||
|
let { gid: workShopGid, name: value } = workShopList.find(({ code }) => code === key) || {};
|
||||||
|
getOperation(workShopGid);
|
||||||
|
form.setFieldsValue({
|
||||||
|
workCenterCode: key,
|
||||||
|
workCenterName: value,
|
||||||
|
operationName: undefined,
|
||||||
|
operationCode: undefined,
|
||||||
|
equipName: undefined,
|
||||||
|
equipCode: undefined,
|
||||||
|
});
|
||||||
|
setOperationList([]);
|
||||||
|
setEquipList([]);
|
||||||
|
},
|
||||||
|
[workShopList, form, getOperation]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 查询车间
|
||||||
|
const getWorkShop = useCallback((callback) => {
|
||||||
|
request({
|
||||||
|
method: "POST",
|
||||||
|
url: `${window.config.mesUrl}/mdgeneric/md/bmWorkCenter/findAllWithPage`,
|
||||||
|
bodys: {
|
||||||
|
// removePermission: true,
|
||||||
|
page: -1,
|
||||||
|
pageSize: -1,
|
||||||
|
sorted: "createTime desc",
|
||||||
|
filter: "type eq 'workshop'",
|
||||||
|
},
|
||||||
|
}).then(({ data, success } = {}) => {
|
||||||
|
if (!success) data = [];
|
||||||
|
setWorkShopList(data);
|
||||||
|
callback && callback(data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 设备变化
|
||||||
|
const onEquipChange = useCallback(
|
||||||
|
(key) => {
|
||||||
|
let { name: value} = equipList.find(({ code }) => code === key) || {}
|
||||||
|
form.setFieldsValue({
|
||||||
|
equipName: value,
|
||||||
|
equipCode: key,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[form, equipList]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
const init = useCallback(
|
||||||
|
(data, callback) => {
|
||||||
|
setVisible(true);
|
||||||
|
form.resetFields();
|
||||||
|
form.setFieldsValue(data);
|
||||||
|
callbackRef.current = callback;
|
||||||
|
let { workCenterCode, operationCode } = data;
|
||||||
|
getWorkShop((res) => {
|
||||||
|
if (workCenterCode) {
|
||||||
|
let gid = res.find(({ code }) => code === workCenterCode)?.gid;
|
||||||
|
getOperation(gid, (res) => {
|
||||||
|
if (operationCode) {
|
||||||
|
let gid = res.find(({ code }) => code === operationCode)?.gid;
|
||||||
|
getEquip(gid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[form, getWorkShop, getOperation, getEquip]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOk = useCallback(() => {
|
||||||
|
let formData = form.getFieldValue();
|
||||||
|
if (["", null, undefined].includes(formData.workCenterCode))
|
||||||
|
return message.error("请完善车间!");
|
||||||
|
if (["", null, undefined].includes(formData.equipCode))
|
||||||
|
return message.error("请完善包装机!");
|
||||||
|
if (["", null, undefined].includes(formData.bagWeight))
|
||||||
|
return message.error("请完善包装袋重量!");
|
||||||
|
if (["", null, undefined].includes(formData.packSpecs))
|
||||||
|
return message.error("请完包装规格!");
|
||||||
|
if (["", null, undefined].includes(formData.upperLimit))
|
||||||
|
return message.error("请完善上限值!");
|
||||||
|
if (["", null, undefined].includes(formData.lowerLimit))
|
||||||
|
return message.error("请完善下限值!");
|
||||||
|
// console.log("form", formData);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request({
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/setPackInfo`,
|
||||||
|
bodys: formData,
|
||||||
|
}).then((res) => {
|
||||||
|
handleCancel();
|
||||||
|
callbackRef.current();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [handleCancel, form, workShopList, equipList]);
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
show: init,
|
||||||
|
}),
|
||||||
|
[init]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ModalClient
|
||||||
|
visible={visible}
|
||||||
|
title="设置"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onOk={handleOk}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
labelCol={{ span: 8 }}
|
||||||
|
wrapperCol={{ span: 16 }}
|
||||||
|
// onFieldsChange={ onFieldsChange }
|
||||||
|
autoComplete="off"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<Form.Item label="车间" name="workCenterName">
|
||||||
|
<Select
|
||||||
|
placeholder="选择车间"
|
||||||
|
onSelect={onWorkShopChange}
|
||||||
|
// labelInValue
|
||||||
|
>
|
||||||
|
{workShopList.map(({ code, name }) => (
|
||||||
|
<Select.Option value={code} key={code} className="modalClient">
|
||||||
|
{name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="工序" name="operationName">
|
||||||
|
<Select
|
||||||
|
placeholder="选择工序"
|
||||||
|
onSelect={onOperationChange}
|
||||||
|
// labelInValue
|
||||||
|
>
|
||||||
|
{operationList.map(({ code, name }) => (
|
||||||
|
<Select.Option value={code} key={code} className="modalClient">
|
||||||
|
{name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="包装机" name="equipName">
|
||||||
|
<Select
|
||||||
|
placeholder="选择包装机"
|
||||||
|
onSelect={onEquipChange}
|
||||||
|
// labelInValue
|
||||||
|
>
|
||||||
|
{equipList.map(({ code, name }) => (
|
||||||
|
<Select.Option value={code} key={code} className="modalClient">
|
||||||
|
{name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="包装袋重量" name="bagWeight">
|
||||||
|
<Input addonAfter="KG" placeholder="填写包装袋重量" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="包装规格" name="packSpecs">
|
||||||
|
<Input addonAfter="KG" placeholder="填写包装规格" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="上限-下限">
|
||||||
|
<Input.Group>
|
||||||
|
<Row gutter={8}>
|
||||||
|
<Col span={11}>
|
||||||
|
<Form.Item name={"upperLimit"} noStyle>
|
||||||
|
<Input addonAfter="KG" placeholder="上限" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={2}>
|
||||||
|
<div className="item-line">-</div>
|
||||||
|
</Col>
|
||||||
|
<Col span={11}>
|
||||||
|
<Form.Item name={"lowerLimit"} noStyle>
|
||||||
|
<Input addonAfter="KG" placeholder="下限" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Input.Group>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</ModalClient>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 中断
|
||||||
|
export const ModelBreak = React.forwardRef((props, ref) => {
|
||||||
|
const {} = props;
|
||||||
|
const callbackRef = useRef(() => {});
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
const handleCancel = useCallback(() => setVisible(false), []);
|
||||||
|
|
||||||
|
const handleOk = useCallback(() => {
|
||||||
|
let formData = form.getFieldValue();
|
||||||
|
if (["", null, undefined].includes(formData.reason))
|
||||||
|
return message.error("请完善中断原因!");
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request({
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/interruptPack`,
|
||||||
|
bodys: {
|
||||||
|
gid: formData.gid,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
handleCancel();
|
||||||
|
callbackRef.current();
|
||||||
|
} else message.error(res.message || "操作失败!");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [handleCancel]);
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
show() {
|
||||||
|
setVisible(true);
|
||||||
|
},
|
||||||
|
show: (data, callback) => {
|
||||||
|
setVisible(true);
|
||||||
|
form.resetFields();
|
||||||
|
form.setFieldsValue(data);
|
||||||
|
callbackRef.current = callback;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[form]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ModalClient
|
||||||
|
visible={visible}
|
||||||
|
title="中断原因"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onOk={handleOk}
|
||||||
|
>
|
||||||
|
<Form autoComplete="off" size="large" form={form}>
|
||||||
|
<Form.Item name="reason">
|
||||||
|
<Input.TextArea placeholder="填写设备故障原因" rows={4} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</ModalClient>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 结束
|
||||||
|
export const ModelFinish = React.forwardRef((props, ref) => {
|
||||||
|
const {} = props;
|
||||||
|
const [form, setForm] = useState([]);
|
||||||
|
const callbackRef = useRef(() => {});
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const handleCancel = useCallback(() => setVisible(false), []);
|
||||||
|
const handleOk = useCallback(() => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request({
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/endPack`,
|
||||||
|
bodys: form,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
handleCancel();
|
||||||
|
callbackRef.current();
|
||||||
|
message.success("包装完成!");
|
||||||
|
} else message.error(res.message || "操作失败!");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [handleCancel, form]);
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
show: (data, callback) => {
|
||||||
|
setVisible(true);
|
||||||
|
setForm(data);
|
||||||
|
callbackRef.current = callback;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ModalClient
|
||||||
|
visible={visible}
|
||||||
|
title="结束"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onOk={handleOk}
|
||||||
|
>
|
||||||
|
<p>是否确认结束?</p>
|
||||||
|
</ModalClient>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 超产
|
||||||
|
export const ModelOverproductionBatch = React.forwardRef((props, ref) => {
|
||||||
|
const { } = props;
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const codeRef = useRef();
|
||||||
|
const callbackRef = useRef(() => {});
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const handleCancel = useCallback(() => setVisible(false), []);
|
||||||
|
const handleOk = useCallback(() => {
|
||||||
|
let formData = form.getFieldValue();
|
||||||
|
if (codeRef.current && codeRef.current === formData.outBatch) {
|
||||||
|
message.error("超产批号不能与原单号一致!");
|
||||||
|
} else {
|
||||||
|
callbackRef.current && callbackRef.current(formData.outBatch);
|
||||||
|
handleCancel();
|
||||||
|
};
|
||||||
|
}, [handleCancel, form]);
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
show(code, callback) {
|
||||||
|
codeRef.current = code;
|
||||||
|
callbackRef.current = callback;
|
||||||
|
setVisible(true);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ModalClient
|
||||||
|
visible={visible}
|
||||||
|
title="超产批号"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onOk={handleOk}
|
||||||
|
>
|
||||||
|
<Form autoComplete="off" size="large" form={form}>
|
||||||
|
<Form.Item name="outBatch">
|
||||||
|
<Input placeholder="填写超产批号" allowClear/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</ModalClient>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 超量打印
|
||||||
|
export const ModelOverPrint = React.forwardRef((props, ref) => {
|
||||||
|
const {} = props;
|
||||||
|
const [form, setForm] = useState("");
|
||||||
|
const callbackRef = useRef(() => {});
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const handleCancel = useCallback(() => setVisible(false), []);
|
||||||
|
const handleOk = useCallback(() => {
|
||||||
|
return callbackRef.current(handleCancel);
|
||||||
|
}, [handleCancel, form]);
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
show: (data, callback) => {
|
||||||
|
setVisible(true);
|
||||||
|
setForm(data);
|
||||||
|
callbackRef.current = callback;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ModalClient
|
||||||
|
visible={visible}
|
||||||
|
title="确认打印"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onOk={handleOk}
|
||||||
|
>
|
||||||
|
<p>{ form }</p>
|
||||||
|
</ModalClient>
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,199 @@
|
||||||
|
import React, { useCallback, useEffect, useState, useRef } from "react";
|
||||||
|
import { message } from "antd";
|
||||||
|
import { request, getQueryVariable } from "../utils";
|
||||||
|
|
||||||
|
export const isDev = false;
|
||||||
|
|
||||||
|
let rules = (function () {
|
||||||
|
let data = [];
|
||||||
|
let callback = () => {};
|
||||||
|
let setData = (res) => {
|
||||||
|
setData = () => {};
|
||||||
|
data = res;
|
||||||
|
callback({ success: true, data: res });
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
setData,
|
||||||
|
getData: () => data,
|
||||||
|
loaded: new Promise((resolve, reject) => {
|
||||||
|
callback = resolve;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
let getRule = () => {
|
||||||
|
getRule = () => rules.loaded;
|
||||||
|
return request({
|
||||||
|
method: "get",
|
||||||
|
url: `${window.config.mesUrl}/console/getpermissions`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRule = (code) => {
|
||||||
|
const [ruleList, setRuleList] = useState(rules.getData());
|
||||||
|
useEffect(() => {
|
||||||
|
getRule().then(({ success, data } = {}) => {
|
||||||
|
if (success) {
|
||||||
|
setRuleList(data);
|
||||||
|
rules.setData(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (code) return ruleList.includes(code);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUser = () => {
|
||||||
|
const [data, setData] = useState();
|
||||||
|
const getUser = useCallback(() => {
|
||||||
|
// let user = localStorage.getItem("userInfo");
|
||||||
|
// try {
|
||||||
|
// user = JSON.parse(user)
|
||||||
|
// } catch (e) {
|
||||||
|
// console.log(e);
|
||||||
|
// user = {};
|
||||||
|
// }
|
||||||
|
// console.log("user", user, localStorage.getItem("userInfo"))
|
||||||
|
// setData(user);
|
||||||
|
request({
|
||||||
|
method: "get",
|
||||||
|
url: `${window.config.mesUrl}/console/currentinfo`,
|
||||||
|
}).then(({ success, data } = {}) => {
|
||||||
|
if (success) {
|
||||||
|
setData(data);
|
||||||
|
} else setData();
|
||||||
|
});
|
||||||
|
}, [])
|
||||||
|
// useEffect(() => {
|
||||||
|
// getUser();
|
||||||
|
// }, [getUser]);
|
||||||
|
return [data, getUser];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useEquipKey = () => {
|
||||||
|
const [equipKey, setEquipKey] = useState();
|
||||||
|
const getEquipKey = useCallback(() => {
|
||||||
|
let ip = getQueryVariable("ip");
|
||||||
|
console.log("ip", ip);
|
||||||
|
if (!ip || ip === "null") {
|
||||||
|
message.error("获取 ip 失败!");
|
||||||
|
} else {
|
||||||
|
setEquipKey(ip);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
getEquipKey();
|
||||||
|
}, [getEquipKey]);
|
||||||
|
return { equipKey, getEquipKey };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetting = (equipKey) => {
|
||||||
|
const [settingData, setSettingData] = useState({});
|
||||||
|
|
||||||
|
const getSettingData = useCallback(() => {
|
||||||
|
if (equipKey) {
|
||||||
|
request({
|
||||||
|
method: "post",
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/getPackInfoByKey?equipKey=${equipKey}`,
|
||||||
|
}).then(({ success, data } = {}) => {
|
||||||
|
if (success) setSettingData(data || {});
|
||||||
|
});
|
||||||
|
} else setSettingData({});
|
||||||
|
}, [equipKey]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getSettingData();
|
||||||
|
}, [getSettingData]);
|
||||||
|
return { settingData, getSettingData };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMetaInfo = (equipCode) => {
|
||||||
|
const [data, setData] = useState({}); // bagNo outBagNo
|
||||||
|
const timmerRef = useRef();
|
||||||
|
const getData = useCallback(() => {
|
||||||
|
if (timmerRef.current) {
|
||||||
|
clearTimeout(timmerRef.current);
|
||||||
|
timmerRef.current = undefined;
|
||||||
|
}
|
||||||
|
if (equipCode) {
|
||||||
|
request({
|
||||||
|
method: "post",
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/getEquipRecord?equipCode=${equipCode}`,
|
||||||
|
}).then(({ success, data, message: msg } = {}) => {
|
||||||
|
if (success) {
|
||||||
|
let { status } = data || {};
|
||||||
|
// 没有数据,或者数据为中断状态,尝试定时重复请求
|
||||||
|
if (!data || status === "INTERRUPT") {
|
||||||
|
if (!data) {
|
||||||
|
data = { status: "no_job" };
|
||||||
|
message.error("请先分配任务!");
|
||||||
|
}
|
||||||
|
let { reloadTime } = window.config.packagingOperaConfig || {};
|
||||||
|
if (reloadTime !== undefined) {
|
||||||
|
timmerRef.current = setTimeout(getData, reloadTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setData(data);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else setData({});
|
||||||
|
}, [equipCode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, [getData]);
|
||||||
|
return [data, getData];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useWsInfo = (equipCode, isEdit) => {
|
||||||
|
const [data, setData] = useState({});
|
||||||
|
useEffect(() => {
|
||||||
|
if (equipCode) {
|
||||||
|
let url = window.config.edgewsUrl;
|
||||||
|
if (isDev) {
|
||||||
|
url = "ws://120.202.38.15:8081/rongtong-edge-application-ws/";
|
||||||
|
}
|
||||||
|
if (!isEdit) {
|
||||||
|
console.log("link: ", url);
|
||||||
|
let ws = new WebSocket(url);
|
||||||
|
ws.onmessage = (evt) => {
|
||||||
|
let res = evt.data;
|
||||||
|
// if (isDev) {
|
||||||
|
// res = {
|
||||||
|
// czzl: 1.11,
|
||||||
|
// fczl: 2.22,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (typeof res === "string") {
|
||||||
|
try {
|
||||||
|
res = JSON.parse(res);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("ws", res);
|
||||||
|
res = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("ws res:", res);
|
||||||
|
setData(res);
|
||||||
|
};
|
||||||
|
ws.onopen = () => {
|
||||||
|
if (isDev) {
|
||||||
|
// 01 02 03
|
||||||
|
equipCode = "L5-BZ-02";
|
||||||
|
}
|
||||||
|
console.log("Connection open ... send:", equipCode);
|
||||||
|
ws.send(equipCode);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (!isEdit) {
|
||||||
|
console.log("ws close:", equipCode);
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [equipCode, isEdit]);
|
||||||
|
return [data];
|
||||||
|
};
|
|
@ -0,0 +1,619 @@
|
||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
useReducer,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
|
import { message } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import Decimal from "decimal.js"
|
||||||
|
import Row from "../../../components/screen/Row";
|
||||||
|
import { FormItem } from "../FormItem";
|
||||||
|
import { Button } from "../Button";
|
||||||
|
import {
|
||||||
|
ModelSetting,
|
||||||
|
ModelBreak,
|
||||||
|
ModelOverproductionBatch,
|
||||||
|
ModelFinish,
|
||||||
|
ModelOverPrint,
|
||||||
|
} from "./ModalClient";
|
||||||
|
import { logo } from "./logo";
|
||||||
|
import { request } from "../utils";
|
||||||
|
import {
|
||||||
|
useEquipKey,
|
||||||
|
useSetting,
|
||||||
|
useMetaInfo,
|
||||||
|
useWsInfo,
|
||||||
|
useUser,
|
||||||
|
isDev,
|
||||||
|
} from "./hooks";
|
||||||
|
|
||||||
|
const headerHeight = 56;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url获取token
|
||||||
|
* 获取用户信息
|
||||||
|
* 获取权限信息
|
||||||
|
*
|
||||||
|
* url获取ip
|
||||||
|
* ip - 设置信息
|
||||||
|
* 设置信息 - 产品信息
|
||||||
|
* 设置信息 - socket 重量信息
|
||||||
|
*
|
||||||
|
* 任务状态:
|
||||||
|
* 无任务 待包装 包装中 中断 包装完成
|
||||||
|
*/
|
||||||
|
|
||||||
|
function formReducer(state, action) {
|
||||||
|
let { type, value } = action;
|
||||||
|
// console.log("action", action);
|
||||||
|
if (type === "FROM_INIT") return value;
|
||||||
|
if (type === "FROM_CHANGE") return Object.assign({}, state, value);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PackagingOperation(props) {
|
||||||
|
const ModelSettingRef = useRef();
|
||||||
|
const ModelBreakRef = useRef();
|
||||||
|
const ModelOverproductionBatchRef = useRef();
|
||||||
|
const ModelFinishRef = useRef();
|
||||||
|
|
||||||
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
|
|
||||||
|
const { equipKey, getEquipKey } = useEquipKey();
|
||||||
|
const { settingData, getSettingData } = useSetting(equipKey);
|
||||||
|
const [metaInfo, getMetaInfo] = useMetaInfo(settingData?.equipCode);
|
||||||
|
const [wsData] = useWsInfo(settingData?.equipCode, isEdit);
|
||||||
|
const [user, getUser] = useUser();
|
||||||
|
|
||||||
|
// 页面表单变化
|
||||||
|
const [form, dispatch] = useReducer(formReducer, {});
|
||||||
|
const onFormChange = useCallback((type, value) => {
|
||||||
|
dispatch({ type: "FROM_CHANGE", value: { [type]: value } });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 更新当前登录用户
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: "FROM_CHANGE", value: { userName: user?.userName } });
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
// 初始化表单
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: "FROM_INIT", value: metaInfo });
|
||||||
|
getUser();
|
||||||
|
}, [metaInfo, getUser]);
|
||||||
|
|
||||||
|
// ws更新表单
|
||||||
|
useEffect(() => {
|
||||||
|
let { czzl, fczl } = wsData || {};
|
||||||
|
dispatch({ type: "FROM_CHANGE", value: { czzl, fczl } });
|
||||||
|
}, [wsData]);
|
||||||
|
|
||||||
|
// 更新净重
|
||||||
|
const netWeight = useMemo(() => {
|
||||||
|
let { bagWeight } = settingData;
|
||||||
|
let { fczl } = form;
|
||||||
|
if (isNaN(parseFloat(fczl))) return "";
|
||||||
|
if (isNaN(parseFloat(bagWeight))) return "";
|
||||||
|
let res = Decimal(fczl).sub(Decimal(bagWeight)).toNumber();
|
||||||
|
if (isNaN(res)) return "";
|
||||||
|
return res;
|
||||||
|
}, [form, settingData]);
|
||||||
|
|
||||||
|
// 更新持续时长
|
||||||
|
const [durationTime, setDurationTime] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
let getTime = () => {
|
||||||
|
let {
|
||||||
|
startTime,
|
||||||
|
status,
|
||||||
|
recoverDate,
|
||||||
|
interruptDate,
|
||||||
|
intrptDuration = 0,
|
||||||
|
} = metaInfo;
|
||||||
|
// console.log("metaInfo", metaInfo, {startTime, status, recoverDate, interruptDate, intrptDuration })
|
||||||
|
/**
|
||||||
|
* 无任务 “”
|
||||||
|
* 待包装 “”
|
||||||
|
* 包装中 currentDate - startTime - intrptDuration
|
||||||
|
* 中断 interruptDate - startTime - intrptDuration
|
||||||
|
* 包装完成 endTime - startTime - intrptDuration
|
||||||
|
*/
|
||||||
|
if (["no_job", "TO_BE_PACKAGED"].includes(status)) return "";
|
||||||
|
let dif = new Date(startTime).getTime() + intrptDuration * 1000;
|
||||||
|
if (status === "IN_PACKAGING") return new Date().getTime() - dif;
|
||||||
|
if (status === "INTERRUPT")
|
||||||
|
return new Date(interruptDate).getTime() - dif;
|
||||||
|
if (status === "PACKAGING_COMPLETED")
|
||||||
|
return new Date(endTime).getTime() - dif;
|
||||||
|
};
|
||||||
|
let timmer;
|
||||||
|
let run = () => {
|
||||||
|
setDurationTime(getTime());
|
||||||
|
timmer = setTimeout(() => {
|
||||||
|
run();
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
run();
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timmer);
|
||||||
|
};
|
||||||
|
}, [metaInfo]);
|
||||||
|
const durationTimeStr = useMemo(() => {
|
||||||
|
let time = moment.duration(durationTime);
|
||||||
|
let res = "";
|
||||||
|
let sec = time.seconds();
|
||||||
|
let min = time.minutes();
|
||||||
|
let hor = time.hours();
|
||||||
|
let day = time.days();
|
||||||
|
let m = time.months();
|
||||||
|
let y = time.years();
|
||||||
|
let flag = false;
|
||||||
|
let arr = [y, m, day, hor, min, sec];
|
||||||
|
let unt = ["年", "月", "日", "时", "分", "秒"];
|
||||||
|
arr.forEach((val, index) => {
|
||||||
|
if (val) {
|
||||||
|
flag = true;
|
||||||
|
res += val + unt[index];
|
||||||
|
} else if (flag) {
|
||||||
|
res += 0 + unt[index];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}, [durationTime]);
|
||||||
|
|
||||||
|
// 包装袋数
|
||||||
|
const [realBagNo, setRealBagNo] = useState(1);
|
||||||
|
useEffect(() => {
|
||||||
|
let { bagNo, outBagNo, outBatch } = form;
|
||||||
|
let res = 0;
|
||||||
|
if (outBatch) {
|
||||||
|
res = parseFloat(outBagNo) + 1;
|
||||||
|
} else {
|
||||||
|
res = parseFloat(bagNo) + 1;
|
||||||
|
}
|
||||||
|
setRealBagNo(isNaN(res) ? 0 : res);
|
||||||
|
}, [form.bagNo, form.outBagNo, form.outBatch]);
|
||||||
|
|
||||||
|
// 设备状态
|
||||||
|
const equipStatus = useMemo(() => {
|
||||||
|
if (form.status === "no_job") return "无任务";
|
||||||
|
if (form.status === "INTERRUPT") return "中断";
|
||||||
|
if (form.status === "TO_BE_PACKAGED") return "待包装";
|
||||||
|
if (form.status === "IN_PACKAGING") return "包装中";
|
||||||
|
if (form.status === "PACKAGING_COMPLETED") return "包装完成";
|
||||||
|
return "-";
|
||||||
|
}, [form.status]);
|
||||||
|
|
||||||
|
// 按钮启用禁用
|
||||||
|
const btnEnable = useMemo(() => {
|
||||||
|
/**
|
||||||
|
* 超产批号 ?
|
||||||
|
* 设置 - 权限
|
||||||
|
* 历史记录 - 非无任务
|
||||||
|
* 开始 - 待包装
|
||||||
|
* 打印 - 包装中
|
||||||
|
* 结束 - 包装中 中断
|
||||||
|
* 中断 - 包装中
|
||||||
|
* 编辑 - 权限 包装中
|
||||||
|
* 编辑完成 - 权限 包装中
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
overPro: true,
|
||||||
|
setting: true,
|
||||||
|
history: ["中断", "待包装", "包装中", "包装完成"].includes(equipStatus),
|
||||||
|
start: ["待包装"].includes(equipStatus),
|
||||||
|
print: ["包装中"].includes(equipStatus),
|
||||||
|
finish: ["包装中", "中断"].includes(equipStatus),
|
||||||
|
break: ["包装中"].includes(equipStatus),
|
||||||
|
edit: ["包装中"].includes(equipStatus),
|
||||||
|
};
|
||||||
|
}, [equipStatus]);
|
||||||
|
|
||||||
|
// 退出
|
||||||
|
const handleCancel = useCallback(() => {
|
||||||
|
window.parent.postMessage({ type: "logOut" }, window.config.mesHost);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 设置
|
||||||
|
const handleSetting = useCallback(() => {
|
||||||
|
if (equipKey) {
|
||||||
|
let settingRes = Object.assign({}, settingData || {}, { equipKey });
|
||||||
|
ModelSettingRef.current.show(settingRes, getSettingData);
|
||||||
|
}
|
||||||
|
}, [equipKey, getSettingData, settingData]);
|
||||||
|
|
||||||
|
// 超产批号
|
||||||
|
const handleOverproduct = useCallback(() => {
|
||||||
|
ModelOverproductionBatchRef.current.show(
|
||||||
|
metaInfo.workOrderCode,
|
||||||
|
(outBatch) => {
|
||||||
|
onFormChange("outBatch", outBatch);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, [metaInfo.workOrderCode, onFormChange]);
|
||||||
|
|
||||||
|
// 历史记录
|
||||||
|
const handleHistory = useCallback(() => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{ type: "history", data: metaInfo },
|
||||||
|
window.config.mesHost
|
||||||
|
);
|
||||||
|
}, [metaInfo]);
|
||||||
|
|
||||||
|
// 开始
|
||||||
|
const handleStart = useCallback(() => {
|
||||||
|
if (metaInfo.gid)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request({
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/startPack`,
|
||||||
|
bodys: { gid: metaInfo.gid },
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
message.success("开始成功!");
|
||||||
|
getMetaInfo();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [metaInfo.gid, getMetaInfo]);
|
||||||
|
|
||||||
|
// 打印
|
||||||
|
const ModelOverPrintRef = useRef();
|
||||||
|
const [isGT, setIsGT] = useState(false)
|
||||||
|
const handlePrint = useCallback((GT, callback) => {
|
||||||
|
if (GT === undefined) GT = isGT;
|
||||||
|
let { gid: recordGid } = metaInfo;
|
||||||
|
let { gid: setInfoGid, bagWeight } = settingData;
|
||||||
|
let {
|
||||||
|
fczl,
|
||||||
|
czzl,
|
||||||
|
outBatch,
|
||||||
|
materialCode,
|
||||||
|
productLevel,
|
||||||
|
startTime,
|
||||||
|
materialName,
|
||||||
|
workOrderCode,
|
||||||
|
} = form;
|
||||||
|
// collectValue 页面可见数据 除自定义字段外的 对象json 字符串
|
||||||
|
let collectValue = {
|
||||||
|
materialCode,
|
||||||
|
productLevel,
|
||||||
|
startTime,
|
||||||
|
materialName,
|
||||||
|
workOrderCode,
|
||||||
|
czzl,
|
||||||
|
fczl,
|
||||||
|
bagWeight,
|
||||||
|
yetWeight: netWeight,
|
||||||
|
bagNo: realBagNo,
|
||||||
|
};
|
||||||
|
let data = {
|
||||||
|
recordGid,
|
||||||
|
mark: GT? "GT": undefined,
|
||||||
|
fczl,
|
||||||
|
bagNo: realBagNo,
|
||||||
|
setInfoGid,
|
||||||
|
yetWeight: netWeight,
|
||||||
|
outBatch,
|
||||||
|
collectValue: JSON.stringify(collectValue),
|
||||||
|
};
|
||||||
|
if (recordGid)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request({
|
||||||
|
url: `${window.config.mesUrl}/prodexec/prodexec/packageEquipment/print`,
|
||||||
|
bodys: data,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
// if (isDev) {
|
||||||
|
// if (res.message.indexOf('打印命令发送失败') > -1) {
|
||||||
|
// res = {
|
||||||
|
// success: false,
|
||||||
|
// data: "GT",
|
||||||
|
// message: "message text"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (res && res.success) {
|
||||||
|
message.success("打印成功!");
|
||||||
|
setRealBagNo(parseFloat(realBagNo) + 1);
|
||||||
|
} else {
|
||||||
|
if (res?.data === "GT") {
|
||||||
|
ModelOverPrintRef.current.show(res.message, (close) => {
|
||||||
|
setIsGT(true);
|
||||||
|
return handlePrint(true, close);
|
||||||
|
})
|
||||||
|
}else message.error(res?.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
resolve();
|
||||||
|
callback && callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [metaInfo, settingData, form, netWeight, realBagNo, isGT]);
|
||||||
|
|
||||||
|
// 结束
|
||||||
|
const handleFinish = useCallback(() => {
|
||||||
|
ModelFinishRef.current.show({ gid: metaInfo.gid }, getMetaInfo);
|
||||||
|
}, [metaInfo.gid, getMetaInfo]);
|
||||||
|
|
||||||
|
// 中断
|
||||||
|
const handleBreak = useCallback(() => {
|
||||||
|
ModelBreakRef.current.show({ gid: metaInfo.gid }, getMetaInfo);
|
||||||
|
}, [metaInfo.gid, getMetaInfo]);
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = useCallback(() => {
|
||||||
|
// console.log("handleEdit");
|
||||||
|
if (isEdit) {
|
||||||
|
setIsEdit(false);
|
||||||
|
} else {
|
||||||
|
setIsEdit(true);
|
||||||
|
}
|
||||||
|
}, [isEdit]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="client-content">
|
||||||
|
<div className="client-content-header">
|
||||||
|
<div className="logo-box">
|
||||||
|
<img src={logo} alt="logo" className="logo-img" />
|
||||||
|
</div>
|
||||||
|
<div className="btn-box">
|
||||||
|
<div className="btn link-btn" onClick={handleCancel}>
|
||||||
|
退出
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="client-content-body">
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
<span className="title">产品信息</span>
|
||||||
|
<div className="extra">
|
||||||
|
<Button
|
||||||
|
title="超产批号"
|
||||||
|
ghost
|
||||||
|
onClick={handleOverproduct}
|
||||||
|
enable={btnEnable.overPro}
|
||||||
|
/>
|
||||||
|
<ModelOverproductionBatch ref={ModelOverproductionBatchRef} />
|
||||||
|
<Button
|
||||||
|
title="设置"
|
||||||
|
ghost
|
||||||
|
ruleCode="set_pack_info"
|
||||||
|
onClick={handleSetting}
|
||||||
|
enable={btnEnable.setting}
|
||||||
|
/>
|
||||||
|
<ModelSetting ref={ModelSettingRef} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="statu-box">{equipStatus}</div>
|
||||||
|
<div className="statu-content">
|
||||||
|
<Row gutter={32}>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem value={form.materialCode} title={"产品编码"} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem value={form.productLevel} title={"产品型号"} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem value={form.startTime} title={"开始时间"} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem value={form.materialName} title={"产品名称"} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem value={form.workOrderCode} title={"产品批次号"} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem value={durationTimeStr} title={"持续时长"} />
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
<span className="title">生产信息</span>
|
||||||
|
<div className="extra">
|
||||||
|
<Button
|
||||||
|
title="历史记录"
|
||||||
|
ghost={true}
|
||||||
|
onClick={handleHistory}
|
||||||
|
enable={btnEnable.history}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<Row gutter={32}>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem
|
||||||
|
value={form.czzl}
|
||||||
|
title={"毛重吨包/KG"}
|
||||||
|
horizontal={false}
|
||||||
|
type="number"
|
||||||
|
readonly={!isEdit}
|
||||||
|
onChange={(val) => onFormChange("czzl", val)}
|
||||||
|
/>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem
|
||||||
|
value={form.fczl}
|
||||||
|
title={"复称重量吨包/KG"}
|
||||||
|
horizontal={false}
|
||||||
|
type="number"
|
||||||
|
readonly={!isEdit}
|
||||||
|
onChange={(val) => onFormChange("fczl", val)}
|
||||||
|
/>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem
|
||||||
|
value={form.userName}
|
||||||
|
title={"复称人"}
|
||||||
|
horizontal={false}
|
||||||
|
/>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem
|
||||||
|
value={settingData.bagWeight}
|
||||||
|
title={"包装袋重量/KG"}
|
||||||
|
horizontal={false}
|
||||||
|
/>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem
|
||||||
|
value={netWeight}
|
||||||
|
title={"净重/KG"}
|
||||||
|
horizontal={false}
|
||||||
|
/>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
<FormItem
|
||||||
|
value={realBagNo}
|
||||||
|
title={"包装袋数"}
|
||||||
|
horizontal={false}
|
||||||
|
type="number"
|
||||||
|
readonly={false}
|
||||||
|
min={1}
|
||||||
|
onChange={(val) => setRealBagNo(val)}
|
||||||
|
/>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="client-content-footer">
|
||||||
|
<Button
|
||||||
|
title="开始"
|
||||||
|
type="success"
|
||||||
|
size="large"
|
||||||
|
ruleCode="startPack"
|
||||||
|
onClick={handleStart}
|
||||||
|
enable={btnEnable.start}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="确认打印"
|
||||||
|
size="large"
|
||||||
|
ruleCode="print_pack"
|
||||||
|
onClick={handlePrint}
|
||||||
|
enable={btnEnable.print}
|
||||||
|
/>
|
||||||
|
<ModelOverPrint ref={ModelOverPrintRef}/>
|
||||||
|
<Button
|
||||||
|
title="结束"
|
||||||
|
type="danger"
|
||||||
|
size="large"
|
||||||
|
ruleCode="end_pack"
|
||||||
|
enable={btnEnable.finish}
|
||||||
|
onClick={handleFinish}
|
||||||
|
/>
|
||||||
|
<ModelFinish ref={ModelFinishRef} />
|
||||||
|
<Button
|
||||||
|
title="中断"
|
||||||
|
type="warning"
|
||||||
|
size="large"
|
||||||
|
ruleCode="interrupt_pack"
|
||||||
|
enable={btnEnable.break}
|
||||||
|
onClick={handleBreak}
|
||||||
|
/>
|
||||||
|
<ModelBreak ref={ModelBreakRef} />
|
||||||
|
<Button
|
||||||
|
title={!isEdit ? "编辑" : "编辑完成"}
|
||||||
|
type="info"
|
||||||
|
size="large"
|
||||||
|
ruleCode="eidt_fczl"
|
||||||
|
onClick={handleEdit}
|
||||||
|
enable={btnEnable.edit}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.client-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.client-content-header {
|
||||||
|
height: ${headerHeight}px;
|
||||||
|
padding: 0 16px;
|
||||||
|
background: #272f3e;
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.logo-box {
|
||||||
|
float: left;
|
||||||
|
line-height: ${headerHeight}px;
|
||||||
|
}
|
||||||
|
.logo-img {
|
||||||
|
width: 175px;
|
||||||
|
height: 28px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.btn-box {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.btn.link-btn {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: ${headerHeight}px;
|
||||||
|
padding: 0 24px;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.client-content-footer {
|
||||||
|
background: white;
|
||||||
|
height: 96px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
.client-content-body {
|
||||||
|
background: rgb(243, 243, 243);
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 18px 16px 0;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 24px 32px;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.card-header .title {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #000000 85%;
|
||||||
|
}
|
||||||
|
.card-header .extra {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.card-body {
|
||||||
|
padding: 20px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.statu-box {
|
||||||
|
height: 112px;
|
||||||
|
width: 112px;
|
||||||
|
background: #fff7e8;
|
||||||
|
border: 1px solid #f16704;
|
||||||
|
border-radius: 6px;
|
||||||
|
line-height: 112px;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
color: #faab0c;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.statu-content {
|
||||||
|
margin-left: ${112 + 32}px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PackagingOperation;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import {default as reportRequest} from "../../reportUtils/request"
|
||||||
|
|
||||||
|
|
||||||
|
export 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 const request = (params) => {
|
||||||
|
let token = getQueryVariable("token");
|
||||||
|
return reportRequest(params, token)
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const titleSize = 28;
|
||||||
|
|
||||||
|
function Card(props) {
|
||||||
|
const {
|
||||||
|
full = true,
|
||||||
|
title,
|
||||||
|
titleSpace,
|
||||||
|
overVisible,
|
||||||
|
padding,
|
||||||
|
withBorder = true,
|
||||||
|
withBg = "rgba(6,36,109,0.80)"
|
||||||
|
} = props;
|
||||||
|
return (
|
||||||
|
<div className={'screen-card'}>
|
||||||
|
{title && <div className="screen-card-title">
|
||||||
|
{title}
|
||||||
|
</div>}
|
||||||
|
<div className="screen-card-content">
|
||||||
|
<div className="box">
|
||||||
|
{React.Children.map(props.children, item => item)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-card {
|
||||||
|
background: ${withBg ? withBg : 'transparent'};
|
||||||
|
border: 2px solid ${withBorder ? '#0072EE' : 'transparent'};
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
color: white;
|
||||||
|
height: ${full ? "100%" : "unset"}
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-title {
|
||||||
|
background: #0072EE;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: ${titleSize}px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-title:after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 10px;
|
||||||
|
height: ${titleSize}px;
|
||||||
|
position: absolute;
|
||||||
|
right: -10px;
|
||||||
|
bottom: 0;
|
||||||
|
border-color: transparent;
|
||||||
|
border-top-color: #0072EE;
|
||||||
|
border-left-color: #0072EE;
|
||||||
|
border-width: 14px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-content {
|
||||||
|
overflow: ${overVisible ? "visible" : "auto"};
|
||||||
|
padding-top: ${titleSpace ? titleSize : 0}px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-content > .box {
|
||||||
|
padding: ${padding ? padding : "unset"};
|
||||||
|
min-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-content::-webkit-scrollbar {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-content::-webkit-scrollbar-track {
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-content::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 10px;
|
||||||
|
-webkit-box-shadow: inset 0 0 0 4px #092062;
|
||||||
|
background-color: #0072EE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-card-content::-webkit-scrollbar-corner {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Card;
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React, {useRef, useEffect, useImperativeHandle} from "react";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
import theme from "./theme.json";
|
||||||
|
import './echarts-liquidfill'
|
||||||
|
// import('echarts-liquidfill');
|
||||||
|
|
||||||
|
echarts.registerTheme("darkBlue", theme);
|
||||||
|
|
||||||
|
export default React.forwardRef(({option}, ref) => {
|
||||||
|
const chartRef = useRef(null);
|
||||||
|
const chart = useRef();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
chart.current = echarts.init(chartRef.current, "darkBlue");
|
||||||
|
let resize = () => {
|
||||||
|
chart.current.resize();
|
||||||
|
};
|
||||||
|
window.addEventListener("resize", () => resize());
|
||||||
|
return window.removeEventListener("resize", () => resize());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (chart && option) {
|
||||||
|
// console.log("chart",chart)
|
||||||
|
chart.current.setOption(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
setOption: (opt) => {
|
||||||
|
// chart.current.setOption(Object.assign(opt, option));
|
||||||
|
chart.current.setOption(opt);
|
||||||
|
},
|
||||||
|
getChart: () => chart.current,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return <div ref={chartRef} style={{height: "100%", width: "100%"}}/>;
|
||||||
|
});
|
|
@ -0,0 +1,440 @@
|
||||||
|
{
|
||||||
|
"color": [
|
||||||
|
"#28a3ff",
|
||||||
|
"#ffc760",
|
||||||
|
"#6fe621",
|
||||||
|
"#f95757",
|
||||||
|
"#54c4f1",
|
||||||
|
"#fc8452",
|
||||||
|
"#9a60b4",
|
||||||
|
"#ea7ccc",
|
||||||
|
"#7289ab",
|
||||||
|
"#91ca8c",
|
||||||
|
"#f49f42"
|
||||||
|
],
|
||||||
|
"backgroundColor": "rgba(4,20,87,0)",
|
||||||
|
"textStyle": {},
|
||||||
|
"xAxis": {
|
||||||
|
"nameGap": 5
|
||||||
|
},
|
||||||
|
"yAxis": {
|
||||||
|
"nameGap": 5
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
},
|
||||||
|
"subtextStyle": {
|
||||||
|
"color": "#aaaaaa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"top": 30,
|
||||||
|
"left": 30,
|
||||||
|
"bottom": 30,
|
||||||
|
"right": 30
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 1
|
||||||
|
},
|
||||||
|
"lineStyle": {
|
||||||
|
"width": 2
|
||||||
|
},
|
||||||
|
"symbolSize": 10,
|
||||||
|
"symbol": "circle",
|
||||||
|
"smooth": false
|
||||||
|
},
|
||||||
|
"radar": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 1
|
||||||
|
},
|
||||||
|
"lineStyle": {
|
||||||
|
"width": 2
|
||||||
|
},
|
||||||
|
"symbolSize": 4,
|
||||||
|
"symbol": "circle",
|
||||||
|
"smooth": false
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
"itemStyle": {
|
||||||
|
"barBorderWidth": 0,
|
||||||
|
"barBorderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pie": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scatter": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"boxplot": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sankey": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"funnel": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gauge": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candlestick": {
|
||||||
|
"itemStyle": {
|
||||||
|
"color": "#fd1050",
|
||||||
|
"color0": "#0cf49b",
|
||||||
|
"borderColor": "#fd1050",
|
||||||
|
"borderColor0": "#0cf49b",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"graph": {
|
||||||
|
"itemStyle": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"lineStyle": {
|
||||||
|
"width": 1,
|
||||||
|
"color": "#aaaaaa"
|
||||||
|
},
|
||||||
|
"symbolSize": 4,
|
||||||
|
"symbol": "circle",
|
||||||
|
"smooth": false,
|
||||||
|
"color": [
|
||||||
|
"#dd6b66",
|
||||||
|
"#759aa0",
|
||||||
|
"#e69d87",
|
||||||
|
"#8dc1a9",
|
||||||
|
"#ea7e53",
|
||||||
|
"#eedd78",
|
||||||
|
"#73a373",
|
||||||
|
"#73b9bc",
|
||||||
|
"#7289ab",
|
||||||
|
"#91ca8c",
|
||||||
|
"#f49f42"
|
||||||
|
],
|
||||||
|
"label": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"map": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"areaColor": "#eee",
|
||||||
|
"borderColor": "#444",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"areaColor": "rgba(255,215,0,0.8)",
|
||||||
|
"borderColor": "#444",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "rgb(100,0,0)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geo": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"areaColor": "#eee",
|
||||||
|
"borderColor": "#444",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"areaColor": "rgba(255,215,0,0.8)",
|
||||||
|
"borderColor": "#444",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "rgb(100,0,0)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"categoryAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#aaaaaa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"valueAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#aaaaaa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#aaaaaa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#aaaaaa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toolbox": {
|
||||||
|
"iconStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderColor": "rgba(153,153,153,1)"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderColor": "rgba(102,102,102,1)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"show": true,
|
||||||
|
"left": "center",
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"axisPointer": {
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "rgba(238,238,238,1)",
|
||||||
|
"width": "1"
|
||||||
|
},
|
||||||
|
"crossStyle": {
|
||||||
|
"color": "rgba(238,238,238,1)",
|
||||||
|
"width": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#eeeeee",
|
||||||
|
"width": 1
|
||||||
|
},
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"color": "#dd6b66",
|
||||||
|
"borderWidth": 1
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"color": "#a9334c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"controlStyle": {
|
||||||
|
"normal": {
|
||||||
|
"color": "#eeeeee",
|
||||||
|
"borderColor": "#eeeeee",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"color": "#eeeeee",
|
||||||
|
"borderColor": "#eeeeee",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkpointStyle": {
|
||||||
|
"color": "#e43c59",
|
||||||
|
"borderColor": "#c23531"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"visualMap": {
|
||||||
|
"color": [
|
||||||
|
"#bf444c",
|
||||||
|
"#d88273",
|
||||||
|
"#f6efa6"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dataZoom": {
|
||||||
|
"backgroundColor": "rgba(47,69,84,0)",
|
||||||
|
"dataBackgroundColor": "rgba(255,255,255,0.3)",
|
||||||
|
"fillerColor": "rgba(167,183,204,0.4)",
|
||||||
|
"handleColor": "#a7b7cc",
|
||||||
|
"handleSize": "100%",
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"markPoint": {
|
||||||
|
"label": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"label": {
|
||||||
|
"color": "#eeeeee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
import React, { useRef, useEffect, useState, useMemo } from 'react'
|
||||||
|
|
||||||
|
const imgHeight = 951; //图片原始高度
|
||||||
|
const imgWidth = 184; //图片原始高度
|
||||||
|
const circleBorder = 2; //圆环的宽度
|
||||||
|
const baseSize = 170;//满状态宽度
|
||||||
|
const itemHeight = 370; //单个高度(包含空白)
|
||||||
|
const waveHeight = 29; //波浪高度
|
||||||
|
const colors = ["#8ee34d", "#f6c971", "#ea335d"]; //圆边框颜色
|
||||||
|
const colorIndexTrans = [2, 1, 0]; //调换第一个和最后一个位置
|
||||||
|
|
||||||
|
function CircleProcess(props) {
|
||||||
|
const sizeRef = useRef();
|
||||||
|
const outerRef = useRef();
|
||||||
|
const innerRef = useRef();
|
||||||
|
const [scale, setScale] = useState(1)
|
||||||
|
const [innerWidth, setInnerWidth] = useState(0)
|
||||||
|
|
||||||
|
//接受外部参数
|
||||||
|
let { percent:realPercent = 0.5, percentRule = [0, 0.8, 0.95], size = 0.9 } = props;
|
||||||
|
|
||||||
|
const percent = useMemo(() => {
|
||||||
|
return realPercent > 1 ? 1 : realPercent
|
||||||
|
}, [realPercent])
|
||||||
|
|
||||||
|
//颜色的下标
|
||||||
|
const colorIndex = useMemo(() => {
|
||||||
|
let res = 0;
|
||||||
|
percentRule.forEach((val, index) => {
|
||||||
|
if (percent > val) res = index;
|
||||||
|
})
|
||||||
|
return colorIndexTrans[res];
|
||||||
|
}, [percentRule, percent])
|
||||||
|
|
||||||
|
//颜色的位置
|
||||||
|
const positionY = useMemo(() => {
|
||||||
|
return -(-innerWidth + percent * innerWidth + waveHeight * scale + colorIndex * scale * itemHeight)
|
||||||
|
}, [innerWidth, percent, scale, colorIndex])
|
||||||
|
|
||||||
|
//计算组件宽度以及比例
|
||||||
|
const tableSetting = () => {
|
||||||
|
//组件实际宽度
|
||||||
|
let comWidth = sizeRef.current.clientWidth;
|
||||||
|
outerRef.current.style.height = comWidth * size + 'px';
|
||||||
|
outerRef.current.style.width = comWidth * size + 'px';
|
||||||
|
//波浪实际宽度
|
||||||
|
let innerWidth = innerRef.current.clientWidth;
|
||||||
|
setInnerWidth(innerWidth);
|
||||||
|
//计算比例
|
||||||
|
setScale(innerWidth / baseSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//监听父级宽度变化
|
||||||
|
let innerWindow = sizeRef.current.contentDocument.defaultView;
|
||||||
|
innerWindow.addEventListener("resize", tableSetting);
|
||||||
|
tableSetting();
|
||||||
|
return () => {
|
||||||
|
innerWindow.removeEventListener("resize", tableSetting);
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='circle-process'>
|
||||||
|
<object
|
||||||
|
ref={sizeRef}
|
||||||
|
tabIndex="-1"
|
||||||
|
type="text/html"
|
||||||
|
aria-hidden="true"
|
||||||
|
data="about:blank"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
border: "none",
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
opacity: 0,
|
||||||
|
zIndex: -1000,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}} />
|
||||||
|
<div className='out-circle' ref={outerRef}>
|
||||||
|
<div className='in-circle' ref={innerRef}>
|
||||||
|
<div className='pic-one'></div>
|
||||||
|
<div className='pic-two'></div>
|
||||||
|
<div className='text'>{realPercent * 100 + "%"}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.circle-process{
|
||||||
|
position:relative;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
.out-circle {
|
||||||
|
margin: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: ${circleBorder}px solid;
|
||||||
|
border-color: ${colors[colorIndex]};
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
.in-circle {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-circle .pic-one, .in-circle .pic-two {
|
||||||
|
background-image: url('/img/hpPercent.png');
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
background-size: ${scale * imgWidth}px ${scale * imgHeight}px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
background-position-y: ${positionY}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-circle .pic-one {
|
||||||
|
animation: 5s one linear infinite normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-circle .pic-two {
|
||||||
|
opacity: 0.3;
|
||||||
|
animation: 10s one linear infinite normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-circle .text {
|
||||||
|
position: absolute;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: ${scale*30}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes one {
|
||||||
|
from {
|
||||||
|
background-position-x: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-position-x: -${imgWidth * scale}px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CircleProcess
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function DateItem(props) {
|
||||||
|
const { text } = props;
|
||||||
|
return (
|
||||||
|
<span className='screen-dateItem'>
|
||||||
|
<span className="screen-dateItem-line left"></span>
|
||||||
|
<span className={text !== '-' && 'screen-dateItem-text'}>{text}</span>
|
||||||
|
<span className="screen-dateItem-line right"></span>
|
||||||
|
<style jsx>{`
|
||||||
|
@font-face {
|
||||||
|
font-family: 'UnidreamLED';
|
||||||
|
src: url('/font/UnidreamLED.ttf');
|
||||||
|
}
|
||||||
|
.screen-dateItem{
|
||||||
|
font-family: 'UnidreamLED';
|
||||||
|
width: 40px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
margin-left: -1px;
|
||||||
|
}
|
||||||
|
.screen-dateItem-line{
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.screen-dateItem-line.left{
|
||||||
|
left: 0;
|
||||||
|
top:0;
|
||||||
|
}
|
||||||
|
.screen-dateItem-line.right{
|
||||||
|
right: 0;
|
||||||
|
bottom:0;
|
||||||
|
}
|
||||||
|
.screen-dateItem-line::after,
|
||||||
|
.screen-dateItem-line::before{
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 1px;
|
||||||
|
height: 30%;
|
||||||
|
background: #28A3FF;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.screen-dateItem-line::before{
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.screen-dateItem-line::after{
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.screen-dateItem-text{
|
||||||
|
display: inline-block;
|
||||||
|
background: linear-gradient(to bottom, #020E2E, #0072EE, #020E2E);
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(DateItem);
|
|
@ -0,0 +1,105 @@
|
||||||
|
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
||||||
|
import DateItem from './DateItem'
|
||||||
|
|
||||||
|
function Footer(props) {
|
||||||
|
const { withTime = true, showDate } = props;
|
||||||
|
const [date, setDate] = useState([]);
|
||||||
|
const dateStrRef = useRef('');
|
||||||
|
|
||||||
|
const timeBoxRef = useRef();
|
||||||
|
|
||||||
|
const fixed0 = useCallback((num) => {
|
||||||
|
num = parseInt(num)
|
||||||
|
if (num < 10) num = "0" + num;
|
||||||
|
return num + '';
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const getTime = useCallback(() => {
|
||||||
|
let dateStr = dateStrRef.current;
|
||||||
|
let currentDate = new Date();
|
||||||
|
let year = currentDate.getUTCFullYear();
|
||||||
|
year = fixed0(year);
|
||||||
|
let month = currentDate.getMonth() + 1;
|
||||||
|
month = fixed0(month);
|
||||||
|
let day = currentDate.getDate();
|
||||||
|
day = fixed0(day);
|
||||||
|
let hour = currentDate.getHours();
|
||||||
|
hour = fixed0(hour);
|
||||||
|
let minute = currentDate.getMinutes();
|
||||||
|
minute = fixed0(minute);
|
||||||
|
let second = currentDate.getSeconds();
|
||||||
|
second = fixed0(second);
|
||||||
|
|
||||||
|
let newDate = `${year}-${month}-${day}`;
|
||||||
|
let time = `${hour}:${minute}:${second}`;
|
||||||
|
|
||||||
|
if (showDate) {
|
||||||
|
setDate(showDate.split(''));
|
||||||
|
}else if(dateStr !== newDate && !showDate) {
|
||||||
|
dateStrRef.current = newDate;
|
||||||
|
setDate(newDate.split(''));
|
||||||
|
}
|
||||||
|
withTime && timeBoxRef.current && (timeBoxRef.current.innerText = time)
|
||||||
|
}, [ withTime, showDate ]);
|
||||||
|
|
||||||
|
const updateTime = useCallback(() => {
|
||||||
|
getTime();
|
||||||
|
if (!showDate) {
|
||||||
|
setTimeout(updateTime, 1000)
|
||||||
|
}
|
||||||
|
}, [getTime, showDate])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateTime();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [updateTime]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="screen-footer">
|
||||||
|
<div className="screen-footer-content">
|
||||||
|
<span className="screen-footer-date">
|
||||||
|
{date.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<DateItem key={index} text={item} />
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
<span className="screen-footer-time" ref={timeBoxRef}>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-footer{
|
||||||
|
height: 82px;
|
||||||
|
background: url('/img/footerBg.png') center bottom no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
padding-top: 18px;
|
||||||
|
}
|
||||||
|
.screen-footer-content{
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
width: 797px;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.screen-footer-date{
|
||||||
|
font-size: 36px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.screen-footer-time{
|
||||||
|
font-size: 30px;
|
||||||
|
font-family: 'Helvetica Neue';
|
||||||
|
letter-spacing: 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0 25px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(Footer)
|
|
@ -0,0 +1,63 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ClockIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
function Header(props) {
|
||||||
|
const { title, time } = props;
|
||||||
|
return (
|
||||||
|
<div className="screen-header">
|
||||||
|
<div className="logo"></div>
|
||||||
|
<span className="screen-title">{title}</span>
|
||||||
|
<span className=" block text-white font-bold text-2xl leading-6 screen-time ">
|
||||||
|
<div className=" inline-block">
|
||||||
|
{time === undefined ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
|
<ClockIcon className=" inline-block flex-0 w-8 h-8 -mt-1" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{time}
|
||||||
|
</span>
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-header {
|
||||||
|
height: 96px;
|
||||||
|
background: url("/img/headerBg.png") center top no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.screen-header .screen-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 42px;
|
||||||
|
font-weight: bold;
|
||||||
|
background: linear-gradient(to right, #28a8ff, #3beaff, #28a8ff);
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "UnidreamLED";
|
||||||
|
src: url("/font/UnidreamLED.ttf");
|
||||||
|
}
|
||||||
|
.screen-header .screen-time {
|
||||||
|
font-family: "黑体";
|
||||||
|
}
|
||||||
|
.screen-header .logo {
|
||||||
|
position: absolute;
|
||||||
|
height: 32px;
|
||||||
|
width: 200px;
|
||||||
|
background: url("/img/logo.png") center center no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
top: 4px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header;
|
|
@ -0,0 +1,138 @@
|
||||||
|
import React, {useRef, useEffect, useCallback, useLayoutEffect} from 'react';
|
||||||
|
import _ from 'lodash'
|
||||||
|
import {imgUrl as url} from '../../../utils/requests'
|
||||||
|
|
||||||
|
let defaultUrl = "/img/noImg.png";
|
||||||
|
|
||||||
|
const fetchImg = fileCode => {
|
||||||
|
// console.log("fetchImg")
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let ajax = new XMLHttpRequest();
|
||||||
|
ajax.open("GET", `${url}${fileCode}`, true);
|
||||||
|
ajax.responseType = "blob";
|
||||||
|
ajax.setRequestHeader("Cache-Control", "max-age=3600")
|
||||||
|
ajax.setRequestHeader("Authorization", window.localStorage.getItem("token"))
|
||||||
|
ajax.onload = function () {
|
||||||
|
if (ajax.status == 200) {
|
||||||
|
ajax.response.text().then(res => {
|
||||||
|
// console.log("res", JSON.parse(res))
|
||||||
|
try{
|
||||||
|
res = JSON.parse(res);
|
||||||
|
}catch(e){
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
if (res.error) {
|
||||||
|
resolve(defaultUrl)
|
||||||
|
} else {
|
||||||
|
let blob = ajax.response;
|
||||||
|
let oFileReader = new FileReader();
|
||||||
|
oFileReader.onloadend = function (e) {
|
||||||
|
console.log("e", e)
|
||||||
|
let base64 = e.target.result;
|
||||||
|
resolve(base64)
|
||||||
|
};
|
||||||
|
oFileReader.readAsDataURL(blob);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ajax.send();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoCard(props) {
|
||||||
|
const imgBox = useRef();
|
||||||
|
const {imgKey, status = {}, columns = [],extraColumns = [], data = {}, cardWidth = 256, contentHeight} = props;
|
||||||
|
const {key = '', value, color} = status;
|
||||||
|
|
||||||
|
const setImg = useCallback(async () => {
|
||||||
|
let url = defaultUrl;
|
||||||
|
if (imgKey && data[imgKey]) {
|
||||||
|
url = await fetchImg(data[imgKey]);
|
||||||
|
}
|
||||||
|
imgBox.current && (imgBox.current.style.backgroundImage = `url('${url}')`)
|
||||||
|
}, [data[imgKey]])
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
let observer = new IntersectionObserver((entries, observer) => {
|
||||||
|
if(entries[0].intersectionRatio > 0) setImg();
|
||||||
|
}, {});
|
||||||
|
observer && observer.observe(imgBox.current);
|
||||||
|
return () => {
|
||||||
|
observer && observer.unobserve(imgBox.current);
|
||||||
|
}
|
||||||
|
}, [setImg])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='infoCard'>
|
||||||
|
<div className="img" ref={imgBox}/>
|
||||||
|
{data[key] && (
|
||||||
|
<div className="status" style={{backgroundColor: color? color(data[key]) : "unset" }}>
|
||||||
|
{value? value(data[key]) : data[key]}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="contentBox">
|
||||||
|
{columns.concat(extraColumns).map(({title, code, text}) => {
|
||||||
|
return (
|
||||||
|
<div key={title} className="info-item">
|
||||||
|
<span className='label'>{title}</span>
|
||||||
|
<span className='value'>{text || data[code]}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.infoCard{
|
||||||
|
height: 100%;
|
||||||
|
width: ${cardWidth? cardWidth + 'px' : '100%'};
|
||||||
|
min-width: ${cardWidth? cardWidth + 'px' : '100%'};
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.infoCard > .img{
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.infoCard > .status{
|
||||||
|
position: absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 28px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.infoCard > .contentBox{
|
||||||
|
background: rgba(6,45,141,0.80);
|
||||||
|
border: 1px solid rgba(255,255,255,0.16);
|
||||||
|
padding: 5px 10px;
|
||||||
|
height: ${contentHeight? contentHeight + 'px' : 'unset'};
|
||||||
|
}
|
||||||
|
.infoCard .info-item{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
line-height: 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.infoCard .info-item .label,
|
||||||
|
.infoCard .info-item .value{
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.infoCard .info-item .value{
|
||||||
|
font-size: 14px;
|
||||||
|
color: #28EAFF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InfoCard;
|
|
@ -0,0 +1,361 @@
|
||||||
|
import React, {
|
||||||
|
useState,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
|
import InfoCard from "./InfoCard";
|
||||||
|
import Row from "../Row";
|
||||||
|
|
||||||
|
const gutter = 20;
|
||||||
|
const cardBottom = 20;
|
||||||
|
|
||||||
|
const InfoCardList = React.forwardRef((props, ref) => {
|
||||||
|
const {
|
||||||
|
mainKey,
|
||||||
|
speed = 50,
|
||||||
|
span = 4,
|
||||||
|
withClassify = false,
|
||||||
|
autoNext = true,
|
||||||
|
classifyKey = { name: "name", children: "children" },
|
||||||
|
pageFinished,
|
||||||
|
type = "move",
|
||||||
|
extraColumnsOpt,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const sizeRef = useRef();
|
||||||
|
const infoCardListRef = useRef();
|
||||||
|
const pageBoxRef = useRef();
|
||||||
|
const pageRef = useRef(1);
|
||||||
|
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
const [energy, setEnergy] = useState([]);
|
||||||
|
const [autoScroll, setAutoScroll] = useState([0, 0]);
|
||||||
|
|
||||||
|
const classifyRef = useRef([]);
|
||||||
|
const classifyBoxRef = useRef();
|
||||||
|
|
||||||
|
const pageSize = useMemo(() => Math.floor(24 / span), [span]);
|
||||||
|
const totalPage = useMemo(() => {
|
||||||
|
if (withClassify) {
|
||||||
|
let { children } = classifyKey;
|
||||||
|
let size = 0;
|
||||||
|
classifyRef.current = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
let itemPageSize = Math.ceil((item[children].length || 0) / pageSize);
|
||||||
|
size += itemPageSize;
|
||||||
|
classifyRef.current.push(size);
|
||||||
|
});
|
||||||
|
return size;
|
||||||
|
} else return Math.ceil(data.length / pageSize);
|
||||||
|
}, [data, pageSize, withClassify, classifyKey]);
|
||||||
|
const [pageHeight, setPageHeight] = useState(0);
|
||||||
|
|
||||||
|
const setActiveClassify = useCallback(
|
||||||
|
(page) => {
|
||||||
|
if (!classifyBoxRef.current) return;
|
||||||
|
let classifyIndex =
|
||||||
|
classifyRef.current.findIndex((maxPageSize) => page <= maxPageSize) ||
|
||||||
|
0;
|
||||||
|
let { name } = classifyKey;
|
||||||
|
let oprationname = data?.[classifyIndex]?.[name].split("】")[1];
|
||||||
|
let text = oprationname === undefined ? "" : "- " + oprationname;
|
||||||
|
if (text) {
|
||||||
|
classifyBoxRef.current.style.display = "block";
|
||||||
|
classifyBoxRef.current.innerText = text;
|
||||||
|
} else classifyBoxRef.current.style.display = "none";
|
||||||
|
},
|
||||||
|
[data]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setPageBoxCss = useCallback(
|
||||||
|
(page) => {
|
||||||
|
pageRef.current = page;
|
||||||
|
setActiveClassify(page);
|
||||||
|
pageBoxRef.current.style.transition =
|
||||||
|
page === 1 ? "none" : "transform 0.3s";
|
||||||
|
pageBoxRef.current.style.transform = `translateY(${
|
||||||
|
-(page - 1) * (pageHeight + cardBottom)
|
||||||
|
}px)`;
|
||||||
|
},
|
||||||
|
[pageHeight, setActiveClassify]
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextPage = useCallback(() => {
|
||||||
|
// console.log();
|
||||||
|
let page = pageRef.current;
|
||||||
|
if (page + 1 > totalPage) {
|
||||||
|
if (autoNext) {
|
||||||
|
setPageBoxCss(1);
|
||||||
|
} else {
|
||||||
|
pageFinished && pageFinished();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setPageBoxCss(page + 1);
|
||||||
|
}
|
||||||
|
}, [totalPage]);
|
||||||
|
|
||||||
|
const moveChecking = useCallback(() => {
|
||||||
|
let { offsetWidth, scrollWidth } = infoCardListRef.current;
|
||||||
|
setAutoScroll([offsetWidth, scrollWidth]);
|
||||||
|
}, [data]);
|
||||||
|
const pageChecking = useCallback(() => {
|
||||||
|
let { scrollHeight, offsetHeight } = sizeRef.current;
|
||||||
|
setPageHeight(scrollHeight);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === "move") {
|
||||||
|
let innerWindow = sizeRef.current.contentDocument.defaultView;
|
||||||
|
innerWindow.addEventListener("resize", moveChecking);
|
||||||
|
moveChecking();
|
||||||
|
return () => {
|
||||||
|
innerWindow.removeEventListener("resize", moveChecking);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [moveChecking]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === "page") {
|
||||||
|
let innerWindow = sizeRef.current.contentDocument.defaultView;
|
||||||
|
innerWindow.addEventListener("resize", pageChecking);
|
||||||
|
pageChecking();
|
||||||
|
return () => {
|
||||||
|
innerWindow.removeEventListener("resize", pageChecking);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [pageChecking]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPageBoxCss(1);
|
||||||
|
}, [data, setPageBoxCss]);
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
setData: (data) => {
|
||||||
|
setData(data);
|
||||||
|
setPageBoxCss(1);
|
||||||
|
},
|
||||||
|
setEnergy:(data)=>{
|
||||||
|
setEnergy(data);
|
||||||
|
},
|
||||||
|
nextPage,
|
||||||
|
}),
|
||||||
|
[nextPage]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="infoCardList" ref={infoCardListRef}>
|
||||||
|
{type === "move" && (
|
||||||
|
<div className="box move">
|
||||||
|
{data.map((item) => {
|
||||||
|
return (
|
||||||
|
<div key={item[mainKey]} className="item">
|
||||||
|
<InfoCard {...otherProps} data={item} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{type === "page" && (
|
||||||
|
<div className="page-content">
|
||||||
|
{withClassify === true && (
|
||||||
|
<div className="classify" ref={classifyBoxRef}></div>
|
||||||
|
)}
|
||||||
|
<div className="classifycenter -mt-14 w-full text-center ">
|
||||||
|
<label className="bg-[#1890ff] p-1 pt-0.5 pb-0.5">能源信息</label>
|
||||||
|
</div>
|
||||||
|
<div className="classifycenterval grid grid-cols-2 gap-1 place-items-start -mt-7 w-full">
|
||||||
|
{console.log("ernergy",energy)}
|
||||||
|
{/* {props.energy ?? `日期:${props.energy[0]} 水: ${props.energy[0].water} 电: ${1} 气: ${1}`} */}
|
||||||
|
{
|
||||||
|
energy.map(x=>{
|
||||||
|
return (
|
||||||
|
<span className={ energy.indexOf(x)===0?`flex-1 mr-20 justify-self-end`:`flex-1 ml-20 justify-self-start`}> 日期:{x.time} 水: {x.water} 电: {x.electricity} 气: {x.gas}</span>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="page-box">
|
||||||
|
<object
|
||||||
|
ref={sizeRef}
|
||||||
|
tabIndex="-1"
|
||||||
|
type="text/html"
|
||||||
|
aria-hidden="true"
|
||||||
|
data="about:blank"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
border: "none",
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
opacity: 0,
|
||||||
|
zIndex: -1000,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}
|
||||||
|
></object>
|
||||||
|
<div className="page" ref={pageBoxRef}>
|
||||||
|
{withClassify === false && (
|
||||||
|
<Row className="height-100" gutter={20}>
|
||||||
|
{data.map((item) => {
|
||||||
|
let extraColumns = [];
|
||||||
|
if (extraColumnsOpt) {
|
||||||
|
let { key, name, val, max } = extraColumnsOpt;
|
||||||
|
let extraData = item[key];
|
||||||
|
extraData.forEach((i, index) => {
|
||||||
|
if (index < max)
|
||||||
|
extraColumns.push({ title: i[name], text: i[val] });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Row.Col span={span} key={item[mainKey]}>
|
||||||
|
<div className="item height-100">
|
||||||
|
<InfoCard
|
||||||
|
{...otherProps}
|
||||||
|
extraColumns={extraColumns}
|
||||||
|
data={item}
|
||||||
|
cardWidth={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Row.Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
{withClassify === true &&
|
||||||
|
data.map((item) => {
|
||||||
|
let { name, children } = classifyKey;
|
||||||
|
return (
|
||||||
|
<Row gutter={20} key={item[name]}>
|
||||||
|
{item[children].map((item) => {
|
||||||
|
let extraColumns = [];
|
||||||
|
if (extraColumnsOpt) {
|
||||||
|
let { key, name, val, max } = extraColumnsOpt;
|
||||||
|
let extraData = item[key];
|
||||||
|
extraData.forEach((i, index) => {
|
||||||
|
if (index < max)
|
||||||
|
extraColumns.push({
|
||||||
|
title: i[name],
|
||||||
|
text: i[val],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Row.Col span={span} key={item[mainKey]}>
|
||||||
|
<div className="item">
|
||||||
|
<InfoCard
|
||||||
|
{...otherProps}
|
||||||
|
extraColumns={extraColumns}
|
||||||
|
data={item}
|
||||||
|
cardWidth={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Row.Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<style jsx>{`
|
||||||
|
.infoCardList {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.infoCardList > .box {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.infoCardList > .box > .item {
|
||||||
|
height: 100%;
|
||||||
|
padding-right: ${gutter}px;
|
||||||
|
}
|
||||||
|
.infoCardList > .box.move {
|
||||||
|
animation: move ${autoScroll[1] / speed}s linear infinite;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
transform: translateX(0px);
|
||||||
|
}
|
||||||
|
@keyframes move {
|
||||||
|
from {
|
||||||
|
transform: translateX(${autoScroll[0]}px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(${-autoScroll[1]}px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.infoCardList .page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.infoCardList .page :global(.col) {
|
||||||
|
margin-bottom: ${cardBottom}px;
|
||||||
|
}
|
||||||
|
.infoCardList .page .item {
|
||||||
|
height: ${pageHeight}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoCardList .page-content {
|
||||||
|
height: 100%;
|
||||||
|
padding-top: 55px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.infoCardList .classify {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
left: 100px;
|
||||||
|
top: 0px;
|
||||||
|
// background: #15579f;
|
||||||
|
// border-radius: 3px;
|
||||||
|
// transition: all .3s;
|
||||||
|
}
|
||||||
|
.infoCardList .classifycenter label {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
.infoCardList .classifycenter {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
// left: 48.5%;
|
||||||
|
// padding: 2px 5px;
|
||||||
|
// top: -3px;
|
||||||
|
// background: #1890ff;
|
||||||
|
// border-radius: 3px;
|
||||||
|
// transition: all .3s;
|
||||||
|
}
|
||||||
|
.infoCardList .classifycenterval {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
// left: 19%;
|
||||||
|
// top: 20px;
|
||||||
|
// padding: 2px 5px;
|
||||||
|
// border-radius: 3px;
|
||||||
|
// transition: all .3s;
|
||||||
|
}
|
||||||
|
.infoCardList .page-box {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default InfoCardList;
|
|
@ -0,0 +1,207 @@
|
||||||
|
import React, {useRef} from 'react';
|
||||||
|
|
||||||
|
function Row(props) {
|
||||||
|
const {gutter = 0, bottom = 0, className} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...props} className={`row ${className ? className : ''} `}>
|
||||||
|
{React.Children.map(props.children, item => item)}
|
||||||
|
<style jsx>{`
|
||||||
|
.row {
|
||||||
|
margin-left: ${gutter / -2}px;
|
||||||
|
margin-right: ${gutter / -2}px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row:before,
|
||||||
|
.row:after {
|
||||||
|
content: " ";
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row:after {
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
font-size: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row > :global(.col) {
|
||||||
|
padding-left: ${gutter / 2}px;
|
||||||
|
padding-right: ${gutter / 2}px;
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: ${bottom}px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<style jsx global>{`
|
||||||
|
.height-100 {
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-75 {
|
||||||
|
height: 75%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-70 {
|
||||||
|
height: 70%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-50 {
|
||||||
|
height: 50%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-55 {
|
||||||
|
height: 55%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-60 {
|
||||||
|
height: 60%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-45 {
|
||||||
|
height: 45%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-40 {
|
||||||
|
height: 40%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-35 {
|
||||||
|
height: 35%
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-30 {
|
||||||
|
height: 30%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-0 {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-1 {
|
||||||
|
width: 4.166666666666666%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-2 {
|
||||||
|
width: 8.333333333333332%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-3 {
|
||||||
|
width: 12.5%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-4 {
|
||||||
|
width: 16.666666666666664%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-4-2 {
|
||||||
|
width: 20%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-5 {
|
||||||
|
width: 20.833333333333336%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-6 {
|
||||||
|
width: 25%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-7 {
|
||||||
|
width: 29.166666666666668%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-8 {
|
||||||
|
width: 33.33333333333333%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-9 {
|
||||||
|
width: 37.5%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-10 {
|
||||||
|
width: 41.66666666666667%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-11 {
|
||||||
|
width: 45.83333333333333%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-12 {
|
||||||
|
width: 50%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-13 {
|
||||||
|
width: 54.166666666666664%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-14 {
|
||||||
|
width: 58.333333333333336%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-15 {
|
||||||
|
width: 62.5%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-16 {
|
||||||
|
width: 66.66666666666666%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-17 {
|
||||||
|
width: 70.83333333333334%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-18 {
|
||||||
|
width: 75%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-19 {
|
||||||
|
width: 79.16666666666666%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-20 {
|
||||||
|
width: 83.33333333333334%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-21 {
|
||||||
|
width: 87.5%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-22 {
|
||||||
|
width: 91.66666666666666%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-23 {
|
||||||
|
width: 95.83333333333334%
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-24 {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Col(props) {
|
||||||
|
const {span = 24, bottom = 0, className, style = {}} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`col ${span && 'col-' + span} ${className ? className : ''} `} style={style}>
|
||||||
|
<div className='height-100'>
|
||||||
|
{React.Children.map(props.children, item => item)}
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.col {
|
||||||
|
padding-bottom: ${bottom}px !important;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Row.Col = Col;
|
||||||
|
|
||||||
|
export default Row;
|
|
@ -0,0 +1,181 @@
|
||||||
|
import React, { useImperativeHandle, useRef, useEffect, useCallback, useState, useMemo } from 'react'
|
||||||
|
|
||||||
|
const itemSize = 40;
|
||||||
|
|
||||||
|
const Table = React.forwardRef((props, ref) => {
|
||||||
|
const {columns = [], mainKey = 'id', autoNext = true, pageFinished, dataSource} = props;
|
||||||
|
|
||||||
|
const sizeRef = useRef();
|
||||||
|
const tableRef = useRef();
|
||||||
|
|
||||||
|
const [pageSize, setPageSize] = useState(0);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
|
||||||
|
const [data, setData] = useState([])
|
||||||
|
|
||||||
|
const totalPage = useMemo(
|
||||||
|
() => Math.ceil(data.length / pageSize),
|
||||||
|
[data, pageSize]
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentData = useMemo(
|
||||||
|
() => data.slice(pageSize * (page - 1), pageSize * page),
|
||||||
|
[data, pageSize, page]
|
||||||
|
)
|
||||||
|
|
||||||
|
const tableSetting = useCallback(() => {
|
||||||
|
let { offsetHeight, scrollHeight, offsetWidth, scrollWidth } = tableRef.current;
|
||||||
|
let pageSize = Math.floor(offsetHeight / itemSize) - 1;
|
||||||
|
setPageSize(pageSize);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const nextPage = useCallback(() => {
|
||||||
|
if(page + 1 > totalPage){
|
||||||
|
if(autoNext){
|
||||||
|
setPage(1);
|
||||||
|
}else{
|
||||||
|
pageFinished && pageFinished();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
setPage(page + 1);
|
||||||
|
}
|
||||||
|
}, [totalPage, page])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let innerWindow = sizeRef.current.contentDocument.defaultView;
|
||||||
|
innerWindow.addEventListener("resize", tableSetting);
|
||||||
|
tableSetting();
|
||||||
|
return () => {
|
||||||
|
innerWindow.removeEventListener("resize", tableSetting);
|
||||||
|
}
|
||||||
|
}, [tableSetting])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (dataSource) {
|
||||||
|
setData(dataSource);
|
||||||
|
setPage(1);
|
||||||
|
}
|
||||||
|
}, [dataSource]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
nextPage,
|
||||||
|
setData: data => {
|
||||||
|
setData(data);
|
||||||
|
setPage(1);
|
||||||
|
},
|
||||||
|
addData: addData => {
|
||||||
|
setData(data.concat(...addData))
|
||||||
|
},
|
||||||
|
updateData: updateData => {
|
||||||
|
setData(data.map(item => {
|
||||||
|
let mainValue = item[mainKey];
|
||||||
|
let update = updateData.find(i => i[mainKey] === mainValue);
|
||||||
|
if(update) item = update;
|
||||||
|
return item;
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
deleteData: deleteData => {
|
||||||
|
setData(data.filter(item => {
|
||||||
|
let mainValue = item[mainKey];
|
||||||
|
return !deleteData.find(i => i[mainKey] === mainValue);
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
}), [data, nextPage, mainKey])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="screen-table" ref={tableRef}>
|
||||||
|
<object
|
||||||
|
ref={sizeRef}
|
||||||
|
tabIndex="-1"
|
||||||
|
type="text/html"
|
||||||
|
aria-hidden="true"
|
||||||
|
data="about:blank"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
border: "none",
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
opacity: 0,
|
||||||
|
zIndex: -1000,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}>
|
||||||
|
</object>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{columns.map(({title, width}) => {
|
||||||
|
let thProps = {key: title};
|
||||||
|
if(width !== undefined) thProps.style = {width: width + 'px'}
|
||||||
|
return <th {...thProps}>{title}</th>
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{currentData.map((item, index) => (
|
||||||
|
<tr key={item[mainKey]} className={index % 2 === 1 ? `delay-${index} stripe`: `delay-${index}`}>
|
||||||
|
{columns.map(config => {
|
||||||
|
let {title,code, render} = config;
|
||||||
|
if(render) return <td key={title}>{render(item[code], config, item)}</td>;
|
||||||
|
return <td key={title}>{item[code]}</td>
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-table{
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.screen-table > table{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.screen-table th,
|
||||||
|
.screen-table td{
|
||||||
|
height: ${itemSize}px;
|
||||||
|
word-break: break-all;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.screen-table th{
|
||||||
|
background: rgba(40, 163, 255, 0.25);
|
||||||
|
}
|
||||||
|
.screen-table .stripe td{
|
||||||
|
background: rgba(40, 163, 255, 0.1);
|
||||||
|
}
|
||||||
|
.screen-table tbody tr{
|
||||||
|
animation-name: show;
|
||||||
|
animation-duration: 0.3s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
opacity: 0;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
@keyframes show {
|
||||||
|
from {opacity: 0;}
|
||||||
|
to {opacity: 1;}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<style jsx global>{`
|
||||||
|
.delay-0{ animation-delay: 0s; }
|
||||||
|
.delay-1{ animation-delay: 0.2s; }
|
||||||
|
.delay-2{ animation-delay: 0.4s; }
|
||||||
|
.delay-3{ animation-delay: 0.6s; }
|
||||||
|
.delay-4{ animation-delay: 0.8s; }
|
||||||
|
.delay-5{ animation-delay: 1s; }
|
||||||
|
.delay-6{ animation-delay: 1.2s; }
|
||||||
|
.delay-7{ animation-delay: 1.4s; }
|
||||||
|
.delay-8{ animation-delay: 1.6s; }
|
||||||
|
.delay-9{ animation-delay: 1.8s; }
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Table;
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, {useMemo} from 'react';
|
||||||
|
|
||||||
|
const labelWidth = 60;
|
||||||
|
|
||||||
|
const flatRules = {
|
||||||
|
// 给1 展示100%
|
||||||
|
// 给1.0000 展示100.0%
|
||||||
|
// 给0.9 展示90%
|
||||||
|
// 给0.90000 展示90.0%
|
||||||
|
"strange": val => {
|
||||||
|
val = val + "";
|
||||||
|
let valArr = val.split("");
|
||||||
|
// console.log("val1", val);
|
||||||
|
for (let n = 2; n > 0; n--) {
|
||||||
|
let dotIndex = valArr.findIndex(i => i === ".");
|
||||||
|
if (dotIndex > 0) {
|
||||||
|
if (dotIndex === valArr.length - 1) {
|
||||||
|
valArr.push("0")
|
||||||
|
}
|
||||||
|
valArr[dotIndex] = valArr.splice(dotIndex + 1, 1, valArr[dotIndex])[0]
|
||||||
|
} else {
|
||||||
|
valArr.push("0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dotIndex = valArr.findIndex(i => i === ".");
|
||||||
|
if (dotIndex > 0) {
|
||||||
|
while (valArr[0] === "0" && valArr[1] !== ".") {
|
||||||
|
valArr = valArr.splice(1, valArr.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val = valArr.join("");
|
||||||
|
// console.log("val2", val);
|
||||||
|
return val + "%"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function TableProgress(props) {
|
||||||
|
const {percent, toFixed = 1, rule} = props;
|
||||||
|
|
||||||
|
const percentVal = useMemo(() => {
|
||||||
|
if (rule && flatRules[rule]) return flatRules[rule](percent);
|
||||||
|
if (toFixed === false) return percent * 100 + "%";
|
||||||
|
return (percent * 100).toFixed(toFixed) + "%"
|
||||||
|
}, [percent, toFixed])
|
||||||
|
|
||||||
|
const width = useMemo(() => {
|
||||||
|
if (percent > 1) percent = 1;
|
||||||
|
if (toFixed === false) return percent * 100;
|
||||||
|
return (percent * 100).toFixed(toFixed)
|
||||||
|
}, [percent, toFixed])
|
||||||
|
return (
|
||||||
|
<div className='table-progress'>
|
||||||
|
<div className='table-progress-label'>{percentVal}</div>
|
||||||
|
<div className='table-progress-bar'>
|
||||||
|
<div className='table-progress-bar-content'></div>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.table-progress-bar{
|
||||||
|
margin-right: ${labelWidth}px;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
.table-progress-bar-content{
|
||||||
|
height: 12px;
|
||||||
|
background: rgba(255,255,255,0.16);
|
||||||
|
}
|
||||||
|
.table-progress-bar-content:before{
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
background: #28A3FF;
|
||||||
|
width: ${width}%;
|
||||||
|
}
|
||||||
|
.table-progress-label{
|
||||||
|
width: ${labelWidth}px;
|
||||||
|
float: right;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TableProgress;
|
|
@ -0,0 +1,141 @@
|
||||||
|
import React, { useRef, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
const TimeShaft = React.forwardRef((props, ref) => {
|
||||||
|
const sizeRef = useRef();
|
||||||
|
const timeShaftRef = useRef();
|
||||||
|
const timeShaftContentRef = useRef();
|
||||||
|
const [move, setTranslateY] = useState(false);
|
||||||
|
const { data = [], time = "time", type = "type", name = "name", detail = "detail" } = props
|
||||||
|
//获取组件高度
|
||||||
|
const tableSetting = () => {
|
||||||
|
//组件实际高度
|
||||||
|
let comHeight = timeShaftRef.current.clientHeight;
|
||||||
|
let contentHeight = timeShaftContentRef.current.clientHeight;
|
||||||
|
let slate = comHeight < contentHeight;
|
||||||
|
|
||||||
|
setTranslateY(slate)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//监听父级宽度变化
|
||||||
|
let innerWindow = sizeRef.current.contentDocument.defaultView;
|
||||||
|
innerWindow.addEventListener("resize", tableSetting);
|
||||||
|
tableSetting();
|
||||||
|
return () => {
|
||||||
|
innerWindow.removeEventListener("resize", tableSetting);
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={timeShaftRef} className="screen-time-shaft">
|
||||||
|
<object
|
||||||
|
ref={sizeRef}
|
||||||
|
tabIndex="-1"
|
||||||
|
type="text/html"
|
||||||
|
aria-hidden="true"
|
||||||
|
data="about:blank"
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
border: "none",
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
opacity: 0,
|
||||||
|
zIndex: -1000,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}} />
|
||||||
|
<div ref={timeShaftContentRef} className={move ? "screen-time-shaft-content move" : "screen-time-shaft-content"}>
|
||||||
|
{
|
||||||
|
data.map((item, index) => {
|
||||||
|
return (<div className="screen-time-shaft-item" key={index}>
|
||||||
|
<div className={`${(item[type] === 'm' && item[time] <= 10) ? 'screen-time-shaft-time-before' : ''} screen-time-shaft-time`}>{`${item[time]}${item[type] === 'm' ? '分钟' : '小时'}前`}</div>
|
||||||
|
<div className="screen-time-shaft-axle">
|
||||||
|
<div className="screen-time-shaft-dot"></div>
|
||||||
|
<div className="screen-time-shaft-line"></div>
|
||||||
|
</div>
|
||||||
|
<div className="screen-time-shaft-name">【{item[name]}】</div>
|
||||||
|
<div className="screen-time-shaft-detail">{item[detail]}</div>
|
||||||
|
</div>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-time-shaft{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
@keyframes move {
|
||||||
|
0% ,10% {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.screen-time-shaft-content.move{
|
||||||
|
animation: 20s move linear infinite;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-item{
|
||||||
|
width: 100%;
|
||||||
|
line-height: 34px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-time{
|
||||||
|
width: 68px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
text-align: center;
|
||||||
|
background: #0072EE;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
padding: 0 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-time-before{
|
||||||
|
background: #F5A623;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-axle{
|
||||||
|
width: 5px;
|
||||||
|
padding: 0 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
transform: translateY(16px);
|
||||||
|
}
|
||||||
|
.screen-time-shaft-dot{
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #0072EE;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-line{
|
||||||
|
height: 100%;
|
||||||
|
width: 1px;
|
||||||
|
background: #0072EE;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-name{
|
||||||
|
width: 198px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #28A3FF;
|
||||||
|
}
|
||||||
|
.screen-time-shaft-detail{
|
||||||
|
width: calc( 100% - 268px);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TimeShaft;
|
|
@ -0,0 +1,12 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
function WithConfig(props) {
|
||||||
|
return (
|
||||||
|
<Head>
|
||||||
|
<script src="/config.js"></script>
|
||||||
|
</Head>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WithConfig;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
images: {
|
||||||
|
loader: 'imgix',
|
||||||
|
path: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"name": "equipment-dashboard",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cross-env NODE_OPTIONS='--inspect' next dev",
|
||||||
|
"build": "next build && next export && rm -rf equipscreen && cp -r out equipscreen",
|
||||||
|
"start": "next start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/charts": "^1.4.2",
|
||||||
|
"@headlessui/react": "^1.5.0",
|
||||||
|
"@heroicons/react": "^1.0.1",
|
||||||
|
"@tailwindcss/forms": "^0.5.0",
|
||||||
|
"antd": "^4.21.6",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"d3-interpolate": "^3.0.1",
|
||||||
|
"decimal.js": "^10.4.1",
|
||||||
|
"dedupe": "^4.0.2",
|
||||||
|
"echarts": "^5.3.2",
|
||||||
|
"isomorphic-fetch": "^3.0.0",
|
||||||
|
"localStorage": "^1.0.4",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"next": "12.1.0",
|
||||||
|
"react": "17.0.2",
|
||||||
|
"react-dom": "17.0.2",
|
||||||
|
"react-flip-move": "^3.0.4",
|
||||||
|
"reconnecting-websocket": "^4.4.0",
|
||||||
|
"tailwind-scrollbar-hide": "^1.0.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^10.2.5",
|
||||||
|
"eslint": "8.11.0",
|
||||||
|
"eslint-config-next": "12.1.0",
|
||||||
|
"postcss": "^8.3.0",
|
||||||
|
"tailwindcss": "3.0.24"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from "react";
|
||||||
|
import Script from "next/script";
|
||||||
|
import "../styles/globals.css";
|
||||||
|
import "antd/dist/antd.css";
|
||||||
|
import { ConfigProvider } from "antd";
|
||||||
|
import zhCN from "antd/lib/locale-provider/zh_CN";
|
||||||
|
import moment from "moment";
|
||||||
|
import "moment/locale/zh-cn";
|
||||||
|
moment.locale("zh-cn");
|
||||||
|
function MyApp({ Component, pageProps }) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider locale={zhCN}>
|
||||||
|
<React.Fragment>
|
||||||
|
<Script src="/config.js" strategy="beforeInteractive"></Script>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
<style jsx global>{`
|
||||||
|
@font-face {
|
||||||
|
font-family: "RubikRegular";
|
||||||
|
src: url("/font/rubik/Rubik-Regular.ttf");
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "PingFang";
|
||||||
|
src: url("/font/PingFang SC Regular.ttf");
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: "PingFang";
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</React.Fragment>
|
||||||
|
</ConfigProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyApp;
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
|
|
||||||
|
export default function handler(req, res) {
|
||||||
|
res.status(200).json({ name: 'John Doe' })
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import Page from "../../../components/client/PackagingOperation"
|
||||||
|
export default Page;
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { RadioGroup } from '@headlessui/react'
|
||||||
|
|
||||||
|
const plans = [
|
||||||
|
{
|
||||||
|
name: 'Startup',
|
||||||
|
ram: '12GB',
|
||||||
|
cpus: '6 CPUs',
|
||||||
|
disk: '160 GB SSD disk',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Business',
|
||||||
|
ram: '16GB',
|
||||||
|
cpus: '8 CPUs',
|
||||||
|
disk: '512 GB SSD disk',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Enterprise',
|
||||||
|
ram: '32GB',
|
||||||
|
cpus: '12 CPUs',
|
||||||
|
disk: '1024 GB SSD disk',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Alert() {
|
||||||
|
const [selected, setSelected] = useState(plans[0])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full px-4 ">
|
||||||
|
<div className="mx-auto w-full max-w-md">
|
||||||
|
<RadioGroup value={selected} onChange={setSelected}>
|
||||||
|
<RadioGroup.Label className="sr-only">Server size</RadioGroup.Label>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{plans.map((plan) => (
|
||||||
|
<RadioGroup.Option
|
||||||
|
key={plan.name}
|
||||||
|
value={plan}
|
||||||
|
className={({ active, checked }) =>
|
||||||
|
`${
|
||||||
|
active
|
||||||
|
? 'ring-2 ring-white ring-opacity-60 ring-offset-2 ring-offset-sky-300'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
${
|
||||||
|
checked ? 'bg-sky-900 bg-opacity-75 text-white' : 'bg-white'
|
||||||
|
}
|
||||||
|
relative flex cursor-pointer rounded-lg px-5 py-4 shadow-md focus:outline-none`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{({ active, checked }) => (
|
||||||
|
<>
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="text-sm">
|
||||||
|
<RadioGroup.Label
|
||||||
|
as="p"
|
||||||
|
className={`font-medium ${
|
||||||
|
checked ? 'text-white' : 'text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{plan.name}
|
||||||
|
</RadioGroup.Label>
|
||||||
|
<RadioGroup.Description
|
||||||
|
as="span"
|
||||||
|
className={`inline ${
|
||||||
|
checked ? 'text-sky-100' : 'text-gray-500'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{plan.ram}/{plan.cpus}
|
||||||
|
</span>{' '}
|
||||||
|
<span aria-hidden="true">·</span>{' '}
|
||||||
|
<span>{plan.disk}</span>
|
||||||
|
</RadioGroup.Description>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{checked && (
|
||||||
|
<div className="shrink-0 text-white">
|
||||||
|
<CheckIcon className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</RadioGroup.Option>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CheckIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" {...props}>
|
||||||
|
<circle cx={12} cy={12} r={12} fill="#fff" opacity="0.2" />
|
||||||
|
<path
|
||||||
|
d="M7 13l3 3 7-7"
|
||||||
|
stroke="#fff"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,367 @@
|
||||||
|
import react from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import requests from "../../../utils/requests";
|
||||||
|
import { doPost } from "../../../utils/requests";
|
||||||
|
import {
|
||||||
|
PlayIcon,
|
||||||
|
StopIcon,
|
||||||
|
PauseIcon,
|
||||||
|
BellIcon,
|
||||||
|
ViewListIcon,
|
||||||
|
} from "@heroicons/react/outline";
|
||||||
|
|
||||||
|
import { BellIcon as BellIconS } from "@heroicons/react/solid";
|
||||||
|
import { PuzzleIcon } from "@heroicons/react/solid";
|
||||||
|
import VLabel from "../Header/VLabel";
|
||||||
|
import eqs from "../../../utils/eqs";
|
||||||
|
import { Tooltip } from 'antd';
|
||||||
|
const { eqstatus, orderstatus } = eqs;
|
||||||
|
|
||||||
|
function Card({
|
||||||
|
eq,
|
||||||
|
popHandle,
|
||||||
|
alartPopHandle,
|
||||||
|
classNamez,
|
||||||
|
typecode,
|
||||||
|
selectedTab,
|
||||||
|
alertData,
|
||||||
|
}) {
|
||||||
|
if (!eq) {
|
||||||
|
eq = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [status, setStatus] = useState(eqstatus[0]);
|
||||||
|
const [alertcount, setAlertcount] = useState(0);
|
||||||
|
const [fieldValueMap, setFieldValueMap] = react.useState({});
|
||||||
|
const [imgUrl, setImgUrl] = react.useState(null);
|
||||||
|
// const [fields, setFields] = react.useState([]);
|
||||||
|
|
||||||
|
react.useEffect(() => {
|
||||||
|
if (eq.runningstatus === undefined) eq.runningstatus = 0;
|
||||||
|
setStatus(eqstatus.filter((item) => item.key === eq.runningstatus)[0]);
|
||||||
|
// console.log(eq.code,eq.runningstatus)
|
||||||
|
}, [eq.runningstatus]);
|
||||||
|
// eq.updatestatus=(statuscur)=>{
|
||||||
|
// setStatus(eqstatus.filter((item) => item.key === statuscur)[0]);
|
||||||
|
// }
|
||||||
|
react.useEffect(() => {
|
||||||
|
//updatefields();
|
||||||
|
// if (eq.operationCode === typecode) {
|
||||||
|
// setFields(eq.bmIotEquipmentRelationshipList);
|
||||||
|
// }
|
||||||
|
async function async1() {
|
||||||
|
if (typecode === "1002") {
|
||||||
|
let item = eq?.bmIotEquipmentRelationshipList.filter(
|
||||||
|
(x) => x.iotField.indexOf("IoStatus") > -1
|
||||||
|
);
|
||||||
|
if (item && item.length > 0) {
|
||||||
|
eq.runningstatus =
|
||||||
|
item[0].iotFieldValue || item[0].iotFieldValue === 1 ? 2 : 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async1();
|
||||||
|
}, [typecode]);
|
||||||
|
|
||||||
|
react.useEffect(() => {
|
||||||
|
if (alertData && alertData.length > 0) {
|
||||||
|
eq.alert = alertData.filter((al) => {
|
||||||
|
if (al.equipCode === eq.code) {
|
||||||
|
return al;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(eq.alert.length>0)
|
||||||
|
{
|
||||||
|
let alertcout = eq?.alert?.filter((aa) => {
|
||||||
|
if (!aa?.eliminationTime) {
|
||||||
|
return aa;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
eq.alertcount = alertcout.length;
|
||||||
|
setAlertcount(alertcout.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [alertData, selectedTab]);
|
||||||
|
// react.useEffect(() => {
|
||||||
|
// if(!eq.alertcount){
|
||||||
|
// eq.alertcount=0;
|
||||||
|
// eq.alert= [];
|
||||||
|
// }
|
||||||
|
// else{
|
||||||
|
// setAlertcount(eq.alertcount);
|
||||||
|
// }
|
||||||
|
// }, [eq.alertcount, selectedTab]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
}, [eq.alert]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
if (eq.Img) {
|
||||||
|
setImgUrl(eq.Img);
|
||||||
|
}
|
||||||
|
}, [eq.Img]);
|
||||||
|
react.useEffect(() => {}, [eq.runningstatus]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
// setTimeout(() => {
|
||||||
|
// setFields(eq.bmIotEquipmentRelationshipList);
|
||||||
|
// }, 100);
|
||||||
|
}, [eq.bmIotEquipmentRelationshipList]);
|
||||||
|
eq.updateFields = () => {
|
||||||
|
// updatefields();
|
||||||
|
};
|
||||||
|
|
||||||
|
let updatefields = () => {
|
||||||
|
if (!eq?.bmIotEquipmentRelationshipList) {
|
||||||
|
if (!eq.code) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doPost(requests.fetchEquipFields.url, [eq.code])
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
// var fileGroupIds = curdata.map((zz) => zz.fileGroupId);
|
||||||
|
//获取图片信息
|
||||||
|
if (data.data.length > 0) {
|
||||||
|
var eqcur = data.data[0][0];
|
||||||
|
eq.bmIotEquipmentRelationshipList =
|
||||||
|
eqcur.bmIotEquipmentRelationshipList;
|
||||||
|
updateonlyfields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateonlyfields();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateonlyfields = () => {
|
||||||
|
//接口请求物联网influxdb查询服务
|
||||||
|
doPost(requests.fetchLastFieldsValue.url, {
|
||||||
|
equipCode: eq.code,
|
||||||
|
fields: eq.bmIotEquipmentRelationshipList
|
||||||
|
.filter((re) => {
|
||||||
|
if (re.status && !re.iotIsalarm) {
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map((field) => field.iotField),
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
if (data.data !== null && data.data !== []) {
|
||||||
|
var _fieldValueMap = data.data[0];
|
||||||
|
if (!_fieldValueMap) return;
|
||||||
|
eq.bmIotEquipmentRelationshipList.map((field) => {
|
||||||
|
field.iotFieldValue = !(field.iotField in _fieldValueMap)
|
||||||
|
? null
|
||||||
|
: _fieldValueMap[field.iotField];
|
||||||
|
if (field.iotIsalarm) {
|
||||||
|
if (!field.iotFieldValue) field.iotFieldValue = 0;
|
||||||
|
if (field.iotField.indexOf("IoStatus") > -1) {
|
||||||
|
field.iotFieldValue =
|
||||||
|
field.iotFieldValue === 1 ? true : false;
|
||||||
|
} else {
|
||||||
|
field.iotFieldValue =
|
||||||
|
field.iotFieldValue === 0 ? false : true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (eq.bmIotEquipmentRelationshipList) {
|
||||||
|
var _alertcount = eq.bmIotEquipmentRelationshipList.filter(
|
||||||
|
(re) => {
|
||||||
|
if (re.iotIsalarm && re.status && re.iotFieldValue) {
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).length;
|
||||||
|
// setAlertcount(_alertcount);
|
||||||
|
setFieldValueMap(_fieldValueMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// console.log(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
console.log(ex);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
react.useEffect(() => {
|
||||||
|
if (eq.code === null) return;
|
||||||
|
//updatefields();
|
||||||
|
}, [eq]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
//updatefields();
|
||||||
|
}, [eq.iotFieldValue]);
|
||||||
|
let togglePop = () => {
|
||||||
|
popHandle(eq);
|
||||||
|
// setState((prevState) => ({
|
||||||
|
// ...prevState,
|
||||||
|
// }));
|
||||||
|
};
|
||||||
|
let alartPop = () => {
|
||||||
|
alartPopHandle(eq);
|
||||||
|
// setState((prevState) => ({
|
||||||
|
// ...prevState,
|
||||||
|
// }));
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className={` bg-white h-fit rounded w-64 `}>
|
||||||
|
{/* 第一行 */}
|
||||||
|
<div className={`group flex flex-raw h-6 rounded-t ${status.bg}`}>
|
||||||
|
<div
|
||||||
|
className={`flex-0 inline-flex items-center text-white justify-center`}
|
||||||
|
>
|
||||||
|
{/* <div className="w-2"></div> {orderstatus[0].title} */}
|
||||||
|
<div className="w-2"></div>
|
||||||
|
<p className="truncate w-28 text-left cardP" alt={eq.code}>
|
||||||
|
{" "}
|
||||||
|
{eq.code}
|
||||||
|
</p>
|
||||||
|
{/* {eq?.typeName} */}
|
||||||
|
</div>
|
||||||
|
<div className="flex-auto"></div>
|
||||||
|
<div className=" flex-0 inline-flex items-center text-white justify-center ">
|
||||||
|
<p className="truncate w-28 text-right cardP" alt={eq.name}>
|
||||||
|
{" "}
|
||||||
|
{eq.name}
|
||||||
|
</p>
|
||||||
|
<div className="w-2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative flex">
|
||||||
|
{!imgUrl ? (
|
||||||
|
<img
|
||||||
|
src={`../img/eqimg.jpeg`}
|
||||||
|
alt={eq.name}
|
||||||
|
className=" w-full h-20 bg-gray-500 "
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
// eslint-disable-next-line
|
||||||
|
<img
|
||||||
|
src={imgUrl}
|
||||||
|
alt={eq.name}
|
||||||
|
className=" w-full h-20 bg-gray-500 "
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
title={eq?.bmIotEquipmentRelationshipList?.length>0?eq?.bmIotEquipmentRelationshipList[0].updateTime:""}
|
||||||
|
mouseEnterDelay={1}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className={` absolute top-1 left-2 inline-block px-1 text-xs ${status.bg}-txt text-white ${status.bg}-border border rounded-sm`}
|
||||||
|
>
|
||||||
|
{status.title}
|
||||||
|
{status.key > 0 ? "中" : ""}
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
{/* 第二行 */}
|
||||||
|
<div className="border-l border-r text-left pl-4 py-2 border-gray-200 flex flex-col h-48">
|
||||||
|
{!eq?.bmIotEquipmentRelationshipList ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
|
eq?.bmIotEquipmentRelationshipList
|
||||||
|
.filter((re) => {
|
||||||
|
if (re.iotIsMainField && re.status) {
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) =>
|
||||||
|
parseInt(a.fieldSort) > parseInt(b.fieldSort) ? 1 : -1
|
||||||
|
)
|
||||||
|
.map((re, idx) => {
|
||||||
|
return (
|
||||||
|
<div className={` flex flex-row mb-1 mr-3 `} key={idx}>
|
||||||
|
<VLabel>{re.iotFieldDescribe}:</VLabel>{" "}
|
||||||
|
<div className="flex-auto"></div>
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
title={re.iotFieldValueTime}
|
||||||
|
mouseEnterDelay={1}
|
||||||
|
>
|
||||||
|
{!fieldValueMap ? (
|
||||||
|
""
|
||||||
|
) : (
|
||||||
|
<label
|
||||||
|
className={` text-right ${classNamez} text-[#297FC8]`}
|
||||||
|
>
|
||||||
|
{typeof re.iotFieldValue === "boolean"
|
||||||
|
? String(re.iotFieldValue)
|
||||||
|
: re.iotFieldValue}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 第三行 */}
|
||||||
|
<div className=" border group flex items-center flex-raw h-12 rounded-b border-gray-200 px-1">
|
||||||
|
<div className=" flex flex-row flex-auto w-12 justify-center cursor-pointer">
|
||||||
|
{status.key === 2 ? (
|
||||||
|
<PlayIcon className={` flex-0 h-5 w-5 mr-1 ${status.text}`} />
|
||||||
|
) : status.key === 1 ? (
|
||||||
|
<PauseIcon className={` flex-0 h-5 w-5 mr-1 ${status.text}`} />
|
||||||
|
) : status.key === 3 ? (
|
||||||
|
<StopIcon className={` flex-0 h-5 w-5 mr-1 ${status.text}`} />
|
||||||
|
) : (
|
||||||
|
<BellIcon className={` flex-0 h-5 w-5 mr-1 ${status.text}`} />
|
||||||
|
)}
|
||||||
|
<div className=" flex-0 inline-flex items-center ml-1 text-black justify-center ">
|
||||||
|
{status.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="border-l border-gray-200 h-full flex-0"></div>
|
||||||
|
<div
|
||||||
|
onClick={alartPop}
|
||||||
|
className=" flex items-center justify-center flex-auto w-12 border-gray-200 cursor-pointer"
|
||||||
|
>
|
||||||
|
{/* {alertcount > 0 ? (
|
||||||
|
<span className=" inline-flex rounded-full text-red-500 items-center justify-center ">
|
||||||
|
{alertcount}
|
||||||
|
</span>
|
||||||
|
) : */}
|
||||||
|
|
||||||
|
<div className="relative inline-flex">
|
||||||
|
{alertcount > 0 ? (
|
||||||
|
<BellIconS
|
||||||
|
className={`flex-0 h-5 w-5 mr-1 text-red-500 `}
|
||||||
|
></BellIconS>
|
||||||
|
) : (
|
||||||
|
<BellIcon className={`flex-0 h-5 w-5 mr-1 `}></BellIcon>
|
||||||
|
)}
|
||||||
|
{alertcount > 0 ? (
|
||||||
|
<span className="text-red-500">{alertcount}</span>
|
||||||
|
) : (
|
||||||
|
``
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`flex-0 inline-flex items-center ml-1 ${
|
||||||
|
alertcount > 0 ? "text-red-500" : "text-black"
|
||||||
|
} justify-center`}
|
||||||
|
>
|
||||||
|
告警
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="border-l border-gray-200 h-full flex-0"></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
onClick={togglePop}
|
||||||
|
className=" flex flex-row items-center justify-center flex-auto w-12 border-gray-200 cursor-pointer"
|
||||||
|
>
|
||||||
|
{ViewListIcon && <ViewListIcon className=" flex-0 h-5 w-5 mr-1 " />}
|
||||||
|
<div className=" flex-0 inline-flex items-center ml-1 text-black justify-center ">
|
||||||
|
其他
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default Card;
|
|
@ -0,0 +1,84 @@
|
||||||
|
import react from "react";
|
||||||
|
|
||||||
|
import { Fragment, useState } from "react";
|
||||||
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
|
import { CheckIcon, SelectorIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
export default function AutoCompleteComboBox({
|
||||||
|
dataSource,
|
||||||
|
selectedone,
|
||||||
|
selectChanged,
|
||||||
|
}) {
|
||||||
|
if (!dataSource) {
|
||||||
|
dataSource = [{ name: "无" }];
|
||||||
|
}
|
||||||
|
react.useEffect(() => {
|
||||||
|
if (JSON.stringify(dataSource[0]) === JSON.stringify(selected)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedone && dataSource.length > 0) {
|
||||||
|
setSelected(dataSource.filter((x) => x.code === selectedone)[0]);
|
||||||
|
} else {
|
||||||
|
setSelected(dataSource[0]);
|
||||||
|
}
|
||||||
|
}, [dataSource, selectedone]);
|
||||||
|
|
||||||
|
const [selected, setSelected] = useState(dataSource[0]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
selectChanged(selected);
|
||||||
|
}, [selected]);
|
||||||
|
return (
|
||||||
|
<div className="w-60">
|
||||||
|
<Listbox value={selected} onChange={setSelected}>
|
||||||
|
<div className="relative mt-1">
|
||||||
|
<Listbox.Button className="relative w-full py-1 pl-3 pr-10 text-left bg-white rounded-lg border cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-opacity-75 focus-visible:ring-white focus-visible:ring-offset-orange-300 focus-visible:ring-offset-2 sm:text-sm">
|
||||||
|
<span className="block truncate">{selected?.name}</span>
|
||||||
|
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||||
|
<SelectorIcon
|
||||||
|
className="w-5 h-5 text-gray-400"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Listbox.Button>
|
||||||
|
<Transition
|
||||||
|
as={Fragment}
|
||||||
|
leave="transition ease-in duration-100"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<Listbox.Options className="absolute w-full py-1 mt-1 overflow-auto text-base bg-white rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||||
|
{dataSource.map((item, itemIdx) => (
|
||||||
|
<Listbox.Option
|
||||||
|
key={itemIdx}
|
||||||
|
className={({ active }) =>
|
||||||
|
`cursor-default select-none relative py-0.5 my-0.5 pl-10 pr-4 ${
|
||||||
|
active ? "text-amber-900 bg-amber-100" : "text-gray-900"
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
value={item}
|
||||||
|
>
|
||||||
|
{({ selected }) => (
|
||||||
|
<>
|
||||||
|
<span
|
||||||
|
className={`block truncate ${
|
||||||
|
selected ? "font-medium" : "font-normal"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
|
{selected ? (
|
||||||
|
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
|
||||||
|
<CheckIcon className="w-5 h-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Listbox.Option>
|
||||||
|
))}
|
||||||
|
</Listbox.Options>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/** @format */
|
||||||
|
import react from "react";
|
||||||
|
import LabelCheck from "./LabelCheck";
|
||||||
|
import VLabel from "./VLabel";
|
||||||
|
import { PuzzleIcon, PauseIcon, RefreshIcon } from "@heroicons/react/solid";
|
||||||
|
import eqs from "../../../utils/eqs";
|
||||||
|
import AutoCompleteCombobox from "../Combobox";
|
||||||
|
import requests from "../../../utils/requests";
|
||||||
|
import { doPost } from "../../../utils/requests";
|
||||||
|
const { eqstatus, orderstatus } = eqs;
|
||||||
|
|
||||||
|
function Header({
|
||||||
|
factorychanged,
|
||||||
|
setTypeList,
|
||||||
|
statuslist,
|
||||||
|
typeList,
|
||||||
|
typechanged,
|
||||||
|
zzref,
|
||||||
|
getEqData,
|
||||||
|
runningstatuschanged,
|
||||||
|
selectedone
|
||||||
|
}) {
|
||||||
|
const [factoryData, setfactoryData] = react.useState(null);
|
||||||
|
const [ischeckAll, setIscheckAll] = react.useState(true);
|
||||||
|
let getCount = (key) => {
|
||||||
|
if (statuslist) {
|
||||||
|
var aa = 0;
|
||||||
|
Object.keys(statuslist).forEach((x) => {
|
||||||
|
if (key === parseInt(x)) {
|
||||||
|
aa = statuslist[x];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return aa;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var selectChanged = (item) => {
|
||||||
|
factorychanged(item);
|
||||||
|
};
|
||||||
|
react.useEffect(() => {}, [typeList]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
doPost(requests.fetchFactory.url, {
|
||||||
|
removePermission: true,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 100000,
|
||||||
|
sorted: "serialNumber"
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
setfactoryData(data.data);
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
console.log(ex);
|
||||||
|
});
|
||||||
|
|
||||||
|
doPost(requests.fetchType.url, {
|
||||||
|
removePermission: true,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 100000,
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
if (data.data) {
|
||||||
|
var typecodedata = data.data
|
||||||
|
.sort((a, b) => (parseInt(a.code) > parseInt(b.code) ? 1 : -1))
|
||||||
|
.map((a) => {
|
||||||
|
return {
|
||||||
|
code: a.code,
|
||||||
|
name: a.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setTypeList(typecodedata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<header
|
||||||
|
className="flex flex-col h-auto space-y-2 sticky top-0 z-20 "
|
||||||
|
ref={zzref}
|
||||||
|
>
|
||||||
|
{/* <div className="flex plblue h-12 px-3">
|
||||||
|
<PuzzleIcon className="flex-none w-11 h-11"></PuzzleIcon>
|
||||||
|
<div className=" border-l border-gray-50 my-3 mx-3 "></div>
|
||||||
|
<label className=" text-xl text-white flex items-center ">
|
||||||
|
融通高科设备状态大屏
|
||||||
|
</label>
|
||||||
|
</div> */}
|
||||||
|
<div className="px-3 bg-white h-14 flex items-center ">
|
||||||
|
<div className="flex items-center ">
|
||||||
|
<VLabel>车间</VLabel>
|
||||||
|
<AutoCompleteCombobox
|
||||||
|
dataSource={factoryData}
|
||||||
|
selectChanged={selectChanged}
|
||||||
|
selectedone={selectedone}
|
||||||
|
/>
|
||||||
|
<div className=" w-5"></div>
|
||||||
|
|
||||||
|
<VLabel>分类</VLabel>
|
||||||
|
<AutoCompleteCombobox
|
||||||
|
dataSource={typeList}
|
||||||
|
selectChanged={typechanged}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-auto"></div>
|
||||||
|
<div className="flex flex-row ">
|
||||||
|
<div className="flex flex-row items-center mr-3">
|
||||||
|
<span> 刷新设备数据: </span>{" "}
|
||||||
|
<button type="button" onClick={getEqData}>
|
||||||
|
<RefreshIcon className="flex-none w-5 h-5 text-gray-300 "></RefreshIcon>{" "}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="space-x-2 items-center flex flex-row ">
|
||||||
|
<VLabel>设备运行状态</VLabel>
|
||||||
|
{eqstatus
|
||||||
|
.sort((a, b) => (a.idx > b.idx ? 1 : -1))
|
||||||
|
.filter((z) => {
|
||||||
|
if (z.key >= -10) return z;
|
||||||
|
})
|
||||||
|
.map((item, idx) => (
|
||||||
|
<LabelCheck
|
||||||
|
oncheckchange={(val) => {
|
||||||
|
if(item.key===-10){
|
||||||
|
setIscheckAll(val.target.checked)
|
||||||
|
}
|
||||||
|
runningstatuschanged(item.key, val.target.checked);
|
||||||
|
}}
|
||||||
|
key={idx}
|
||||||
|
keyv={item.key}
|
||||||
|
PrefixTitle={item.title}
|
||||||
|
SuffixTitle={item.unit}
|
||||||
|
checked= {ischeckAll}
|
||||||
|
Count={item.key == -10 ? "" : getCount(item.key)}
|
||||||
|
Bg={item.bg}
|
||||||
|
></LabelCheck>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* <div className=" border-l border-gray-300 mx-6"></div>
|
||||||
|
|
||||||
|
<div className="space-x-2 items-center flex flex-row ">
|
||||||
|
<VLabel>设备生产状态</VLabel>
|
||||||
|
{orderstatus.map((item, idx) => (
|
||||||
|
<LabelCheck
|
||||||
|
key={idx}
|
||||||
|
PrefixTitle={item.title}
|
||||||
|
SuffixTitle={item.unit}
|
||||||
|
Count={
|
||||||
|
item.key === "1" ? productCount : allEquipCount-productCount
|
||||||
|
}
|
||||||
|
Bg={item.bg}
|
||||||
|
></LabelCheck>
|
||||||
|
))}
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className=" mt-4"></div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header;
|
|
@ -0,0 +1,55 @@
|
||||||
|
import StateCheck from "./StateCheck";
|
||||||
|
import react from "react";
|
||||||
|
|
||||||
|
function LabelCheck({
|
||||||
|
PrefixTitle,
|
||||||
|
SuffixTitle,
|
||||||
|
Count,
|
||||||
|
Icon,
|
||||||
|
Bg,
|
||||||
|
oncheckchange,
|
||||||
|
checked,
|
||||||
|
keyv,
|
||||||
|
}) {
|
||||||
|
let refinput = react.createRef();
|
||||||
|
const [firstload, setFirstLoad] = react.useState(true);
|
||||||
|
|
||||||
|
|
||||||
|
react.useEffect(() => {
|
||||||
|
if ( keyv !== -10 && !firstload) {
|
||||||
|
if(refinput.current?.checked !==checked){
|
||||||
|
refinput.current?.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFirstLoad(false)
|
||||||
|
|
||||||
|
}, [checked]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className=" grow items-center inline-flex">
|
||||||
|
<input
|
||||||
|
id={`shutdown-7`+ keyv}
|
||||||
|
type="checkbox"
|
||||||
|
ref={refinput}
|
||||||
|
onChange={oncheckchange}
|
||||||
|
// checked={checked}
|
||||||
|
defaultChecked={true}
|
||||||
|
className={`rounded ring-1 py-1 h-4 w-4 mr-2 border-0 focus:ring-1 text-${Bg} ${Bg} cursor-pointer`}
|
||||||
|
></input>
|
||||||
|
<label htmlFor="shutdown-7" className=" block text-base text-gray-900 cursor-pointer ">
|
||||||
|
<StateCheck
|
||||||
|
Title={
|
||||||
|
<label className="cursor-pointer">
|
||||||
|
{PrefixTitle}{" "}
|
||||||
|
<span className={`font-bold text-base text-${Bg}`}>{Count}</span>{" "}
|
||||||
|
{SuffixTitle}
|
||||||
|
</label>
|
||||||
|
}
|
||||||
|
Icon={Icon}
|
||||||
|
></StateCheck>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LabelCheck;
|
|
@ -0,0 +1,10 @@
|
||||||
|
function StateCheck ({ Icon, Title }) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="group flex flex-raw cursor-pointer ">
|
||||||
|
{Icon && <Icon className="h-4 mt-1 mr-1 group-hover:animate-bounce" />}
|
||||||
|
{Title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default StateCheck
|
|
@ -0,0 +1,8 @@
|
||||||
|
function VLabel(props) {
|
||||||
|
return (
|
||||||
|
<div className="inline-block align-middle text-xs text-gray-900 mr-2 ">
|
||||||
|
<label> {props.children}</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default VLabel;
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* This example requires Tailwind CSS v2.0+ */
|
||||||
|
import react from "react";
|
||||||
|
import { Fragment, useRef, useState } from "react";
|
||||||
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
import { PuzzleIcon } from "@heroicons/react/outline";
|
||||||
|
import { XIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
export default function Modal({ title, img, closePopHandler, children }) {
|
||||||
|
const [open, setOpen] = useState(true);
|
||||||
|
const [imgUrl, setImgUrl] = react.useState(img);
|
||||||
|
|
||||||
|
const cancelButtonRef = useRef(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition.Root show={open} as={Fragment}>
|
||||||
|
<Dialog
|
||||||
|
as="div"
|
||||||
|
className="fixed z-30 inset-0 overflow-y-auto"
|
||||||
|
initialFocus={cancelButtonRef}
|
||||||
|
onClose={setOpen}
|
||||||
|
>
|
||||||
|
<div className="flex items-end justify-center min-h-screen px-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
{/* This element is to trick the browser into centering the modal contents. */}
|
||||||
|
<span
|
||||||
|
className="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
​
|
||||||
|
</span>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
>
|
||||||
|
<div className="relative inline-block align-bottom bg-white rounded text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
|
||||||
|
<div className="bg-white ">
|
||||||
|
<div className="sm:flex sm:items-start">
|
||||||
|
{/* <div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-gray-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||||
|
{!imgUrl ? <PuzzleIcon
|
||||||
|
className="h-6 w-6 text-black-600"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
: <img
|
||||||
|
src={`${imgUrl}`}
|
||||||
|
alt={title}
|
||||||
|
className="mr-3 w-10 h-10 rounded-full bg-slate-50 dark:bg-slate-800 "
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div> */}
|
||||||
|
<div className="w-full text-center sm:mt-0 sm:text-left ">
|
||||||
|
<Dialog.Title
|
||||||
|
as="h4"
|
||||||
|
className=" text-base leading-6 px-3 pt-2 font-medium text-gray-900 relative"
|
||||||
|
>
|
||||||
|
<XIcon
|
||||||
|
className="h-4 w-4 text-black-600 right-3 top-3 absolute cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false);
|
||||||
|
closePopHandler();
|
||||||
|
}}
|
||||||
|
ref={cancelButtonRef}
|
||||||
|
aria-hidden="true"
|
||||||
|
></XIcon>
|
||||||
|
{title}
|
||||||
|
</Dialog.Title>
|
||||||
|
<div className="hidden sm:block" aria-hidden="true">
|
||||||
|
<div className="pt-2">
|
||||||
|
<div className="border-t border-gray-200" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-2 w-full px-3 h-96 overflow-auto ">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden sm:block" aria-hidden="true">
|
||||||
|
<div className="">
|
||||||
|
<div className="border-t border-gray-200" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-50 px-4 py-1.5 sm:px-6 sm:flex sm:flex-row-reverse ">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className=" w-full inline-flex justify-center border border-gray-300 shadow-sm px-4 py-1 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false);
|
||||||
|
closePopHandler();
|
||||||
|
}}
|
||||||
|
ref={cancelButtonRef}
|
||||||
|
>
|
||||||
|
关闭
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition.Root>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
{code: "1", val: "待产中", bgColor: "#F1AB0A", borderColor: "#F1AB0A"}, //待产中
|
||||||
|
{code: "2", val: "生产中", bgColor: "#26A764", borderColor: "#26A764"}, //生产中
|
||||||
|
{code: "3", val: "停产中", bgColor: "#F05B4F", borderColor: "#F05B4F"}, //停产中
|
||||||
|
]
|
||||||
|
|
||||||
|
function ProcessCard(props) {
|
||||||
|
const {itemData = {}} = props;
|
||||||
|
let {
|
||||||
|
code = "L7-TL",
|
||||||
|
name = "-",
|
||||||
|
productBatchNo = "-",
|
||||||
|
productLevel = "",
|
||||||
|
productName = "",
|
||||||
|
runningstatus = "-"
|
||||||
|
} = itemData;
|
||||||
|
let {val, bgColor, borderColor} = colors.find(item => item.code == runningstatus) || {};
|
||||||
|
let prodInfo = productLevel ? (productName + "-" + productLevel) : (productName ? productName : "-")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='card'>
|
||||||
|
<div className='card-top'>
|
||||||
|
<div className='card-top-img'/>
|
||||||
|
<div className='card-top-text'>{name}</div>
|
||||||
|
</div>
|
||||||
|
<div className='card-bottom'>
|
||||||
|
<div className="card-bottom-info">当前生产信息</div>
|
||||||
|
<div className='card-bottom-item'>
|
||||||
|
<span>产品信息</span>
|
||||||
|
<span className='card-bottom-item-val'>{prodInfo}</span>
|
||||||
|
</div>
|
||||||
|
<div className='card-bottom-item'>
|
||||||
|
<span>批次信息</span>
|
||||||
|
<span className='card-bottom-item-val'>{productBatchNo}</span>
|
||||||
|
</div>
|
||||||
|
<div className='card-bottom-item'>
|
||||||
|
<span>生产状态</span>
|
||||||
|
<span className='card-bottom-item-val process-status'>{val}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
.card {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top .card-top-img {
|
||||||
|
border: 1px solid #0497FD;
|
||||||
|
height: 190px;
|
||||||
|
background: url(/img/process/${code}.png) no-repeat center / cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top {
|
||||||
|
border: 1px solid #0497FD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top .card-top-text {
|
||||||
|
height: 30px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #28A3FF;
|
||||||
|
background-color: #0E2C55;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom {
|
||||||
|
margin-top: 4px;
|
||||||
|
background-color: #062D8D;
|
||||||
|
padding: 8px 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom .card-bottom-info {
|
||||||
|
color: #F1AB0A;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom .card-bottom-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom-item .card-bottom-item-val {
|
||||||
|
color: #28EAFF;
|
||||||
|
max-width: calc(100% - 60px);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom-item .process-status {
|
||||||
|
color: #eee;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid ${borderColor};
|
||||||
|
background-color: ${bgColor};
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProcessCard;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Switch } from '@headlessui/react'
|
||||||
|
|
||||||
|
export default function Example() {
|
||||||
|
const [enabled, setEnabled] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="py-16">
|
||||||
|
<Switch
|
||||||
|
checked={enabled}
|
||||||
|
onChange={setEnabled}
|
||||||
|
className={`${enabled ? 'bg-teal-900' : 'bg-teal-700'}
|
||||||
|
focus:outline-none relative inline-flex h-[38px] w-[74px] flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Use setting</span>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className={`${enabled ? 'translate-x-9' : 'translate-x-0'}
|
||||||
|
pointer-events-none inline-block h-[34px] w-[34px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out`}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Tab } from "@headlessui/react";
|
||||||
|
|
||||||
|
function classNames(...classes) {
|
||||||
|
return classes.filter(Boolean).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function VTab(props) {
|
||||||
|
let [categories, setCategories] = useState([]);
|
||||||
|
let [children, setChildren] = useState([]);
|
||||||
|
let [selectedOne, setSelectedOne] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.ops.length > 0) {
|
||||||
|
setCategories(props.ops);
|
||||||
|
setSelectedOne(0)
|
||||||
|
}
|
||||||
|
}, [props.ops]);
|
||||||
|
const getSelectedIndex = () => {
|
||||||
|
return selectedOne;
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
setChildren(props.children);
|
||||||
|
}, [props.children]);
|
||||||
|
useEffect(() => {
|
||||||
|
// if(props.selectedtypecode!=="1001")
|
||||||
|
// {
|
||||||
|
// setCategories(props.ops.filter(x=>x.code==="-2"));
|
||||||
|
// }
|
||||||
|
// else{
|
||||||
|
// setCategories(props.ops);
|
||||||
|
// }
|
||||||
|
setSelectedOne(0)
|
||||||
|
}, [props.selectedtypecode]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames("w-full sm:px-0", props.className)}>
|
||||||
|
<Tab.Group
|
||||||
|
onChange={(idx) => {
|
||||||
|
props.tabchanged(categories[idx]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tab.List className="flex space-x-5 z-10 " >
|
||||||
|
{categories
|
||||||
|
.sort((a, b) => (a.sort > b.sort ? 1 : -1))
|
||||||
|
.map((category,idx) => (
|
||||||
|
<Tab
|
||||||
|
key={idx}
|
||||||
|
className={({ selected }) =>
|
||||||
|
classNames(
|
||||||
|
" flex-none mx-3 p-4 py-2.5 text-sm font-medium leading-5 text-[#0185FF]",
|
||||||
|
" focus:outline-none focus:ring-none",
|
||||||
|
selected
|
||||||
|
? "bg-white border-b-2 border-[#0185FF]"
|
||||||
|
: " hover:text-[#0185FF]/[0.6]"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/* {category.sort} */}
|
||||||
|
{category.name}
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
|
</Tab.List>
|
||||||
|
<Tab.Panels className="-mt-0.5 h-full overflow-y-auto border-t-2 ">
|
||||||
|
{categories
|
||||||
|
.sort((a, b) => (a.sort > b.sort ? 1 : -1))
|
||||||
|
.map((x) => (
|
||||||
|
<Tab.Panel className={classNames("bg-white p-3")}>
|
||||||
|
{children}
|
||||||
|
</Tab.Panel>
|
||||||
|
))}
|
||||||
|
</Tab.Panels>
|
||||||
|
</Tab.Group>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
{code: "1", val: "待产中", bgColor: "#F1AB0A", borderColor: "#F1AB0A"}, //待产中
|
||||||
|
{code: "2", val: "生产中", bgColor: "#26A764", borderColor: "#26A764"}, //生产中
|
||||||
|
{code: "3", val: "停产中", bgColor: "#F05B4F", borderColor: "#F05B4F"}, //停产中
|
||||||
|
]
|
||||||
|
|
||||||
|
function ProcessTaskCard(props) {
|
||||||
|
const {
|
||||||
|
itemData = {},
|
||||||
|
operationCode = ""
|
||||||
|
} = props;
|
||||||
|
let {
|
||||||
|
code = "-",
|
||||||
|
productBatchNo = "-", //批次
|
||||||
|
productName = "-",
|
||||||
|
onlineTime = 0,
|
||||||
|
runningstatus = "", //运行状态 1:待生产,2:生产中,3:停产中
|
||||||
|
equipParams = {},
|
||||||
|
routeParams = {},
|
||||||
|
} = itemData;
|
||||||
|
runningstatus = runningstatus ? runningstatus.split('.')[0] : ""
|
||||||
|
let {val, bgColor = "", borderColor} = colors.find(item => item.code == runningstatus) || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='card'>
|
||||||
|
<div className='card-top'>
|
||||||
|
<div className='card-top-title'>{code}</div>
|
||||||
|
<div className='card-top-img'>
|
||||||
|
<div className='card-top-status-mask'></div>
|
||||||
|
<div className='card-top-status'>{val}</div>
|
||||||
|
</div>
|
||||||
|
<div className='card-top-duration'>
|
||||||
|
<span>设备运行时长:</span><span>{Number(onlineTime).toFixed(2)}h</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='card-bottom'>
|
||||||
|
<div className="card-bottom-info">生产信息</div>
|
||||||
|
<div className='card-bottom-item'>
|
||||||
|
<span className='item-text'>产品名称</span>
|
||||||
|
<span className='item-val'>{productName}</span>
|
||||||
|
</div>
|
||||||
|
<div className='card-bottom-item'>
|
||||||
|
<span className='item-text'>产品批次</span>
|
||||||
|
<span className='item-val'>{productBatchNo}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-bottom-info">设备参数</div>
|
||||||
|
{equipParams && Object.keys(equipParams).map(item => {
|
||||||
|
return <div className='card-bottom-item'>
|
||||||
|
<span className='item-text'>{item}</span>
|
||||||
|
<span className='item-val'>{equipParams[item] || "-"}</span>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
|
||||||
|
<div className="card-bottom-info">工艺参数</div>
|
||||||
|
{routeParams && Object.keys(routeParams).map(item => {
|
||||||
|
return <div className='card-bottom-item'>
|
||||||
|
<span className='item-text'>{item}</span>
|
||||||
|
<span className='item-val'>{routeParams[item] || "-"}</span>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
.card {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top {
|
||||||
|
border: 1px solid #0497FD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top-title {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #3D7FFF;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top-img {
|
||||||
|
position: relative;
|
||||||
|
height: 65px;
|
||||||
|
background: url(/img/process/${operationCode}.png) no-repeat center / cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top-img .card-top-status-mask {
|
||||||
|
height: 100%;
|
||||||
|
width: 50%;
|
||||||
|
background-color: ${bgColor}80;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top-img .card-top-status {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
top: 4px;
|
||||||
|
left: 4px;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid ${borderColor};
|
||||||
|
background-color: ${bgColor}cc;
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top-duration {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #0E2C55;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom {
|
||||||
|
height: 297px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #102242;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding: 8px 14px 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom .card-bottom-info {
|
||||||
|
color: #F1AB0A;
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom .card-bottom-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
line-height: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom-item .item-text {
|
||||||
|
color: #ffffff;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bottom-item .item-val {
|
||||||
|
color: #28A3FF;
|
||||||
|
margin-left: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProcessTaskCard;
|
|
@ -0,0 +1,984 @@
|
||||||
|
/** @format */
|
||||||
|
import { Tooltip } from "antd";
|
||||||
|
import Head from "next/head";
|
||||||
|
import react from "react";
|
||||||
|
import Header from "./components/Header/Header";
|
||||||
|
import VTab from "./components/Tab";
|
||||||
|
// import Results from "./components/Results/Results";
|
||||||
|
import localStorage from "localStorage";
|
||||||
|
import Rcwebsocket from "reconnecting-websocket";
|
||||||
|
import eqs from "../utils/eqs";
|
||||||
|
import requests, { doPost, getWsUrl, imgUrl } from "../utils/requests";
|
||||||
|
import Card from "./components/Card";
|
||||||
|
import Modal from "./components/Modal";
|
||||||
|
|
||||||
|
const { eqinfos, eqdetail, eqstatus } = eqs;
|
||||||
|
var intX;
|
||||||
|
var isImgLoad = false;
|
||||||
|
var isAlertLoad = false;
|
||||||
|
var isEqDataUpdate = false;
|
||||||
|
var _selectedtypecode, _selectedTab;
|
||||||
|
var ws, connected, wseq, connectedeq;
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
|
||||||
|
export default function Home({ results }) {
|
||||||
|
const [isLoading, setLoading] = react.useState(false);
|
||||||
|
const [selectedTab, setSelectedTab] = react.useState("-2");
|
||||||
|
const [isPop, setPop] = react.useState(false);
|
||||||
|
const [isPopAlert, setIsPopAlert] = react.useState(false);
|
||||||
|
const [popData, setPopData] = react.useState({});
|
||||||
|
const [workCenterCode, setWorkCenterCode] = react.useState();
|
||||||
|
const [queryEqList, setQueryEqList] = react.useState(null);
|
||||||
|
const [eqlist, setEqlist] = react.useState([]);
|
||||||
|
const [operations, setOperations] = react.useState([]);
|
||||||
|
const [curstation, setCurstation] = react.useState(null);
|
||||||
|
const [runningStatus, setRunningStatus] = react.useState({
|
||||||
|
0: true,
|
||||||
|
1: true,
|
||||||
|
2: true,
|
||||||
|
3: true,
|
||||||
|
4: true,
|
||||||
|
});
|
||||||
|
const [statuslist, setStatuslist] = react.useState({});
|
||||||
|
// const [watingCount, setWaitingCount] = react.useState([]);
|
||||||
|
const [typeList, setTypeList] = react.useState(null);
|
||||||
|
// const [workingCount, setWorkingCount] = react.useState([]);
|
||||||
|
const [stopCount, setStopCount] = react.useState([]);
|
||||||
|
const [alertData, setAlertData] = react.useState([]);
|
||||||
|
// const [productCount, setProductCount] = react.useState(0);
|
||||||
|
const [selectedtypecode, setselectedtypecode] = react.useState("");
|
||||||
|
const [allEquipCount, setAllEquipCount] = react.useState([]);
|
||||||
|
// if (isLoading) return <p>Loading...</p>;
|
||||||
|
var handleClose = () => {
|
||||||
|
setPop(false);
|
||||||
|
};
|
||||||
|
var popHandle = (eq) => {
|
||||||
|
setIsPopAlert(false);
|
||||||
|
setPop(!isPop);
|
||||||
|
setPopData(eq);
|
||||||
|
};
|
||||||
|
|
||||||
|
var alartPopHandle = (eq) => {
|
||||||
|
setIsPopAlert(true);
|
||||||
|
setPop(!isPop);
|
||||||
|
setPopData(eq);
|
||||||
|
};
|
||||||
|
const listHeight = react.useRef(null);
|
||||||
|
const screenref = react.useRef(null);
|
||||||
|
const headerref = react.useRef(null);
|
||||||
|
let isresize = false;
|
||||||
|
let counter = 0;
|
||||||
|
react.useEffect(() => {
|
||||||
|
getWsUrl().then((url) => {
|
||||||
|
ws?.close();
|
||||||
|
wseq?.close();
|
||||||
|
|
||||||
|
//判断只有在是edge ws才连接,否则会出现异常连接不停重连
|
||||||
|
if (url.indexOf("application-ws") > -1) {
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
ws = new Rcwebsocket(url);
|
||||||
|
wseq = new Rcwebsocket(url);
|
||||||
|
const onOpen = function () {
|
||||||
|
console.log("OPENED: " + url);
|
||||||
|
connected = true;
|
||||||
|
ws.send("alert");
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = function () {
|
||||||
|
console.log("CLOSED: " + url);
|
||||||
|
ws = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessage = function (event) {
|
||||||
|
var data = event.data;
|
||||||
|
try {
|
||||||
|
var jsonobj = JSON.parse(data);
|
||||||
|
if (jsonobj.length > 0) {
|
||||||
|
setAlertData(jsonobj);
|
||||||
|
}
|
||||||
|
} catch (error) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
const onError = function (event) {
|
||||||
|
//alert(event.data);
|
||||||
|
};
|
||||||
|
ws.onopen = onOpen;
|
||||||
|
ws.onclose = onClose;
|
||||||
|
ws.onmessage = onMessage;
|
||||||
|
ws.onerror = onError;
|
||||||
|
|
||||||
|
const onOpeneq = function () {
|
||||||
|
console.log("eq OPENED: " + url);
|
||||||
|
connectedeq = true;
|
||||||
|
wseq.send("equip");
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCloseeq = function () {
|
||||||
|
console.log("eq CLOSED: " + url);
|
||||||
|
wseq = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessageeq = function (event) {
|
||||||
|
var data = event.data;
|
||||||
|
try {
|
||||||
|
var jsonobj = JSON.parse(data);
|
||||||
|
//获取数据
|
||||||
|
// setLoading(true);
|
||||||
|
bindData(jsonobj, queryEqList);
|
||||||
|
// setLoading(false);
|
||||||
|
} catch (error) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
const onErroreq = function (event) {
|
||||||
|
//alert(event.data);
|
||||||
|
console.log("event:连接websocket发生异常");
|
||||||
|
};
|
||||||
|
wseq.onopen = onOpeneq;
|
||||||
|
wseq.onclose = onCloseeq;
|
||||||
|
wseq.onmessage = onMessageeq;
|
||||||
|
wseq.onerror = onErroreq;
|
||||||
|
|
||||||
|
//设置启动时选择工作中心
|
||||||
|
let curstation = localStorage.getItem("workCenterCode");
|
||||||
|
setCurstation(curstation);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("连接websocket发生异常");
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [queryEqList]);
|
||||||
|
|
||||||
|
react.useEffect(() => {
|
||||||
|
function handleResize() {
|
||||||
|
isresize = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
isresize = false;
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
// react.useEffect(() => {
|
||||||
|
// queryStauts(queryEqList);
|
||||||
|
// }, [alertData, isImgLoad]);
|
||||||
|
react.useEffect(() => {
|
||||||
|
queryStauts(queryEqList);
|
||||||
|
}, [alertData]);
|
||||||
|
// react.useEffect(() => {
|
||||||
|
// if (queryEqList === null || queryEqList === undefined) {
|
||||||
|
// if (intX) {
|
||||||
|
// clearInterval(intX);
|
||||||
|
// intX = null;
|
||||||
|
// }
|
||||||
|
// } else if (queryEqList.length > 0) {
|
||||||
|
// if (intX) {
|
||||||
|
// clearInterval(intX);
|
||||||
|
// intX = null;
|
||||||
|
// }
|
||||||
|
// setTimeout(() => {
|
||||||
|
// intX = setInterval(() => {
|
||||||
|
// // queryFields(queryEqList);
|
||||||
|
// // queryStauts(queryEqList);
|
||||||
|
// }, interval);
|
||||||
|
// }, 2000);
|
||||||
|
// }
|
||||||
|
// }, [queryEqList]);
|
||||||
|
const getAlertData = react.useMemo(() => {
|
||||||
|
return alertData;
|
||||||
|
}, [alertData]);
|
||||||
|
|
||||||
|
const getqueryEqList = react.useMemo(() => {
|
||||||
|
return queryEqList;
|
||||||
|
}, [queryEqList]);
|
||||||
|
|
||||||
|
var factorychanged = (item) => {
|
||||||
|
setQueryEqList([]);
|
||||||
|
setAlertData([]);
|
||||||
|
setStatuslist({});
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isImgLoad = false;
|
||||||
|
setLoading(true);
|
||||||
|
if (item.code === undefined) return;
|
||||||
|
console.log("执行获取所有设备" + item.code);
|
||||||
|
setWorkCenterCode(item.code);
|
||||||
|
localStorage.setItem("workCenterCode", item.code);
|
||||||
|
let eqdata = undefined;//localStorage.getItem(item.code);
|
||||||
|
if (eqdata && JSON.parse(eqdata).length !== 0) {
|
||||||
|
let ed = JSON.parse(eqdata);
|
||||||
|
getTypeList(ed);
|
||||||
|
setEqlist(ed);
|
||||||
|
queryFields(ed);
|
||||||
|
setQueryEqList(ed);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
isEqDataUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取设备明细清单
|
||||||
|
doPost(requests.fetchEquipFieldsList.url, { workCenterCode: item.code })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
if (!data.data) {
|
||||||
|
setTypeList(null);
|
||||||
|
setEqlist(null);
|
||||||
|
setQueryEqList(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//这里如果想要按照name进行分组即如下:
|
||||||
|
const retdata = data.data;
|
||||||
|
const results = GroupBy(retdata, function (item) {
|
||||||
|
return [item.code];
|
||||||
|
});
|
||||||
|
const eqlisttmp = results.map((item) => {
|
||||||
|
return {
|
||||||
|
code: item[0].code,
|
||||||
|
fileGroupId: item[0].fileGroupId,
|
||||||
|
fieldSort: item[0].fieldSort,
|
||||||
|
name: item[0].name,
|
||||||
|
operationCode: item[0].operationCode,
|
||||||
|
operationName: item[0].operationName,
|
||||||
|
operationSort: item[0].operationSort,
|
||||||
|
screenShowSerialNumber: item[0].screenShowSerialNumber,
|
||||||
|
typeCode: item[0].typeCode,
|
||||||
|
runningstatus: undefined,
|
||||||
|
typeName: item[0].typeName,
|
||||||
|
bmIotEquipmentRelationshipList: item,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
getTypeList(eqlisttmp);
|
||||||
|
setEqlist(eqlisttmp);
|
||||||
|
queryFields(eqlisttmp);
|
||||||
|
setQueryEqList(eqlisttmp);
|
||||||
|
//localStorage.setItem(item.code, JSON.stringify(eqlisttmp));
|
||||||
|
isEqDataUpdate = false;
|
||||||
|
} else {
|
||||||
|
setEqlist(null);
|
||||||
|
setTypeList(null);
|
||||||
|
queryFields(null);
|
||||||
|
setQueryEqList(null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
console.log(ex);
|
||||||
|
setTypeList(null);
|
||||||
|
setEqlist(null);
|
||||||
|
queryFields(null);
|
||||||
|
setQueryEqList(null);
|
||||||
|
})
|
||||||
|
.finally((data) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
function refreshData(eqfields) {
|
||||||
|
if (!eqfields) {
|
||||||
|
eqfields = eqlist;
|
||||||
|
}
|
||||||
|
if (!eqfields) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//把所有字段赋值
|
||||||
|
const eqfieldstmp = eqfields.map((item) => {
|
||||||
|
var obj = {};
|
||||||
|
var fields = item.bmIotEquipmentRelationshipList.map((field) => {
|
||||||
|
return field.iotField;
|
||||||
|
});
|
||||||
|
obj["equipCode"] = item.code;
|
||||||
|
fields.map((aa) => {
|
||||||
|
obj[aa] = "";
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
// doPost(requests.fetchLastEqFieldsValue.url, eqfieldstmp)
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .then((data) => {
|
||||||
|
// if (data.success) {
|
||||||
|
// bindData(data.data, eqfields);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((ex) => {
|
||||||
|
// console.log(ex);
|
||||||
|
// })
|
||||||
|
// .finally(() => {
|
||||||
|
// setLoading(false);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
function bindData(data, eqfields) {
|
||||||
|
const zret = data;
|
||||||
|
zret.map((ret) => {
|
||||||
|
eqfields.filter((a) => {
|
||||||
|
// var eqstatus1 = a;
|
||||||
|
// if (getAlertData && getAlertData.length > 0) {
|
||||||
|
// eqstatus1.alert = getAlertData.filter((al) => {
|
||||||
|
// if (al.equipCode === eqstatus1.code) {
|
||||||
|
// return al;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// let alertcout = eqstatus1?.alert?.filter((aa) => {
|
||||||
|
// if (!aa?.eliminationTime) {
|
||||||
|
// return aa;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// eqstatus1.alertcount = alertcout.length;
|
||||||
|
|
||||||
|
// }
|
||||||
|
if (ret && ret.equipCode && a.code === ret.equipCode) {
|
||||||
|
a.bmIotEquipmentRelationshipList.map((b) => {
|
||||||
|
b.iotFieldValue = ret[b.iotField];
|
||||||
|
b.iotFieldValueTime = ret[b.iotField + "__time"];
|
||||||
|
if (
|
||||||
|
(!a.runningstatus || a.runningstatus === 0) &&
|
||||||
|
a.runningstatus !== 3
|
||||||
|
) {
|
||||||
|
a.runningstatus = Number.parseInt(ret["runningstatus"]);
|
||||||
|
}
|
||||||
|
if (Number.isNaN(a.runningstatus)) {
|
||||||
|
a.runningstatus = 0;
|
||||||
|
}
|
||||||
|
if (!b.updateTime) {
|
||||||
|
b.updateTime = ret["time"];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
queryStauts(eqfields);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GroupBy(array, fn) {
|
||||||
|
const groups = {};
|
||||||
|
array.forEach(function (item) {
|
||||||
|
const group = JSON.stringify(fn(item));
|
||||||
|
//这里利用对象的key值唯一性的,创建数组
|
||||||
|
groups[group] = groups[group] || [];
|
||||||
|
groups[group].push(item);
|
||||||
|
});
|
||||||
|
//最后再利用map循环处理分组出来
|
||||||
|
return Object.keys(groups).map(function (group) {
|
||||||
|
return groups[group];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var closePop = () => {
|
||||||
|
setPop(false);
|
||||||
|
};
|
||||||
|
function dateData(property, bol) {
|
||||||
|
//property是你需要排序传入的key,bol为true时是升序,false为降序
|
||||||
|
return function (a, b) {
|
||||||
|
var value1 = a[property];
|
||||||
|
var value2 = b[property];
|
||||||
|
if (bol) {
|
||||||
|
// 升序
|
||||||
|
return Date.parse(value1) - Date.parse(value2);
|
||||||
|
} else {
|
||||||
|
// 降序
|
||||||
|
return Date.parse(value2) - Date.parse(value1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="h-screen overflow-hidden modal-container " ref={screenref}>
|
||||||
|
{isPop ? (
|
||||||
|
<Modal
|
||||||
|
title={`${popData.name} · ${isPopAlert ? "告警" : "其他"} `}
|
||||||
|
img={popData.Img}
|
||||||
|
closePopHandler={closePop}
|
||||||
|
>
|
||||||
|
{console.log(popData.alert)}
|
||||||
|
{isPopAlert ? (
|
||||||
|
<div>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="table table-compact w-full">
|
||||||
|
<thead>
|
||||||
|
<tr className=" ">
|
||||||
|
<th>告警发生时间</th>
|
||||||
|
<th>告警内容</th>
|
||||||
|
<th>告警消除时间</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{popData?.alert?.sort(dateData("time", false)).map((x) => {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td>{x.time}</td>
|
||||||
|
<td>{x.phenomenonName}</td>
|
||||||
|
<td>{x.eliminationTime}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={`grid gap-2 grid-cols-2 w-full`}>
|
||||||
|
{!popData.bmIotEquipmentRelationshipList
|
||||||
|
? ``
|
||||||
|
: popData.bmIotEquipmentRelationshipList
|
||||||
|
.filter((re) => {
|
||||||
|
if (isPopAlert && re.iotIsalarm && re.status) {
|
||||||
|
return re;
|
||||||
|
} else if (
|
||||||
|
!isPopAlert &&
|
||||||
|
!re.iotIsalarm &&
|
||||||
|
!re.iotIsMainField &&
|
||||||
|
re.status
|
||||||
|
) {
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) =>
|
||||||
|
parseFloat(a.fieldSort) > parseFloat(b.fieldSort) ? 1 : -1
|
||||||
|
) // .sort((a, b) => (!a.iotFieldValue ? 1 : -1))
|
||||||
|
.map((re) => {
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<label
|
||||||
|
htmlFor="field-value"
|
||||||
|
className="block text-xs text-gray-700"
|
||||||
|
>
|
||||||
|
{re.iotFieldDescribe}
|
||||||
|
</label>
|
||||||
|
{!isPopAlert ? (
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
title={re.iotFieldValueTime}
|
||||||
|
mouseEnterDelay={1}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
disabled
|
||||||
|
type="text"
|
||||||
|
name="field-value"
|
||||||
|
value={re.iotFieldValue}
|
||||||
|
id="field-value"
|
||||||
|
autoComplete="given-name"
|
||||||
|
className=" mt-1 text-xs disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none
|
||||||
|
focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm h-6 border-gray-300 "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
) : typeof re.iotFieldValue === "boolean" ? (
|
||||||
|
re.iotFieldValue ? (
|
||||||
|
<label
|
||||||
|
name="field-value"
|
||||||
|
id="field-value"
|
||||||
|
className=" mt-1 block w-10 bg-[#eb5f2c26] text-[#EB5E2C] border-[#EB5E2C] border py-0.5 px-1.5"
|
||||||
|
>
|
||||||
|
告警{" "}
|
||||||
|
</label>
|
||||||
|
) : (
|
||||||
|
<label
|
||||||
|
name="field-value"
|
||||||
|
id="field-value"
|
||||||
|
className=" mt-1 block w-10 bg-[#7caea02f] text-[#53B59A] border-[#53B59A] border py-0.5 px-1.5"
|
||||||
|
>
|
||||||
|
正常
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
re.iotFieldValue
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<Head>
|
||||||
|
<title>融通高科设备大屏 1.0</title>
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<script src="http://localhost:8097"></script>
|
||||||
|
</Head>
|
||||||
|
{/* {selectedtypecode==='1001'
|
||||||
|
} */}
|
||||||
|
<Header
|
||||||
|
getEqData={() => {
|
||||||
|
localStorage.removeItem(workCenterCode);
|
||||||
|
factorychanged({ code: workCenterCode });
|
||||||
|
}}
|
||||||
|
runningstatuschanged={(key, value) => {
|
||||||
|
setRunningStatus((prestate) => {
|
||||||
|
return {
|
||||||
|
...prestate,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
zzref={headerref}
|
||||||
|
selectedone={curstation}
|
||||||
|
factorychanged={factorychanged}
|
||||||
|
setTypeList={(data) => {
|
||||||
|
setTypeList(data);
|
||||||
|
}}
|
||||||
|
typechanged={(ty) => {
|
||||||
|
if(ty.name==='无'){return}
|
||||||
|
setselectedtypecode(ty?.code);
|
||||||
|
_selectedtypecode = ty?.code;
|
||||||
|
if (eqlist&&eqlist.length > 0) {
|
||||||
|
getTypeList(eqlist);
|
||||||
|
queryFields(eqlist);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
typeList={typeList}
|
||||||
|
statuslist={statuslist}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* overflow-y-scroll scrollbar-hide */}
|
||||||
|
{/* ${
|
||||||
|
screenref.current?.clientHeight
|
||||||
|
} ${headerref.current?.clientHeight} ${
|
||||||
|
listHeight.current?.clientHeight
|
||||||
|
} ${
|
||||||
|
screenref.current?.clientHeight - headerref.current?.clientHeight <
|
||||||
|
listHeight.current?.clientHeight && !isresize
|
||||||
|
? "h-[92%]"
|
||||||
|
: "h-fit"
|
||||||
|
} */}
|
||||||
|
<VTab
|
||||||
|
className="flex flex-col h-[94%] w-full bg-white px-3 pb-3"
|
||||||
|
ops={operations}
|
||||||
|
selectedtypecode={selectedtypecode}
|
||||||
|
tabchanged={(tab) => {
|
||||||
|
if (tab) {
|
||||||
|
setSelectedTab(tab.code);
|
||||||
|
_selectedTab = tab.code;
|
||||||
|
console.log(JSON.stringify(tab));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="h-full w-full">
|
||||||
|
{operations
|
||||||
|
.filter((x) => {
|
||||||
|
if (selectedTab === "-1" && x.code === "-1") {
|
||||||
|
return x;
|
||||||
|
} else if (selectedTab === "-2" && x.code !== "-2") {
|
||||||
|
return x;
|
||||||
|
} else if (
|
||||||
|
selectedTab !== "-2" &&
|
||||||
|
selectedTab !== "-1" &&
|
||||||
|
x.code === selectedTab
|
||||||
|
) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map((op) => {
|
||||||
|
return selectedtypecode === "-001" ? (
|
||||||
|
getTabContent(op.code)
|
||||||
|
) : !op.code ||
|
||||||
|
eqlist?.filter(
|
||||||
|
(z) =>
|
||||||
|
z.operationCode === op.code &&
|
||||||
|
z.typeCode === selectedtypecode
|
||||||
|
)?.length === 0 ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<span className="text-[#0185FF]">
|
||||||
|
<label className=" text-lg ">{"▌"}</label>
|
||||||
|
<label className=" text-lg ">{op.name}</label>
|
||||||
|
</span>
|
||||||
|
<div className="h-3"></div>
|
||||||
|
{getTabContent(op.code)}
|
||||||
|
<div className="h-3"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</VTab>
|
||||||
|
{/* <Results results={results} /> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
function getTabContent(op) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={listHeight}
|
||||||
|
className={`" relative grid grid-flow-row gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 text-sm text-center font-bold overflow-y-auto h-[92%] "`}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<svg
|
||||||
|
className="animate-spin -ml-1 mr-3 h-5 w-5 text-red absolute top-4 right-4"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
// stroke-width="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
className="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
{!eqlist || eqlist.length === 0
|
||||||
|
? `当前没有数据`
|
||||||
|
: eqlist
|
||||||
|
.filter((item) => {
|
||||||
|
{
|
||||||
|
if (op === item.operationCode) {
|
||||||
|
if (!item.runningstatus) {
|
||||||
|
if (runningStatus[0]) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
} else if (runningStatus[item.runningstatus]) {
|
||||||
|
return item;
|
||||||
|
} else if (item.runningstatus < 4 && item?.alertcount > 0) {
|
||||||
|
if (runningStatus[4] && !runningStatus[2]) {
|
||||||
|
return item;
|
||||||
|
} else if (!runningStatus[4] && runningStatus[2]) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((item) => {
|
||||||
|
if (item.typeCode === selectedtypecode) {
|
||||||
|
if (!item.screenShowSerialNumber)
|
||||||
|
item.screenShowSerialNumber = 1000000;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) =>
|
||||||
|
a.screenShowSerialNumber > b.screenShowSerialNumber ? 1 : -1
|
||||||
|
)
|
||||||
|
.map((item, idx) => (
|
||||||
|
// eslint-disable-next-line react/jsx-key
|
||||||
|
<Card
|
||||||
|
// classNamez={isLoading ? `animate-pulse` : ``}
|
||||||
|
alertData={alertData}
|
||||||
|
key={idx}
|
||||||
|
selectedTab={selectedTab}
|
||||||
|
typecode={_selectedtypecode}
|
||||||
|
eq={item}
|
||||||
|
popHandle={popHandle}
|
||||||
|
alartPopHandle={alartPopHandle}
|
||||||
|
></Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function getTypeList(curdata) {
|
||||||
|
var curdata = curdata.filter((zz) => zz.typeCode === _selectedtypecode);
|
||||||
|
var codes = curdata.map((zz) => zz.code);
|
||||||
|
// var _typelist = [];
|
||||||
|
var _opcodelist = [];
|
||||||
|
_opcodelist.push({
|
||||||
|
code: "-2",
|
||||||
|
name: "全部",
|
||||||
|
sort: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
var ctypes = curdata.map((zz) => {
|
||||||
|
// zz.typecode = zz.typecode;
|
||||||
|
// zz.typename = zz.bmEquipmentTypeIotVo.name;
|
||||||
|
// if (_typelist.filter((item) => item.code === zz.typeCode).length > 0) {
|
||||||
|
// } else {
|
||||||
|
// _typelist.push({
|
||||||
|
// name: zz.typeName,
|
||||||
|
// code: zz.typeCode,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
if (
|
||||||
|
_opcodelist.filter((item) => item.code === zz.operationCode).length > 0
|
||||||
|
) {
|
||||||
|
} else {
|
||||||
|
if (zz.operationCode) {
|
||||||
|
_opcodelist.push({
|
||||||
|
code: zz.operationCode,
|
||||||
|
name: zz.operationName,
|
||||||
|
sort: zz.operationSort ?? 10000,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
zz.operationCode = "-1";
|
||||||
|
zz.operationName = "其他";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (_opcodelist.filter((x) => x.code === "-1")?.length === 0) {
|
||||||
|
_opcodelist.push({
|
||||||
|
code: "-1",
|
||||||
|
name: "其他",
|
||||||
|
sort: 100000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var opslist = _opcodelist.sort((a, b) => (a.sort > b.sort ? 1 : -1));
|
||||||
|
setOperations(opslist);
|
||||||
|
// setTypeList(_typelist);
|
||||||
|
|
||||||
|
if (isImgLoad || !curdata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
curdata.map((eq) => {
|
||||||
|
if (!eq.fileGroupId) return;
|
||||||
|
doPost(requests.fetchEquipImg.url + eq.fileGroupId, {
|
||||||
|
clientId: "pc",
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success && data.rows.length > 0) {
|
||||||
|
var img = imgUrl + data.rows[0].fileCode;
|
||||||
|
doPost(img, {})
|
||||||
|
.then((res) => res.blob())
|
||||||
|
.then((file) => {
|
||||||
|
var blobUrl = URL.createObjectURL(file);
|
||||||
|
eq.Img = blobUrl;
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
console.log(ex);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// console.log(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
console.log(ex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
isImgLoad = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryStauts(eqdata) {
|
||||||
|
if (!_selectedtypecode) {
|
||||||
|
_selectedtypecode = selectedtypecode;
|
||||||
|
}
|
||||||
|
if (!eqdata || !_selectedtypecode) {
|
||||||
|
// setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var codes = eqdata
|
||||||
|
.filter((item) => {
|
||||||
|
if (item.typeCode === _selectedtypecode) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map((aa) => aa.code);
|
||||||
|
//获取设备状态
|
||||||
|
//接口请求物联网influxdb查询服务
|
||||||
|
//0 待产 1 生产 2 停产
|
||||||
|
if (_selectedtypecode && _selectedtypecode !== "1001") {
|
||||||
|
setStatuslist((prestate) => {
|
||||||
|
return {
|
||||||
|
...prestate,
|
||||||
|
0: codes.length,
|
||||||
|
1: 0,
|
||||||
|
2: 0,
|
||||||
|
3: 0,
|
||||||
|
4: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var alertcount = [];
|
||||||
|
if (_selectedtypecode) {
|
||||||
|
alertcount = eqdata.filter((eqstatus) => {
|
||||||
|
if (
|
||||||
|
eqstatus?.alertcount > 0 &&
|
||||||
|
eqstatus.typeCode === _selectedtypecode
|
||||||
|
) {
|
||||||
|
return eqstatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// setStatuslist((prestate) => {
|
||||||
|
// return {
|
||||||
|
// ...prestate,
|
||||||
|
// 4: alertcount?.length,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
const alertcountl = alertcount?.length;
|
||||||
|
|
||||||
|
if (codes.length === 0) return;
|
||||||
|
|
||||||
|
var waitingcount = eqdata.filter((eqstatus) => {
|
||||||
|
if (eqstatus.runningstatus === 1) {
|
||||||
|
return eqstatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var workingcount = eqdata.filter((eqstatus) => {
|
||||||
|
if (eqstatus.runningstatus === 2) {
|
||||||
|
return eqstatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var stopcount = eqdata.filter((eqstatus) => {
|
||||||
|
if (eqstatus.runningstatus === 3) {
|
||||||
|
return eqstatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// setWaitingCount(waitingcount.length);
|
||||||
|
// setWorkingCount(workingcount.length);
|
||||||
|
// setStopCount(stopcount.length);
|
||||||
|
// setStatuslist((prestate) => {
|
||||||
|
// return {
|
||||||
|
// ...prestate,
|
||||||
|
// [eqstatus.runningstatus]: waitingcount.length,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
setStatuslist((prestate) => {
|
||||||
|
return {
|
||||||
|
...prestate,
|
||||||
|
1: waitingcount.length,
|
||||||
|
2: workingcount.length,
|
||||||
|
3: stopcount.length,
|
||||||
|
4: alertcountl,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
var wclist = waitingcount.map((count) => count.code);
|
||||||
|
var sclist = stopcount.map((count) => count.code);
|
||||||
|
var rclist = workingcount.map((count) => count.code);
|
||||||
|
var alertlist = alertcount?.map((count) => count.code);
|
||||||
|
var nonestate = 0;
|
||||||
|
eqdata
|
||||||
|
.filter((item) => {
|
||||||
|
if (codes.indexOf(item.code) > -1) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map((eq) => {
|
||||||
|
if (wclist.filter((a) => a === eq.code).length > 0) {
|
||||||
|
eq.runningstatus = 1;
|
||||||
|
// eq.updatestatus(1);
|
||||||
|
} else if (sclist.filter((a) => a === eq.code).length > 0) {
|
||||||
|
eq.runningstatus = 3;
|
||||||
|
// eq.updatestatus(3);
|
||||||
|
} else if (rclist.filter((a) => a === eq.code).length > 0) {
|
||||||
|
eq.runningstatus = 2;
|
||||||
|
// eq.updatestatus(2);
|
||||||
|
} else if (alertlist.filter((a) => a === eq.code).length > 0) {
|
||||||
|
eq.runningstatus = 4;
|
||||||
|
// eq.updatestatus(4);
|
||||||
|
} else {
|
||||||
|
eq.runningstatus = 0;
|
||||||
|
nonestate++;
|
||||||
|
}
|
||||||
|
// if (alertlist?.filter((a) => a === eq.code).length > 0) {
|
||||||
|
// eq.runningstatus = 1;
|
||||||
|
// // eq.updatestatus();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
setStatuslist((prestate) => {
|
||||||
|
return {
|
||||||
|
...prestate,
|
||||||
|
0: nonestate,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryFields(data) {
|
||||||
|
if (!data || data === undefined || data.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// setLoading(true);
|
||||||
|
data.filter((item) => {
|
||||||
|
if (item.typeCode === _selectedtypecode && item.updateFields) {
|
||||||
|
item.updateFields();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// console.log(typelist);
|
||||||
|
refreshData(data);
|
||||||
|
//获取绑定的工单
|
||||||
|
// doPost(requests.fetchBindList.url, {})
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .then((data) => {
|
||||||
|
// if (data.success) {
|
||||||
|
// var bindlist = data.data;
|
||||||
|
// var arr = bindlist.map((bind) => bind.workOrderCode);
|
||||||
|
// const set = new Set(arr);
|
||||||
|
// setProductCount(set.length)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((ex) => {
|
||||||
|
// console.log(ex);
|
||||||
|
// });
|
||||||
|
//获取设备以及字段信息
|
||||||
|
// queryStauts(codes, eqdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// doPost(requests.fetchEquipFields.url, codes)
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .then((data) => {
|
||||||
|
// if (data.success) {
|
||||||
|
// // var fileGroupIds = curdata.map((zz) => zz.fileGroupId);
|
||||||
|
// //获取图片信息
|
||||||
|
// var eqdata = data.data;
|
||||||
|
// setEqlist(eqdata);
|
||||||
|
// setAllEquipCount(eqdata.length);
|
||||||
|
// queryStauts(codes, eqdata);
|
||||||
|
|
||||||
|
// if (isImgLoad) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// eqdata.map((eq) => {
|
||||||
|
// doPost(requests.fetchEquipImg.url + eq.fileGroupId, {
|
||||||
|
// clientId: "pc",
|
||||||
|
// })
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .then((data) => {
|
||||||
|
// if (data.success && data.rows.length > 0) {
|
||||||
|
// var img = imgUrl + data.rows[0].fileCode;
|
||||||
|
// doPost(img, {})
|
||||||
|
// .then((res) => res.blob())
|
||||||
|
// .then((file) => {
|
||||||
|
// var blobUrl = URL.createObjectURL(file);
|
||||||
|
// eq.Img = blobUrl;
|
||||||
|
// eq.updateImg();
|
||||||
|
// })
|
||||||
|
// .catch((ex) => {
|
||||||
|
// console.log(ex);
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// // console.log(data);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((ex) => {
|
||||||
|
// console.log(ex);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// isImgLoad = true;
|
||||||
|
// } else {
|
||||||
|
// setEqlist([]);
|
||||||
|
// setAllEquipCount(0);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((ex) => {
|
||||||
|
// setEqlist([]);
|
||||||
|
// console.log(ex);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// export async function getStaticProps() {
|
||||||
|
// const request = await fetch(
|
||||||
|
// `&{
|
||||||
|
// requests.fetchFactory.url
|
||||||
|
// }`
|
||||||
|
// )
|
||||||
|
// .then((res) => res.json())
|
||||||
|
// .catch((ex) => console.log(ex)); // then get result and pass it with result.json
|
||||||
|
// return {
|
||||||
|
// props: {
|
||||||
|
// results: !request ? null : request.results,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// }
|
|
@ -0,0 +1,216 @@
|
||||||
|
import React, { useRef, useEffect, useState, useMemo } from "react";
|
||||||
|
import { SearchOutlined } from "@ant-design/icons";
|
||||||
|
import _, { indexOf } from "lodash";
|
||||||
|
import moment from "moment";
|
||||||
|
import "antd/dist/antd.css";
|
||||||
|
import { Select, Button, DatePicker, Checkbox, message, Spin } from "antd";
|
||||||
|
const { Option } = Select;
|
||||||
|
import {
|
||||||
|
getFactory,
|
||||||
|
getEquipType,
|
||||||
|
getEquip,
|
||||||
|
getEquipmentLocation,
|
||||||
|
getEquipmentLocationEchart,
|
||||||
|
} from "../../../reportUtils/getQueryData";
|
||||||
|
function CurveParam() {
|
||||||
|
//HTML元素
|
||||||
|
let refinput = useRef();
|
||||||
|
|
||||||
|
//常量
|
||||||
|
const [factoryValue, setFactoryValue] = useState("");
|
||||||
|
const [factory, setFactory] = useState([]);
|
||||||
|
const [equipTypeValue, setEquipTypeValue] = useState("");
|
||||||
|
const [equipType, setEquipType] = useState([]);
|
||||||
|
const [equipValue, setEquipValue] = useState("");
|
||||||
|
const [equip, setEquip] = useState([]);
|
||||||
|
// 点位集合
|
||||||
|
const [locations, setLocations] = useState([]);
|
||||||
|
// 点位变化值
|
||||||
|
const [locationValue, setLocationValue] = useState([]);
|
||||||
|
|
||||||
|
//react render
|
||||||
|
useEffect(() => {
|
||||||
|
let _locationValue = locationValue;
|
||||||
|
// iframe 消息通讯
|
||||||
|
let locationsValues = _.cloneDeep(locations);
|
||||||
|
let retData = {};
|
||||||
|
let selectItems = [];
|
||||||
|
_locationValue.map((key) => {
|
||||||
|
let index = _.findIndex(locationsValues, function (o) {
|
||||||
|
return o.value === key;
|
||||||
|
});
|
||||||
|
selectItems.push(locationsValues[index]);
|
||||||
|
});
|
||||||
|
retData.selectItems = selectItems;
|
||||||
|
retData.equipCode = equipValue;
|
||||||
|
console.log(retData);
|
||||||
|
if (retData.selectItems?.length > 0) {
|
||||||
|
window.parent.postMessage(retData, "*");
|
||||||
|
}
|
||||||
|
}, [locationValue]);
|
||||||
|
|
||||||
|
// 查询设备类型,查询车间,
|
||||||
|
useEffect(() => {
|
||||||
|
getEquipType().then((data) => {
|
||||||
|
setEquipType(data);
|
||||||
|
});
|
||||||
|
getFactory().then((data) => {
|
||||||
|
setFactory(data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
// 查询设备 监听设备类型
|
||||||
|
useEffect(() => {
|
||||||
|
getEquip({ equipTypeValue, factoryValue }).then((data) => {
|
||||||
|
setEquip(data);
|
||||||
|
setEquipValue("");
|
||||||
|
setLocations([]);
|
||||||
|
setLocationValue([]);
|
||||||
|
});
|
||||||
|
}, [equipTypeValue, factoryValue]);
|
||||||
|
|
||||||
|
//方法
|
||||||
|
const locationChange = (checkValue) => {
|
||||||
|
setLocationValue(checkValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelClick = () => {
|
||||||
|
setLocationValue([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const equipTypeChange = (value) => {
|
||||||
|
setEquipTypeValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const factoryChange = (value) => {
|
||||||
|
setFactoryValue(value);
|
||||||
|
};
|
||||||
|
const equipChange = (value = "") => {
|
||||||
|
if (value && equipValue !== value) {
|
||||||
|
setLocationValue([]);
|
||||||
|
|
||||||
|
getEquipmentLocation(value).then((data) => {
|
||||||
|
let locationArr = data
|
||||||
|
.sort((a, b) => (a.iotfieldsort > b.iotfieldsort ? 1 : -1))
|
||||||
|
.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item.iotfielddescribe,
|
||||||
|
value: item.iotField,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setLocations(locationArr);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setLocationValue([]);
|
||||||
|
setLocations([]);
|
||||||
|
}
|
||||||
|
setEquipValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-90 mt-4 mx-3">
|
||||||
|
<div className="flex flex-row space-x-4 ">
|
||||||
|
<div className="workShop">
|
||||||
|
<span>车间:</span>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
size="small"
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={factoryChange}
|
||||||
|
value={factoryValue}
|
||||||
|
>
|
||||||
|
{factory.map((item) => {
|
||||||
|
return (
|
||||||
|
<Option value={item.code} key={item.gid}>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="equipType">
|
||||||
|
<span>设备类型:</span>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
size="small"
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={equipTypeChange}
|
||||||
|
value={equipTypeValue}
|
||||||
|
>
|
||||||
|
{equipType.map((item) => {
|
||||||
|
return (
|
||||||
|
<Option value={item.equipTypeGid} key={item.gid}>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="equipNo">
|
||||||
|
<span>设备编号:</span>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
size="small"
|
||||||
|
onChange={equipChange}
|
||||||
|
optionFilterProp="children"
|
||||||
|
style={{ width: 150 }}
|
||||||
|
value={equipValue}
|
||||||
|
filterOption={(input, option) =>
|
||||||
|
option.children.toLowerCase().includes(input.toLowerCase())
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{equip.map((item) => {
|
||||||
|
return (
|
||||||
|
<Option
|
||||||
|
value={item.serialNumber}
|
||||||
|
key={item.serialNumber + item.name}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="toolBar flex flex-row mt-4">
|
||||||
|
<div className=" text-xs ">
|
||||||
|
{locations.length > 0 ? (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="ml-2 mt-2"
|
||||||
|
size="small"
|
||||||
|
onClick={handleCancelClick}
|
||||||
|
>
|
||||||
|
取消全选
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="legend overflow-y-auto ml-10 mt-2 h-32 ">
|
||||||
|
<Checkbox.Group
|
||||||
|
ref={refinput}
|
||||||
|
value={locationValue}
|
||||||
|
options={locations}
|
||||||
|
onChange={locationChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.ant-checkbox-group-item {
|
||||||
|
width: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolBar {
|
||||||
|
position: relative;
|
||||||
|
border-width: 1px 0 1px 0;
|
||||||
|
border-color: #d3d3d3;
|
||||||
|
background: #f8f8f8;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default CurveParam;
|
|
@ -0,0 +1,592 @@
|
||||||
|
import React, { useRef, useEffect, useState, useMemo } from "react";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
import { SearchOutlined } from "@ant-design/icons";
|
||||||
|
import _, { indexOf } from "lodash";
|
||||||
|
import moment from "moment";
|
||||||
|
import "antd/dist/antd.css";
|
||||||
|
import { Select, Button, DatePicker, Checkbox, message, Spin } from "antd";
|
||||||
|
import {
|
||||||
|
getFactory,
|
||||||
|
getEquipType,
|
||||||
|
getEquip,
|
||||||
|
getEquipmentLocation,
|
||||||
|
getEquipmentLocationEchart,
|
||||||
|
} from "../../../reportUtils/getQueryData";
|
||||||
|
const { Option } = Select;
|
||||||
|
import theme from "../../../reportUtils/theme.json";
|
||||||
|
|
||||||
|
function EquipmentCurve() {
|
||||||
|
const dataZoomRef = useRef();
|
||||||
|
let refinput = useRef();
|
||||||
|
|
||||||
|
echarts.registerTheme("black", theme);
|
||||||
|
// 设备类型集合
|
||||||
|
const [equipType, setEquipType] = useState([]);
|
||||||
|
// 车间集合
|
||||||
|
const [factory, setFactory] = useState([]);
|
||||||
|
// 设备集合
|
||||||
|
const [equip, setEquip] = useState([]);
|
||||||
|
//设备类型值
|
||||||
|
const [equipTypeValue, setEquipTypeValue] = useState("");
|
||||||
|
//车间值
|
||||||
|
const [factoryValue, setFactoryValue] = useState("");
|
||||||
|
//设备值
|
||||||
|
const [equipValue, setEquipValue] = useState("");
|
||||||
|
// 点位集合
|
||||||
|
const [locations, setLocations] = useState([]);
|
||||||
|
// 点位变化值
|
||||||
|
const [locationValue, setLocationValue] = useState([]);
|
||||||
|
// 时间变化值
|
||||||
|
const [dateValue, setDateValue] = useState("");
|
||||||
|
// 存放chartData
|
||||||
|
const [chartData, setChartData] = useState({});
|
||||||
|
// 存放chartref
|
||||||
|
const [chartRef, setChartRef] = useState([]);
|
||||||
|
const [spin, setSpin] = useState(false);
|
||||||
|
const equipTypeChange = (value) => {
|
||||||
|
setEquipTypeValue(value);
|
||||||
|
};
|
||||||
|
const factoryChange = (value) => {
|
||||||
|
setFactoryValue(value);
|
||||||
|
};
|
||||||
|
const equipChange = (value = "") => {
|
||||||
|
if (value && equipValue !== value) {
|
||||||
|
setLocationValue([]);
|
||||||
|
setChartData({});
|
||||||
|
setChartRef([]);
|
||||||
|
getEquipmentLocation(value).then((data) => {
|
||||||
|
let locationArr = data
|
||||||
|
.sort((a, b) => (a.iotfieldsort > b.iotfieldsort ? 1 : -1))
|
||||||
|
.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item.iotfielddescribe,
|
||||||
|
value: item.iotField,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setLocations(locationArr);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setLocationValue([]);
|
||||||
|
setChartData({});
|
||||||
|
setChartRef([]);
|
||||||
|
setLocations([]);
|
||||||
|
}
|
||||||
|
setEquipValue(value);
|
||||||
|
};
|
||||||
|
const locationChange = (checkValue) => {
|
||||||
|
setLocationValue(checkValue);
|
||||||
|
if (checkValue.length > 0) {
|
||||||
|
doSearch(checkValue);
|
||||||
|
} else {
|
||||||
|
setChartRef([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dateChange = (date, dateString) => {
|
||||||
|
setDateValue(dateString);
|
||||||
|
};
|
||||||
|
const handleCancelClick = () => {
|
||||||
|
setLocationValue([]);
|
||||||
|
setChartRef([]);
|
||||||
|
};
|
||||||
|
const handleClick = () => {
|
||||||
|
if (!equipValue) {
|
||||||
|
message.warning("请选择需要查询的设备!");
|
||||||
|
} else if (!dateValue) {
|
||||||
|
message.warning("请选择需要查询的时间!");
|
||||||
|
} else if (locationValue.length === 0) {
|
||||||
|
message.warning("请勾选需要查询的点位!");
|
||||||
|
} else {
|
||||||
|
setSpin(true);
|
||||||
|
doSearch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 查询设备类型,查询车间,
|
||||||
|
useEffect(() => {
|
||||||
|
getEquipType().then((data) => {
|
||||||
|
setEquipType(data);
|
||||||
|
});
|
||||||
|
getFactory().then((data) => {
|
||||||
|
setFactory(data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
// 查询设备 监听设备类型
|
||||||
|
useEffect(() => {
|
||||||
|
getEquip({ equipTypeValue, factoryValue }).then((data) => {
|
||||||
|
setEquip(data);
|
||||||
|
setEquipValue("");
|
||||||
|
setLocations([]);
|
||||||
|
setLocationValue([]);
|
||||||
|
setChartData({});
|
||||||
|
setChartRef([]);
|
||||||
|
});
|
||||||
|
}, [equipTypeValue, factoryValue]);
|
||||||
|
useEffect(() => {
|
||||||
|
let dataZoomChart = echarts.init(dataZoomRef.current);
|
||||||
|
dataZoomChart.clear();
|
||||||
|
if (locationValue.length > 0) {
|
||||||
|
if (!dataZoomRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let dataZoomData = {};
|
||||||
|
let flag = true;
|
||||||
|
for (let i = 0; i < locationValue.length; i++) {
|
||||||
|
if (flag && chartData[locationValue[i]]) {
|
||||||
|
dataZoomData = chartData[locationValue[i]];
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dataZoomChart = echarts.init(dataZoomRef.current);
|
||||||
|
let option = {
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: dataZoomData["times"] || [],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
scale: true,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
height: "10",
|
||||||
|
width: "2000",
|
||||||
|
},
|
||||||
|
dataZoom: {
|
||||||
|
top: "10",
|
||||||
|
left: "0",
|
||||||
|
type: "slider",
|
||||||
|
width: "92%",
|
||||||
|
height: "30px",
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: dataZoomData["values"] || [],
|
||||||
|
type: "line",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
dataZoomChart.setOption(option);
|
||||||
|
// 动态生成echart
|
||||||
|
let childsEchart = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < chartRef.length; i++) {
|
||||||
|
if (!chartData[chartRef[i]]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let dom = document.getElementById(chartRef[i]);
|
||||||
|
let echatItem = echarts.init(dom, "black");
|
||||||
|
let optionItem = {
|
||||||
|
grid: {
|
||||||
|
top: "30",
|
||||||
|
height: "125",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: `${chartData[chartRef[i]]["title"]}`,
|
||||||
|
x: "center",
|
||||||
|
y: "top",
|
||||||
|
textAlign: "left",
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
// link: null,
|
||||||
|
animation: true,
|
||||||
|
type: "cross",
|
||||||
|
},
|
||||||
|
formatter: function (params) {
|
||||||
|
//在此处直接用 formatter 属性
|
||||||
|
let showdata = params[0];
|
||||||
|
// 根据自己的需求返回数据
|
||||||
|
return `
|
||||||
|
<div>时间:${showdata.axisValueLabel}</div>
|
||||||
|
<div>数据:<a style="color: #00E8D7">${showdata.data}</a></div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: chartData[chartRef[i]]["times"],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
height: "10",
|
||||||
|
width: "2000",
|
||||||
|
},
|
||||||
|
dataZoom: {
|
||||||
|
type: "inside",
|
||||||
|
zoomOnMouseWheel: false,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
symbol: "circle",
|
||||||
|
symbolSize: 5,
|
||||||
|
data: chartData[chartRef[i]]["values"],
|
||||||
|
type: "line",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
echatItem.setOption(optionItem);
|
||||||
|
childsEchart.push(echatItem);
|
||||||
|
}
|
||||||
|
echarts.connect([dataZoomChart].concatchildsEchart);
|
||||||
|
}
|
||||||
|
}, [chartRef]);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Spin spinning={spin}>
|
||||||
|
<div className="header">
|
||||||
|
<div className="search">
|
||||||
|
<div className="workShop">
|
||||||
|
<span>车间:</span>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
size="small"
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={factoryChange}
|
||||||
|
value={factoryValue}
|
||||||
|
>
|
||||||
|
{factory.map((item) => {
|
||||||
|
return (
|
||||||
|
<Option value={item.code} key={item.gid}>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="equipType">
|
||||||
|
<span>设备类型:</span>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
size="small"
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={equipTypeChange}
|
||||||
|
value={equipTypeValue}
|
||||||
|
>
|
||||||
|
{equipType.map((item) => {
|
||||||
|
return (
|
||||||
|
<Option value={item.equipTypeGid} key={item.gid}>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="equipNo">
|
||||||
|
<span>设备编号:</span>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
size="small"
|
||||||
|
onChange={equipChange}
|
||||||
|
optionFilterProp="children"
|
||||||
|
style={{ width: 150 }}
|
||||||
|
value={equipValue}
|
||||||
|
filterOption={(input, option) =>
|
||||||
|
option.children.toLowerCase().includes(input.toLowerCase())
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{equip.map((item) => {
|
||||||
|
return (
|
||||||
|
<Option
|
||||||
|
value={item.serialNumber}
|
||||||
|
key={item.serialNumber + item.name}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="equipDate">
|
||||||
|
<span>查询日期:</span>
|
||||||
|
<DatePicker
|
||||||
|
style={{ width: 150 }}
|
||||||
|
size="small"
|
||||||
|
onChange={dateChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<SearchOutlined />}
|
||||||
|
size="small"
|
||||||
|
shape="round"
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
查询
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="dataZoom">
|
||||||
|
<div className="zoomTitle">时间窗口选择框:</div>
|
||||||
|
<div
|
||||||
|
className="zoomContent"
|
||||||
|
ref={dataZoomRef}
|
||||||
|
style={{
|
||||||
|
width: "90%",
|
||||||
|
height: "150px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div className="toolBar flex flex-row">
|
||||||
|
<div className=" text-xs ">
|
||||||
|
{locations.length > 0 ? (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="ml-2 mt-2"
|
||||||
|
size="small"
|
||||||
|
onClick={handleCancelClick}
|
||||||
|
>
|
||||||
|
取消全选
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="legend overflow-y-auto h-32 ">
|
||||||
|
<Checkbox.Group
|
||||||
|
ref={refinput}
|
||||||
|
value={locationValue}
|
||||||
|
options={locations}
|
||||||
|
onChange={locationChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex h-2"></div>
|
||||||
|
<div className="content ">
|
||||||
|
{chartRef.map((item) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id={item}
|
||||||
|
key={item}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "200px",
|
||||||
|
marginBottom: "16px",
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
|
<div className={spin ? "mask" : "unmask"}></div>
|
||||||
|
<style jsx>{`
|
||||||
|
.ant-checkbox-group-item {
|
||||||
|
width: 200px !important;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 35px;
|
||||||
|
box-shadow: 5px 2px 20px -4px rgb(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
.search {
|
||||||
|
display: flex;
|
||||||
|
height: 35px;
|
||||||
|
margin: auto 15px;
|
||||||
|
}
|
||||||
|
.search div {
|
||||||
|
line-height: 35px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
.search .workShop {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
.search .equipDate {
|
||||||
|
margin-right: 80px;
|
||||||
|
}
|
||||||
|
.dataZoom {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.zoomTitle {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 10px;
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
.zoomContent {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
left: 140px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
height: 66vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.toolBar {
|
||||||
|
position: relative;
|
||||||
|
border-width: 1px 0 1px 0;
|
||||||
|
border-color: #d3d3d3;
|
||||||
|
background: #f8f8f8;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend {
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
padding-left: 6%;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
.unmask {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
function doSearch(checkValue) {
|
||||||
|
let _locationValue = ``;
|
||||||
|
if (checkValue) {
|
||||||
|
_locationValue = checkValue;
|
||||||
|
} else {
|
||||||
|
_locationValue = locationValue;
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
workCenterCode:`${factoryValue}`,
|
||||||
|
startTime: `${dateValue} 00:00:00`,
|
||||||
|
endTime: `${dateValue} 23:59:59`,
|
||||||
|
equipCode: equipValue,
|
||||||
|
fields: _locationValue,
|
||||||
|
};
|
||||||
|
getEquipmentLocationEchart(params).then((data) => {
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
createCharts();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
createChartsEmp();
|
||||||
|
message.warning("未能查询到数据!");
|
||||||
|
|
||||||
|
}
|
||||||
|
setSpin(false);
|
||||||
|
|
||||||
|
function createChartsEmp() {
|
||||||
|
let locationsValues = _.cloneDeep(locations);
|
||||||
|
let processData = {};
|
||||||
|
let commonTimes = [];
|
||||||
|
_locationValue.map(key=> {
|
||||||
|
if (key !== "time") {
|
||||||
|
if (!processData.hasOwnProperty(key)) {
|
||||||
|
let index = _.findIndex(locationsValues, function (o) {
|
||||||
|
return o.value === key;
|
||||||
|
});
|
||||||
|
|
||||||
|
let title = index >= 0 ? locationsValues[index]["label"] : "";
|
||||||
|
processData[key] = {
|
||||||
|
title: title,
|
||||||
|
key: key,
|
||||||
|
times: [],
|
||||||
|
values: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// processData[key]["values"].push([]);
|
||||||
|
}
|
||||||
|
// let time = moment(data[i]["time"]).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
// commonTimes.push(time);
|
||||||
|
})
|
||||||
|
let domCharts = [];
|
||||||
|
commonTimes = _.reverse(commonTimes);
|
||||||
|
for (let key in processData) {
|
||||||
|
processData[key]["times"] = commonTimes;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < _locationValue.length; i++) {
|
||||||
|
if (processData[_locationValue[i]]) {
|
||||||
|
domCharts.push(_locationValue[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setChartData(processData);
|
||||||
|
setChartRef(domCharts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCharts() {
|
||||||
|
let locationsValues = _.cloneDeep(locations);
|
||||||
|
let processData = {};
|
||||||
|
let commonTimes = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
for (let key in data[i]) {
|
||||||
|
if (key !== "time") {
|
||||||
|
if (!processData.hasOwnProperty(key)) {
|
||||||
|
let index = _.findIndex(locationsValues, function (o) {
|
||||||
|
return o.value === key;
|
||||||
|
});
|
||||||
|
|
||||||
|
let title = index >= 0 ? locationsValues[index]["label"] : "";
|
||||||
|
processData[key] = {
|
||||||
|
title: title,
|
||||||
|
key: key,
|
||||||
|
times: [],
|
||||||
|
values: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
processData[key]["values"].push(data[i][key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let time = moment(data[i]["time"]).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
commonTimes.push(time);
|
||||||
|
}
|
||||||
|
let domCharts = [];
|
||||||
|
commonTimes = _.reverse(commonTimes);
|
||||||
|
for (let key in processData) {
|
||||||
|
processData[key]["times"] = commonTimes;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < _locationValue.length; i++) {
|
||||||
|
if (processData[_locationValue[i]]) {
|
||||||
|
domCharts.push(_locationValue[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_locationValue.map(x=>{
|
||||||
|
if(domCharts.indexOf(x)===-1){
|
||||||
|
domCharts.push(x);
|
||||||
|
|
||||||
|
let index = _.findIndex(locationsValues, function (o) {
|
||||||
|
return o.value === x;
|
||||||
|
});
|
||||||
|
|
||||||
|
let title = index >= 0 ? locationsValues[index]["label"] : "";
|
||||||
|
|
||||||
|
processData[x] = {
|
||||||
|
title: title,
|
||||||
|
key: x,
|
||||||
|
times: [],
|
||||||
|
values: [],
|
||||||
|
};
|
||||||
|
processData[x]["times"] = commonTimes;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setChartData(processData);
|
||||||
|
setChartRef(domCharts);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EquipmentCurve;
|
|
@ -0,0 +1,981 @@
|
||||||
|
import React, { useEffect, useState, useMemo } from 'react';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
const Liquid = dynamic(() => import('@ant-design/plots').then(({ Liquid }) => Liquid),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
const RingProgress = dynamic(() => import('@ant-design/plots').then(({ RingProgress }) => RingProgress),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const Progress = dynamic(() => import('@ant-design/plots').then(({ Progress }) => Progress),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
const Line = dynamic(() => import('@ant-design/plots').then(({ Line }) => Line),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
import { List, Carousel, Cascader, Skeleton, Spin } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import {
|
||||||
|
getTimeRate,
|
||||||
|
getAbnlException,
|
||||||
|
getSevenQualifiedRate,
|
||||||
|
getOpByRouterLine,
|
||||||
|
getOrganization,
|
||||||
|
} from '../../../reportUtils/getHomePageData';
|
||||||
|
import homecss from '../../../styles/homecss.module.css';
|
||||||
|
function HomePage() {
|
||||||
|
let [loading, setLoading] = useState(false);
|
||||||
|
// 工厂选择
|
||||||
|
let [workShops, setWorkShops] = useState([]);
|
||||||
|
// 级联选择框的数据存储
|
||||||
|
let [workShopValue, setWorkShopValue] = useState([]);
|
||||||
|
// 级联选择工作中心
|
||||||
|
let [caWorkCenterCode, setCaWorkCenterCode] = useState('');
|
||||||
|
// 级联选择产线
|
||||||
|
let [caRouterLineCode, setCaRouterLineCode] = useState('');
|
||||||
|
// 水波图,柱形图,环形图
|
||||||
|
let [timeRate, setTimeRate] = useState({});
|
||||||
|
// 异常消息
|
||||||
|
let [abnlException, setAbnlException] = useState({
|
||||||
|
equip: [],
|
||||||
|
quality: [],
|
||||||
|
prodexec: [],
|
||||||
|
});
|
||||||
|
let [exceptions, setExceptions] = useState([]);
|
||||||
|
let [currentExceptions, setCurrentExceptions] = useState([]);
|
||||||
|
//存储工序消息
|
||||||
|
let [opData, setOpdata] = useState([]);
|
||||||
|
// 质量合格率
|
||||||
|
let [qualifiedRate, setQualifiedRate] = useState([]);
|
||||||
|
// 页面初始化加载
|
||||||
|
useEffect(() => {
|
||||||
|
getOrganization().then(data => {
|
||||||
|
if (data.length > 0) {
|
||||||
|
let defaultOrg = {};
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (
|
||||||
|
data[i]['children'] &&
|
||||||
|
data[i]['children'].length === 0
|
||||||
|
) {
|
||||||
|
data[i]['isLeaf'] = false;
|
||||||
|
}
|
||||||
|
if (data[i]['defaultWorkCenter']) {
|
||||||
|
defaultOrg = data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let defaultOrgValue = [];
|
||||||
|
if (defaultOrg['defaultWorkCenter']) {
|
||||||
|
defaultOrgValue.push(defaultOrg['value']);
|
||||||
|
|
||||||
|
let child = defaultOrg['children'];
|
||||||
|
let flag = true;
|
||||||
|
for (let i = 0; i < child.length; i++) {
|
||||||
|
if (child[i]['defaultRoute'] && flag) {
|
||||||
|
flag = false;
|
||||||
|
defaultOrgValue.push(child[i]['value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setWorkShops(data);
|
||||||
|
setWorkShopValue(defaultOrgValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let workCenterCode = '';
|
||||||
|
let routerLineCode = '';
|
||||||
|
if (workShopValue.length > 0) {
|
||||||
|
workCenterCode = workShopValue[0];
|
||||||
|
setOpdata([]);
|
||||||
|
getTimeRate(workCenterCode).then(data => {
|
||||||
|
if (data) {
|
||||||
|
for (let key in data) {
|
||||||
|
if (data[key] == 1 || data[key] == 0) {
|
||||||
|
data[key] = parseInt(data[key]);
|
||||||
|
} else {
|
||||||
|
data[key] = parseFloat(data[key].toFixed(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeRate(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getAbnlException(workCenterCode).then(data => {
|
||||||
|
if (data) {
|
||||||
|
let exceptions = {
|
||||||
|
equip: [],
|
||||||
|
quality: [],
|
||||||
|
prodexec: [],
|
||||||
|
};
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].typeException === 'equip') {
|
||||||
|
exceptions['equip'].push(data[i]);
|
||||||
|
} else if (data[i].typeException === 'quality') {
|
||||||
|
exceptions['quality'].push(data[i]);
|
||||||
|
} else {
|
||||||
|
exceptions['prodexec'].push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setExceptions(data);
|
||||||
|
setCurrentExceptions(data);
|
||||||
|
setAbnlException(exceptions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getSevenQualifiedRate(workCenterCode).then(data => {
|
||||||
|
if (data) {
|
||||||
|
let rates = [];
|
||||||
|
for (let key in data) {
|
||||||
|
rates.push({
|
||||||
|
month: key,
|
||||||
|
value: data[key],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setQualifiedRate(rates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (workShopValue.length === 2) {
|
||||||
|
setLoading(true);
|
||||||
|
routerLineCode = workShopValue[1];
|
||||||
|
getOpByRouterLine(routerLineCode).then(data => {
|
||||||
|
setLoading(false);
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
setOpdata(data);
|
||||||
|
} else {
|
||||||
|
setOpdata([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setQualifiedRate([]);
|
||||||
|
setOpdata([]);
|
||||||
|
setCurrentExceptions([]);
|
||||||
|
setExceptions([]);
|
||||||
|
setTimeRate({});
|
||||||
|
setAbnlException({
|
||||||
|
equip: [],
|
||||||
|
quality: [],
|
||||||
|
prodexec: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [workShopValue]);
|
||||||
|
const clickException = type => {
|
||||||
|
let exceptionData = [];
|
||||||
|
if (type === 'all') {
|
||||||
|
exceptionData = exceptions;
|
||||||
|
} else if (type === 'equip') {
|
||||||
|
exceptionData = abnlException['equip'];
|
||||||
|
} else if (type === 'quality') {
|
||||||
|
exceptionData = abnlException['quality'];
|
||||||
|
} else {
|
||||||
|
exceptionData = abnlException['prodexec'];
|
||||||
|
}
|
||||||
|
setCurrentExceptions(exceptionData);
|
||||||
|
};
|
||||||
|
const factoryChange = (value, selectedOptions) => {
|
||||||
|
let currentValue = [];
|
||||||
|
if (value) {
|
||||||
|
currentValue = value;
|
||||||
|
}
|
||||||
|
console.log(currentValue);
|
||||||
|
setWorkShopValue(currentValue);
|
||||||
|
};
|
||||||
|
// 用来进行异步加载
|
||||||
|
/* const loadData = selectedOptions => {
|
||||||
|
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||||
|
const { value = '' } = targetOption;
|
||||||
|
targetOption.loading = true;
|
||||||
|
getRouterLineByFactory(value).then(data => {
|
||||||
|
targetOption.loading = false;
|
||||||
|
let children = [];
|
||||||
|
if (data.length > 0) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
children.push({
|
||||||
|
value: data[i].code,
|
||||||
|
label: data[i].name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetOption.children = children;
|
||||||
|
setWorkShops([...workShops]);
|
||||||
|
});
|
||||||
|
}; */
|
||||||
|
return (
|
||||||
|
<div className={homecss['home_page']}>
|
||||||
|
<div className={homecss['header']}>
|
||||||
|
<div className={homecss['header_title']}>
|
||||||
|
<span className={homecss['title']}>
|
||||||
|
<Cascader
|
||||||
|
className={homecss['header_workshop']}
|
||||||
|
bordered={false}
|
||||||
|
value={workShopValue}
|
||||||
|
suffixIcon={<Triangle />}
|
||||||
|
options={workShops}
|
||||||
|
onChange={factoryChange}
|
||||||
|
changeOnSelect
|
||||||
|
placeholder={'请选择产线'}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<ul className={homecss['equip_state_mark']}>
|
||||||
|
<li>
|
||||||
|
<span style={{ backgroundColor: '#28A3FF' }}></span>
|
||||||
|
生产
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span style={{ backgroundColor: '#FAAB0C' }}></span>
|
||||||
|
待产
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span style={{ backgroundColor: '#F5222D' }}></span>
|
||||||
|
停产
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className={homecss['equip_state_card']}>
|
||||||
|
<Spin tip="Loading..." spinning={loading}>
|
||||||
|
<Skeleton loading={loading} active>
|
||||||
|
<ProcessCard
|
||||||
|
data={opData}
|
||||||
|
rowKey={'code'}
|
||||||
|
workCenterCode={workShopValue}
|
||||||
|
Content={OperationCard}
|
||||||
|
/>
|
||||||
|
</Skeleton>
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={homecss['content']}>
|
||||||
|
<div className={homecss['content_row']}>
|
||||||
|
<div>
|
||||||
|
<span className={homecss['title']}>时间稼动率</span>
|
||||||
|
<div className={homecss['liquid_parent']}>
|
||||||
|
<div>
|
||||||
|
<WaterWave
|
||||||
|
color="#5487f3"
|
||||||
|
stroke="#5487f3"
|
||||||
|
percent={
|
||||||
|
timeRate['BY_TIME']
|
||||||
|
? timeRate['BY_TIME']
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="echart_title">本月</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<WaterWave
|
||||||
|
color="#f8ae3c"
|
||||||
|
stroke="#f8ae3c"
|
||||||
|
percent={
|
||||||
|
timeRate['BZ_TIME']
|
||||||
|
? timeRate['BZ_TIME']
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="echart_title">本周</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<WaterWave
|
||||||
|
color="#3ad18c"
|
||||||
|
stroke="#3ad18c"
|
||||||
|
percent={
|
||||||
|
timeRate['JR_TIME']
|
||||||
|
? timeRate['JR_TIME']
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="echart_title">今日</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className={homecss['title']}>性能稼动率</span>
|
||||||
|
<div className={homecss['progress_parent']}>
|
||||||
|
<div className={homecss['progress_child']}>
|
||||||
|
<span
|
||||||
|
className={homecss['progress_child_title']}
|
||||||
|
>
|
||||||
|
本月
|
||||||
|
</span>
|
||||||
|
<div className={homecss['progress_child_bar']}>
|
||||||
|
<ProgressBar
|
||||||
|
config={{
|
||||||
|
autoFit: true,
|
||||||
|
percent: timeRate['BY_PERFORMANCE']
|
||||||
|
? timeRate['BY_PERFORMANCE']
|
||||||
|
: 0,
|
||||||
|
barWidthRatio: 0.3,
|
||||||
|
color: ['#28A3FF', '#E4E4E4'],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className={homecss['progress_child_val']}>
|
||||||
|
{timeRate['BY_PERFORMANCE']
|
||||||
|
? parseFloat(
|
||||||
|
(
|
||||||
|
timeRate['BY_PERFORMANCE'] *
|
||||||
|
100
|
||||||
|
).toFixed(2)
|
||||||
|
) + '%'
|
||||||
|
: '0%'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={homecss['progress_child']}>
|
||||||
|
<span
|
||||||
|
className={homecss['progress_child_title']}
|
||||||
|
>
|
||||||
|
本周
|
||||||
|
</span>
|
||||||
|
<div className={homecss['progress_child_bar']}>
|
||||||
|
<ProgressBar
|
||||||
|
config={{
|
||||||
|
percent: timeRate['BZ_PERFORMANCE']
|
||||||
|
? timeRate['BZ_PERFORMANCE']
|
||||||
|
: 0,
|
||||||
|
barWidthRatio: 0.3,
|
||||||
|
color: ['#FFC760', '#E4E4E4'],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className={homecss['progress_child_val']}>
|
||||||
|
{timeRate['BZ_PERFORMANCE']
|
||||||
|
? parseFloat(
|
||||||
|
(
|
||||||
|
timeRate['BZ_PERFORMANCE'] *
|
||||||
|
100
|
||||||
|
).toFixed(2)
|
||||||
|
) + '%'
|
||||||
|
: '0%'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={homecss['progress_child']}>
|
||||||
|
<span
|
||||||
|
className={homecss['progress_child_title']}
|
||||||
|
>
|
||||||
|
今日
|
||||||
|
</span>
|
||||||
|
<div className={homecss['progress_child_bar']}>
|
||||||
|
<ProgressBar
|
||||||
|
config={{
|
||||||
|
percent: timeRate['JR_PERFORMANCE']
|
||||||
|
? timeRate['JR_PERFORMANCE']
|
||||||
|
: 0,
|
||||||
|
barWidthRatio: 0.3,
|
||||||
|
color: ['#17C082', '#E4E4E4'],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className={homecss['progress_child_val']}>
|
||||||
|
{timeRate['BZ_PERFORMANCE']
|
||||||
|
? parseFloat(
|
||||||
|
(
|
||||||
|
timeRate['JR_PERFORMANCE'] *
|
||||||
|
100
|
||||||
|
).toFixed(2)
|
||||||
|
) + '%'
|
||||||
|
: '0%'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={homecss['content_last_col']}>
|
||||||
|
<span className={homecss['title']}>合格率</span>
|
||||||
|
<div className={homecss['ringgraph_parent']}>
|
||||||
|
<div className={homecss['ringgraph_child']}>
|
||||||
|
<CircleProgress
|
||||||
|
percent={
|
||||||
|
timeRate['JR_GOODPRODUCT']
|
||||||
|
? timeRate['JR_GOODPRODUCT']
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={homecss['footer']}>
|
||||||
|
<div className={homecss['footer_row']}>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
className={homecss['title']}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => clickException('all')}
|
||||||
|
>
|
||||||
|
今日预警通知
|
||||||
|
</span>
|
||||||
|
<div className={homecss['footer_content']}>
|
||||||
|
<ul className={homecss['footer_statistics']}>
|
||||||
|
<li onClick={() => clickException('equip')}>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_title']
|
||||||
|
}
|
||||||
|
>
|
||||||
|
设备预警
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_val']
|
||||||
|
}
|
||||||
|
style={{ color: '#f5222d' }}
|
||||||
|
>
|
||||||
|
{abnlException['equip'].length}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => clickException('quality')}>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_title']
|
||||||
|
}
|
||||||
|
>
|
||||||
|
质量预警
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_val']
|
||||||
|
}
|
||||||
|
style={{ color: '#28A3FF' }}
|
||||||
|
>
|
||||||
|
{abnlException['quality'].length}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_last']
|
||||||
|
}
|
||||||
|
onClick={() => clickException('prodexec')}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_title']
|
||||||
|
}
|
||||||
|
>
|
||||||
|
生产预警
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
homecss['footer_statistics_val']
|
||||||
|
}
|
||||||
|
style={{ color: '#17C082' }}
|
||||||
|
>
|
||||||
|
{abnlException['prodexec'].length}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div className={homecss['footer_tips']}>
|
||||||
|
<List
|
||||||
|
itemLayout="horizontal"
|
||||||
|
split={false}
|
||||||
|
dataSource={currentExceptions}
|
||||||
|
renderItem={item => (
|
||||||
|
<List.Item
|
||||||
|
extra={
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
homecss[
|
||||||
|
'footer_tips_time'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{item.time}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<List.Item.Meta
|
||||||
|
avatar={
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
homecss[
|
||||||
|
'footer_tips_title'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
item.typeException ===
|
||||||
|
'equip'
|
||||||
|
? '#F5222D'
|
||||||
|
: item.typeException ===
|
||||||
|
'quality'
|
||||||
|
? '#28A3FF'
|
||||||
|
: '#17C082',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.typeException ===
|
||||||
|
'equip'
|
||||||
|
? '【设备】'
|
||||||
|
: item.typeException ===
|
||||||
|
'quality'
|
||||||
|
? '【质量】'
|
||||||
|
: '【生产】'}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
homecss[
|
||||||
|
'footer_tips_content'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{item.phenomenonName}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className={homecss['title']}>合格率走势图</span>
|
||||||
|
<div className={homecss['footer_qualified_rate']}>
|
||||||
|
<LineChart data={qualifiedRate} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const WaterWave = React.memo(props => {
|
||||||
|
const { color, stroke, percent } = props;
|
||||||
|
let config = {
|
||||||
|
autoFit: true,
|
||||||
|
liquidStyle: { fill: color },
|
||||||
|
percent: percent,
|
||||||
|
outline: {
|
||||||
|
border: 3,
|
||||||
|
distance: 1,
|
||||||
|
style: {
|
||||||
|
stroke: stroke,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wave: {
|
||||||
|
length: 80,
|
||||||
|
count: 3,
|
||||||
|
},
|
||||||
|
statistic: {
|
||||||
|
title: {
|
||||||
|
style: {
|
||||||
|
fontFamily: 'PingFangSC-Medium',
|
||||||
|
fontSize: '28px',
|
||||||
|
color: 'rgba(0,0,0,0.85)',
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: '26px',
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
formatter: data => {
|
||||||
|
return `${parseFloat((data.percent * 100).toFixed(1))}%`;
|
||||||
|
},
|
||||||
|
offsetX: 3,
|
||||||
|
offsetY: -20,
|
||||||
|
},
|
||||||
|
content: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return <Liquid {...config} />;
|
||||||
|
});
|
||||||
|
const CircleProgress = React.memo(props => {
|
||||||
|
const { percent } = props;
|
||||||
|
let config = {
|
||||||
|
autoFit: true,
|
||||||
|
percent: percent,
|
||||||
|
color: ['#28a3ff', '#f1f1f1'],
|
||||||
|
statistic: {
|
||||||
|
title: {
|
||||||
|
style: {
|
||||||
|
fontFamily: 'PingFangSC-Medium',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: 'rgba(0,0,0,0.85)',
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: '20px',
|
||||||
|
fontWeight: '400',
|
||||||
|
},
|
||||||
|
content: '合格率',
|
||||||
|
offsetX: 1,
|
||||||
|
offsetY: 35,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
style: {
|
||||||
|
fontFamily: 'PingFangSC-Medium',
|
||||||
|
fontSize: '24px',
|
||||||
|
color: 'rgba(0,0,0,0.85)',
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: '18px',
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
formatter: data => {
|
||||||
|
return `${data.percent * 100}%`;
|
||||||
|
},
|
||||||
|
offsetX: 3,
|
||||||
|
offsetY: -18,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return <RingProgress {...config} />;
|
||||||
|
});
|
||||||
|
const ProgressBar = React.memo(props => {
|
||||||
|
const { percent, barWidthRatio, color } = props.config;
|
||||||
|
let config = {
|
||||||
|
autoFit: true,
|
||||||
|
percent: percent,
|
||||||
|
barWidthRatio: barWidthRatio,
|
||||||
|
color: color,
|
||||||
|
};
|
||||||
|
return <Progress {...config} />;
|
||||||
|
});
|
||||||
|
const LineChart = React.memo(props => {
|
||||||
|
let { data = [] } = props;
|
||||||
|
const config = {
|
||||||
|
autoFit: true,
|
||||||
|
smooth: true,
|
||||||
|
color: '#2D99FF',
|
||||||
|
lineStyle: {
|
||||||
|
fill: '#cae5ff',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
line: {
|
||||||
|
style: {
|
||||||
|
stroke: '#bfbfbf',
|
||||||
|
lineWidth: 1,
|
||||||
|
opacity: 0.75,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
formatter: axisValue => {
|
||||||
|
if (axisValue > 0) {
|
||||||
|
return axisValue * 100 + '%';
|
||||||
|
}
|
||||||
|
return axisValue;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
xField: 'month',
|
||||||
|
yField: 'value',
|
||||||
|
point: {
|
||||||
|
size: 4,
|
||||||
|
shape: 'circle',
|
||||||
|
style: {
|
||||||
|
fill: '#2D99FF',
|
||||||
|
stroke: '#2D99FF',
|
||||||
|
lineWidth: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
formatter: datum => {
|
||||||
|
return { name: '合格率', value: datum.value * 100 + '%' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return <Line {...config} />;
|
||||||
|
});
|
||||||
|
const Triangle = React.memo(() => {
|
||||||
|
return (
|
||||||
|
<div className="triangle">
|
||||||
|
<style jsx>{`
|
||||||
|
.triangle {
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 5px solid #000000;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const ProcessCard = React.memo(props => {
|
||||||
|
const spacing = [24, 24];
|
||||||
|
|
||||||
|
const { Content, data, rowKey, workCenterCode } = props;
|
||||||
|
const [ruleLength] = useState(5);
|
||||||
|
|
||||||
|
const memoData = useMemo(() => {
|
||||||
|
let res = [];
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
let row = Math.ceil((index + 1) / ruleLength);
|
||||||
|
let rowIndex = index % ruleLength;
|
||||||
|
if (!res[row]) res[row] = [];
|
||||||
|
res[row][rowIndex] = item;
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}, [data, ruleLength]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="list-flex">
|
||||||
|
{memoData.map((row, rowIndex) => {
|
||||||
|
let reverse = rowIndex % 2 > 0;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`list-flex-row ${reverse ? '' : 'reverse'}`}
|
||||||
|
key={rowIndex}
|
||||||
|
>
|
||||||
|
{row.map(item => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="list-flex-item"
|
||||||
|
key={item[rowKey]}
|
||||||
|
>
|
||||||
|
<Content
|
||||||
|
workCenterCode={workCenterCode}
|
||||||
|
equipList={item.iotEquipmentVos || []}
|
||||||
|
title={`${item['code']}-${item['name']}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
.list-flex {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.list-flex-row {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -${spacing[0] / 2}px;
|
||||||
|
}
|
||||||
|
.list-flex-row.reverse {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
.list-flex-item {
|
||||||
|
width: ${100 / ruleLength}%;
|
||||||
|
padding: 0 ${spacing[0] / 2}px;
|
||||||
|
margin-bottom: ${spacing[1]}px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.list-flex-row:last-child .list-flex-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.list-flex-item::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
// 右箭头
|
||||||
|
.list-flex-row:not(.reverse) .list-flex-item::after {
|
||||||
|
width: 16px;
|
||||||
|
height: 14px;
|
||||||
|
right: 0;
|
||||||
|
left: unset;
|
||||||
|
bottom: unset;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
background: url('/img/jt.png') center center no-repeat;
|
||||||
|
}
|
||||||
|
// 左箭头
|
||||||
|
.list-flex-row.reverse .list-flex-item::after {
|
||||||
|
width: 16px;
|
||||||
|
height: 14px;
|
||||||
|
left: 0;
|
||||||
|
right: unset;
|
||||||
|
top: 50%;
|
||||||
|
bottom: unset;
|
||||||
|
transform: translate(-50%, -50%) rotate(180deg);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
background: url('/img/jt.png') center center no-repeat;
|
||||||
|
}
|
||||||
|
// 下箭头
|
||||||
|
.list-flex-row .list-flex-item:last-child::after {
|
||||||
|
width: 16px;
|
||||||
|
height: 14px;
|
||||||
|
left: unset;
|
||||||
|
right: 50%;
|
||||||
|
top: unset;
|
||||||
|
bottom: -${(spacing[1] - 14) / 2}px;
|
||||||
|
top: unset;
|
||||||
|
transform: translate(-50%, 100%) rotate(90deg);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
background: url('/img/jt.png') center center no-repeat;
|
||||||
|
}
|
||||||
|
// 无箭头
|
||||||
|
.list-flex-row:last-child .list-flex-item:last-child::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const OperationCard = React.memo(props => {
|
||||||
|
let { equipList = [], title = '', workCenterCode = '' } = props;
|
||||||
|
return (
|
||||||
|
<div className="operation_card">
|
||||||
|
<div className="operation_card_title">
|
||||||
|
<span>{title}</span>
|
||||||
|
</div>
|
||||||
|
<div className="operation_card_content">
|
||||||
|
<Carousel autoplay dots={false} autoplaySpeed={10000}>
|
||||||
|
{equipList.map(item => {
|
||||||
|
return (
|
||||||
|
<div key={`${item.equipCode}-${item.equipName}`}>
|
||||||
|
<EquipCard
|
||||||
|
config={{
|
||||||
|
status: parseInt(item.runningstatus),
|
||||||
|
equipCode: item.equipCode,
|
||||||
|
equipName: item.equipName,
|
||||||
|
workCenterCode: workCenterCode,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
<style jsx>{`
|
||||||
|
.operation_card {
|
||||||
|
width: 100%;
|
||||||
|
height: 116px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.operation_card_title {
|
||||||
|
width: 100%;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
.operation_card_title > span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 0 0 16px;
|
||||||
|
fontfamily: PingFangSC-Semibold;
|
||||||
|
fontsize: 14px;
|
||||||
|
color: #000000;
|
||||||
|
fontweight: 600;
|
||||||
|
}
|
||||||
|
.operation_card_content {
|
||||||
|
width: 100%;
|
||||||
|
height: 81px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const EquipCard = React.memo(props => {
|
||||||
|
let { status, equipCode, equipName, workCenterCode } = props.config;
|
||||||
|
let title = '';
|
||||||
|
let background = '';
|
||||||
|
let img = '';
|
||||||
|
if (status === 1) {
|
||||||
|
title = '待产中';
|
||||||
|
background = 'linear-gradient(270deg, #FFDB93 0%, #FAAB0C 100%)';
|
||||||
|
img = '/img/dc.png';
|
||||||
|
} else if (status === 2) {
|
||||||
|
title = '生产中';
|
||||||
|
background = 'linear-gradient(270deg, #7AC7FF 0%, #28A3FF 100%)';
|
||||||
|
img = '/img/sz.png';
|
||||||
|
} else if (status === 3) {
|
||||||
|
title = '停产中';
|
||||||
|
background = 'linear-gradient(270deg, #FF735F 0%, #F5222D 100%)';
|
||||||
|
img = '/img/tc.png';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
width: '100%',
|
||||||
|
height: '81px',
|
||||||
|
backgroundImage: background,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
cursor: 'pointer',
|
||||||
|
borderRadius: '0px 0px 4px 4px',
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
path: `/mdgeneric/neusoft_web/runtime/f4c2f3083ec84daca1b6bea7985194cb?workCenterCode=${workCenterCode[0]}`,
|
||||||
|
},
|
||||||
|
'*'
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
localStorage.setItem('workCenterCode', workCenterCode[0]);
|
||||||
|
console.log(workCenterCode[0], '+++++');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
height: '81px',
|
||||||
|
marginLeft: '4%',
|
||||||
|
marginRight: '6%',
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: '75px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
fontFamily: 'PingFangSC-Semibold',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontWeight: '600',
|
||||||
|
left: '25%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
<img
|
||||||
|
src={img}
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
lineHeight: 'initial',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minWidth: '25%',
|
||||||
|
height: '81px',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
padding: '16px 0 6px 0',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
minWidth: '25%',
|
||||||
|
fontFamily: 'PingFangSC-Medium',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontWeight: '500',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{equipName}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
minWidth: '25%',
|
||||||
|
fontFamily: 'PingFangSC-Medium',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontWeight: '500',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{equipCode}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
export default HomePage;
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"productBatchNo":"HRS10xxxxxxxxx",
|
||||||
|
"imgItemList":[
|
||||||
|
{
|
||||||
|
"operationName":"细磨",
|
||||||
|
"templateName":"L7细磨检验模板",
|
||||||
|
"imgShotTime": "2023-02-21 14:05:00",
|
||||||
|
"imgPath":[
|
||||||
|
"/quality/quality/simpling/downloadByFileCode?bmFileInfoGid=1231232432",
|
||||||
|
"/quality/quality/simpling/downloadByFileCode?bmFileInfoGid=1543543435"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { useRouter, useCallback } from "next/router";
|
||||||
|
import "antd/dist/antd.css";
|
||||||
|
|
||||||
|
import { Image } from "antd";
|
||||||
|
|
||||||
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
getShotImgByProductBatchNo,
|
||||||
|
getShotImg,
|
||||||
|
} from "../../../reportUtils/getQueryData";
|
||||||
|
const { config: { mesUrl } = {} } = global;
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
const ListItem = React.memo((props) => {
|
||||||
|
const { itemData } = props;
|
||||||
|
return (
|
||||||
|
<div className="border-t-[1px] border-[#D2D2D2] px-4 pb-2">
|
||||||
|
<div className=" flex flex-row space-x-7 py-2">
|
||||||
|
<div>
|
||||||
|
<label>工序:</label>
|
||||||
|
<label>{itemData.operationName}</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>质检单模板名称:</label>
|
||||||
|
<label>{itemData.templateName}</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>拍照时间:</label>
|
||||||
|
<label>{itemData.imgShotTime}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row space-x-7 ">
|
||||||
|
<Imag imgurls={itemData.imgPath}></Imag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
function Imag(props) {
|
||||||
|
const { imgurls } = props;
|
||||||
|
const [imglist, setImgList] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
setImgList([]);
|
||||||
|
imgurls.map((imgsrc) => {
|
||||||
|
getShotImg(mesUrl + imgsrc).then((rep) => {
|
||||||
|
setImgList((prev) => {
|
||||||
|
return [...prev, URL.createObjectURL(rep)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return imglist.map((imgsrc) => {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
width={200}
|
||||||
|
height={240}
|
||||||
|
// className=" w-24 h-32 border"
|
||||||
|
src={imgsrc ?? "../img/noImg.png"}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const List = React.memo((props) => {
|
||||||
|
const { dataSource } = props;
|
||||||
|
return (
|
||||||
|
<div className=" ">
|
||||||
|
{dataSource?.map((item) => {
|
||||||
|
return <ListItem itemData={item}></ListItem>;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const Header = React.memo((props) => {
|
||||||
|
const { productBatchNo } = props;
|
||||||
|
return (
|
||||||
|
<div className=" py-6 px-4">
|
||||||
|
<label className=" ">批次生产单:</label>
|
||||||
|
<label>{productBatchNo}</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const getQueryVariable = (name) => {
|
||||||
|
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
||||||
|
var r = window.location.search.substr(1).match(reg);
|
||||||
|
if (r != null) return decodeURI(r[2]);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PicView() {
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
const [productBatchNo, setProductBatchNo] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
setProductBatchNo(getQueryVariable("productBatchNo"));
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
getShotImgByProductBatchNo(productBatchNo).then((json) => {
|
||||||
|
setData(json);
|
||||||
|
});
|
||||||
|
}, [productBatchNo]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="font-bold">
|
||||||
|
|
||||||
|
<Header productBatchNo={data.productBatchNo}></Header>
|
||||||
|
<List dataSource={data.imgItemList}></List>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,335 @@
|
||||||
|
import React, { useRef, useEffect, useState, useMemo } from "react";
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
const Mix = dynamic(() => import('@ant-design/plots').then(({ Mix }) => Mix),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
// import { Mix } from '@ant-design/charts'
|
||||||
|
|
||||||
|
import _, { max } from "lodash";
|
||||||
|
const symbol= function symbol(x, y, r) {
|
||||||
|
return [
|
||||||
|
["M", x - r, y],
|
||||||
|
["L", x + r, y],
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const DemoDualAxes = ({ dataSource, UCL, LCL, CL }) => {
|
||||||
|
const [chartDatanew,setchartDatanew] = useState([]);
|
||||||
|
const [xAxisarr,setxAxisarr] = useState([]);
|
||||||
|
const data = dataSource;
|
||||||
|
const maxnnn = () => {
|
||||||
|
let max = Math.max.apply(
|
||||||
|
null,
|
||||||
|
data?.map((x) => x.value)
|
||||||
|
);
|
||||||
|
let tmparr = [];
|
||||||
|
tmparr.push(UCL, LCL, CL);
|
||||||
|
if (!isNaN(max)) {
|
||||||
|
tmparr.push(max);
|
||||||
|
}
|
||||||
|
console.log(tmparr);
|
||||||
|
let maxnew = Math.max.apply(null, tmparr);
|
||||||
|
|
||||||
|
return maxnew;
|
||||||
|
};
|
||||||
|
function closestTo(arr, target) {
|
||||||
|
return arr.reduce(function(prev, curr) {
|
||||||
|
return (Math.abs(curr - target) < Math.abs(prev - target) ? curr : prev);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let maxn = useMemo(()=>{return maxnnn},[maxnnn])
|
||||||
|
let category = ["y", "curve", "usl", "cl", "lsl"];
|
||||||
|
let categoryNames = ["频数", "正态曲线", "USL", "规格中线", "LSL"];
|
||||||
|
// let chartData = [];
|
||||||
|
// if (data && data.length > 0)
|
||||||
|
// data.map((item) => {
|
||||||
|
// Object.keys(item).map((key) => {
|
||||||
|
// if (category.includes(key)) {
|
||||||
|
// chartData.push({
|
||||||
|
// x: item.x,
|
||||||
|
// category: key,
|
||||||
|
// categoryName: categoryNames[category.indexOf(key)],
|
||||||
|
// value: item[key],
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// let tmplist = [],
|
||||||
|
// chartDatanew = [];
|
||||||
|
// chartDatanew = chartData
|
||||||
|
// .map((x) => {
|
||||||
|
// if (x.category === "usl" || x.category === "lsl" || x.category === "cl") {
|
||||||
|
// let val = x.value;
|
||||||
|
// if (tmplist.indexOf(val) === -1) {
|
||||||
|
// x.x = val;
|
||||||
|
// x.value = maxn;
|
||||||
|
// tmplist.push(val);
|
||||||
|
// } else {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return x;
|
||||||
|
// })
|
||||||
|
// .filter((x) => x !== null);
|
||||||
|
useEffect(() => {
|
||||||
|
// data.push({ x: maxnnn(), y: 0 });
|
||||||
|
setchartDatanew(data)
|
||||||
|
setxAxisarr(data?.filter(x=>!isNaN(x.x)).map(x=>x.x))
|
||||||
|
}, [data]);
|
||||||
|
const config = {
|
||||||
|
tooltip: {
|
||||||
|
shared: true,
|
||||||
|
},
|
||||||
|
syncViewPadding: true,
|
||||||
|
plots: [
|
||||||
|
// {
|
||||||
|
// type: 'column',
|
||||||
|
// options:{
|
||||||
|
// data: chartData,
|
||||||
|
// xField: 'x',
|
||||||
|
// seriesField: 'category',
|
||||||
|
// yField: 'value',
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
type: "column",
|
||||||
|
options: {
|
||||||
|
data: chartDatanew?.map((c) => {
|
||||||
|
return {
|
||||||
|
x: c.x,
|
||||||
|
value: c.y,
|
||||||
|
categoryName: "频数",
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
xField: "x",
|
||||||
|
minColumnWidth: 20,
|
||||||
|
maxColumnWidth: 20,
|
||||||
|
seriesField: "categoryName",
|
||||||
|
color: "#7B9DCE",
|
||||||
|
yField: "value",
|
||||||
|
yAxis: {
|
||||||
|
line: {
|
||||||
|
style: {
|
||||||
|
stroke: "#aaa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
nice: true,
|
||||||
|
type: "linear", // 数据呈连续性
|
||||||
|
|
||||||
|
tickLine: {
|
||||||
|
length: 0,
|
||||||
|
alignTick:true,
|
||||||
|
style: {
|
||||||
|
lineWidth: 2,
|
||||||
|
stroke: "red",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
style: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
formatter: (text,item,index)=>{
|
||||||
|
let newtext = text;
|
||||||
|
try {
|
||||||
|
newtext = closestTo(xAxisarr, text)
|
||||||
|
} catch (error) {
|
||||||
|
newtext = text;
|
||||||
|
}
|
||||||
|
return newtext
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
annotations: [
|
||||||
|
{
|
||||||
|
top: true,
|
||||||
|
type: "line",
|
||||||
|
start: [UCL, "min"],
|
||||||
|
end: [UCL, "max"],
|
||||||
|
style: {
|
||||||
|
stroke: "#FF0000",
|
||||||
|
fill: "#FF0000",
|
||||||
|
// lineDash: [6, 3],
|
||||||
|
lineWidth:2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
top: true,
|
||||||
|
type: "line",
|
||||||
|
start: [LCL, "min"],
|
||||||
|
end: [LCL, "max"],
|
||||||
|
style: {
|
||||||
|
stroke: "#FF0000",
|
||||||
|
fill: "#FF0000",
|
||||||
|
// lineDash: [6, 3],
|
||||||
|
lineWidth:2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
top: true,
|
||||||
|
type: "line",
|
||||||
|
start: [CL, "min"],
|
||||||
|
end: [CL, "max"],
|
||||||
|
style: {
|
||||||
|
stroke: "#7D679F",
|
||||||
|
fill: "#7D679F",
|
||||||
|
lineDash: [6, 3],
|
||||||
|
lineWidth:2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
options: {
|
||||||
|
data: chartDatanew?.map((c) => {
|
||||||
|
return {
|
||||||
|
x: c.x,
|
||||||
|
value: c.curve,
|
||||||
|
categoryName: "正态曲线",
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
xField: "x",
|
||||||
|
smooth: true,
|
||||||
|
yField: "value",
|
||||||
|
seriesField: "categoryName",
|
||||||
|
color: "#B45A53",
|
||||||
|
xAxis: false,
|
||||||
|
yAxis: {
|
||||||
|
line: null,
|
||||||
|
grid: null,
|
||||||
|
position: "right",
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// type: "column",
|
||||||
|
// options: {
|
||||||
|
// data: chartDatanew.filter(
|
||||||
|
// (x) =>
|
||||||
|
// x.category === "usl" ||
|
||||||
|
// x.category === "cl" ||
|
||||||
|
// x.category === "lsl"
|
||||||
|
// ),
|
||||||
|
// minColumnWidth: 1,
|
||||||
|
// maxColumnWidth: 2,
|
||||||
|
// xField: "x",
|
||||||
|
// // color: "#EA3627",
|
||||||
|
// color: [ "#7D679F", "#F5C242","#EA3627"],
|
||||||
|
// yField: "value",
|
||||||
|
// seriesField: "categoryName",
|
||||||
|
// xAxis: false,
|
||||||
|
// yAxis: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: "column",
|
||||||
|
// options: {
|
||||||
|
// data: chartDatanew.filter((x) => x.category === "cl"),
|
||||||
|
// minColumnWidth: 1,
|
||||||
|
// maxColumnWidth: 2,
|
||||||
|
// xField: "x",
|
||||||
|
// color: "#7D679F",
|
||||||
|
// yField: "value",
|
||||||
|
// seriesField: "categoryName",
|
||||||
|
// xAxis: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: "column",
|
||||||
|
// options: {
|
||||||
|
// data: chartDatanew.filter((x) => x.category === "lsl"),
|
||||||
|
// minColumnWidth: 1,
|
||||||
|
// maxColumnWidth: 2,
|
||||||
|
// xField: "x",
|
||||||
|
// color: "#F5C242",
|
||||||
|
// yField: "value",
|
||||||
|
// seriesField: "categoryName",
|
||||||
|
// xAxis: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
legend: {
|
||||||
|
position: "bottom-left",
|
||||||
|
layout: "horizontal",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
value: "y",
|
||||||
|
name: "频数",
|
||||||
|
marker: {
|
||||||
|
symbol: "square",
|
||||||
|
style: {
|
||||||
|
stroke: "#5A80B8",
|
||||||
|
// lineDash: [4, 2],
|
||||||
|
fill: "#5A80B8",
|
||||||
|
r: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "curve",
|
||||||
|
name: "正态曲线",
|
||||||
|
marker: {
|
||||||
|
symbol: symbol,
|
||||||
|
style: {
|
||||||
|
lineWidth: 2,
|
||||||
|
stroke: "#B45A53",
|
||||||
|
// lineDash: [4, 2],
|
||||||
|
fill: "#B45A53",
|
||||||
|
r: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "usl",
|
||||||
|
name: "USL",
|
||||||
|
marker: {
|
||||||
|
symbol: symbol,
|
||||||
|
style: {
|
||||||
|
lineWidth: 2,
|
||||||
|
stroke: "#FF0000",
|
||||||
|
lineDash: [4, 2],
|
||||||
|
fill: "#FF0000",
|
||||||
|
r: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "cl",
|
||||||
|
name: "规格中线",
|
||||||
|
marker: {
|
||||||
|
symbol: symbol,
|
||||||
|
style: {
|
||||||
|
lineWidth: 2,
|
||||||
|
stroke: "#7D679F",
|
||||||
|
lineDash: [4, 2],
|
||||||
|
fill: "#7D679F",
|
||||||
|
r: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "lsl",
|
||||||
|
name: "LSL",
|
||||||
|
marker: {
|
||||||
|
symbol: symbol,
|
||||||
|
style: {
|
||||||
|
lineWidth: 2,
|
||||||
|
stroke: "#FF0000",
|
||||||
|
lineDash: [4, 2],
|
||||||
|
fill: "#FF0000",
|
||||||
|
r: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return <Mix {...config} height={250}/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DemoDualAxes;
|
|
@ -0,0 +1,216 @@
|
||||||
|
import React, {
|
||||||
|
useRef,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useMemo,
|
||||||
|
useCallback,
|
||||||
|
} from "react";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
const Line = dynamic(
|
||||||
|
() => import("@ant-design/charts").then(({ Line }) => Line),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// import { Line } from '@ant-design/charts'
|
||||||
|
const DemoLine = ({
|
||||||
|
dataSource,
|
||||||
|
UCL,
|
||||||
|
LCL,
|
||||||
|
AVER,
|
||||||
|
legend,
|
||||||
|
USL,
|
||||||
|
LSL,
|
||||||
|
maxz,
|
||||||
|
minz,
|
||||||
|
}) => {
|
||||||
|
const data = dataSource;
|
||||||
|
const line = useRef();
|
||||||
|
const name = data?.map((x) => x.name);
|
||||||
|
let isX = false;
|
||||||
|
if (USL && LSL) {
|
||||||
|
isX = true;
|
||||||
|
}
|
||||||
|
let maxn = useMemo(() => {
|
||||||
|
let max = Math.max.apply(
|
||||||
|
null,
|
||||||
|
data?.map((x) => {
|
||||||
|
if (x?.value) {
|
||||||
|
return x.value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
let tmparr = [];
|
||||||
|
tmparr.push(UCL, LCL, AVER);
|
||||||
|
if (isX) {
|
||||||
|
tmparr.push(USL, LSL);
|
||||||
|
}
|
||||||
|
if (!isNaN(max)) {
|
||||||
|
tmparr.push(max);
|
||||||
|
}
|
||||||
|
let maxnew = Math.max.apply(null, tmparr);
|
||||||
|
|
||||||
|
return maxnew;
|
||||||
|
}, [data]);
|
||||||
|
const config = {
|
||||||
|
data,
|
||||||
|
slider: {},
|
||||||
|
color: "#8AA1C9",
|
||||||
|
xField: "x",
|
||||||
|
yField: "value",
|
||||||
|
xAxis: {
|
||||||
|
animation: false,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
tickCount: 7,
|
||||||
|
nice: true,
|
||||||
|
animation: false,
|
||||||
|
max: maxz,
|
||||||
|
min: minz,
|
||||||
|
line: {
|
||||||
|
style: {
|
||||||
|
stroke: "#aaa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tickLine: {},
|
||||||
|
},
|
||||||
|
point: {
|
||||||
|
style: {
|
||||||
|
r: 2.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
smooth: true,
|
||||||
|
tooltip: {
|
||||||
|
showCrosshairs: true,
|
||||||
|
shared: true,
|
||||||
|
},
|
||||||
|
seriesField: "name",
|
||||||
|
annotations: [
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// position: ["min", UCL],
|
||||||
|
// content: "UCL",
|
||||||
|
// offsetY: -4,
|
||||||
|
// style: {
|
||||||
|
// textBaseline: "bottom",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// position: ["min", LCL],
|
||||||
|
// content: "LCL",
|
||||||
|
// offsetY: -4,
|
||||||
|
// style: {
|
||||||
|
// textBaseline: "bottom",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// position: ["min", AVER],
|
||||||
|
// content: "AVER",
|
||||||
|
// offsetY: -4,
|
||||||
|
// style: {
|
||||||
|
// textBaseline: "bottom",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
start: ["min", LCL],
|
||||||
|
end: ["max", LCL],
|
||||||
|
style: {
|
||||||
|
stroke: "#FF0000",
|
||||||
|
fill: "#FF0000",
|
||||||
|
// lineDash: [6, 3],
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
start: ["min", UCL],
|
||||||
|
end: ["max", UCL],
|
||||||
|
style: {
|
||||||
|
stroke: "#FF0000",
|
||||||
|
fill: "#FF0000",
|
||||||
|
// lineDash: [6, 3],
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
start: ["min", AVER],
|
||||||
|
end: ["max", AVER],
|
||||||
|
style: {
|
||||||
|
stroke: "#80FC5E",
|
||||||
|
fill: "#80FC5E",
|
||||||
|
lineDash: [6, 3],
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
legend: legend,
|
||||||
|
};
|
||||||
|
|
||||||
|
dataSource
|
||||||
|
?.filter((a) => a?.controlRefList?.length > 0)
|
||||||
|
.forEach((b) => {
|
||||||
|
const _controlPerf = "";
|
||||||
|
if (legend.items[0].name === "极差值") {
|
||||||
|
if (b.controlRefList?.indexOf("I") > -1) {
|
||||||
|
_controlPerf = "I";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_controlPerf = b.controlRefList?.filter((z) => z !== "I").join(",");
|
||||||
|
}
|
||||||
|
if (_controlPerf !== "") {
|
||||||
|
const tmplateann = {
|
||||||
|
type: "text",
|
||||||
|
content: _controlPerf,
|
||||||
|
position: (xScale, yScale) => {
|
||||||
|
return [
|
||||||
|
`${xScale.scale(b.x) * 100}%`,
|
||||||
|
b.value>0? `${(1 - yScale.value.scale(b.value)) * 50}%` : `${(1 - yScale.value.scale(b.value)) * 94}%`,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
textAlign: "center",
|
||||||
|
fill: "rgba(0,0,0,0.85)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
config.annotations.push(tmplateann);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let usllsl = [
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
start: ["min", USL],
|
||||||
|
end: ["max", USL],
|
||||||
|
style: {
|
||||||
|
stroke: "#FF0000",
|
||||||
|
fill: "#FF0000",
|
||||||
|
lineDash: [6, 3],
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
start: ["min", LSL],
|
||||||
|
end: ["max", LSL],
|
||||||
|
style: {
|
||||||
|
stroke: "#FF0000",
|
||||||
|
fill: "#FF0000",
|
||||||
|
lineDash: [6, 3],
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (isX) {
|
||||||
|
usllsl.forEach((item) => {
|
||||||
|
config.annotations.push(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return <Line {...config} height={250} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DemoLine;
|
|
@ -0,0 +1,438 @@
|
||||||
|
import React, { useRef, useEffect, useState, useCallback } from "react";
|
||||||
|
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 Table from "../../../components/screen/Table";
|
||||||
|
import TableProgress from "../../../components/screen/TableProgress";
|
||||||
|
import io from "../../../utils/socket.io.js";
|
||||||
|
const gutter = 20;
|
||||||
|
const bottom = 20;
|
||||||
|
const tableToNextPage = 6000;
|
||||||
|
|
||||||
|
const polarBarOpt = {
|
||||||
|
title: {
|
||||||
|
text: "计划总数",
|
||||||
|
left: "center",
|
||||||
|
bottom: 24,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
radiusAxis: {
|
||||||
|
show: false,
|
||||||
|
type: "category",
|
||||||
|
data: ["%"],
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
series: {
|
||||||
|
type: "gauge",
|
||||||
|
center: ["50%", "40%"],
|
||||||
|
radius: "70%",
|
||||||
|
startAngle: 90,
|
||||||
|
endAngle: -270,
|
||||||
|
pointer: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
show: true,
|
||||||
|
overlap: false,
|
||||||
|
roundCap: false,
|
||||||
|
clip: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
width: 20,
|
||||||
|
color: [[1, "rgba(40, 163, 255, 0.2)"]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
name: "生产总数",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: {
|
||||||
|
offsetCenter: ["0%", "20"],
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
show: true,
|
||||||
|
offsetCenter: ["0%", "-20"],
|
||||||
|
width: "auto",
|
||||||
|
height: 32,
|
||||||
|
fontSize: 32,
|
||||||
|
color: "#00E1FA",
|
||||||
|
borderColor: "auto",
|
||||||
|
borderWidth: 0,
|
||||||
|
formatter: "{value}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const setNumOpt = (total, done, title) => {
|
||||||
|
let res = Object.assign({}, polarBarOpt);
|
||||||
|
if (title) res.title.text = `${title}:${total}`;
|
||||||
|
let percent = ((100 * done) / (total || 1)).toFixed(2);
|
||||||
|
res.series.data[0].value = percent;
|
||||||
|
res.series.detail.formatter = () => done;
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
const setOkNumOpt = (total, done, title) => {
|
||||||
|
let res = Object.assign({}, polarBarOpt);
|
||||||
|
let percent = ((100 * done) / (total || 1)).toFixed(2);
|
||||||
|
if (title) res.title.text = `${title}:${percent}%`;
|
||||||
|
res.series.data[0].value = percent;
|
||||||
|
res.series.data[0].name = "实际总量";
|
||||||
|
res.series.detail.formatter = () => done;
|
||||||
|
res.series.detail.color = "#6FE621";
|
||||||
|
res.series.progress.itemStyle.color = "#6FE621";
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const barOpt = {
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
data: ["生产数量", "合格数量"],
|
||||||
|
top: 10,
|
||||||
|
right: 10,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "shadow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
axisTick: {
|
||||||
|
lineStyle: { opacity: 0.3 },
|
||||||
|
alignWithLabel: true,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: { opacity: 0.3 },
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
axisTick: {
|
||||||
|
lineStyle: { opacity: 0.3 },
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: { opacity: 0.3 },
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
bottom: 40,
|
||||||
|
top: 60,
|
||||||
|
left: 50,
|
||||||
|
},
|
||||||
|
barWidth: 20,
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "生产数量",
|
||||||
|
type: "bar",
|
||||||
|
barGap: -1,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "合格数量",
|
||||||
|
itemStyle: {
|
||||||
|
color: "#6FE621",
|
||||||
|
},
|
||||||
|
type: "bar",
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const setBarOpt = ({ product = {}, qualify = {} }) => {
|
||||||
|
let res = Object.assign({}, barOpt);
|
||||||
|
for (let n = 0; n < 24; n++) {
|
||||||
|
res.xAxis.data.push(`${n}:00`.padStart(5, "0"));
|
||||||
|
}
|
||||||
|
for (let index in product) {
|
||||||
|
res.series[0].data[parseInt(index)] = product[index];
|
||||||
|
}
|
||||||
|
for (let index in qualify) {
|
||||||
|
res.series[1].data[parseInt(index)] = qualify[index];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const productTableColumns = [
|
||||||
|
{ title: "客户编码", code: "vendorCode" },
|
||||||
|
{ title: "客户名称", code: "vendorName" },
|
||||||
|
{ title: "销售名称", code: "userName" },
|
||||||
|
{ title: "客户分类", code: "customType" },
|
||||||
|
{ title: "产品型号", code: "productMode" },
|
||||||
|
{ title: "计划发货日期", code: "planDeliveryTime" },
|
||||||
|
{ title: "计划发货数量", code: "planDeliveryQty" },
|
||||||
|
{ title: "实际发货数量", code: "deliveryQty" },
|
||||||
|
{
|
||||||
|
title: "发货率",
|
||||||
|
code: "deliveryRateNew",
|
||||||
|
render: (value, config, rowData) => {
|
||||||
|
console.log(config, rowData);
|
||||||
|
return <TableProgress percent={value} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// {title: "生产状态", code: "code",
|
||||||
|
// value: val => {
|
||||||
|
// if(val === '1') return "生产中";
|
||||||
|
// if(val === '2') return "中断";
|
||||||
|
// if(val === '3') return "转产";
|
||||||
|
// },
|
||||||
|
// color: val => {
|
||||||
|
// if(val === '1') return "#6FE621";
|
||||||
|
// if(val === '2') return "#28A3FF";
|
||||||
|
// if(val === '3') return "#F82D22";
|
||||||
|
// },
|
||||||
|
// render: (text, config, rowData) => {
|
||||||
|
// const {value, color} = config;
|
||||||
|
// let resColor = color(text);
|
||||||
|
// let resText = value(text);
|
||||||
|
// return (
|
||||||
|
// <div style={{color: resColor}}>
|
||||||
|
// <span style={{
|
||||||
|
// background: resColor,
|
||||||
|
// width: '8px',
|
||||||
|
// height: '8px',
|
||||||
|
// display: 'inline-block',
|
||||||
|
// borderRadius: '50%',
|
||||||
|
// verticalAlign: 'middle',
|
||||||
|
// }}></span>
|
||||||
|
// <span style={{
|
||||||
|
// verticalAlign: 'middle',
|
||||||
|
// marginLeft: '6px',
|
||||||
|
// }}>{resText}</span>
|
||||||
|
// </div>
|
||||||
|
// )
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {title: "产品编码", code: "code"},
|
||||||
|
// {title: "产品名称", code: "code"},
|
||||||
|
// {title: "工单编号", code: "code"},
|
||||||
|
// {title: "生产数量", code: "code"},
|
||||||
|
// {title: "计划数量", code: "code"},
|
||||||
|
// {title: "生产进度", code: "code",
|
||||||
|
// render: (value, config, rowData) => {
|
||||||
|
// console.log(config, rowData)
|
||||||
|
// return <TableProgress percent={0.31}/>
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {title: "不良品数量", code: "code"},
|
||||||
|
// {title: "不良品率", code: "code"},
|
||||||
|
];
|
||||||
|
|
||||||
|
const productTableColumnNew = [
|
||||||
|
{ title: "产品类型", code: "materialTypeName" },
|
||||||
|
{ title: "产品型号", code: "productLevel" },
|
||||||
|
{ title: "产线信息", code: "workCenterName" },
|
||||||
|
{ title: "计划产出日期", code: "planOutputDate" },
|
||||||
|
{ title: "计划产量", code: "planNum" },
|
||||||
|
{ title: "实际产量", code: "actualNum" },
|
||||||
|
{
|
||||||
|
title: "达成率",
|
||||||
|
code: "fulfillRateNew",
|
||||||
|
render: (value, config, rowData) => {
|
||||||
|
console.log(config, rowData);
|
||||||
|
return <TableProgress percent={value} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function Comprehensive(props) {
|
||||||
|
const { config: { ioSocketUrl, comprehensiveConfig = {} } = {} } = global;
|
||||||
|
const okNumRefNew = useRef();
|
||||||
|
const okNumRef = useRef();
|
||||||
|
const productTableRefNew = useRef();
|
||||||
|
const productTableRef = useRef();
|
||||||
|
const socket = useRef();
|
||||||
|
let date = "";
|
||||||
|
let query = {
|
||||||
|
screenType: "ComprehensiveType",
|
||||||
|
};
|
||||||
|
if (date) query.date = date;
|
||||||
|
else {
|
||||||
|
query.date = new Date().toJSON().split("T").join(" ").substr(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = () =>
|
||||||
|
socket.current.emit("timePerformanceGoodProduct", "channel", { query });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
getData();
|
||||||
|
}, 300000);
|
||||||
|
const setTableNextPage = useCallback(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
productTableRef.current.nextPage();
|
||||||
|
setTableNextPage();
|
||||||
|
}, tableToNextPage);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setTableNextPageNew = useCallback(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
productTableRefNew.current.nextPage();
|
||||||
|
setTableNextPageNew();
|
||||||
|
}, tableToNextPage);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.current = io.connect(ioSocketUrl, {transports:['websocket']});
|
||||||
|
|
||||||
|
socket.current.on("connect", function () {
|
||||||
|
console.log("msg页面连接成功!");
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.current.on("timePerformanceGoodProduct", function (data) {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
console.log(data, "data");
|
||||||
|
okNumRef.current.setOption(
|
||||||
|
setOkNumOpt(
|
||||||
|
data.data.deliveryRates.planQty,
|
||||||
|
data.data.deliveryRates.qty,
|
||||||
|
"发货总体达成率"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
okNumRefNew.current.setOption(
|
||||||
|
setOkNumOpt(
|
||||||
|
data.data.planRates.planOutQty,
|
||||||
|
data.data.planRates.OutQty,
|
||||||
|
"计划总体达成率"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
productTableRefNew.current.addData(data.data.planOutputList);
|
||||||
|
productTableRef.current.addData(data.data.deliveryPlanList);
|
||||||
|
});
|
||||||
|
|
||||||
|
//planNumRef.current.setOption(setNumOpt(500000, 212923, "计划总数"));
|
||||||
|
|
||||||
|
// productNumRef.current.setOption(setBarOpt({
|
||||||
|
// product: {
|
||||||
|
// "0": 10,
|
||||||
|
// "1": 40,
|
||||||
|
// "5": 30,
|
||||||
|
// "6": 40,
|
||||||
|
// },
|
||||||
|
// qualify: {
|
||||||
|
// "0": 5,
|
||||||
|
// "1": 4,
|
||||||
|
// "5": 3,
|
||||||
|
// },
|
||||||
|
// }));
|
||||||
|
|
||||||
|
setTableNextPageNew();
|
||||||
|
setTableNextPage();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="screen-container">
|
||||||
|
<Header title={"综合看板"} />
|
||||||
|
<div className="screen-container-content">
|
||||||
|
<Row className="height-50" gutter={gutter} bottom={bottom}>
|
||||||
|
<Row.Col span={18}>
|
||||||
|
<Card
|
||||||
|
title="发货达成率"
|
||||||
|
titleSpace={true}
|
||||||
|
padding={`15px ${gutter}px`}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
ref={productTableRef}
|
||||||
|
mainKey="code"
|
||||||
|
columns={productTableColumns}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
{/*<Row.Col span={14}>*/}
|
||||||
|
{/* <Card title="今日生产统计" overVisible={true}>*/}
|
||||||
|
{/* <Chart ref={productNumRef} />*/}
|
||||||
|
{/* </Card>*/}
|
||||||
|
{/*</Row.Col>*/}
|
||||||
|
<Row.Col span={6}>
|
||||||
|
<Card title="发货总体达成率" titleSpace={true} overVisible={true}>
|
||||||
|
<Chart ref={okNumRef} />
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-50" gutter={gutter} bottom={bottom}>
|
||||||
|
<Row.Col span={18}>
|
||||||
|
<Card
|
||||||
|
title="计划达成率"
|
||||||
|
titleSpace={true}
|
||||||
|
padding={`15px ${gutter}px`}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
ref={productTableRefNew}
|
||||||
|
mainKey="code"
|
||||||
|
columns={productTableColumnNew}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={6}>
|
||||||
|
<Card title="计划总体达成率" titleSpace={true} overVisible={true}>
|
||||||
|
<Chart ref={okNumRefNew} />
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-container {
|
||||||
|
height: 100vh;
|
||||||
|
overflow: auto;
|
||||||
|
background: url("/img/bg.png") center center;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 96px;
|
||||||
|
padding-bottom: 82px;
|
||||||
|
}
|
||||||
|
.screen-container-content {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 ${gutter}px;
|
||||||
|
}
|
||||||
|
`}</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 Comprehensive;
|
|
@ -0,0 +1,978 @@
|
||||||
|
import React, { useRef, useEffect, useState, useCallback } from 'react';
|
||||||
|
import { withRouter } from 'next/router';
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
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 Table from '../../../components/screen/Table';
|
||||||
|
import InfoCardList from '../../../components/screen/InfoCardList';
|
||||||
|
import io from '../../../utils/socket.io.js';
|
||||||
|
|
||||||
|
const gutter = 20;
|
||||||
|
const bottom = 20;
|
||||||
|
|
||||||
|
const polarBarOpt = {
|
||||||
|
title: {
|
||||||
|
text: '',
|
||||||
|
left: 'center',
|
||||||
|
top: 24,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['时间稼动率', '性能稼动率', '良品率'],
|
||||||
|
bottom: 43,
|
||||||
|
orient: 'vertical',
|
||||||
|
itemHeight: 18,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
polar: {
|
||||||
|
radius: ['20%', '90%'],
|
||||||
|
center: ['50%', '40%'],
|
||||||
|
},
|
||||||
|
angleAxis: {
|
||||||
|
show: false,
|
||||||
|
max: 100,
|
||||||
|
startAngle: 90,
|
||||||
|
},
|
||||||
|
radiusAxis: {
|
||||||
|
show: false,
|
||||||
|
type: 'category',
|
||||||
|
data: ['%']
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
formatter: '{a}'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: '时间稼动率',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: '0',
|
||||||
|
barWidth: 14,
|
||||||
|
data: [],
|
||||||
|
coordinateSystem: 'polar',
|
||||||
|
}, {
|
||||||
|
name: '性能稼动率',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: '0',
|
||||||
|
barWidth: 14,
|
||||||
|
data: [],
|
||||||
|
coordinateSystem: 'polar',
|
||||||
|
}, {
|
||||||
|
name: '良品率',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: '0',
|
||||||
|
barWidth: 14,
|
||||||
|
data: [],
|
||||||
|
coordinateSystem: 'polar',
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
const setPolarBarOpt = (values, title) => {
|
||||||
|
values = values.map(i => (i * 100).toFixed(2));
|
||||||
|
values = values.map(i => {
|
||||||
|
if (isNaN(i)) i = '0.00';
|
||||||
|
return i;
|
||||||
|
});
|
||||||
|
const names = ['时间稼动率 ', '性能稼动率 ', '良品率 '];
|
||||||
|
let res = _.cloneDeep(polarBarOpt);
|
||||||
|
if (title) res.title.text = title;
|
||||||
|
values[0] !== undefined && (res.series[0].data[0] = values[0]);
|
||||||
|
values[1] !== undefined && (res.series[1].data[0] = values[1]);
|
||||||
|
values[2] !== undefined && (res.series[2].data[0] = values[2]);
|
||||||
|
res.legend.data = names.map((name, index) => name = values[index] !== undefined ? name + ` ${values[index]}%` : name);
|
||||||
|
res.series = res.series.map((item, index) => {
|
||||||
|
values[index] !== undefined && (item.name = names[index] + ` ${values[index]}%`);
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineOpt = {
|
||||||
|
legend: {
|
||||||
|
data: ['OEE'],
|
||||||
|
top: 10,
|
||||||
|
right: 10,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisTick: {
|
||||||
|
lineStyle: { opacity: 0.3 },
|
||||||
|
alignWithLabel: true,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: { opacity: 0.3 },
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(255, 255, 255, 0.8)'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
max: 100,
|
||||||
|
axisTick: {
|
||||||
|
lineStyle: { opacity: 0.3 }
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: { opacity: 0.3 }
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}%',
|
||||||
|
color: 'rgba(255, 255, 255, 0.8)'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
bottom: 40,
|
||||||
|
top: 110,
|
||||||
|
left: 50,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
formatter: data => `${data.value[0]}:${data.value[1]}%`,
|
||||||
|
},
|
||||||
|
series: {
|
||||||
|
name: "OEE",
|
||||||
|
data: [],
|
||||||
|
type: 'line',
|
||||||
|
color: "#50D9B0",
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
formatter: data => `${data.value[1]}%`,
|
||||||
|
textBorderColor: 'rgba(0, 0, 0, 0)',
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
symbolSize: 8,
|
||||||
|
lineStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
|
||||||
|
offset: 0, color: 'rgba(255, 225, 50, 0.54)'
|
||||||
|
}, {
|
||||||
|
offset: 1, color: 'rgb(45, 240, 219)'
|
||||||
|
}])
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.4,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 1, 1, 1, [{
|
||||||
|
offset: 0, color: 'rgba(255, 225, 50, 0)'
|
||||||
|
}, {
|
||||||
|
offset: 1, color: 'rgb(1, 191, 236)'
|
||||||
|
}])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const setLineOpt = data => {
|
||||||
|
let res = _.cloneDeep(lineOpt);
|
||||||
|
res.series.data = data;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
const getOEEnearWeekTrendInfo = (data) => {
|
||||||
|
let total = 0;
|
||||||
|
let max = 0;
|
||||||
|
console.log("data", data)
|
||||||
|
data.forEach(([day, val]) => {
|
||||||
|
total = total + val;
|
||||||
|
if (val > max) max = val;
|
||||||
|
})
|
||||||
|
let average = (parseInt(total * 100 / data.length)) / 100;
|
||||||
|
if (isNaN(average)) average = 0;
|
||||||
|
return [max, average]
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
"code": "0000",
|
||||||
|
"extra": {
|
||||||
|
"PieChart": {
|
||||||
|
"SY_TIME": 0.442,
|
||||||
|
"DT_GOODPRODUCT": 0.12,
|
||||||
|
"SY_GOODPRODUCT": 0.12,
|
||||||
|
"DY_GOODPRODUCT": 0.12,
|
||||||
|
"DT_PERFORMANCE": 0.52,
|
||||||
|
"DT_TIME": 0.27,
|
||||||
|
"DY_TIME": 0.33999999999999997,
|
||||||
|
"DY_PERFORMANCE": 0.32999999999999996,
|
||||||
|
"SY_PERFORMANCE": 0.431
|
||||||
|
},
|
||||||
|
"TrendChart": {
|
||||||
|
"03-31": 0.0514,
|
||||||
|
"04-10": 0.9423,
|
||||||
|
"04-11": 0.2197,
|
||||||
|
"04-12": 0.8591,
|
||||||
|
"04-13": 0.3331,
|
||||||
|
"04-08": 0.2534,
|
||||||
|
"04-09": 0.4141
|
||||||
|
},
|
||||||
|
"EquipStatus": [{
|
||||||
|
"phenomenonCode": "YC001",
|
||||||
|
"equipName": "窑炉1号",
|
||||||
|
"phenomenonName": "1002,1003,啊啊啊啊啊啊",
|
||||||
|
"equipCode": "YL001"
|
||||||
|
}, {
|
||||||
|
"phenomenonCode": "BBB",
|
||||||
|
"equipName": "111",
|
||||||
|
"phenomenonName": "1003,iiiiiiiii",
|
||||||
|
"equipCode": "YL006"
|
||||||
|
}, {
|
||||||
|
"phenomenonCode": "请求",
|
||||||
|
"equipName": "除磁001",
|
||||||
|
"phenomenonName": "请求",
|
||||||
|
"equipCode": "CC001"
|
||||||
|
}],
|
||||||
|
// "EquipData": {
|
||||||
|
// "C001": [{
|
||||||
|
// "code": "code",
|
||||||
|
// "name": "name",
|
||||||
|
// "temp": "32", // 温度
|
||||||
|
// "rps": "11", // 转速
|
||||||
|
// "ia": "12", // 电流
|
||||||
|
// "ua": "13", // 电压
|
||||||
|
// "runningstatus": "87" // 运行状态
|
||||||
|
// }]
|
||||||
|
// },
|
||||||
|
"EquipData": {
|
||||||
|
"CC": [
|
||||||
|
{
|
||||||
|
"equipCode": "123456789",
|
||||||
|
"equipName": "测试设备-dwj",
|
||||||
|
"fileCode": "9NO0EXP6MXQ3VHSN1OTS5OXZ2463PWZ8F247B02EF9CKHCP90F0TJHIV6HFMX148YIS3GONKYDAWLLWXS4GNPJUGG4O17PF6DEFIN94B549A0Y5XCWMB4IUDKSY62D0F7ZWQ32364DF7D9FB3E584D63QM5JHMLU33T5NXWQGPWDOXWL0K9I71RCUXY1OJ64FSXW9NB912L9J7NYUFK45YF8YLXYQA402E667DAA82A558675E26D345EFD35747",
|
||||||
|
"opCode": "CC",
|
||||||
|
"opName": "除磁",
|
||||||
|
"runningstatus": null,
|
||||||
|
"shipList": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"XM": [
|
||||||
|
{
|
||||||
|
"equipCode": "eqtest419",
|
||||||
|
"equipName": "设备测试419",
|
||||||
|
"fileCode": "01TR2HYCOOH6APN22DUFA9P5EHCQ6WK9FA1B789C3CU087W5R57HC704LEOFQK69SAX02I2T329SKVELGB686CEC5KLJPA6ML7J8S3451196V9KYFSFSELQA0CINDS2B4HBCIAFF389A4BC89DCHQVSFX41AY8PAI7HTZC8UKY00ZZ1KDM3LHSLNMWDEE3X0YA81EZ0BGMLMHXSW4R1INRW7ZCYIA7XU7BB35C97C67D63F9390C342479E1A150",
|
||||||
|
"opCode": "XM",
|
||||||
|
"opName": "细磨",
|
||||||
|
"runningstatus": null,
|
||||||
|
"shipList": [
|
||||||
|
{
|
||||||
|
"data": null,
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "1",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1681416533722673152",
|
||||||
|
"iotField": "ty1",
|
||||||
|
"iotFieldDescribe": "信号1",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1621704831515373568",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": null,
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "2",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1681424290735599616",
|
||||||
|
"iotField": "ty2",
|
||||||
|
"iotFieldDescribe": "信号2",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1621704831515373568",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": null,
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "4",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1681424290769154048",
|
||||||
|
"iotField": "ty4",
|
||||||
|
"iotFieldDescribe": "信号4",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1621704831515373568",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": null,
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "5",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1681424290802708480",
|
||||||
|
"iotField": "ty5",
|
||||||
|
"iotFieldDescribe": "信号5",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1621704831515373568",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": null,
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "3",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1681556221980717056",
|
||||||
|
"iotField": "ty3",
|
||||||
|
"iotFieldDescribe": "信号3",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1621704831515373568",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BZ": [
|
||||||
|
{
|
||||||
|
"equipCode": "PP001",
|
||||||
|
"equipName": "设备小明 001",
|
||||||
|
"fileCode": "1LM22ETECZCV25GCIUBNQRI5P96TA4I3AAE19CD0FCKWVLO17UI4KHG9HDX1R0MHMRKYWRTGCRF0IZ3HVL2EOTQUWZELDJS0OORIB44AB680W7K2DTXZVRF68FAI8SKPAOJ3UD0D203F20551EANV7C8580PMMRN4I67F070AN0IZJL76S2UKUQ7O6ATMKMGZ8NWPOAIEW75LRMZMNPD0GLZ06AKTD5W7FA073DC704B06D1FE9CDC08E156FB61",
|
||||||
|
"opCode": "BZ",
|
||||||
|
"opName": "包装",
|
||||||
|
"runningstatus": null,
|
||||||
|
"shipList": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TL": [
|
||||||
|
{
|
||||||
|
"equipCode": "L5_TLXT_B01",
|
||||||
|
"equipName": "L5_TLXT_B01设备",
|
||||||
|
"fileCode": "ZCFNP5MUJORD07O3N3SYM81VRH443R8F4A20A141A68H75DL162LU6XYA5QS4TWZN2CEKIKOJOVX6UE2CL6YRFMV6EFN0YAVH0SEZ14945A57UZ9JD466RKDULDMONJU3XPZMEA4E868EFF6BEDJ8YCQG2P61SKEJS4M82JBYXEV7L7S41E02S3NMC6S7JJ9EIO0EORRZP2STV73L0LGPKH6N6JXVCL163615505CB9899AD60CABB886B5E2B92",
|
||||||
|
"opCode": "TL",
|
||||||
|
"opName": "投料",
|
||||||
|
"runningstatus": "2",
|
||||||
|
"shipList": [
|
||||||
|
{
|
||||||
|
"data": "104.9",
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "1",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1682683971810504704",
|
||||||
|
"iotField": "ChnSL_WG1_TL2_TL2_DB_AI_DB4_472",
|
||||||
|
"iotFieldDescribe": "信号472",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1682681371711123456",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "746.7",
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "2",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1682683971894390784",
|
||||||
|
"iotField": "ChnSL_WG1_TL2_TL2_DB_AI_DB4_148",
|
||||||
|
"iotFieldDescribe": "信号148",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1682681371711123456",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "699.4",
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "3",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1682683971860836352",
|
||||||
|
"iotField": "ChnSL_WG1_TL2_TL2_DB_AI_DB4_168",
|
||||||
|
"iotFieldDescribe": "信号168",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1682681371711123456",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "462",
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "5",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1682853205282795520",
|
||||||
|
"iotField": "ChnSL_WG1_TL2_TL2_DB_AI_DB4_400",
|
||||||
|
"iotFieldDescribe": "信号400",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": false,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1682681371711123456",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equipCode": "L5_YMXT_A01",
|
||||||
|
"equipName": "L5_YMXT_A01设备",
|
||||||
|
"fileCode": "NGERX0JN0T2MNEZ8Z98XT7LKTKZ4D266EC596DDD8BVZM0KFO36T79MYAF4VY15VH76T12ID0XRJV6P2RG53MBE1U5HP0FDPAH8V554BC6A42BS7SB79ZP0X8S2U655EPDF3MC9309151D5558D4JQYD6CB3JQJ5J0H82EJOHJUTWOBUL5Y4L9I97OMP5BWP2I7K1E7ACEDWOC88TZOL48H4THCXA0AV15D0290575E73FB8B736A503522EE1D3",
|
||||||
|
"opCode": "TL",
|
||||||
|
"opName": "投料",
|
||||||
|
"runningstatus": "2",
|
||||||
|
"shipList": [
|
||||||
|
{
|
||||||
|
"data": "0",
|
||||||
|
"deleted": false,
|
||||||
|
"displayWork": false,
|
||||||
|
"fieldSort": "1",
|
||||||
|
"flag": null,
|
||||||
|
"gid": "1682898429577146368",
|
||||||
|
"iotField": "ChnSL_WG1_YM1_YM1_DB4_DB4_350",
|
||||||
|
"iotFieldDescribe": "350信号",
|
||||||
|
"iotIsMainField": true,
|
||||||
|
"iotIsalarm": true,
|
||||||
|
"iottype": "string",
|
||||||
|
"relationGid": "1682897844270411776",
|
||||||
|
"serialNumber": null,
|
||||||
|
"status": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"FS": [
|
||||||
|
{
|
||||||
|
"equipCode": "CS001",
|
||||||
|
"equipName": "测试1号设备aaa",
|
||||||
|
"fileCode": null,
|
||||||
|
"opCode": "FS",
|
||||||
|
"opName": "粉碎",
|
||||||
|
"runningstatus": null,
|
||||||
|
"shipList": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"message": "OK",
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
data = JSON.stringify(data);
|
||||||
|
|
||||||
|
function Equipment(props) {
|
||||||
|
const { config: { ioSocketUrl, equipmentConfig = {} } = {} } = global;
|
||||||
|
const { equipListNext: listToNextPage, equipAbnormalNext: tableToNextPage } = equipmentConfig;
|
||||||
|
|
||||||
|
const { router } = props;
|
||||||
|
|
||||||
|
const socket = useRef();
|
||||||
|
const getData = () => {
|
||||||
|
// let {workCenterCode} = router.query;
|
||||||
|
let workCenterCode = getQueryVariable('workCenterCode');
|
||||||
|
let date = getQueryVariable('date');
|
||||||
|
console.log("fetch", workCenterCode)
|
||||||
|
if (!workCenterCode) return;
|
||||||
|
|
||||||
|
// console.log("00000", {
|
||||||
|
// workCenterCode,
|
||||||
|
// date,
|
||||||
|
// screenType: "EquipDashBoardType",
|
||||||
|
// });
|
||||||
|
let query = {
|
||||||
|
workCenterCode,
|
||||||
|
screenType: "EquipDashBoardType",
|
||||||
|
}
|
||||||
|
if (date) query.date = date;
|
||||||
|
else {
|
||||||
|
query.date = new Date().toJSON().split("T").join(" ").substr(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log("query", {query})
|
||||||
|
socket.current.emit('timePerformanceGoodProduct', 'channel', {query})
|
||||||
|
|
||||||
|
// update(data)
|
||||||
|
};
|
||||||
|
|
||||||
|
const OEEonDay = useRef();
|
||||||
|
const OEEonMonth = useRef();
|
||||||
|
const OEELastMonth = useRef();
|
||||||
|
const OEEnearWeekTrend = useRef();
|
||||||
|
const equipTableRef = useRef();
|
||||||
|
const infoCardListRef = useRef();
|
||||||
|
const [oEEnearWeekTrendInfo, setOEEnearWeekTrendInfo] = useState([0, 0]);
|
||||||
|
|
||||||
|
const tableTimeoutRef = useRef();
|
||||||
|
const listTimeoutRef = useRef();
|
||||||
|
|
||||||
|
const [{DT_OEE, DY_OEE, SY_OEE}, setOEE] = useState({})
|
||||||
|
|
||||||
|
const update = useCallback(data => {
|
||||||
|
try { data = JSON.parse(data) } catch (e) { console.log(e) }
|
||||||
|
const { PieChart = {}, TrendChart = {}, EquipStatus = [], EquipData = [],energySummary = [],recentAlert = []} = data.extra || {};
|
||||||
|
// energySummary = {
|
||||||
|
// "2022-08-19": {
|
||||||
|
// packingWeight: 0.0,
|
||||||
|
// packingQty: 0.0,
|
||||||
|
// gas: 10626.98,
|
||||||
|
// electricity: 101161.97,
|
||||||
|
// feedingWeight: 76002.0,
|
||||||
|
// water: 60551.0,
|
||||||
|
// },
|
||||||
|
// "2022-08-20": {
|
||||||
|
// packingWeight: 861.0,
|
||||||
|
// packingQty: 2.0,
|
||||||
|
// gas: 3555.55,
|
||||||
|
// electricity: 50378.71,
|
||||||
|
// feedingWeight: 27102.0,
|
||||||
|
// water: 24655.0,
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// recentAlert = [
|
||||||
|
// {
|
||||||
|
// duration: 18677,
|
||||||
|
// eliminationTime: "2022-08-20 11:20:18",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_0_4",
|
||||||
|
// fuzzyDate: "5小时12分钟",
|
||||||
|
// phenomenonName: " SL-101下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-20 06:09:01",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 18574,
|
||||||
|
// eliminationTime: "2022-08-20 11:20:20",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_1_6",
|
||||||
|
// fuzzyDate: "5小时10分钟",
|
||||||
|
// phenomenonName: " SL-103下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-20 06:10:46",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 16931,
|
||||||
|
// eliminationTime: "2022-08-20 06:04:13",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_0_4",
|
||||||
|
// fuzzyDate: "4小时43分钟",
|
||||||
|
// phenomenonName: " SL-101下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-20 01:22:02",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 16832,
|
||||||
|
// eliminationTime: "2022-08-20 06:04:17",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_1_6",
|
||||||
|
// fuzzyDate: "4小时41分钟",
|
||||||
|
// phenomenonName: " SL-103下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-20 01:23:45",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 9365,
|
||||||
|
// eliminationTime: "2022-08-20 06:44:59",
|
||||||
|
// field: "ChnSL_WG5_HDSS_HDSS_BJ_DB25_9_2",
|
||||||
|
// fuzzyDate: "2小时37分钟",
|
||||||
|
// phenomenonName: "QF-616阀门故障",
|
||||||
|
// equipCode: "HDSSZDX001",
|
||||||
|
// alertTime: "2022-08-20 04:08:54",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 9203,
|
||||||
|
// eliminationTime: "2022-08-20 13:58:34",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_0_4",
|
||||||
|
// fuzzyDate: "2小时34分钟",
|
||||||
|
// phenomenonName: " SL-101下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-20 11:25:11",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 9054,
|
||||||
|
// eliminationTime: "2022-08-20 13:58:37",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_1_6",
|
||||||
|
// fuzzyDate: "2小时31分钟",
|
||||||
|
// phenomenonName: " SL-103下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-20 11:27:43",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 7636,
|
||||||
|
// eliminationTime: "2022-08-20 13:35:05",
|
||||||
|
// field: "ChnSL_WG5_HDSS_HDSS_BJ_DB25_9_2",
|
||||||
|
// fuzzyDate: "2小时8分钟",
|
||||||
|
// phenomenonName: "QF-616阀门故障",
|
||||||
|
// equipCode: "HDSSZDX001",
|
||||||
|
// alertTime: "2022-08-20 11:27:49",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 7221,
|
||||||
|
// eliminationTime: "2022-08-20 01:17:08",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_0_4",
|
||||||
|
// fuzzyDate: "2小时1分钟",
|
||||||
|
// phenomenonName: " SL-101下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-19 23:16:47",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 7084,
|
||||||
|
// eliminationTime: "2022-08-20 01:17:10",
|
||||||
|
// field: "ChnSL_WG1_TL2_TL2_BJ_DB3_1_6",
|
||||||
|
// fuzzyDate: "1小时59分钟",
|
||||||
|
// phenomenonName: " SL-103下料阀故障2",
|
||||||
|
// equipCode: "L5_TLXT_B01",
|
||||||
|
// alertTime: "2022-08-19 23:19:06",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 6484,
|
||||||
|
// eliminationTime: "2022-08-20 08:38:23",
|
||||||
|
// field: "ChnSL_WG5_HDSS_HDSS_BJ_DB25_9_2",
|
||||||
|
// fuzzyDate: "1小时49分钟",
|
||||||
|
// phenomenonName: "QF-616阀门故障",
|
||||||
|
// equipCode: "HDSSZDX001",
|
||||||
|
// alertTime: "2022-08-20 06:50:19",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 4652,
|
||||||
|
// eliminationTime: "2022-08-20 15:46:19",
|
||||||
|
// field: "ChnSL_WG5_HDSS_HDSS_BJ_DB25_9_2",
|
||||||
|
// fuzzyDate: "1小时18分钟",
|
||||||
|
// phenomenonName: "QF-616阀门故障",
|
||||||
|
// equipCode: "HDSSZDX001",
|
||||||
|
// alertTime: "2022-08-20 14:28:47",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 2331,
|
||||||
|
// eliminationTime: "2022-08-20 13:05:20",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_7",
|
||||||
|
// fuzzyDate: "39分钟",
|
||||||
|
// phenomenonName: " 粗磨机1总故障",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 12:26:29",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 2331,
|
||||||
|
// eliminationTime: "2022-08-20 13:05:20",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_4",
|
||||||
|
// fuzzyDate: "39分钟",
|
||||||
|
// phenomenonName: " 粗磨机1出料温度报警",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 12:26:29",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 1898,
|
||||||
|
// eliminationTime: "2022-08-20 15:33:21",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_7",
|
||||||
|
// fuzzyDate: "32分钟",
|
||||||
|
// phenomenonName: " 粗磨机1总故障",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 15:01:43",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 1898,
|
||||||
|
// eliminationTime: "2022-08-20 15:33:21",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_4",
|
||||||
|
// fuzzyDate: "32分钟",
|
||||||
|
// phenomenonName: " 粗磨机1出料温度报警",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 15:01:43",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 1667,
|
||||||
|
// eliminationTime: "2022-08-20 11:23:50",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_7",
|
||||||
|
// fuzzyDate: "28分钟",
|
||||||
|
// phenomenonName: " 粗磨机1总故障",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 10:56:03",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 1667,
|
||||||
|
// eliminationTime: "2022-08-20 11:23:50",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_4",
|
||||||
|
// fuzzyDate: "28分钟",
|
||||||
|
// phenomenonName: " 粗磨机1出料温度报警",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 10:56:03",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 1280,
|
||||||
|
// eliminationTime: "2022-08-20 04:54:41",
|
||||||
|
// field: "ChnSL_WG2_ylzdx1_ylzdx_1_bj_DBX85_3",
|
||||||
|
// fuzzyDate: "22分钟",
|
||||||
|
// phenomenonName: "下方输送管道故障_11",
|
||||||
|
// equipCode: "YLZDX001",
|
||||||
|
// alertTime: "2022-08-20 04:33:21",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// duration: 1160,
|
||||||
|
// eliminationTime: "2022-08-20 13:32:12",
|
||||||
|
// field: "ChnSL_WG1_YM1_YM1_BJ_M150_7",
|
||||||
|
// fuzzyDate: "20分钟",
|
||||||
|
// phenomenonName: " 粗磨机1总故障",
|
||||||
|
// equipCode: "L5_YMXT_A01",
|
||||||
|
// alertTime: "2022-08-20 13:12:52",
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
|
console.log("data", data)
|
||||||
|
|
||||||
|
// 1 OEE趋势
|
||||||
|
let {
|
||||||
|
DT_TIME, DT_PERFORMANCE, DT_GOODPRODUCT,
|
||||||
|
DY_TIME, DY_PERFORMANCE, DY_GOODPRODUCT,
|
||||||
|
SY_TIME, SY_PERFORMANCE, SY_GOODPRODUCT,
|
||||||
|
} = PieChart;
|
||||||
|
let DT_OEE = DT_TIME * DT_PERFORMANCE * DT_GOODPRODUCT;
|
||||||
|
let DY_OEE = DY_TIME * DY_PERFORMANCE * DY_GOODPRODUCT;
|
||||||
|
let SY_OEE = SY_TIME * SY_PERFORMANCE * SY_GOODPRODUCT;
|
||||||
|
DT_OEE = (DT_OEE * 100).toFixed(2) + "%";
|
||||||
|
DY_OEE = (DY_OEE * 100).toFixed(2) + "%";
|
||||||
|
SY_OEE = (SY_OEE * 100).toFixed(2) + "%";
|
||||||
|
setOEE({DT_OEE, DY_OEE, SY_OEE})
|
||||||
|
try {
|
||||||
|
OEEonDay.current.setOption(setPolarBarOpt([
|
||||||
|
DT_TIME, DT_PERFORMANCE, DT_GOODPRODUCT
|
||||||
|
], "当日OEE"));
|
||||||
|
OEEonMonth.current.setOption(setPolarBarOpt([
|
||||||
|
DY_TIME, DY_PERFORMANCE, DY_GOODPRODUCT,
|
||||||
|
], "当月OEE"));
|
||||||
|
OEELastMonth.current.setOption(setPolarBarOpt([
|
||||||
|
SY_TIME, SY_PERFORMANCE, SY_GOODPRODUCT,
|
||||||
|
], "上月OEE"));
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
|
// 2 近一周OEE趋势图
|
||||||
|
let lineData = [];
|
||||||
|
try {
|
||||||
|
for (let time in TrendChart || {}) {
|
||||||
|
let timeCode = new Date(time).getTime()
|
||||||
|
lineData.push([time, parseFloat((TrendChart[time] * 100).toFixed(2)), timeCode])
|
||||||
|
}
|
||||||
|
// console.log("lineData", lineData)
|
||||||
|
lineData.sort((a, b) => {
|
||||||
|
let [,,code1] = a;
|
||||||
|
let [,,code2] = b;
|
||||||
|
return code1 - code2;
|
||||||
|
})
|
||||||
|
OEEnearWeekTrend.current.setOption(setLineOpt(lineData));
|
||||||
|
setOEEnearWeekTrendInfo(getOEEnearWeekTrendInfo(lineData));
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
|
// 3 设备异常监控
|
||||||
|
try {
|
||||||
|
recentAlert.map(item=>{
|
||||||
|
item.alertTime=item.alertTime.substring(5,item.alertTime.length);
|
||||||
|
item.eliminationTime=item.eliminationTime.substring(5,item.eliminationTime.length);
|
||||||
|
})
|
||||||
|
equipTableRef.current.setData(recentAlert);
|
||||||
|
let getNextPage = () => {
|
||||||
|
clearTimeout(tableTimeoutRef.current);
|
||||||
|
tableTimeoutRef.current = setTimeout(() => {
|
||||||
|
equipTableRef.current.nextPage();
|
||||||
|
getNextPage();
|
||||||
|
}, tableToNextPage)
|
||||||
|
};
|
||||||
|
getNextPage();
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
|
// 4 设备运行状态
|
||||||
|
let withClassifyData = [];
|
||||||
|
// console.log("EquipData", EquipData)
|
||||||
|
console.log("res get")
|
||||||
|
try {
|
||||||
|
for (let name in EquipData) {
|
||||||
|
withClassifyData.push({ name, children: EquipData[name] })
|
||||||
|
}
|
||||||
|
infoCardListRef.current.setData(withClassifyData);
|
||||||
|
try {
|
||||||
|
infoCardListRef.current.setEnergy(energySummary);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
let getNextPage = () => {
|
||||||
|
clearTimeout(listTimeoutRef.current);
|
||||||
|
listTimeoutRef.current = setTimeout(() => {
|
||||||
|
infoCardListRef.current.nextPage();
|
||||||
|
getNextPage();
|
||||||
|
}, listToNextPage)
|
||||||
|
};
|
||||||
|
getNextPage();
|
||||||
|
} catch (e) { }
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const listFinished = useCallback(() => {
|
||||||
|
console.log("finished")
|
||||||
|
clearTimeout(listTimeoutRef.current);
|
||||||
|
clearTimeout(tableTimeoutRef.current);
|
||||||
|
getData();
|
||||||
|
}, [update])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { workCenterCode } = router.query;
|
||||||
|
if (!workCenterCode) return;
|
||||||
|
|
||||||
|
console.log("ioSocketUrl", ioSocketUrl)
|
||||||
|
socket.current = io.connect(ioSocketUrl, {transports:['websocket']});
|
||||||
|
|
||||||
|
socket.current.on('connect', function () {
|
||||||
|
console.log("msg页面连接成功!");
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.current.on('timePerformanceGoodProduct', function (data) {
|
||||||
|
// console.log("data", data)
|
||||||
|
update(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
// update(data)
|
||||||
|
|
||||||
|
}, [router])
|
||||||
|
|
||||||
|
return (// 683 733 424
|
||||||
|
<div className='screen-container'>
|
||||||
|
<Header title={"设备看板"} />
|
||||||
|
<div className="screen-container-content">
|
||||||
|
<Row style={{ height: '50.55%' }} gutter={gutter} bottom={bottom}>
|
||||||
|
<Row.Col style={{ width: '33%' }}>
|
||||||
|
<Card title="OEE趋势" titleSpace={true} overVisible={true}>
|
||||||
|
<Row className='height-100'>
|
||||||
|
<Row.Col span={8} gutter={10}>
|
||||||
|
{DT_OEE && <div className="OEE"> {DT_OEE} </div>}
|
||||||
|
<Chart ref={OEEonDay} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
{DY_OEE && <div className="OEE"> {DY_OEE} </div>}
|
||||||
|
<Chart ref={OEEonMonth} />
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={8}>
|
||||||
|
{SY_OEE && <div className="OEE"> {SY_OEE} </div>}
|
||||||
|
<Chart ref={OEELastMonth} />
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col style={{ width: '33%' }}>
|
||||||
|
<Card title="近一周OEE趋势图" overVisible={true}>
|
||||||
|
<div className="OEEnearWeekTrend-info">
|
||||||
|
<span className="item">最高OEE :{oEEnearWeekTrendInfo[0]}%</span>
|
||||||
|
<span className="item">平均OEE :{oEEnearWeekTrendInfo[1]}%</span>
|
||||||
|
</div>
|
||||||
|
<Chart ref={OEEnearWeekTrend} />
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col style={{ width: '34%' }}>
|
||||||
|
<Card title="设备异常监控" titleSpace={true} padding={`${gutter}px`}>
|
||||||
|
<Table
|
||||||
|
ref={equipTableRef}
|
||||||
|
mainKey="equipCode"
|
||||||
|
columns={[
|
||||||
|
{ title: "设备编码", code: "equipCode" },
|
||||||
|
{ title: "告警内容", code: "phenomenonName" },
|
||||||
|
{ title: "告警发生时间", code: "alertTime" },
|
||||||
|
{ title: "告警消除时间", code: "eliminationTime" },
|
||||||
|
{ title: "告警时长", code: "fuzzyDate" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{ height: '49.45%' }} gutter={gutter}>
|
||||||
|
<Row.Col span={24}>
|
||||||
|
<Card title="设备运行状态 " titleSpace={false} padding={`0px ${gutter}px`}>
|
||||||
|
<InfoCardList
|
||||||
|
ref={infoCardListRef}
|
||||||
|
mainKey={"code"}
|
||||||
|
type={'page'}
|
||||||
|
withClassify={true}
|
||||||
|
classifyKey={{ name: 'name', children: 'children' }}
|
||||||
|
autoNext={false}
|
||||||
|
pageFinished={listFinished}
|
||||||
|
span={equipmentConfig.equipListSpan}
|
||||||
|
imgKey={"fileCode"}
|
||||||
|
status={{
|
||||||
|
key: "runningstatus",
|
||||||
|
value: val => {
|
||||||
|
if (val === '1') return "待机中";
|
||||||
|
if (val === '2') return "运行中";
|
||||||
|
if (val === '3') return "停机中";
|
||||||
|
},
|
||||||
|
color: val => {
|
||||||
|
if (val === '1') return "#F5A623";
|
||||||
|
if (val === '2') return "#58BC17";
|
||||||
|
if (val === '3') return "#FF005A";
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{ title: "设备编号", code: "equipCode" },
|
||||||
|
{ title: "设备名称", code: "equipName" },
|
||||||
|
]}
|
||||||
|
extraColumnsOpt={{
|
||||||
|
key: "shipList",
|
||||||
|
name: "iotFieldDescribe",
|
||||||
|
val: "data",
|
||||||
|
max: equipmentConfig.equipItemLength - 2
|
||||||
|
}}
|
||||||
|
contentHeight={12 + 28 * equipmentConfig.equipItemLength}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
<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;
|
||||||
|
}
|
||||||
|
.screen-container-content{
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 ${gutter}px;
|
||||||
|
}
|
||||||
|
.OEEnearWeekTrend-info{
|
||||||
|
position: absolute;
|
||||||
|
top: 46px;
|
||||||
|
left: 20px;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 16px;
|
||||||
|
background: rgba(216, 216, 216, 0.08);
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
.OEEnearWeekTrend-info > .item{
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
.OEE{
|
||||||
|
font-size: 18px;
|
||||||
|
position: absolute;
|
||||||
|
top: 38%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
font-family: 'RubikRegular';
|
||||||
|
}
|
||||||
|
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(Equipment)
|
|
@ -0,0 +1,668 @@
|
||||||
|
import React, { useRef, useEffect, useCallback } from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { withRouter } from 'next/router';
|
||||||
|
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 Table from '../../../components/screen/Table';
|
||||||
|
import TableProgress from '../../../components/screen/TableProgress';
|
||||||
|
import io from '../../../utils/socket.io.js';
|
||||||
|
|
||||||
|
|
||||||
|
const gutter = 20;
|
||||||
|
const bottom = 20;
|
||||||
|
|
||||||
|
// 返回结果是个字符串,需要转一下
|
||||||
|
let testData = {
|
||||||
|
"code": "0000",
|
||||||
|
"data": {
|
||||||
|
//今日生产总数
|
||||||
|
"productCount": {
|
||||||
|
"planQty": 3162.000000, //计划总数
|
||||||
|
"productQty": 199.000000, //生产总数
|
||||||
|
"unFinishedQty": 2963.000000 //未完成数量
|
||||||
|
},
|
||||||
|
//今日生产统计
|
||||||
|
"productDayCountList": [
|
||||||
|
{
|
||||||
|
"productQty": 0.000000, //生产数量
|
||||||
|
"qualityQty": 0.000000, //合格数量
|
||||||
|
"time": "00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "01:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "02:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "03:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "04:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "05:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "06:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "07:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "08:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "09:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 69.000000,
|
||||||
|
"qualityQty": 17.000000,
|
||||||
|
"time": "10:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 12.000000,
|
||||||
|
"qualityQty": 12.000000,
|
||||||
|
"time": "11:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "12:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 15.000000,
|
||||||
|
"qualityQty": 15.000000,
|
||||||
|
"time": "13:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 25.000000,
|
||||||
|
"qualityQty": 25.000000,
|
||||||
|
"time": "14:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 26.000000,
|
||||||
|
"qualityQty": 26.000000,
|
||||||
|
"time": "15:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 52.000000,
|
||||||
|
"qualityQty": 51.000000,
|
||||||
|
"time": "16:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "17:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "18:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "19:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "20:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "21:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "22:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"qualityQty": 0.000000,
|
||||||
|
"time": "23:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
//日产能概况
|
||||||
|
"productDayRecordList": [
|
||||||
|
{
|
||||||
|
"code": "WO202203300001", //工单号
|
||||||
|
"materialCode": "L5CP", //产品编码
|
||||||
|
"materialName": "L5车间成品", //产品名称
|
||||||
|
"planQty": 1000.000000, //计划数量
|
||||||
|
"productQty": 10.000000, //生产数量
|
||||||
|
"productRate": 0.010, //生产进度
|
||||||
|
"productStatus": "已完工", //生产状态
|
||||||
|
"unQualityQty": 0.000000, //不合格数量
|
||||||
|
"unQualityRate": 0.000, //不合格率
|
||||||
|
"workCenterCode": "L5", //产线编码
|
||||||
|
"workCenterName": "L5车间" //产线名称
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204010001",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 100.000000,
|
||||||
|
"productQty": 15.000000,
|
||||||
|
"productRate": 0.150,
|
||||||
|
"productStatus": "已创建",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204010002",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 10.000000,
|
||||||
|
"productQty": 15.000000,
|
||||||
|
"productRate": 1.500,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204010003",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 10.000000,
|
||||||
|
"productQty": 12.000000,
|
||||||
|
"productRate": 1.200,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204060001",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 1000.000000,
|
||||||
|
"productQty": 14.000000,
|
||||||
|
"productRate": 0.014,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204060002",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 10.000000,
|
||||||
|
"productQty": 12.000000,
|
||||||
|
"productRate": 1.200,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204060003",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 10.000000,
|
||||||
|
"productQty": 14.000000,
|
||||||
|
"productRate": 1.400,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204070001",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 10.000000,
|
||||||
|
"productQty": 52.000000,
|
||||||
|
"productRate": 5.200,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 1.000000,
|
||||||
|
"unQualityRate": 0.019,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "WO202204080001",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 12.000000,
|
||||||
|
"productQty": 55.000000,
|
||||||
|
"productRate": 4.583,
|
||||||
|
"productStatus": "生产中",
|
||||||
|
"unQualityQty": 52.000000,
|
||||||
|
"unQualityRate": 0.945,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "RcQuickWearStr",
|
||||||
|
"materialCode": "L5CP",
|
||||||
|
"materialName": "L5车间成品",
|
||||||
|
"planQty": 1000.000000,
|
||||||
|
"productQty": 0.000000,
|
||||||
|
"productRate": 0.000,
|
||||||
|
"productStatus": "待生产",
|
||||||
|
"unQualityQty": 0.000000,
|
||||||
|
"unQualityRate": 0.000,
|
||||||
|
"workCenterCode": "L5",
|
||||||
|
"workCenterName": "L5车间"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 今日生产合格数
|
||||||
|
"qualityDayCount": {
|
||||||
|
"qualityQty": 146.000000, //合格数量
|
||||||
|
"qualityRate": 0.734, //合格率
|
||||||
|
"unQualityQty": 53.000000 //不合格数量
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
};
|
||||||
|
|
||||||
|
const polarBarOpt = {
|
||||||
|
title: {
|
||||||
|
text: '计划总数',
|
||||||
|
left: 'center',
|
||||||
|
bottom: 24,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 20,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
radiusAxis: {
|
||||||
|
show: false,
|
||||||
|
type: 'category',
|
||||||
|
data: ['%']
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
series: {
|
||||||
|
type: 'gauge',
|
||||||
|
center: ['50%', "40%"],
|
||||||
|
radius: "70%",
|
||||||
|
startAngle: 90,
|
||||||
|
endAngle: -270,
|
||||||
|
pointer: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
show: true,
|
||||||
|
overlap: false,
|
||||||
|
roundCap: false,
|
||||||
|
clip: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
width: 20,
|
||||||
|
color: [[1, 'rgba(40, 163, 255, 0.2)']],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [{
|
||||||
|
value: 0,
|
||||||
|
name: '生产总数'
|
||||||
|
}],
|
||||||
|
title: {
|
||||||
|
offsetCenter: ['0%', '20'],
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
show: true,
|
||||||
|
offsetCenter: ['0%', '-20'],
|
||||||
|
width: 'auto',
|
||||||
|
height: 32,
|
||||||
|
fontSize: 32,
|
||||||
|
color: '#00E1FA',
|
||||||
|
borderColor: 'auto',
|
||||||
|
borderWidth: 0,
|
||||||
|
formatter: '{value}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const setNumOpt = (total, done, title) => {
|
||||||
|
let res = _.cloneDeep(polarBarOpt);
|
||||||
|
if(title) res.title.text = `${title}:${total}`;
|
||||||
|
let percent = (100 * done / total).toFixed(2);
|
||||||
|
res.series.data[0].name = "生产总数";
|
||||||
|
res.series.data[0].value = percent;
|
||||||
|
res.series.detail.formatter = () => done;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
const setOkNumOpt = (total, done, title) => {
|
||||||
|
let res = _.cloneDeep(polarBarOpt);
|
||||||
|
let percent = (100 * done / (total === 0 ? 1 : total)).toFixed(2);
|
||||||
|
if(isNaN(percent)) percent = 0;
|
||||||
|
if(title) res.title.text = `${title}:${percent}%`;
|
||||||
|
res.series.data[0].value = percent;
|
||||||
|
res.series.data[0].name = "合格数量";
|
||||||
|
res.series.detail.formatter = () => done;
|
||||||
|
res.series.detail.color = "#6FE621";
|
||||||
|
res.series.progress.itemStyle.color = "#6FE621";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const barOpt = {
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
data: ['生产数量', '合格数量'],
|
||||||
|
top: 10,
|
||||||
|
right: 10,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisTick: {
|
||||||
|
lineStyle: {opacity: 0.3},
|
||||||
|
alignWithLabel: true,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {opacity: 0.3},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(255, 255, 255, 0.8)'
|
||||||
|
},
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisTick: {
|
||||||
|
lineStyle: {opacity: 0.3}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {opacity: 0.3}
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(255, 255, 255, 0.8)'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
bottom: 40,
|
||||||
|
top: 60,
|
||||||
|
left: 50,
|
||||||
|
},
|
||||||
|
barWidth: 20,
|
||||||
|
series: [{
|
||||||
|
name: '生产数量',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: -1,
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '合格数量',
|
||||||
|
itemStyle: {
|
||||||
|
color: "#6FE621",
|
||||||
|
},
|
||||||
|
type: 'bar',
|
||||||
|
data: []
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
const setBarOpt = (data) => {
|
||||||
|
let res = _.cloneDeep(barOpt);
|
||||||
|
data.forEach(({productQty, qualityQty, time}, index) => {
|
||||||
|
res.xAxis.data.push(time);
|
||||||
|
res.series[0].data[index] = productQty;
|
||||||
|
res.series[1].data[index] = qualityQty;
|
||||||
|
})
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const productTableColumns = [
|
||||||
|
{title: "机台", code: "workCenterName"},
|
||||||
|
{title: "生产状态", code: "productStatus",
|
||||||
|
color: val => {
|
||||||
|
if(val === '待生产') return "#d5542d";
|
||||||
|
if(val === '已创建') return "#1a7cc6";
|
||||||
|
if(val === '生产中') return "#0aac61";
|
||||||
|
if(val === '已完工') return "#0db5a4";
|
||||||
|
if(val === '已关闭') return "#c1c1c1";
|
||||||
|
if(val === '已取消') return "#c1c1c1";
|
||||||
|
},
|
||||||
|
render: (text, config, rowData) => {
|
||||||
|
const {color} = config;
|
||||||
|
let resColor = color(text);
|
||||||
|
return (
|
||||||
|
<div style={{color: resColor}}>
|
||||||
|
<span style={{
|
||||||
|
background: resColor,
|
||||||
|
width: '8px',
|
||||||
|
height: '8px',
|
||||||
|
display: 'inline-block',
|
||||||
|
borderRadius: '50%',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
}}></span>
|
||||||
|
<span style={{
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
marginLeft: '6px',
|
||||||
|
}}>{text}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{title: "产品编码", code: "materialCode"},
|
||||||
|
{title: "产品名称", code: "materialName"},
|
||||||
|
{title: "工单编号", code: "code"},
|
||||||
|
{title: "生产数量", code: "productQty"},
|
||||||
|
{title: "计划数量", code: "planQty"},
|
||||||
|
{title: "生产进度", code: "productRate",
|
||||||
|
width: 200,
|
||||||
|
render: (value, config, rowData) => {
|
||||||
|
return <TableProgress percent={value}/>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{title: "不良品数量", code: "unQualityQty"},
|
||||||
|
{title: "不良品率", code: "unQualityRate",
|
||||||
|
render: (value, config, rowData) => {
|
||||||
|
return <span>{value * 100}%</span>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function PlanBoard(props) {
|
||||||
|
const {config : {ioSocketUrl, planBoard = {}} = {}} = global;
|
||||||
|
const tableToNextPage = planBoard.productTableNext;
|
||||||
|
|
||||||
|
const {router} = props;
|
||||||
|
|
||||||
|
const planNumRef = useRef();
|
||||||
|
const okNumRef = useRef();
|
||||||
|
const productNumRef = useRef();
|
||||||
|
const productTableRef = useRef();
|
||||||
|
|
||||||
|
const socket = useRef();
|
||||||
|
const workCenterCodeRef = useRef();
|
||||||
|
const tableTimeoutRef = useRef();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
const getData = () => {
|
||||||
|
let workCenterCode = workCenterCodeRef.current
|
||||||
|
let date = getQueryVariable('date');
|
||||||
|
|
||||||
|
console.log("fetch", workCenterCode)
|
||||||
|
if(!workCenterCode) return;
|
||||||
|
let query = {
|
||||||
|
workCenterCode,
|
||||||
|
screenType: "ProductPlanType",
|
||||||
|
}
|
||||||
|
if (date) query.date = date;
|
||||||
|
else{
|
||||||
|
query.date = new Date().toJSON().split('T').join(' ').substr(0,10)
|
||||||
|
}
|
||||||
|
socket.current.emit('timePerformanceGoodProduct', 'channel', {query})
|
||||||
|
setTimeGetData()
|
||||||
|
};
|
||||||
|
const setTimeGetData = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
getData()
|
||||||
|
}, 300000)
|
||||||
|
}
|
||||||
|
const pageFinished = useCallback(() => {
|
||||||
|
console.log("finished")
|
||||||
|
clearInterval(tableTimeoutRef.current);
|
||||||
|
getData();
|
||||||
|
})
|
||||||
|
const setTableNextPage = useCallback(() => {
|
||||||
|
tableTimeoutRef.current = setInterval(() => {
|
||||||
|
productTableRef.current.nextPage();
|
||||||
|
}, tableToNextPage);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const update = (data) => {
|
||||||
|
try{ data = JSON.parse(data) }catch(e){console.log(e)}
|
||||||
|
// if(!data) data = testData;
|
||||||
|
const {productCount, productDayCountList, qualityDayCount, productDayRecordList} = data.data;
|
||||||
|
console.log("data", data.data)
|
||||||
|
|
||||||
|
planNumRef.current.setOption(setNumOpt(
|
||||||
|
productCount.planQty,
|
||||||
|
productCount.productQty,
|
||||||
|
"计划总数"
|
||||||
|
));
|
||||||
|
okNumRef.current.setOption(setOkNumOpt(
|
||||||
|
qualityDayCount.qualityQty,
|
||||||
|
qualityDayCount.qualityQty - qualityDayCount.unQualityQty,
|
||||||
|
"合格率"
|
||||||
|
));
|
||||||
|
|
||||||
|
productNumRef.current.setOption(setBarOpt(productDayCountList));
|
||||||
|
|
||||||
|
productTableRef.current.setData(productDayRecordList);
|
||||||
|
setTableNextPage();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const {workCenterCode} = router.query;
|
||||||
|
if(!workCenterCode) return;
|
||||||
|
workCenterCodeRef.current = workCenterCode;
|
||||||
|
}, [router])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.current = io.connect(ioSocketUrl, {transports:['websocket']});
|
||||||
|
|
||||||
|
socket.current.on('connect', function() {
|
||||||
|
console.log("msg页面连接成功!");
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.current.on('timePerformanceGoodProduct', function(data) {
|
||||||
|
update(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
// update();
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='screen-container'>
|
||||||
|
<Header title={"计划看板"} />
|
||||||
|
<div className="screen-container-content">
|
||||||
|
<Row className='height-45' gutter={gutter} bottom={bottom}>
|
||||||
|
<Row.Col span={5}>
|
||||||
|
<Card title="今日生产总数" titleSpace={true} overVisible={true}>
|
||||||
|
<Chart ref={planNumRef} />
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={14}>
|
||||||
|
<Card title="今日生产统计" overVisible={true}>
|
||||||
|
<Chart ref={productNumRef} />
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
<Row.Col span={5}>
|
||||||
|
<Card title="今日生产合格总数" titleSpace={true} overVisible={true}>
|
||||||
|
<Chart ref={okNumRef} />
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='height-55' gutter={gutter}>
|
||||||
|
<Row.Col span={24}>
|
||||||
|
<Card title="生产概况" titleSpace={true} padding={`15px ${gutter}px`}>
|
||||||
|
<Table
|
||||||
|
ref={productTableRef}
|
||||||
|
mainKey="code"
|
||||||
|
columns={productTableColumns}
|
||||||
|
autoNext={false}
|
||||||
|
pageFinished={pageFinished}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-container{
|
||||||
|
height: 100vh;
|
||||||
|
overflow: auto;
|
||||||
|
background: url('/img/bg.png') center center;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 96px;
|
||||||
|
padding-bottom: 82px;
|
||||||
|
}
|
||||||
|
.screen-container-content{
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 ${gutter}px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(PlanBoard)
|
|
@ -0,0 +1,416 @@
|
||||||
|
import {useRouter} from "next/router";
|
||||||
|
import React, {useMemo, useState, useRef, useEffect} from "react";
|
||||||
|
import _ from "lodash";
|
||||||
|
import moment from 'moment';
|
||||||
|
import Footer from "../../../components/screen/Footer";
|
||||||
|
import Header from "../../../components/screen/Header";
|
||||||
|
import Chart from "../../../components/screen/Chart";
|
||||||
|
import Row from "../../../components/screen/Row";
|
||||||
|
import Card from '../../../components/screen/Card';
|
||||||
|
import Table from "../../../components/screen/Table";
|
||||||
|
import ProcessCard from "../../components/ProcessCard";
|
||||||
|
import {useSocket} from "../../../utils/hooks";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生产工序总图
|
||||||
|
*/
|
||||||
|
const socketScreenType = 'AllOperationType';
|
||||||
|
const gutter = 20;
|
||||||
|
const bottom = 0;
|
||||||
|
|
||||||
|
//关键生产信息
|
||||||
|
const getProdOpt = (workCenter, data) => {
|
||||||
|
let {xData = [], yData = []} = data;
|
||||||
|
let opt = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: `${workCenter}日生产进度`,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: "14px"
|
||||||
|
},
|
||||||
|
top: 15,
|
||||||
|
left: 60
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: 50,
|
||||||
|
left: 55
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: yData,
|
||||||
|
type: 'bar'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
let newXData = []
|
||||||
|
xData.forEach(item => {
|
||||||
|
let date = (new Date(item).getMonth() + 1) + "." + new Date(item).getDate();
|
||||||
|
newXData.push(date)
|
||||||
|
})
|
||||||
|
opt.xAxis.data = newXData;
|
||||||
|
return opt;
|
||||||
|
};
|
||||||
|
|
||||||
|
//关键能耗信息
|
||||||
|
const getUsedOpt = (data = {}, params = []) => {
|
||||||
|
let {
|
||||||
|
energyOfDay = 0, //电
|
||||||
|
energyOfMonth = 0,
|
||||||
|
gasOfDay = 0, //气
|
||||||
|
gasOfMonth = 0,
|
||||||
|
waterOfDay = 0,//水
|
||||||
|
waterOfMonth = 0,
|
||||||
|
} = data;
|
||||||
|
if (params.length > 0) {
|
||||||
|
params[0].values[0].value = energyOfMonth;
|
||||||
|
params[0].values[1].value = energyOfDay;
|
||||||
|
params[1].values[0].value = waterOfMonth;
|
||||||
|
params[1].values[1].value = waterOfDay;
|
||||||
|
params[2].values[0].value = gasOfMonth;
|
||||||
|
params[2].values[1].value = gasOfDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = {
|
||||||
|
title: {
|
||||||
|
text: "",
|
||||||
|
left: "center",
|
||||||
|
top: 70,
|
||||||
|
textStyle: {
|
||||||
|
fontWeight: "normal",
|
||||||
|
fontSize: "16px"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "item",
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
bottom: 10,
|
||||||
|
left: "center",
|
||||||
|
orient: "vertical",
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 13,
|
||||||
|
lineHeight: 16
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "pie",
|
||||||
|
radius: ["45%", "70%"],
|
||||||
|
center: ["50%", "40%"],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: "center",
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let res = [];
|
||||||
|
let colors = ["#6FE621", "#FF005A", "#4D7BF3", "#4FCCFF"]
|
||||||
|
params.forEach(({name, unit, values}, index) => {
|
||||||
|
let newOpt = _.cloneDeep(opt);
|
||||||
|
newOpt.color = [colors[index], "#FFC760"];
|
||||||
|
newOpt.title.text = `${name}\n(${unit})`;
|
||||||
|
newOpt.series[0].data = values;
|
||||||
|
newOpt.legend.formatter = name => {
|
||||||
|
let {value = ""} = values.find(item => item.name === name) || {};
|
||||||
|
return `${name}: ${value}`
|
||||||
|
}
|
||||||
|
res.push(newOpt);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
//维保列表字段
|
||||||
|
const prodTableColumns = [
|
||||||
|
{title: "任务编码", code: "code"},
|
||||||
|
{title: "维保类型", code: "type"},
|
||||||
|
{title: "设备名称", code: "equipSerialName"},
|
||||||
|
{title: "任务状态", code: "status"},
|
||||||
|
]
|
||||||
|
|
||||||
|
const ProcessBoard = () => {
|
||||||
|
const [isClear, setClear] = useState(true)
|
||||||
|
const router = useRouter();
|
||||||
|
const productTableRef = useRef();
|
||||||
|
const tableTimeoutRef = useRef();
|
||||||
|
const spacing = [30, 50]; //工序流程图间距
|
||||||
|
const {
|
||||||
|
processBoard: {
|
||||||
|
workCenter,
|
||||||
|
taskTableNext,
|
||||||
|
reloadTime,
|
||||||
|
pages = {},
|
||||||
|
energyParams = []
|
||||||
|
} = {}
|
||||||
|
} = global.config || {};
|
||||||
|
const {workCenterCode, date = moment().format('YYYY-MM-DD')} = router.query;
|
||||||
|
const title = pages['title'];
|
||||||
|
|
||||||
|
const [{productionRecords = {}, maintenance = [], coreProcess = [], energy = {}} = {},
|
||||||
|
reload,
|
||||||
|
] = useSocket({socketScreenType, workCenterCode, date});
|
||||||
|
|
||||||
|
//核心工序数据分组
|
||||||
|
const [ruleLength, setRuleLength] = useState(6); //一行摆放多少个
|
||||||
|
const memoData = useMemo(() => {
|
||||||
|
let res = [];
|
||||||
|
if (coreProcess.length > 0) {
|
||||||
|
let newCoreProcess = _.cloneDeep(coreProcess) || [];
|
||||||
|
let ccIndex = _.findIndex(coreProcess, item => item.code == "L7-CC"); //除磁->双层除磁
|
||||||
|
newCoreProcess[ccIndex].code = 'L7-SCCC'
|
||||||
|
newCoreProcess[ccIndex].name = '双层除磁'
|
||||||
|
let xmIndex = _.findIndex(coreProcess, item => item.code == "L7-XM"); //细磨
|
||||||
|
newCoreProcess.splice(xmIndex + 1, 0, coreProcess[ccIndex]) //细磨后面添加除磁
|
||||||
|
|
||||||
|
newCoreProcess.forEach((item, index) => {
|
||||||
|
let row = Math.ceil((index + 1) / ruleLength);
|
||||||
|
let rowIndex = index % ruleLength;
|
||||||
|
if (!res[row]) res[row] = [];
|
||||||
|
res[row][rowIndex] = item;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, [coreProcess, ruleLength]);
|
||||||
|
console.log("memoData", memoData)
|
||||||
|
|
||||||
|
//关键生产信息
|
||||||
|
const prodData = useMemo(() => {
|
||||||
|
return getProdOpt(workCenter, productionRecords);
|
||||||
|
}, [productionRecords]);
|
||||||
|
|
||||||
|
//关键能耗信息
|
||||||
|
const usedInfo = useMemo(() => {
|
||||||
|
return getUsedOpt(energy, energyParams);
|
||||||
|
}, [energy])
|
||||||
|
|
||||||
|
//表格定时刷新
|
||||||
|
useEffect(() => {
|
||||||
|
tableTimeoutRef.current = setInterval(() => {
|
||||||
|
productTableRef.current && productTableRef.current.nextPage();
|
||||||
|
}, taskTableNext);
|
||||||
|
return () => {
|
||||||
|
clearInterval(tableTimeoutRef.current)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
//整体刷新
|
||||||
|
useEffect(() => {
|
||||||
|
tableTimeoutRef.current = setInterval(() => {
|
||||||
|
reload()
|
||||||
|
}, reloadTime);
|
||||||
|
return () => {
|
||||||
|
clearInterval(tableTimeoutRef.current)
|
||||||
|
}
|
||||||
|
}, [reload])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="screen-container">
|
||||||
|
{isClear && (
|
||||||
|
<React.Fragment>
|
||||||
|
<Header title={title}/>
|
||||||
|
<div className="screen-container-content">
|
||||||
|
<Row className='height-100' gutter={gutter} bottom={bottom}>
|
||||||
|
<Row.Col span={17}>
|
||||||
|
<Card title="核心工序流程图" overVisible={true}>
|
||||||
|
<div className='process-flow-list'>
|
||||||
|
<div className="list-flex">
|
||||||
|
{memoData.map((row, rowIndex) => {
|
||||||
|
let reverse = rowIndex % 2 > 0;
|
||||||
|
return (
|
||||||
|
<div className={`list-flex-row ${reverse ? '' : 'reverse'}`}
|
||||||
|
key={rowIndex}>
|
||||||
|
{row.map(item => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={"list-flex-item"}
|
||||||
|
key={item['code']}>
|
||||||
|
<ProcessCard itemData={item}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
|
||||||
|
<Row.Col span={7}>
|
||||||
|
<Card>
|
||||||
|
<div className="used-content">
|
||||||
|
<Row className="height-30">
|
||||||
|
<Card title="关键生产信息" titleSpace={true} overVisible={true}
|
||||||
|
withBorder={false}>
|
||||||
|
<Chart option={prodData}/>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-30">
|
||||||
|
<Card title="关键能耗信息" titleSpace={true} overVisible={true}
|
||||||
|
withBorder={false} padding={"0 16px"}>
|
||||||
|
<Row className='height-100'>
|
||||||
|
{usedInfo.map((opt, index) => {
|
||||||
|
return (
|
||||||
|
<Row.Col span={8} key={index}>
|
||||||
|
<Chart option={opt}/>
|
||||||
|
</Row.Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-40">
|
||||||
|
<Card title="车间维保计划" titleSpace={true} overVisible={true}
|
||||||
|
withBorder={false} padding={"16px"}>
|
||||||
|
<Table
|
||||||
|
ref={productTableRef}
|
||||||
|
mainKey="taskCode"
|
||||||
|
columns={prodTableColumns}
|
||||||
|
dataSource={maintenance}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Footer/>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-container {
|
||||||
|
height: 100vh;
|
||||||
|
min-height: 1080px;
|
||||||
|
min-width: 1920px;
|
||||||
|
background: url("/img/bg.png") center center;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 96px;
|
||||||
|
padding-bottom: 82px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-container-content {
|
||||||
|
height: 100%;
|
||||||
|
padding: ${gutter}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-flow-list {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 50px 30px 30px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-flex {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-flex-row {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -${spacing[0] / 2}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-flex-row.reverse {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-flex-item {
|
||||||
|
width: ${100 / ruleLength}%;
|
||||||
|
padding: 0 ${spacing[0] / 2}px;
|
||||||
|
margin-bottom: ${spacing[1]}px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-flex-row:last-child .list-flex-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-flex-item::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右箭头
|
||||||
|
.list-flex-row:not(.reverse) .list-flex-item::after {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
right: 0;
|
||||||
|
left: unset;
|
||||||
|
bottom: unset;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
background: url('/img/arrow.png') center center no-repeat;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reverse {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左箭头
|
||||||
|
.list-flex-row.reverse .list-flex-item::after {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
left: -${spacing[1] / 2}px;
|
||||||
|
right: unset;
|
||||||
|
top: 50%;
|
||||||
|
bottom: unset;
|
||||||
|
transform: translate(-50%, -50%) rotate(180deg);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
background: url('/img/arrow.png') center center no-repeat;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下箭头
|
||||||
|
.list-flex-row .list-flex-item:last-child::after {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
left: unset;
|
||||||
|
right: calc(50% - 44px);
|
||||||
|
top: unset;
|
||||||
|
bottom: -${(spacing[1] - 44) / 2}px;
|
||||||
|
transform: translate(-50%, 100%) rotate(90deg);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
background: url('/img/arrow.png') center center no-repeat;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无箭头
|
||||||
|
.list-flex-row:last-child .list-flex-item:last-child::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.used-content {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.used-content :global(.screen-card) {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProcessBoard;
|
|
@ -0,0 +1,809 @@
|
||||||
|
import {useRouter} from "next/router";
|
||||||
|
import React, {useEffect, useMemo, useRef, useState} from "react";
|
||||||
|
import {Carousel} from 'antd'
|
||||||
|
import _ from "lodash";
|
||||||
|
import Footer from "../../../components/screen/Footer";
|
||||||
|
import Header from "../../../components/screen/Header";
|
||||||
|
import Chart from "../../../components/screen/Chart";
|
||||||
|
import Row from "../../../components/screen/Row";
|
||||||
|
import Card from '../../../components/screen/Card';
|
||||||
|
import ProcessTaskCard from "../../components/processTaskCard";
|
||||||
|
import {useSocket} from "../../../utils/hooks";
|
||||||
|
import moment from "moment/moment";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生产分工序
|
||||||
|
*/
|
||||||
|
const socketScreenType = 'BranchOperationType';
|
||||||
|
const gutter = 20;
|
||||||
|
const bottom = 0;
|
||||||
|
const chartsBgColor = '#102242';
|
||||||
|
|
||||||
|
//关键生产信息
|
||||||
|
const getProdOpt = ({xData, yData}) => {
|
||||||
|
let opt = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: "L7车间生产进度",
|
||||||
|
textStyle: {
|
||||||
|
fontSize: "14px"
|
||||||
|
},
|
||||||
|
top: 10,
|
||||||
|
left: 60
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: 40,
|
||||||
|
left: 55
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
type: 'bar'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
opt.xAxis.data = xData
|
||||||
|
opt.series[0].data = yData
|
||||||
|
return opt;
|
||||||
|
};
|
||||||
|
|
||||||
|
//车间OEE
|
||||||
|
const getOEEOpt = (data = {}, params = []) => {
|
||||||
|
const {oeeOfMonth = {}, oeeOfDay = {}} = data;
|
||||||
|
// passRate 良品率, performanceRate 性能稼动率, timeRate 时间稼动率, oee OEE
|
||||||
|
if (params.length > 0) {
|
||||||
|
params[0].values = [oeeOfDay.timeRate || 0, oeeOfDay.performanceRate || 0, oeeOfDay.passRate || 0] //当日
|
||||||
|
params[0].result = [oeeOfDay.oee || 0]
|
||||||
|
params[1].values = [oeeOfMonth.timeRate || 0, oeeOfMonth.performanceRate || 0, oeeOfMonth.passRate || 0] //当日
|
||||||
|
params[1].result = [oeeOfMonth.oee || 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = {
|
||||||
|
title: {
|
||||||
|
text: "",
|
||||||
|
left: "center",
|
||||||
|
top: 180,
|
||||||
|
},
|
||||||
|
graphic: {
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
left: "center",
|
||||||
|
top: 85,
|
||||||
|
style: {
|
||||||
|
fill: "#fff",
|
||||||
|
text: "",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
// icon: "rect",
|
||||||
|
data: ["时间稼动率", "性能稼动率", "良品率"],
|
||||||
|
bottom: 10,
|
||||||
|
orient: "vertical",
|
||||||
|
itemHeight: 16,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
polar: {
|
||||||
|
radius: ["25%", "65%"],
|
||||||
|
center: ["50%", "30%"],
|
||||||
|
},
|
||||||
|
angleAxis: {
|
||||||
|
show: false,
|
||||||
|
max: 100,
|
||||||
|
startAngle: 90,
|
||||||
|
},
|
||||||
|
radiusAxis: {
|
||||||
|
show: false,
|
||||||
|
type: "category",
|
||||||
|
data: ["%"],
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
formatter: "{a}",
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "时间稼动率",
|
||||||
|
type: "bar",
|
||||||
|
barGap: "0",
|
||||||
|
barWidth: 14,
|
||||||
|
data: [12],
|
||||||
|
coordinateSystem: "polar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "性能稼动率",
|
||||||
|
type: "bar",
|
||||||
|
barGap: "0",
|
||||||
|
barWidth: 14,
|
||||||
|
data: [13],
|
||||||
|
coordinateSystem: "polar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "良品率",
|
||||||
|
type: "bar",
|
||||||
|
barGap: "0",
|
||||||
|
barWidth: 14,
|
||||||
|
data: [14],
|
||||||
|
coordinateSystem: "polar",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let res = [];
|
||||||
|
params.forEach(({name, values, result}) => {
|
||||||
|
let newOpt = _.cloneDeep(opt);
|
||||||
|
newOpt.title.text = name;
|
||||||
|
newOpt.graphic.elements[0].style.text = result + "%";
|
||||||
|
const names = ["时间稼动率 ", "性能稼动率 ", "良品率 "];
|
||||||
|
newOpt.legend.data = names.map(
|
||||||
|
(name, index) => (name = name + ` ${values[index]}%`)
|
||||||
|
);
|
||||||
|
newOpt.series = newOpt.series.map((item, index) => {
|
||||||
|
item.name = names[index] + ` ${values[index]}%`;
|
||||||
|
item.data[0] = values[index];
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
res.push(newOpt);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
//能耗信息
|
||||||
|
const getUsedOpt = (data = {}, params = []) => {
|
||||||
|
const {
|
||||||
|
energyOfMonth = 0, //电(KW/H) 今日用量
|
||||||
|
energyOfDay = 0, // 当月累计用量
|
||||||
|
gasOfMonth = 0, //气 (m3)
|
||||||
|
gasOfDay = 0,
|
||||||
|
waterOfDay = 0, //水(T)
|
||||||
|
waterOfMonth = 0,
|
||||||
|
} = data;
|
||||||
|
if (params.length > 0) {
|
||||||
|
params[0].values[0].value = gasOfMonth;
|
||||||
|
params[0].values[1].value = gasOfDay;
|
||||||
|
params[1].values[0].value = waterOfMonth;
|
||||||
|
params[1].values[1].value = waterOfDay;
|
||||||
|
params[2].values[0].value = energyOfMonth;
|
||||||
|
params[2].values[1].value = energyOfDay;
|
||||||
|
}
|
||||||
|
let opt = {
|
||||||
|
title: {
|
||||||
|
text: "",
|
||||||
|
left: "center",
|
||||||
|
top: 70,
|
||||||
|
textStyle: {
|
||||||
|
fontWeight: "normal",
|
||||||
|
fontSize: "16px"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "item",
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
bottom: 15,
|
||||||
|
left: "center",
|
||||||
|
orient: "vertical",
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 14
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "pie",
|
||||||
|
radius: ["45%", "70%"],
|
||||||
|
center: ["50%", "40%"],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: "center",
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let res = [];
|
||||||
|
let colors = ["#6FE621", "#FF005A", "#4D7BF3", "#4FCCFF"]
|
||||||
|
params.forEach(({name, unit, values}, index) => {
|
||||||
|
let newOpt = _.cloneDeep(opt);
|
||||||
|
newOpt.color = [colors[index], "#FFC760"];
|
||||||
|
newOpt.title.text = `${name}\n(${unit})`;
|
||||||
|
newOpt.series[0].data = values;
|
||||||
|
newOpt.legend.formatter = name => {
|
||||||
|
let {value = ""} = values.find(item => item.name === name) || {};
|
||||||
|
return `${name}: ${value}`
|
||||||
|
}
|
||||||
|
res.push(newOpt);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
//设备监控参数
|
||||||
|
const getEquipMonitorOpt = (data = []) => {
|
||||||
|
let option = {
|
||||||
|
type: "equip",
|
||||||
|
backgroundColor: chartsBgColor,
|
||||||
|
grid: {
|
||||||
|
top: 25,
|
||||||
|
left: 60,
|
||||||
|
right: "5%"
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: "",
|
||||||
|
x: "center",
|
||||||
|
y: "top",
|
||||||
|
textStyle: {
|
||||||
|
fontSize: "14px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
symbol: "circle",
|
||||||
|
symbolSize: 5,
|
||||||
|
data: [],
|
||||||
|
type: "line",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let newOpt = []
|
||||||
|
if (data.length > 0) {
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
let opt = _.cloneDeep(option);
|
||||||
|
opt.title.text = item.name;
|
||||||
|
opt.xAxis.data = item.time.slice().reverse();
|
||||||
|
opt.series[0].data = item.data.slice().reverse();
|
||||||
|
newOpt.push(opt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return newOpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
//质量监控参数
|
||||||
|
const getQualityMonitorOpt = (data = {}) => {
|
||||||
|
let res = [];
|
||||||
|
let option = {
|
||||||
|
type: "quality",
|
||||||
|
backgroundColor: chartsBgColor,
|
||||||
|
title: {
|
||||||
|
show: false,
|
||||||
|
/* text: '',
|
||||||
|
x: 'right',
|
||||||
|
textStyle: {
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: "14px"
|
||||||
|
}*/
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
markLine: {},
|
||||||
|
legend: {
|
||||||
|
x: 'left',
|
||||||
|
y: 'bottom',
|
||||||
|
// data: ['极差值', 'UCLmr', 'LCLmr', 'CLmr'],
|
||||||
|
textStyle: { //文字 样式
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: "12px"
|
||||||
|
},
|
||||||
|
itemStyle: { //圆点样式
|
||||||
|
opacity: 0
|
||||||
|
},
|
||||||
|
lineStyle: { //线样式
|
||||||
|
// type: [4, 4],
|
||||||
|
dashOffset: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: 15,
|
||||||
|
left: '3%',
|
||||||
|
right: '3%',
|
||||||
|
bottom: '15%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [],
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
type: 'solid',
|
||||||
|
color: '#ffffff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
splitNumber: 8,
|
||||||
|
//轴线
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
type: 'solid',
|
||||||
|
color: '#ffffff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#ffffff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: []
|
||||||
|
};
|
||||||
|
if (data.spcBasicDatas && data.topData) {
|
||||||
|
let {spcBasicDatas = [], topData = {}} = data;
|
||||||
|
let {
|
||||||
|
usl = 0, lsl = 0,
|
||||||
|
uclx = 0, averx = 0, lclx = 0,
|
||||||
|
uclr = 0, averr = 0, lclr = 0,
|
||||||
|
} = topData
|
||||||
|
|
||||||
|
let xData = [], yValData = [], yMrValData = [];
|
||||||
|
spcBasicDatas.forEach(item => {
|
||||||
|
if (!item.seq) item.seq = 0
|
||||||
|
if (!item.value) item.value = 0
|
||||||
|
if (!item.mrValue) item.mrValue = 0
|
||||||
|
xData.push(item.seq)
|
||||||
|
yValData.push(item.value)
|
||||||
|
yMrValData.push(item.mrValue)
|
||||||
|
})
|
||||||
|
//标准点数据处理
|
||||||
|
let basicOpt = _.cloneDeep(option);
|
||||||
|
const basicName = ['数据点', 'USLi', 'UCli', 'CLi', 'LCLi', 'LSLi'];
|
||||||
|
basicOpt.xAxis.data = xData
|
||||||
|
let min = _.min(yValData)
|
||||||
|
basicOpt.yAxis.min = min;
|
||||||
|
basicName.forEach((item, index) => {
|
||||||
|
basicOpt.series[index] = {
|
||||||
|
type: 'line',
|
||||||
|
smooth: true, //平滑
|
||||||
|
symbolSize: 4,
|
||||||
|
symbol: 'circle',
|
||||||
|
data: [],
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed', //'dotted'点线 'dashed' 虚线 'solid' 实线
|
||||||
|
width: 1
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
symbol: 'none',
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
lineStyle: {},
|
||||||
|
data: [{
|
||||||
|
yAxis: 0,
|
||||||
|
x: '6.8%'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
basicOpt.series[index].name = basicName[index]
|
||||||
|
})
|
||||||
|
basicOpt.series[0].data = yValData;
|
||||||
|
basicOpt.series[0].lineStyle.type = "solid";
|
||||||
|
basicOpt.series[1].markLine.data[0].yAxis = usl
|
||||||
|
basicOpt.series[2].markLine.data[0].yAxis = lsl
|
||||||
|
basicOpt.series[3].markLine.data[0].yAxis = uclx
|
||||||
|
basicOpt.series[4].markLine.data[0].yAxis = averx
|
||||||
|
basicOpt.series[5].markLine.data[0].yAxis = lclx
|
||||||
|
|
||||||
|
//极差点数据处理
|
||||||
|
let mrOpt = _.cloneDeep(option);
|
||||||
|
const mrName = ['极差点', 'UCLmr', 'CLmr', 'LCLmr'];
|
||||||
|
mrOpt.xAxis.data = xData
|
||||||
|
mrName.forEach((item, index) => {
|
||||||
|
mrOpt.series[index] = {
|
||||||
|
type: 'line',
|
||||||
|
smooth: true, //平滑
|
||||||
|
symbolSize: 4,
|
||||||
|
data: [],
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed', //'dashed'点线 'dashed' 虚线 'solid' 实线
|
||||||
|
width: 1
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
symbol: 'none',
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: [{
|
||||||
|
yAxis: 0,
|
||||||
|
x: '6.8%'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mrOpt.series[index].name = mrName[index]
|
||||||
|
})
|
||||||
|
mrOpt.series[0].data = yMrValData;
|
||||||
|
mrOpt.series[0].lineStyle.type = "solid";
|
||||||
|
mrOpt.series[1].markLine.data[0].yAxis = uclr
|
||||||
|
mrOpt.series[2].markLine.data[0].yAxis = averr
|
||||||
|
mrOpt.series[3].markLine.data[0].yAxis = lclr
|
||||||
|
|
||||||
|
res = _.concat([], [basicOpt], [mrOpt])
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProcessItem = () => {
|
||||||
|
const tableTimeoutRef = useRef();
|
||||||
|
const equipMonitorRef = useRef();
|
||||||
|
const router = useRouter();
|
||||||
|
const [isClear, setClear] = useState(false)
|
||||||
|
const [length, setLength] = useState(4) //卡片轮播个数
|
||||||
|
const [len, setLen] = useState(2) //监控轮播个数
|
||||||
|
const [height, setHeight] = useState(0);
|
||||||
|
const [width, setWidth] = useState(0);
|
||||||
|
const [monitorTitle, setMonitorTitle] = useState("设备")
|
||||||
|
const [isQuality, setIsQuality] = useState(false)
|
||||||
|
const {operationCode, workCenterCode, date = moment().format('YYYY-MM-DD')} = router.query;
|
||||||
|
const {
|
||||||
|
processItem: {
|
||||||
|
reloadTime, cardAutoNext, pages = {}, energyParams = [], OEEParams = []
|
||||||
|
} = {}
|
||||||
|
} = global.config || {};
|
||||||
|
//监控类型名,质量、设备
|
||||||
|
let title = "";
|
||||||
|
if (operationCode && pages[operationCode]) {
|
||||||
|
title = pages[operationCode].title;
|
||||||
|
}
|
||||||
|
const [
|
||||||
|
{
|
||||||
|
keyEnergy = {}, //关键能耗
|
||||||
|
keyProduction = {}, //关键生产
|
||||||
|
taskExecution = [], //任务执行状态
|
||||||
|
oee = {}, //车间OEE
|
||||||
|
paramMonitor = {} //监控
|
||||||
|
} = {},
|
||||||
|
reload,
|
||||||
|
] = useSocket({socketScreenType, operationCode, workCenterCode, date});
|
||||||
|
const {iotEquipResultData = [], spcResultData = {}} = paramMonitor; //监控
|
||||||
|
const {topData = {}} = spcResultData;
|
||||||
|
//质量监控检测项
|
||||||
|
let featureName = "";
|
||||||
|
if (topData.featureName) {
|
||||||
|
featureName = topData.featureName
|
||||||
|
}
|
||||||
|
|
||||||
|
//工序任务执行状态
|
||||||
|
const taskExecutData = useMemo(() => {
|
||||||
|
let res = [];
|
||||||
|
taskExecution.forEach((item, index) => {
|
||||||
|
let row = Math.ceil((index + 1) / length);
|
||||||
|
let rowIndex = index % length;
|
||||||
|
if (!res[row]) res[row] = [];
|
||||||
|
res[row][rowIndex] = item;
|
||||||
|
})
|
||||||
|
res = res.filter(item => item);
|
||||||
|
return res;
|
||||||
|
}, [taskExecution]);
|
||||||
|
|
||||||
|
//设备、质量参数监控
|
||||||
|
const monitorOpt = useMemo(() => {
|
||||||
|
let equipData = getEquipMonitorOpt(iotEquipResultData);
|
||||||
|
let qualityData = getQualityMonitorOpt(spcResultData);
|
||||||
|
|
||||||
|
let res = [], equipRes = [], qualityRes = [];
|
||||||
|
//设备参数分组
|
||||||
|
equipData.forEach((item, index) => {
|
||||||
|
let row = Math.ceil((index + 1) / len);
|
||||||
|
let rowIndex = index % len;
|
||||||
|
if (!equipRes[row]) equipRes[row] = [];
|
||||||
|
equipRes[row][rowIndex] = item;
|
||||||
|
})
|
||||||
|
//质量参数分组
|
||||||
|
qualityData.forEach((item, index) => {
|
||||||
|
let row = Math.ceil((index + 1) / len);
|
||||||
|
let rowIndex = index % len;
|
||||||
|
if (!qualityRes[row]) qualityRes[row] = [];
|
||||||
|
qualityRes[row][rowIndex] = item;
|
||||||
|
})
|
||||||
|
res = _.concat([], equipRes, qualityRes)
|
||||||
|
res = res.filter(item => item);
|
||||||
|
return res
|
||||||
|
}, [iotEquipResultData, spcResultData])
|
||||||
|
|
||||||
|
//关键生产信息
|
||||||
|
const prodOpt = useMemo(() => {
|
||||||
|
return getProdOpt(keyProduction)
|
||||||
|
}, [keyProduction])
|
||||||
|
|
||||||
|
//车间能耗信息
|
||||||
|
const usedOpt = useMemo(() => {
|
||||||
|
return getUsedOpt(keyEnergy, energyParams);
|
||||||
|
}, [keyEnergy])
|
||||||
|
|
||||||
|
//车间OEE
|
||||||
|
const OEEOpt = useMemo(() => {
|
||||||
|
return getOEEOpt(oee, OEEParams);
|
||||||
|
}, [oee]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (operationCode) {
|
||||||
|
setClear(true);
|
||||||
|
}
|
||||||
|
}, [operationCode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
//轮播图标的宽高设置
|
||||||
|
if (equipMonitorRef.current) {
|
||||||
|
setHeight(equipMonitorRef.current.clientHeight)
|
||||||
|
setWidth(equipMonitorRef.current.clientWidth)
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
//整体刷新
|
||||||
|
useEffect(() => {
|
||||||
|
tableTimeoutRef.current = setInterval(() => {
|
||||||
|
reload()
|
||||||
|
}, reloadTime);
|
||||||
|
return () => {
|
||||||
|
clearInterval(tableTimeoutRef.current)
|
||||||
|
}
|
||||||
|
}, [reload])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (iotEquipResultData.length <= 0 && _.get(spcResultData, "spcBasicDatas.length") > 0) {
|
||||||
|
setMonitorTitle('质量')
|
||||||
|
setIsQuality(true)
|
||||||
|
}
|
||||||
|
}, [iotEquipResultData, spcResultData])
|
||||||
|
|
||||||
|
//当前轮播位置,更新标题
|
||||||
|
const afterChange = (current) => {
|
||||||
|
let index = _.findIndex(monitorOpt, item => item[0].type == 'quality');
|
||||||
|
if (index == current) {
|
||||||
|
setMonitorTitle('质量')
|
||||||
|
setIsQuality(true)
|
||||||
|
} else {
|
||||||
|
setMonitorTitle('设备')
|
||||||
|
setIsQuality(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="screen-container">
|
||||||
|
{isClear && (
|
||||||
|
<React.Fragment>
|
||||||
|
<Header title={`${title}工序`}/>
|
||||||
|
<div className="screen-container-content">
|
||||||
|
<Row className='height-100' gutter={gutter} bottom={bottom}>
|
||||||
|
<Row.Col span={5}>
|
||||||
|
<Row className="height-55">
|
||||||
|
<Card title={`${title}工序设备示意图`} overVisible={true}
|
||||||
|
padding={"40px 20px 25px 20px"}>
|
||||||
|
<div className='screen-img img-gxsb'/>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-45" style={{paddingTop: `${gutter}px`}}>
|
||||||
|
<Card title={`${title}工序成品示意图`} overVisible={true}
|
||||||
|
padding={"40px 20px 25px 20px"}>
|
||||||
|
<div className='screen-img img-gxzzp'/>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
</Row.Col>
|
||||||
|
|
||||||
|
<Row.Col span={12}>
|
||||||
|
<Row className="height-55">
|
||||||
|
<Card title="工序任务执行状态" overVisible={true} padding={"40px 20px 25px 20px"}>
|
||||||
|
<div className='process-task-list'>
|
||||||
|
<Carousel autoplay dots={false} autoplaySpeed={cardAutoNext}>
|
||||||
|
{taskExecutData.map((row, rowIndex) => {
|
||||||
|
return <div key={Math.random()}>
|
||||||
|
<div className='process-task'>
|
||||||
|
{row.map((item, index) => {
|
||||||
|
return <div className='process-task-item'
|
||||||
|
key={item.code}>
|
||||||
|
<ProcessTaskCard itemData={item}
|
||||||
|
operationCode={operationCode}/>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-45" style={{paddingTop: `${gutter}px`}}>
|
||||||
|
<Card title={`${monitorTitle}参数监控`} overVisible={true}
|
||||||
|
padding={"35px 16px 16px 16px"}>
|
||||||
|
{
|
||||||
|
isQuality &&
|
||||||
|
<div className='monitor-quality-item'>
|
||||||
|
{`最新检测项:${featureName}`}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div className='equip-monitor-list' ref={equipMonitorRef}>
|
||||||
|
<Row className='height-100'>
|
||||||
|
<Carousel autoplay dots={false} dotPosition={"right"}
|
||||||
|
autoplaySpeed={cardAutoNext} adaptiveHeight={true}
|
||||||
|
afterChange={afterChange}>
|
||||||
|
{monitorOpt.map((row, rowIndex) => {
|
||||||
|
return <div>
|
||||||
|
<div key={Math.random()}
|
||||||
|
style={{height: height, width: width}}>
|
||||||
|
{row.map((item, index) => {
|
||||||
|
return <div className='equip-monitor-item'
|
||||||
|
key={rowIndex + index}>
|
||||||
|
<Chart option={item}/>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</Carousel>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
</Row.Col>
|
||||||
|
|
||||||
|
<Row.Col span={7}>
|
||||||
|
<Card>
|
||||||
|
<div className="used-content">
|
||||||
|
<Row className="height-30">
|
||||||
|
<Card title="关键生产信息" titleSpace={true} overVisible={true}
|
||||||
|
withBorder={false}>
|
||||||
|
<Chart option={prodOpt}/>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-30">
|
||||||
|
<Card title="关键能耗信息" titleSpace={true} overVisible={true}
|
||||||
|
withBorder={false} padding={"0 16px"}>
|
||||||
|
<Row className='height-100'>
|
||||||
|
{usedOpt.map((opt, index) => {
|
||||||
|
return (
|
||||||
|
<Row.Col span={8} key={index}>
|
||||||
|
<Chart option={opt}/>
|
||||||
|
</Row.Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="height-40">
|
||||||
|
<Card title="车间OEE" titleSpace={true} overVisible={true}
|
||||||
|
withBorder={false} padding={"0 16px"}>
|
||||||
|
<Row className='height-100'>
|
||||||
|
{OEEOpt.map((opt, index) => {
|
||||||
|
return (
|
||||||
|
<Row.Col span={12} key={index}>
|
||||||
|
<Chart option={opt}/>
|
||||||
|
</Row.Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Row.Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Footer/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<style jsx>{`
|
||||||
|
.screen-container {
|
||||||
|
font-family: PingFangSC-Semibold;
|
||||||
|
height: 100vh;
|
||||||
|
min-height: 1080px;
|
||||||
|
min-width: 1920px;
|
||||||
|
background: url("/img/bg.png") center center;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 96px;
|
||||||
|
padding-bottom: 82px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-container-content {
|
||||||
|
height: 100%;
|
||||||
|
padding: ${gutter}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.OEE-content {
|
||||||
|
position: absolute;
|
||||||
|
width: 900px;
|
||||||
|
height: 320px;
|
||||||
|
top: 660px;
|
||||||
|
left: 460px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monitor-quality-item {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: -22px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equip-monitor-list {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equip-monitor-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-task-list {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-task {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-task-item {
|
||||||
|
width: calc(100% / 4);
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.used-content {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.used-content :global(.screen-card) {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aaa {
|
||||||
|
width: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-gxsb {
|
||||||
|
background: url(/img/process/${operationCode}-SB.png) center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-gxzzp {
|
||||||
|
background: url(/img/process/${operationCode}-ZZP.png) center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProcessItem;
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
(function () {
|
||||||
|
const gethost = () => {
|
||||||
|
let host = window.location.host;
|
||||||
|
// let host = '120.202.38.15:8081';
|
||||||
|
// host = '120.202.38.15:8081';
|
||||||
|
console.log(host)
|
||||||
|
if (host.indexOf("localhost") != -1 || host.indexOf("137.65") != -1) {
|
||||||
|
host = '120.202.38.15:8081';
|
||||||
|
// host = '10.10.21.8';
|
||||||
|
}
|
||||||
|
return host;
|
||||||
|
};
|
||||||
|
window.host = gethost();
|
||||||
|
window.config = {
|
||||||
|
// "ioSocketUrl": "http://192.168.137.212:4553",
|
||||||
|
// "ioSocketUrl": "http://192.168.136.234:4553",
|
||||||
|
// "ioSocketUrl": "http://192.168.132.204:4553",
|
||||||
|
// "ioSocketUrl": "http://192.168.132.204:4553",
|
||||||
|
// "mesUrl": "http://120.202.38.15:8081/gateway",
|
||||||
|
// "mesUrl": "http://192.168.132.204/gateway",
|
||||||
|
// "ioSocketUrl": "http://10.10.21.73:4553",
|
||||||
|
// "ioSocketUrl": "http://192.168.136.213:4553",
|
||||||
|
// "ioSocketUrl": "http://120.202.38.15:8081/",
|
||||||
|
mesHost: "http://" + gethost(),
|
||||||
|
ioSocketUrl: "http://" + gethost(),
|
||||||
|
mesUrl: "http://" + gethost() + "/gateway",
|
||||||
|
edgeQueryUrl: "http://" + gethost() + "/iot/edge/application/api",
|
||||||
|
edgeUrl: "http://" + gethost() + "/iot/edge/cxf/rongtong-edge-application",
|
||||||
|
edgewsUrl: "ws://" + gethost() + "/rongtong-edge-application-ws/",
|
||||||
|
equipservice: "/gateway/md-mdm-deploy-v1/md/bmEquipment",
|
||||||
|
ImgUrl: "/console/fs/file/viewimage?fileCode=",
|
||||||
|
refreshInterval: 15000,
|
||||||
|
packagingOperaConfig: {
|
||||||
|
reloadTime: 60 * 1000,
|
||||||
|
},
|
||||||
|
equipmentConfig: {
|
||||||
|
equipAbnormalNext: 4000, // 设备异常监控翻页时间
|
||||||
|
equipListNext: 4000, // 设备运行状态翻页时间
|
||||||
|
equipListSpan: 4, // 设备运行状态栅格值,24分:如设置 4 则每行展示 24/4 = 6 个
|
||||||
|
equipItemLength: 6, // 设备运行状态卡片内容条数
|
||||||
|
},
|
||||||
|
planBoard: {
|
||||||
|
productTableNext: 4000,
|
||||||
|
},
|
||||||
|
planDailyConfig: {
|
||||||
|
reloadTime: 5000, // 计划看板轮询时间
|
||||||
|
isSocket: true, // 是否使用socket服务
|
||||||
|
},
|
||||||
|
// 质量看板
|
||||||
|
qualityBoard: {
|
||||||
|
threeDayRejectTableNext: 4000, // 近三天不良率表格翻页时间
|
||||||
|
taskRemindTableNext: 4000, // 任务提醒表格翻页时间
|
||||||
|
},
|
||||||
|
// 工序总图看板
|
||||||
|
processBoard: {
|
||||||
|
workCenter: "L7车间",
|
||||||
|
reloadTime: 300000, //整体刷新时间
|
||||||
|
taskTableNext: 10000, //表格翻页时间
|
||||||
|
energyParams: [ //能耗参数
|
||||||
|
{
|
||||||
|
name: "电",
|
||||||
|
unit: "KW/h",
|
||||||
|
values: [
|
||||||
|
{name: "当月累计用量", value: 0},
|
||||||
|
{name: "今日用量", value: 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "水",
|
||||||
|
unit: "KG",
|
||||||
|
values: [
|
||||||
|
{name: "当月累计用量", value: 0},
|
||||||
|
{name: "今日用量", value: 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "气",
|
||||||
|
unit: "m³",
|
||||||
|
values: [
|
||||||
|
{name: "当月累计用量", value: 0},
|
||||||
|
{name: "今日用量", value: 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pages: {
|
||||||
|
// 键名对应路由 screen/processBoard?pid={key}
|
||||||
|
// 同时对应图片 /img/processBg/{key}.png
|
||||||
|
title: "工序总图",
|
||||||
|
withChart: {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
processItem: {
|
||||||
|
reloadTime: 300000, //整体刷新时间
|
||||||
|
cardAutoNext: 5000, //卡片轮播时间间隔
|
||||||
|
//能耗参数
|
||||||
|
energyParams: [
|
||||||
|
{
|
||||||
|
name: "电",
|
||||||
|
unit: "KW/h",
|
||||||
|
values: [
|
||||||
|
{name: "当月累计用量", value: 0},
|
||||||
|
{name: "今日用量", value: 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "水",
|
||||||
|
unit: "KG",
|
||||||
|
values: [
|
||||||
|
{name: "当月累计用量", value: 0},
|
||||||
|
{name: "今日用量", value: 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "气",
|
||||||
|
unit: "m³",
|
||||||
|
values: [
|
||||||
|
{name: "当月累计用量", value: 0},
|
||||||
|
{name: "今日用量", value: 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 设备OEE数据配置
|
||||||
|
OEEParams: [
|
||||||
|
{
|
||||||
|
name: "当日OEE", // 标题
|
||||||
|
values: [], // 依次:时间, 性能, 良品 百分比值 必须填三个
|
||||||
|
result: "0", // 图形中间值
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "当月OEE",
|
||||||
|
values: [],
|
||||||
|
result: "0",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pages: {
|
||||||
|
"L7-TL": {
|
||||||
|
title: "投料",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-YH": {
|
||||||
|
title: "预混",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-CM": {
|
||||||
|
title: "粗磨",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-XM": {
|
||||||
|
title: "细磨",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-PWGZ": {
|
||||||
|
title: "喷雾干燥",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-SJ": {
|
||||||
|
title: "烧结",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-FS": {
|
||||||
|
title: "粉碎",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-CC": {
|
||||||
|
title: "除磁",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-HL": {
|
||||||
|
title: "混料",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
"L7-BZ": {
|
||||||
|
title: "包装",
|
||||||
|
withChart: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,48 @@
|
||||||
|
Copyright (c) 2015 by Hubert & Fischer. All rights reserved.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 707 B |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 256 KiB |
After Width: | Height: | Size: 163 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 300 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 304 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 250 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 184 KiB |