Files
fishery-app/src/components/other/index.vue
2026-01-19 22:34:37 +08:00

2198 lines
63 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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="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);
// 设置 intervalType1天->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&section=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&section=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>