Compare commits

...

3 Commits
master ... dev

  1. 1
      src/assets/icon_arr_down_s.svg
  2. 1
      src/assets/icon_dest.svg
  3. 1
      src/assets/icon_selected.svg
  4. 1
      src/assets/icon_track.svg
  5. 1
      src/assets/icon_unselect.svg
  6. 6
      src/index.tsx
  7. 115
      src/pages/measure/components/MeasureAction.tsx
  8. 112
      src/pages/measure/components/MeasureConfig.tsx
  9. 13
      src/pages/measure/components/RadioItem.tsx
  10. 17
      src/pages/measure/components/SelectorBtn.tsx
  11. 10
      src/services/apiTypes.ts
  12. 16
      src/services/calibration/calibration.ts
  13. 12
      src/services/device/deviceState.ts
  14. 134
      src/services/measure/analysis.ts
  15. 16
      src/services/standardRail/standardRail.ts
  16. 121
      src/store/features/baseDataSlice.ts
  17. 36
      src/store/features/contextSlice.ts
  18. 4
      src/store/index.ts

1
src/assets/icon_arr_down_s.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="11.874804496765137" height="8" viewBox="0 0 11.874804496765137 8"><g><path d="M11.7502,0C11.7502,0,10.5783,0,10.5783,0C10.4986,0,10.4236,0.0390626,10.3768,0.103125C10.3768,0.103125,5.9377,6.22188,5.9377,6.22188C5.9377,6.22188,1.49864,0.103125,1.49864,0.103125C1.45176,0.0390626,1.37676,0,1.29707,0C1.29707,0,0.125198,0,0.125198,0C0.0236354,0,-0.0357396,0.115625,0.0236354,0.198438C0.0236354,0.198438,5.53301,7.79375,5.53301,7.79375C5.73301,8.06875,6.14239,8.06875,6.34082,7.79375C6.34082,7.79375,11.8502,0.198438,11.8502,0.198438C11.9111,0.115625,11.8518,0,11.7502,0C11.7502,0,11.7502,0,11.7502,0Z" fill="#000000" fill-opacity="0.25"/></g></svg>

