This commit is contained in:
gtz 2024-03-19 15:57:57 +08:00
commit 5652cff730
49 changed files with 3215 additions and 338 deletions

Binary file not shown.

Before

(image error) Size: 2.3 MiB

Binary file not shown.

Before

(image error) Size: 2.0 MiB

Binary file not shown.

Before

(image error) Size: 1.5 MiB

Binary file not shown.

Before

(image error) Size: 1.4 MiB

Binary file not shown.

Before

(image error) Size: 1.3 MiB

Binary file not shown.

Before

(image error) Size: 1.2 MiB

Binary file not shown.

Before

(image error) Size: 1.9 MiB

Binary file not shown.

Before

(image error) Size: 2.9 MiB

Binary file not shown.

Before

(image error) Size: 2.6 MiB

Binary file not shown.

Before

(image error) Size: 1.7 MiB

Binary file not shown.

Before

(image error) Size: 1.4 MiB

Binary file not shown.

Before

(image error) Size: 2.4 MiB

Binary file not shown.

Before

(image error) Size: 761 KiB

Binary file not shown.

Before

(image error) Size: 218 KiB

Binary file not shown.

Before

(image error) Size: 16 KiB

Binary file not shown.

Before

(image error) Size: 103 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

(image error) Size: 45 KiB

Binary file not shown.

Before

(image error) Size: 429 KiB

Binary file not shown.

Before

(image error) Size: 10 KiB

14
dist/index.html vendored
View File

@ -1,14 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
<script type="module" crossorigin src="/assets/index-z875ZJyp.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-b8YKTp8F.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

1
dist/vite.svg vendored
View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

(image error) Size: 1.5 KiB

View File

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@ -6,8 +6,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title> <title>Vite + Vue</title>
</head> </head>
<body> <body style="position: relative">
<div id="app"></div> <div id="app"></div>
<!-- <div
id="glass-layer"
style="position: absolute; top: 0; left: 0; height: 100%"
></div> -->
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

270
src/AlertListScreen.vue Normal file
View File

@ -0,0 +1,270 @@
<!-- 报警列表页面 -->
<script setup>
import { ref, onMounted } from "vue";
import { useWsStore } from "./store";
import { useSettings } from "./store/settings";
import Container from "./components/Base/Container.vue";
import DatetimeTool from "./components/HeadTime.vue";
import AppHeader from "./components/Base/Header.vue";
import Tools from "./components/Tools.vue";
import FullEqList from "./FullEqList.vue";
import { useGlass } from "./utils/glass";
const store = useWsStore();
const GlassContainer = useGlass(store);
const settingStore = useSettings();
const props = defineProps({
line: {
type: Number,
default: 1,
},
});
const mainContainer = ref(null);
const alarmList = ref(
(store.mainDataAlarm.alarmArrList || []).map((item, index) => ({
id: item.id,
eqName: item.equipmentName,
eqIndex: index + 1,
alarmGrade: item.alarmLevel,
alarmDetail: item.alarmDetails,
position: `${item.productLine} - ${item.segment}`,
}))
);
store.$subscribe((mutation, state) => {
alarmList.value = (state.mainDataAlarm.alarmArrList || []).map(
(item, index) => ({
id: item.id,
eqName: item.equipmentName,
eqIndex: index + 1,
alarmGrade: item.alarmLevel,
alarmDetail: item.alarmDetails,
position: `${item.productLine} - ${item.segment}`,
})
);
});
//
onMounted(() => {
const settings = settingStore.settings;
//
handleResolutionChange(settings.resolution.width, settings.resolution.height);
});
function changeScale(elm, width, height) {
const xScale = width / 1920;
const yScale = height / 1080;
const style = {
transform: `scale(${xScale}, ${yScale})`,
transformOrigin: "top left",
};
elm.style.transform = style.transform;
elm.style.transformOrigin = style.transformOrigin;
}
function handleResolutionChange(width, height) {
mainContainer.value &&
changeScale(mainContainer.value, width, height) &&
changeScale(document.getElementById("glass-layer"), width, height);
}
</script>
<template>
<div id="main-container" ref="mainContainer" class="main-container">
<div id="main-container__fulleq">
<FullEqList v-if="0" />
</div>
<DatetimeTool />
<Tools @change-resolution="handleResolutionChange" />
<AppHeader />
<GlassContainer />
<!-- <div
id="glass-layer"
style="position: absolute; top: 0; left: 0; height: 100%"
></div>-->
<div class="alert-list-page">
<Container class="alert-list" title="报警列表" icon="cube">
<div class="alert-list__table" style="">
<el-table
class="dark-table"
:data="alarmList"
row-class-name="dark-row"
header-row-class-name="dark-header"
>
<el-table-column
prop="eqName"
label="设备名"
width="90"
></el-table-column>
<el-table-column
prop="eqIndex"
label="序号"
width="60"
></el-table-column>
<el-table-column
prop="alarmGrade"
label="等级"
width="60"
></el-table-column>
<el-table-column prop="alarmDetail" label="报警细节">
<template v-slot="{ row }">
<el-tooltip
class="box-item"
effect="dark"
:content="row.alarmDetail || '---'"
placement="top-start"
>
<span
style="
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
"
>{{ row.alarmDetail || "---" }}</span
>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="position"
width="128"
label="定位"
></el-table-column>
</el-table>
</div>
<!-- <button @click="handleIgnore" class="alert-btn">忽略</button> -->
</Container>
</div>
</div>
</template>
<style>
.dark-table {
height: 100%;
background: transparent;
user-select: none;
}
.dark-row {
color: #fff;
background: #006acd32 !important;
user-select: none;
}
.dark-row:nth-child(odd) {
background: #020d2d32 !important;
}
.dark-row > td.el-table__cell {
border: none;
font-size: 16px;
}
.dark-row > td.el-table__cell:not(:last-child) {
border-right: 1px solid #0003;
}
.dark-row:hover > td.el-table__cell {
background-color: #fff1 !important;
}
.dark-header {
background-color: transparent !important;
color: #fff;
}
.dark-header > th.el-table__cell.is-leaf {
border-bottom: none;
background-color: #006acd32 !important;
font-weight: 400;
letter-spacing: 1px;
}
.dark-header > th.el-table__cell {
font-size: 16px;
}
.dark-header > th.el-table__cell:not(:last-child) {
border-right: 1px solid #0003;
}
</style>
<style scoped>
.main-container {
width: 1920px;
height: 1080px;
position: relative;
display: flex;
flex-direction: column;
background: url(./assets/bg.png) 100% / cover no-repeat;
}
#main-container__fulleq {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url(./assets/full-lines.png) 100% / cover no-repeat;
}
.pages-wrapper {
flex: 1;
display: flex;
position: relative;
}
.alert-list-page {
flex: 1;
position: relative;
}
.alert-list {
height: calc(100% - 56px);
width: 640px;
position: absolute;
top: 15px;
right: 32px;
z-index: 300;
display: flex;
flex-direction: column;
}
:fullscreen .alert-list {
width: 35%;
top: 32px;
height: calc(100% - 64px);
}
.alert-list__table {
height: calc(100% - 72px);
}
.alert-list__table >>> .el-table__inner-wrapper::before {
background: transparent;
}
button {
appearance: none;
outline: none;
border: none;
cursor: pointer;
}
.alert-btn {
width: 100%;
height: 72px;
background: #0f04;
color: #fff;
font-size: 32px;
line-height: 1;
letter-spacing: 2px;
transition: all 0.2s ease-out;
}
.alert-btn:hover {
background: #0f08;
}
</style>

