2025-05-22 16:24:05 +08:00

420 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<u--input v-model="queryParams.modelName" placeholder="请输入变量名称" prefixIcon="search"
prefixIconStyle="color: #909399" customStyle="height:50rpx;margin:0 20rpx;background-color: #ffffff;">
<template slot="suffix">
<u-button :text="$tt('scene.search')" type="primary" size="mini" @click="handleSearch"></u-button>
</template>
</u--input>
<view class="event-wrap">
<view class="alert-item" v-for="(item, index) in datas" :key="index">
<view class="left">
<u--text color="#666" :text="item.modelName"></u--text>
<view style="width:40px;margin-top: 10px;">
<u-tag text="事件" type="warning" size="mini" :plain="true" v-if="item.type==3"></u-tag>
<u-tag text="属性" type="primary" size="mini" :plain="true" v-if="item.type==1"></u-tag>
<u-tag text="功能" type="success" size="mini" :plain="true" v-if="item.type==2"></u-tag>
</view>
</view>
<view :class="deviceInfo.protocolCode==='MODBUS-RTU' ? 'middle' : 'right'">
<u-row justify="space-between">
<u-col :span="3">
<u--text color="#666" :text="item.value=== '' ||item.value === null ? '-' : item.value">
</u--text>
</u-col>
<u-col :span="2">
<u--text :text="item.unit"></u--text>
</u-col>
<u-col :span="2">
<!-- 下发按钮 -->
<u-icon name="edit-pen" color="#2979ff" size="20" v-if="item.isReadonly === 0&&item.type!=3"
@click="editFunc(item)"></u-icon>
</u-col>
</u-row>
</view>
<view class="right" v-if="deviceInfo.protocolCode==='MODBUS-RTU'">
<u-button type="primary" size="mini" :plain="true" custom-style="margin:10px;"
@click="activeCollection(item)" v-if="deviceInfo.protocolCode==='MODBUS-RTU'">主动采集</u-button>
</view>
</view>
<u-empty mode="data" :show="total === 0" marginTop="60" :text="$tt('scene.emptyData')"></u-empty>
</view>
<!-- 下发数据弹窗 -->
<u-popup :show="isShowModel" :round="5" mode="bottom" bgColor="#eef3f7" :closeOnClickOverlay="false"
@close="isShowModel = false">
<view class="integer-popup-wrap" style="height:150px;padding: 10px;">
<view class="nav">
<text @click="isShowModel = false">{{$tt('common.cancel')}}</text>
<text @click="sendService">{{$tt('common.confirm')}}</text>
</view>
<view style="background-color: #fff;height:50px;padding: 10px;border-radius: 5px;">
<u--form labelPosition="left" label-width="180px">
<u-form-item v-for="(item, index) in opationList" :label="`${item.label}`" :key="index">
<!--枚举,布尔型,弹出选择框 -->
<u-popup :show="showSelectModel" :round="5" mode="bottom" :closeOnClickOverlay="true"
@close="showSelectModel = false">
<view class="operator-popup-wrap">
<view class="cell-group-wrap">
<u-cell-group :border="false">
<view class="cell-wrap" v-for="(item1,index1) in item.options" :key="index">
<u-cell :title="item1.label" :name="item1.value"
@click="handleConfirmOperator(item1)"></u-cell>
</view>
</u-cell-group>
</view>
</view>
</u-popup>
<!-- 字符串 -->
<u--input v-model="funVal[item.key]" border="none" inputAlign="right" placeholder="请输入"
:customStyle="{ marginRight: '20rpx',width:'150px' }"
v-if="item.dataTypeName == 'string'||(item.dataTypeName == 'array'&&item.arrayType=='string')" />
<!-- 整数小数 -->
<u--input v-model="funVal[item.key]" border="none" inputAlign="right" placeholder="请输入"
@input="justNumber(item)" type="number"
:customStyle="{ marginRight: '10rpx',width:'10px',color:'#000' }"
v-if="item.dataTypeName == 'integer' || item.dataTypeName == 'decimal'||item.dataTypeName == 'array'||(item.dataTypeName == 'array'&&item.arrayType=='integer')||(item.dataTypeName == 'array'&&item.arrayType=='decimal')"></u--input>
<span
v-if="(item.dataTypeName == 'integer' || item.dataTypeName == 'decimal'||(item.dataTypeName == 'array'&&item.arrayType=='integer')||(item.dataTypeName == 'array'&&item.arrayType=='decimal')) && item.unit && item.unit != 'un' && item.unit != '/'">{{
item.unit
}}</span>
<span class="range"
v-if="item.dataTypeName == 'integer'|| item.dataTypeName == 'decimal'||(item.dataTypeName == 'array'&&item.arrayType=='integer')||(item.dataTypeName == 'array'&&item.arrayType=='decimal')">
({{ item.min }} ~ {{ item.max }})
</span>
<!-- 枚举布尔型 -->
<u--input v-model="operators.label" border="none" inputAlign="right" placeholder="请选择"
disabledColor="#fff" disabled :customStyle="{ marginRight: '20rpx' }"
v-if="item.dataTypeName == 'enum' || item.dataTypeName == 'bool'" />
<u-icon slot="right" name="arrow-right" @click="openList"
v-if="item.dataTypeName == 'enum' || item.dataTypeName == 'bool'"></u-icon>
</u-form-item>
</u--form>
</view>
</view>
</u-popup>
<u-modal :show="centerDialogVisible" content="下发指令主动采集变量,是否继续?" @confirm="confirmCollection()"
@cancel="() => centerDialogVisible = false" showCancelButton></u-modal>
<u-loadmore :status="loadmoreStatus" v-if="total > queryParams.pageSize" loading-text="努力加载中..."
loadmoreText="点击我加载更多" nomoreText="实在没有了" marginTop="20" @loadmore="loadMoreLogs" />
<u-loading-page :loading="loading" bg-color="#eef3f7" loadingText="ZYC.cn"></u-loading-page>
</view>
</template>
<script>
import {
listThingsModel,
getOrderControl,
propGet,
serviceInvokeReply
} from '@/apis/modules/device.js';
export default {
name: "device-variable",
props: {
device: {
type: Object,
default: null,
required: true
}
},
watch: {
// 兼容小程序
device: function(newVal, oldVal) {
this.deviceInfo = newVal;
if (newVal.deviceId && newVal.deviceId !== oldVal.deviceId) {
this.queryParams.deviceId = newVal.deviceId;
this.getVariableList();
}
},
},
data() {
return {
queryParams: {
deviceId: '',
pageNum: 1,
pageSize: 10,
},
datas: [],
centerDialogVisible: false, //采集弹窗
form: {},
canSend: false, //是否可以下发,主要判断数值在不在范围
funVal: {},
chooseFun: {},
//下发的设备
serialNumber: '',
opationList: [],
total: 0, // 总条数
loadmoreStatus: 'loadmore', // 加载更多
loading: false,
isShowModel: false, //下发弹窗
operators: {},
showSelectModel: false,
deviceInfo: {},
};
},
mounted() {
const {
deviceId,
serialNumber
} = this.device;
if (deviceId) {
this.queryParams.deviceId = deviceId;
this.serialNumber = serialNumber;
this.getVariableList();
}
},
methods: {
//查询物模型列表
getVariableList() {
listThingsModel(this.queryParams).then((res) => {
if (res.code === 200) {
if (this.queryParams.pageNum == 1) {
this.datas = res.rows;
} else {
this.datas = this.datas.concat(res.rows);
}
this.total = res.total;
setTimeout(() => {
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
this.loadmoreStatus = 'nomore';
} else {
this.loadmoreStatus = 'loadmore';
}
}, 1000);
}
});
},
handleConfirmOperator(item) {
this.operators = item;
for (let key in this.funVal) {
this.funVal[key] = this.operators.value;
}
this.showSelectModel = false;
},
openList() {
this.operators = {};
this.showSelectModel = true;
},
//搜索
handleSearch() {
this.queryParams.pageNum = 1;
this.getVariableList();
},
//指令下发
async editFunc(item) {
const params = {
deviceId: this.device.deviceId,
modelId: item.modelId
}
//判断是否有权限
const response = await getOrderControl(params);
if (response.code != 200) {
uni.$u.toast(response.msg);
return;
}
//这里兼容子设备的下发,在网关设备下发的时候选择实际子设备的编号
this.serialNumber = item.serialNumber;
let title = '';
if (this.device.status !== 3 && this.device.isShadow !== 1) {
if (this.device.status === 1) {
title = '设备未激活';
} else if (this.device.status === 2) {
title = '设备处于禁用状态';
} else {
title = '设备处于离线状态';
}
uni.$u.toast(title);
return;
}
this.isShowModel = true;
this.canSend = true;
this.funVal = {};
this.operators.label = '';
this.chooseFun = item;
this.getOpationList(item);
},
// 发送指令
async sendService() {
if (this.canSend) {
try {
let params = this.funVal;
const pas = {
serialNumber: this.serialNumber,
identifier: this.chooseFun.identifier,
remoteCommand: params
}
this.isShowModel = true;
const res = await serviceInvokeReply(pas);
if (res.code == 200) {
this.$message.success('下发成功')
} else {
this.$message.error(res.data)
}
} finally {}
} else {
uni.showToast({
icon: 'none',
title: '数值不在范围内,不能下发,请重新输入!'
});
}
},
//主动采集数据
activeCollection(item) {
this.centerDialogVisible = true;
this.form.serialNumber = item.serialNumber;
this.form.type = 1;
this.form.identifier = item.identifier;
},
//确认采集
confirmCollection() {
propGet(this.form).then(response => {
if (response.code == 200) {
this.centerDialogVisible = false;
} else {
this.centerDialogVisible = false;
uni.showToast({
icon: 'none',
title: response.msg
});
return;
}
})
},
// 封装操作列表
getOpationList(item) {
this.opationList = []
let options = []
this.funVal = {}
const datatype = item.datatype;
if (datatype.type == 'enum') {
options = datatype.enumList?.map(option => {
return {
label: option.text,
value: option.value + ''
}
}) || []
}
if (datatype.type == 'bool') {
options = [{
label: datatype.falseText || '',
value: '0'
}, {
label: datatype.trueText || '',
value: '1'
}]
}
this.opationList.push({
dataTypeName: datatype.type,
arrayType: datatype.arrayType,
label: item.modelName,
key: item.identifier,
max: parseInt(datatype?.max || 100),
min: parseInt(datatype?.min || -100),
options: options,
value: item.value
})
this.opationList.forEach(item => {
let value = item.value
if (item.datatype == 'integer' || item.datatype == 'decimal' || (item.dataTypeName ==
'array' && item.arrayType == 'integer') || (item.dataTypeName == 'array' && item
.arrayType == 'decimal')) {
value = parseInt(value)
}
this.funVal[item.key] = value;
})
},
//判断输入是否超过范围
justNumber(val) {
this.canSend = true
this.opationList.some(item => {
if (item.max < this.funVal[item.key] || item.min > this.funVal[item.key]) {
this.canSend = false
return true
}
})
this.$forceUpdate()
},
// 上拉加载
loadMoreLogs() {
this.loadmoreStatus = 'loading';
this.queryParams.pageNum = ++this.queryParams.pageNum;
// 模拟网络请求
setTimeout(() => {
if ((this.queryParams.pageNum - 1) * this.queryParams.pageSize >= this.total) {
this.loadmoreStatus = 'nomore';
} else {
this.loadmoreStatus = 'loading';
this.getVariableList();
}
}, 1000);
},
// 下拉刷新
onPullDownRefresh() {
this.datas = [];
this.queryParams.pageNum = 1;
// 模拟网络请求
setTimeout(x => {
this.type === 1 && this.getList();
uni.stopPullDownRefresh();
}, 1000);
},
}
}
</script>
<style>
page {
background: #eef3f7;
}
</style>
<style lang="scss" scoped>
.event-wrap {
.log-item {
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
border-radius: 5px;
margin: 10px;
padding: 10px;
background-color: #ffffff;
}
.alert-item {
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
border-radius: 5px;
margin: 10px;
padding: 10px;
background-color: #ffffff;
.left {
margin-right: 10px;
width: 30%;
}
.middle {
flex: 1;
width: 30%;
}
.right {
margin-right: 10rpx;
width: 20%;
}
}
}
.nav {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
font-size: 32rpx;
margin-bottom: 34rpx;
}
.u-popup {
text-align: center;
/* 文字居中 */
}
</style>