feat(event): 首页/消息中心接入告警列表真实接口,移除事件 mock 层
This commit is contained in:
parent
7bef729e93
commit
5761a5fed2
|
|
@ -1,26 +0,0 @@
|
|||
import { request } from '@/http'
|
||||
|
||||
const USE_MOCK = true
|
||||
|
||||
export interface EventItem {
|
||||
id: number
|
||||
sceneName: string
|
||||
deviceName: string
|
||||
status: number
|
||||
time: string
|
||||
}
|
||||
|
||||
export interface EventQuery {
|
||||
sceneId: number
|
||||
status: number
|
||||
startDate: string
|
||||
endDate: string
|
||||
}
|
||||
|
||||
// 事件列表接口未定:mock 返回空数组,页面保持空态
|
||||
export function getEventList(_query: EventQuery): Promise<EventItem[]> {
|
||||
if (USE_MOCK) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
return request.get<EventItem[]>('/event/list', { ..._query })
|
||||
}
|
||||
|
|
@ -1,16 +1,30 @@
|
|||
import { request } from '@/http'
|
||||
|
||||
// 入参对齐 apifox YlHomeAlarmListReq(homeId 必填,其余可选筛选)
|
||||
export interface AlarmListReq {
|
||||
homeId: string
|
||||
roomId?: string // 传入则只查该房间告警
|
||||
status?: number // 告警状态,不传则不筛选
|
||||
beginTime?: string // yyyy-MM-dd HH:mm:ss
|
||||
endTime?: string
|
||||
pageIndex?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
// 骨架:本期仅锁定接口路径,字段类型联调时修正(spec D7)
|
||||
export interface HomeAlarm {
|
||||
alarmId: string
|
||||
homeId: string
|
||||
roomId: string
|
||||
deviceId: string
|
||||
deviceName: string
|
||||
alarmType: string
|
||||
alarmTime: string
|
||||
handled: boolean
|
||||
status: number // 告警状态:0-未处理 1-已处理 2-已忽略(待实测确认枚举)
|
||||
}
|
||||
|
||||
export function getAlarmList(params: Record<string, unknown>): Promise<HomeAlarm[]> {
|
||||
return request.get('/yanglao/app/ylHomeAlarm/getList', params)
|
||||
export function getAlarmList(params: AlarmListReq): Promise<HomeAlarm[]> {
|
||||
return request.post('/yanglao/app/ylHomeAlarm/getList', params as unknown as Record<string, unknown>)
|
||||
}
|
||||
|
||||
export function getAlarmDetail(alarmId: string): Promise<HomeAlarm> {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ export interface HomeRoom {
|
|||
roomName: string
|
||||
}
|
||||
|
||||
// 文档路径为 POST + body { homeId }(YlHomeRoomListReq)
|
||||
export function getRoomList(homeId: string): Promise<HomeRoom[]> {
|
||||
return request.get('/yanglao/app/ylHomeRoom/getList', { homeId })
|
||||
return request.post('/yanglao/app/ylHomeRoom/getList', { homeId })
|
||||
}
|
||||
|
||||
export function addRoom(params: Record<string, unknown>): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,78 @@
|
|||
color: $color-text-tertiary;
|
||||
}
|
||||
|
||||
/* 告警卡片列表 */
|
||||
.alarm-list {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.alarm-card {
|
||||
background: $color-bg-white;
|
||||
border-radius: $radius-lg;
|
||||
box-shadow: $shadow-card;
|
||||
padding: 28px 32px;
|
||||
|
||||
& + & { margin-top: 20px; }
|
||||
}
|
||||
|
||||
.alarm-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.alarm-type {
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
color: $color-text;
|
||||
}
|
||||
|
||||
.alarm-tag {
|
||||
font-size: 22px;
|
||||
padding: 4px 16px;
|
||||
border-radius: $radius-pill;
|
||||
color: $color-text-secondary;
|
||||
background: $color-bg-gray;
|
||||
}
|
||||
|
||||
/* 状态枚举待实测,先按未处理红色、已处理绿色、已忽略灰的常见语义 */
|
||||
.tag1 {
|
||||
color: $color-danger;
|
||||
background: $color-danger-light-bg;
|
||||
}
|
||||
|
||||
.tag2 {
|
||||
color: $color-primary-deep;
|
||||
background: $color-primary-light-bg;
|
||||
}
|
||||
|
||||
.tag3 {
|
||||
color: $color-text-tertiary;
|
||||
background: $color-bg-gray;
|
||||
}
|
||||
|
||||
.alarm-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.alarm-label {
|
||||
width: 120px;
|
||||
flex-shrink: 0;
|
||||
font-size: 26px;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.alarm-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 26px;
|
||||
color: $color-text;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 数据层:三抽屉的定位锚点 */
|
||||
.content-area {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -1,92 +1,112 @@
|
|||
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 { getSceneList } from '@/api/scene'
|
||||
import { getEventList, type EventItem } from '@/api/event'
|
||||
import { getHomeList, type HomeInfo } from '@/api/home'
|
||||
import { getRoomList, type HomeRoom } from '@/api/homeRoom'
|
||||
import { getAlarmList, type HomeAlarm } from '@/api/homeAlarm'
|
||||
import { CalendarDrawer } from './calendar-drawer'
|
||||
import styles from './index.module.scss'
|
||||
|
||||
interface FilterOption {
|
||||
id: number
|
||||
id: string // '' 表示全部
|
||||
name: string
|
||||
}
|
||||
|
||||
// 告警状态筛选枚举(后端 integer,待实测确认:1-未处理 2-已处理 3-已忽略)
|
||||
const ALARM_STATUS: Record<string, string> = {
|
||||
'1': '未处理',
|
||||
'2': '已处理',
|
||||
'3': '已忽略'
|
||||
}
|
||||
|
||||
export default function Event() {
|
||||
// '' | 'scene' | 'status' | 'time'
|
||||
const [drawer, setDrawer] = useState<'' | 'scene' | 'status' | 'time'>('')
|
||||
// 事件列表:后端接口定型前 mock 返回空数组 → 空态
|
||||
const [eventList, setEventList] = useState<EventItem[]>([])
|
||||
// '' | 'room' | 'status' | 'time'
|
||||
const [drawer, setDrawer] = useState<'' | 'room' | 'status' | 'time'>('')
|
||||
const [alarmList, setAlarmList] = useState<HomeAlarm[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [filters, setFilters] = useState({
|
||||
sceneId: 0, // 0 表示全部
|
||||
status: 0,
|
||||
roomId: '',
|
||||
status: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
timeText: ''
|
||||
})
|
||||
const [sceneOptions, setSceneOptions] = useState<FilterOption[]>([{ id: 0, name: '全部' }])
|
||||
const [home, setHome] = useState<HomeInfo | null>(null)
|
||||
const [roomOptions, setRoomOptions] = useState<FilterOption[]>([{ id: '', name: '全部' }])
|
||||
const statusOptions: FilterOption[] = [
|
||||
{ id: 0, name: '全部' },
|
||||
{ id: 1, name: '未处理' },
|
||||
{ id: 2, name: '已处理' },
|
||||
{ id: 3, name: '已忽略' }
|
||||
{ id: '', name: '全部' },
|
||||
{ id: '1', name: '未处理' },
|
||||
{ id: '2', name: '已处理' },
|
||||
{ id: '3', name: '已忽略' }
|
||||
]
|
||||
|
||||
// 场所列表:复用场景接口;失败时保留"全部"不阻塞筛选
|
||||
// 默认家庭 + 其房间列表:失败时保留"全部"选项不阻塞筛选
|
||||
useEffect(() => {
|
||||
getSceneList()
|
||||
.then((data) => {
|
||||
setSceneOptions([{ id: 0, name: '全部' }, ...(data ?? [])])
|
||||
getHomeList()
|
||||
.then(async (list) => {
|
||||
const first = list[0] ?? null
|
||||
setHome(first)
|
||||
if (!first) return
|
||||
const rooms = await getRoomList(first.homeId)
|
||||
setRoomOptions([{ id: '', name: '全部' }, ...rooms.map(toRoomOption)])
|
||||
})
|
||||
.catch(() => setSceneOptions([{ id: 0, name: '全部' }]))
|
||||
.catch(() => setRoomOptions([{ id: '', name: '全部' }]))
|
||||
}, [])
|
||||
|
||||
useDidShow(() => {
|
||||
// 进页首拉:home 就绪与否都调一次,home 为空时后端会报错并保持空态
|
||||
if (home) loadAlarms(filters)
|
||||
})
|
||||
|
||||
const toRoomOption = (r: HomeRoom): FilterOption => ({ id: r.roomId, name: r.roomName })
|
||||
|
||||
const closeDrawer = useCallback(() => setDrawer(''), [])
|
||||
|
||||
const sceneLabel = sceneOptions.find((o) => o.id === filters.sceneId)?.name ?? '全部场所'
|
||||
const roomLabel = roomOptions.find((o) => o.id === filters.roomId)?.name ?? '全部场所'
|
||||
const statusLabel = statusOptions.find((o) => o.id === filters.status)?.name ?? '全部状态'
|
||||
|
||||
// 筛选变化:拉取事件列表(当前 mock 空,保持空态展示)
|
||||
const handleFilter = useCallback(async (next: typeof filters) => {
|
||||
// 拉取告警列表: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 getEventList({
|
||||
sceneId: next.sceneId,
|
||||
status: next.status,
|
||||
startDate: next.startDate,
|
||||
endDate: next.endDate
|
||||
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
|
||||
})
|
||||
setEventList(list)
|
||||
setAlarmList(list ?? [])
|
||||
} catch {
|
||||
// 失败保持现有列表,toast 由请求封装统一处理
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
}, [home?.homeId])
|
||||
|
||||
const selectScene = (id: number) => {
|
||||
const next = { ...filters, sceneId: id }
|
||||
// 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()
|
||||
handleFilter(next)
|
||||
loadAlarms(next)
|
||||
}
|
||||
|
||||
const selectStatus = (id: number) => {
|
||||
const next = { ...filters, status: id }
|
||||
setFilters(next)
|
||||
closeDrawer()
|
||||
handleFilter(next)
|
||||
}
|
||||
|
||||
const onTimeConfirm = (start: string, end: string) => {
|
||||
const next = { ...filters, startDate: start, endDate: end, timeText: `${start} 至 ${end}` }
|
||||
setFilters(next)
|
||||
closeDrawer()
|
||||
handleFilter(next)
|
||||
}
|
||||
|
||||
const renderOptionDrawer = (kind: 'scene' | 'status') => {
|
||||
const opts = kind === 'scene' ? sceneOptions : statusOptions
|
||||
const currentId = kind === 'scene' ? filters.sceneId : filters.status
|
||||
const title = kind === 'scene' ? '选择场所' : '选择状态'
|
||||
const onSelect = kind === 'scene' ? selectScene : selectStatus
|
||||
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}>
|
||||
|
|
@ -111,20 +131,29 @@ export default function Event() {
|
|||
)
|
||||
}
|
||||
|
||||
const statusTagOf = (alarm: HomeAlarm) => ALARM_STATUS[String(alarm.status)] ?? '未知'
|
||||
|
||||
// CSS Modules 动态 key 映射对象(避免索引签名问题)
|
||||
const tagClsMap: Record<string, string> = {
|
||||
'1': styles.tag1,
|
||||
'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 === 'scene' ? '' : 'scene')}>
|
||||
<View className={`${styles.filterTrigger} ${filters.sceneId !== 0 ? styles.filterTriggerActive : ''}`}>
|
||||
<Text className={styles.filterText}>{sceneLabel}</Text>
|
||||
<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 !== 0 ? styles.filterTriggerActive : ''}`}>
|
||||
<View className={`${styles.filterTrigger} ${filters.status ? styles.filterTriggerActive : ''}`}>
|
||||
<Text className={styles.filterText}>{statusLabel}</Text>
|
||||
<Text className={styles.filterArrow}>▾</Text>
|
||||
</View>
|
||||
|
|
@ -140,32 +169,49 @@ export default function Event() {
|
|||
{/* 数据层:抽屉锚点(筛选栏下缘展开,遮罩盖住本层全部) */}
|
||||
<View className={styles.contentArea}>
|
||||
<View className={styles.contentBody}>
|
||||
{eventList.length === 0 ? (
|
||||
{alarmList.length === 0 ? (
|
||||
<>
|
||||
<View className={styles.emptyArea}>
|
||||
<Empty text="暂无事件数据" mode="message" />
|
||||
<Empty text="暂无告警数据" mode="message" />
|
||||
</View>
|
||||
<View className={styles.emptyHint}>
|
||||
<Text>调整筛选条件后的事件列表将在此展示</Text>
|
||||
<Text>调整筛选条件后的告警列表将在此展示</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View className={styles.eventList}>
|
||||
{/* 事件卡片列表:布局等后端接口定型后补(Task 5.1.c 说明) */}
|
||||
<View className={styles.alarmList}>
|
||||
{alarmList.map((alarm) => (
|
||||
<View key={alarm.alarmId} className={styles.alarmCard}>
|
||||
<View className={styles.alarmHead}>
|
||||
<Text className={styles.alarmType}>{alarm.alarmType}</Text>
|
||||
<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.deviceName || alarm.deviceId}</Text>
|
||||
</View>
|
||||
<View className={styles.alarmRow}>
|
||||
<Text className={styles.alarmLabel}>时间</Text>
|
||||
<Text className={styles.alarmValue}>{alarm.alarmTime}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{drawer !== '' && <View className={styles.contentMask} onClick={closeDrawer} catchMove />}
|
||||
|
||||
{drawer === 'scene' && renderOptionDrawer('scene')}
|
||||
{drawer === 'room' && renderOptionDrawer('room')}
|
||||
{drawer === 'status' && renderOptionDrawer('status')}
|
||||
{drawer === 'time' && (
|
||||
<CalendarDrawer
|
||||
initialStart={filters.startDate}
|
||||
initialEnd={filters.endDate}
|
||||
onClose={closeDrawer}
|
||||
onConfirm={onTimeConfirm}
|
||||
onConfirm={(start, end) =>
|
||||
applyFilter({ ...filters, startDate: start, endDate: end, timeText: `${start} 至 ${end}` })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import { Navbar } from '@/components/navbar'
|
|||
import { Loading } from '@/components/loading'
|
||||
import { Empty } from '@/components/empty'
|
||||
import { StateView } from '@/components/state-view'
|
||||
import { getEventList, type EventItem } from '@/api/event'
|
||||
import { getHomeList, type HomeInfo } from '@/api/home'
|
||||
import { getAlarmList, type HomeAlarm } from '@/api/homeAlarm'
|
||||
import { store } from '@/store'
|
||||
import { storage } from '@/utils/storage'
|
||||
import { ROUTES } from '@/constants/routes'
|
||||
|
|
@ -15,17 +16,23 @@ import { splitAlerts } from './splitAlerts'
|
|||
import styles from './index.module.scss'
|
||||
|
||||
export default function Index() {
|
||||
const [alertList, setAlertList] = useState<EventItem[]>([])
|
||||
const [alertList, setAlertList] = useState<HomeAlarm[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [loadError, setLoadError] = useState(false)
|
||||
|
||||
// 每次进入页面重拉全量告警(与事件页"全部"筛选同参数)
|
||||
// 每次进入页面重拉默认家庭的全部告警(与事件页"全部"筛选同参数)
|
||||
const loadAlerts = useCallback(async () => {
|
||||
setLoading(true)
|
||||
setLoadError(false)
|
||||
try {
|
||||
const list = await getEventList({ sceneId: 0, status: 0, startDate: '', endDate: '' })
|
||||
setAlertList(list)
|
||||
const homes = await getHomeList()
|
||||
const first: HomeInfo | undefined = homes[0]
|
||||
if (!first) {
|
||||
setAlertList([])
|
||||
return
|
||||
}
|
||||
const list = await getAlarmList({ homeId: first.homeId })
|
||||
setAlertList(list ?? [])
|
||||
} catch {
|
||||
setLoadError(true)
|
||||
} finally {
|
||||
|
|
@ -66,7 +73,7 @@ export default function Index() {
|
|||
{messages.length > 0 && (
|
||||
<View className={styles.messageList}>
|
||||
{messages.map((alert) => (
|
||||
<AlertMessageItem key={alert.id} alert={alert} onClick={goAlertDetail} />
|
||||
<AlertMessageItem key={alert.alarmId} alert={alert} onClick={goAlertDetail} />
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
|
|
@ -94,33 +101,33 @@ function FamilyEntry({ onClick }: { onClick: () => void }) {
|
|||
)
|
||||
}
|
||||
|
||||
function UnhandledAlert({ alert, onClick }: { alert: EventItem; onClick: () => void }) {
|
||||
function UnhandledAlert({ alert, onClick }: { alert: HomeAlarm; onClick: () => void }) {
|
||||
return (
|
||||
<View className={styles.unhandledCard} onClick={onClick}>
|
||||
<View className={styles.unhandledHead}>
|
||||
<View className={styles.unhandledIcon}>
|
||||
<Text className={styles.unhandledIconText}>!</Text>
|
||||
</View>
|
||||
<Text className={styles.unhandledTitle}>{alert.deviceName || '未知设备'}</Text>
|
||||
<Text className={styles.unhandledTitle}>{alert.deviceName || alert.deviceId || '未知设备'}</Text>
|
||||
<View className={styles.unhandledTag}>
|
||||
<Text className={styles.unhandledTagText}>未处理</Text>
|
||||
</View>
|
||||
</View>
|
||||
{alert.sceneName && (
|
||||
{alert.alarmType && (
|
||||
<View className={styles.unhandledMeta}>
|
||||
<Text className={styles.unhandledMetaText}>{alert.sceneName}</Text>
|
||||
<Text className={styles.unhandledMetaText}>{alert.alarmType}</Text>
|
||||
</View>
|
||||
)}
|
||||
{alert.time && (
|
||||
{alert.alarmTime && (
|
||||
<View className={styles.unhandledMeta}>
|
||||
<Text className={styles.unhandledMetaText}>{alert.time}</Text>
|
||||
<Text className={styles.unhandledMetaText}>{alert.alarmTime}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertMessageItem({ alert, onClick }: { alert: EventItem; onClick: () => void }) {
|
||||
function AlertMessageItem({ alert, onClick }: { alert: HomeAlarm; onClick: () => void }) {
|
||||
return (
|
||||
<View className={styles.messageCard} onClick={onClick}>
|
||||
<View className={styles.messageIcon}>
|
||||
|
|
@ -128,8 +135,8 @@ function AlertMessageItem({ alert, onClick }: { alert: EventItem; onClick: () =>
|
|||
</View>
|
||||
<View className={styles.messageBody}>
|
||||
<View className={styles.messageHead}>
|
||||
<Text className={styles.messageTitle}>告警消息</Text>
|
||||
{alert.time && <Text className={styles.messageTime}>{alert.time}</Text>}
|
||||
<Text className={styles.messageTitle}>{alert.alarmType || '告警消息'}</Text>
|
||||
{alert.alarmTime && <Text className={styles.messageTime}>{alert.alarmTime}</Text>}
|
||||
</View>
|
||||
{alert.deviceName && (
|
||||
<Text className={styles.messageDevice}>{alert.deviceName}</Text>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import type { EventItem } from '@/api/event'
|
||||
import type { HomeAlarm } from '@/api/homeAlarm'
|
||||
|
||||
// 首页告警分流:status=1 未处理(首页只展示最新一条),其余全部进消息列表
|
||||
export function splitAlerts(list: EventItem[]): {
|
||||
unhandled: EventItem[]
|
||||
messages: EventItem[]
|
||||
export function splitAlerts(list: HomeAlarm[]): {
|
||||
unhandled: HomeAlarm[]
|
||||
messages: HomeAlarm[]
|
||||
} {
|
||||
const unhandled: EventItem[] = []
|
||||
const messages: EventItem[] = []
|
||||
const unhandled: HomeAlarm[] = []
|
||||
const messages: HomeAlarm[] = []
|
||||
for (const item of list) {
|
||||
if (item.status === 1) unhandled.push(item)
|
||||
else messages.push(item)
|
||||
|
|
|
|||
|
|
@ -1,33 +1,36 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { splitAlerts } from '@/pages/index/splitAlerts'
|
||||
import type { EventItem } from '@/api/event'
|
||||
import type { HomeAlarm } from '@/api/homeAlarm'
|
||||
|
||||
const item = (over: Partial<EventItem>): EventItem => ({
|
||||
id: 1,
|
||||
sceneName: '客厅',
|
||||
const item = (over: Partial<HomeAlarm>): HomeAlarm => ({
|
||||
alarmId: '1',
|
||||
homeId: 'h1',
|
||||
roomId: 'r1',
|
||||
deviceId: 'd1',
|
||||
deviceName: '体征监测仪',
|
||||
alarmType: '心率异常',
|
||||
alarmTime: '2026-09-07 21:22:12',
|
||||
status: 2,
|
||||
time: '2026-09-07 21:22:12',
|
||||
...over
|
||||
})
|
||||
|
||||
describe('splitAlerts', () => {
|
||||
it('status=1 进入未处理组,其余进入消息组', () => {
|
||||
const list = [
|
||||
item({ id: 1, status: 1 }),
|
||||
item({ id: 2, status: 2 }),
|
||||
item({ id: 3, status: 3 })
|
||||
item({ alarmId: '1', status: 1 }),
|
||||
item({ alarmId: '2', status: 2 }),
|
||||
item({ alarmId: '3', status: 3 })
|
||||
]
|
||||
const { unhandled, messages } = splitAlerts(list)
|
||||
expect(unhandled.map((i) => i.id)).toEqual([1])
|
||||
expect(messages.map((i) => i.id)).toEqual([2, 3])
|
||||
expect(unhandled.map((i) => i.alarmId)).toEqual(['1'])
|
||||
expect(messages.map((i) => i.alarmId)).toEqual(['2', '3'])
|
||||
})
|
||||
|
||||
it('status 异常值(如 0、99)落入消息组不丢弃', () => {
|
||||
const list = [item({ id: 1, status: 0 }), item({ id: 2, status: 99 })]
|
||||
const list = [item({ alarmId: '1', status: 0 }), item({ alarmId: '2', status: 99 })]
|
||||
const { unhandled, messages } = splitAlerts(list)
|
||||
expect(unhandled).toEqual([])
|
||||
expect(messages.map((i) => i.id)).toEqual([1, 2])
|
||||
expect(messages.map((i) => i.alarmId)).toEqual(['1', '2'])
|
||||
})
|
||||
|
||||
it('空列表两组均为空', () => {
|
||||
|
|
@ -38,12 +41,12 @@ describe('splitAlerts', () => {
|
|||
|
||||
it('两组均保持原有相对顺序', () => {
|
||||
const list = [
|
||||
item({ id: 3, status: 2 }),
|
||||
item({ id: 1, status: 1 }),
|
||||
item({ id: 2, status: 2 })
|
||||
item({ alarmId: '3', status: 2 }),
|
||||
item({ alarmId: '1', status: 1 }),
|
||||
item({ alarmId: '2', status: 2 })
|
||||
]
|
||||
const { unhandled, messages } = splitAlerts(list)
|
||||
expect(unhandled.map((i) => i.id)).toEqual([1])
|
||||
expect(messages.map((i) => i.id)).toEqual([3, 2])
|
||||
expect(unhandled.map((i) => i.alarmId)).toEqual(['1'])
|
||||
expect(messages.map((i) => i.alarmId)).toEqual(['3', '2'])
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue