Files
APP/pages/common/index/index.vue
2025-08-28 14:05:06 +08:00

978 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>
<scroll-view class="device-list" scroll-y @scrolltolower="onScrollToLower" :lower-threshold="100"
style="height:80vh;">
<view v-if="deviceList.length>0">
<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>电量{{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>
</view>
<view v-else class="noDATA">
<view> <uni-icons type="image-filled" size="120" color="rgba(255, 255, 255, 0.9)"></uni-icons>
</view>
暂无数据
</view>
</scroll-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'
export default {
onPullDownRefresh() {
// 执行下拉刷新时的操作,比如重新获取数据
this.onIntall();
},
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'
}
],
page: 1, // 当前页码
size: 10, // 每页条数
total: 0, // 总数据量
loading: false,
finished: false,
deviceId: '',
deviceName: "", //重命名
activeTabInfo: ''
}
},
methods: {
// 更多
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/6170/allShare/index"
})
break;
}
},
// 点击弹框外的区域关闭
closePopup(type) {
if (type === 'delete') {
this.deleteShow = false;
uni.showTabBar(); // 显示TabBar
} else if (type === 'rename') {
this.RenameModel = false;
uni.showTabBar(); // 显示TabBar
}
},
// tab导航切换栏
getTab() {
deviceTypeList({}).then((res) => {
if (res.code == 200) {
//console.log("deviceTypeList=" + JSON.stringify(res.data));
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; // 重
// 明确传递参数空字符串改为null或undefined
const deviceType = tab.id === '' ? undefined : tab.id;
this.getData(deviceType);
},
// 获取设备列表
getData(deviceType = '') {
if (this.loading || this.finished) 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
}));
// 如果是第一页或切换分类,替换数据
this.deviceList = this.page === 1 ? newDevices : [...this.deviceList, ...newDevices];
this.total = res.total;
// 判断是否加载完成
if (res.rows.length < this.size || this.deviceList.length >= this.total) {
this.finished = true;
} else {
this.page++;
}
}
}).finally(() => {
this.loading = false;
});
},
// 滚动触底事件处理
onScrollToLower() {
this.getData();
},
// 添加扫一扫图标
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) => {
console.log('条码内容:', res);
// 清除之前的数据
this.previousScanResult = null;
// 处理新的扫码结果
const cleanedResult = res.result.trim();
console.log('扫码结果:', cleanedResult);
// 跳转并传递扫描结果
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"
})
break;
}
},
// 右滑点击事件处理
handleSwipeClick(e, item, index) {
const {
content
} = e
console.log(item, 'eeeee');
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()
let data = {
id: this.deviceId.id
}
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
// 关闭所有滑动项
this.$refs.swipeAction.closeAll();
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
});
}
})
},
// 重命名设备
handleRenameDevice(item, index) {
this.RenameModel = true
uni.hideTabBar()
this.deviceId = item
},
handleBtnName() {
uni.showTabBar()
let data = {
id: this.deviceId.id,
deviceName: this.deviceName
}
deviceReName(data).then((res) => {
if (res.code == 200) {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
});
setTimeout(() => {
this.onIntall();
}, 500);
this.RenameModel = false
this.deviceName = ''
// 关闭所有滑动项
this.$refs.swipeAction.closeAll();
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
});
}
})
},
// 报警
callpolice() {
const currentTab = this.tabs[this.activeTab];
const deviceType = currentTab.id || '';
console.log(`跳转到发送信息页面\n当前设备类型: ${deviceType}\n设备类型名称: ${currentTab.typeName}`);
uni.navigateTo({
url: '/pages/6170/callPolice/index',
events: {
ack: function(data) {}
},
success: (res) => {
res.eventChannel.emit('devicePolice', {
data: currentTab
});
}
})
},
// 发生短信
handleSend() {
const currentTab = this.tabs[this.activeTab];
const deviceType = currentTab.id || '';
console.log(`跳转到发送信息页面\n当前设备类型: ${deviceType}\n设备类型名称: ${currentTab.typeName}`);
uni.navigateTo({
url: '/pages/common/send/index',
events: {
ack: function(data) {}
},
success: (res) => {
res.eventChannel.emit('deviceSend', {
//data: deviceType,
data: currentTab
});
}
})
},
// 位置
location() {
uni.navigateTo({
url: '/pages/common/map/index',
events: {
ack: function(data) {}
},
success: (res) => {
// 页面跳转成功后的回调函数
res.eventChannel.emit('Map', {
data: this.deviceList,
});
}
})
},
// 列表跳转
handleFile(item) {
let url = item.detailPageUrl;
// console.log("url=",url);
// if(!url){
//url="/pages/670/HBY670"
// }
uni.navigateTo({
url: url,
events: {
ack: function(data) {}
},
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;
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; // 如果 item 是 undefined/null返回 null
if (item.communicationMode == 0) {
let messageData;
try {
messageData = data.message;
} catch (e) {
return item; // 解析失败则返回原 item
}
const [deviceId, onlineStatus, battery] = messageData.state || [];
console.log('我收到消息了没', item.battery);
return {
...item,
battery: battery ?? item.battery,
onlineStatus: onlineStatus ?? item.onlineStatus,
lastUpdate: data.timestamp,
};
}
return item;
})
.filter(Boolean);
},
},
onLoad() {
this.getTab()
this.onIntall()
// 绑定页面做了监听,新增成功,刷新页面
uni.$on('refreshDeviceList', () => {
this.getTab() // 刷新数据
this.onIntall()
});
// 监听设备状态更新事件
uni.$on('deviceStatusUpdate', (data) => {
console.log('列表收到消息了么');
this.onIntall()
});
},
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;
/* min-width: 120rpx; */
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;
/* 可选(若需要绝对定位 fallback */
}
.tab-more {
margin-left: 10rpx;
/* 与Tab的间距 */
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);
}
.device-id {
font-size: 26rpx;
color: #999;
margin-bottom: 20rpx;
display: block;
}
.device-info {
display: flex;
justify-content: space-evenly;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.87);
position: relative;
padding: 0rpx 0rpx 30rpx 30rpx;
}
.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
}
.line {
width: 2rpx;
height: 24rpx;
background: linear-gradient(90deg,
rgba(0, 0, 0, 0) 0%,
rgb(255, 255, 255) 50%,
rgba(255, 255, 255, 0) 100%);
margin-top: 12rpx;
}
.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-popup {
width: 100%;
height: 50%;
background-color: rgb(42, 42, 42);
border-radius: 60rpx 60rpx 0rpx 0rpx;
padding: 40rpx;
box-sizing: border-box;
position: absolute;
bottom: 0rpx;
}
.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>