feat: 多检测方式推荐引擎(#20,设备/紧急/操作者/成本偏好)
This commit is contained in:
@@ -71,3 +71,15 @@ export const uploadSpectrumEntryFile = (file: File) => {
|
||||
return post<{ url: string }>('/spectrum-entries/upload', fd);
|
||||
};
|
||||
export const deleteSpectrumEntry = (id: string) => del(`/spectrum-entries/${id}`);
|
||||
|
||||
export interface DetectionRecommendation {
|
||||
method: string;
|
||||
name: string;
|
||||
reason: string;
|
||||
time: string;
|
||||
cost: string;
|
||||
difficulty: string;
|
||||
}
|
||||
|
||||
export const recommendDetectionMethod = (params: any) =>
|
||||
get<DetectionRecommendation[]>('/detection-methods/recommend', { params });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Button, Checkbox, Form, Input, Modal, Popconfirm, Radio, Select, Switch, Tabs, Tag, Upload, message,
|
||||
Button, Card, Checkbox, Form, Input, Modal, Popconfirm, Radio, Select, Space, Switch, Tabs, Tag, Upload, message,
|
||||
} from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import type { UploadFile } from 'antd';
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
createSpectrumEntry,
|
||||
uploadSpectrumEntryFile,
|
||||
deleteSpectrumEntry,
|
||||
recommendDetectionMethod,
|
||||
type DetectionRecommendation,
|
||||
type LampTest,
|
||||
type LampTestStep,
|
||||
type SpectrumEntry,
|
||||
@@ -66,6 +68,11 @@ function LampTab() {
|
||||
const [steps, setSteps] = useState<LampTestStep[]>([]);
|
||||
const [ctValues, setCtValues] = useState('');
|
||||
const [threshold, setThreshold] = useState<number>(35);
|
||||
const [recDevices, setRecDevices] = useState({ hasLamp: true, hasSers: false, hasQpcr: false, hasHyperspectral: false });
|
||||
const [recUrgency, setRecUrgency] = useState('routine');
|
||||
const [recOperator, setRecOperator] = useState('expert');
|
||||
const [recCost, setRecCost] = useState('balanced');
|
||||
const [recommendations, setRecommendations] = useState<DetectionRecommendation[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
@@ -200,6 +207,84 @@ function LampTab() {
|
||||
<Form.Item label="蚕房" name="roomId">
|
||||
<Select allowClear options={rooms.map((r) => ({ value: r.id, label: r.name }))} />
|
||||
</Form.Item>
|
||||
<Card size="small" title="推荐检测方式" style={{ marginBottom: 16 }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Space wrap>
|
||||
<Checkbox
|
||||
checked={recDevices.hasLamp}
|
||||
onChange={(e) => setRecDevices((p) => ({ ...p, hasLamp: e.target.checked }))}
|
||||
>
|
||||
LAMP 设备
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checked={recDevices.hasSers}
|
||||
onChange={(e) => setRecDevices((p) => ({ ...p, hasSers: e.target.checked }))}
|
||||
>
|
||||
SERS 设备
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checked={recDevices.hasQpcr}
|
||||
onChange={(e) => setRecDevices((p) => ({ ...p, hasQpcr: e.target.checked }))}
|
||||
>
|
||||
qPCR 设备
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checked={recDevices.hasHyperspectral}
|
||||
onChange={(e) => setRecDevices((p) => ({ ...p, hasHyperspectral: e.target.checked }))}
|
||||
>
|
||||
高光谱
|
||||
</Checkbox>
|
||||
</Space>
|
||||
<Space wrap>
|
||||
<Select
|
||||
value={recUrgency}
|
||||
style={{ width: 110 }}
|
||||
onChange={setRecUrgency}
|
||||
options={[
|
||||
{ value: 'routine', label: '常规' },
|
||||
{ value: 'urgent', label: '紧急' },
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
value={recOperator}
|
||||
style={{ width: 110 }}
|
||||
onChange={setRecOperator}
|
||||
options={[
|
||||
{ value: 'expert', label: '熟练' },
|
||||
{ value: 'novice', label: '新手' },
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
value={recCost}
|
||||
style={{ width: 130 }}
|
||||
onChange={setRecCost}
|
||||
options={[
|
||||
{ value: 'balanced', label: '平衡' },
|
||||
{ value: 'low_cost', label: '最低成本' },
|
||||
{ value: 'fastest', label: '最快' },
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const res = await recommendDetectionMethod({ ...recDevices, urgency: recUrgency, operatorLevel: recOperator, costPreference: recCost });
|
||||
setRecommendations(res);
|
||||
}}
|
||||
>
|
||||
获取推荐
|
||||
</Button>
|
||||
</Space>
|
||||
{recommendations.map((r) => (
|
||||
<div key={r.method}>
|
||||
<Tag color="blue">{r.name}</Tag>
|
||||
<span style={{ marginRight: 8 }}>{r.time} · {r.difficulty}</span>
|
||||
<div style={{ color: '#666', fontSize: 12 }}>{r.reason}</div>
|
||||
</div>
|
||||
))}
|
||||
{recommendations.length === 0 ? (
|
||||
<span style={{ color: '#999' }}>勾选可用设备后点击获取推荐</span>
|
||||
) : null}
|
||||
</Space>
|
||||
</Card>
|
||||
<Form.Item label="检测方式" name="method" initialValue="lamp">
|
||||
<Select options={Object.entries(METHOD_LABELS).map(([value, label]) => ({ value, label }))} />
|
||||
</Form.Item>
|
||||
|
||||
Reference in New Issue
Block a user