420 lines
13 KiB
Vue
420 lines
13 KiB
Vue
|
<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>
|