1
src/assets/icon_dest.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="30" height="30" viewBox="0 0 30 30"><defs><clipPath id="master_svg0_3_00832"><rect x="0" y="0" width="30" height="30" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_3_00832)"><g><path d="M17.522706562499998,15.976019921875L12.4769035625,15.976019921875C11.9401435625,15.976019921875,11.5009765625,15.536859921875,11.5009765625,15.000096921875C11.5009765625,14.463336921875,11.9401435625,14.024169921875,12.4769035625,14.024169921875L17.522706562499998,14.024169921875C18.0594665625,14.024169921875,18.4986365625,14.463336921875,18.4986365625,15.000096921875C18.4986365625,15.536859921875,18.0594665625,15.976019921875,17.522706562499998,15.976019921875Z" fill="#5F5F5F" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M15.9759,12.4771L15.9759,17.5229C15.9759,18.0597,15.5368,18.4989,15,18.4989C14.4633,18.4989,14.0241,18.0597,14.0241,17.5229L14.0241,12.4771C14.0241,11.9403,14.4633,11.5011,15,11.5011C15.5368,11.5011,15.9759,11.9403,15.9759,12.4771ZM6.02305,15.9759L0.97718,15.9759C0.44042,15.9759,0.00125248,15.5368,0.00125248,15C0.00125248,14.4632,0.44042,14.0241,0.97718,14.0241L6.02302,14.0241C6.55978,14.0241,6.99894,14.4632,6.99894,15C6.99898,15.5368,6.55981,15.9759,6.02305,15.9759ZM29.0229,15.9759L23.977,15.9759C23.4402,15.9759,23.0011,15.5368,23.0011,15C23.0011,14.4632,23.4402,14.0241,23.977,14.0241L29.0229,14.0241C29.5596,14.0241,29.9988,14.4632,29.9988,15C29.9988,15.5368,29.5596,15.9759,29.0229,15.9759ZM16.021,0.975927L16.021,6.02183C16.021,6.55859,15.5818,6.99776,15.045,6.99776C14.5083,6.99776,14.0691,6.55859,14.0691,6.02183L14.0691,0.975927C14.0691,0.439167,14.5083,0,15.045,0C15.5818,0,16.021,0.439167,16.021,0.975927ZM16.021,23.9782L16.021,29.0241C16.021,29.5608,15.5818,30,15.045,30C14.5083,30,14.0691,29.5608,14.0691,29.0241L14.0691,23.9782C14.0691,23.4414,14.5083,23.0023,15.045,23.0023C15.5818,23.0023,16.021,23.4414,16.021,23.9782Z" fill="#5F5F5F" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M15.04506640625,27.683050390625C13.34586640625,27.683050390625,11.69707640625,27.350050390625,10.144346406250001,26.693350390625C8.645096406250001,26.059150390625,7.29883640625,25.151550390625,6.14294640625,23.995650390625C4.98705640625,22.839850390625,4.07944640625,21.493550390625,3.44531940625,19.994250390625C2.78858540625,18.441550390625,2.45556640625,16.792750390625002,2.45556640625,15.093650390625C2.45556640625,13.394450390625,2.78855240625,11.745630390625,3.44531940625,10.192930390625001C4.07944640625,8.693680390625001,4.98708640625,7.347380390625,6.14294640625,6.191530390625C7.29879640625,5.035670390625,8.645096406250001,4.128030390625,10.144346406250001,3.493903390625C11.69707640625,2.837169390625,13.34586640625,2.504150390625,15.04506640625,2.504150390625C16.744166406250002,2.504150390625,18.39296640625,2.837136390625,19.94566640625,3.493903390625C21.44496640625,4.128030390625,22.79126640625,5.035640390625,23.94706640625,6.191530390625C25.10296640625,7.347420390625,26.01056640625,8.693680390625001,26.64476640625,10.192930390625001C27.30146640625,11.745630390625,27.63446640625,13.394450390625,27.63446640625,15.093650390625C27.63446640625,16.792750390625002,27.30146640625,18.441550390625,26.64476640625,19.994250390625C26.01056640625,21.493550390625,25.10296640625,22.839850390625,23.94706640625,23.995650390625C22.79126640625,25.151550390625,21.44496640625,26.059150390625,19.94566640625,26.693350390625C18.39296640625,27.350050390625,16.744166406250002,27.683050390625,15.04506640625,27.683050390625ZM15.04506640625,4.456000390625C9.179446406250001,4.456000390625,4.40741640625,9.228030390625001,4.40741640625,15.093650390625C4.40741640625,20.959250390625,9.179446406250001,25.731250390625,15.04506640625,25.731250390625C20.91066640625,25.731250390625,25.68266640625,20.959250390625,25.68266640625,15.093650390625C25.68266640625,9.228030390625001,20.91056640625,4.456000390625,15.04506640625,4.456000390625Z" fill="#5F5F5F" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>

1
src/assets/icon_selected.svg
File diff suppressed because it is too large
View File

1
src/assets/icon_track.svg
File diff suppressed because it is too large
View File

1
src/assets/icon_unselect.svg
File diff suppressed because it is too large
View File

6
src/index.tsx

