524 lines
18 KiB
Vue
524 lines
18 KiB
Vue
<template>
|
||
<view class="page-container"
|
||
@touchstart="handleTouchStart"
|
||
@touchmove="handleTouchMove"
|
||
@touchend="handleTouchEnd">
|
||
<!-- 顶部栏空间切换 -->
|
||
<CustomHeader
|
||
:title="currentPageInfo.title"
|
||
:subtitle="currentPageInfo.subtitle"
|
||
:spaces="spaceOptions"
|
||
:selectedIndex="selectedSpaceIndex"
|
||
@space-change="handleSpaceChange"
|
||
@toggle-side-menu="toggleSideMenu"
|
||
/>
|
||
<!-- 加载遮罩:请求空间数据时覆盖页面 -->
|
||
<view class="loading-overlay" v-if="isLoadingSpaces">
|
||
<view class="spinner"></view>
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<!-- 页面内容区域 -->
|
||
<view class="page-content">
|
||
<!-- 设备模块 -->
|
||
<DeviceTab v-if="currentTabIndex === 0" />
|
||
|
||
<!-- 房间模块 -->
|
||
<RoomTab v-if="currentTabIndex === 1" />
|
||
|
||
<!-- 通知模块 -->
|
||
<NotificationTab v-if="currentTabIndex === 2" ref="notificationTab" />
|
||
|
||
<!-- 控制模块 -->
|
||
<ControlTab v-if="currentTabIndex === 3" :statusTitle="currentStatusTitle" @status-changed="handleStatusChanged" />
|
||
|
||
</view>
|
||
|
||
<!-- 底部导航 -->
|
||
<BottomTab
|
||
:currentIndex="currentTabIndex"
|
||
@tab-change="handleTabChange"
|
||
/>
|
||
<!-- 侧边菜单 -->
|
||
<SideMenu
|
||
:visible="showSideMenu"
|
||
:logoflag="logoflag"
|
||
@close="closeSideMenu"
|
||
@menu-click="handleMenuClick"
|
||
@add-room="handleAddRoom"
|
||
/>
|
||
<!-- 无空间展示 -->
|
||
<view class="" v-if="!isLoadingSpaces && spaceOptions.length == 0" style="background-color: #F3F5F6;position: fixed;top: 168rpx;left: 0;width: 100%;height: 100vh;z-index: 999;text-align: center;">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/uKFO4XdQVUgGYJHt4gwn" style="width: 272rpx;height: 272rpx;margin-top: 80rpx;" mode="aspectFill"></image>
|
||
<view class="" style="font-weight: 600;font-size: 36rpx;margin-top: 70rpx;color: #3D3D3D;">
|
||
{{ $i18n.t('emptySpaceTitle') }}
|
||
</view>
|
||
<view class="" style="width: 524rpx;margin-top: 30rpx;font-size: 28rpx;color: #727272;margin: auto;">
|
||
{{ $i18n.t('emptySpaceDesc') }}
|
||
</view>
|
||
<view class="" @click="btnaddkj" style="width: 674rpx;height: 100rpx;background: #0F0F0F;border-radius: 60rpx 60rpx 60rpx 60rpx;border: 2rpx solid #0F0F0F;font-size: 36rpx;color: #fff;font-weight: 600;text-align: center;line-height: 100rpx;margin: auto;margin-top: 70rpx;">
|
||
+ {{ $i18n.t('addSpace') || $i18n.t('emptySpaceCta') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import CustomHeader from '@/common/components/CustomHeader.vue'
|
||
import BottomTab from '@/common/components/BottomTab.vue'
|
||
import SideMenu from '@/common/components/SideMenu.vue'
|
||
import DeviceTab from '@/common/components/DeviceTab.vue'
|
||
import RoomTab from '@/common/components/RoomTab.vue'
|
||
import NotificationTab from '@/common/components/NotificationTab.vue'
|
||
import ControlTab from '@/common/components/ControlTab.vue'
|
||
export default {
|
||
components: {
|
||
CustomHeader,
|
||
BottomTab,
|
||
SideMenu,
|
||
DeviceTab,
|
||
RoomTab,
|
||
NotificationTab,
|
||
ControlTab
|
||
},
|
||
data() {
|
||
return {
|
||
currentTabIndex: 0, // 当前选中的标签页索引
|
||
showSideMenu: false, // 控制侧边菜单的显示状态
|
||
// 加载状态
|
||
isLoadingSpaces: true,
|
||
// 添加页面配置数据
|
||
pageConfigs: [],
|
||
// 空间下拉
|
||
spaceOptions: [
|
||
{ name: 'home', status: '2' }, // 撤防
|
||
{ name: 'office', status: '1' }, // 布防
|
||
{ name: 'warehouse', status: '3' }, // 夜间
|
||
{ name: 'garage', status: '4' } // 紧急
|
||
],
|
||
selectedSpaceIndex: 0,
|
||
// 添加触摸相关数据
|
||
touchStartX: 0,
|
||
touchStartY: 0,
|
||
touchEndX: 0,
|
||
touchEndY: 0,
|
||
minSwipeDistance: 50, // 最小滑动距离
|
||
maxVerticalDistance: 100, // 最大垂直距离(防止误触)
|
||
touchStartTime: 0, // 触摸开始时间
|
||
maxSwipeTime: 500, // 最大滑动时间(毫秒)
|
||
lastSwipeTime: 0, // 上次手势时间
|
||
swipeDebounceTime: 300 ,// 手势防抖时间(毫秒)
|
||
logoflag:false
|
||
}
|
||
},
|
||
computed: {
|
||
// 计算当前语言,用于触发更新
|
||
currentLanguage() {
|
||
return this.$i18n.getCurrentLanguage()
|
||
},
|
||
currentPageInfo() {
|
||
return this.pageConfigs[0] || { title: '', subtitle: '' }
|
||
},
|
||
// 当前选中空间的状态标题(1-布防 2-撤防 3-夜间 4-紧急)
|
||
currentStatusTitle() {
|
||
const spaces = Array.isArray(this.spaceOptions) ? this.spaceOptions : []
|
||
const index = Math.min(Math.max(0, this.selectedSpaceIndex || 0), spaces.length - 1)
|
||
const status = spaces[index] && (spaces[index].status || spaces[index].Status || spaces[index].state)
|
||
return this.getStatusTitleByCode(status)
|
||
}
|
||
},
|
||
watch: {
|
||
// 监听语言变化,强制更新组件
|
||
currentLanguage() {
|
||
console.log('主页面语言变化:', this.currentLanguage)
|
||
this.updatePageConfigs()
|
||
this.updateSpaceOptions()
|
||
}
|
||
},
|
||
mounted() {
|
||
// 监听语言变化事件
|
||
uni.$on('languageChanged', this.handleLanguageChange)
|
||
this.updatePageConfigs()
|
||
// this.updateSpaceOptions();
|
||
},
|
||
beforeDestroy() {
|
||
// 移除事件监听
|
||
uni.$off('languageChanged', this.handleLanguageChange)
|
||
},
|
||
onLoad() {
|
||
console.log('页面加载完成');
|
||
if(!uni.getStorageSync('language')){
|
||
uni.setStorageSync('language','zh')
|
||
}
|
||
},
|
||
onShow() {
|
||
// 页面显示时强制更新
|
||
this.updatePageConfigs()
|
||
this.updateSpaceOptions()
|
||
},
|
||
methods: {
|
||
// 点击跳转添加空间
|
||
btnaddkj(){
|
||
if(this.logoflag == true){
|
||
uni.reLaunch({
|
||
url:'/pages/login/index'
|
||
})
|
||
}else{
|
||
uni.navigateTo({
|
||
url:'/pages/kongjian/index'
|
||
})
|
||
}
|
||
},
|
||
// 触摸开始事件
|
||
handleTouchStart(e) {
|
||
// 在通知页面时,只有高亮索引为0时才启用手势
|
||
if (this.currentTabIndex === 2) {
|
||
// 获取通知页面的高亮索引
|
||
const notificationTab = this.$refs.notificationTab
|
||
if (notificationTab && notificationTab.swiperCurrent !== 0) {
|
||
return; // 高亮索引不是0时,禁用手势
|
||
}
|
||
}
|
||
// 记录触摸开始位置,无论侧边菜单是否显示
|
||
this.touchStartX = e.touches[0].clientX
|
||
this.touchStartY = e.touches[0].clientY
|
||
this.touchStartTime = Date.now() // 记录触摸开始时间
|
||
},
|
||
// 触摸移动事件
|
||
handleTouchMove(e) {
|
||
// 在通知页面时,只有高亮索引为0时才启用手势
|
||
if (this.currentTabIndex === 2) {
|
||
// 获取通知页面的高亮索引
|
||
const notificationTab = this.$refs.notificationTab
|
||
if (notificationTab && notificationTab.swiperCurrent !== 0) {
|
||
return; // 高亮索引不是0时,禁用手势
|
||
}
|
||
}
|
||
// 可以在这里添加实时反馈,比如显示滑动提示
|
||
// 暂时保持空实现,避免影响性能
|
||
},
|
||
// 触摸结束事件
|
||
handleTouchEnd(e) {
|
||
// 在通知页面时,只有高亮索引为0时才启用手势
|
||
if (this.currentTabIndex === 2) {
|
||
// 获取通知页面的高亮索引
|
||
const notificationTab = this.$refs.notificationTab
|
||
if (notificationTab && notificationTab.swiperCurrent !== 0) {
|
||
return // 高亮索引不是0时,禁用手势
|
||
}
|
||
}
|
||
const touchEndTime = Date.now()
|
||
const deltaTime = touchEndTime - this.touchStartTime
|
||
// 防抖检查:避免快速连续触发
|
||
if (touchEndTime - this.lastSwipeTime < this.swipeDebounceTime) {
|
||
return
|
||
}
|
||
this.touchEndX = e.changedTouches[0].clientX
|
||
this.touchEndY = e.changedTouches[0].clientY
|
||
// 计算滑动距离
|
||
const deltaX = this.touchEndX - this.touchStartX
|
||
const deltaY = Math.abs(this.touchEndY - this.touchStartY)
|
||
// 判断是否为有效的左滑手势(隐藏侧边菜单)
|
||
if (this.showSideMenu && deltaX < -this.minSwipeDistance && deltaY < this.maxVerticalDistance && deltaTime < this.maxSwipeTime) {
|
||
console.log('检测到左滑手势,隐藏侧边菜单')
|
||
this.closeSideMenu()
|
||
this.lastSwipeTime = touchEndTime // 更新上次手势时间
|
||
// 可选:添加触觉反馈
|
||
uni.vibrateShort({
|
||
type: 'light'
|
||
})
|
||
}
|
||
// 判断是否为有效的右滑手势(打开侧边菜单)
|
||
else if (!this.showSideMenu && deltaX > this.minSwipeDistance && deltaY < this.maxVerticalDistance && deltaTime < this.maxSwipeTime) {
|
||
console.log('检测到右滑手势,打开侧边菜单')
|
||
this.toggleSideMenu()
|
||
this.lastSwipeTime = touchEndTime // 更新上次手势时间
|
||
// 可选:添加触觉反馈
|
||
uni.vibrateShort({
|
||
type: 'light'
|
||
})
|
||
}
|
||
},
|
||
|
||
handleLanguageChange(lang) {
|
||
console.log('主页面语言切换事件触发:', lang)
|
||
this.updatePageConfigs()
|
||
this.updateSpaceOptions()
|
||
},
|
||
// 将状态码映射为标题
|
||
getStatusTitleByCode(code) {
|
||
const n = Number(code)
|
||
// 添加容错处理
|
||
const t = this.$i18n && this.$i18n.t ? this.$i18n.t.bind(this.$i18n) : (key) => key
|
||
if (n === 1) return t('statusArmed') || '布防'
|
||
if (n === 2) return t('statusDisarmed') || '撤防'
|
||
if (n === 3) return t('statusNight') || '夜间'
|
||
if (n === 4) return t('statusEmergency') || '紧急'
|
||
return t('work') || '工作'
|
||
},
|
||
// 更新主页面配置
|
||
updatePageConfigs() {
|
||
console.log('更新主页面配置');
|
||
// 重新构建页面配置,确保所有文本都通过 $i18n.t() 获取
|
||
const spaces = Array.isArray(this.spaceOptions) ? this.spaceOptions : []
|
||
const index = Math.min(Math.max(0, this.selectedSpaceIndex || 0), spaces.length - 1)
|
||
const status = spaces[index] && (spaces[index].status || spaces[index].Status || spaces[index].state)
|
||
const title = this.getStatusTitleByCode(status)
|
||
// 添加容错处理
|
||
const t = this.$i18n && this.$i18n.t ? this.$i18n.t.bind(this.$i18n) : (key) => key
|
||
this.pageConfigs = [
|
||
{
|
||
title,
|
||
subtitle: t('alarmCleared') || '--'
|
||
}
|
||
]
|
||
// 强制更新组件
|
||
this.$forceUpdate()
|
||
},
|
||
// 更新空间下拉选项(示例数据,可替换为实际接口)
|
||
updateSpaceOptions() {
|
||
this.spaceOptions = []
|
||
this.selectedSpaceIndex = 0
|
||
this.isLoadingSpaces = true
|
||
this.$http.get('/bst/space/list').then(res => {
|
||
if (res.code == 200) {
|
||
this.logoflag = false
|
||
this.spaceOptions = res.rows
|
||
// 若有本地已选空间,默认选中;否则选中第一个并写入本地
|
||
try {
|
||
const localKjid = uni.getStorageSync('kjid')
|
||
const spaces = Array.isArray(this.spaceOptions) ? this.spaceOptions : []
|
||
let matchIndex = 0
|
||
if (localKjid !== undefined && localKjid !== null && localKjid !== '') {
|
||
const found = spaces.findIndex(s => (s.id || s.spaceId || s.ID || s.Id) == localKjid)
|
||
if (found >= 0) {
|
||
matchIndex = found
|
||
}
|
||
}
|
||
// 如果空间列表不为空,设置默认选中并写入本地
|
||
if (spaces.length > 0) {
|
||
this.selectedSpaceIndex = matchIndex
|
||
const selected = spaces[matchIndex]
|
||
const kjid = selected && (selected.id || selected.spaceId || selected.ID || selected.Id)
|
||
if (kjid !== undefined && kjid !== null) {
|
||
uni.setStorageSync('kjid', kjid)
|
||
uni.setStorageSync('kjobj', selected)
|
||
}
|
||
}
|
||
} catch(e) {
|
||
console.warn('处理本地空间ID失败:', e)
|
||
}
|
||
// 空间列表加载后,立即根据状态刷新标题
|
||
this.updatePageConfigs()
|
||
}else if(res.code == 401){
|
||
this.logoflag = true
|
||
}
|
||
}).finally(() => {
|
||
this.isLoadingSpaces = false
|
||
})
|
||
},
|
||
// 顶部空间选择变更
|
||
handleSpaceChange(index, item, name) {
|
||
this.selectedSpaceIndex = index
|
||
console.log('选择空间:', index, item, name)
|
||
// 获取新选中的空间信息
|
||
const selected = item || (Array.isArray(this.spaceOptions) ? this.spaceOptions[index] : null)
|
||
const kjid = selected && (selected.id || selected.spaceId || selected.ID || selected.Id)
|
||
if (kjid !== undefined && kjid !== null) {
|
||
// 持久化选中的空间ID到本地
|
||
uni.setStorageSync('kjid', kjid)
|
||
uni.setStorageSync('kjobj', selected)
|
||
console.log('已保存 kjid:', kjid)
|
||
// 广播全局事件,通知需要依赖空间的模块刷新
|
||
console.log('主页面发送空间变化事件:', { kjid, space: selected })
|
||
uni.$emit('spaceChanged', { kjid, space: selected })
|
||
// 根据当前tab刷新对应的数据
|
||
this.refreshCurrentTabData(kjid)
|
||
}
|
||
this.updatePageConfigs()
|
||
},
|
||
// 根据当前tab刷新对应的数据
|
||
refreshCurrentTabData(kjid) {
|
||
console.log('刷新当前tab数据,空间ID:', kjid)
|
||
switch (this.currentTabIndex) {
|
||
case 0: // 设备模块
|
||
this.refreshDeviceTab(kjid)
|
||
break;
|
||
case 1: // 房间模块
|
||
this.refreshRoomTab(kjid)
|
||
break;
|
||
case 2: // 通知模块
|
||
this.refreshNotificationTab(kjid)
|
||
break;
|
||
case 3: // 控制模块
|
||
this.refreshControlTab(kjid)
|
||
break;
|
||
default:
|
||
console.log('未知的tab索引:', this.currentTabIndex)
|
||
}
|
||
},
|
||
// 刷新设备模块数据
|
||
refreshDeviceTab(kjid) {
|
||
console.log('刷新设备模块数据')
|
||
// 如果有设备模块的引用,可以调用其刷新方法
|
||
// 这里可以根据实际的DeviceTab组件API进行调整
|
||
uni.$emit('refreshDeviceData', { kjid })
|
||
},
|
||
// 刷新房间模块数据
|
||
refreshRoomTab(kjid) {
|
||
console.log('刷新房间模块数据')
|
||
// 如果有房间模块的引用,可以调用其刷新方法
|
||
// 这里可以根据实际的RoomTab组件API进行调整
|
||
uni.$emit('refreshRoomData', { kjid })
|
||
},
|
||
// 刷新通知模块数据
|
||
refreshNotificationTab(kjid) {
|
||
console.log('刷新通知模块数据')
|
||
if (this.$refs && this.$refs.notificationTab) {
|
||
try {
|
||
this.$refs.notificationTab.pageNum = 1
|
||
this.$refs.notificationTab.kjid = kjid
|
||
this.$refs.notificationTab.getlist()
|
||
} catch(err) {
|
||
console.warn('通知页刷新失败:', err)
|
||
}
|
||
}
|
||
},
|
||
// 刷新控制模块数据
|
||
refreshControlTab(kjid) {
|
||
console.log('刷新控制模块数据')
|
||
// 控制模块会自动通过kjid获取最新状态,无需额外操作
|
||
// 如果需要强制刷新,可以发送事件
|
||
uni.$emit('refreshControlData', { kjid })
|
||
},
|
||
// 处理标签页切换
|
||
handleTabChange(index) {
|
||
console.log('点击了标签页:', index)
|
||
this.currentTabIndex = index
|
||
console.log('切换到标签页:', index)
|
||
},
|
||
// 侧边菜单相关方法
|
||
toggleSideMenu() {
|
||
console.log('点击了菜单按钮')
|
||
this.showSideMenu = !this.showSideMenu
|
||
console.log('侧边菜单状态:', this.showSideMenu)
|
||
},
|
||
closeSideMenu() {
|
||
this.showSideMenu = false
|
||
},
|
||
handleMenuClick(type) {
|
||
console.log('Clicked menu item:', type)
|
||
this.closeSideMenu();
|
||
// 根据 type 执行不同的操作
|
||
uni.showToast({
|
||
title: `${this.$i18n.t('clicked')}${type}`,
|
||
icon: 'none',
|
||
duration:3000
|
||
})
|
||
},
|
||
handleAddRoom() {
|
||
console.log('Add Room clicked')
|
||
this.closeSideMenu()
|
||
uni.showToast({
|
||
title: this.$i18n.t('addRoomClicked'),
|
||
icon: 'none',
|
||
duration:3000
|
||
})
|
||
},
|
||
// 处理控制模块状态变化
|
||
handleStatusChanged(data) {
|
||
console.log('控制模块状态改变:', data)
|
||
// 刷新空间列表以更新状态
|
||
this.refreshSpaceStatus(data.spaceId, data.status)
|
||
},
|
||
// 刷新空间状态
|
||
refreshSpaceStatus(spaceId, newStatus) {
|
||
// 更新本地存储的空间状态
|
||
try {
|
||
const currentSpace = uni.getStorageSync('kjobj')
|
||
if (currentSpace && currentSpace.id === spaceId) {
|
||
currentSpace.status = newStatus
|
||
uni.setStorageSync('kjobj', currentSpace)
|
||
}
|
||
} catch(e) {
|
||
console.warn('更新本地空间状态失败:', e)
|
||
}
|
||
// 更新spaceOptions中对应空间的状态
|
||
const spaces = Array.isArray(this.spaceOptions) ? this.spaceOptions : []
|
||
const targetIndex = spaces.findIndex(space =>
|
||
(space.id || space.spaceId || space.ID || space.Id) == spaceId
|
||
)
|
||
if (targetIndex >= 0) {
|
||
// 直接更新状态
|
||
this.spaceOptions[targetIndex].status = newStatus
|
||
// 强制更新页面配置以刷新头部显示
|
||
this.updatePageConfigs()
|
||
console.log('空间状态已更新:', this.spaceOptions[targetIndex])
|
||
}
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page {
|
||
background: #fff;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.page-container {
|
||
background: #fff;
|
||
pointer-events: auto;
|
||
/* 确保触摸事件能够正常工作 */
|
||
touch-action: pan-x pan-y;
|
||
/* 防止页面滚动干扰手势识别 */
|
||
overflow: hidden;
|
||
height: 100vh;
|
||
}
|
||
|
||
/* 加载遮罩层样式 */
|
||
.loading-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 1000;
|
||
background: rgba(255,255,255,0.96);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.spinner {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
border: 6rpx solid #e0e0e0;
|
||
border-top-color: #4caf50;
|
||
animation: spin 1s linear infinite;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
.loading-text {
|
||
font-size: 28rpx;
|
||
color: #4caf50;
|
||
}
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
.page-content {
|
||
padding-bottom: 120rpx; /* 为底部导航留出空间 */
|
||
height: 83vh;
|
||
overflow: scroll;
|
||
position: relative;
|
||
z-index: 1;
|
||
/* 确保内容区域可以正常滚动 */
|
||
touch-action: pan-x pan-y;
|
||
}
|
||
|
||
|
||
</style> |