Files
app-govern/pages/index/comp/monitoringPoint.vue
2026-05-29 16:23:56 +08:00

664 lines
19 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>
<view class="itic2-page">
<scroll-view class="itic2-scroll" scroll-y :show-scrollbar="false">
<view class="project-header card">
<view class="project-title-row">
<view class="project-title-left">
<uni-icons custom-prefix="iconfont" type="icon-gongcheng" size="22" color="#376cf3" />
<text class="project-name">{{ engineeringName }}</text>
</view>
<view class="switch-btn" @click="switchEngineering">
<uni-icons type="loop" size="14" color="#376cf3" />
<text>切换工程</text>
</view>
</view>
<view class="stats-row">
<view class="stats-item" v-for="(item, idx) in summaryStats" :key="idx">
<text class="stats-num">{{ item.value }}</text>
<text class="stats-label">{{ item.label }}</text>
</view>
</view>
</view>
<view class="monitor-section-header">
<view class="section-title-row section-title-row--no-mb">
<uni-icons type="settings" size="18" color="#376cf3" />
<text class="section-title">已选展示指标</text>
</view>
</view>
<view class="indicators-card card">
<view class="indicator-tags">
<view
v-for="(tag, idx) in selectedIndicators"
:key="tag"
class="indicator-tag indicator-tag--active"
@click="removeIndicator(idx)"
>
<text class="indicator-tag-text">{{ formatIndicatorTag(tag) }}</text>
<uni-icons type="closeempty" size="12" color="#376cf3" />
</view>
<view class="indicator-tag indicator-tag--add" @click="openIndicatorPopup">
<text>+添加指标</text>
</view>
</view>
</view>
<view class="monitor-section">
<view class="monitor-section-header">
<view class="section-title-row section-title-row--no-mb">
<uni-icons type="map-pin-ellipse" size="20" color="#376cf3" />
<text class="section-title">监测点信息</text>
</view>
<view class="legend-row">
<view class="legend-item" v-for="phase in phaseColors" :key="phase.name">
<view class="legend-dot" :style="{ background: phase.color }" />
<text class="legend-text">{{ phase.name }}</text>
</view>
</view>
</view>
<view class="monitor-list">
<view class="monitor-card card" v-for="(point, idx) in monitoringPoints" :key="idx">
<view class="card-header">
<view class="event-icon">
<Cn-icon-transient name="监测点" />
</view>
<view class="card-header-info">
<text class="point-name ellipsis">{{ point.pointName }}</text>
<view class="meta-row">
<text class="meta-item ellipsis">项目{{ point.projectName }}</text>
<text class="meta-item ellipsis">设备{{ point.deviceName }}</text>
</view>
<text class="meta-time ellipsis">最新数据时间{{ point.dataTime || '-' }}</text>
</view>
</view>
<view class="params-section">
<view
v-for="(rowItems, rowIdx) in chunkedChildren(getDisplayChildren(point))"
:key="rowIdx"
class="double-row"
>
<view v-for="(child, childIdx) in rowItems" :key="childIdx" class="param-group">
<view class="param-title">
<text>{{ child.name }}</text>
</view>
<view class="phase-vertical">
<view class="phase-item-vertical">
<text class="phase-value-vertical" :style="{ color: phaseColors[0].color }">{{ child.A }}</text>
</view>
<view class="phase-divider" />
<view class="phase-item-vertical">
<text class="phase-value-vertical" :style="{ color: phaseColors[1].color }">{{ child.B }}</text>
</view>
<view class="phase-divider" />
<view class="phase-item-vertical">
<text class="phase-value-vertical" :style="{ color: phaseColors[2].color }">{{ child.C }}</text>
</view>
</view>
</view>
</view>
</view>
<view class="more-btn" @click="onMoreIndicators(point)">
<uni-icons type="list" size="16" color="#376cf3" />
<text>更多指标</text>
</view>
</view>
</view>
</view>
</scroll-view>
<uni-popup ref="indicatorPopup" type="bottom">
<view class="indicator-popup">
<view class="indicator-popup-header">
<text class="indicator-popup-cancel" @click="closeIndicatorPopup">取消</text>
<text class="indicator-popup-confirm" @click="confirmIndicatorPopup">确定</text>
</view>
<scroll-view class="indicator-popup-list" scroll-y>
<view v-if="targetLists.length === 0" class="indicator-popup-empty">
<text>暂无指标数据</text>
</view>
<view
v-for="item in targetLists"
:key="item.id || item.name"
class="indicator-popup-item"
:class="{ 'indicator-popup-item--active': popupSelectedIndicators.includes(item.name) }"
@click="togglePopupIndicator(item.name)"
>
<text>{{ item.name }}</text>
<uni-icons
v-if="popupSelectedIndicators.includes(item.name)"
type="checkmarkempty"
size="18"
color="#376cf3"
/>
</view>
</scroll-view>
</view>
</uni-popup>
</view>
</template>
<script>
import { queryByCode, queryCsDictTree } from '@/common/api/dictionary'
const DEFAULT_INDICATOR_CODES = ['Key_Power_Quality_V', 'Key_Power_Quality_I']
export default {
data() {
return {
targetLists: [],
popupSelectedIndicators: [],
engineeringName: '',
summaryStats: [
{ label: '项目总数', value: 10 },
{ label: '设备总数', value: 8 },
{ label: '监测点总数', value: 12 },
],
selectedIndicators: [],
phaseColors: [
{ name: 'A相', color: '#F1B22E' },
{ name: 'B相', color: '#2BA471' },
{ name: 'C相', color: '#D54941' },
],
monitoringPoints: [
{
pointName: '测试监测点',
projectName: '测试项目',
deviceName: '测试设备',
dataTime: '2026-05-29 12:00:00',
children: [
{ name: '电压总有效值', A: '10.52', B: '10.52', C: '10.52' },
{ name: '电流总有效值', A: '10.52', B: '10.52', C: '10.52' },
{ name: '基波电压幅值', A: '10.52', B: '10.52', C: '10.52' },
{ name: '基波电流幅值', A: '10.52', B: '10.52', C: '10.52' },
],
},
],
}
},
// 页面显示时同步工程名称
onShow() {
const engineering = uni.getStorageSync('engineering')
if (engineering?.name) {
this.engineeringName = engineering.name
}
},
// 加载电能质量指标字典
created() {
queryByCode('Key_Power_Quality').then((res) => {
queryCsDictTree(res.data.id).then((resp) => {
this.targetLists = (resp.data || []).slice().reverse()
this.initDefaultIndicators()
})
})
},
methods: {
// 指标名称超过6个字截断显示
formatIndicatorTag(name) {
if (!name || name.length <= 6) return name
return `${name.slice(0, 6)}...`
},
// 判断指标名称是否匹配(兼容括号后缀)
matchIndicator(name, selected) {
const base = (name || '').replace(/\(.*\)/, '')
return base.indexOf(selected) === 0 || selected.indexOf(base) === 0
},
// 初始化默认勾选指标
initDefaultIndicators() {
if (this.selectedIndicators.length) return
this.selectedIndicators = this.targetLists
.filter((item) => DEFAULT_INDICATOR_CODES.includes(item.code) && item.name)
.map((item) => item.name)
},
// 跳转切换工程
switchEngineering() {
uni.navigateTo({ url: '/pages/home/selectEngineering' })
},
// 移除已选指标
removeIndicator(idx) {
if (this.selectedIndicators.length <= 1) {
uni.showToast({ title: '至少保留一个指标', icon: 'none' })
return
}
this.selectedIndicators.splice(idx, 1)
},
// 打开指标选择弹窗
openIndicatorPopup() {
if (!this.targetLists.length) {
uni.showToast({ title: '暂无指标数据', icon: 'none' })
return
}
this.popupSelectedIndicators = this.targetLists
.filter((item) => this.selectedIndicators.some((name) => this.matchIndicator(item.name, name)))
.map((item) => item.name)
this.$refs.indicatorPopup.open()
},
// 关闭指标选择弹窗
closeIndicatorPopup() {
this.$refs.indicatorPopup.close()
this.popupSelectedIndicators = []
},
// 切换弹窗内指标勾选状态
togglePopupIndicator(name) {
const idx = this.popupSelectedIndicators.indexOf(name)
if (idx > -1) {
this.popupSelectedIndicators.splice(idx, 1)
} else {
this.popupSelectedIndicators.push(name)
}
},
// 确认指标选择
confirmIndicatorPopup() {
if (!this.popupSelectedIndicators.length) {
uni.showToast({ title: '至少保留一个指标', icon: 'none' })
return
}
this.selectedIndicators = [...this.popupSelectedIndicators]
this.closeIndicatorPopup()
},
// 查看更多指标
onMoreIndicators(point) {
const params = encodeURIComponent(
JSON.stringify({
...point,
engineeringName: this.engineeringName,
selectedIndicators: this.selectedIndicators,
}),
)
uni.navigateTo({
url: `/pages/index/comp/targetInfo?point=${params}`,
})
},
// 获取监测点当前展示的指标数据
getDisplayChildren(point) {
return (point.children || []).filter((child) =>
this.selectedIndicators.some((name) => this.matchIndicator(child.name, name)),
)
},
// 将指标列表按每行两个分组
chunkedChildren(children) {
const result = []
for (let i = 0; i < children.length; i += 2) {
result.push(children.slice(i, i + 2))
}
return result
},
},
}
</script>
<style lang="scss" scoped>
.itic2-page {
min-height: 100vh;
background: #f7f8fa;
}
.itic2-scroll {
height: 100vh;
padding-top: 20rpx;
box-sizing: border-box;
}
.card {
background: #ffffff;
border-radius: 10px;
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 3px 1px;
margin: 0 10px 10px;
overflow: hidden;
}
.project-header {
padding: 20rpx;
// margin-top: 20rpx;
}
.project-title-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.project-title-left {
display: flex;
align-items: center;
gap: 12rpx;
flex: 1;
min-width: 0;
}
.project-name {
font-size: 32rpx;
font-weight: 700;
color: #333333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.switch-btn {
display: flex;
align-items: center;
gap: 6rpx;
padding: 8rpx 20rpx;
border: 1rpx solid #376cf380;
background-color: #266FFF10;
border-radius: 16rpx;
flex-shrink: 0;
text {
font-size: 24rpx;
color: #376cf3;
}
}
.stats-row {
display: flex;
gap: 20rpx;
}
.stats-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4rpx;
padding: 16rpx 12rpx;
background: #376cf3;
border-radius: 16rpx;
color: #ffffff;
}
.stats-num {
font-size: 44rpx;
font-weight: 700;
line-height: 1.2;
}
.stats-label {
font-size: 26rpx;
}
.indicators-card {
padding: 24rpx;
}
.section-title-row {
display: flex;
align-items: center;
gap: 10rpx;
margin-bottom: 20rpx;
&--no-mb {
margin-bottom: 0;
}
}
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333333;
}
.indicator-tags {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.indicator-tag {
display: flex;
align-items: center;
gap: 8rpx;
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-size: 26rpx;
max-width: 100%;
.indicator-tag-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&--active {
background: #376cf315;
color: #376cf3;
}
&--add {
background: #f5f5f5;
color: #666666;
border: 1rpx dashed #dcdfe6;
}
}
.monitor-section-header {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12rpx;
padding: 0 10px 16rpx;
}
.legend-row {
display: flex;
gap: 20rpx;
align-items: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 8rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
}
.legend-text {
font-size: 24rpx;
color: #666666;
}
.monitor-list {
display: flex;
flex-direction: column;
padding-bottom: 40rpx;
}
.monitor-card {
border: 1rpx solid #eef2f6;
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 3px 1px;
}
.card-header {
padding: 20rpx 20rpx 12rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #eef2f6;
}
.event-icon {
width: 90rpx;
height: 90rpx;
border-radius: 16rpx;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
background-color: #376cf320;
flex-shrink: 0;
}
.card-header-info {
flex: 1;
min-width: 0;
}
.point-name {
display: block;
font-size: 30rpx;
font-weight: 700;
color: #333333;
margin-bottom: 8rpx;
}
.meta-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6rpx 12rpx;
}
.meta-item {
font-size: 26rpx;
color: #666666;
line-height: 1.3;
}
.meta-time {
display: block;
margin-top: 8rpx;
font-size: 26rpx;
color: #666666;
line-height: 1.3;
}
.params-section {
padding: 20rpx 16rpx;
}
.double-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.param-group {
min-width: 0;
background: #f3f3f3;
border-radius: 16rpx;
padding: 16rpx 8rpx 12rpx;
}
.param-title {
font-size: 24rpx;
color: #666666;
margin-bottom: 8rpx;
padding-left: 4rpx;
}
.phase-vertical {
display: flex;
align-items: stretch;
}
.phase-item-vertical {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 4rpx;
}
.phase-value-vertical {
font-size: 28rpx;
font-weight: 700;
}
.phase-divider {
width: 1px;
background: #d2d2d2;
margin: 8rpx 0;
}
.more-btn {
margin: 0 20rpx 20rpx;
height: 72rpx;
line-height: 72rpx;
text-align: center;
border: 1rpx solid #376cf380;
background-color: #266FFF10;
border-radius: 16rpx;
text {
font-size: 28rpx;
color: #376cf3;
font-weight: 500;
}
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.indicator-popup {
background: #ffffff;
overflow: hidden;
}
.indicator-popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 32rpx;
border-bottom: 1rpx solid #eef2f6;
}
.indicator-popup-cancel {
font-size: 27rpx;
color: #666666;
min-width: 80rpx;
}
.indicator-popup-confirm {
font-size: 28rpx;
color: #376cf3;
min-width: 80rpx;
text-align: right;
}
.indicator-popup-list {
max-height: 60vh;
padding: 16rpx 0;
box-sizing: border-box;
}
.indicator-popup-empty {
padding: 80rpx 0;
text-align: center;
font-size: 28rpx;
color: #999999;
}
.indicator-popup-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 32rpx;
font-size: 28rpx;
color: #333333;
&--active {
color: #376cf3;
background: #376cf310;
}
}
</style>