1430 lines
54 KiB
Vue
1430 lines
54 KiB
Vue
<template>
|
||
<div class="p-2">
|
||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||
<div v-show="showSearch" class="mb-[10px]">
|
||
<el-card shadow="hover">
|
||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||
<el-form-item label="用户名" prop="userName">
|
||
<el-input v-model="queryParams.userName" placeholder="请输入用户名" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="手机号" prop="mobilePhone">
|
||
<el-input v-model="queryParams.mobilePhone" placeholder="请输入手机号" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<!-- <el-form-item label="省份" prop="province">
|
||
<el-input v-model="queryParams.province" placeholder="请输入省份" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="城市" prop="city">
|
||
<el-input v-model="queryParams.city" placeholder="请输入城市" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="区县" prop="district">
|
||
<el-input v-model="queryParams.district" placeholder="请输入区县" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item> -->
|
||
<el-form-item label="报警电话" prop="warnPhoneJson">
|
||
<el-input v-model="queryParams.warnPhoneJson" placeholder="请输入报警电话" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
</transition>
|
||
|
||
<el-card shadow="never">
|
||
<template #header>
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:aquUser:add']">新增</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:aquUser:edit']"
|
||
>修改</el-button
|
||
>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:aquUser:remove']"
|
||
>删除</el-button
|
||
>
|
||
</el-col>
|
||
<!-- <el-col :span="1.5">
|
||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['fishery:aquUser:export']">导出</el-button>
|
||
</el-col> -->
|
||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||
</el-row>
|
||
</template>
|
||
|
||
<el-table v-loading="loading" border :data="aquUserList" @selection-change="handleSelectionChange">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="主键id" align="center" prop="id" v-if="false" />
|
||
<el-table-column label="用户名" align="center" prop="userName" />
|
||
<el-table-column label="手机号" align="center" prop="mobilePhone" />
|
||
<!-- <el-table-column label="省份" align="center" prop="province" />
|
||
<el-table-column label="城市" align="center" prop="city" />
|
||
<el-table-column label="区县" align="center" prop="district" /> -->
|
||
<!-- <el-table-column label="展示标题" align="center" prop="title" /> -->
|
||
<el-table-column label="创建时间" align="center" prop="createTime" />
|
||
<el-table-column label="更新时间" align="center" prop="updateTime" />
|
||
<el-table-column label="操作" align="center" fixed="right" width="240" class-name="small-padding fixed-width">
|
||
<template #default="scope">
|
||
<el-tooltip content="子账号" placement="top">
|
||
<el-button link type="primary" icon="User" @click="handleViewSubAccounts(scope.row)"></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="报警电话" placement="top">
|
||
<el-button link type="primary" icon="Phone" @click="handleViewWarnPhones(scope.row)"></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="查看塘口" placement="top">
|
||
<el-button link type="primary" icon="View" @click="handleViewPonds(scope.row)"></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="修改" placement="top">
|
||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['fishery:aquUser:edit']"></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="删除" placement="top">
|
||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['fishery:aquUser:remove']"></el-button>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||
</el-card>
|
||
<!-- 添加或修改养殖账号对话框 -->
|
||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="850px" append-to-body>
|
||
<el-form ref="aquUserFormRef" :model="form" :rules="rules" :inline="true" label-width="120px">
|
||
<el-form-item label="用户名" prop="userName">
|
||
<el-input v-model="form.userName" placeholder="请输入用户名" />
|
||
</el-form-item>
|
||
<el-form-item label="手机号" prop="mobilePhone">
|
||
<el-input v-model="form.mobilePhone" placeholder="请输入手机号" />
|
||
</el-form-item>
|
||
<el-form-item label="报警电话" prop="warnPhoneJson">
|
||
<el-input v-model="form.warnPhoneJson" placeholder="请输入报警电话" />
|
||
</el-form-item>
|
||
<el-form-item label="省份" prop="province">
|
||
<el-input v-model="form.province" placeholder="请输入省份" />
|
||
</el-form-item>
|
||
<el-form-item label="城市" prop="city">
|
||
<el-input v-model="form.city" placeholder="请输入城市" />
|
||
</el-form-item>
|
||
<el-form-item label="区县" prop="district">
|
||
<el-input v-model="form.district" placeholder="请输入区县" />
|
||
</el-form-item>
|
||
<el-form-item label="访问Token" prop="accessToken">
|
||
<el-input v-model="form.accessToken" placeholder="请输入访问Token" />
|
||
</el-form-item>
|
||
<el-form-item label="刷新Token" prop="refreshToken">
|
||
<el-input v-model="form.refreshToken" placeholder="请输入刷新Token" />
|
||
</el-form-item>
|
||
<el-form-item label="小程序的openId" prop="wxOpenId">
|
||
<el-input v-model="form.wxOpenId" placeholder="请输入小程序的openId" />
|
||
</el-form-item>
|
||
<el-form-item label="微信SessionKey" prop="wxSessionKey">
|
||
<el-input v-model="form.wxSessionKey" placeholder="请输入微信SessionKey" />
|
||
</el-form-item>
|
||
<el-form-item label="是否管理员" prop="isManager">
|
||
<el-input v-model="form.isManager" placeholder="请输入是否管理员" />
|
||
</el-form-item>
|
||
<el-form-item label="微信unionId" prop="wxUnionId">
|
||
<el-input v-model="form.wxUnionId" placeholder="请输入微信unionId" />
|
||
</el-form-item>
|
||
<el-form-item label="公众号的openId" prop="tecentOpenId">
|
||
<el-input v-model="form.tecentOpenId" placeholder="请输入公众号的openId" />
|
||
</el-form-item>
|
||
<el-form-item label="展示标题" prop="title">
|
||
<el-input v-model="form.title" placeholder="请输入展示标题" />
|
||
</el-form-item>
|
||
<el-form-item label="是否拥有大屏" prop="hasScreen">
|
||
<el-input v-model="form.hasScreen" placeholder="请输入是否拥有大屏" />
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||
<el-button @click="cancel">取 消</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 子账号对话框 -->
|
||
<el-dialog :title="subAccountDialog.title" v-model="subAccountDialog.visible" width="1000px" append-to-body>
|
||
<!-- 加载状态 -->
|
||
<div v-if="subAccountLoading" class="text-center py-8">
|
||
<el-icon class="is-loading" :size="40" color="#409eff">
|
||
<Loading />
|
||
</el-icon>
|
||
<p class="mt-4 text-gray-500">正在加载子账号信息...</p>
|
||
</div>
|
||
|
||
<!-- 子账号表格 -->
|
||
<template v-else>
|
||
<el-table
|
||
:data="subAccountList"
|
||
border
|
||
height="400px"
|
||
v-if="subAccountList.length > 0"
|
||
>
|
||
<el-table-column type="index" label="序号" width="60" align="center"/>
|
||
<el-table-column label="用户名" align="center" prop="userName" width="180"/>
|
||
<el-table-column label="手机号" align="center" prop="mobilePhone" width="150"/>
|
||
<el-table-column label="报警电话" align="center" prop="warnPhoneJson" show-overflow-tooltip>
|
||
<template #default="scope">
|
||
<span v-if="scope.row.warnPhoneJson">
|
||
<el-icon class="mr-1"><Phone /></el-icon>
|
||
{{ scope.row.warnPhoneJson }}
|
||
</span>
|
||
<span v-else class="text-gray-400">未设置</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
|
||
</el-table>
|
||
|
||
<div v-if="subAccountList.length === 0" class="text-center py-8 text-gray-500">
|
||
该用户暂无子账号
|
||
</div>
|
||
</template>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="subAccountDialog.visible = false">关 闭</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 报警电话对话框 -->
|
||
<el-dialog :title="warnPhoneDialog.title" v-model="warnPhoneDialog.visible" width="500px" append-to-body>
|
||
<el-descriptions :column="1" border>
|
||
<el-descriptions-item label="第一报警电话">
|
||
<span v-if="warnPhones[0]" class="text-base">
|
||
<el-icon class="mr-1"><Phone /></el-icon>
|
||
{{ warnPhones[0] }}
|
||
</span>
|
||
<span v-else class="text-gray-400">未设置</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="第二报警电话">
|
||
<span v-if="warnPhones[1]" class="text-base">
|
||
<el-icon class="mr-1"><Phone /></el-icon>
|
||
{{ warnPhones[1] }}
|
||
</span>
|
||
<span v-else class="text-gray-400">未设置</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="第三报警电话">
|
||
<span v-if="warnPhones[2]" class="text-base">
|
||
<el-icon class="mr-1"><Phone /></el-icon>
|
||
{{ warnPhones[2] }}
|
||
</span>
|
||
<span v-else class="text-gray-400">未设置</span>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<div v-if="warnPhones.length === 0" class="text-center py-8 text-gray-500">
|
||
该用户暂未设置报警电话
|
||
</div>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="warnPhoneDialog.visible = false">关 闭</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 用户塘口信息对话框 -->
|
||
<el-dialog
|
||
:title="pondDialog.title"
|
||
v-model="pondDialog.visible"
|
||
width="1400px"
|
||
top="7.5vh"
|
||
class="pond-dialog"
|
||
append-to-body
|
||
destroy-on-close
|
||
>
|
||
<!-- 加载状态 -->
|
||
<div v-if="pondLoading" class="loading-container">
|
||
<div class="text-center py-20">
|
||
<el-icon class="is-loading" :size="40" color="#409eff">
|
||
<Loading />
|
||
</el-icon>
|
||
<p class="mt-4 text-gray-500">正在加载塘口信息...</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内容区 -->
|
||
<template v-else>
|
||
<el-tabs v-model="activePondTab" type="border-card" v-if="userPondList.length > 0">
|
||
<el-tab-pane
|
||
v-for="pond in userPondList"
|
||
:key="pond.id"
|
||
:label="pond.pondName"
|
||
:name="String(pond.id)"
|
||
>
|
||
<!-- 塘口基本信息 -->
|
||
<div class="pond-info mb-2">
|
||
<el-descriptions :column="4" border size="small">
|
||
<el-descriptions-item label="设备数量">
|
||
<span class="text-lg font-bold text-primary">{{ getPondDeviceCount(String(pond.id)) }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="开关数量">
|
||
<span class="text-lg font-bold">{{ getPondSwitchCount(String(pond.id)) }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="夜间防扰开关">
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="'0'"/>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="创建时间">
|
||
{{ (pond as any).createTime ? parseTime((pond as any).createTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
|
||
<!-- 设备列表 -->
|
||
<div class="devices-section">
|
||
<div class="device-list">
|
||
<el-card
|
||
v-for="device in getPondDevicesByType(String(pond.id), [1,3])"
|
||
:key="device.id"
|
||
class="device-card mb-2"
|
||
shadow="hover"
|
||
>
|
||
<template #header>
|
||
<div class="flex justify-between items-center">
|
||
<span class="font-bold">{{ device.deviceName }}</span>
|
||
<el-tag size="small" type="primary">水质检测仪</el-tag>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 水质检测仪基本信息 -->
|
||
<el-row :gutter="10">
|
||
<el-col :span="8">
|
||
<div class="param-item">
|
||
<div class="param-label">设备编号</div>
|
||
<div class="param-value-small">{{ device.serialNum || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<div class="param-item">
|
||
<div class="param-label">物联网卡号</div>
|
||
<div class="param-value-small">{{ device.iccId || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<div class="param-item">
|
||
<div class="param-label">设备分类</div>
|
||
<div class="param-value-small">{{ device.category || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 实时监测数据 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">溶解氧</div>
|
||
<div class="param-value text-green-600">{{ device.valueDissolvedOxygen || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">水温</div>
|
||
<div class="param-value text-blue-600">{{ device.valueTemperature || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">饱和度</div>
|
||
<div class="param-value">{{ device.valueSaturability || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">PH值</div>
|
||
<div class="param-value">{{ device.valuePh || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">盐度</div>
|
||
<div class="param-value">{{ device.valueSalinity || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">参比值</div>
|
||
<div class="param-value">{{ device.treference || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">荧光值</div>
|
||
<div class="param-value">{{ device.tfluorescence || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">相位差</div>
|
||
<div class="param-value">{{ device.phaseDifference || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 补偿参数 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">盐度补偿</div>
|
||
<div class="param-value-small">{{ device.salinityCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度补偿</div>
|
||
<div class="param-value-small">{{ device.temperatureCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">线性系数补偿</div>
|
||
<div class="param-value-small">{{ device.phaseCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">相位差补偿</div>
|
||
<div class="param-value-small">{{ device.phasedifCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 时间信息 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">绑定时间</div>
|
||
<div class="param-value-small">{{ parseTime(device.bindTime, '{y}-{m}-{d}') || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">激活时间</div>
|
||
<div class="param-value-small">{{ parseTime((device as any).createTime, '{y}-{m}-{d}') || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">服务到期</div>
|
||
<div class="param-value-small text-danger">{{ parseTime(device.deadTime, '{y}-{m}-{d}') || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">校准时间</div>
|
||
<div class="param-value-small">{{ (device as any).correctTime ? parseTime((device as any).correctTime, '{y}-{m}-{d}') : '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 告警配置 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">溶解氧电话告警</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.oxyWarnCallOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">溶解氧告警下限</div>
|
||
<div class="param-value-small">{{ device.oxyWarnLower || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度电话告警</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.tempWarnCallOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">电压告警开关</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.voltageWarnOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度告警上限</div>
|
||
<div class="param-value-small">{{ device.tempWarnUpper || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度告警下限</div>
|
||
<div class="param-value-small">{{ device.tempWarnLower || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">电量电话告警</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.batteryWarnCallOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">电量告警下限</div>
|
||
<div class="param-value-small">{{ device.batteryWarnLower || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</el-card>
|
||
</div>
|
||
|
||
<div v-if="getPondDevicesByType(String(pond.id), [1,3]).length === 0" class="text-center py-4 text-gray-400">
|
||
暂无水质检测仪
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 测控一体机设备 -->
|
||
<div class="devices-section mt-4" v-if="getPondDevicesByType(String(pond.id), [2]).length > 0">
|
||
<div class="device-list">
|
||
<el-card
|
||
v-for="device in getPondDevicesByType(String(pond.id), [2])"
|
||
:key="device.id"
|
||
class="device-card mb-3"
|
||
shadow="hover"
|
||
>
|
||
<template #header>
|
||
<div class="flex justify-between items-center">
|
||
<span class="font-bold">{{ device.deviceName }}</span>
|
||
<el-tag size="small" type="success">测控一体机</el-tag>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 测控一体机基本信息 -->
|
||
<el-row :gutter="10">
|
||
<el-col :span="8">
|
||
<div class="param-item">
|
||
<div class="param-label">设备编号</div>
|
||
<div class="param-value-small">{{ device.serialNum || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<div class="param-item">
|
||
<div class="param-label">物联网卡号</div>
|
||
<div class="param-value-small">{{ device.iccId || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<div class="param-item">
|
||
<div class="param-label">设备分类</div>
|
||
<div class="param-value-small">{{ device.category || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 实时监测数据 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">溶解氧</div>
|
||
<div class="param-value text-green-600">{{ device.valueDissolvedOxygen || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">水温</div>
|
||
<div class="param-value text-blue-600">{{ device.valueTemperature || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">饱和度</div>
|
||
<div class="param-value">{{ device.valueSaturability || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">PH值</div>
|
||
<div class="param-value">{{ device.valuePh || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">盐度</div>
|
||
<div class="param-value">{{ device.valueSalinity || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">参比值</div>
|
||
<div class="param-value">{{ device.treference || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">荧光值</div>
|
||
<div class="param-value">{{ device.tfluorescence || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">相位差</div>
|
||
<div class="param-value">{{ device.phaseDifference || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 补偿参数 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">盐度补偿</div>
|
||
<div class="param-value-small">{{ device.salinityCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度补偿</div>
|
||
<div class="param-value-small">{{ device.temperatureCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">线性系数补偿</div>
|
||
<div class="param-value-small">{{ device.phaseCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">相位差补偿</div>
|
||
<div class="param-value-small">{{ device.phasedifCompensation || '0.00' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 时间信息 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">绑定时间</div>
|
||
<div class="param-value-small">{{ parseTime(device.bindTime, '{y}-{m}-{d}') || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">激活时间</div>
|
||
<div class="param-value-small">{{ parseTime((device as any).createTime, '{y}-{m}-{d}') || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">服务到期</div>
|
||
<div class="param-value-small text-danger">{{ parseTime(device.deadTime, '{y}-{m}-{d}') || '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">校准时间</div>
|
||
<div class="param-value-small">{{ (device as any).correctTime ? parseTime((device as any).correctTime, '{y}-{m}-{d}') : '-' }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 告警配置 -->
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">溶解氧电话告警</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.oxyWarnCallOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">溶解氧告警下限</div>
|
||
<div class="param-value-small">{{ device.oxyWarnLower || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度电话告警</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.tempWarnCallOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">电压告警开关</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.voltageWarnOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="10" class="mt-3">
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度告警上限</div>
|
||
<div class="param-value-small">{{ device.tempWarnUpper || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">温度告警下限</div>
|
||
<div class="param-value-small">{{ device.tempWarnLower || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">电量电话告警</div>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.batteryWarnCallOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<div class="param-item">
|
||
<div class="param-label">电量告警下限</div>
|
||
<div class="param-value-small">{{ device.batteryWarnLower || 0 }}</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 开关列表 -->
|
||
<div class="mt-4" v-if="getPondSwitchesByDevice(String(pond.id), device.id).length > 0">
|
||
<el-divider content-position="left">
|
||
<span class="text-sm font-bold text-gray-600">开关列表 ({{ getPondSwitchesByDevice(String(pond.id), device.id).length }}个)</span>
|
||
</el-divider>
|
||
<el-row :gutter="10">
|
||
<el-col
|
||
:span="12"
|
||
v-for="sw in getPondSwitchesByDevice(String(pond.id), device.id)"
|
||
:key="sw.id"
|
||
>
|
||
<div class="switch-item mb-3">
|
||
<div class="switch-header">
|
||
<div class="flex items-center gap-2">
|
||
<span class="switch-index">#{{ sw.index }}</span>
|
||
<span class="switch-name">{{ sw.switchName }}</span>
|
||
</div>
|
||
<el-tag :type="sw.isOpen === 1 ? 'success' : 'info'" size="small">
|
||
{{ sw.isOpen === 1 ? '开启' : '关闭' }}
|
||
</el-tag>
|
||
</div>
|
||
<div class="switch-details mt-3">
|
||
<el-row :gutter="8">
|
||
<el-col :span="12">
|
||
<div class="detail-item">
|
||
<span class="detail-label">测定电流</span>
|
||
<span class="detail-value text-primary">{{ sw.detectElectricValue || 0 }}A</span>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<div class="detail-item">
|
||
<span class="detail-label">测定电压</span>
|
||
<span class="detail-value text-blue-600">{{ sw.detectVoltageValue || 0 }}V</span>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="8" class="mt-2">
|
||
<el-col :span="12">
|
||
<div class="detail-item">
|
||
<span class="detail-label">额定电流</span>
|
||
<span class="detail-value">{{ sw.rateElectricValue || 0 }}A</span>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<div class="detail-item">
|
||
<span class="detail-label">接线方式</span>
|
||
<dict-tag :options="connect_voltage_type" :value="String(sw.connectVoltageType)" :show-value="true"/>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="8" class="mt-2">
|
||
<el-col :span="12">
|
||
<div class="detail-item">
|
||
<span class="detail-label">电流告警</span>
|
||
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(sw.electricWarnOpen || 0)"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<div class="detail-item">
|
||
<span class="detail-label">操作时间</span>
|
||
<span class="detail-value-time">{{ sw.lastTurnTime ? parseTime(sw.lastTurnTime, '{m}-{d} {h}:{i}') : '-' }}</span>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<div v-if="userPondList.length === 0" class="text-center py-8 text-gray-500">
|
||
该用户暂无塘口信息
|
||
</div>
|
||
</template>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="pondDialog.visible = false">关 闭</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="AquUser" lang="ts">
|
||
import { listAquUser, getAquUser, delAquUser, addAquUser, updateAquUser } from '@/api/fishery/aquUser';
|
||
import { listPond } from '@/api/fishery/pond';
|
||
import { listDevice } from '@/api/fishery/device';
|
||
import { listDeviceSwitch } from '@/api/fishery/deviceSwitch';
|
||
import { listUserRelation } from '@/api/fishery/userRelation';
|
||
import { AquUserVO, AquUserQuery, AquUserForm } from '@/api/fishery/aquUser/types';
|
||
import { PondVO } from '@/api/fishery/pond/types';
|
||
import { DeviceVO } from '@/api/fishery/device/types';
|
||
import { DeviceSwitchVO } from '@/api/fishery/deviceSwitch/types';
|
||
import { UserRelationVO } from '@/api/fishery/userRelation/types';
|
||
|
||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||
const { connect_voltage_type } = toRefs<any>(proxy?.useDict('connect_voltage_type'));
|
||
|
||
const aquUserList = ref<AquUserVO[]>([]);
|
||
const buttonLoading = ref(false);
|
||
const loading = ref(true);
|
||
const showSearch = ref(true);
|
||
const ids = ref<Array<string | number>>([]);
|
||
const single = ref(true);
|
||
const multiple = ref(true);
|
||
const total = ref(0);
|
||
|
||
const queryFormRef = ref<ElFormInstance>();
|
||
const aquUserFormRef = ref<ElFormInstance>();
|
||
|
||
const dialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
// 塘口信息相关
|
||
const pondDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
const userPondList = ref<PondVO[]>([]);
|
||
const currentUserId = ref<string | number>('');
|
||
const activePondTab = ref<string>('');
|
||
const pondDeviceMap = ref<Map<string | number, DeviceVO[]>>(new Map());
|
||
const pondSwitchMap = ref<Map<string | number, DeviceSwitchVO[]>>(new Map());
|
||
const pondLoading = ref(false); // 塘口数据加载状态
|
||
const deviceLoading = ref(false); // 设备数据加载状态
|
||
const loadedPonds = ref<Set<string | number>>(new Set()); // 已加载设备数据的塘口ID集合
|
||
|
||
// 报警电话相关
|
||
const warnPhoneDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
const warnPhones = ref<string[]>([]);
|
||
|
||
// 子账号相关
|
||
const subAccountDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
const subAccountList = ref<AquUserVO[]>([]);
|
||
const subAccountLoading = ref(false);
|
||
const currentParentUser = ref<AquUserVO | null>(null);
|
||
|
||
const initFormData: AquUserForm = {
|
||
id: undefined,
|
||
userName: undefined,
|
||
mobilePhone: undefined,
|
||
province: undefined,
|
||
city: undefined,
|
||
district: undefined,
|
||
warnPhoneJson: undefined,
|
||
accessToken: undefined,
|
||
refreshToken: undefined,
|
||
wxOpenId: undefined,
|
||
wxSessionKey: undefined,
|
||
isManager: undefined,
|
||
wxUnionId: undefined,
|
||
tecentOpenId: undefined,
|
||
title: undefined,
|
||
hasScreen: undefined,
|
||
remark: undefined
|
||
};
|
||
const data = reactive<PageData<AquUserForm, AquUserQuery>>({
|
||
form: { ...initFormData },
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
userName: undefined,
|
||
mobilePhone: undefined,
|
||
province: undefined,
|
||
city: undefined,
|
||
district: undefined,
|
||
warnPhoneJson: undefined,
|
||
params: {}
|
||
},
|
||
rules: {
|
||
userName: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],
|
||
mobilePhone: [{ required: true, message: '手机号不能为空', trigger: 'blur' }],
|
||
warnPhoneJson: [{ required: true, message: '报警电话不能为空', trigger: 'blur' }]
|
||
}
|
||
});
|
||
|
||
const { queryParams, form, rules } = toRefs(data);
|
||
|
||
/** 查询养殖账号列表 */
|
||
const getList = async () => {
|
||
loading.value = true;
|
||
const res = await listAquUser(queryParams.value);
|
||
aquUserList.value = res.rows;
|
||
total.value = res.total;
|
||
loading.value = false;
|
||
};
|
||
|
||
/** 取消按钮 */
|
||
const cancel = () => {
|
||
reset();
|
||
dialog.visible = false;
|
||
};
|
||
|
||
/** 表单重置 */
|
||
const reset = () => {
|
||
form.value = { ...initFormData };
|
||
aquUserFormRef.value?.resetFields();
|
||
};
|
||
|
||
/** 搜索按钮操作 */
|
||
const handleQuery = () => {
|
||
queryParams.value.pageNum = 1;
|
||
getList();
|
||
};
|
||
|
||
/** 重置按钮操作 */
|
||
const resetQuery = () => {
|
||
queryFormRef.value?.resetFields();
|
||
handleQuery();
|
||
};
|
||
|
||
/** 多选框选中数据 */
|
||
const handleSelectionChange = (selection: AquUserVO[]) => {
|
||
ids.value = selection.map((item) => item.id);
|
||
single.value = selection.length != 1;
|
||
multiple.value = !selection.length;
|
||
};
|
||
|
||
/** 新增按钮操作 */
|
||
const handleAdd = () => {
|
||
reset();
|
||
dialog.visible = true;
|
||
dialog.title = '添加养殖账号';
|
||
};
|
||
|
||
/** 修改按钮操作 */
|
||
const handleUpdate = async (row?: AquUserVO) => {
|
||
reset();
|
||
const _id = row?.id || ids.value[0];
|
||
const res = await getAquUser(_id);
|
||
Object.assign(form.value, res.data);
|
||
dialog.visible = true;
|
||
dialog.title = '修改养殖账号';
|
||
};
|
||
|
||
/** 提交按钮 */
|
||
const submitForm = () => {
|
||
aquUserFormRef.value?.validate(async (valid: boolean) => {
|
||
if (valid) {
|
||
buttonLoading.value = true;
|
||
if (form.value.id) {
|
||
await updateAquUser(form.value).finally(() => (buttonLoading.value = false));
|
||
} else {
|
||
await addAquUser(form.value).finally(() => (buttonLoading.value = false));
|
||
}
|
||
proxy?.$modal.msgSuccess('操作成功');
|
||
dialog.visible = false;
|
||
await getList();
|
||
}
|
||
});
|
||
};
|
||
|
||
/** 删除按钮操作 */
|
||
const handleDelete = async (row?: AquUserVO) => {
|
||
const _ids = row?.id || ids.value;
|
||
await proxy?.$modal.confirm('是否确认删除养殖账号编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||
await delAquUser(_ids);
|
||
proxy?.$modal.msgSuccess('删除成功');
|
||
await getList();
|
||
};
|
||
|
||
/** 导出按钮操作 */
|
||
const handleExport = () => {
|
||
proxy?.download(
|
||
'fishery/aquUser/export',
|
||
{
|
||
...queryParams.value
|
||
},
|
||
`aquUser_${new Date().getTime()}.xlsx`
|
||
);
|
||
};
|
||
|
||
/** 查看子账号 */
|
||
const handleViewSubAccounts = async (row: AquUserVO) => {
|
||
currentParentUser.value = row;
|
||
subAccountDialog.title = `${row.userName} 的子账号列表`;
|
||
subAccountDialog.visible = true;
|
||
subAccountLoading.value = true;
|
||
|
||
try {
|
||
// 查询该用户的子账号关系
|
||
const relationRes = await listUserRelation({
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
parentUserId: row.id
|
||
});
|
||
|
||
const relations = relationRes.rows || relationRes.data || [];
|
||
|
||
if (relations.length === 0) {
|
||
subAccountList.value = [];
|
||
return;
|
||
}
|
||
|
||
// 获取所有子账号ID
|
||
const childUserIds = relations.map((r: UserRelationVO) => r.childUserId);
|
||
|
||
// 查询子账号详细信息
|
||
const userRes = await listAquUser({
|
||
pageNum: 1,
|
||
pageSize: 1000
|
||
});
|
||
|
||
const allUsers = userRes.rows || [];
|
||
// 筛选出子账号
|
||
subAccountList.value = allUsers.filter((u: AquUserVO) => childUserIds.includes(u.id));
|
||
|
||
} catch (error) {
|
||
console.error('加载子账号失败:', error);
|
||
proxy?.$modal.msgError('加载子账号失败,请重试');
|
||
subAccountList.value = [];
|
||
} finally {
|
||
subAccountLoading.value = false;
|
||
}
|
||
};
|
||
|
||
/** 查看报警电话 */
|
||
const handleViewWarnPhones = (row: AquUserVO) => {
|
||
warnPhoneDialog.title = `${row.userName} 的报警电话`;
|
||
|
||
// 解析报警电话JSON字符串
|
||
try {
|
||
if (row.warnPhoneJson) {
|
||
const phonesData = JSON.parse(row.warnPhoneJson);
|
||
// 如果是数组,直接使用;如果是对象,提取值
|
||
if (Array.isArray(phonesData)) {
|
||
warnPhones.value = phonesData.filter(phone => phone && phone.trim());
|
||
} else if (typeof phonesData === 'object') {
|
||
warnPhones.value = Object.values(phonesData)
|
||
.filter(phone => phone && String(phone).trim())
|
||
.map(phone => String(phone));
|
||
} else {
|
||
// 如果是逗号分隔的字符串
|
||
warnPhones.value = row.warnPhoneJson.split(',').filter(phone => phone && phone.trim());
|
||
}
|
||
} else {
|
||
warnPhones.value = [];
|
||
}
|
||
} catch (e) {
|
||
// 如果解析失败,尝试按逗号分隔
|
||
if (row.warnPhoneJson) {
|
||
warnPhones.value = row.warnPhoneJson.split(',').filter(phone => phone && phone.trim());
|
||
} else {
|
||
warnPhones.value = [];
|
||
}
|
||
}
|
||
|
||
warnPhoneDialog.visible = true;
|
||
};
|
||
|
||
/** 获取塘口设备数量 */
|
||
const getPondDeviceCount = (pondId: string | number): number => {
|
||
return pondDeviceMap.value.get(pondId)?.length || 0;
|
||
};
|
||
|
||
/** 获取塘口开关数量 */
|
||
const getPondSwitchCount = (pondId: string | number): number => {
|
||
const switches = pondSwitchMap.value.get(pondId) || [];
|
||
return switches.length;
|
||
};
|
||
|
||
/** 根据设备类型获取塘口设备列表 */
|
||
const getPondDevicesByType = (pondId: string | number, types: number[]): DeviceVO[] => {
|
||
console.log(`🔍 调用 getPondDevicesByType - 塘口ID: ${pondId}, 类型: ${types}`);
|
||
const devices = pondDeviceMap.value.get(pondId) || [];
|
||
console.log(` - 从 Map 中获取的设备数量: ${devices.length}`);
|
||
console.log(` - 设备详情:`, devices);
|
||
const filtered = devices.filter(d => {
|
||
const match = types.includes(d.deviceType);
|
||
console.log(` - 设备 ${d.deviceName} (type: ${d.deviceType}) 匹配: ${match}`);
|
||
return match;
|
||
});
|
||
console.log(` - 过滤后的设备数量: ${filtered.length}`);
|
||
console.log(` - 过滤后的设备:`, filtered);
|
||
return filtered;
|
||
};
|
||
|
||
/** 根据设备ID获取开关列表 */
|
||
const getPondSwitchesByDevice = (pondId: string | number, deviceId: string | number): DeviceSwitchVO[] => {
|
||
const switches = pondSwitchMap.value.get(pondId) || [];
|
||
const filtered = switches.filter(sw => sw.deviceId === deviceId);
|
||
// 调试:打印开关数据
|
||
if (filtered.length > 0) {
|
||
console.log('开关数据:', filtered);
|
||
filtered.forEach(sw => {
|
||
console.log(`开关 ${sw.switchName} 的接线方式值:`, sw.connectVoltageType, '类型:', typeof sw.connectVoltageType);
|
||
});
|
||
}
|
||
return filtered;
|
||
};
|
||
|
||
/** 加载指定塘口的设备和开关数据 */
|
||
const loadPondDevices = async (pondId: string | number) => {
|
||
console.log('===== 开始加载塘口设备数据 =====');
|
||
console.log('塘口ID:', pondId);
|
||
console.log('塘口ID类型:', typeof pondId);
|
||
console.log('已加载塘口集合:', Array.from(loadedPonds.value));
|
||
|
||
// 如果已经加载过,直接返回
|
||
if (loadedPonds.value.has(pondId)) {
|
||
console.log('该塘口已加载过,跳过');
|
||
return;
|
||
}
|
||
|
||
deviceLoading.value = true;
|
||
|
||
try {
|
||
console.log('发起设备和开关数据请求...');
|
||
|
||
// 并行查询该塘口的设备和开关
|
||
const [deviceRes, switchRes] = await Promise.all([
|
||
listDevice({
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
pondId: pondId
|
||
}),
|
||
listDeviceSwitch({
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
pondId: pondId
|
||
})
|
||
]);
|
||
|
||
console.log('设备数据响应:', deviceRes);
|
||
console.log('开关数据响应:', switchRes);
|
||
|
||
// 更新设备数据(即使为空也要设置,避免重复查询)
|
||
const devices = deviceRes.rows || deviceRes.data || [];
|
||
console.log('解析出的设备列表:', devices);
|
||
console.log('设备数量:', devices.length);
|
||
|
||
// 创建新的 Map 以触发 Vue 响应式更新
|
||
const newDeviceMap = new Map(pondDeviceMap.value);
|
||
newDeviceMap.set(pondId, devices);
|
||
pondDeviceMap.value = newDeviceMap;
|
||
console.log('设备Map已更新,新的Map:', pondDeviceMap.value);
|
||
|
||
// 更新开关数据(即使为空也要设置)
|
||
const switches = switchRes.rows || switchRes.data || [];
|
||
console.log('解析出的开关列表:', switches);
|
||
console.log('开关数量:', switches.length);
|
||
|
||
// 创建新的 Map 以触发 Vue 响应式更新
|
||
const newSwitchMap = new Map(pondSwitchMap.value);
|
||
newSwitchMap.set(pondId, switches);
|
||
pondSwitchMap.value = newSwitchMap;
|
||
console.log('开关Map已更新,新的Map:', pondSwitchMap.value);
|
||
|
||
// 标记为已加载
|
||
loadedPonds.value.add(pondId);
|
||
|
||
console.log(`✅ 塘口 ${pondId} 加载完成: ${devices.length} 个设备, ${switches.length} 个开关`);
|
||
console.log('当前设备Map:', pondDeviceMap.value);
|
||
console.log('===== 加载完成 =====');
|
||
} catch (error) {
|
||
console.error('❌ 加载塘口设备数据失败:', error);
|
||
proxy?.$modal.msgError('加载设备数据失败,请重试');
|
||
} finally {
|
||
deviceLoading.value = false;
|
||
}
|
||
};
|
||
|
||
/** 监听塘口Tab切换 */
|
||
watch(activePondTab, async (newPondId, oldPondId) => {
|
||
console.log('\n===== Tab切换事件触发 =====');
|
||
console.log('从塘口', oldPondId, '切换到塘口', newPondId);
|
||
if (newPondId) {
|
||
await loadPondDevices(newPondId);
|
||
}
|
||
console.log('===== Tab切换处理完成 =====\n');
|
||
});
|
||
|
||
/** 查看用户塘口信息 */
|
||
const handleViewPonds = async (row: AquUserVO) => {
|
||
currentUserId.value = row.id;
|
||
pondDialog.title = `${row.userName} 的塘口信息`;
|
||
pondDialog.visible = true; // 先打开弹窗
|
||
pondLoading.value = true; // 开启加载状态
|
||
|
||
// 清空之前的数据
|
||
userPondList.value = [];
|
||
pondDeviceMap.value.clear();
|
||
pondSwitchMap.value.clear();
|
||
loadedPonds.value.clear();
|
||
|
||
try {
|
||
// 只查询塘口列表,不查询设备数据
|
||
const pondRes = await listPond({
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
userId: row.id
|
||
});
|
||
|
||
userPondList.value = pondRes.rows || [];
|
||
|
||
// 设置第一个塘口为激活状态,并显式加载该塘口的设备数据
|
||
if (userPondList.value.length > 0) {
|
||
const firstPondId = String(userPondList.value[0].id);
|
||
activePondTab.value = firstPondId;
|
||
// 显式加载第一个塘口的设备数据,不依赖 watch 触发
|
||
await loadPondDevices(firstPondId);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载塘口信息失败:', error);
|
||
proxy?.$modal.msgError('加载塘口信息失败,请重试');
|
||
} finally {
|
||
pondLoading.value = false; // 关闭加载状态
|
||
}
|
||
};
|
||
|
||
onMounted(() => {
|
||
getList();
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
// 加载容器
|
||
.loading-container {
|
||
min-height: 300px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
// 塘口弹窗样式
|
||
:deep(.pond-dialog) {
|
||
.el-dialog {
|
||
height: 85vh !important;
|
||
min-height: 85vh !important;
|
||
max-height: 85vh !important;
|
||
margin: 0 !important;
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
}
|
||
|
||
.el-dialog__header {
|
||
flex-shrink: 0 !important;
|
||
padding: 15px 20px;
|
||
}
|
||
|
||
.el-dialog__body {
|
||
flex: 1 !important;
|
||
overflow-y: auto !important;
|
||
padding: 15px 20px;
|
||
min-height: 0 !important;
|
||
}
|
||
|
||
.el-dialog__footer {
|
||
flex-shrink: 0 !important;
|
||
padding: 10px 20px;
|
||
}
|
||
|
||
.el-tabs--border-card {
|
||
border: none;
|
||
box-shadow: none;
|
||
height: 100% !important;
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
}
|
||
|
||
.el-tabs__header {
|
||
flex-shrink: 0 !important;
|
||
}
|
||
|
||
.el-tabs__content {
|
||
flex: 1 !important;
|
||
overflow-y: auto !important;
|
||
padding: 10px;
|
||
min-height: 0 !important;
|
||
}
|
||
}
|
||
|
||
.pond-info {
|
||
:deep(.el-descriptions) {
|
||
.el-descriptions__body {
|
||
.el-descriptions__cell {
|
||
padding: 6px 10px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.device-card {
|
||
border: 2px solid #e4e7ed;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
transition: all 0.3s ease;
|
||
background: #fff;
|
||
height: auto !important;
|
||
min-height: auto !important;
|
||
|
||
&:hover {
|
||
border-color: #409eff;
|
||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
:deep(.el-card__header) {
|
||
padding: 12px 18px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
background: linear-gradient(to right, #f8f9fa, #ffffff);
|
||
border-bottom: 2px solid #e4e7ed;
|
||
}
|
||
|
||
:deep(.el-card__body) {
|
||
padding: 15px 18px;
|
||
height: auto !important;
|
||
max-height: none !important;
|
||
overflow: visible !important;
|
||
}
|
||
}
|
||
|
||
.param-item {
|
||
text-align: center;
|
||
padding: 5px 0;
|
||
border-radius: 4px;
|
||
transition: background-color 0.2s;
|
||
|
||
&:hover {
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
.param-label {
|
||
font-size: 13px;
|
||
color: #909399;
|
||
margin-bottom: 4px;
|
||
line-height: 1.4;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.param-value {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.param-value-small {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
line-height: 1.4;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.text-primary {
|
||
color: #409eff;
|
||
}
|
||
|
||
.text-danger {
|
||
color: #f56c6c;
|
||
font-weight: 600;
|
||
}
|
||
|
||
// 开关卡片样式
|
||
.switch-item {
|
||
background: #f8f9fa;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 6px;
|
||
padding: 12px 14px;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
border-color: #409eff;
|
||
background: #ecf5ff;
|
||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
|
||
}
|
||
|
||
.switch-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
|
||
.switch-index {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #909399;
|
||
background: #e4e7ed;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.switch-name {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
.switch-details {
|
||
.detail-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 6px 8px;
|
||
background: #ffffff;
|
||
border-radius: 4px;
|
||
|
||
.detail-label {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.detail-value {
|
||
font-size: 13px;
|
||
color: #606266;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.detail-value-time {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|