一、效果:
二、实现:
<template>
<a-card :bordered="false" class="terminalsettings">
<!-- 上部分 -->
<div class="topBox">
<div class="topBox-box">
<!-- 西地磅 -->
<div class="box">
<h3>西地磅</h3>
<section>
<div>
地磅状态
<img src="./img/red.png" alt="" class="content-img" v-show="WastStationIdInfo.stationStatus == 2" />
<img src="./img/green.png" alt="" class="content-img" v-show="WastStationIdInfo.stationStatus == 1" />
</div>
<div class="content-span">
<span>
进磅
<img src="./img/red.png" alt="" class="content-img" v-show="WastStationIdInfo.enterStatus == 2" />
<img src="./img/green.png" alt="" class="content-img" v-show="WastStationIdInfo.enterStatus == 1" />
</span>
<span>
出磅
<img src="./img/red.png" alt="" class="content-img" v-show="WastStationIdInfo.leaveStatus == 2" />
<img src="./img/green.png" alt="" class="content-img" v-show="WastStationIdInfo.leaveStatus == 1" />
</span>
</div>
<div class="content-input">车牌<a-input v-model="WastStationIdInfo.carNumber" disabled /></div>
<div class="content-input">称重<a-input v-model="WastStationIdInfo.weight" disabled />(kg)</div>
<div class="content-input">车道编号<a-input v-model="WastStationIdInfo.laneNum" disabled /></div>
<div>
<div class="content-tipTitle"
v-if="WastStationIdInfo.contentList && WastStationIdInfo.contentList.length > 0">
<p v-for="(item, i) in WastStationIdInfo.contentList" :key="i">{{ item }}</p>
</div>
<div class="content-tipTitle" v-else>
<p>{{ $store.state.settings.PLATFORM_NAME }}</p>
<p>无人值守地磅</p>
<p>欢迎光临</p>
</div>
<p class="content-p">汽车衡</p>
</div>
<div class="content-radioGroup">
<a-radio-group v-model="WastStationIdInfo.orderData">
<a-radio :style="radioStyle" :value="'2'">禁止过磅</a-radio>
<a-radio :style="radioStyle" :value="'1'">允许过磅</a-radio>
</a-radio-group>
</div>
<div class="content-button">
<a-button type="primary" @click="connecter()">刷新</a-button>
<a-button type="danger"
@click="handleOrderData(WastStationIdInfo.stationId, WastStationIdInfo.orderData, WastStationIdInfo.laneNum)">下发命令</a-button>
</div>
</section>
</div>
<!-- 东地磅 -->
<div class="box">
<h3>东地磅</h3>
<section>
<div>
地磅状态
<img src="./img/red.png" alt="" class="content-img" v-show="EastStationIdInfo.stationStatus == 2" />
<img src="./img/green.png" alt="" class="content-img" v-show="EastStationIdInfo.stationStatus == 1" />
</div>
<div class="content-span">
<span>
进磅
<img src="./img/red.png" alt="" class="content-img" v-show="EastStationIdInfo.enterStatus == 2" />
<img src="./img/green.png" alt="" class="content-img" v-show="EastStationIdInfo.enterStatus == 1" />
</span>
<span>
出磅
<img src="./img/red.png" alt="" class="content-img" v-show="EastStationIdInfo.leaveStatus == 2" />
<img src="./img/green.png" alt="" class="content-img" v-show="EastStationIdInfo.leaveStatus == 1" />
</span>
</div>
<div class="content-input">车牌<a-input v-model="EastStationIdInfo.carNumber" disabled /></div>
<div class="content-input">称重<a-input v-model="EastStationIdInfo.weight" disabled />(kg)</div>
<div class="content-input">车道编号<a-input v-model="EastStationIdInfo.laneNum" disabled /></div>
<div>
<div class="content-tipTitle"
v-if="EastStationIdInfo.contentList && EastStationIdInfo.contentList.length > 0">
<p v-for="(item, i) in EastStationIdInfo.contentList" :key="i">{{ item }}</p>
</div>
<div class="content-tipTitle" v-else>
<p>{{ $store.state.settings.PLATFORM_NAME }}</p>
<p>无人值守地磅</p>
<p>欢迎光临</p>
</div>
<p class="content-p">汽车衡</p>
</div>
<div class="content-radioGroup">
<a-radio-group v-model="EastStationIdInfo.orderData">
<a-radio :style="radioStyle" :value="'2'">禁止过磅</a-radio>
<a-radio :style="radioStyle" :value="'1'">允许过磅</a-radio>
</a-radio-group>
</div>
<div class="content-button">
<a-button type="primary" @click="connecter()">刷新</a-button>
<a-button type="danger"
@click="handleOrderData(EastStationIdInfo.stationId, EastStationIdInfo.orderData, EastStationIdInfo.laneNum)">下发命令</a-button>
</div>
</section>
</div>
</div>
</div>
</a-card>
</template>
<script>
import { mapState } from 'vuex';
import '@/assets/less/TableExpand.less'
import { mixinDevice } from '@/utils/mixin'
import { httpAction, getAction, httpActions } from '@/api/manage'
import { filterMultiDictText, filterDictTextByCache } from '@/components/dict/JDictSelectUtil'
import mqtt from './mqtt.js';
export default {
name: 'terminalsettings',
mixins: [mixinDevice],
data() {
return {
//西地磅
WastStationIdInfo: {
orderData: '1',
laneNum: '1'
},
//东地磅
EastStationIdInfo: {
orderData: '1',
laneNum: '2'
},
deptId: null,
stationId: null,
radioStyle: {
display: 'block',
height: '30px',
lineHeight: '30px',
},
url: {
list: "/kd/actual/getActual",//实时数据
edit: "/kd/stationOrder/submitOrder",//下发命令
getOrder: "/kd/stationOrder/getOrder",//查询磅点指令
firstweightInfo: "/kd/weightInfo/findInfo",//首次过磅列表
secondweightInfo: "/kd/weightInfo/findInfo",//首次过磅列表
},
// 主题:mqtt/{车道编号}/{站点编号}/ledContent
// 地址:
// 端口号:8083
// 用户名:
// 密码:
serveMqtt: 'ws://地址:端口号/mqtt',
sendMassage: {},
receiveMessage: {}, //收到的推送消息
client: null,
//MQTT连接的配置
options: {
// wsOptions: {},
// protocolVersion: 4, //MQTT连接协议版本
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
keepalive: 60,
clean: false,
username: 用户名,
password: 密码,
reconnectPeriod: 3000, //1000毫秒,两次重新连接之间的间隔
connectTimeout: 30 * 1000, //1000毫秒,两次重新连接之间的间隔
resubscribe: true //如果连接断开并重新连接,则会再次自动订阅已订阅的主题(默认true)
},
};
},
computed: {
...mapState(['bdidInfo']),
},
watch: {
// 监听机构和磅点bdidInfo存储信息的变化
bdidInfo: {
handler(value) {
if (value != null) {
this.deptId = value.deptId
this.stationId = value.stationId
this.sendMassage.stationId = value.stationId
}
},
immediate: true
}
},
mounted() {
this.connecter()
},
methods: {
handleOrderData(stationId, orderData, laneNum) {
const that = this
if (!laneNum || laneNum == null || laneNum == undefined) {
that.$message.warning("车道编号不能为空");
return
} else if (!orderData || orderData == null || orderData == undefined) {
that.$message.warning("指令不能为空");
return
}
var param = {
stationId: that.sendMassage.stationId || stationId,
orderData: orderData,//指令。1=允许过磅,2=禁止过磅
laneNum: laneNum,//车道编号
}
httpActions(this.url.edit, param, 'POST').then((res) => {
if (res.success) {
that.$message.success(res.message);
} else {
that.$message.warning(res.message);
}
})
},
/*
mqtt 推送
*/
connecter: function () { // 连接
const that = this;
if (that.client == null || that.client.connented == false) {
that.$message.loading({ content: '连接中···', duration: 3 });
// 连接
that.client = mqtt.connect(that.serveMqtt, that.options);
that.client.on('connect', () => {
that.$message.success({ content: '连接成功!' });
that.subscribe();
});
that.client.on('message', (topic, message) => {
console.log('收到来自' + topic + '的消息' + message.toString());
that.receiveMessage = JSON.parse(message);
console.log("receiveMessage:", that.receiveMessage)
if (!(typeof that.receiveMessage == 'object') || Object.keys(that.receiveMessage).length == 0) {
that.WastStationIdInfo = { orderData: '1', laneNum: '1' }
that.EastStationIdInfo = { orderData: '1', laneNum: '2' }
return
}
that.receiveMessage.contentList = that.receiveMessage.contentList && that.receiveMessage.contentList.length > 0 ? that.receiveMessage.contentList.split(",") : []
if (!that.receiveMessage.laneNum) {
that.$message.error("MQTT获取到的车道编号为空")
return
}
if (that.receiveMessage.laneNum == 1) {// 西地磅
that.WastStationIdInfo = { ...that.receiveMessage }
console.log("西地磅", that.WastStationIdInfo)
} else {// 东地磅
that.EastStationIdInfo = { ...that.receiveMessage }
console.log("东地磅", that.EastStationIdInfo)
}
});
}
// 重连
that.client.on('reconnect', error => {
that.$message.loading({ content: '加载中...', duration: 2 });
setTimeout(() => {
that.$message.warning({ content: "正在重连···" });
}, 2000);
});
// 连接失败
that.client.on('error', error => {
that.$message.error({ content: '连接失败!' + error });
});
},
//订阅
subscribe: function () {
const that = this;
if (!that.client || !that.client.connected) {// 判断是否已成功连接
that.$message.error({ content: '客户端未连接!' });
that.connecter()
return;
}
// 订阅主题(网页接收主题)
// mqtt/磅点ID+车道/ReceivedCarNumber 测试主题: let Topic = 'mqtt/1/10086/ledContent'; // mqtt/磅点ID+车道/ReceivedCarNumber
let Topic1 = 'mqtt/1/' + that.sendMassage.stationId + '/ledContent';
let Topic2 = 'mqtt/2/' + that.sendMassage.stationId + '/ledContent';
let Topic3 = 'mqtt/3/' + that.sendMassage.stationId + '/ledContent';
let Topic4 = 'mqtt/4/' + that.sendMassage.stationId + '/ledContent';
// 【1】[Topic1, Topic2]:订阅多个主题。
// 【2】QoS: (1)0:消息最多传递一次,如果当时客户端不可用,则会丢失该消息;(2)1:消息传递至少 1 次;(3)2:消息仅传送一次。
that.client.subscribe([Topic1, Topic2, Topic3, Topic4], { qos: 0 }, error => {
if (!error) {
that.$message.loading({ content: '加载中...', duration: 2 });
setTimeout(() => {
that.$message.success({ content: '订阅成功' });
}, 2000);
} else {
that.$message.error({ content: '订阅失败!' + error });
}
});
// 订阅多个主题 称重信息 支付信息
// this.client.subscribe(['mqtt/' + 317522 + '/weight', 'mqtt/' + 317522 + '/pay '], { qos: this.Qos }, err => {
// console.log(err || '订阅成功');
// // this.show(err || '订阅成功');
// });
// 订阅不同 qos 的不同主题
// this.client.subscribe([Topic, Topic1], (error) => {
// if (!error) {
// that.$message.loading({ content: '加载中...' });
// setTimeout(() => {
// that.$message.success({ content: '订阅成功', duration: 2 });
// }, 1000);
// } else {
// console.log(error)
// }
// });
},
// 推送消息
publish: function () {
const that = this;
if (!that.client || !that.client.connected) {// 判断是否已成功连接
that.$message.error({ content: '客户端未连接!' });
that.connecter()
return;
}
if (Object.keys(that.sendMassage).length > 0) {
const that = this
if (!that.sendMassage.laneNum || that.sendMassage.laneNum == null || that.sendMassage.laneNum == undefined) {
that.$message.warning("车道编号不能为空");
return
} else if (!that.sendMassage.orderData || that.sendMassage.orderData == null || that.sendMassage.orderData == undefined) {
that.$message.warning("指令不能为空");
return
}
let Topic = 'mqtt/' + that.sendMassage.laneNum + '/' + that.sendMassage.orderData + '/' + that.sendMassage.stationId + '/ledContent'; // mqtt/磅点ID+车道/ReceivedCarNumber 网页推送(发布)主题
let sendInfo = JSON.stringify(that.sendMassage);
that.client.publish(Topic, sendInfo, error => {
if (!error) {
that.$message.success("指令发送成功");
} else {
that.$message.error({ content: '指令发送失败!' + error });
}
});
} else {
that.$message.error('推送消息为空!');
}
},
}
};
</script>
<style lang="less" scoped>
.terminalsettings {
display: flex;
flex-direction: column;
.topBox {
border: 1px solid lightgray;
box-sizing: border-box;
.noResult {
height: 180px;
display: flex;
align-items: center;
justify-content: center;
}
.topBox-box {
display: grid;
grid: auto / auto auto auto auto;
}
.box {
border-right: 1px solid lightgray;
box-sizing: border-box;
&:nth-child(4n + 4) {
border-right: unset;
}
h3 {
font-weight: bold;
margin: 5px 10px;
padding-left: 5px;
border-left: 6px solid
}
section {
font-size: 15px;
color:
font-weight: 600;
padding: 10px 30px;
.content-span {
margin: 10px 0;
span {
&:nth-child(1) {
padding-right: 20px;
}
}
}
.content-img {
width: 20px;
height: 20px;
margin-left: 10px;
}
.content-input {
padding: 5px 0;
/deep/.ant-input {
margin-left: 30px;
width: 65%;
color:
}
}
.content-inputTime {
padding: 5px 0;
/deep/.ant-input {
margin-left: 10px;
width: 60%;
color:
}
}
.content-tipTitle {
width: 100%;
height: 180px;
margin-top: 5px;
padding: 25px 0;
border-radius: 4px;
border: 1px solid lightgrey;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-y: auto;
overflow-x: hidden;
p {
color: red;
font-size: 14px;
font-weight: bold;
// letter-spacing: 4px;
padding: 0;
margin: 0;
}
}
.content-p {
text-align: center;
font-weight: normal;
font-size: 12px;
margin-top: 4px;
}
.content-radioGroup {
text-align: center;
font-size: 14px;
padding: 5px 0 15px 0;
cursor: pointer;
}
.content-button {
display: flex;
justify-content: space-evenly;
}
}
}
}
.bottomBox {
margin-top: 24px;
box-sizing: border-box;
}
}
</style>