diff --git a/package.json b/package.json index d75c597ca..a7212e46d 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,10 @@ "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "redux": "^4.2.0", - "typescript": "^4.7.4" + "typescript": "^4.7.4", + "konva": "^8.3.14", + "react-konva": "^18.2.3" + }, "scripts": { "start": "craco start", diff --git a/src/models/auth/_.ts b/src/models/auth/_.ts index 4e3dd638a..3d45cb829 100644 --- a/src/models/auth/_.ts +++ b/src/models/auth/_.ts @@ -315,6 +315,9 @@ export async function loadMenu(): Promise { { id: id(), title: '防治宣传', path: '/mgr/sg/byfz/byxc' }, ] }, + { + id: id(), title: '闸门监控', path: '/mgr/sg/zmjk', + }, { id: id(), title: '维修养护', path: '/mgr/sg/wxyh', }, diff --git a/src/service/apiurl.js b/src/service/apiurl.js index bcba9dc8d..854d64d47 100644 --- a/src/service/apiurl.js +++ b/src/service/apiurl.js @@ -55,6 +55,14 @@ const apiurl = { role: service_xyt + '/system/menu/list' }, + //闸门监控 + zmjk: { + getList : service_xyt + '/attGateB/list', + getInformation: service_xyt + '/attGateB/data', + getDamData: service_xyt + '/gatePore/listByStcd', + getVideo: service_xyt + '/gateValveCctvRel/list' + }, + // 基本情况 home: { yq: service_fxdd + '/real/rain/list',//'/stStbprpB/rainfallStationDetails/rainfallList',//雨情 diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 90b8d4b42..90d5b11bd 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -10,6 +10,20 @@ export function changeObjectStringToMoment(obj: { [key: string]: any }, fields: return ret; } +export function apertureMeter(val?: any): number | undefined { + if (typeof val !== 'number') { + return undefined + } + return val / 1000; +} + +export function renAperture(val?: any) { + if (typeof val !== 'number') { + return '-' + } + + return (val / 1000).toFixed(3); +} export async function base64FromFile(file: File): Promise { return new Promise((resolve) => { diff --git a/src/views/AppRouters.tsx b/src/views/AppRouters.tsx index fa4ffc054..37338e8b5 100644 --- a/src/views/AppRouters.tsx +++ b/src/views/AppRouters.tsx @@ -48,6 +48,7 @@ import Xjxpz from "./rcgl/xcxj/xjxpz" import Fzxc from "./rcgl/byfz/fzxc" import Bypc from "./rcgl/byfz/bypc" +import Zmjk from "./rcgl/zmjk" import Wxyh from "./rcgl/wxyh" import Szzb from "./rcgl/szzb" import Stlljc from "./rcgl/stlljc" @@ -193,6 +194,7 @@ const AppRouters: React.FC = () => { { path: 'sg/xcxj/xjxpz', element: }, { path: 'sg/byfz/byxc', element: }, { path: 'sg/byfz/bypc', element: }, + { path: 'sg/zmjk', element: }, { path: 'sg/wxyh', element: }, { path: 'sg/aqgl/aqjcgl', element: }, { path: 'sg/aqgl/fxgkqd', element: }, diff --git a/src/views/rcgl/zmjk/ColorPolygon.tsx b/src/views/rcgl/zmjk/ColorPolygon.tsx new file mode 100644 index 000000000..815144ca4 --- /dev/null +++ b/src/views/rcgl/zmjk/ColorPolygon.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { Shape } from 'react-konva'; + +const ColorPolygon: React.FC<{ + pts: ({ x?: number, y?: number } | [number, number])[] + fill: string; + desc?: string; + opacity?: number; +}> = ({ pts, fill, opacity }) => { + return ( + { + context.beginPath(); + pts.forEach((p, i) => { + let x:any, y:any; + if (Array.isArray(p)) { + x = p[0]; + y = p[1]; + } else { + x = p.x; + y = p.y; + } + if (i === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } + }) + context.closePath(); + context.fillStrokeShape(shape) + }} + /> + ) +} + +export default ColorPolygon \ No newline at end of file diff --git a/src/views/rcgl/zmjk/Sider.tsx b/src/views/rcgl/zmjk/Sider.tsx new file mode 100644 index 000000000..c443cbd9a --- /dev/null +++ b/src/views/rcgl/zmjk/Sider.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { Layer, Rect } from 'react-konva'; +import ColorPolygon from './ColorPolygon'; +import { CanvasH, CanvasW, FarBuildingTop, Horizontal, RoofTop, SideRoomSize } from './consts'; +import { ControlPts } from './coordinates'; +import { RoomWindows } from './Window'; + +const Sider: React.FC<{ + pts: ControlPts; + side: 'left' | 'right'; +}> = ({ side, pts }) => { + + return ( + + + + + + + + + + + + + + + + + + + + + ) +} + +export default React.memo(Sider) \ No newline at end of file diff --git a/src/views/rcgl/zmjk/Topper1.tsx b/src/views/rcgl/zmjk/Topper1.tsx new file mode 100644 index 000000000..4a4f291e6 --- /dev/null +++ b/src/views/rcgl/zmjk/Topper1.tsx @@ -0,0 +1,105 @@ +import React, { useMemo } from 'react'; +import { Layer, Rect } from 'react-konva'; +import ColorPolygon from './ColorPolygon'; +import { Window1, WindowSep } from './Window'; +import { TopRoomHeight } from './consts'; +import { ControlPts, mirror } from './coordinates'; + + +const Topper1: React.FC<{ + pts: ControlPts; + type: number; +}> = ({ pts, type }) => { + + const windows = useMemo<{ + w: number[][]; + h: number[][]; + }>(() => { + const x1 = pts.TopRectLB.x; + const y0 = pts.TopRectLB.y - TopRoomHeight + const y1 = pts.TopRectLB.y - TopRoomHeight * 0.75; + const x2 = pts.TopRectRB.x; + const y2 = pts.TopRectLB.y - TopRoomHeight * 0.25; + if (type === 1) { + const u = (x2 - x1) / 10; + return { + w: [ + [x1 + u, y1, x1 + 3 * u, y2], + [x1 + 4 * u, y1, x1 + 6 * u, y2], + [x1 + 7 * u, y1, x1 + 9 * u, y2], + ], + h: [] + } + } else if (type === 2) { + const u = (x2 - x1 - 8) / 14; + return { + w: [ + [x1 + u, y1, x1 + 3 * u, y2], + [x1 + 4 * u, y1, x1 + 6 * u, y2], + [x1 + 8 * u + 8, y1, x1 + 10 * u + 8, y2], + [x1 + 11 * u + 8, y1, x1 + 13 * u + 8, y2], + ], + h: [ + [x1 + 7 * u, y0, x1 + 7 * u + 8, pts.TopRectLB.y], + ], + } + } else if (type === 3) { + const xc = (x2 - x1) * 0.5; + const u = (xc - 8) / 8; + return { + w: [ + [x1 + u, y1, x1 + 3 * u, y2], + [x1 + 5 * u + 8, y1, x1 + 7 * u + 8, y2], + [x1 + u + xc, y1, x1 + 3 * u + xc, y2], + [x1 + 5 * u + 8 + xc, y1, x1 + 7 * u + 8 + xc, y2], + ], + h: [ + [x1 + 4 * u, y0, x1 + 4 * u + 8, pts.TopRectLB.y], + [x1 + 4 * u + xc, y0, x1 + 4 * u + 8 + xc, pts.TopRectLB.y], + ] + } + } else if (type > 3) { + const xc1 = (x2 - x1) / 3; + const xc2 = xc1 * 2; + const u = (xc1 - 8) / 8; + return { + w: [ + [x1 + u, y1, x1 + 3 * u, y2], + [x1 + 5 * u + 8, y1, x1 + 7 * u + 8, y2], + [x1 + u + xc1, y1, x1 + 3 * u + xc1, y2], + [x1 + 5 * u + 8 + xc1, y1, x1 + 7 * u + 8 + xc1, y2], + [x1 + u + xc2, y1, x1 + 3 * u + xc2, y2], + [x1 + 5 * u + 8 + xc2, y1, x1 + 7 * u + 8 + xc2, y2], + ], + h: [ + [x1 + 4 * u, y0, x1 + 4 * u + 8, pts.TopRectLB.y], + [x1 + 4 * u + xc1, y0, x1 + 4 * u + 8 + xc1, pts.TopRectLB.y], + [x1 + 4 * u + xc2, y0, x1 + 4 * u + 8 + xc2, pts.TopRectLB.y], + ] + } + } + return { + w: [], h: [] + } + }, []) + + return ( + + + + { + pts.SepsLTLBRBRT.map((s, index) => ( + + )) + } + { + windows.w.map((o, index) => ) + } + { + windows.h.map((o, index) => ) + } + + ) +} + +export default React.memo(Topper1); \ No newline at end of file diff --git a/src/views/rcgl/zmjk/Topper2.tsx b/src/views/rcgl/zmjk/Topper2.tsx new file mode 100644 index 000000000..cb393f13c --- /dev/null +++ b/src/views/rcgl/zmjk/Topper2.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Layer, Rect } from 'react-konva'; +import ColorPolygon from './ColorPolygon'; +import { CanvasH, CanvasW, ViewCenter, WaterTop } from './consts'; +import { ControlPts, interpolate, intersection, mirror } from './coordinates'; + + +const Topper2: React.FC<{ + pts: ControlPts; + waterRatio: number; +}> = ({ pts, waterRatio }) => { + + const waterP1 = interpolate(pts.B1, { x: pts.L1.x, y: WaterTop }, waterRatio); + const waterP2 = intersection(waterP1, ViewCenter, { x: 0, y: undefined }); + + return ( + + { + pts.SepsFront.map((s, index) => ( + + )) + } + + + + ) +} + +export default React.memo(Topper2); \ No newline at end of file diff --git a/src/views/rcgl/zmjk/Window.tsx b/src/views/rcgl/zmjk/Window.tsx new file mode 100644 index 000000000..550ed8c7e --- /dev/null +++ b/src/views/rcgl/zmjk/Window.tsx @@ -0,0 +1,104 @@ +import React from 'react' +import { Line, Rect } from 'react-konva'; +import ColorPolygon from './ColorPolygon'; + +const S1 = 4; + +export const Window1: React.FC<{ + rect: number[]; +}> = ({ rect }) => { + const [x1, y1, x2, y2] = rect; + + const w1 = (x2 - x1 - S1) / 2; + const w2 = (x2 - x1 - S1 * 2) / 3; + + return ( + <> + + + + + + + + ) +} + +export const WindowSep: React.FC<{ + rect: number[]; +}> = ({ rect }) => { + const [x1, y1, x2, y2] = rect; + + return ( + + ) +} + +export const RoomWindows: React.FC<{ + rect: number[]; +}> = ({ rect }) => { + const [x1, y1, x2, y2] = rect; + + const u = (x2 - x1) / 10; + const v = (y2 - y1) / 6; + + const c1 = x1 - 10 + 1; + const c2 = x1 - 10 + 7 * u; + const dc = (c2 - c1) / 10; + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(i => ( + + )) + } + + + + ) +} \ No newline at end of file diff --git a/src/views/rcgl/zmjk/ZmColumn.tsx b/src/views/rcgl/zmjk/ZmColumn.tsx new file mode 100644 index 000000000..81aa78024 --- /dev/null +++ b/src/views/rcgl/zmjk/ZmColumn.tsx @@ -0,0 +1,119 @@ +import React from 'react' +import { Circle, Layer, Line, Rect } from 'react-konva'; +import ColorPolygon from './ColorPolygon'; +import { ViewCenter, WaterTop } from './consts'; +import { ControlPts, interpolate, intersection } from './coordinates'; +import ZmDec from './ZmDec'; +import { useLinearAnim } from './useLinearAnim'; + +const ZmColumn: React.FC<{ + kdMax: number; + gtophgt?: number; + idx: number; + pts: ControlPts; + waterRatio: number; + selected: boolean; +}> = ({ kdMax, gtophgt, idx, pts, waterRatio, selected }) => { + + const renKd = gtophgt; //useLinearAnim(gtophgt); + + const a = pts.ZmArea[idx]; + if (!a || typeof renKd !== 'number') { + return null; + } + + const hh1 = (a.lb1.y - a.lt1.y) * 0.5; + const hh2 = (a.lb2.y - a.lt2.y) * 0.5; + + const ratio = renKd / kdMax; + const e1 = hh1 * ratio * 0.8; + const e2 = hh2 * ratio * 0.8; + + const lineLineLeft = { x: (a.lt1.x + a.lt2.x) * 0.5, y: (a.lt1.y + a.lt2.y) * 0.5 }; + const lineLineRight = { x: (a.rt1.x + a.rt2.x) * 0.5, y: (a.rt1.y + a.rt2.y) * 0.5 }; + const line1 = interpolate(lineLineLeft, lineLineRight, 0.25); + const line2 = interpolate(lineLineLeft, lineLineRight, 0.75); + + const ty1 = a.lt1.y + hh1 - e1; + const ty2 = a.lt2.y + hh2 - e2; + const joiny = (ty1 + ty2) * 0.5; + + const waterP1 = interpolate(a.lb0, { x: a.lb0.x, y: WaterTop }, waterRatio); + const waterP1R = { x: a.rb0.x, y: waterP1.y }; + const waterP2 = intersection(ViewCenter, waterP1, { x: a.lb1.x, y: undefined }); + const waterP2R = { x: a.rb1.x, y: waterP2.y }; + const waterP3 = intersection(ViewCenter, waterP1, { x: a.lb3.x, y: undefined }); + waterP3.y -= 26; + const waterP3R = { x: a.rb3.x, y: waterP3.y }; + if (waterP3.x < waterP2.x) { + waterP3.x = waterP2.x; + } + if (waterP3R.x > waterP2R.x) { + waterP3R.x = waterP2R.x + } + + // + const b1 = { x: a.lb0.x, y: a.lb0.y - 32 }; + const b6 = { x: a.rb0.x, y: a.rb0.y - 32 }; + const b2 = interpolate(b1, b6, 0.4); + const b5 = interpolate(b1, b6, 0.6); + const b3 = { x: b2.x, y: b2.y + 12 }; + const b4 = { x: b5.x, y: b5.y + 12 }; + + const b1_1 = intersection(b1, ViewCenter, { x: a.lb1.x, y: undefined }); + const b1_6 = intersection(b6, ViewCenter, { x: a.rb1.x, y: undefined }); + const b1_2 = intersection(b2, ViewCenter, { x: undefined, y: b1_1.y }); + const b1_5 = intersection(b5, ViewCenter, { x: undefined, y: b1_6.y }); + const b1_3 = intersection(b3, ViewCenter, { x: b1_2.x, y: undefined }); + const b1_4 = intersection(b4, ViewCenter, { x: b1_5.x, y: undefined }); + + return ( + <> + { + waterP3 && waterP3R ? ( + + ) : null + } + + + + + + + + + + + + + + + + + + + + { + waterP1.y < b1.y ? ( + + ) : null + } + + + ) +} + +export default React.memo(ZmColumn) \ No newline at end of file diff --git a/src/views/rcgl/zmjk/ZmColumns.tsx b/src/views/rcgl/zmjk/ZmColumns.tsx new file mode 100644 index 000000000..b3b4f8ef7 --- /dev/null +++ b/src/views/rcgl/zmjk/ZmColumns.tsx @@ -0,0 +1,35 @@ +import React, { useMemo } from 'react' +import { Layer } from 'react-konva'; +import { ControlPts } from './coordinates'; +import ZmColumn from './ZmColumn'; +// import { StationItem, GateRuntime } from '../../../models/_/defs'; +import { apertureMeter } from '../../../utils/utils'; + +const ZmColumns: React.FC<{ + zmobj: any; + runtime: any; + pts: ControlPts; + selectedId?: string; + waterRatio: number; +}> = ({ zmobj, runtime, pts, waterRatio }) => { + const eqpnoList = useMemo(() => new Array(runtime.length).fill(0).map((o, index) => index), [runtime]); + return ( + + { + eqpnoList.map((o, index) => ( + ) + ) + } + + ) +} + +export default ZmColumns \ No newline at end of file diff --git a/src/views/rcgl/zmjk/ZmDec.tsx b/src/views/rcgl/zmjk/ZmDec.tsx new file mode 100644 index 000000000..30f9f2681 --- /dev/null +++ b/src/views/rcgl/zmjk/ZmDec.tsx @@ -0,0 +1,60 @@ +import React from 'react' +import { Line, Rect } from 'react-konva'; +import { Horizontal, ViewCenter } from './consts'; +import { XY } from './coordinates' + +const INTERVALS = [0, 1, 2]; + +const ZmDec: React.FC<{ + tl: XY; + rb: XY; +}> = ({ tl, rb }) => { + + const ux = (rb.x - tl.x) / 12; + const uy = (rb.y - tl.y) / 12; + + const ox = tl.x + 3 * ux; + const oy = tl.y + 3 * uy; + + return ( + <> + { + INTERVALS.map(y => ( + + { + INTERVALS.map(x => { + const cx = ox + x * 3 * ux; + const cy = oy + y * 3 * uy; + const l = cx - ux; + const r = cx + ux; + const t = cy - uy; + const b = cy + uy; + return ( + + + { + cx < ViewCenter.x ? ( + + ) : ( + + ) + } + { + cy < Horizontal ? ( + + ) : ( + + ) + } + + ) + }) + } + + )) + } + + ) +} + +export default ZmDec \ No newline at end of file diff --git a/src/views/rcgl/zmjk/consts.ts b/src/views/rcgl/zmjk/consts.ts new file mode 100644 index 000000000..1a9147e3d --- /dev/null +++ b/src/views/rcgl/zmjk/consts.ts @@ -0,0 +1,16 @@ +export const CanvasW = 1080; +export const CanvasH = 640; + +export const Horizontal = CanvasH * 1.4 / 3; +export const ViewCenter = { x: CanvasW * 0.5, y: Horizontal }; + +export const GroundBase = CanvasH * 1.8 / 3; +export const BottomBase = CanvasH * 2.5 / 3; +export const RoofTop = CanvasH * 0.8 / 3; +export const RoofTopFar = RoofTop + 32; +export const FarBuildingTop = RoofTop - 16; + +export const SideRoomSize = CanvasH * 0.7 / 3; +export const TopRoomHeight = SideRoomSize - 24; +export const PillarRatio = 0.2; +export const WaterTop = GroundBase; diff --git a/src/views/rcgl/zmjk/coordinates.ts b/src/views/rcgl/zmjk/coordinates.ts new file mode 100644 index 000000000..93bff7e08 --- /dev/null +++ b/src/views/rcgl/zmjk/coordinates.ts @@ -0,0 +1,180 @@ +import { BottomBase, CanvasW, GroundBase, PillarRatio, RoofTop, RoofTopFar, SideRoomSize, ViewCenter } from "./consts"; + +export type XY = { + x: number; + y: number; +} + +export type XYWH = { + x: number; + y: number; + w: number; + h: number; +} + +export function intersection(pt1: XY, pt2: XY, result: { x?: number, y?: number }): XY { + const invalid: XY = { x: result.x || 0, y: result.y || 0 }; + if (typeof result.x === typeof result.y) { + return invalid; + } + + const x1 = pt1.x, y1 = pt1.y, x2 = pt2.x, y2 = pt2.y; + + if (typeof result.x === 'number') { + const x3 = result.x; + + if (x2 === x1) { + return invalid; + } + + result.y = (y2 - y1) * (x3 - x1) / (x2 - x1) + y1; + } else { + const y3 = result.y!; + + if (y2 === y1) { + return invalid; + } + + result.x = (x2 - x1) * (y3 - y1) / (y2 - y1) + x1; + } + + return result as any; +} + +export function interpolate(pt1: XY, pt2: XY, ratio: number): XY { + return { + x: pt1.x + (pt2.x - pt1.x) * ratio, + y: pt1.y + (pt2.y - pt1.y) * ratio, + } +} + +export function mirror(pt: XY): XY { + return { + x: CanvasW - pt.x, + y: pt.y, + } +} + +export type ControlPts = { + C1: XY; + L1: XY; + B1: XY; + C2: XY; + L2: XY; + B2: XY; + A1: XY; + A2: XY; + B3: XY; + RoomLT: XY; + RoomRT: XY; + RoomRB: XY; + RoomLB: XY; + RoomRTFar: XY; + RoomRBFar: XY; + RoomLBFar: XY; + TopRectLB: XY; + TopRectRB: XY; + SepsLTLBRBRT: XY[][]; + SepsFront: XYWH[]; + ZmArea: { + lt0: XY; lt1: XY; lt2: XY; lt3: XY; + lb0: XY; lb1: XY; lb2: XY; lb3: XY; + rt0: XY; rt1: XY; rt2: XY; rt3: XY; + rb0: XY; rb1: XY; rb2: XY; rb3: XY; + + }[]; +} + + + + +export function contextCoordinates(unitWidth: number, hole: number): ControlPts { + const C1 = { x: unitWidth * 0.6, y: GroundBase }; + const L1 = { x: unitWidth, y: GroundBase }; + const B1 = { x: unitWidth, y: BottomBase } + + const C2 = intersection(ViewCenter, C1, { x: 0, y: undefined }); + const L2 = intersection(ViewCenter, L1, { x: 0, y: undefined }); + const B2 = intersection(ViewCenter, B1, { x: 0, y: undefined }); + + const A1 = { x: L1.x, y: RoofTop }; + const A2 = intersection(ViewCenter, A1, { x: undefined, y: RoofTopFar }); + + const B3 = intersection(ViewCenter, B2, { x: A2.x, y: undefined }) + + const RoomLT = { x: (C1.x + L1.x) * 0.5 - SideRoomSize * 0.5, y: RoofTop - SideRoomSize } + const RoomRT = { x: (C1.x + L1.x) * 0.5 + SideRoomSize * 0.5, y: RoofTop - SideRoomSize } + const RoomRB = { x: (C1.x + L1.x) * 0.5 + SideRoomSize * 0.5, y: RoofTop } + const RoomLB = { x: (C1.x + L1.x) * 0.5 - SideRoomSize * 0.5, y: RoofTop } + const RoomRBFar = intersection(ViewCenter, RoomRB, { x: undefined, y: RoofTopFar }); + const RoomRTFar = intersection(ViewCenter, RoomRT, { x: RoomRBFar.x, y: undefined }); + const RoomLBFar = { x: RoomRBFar.x! - SideRoomSize, y: RoomRBFar.y }; + + + const TopRectLB = interpolate(RoomRB, RoomRBFar, 0.05); + const TopRectRB = mirror(TopRectLB); + + const TopHoleLT = intersection(A1, ViewCenter, { x: undefined, y: TopRectLB.y }); + const TopHoleRT = mirror(TopHoleLT); + const HolesWidth = TopHoleRT.x - TopHoleLT.x; + const PillarWidth = (HolesWidth / hole) * PillarRatio; + const HoleWidth = (HolesWidth - PillarWidth * (hole - 1)) / hole; + + const SepsLTLBRBRT: XY[][] = []; + const SepsFront: XYWH[] = []; + const ZmArea = []; + for (let i = 0; i < hole; i++) { + const TopBase = TopHoleLT.y; + // 隔断 + if (i > 0) { + const frontRight = TopHoleLT.x + (PillarWidth + HoleWidth) * i; + const frontLeft = frontRight - PillarWidth; + SepsFront.push({ x: frontLeft, y: TopBase, w: PillarWidth, h: BottomBase - TopBase }); + if (i < hole / 2) { + // 右侧面 + const p1 = { x: frontRight, y: TopBase }; + const p2 = { x: frontRight, y: BottomBase }; + const p4 = intersection(p1, ViewCenter, { x: undefined, y: RoofTopFar }); + const p3 = intersection(p2, ViewCenter, { x: p4.x, y: undefined }); + SepsLTLBRBRT.push([p1, p2, p3, p4]); + } else if (i > hole / 2) { + // 左侧面 + const p1 = { x: frontLeft, y: TopBase }; + const p2 = { x: frontLeft, y: BottomBase }; + const p4 = intersection(p1, ViewCenter, { x: undefined, y: RoofTopFar }); + const p3 = intersection(p2, ViewCenter, { x: p4.x, y: undefined }); + SepsLTLBRBRT.push([p1, p2, p3, p4]); + } + } + + // 闸门面 + { + const ZmTopBaseFront = TopBase + 6; + const ZmTopBaseBack = ZmTopBaseFront + 10; + const lt0 = { x: TopHoleLT.x + (PillarWidth + HoleWidth) * i, y: TopBase }; + const lb0 = { x: lt0.x, y: BottomBase }; + const rb0 = { x: lt0.x + HoleWidth, y: BottomBase }; + const lt1 = intersection(ViewCenter, lt0, { x: undefined, y: ZmTopBaseFront }); + const lt2 = intersection(ViewCenter, lt0, { x: undefined, y: ZmTopBaseBack }); + const lt3 = intersection(ViewCenter, lt0, { x: undefined, y: RoofTopFar }); + const lb1 = intersection(ViewCenter, lb0, { x: lt1.x, y: undefined }); + const lb2 = intersection(ViewCenter, lb0, { x: lt2.x, y: undefined }); + const lb3 = intersection(ViewCenter, lb0, { x: lt3.x, y: undefined }); + const rb1 = intersection(ViewCenter, rb0, { x: undefined, y: lb1.y }); + const rb2 = intersection(ViewCenter, rb0, { x: undefined, y: lb2.y }); + const rb3 = intersection(ViewCenter, rb0, { x: undefined, y: lb3.y }); + const rt0 = { x: rb0.x, y: lt0.y }; + const rt1 = { x: rb1.x, y: lt1.y }; + const rt2 = { x: rb2.x, y: lt2.y }; + const rt3 = { x: rb3.x, y: lt3.y }; + ZmArea.push({ lt0, lb0, rb0, rt0, lt1, rt1, lb1, rb1, lt2, lb2, rt2, rb2, lt3, lb3, rt3, rb3 }); + } + } + + return { + C1, L1, B1, C2, L2, B2, A1, A2, B3, + RoomLT, RoomRT, RoomRB, RoomLB, RoomRTFar, RoomRBFar, RoomLBFar, + TopRectLB, TopRectRB, + SepsLTLBRBRT, SepsFront, ZmArea, + } +} \ No newline at end of file diff --git a/src/views/rcgl/zmjk/index.js b/src/views/rcgl/zmjk/index.js new file mode 100644 index 000000000..4d6415b9c --- /dev/null +++ b/src/views/rcgl/zmjk/index.js @@ -0,0 +1,350 @@ +import React, { useEffect, useMemo, useState } from 'react' +import { Stage } from 'react-konva'; +import Sider from './Sider'; +import Topper1 from './Topper1'; +import Topper2 from './Topper2'; +import ZmColumns from './ZmColumns'; +import { contextCoordinates } from './coordinates'; +import { renAperture } from '../../../utils/utils'; +import HFivePlayer from '../../../components/video1Plary' +import './index.less'; +import { httpget, httpget2, httppost2 } from '../../../utils/request'; +import apiurl from '../../../service/apiurl'; +const CanvasW = 1080 +const CanvasH = 640 +const waterRatio = 0 +const zmobj ={ + "hpCode": "HP0074208040002120", + "stcd": "4265630075", + "ctrlType": "PLC", + "ctrlProtocol": "PLC", + "uprzStcd": null, + "dwrzStcd": null, + "flowStcd": null, + "gaType": "waga", + "ctrlPass": null, + "maxHgt": 1.9, + "minHgt": 0, + "name": "五岭包节制闸", + "ghtX": null, + "ghtY": null, + "irrCode": "D00000020", + "irrName": "三干渠", + "engCode": "ENG100076", + "engName": "三干渠管理处", + "orgCode": "A07", + "gaorNum": 3, + "wagaType": "节制闸", + "plcType": null, + "bim": 0, + "vip": 0, + "miu": null, + "lgtd": 112.242945, + "lttd": 30.848166, + "runtime": [ + null, + { + "stcd": "4265630075", + "gateNumber": 1, + "realAperture": 376, + "setAperture": 0, + "sensorLever": null, + "altitudeLever": null, + "remoteSignal": 0, + "powerSignal": 0, + "openingSignal": 0, + "closeingSignal": 0, + "errorSignal": 0, + "openedSignal": 0, + "closedSignal": 0, + "tm": "2024-09-25 20:03:26", + "_online": true + }, + { + "stcd": "4265630075", + "gateNumber": 2, + "realAperture": 388, + "setAperture": 0, + "sensorLever": null, + "altitudeLever": null, + "remoteSignal": 0, + "powerSignal": 0, + "openingSignal": 0, + "closeingSignal": 0, + "errorSignal": 0, + "openedSignal": 0, + "closedSignal": 0, + "tm": "2024-09-25 20:03:26", + "_online": true + }, + { + "stcd": "4265630075", + "gateNumber": 3, + "realAperture": 394, + "setAperture": 0, + "sensorLever": null, + "altitudeLever": null, + "remoteSignal": 0, + "powerSignal": null, + "openingSignal": 0, + "closeingSignal": 0, + "errorSignal": 0, + "openedSignal": 0, + "closedSignal": 0, + "tm": "2024-09-25 20:03:26", + "_online": true + } + ], + "real": { + "stcd": "4265630075", + "stationName": "五岭包节制闸", + "z1": null, + "zz1": null, + "z1tm": null, + "z2": null, + "zz2": null, + "z2tm": null, + "hq": null, + "hqtm": null, + "demtl": null + }, + "cctvs": [], + "_idx": 88, + "_fav": false, + "_sort": 10086 +} + +const runtime = [ + null, + { + "stcd": "4265630075", + "gateNumber": 1, + "realAperture": 976, + "setAperture": 0, + "sensorLever": null, + "altitudeLever": null, + "remoteSignal": 0, + "powerSignal": 0, + "openingSignal": 0, + "closeingSignal": 0, + "errorSignal": 0, + "openedSignal": 0, + "closedSignal": 0, + "tm": "2024-09-25 20:03:31" + }, + { + "stcd": "4265630075", + "gateNumber": 2, + "realAperture": 388, + "setAperture": 0, + "sensorLever": null, + "altitudeLever": null, + "remoteSignal": 0, + "powerSignal": 0, + "openingSignal": 0, + "closeingSignal": 0, + "errorSignal": 0, + "openedSignal": 0, + "closedSignal": 0, + "tm": "2024-09-25 20:03:31" + }, + { + "stcd": "4265630075", + "gateNumber": 3, + "realAperture": 394, + "setAperture": 0, + "sensorLever": null, + "altitudeLever": null, + "remoteSignal": 0, + "powerSignal": null, + "openingSignal": 0, + "closeingSignal": 0, + "errorSignal": 0, + "openedSignal": 0, + "closedSignal": 0, + "tm": "2024-09-25 20:03:31" + } +] + +const myType = { + // 闸前水位站 2闸后水位站 3流量站 + '1':'闸前水位/水深(m)', + '2':'闸后水位/水深(m)', + '3':'流量 (m³/s)', +} + +const Page = () => { + const [itemIndex,setItemIndex] = useState(null) + const [data,setData] = useState({}) + const [list, setList ] = useState([]) + const [damList, setDamList ] = useState([]) + const [videoList, setVideoList ] = useState([]) + const [videoArr, setvideoArr] = useState({}) + + + const hole = 3;//zmobj.gaorNum; + const xunit = CanvasW / (2 + hole); + const pts = contextCoordinates(xunit, hole); + const eqpnoList = useMemo(() => damList ? new Array(damList.length).fill(0).map((o, index) => index) : [], [damList]); + + useEffect(()=>{ + getList() + },[]) + + const getList = async()=>{ + const {code, data} = await httppost2(apiurl.zmjk.getList) + if(code!==200){ + return + } + const obj = data[0]||{} + getInformation(obj.gateCode) + getDamData(obj.stcd) + getVideo(obj.gateCode) + setData(obj) + } + + const getInformation = async(gateCode)=>{ + const {code, data} = await httpget2(apiurl.zmjk.getInformation,{gateCode}) + if(code!==200){ + return + } + setList(data) + } + + const getDamData = async(stcd)=>{ + const {code, data} = await httpget2(apiurl.zmjk.getDamData,{stcd}) + if(code!==200){ + return + } + const list = [] + data.map((item)=>{ + list.push({ + ...item + // ,realAperture:item.realAperture*1000 + }) + }) + setDamList(list) + } + + const getVideo = async(valveCode)=>{ + const {code, data} = await httppost2(apiurl.zmjk.getVideo,{valveCode}) + if(code!==200){ + return + } + setVideoList(data) + } + + const getVideoSrc = async (current) => { + const res = await httpget2(`${apiurl.gsxl.zfzl.videosrc}${'32023a7f27d8448fa10511f24e96acff'}`) + if (res.code == 200 && res.data?.length !== 0) { + setvideoArr({src:res.data}) + }else{ + setvideoArr({}) + } + } + + + return ( + <> +
+
+ {/* + */} +
+ + + + + + + + +
+
+ { + eqpnoList.map(o => ( +
{}} + className='o' style={{ flexGrow: 1, width: 100, display: 'flex', justifyContent: 'center', cursor: 'pointer' }}> +
#{o+1}
+
+ )) + } +
+
+
+
+ { + eqpnoList.map(o => ( +
+
{}} + style={{ width: 80, height: 32, border: '1px solid #444', backgroundColor: '#fff', borderRadius: 4, color: '#888', display: 'flex', justifyContent: 'center', alignItems: 'center', fontSize: 18, cursor: 'pointer' }} + > + {renAperture(damList[o]?.realAperture)} +
+
+ )) + } +
+
+
+ +
+
视频监控
+
+
+ { + videoList.map((item,index)=>( +
{setItemIndex(index);getVideoSrc(item.indexCode)}}> + {item.name} +
+ )) + } +
+
+ { + videoArr?.src && +
{ + // if (controlerParams.type == 1) { + // setVideoOpen(true) + // setIsShow(!isShow) + // } + }} + > + + {/*
注:单击视频显示/隐藏云台
*/} +
+ } +
+
+
+
+
监测数据
+
+ { + list?.map((item)=>{ + return ( +
+
{myType[item.type]}
+
112.079 / 1.279
+
07-10 12:09:00
+
+ ) + }) + } +
+
+
》》查看更多信息
+
+
+
+
+ + ); +} + +export default Page; diff --git a/src/views/rcgl/zmjk/index.less b/src/views/rcgl/zmjk/index.less new file mode 100644 index 000000000..b97ba316a --- /dev/null +++ b/src/views/rcgl/zmjk/index.less @@ -0,0 +1,92 @@ +.sg_zmjk{ + .ant-card-body{ + display: flex; + height: 100%; + .sg_zmjk_left{ + top: 30px; + position: relative; + width: 1080px; + height: 640px; + transform: scale(0.9,1); + + } + .sg_zmjk_right{ + flex: 1; + // width: 40%; + height: 100%; + display: flex; + flex-direction: column; + + .sg_zmjk_right_video{ + height: 400px; + margin: 30px 20px 30px -30px; + border-radius: 5px; + border: 1px solid #bbb; + display: flex; + flex-direction: column; + .sg_zmjk_right_video_title{ + height: 35px; + line-height: 35px; + padding-left: 10px; + border-bottom: 1px solid #bbb; + } + .sg_zmjk_right_video_content{ + flex: 1; + display: flex; + width: 100%; + // height: 100%; + .sg_zmjk_right_video_content_left{ + overflow-y: auto; + overflow-x: hidden; + padding: 10px; + width: 130px; + height: 363px; + border-right: 1px solid #bbb; + cursor: pointer; + .sg_zmjk_right_video_content_left_item{ + width: 110px; + // height: 30px; + margin-bottom: 5px; + // line-height: 30px; + text-align: center; + border: 1px solid #bbb; + } + .itemChecked{ + background-color: #1890FF; + color: #ffffff; + } + } + .sg_zmjk_right_video_content_right{ + flex: 1; + height: 100%; + padding: 10px; + } + } + } + .sg_zmjk_right_information{ + height: 180px; + margin: -10px 20px 30px -30px; + border-radius: 5px; + border: 1px solid #bbb; + .sg_zmjk_right_information_title{ + height: 35px; + line-height: 35px; + padding-left: 10px; + border-bottom: 1px solid #bbb; + } + .sg_zmjk_right_information_content{ + display: flex; + padding: 2px 10px; + div{ + width: 33%; + margin-bottom: 10px; + } + } + } + .sg_zmjk_right_more{ + margin: -10px 20px 30px -30px; + cursor: pointer; + } + } + } +} \ No newline at end of file diff --git a/src/views/rcgl/zmjk/useLinearAnim.ts b/src/views/rcgl/zmjk/useLinearAnim.ts new file mode 100644 index 000000000..7fb7040a9 --- /dev/null +++ b/src/views/rcgl/zmjk/useLinearAnim.ts @@ -0,0 +1,54 @@ +import { useEffect, useReducer, useRef, useState } from "react"; + + +const DURATION = 900; +const CNT = 5; + +const INTERVAL = DURATION / CNT; + +export function useLinearAnim(val: number | undefined) { + + const ren = useRef(val); + + const [_, refresh] = useReducer(s => s + 1, 0); + + const alive = useRef(true); + + useEffect(() => { + return () => { + alive.current = false; + } + }, []) + + useEffect(() => { + if (typeof val !== 'number') { + return; + } + + if (ren.current == undefined) { + ren.current = val; + } + const len = val - ren.current; + + let handle: any = null; + if (len) { + const dv = len / CNT; + let cnt = CNT; + handle = setInterval(() => { + if (cnt > 0 && alive.current) { + cnt--; + ren.current! -= dv; + refresh(); + } + }, INTERVAL) + } + + return () => { + if (handle) { + clearInterval(handle); + } + } + }, [val]); + + return ren.current ?? val; +} \ No newline at end of file