Compare commits

...

61 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
51 changed files with 5669 additions and 814 deletions

View File

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

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

119
src/FullEqList.vue Normal file
View File

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

View File

@@ -13,7 +13,7 @@ import { useSettings } from "./store/settings";
const props = defineProps(["path"]); const props = defineProps(["path"]);
const pages = ['3d', 'data', 'realtime', 'alert', 'announcement'] const pages = ["3d", "data", "realtime", "alert", "announcement"];
const currentPage = ref("3d"); const currentPage = ref("3d");
const handlePageChange = (page) => { const handlePageChange = (page) => {
currentPage.value = page; currentPage.value = page;
@@ -23,67 +23,84 @@ const mainContainer = ref(null);
const store = useSettings(); const store = useSettings();
const timer = ref(null); 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) => { store.$subscribe((mutation, state) => {
const pages = state.settings.carouselPages;
// 如果更新了时间 // 如果更新了时间
if (mutation.events.key == 'carouselTime' && state.settings.carouselTime > 0 && state.settings.carousel) { if (
if (timer.value) clearInterval(timer.value); mutation.events.key == "carouselTime" &&
timer.value = setInterval(() => { state.settings.carouselTime > 0 &&
handlePageChange(pages[(pages.indexOf(currentPage.value) + 1) % pages.length]) state.settings.carousel
}, state.settings.carouselTime * 1000); ) {
} else if (mutation.events.key == 'carousel') { startCarousel(pages, state.settings.carouselTime * 1000);
} else if (mutation.events.key == "carousel") {
// 如果更新了状态 // 如果更新了状态
if (state.settings.carousel) { if (state.settings.carousel) {
timer.value = setInterval(() => { startCarousel(pages, state.settings.carouselTime * 1000);
handlePageChange(pages[(pages.indexOf(currentPage.value) + 1) % pages.length]) } else {
}, state.settings.carouselTime * 1000); clearInterval(timer.value);
timer.value = null;
}
} else if (mutation.events.key == "carouselPages") {
if (state.settings.carousel) {
startCarousel(pages, state.settings.carouselTime * 1000);
} else { } else {
clearInterval(timer.value); clearInterval(timer.value);
timer.value = null; timer.value = null;
} }
} }
}) });
// 检查状态 // 检查状态
onMounted(() => { onMounted(() => {
const settings = store.settings; const settings = store.settings;
const pages = settings.carouselPages;
if (settings.carousel) { if (settings.carousel) {
// 开始轮播 // 开始轮播
if (timer.value) clearInterval(timer.value); if (timer.value) clearInterval(timer.value);
timer.value = setInterval(() => { timer.value = setInterval(() => {
handlePageChange(pages[(pages.indexOf(currentPage.value) + 1) % pages.length]) handlePageChange(
pages[(pages.indexOf(currentPage.value) + 1) % pages.length]
);
}, settings.carouselTime * 1000); }, settings.carouselTime * 1000);
} }
// 设置分辨率 // 设置分辨率
handleResolutionChange(settings.resolution.width, settings.resolution.height); handleResolutionChange(settings.resolution.width, settings.resolution.height);
}) });
const pathMap = { const pathMap = {
// 钢三线 // 钢三线
'/3-1': 1, "/3-1": 1,
'/3-2': 2, "/3-2": 2,
'/3-3': 11, // 3, "/3-3": 11, // 3,
'/3-4': 12, "/3-4": 12,
// 钢二线 // 钢二线
'/2-1': 5, "/2-1": 5,
'/2-2': 6, "/2-2": 6,
'/2-3': 7, "/2-3": 7,
'/2-4': 4, "/2-4": 4,
// 钢一线 // 钢一线
'/1-1': 9, "/1-1": 9,
'/1-2': 10, "/1-2": 10,
'/1-3': 3, "/1-3": 3,
'/1-4': 8 "/1-4": 8,
} };
function handleResolutionChange(width, height) { function handleResolutionChange(width, height) {
console.log('document.documentElement', document.documentElement) console.log("document.documentElement", document.documentElement);
if (mainContainer.value) { if (mainContainer.value) {
// mainContainer.value.style.width = `${width}px`; // mainContainer.value.style.width = `${width}px`;
// mainContainer.value.style.height = `${height}px`; // mainContainer.value.style.height = `${height}px`;
// changeScale(mainContainer.value, width, height) // changeScale(mainContainer.value, width, height)
changeScale(mainContainer.value, width, height) changeScale(mainContainer.value, width, height);
} }
} }
@@ -102,23 +119,37 @@ function resetScale(elm) {
elm.style.transform = "initial"; elm.style.transform = "initial";
elm.style.transformOrigin = "initial"; elm.style.transformOrigin = "initial";
} }
</script> </script>
<template> <template>
<div id="main-container" ref="mainContainer" class="main-container"> <div id="main-container" ref="mainContainer" class="main-container">
<DatetimeTool v-if="currentPage !== 'announcement'" /> <DatetimeTool v-if="currentPage !== 'announcement'" />
<Tools v-if="currentPage !== 'announcement'" @change-resolution="handleResolutionChange" /> <Tools
v-if="currentPage !== 'announcement'"
@change-resolution="handleResolutionChange"
/>
<AppHeader v-if="currentPage !== 'announcement'" /> <AppHeader v-if="currentPage !== 'announcement'" />
<AnnoucementPage v-if="currentPage === 'announcement'" class="annoucement-page" <AnnoucementPage
@home="() => handlePageChange('3d')" /> v-if="currentPage === 'announcement'"
class="annoucement-page"
@home="() => handlePageChange('3d')"
/>
<div v-else class="pages-wrapper"> <div v-else class="pages-wrapper">
<NavMenu @change="handlePageChange" :value="currentPage" /> <NavMenu @change="handlePageChange" :value="currentPage" />
<!-- <TriplePage v-if="currentPage === '3d'" :line="pathMap[path] ?? '1'" /> --> <!-- <TriplePage v-if="currentPage === '3d'" :line="pathMap[path] ?? '1'" /> -->
<ThreeDimension 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'" /> <DataPage v-if="currentPage === 'data'" :line="pathMap[path] ?? '1'" />
<AlertListPage v-if="currentPage === 'alert'" :line="pathMap[path] ?? '1'" /> <AlertListPage
<RealtimePage v-if="currentPage === 'realtime'" :line="pathMap[path] ?? '1'" /> v-if="currentPage === 'alert'"
:line="pathMap[path] ?? '1'"
/>
<RealtimePage
v-if="currentPage === 'realtime'"
:line="pathMap[path] ?? '1'"
/>
</div> </div>
</div> </div>
</template> </template>

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/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

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,13 +24,7 @@ onMounted(() => {
top: 50px; top: 50px;
right: 420px; right: 420px;
font-size: 24px; font-size: 24px;
color: #69B4FF; color: #69b4ff;
line-height: 1; line-height: 1;
} }
:fullscreen .datetime {
/* right: 620px;
top: 56px; */
color: red;
}
</style> </style>

