@@ -0,0 +1,2 @@ | |||
# Place your StackBlitz environment variables here, | |||
# and they will be securely synced to your account. |
@@ -0,0 +1 @@ | |||
node_modules |
@@ -0,0 +1,3 @@ | |||
{ | |||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] | |||
} |
@@ -0,0 +1,7 @@ | |||
# Vue 3 + Vite | |||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. | |||
## Recommended IDE Setup | |||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). |
@@ -0,0 +1,24 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
pnpm-debug.log* | |||
lerna-debug.log* | |||
node_modules | |||
dist | |||
dist-ssr | |||
*.local | |||
# Editor directories and files | |||
.vscode/* | |||
!.vscode/extensions.json | |||
.idea | |||
.DS_Store | |||
*.suo | |||
*.ntvs* | |||
*.njsproj | |||
*.sln | |||
*.sw? |
@@ -0,0 +1,13 @@ | |||
<!doctype html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title>Vite + Vue</title> | |||
</head> | |||
<body> | |||
<div id="app"></div> | |||
<script type="module" src="/src/main.js"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,22 @@ | |||
{ | |||
"name": "vite-vue-starter", | |||
"private": true, | |||
"version": "0.0.0", | |||
"type": "module", | |||
"scripts": { | |||
"dev": "vite", | |||
"build": "vite build", | |||
"preview": "vite preview" | |||
}, | |||
"dependencies": { | |||
"echarts": "^5.4.3", | |||
"element-plus": "^2.4.4", | |||
"pinia": "^2.1.7", | |||
"vue": "^3.3.11", | |||
"vue3-marquee": "^4.1.0" | |||
}, | |||
"devDependencies": { | |||
"@vitejs/plugin-vue": "^5.0.2", | |||
"vite": "^5.0.8" | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> |
@@ -0,0 +1,71 @@ | |||
<script setup> | |||
import { ref, provide } from "vue"; | |||
import MainPage from "./MainPage.vue"; | |||
import Slider from "./components/Slider.vue"; | |||
import Client from "./utils/ws"; | |||
import { useWsStore } from "./store"; | |||
const store = useWsStore(); | |||
const url = "ws://192.168.1.101:8082/QbMonitoring/websocket"; | |||
let urlPath = document.location.pathname; | |||
if (urlPath === "/") { | |||
urlPath = "/1-1"; | |||
} | |||
new Client( | |||
{ | |||
url: url + urlPath, | |||
name: urlPath, | |||
}, | |||
(message) => { | |||
try { | |||
const data = JSON.parse(message.data); | |||
console.log("message", JSON.parse(message.data)); | |||
if ("specificationChanges" in data) { | |||
console.log('[*] setting data1') | |||
// 分屏推送1数据 | |||
store.updateData('1', data); | |||
} else if ("deliveryNotification" in data) { | |||
// 分屏推送3数据 | |||
console.log('[*] setting data3') | |||
store.updateData('3', data); | |||
} else { | |||
// 分屏推送2数据 | |||
console.log('[*] setting data2') | |||
store.updateData('2', data); | |||
} | |||
} catch(err) { | |||
console.log('[x] 未解析的ws数据: ', err); | |||
} | |||
} | |||
); | |||
const styles = ref({}); | |||
function setSize(value) { | |||
const v = (value / 100).toFixed(2); | |||
console.log("size change", value); | |||
styles.value = { | |||
transform: `scale(${v})`, | |||
// transform: `scale(${v * 24 / 33}, ${v})`, | |||
transformOrigin: "top left", | |||
}; | |||
} | |||
</script> | |||
<template> | |||
<div id="app-container"> | |||
<MainPage :style="styles" /> | |||
<Slider @size-change="setSize" /> | |||
</div> | |||
</template> | |||
<style scoped> | |||
#app-container { | |||
width: 100vw; | |||
height: 100vh; | |||
background: #fff; | |||
} | |||
</style> |
@@ -0,0 +1,57 @@ | |||
<script setup> | |||
import { ref } from 'vue'; | |||
import Tools from './components/Tools.vue'; | |||
import NavMenu from './components/NavMenu.vue'; | |||
import AnnoucementPage from './pages/AnnouncementPage.vue'; | |||
import RealtimePage from './pages/RealtimePage.vue'; | |||
import AppHeader from './components/Base/Header.vue'; | |||
import AlertListPage from './pages/AlertListPage.vue' | |||
import DataPage from './pages/DataPage.vue'; | |||
const currentPage = ref('3d'); | |||
const handlePageChange = (page) => { | |||
currentPage.value = page; | |||
console.log(currentPage.value); | |||
}; | |||
</script> | |||
<template> | |||
<div class="main-container"> | |||
<Tools v-if="currentPage !== 'announcement'" /> | |||
<AppHeader v-if="currentPage !== 'announcement'" /> | |||
<AnnoucementPage | |||
v-if="currentPage === 'announcement'" | |||
class="annoucement-page" | |||
@home="() => handlePageChange('3d')" | |||
/> | |||
<div v-else class="pages-wrapper"> | |||
<NavMenu @change="handlePageChange" /> | |||
<div v-if="currentPage === '3d'" class="3d-page">3d</div> | |||
<DataPage v-if="currentPage === 'data'" /> | |||
<AlertListPage v-if="currentPage === 'alert'" /> | |||
<RealtimePage v-if="currentPage === 'realtime'" /> | |||
</div> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.main-container { | |||
width: 1920px; | |||
height: 1080px; | |||
background: #000; | |||
position: relative; | |||
display: flex; | |||
flex-direction: column; | |||
/** | |||
background: url('https://img1.baidu.com/it/u=2052683582,2603151390&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800') | |||
100% / 100% no-repeat; | |||
*/ | |||
background: url(./assets/bg.png) 100% / cover no-repeat; | |||
} | |||
.pages-wrapper { | |||
flex: 1; | |||
display: flex; | |||
} | |||
</style> |
@@ -0,0 +1,24 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<svg width="26px" height="26px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||
<title>任务</title> | |||
<defs> | |||
<linearGradient x1="91.384997%" y1="100%" x2="25.4330364%" y2="7.84095011e-14%" id="linearGradient-1"> | |||
<stop stop-color="#4BFFC8" offset="0%"></stop> | |||
<stop stop-color="#45F2EC" offset="100%"></stop> | |||
</linearGradient> | |||
<linearGradient x1="91.384997%" y1="100%" x2="25.4330364%" y2="7.84095011e-14%" id="linearGradient-2"> | |||
<stop stop-color="#FFFFFF" offset="0%"></stop> | |||
<stop stop-color="#FFFFFF" offset="100%"></stop> | |||
</linearGradient> | |||
</defs> | |||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||
<g id="环动科技热处理车间立库看板" transform="translate(-989.000000, -621.000000)" fill-rule="nonzero"> | |||
<g id="编组-36" transform="translate(982.000000, 621.000000)"> | |||
<g id="任务" transform="translate(7.000000, 0.000000)"> | |||
<rect id="矩形" fill="#FFFFFF" opacity="0" x="0" y="0" width="32" height="32"></rect> | |||
<path d="M24.8336138,7.21888656 C25.307557,7.21888656 25.7025096,7.62700427 25.6893445,8.08778234 L25.6893445,25.768495 C25.6893445,26.5452352 25.2417316,26.6505559 24.859944,26.6505559 L7.17923134,26.6505559 C6.6921231,26.6505559 6.31033556,26.2556033 6.31033556,25.7816601 L6.31033556,8.10094743 C6.31033556,7.61383919 6.70528819,7.23205165 7.17923134,7.23205165 L10.3915127,7.23205165 L10.3915127,9.00933847 C10.3915127,9.41745619 10.733805,9.75974847 11.1419227,9.75974847 L20.8709225,9.75974847 C21.2790402,9.75974847 21.6213325,9.43062128 21.6213325,9.00933847 L21.6213325,7.21888656 Z M21.3580307,13.7224398 C21.0289035,13.3933127 20.5154651,13.3933127 20.1863379,13.7224398 L14.5385153,19.3702624 L11.7738469,16.6319242 C11.4447197,16.302797 10.9312813,16.302797 10.6021541,16.6319242 C10.2730269,16.9610514 10.2730269,17.4744898 10.6021541,17.803617 L13.9460864,21.1475493 C14.2752136,21.4766764 14.788652,21.4766764 15.1177792,21.1475493 L21.3711958,14.9072977 C21.5423419,14.7361516 21.6213325,14.5386753 21.6213325,14.3148688 C21.6213325,14.0910623 21.5291768,13.893586 21.3580307,13.7224398 Z M19.896706,5.34944412 C20.1863379,5.34944412 20.4364746,5.59958078 20.4364746,5.88921271 L20.4364746,8.10094743 C20.4364746,8.39057936 20.1863379,8.64071602 19.896706,8.64071602 L12.1161392,8.64071602 L12.1161392,8.6670462 C11.8265073,8.6670462 11.5763706,8.41690953 11.5763706,8.1272776 L11.5763706,5.88921271 C11.5763706,5.59958078 11.8265073,5.34944412 12.1161392,5.34944412 L19.896706,5.34944412 Z" id="形状结合" fill="url(#linearGradient-2)"></path> | |||
</g> | |||
</g> | |||
</g> | |||
</g> | |||
</svg> |
@@ -0,0 +1,22 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<svg width="26px" height="26px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||
<title>更多 2</title> | |||
<defs> | |||
<linearGradient x1="100%" y1="100%" x2="20.318998%" y2="7.84095011e-14%" id="linearGradient-1"> | |||
<stop stop-color="#4BFFC8" offset="0%"></stop> | |||
<stop stop-color="#45F2EC" offset="100%"></stop> | |||
</linearGradient> | |||
<linearGradient x1="100%" y1="100%" x2="20.318998%" y2="7.84095011e-14%" id="linearGradient-2"> | |||
<stop stop-color="#FFFFFF" offset="0%"></stop> | |||
<stop stop-color="#FFFFFF" offset="100%"></stop> | |||
</linearGradient> | |||
</defs> | |||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||
<g id="环动科技热处理车间立库看板" transform="translate(-670.000000, -119.000000)" fill-rule="nonzero"> | |||
<g id="更多-2" transform="translate(670.000000, 119.000000)"> | |||
<rect id="矩形" fill="#FFFFFF" opacity="0" x="0" y="0" width="32" height="32"></rect> | |||
<path d="M18.6666667,12 L18.6666667,5.33333333 L7.33333333,5.33333333 C6.22876383,5.33333333 5.33333333,6.22876383 5.33333333,7.33333333 L5.33333333,18.6666667 L5.33333333,18.6666667 L12,18.6666667 L12,12 L18.6666667,12 Z M20,13.3333333 L20,20 L13.3333333,20 L13.3333333,26.6666667 L24.6666667,26.6666667 C25.7712362,26.6666667 26.6666667,25.7712362 26.6666667,24.6666667 L26.6666667,13.3333333 L26.6666667,13.3333333 L20,13.3333333 Z" id="形状" fill="url(#linearGradient-2)"></path> | |||
</g> | |||
</g> | |||
</g> | |||
</svg> |
@@ -0,0 +1,12 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<svg width="26px" height="26px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||
<title>多种 2</title> | |||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||
<g id="环动科技热处理车间立库看板" transform="translate(-39.000000, -620.000000)" fill="#FFFFFF" fill-rule="nonzero"> | |||
<g id="多种-2" transform="translate(39.000000, 620.000000)"> | |||
<rect id="矩形" opacity="0" x="0" y="0" width="32" height="32"></rect> | |||
<path d="M26,23 L18.2,27.8 C17.6,28.2 17,27.8 17,27 L17,15.8 C17,15.6 17.2,15.2 17.4,15 L25.2,10.2 C25.8,9.8 26.4,10.2 26.4,11 L26.4,22.4 C26.4,22.6 26.2,23 26,23 Z M6,23 L13.8,27.8 C14.4,28.2 15,27.8 15,27 L15,15.8 C15,15.6 14.8,15.2 14.6,15 L6.8,10.2 C6.4,10 5.6,10.4 5.6,11 L5.6,22.4 C5.6,22.6 5.8,23 6,23 Z M15.8,4 L6.8,7.4 C6.4,7.6 6.4,8.2 6.8,8.4 L15.8,14 C16,14 16.2,14 16.2,14 L25.4,8.4 C25.8,8.2 25.8,7.6 25.4,7.4 L16.2,4 C16,4 16,4 15.8,4 Z" id="形状"></path> | |||
</g> | |||
</g> | |||
</g> | |||
</svg> |
@@ -0,0 +1,40 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<svg width="26px" height="26px" viewBox="0 0 24.4721163 23.7661066" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||
<title>多种</title> | |||
<defs> | |||
<linearGradient x1="100%" y1="89.6187762%" x2="20.318998%" y2="10.3812238%" id="linearGradient-1"> | |||
<stop stop-color="#4BFFC8" offset="0%"></stop> | |||
<stop stop-color="#45F2EC" offset="100%"></stop> | |||
</linearGradient> | |||
<linearGradient x1="100%" y1="89.6187762%" x2="20.318998%" y2="10.3812238%" id="linearGradient-2"> | |||
<stop stop-color="#FFFFFF" offset="0%"></stop> | |||
<stop stop-color="#FFFFFF" offset="100%"></stop> | |||
</linearGradient> | |||
<linearGradient x1="100%" y1="54.7269958%" x2="20.318998%" y2="45.2730042%" id="linearGradient-3"> | |||
<stop stop-color="#4BFFC8" offset="0%"></stop> | |||
<stop stop-color="#45F2EC" offset="100%"></stop> | |||
</linearGradient> | |||
<linearGradient x1="100%" y1="54.7269958%" x2="20.318998%" y2="45.2730042%" id="linearGradient-4"> | |||
<stop stop-color="#FFFFFF" offset="0%"></stop> | |||
<stop stop-color="#FFFFFF" offset="100%"></stop> | |||
</linearGradient> | |||
</defs> | |||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||
<g id="环动科技热处理车间立库看板" transform="translate(-43.763942, -124.000000)"> | |||
<g id="多种" transform="translate(40.000000, 120.000000)"> | |||
<rect id="矩形" x="0" y="0" width="32" height="32"></rect> | |||
<g id="异常" transform="translate(3.763942, 4.000000)" fill-rule="nonzero"> | |||
<rect id="矩形" fill="#FFFFFF" opacity="0" x="0.236058137" y="0" width="22.6666667" height="22.6666667"></rect> | |||
<g transform="translate(0.000000, 1.333333)"> | |||
<rect id="矩形" fill="#FFFFFF" opacity="0" x="3.05901453" y="5.09835756" width="17.3344157" height="17.3344157"></rect> | |||
<g id="数量_面性"> | |||
<rect id="矩形" fill="#FFFFFF" opacity="0" x="0" y="0" width="21.7851425" height="21.7851425"></rect> | |||
<path d="M12.237403,12.1431998 C12.1208704,12.1431998 12.0057183,12.1179773 11.8998678,12.0692379 L0.469391828,6.80449511 C0.183288453,6.67275726 2.8725996e-07,6.38657609 2.8725996e-07,6.07159989 C2.8725996e-07,5.7566237 0.183288453,5.47044252 0.469391828,5.33870467 L11.8998678,0.073961899 C12.1133182,-0.0238729288 12.3587984,-0.0238729288 12.5722488,0.073961899 L24.0027247,5.33870467 C24.2888281,5.47044252 24.4721163,5.7566237 24.4721163,6.07159989 C24.4721163,6.38657609 24.2888281,6.67275726 24.0027247,6.80449511 L12.5722488,12.0692379 C12.4672224,12.1175995 12.3530285,12.142823 12.237403,12.1431998 L12.237403,12.1431998 Z M23.2644505,15.0102321 L12.237403,20.0893977 L1.21035562,15.0102321 L1.21035562,16.7866626 L11.8998678,21.7111806 C12.1133182,21.8090154 12.3587984,21.8090154 12.5722488,21.7111806 L23.2644505,16.7866626 L23.2644505,15.0102321 Z" id="形状" fill="url(#linearGradient-2)"></path> | |||
<path d="M23.2644505,10.1838817 L12.237403,15.2643921 L1.21035562,10.1838817 L1.21035562,11.9683807 L11.8998678,16.891554 C12.1133182,16.9893888 12.3587984,16.9893888 12.5722488,16.891554 L23.2644505,11.9683807 L23.2644505,10.1838817 Z" id="路径" fill="url(#linearGradient-4)"></path> | |||
</g> | |||
</g> | |||
</g> | |||
</g> | |||
</g> | |||
</g> | |||
</svg> |
@@ -0,0 +1,114 @@ | |||
<script setup> | |||
import Icon from './Icon.vue'; | |||
import Corner from './Corner.vue'; | |||
const props = defineProps({ | |||
title: { | |||
type: String, | |||
default: 'Default title', | |||
}, | |||
icon: { | |||
type: String, | |||
default: 'icon', | |||
}, | |||
corner: { | |||
type: Array, | |||
default: ['tl', 'tr', 'bl', 'br'], //'trbl', // top right bottom left | |||
}, | |||
cubeCorner: { | |||
type: String, | |||
default: 'bl', | |||
validator: (value, props) => { | |||
return ['bl', 'br'].includes(value); | |||
}, | |||
}, | |||
}); | |||
</script> | |||
<template> | |||
<div class="container-border"> | |||
<Corner | |||
v-if="corner.indexOf('tl') !== -1" | |||
position="top-left" | |||
key="top-left" | |||
/> | |||
<Corner | |||
v-if="corner.indexOf('tr') !== -1" | |||
position="top-right" | |||
key="top-right" | |||
/> | |||
<Corner | |||
v-if="corner.indexOf('bl') !== -1" | |||
position="bottom-left" | |||
key="bottom-left" | |||
/> | |||
<Corner | |||
v-if="corner.indexOf('br') !== -1" | |||
position="bottom-right" | |||
key="bottom-right" | |||
/> | |||
<div class="container"> | |||
<div class="cube" :class="[`cube-${cubeCorner}`]"></div> | |||
<div class="container-header"> | |||
<Icon :name="icon" /> | |||
<span>{{ title }}</span> | |||
</div> | |||
<div class="container-body"> | |||
<slot /> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.container-border { | |||
position: relative; | |||
display: flex; | |||
} | |||
.cube { | |||
width: 26px; | |||
height: 26px; | |||
background: #1f8fff; | |||
position: absolute; | |||
z-index: 1; | |||
} | |||
.cube-bl { | |||
transform: rotate(45deg); | |||
bottom: -13px; | |||
left: -13px; | |||
} | |||
.cube-br { | |||
transform: rotate(45deg); | |||
bottom: -13px; | |||
right: -13px; | |||
} | |||
.container { | |||
flex: 1; | |||
display: flex; | |||
flex-direction: column; | |||
position: relative; | |||
overflow: hidden; | |||
color: #fff; | |||
} | |||
.container-header { | |||
background: #006acd66; | |||
backdrop-filter: blur(2px); | |||
color: #fff; | |||
display: flex; | |||
align-items: center; | |||
gap: 12px; | |||
padding: 8px 12px; | |||
} | |||
.container-body { | |||
background: #ffffff33; | |||
backdrop-filter: blur(1px); | |||
flex: 1; | |||
padding: 20px; | |||
} | |||
</style> |
@@ -0,0 +1,62 @@ | |||
<script setup> | |||
import { ref, computed } from 'vue'; | |||
const props = defineProps({ | |||
position: { | |||
type: String, | |||
default: 'bottom-left', | |||
}, | |||
}); | |||
const styles = computed(() => { | |||
switch (props.position) { | |||
case 'top-left': | |||
return { | |||
transform: 'rotate(90deg)', | |||
top: 0, | |||
left: 0, | |||
bottom: 'unset', | |||
right: 'unset', | |||
}; | |||
case 'top-right': | |||
return { | |||
transform: 'rotate(180deg)', | |||
top: 0, | |||
left: 'unset', | |||
bottom: 'unset', | |||
right: 0, | |||
}; | |||
case 'bottom-left': | |||
return { | |||
transform: 'rotate(0deg)', | |||
bottom: 0, | |||
left: 0, | |||
right: 'unset', | |||
top: 'unset', | |||
}; | |||
case 'bottom-right': | |||
return { | |||
transform: 'rotate(-90deg)', | |||
top: 'unset', | |||
left: 'unset', | |||
bottom: 0, | |||
right: 0, | |||
}; | |||
} | |||
}); | |||
</script> | |||
<template> | |||
<div class="corner" :style="styles"></div> | |||
</template> | |||
<style scoped> | |||
.corner { | |||
position: absolute; | |||
width: 16px; | |||
height: 16px; | |||
border-left: 2px solid #0078e4; | |||
border-bottom: 2px solid #0078e4; | |||
z-index: 2; | |||
} | |||
</style> |
@@ -0,0 +1,37 @@ | |||
<script setup> | |||
import { ref, computed } from 'vue'; | |||
</script> | |||
<template> | |||
<header class="header"> | |||
<div> | |||
<Icon htmlRole="logo" icon="logo" /> | |||
<h1 class="header__title">郴州旗滨光伏光电玻璃有限公司</h1> | |||
</div> | |||
</header> | |||
</template> | |||
<style scoped> | |||
.header { | |||
position: relative; | |||
height: 90px; | |||
background: url(../../assets/header-bg@2x.png) 100% / 100% no-repeat; | |||
} | |||
.header > div { | |||
position: absolute; | |||
left: 0; | |||
right: 0; | |||
margin: 0 auto; | |||
/** width: 520px; **/ | |||
width: 480px; | |||
top: 20px; | |||
} | |||
.header__title { | |||
font-weight: 400; | |||
letter-spacing: 2px; | |||
color: #fff; | |||
user-select: none; | |||
} | |||
</style> |
@@ -0,0 +1,51 @@ | |||
<script setup> | |||
// import icons | |||
import checkIcon from '../../assets/svg/check.svg'; | |||
import cubeIcon from '../../assets/svg/cube.svg'; | |||
import cube2Icon from '../../assets/svg/cube-twice.svg'; | |||
import stackIcon from '../../assets/svg/stack.svg'; | |||
import { ref, computed } from 'vue'; | |||
const props = defineProps({ | |||
name: { | |||
type: String, | |||
default: 'icon', | |||
}, | |||
htmlRole: { | |||
type: String, | |||
default: 'icon', // 'icon' | 'logo' | |||
}, | |||
}); | |||
const icon = computed(() => { | |||
switch (props.name) { | |||
case 'icon': | |||
case 'cube': | |||
return cubeIcon; | |||
case 'cube-2': | |||
return cube2Icon; | |||
case 'stack': | |||
return stackIcon; | |||
case 'check': | |||
return checkIcon; | |||
} | |||
}); | |||
</script> | |||
<template> | |||
<div class="icon"> | |||
<img :src="icon" :class="icon" /> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.icon { | |||
height: 16px; | |||
width: 16px; | |||
background: #fff3; | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
</style> |
@@ -0,0 +1,54 @@ | |||
<script setup> | |||
import './style.css'; | |||
import { Close } from '@element-plus/icons-vue'; | |||
import { ref, onMounted, onBeforeUnmount } from 'vue'; | |||
const contentVisible = ref(false); | |||
const emit = defineEmits(['close']); | |||
onMounted(() => { | |||
setTimeout(() => { | |||
contentVisible.value = true; | |||
}, 100); | |||
}); | |||
onBeforeUnmount(() => {}); | |||
function close() { | |||
contentVisible.value = false; | |||
setTimeout(() => { | |||
emit('close'); | |||
}, 150); | |||
} | |||
</script> | |||
<template> | |||
<div class="modal"> | |||
<div class="dribbble-dialog pop-out" :class="{ visible: contentVisible }"> | |||
<div class="logo"></div> | |||
<div class="left"></div> | |||
<div class="right"></div> | |||
<div class="content-text"> | |||
<p>Advance your career with a</p> | |||
<p>Professional Diploma in UX Design</p> | |||
<button>Learn More</button> | |||
</div> | |||
<el-button | |||
:icon="Close" | |||
size="small" | |||
circle | |||
style=" | |||
background: #eeeb; | |||
color: #000; | |||
border: none; | |||
position: absolute; | |||
top: -3px; | |||
right: 8px; | |||
z-index: -10; | |||
" | |||
@click="close" | |||
></el-button> | |||
</div> | |||
</div> | |||
</template> | |||
i |
@@ -0,0 +1,105 @@ | |||
.modal { | |||
background: #0003; | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
height: 100%; | |||
width: 100%; | |||
display: grid; | |||
place-items: center; | |||
color: #000; | |||
} | |||
.dribbble-dialog { | |||
position: relative; | |||
margin: 12px; | |||
background: #fff; | |||
border-radius: 18px; | |||
box-shadow: 0 0 32px 2px #0001; | |||
overflow-x: hidden; | |||
height: auto; | |||
padding: 20px; | |||
transition: all 0.1s cubic-bezier(0, 0.48, 0, 1.18); | |||
} | |||
.content-text { | |||
margin: 56px 56px 32px; | |||
text-align: center; | |||
font-weight: 700; | |||
line-height: 1; | |||
} | |||
p { | |||
margin: 10px 0; | |||
user-select: none; | |||
} | |||
button { | |||
border: none; | |||
outline: none; | |||
appearance: none; | |||
padding: 12px 24px; | |||
border-radius: 20px; | |||
background: #000; | |||
color: #fff; | |||
margin-top: 12px; | |||
font-size: 12px; | |||
font-weight: 700; | |||
letter-spacing: 0.5px; | |||
text-transform: uppercase; | |||
transition: all 0.1s ease-out; | |||
} | |||
button:hover { | |||
cursor: pointer; | |||
background: #000b; | |||
} | |||
.logo { | |||
width: 128px; | |||
height: 40px; | |||
position: absolute; | |||
top: 20px; | |||
left: 20px; | |||
background: url(../../assets/logo@2x.png) center / contain no-repeat; | |||
} | |||
.left { | |||
width: 160px; | |||
height: 80px; | |||
position: absolute; | |||
bottom: 0; | |||
left: 0; | |||
background: url(../../assets/leftcorner@2x.png) center / 100% 100% no-repeat; | |||
z-index: -100; | |||
} | |||
.right { | |||
width: 100px; | |||
height: 118px; | |||
position: absolute; | |||
z-index: -100; | |||
top: -2px; | |||
right: 0; | |||
background: url(../../assets/rightcorner@2x.png) center / 100% 100% no-repeat; | |||
} | |||
.fade-in { | |||
opacity: 0; | |||
transform: translateY(-56px); | |||
} | |||
.fade-in.visible { | |||
transform: translateY(0); | |||
opacity: 1; | |||
} | |||
.pop-out { | |||
opacity: 0; | |||
transform: scale(0); | |||
} | |||
.pop-out.visible { | |||
opacity: 1; | |||
transform: scale(1); | |||
} |
@@ -0,0 +1,52 @@ | |||
<script setup> | |||
import { ref, onMounted } from 'vue'; | |||
import * as echarts from 'echarts'; | |||
import Container from './Base/Container.vue'; | |||
const chartChart = ref(null); | |||
onMounted(() => { | |||
chartChart.value.classList.add('h-full'); | |||
const mc = echarts.init(chartChart.value); | |||
mc.setOption({ | |||
grid: { | |||
top: 24, | |||
bottom: 24, | |||
left: 24, | |||
right: 24, | |||
}, | |||
tooltip: {}, | |||
xAxis: { | |||
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], | |||
}, | |||
yAxis: {}, | |||
series: [ | |||
{ | |||
name: '销量', | |||
type: 'bar', | |||
data: [5, 20, 36, 10, 10, 20], | |||
}, | |||
], | |||
}); | |||
}); | |||
</script> | |||
<template> | |||
<Container class="chart" title="小时数据" icon="cube"> | |||
<div ref="chartChart" class="chart-chart"></div> | |||
</Container> | |||
</template> | |||
<style scoped> | |||
.chart { | |||
height: 300px; | |||
} | |||
.chart-inner { | |||
background: #0f0; | |||
} | |||
.chart-chart { | |||
height: 100%; | |||
} | |||
</style> |
@@ -0,0 +1,55 @@ | |||
<script setup> | |||
import { ref } from 'vue'; | |||
const emit = defineEmits(['change']); | |||
const handleClick = (page) => { | |||
emit('change', page); | |||
}; | |||
</script> | |||
<template> | |||
<nav class="nav-menu"> | |||
<ul> | |||
<li><button @click="(e) => handleClick('3d')">三维界面</button></li> | |||
<li><button @click="(e) => handleClick('data')">数据界面</button></li> | |||
<li><button @click="(e) => handleClick('realtime')">实时数据</button></li> | |||
<li><button @click="(e) => handleClick('alert')">报警列表</button></li> | |||
<li> | |||
<button @click="(e) => handleClick('announcement')">公告页面</button> | |||
</li> | |||
</ul> | |||
</nav> | |||
</template> | |||
<style scoped> | |||
.nav-menu { | |||
width: 480px; | |||
padding: 12px; | |||
background: #0006; | |||
color: #fff; | |||
/** | |||
position: fixed; | |||
top: 45%; | |||
left: 10px; | |||
*/ | |||
} | |||
button { | |||
appearance: none; | |||
border: none; | |||
outline: none; | |||
background: #7777; | |||
color: #fff; | |||
cursor: pointer; | |||
} | |||
button:hover { | |||
background: #ccc; | |||
} | |||
ul, | |||
li { | |||
margin: 0; | |||
padding: 0; | |||
list-style: none; | |||
} | |||
</style> |
@@ -0,0 +1,56 @@ | |||
<!-- 货物情况 --> | |||
<script setup> | |||
import { ref, onMounted } from 'vue'; | |||
import * as echarts from 'echarts'; | |||
import Container from './Base/Container.vue'; | |||
const cargoChart = ref(null); | |||
onMounted(() => { | |||
cargoChart.value.classList.add('h-full'); | |||
const mc = echarts.init(cargoChart.value); | |||
mc.setOption({ | |||
grid: { | |||
top: 24, | |||
bottom: 24, | |||
left: 24, | |||
right: 24, | |||
}, | |||
tooltip: {}, | |||
xAxis: { | |||
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], | |||
}, | |||
yAxis: {}, | |||
series: [ | |||
{ | |||
name: '销量', | |||
type: 'bar', | |||
data: [5, 20, 36, 10, 10, 20], | |||
}, | |||
], | |||
}); | |||
}); | |||
</script> | |||
<template> | |||
<Container class="cargo" title="实时数据" icon="cube"> | |||
<div ref="cargoChart" class="cargo-chart"></div> | |||
</Container> | |||
</template> | |||
<style scoped> | |||
.cargo { | |||
height: 640px; | |||
width: 80%; | |||
align-self: self-end; | |||
} | |||
.cargo-inner { | |||
background: #0f0; | |||
} | |||
.cargo-chart { | |||
height: 100%; | |||
} | |||
</style> |
@@ -0,0 +1,49 @@ | |||
<!-- 实时数据表格 --> | |||
<script setup> | |||
const tableData = [ | |||
{ productLine: '00A', mbt: '---', mbb: '---', dkt: '---', dkb: '---', dmt: '---', dmb: '---', syt: '---', syb: '---', ght: '---', ghb: '---', gh1: '---', gh2: '---', bzt: '---', bzb: '---' }, | |||
{ productLine: '00B', mbt: '---', mbb: '---', dkt: '---', dkb: '---', dmt: '---', dmb: '---', syt: '---', syb: '---', ght: '---', ghb: '---', gh1: '---', gh2: '---', bzt: '---', bzb: '---' }, | |||
{ productLine: '00C', mbt: '---', mbb: '---', dkt: '---', dkb: '---', dmt: '---', dmb: '---', syt: '---', syb: '---', ght: '---', ghb: '---', gh1: '---', gh2: '---', bzt: '---', bzb: '---' }, | |||
{ productLine: '00D', mbt: '---', mbb: '---', dkt: '---', dkb: '---', dmt: '---', dmb: '---', syt: '---', syb: '---', ght: '---', ghb: '---', gh1: '---', gh2: '---', bzt: '---', bzb: '---' }, | |||
{ productLine: '00E', mbt: '---', mbb: '---', dkt: '---', dkb: '---', dmt: '---', dmb: '---', syt: '---', syb: '---', ght: '---', ghb: '---', gh1: '---', gh2: '---', bzt: '---', bzb: '---' }, | |||
{ productLine: '00F', mbt: '---', mbb: '---', dkt: '---', dkb: '---', dmt: '---', dmb: '---', syt: '---', syb: '---', ght: '---', ghb: '---', gh1: '---', gh2: '---', bzt: '---', bzb: '---' }, | |||
] | |||
</script> | |||
<template> | |||
<div class="realtime-table"> | |||
<el-table class="dark-table" :data="tableData" :show-overflow-tooltip="true" row-class-name="dark-row" | |||
header-row-class-name="dark-header"> | |||
<el-table-column prop="productLine" label="产线"></el-table-column> | |||
<el-table-column prop="mbt" label="磨边上"></el-table-column> | |||
<el-table-column prop="mbb" label="磨边下"></el-table-column> | |||
<el-table-column prop="dkt" label="打孔上"></el-table-column> | |||
<el-table-column prop="dkb" label="打孔下"></el-table-column> | |||
<el-table-column prop="dmt" label="镀膜上"></el-table-column> | |||
<el-table-column prop="dmb" label="镀膜下"></el-table-column> | |||
<el-table-column prop="syt" label="丝印上"></el-table-column> | |||
<el-table-column prop="syb" label="丝印下"></el-table-column> | |||
<el-table-column prop="ght" label="固化上"></el-table-column> | |||
<el-table-column prop="ghb" label="固化下"></el-table-column> | |||
<el-table-column prop="gh1" label="钢化上"></el-table-column> | |||
<el-table-column prop="gh2" label="钢化下"></el-table-column> | |||
<el-table-column prop="bzt" label="包装上"></el-table-column> | |||
<el-table-column prop="bzb" label="包装下"></el-table-column> | |||
</el-table> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.realtime-table { | |||
background: #00f3; | |||
height: 240px; | |||
width: 80%; | |||
align-self: self-start; | |||
} | |||
.dark-table { | |||
height: 100%; | |||
} | |||
</style> |
@@ -0,0 +1,65 @@ | |||
<script setup> | |||
import { ref, watch, onMounted } from 'vue'; | |||
const emit = defineEmits(['size-change']); | |||
const size = ref(100); | |||
const slider = ref(null); | |||
// watchers | |||
watch(size, (value) => { | |||
if (value == null) return; | |||
emit('size-change', value); | |||
}); | |||
// handlers | |||
function keydownHandler(e) { | |||
if (e.shiftKey && e.key === 'L') { | |||
if (slider.value) { | |||
slider.value.classList.toggle('show'); | |||
} | |||
} | |||
} | |||
// hooks | |||
onMounted(() => { | |||
document.addEventListener('keydown', keydownHandler); | |||
}); | |||
</script> | |||
<template> | |||
<div ref="slider" class="slider"> | |||
<input type="range" min="0" max="100" v-model="size" /> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.slider { | |||
height: 5vh; | |||
width: 20vw; | |||
border-radius: 88px; | |||
box-shadow: 0 0 68px 8px rgba(0, 0, 0, 0.3); | |||
padding: 32px; | |||
display: grid; | |||
place-items: center; | |||
background: #fff; | |||
position: fixed; | |||
z-index: 1000; | |||
bottom: -5vh; | |||
opacity: 0; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
transition: opacity 0.3s ease-out, bottom 0.3s ease-out; | |||
} | |||
.slider input { | |||
width: 100%; | |||
transform: translateY(-7px); | |||
color: #0b58ff; | |||
background: #fcc; | |||
} | |||
.slider.show { | |||
opacity: 1; | |||
bottom: 64px; | |||
} | |||
</style> |
@@ -0,0 +1,52 @@ | |||
<script setup> | |||
import { ref, onMounted } from 'vue'; | |||
import * as echarts from 'echarts'; | |||
import Container from './Base/Container.vue'; | |||
const chartChart = ref(null); | |||
onMounted(() => { | |||
chartChart.value.classList.add('h-full'); | |||
const mc = echarts.init(chartChart.value); | |||
mc.setOption({ | |||
grid: { | |||
top: 24, | |||
bottom: 24, | |||
left: 24, | |||
right: 24, | |||
}, | |||
tooltip: {}, | |||
xAxis: { | |||
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], | |||
}, | |||
yAxis: {}, | |||
series: [ | |||
{ | |||
name: '销量', | |||
type: 'bar', | |||
data: [5, 20, 36, 10, 10, 20], | |||
}, | |||
], | |||
}); | |||
}); | |||
</script> | |||
<template> | |||
<Container class="chart" title="本日班组情况" icon="cube"> | |||
<div ref="chartChart" class="chart-chart"></div> | |||
</Container> | |||
</template> | |||
<style scoped> | |||
.chart { | |||
height: 300px; | |||
} | |||
.chart-inner { | |||
background: #0f0; | |||
} | |||
.chart-chart { | |||
height: 100%; | |||
} | |||
</style> |
@@ -0,0 +1,52 @@ | |||
<script setup> | |||
import { ref, onMounted } from 'vue'; | |||
import * as echarts from 'echarts'; | |||
import Container from './Base/Container.vue'; | |||
const chartChart = ref(null); | |||
onMounted(() => { | |||
chartChart.value.classList.add('h-full'); | |||
const mc = echarts.init(chartChart.value); | |||
mc.setOption({ | |||
grid: { | |||
top: 24, | |||
bottom: 24, | |||
left: 24, | |||
right: 24, | |||
}, | |||
tooltip: {}, | |||
xAxis: { | |||
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], | |||
}, | |||
yAxis: {}, | |||
series: [ | |||
{ | |||
name: '销量', | |||
type: 'bar', | |||
data: [5, 20, 36, 10, 10, 20], | |||
}, | |||
], | |||
}); | |||
}); | |||
</script> | |||
<template> | |||
<Container class="chart" title="本月班组情况" icon="cube"> | |||
<div ref="chartChart" class="chart-chart"></div> | |||
</Container> | |||
</template> | |||
<style scoped> | |||
.chart { | |||
height: 300px; | |||
} | |||
.chart-inner { | |||
background: #0f0; | |||
} | |||
.chart-chart { | |||
height: 100%; | |||
} | |||
</style> |
@@ -0,0 +1,21 @@ | |||
<script setup> | |||
import { ref, watch, onMounted } from 'vue'; | |||
</script> | |||
<template> | |||
<div class="tools"> | |||
<button>Home</button> | |||
<button>Settings</button> | |||
<button>AutoScroll</button> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.tools { | |||
background: #00f; | |||
padding: 8px; | |||
position: absolute; | |||
bottom: 24px; | |||
left: 24px; | |||
} | |||
</style> |
@@ -0,0 +1,11 @@ | |||
{ | |||
"code": 0, | |||
"data": [ | |||
{ "date": "2023-01-01", "检测项目": "光刻胶&显影", "光刻胶残留": "1" }, | |||
{ "date": "2023-01-01", "检测项目": "激光一", "磨面擦伤": "1" }, | |||
{ "date": "2023-01-01", "检测项目": "激光二", "刻线宽度不良": "1" }, | |||
{ "date": "2023-01-02", "检测项目": "光刻胶&显影", "光刻胶残留": "2" }, | |||
{ "date": "2023-01-02", "检测项目": "激光一", "刻线宽度不良": "2" }, | |||
{ "date": "2023-01-03", "检测项目": "激光二", "擦伤": "3" } | |||
] | |||
} |
@@ -0,0 +1,16 @@ | |||
import { createApp } from 'vue'; | |||
import { createPinia } from 'pinia' | |||
import './style.css'; | |||
import ElementPlus from 'element-plus'; | |||
import 'element-plus/dist/index.css'; | |||
import Vue3Marquee from 'vue3-marquee'; | |||
import App from './App.vue'; | |||
const pinia = createPinia() | |||
const app = createApp(App); | |||
app.use(pinia); | |||
app.use(ElementPlus); | |||
app.use(Vue3Marquee, { name: 'ScrollText' }); | |||
app.mount('#app'); |
@@ -0,0 +1 @@ | |||
<!-- 三维页面 --> |
@@ -0,0 +1,140 @@ | |||
<!-- 报警列表页面 --> | |||
<script setup> | |||
import { ref } from 'vue'; | |||
const tableData = [ | |||
{ id: 1, eqName: '设备00A', eqIndex: '1', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 2, eqName: '设备00B', eqIndex: '2', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 3, eqName: '设备00C', eqIndex: '3', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 4, eqName: '设备00D', eqIndex: '4', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 5, eqName: '设备00E', eqIndex: '5', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 6, eqName: '设备00F', eqIndex: '6', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 7, eqName: '设备00G', eqIndex: '7', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 1, eqName: '设备00A', eqIndex: '1', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 2, eqName: '设备00B', eqIndex: '2', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 3, eqName: '设备00C', eqIndex: '3', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 4, eqName: '设备00D', eqIndex: '4', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 5, eqName: '设备00E', eqIndex: '5', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 6, eqName: '设备00F', eqIndex: '6', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 7, eqName: '设备00G', eqIndex: '7', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 1, eqName: '设备00A', eqIndex: '1', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 2, eqName: '设备00B', eqIndex: '2', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 3, eqName: '设备00C', eqIndex: '3', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 4, eqName: '设备00D', eqIndex: '4', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 5, eqName: '设备00E', eqIndex: '5', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 6, eqName: '设备00F', eqIndex: '6', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 7, eqName: '设备00G', eqIndex: '7', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 1, eqName: '设备00A', eqIndex: '1', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 2, eqName: '设备00B', eqIndex: '2', alarmGrade: 1, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 3, eqName: '设备00C', eqIndex: '3', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 4, eqName: '设备00D', eqIndex: '4', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 5, eqName: '设备00E', eqIndex: '5', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 6, eqName: '设备00F', eqIndex: '6', alarmGrade: 3, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
{ id: 7, eqName: '设备00G', eqIndex: '7', alarmGrade: 2, alarmDetail: 'lorem sdf fkj', position: 'x,y' }, | |||
] | |||
</script> | |||
<template> | |||
<div class="alert-list-page"> | |||
<div class="alert-list"> | |||
<div class="alert-list__table" style=""> | |||
<el-table class="dark-table" :data="tableData" :show-overflow-tooltip="true" 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="90"></el-table-column> | |||
<el-table-column prop="alarmDetail" label="报警细节" width="144"></el-table-column> | |||
<el-table-column prop="position" label="定位"></el-table-column> | |||
</el-table> | |||
</div> | |||
<button class="alert-btn">忽略</button> | |||
</div> | |||
</div> | |||
</template> | |||
<style> | |||
.dark-table { | |||
/* height: 72vh; */ | |||
/* height: 72%; */ | |||
height: 100%; | |||
background: transparent; | |||
} | |||
.dark-row { | |||
color: #fff; | |||
background: #005eff25 !important; | |||
} | |||
.dark-row>td.el-table__cell { | |||
border: none; | |||
} | |||
.dark-row:hover>td.el-table__cell { | |||
background-color: #005effaa !important; | |||
} | |||
.dark-header { | |||
background-color: transparent !important; | |||
color: #fff; | |||
} | |||
.dark-header>th.el-table__cell.is-leaf { | |||
border-bottom: none; | |||
background-color: #005eff95 !important; | |||
font-weight: 400; | |||
letter-spacing: 1px; | |||
} | |||
.dark-header>th.el-table__cell { | |||
} | |||
</style> | |||
<style scoped> | |||
.alert-list-page { | |||
flex: 1; | |||
position: relative; | |||
} | |||
.alert-list { | |||
height: 100%; | |||
width: 480px; | |||
position: absolute; | |||
top: 0; | |||
right: 0; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.alert-list__table { | |||
height: calc(100% - 72px); | |||
background: linear-gradient(to right, transparent, #0ba6ff80); | |||
} | |||
.alert-list__table>>>.el-table__inner-wrapper::before { | |||
background: transparent; | |||
} | |||
button { | |||
appearance: none; | |||
outline: none; | |||
border: none; | |||
cursor: pointer; | |||
} | |||
.alert-btn { | |||
height: 72px; | |||
background: #0f04; | |||
color: #fff; | |||
font-size: 32px; | |||
line-height: 1; | |||
letter-spacing: 2px; | |||
transition: all .2s ease-out; | |||
} | |||
.alert-btn:hover { | |||
background: #0f08; | |||
} | |||
</style> |
@@ -0,0 +1,68 @@ | |||
<!-- 公告页面 --> | |||
<script setup> | |||
import { ref } from 'vue'; | |||
const emit = defineEmits(['home']); | |||
const content = ref('公告加载中...'); | |||
const handleClose = () => { | |||
emit('home'); | |||
}; | |||
</script> | |||
<template> | |||
<div class="announcement-page"> | |||
<h1 class="announcement-title">公告栏</h1> | |||
<main class="announcement-content"> | |||
<ScrollText :vertical="true" :duration="10" :pause-on-hover="true"> | |||
{{ content }} | |||
</ScrollText> | |||
</main> | |||
<div class="announcement-footer"> | |||
<button | |||
style="position: absolute; left: 0; bottom: 10px" | |||
@click="handleClose" | |||
> | |||
返回 | |||
</button> | |||
<ScrollText> 数据加载异常 </ScrollText> | |||
</div> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.announcement-page { | |||
flex: 1; | |||
height: 100%; | |||
width: 100%; | |||
background: #000; | |||
display: flex; | |||
flex-direction: column; | |||
color: #f00; | |||
} | |||
.announcement-title { | |||
color: #fff; | |||
font-size: 72px; | |||
font-weight: 500; | |||
letter-spacing: 2px; | |||
text-align: center; | |||
} | |||
.announcement-content { | |||
/* background: #ccc3; */ | |||
flex: 1; | |||
padding: 8px 80px; | |||
font-size: 72px; | |||
font-family: serif; | |||
} | |||
.announcement-footer { | |||
height: 128px; | |||
/* background: #ccc1; */ | |||
font-size: 64px; | |||
padding: 0 80px; | |||
line-height: 128px; | |||
font-family: sans-serif; | |||
position: relative; | |||
} | |||
</style> |
@@ -0,0 +1,43 @@ | |||
<!-- 实时数据页面 --> | |||
<script setup> | |||
import HourChart from "../components/HourChart.vue"; | |||
import TeamChartDay from "../components/TeamChartDay.vue"; | |||
import TeamChartMonth from "../components/TeamChartMonth.vue"; | |||
import { useWsStore } from '../store' | |||
const store = useWsStore(); | |||
console.log('store', store.data1.test); | |||
</script> | |||
<template> | |||
<div class="data-page"> | |||
<div class="data-list"> | |||
<HourChart /> | |||
<TeamChartDay /> | |||
<TeamChartMonth /> | |||
</div> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.data-page { | |||
flex: 1; | |||
position: relative; | |||
} | |||
.data-list { | |||
height: 100%; | |||
width: 480px; | |||
background: #ccc3; | |||
position: absolute; | |||
top: 0; | |||
right: 0; | |||
display: flex; | |||
flex-direction: column; | |||
justify-content: center; | |||
} | |||
</style> |
@@ -0,0 +1,21 @@ | |||
<!-- 实时数据页面 --> | |||
<script setup> | |||
import Chart from '../components/RealtimeChart.vue'; | |||
import RealtimeTable from '../components/RealtimeTable.vue'; | |||
</script> | |||
<template> | |||
<div class="realtime-page"> | |||
<Chart /> | |||
<RealtimeTable /> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.realtime-page { | |||
flex: 1; | |||
display: grid; | |||
place-items: center; | |||
gap: 24px; | |||
} | |||
</style> |
@@ -0,0 +1,41 @@ | |||
import { defineStore } from "pinia"; | |||
import { ref } from 'vue'; | |||
export const useWsStore = defineStore("wsData", () => { | |||
const data1 = ref({ test: 'hello world'}); | |||
const data2 = ref({}); | |||
const data3 = ref({}); | |||
function updateData(category, data) { | |||
console.log('update', data) | |||
switch (category) { | |||
case "1": | |||
data1.value = data; | |||
break; | |||
case "2": | |||
data2.value = data; | |||
break; | |||
case "3": | |||
data3.value = data; | |||
default: | |||
break; | |||
} | |||
} | |||
return { data1, data2, data3, updateData }; | |||
}); | |||
// export const useWsStore = defineStore('wsData', { | |||
// state: () => ({ | |||
// data1: { | |||
// test: 'hello world' | |||
// }, | |||
// data2: null, | |||
// data3: null, | |||
// }), | |||
// actions: { | |||
// updateData(category, data) { | |||
// this[category] = data | |||
// } | |||
// } | |||
// }) |
@@ -0,0 +1,4 @@ | |||
* { | |||
box-sizing: border-box; | |||
margin: 0; | |||
} |
@@ -0,0 +1,22 @@ | |||
import Client from './ws' | |||
const uid_list = [ | |||
// 磨边 丝印 钢化 包装 | |||
// 一线 | |||
'1-1', '1-2', '1-3', '1-4', | |||
// 二线 | |||
'2-1', '2-2', '2-3', '2-4', | |||
// 三线 | |||
'3-1', '3-2', '3-3', '3-4', | |||
] | |||
const url = 'wss://192.168.1.101:8082/QbMonitoring/websocket/' | |||
uid_list.forEach(uid => { | |||
new Client({ | |||
url: url + uid, | |||
name: '/' + uid | |||
}, message => { | |||
console.log('message', message); | |||
}) | |||
}) |
@@ -0,0 +1,95 @@ | |||
export default class XClient { | |||
url = ""; | |||
name = ""; | |||
ws = null; | |||
timeout = 1000 * 5; | |||
heartbeatDelay = 1000 * 60 * 3; | |||
reconnectCount = 0; | |||
onmessage = null; | |||
hb = 0; | |||
constructor( | |||
config, | |||
onmessage | |||
) { | |||
this.url = config.url; | |||
this.name = config.name; | |||
this.ws = new WebSocket(config.url); | |||
this.timeout = config.timeout || 1000 * 5; | |||
this.ws.onopen = () => { | |||
console.log(`[*] ${this.name} 初始化连接成功`); | |||
this.hb = this.heartbeat(); | |||
}; | |||
this.onmessage = onmessage; | |||
this.ws.onmessage = onmessage; | |||
this.ws.onerror = (err) => { | |||
console.log(`[*] ${this.name} 连接错误`, err); | |||
this.tryReconnect(); | |||
}; | |||
this.ws.onclose = (e) => { | |||
console.log(`[*] ${this.name} 连接关闭`); | |||
}; | |||
} | |||
heartbeat() { | |||
if (this.hb) clearInterval(this.hb); | |||
return setInterval(() => { | |||
console.log(`${this.name} ping...`); | |||
if (this.ws.readyState == WebSocket.OPEN) { | |||
this.ws.send("ping"); | |||
} else { | |||
clearInterval(this.hb); | |||
this.tryReconnect(); | |||
} | |||
}, this.heartbeatDelay); | |||
} | |||
tryReconnect() { | |||
console.log(`[*] ${this.name} 重连中,已尝试:`, this.reconnectCount, "次"); | |||
setTimeout( | |||
() => { | |||
if (this.ws.readyState === WebSocket.OPEN) { | |||
this.reconnectCount = 0; | |||
console.log(`[*] ${this.name} 已恢复连接,取消重连`); | |||
return; | |||
} | |||
this.reconnectCount++; | |||
this.ws = new WebSocket(this.url); | |||
this.ws.onopen = () => { | |||
console.log(`[*] ${this.name} 重连成功`); | |||
this.reconnectCount = 0; | |||
this.hb = this.heartbeat(); | |||
}; | |||
this.ws.onmessage = this.onmessage; | |||
this.ws.onerror = (err) => { | |||
console.log(`[*] ${this.name} 重连出错`); | |||
this.tryReconnect(); | |||
}; | |||
this.ws.onclose = (e) => { | |||
console.log(`[*] ${this.name} 重连连接关闭`); | |||
}; | |||
}, | |||
this.reconnectCount == 0 ? 0 : this.timeout | |||
); | |||
} | |||
logStatus() { | |||
console.log( | |||
`[*] ${this.name} 重连状态: 连接中`, | |||
this.ws.readyState == WebSocket.CONNECTING | |||
); | |||
console.log( | |||
`[*] ${this.name} 重连状态: 已连接`, | |||
this.ws.readyState == WebSocket.OPEN | |||
); | |||
console.log( | |||
`[*] ${this.name} 重连状态: 关闭中`, | |||
this.ws.readyState == WebSocket.CLOSING | |||
); | |||
console.log( | |||
`[*] ${this.name} 重连状态: 关闭`, | |||
this.ws.readyState == WebSocket.CLOSED | |||
); | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
import { defineConfig } from 'vite' | |||
import vue from '@vitejs/plugin-vue' | |||
// https://vitejs.dev/config/ | |||
export default defineConfig({ | |||
plugins: [vue()], | |||
}) |