SEC_Warehouse/src/pages/event/index.tsx

236 lines
9.3 KiB
TypeScript
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.

import { useState, useCallback, useEffect } from 'react'
import { useDidShow } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { Navbar } from '@/components/navbar'
import { Empty } from '@/components/empty'
import { getHomeList, type HomeInfo } from '@/api/home'
import { resolveCurrentHome } from '@/store/currentHome'
import { getRoomList, type HomeRoom } from '@/api/homeRoom'
import { getAlarmList, type HomeAlarm } from '@/api/homeAlarm'
import { navigateTo } from '@/utils/navigation'
import { ROUTES } from '@/constants/routes'
import { CalendarDrawer } from './calendar-drawer'
import styles from './index.module.scss'
interface FilterOption {
id: string // '' 表示全部
name: string
}
// 告警状态枚举(2026-09-23 后端确认:0-待处理 1-已忽略 2-已处理 3-自动处理)
const ALARM_STATUS: Record<string, string> = {
'0': '待处理',
'1': '已忽略',
'2': '已处理',
'3': '自动处理'
}
export default function Event() {
// '' | 'room' | 'status' | 'time'
const [drawer, setDrawer] = useState<'' | 'room' | 'status' | 'time'>('')
const [alarmList, setAlarmList] = useState<HomeAlarm[]>([])
const [loading, setLoading] = useState(true)
const [filters, setFilters] = useState({
roomId: '',
status: '',
startDate: '',
endDate: '',
timeText: ''
})
const [home, setHome] = useState<HomeInfo | null>(null)
const [roomOptions, setRoomOptions] = useState<FilterOption[]>([{ id: '', name: '全部' }])
const statusOptions: FilterOption[] = [
{ id: '', name: '全部' },
{ id: '0', name: '待处理' },
{ id: '1', name: '已忽略' },
{ id: '2', name: '已处理' },
{ id: '3', name: '自动处理' }
]
// 当前家庭 + 其房间列表:与智能设备页共享选择(resolveCurrentHome 读持久化),
// 失败时保留"全部"选项不阻塞筛选
useEffect(() => {
getHomeList()
.then(async (list) => {
const first = resolveCurrentHome(list)
setHome(first)
if (!first) return
const rooms = await getRoomList(first.homeId)
setRoomOptions([{ id: '', name: '全部' }, ...rooms.map(toRoomOption)])
})
.catch(() => setRoomOptions([{ id: '', name: '全部' }]))
}, [])
useDidShow(() => {
// 进页首拉:home 就绪与否都调一次,home 为空时后端会报错并保持空态
if (home) loadAlarms(filters)
})
const toRoomOption = (r: HomeRoom): FilterOption => ({ id: r.roomId, name: r.name })
const closeDrawer = useCallback(() => setDrawer(''), [])
const roomLabel = roomOptions.find((o) => o.id === filters.roomId)?.name ?? '全部场所'
const statusLabel = statusOptions.find((o) => o.id === filters.status)?.name ?? '全部状态'
// 拉取告警列表:home 未就绪时跳过(useEffect 到达后由 loadAlarms 首拉兜底)
const loadAlarms = useCallback(async (next: typeof filters, homeId?: string) => {
const targetHomeId = homeId ?? home?.homeId
if (!targetHomeId) return
setLoading(true)
try {
const list = await getAlarmList({
homeId: targetHomeId,
roomId: next.roomId || undefined,
status: next.status ? Number(next.status) : undefined,
// 接口要求完整时分秒:起止日拼边界值覆盖整天
beginTime: next.startDate ? `${next.startDate} 00:00:00` : undefined,
endTime: next.endDate ? `${next.endDate} 23:59:59` : undefined
})
setAlarmList(list ?? [])
} catch {
// 失败保持现有列表,toast 由请求封装统一处理
} finally {
setLoading(false)
}
}, [home?.homeId])
// home 就绪后的首拉(初始 useDidShow 时 home 可能还没到)
useEffect(() => {
if (home) loadAlarms(filters, home.homeId)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [home?.homeId])
const applyFilter = (next: typeof filters) => {
setFilters(next)
closeDrawer()
loadAlarms(next)
}
const renderOptionDrawer = (kind: 'room' | 'status') => {
const opts = kind === 'room' ? roomOptions : statusOptions
const currentId = kind === 'room' ? filters.roomId : filters.status
const title = kind === 'room' ? '选择场所' : '选择状态'
const onSelect = (id: string) =>
applyFilter(kind === 'room' ? { ...filters, roomId: id } : { ...filters, status: id })
return (
<View className={styles.dropPanelOptions}>
<View className={styles.drawerHeader}>
<Text className={styles.drawerTitle}>{title}</Text>
<View className={styles.drawerClose} onClick={closeDrawer}>
<Text className={styles.closeIcon}>×</Text>
</View>
</View>
<View className={styles.drawerList}>
{opts.map((opt) => (
<View
key={opt.id}
className={`${styles.drawerItem} ${currentId === opt.id ? styles.drawerItemActive : ''}`}
onClick={() => onSelect(opt.id)}
>
<Text className={styles.drawerItemText}>{opt.name}</Text>
{currentId === opt.id && <Text className={styles.checkIcon}>✓</Text>}
</View>
))}
</View>
</View>
)
}
const statusTagOf = (alarm: HomeAlarm) => ALARM_STATUS[String(alarm.status)] ?? '未知'
// CSS Modules 动态 key 映射对象(避免索引签名问题)
const tagClsMap: Record<string, string> = {
'0': styles.tag1, // 待处理-红
'1': styles.tag3, // 已忽略-灰
'2': styles.tag2, // 已处理-绿
'3': styles.tag3 // 自动处理-灰
}
return (
<View className={styles.page}>
<Navbar title="消息中心" autoBack={false} variant="hero" />
{/* 筛选栏:三格等分 */}
<View className={styles.filters}>
<View className={styles.filterCell} onClick={() => setDrawer(drawer === 'room' ? '' : 'room')}>
<View className={`${styles.filterTrigger} ${filters.roomId ? styles.filterTriggerActive : ''}`}>
<Text className={styles.filterText}>{roomLabel}</Text>
<Text className={styles.filterArrow}>▾</Text>
</View>
</View>
<View className={styles.filterCell} onClick={() => setDrawer(drawer === 'status' ? '' : 'status')}>
<View className={`${styles.filterTrigger} ${filters.status ? styles.filterTriggerActive : ''}`}>
<Text className={styles.filterText}>{statusLabel}</Text>
<Text className={styles.filterArrow}>▾</Text>
</View>
</View>
<View className={styles.filterCell} onClick={() => setDrawer(drawer === 'time' ? '' : 'time')}>
<View className={`${styles.filterTrigger} ${filters.startDate ? styles.filterTriggerActive : ''}`}>
<Text className={styles.filterText}>{filters.timeText || '时间'}</Text>
<Text className={styles.filterArrow}>▾</Text>
</View>
</View>
</View>
{/* 数据层:抽屉锚点(筛选栏下缘展开,遮罩盖住本层全部) */}
<View className={styles.contentArea}>
<View className={styles.contentBody}>
{alarmList.length === 0 ? (
<>
<View className={styles.emptyArea}>
<Empty text="暂无告警数据" mode="message" />
</View>
<View className={styles.emptyHint}>
<Text>调整筛选条件后的告警列表将在此展示</Text>
</View>
</>
) : (
<View className={styles.alarmList}>
{alarmList.map((alarm) => (
<View
key={alarm.alarmId}
className={styles.alarmCard}
hoverClass={styles.alarmCardPressed}
onClick={() => navigateTo(`${ROUTES.EVENT_DETAIL}?id=${alarm.alarmId}`)}
>
<View className={styles.alarmHead}>
<View className={styles.alarmTypeWrap}>
<View className={styles.alarmTypeDot} />
<Text className={styles.alarmType}>{alarm.content || '告警消息'}</Text>
</View>
<Text className={`${styles.alarmTag} ${tagClsMap[String(alarm.status)] ?? ''}`}>{statusTagOf(alarm)}</Text>
</View>
<View className={styles.alarmRow}>
<Text className={styles.alarmLabel}>设备</Text>
<Text className={styles.alarmValue}>{alarm.deviceAlias || alarm.deviceName || alarm.deviceId}</Text>
</View>
<View className={styles.alarmRow}>
<Text className={styles.alarmLabel}>时间</Text>
<Text className={styles.alarmValue}>{alarm.createTime}</Text>
</View>
</View>
))}
</View>
)}
</View>
{drawer !== '' && <View className={styles.contentMask} onClick={closeDrawer} catchMove />}
{drawer === 'room' && renderOptionDrawer('room')}
{drawer === 'status' && renderOptionDrawer('status')}
{drawer === 'time' && (
<CalendarDrawer
initialStart={filters.startDate}
initialEnd={filters.endDate}
onClose={closeDrawer}
onConfirm={(start, end) =>
applyFilter({ ...filters, startDate: start, endDate: end, timeText: `${start} 至 ${end}` })
}
/>
)}
</View>
</View>
)
}