View File

@@ -10,34 +10,25 @@ const chartChart = ref(null);
const chart = ref(null); const chart = ref(null);
// 小时数据 // 小时数据
const hourData = ref([ const hourData = ref([
// { lineName: '001', hour: '00:00', num: 10 }, // { lineName: "001", hour: "00:00", num: 10 },
// { lineName: '002', hour: '00:20', num: 20 }, // { lineName: "002", hour: "00:20", num: 20 },
// { lineName: '003', hour: '00:30', num: 30 }, // { lineName: "003", hour: "00:30", num: 30 },
// { lineName: '004', hour: '00:40', num: 14 }, // { lineName: "004", hour: "00:40", num: 14 },
// { lineName: '005', hour: '00:50', num: 50 } // { lineName: "005", hour: "00:50", num: 50 },
]); ]);
store.$subscribe((mutation, state) => { store.$subscribe((mutation, state) => {
// console.log("[HourChart] =======>", state.data2.lineHourList?.length);
if ( if (
state.data2.lineHourList == undefined || state.data2.lineHourList == undefined ||
state.data2.lineHourList?.length == 0 state.data2.lineHourList?.length == 0
) { ) {
// console.log("[HourChart] 清除数据");
hourData.value.splice(0); hourData.value.splice(0);
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
return; return;
} }
// console.log("[HourChart] ===> 有数据: ", state.data2.lineHourList); hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
hourData.value = (state.data2?.lineHourList ?? [
// { 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 },
]).map((item, index) => ({
id: `${item.lineName}_${index}`, id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"), hour: item.hour || "__",
data: item.num || Math.floor(Math.random() * 100), data: item.num || 0,
})); }));
setupChart(); setupChart();
}); });
@@ -46,7 +37,6 @@ function setupChart() {
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
nextTick(() => { nextTick(() => {
chart.value = echarts.init(chartChart.value); chart.value = echarts.init(chartChart.value);
// console.log("[HourChart] ===> 设置chart: ", chart.value, hourData.value.map((item) => item.hour), hourData.value.map((item) => item.data));
chartSetup( chartSetup(
chart.value, chart.value,
hourData.value.map((item) => item.hour), hourData.value.map((item) => item.hour),
@@ -73,12 +63,17 @@ function setupChart() {
onMounted(() => { onMounted(() => {
chartChart.value.classList.add("h-full"); chartChart.value.classList.add("h-full");
// setupChart();
}); });
</script> </script>
<template> <template>
<Container class="chart" title="小时数据" icon="cube"> <Container class="chart" title="小时数据" icon="cube">
<div ref="chartChart" class="chart-chart" style="{opacity: (hourData && hourData.length > 0) ? 1 : 0}"></div> <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 v-show="!hourData || hourData.length === 0" class="empty-data-hint">
暂无数据 暂无数据
</p> </p>
@@ -87,7 +82,7 @@ onMounted(() => {
<style scoped> <style scoped>
.chart { .chart {
height: 300px; height: 450px;
} }
.chart-chart { .chart-chart {

View File

@@ -1,4 +1,5 @@
export const options = { export const options = {
color: ["#FFD160", "#99D66C", "#5B9BFF", "#2760FF", "#8167F6", "#FF00B2"],
grid: { grid: {
top: 10, top: 10,
bottom: 0, bottom: 0,
@@ -37,9 +38,12 @@ export const options = {
fontSize: 16, fontSize: 16,
color: '#fff8' color: '#fff8'
}, },
minInterval: 1, // minInterval: 1,
max: 100, // max: 100,
min: 1 // min: 1,
max: function(value) {
return value.max + Math.floor(value.max / 5)
}
}, },
series: [ series: [
{ {
@@ -48,6 +52,16 @@ export const options = {
type: "bar", type: "bar",
// barWidth: 20, // barWidth: 20,
showBackground: true, showBackground: true,
label: {
show: true,
distance: 20,
rotate: 90,
color: '#fff',
fontSize: 16,
verticalAlign: "middle",
position: 'top',
formatter: "{c}"
},
backgroundStyle: { backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)", 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

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

View File

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

View File

@@ -8,58 +8,68 @@ import setupFn from "./TeamChartDayOptions";
const store = useWsStore(); const store = useWsStore();
const chartChart = ref(null); const chartChart = ref(null);
const chart = ref(null); const chart = ref(null);
const showChartDom = ref(false);
const dayData = ref([]); /** 无状态,处理数据 */
store.$subscribe((mutation, state) => { function loadData(yieldArray) {
console.log("[ChartDay] ===> state: ", state.data2.classTodayProductYield); const result = [];
if ( if (yieldArray == undefined || yieldArray?.length == 0) return null;
state.data2.classTodayProductYield == undefined || for (let i = 0; i < yieldArray.length; ++i) {
state.data2.classTodayProductYield?.length == 0 if (yieldArray[i].teamName == "A组") {
) { result[0] = parseInt(yieldArray[i].yield);
console.log("[ChartDay] ===> 清除状态"); } else if (yieldArray[i].teamName == "B组") {
dayData.value.splice(0); result[1] = parseInt(yieldArray[i].yield);
if (chart.value) chart.value.dispose(); } else if (yieldArray[i].teamName == "C组") {
return; result[2] = parseInt(yieldArray[i].yield);
}
for (let i = 0; i < state.data2.classTodayProductYield.length; ++i) {
if (state.data2.classTodayProductYield[i].teamName == "A组") {
dayData.value[0] = parseInt(state.data2.classTodayProductYield[i].yield);
} else if (state.data2.classTodayProductYield[i].teamName == "B组") {
dayData.value[1] = parseInt(state.data2.classTodayProductYield[i].yield);
} else if (state.data2.classTodayProductYield[i].teamName == "C组") {
dayData.value[2] = parseInt(state.data2.classTodayProductYield[i].yield);
} }
} }
console.log("[ChartDay] ===> dayData: ", dayData.value); return result;
setupChart(); }
});
// onMounted(() => { function setupChart(chart, dom, data) {
// nextTick(() => {
// setupChart();
// })
// })
function setupChart() {
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
nextTick(() => { nextTick(() => {
chart.value = echarts.init(chartChart.value); chart.value = echarts.init(dom);
setupFn(chart.value, dayData.value); 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(() => { onMounted(() => {
chartChart.value.classList.add("h-full"); chartChart.value.classList.add("h-full");
// __apply();
}); });
</script> </script>
<template> <template>
<Container class="chart" title="本日班组情况" icon="cube"> <Container class="chart" title="本日班组情况" icon="cube">
<div ref="chartChart" class="chart-chart" style="{opacity: (dayData && dayData.length > 0) ? 1 : 0}"></div> <div
<p v-show="!dayData || dayData.length === 0" class="empty-data-hint"> ref="chartChart"
暂无数据 class="chart-chart"
</p> :style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container> </Container>
</template> </template>

View File

@@ -1,11 +1,13 @@
export const options = { export const options = {
color: ['#ffd601', '#72340b'], color: ['#a4c9d1', '#72340b', '#ffd601' ],
grid: { grid: {
top: 10, top: 8,
bottom: 0, bottom: 20,
left: 0, left: 42,
right: 28, right: 28,
containLabel: true, },
legend: {
show: false,
}, },
xAxis: { xAxis: {
max: 100, max: 100,
@@ -23,8 +25,6 @@ export const options = {
type: "category", type: "category",
data: ["A组", "B组", "C组"], data: ["A组", "B组", "C组"],
inverse: true, inverse: true,
animationDuration: 300,
animationDurationUpdate: 300,
max: 2, // only the largest 3 bars will be displayed max: 2, // only the largest 3 bars will be displayed
axisLabel: { axisLabel: {
fontSize: 16, fontSize: 16,
@@ -38,27 +38,18 @@ export const options = {
}, },
series: [ series: [
{ {
realtimeSort: true,
type: "bar", type: "bar",
// data: [10, 20, 30], data: [34, 2, 23],
data: [],
label: { label: {
show: true, show: true,
position: "right", position: "right",
valueAnimation: true,
formatter: "{c}%", formatter: "{c}%",
color: "#fff", color: "#fff",
fontSize: 16, fontSize: 16,
}, },
}, },
], ],
legend: {
show: false,
},
animationDuration: 0,
animationDurationUpdate: 3000,
animationEasing: "linear",
animationEasingUpdate: "linear",
}; };
export default function setup(echartInstance, dataArr) { export default function setup(echartInstance, dataArr) {

View File

@@ -5,58 +5,83 @@ import Container from "./Base/Container.vue";
import { useWsStore } from "../store"; import { useWsStore } from "../store";
import setupFn from "./TeamChartMonthOptions"; import setupFn from "./TeamChartMonthOptions";
const show = ref(false);
const chartContainer = ref(null);
const chartInstance = ref(null);
const store = useWsStore(); const store = useWsStore();
const chartChart = ref(null);
const chart = ref(null);
const monthData = ref(null);
store.$subscribe((mutation, state) => {
console.log("[ChartMonth] ===> state: ", state.data2.monthlyTarget);
if (
state.data2.monthlyTarget == undefined ||
state.data2.monthlyTarget?.length == 0
) {
console.log("[ChartMonth] ===> 清除状态");
monthData.value = null;
if (chart.value) chart.value.dispose();
return;
}
if (!state.data2.monthlyTarget[0]) return;
const { targetProduction, nowProduction, targetYield, nowYield } =
state.data2.monthlyTarget[0];
monthData.value = { targetProduction, nowProduction, targetYield, nowYield };
setupChart();
});
// 绿色24FF5E // 绿色24FF5E
// 黄色FFB524 // 黄色FFB524
// 红色FF3737 // 红色FF3737
function setupChart() {
if (chart.value) chart.value.dispose();
nextTick(() => {
console.log("[ChartMonth] ===> 初始化表格: ", monthData.value);
chart.value = echarts.init(chartChart.value);
setupFn(chart.value, monthData.value);
});
}
onMounted(() => { onMounted(() => {
chartChart.value.classList.add("h-full"); chartContainer.value.classList.add("h-full");
// nextTick(() => { const d = loadData(store.data2.monthlyTarget);
// setupChart(); // 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> </script>
<template> <template>
<Container class="chart" title="本月班组情况" icon="cube"> <Container class="chart" title="本月班组情况" icon="cube">
<div ref="chartChart" class="chart-chart" style="{opacity: (monthData) ? 1 : 0}"></div> <div
<p v-show="!monthData" class="empty-data-hint">暂无数据</p> ref="chartContainer"
class="chart-chart"
:style="{ opacity: show ? 1 : 0 }"
></div>
<p v-show="!show" class="empty-data-hint">暂无数据</p>
</Container> </Container>
</template> </template>
@@ -65,8 +90,6 @@ onMounted(() => {
height: 300px; height: 300px;
} }
.chart-inner {}
.chart-chart { .chart-chart {
height: 100%; height: 100%;
} }

View File

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

View File

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

View File

@@ -10,34 +10,28 @@ const chartChart = ref(null);
const chart = ref(null); const chart = ref(null);
// 小时数据 // 小时数据
const hourData = ref([ const hourData = ref([
// { lineName: '001', hour: '00:00', num: 10 }, // { hour: "0:1", data: 10 },
// { lineName: '002', hour: '00:20', num: 20 }, // { hour: "0:2", data: 13 },
// { lineName: '003', hour: '00:30', num: 30 }, // { hour: "0:3", data: 20 },
// { lineName: '004', hour: '00:40', num: 14 }, // { hour: "0:4", data: 12 },
// { lineName: '005', hour: '00:50', num: 50 } // { hour: "0:5", data: 12 },
// { hour: "0:6", data: 11 },
// { hour: "0:7", data: 10 },
// { hour: "0:8", data: 1 },
]); ]);
store.$subscribe((mutation, state) => { store.$subscribe((mutation, state) => {
// console.log("[HourChart] =======>", state.data2.lineHourList?.length);
if ( if (
state.data2.lineHourList == undefined || state.data2.lineHourList == undefined ||
state.data2.lineHourList?.length == 0 state.data2.lineHourList?.length == 0
) { ) {
// console.log("[HourChart] 清除数据");
hourData.value.splice(0); hourData.value.splice(0);
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
return; return;
} }
// console.log("[HourChart] ===> 有数据: ", state.data2.lineHourList); hourData.value = (state.data2?.lineHourList ?? []).map((item, index) => ({
hourData.value = (state.data2?.lineHourList ?? [
// { 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 },
]).map((item, index) => ({
id: `${item.lineName}_${index}`, id: `${item.lineName}_${index}`,
hour: item.hour || `${index}`.padStart(2, "0"), hour: item.hour || `__`,
data: item.num || Math.floor(Math.random() * 100), data: item.num || 0,
})); }));
setupChart(); setupChart();
}); });
@@ -46,39 +40,27 @@ function setupChart() {
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
nextTick(() => { nextTick(() => {
chart.value = echarts.init(chartChart.value); chart.value = echarts.init(chartChart.value);
// console.log("[HourChart] ===> 设置chart: ", chart.value, hourData.value.map((item) => item.hour), hourData.value.map((item) => item.data));
chartSetup( chartSetup(
chart.value, chart.value,
hourData.value.map((item) => item.hour), hourData.value.map((item) => item.hour),
hourData.value.map((item) => item.data) 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(() => { onMounted(() => {
chartChart.value.classList.add("h-full"); chartChart.value.classList.add("h-full");
// setupChart();
}); });
</script> </script>
<template> <template>
<Container class="chart" title="小时数据" icon="cube"> <Container class="chart" title="小时数据" icon="cube">
<div ref="chartChart" class="chart-chart" style="{opacity: (hourData && hourData.length > 0) ? 1 : 0}"></div> <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 v-show="!hourData || hourData.length === 0" class="empty-data-hint">
暂无数据 暂无数据
</p> </p>
@@ -88,6 +70,7 @@ onMounted(() => {
<style scoped> <style scoped>
.chart { .chart {
/* height: 300px; */ /* height: 300px; */
height: auto;
} }
.chart-chart { .chart-chart {

View File

@@ -1,4 +1,5 @@
export const options = { export const options = {
color: ["#FFD160", "#99D66C", "#5B9BFF", "#2760FF", "#8167F6", "#FF00B2"],
grid: { grid: {
top: 10, top: 10,
bottom: 0, bottom: 0,
@@ -11,7 +12,7 @@ export const options = {
data: [], data: [],
axisLabel: { axisLabel: {
fontSize: 16, fontSize: 16,
color: '#fff8' color: "#fff8",
}, },
axisLine: { axisLine: {
show: true, show: true,
@@ -30,16 +31,19 @@ export const options = {
}, },
splitLine: { splitLine: {
lineStyle: { lineStyle: {
color: '#e6e6e633' color: "#e6e6e633",
} },
}, },
axisLabel: { axisLabel: {
fontSize: 16, fontSize: 16,
color: '#fff8' color: "#fff8",
}, },
minInterval: 1, minInterval: 1,
max: 100, // max: 100,
min: 1 // min: 1
max: function(value) {
return value.max + Math.floor(value.max / 4)
}
}, },
series: [ series: [
{ {
@@ -48,6 +52,16 @@ export const options = {
type: "bar", type: "bar",
// barWidth: 20, // barWidth: 20,
showBackground: true, showBackground: true,
label: {
show: true,
distance: 20,
rotate: 90,
color: '#fff',
fontSize: 16,
verticalAlign: "middle",
position: 'top',
formatter: "{c}"
},
backgroundStyle: { backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)", color: "rgba(180, 180, 180, 0.2)",
}, },

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

@@ -8,58 +8,68 @@ import setupFn from "./TeamChartDayOptions";
const store = useWsStore(); const store = useWsStore();
const chartChart = ref(null); const chartChart = ref(null);
const chart = ref(null); const chart = ref(null);
const showChartDom = ref(false);
const dayData = ref([]); /** 无状态,处理数据 */
store.$subscribe((mutation, state) => { function loadData(yieldArray) {
console.log("[ChartDay] ===> state: ", state.data2.classTodayProductYield); const result = [];
if ( if (yieldArray == undefined || yieldArray?.length == 0) return null;
state.data2.classTodayProductYield == undefined || for (let i = 0; i < yieldArray.length; ++i) {
state.data2.classTodayProductYield?.length == 0 if (yieldArray[i].teamName == "A组") {
) { result[0] = parseInt(yieldArray[i].yield);
console.log("[ChartDay] ===> 清除状态"); } else if (yieldArray[i].teamName == "B组") {
dayData.value.splice(0); result[1] = parseInt(yieldArray[i].yield);
if (chart.value) chart.value.dispose(); } else if (yieldArray[i].teamName == "C组") {
return; result[2] = parseInt(yieldArray[i].yield);
}
for (let i = 0; i < state.data2.classTodayProductYield.length; ++i) {
if (state.data2.classTodayProductYield[i].teamName == "A组") {
dayData.value[0] = parseInt(state.data2.classTodayProductYield[i].yield);
} else if (state.data2.classTodayProductYield[i].teamName == "B组") {
dayData.value[1] = parseInt(state.data2.classTodayProductYield[i].yield);
} else if (state.data2.classTodayProductYield[i].teamName == "C组") {
dayData.value[2] = parseInt(state.data2.classTodayProductYield[i].yield);
} }
} }
console.log("[ChartDay] ===> dayData: ", dayData.value); return result;
setupChart(); }
});
// onMounted(() => { function setupChart(chart, dom, data) {
// nextTick(() => {
// setupChart();
// })
// })
function setupChart() {
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
nextTick(() => { nextTick(() => {
chart.value = echarts.init(chartChart.value); chart.value = echarts.init(dom);
setupFn(chart.value, dayData.value); 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(() => { onMounted(() => {
chartChart.value.classList.add("h-full"); chartChart.value.classList.add("h-full");
// __apply();
}); });
</script> </script>
<template> <template>
<Container class="chart" title="本日班组情况" icon="cube"> <Container class="chart" title="本日班组情况" icon="cube">
<div ref="chartChart" class="chart-chart" style="{opacity: (dayData && dayData.length > 0) ? 1 : 0}"></div> <div
<p v-show="!dayData || dayData.length === 0" class="empty-data-hint"> ref="chartChart"
暂无数据 class="chart-chart"
</p> :style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container> </Container>
</template> </template>

View File

@@ -1,11 +1,13 @@
export const options = { export const options = {
color: ['#ffd601', '#72340b'], color: ['#a4c9d1', '#72340b', '#ffd601' ],
grid: { grid: {
top: 10, top: 8,
bottom: 0, bottom: 20,
left: 0, left: 42,
right: 28, right: 28,
containLabel: true, },
legend: {
show: false,
}, },
xAxis: { xAxis: {
max: 100, max: 100,
@@ -23,8 +25,6 @@ export const options = {
type: "category", type: "category",
data: ["A组", "B组", "C组"], data: ["A组", "B组", "C组"],
inverse: true, inverse: true,
animationDuration: 300,
animationDurationUpdate: 300,
max: 2, // only the largest 3 bars will be displayed max: 2, // only the largest 3 bars will be displayed
axisLabel: { axisLabel: {
fontSize: 16, fontSize: 16,
@@ -38,27 +38,18 @@ export const options = {
}, },
series: [ series: [
{ {
realtimeSort: true,
type: "bar", type: "bar",
// data: [10, 20, 30], data: [34, 2, 23],
data: [],
label: { label: {
show: true, show: true,
position: "right", position: "right",
valueAnimation: true,
formatter: "{c}%", formatter: "{c}%",
color: "#fff", color: "#fff",
fontSize: 16, fontSize: 16,
}, },
}, },
], ],
legend: {
show: false,
},
animationDuration: 0,
animationDurationUpdate: 3000,
animationEasing: "linear",
animationEasingUpdate: "linear",
}; };
export default function setup(echartInstance, dataArr) { export default function setup(echartInstance, dataArr) {

View File

@@ -8,55 +8,68 @@ import setupFn from "./TeamChartMonthOptions";
const store = useWsStore(); const store = useWsStore();
const chartChart = ref(null); const chartChart = ref(null);
const chart = ref(null); const chart = ref(null);
const showChartDom = ref(false);
const monthData = ref(null); /** 无状态,处理数据 */
store.$subscribe((mutation, state) => { function loadData(yieldArray) {
console.log("[ChartMonth] ===> state: ", state.data2.monthlyTarget); if (yieldArray == undefined || yieldArray?.length == 0) return null;
if ( const result = [];
state.data2.monthlyTarget == undefined || for (let i = 0; i < yieldArray.length; ++i) {
state.data2.monthlyTarget?.length == 0 if (yieldArray[i].teamName == "A组") {
) { result[0] = parseInt(yieldArray[i].yield);
console.log("[ChartMonth] ===> 清除状态"); } else if (yieldArray[i].teamName == "B组") {
monthData.value = null; result[1] = parseInt(yieldArray[i].yield);
if (chart.value) chart.value.dispose(); } else if (yieldArray[i].teamName == "C组") {
return; result[2] = parseInt(yieldArray[i].yield);
}
} }
return result;
}
if (!state.data2.monthlyTarget[0]) return; function setupChart(chart, dom, data) {
const { targetProduction, nowProduction, targetYield, nowYield } =
state.data2.monthlyTarget[0];
monthData.value = { targetProduction, nowProduction, targetYield, nowYield };
setupChart();
});
// 绿色24FF5E
// 黄色FFB524
// 红色FF3737
function setupChart() {
if (chart.value) chart.value.dispose(); if (chart.value) chart.value.dispose();
nextTick(() => { nextTick(() => {
console.log("[ChartMonth] ===> 初始化表格: ", monthData.value); chart.value = echarts.init(dom);
chart.value = echarts.init(chartChart.value); setupFn(chart.value, data);
setupFn(chart.value, monthData.value);
}); });
} }
/** 有状态,处理数据 */
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(() => { onMounted(() => {
chartChart.value.classList.add("h-full"); chartChart.value.classList.add("h-full");
// nextTick(() => { // __apply();
// setupChart();
// })
}); });
</script> </script>
<template> <template>
<Container class="chart" title="本月班组情况" icon="cube"> <Container class="chart" title="本月班组情况" icon="cube">
<div ref="chartChart" class="chart-chart" style="{opacity: (monthData) ? 1 : 0}"></div> <div
<p v-show="!monthData" class="empty-data-hint">暂无数据</p> ref="chartChart"
class="chart-chart"
:style="{ opacity: showChartDom ? 1 : 0 }"
></div>
<p v-show="!chart" class="empty-data-hint">暂无数据</p>
</Container> </Container>
</template> </template>
@@ -65,8 +78,6 @@ onMounted(() => {
/* height: 300px; */ /* height: 300px; */
} }
.chart-inner {}
.chart-chart { .chart-chart {
height: 100%; height: 100%;
} }

View File

@@ -1,176 +1,59 @@
export const options = { export const options = {
color: ['#a4c9d1', '#72340b', '#ffd601' ],
grid: { grid: {
top: 0, top: 8,
left: 0, bottom: 20,
right: 0, left: 42,
bottom: 0 right: 28,
},
legend: {
show: false,
}, },
title: [ xAxis: {
{ max: 100,
text: "当前产量:" + 100 + " 片", splitLine: {
left: "27%", lineStyle: {
textAlign: "center", color: "#fff2",
top: "70%",
textStyle: {
fontSize: 16,
color: '#fffa'
}, },
}, },
{ axisLabel: {
text: "目标产量:" + 100 + " 片", fontSize: 16,
left: "27%", color: "#e5e5e5a3",
textAlign: "center", },
top: "85%", },
textStyle: { yAxis: {
fontSize: 16, type: "category",
color: '#fffa' 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",
}, },
}, },
{ },
text: "当前成品率:" + 22 + "%",
left: "72%",
textAlign: "center",
top: "70%",
textStyle: {
fontSize: 16,
color: '#fffa'
},
},
{
text: "目标成品率:" + 22 + "%",
left: "72%",
textAlign: "center",
top: "85%",
textStyle: {
fontSize: 16,
color: '#fffa'
},
},
],
series: [ series: [
{ {
type: "gauge", type: "bar",
startAngle: 90, data: [34, 2, 23],
center: ["27%", "35%"], label: {
endAngle: -270,
radius: "55%",
progress: {
show: true, show: true,
width: 12, position: "right",
}, formatter: "{c}%",
axisLine: {
lineStyle: {
width: 12,
},
},
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: 12,
},
axisLine: {
lineStyle: {
width: 12,
},
},
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", color: "#fff",
fontSize: 16,
}, },
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) {
export default function setup(echartInstance, dataArr) {
const new_options = { ...options }; const new_options = { ...options };
new_options.title[0].text = "当前产量:" + data.nowProduction + " 片"; new_options.series[0].data = dataArr;
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); 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 { createApp } from "vue";
import { createPinia } from 'pinia' import { createPinia } from "pinia";
import "./style.css";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import Vue3Marquee from "vue3-marquee";
import App from "./App.vue";
const pinia = createPinia();
import './style.css'; setTimeout(() => {
import ElementPlus from 'element-plus'; window.location.reload();
import 'element-plus/dist/index.css'; }, 60 * 60 * 1000);
import Vue3Marquee from 'vue3-marquee';
import App from './App.vue';
const pinia = createPinia()
const app = createApp(App); const app = createApp(App);
app.use(pinia); app.use(pinia);
app.use(ElementPlus); app.use(ElementPlus);
app.use(Vue3Marquee, { name: 'ScrollText' }); app.use(Vue3Marquee, { name: "ScrollText" });
app.mount('#app'); app.mount("#app");

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,28 @@
<!-- 实时数据页面 --> <!-- 实时数据页面 -->
<script setup> <script setup>
import HourChart from "../components/datapage/HourChart.vue"; import HourChart from "../components/datapage/HourChart.vue";
import TeamChartDay from "../components/datapage/TeamChartDay.vue"; // import TeamChartDay from "../components/datapage/TeamChartDay.vue";
import TeamChartMonth from "../components/datapage/TeamChartMonth.vue"; // import TeamChartMonth from "../components/datapage/TeamChartMonth.vue";
import LatestWeekYield from '../components/datapage/LatestWeekYield.vue'
import LineToday from '../components/datapage/LineToday.vue';
import LineMonth from '../components/datapage/LineMonth.vue';
</script> </script>
<template> <template>
<div class="data-page"> <div class="data-page">
<!-- 小时数据 -->
<HourChart /> <HourChart />
<TeamChartDay /> <!-- 近7日产量 -->
<TeamChartMonth /> <LatestWeekYield />
<HourChart /> <!-- 本日生产线情况 -->
<TeamChartDay /> <LineToday />
<TeamChartMonth /> <!-- 本月生产线情况 -->
<LineMonth />
<!-- 本日班组情况 -->
<!-- <TeamChartDay /> -->
<!-- 本月班组情况 -->
<!-- <TeamChartMonth /> -->
</div> </div>
</template> </template>
@@ -22,12 +30,11 @@ import TeamChartMonth from "../components/datapage/TeamChartMonth.vue";
.data-page { .data-page {
flex: 1; flex: 1;
position: relative; position: relative;
background: #cccc;
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr; grid-template-rows: 1fr 1fr;
gap: 24px; gap: 24px;
padding: 32px; padding: 32px 56px;
align-items: stretch; align-items: stretch;
} }

View File

@@ -3,6 +3,7 @@
import HourChart from "../components/HourChart.vue"; import HourChart from "../components/HourChart.vue";
import TeamChartDay from "../components/TeamChartDay.vue"; import TeamChartDay from "../components/TeamChartDay.vue";
import TeamChartMonth from "../components/TeamChartMonth.vue"; import TeamChartMonth from "../components/TeamChartMonth.vue";
import LatestWeekYield from '../components/LatestWeekYield.vue'
import ThreeD from './3D.vue' import ThreeD from './3D.vue'
const props = defineProps({ const props = defineProps({
@@ -21,8 +22,9 @@ const props = defineProps({
</div> </div>
<div class="data-list"> <div class="data-list">
<HourChart /> <HourChart />
<TeamChartDay /> <LatestWeekYield />
<TeamChartMonth /> <!-- <TeamChartDay />
<TeamChartMonth /> -->
</div> </div>
</div> </div>
</template> </template>
@@ -34,21 +36,20 @@ const props = defineProps({
} }
.data-list { .data-list {
height: 100%; /* height: 100%; */
width: 480px; width: 480px;
position: absolute; position: absolute;
top: 0; top: 24px;
right: 32px; right: 32px;
gap: 24px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-around;
} }
:fullscreen .data-list { :fullscreen .data-list {
width: 35%; width: 35%;
gap: 32px; gap: 12px;
padding: 32px 0; padding: 32px 0;
justify-content: space-around; justify-content: space-around;
} }

View File

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

View File

@@ -11,8 +11,9 @@ export const useSettings = defineStore("settings", () => {
width: 1920, width: 1920,
height: 1080, height: 1080,
}, },
carousel: false, carousel: true,
carouselTime: 10, // s carouselTime: 30, // s
carouselPages: ["3d", "data", "realtime", "alert", "announcement"],
fullscreen: false, fullscreen: false,
eqStatus: true, eqStatus: true,
} }
@@ -70,6 +71,20 @@ export const useSettings = defineStore("settings", () => {
settings.value.resolution.height = value.height; settings.value.resolution.height = value.height;
settings.value.resolution.width = value.width; settings.value.resolution.width = value.width;
break; 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 }; return { settings, updateSettings, rewriteSettings };

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

BIN
郴州管理端.rar Normal file

Binary file not shown.