2198 lines
63 KiB
Vue
2198 lines
63 KiB
Vue
<template>
|
||
<view :class="props.type == 1 ? 'nut-cell' : ''">
|
||
<nut-config-provider :theme-vars="themeVarsBody">
|
||
<nut-row>
|
||
<nut-col :span="24">
|
||
<nut-button
|
||
type="primary"
|
||
class="btn font_28"
|
||
size="mini"
|
||
@click="updatePond"
|
||
:style="{ marginRight: '5px' }"
|
||
>
|
||
<template #icon>
|
||
<IconFont :name="setting" size="16" />
|
||
</template>
|
||
<text class="font_28">编辑塘口</text>
|
||
</nut-button>
|
||
<nut-button
|
||
type="primary"
|
||
class="btn font_28"
|
||
size="mini"
|
||
@click="addDeviceChild"
|
||
id="addDevice"
|
||
>
|
||
<template #icon>
|
||
<Uploader size="16" />
|
||
</template>
|
||
<text class="font_28">添加设备</text>
|
||
</nut-button>
|
||
</nut-col>
|
||
<nut-col :span="24">
|
||
<nut-divider class="divider" />
|
||
</nut-col>
|
||
</nut-row>
|
||
<nut-row v-if="doList.length > 0" class="pondInfo_home">
|
||
<nut-col :span="24">
|
||
<view class="device-tabs-wrapper">
|
||
<view class="device-tabs">
|
||
<view
|
||
v-for="item in doList"
|
||
:key="item.id"
|
||
class="custom-tab-item font_32"
|
||
:class="{ 'tab-active': doDev === item.id }"
|
||
@click="changeDev(item)"
|
||
>
|
||
<view class="custom-title" :class="{ active: doDev === item.id }"
|
||
>{{ item.deviceName }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</nut-col>
|
||
<nut-row v-for="item in doList" :key="item.id">
|
||
<nut-col :span="24" v-if="doDev == item.id">
|
||
<view class="view_f_between">
|
||
<view>
|
||
<nut-tag class="tag font_28 m_r_15" v-if="item.status == 1">
|
||
正常</nut-tag
|
||
>
|
||
<nut-tag class="tagErr font_28 m_r_15" v-else-if="item.status == 2">
|
||
{{ item.warnCodeInfo?.warnDescription }}</nut-tag
|
||
>
|
||
<nut-tag
|
||
class="font_28 m_r_15"
|
||
color="#E9E9E9"
|
||
text-color="#999999"
|
||
plain
|
||
v-else
|
||
>
|
||
空闲</nut-tag
|
||
>
|
||
<nut-tag class="tag font_28" v-if="item.isLinkCtrl">
|
||
已开启联动控制
|
||
</nut-tag>
|
||
<!-- <nut-tag class="tagErr font_28" v-else> 未开启联动控制 </nut-tag> -->
|
||
</view>
|
||
<!-- <view
|
||
class="font_28 c_1589E9"
|
||
:style="{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '12px', margin: '0 15px' }"
|
||
@click="setParams(item.id, item.deviceName, item.deviceType)"
|
||
>参数设置</view> -->
|
||
</view>
|
||
<nut-config-provider :theme-vars="themeVars">
|
||
<view class="m_t_20 view_f" style="width: 100%; overflow: hidden;">
|
||
<view
|
||
:style="{
|
||
width: 100 / Number(item.num) + '%',
|
||
textAlign: 'center',
|
||
}"
|
||
class="grid"
|
||
:class="{ active: prDev === 1 }"
|
||
@click="selectCharts(1)"
|
||
>
|
||
<view>溶解氧</view>
|
||
<view><text class="font_20">(Mg/L)</text></view>
|
||
<view>
|
||
<text
|
||
class="h_grid_content_5"
|
||
:class="
|
||
!item.up
|
||
? ''
|
||
: alarmJudgeCode(item.warnCodeInfo?.warnCode, 7)
|
||
? 'c_E22323'
|
||
: ''
|
||
"
|
||
>{{
|
||
item.up ? toDecimal(item.valueDissolvedOxygen, 2) : "--"
|
||
}}</text
|
||
></view
|
||
>
|
||
</view>
|
||
<view
|
||
:style="{
|
||
width: 100 / Number(item.num) + '%',
|
||
textAlign: 'center',
|
||
}"
|
||
class="grid"
|
||
:class="{ active: prDev === 2 }"
|
||
@click="selectCharts(2)"
|
||
>
|
||
<view>水温</view>
|
||
<view><text class="font_20">(℃)</text></view>
|
||
<view>
|
||
<text
|
||
class="h_grid_content_5"
|
||
:class="
|
||
!item.up
|
||
? ''
|
||
: alarmJudgeCode(item.warnCodeInfo?.warnCode, 9) ||
|
||
alarmJudgeCode(item.warnCodeInfo?.warnCode, 8)
|
||
? 'c_E22323'
|
||
: ''
|
||
"
|
||
>{{ item.up ? toDecimal(item.valueTemperature, 2) : "--" }}</text
|
||
>
|
||
</view>
|
||
</view>
|
||
<view
|
||
:style="{
|
||
width: 100 / Number(item.num) + '%',
|
||
textAlign: 'center',
|
||
}"
|
||
class="grid"
|
||
:class="{ active: prDev === 3 }"
|
||
@click="selectCharts(3)"
|
||
>
|
||
<view>饱和度</view>
|
||
<view><text class="font_20">(%)</text></view>
|
||
<view>
|
||
<text
|
||
class="h_grid_content_5"
|
||
:class="
|
||
!item.up
|
||
? ''
|
||
: alarmJudgeCode(item.warnCodeInfo?.warnCode, 10)
|
||
? 'c_E22323'
|
||
: ''
|
||
"
|
||
>{{ item.up ? toDecimal(item.valueSaturability, 2) : "--" }}</text
|
||
>
|
||
</view>
|
||
</view>
|
||
<view
|
||
v-show="item.isPh"
|
||
:style="{
|
||
width: 100 / Number(item.num) + '%',
|
||
textAlign: 'center',
|
||
}"
|
||
class="grid"
|
||
:class="{ active: prDev === 4 }"
|
||
@click="selectCharts(4)"
|
||
>
|
||
<view>PH</view>
|
||
<view><text class="font_20">(Ph)</text></view>
|
||
<view>
|
||
<text class="h_grid_content_5">{{ item.up ?toDecimal(item.valuePH, 2):'--' }}</text>
|
||
</view>
|
||
</view>
|
||
<view
|
||
v-show="item.isSa"
|
||
:style="{
|
||
width: 100 / Number(item.num) + '%',
|
||
textAlign: 'center',
|
||
}"
|
||
class="grid"
|
||
:class="{ active: prDev === 5 }"
|
||
@click="selectCharts(5)"
|
||
>
|
||
<view>盐度</view>
|
||
<view><text class="font_20">(‰)</text></view>
|
||
<view>
|
||
<text class="h_grid_content_5">{{
|
||
item.up ?toDecimal(item.valueSalinity, 2):'--'
|
||
}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</nut-config-provider>
|
||
<view class="m_t_20" style="width: 100%; box-sizing: border-box;">
|
||
<view class="chart-header-row">
|
||
<view class="left-section">
|
||
<text class="chart-title-text">{{
|
||
prDev === 1
|
||
? "溶解氧"
|
||
: prDev === 2
|
||
? "水 温"
|
||
: prDev === 3
|
||
? "饱和度"
|
||
: prDev === 4
|
||
? "PH "
|
||
: prDev === 5
|
||
? "盐 度"
|
||
: ""
|
||
}}</text>
|
||
</view>
|
||
<view class="center-section">
|
||
<view class="day-selector">
|
||
<view
|
||
class="day-btn"
|
||
:class="{ active: dayDev === 1 }"
|
||
@click="changeDay(1)"
|
||
>
|
||
1天
|
||
</view>
|
||
<view
|
||
class="day-btn"
|
||
:class="{ active: dayDev === 3 }"
|
||
@click="changeDay(3)"
|
||
>
|
||
3天
|
||
</view>
|
||
<view
|
||
class="day-btn"
|
||
:class="{ active: dayDev === 7 }"
|
||
@click="changeDay(7)"
|
||
>
|
||
7天
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="right-section">
|
||
<view v-show="showChart" @click="fullscreenChart" class="action-link">放大</view>
|
||
<text v-show="showChart" class="divider-dot">·</text>
|
||
<view @click="changeShow" class="action-link">{{ setTitle }}</view>
|
||
</view>
|
||
</view>
|
||
<!-- <nut-col :span="8" class="font_32 f_weight c_222">
|
||
|
||
{{
|
||
prDev === 1
|
||
? "溶解氧"
|
||
: prDev === 2
|
||
? "水 温"
|
||
: prDev === 3
|
||
? "饱和度"
|
||
: prDev === 4
|
||
? "PH "
|
||
: prDev === 5
|
||
? "盐 度"
|
||
: ""
|
||
}}
|
||
</nut-col>
|
||
<nut-col
|
||
:span="8"
|
||
:style="{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
}"
|
||
>
|
||
<view
|
||
class="gridSmall font_26 c_222"
|
||
:class="{ active: dayDev === 1 }"
|
||
@click="changeDay(1)"
|
||
>
|
||
1天
|
||
</view>
|
||
<view
|
||
class="gridSmall font_26 c_222"
|
||
:class="{ active: dayDev === 3 }"
|
||
@click="changeDay(3)"
|
||
>
|
||
3天
|
||
</view>
|
||
<view
|
||
class="gridSmall font_26 c_222"
|
||
:class="{ active: dayDev === 7 }"
|
||
@click="changeDay(7)"
|
||
>
|
||
7天
|
||
</view>
|
||
</nut-col>
|
||
<nut-col :span="8" class="font_32 c_1589E9" :style="{ textAlign: 'right' }">
|
||
<view @click="changeShow">{{ setTitle }}</view>
|
||
</nut-col> -->
|
||
</view>
|
||
<view :style="{ position: 'relative', marginTop: '10px', width: '100%', overflow: 'hidden' }" v-show="showChart">
|
||
<view class="loading-container" v-show="loading">
|
||
<Loading />
|
||
</view>
|
||
<canvas
|
||
v-if="chartsDatas.length > 0"
|
||
class="charts"
|
||
type="2d"
|
||
:id="
|
||
'GdHQcFQYzLhfeEvZbEJnhDxJzBlbCxhE' +
|
||
String(doDev) +
|
||
String(prDev) +
|
||
String(dayDev)
|
||
"
|
||
@touchend="touchEnd"
|
||
@touchstart="touchCandle"
|
||
@touchmove="moveCandle"
|
||
:style="{
|
||
width: chartConf.cWidth + 'px',
|
||
height: chartConf.cHeight + 'px'
|
||
}"
|
||
></canvas>
|
||
<nut-empty description="暂无数据" v-else>
|
||
<template #image>
|
||
<img :src="pond404" />
|
||
</template>
|
||
</nut-empty>
|
||
</view>
|
||
<!-- 功能按钮区域 -->
|
||
<nut-row class="action-buttons" v-if="doList.length > 0">
|
||
<nut-col :span="24">
|
||
<view class="button-wrapper">
|
||
<nut-button
|
||
type="primary"
|
||
class="action-btn"
|
||
size="small"
|
||
@click="goDeviceInfo"
|
||
>
|
||
设备属性
|
||
</nut-button>
|
||
<nut-button
|
||
type="primary"
|
||
class="action-btn"
|
||
size="small"
|
||
@click="goAlarmSettings"
|
||
>
|
||
报警设置
|
||
</nut-button>
|
||
<nut-button
|
||
type="primary"
|
||
class="action-btn"
|
||
size="small"
|
||
@click="goDataQuery"
|
||
>
|
||
数据查询
|
||
</nut-button>
|
||
</view>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-row>
|
||
<nut-row v-else>
|
||
<nut-col :span="24">
|
||
<nut-empty description="暂无设备">
|
||
<template #image>
|
||
<img :src="pond404" />
|
||
</template>
|
||
</nut-empty>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-config-provider>
|
||
</view>
|
||
<nut-config-provider :theme-vars="themeVarsOther">
|
||
<nut-cell v-if="controlList.length > 0">
|
||
<!-- <nut-cell v-if="controlList.length > 0"> -->
|
||
<nut-row>
|
||
<nut-col :span="24">
|
||
<nut-row :gutter="10">
|
||
<nut-col :span="4" :style="{ lineHeight: '60rpx' }">
|
||
<view class="font_32 f_weight c_22">控制器</view>
|
||
</nut-col>
|
||
<nut-col
|
||
:span="12"
|
||
:style="{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
lineHeight: '60rpx',
|
||
}"
|
||
>
|
||
<view
|
||
:style="{
|
||
color: '#888',
|
||
fontSize: '26rpx',
|
||
marginRight: '10rpx',
|
||
}"
|
||
>夜间防误开关</view
|
||
>
|
||
<!-- <nut-switch v-model="controlVal" active-color="#04D98A" @change="changeControl"/> -->
|
||
<nut-switch
|
||
v-model="keepNightOpen"
|
||
active-color="#04D98A"
|
||
@change="(value, event) => changeControl({ value, event })"
|
||
/>
|
||
</nut-col>
|
||
<nut-col :span="4" :style="{ lineHeight: '60rpx' }">
|
||
<view
|
||
class="gridCon"
|
||
:class="openStatus == 1 ? 'openS' : 'openD'"
|
||
@click="changeAllSwitch(false)"
|
||
>全关
|
||
</view>
|
||
</nut-col>
|
||
<nut-col :span="4" :style="{ lineHeight: '60rpx' }">
|
||
<view
|
||
class="gridCon"
|
||
:class="openStatus == 2 ? 'openS' : 'openD'"
|
||
@click="changeAllSwitch(true)"
|
||
>全开
|
||
</view>
|
||
</nut-col>
|
||
<nut-col :span="24">
|
||
<nut-divider class="divider" />
|
||
</nut-col>
|
||
</nut-row>
|
||
<nut-row
|
||
:gutter="10"
|
||
v-for="item in controlList"
|
||
:key="item.id"
|
||
@click="setControl(item)"
|
||
>
|
||
<nut-col :span="5" :style="{ height: '70px' }">
|
||
<nut-row :gutter="5">
|
||
<nut-col :span="24" class="tc">
|
||
<view class="table-container">
|
||
<view class="single-line" :style="{ marginBottom: '1px' }">
|
||
{{ item.deviceName }}
|
||
</view>
|
||
<view class="tagErr single-line" v-if="item.errorMessage">{{
|
||
item.errorMessage
|
||
}}</view>
|
||
</view>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-col>
|
||
<nut-col :span="18" :style="{ height: '70px' }">
|
||
<nut-row :gutter="5">
|
||
<nut-col :span="6" class="tc">
|
||
<view
|
||
><img
|
||
:src="switchRes(item.listSwitch, 1, 'img')"
|
||
class="img_size_def"
|
||
/></view>
|
||
<view class="tc truncate">{{
|
||
switchRes(item.listSwitch, 1, "switchName")
|
||
}}</view>
|
||
</nut-col>
|
||
<nut-col :span="6" class="tc">
|
||
<view
|
||
><img
|
||
:src="switchRes(item.listSwitch, 2, 'img')"
|
||
class="img_size_def"
|
||
/></view>
|
||
<view class="tc truncate">{{
|
||
switchRes(item.listSwitch, 2, "switchName")
|
||
}}</view>
|
||
</nut-col>
|
||
<nut-col :span="6" class="tc">
|
||
<view
|
||
><img
|
||
:src="switchRes(item.listSwitch, 3, 'img')"
|
||
class="img_size_def"
|
||
/></view>
|
||
<view class="tc truncate">{{
|
||
switchRes(item.listSwitch, 3, "switchName")
|
||
}}</view>
|
||
</nut-col>
|
||
<nut-col :span="6" class="tc">
|
||
<view
|
||
><img
|
||
:src="switchRes(item.listSwitch, 4, 'img')"
|
||
class="img_size_def"
|
||
/></view>
|
||
<view class="tc truncate">{{
|
||
switchRes(item.listSwitch, 4, "switchName")
|
||
}}</view>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-col>
|
||
<nut-col :span="24">
|
||
<nut-divider
|
||
:style="{
|
||
color: '#eee',
|
||
borderColor: '#eee',
|
||
padding: '0 0 0 0px',
|
||
margin: '0',
|
||
}"
|
||
/>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-cell>
|
||
</nut-config-provider>
|
||
<!-- 引导 -->
|
||
<nut-tour
|
||
v-model="showTour"
|
||
:steps="steps"
|
||
type="tile"
|
||
location="bottom-end"
|
||
></nut-tour>
|
||
<!-- 弹出层提示 -->
|
||
<nut-toast :msg="state.msg" v-model:visible="state.show" :type="state.type" />
|
||
<nut-config-provider :theme-vars="themeVarsDialog">
|
||
<nut-dialog
|
||
v-model:visible="visControl"
|
||
class="dialog"
|
||
text-align="left"
|
||
no-footer
|
||
>
|
||
<view :style="{ height: '100%' }">
|
||
<view class="d_body">
|
||
<nut-row>
|
||
<nut-col :span="12">
|
||
<view class="d_title">{{ controlInfo.deviceName }}</view>
|
||
</nut-col>
|
||
<nut-col :span="12" :style="{ float: 'right' }">
|
||
<view
|
||
:style="{
|
||
color: '#1589E9',
|
||
fontSize: '32rpx',
|
||
float: 'right',
|
||
marginLeft: 'auto',
|
||
}"
|
||
@click="setParamsCtr(controlInfo.id)"
|
||
>
|
||
参数设置</view
|
||
>
|
||
</nut-col>
|
||
<nut-col :span="24">
|
||
<nut-divider class="divider" />
|
||
</nut-col>
|
||
<nut-col
|
||
:span="24"
|
||
:style="{
|
||
marginTop: index === 0 ? '20rpx' : '45rpx',
|
||
paddingBottom: '10rpx'
|
||
}"
|
||
v-for="(item, index) in controlInfo.listSwitch"
|
||
:key="item.id"
|
||
>
|
||
<nut-row :style="{ lineHeight: '50rpx', alignItems: 'center' }">
|
||
<nut-col span="10">
|
||
<view class="d_c_title" :style="{ whiteSpace: 'nowrap' }">{{ item.switchName }}</view>
|
||
</nut-col>
|
||
<nut-col
|
||
span="14"
|
||
:style="{
|
||
textAlign: 'right',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'flex-end'
|
||
}"
|
||
>
|
||
<nut-switch
|
||
:model-value="item.isOpen === 1"
|
||
active-color="#04D98A"
|
||
@change="(value, event) => setSwitchOpen({ value, event }, item)"
|
||
/>
|
||
<view
|
||
:style="{
|
||
color: '#1589E9',
|
||
fontSize: '28rpx',
|
||
marginLeft: '50rpx',
|
||
whiteSpace: 'nowrap'
|
||
}"
|
||
@click="setSwitch(item.id, item.switchName)"
|
||
>设置
|
||
</view>
|
||
</nut-col>
|
||
</nut-row>
|
||
</nut-col>
|
||
</nut-row>
|
||
</view>
|
||
<view class="d_btn">
|
||
<nut-row>
|
||
<!-- <nut-col :span="12" :style="{ border: '1px solid #eee', color: '#222' }" @click="visControl = false">
|
||
取消
|
||
</nut-col> -->
|
||
<nut-col
|
||
:span="24"
|
||
:style="{ border: '1px solid #eee', color: '#15BBC5' }"
|
||
@click="visControl = false"
|
||
>
|
||
确定
|
||
</nut-col>
|
||
</nut-row>
|
||
</view>
|
||
</view>
|
||
</nut-dialog>
|
||
</nut-config-provider>
|
||
<!-- 防误触开关提醒 -->
|
||
<nut-dialog v-model:visible="alertShow" text-align="left" pop-class="pop-class">
|
||
<template #default>
|
||
<view :style="{ height: '100%', padding: '10px' }">
|
||
<nut-row>
|
||
<nut-col :span="24" class="font_28 c_E22323">
|
||
说明:开启“夜间防误关”功能后,该增氧机在从晚上23:00次日6:00如发生状态由开启变为关闭(除定时关机、联动关机、遥控关机外)时,会进行电话报警。
|
||
</nut-col>
|
||
</nut-row>
|
||
</view>
|
||
</template>
|
||
<template #footer>
|
||
<view class="d_btn" :style="{ position: 'absolute' }">
|
||
<nut-row>
|
||
<nut-col
|
||
:span="12"
|
||
:style="{ border: '1px solid #eee', color: '#222' }"
|
||
@click="cancel"
|
||
>
|
||
取消
|
||
</nut-col>
|
||
<nut-col :span="12" :style="{ border: '1px solid #eee', color: '#15BBC5' }">
|
||
<nut-button type="primary" shape="square" block @click="submit"
|
||
>确定</nut-button
|
||
>
|
||
</nut-col>
|
||
</nut-row>
|
||
</view>
|
||
</template>
|
||
</nut-dialog>
|
||
|
||
</template>
|
||
<script setup lang="ts" name="PondInfo">
|
||
import { IconFont, Loading, RectRight, Uploader } from "@nutui/icons-vue-taro";
|
||
import { imgUrl } from "@/utils/imgUrl";
|
||
import Taro from "@tarojs/taro";
|
||
import uCharts from "@/utils/js-sdk/u-charts/u-charts.js";
|
||
import {
|
||
timeX,
|
||
toDecimal,
|
||
alarmJudgeCode,
|
||
generateTimeSlotsEndingToday,
|
||
formatDateString,
|
||
} from "@/utils/tools";
|
||
import { defineEmits } from "vue";
|
||
import { pondDeviceInfo } from "@/api/pond";
|
||
import { deviceHistory, turnPondSwitch, turnSwitch, setNightProtect } from "@/api/device";
|
||
import { stateType } from "@/utils/types";
|
||
const pond404 = `${imgUrl}zanwurenwu.png`;
|
||
const setting = `${imgUrl}setting.png`;
|
||
const phone = `${imgUrl}tel.png`;
|
||
// 其他
|
||
const c1 = `${imgUrl}c1.png`;
|
||
const c1_d = `${imgUrl}c1_d.png`;
|
||
const c1_n = `${imgUrl}c1_n.png`;
|
||
const c2 = `${imgUrl}c2.png`;
|
||
const c2_d = `${imgUrl}c2_d.png`;
|
||
const c2_n = `${imgUrl}c2_n.png`;
|
||
const c3 = `${imgUrl}c3.png`;
|
||
const c3_d = `${imgUrl}c3_d.png`;
|
||
const c3_n = `${imgUrl}c3_n.png`;
|
||
const c_def = `${imgUrl}c_def.png`;
|
||
const c_def2 = `${imgUrl}c_def2.png`;
|
||
const c4 = `${imgUrl}c4.png`;
|
||
const c4_d = `${imgUrl}c4_d.png`;
|
||
const c4_n = `${imgUrl}c4_n.png`;
|
||
// 选中的溶解氧
|
||
const doDev = ref();
|
||
// 选中的指标参数
|
||
const prDev = ref(1);
|
||
const dayDev = ref(1);
|
||
// 溶解氧机列表
|
||
const doList = ref<any[]>([]);
|
||
const oxyWarnLower = ref(null);
|
||
const tempWarnUpper = ref(null);
|
||
const tempWarnLower = ref(null);
|
||
const setTitle = ref("隐藏");
|
||
const loading = ref<boolean>(false);
|
||
const showChart = ref<boolean>(true);
|
||
const themeVars = ref({
|
||
gridItemTextMargin: "0px",
|
||
});
|
||
const themeVarsBody = ref({
|
||
noticebarAcrossHeight: "60rpx",
|
||
cellAfterBorderBottom: "0px",
|
||
cellPadding: "20rpx",
|
||
tabsHorizontalTitlesHeight: "60rpx",
|
||
});
|
||
const themeVarsOther = ref({
|
||
switchWidth: "36px",
|
||
switchHeight: "21px",
|
||
switchInsideWidth: "14px",
|
||
switchInsideHeight: "14px",
|
||
});
|
||
// 弹窗开关样式配置,与主页面开关保持一致
|
||
const themeVarsDialog = ref({
|
||
switchWidth: "36px",
|
||
switchHeight: "21px",
|
||
switchInsideWidth: "14px",
|
||
switchInsideHeight: "14px",
|
||
});
|
||
// 图表参数
|
||
const paramsHistory = ref({
|
||
serialNum: "", // 设备序列号
|
||
startTime: "", // 开始时间
|
||
endTime: "", // 结束时间
|
||
intervalType: 1, // 时间间隔类型:1-1天, 2-3天, 3-7天
|
||
});
|
||
// 图表数据
|
||
const chartsDatas = ref<any>([]);
|
||
const uChartsOp = ref<any>({});
|
||
// 图表参数
|
||
const chartConf = reactive({
|
||
cWidth: 750,
|
||
cHeight: 500,
|
||
pixelRatio: 1,
|
||
top: 0,
|
||
left: 0,
|
||
});
|
||
const props = defineProps({
|
||
// 数据
|
||
modelValue: {
|
||
type: Number || String,
|
||
required: true,
|
||
},
|
||
type: {
|
||
type: Number || String,
|
||
required: true,
|
||
default: 1,
|
||
},
|
||
|
||
});
|
||
const pondDeviceRes = ref<any>();
|
||
const emit = defineEmits(["getPondInfo", "getRes", "render", "addDevice"]);
|
||
// 首个设备id
|
||
const firstDoId = ref();
|
||
// 7天x轴数据
|
||
const x7 = ref([]);
|
||
const showTour = ref<boolean>(false);
|
||
const steps = ref([
|
||
{
|
||
content: "请添加塘口",
|
||
target: "addPondBtn",
|
||
},
|
||
]);
|
||
const hideState = ref(true);
|
||
// 控制器列表
|
||
const controlList = ref<any[]>([]);
|
||
// 控制器开关
|
||
const state: stateType = reactive({
|
||
msg: "",
|
||
type: "text",
|
||
center: true,
|
||
show: false,
|
||
});
|
||
const visControl = ref(false);
|
||
// 当前控制器下开关列表
|
||
const controlInfo = ref<any>({
|
||
deviceName: undefined,
|
||
id: undefined,
|
||
listSwitch: [],
|
||
});
|
||
// 防误触开关提醒
|
||
const alertShow = ref(false);
|
||
const openStatus = ref(0);
|
||
const keepNightOpen = ref(false);
|
||
/** -----------------method start----------------- */
|
||
Taro.useDidShow(() => {
|
||
const sysInfo = Taro.getSystemInfoSync();
|
||
// 修改宽度计算:从 700/750 改为 680/750,为左右边距预留空间
|
||
let cWidth = (680 / 750) * sysInfo.screenWidth;
|
||
let cHeight = (220 / 750) * sysInfo.windowHeight;
|
||
let pixelRatio = sysInfo.pixelRatio;
|
||
chartConf.cWidth = cWidth;
|
||
chartConf.cHeight = cHeight;
|
||
chartConf.pixelRatio = pixelRatio;
|
||
|
||
// 关闭弹窗类
|
||
alertShow.value = false;
|
||
visControl.value = false;
|
||
getDeviceList();
|
||
getNowDate();
|
||
});
|
||
onMounted(() => {
|
||
const sysInfo = Taro.getSystemInfoSync();
|
||
// 修改宽度计算:从 700/750 改为 680/750,为左右边距预留空间
|
||
let cWidth = (680 / 750) * sysInfo.screenWidth;
|
||
let cHeight = (220 / 750) * sysInfo.windowHeight;
|
||
let pixelRatio = sysInfo.pixelRatio;
|
||
chartConf.cWidth = cWidth;
|
||
chartConf.cHeight = cHeight;
|
||
chartConf.pixelRatio = pixelRatio;
|
||
|
||
// 关闭弹窗类
|
||
alertShow.value = false;
|
||
visControl.value = false;
|
||
|
||
getDeviceList();
|
||
getNowDate();
|
||
});
|
||
watchEffect(() => {
|
||
emit("getPondInfo", pondDeviceRes.value);
|
||
});
|
||
watch(
|
||
() => props.modelValue,
|
||
() => {
|
||
getDeviceList();
|
||
}
|
||
);
|
||
// 获取塘口下的设备列表
|
||
function getDeviceList() {
|
||
emit("render", "");
|
||
const id = props.modelValue;
|
||
if (!id) {
|
||
return;
|
||
}
|
||
// 查询塘口下设备列表
|
||
pondDeviceInfo({ id }).then((res: any) => {
|
||
if (res.code == 200) {
|
||
setTimeout(() => {
|
||
if (
|
||
res.data &&
|
||
res.data.listDetector <= 0 &&
|
||
res.data.listDetector.length <= 0 &&
|
||
res.data.listController.length <= 0 &&
|
||
props.type == 1 &&
|
||
hideState.value
|
||
) {
|
||
showTour.value = true;
|
||
steps.value = [
|
||
{
|
||
content: "请添加设备",
|
||
target: "addDevice",
|
||
},
|
||
];
|
||
}
|
||
}, 500);
|
||
const oxyIds = [];
|
||
res.data.listDetector.forEach((item: any) => {
|
||
oxyIds.push(item.id);
|
||
let num = 3;
|
||
const devCount = Number(item.deviceCount) + Number(item.switchCount);
|
||
const msg = item.warnCodeInfo?.warnDescription || '';
|
||
const showPh = !alarmJudgeCode(item.warnCodeInfo?.warnCode, 1);
|
||
const showSa = !alarmJudgeCode(item.warnCodeInfo?.warnCode, 2);
|
||
if (msg) {
|
||
item.isPh = showPh;
|
||
item.isSa = showSa;
|
||
} else {
|
||
item.isPh = showPh == false ? false : item.valuePH ? item.valuePH : false;
|
||
item.isSa =
|
||
showSa == false ? false : item.valueSalinity ? item.valueSalinity : false;
|
||
}
|
||
if (item.isPh) {
|
||
num += 1;
|
||
}
|
||
if (item.isSa) {
|
||
num += 1;
|
||
}
|
||
item.num = num;
|
||
item.status = Number(item.deviceCount) == 0 ? 0 : msg ? 2 : 1;
|
||
// 判断设备是否离线,异常、断电 0 6 15
|
||
const code0 = alarmJudgeCode(item.warnCodeInfo?.warnCode, 0);
|
||
const code6 = alarmJudgeCode(item.warnCodeInfo?.warnCode, 6);
|
||
const code11 = alarmJudgeCode(item.warnCodeInfo?.warnCode, 15);
|
||
const code17 = alarmJudgeCode(item.warnCodeInfo?.warnCode, 17);
|
||
item.up = true;
|
||
if (code0 || code6 || code11 || code17) {
|
||
item.up = false;
|
||
}
|
||
});
|
||
pondDeviceRes.value = res.data;
|
||
keepNightOpen.value = res.data.keepNightOpen === 1;
|
||
doList.value = res.data.listDetector;
|
||
controlList.value = res.data.listController;
|
||
if (res.data.listDetector && res.data.listDetector.length > 0) {
|
||
firstDoId.value = res.data.listDetector[0].id
|
||
? res.data.listDetector[0].id
|
||
: undefined;
|
||
doDev.value = doDev.value
|
||
? doDev.value
|
||
: res.data.listDetector[0].id
|
||
? res.data.listDetector[0].id
|
||
: undefined;
|
||
|
||
if (doDev.value) {
|
||
// 根据当前选中的设备设置 serialNum
|
||
const currentDevice = res.data.listDetector.find((item: any) => item.id === doDev.value);
|
||
if (currentDevice && currentDevice.serialNum) {
|
||
paramsHistory.value.serialNum = currentDevice.serialNum;
|
||
}
|
||
|
||
if (!oxyIds.includes(doDev.value)) {
|
||
doDev.value = res.data.listDetector[0].id
|
||
? res.data.listDetector[0].id
|
||
: undefined;
|
||
oxyWarnLower.value = res.data.listDetector[0].oxyWarnLower
|
||
? res.data.listDetector[0].oxyWarnLower
|
||
: undefined;
|
||
tempWarnUpper.value = res.data.listDetector[0].tempWarnUpper
|
||
? res.data.listDetector[0].tempWarnUpper
|
||
: undefined;
|
||
tempWarnLower.value = res.data.listDetector[0].tempWarnLower
|
||
? res.data.listDetector[0].tempWarnLower
|
||
: undefined;
|
||
} else {
|
||
res.data.listDetector.forEach((r) => {
|
||
if (r.id == doDev.value) {
|
||
oxyWarnLower.value = r.oxyWarnLower ? r.oxyWarnLower : undefined;
|
||
tempWarnUpper.value = r.tempWarnUpper ? r.tempWarnUpper : undefined;
|
||
tempWarnLower.value = r.tempWarnLower ? r.tempWarnLower : undefined;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
emit("getPondInfo", { res: res.data });
|
||
setTimeout(() => {
|
||
getDaysAgo(dayDev.value);
|
||
}, 200);
|
||
}
|
||
});
|
||
}
|
||
// 选择日期
|
||
function changeDay(e) {
|
||
dayDev.value = e;
|
||
// 直接传递天数,getDaysAgo 会自动设置正确的 intervalType
|
||
getDaysAgo(e);
|
||
}
|
||
// 显隐控制器
|
||
// 显隐控制器
|
||
function changeShow() {
|
||
showChart.value = !showChart.value;
|
||
if (showChart.value) {
|
||
setTitle.value = "隐藏";
|
||
setTimeout(() => {
|
||
getDaysAgo(dayDev.value);
|
||
}, 200);
|
||
} else {
|
||
setTitle.value = "显示";
|
||
}
|
||
}
|
||
|
||
// 全屏显示图表 - 跳转到单独页面
|
||
function fullscreenChart() {
|
||
// 获取当前设备的 serialNum
|
||
const currentDevice = pondDeviceRes.value?.listDetector?.find(
|
||
(item: any) => item.id === doDev.value
|
||
);
|
||
|
||
if (!currentDevice) {
|
||
Taro.showToast({
|
||
title: '设备信息不存在',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
const serialNum = currentDevice.serialNum || currentDevice.serialNumber || currentDevice.deviceSerialNum || currentDevice.sn;
|
||
|
||
if (!serialNum) {
|
||
Taro.showToast({
|
||
title: '设备序列号不存在',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 跳转到全屏图表页面
|
||
Taro.navigateTo({
|
||
url: `/components/other/chartFullscreen?serialNum=${serialNum}&dataType=${prDev.value}&dayType=${dayDev.value}&oxyWarnLower=${oxyWarnLower.value || ''}&tempWarnUpper=${tempWarnUpper.value || ''}&tempWarnLower=${tempWarnLower.value || ''}`
|
||
});
|
||
}
|
||
|
||
// 切换检测指标
|
||
function selectCharts(e) {
|
||
prDev.value = e;
|
||
|
||
// 直接使用已有的图表数据重新绘制,避免重复请求和抖动
|
||
if (chartsDatas.value && chartsDatas.value.length > 0) {
|
||
// 使用 updateCharts 而不是 drawCharts,避免重新创建图表实例
|
||
setTimeout(() => {
|
||
drawCharts(2);
|
||
}, 50);
|
||
}
|
||
}
|
||
// 参数设置
|
||
function setParams(id: number, name: string, type: number) {
|
||
if (type == 1) {
|
||
Taro.navigateTo({
|
||
url: `/home/wqm?id=${id}&name=${name}&page=home`,
|
||
});
|
||
} else {
|
||
Taro.navigateTo({
|
||
url: `/home/ctr?id=${id}&name=${name}&page=home`,
|
||
});
|
||
}
|
||
}
|
||
// 切换设备-加载图表
|
||
function changeDev(item: any) {
|
||
doDev.value = item.id;
|
||
oxyWarnLower.value = item.oxyWarnLower;
|
||
tempWarnUpper.value = item.tempWarnUpper;
|
||
tempWarnLower.value = item.tempWarnLower;
|
||
paramsHistory.value.serialNum = item.serialNum; // 使用设备序列号
|
||
dayDev.value = 1;
|
||
getDaysAgo(1);
|
||
}
|
||
// 设备历史数据图表
|
||
function getDeviceHistory() {
|
||
loading.value = true;
|
||
|
||
if (!paramsHistory.value.serialNum) {
|
||
loading.value = false;
|
||
return;
|
||
}
|
||
|
||
deviceHistory(paramsHistory.value)
|
||
.then((res: any) => {
|
||
// 后端直接返回数组,而不是 {code: 200, data: []}
|
||
if (Array.isArray(res)) {
|
||
chartsDatas.value = res;
|
||
drawCharts(1);
|
||
} else if (res.code == 200) {
|
||
// 兼容标准响应格式
|
||
chartsDatas.value = res.data || [];
|
||
drawCharts(1);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
})
|
||
.finally(() => {
|
||
loading.value = false;
|
||
});
|
||
}
|
||
// 监测图表 - 重构版本
|
||
function drawCharts(type) {
|
||
loading.value = true;
|
||
Taro.nextTick(() => {
|
||
const data = chartsDatas.value; // 后端直接返回的数据数组
|
||
const paramsType = prDev.value; // 当前选中的指标类型
|
||
|
||
if (!data || data.length === 0) {
|
||
loading.value = false;
|
||
return;
|
||
}
|
||
|
||
let series: any = [];
|
||
let x_time_list: any = []; // x轴时间列表
|
||
let seriesData: any = []; // y轴数据
|
||
let warnLines: any = []; // 告警线
|
||
|
||
// 根据 paramsType 选择对应的数据字段
|
||
let fieldName = '';
|
||
let seriesName = '';
|
||
|
||
if (paramsType == 1) {
|
||
fieldName = 'dissolvedOxygen';
|
||
seriesName = '溶解氧';
|
||
// 添加告警下限
|
||
if (oxyWarnLower.value) {
|
||
warnLines = data.map(() => oxyWarnLower.value);
|
||
series.push({
|
||
name: '告警下限',
|
||
data: warnLines,
|
||
color: '#c12e34',
|
||
lineType: 'dash',
|
||
});
|
||
}
|
||
} else if (paramsType == 2) {
|
||
fieldName = 'temperature';
|
||
seriesName = '水温';
|
||
// 添加告警上下限
|
||
if (tempWarnLower.value) {
|
||
warnLines = data.map(() => tempWarnLower.value);
|
||
series.push({
|
||
name: '告警下限',
|
||
data: warnLines,
|
||
color: '#c12e34',
|
||
lineType: 'dash',
|
||
});
|
||
}
|
||
if (tempWarnUpper.value) {
|
||
const upperLines = data.map(() => tempWarnUpper.value);
|
||
series.push({
|
||
name: '告警上限',
|
||
data: upperLines,
|
||
color: '#e098c7',
|
||
lineType: 'dash',
|
||
});
|
||
}
|
||
} else if (paramsType == 3) {
|
||
fieldName = 'saturability';
|
||
seriesName = '饱和度';
|
||
} else if (paramsType == 4) {
|
||
fieldName = 'ph';
|
||
seriesName = 'PH';
|
||
} else if (paramsType == 5) {
|
||
fieldName = 'salinity';
|
||
seriesName = '盐度';
|
||
}
|
||
|
||
// 提取时间和数据
|
||
data.forEach((item: any, index: number) => {
|
||
// 格式化时间:2026-01-12 09:38:32.0 -> 01-12 09:38
|
||
const timeStr = item.time || item.createTime || '';
|
||
|
||
const timeParts = timeStr.split(' ');
|
||
if (timeParts.length > 1) {
|
||
const datePart = timeParts[0]; // YYYY-MM-DD
|
||
const timePart = timeParts[1]; // HH:mm:ss.0
|
||
|
||
// 提取 MM-DD HH:mm
|
||
const dateMatch = datePart.match(/\d{4}-(\d{2})-(\d{2})/);
|
||
const monthDay = dateMatch ? `${dateMatch[1]}-${dateMatch[2]}` : '';
|
||
const hourMin = timePart.substring(0, 5); // HH:mm
|
||
|
||
const formattedTime = `${monthDay} ${hourMin}`;
|
||
x_time_list.push(formattedTime);
|
||
} else {
|
||
x_time_list.push('--:--'); // 占位符
|
||
}
|
||
|
||
// 提取对应字段的数据
|
||
const value = item[fieldName];
|
||
|
||
if (value !== null && value !== undefined) {
|
||
const numValue = Number(value.toFixed(2));
|
||
seriesData.push(numValue);
|
||
} else {
|
||
seriesData.push(null);
|
||
}
|
||
});
|
||
|
||
// 添加主数据系列
|
||
series.unshift({
|
||
name: seriesName,
|
||
data: seriesData,
|
||
});
|
||
|
||
setTimeout(() => {
|
||
loading.value = false;
|
||
const ids = "GdHQcFQYzLhfeEvZbEJnhDxJzBlbCxhE" + String(doDev.value) + String(prDev.value) + String(dayDev.value);
|
||
|
||
const query = Taro.createSelectorQuery();
|
||
query.select("#" + ids).boundingClientRect();
|
||
query.selectViewport().scrollOffset();
|
||
query.exec((res) => {
|
||
if (res.length > 0) {
|
||
chartConf.top = res[0] ? res[0].top : 0;
|
||
chartConf.left = res[0] ? res[0].left : 0;
|
||
}
|
||
});
|
||
if (type == 1) {
|
||
createCharts(x_time_list, series, ids);
|
||
} else {
|
||
updateCharts(x_time_list, series, ids);
|
||
}
|
||
}, 200);
|
||
});
|
||
}
|
||
// 触屏图表
|
||
function touchEnd(e) {
|
||
const x = e.mpEvent.changedTouches[0].clientX;
|
||
const y = e.mpEvent.changedTouches[0].clientY;
|
||
let tmpe = {};
|
||
tmpe = { x: x - chartConf.left, y: y - chartConf.top };
|
||
e.changedTouches = [];
|
||
e.changedTouches.unshift(tmpe);
|
||
if (e.touches.length == 0) {
|
||
uChartsOp.value[e.target.id].touchLegend(e);
|
||
// uChartsOp.value[e.target.id].scrollEnd(e);
|
||
uChartsOp.value[e.target.id].showToolTip(e, {
|
||
formatter: (item, category, index, opts) => {
|
||
if (dayDev.value == 7) {
|
||
let msg = item.name + ":" + item.data;
|
||
x7.value.forEach((value, i) => {
|
||
if (i == index) {
|
||
msg = value.time + " " + item.name + ":" + item.data;
|
||
}
|
||
});
|
||
return msg;
|
||
} else {
|
||
return item.name + ":" + item.data;
|
||
}
|
||
},
|
||
});
|
||
}
|
||
}
|
||
function touchCandle(e) {
|
||
const x = e.mpEvent.changedTouches[0].clientX;
|
||
const y = e.mpEvent.changedTouches[0].clientY;
|
||
let tmpe = {};
|
||
tmpe = { x: x - chartConf.left, y: y - chartConf.top };
|
||
e.changedTouches = [];
|
||
e.changedTouches.unshift(tmpe);
|
||
uChartsOp.value[e.target.id].scrollStart(e);
|
||
}
|
||
function moveCandle(e) {
|
||
const x = e.mpEvent.changedTouches[0].clientX;
|
||
const y = e.mpEvent.changedTouches[0].clientY;
|
||
let tmpe = {};
|
||
tmpe = { x: x - chartConf.left, y: y - chartConf.top };
|
||
e.changedTouches = [];
|
||
e.changedTouches.unshift(tmpe);
|
||
uChartsOp.value[e.target.id].scroll(e);
|
||
}
|
||
// 创建图表 - 兼容模式(支持微信小程序和H5)
|
||
function createCharts(x, series, ids) {
|
||
Taro.nextTick(() => {
|
||
let cWidth = chartConf.cWidth;
|
||
let cHeight = chartConf.cHeight;
|
||
let pixelRatio = chartConf.pixelRatio;
|
||
|
||
// 判断环境
|
||
const env = Taro.getEnv();
|
||
|
||
if (env === 'WEB' || env === 'h5') {
|
||
// H5 环境需要特殊处理
|
||
setTimeout(() => {
|
||
// H5 下需要找到真实的 canvas 元素,不是 Taro 的封装组件
|
||
const canvasElement = document.querySelector(`#${ids} canvas`);
|
||
|
||
if (!canvasElement) {
|
||
return;
|
||
}
|
||
|
||
const ctx = canvasElement.getContext('2d');
|
||
|
||
if (!ctx) {
|
||
return;
|
||
}
|
||
|
||
uChartsOp.value[ids] = new uCharts({
|
||
type: "line",
|
||
context: ctx,
|
||
width: cWidth,
|
||
height: cHeight,
|
||
categories: x,
|
||
series: series,
|
||
pixelRatio: 1,
|
||
dataPointShape: false,
|
||
animation: true,
|
||
background: "#FFFFFF",
|
||
color: ["#17B4B2", "#5470c6", "#fac858"],
|
||
padding: [15, 30, 35, 45],
|
||
enableScroll: false,
|
||
boundaryGap: "justify",
|
||
update: true,
|
||
legend: {
|
||
fontColor: "#222222",
|
||
itemGap: 10,
|
||
position: "top",
|
||
float: "center",
|
||
},
|
||
dataLabel: false,
|
||
xAxis: {
|
||
disableGrid: true,
|
||
axisLine: true,
|
||
type: "grid",
|
||
gridType: "dash",
|
||
itemCount: 0,
|
||
scrollShow: false,
|
||
scrollAlign: "left",
|
||
labelCount: 7,
|
||
rotateLabel: true,
|
||
rotateAngle: 25,
|
||
},
|
||
yAxis: {
|
||
disableGrid: false,
|
||
gridType: "dash",
|
||
tofix: 2,
|
||
},
|
||
extra: {
|
||
tooltip: {
|
||
showBox: true,
|
||
showArrow: true,
|
||
showCategory: true,
|
||
borderWidth: 0,
|
||
borderRadius: 0,
|
||
borderColor: "#000000",
|
||
borderOpacity: 0.7,
|
||
bgColor: "#000000",
|
||
bgOpacity: 0.7,
|
||
gridType: "solid",
|
||
dashLength: 4,
|
||
gridColor: "#CCCCCC",
|
||
boxPadding: 3,
|
||
fontSize: 13,
|
||
lineHeight: 20,
|
||
fontColor: "#FFFFFF",
|
||
legendShow: true,
|
||
legendShape: "auto",
|
||
splitLine: true,
|
||
labelBgColor: "#FFFFFF",
|
||
labelBgOpacity: 0.7,
|
||
labelFontColor: "#666666",
|
||
},
|
||
line: {
|
||
type: "curve",
|
||
width: 1,
|
||
activeType: "hollow",
|
||
},
|
||
},
|
||
});
|
||
}, 300);
|
||
} else {
|
||
// 小程序环境 - 使用 canvas 2d 模式
|
||
// 通过 createSelectorQuery 获取 canvas 2d 节点
|
||
const query = Taro.createSelectorQuery();
|
||
query.select('#' + ids)
|
||
.fields({ node: true, size: true })
|
||
.exec((res) => {
|
||
if (!res || !res[0] || !res[0].node) {
|
||
return;
|
||
}
|
||
|
||
const canvas = res[0].node;
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
if (!ctx) {
|
||
return;
|
||
}
|
||
|
||
// 设置 canvas 实际尺寸
|
||
canvas.width = cWidth * pixelRatio;
|
||
canvas.height = cHeight * pixelRatio;
|
||
|
||
uChartsOp.value[ids] = new uCharts({
|
||
type: "line",
|
||
context: ctx,
|
||
width: cWidth * pixelRatio,
|
||
height: cHeight * pixelRatio,
|
||
categories: x,
|
||
series: series,
|
||
pixelRatio: pixelRatio,
|
||
dataPointShape: false,
|
||
animation: true,
|
||
background: "#FFFFFF",
|
||
color: ["#17B4B2", "#5470c6", "#fac858"],
|
||
padding: [15, 50, 35, 15],
|
||
enableScroll: false,
|
||
boundaryGap: "justify",
|
||
update: true,
|
||
legend: {
|
||
fontColor: "#222222",
|
||
itemGap: 10,
|
||
position: "top",
|
||
float: "center",
|
||
},
|
||
dataLabel: false,
|
||
xAxis: {
|
||
disableGrid: true,
|
||
axisLine: true,
|
||
type: "grid",
|
||
gridType: "dash",
|
||
itemCount: 0,
|
||
scrollShow: false,
|
||
scrollAlign: "left",
|
||
labelCount: 6,
|
||
rotateLabel: true,
|
||
rotateAngle: 45,
|
||
marginTop: 10,
|
||
},
|
||
yAxis: {
|
||
disableGrid: false,
|
||
gridType: "dash",
|
||
tofix: 2,
|
||
},
|
||
extra: {
|
||
tooltip: {
|
||
showBox: true,
|
||
showArrow: true,
|
||
showCategory: true,
|
||
borderWidth: 0,
|
||
borderRadius: 0,
|
||
borderColor: "#000000",
|
||
borderOpacity: 0.7,
|
||
bgColor: "#000000",
|
||
bgOpacity: 0.7,
|
||
gridType: "solid",
|
||
dashLength: 4,
|
||
gridColor: "#CCCCCC",
|
||
boxPadding: 3,
|
||
fontSize: 13,
|
||
lineHeight: 20,
|
||
fontColor: "#FFFFFF",
|
||
legendShow: true,
|
||
legendShape: "auto",
|
||
splitLine: true,
|
||
labelBgColor: "#FFFFFF",
|
||
labelBgOpacity: 0.7,
|
||
labelFontColor: "#666666",
|
||
},
|
||
line: {
|
||
type: "curve",
|
||
width: 1,
|
||
activeType: "hollow",
|
||
},
|
||
},
|
||
});
|
||
});
|
||
}
|
||
});
|
||
}
|
||
// 更新图表数据
|
||
function updateCharts(x, series, ids) {
|
||
Taro.nextTick(() => {
|
||
if (uChartsOp.value[ids]) {
|
||
uChartsOp.value[ids].updateData({
|
||
series: series,
|
||
categories: x,
|
||
legend: {
|
||
fontColor: "#222222",
|
||
itemGap: 10,
|
||
float: "center",
|
||
},
|
||
xAxis: {
|
||
disableGrid: true,
|
||
axisLine: true,
|
||
type: "grid",
|
||
gridType: "dash",
|
||
itemCount: 0,
|
||
scrollShow: false,
|
||
scrollAlign: "left",
|
||
labelCount: 7,
|
||
},
|
||
});
|
||
} else {
|
||
createCharts(x, series, ids);
|
||
}
|
||
});
|
||
}
|
||
// 切换日期
|
||
function getDaysAgo(days) {
|
||
const now = new Date();
|
||
// days = 1 表示 1 天,从昨天到今天,所以往前推 1 天
|
||
// days = 3 表示 3 天,从 3 天前到今天,所以往前推 3 天
|
||
const start = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
|
||
|
||
// 格式化为 YYYY-MM-DD HH:mm:ss
|
||
paramsHistory.value.startTime = formatDateTime(start);
|
||
paramsHistory.value.endTime = formatDateTime(now);
|
||
|
||
|
||
|
||
// 设置 intervalType:1天->1, 3天->2, 7天->3
|
||
if (days === 1) {
|
||
paramsHistory.value.intervalType = 1;
|
||
} else if (days === 3) {
|
||
paramsHistory.value.intervalType = 2;
|
||
} else if (days === 7) {
|
||
paramsHistory.value.intervalType = 3;
|
||
}
|
||
|
||
getDeviceHistory();
|
||
}
|
||
|
||
// 格式化时间为 YYYY-MM-DD HH:mm:ss
|
||
function formatDateTime(date: Date): string {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||
}
|
||
function getStartDate(days) {
|
||
// 获取当前日期时间
|
||
const currentDate = new Date();
|
||
|
||
// 将天数转换为毫秒数,因为`Date`对象的减法运算会按毫秒计算
|
||
const msInOneDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
|
||
const daysAgoInMs = days * msInOneDay;
|
||
|
||
// 从当前日期减去指定的天数
|
||
const dateBefore = new Date(currentDate.getTime() - daysAgoInMs);
|
||
let year = dateBefore.getFullYear(); //得到年份
|
||
let month = dateBefore.getMonth(); //得到月份
|
||
let date = dateBefore.getDate(); //得到日期
|
||
month = month + 1;
|
||
month = month.toString().padStart(2, "0");
|
||
date = date.toString().padStart(2, "0");
|
||
let day = `${year}-${month}-${date}`;
|
||
return day;
|
||
}
|
||
// 加载时间参数
|
||
function getNowDate() {
|
||
const now = new Date();
|
||
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000); // 昨天
|
||
|
||
paramsHistory.value.startTime = formatDateTime(yesterday);
|
||
paramsHistory.value.endTime = formatDateTime(now);
|
||
paramsHistory.value.intervalType = 1; // 默认 1 天
|
||
}
|
||
function groupByDate(data) {
|
||
const result = {};
|
||
|
||
for (const item of data) {
|
||
// const date = formatDate(new Date(item.createdTime));
|
||
const date = item.day;
|
||
if (!result[date]) {
|
||
result[date] = []; // Initialize an empty array for this date if it doesn't exist yet
|
||
}
|
||
result[date].push(item); // Add the current item to the array for the corresponding date
|
||
}
|
||
return result;
|
||
}
|
||
function getRelativeDay(date) {
|
||
const dateStr = date.replace(/-/g, "/");
|
||
// 将输入转换为 Date 对象
|
||
const inputDate = typeof dateStr === "string" ? new Date(dateStr) : dateStr;
|
||
|
||
// 获取当前日期和时间
|
||
const now = new Date();
|
||
|
||
// 重置时间为零点,以便只比较日期部分
|
||
inputDate.setHours(0, 0, 0, 0);
|
||
now.setHours(0, 0, 0, 0);
|
||
|
||
// 计算日期差值(以天为单位)
|
||
const diffDays = Math.abs(
|
||
(now.getTime() - inputDate.getTime()) / (1000 * 60 * 60 * 24)
|
||
);
|
||
if (diffDays === 0) {
|
||
return "今日";
|
||
} else if (diffDays === 1) {
|
||
return "昨日";
|
||
} else if (diffDays === 2) {
|
||
return "前日";
|
||
} else {
|
||
return undefined; // 不属于昨天、今天或前天
|
||
}
|
||
}
|
||
function processData(dataArray) {
|
||
const resultArray = dataArray.reduce((acc, curr) => {
|
||
const createdDate = new Date(curr.createdTime);
|
||
const halfHour = Math.floor(createdDate.getMinutes() / 30);
|
||
const hour = createdDate.getHours();
|
||
const minute = halfHour * 30;
|
||
const halfHourKey = `${createdDate.getFullYear()}-${(createdDate.getMonth() + 1)
|
||
.toString()
|
||
.padStart(2, "0")}-${createdDate.getDate()} ${hour
|
||
.toString()
|
||
.padStart(2, "0")}:${minute.toString().padStart(2, "0")}:00`;
|
||
|
||
if (!acc[halfHourKey]) {
|
||
acc[halfHourKey] = {
|
||
count: 0,
|
||
value: 0,
|
||
};
|
||
}
|
||
|
||
acc[halfHourKey].count++;
|
||
acc[halfHourKey].value += curr.value;
|
||
return acc;
|
||
}, {});
|
||
|
||
for (const key in resultArray) {
|
||
resultArray[key].value /= resultArray[key].count;
|
||
}
|
||
|
||
const finalResult = Object.entries(resultArray).map(([key, value]) => {
|
||
const dateStr = String(key).split(" ")[0].replace(/-/g, "/");
|
||
const day = formatDateString(dateStr, "yyyy-mm-dd");
|
||
return {
|
||
createdTime: String(key).split(" ")[1].slice(0, -3),
|
||
day: day,
|
||
value: value.value,
|
||
};
|
||
});
|
||
|
||
return finalResult;
|
||
}
|
||
// 编辑塘口
|
||
function updatePond() {
|
||
if (props.modelValue) {
|
||
Taro.navigateTo({
|
||
url: `/home/updatePond?id=${props.modelValue}`,
|
||
});
|
||
}
|
||
}
|
||
// 添加设备
|
||
function addDeviceChild() {
|
||
emit("addDevice");
|
||
}
|
||
// 隐藏引导框
|
||
function hideTour(a) {
|
||
showTour.value = false;
|
||
hideState.value = !a;
|
||
}
|
||
// 刷新
|
||
function refresh() {
|
||
// 确保重新设置图表尺寸
|
||
const sysInfo = Taro.getSystemInfoSync();
|
||
// 修改宽度计算:从 700/750 改为 680/750,为左右边距预留空间
|
||
let cWidth = (680 / 750) * sysInfo.screenWidth;
|
||
let cHeight = (220 / 750) * sysInfo.windowHeight;
|
||
let pixelRatio = sysInfo.pixelRatio;
|
||
chartConf.cWidth = cWidth;
|
||
chartConf.cHeight = cHeight;
|
||
chartConf.pixelRatio = pixelRatio;
|
||
|
||
// 重新加载设备列表和图表数据
|
||
getDeviceList();
|
||
}
|
||
// 启停开关
|
||
function setSwitchOpen({ value, event }, item: any) {
|
||
if (event) {
|
||
const data = {
|
||
id: item.id,
|
||
isOpen: value ? 1 : 0,
|
||
};
|
||
turnSwitch(data)
|
||
.then((res: any) => {
|
||
if (res.code == 200) {
|
||
state.show = true;
|
||
state.msg = "操作成功";
|
||
state.type = "success";
|
||
// 立即更新本地状态
|
||
item.isOpen = value ? 1 : 0;
|
||
} else {
|
||
state.show = true;
|
||
state.msg = res.msg || "设置失败,请重试";
|
||
state.type = "fail";
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
state.show = true;
|
||
state.msg = "网络错误,请重试";
|
||
state.type = "fail";
|
||
});
|
||
}
|
||
}
|
||
// ---------------------控制器相关----------------------- //
|
||
// 全开全关
|
||
function changeAllSwitch(e) {
|
||
const msg = e ? "全开" : "全关";
|
||
Taro.showModal({
|
||
title: "提示",
|
||
content: `确认设置控制器 ${msg}?`,
|
||
success: function (res) {
|
||
if (res.confirm) {
|
||
const data = {
|
||
id: props.modelValue,
|
||
isOpen: e ? 1 : 0,
|
||
};
|
||
|
||
turnPondSwitch(data)
|
||
.then((res: any) => {
|
||
if (res.code == 200) {
|
||
state.show = true;
|
||
state.msg = "操作成功";
|
||
state.type = "success";
|
||
|
||
// 接口成功后,立即更新本地状态
|
||
openStatus.value = e == true ? 1 : 2;
|
||
|
||
// 立即更新所有控制器下的开关状态
|
||
controlList.value.forEach((controller) => {
|
||
if (controller.listSwitch && controller.listSwitch.length > 0) {
|
||
controller.listSwitch.forEach((switchItem) => {
|
||
switchItem.isOpen = e ? 1 : 0;
|
||
});
|
||
}
|
||
});
|
||
} else {
|
||
// 接口返回失败
|
||
state.show = true;
|
||
state.msg = res.msg || "设置失败,请重试";
|
||
state.type = "fail";
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
// 网络错误
|
||
state.show = true;
|
||
state.msg = "网络错误,请重试";
|
||
state.type = "fail";
|
||
});
|
||
} else if (res.cancel) {
|
||
}
|
||
},
|
||
});
|
||
}
|
||
// 设置控制器
|
||
function setParamsCtr(id) {
|
||
Taro.navigateTo({
|
||
url: `/home/ctr?id=${id}&page=home`,
|
||
});
|
||
}
|
||
function switchRes(list, index, type) {
|
||
if (list.length > 0) {
|
||
const info = list.filter((res) => res.index == index);
|
||
if (type == "img") {
|
||
const isLinkedCtrl = info.length > 0 ? info[0]["isLinkedCtrl"] : 0;
|
||
const isTimingCtrl = info.length > 0 ? info[0]["isTimingCtrl"] : 0;
|
||
const isOpen = info.length > 0 ? info[0]["isOpen"] : 0;
|
||
const hasErrorCode = info.length > 0 ? info[0]["hasErrorCode"] : 0;
|
||
if (isTimingCtrl && isLinkedCtrl) {
|
||
if (!isOpen) {
|
||
return c4_n;
|
||
} else {
|
||
if (hasErrorCode) {
|
||
return c4_d;
|
||
} else {
|
||
return c4;
|
||
}
|
||
}
|
||
}
|
||
if (isLinkedCtrl) {
|
||
if (!isOpen) {
|
||
return c2_n;
|
||
} else {
|
||
if (hasErrorCode) {
|
||
return c2_d;
|
||
} else {
|
||
return c2;
|
||
}
|
||
}
|
||
}
|
||
if (isTimingCtrl) {
|
||
if (!isOpen) {
|
||
return c1_n;
|
||
} else {
|
||
if (hasErrorCode) {
|
||
return c1_d;
|
||
} else {
|
||
return c1;
|
||
}
|
||
}
|
||
}
|
||
if (info.length > 0) {
|
||
if (!isOpen) {
|
||
return c3_n;
|
||
} else {
|
||
if (hasErrorCode) {
|
||
return c3_d;
|
||
} else {
|
||
return c3;
|
||
}
|
||
}
|
||
}
|
||
return c_def2;
|
||
} else {
|
||
const r = info.length > 0 ? info[0][type] : "";
|
||
return r;
|
||
}
|
||
} else {
|
||
return "开关" + index;
|
||
}
|
||
}
|
||
// 控制器弹出层
|
||
function setControl(item) {
|
||
visControl.value = true;
|
||
controlInfo.value.deviceName = item.deviceName;
|
||
controlInfo.value.id = item.id;
|
||
controlInfo.value.listSwitch = item.listSwitch;
|
||
}
|
||
// 跳转到开关设置页面
|
||
function setSwitch(id, name) {
|
||
Taro.navigateTo({
|
||
url: `/home/switch?id=${id}&name=${name}`,
|
||
});
|
||
}
|
||
// 夜间防误触开关
|
||
function changeControl({ value, event }) {
|
||
if (event) {
|
||
alertShow.value = value ? true : false;
|
||
if (!value) {
|
||
submit();
|
||
}
|
||
}
|
||
}
|
||
function cancel() {
|
||
alertShow.value = false;
|
||
keepNightOpen.value = false;
|
||
}
|
||
function submit() {
|
||
alertShow.value = false;
|
||
const data = {
|
||
id: props.modelValue,
|
||
isOpen: keepNightOpen.value ? 1 : 0,
|
||
};
|
||
setNightProtect(data).then((res) => {
|
||
if (res.code == 200) {
|
||
state.type = "success";
|
||
state.msg = "操作成功";
|
||
state.show = true;
|
||
// getDeviceList();
|
||
} else {
|
||
state.type = "fail";
|
||
state.msg = res.msg || "设置失败,请重试";
|
||
state.show = true;
|
||
}
|
||
}).catch((error) => {
|
||
state.type = "fail";
|
||
state.msg = "网络错误,请重试";
|
||
state.show = true;
|
||
});
|
||
}
|
||
// 跳转到设备信息页面(设备属性)
|
||
function goDeviceInfo() {
|
||
const currentDevice = doList.value.find(
|
||
(item: any) => item.id === doDev.value
|
||
);
|
||
|
||
if (!currentDevice) {
|
||
state.show = true;
|
||
state.msg = '设备信息不存在';
|
||
state.type = 'fail';
|
||
return;
|
||
}
|
||
|
||
// 跳转到 wqm 页面,带上 deviceInfo 参数表示跳到设备信息区域
|
||
Taro.navigateTo({
|
||
url: `/home/wqm?id=${currentDevice.id}&name=${currentDevice.deviceName}&page=home§ion=deviceInfo`,
|
||
});
|
||
}
|
||
|
||
// 跳转到报警设置页面(溶解氧配置)
|
||
function goAlarmSettings() {
|
||
const currentDevice = doList.value.find(
|
||
(item: any) => item.id === doDev.value
|
||
);
|
||
|
||
if (!currentDevice) {
|
||
state.show = true;
|
||
state.msg = '设备信息不存在';
|
||
state.type = 'fail';
|
||
return;
|
||
}
|
||
|
||
// 跳转到 wqm 页面,带上 alarmSettings 参数表示跳到溶解氧配置区域
|
||
Taro.navigateTo({
|
||
url: `/home/wqm?id=${currentDevice.id}&name=${currentDevice.deviceName}&page=home§ion=alarmSettings`,
|
||
});
|
||
}
|
||
|
||
// 跳转到数据查询页面
|
||
function goDataQuery() {
|
||
const currentDevice = doList.value.find(
|
||
(item: any) => item.id === doDev.value
|
||
);
|
||
|
||
if (!currentDevice) {
|
||
state.show = true;
|
||
state.msg = '设备信息不存在';
|
||
state.type = 'fail';
|
||
return;
|
||
}
|
||
|
||
// 获取设备序列号
|
||
const serialNum = currentDevice.serialNum || currentDevice.serialNumber || currentDevice.deviceSerialNum || currentDevice.sn;
|
||
|
||
if (!serialNum) {
|
||
state.show = true;
|
||
state.msg = '设备序列号不存在';
|
||
state.type = 'fail';
|
||
return;
|
||
}
|
||
|
||
// 跳转到数据查询页面(横屏显示)
|
||
Taro.navigateTo({
|
||
url: `/components/other/dataQuery?serialNum=${serialNum}&dataType=${prDev.value}&deviceName=${currentDevice.deviceName}&oxyWarnLower=${oxyWarnLower.value || ''}&tempWarnUpper=${tempWarnUpper.value || ''}&tempWarnLower=${tempWarnLower.value || ''}`,
|
||
});
|
||
}
|
||
/** -----------------method end------------------- */
|
||
defineExpose({
|
||
getDeviceList,
|
||
hideTour,
|
||
refresh,
|
||
});
|
||
/** -----------------method end------------------- */
|
||
</script>
|
||
<style lang="scss">
|
||
.pondInfo_home {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
height: 100%;
|
||
.tag {
|
||
background: RGB(14, 191, 117, 0.1);
|
||
border-radius: 4px;
|
||
color: #0ebf75;
|
||
}
|
||
|
||
.tagErr {
|
||
background: RGB(191, 41, 14, 0.1);
|
||
border-radius: 4px;
|
||
color: #bf290e;
|
||
}
|
||
.device-tabs-wrapper {
|
||
width: 100%;
|
||
padding: 0;
|
||
margin: 2rpx 0rpx 15rpx 0rpx;
|
||
}
|
||
|
||
.device-tabs {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
padding: 0;
|
||
}
|
||
.custom-tab-item {
|
||
padding: 10rpx 20rpx;
|
||
background-color: #f5f6f7;
|
||
border-radius: 8rpx;
|
||
transition: all 0.3s ease;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
min-width: 120rpx;
|
||
text-align: center;
|
||
|
||
&.tab-active {
|
||
background-color: #ffffff;
|
||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.custom-title {
|
||
color: #333;
|
||
display: inline-block;
|
||
font-size: 28rpx;
|
||
|
||
&.active {
|
||
color: #09b8c2;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
.nut-tab-pane {
|
||
padding: 0px !important;
|
||
}
|
||
|
||
.nut-grid-item__content {
|
||
height: 140rpx;
|
||
}
|
||
|
||
.nut-grid-item {
|
||
height: 140rpx;
|
||
padding-top: 0% !important;
|
||
}
|
||
|
||
.nut-grid {
|
||
height: 140rpx;
|
||
}
|
||
|
||
.grid {
|
||
background: #fff;
|
||
border: 1px solid #f5f6f7;
|
||
.nut-grid-item__text {
|
||
font-weight: 500;
|
||
font-size: 28px;
|
||
color: #222;
|
||
}
|
||
font-weight: 500;
|
||
font-size: 28px;
|
||
color: #222;
|
||
}
|
||
|
||
.grid.active {
|
||
color: #fff;
|
||
background: #09b8c2;
|
||
.nut-grid-item__text {
|
||
color: #fff;
|
||
}
|
||
|
||
.nut-grid-item__content--center {
|
||
background: #09b8c2;
|
||
}
|
||
}
|
||
|
||
.gridSmall {
|
||
width: 160px;
|
||
background: #fff;
|
||
border: 1px solid #eee;
|
||
text-align: center;
|
||
height: 48px;
|
||
}
|
||
|
||
.gridSmall.active {
|
||
color: #fff;
|
||
background: #09b8c2;
|
||
}
|
||
|
||
// 全新设计的图表标题行布局
|
||
.chart-header-row {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
height: 40px;
|
||
padding: 0 12px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
// 左侧区域:标题
|
||
.left-section {
|
||
flex-shrink: 0;
|
||
width: 70px;
|
||
|
||
.chart-title-text {
|
||
font-size: 30px;
|
||
font-weight: 600;
|
||
color: #222;
|
||
white-space: nowrap;
|
||
display: inline-block;
|
||
}
|
||
}
|
||
|
||
// 中间区域:按钮组
|
||
.center-section {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 0 10px;
|
||
|
||
.day-selector {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
border: 1px solid #e5e5e5;
|
||
|
||
.day-btn {
|
||
padding: 0 16px;
|
||
height: 34px;
|
||
line-height: 34px;
|
||
font-size: 24px;
|
||
color: #666;
|
||
background: #fff;
|
||
border-right: 1px solid #e5e5e5;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
|
||
&:last-child {
|
||
border-right: none;
|
||
}
|
||
|
||
&.active {
|
||
color: #fff;
|
||
background: linear-gradient(135deg, #09b8c2 0%, #07a5ae 100%);
|
||
font-weight: 500;
|
||
}
|
||
|
||
&:not(.active):active {
|
||
background: #f8f8f8;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 右侧区域:放大隐藏
|
||
.right-section {
|
||
flex-shrink: 0;
|
||
width: 105px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
gap: 6px;
|
||
|
||
.action-link {
|
||
font-size: 26px;
|
||
color: #1589E9;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
transition: opacity 0.2s ease;
|
||
|
||
&:active {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.divider-dot {
|
||
font-size: 18px;
|
||
color: #ccc;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.zoom-btn {
|
||
font-size: 28px;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
color: #1589E9;
|
||
}
|
||
|
||
.hide-btn {
|
||
font-size: 28px;
|
||
white-space: nowrap;
|
||
color: #1589E9;
|
||
}
|
||
|
||
.gridRight {
|
||
text-align: right;
|
||
color: #1589e9;
|
||
}
|
||
|
||
.nut-popup--bottom {
|
||
box-shadow: 0px 1px 7px 0px rgb(20, 20, 20);
|
||
border-radius: 0px;
|
||
}
|
||
|
||
.nut-popup--center.round {
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.nut-dialog {
|
||
padding: 0;
|
||
}
|
||
|
||
.nut-dialog__content {
|
||
margin: 0;
|
||
max-height: 100%;
|
||
}
|
||
}
|
||
.btn {
|
||
border-radius: 10px;
|
||
}
|
||
.charts {
|
||
height: 330px;
|
||
width: 100%;
|
||
max-width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
.loading-container {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 10;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/** 底部弹出层 */
|
||
.rowPd {
|
||
padding: 40px;
|
||
height: 100%;
|
||
}
|
||
.gridCon {
|
||
background: #fff;
|
||
border: 1px solid #eee;
|
||
font-size: 26rpx;
|
||
color: #222;
|
||
text-align: center;
|
||
height: 48px;
|
||
line-height: 48px;
|
||
width: 90px;
|
||
}
|
||
|
||
.gridCon.active {
|
||
color: #fff;
|
||
background: #09b8c2;
|
||
}
|
||
|
||
.nut-grid-item__content {
|
||
background: #fff;
|
||
}
|
||
|
||
// 功能按钮区域样式
|
||
.action-buttons {
|
||
margin-top: 10rpx;
|
||
padding: 0;
|
||
width: 100%;
|
||
|
||
.nut-col {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.button-wrapper {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
padding: 0 20rpx;
|
||
|
||
.action-btn {
|
||
flex: 0 0 auto;
|
||
width: 180rpx;
|
||
height: 70rpx;
|
||
font-size: 28rpx;
|
||
border-radius: 8rpx;
|
||
background: linear-gradient(135deg, #09b8c2 0%, #06a0a9 100%);
|
||
border: none;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
box-shadow: 0 4rpx 12rpx rgba(9, 184, 194, 0.3);
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
transform: scale(0.95);
|
||
box-shadow: 0 2rpx 8rpx rgba(9, 184, 194, 0.4);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|