feat(web/miniapp): 批次/蚕匾管理与蚕房详情展示(#7)

This commit is contained in:
weijuesen
2026-08-12 17:15:48 +08:00
parent 362a70b8a8
commit 0a5e2b18e1
6 changed files with 588 additions and 1 deletions
+29
View File
@@ -0,0 +1,29 @@
import { get } from './request';
export interface Tray {
id: string;
name: string;
code?: string;
position?: string;
roomId: string;
enabled?: boolean;
}
export interface Batch {
id: string;
roomId: string;
name: string;
variety?: string;
source?: string;
instar?: number;
status: string;
enteredAt?: string;
}
export const getTrays = (roomId: string) => get<Tray[]>('/trays', { roomId });
export const getBatches = (roomId: string) => get<Batch[]>('/batches', { roomId });
export const batchStatusLabel = (status: string) => {
const labels: Record<string, string> = { rearing: '饲养中', mounted: '已上蔟', finished: '已结束' };
return labels[status] || status;
};
+63 -1
View File
@@ -5,6 +5,7 @@ import styles from './index.module.scss';
import { getRoomById } from '@/api/rooms';
import { getDevices } from '@/api/devices';
import { getLatestTelemetry, fetchTrend } from '@/api/telemetry';
import { getTrays, getBatches, batchStatusLabel, type Tray, type Batch } from '@/api/trayBatch';
import MiniChart from '@/components/MiniChart';
import {
roomStatusLabel,
@@ -24,6 +25,8 @@ const RoomDetailPage: React.FC = () => {
const [devices, setDevices] = useState<Device[]>([]);
const [telemetryMap, setTelemetryMap] = useState<Record<string, TelemetryRecord[]>>({});
const [trend, setTrend] = useState<TrendPoint[]>([]);
const [trays, setTrays] = useState<Tray[]>([]);
const [batches, setBatches] = useState<Batch[]>([]);
const [trendLoading, setTrendLoading] = useState(false);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
@@ -37,12 +40,14 @@ const RoomDetailPage: React.FC = () => {
}
try {
setError('');
const [roomRes, devicesRes] = await Promise.all([
const [roomRes, devicesRes, traysRes, batchesRes] = await Promise.all([
getRoomById(roomId).catch((e) => {
console.error('[RoomDetail] 获取蚕房失败:', e);
return null;
}),
getDevices().catch(() => [] as Device[]),
getTrays(roomId).catch(() => [] as Tray[]),
getBatches(roomId).catch(() => [] as Batch[]),
]);
if (roomRes) {
@@ -51,6 +56,8 @@ const RoomDetailPage: React.FC = () => {
const roomDevices = devicesRes.filter((d) => d.roomId === roomId);
setDevices(roomDevices);
setTrays(traysRes);
setBatches(batchesRes);
// 获取每个设备的最新遥测
const telemetryEntries = await Promise.all(
@@ -271,6 +278,61 @@ const RoomDetailPage: React.FC = () => {
</View>
)}
</View>
{/* 蚕匾 */}
<View className={styles.section}>
<Text className={styles.sectionTitle}></Text>
{trays.length > 0 ? (
<View className={styles.deviceList}>
{trays.map((t) => (
<View key={t.id} className={styles.deviceItem}>
<View className={styles.deviceLeft}>
<Text className={styles.deviceIcon}>🪵</Text>
<View className={styles.deviceInfo}>
<Text className={styles.deviceName}>{t.name}</Text>
<Text className={styles.deviceMeta}>
{[t.code, t.position].filter(Boolean).join(' · ') || '无位置信息'}
</Text>
</View>
</View>
</View>
))}
</View>
) : (
<View className={styles.emptyWrap}>
<Text className={styles.emptyIcon}>📭</Text>
<Text className={styles.emptyText}></Text>
</View>
)}
</View>
{/* 当前批次 */}
<View className={styles.section}>
<Text className={styles.sectionTitle}></Text>
{batches.length > 0 ? (
<View className={styles.deviceList}>
{batches.map((b) => (
<View key={b.id} className={styles.deviceItem}>
<View className={styles.deviceLeft}>
<Text className={styles.deviceIcon}>🐛</Text>
<View className={styles.deviceInfo}>
<Text className={styles.deviceName}>{b.name}</Text>
<Text className={styles.deviceMeta}>
{batchStatusLabel(b.status)} · {b.instar ? `${b.instar}` : '龄期未设置'}
{b.variety ? ` · ${b.variety}` : ''}
</Text>
</View>
</View>
</View>
))}
</View>
) : (
<View className={styles.emptyWrap}>
<Text className={styles.emptyIcon}>📭</Text>
<Text className={styles.emptyText}></Text>
</View>
)}
</View>
</View>
);
};