View File

@ -1,17 +1,21 @@
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import MainPage from "./MainPage.vue"; import MainPage from "./MainPage.vue";
import MainScreen from "./MainScreen.vue";
import AlertListScreen from "./AlertListScreen.vue";
// import Slider from "./components/Slider.vue"; // import Slider from "./components/Slider.vue";
import useWebsocket from "./utils/useWebsocket"; import useWebsocket from "./utils/useWebsocket";
import { useWsStore } from "./store"; import { useWsStore } from "./store";
const store = useWsStore(); const store = useWsStore();
const excludePaths = ["/alert-list", "/main-screen"];
// use websocket // use websocket
let urlPath = ref(document.location.pathname); let urlPath = ref(document.location.pathname);
if (urlPath.value === "/") { if (urlPath.value === "/") {
urlPath.value = "/1-1"; urlPath.value = "/1-1";
} }
useWebsocket(store, urlPath.value); useWebsocket(store, urlPath.value, excludePaths);
// size setting // size setting
// const size = ref(80); // const size = ref(80);
@ -33,8 +37,10 @@ useWebsocket(store, urlPath.value);
<template> <template>
<div id="app-container"> <div id="app-container">
<MainPage :path="urlPath" />
<!-- <Slider :size="size" @size-change="setSize" /> --> <!-- <Slider :size="size" @size-change="setSize" /> -->
<MainPage v-if="!excludePaths.includes(urlPath)" :path="urlPath" />
<MainScreen v-if="urlPath == '/main-screen'" />
<AlertListScreen v-if="urlPath == '/alert-list'" />
</div> </div>
</template> </template>

119
src/FullEqList.vue Normal file
View File

@ -0,0 +1,119 @@
<!--
filename: FullEqList.vue
author: liubin
date: 2024-03-05 14:13:06
description:
-->
<script setup>
import "./full-eq-list.css";
</script>
<template>
<div class="absolute v-1 tag-container">
<span class="name-tag">M7上片</span>
<span class="name-tag">M6上片</span>
<span class="name-tag">M5上片</span>
<span class="name-tag">M4上片</span>
<span class="name-tag">M3上片</span>
<span class="name-tag">M2上片</span>
<span class="name-tag">M1上片</span>
</div>
<div class="absolute v-2 tag-container">
<span class="name-tag">M7磨边机</span>
<span class="name-tag">M6磨边机</span>
<span class="name-tag">M5磨边机</span>
<span class="name-tag">M4磨边机</span>
<span class="name-tag">M3磨边机</span>
<span class="name-tag">M2磨边机</span>
<span class="name-tag">M1磨边机</span>
</div>
<div class="absolute v-3 tag-container">
<span class="name-tag">M7清洗机</span>
<span class="name-tag">M6清洗机</span>
<span class="name-tag">M5清洗机</span>
<span class="name-tag">M4清洗机</span>
<span class="name-tag">M3清洗机</span>
<span class="name-tag">M2清洗机</span>
<span class="name-tag">M1清洗机</span>
</div>
<div class="absolute z-1 v-4 tag-container">
<span class="name-tag">Z7钻孔机</span>
<span class="name-tag">Z6钻孔机</span>
<span class="name-tag">Z5钻孔机</span>
<span class="name-tag">Z4钻孔机</span>
<span class="name-tag">Z3钻孔机</span>
<span class="name-tag">Z2钻孔机</span>
<span class="name-tag">Z1钻孔机</span>
</div>
<div class="absolute z-1 v-5 tag-container">
<span class="name-tag">S7清洗机</span>
<span class="name-tag">S6清洗机</span>
<span class="name-tag">S5清洗机</span>
<span class="name-tag">S4清洗机</span>
<span class="name-tag">S3清洗机</span>
<span class="name-tag">S2清洗机</span>
<span class="name-tag">S1清洗机</span>
</div>
<div class="absolute z-1 v-6 tag-container">
<span class="name-tag">S7前储片台</span>
<span class="name-tag">S6前储片台</span>
<span class="name-tag">S5前储片台</span>
<span class="name-tag">S4前储片台</span>
<span class="name-tag">S3前储片台</span>
<span class="name-tag">S2前储片台</span>
<span class="name-tag">S1前储片台</span>
</div>
<div class="absolute z-2 v-7 tag-container">
<span class="name-tag">S7丝印机</span>
<span class="name-tag">S6丝印机</span>
<span class="name-tag">S5丝印机</span>
<span class="name-tag">S4丝印机</span>
<span class="name-tag">S3丝印机</span>
<span class="name-tag">S2丝印机</span>
<span class="name-tag">S1丝印机</span>
</div>
<div class="absolute z-2 v-8 tag-container">
<span class="name-tag">S7固化炉</span>
<span class="name-tag">S6固化炉</span>
<span class="name-tag">S5固化炉</span>
<span class="name-tag">S4固化炉</span>
<span class="name-tag">S3固化炉</span>
<span class="name-tag">S2固化炉</span>
<span class="name-tag">S1固化炉</span>
</div>
<div class="absolute z-1 v-10 tag-container">
<span class="name-tag">D2镀膜机</span>
<span class="name-tag">D1镀膜机</span>
</div>
<div class="absolute z-1 v-9 tag-container">
<span class="name-tag">S7后储片台</span>
<span class="name-tag">S6后储片台</span>
<span class="name-tag">S5后储片台</span>
<span class="name-tag">S4后储片台</span>
<span class="name-tag">S3后储片台</span>
<span class="name-tag">S2后储片台</span>
<span class="name-tag">S1后储片台</span>
</div>
<div class="absolute z-3 tag-container">
<span class="name-tag">B3前储片台</span>
<span class="name-tag">B2前储片台</span>
<span class="name-tag">B1前储片台</span>
</div>
<div class="absolute z-3 b-1 tag-container">
<span class="name-tag">B3清洗机</span>
<span class="name-tag">B2清洗机</span>
<span class="name-tag">B1清洗机</span>
</div>
<div class="absolute z-3 b-2 tag-container">
<span class="name-tag">B3下片</span>
<span class="name-tag">B2下片</span>
<span class="name-tag">B1下片</span>
</div>
<div class="absolute z-3 b-3 tag-container">
<span class="name-tag">B3后储片台</span>
<span class="name-tag">B2后储片台</span>
<span class="name-tag">B1后储片台</span>
</div>
</template>
<style scoped lang="scss"></style>

236
src/MainScreen.vue Normal file
View File

