feat: 补齐消毒、种源与二维码身份链

This commit is contained in:
weijuesen
2026-08-14 08:39:56 +08:00
parent cc93c9a053
commit 6b222abb7b
24 changed files with 1344 additions and 9 deletions
@@ -0,0 +1,62 @@
.page {
min-height: 100vh;
padding: 24px;
background: #f5f6f7;
}
.card {
background: #ffffff;
border-radius: 12px;
padding: 24px;
margin-bottom: 16px;
}
.title {
display: block;
font-size: 32px;
font-weight: 600;
margin-bottom: 20px;
}
.label {
display: block;
font-size: 26px;
color: #4e5969;
margin: 18px 0 8px;
}
.input {
height: 76px;
background: #f2f3f5;
border-radius: 8px;
padding: 0 20px;
font-size: 28px;
}
.btnPrimary {
margin-top: 24px;
background: #10b981;
color: #ffffff;
border-radius: 8px;
font-size: 28px;
}
.entityBox {
margin-top: 20px;
padding: 20px;
background: #f0fdf4;
border-radius: 8px;
}
.entityType {
display: block;
color: #4e5969;
font-size: 24px;
}
.entityName {
display: block;
margin-top: 8px;
font-size: 30px;
font-weight: 600;
}
+127
View File
@@ -0,0 +1,127 @@
import React, { useState } from 'react';
import { Button, Input, Text, View } from '@tarojs/components';
import Taro from '@tarojs/taro';
import styles from './index.module.scss';
import { createDisinfectionRecord, resolveQR, type QRResolveResult } from '@/api/biosecurity';
const BiosecurityPage: React.FC = () => {
const [entity, setEntity] = useState<QRResolveResult | null>(null);
const [agent, setAgent] = useState('');
const [concentration, setConcentration] = useState('');
const [amount, setAmount] = useState('');
const [note, setNote] = useState('');
const [submitting, setSubmitting] = useState(false);
const handleScan = async () => {
try {
const res = await Taro.scanCode({ scanType: ['qrCode'] });
if (!res.result) throw new Error('未识别到二维码');
const data = await resolveQR(res.result);
setEntity(data);
Taro.showToast({ title: '扫码成功', icon: 'success' });
} catch (err) {
Taro.showToast({
title: err instanceof Error ? err.message : '扫码失败',
icon: 'none',
});
}
};
const handleSubmit = async () => {
if (!entity) {
Taro.showToast({ title: '请先扫码', icon: 'none' });
return;
}
if (!agent || !concentration) {
Taro.showToast({ title: '请填写药剂和浓度', icon: 'none' });
return;
}
setSubmitting(true);
try {
await createDisinfectionRecord({
kind: 'execution',
roomId: entity.entity.roomId,
batchId: entity.entityType === 'batch' ? entity.entity.id : entity.entity.batchId,
agent,
concentration,
amount: amount || undefined,
note: note || undefined,
});
Taro.showToast({ title: '消毒记录已提交', icon: 'success' });
setAgent('');
setConcentration('');
setAmount('');
setNote('');
} catch (err) {
Taro.showToast({
title: err instanceof Error ? err.message : '提交失败',
icon: 'none',
});
} finally {
setSubmitting(false);
}
};
return (
<View className={styles.page}>
<View className={styles.card}>
<Text className={styles.title}></Text>
<Button className={styles.btnPrimary} onClick={handleScan}>
//
</Button>
{entity ? (
<View className={styles.entityBox}>
<Text className={styles.entityType}>{entity.entityType}</Text>
<Text className={styles.entityName}>
{entity.entity.name || entity.entity.sampleNo || entity.entity.id}
</Text>
</View>
) : null}
</View>
{entity ? (
<View className={styles.card}>
<Text className={styles.title}></Text>
<Text className={styles.label}></Text>
<Input
className={styles.input}
value={agent}
placeholder="例如:漂白粉"
onInput={(e) => setAgent(e.detail.value)}
/>
<Text className={styles.label}></Text>
<Input
className={styles.input}
value={concentration}
placeholder="例如:1%"
onInput={(e) => setConcentration(e.detail.value)}
/>
<Text className={styles.label}></Text>
<Input
className={styles.input}
value={amount}
placeholder="可选"
onInput={(e) => setAmount(e.detail.value)}
/>
<Text className={styles.label}></Text>
<Input
className={styles.input}
value={note}
placeholder="可选"
onInput={(e) => setNote(e.detail.value)}
/>
<Button
className={styles.btnPrimary}
loading={submitting}
disabled={submitting}
onClick={handleSubmit}
>
</Button>
</View>
) : null}
</View>
);
};
export default BiosecurityPage;
+8
View File
@@ -59,6 +59,9 @@ const SettingsPage: React.FC = () => {
case 'lamp':
Taro.navigateTo({ url: '/pages/lamp/index' });
break;
case 'biosecurity':
Taro.navigateTo({ url: '/pages/biosecurity/index' });
break;
case 'about':
Taro.showModal({
title: '关于',
@@ -190,6 +193,11 @@ const SettingsPage: React.FC = () => {
<Text className={styles.menuLabel}>LAMP </Text>
<Text className={styles.menuArrow}></Text>
</View>
<View className={styles.menuItem} onClick={() => handleMenuTap('biosecurity')}>
<Text className={styles.menuIcon}>🛡</Text>
<Text className={styles.menuLabel}></Text>
<Text className={styles.menuArrow}></Text>
</View>
<View className={styles.menuItem} onClick={() => handleMenuTap('wsStatus')}>
<Text className={styles.menuIcon}>🔗</Text>
<Text className={styles.menuLabel}></Text>