feat(web/miniapp): 高发病天气预警展示(#12)
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import { get } from './request';
|
||||
|
||||
export interface WeatherAlert {
|
||||
id: string;
|
||||
disease: string;
|
||||
level: string;
|
||||
reason: string;
|
||||
createdAt?: string;
|
||||
}
|
||||
|
||||
export const getWeatherAlerts = (limit = 5) =>
|
||||
get<WeatherAlert[]>('/weather/alerts', { limit });
|
||||
@@ -6,6 +6,47 @@
|
||||
padding-bottom: $spacing-xl;
|
||||
}
|
||||
|
||||
.weatherWrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-sm;
|
||||
margin: $spacing-md $spacing-lg 0;
|
||||
}
|
||||
|
||||
.weatherCard {
|
||||
background: rgba(255, 125, 0, 0.08);
|
||||
border-radius: $radius-md;
|
||||
padding: $spacing-md;
|
||||
}
|
||||
|
||||
.weatherHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-sm;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.weatherBadge {
|
||||
font-size: $font-size-xs;
|
||||
color: $color-warning;
|
||||
background: rgba(255, 125, 0, 0.15);
|
||||
padding: 4rpx $spacing-sm;
|
||||
border-radius: $radius-round;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.weatherDisease {
|
||||
font-size: $font-size-md;
|
||||
font-weight: $font-weight-medium;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.weatherReason {
|
||||
font-size: $font-size-xs;
|
||||
color: $color-text-secondary;
|
||||
line-height: $line-height-normal;
|
||||
}
|
||||
|
||||
.filterBar {
|
||||
display: flex;
|
||||
background: $color-bg-card;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { View, Text } from '@tarojs/components';
|
||||
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
|
||||
import styles from './index.module.scss';
|
||||
import { getAlarms, acknowledgeAlarm, getAlarmClip } from '@/api/alarms';
|
||||
import { getWeatherAlerts, type WeatherAlert } from '@/api/weather';
|
||||
import {
|
||||
formatDateTime,
|
||||
formatRelativeTime,
|
||||
@@ -16,6 +17,7 @@ import type { Alarm } from '@/types';
|
||||
|
||||
const AlertsPage: React.FC = () => {
|
||||
const [alarms, setAlarms] = useState<Alarm[]>([]);
|
||||
const [weatherAlerts, setWeatherAlerts] = useState<WeatherAlert[]>([]);
|
||||
const [filterOpen, setFilterOpen] = useState(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
@@ -25,6 +27,8 @@ const AlertsPage: React.FC = () => {
|
||||
setError('');
|
||||
const res = await getAlarms({ openOnly: filterOpen }).catch(() => [] as Alarm[]);
|
||||
setAlarms(res);
|
||||
const weatherRes = await getWeatherAlerts(5).catch(() => [] as WeatherAlert[]);
|
||||
setWeatherAlerts(weatherRes);
|
||||
} catch (err) {
|
||||
console.error('[Alerts] 数据加载失败:', err);
|
||||
setError(err instanceof Error ? err.message : '数据加载失败');
|
||||
@@ -147,6 +151,23 @@ const AlertsPage: React.FC = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 高发病天气预警 */}
|
||||
{weatherAlerts.length > 0 ? (
|
||||
<View className={styles.weatherWrap}>
|
||||
{weatherAlerts.map((w) => (
|
||||
<View key={w.id} className={styles.weatherCard}>
|
||||
<View className={styles.weatherHeader}>
|
||||
<Text className={styles.weatherBadge}>
|
||||
{w.level === 'orange' ? '高风险' : w.level === 'red' ? '极高风险' : '注意'}
|
||||
</Text>
|
||||
<Text className={styles.weatherDisease}>{w.disease}</Text>
|
||||
</View>
|
||||
<Text className={styles.weatherReason}>{w.reason}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{alarms.length === 0 ? (
|
||||
<View className={styles.emptyWrap}>
|
||||
<Text className={styles.emptyIcon}>{filterOpen ? '✅' : '🔔'}</Text>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { get } from '../api/http';
|
||||
|
||||
export interface WeatherAlert {
|
||||
id: string;
|
||||
disease: string;
|
||||
level: string;
|
||||
reason: string;
|
||||
createdAt?: string;
|
||||
}
|
||||
|
||||
export const listWeatherAlerts = (limit = 5) =>
|
||||
get<WeatherAlert[]>('/weather/alerts', { params: { limit } });
|
||||
+39
-4
@@ -1,9 +1,10 @@
|
||||
import { Button, Tag, message } from 'antd';
|
||||
import { Button, Card, Space, Tag, message } from 'antd';
|
||||
import { CheckOutlined } from '@ant-design/icons';
|
||||
import { ProTable, type ProColumns } from '@ant-design/pro-components';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import { getAlertClip, listAlerts, markAlertRead, markAllRead, type Alert } from '../dal/alert';
|
||||
import { listWeatherAlerts, type WeatherAlert } from '../dal/weather';
|
||||
|
||||
const LEVEL_MAP: Record<string, { color: string; text: string }> = {
|
||||
info: { color: 'blue', text: '提示' },
|
||||
@@ -19,6 +20,37 @@ const LEVEL_MAP: Record<string, { color: string; text: string }> = {
|
||||
const levelOf = (alert: Alert) => alert.level || alert.severity || 'info';
|
||||
const isRead = (alert: Alert) => alert.read ?? alert.acknowledged ?? false;
|
||||
|
||||
const WEATHER_LEVEL_COLORS: Record<string, string> = {
|
||||
yellow: 'gold',
|
||||
orange: 'orange',
|
||||
red: 'red',
|
||||
};
|
||||
|
||||
function WeatherAlertsBlock() {
|
||||
const [alerts, setAlerts] = useState<WeatherAlert[]>([]);
|
||||
useEffect(() => {
|
||||
listWeatherAlerts(5)
|
||||
.then(setAlerts)
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
if (alerts.length === 0) return null;
|
||||
return (
|
||||
<Card title="高发病天气预警" size="small" style={{ marginBottom: 16 }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
{alerts.map((a) => (
|
||||
<div key={a.id}>
|
||||
<Tag color={WEATHER_LEVEL_COLORS[a.level] || 'gold'}>
|
||||
{a.level === 'orange' ? '高风险' : a.level === 'red' ? '极高风险' : '注意'}
|
||||
</Tag>
|
||||
<b>{a.disease}</b>
|
||||
<span style={{ marginLeft: 8, color: '#666' }}>{a.reason}</span>
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AlertPage() {
|
||||
const [, setTick] = useState(0);
|
||||
|
||||
@@ -89,7 +121,9 @@ export default function AlertPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<ProTable<Alert>
|
||||
<>
|
||||
<WeatherAlertsBlock />
|
||||
<ProTable<Alert>
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
request={async (p) => {
|
||||
@@ -109,6 +143,7 @@ export default function AlertPage() {
|
||||
全部标为已读
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user