Compare commits

...

84 Commits

Author SHA1 Message Date
DESKTOP-FUDKNA8\znjsz
d77506ed31 update carosul 2024-03-21 17:05:20 +08:00
DESKTOP-FUDKNA8\znjsz
f03cf59e73 update 环 2024-03-21 15:18:50 +08:00
DESKTOP-FUDKNA8\znjsz
c2e901e4bc Merge branch 'master' of http://git.picaiba.com/g7hoo/chenzhou 2024-03-21 10:48:24 +08:00
DESKTOP-FUDKNA8\znjsz
8cf50b9cb9 1 2024-03-21 10:47:49 +08:00
gtz
04e98ab1db 管理端备份 2024-03-21 10:31:12 +08:00
gtz
347e51e9a4 Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-21 10:30:57 +08:00
DESKTOP-FUDKNA8\znjsz
fe7b34615a 1 2024-03-21 10:30:40 +08:00
gtz
1b0746cc2d Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-20 11:45:43 +08:00
DESKTOP-FUDKNA8\znjsz
c992fa0200 1 2024-03-20 11:22:50 +08:00
gtz
b97532e4e1 Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-20 10:43:12 +08:00
DESKTOP-FUDKNA8\znjsz
82573307e9 1 2024-03-20 10:36:40 +08:00
gtz
20f5cec3e9 Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-20 10:32:32 +08:00
DESKTOP-FUDKNA8\znjsz
67d59c5693 1 2024-03-20 10:30:30 +08:00
gtz
43f7ccac4c Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-20 10:23:40 +08:00
DESKTOP-FUDKNA8\znjsz
55947ceb16 update 2024-03-20 09:18:16 +08:00
DESKTOP-FUDKNA8\znjsz
fccd2ab9cc 1 2024-03-19 16:53:05 +08:00
gtz
e44b2b5f5b Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-19 16:47:46 +08:00
DESKTOP-FUDKNA8\znjsz
43a6f59afe update chart 2024-03-19 16:43:08 +08:00
gtz
aeb43c8d41 Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-03-19 15:58:00 +08:00
gtz
5652cff730 1 2024-03-19 15:57:57 +08:00
Melete
f66b3a8a7a update 2024-03-18 19:58:42 +08:00
DESKTOP-FUDKNA8\znjsz
476b936660 conflict 2024-03-18 17:16:30 +08:00
DESKTOP-FUDKNA8\znjsz
59c652c3cf add 导航 & update chart样式 & update 横坐标倾斜 2024-03-18 17:14:20 +08:00
DESKTOP-FUDKNA8\znjsz
bb7f77e3ac add 导航 & update chart样式 2024-03-18 17:13:41 +08:00
gtz
e306b0f837 1 2024-03-16 10:12:01 +08:00
DESKTOP-FUDKNA8\znjsz
85cc0c4c43 update alert list 3d info 2024-03-15 16:08:48 +08:00
DESKTOP-FUDKNA8\znjsz
cfcdf4eb0a 1 2024-03-15 14:49:00 +08:00
DESKTOP-FUDKNA8\znjsz
bb44001961 update 2024-03-14 17:06:53 +08:00
DESKTOP-FUDKNA8\znjsz
f9fc8e8c66 1 2024-03-13 16:08:09 +08:00
DESKTOP-FUDKNA8\znjsz
d56efd1c2f update main-screen glass 2024-03-11 09:43:50 +08:00
DESKTOP-FUDKNA8\znjsz
6d57597aa1 update 2024-03-08 17:09:26 +08:00
DESKTOP-FUDKNA8\znjsz
149e801d6d add 波片移动 2024-03-08 16:31:44 +08:00
DESKTOP-FUDKNA8\znjsz
c6f5bc7d80 add glass 2024-03-08 10:17:05 +08:00
DESKTOP-FUDKNA8\znjsz
822732742d update alert list screen 2024-03-06 10:14:35 +08:00
DESKTOP-FUDKNA8\znjsz
9c3b9218d2 1 2024-03-05 17:00:30 +08:00
DESKTOP-FUDKNA8\znjsz
52a9111a19 update websocket and store 2024-03-05 16:42:21 +08:00
DESKTOP-FUDKNA8\znjsz
8ff6fd02df update main-screen 2024-03-05 14:15:31 +08:00
DESKTOP-FUDKNA8\znjsz
1b33520b61 1 2024-03-05 10:58:38 +08:00
DESKTOP-FUDKNA8\znjsz
335f61c0a1 update charts 2024-03-05 10:24:45 +08:00
DESKTOP-FUDKNA8\znjsz
fb4c1500e7 update bg 2024-02-29 15:23:31 +08:00
DESKTOP-FUDKNA8\znjsz
8d30507f1b update 2024-02-28 17:02:33 +08:00
gtz
ae7adebae0 Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-02-28 16:51:57 +08:00
gtz
ae3c430d38 1 2024-02-28 16:51:52 +08:00
DESKTOP-FUDKNA8\znjsz
23bf4790df add 2 extra pages 2024-02-28 16:07:44 +08:00
DESKTOP-FUDKNA8\znjsz
544e42ac83 bugfix 2024-02-28 15:14:02 +08:00
gtz
d4001ed86e Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou 2024-02-27 16:00:17 +08:00
DESKTOP-FUDKNA8\znjsz
ca35a1768d update init settings 2024-02-27 15:59:54 +08:00
gtz
9cdd255947 1 2024-02-27 15:52:28 +08:00
DESKTOP-FUDKNA8\znjsz
1c4ce804ff done 本月班组情况 2024-01-31 11:22:01 +08:00
DESKTOP-FUDKNA8\znjsz
da305dbba9 done 本日班组情况 2024-01-31 11:16:39 +08:00
DESKTOP-FUDKNA8\znjsz
62d3566f29 done 本月生产线情况 2024-01-31 11:04:52 +08:00
DESKTOP-FUDKNA8\znjsz
7eab7ddebb done 本日生产线情况 2024-01-31 10:49:56 +08:00
DESKTOP-FUDKNA8\znjsz
e0da16c903 done latest week 2024-01-31 10:20:34 +08:00
DESKTOP-FUDKNA8\znjsz
53ead47882 1 2024-01-30 17:00:44 +08:00
DESKTOP-FUDKNA8\znjsz
31f427789f update 3d--本月班组情况 2024-01-30 15:39:10 +08:00
DESKTOP-FUDKNA8\znjsz
2a6cb3fe87 update 2024-01-30 14:23:51 +08:00
DESKTOP-FUDKNA8\znjsz
22f4b99ab1 1 2024-01-30 14:09:40 +08:00
DESKTOP-FUDKNA8\znjsz
2276c1f9dd 1 2024-01-29 17:03:21 +08:00
DESKTOP-FUDKNA8\znjsz
85e82a1913 modify 2024-01-29 14:14:06 +08:00
DESKTOP-FUDKNA8\znjsz
23cb871c93 adjust file structures 2024-01-29 09:21:49 +08:00
DESKTOP-FUDKNA8\znjsz
7c84ba4595 1 2024-01-26 17:15:51 +08:00
DESKTOP-FUDKNA8\znjsz
c314f4b78e update datapage 2024-01-26 16:48:51 +08:00
DESKTOP-FUDKNA8\znjsz
086be4da66 fix lunbo 2024-01-26 16:38:23 +08:00
DESKTOP-FUDKNA8\znjsz
259752291c fix fullscreen 2024-01-26 16:35:51 +08:00
DESKTOP-FUDKNA8\znjsz
b6b17d1ef0 1 2024-01-26 16:01:56 +08:00
DESKTOP-FUDKNA8\znjsz
9b3779555c update 3d 2024-01-26 14:41:37 +08:00
DESKTOP-FUDKNA8\znjsz
e25dfe8f02 1 2024-01-23 16:49:29 +08:00
DESKTOP-FUDKNA8\znjsz
98dd4428af 1 2024-01-23 16:32:08 +08:00
DESKTOP-FUDKNA8\znjsz
8402afcdfc update fullscreen layout 2024-01-23 15:39:15 +08:00
DESKTOP-FUDKNA8\znjsz
90c29c141c solve fullscreen issue 2024-01-23 14:55:16 +08:00
DESKTOP-FUDKNA8\znjsz
0d96fec181 update status 2024-01-23 11:23:36 +08:00
DESKTOP-FUDKNA8\znjsz
7bf5a24cda update names 2024-01-23 10:58:36 +08:00
DESKTOP-FUDKNA8\znjsz
bc96f76d7f update 2024-01-23 10:12:39 +08:00
DESKTOP-FUDKNA8\znjsz
112730b52a update 上下片数据 2024-01-23 10:08:25 +08:00
DESKTOP-FUDKNA8\znjsz
733ee07fa7 update chart month 2024-01-22 17:02:38 +08:00
DESKTOP-FUDKNA8\znjsz
32ae14602e update team chart day 2024-01-22 16:37:54 +08:00
DESKTOP-FUDKNA8\znjsz
e7a0d10423 update 2024-01-22 16:29:59 +08:00
DESKTOP-FUDKNA8\znjsz
ef3c7a422a update lunbo 2024-01-22 15:36:44 +08:00
DESKTOP-FUDKNA8\znjsz
e425de93c7 update fullscreen 2024-01-22 15:06:29 +08:00
DESKTOP-FUDKNA8\znjsz
14991c7ede setup settings store 2024-01-22 14:23:50 +08:00
DESKTOP-FUDKNA8\znjsz
71e9204fd4 done announcement 2024-01-22 13:55:27 +08:00
DESKTOP-FUDKNA8\znjsz
c39a00cac1 update tools |dialog|store 2024-01-22 11:25:13 +08:00
DESKTOP-FUDKNA8\znjsz
3d765b4026 update charts 2024-01-19 16:58:33 +08:00
DESKTOP-FUDKNA8\znjsz
ac8eb57317 update 2024-01-19 15:24:47 +08:00
77 changed files with 7649 additions and 570 deletions

View File

@@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -6,8 +6,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<body style="position: relative">
<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>
</body>
</html>

14
package-lock.json generated
View File

@@ -11,6 +11,7 @@
"echarts": "^5.4.3",
"element-plus": "^2.4.4",
"pinia": "^2.1.7",
"screenfull": "^6.0.2",
"vue": "^3.3.11",
"vue3-marquee": "^4.1.0"
},
@@ -1123,6 +1124,14 @@
"fsevents": "~2.3.2"
}
},
"node_modules/screenfull": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/screenfull/-/screenfull-6.0.2.tgz",
"integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==",
"engines": {
"node": "^14.13.1 || >=16.0.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -1888,6 +1897,11 @@
"fsevents": "~2.3.2"
}
},
"screenfull": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/screenfull/-/screenfull-6.0.2.tgz",
"integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw=="
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",

View File

@@ -12,6 +12,7 @@
"echarts": "^5.4.3",
"element-plus": "^2.4.4",
"pinia": "^2.1.7",
"screenfull": "^6.0.2",
"vue": "^3.3.11",
"vue3-marquee": "^4.1.0"
},

276
src/AlertListScreen.vue Normal file
View File

@@ -0,0 +1,276 @@
<!-- 报警列表页面 -->
<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>
<ul class="main-screen-navigator alert-screen-navigator">
<li><a href="/main-screen">主屏页面</a></li>
<li class="active">报警列表</li>
<li><a href="/1-1">分屏页面</a></li>
</ul>
</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,48 +1,55 @@
<script setup>
import { ref, onMounted } from "vue";
import MainPage from "./MainPage.vue";
import Slider from "./components/Slider.vue";
import MainScreen from "./MainScreen.vue";
import AlertListScreen from "./AlertListScreen.vue";
// import Slider from "./components/Slider.vue";
import useWebsocket from "./utils/useWebsocket";
import { useWsStore } from "./store";
const store = useWsStore();
const excludePaths = ["/alert-list", "/main-screen"];
// use websocket
let urlPath = document.location.pathname;
if (urlPath === "/") {
urlPath = "/1-1";
let urlPath = ref(document.location.pathname);
if (urlPath.value === "/") {
urlPath.value = "/1-1";
}
useWebsocket(store, urlPath);
useWebsocket(store, urlPath.value, excludePaths);
// size setting
const size = ref(80);
onMounted(() => {
setSize(size.value);
});
// const size = ref(80);
// onMounted(() => {
// setSize(size.value);
// });
// style update
const styles = ref({});
function setSize(value) {
const v = (value / 100).toFixed(2);
console.log("size change", value);
styles.value = {
transform: `scale(${v})`,
// transform: `scale(${v * 24 / 33}, ${v})`,
transformOrigin: "top left",
};
}
// const styles = ref({});
// function setSize(value) {
// const v = (value / 100).toFixed(2);
// styles.value = {
// transform: `scale(${v})`,
// // transform: `scale(${v * 24 / 33}, ${v})`,
// transformOrigin: "top left",
// };
// }
</script>
<template>
<div id="app-container">
<MainPage :style="styles" />
<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>
</template>
<style scoped>
#app-container {
width: 100vw;
height: 100vh;
/* width: 100vw;
height: 100vh; */
background: #fff;
height: 100%;
display: inline-block;
}
</style>

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>

View File

