fix:sample/plate 之前的开发
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { GitFork } from "lucide-react";
|
||||
import { BrapiEntityPage, type BrapiFormField } from "@/components/brapi/BrapiEntityPage";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import {
|
||||
CROSS_TYPE_OPTIONS,
|
||||
PLANNED_CROSS_STATUS_OPTIONS,
|
||||
crossTypeLabel,
|
||||
plannedStatusLabel,
|
||||
} from "../constants";
|
||||
import {
|
||||
createCrossRow,
|
||||
createPlannedCrossRow,
|
||||
normalizeCrossForm,
|
||||
normalizePlannedCrossForm,
|
||||
updateCrossRow,
|
||||
updatePlannedCrossRow,
|
||||
} from "../api";
|
||||
import { useCrossPedigree } from "../CrossPedigreeContext";
|
||||
import { NONE_SELECT_VALUE } from "../types";
|
||||
|
||||
export function CrossEntityTab() {
|
||||
const { snapshot, refresh } = useCrossPedigree();
|
||||
const [subTab, setSubTab] = useState("planned");
|
||||
|
||||
const crossingProjectOptions = snapshot?.crossingProjectOptions ?? [];
|
||||
const plannedCrossOptions = snapshot?.plannedCrossOptions ?? [];
|
||||
|
||||
const loadPlannedRows = useCallback(async () => {
|
||||
const data = await refresh(false);
|
||||
return data.plannedCrosses as unknown as Record<string, unknown>[];
|
||||
}, [refresh]);
|
||||
|
||||
const loadActualRows = useCallback(async () => {
|
||||
const data = await refresh(false);
|
||||
return data.actualCrosses as unknown as Record<string, unknown>[];
|
||||
}, [refresh]);
|
||||
|
||||
const fetchPlannedRecord = useCallback(async (id: string) => {
|
||||
const data = await refresh(false);
|
||||
const row = data.plannedCrosses.find((item) => item.id === id);
|
||||
if (!row) throw new Error("计划杂交不存在");
|
||||
return normalizePlannedCrossForm(row);
|
||||
}, [refresh]);
|
||||
|
||||
const fetchActualRecord = useCallback(async (id: string) => {
|
||||
const data = await refresh(false);
|
||||
const row = data.actualCrosses.find((item) => item.id === id);
|
||||
if (!row) throw new Error("实际杂交不存在");
|
||||
return normalizeCrossForm(row);
|
||||
}, [refresh]);
|
||||
|
||||
const plannedFields = useMemo<BrapiFormField[]>(() => [
|
||||
{
|
||||
key: "name",
|
||||
label: "计划杂交名称",
|
||||
type: "text",
|
||||
required: true,
|
||||
placeholder: "如 B73 x Mo17 / Cross-2026-001",
|
||||
},
|
||||
{
|
||||
key: "crossing_project_id",
|
||||
label: "杂交项目",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [{ value: NONE_SELECT_VALUE, label: "请选择杂交项目" }, ...crossingProjectOptions],
|
||||
},
|
||||
{
|
||||
key: "cross_type",
|
||||
label: "杂交类型",
|
||||
type: "select",
|
||||
options: [{ value: NONE_SELECT_VALUE, label: "不指定类型" }, ...CROSS_TYPE_OPTIONS],
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
type: "select",
|
||||
options: PLANNED_CROSS_STATUS_OPTIONS,
|
||||
},
|
||||
], [crossingProjectOptions]);
|
||||
|
||||
const actualFields = useMemo<BrapiFormField[]>(() => [
|
||||
{
|
||||
key: "name",
|
||||
label: "实际杂交名称",
|
||||
type: "text",
|
||||
required: true,
|
||||
placeholder: "如 B73 x Mo17 实际杂交",
|
||||
},
|
||||
{
|
||||
key: "crossing_project_id",
|
||||
label: "杂交项目",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [{ value: NONE_SELECT_VALUE, label: "请选择杂交项目" }, ...crossingProjectOptions],
|
||||
},
|
||||
{
|
||||
key: "cross_type",
|
||||
label: "杂交类型",
|
||||
type: "select",
|
||||
options: [{ value: NONE_SELECT_VALUE, label: "不指定类型" }, ...CROSS_TYPE_OPTIONS],
|
||||
},
|
||||
{
|
||||
key: "planned_cross_id",
|
||||
label: "来源计划杂交",
|
||||
type: "select",
|
||||
options: [{ value: NONE_SELECT_VALUE, label: "不关联计划杂交" }, ...plannedCrossOptions],
|
||||
},
|
||||
], [crossingProjectOptions, plannedCrossOptions]);
|
||||
|
||||
return (
|
||||
<Tabs value={subTab} onValueChange={setSubTab} className="flex min-h-full flex-col gap-4">
|
||||
<TabsList className="w-full justify-start overflow-x-auto rounded-lg border bg-white p-1 dark:border-slate-800 dark:bg-slate-950 sm:w-fit">
|
||||
<TabsTrigger value="planned">计划杂交 (planned=true)</TabsTrigger>
|
||||
<TabsTrigger value="actual">实际杂交 (planned=false)</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{subTab === "planned" ? (
|
||||
<TabsContent value="planned" className="mt-0 min-h-0 flex-1">
|
||||
<BrapiEntityPage
|
||||
icon={GitFork}
|
||||
iconBg="bg-gradient-to-br from-emerald-500 to-green-600"
|
||||
title="计划杂交"
|
||||
description="cross_entity(planned=true):录入杂交计划,亲本请在「杂交亲本」Tab 维护"
|
||||
addLabel="新增计划杂交"
|
||||
useEnhancedDialog
|
||||
columns={[
|
||||
{ key: "plannedCrossDbId", label: "Cross ID" },
|
||||
{ key: "name", label: "名称" },
|
||||
{ key: "crossing_project_name", label: "杂交项目" },
|
||||
{ key: "cross_type", label: "类型", render: crossTypeLabel },
|
||||
{ key: "status", label: "状态", render: plannedStatusLabel },
|
||||
]}
|
||||
fields={plannedFields}
|
||||
data={[]}
|
||||
stats={[
|
||||
{
|
||||
label: "/brapi/v2/plannedcrosses",
|
||||
value: "BrAPI",
|
||||
className: "bg-emerald-50 text-emerald-700 dark:bg-emerald-400/10 dark:text-emerald-200",
|
||||
},
|
||||
]}
|
||||
loadData={loadPlannedRows}
|
||||
fetchRecord={fetchPlannedRecord}
|
||||
createRecord={(payload) => createPlannedCrossRow(payload) as unknown as Promise<Record<string, unknown>>}
|
||||
updateRecord={(id, payload) => updatePlannedCrossRow(id, payload) as unknown as Promise<Record<string, unknown>>}
|
||||
/>
|
||||
</TabsContent>
|
||||
) : null}
|
||||
|
||||
{subTab === "actual" ? (
|
||||
<TabsContent value="actual" className="mt-0 min-h-0 flex-1">
|
||||
<BrapiEntityPage
|
||||
icon={GitFork}
|
||||
iconBg="bg-gradient-to-br from-green-600 to-emerald-700"
|
||||
title="实际杂交"
|
||||
description="cross_entity(planned=false):完成实际杂交后可关联来源计划杂交;亲本请在「杂交亲本」Tab 维护"
|
||||
addLabel="新增实际杂交"
|
||||
useEnhancedDialog
|
||||
columns={[
|
||||
{ key: "crossDbId", label: "Cross ID" },
|
||||
{ key: "name", label: "名称" },
|
||||
{ key: "crossing_project_name", label: "杂交项目" },
|
||||
{ key: "plannedCrossName", label: "来源计划杂交" },
|
||||
{ key: "cross_type", label: "类型", render: crossTypeLabel },
|
||||
]}
|
||||
fields={actualFields}
|
||||
data={[]}
|
||||
stats={[
|
||||
{
|
||||
label: "/brapi/v2/crosses",
|
||||
value: "BrAPI",
|
||||
className: "bg-green-50 text-green-700 dark:bg-green-400/10 dark:text-green-200",
|
||||
},
|
||||
]}
|
||||
loadData={loadActualRows}
|
||||
fetchRecord={fetchActualRecord}
|
||||
createRecord={(payload) => createCrossRow(payload) as unknown as Promise<Record<string, unknown>>}
|
||||
updateRecord={(id, payload) => updateCrossRow(id, payload) as unknown as Promise<Record<string, unknown>>}
|
||||
/>
|
||||
</TabsContent>
|
||||
) : null}
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user