|
|
@@ -1,188 +1,503 @@
|
|
|
<template>
|
|
|
<div class="safety-container">
|
|
|
- <div class="tab-bar">
|
|
|
- <div class="tab-item" :class="{ active: currentTab === 'dam' }" @click="currentTab = 'dam'">大坝整体</div>
|
|
|
- <div class="tab-item" :class="{ active: currentTab === 'spillway' }" @click="currentTab = 'spillway'">泄洪设施</div>
|
|
|
- <div class="tab-item" :class="{ active: currentTab === 'conveyance' }" @click="currentTab = 'conveyance'">输水设施</div>
|
|
|
- <div class="tab-item" :class="{ active: currentTab === 'bank' }" @click="currentTab = 'bank'">库岸稳定</div>
|
|
|
- </div>
|
|
|
<div class="safety-scroll-area">
|
|
|
- <div v-if="currentTab === 'dam'" class="tab-content">
|
|
|
+ <div class="tab-content">
|
|
|
<div class="left-sidebar">
|
|
|
- <div class="data-card">
|
|
|
+ <div class="data-card tall-card">
|
|
|
<div class="card-header">
|
|
|
- <h3 class="card-title">大坝变形监测</h3>
|
|
|
- <div class="header-actions"><span class="update-time">单位:mm</span></div>
|
|
|
+ <h3 class="card-title">安全状态评估</h3>
|
|
|
</div>
|
|
|
<div class="card-body chart-body">
|
|
|
- <div class="monitor-chart-box">
|
|
|
- <div class="monitor-chart-label">变形趋势</div>
|
|
|
- <canvas id="deformationChart" width="320" height="170"></canvas>
|
|
|
- <div class="chart-legend">
|
|
|
- <span class="legend-dot dot-horizontal"></span>水平位移
|
|
|
- <span class="legend-dot dot-vertical" style="margin-left:12px;"></span>垂直位移
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="water-stats-row" style="margin-top:4px;">
|
|
|
- <div class="water-stat-card">
|
|
|
- <div class="water-stat-label">最大水平位移</div>
|
|
|
- <div class="water-stat-value">12.5</div>
|
|
|
+ <div class="safety-eval-section">
|
|
|
+ <div class="safety-eval-top">
|
|
|
+ <span class="safety-eval-section-title">综合安全等级</span>
|
|
|
+ <span class="safety-eval-grade grade-a">一类坝</span>
|
|
|
</div>
|
|
|
- <div class="water-stat-card">
|
|
|
- <div class="water-stat-label">最大垂直位移</div>
|
|
|
- <div class="water-stat-value">8.3</div>
|
|
|
+ <div class="safety-eval-sub-row">
|
|
|
+ <div class="safety-eval-sub-card">
|
|
|
+ <span class="eval-sub-label">溢洪道侵占</span>
|
|
|
+ <span class="eval-sub-value">无</span>
|
|
|
+ </div>
|
|
|
+ <div class="safety-eval-sub-card">
|
|
|
+ <span class="eval-sub-label">近坝库岸状态</span>
|
|
|
+ <span class="eval-sub-value status-ok">稳定</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="data-card mt-10">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">渗流监测</h3>
|
|
|
- <div class="header-actions"><span class="update-time">单位:L/s</span></div>
|
|
|
- </div>
|
|
|
- <div class="card-body chart-body">
|
|
|
- <div class="water-stats-row">
|
|
|
- <div class="water-stat-card">
|
|
|
- <div class="water-stat-label">渗流量</div>
|
|
|
- <div class="water-stat-value">3.5</div>
|
|
|
+ <div class="safety-eval-mid">
|
|
|
+ <div class="safety-eval-item">
|
|
|
+ <span class="eval-item-name">防洪安全</span>
|
|
|
+ <span class="eval-item-tag tag-ok">正常</span>
|
|
|
+ </div>
|
|
|
+ <div class="safety-eval-item">
|
|
|
+ <span class="eval-item-name">渗流安全</span>
|
|
|
+ <span class="eval-item-tag tag-ok">正常</span>
|
|
|
+ </div>
|
|
|
+ <div class="safety-eval-item">
|
|
|
+ <span class="eval-item-name">坝体变形</span>
|
|
|
+ <span class="eval-item-tag tag-ok">正常</span>
|
|
|
+ </div>
|
|
|
+ <div class="safety-eval-item">
|
|
|
+ <span class="eval-item-name">设备运行</span>
|
|
|
+ <span class="eval-item-tag tag-warn">预警</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="water-stat-card">
|
|
|
- <div class="water-stat-label">坝基扬压力</div>
|
|
|
- <div class="water-stat-value">18.2</div>
|
|
|
+ <div class="safety-eval-bot">
|
|
|
+ <div class="safety-eval-stat">
|
|
|
+ <span class="eval-stat-dot dot-ok"></span>正常 322
|
|
|
+ </div>
|
|
|
+ <div class="safety-eval-stat">
|
|
|
+ <span class="eval-stat-dot dot-warn"></span>预警 8
|
|
|
+ </div>
|
|
|
+ <div class="safety-eval-stat">
|
|
|
+ <span class="eval-stat-dot dot-err"></span>故障 3
|
|
|
+ </div>
|
|
|
+ <span class="safety-eval-time">最近巡检 2026-05-28</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="monitor-chart-box" style="margin-top:4px;">
|
|
|
- <div class="monitor-chart-label">渗流趋势</div>
|
|
|
- <canvas id="seepageChart" width="320" height="170"></canvas>
|
|
|
- <div class="chart-legend">
|
|
|
- <span class="legend-dot dot-seepage"></span>渗流量
|
|
|
- <span class="legend-dot dot-pressure" style="margin-left:12px;"></span>渗流压力(kPa)
|
|
|
+ <div class="device-stats-row">
|
|
|
+ <div class="device-stat-card">
|
|
|
+ <div class="device-stat-percent">89.55%</div>
|
|
|
+ <div class="device-stat-icon"><img src="/src/assets/images/小图标库水位.png" class="stat-icon-img" /></div>
|
|
|
+ <div class="device-stat-label">完好率</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="right-sidebar">
|
|
|
- <div class="data-card">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">安全状态评估</h3>
|
|
|
- </div>
|
|
|
- <div class="card-body chart-body">
|
|
|
- <div class="safety-assessment-row">
|
|
|
- <div class="safety-pie-section">
|
|
|
- <canvas id="statusPieChart" width="140" height="140"></canvas>
|
|
|
- <div class="pie-legend">
|
|
|
- <div class="pie-legend-item" v-for="item in pieData" :key="item.name">
|
|
|
- <span class="pie-dot" :style="{ background: item.color }"></span>
|
|
|
- <span class="pie-name">{{ item.name }}</span>
|
|
|
- <span class="pie-count">{{ item.count }}个</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div class="device-gauge-section">
|
|
|
+ <canvas id="deviceGaugeChart" width="140" height="150"></canvas>
|
|
|
</div>
|
|
|
- <div class="safety-assessment-section">
|
|
|
- <div class="safety-assessment-bar">
|
|
|
- <div class="safety-assessment-grade">一类坝</div>
|
|
|
- <div class="safety-assessment-texts">
|
|
|
- <div class="safety-assessment-text">评估时间:2026-05-15</div>
|
|
|
- <div class="safety-assessment-text">运行正常,安全可靠</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="safety-assessment-tags">
|
|
|
- <div class="assess-tag good" v-for="item in assessmentIndicators" :key="item">{{ item }}</div>
|
|
|
- </div>
|
|
|
+ <div class="device-stat-card">
|
|
|
+ <div class="device-stat-percent">84.36%</div>
|
|
|
+ <div class="device-stat-icon"><img src="/src/assets/images/小图标库水位.png" class="stat-icon-img" /></div>
|
|
|
+ <div class="device-stat-label">运行期 完好率</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="data-card mt-10">
|
|
|
- <div class="card-header">
|
|
|
- <h3 class="card-title">监测点状态</h3>
|
|
|
- <div class="header-actions"><span class="update-time">共 {{ monitorList.length }} 个测点</span></div>
|
|
|
- </div>
|
|
|
+ <div class="monitor-divider"></div>
|
|
|
<div class="card-body card-body-tight">
|
|
|
- <div class="monitor-table">
|
|
|
- <div class="monitor-table-header">
|
|
|
- <span class="col-name">测点名称</span>
|
|
|
- <span class="col-value">当前值</span>
|
|
|
- <span class="col-status">状态</span>
|
|
|
- <span class="col-trend">趋势</span>
|
|
|
+ <div class="monitor-summary-row">
|
|
|
+ <div class="monitor-summary-card" :class="{ active: activeSummaryCard === item.name }" v-for="item in monitorSummaryList" :key="item.name" @click="toggleSummaryCard(item.name)">
|
|
|
+ <svg class="progress-ring" width="48" height="48" viewBox="0 0 48 48">
|
|
|
+ <circle class="progress-ring-bg" cx="24" cy="24" r="20" />
|
|
|
+ <circle class="progress-ring-fill" cx="24" cy="24" r="20"
|
|
|
+ :stroke-dasharray="2 * Math.PI * 20"
|
|
|
+ :stroke-dashoffset="2 * Math.PI * 20 * (1 - item.progress / 100)"
|
|
|
+ :stroke="item.color" />
|
|
|
+ <text x="24" y="24" text-anchor="middle" dominant-baseline="central"
|
|
|
+ :fill="item.color" font-size="14" font-weight="bold">{{ item.progress }}%</text>
|
|
|
+ </svg>
|
|
|
+ <span class="monitor-summary-name">{{ item.name }}</span>
|
|
|
+ <span class="monitor-summary-count" :style="{ color: item.color }">{{ item.count }}</span>
|
|
|
</div>
|
|
|
- <div class="monitor-table-body">
|
|
|
- <div class="monitor-row" v-for="item in monitorList" :key="item.name">
|
|
|
- <span class="col-name">{{ item.name }}</span>
|
|
|
- <span class="col-value">{{ item.value }}{{ item.unit }}</span>
|
|
|
- <span class="col-status"><span class="status-tag" :class="item.status">{{ item.statusText }}</span></span>
|
|
|
- <span class="col-trend"><span class="trend-icon" :class="item.trend">{{ item.trend === 'up' ? '↑' : item.trend === 'down' ? '↓' : '→' }}</span></span>
|
|
|
+ </div>
|
|
|
+ <div class="summary-pie-area" v-if="activeSummaryCard">
|
|
|
+ <canvas :id="'summaryPieChart'" width="290" height="200"></canvas>
|
|
|
+ </div>
|
|
|
+ <div class="monitor-subtitle">设备清单</div>
|
|
|
+ <div class="tree-search-bar">
|
|
|
+ <svg class="tree-search-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#7bbef6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="7.5"/><line x1="16" y1="16" x2="22" y2="22"/></svg>
|
|
|
+ <input class="tree-search-input" v-model="searchKey" placeholder="请输入关键词" />
|
|
|
+ </div>
|
|
|
+ <div class="tree-container">
|
|
|
+ <div class="tree-node" v-for="node in filteredTree" :key="node.name">
|
|
|
+ <div class="tree-node-row" :class="{ 'tree-leaf': !node.children }" @click="toggleNode(node)">
|
|
|
+ <svg v-if="node.children" class="tree-arrow" :class="{ expanded: node.expanded }" viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="#7bbef6" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
|
+ <span v-else class="tree-dot"></span>
|
|
|
+ <span class="tree-node-label" :class="{ 'tree-node-bold': !node.children }">{{ node.name }}</span>
|
|
|
+ <span v-if="node.count !== undefined" class="tree-node-count">{{ node.count }}</span>
|
|
|
+ <div v-if="node.children" class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tree-children" v-if="node.children && node.expanded" v-for="child in node.children" :key="child.name">
|
|
|
+ <div class="tree-node-row" :class="{ 'tree-leaf': !child.children }" @click="toggleNode(child)">
|
|
|
+ <svg v-if="child.children" class="tree-arrow" :class="{ expanded: child.expanded }" viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="#7bbef6" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
|
+ <span v-else class="tree-dot"></span>
|
|
|
+ <span class="tree-node-label" :class="{ 'tree-node-bold': !child.children }">{{ child.name }}</span>
|
|
|
+ <span v-if="child.count !== undefined" class="tree-node-count">{{ child.count }}</span>
|
|
|
+ <div v-if="!child.children" class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tree-grandchildren" v-if="child.children && child.expanded" v-for="grand in child.children" :key="grand.name">
|
|
|
+ <div class="tree-node-row" :class="{ 'tree-leaf': !grand.children }" @click="toggleNode(grand)">
|
|
|
+ <svg v-if="grand.children" class="tree-arrow" :class="{ expanded: grand.expanded }" viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="#7bbef6" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
|
+ <span v-else class="tree-dot"></span>
|
|
|
+ <span class="tree-node-label" :class="{ 'tree-node-bold': !grand.children }">{{ grand.name }}</span>
|
|
|
+ <span v-if="grand.count !== undefined" class="tree-node-count">{{ grand.count }}</span>
|
|
|
+ <div v-if="grand.children" class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ <div v-else class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tree-locations" v-if="grand.children && grand.expanded" v-for="loc in grand.children" :key="loc.name">
|
|
|
+ <div class="tree-node-row" :class="{ 'tree-leaf': !loc.children }" @click="toggleNode(loc)">
|
|
|
+ <svg v-if="loc.children" class="tree-arrow" :class="{ expanded: loc.expanded }" viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="#7bbef6" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
|
+ <span v-else class="tree-dot"></span>
|
|
|
+ <span class="tree-node-label" :class="{ 'tree-node-bold': !loc.children }">{{ loc.name }}</span>
|
|
|
+ <span v-if="loc.count !== undefined" class="tree-node-count">{{ loc.count }}</span>
|
|
|
+ <div v-if="loc.children" class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ <div v-else class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tree-points" v-if="loc.children && loc.expanded" v-for="pt in loc.children" :key="pt.name">
|
|
|
+ <div class="tree-node-row" :class="{ 'tree-leaf': !pt.children }" @click="toggleNode(pt)">
|
|
|
+ <svg v-if="pt.children" class="tree-arrow" :class="{ expanded: pt.expanded }" viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="#7bbef6" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
|
+ <span v-else class="tree-dot"></span>
|
|
|
+ <span class="tree-node-label" :class="{ 'tree-node-bold': !pt.children }">{{ pt.name }}</span>
|
|
|
+ <span v-if="pt.count !== undefined" class="tree-node-count">{{ pt.count }}</span>
|
|
|
+ <div v-if="pt.children" class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ <div v-else class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tree-devices" v-if="pt.children && pt.expanded" v-for="dev in pt.children" :key="dev.name">
|
|
|
+ <div class="tree-node-row tree-leaf" @click.stop="openDevicePanel(dev, grand, loc, pt, child)">
|
|
|
+ <span class="tree-dot"></span>
|
|
|
+ <span class="tree-node-label tree-node-bold" style="color:#62f6fb;">{{ dev.name }}</span>
|
|
|
+ <div class="tree-node-actions">
|
|
|
+ <svg class="tree-action-icon" title="定位" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#22c55e" stroke-width="2"><circle cx="12" cy="10.5" r="2.5"/><path d="M12 21C16 17 19 13.5 19 10.5C19 6.5 15.5 3 12 3C8.5 3 5 6.5 5 10.5C5 13.5 8 17 12 21Z"/></svg>
|
|
|
+ <svg class="tree-action-icon" title="趋势" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#62f6fb" stroke-width="2"><polyline points="3 18 9 12 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="middle-area">
|
|
|
+ <TempStressCard
|
|
|
+ :device="selectedDevice"
|
|
|
+ :breadcrumb="deviceBreadcrumb"
|
|
|
+ :visible="!!selectedDevice"
|
|
|
+ @close="closeTempCard"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="right-sidebar">
|
|
|
+ <DeviceRightPanel
|
|
|
+ :device="selectedDevice"
|
|
|
+ :breadcrumb="deviceBreadcrumb"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div v-if="currentTab === 'spillway'" class="tab-content tab-placeholder"><div class="placeholder-text">泄洪设施监测数据</div></div>
|
|
|
- <div v-if="currentTab === 'conveyance'" class="tab-content tab-placeholder"><div class="placeholder-text">输水设施监测数据</div></div>
|
|
|
- <div v-if="currentTab === 'bank'" class="tab-content tab-placeholder"><div class="placeholder-text">库岸稳定监测数据</div></div>
|
|
|
</div>
|
|
|
+
|
|
|
</div>
|
|
|
</template>
|
|
|
<script>
|
|
|
import * as echarts from "echarts";
|
|
|
+import TempStressCard from "../components/TempStressCard.vue";
|
|
|
+import DeviceRightPanel from "../components/DeviceRightPanel.vue";
|
|
|
export default {
|
|
|
name: "EngineeringSafetyView",
|
|
|
+ components: { TempStressCard, DeviceRightPanel },
|
|
|
data() {
|
|
|
return {
|
|
|
- currentTab: "dam",
|
|
|
- monitorList: [
|
|
|
- { name: "坝顶水平位移-01", value: "12.5", unit: "mm", status: "normal", statusText: "正常", trend: "up" },
|
|
|
- { name: "坝顶水平位移-02", value: "11.8", unit: "mm", status: "normal", statusText: "正常", trend: "steady" },
|
|
|
- { name: "坝基垂直位移-01", value: "8.3", unit: "mm", status: "normal", statusText: "正常", trend: "down" },
|
|
|
- { name: "坝基垂直位移-02", value: "7.9", unit: "mm", status: "normal", statusText: "正常", trend: "steady" },
|
|
|
- { name: "渗流压力-P01", value: "25.6", unit: "kPa", status: "normal", statusText: "正常", trend: "up" },
|
|
|
- { name: "渗流压力-P02", value: "22.1", unit: "kPa", status: "normal", statusText: "正常", trend: "steady" },
|
|
|
- { name: "绕坝渗流-S01", value: "1.2", unit: "L/s", status: "normal", statusText: "正常", trend: "steady" },
|
|
|
- { name: "裂缝开合度-C01", value: "1.2", unit: "mm", status: "warning", statusText: "注意", trend: "up" },
|
|
|
- { name: "钢筋应力-R01", value: "45.2", unit: "MPa", status: "normal", statusText: "正常", trend: "steady" },
|
|
|
- { name: "库岸变形-B01", value: "3.2", unit: "mm", status: "normal", statusText: "正常", trend: "down" },
|
|
|
+ searchKey: "",
|
|
|
+ treeData: [
|
|
|
+ {
|
|
|
+ name: "大坝安全监测系统", expanded: true, children: [
|
|
|
+ {
|
|
|
+ name: "主坝", expanded: true, children: [
|
|
|
+ {
|
|
|
+ name: "变形监测", expanded: true, children: [
|
|
|
+ {
|
|
|
+ name: "坝顶", children: [
|
|
|
+ { name: "坝顶水平位移-01", children: [{ name: "R201YL1" }, { name: "R201YL2" }, { name: "R201YL3" }] },
|
|
|
+ { name: "坝顶水平位移-02", children: [{ name: "R201YL4" }, { name: "R201YL5" }, { name: "R201YL6" }] },
|
|
|
+ { name: "坝顶水平位移-03", children: [{ name: "R201YL7" }, { name: "R201YL8" }, { name: "R201YL9" }] },
|
|
|
+ { name: "坝顶水平位移-04", children: [{ name: "R201YL10" }, { name: "R201YL11" }, { name: "R201YL12" }] },
|
|
|
+ { name: "坝顶水平位移-05", children: [{ name: "R201YL13" }, { name: "R201YL14" }, { name: "R201YL15" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "坝基", children: [
|
|
|
+ { name: "坝基垂直位移-01", children: [{ name: "B301YL1" }, { name: "B301YL2" }, { name: "B301YL3" }] },
|
|
|
+ { name: "坝基垂直位移-02", children: [{ name: "B301YL4" }, { name: "B301YL5" }, { name: "B301YL6" }] },
|
|
|
+ { name: "坝基垂直位移-03", children: [{ name: "B301YL7" }, { name: "B301YL8" }, { name: "B301YL9" }] },
|
|
|
+ { name: "坝基垂直位移-04", children: [{ name: "B301YL10" }, { name: "B301YL11" }, { name: "B301YL12" }] },
|
|
|
+ { name: "坝基垂直位移-05", children: [{ name: "B301YL13" }, { name: "B301YL14" }, { name: "B301YL15" }] },
|
|
|
+ { name: "坝基垂直位移-06", children: [{ name: "B301YL16" }, { name: "B301YL17" }, { name: "B301YL18" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "左岸", children: [
|
|
|
+ { name: "库岸变形-B01", children: [{ name: "K401YL1" }, { name: "K401YL2" }, { name: "K401YL3" }] },
|
|
|
+ { name: "库岸变形-B02", children: [{ name: "K401YL4" }, { name: "K401YL5" }, { name: "K401YL6" }] },
|
|
|
+ { name: "库岸变形-B03", children: [{ name: "K401YL7" }, { name: "K401YL8" }, { name: "K401YL9" }] },
|
|
|
+ { name: "裂缝开合度-C01", children: [{ name: "F501YL1" }, { name: "F501YL2" }, { name: "F501YL3" }] },
|
|
|
+ { name: "裂缝开合度-C02", children: [{ name: "F501YL4" }, { name: "F501YL5" }, { name: "F501YL6" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "右岸", children: [
|
|
|
+ { name: "库岸变形-B04", children: [{ name: "K401YL10" }, { name: "K401YL11" }, { name: "K401YL12" }] },
|
|
|
+ { name: "库岸变形-B05", children: [{ name: "K401YL13" }, { name: "K401YL14" }, { name: "K401YL15" }] },
|
|
|
+ { name: "裂缝开合度-C03", children: [{ name: "F501YL7" }, { name: "F501YL8" }, { name: "F501YL9" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "坝肩", children: [
|
|
|
+ { name: "坝肩位移-J01", children: [{ name: "J601YL1" }, { name: "J601YL2" }, { name: "J601YL3" }] },
|
|
|
+ { name: "坝肩位移-J02", children: [{ name: "J601YL4" }, { name: "J601YL5" }, { name: "J601YL6" }] },
|
|
|
+ { name: "精密水准-S01", children: [{ name: "S701YL1" }, { name: "S701YL2" }, { name: "S701YL3" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "渗流监测", expanded: true, children: [
|
|
|
+ {
|
|
|
+ name: "坝基", children: [
|
|
|
+ { name: "渗流压力-P01", children: [{ name: "P801YL1" }, { name: "P801YL2" }, { name: "P801YL3" }] },
|
|
|
+ { name: "渗流压力-P02", children: [{ name: "P801YL4" }, { name: "P801YL5" }, { name: "P801YL6" }] },
|
|
|
+ { name: "渗流压力-P03", children: [{ name: "P801YL7" }, { name: "P801YL8" }, { name: "P801YL9" }] },
|
|
|
+ { name: "渗流压力-P04", children: [{ name: "P801YL10" }, { name: "P801YL11" }, { name: "P801YL12" }] },
|
|
|
+ { name: "绕坝渗流-S01", children: [{ name: "R901YL1" }, { name: "R901YL2" }, { name: "R901YL3" }] },
|
|
|
+ { name: "绕坝渗流-S02", children: [{ name: "R901YL4" }, { name: "R901YL5" }, { name: "R901YL6" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "坝体", children: [
|
|
|
+ { name: "渗流压力-P05", children: [{ name: "P801YL13" }, { name: "P801YL14" }, { name: "P801YL15" }] },
|
|
|
+ { name: "渗流压力-P06", children: [{ name: "P801YL16" }, { name: "P801YL17" }, { name: "P801YL18" }] },
|
|
|
+ { name: "渗流压力-P07", children: [{ name: "P801YL19" }, { name: "P801YL20" }, { name: "P801YL21" }] },
|
|
|
+ { name: "渗流压力-P08", children: [{ name: "P801YL22" }, { name: "P801YL23" }, { name: "P801YL24" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "廊道", children: [
|
|
|
+ { name: "廊道渗流-L01", children: [{ name: "L011YL1" }, { name: "L011YL2" }, { name: "L011YL3" }] },
|
|
|
+ { name: "廊道渗流-L02", children: [{ name: "L011YL4" }, { name: "L011YL5" }, { name: "L011YL6" }] },
|
|
|
+ { name: "廊道渗流-L03", children: [{ name: "L011YL7" }, { name: "L011YL8" }, { name: "L011YL9" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "应力应变", expanded: true, children: [
|
|
|
+ {
|
|
|
+ name: "坝体", children: [
|
|
|
+ { name: "钢筋应力-R01", children: [{ name: "G111YL1" }, { name: "G111YL2" }, { name: "G111YL3" }] },
|
|
|
+ { name: "钢筋应力-R02", children: [{ name: "G111YL4" }, { name: "G111YL5" }, { name: "G111YL6" }] },
|
|
|
+ { name: "钢筋应力-R03", children: [{ name: "G111YL7" }, { name: "G111YL8" }, { name: "G111YL9" }] },
|
|
|
+ { name: "钢筋应力-R04", children: [{ name: "G111YL10" }, { name: "G111YL11" }, { name: "G111YL12" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "坝基", children: [
|
|
|
+ { name: "基岩应力-Y01", children: [{ name: "J121YL1" }, { name: "J121YL2" }, { name: "J121YL3" }] },
|
|
|
+ { name: "基岩应力-Y02", children: [{ name: "J121YL4" }, { name: "J121YL5" }, { name: "J121YL6" }] },
|
|
|
+ { name: "基岩应力-Y03", children: [{ name: "J121YL7" }, { name: "J121YL8" }, { name: "J121YL9" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "环境量监测", expanded: true, children: [
|
|
|
+ {
|
|
|
+ name: "库水位", children: [
|
|
|
+ { name: "库水位-W01", children: [{ name: "W131YL1" }, { name: "W131YL2" }, { name: "W131YL3" }] },
|
|
|
+ { name: "库水位-W02", children: [{ name: "W131YL4" }, { name: "W131YL5" }, { name: "W131YL6" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "降雨量", children: [
|
|
|
+ { name: "雨量计-R01", children: [{ name: "Y141YL1" }, { name: "Y141YL2" }, { name: "Y141YL3" }] },
|
|
|
+ { name: "雨量计-R02", children: [{ name: "Y141YL4" }, { name: "Y141YL5" }, { name: "Y141YL6" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "温度", children: [
|
|
|
+ { name: "温度计-T01", children: [{ name: "T151YL1" }, { name: "T151YL2" }, { name: "T151YL3" }] },
|
|
|
+ { name: "温度计-T02", children: [{ name: "T151YL4" }, { name: "T151YL5" }, { name: "T151YL6" }] },
|
|
|
+ { name: "温度计-T03", children: [{ name: "T151YL7" }, { name: "T151YL8" }, { name: "T151YL9" }] },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
],
|
|
|
- pieData: [
|
|
|
- { name: "正常", count: 8, color: "#22c55e" },
|
|
|
- { name: "注意", count: 2, color: "#ffd93d" },
|
|
|
- { name: "警戒", count: 0, color: "#f97316" },
|
|
|
- { name: "危险", count: 0, color: "#ef4444" },
|
|
|
+ monitorSummaryList: [
|
|
|
+ { name: "变形检测", count: 42, progress: 82, color: "#62f6fb" },
|
|
|
+ { name: "渗流渗压", count: 28, progress: 65, color: "#a78bfa" },
|
|
|
+ { name: "应力应变", count: 16, progress: 45, color: "#fbbf24" },
|
|
|
],
|
|
|
- assessmentIndicators: ["变形指标 合格", "渗流指标 合格", "结构指标 合格", "环境指标 合格"],
|
|
|
- deformationChart: null,
|
|
|
- seepageChart: null,
|
|
|
- statusPieChart: null,
|
|
|
+ summaryPieMap: {
|
|
|
+ "变形检测": [
|
|
|
+ { name: "精密水准", value: 12, color: "#62f6fb" },
|
|
|
+ { name: "基岩变形计", value: 10, color: "#34d399" },
|
|
|
+ { name: "电磁沉降环", value: 8, color: "#fbbf24" },
|
|
|
+ { name: "测缝计", value: 7, color: "#f97316" },
|
|
|
+ { name: "多点位移计", value: 5, color: "#ef4444" },
|
|
|
+ ],
|
|
|
+ "渗流渗压": [
|
|
|
+ { name: "渗压计", value: 10, color: "#a78bfa" },
|
|
|
+ { name: "量水堰", value: 7, color: "#818cf8" },
|
|
|
+ { name: "测压管", value: 6, color: "#c084fc" },
|
|
|
+ { name: "孔隙水压力计", value: 5, color: "#e879f9" },
|
|
|
+ ],
|
|
|
+ "应力应变": [
|
|
|
+ { name: "钢筋计", value: 6, color: "#fbbf24" },
|
|
|
+ { name: "应变计", value: 5, color: "#f59e0b" },
|
|
|
+ { name: "无应力计", value: 5, color: "#f97316" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ activeSummaryCard: "变形检测",
|
|
|
+ summaryPieChart: null,
|
|
|
+ selectedDevice: null,
|
|
|
+ deviceBreadcrumb: "",
|
|
|
+ deviceGaugeChart: null,
|
|
|
};
|
|
|
},
|
|
|
- mounted() { this.$nextTick(() => this.initCharts()); },
|
|
|
+ computed: {
|
|
|
+ filteredTree() {
|
|
|
+ const kw = this.searchKey.trim().toLowerCase();
|
|
|
+ if (!kw) return this.treeData;
|
|
|
+ const result = [];
|
|
|
+ for (const root of this.treeData) {
|
|
|
+ const matched = this.filterNode(root, kw);
|
|
|
+ if (matched) result.push(matched);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.initCharts();
|
|
|
+ this.renderSummaryPieChart();
|
|
|
+ });
|
|
|
+ },
|
|
|
beforeUnmount() {
|
|
|
- if (this.deformationChart) this.deformationChart.dispose();
|
|
|
- if (this.seepageChart) this.seepageChart.dispose();
|
|
|
- if (this.statusPieChart) this.statusPieChart.dispose();
|
|
|
+ if (this.summaryPieChart) this.summaryPieChart.dispose();
|
|
|
+ if (this.deviceGaugeChart) this.deviceGaugeChart.dispose();
|
|
|
},
|
|
|
methods: {
|
|
|
- initCharts() {
|
|
|
- if (!echarts) return;
|
|
|
- const dc = document.getElementById("deformationChart");
|
|
|
- if (dc) {
|
|
|
- if (this.deformationChart) this.deformationChart.dispose();
|
|
|
- this.deformationChart = echarts.init(dc);
|
|
|
- this.deformationChart.setOption({ animation: false, tooltip: { trigger: "axis" }, grid: { left: "8%", right: "5%", top: "8%", bottom: "12%" }, xAxis: { type: "category", data: ["1月","2月","3月","4月","5月","6月"], axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 } }, yAxis: { type: "value", name: "mm", nameTextStyle: { color: "#7bbef6", fontSize: 9 }, axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 }, splitLine: { lineStyle: { color: "rgba(123,190,246,0.15)" } } }, series: [{ name: "水平位移", type: "line", data: [8.2,9.5,10.8,11.2,12.0,12.5], smooth: true, symbol: "circle", symbolSize: 4, lineStyle: { color: "#62f6fb", width: 2 }, itemStyle: { color: "#62f6fb" }, areaStyle: { color: "rgba(98,246,251,0.1)" } }, { name: "垂直位移", type: "line", data: [5.1,6.2,7.0,7.5,8.0,8.3], smooth: true, symbol: "diamond", symbolSize: 4, lineStyle: { color: "#fbbf24", width: 2 }, itemStyle: { color: "#fbbf24" }, areaStyle: { color: "rgba(251,191,36,0.08)" } }] });
|
|
|
+ filterNode(node, kw) {
|
|
|
+ if (!node.children) {
|
|
|
+ return node.name.toLowerCase().includes(kw) ? { ...node } : null;
|
|
|
+ }
|
|
|
+ const matchedChildren = node.children.map(c => this.filterNode(c, kw)).filter(Boolean);
|
|
|
+ if (matchedChildren.length > 0 || node.name.toLowerCase().includes(kw)) {
|
|
|
+ return { ...node, children: matchedChildren.length > 0 ? matchedChildren : node.children, expanded: true };
|
|
|
}
|
|
|
- const sc = document.getElementById("seepageChart");
|
|
|
- if (sc) {
|
|
|
- if (this.seepageChart) this.seepageChart.dispose();
|
|
|
- this.seepageChart = echarts.init(sc);
|
|
|
- this.seepageChart.setOption({ animation: false, tooltip: { trigger: "axis" }, legend: { data: ["渗流量","渗流压力"], textStyle: { color: "#7bbef6", fontSize: 9 }, bottom: 0, itemWidth: 10, itemHeight: 8 }, grid: { left: "8%", right: "5%", top: "8%", bottom: "22%" }, xAxis: { type: "category", data: ["1月","2月","3月","4月","5月","6月"], axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 } }, yAxis: { type: "value", nameTextStyle: { color: "#7bbef6", fontSize: 9 }, axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 }, splitLine: { lineStyle: { color: "rgba(123,190,246,0.15)" } } }, series: [{ name: "渗流量", type: "line", data: [4.2,3.8,3.6,3.5,3.5,3.5], smooth: true, symbol: "circle", symbolSize: 4, lineStyle: { color: "#62f6fb", width: 2 }, itemStyle: { color: "#62f6fb" }, areaStyle: { color: "rgba(98,246,251,0.12)" } }, { name: "渗流压力", type: "line", data: [28.5,26.8,25.2,24.8,25.0,25.6], smooth: true, symbol: "diamond", symbolSize: 4, lineStyle: { color: "#a78bfa", width: 2 }, itemStyle: { color: "#a78bfa" }, areaStyle: { color: "rgba(167,139,250,0.08)" } }] });
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+ toggleNode(node) {
|
|
|
+ if (node.children) {
|
|
|
+ node.expanded = !node.expanded;
|
|
|
}
|
|
|
- const pc = document.getElementById("statusPieChart");
|
|
|
- if (pc) {
|
|
|
- if (this.statusPieChart) this.statusPieChart.dispose();
|
|
|
- this.statusPieChart = echarts.init(pc);
|
|
|
- this.statusPieChart.setOption({ animation: false, tooltip: { trigger: "item", formatter: "{b}: {c}个 ({d}%)" }, series: [{ type: "pie", radius: ["45%","75%"], center: ["50%","50%"], avoidLabelOverlap: false, itemStyle: { borderColor: "rgba(0,30,60,0.6)", borderWidth: 2 }, label: { show: false }, labelLine: { show: false }, data: [{ value: 8, name: "正常", itemStyle: { color: "#22c55e" } }, { value: 2, name: "注意", itemStyle: { color: "#ffd93d" } }, { value: 0, name: "警戒", itemStyle: { color: "#f97316" } }, { value: 0, name: "危险", itemStyle: { color: "#ef4444" } }] }] });
|
|
|
+ },
|
|
|
+ toggleSummaryCard(name) {
|
|
|
+ if (this.activeSummaryCard === name) {
|
|
|
+ this.activeSummaryCard = null;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.activeSummaryCard = name;
|
|
|
+ this.$nextTick(() => this.renderSummaryPieChart());
|
|
|
+ },
|
|
|
+ renderSummaryPieChart() {
|
|
|
+ if (!echarts || !this.activeSummaryCard) return;
|
|
|
+ if (this.summaryPieChart) this.summaryPieChart.dispose();
|
|
|
+ const el = document.getElementById("summaryPieChart");
|
|
|
+ if (!el) return;
|
|
|
+ this.summaryPieChart = echarts.init(el, null, { renderer: "canvas", devicePixelRatio: window.devicePixelRatio || 1 });
|
|
|
+ const data = this.summaryPieMap[this.activeSummaryCard] || [];
|
|
|
+ const summaryItem = this.monitorSummaryList.find(i => i.name === this.activeSummaryCard);
|
|
|
+ const total = summaryItem ? summaryItem.count : 0;
|
|
|
+ this.summaryPieChart.setOption({
|
|
|
+ animation: false,
|
|
|
+ tooltip: { trigger: "item", formatter: "{b}: {c}个 ({d}%)" },
|
|
|
+ graphic: [{
|
|
|
+ type: "text",
|
|
|
+ left: "center",
|
|
|
+ top: "center",
|
|
|
+ style: {
|
|
|
+ text: `{total|${total}}\n{label|${this.activeSummaryCard}}`,
|
|
|
+ fill: "#ffffff",
|
|
|
+ rich: {
|
|
|
+ total: { color: "#ffffff", fontSize: 22, fontWeight: "bold", lineHeight: 30 },
|
|
|
+ label: { color: "#ffffff", fontSize: 11, lineHeight: 16 },
|
|
|
+ },
|
|
|
+ textAlign: "center",
|
|
|
+ },
|
|
|
+ }],
|
|
|
+ series: [{
|
|
|
+ type: "pie",
|
|
|
+ radius: ["52%", "72%"],
|
|
|
+ center: ["50%", "50%"],
|
|
|
+ avoidLabelOverlap: true,
|
|
|
+ itemStyle: { borderColor: "rgba(0,30,60,0.6)", borderWidth: 2 },
|
|
|
+ label: { show: true, formatter: "{b}\n{d}%", color: "#ffffff", fontSize: 10, lineHeight: 14 },
|
|
|
+ labelLine: { show: true, length: 10, length2: 8, lineStyle: { color: "rgba(0,212,255,0.3)" } },
|
|
|
+ emphasis: { label: { show: true, fontSize: 12, fontWeight: "bold" }, itemStyle: { shadowBlur: 6, shadowColor: "rgba(0,212,255,0.3)" } },
|
|
|
+ data: data.map(d => ({ value: d.value, name: d.name, itemStyle: { color: d.color } })),
|
|
|
+ }],
|
|
|
+ });
|
|
|
+ },
|
|
|
+ openDevicePanel(dev, grand, loc, pt, child) {
|
|
|
+ this.selectedDevice = dev;
|
|
|
+ this.deviceBreadcrumb = `${child.name} > ${grand.name} > ${loc.name} > ${pt.name} > ${dev.name}`;
|
|
|
+ },
|
|
|
+ closeTempCard() {
|
|
|
+ this.selectedDevice = null;
|
|
|
+ },
|
|
|
+ initCharts() {
|
|
|
+ if (!echarts) return;
|
|
|
+ const gc = document.getElementById("deviceGaugeChart");
|
|
|
+ if (gc) {
|
|
|
+ if (this.deviceGaugeChart) this.deviceGaugeChart.dispose();
|
|
|
+ this.deviceGaugeChart = echarts.init(gc, null, { renderer: "canvas", devicePixelRatio: window.devicePixelRatio || 1 });
|
|
|
+ this.deviceGaugeChart.setOption({
|
|
|
+ animation: false,
|
|
|
+ series: [{
|
|
|
+ type: "gauge",
|
|
|
+ center: ["50%", "55%"],
|
|
|
+ radius: "80%",
|
|
|
+ startAngle: 210,
|
|
|
+ endAngle: -30,
|
|
|
+ min: 0,
|
|
|
+ max: 3000,
|
|
|
+ splitNumber: 3,
|
|
|
+ progress: { show: true, width: 16, itemStyle: { color: { type: "linear", x: 0, y: 0, x2: 1, y2: 0, colorStops: [{ offset: 0, color: "#62f6fb" }, { offset: 0.5, color: "#a78bfa" }, { offset: 1, color: "#fbbf24" }] } } },
|
|
|
+ axisLine: { lineStyle: { width: 16, color: [[1, "rgba(255,255,255,0.08)"]] } },
|
|
|
+ axisTick: { show: false },
|
|
|
+ splitLine: { show: false },
|
|
|
+ axisLabel: { show: false },
|
|
|
+ pointer: { show: false },
|
|
|
+ anchor: { show: false },
|
|
|
+ title: { show: false },
|
|
|
+ detail: {
|
|
|
+ offsetCenter: [0, "20%"],
|
|
|
+ formatter: () => "{val|2,279}\n{label|在线仪器}",
|
|
|
+ rich: {
|
|
|
+ val: { color: "#ffffff", fontSize: 22, fontWeight: "bold", lineHeight: 30 },
|
|
|
+ label: { color: "#ffffff", fontSize: 15, lineHeight: 22 },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data: [{ value: 2279, name: "" }],
|
|
|
+ }],
|
|
|
+ });
|
|
|
}
|
|
|
},
|
|
|
},
|
|
|
@@ -190,19 +505,15 @@ export default {
|
|
|
</script>
|
|
|
<style scoped>
|
|
|
.safety-container { width: 100%; height: 100%; display: flex; flex-direction: column; position: relative; overflow: hidden; }
|
|
|
-.tab-bar { position: absolute; left: 20px; top: 80px; z-index: 10; display: flex; gap: 4px; }
|
|
|
-.tab-item { padding: 6px 24px; font-size: 14px; font-weight: bold; color: #7bbef6; background: rgba(0,20,40,0.6); border: 1px solid rgba(0,212,255,0.25); border-radius: 3px 3px 0 0; cursor: pointer; transition: all 0.2s; user-select: none; }
|
|
|
-.tab-item:hover { background: rgba(0,40,80,0.7); color: #62f6fb; }
|
|
|
-.tab-item.active { background: rgba(0,20,40,0.85); color: #e0fcff; border-color: rgba(0,212,255,0.5); border-bottom-color: transparent; text-shadow: 0 0 5px rgba(0,212,255,0.3); }
|
|
|
-.safety-scroll-area { position: absolute; left: 0; right: 0; top: 110px; bottom: 0; overflow-y: auto; overflow-x: hidden; z-index: 5; padding-bottom: 40px; }
|
|
|
+.safety-scroll-area { position: absolute; left: 0; right: 0; top: 80px; bottom: 0; overflow-y: auto; overflow-x: hidden; z-index: 5; padding-bottom: 40px; pointer-events: none; }
|
|
|
.safety-scroll-area::-webkit-scrollbar { width: 4px; }
|
|
|
.safety-scroll-area::-webkit-scrollbar-track { background: rgba(0,20,40,0.5); }
|
|
|
.safety-scroll-area::-webkit-scrollbar-thumb { background: rgba(0,212,255,0.3); border-radius: 2px; }
|
|
|
.tab-content { width: 100%; display: flex; justify-content: space-between; padding: 0 20px; box-sizing: border-box; }
|
|
|
-.left-sidebar { width: 350px; flex-shrink: 0; }
|
|
|
-.right-sidebar { width: 350px; flex-shrink: 0; }
|
|
|
-.tab-placeholder { justify-content: center; align-items: center; min-height: 300px; }
|
|
|
-.placeholder-text { color: #7bbef6; font-size: 18px; opacity: 0.6; padding: 80px 0; }
|
|
|
+.left-sidebar { width: 350px; flex-shrink: 0; pointer-events: auto; }
|
|
|
+.middle-area { flex: 1; margin: 0 10px; min-width: 0; pointer-events: auto; display: flex; flex-direction: column; }
|
|
|
+.middle-area-content { margin-top: auto; }
|
|
|
+.right-sidebar { width: 350px; flex-shrink: 0; pointer-events: auto; }
|
|
|
.mt-10 { margin-top: 10px; }
|
|
|
.data-card { width: 100%; background: rgba(0,20,40,0.7); border-radius: 4px; overflow: hidden; box-shadow: 0 0 10px rgba(0,212,255,0.2); }
|
|
|
.card-header { height: 42px; background-image: url("/src/assets/images/数据小标题.png"); background-size: 100% 100%; background-position: center; background-repeat: no-repeat; display: flex; align-items: flex-start; justify-content: space-between; padding: 4px 16px 0; }
|
|
|
@@ -210,8 +521,8 @@ export default {
|
|
|
.header-actions { display: flex; align-items: flex-start; gap: 10px; }
|
|
|
.update-time { color: #7bbef6; font-size: 11px; font-family: monospace; }
|
|
|
.card-body { padding: 8px; min-height: auto; font-size: 13px; line-height: 1.5; margin-top: -6px; }
|
|
|
-.card-body.chart-body { padding: 6px 8px 8px; }
|
|
|
-.card-body.card-body-tight { padding: 6px 8px; }
|
|
|
+.card-body.chart-body { padding: 2px 8px 4px; }
|
|
|
+.card-body.card-body-tight { padding: 2px 8px 4px; }
|
|
|
.monitor-chart-box { background: rgba(0,20,40,0.4); border-radius: 4px; border: 1px solid rgba(0,212,255,0.15); padding: 6px; }
|
|
|
.monitor-chart-label { color: #62f6fb; font-size: var(--fs-label); font-weight: bold; margin-bottom: 2px; }
|
|
|
.chart-legend { text-align: center; margin-top: 3px; color: #7bbef6; font-size: 10px; }
|
|
|
@@ -221,36 +532,69 @@ export default {
|
|
|
.water-stat-card { flex: 1; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 3px 10px; background-image: url("/src/assets/images/卡片背景2.png"); background-size: 100% 100%; background-position: center; background-repeat: no-repeat; height: 42px; }
|
|
|
.water-stat-label { color: #62f6fb; font-size: var(--fs-label); margin-bottom: 0; text-align: left; line-height: 1.2; white-space: nowrap; }
|
|
|
.water-stat-value { color: #ffffff; font-size: var(--fs-value-xl); font-weight: bold; text-shadow: 0 0 6px rgba(0,212,255,0.5); line-height: 1.2; }
|
|
|
-.safety-assessment-row { display: flex; gap: 8px; align-items: stretch; }
|
|
|
-.safety-pie-section { flex-shrink: 0; width: 45%; text-align: center; }
|
|
|
-.pie-legend { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 3px; }
|
|
|
-.pie-legend-item { display: flex; align-items: center; gap: 3px; flex: 1 1 45%; min-width: 70px; }
|
|
|
-.pie-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
|
-.pie-name { color: #7bbef6; font-size: 10px; }
|
|
|
-.pie-count { color: #e0fcff; font-size: 11px; font-weight: bold; margin-left: auto; }
|
|
|
-.safety-assessment-section { flex: 1; display: flex; flex-direction: column; gap: 4px; }
|
|
|
-.safety-assessment-bar { background: rgba(0,20,40,0.5); border-radius: 4px; border: 1px solid rgba(0,212,255,0.15); padding: 8px 10px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
|
|
|
-.safety-assessment-grade { font-size: 20px; font-weight: bold; padding: 2px 16px; border-radius: 4px; background: rgba(34,197,94,0.25); color: #22c55e; border: 1px solid rgba(34,197,94,0.5); text-align: center; }
|
|
|
-.safety-assessment-texts { display: flex; flex-direction: column; align-items: center; gap: 1px; }
|
|
|
-.safety-assessment-text { color: #7bbef6; font-size: 11px; }
|
|
|
-.safety-assessment-tags { display: flex; flex-wrap: wrap; gap: 3px; justify-content: center; }
|
|
|
-.assess-tag { font-size: 10px; font-weight: bold; padding: 2px 8px; border-radius: 2px; }
|
|
|
-.assess-tag.good { background: rgba(34,197,94,0.15); color: #22c55e; border: 1px solid rgba(34,197,94,0.3); }
|
|
|
-.monitor-table { width: 100%; font-size: 11px; }
|
|
|
-.monitor-table-header { display: flex; padding: 4px 6px; background: rgba(0,212,255,0.1); border-bottom: 1px solid rgba(0,212,255,0.2); color: #7bbef6; font-weight: bold; }
|
|
|
-.monitor-table-body { max-height: 260px; overflow-y: auto; }
|
|
|
-.monitor-table-body::-webkit-scrollbar { width: 3px; }
|
|
|
-.monitor-table-body::-webkit-scrollbar-track { background: rgba(0,20,40,0.5); }
|
|
|
-.monitor-table-body::-webkit-scrollbar-thumb { background: rgba(0,212,255,0.3); border-radius: 2px; }
|
|
|
-.monitor-row { display: flex; padding: 4px 6px; border-bottom: 1px solid rgba(0,212,255,0.08); align-items: flex-start; color: #e0fcff; }
|
|
|
-.monitor-row:last-child { border-bottom: none; }
|
|
|
-.monitor-row:hover { background: rgba(0,212,255,0.05); }
|
|
|
-.col-name { flex: 2.5; } .col-value { flex: 1.5; text-align: center; } .col-status { flex: 1.2; text-align: center; } .col-trend { flex: 0.8; text-align: center; }
|
|
|
-.status-tag { font-size: var(--fs-status); font-weight: bold; padding: 1px 6px; border-radius: 2px; display: inline-block; }
|
|
|
-.status-tag.normal { background: rgba(34,197,94,0.2); color: #22c55e; }
|
|
|
-.status-tag.warning { background: rgba(255,217,61,0.2); color: #ffd93d; }
|
|
|
-.status-tag.danger { background: rgba(239,68,68,0.2); color: #ff6b6b; }
|
|
|
-.trend-icon { font-size: 12px; font-weight: bold; }
|
|
|
-.trend-icon.up { color: #f97316; } .trend-icon.down { color: #22c55e; } .trend-icon.steady { color: #7bbef6; }
|
|
|
+.device-stats-row { display: flex; gap: 4px; align-items: center; }
|
|
|
+.safety-eval-section { background: rgba(0,20,40,0.5); border-radius: 4px; border: 1px solid rgba(0,212,255,0.12); padding: 8px 10px; margin-bottom: 6px; min-height: 120px; display: flex; flex-direction: column; justify-content: space-between; }
|
|
|
+.safety-eval-top { display: flex; justify-content: space-between; align-items: center; height: 30px; padding: 0 10px; border-radius: 3px; margin-bottom: 6px; background: #22c55e; }
|
|
|
+.safety-eval-section-title { color: #ffffff; font-size: 15px; }
|
|
|
+.safety-eval-grade { color: #ffffff; font-size: 16px; font-weight: bold; }
|
|
|
+.safety-eval-sub-row { display: flex; gap: 6px; margin-bottom: 6px; }
|
|
|
+.safety-eval-sub-card { flex: 1; display: flex; justify-content: space-between; align-items: center; padding: 4px 8px; background: rgba(0,20,40,0.4); border-radius: 3px; }
|
|
|
+.eval-sub-label { color: #7bbef6; font-size: 13px; }
|
|
|
+.eval-sub-value { color: #e0fcff; font-size: 13px; font-weight: bold; }
|
|
|
+.eval-sub-value.status-ok { color: #22c55e; }
|
|
|
+.safety-eval-mid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 6px; }
|
|
|
+.safety-eval-item { display: flex; justify-content: space-between; align-items: center; padding: 4px 8px; background: rgba(0,20,40,0.4); border-radius: 3px; }
|
|
|
+.eval-item-name { color: #e0fcff; font-size: 11px; }
|
|
|
+.eval-item-tag { font-size: 10px; font-weight: bold; padding: 1px 8px; border-radius: 2px; }
|
|
|
+.eval-item-tag.tag-ok { color: #22c55e; background: rgba(34,197,94,0.15); }
|
|
|
+.eval-item-tag.tag-warn { color: #ffd93d; background: rgba(255,217,61,0.15); }
|
|
|
+.safety-eval-bot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
|
+.safety-eval-stat { display: flex; align-items: center; gap: 4px; color: #e0fcff; font-size: 10px; }
|
|
|
+.eval-stat-dot { width: 6px; height: 6px; border-radius: 50%; }
|
|
|
+.dot-ok { background: #22c55e; } .dot-warn { background: #ffd93d; } .dot-err { background: #ef4444; }
|
|
|
+.safety-eval-time { margin-left: auto; color: #7bbef6; font-size: 10px; white-space: nowrap; }
|
|
|
+.device-stat-card { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; border-radius: 4px; padding: 8px 4px; }
|
|
|
+.device-stat-percent { color: #e0fcff; font-size: 20px; font-weight: bold; }
|
|
|
+.device-stat-icon { display: flex; align-items: center; justify-content: center; height: 36px; }
|
|
|
+.stat-icon-img { height: auto; width: auto; max-height: none; }
|
|
|
+.device-stat-label { color: #ffffff; font-size: 13px; white-space: nowrap; }
|
|
|
+.device-gauge-section { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; margin: 0 2px; }
|
|
|
+.tree-search-bar { display: flex; align-items: center; gap: 6px; padding: 4px 8px; background: rgba(0,20,40,0.5); border: 1px solid rgba(0,212,255,0.15); border-radius: 4px; margin-bottom: 4px; }
|
|
|
+.tree-search-icon { flex-shrink: 0; }
|
|
|
+.tree-search-input { flex: 1; background: none; border: none; outline: none; color: #e0fcff; font-size: 12px; line-height: 26px; }
|
|
|
+.tree-search-input::placeholder { color: #7bbef6; }
|
|
|
+.tree-container { font-size: 13px; max-height: 200px; overflow-y: auto; }
|
|
|
+.tree-container::-webkit-scrollbar { width: 3px; }
|
|
|
+.tree-container::-webkit-scrollbar-track { background: rgba(0,20,40,0.5); }
|
|
|
+.tree-container::-webkit-scrollbar-thumb { background: rgba(0,212,255,0.3); border-radius: 2px; }
|
|
|
+.tree-node-row { display: flex; align-items: center; gap: 4px; padding: 4px 6px; cursor: pointer; border-radius: 2px; transition: background 0.15s; }
|
|
|
+.tree-node-row:hover { background: rgba(0,212,255,0.06); }
|
|
|
+.tree-arrow { flex-shrink: 0; transition: transform 0.2s; }
|
|
|
+.tree-arrow.expanded { transform: rotate(90deg); }
|
|
|
+.tree-dot { flex-shrink: 0; width: 5px; height: 5px; border-radius: 50%; background: #7bbef6; margin-left: 3px; margin-right: 3px; }
|
|
|
+.tree-node-label { color: #ffffff; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
|
+.tree-node-bold { color: #ffffff; font-weight: bold; }
|
|
|
+.tree-node-count { color: #ffffff; font-size: 12px; }
|
|
|
+.tree-node-actions { display: flex; gap: 4px; opacity: 0; transition: opacity 0.15s; }
|
|
|
+.tree-node-row:hover .tree-node-actions { opacity: 1; }
|
|
|
+.tree-action-icon { cursor: pointer; flex-shrink: 0; }
|
|
|
+.tree-action-icon:hover { opacity: 0.8; }
|
|
|
+.tree-children { padding-left: 16px; }
|
|
|
+.tree-grandchildren { padding-left: 24px; }
|
|
|
+.tree-locations { padding-left: 36px; }
|
|
|
+.tree-points { padding-left: 48px; }
|
|
|
+.tree-devices { padding-left: 60px; }
|
|
|
+.tall-card { height: calc(100% + 10px); min-height: 440px; }
|
|
|
+.monitor-divider { height: 1px; background: rgba(0,212,255,0.15); margin: 0 8px; }
|
|
|
+.monitor-subtitle { color: #e0fcff; font-size: var(--fs-label); font-weight: bold; padding: 3px 16px 2px 22px; background-image: url("/src/assets/images/小标题.png"); background-size: 100% 100%; background-position: center; background-repeat: no-repeat; height: 24px; display: inline-flex; align-items: center; margin-bottom: 2px; }
|
|
|
+.monitor-summary-row { display: flex; gap: 6px; margin-bottom: 4px; }
|
|
|
+.monitor-summary-card { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; background: rgba(0,20,40,0.5); border-radius: 4px; border: 1px solid rgba(0,212,255,0.12); padding: 8px 4px; cursor: pointer; transition: all 0.2s; }
|
|
|
+.monitor-summary-card:hover { border-color: rgba(0,212,255,0.35); background: rgba(0,30,60,0.6); }
|
|
|
+.monitor-summary-card.active { border-color: rgba(0,212,255,0.6); background: rgba(0,40,80,0.7); box-shadow: 0 0 8px rgba(0,212,255,0.2); }
|
|
|
+.summary-pie-area { background: rgba(0,20,40,0.4); border-radius: 4px; border: 1px solid rgba(0,212,255,0.1); padding: 2px; margin-bottom: 2px; }
|
|
|
+.progress-ring-bg { fill: none; stroke: rgba(255,255,255,0.08); stroke-width: 3; }
|
|
|
+.progress-ring-fill { fill: none; stroke-width: 3; stroke-linecap: round; transform: rotate(-90deg); transform-origin: 24px 24px; }
|
|
|
+.monitor-summary-name { color: #7bbef6; font-size: 10px; }
|
|
|
+.monitor-summary-count { font-size: 18px; font-weight: bold; }
|
|
|
canvas { display: block; margin: 0 auto; }
|
|
|
</style>
|