前端项目结构调整
This commit is contained in:
@@ -1,58 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "tjwater-app",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "cross-env NODE_OPTIONS=--max_old_space_size=4096 refine dev",
|
|
||||||
"build": "refine build",
|
|
||||||
"start": "refine start",
|
|
||||||
"lint": "next lint",
|
|
||||||
"refine": "refine"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@emotion/react": "^11.8.2",
|
|
||||||
"@emotion/styled": "^11.8.1",
|
|
||||||
"@mui/icons-material": "^6.1.6",
|
|
||||||
"@mui/lab": "^6.0.0-beta.14",
|
|
||||||
"@mui/material": "^6.1.7",
|
|
||||||
"@mui/x-data-grid": "^7.22.2",
|
|
||||||
"@refinedev/cli": "^2.16.48",
|
|
||||||
"@refinedev/core": "^5.0.0",
|
|
||||||
"@refinedev/devtools": "^2.0.1",
|
|
||||||
"@refinedev/kbar": "^2.0.0",
|
|
||||||
"@refinedev/mui": "^7.0.0",
|
|
||||||
"@refinedev/nextjs-router": "^7.0.0",
|
|
||||||
"@refinedev/react-hook-form": "^5.0.0",
|
|
||||||
"@refinedev/simple-rest": "^6.0.0",
|
|
||||||
"@tailwindcss/postcss": "^4.1.13",
|
|
||||||
"@turf/turf": "^7.2.0",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"deck.gl": "^9.1.14",
|
|
||||||
"js-cookie": "^3.0.5",
|
|
||||||
"next": "^15.2.4",
|
|
||||||
"next-auth": "^4.24.5",
|
|
||||||
"ol": "^10.6.1",
|
|
||||||
"postcss": "^8.5.6",
|
|
||||||
"react": "^19.1.0",
|
|
||||||
"react-dom": "^19.1.0",
|
|
||||||
"react-icons": "^5.5.0",
|
|
||||||
"tailwindcss": "^4.1.13"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@svgr/webpack": "^8.1.0",
|
|
||||||
"@types/js-cookie": "^3.0.6",
|
|
||||||
"@types/node": "^20",
|
|
||||||
"@types/react": "^19.1.0",
|
|
||||||
"@types/react-dom": "^19.1.0",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"eslint": "^8",
|
|
||||||
"eslint-config-next": "^15.0.3",
|
|
||||||
"typescript": "^5.8.3"
|
|
||||||
},
|
|
||||||
"refine": {
|
|
||||||
"projectId": "4LwOCL-BBaV29-qUYMAJ"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import Timeline from "@components/olmap/HealthRiskAnalysis/Timeline";
|
import Timeline from "@components/olmap/HealthRiskAnalysis/Timeline";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import { HealthRiskProvider } from "@components/olmap/HealthRiskAnalysis/HealthRiskContext";
|
import { HealthRiskProvider } from "@components/olmap/HealthRiskAnalysis/HealthRiskContext";
|
||||||
import HealthRiskStatistics from "@components/olmap/HealthRiskAnalysis/HealthRiskStatistics";
|
import HealthRiskStatistics from "@components/olmap/HealthRiskAnalysis/HealthRiskStatistics";
|
||||||
import PredictDataPanel from "@components/olmap/HealthRiskAnalysis/PredictDataPanel";
|
import PredictDataPanel from "@components/olmap/HealthRiskAnalysis/PredictDataPanel";
|
||||||
import StyleLegend from "@app/OlMap/Controls/StyleLegend";
|
import StyleLegend from "@components/olmap/core/Controls/StyleLegend";
|
||||||
import {
|
import {
|
||||||
RAINBOW_COLORS,
|
RAINBOW_COLORS,
|
||||||
RISK_BREAKS,
|
RISK_BREAKS,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import BurstLocationPanel from "@/components/olmap/BurstLocation/BurstLocationPanel";
|
import BurstLocationPanel from "@/components/olmap/BurstLocation/BurstLocationPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import BurstPipeAnalysisPanel from "@/components/olmap/BurstPipeAnalysis/BurstPipeAnalysisPanel";
|
import BurstPipeAnalysisPanel from "@/components/olmap/BurstSimulation/BurstPipeAnalysisPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import WaterQualityPanel from "@/components/olmap/ContaminantSimulation/WaterQualityPanel";
|
import WaterQualityPanel from "@/components/olmap/ContaminantSimulation/WaterQualityPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import DMALeakDetectionPanel from "@/components/olmap/DMALeakDetection/DMALeakDetectionPanel";
|
import DMALeakDetectionPanel from "@/components/olmap/DMALeakDetection/DMALeakDetectionPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import FlushingAnalysisPanel from "@/components/olmap/FlushingAnalysis/FlushingAnalysisPanel";
|
import FlushingAnalysisPanel from "@/components/olmap/FlushingAnalysis/FlushingAnalysisPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import MonitoringPlaceOptimizationPanel from "@components/olmap/MonitoringPlaceOptimization/MonitoringPlaceOptimizationPanel";
|
import MonitoringPlaceOptimizationPanel from "@components/olmap/MonitoringPlaceOptimization/MonitoringPlaceOptimizationPanel";
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
import ZonePropsPanel from "@components/olmap/NetworkPartitionOptimization/ZonePropsPanel";
|
import ZonePropsPanel from "@components/olmap/NetworkPartitionOptimization/ZonePropsPanel";
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import Timeline from "@app/OlMap/Controls/Timeline";
|
import Timeline from "@components/olmap/core/Controls/Timeline";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
|
|
||||||
import SCADADeviceList from "@components/olmap/SCADADeviceList";
|
import SCADADeviceList from "@components/olmap/SCADA/SCADADeviceList";
|
||||||
import SCADADataPanel from "@components/olmap/SCADADataPanel";
|
import SCADADataPanel from "@components/olmap/SCADA/SCADADataPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [selectedDeviceIds, setSelectedDeviceIds] = useState<string[]>([]);
|
const [selectedDeviceIds, setSelectedDeviceIds] = useState<string[]>([]);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import MapComponent from "@app/OlMap/MapComponent";
|
import MapComponent from "@components/olmap/core/MapComponent";
|
||||||
import MapToolbar from "@app/OlMap/Controls/Toolbar";
|
import MapToolbar from "@components/olmap/core/Controls/Toolbar";
|
||||||
|
|
||||||
import SCADADeviceList from "@components/olmap/SCADADeviceList";
|
import SCADADeviceList from "@components/olmap/SCADA/SCADADeviceList";
|
||||||
import SCADADataPanel from "@components/olmap/SCADADataPanel";
|
import SCADADataPanel from "@components/olmap/SCADA/SCADADataPanel";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [selectedDeviceIds, setSelectedDeviceIds] = useState<string[]>([]);
|
const [selectedDeviceIds, setSelectedDeviceIds] = useState<string[]>([]);
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ const App = (props: React.PropsWithChildren<AppProps>) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "爆管模拟",
|
name: "爆管模拟",
|
||||||
list: "/hydraulic-simulation/pipe-burst-analysis",
|
list: "/hydraulic-simulation/burst-simulation",
|
||||||
meta: {
|
meta: {
|
||||||
parent: "Hydraulic Simulation",
|
parent: "Hydraulic Simulation",
|
||||||
icon: <TbLocationPin className="w-6 h-6" />,
|
icon: <TbLocationPin className="w-6 h-6" />,
|
||||||
@@ -204,7 +204,7 @@ const App = (props: React.PropsWithChildren<AppProps>) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "水质模拟",
|
name: "水质模拟",
|
||||||
list: "/hydraulic-simulation/water-quality-simulation",
|
list: "/hydraulic-simulation/contaminant-simulation",
|
||||||
meta: {
|
meta: {
|
||||||
parent: "Hydraulic Simulation",
|
parent: "Hydraulic Simulation",
|
||||||
icon: <MdOutlineWaterDrop className="w-6 h-6" />,
|
icon: <MdOutlineWaterDrop className="w-6 h-6" />,
|
||||||
@@ -213,7 +213,7 @@ const App = (props: React.PropsWithChildren<AppProps>) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "管道冲洗",
|
name: "管道冲洗",
|
||||||
list: "/hydraulic-simulation/pipe-flushing",
|
list: "/hydraulic-simulation/flushing-analysis",
|
||||||
meta: {
|
meta: {
|
||||||
parent: "Hydraulic Simulation",
|
parent: "Hydraulic Simulation",
|
||||||
icon: <MdCleaningServices className="w-6 h-6" />,
|
icon: <MdCleaningServices className="w-6 h-6" />,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
Map as MapIcon,
|
Map as MapIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import Feature from "ol/Feature";
|
import Feature from "ol/Feature";
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ import { zhCN as pickerZhCN } from "@mui/x-date-pickers/locales";
|
|||||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||||
import "dayjs/locale/zh-cn"; // 引入中文包
|
import "dayjs/locale/zh-cn"; // 引入中文包
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
import { Style, Stroke, Icon } from "ol/style";
|
import { Style, Stroke, Icon } from "ol/style";
|
||||||
+1
-1
@@ -13,7 +13,7 @@ import {
|
|||||||
LocationOn as LocationIcon,
|
LocationOn as LocationIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
+2
-2
@@ -32,7 +32,7 @@ import { config, NETWORK_NAME } from "@config/config";
|
|||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
|
|
||||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { useData, useMap } from "@app/OlMap/MapComponent";
|
import { useData, useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
@@ -48,7 +48,7 @@ import {
|
|||||||
} from "@turf/turf";
|
} from "@turf/turf";
|
||||||
import { Point } from "ol/geom";
|
import { Point } from "ol/geom";
|
||||||
import { toLonLat } from "ol/proj";
|
import { toLonLat } from "ol/proj";
|
||||||
import Timeline from "@app/OlMap/Controls/Timeline";
|
import Timeline from "@components/olmap/core/Controls/Timeline";
|
||||||
import { SchemaItem, SchemeRecord } from "./types";
|
import { SchemaItem, SchemeRecord } from "./types";
|
||||||
|
|
||||||
interface SchemeQueryProps {
|
interface SchemeQueryProps {
|
||||||
+1
-1
@@ -35,7 +35,7 @@ import {
|
|||||||
queryFeaturesByIds,
|
queryFeaturesByIds,
|
||||||
handleMapClickSelectFeatures,
|
handleMapClickSelectFeatures,
|
||||||
} from "@/utils/mapQueryService";
|
} from "@/utils/mapQueryService";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
@@ -19,7 +19,7 @@ import dayjs, { Dayjs } from "dayjs";
|
|||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
import { config, NETWORK_NAME } from "@/config/config";
|
import { config, NETWORK_NAME } from "@/config/config";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
import { Style, Stroke, Fill, Circle as CircleStyle, Icon } from "ol/style";
|
import { Style, Stroke, Fill, Circle as CircleStyle, Icon } from "ol/style";
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ import moment from "moment";
|
|||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
import { config, NETWORK_NAME } from "@config/config";
|
import { config, NETWORK_NAME } from "@config/config";
|
||||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { useData, useMap } from "@app/OlMap/MapComponent";
|
import { useData, useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
import { Style, Icon, Circle, Fill, Stroke } from "ol/style";
|
import { Style, Icon, Circle, Fill, Stroke } from "ol/style";
|
||||||
import Feature from "ol/Feature";
|
import Feature from "ol/Feature";
|
||||||
import { bbox, featureCollection } from "@turf/turf";
|
import { bbox, featureCollection } from "@turf/turf";
|
||||||
import Timeline from "@app/OlMap/Controls/Timeline";
|
import Timeline from "@components/olmap/core/Controls/Timeline";
|
||||||
import { ContaminantSchemaItem, ContaminantSchemeRecord } from "./types";
|
import { ContaminantSchemaItem, ContaminantSchemeRecord } from "./types";
|
||||||
|
|
||||||
interface SchemeQueryProps {
|
interface SchemeQueryProps {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import ContaminantAnalysisParameters from "./AnalysisParameters";
|
import ContaminantAnalysisParameters from "./AnalysisParameters";
|
||||||
import ContaminantSchemeQuery from "./SchemeQuery";
|
import ContaminantSchemeQuery from "./SchemeQuery";
|
||||||
import { useData } from "@app/OlMap/MapComponent";
|
import { useData } from "@components/olmap/core/MapComponent";
|
||||||
|
|
||||||
interface WaterQualityPanelProps {
|
interface WaterQualityPanelProps {
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
|||||||
import VectorTileSource from "ol/source/VectorTile";
|
import VectorTileSource from "ol/source/VectorTile";
|
||||||
import { VectorTile } from "ol";
|
import { VectorTile } from "ol";
|
||||||
import { FlatStyleLike } from "ol/style/flat";
|
import { FlatStyleLike } from "ol/style/flat";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import StyleLegend from "@app/OlMap/Controls/StyleLegend";
|
import StyleLegend from "@components/olmap/core/Controls/StyleLegend";
|
||||||
import AnalysisParameters from "./AnalysisParameters";
|
import AnalysisParameters from "./AnalysisParameters";
|
||||||
import SchemeQuery from "./SchemeQuery";
|
import SchemeQuery from "./SchemeQuery";
|
||||||
import RecognitionResults from "./RecognitionResults";
|
import RecognitionResults from "./RecognitionResults";
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { zhCN as pickerZhCN } from "@mui/x-date-pickers/locales";
|
|||||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||||
import "dayjs/locale/zh-cn";
|
import "dayjs/locale/zh-cn";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
import { Style, Stroke, Fill, Circle as CircleStyle } from "ol/style";
|
import { Style, Stroke, Fill, Circle as CircleStyle } from "ol/style";
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { api } from "@/lib/api";
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { config, NETWORK_NAME } from "@config/config";
|
import { config, NETWORK_NAME } from "@config/config";
|
||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
import { useData, useMap } from "@app/OlMap/MapComponent";
|
import { useData, useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
@@ -38,7 +38,7 @@ import VectorSource from "ol/source/Vector";
|
|||||||
import { Style, Icon, Circle, Fill, Stroke } from "ol/style";
|
import { Style, Icon, Circle, Fill, Stroke } from "ol/style";
|
||||||
import Feature, { FeatureLike } from "ol/Feature";
|
import Feature, { FeatureLike } from "ol/Feature";
|
||||||
import { bbox, featureCollection } from "@turf/turf";
|
import { bbox, featureCollection } from "@turf/turf";
|
||||||
import Timeline from "@app/OlMap/Controls/Timeline";
|
import Timeline from "@components/olmap/core/Controls/Timeline";
|
||||||
import { SchemeRecord, SchemaItem } from "./types";
|
import { SchemeRecord, SchemaItem } from "./types";
|
||||||
import { FLOW_DISPLAY_UNIT } from "@utils/units";
|
import { FLOW_DISPLAY_UNIT } from "@utils/units";
|
||||||
|
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ import dayjs from "dayjs";
|
|||||||
import { PlayArrow, Pause, Stop, Refresh } from "@mui/icons-material";
|
import { PlayArrow, Pause, Stop, Refresh } from "@mui/icons-material";
|
||||||
import { TbArrowBackUp, TbArrowForwardUp } from "react-icons/tb";
|
import { TbArrowBackUp, TbArrowForwardUp } from "react-icons/tb";
|
||||||
import { FiSkipBack, FiSkipForward } from "react-icons/fi";
|
import { FiSkipBack, FiSkipForward } from "react-icons/fi";
|
||||||
import { useData } from "../../../app/OlMap/MapComponent";
|
import { useData } from "@components/olmap/core/MapComponent";
|
||||||
import { config, NETWORK_NAME } from "@/config/config";
|
import { config, NETWORK_NAME } from "@/config/config";
|
||||||
import { apiFetch } from "@/lib/apiFetch";
|
import { apiFetch } from "@/lib/apiFetch";
|
||||||
import { useMap } from "../../../app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { useHealthRisk } from "./HealthRiskContext";
|
import { useHealthRisk } from "./HealthRiskContext";
|
||||||
import {
|
import {
|
||||||
PredictionResult,
|
PredictionResult,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { api } from "@/lib/api";
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { config, NETWORK_NAME } from "@config/config";
|
import { config, NETWORK_NAME } from "@config/config";
|
||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import VectorLayer from "ol/layer/Vector";
|
import VectorLayer from "ol/layer/Vector";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Fill from "ol/style/Fill";
|
|||||||
import { Stroke } from "ol/style";
|
import { Stroke } from "ol/style";
|
||||||
import GeoJson from "ol/format/GeoJSON";
|
import GeoJson from "ol/format/GeoJSON";
|
||||||
import config from "@config/config";
|
import config from "@config/config";
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { useProject } from "@/contexts/ProjectContext";
|
import { useProject } from "@/contexts/ProjectContext";
|
||||||
|
|
||||||
interface PropertyItem {
|
interface PropertyItem {
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ import { api } from "@/lib/api";
|
|||||||
import { useGetIdentity } from "@refinedev/core";
|
import { useGetIdentity } from "@refinedev/core";
|
||||||
import config from "@/config/config";
|
import config from "@/config/config";
|
||||||
|
|
||||||
import { useMap } from "@app/OlMap/MapComponent";
|
import { useMap } from "@components/olmap/core/MapComponent";
|
||||||
import { useProject } from "@/contexts/ProjectContext";
|
import { useProject } from "@/contexts/ProjectContext";
|
||||||
import { GeoJSON } from "ol/format";
|
import { GeoJSON } from "ol/format";
|
||||||
import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
|
import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
/**
|
|
||||||
* 优雅分段分类 - 类似QGIS的Pretty Breaks
|
|
||||||
* 生成"好看"、易读的断点数值
|
|
||||||
* @param {number[]} data - 数据数组
|
|
||||||
* @param {number} n_classes - 分类数量
|
|
||||||
* @returns {number[]} 断点数组
|
|
||||||
*/
|
|
||||||
function prettyBreaksClassification(data, n_classes) {
|
|
||||||
if (data.length === 0) return [];
|
|
||||||
|
|
||||||
// const min_val = Math.min(...data);
|
|
||||||
// const max_val = Math.max(...data);
|
|
||||||
// const min_val = data.reduce((min, val) => Math.min(min, val), Infinity);
|
|
||||||
// 保证最小值不小于0
|
|
||||||
const min_val = Math.max(data.reduce((min, val) => Math.min(min, val), Infinity), 0);
|
|
||||||
const max_val = data.reduce((max, val) => Math.max(max, val), -Infinity);
|
|
||||||
const data_range = max_val - min_val;
|
|
||||||
|
|
||||||
// 计算基础间隔
|
|
||||||
const raw_interval = data_range / n_classes;
|
|
||||||
|
|
||||||
// 寻找"优雅"的间隔
|
|
||||||
const magnitude = 10 ** Math.floor(Math.log10(raw_interval));
|
|
||||||
const normalized = raw_interval / magnitude;
|
|
||||||
|
|
||||||
// 选择最接近的优雅数字
|
|
||||||
let nice_interval;
|
|
||||||
if (normalized <= 1) {
|
|
||||||
nice_interval = magnitude;
|
|
||||||
} else if (normalized <= 2) {
|
|
||||||
nice_interval = 2 * magnitude;
|
|
||||||
} else if (normalized <= 5) {
|
|
||||||
nice_interval = 5 * magnitude;
|
|
||||||
} else {
|
|
||||||
nice_interval = 10 * magnitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算优雅的起始点
|
|
||||||
const nice_min = Math.floor(min_val / nice_interval) * nice_interval;
|
|
||||||
const nice_max = Math.ceil(max_val / nice_interval) * nice_interval;
|
|
||||||
|
|
||||||
// 生成断点
|
|
||||||
const breaks = [];
|
|
||||||
let current = nice_min;
|
|
||||||
while (current <= nice_max && breaks.length < n_classes + 1) {
|
|
||||||
breaks.push(current);
|
|
||||||
current += nice_interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保包含最大值
|
|
||||||
if (breaks.length === 0 || breaks[breaks.length - 1] < max_val) {
|
|
||||||
breaks.push(nice_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调整为n_classes个区间
|
|
||||||
if (breaks.length > n_classes + 1) {
|
|
||||||
breaks.splice(n_classes + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return breaks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算类内方差
|
|
||||||
* @param {number[]} data - 排序后的数据数组
|
|
||||||
* @param {number} start - 起始索引
|
|
||||||
* @param {number} end - 结束索引
|
|
||||||
* @returns {number} 类内方差
|
|
||||||
*/
|
|
||||||
function variance(data, start, end) {
|
|
||||||
if (start >= end) return 0;
|
|
||||||
const mean = data.slice(start, end + 1).reduce((a, b) => a + b, 0) / (end - start + 1);
|
|
||||||
return data.slice(start, end + 1).reduce((sum, val) => sum + (val - mean) ** 2, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Jenks自然断点分类算法
|
|
||||||
* @param {number[]} data - 数据数组
|
|
||||||
* @param {number} n_classes - 分类数量
|
|
||||||
* @returns {number[]} 断点数组
|
|
||||||
*/
|
|
||||||
function jenks_breaks_jenkspy(data, n_classes) {
|
|
||||||
if (data.length === 0) return [];
|
|
||||||
if (n_classes >= data.length) return data.slice().sort((a, b) => a - b);
|
|
||||||
|
|
||||||
const sortedData = data.slice().sort((a, b) => a - b);
|
|
||||||
const n = sortedData.length;
|
|
||||||
const k = n_classes;
|
|
||||||
|
|
||||||
// 初始化矩阵
|
|
||||||
const lowerClassLimits = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
|
|
||||||
const varianceCombinations = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
|
|
||||||
|
|
||||||
for (let i = 1; i <= n; i++) {
|
|
||||||
lowerClassLimits[i][1] = 1;
|
|
||||||
varianceCombinations[i][1] = variance(sortedData, 0, i - 1);
|
|
||||||
for (let j = 2; j <= k; j++) {
|
|
||||||
varianceCombinations[i][j] = Infinity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态规划
|
|
||||||
for (let l = 2; l <= k; l++) {
|
|
||||||
for (let m = l; m <= n; m++) {
|
|
||||||
for (let i = l - 1; i < m; i++) {
|
|
||||||
const v = varianceCombinations[i][l - 1] + variance(sortedData, i, m - 1);
|
|
||||||
if (v < varianceCombinations[m][l]) {
|
|
||||||
varianceCombinations[m][l] = v;
|
|
||||||
lowerClassLimits[m][l] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回溯找到断点
|
|
||||||
const breaks = [];
|
|
||||||
let current = n;
|
|
||||||
for (let j = k; j >= 1; j--) {
|
|
||||||
breaks.unshift(sortedData[lowerClassLimits[current][j] - 1] || sortedData[0]);
|
|
||||||
current = lowerClassLimits[current][j];
|
|
||||||
}
|
|
||||||
breaks.push(sortedData[n - 1]);
|
|
||||||
|
|
||||||
return breaks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用分层采样优化的Jenks算法
|
|
||||||
* 确保采样数据能代表原数据的分布
|
|
||||||
* @param {number[]} data - 数据数组
|
|
||||||
* @param {number} n_classes - 分类数量
|
|
||||||
* @param {number} sample_size - 采样大小,默认10000
|
|
||||||
* @returns {number[]} 断点数组
|
|
||||||
*/
|
|
||||||
function jenks_with_stratified_sampling(data, n_classes, sample_size = 10000) {
|
|
||||||
if (data.length <= sample_size) {
|
|
||||||
return jenks_breaks_jenkspy(data, n_classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对数据排序
|
|
||||||
const sorted_data = data.slice().sort((a, b) => a - b);
|
|
||||||
|
|
||||||
// 计算采样间隔
|
|
||||||
const interval = sorted_data.length / sample_size;
|
|
||||||
|
|
||||||
// 分层采样
|
|
||||||
const sampled_data = [];
|
|
||||||
for (let i = 0; i < sample_size; i++) {
|
|
||||||
const index = Math.floor(i * interval);
|
|
||||||
if (index < sorted_data.length) {
|
|
||||||
sampled_data.push(sorted_data[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jenks_breaks_jenkspy(sampled_data, n_classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据指定的方法计算数据的分类断点。
|
|
||||||
* @param {Array<number>} data - 要分类的数值数据数组。
|
|
||||||
* @param {number} segments - 要创建的段数或类别数。
|
|
||||||
* @param {string} classificationMethod - 要使用的分类方法。支持的值:"pretty_breaks" 或 "jenks_optimized"。
|
|
||||||
* @returns {Array<number>} 分类的断点数组。如果数据为空或无效,则返回空数组。
|
|
||||||
*/
|
|
||||||
function calculateClassification(
|
|
||||||
data,
|
|
||||||
segments,
|
|
||||||
classificationMethod
|
|
||||||
) {
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (classificationMethod === "pretty_breaks") {
|
|
||||||
return prettyBreaksClassification(data, segments);
|
|
||||||
}
|
|
||||||
if (classificationMethod === "jenks_optimized") {
|
|
||||||
return jenks_with_stratified_sampling(data, segments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { prettyBreaksClassification, jenks_breaks_jenkspy, jenks_with_stratified_sampling, calculateClassification };
|
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
export function prettyBreaksClassification(
|
||||||
|
data: number[],
|
||||||
|
nClasses: number
|
||||||
|
): number[] {
|
||||||
|
if (data.length === 0) return [];
|
||||||
|
|
||||||
|
const minVal = Math.max(data.reduce((min, val) => Math.min(min, val), Infinity), 0);
|
||||||
|
const maxVal = data.reduce((max, val) => Math.max(max, val), -Infinity);
|
||||||
|
const dataRange = maxVal - minVal;
|
||||||
|
const rawInterval = dataRange / nClasses;
|
||||||
|
|
||||||
|
const magnitude = 10 ** Math.floor(Math.log10(rawInterval));
|
||||||
|
const normalized = rawInterval / magnitude;
|
||||||
|
|
||||||
|
let niceInterval: number;
|
||||||
|
if (normalized <= 1) {
|
||||||
|
niceInterval = magnitude;
|
||||||
|
} else if (normalized <= 2) {
|
||||||
|
niceInterval = 2 * magnitude;
|
||||||
|
} else if (normalized <= 5) {
|
||||||
|
niceInterval = 5 * magnitude;
|
||||||
|
} else {
|
||||||
|
niceInterval = 10 * magnitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
const niceMin = Math.floor(minVal / niceInterval) * niceInterval;
|
||||||
|
const niceMax = Math.ceil(maxVal / niceInterval) * niceInterval;
|
||||||
|
|
||||||
|
const breaks: number[] = [];
|
||||||
|
let current = niceMin;
|
||||||
|
while (current <= niceMax && breaks.length < nClasses + 1) {
|
||||||
|
breaks.push(current);
|
||||||
|
current += niceInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breaks.length === 0 || breaks[breaks.length - 1] < maxVal) {
|
||||||
|
breaks.push(niceMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breaks.length > nClasses + 1) {
|
||||||
|
breaks.splice(nClasses + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return breaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function variance(data: number[], start: number, end: number): number {
|
||||||
|
if (start >= end) return 0;
|
||||||
|
const mean = data.slice(start, end + 1).reduce((a, b) => a + b, 0) / (end - start + 1);
|
||||||
|
return data.slice(start, end + 1).reduce((sum, val) => sum + (val - mean) ** 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jenks_breaks_jenkspy(data: number[], nClasses: number): number[] {
|
||||||
|
if (data.length === 0) return [];
|
||||||
|
if (nClasses >= data.length) return data.slice().sort((a, b) => a - b);
|
||||||
|
|
||||||
|
const sortedData = data.slice().sort((a, b) => a - b);
|
||||||
|
const n = sortedData.length;
|
||||||
|
const k = nClasses;
|
||||||
|
|
||||||
|
const lowerClassLimits = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
|
||||||
|
const varianceCombinations = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
|
||||||
|
|
||||||
|
for (let i = 1; i <= n; i++) {
|
||||||
|
lowerClassLimits[i][1] = 1;
|
||||||
|
varianceCombinations[i][1] = variance(sortedData, 0, i - 1);
|
||||||
|
for (let j = 2; j <= k; j++) {
|
||||||
|
varianceCombinations[i][j] = Infinity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let l = 2; l <= k; l++) {
|
||||||
|
for (let m = l; m <= n; m++) {
|
||||||
|
for (let i = l - 1; i < m; i++) {
|
||||||
|
const v = varianceCombinations[i][l - 1] + variance(sortedData, i, m - 1);
|
||||||
|
if (v < varianceCombinations[m][l]) {
|
||||||
|
varianceCombinations[m][l] = v;
|
||||||
|
lowerClassLimits[m][l] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const breaks: number[] = [];
|
||||||
|
let current = n;
|
||||||
|
for (let j = k; j >= 1; j--) {
|
||||||
|
breaks.unshift(sortedData[lowerClassLimits[current][j] - 1] || sortedData[0]);
|
||||||
|
current = lowerClassLimits[current][j];
|
||||||
|
}
|
||||||
|
breaks.push(sortedData[n - 1]);
|
||||||
|
|
||||||
|
return breaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jenks_with_stratified_sampling(
|
||||||
|
data: number[],
|
||||||
|
nClasses: number,
|
||||||
|
sampleSize = 10000
|
||||||
|
): number[] {
|
||||||
|
if (data.length <= sampleSize) {
|
||||||
|
return jenks_breaks_jenkspy(data, nClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedData = data.slice().sort((a, b) => a - b);
|
||||||
|
const interval = sortedData.length / sampleSize;
|
||||||
|
|
||||||
|
const sampledData: number[] = [];
|
||||||
|
for (let i = 0; i < sampleSize; i++) {
|
||||||
|
const index = Math.floor(i * interval);
|
||||||
|
if (index < sortedData.length) {
|
||||||
|
sampledData.push(sortedData[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jenks_breaks_jenkspy(sampledData, nClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateClassification(
|
||||||
|
data: number[],
|
||||||
|
segments: number,
|
||||||
|
classificationMethod: string
|
||||||
|
): number[] {
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classificationMethod === "pretty_breaks") {
|
||||||
|
return prettyBreaksClassification(data, segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classificationMethod === "jenks_optimized") {
|
||||||
|
return jenks_with_stratified_sampling(data, segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* 将颜色字符串解析为包含红色、绿色、蓝色和 alpha 分量的对象。
|
|
||||||
* 支持 rgba、rgb 和十六进制颜色格式。对于 rgba 和 rgb,提取 r、g、b 和 a(如果未提供,则默认为 1)。
|
|
||||||
* 对于十六进制(例如 #RRGGBB),提取 r、g、b。(e.g., "rgba(255, 0, 0, 0.5)", "rgb(255, 0, 0)", or "#FF0000").
|
|
||||||
* @param {string} color - 要解析的颜色字符串(例如 "rgba(255, 0, 0, 0.5)"、"rgb(255, 0, 0)" 或 "#FF0000")。
|
|
||||||
* @returns {{r: number, g: number, b: number, a?: number}} 包含颜色分量的对象:
|
|
||||||
* - r: 红色分量 (0-255)
|
|
||||||
* - g: 绿色分量 (0-255)
|
|
||||||
* - b: 蓝色分量 (0-255)
|
|
||||||
* - a: Alpha 分量 (0-1),如果未指定则默认为 1
|
|
||||||
**/
|
|
||||||
function parseColor(color) {
|
|
||||||
// 解析 rgba 格式的颜色
|
|
||||||
const match = color.match(
|
|
||||||
/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/
|
|
||||||
);
|
|
||||||
if (match) {
|
|
||||||
return {
|
|
||||||
r: parseInt(match[1], 10),
|
|
||||||
g: parseInt(match[2], 10),
|
|
||||||
b: parseInt(match[3], 10),
|
|
||||||
// 如果没有 alpha 值,默认为 1
|
|
||||||
a: match[4] ? parseFloat(match[4]) : 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 如果还是十六进制格式,保持原来的解析方式
|
|
||||||
const hex = color.replace("#", "");
|
|
||||||
return {
|
|
||||||
r: parseInt(hex.slice(0, 2), 16),
|
|
||||||
g: parseInt(hex.slice(2, 4), 16),
|
|
||||||
b: parseInt(hex.slice(4, 6), 16),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { parseColor };
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
const { parseColor } = require('./parseColor');
|
|
||||||
|
|
||||||
describe('parseColor', () => {
|
|
||||||
it('should parse hex color', () => {
|
|
||||||
expect(parseColor('#FF0000')).toEqual({ r: 255, g: 0, b: 0 });
|
|
||||||
expect(parseColor('00FF00')).toEqual({ r: 0, g: 255, b: 0 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse rgb color', () => {
|
|
||||||
expect(parseColor('rgb(0, 0, 255)')).toEqual({ r: 0, g: 0, b: 255, a: 1 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse rgba color', () => {
|
|
||||||
expect(parseColor('rgba(0, 0, 255, 0.5)')).toEqual({ r: 0, g: 0, b: 255, a: 0.5 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should default alpha to 1 if not provided in rgba-like pattern', () => {
|
|
||||||
// The regex supports optional alpha
|
|
||||||
expect(parseColor('rgba(0, 0, 255)')).toEqual({ r: 0, g: 0, b: 255, a: 1 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { parseColor } from "./parseColor";
|
||||||
|
|
||||||
|
describe("parseColor", () => {
|
||||||
|
it("should parse hex color", () => {
|
||||||
|
expect(parseColor("#FF0000")).toEqual({ r: 255, g: 0, b: 0 });
|
||||||
|
expect(parseColor("00FF00")).toEqual({ r: 0, g: 255, b: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse rgb color", () => {
|
||||||
|
expect(parseColor("rgb(0, 0, 255)")).toEqual({ r: 0, g: 0, b: 255, a: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse rgba color", () => {
|
||||||
|
expect(parseColor("rgba(0, 0, 255, 0.5)")).toEqual({
|
||||||
|
r: 0,
|
||||||
|
g: 0,
|
||||||
|
b: 255,
|
||||||
|
a: 0.5,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should default alpha to 1 if not provided in rgba-like pattern", () => {
|
||||||
|
expect(parseColor("rgba(0, 0, 255)")).toEqual({ r: 0, g: 0, b: 255, a: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
export interface ParsedColor {
|
||||||
|
r: number;
|
||||||
|
g: number;
|
||||||
|
b: number;
|
||||||
|
a?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将颜色字符串解析为包含红色、绿色、蓝色和 alpha 分量的对象。
|
||||||
|
*/
|
||||||
|
export function parseColor(color: string): ParsedColor {
|
||||||
|
const match = color.match(
|
||||||
|
/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
r: parseInt(match[1], 10),
|
||||||
|
g: parseInt(match[2], 10),
|
||||||
|
b: parseInt(match[3], 10),
|
||||||
|
a: match[4] ? parseFloat(match[4]) : 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const hex = color.replace("#", "");
|
||||||
|
return {
|
||||||
|
r: parseInt(hex.slice(0, 2), 16),
|
||||||
|
g: parseInt(hex.slice(2, 4), 16),
|
||||||
|
b: parseInt(hex.slice(4, 6), 16),
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user