refactor(frontend): align TJWater Next integrations
This commit is contained in:
+3
-3
@@ -26,11 +26,11 @@ jest.mock("./SchemeQuery", () => ({
|
||||
onEdit,
|
||||
active,
|
||||
}: {
|
||||
onEdit: (schemeId: number) => void;
|
||||
onEdit: (schemeId: string) => void;
|
||||
active?: boolean;
|
||||
}) => {
|
||||
mockSchemeQueryRender(active);
|
||||
return <button onClick={() => onEdit(7)}>打开测试方案</button>;
|
||||
return <button onClick={() => onEdit("run-7")}>打开测试方案</button>;
|
||||
},
|
||||
createMonitoringSchemeQueryState: () => ({}),
|
||||
}));
|
||||
@@ -61,7 +61,7 @@ jest.mock("@components/olmap/common/PanelEmptyState", () => ({
|
||||
const mockGetSensorPlacementScheme = jest.mocked(getSensorPlacementScheme);
|
||||
|
||||
const scheme: SensorPlacementScheme = {
|
||||
id: 7,
|
||||
id: "run-7",
|
||||
scheme_name: "测试方案",
|
||||
sensor_number: 1,
|
||||
min_diameter: 100,
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ const MonitoringPlaceOptimizationPanel: React.FC<
|
||||
const [activeScheme, setActiveScheme] =
|
||||
useState<SensorPlacementScheme | null>(null);
|
||||
const [loadingScheme, setLoadingScheme] = useState(false);
|
||||
const [pendingOpenSchemeId, setPendingOpenSchemeId] = useState<number | null>(
|
||||
const [pendingOpenSchemeId, setPendingOpenSchemeId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const { open: notify } = useNotification();
|
||||
@@ -138,7 +138,7 @@ const MonitoringPlaceOptimizationPanel: React.FC<
|
||||
setPendingOpenSchemeId(null);
|
||||
}, []);
|
||||
|
||||
const handleOpenScheme = async (schemeId: number) => {
|
||||
const handleOpenScheme = async (schemeId: string) => {
|
||||
setLoadingScheme(true);
|
||||
try {
|
||||
const scheme = await getSensorPlacementScheme(schemeId);
|
||||
|
||||
@@ -98,7 +98,7 @@ const OptimizationParameters: React.FC<OptimizationParametersProps> = ({
|
||||
|
||||
try {
|
||||
const created = await optimizeSensorPlacement({
|
||||
scheme_name: normalizeSchemeName(schemeName),
|
||||
run_name: normalizeSchemeName(schemeName),
|
||||
sensor_type: "pressure",
|
||||
method: method as "sensitivity" | "kmeans",
|
||||
sensor_count: sensorCount,
|
||||
|
||||
@@ -146,7 +146,7 @@ jest.mock("./schemeApi", () => ({
|
||||
}));
|
||||
|
||||
const scheme: SensorPlacementScheme = {
|
||||
id: 1,
|
||||
id: "run-1",
|
||||
scheme_name: "测试方案",
|
||||
sensor_number: 1,
|
||||
min_diameter: 300,
|
||||
|
||||
@@ -24,9 +24,8 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||
import "dayjs/locale/zh-cn"; // 引入中文包
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import { api } from "@/lib/api";
|
||||
import moment from "moment";
|
||||
import { config, NETWORK_NAME } from "@config/config";
|
||||
import { NETWORK_NAME } from "@config/config";
|
||||
import { useNotification } from "@refinedev/core";
|
||||
import { useMap } from "@components/olmap/core/MapComponent";
|
||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||
@@ -40,22 +39,13 @@ import { bbox, featureCollection } from "@turf/turf";
|
||||
import type { SchemeRecord } from "./types";
|
||||
import { useSchemeCreatorName } from "@components/olmap/core/useSchemeCreatorName";
|
||||
import { SchemeQueryEmptyState } from "@components/olmap/common/PanelEmptyState";
|
||||
|
||||
interface SchemaItem {
|
||||
id: number;
|
||||
scheme_name: string;
|
||||
sensor_number: number;
|
||||
min_diameter: number;
|
||||
username: string;
|
||||
create_time: string;
|
||||
sensor_location?: string[];
|
||||
}
|
||||
import { listSensorPlacementSchemes } from "./schemeApi";
|
||||
|
||||
interface SchemeQueryProps {
|
||||
schemes?: SchemeRecord[];
|
||||
active?: boolean;
|
||||
onSchemesChange?: (schemes: SchemeRecord[]) => void;
|
||||
onEdit?: (id: number) => void;
|
||||
onEdit?: (id: string) => void;
|
||||
network?: string;
|
||||
state?: MonitoringSchemeQueryState;
|
||||
onStateChange?: (state: MonitoringSchemeQueryState) => void;
|
||||
@@ -64,7 +54,7 @@ interface SchemeQueryProps {
|
||||
export interface MonitoringSchemeQueryState {
|
||||
queryAll: boolean;
|
||||
queryDate: Dayjs | null;
|
||||
expandedId: number | null;
|
||||
expandedId: string | null;
|
||||
hasQueried: boolean;
|
||||
}
|
||||
|
||||
@@ -185,22 +175,18 @@ const SchemeQuery: React.FC<SchemeQueryProps> = ({
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await api.get(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-schemes`,
|
||||
);
|
||||
|
||||
let filteredResults = response.data;
|
||||
let filteredResults = await listSensorPlacementSchemes();
|
||||
|
||||
// 按日期过滤
|
||||
if (!queryAll && queryDate) {
|
||||
const formattedDate = queryDate.format("YYYY-MM-DD");
|
||||
filteredResults = filteredResults.filter((item: SchemaItem) => {
|
||||
filteredResults = filteredResults.filter((item) => {
|
||||
const itemDate = moment(item.create_time).format("YYYY-MM-DD");
|
||||
return itemDate === formattedDate;
|
||||
});
|
||||
}
|
||||
|
||||
const nextSchemes = filteredResults.map((item: SchemaItem) => ({
|
||||
const nextSchemes = filteredResults.map((item) => ({
|
||||
id: item.id,
|
||||
schemeName: item.scheme_name,
|
||||
sensorNumber: item.sensor_number,
|
||||
@@ -250,7 +236,7 @@ const SchemeQuery: React.FC<SchemeQueryProps> = ({
|
||||
}
|
||||
|
||||
if (sensorIds.length > 0) {
|
||||
queryFeaturesByIds(sensorIds, "geo_junctions_mat").then((features) => {
|
||||
queryFeaturesByIds(sensorIds, "junctions").then((features) => {
|
||||
if (features.length > 0) {
|
||||
// 设置高亮要素
|
||||
setHighlightFeatures(features);
|
||||
@@ -271,7 +257,7 @@ const SchemeQuery: React.FC<SchemeQueryProps> = ({
|
||||
};
|
||||
|
||||
// 查看详情(展开/收起)
|
||||
const handleViewDetails = (id: number) => {
|
||||
const handleViewDetails = (id: string) => {
|
||||
setQueryField("expandedId", expandedId === id ? null : id);
|
||||
};
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ const point = (
|
||||
const rows = [point("A", 1, 400, 200), point("B", 2, 500, 300)];
|
||||
|
||||
const scheme: SensorPlacementScheme = {
|
||||
id: 1,
|
||||
id: "run-1",
|
||||
scheme_name: "测试方案",
|
||||
sensor_number: rows.length,
|
||||
min_diameter: 300,
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { api } from "@/lib/api";
|
||||
import {
|
||||
exportSensorPlacementExcel,
|
||||
getSensorPlacementScheme,
|
||||
listSensorPlacementSchemes,
|
||||
optimizeSensorPlacement,
|
||||
overwriteSensorPlacementScheme,
|
||||
} from "./schemeApi";
|
||||
|
||||
jest.mock("@/lib/api", () => ({
|
||||
api: {
|
||||
get: jest.fn(),
|
||||
post: jest.fn(),
|
||||
put: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const run = {
|
||||
run_id: "9fb546cb-5f72-4ddd-8a46-d13e9aee2c6b",
|
||||
name: "sensor-plan-a",
|
||||
sensor_count: 2,
|
||||
min_diameter: 300,
|
||||
created_by: "operator",
|
||||
created_at: "2026-08-25T08:00:00Z",
|
||||
sensor_locations: ["J-1", "J-2"],
|
||||
sensor_points: [],
|
||||
can_edit: true,
|
||||
status: "completed",
|
||||
};
|
||||
|
||||
describe("sensor placement runs API adapter", () => {
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
|
||||
it("lists paged runs and maps run fields to the existing screen model", async () => {
|
||||
jest.mocked(api.get).mockResolvedValue({
|
||||
data: { items: [run], total: 1, limit: 1000, offset: 0 },
|
||||
});
|
||||
|
||||
await expect(listSensorPlacementSchemes()).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: run.run_id,
|
||||
scheme_name: run.name,
|
||||
sensor_number: 2,
|
||||
sensor_location: ["J-1", "J-2"],
|
||||
}),
|
||||
]);
|
||||
expect(api.get).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/sensor-placement-runs"),
|
||||
{ params: { limit: 1000, offset: 0 } },
|
||||
);
|
||||
});
|
||||
|
||||
it("uses run endpoints for creation and detail lookup", async () => {
|
||||
jest.mocked(api.post).mockResolvedValue({ data: run });
|
||||
jest.mocked(api.get).mockResolvedValue({ data: run });
|
||||
|
||||
await optimizeSensorPlacement({
|
||||
run_name: run.name,
|
||||
sensor_type: "pressure",
|
||||
method: "sensitivity",
|
||||
sensor_count: 2,
|
||||
min_diameter: 300,
|
||||
});
|
||||
await getSensorPlacementScheme(run.run_id);
|
||||
|
||||
expect(api.post).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/\/api\/v1\/sensor-placement-runs$/),
|
||||
expect.objectContaining({ run_name: run.name }),
|
||||
);
|
||||
expect(api.get).toHaveBeenLastCalledWith(
|
||||
expect.stringContaining(`/api/v1/sensor-placement-runs/${run.run_id}`),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses the plural result fields required by update and export", async () => {
|
||||
jest.mocked(api.put).mockResolvedValue({ data: run });
|
||||
jest.mocked(api.post).mockResolvedValue({ data: new Blob() });
|
||||
|
||||
await overwriteSensorPlacementScheme(run.run_id, ["J-1"], ["J-2"]);
|
||||
await exportSensorPlacementExcel(run.run_id, ["J-2"], { "J-2": "added" });
|
||||
|
||||
expect(api.put).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`/sensor-placement-runs/${run.run_id}`),
|
||||
{
|
||||
expected_sensor_locations: ["J-1"],
|
||||
sensor_locations: ["J-2"],
|
||||
},
|
||||
);
|
||||
expect(api.post).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`/sensor-placement-runs/${run.run_id}/exports/excel`),
|
||||
{
|
||||
sensor_locations: ["J-2"],
|
||||
adjustment_status: { "J-2": "added" },
|
||||
},
|
||||
{ responseType: "blob" },
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -7,30 +7,67 @@ import type {
|
||||
} from "./types";
|
||||
|
||||
export interface OptimizeSchemeInput {
|
||||
scheme_name: string;
|
||||
run_name: string;
|
||||
sensor_type: "pressure";
|
||||
method: "sensitivity" | "kmeans";
|
||||
sensor_count: number;
|
||||
min_diameter: number;
|
||||
}
|
||||
|
||||
type SensorPlacementRunResponse = {
|
||||
run_id: string;
|
||||
name: string;
|
||||
sensor_count: number;
|
||||
min_diameter: number;
|
||||
created_by: string;
|
||||
created_at: string;
|
||||
sensor_locations: string[];
|
||||
sensor_points: SensorPoint[];
|
||||
can_edit: boolean;
|
||||
};
|
||||
|
||||
const toSensorPlacementScheme = (
|
||||
run: SensorPlacementRunResponse,
|
||||
): SensorPlacementScheme => ({
|
||||
id: run.run_id,
|
||||
scheme_name: run.name,
|
||||
sensor_number: run.sensor_count,
|
||||
min_diameter: run.min_diameter,
|
||||
username: run.created_by,
|
||||
create_time: run.created_at,
|
||||
sensor_location: run.sensor_locations,
|
||||
sensor_points: run.sensor_points,
|
||||
can_edit: run.can_edit,
|
||||
});
|
||||
|
||||
export const optimizeSensorPlacement = async (
|
||||
input: OptimizeSchemeInput,
|
||||
): Promise<SensorPlacementScheme> => {
|
||||
const response = await api.post<SensorPlacementScheme>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-optimization-runs`,
|
||||
const response = await api.post<SensorPlacementRunResponse>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-runs`,
|
||||
input,
|
||||
);
|
||||
return response.data;
|
||||
return toSensorPlacementScheme(response.data);
|
||||
};
|
||||
|
||||
export const listSensorPlacementSchemes = async (): Promise<
|
||||
SensorPlacementScheme[]
|
||||
> => {
|
||||
const response = await api.get<{
|
||||
items: SensorPlacementRunResponse[];
|
||||
}>(`${config.BACKEND_URL}/api/v1/sensor-placement-runs`, {
|
||||
params: { limit: 1000, offset: 0 },
|
||||
});
|
||||
return response.data.items.map(toSensorPlacementScheme);
|
||||
};
|
||||
|
||||
export const getSensorPlacementScheme = async (
|
||||
schemeId: number,
|
||||
schemeId: string,
|
||||
): Promise<SensorPlacementScheme> => {
|
||||
const response = await api.get<SensorPlacementScheme>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-schemes/${schemeId}`,
|
||||
const response = await api.get<SensorPlacementRunResponse>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-runs/${encodeURIComponent(schemeId)}`,
|
||||
);
|
||||
return response.data;
|
||||
return toSensorPlacementScheme(response.data);
|
||||
};
|
||||
|
||||
export const getSensorPlacementCandidate = async (
|
||||
@@ -43,29 +80,29 @@ export const getSensorPlacementCandidate = async (
|
||||
};
|
||||
|
||||
export const overwriteSensorPlacementScheme = async (
|
||||
schemeId: number,
|
||||
schemeId: string,
|
||||
expectedSensorLocation: string[],
|
||||
sensorLocation: string[],
|
||||
): Promise<SensorPlacementScheme> => {
|
||||
const response = await api.put<SensorPlacementScheme>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-schemes/${schemeId}`,
|
||||
const response = await api.put<SensorPlacementRunResponse>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-runs/${encodeURIComponent(schemeId)}`,
|
||||
{
|
||||
expected_sensor_location: expectedSensorLocation,
|
||||
sensor_location: sensorLocation,
|
||||
expected_sensor_locations: expectedSensorLocation,
|
||||
sensor_locations: sensorLocation,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
return toSensorPlacementScheme(response.data);
|
||||
};
|
||||
|
||||
export const exportSensorPlacementExcel = async (
|
||||
schemeId: number,
|
||||
schemeId: string,
|
||||
sensorLocation: string[],
|
||||
adjustmentStatus: Record<string, AdjustmentStatus>,
|
||||
) => {
|
||||
const response = await api.post<Blob>(
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-schemes/${schemeId}/exports/excel`,
|
||||
`${config.BACKEND_URL}/api/v1/sensor-placement-runs/${encodeURIComponent(schemeId)}/exports/excel`,
|
||||
{
|
||||
sensor_location: sensorLocation,
|
||||
sensor_locations: sensorLocation,
|
||||
adjustment_status: adjustmentStatus,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ const point = (node_id: string): SensorPoint => ({
|
||||
});
|
||||
|
||||
const scheme: SensorPlacementScheme = {
|
||||
id: 1,
|
||||
id: "run-1",
|
||||
scheme_name: "测试方案",
|
||||
sensor_number: 2,
|
||||
min_diameter: 300,
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface SensorPoint {
|
||||
}
|
||||
|
||||
export interface SensorPlacementScheme {
|
||||
id: number;
|
||||
id: string;
|
||||
scheme_name: string;
|
||||
sensor_number: number;
|
||||
min_diameter: number;
|
||||
@@ -25,7 +25,7 @@ export interface SensorPlacementScheme {
|
||||
}
|
||||
|
||||
export interface SchemeRecord {
|
||||
id: number;
|
||||
id: string;
|
||||
schemeName: string;
|
||||
sensorNumber: number;
|
||||
minDiameter: number;
|
||||
|
||||
Reference in New Issue
Block a user