DESKTOP-FUDKNA8\znjsz 8 månader sedan
incheckning
9d48e56dd0
50 ändrade filer med 3533 tillägg och 0 borttagningar
  1. +2
    -0
      .env
  2. +1
    -0
      .gitignore
  3. +3
    -0
      .vscode/extensions.json
  4. +7
    -0
      README.md
  5. +24
    -0
      _gitignore
  6. +13
    -0
      index.html
  7. +1940
    -0
      package-lock.json
  8. +22
    -0
      package.json
  9. +1
    -0
      public/vite.svg
  10. +71
    -0
      src/App.vue
  11. +57
    -0
      src/MainPage.vue
  12. Binär
      src/assets/1.png
  13. Binär
      src/assets/2.png
  14. Binär
      src/assets/3.png
  15. Binär
      src/assets/bg.png
  16. Binär
      src/assets/header-bg.png
  17. Binär
      src/assets/header-bg@2x.png
  18. Binär
      src/assets/leftcorner@2x.png
  19. Binär
      src/assets/logo@2x.png
  20. Binär
      src/assets/rightcorner@2x.png
  21. +24
    -0
      src/assets/svg/check.svg
  22. +22
    -0
      src/assets/svg/cube-twice.svg
  23. +12
    -0
      src/assets/svg/cube.svg
  24. +40
    -0
      src/assets/svg/stack.svg
  25. +114
    -0
      src/components/Base/Container.vue
  26. +62
    -0
      src/components/Base/Corner.vue
  27. +37
    -0
      src/components/Base/Header.vue
  28. +51
    -0
      src/components/Base/Icon.vue
  29. +54
    -0
      src/components/Dribbble/Dialog.vue
  30. +105
    -0
      src/components/Dribbble/style.css
  31. +52
    -0
      src/components/HourChart.vue
  32. +55
    -0
      src/components/NavMenu.vue
  33. +56
    -0
      src/components/RealtimeChart.vue
  34. +49
    -0
      src/components/RealtimeTable.vue
  35. +65
    -0
      src/components/Slider.vue
  36. +52
    -0
      src/components/TeamChartDay.vue
  37. +52
    -0
      src/components/TeamChartMonth.vue
  38. +21
    -0
      src/components/Tools.vue
  39. +11
    -0
      src/components/data.json
  40. +16
    -0
      src/main.js
  41. +1
    -0
      src/pages/3D.vue
  42. +140
    -0
      src/pages/AlertListPage.vue
  43. +68
    -0
      src/pages/AnnouncementPage.vue
  44. +43
    -0
      src/pages/DataPage.vue
  45. +21
    -0
      src/pages/RealtimePage.vue
  46. +41
    -0
      src/store/index.js
  47. +4
    -0
      src/style.css
  48. +22
    -0
      src/utils/connects.js
  49. +95
    -0
      src/utils/ws.js
  50. +7
    -0
      vite.config.js

+ 2
- 0
.env Visa fil

@@ -0,0 +1,2 @@
# Place your StackBlitz environment variables here,
# and they will be securely synced to your account.

+ 1
- 0
.gitignore Visa fil

@@ -0,0 +1 @@
node_modules

+ 3
- 0
.vscode/extensions.json Visa fil

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

+ 7
- 0
README.md Visa fil

@@ -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).

+ 24
- 0
_gitignore Visa fil

@@ -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?

+ 13
- 0
index.html Visa fil

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

+ 1940
- 0
package-lock.json
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 22
- 0
package.json Visa fil

@@ -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"
}
}

+ 1
- 0
public/vite.svg Visa fil

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

+ 71
- 0
src/App.vue Visa fil

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

+ 57
- 0
src/MainPage.vue Visa fil

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

Binär
src/assets/1.png Visa fil

Före Efter
Bredd: 80  |  Höjd: 80  |  Storlek: 3.1 KiB

Binär
src/assets/2.png Visa fil

Före Efter
Bredd: 80  |  Höjd: 80  |  Storlek: 3.5 KiB

Binär
src/assets/3.png Visa fil

Före Efter
Bredd: 80  |  Höjd: 80  |  Storlek: 3.1 KiB

Binär
src/assets/bg.png Visa fil

Före Efter
Bredd: 1920  |  Höjd: 1080  |  Storlek: 761 KiB

Binär
src/assets/header-bg.png Visa fil

Före Efter
Bredd: 1920  |  Höjd: 89  |  Storlek: 25 KiB

Binär
src/assets/header-bg@2x.png Visa fil

Före Efter
Bredd: 3840  |  Höjd: 178  |  Storlek: 90 KiB

Binär
src/assets/leftcorner@2x.png Visa fil

Före Efter
Bredd: 370  |  Höjd: 194  |  Storlek: 4.3 KiB

Binär
src/assets/logo@2x.png Visa fil

Före Efter
Bredd: 310  |  Höjd: 88  |  Storlek: 12 KiB

Binär
src/assets/rightcorner@2x.png Visa fil

Före Efter
Bredd: 302  |  Höjd: 376  |  Storlek: 5.2 KiB

+ 24
- 0
src/assets/svg/check.svg Visa fil

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

+ 22
- 0
src/assets/svg/cube-twice.svg Visa fil

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

+ 12
- 0
src/assets/svg/cube.svg Visa fil

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

+ 40
- 0
src/assets/svg/stack.svg Visa fil

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

+ 114
- 0
src/components/Base/Container.vue Visa fil

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

+ 62
- 0
src/components/Base/Corner.vue Visa fil

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

+ 37
- 0
src/components/Base/Header.vue Visa fil

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

+ 51
- 0
src/components/Base/Icon.vue Visa fil

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

+ 54
- 0
src/components/Dribbble/Dialog.vue Visa fil

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

+ 105
- 0
src/components/Dribbble/style.css Visa fil

@@ -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);
}

+ 52
- 0
src/components/HourChart.vue Visa fil

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

+ 55
- 0
src/components/NavMenu.vue Visa fil

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

+ 56
- 0
src/components/RealtimeChart.vue Visa fil

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

+ 49
- 0
src/components/RealtimeTable.vue Visa fil

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

+ 65
- 0
src/components/Slider.vue Visa fil

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

+ 52
- 0
src/components/TeamChartDay.vue Visa fil

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

+ 52
- 0
src/components/TeamChartMonth.vue Visa fil

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

+ 21
- 0
src/components/Tools.vue Visa fil

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

+ 11
- 0
src/components/data.json Visa fil

@@ -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" }
]
}

+ 16
- 0
src/main.js Visa fil

@@ -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');

+ 1
- 0
src/pages/3D.vue Visa fil

@@ -0,0 +1 @@
<!-- 三维页面 -->

+ 140
- 0
src/pages/AlertListPage.vue Visa fil

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

+ 68
- 0
src/pages/AnnouncementPage.vue Visa fil

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

+ 43
- 0
src/pages/DataPage.vue Visa fil

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

+ 21
- 0
src/pages/RealtimePage.vue Visa fil

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

+ 41
- 0
src/store/index.js Visa fil

@@ -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
// }
// }
// })

+ 4
- 0
src/style.css Visa fil

@@ -0,0 +1,4 @@
* {
box-sizing: border-box;
margin: 0;
}

+ 22
- 0
src/utils/connects.js Visa fil

@@ -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);
})
})

+ 95
- 0
src/utils/ws.js Visa fil

@@ -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
);
}
}

+ 7
- 0
vite.config.js Visa fil

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
})

Laddar…
Avbryt
Spara