Files
APP/pages/common/index/index.vue

983 lines
23 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>
<!-- 使用自定义导航栏 -->
<custom-navbar :title="navTitle" :showBack="false" backgroundColor="#202020" color="#FFFFFF"
rightIcon="/static/images/common/add.png" @right-click="scan"></custom-navbar>
<view class="device-page" :style="{ paddingTop: navBarHeight + 'px' }">
<!-- handleSend 发送信息 -->
<view class="tab-bar-wrap">
<scroll-view class="tab-bar" scroll-x="true" scroll-with-animation>
<view class="tab-container">
<view v-for="(tab, index) in tabs" :key="index"
:class="['tab-item', activeTab === index ? 'active' : '']" @click="switchTab(tab, index)">
{{ tab.typeName }}
</view>
</view>
</scroll-view>
<view class="tab-more" @click="allMore">
<image src="/static/images/common/more.png" mode="aspectFit" class="more"></image>
</view>
</view>
<view class="sendFlex" v-if="activeTab && activeTab.id !== '' && activeTabInfo.communicationMode == 0">
<view class="callpolice" @click="callpolice">报警</view>
<view class="Sendmessage" @click="location">位置</view>
<view class="Sendmessage" @click="handleSend">发送信息</view>
</view>
<mescroll-uni v-if="deviceList.length > 0" class="device-list" @init="mescrollInit" @down="downCallback"
@up="upCallback" :up="upOption" :down="downOption" :fixed="false"
:style="{ height: mescrollHeight + 'px' }">
<uni-swipe-action ref="swipeAction">
<block v-for="(item, index) in deviceList" :key="index" :ref="'swipeItem_' + index">
<uni-swipe-action-item :right-options="Options" @click="handleSwipeClick($event, item, index)"
class="device-card"
:style="{ border: item.communicationMode == 0 && item.onlineStatus == 1 && item.alarmStatus == 1 ? '1px solid rgba(224, 52, 52, 1)' : 'none' }">
<view @click.stop="handleFile(item)">
<view class="device-header">
<view class="deviceIMG">
<image :src="item.devicePic" class="IMG" mode="aspectFit"></image>
</view>
<view class="device-name">
<view>设备:{{ item.deviceName }}</view>
<view class="ID">
<view class="ID" v-if="item.communicationMode == 0">ID:{{
item.deviceImei }}
</view>
<view class="ID" v-else>ID:{{ item.deviceMac }}</view>
<!-- 在线状态 -->
<view class="onlines"
v-if="item.communicationMode == 0 && item.onlineStatus == 1">在线
</view>
<!-- 离线状态 -->
<view class="offlines"
v-if="item.communicationMode == 0 && item.onlineStatus == 0">离线
</view>
<view class="offlines"
v-if="item.communicationMode == 0 && item.onlineStatus == 2">故障
</view>
<view>电量{{ item.battery || '0' }}%</view>
</view>
</view>
</view>
<view class="device-callpolice"
v-if="item.communicationMode == 0 && item.onlineStatus == 1 && item.alarmStatus == 1">
报警中</view>
<view v-if="item.communicationMode == 1">
<view class="device-status online">已连接</view>
<view class="device-status unline">未连接</view>
</view>
</view>
<image src="/static/images/common/cires.png" class="circle" mode="aspectFit"></image>
</uni-swipe-action-item>
</block>
</uni-swipe-action>
<!-- 加载状态提示 -->
<view class="loading-status">
<text v-if="loading">加载中...</text>
<text v-if="finished">没有更多数据了</text>
</view>
</mescroll-uni>
<view v-else class="noDATA">
<view> <uni-icons type="image-filled" size="120" color="rgba(255, 255, 255, 0.9)"></uni-icons>
</view>
暂无数据
</view>
</view>
<!-- 删除弹框 -->
<view class="agreement-mask" v-if="deleteShow" @click="closePopup('delete')" catchtouchmove="true">
<view class="agreement-popupC" @click.stop>
<view class="popup-content">
<image src="/static/images/common/dell.png" mode="" class="svg"></image>
<uni-icon class="trash"></uni-icon>
<view>
<view class="popup-Title">确定删除所选设备</view>
</view>
</view>
<!-- 按钮组 -->
<view class="popup-buttons">
<button class="btn agreeBtn" @click="handleBtn">确定</button>
</view>
</view>
</view>
<!-- =========重命名============== -->
<view class="agreement-mask" v-if="RenameModel" @click="closePopup('rename')" catchtouchmove="true">
<view class="agreement-popupD" @click.stop>
<view class="popup-content">
<view>
<view class="popup-flex">
<text>设备名称</text>
<input type="text" v-model="deviceName" placeholder="请输入设备名称" class="popup-input"
@click.stop />
</view>
</view>
</view>
<!-- 按钮组 -->
<view class="popup-buttons" style="margin-top:50rpx;">
<button class="btn agreeBtn4" @click="handleBtnName">确定</button>
</view>
</view>
</view>
<!-- 小提示框 -->
<view class="tooltip-box" v-if="showTooltip" @click="closePopupTooltip" catchtouchmove="true">
<view class="tooltip-arrow"></view>
<view class="tooltip-content" @click.stop>
<view class="tooltip-item" v-for="(item, index) in menuItems" :key="index"
@click="handleMenuClick(item)">
<image :src="item.icon" class="item-icon" />
<text>{{ item.text }}</text>
</view>
</view>
</view>
<!-- ====分享类型提示框==== -->
<view class="tooltip-share" v-if="showshare" @click="closePopupTooltip" catchtouchmove="true">
<view class="tooltip-arrow"></view>
<view class="tooltip-content" @click.stop>
<view class="tooltip-item" v-for="(item, index) in shareItems" :key="index"
@click="handleshareClick(item)">
<image :src="item.icon" class="item-icon" />
<text>{{ item.text }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import {
deviceTypeList,
deviceInfo,
deviceUnbind, //删除设备
deviceReName
} from '@/api/common/index.js'
import BleHelper from '@/utils/BleHelper.js';
import MescrollUni from 'mescroll-uni/mescroll-uni.vue'
var ble = null;
export default {
components: {
MescrollUni
},
data() {
return {
navBarHeight: 70 + uni.getSystemInfoSync().statusBarHeight,
deviceList: [],
tabs: [],
activeTab: 0,
showTooltip: false,
showshare: false,
Options: [{
text: '重命名',
style: {
backgroundColor: '#E09319',
borderRadius: '16px',
width: '240rpx', // 初始宽度
},
},
{
text: '删除',
style: {
backgroundColor: 'rgb(240, 60, 60)',
borderRadius: '16px',
width: '240rpx', // 初始宽度
},
},
],
navTitle: "我的设备",
deleteShow: false,
RenameModel: false,
menuItems: [{
text: '扫一扫添加',
icon: '/static/images/common/scane.png',
action: 'scan'
},
{
text: '蓝牙添加',
icon: '/static/images/common/bluetooth.png',
action: 'bluetooth'
}
],
shareItems: [{
text: '所有类型',
icon: '/static/images/common/type.png',
action: 'type'
},
{
text: '所有分享',
icon: '/static/images/common/share.png',
action: 'share'
}
],
mescroll: null,
downOption: {
auto: false
},
upOption: {
auto: false,
noMoreSize: 0,
offset: 50,
isLock: false,
empty: {
tip: '暂无数据',
hideScroll: false
}
},
page: 1, // 当前页码
size: 12, // 每页条数
total: 0, // 总数据量
loadedCount: 0,
loading: false,
finished: false,
deviceId: '',
deviceName: "", //重命名
activeTabInfo: '',
mescrollHeight: 0,
}
},
methods: {
mescrollInit(mescroll) {
this.mescroll = mescroll;
},
// 下拉刷新
downCallback() {
const currentDeviceType = this.activeTabInfo?.id === '' ? undefined : this.activeTabInfo?.id;
const tempList = [...this.deviceList];
// 重置分页参数
this.page = 1;
this.finished = false;
this.loadedCount = 0;
this.total = 0; // 重置总数
this.getData(currentDeviceType)
.then(() => {
this.mescroll.endDownScroll(true);
})
.catch(() => {
this.deviceList = tempList;
this.mescroll.endDownScroll(false);
});
},
// 上拉加载
upCallback() {
if (this.loading) {
this.mescroll.endUpScroll(false);
return;
}
const currentDeviceType = this.activeTabInfo?.id === '' ? undefined : this.activeTabInfo?.id;
this.getData(currentDeviceType)
.then(() => {
// 如果本次加载的数据量为0则说明没有更多数据
const hasMore = this.deviceList.length > 0 && this.deviceList.length % this.size === 0;
this.mescroll.endUpScroll(hasMore);
})
.catch(() => {
this.page--;
this.mescroll.endUpScroll(false);
});
},
// 更多
allMore() {
this.showshare = !this.showshare;
},
// 所有分享,所有类型
handleshareClick(item) {
this.showshare = false;
switch (item.action) {
case 'type':
uni.navigateTo({
url: '/pages/common/allType/index'
});
break;
case 'share':
uni.navigateTo({
url: "/pages/common/allShare/index"
})
break;
}
},
// 点击弹框外的区域关闭
closePopup(type) {
if (type === 'delete') {
this.deleteShow = false;
uni.showTabBar();
} else if (type === 'rename') {
this.RenameModel = false;
uni.showTabBar();
}
},
// tab导航切换栏
getTab() {
deviceTypeList({}).then((res) => {
if (res.code == 200) {
this.tabs = [{
id: '',
name: '全部设备',
typeName: '全部设备'
},
...res.data.map(item => ({
id: item.id,
name: item.typeName,
typeName: item.typeName,
communicationMode: item.communicationMode
}))
];
}
})
},
// tab切换页
switchTab(tab, index) {
this.deviceList = [];
this.activeTab = index;
this.activeTabInfo = tab;
// 完全重置分页状态
this.page = 1;
this.finished = false;
this.loadedCount = 0;
this.total = 0; // 重置总数
const deviceType = tab.id === '' ? undefined : tab.id;
this.$nextTick(() => {
this.getSystemInfoSyncH();
});
this.getData(deviceType);
if (this.mescroll) {
this.mescroll.resetUpScroll();
}
},
// 获取设备列表
getData(deviceType = '') {
return new Promise((resolve, reject) => {
if (this.loading || this.finished) {
reject('正在加载或已无更多数据');
return;
}
this.loading = true;
let data = {
pageNum: this.page,
pageSize: this.size,
deviceType: deviceType
}
deviceInfo(data).then((res) => {
if (res.code == 200) {
const newDevices = res.rows.map(device => ({
...device,
showConfirm: false
}));
if (this.page === 1) {
this.total = Number(res.total) || 0;
this.deviceList = newDevices;
this.loadedCount = newDevices.length;
} else {
this.deviceList = [...this.deviceList, ...newDevices];
this.loadedCount += newDevices.length;
}
const hasMoreData = this.loadedCount < this.total;
if (!hasMoreData) {
this.finished = true;
}
if (hasMoreData) {
this.page++;
}
resolve();
} else {
reject(res.msg || '获取数据失败');
}
}).catch((err) => {
reject(err);
}).finally(() => {
this.loading = false;
});
});
},
// 添加扫一扫图标
scan() {
this.showTooltip = !this.showTooltip;
},
closePopupTooltip() {
this.showTooltip = !this.showTooltip
this.showshare = !this.showshare
},
// 添加设备,扫一扫,蓝牙
handleMenuClick(item) {
this.showTooltip = false;
switch (item.action) {
case 'scan':
uni.scanCode({
success: (res) => {
const cleanedResult = res.result.trim();
uni.navigateTo({
url: `/pages/common/qrcode/qrcode?deviceId=${encodeURIComponent(cleanedResult)}`
});
},
fail: (err) => {
console.log('扫码失败', err);
uni.showToast({
title: '扫码失败',
icon: 'none'
});
}
});
break;
case 'bluetooth':
uni.navigateTo({
url: "/pages/common/addBLE/addEquip?search=all"
})
break;
}
},
// 右滑点击事件处理
handleSwipeClick(e, item, index) {
switch (e.content.text) {
case '删除':
this.handleDeleteDevice(item, index)
break
case '重命名':
this.handleRenameDevice(item, index)
break
};
},
// 删除设备
handleDeleteDevice(item, index) {
this.deviceId = item
this.deleteShow = true
uni.hideTabBar()
},
// 确认删除
handleBtn() {
uni.showTabBar()
deviceUnbind(this.deviceId.id).then((res) => {
if (res.code == 200) {
uni.showToast({
title: '删除成功',
icon: 'none',
duration: 1000
});
setTimeout(() => {
this.onIntall();
this.getTab()
}, 500);
this.deleteShow = false
if (this.$refs.swipeAction) {
this.$refs.swipeAction.closeAll();
}
console.log("111111",this.deviceId);
console.log("ble==null,",ble)
ble && ble.DropDevice(this.deviceId.id);
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
});
}
})
},
// 重命名设备
handleRenameDevice(item, index) {
this.RenameModel = true
uni.hideTabBar()
this.deviceId = item
},
handleBtnName() {
uni.showTabBar()
deviceReName({
id: this.deviceId.id,
deviceName: this.deviceName
}).then((res) => {
if (res.code == 200) {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
});
setTimeout(() => {
this.onIntall();
}, 500);
this.RenameModel = false
this.deviceName = ''
if (this.$refs.swipeAction) {
this.$refs.swipeAction.closeAll();
}
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
});
}
})
},
// 报警
callpolice() {
const currentTab = this.tabs[this.activeTab];
uni.navigateTo({
url: '/pages/6170/callPolice/index',
success: (res) => {
res.eventChannel.emit('devicePolice', {
data: currentTab
});
}
})
},
// 发生短信
handleSend() {
const currentTab = this.tabs[this.activeTab];
uni.navigateTo({
url: '/pages/common/send/index',
success: (res) => {
res.eventChannel.emit('deviceSend', {
data: currentTab
});
}
})
},
// 位置
location() {
uni.navigateTo({
url: '/pages/common/map/index',
success: (res) => {
res.eventChannel.emit('Map', {
data: this.deviceList,
});
}
})
},
// 列表跳转
handleFile(item) {
let url = item.detailPageUrl;
uni.navigateTo({
url: url,
success: (res) => {
res.eventChannel.emit('detailData', {
data: item,
deviceType: this.tabs[this.activeTab].id || '',
apiType: 'listA'
});
},
fail(ex) {
console.log("ex=", ex);
}
})
},
onIntall() {
this.page = 1;
this.finished = false;
this.loadedCount = 0;
this.total = 0;
const deviceType = this.activeTabInfo?.id === '' ? undefined : this.activeTabInfo?.id;
this.getData(deviceType);
setTimeout(() => {
uni.stopPullDownRefresh();
}, 800);
},
updateDeviceStatus(data) {
this.deviceList = this.deviceList
.map(item => {
if (!item) return null;
if (item.communicationMode == 0) {
let messageData;
try {
messageData = data.message;
} catch (e) {
return item;
}
const [deviceId, onlineStatus, battery] = messageData.state || [];
return {
...item,
battery: battery ?? item.battery,
onlineStatus: onlineStatus ?? item.onlineStatus,
lastUpdate: data.timestamp,
};
}
return item;
})
.filter(Boolean);
},
// 动态计算屏幕高度
getSystemInfoSyncH() {
const sysInfo = uni.getSystemInfoSync();
const tabBarHeight = 80;
const sendBarHeight = 60;
const padding = 60;
const totalTopHeight = (this.navBarHeight) + (tabBarHeight + sendBarHeight + padding) * (sysInfo
.screenWidth / 750);
this.mescrollHeight = sysInfo.screenHeight - totalTopHeight;
}
},
onLoad() {
this.getSystemInfoSyncH()
this.getTab()
this.onIntall()
uni.$on('refreshDeviceList', () => {
this.getTab()
this.onIntall()
});
uni.$on('deviceStatusUpdate', (data) => {
this.onIntall()
});
ble = BleHelper.getBleTool();
},
beforeDestroy() {
uni.$off('refreshDeviceList');
},
onUnload() {
uni.$off('deviceStatusUpdate');
}
}
</script>
<style>
/* 保持原有样式不变 */
.device-page {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: rgb(18, 18, 18);
padding: 30rpx;
}
.tab-bar {
width: 100%;
color: rgb(255, 255, 255);
white-space: nowrap;
overflow: hidden;
position: relative;
}
.tab-container {
display: flex;
cursor: pointer;
margin-bottom: 40rpx;
padding-right: 80rpx;
}
.tab-item {
font-size: 28rpx;
padding: 0 30rpx;
text-align: center;
}
.active {
color: rgba(187, 230, 0, 1);
border-bottom: 6rpx solid rgba(187, 230, 0, 1);
height: 60rpx;
}
.sendFlex {
display: flex;
color: rgba(255, 255, 255, 0.87);
justify-content: flex-end;
cursor: pointer;
margin-bottom: 30rpx;
font-size: 28rpx;
}
.tab-bar-wrap {
display: flex;
align-items: baseline;
position: relative;
}
.tab-more {
margin-left: 10rpx;
display: flex;
align-items: center;
background: linear-gradient(-88.60deg, rgba(18, 18, 18, 1), rgba(18, 18, 18, 0) 100%);
}
.more {
width: 40rpx;
height: 8rpx;
}
.Sendmessage {
margin-left: 50rpx;
color: rgba(255, 255, 255, 0.87);
}
.callpolice {
color: rgba(224, 52, 52, 1);
}
/* 设备卡片 */
.device-card {
background-color: rgb(26, 26, 26);
border-radius: 16rpx;
margin-bottom: 20rpx;
box-sizing: border-box;
position: relative;
}
.device-header {
display: flex;
align-items: center;
margin-bottom: 15rpx;
padding: 30rpx 0 10rpx 30rpx;
}
.unline {
color: rgba(255, 255, 255, 0.4);
}
.device-name {
font-size: 32rpx;
color: rgba(255, 255, 255, 0.87);
margin-left: 24rpx;
line-height: 50rpx;
width: 75%;
white-space: nowrap;
}
.ID {
color: rgba(255, 255, 255, 0.6);
font-size: 26rpx;
display: flex;
justify-content: space-between;
position: relative;
}
.device-callpolice {
width: 122rpx;
height: 52rpx;
font-size: 24rpx;
border-radius: 0px 8px 0px 8px;
background-color: rgba(224, 52, 52, 1);
position: absolute;
top: 0rpx;
right: -4rpx;
text-align: center;
line-height: 52rpx;
color: #fff;
}
.device-status {
width: 122rpx;
height: 52rpx;
font-size: 26rpx;
border-radius: 0px 8px 0px 8px;
background-color: rgb(42, 42, 42);
position: absolute;
top: 0rpx;
right: 0rpx;
text-align: center;
line-height: 52rpx;
}
.circle {
width: 8rpx;
height: 40rpx;
position: absolute;
right: 25rpx;
top: 60rpx;
}
.online {
color: rgb(187, 230, 0);
}
.deviceIMG {
width: 100rpx;
height: 100rpx;
border-radius: 16rpx;
position: relative;
background-color: rgba(42, 42, 42, 0.6);
display: flex;
align-items: center;
}
.IMG {
width: 68rpx;
height: 50rpx;
margin-left: 17%;
object-fit: contain;
}
.onlines {
position: relative;
}
.onlines::before {
content: '';
position: absolute;
width: 15rpx;
height: 15rpx;
background: rgb(0, 171, 103);
border-radius: 50%;
top: 20rpx;
left: -20rpx
}
.offlines {
position: relative;
}
.offlines::before {
content: '';
position: absolute;
width: 15rpx;
height: 15rpx;
background: rgba(255, 255, 255, 0.4);
border-radius: 50%;
top: 20rpx;
left: -20rpx
}
.loading-status {
text-align: center;
color: rgba(255, 255, 255, 0.6);
padding: 20rpx;
font-size: 22rpx;
}
.noDATA {
text-align: center;
color: rgba(255, 255, 255, 0.87);
transform: translate(-0%, 100%);
}
/* 遮罩层 */
.agreement-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.popup-Title {
color: rgba(255, 255, 255, 0.86);
text-align: center;
padding: 30rpx 0rpx;
}
.popup-buttons {
display: flex;
text-align: center;
}
.agreement-popupC {
width: 60%;
background-color: rgb(42, 42, 42);
border-radius: 40rpx;
padding: 30rpx;
text-align: center;
border: 1px solid rgba(255, 200, 78, 0.3);
}
.agreement-popupD {
width: 70%;
background-color: rgb(42, 42, 42);
border-radius: 40rpx;
padding: 40rpx;
text-align: center;
border: 1px solid rgba(187, 230, 0, 0.3);
}
.popup-flex {
display: flex;
white-space: nowrap;
color: rgba(255, 255, 255, 0.87);
height: 50rpx;
padding: 30rpx;
align-items: center;
}
.popup-input {
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 12rpx;
margin-left: 15rpx;
padding: 10rpx 0rpx;
font-size: 28rpx;
}
.svg {
width: 58rpx;
height: 62rpx;
}
/* 通用按钮样式 */
.btn {
height: 60rpx;
line-height: 60rpx;
border-radius: 40rpx;
font-size: 24rpx;
margin: 10rpx auto;
text-align: center;
}
.agreeBtn {
background: #FFC84E;
color: #232323;
border: none;
width: 170rpx !important;
}
.agreeBtn4 {
background: rgba(187, 230, 0, 1);
color: #232323;
border: none;
width: 170rpx !important;
}
.closeBtn {
border: 1px solid rgba(255, 255, 255, 0.2);
background-color: rgba(35, 35, 35, 0.87);
color: rgba(255, 255, 255, 1);
}
/* 提示框样式 */
.tooltip-box {
position: fixed;
right: 18rpx;
top: 140rpx;
z-index: 9999;
}
.tooltip-share {
position: fixed;
right: 18rpx;
top: 230rpx;
z-index: 9999;
}
.tooltip-arrow {
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid #333;
position: absolute;
right: 12px;
top: -8px;
}
.tooltip-content {
border-radius: 8rpx;
backdrop-filter: blur(14px);
background: rgba(58, 58, 58, 1);
padding: 10px 0;
min-width: 120px;
}
.tooltip-item {
padding: 8px 16px;
display: flex;
align-items: center;
color: #fff;
}
.tooltip-item text {
margin-left: 8px;
font-size: 14px;
}
.item-icon {
width: 16px;
height: 16px;
}
</style>