congming_huose-apk/pages/index/index.vue

524 lines
18 KiB
Vue
Raw Permalink Normal View History

2025-11-08 11:30:06 +08:00
<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>