| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738 |
- <template>
- <view class="facilitystate-page">
- <custom-card>
- <block slot="backText">
- <view class="facilitystate-top__title">
- <text :class="devStatus == '1' ? 'online' : 'offline'">{{
- devStatus == '1' ? '在线' : '离线'
- }}</text>
- {{ devName }}
- </view>
- </block>
- </custom-card>
- <view class="facilitystate-top">
- <view
- class="facilitystate-top__item"
- v-for="(item, index) in deviceList"
- :key="index"
- @click="nativeTo(item)"
- >
- <view class="item-icon-container" :class="item.className">
- <image class="item-icon" :src="item.icon" />
- </view>
- <view class="facilitystate-top__item-text">{{ item.title }}</view>
- </view>
- </view>
- <view class="irrMode">
- <view class="irrMode__header">
- <view @click="changeMode('1')" :class="workStatus == '1' ? 'active' : ''">施肥模式</view>
- <view @click="changeMode('0')" :class="workStatus == '0' ? 'active' : ''">灌溉模式</view>
- </view>
- <view class="irrMode__content">
- <view class="irrMode__content-header">
- <view class="irrMode__content-header-list">
- <view class="irrMode__content-header-title">
- <view class="irrMode__content-header-container">
- <image class="header-item-icon" :src="rain" />
- <text style="color:#333;font-size: 24rpx;margin-left: 10rpx">当前灌溉区域:</text>{{ runTitle }}
- </view>
- <view class="timer-text">{{ timer }}分</view>
- </view>
- </view>
- <view class="irrMode__content-item">
- <view class="irrMode__content-item-raduis" :class="runStatus == '1' ? 'active-radius' : ''" @click="changeActive('1')">
- <image class="item-icon" :src="runStatus=='1'?mediaPlayFill:mediaPlay" />
- <view class="item-text">启动</view>
- </view>
- <view class="irrMode__content-item-raduis" :class="runStatus == '2' ? 'active-radius' : ''" @click="changeActive('2')">
- <image class="item-icon" :src="runStatus=='2'?mediaPauseFill:mediaPause" />
- <view class="item-text">暂停</view>
- </view>
- <view class="irrMode__content-item-raduis" :class="runStatus == '0' ? 'active-radius' : ''" @click="changeActive('0')" style="margin-right:0">
- <image class="item-icon" :src="runStatus=='0'?stopFill:stop" />
- <view class="item-text">停止</view>
- </view>
- </view>
- <view class="irr-run-list">
- <text v-for="(item, index) in selectAreaList" :key="index">
- {{ item.group_name }}
- <text v-if="index != selectAreaList.length - 1">、</text>
- </text>
- </view>
- </view>
- </view>
- <Base :dataList="dataList" />
- <facilitystate
- :devBid="devBid"
- :ggbCurrent="ggbCurrent"
- :sfbCurrent="sfbCurrent"
- :irrigatedAreaList="irrigatedAreaList"
- :webSockedData="webSockedData"
- :alreadyfertilizerBucketList="alreadyfertilizerBucketList"
- />
- </view>
- </view>
- </template>
- <script>
- import manualControl from './assets/manualControl.png';
- import mediaPause from './assets/media-pause.png';
- import mediaPauseFill from './assets/media-pause-fill.png';
- import mediaPlay from './assets/media-play.png';
- import mediaPlayFill from './assets/media-play-fill.png';
- import rain from './assets/rain.png';
- import stop from './assets/stop.png';
- import stopFill from './assets/stop-fill.png';
- import wheelIrrigation from './assets/wheelIrrigation.png';
- import time from './assets/timing.png';
- import formulaSetting from './assets/formulaSetting.png';
- import operatingRecord from './assets/operatingRecord.png';
- import Base from './components/base.vue';
- import facilitystate from './components/facilitystate.vue';
- export default {
- data() {
- return {
- ws: null,
- currentMode: '1',
- title: '水肥一体机',
- manualControl,
- devBid: '',
- devName: '',
- devStatus: '1',
- dataList: [],
- irrigatedAreaList: [],
- alreadyfertilizerBucketList: [],
- mediaPause,
- mediaPauseFill,
- mediaPlay,
- mediaPlayFill,
- rain,
- stop,
- stopFill,
- deviceList: [
- {
- icon: manualControl,
- title: '手动控制',
- className: 'manualControl',
- url: '/pages/cb/shuifeizsFirst/control',
- },
- {
- icon: wheelIrrigation,
- title: '自动控制',
- className: 'wheelIrrigation',
- url: '/pages/cb/shuifeizsFirst/autoSetting?devBid=' + this.devBid,
- }, {
- icon: time,
- title: '定时设置',
- className: 'timedSetting',
- url: '/pages/cb/shuifeizsFirst/timingSetting?devBid=' + this.devBid,
- },{
- icon: formulaSetting,
- title: '配方设置',
- className: 'formulaSetting',
- url: '/pages/cb/shuifeizsFirst/formulaSetting?devBid=' + this.devBid,
- }, {
- icon: operatingRecord,
- title: '操作记录',
- className: 'operatingRecord',
- url: '/pages/cb/shuifeizsFirst/history?devBid=' + this.devBid,
- },
- ],
- ggbCurrent: {},
- sfbCurrent: {},
- sfToken: '',
- entityId: '',
- heartbeatTimer: null,
- info: {},
- dataArray:[],
- webSockedData: {},
- reconnectCount: 0,
- isActive: '0',
- runStatus: -1,
- workStatus: -1,
- clearIrr: {},
- selectAreaList: [],
- runTitle: '--',
- timer:'--'
- };
- },
- components: {
- Base,
- facilitystate,
- },
- onLoad(options) {
- /*
- 0 水源 泵类 负责水源进入管道的总泵或者阀
- 1 肥料 泵类 负责肥料进入管道的总阀或者泵
- 2 吸肥 泵类 控制肥料桶出肥的阀或者泵,每个肥料桶一个
- 3 搅拌 泵类 肥料桶的搅拌电机或者泵 每个肥料桶一个
- 4 肥料桶 泵类 肥料桶,每个施肥机有多个或者没有,不一定真实存在,只是逻辑上的概念
- 5 电磁阀 无 管道最末端每个田地里控制出水的阀门
- 6 传感器 无 水肥机上的 温度,压力,流速,PH EC等监测类要素
- 7 灌区 无 逻辑区域,电磁阀的分组
- */
- const { devBid, devName, devStatus } = options;
- this.devBid = devBid;
- this.devName = devName;
- this.devStatus = devStatus;
- this.deviceList[0].url = `/pages/cb/shuifeizsFirst/control?devBid=${options.devBid}`;
- this.deviceList[1].url = `/pages/cb/shuifeizsFirst/autoSetting?devBid=${options.devBid}`;
- this.deviceList[2].url = `/pages/cb/shuifeizsFirst/timingSetting?devBid=${options.devBid}`;
- this.deviceList[3].url = `/pages/cb/shuifeizsFirst/formulaSetting?devBid=${options.devBid}`;
- this.deviceList[
- this.deviceList.length - 1
- ].url = `/pages/cb/shuifeizsFirst/history?devBid=${options.devBid}`;
- if (devBid) {
- this.init();
- }
- },
- onUnload() {
- this.ws.close();
- },
- onShow(){
- if (this.devBid) {
- this.init();
- }
- },
- methods: {
- isHaveTime(childrenList) {
- let time = 0;
- let currentKey = '';
- let currentItem = {};
- for (let i = 0; i < this.selectAreaList.length; i++) {
- for (let key in this.selectAreaList[i]) {
- if (key.includes(':Status')) {
- currentKey = key;
- currentItem = this.selectAreaList[i];
- break;
- }
- }
- for (let i = 0; i < childrenList.length; i++) {
- const item = childrenList[i];
- if (item.sfCode === currentKey) {
- for (let key in currentItem) {
- if (key.includes(':PartTim')) {
- time = currentItem[key];
- }
- }
- break;
- }
- }
- }
- return time;
- },
- isAutoRun(childrenList) {
- for (let i = 0; i < childrenList.length; i++) {
- const item = childrenList[i];
- if (item.value == 1) {
- return true
- }
- }
- return false
- },
- async initsfyunshangAutoConfigInfo() {
- const selectAreaList = [];
- const res = await this.$myRequest({
- url:'/api/v2/iot/device/sf/yunshang/auto/config/info/',
- method:'post',
- data: {
- devBid: String(this.devBid),
- },
- })
- const list = res;
- const group_list = list.group_list || [];
- group_list.forEach((item) => {
- if (item.group_value == 1) {
- selectAreaList.push(item);
- }
- });
- this.selectAreaList = selectAreaList;
- },
- async devctlContorl(data){
- const params = {
- devBid: Number(this.devBid),
- data,
- };
- const res = await this.$myRequest({
- url: '/api/v2/iot/mobile/device/sf/devctl/',
- method: 'post',
- data:params,
- header: {
- 'Content-Type': 'application/json',
- },
- });
- uni.showToast({
- title: res.msg,
- icon: 'none',
- });
- },
- changeActive(active) {
- this.runStatus = active;
- const params = {
- IrrStatus:active
- }
- this.devctlContorl(params);
- },
- changeMode(mode) {
- if(this.runStatus == '1' || this.runStatus == '2'){
- uni.showToast({
- title: '请先停止运行',
- icon: 'none',
- });
- return
- }
- this.currentMode = mode;
- const params = {
- IrrMode:mode
- }
- this.devctlContorl(params);
- },
- initWebSocket() {
- const url = 'wss://things.ysiot.net:18080/api/ws';
- this.ws = uni.connectSocket({
- url: url,
- success: () => {
- console.log('WebSocket 连接初始化成功');
- }
- });
- uni.onSocketOpen(() => {
- console.log('WebSocket 已连接');
- // 发送测试参数
- const testParams = {
- cmds: [
- {
- type: 'TIMESERIES',
- entityType: 'DEVICE',
- entityId: this.entityId,
- scope: 'LATEST_TELEMETRY',
- cmdId: 1
- }
- ],
- authCmd: {
- cmdId: 0,
- token: this.sfToken,
- }
- };
- console.log('发送测试参数:', testParams);
- uni.sendSocketMessage({
- data: JSON.stringify(testParams)
- });
- // 心跳:每 30 秒 ping 一次
- this.heartbeatTimer = setInterval(() => {
- uni.sendSocketMessage({
- data: JSON.stringify({ type: 'ping' })
- });
- }, 30 * 1000);
- });
- uni.onSocketMessage((evt) => {
- try {
- const data = JSON.parse(evt.data);
- this.webSockedData = data;
- // 收到实时数据后刷新右侧组件
- if(this.dataArray.length){
- this.mergeTwoObject(this.dataArray,this.webSockedData?.data);
- this.initData(this.dataArray);
- }
- } catch (e) {
- console.error('WebSocket 消息解析失败', e);
- }
- });
- uni.onSocketError((e) => {
- console.error('WebSocket 错误', e);
- });
- uni.onSocketClose(() => {
- console.warn('WebSocket 已断开,3 秒后尝试重连');
- clearInterval(this.heartbeatTimer);
- this.reconnectCount = (this.reconnectCount || 0) + 1;
- if (this.reconnectCount <= 10) {
- setTimeout(() => this.initWebSocket(), 3000);
- } else {
- console.warn('WebSocket 重连次数已达上限,停止重连');
- }
- });
- },
-
-
- mergeTwoObject(firstObject, secondObject) {
- // 构建 sfCode 到对象的映射,实现 O(1) 查找
- const sfCodeMap = new Map();
-
- // 递归构建映射
- const buildMap = (items) => {
- for (const item of items) {
- if (item.sfCode) {
- sfCodeMap.set(item.sfCode, item);
- }
- if (item.childrenList && item.childrenList.length) {
- buildMap(item.childrenList);
- }
- }
- };
-
- buildMap(firstObject);
-
- // 遍历 secondObject 并更新值
- for (const key in secondObject) {
- let newKey = '';
- if(key.includes('GHChannelDev:ChannelParamSet')){
- newKey = key + ':Status';
- const item = sfCodeMap.get(newKey);
- if (item) {
- const params = JSON.parse(secondObject[key][0][1]);
- this.$set(item,'value',params.Status)
- }
- }else{
- const item = sfCodeMap.get(key);
- if (item) {
- this.$set(item,'value',secondObject[key][0][1])
- }
- }
- }
- },
- // 关闭 WebSocket
- closeWebSocket() {
- clearInterval(this.heartbeatTimer);
- if (this.ws) {
- uni.closeSocket();
- this.ws = null;
- }
- },
- async getsfrhinfo() {
- const res = await this.$myRequest({
- url: '/api/v2/iot/device/sf/info/',
- method: 'post',
- data: {
- devBid: String(this.devBid),
- },
- });
- this.info = res;
- this.sfToken = this.info?.sfToken;
- this.entityId = this.info?.sfUuid;
- this.closeWebSocket();
- this.initWebSocket();
- },
- async init() {
- this.getdeviceSfStatus();
- await this.getpeifangRefresh();
- this.getsfrhinfo();
- this.initsfyunshangAutoConfigInfo();
- await this.getRunStatus();
- },
- async getpeifangRefresh() {
- await this.$myRequest({
- url: '/api/v2/iot/mobile/device/sf/peifang/refresh/',
- method: 'post',
- data: {
- devBid: this.devBid,
- },
- });
- },
- async getRunStatus() {
- const res = await this.$myRequest({
- url: '/api/v2/iot/mobile/device/sf/zsrf/task/run/status/',
- method: 'post',
- data: {
- devBid: this.devBid,
- },
- });
- const data = JSON.stringify(res || {});
- if (data == '{}') {
- this.isRun = true;
- } else {
- this.isRun = false;
- }
- },
- initData(res){
- this.dataList = [];
- this.irrigatedAreaList = [];
- this.alreadyfertilizerBucketList = [];
- res?.forEach((item) => {
- if (item.sfType === '0') {
- this.ggbCurrent = item;
- } else if (item.sfType === '1') {
- this.sfbCurrent = item;
- } else if (item.sfType === '4') {
- this.alreadyfertilizerBucketList.push(item);
- } else if (item.sfType === '6') {
- this.dataList.push(item);
- } else if (item.sfType === '7') {
- this.irrigatedAreaList.push(item);
- } else if (item.sfType === '9') {
- if (item.sfCode === 'IrrStatus') {
- this.runStatus = item.value;
- } else if (item.sfCode === 'IrrMode') {
- this.workStatus = item.value;
- } else if (item.sfCode === 'WaterFlowSum:Value') {
- this.clearIrr = item;
- }
- }
- });
- this.runTitle = '--';
- this.timer = '--';
- if(this.runStatus == '1'){
- this.irrigatedAreaList.forEach((item) => {
- if (this.isAutoRun(item.childrenList)) {
- this.runTitle = item.sfDisplayname || item.sfName;
- this.timer = this.isHaveTime(item.childrenList);
- }
- });
- }else{
- this.runTitle = '--';
- this.timer = '--';
- }
- },
- async getdeviceSfStatus() {
- const res = await this.$myRequest({
- url: '/api/v2/iot/mobile/device/sf/status/',
- method: 'post',
- data: {
- devBid: this.devBid,
- },
- });
- this.dataArray = res || [];
- this.initData(this.dataArray);
- },
- nativeTo(item) {
- if(item.className == 'manualControl'){
- if(this.runStatus == '1' || this.runStatus == '2'){
- uni.showToast({
- title: '请先停止运行',
- icon: 'none',
- });
- return
- }
- }
- uni.navigateTo({
- url: item.url,
- });
- },
- },
- };
- </script>
- <style scoped lang="scss">
- uni-page-body {
- position: relative;
- height: 100%;
- }
- .irrMode{
- margin-top: 30rpx;
- border-radius: 32rpx 32rpx 0 0;
- margin-bottom: 30rpx;
- padding: 32rpx;
- border: 3rpx solid #FFF;
- background: linear-gradient(180deg, #D3F3F2 0%, #FFF 41.06%);
- .irrMode__header{
- display: flex;
- width:320rpx;
- padding:0 10rpx;
- align-items: center;
- background: #ffffff;
- border-radius: 12rpx;
- margin-bottom: 24rpx;
- font-family: "Source Han Sans CN VF";
- font-size: 28rpx;
- font-style: normal;
- font-weight: 400;
- line-height: normal;
- color:#999999;
- height: 68rpx;
- line-height: 68rpx;
- view{
- width: 160rpx;
- text-align: center;
- }
- }
- .active{
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 8rpx;
- color: #ffffff;
- background: #0BBC58;
- height: 56rpx;
- }
- .irrMode__content{
- margin-top: 24rpx;
- border-radius: 16rpx;
- padding: 2rpx;
- background: linear-gradient(180deg, #FFF 0%, #F5F6FA 100%);
- .irrMode__content-header{
- width: calc(100% - 32rpx);
- padding: 16rpx 16rpx 16rpx 16rpx;
- border-radius: 12rpx;
- background: linear-gradient(180deg, #BBF1EA 0%, #E6F4F9 100%);
- .irrMode__content-header-list{
- display:flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12rpx;
- }
- .irrMode__content-header-title{
- color: #020305;
- text-align: right;
- font-family: "Source Han Sans CN VF";
- font-size: 30rpx;
- font-weight: 500;
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- .irrMode__content-header-container{
- display: flex;
- align-items: center;
- }
- .header-item-icon{
- width: 48rpx;
- height: 48rpx;
- }
- .timer-text{
- color: #0bbc58;
- text-align: right;
- font-family: "Source Han Sans CN VF";
- font-size: 28rpx;
- font-weight: 400;
- }
- }
- .irrMode__content-header-desc{
- color: #999999;
- text-align: right;
- font-family: "Source Han Sans CN VF";
- font-size: 28rpx;
- font-weight: 400;
- }
- }
- .irrMode__content-item{
- display:flex;
- align-items: center;
- justify-content: center;
- padding: 16rpx 0;
- border-radius: 12rpx;
- .irrMode__content-item-raduis{
- display: flex;
- height: 88rpx;
- padding: 0rpx 32rpx;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- flex: 1 0 0;
- border-radius: 16rpx;
- margin-right: 16rpx;
- border: 2rpx solid #0BBC58;
- color: #0bbc58;
- text-align: right;
- font-family: "Source Han Sans CN VF";
- font-size: 20rpx;
- image{
- width: 48rpx;
- height: 48rpx;
- }
- }
- .active-radius{
- background: #0BBC58;
- color:#ffffff;
- }
- }
- .irr-run-list{
- padding: 16rpx;
- font-family: "Source Han Sans CN VF";
- color: #999999;
- font-family: "Source Han Sans CN VF";
- font-size: 24rpx;
- font-weight: 400;
- }
- }
- }
- .online {
- height: 32rpx;
- line-height: 32rpx;
- padding: 0 8rpx;
- border-radius: 4rpx;
- background: #14a478;
- color: #ffffff;
- font-family: 'Source Han Sans CN';
- font-size: 20rpx;
- font-weight: 500;
- margin-right: 8rpx;
- top: -4rpx;
- display:inline-block;
- position: relative;
- }
- .manualControl{
- background: linear-gradient(326deg, #FFAB3D 6.86%, #FED55A 93.52%);
- }
- .wheelIrrigation{
- background: linear-gradient(152deg, #83D2FF 14.82%, #02A2FC 92.92%);
- }
- .timedSetting{
- background: linear-gradient(335deg, #0BBC58 6.91%, #60D799 89.95%);
- }
- .formulaSetting{
- background: linear-gradient(147deg, #FE9797 12.77%, #FF6060 92.63%);
- }
- .operatingRecord{
- background: linear-gradient(152deg, #B9B9F6 11.22%, #8080F8 94.92%);
- }
- .facilitystate-top__title{
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- .offline {
- height: 32rpx;
- line-height: 32rpx;
- padding: 0 8rpx;
- border-radius: 4rpx;
- background: #ff4d4f;
- color: #ffffff;
- font-family: 'Source Han Sans CN';
- font-size: 20rpx;
- font-weight: 500;
- margin-right: 8rpx;
- top: -4rpx;
- display:inline-block;
- position: relative;
- }
- .facilitystate-page {
- background: linear-gradient(180deg, #f5f6fa00 0%, #F5F6FA 23.46%, #FFF 25.24%, #FFF 100%), linear-gradient(102deg, #BFEADD 6.77%, #B8F1E7 40.15%, #B9EEF5 84.02%);
- height: 100%;
- width: 100%;
- overflow-x: hidden;
- overflow-y: scroll;
- .facilitystate-top {
- display: grid;
- grid-template-columns: repeat(5, 1fr);
- text-align: center;
- margin-top: 18rpx;
- &__item {
- margin-bottom: 32rpx;
- .item-icon-container{
- width: 96rpx;
- height: 96rpx;
- border-radius: 26rpx;
- overflow: hidden;
- margin: 0 auto;
- display:flex;
- align-items: center;
- justify-content: center;
- }
- .item-icon {
- width: 64rpx;
- height: 64rpx;
- }
- }
- &__item-text {
- color: #042118;
- font-family: 'Source Han Sans CN VF';
- font-size: 28rpx;
- font-weight: 400;
- margin-top: 16rpx;
- }
- }
- }
- </style>
|