cepianyi-ui/src/views/equipment/equipmentDetails.vue
2025-06-04 13:25:40 +08:00

893 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="equipment-container">
<div class="data-overview">
<div class="data-left">
<div class="equipment-name">设备名称设备编码</div>
<el-button type="info" size="small">离线</el-button>
<div style="margin-left:16px;color:#606266">时间显示</div>
</div>
<div class="action-buttons">
<div class="button-group">
<!-- <el-button-->
<!-- type="primary"-->
<!-- link-->
<!-- @click="handleShowImage()"-->
<!-- >-->
<!-- {{proxy.$t('equipment.imagePage')}}-->
<!-- </el-button>-->
<el-button
:type="mode === 'fast' ? 'primary' : ''"
@click="handleModeChange('fast')"
>
<el-icon><Timer /></el-icon>
快速模式
</el-button>
<el-button
plain
:type="xRayStatus ? 'danger' : 'success'"
@click="handleXRayToggle"
>
X{{proxy.$t('equipment.Ray')}}{{ xRayStatus ? proxy.$t('common.open') : proxy.$t('common.close') }}
</el-button>
<el-button
type="primary"
plain
@click="handleGenerateReport"
>
{{proxy.$t('equipment.reporting')}}
</el-button>
<el-button
type="success"
plain
@click="handleDeviceShare"
>
{{proxy.$t('equipment.deviceShare')}}
</el-button>
</div>
</div>
</div>
<div class="chart-container">
<div class="chart-box">
<el-tabs
v-model="activeName"
type="card"
class="demo-tabs"
@tab-click="handleClick"
>
<el-tab-pane label="数据组1" name="first">
<div class="card-container">
<div class="card-list">
<div class="eccentricity-card" v-for="(item, index) in cardList" :key="index">
<div class="card-header">
<span class="title">{{item.title}}</span>
<el-icon class="close-icon" @click="deleteCard(index)"><Close /></el-icon>
</div>
<div class="card-content">
<div class="card-text">
<div>
<div class="difference">
<span class="plus">收缩率</span>
<span class="label">9%</span>
</div>
<div class="difference">
<span class="plus">+</span>
<span class="label">公差</span>
</div>
</div>
<div class="value">
21.15<span class="unit">%</span>
</div>
</div>
<div class="tolerance-section">
<el-button
type="primary"
plain
class="switch-btn left"
@click="handlePrevious"
>
<el-icon><ArrowLeft /></el-icon>
</el-button>
<div class="tolerance-text">
偏心度公差 {{ toleranceList[currentIndex] }}
</div>
<el-button
type="primary"
plain
class="switch-btn right"
@click="handleNext"
>
<el-icon><ArrowRight /></el-icon>
</el-button>
</div>
</div>
</div>
<div class="eccentricity-card" @click="openDialog">
<el-icon><Plus></Plus></el-icon>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="数据组2" name="second">数据组2</el-tab-pane>
<el-tab-pane label="数据组3" name="third">数据组3</el-tab-pane>
</el-tabs>
</div>
</div>
<div class="chart-container">
<div class="chart-box">
<el-tabs
v-model="activeTwoName"
type="card"
class="demo-tabs"
@tab-click="handleClick"
>
<el-tab-pane :label="proxy.$t('equipment.sketch')" name="first">
<div class="detection-section">
<el-radio-group v-model="selectedLayer" class="layer-select">
<el-radio label="outer">外屏蔽层</el-radio>
<el-radio label="middle">绝缘层</el-radio>
<el-radio label="inner">内屏蔽层</el-radio>
</el-radio-group>
<!-- <CircleDetectionChart-->
<!-- :inner-data="innerLayerData"-->
<!-- :middle-data="middleLayerData"-->
<!-- :outer-data="outerLayerData"-->
<!-- />-->
<CircleDetectionChart :selected-layer="selectedLayer" />
<!-- <CircleDetectionChart :eccentricity="0.08" :thickness="20" />-->
<div class="history-content">
<div>左边指南针</div>
<div class="history-right">
<div>
<el-button v-if="showHistory" type="primary" @click="showHistoryDialog">选择历史数据</el-button>
<el-switch
v-model="showHistory"
size="large"
style="margin-left: 6px"
:inactive-text="proxy.$t('equipment.historyData')"
/>
</div>
<div class="scanItem">扫描时间2025/01/07 04:34:8</div>
<div class="scanItem">2025-01-07_04_30_34_866_direction_1_device2.json</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="proxy.$t('equipment.deviceInfo')" name="second">
<div class="device-status">
<DeviceStatus v-if="!showBtn" :status-data="deviceStatus" />
<DeviceSetting v-if="showBtn" @update="handleSettingUpdate" />
</div>
</el-tab-pane>
</el-tabs>
<div class="btn" v-if="activeTwoName === 'second'">
<el-button v-if="!showBtn" type="primary" @click="paramsHandle">{{proxy.$t('equipment.paramsSetting')}}</el-button>
<el-button v-if="showBtn" type="primary" @click="paramsHandle">{{proxy.$t('button.return')}}</el-button>
</div>
</div>
</div>
<div class="chart-container" style="margin-top: 14px">
<div class="chart-box">
<el-tabs
v-model="activeThreeName"
type="card"
class="demo-tabs"
@tab-click="handleClick"
>
<el-tab-pane :label="proxy.$t('equipment.deviceTemperature')" name="first">
<TemperatureChart :chart-data="temperatureData"></TemperatureChart>
</el-tab-pane>
<el-tab-pane :label="proxy.$t('equipment.warningInfo')" name="second">
<div class="warning-container">
<div class="warning-item" v-for="(item, index) in warningList" :key="index">
{{item.title}}
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="proxy.$t('equipment.imageView')" name="third">
<div class="controls">
<el-button-group>
<el-button
type="primary"
plain
class="switch-btn right"
@click="handleImagePrev"
>
<el-icon><ArrowLeft /></el-icon>
</el-button>
<el-button
type="primary"
plain
class="switch-btn right switch"
@click="handleImageNext"
>
<el-icon><ArrowRight /></el-icon>
</el-button>
</el-button-group>
</div>
<div style="display: flex">
<div v-if="showRed">
<CurveChart ref="bowlCurveRef" :title="chartTitle.left" :color="colorRed"/>
<CurveChart ref="bowlCurveRef" :title="chartTitle.right" :color="colorRed"/>
</div>
<div v-if="showGreen">
<CurveChart ref="bowlCurveRef" :title="chartTitle.left" :color="colorGreen"/>
<CurveChart ref="bowlCurveRef" :title="chartTitle.right" :color="colorGreen"/>
</div>
<div v-if="showRedGreen">
<DualChannelChart
title="扫描臂1"
ref="dualChart1Ref"
:chart-data="arm1ChartData"
/>
<DualChannelChart
title="扫描臂2"
ref="dualChart2Ref"
:chart-data="arm2ChartData"
/>
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="proxy.$t('equipment.trendChart')" name="four">
<RealtimeChart ref="realtimeChartRef" />
</el-tab-pane>
</el-tabs>
</div>
<!-- <div class="chart-box chart-bottom-right">-->
<!-- <div class="header">-->
<!-- <div>-->
<!-- <span>{{proxy.$t('equipment.imagePage')}}</span>-->
<!-- <span class="time">{{ currentTime }}</span>-->
<!-- </div>-->
<!-- <el-divider/>-->
<!-- </div>-->
<!-- <div class="imagePage-container">-->
<!-- <div class="imagePage-left">-->
<!-- <DataTable :table-data="tableData" />-->
<!-- </div>-->
<!-- <div class="charts">-->
<!-- <div class="controls">-->
<!-- <el-button-group>-->
<!-- <el-button type="primary" plain class="switch-btn" @click="handleImagePagePrev"><el-icon><ArrowLeft /></el-icon></el-button>-->
<!-- <el-button type="primary" plain class="switch-btn switch" @click="handleImagePageNext"><el-icon><ArrowRight /></el-icon></el-button>-->
<!-- <el-button type="primary" plain class="switch-btn switch" @click="handleRefresh"><el-icon><Refresh /></el-icon></el-button>-->
<!-- </el-button-group>-->
<!-- </div>-->
<!-- <div v-if="showImagePageRed">-->
<!-- <CurveChart ref="bowlCurvePageRef" :title="chartTitle.left" :color="colorRed"/>-->
<!-- <CurveChart ref="bowlCurvePageRef" :title="chartTitle.right" :color="colorRed"/>-->
<!-- </div>-->
<!-- <div v-if="showImagePageGreen">-->
<!-- <CurveChart ref="bowlCurvePageRef" :title="chartTitle.left" :color="colorGreen"/>-->
<!-- <CurveChart ref="bowlCurvePageRef" :title="chartTitle.right" :color="colorGreen"/>-->
<!-- </div>-->
<!-- <div v-if="showImagePageRedGreen">-->
<!-- <DualChannelChart-->
<!-- title="扫描臂1"-->
<!-- ref="dualChart1PageRef"-->
<!-- :chart-data="arm1ChartData"-->
<!-- />-->
<!-- <DualChannelChart-->
<!-- title="扫描臂2"-->
<!-- ref="dualChart2PageRef"-->
<!-- :chart-data="arm2ChartData"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
<!-- 历史数据弹框-->
<HistoryDataDialog
ref="historyDialogRef"
@confirm="handleHistoryConfirm"
@cancel="handleHistoryCancel"
/>
<!-- 添加数据弹框-->
<AddMonitorData ref="addMonitorRef" @confirm="handleDataConfirm" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {getDeviceDetailApi} from "@/api/equipment/index.js";
import {Close, ArrowLeft, ArrowRight, Timer, Refresh, Plus} from '@element-plus/icons-vue'
import TemperatureChart from './common/TemperatureChart.vue'
import RealtimeChart from './common/RealtimeChart.vue';
import DeviceStatus from './common/DeviceStatus.vue';
import DeviceSetting from './common/DeviceSetting.vue';
import DualChannelChart from './common/DualChannelChart.vue';
import CurveChart from './common/CurveChart.vue';
import CircleDetectionChart from "./common/CircleDetectionChart.vue";
import HistoryDataDialog from './common/HistoryDataDialog.vue';
import {ElMessageBox, ElMessage} from "element-plus";
import { useMqtt } from '@/utils/mqttClient.js';
import {useRoute} from 'vue-router';
import AddMonitorData from './common/AddMonitorData.vue'
const {
connected,
messageData,
connect,
subscribe,
disconnect,
publish
} = useMqtt()
const deviceInfo = ref(null)
const deviceData = ref(null)
const { proxy } = getCurrentInstance();
const route = useRoute()
const deviceId = ref(null)
const selectedLayer = ref('outer')
const toleranceList = ['15%', '20%', '25%']
const showRed = ref(true)
const showGreen = ref(false)
const showRedGreen = ref(false)
const showImagePageRed = ref(true)
const showImagePageGreen = ref(false)
const showImagePageRedGreen = ref(false)
const colorRed = ref('#ff4444')
const colorGreen = ref('#67c23a')
const currentIndex = ref(0)
const showHistory = ref(false)
const mode = ref('normal')
const realtimeChartRef = ref(null)
const dualChart1Ref = ref(null)
const dualChart2Ref = ref(null)
const dualChart1PageRef = ref(null)
const dualChart2PageRef = ref(null)
const bowlCurvePageRef = ref(null)
const xRayStatus = ref(false)
const activeName = ref('first')
const activeTwoName = ref('first')
const activeThreeName = ref('first')
const bowlCurveRef = ref(null)
const showBtn = ref(false)
const historyDialogRef = ref(null)
const currentTime = ref('2021-02-11 13:23:39')
const chartTitle = ref({
left: '扫描臂1 曲线',
right: '扫描臂2 曲线'
})
const cardList = ref([
{ title: '外屏层偏心度' },
{ title: '内屏层偏心度' },
{ title: '绝缘层偏心度' },
{ title: '外屏层厚度' },
{ title: '内屏层厚度' },
// { title: '绝缘层厚度' }
])
const warningList = ref([
{ title: '设备1离线', status: 'offline' },
{ title: '设备2离线', status: 'offline' },
{ title: '设备3离线', status: 'offline' }
])
// 示意图数据
const innerLayerData = ref([0.68, 0.85, 1.07, 1.18, 1.13, 0.97, 0.67, 0.98])
const middleLayerData = ref([9.52, 9.95, 10.53, 11.04, 11.34, 11.15, 9.66, 10.40])
const outerLayerData = ref([1.15, 1.19, 1.21, 1.17, 1.05, 0.96, 1.06, 0.79])
// 温度数据
const temperatureData = ref({
arm1: [33, 46, 57, 24, 48, 51, 33],
arm2: [11, 24, 39, 13, 48, 41, 33],
times: [
'2023-01-01 09:00:00',
'2023-01-01 10:00:00',
'2023-01-01 11:00:00',
'2023-01-01 12:00:00',
'2023-01-01 13:00:00',
'2023-01-01 14:00:00'
]
})
const deviceStatus = ref({
arm1: {
communication: '正常',
fault: '正常',
enable: '未使用',
position: '下过渡段',
speed: '0',
powerSwitch: { value: '关闭', type: 'danger' },
safetyLock: '打开',
voltageSet: '55000',
voltageFeedback: '15',
currentFeedback: '2',
currentSet: '1800'
},
arm2: {
communication: '正常',
fault: '正常',
enable: '未使用',
position: '下过渡段',
speed: '0',
powerSwitch: { value: '关闭', type: 'danger' },
safetyLock: '打开',
voltageSet: '55000',
voltageFeedback: '0',
currentFeedback: '2',
currentSet: '1800'
}
})
const addMonitorRef = ref(null)
// 表格数据
const tableData = ref([
{ name: '射线电源反馈电压[V]', arm1: '15', arm2: '0' },
{ name: '射线电源设定电压[V]', arm1: '55000', arm2: '55000' },
// ... 其他数据
])
// 图表数据
const arm1ChartData = ref({
a: Array.from({ length: 100 }, () => Math.random() * 9000),
b: Array.from({ length: 100 }, () => Math.random() * 9000)
})
const arm2ChartData = ref({
a: Array.from({ length: 100 }, () => Math.random() * 9000),
b: Array.from({ length: 100 }, () => Math.random() * 9000)
})
// 打开新增设备弹框
const openDialog = () => {
addMonitorRef.value.showDialog()
}
const handleDataConfirm = (dataType) => {
console.log('选择的数据项:', dataType)
// 这里处理选择的数据项
}
// mqtt连接
const mqttConnect = () => {
connect('ws://123.57.81.127:8085/mqtt', { // 添加 /mqtt 路径
clientId: 'vue-client-' + Math.random().toString(16).substring(2, 8),
username: 'cepianyi',
password: 'cpy123',
protocol: 'ws',
protocolVersion: 4,
keepalive: 60,
reconnectPeriod: 1000,
connectTimeout: 30 * 1000,
path: '/mqtt'// 指定 WebSocket 路径
})
}
const deleteCard = (index) => {
ElMessageBox.confirm(
'确认删除该卡片吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
cardList.value.splice(index, 1)
ElMessage.success('删除成功')
}).catch(() => {
// 用户取消删除
})
}
// 打开历史数据弹窗
const showHistoryDialog = () => {
historyDialogRef.value.show()
}
const handleHistoryConfirm = (data) => {
console.log('选择的历史数据:', data)
}
const handleHistoryCancel = () => {
console.log('取消选择')
}
// 图像预览
const handleImageNext = () => {
if (showRedGreen.value) {
showRedGreen.value = false
showRed.value = true
showGreen.value = false
} else if (showRed.value) {
showRedGreen.value = false
showRed.value = false
showGreen.value = true
} else if (showGreen.value) {
showRedGreen.value = true
showRed.value = false
showGreen.value = false
}
}
const handleImagePrev = () => {
if (showRedGreen.value) {
showRedGreen.value = false
showRed.value = false
showGreen.value = true
} else if (showGreen.value) {
showRedGreen.value = false
showRed.value = true
showGreen.value = false
} else if (showRed.value) {
showRedGreen.value = true
showRed.value = false
showGreen.value = false
}
}
const generateData = (type) => {
const data = []
for (let i = 0; i <= 90; i++) {
let value
if (i < 45) {
value = type === 'a' ? 8500 : 7500
} else if (i >= 45 && i < 60) {
value = 8500 - ((i - 45) * 500)
} else if (i >= 60 && i < 75) {
value = 1000 + ((i - 60) * 500)
} else {
value = type === 'a' ? 8500 : 7500
}
data.push([i, value])
}
arm1ChartData.value = data
}
// 标签页切换
const handleClick = (tab, event) => {
console.log(tab.props.label,'----')
nextTick(() => {
if(tab.props.label === '趋势图') {
realtimeChartRef.value?.resize()
}
if(tab.props.label === '图像预览') {
bowlCurveRef.value?.resize()
}
})
}
const handlePrevious = () => {
if (currentIndex.value > 0) {
currentIndex.value--
}
}
const handleNext = () => {
if (currentIndex.value < toleranceList.length - 1) {
currentIndex.value++
}
}
// 设备信息
const paramsHandle = () => {
showBtn.value = !showBtn.value
}
// 参数设定按钮
const handleSettingUpdate = ({ key, value }) => {
console.log('更新设置:', key, value)
// 调用接口更新设置
}
// 图像预览
const handleDataUpdate = (data) => {
console.log('数据更新:', data)
}
// 快速模式
const handleModeChange = (newMode) => {
mode.value = newMode
}
//x射线
const handleXRayToggle = () => {
ElMessageBox.confirm(
'是否确认切换X射线状态',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// 这里可以调用接口来切换X射线状态
xRayStatus.value = !xRayStatus.value
}).catch(() => {
// 用户取消操作
})
}
// 生成报告
const handleGenerateReport = () => {
}
// 设备分享
const handleDeviceShare = () => {
}
// 获取设备详细信息
const getDeviceDetail = async () => {
try {
const response = await getDeviceDetailApi(deviceId.value)
console.log('设备信息:', deviceInfo.value)
} catch (error) {
console.error('获取设备信息失败:', error)
}
}
onMounted(() => {
if(route.query.id) {
deviceId.value= route.query.id;
mqttConnect()
}
generateData()
})
onBeforeUnmount(() => {
disconnect()
})
</script>
<style scoped lang="scss">
.equipment-container {
padding: 20px;
h3 {
margin: 0 0 20px;
font-size: 16px;
font-weight: 500;
}
.data-overview {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
.data-left {
display: flex;
align-items: center;
}
}
.data-card {
flex: 1;
display: flex;
padding: 20px;
background: #fff;
border-radius: 4px;
align-items: center;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
.img-icon {
border:1px dashed #ccc;
margin-right: 10px;
}
.number {
font-size: 28px;
font-weight: bold;
margin-bottom: 10px;
color:#333333;
}
.label {
color: #999;
font-size: 14px;
}
}
.chart-container {
display: flex;
gap: 20px;
.chart-box {
flex:1;
position: relative;
padding: 14px;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
.btn {
position: absolute;
top:14px;
right: 14px;
}
}
}
}
.action-buttons {
padding: 10px 0;
.button-group {
display: flex;
gap: 10px;
.el-button {
display: flex;
align-items: center;
gap: 5px;
&.el-button--danger {
background-color: #f56c6c;
border-color: #f56c6c;
color: #fff;
&:hover {
background-color: #f78989;
border-color: #f78989;
}
}
}
}
}
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
.eccentricity-card {
background-color: #F5F7FA;
border-radius: 4px;
padding: 14px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
border-bottom:1px solid #ccc;
margin-bottom: 10px;
.title {
font-size: 14px;
color: #333;
font-weight: 600;
}
.close-icon {
cursor: pointer;
color: #999;
}
}
.card-content {
.card-text {
display: flex;
justify-content: space-between;
}
.difference {
margin-bottom: 6px;
font-size: 14px;
.plus {
color: #f56c6c;
margin-right: 5px;
}
.label {
color: #f56c6c;
}
}
.value {
font-size: 32px;
font-weight: bold;
color: red;
margin-bottom: 15px;
.unit {
font-size: 16px;
margin-left: 5px;
}
}
.tolerance-section {
display: flex;
align-items: center;
justify-content: space-between;
background: #E5ECF6 ;
padding: 8px;
border-radius: 4px;
.tolerance-text {
color: #666;
font-size: 14px;
}
}
}
}
.card-container {
.card-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
border-radius: 4px;
}
}
.chart-bottom-left {
width:38%;
}
.chart-bottom-right {
flex:1
}
.warning-container {
padding-left:10px;
.warning-item {
color:red;
margin-bottom: 10px;
}
}
.device-status {
background: #fff;
border-radius: 4px;
height: 346px;
overflow-y: scroll;
:deep(.el-table) {
.el-tag {
width: 100%;
justify-content: center;
&.el-tag--danger {
background-color: #fef0f0;
}
}
.cell {
padding: 8px 0;
}
}
}
.header {
//display: flex;
//justify-content: space-between;
//align-items: center;
//margin-bottom: 14px;
.time {
font-size: 14px;
color: #606266;
margin-left: 6px;
}
}
.imagePage-container {
display: flex;
.imagePage-left {
width: 52%;
}
}
.charts {
//display: flex;
//gap: 20px;
padding: 0 6px;
flex:1;
//
//> div {
// flex: 1;
//}
}
.controls {
display: flex;
justify-content: flex-end;
.switch {
margin-left: 5px;
}
}
.switch-btn {
padding: 5px;
//&:hover {
// background: #f0f9eb;
//}
}
.detection-section {
padding: 20px;
background: #fff;
border-radius: 4px;
}
.history-content {
display: flex;
justify-content: space-between;
align-items: center;
.history-right {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.scanItem {
color:#666;
font-size: 12px;
margin-bottom: 6px;
}
}
</style>