@@ -1,5 +1,5 @@
<script setup>
import { ref } from "vue";
import { ref, onMounted } from "vue";
import Tools from "./components/Tools.vue";
import NavMenu from "./components/NavMenu.vue";
import AnnoucementPage from "./pages/AnnouncementPage.vue";
@@ -8,19 +8,126 @@ import AppHeader from "./components/Base/Header.vue";
import AlertListPage from "./pages/AlertListPage.vue";
import DataPage from "./pages/DataPage.vue";
import DatetimeTool from "./components/HeadTime.vue";
import TriplePage from "./pages/3D.vue";
import ThreeDimension from "./pages/ThreeDimension.vue";
import { useSettings } from "./store/settings";
const props = defineProps(["path"]);
const pages = ["3d", "data", "realtime", "alert", "announcement"];
const currentPage = ref("3d");
const handlePageChange = (page) => {
currentPage.value = page;
console.log(currentPage.value);
};
const mainContainer = ref(null);
const store = useSettings();
const timer = ref(null);
function startCarousel(pages, duration) {
if (timer.value) clearInterval(timer.value);
timer.value = setInterval(() => {
handlePageChange(
pages[(pages.indexOf(currentPage.value) + 1) % pages.length]
);
}, duration);
}
store.$subscribe((mutation, state) => {
const pages = state.settings.carouselPages;
// 如果更新了时间
if (
mutation.events.key == "carouselTime" &&
state.settings.carouselTime > 0 &&
state.settings.carousel
) {
startCarousel(pages, state.settings.carouselTime * 1000);
} else if (mutation.events.key == "carousel") {
// 如果更新了状态
if (state.settings.carousel) {
startCarousel(pages, state.settings.carouselTime * 1000);
} else {
clearInterval(timer.value);
timer.value = null;
}
} else if (mutation.events.key == "carouselPages") {
if (state.settings.carousel) {
startCarousel(pages, state.settings.carouselTime * 1000);
} else {
clearInterval(timer.value);
timer.value = null;
}
}
});
// 检查状态
onMounted(() => {
const settings = store.settings;
const pages = settings.carouselPages;
if (settings.carousel) {
// 开始轮播
if (timer.value) clearInterval(timer.value);
timer.value = setInterval(() => {
handlePageChange(
pages[(pages.indexOf(currentPage.value) + 1) % pages.length]
);
}, settings.carouselTime * 1000);
}
// 设置分辨率
handleResolutionChange(settings.resolution.width, settings.resolution.height);
});
const pathMap = {
// 钢三线
"/3-1": 1,
"/3-2": 2,
"/3-3": 11, // 3,
"/3-4": 12,
// 钢二线
"/2-1": 5,
"/2-2": 6,
"/2-3": 7,
"/2-4": 4,
// 钢一线
"/1-1": 9,
"/1-2": 10,
"/1-3": 3,
"/1-4": 8,
};
function handleResolutionChange(width, height) {
console.log("document.documentElement", document.documentElement);
if (mainContainer.value) {
// mainContainer.value.style.width = `${width}px`;
// mainContainer.value.style.height = `${height}px`;
// changeScale(mainContainer.value, width, height)
changeScale(mainContainer.value, width, 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 resetScale(elm) {
elm.style.transform = "initial";
elm.style.transformOrigin = "initial";
}
</script>
<template>
<div class="main-container">
<div id="main-container" ref="mainContainer" class="main-container">
<DatetimeTool v-if="currentPage !== 'announcement'" />
<Tools v-if="currentPage !== 'announcement'" />
<Tools
v-if="currentPage !== 'announcement'"
@change-resolution="handleResolutionChange"
/>
<AppHeader v-if="currentPage !== 'announcement'" />
<AnnoucementPage
v-if="currentPage === 'announcement'"
@@ -28,11 +135,21 @@ const handlePageChange = (page) => {
@home="() => handlePageChange('3d')"
/>
<div v-else class="pages-wrapper">
<NavMenu @change="handlePageChange" />
<TriplePage v-if="currentPage === '3d'" line="8" />
<DataPage v-if="currentPage === 'data'" />
<AlertListPage v-if="currentPage === 'alert'" />
<RealtimePage v-if="currentPage === 'realtime'" />
<NavMenu @change="handlePageChange" :value="currentPage" />
<!-- <TriplePage v-if="currentPage === '3d'" :line="pathMap[path] ?? '1'" /> -->
<ThreeDimension
v-if="currentPage === '3d'"
:line="pathMap[path] ?? '1'"
/>
<DataPage v-if="currentPage === 'data'" :line="pathMap[path] ?? '1'" />
<AlertListPage
v-if="currentPage === 'alert'"
:line="pathMap[path] ?? '1'"
/>
<RealtimePage
v-if="currentPage === 'realtime'"
:line="pathMap[path] ?? '1'"
/>
</div>
</div>
</template>
@@ -45,10 +162,6 @@ const handlePageChange = (page) => {
position: relative;
display: flex;
flex-direction: column;
/**
background: url('https://img1.baidu.com/it/u=2052683582,2603151390&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800')
100% / 100% no-repeat;
*/
background: url(./assets/bg.png) 100% / cover no-repeat;
}

294
src/MainScreen.vue Normal file
View File

@@ -0,0 +1,294 @@
<!-- 报警列表页面 -->
<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>
<ul class="main-screen-navigator alert-screen-navigator">
<li class="active">主屏页面</li>
<li><a href="/alert-list">报警列表</a></li>
<li><a href="/1-1">分屏页面</a></li>
</ul>
</div>
</template>
<style>
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
.alert-screen-navigator a,
.main-screen-navigator a {
color: #fff;
text-decoration: none;
}
.alert-screen-navigator,
.main-screen-navigator {
position: absolute;
z-index: 1000;
bottom: 32px;
left: 0;
right: 0;
margin: 0 auto;
width: 460px;
display: flex;
justify-content: space-around;
align-items: center;
background: rgba(46, 175, 214, 0.1);
color: #fff;
font-size: 16px;
letter-spacing: 1px;
user-select: none;
padding: 12px;
border-radius: 32px;
}
.alert-screen-navigator > li,
.main-screen-navigator > li {
cursor: pointer;
transition: all 0.2s ease-out;
border-radius: 20px;
padding: 12px 24px;
font-size: 20px;
}
.alert-screen-navigator > li:hover,
.main-screen-navigator > li:hover {
background: rgba(5, 106, 246, 0.168);
}
.alert-screen-navigator > li.active,
.main-screen-navigator > li.active {
background: rgba(5, 106, 246, 0.668);
}
.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/dialog-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB

BIN
src/assets/glass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/line.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -40,7 +40,7 @@ defineProps({
result="shadowBlurOuter1"
></feGaussianBlur>
<feColorMatrix
values="0 0 0 0 0.000727194297 0 0 0 0 0.0682545659 0 0 0 0 0.141870471 0 0 0 1 0"
values="0 0 0 0 0.0123 0 0 0 0 0.0123 0 0 0 0 0.0123 0 0 0 1 0"
type="matrix"
in="shadowBlurOuter1"
result="shadowMatrixOuter1"

View File

@@ -43,7 +43,7 @@ defineProps({
<path
d="M40.6,25.8120454 C40.6,28.1705437 40.1561224,30.3912077 39.2683673,32.4740374 C38.3806122,34.5568671 37.1637755,36.4023154 35.6178571,38.0103825 C34.0719388,39.6184495 32.2505102,40.935533 30.1535714,41.9616329 C28.0566327,42.9877328 25.7989796,43.6079872 23.3806122,43.8223962 C23.227551,43.853026 23.0897959,43.8759984 22.9673469,43.8913133 C22.844898,43.9066283 22.7071429,43.9142857 22.5540816,43.9142857 L14.6102041,43.9142857 C14.0897959,43.9142857 13.5770408,43.8223962 13.0719388,43.6386171 C12.5668367,43.454838 12.1153061,43.2021417 11.7173469,42.8805283 C11.3193878,42.5589149 10.9903061,42.1836993 10.730102,41.7548814 C10.469898,41.3260635 10.3397959,40.8819307 10.3397959,40.422483 C10.3397959,39.9630353 10.4622449,39.5189025 10.7071429,39.0900846 C10.9520408,38.6612667 11.2734694,38.2937086 11.6714286,37.9874101 C12.0693878,37.6811116 12.5132653,37.4360728 13.0030612,37.2522937 C13.4928571,37.0685146 13.9979592,36.9766251 14.5183673,36.9766251 L21.6357143,37.0225699 C23.2581633,37.0225699 24.7887755,36.7239288 26.227551,36.1266468 C27.6663265,35.5293648 28.9214286,34.7253312 29.9928571,33.7145462 C31.0642857,32.7037613 31.9061224,31.5168546 32.5183673,30.1538264 C33.1306122,28.7907981 33.4367347,27.3282229 33.4367347,25.7661006 C33.4367347,24.2958679 33.1612245,22.9175247 32.6102041,21.6310711 C32.0591837,20.3446175 31.3015306,19.1959982 30.3372449,18.1852132 C29.3729592,17.1744282 28.2479592,16.3550797 26.9622449,15.7271679 C25.6765306,15.099256 24.2836735,14.7087254 22.7836735,14.5555762 C22.722449,14.5555762 22.6841837,14.5632336 22.6688776,14.5785485 C22.6535714,14.5938635 22.6153061,14.6015209 22.5540816,14.6015209 L15.2530612,14.6015209 L15.2530612,18.2311579 C15.2530612,18.7518654 15.0464286,19.1347385 14.6331633,19.3797773 C14.219898,19.624816 13.5540816,19.4716668 12.6357143,18.9203295 C12.3908163,18.7671803 12.0005102,18.4991691 11.4647959,18.116296 C10.9290816,17.7334229 10.3015306,17.2816327 9.58214286,16.7609252 C8.8627551,16.2402178 8.10510204,15.6888805 7.30918367,15.1069134 L5.01326531,13.3610121 C4.55408163,12.9934539 4.15612245,12.664183 3.81938776,12.3731995 C3.48265306,12.0822159 3.31428571,11.7223152 3.31428571,11.2934973 C3.31428571,10.9259391 3.50561224,10.543066 3.88826531,10.144878 C4.27091837,9.74668998 4.70714286,9.34850195 5.19693878,8.95031393 C5.74795918,8.4908662 6.40612245,7.95484386 7.17142857,7.34224689 C7.93673469,6.72964993 8.69438776,6.13236789 9.44438776,5.55040077 C10.1943878,4.96843365 10.8678571,4.44006877 11.4647959,3.96530612 C12.0617347,3.49054347 12.4826531,3.16127261 12.727551,2.97749352 C13.4622449,2.4567861 14.0591837,2.34958163 14.5183673,2.65588011 C14.977551,2.96217859 15.222449,3.42162632 15.2530612,4.03422328 L15.2530612,7.70980507 L22.5540816,7.70980507 C22.7071429,7.70980507 22.8678571,7.72512 23.0362245,7.75574984 C23.2045918,7.78637969 23.3653061,7.81700954 23.5183673,7.84763939 C25.9061224,8.09267818 28.1408163,8.72059006 30.222449,9.73137506 C32.3040816,10.74216 34.1178571,12.0515861 35.6637755,13.6596531 C37.2096939,15.2677201 38.4188776,17.1131685 39.2913265,19.1959982 C40.1637755,21.2788279 40.6,23.4841769 40.6,25.8120454 L40.6,25.8120454 Z"
id="back-btn"
fill="#50A1EC"
:fill="color ?? '#50A1EC'"
></path>
</g>
</g>

View File

@@ -0,0 +1,24 @@
<!-- Icon: alert Icon -->
<script setup>
defineProps({
color: {
type: String,
default: null,
},
});
</script>
<template>
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>状态icon</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="郴州旗滨光伏光电玻璃" transform="translate(-641.167539, -519.090909)" :fill="color ?? '#24FF5E'" fill-rule="nonzero">
<g id="状态icon" transform="translate(641.167539, 519.090909)">
<path
d="M21.6498582,4.55479956 C21.6463914,4.55050501 21.642058,4.54535155 21.6368579,4.54191591 C18.7967223,1.61303292 15.0188308,0 11,0 C6.98116924,0 3.20327766,1.61303292 0.362275449,4.54191591 C0.128269776,4.7824107 0,5.10278412 0,5.44377138 C0,5.78389972 0.127403089,6.10341423 0.361408761,6.34390902 C0.595414434,6.58440381 0.905688623,6.71753486 1.23676332,6.71753486 C1.56697132,6.71753486 1.87724551,6.58440381 2.11125118,6.34390902 C4.48510873,3.89687452 7.64158525,2.54924478 11,2.54924478 C14.3584147,2.54924478 17.515758,3.89773343 19.8887488,6.34476793 C20.1236212,6.58526272 20.4338954,6.71839377 20.7641034,6.71839377 C21.0943114,6.71839377 21.4054523,6.58526272 21.6377246,6.34476793 C21.8725969,6.10427314 22,5.78304081 22,5.44291247 C21.9991333,5.10879649 21.8760637,4.79443544 21.6498582,4.55479956 Z M10.9998918,22 C14.348751,22 17.0635378,19.3620338 17.0635378,16.1079376 C17.0635378,12.8538414 14.348751,10.2158752 10.9998918,10.2158752 C7.65103259,10.2158752 4.9362458,12.8538414 4.9362458,16.1079376 C4.9362458,19.3620338 7.65103259,22 10.9998918,22 Z M11,5.19640531 C8.47793886,5.19640531 6.10668137,6.16869139 4.32390482,7.9363281 C4.06909864,8.18884763 3.92869524,8.52382252 3.92869524,8.88027016 C3.92869524,9.23757671 4.06909864,9.5734105 4.32390482,9.82507112 C4.57784431,10.0767317 4.91585251,10.2158752 5.27552789,10.2158752 C5.63520328,10.2158752 5.97407816,10.0767317 6.22801765,9.82507112 C7.50291522,8.56161456 9.19815632,7.86589749 11,7.86589749 C12.8027104,7.86589749 14.4979515,8.56161456 15.7719824,9.82507112 C16.0267885,10.0767317 16.3647967,10.2158752 16.7253388,10.2158752 C17.0858809,10.2158752 17.4247558,10.0767317 17.6778286,9.82421221 C18.2030413,9.30285386 18.2013079,8.45682754 17.6778286,7.9363281 C15.8933186,6.1695503 13.5220611,5.19640531 11,5.19640531 Z"
id="形状"></path>
</g>
</g>
</g>
</svg></template>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="26px" height="26px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="28px" height="28px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>任务</title>
<defs>
<linearGradient x1="91.384997%" y1="100%" x2="25.4330364%" y2="7.84095011e-14%" id="linearGradient-1">

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="26px" height="26px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="28px" height="28px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>更多 2</title>
<defs>
<linearGradient x1="100%" y1="100%" x2="20.318998%" y2="7.84095011e-14%" id="linearGradient-1">

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="26px" height="26px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="28px" height="28px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>多种 2</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="环动科技热处理车间立库看板" transform="translate(-39.000000, -620.000000)" fill="#FFFFFF" fill-rule="nonzero">

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="26px" height="26px" viewBox="0 0 24.4721163 23.7661066" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="28px" height="28px" viewBox="0 0 24.4721163 23.7661066" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>多种</title>
<defs>
<linearGradient x1="100%" y1="89.6187762%" x2="20.318998%" y2="10.3812238%" id="linearGradient-1">

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>状态icon</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="郴州旗滨光伏光电玻璃" transform="translate(-641.167539, -519.090909)" fill="#FF3737" fill-rule="nonzero">
<g id="状态icon" transform="translate(641.167539, 519.090909)">
<path d="M21.6498582,4.55479956 C21.6463914,4.55050501 21.642058,4.54535155 21.6368579,4.54191591 C18.7967223,1.61303292 15.0188308,0 11,0 C6.98116924,0 3.20327766,1.61303292 0.362275449,4.54191591 C0.128269776,4.7824107 0,5.10278412 0,5.44377138 C0,5.78389972 0.127403089,6.10341423 0.361408761,6.34390902 C0.595414434,6.58440381 0.905688623,6.71753486 1.23676332,6.71753486 C1.56697132,6.71753486 1.87724551,6.58440381 2.11125118,6.34390902 C4.48510873,3.89687452 7.64158525,2.54924478 11,2.54924478 C14.3584147,2.54924478 17.515758,3.89773343 19.8887488,6.34476793 C20.1236212,6.58526272 20.4338954,6.71839377 20.7641034,6.71839377 C21.0943114,6.71839377 21.4054523,6.58526272 21.6377246,6.34476793 C21.8725969,6.10427314 22,5.78304081 22,5.44291247 C21.9991333,5.10879649 21.8760637,4.79443544 21.6498582,4.55479956 Z M10.9998918,22 C14.348751,22 17.0635378,19.3620338 17.0635378,16.1079376 C17.0635378,12.8538414 14.348751,10.2158752 10.9998918,10.2158752 C7.65103259,10.2158752 4.9362458,12.8538414 4.9362458,16.1079376 C4.9362458,19.3620338 7.65103259,22 10.9998918,22 Z M11,5.19640531 C8.47793886,5.19640531 6.10668137,6.16869139 4.32390482,7.9363281 C4.06909864,8.18884763 3.92869524,8.52382252 3.92869524,8.88027016 C3.92869524,9.23757671 4.06909864,9.5734105 4.32390482,9.82507112 C4.57784431,10.0767317 4.91585251,10.2158752 5.27552789,10.2158752 C5.63520328,10.2158752 5.97407816,10.0767317 6.22801765,9.82507112 C7.50291522,8.56161456 9.19815632,7.86589749 11,7.86589749 C12.8027104,7.86589749 14.4979515,8.56161456 15.7719824,9.82507112 C16.0267885,10.0767317 16.3647967,10.2158752 16.7253388,10.2158752 C17.0858809,10.2158752 17.4247558,10.0767317 17.6778286,9.82421221 C18.2030413,9.30285386 18.2013079,8.45682754 17.6778286,7.9363281 C15.8933186,6.1695503 13.5220611,5.19640531 11,5.19640531 Z" id="形状"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -51,7 +51,7 @@ const props = defineProps({
<div class="cube" :class="[`cube-${cubeCorner}`]"></div>
<div class="container-header">
<Icon :name="icon" />
<span>{{ title }}</span>
<span class="container-header__title">{{ title }}</span>
</div>
<div class="container-body">
<slot />
@@ -105,6 +105,10 @@ const props = defineProps({
padding: 8px 12px;
}
.container-header__title {
font-size: 24px;
}
.container-body {
background: #ffffff11;
backdrop-filter: blur(2px);

View File

@@ -1,11 +1,10 @@
<script setup>
import { ref, computed } from 'vue';
</script>
<template>
<header class="header">
<div>
<Icon htmlRole="logo" icon="logo" />
<div class="icon"></div>
<h1 class="header__title">郴州旗滨光伏光电玻璃有限公司</h1>
</div>
</header>
@@ -18,18 +17,29 @@ import { ref, computed } from 'vue';
background: url(../../assets/header-bg@2x.png) 100% / 100% no-repeat;
}
header .icon {
background: url(../../assets/logo.png) 100% / contain no-repeat;
width: 52px;
height: 40px;
}
.header > div {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
/** width: 520px; **/
width: 480px;
width: 520px;
top: 20px;
display: flex;
align-items: center;
gap: 12px;
}
.header__title {
font-weight: 400;
font-size: 30px;
letter-spacing: 2px;
color: #fff;
user-select: none;

View File

@@ -41,9 +41,8 @@ const icon = computed(() => {
<style scoped>
.icon {
height: 16px;
width: 16px;
background: #fff3;
height: 24px;
width: 24px;
display: flex;
justify-content: center;
align-items: center;

View File

@@ -0,0 +1,159 @@
<script setup>
import { computed, nextTick, onMounted, ref, watch } from "vue";
import * as echarts from "echarts";
import getOptions from "./rateOption";
const props = defineProps({
isOnlyChild: {
type: Boolean,
default: false,
},
rawData: {
type: Object,
default: () => {
return {
targetProduction: 0,
nowProduction: 0,
targetYield: 0,
nowYield: 0,
};
},
},
displayPlaceholder: {
type: Boolean,
default: false,
},
});
watch(
() => props.isOnlyChild,
(newVal) => {
reInitChart();
}
);
const rate = computed(() => {
if (!props.rawData?.nowYield || props.rawData.nowYield == 0) return [0, 0];
// const _rate = ((props.rawData.nowYield / props.rawData.targetYield) * 100)
// .toFixed(2)
// .toString();
// return [parseInt(_rate), _rate.split(".")[1]];
return [
parseInt(props.rawData.nowYield),
((props.rawData.nowYield + "").split(".")[1] ?? "").padStart(2, "0"),
];
});
const chart = ref(null);
const rateChartRef = ref(null);
function reInitChart() {
if (chart.value) chart.value.dispose();
const _chart = echarts.init(rateChartRef.value);
_chart.setOption(getOptions(props.rawData));
chart.value = _chart;
}
onMounted(() => {
nextTick(() => {
reInitChart();
});
});
</script>
<template>
<div class="chart rate-chart">
<div ref="rateChartRef" class="chart-container"></div>
<div :class="['fake-chart-title', isOnlyChild ? 'is-only-child' : '']">
<span class="integer-part">{{ rate[0] }}.</span>
<span class="decimal-part">{{ rate[1] }}%</span>
</div>
<div class="text-intro">
<div class="text-intro__item">
<span class="legend-box green"></span>
<span>当前成品率: {{ props.rawData?.nowYield ?? 0 }}%</span>
</div>
<div class="text-intro__item">
<span class="legend-box blue"></span>
<span>目标成品率: {{ props.rawData?.targetYield ?? 0 }}%</span>
</div>
</div>
</div>
</template>
<style scoped>
.rate-chart {
height: 240px;
flex-grow: 1;
position: relative;
}
.chart-placeholder,
.chart-container {
margin: auto;
width: 320px;
height: 100%;
background: "#0f01";
position: relative;
}
.fake-chart-title {
user-select: none;
position: absolute;
top: 30%;
left: 36%;
}
.fake-chart-title.is-only-child {
left: 36%;
}
.fake-chart-title > .integer-part {
font-size: 48px;
color: #fff;
}
.fake-chart-title > .decimal-part {
font-size: 32px;
color: #fff;
}
.text-intro {
position: absolute;
height: auto;
width: 240px;
bottom: 18px;
left: 0;
right: 0;
margin: 0 auto;
padding: 12px;
display: flex;
flex-direction: column;
gap: 12px;
align-items: center;
user-select: none;
}
.text-intro__item {
font-size: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.legend-box {
width: 16px;
height: 16px;
border-radius: 4px;
}
.green {
background: #4cf0e8;
}
.blue {
background: #1065ff;
}
</style>

View File

@@ -0,0 +1,110 @@
<script setup>
import { nextTick, onMounted, ref, watch } from "vue";
import * as echarts from "echarts";
import getOptions from "./yieldOption";
const props = defineProps({
rawData: {
type: Object,
default: () => {
return {
targetProduction: 0,
nowProduction: 0,
targetYield: 0,
nowYield: 0,
};
},
},
displayPlaceholder: {
type: Boolean,
default: false,
},
});
const chart = ref(null);
const yieldChartRef = ref(null);
function reInitChart() {
// if (props.displayPlaceholder) return;
if (chart.value) chart.value.dispose();
const _chart = echarts.init(yieldChartRef.value);
_chart.setOption(
getOptions(
props.rawData ?? {
nowProduction: 0,
targetProduction: 0,
}
)
);
chart.value = _chart;
}
onMounted(() => {
nextTick(() => {
reInitChart();
});
});
</script>
<template>
<div class="chart yield-chart">
<!-- <div v-if="displayPlaceholder" class="chart-placeholder"></div>
<div v-else ref="yieldChartRef" class="chart-container"></div> -->
<div ref="yieldChartRef" class="chart-container"></div>
<div class="text-intro">
<div class="text-intro__item">
<span class="legend-box green"></span>
<span>当前产量: {{ rawData?.nowProduction ?? 0 }}</span>
</div>
<div v-if="!displayPlaceholder" class="text-intro__item">
<span>目标产量: {{ rawData?.targetProduction ?? 0 }}</span>
</div>
</div>
</div>
</template>
<style scoped>
.yield-chart {
height: 240px;
flex-grow: 1;
position: relative;
}
.chart-placeholder,
.chart-container {
margin: auto;
width: 320px;
height: 100%;
background: "#0f01";
}
.text-intro {
position: absolute;
height: auto;
width: 220px;
bottom: 18px;
left: 0;
right: 0;
margin: 0 auto;
padding: 12px;
display: flex;
flex-direction: column;
gap: 12px;
align-items: center;
user-select: none;
}
.text-intro__item {
font-size: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.legend-box {
width: 16px;
height: 16px;
border-radius: 4px;
background: #4cf0e8;
}
</style>

View File

@@ -0,0 +1,149 @@
const radius = ["58%", "72%"];
const radius2 = ["45%", "58%"];
const grid = {
top: 0,
left: 24,
right: 24,
bottom: 32,
};
const title = {
// 由外部负责展示,此处占位
text: " ",
left: "50%",
top: "30%",
textAlign: "center",
textStyle: {
fontWeight: 400,
fontSize: 48,
color: "#fffd",
},
subtext: "当前成品率\u2002",
subtextStyle: {
fontSize: 20,
fontWeight: 100,
color: "#fffd",
align: "right",
},
};
const tooltip = {
// trigger: "item",
show: false,
};
const legend = {
top: "5%",
left: "center",
};
const bgSerie = {
type: "pie",
radius: radius,
center: ["50%", "40%"],
emptyCircleStyle: {
color: "#042c5f33",
},
};
const dataSerie = {
type: "pie",
radius: radius,
center: ["50%", "40%"],
avoidLabelOvervlap: false,
label: {
show: false,
// position: "center",
},
labelLine: {
show: false,
},
data: [
{
value: 90,
name: "当前成品率",
selected: false,
itemStyle: {
borderJoin: "round",
borderCap: "round",
borderWidth: 12,
borderRadius: "50%",
color: {
type: "linear",
x: 1,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: "#4CF0E811" },
{ offset: 1, color: "#4CF0E8" },
],
},
},
},
{
value: 20,
name: "-",
itemStyle: { color: "transparent" },
label: { show: false },
},
],
};
const targetSerie = {
type: "pie",
radius: radius2,
center: ["50%", "40%"],
avoidLabelOvervlap: false,
label: {
show: false,
},
labelLine: {
show: false,
},
data: [
{
value: 90,
name: "目标成品率",
selected: false,
itemStyle: {
borderJoin: "round",
borderCap: "round",
borderWidth: 12,
borderRadius: "50%",
color: {
type: "linear",
x: 1,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: "#1065ff66" },
{ offset: 1, color: "#1065ff" },
],
},
},
},
{
value: 20,
name: "-",
itemStyle: { color: "transparent" },
label: { show: false },
},
],
};
export default (data) => {
title.subtext = "当前成品率\u2002";
dataSerie.data[0].value = data?.nowYield ?? 0;
dataSerie.data[1].value = 100 - (data?.nowYield ?? 0);
targetSerie.data[0].value = data?.targetYield ?? 0;
targetSerie.data[1].value = 100 - (data?.targetYield ?? 0);
return {
tooltip,
title,
grid,
series: [
// background
bgSerie,
// actual data
dataSerie,
// target data
targetSerie,
],
};
};

View File

@@ -0,0 +1,157 @@
const radius = ["58%", "72%"];
const radius2 = ["45%", "58%"];
const grid = {
top: 0,
left: 24,
right: 24,
bottom: 32,
};
const title = {
text: "75%",
left: "50%",
top: "30%",
textAlign: "center",
textStyle: {
fontWeight: 400,
fontSize: 48,
color: "#fffd",
},
subtext: "当前产量\u2002",
subtextStyle: {
fontSize: 20,
fontWeight: 100,
color: "#fffd",
align: "right",
},
};
const tooltip = {
// trigger: "item",
show: false,
};
const legend = {
top: "5%",
left: "center",
};
const bgSerie = {
type: "pie",
radius: radius,
center: ["50%", "40%"],
emptyCircleStyle: {
color: "#042c5f33",
},
};
const dataSerie = {
type: "pie",
radius: radius,
center: ["50%", "40%"],
avoidLabelOvervlap: false,
label: {
show: false,
// position: "center",
},
labelLine: {
show: false,
},
data: [
{
value: 90,
name: "当前产量",
selected: false,
itemStyle: {
borderJoin: "round",
borderCap: "round",
borderWidth: 12,
borderRadius: "50%",
color: {
type: "linear",
x: 1,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: "#4CF0E811" },
{ offset: 1, color: "#4CF0E8" },
],
},
},
},
{
value: 20,
name: "-",
itemStyle: { color: "transparent" },
label: { show: false },
},
],
};
const targetSerie = {
type: "pie",
radius: radius2,
center: ["50%", "40%"],
avoidLabelOvervlap: false,
label: {
show: false,
},
labelLine: {
show: false,
},
data: [
{
value: 90,
name: "目标成产量",
selected: false,
itemStyle: {
borderJoin: "round",
borderCap: "round",
borderWidth: 12,
borderRadius: "50%",
color: {
type: "linear",
x: 1,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: "#1065ff66" },
{ offset: 1, color: "#1065ff" },
],
},
},
},
{
value: 20,
name: "-",
itemStyle: { color: "transparent" },
label: { show: false },
},
],
};
export default (data) => {
// title.text =
// (100 * (+data.nowProduction / +data.targetProduction)).toFixed(0) + "%";
// 外圈
title.text = data.nowProduction || 0;
dataSerie.data[0].value = data.nowProduction;
dataSerie.data[1].value = !data.targetProduction
? data.nowProduction == 0
? 1
: 0
: data.targetProduction - data.nowProduction;
// 内圈
targetSerie.data[0].value = data?.targetProduction ?? 0;
targetSerie.data[1].value = data?.targetProduction ? 0 : 1;
return {
tooltip,
title,
grid,
series: [
// background
bgSerie,
dataSerie,
targetSerie,
],
};
};

View File

@@ -24,7 +24,7 @@ onMounted(() => {
top: 50px;
right: 420px;
font-size: 24px;
color: #69B4FF;
color: #69b4ff;
line-height: 1;
}
</style>

View File

@@ -1,5 +1,5 @@
<script setup>
import { ref, watch, onMounted } from "vue";
import { ref, watch, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "./Base/Container.vue";
import { useWsStore } from "../store";
@@ -9,57 +9,80 @@ const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
// 小时数据
const hourData = ref(null);
const hourData = ref([
// { lineName: "001", hour: "00:00", num: 10 },
// { lineName: "002", hour: "00:20", num: 20 },
// { lineName: "003", hour: "00:30", num: 30 },
// { lineName: "004", hour: "00:40", num: 14 },
// { lineName: "005", hour: "00:50", num: 50 },
]);
store.$subscribe((mutation, state) => {
console.log("lineHourList ===> ", state.data2.lineHourList);
if (
state.data2.lineHourList == undefined ||
state.data2.lineHourList?.length == 0
) {
hourData.value.splice(0);
if (chart.value) chart.value.dispose();
return;
}
hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"),
data: item.num || Math.random() * 100,
hour: item.hour || "__",
data: item.num || 0,
}));
chartSetup(
chart.value,
hourData.value.map((item) => item.hour),
hourData.value.map((item) => item.data)
);
setupChart();
});
// watch(hourData, (newVal) => {
// console.log("hourData", newVal);
// if (newVal) {
// chartSetup(
// chart.value,
// newVal.map((item) => item.hour),
// newVal.map((item) => item.data)
// );
// }
// });
function setupChart() {
if (chart.value) chart.value.dispose();
nextTick(() => {
chart.value = echarts.init(chartChart.value);
chartSetup(
chart.value,
hourData.value.map((item) => item.hour),
hourData.value.map((item) => item.data)
);
});
// chart.value.setOption({
// xAxis: {
// type: "category",
// data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
// },
// yAxis: {
// type: "value",
// },
// series: [
// {
// data: [150, 230, 224, 218, 135, 147, 260],
// type: "line",
// },
// ],
// });
}
onMounted(() => {
chartChart.value.classList.add("h-full");
chart.value = echarts.init(chartChart.value);
chart.value.setOption({})
// setupChart();
});
</script>
<template>
<Container class="chart" title="小时数据" icon="cube">
<div
v-show="hourData && hourData.length > 0"
ref="chartChart"
class="chart-chart"
style="{opacity: (hourData && hourData.length > 0) ? 1 : 0}"
></div>
<p v-show="!hourData || hourData.length === 0" class="empty-data-hint">暂无数据</p>
<p v-show="!hourData || hourData.length === 0" class="empty-data-hint">
暂无数据
</p>
</Container>
</template>
<style scoped>
.chart {
height: 300px;
}
.chart-inner {
background: #0f0;
height: 450px;
}
.chart-chart {

View File

@@ -1,21 +1,23 @@
export const options = {
color: ["#FFD160", "#99D66C", "#5B9BFF", "#2760FF", "#8167F6", "#FF00B2"],
grid: {
top: "5%",
bottom: "5%",
left: "3%",
right: "3%",
top: 10,
bottom: 0,
left: 12,
right: 10,
containLabel: true,
},
xAxis: {
type: "category",
data: ["00", "11", "22", "33", "44", "55", "66"],
data: [],
axisLabel: {
fontSize: 16,
color: '#fff8'
},
axisLine: {
show: true,
lineStyle: {
color: "#dff1fe",
color: "#e6e6e633",
},
},
},
@@ -24,19 +26,42 @@ export const options = {
axisLine: {
show: true,
lineStyle: {
color: "#dff1fe",
color: "#e6e6e633",
},
},
splitLine: {
lineStyle: {
color: '#e6e6e633'
}
},
axisLabel: {
fontSize: 16,
color: '#fff8'
},
minInterval: 1,
// minInterval: 1,
// max: 100,
// min: 1,
max: function(value) {
return value.max + Math.floor(value.max / 5)
}
},
series: [
{
data: Array.from({ length: 7 }, () => Math.random() * 100),
data: [],
// data: Array.from({ length: 7 }, () => Math.random() * 100),
type: "bar",
// barWidth: 20,
showBackground: true,
label: {
show: true,
distance: 20,
rotate: 90,
color: '#fff',
fontSize: 16,
verticalAlign: "middle",
position: 'top',
formatter: "{c}"
},
backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)",
},

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

@@ -5,7 +5,7 @@ import IconAlert from "../assets/menu_icon/IconAlert.vue";
import IconChart from "../assets/menu_icon/IconChart.vue";
import IconRealtime from "../assets/menu_icon/IconRealtime.vue";
import IconAnnounce from "../assets/menu_icon/IconAnnouncement.vue";
const props = defineProps(['value'])
const emit = defineEmits(["change"]);
const handleClick = (page) => {
emit("change", page);
@@ -17,27 +17,32 @@ const handleClick = (page) => {
<ul class="flex-list">
<li>
<button type="button" @click="(e) => handleClick('3d')">
<span style="">三维界面</span><Icon3D class="nav-icon" />
<span :style="{ color: value == '3d' ? '#b1daff' : '#339dff' }">三维界面</span>
<Icon3D class="nav-icon" :color="value == '3d' ? '#b1daff' : '#339dff'" />
</button>
</li>
<li>
<button type="button" @click="(e) => handleClick('data')">
<span style="">数据界面</span><IconChart class="nav-icon" />
<span :style="{ color: value == 'data' ? '#b1daff' : '#339dff' }">数据界面</span>
<IconChart class="nav-icon" :color="value == 'data' ? '#b1daff' : '#339dff'" />
</button>
</li>
<li>
<button type="button" @click="(e) => handleClick('realtime')">
<span style="">实时数据</span><IconRealtime class="nav-icon" />
<span :style="{ color: value == 'realtime' ? '#b1daff' : '#339dff' }">实时数据</span>
<IconRealtime class="nav-icon" :color="value == 'realtime' ? '#b1daff' : '#339dff'" />
</button>
</li>
<li>
<button type="button" @click="(e) => handleClick('alert')">
<span style="">报警列表</span><IconAlert class="nav-icon" />
<span :style="{ color: value == 'alert' ? '#b1daff' : '#339dff' }">报警列表</span>
<IconAlert class="nav-icon" :color="value == 'alert' ? '#b1daff' : '#339dff'" />
</button>
</li>
<li>
<button type="button" @click="(e) => handleClick('announcement')">
<span style="">公告页面</span><IconAnnounce class="nav-icon" />
<span style="">公告页面</span>
<IconAnnounce class="nav-icon" />
</button>
</li>
</ul>
@@ -54,8 +59,13 @@ const handleClick = (page) => {
position: relative;
top: 72px;
left: 0;
z-index: 10;
}
/* :fullscreen .nav-menu {
top: 25%;
} */
.flex-list {
display: flex;
flex-direction: column;
@@ -84,7 +94,7 @@ li {
gap: 8px;
}
.nav-menu button > span {
.nav-menu button>span {
font-size: 32px;
letter-spacing: 3px;
line-height: 45px;
@@ -92,18 +102,19 @@ li {
text-shadow: 0 5px 1px #001124;
/* text-shadow: 0 5px 1px #004969; */
}
.nav-menu button:hover > span {
color: #b1daff;
.nav-menu button:hover>span {
color: #b1daff !important;
}
</style>
<style>
.nav-menu button:hover > .nav-icon #icon3d-g,
.nav-menu button:hover > .nav-icon #alert-rect,
.nav-menu button:hover > .nav-icon #alert-dot,
.nav-menu button:hover > .nav-icon #realtime-main,
.nav-menu button:hover > .nav-icon #announce-main,
.nav-menu button:hover > .nav-icon #chart-main {
.nav-menu button:hover>.nav-icon #icon3d-g,
.nav-menu button:hover>.nav-icon #alert-rect,
.nav-menu button:hover>.nav-icon #alert-dot,
.nav-menu button:hover>.nav-icon #realtime-main,
.nav-menu button:hover>.nav-icon #announce-main,
.nav-menu button:hover>.nav-icon #chart-main {
fill: #b1daff !important;
}
</style>

View File

@@ -41,7 +41,7 @@ const options = {
axisLabel: { show: false },
axisTick: { show: false },
splitLine: { show: false },
data: ["废片", "阶段成品"],
data: ["阶段成品", "废片"],
axisLabel: {
fontSize: 24,
color: "#fff",

View File

@@ -3,8 +3,26 @@
import { ref } from "vue";
import { useWsStore } from "../store";
const listData = ref([]);
const store = useWsStore();
const listData = ref((store.data2.lineDetailData || Array(3).fill({})).map(
(item, index) => ({
productLine: item.productLine || "---",
mbt: item.edgingInput || "---",
mbb: item.edgingOutput || "---",
dkt: item.drillingInput || "---",
dkb: item.drillingOutput || "---",
dmt: item.coatingInput || "---",
dmb: item.coatingOutput || "---",
syt: item.silkInput || "---",
syb: item.silkOutput || "---",
ght: item.solidificationInput || "---",
ghb: item.solidificationOutput || "---",
gh1: item.temperingInput || "---",
gh2: item.temperingOutput || "---",
bzt: item.finalInput || "---",
bzb: item.finalOutput || "---",
})
));
store.$subscribe((mutation, state) => {
listData.value = (state.data2.lineDetailData || Array(3).fill({})).map(
(item, index) => ({
@@ -30,13 +48,8 @@ store.$subscribe((mutation, state) => {
<template>
<div class="realtime-table">
<el-table
class="dark-table"
:data="listData"
:show-overflow-tooltip="true"
row-class-name="dark-row"
header-row-class-name="dark-header"
>
<el-table class="dark-table" :data="listData" :show-overflow-tooltip="true" row-class-name="dark-row"
header-row-class-name="dark-header">
<el-table-column prop="productLine" label="产线"></el-table-column>
<el-table-column prop="mbt" label="磨边上"></el-table-column>
<el-table-column prop="mbb" label="磨边下"></el-table-column>
@@ -58,7 +71,7 @@ store.$subscribe((mutation, state) => {
<style scoped>
.realtime-table {
background: #00f3;
/* background: #00f3; */
height: 160px;
width: 80%;
align-self: self-start;
@@ -68,7 +81,7 @@ store.$subscribe((mutation, state) => {
height: 100%;
}
.dark-table >>> .el-table__inner-wrapper::before {
.dark-table>>>.el-table__inner-wrapper::before {
background-color: transparent !important;
}
</style>

View File

@@ -0,0 +1,363 @@
<script setup>
import { ref } from "vue";
import { useSettings } from "../store/settings";
const emit = defineEmits(["close", "change-resolution"]);
const store = useSettings();
const settings = ref(store.settings);
store.$subscribe((mutation, state) => {
settings.value.fullscreen = state.settings.fullscreen;
});
function handleCancel() {
emit("close");
}
function handleConfirm() {
if (settings.value.resolution.width < 480)
store.settings.resolution.width = 480;
if (settings.value.resolution.width > 7680)
store.settings.resolution.width = 7680;
if (settings.value.resolution.height < 270)
store.settings.resolution.height = 270;
if (settings.value.resolution.height > 4320)
store.settings.resolution.height = 4320;
emit(
"change-resolution",
store.settings.resolution.width,
store.settings.resolution.height
);
}
</script>
<template>
<div class="setting-dialog">
<h1>设置</h1>
<div class="main-content">
<div class="form-item">
<label for="carousel">轮播时间</label>
<input id="carousel" type="number" v-model="settings.carouselTime" />
<span></span>
</div>
<div
class="form-item"
style="display: flex; flex-direction: column; gap: 12px"
>
<label for="carouselPages">轮播项目</label>
<div class="carousel-page__list">
<div>
<input
type="checkbox"
id="cp-3d"
name="carouselPages"
:class="[
settings.carouselPages.includes('3d') ? 'checked' : '',
'carousel-page',
]"
@change="
() => {
store.updateSettings({ type: 'carousel-page', value: '3d' });
}
"
/>
<label for="cp-3d">三维界面</label>
</div>
<div>
<input
type="checkbox"
id="cp-data"
name="carouselPages"
:class="[
settings.carouselPages.includes('data') ? 'checked' : '',
'carousel-page',
]"
@change="
() => {
store.updateSettings({
type: 'carousel-page',
value: 'data',
});
}
"
/>
<label for="cp-data">数据界面</label>
</div>
<div>
<input
type="checkbox"
id="cp-realtime"
name="carouselPages"
:class="[
settings.carouselPages.includes('realtime') ? 'checked' : '',
'carousel-page',
]"
@change="
() => {
store.updateSettings({
type: 'carousel-page',
value: 'realtime',
});
}
"
/>
<label for="cp-realtime">实时数据</label>
</div>
<div>
<input
type="checkbox"
id="cp-alert"
name="carouselPages"
:class="[
settings.carouselPages.includes('alert') ? 'checked' : '',
'carousel-page',
]"
@change="
() => {
store.updateSettings({
type: 'carousel-page',
value: 'alert',
});
}
"
/>
<label for="cp-alert">报警列表</label>
</div>
<div>
<input
type="checkbox"
id="cp-announcement"
name="carouselPages"
:class="[
settings.carouselPages.includes('announcement')
? 'checked'
: '',
'carousel-page',
]"
@change="
() => {
store.updateSettings({
type: 'carousel-page',
value: 'announcement',
});
}
"
/>
<label for="cp-announcement">公告页面</label>
</div>
</div>
</div>
<div class="form-item">
<label for="resolution1">分辨率</label>
<input
id="resolution1"
type="number"
min="480"
max="7680"
v-model="settings.resolution.width"
/>
<span>X</span>
<input
id="resolution2"
type="number"
min="270"
max="4320"
v-model="settings.resolution.height"
/>
<span>px</span>
</div>
<div class="form-item selector">
<!-- <div class="opt opt1">
<input type="checkbox" id="fullscreen" name="fullscreen" :class="[settings.fullscreen ? 'checked' : '']"
v-model="settings.fullscreen" />
<label for="fullscreen">全屏显示</label>
</div> -->
<div class="opt opt2">
<input
type="checkbox"
id="status"
name="status"
:class="[settings.eqStatus ? 'checked' : '', 'carousel-page']"
v-model="settings.eqStatus"
/>
<label for="status">设备状态</label>
</div>
</div>
</div>
<div class="footer">
<button @click="handleCancel" class="btn btn-cancel">取消</button>
<button @click="handleConfirm" class="btn btn-confirm">确认</button>
</div>
</div>
<div class="modal"></div>
</template>
<style scoped>
* {
user-select: none;
}
.setting-dialog {
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 577px;
height: 422px;
background: url(../assets/dialog-bg.png) 100% / contain no-repeat;
z-index: 1001;
transition: all 0.3s ease-out;
display: flex;
flex-direction: column;
gap: 12px;
padding: 24px 80px;
}
.main-content {
flex: 1;
padding-top: 10px;
}
.form-item {
margin: 20px 0;
display: flex;
gap: 8px;
color: #fff;
font-size: 24px;
letter-spacing: 2px;
padding: 0 12px;
}
.form-item.selector {
gap: 32px;
}
.opt {
display: flex;
align-items: center;
gap: 8px;
}
.opt2 {
font-size: 16px;
}
.form-item input {
flex: 1;
border: none;
appearance: none;
outline: none;
border-radius: 4px;
padding: 4px 12px;
font-size: 18px;
}
input#resolution1,
input#resolution2 {
width: 10px;
}
input[type="checkbox"] {
appearance: initial;
width: 24px;
height: 24px;
background: #fff;
flex: unset;
padding: unset;
font-size: unset;
cursor: pointer;
}
input[type="checkbox"].checked {
background: #0049ff;
position: relative;
}
input[type="checkbox"].checked::after {
content: "\2713";
color: #fff;
font-size: 22px;
line-height: 24px;
text-align: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.carousel-page__list {
display: flex;
gap: 8px;
flex-wrap: wrap;
font-size: 16px;
}
.carousel-page__list > div {
display: flex;
align-items: center;
gap: 8px;
}
input[type="checkbox"].carousel-page {
width: 18px;
height: 18px;
}
input[type="checkbox"].carousel-page.checked::after {
font-size: 14px;
line-height: 18px;
}
label {
cursor: pointer;
user-select: none;
}
.footer {
display: flex;
gap: 12px;
}
.btn {
flex: 1;
padding: 12px;
font-size: 20px;
cursor: pointer;
letter-spacing: 8px;
background: url(../assets/dialog-button.png) 0 0 / 100% 100% no-repeat;
}
button {
appearance: none;
outline: none;
background: none;
border: none;
color: #fff;
}
.modal {
/* position: fixed;
height: 1080px;
width: 1920px; */
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
background: #0003;
backdrop-filter: blur(3px);
z-index: 999;
transition: all 0.3s ease-out;
}
.setting-dialog > h1 {
text-align: center;
letter-spacing: 24px;
font-weight: 400;
text-shadow: 0 5px 1px #001124;
user-select: none;
color: #fff;
}
</style>

View File

@@ -1,47 +1,75 @@
<script setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "./Base/Container.vue";
import { useWsStore } from "../store";
import chartSetup from "./HourChartOptions";
import setupFn from "./TeamChartDayOptions";
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const showChartDom = ref(false);
// 小时数据
const hourData = ref(null);
/** 无状态,处理数据 */
function loadData(yieldArray) {
const result = [];
if (yieldArray == undefined || yieldArray?.length == 0) return null;
for (let i = 0; i < yieldArray.length; ++i) {
if (yieldArray[i].teamName == "A组") {
result[0] = parseInt(yieldArray[i].yield);
} else if (yieldArray[i].teamName == "B组") {
result[1] = parseInt(yieldArray[i].yield);
} else if (yieldArray[i].teamName == "C组") {
result[2] = parseInt(yieldArray[i].yield);
}
}
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);
// const d = loadData([
// { teamName: "A组", yield: 11 },
// { teamName: "B组", yield: 23 },
// { teamName: "C组", yield: 14 },
// ]);
if (!d) {
showChartDom.value = false;
if (chart.value) chart.value.dispose();
return;
}
showChartDom.value = true;
setupChart(chart, chartChart.value, d);
}
// 订阅
store.$subscribe((mutation, state) => {
console.log("lineHourList ===> ", state.data2.lineHourList);
hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"),
data: item.num || Math.random() * 100,
}));
chartSetup(
chart.value,
hourData.value.map((item) => item.hour),
hourData.value.map((item) => item.data)
);
__apply(state.data2.classTodayProductYield);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
chart.value = echarts.init(chartChart.value);
chart.value.setOption({});
// __apply();
});
</script>
<template>
<Container class="chart" title="本日班组情况" icon="cube">
<div
v-show="hourData && hourData.length > 0"
ref="chartChart"
class="chart-chart"
:style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!hourData || hourData.length === 0" class="empty-data-hint">
暂无数据
</p>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container>
</template>
@@ -50,10 +78,6 @@ onMounted(() => {
height: 300px;
}
.chart-inner {
background: #0f0;
}
.chart-chart {
height: 100%;
}

View File

@@ -1,52 +1,55 @@
export const options = {
color: ['#a4c9d1', '#72340b', '#ffd601' ],
grid: {
top: "10%",
bottom: "5%",
left: "3%",
right: "5%",
containLabel: true,
top: 8,
bottom: 20,
left: 42,
right: 28,
},
legend: {
show: false,
},
xAxis: {
max: 100,
splitLine: {
lineStyle: {
color: "#fff2",
},
},
axisLabel: {
fontSize: 22,
fontSize: 16,
color: "#e5e5e5a3",
},
},
yAxis: {
type: "category",
data: ["A组", "B组", "C组"],
inverse: true,
animationDuration: 300,
animationDurationUpdate: 300,
max: 2, // only the largest 3 bars will be displayed
axisLabel: {
fontSize: 22,
fontSize: 16,
color: "#e5e5e5a3",
},
splitLine: {
lineStyle: {
color: "#e5e5e5",
},
},
},
series: [
{
realtimeSort: true,
name: "X",
type: "bar",
data: [10, 20, 30],
data: [34, 2, 23],
label: {
show: true,
position: "right",
valueAnimation: true,
formatter: "{c}%",
color: "rgba(255, 255, 255, 1)",
fontWeight: "bold",
fontSize: 22,
color: "#fff",
fontSize: 16,
},
},
],
legend: {
show: false,
},
animationDuration: 0,
animationDurationUpdate: 3000,
animationEasing: "linear",
animationEasingUpdate: "linear",
};
export default function setup(echartInstance, dataArr) {

View File

@@ -1,47 +1,87 @@
<script setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "./Base/Container.vue";
import { useWsStore } from "../store";
import chartSetup from "./HourChartOptions";
import setupFn from "./TeamChartMonthOptions";
const show = ref(false);
const chartContainer = ref(null);
const chartInstance = ref(null);
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
// 小时数据
const hourData = ref(null);
store.$subscribe((mutation, state) => {
console.log("lineHourList ===> ", state.data2.lineHourList);
hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"),
data: item.num || Math.random() * 100,
}));
chartSetup(
chart.value,
hourData.value.map((item) => item.hour),
hourData.value.map((item) => item.data)
);
});
// 绿色24FF5E
// 黄色FFB524
// 红色FF3737
onMounted(() => {
chartChart.value.classList.add("h-full");
chart.value = echarts.init(chartChart.value);
chart.value.setOption({});
chartContainer.value.classList.add("h-full");
const d = loadData(store.data2.monthlyTarget);
// const d = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
} else {
if (!chartInstance.value)
chartInstance.value = echarts.init(chartContainer.value);
setupFn(chartInstance.value, d);
show.value = true;
}
});
// 订阅
store.$subscribe((mutation, state) => {
const d = loadData(state.data2.monthlyTarget);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
} else {
if (!chartInstance.value)
chartInstance.value = echarts.init(chartContainer.value);
setupFn(chartInstance.value, d);
show.value = true;
}
});
// utils
function loadData(monthlyTarget) {
if (
monthlyTarget == undefined ||
// monthlyTarget?.length == 0 ||
!monthlyTarget[0]
) {
return null;
}
return {
targetProduction: monthlyTarget[0].targetProduction,
nowProduction: monthlyTarget[0].nowProduction,
targetYield: monthlyTarget[0].targetYield,
nowYield: monthlyTarget[0].nowYield,
};
}
</script>
<template>
<Container class="chart" title="本月班组情况" icon="cube">
<div
v-show="hourData && hourData.length > 0"
ref="chartChart"
ref="chartContainer"
class="chart-chart"
:style="{ opacity: show ? 1 : 0 }"
></div>
<p v-show="!hourData || hourData.length === 0" class="empty-data-hint">
暂无数据
</p>
<p v-show="!show" class="empty-data-hint">暂无数据</p>
</Container>
</template>
@@ -50,10 +90,6 @@ onMounted(() => {
height: 300px;
}
.chart-inner {
background: #0f0;
}
.chart-chart {
height: 100%;
}

View File

@@ -1,43 +1,50 @@
export const options = {
color: ["#99D66C", "#5B9BFF", "#8167F6", "#FF00B2"],
grid: {
top: "10%",
bottom: "10%",
top: 0,
left: 0,
right: 0,
bottom: 0,
},
title: [
{
text: "当前产量:" + 100 + " 片",
text: "当前产量:" + 118 + " 片",
left: "27%",
textAlign: "center",
top: "67%",
top: "70%",
textStyle: {
fontSize: 12,
fontSize: 16,
color: "#fffa",
},
},
{
text: "目标产量:" + 100 + " 片",
text: "目标产量:" + 213 + " 片",
left: "27%",
textAlign: "center",
top: "82%",
top: "85%",
textStyle: {
fontSize: 12,
fontSize: 16,
color: "#fffa",
},
},
{
text: "当前成品率:" + 22 + "%",
left: "73%",
text: "当前成品率:" + 78 + "%",
left: "72%",
textAlign: "center",
top: "67%",
top: "70%",
textStyle: {
fontSize: 12,
fontSize: 16,
color: "#fffa",
},
},
{
text: "目标成品率:" + 22 + "%",
left: "73%",
text: "目标成品率:" + 90 + "%",
left: "72%",
textAlign: "center",
top: "82%",
top: "85%",
textStyle: {
fontSize: 12,
fontSize: 16,
color: "#fffa",
},
},
],
@@ -71,7 +78,7 @@ export const options = {
},
detail: {
valueAnimation: true,
fontSize: 12,
fontSize: 16,
offsetCenter: [0, "0%"],
formatter: "{value}%",
color: "rgba(255, 255, 255, 1)",
@@ -79,7 +86,7 @@ export const options = {
data: [
{
// value: (nowProduction / targetProduction * 100).toFixed(1),
value: 100,
value: 89.78,
},
],
},
@@ -113,16 +120,16 @@ export const options = {
detail: {
show: true,
valueAnimation: true,
fontSize: 12,
fontSize: 16,
offsetCenter: [0, "0%"],
formatter: 0 + "%",
// formatter: 0 + "%",
// formatter: (nowYield / targetYield * 100).toFixed(1) + '%',
color: "rgba(255, 255, 255, 1)",
color: "#fff",
},
data: [
{
// value: targetYield,
value: 100,
value: 78,
name: "Perfect",
title: {
show: false,
@@ -134,8 +141,7 @@ export const options = {
},
},
{
value: 100,
// value: nowYield,
value: 90,
name: "Good",
title: {
show: false,
@@ -144,6 +150,7 @@ export const options = {
show: false,
valueAnimation: true,
offsetCenter: ["0%", "10%"],
formatter: "99.23%",
},
},
{
@@ -162,8 +169,24 @@ export default function setup(echartInstance, data) {
new_options.title[1].text = "目标产量:" + data.targetProduction + " 片";
new_options.title[2].text = "当前成品率:" + data.nowYield + "%";
new_options.title[3].text = "目标成品率:" + data.targetYield + "%";
new_options.series[0].data[0].value = (data.nowProduction / data.targetProduction * 100).toFixed(1)
new_options.series[1].data[0].value = data.targetYield
new_options.series[1].data[1].value = data.nowYield
new_options.series[0].data[0].value =
data.nowProduction != null &&
data.targetProduction != null &&
data.targetProduction != 0
? ((data.nowProduction / data.targetProduction) * 100).toFixed(1)
: 0;
new_options.series[1].data[0].value = data.targetYield;
new_options.series[1].data[1].value = data.nowYield;
new_options.series[1].detail.formatter =
data.nowYield != null && data.targetYield != null && data.targetYield != 0
? ((data.nowYield / data.targetYield) * 100).toFixed(2) + "%"
: "0%";
echartInstance.setOption(new_options);
}
// {
// "targetProduction": 99,
// "nowProduction": 58,
// "targetYield": 12,
// "nowYield": 9
// }

View File

@@ -3,14 +3,68 @@ import { ref, watch, onMounted } from "vue";
import IconBack from "../assets/menu_icon/IconBack.vue";
import IconExchange from "../assets/menu_icon/IconExchange.vue";
import IconSetting from "../assets/menu_icon/IconSetting.vue";
import SettingDialogVue from "./SettingDialog.vue";
import { useSettings } from "../store/settings";
const emit = defineEmits(["change-resolution"]);
const { settings, updateSettings } = useSettings();
// store.$subscribe((_, state) => {
// settings.value.carousel = state.settings.carousel;
// })
const visible = ref(false);
function toHome() {
document.location.reload();
}
function showDialog() {
visible.value = true;
}
function toggleLunbo() {
updateSettings({ type: "carousel", value: null });
}
function handleChangeResolution(w, h) {
emit("change-resolution", w, h);
visible.value = false;
}
</script>
<template>
<div class="tools">
<button><IconBack /></button>
<button><IconSetting /></button>
<button><IconExchange /></button>
<div id="tools-bar" class="tools">
<button @click="toHome">
<IconBack />
</button>
<button @click="showDialog">
<IconSetting />
</button>
<button style="position: relative" @click="toggleLunbo">
<span
v-if="settings.carousel"
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'" />
</button>
</div>
<Teleport to="body">
<SettingDialogVue
v-if="visible"
@close="visible = false"
@change-resolution="handleChangeResolution"
/>
</Teleport>
</template>
<style scoped>
@@ -20,7 +74,7 @@ import IconSetting from "../assets/menu_icon/IconSetting.vue";
right: 24px;
display: flex;
gap: 0px;
z-index: 3;
z-index: 1000;
}
button {
@@ -51,7 +105,6 @@ button svg #switch-btn {
transition: fill 0.2s ease-out;
}
button:hover svg #back-btn,
button:hover svg #setting-btn,
button:hover svg #switch-btn {

View File

@@ -0,0 +1,99 @@
<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 from "./HourChartOptions";
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
// 小时数据
const hourData = ref([
// { hour: "0:1", data: 10 },
// { hour: "0:2", data: 13 },
// { hour: "0:3", data: 20 },
// { hour: "0:4", data: 12 },
// { hour: "0:5", data: 12 },
// { hour: "0:6", data: 11 },
// { hour: "0:7", data: 10 },
// { hour: "0:8", data: 1 },
]);
store.$subscribe((mutation, state) => {
if (
state.data2.lineHourList == undefined ||
state.data2.lineHourList?.length == 0
) {
hourData.value.splice(0);
if (chart.value) chart.value.dispose();
return;
}
hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
id: `${item.lineName}_${index}`,
hour: item.hour || `__`,
data: item.num || 0,
}));
setupChart();
});
function setupChart() {
if (chart.value) chart.value.dispose();
nextTick(() => {
chart.value = echarts.init(chartChart.value);
chartSetup(
chart.value,
hourData.value.map((item) => item.hour),
hourData.value.map((item) => item.data)
);
});
}
onMounted(() => {
chartChart.value.classList.add("h-full");
// setupChart();
});
</script>
<template>
<Container class="chart" title="小时数据" icon="cube">
<div
ref="chartChart"
class="chart-chart"
style="{opacity: (hourData && hourData.length > 0) ? 1 : 0}"
></div>
<p v-show="!hourData || hourData.length === 0" class="empty-data-hint">
暂无数据
</p>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
height: auto;
}
.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,77 @@
export const options = {
color: ["#FFD160", "#99D66C", "#5B9BFF", "#2760FF", "#8167F6", "#FF00B2"],
grid: {
top: 10,
bottom: 0,
left: 12,
right: 10,
containLabel: true,
},
xAxis: {
type: "category",
data: [],
axisLabel: {
fontSize: 16,
color: "#fff8",
},
axisLine: {
show: true,
lineStyle: {
color: "#e6e6e633",
},
},
},
yAxis: {
type: "value",
axisLine: {
show: true,
lineStyle: {
color: "#e6e6e633",
},
},
splitLine: {
lineStyle: {
color: "#e6e6e633",
},
},
axisLabel: {
fontSize: 16,
color: "#fff8",
},
minInterval: 1,
// max: 100,
// min: 1
max: function(value) {
return value.max + Math.floor(value.max / 4)
}
},
series: [
{
data: [],
// data: Array.from({ length: 7 }, () => Math.random() * 100),
type: "bar",
// barWidth: 20,
showBackground: true,
label: {
show: true,
distance: 20,
rotate: 90,
color: '#fff',
fontSize: 16,
verticalAlign: "middle",
position: 'top',
formatter: "{c}"
},
backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)",
},
},
],
};
export default function setup(echartInstance, timeArr, dataArr) {
const new_options = { ...options };
new_options.xAxis.data = timeArr;
new_options.series[0].data = dataArr;
echartInstance.setOption(new_options);
}

View File

@@ -0,0 +1,164 @@
<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: 300px; */
height: auto;
}
.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,134 @@
export const options = {
color: ['#00D3E7', '#2B9BFF', '#0D4DFF'],
legend: {
textStyle: {
color: "#fff8",
},
},
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

@@ -0,0 +1,143 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
import YieldChart from "../Chart/YieldChart.vue";
import RateChart from "../Chart/RateChart.vue";
const displayProductionChart = ref(false);
const displayRateChart = ref(false);
const websocketData = ref(null);
const refreshToken = ref(1);
const store = useWsStore();
onMounted(() => {
// websocketData.value = loadData([
// {
// targetProduction: 0,
// nowProduction: 10,
// targetYield: 10.34,
// nowYield: 3.11,
// },
// ]);
websocketData.value = loadData(store.data2.monthlyTarget);
if (!websocketData.value) {
displayProductionChart.value = false;
displayRateChart.value = false;
} else {
/** 阻止 targetProduction == 0 */
if (!websocketData.value.targetProduction) {
displayProductionChart.value = false;
} else {
displayProductionChart.value = true;
}
/** 阻止 targetYield == 0 */
if (!websocketData.value.targetYield) {
displayRateChart.value = false;
} else {
displayRateChart.value = true;
}
}
});
// 订阅
store.$subscribe((mutation, state) => {
// const d = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// },
// ]);
websocketData.value = loadData(state.data2.monthlyTarget);
if (!websocketData.value) {
displayProductionChart.value = false;
displayRateChart.value = false;
} else {
/** 阻止 targetProduction == 0 */
if (!websocketData.value.targetProduction) {
displayProductionChart.value = false;
} else {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayProductionChart.value = true;
}
/** 阻止 targetYield == 0 */
if (!websocketData.value.targetYield) {
displayRateChart.value = false;
} else {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayRateChart.value = true;
}
}
});
// utils
function loadData(monthlyTarget) {
if (monthlyTarget == undefined || !monthlyTarget[0]) {
return null;
}
return {
targetProduction: monthlyTarget[0].targetProduction,
nowProduction: monthlyTarget[0].nowProduction,
targetYield: monthlyTarget[0].targetYield,
nowYield: monthlyTarget[0].nowYield,
};
}
</script>
<template>
<Container class="chart" title="本月生产线情况" icon="cube">
<!-- <div
ref="chartContainer"
class="chart-chart"
:style="{ opacity: show ? 1 : 0 }"
></div>
<p v-show="!show" class="empty-data-hint">暂无数据</p> -->
<div class="container-body__h-full">
<yield-chart
:key="refreshToken + '_yield_chart_linemonth'"
:raw-data="websocketData"
/>
<rate-chart
:display-placeholder="!displayRateChart"
:key="refreshToken + '_rate_chart_linemonth'"
:raw-data="websocketData"
:isOnlyChild="!displayProductionChart"
/>
<!-- <p
v-if="!displayProductionChart && !displayRateChart"
style="
height: 100%;
line-height: 350px;
user-select: none;
flex: 1;
color: #fffc;
font-size: 24px;
text-align: center;
"
>
暂无数据
</p> -->
</div>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
height: auto;
}
.chart-chart {
height: 100%;
}
.container-body__h-full {
height: 100%;
display: flex;
gap: 12px;
}
</style>

View File

@@ -0,0 +1,177 @@
export const options = {
color: ["#99D66C", "#5B9BFF", "#8167F6", "#FF00B2"],
grid: {
top: 0,
left: 0,
right: 0,
bottom: 0
},
title: [
{
text: "当前产量:" + 100 + " 片",
left: "27%",
textAlign: "center",
top: "70%",
textStyle: {
fontSize: 16,
color: '#fffd'
},
},
{
text: "目标产量:" + 100 + " 片",
left: "27%",
textAlign: "center",
top: "85%",
textStyle: {
fontSize: 16,
color: '#fffd'
},
},
{
text: "当前成品率:" + 22 + "%",
left: "72%",
textAlign: "center",
top: "70%",
textStyle: {
fontSize: 16,
color: '#fffd'
},
},
{
text: "目标成品率:" + 22 + "%",
left: "72%",
textAlign: "center",
top: "85%",
textStyle: {
fontSize: 16,
color: '#fffd'
},
},
],
series: [
{
type: "gauge",
startAngle: 90,
center: ["27%", "35%"],
endAngle: -270,
radius: "55%",
progress: {
show: true,
width: 24,
},
axisLine: {
lineStyle: {
width: 24,
},
},
pointer: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
detail: {
valueAnimation: true,
fontSize: 16,
offsetCenter: [0, "0%"],
formatter: "{value}%",
color: "rgba(255, 255, 255, 1)",
},
data: [
{
// value: (nowProduction / targetProduction * 100).toFixed(1),
value: 100,
},
],
},
{
type: "gauge",
startAngle: 90,
center: ["73%", "35%"],
endAngle: -270,
radius: "55%",
progress: {
show: true,
width: 24,
},
axisLine: {
lineStyle: {
width: 24,
},
},
pointer: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
detail: {
show: true,
valueAnimation: true,
fontSize: 16,
offsetCenter: [0, "0%"],
// formatter: 0 + "%",
// formatter: (nowYield / targetYield * 100).toFixed(1) + '%',
color: "#fff",
},
data: [
{
// value: targetYield,
value: 100,
name: "Perfect",
title: {
show: false,
},
detail: {
show: false,
valueAnimation: true,
offsetCenter: ["0%", "-20%"],
},
},
{
value: 100,
// value: nowYield,
name: "Good",
title: {
show: false,
},
detail: {
show: false,
valueAnimation: true,
offsetCenter: ["0%", "10%"],
},
},
{
value: 0,
detail: {
show: true,
},
},
],
},
],
};
export default function setup(echartInstance, data) {
const new_options = { ...options };
new_options.title[0].text = "当前产量:" + data.nowProduction + " 片";
new_options.title[1].text = "目标产量:" + data.targetProduction + " 片";
new_options.title[2].text = "当前成品率:" + data.nowYield + "%";
new_options.title[3].text = "目标成品率:" + data.targetYield + "%";
new_options.series[0].data[0].value = (data.nowProduction / data.targetProduction * 100).toFixed(1)
new_options.series[1].data[0].value = data.targetYield
new_options.series[1].data[1].value = data.nowYield
new_options.series[1].detail.formatter = (data.nowYield / data.targetYield * 100).toFixed(2) + '%',
echartInstance.setOption(new_options);
}

View File

@@ -0,0 +1,102 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
import setupFn from "./LineTodayOptions";
const show = ref(false);
const chartContainer = ref(null);
const chartInstance = ref(null);
const store = useWsStore();
onMounted(() => {
chartContainer.value.classList.add("h-full");
const d = loadData(store.data2.dailyTarget);
// const d = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
} else {
if (!chartInstance.value)
chartInstance.value = echarts.init(chartContainer.value);
setupFn(chartInstance.value, d);
show.value = true;
}
});
// 订阅
store.$subscribe((mutation, state) => {
const d = loadData(state.data2.dailyTarget);
// const d = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
} else {
if (!chartInstance.value)
chartInstance.value = echarts.init(chartContainer.value);
setupFn(chartInstance.value, d);
show.value = true;
}
});
// utils
function loadData(dailyTarget) {
if (
dailyTarget == undefined ||
// dailyTarget?.length == 0 ||
!dailyTarget[0]
) {
return null;
}
return {
targetProduction: dailyTarget[0].targetProduction,
nowProduction: dailyTarget[0].nowProduction,
targetYield: dailyTarget[0].targetYield,
nowYield: dailyTarget[0].nowYield,
};
}
</script>
<template>
<Container class="chart" title="本日生产线情况" 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: 300px; */
height: auto;
}
.chart-chart {
height: 100%;
}
</style>

View File

@@ -0,0 +1,148 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
import YieldChart from "../Chart/YieldChart.vue";
import RateChart from "../Chart/RateChart.vue";
const displayProductionChart = ref(false);
const displayRateChart = ref(false);
const websocketData = ref(null);
const refreshToken = ref(1);
const store = useWsStore();
onMounted(() => {
// websocketData.value = loadData([
// {
// targetProduction: 1220,
// nowProduction: 8,
// targetYield: null,
// nowYield: null,
// },
// ]);
websocketData.value = loadData(store.data2.dailyTarget);
if (!websocketData.value) {
displayProductionChart.value = false;
displayRateChart.value = false;
} else {
/** 阻止 targetProduction == 0 */
if (!websocketData.value.targetProduction) {
displayProductionChart.value = false;
} else {
displayProductionChart.value = true;
}
/** 阻止 targetYield == 0 */
if (!websocketData.value.targetYield) {
displayRateChart.value = false;
} else {
displayRateChart.value = true;
}
}
});
// 订阅
store.$subscribe((mutation, state) => {
// const d = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// },
// ]);
websocketData.value = loadData(state.data2.dailyTarget);
if (!websocketData.value) {
displayProductionChart.value = false;
displayRateChart.value = false;
} else {
/** 阻止 targetProduction == 0 */
if (!websocketData.value.targetProduction) {
displayProductionChart.value = false;
} else {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayProductionChart.value = true;
}
/** 阻止 targetYield == 0 */
if (!websocketData.value.targetYield) {
displayRateChart.value = false;
} else {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayRateChart.value = true;
}
}
});
// utils
function loadData(dailyTarget) {
if (dailyTarget == undefined || !dailyTarget[0]) {
return null;
}
return {
// 目标产量
targetProduction: dailyTarget[0].targetProduction,
// 当前产量
nowProduction: dailyTarget[0].nowProduction,
// 目标成品率
targetYield: dailyTarget[0].targetYield,
// 当前成品率
nowYield: dailyTarget[0].nowYield,
};
}
</script>
<template>
<Container class="chart" title="本日生产线情况" icon="cube">
<!-- <div
ref="chartContainer"
class="chart-chart"
:style="{ opacity: show ? 1 : 0 }"
></div>
<p v-show="!show" class="empty-data-hint">暂无数据</p> -->
<div class="container-body__h-full">
<yield-chart
:key="refreshToken + '_yield_chart_linetoday'"
:raw-data="websocketData"
/>
<rate-chart
:display-placeholder="!displayRateChart"
:key="refreshToken + '_rate_chart_linetoday'"
:raw-data="websocketData"
:isOnlyChild="!displayProductionChart"
/>
<!-- <p
v-if="!displayProductionChart && !displayRateChart"
style="
height: 100%;
line-height: 350px;
user-select: none;
flex: 1;
color: #fffc;
font-size: 24px;
text-align: center;
"
>
暂无数据
</p> -->
</div>
</Container>
</template>
<style scoped>
.chart {
/* height: 300px; */
height: auto;
}
.chart-chart {
height: 100%;
}
.container-body__h-full {
height: 100%;
display: flex;
gap: 12px;
}
</style>

View File

@@ -0,0 +1,192 @@
export const options = {
color: ["#99D66C", "#5B9BFF", "#8167F6", "#FF00B2"],
grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
},
title: [
{
text: "当前产量:" + 118 + " 片",
left: "27%",
textAlign: "center",
top: "70%",
textStyle: {
fontSize: 16,
color: "#fffd",
},
},
{
text: "目标产量:" + 213 + " 片",
left: "27%",
textAlign: "center",
top: "85%",
textStyle: {
fontSize: 16,
color: "#fffd",
},
},
{
text: "当前成品率:" + 78 + "%",
left: "72%",
textAlign: "center",
top: "70%",
textStyle: {
fontSize: 16,
color: "#fffd",
},
},
{
text: "目标成品率:" + 90 + "%",
left: "72%",
textAlign: "center",
top: "85%",
textStyle: {
fontSize: 16,
color: "#fffd",
},
},
],
series: [
{
type: "gauge",
startAngle: 90,
center: ["27%", "35%"],
endAngle: -270,
radius: "55%",
progress: {
show: true,
width: 24,
},
axisLine: {
lineStyle: {
width: 24,
},
},
pointer: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
detail: {
valueAnimation: true,
fontSize: 16,
offsetCenter: [0, "0%"],
formatter: "{value}%",
color: "rgba(255, 255, 255, 1)",
},
data: [
{
// value: (nowProduction / targetProduction * 100).toFixed(1),
value: 89.78,
},
],
},
{
type: "gauge",
startAngle: 90,
center: ["73%", "35%"],
endAngle: -270,
radius: "55%",
progress: {
show: true,
width: 24,
},
axisLine: {
lineStyle: {
width: 24,
},
},
pointer: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
detail: {
show: true,
valueAnimation: true,
fontSize: 16,
offsetCenter: [0, "0%"],
// formatter: 0 + "%",
// formatter: (nowYield / targetYield * 100).toFixed(1) + '%',
color: "#fff",
},
data: [
{
// value: targetYield,
value: 78,
name: "Perfect",
title: {
show: false,
},
detail: {
show: false,
valueAnimation: true,
offsetCenter: ["0%", "-20%"],
},
},
{
value: 90,
name: "Good",
title: {
show: false,
},
detail: {
show: false,
valueAnimation: true,
offsetCenter: ["0%", "10%"],
formatter: "99.23%",
},
},
{
value: 0,
detail: {
show: true,
},
},
],
},
],
};
export default function setup(echartInstance, data) {
const new_options = { ...options };
new_options.title[0].text = "当前产量:" + data.nowProduction + " 片";
new_options.title[1].text = "目标产量:" + data.targetProduction + " 片";
new_options.title[2].text = "当前成品率:" + data.nowYield + "%";
new_options.title[3].text = "目标成品率:" + data.targetYield + "%";
new_options.series[0].data[0].value =
data.nowProduction != null &&
data.targetProduction != null &&
data.targetProduction != 0
? ((data.nowProduction / data.targetProduction) * 100).toFixed(1)
: 0;
new_options.series[1].data[0].value = data.targetYield;
new_options.series[1].data[1].value = data.nowYield;
new_options.series[1].detail.formatter =
data.nowYield != null && data.targetYield != null && data.targetYield != 0
? ((data.nowYield / data.targetYield) * 100).toFixed(2) + "%"
: "0%";
echartInstance.setOption(new_options);
}
// {
// "targetProduction": 99,
// "nowProduction": 58,
// "targetYield": 12,
// "nowYield": 9
// }

View File

@@ -0,0 +1,84 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
import setupFn from "./TeamChartDayOptions";
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].teamName == "A组") {
result[0] = parseInt(yieldArray[i].yield);
} else if (yieldArray[i].teamName == "B组") {
result[1] = parseInt(yieldArray[i].yield);
} else if (yieldArray[i].teamName == "C组") {
result[2] = parseInt(yieldArray[i].yield);
}
}
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);
// const d = loadData([
// { teamName: "A组", yield: 11 },
// { teamName: "B组", yield: 23 },
// { teamName: "C组", yield: 14 },
// ]);
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.data2.classTodayProductYield);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
// __apply();
});
</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,59 @@
export const options = {
color: ['#a4c9d1', '#72340b', '#ffd601' ],
grid: {
top: 8,
bottom: 20,
left: 42,
right: 28,
},
legend: {
show: false,
},
xAxis: {
max: 100,
splitLine: {
lineStyle: {
color: "#fff2",
},
},
axisLabel: {
fontSize: 16,
color: "#e5e5e5a3",
},
},
yAxis: {
type: "category",
data: ["A组", "B组", "C组"],
inverse: true,
max: 2, // only the largest 3 bars will be displayed
axisLabel: {
fontSize: 16,
color: "#e5e5e5a3",
},
splitLine: {
lineStyle: {
color: "#e5e5e5",
},
},
},
series: [
{
type: "bar",
data: [34, 2, 23],
label: {
show: true,
position: "right",
formatter: "{c}%",
color: "#fff",
fontSize: 16,
},
},
],
};
export default function setup(echartInstance, dataArr) {
const new_options = { ...options };
new_options.series[0].data = dataArr;
echartInstance.setOption(new_options);
}

View File

@@ -0,0 +1,84 @@
<script setup>
import { ref, onMounted, nextTick } from "vue";
import * as echarts from "echarts";
import Container from "../Base/Container.vue";
import { useWsStore } from "../../store";
import setupFn from "./TeamChartMonthOptions";
const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const showChartDom = ref(false);
/** 无状态,处理数据 */
function loadData(yieldArray) {
if (yieldArray == undefined || yieldArray?.length == 0) return null;
const result = [];
for (let i = 0; i < yieldArray.length; ++i) {
if (yieldArray[i].teamName == "A组") {
result[0] = parseInt(yieldArray[i].yield);
} else if (yieldArray[i].teamName == "B组") {
result[1] = parseInt(yieldArray[i].yield);
} else if (yieldArray[i].teamName == "C组") {
result[2] = parseInt(yieldArray[i].yield);
}
}
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);
// const d = loadData([
// { teamName: "A组", yield: 11 },
// { teamName: "B组", yield: 23 },
// { teamName: "C组", yield: 14 },
// ]);
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.data2.lineTeamMonthYieldList);
});
onMounted(() => {
chartChart.value.classList.add("h-full");
// __apply();
});
</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,59 @@
export const options = {
color: ['#a4c9d1', '#72340b', '#ffd601' ],
grid: {
top: 8,
bottom: 20,
left: 42,
right: 28,
},
legend: {
show: false,
},
xAxis: {
max: 100,
splitLine: {
lineStyle: {
color: "#fff2",
},
},
axisLabel: {
fontSize: 16,
color: "#e5e5e5a3",
},
},
yAxis: {
type: "category",
data: ["A组", "B组", "C组"],
inverse: true,
max: 2, // only the largest 3 bars will be displayed
axisLabel: {
fontSize: 16,
color: "#e5e5e5a3",
},
splitLine: {
lineStyle: {
color: "#e5e5e5",
},
},
},
series: [
{
type: "bar",
data: [34, 2, 23],
label: {
show: true,
position: "right",
formatter: "{c}%",
color: "#fff",
fontSize: 16,
},
},
],
};
export default function setup(echartInstance, dataArr) {
const new_options = { ...options };
new_options.series[0].data = dataArr;
echartInstance.setOption(new_options);
}

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,157 @@
<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",
rotate: 32,
margin: 12
},
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,161 @@
<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",
rotate: 32,
margin: 12
},
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 { createPinia } from 'pinia'
import { createApp } from "vue";
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';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import Vue3Marquee from 'vue3-marquee';
import App from './App.vue';
const pinia = createPinia()
setTimeout(() => {
window.location.reload();
}, 60 * 60 * 1000);
const app = createApp(App);
app.use(pinia);
app.use(ElementPlus);
app.use(Vue3Marquee, { name: 'ScrollText' });
app.mount('#app');
app.use(Vue3Marquee, { name: "ScrollText" });
app.mount("#app");

File diff suppressed because it is too large Load Diff

View File

@@ -3,18 +3,23 @@
import { ref } from "vue";
import { useWsStore } from "../store";
import Container from "../components/Base/Container.vue";
const alarmList = ref([]);
const store = useWsStore();
import ThreeD from './3D.vue'
alarmList.value = (store.data1.alarmArrList ?? []).map((item, index) => ({
const props = defineProps({
line: {
type: Number,
default: 1,
},
});
const store = useWsStore();
const alarmList = ref((store.data1.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.data1.alarmArrList.map((item, index) => ({
id: item.id,
@@ -25,43 +30,29 @@ store.$subscribe((mutation, state) => {
position: `${item.productLine} - ${item.segment}`,
}));
});
// function handleIgnore() {
// alarmList.value.splice(0)
// }
</script>
<template>
<div class="alert-list-page">
<div style="position: absolute; top: 0; left: -279px; width: calc(100% + 279px); height: 100%;">
<ThreeD :line="line ?? '1'" />
</div>
<Container class="alert-list" title="报警列表" icon="cube">
<div class="alert-list__table" style="">
<el-table
class="dark-table"
:data="alarmList"
:show-overflow-tooltip="true"
row-class-name="dark-row"
header-row-class-name="dark-header"
>
<el-table-column
prop="eqName"
label="设备名"
width="78"
></el-table-column>
<el-table-column
prop="eqIndex"
label="序号"
width="56"
></el-table-column>
<el-table-column
prop="alarmGrade"
label="报警等级"
width="90"
></el-table-column>
<el-table-column
prop="alarmDetail"
label="报警细节"
width="144"
></el-table-column>
<el-table class="dark-table" :data="alarmList" :show-overflow-tooltip="true" row-class-name="dark-row"
header-row-class-name="dark-header">
<el-table-column prop="eqName" label="设备名" width="80"></el-table-column>
<el-table-column prop="eqIndex" label="序号" width="60"></el-table-column>
<el-table-column prop="alarmGrade" label="报警等级" width="100"></el-table-column>
<el-table-column prop="alarmDetail" label="报警细节" width="144"></el-table-column>
<el-table-column prop="position" label="定位"></el-table-column>
</el-table>
</div>
<button class="alert-btn">忽略</button>
<!-- <button @click="handleIgnore" class="alert-btn">忽略</button> -->
</Container>
</div>
</template>
@@ -72,19 +63,31 @@ store.$subscribe((mutation, state) => {
/* height: 72%; */
height: 100%;
background: transparent;
user-select: none;
}
.dark-row {
color: #fff;
background: #005eff25 !important;
background: #006ACD32 !important;
user-select: none;
}
.dark-row > td.el-table__cell {
.dark-row:nth-child(odd) {
background: #020D2D32 !important;
}
.dark-row>td.el-table__cell {
border: none;
font-size: 16px;
}
.dark-row:hover > td.el-table__cell {
background-color: #005effaa !important;
.dark-row>td.el-table__cell:not(:last-child) {
border-right: 1px solid #0003;
}
.dark-row:hover>td.el-table__cell {
/* background-color: #020D2D20 !important; */
background-color: #fff1 !important;
}
.dark-header {
@@ -92,14 +95,19 @@ store.$subscribe((mutation, state) => {
color: #fff;
}
.dark-header > th.el-table__cell.is-leaf {
.dark-header>th.el-table__cell.is-leaf {
border-bottom: none;
background-color: #005eff95 !important;
background-color: #006ACD32 !important;
font-weight: 400;
letter-spacing: 1px;
}
.dark-header > th.el-table__cell {
.dark-header>th.el-table__cell {
font-size: 16px;
}
.dark-header>th.el-table__cell:not(:last-child) {
border-right: 1px solid #0003;
}
</style>
@@ -121,12 +129,18 @@ store.$subscribe((mutation, state) => {
flex-direction: column;
}
:fullscreen .alert-list {
width: 35%;
top: 32px;
height: calc(100% - 64px);
}
.alert-list__table {
height: calc(100% - 72px);
/* background: linear-gradient(to right, transparent, #0ba6ff80); */
}
.alert-list__table >>> .el-table__inner-wrapper::before {
.alert-list__table>>>.el-table__inner-wrapper::before {
background: transparent;
}

View File

@@ -2,6 +2,7 @@
<script setup>
import { ref } from "vue";
import { useWsStore } from "../store";
import IconBack from "../assets/menu_icon/IconBack.vue";
const emit = defineEmits(["home"]);
// load 公告
@@ -39,36 +40,49 @@ const handleClose = () => {
<div class="announcement-page">
<h1 class="announcement-title">公告栏</h1>
<main class="announcement-content">
<ScrollText :vertical="true" :duration="10" :pause-on-hover="true">
<ScrollText :vertical="true" :duration="120" :pause-on-hover="false">
<div v-html="vertical_content"></div>
</ScrollText>
</main>
<div class="announcement-footer">
<button
style="position: absolute; left: 0; bottom: 10px"
@click="handleClose"
>
返回
<button id="return-btn" style="
position: absolute;
right: 32px;
top: 56px;
appearance: none;
background: transparent;
border: none;
cursor: pointer;
" @click="handleClose">
<IconBack color="#f00" />
</button>
<ScrollText> {{ horizontal_content }} </ScrollText>
<ScrollText :duration="50"> {{ horizontal_content }} </ScrollText>
</div>
</div>
</template>
<style>
#return-btn:hover #back-btn__inner #back-btn {
fill: #F008 !important;
}
</style>
<style scoped>
.announcement-page {
flex: 1;
height: 100%;
width: 100%;
background: #000;
background: url(../assets/line.png) 0 0 / 100% 100% no-repeat, #000;
display: flex;
flex-direction: column;
color: #f00;
position: relative;
}
.announcement-title {
color: #fff;
font-size: 72px;
line-height: 144px;
font-weight: 500;
letter-spacing: 2px;
text-align: center;
@@ -82,16 +96,25 @@ const handleClose = () => {
font-family: serif;
overflow: hidden;
user-select: none;
margin-bottom: 32px;
}
:fullscreen .announcement-content {
margin-bottom: 5%;
padding: 56px 80px 12px 80px;
}
.announcement-footer {
height: 128px;
height: 144px;
/* background: #ccc1; */
font-size: 64px;
padding: 0 80px;
line-height: 128px;
font-family: sans-serif;
position: relative;
user-select: none;
}
:fullscreen .announcement-footer {
line-height: 80px;
}
</style>

View File

@@ -1,17 +1,28 @@
<!-- 实时数据页面 -->
<script setup>
import HourChart from "../components/HourChart.vue";
import TeamChartDay from "../components/TeamChartDay.vue";
import TeamChartMonth from "../components/TeamChartMonth.vue";
import HourChart from "../components/datapage/HourChart.vue";
// import TeamChartDay from "../components/datapage/TeamChartDay.vue";
// import TeamChartMonth from "../components/datapage/TeamChartMonth.vue";
import LatestWeekYield from '../components/datapage/LatestWeekYield.vue'
import LineToday from '../components/datapage/LineToday.vue';
import LineMonth from '../components/datapage/LineMonth.vue';
</script>
<template>
<div class="data-page">
<div class="data-list">
<HourChart />
<TeamChartDay />
<TeamChartMonth />
</div>
<!-- 小时数据 -->
<HourChart />
<!-- 近7日产量 -->
<LatestWeekYield />
<!-- 本日生产线情况 -->
<LineToday />
<!-- 本月生产线情况 -->
<LineMonth />
<!-- 本日班组情况 -->
<!-- <TeamChartDay /> -->
<!-- 本月班组情况 -->
<!-- <TeamChartMonth /> -->
</div>
</template>
@@ -19,18 +30,23 @@ import TeamChartMonth from "../components/TeamChartMonth.vue";
.data-page {
flex: 1;
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 24px;
padding: 32px 56px;
align-items: stretch;
}
.data-list {
height: 100%;
width: 480px;
position: absolute;
top: 0;
right: 32px;
display: flex;
flex-direction: column;
/*
:fullscreen .data-list {
width: 35%;
gap: 32px;
padding: 32px 0;
justify-content: space-around;
}
:fullscreen .data-list>div {
flex: 1;
} */
</style>

View File

@@ -0,0 +1,60 @@
<!-- 实时数据页面 -->
<script setup>
import HourChart from "../components/HourChart.vue";
import TeamChartDay from "../components/TeamChartDay.vue";
import TeamChartMonth from "../components/TeamChartMonth.vue";
import LatestWeekYield from '../components/LatestWeekYield.vue'
import ThreeD from './3D.vue'
const props = defineProps({
line: {
type: Number,
default: 1,
},
});
</script>
<template>
<div class="data-page">
<div style="position: absolute; top: 0; left: -279px; width: calc(100% + 279px); height: 100%;">
<ThreeD :line="line ?? '1'" />
</div>
<div class="data-list">
<HourChart />
<LatestWeekYield />
<!-- <TeamChartDay />
<TeamChartMonth /> -->
</div>
</div>
</template>
<style scoped>
.data-page {
flex: 1;
position: relative;
}
.data-list {
/* height: 100%; */
width: 480px;
position: absolute;
top: 24px;
right: 32px;
gap: 24px;
display: flex;
flex-direction: column;
}
:fullscreen .data-list {
width: 35%;
gap: 12px;
padding: 32px 0;
justify-content: space-around;
}
:fullscreen .data-list>div {
flex: 1;
}
</style>

166
src/pages/eqMaps.js Normal file
View File

@@ -0,0 +1,166 @@
export const initState = {
// 钢一线
M1上片: "",
M2上片: "",
M1磨边机: "",
M2磨边机: "",
M1清洗机: "",
M1清洗机: "",
S1丝印机: "",
S2丝印机: "",
S1前储片台: "",
S2前储片台: "",
S1清洗机: "",
S2清洗机: "",
Z1钻孔机: "",
Z2钻孔机: "",
B1前储片台: "",
B1后储片台: "",
B1下片: "",
B1清洗机: "",
//
M3清洗机: "",
M4清洗机: "",
M5清洗机: "",
M3磨边机: "",
M4磨边机: "",
M5磨边机: "",
M3上片: "",
M4上片: "",
M5上片: "",
Z3钻孔机: "",
Z4钻孔机: "",
Z5钻孔机: "",
S3清洗机: "",
S4清洗机: "",
S5清洗机: "",
S3前储片台: "",
S4前储片台: "",
S5前储片台: "",
S3丝印机: "",
S4丝印机: "",
S5丝印机: "",
S3固化机: "",
S4固化机: "",
S5固化机: "",
S3后储片台: "",
S4后储片台: "",
S5后储片台: "",
B2前储片台: "",
B2后储片台: "",
B2下片: "",
B2清洗机: "",
//
M6上片: "",
M7上片: "",
M6磨边机: "",
M7磨边机: "",
M6清洗机: "",
M7清洗机: "",
Z6钻孔机: "",
Z7钻孔机: "",
S6清洗机: "",
S7清洗机: "",
S6前储片台: "",
S7前储片台: "",
S6丝印机: "",
S7丝印机: "",
S6固化炉: "",
S7固化炉: "",
S6后储片台: "",
S7后储片台: "",
B3前储片台: "",
B3后储片台: "",
B3清洗机: "",
B3下片: "",
};
export const statusMap = [
"error", // 关机
"work", // 工作
"warn", // 未工作
"error", // 错误
];
export const eqMap = {
// 钢一线
C1LR1: "M1上片",
C1LR2: "M2上片",
C1MM1: "M1磨边机",
C1MM2: "M2磨边机",
C1ECM1: "M1清洗机",
C1ECM2: "M2清洗机",
"s1--": "S1丝印机",
"s2--": "S2丝印机",
C1ST1: "S1前储片台",
C1ST2: "S2前储片台",
C1SCM1: "S1清洗机",
C1SCM2: "S2清洗机",
C1HP1: "Z1钻孔机",
C1HP2: "Z2钻孔机",
C3ST1: "B1前储片台",
C3ST2: "B1后储片台",
C3UL1: "B1下片",
C3UL1: "B1下片1",
C3UL2: "B1下片2",
C3UCM1: "B1清洗机",
//
B1ECM1: "M3清洗机",
B1ECM2: "M4清洗机",
B1ECM3: "M5清洗机",
B1MM1: "M3磨边机",
B1MM2: "M4磨边机",
B1MM3: "M5磨边机",
B1LR1: "M3上片",
B1LR2: "M4上片",
B1LR3: "M5上片",
B1HP1: "Z3钻孔机",
B1HP2: "Z4钻孔机",
B1HP3: "Z5钻孔机",
B1SCM1: "S3清洗机",
B1SCM2: "S4清洗机",
B1SCM3: "S5清洗机",
B1ST1: "S3前储片台",
B1ST2: "S4前储片台",
B1ST3: "S5前储片台",
B1SP1: "S3丝印机",
B1SP2: "S4丝印机",
B1SP3: "S5丝印机",
B2CO1: "S3固化炉",
B2CO2: "S4固化炉",
B2CO3: "S5固化炉",
B2ST1: "S3后储片台",
B2ST2: "S4后储片台",
B2ST3: "S5后储片台",
B3ST1: "B2前储片台",
B3ST2: "B2后储片台",
B3UL1: "B2下片",
B3UL1: "B2下片1",
B3UL2: "B2下片2",
B3UCM1: "B2清洗机",
//
A1LR1: "M6上片",
A1LR2: "M7上片",
A1MM1: "M6磨边机",
A1MM2: "M7磨边机",
A1ECM1: "M6清洗机",
A1ECM2: "M7清洗机",
A1HP1: "Z6钻孔机",
A1HP2: "Z7钻孔机",
A1SCM1: "S6清洗机",
A1SCM2: "S7清洗机",
A1ST1: "S6前储片台",
A1ST2: "S7前储片台",
A1SP1: "S6丝印机",
A1SP2: "S7丝印机",
A2CO1: "S6固化炉",
A2CO2: "S7固化炉",
A2ST1: "S6后储片台",
A2ST2: "S7后储片台",
A3ST1: "B3前储片台",
A3ST2: "B3后储片台",
A3UCM1: "B3清洗机",
A3UL1: "B3下片",
A3UL1: "B3下片1",
A3UL2: "B3下片2",
};

View File

@@ -1,41 +1,34 @@
import { defineStore } from "pinia";
import { ref } from 'vue';
import { ref } from "vue";
export const useWsStore = defineStore("wsData", () => {
const data1 = ref({ test: 'hello world'});
const data1 = ref({});
const data2 = ref({});
const data3 = ref({});
const mainDataChart = ref({});
const mainDataAlarm = ref({});
function updateData(category, data) {
console.log('update', data)
switch (category) {
case "1":
data1.value = data;
data1.value = { ...data1.value, ...data };
break;
case "2":
data2.value = data;
data2.value = { ...data2.value, ...data };
break;
case "3":
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:
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
// }
// }
// })

91
src/store/settings.js Normal file
View File

@@ -0,0 +1,91 @@
import { defineStore } from "pinia";
import { ref, watch } from "vue";
export const useSettings = defineStore("settings", () => {
const initialSettings = localStorage.getItem("settings");
const settings = ref(
initialSettings
? JSON.parse(initialSettings)
: {
resolution: {
width: 1920,
height: 1080,
},
carousel: true,
carouselTime: 30, // s
carouselPages: ["3d", "data", "realtime", "alert", "announcement"],
fullscreen: false,
eqStatus: true,
}
);
const clearFullscreenFlag = (e) => {
if (!document.fullscreenElement) {
settings.value.fullscreen = false;
}
};
watch(
() => settings.value.fullscreen,
(val) => {
// const mainContainer = document.getElementById("main-container");
const mainContainer = document.documentElement;
if (val) {
mainContainer.requestFullscreen().then(() => {
document.removeEventListener("fullscreenchange", clearFullscreenFlag);
document.addEventListener("fullscreenchange", clearFullscreenFlag);
});
return;
}
if (document.fullscreenElement) document.exitFullscreen();
}
);
watch(
() => settings.value,
(val) => {
localStorage.setItem("settings", JSON.stringify(val));
},
{
immediate: true,
deep: true,
}
);
function rewriteSettings(payload) {
settings.value = payload;
}
function updateSettings({ type, value }) {
switch (type) {
case "carousel":
settings.value.carousel = !settings.value.carousel;
break;
case "fullscreen":
settings.value.fullscreen = !settings.value.fullscreen;
break;
case "eq":
settings.value.eqStatus = !settings.value.eqStatus;
break;
case "resolution":
settings.value.resolution.height = value.height;
settings.value.resolution.width = value.width;
break;
case "carousel-page":
const exists = settings.value.carouselPages.includes(value);
if (exists)
settings.value.carouselPages = settings.value.carouselPages.filter(
(page) => page !== value
);
else {
settings.value.carouselPages.splice(
["3d", "data", "realtime", "alert", "announcement"].indexOf(value),
0,
value
);
}
break;
}
}
return { settings, updateSettings, rewriteSettings };
});

View File

@@ -1,4 +1,9 @@
* {
box-sizing: border-box;
margin: 0;
font-family: '黑体', Arial, Helvetica, sans-serif;
}
html, body {
height: 100%;
}

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 / 10}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,11 +1,13 @@
import Client from "./ws";
export default function useWebsocket(store, path) {
export default function useWebsocket(store, path, excludePaths = []) {
if (excludePaths.includes(path)) return;
// connect0(store);
connectPath(store, path);
}
const url = "ws://192.168.1.101:8082/QbMonitoring/websocket";
// const url = "ws://192.168.1.101:8082/QbMonitoring/websocket";
const url = "ws://192.168.0.254:8082/QbMonitoring/websocket";
function connectPath(store, path) {
new Client(
{
@@ -15,19 +17,15 @@ function connectPath(store, path) {
(message) => {
try {
const data = JSON.parse(message.data);
console.log("message", JSON.parse(message.data));
if ("specificationChanges" in data) {
console.log("[*] setting data1");
// 分屏推送1数据
store.updateData("1", data);
} else if ("deliveryNotification" in data) {
// 分屏推送3数据
console.log("[*] setting data3");
store.updateData("3", data);
} else {
// 分屏推送2数据
console.log("[*] setting data2");
store.updateData("2", data);
}
} catch (err) {
@@ -37,7 +35,8 @@ function connectPath(store, path) {
);
}
function connect0(store) {
export function connect0(store) {
console.log("[*] Connecting main screen........");
new Client(
{
url: url + "/0",
@@ -47,6 +46,14 @@ function connect0(store) {
try {
const data = 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) {
console.log("[x] 未解析的ws数据: ", err);
}

View File

@@ -35,7 +35,6 @@ export default class XClient {
heartbeat() {
if (this.hb) clearInterval(this.hb);
return setInterval(() => {
console.log(`${this.name} ping...`);
if (this.ws.readyState == WebSocket.OPEN) {
this.ws.send("ping");
} else {

View File

@@ -4,4 +4,8 @@ import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
host: '0.0.0.0',
port: '5173'
}
})

BIN
郴州管理端.rar Normal file

Binary file not shown.