master
xce 2025-12-23 02:30:18 +08:00
parent 7143fb2ca5
commit 7648d91935
4 changed files with 240 additions and 267 deletions

View File

@ -1,7 +1,8 @@
// 应用全局配置 // 应用全局配置
module.exports = { module.exports = {
// baseUrl: 'https://vue.ruoyi.vip/prod-api', // baseUrl: 'https://vue.ruoyi.vip/prod-api',
baseUrl: 'http://localhost:8088', // baseUrl: 'http://localhost:8088',
baseUrl: 'http://1.94.254.176:8088',
// 应用信息 // 应用信息
appInfo: { appInfo: {
// 应用名称 // 应用名称

View File

@ -1,4 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import App from './App' import App from './App'
import store from './store' // store import store from './store' // store
import plugins from './plugins' // plugins import plugins from './plugins' // plugins

View File

@ -1,166 +1,138 @@
<template> <template>
<view class="container"> <view class="container">
<view class="header"> <uni-section :title="lastTopic" type="line" padding>
<text class="title">我的相册</text> <uni-grid :column="4" border-color="#03a9f4">
<view class="header-actions"> <uni-grid-item :index="0">
<text class="action-btn" @click="selectMode"></text> <view class="grid-item-box">
</view> <text class="text">{{ temp1 }} </text>
</view> <text class="text"> 温度1 </text>
</view>
<view v-if="albums.length > 0" class="album-list"> </uni-grid-item>
<view <uni-grid-item :index="1">
class="album-item" <view class="grid-item-box">
v-for="(album, index) in albums" <text class="text">{{ temp2 }}</text>
:key="index" <text class="text"> 温度2 </text>
@click="openAlbum(album)" </view>
> </uni-grid-item>
<view class="album-cover"> <uni-grid-item :index="2">
<image <view class="grid-item-box">
:src="album.cover || defaultImage" <text class="text">{{ temp3 }}</text>
class="cover-image" <text class="text"> 温度3 </text>
mode="aspectFill" </view>
></image> </uni-grid-item>
<view class="album-count">{{ album.photos.length }}</view> <uni-grid-item :index="3">
</view> <view class="grid-item-box">
<view class="album-info"> <text class="text">{{ temp4 }}</text>
<text class="album-name">{{ album.name }}</text> <text class="text"> 温度4 </text>
<text class="album-date">{{ album.date }}</text> </view>
</view> </uni-grid-item>
</view> </uni-grid>
</view> <uni-grid :column="4" border-color="#03a9f4">
<uni-grid-item :index="0">
<view v-else class="empty-state"> <view class="grid-item-box">
<image src="/static/empty.png" class="empty-image" mode="aspectFit"></image> <text class="text">{{ humi1 }}</text>
<text class="empty-text">暂无相册</text> <text class="text"> 湿度1 </text>
<button class="create-btn" @click="createNewAlbum"></button> </view>
</view> </uni-grid-item>
<uni-grid-item :index="1">
<!-- 选择模式下的底部操作栏 --> <view class="grid-item-box">
<view v-if="isSelectMode" class="bottom-actions"> <text class="text">{{ humi2 }}</text>
<view class="select-all" @click="toggleSelectAll"> <text class="text"> 湿度2 </text>
<text>{{ isAllSelected ? '取消全选' : '全选' }}</text> </view>
</view> </uni-grid-item>
<button class="delete-btn" @click="deleteSelected" :disabled="selectedAlbums.length === 0"> <uni-grid-item :index="2">
删除({{ selectedAlbums.length }}) <view class="grid-item-box">
</button> <text class="text">{{ humi3 }}</text>
</view> <text class="text"> 湿度3 </text>
</view>
</uni-grid-item>
<uni-grid-item :index="3">
<view class="grid-item-box">
<text class="text">{{ humi4 }}</text>
<text class="text"> 湿度4 %RH</text>
</view>
</uni-grid-item>
</uni-grid>
</uni-section>
</view> </view>
</template> </template>
<script> <script>
import mqtt from 'mqtt/dist/mqtt.js' //mqtt
// mqtt.connect(...)
let client = null
export default { export default {
data() { data() {
return { return {
isSelectMode: false, connected: false,
isAllSelected: false, temp1: null,
selectedAlbums: [], humi1: null,
defaultImage: 'https://picsum.photos/300/200?random=1', temp2: null,
albums: [ humi2: null,
{ temp3: null,
id: 1, humi3: null,
name: '旅行记忆', temp4: null,
date: '2024-01-15', humi4: null,
cover: 'https://picsum.photos/300/200?random=2', lastTopic: "设备" + '',
photos: [ lastRaw: ''
'https://picsum.photos/400/300?random=3',
'https://picsum.photos/400/300?random=4',
'https://picsum.photos/400/300?random=5'
]
},
{
id: 2,
name: '家庭聚会',
date: '2024-01-10',
cover: 'https://picsum.photos/300/200?random=6',
photos: [
'https://picsum.photos/400/300?random=7',
'https://picsum.photos/400/300?random=8'
]
},
{
id: 3,
name: '工作记录',
date: '2024-01-05',
cover: 'https://picsum.photos/300/200?random=9',
photos: [
'https://picsum.photos/400/300?random=10',
'https://picsum.photos/400/300?random=11',
'https://picsum.photos/400/300?random=12'
]
}
]
} }
}, },
methods: { onLoad() {
selectMode() { const clientId = 'uniapp_' + Date.now() + '_' + Math.floor(Math.random() * 100000)
this.isSelectMode = !this.isSelectMode
if (!this.isSelectMode) {
this.selectedAlbums = []
this.isAllSelected = false
}
},
openAlbum(album) { client = mqtt.connect('ws://1.94.254.176:9001', {
if (this.isSelectMode) { clientId,
this.toggleAlbumSelection(album.id) username: 'admin', //
} else { password: 'Admin#12345678', //
uni.navigateTo({ clean: true,
url: `/pages/album/album?id=${album.id}&name=${album.name}` reconnectPeriod: 2000,
}) connectTimeout: 5000
} })
},
toggleAlbumSelection(id) { client.on('connect', () => {
const index = this.selectedAlbums.indexOf(id) this.connected = true
if (index > -1) { client.subscribe('dtu/+/up', { qos: 0 })
this.selectedAlbums.splice(index, 1) })
} else {
this.selectedAlbums.push(id)
}
this.checkAllSelected()
},
toggleSelectAll() { client.on('message', (topic, payload) => {
this.isAllSelected = !this.isAllSelected this.lastTopic = "设备【" + topic+"】"
if (this.isAllSelected) { const s = payload.toString()
this.selectedAlbums = this.albums.map(album => album.id) this.lastRaw = s
} else {
this.selectedAlbums = []
}
},
checkAllSelected() { // JSON
this.isAllSelected = this.selectedAlbums.length === this.albums.length if (!(s.startsWith('{') && s.endsWith('}'))) return
}, let obj
try { obj = JSON.parse(s) } catch(e) { return }
deleteSelected() { // 101~104湿201~204/10湿/10
uni.showModal({ const div10 = (v) => (v == null ? null : Math.round((Number(v)/10)*10)/10)
title: '确认删除',
content: `确定要删除选中的${this.selectedAlbums.length}个相册吗?`,
success: (res) => {
if (res.confirm) {
this.albums = this.albums.filter(album =>
!this.selectedAlbums.includes(album.id)
)
this.selectedAlbums = []
this.isSelectMode = false
uni.showToast({
title: '删除成功',
icon: 'success'
})
}
}
})
},
createNewAlbum() { this.temp1 = div10(obj["201"]); this.humi1 = div10(obj["101"])
uni.navigateTo({ this.temp2 = div10(obj["202"]); this.humi2 = div10(obj["102"])
url: '/pages/create/create' this.temp3 = div10(obj["203"]); this.humi3 = div10(obj["103"])
}) this.temp4 = div10(obj["204"]); this.humi4 = div10(obj["104"])
} })
client.on('close', () => { this.connected = false })
client.on('error', () => { this.connected = false })
},
onUnload() {
try { client && client.end(true) } catch(e) {}
//
//
// connected
//
// temp1/humi1 temp4/humi4
//
// lastTopic lastRaw
} }
} }
</script> </script>
<style scoped> <style scoped>
@ -170,133 +142,64 @@ export default {
min-height: 100vh; min-height: 100vh;
} }
.header { .text {
font-size: 22px;
font-weight: bolder;
display: inline-block;
}
.text:nth-child(even) {
font-size: 14px;
margin-top: 5px;
color: #9c9c9c;
}
.example-body {
/* #ifndef APP-NVUE */
// display: block;
/* #endif */
}
.grid-dynamic-box {
margin-bottom: 15px;
}
.grid-item-box {
flex: 1;
// position: relative;
/* #ifndef APP-NVUE */
display: flex; display: flex;
justify-content: space-between; /* #endif */
flex-direction: column;
align-items: center; align-items: center;
padding: 20rpx 0; justify-content: center;
background-color: #fff; padding: 15px 0;
margin-bottom: 20rpx;
border-radius: 10rpx;
padding: 20rpx;
} }
.title { .grid-item-box-row {
font-size: 36rpx; flex: 1;
font-weight: bold; // position: relative;
color: #333; /* #ifndef APP-NVUE */
}
.action-btn {
color: #007aff;
font-size: 28rpx;
}
.album-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.album-item {
background-color: #fff;
border-radius: 10rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}
.album-cover {
position: relative;
height: 200rpx;
}
.cover-image {
width: 100%;
height: 100%;
}
.album-count {
position: absolute;
top: 10rpx;
right: 10rpx;
background-color: rgba(0,0,0,0.5);
color: #fff;
font-size: 20rpx;
padding: 4rpx 8rpx;
border-radius: 6rpx;
}
.album-info {
padding: 15rpx;
}
.album-name {
font-size: 28rpx;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.album-date {
font-size: 20rpx;
color: #999;
}
.empty-state {
text-align: center;
padding: 100rpx 0;
}
.empty-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
display: block;
margin-bottom: 30rpx;
}
.create-btn {
background-color: #007aff;
color: #fff;
border: none;
padding: 20rpx 40rpx;
border-radius: 50rpx;
font-size: 28rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx;
display: flex; display: flex;
justify-content: space-between; /* #endif */
flex-direction: row;
align-items: center; align-items: center;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.1); justify-content: center;
padding: 15px 0;
} }
.select-all {
color: #007aff; /* #ifdef H5 */
font-size: 28rpx; @media screen and (min-width: 768px) and (max-width: 1425px) {
.swiper {
height: 630px;
}
} }
.delete-btn { @media screen and (min-width: 1425px) {
background-color: #ff3b30; .swiper {
color: #fff; height: 830px;
border: none; }
padding: 15rpx 30rpx;
border-radius: 50rpx;
font-size: 24rpx;
} }
.delete-btn[disabled] { /* #endif */
background-color: #ccc;
}
</style> </style>

68
utils/mqtt.js Normal file
View File

@ -0,0 +1,68 @@
import mqtt from 'mqtt/dist/mqtt.js' //引入mqtt依赖
var client
let mqttConnected = false //mqtt连接状态这个可以不要直接查询client.connected即可
const publishTopic = '/test/123/456/set' //发布Topic
const MQTT_IP = '192.168.9.128:8083/mqtt' //mqtt地址端口
const MQTT_OPTIONS = {
connectTimeout: 5000, //连接超时时间
clientId: 'SiD0FMMAxrs', //clientId不能重复这里可以随机生成
username: 'test', //用户名
password: 'test', //密码
clean: false
}
//创建客户端连接
export function mqttConnect() {
// #ifdef H5
client = mqtt.connect('ws://' + MQTT_IP, MQTT_OPTIONS,function(err){
console.log(err)
})
// #endif
// #ifdef MP-WEIXIN||APP-PLUS
client = mqtt.connect('wx://' + MQTT_IP, MQTT_OPTIONS,function(err){
console.log(err)
})
// #endif
client.on('connect', function() {
console.log('连接成功')
mqttConnected = true
}).on('reconnect', function(error) {
console.log('正在重连...', error)
mqttConnected = false
}).on('error', function(error) {
console.log('连接失败...', error)
mqttConnected = false
}).on('end', function() {
console.log('连接断开')
mqttConnected = false
}).on('close',function(){
console.log('连接关闭')
mqttConnected = false
}).on('offline',function(){
console.log('客户端下线')
})
}
//发布消息
export function mqttPublish(msg){
if(mqttConnected){
client.publish(publishTopic,msg,{qos:1,retain:false});//hello mqtt +
console.log('发布了一条消息',msg)
}else{
uni.showToast({
title: 'MQTT服务器未连接',
icon: 'none'
});
}
}
//断开连接
export function mqttDisconnect(){
console.log('mqttConnected',mqttConnected)
console.log('client',client)
if(mqttConnected){
client.end(true)
mqttConnected = false
}
}