@ -70,13 +70,13 @@ const router = createBrowserRouter([
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
// <React.StrictMode>
<React.StrictMode>
<Provider store={store}>
<ConfigProvider locale={zhCN}>
<RouterProvider router={router}/>
<RouterProvider router={router} />
</ConfigProvider>
</Provider>
// </React.StrictMode>
</React.StrictMode>
);
console.log(process.env.REACT_APP_WS_URL);

115
src/pages/measure/components/MeasureAction.tsx

@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { Button, Checkbox, CheckboxProps, Drawer, message, Switch } from "antd";
import { Button, Checkbox, CheckboxProps, Drawer, message, Modal, Switch } from "antd";
import { useNavigate } from "react-router";
import {
fetchAnalysisReport,
@ -8,14 +8,19 @@ import {
startMeasurement,
} from "../../../services/measure/analysis";
import { createWebSocket, sharedWsUrl } from "../../../services/socket";
import { switchMeasureAfterSave } from "../../../store/features/contextSlice";
import { switchMeasureAfterSave, updateCalibrationTypeId, updateRailTypeId } from "../../../store/features/contextSlice";
import { AnalysisReport } from "../../../services/measure/type";
import { MeasureState, TaskState, TrackRecordSig } from "../../../services/wsTypes";
import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
import Gr_round from "../../../assets/green_round.svg";
import Bl_round from "../../../assets/blue_round.svg";
import icon_track from "../../../assets/icon_track.svg";
import icon_dest from "../../../assets/icon_dest.svg";
import MeasurementCanvas, { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from "./konva/MeasurementCanvas";
import "./MeasureAction.scss";
import SelectorBtn from "./SelectorBtn";
import RadioItem from "./RadioItem";
import { fetchCalibrationTypes, fetchRailTypes } from "../../../store/features/baseDataSlice";
// 创建 websocket 客户端
const wsClient = createWebSocket(sharedWsUrl);
@ -24,6 +29,9 @@ export default function MeasureAction() {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const baseData = useAppSelector(store => store.baseData);
const context = useAppSelector(store => store.context);
/** ----------------------- 引用 ----------------------- **/
const canvasRef = useRef<MeasurementCanvasRef>(null);
const leftPoints = useRef<{ x: number; y: number }[]>([]);
@ -62,6 +70,12 @@ export default function MeasureAction() {
const [statusList, setStatusList] = useState(initialStatusList);
// 打开抽屉(显示分析结果)
const [openDrawer, setOpenDrawer] = useState(false);
// 显示钢轨轨型弹框
const [showRailTypeModel, setShowRailTypeModel] = useState(false);
const [selectedRailType, setSelectedRailType] = useState(1);
// 显示核校方式弹框
const [showCalibrationModel, setShowCalibrationModel] = useState(false);
const [selectCalibrationType, setSelectCalibrationType] = useState(1);
/** ----------------------- 事件处理函数 ----------------------- */
// 切换保存后自动开始新测量
@ -165,6 +179,13 @@ export default function MeasureAction() {
}
};
useEffect(() => {
if (baseData.railTypes.length === 0) {
dispatch(fetchRailTypes());
dispatch(fetchCalibrationTypes());
}
}, [baseData.railTypes, dispatch]);
/** ----------------------- WebSocket 消息处理 ----------------------- **/
useEffect(() => {
// 处理任务状态消息
@ -296,6 +317,20 @@ export default function MeasureAction() {
}, []);
const onExport = () => {};
const currentRailType = () => baseData.railTypes.find(t => t.id === context.currRailTypeId);
const currentCalibrationType = () => baseData.calibrationTypes.find(t => t.id === context.currCalibrationTypeId);
const onConfirmRailType = () => {
setShowRailTypeModel(false);
dispatch(updateRailTypeId(selectedRailType));
// TODO 重新分析
};
const onConfirmCalibrationType = () => {
setShowCalibrationModel(false);
dispatch(updateCalibrationTypeId(selectCalibrationType));
// TODO 重新分析
};
/** ----------------------- 渲染 ----------------------- **/
return (
<>
@ -364,7 +399,26 @@ export default function MeasureAction() {
showCoordinates={showGrid}
ref={canvasRef}
/>
{analysisReport && (
<div className="flex mt-2 justify-end items-center gap-4">
<SelectorBtn
imgNode={() => <img src={icon_track} alt="" />}
text={currentRailType()?.name || "--"}
onClick={() => {
setSelectedRailType(context.currRailTypeId);
setShowRailTypeModel(true);
}}
/>
<SelectorBtn
imgNode={() => <img src={icon_dest} alt="" />}
text={currentCalibrationType()?.name || "--"}
onClick={() => {
setSelectCalibrationType(context.currCalibrationTypeId);
setShowCalibrationModel(true);
}}
/>
</div>
)}
</div>
</div>
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */}
@ -451,6 +505,61 @@ export default function MeasureAction() {
</>
)}
</Drawer>
<Modal
title={<h1 className="text-text font-medium text-center"></h1>}
closable={false}
open={showRailTypeModel}
width={360}
footer={
<div className="flex justify-center gap-4">
<Button
onClick={() => {
setShowRailTypeModel(false);
}}>
</Button>
<Button type="primary" onClick={onConfirmRailType}>
</Button>
</div>
}>
{baseData.railTypes.map(t => (
<RadioItem
key={t.id}
label={t.name}
selected={t.id === selectedRailType}
onSelect={() => setSelectedRailType(t.id)}
/>
))}
</Modal>
<Modal
title={<h1 className="text-text font-medium text-center"></h1>}
closable={false}
open={showCalibrationModel}
width={360}
footer={
<div className="flex justify-center gap-4">
<Button
onClick={() => {
setShowCalibrationModel(false);
}}>
</Button>
<Button type="primary" onClick={onConfirmCalibrationType}>
</Button>
</div>
}>
{baseData.calibrationTypes.map(t => (
<RadioItem
key={t.id}
label={t.name}
selected={t.id === selectCalibrationType}
onSelect={() => setSelectCalibrationType(t.id)}
/>
))}
</Modal>
</>
);
}

112
src/pages/measure/components/MeasureConfig.tsx

@ -1,19 +1,24 @@
import { Button, Form, Input, message, Select } from "antd";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useNavigate } from "react-router";
import { createMeasure } from "../../../services/measure/analysis";
import { useAppSelector } from "../../../utils/hooks";
import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
import { fetchCalibrationTypes, fetchRailTypes } from "../../../store/features/baseDataSlice";
import { updateCalibrationTypeId, updateRailTypeId } from "../../../store/features/contextSlice";
export default function MeasureConfig() {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const [messageApi, contextHolder] = message.useMessage();
const context = useAppSelector(store => store.context);
// const context = useAppSelector(store => store.context);
const deviceState = useAppSelector(store => store.deviceState);
const baseData = useAppSelector(store => store.baseData);
const onFinish = (values: any) => {
console.log("Received values of form: ", values);
//判断是否连接了设备
if(!deviceState.isConnect){
if (!deviceState.isConnect) {
// message.error('设备尚未连接或连接失败,请重新连接')
// return;
}
@ -22,29 +27,49 @@ export default function MeasureConfig() {
name: values["measureName"],
lineName: values["lineName"],
location: values["position"],
direction: values["direction"]
direction: values["direction"],
}).then(res => {
if (res.status !== 0) {
messageApi.error(res.data.info);
} else {
dispatch(updateRailTypeId(values["railType"]));
dispatch(updateCalibrationTypeId(values["calibrationType"]));
navigate("../action");
}
});
};
let user = localStorage.getItem('user') || ''
let userInfo = user && JSON.parse(user) || {};
let [nickName, setNickName] = useState()
if(context.user.loginUser && context.user.loginUser.nickname){
// setNickName(context.user.loginUser.nickname)
}
useEffect(() => {
if (baseData.railTypes.length === 0) {
dispatch(fetchRailTypes());
dispatch(fetchCalibrationTypes());
}
}, [baseData.railTypes, dispatch]);
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue({
const user = localStorage.getItem("user") || "";
const userInfo = (user && JSON.parse(user)) || {};
const fieldValue: Record<string, any> = {
username: userInfo.nickname || "",
direction: "左"
});
}, [userInfo.nickname, form]);
direction: "左",
};
if (baseData.railTypes.length > 0) {
fieldValue.railType = baseData.railTypes[0].id;
}
if (baseData.calibrationTypes.length > 0) {
fieldValue.calibrationType = baseData.calibrationTypes[0].id;
}
form.setFieldsValue(fieldValue);
}, [baseData.calibrationTypes, baseData.railTypes, form]);
// useEffect(() => {
// if (!baseData.loading && baseData.error) {
// messageApi.error(baseData.error);
// }
// }, [baseData.error, baseData.loading, messageApi]);
return (
<>
{contextHolder}
@ -60,47 +85,45 @@ export default function MeasureConfig() {
onFinish={onFinish}
// onFinishFailed={onFinishFailed}
autoComplete="off">
<Form.Item
label="操作员"
name="username"
rules={[{ required: true, message: "请输入操作员姓名" }]}>
<Form.Item label="操作员" name="username" rules={[{ required: true, message: "请输入操作员姓名" }]}>
<Input />
</Form.Item>
{/* <Form.Item label="" name="trackType">
<Select>
<Select.Option value="demo">Demo</Select.Option>
</Select>
</Form.Item> */}
{/* <Form.Item label="" name="calibrationType">
<Select>
<Select.Option value="demo">Demo</Select.Option>
</Select>
</Form.Item> */}
<Form.Item
label="测量名称"
name="measureName"
rules={[{ required: true, message: "请输入测量名称" }]}>
<Form.Item label="轨型" name="railType" rules={[{ required: true, message: "请选择轨型" }]}>
<Select>
{baseData.railTypes.map(t => (
<Select.Option key={t.id} value={t.id}>
{t.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="核校" name="calibrationType" rules={[{ required: true, message: "请选择核校类型" }]}>
<Select>
{baseData.calibrationTypes.map(t => (
<Select.Option key={t.id} value={t.id}>
{t.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="测量名称" name="measureName" rules={[{ required: true, message: "请输入测量名称" }]}>
<Input />
</Form.Item>
<Form.Item
label="线路名称"
name="lineName"
rules={[{ required: false, message: "请输入线路名称" }]}>
<Form.Item label="线路名称" name="lineName" rules={[{ required: false, message: "请输入线路名称" }]}>
<Input />
</Form.Item>
<Form.Item label="位置" name="position" rules={[{ required: false, message: "请输入位置" }]}>
<Input />
</Form.Item>
<Form.Item label="方向" name="direction" rules={[{ required: false, message: "请输入方向" }]}>
<Form.Item label="方向" name="direction" rules={[{ required: false, message: "请输入方向" }]}>
{/* <Input /> */}
<Select
className="w-[5rem]"
// defaultValue="左"
options={[
{ value: '左', label: '左侧' },
{ value: '右', label: '右侧' },
{ value: "左", label: "左侧" },
{ value: "右", label: "右侧" },
]}
/>
</Form.Item>
@ -110,6 +133,11 @@ export default function MeasureConfig() {
</Button>
</Form.Item>
{/* <Form.Item label={null}>
<Button type="primary" size="large" style={{ width: 220 }} onClick={() => dispatch(deleteRailType([1]))}>
</Button>
</Form.Item> */}
</Form>
</div>
</>

13
src/pages/measure/components/RadioItem.tsx

@ -0,0 +1,13 @@
import icon_select from "../../../assets/icon_selected.svg";
import icon_unselect from "../../../assets/icon_unselect.svg";
export default function RadioItem(props: { label: string; selected: boolean; onSelect?: () => void }) {
return (
<div
className="h-10 border-b border-[#f7f7f7] flex justify-between items-center"
onClick={() => props.onSelect && props.onSelect()}>
<p className="text-sm text-text">{props.label}</p>
<img src={props.selected ? icon_select : icon_unselect} alt="icon" />
</div>
);
}

17
src/pages/measure/components/SelectorBtn.tsx

@ -0,0 +1,17 @@
import { ReactNode } from "react";
import icon_arr from "../../../assets/icon_arr_down_s.svg";
export default function SelectorBtn({ imgNode, text, onClick }: { imgNode: () => ReactNode; text: string; onClick: () => void }) {
return (
<div className="flex items-center gap-2 cursor-pointer" onClick={onClick}>
<div className="w-[24px] h-[24px]">
{imgNode()}
</div>
<div className="flex items-center px-2 border border-[#e7e7e7] rounded h-7 min-w-[120px] gap-2 text-sm ">
<span>{text}</span>
<img src={icon_arr} alt="arr" className="w-2 ml-auto" />
</div>
</div>
);
}

10
src/services/apiTypes.ts

@ -0,0 +1,10 @@
export interface Rail {
id: number;
code: string;
name: string;
}
export interface Calibration {
id: number;
name: string;
}

16
src/services/calibration/calibration.ts

@ -0,0 +1,16 @@
import { Calibration } from "../apiTypes";
import httpRequest, { BaseResponse } from "../httpRequest";
export function getCalibrationTypes() {
return httpRequest<BaseResponse<Calibration[]>>({
url: "/api/calibration/list",
method: "POST",
});
}
export function deleteCalibrationTypes(ids: number[]) {
return httpRequest<BaseResponse>({
url: `/api/calibration/delete/${ids.join(",")}`,
method: "POST",
});
}

12
src/services/device/deviceState.ts

@ -1,9 +1,9 @@
import httpRequest, { type BaseResponse } from "../httpRequest";
import type { Device } from "../../services/measure/type";
import type { Device } from "../measure/type";
export function getDeviceInfo() {
return httpRequest<BaseResponse<{list:Device[]}>>({
url: "/api/measurement-data/getDevice",
method: "POST",
});
}
return httpRequest<BaseResponse<{ list: Device[] }>>({
url: "/api/measurement-data/getDevice",
method: "POST",
});
}

134
src/services/measure/analysis.ts

@ -1,65 +1,69 @@
import httpRequest, {type BaseResponse} from "../httpRequest";
import httpRequest, { type BaseResponse } from "../httpRequest";
import type {
AnalyzeResult, DetailTable, MeasureRecord,
ProfileRecordPointSet, BaseProfileRecordPointSet, AnalysisResults, AnalysisReport
} from "../../services/measure/type";
AnalyzeResult,
DetailTable,
MeasureRecord,
ProfileRecordPointSet,
BaseProfileRecordPointSet,
AnalysisReport,
} from "./type";
export function startMeasurement() {
return httpRequest<BaseResponse>({
url: "/api/measurement-task/start-measurement",
method: "POST",
});
return httpRequest<BaseResponse>({
url: "/api/measurement-task/start-measurement",
method: "POST",
});
}
export function stopMeasurement() {
return httpRequest<BaseResponse>({
url: "/api/measurement-task/stop-measurement",
method: "POST",
});
return httpRequest<BaseResponse>({
url: "/api/measurement-task/stop-measurement",
method: "POST",
});
}
export function analyzeMeasurement() {
return httpRequest<BaseResponse<AnalyzeResult>>({
url: "/api/measurement-task/analyze-measurement",
method: "POST",
});
return httpRequest<BaseResponse<AnalyzeResult>>({
url: "/api/measurement-task/analyze-measurement",
method: "POST",
});
}
export function saveMeasurement() {
return httpRequest<BaseResponse>({
url: "/api/measurement-task/save-report",
method: "POST",
});
return httpRequest<BaseResponse>({
url: "/api/measurement-task/save-report",
method: "POST",
});
}
export function getDetailList(params: {name?:string, pageSize: number, pageNum: number }) {
return httpRequest<BaseResponse<{ list: DetailTable[], total: number }>>({
url: "/api/measurement-data/list",
params,
method: "POST",
});
export function getDetailList(params: { name?: string; pageSize: number; pageNum: number }) {
return httpRequest<BaseResponse<{ list: DetailTable[]; total: number }>>({
url: "/api/measurement-data/list",
params,
method: "POST",
});
}
export function delDetail(params: { ids: string | number }) {
return httpRequest<BaseResponse>({
url: `/api/measurement-data/delete/${params.ids}`,
method: "POST",
});
return httpRequest<BaseResponse>({
url: `/api/measurement-data/delete/${params.ids}`,
method: "POST",
});
}
export function createMeasure(params: MeasureRecord) {
return httpRequest<BaseResponse>({
url: "/api/measurement-task/cache-measurement",
params,
method: "POST",
});
return httpRequest<BaseResponse>({
url: "/api/measurement-task/cache-measurement",
params,
method: "POST",
});
}
export function fetchAnalysisReport(code: string) {
return httpRequest<BaseResponse<AnalysisReport>>({
url: `/api/measurement-task/save-analysis-report/${code}`,
method: "POST",
});
return httpRequest<BaseResponse<AnalysisReport>>({
url: `/api/measurement-task/save-analysis-report/${code}`,
method: "POST",
});
}
/**
@ -67,10 +71,10 @@ export function fetchAnalysisReport(code: string) {
* POST /measurement-analysis/point/{uuid}
*/
export function getRecordPointSetByUUID(uuid: string) {
return httpRequest<BaseResponse<ProfileRecordPointSet>>({
url: `/api/measurement-analysis/point/${uuid}`,
method: "POST",
});
return httpRequest<BaseResponse<ProfileRecordPointSet>>({
url: `/api/measurement-analysis/point/${uuid}`,
method: "POST",
});
}
/**
@ -78,10 +82,10 @@ export function getRecordPointSetByUUID(uuid: string) {
* POST /measurement-analysis/base-point/{code}
*/
export function getBaseRecordPointSetByCode(code: string) {
return httpRequest<BaseResponse<BaseProfileRecordPointSet>>({
url: `/api/measurement-analysis/base-point/${code}`,
method: "POST",
});
return httpRequest<BaseResponse<BaseProfileRecordPointSet>>({
url: `/api/measurement-analysis/base-point/${code}`,
method: "POST",
});
}
/**
@ -92,29 +96,29 @@ export function getBaseRecordPointSetByCode(code: string) {
* @param code code
*/
export function getReport(uuid: string, code: string) {
return httpRequest<BaseResponse<AnalysisReport>>({
url: `/api/measurement-analysis/report`,
method: "POST",
params: { uuid, code },
});
return httpRequest<BaseResponse<AnalysisReport>>({
url: `/api/measurement-analysis/report`,
method: "POST",
params: { uuid, code },
});
}
/**
*
* /measurement-data
*/
export function getDetail(params:{id:number}){
return httpRequest<BaseResponse<{uuid:string}>>({
url: `/api/measurement-data/${params.id}`,
method: "POST",
params,
});
export function getDetail(params: { id: number }) {
return httpRequest<BaseResponse<{ uuid: string }>>({
url: `/api/measurement-data/${params.id}`,
method: "POST",
params,
});
}
export function getPointByUuid(params:{uuid:string}){
return httpRequest<BaseResponse>({
url: `/api/measurement-analysis/point/${params.uuid}`,
method: "POST",
params,
});
}
export function getPointByUuid(params: { uuid: string }) {
return httpRequest<BaseResponse>({
url: `/api/measurement-analysis/point/${params.uuid}`,
method: "POST",
params,
});
}

16
src/services/standardRail/standardRail.ts

@ -0,0 +1,16 @@
import { Rail } from "../apiTypes";
import httpRequest, { BaseResponse } from "../httpRequest";
export function getRailTypes() {
return httpRequest<BaseResponse<Rail[]>>({
url: "/api/standard-rail/list",
method: "POST",
});
}
export function deleteRailTypes(ids: number[]) {
return httpRequest<BaseResponse>({
url: `/api/standard-rail/delete/${ids.join(",")}`,
method: "POST",
});
}

121
src/store/features/baseDataSlice.ts

@ -0,0 +1,121 @@
// counterSlice.ts 文件
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
import { Calibration, Rail } from "../../services/apiTypes";
import { deleteRailTypes, getRailTypes } from "../../services/standardRail/standardRail";
import { deleteCalibrationTypes, getCalibrationTypes } from "../../services/calibration/calibration";
export interface BaseDataState {
loading: boolean;
error: string;
railTypes: Rail[];
calibrationTypes: Calibration[];
}
const initialState: BaseDataState = {
loading: false,
error: "",
// 轨型列表
railTypes: [],
// 核校类型列表
calibrationTypes: [],
};
// 可以添加泛型参数,第一个是 成功返回类型,第二个是 入参类型,第三个是 reject返回类型
// createAsyncThunk<Rail[], void, { rejectValue: { msg: string } }>
export const fetchRailTypes = createAsyncThunk("fetchRailTypes", async (_, thunkAPI) => {
try {
const res = await getRailTypes();
if (res.success) {
return res.data;
} else {
return thunkAPI.rejectWithValue({ msg: res.data.info });
}
} catch (error: any) {
return thunkAPI.rejectWithValue({ msg: (error.message as string) || "请求失败" });
}
});
export const deleteRailType = createAsyncThunk("deleteRailType", async (ids: number[], thunkAPI) => {
try {
const res = await deleteRailTypes(ids);
if (res.success) {
thunkAPI.dispatch(fetchRailTypes());
} else {
return thunkAPI.rejectWithValue({ msg: res.data.info });
}
} catch (error: any) {
return thunkAPI.rejectWithValue({ msg: (error.message as string) || "请求失败" });
}
});
export const fetchCalibrationTypes = createAsyncThunk("fetchCalibrationTypes", async (_, thunkAPI) => {
try {
const res = await getCalibrationTypes();
if (res.success) {
return res.data;
} else {
return thunkAPI.rejectWithValue({ msg: res.data.info });
}
} catch (error: any) {
return thunkAPI.rejectWithValue({ msg: (error.message as string) || "请求失败" });
}
});
export const deleteCalibrationType = createAsyncThunk("deleteCalibrationType", async (ids: number[], thunkAPI) => {
try {
const res = await deleteCalibrationTypes(ids);
if (res.success) {
thunkAPI.dispatch(fetchCalibrationTypes());
} else {
return thunkAPI.rejectWithValue({ msg: res.data.info });
}
} catch (error: any) {
return thunkAPI.rejectWithValue({ msg: (error.message as string) || "请求失败" });
}
});
export const baseDataSlice = createSlice({
name: "baseData",
initialState,
reducers: {
updateRailTypes: (state, action: PayloadAction<Rail[]>) => {
state.railTypes = action.payload;
},
updateCalibrationTypes: (state, action: PayloadAction<Calibration[]>) => {
state.calibrationTypes = action.payload;
},
},
extraReducers: builder => {
builder.addCase(fetchRailTypes.fulfilled, (state, action) => {
state.railTypes = action.payload;
});
builder.addCase(fetchCalibrationTypes.fulfilled, (state, action) => {
state.calibrationTypes = action.payload;
});
builder.addMatcher(
action => action.type.endsWith("/pending"),
state => {
state.loading = true;
state.error = "";
}
);
builder.addMatcher(
action => action.type.endsWith("/fulfilled"),
state => {
state.loading = false;
state.error = "";
}
);
builder.addMatcher(
action => action.type.endsWith("/rejected"),
(state, action: PayloadAction<{ msg: string }>) => {
state.loading = false;
state.error = action.payload?.msg || "请求失败";
}
);
},
});
// export const { updateRailTypes, updateCalibrationTypes } = baseDataSlice.actions;
export default baseDataSlice.reducer;

36
src/store/features/contextSlice.ts

@ -2,15 +2,18 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import type { ContextMessage, DeviceStatus } from "../../services/wsTypes";
import { fetchCalibrationTypes, fetchRailTypes } from "./baseDataSlice";
interface ContextSlice {
user: ContextMessage["data"];
newMeasureAfterSave: boolean;
device: DeviceStatus["data"];
newMeasureAfterSave: boolean;
currRailTypeId: number;
currCalibrationTypeId: number;
}
const initialState: ContextSlice = {
user: { loginFlag: false, loginUser: { nickname: "", userRole: "User" } },
newMeasureAfterSave: false,
device: {
isConnected: true, //是否链接
power: 60, //电量
@ -18,6 +21,14 @@ const initialState: ContextSlice = {
inclinatorY: 3.019, //y轴倾斜
temperature: 32.026, //温度
},
// 测量/分析相关
// 保存后自动开始新测量
newMeasureAfterSave: false,
// 当前选择的轨型
currRailTypeId: 1,
// 当前选择的核校
currCalibrationTypeId: 1,
};
export const contextSlice = createSlice({
@ -35,10 +46,29 @@ export const contextSlice = createSlice({
updateDevice: (state, action: PayloadAction<DeviceStatus["data"]>) => {
state.device = action.payload;
},
updateRailTypeId: (state, action: PayloadAction<number>) => {
state.currRailTypeId = action.payload;
},
updateCalibrationTypeId: (state, action: PayloadAction<number>) => {
state.currCalibrationTypeId = action.payload;
},
},
extraReducers: builder => {
builder.addCase(fetchRailTypes.fulfilled, (state, action) => {
if (!action.payload.some(t => t.id === state.currRailTypeId)) {
state.currRailTypeId = action.payload[0].id;
}
});
builder.addCase(fetchCalibrationTypes.fulfilled, (state, action) => {
if (!action.payload.some(t => t.id === state.currCalibrationTypeId)) {
state.currCalibrationTypeId = action.payload[0].id;
}
})
}
});
export const { updateUser, switchMeasureAfterSave, updateDevice } = contextSlice.actions;
export const { updateUser, switchMeasureAfterSave, updateDevice, updateRailTypeId, updateCalibrationTypeId } =
contextSlice.actions;
// 默认导出
export default contextSlice.reducer;

4
src/store/index.ts

@ -4,13 +4,15 @@ import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "./features/counterSlice";
import contextSlice from "./features/contextSlice";
import deviceStateSlice from "./device/deviceState";
import baseDataSlice from "./features/baseDataSlice";
// configureStore创建一个redux数据
const store = configureStore({
// 合并多个Slice
reducer: {
counter: counterSlice,
context: contextSlice,
deviceState: deviceStateSlice
deviceState: deviceStateSlice,
baseData: baseDataSlice,
},
});

Loading…
Cancel
Save