forked from VinokurovVE/tests
Better map
This commit is contained in:
@ -17,9 +17,8 @@ import Settings from "./pages/Settings"
|
|||||||
import PasswordReset from "./pages/auth/PasswordReset"
|
import PasswordReset from "./pages/auth/PasswordReset"
|
||||||
import MapTest from "./pages/MapTest"
|
import MapTest from "./pages/MapTest"
|
||||||
import MonitorPage from "./pages/MonitorPage"
|
import MonitorPage from "./pages/MonitorPage"
|
||||||
import ChunkedUpload from "./components/map/ChunkedUpload"
|
|
||||||
import DashboardLayout from "./layouts/DashboardLayout"
|
import DashboardLayout from "./layouts/DashboardLayout"
|
||||||
import { IconApi, IconBuildingFactory2, IconDeviceDesktopAnalytics, IconFiles, IconFlag2, IconHome, IconLogin, IconLogin2, IconMap, IconPassword, IconReport, IconServer, IconSettings, IconShield, IconTable, IconUsers } from "@tabler/icons-react"
|
import { IconApi, IconBuildingFactory2, IconDeviceDesktopAnalytics, IconFiles, IconHome, IconLogin, IconLogin2, IconMap, IconPassword, IconReport, IconServer, IconSettings, IconShield, IconTable, IconUsers } from "@tabler/icons-react"
|
||||||
import { Box, Loader } from "@mantine/core"
|
import { Box, Loader } from "@mantine/core"
|
||||||
import TableTest from "./pages/TableTest"
|
import TableTest from "./pages/TableTest"
|
||||||
|
|
||||||
@ -142,15 +141,6 @@ export const pages = [
|
|||||||
dashboard: true,
|
dashboard: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "Chunk test",
|
|
||||||
path: "/chunk-test",
|
|
||||||
icon: <IconFlag2 />,
|
|
||||||
component: <ChunkedUpload />,
|
|
||||||
drawer: true,
|
|
||||||
dashboard: true,
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: "Монитор",
|
label: "Монитор",
|
||||||
path: "/monitor",
|
path: "/monitor",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Flex, Input, Table } from '@mantine/core';
|
import { Input, Table } from '@mantine/core';
|
||||||
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
|
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import styles from './CustomTable.module.scss'
|
import styles from './CustomTable.module.scss'
|
||||||
|
@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
import DocumentService from '../services/DocumentService'
|
import DocumentService from '../services/DocumentService'
|
||||||
import { mutate } from 'swr'
|
import { mutate } from 'swr'
|
||||||
import FileViewer from './modals/FileViewer'
|
import FileViewer from './modals/FileViewer'
|
||||||
import { ActionIcon, Anchor, Breadcrumbs, Button, FileButton, Flex, Loader, RingProgress, Table, Text } from '@mantine/core'
|
import { ActionIcon, Anchor, Breadcrumbs, Button, FileButton, Flex, Loader, RingProgress, ScrollAreaAutosize, Table, Text } from '@mantine/core'
|
||||||
import { IconCancel, IconDownload, IconFile, IconFilePlus, IconFileUpload, IconX } from '@tabler/icons-react'
|
import { IconCancel, IconDownload, IconFile, IconFilePlus, IconFileUpload, IconX } from '@tabler/icons-react'
|
||||||
|
|
||||||
interface FolderProps {
|
interface FolderProps {
|
||||||
@ -176,12 +176,7 @@ export default function FolderViewer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<ScrollAreaAutosize w={'100%'} h={'100%'} p={'sm'}>
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '16px',
|
|
||||||
p: '16px'
|
|
||||||
}}>
|
|
||||||
<FileViewer
|
<FileViewer
|
||||||
open={fileViewerModal}
|
open={fileViewerModal}
|
||||||
setOpen={setFileViewerModal}
|
setOpen={setFileViewerModal}
|
||||||
@ -313,6 +308,6 @@ export default function FolderViewer() {
|
|||||||
)}
|
)}
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,9 +1,8 @@
|
|||||||
import { AppBar, Box, CircularProgress, Dialog, Grid, IconButton, TextField, Toolbar } from '@mui/material'
|
import { AppBar, Box, Dialog, Grid, IconButton, Toolbar } from '@mui/material'
|
||||||
import { Fragment, useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { IRegion } from '../interfaces/fuel'
|
import { IRegion } from '../interfaces/fuel'
|
||||||
import { useRegions, useServers, useServersInfo } from '../hooks/swrHooks'
|
import { useRegions, useServers, useServersInfo } from '../hooks/swrHooks'
|
||||||
import ServerService from '../services/ServersService'
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'
|
|
||||||
import { Close, Cloud, CloudOff } from '@mui/icons-material'
|
import { Close, Cloud, CloudOff } from '@mui/icons-material'
|
||||||
import ServerData from './ServerData'
|
import ServerData from './ServerData'
|
||||||
import { IServersInfo } from '../interfaces/servers'
|
import { IServersInfo } from '../interfaces/servers'
|
||||||
@ -20,14 +19,14 @@ export default function ServersView() {
|
|||||||
|
|
||||||
const [selectedOption, setSelectedOption] = useState<number | null>(null)
|
const [selectedOption, setSelectedOption] = useState<number | null>(null)
|
||||||
|
|
||||||
const { regions, isLoading } = useRegions(10, 1, debouncedSearch)
|
const { regions } = useRegions(10, 1, debouncedSearch)
|
||||||
|
|
||||||
const { serversInfo } = useServersInfo(selectedOption)
|
const { serversInfo } = useServersInfo(selectedOption)
|
||||||
|
|
||||||
const [serverDataOpen, setServerDataOpen] = useState(false)
|
const [serverDataOpen, setServerDataOpen] = useState(false)
|
||||||
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
const [currentServerData, setCurrentServerData] = useState<any | null>(null)
|
||||||
|
|
||||||
const { servers, isLoading: serversLoading } = useServers(selectedOption, 0, 10)
|
const { servers } = useServers(selectedOption, 0, 10)
|
||||||
|
|
||||||
const serversColumns: GridColDef[] = [
|
const serversColumns: GridColDef[] = [
|
||||||
//{ field: 'id', headerName: 'ID', type: "number" },
|
//{ field: 'id', headerName: 'ID', type: "number" },
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const ChunkedUpload = () => {
|
|
||||||
const [file, setFile] = useState<File | null>(null);
|
|
||||||
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
|
||||||
|
|
||||||
// Handle file selection
|
|
||||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
if (event.target.files) {
|
|
||||||
setFile(event.target.files[0]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Upload the file in chunks
|
|
||||||
const uploadFile = async () => {
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const chunkSize = 1024 * 1024; // 1MB per chunk
|
|
||||||
const totalChunks = Math.ceil(file.size / chunkSize);
|
|
||||||
const fileId = `${file.name}-${Date.now()}`; // Unique file identifier
|
|
||||||
let uploadedChunks = 0;
|
|
||||||
|
|
||||||
for (let start = 0; start < file.size; start += chunkSize) {
|
|
||||||
const chunk = file.slice(start, start + chunkSize);
|
|
||||||
const chunkNumber = Math.ceil(start / chunkSize) + 1;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await axios.post(`${import.meta.env.VITE_API_EMS_URL}/upload`, chunk, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/octet-stream',
|
|
||||||
'X-Chunk-Number': chunkNumber.toString(),
|
|
||||||
'X-Total-Chunks': totalChunks.toString(),
|
|
||||||
'X-File-Id': fileId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
uploadedChunks++;
|
|
||||||
setUploadProgress((uploadedChunks / totalChunks) * 100);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Chunk upload failed', error);
|
|
||||||
// Implement retry logic if needed
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<input type="file" onChange={handleFileChange} />
|
|
||||||
<button onClick={uploadFile} disabled={!file}>
|
|
||||||
Upload File
|
|
||||||
</button>
|
|
||||||
<div>Upload Progress: {uploadProgress.toFixed(2)}%</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChunkedUpload;
|
|
@ -6,7 +6,6 @@ import View from 'ol/View'
|
|||||||
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
|
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
|
||||||
import { ImageStatic, OSM, Vector as VectorSource, XYZ } from 'ol/source'
|
import { ImageStatic, OSM, Vector as VectorSource, XYZ } from 'ol/source'
|
||||||
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
|
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
|
||||||
import { Stack, Typography } from '@mui/material'
|
|
||||||
import { Type } from 'ol/geom/Geometry'
|
import { Type } from 'ol/geom/Geometry'
|
||||||
import { click, never, noModifierKeys, platformModifierKeyOnly, primaryAction, shiftKeyOnly } from 'ol/events/condition'
|
import { click, never, noModifierKeys, platformModifierKeyOnly, primaryAction, shiftKeyOnly } from 'ol/events/condition'
|
||||||
import Feature from 'ol/Feature'
|
import Feature from 'ol/Feature'
|
||||||
@ -24,27 +23,30 @@ import { Coordinate } from 'ol/coordinate'
|
|||||||
import { Stroke, Fill, Circle as CircleStyle, Style, Text } from 'ol/style'
|
import { Stroke, Fill, Circle as CircleStyle, Style, Text } from 'ol/style'
|
||||||
import { calculateCenter, calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
|
import { calculateCenter, calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
|
||||||
import MapBrowserEvent from 'ol/MapBrowserEvent'
|
import MapBrowserEvent from 'ol/MapBrowserEvent'
|
||||||
import { fromLonLat, get, transform } from 'ol/proj'
|
import { get, transform } from 'ol/proj'
|
||||||
import { useCities } from '../../hooks/swrHooks'
|
import { useCities } from '../../hooks/swrHooks'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { fetcher } from '../../http/axiosInstance'
|
import { fetcher } from '../../http/axiosInstance'
|
||||||
import { BASE_URL } from '../../constants'
|
import { BASE_URL } from '../../constants'
|
||||||
import { Accordion, ActionIcon, Box, Button, Flex, Select as MantineSelect, MantineStyleProp, rem, Slider, useMantineColorScheme } from '@mantine/core'
|
import { Accordion, ActionIcon, Autocomplete, Box, Button, CloseButton, Flex, Grid, Select as MantineSelect, Text as MantineText, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Divider, Portal } from '@mantine/core'
|
||||||
import { IconApi, IconArrowBackUp, IconArrowsMove, IconCircle, IconExclamationCircle, IconLine, IconPlus, IconPoint, IconPolygon, IconRuler, IconTable, IconUpload } from '@tabler/icons-react'
|
import { IconApi, IconArrowBackUp, IconArrowsMove, IconCircle, IconExclamationCircle, IconLine, IconPlus, IconPoint, IconPolygon, IconRuler, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
|
||||||
import { getGridCellPosition } from './mapUtils'
|
import { getGridCellPosition } from './mapUtils'
|
||||||
import { IFigure, ILine } from '../../interfaces/gis'
|
import { IFigure, ILine } from '../../interfaces/gis'
|
||||||
import { Height } from '@mui/icons-material'
|
import axios from 'axios'
|
||||||
|
import ObjectParameter from './ObjectParameter'
|
||||||
|
import { IObjectData, IObjectParam } from '../../interfaces/objects'
|
||||||
|
import ObjectData from './ObjectData'
|
||||||
|
|
||||||
const MapComponent = () => {
|
const MapComponent = () => {
|
||||||
const { cities } = useCities(100, 1)
|
//const { cities } = useCities(100, 1)
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (cities) {
|
// if (cities) {
|
||||||
cities.map((city: any) => {
|
// cities.map((city: any) => {
|
||||||
citiesLayer.current?.getSource()?.addFeature(new Feature(new Point(fromLonLat([city.longitude, city.width]))))
|
// citiesLayer.current?.getSource()?.addFeature(new Feature(new Point(fromLonLat([city.longitude, city.width]))))
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}, [cities])
|
// }, [cities])
|
||||||
|
|
||||||
const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
|
const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
|
||||||
const [currentZ, setCurrentZ] = useState<number | undefined>(undefined)
|
const [currentZ, setCurrentZ] = useState<number | undefined>(undefined)
|
||||||
@ -104,6 +106,10 @@ const MapComponent = () => {
|
|||||||
source: new VectorSource()
|
source: new VectorSource()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const linesLayer = useRef<VectorLayer>(new VectorLayer({
|
||||||
|
source: new VectorSource()
|
||||||
|
}))
|
||||||
|
|
||||||
const regionsLayer = useRef<VectorImageLayer>(new VectorImageLayer({
|
const regionsLayer = useRef<VectorImageLayer>(new VectorImageLayer({
|
||||||
source: regionsLayerSource,
|
source: regionsLayerSource,
|
||||||
style: regionsLayerStyle
|
style: regionsLayerStyle
|
||||||
@ -576,7 +582,7 @@ const MapComponent = () => {
|
|||||||
|
|
||||||
map.current = new Map({
|
map.current = new Map({
|
||||||
controls: [],
|
controls: [],
|
||||||
layers: [baseLayer.current, satLayer.current, regionsLayer.current, citiesLayer.current, figuresLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current, nodeLayer.current],
|
layers: [baseLayer.current, satLayer.current, regionsLayer.current, citiesLayer.current, linesLayer.current, figuresLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current, nodeLayer.current],
|
||||||
target: mapElement.current as HTMLDivElement,
|
target: mapElement.current as HTMLDivElement,
|
||||||
view: new View({
|
view: new View({
|
||||||
center: transform([129.7659541, 62.009504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
|
center: transform([129.7659541, 62.009504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
|
||||||
@ -596,6 +602,14 @@ const MapComponent = () => {
|
|||||||
setCurrentY(tileY)
|
setCurrentY(tileY)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
map.current.on('click', function (e: MapBrowserEvent<any>) {
|
||||||
|
const pixel = map.current?.getEventPixel(e.originalEvent)
|
||||||
|
|
||||||
|
map.current?.forEachFeatureAtPixel(pixel, function (feature) {
|
||||||
|
setCurrentObjectId(feature.get('object_id'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const modify = new Modify({ source: drawingLayerSource.current })
|
const modify = new Modify({ source: drawingLayerSource.current })
|
||||||
map.current.addInteraction(modify)
|
map.current.addInteraction(modify)
|
||||||
|
|
||||||
@ -842,291 +856,407 @@ const MapComponent = () => {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [currentObjectId, setCurrentObjectId] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const [selectedCity, setSelectedCity] = useState<number | null>(null)
|
||||||
|
const [selectedYear, setSelectedYear] = useState<number | null>(2023)
|
||||||
|
const [citiesPage, setCitiesPage] = useState<number>(0)
|
||||||
|
|
||||||
|
const [searchCity, setSearchCity] = useState<string | undefined>("")
|
||||||
|
|
||||||
|
const { data: currentObjectData } = useSWR(
|
||||||
|
currentObjectId ? `/general/objects/${currentObjectId}` : null,
|
||||||
|
(url) => fetcher(url, BASE_URL.ems),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { data: valuesData } = useSWR(
|
||||||
|
currentObjectId ? `/general/values/all?object_id=${currentObjectId}` : null,
|
||||||
|
(url) => fetcher(url, BASE_URL.ems),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { data: citiesData } = useSWR(
|
||||||
|
`/general/cities/all?limit=${10}&offset=${citiesPage || 0}${searchCity ? `&search=${searchCity}` : ''}`,
|
||||||
|
(url) => fetcher(url, BASE_URL.ems),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { data: figuresData, isValidating: figuresValidating } = useSWR(
|
||||||
|
selectedCity && selectedYear ? `/gis/figures/all?city_id=${selectedCity}&year=${selectedYear}&offset=0&limit=${10000}` : null,
|
||||||
|
(url) => axios.get(url, {
|
||||||
|
baseURL: BASE_URL.ems
|
||||||
|
}).then((res) => res.data),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { data: linesData } = useSWR(
|
||||||
|
!figuresValidating && selectedCity && selectedYear ? `/gis/lines/all?city_id=${selectedCity}&year=${selectedYear}&offset=0&limit=${10000}` : null,
|
||||||
|
(url) => axios.get(url, {
|
||||||
|
baseURL: BASE_URL.ems
|
||||||
|
}).then((res) => {
|
||||||
|
return res.data
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Array.isArray(figuresData)) {
|
||||||
|
figuresLayer.current.getSource()?.clear()
|
||||||
|
if (figuresData.length > 0) {
|
||||||
|
const scaling = {
|
||||||
|
w: 10000, // responseData[0].width
|
||||||
|
h: 10000 // responseData[0].width
|
||||||
|
}
|
||||||
|
|
||||||
|
figuresData.map((figure: IFigure) => {
|
||||||
|
if (figure.figure_type_id == 1) {
|
||||||
|
const width = figure.width * scaling.w
|
||||||
|
const height = figure.height * scaling.h
|
||||||
|
|
||||||
|
const left = figure.left * scaling.w
|
||||||
|
const top = figure.top * scaling.h
|
||||||
|
|
||||||
|
const centerX = mapCenter[0] + left + (width / 2)
|
||||||
|
const centerY = mapCenter[1] - top - (height / 2)
|
||||||
|
|
||||||
|
const radius = width / 2;
|
||||||
|
const circleGeom = new Circle([centerX, centerY], radius)
|
||||||
|
|
||||||
|
const ellipseGeom = fromCircle(circleGeom, 64)
|
||||||
|
ellipseGeom.scale(1, height / width)
|
||||||
|
|
||||||
|
const feature = new Feature(ellipseGeom)
|
||||||
|
|
||||||
|
feature.setStyle(firstStyleFunction(feature))
|
||||||
|
feature.set('object_id', figure.object_id)
|
||||||
|
figuresLayer.current?.getSource()?.addFeature(feature)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (figure.figure_type_id == 3) {
|
||||||
|
const x = figure.left * scaling.w
|
||||||
|
const y = figure.top * scaling.h
|
||||||
|
|
||||||
|
const center = [mapCenter[0] + x, mapCenter[1] - y]
|
||||||
|
|
||||||
|
const coords = figure.points?.split(' ').map(pair => {
|
||||||
|
const [x, y] = pair.split(';').map(Number)
|
||||||
|
return [
|
||||||
|
center[0] + (x * scaling.w),
|
||||||
|
center[1] - (y * scaling.h)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
if (coords) {
|
||||||
|
const polygon = new Polygon([coords])
|
||||||
|
|
||||||
|
const feature = new Feature({
|
||||||
|
geometry: polygon
|
||||||
|
})
|
||||||
|
|
||||||
|
feature.set('object_id', figure.object_id)
|
||||||
|
feature.setStyle(thirdStyleFunction(feature))
|
||||||
|
figuresLayer.current?.getSource()?.addFeature(feature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (figure.figure_type_id == 4) {
|
||||||
|
const width = figure.width * scaling.w
|
||||||
|
const height = figure.height * scaling.h
|
||||||
|
const left = figure.left * scaling.w
|
||||||
|
const top = figure.top * scaling.h
|
||||||
|
|
||||||
|
const halfWidth = width / 2
|
||||||
|
const halfHeight = height / 2
|
||||||
|
|
||||||
|
const center = [mapCenter[0] + left + halfWidth, mapCenter[1] - top - halfHeight]
|
||||||
|
|
||||||
|
const testCoords = [
|
||||||
|
[center[0] - halfWidth, center[1] - halfHeight],
|
||||||
|
[center[0] - halfWidth, center[1] + halfHeight],
|
||||||
|
[center[0] + halfWidth, center[1] + halfHeight],
|
||||||
|
[center[0] + halfWidth, center[1] - halfHeight],
|
||||||
|
[center[0] - halfWidth, center[1] - halfHeight]
|
||||||
|
]
|
||||||
|
|
||||||
|
const geometry1 = new Polygon([testCoords])
|
||||||
|
const anchor1 = center
|
||||||
|
geometry1.rotate(-figure.angle * Math.PI / 180, anchor1)
|
||||||
|
const feature1 = new Feature(geometry1)
|
||||||
|
feature1.set('object_id', figure.object_id)
|
||||||
|
feature1.set('angle', figure.angle)
|
||||||
|
feature1.setStyle(fourthStyleFunction(feature1))
|
||||||
|
figuresLayer.current?.getSource()?.addFeature(feature1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(linesData)) {
|
||||||
|
linesLayer.current.getSource()?.clear()
|
||||||
|
if (linesData.length > 0) {
|
||||||
|
const scaling = {
|
||||||
|
w: 10000, // responseData[0].width
|
||||||
|
h: 10000 // responseData[0].width
|
||||||
|
}
|
||||||
|
|
||||||
|
linesData.map((line: ILine) => {
|
||||||
|
const x1 = line.x1 * scaling.w
|
||||||
|
const y1 = line.y1 * scaling.h
|
||||||
|
const x2 = line.x2 * scaling.w
|
||||||
|
const y2 = line.y2 * scaling.h
|
||||||
|
|
||||||
|
const center = [mapCenter[0], mapCenter[1]]
|
||||||
|
|
||||||
|
const testCoords = [
|
||||||
|
[center[0] + x1, center[1] - y1],
|
||||||
|
[center[0] + x2, center[1] - y2],
|
||||||
|
]
|
||||||
|
|
||||||
|
const feature = new Feature(new LineString(testCoords))
|
||||||
|
feature.setStyle(styleFunction(feature))
|
||||||
|
feature.set('object_id', line.object_id)
|
||||||
|
|
||||||
|
linesLayer.current?.getSource()?.addFeature(feature)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [figuresData, linesData, selectedCity, selectedYear])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box w={'100%'} h={'calc(100% - 64px)'} mah={'100%'} flex={'1'} pos={'relative'}>
|
<Box w={'100%'} h='100%' pos={'relative'}>
|
||||||
<ActionIcon.Group orientation='vertical' pos='absolute' top='8px' right='8px' style={{ zIndex: 1, backdropFilter: 'blur(8px)', backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA', borderRadius: '4px' }}>
|
<Portal target='#header-portal'>
|
||||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
<Flex gap={'sm'} direction={'row'}>
|
||||||
fetch(`${import.meta.env.VITE_API_EMS_URL}/hello`, { method: 'GET' }).then(res => console.log(res))
|
<form>
|
||||||
}}>
|
<Autocomplete
|
||||||
<IconApi />
|
placeholder="Район"
|
||||||
</ActionIcon>
|
flex={'1'}
|
||||||
|
data={citiesData ? citiesData.map((item: any) => ({ label: item.name, value: item.id.toString() })) : []}
|
||||||
|
//onSelect={(e) => console.log(e.currentTarget.value)}
|
||||||
|
onChange={(value) => setSearchCity(value)}
|
||||||
|
onOptionSubmit={(value) => setSelectedCity(Number(value))}
|
||||||
|
rightSection={
|
||||||
|
searchCity !== '' && (
|
||||||
|
<CloseButton
|
||||||
|
size="sm"
|
||||||
|
onMouseDown={(event) => event.preventDefault()}
|
||||||
|
onClick={() => {
|
||||||
|
setSearchCity('')
|
||||||
|
setSelectedCity(null)
|
||||||
|
}}
|
||||||
|
aria-label="Clear value"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value={searchCity}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
|
||||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
<MantineSelect
|
||||||
saveFeatures()
|
data={[
|
||||||
}}>
|
{ label: '2018', value: '2018' },
|
||||||
<IconExclamationCircle />
|
{ label: '2019', value: '2019' },
|
||||||
</ActionIcon>
|
{ label: '2020', value: '2020' },
|
||||||
|
{ label: '2021', value: '2021' },
|
||||||
|
{ label: '2022', value: '2022' },
|
||||||
|
{ label: '2023', value: '2023' },
|
||||||
|
{ label: '2024', value: '2024' },
|
||||||
|
]}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelectedYear(Number(e))
|
||||||
|
}}
|
||||||
|
defaultValue={selectedYear?.toString()}
|
||||||
|
//variant="unstyled"
|
||||||
|
allowDeselect={false}
|
||||||
|
/>
|
||||||
|
|
||||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
|
||||||
draw.current?.removeLastPoint()
|
|
||||||
}}>
|
|
||||||
<IconArrowBackUp />
|
|
||||||
</ActionIcon>
|
|
||||||
|
|
||||||
<ActionIcon
|
|
||||||
size='lg'
|
|
||||||
variant={currentTool === 'Point' ? 'filled' : 'transparent'}
|
|
||||||
onClick={() => {
|
|
||||||
handleToolSelect('Point')
|
|
||||||
}}>
|
|
||||||
<IconPoint />
|
|
||||||
</ActionIcon>
|
|
||||||
|
|
||||||
<ActionIcon
|
|
||||||
size='lg'
|
|
||||||
variant={currentTool === 'LineString' ? 'filled' : 'transparent'}
|
|
||||||
onClick={() => {
|
|
||||||
handleToolSelect('LineString')
|
|
||||||
}}>
|
|
||||||
<IconLine />
|
|
||||||
</ActionIcon>
|
|
||||||
|
|
||||||
<ActionIcon
|
|
||||||
size='lg'
|
|
||||||
variant={currentTool === 'Polygon' ? 'filled' : 'transparent'}
|
|
||||||
onClick={() => {
|
|
||||||
handleToolSelect('Polygon')
|
|
||||||
}}>
|
|
||||||
<IconPolygon />
|
|
||||||
</ActionIcon>
|
|
||||||
|
|
||||||
<ActionIcon
|
|
||||||
size='lg'
|
|
||||||
variant={currentTool === 'Circle' ? 'filled' : 'transparent'}
|
|
||||||
onClick={() => {
|
|
||||||
handleToolSelect('Circle')
|
|
||||||
}}>
|
|
||||||
<IconCircle />
|
|
||||||
</ActionIcon>
|
|
||||||
|
|
||||||
<ActionIcon
|
|
||||||
size='lg'
|
|
||||||
variant='transparent'
|
|
||||||
onClick={() => map?.current?.addInteraction(new Translate())}
|
|
||||||
>
|
|
||||||
<IconArrowsMove />
|
|
||||||
</ActionIcon>
|
|
||||||
|
|
||||||
<ActionIcon
|
|
||||||
size='lg'
|
|
||||||
variant='transparent'
|
|
||||||
>
|
|
||||||
<IconRuler />
|
|
||||||
</ActionIcon>
|
|
||||||
</ActionIcon.Group>
|
|
||||||
|
|
||||||
<Flex direction='column' style={{
|
|
||||||
...mapControlsStyle,
|
|
||||||
maxWidth: '300px',
|
|
||||||
width: '100%',
|
|
||||||
top: '8px',
|
|
||||||
left: '8px'
|
|
||||||
}}>
|
|
||||||
<Flex direction='row'>
|
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
size='lg'
|
size='lg'
|
||||||
variant='transparent'
|
variant='transparent'
|
||||||
onClick={() => submitOverlay()}
|
title='Настройки ИКС'
|
||||||
>
|
>
|
||||||
<IconUpload style={{ width: rem(20), height: rem(20) }} />
|
<IconSettings style={{ width: rem(20), height: rem(20) }} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
</Portal>
|
||||||
|
|
||||||
|
<Flex w={'100%'} h={'100%'} pos={'absolute'}>
|
||||||
|
<ActionIcon.Group orientation='vertical' pos='absolute' top='8px' right='8px' style={{ zIndex: 1, backdropFilter: 'blur(8px)', backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA', borderRadius: '4px' }}>
|
||||||
|
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||||
|
fetch(`${import.meta.env.VITE_API_EMS_URL}/hello`, { method: 'GET' }).then(res => console.log(res))
|
||||||
|
}}>
|
||||||
|
<IconApi />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||||
|
saveFeatures()
|
||||||
|
}}>
|
||||||
|
<IconExclamationCircle />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||||
|
draw.current?.removeLastPoint()
|
||||||
|
}}>
|
||||||
|
<IconArrowBackUp />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<ActionIcon
|
||||||
|
size='lg'
|
||||||
|
variant={currentTool === 'Point' ? 'filled' : 'transparent'}
|
||||||
|
onClick={() => {
|
||||||
|
handleToolSelect('Point')
|
||||||
|
}}>
|
||||||
|
<IconPoint />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<ActionIcon
|
||||||
|
size='lg'
|
||||||
|
variant={currentTool === 'LineString' ? 'filled' : 'transparent'}
|
||||||
|
onClick={() => {
|
||||||
|
handleToolSelect('LineString')
|
||||||
|
}}>
|
||||||
|
<IconLine />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<ActionIcon
|
||||||
|
size='lg'
|
||||||
|
variant={currentTool === 'Polygon' ? 'filled' : 'transparent'}
|
||||||
|
onClick={() => {
|
||||||
|
handleToolSelect('Polygon')
|
||||||
|
}}>
|
||||||
|
<IconPolygon />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<ActionIcon
|
||||||
|
size='lg'
|
||||||
|
variant={currentTool === 'Circle' ? 'filled' : 'transparent'}
|
||||||
|
onClick={() => {
|
||||||
|
handleToolSelect('Circle')
|
||||||
|
}}>
|
||||||
|
<IconCircle />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
size='lg'
|
size='lg'
|
||||||
variant='transparent'
|
variant='transparent'
|
||||||
title='Добавить подложку'
|
onClick={() => map?.current?.addInteraction(new Translate())}
|
||||||
>
|
>
|
||||||
<IconPlus style={{ width: rem(20), height: rem(20) }} />
|
<IconArrowsMove />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex align='center' direction='row' p='sm' gap='sm'>
|
<ActionIcon
|
||||||
<Slider w='100%' min={0} max={1} step={0.001} value={satelliteOpacity} defaultValue={satelliteOpacity} onChange={(value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />
|
size='lg'
|
||||||
|
variant='transparent'
|
||||||
|
>
|
||||||
|
<IconRuler />
|
||||||
|
</ActionIcon>
|
||||||
|
</ActionIcon.Group>
|
||||||
|
|
||||||
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
<Flex direction='column' mah={'86%'} pl='sm' style={{
|
||||||
</Flex>
|
...mapControlsStyle,
|
||||||
|
maxWidth: '300px',
|
||||||
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
|
width: '100%',
|
||||||
<Accordion.Item key={'s'} value={'Объекты'}>
|
top: '8px',
|
||||||
<Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
|
left: '8px',
|
||||||
<Accordion.Panel>
|
}}>
|
||||||
<Button
|
<ScrollAreaAutosize offsetScrollbars>
|
||||||
onClick={async () => {
|
<Flex direction='row'>
|
||||||
//open()
|
<ActionIcon
|
||||||
|
size='lg'
|
||||||
const city_id = 145
|
variant='transparent'
|
||||||
const year = 2023
|
onClick={() => submitOverlay()}
|
||||||
|
|
||||||
const figuresLimit = 10000
|
|
||||||
const linesLimit = 10000
|
|
||||||
|
|
||||||
figuresLayer.current.getSource()?.clear()
|
|
||||||
|
|
||||||
try {
|
|
||||||
// const response = await fetch(`${import.meta.env.VITE_API_EMS_URL}/gis/images/all?city_id=${city_id}`, {
|
|
||||||
// method: 'GET',
|
|
||||||
// headers: {
|
|
||||||
// 'Content-Type': 'application/json',
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
|
|
||||||
//const responseData = await response.json()
|
|
||||||
|
|
||||||
const scaling = {
|
|
||||||
w: 10000, // responseData[0].width
|
|
||||||
h: 10000 // responseData[0].width
|
|
||||||
}
|
|
||||||
|
|
||||||
const figuresResponse = await fetch(`${import.meta.env.VITE_API_EMS_URL}/gis/figures/all?city_id=${city_id}&year=${year}&offset=0&limit=${figuresLimit}`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const linesResponse = await fetch(`${import.meta.env.VITE_API_EMS_URL}/gis/lines/all?city_id=${city_id}&year=${year}&offset=0&limit=${linesLimit}`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await linesResponse.json().then(linesData => {
|
|
||||||
linesData.map((line: ILine) => {
|
|
||||||
const x1 = line.x1 * scaling.w
|
|
||||||
const y1 = line.y1 * scaling.h
|
|
||||||
const x2 = line.x2 * scaling.w
|
|
||||||
const y2 = line.y2 * scaling.h
|
|
||||||
|
|
||||||
const center = [mapCenter[0], mapCenter[1]]
|
|
||||||
|
|
||||||
const testCoords = [
|
|
||||||
[center[0] + x1, center[1] - y1],
|
|
||||||
[center[0] + x2, center[1] - y2],
|
|
||||||
]
|
|
||||||
|
|
||||||
const feature = new Feature(new LineString(testCoords))
|
|
||||||
feature.setStyle(styleFunction(feature))
|
|
||||||
feature.set('object_id', line.object_id)
|
|
||||||
|
|
||||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await figuresResponse.json().then(figuresData => {
|
|
||||||
figuresData.map((figure: IFigure) => {
|
|
||||||
if (figure.figure_type_id == 1) {
|
|
||||||
const width = figure.width * scaling.w
|
|
||||||
const height = figure.height * scaling.h
|
|
||||||
|
|
||||||
const left = figure.left * scaling.w
|
|
||||||
const top = figure.top * scaling.h
|
|
||||||
|
|
||||||
const centerX = mapCenter[0] + left + (width / 2)
|
|
||||||
const centerY = mapCenter[1] - top - (height / 2)
|
|
||||||
|
|
||||||
const radius = width / 2;
|
|
||||||
const circleGeom = new Circle([centerX, centerY], radius)
|
|
||||||
|
|
||||||
const ellipseGeom = fromCircle(circleGeom, 64)
|
|
||||||
ellipseGeom.scale(1, height / width)
|
|
||||||
|
|
||||||
const feature = new Feature(ellipseGeom)
|
|
||||||
|
|
||||||
feature.setStyle(firstStyleFunction(feature))
|
|
||||||
feature.set('object_id', figure.object_id)
|
|
||||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (figure.figure_type_id == 3) {
|
|
||||||
const x = figure.left * scaling.w
|
|
||||||
const y = figure.top * scaling.h
|
|
||||||
|
|
||||||
const center = [mapCenter[0] + x, mapCenter[1] - y]
|
|
||||||
|
|
||||||
const coords = figure.points?.split(' ').map(pair => {
|
|
||||||
const [x, y] = pair.split(';').map(Number)
|
|
||||||
return [
|
|
||||||
center[0] + (x * scaling.w),
|
|
||||||
center[1] - (y * scaling.h)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
if (coords) {
|
|
||||||
const polygon = new Polygon([coords])
|
|
||||||
|
|
||||||
const feature = new Feature({
|
|
||||||
geometry: polygon
|
|
||||||
})
|
|
||||||
|
|
||||||
feature.set('object_id', figure.object_id)
|
|
||||||
feature.setStyle(thirdStyleFunction(feature))
|
|
||||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (figure.figure_type_id == 4) {
|
|
||||||
const width = figure.width * scaling.w
|
|
||||||
const height = figure.height * scaling.h
|
|
||||||
const left = figure.left * scaling.w
|
|
||||||
const top = figure.top * scaling.h
|
|
||||||
|
|
||||||
const halfWidth = width / 2
|
|
||||||
const halfHeight = height / 2
|
|
||||||
|
|
||||||
const center = [mapCenter[0] + left + halfWidth, mapCenter[1] - top - halfHeight]
|
|
||||||
|
|
||||||
const testCoords = [
|
|
||||||
[center[0] - halfWidth, center[1] - halfHeight],
|
|
||||||
[center[0] - halfWidth, center[1] + halfHeight],
|
|
||||||
[center[0] + halfWidth, center[1] + halfHeight],
|
|
||||||
[center[0] + halfWidth, center[1] - halfHeight],
|
|
||||||
[center[0] - halfWidth, center[1] - halfHeight]
|
|
||||||
]
|
|
||||||
|
|
||||||
const geometry1 = new Polygon([testCoords])
|
|
||||||
const anchor1 = center
|
|
||||||
geometry1.rotate(-figure.angle * Math.PI / 180, anchor1)
|
|
||||||
const feature1 = new Feature(geometry1)
|
|
||||||
feature1.set('object_id', figure.object_id)
|
|
||||||
feature1.set('angle', figure.angle)
|
|
||||||
feature1.setStyle(fourthStyleFunction(feature1))
|
|
||||||
figuresLayer.current?.getSource()?.addFeature(feature1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.error('No data')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Test
|
<IconUpload style={{ width: rem(20), height: rem(20) }} />
|
||||||
</Button>
|
</ActionIcon>
|
||||||
</Accordion.Panel>
|
|
||||||
</Accordion.Item>
|
|
||||||
</Accordion>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex direction='row' pos='absolute' bottom='8px' left='8px' style={{ ...mapControlsStyle }}>
|
<ActionIcon
|
||||||
<Stack>
|
size='lg'
|
||||||
<Typography>
|
variant='transparent'
|
||||||
|
title='Добавить подложку'
|
||||||
|
>
|
||||||
|
<IconPlus style={{ width: rem(20), height: rem(20) }} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex align='center' direction='row' p='sm' gap='sm'>
|
||||||
|
<Slider w='100%' min={0} max={1} step={0.001} value={satelliteOpacity} defaultValue={satelliteOpacity} onChange={(value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />
|
||||||
|
|
||||||
|
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
|
||||||
|
<Accordion.Item key={'objects'} value={'Объекты'}>
|
||||||
|
<Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
|
||||||
|
<Accordion.Item key={'current_object'} value='current_object'>
|
||||||
|
<Accordion.Control icon={<IconTable />}>
|
||||||
|
{'Текущий объект'}
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<ObjectData {...currentObjectData as IObjectData} />
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
|
||||||
|
{valuesData && <Accordion.Item key={'parameters'} value={'Параметры объекта'}>
|
||||||
|
<Accordion.Control icon={<IconTable />}>{'Параметры объекта'}</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<Flex gap={'sm'} direction={'column'}>
|
||||||
|
{Array.isArray(valuesData) && valuesData.map((param: IObjectParam) => {
|
||||||
|
return (
|
||||||
|
<ObjectParameter key={param.id_param} value={param.value} id_param={param.id_param} />
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>}
|
||||||
|
</Accordion>
|
||||||
|
</ScrollAreaAutosize>
|
||||||
|
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex gap='sm' p={'4px'} miw={'100%'} fz={'xs'} pos='absolute' bottom='0px' left='0px' style={{ ...mapControlsStyle, borderRadius: 0 }}>
|
||||||
|
<MantineText fz='xs' w={rem(130)}>
|
||||||
x: {currentCoordinate?.[0]}
|
x: {currentCoordinate?.[0]}
|
||||||
</Typography>
|
</MantineText>
|
||||||
<Typography>
|
|
||||||
|
<MantineText fz='xs' w={rem(130)}>
|
||||||
y: {currentCoordinate?.[1]}
|
y: {currentCoordinate?.[1]}
|
||||||
</Typography>
|
</MantineText>
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Typography>
|
<Divider orientation='vertical' />
|
||||||
Z={currentZ}
|
|
||||||
X={currentX}
|
<MantineText fz='xs'>
|
||||||
Y={currentY}
|
Z={currentZ}
|
||||||
</Typography>
|
</MantineText>
|
||||||
|
|
||||||
|
<MantineText fz='xs'>
|
||||||
|
X={currentX}
|
||||||
|
</MantineText>
|
||||||
|
|
||||||
|
<MantineText fz='xs'>
|
||||||
|
Y={currentY}
|
||||||
|
</MantineText>
|
||||||
|
|
||||||
|
<MantineText fz='xs' ml='auto'>
|
||||||
|
{statusText}
|
||||||
|
</MantineText>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex direction='row' style={{ ...mapControlsStyle, bottom: '8px', right: '8px' }}>
|
|
||||||
{statusText}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="map-container"
|
id="map-container"
|
||||||
|
25
client/src/components/map/ObjectData.tsx
Normal file
25
client/src/components/map/ObjectData.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Flex, Table } from '@mantine/core'
|
||||||
|
import { IObjectData, IObjectType } from '../../interfaces/objects'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
import { fetcher } from '../../http/axiosInstance'
|
||||||
|
import { BASE_URL } from '../../constants'
|
||||||
|
|
||||||
|
const ObjectData = (object_data: IObjectData) => {
|
||||||
|
const { data: typeData } = useSWR(
|
||||||
|
object_data.type ? `/general/types/all` : null,
|
||||||
|
(url) => fetcher(url, BASE_URL.ems),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap='sm' direction='column'>
|
||||||
|
{Array.isArray(typeData) && (typeData.find(type => Number(type.id) === Number(object_data.type)) as IObjectType).name}
|
||||||
|
|
||||||
|
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectData
|
51
client/src/components/map/ObjectParameter.tsx
Normal file
51
client/src/components/map/ObjectParameter.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
import { fetcher } from '../../http/axiosInstance'
|
||||||
|
import { BASE_URL } from '../../constants'
|
||||||
|
import { Checkbox, Flex, Grid } from '@mantine/core'
|
||||||
|
import { IObjectParam, IParam } from '../../interfaces/objects'
|
||||||
|
|
||||||
|
const ObjectParameter = ({
|
||||||
|
id_param,
|
||||||
|
value
|
||||||
|
}: IObjectParam) => {
|
||||||
|
const { data: paramData } = useSWR(
|
||||||
|
`/general/params/all?param_id=${id_param}`,
|
||||||
|
(url) => fetcher(url, BASE_URL.ems).then(res => res[0] as IParam),
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Parameter = (type: string, name: string, value: unknown) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'bit':
|
||||||
|
return (
|
||||||
|
<Grid align='center' gutter='xl'>
|
||||||
|
<Grid.Col span={1}>
|
||||||
|
<Checkbox defaultChecked={value as boolean} />
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={'auto'}>
|
||||||
|
<p>{name}</p>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Неподдерживаемый параметр
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{paramData &&
|
||||||
|
Parameter(paramData.format, paramData.name, value)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectParameter
|
38
client/src/interfaces/objects.ts
Normal file
38
client/src/interfaces/objects.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
export interface IObjectData {
|
||||||
|
object_id: string,
|
||||||
|
id_city: number,
|
||||||
|
year: number,
|
||||||
|
id_parent: number | null,
|
||||||
|
type: number,
|
||||||
|
planning: boolean,
|
||||||
|
activity: boolean,
|
||||||
|
kvr: string | null,
|
||||||
|
jur: string | null,
|
||||||
|
fuel: string | null,
|
||||||
|
boiler_id: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IObjectParam {
|
||||||
|
id_object: string,
|
||||||
|
id_param: number,
|
||||||
|
value: string,
|
||||||
|
date_s: string | null,
|
||||||
|
date_po: string | null,
|
||||||
|
id_user: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IParam {
|
||||||
|
id: number,
|
||||||
|
id_group: number | null,
|
||||||
|
name: string,
|
||||||
|
format: string,
|
||||||
|
vtable: string,
|
||||||
|
unit: string | null,
|
||||||
|
exact_format: string | null,
|
||||||
|
inHistory: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IObjectType {
|
||||||
|
id: number,
|
||||||
|
name: string
|
||||||
|
}
|
@ -49,10 +49,14 @@ function DashboardLayout() {
|
|||||||
<Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
<Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group w='100%'>
|
<Group w='auto'>
|
||||||
{getPageTitle()}
|
{getPageTitle()}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
<Group id='header-portal' w='auto' ml='auto'>
|
||||||
|
|
||||||
|
</Group>
|
||||||
|
|
||||||
<Group style={{ flexShrink: 0 }}>
|
<Group style={{ flexShrink: 0 }}>
|
||||||
<Menu
|
<Menu
|
||||||
width={260}
|
width={260}
|
||||||
@ -111,12 +115,17 @@ function DashboardLayout() {
|
|||||||
label={item.label}
|
label={item.label}
|
||||||
leftSection={item.icon}
|
leftSection={item.icon}
|
||||||
active={location.pathname === item.path}
|
active={location.pathname === item.path}
|
||||||
style={{textWrap: 'nowrap'}}
|
style={{ textWrap: 'nowrap' }}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</AppShell.Navbar>
|
</AppShell.Navbar>
|
||||||
<AppShell.Main>
|
<AppShell.Main>
|
||||||
<Outlet />
|
<Flex w={{
|
||||||
|
sm: desktopOpened ? 'calc(100% - 200px)' : 'calc(100% - 50px)',
|
||||||
|
base: '100%'
|
||||||
|
}} h={'calc(100% - 60px)'} style={{ transition: "width 0.2s ease" }} pos={'fixed'}>
|
||||||
|
<Outlet />
|
||||||
|
</Flex>
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { GridColDef } from '@mui/x-data-grid'
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useBoilers } from '../hooks/swrHooks'
|
import { useBoilers } from '../hooks/swrHooks'
|
||||||
import { Badge, Flex, Table, Text } from '@mantine/core'
|
import { Badge, ScrollAreaAutosize, Table, Text } from '@mantine/core'
|
||||||
|
|
||||||
function Boilers() {
|
function Boilers() {
|
||||||
const [boilersPage, setBoilersPage] = useState(1)
|
const [boilersPage, setBoilersPage] = useState(1)
|
||||||
@ -33,7 +33,7 @@ function Boilers() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' gap='sm' p='sm'>
|
<ScrollAreaAutosize w={'100%'} h={'100%'} p='sm'>
|
||||||
<Text size="xl" fw={600}>
|
<Text size="xl" fw={600}>
|
||||||
Котельные
|
Котельные
|
||||||
</Text>
|
</Text>
|
||||||
@ -78,7 +78,7 @@ function Boilers() {
|
|||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
}
|
}
|
||||||
</Flex>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export default function Main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' gap='sm' p='sm'>
|
<Flex w={'100%'} h={'100%'} direction='column' gap='sm' p='sm'>
|
||||||
<Text size="xl" fw={700}>
|
<Text size="xl" fw={700}>
|
||||||
Главная
|
Главная
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -3,14 +3,13 @@ import { IconError404 } from "@tabler/icons-react";
|
|||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<Flex p='sm' gap='sm' align='center' justify='center'>
|
<Flex w={'100%'} h={'100%'} p='sm' gap='sm' align='center' justify='center'>
|
||||||
<Flex direction='column' gap='sm' align='center'>
|
<Flex direction='column' gap='sm' align='center'>
|
||||||
<IconError404 size={100} />
|
<IconError404 size={100} />
|
||||||
<Text size="xl" fw={500} ta='center'>
|
<Text size="xl" fw={500} ta='center'>
|
||||||
Запрашиваемая страница не найдена.
|
Запрашиваемая страница не найдена.
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ import { useCities, useReport, useReportExport } from "../hooks/swrHooks"
|
|||||||
import { useDebounce } from "@uidotdev/usehooks"
|
import { useDebounce } from "@uidotdev/usehooks"
|
||||||
import { ICity } from "../interfaces/fuel"
|
import { ICity } from "../interfaces/fuel"
|
||||||
import { mutate } from "swr"
|
import { mutate } from "swr"
|
||||||
import { ActionIcon, Autocomplete, Badge, Button, CloseButton, Flex, Table } from "@mantine/core"
|
import { ActionIcon, Autocomplete, Badge, Button, CloseButton, Flex, ScrollAreaAutosize, Table } from "@mantine/core"
|
||||||
import { IconRefresh } from "@tabler/icons-react"
|
import { IconRefresh } from "@tabler/icons-react"
|
||||||
|
|
||||||
export default function Reports() {
|
export default function Reports() {
|
||||||
@ -40,7 +40,7 @@ export default function Reports() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' gap='sm' p='sm'>
|
<ScrollAreaAutosize w={'100%'} h={'100%'} p='sm'>
|
||||||
<Flex component="form" gap={'sm'}>
|
<Flex component="form" gap={'sm'}>
|
||||||
{/* <SearchableSelect /> */}
|
{/* <SearchableSelect /> */}
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
@ -201,6 +201,6 @@ export default function Reports() {
|
|||||||
onProcessRowUpdateError={() => {
|
onProcessRowUpdateError={() => {
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
</Flex>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ import { useRoles } from '../hooks/swrHooks'
|
|||||||
import { CreateField } from '../interfaces/create'
|
import { CreateField } from '../interfaces/create'
|
||||||
import RoleService from '../services/RoleService'
|
import RoleService from '../services/RoleService'
|
||||||
import FormFields from '../components/FormFields'
|
import FormFields from '../components/FormFields'
|
||||||
import { Button, Flex, Loader, Modal, Table } from '@mantine/core'
|
import { Button, Loader, Modal, ScrollAreaAutosize, Table } from '@mantine/core'
|
||||||
import { useDisclosure } from '@mantine/hooks'
|
import { useDisclosure } from '@mantine/hooks'
|
||||||
|
|
||||||
export default function Roles() {
|
export default function Roles() {
|
||||||
@ -26,7 +26,7 @@ export default function Roles() {
|
|||||||
if (isLoading) return <Loader />
|
if (isLoading) return <Loader />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' align='flex-start' gap='sm' p='sm'>
|
<ScrollAreaAutosize w={'100%'} h={'100%'} p='sm'>
|
||||||
<Button onClick={open}>
|
<Button onClick={open}>
|
||||||
Добавить роль
|
Добавить роль
|
||||||
</Button>
|
</Button>
|
||||||
@ -78,6 +78,6 @@ export default function Roles() {
|
|||||||
onProcessRowUpdateError={() => {
|
onProcessRowUpdateError={() => {
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
</Flex>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -3,13 +3,13 @@ import ServersView from "../components/ServersView"
|
|||||||
import ServerIpsView from "../components/ServerIpsView"
|
import ServerIpsView from "../components/ServerIpsView"
|
||||||
import ServerHardware from "../components/ServerHardware"
|
import ServerHardware from "../components/ServerHardware"
|
||||||
import ServerStorage from "../components/ServerStorages"
|
import ServerStorage from "../components/ServerStorages"
|
||||||
import { Flex, Tabs } from "@mantine/core"
|
import { Flex, ScrollAreaAutosize, Tabs } from "@mantine/core"
|
||||||
|
|
||||||
export default function Servers() {
|
export default function Servers() {
|
||||||
const [currentTab, setCurrentTab] = useState<string | null>('0')
|
const [currentTab, setCurrentTab] = useState<string | null>('0')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap='sm' p='sm' direction='column'>
|
<ScrollAreaAutosize w={'100%'} h={'100%'} p='sm'>
|
||||||
<Flex gap='sm' direction='column'>
|
<Flex gap='sm' direction='column'>
|
||||||
<Tabs value={currentTab} onChange={setCurrentTab}>
|
<Tabs value={currentTab} onChange={setCurrentTab}>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
@ -43,6 +43,6 @@ export default function Servers() {
|
|||||||
width={500}
|
width={500}
|
||||||
height={300}
|
height={300}
|
||||||
/> */}
|
/> */}
|
||||||
</Flex>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import { CreateField } from "../interfaces/create"
|
|||||||
import { IUser } from "../interfaces/user"
|
import { IUser } from "../interfaces/user"
|
||||||
import FormFields from "../components/FormFields"
|
import FormFields from "../components/FormFields"
|
||||||
import AuthService from "../services/AuthService"
|
import AuthService from "../services/AuthService"
|
||||||
import { Flex } from "@mantine/core"
|
import { Flex, ScrollAreaAutosize } from "@mantine/core"
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const { token } = useAuthStore()
|
const { token } = useAuthStore()
|
||||||
@ -39,10 +39,9 @@ export default function Settings() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<ScrollAreaAutosize
|
||||||
direction='column'
|
w={'100%'}
|
||||||
align='flex-start'
|
h={'100%'}
|
||||||
gap='sm'
|
|
||||||
p='sm'
|
p='sm'
|
||||||
>
|
>
|
||||||
{currentUser &&
|
{currentUser &&
|
||||||
@ -64,6 +63,6 @@ export default function Settings() {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
</Flex>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Flex } from '@mantine/core';
|
import { Flex } from '@mantine/core';
|
||||||
import CustomTable from '../components/CustomTable';
|
import CustomTable from '../components/CustomTable';
|
||||||
|
|
||||||
@ -6,24 +5,6 @@ function TableTest() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' align='flex-start' gap='sm' p='sm'>
|
<Flex direction='column' align='flex-start' gap='sm' p='sm'>
|
||||||
{/* <Table
|
|
||||||
columns={[
|
|
||||||
{ key: 'column1', title: 'Column 1', dataType: DataType.String },
|
|
||||||
{ key: 'column2', title: 'Column 2', dataType: DataType.String },
|
|
||||||
{ key: 'column3', title: 'Column 3', dataType: DataType.String },
|
|
||||||
{ key: 'column4', title: 'Column 4', dataType: DataType.String },
|
|
||||||
]}
|
|
||||||
data={Array(100).fill(undefined).map(
|
|
||||||
(_, index) => ({
|
|
||||||
column1: `column:1 row:${index}`,
|
|
||||||
column2: `column:2 row:${index}`,
|
|
||||||
column3: `column:3 row:${index}`,
|
|
||||||
column4: `column:4 row:${index}`,
|
|
||||||
id: index,
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
rowKeyField={'id'}
|
|
||||||
/> */}
|
|
||||||
<CustomTable />
|
<CustomTable />
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,7 @@ import { useEffect, useState } from "react"
|
|||||||
import { CreateField } from "../interfaces/create"
|
import { CreateField } from "../interfaces/create"
|
||||||
import UserService from "../services/UserService"
|
import UserService from "../services/UserService"
|
||||||
import FormFields from "../components/FormFields"
|
import FormFields from "../components/FormFields"
|
||||||
import { Badge, Button, Flex, Loader, Modal, Pagination, Select, Table } from "@mantine/core"
|
import { Badge, Button, Flex, Loader, Modal, Pagination, ScrollAreaAutosize, Select, Table } from "@mantine/core"
|
||||||
import { useDisclosure } from "@mantine/hooks"
|
import { useDisclosure } from "@mantine/hooks"
|
||||||
|
|
||||||
export default function Users() {
|
export default function Users() {
|
||||||
@ -65,7 +65,7 @@ export default function Users() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' align='flex-start' gap='sm' p='sm'>
|
<ScrollAreaAutosize w={'100%'} h={'100%'} p='sm'>
|
||||||
<Button onClick={open}>
|
<Button onClick={open}>
|
||||||
Добавить пользователя
|
Добавить пользователя
|
||||||
</Button>
|
</Button>
|
||||||
@ -157,6 +157,6 @@ export default function Users() {
|
|||||||
onProcessRowUpdateError={() => {
|
onProcessRowUpdateError={() => {
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
</Flex>
|
</ScrollAreaAutosize>
|
||||||
)
|
)
|
||||||
}
|
}
|
159
ems/src/index.ts
159
ems/src/index.ts
@ -9,9 +9,9 @@ import cors from 'cors'
|
|||||||
import { Coordinate } from './interfaces/map'
|
import { Coordinate } from './interfaces/map'
|
||||||
import { generateTilesForZoomLevel } from './utils/tiles'
|
import { generateTilesForZoomLevel } from './utils/tiles'
|
||||||
import { query, validationResult } from 'express-validator'
|
import { query, validationResult } from 'express-validator'
|
||||||
import { Connection, Request as TediousRequest } from 'tedious'
|
import { Connection, ConnectionConfiguration, Request as TediousRequest } from 'tedious'
|
||||||
|
|
||||||
const tedious = new Connection({
|
const tediousConfig: ConnectionConfiguration = {
|
||||||
server: 'localhost',
|
server: 'localhost',
|
||||||
options: {
|
options: {
|
||||||
trustServerCertificate: true,
|
trustServerCertificate: true,
|
||||||
@ -25,17 +25,7 @@ const tedious = new Connection({
|
|||||||
password: 'oMhthmsvbYHc'
|
password: 'oMhthmsvbYHc'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
tedious.on('connect', function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Error: ', err)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Connected to General')
|
|
||||||
})
|
|
||||||
|
|
||||||
tedious.connect()
|
|
||||||
|
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
const app = express()
|
const app = express()
|
||||||
@ -185,33 +175,53 @@ app.get('/tile/:provider/:z/:x/:y', async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
function tediousQuery(query: string) {
|
function tediousQuery(query: string) {
|
||||||
// Read all rows from table
|
// Read all rows from table
|
||||||
const request = new TediousRequest(
|
|
||||||
query,
|
|
||||||
(err, rowCount) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(`Executing ${query}, ${rowCount} rows.`);
|
|
||||||
console.error(err.message);
|
|
||||||
} else {
|
|
||||||
console.log(`Executing ${query}, ${rowCount} rows.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const result: any = [];
|
const connection = new Connection(tediousConfig)
|
||||||
|
|
||||||
request.on("row", (columns) => {
|
connection.on('connect', (err) => {
|
||||||
const entry: any = {};
|
if (err) {
|
||||||
columns.forEach((column: any) => {
|
reject(err)
|
||||||
entry[column.metadata.colName] = column.value;
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: any = [];
|
||||||
|
|
||||||
|
const request = new TediousRequest(
|
||||||
|
query,
|
||||||
|
(err, rowCount) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(`Executing ${query}, ${rowCount} rows.`);
|
||||||
|
console.error(err.message);
|
||||||
|
} else {
|
||||||
|
console.log(`Executing ${query}, ${rowCount} rows.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
request.on("row", (columns) => {
|
||||||
|
const entry: any = {};
|
||||||
|
columns.forEach((column: any) => {
|
||||||
|
entry[column.metadata.colName] = column.value;
|
||||||
|
});
|
||||||
|
result.push(entry);
|
||||||
});
|
});
|
||||||
result.push(entry);
|
|
||||||
});
|
|
||||||
|
|
||||||
request.on('error', error => reject(error));// some error happened, reject the promise
|
request.on('error', error => reject(error));// some error happened, reject the promise
|
||||||
request.on('requestCompleted', () => resolve(result)); // resolve the promise with the result rows.
|
request.on('requestCompleted', () => {
|
||||||
|
connection.close();
|
||||||
|
resolve(result)
|
||||||
|
}); // resolve the promise with the result rows.
|
||||||
|
|
||||||
tedious.execSql(request);
|
connection.execSql(request)
|
||||||
|
})
|
||||||
|
|
||||||
|
connection.on('error', (err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
connection.connect()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,16 +245,29 @@ app.get('/general/cities/all', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.get('/general/types/all', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const result = await tediousQuery(
|
||||||
|
`
|
||||||
|
SELECT * FROM nGeneral..tTypes
|
||||||
|
ORDER BY id
|
||||||
|
`
|
||||||
|
)
|
||||||
|
res.status(200).json(result)
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
app.get('/general/objects/all', async (req: Request, res: Response) => {
|
app.get('/general/objects/all', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { offset, limit, city_id, id } = req.query
|
const { offset, limit, city_id } = req.query
|
||||||
|
|
||||||
const result = await tediousQuery(
|
const result = await tediousQuery(
|
||||||
`
|
`
|
||||||
SELECT * FROM nGeneral..tObjects
|
SELECT * FROM nGeneral..vObjects
|
||||||
${city_id ? `WHERE id_city = ${city_id}` : ''}
|
${city_id ? `WHERE id_city = ${city_id}` : ''}
|
||||||
${id ? `WHERE id = '${id}'` : ''}
|
ORDER BY object_id
|
||||||
ORDER BY id
|
|
||||||
OFFSET ${Number(offset) || 0} ROWS
|
OFFSET ${Number(offset) || 0} ROWS
|
||||||
FETCH NEXT ${Number(limit) || 10} ROWS ONLY;
|
FETCH NEXT ${Number(limit) || 10} ROWS ONLY;
|
||||||
`
|
`
|
||||||
@ -255,6 +278,66 @@ app.get('/general/objects/all', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.get('/general/objects/:id([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const result = await tediousQuery(
|
||||||
|
`
|
||||||
|
SELECT * FROM nGeneral..vObjects
|
||||||
|
${id ? `WHERE object_id = '${id}'` : ''}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
if (Array.isArray(result) && result.length > 0) {
|
||||||
|
res.status(200).json(result[0])
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/general/values/all', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { object_id } = req.query
|
||||||
|
|
||||||
|
if (!object_id) {
|
||||||
|
res.status(500)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await tediousQuery(
|
||||||
|
`
|
||||||
|
SELECT * FROM nGeneral..tValues
|
||||||
|
WHERE id_object = '${object_id}'
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
res.status(200).json(result)
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/general/params/all', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { param_id } = req.query
|
||||||
|
|
||||||
|
if (!param_id) {
|
||||||
|
res.status(500)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await tediousQuery(
|
||||||
|
`
|
||||||
|
SELECT * FROM nGeneral..tParameters
|
||||||
|
WHERE id = '${param_id}'
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
res.status(200).json(result)
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
app.get('/gis/images/all', async (req: Request, res: Response) => {
|
app.get('/gis/images/all', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { offset, limit, city_id } = req.query
|
const { offset, limit, city_id } = req.query
|
||||||
|
Reference in New Issue
Block a user