@ -0,0 +1,236 @@
<!-- 报警列表页面 -->
<script setup>
import { ref, onMounted } from "vue";
import { useWsStore } from "./store";
import { useSettings } from "./store/settings";
import DatetimeTool from "./components/HeadTime.vue";
import AppHeader from "./components/Base/Header.vue";
import Tools from "./components/Tools.vue";
import TodayYield from "./components/mainscreen/TodayYield.vue";
import SumYield from "./components/mainscreen/SumYield.vue";
import TodayRate from "./components/mainscreen/TodayRate.vue";
import SumRate from "./components/mainscreen/SumRate.vue";
import FullEqList from "./FullEqList.vue";
import { useGlass } from "./utils/glass";
const mainContainer = ref(null);
const props = defineProps({
line: {
type: Number,
default: 1,
},
});
const store = useWsStore();
const GlassContainer = useGlass(store);
const settingStore = useSettings();
//
onMounted(() => {
const settings = settingStore.settings;
//
handleResolutionChange(settings.resolution.width, settings.resolution.height);
});
function changeScale(elm, width, height) {
const xScale = width / 1920;
const yScale = height / 1080;
const style = {
transform: `scale(${xScale}, ${yScale})`,
transformOrigin: "top left",
};
elm.style.transform = style.transform;
elm.style.transformOrigin = style.transformOrigin;
}
function handleResolutionChange(width, height) {
mainContainer.value && changeScale(mainContainer.value, width, height);
}
</script>
<template>
<div id="main-container" ref="mainContainer" class="main-container">
<div id="main-container__fulleq">
<FullEqList v-if="0" />
</div>
<DatetimeTool />
<Tools @change-resolution="handleResolutionChange" />
<AppHeader />
<GlassContainer />
<div class="main-screen">
<!-- <div
style="
position: absolute;
top: 0;
left: -279px;
width: calc(100% + 279px);
height: 100%;
background: #ccc3;
"
>
<ThreeD :line="line ?? '1'" />
</div> -->
<TodayYield class=" " />
<SumYield class=" " />
<TodayRate class=" " />
<SumRate class=" " />
</div>
</div>
</template>
<style>
.dark-table {
height: 100%;
background: transparent;
user-select: none;
}
.dark-row {
color: #fff;
background: #006acd32 !important;
user-select: none;
}
.dark-row:nth-child(odd) {
background: #020d2d32 !important;
}
.dark-row > td.el-table__cell {
border: none;
font-size: 16px;
}
.dark-row > td.el-table__cell:not(:last-child) {
border-right: 1px solid #0003;
}
.dark-row:hover > td.el-table__cell {
background-color: #fff1 !important;
}
.dark-header {
background-color: transparent !important;
color: #fff;
}
.dark-header > th.el-table__cell.is-leaf {
border-bottom: none;
background-color: #006acd32 !important;
font-weight: 400;
letter-spacing: 1px;
}
.dark-header > th.el-table__cell {
font-size: 16px;
}
.dark-header > th.el-table__cell:not(:last-child) {
border-right: 1px solid #0003;
}
</style>
<style scoped>
.ccontainer {
width: 420px;
height: 420px;
background: #ccc3;
/* position: absolute; */
}
.left-top {
top: 32px;
left: 32px;
}
.left-bottom {
bottom: 32px;
left: 32px;
}
.right-top {
top: 32px;
right: 32px;
}
.right-bottom {
bottom: 32px;
right: 32px;
}
.main-container {
width: 1920px;
height: 1080px;
background: #000;
position: relative;
display: flex;
flex-direction: column;
background: url(./assets/bg.png) 100% / cover no-repeat;
}
.pages-wrapper {
flex: 1;
display: flex;
position: relative;
}
.main-screen {
flex: 1;
position: relative;
display: grid;
grid-template-rows: repeat(2, 400px);
grid-template-columns: repeat(2, 400px);
justify-content: center;
/* align-content: center; */
/* place-content: center; */
gap: 15% 55%;
z-index: 300;
}
.alert-list {
height: calc(100% - 56px);
width: 520px;
position: absolute;
top: 15px;
right: 32px;
display: flex;
flex-direction: column;
}
:fullscreen .alert-list {
width: 35%;
top: 32px;
height: calc(100% - 64px);
}
.alert-list__table {
height: calc(100% - 72px);
}
.alert-list__table >>> .el-table__inner-wrapper::before {
background: transparent;
}
button {
appearance: none;
outline: none;
border: none;
cursor: pointer;
}
.alert-btn {
width: 100%;
height: 72px;
background: #0f04;
color: #fff;
font-size: 32px;
line-height: 1;
letter-spacing: 2px;
transition: all 0.2s ease-out;
}
.alert-btn:hover {
background: #0f08;
}
</style>

BIN
src/assets/full-lines.png Normal file

Binary file not shown.

After

(image error) Size: 978 KiB

BIN
src/assets/glass.png Normal file

Binary file not shown.

After

(image error) Size: 4.2 KiB

View File

