Merge branch 'main' of http://121.37.111.42:3000/zhengsl/emcp
@ -1,54 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import { usePlatformStore } from './stores/platform'
|
|
||||||
import RealtimeView from './views/RealtimeView.vue'
|
|
||||||
import StatusView from './views/StatusView.vue'
|
|
||||||
import DeviceConfigView from './views/DeviceConfigView.vue'
|
|
||||||
import ChannelConfigView from './views/ChannelConfigView.vue'
|
|
||||||
import AlarmSettingView from './views/AlarmSettingView.vue'
|
|
||||||
import AlarmHistoryView from './views/AlarmHistoryView.vue'
|
|
||||||
import ControlView from './views/ControlView.vue'
|
|
||||||
import SystemConfigView from './views/SystemConfigView.vue'
|
|
||||||
|
|
||||||
const store = usePlatformStore()
|
|
||||||
const activeTab = ref('realtime')
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{ key: 'realtime', label: '实时数据', component: RealtimeView },
|
|
||||||
{ key: 'status', label: '设备状态', component: StatusView },
|
|
||||||
{ key: 'device', label: '设备配置', component: DeviceConfigView },
|
|
||||||
{ key: 'channel', label: '通道配置', component: ChannelConfigView },
|
|
||||||
{ key: 'alarm-setting', label: '报警设置', component: AlarmSettingView },
|
|
||||||
{ key: 'alarms', label: '报警历史', component: AlarmHistoryView },
|
|
||||||
{ key: 'control', label: '控制指令', component: ControlView },
|
|
||||||
{ key: 'system', label: '系统设置', component: SystemConfigView },
|
|
||||||
]
|
|
||||||
|
|
||||||
const currentComponent = computed(() => tabs.find((item) => item.key === activeTab.value)?.component ?? RealtimeView)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
void store.bootstrap()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="app-shell">
|
<router-view />
|
||||||
<header class="page-header">
|
|
||||||
<h1>电气量测控平台</h1>
|
|
||||||
<p>RK3568 + FastAPI + WebSocket + Vue 的前后端开发框架骨架。</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="tabs">
|
|
||||||
<button
|
|
||||||
v-for="tab in tabs"
|
|
||||||
:key="tab.key"
|
|
||||||
:class="{ active: tab.key === activeTab }"
|
|
||||||
@click="activeTab = tab.key"
|
|
||||||
>
|
|
||||||
{{ tab.label }}
|
|
||||||
</button>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<component :is="currentComponent" :store="store.state" :actions="store" />
|
|
||||||
</main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export const http = axios.create({
|
export const http = axios.create({
|
||||||
baseURL: 'http://127.0.0.1:8000/api',
|
baseURL: '/api',
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
BIN
frontend/src/assets/images/dashboard/bg.png
Normal file
|
After Width: | Height: | Size: 360 KiB |
6
frontend/src/assets/images/dashboard/logo.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="20px" height="20px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="matrix(1 0 0 1 -204 -85 )">
|
||||||
|
<path d="M 20 18.6396677102804 C 19.9814177078209 19.3737533513783 19.3550029866943 19.9688473469092 18.5822812592228 20 L 1.44285949335955 20 C 0.670137756283424 19.9688473598131 0.0437230201672776 19.3737533605027 0 18.6396677102804 L 0 1.36033228971964 C 0.0437230201672776 0.626246639497268 0.670137756283424 0.0311526401869173 1.44285949335955 0 L 18.5822812592228 0 C 19.3550029866943 0.0311526530907713 19.9814177078209 0.626246648621674 20 1.36033228971964 L 20 18.6396677102804 Z M 12.1326119035907 9.41416406542056 L 9.66278625184458 9.41316719626168 L 7.4476034431874 13.1564693691589 L 10.1535333743236 14.1201246028038 L 9.05486146089524 17.494911728972 L 12.6835218888342 13.5346209813084 L 10.1538831529759 12.3051298130841 L 12.1326119035907 9.41416406542056 Z M 17.8827130103296 7 L 2.14242771765862 7 C 2.14242771765862 7.00934579439253 2.14242771765862 7.00934579439253 2.14242771765862 7.00934579439253 C 1.75606684912057 7.00934579439253 1.4428594810625 7.30689279404771 1.4428594810625 7.67393561915888 C 1.4428594810625 8.04097844427005 1.75606684912057 8.33852544392524 2.14242771765862 8.33852544392524 L 17.8827130103296 8.33852544392524 C 18.2690738788676 8.33852544392524 18.5822812469257 8.04097844427005 18.5822812469257 7.67393561915888 C 18.5822812469257 7.30689279404768 18.2690738788676 7.00934579439253 17.8827130103296 7.00934579439253 C 17.8827130103296 7.00934579439253 17.8827130103296 7.00934579439253 17.8827130103296 7 Z M 14.7346559517954 2.68951193925233 C 14.3482950900488 2.68951193925233 14.0350877274963 2.9870589336772 14.0350877274963 3.35410175233645 L 14.0350877274963 5.01557633177569 C 14.0350877274963 5.38261915688689 14.3482950955544 5.68016615654206 14.7346559640924 5.68016615654206 C 15.1210168326305 5.68016615654206 15.4342242006886 5.38261915688689 15.4342242006886 5.01557633177569 L 15.4342242006886 3.35410177570094 C 15.4342242006886 2.9870589336772 15.1210168381361 2.68951193925233 14.7346559763895 2.68951193925233 Z M 10.1874624200688 2.68951193925233 C 9.80110155832227 2.68951193925233 9.4878941957698 2.9870589336772 9.4878941957698 3.35410175233645 L 9.4878941957698 5.01557633177569 C 9.4878941957698 5.38261915688689 9.80110156382787 5.68016615654206 10.1874624323659 5.68016615654206 C 10.573823300904 5.68016615654206 10.8870306689621 5.38261915688689 10.8870306689621 5.01557633177569 L 10.8870306689621 3.35410177570094 C 10.8870306689621 2.9870589336772 10.5738233064096 2.68951193925233 10.187462444663 2.68951193925233 Z M 5.64026888834235 2.68951193925233 C 5.25390802659572 2.68951193925233 4.94070066404331 2.9870589336772 4.94070066404331 3.35410175233645 L 4.94070066404331 5.01557633177569 C 4.94070066404331 5.38261915688689 5.25390803210138 5.68016615654206 5.64026890063943 5.68016615654206 C 6.02662976917753 5.68016615654206 6.33983713723561 5.38261915688689 6.33983713723561 5.01557633177569 L 6.33983713723561 3.35410177570094 C 6.33983713723561 2.9870589336772 6.0266297746831 2.68951193925233 5.64026891293656 2.68951193925233 Z " fill-rule="nonzero" fill="#0099ff" stroke="none" transform="matrix(1 0 0 1 204 85 )" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
BIN
frontend/src/assets/images/menuicon/1-1.png
Normal file
|
After Width: | Height: | Size: 388 B |
BIN
frontend/src/assets/images/menuicon/1.png
Normal file
|
After Width: | Height: | Size: 442 B |
BIN
frontend/src/assets/images/menuicon/2-1.png
Normal file
|
After Width: | Height: | Size: 515 B |
BIN
frontend/src/assets/images/menuicon/2.png
Normal file
|
After Width: | Height: | Size: 556 B |
BIN
frontend/src/assets/images/menuicon/3-1.png
Normal file
|
After Width: | Height: | Size: 358 B |
BIN
frontend/src/assets/images/menuicon/3.png
Normal file
|
After Width: | Height: | Size: 367 B |
BIN
frontend/src/assets/images/menuicon/4-1.png
Normal file
|
After Width: | Height: | Size: 672 B |
BIN
frontend/src/assets/images/menuicon/4.png
Normal file
|
After Width: | Height: | Size: 693 B |
BIN
frontend/src/assets/images/menuicon/5-1.png
Normal file
|
After Width: | Height: | Size: 443 B |
BIN
frontend/src/assets/images/menuicon/5.png
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
frontend/src/assets/images/menuicon/6-1.png
Normal file
|
After Width: | Height: | Size: 466 B |
BIN
frontend/src/assets/images/menuicon/6.png
Normal file
|
After Width: | Height: | Size: 482 B |
BIN
frontend/src/assets/images/menuicon/7-1.png
Normal file
|
After Width: | Height: | Size: 528 B |
BIN
frontend/src/assets/images/menuicon/7.png
Normal file
|
After Width: | Height: | Size: 547 B |
6
frontend/src/assets/images/menuicon/bb.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="50px" height="50px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="matrix(1 0 0 1 -1120 -360 )">
|
||||||
|
<path d="M 49.8366012867643 5.39215686274486 C 49.8366012867643 2.50326795343153 47.4967319852942 0.163398713235551 44.607843137255 1 L 5.39215686274497 1 C 2.50326795343108 0.163398713235551 0.163398713235665 2.50326795343153 0.163398713235665 5.39215686274486 L 0.163398713235665 14 L 49.8366012867643 14 L 49.8366012867643 5.39215686274486 Z M 49 18 L 1 18 L 1 44.607843137255 C 0.163398713235665 47.4967320465686 2.50326801470578 49.8366012867643 5.39215686274497 49 L 44.607843137255 49 C 47.4967320465689 49.8366012867643 49.8366012867643 47.4967319852942 49 44.607843137255 L 49 18 Z M 12.1503267769606 32.8431372549019 L 17.2352941176471 37.9281045955884 C 18.0065359681371 38.6993464460781 18.0065359681371 39.9411764705883 17.2352941176471 40.6993463848039 C 16.4640522671571 41.4705882352942 15.2222222426468 41.4705882352942 14.4640522671571 40.6993463848039 L 7.99346403186269 34.2287581495094 C 7.62304306723877 33.862831504984 7.41456073994789 33.3638233409525 7.41456073994789 32.8431372549019 C 7.41456073994789 32.3224511688514 7.62304306723877 31.8234430048199 7.99346403186269 31.4575163602944 L 14.4640522671571 24.986928125 C 14.8299789140145 24.6165071414319 15.3289870912354 24.4080248020717 15.8496731924017 24.4080248020717 C 16.3703592935681 24.4080248020717 16.8693674707896 24.6165071414319 17.2352941176471 24.986928125 C 18.0065359681371 25.7581699754903 18.0065359681371 27.0000000000002 17.2352941176471 27.7581699142155 L 12.1503267769606 32.8431372549019 Z M 27.169934620098 22.6078431372551 C 28.2287581495098 22.8431372549019 28.9084966911768 23.8758169730394 28.6862745098043 24.9346405024511 L 25.1568627450985 41.5620914828432 C 24.9346405024511 42.6209150122551 23.8888889093139 43.3006535539215 22.830065379902 43.0784313725491 C 21.7712418504902 42.856209129902 21.0915033088233 41.8104575367644 21.313725490196 40.7516340073533 L 24.8431372549015 24.1241830269607 C 25.0653594975489 23.0653594975487 26.1111110906862 22.3856209558824 27.169934620098 22.6078431372551 Z M 42.0065359681373 31.4575163602944 C 42.7777778186273 32.2287582107842 42.7777778186273 33.4575163602941 42.0065359681373 34.2287581495094 L 35.5359477328429 40.6993463848039 C 34.7647058823532 41.4705882352942 33.522875857843 41.4705882352942 32.7647058823529 40.6993463848039 C 32.3942849177286 40.3334197402785 32.1858025904376 39.834411576247 32.1858025904376 39.3137254901964 C 32.1858025904376 38.7930394041453 32.3942849177286 38.2940312401138 32.7647058823529 37.9281045955884 L 37.8496732230394 32.8431372549019 L 32.7647058823529 27.7581699142155 C 32.3942849177286 27.3922432696901 32.1858025904376 26.8932351056586 32.1858025904376 26.3725490196075 C 32.1858025904376 25.8518629335569 32.3942849177286 25.3528547695254 32.7647058823529 24.986928125 C 33.1306325292104 24.6165071414319 33.6296407064319 24.4080248020717 34.1503268075983 24.4080248020717 C 34.6710129087646 24.4080248020717 35.1700210859855 24.6165071414319 35.5359477328429 24.986928125 L 42.0065359681373 31.4575163602944 Z " fill-rule="nonzero" fill="#0099ff" stroke="none" transform="matrix(1 0 0 1 1120 360 )" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
BIN
frontend/src/assets/images/menuicon/lb.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
frontend/src/assets/images/menuicon/noitem.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
frontend/src/assets/images/menuicon/off.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
frontend/src/assets/images/menuicon/off2.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
frontend/src/assets/images/menuicon/on.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
frontend/src/assets/images/menuicon/on2.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/src/assets/images/menuicon/u699.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
6
frontend/src/assets/images/menuicon/yj.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="50px" height="48px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="matrix(1 0 0 1 -680 -361 )">
|
||||||
|
<path d="M 1.98245041436462 27 L 48.0886578038674 27 C 49.0932727209945 27.0057821227101 49.9086122928177 27.8297042861297 50 28.8448940273335 L 50 46.010639232335 C 49.9086122928177 47.0263526904908 49.0937909135007 47.8497511369584 48.0886578038674 47 L 1.98245041436462 47 C 0.977317304731287 47.8497511369584 0.162495925414532 47.0263526904908 1 46.010639232335 L 1 28.8448940273335 C 0.162495925414532 27.829704216342 0.977835566298381 27.0057821227101 1.98245041436462 27 Z M 14.0458203554854 37.4277666298343 C 14.0458203554854 36.1010523503381 13.001899572824 35.0148004714154 11.6895677486189 34.9759677348066 C 11.6658918571127 34.9752671500598 11.6422070859785 34.9749168013958 11.6185210460931 34.9749168013958 C 10.2779606542408 34.9749168013958 9.19122173670075 36.0730950759626 9.19122173670075 37.4277666298343 C 9.19122173670075 38.7824381837059 10.2779606542408 39.8806164582728 11.6185210460931 39.8806164582728 C 11.6422070859785 39.8806164582728 11.6658918571127 39.8802661096087 11.6895677486189 39.8795655248618 C 13.001899572824 39.8407327882532 14.0458203554854 38.7544809093304 14.0458203554854 37.4277666298343 Z M 23.7508577173637 37.4277666298343 C 23.7508577173637 36.1010523503381 22.7069369347023 35.0148004714154 21.3946051104972 34.9759677348066 C 21.3709292189913 34.9752671500598 21.3472444478568 34.9749168013958 21.3235584079714 34.9749168013958 C 19.9829980161192 34.9749168013958 18.8962590985791 36.0730950759626 18.8962590985791 37.4277666298343 C 18.8962590985791 38.7824381837059 19.9829980161192 39.8806164582728 21.3235584079714 39.8806164582728 C 21.3472444478568 39.8806164582728 21.3709292189913 39.8802661096087 21.3946051104972 39.8795655248618 C 22.7069369347023 39.8407327882532 23.7508577173637 38.7544809093304 23.7508577173637 37.4277666298343 Z M 38.3815404696131 35.5897056586217 L 33.5290217541437 35.5897056586217 C 32.5238885360327 35.5897056586217 31.7090671567157 36.4131041050894 31.7090671567157 37.4288175632451 C 31.7090671567157 38.4445310214009 32.5238885360327 39.2679294678686 33.5290216456658 39.2679294678686 L 38.3815404696131 39.2679294678686 C 39.386673470769 39.2679294678686 40.2014948500857 38.4445310214009 40.2014948500857 37.4288175632451 C 40.2014948500857 36.4131041050894 39.386673470769 35.5897056586217 38.3815403611354 35.5897056586217 Z M 1.98245041436462 0 L 48.0886578038674 0 C 49.0932727209945 0.0328412678103973 49.9086122928177 0.856763431230036 50 1.87195317243385 L 50 19.0376983774353 C 49.9086122928177 20.0534118355911 49.0937909135007 20.8768102820587 48.0886578038674 21 L 1.98245041436462 21 C 0.977317304731287 20.8768102820587 0.162495925414532 20.0534118355911 1 19.0376983774353 L 1 1.87195317243385 C 0.162495925414532 0.85676336144229 0.977835566298381 0.0328412678103973 1.98245041436462 0 Z M 14.0458203554851 10.4548257749346 C 14.0458203554851 9.12811149543842 13.001899572824 8.04185961651569 11.6895677486189 8.00302687990694 C 11.6658918571127 8.00232629516012 11.6422070859785 8.00197594649609 11.6185210460931 8.00197594649609 C 10.2779606542408 8.00197594649609 9.19122173670053 9.10015422106289 9.19122173670053 10.4548257749346 C 9.19122173670053 11.8094973288062 10.2779606542408 12.9076756033731 11.6185210460931 12.9076756033731 C 11.6422070859785 12.9076756033731 11.6658918571127 12.9073252547091 11.6895677486189 12.9066246699622 C 13.001899572824 12.8677919333535 14.0458203554851 11.7815400544307 14.0458203554851 10.4548257749346 Z M 23.7508577173637 10.4548257749346 C 23.7508577173637 9.12811149543842 22.7069369347023 8.04185961651569 21.3946051104972 8.00302687990694 C 21.370929218991 8.00232629516012 21.3472444478568 8.00197594649609 21.3235584079714 8.00197594649609 C 19.9829980161192 8.00197594649609 18.8962590985791 9.10015422106289 18.8962590985791 10.4548257749346 C 18.8962590985791 11.8094973288062 19.9829980161192 12.9076756033731 21.3235584079714 12.9076756033731 C 21.3472444478568 12.9076756033731 21.370929218991 12.9073252547091 21.3946051104972 12.9066246699622 C 22.7069369347023 12.8677919333535 23.7508577173637 11.7815400544307 23.7508577173637 10.4548257749346 Z M 38.3815404696131 8.61466293690029 L 33.5290217541437 8.61466293690029 C 32.5238886254397 8.61466293690029 31.7090672306629 9.43806139899044 31.7090672306629 10.4537748764176 C 31.7090672306629 11.4694883538447 32.5238886254397 12.2928868159349 33.5290217541437 12.2928868159349 L 38.3815404696131 12.2928868159349 C 39.3866735983172 12.2928868159349 40.201494993094 11.4694883538447 40.201494993094 10.4537748764176 C 40.201494993094 9.43806139899044 39.3866735983172 8.61466293690029 38.3815404696131 8.61466293690029 Z " fill-rule="nonzero" fill="#0099ff" stroke="none" transform="matrix(1 0 0 1 680 361 )" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
38
frontend/src/layout/AppLayout.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import HeaderView from './components/header.vue'
|
||||||
|
import SidebarView from './components/sidebar.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="app-layout">
|
||||||
|
<header class="app-layout__header">
|
||||||
|
<HeaderView />
|
||||||
|
</header>
|
||||||
|
<main class="app-layout__main">
|
||||||
|
<SidebarView />
|
||||||
|
<div class="app-layout__main-content">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.app-layout {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
.app-layout__header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.app-layout__main {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 60px);
|
||||||
|
background-color: #F2F4F9;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.app-layout__main-content {
|
||||||
|
width: calc(100% - 240px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
147
frontend/src/layout/components/header.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { fetchDeviceStatus } from '@/api/platform'
|
||||||
|
const time = ref('')
|
||||||
|
const date = ref('')
|
||||||
|
const day = ref('')
|
||||||
|
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||||||
|
const updateTime = () => {
|
||||||
|
const now = new Date()
|
||||||
|
const hours = now.getHours().toString().padStart(2, '0')
|
||||||
|
const minutes = now.getMinutes().toString().padStart(2, '0')
|
||||||
|
const seconds = now.getSeconds().toString().padStart(2, '0')
|
||||||
|
const year = now.getFullYear()
|
||||||
|
const month = (now.getMonth() + 1).toString().padStart(2, '0')
|
||||||
|
const dayOfMonth = now.getDate().toString().padStart(2, '0')
|
||||||
|
|
||||||
|
time.value = `${hours}:${minutes}:${seconds}`
|
||||||
|
date.value = `${year}-${month}-${dayOfMonth}`
|
||||||
|
day.value = weekDays[now.getDay()]
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer: ReturnType<typeof setInterval>
|
||||||
|
const centerItems = ref([
|
||||||
|
{
|
||||||
|
label: '装置自检状态',
|
||||||
|
status: '--',
|
||||||
|
code: 'self-check'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '网络1状态',
|
||||||
|
status: '--',
|
||||||
|
code: 'network1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '网络2状态',
|
||||||
|
status: '--',
|
||||||
|
code: 'network2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '串口1状态',
|
||||||
|
status: '--',
|
||||||
|
code: 'serial1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '串口2状态',
|
||||||
|
status: '--',
|
||||||
|
code: 'serial2'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
async function refreshStatus() {
|
||||||
|
try {
|
||||||
|
const status:any = await fetchDeviceStatus()
|
||||||
|
centerItems.value.forEach(item => {
|
||||||
|
item.status = status[item.code] || '正常'
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateTime()
|
||||||
|
timer = setInterval(updateTime, 1000)
|
||||||
|
refreshStatus()
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="header-container">
|
||||||
|
<div class="header-container-left">
|
||||||
|
<img src="@/assets/images/dashboard/logo.svg" alt="">
|
||||||
|
<div style="margin-left: 10px;">电气量测控平台</div>
|
||||||
|
</div>
|
||||||
|
<div class="header-container-center">
|
||||||
|
<div v-for="item in centerItems" :key="item.label">
|
||||||
|
<div>{{ item.label }}:<span style="font-weight: 700;" :style="{ color: item.status === '正常' ? '#009933' : '#FF0000' }">{{ item.status }}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="header-container-right">
|
||||||
|
<div class="header-container-right-time">
|
||||||
|
<div class="header-container-right-time1">{{ time }}</div>
|
||||||
|
<div class="header-container-right-time2">{{ date }} {{ day }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.header-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 5px 5px 0px 0px;
|
||||||
|
color: rgb(31, 41, 55);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 30px;
|
||||||
|
gap: 20px;
|
||||||
|
box-shadow: 0px 2px 8px rgb(219, 225, 236);
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #1F2937;
|
||||||
|
width: 210px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: calc(100% - 300px);
|
||||||
|
gap: 40px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #505050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-right-time {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-right-time1 {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-right-time2 {
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
275
frontend/src/layout/components/sidebar.vue
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import icon1 from '@/assets/images/menuicon/1.png'
|
||||||
|
import icon1_1 from '@/assets/images/menuicon/1-1.png'
|
||||||
|
import icon2 from '@/assets/images/menuicon/2.png'
|
||||||
|
import icon2_1 from '@/assets/images/menuicon/2-1.png'
|
||||||
|
import icon3 from '@/assets/images/menuicon/3.png'
|
||||||
|
import icon3_1 from '@/assets/images/menuicon/3-1.png'
|
||||||
|
import icon4 from '@/assets/images/menuicon/4.png'
|
||||||
|
import icon4_1 from '@/assets/images/menuicon/4-1.png'
|
||||||
|
import icon5 from '@/assets/images/menuicon/5.png'
|
||||||
|
import icon5_1 from '@/assets/images/menuicon/5-1.png'
|
||||||
|
import icon6 from '@/assets/images/menuicon/6.png'
|
||||||
|
import icon6_1 from '@/assets/images/menuicon/6-1.png'
|
||||||
|
import icon7 from '@/assets/images/menuicon/7.png'
|
||||||
|
import icon7_1 from '@/assets/images/menuicon/7-1.png'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const menuList = ref([
|
||||||
|
{
|
||||||
|
index: '1',
|
||||||
|
title: '显示信息',
|
||||||
|
icon: icon1,
|
||||||
|
iconHover: icon1_1,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: '1-1',
|
||||||
|
title: '模拟量',
|
||||||
|
path: '/analogQuantity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '1-2',
|
||||||
|
title: '开关量',
|
||||||
|
path: '/switchQuantity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '1-3',
|
||||||
|
title: 'AI采集',
|
||||||
|
path: '/aiQuantity',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '2',
|
||||||
|
title: '事件报告',
|
||||||
|
icon: icon2,
|
||||||
|
iconHover: icon2_1,
|
||||||
|
path:'/eventReport',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '3',
|
||||||
|
title: '通讯设置',
|
||||||
|
icon: icon3,
|
||||||
|
iconHover: icon3_1,
|
||||||
|
path:'/communicationSetting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '4',
|
||||||
|
title: '装置设置',
|
||||||
|
icon: icon4,
|
||||||
|
iconHover: icon4_1,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: '4-1',
|
||||||
|
title: 'AI通道设置',
|
||||||
|
path: '/aiChannelSetting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '4-2',
|
||||||
|
title: 'AO通道设置',
|
||||||
|
path: '/aoChannelSetting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '4-3',
|
||||||
|
title: '密码设置',
|
||||||
|
path: '/passwordSetting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '4-4',
|
||||||
|
title: '对时设置',
|
||||||
|
path: '/timeSetting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '4-5',
|
||||||
|
title: '灯光设置',
|
||||||
|
path: '/lightSetting',
|
||||||
|
},
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// index: '5',
|
||||||
|
// title: '录波设置',
|
||||||
|
// icon: icon5,
|
||||||
|
// iconHover: icon5_1,
|
||||||
|
// path:'/recordSetting',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
index: '6',
|
||||||
|
title: '定值设置',
|
||||||
|
icon: icon6,
|
||||||
|
iconHover: icon6_1,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: '6-1',
|
||||||
|
title: '线路定值设置',
|
||||||
|
path: '/lineSetting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '6-2',
|
||||||
|
title: 'AI报警设置',
|
||||||
|
path: '/aiAlarmSetting',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '7',
|
||||||
|
title: '装置信息',
|
||||||
|
icon: icon7,
|
||||||
|
iconHover: icon7_1,
|
||||||
|
path:'/deviceInfo',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const handleClick = (path: any) => {
|
||||||
|
if (path === route.path || path == '') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isActive = (path: any) => {
|
||||||
|
return route.path === path
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-container">
|
||||||
|
<el-menu unique-opened>
|
||||||
|
<template v-for="menu in menuList" :key="menu.index">
|
||||||
|
<el-sub-menu v-if="menu.children" :index="menu.index">
|
||||||
|
<template #title>
|
||||||
|
<div class="menu-icon-wrapper">
|
||||||
|
<img :src="menu.icon" class="menu-icon" />
|
||||||
|
<img :src="menu.iconHover" class="menu-icon-hover" />
|
||||||
|
</div>
|
||||||
|
<span>{{ menu.title }}</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item
|
||||||
|
class="child-item"
|
||||||
|
v-for="child in menu.children"
|
||||||
|
:key="child.index"
|
||||||
|
:index="child.index"
|
||||||
|
@click="handleClick(child.path)"
|
||||||
|
:class="{ active: isActive(child.path) }"
|
||||||
|
>
|
||||||
|
{{ child.title }}
|
||||||
|
</el-menu-item>
|
||||||
|
</el-sub-menu>
|
||||||
|
<el-menu-item
|
||||||
|
v-else
|
||||||
|
:index="menu.index"
|
||||||
|
@click="handleClick(menu.path)"
|
||||||
|
:class="{ active: isActive(menu.path) }"
|
||||||
|
>
|
||||||
|
<div class="menu-icon-wrapper">
|
||||||
|
<img :src="menu.icon" class="menu-icon" />
|
||||||
|
<img :src="menu.iconHover" class="menu-icon-hover" />
|
||||||
|
</div>
|
||||||
|
<span>{{ menu.title }}</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</template>
|
||||||
|
</el-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.sidebar-container {
|
||||||
|
width: 240px;
|
||||||
|
height: calc(100vh - 60px);
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(229, 231, 235, 1);
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
border-bottom: none;
|
||||||
|
box-shadow: 2px 0px 8px rgba(219, 225, 236, 1);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu) {
|
||||||
|
background: transparent;
|
||||||
|
border-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-sub-menu__icon-arrow) {
|
||||||
|
transform: rotate(-90deg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-sub-menu.is-opened > .el-sub-menu__title .el-sub-menu__icon-arrow) {
|
||||||
|
transform: rotate(0deg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon,
|
||||||
|
.menu-icon-hover {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transition: opacity 0s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon-hover {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item:hover) .menu-icon-hover,
|
||||||
|
:deep(.el-sub-menu__title:hover) .menu-icon-hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item:hover) .menu-icon,
|
||||||
|
:deep(.el-sub-menu__title:hover) .menu-icon {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
:deep(.el-menu-item){
|
||||||
|
color: #505050 !important;
|
||||||
|
}
|
||||||
|
:deep(.el-sub-menu__title){
|
||||||
|
color: #505050 !important;
|
||||||
|
}
|
||||||
|
:deep(.el-menu-item){
|
||||||
|
padding-left: 30px !important;
|
||||||
|
}
|
||||||
|
:deep(.el-sub-menu__title){
|
||||||
|
padding-left: 30px !important;
|
||||||
|
}
|
||||||
|
:deep(.child-item){
|
||||||
|
padding-left: 60px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item:hover),
|
||||||
|
:deep(.el-sub-menu__title:hover) {
|
||||||
|
background-color: transparent !important;
|
||||||
|
color: #0099ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item.active) {
|
||||||
|
background-color: rgba(0, 153, 255, 0.047) !important;
|
||||||
|
color: #0099ff !important;
|
||||||
|
border-right: 3px solid #0099ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item.active) .menu-icon-hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item.active) .menu-icon {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,5 +1,14 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './routes'
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
app.use(router)
|
||||||
|
app.use(ElementPlus as any, {
|
||||||
|
locale: zhCn,
|
||||||
|
})
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
252
frontend/src/routes/index.ts
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import {
|
||||||
|
createRouter,
|
||||||
|
createWebHistory,
|
||||||
|
type RouteRecordRaw,
|
||||||
|
} from "vue-router"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由元信息类型
|
||||||
|
*/
|
||||||
|
declare module "vue-router" {
|
||||||
|
interface RouteMeta {
|
||||||
|
title?: string
|
||||||
|
requiresAuth?: boolean
|
||||||
|
hidden?: boolean
|
||||||
|
keepAlive?: boolean
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由表
|
||||||
|
*/
|
||||||
|
// src/routes/index.ts
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
// {
|
||||||
|
// path: "/login",
|
||||||
|
// name: "Login",
|
||||||
|
// component: () => import("@/views/login/index.vue"),
|
||||||
|
// meta: {
|
||||||
|
// title: "登录",
|
||||||
|
// hidden: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
path: "/dashboard",
|
||||||
|
name: "Dashboard",
|
||||||
|
component: () => import("@/views/dashboard/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "系统总览",
|
||||||
|
requiresAuth: false,
|
||||||
|
icon: "dashboard",
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
component: () => import("@/layout/AppLayout.vue"),
|
||||||
|
meta: {
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
redirect: "/dashboard",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "analogQuantity",
|
||||||
|
name: "AnalogQuantity",
|
||||||
|
component: () => import("@/views/analogQuantity/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "模拟量",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "switchQuantity",
|
||||||
|
name: "SwitchQuantity",
|
||||||
|
component: () => import("@/views/switchQuantity/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "开关量",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "aiQuantity",
|
||||||
|
name: "AiQuantity",
|
||||||
|
component: () => import("@/views/aiQuantity/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "AI采集",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "eventReport",
|
||||||
|
name: "EventReport",
|
||||||
|
component: () => import("@/views/eventReport/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "事件报告",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "communicationSetting",
|
||||||
|
name: "CommunicationSetting",
|
||||||
|
component: () => import("@/views/communicationSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "通信设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "aiChannelSetting",
|
||||||
|
name: "AiChannelSetting",
|
||||||
|
component: () => import("@/views/aiChannelSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "AI通道设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "aoChannelSetting",
|
||||||
|
name: "AoChannelSetting",
|
||||||
|
component: () => import("@/views/aoChannelSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "AO通道设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "timeSetting",
|
||||||
|
name: "TimeSetting",
|
||||||
|
component: () => import("@/views/timeSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "对时设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "passwordSetting",
|
||||||
|
name: "PasswordSetting",
|
||||||
|
component: () => import("@/views/passwordSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "密码设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "lightSetting",
|
||||||
|
name: "LightSetting",
|
||||||
|
component: () => import("@/views/lightSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "灯光设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "recordSetting",
|
||||||
|
name: "RecordSetting",
|
||||||
|
component: () => import("@/views/recordSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "录波功能",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "lineSetting",
|
||||||
|
name: "LineSetting",
|
||||||
|
component: () => import("@/views/lineSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "线路定值设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "aiAlarmSetting",
|
||||||
|
name: "AiAlarmSetting",
|
||||||
|
component: () => import("@/views/aiAlarmSetting/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "AI报警设置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "deviceInfo",
|
||||||
|
name: "DeviceInfo",
|
||||||
|
component: () => import("@/views/deviceInfo/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "装置信息",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: "/404",
|
||||||
|
// name: "NotFoundPage",
|
||||||
|
// component: () => import("../views/404/index.vue"),
|
||||||
|
// meta: {
|
||||||
|
// title: "404",
|
||||||
|
// hidden: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
path: "/:pathMatch(.*)*",
|
||||||
|
redirect: "/404",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建路由实例
|
||||||
|
* createWebHistory() 是 Vue Router 4 推荐的 history 模式写法
|
||||||
|
*/
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
routes,
|
||||||
|
scrollBehavior() {
|
||||||
|
return {
|
||||||
|
top: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 白名单页面
|
||||||
|
*/
|
||||||
|
const WHITE_LIST = ["/dashboard", "/404"]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 token
|
||||||
|
* 这里先写成本地存储,你后面可以替换成 Pinia / cookies / 你项目自己的 auth 方案
|
||||||
|
*/
|
||||||
|
function getToken() {
|
||||||
|
return localStorage.getItem("token")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局前置守卫
|
||||||
|
*/
|
||||||
|
router.beforeEach((to, _from, next) => {
|
||||||
|
const token = getToken()
|
||||||
|
|
||||||
|
// 设置页面标题
|
||||||
|
if (to.meta?.title) {
|
||||||
|
document.title = `${to.meta.title} - 电气量测控平台`
|
||||||
|
} else {
|
||||||
|
document.title = "电气量测控平台"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不需要登录的页面直接放行
|
||||||
|
if (WHITE_LIST.includes(to.path)) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要登录但没 token
|
||||||
|
if (to.meta?.requiresAuth && !token) {
|
||||||
|
next({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
redirect: to.fullPath,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局后置守卫
|
||||||
|
*/
|
||||||
|
router.afterEach(() => {
|
||||||
|
// 这里后面可以接入进度条、埋点、页面访问日志之类
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
193
frontend/src/views/aiAlarmSetting/index.vue
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const channelList = ref([
|
||||||
|
{ no: 1, type: '4~20mA', line: '开出一', down: 1, up: 8,delay:8,isInput:'0' },
|
||||||
|
{ no: 2, type: '1~5V', line: '开出一', down: 1, up: 8,delay:8,isInput:'0' },
|
||||||
|
{ no: 3, type: '4~20mA', line: '开出一', down: 1, up: 8,delay:8,isInput:'0' },
|
||||||
|
{ no: 4, type: '1~5V', line: '开出一', down: 1, up: 8,delay:8,isInput:'0' },
|
||||||
|
{ no: 5, type: '4~20mA', line: '开出一', down: 1, up: 8,delay:8,isInput:'0' },
|
||||||
|
{ no: 6, type: '1~5V', line: '开出一', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
{ no: 7, type: '4~20mA', line: '开出一', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
{ no: 8, type: '1~5V', line: '开出一', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
{ no: 9, type: '1~5V', line: '开出二', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
{ no: 10, type: '4~20mA', line: '开出二', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
{ no: 11, type: '4~20mA', line: '开出二', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
{ no: 12, type: '4~20mA', line: '开出二', down: 1, up: 8,delay:8,isInput:'1' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const nodeOptions = ref(['开出一', '开出二'])
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
console.log('保存AI通道设置', channelList.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="ai-alarm-container">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="row header-row">
|
||||||
|
<div class="cell cell-no">通道号</div>
|
||||||
|
<div class="cell cell-type" style="color: #787878;">信号类型</div>
|
||||||
|
<div class="cell cell-line">下限值</div>
|
||||||
|
<div class="cell cell-category">上限值</div>
|
||||||
|
<div class="cell cell-low">延时(s)</div>
|
||||||
|
<div class="cell cell-high">输出节点</div>
|
||||||
|
<div class="cell cell-high">是否投入</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" v-for="(item, idx) in channelList" :key="idx">
|
||||||
|
<div class="cell cell-no">{{ item.no }}</div>
|
||||||
|
<div class="cell cell-type" style="background: #ffffff;">{{ item.type }}</div>
|
||||||
|
<div class="cell cell-line" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.down" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-low" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.up" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-high" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.delay" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-line" style="background: #ffffff;">
|
||||||
|
<el-select v-model="item.line">
|
||||||
|
<el-option v-for="line in nodeOptions" :key="line" :value="line" :label="line"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-category" style="background: #ffffff;">
|
||||||
|
<el-switch v-model="item.isInput" active-value="1" inactive-value="0" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-wrap">
|
||||||
|
<button class="save-btn" @click="handleSave">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ai-alarm-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-row .cell {
|
||||||
|
background-color: #f9fafe;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 37px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f9fafe;
|
||||||
|
color: #787878;
|
||||||
|
|
||||||
|
&+.cell {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-no {
|
||||||
|
width: 100px;
|
||||||
|
background: #f9fafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-type {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-line {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-category {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-low {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-high {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
input {
|
||||||
|
width: 85%;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
text-align: center;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrap {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background-color: #0099ff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 150px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去掉所有边框 + 阴影
|
||||||
|
:deep(.el-select__wrapper),
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select__wrapper.is-hover),
|
||||||
|
:deep(.el-input__wrapper.is-hover),
|
||||||
|
:deep(.el-select__wrapper.is-focused),
|
||||||
|
:deep(.el-input__wrapper.is-focused) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
194
frontend/src/views/aiChannelSetting/index.vue
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const channelList = ref([
|
||||||
|
{ no: 1, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 2, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 3, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 4, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 5, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 6, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 7, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 8, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 9, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 10, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 11, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 12, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
])
|
||||||
|
|
||||||
|
const lineOptions = ref(['线路一', '线路二'])
|
||||||
|
const categoryOptions = ref(['UA', 'UB', 'UC'])
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
console.log('保存AI通道设置', channelList.value)
|
||||||
|
}
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="ai-channel-container">
|
||||||
|
<div class="title">AI通道设置</div>
|
||||||
|
<div class="panel">
|
||||||
|
<div class="row header-row">
|
||||||
|
<div class="cell cell-no">通道号</div>
|
||||||
|
<div class="cell cell-type" style="color: #787878;">信号类型</div>
|
||||||
|
<div class="cell cell-line">所属线路</div>
|
||||||
|
<div class="cell cell-category">类别</div>
|
||||||
|
<div class="cell cell-low">低端值</div>
|
||||||
|
<div class="cell cell-high">高端值</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" v-for="(item, idx) in channelList" :key="idx">
|
||||||
|
<div class="cell cell-no">{{ item.no }}</div>
|
||||||
|
<div class="cell cell-type" style="background: #ffffff;">{{ item.type }}</div>
|
||||||
|
<div class="cell cell-line" style="background: #ffffff;">
|
||||||
|
<el-select v-model="item.line">
|
||||||
|
<el-option v-for="line in lineOptions" :key="line" :value="line" :label="line"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-category" style="background: #ffffff;">
|
||||||
|
<el-select v-model="item.category">
|
||||||
|
<el-option v-for="cat in categoryOptions" :key="cat" :value="cat" :label="cat"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-low" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.low" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-high" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.high" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-wrap">
|
||||||
|
<button class="save-btn" @click="handleSave">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ai-channel-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-row .cell {
|
||||||
|
background-color: #f9fafe;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 37px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f9fafe;
|
||||||
|
color: #787878;
|
||||||
|
& + .cell {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cell-no {
|
||||||
|
width: 100px;
|
||||||
|
background: #f9fafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-type {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-line {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-category {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-low {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-high {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
input {
|
||||||
|
width: 85%;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
text-align: center;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrap {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background-color: #0099ff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 150px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 去掉所有边框 + 阴影
|
||||||
|
:deep(.el-select__wrapper),
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
:deep(.el-select__wrapper.is-hover),
|
||||||
|
:deep(.el-input__wrapper.is-hover),
|
||||||
|
:deep(.el-select__wrapper.is-focused),
|
||||||
|
:deep(.el-input__wrapper.is-focused) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
:deep(.el-input__inner){
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
207
frontend/src/views/aiQuantity/index.vue
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted } from 'vue'
|
||||||
|
import { usePlatformStore } from '../../stores/platform'
|
||||||
|
|
||||||
|
const { state, bootstrap } = usePlatformStore()
|
||||||
|
|
||||||
|
const aiData = computed(() => {
|
||||||
|
if (!state.realtime?.ai_collect) {
|
||||||
|
return Array.from({ length: 12 }, (_, i) => ({
|
||||||
|
channel: i + 1,
|
||||||
|
rawValue: '--',
|
||||||
|
line: '线路一',
|
||||||
|
category: '--',
|
||||||
|
lowValue1: 0,
|
||||||
|
lowValue2: 0,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from({ length: 12 }, (_, i) => {
|
||||||
|
const channel = i + 1
|
||||||
|
const key = `ai_${channel}`
|
||||||
|
const rawValue = state.realtime!.ai_collect[key]
|
||||||
|
|
||||||
|
let category = '--'
|
||||||
|
let rawDisplay = '--'
|
||||||
|
|
||||||
|
if (rawValue !== undefined) {
|
||||||
|
if (channel % 2 === 1) {
|
||||||
|
const voltage = (rawValue / 1000).toFixed(3)
|
||||||
|
category = `UA: ${voltage}V`
|
||||||
|
rawDisplay = `${rawValue}mV`
|
||||||
|
} else {
|
||||||
|
const freq = ((rawValue / 1000) * 50).toFixed(4)
|
||||||
|
category = `f: ${freq}Hz`
|
||||||
|
rawDisplay = `${rawValue}mA`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
channel,
|
||||||
|
rawValue: rawDisplay,
|
||||||
|
line: '线路一',
|
||||||
|
category,
|
||||||
|
lowValue1: 1,
|
||||||
|
lowValue2: 8,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = computed(() => {
|
||||||
|
return state.alarms.slice(0, 10).map((alarm, index) => ({
|
||||||
|
name: '线路一',
|
||||||
|
typeName: alarm.type,
|
||||||
|
content: alarm.content,
|
||||||
|
time: alarm.time,
|
||||||
|
ts: '0',
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void bootstrap()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="ai-container">
|
||||||
|
<div class="ai-container-top">
|
||||||
|
<div class="top-title">AI采集信息</div>
|
||||||
|
<div class="container-top-box">
|
||||||
|
<!-- 表格表头 -->
|
||||||
|
<div class="table-header">
|
||||||
|
<div class="header-cell" style="width: 90px;max-width: 90px;min-width: 90px;">通道号</div>
|
||||||
|
<div class="header-cell">原始值</div>
|
||||||
|
<div class="header-cell">所属路线</div>
|
||||||
|
<div class="header-cell">类别</div>
|
||||||
|
<div class="header-cell">低值</div>
|
||||||
|
<div class="header-cell">低值</div>
|
||||||
|
</div>
|
||||||
|
<!-- 表格内容 -->
|
||||||
|
<div class="table-body">
|
||||||
|
<div v-for="(item, index) in aiData" :key="index" class="table-row">
|
||||||
|
<div class="cell" style="width: 90px;max-width: 90px;min-width: 90px;color: #787878;">{{ item.channel }}</div>
|
||||||
|
<div class="cell">{{ item.rawValue }}</div>
|
||||||
|
<div class="cell">{{ item.line }}</div>
|
||||||
|
<div class="cell">{{ item.category }}</div>
|
||||||
|
<div class="cell">{{ item.lowValue1 }}</div>
|
||||||
|
<div class="cell">{{ item.lowValue2 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ai-container-bottom">
|
||||||
|
<div class="top-title">报警信息</div>
|
||||||
|
<div class="container-bottom-box">
|
||||||
|
<div v-for="(item, index) in tableData" :key="index" class="container-bottom-box-line1">
|
||||||
|
<div style="width: 8%; text-align: center;">{{ index + 1 }}</div>
|
||||||
|
<div style="width: 23%; text-align: center;">{{ item.name }}</div>
|
||||||
|
<div style="width: 15%; text-align: center;">{{ item.typeName }}</div>
|
||||||
|
<div style="width: 25%; text-align: center;">{{ item.content }}</div>
|
||||||
|
<div style="width: 20%; text-align: center;">{{ item.time }}</div>
|
||||||
|
<div style="width: 10%; text-align: center;">{{ item.ts }}ms</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ai-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.top-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-container-top {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
.container-top-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 460px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.header-cell {
|
||||||
|
flex: 1;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
background-color: #f8f9fc;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
.table-row {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
flex: 1;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-container-bottom {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.container-bottom-box {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 660px);
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.container-bottom-box-line1 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #787878;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #505050;
|
||||||
|
border-bottom: 1px solid #f2f2f2;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
349
frontend/src/views/analogQuantity/index.vue
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { usePlatformStore } from '../../stores/platform'
|
||||||
|
import type { ValueGroup } from '../../types/platform'
|
||||||
|
const { state, bootstrap } = usePlatformStore()
|
||||||
|
|
||||||
|
const lineList = ref([
|
||||||
|
{ name: '线路一', lineNo: 1 },
|
||||||
|
{ name: '线路二', lineNo: 2 },
|
||||||
|
{ name: '线路三', lineNo: 3 },
|
||||||
|
{ name: '线路四', lineNo: 4 },
|
||||||
|
])
|
||||||
|
const checkedLine = ref('线路一')
|
||||||
|
const valList = ref([
|
||||||
|
{ name: '一次值', key: 'pri_val' },
|
||||||
|
{ name: '二次值', key: 'sec_val' },
|
||||||
|
])
|
||||||
|
const checkedVal = ref('一次值')
|
||||||
|
|
||||||
|
const currentLineData = computed(() => {
|
||||||
|
console.log(state)
|
||||||
|
if (!state.realtime) return null
|
||||||
|
const line = lineList.value.find(l => l.name === checkedLine.value)
|
||||||
|
if (!line) return null
|
||||||
|
return state.realtime.line_list.find(l => l.line_no === line.lineNo)
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentValues = computed<ValueGroup | null>(() => {
|
||||||
|
if (!currentLineData.value) return null
|
||||||
|
const valKey = valList.value.find(v => v.name === checkedVal.value)?.key
|
||||||
|
return currentLineData.value[valKey as keyof typeof currentLineData.value] as ValueGroup || null
|
||||||
|
})
|
||||||
|
|
||||||
|
const frequency = computed(() => {
|
||||||
|
if (!currentValues.value) return '50.000'
|
||||||
|
return currentValues.value.frq.toFixed(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
const sqrt3 = Math.sqrt(3)
|
||||||
|
|
||||||
|
const infoList = computed(() => {
|
||||||
|
const values = currentValues.value
|
||||||
|
if (!values) {
|
||||||
|
return [
|
||||||
|
{ name: '单项电压(V)', a: '--', b: '--', c: '--', total: '--' },
|
||||||
|
{ name: '单相电流(A)', a: '--', b: '--', c: '--', total: '--' },
|
||||||
|
{ name: '有功功率(kw)', a: '--', b: '--', c: '--', total: '--' },
|
||||||
|
{ name: '无功功率(kvar)', a: '--', b: '--', c: '--', total: '--' },
|
||||||
|
{ name: '视在功率(kVA)', a: '--', b: '--', c: '--', total: '--' },
|
||||||
|
{ name: '功率因数(cos)', a: '--', b: '--', c: '--', total: '--' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: '单项电压(V)',
|
||||||
|
a: values.Ua.toFixed(3),
|
||||||
|
b: values.Ub.toFixed(3),
|
||||||
|
c: values.Uc.toFixed(3),
|
||||||
|
total: ((values.Ua + values.Ub + values.Uc) / 3).toFixed(3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '单相电流(A)',
|
||||||
|
a: values.Ia.toFixed(3),
|
||||||
|
b: values.Ib.toFixed(3),
|
||||||
|
c: values.Ic.toFixed(3),
|
||||||
|
total: ((values.Ia + values.Ib + values.Ic) / 3).toFixed(3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '有功功率(kw)',
|
||||||
|
a: ((values.Ua * values.Ia * 0.9) / 1000).toFixed(3),
|
||||||
|
b: ((values.Ub * values.Ib * 0.9) / 1000).toFixed(3),
|
||||||
|
c: ((values.Uc * values.Ic * 0.9) / 1000).toFixed(3),
|
||||||
|
total: (values.Pt / 1000).toFixed(3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '无功功率(kvar)',
|
||||||
|
a: ((values.Ua * values.Ia * Math.sin(Math.acos(0.9))) / 1000).toFixed(3),
|
||||||
|
b: ((values.Ub * values.Ib * Math.sin(Math.acos(0.9))) / 1000).toFixed(3),
|
||||||
|
c: ((values.Uc * values.Ic * Math.sin(Math.acos(0.9))) / 1000).toFixed(3),
|
||||||
|
total: ((sqrt3 * values.Ua * values.Ia * Math.sin(Math.acos(0.9))) / 1000).toFixed(3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '视在功率(kVA)',
|
||||||
|
a: ((values.Ua * values.Ia) / 1000).toFixed(3),
|
||||||
|
b: ((values.Ub * values.Ib) / 1000).toFixed(3),
|
||||||
|
c: ((values.Uc * values.Ic) / 1000).toFixed(3),
|
||||||
|
total: ((sqrt3 * values.Ua * values.Ia) / 1000).toFixed(3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '功率因数(cos)',
|
||||||
|
a: '0.900',
|
||||||
|
b: '0.900',
|
||||||
|
c: '0.900',
|
||||||
|
total: '0.900',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const infoData = computed(() => {
|
||||||
|
const values = currentValues.value
|
||||||
|
if (!values) {
|
||||||
|
return { name: '线电压(V)', a: '--', b: '--', c: '--' }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: '线电压(V)',
|
||||||
|
a: (values.Ua * sqrt3).toFixed(3),
|
||||||
|
b: (values.Ub * sqrt3).toFixed(3),
|
||||||
|
c: (values.Uc * sqrt3).toFixed(3),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = computed(() => {
|
||||||
|
return state.alarms.slice(0, 10).map((alarm, index) => ({
|
||||||
|
name: `线路${currentLineData.value?.line_no || 1}`,
|
||||||
|
typeName: alarm.type,
|
||||||
|
content: alarm.content,
|
||||||
|
time: alarm.time,
|
||||||
|
ts: '0',
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClick(name: string) {
|
||||||
|
checkedLine.value = name
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClicks(name: string) {
|
||||||
|
checkedVal.value = name
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
checkedLine.value = lineList.value[0].name
|
||||||
|
checkedVal.value = valList.value[0].name
|
||||||
|
bootstrap()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="analog-quantity-container">
|
||||||
|
<div class="analog-quantity-container-top">
|
||||||
|
<div class="top-title">实时信息</div>
|
||||||
|
<div class="container-top-box">
|
||||||
|
<div class="container-top-box-nav">
|
||||||
|
<div class="nav-box">
|
||||||
|
<div class="nav-box-line">
|
||||||
|
<div v-for="item in lineList" :key="item.name"
|
||||||
|
:class="{ 'nav-box-line-item-checked': item.name === checkedLine }" class="nav-box-line-item"
|
||||||
|
@click="handleClick(item.name)">
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="nav-box-line">
|
||||||
|
<div v-for="item in valList" :key="item.name" style="padding: 0px 13px;"
|
||||||
|
:class="{ 'nav-box-line-item-checked': item.name === checkedVal }" class="nav-box-line-item"
|
||||||
|
@click="handleClicks(item.name)">
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="nav-right">
|
||||||
|
<span>频率:<span class="nav-right-value">{{ frequency }} HZ</span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-top-box-content">
|
||||||
|
<div class="container-top-box-content-top">
|
||||||
|
<div class="container-top-box-content-line1">
|
||||||
|
<div class="table-line1-item" style="border: none;background-color: transparent;">
|
||||||
|
<img src="@/assets/images/menuicon/noitem.png" style="width: 100%;height: 100%;" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="table-line1-item">A相</div>
|
||||||
|
<div class="table-line1-item">B相</div>
|
||||||
|
<div class="table-line1-item">C相</div>
|
||||||
|
<div class="table-line1-item">三相总</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="item in infoList" :key="item.name" class="container-top-box-content-line1">
|
||||||
|
<div class="table-line1-item" style="background-color: #f9fafe;">{{ item.name }}</div>
|
||||||
|
<div class="table-line1-item2">{{ item.a }}</div>
|
||||||
|
<div class="table-line1-item2">{{ item.b }}</div>
|
||||||
|
<div class="table-line1-item2">{{ item.c }}</div>
|
||||||
|
<div class="table-line1-item2">{{ item.total }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-top-box-content-line1">
|
||||||
|
<div class="table-line1-item" style="width:calc(20% - 4px);">线电压(V)</div>
|
||||||
|
<div style="width: 80%;display: flex; gap: 5px;">
|
||||||
|
<div class="table-line1-item">Uab</div>
|
||||||
|
<div class="table-line1-item2">{{ infoData.a }}</div>
|
||||||
|
<div class="table-line1-item">Ubc</div>
|
||||||
|
<div class="table-line1-item2">{{ infoData.b }}</div>
|
||||||
|
<div class="table-line1-item">Uca</div>
|
||||||
|
<div class="table-line1-item2">{{ infoData.c }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="analog-quantity-container-bottom">
|
||||||
|
<div class="top-title">报警信息</div>
|
||||||
|
<div class="container-bottom-box">
|
||||||
|
<div v-for="(item, index) in tableData" :key="item.name" class="container-bottom-box-line1">
|
||||||
|
<div style="width: 8%;text-align: center;">{{ index + 1 }}</div>
|
||||||
|
<div style="width: 23%;text-align: center;">{{ item.name }}</div>
|
||||||
|
<div style="width: 15%;text-align: center;">{{ item.typeName }}</div>
|
||||||
|
<div style="width: 25%;text-align: center;">{{ item.content }}</div>
|
||||||
|
<div style="width: 20%;text-align: center;">{{ item.time }}</div>
|
||||||
|
<div style="width: 10%;text-align: center;">{{ item.ts }}ms</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.analog-quantity-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.top-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analog-quantity-container-top {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
.container-top-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 458px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.container-top-box-nav {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.nav-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.nav-box-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid rgba(228, 228, 228, 1);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-box-line-item {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #505050;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0px 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-box-line-item-checked {
|
||||||
|
background-color: rgba(0, 153, 255, 1);
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-right {
|
||||||
|
font-weight: 400;
|
||||||
|
color: #363636;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
.nav-right-value {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #FF9900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-top-box-content {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.container-top-box-content-top {
|
||||||
|
.container-top-box-content-line1 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 5px;
|
||||||
|
gap: 5px;
|
||||||
|
color: #787878;
|
||||||
|
|
||||||
|
.table-line1-item {
|
||||||
|
width: 20%;
|
||||||
|
background: #f9fafe;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-line1-item2 {
|
||||||
|
width: 20%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.analog-quantity-container-bottom {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.container-bottom-box {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 660px);
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
.container-bottom-box-line1 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #787878;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #505050;
|
||||||
|
border-bottom: 1px solid #f2f2f2;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
194
frontend/src/views/aoChannelSetting/index.vue
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const channelList = ref([
|
||||||
|
{ no: 1, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 2, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 3, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 4, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 5, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 6, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 7, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 8, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 9, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 10, type: '1~5V', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 11, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
{ no: 12, type: '4~20mA', line: '线路一', category: 'UA', low: 1, high: 8 },
|
||||||
|
])
|
||||||
|
|
||||||
|
const lineOptions = ref(['线路一', '线路二'])
|
||||||
|
const categoryOptions = ref(['UA', 'UB', 'UC'])
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
console.log('保存ao通道设置', channelList.value)
|
||||||
|
}
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="ao-channel-contaoner">
|
||||||
|
<div class="title">AO通道设置</div>
|
||||||
|
<div class="panel">
|
||||||
|
<div class="row header-row">
|
||||||
|
<div class="cell cell-no">通道号</div>
|
||||||
|
<div class="cell cell-type" style="color: #787878;">信号类型</div>
|
||||||
|
<div class="cell cell-line">所属线路</div>
|
||||||
|
<div class="cell cell-category">类别</div>
|
||||||
|
<div class="cell cell-low">低端值</div>
|
||||||
|
<div class="cell cell-high">高端值</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" v-for="(item, idx) in channelList" :key="idx">
|
||||||
|
<div class="cell cell-no">{{ item.no }}</div>
|
||||||
|
<div class="cell cell-type" style="background: #ffffff;">{{ item.type }}</div>
|
||||||
|
<div class="cell cell-line" style="background: #ffffff;">
|
||||||
|
<el-select v-model="item.line">
|
||||||
|
<el-option v-for="line in lineOptions" :key="line" :value="line" :label="line"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-category" style="background: #ffffff;">
|
||||||
|
<el-select v-model="item.category">
|
||||||
|
<el-option v-for="cat in categoryOptions" :key="cat" :value="cat" :label="cat"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-low" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.low" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="cell cell-high" style="background: #ffffff;">
|
||||||
|
<el-input-number v-model="item.high" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-wrap">
|
||||||
|
<button class="save-btn" @click="handleSave">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ao-channel-contaoner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-row .cell {
|
||||||
|
background-color: #f9fafe;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 37px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f9fafe;
|
||||||
|
color: #787878;
|
||||||
|
& + .cell {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cell-no {
|
||||||
|
width: 100px;
|
||||||
|
background: #f9fafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-type {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-line {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-category {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-low {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-high {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
input {
|
||||||
|
width: 85%;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
text-align: center;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrap {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background-color: #0099ff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 150px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 去掉所有边框 + 阴影
|
||||||
|
:deep(.el-select__wrapper),
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
:deep(.el-select__wrapper.is-hover),
|
||||||
|
:deep(.el-input__wrapper.is-hover),
|
||||||
|
:deep(.el-select__wrapper.is-focused),
|
||||||
|
:deep(.el-input__wrapper.is-focused) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
:deep(.el-input__inner){
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
273
frontend/src/views/communicationSetting/index.vue
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
networkCard: '网卡二',
|
||||||
|
ipAddress: '192.168.1.56',
|
||||||
|
subnetMask: '255.255.255.255',
|
||||||
|
gateway: '192.168.1.56',
|
||||||
|
tcpProtocol: 'Modbus TCP',
|
||||||
|
// 串口设置
|
||||||
|
comPort: 'COM3',
|
||||||
|
baudRate: 115200,
|
||||||
|
parity: 'NONE',
|
||||||
|
dataBits: 8,
|
||||||
|
stopBits: 1,
|
||||||
|
rtuProtocol: 'Modbus RTU'
|
||||||
|
})
|
||||||
|
|
||||||
|
const networkCardOptions = [
|
||||||
|
{ label: '网卡一', value: '网卡一' },
|
||||||
|
{ label: '网卡二', value: '网卡二' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const tcpProtocolOptions = [
|
||||||
|
{ label: 'Modbus TCP', value: 'Modbus TCP' },
|
||||||
|
{ label: 'IEC104 ', value: 'IEC104' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const comPortOptions = [
|
||||||
|
{ label: 'COM1', value: 'COM1' },
|
||||||
|
{ label: 'COM2', value: 'COM2' },
|
||||||
|
{ label: 'COM3', value: 'COM3' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const baudRateOptions = [
|
||||||
|
{ label: '9600', value: 9600 },
|
||||||
|
{ label: '19200', value: 19200 },
|
||||||
|
{ label: '38400', value: 38400 },
|
||||||
|
{ label: '57600', value: 57600 },
|
||||||
|
{ label: '115200', value: 115200 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const parityOptions = [
|
||||||
|
{ label: 'NONE', value: 'NONE' },
|
||||||
|
{ label: 'EVEN', value: 'EVEN' },
|
||||||
|
{ label: 'ODD', value: 'ODD' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const dataBitsOptions = [
|
||||||
|
{ label: '5', value: 5 },
|
||||||
|
{ label: '6', value: 6 },
|
||||||
|
{ label: '7', value: 7 },
|
||||||
|
{ label: '8', value: 8 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const stopBitsOptions = [
|
||||||
|
{ label: '1', value: 1 },
|
||||||
|
{ label: '2', value: 2 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const rtuProtocolOptions = [
|
||||||
|
{ label: 'Modbus RTU', value: 'Modbus RTU' },
|
||||||
|
{ label: 'IEC101 ', value: 'IEC101' }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
const handleSave = () => {
|
||||||
|
ElMessageBox.prompt('请输入密码', '保存', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
// inputType: 'password',
|
||||||
|
})
|
||||||
|
.then(({ value }) => {
|
||||||
|
console.log(value)
|
||||||
|
|
||||||
|
// ElMessage({
|
||||||
|
// type: 'success',
|
||||||
|
// message: `密码正确,保存成功`,
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// ElMessage({
|
||||||
|
// type: 'info',
|
||||||
|
// message: '密码错误',
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
console.log('保存配置:', formData.value)
|
||||||
|
}
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="communication-container">
|
||||||
|
<el-form :model="formData" label-width="100px" class="form-wrapper">
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">
|
||||||
|
<div class="icon"></div>
|
||||||
|
<div>常规配置</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<el-form-item label="网卡号">
|
||||||
|
<el-select v-model="formData.networkCard" placeholder="请选择">
|
||||||
|
<el-option v-for="item in networkCardOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="IP地址">
|
||||||
|
<el-input v-model="formData.ipAddress" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<el-form-item label="子网掩码">
|
||||||
|
<el-input v-model="formData.subnetMask" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="默认网关">
|
||||||
|
<el-input v-model="formData.gateway" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<el-form-item label="通讯协议">
|
||||||
|
<el-select v-model="formData.tcpProtocol" placeholder="请选择">
|
||||||
|
<el-option v-for="item in tcpProtocolOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">
|
||||||
|
<div class="icon"></div>
|
||||||
|
<div>串口设置</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<el-form-item label="串口号">
|
||||||
|
<el-select v-model="formData.comPort" placeholder="请选择">
|
||||||
|
<el-option v-for="item in comPortOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="波特率">
|
||||||
|
<el-select v-model="formData.baudRate" placeholder="请选择">
|
||||||
|
<el-option v-for="item in baudRateOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<el-form-item label="校验位">
|
||||||
|
<el-select v-model="formData.parity" placeholder="请选择">
|
||||||
|
<el-option v-for="item in parityOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据位">
|
||||||
|
<el-select v-model="formData.dataBits" placeholder="请选择">
|
||||||
|
<el-option v-for="item in dataBitsOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<el-form-item label="停止位">
|
||||||
|
<el-select v-model="formData.stopBits" placeholder="请选择">
|
||||||
|
<el-option v-for="item in stopBitsOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="通讯协议">
|
||||||
|
<el-select v-model="formData.rtuProtocol" placeholder="请选择">
|
||||||
|
<el-option v-for="item in rtuProtocolOptions" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
<div class="btn-wrapper">
|
||||||
|
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;"
|
||||||
|
@click="handleSave">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.communication-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
.form-wrapper {
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-bottom: 120px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
.section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0 0 21px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333333;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
margin-right: 8px;
|
||||||
|
background-color: #0099ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item--label-right .el-form-item__label) {
|
||||||
|
text-align: left;
|
||||||
|
justify-content: flex-start;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select__placeholder) {
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: #363636;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select__wrapper) {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
150
frontend/src/views/dashboard/index.vue
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
|
import Sidebar from '@/layout/components/sidebar.vue'
|
||||||
|
|
||||||
|
const time = ref('')
|
||||||
|
const date = ref('')
|
||||||
|
const day = ref('')
|
||||||
|
|
||||||
|
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||||||
|
|
||||||
|
const updateTime = () => {
|
||||||
|
const now = new Date()
|
||||||
|
const hours = now.getHours().toString().padStart(2, '0')
|
||||||
|
const minutes = now.getMinutes().toString().padStart(2, '0')
|
||||||
|
const seconds = now.getSeconds().toString().padStart(2, '0')
|
||||||
|
const year = now.getFullYear()
|
||||||
|
const month = (now.getMonth() + 1).toString().padStart(2, '0')
|
||||||
|
const dayOfMonth = now.getDate().toString().padStart(2, '0')
|
||||||
|
|
||||||
|
time.value = `${hours}:${minutes}:${seconds}`
|
||||||
|
date.value = `${year}-${month}-${dayOfMonth}`
|
||||||
|
day.value = weekDays[now.getDay()]
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer: ReturnType<typeof setInterval>
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateTime()
|
||||||
|
timer = setInterval(updateTime, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-container">
|
||||||
|
<div class="dashboard-container-left">
|
||||||
|
<div class="dashboard-container-header">
|
||||||
|
<img src="@/assets/images/dashboard/logo.svg" alt="">
|
||||||
|
<div style="margin-left: 10px;">电气量测控平台</div>
|
||||||
|
</div>
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-container-right">
|
||||||
|
<div class="dashboard-container-right-time">
|
||||||
|
<div class="dashboard-container-right-time1">{{ time }}</div>
|
||||||
|
<div class="dashboard-container-right-time2">{{ date }} {{ day }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-container-right-content">
|
||||||
|
<div class="dashboard-container-right-content-title">
|
||||||
|
<span>欢迎使用</span>
|
||||||
|
<span style="color: #0099FF;">电气量测控平台</span>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-container-right-content-title2">提供实时、精准、可靠的电力系统模拟量监测、开关量监控及装置自检服</div>
|
||||||
|
<div class="dashboard-container-right-content-title2">务,助力电网安全运行。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.dashboard-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-left {
|
||||||
|
width: 240px;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(229, 231, 235, 1);
|
||||||
|
font-family: '思源黑体 Bold', '思源黑体 Regular', '思源黑体', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #1F2937;
|
||||||
|
border-top: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-right {
|
||||||
|
width: calc(100% - 240px);
|
||||||
|
background: url('@/assets/images/dashboard/bg.png') no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-right-content {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-right-content-title {
|
||||||
|
font-family: '思源黑体 Bold', '思源黑体 Regular', '思源黑体', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 48px;
|
||||||
|
letter-spacing: normal;
|
||||||
|
color: #282828;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-right-content-title2 {
|
||||||
|
font-family: '思源黑体', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #6B7280;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-right-time {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 30px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.dashboard-container-right-time1 {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-container-right-time2 {
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
116
frontend/src/views/deviceInfo/index.vue
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="device-info-container">
|
||||||
|
<div class="container-box">
|
||||||
|
<div class="box-item">
|
||||||
|
<div class="box-item-content">
|
||||||
|
<div class="box-item-icon">
|
||||||
|
<img src="@/assets/images/menuicon/yj.svg" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="box-item-text1">设备硬件信息</div>
|
||||||
|
<div class="box-item-line">
|
||||||
|
<div>板卡版本</div>
|
||||||
|
<div>B001.001.001</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-item-line">
|
||||||
|
<div>显示版本</div>
|
||||||
|
<div>B001.001.001</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-item-line">
|
||||||
|
<div>其他版本</div>
|
||||||
|
<div>B001.001.001</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-item">
|
||||||
|
<div class="box-item-content">
|
||||||
|
<div class="box-item-icon">
|
||||||
|
<img src="@/assets/images/menuicon/bb.svg" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="box-item-text1">程序版本信息</div>
|
||||||
|
<div class="box-item-line">
|
||||||
|
<div>显示程序版本</div>
|
||||||
|
<div>001.001.001</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-item-line">
|
||||||
|
<div>通讯程序版本</div>
|
||||||
|
<div>001.001.001</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-item-line">
|
||||||
|
<div>测量程序版本</div>
|
||||||
|
<div>001.001.001</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.device-info-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.container-box {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 100px;
|
||||||
|
min-height: 480px;
|
||||||
|
min-width: 820px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-item {
|
||||||
|
width: 340px;
|
||||||
|
height: 440px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(0, 153, 255, 0.0470588235294118);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.box-item-content {
|
||||||
|
width: 230px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.box-item-icon {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: rgba(0, 153, 255, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-item-text1 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-top: 40px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-item-line {
|
||||||
|
width: 230px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #505050;
|
||||||
|
line-height: 40px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
192
frontend/src/views/eventReport/index.vue
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
line: '线路一',
|
||||||
|
type: '操作事件',
|
||||||
|
timeRange: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
belong: '线路一',
|
||||||
|
type: '操作事件',
|
||||||
|
detail: '手动修改时间操作',
|
||||||
|
time: '2026-01-12 12:12:12 886ms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
belong: '线路一',
|
||||||
|
type: '操作事件',
|
||||||
|
detail: '手动修改时间操作',
|
||||||
|
time: '2026-01-12 12:12:12 886ms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
belong: '线路一',
|
||||||
|
type: '操作事件',
|
||||||
|
detail: '手动修改时间操作',
|
||||||
|
time: '2026-01-12 12:12:12 886ms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
belong: '线路一',
|
||||||
|
type: '操作事件',
|
||||||
|
detail: '手动修改时间操作',
|
||||||
|
time: '2026-01-12 12:12:12 886ms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
belong: '线路一',
|
||||||
|
type: '操作事件',
|
||||||
|
detail: '手动修改时间操作',
|
||||||
|
time: '2026-01-12 12:12:12 886ms',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const pagination = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 36,
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleQuery = () => {
|
||||||
|
console.log('查询', formData)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSizeChange = (val: number) => {
|
||||||
|
pagination.pageSize = val
|
||||||
|
pagination.currentPage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val: number) => {
|
||||||
|
pagination.currentPage = val
|
||||||
|
}
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
formData.line = ''
|
||||||
|
formData.type = ''
|
||||||
|
formData.timeRange = []
|
||||||
|
init()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="event-report-container">
|
||||||
|
<div class="search-box">
|
||||||
|
<el-select v-model="formData.line" placeholder="所属线路" style="width: 180px">
|
||||||
|
<el-option label="线路一" value="线路一" />
|
||||||
|
<el-option label="线路二" value="线路二" />
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="formData.type" placeholder="事件类型" style="width: 180px; margin-left: 15px">
|
||||||
|
<el-option label="操作事件" value="操作事件" />
|
||||||
|
<el-option label="报警事件" value="报警事件" />
|
||||||
|
</el-select>
|
||||||
|
<div>
|
||||||
|
<el-date-picker v-model="formData.timeRange" type="datetimerange" start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 380px; margin-left: 15px" />
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" style="margin-left: 15px;" @click="handleQuery">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="table-box">
|
||||||
|
<el-table :data="tableData" style="width: 100%; height: calc(100vh - 280px)" header-align="center"
|
||||||
|
align="center">
|
||||||
|
<el-table-column label="序号" prop="id" width="80" />
|
||||||
|
<el-table-column label="事件所属" prop="belong" />
|
||||||
|
<el-table-column label="事件类型" prop="type" />
|
||||||
|
<el-table-column label="事件详情" prop="detail" />
|
||||||
|
<el-table-column label="发生时间" prop="time" />
|
||||||
|
</el-table>
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
|
||||||
|
:page-sizes="[10, 20, 30, 40]" @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||||
|
:total="pagination.total" layout="prev, pager, next, total, sizes" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.event-report-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-box {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 0 10px rgba(219, 225, 236, 1);
|
||||||
|
height: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__header-wrapper) {
|
||||||
|
background-color: #f9fafe !important;
|
||||||
|
height: 46px;
|
||||||
|
line-height: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__header) {
|
||||||
|
th {
|
||||||
|
background-color: #f9fafe !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
color: #333 !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
--el-table-row-hover-bg-color: #f8f9fa !important;
|
||||||
|
--el-table-border-color: #f2f2f2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table td) {
|
||||||
|
border-bottom: 1px solid #f2f2f2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__cell) {
|
||||||
|
color: #505050 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: #363636;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(el-input__wrapper) {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select__wrapper) {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-range-editor.el-input__wrapper) {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
161
frontend/src/views/lightSetting/index.vue
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, watch } from 'vue'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive({
|
||||||
|
sleepTime: 180, // 息屏时间(秒)
|
||||||
|
brightness: 80 // 屏幕亮度(百分比)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 提交保存
|
||||||
|
const handleSave = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
await formRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log('灯光设置数据:', form)
|
||||||
|
// 这里写接口提交逻辑
|
||||||
|
alert('灯光设置保存成功!')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="light-container">
|
||||||
|
<div class="light-container-content">
|
||||||
|
<div class="title">
|
||||||
|
<span class="title-line"></span>
|
||||||
|
灯光设置
|
||||||
|
</div>
|
||||||
|
<el-form ref="formRef" :model="form" label-width="100px" class="light-form">
|
||||||
|
<el-form-item label="息屏时间设置" prop="sleepTime">
|
||||||
|
<el-input v-model.number="form.sleepTime" type="number" min="0">
|
||||||
|
<template #suffix>
|
||||||
|
<span class="unit">秒</span>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="屏幕亮度" prop="brightness">
|
||||||
|
<div class="slider-wrap">
|
||||||
|
<el-slider v-model="form.brightness" :min="0" :max="100" :step="1" style="width: 100%;" />
|
||||||
|
<span class="brightness-value">{{ form.brightness }}%</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" class="save-btn" @click="handleSave">
|
||||||
|
保存
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.light-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.light-container-content {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 230px);
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 20px 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title-line {
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: #409eff;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-form {
|
||||||
|
max-width: 600px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.slider-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.brightness-value {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background-color: #0099ff;
|
||||||
|
width: 150px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item--label-right .el-form-item__label) {
|
||||||
|
text-align: left;
|
||||||
|
justify-content: flex-start;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: #363636;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__bar) {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button) {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
line-height: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider) {
|
||||||
|
height: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__bar) {
|
||||||
|
height: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__button) {
|
||||||
|
border-color: #ffffff;
|
||||||
|
border-width: 3px;
|
||||||
|
box-shadow: 0px 0px 5px rgb(0, 0, 0, 0.35);
|
||||||
|
background-color: #0099ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-slider__runway) {
|
||||||
|
height: 8px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
298
frontend/src/views/lineSetting/index.vue
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
const lineTabs = ref([
|
||||||
|
{
|
||||||
|
label: '线路一定值',
|
||||||
|
value: '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '线路二定值',
|
||||||
|
value: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '线路三定值',
|
||||||
|
value: '3'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '线路四定值',
|
||||||
|
value: '4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '线路五定值',
|
||||||
|
value: '5'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const activeTab = ref('1')
|
||||||
|
const infoList = ref({
|
||||||
|
category: {a: '180', b: '180', c: '开出1', d: '0'},
|
||||||
|
voltage: {a: '180', b: '180', c: '开出1', d: '0'},
|
||||||
|
current: {a: '180', b: '180', c: '开出1', d: '0'},
|
||||||
|
diffCurrent: {a: '180', b: '180', c: '开出1', d: '0'},
|
||||||
|
power: {a: '180', b: '180', c: '开出1', d: '0'},
|
||||||
|
frequency: {a: '180', b: '180', c: '开出1', d: '0'},
|
||||||
|
ptBreak: {a: '180', b: '180', c: '开出1', d: '1'},
|
||||||
|
ctBreak: {a: '180', b: '180', c: '开出1', d: '1'},
|
||||||
|
})
|
||||||
|
const handleSave = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
const options = ref([
|
||||||
|
{
|
||||||
|
label: '开出1',
|
||||||
|
value: '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '开出2',
|
||||||
|
value: '2'
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="line-page">
|
||||||
|
<div class="line-tabs">
|
||||||
|
<div class="tab-item" v-for="item in lineTabs" :key="item.value"
|
||||||
|
:class="{ 'is-active': activeTab === item.value }" @click="activeTab = item.value">
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="line-tabs-box">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-content-item">
|
||||||
|
<div class="tab-content-item-box" style="height: 265px;">越限告警</div>
|
||||||
|
<div class="tab-content-item-box" style="height: 85px;">故障告警</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item">
|
||||||
|
<div class="tab-content-item-box box-height">类别</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">电压</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">电流</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">差流</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">功率</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">频率</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">PT断线</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">CT断线</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item">
|
||||||
|
<div class="tab-content-item-box box-height">限制</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.voltage.a" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.current.a" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.diffCurrent.a" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.power.a" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.frequency.a" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height" style="border: none;">
|
||||||
|
<img src="@/assets/images/menuicon/noitem.png" style="width: 100%; height: 100%;" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height" style="border: none;">
|
||||||
|
<img src="@/assets/images/menuicon/noitem.png" style="width: 100%; height: 100%;" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item">
|
||||||
|
<div class="tab-content-item-box box-height">延时(s)</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.voltage.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.current.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.diffCurrent.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.power.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.frequency.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.ptBreak.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-input-number v-model="infoList.ctBreak.b" style="width: 100%;" :controls="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item">
|
||||||
|
<div class="tab-content-item-box box-height">输出节点</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.voltage.c">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.current.c">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.diffCurrent.c">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.power.c">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.frequency.c">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.ptBreak.c" >
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-select v-model="infoList.ctBreak.c" >
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item">
|
||||||
|
<div class="tab-content-item-box box-height">是否投入</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.voltage.d" active-value="1" inactive-value="0"></el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.current.d" active-value="1" inactive-value="0"></el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.diffCurrent.d" active-value="1" inactive-value="0"></el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.power.d" active-value="1" inactive-value="0"></el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.frequency.d" active-value="1" inactive-value="10"></el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.ptBreak.d" active-value="1" inactive-value="0"></el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content-item-box box-color box-height">
|
||||||
|
<el-switch v-model="infoList.ctBreak.d" active-value="1" inactive-value="0"></el-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" class="save-btn" @click="handleSave">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.line-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.line-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
width: 120px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #e9ecf3;
|
||||||
|
border: 1px solid #dfe4ee;
|
||||||
|
border-radius: 10px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
box-shadow: 0px 0px 6px rgba(232, 236, 244, 1);
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606060;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-active {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #0099ff;
|
||||||
|
border-color: #0099ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-tabs-box {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding: 60px 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
// height: calc(100vh - 260px);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
padding: 20px 20px;
|
||||||
|
min-height: max-content;
|
||||||
|
gap: 5px;
|
||||||
|
.tab-content-item {
|
||||||
|
width: calc(100% / 6);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.tab-content-item-box {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #f9fafe;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(228, 228, 228, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #787878;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-color {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
.box-height{
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
width: 150px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #0099ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.el-select__wrapper),
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
:deep(.el-select__wrapper.is-hover),
|
||||||
|
:deep(.el-input__wrapper.is-hover),
|
||||||
|
:deep(.el-select__wrapper.is-focused),
|
||||||
|
:deep(.el-input__wrapper.is-focused) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
:deep(.el-input__inner){
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
193
frontend/src/views/login/index.vue
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<!-- <script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { login } from '@/api/auth'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const loginForm = ref({
|
||||||
|
account: '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const loginFormRef = ref()
|
||||||
|
const loginRules = reactive({
|
||||||
|
account: [
|
||||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const handleLogin = () => {
|
||||||
|
loginFormRef.value.validate((valid: any) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
const params = {
|
||||||
|
account: loginForm.value.account,
|
||||||
|
password: loginForm.value.password
|
||||||
|
};
|
||||||
|
login(params)
|
||||||
|
.then((response: any) => {
|
||||||
|
localStorage.setItem('token', response.token)
|
||||||
|
localStorage.setItem('username', response.user.username)
|
||||||
|
localStorage.setItem('account', response.user.account)
|
||||||
|
const redirect = (route.query.redirect as string) || '/dashboard'
|
||||||
|
router.push(redirect)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
ElMessage.error(error.message || '登录失败,请检查用户名和密码');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="login-title-box">
|
||||||
|
<div class="login-title">PEX50K台架试验数据监控系统</div>
|
||||||
|
</div>
|
||||||
|
<div class="login-form">
|
||||||
|
<div class="login-form-title">欢迎登录</div>
|
||||||
|
|
||||||
|
<el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" class="login-form-box">
|
||||||
|
<el-form-item prop="account">
|
||||||
|
<el-input v-model="loginForm.account" placeholder="请输入用户名">
|
||||||
|
<template #prefix>
|
||||||
|
<img src="../../assets/login/yh.png" alt="" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码">
|
||||||
|
<template #prefix>
|
||||||
|
<img src="../../assets/login/mm.png" alt="" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="login-btn" @click="handleLogin">
|
||||||
|
<img src="../../assets/login/loginbt.png" alt="">
|
||||||
|
<span class="login-text">登录</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 16px;
|
||||||
|
background-image: url('../../assets/login/dl_bg.png');
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
position: absolute;
|
||||||
|
top: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title {
|
||||||
|
font-size: 50px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
background: linear-gradient(to bottom, #FFFFFF 0%, #C3E3FF 75%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
color: transparent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
width: 650px;
|
||||||
|
height: 350px;
|
||||||
|
background-image: url('../../assets/login/dl_kuang.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: right;
|
||||||
|
position: relative;
|
||||||
|
top: 80px;
|
||||||
|
right: -385px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FFFFFF;
|
||||||
|
margin-top: 35px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-box {
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
width: 300px;
|
||||||
|
height: 46px;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
/* border: 1px solid rgba(0, 153, 255, 0.5); */
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 153, 255, 0.5) inset;
|
||||||
|
position: relative;
|
||||||
|
top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__prefix-inner>:last-child) {
|
||||||
|
margin-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item__error) {
|
||||||
|
padding-top: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn img {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
right: -177px;
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-text {
|
||||||
|
position: absolute;
|
||||||
|
left: 305px;
|
||||||
|
top: 267px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style> -->
|
||||||
136
frontend/src/views/passwordSetting/index.vue
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive({
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 校验规则
|
||||||
|
const rules: FormRules = {
|
||||||
|
oldPassword: [
|
||||||
|
{ required: true, message: '请输入原密码', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
newPassword: [
|
||||||
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
|
{ min: 6, max: 20, message: '密码长度需在 6~20 位之间', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value !== form.newPassword) {
|
||||||
|
callback(new Error('两次输入的密码不一致'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交保存
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
await formRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log('表单数据:', form)
|
||||||
|
// 这里写接口提交逻辑
|
||||||
|
alert('密码修改成功!')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="password-container">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="password-container-content">
|
||||||
|
<div class="title">
|
||||||
|
<span class="title-line"></span>
|
||||||
|
密码设置
|
||||||
|
</div>
|
||||||
|
<!-- 表单 -->
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px" class="password-form">
|
||||||
|
<el-form-item label="原密码">
|
||||||
|
<el-input v-model="form.oldPassword" type="password" placeholder="请输入原密码" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="新密码">
|
||||||
|
<el-input v-model="form.newPassword" type="password" placeholder="请输入新密码" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="确认新密码">
|
||||||
|
<el-input v-model="form.confirmPassword" type="password" placeholder="请再次输入新密码" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 保存按钮 -->
|
||||||
|
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;" class="save-btn" @click="handleSubmit">
|
||||||
|
保存
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.password-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.password-container-content {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 230px);
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 20px 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title-line {
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: #409eff;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-form {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.save-btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item--label-right .el-form-item__label) {
|
||||||
|
text-align: left;
|
||||||
|
justify-content: flex-start;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: #363636;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
line-height: 40px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
321
frontend/src/views/recordSetting/index.vue
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
|
||||||
|
// 默认打开 手动录波
|
||||||
|
const activeTab = ref('manual')
|
||||||
|
|
||||||
|
// 自动录波数据
|
||||||
|
const autoRecord = reactive({
|
||||||
|
overLimit: {
|
||||||
|
voltage: true,
|
||||||
|
current: false,
|
||||||
|
diffCurrent: true,
|
||||||
|
power: false,
|
||||||
|
frequency: true
|
||||||
|
},
|
||||||
|
fault: {
|
||||||
|
ptBreak: false,
|
||||||
|
ctBreak: true
|
||||||
|
},
|
||||||
|
switchIn: {
|
||||||
|
channel1: true,
|
||||||
|
channel2: false,
|
||||||
|
channel3: true,
|
||||||
|
channel4: false,
|
||||||
|
channel5: true,
|
||||||
|
channel6: false,
|
||||||
|
channel7: true,
|
||||||
|
channel8: false,
|
||||||
|
channel9: true,
|
||||||
|
channel10: false,
|
||||||
|
channel11: true,
|
||||||
|
channel12: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const isRecording = ref(false)
|
||||||
|
// 保存设置
|
||||||
|
const handleSave = () => {
|
||||||
|
alert('设置已保存')
|
||||||
|
}
|
||||||
|
// 启动录波
|
||||||
|
const startRecord = () => {
|
||||||
|
isRecording.value = !isRecording.value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="record-page">
|
||||||
|
<div class="record-tabs">
|
||||||
|
<div class="tab-item" :class="{ 'is-active': activeTab === 'manual' }" @click="activeTab = 'manual'">手动录波
|
||||||
|
</div>
|
||||||
|
<div class="tab-item" :class="{ 'is-active': activeTab === 'auto' }" @click="activeTab = 'auto'">自动录波</div>
|
||||||
|
</div>
|
||||||
|
<div class="record-tabs-box">
|
||||||
|
<div v-if="activeTab === 'manual'" class="manual-container">
|
||||||
|
<div class="circle-box">
|
||||||
|
<img src="@/assets/images/menuicon/lb.png" alt="record" />
|
||||||
|
</div>
|
||||||
|
<div class="text-box">
|
||||||
|
<div v-if="!isRecording">
|
||||||
|
<div class="text-box-text1">录波未开启</div>
|
||||||
|
<div class="text-box-text2">请点击“启动录波”进行录波</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="text-box-text3">
|
||||||
|
录波中...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" :disabled="isRecording" :class="{ 'disabled-btn': isRecording }"
|
||||||
|
class="record-btn" size="default" @click="startRecord">启动录波</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="activeTab === 'auto'" class="auto-container">
|
||||||
|
<div class="auto-container-content">
|
||||||
|
<div class="auto-container-content-item">
|
||||||
|
<div class="box1" style="height: 403px;">越限录波</div>
|
||||||
|
<div class="box1" style="height: 133px;">故障录波</div>
|
||||||
|
</div>
|
||||||
|
<div class="auto-container-content-item">
|
||||||
|
<div class="box1 box-height">类别</div>
|
||||||
|
<div class="box1 box2 box-height">电压</div>
|
||||||
|
<div class="box1 box2 box-height">电流</div>
|
||||||
|
<div class="box1 box2 box-height">差流</div>
|
||||||
|
<div class="box1 box2 box-height">功率</div>
|
||||||
|
<div class="box1 box2 box-height">频率</div>
|
||||||
|
<div class="box1 box2 box-height">PT断线</div>
|
||||||
|
<div class="box1 box2 box-height">CT断线</div>
|
||||||
|
</div>
|
||||||
|
<div class="auto-container-content-item">
|
||||||
|
<div class="box1 box-height">是否投入</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.overLimit.voltage" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.overLimit.current" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.overLimit.diffCurrent" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.overLimit.power" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.overLimit.frequency" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.fault.ptBreak" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height">
|
||||||
|
<el-switch v-model="autoRecord.fault.ctBreak" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="auto-container-content-item">
|
||||||
|
<div class="box1" style="height: 541px;">开入录波</div>
|
||||||
|
</div>
|
||||||
|
<div class="auto-container-content-item">
|
||||||
|
<div class="box1 box-height2">类别</div>
|
||||||
|
<div class="box1 box2 box-height2">通道1</div>
|
||||||
|
<div class="box1 box2 box-height2">通道2</div>
|
||||||
|
<div class="box1 box2 box-height2">通道3</div>
|
||||||
|
<div class="box1 box2 box-height2">通道4</div>
|
||||||
|
<div class="box1 box2 box-height2">通道5</div>
|
||||||
|
<div class="box1 box2 box-height2">通道6</div>
|
||||||
|
<div class="box1 box2 box-height2">通道7</div>
|
||||||
|
<div class="box1 box2 box-height2">通道8</div>
|
||||||
|
<div class="box1 box2 box-height2">通道9</div>
|
||||||
|
<div class="box1 box2 box-height2">通道10</div>
|
||||||
|
<div class="box1 box2 box-height2">通道11</div>
|
||||||
|
<div class="box1 box2 box-height2">通道12</div>
|
||||||
|
</div>
|
||||||
|
<div class="auto-container-content-item">
|
||||||
|
<div class="box1 box-height2">是否投入</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel1" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel2" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel3" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel4" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel5" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel6" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel7" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel8" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel9" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel10" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel11" />
|
||||||
|
</div>
|
||||||
|
<div class="box1 box2 box-height2">
|
||||||
|
<el-switch v-model="autoRecord.switchIn.channel12" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" class="save-btn" @click="handleSave">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.record-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.record-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding-left: 20px;
|
||||||
|
.tab-item {
|
||||||
|
width: 120px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #e9ecf3;
|
||||||
|
border: 1px solid #dfe4ee;
|
||||||
|
border-radius: 10px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
box-shadow: 0px 0px 6px rgba(232, 236, 244, 1);
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606060;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-active {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #0099ff;
|
||||||
|
border-color: #0099ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-tabs-box {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.manual-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
height: calc(100vh - 140px);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
|
||||||
|
.circle-box {
|
||||||
|
position: relative;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-box {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
.text-box-text1 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-box-text2 {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #949494;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-box-text3 {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #0099ff;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 20px 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-btn {
|
||||||
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #0099ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-btn {
|
||||||
|
background-color: #c0c4cc;
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto-container {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.auto-container-content {
|
||||||
|
width: 100%;
|
||||||
|
// height: calc(100vh - 230px);
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
display: flex;
|
||||||
|
padding: 20px 20px;
|
||||||
|
gap: 5px;
|
||||||
|
min-height: min-content;
|
||||||
|
.auto-container-content-item {
|
||||||
|
width: 20%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
.auto-container-content-item .box1 {
|
||||||
|
border: 1px solid #e4e4e4;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f9fafe;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #787878;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.auto-container-content-item .box2 {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
.auto-container-content-item .box-height {
|
||||||
|
height: 63px;
|
||||||
|
min-height: 63px;
|
||||||
|
}
|
||||||
|
.auto-container-content-item .box-height2 {
|
||||||
|
height: 37px;
|
||||||
|
min-height: 37px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
width: 150px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #0099ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
210
frontend/src/views/switchQuantity/index.vue
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted } from 'vue'
|
||||||
|
import { usePlatformStore } from '../../stores/platform'
|
||||||
|
|
||||||
|
const { state, bootstrap } = usePlatformStore()
|
||||||
|
|
||||||
|
const inputStates = computed(() => {
|
||||||
|
if (!state.realtime?.switch) {
|
||||||
|
return Array(12).fill(false)
|
||||||
|
}
|
||||||
|
return Array.from({ length: 12 }, (_, i) => {
|
||||||
|
const key = `input_${i + 1}`
|
||||||
|
return state.realtime!.switch[key] === 1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const outputStates = computed(() => {
|
||||||
|
if (!state.realtime?.switch) {
|
||||||
|
return Array(12).fill(false)
|
||||||
|
}
|
||||||
|
return Array.from({ length: 12 }, (_, i) => {
|
||||||
|
const key = `output_${i + 1}`
|
||||||
|
return state.realtime!.switch[key] === 1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = computed(() => {
|
||||||
|
return state.alarms.slice(0, 10).map((alarm, index) => ({
|
||||||
|
name: '线路一',
|
||||||
|
typeName: alarm.type,
|
||||||
|
content: alarm.content,
|
||||||
|
time: alarm.time,
|
||||||
|
ts: '0',
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void bootstrap()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="switch-container">
|
||||||
|
<div class="switch-container-top">
|
||||||
|
<div class="top-title">开入/开出状态</div>
|
||||||
|
<div class="container-top-box">
|
||||||
|
<div class="state-column">
|
||||||
|
<div class="column-title">
|
||||||
|
<div class="column-title-icon"></div>
|
||||||
|
<div class="column-title-text">开入状态</div>
|
||||||
|
</div>
|
||||||
|
<div class="state-grid">
|
||||||
|
<div v-for="(state, index) in inputStates" :key="`input-${index}`" class="state-item">
|
||||||
|
<img v-if="state" src="@/assets/images/menuicon/off.png" style="width: 34px; height: 36px;"
|
||||||
|
alt="">
|
||||||
|
<img v-else src="@/assets/images/menuicon/on.png" alt="" style="width: 34px; height: 36px;">
|
||||||
|
<div class="label">开入{{ index + 1 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="state-column">
|
||||||
|
<div class="column-title">
|
||||||
|
<div class="column-title-icon"></div>
|
||||||
|
<div class="column-title-text">开出状态</div>
|
||||||
|
</div>
|
||||||
|
<div class="state-grid">
|
||||||
|
<div v-for="(state, index) in outputStates" :key="`output-${index}`" class="state-item">
|
||||||
|
<img v-if="state" src="@/assets/images/menuicon/off2.png" style="width: 46px; height: 24px;"
|
||||||
|
alt="">
|
||||||
|
<img v-else src="@/assets/images/menuicon/on2.png" style="width: 46px; height: 24px;"
|
||||||
|
alt="">
|
||||||
|
<div class="label">开出{{ index + 1 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="switch-container-bottom">
|
||||||
|
<div class="top-title">报警信息</div>
|
||||||
|
<div class="container-bottom-box">
|
||||||
|
<div v-for="(item, index) in tableData" :key="index" class="container-bottom-box-line1">
|
||||||
|
<div style="width: 8%; text-align: center;">{{ index + 1 }}</div>
|
||||||
|
<div style="width: 23%; text-align: center;">{{ item.name }}</div>
|
||||||
|
<div style="width: 15%; text-align: center;">{{ item.typeName }}</div>
|
||||||
|
<div style="width: 25%; text-align: center;">{{ item.content }}</div>
|
||||||
|
<div style="width: 20%; text-align: center;">{{ item.time }}</div>
|
||||||
|
<div style="width: 10%; text-align: center;">{{ item.ts }}ms</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.switch-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.top-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #363636;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-container-top {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
.container-top-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 458px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
.state-column {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.column-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #363636;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.column-title-icon {
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-title-text {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #363636;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
padding: 20px 10px;
|
||||||
|
background-color: rgba(249, 250, 254, 1);
|
||||||
|
height: 380px;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
.state-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 65px;
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #363636;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
// 文字居中对齐
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-container-bottom {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.container-bottom-box {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 660px);
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.container-bottom-box-line1 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #787878;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #505050;
|
||||||
|
border-bottom: 1px solid #f2f2f2;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
107
frontend/src/views/timeSetting/index.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
targetTime: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
|
||||||
|
}
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
await formRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log('表单数据:', form)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="time-container">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="time-container-content">
|
||||||
|
<div class="title">
|
||||||
|
<span class="title-line"></span>
|
||||||
|
对时设置
|
||||||
|
</div>
|
||||||
|
<!-- 表单 -->
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px" class="time-form">
|
||||||
|
<el-form-item label="选择时间" prop="targetTime">
|
||||||
|
<el-date-picker v-model="form.targetTime" type="datetime" placeholder="请选择时间"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;" class="save-btn"
|
||||||
|
@click="handleSubmit">
|
||||||
|
更改
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.time-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.time-container-content {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 230px);
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 20px 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title-line {
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: #409eff;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-form {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item--label-right .el-form-item__label) {
|
||||||
|
text-align: left;
|
||||||
|
justify-content: flex-start;
|
||||||
|
color: #787878;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: #363636;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
line-height: 40px !important;
|
||||||
|
}
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
min-height: 40px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,7 +1,9 @@
|
|||||||
import type { AlarmEvent, RealtimeData } from '../types/platform'
|
import type { AlarmEvent, RealtimeData } from '../types/platform'
|
||||||
|
|
||||||
function buildWsUrl(path: string): string {
|
function buildWsUrl(path: string): string {
|
||||||
return `ws://127.0.0.1:8000${path}`
|
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
|
const host = window.location.host
|
||||||
|
return `${protocol}://${host}${path}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function connectRealtime(onMessage: (data: RealtimeData) => void): WebSocket {
|
export function connectRealtime(onMessage: (data: RealtimeData) => void): WebSocket {
|
||||||
|
|||||||
@ -9,7 +9,11 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": ["ES2020", "DOM"],
|
"lib": ["ES2020", "DOM"],
|
||||||
"types": ["vite/client"]
|
"types": ["vite/client"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.vue"]
|
"include": ["src/**/*.ts", "src/**/*.vue"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,27 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: 0.0.0.0,
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8000',
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
'/ws': {
|
||||||
|
target: 'ws://localhost:8000',
|
||||||
|
changeOrigin: true,
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, 'src'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||