420 lines
13 KiB
Vue
Raw Permalink Normal View History

2025-05-22 16:24:05 +08:00
<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>