@ -17,17 +17,14 @@ const hourData = ref([
// { lineName: "005", hour: "00:50", num: 50 }, // { lineName: "005", hour: "00:50", num: 50 },
]); ]);
store.$subscribe((mutation, state) => { store.$subscribe((mutation, state) => {
// console.log("[HourChart] =======>", state.data2.lineHourList?.length);
if ( if (
state.data2.lineHourList == undefined || state.data2.lineHourList == undefined ||
state.data2.lineHourList?.length == 0 state.data2.lineHourList?.length == 0
) { ) {
// console.log("[HourChart] ");
hourData.value.splice(0); hourData.value.splice(0);
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
return; return;
} }
// console.log("[HourChart] ===> : ", state.data2.lineHourList);
hourData.value = (state.data2?.lineHourList ?? [ hourData.value = (state.data2?.lineHourList ?? [
// { lineName: '001', hour: '00:00', num: 10 }, // { lineName: '001', hour: '00:00', num: 10 },
// { lineName: '002', hour: '00:20', num: 20 }, // { lineName: '002', hour: '00:20', num: 20 },
@ -36,8 +33,8 @@ store.$subscribe((mutation, state) => {
// { lineName: '005', hour: '00:50', num: 50 }, // { lineName: '005', hour: '00:50', num: 50 },
]).map((item, index) => ({ ]).map((item, index) => ({
id: `${item.lineName}_${index}`, id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"), hour: item.hour || '__',
data: item.num || Math.floor(Math.random() * 100), data: item.num || 0,
})); }));
setupChart(); setupChart();
}); });
@ -46,7 +43,6 @@ function setupChart() {
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
nextTick(() => { nextTick(() => {
chart.value = echarts.init(chartChart.value); chart.value = echarts.init(chartChart.value);
// console.log("[HourChart] ===> chart: ", chart.value, hourData.value.map((item) => item.hour), hourData.value.map((item) => item.data));
chartSetup( chartSetup(
chart.value, chart.value,
hourData.value.map((item) => item.hour), hourData.value.map((item) => item.hour),
@ -92,7 +88,7 @@ onMounted(() => {
<style scoped> <style scoped>
.chart { .chart {
height: 300px; height: 450px;
} }
.chart-chart { .chart-chart {

View File

@ -0,0 +1,163 @@
<script setup>
import { ref, watch, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "./Base/Container.vue";
import { useWsStore } from "../store";
import chartSetup, { loadData } from "./LatestWeekYieldOptions";
const store = useWsStore();
const chartContainer = ref(null);
const chartInstance = ref(null);
const show = ref(false);
onMounted(() => {
chartContainer.value.classList.add("h-full");
const d = loadData(store.data2.lineSevenDayLogList);
// const d = loadData([
// {
// data: [
// { day: "10-10", num: Math.floor(Math.random() * 500) },
// { day: "10-11", num: Math.floor(Math.random() * 500) },
// { day: "10-12", num: Math.floor(Math.random() * 500) },
// { day: "10-13", num: Math.floor(Math.random() * 500) },
// { day: "10-14", num: Math.floor(Math.random() * 500) },
// { day: "10-15", num: Math.floor(Math.random() * 500) },
// { day: "10-16", num: Math.floor(Math.random() * 500) },
// ],
// name: "线",
// },
// {
// data: [
// { day: "10-10", num: Math.floor(Math.random() * 500) },
// { day: "10-11", num: Math.floor(Math.random() * 500) },
// { day: "10-12", num: Math.floor(Math.random() * 500) },
// { day: "10-13", num: Math.floor(Math.random() * 500) },
// { day: "10-14", num: Math.floor(Math.random() * 500) },
// { day: "10-15", num: Math.floor(Math.random() * 500) },
// { day: "10-16", num: Math.floor(Math.random() * 500) },
// ],
// name: "线",
// },
// {
// data: [
// { day: "10-10", num: Math.floor(Math.random() * 500) },
// { day: "10-11", num: Math.floor(Math.random() * 500) },
// { day: "10-12", num: Math.floor(Math.random() * 500) },
// { day: "10-13", num: Math.floor(Math.random() * 500) },
// { day: "10-14", num: Math.floor(Math.random() * 500) },
// { day: "10-15", num: Math.floor(Math.random() * 500) },
// { day: "10-16", num: Math.floor(Math.random() * 500) },
// ],
// name: "线",
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
} else {
if (!chartInstance.value)
chartInstance.value = echarts.init(chartContainer.value);
chartSetup(chartInstance.value, d);
show.value = true;
}
});
//
store.$subscribe((mutation, state) => {
const d = loadData(state.data2.lineSevenDayLogList);
// const d = loadData([
// {
// data: [
// { day: "10-10", num: Math.floor(Math.random() * 500) },
// { day: "10-11", num: Math.floor(Math.random() * 500) },
// { day: "10-12", num: Math.floor(Math.random() * 500) },
// { day: "10-13", num: Math.floor(Math.random() * 500) },
// { day: "10-14", num: Math.floor(Math.random() * 500) },
// { day: "10-15", num: Math.floor(Math.random() * 500) },
// { day: "10-16", num: Math.floor(Math.random() * 500) },
// ],
// name: "线",
// },
// {
// data: [
// { day: "10-10", num: Math.floor(Math.random() * 500) },
// { day: "10-11", num: Math.floor(Math.random() * 500) },
// { day: "10-12", num: Math.floor(Math.random() * 500) },
// { day: "10-13", num: Math.floor(Math.random() * 500) },
// { day: "10-14", num: Math.floor(Math.random() * 500) },
// { day: "10-15", num: Math.floor(Math.random() * 500) },
// { day: "10-16", num: Math.floor(Math.random() * 500) },
// ],
// name: "线",
// },
// {
// data: [
// { day: "10-10", num: Math.floor(Math.random() * 500) },
// { day: "10-11", num: Math.floor(Math.random() * 500) },
// { day: "10-12", num: Math.floor(Math.random() * 500) },
// { day: "10-13", num: Math.floor(Math.random() * 500) },
// { day: "10-14", num: Math.floor(Math.random() * 500) },
// { day: "10-15", num: Math.floor(Math.random() * 500) },
// { day: "10-16", num: Math.floor(Math.random() * 500) },
// ],
// name: "线",
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
} else {
if (!chartInstance.value)
chartInstance.value = echarts.init(chartContainer.value);
chartSetup(chartInstance.value, d);
show.value = true;
}
});
</script>
<template>
<Container class="chart" title="近7日产量" icon="cube">
<div
ref="chartContainer"
class="chart-chart"
:style="{ opacity: show ? 1 : 0 }"
></div>
<p v-show="!show" class="empty-data-hint">暂无数据</p>
</Container>
</template>
<style scoped>
.chart {
height: 450px;
}
.chart-chart {
height: 100%;
}
</style>
<style>
.empty-data-hint {
color: #c5c5c5;
letter-spacing: 1px;
font-size: 24px;
line-height: 1.25;
text-align: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
display: inline-block;
width: 200px;
height: 32px;
user-select: none;
}
</style>

View File

@ -0,0 +1,137 @@
export const options = {
color: ['#00D3E7', '#2B9BFF', '#0D4DFF'],
legend: {
textStyle: {
color: "#fff8",
},
right: 0,
itemWidth: 12,
itemHeight: 12
},
tooltip: {},
dataset: {
source: [],
},
grid: {
top: 56,
bottom: 12,
left: 10,
right: 20,
containLabel: true,
},
xAxis: {
type: "category",
axisLabel: {
fontSize: 12,
color: "#fff8",
},
axisTick: {
alignWithLabel: true,
},
},
yAxis: {
name: "片",
nameTextStyle: {
color: "#fff8",
fontSize: 14,
},
axisLabel: {
fontSize: 12,
color: "#fff8",
},
splitLine: {
lineStyle: {
color: "#fff8",
},
},
minInterval: 1,
},
series: [
{
type: "bar",
label: {
show: true,
distance: 20,
rotate: 90,
color: "#fff",
verticalAlign: "middle",
position: 'top',
formatter: (params) => {
return params.data[1];
},
},
},
{
type: "bar",
label: {
show: true,
distance: 20,
rotate: 90,
color: "#fff",
verticalAlign: "middle",
position: 'top',
formatter: (params) => {
return params.data[2];
},
},
},
{
type: "bar",
label: {
show: true,
distance: 20,
rotate: 90,
color: "#fff",
verticalAlign: "middle",
position: 'top',
formatter: (params) => {
return params.data[3];
},
},
},
],
};
export default function setup(
echartInstance,
list = [
["1-12", 1, 2, 3],
["1-13", 4, 5, 6],
["1-14", 14, 15, 16],
["1-15", 9],
["1-16", 4, 5, 6],
["1-17", 1, 1, 1],
]
) {
const new_options = { ...options };
new_options.dataset.source = [["date", "钢1线", "钢2线", "钢3线"], ...list];
echartInstance.setOption(new_options);
}
export function createDatasetFrom(lines, datelist) {
const dataset = [];
datelist.forEach((date) => {
const row = [date];
lines.forEach((line) => {
const item = line.data.find((item) => item.day === date);
row.push(item ? item.num : 0);
});
dataset.push(row);
});
console.log(dataset);
return dataset;
}
export function findDatelist(lines) {
const dateList = [];
lines.forEach((line) => {
dateList.push(...line.data.map((item) => item.day));
});
return Array.from(new Set(dateList)).sort();
}
export function loadData(list) {
if (!list || list.length != 3 || list[0].data.length <= 0) return null;
const datelist = findDatelist(list);
return createDatasetFrom(list, datelist);
}

View File

@ -77,7 +77,7 @@ function handleConfirm() {
width: 577px; width: 577px;
height: 422px; height: 422px;
background: url(../assets/dialog-bg.png) 100% / contain no-repeat; background: url(../assets/dialog-bg.png) 100% / contain no-repeat;
z-index: 1000; z-index: 1001;
transition: all .3s ease-out; transition: all .3s ease-out;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -4,9 +4,9 @@ import IconBack from "../assets/menu_icon/IconBack.vue";
import IconExchange from "../assets/menu_icon/IconExchange.vue"; import IconExchange from "../assets/menu_icon/IconExchange.vue";
import IconSetting from "../assets/menu_icon/IconSetting.vue"; import IconSetting from "../assets/menu_icon/IconSetting.vue";
import SettingDialogVue from "./SettingDialog.vue"; import SettingDialogVue from "./SettingDialog.vue";
import { useSettings } from '../store/settings'; import { useSettings } from "../store/settings";
const emit = defineEmits(['change-resolution']) const emit = defineEmits(["change-resolution"]);
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
// store.$subscribe((_, state) => { // store.$subscribe((_, state) => {
@ -15,17 +15,17 @@ const { settings, updateSettings } = useSettings();
const visible = ref(false); const visible = ref(false);
function toHome() { function toHome() {
document.location.reload() document.location.reload();
} }
function showDialog() { function showDialog() {
visible.value = true; visible.value = true;
} }
function toggleLunbo() { function toggleLunbo() {
updateSettings({ type: 'carousel', value: null }) updateSettings({ type: "carousel", value: null });
} }
function handleChangeResolution(w, h) { function handleChangeResolution(w, h) {
emit('change-resolution', w, h); emit("change-resolution", w, h);
visible.value = false; visible.value = false;
} }
</script> </script>
@ -38,14 +38,33 @@ function handleChangeResolution(w, h) {
<button @click="showDialog"> <button @click="showDialog">
<IconSetting /> <IconSetting />
</button> </button>
<button style="position: relative;" @click="toggleLunbo"> <button style="position: relative" @click="toggleLunbo">
<span v-if="settings.carousel" <span
style="color: #fffa; font-family: 'Courier New', Courier, monospace; font-weight:600; width: 56px; font-size: 22px; user-select: none; position: absolute; line-height: 52px; text-align: center;"> v-if="settings.carousel"
{{ settings.carouselTime }}S</span> style="
color: #fffa;
font-family: 'Courier New', Courier, monospace;
font-weight: 600;
width: 56px;
font-size: 22px;
user-select: none;
position: absolute;
line-height: 52px;
text-align: center;
"
>
{{ settings.carouselTime }}S</span
>
<IconExchange :color="settings.carousel ? '#03356a' : '#50A1EC'" /> <IconExchange :color="settings.carousel ? '#03356a' : '#50A1EC'" />
</button> </button>
</div> </div>
<SettingDialogVue v-if="visible" @close="visible = false;" @change-resolution="handleChangeResolution" /> <Teleport to="body">
<SettingDialogVue
v-if="visible"
@close="visible = false"
@change-resolution="handleChangeResolution"
/>
</Teleport>
</template> </template>
<style scoped> <style scoped>
@ -55,7 +74,7 @@ function handleChangeResolution(w, h) {
right: 24px; right: 24px;
display: flex; display: flex;
gap: 0px; gap: 0px;
z-index: 3; z-index: 1000;
} }
button { button {
@ -86,7 +105,6 @@ button svg #switch-btn {
transition: fill 0.2s ease-out; transition: fill 0.2s ease-out;
} }
button:hover svg #back-btn, button:hover svg #back-btn,
button:hover svg #setting-btn, button:hover svg #setting-btn,
button:hover svg #switch-btn { button:hover svg #switch-btn {

View File

@ -30,8 +30,8 @@ store.$subscribe((mutation, state) => {
} }
hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({ hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
id: `${item.lineName}_${index}`, id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"), hour: item.hour || `__`,
data: item.num || Math.floor(Math.random() * 100), data: item.num || 0,
})); }));
setupChart(); setupChart();
}); });

View File

@ -1,4 +1,5 @@
export const options = { export const options = {
color: ['#00D3E7', '#2B9BFF', '#0D4DFF'],
legend: { legend: {
textStyle: { textStyle: {
color: "#fff8", color: "#fff8",

View File

@ -0,0 +1,152 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
const setupFn = (chart, datalist = [0.0, 0.0, 0.0]) => {
const option = {
color: ["#F3908B"],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
top: "5%",
bottom: "5%",
left: "3%",
right: "5%",
containLabel: true,
},
yAxis: {
type: "value",
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
xAxis: {
type: "category",
data: ["钢1线", "钢2线", "钢3线"],
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
series: [
{
type: "line",
data: datalist,
label: {
show: true,
position: "top",
// formatter: (params) => {
// console.log("params", datalist[params.dataIndex]);
// },
formatter: "{c}%",
color: "#fff",
fontSize: 14,
},
},
],
};
chart.setOption(option);
};
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const showChartDom = ref(false);
/** 无状态,处理数据 */
function loadData(yieldArray) {
// lineSumProductYield
const result = [];
if (yieldArray == undefined || yieldArray?.length == 0) return null;
for (let i = 0; i < yieldArray.length; ++i) {
if (yieldArray[i].name == "钢1线") {
result[0] = parseFloat(yieldArray[i].data);
} else if (yieldArray[i].name == "钢2线") {
result[1] = parseFloat(yieldArray[i].data);
} else if (yieldArray[i].name == "钢3线") {
result[2] = parseFloat(yieldArray[i].data);
}
}
return result;
}
function setupChart(chart, dom, data) {
if (chart.value) chart.value.dispose();
nextTick(() => {
chart.value = echarts.init(dom);
setupFn(chart.value, data);
});
}
/** 有状态,处理数据 */
function __apply(yieldArray) {
const d = loadData(yieldArray);
if (!d) {
showChartDom.value = false;
if (chart.value) chart.value.dispose();
return;
}
showChartDom.value = true;
setupChart(chart, chartChart.value, d);
}
//
store.$subscribe((mutation, state) => {
__apply(state.mainDataChart.lineSumProductYield);
// __apply([
// { name: "1线", data: 0.32 },
// { name: "2线", data: 0.91 },
// { name: "3线", data: 0.54 },
// ]);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
__apply([
{ name: "钢1线", data: "0%" },
{ name: "钢2线", data: "0%" },
{ name: "钢3线", data: "0%" },
]);
});
</script>
<template>
<Container class="chart" title="累计生产线成品率" icon="cube">
<div
ref="chartChart"
class="chart-chart"
:style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
}
.chart-chart {
height: 100%;
}
</style>

View File

@ -0,0 +1,155 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
const setupFn = (chart, datalist = [0.0, 0.0, 0.0, 0.0]) => {
console.log("datalist", datalist);
const option = {
color: ["#3398FB"],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
top: "5%",
bottom: "5%",
left: "3%",
right: "5%",
containLabel: true,
},
xAxis: {
type: "value",
boundaryGap: [0, 0.01],
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
yAxis: {
type: "category",
data: ["钢1线", "钢2线", "钢3线", "合计"],
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
series: [
{
type: "bar",
data: datalist,
label: {
show: true,
position: "right",
formatter: "{c}",
color: "#fff",
fontSize: 14,
},
},
],
};
chart.setOption(option);
};
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const showChartDom = ref(false);
/** 无状态,处理数据 */
function loadData(yieldArray) {
const result = [];
if (yieldArray == undefined || yieldArray?.length == 0) return null;
for (let i = 0; i < yieldArray.length; ++i) {
if (yieldArray[i].name == "钢1线") {
result[0] = parseFloat(yieldArray[i].data) || 0;
} else if (yieldArray[i].name == "钢2线") {
result[1] = parseFloat(yieldArray[i].data) || 0;
} else if (yieldArray[i].name == "钢3线") {
result[2] = parseFloat(yieldArray[i].data) || 0;
} else {
//
result[3] = parseFloat(yieldArray[i].data) || 0;
}
}
return result;
}
function setupChart(chart, dom, data) {
if (chart.value) chart.value.dispose();
nextTick(() => {
chart.value = echarts.init(dom);
setupFn(chart.value, data);
});
}
/** 有状态,处理数据 */
function __apply(yieldArray) {
const d = loadData(yieldArray);
if (!d) {
showChartDom.value = false;
if (chart.value) chart.value.dispose();
return;
}
showChartDom.value = true;
setupChart(chart, chartChart.value, d);
}
//
store.$subscribe((mutation, state) => {
__apply(state.mainDataChart.lineSumProductData);
// __apply([
// { name: "1线", data: 11 },
// { name: "2线", data: 22 },
// { name: "3线", data: 33 },
// { name: "", data: 66 },
// ]);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
__apply([
{ name: "钢1线", data: 0 },
{ name: "钢2线", data: 0 },
{ name: "钢3线", data: 0 },
{ name: "合计", data: 0 },
]);
});
</script>
<template>
<Container class="chart" title="累计生产线产量" icon="cube">
<div
ref="chartChart"
class="chart-chart"
:style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
}
.chart-chart {
height: 100%;
}
</style>

View File

@ -0,0 +1,153 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
const setupFn = (chart, datalist = [0.0, 0.0, 0.0]) => {
const option = {
color: ["#F3908B"],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
top: "5%",
bottom: "5%",
left: "3%",
right: "5%",
containLabel: true,
},
yAxis: {
type: "value",
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
xAxis: {
type: "category",
data: ["钢1线", "钢2线", "钢3线"],
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
series: [
{
type: "line",
data: datalist,
// data: datalist.map((item) => item * 100),
label: {
show: true,
position: "top",
// formatter: (params) => {
// console.log("params", datalist[params.dataIndex]);
// },
formatter: "{c}%",
color: "#fff",
fontSize: 14,
},
},
],
};
chart.setOption(option);
};
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const showChartDom = ref(false);
/** 无状态,处理数据 */
function loadData(yieldArray) {
// lineTodayProductYield
const result = [];
if (yieldArray == undefined || yieldArray?.length == 0) return null;
for (let i = 0; i < yieldArray.length; ++i) {
if (yieldArray[i].name == "钢1线") {
result[0] = parseFloat(yieldArray[i].data) || 0.0;
} else if (yieldArray[i].name == "钢2线") {
result[1] = parseFloat(yieldArray[i].data) || 0.0;
} else if (yieldArray[i].name == "钢3线") {
result[2] = parseFloat(yieldArray[i].data) || 0.0;
}
}
return result;
}
function setupChart(chart, dom, data) {
if (chart.value) chart.value.dispose();
nextTick(() => {
chart.value = echarts.init(dom);
setupFn(chart.value, data);
});
}
/** 有状态,处理数据 */
function __apply(yieldArray) {
const d = loadData(yieldArray);
if (!d) {
showChartDom.value = false;
if (chart.value) chart.value.dispose();
return;
}
showChartDom.value = true;
setupChart(chart, chartChart.value, d);
}
//
store.$subscribe((mutation, state) => {
__apply(state.mainDataChart.lineTodayProductYield);
// __apply([
// { name: "1线", data: 0.32 },
// { name: "2线", data: 0.91 },
// { name: "3线", data: 0.54 },
// ]);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
__apply([
{ name: "钢1线", data: "0%" },
{ name: "钢2线", data: "0%" },
{ name: "钢3线", data: "0%" },
]);
});
</script>
<template>
<Container class="chart" title="今日生产线成品率" icon="cube">
<div
ref="chartChart"
class="chart-chart"
:style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
}
.chart-chart {
height: 100%;
}
</style>

View File

@ -0,0 +1,159 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
const setupFn = (chart, datalist = [0.0, 0.0, 0.0, 0.0]) => {
console.log("datalist", datalist);
const option = {
color: ["#3398FB"],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
grid: {
top: "5%",
bottom: "5%",
left: "3%",
right: "5%",
containLabel: true,
},
xAxis: {
type: "value",
boundaryGap: [0, 0.01],
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
yAxis: {
type: "category",
data: ["钢1线", "钢2线", "钢3线", "合计"],
axisLabel: {
fontSize: 14,
color: "#fff",
},
splitLine: {
show: true,
lineStyle: {
color: "#ccc6",
},
},
},
series: [
{
type: "bar",
data: datalist,
label: {
show: true,
position: "right",
formatter: "{c}",
color: "#fff",
fontSize: 14,
},
},
],
};
chart.setOption(option);
};
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const showChartDom = ref(false);
/** 无状态,处理数据 */
function loadData(yieldArray) {
// lineTodayProductData
const result = [];
if (yieldArray == undefined || yieldArray?.length == 0) return null;
for (let i = 0; i < yieldArray.length; ++i) {
if (yieldArray[i].name == "钢1线") {
result[0] = parseFloat(yieldArray[i].data) || 0;
} else if (yieldArray[i].name == "钢2线") {
result[1] = parseFloat(yieldArray[i].data) || 0;
} else if (yieldArray[i].name == "钢3线") {
result[2] = parseFloat(yieldArray[i].data) || 0;
} else {
//
result[3] = parseFloat(yieldArray[i].data) || 0;
}
}
return result;
}
function setupChart(chart, dom, data) {
if (chart.value) {
chart.value.dispose();
chart.value = null;
}
nextTick(() => {
chart.value = echarts.init(dom);
setupFn(chart.value, data);
});
}
/** 有状态,处理数据 */
function __apply(yieldArray) {
const d = loadData(yieldArray);
if (!d) {
showChartDom.value = false;
if (chart.value) chart.value.dispose();
return;
}
showChartDom.value = true;
setupChart(chart, chartChart.value, d);
}
//
store.$subscribe((mutation, state) => {
__apply(state.mainDataChart.lineTodayProductData);
// __apply([
// { name: "1线", data: 11 },
// { name: "2线", data: 22 },
// { name: "3线", data: 33 },
// { name: "", data: 66 },
// ]);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
__apply([
{ name: "钢1线", data: 0 },
{ name: "钢2线", data: 0 },
{ name: "钢3线", data: 0 },
{ name: "合计", data: 0 },
]);
});
</script>
<template>
<Container class="chart" title="今日生产线产量" icon="cube">
<div
ref="chartChart"
class="chart-chart"
:style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
}
.chart-chart {
height: 100%;
}
</style>

180
src/full-eq-list.css Normal file
View File

@ -0,0 +1,180 @@
#main-container__fulleq {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url(./assets/full-lines.png) 100% / cover no-repeat;
}
.name-tag {
padding: 5px;
border-radius: 5px;
background: #ccc3;
backdrop-filter: blur(3px);
color: #000a;
font-size: 12px;
/* font-weight: 600; */
transition: background 0.1s ease-out;
user-select: none;
width: 72px;
text-align: center;
position: absolute;
/* cursor: pointer; */
/* transform: rotate(-45deg); */
box-shadow: 0 0 5px 2px #0003;
}
/* .name-tag:hover {
background: #666c;
} */
.tag-container > .name-tag:first-child {
bottom: 214px;
left: -128px;
}
.tag-container > .name-tag:nth-child(2) {
bottom: 182px;
left: -100px;
}
.tag-container > .name-tag:nth-child(3) {
bottom: 150px;
left: -88px;
}
.tag-container > .name-tag:nth-child(4) {
bottom: 118px;
left: -68px;
}
.tag-container > .name-tag:nth-child(5) {
bottom: 86px;
left: -52px;
}
.tag-container > .name-tag:nth-child(6) {
bottom: 32px;
left: -20px;
}
.tag-container > .name-tag:nth-child(7) {
top: 0;
left: 0;
}
.absolute {
position: absolute;
padding: 8px;
z-index: 200;
}
.v-1 {
bottom: 160px;
left: 430px;
}
.v-2 {
bottom: 200px;
left: 480px;
}
.v-3 {
bottom: 250px;
left: 620px;
}
.z-1 > .name-tag:first-child {
bottom: 205px;
left: -155px;
}
.z-1 > .name-tag:nth-child(2) {
bottom: 180px;
left: -130px;
}
.v-4 {
bottom: 300px;
left: 730px;
}
.v-5 {
bottom: 340px;
left: 780px;
}
.v-6 {
bottom: 360px;
left: 840px;
}
.z-2 > .name-tag:first-child {
bottom: 205px;
left: -140px;
}
.z-2 > .name-tag:nth-child(2) {
bottom: 180px;
left: -110px;
}
.z-2 > .name-tag:nth-child(3) {
bottom: 152px;
left: -70px;
}
.z-2 > .name-tag:nth-child(4) {
bottom: 128px;
left: -50px;
}
.z-2 > .name-tag:nth-child(5) {
bottom: 100px;
left: -36px;
}
.v-7 {
bottom: 380px;
left: 880px;
}
.v-8 {
bottom: 410px;
left: 930px;
}
.v-9 {
bottom: 440px;
left: 1020px;
}
.v-10 {
bottom: 260px;
left: 1100px;
}
.z-3 {
bottom: 550px;
left: 1400px;
/* background: #c003; */
}
.z-3 > .name-tag:first-child {
bottom: 235px;
left: -155px;
}
.z-3 > .name-tag:nth-child(2) {
bottom: 188px;
left: -85px;
}
.z-3 > .name-tag:nth-child(3) {
bottom: 120px;
left: 35px;
}
.b-1 {
bottom: 570px;
left: 1440px;
}
.b-2 {
bottom: 600px;
left: 1490px;
}
.b-3 {
bottom: 620px;
left: 1530px;
}

View File

@ -1,16 +1,19 @@
import { createApp } from 'vue'; import { createApp } from "vue";
import { createPinia } from 'pinia' import { createPinia } from "pinia";
import "./style.css";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import Vue3Marquee from "vue3-marquee";
import App from "./App.vue";
const pinia = createPinia();
import './style.css'; setTimeout(() => {
import ElementPlus from 'element-plus'; window.location.reload();
import 'element-plus/dist/index.css'; }, 24 * 60 * 60 * 1000);
import Vue3Marquee from 'vue3-marquee';
import App from './App.vue';
const pinia = createPinia()
const app = createApp(App); const app = createApp(App);
app.use(pinia); app.use(pinia);
app.use(ElementPlus); app.use(ElementPlus);
app.use(Vue3Marquee, { name: 'ScrollText' }); app.use(Vue3Marquee, { name: "ScrollText" });
app.mount('#app'); app.mount("#app");

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
<!-- 实时数据页面 --> <!-- 实时数据页面 -->
<script setup> <script setup>
import HourChart from "../components/datapage/HourChart.vue"; import HourChart from "../components/datapage/HourChart.vue";
import TeamChartDay from "../components/datapage/TeamChartDay.vue"; // import TeamChartDay from "../components/datapage/TeamChartDay.vue";
import TeamChartMonth from "../components/datapage/TeamChartMonth.vue"; // import TeamChartMonth from "../components/datapage/TeamChartMonth.vue";
import LatestWeekYield from '../components/datapage/LatestWeekYield.vue' import LatestWeekYield from '../components/datapage/LatestWeekYield.vue'
import LineToday from '../components/datapage/LineToday.vue'; import LineToday from '../components/datapage/LineToday.vue';
import LineMonth from '../components/datapage/LineMonth.vue'; import LineMonth from '../components/datapage/LineMonth.vue';
@ -20,9 +20,9 @@ import LineMonth from '../components/datapage/LineMonth.vue';
<!-- 本月生产线情况 --> <!-- 本月生产线情况 -->
<LineMonth /> <LineMonth />
<!-- 本日班组情况 --> <!-- 本日班组情况 -->
<TeamChartDay /> <!-- <TeamChartDay /> -->
<!-- 本月班组情况 --> <!-- 本月班组情况 -->
<TeamChartMonth /> <!-- <TeamChartMonth /> -->
</div> </div>
</template> </template>
@ -31,10 +31,10 @@ import LineMonth from '../components/datapage/LineMonth.vue';
flex: 1; flex: 1;
position: relative; position: relative;
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr; grid-template-rows: 1fr 1fr;
gap: 24px; gap: 24px;
padding: 32px; padding: 32px 56px;
align-items: stretch; align-items: stretch;
} }

View File

@ -3,6 +3,7 @@
import HourChart from "../components/HourChart.vue"; import HourChart from "../components/HourChart.vue";
import TeamChartDay from "../components/TeamChartDay.vue"; import TeamChartDay from "../components/TeamChartDay.vue";
import TeamChartMonth from "../components/TeamChartMonth.vue"; import TeamChartMonth from "../components/TeamChartMonth.vue";
import LatestWeekYield from '../components/LatestWeekYield.vue'
import ThreeD from './3D.vue' import ThreeD from './3D.vue'
const props = defineProps({ const props = defineProps({
@ -21,8 +22,9 @@ const props = defineProps({
</div> </div>
<div class="data-list"> <div class="data-list">
<HourChart /> <HourChart />
<TeamChartDay /> <LatestWeekYield />
<TeamChartMonth /> <!-- <TeamChartDay />
<TeamChartMonth /> -->
</div> </div>
</div> </div>
</template> </template>
@ -38,7 +40,7 @@ const props = defineProps({
width: 480px; width: 480px;
position: absolute; position: absolute;
top: 0; top: 24px;
right: 32px; right: 32px;
gap: 24px; gap: 24px;
display: flex; display: flex;

View File

@ -2,9 +2,11 @@ import { defineStore } from "pinia";
import { ref } from "vue"; import { ref } from "vue";
export const useWsStore = defineStore("wsData", () => { export const useWsStore = defineStore("wsData", () => {
const data1 = ref({ test: "hello world" }); const data1 = ref({});
const data2 = ref({}); const data2 = ref({});
const data3 = ref({}); const data3 = ref({});
const mainDataChart = ref({});
const mainDataAlarm = ref({});
function updateData(category, data) { function updateData(category, data) {
switch (category) { switch (category) {
@ -16,24 +18,17 @@ export const useWsStore = defineStore("wsData", () => {
break; break;
case "3": case "3":
data3.value = { ...data3.value, ...data }; data3.value = { ...data3.value, ...data };
break;
case "main-chart":
mainDataChart.value = { ...mainDataChart.value, ...data };
break;
case "main-alarm":
mainDataAlarm.value = { ...mainDataAlarm.value, ...data };
break;
default: default:
break; break;
} }
} }
return { data1, data2, data3, updateData }; return { data1, data2, data3, mainDataChart, mainDataAlarm, updateData };
}); });
// export const useWsStore = defineStore('wsData', {
// state: () => ({
// data1: {
// test: 'hello world'
// },
// data2: null,
// data3: null,
// }),
// actions: {
// updateData(category, data) {
// this[category] = data
// }
// }
// })

206
src/utils/glass.js Normal file
View File

@ -0,0 +1,206 @@
import glassImgSrc from "../assets/glass.png";
import { connect0 } from "./useWebsocket";
import { ref, watchEffect, onMounted, h } from "vue";
const positionMapping = {
A1: {
M101: [640, 420, -26, 150], // top, left, angle, distance(px)
M201: [675, 420, -26, 150],
M105_1: [530, 680, -28, 128],
M205_1: [560, 680, -28, 138],
},
A2: {
M101_1: [440, 880, -28, 100],
M201_1: [470, 880, -28, 60],
// M201_1: {
// // 分段
// 0: [0, 0, 60, 400],
// 1: [0, 0, 60, 400],
// 2: [0, 0, 60, 400],
// },
},
A3: {
M101_1: [285, 1250, -32, 140],
},
B1: {
M101: [720, 410, -27, 190],
M201: [760, 410, -27, 215],
M301: [800, 410, -27, 240],
M105_1: [590, 710, -28, 140],
M205_1: [615, 730, -29, 145],
M305_1: [640, 750, -29, 150],
},
B2: {
M101: [490, 930, -30, 60],
M201: [515, 960, -30, 80],
M301: [530, 990, -30, 50],
},
B3: {
M101: [350, 1310, -32, 180],
},
C1: {
M101: [825, 510, -27, 190],
M201: [870, 510, -27, 210],
M104_1: [705, 760, -26, 150],
M204_1: [740, 775, -26, 165],
},
C2: {
// M101: { 0: [], 1: [] },
M101: [590, 980, -28, 30],
M201: [620, 1000, -29, 150],
},
C3: {
M101: [415, 1400, -34, 210],
},
};
function calcPosition(positionString = "") {
const lineId = positionString.slice(0, 2).toUpperCase(); // A1 B1 C1
const subLineId = positionString.slice(2).toUpperCase(); // M1 M206 M3
return {
top: positionMapping[lineId][subLineId][0],
left: positionMapping[lineId][subLineId][1],
angle: positionMapping[lineId][subLineId][2],
distance: positionMapping[lineId][subLineId][3],
};
}
export default class Glass {
el = null;
distance = 100;
valid = true;
constructor(positionStr, size, bgColor) {
const { left, top, angle, distance } = calcPosition(positionStr);
// this.el = document.createElement("img");
// this.el.src = glassImgSrc;
this.el = document.createElement("div");
this.el.style.background = bgColor || "#f00";
this.el.style.position = "absolute";
this.el.style.top = top ? `${top}px` : "740px";
this.el.style.left = left ? `${left}px` : "380px";
this.distance = distance || 100;
// this.el.style.border = "3px solid red";
// this.el.style.display = "none";
this.el.style.borderRadius = "2px";
this.el.style.boxShadow = "0 0 10px 2px #000";
this.el.style.width = size ? `${size}px` : "16px";
this.el.style.height = size ? `${size}px` : "16px";
this.el.style.transformOrigin = "center";
this.el.style.transition = `all ${distance / 25}s linear`;
this.el.style.zIndex = "100";
this.el.style.pointerEvents = "none";
// this.el.style.transform = `rotate(${angle}deg) skew(32deg, 8deg)`;
this.el.style.transform = `rotate(${angle}deg) skew(${
-1 * angle
}deg, ${Math.floor((-1 * angle) / 4.5)}deg)`;
// debugger;
document.getElementById("glass-layer").appendChild(this.el);
this.el.addEventListener("transitionend", () => {
// document.body.removeChild(this.el);
this.el.style.opacity = 0;
this.valid = false;
});
}
get visible() {
return this.visibleOnScreen;
}
move() {
const getTranslate = (d) => `${d}px ${-1 * Math.floor(Math.cos(24) * d)}px`;
this.el.style.translate = getTranslate(this.distance);
this.el.style.opacity = 1;
}
}
/** 允许展示的工段 */
const allowedList = [
"c2m101",
"c2m201",
"c1m104_1",
"c1m204_1",
"c2m101",
"c2m201",
"a3m101_1",
"b3m101",
"c3m101",
"a2m101_1",
"a2m201_1",
"b2m101",
"b2m201",
"b2m301",
"a1m105_1",
"a1m205_1",
"b1m105_1",
"b1m205_1",
"b1m305_1",
"a1m101",
"a1m201",
"b1m101",
"b1m201",
"b1m301",
"c1m101",
"c1m201",
].map((item) => item.toUpperCase());
export function useGlass(store) {
onMounted(() => {
connect0(store);
});
const grassArr = ref({});
const doRender = (data) => {
return setTimeout(() => {
const commingData = data
.filter((item) => item.haveGlass)
.filter((item) => allowedList.includes(item.lightCode));
if (commingData.length != 0) {
/** 清空之前的状态 */
document.getElementById("glass-layer").innerHTML = "";
document.getElementById("glass-layer").innerHTML = "";
grassArr.value = {};
commingData.forEach((item) => {
const n = new Glass(
item.lightCode,
item.lightCode.includes("A3") ||
item.lightCode.includes("B3") ||
item.lightCode.includes("C3")
? 12
: undefined,
"#0F0D"
);
setTimeout(() => {
n.move();
}, 100);
grassArr.value[item.lightCode] = n;
});
}
}, 1000);
};
watchEffect((onInvalidate) => {
const timer = doRender(
store.mainDataAlarm.orbitalPositioningArrayList || []
);
onInvalidate(() => {
clearTimeout(timer);
});
});
return h("div", {
id: "glass-layer",
style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
pointerEvents: "none",
},
});
}

View File

@ -1,6 +1,7 @@
import Client from "./ws"; import Client from "./ws";
export default function useWebsocket(store, path) { export default function useWebsocket(store, path, excludePaths = []) {
if (excludePaths.includes(path)) return;
// connect0(store); // connect0(store);
connectPath(store, path); connectPath(store, path);
} }
@ -34,7 +35,8 @@ function connectPath(store, path) {
); );
} }
function connect0(store) { export function connect0(store) {
console.log("[*] Connecting main screen........");
new Client( new Client(
{ {
url: url + "/0", url: url + "/0",
@ -44,6 +46,14 @@ function connect0(store) {
try { try {
const data = JSON.parse(message.data); const data = JSON.parse(message.data);
console.log("message --- 0 ---> ", JSON.parse(message.data)); console.log("message --- 0 ---> ", JSON.parse(message.data));
if ("lineSumProductYield" in data) {
// 主屏推送 图表数据
store.updateData("main-chart", data);
} else {
// 主屏推送 报警数据
store.updateData("main-alarm", data);
}
} catch (err) { } catch (err) {
console.log("[x] 未解析的ws数据: ", err); console.log("[x] 未解析的ws数据: ", err);
} }