修改测试用例

This commit is contained in:
guanj
2025-07-29 08:33:04 +08:00
parent d53b9df52f
commit 24c590478e
26 changed files with 880 additions and 235 deletions

View File

@@ -56,6 +56,7 @@
"mathjax": "^3.2.2", "mathjax": "^3.2.2",
"min-dash": "^4.2.1", "min-dash": "^4.2.1",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"mqtt": "^5.13.3",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
public/favicon1.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -88,8 +88,8 @@ export const updateStatistical = (data: any) => {
// 单位绑定 // 单位绑定
export function codeDicTree(data: any) { export function codeDicTree(data: any) {
return createAxios({ return createAxios({
url: '/system-boot/dicTree/codeDicTree', url: '/system-boot/dictTree/queryByCodeList',
method: 'get', method: 'post',
params: data params: data
}) })
} }

View File

@@ -135,14 +135,28 @@ export default {
methods: { methods: {
backbxlb() { backbxlb() {
this.waveDatas = [] this.waveDatas = []
if(this.myChartess){this.myChartess.dispose(); this.myChartess=null; }
if(this.myChartess1){this.myChartess1.dispose(); this.myChartess1=null; }
if(this.myChartess2){this.myChartess2.dispose(); this.myChartess2=null; }
if(this.myChartess3){this.myChartess3.dispose(); this.myChartess3=null; }
if(this.myChartess4){this.myChartess4.dispose(); this.myChartess4=null; }
if(this.myChartess5){this.myChartess5.dispose(); this.myChartess5=null; }
echarts.disconnect([this.myChartess,this.myChartess1,this.myChartess2,this.myChartess3,this.myChartess4,this.myChartess5]) // 清理所有可能的 echarts 实例
const chartNames = ['myChartess', 'myChartess1', 'myChartess2', 'myChartess3', 'myChartess4', 'myChartess5']
chartNames.forEach(name => {
if (this[name]) {
this[name].dispose()
this[name] = null
}
})
// 断开 echarts 实例连接
echarts.disconnect(chartNames.map(name => this[name]).filter(Boolean))
// this.waveDatas = []
// if(this.myChartess){this.myChartess.dispose(); this.myChartess=null; }
// if(this.myChartess1){this.myChartess1.dispose(); this.myChartess1=null; }
// if(this.myChartess2){this.myChartess2.dispose(); this.myChartess2=null; }
// if(this.myChartess3){this.myChartess3.dispose(); this.myChartess3=null; }
// if(this.myChartess4){this.myChartess4.dispose(); this.myChartess4=null; }
// if(this.myChartess5){this.myChartess5.dispose(); this.myChartess5=null; }
// echarts.disconnect([this.myChartess,this.myChartess1,this.myChartess2,this.myChartess3,this.myChartess4,this.myChartess5])
// if (this.echartlist.length > 0) { // if (this.echartlist.length > 0) {

View File

@@ -123,15 +123,19 @@ export default {
methods: { methods: {
backbxlb() { backbxlb() {
this.waveDatas = [] this.waveDatas = []
if(this.myChartess){this.myChartess.dispose(); this.myChartess=null; }
if(this.myChartess1){this.myChartess1.dispose(); this.myChartess1=null; }
if(this.myChartess2){this.myChartess2.dispose(); this.myChartess2=null; }
if(this.myChartess3){this.myChartess3.dispose(); this.myChartess3=null; }
if(this.myChartess4){this.myChartess4.dispose(); this.myChartess4=null; }
if(this.myChartess5){this.myChartess5.dispose(); this.myChartess5=null; }
echarts.disconnect([this.myChartess,this.myChartess1,this.myChartess2,this.myChartess3,this.myChartess4,this.myChartess5]) // 清理所有可能的 echarts 实例
const chartNames = ['myChartess', 'myChartess1', 'myChartess2', 'myChartess3', 'myChartess4', 'myChartess5']
chartNames.forEach(name => {
if (this[name]) {
this[name].dispose()
this[name] = null
}
})
// 断开 echarts 实例连接
echarts.disconnect(chartNames.map(name => this[name]).filter(Boolean))
// this.echartlist.forEach(item => { // this.echartlist.forEach(item => {
// if (item) { // if (item) {

View File

@@ -4,7 +4,7 @@ export const defaultAttribute: VxeTableProps = {
align: 'center', align: 'center',
headerCellClassName: 'table-header-cell', headerCellClassName: 'table-header-cell',
border: true, border: true,
// stripe: true,
size: 'small', size: 'small',
columnConfig: { resizable: true }, columnConfig: { resizable: true },
rowConfig: { isCurrent: true, isHover: true }, rowConfig: { isCurrent: true, isHover: true },

View File

@@ -0,0 +1,91 @@
<template>
<div>
<!-- 全局暂降事件 -->
<el-drawer v-model="drawer" title="暂降事件" size="1000px" :before-close="handleClose">
<div :style="height">
<vxe-table
v-bind="defaultAttribute"
v-loading="isLoading"
height="100%"
ref="xTable1Ref"
:data="eventList"
>
<vxe-column type="seq" width="70px" title="序号"></vxe-column>
<vxe-column field="time" width="180px" sortable title="发生时刻"></vxe-column>
<vxe-column field="lineName" title="监测点"></vxe-column>
<vxe-column field="powerCompany" title="变电站"></vxe-column>
<vxe-column field="powerCompany" title="供电公司"></vxe-column>
<vxe-column field="persistTime" width="120px" sortable title="持续时间(s)"></vxe-column>
<vxe-column field="eventValue" width="160px" sortable title="暂降(骤升)幅值(%)">
<template #default="{ row }">
{{ Math.floor(row.eventValue * 10000) / 100 }}
</template>
</vxe-column>
<vxe-column field="eventReason" width="110px" title="暂降类型">
<template #default="{ row }">
{{ eventType.filter(item => item.id == row.eventReason)[0]?.name || '/' }}
</template>
</vxe-column>
</vxe-table>
</div>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { mainHeight } from '@/utils/layout'
import { useDictData } from '@/stores/dictData'
import MQTT from '@/utils/mqtt'
const dictData = useDictData()
const eventType = dictData.getBasicData('Event_Type')
import { useAdminInfo } from '@/stores/adminInfo'
const adminInfo = useAdminInfo()
const height = mainHeight(-20)
const drawer = ref(false)
const isLoading = ref(false)
const eventList = ref([])
const open = () => {
drawer.value = true
}
const handleClose = (done: any) => {
drawer.value = false
done()
}
const init = async () => {
const mqttClient = new MQTT('/sendEvent')
// 设置消息接收回调
try {
await mqttClient.init()
// 订阅主题
await mqttClient.subscribe()
// 设置消息接收回调
mqttClient.onMessage((topic, message) => {
const msg = JSON.parse(message.toString())
if (msg.deptList.includes(adminInfo.$state.deptId)) {
drawer.value = true
isLoading.value = true
eventList.value.unshift(msg)
setTimeout(() => {
isLoading.value = false
}, 500)
}
})
} catch (error) {
console.error('MQTT 初始化失败:', error)
}
}
onMounted(() => {
// startMqtt('/sendEvent', (topic, message) => {
// const msg = JSON.parse(message.toString())
// console.log(msg)
// })
init()
})
defineExpose({
open,
eventList
})
</script>
<style lang="scss" scoped></style>

View File

@@ -1,5 +1,19 @@
<template> <template>
<div class="nav-menus" :class="configStore.layout.layoutMode"> <div class="nav-menus" :class="configStore.layout.layoutMode">
<el-tooltip effect="dark" content="暂降事件" placement="bottom">
<div @click="temporaryLandingEvent" class="nav-menu-item">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
name="el-icon-BellFilled"
size="18"
/>
<span class="nav-menu-text" v-if="globalPopUpRef?.eventList.length != 0">
{{ globalPopUpRef?.eventList.length || 0 }}
</span>
</div>
</el-tooltip>
<el-tooltip effect="dark" content="截图" placement="bottom">
<div @click="savePng" class="nav-menu-item"> <div @click="savePng" class="nav-menu-item">
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
@@ -8,6 +22,8 @@
size="18" size="18"
/> />
</div> </div>
</el-tooltip>
<el-tooltip effect="dark" :content="state.isFullScreen ? '缩小' : '放大'" placement="bottom">
<div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''"> <div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''">
<Icon <Icon
:color="configStore.getColorVal('headerBarTabColor')" :color="configStore.getColorVal('headerBarTabColor')"
@@ -24,6 +40,7 @@
size="18" size="18"
/> />
</div> </div>
</el-tooltip>
<el-dropdown style="height: 100%" @command="handleCommand"> <el-dropdown style="height: 100%" @command="handleCommand">
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''"> <div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
<el-avatar :size="25" fit="fill"> <el-avatar :size="25" fit="fill">
@@ -51,6 +68,8 @@
<PopupPwd ref="popupPwd" /> <PopupPwd ref="popupPwd" />
<AdminInfo ref="popupAdminInfo" /> <AdminInfo ref="popupAdminInfo" />
<!-- <TerminalVue /> --> <!-- <TerminalVue /> -->
<!-- 全局暂降事件 -->
<globalPopUp ref="globalPopUpRef" />
</div> </div>
</template> </template>
@@ -62,6 +81,7 @@ import { ElMessage } from 'element-plus'
import Config from './config.vue' import Config from './config.vue'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import router from '@/router' import router from '@/router'
import globalPopUp from './globalPopUp.vue'
import { routePush } from '@/utils/router' import { routePush } from '@/utils/router'
import { fullUrl } from '@/utils/common' import { fullUrl } from '@/utils/common'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
@@ -80,7 +100,7 @@ const state = reactive({
showLayoutDrawer: false, showLayoutDrawer: false,
showAdminInfoPopover: false showAdminInfoPopover: false
}) })
const globalPopUpRef = ref()
const savePng = () => { const savePng = () => {
html2canvas(document.body, { html2canvas(document.body, {
scale: 1, scale: 1,
@@ -122,6 +142,9 @@ const handleCommand = (key: string) => {
break break
} }
} }
const temporaryLandingEvent = () => {
globalPopUpRef.value.open()
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -139,6 +162,7 @@ const handleCommand = (key: string) => {
background-color: v-bind('configStore.getColorVal("headerBarBackground")'); background-color: v-bind('configStore.getColorVal("headerBarBackground")');
.nav-menu-item { .nav-menu-item {
position: relative;
height: 100%; height: 100%;
width: 40px; width: 40px;
display: flex; display: flex;
@@ -233,4 +257,15 @@ const handleCommand = (key: string) => {
transform: scale(1); transform: scale(1);
} }
} }
.nav-menu-text {
position: absolute;
top: 13px;
right: 7px;
font-size: 12px;
display: inline-block;
background-color: #ff0000;
color: #fff;
border-radius: 5px;
padding: 0 3px;
}
</style> </style>

View File

@@ -68,7 +68,7 @@
} }
.el-dialog__header .el-dialog__headerbtn:hover .el-icon { .el-dialog__header .el-dialog__headerbtn:hover .el-icon {
color: #409eff; color: #ccc;
} }
.el-dialog__header .el-dialog__title { .el-dialog__header .el-dialog__title {
@@ -244,3 +244,30 @@
.el-select__input-wrapper { .el-select__input-wrapper {
width: 11px; width: 11px;
} }
.el-drawer__header {
background: var(--el-color-primary);
padding: 18px;
margin-right: 0px;
}
.el-drawer__header .el-drawer__close-btn {
top: 5px;
}
.el-drawer__header .el-drawer__close-btn .el-icon {
color: var(--el-color-white);
}
.el-drawer__header .el-drawer__close-btn:hover .el-icon {
color: #ccc;
}
.el-drawer__header .el-drawer__title {
color: var(--el-color-white);
font-size: 18px;
}
.el-drawer__body {
padding: 10px;
}

View File

@@ -1 +1 @@
.el-input .el-input__inner{height:30px;line-height:calc(var(--el-input-height, 40px) - 4px)}.datetime-picker{height:32px;padding-top:0;padding-bottom:0}.el-divider__text.is-center{transform:translateX(-50%) translateY(-62%)}.el-menu{user-select:none}.el-menu .el-menu-item:hover,.el-menu .el-sub-menu__title:hover{background-color:var(--el-menu-hover-color) !important;color:var(--el-menu-active-color) !important}.el-menu .el-menu-item:hover .icon,.el-menu .el-sub-menu__title:hover .icon{color:var(--el-menu-active-color) !important}.atooltip{margin-top:0px !important;padding:0 !important}.el-dialog{padding:0px !important}.el-dialog .el-dialog__footer{padding:15px;box-shadow:var(--el-box-shadow);width:100%;bottom:0}.el-dialog__body{max-height:60vh;overflow-y:auto;padding:10px}.el-dialog__header{background:var(--el-color-primary);padding:15px;margin-right:0px}.el-dialog__header .el-dialog__headerbtn{top:5px}.el-dialog__header .el-dialog__headerbtn .el-icon{color:var(--el-color-white)}.el-dialog__header .el-dialog__headerbtn:hover .el-icon{color:#409eff}.el-dialog__header .el-dialog__title{color:var(--el-color-white)}.el-table{--el-table-border-color:var(--ba-border-color)}.el-card{border:none}.el-card__header{border-bottom:1px solid var(--el-border-color-extra-light)}.el-textarea__inner{padding:5px 11px}.el-overlay-dialog,.el-tabs__content,.ba-scroll-style{scrollbar-width:none}.el-overlay-dialog::-webkit-scrollbar,.el-tabs__content::-webkit-scrollbar,.ba-scroll-style::-webkit-scrollbar{width:5px;height:5px}.el-overlay-dialog::-webkit-scrollbar-thumb,.el-tabs__content::-webkit-scrollbar-thumb,.ba-scroll-style::-webkit-scrollbar-thumb{background:#eaeaea;border-radius:var(--el-border-radius-base);box-shadow:none;-webkit-box-shadow:none}.el-overlay-dialog:hover::-webkit-scrollbar-thumb:hover,.el-tabs__content:hover::-webkit-scrollbar-thumb:hover,.ba-scroll-style:hover::-webkit-scrollbar-thumb:hover{background:#c8c9cc}.ba-input-item-radio{margin-bottom:10px}.ba-input-item-radio .el-radio-group .el-radio{margin-bottom:8px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{background:var(--el-color-primary);color:var(--el-color-white)}.el-tabs__header{margin-bottom:0}.el-form--inline .el-form-item{margin-bottom:10px}.el-tabs--border-card>.el-tabs__content{padding:10px}.el-page-header__header{line-height:32px}.el-page-header__header .el-page-header__content{font-size:14px;font-weight:700}.el-select{min-width:200px}.el-tabs__content{height:calc(100% - 40px)}.el-tabs__content .el-tab-pane{height:100%}.el-button--primary:focus{color:var(--el-color-white);outline:0}.el-button--primary:hover{color:var(--el-color-white);border-color:var(--el-color-primary-light-3);background-color:var(--el-color-primary-light-3);outline:0}.el-button.is-plain:focus{color:var(--el-button-text-color);border-color:var(--el-button-border-color)}.el-button.is-plain:hover{color:var(--el-color-primary);border-color:var(--el-color-primary)}.el-button.is-link:focus{color:var(--el-button-text-color)}.el-button.is-link:hover{color:var(--el-button-hover-link-text-color)}.el-button--primary.is-link:hover,.el-button--primary.is-plain:hover,.el-button--primary.is-text:hover{color:var(--el-color-primary-light-5);background-color:var(--el-color-primary-light-9) !important}.el-divider--horizontal{margin:15px 0}.el-step__title{cursor:pointer}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:var(--el-color-primary);opacity:0.6}.sgmap-ctrl-bottom-left{display:none !important}.el-drawer__header{margin-bottom:0 !important}.el-pagination__sizes .el-select{min-width:128px}.el-card__header{padding:10px}.el-card__header span{font-size:16px;font-weight:600}.el-select__input-wrapper{width:11px} .el-input .el-input__inner{height:30px;line-height:calc(var(--el-input-height, 40px) - 4px)}.datetime-picker{height:32px;padding-top:0;padding-bottom:0}.el-divider__text.is-center{transform:translateX(-50%) translateY(-62%)}.el-menu{user-select:none}.el-menu .el-menu-item:hover,.el-menu .el-sub-menu__title:hover{background-color:var(--el-menu-hover-color) !important;color:var(--el-menu-active-color) !important}.el-menu .el-menu-item:hover .icon,.el-menu .el-sub-menu__title:hover .icon{color:var(--el-menu-active-color) !important}.atooltip{margin-top:0px !important;padding:0 !important}.el-dialog{padding:0px !important}.el-dialog .el-dialog__footer{padding:15px;box-shadow:var(--el-box-shadow);width:100%;bottom:0}.el-dialog__body{max-height:60vh;overflow-y:auto;padding:10px}.el-dialog__header{background:var(--el-color-primary);padding:15px;margin-right:0px}.el-dialog__header .el-dialog__headerbtn{top:5px}.el-dialog__header .el-dialog__headerbtn .el-icon{color:var(--el-color-white)}.el-dialog__header .el-dialog__headerbtn:hover .el-icon{color:#ccc}.el-dialog__header .el-dialog__title{color:var(--el-color-white)}.el-table{--el-table-border-color:var(--ba-border-color)}.el-card{border:none}.el-card__header{border-bottom:1px solid var(--el-border-color-extra-light)}.el-textarea__inner{padding:5px 11px}.el-overlay-dialog,.el-tabs__content,.ba-scroll-style{scrollbar-width:none}.el-overlay-dialog::-webkit-scrollbar,.el-tabs__content::-webkit-scrollbar,.ba-scroll-style::-webkit-scrollbar{width:5px;height:5px}.el-overlay-dialog::-webkit-scrollbar-thumb,.el-tabs__content::-webkit-scrollbar-thumb,.ba-scroll-style::-webkit-scrollbar-thumb{background:#eaeaea;border-radius:var(--el-border-radius-base);box-shadow:none;-webkit-box-shadow:none}.el-overlay-dialog:hover::-webkit-scrollbar-thumb:hover,.el-tabs__content:hover::-webkit-scrollbar-thumb:hover,.ba-scroll-style:hover::-webkit-scrollbar-thumb:hover{background:#c8c9cc}.ba-input-item-radio{margin-bottom:10px}.ba-input-item-radio .el-radio-group .el-radio{margin-bottom:8px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{background:var(--el-color-primary);color:var(--el-color-white)}.el-tabs__header{margin-bottom:0}.el-form--inline .el-form-item{margin-bottom:10px}.el-tabs--border-card>.el-tabs__content{padding:10px}.el-page-header__header{line-height:32px}.el-page-header__header .el-page-header__content{font-size:14px;font-weight:700}.el-select{min-width:200px}.el-tabs__content{height:calc(100% - 40px)}.el-tabs__content .el-tab-pane{height:100%}.el-button--primary:focus{color:var(--el-color-white);outline:0}.el-button--primary:hover{color:var(--el-color-white);border-color:var(--el-color-primary-light-3);background-color:var(--el-color-primary-light-3);outline:0}.el-button.is-plain:focus{color:var(--el-button-text-color);border-color:var(--el-button-border-color)}.el-button.is-plain:hover{color:var(--el-color-primary);border-color:var(--el-color-primary)}.el-button.is-link:focus{color:var(--el-button-text-color)}.el-button.is-link:hover{color:var(--el-button-hover-link-text-color)}.el-button--primary.is-link:hover,.el-button--primary.is-plain:hover,.el-button--primary.is-text:hover{color:var(--el-color-primary-light-5);background-color:var(--el-color-primary-light-9) !important}.el-divider--horizontal{margin:15px 0}.el-step__title{cursor:pointer}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:var(--el-color-primary);opacity:0.6}.sgmap-ctrl-bottom-left{display:none !important}.el-drawer__header{margin-bottom:0 !important}.el-pagination__sizes .el-select{min-width:128px}.el-card__header{padding:10px}.el-card__header span{font-size:16px;font-weight:600}.el-select__input-wrapper{width:11px}.el-drawer__header{background:var(--el-color-primary);padding:18px;margin-right:0px}.el-drawer__header .el-drawer__close-btn{top:5px}.el-drawer__header .el-drawer__close-btn .el-icon{color:var(--el-color-white)}.el-drawer__header .el-drawer__close-btn:hover .el-icon{color:#ccc}.el-drawer__header .el-drawer__title{color:var(--el-color-white);font-size:18px}.el-drawer__body{padding:10px}

View File

@@ -63,7 +63,7 @@
} }
.el-dialog__headerbtn:hover { .el-dialog__headerbtn:hover {
.el-icon { .el-icon {
color: #409eff; color: #ccc;
} }
} }
@@ -232,4 +232,28 @@
.el-select__input-wrapper { .el-select__input-wrapper {
width: 11px; width: 11px;
} }
.el-drawer__header {
background: var(--el-color-primary);
padding: 18px;
margin-right: 0px;
.el-drawer__close-btn {
top: 5px;
.el-icon {
color: var(--el-color-white);
}
}
.el-drawer__close-btn:hover {
.el-icon {
color: #ccc;
}
}
.el-drawer__title {
color: var(--el-color-white);
font-size: 18px;
}
}
.el-drawer__body {
padding: 10px;
}

251
src/utils/mqtt.ts Normal file
View File

@@ -0,0 +1,251 @@
import type { MqttClient, OnMessageCallback, IClientOptions, IClientSubscribeOptions } from 'mqtt';
import mqtt from 'mqtt';
interface MQTTOptions {
protocolId?: string;
qos?: 0 | 1 | 2;
clean?: boolean;
connectTimeout?: number;
clientId?: string;
username?: string;
password?: string;
reconnectPeriod?: number; // 重连间隔(ms)
maxReconnectTimes?: number; // 最大重连次数
}
class MQTT {
private topic: string;
private client: MqttClient | null = null;
private isConnected: boolean = false;
private reconnectCount: number = 0;
private maxReconnectTimes: number;
private reconnectPeriod: number;
private isManuallyDisconnected: boolean = false;
private defaultOptions: MQTTOptions = {
protocolId: 'MQTT',
qos: 2,
clean: true,
connectTimeout: 30 * 1000,
clientId: `mqttjs_${Math.random().toString(16).substr(2, 8)}`,
username: 't_user',
password: 'njcnpqs',
reconnectPeriod: 1000, // 默认1秒重试一次
maxReconnectTimes: 3, // 默认最大重连5次
};
constructor(topic: string, options: MQTTOptions = {}) {
this.topic = topic;
this.maxReconnectTimes = options.maxReconnectTimes || this.defaultOptions.maxReconnectTimes!;
this.reconnectPeriod = options.reconnectPeriod || this.defaultOptions.reconnectPeriod!;
// 合并选项
this.defaultOptions = { ...this.defaultOptions, ...options };
}
/**
* 初始化 MQTT 客户端
* @returns Promise<void>
*/
async init(): Promise<void> {
if (this.client) {
throw new Error('MQTT 客户端已初始化');
}
try {
const response = await fetch('/');
const mqttUrl = response.headers.get('X-Mqtt-Url') || 'ws://192.168.1.68:8083/mqtt';
this.client = mqtt.connect(mqttUrl, this.defaultOptions as IClientOptions);
this.setupEventListeners();
// 等待连接成功或超时
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
if (!this.isConnected) {
reject(new Error('MQTT 连接超时'));
}
}, this.defaultOptions.connectTimeout);
this.client?.on('connect', () => {
clearTimeout(timeout);
this.isConnected = true;
this.reconnectCount = 0; // 连接成功重置重连计数
resolve();
});
this.client?.on('error', (error) => {
clearTimeout(timeout);
console.error('MQTT 连接错误:', error);
reject(error);
});
});
} catch (error) {
console.error('初始化 MQTT 失败:', error);
throw error;
}
}
/**
* 设置事件监听器
*/
private setupEventListeners(): void {
if (!this.client) return;
this.client.on('close', () => {
console.log('MQTT 连接已关闭');
this.isConnected = false;
});
this.client.on('offline', () => {
console.log('MQTT 客户端离线');
this.isConnected = false;
});
this.client.on('reconnect', () => {
console.log(`MQTT 正在尝试重连 (${this.reconnectCount + 1}/${this.maxReconnectTimes})...`);
// 检查是否超过最大重连次数
if (this.reconnectCount >= this.maxReconnectTimes) {
console.log('已达到最大重连次数,停止重连');
this.client?.end(true);
this.client = null;
return;
}
this.reconnectCount++;
});
}
/**
* 订阅主题
* @param subscribeOptions 可选的订阅选项
* @returns Promise<void>
*/
async subscribe(subscribeOptions: IClientSubscribeOptions = {}): Promise<void> {
if (!this.client || !this.isConnected) {
throw new Error('MQTT 客户端未连接');
}
return new Promise((resolve, reject) => {
this.client?.subscribe(
this.topic,
{ qos: this.defaultOptions.qos, ...subscribeOptions },
(error) => {
if (error) {
console.error('订阅失败:', error);
reject(error);
} else {
console.log('订阅成功');
resolve();
}
}
);
});
}
/**
* 取消订阅
* @returns Promise<void>
*/
async unsubscribe(): Promise<void> {
if (!this.client || !this.isConnected) {
throw new Error('MQTT 客户端未连接');
}
return new Promise((resolve, reject) => {
this.client?.unsubscribe(this.topic, (error) => {
if (error) {
console.error('取消订阅失败:', error);
reject(error);
} else {
console.log('取消订阅成功');
resolve();
}
});
});
}
/**
* 设置消息回调
* @param callback 消息回调函数
*/
onMessage(callback: OnMessageCallback): void {
if (!this.client) {
throw new Error('MQTT 客户端未初始化');
}
this.client.on('message', callback);
}
/**
* 发布消息
* @param message 要发布的消息
* @param options 发布选项
* @returns Promise<void>
*/
async publish(
message: string | Buffer,
options: { qos?: 0 | 1 | 2; retain?: boolean } = {}
): Promise<void> {
if (!this.client || !this.isConnected) {
throw new Error('MQTT 客户端未连接');
}
return new Promise((resolve, reject) => {
this.client?.publish(
this.topic,
message,
{ qos: this.defaultOptions.qos, ...options },
(error) => {
if (error) {
console.error('消息发布失败:', error);
reject(error);
} else {
console.log('消息发布成功');
resolve();
}
}
);
});
}
/**
* 断开连接
* @param force 是否强制断开
*/
disconnect(force: boolean = false): void {
this.isManuallyDisconnected = true;
if (this.client) {
this.client.end(force, () => {
console.log('MQTT 连接已断开');
this.isConnected = false;
this.client = null;
});
}
}
/**
* 检查连接状态
* @returns boolean
*/
isConnectedToBroker(): boolean {
return this.isConnected;
}
/**
* 获取当前重连次数
* @returns number
*/
getReconnectCount(): number {
return this.reconnectCount;
}
/**
* 重置重连计数器
*/
resetReconnectCount(): void {
this.reconnectCount = 0;
}
}
export default MQTT;

View File

@@ -45,10 +45,10 @@ export default class SocketService {
} }
const response = await fetch('/') const response = await fetch('/')
const mqttUrl = response.headers.get('X-Mqtt-Url') const WebSocketUrl = response.headers.get('X-WebSocket-Url')
setTimeout(() => { setTimeout(() => {
// ws://192.168.1.69:10407/mgtt/api/pushMessage/ // ws://192.168.1.69:10407/mgtt/api/pushMessage/
const url = (mqttUrl || 'ws://192.168.1.68:10407/api/pushMessage/') + id const url = (WebSocketUrl || 'ws://192.168.1.68:10407/api/pushMessage/') + id
this.ws = new WebSocket(url) this.ws = new WebSocket(url)
this.ws.onopen = () => this.handleOpen() this.ws.onopen = () => this.handleOpen()

View File

@@ -89,39 +89,18 @@
</vxe-column> </vxe-column>
<vxe-column field="version" title="版本信息"></vxe-column> <vxe-column field="version" title="版本信息"></vxe-column>
<vxe-column field="ip" title="网络参数"></vxe-column> <vxe-column field="ip" title="网络参数"></vxe-column>
<!-- <vxe-column field="baseFlowMeal" title="基础套餐(MB)"></vxe-column>
<vxe-column field="reamFlowMeal" title="扩展套餐(MB)"></vxe-column>
<vxe-column title="剩余流量(MB)">
<template #default="{ row }">
<el-tag v-if="row.level === 4" type="primary">
{{
row.level === 4
? (row.baseFlowMeal + row.reamFlowMeal - row.statisValue).toFixed(2)
: ''
}}
</el-tag>
</template>
</vxe-column>
<vxe-column title="流量使用占比(%)">
<template #default="{ row }">
{{
row.level === 4
? ((row.statisValue / (row.baseFlowMeal + row.reamFlowMeal)) * 100).toFixed(2)
: ''
}}
</template>
</vxe-column> -->
<vxe-column field="runFlag" title="终端状态"> <vxe-column field="runFlag" title="终端状态">
<template #default="{ row }"> <template #default="{ row }">
<el-tag v-if="row.runFlag === 0" type="success" size="small">投运</el-tag> <el-tag v-if="row.runFlag === 0" type="success" :disable-transitions="true" size="small">投运</el-tag>
<el-tag v-if="row.runFlag === 1" type="warning" size="small">检修</el-tag> <el-tag v-if="row.runFlag === 1" type="warning" :disable-transitions="true" size="small">检修</el-tag>
<el-tag v-if="row.runFlag === 2" type="danger" size="small">停运</el-tag> <el-tag v-if="row.runFlag === 2" type="danger" :disable-transitions="true" size="small">停运</el-tag>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="comFlag" title="通讯状态"> <vxe-column field="comFlag" title="通讯状态">
<template #default="{ row }"> <template #default="{ row }">
<el-tag v-if="row.comFlag === 0" type="danger" size="small">中断</el-tag> <el-tag v-if="row.comFlag === 0" type="danger" :disable-transitions="true" size="small">中断</el-tag>
<el-tag v-if="row.comFlag === 1" type="success" size="small">正常</el-tag> <el-tag v-if="row.comFlag === 1" type="success" :disable-transitions="true" size="small">正常</el-tag>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column title="操作" width="160"> <vxe-column title="操作" width="160">

View File

@@ -53,23 +53,15 @@
修改提交 修改提交
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item style="right: 0; position: absolute; overflow: hidden"> <el-form-item style="right: 500px; position: absolute; overflow: hidden">
<div class="title">
<LocationInformation style="width: 16px; margin-right: 8px; color: #396" /> <LocationInformation style="width: 16px; margin-right: 8px; color: #396" />
<span style="font-size: 16px; font-weight: bold; color: #396">当前操作节点</span> <span style="font-size: 16px; font-weight: bold; color: #396">当前操作节点</span>
</el-form-item>
<el-form-item style="right: 0; position: absolute; overflow: hidden">
<div class="title" :class="titleList.length > 5 ? 'titleScroll' : ''">
<span v-for="(item, index) in titleList">{{ index == 0 ? '' : ' > ' }}{{ item }}</span> <span v-for="(item, index) in titleList">{{ index == 0 ? '' : ' > ' }}{{ item }}</span>
</div> </div>
<!-- <i style="font-size: 20px; color: #396" icon="el-icon-LocationInformation"></i> -->
<!-- <div style="float: right">
<el-breadcrumb separator-icon="el-icon-ArrowRightBold" style="font-weight: bold">
<el-breadcrumb-item v-for="item in titleList" :key="item">
{{ item }}
</el-breadcrumb-item>
</el-breadcrumb>
</div> -->
</el-form-item> </el-form-item>
</el-form> </el-form>
<div id="scrollBox" :style="{ height: Height.height }" style="overflow-y: auto"> <div id="scrollBox" :style="{ height: Height.height }" style="overflow-y: auto">
@@ -1609,12 +1601,11 @@
placeholder="请输入电网侧变电站" placeholder="请输入电网侧变电站"
></el-input> ></el-input>
</el-form-item> --> </el-form-item> -->
<el-form-item class="form-item" label="实际安裝粒置:"> <el-form-item class="form-item" label="实际安装位置:">
<el-select <el-select
filterable filterable
v-model="lineItem.actualArea" v-model="lineItem.actualArea"
placeholder="请选择实际安裝粒置" placeholder="请选择实际安装位置"
:disabled="pageStatus == 1" :disabled="pageStatus == 1"
style="width: 100%" style="width: 100%"
> >
@@ -2033,7 +2024,11 @@ const latff: any = ref((rule: any, value: any, callback: any) => {
} }
}) })
const plevel = ref(0) const plevel = ref(0)
const nodeDataList=ref()
const nodeEventList=ref()
const nodeClick = (e: anyObj, data: any) => { const nodeClick = (e: anyObj, data: any) => {
nodeDataList.value = data
nodeEventList.value = e
plevel.value = data.data.plevel plevel.value = data.data.plevel
treeClickCount.value++ treeClickCount.value++
if (treeClickCount.value > 2) return if (treeClickCount.value > 2) return
@@ -2355,8 +2350,10 @@ const next = async () => {
const black = () => { const black = () => {
pageStatus.value = 1 pageStatus.value = 1
busBarIndex.value = '0' busBarIndex.value = '0'
deviceIndex.value = '0'; deviceIndex.value = '0'
lineIndex.value = '0'; lineIndex.value = '0'
nodeClick(nodeEventList.value,nodeDataList.value)
} }
// 确认提交 // 确认提交
const onsubmit = () => { const onsubmit = () => {
@@ -2880,58 +2877,107 @@ const evaluate = (node: any) => {
evaluate(node.parent) evaluate(node.parent)
} }
} }
const selectChanged = (value: any) => { const selectChanged = async (value: any) => {
if (value === '3d68ceef26a579efe2fe0cdc654911b7') { let num: any = voltageLevelArr.filter(item => item.id == value)[0].value
setTheDefaultValue(10, 0.38 * 1000, 0.38 * 100) let capacity = 10
switch (num) {
//750kv case '500':
} else if (value === '4cf2d844c47a15a1c16a65b4bbfd1b0e') { capacity = 4500
setTheDefaultValue(7000, 750 * 1000, 750 * 100) break
case '330':
//6kv capacity = 3000
} else if (value === '37b81bf8aa0fd54098716da3fc0ee433') { break
setTheDefaultValue(100, 6 * 1000, 6 * 100) case '220':
capacity = 2000
//10kv break
} else if (value === 'e3da890104e3c4ae1f005021411a1fd7') { case '110':
setTheDefaultValue(100, 10 * 1000, 10 * 100) capacity = 750
break
//20kv case '35':
} else if (value === '87065e15765e5899114a6d6b9e4fb3cb') { capacity = 259
setTheDefaultValue(200, 20 * 1000, 20 * 100) break
case '10':
//35kv capacity = 100
} else if (value === '8529cfa11356a0666afd3f9fa4da09a4') { break
setTheDefaultValue(250, 35 * 1000, 35 * 100) case '6':
capacity = 100
//66kv break
} else if (value === '9ce75596a3368da4adf3374b4fc3b619') { case '0.38':
setTheDefaultValue(500, 66 * 1000, 66 * 100) capacity = 10
break
//110kv case '20':
} else if (value === 'e96d74b79bd50ad0bc00a405246f1e1f') { capacity = 200
setTheDefaultValue(750, 110 * 1000, 110 * 100) break
case '66':
//220kv capacity = 500
} else if (value === '1b7b58ed8fcc2992b95334eaa9010c41') { break
setTheDefaultValue(2000, 220 * 1000, 220 * 100) case '750':
capacity = 7000
//330kv break
} else if (value === 'c1b37350a67f5e229a1f96ace0ad04dc') { case '800':
setTheDefaultValue(3000, 330 * 1000, 330 * 100) capacity = 7000
break
//500kv case '1000':
} else if (value === '1fa650685c77db1656c70f9db4a2edc6') { capacity = 9000
setTheDefaultValue(4500, 500 * 1000, 500 * 100) break
//1000kv
} else if (value === '674cf02fb3fcfd9f99fd786cfca090df') {
setTheDefaultValue(9000, 1000 * 1000, 1000 * 100)
} else {
setTheDefaultValue(10, 10, 10)
} }
setvoltageDev(voltageLevelArr.filter(item => item.id == value)[0].value) setTimeout(() => {
setTheDefaultValue(capacity, num * 1000, num < 1 ? num * 1000 : 100)
setvoltageDev(num)
}, 0)
// // 0.38
// if (value === '3d68ceef26a579efe2fe0cdc654911b7') {
// setTheDefaultValue(10, 0.38 * 1000, 0.38 * 1000)
// //750kv
// } else if (value === '4cf2d844c47a15a1c16a65b4bbfd1b0e') {
// setTheDefaultValue(7000, 750 * 1000, 100)
// //6kv
// } else if (value === '37b81bf8aa0fd54098716da3fc0ee433') {
// setTheDefaultValue(100, 6 * 1000, 100)
// //10kv
// } else if (value === 'e3da890104e3c4ae1f005021411a1fd7') {
// setTheDefaultValue(100, 10 * 1000, 100)
// //20kv
// } else if (value === '87065e15765e5899114a6d6b9e4fb3cb') {
// setTheDefaultValue(200, 20 * 1000, 100)
// //35kv
// } else if (value === '8529cfa11356a0666afd3f9fa4da09a4') {
// setTheDefaultValue(250, 35 * 1000, 100)
// //66kv
// } else if (value === '9ce75596a3368da4adf3374b4fc3b619') {
// setTheDefaultValue(500, 66 * 1000, 100)
// //110kv
// } else if (value === 'e96d74b79bd50ad0bc00a405246f1e1f') {
// setTheDefaultValue(750, 110 * 1000, 100)
// //220kv
// } else if (value === '1b7b58ed8fcc2992b95334eaa9010c41') {
// setTheDefaultValue(2000, 220 * 1000, 100)
// //330kv
// } else if (value === 'c1b37350a67f5e229a1f96ace0ad04dc') {
// setTheDefaultValue(3000, 330 * 1000, 100)
// //500kv
// } else if (value === '1fa650685c77db1656c70f9db4a2edc6') {
// setTheDefaultValue(4500, 500 * 1000, 100)
// //1000kv
// } else if (value === '674cf02fb3fcfd9f99fd786cfca090df') {
// setTheDefaultValue(9000, 1000 * 1000, 100)
// } else {
// setTheDefaultValue(10, 1, 1)
// }
// setvoltageDev(voltageLevelArr.filter(item => item.id == value)[0].value)
} }
const setTheDefaultValue = (capacity: number, pt1: number, pt2: number) => { const setTheDefaultValue = (capacity: number, pt1: number, pt2: number) => {
@@ -3008,7 +3054,7 @@ const area = () => {
if (day >= 10) { if (day >= 10) {
day = day day = day
} else { } else {
day = '0' + (day ) day = '0' + day
} }
deviceBODetail.value.thisTimeCheck = year + '-' + month + '-' + day deviceBODetail.value.thisTimeCheck = year + '-' + month + '-' + day
deviceBODetail.value.loginTime = year + '-' + month + '-' + day deviceBODetail.value.loginTime = year + '-' + month + '-' + day
@@ -3087,9 +3133,12 @@ area()
} }
.title { .title {
width: 500px; width: 500px;
overflow: hidden;
// display: flex; // display: flex;
white-space: nowrap; white-space: nowrap;
font-weight: bold; font-weight: bold;
}
.titleScroll {
animation: scroll 10s linear infinite; /* 滚动动画 */ animation: scroll 10s linear infinite; /* 滚动动画 */
} }
@keyframes scroll { @keyframes scroll {

View File

@@ -114,7 +114,7 @@
label="装机容量MW" label="装机容量MW"
> >
{{ proviteData.ratePower }} {{ proviteData?.ratePower }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item <el-descriptions-item
label="PCC供电设备容量MVA" label="PCC供电设备容量MVA"

View File

@@ -26,10 +26,10 @@
<template #operation> <template #operation>
<el-button icon="el-icon-Plus" type="primary" @click="addFormModel">新增</el-button> <el-button icon="el-icon-Plus" type="primary" @click="addFormModel">新增</el-button>
<el-button icon="el-icon-Delete" type="primary" @click="deleteEven">删除</el-button> <el-button icon="el-icon-Delete" type="primary" @click="deleteEven">删除</el-button>
<el-button icon="el-icon-Download" type="primary" @click="exportExcelTemplate" :loading="loading"> <!-- <el-button icon="el-icon-Download" type="primary" @click="exportExcelTemplate" :loading="loading">
模板下载 模板下载
</el-button> </el-button>
<el-button icon="el-icon-Upload" type="primary" @click="importUserData">批量导入</el-button> <el-button icon="el-icon-Upload" type="primary" @click="importUserData">批量导入</el-button> -->
</template> </template>
</TableHeader> </TableHeader>
<Table ref="tableRef" /> <Table ref="tableRef" />

View File

@@ -3,12 +3,12 @@
<TableHeader datePicker area showExport> <TableHeader datePicker area showExport>
<template #select> <template #select>
<el-form-item label="统计类型:"> <!-- <el-form-item label="统计类型:">
<el-select v-model="tableStore.table.params.statisticalType" placeholder="请选择统计类型" value-key="id"> <el-select v-model="tableStore.table.params.statisticalType" placeholder="请选择统计类型" value-key="id">
<el-option v-for="item in classificationData" :key="item.id" :label="item.name" :value="item"> <el-option v-for="item in classificationData" :key="item.id" :label="item.name" :value="item">
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<el-form-item label="电压等级:"> <el-form-item label="电压等级:">
<el-select v-model="tableStore.table.params.scale" filterable multiple collapse-tags clearable <el-select v-model="tableStore.table.params.scale" filterable multiple collapse-tags clearable
placeholder="请选择电压等级" value-key="id"> placeholder="请选择电压等级" value-key="id">

View File

@@ -91,7 +91,6 @@ onMounted(() => {
}) })
const handleNodeClick = (data: any, node: any) => { const handleNodeClick = (data: any, node: any) => {
if (data.level === 6) { if (data.level === 6) {
console.log('🚀 ~ handleNodeClick ~ data:', data)
monitoringPoint.setValue('lineId', data.id) monitoringPoint.setValue('lineId', data.id)
monitoringPoint.setValue('pid', data.pids) monitoringPoint.setValue('pid', data.pids)
monitoringPoint.setValue('lineName', data.alias) monitoringPoint.setValue('lineName', data.alias)

View File

@@ -167,14 +167,14 @@ const init = () => {
if (formData.condition.length == 0) { if (formData.condition.length == 0) {
return ElMessage.warning('请选择指标类型') return ElMessage.warning('请选择指标类型')
} }
loading.value = true
formData.lineId = checked.value ? monitoringPoint.state.lineIds : [monitoringPoint.state.lineId] formData.lineId = checked.value ? monitoringPoint.state.lineIds : [monitoringPoint.state.lineId]
formData.searchBeginTime = datePickerRef.value.timeValue[0] formData.searchBeginTime = datePickerRef.value.timeValue[0]
formData.searchEndTime = datePickerRef.value.timeValue[1] formData.searchEndTime = datePickerRef.value.timeValue[1]
if (formData.lineId.length > 3) { if (formData.lineId.length > 3) {
return ElMessage.warning('最多只能选择3个监测点') return ElMessage.warning('最多只能选择3个监测点')
} }
loading.value = true
let directionValue = formData.condition.findIndex(item => { let directionValue = formData.condition.findIndex(item => {
if (item === '39') { if (item === '39') {
item = '50' item = '50'
@@ -1695,6 +1695,8 @@ const getEcharts = () => {
echarts.connect('group') echarts.connect('group')
} }
const conditionChange = () => { const conditionChange = () => {
console.log(123)
//判断一个指标时 //判断一个指标时
if (formData.condition.length == 1) { if (formData.condition.length == 1) {
if ( if (

View File

@@ -15,12 +15,7 @@
<el-col :span="12"> <el-col :span="12">
<my-echart class="tall pt10" :options="echartList" /> <my-echart class="tall pt10" :options="echartList" />
<div class="tall"> <div class="tall">
<vxe-table <vxe-table height="auto" auto-resize :data="list" v-bind="defaultAttribute">
height="auto"
auto-resize
:data="list"
v-bind="defaultAttribute"
>
<vxe-column field="gdName" title="供电公司"></vxe-column> <vxe-column field="gdName" title="供电公司"></vxe-column>
<vxe-column field="subName" title="变电站"></vxe-column> <vxe-column field="subName" title="变电站"></vxe-column>
<vxe-column field="lineName" title="监测点"></vxe-column> <vxe-column field="lineName" title="监测点"></vxe-column>
@@ -125,11 +120,11 @@ const map = (res: any) => {
visualMap: { visualMap: {
left: 26, left: 26,
bottom: 40, bottom: 40,
show: true, show: true,
color: ['#ff0000', '#37b70c'], color: ['#A52a2a', '#DAA520'],
min: minNum, min: 0,
max: maxNum, max: maxNum,
calculable: true,
textStyle: { textStyle: {
color: '#000', color: '#000',
fontSize: 12 fontSize: 12
@@ -140,13 +135,14 @@ const map = (res: any) => {
series: [ series: [
{ {
mapType: 'nanshan', mapType: 'nanshan',
type: 'heatmap',
top: 'center', top: 'center',
left: 'center', left: 'center',
width: '65%', width: '65%',
height: '95%', height: '95%',
// name: 'AQI', // name: 'AQI',
type: 'heatmap',
coordinateSystem: 'geo', coordinateSystem: 'geo',
blurSize: 40, blurSize: 40,
data: areaData data: areaData
@@ -159,6 +155,7 @@ const map = (res: any) => {
// 柱状图数据处理 // 柱状图数据处理
const histogram = (res: any) => { const histogram = (res: any) => {
echartMapList.value.visualMap.max = Math.max(...res.map((item: any) => item.count)) || 1
echartList.value = { echartList.value = {
title: { title: {
text: '区域暂降次数' text: '区域暂降次数'

View File

@@ -32,7 +32,7 @@ import router from '@/router'
import { useMonitoringPoint } from '@/stores/monitoringPoint' import { useMonitoringPoint } from '@/stores/monitoringPoint'
defineOptions({ defineOptions({
// name: 'Descentsystem/monitoringpoint' name: 'Descentsystem/monitoringpoint'
}) })
const isReload = ref(false) const isReload = ref(false)
const navigationRef = ref() const navigationRef = ref()

View File

@@ -37,9 +37,10 @@
<span>无暂降</span> <span>无暂降</span>
</div> </div>
</div> </div>
<!-- v-if="hackReset" -->
<baidu-map <baidu-map
class="bm-view" class="bm-view"
v-if="hackReset"
:zoom="zoom" :zoom="zoom"
:map-click="false" :map-click="false"
:scroll-wheel-zoom="true" :scroll-wheel-zoom="true"

View File

@@ -51,9 +51,10 @@
></el-option> ></el-option>
</el-select> </el-select>
</div> </div>
<!-- v-if="hackReset" -->
<baidu-map <baidu-map
class="bm-view" class="bm-view"
v-if="hackReset"
:zoom="zoom" :zoom="zoom"
:map-click="false" :map-click="false"
:scroll-wheel-zoom="true" :scroll-wheel-zoom="true"
@@ -158,7 +159,7 @@ import PopupEvent from './popupEvent.vue'
import router from '@/router' import router from '@/router'
defineOptions({ defineOptions({
// name: 'Descentsystem/overview' name: 'Descentsystem/overview'
}) // 添加响应式变量 }) // 添加响应式变量
const height = mainHeight(20) const height = mainHeight(20)

View File

@@ -1,12 +1,12 @@
<template> <template>
<el-dialog draggable v-model="dialogVisible" :title="title" :before-close="Cancel"> <el-dialog draggable v-model="dialogVisible" :title="title" :before-close="Cancel">
<el-form :inline="false" :model="configStore" label-width="auto"> <el-form :inline="false" :model="configStore" label-width="auto" ref="formRef" :rules="rules">
<el-divider border-style="dashed">全局</el-divider> <el-divider border-style="dashed">全局</el-divider>
<div class="layout-config-global form-two"> <div class="layout-config-global form-two">
<el-form-item label="组件主名称"> <el-form-item label="组件主名称" prop="name">
<el-input v-model="configStore.name" placeholder="请输入主题名称" /> <el-input v-model.trim="configStore.name" placeholder="请输入主题名称" maxlength="32" show-word-limit />
</el-form-item> </el-form-item>
<el-form-item label="后台页面切换动画"> <el-form-item label="后台页面切换动画" prop="mainAnimation">
<el-select v-model="configStore.mainAnimation"> <el-select v-model="configStore.mainAnimation">
<el-option label="slide-right" value="slide-right"></el-option> <el-option label="slide-right" value="slide-right"></el-option>
<el-option label="slide-left" value="slide-left"></el-option> <el-option label="slide-left" value="slide-left"></el-option>
@@ -17,22 +17,23 @@
<el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option> <el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="组件主题色"> <el-form-item label="组件主题色" prop="elementUiPrimary">
<el-color-picker v-model="configStore.elementUiPrimary[0]" /> <el-color-picker v-model="configStore.elementUiPrimary[0]" />
</el-form-item> </el-form-item>
<el-form-item label="表格标题栏背景颜色"> <el-form-item label="表格标题栏背景颜色" prop="tableHeaderBackground">
<el-color-picker v-model="configStore.tableHeaderBackground[0]" /> <el-color-picker v-model="configStore.tableHeaderBackground[0]" />
</el-form-item> </el-form-item>
<el-form-item label="表格标题栏文字颜色"> <el-form-item label="表格标题栏文字颜色" prop="tableHeaderColor">
<el-color-picker v-model="configStore.tableHeaderColor[0]" /> <el-color-picker v-model="configStore.tableHeaderColor[0]" />
</el-form-item> </el-form-item>
<el-form-item label="表格激活栏颜色"> <el-form-item label="表格激活栏颜色" prop="tableCurrent">
<el-color-picker v-model="configStore.tableCurrent[0]" /> <el-color-picker v-model="configStore.tableCurrent[0]" />
</el-form-item> </el-form-item>
<el-form-item label="组件主描述"> <el-form-item label="组件主描述" prop="remark">
<el-input <el-input
v-model="configStore.remark" v-model.trim="configStore.remark"
:autosize="{ minRows: 2, maxRows: 4 }" :autosize="{ minRows: 2, maxRows: 4 }"
maxlength="300" show-word-limit
type="textarea" type="textarea"
placeholder="请输入描述" placeholder="请输入描述"
/> />
@@ -40,32 +41,32 @@
</div> </div>
<el-divider border-style="dashed">侧边栏</el-divider> <el-divider border-style="dashed">侧边栏</el-divider>
<div class="layout-config-aside form-two"> <div class="layout-config-aside form-two">
<el-form-item label="侧边菜单栏背景色"> <el-form-item label="侧边菜单栏背景色" prop="menuBackground">
<el-color-picker v-model="configStore.menuBackground[0]" /> <el-color-picker v-model="configStore.menuBackground[0]" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单文字颜色"> <el-form-item label="侧边菜单文字颜色" prop="menuColor">
<el-color-picker v-model="configStore.menuColor[0]" /> <el-color-picker v-model="configStore.menuColor[0]" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单激活项背景色"> <el-form-item label="侧边菜单激活项背景色" prop="menuActiveBackground">
<el-color-picker v-model="configStore.menuActiveBackground[0]" /> <el-color-picker v-model="configStore.menuActiveBackground[0]" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单激活项文字色"> <el-form-item label="侧边菜单激活项文字色" prop="menuActiveColor">
<el-color-picker v-model="configStore.menuActiveColor[0]" /> <el-color-picker v-model="configStore.menuActiveColor[0]" />
</el-form-item> </el-form-item>
<el-form-item label="侧边菜单顶栏背景色"> <el-form-item label="侧边菜单顶栏背景色" prop="menuTopBarBackground">
<el-color-picker v-model="configStore.menuTopBarBackground[0]" /> <el-color-picker v-model="configStore.menuTopBarBackground[0]" />
</el-form-item> </el-form-item>
</div> </div>
<el-divider border-style="dashed">顶栏</el-divider> <el-divider border-style="dashed">顶栏</el-divider>
<div class="layout-config-aside form-two"> <div class="layout-config-aside form-two">
<el-form-item label="顶栏背景色"> <el-form-item label="顶栏背景色" prop="headerBarBackground">
<el-color-picker v-model="configStore.headerBarBackground[0]" /> <el-color-picker v-model="configStore.headerBarBackground[0]" />
</el-form-item> </el-form-item>
<el-form-item label="顶栏文字色"> <el-form-item label="顶栏文字色" prop="headerBarTabColor">
<el-color-picker v-model="configStore.headerBarTabColor[0]" /> <el-color-picker v-model="configStore.headerBarTabColor[0]" />
</el-form-item> </el-form-item>
<el-form-item label="顶栏logo"> <el-form-item label="顶栏logo" prop="logoFile">
<el-image <el-image
style="height: 50px" style="height: 50px"
:src="logoFile.url" :src="logoFile.url"
@@ -133,11 +134,176 @@ const configStore = ref({
faviconFile: '', faviconFile: '',
remark: '' remark: ''
}) })
// 完善校验规则
const rules = reactive({
name: [{ required: true, message: '请输入组件主名称', trigger: 'blur' }],
remark: [{ required: true, message: '请输入组件备注', trigger: 'blur' }],
mainAnimation: [{ required: true, message: '请选择页面切换动画', trigger: 'change' }],
elementUiPrimary: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择组件主题色'))
} else {
callback()
}
},
trigger: 'change'
}
],
tableHeaderBackground: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择表格标题栏背景颜色'))
} else {
callback()
}
},
trigger: 'change'
}
],
tableHeaderColor: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择表格标题栏文字颜色'))
} else {
callback()
}
},
trigger: 'change'
}
],
tableCurrent: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择表格激活栏颜色'))
} else {
callback()
}
},
trigger: 'change'
}
],
menuBackground: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择侧边菜单栏背景色'))
} else {
callback()
}
},
trigger: 'change'
}
],
menuColor: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择侧边菜单文字颜色'))
} else {
callback()
}
},
trigger: 'change'
}
],
menuActiveBackground: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择侧边菜单激活项背景色'))
} else {
callback()
}
},
trigger: 'change'
}
],
menuActiveColor: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择侧边菜单激活项文字色'))
} else {
callback()
}
},
trigger: 'change'
}
],
menuTopBarBackground: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择侧边菜单顶栏背景色'))
} else {
callback()
}
},
trigger: 'change'
}
],
headerBarBackground: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择顶栏背景色'))
} else {
callback()
}
},
trigger: 'change'
}
],
headerBarTabColor: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value || !value[0]) {
callback(new Error('请选择顶栏文字色'))
} else {
callback()
}
},
trigger: 'change'
}
],
logoFile: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
// 编辑时如果已有图片则不校验
if (title.value === '修改主题' && logoFile.url) {
callback()
} else if (!value && !logoFile.url) {
callback(new Error('请上传顶栏logo'))
} else {
callback()
}
},
trigger: 'change'
}
]
})
const logoFile = reactive({ const logoFile = reactive({
url: '', url: '',
raw: '' raw: ''
}) })
const formRef = ref()
const open = (e: any) => { const open = (e: any) => {
title.value = e.text title.value = e.text
@@ -167,6 +333,8 @@ const open = (e: any) => {
} }
// 确定主题 // 确定主题
const onSubmit = () => { const onSubmit = () => {
formRef.value.validate((valid: boolean) => {
if (valid) {
configStore.value.faviconFile = configStore.value.logoFile configStore.value.faviconFile = configStore.value.logoFile
configStore.value.color = configStore.value.elementUiPrimary[0] configStore.value.color = configStore.value.elementUiPrimary[0]
let form = new FormData() let form = new FormData()
@@ -211,6 +379,8 @@ const onSubmit = () => {
Cancel() Cancel()
}) })
} }
}
})
} }
// 取消 // 取消
const Cancel = () => { const Cancel = () => {