This commit is contained in:
严争鸣 2025-01-23 09:05:13 +08:00
parent ab89bed57d
commit dcac2021f5
35 changed files with 8480 additions and 451 deletions

326
package-lock.json generated
View File

@ -22,6 +22,7 @@
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
"lodash": "^4.17.21",
"maplibre-gl": "^5.0.1",
"moment-timezone": "^0.5.46",
"normalize.css": "^8.0.1",
"pinia": "^2.0.28",
@ -4394,6 +4395,113 @@
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@mapbox/geojson-rewind": {
"version": "0.5.2",
"resolved": "https://registry.npmmirror.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
"integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
"license": "ISC",
"dependencies": {
"get-stream": "^6.0.1",
"minimist": "^1.2.6"
},
"bin": {
"geojson-rewind": "geojson-rewind"
}
},
"node_modules/@mapbox/geojson-rewind/node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==",
"license": "ISC"
},
"node_modules/@mapbox/tiny-sdf": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz",
"integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/unitbezier": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/vector-tile": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/point-geometry": "~0.1.0"
}
},
"node_modules/@mapbox/whoots-js": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
"license": "ISC",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "23.1.0",
"resolved": "https://registry.npmmirror.com/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.1.0.tgz",
"integrity": "sha512-R6/ihEuC5KRexmKIYkWqUv84Gm+/QwsOUgHyt1yy2XqCdGdLvlBWVWIIeTZWN4NGdwmY6xDzdSGU2R9oBLNg2w==",
"license": "ISC",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
"@mapbox/unitbezier": "^0.0.1",
"json-stringify-pretty-compact": "^4.0.0",
"minimist": "^1.2.8",
"quickselect": "^3.0.0",
"rw": "^1.3.3",
"tinyqueue": "^3.0.0"
},
"bin": {
"gl-style-format": "dist/gl-style-format.mjs",
"gl-style-migrate": "dist/gl-style-migrate.mjs",
"gl-style-validate": "dist/gl-style-validate.mjs"
}
},
"node_modules/@maplibre/maplibre-gl-style-spec/node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC"
},
"node_modules/@maplibre/maplibre-gl-style-spec/node_modules/tinyqueue": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/tinyqueue/-/tinyqueue-3.0.0.tgz",
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
"license": "ISC"
},
"node_modules/@mars3d/heatmap.js": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/@mars3d/heatmap.js/-/heatmap.js-2.0.7.tgz",
@ -7410,6 +7518,15 @@
"integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==",
"license": "MIT"
},
"node_modules/@types/geojson-vt": {
"version": "3.2.5",
"resolved": "https://registry.npmmirror.com/@types/geojson-vt/-/geojson-vt-3.2.5.tgz",
"integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -7439,6 +7556,23 @@
"@types/lodash": "*"
}
},
"node_modules/@types/mapbox__point-geometry": {
"version": "0.1.4",
"resolved": "https://registry.npmmirror.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz",
"integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==",
"license": "MIT"
},
"node_modules/@types/mapbox__vector-tile": {
"version": "1.3.4",
"resolved": "https://registry.npmmirror.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz",
"integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*",
"@types/mapbox__point-geometry": "*",
"@types/pbf": "*"
}
},
"node_modules/@types/node": {
"version": "18.19.68",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.68.tgz",
@ -7448,6 +7582,12 @@
"undici-types": "~5.26.4"
}
},
"node_modules/@types/pbf": {
"version": "3.0.5",
"resolved": "https://registry.npmmirror.com/@types/pbf/-/pbf-3.0.5.tgz",
"integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==",
"license": "MIT"
},
"node_modules/@types/semver": {
"version": "7.5.8",
"resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.8.tgz",
@ -7461,6 +7601,15 @@
"integrity": "sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==",
"license": "MIT"
},
"node_modules/@types/supercluster": {
"version": "7.1.3",
"resolved": "https://registry.npmmirror.com/@types/supercluster/-/supercluster-7.1.3.tgz",
"integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@ -10660,6 +10809,12 @@
"integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==",
"license": "MIT"
},
"node_modules/geojson-vt": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/geojson-vt/-/geojson-vt-4.0.2.tgz",
"integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==",
"license": "ISC"
},
"node_modules/get-intrinsic": {
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
@ -10693,6 +10848,18 @@
"node": ">=4"
}
},
"node_modules/get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-symbol-description": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
@ -10720,6 +10887,12 @@
"js-binary-schema-parser": "^2.0.3"
}
},
"node_modules/gl-matrix": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/gl-matrix/-/gl-matrix-3.4.3.tgz",
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==",
"license": "MIT"
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
@ -10755,6 +10928,44 @@
"node": ">=10.13.0"
}
},
"node_modules/global-prefix": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/global-prefix/-/global-prefix-4.0.0.tgz",
"integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==",
"license": "MIT",
"dependencies": {
"ini": "^4.1.3",
"kind-of": "^6.0.3",
"which": "^4.0.0"
},
"engines": {
"node": ">=16"
}
},
"node_modules/global-prefix/node_modules/isexe": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.1.tgz",
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
"license": "ISC",
"engines": {
"node": ">=16"
}
},
"node_modules/global-prefix/node_modules/which": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/which/-/which-4.0.0.tgz",
"integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
"license": "ISC",
"dependencies": {
"isexe": "^3.1.1"
},
"bin": {
"node-which": "bin/which.js"
},
"engines": {
"node": "^16.13.0 || >=18.0.0"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
@ -11059,6 +11270,15 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ini": {
"version": "4.1.3",
"resolved": "https://registry.npmmirror.com/ini/-/ini-4.1.3.tgz",
"integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
"license": "ISC",
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/internal-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
@ -11614,6 +11834,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/json-stringify-pretty-compact": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
@ -11652,6 +11878,15 @@
"json-buffer": "3.0.1"
}
},
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/kolorist": {
"version": "1.8.0",
"resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz",
@ -11812,6 +12047,65 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/maplibre-gl": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/maplibre-gl/-/maplibre-gl-5.0.1.tgz",
"integrity": "sha512-kNvod1Tq0BcZvn43UAciA3DrzaEGmowqMoI6nh3kUo9rf+7m89mFJI9dELxkWzJ/N9Pgnkp7xF1jzTP08PGpCw==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/point-geometry": "^0.1.0",
"@mapbox/tiny-sdf": "^2.0.6",
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"@maplibre/maplibre-gl-style-spec": "^23.0.0",
"@types/geojson": "^7946.0.15",
"@types/geojson-vt": "3.2.5",
"@types/mapbox__point-geometry": "^0.1.4",
"@types/mapbox__vector-tile": "^1.3.4",
"@types/pbf": "^3.0.5",
"@types/supercluster": "^7.1.3",
"earcut": "^3.0.1",
"geojson-vt": "^4.0.2",
"gl-matrix": "^3.4.3",
"global-prefix": "^4.0.0",
"kdbush": "^4.0.2",
"murmurhash-js": "^1.0.0",
"pbf": "^3.3.0",
"potpack": "^2.0.0",
"quickselect": "^3.0.0",
"supercluster": "^8.0.1",
"tinyqueue": "^3.0.0",
"vt-pbf": "^3.1.3"
},
"engines": {
"node": ">=16.14.0",
"npm": ">=8.1.0"
},
"funding": {
"url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
}
},
"node_modules/maplibre-gl/node_modules/earcut": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/earcut/-/earcut-3.0.1.tgz",
"integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==",
"license": "ISC"
},
"node_modules/maplibre-gl/node_modules/quickselect": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC"
},
"node_modules/maplibre-gl/node_modules/tinyqueue": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/tinyqueue/-/tinyqueue-3.0.0.tgz",
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
"license": "ISC"
},
"node_modules/marchingsquares": {
"version": "1.3.3",
"resolved": "https://registry.npmmirror.com/marchingsquares/-/marchingsquares-1.3.3.tgz",
@ -11989,6 +12283,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
"license": "MIT"
},
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz",
@ -12971,6 +13271,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/potpack": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/potpack/-/potpack-2.0.0.tgz",
"integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==",
"license": "ISC"
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -14323,6 +14629,15 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/supercluster": {
"version": "8.0.1",
"resolved": "https://registry.npmmirror.com/supercluster/-/supercluster-8.0.1.tgz",
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
"license": "ISC",
"dependencies": {
"kdbush": "^4.0.2"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
@ -15250,6 +15565,17 @@
"vue": "^3.0.0"
}
},
"node_modules/vt-pbf": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/vt-pbf/-/vt-pbf-3.1.3.tgz",
"integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
"license": "MIT",
"dependencies": {
"@mapbox/point-geometry": "0.1.0",
"@mapbox/vector-tile": "^1.3.1",
"pbf": "^3.2.1"
}
},
"node_modules/vue": {
"version": "3.5.13",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz",

View File

@ -25,6 +25,7 @@
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
"lodash": "^4.17.21",
"maplibre-gl": "^5.0.1",
"moment-timezone": "^0.5.46",
"normalize.css": "^8.0.1",
"pinia": "^2.0.28",

View File

@ -6,10 +6,23 @@ window['settings'] = {
base: {
icon: './images/icons/base/党政首脑机关.png',
color: '#ea7354',
model: './models/基地.glb',
},
airport: {
icon: './images/icons/base/军用机场.png',
color: '#80b1d3',
model: './models/机场.glb',
},
port: {
icon: './images/icons/base/军用港口.png',
color: '#fcee82',
model: './models/港口.glb',
},
station: {
icon: './images/icons/base/民用机场.png',
color: '#8dd3c7',
model: './models/雷达.glb',
},
airport: { icon: './images/icons/base/军用机场.png', color: '#80b1d3' },
port: { icon: './images/icons/base/军用港口.png', color: '#fcee82' },
station: { icon: './images/icons/base/民用机场.png', color: '#8dd3c7' },
},
mbDict: {
: {

BIN
public/models/基地.glb Normal file

Binary file not shown.

BIN
public/models/机场.glb Normal file

Binary file not shown.

BIN
public/models/港口.glb Normal file

Binary file not shown.

BIN
public/models/雷达.glb Normal file

Binary file not shown.

6738
public/style.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,3 +15,11 @@ export function getHangjing(data = {}) {
data,
})
}
export function updateHjStyle(data = {}) {
return request({
url: `${baseUrl}/hangjing/style`,
method: 'put',
data,
})
}

View File

@ -55,9 +55,10 @@
#71ffff77
);
--color-bg: #1a222966;
/* --color-bg: #1a222966; */
--color-bg: #1a222999;
--gradient-bg-title: linear-gradient(to right, #4fd2dd55, #4b877400);
--gradient-bg-title: linear-gradient(to right, #4fd2dd66, #4b877400);
--tw-from-opacity: 33%;
}

3
src/assets/earth.scss Normal file
View File

@ -0,0 +1,3 @@
.cesium-widget-credits {
display: none !important;
}

View File

@ -1,6 +1,7 @@
@import './base.css';
@import './naiveui.css';
@import './detail.scss';
@import './earth.scss';
#app {
width: 100%;

View File

@ -23,10 +23,10 @@
border-image-slice: 2;
border-image-repeat: stretch; */
position: relative;
margin-bottom: 10px;
/* position: relative;
margin-bottom: 10px; */
/* background: rgba(26, 34, 41, 0.8); */
background: rgba(0, 0, 0, 0.8);
/* background: rgba(0, 0, 0, 0.8); */
}
/* .n-notification-wrapper {
width: 200px;
@ -38,14 +38,14 @@
} */
.n-message-wrapper::before {
border-radius: 5px;
/* border-radius: 5px;
content: '';
position: absolute;
top: -2px;
bottom: -2px;
left: -2px;
right: -2px;
z-index: -1;
z-index: -1; */
/* background: linear-gradient(
45deg,
#f7c30b,
@ -55,7 +55,7 @@
#f49805,
#f7c30b
); */
background: white;
/* background: white;
background-size: 300% 300%;
animation: gradientAnimation 3s ease infinite alternate;
@ -70,7 +70,7 @@
2px 100%,
100% 100%,
100% 0%
);
); */
}
@keyframes gradientAnimation {

View File

@ -11,6 +11,7 @@ export const useEntity = () => {
showEntity,
getHisTraj,
getMBEntityOpt,
iconOrModel,
changeIconOrModel,
}
}
@ -89,7 +90,7 @@ function getMBEntityOpt({
scaleByDistance: new Cesium.NearFarScalar(7000000, 1.0, 18000000, 0.4),
},
billboard: {
show: true,
show: !iconOrModel.value,
image: mubiaoDict.icon,
width: 30,
height: 30,
@ -97,7 +98,7 @@ function getMBEntityOpt({
scaleByDistance: new Cesium.NearFarScalar(7000000, 1.0, 18000000, 0.4),
},
model: {
show: false,
show: iconOrModel.value,
uri: mubiaoDict.model,
scale: 1000,
minimumPixelSize: 50,
@ -124,8 +125,10 @@ function getMBEntityOpt({
}
}
const iconOrModel = ref(false)
function changeIconOrModel() {
;[...mubiaoMap.values()].forEach(entity => {
iconOrModel.value = !iconOrModel.value
;[...mubiaoMap.values(), ...baseMap.values()].forEach(entity => {
entity.model.show = !entity.model.show._value
entity.billboard.show = !entity.billboard.show._value
})

View File

@ -36,6 +36,11 @@ const router = createRouter({
},
],
},
{
path: '/placename',
name: 'PlaceName',
component: () => import('@/views/PlaceName'),
},
],
})

View File

@ -10,7 +10,7 @@ import { useEntity } from '@/hooks/entity'
const treeData = ref([])
const { baseMap } = useEntity()
const { baseMap, iconOrModel } = useEntity()
// const { baseMap } = storeToRefs(entity)
// const allKeys = computed(() => getAllKeys(treeData))
@ -48,10 +48,136 @@ export const useBase = () => {
// console.log(nodes, '====')
nodes.forEach(({ data, dataId }: TBase) => {
const entity = addEntity(data)
// console.log(entity)
addEventSub(dataId, entity, data)
baseMap.set(dataId, entity)
// console.log(entity)
// const boundingSphere = entity.computeBoundingSphere()
// const radius = boundingSphere.radius
// // 计算模型的底部位置
// const newPosition = Cesium.Cartesian3.subtract(
// entity.position.getValue(),
// Cesium.Cartesian3.multiplyByScalar(
// Cesium.Cartesian3.UNIT_Z,
// radius,
// new Cesium.Cartesian3()
// )
// )
// // 设置模型的新的位置
// entity.position = new Cesium.ConstantPositionProperty(newPosition)
})
const modelUrls = Object.values(window.settings.baseMBDict).map(
item => item.model
)
// console.log(viewer.scene.primitives)
viewer.scene.preRender.addEventListener(() => {
viewer.scene.primitives._primitives.forEach(primitive => {
if (primitive instanceof Cesium.Model && primitive?._id?._name) {
const entity = baseMap.get(primitive._id._name)
if (!entity) {
return
}
if (primitive.ready && entity) {
// // 获取模型的 BoundingSphere
// const boundingSphere = primitive.boundingSphere
// const entityPos = entity.position._value
// // 计算底部高度调整
// const center = boundingSphere.center
// // const height = Cesium.Cartesian3.distance(center, entityPos)
// const radius = boundingSphere.radius
// // console.log(center, radius)
// const cartographic = Cesium.Cartographic.fromCartesian(
// entity.position._value
// )
// // -(Math.sqrt(3) * radius) / 3
// const adjustedHeight = radius * 0.8
// // + cartographic.height
// const newPosition = Cesium.Cartesian3.fromRadians(
// cartographic.longitude,
// cartographic.latitude,
// adjustedHeight
// )
let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
entity.position._value
)
const heightOffset = Cesium.Matrix4.fromTranslation(
new Cesium.Cartesian3(
0,
0,
primitive.boundingSphere.radius * 0.8
)
)
modelMatrix = Cesium.Matrix4.multiply(
modelMatrix,
heightOffset,
new Cesium.Matrix4()
)
primitive.modelMatrix = modelMatrix
// Cesium.Matrix4.setTranslation(
// primitive.modelMatrix,
// newPosition,
// primitive.modelMatrix
// )
}
}
})
})
// console.log(viewer.scene.primitives._primitives)
// // 获取模型的 Primitive 实例
// const primitiveModel = viewer.scene.primitives._primitives.filter(
// primitive => {
// console.log(primitive, modelUrls)
// return modelUrls.includes(primitive?._resource?.url)
// }
// )
// console.log(primitiveModel)
// 等待模型加载完成并调整其位置
// viewer.scene.preRender.addEventListener(() => {
// // 遍历场景中的所有 Primitive
// viewer.scene.primitives._primitives.forEach(primitive => {
// if (
// primitive instanceof Cesium.Model && // 判断是否为 Model 类型
// modelUrls.includes(primitive.uri) // 确保是我们需要的模型
// ) {
// // 获取 BoundingSphere 信息
// const boundingSphere = primitive.boundingSphere
// if (boundingSphere) {
// // 计算模型底部到中心的偏移量
// const bottomOffset = boundingSphere.radius
// // 获取模型当前的 Cartographic 坐标
// const cartographic = Cesium.Cartographic.fromCartesian(
// modelEntity.position
// )
// // 调整高度,使模型底部贴地
// const adjustedHeight = cartographic.height - bottomOffset
// const newPosition = Cesium.Cartesian3.fromRadians(
// cartographic.longitude,
// cartographic.latitude,
// adjustedHeight
// )
// // 更新 Entity 的位置
// modelEntity.position = newPosition
// // 删除监听器,防止重复调整
// viewer.scene.preRender.removeEventListener(arguments.callee)
// }
// }
// })
// })
}
removeIds.forEach(id => {
@ -109,13 +235,13 @@ function getAllKeys(treeData: any) {
}
function addEntity(data: TBaseNode) {
const { sheShiName, sheShiType, geom } = data
const { id, sheShiName, sheShiType, geom } = data
const pos = parseWKT(geom).coordinates
const color = Cesium.Color.fromCssColorString(
window.settings.baseMBDict[sheShiType].color
)
const entity = viewer.entities.add({
name: sheShiName,
name: id,
position: Cesium.Cartesian3.fromDegrees(...pos),
label: {
text: `${sheShiName}`,
@ -135,6 +261,7 @@ function addEntity(data: TBaseNode) {
scaleByDistance: new Cesium.NearFarScalar(7000000, 1.0, 18000000, 0.4),
},
billboard: {
show: !iconOrModel.value,
image: window.settings.baseMBDict[sheShiType].icon,
width: 30,
height: 30,
@ -144,6 +271,13 @@ function addEntity(data: TBaseNode) {
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
model: {
show: iconOrModel.value,
uri: window.settings.baseMBDict[sheShiType].model,
scale: 30,
minimumPixelSize: 30,
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
},
})
return entity
}

View File

@ -19,7 +19,45 @@ export default defineComponent({
tilingScheme: new Cesium[baseMapList[mapId].tilingScheme](),
})
)
// modifyMap()
}
function modifyMap() {
//
let baseLayer = viewer.imageryLayers.get(0)
//2
baseLayer.invertColor = true
baseLayer.filterRGB = [60, 145, 172] //[255,255,255] = > [0,50,100]
//
const baseFragmentShader =
viewer.scene.globe._surfaceShaderSet.baseFragmentShaderSource.sources
// console.log(baseFragmentShader);
//
// for (let i = 0; i < baseFragmentShader.length; i++) {
// console.log(baseFragmentShader[i]);
const strS = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
let strT = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
if (baseLayer.invertColor) {
strT += `
color.r = 1.0 - color.r;
color.g = 1.0 - color.g;
color.b = 1.0 - color.b;
`
}
if (baseLayer.filterRGB) {
strT += `
color.r = color.r*${baseLayer.filterRGB[0]}.0/255.0;
color.g = color.g*${baseLayer.filterRGB[1]}.0/255.0;
color.b = color.b*${baseLayer.filterRGB[2]}.0/255.0;
`
}
baseFragmentShader[2] = baseFragmentShader[2].replace(strS, strT)
// }
}
onMounted(() => {
changeMap(Object.keys(baseMapList)[2])
})

View File

@ -15,6 +15,8 @@ import MultiHisTrajectory from '../Mubiao/components/MultiHisTrajectory/index.vu
import YsHangjing from '../YsHangjing/index.vue'
import TextMessage from '../TextReport/components/Message'
import Daodan from '../Daodan'
// import HeatMap from '../Mubiao/components/HeatMap/index.vue'
// import TextDetailModal from '../TextReport/components/DetailsModal/index.vue'
@ -22,6 +24,7 @@ import { useTextReport } from '../TextReport/hooks/text'
import { useHisImage } from '../BaseMB/components/HisImages/hooks/hisImage'
import { useMBTrajectory } from '../Mubiao/components/HisTrajectory/hooks/mbTraj'
import { useMultiMBTrajectory } from '../Mubiao/components/MultiHisTrajectory/hooks/multiMbTraj'
import { useDaodan } from '../Daodan/daodan'
// import { useWeather } from '../Weather/hooks/weather'
import DetailsModal from './components/DetailsModal/index.vue'
@ -30,6 +33,7 @@ const { getTextConfigs, initWebSocket } = useTextReport()
const { showHisImageCom } = useHisImage()
const { showHisTrajCom } = useMBTrajectory()
const { showMultiHisTrajCom } = useMultiMBTrajectory()
const { showDdConfigCom } = useDaodan()
const getConfig = async () => {
const res = await getTextConfigs()
// console.log(res)
@ -122,9 +126,17 @@ const showOrHideTextReport = () => {
<div class="grid flex-1 grid-cols-[1.5fr_3fr_1.5fr] grid-rows-1 gap-1">
<div class="left-panel pl-8">
<div class="radio-group absolute -left-4 top-[15%] z-30 flex w-12 translate-y-[-50%] transform flex-col">
<div
class="radio-group absolute -left-4 top-[15%] z-30 flex w-12 translate-y-[-50%] transform flex-col"
>
<template v-for="panel in panelList" :key="panel.id">
<input type="radio" :id="panel.id" name="selector" v-model="showPanelName" :value="panel.value" />
<input
type="radio"
:id="panel.id"
name="selector"
v-model="showPanelName"
:value="panel.value"
/>
<label for="wx" @click="hidePanel($event, panel.value)">{{
panel.name
}}</label>
@ -179,10 +191,21 @@ const showOrHideTextReport = () => {
</div>
<!-- <div class="z-20 grid grid-cols-1 grid-rows-3 gap-1"> -->
<div>
<div class="btn-transform z-20 w-h-full" :class="showTextReport ? '' : 'btn-transform-pos'">
<n-button class="absolute -left-[16px] top-5 z-30 border border-[#29baf1] bg-[var(--color-bg)]" size="tiny"
@click="showOrHideTextReport">
<n-icon class="btn-transform" :class="showTextReport ? '' : 'icon-transform'"><arrow-right /></n-icon>
<div
class="btn-transform z-20 w-h-full"
:class="showTextReport ? '' : 'btn-transform-pos'"
>
<n-button
class="absolute -left-[16px] top-5 z-30 border border-[#29baf1] bg-[var(--color-bg)]"
size="tiny"
@click="showOrHideTextReport"
>
<n-icon
class="btn-transform"
:class="showTextReport ? '' : 'icon-transform'"
>
<arrow-right />
</n-icon>
</n-button>
<!-- <transition name="slide2">.slice(0, 3) -->
<panel title="文字报"><text-report :tabs="types" /></panel>
@ -200,11 +223,23 @@ const showOrHideTextReport = () => {
</div> -->
</div>
</div>
<div class="absolute bottom-0 flex h-full w-full flex-col justify-end">
<text-message class="absolute z-30 h-[200px]"></text-message>
<mubiao-his-trajectory v-if="showHisTrajCom" class="z-30 h-[260px]"></mubiao-his-trajectory>
<multi-his-trajectory v-if="showMultiHisTrajCom" class="z-30 h-[260px]"></multi-his-trajectory>
<his-images v-if="showHisImageCom" class="z-30 h-[260px]"></his-images>
<div
class="absolute bottom-0 flex h-full w-full flex-col items-center justify-end"
>
<text-message class="absolute z-30 h-[200px] w-full"></text-message>
<mubiao-his-trajectory
v-if="showHisTrajCom"
class="z-30 h-[260px] w-full"
></mubiao-his-trajectory>
<multi-his-trajectory
v-if="showMultiHisTrajCom"
class="z-30 h-[260px] w-full"
></multi-his-trajectory>
<his-images
v-if="showHisImageCom"
class="z-30 h-[260px] w-full"
></his-images>
<daodan v-show="showDdConfigCom" class="z-30 h-[75%] w-[75%]"></daodan>
</div>
<details-modal></details-modal>
</div>
@ -373,4 +408,5 @@ const showOrHideTextReport = () => {
// // background: var(--gradient-bg);
// // background-clip: text;
// }
// }</style>
// }
</style>

View File

@ -0,0 +1,129 @@
import {
NInputNumber,
NDataTable,
NButton,
NDatePicker,
NSwitch,
NIcon,
} from 'naive-ui'
export default defineComponent({
name: 'TrajTable',
props: {
title: {
type: String,
required: true,
},
data: {
type: Array,
default: () => [],
},
},
setup(props) {
const { data: trajData } = toRefs(props)
const columns = [
{
title: '',
key: 'name',
render(row) {
return (
<div class="flex items-center justify-between gap-2">
<div>{row.name} </div>
<NButton
// quaternary
// type="primary"
size="tiny"
v-slots={{
icon: () => (
<NIcon size="14">
<svg
t="1737444618400"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="2312"
width="64"
height="64"
>
<path
d="M512 42.688c23.616 0 42.688 19.072 42.688 42.624v31.488c0 32.32 24.32 58.88 55.296 68.16a342.08 342.08 0 0 1 229.12 229.12c9.216 30.912 35.84 55.232 68.096 55.232h31.488a42.688 42.688 0 0 1 0 85.376h-31.488c-32.256 0-58.88 24.32-68.096 55.232a342.08 342.08 0 0 1-229.12 229.12c-30.976 9.28-55.232 35.84-55.296 68.16v31.488a42.688 42.688 0 0 1-85.312 0v-31.488c0-32.256-24.32-58.88-55.296-68.096a342.08 342.08 0 0 1-229.12-229.12c-9.28-30.976-35.84-55.296-68.096-55.296h-31.488a42.688 42.688 0 0 1 0-85.376h31.424c32.32 0 58.88-24.32 68.16-55.232a342.08 342.08 0 0 1 229.12-229.12c30.976-9.28 55.296-35.84 55.296-68.16v-31.424c0-23.616 19.072-42.688 42.624-42.688zM512 256a256 256 0 1 0 0 512 256 256 0 0 0 0-512z m0 170.688a85.312 85.312 0 1 1 0 170.624 85.312 85.312 0 0 1 0-170.624z"
p-id="2313"
></path>
</svg>
</NIcon>
),
}}
/>
</div>
)
},
},
{
title: '经度',
key: 'lon',
render(row) {
return (
<NInputNumber
v-model:value={row.lon}
max={180}
min={-180}
></NInputNumber>
)
},
},
{
title: '纬度',
key: 'lat',
render(row) {
return (
<NInputNumber
v-model:value={row.lat}
max={90}
min={-90}
></NInputNumber>
)
},
},
{
title: '高度',
key: 'alt',
render(row) {
return <NInputNumber v-model:value={row.alt} min={0}></NInputNumber>
},
},
{
title: '时间',
key: 'time',
render(row) {
return (
<NDatePicker v-model:value={row.time} type="datetime"></NDatePicker>
)
},
},
{
title: '助推器脱落',
key: 'detached',
render(row, rowIndex) {
return rowIndex !== 0 && rowIndex < trajData.value.length - 1 ? (
<NSwitch v-model:value={row.detached} type="datetime"></NSwitch>
) : (
'-'
)
},
},
]
return () => (
<>
<div class="flex justify-between">
<div class="detail-item-title">{props.title}</div>
<NButton quaternary type="primary">
添加特征点
</NButton>
</div>
<NDataTable data={trajData.value} columns={columns} />
</>
)
},
})

View File

@ -0,0 +1,24 @@
import { NUpload, NButton } from 'naive-ui'
export default defineComponent({
name: 'TrajUpload',
props: {
title: {
type: String,
required: true,
},
},
setup(props, { emit }) {
return () => (
<>
<div class="detail-item-title">{props.title}</div>
<div class="flex items-center gap-2">
<div class="w-[80px]">STK 文件</div>
<NUpload action="https://www.mocky.io/v2/5e4bafc63100007100d8b70f">
<NButton>上传文件</NButton>
</NUpload>
</div>
</>
)
},
})

View File

@ -0,0 +1,16 @@
import { ref } from 'vue'
const daodanData = ref({})
export function useDaodan() {
return {
showDdConfigCom,
showOrHideDdConfig,
daodanData,
}
}
const showDdConfigCom = ref(false)
function showOrHideDdConfig(data) {
daodanData.value = data
showDdConfigCom.value = !showDdConfigCom.value
}

View File

@ -0,0 +1,87 @@
import { NTabs, NTabPane, NButton, NScrollbar } from 'naive-ui'
import TrajTable from './components/TrajTable'
import TrajUpload from './components/TrajUpload'
import Panel from '@/components/Panel/index.vue'
import { useDaodan } from './daodan'
const panels = ['手动配置', 'STK轨迹文件配置']
export default defineComponent({
setup() {
const { daodanData, showOrHideDdConfig } = useDaodan()
const name = ref('手动配置')
const data = ref([
{
name: '起始点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
},
{
name: '中间特征点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
detached: false,
},
{
name: '落点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
},
])
const handleClose = () => {}
return () => (
<div>
<Panel title={`${daodanData.value.targetName}配置`}>
<div class="flex h-full w-full flex-col gap-2 p-2">
<NTabs
class="flex h-[calc(100%-42px)] flex-col"
v-model:value={name.value}
type="card"
tab-style="min-width: 80px;"
onClose={handleClose}
>
{panels.map(panel => (
<NTabPane
class="flex-1 overflow-y-auto rounded-b-[var(--n-tab-border-radius)] border border-[var(--n-tab-border-color)] border-t-transparent"
key={panel}
tab={panel}
name={panel}
>
<NScrollbar>
<div class="px-4 pb-4">
<div class="detail-container">
{panel === '手动配置' ? (
<>
<TrajTable title="轨迹点" data={data.value} />
<TrajTable title="拦截弹" data={data.value} />
</>
) : (
<>
<TrajUpload title="轨迹点" />
<TrajUpload title="拦截弹" />
</>
)}
</div>
</div>
</NScrollbar>
</NTabPane>
))}
</NTabs>
<div class="flex justify-end gap-2">
<NButton type="primary" onClick={() => showOrHideDdConfig({})}>
确认
</NButton>
<NButton onClick={() => showOrHideDdConfig({})}>取消</NButton>
</div>
</div>
</Panel>
</div>
)
},
})

View File

@ -52,18 +52,18 @@ onMounted(async () => {
viewer.scene.postProcessStages.fxaa.enabled = true
viewer.scene.skyBox = new Cesium.SkyBox({
sources: {
// positiveX: 'GV/resources/theme/skyBox/PositiveX.jpg',
// negativeX: 'GV/resources/theme/skyBox/NegativeX.jpg',
// positiveY: 'GV/resources/theme/skyBox/PositiveY.jpg',
// negativeY: 'GV/resources/theme/skyBox/NegativeY.jpg',
// positiveZ: 'GV/resources/theme/skyBox/PositiveZ.jpg',
// negativeZ: 'GV/resources/theme/skyBox/NegativeZ.jpg',
positiveX: './images/skybox/skyCube2k_px.jpg',
negativeX: './images/skybox/skyCube2k_mx.jpg',
positiveY: './images/skybox/skyCube2k_py.jpg',
negativeY: './images/skybox/skyCube2k_my.jpg',
positiveZ: './images/skybox/skyCube2k_pz.jpg',
negativeZ: './images/skybox/skyCube2k_mz.jpg',
positiveX: 'GV/resources/theme/skyBox/PositiveX.jpg',
negativeX: 'GV/resources/theme/skyBox/NegativeX.jpg',
positiveY: 'GV/resources/theme/skyBox/PositiveY.jpg',
negativeY: 'GV/resources/theme/skyBox/NegativeY.jpg',
positiveZ: 'GV/resources/theme/skyBox/PositiveZ.jpg',
negativeZ: 'GV/resources/theme/skyBox/NegativeZ.jpg',
// positiveX: './images/skybox/skyCube2k_px.jpg',
// negativeX: './images/skybox/skyCube2k_mx.jpg',
// positiveY: './images/skybox/skyCube2k_py.jpg',
// negativeY: './images/skybox/skyCube2k_my.jpg',
// positiveZ: './images/skybox/skyCube2k_pz.jpg',
// negativeZ: './images/skybox/skyCube2k_mz.jpg',
},
})
@ -118,9 +118,3 @@ onMounted(async () => {
class="absolute bottom-0 left-0 right-0 top-0"
></div>
</template>
<style lang="scss">
.cesium-widget-credits {
display: none !important;
}
</style>

View File

@ -2,6 +2,8 @@ import { difference } from 'lodash'
import { useWebSocket } from '@vueuse/core'
import { useTree } from '@/utils/tree'
import { useHjPolygon } from './hangjingPolygon'
import { getHangjing } from '@/api/Hangjing'
interface IPolygonData {
id: string
position: number[]
@ -17,8 +19,8 @@ export const useHangjing = () => {
onMounted(() => {
initWebSocket()
})
const addHangjing = (ids, hangjingData) => {
addHangjingPolygon(ids, hangjingData)
const addHangjing = ids => {
addHangjingPolygon(ids, hjTreeData)
}
const removeHangjing = (id: string) => {
@ -28,6 +30,15 @@ export const useHangjing = () => {
return {
addHangjing,
removeHangjing,
hjTreeData,
isLoading,
timeRange,
searchTitle,
type,
searchHangjing,
clearSelected,
getHangjingData,
checkedKeys,
}
}
@ -89,3 +100,44 @@ const initWebSocket = () => {
}
})
}
const hjTreeData = ref([])
const isLoading = ref(false)
async function getHangjingData(params = {}) {
isLoading.value = true
const { code, data: resData } = await getHangjing(params)
if (code === '200') {
hjTreeData.value = [resData]
}
isLoading.value = false
}
const timeRange = ref(null)
const searchTitle = ref('')
const type = ref<string>('')
async function searchHangjing(checked: string[]) {
// handleCheck([])
await getHangjingData({
timeBegin: timeRange.value?.[0],
timeEnd: timeRange.value?.[1],
// wkt: drawnArea.value,
hjType: type.value === '' ? null : type.value,
title: searchTitle.value,
})
if (checked && checked.length > 0) {
checkedKeys.value = checked
}
}
function clearSelected() {
timeRange.value = null
type.value = ''
searchTitle.value = ''
searchHangjing()
}
const checkedKeys = ref<Array<string | number>>([])

View File

@ -13,7 +13,7 @@ function renderDetailsContent(data) {
<div class="detail-item-title">基本信息</div>
<div>
{Object.keys(data)
.filter(key => key !== 'geom')
.filter(key => !['geom', 'styleJsonData'].includes(key))
.map(key => (
<div>
{key}{data[key]}

View File

@ -4,8 +4,9 @@ import { parseWKT } from '@/utils/parseWKT'
import { difference } from 'lodash'
import { polygonGradient } from '@/js/polygonGradient'
import { polygonMaterial } from '@/js/polygon'
import { centerOfMass } from '@turf/turf'
import { centerOfMass, midpoint, point } from '@turf/turf'
import { useTree } from '@/utils/tree'
import { cartesian32LonLat } from '@/utils/pos'
import { useHangjingPopup } from './hangjingPopup'
@ -34,12 +35,14 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
params: addIds,
paramName: 'dataId',
})
// console.log(nodes, 'nodes')
nodes.forEach(({ data: hjData, dataId: id }) => {
// const item = data.find(item => item.id === id)
if (hjData) {
if (hjData.zoneList.length > 0) {
hjData.zoneList.forEach(zone => {
addPolygon(zone, id)
// console.log(zone, 'zone')
addPolygon({ ...zone, styleJsonData: hjData.styleJsonData }, id)
})
} else {
addPolygon(hjData)
@ -49,7 +52,7 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
}
// 删除
if (removeIds.length > 0) {
console.log(removeIds, 'removeIds')
// console.log(removeIds, 'removeIds')
removeIds.forEach(id => {
removeEventSub(polygonMap.get(id))
removeHangjingPolygon(id)
@ -58,13 +61,13 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
}
function addPolygon(item, parentId: number | null = null) {
const { id, geom, title } = item
const { id, geom, title, styleJsonData } = item
const feature = parseWKT(geom)
const position = feature.coordinates[0].map(pos => {
return Cesium.Cartesian3.fromDegrees(...pos)
})
const labels = addTextAlongCurve('Cesium中文垂直排列测试', position)
// const labels = addTextAlongCurve('Cesium中文垂直排列测试', position)
// console.log(item, id, position, 'id, position, color')
// const randomColor =
@ -86,10 +89,13 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
}
const color = colors.get(curId)
const style = JSON.parse(styleJsonData || '{}')
const polygon = addPrimitivePolygon({
id,
title,
color: color,
position,
style,
})
const centerPoint = centerOfMass(feature).geometry.coordinates
@ -117,12 +123,21 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
function addPolygonCenter(centerPoint: number[]) {
return viewer.entities.add({
show: false,
show: true,
position: Cesium.Cartesian3.fromDegrees(...centerPoint),
point: {
pixelSize: 10,
color: Cesium.Color.RED,
},
label: {
text: centerPoint.toString(),
font: '14px sans-serif',
fillColor: Cesium.Color.RED,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
pixelOffset: new Cesium.Cartesian2(0, -20),
},
})
}
@ -130,11 +145,24 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
id,
position,
color,
style,
title,
}: {
id: string
position: Cesium.Cartesian3[]
color: string
style: Record<string, string | number>
title: string
}): Cesium.Primitive {
const {
fontFamily,
fontSize,
textColor,
polygonColor,
lineColor,
lineWidth,
lineType,
} = style
// const polygonOptions = {
// extrudedHeight: 5000,
// polygonHierarchy: new Cesium.PolygonHierarchy(position),
@ -156,17 +184,37 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
// }),
// })
// return viewer.scene.primitives.add(primitive)
const lineMaterial = Cesium.Color.fromCssColorString(
lineColor || 'rgba(255,0,0,1)'
)
return viewer.entities.add({
id,
position: findNorthernMostPoint(position),
label: {
show: true,
text: title,
font: `${fontSize || 14}px ${fontFamily || '微软雅黑'}`,
fillColor: Cesium.Color.fromCssColorString(
textColor || 'rgba(255,255,0,1)'
),
pixelOffset: new Cesium.Cartesian2(0, -10),
// outlineColor: Cesium.Color.BLACK,
// outlineWidth: 2,
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
},
polyline: {
positions: position,
width: 1,
material: Cesium.Color.RED,
width: lineWidth || 1,
material:
lineType === 'dashed'
? new Cesium.PolylineDashMaterialProperty({ color: lineMaterial })
: lineMaterial,
},
polygon: {
hierarchy: new Cesium.PolygonHierarchy(position),
material: Cesium.Color.fromCssColorString(color),
material: Cesium.Color.fromCssColorString(
polygonColor || 'rgba(255,255,255,0.5)'
),
// material: polygonMaterial(color),
// material: polygonGradient(color),
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
@ -225,120 +273,27 @@ export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
}
}
function addTextAlongCurve(text, polygonPoints) {
// 创建 Billboard 集合
const billboardCollection = viewer.scene.primitives.add(
new Cesium.BillboardCollection()
function findNorthernMostPoint(positions) {
const cartographicPositions = positions.map(pos =>
Cesium.Cartographic.fromCartesian(pos)
)
// 创建文字绘制的辅助函数
function createTextTexture(text, angle) {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
// 设置 Canvas 大小
canvas.width = 20
canvas.height = 20
// 设置文字样式
context.font = '20px sans-serif' // 支持中文
context.fillStyle = 'blue'
context.textAlign = 'center'
context.textBaseline = 'middle'
// 将文字绘制到 Canvas并进行旋转
context.clearRect(0, 0, canvas.width, canvas.height)
context.save()
// 旋转文字
context.translate(canvas.width / 2, canvas.height / 2)
context.rotate(angle)
context.fillText(text, 0, 0)
context.restore()
return canvas
}
// 动态生成字符标注
function generateLabels(cameraHeight) {
billboardCollection.removeAll() // 清除之前的标注
let charIndex = 0 // 当前字符索引
for (let i = 0; i < polygonPoints.length - 1; i++) {
const start = polygonPoints[i] // 当前边的起点
const end = polygonPoints[i + 1] // 当前边的终点
// 计算线段的方向向量
const direction = Cesium.Cartesian3.subtract(
end,
start,
new Cesium.Cartesian3()
)
Cesium.Cartesian3.normalize(direction, direction)
// 计算垂直于线段的方向向量
const perpendicular = Cesium.Cartesian3.cross(
direction,
Cesium.Cartesian3.UNIT_Z, // 使用 Z 轴(垂直地球表面)计算垂直方向
new Cesium.Cartesian3()
)
Cesium.Cartesian3.normalize(perpendicular, perpendicular)
// 计算线段的长度
const length = Cesium.Cartesian3.distance(start, end)
// 动态调整字符间隔,基于相机高度
const baseSpacing = 50 // 基础字符间隔
const charSpacing = Math.max((baseSpacing * cameraHeight) / 5000000, 30)
// 按字符间隔放置文字
let distance = 0
while (distance < length && charIndex < text.length) {
// 计算字符的位置
const fraction = distance / length // 当前字符在边上的位置比例
const position = Cesium.Cartesian3.lerp(
start,
end,
fraction,
new Cesium.Cartesian3()
)
// 计算旋转角度,使文字垂直于线段
const angle = Math.atan2(perpendicular.y, perpendicular.x)
// 创建带旋转的文字纹理
const canvas = createTextTexture(text[charIndex], angle)
// 添加 Billboard 显示文字
billboardCollection.add({
position: position,
image: canvas, // 使用生成的文字纹理
// pixelOffset: new Cesium.Cartesian2(10, 0),
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
})
// 更新字符索引和距离
charIndex++
console.log(charSpacing)
distance += charSpacing * 2000
// distance *= 2
console.log(distance)
}
// 如果字符已用完,跳出循环
if (charIndex >= text.length) {
break
}
let maxLatitude = -Infinity
cartographicPositions.forEach(carto => {
if (carto.latitude > maxLatitude) {
maxLatitude = carto.latitude
}
}
// 初始化标注(根据初始相机高度)
generateLabels(viewer.camera.positionCartographic.height)
// 监听相机变化事件,动态更新字符间隔
viewer.camera.changed.addEventListener(() => {
const cameraHeight = viewer.camera.positionCartographic.height
generateLabels(cameraHeight)
})
const northernMostPoints = cartographicPositions.filter(
carto => carto.latitude.toFixed(15) === maxLatitude.toFixed(15)
)
const lat = Cesium.Math.toDegrees(northernMostPoints[0].latitude)
const startLon = Cesium.Math.toDegrees(northernMostPoints[0].longitude)
const endLon = Cesium.Math.toDegrees(
northernMostPoints[
northernMostPoints.length - (northernMostPoints.length > 2 ? 2 : 1)
].longitude
)
const mid = midpoint(point([startLon, lat]), point([endLon, lat]))
return Cesium.Cartesian3.fromDegrees(...mid.geometry.coordinates)
}

View File

@ -7,23 +7,36 @@ import {
NInputNumber,
NButton,
} from 'naive-ui'
import { useHangjing } from './hangjing'
import { updateHjStyle } from '@/api/hangjing'
import { useModal } from '@/views/Content/hooks/modal'
const { openDetailsModal } = useModal()
const styleForm = ref({
fontFamily: '微软雅黑',
fontSize: 14,
textColor: 'rgba(255,255,0,1)',
polygonColor: 'rgba(255,0,0,0.3)',
lineColor: 'rgba(255,0,0,1)',
lineWidth: 1,
lineType: 'solid',
})
const styleForm = ref({})
export const useHangjingStyle = () => {
return { renderStyleContent, showStyleModal }
}
function initStyle(data) {
const { styleJsonData } = data
const style = JSON.parse(styleJsonData || '{}')
if (Object.keys(style).length > 0) {
styleForm.value = style
} else {
styleForm.value = {
fontFamily: '微软雅黑',
fontSize: 14,
textColor: 'rgba(255,255,0,1)',
polygonColor: 'rgba(255,255,255,0.5)',
lineColor: 'rgba(255,0,0,1)',
lineWidth: 1,
lineType: 'solid',
}
}
}
function renderStyleContent(data) {
initStyle(data)
return (
<div class="flex flex-col gap-2 pt-4">
<NForm
@ -94,7 +107,7 @@ function renderStyleContent(data) {
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton type="primary" onClick={updateStyle}>
<NButton type="primary" onClick={() => updateStyle(data)}>
确定
</NButton>
<NButton onClick={updateStyle}>取消</NButton>
@ -110,6 +123,22 @@ function showStyleModal(title, data) {
})
}
function updateStyle() {
// TODO:
const { checkedKeys, searchHangjing } = useHangjing()
function updateStyle(data) {
updateHjStyle({
id: data.id,
styleJsonData: JSON.stringify(styleForm.value),
})
.then(res => {
if (res.code === '200') {
window.$message.success('保存样式成功')
const checkedBeforePut = JSON.parse(JSON.stringify(checkedKeys.value))
checkedKeys.value = []
searchHangjing(checkedBeforePut)
}
})
.catch(err => {
window.$message.error(`保存样式失败。${err}`)
})
}

View File

@ -12,10 +12,6 @@ import { useHangjingDetail } from './hooks/hangjingDetail'
// import { useHangjingStyle } from './hooks/hangjingStyle'
import Tree from '@/components/Tree/index.vue'
const timeRange = ref(null)
const rangeShortcuts = {
近一天: () => {
const cur = new Date().getTime()
@ -35,9 +31,6 @@ const rangeShortcuts = {
},
}
const searchTitle = ref('')
const type = ref<string>('')
const typeOptions = [
{
label: '全',
@ -102,86 +95,69 @@ const drawArea = () => {
}
const { showDetailsModal } = useHangjingDetail()
// const { showStyleModal } = useHangjingStyle()
const data = ref([])
const renderSuffix = ({ option }: { option: TreeOption }) => {
return option.data ? h('div', { class: 'flex items-center gap-2 pr-2' }, [
h(
NButton,
{
text: true,
size: 'tiny',
type: 'info',
onClick: () => showDetailsModal(`${option.data.title}详情`, option.data),
},
{ default: () => '详情' }
),
// h(
// NButton,
// {
// text: true,
// size: 'tiny',
// type: 'info',
// onClick: () => showStyleModal(`${option.data.title}`, option.data),
// },
// { default: () => '' }
// ),
]) : null
return option.data
? h('div', { class: 'flex items-center gap-2 pr-2' }, [
h(
NButton,
{
text: true,
size: 'tiny',
type: 'info',
onClick: () =>
showDetailsModal(`${option.data.title}详情`, option.data),
},
{ default: () => '详情' }
),
// h(
// NButton,
// {
// text: true,
// size: 'tiny',
// type: 'info',
// onClick: () => showStyleModal(`${option.data.title}`, option.data),
// },
// { default: () => '' }
// ),
])
: null
}
const checkedKeys = ref<Array<string | number>>([])
// const checkedKeys = ref<Array<string | number>>([])
const { addHangjing } = useHangjing()
const {
addHangjing,
hjTreeData,
isLoading,
timeRange,
searchTitle,
type,
searchHangjing,
clearSelected,
getHangjingData,
checkedKeys,
} = useHangjing()
watch(checkedKeys, val => {
addHangjing(val, data)
addHangjing(val)
})
const handleCheck = (rowKeys: DataTableRowKey[]) => {
const selectedList = data.value.filter(item => rowKeys.includes(item.id))
addHangjing(selectedList)
}
const isLoading = ref(false)
const getHangjingData = async (params = {}) => {
isLoading.value = true
const { code, data: resData } = await getHangjing(params)
if (code === '200') {
data.value = [resData]
}
isLoading.value = false
}
onMounted(() => {
getHangjingData()
})
const searchHangjing = () => {
handleCheck([])
getHangjingData({
timeBegin: timeRange.value?.[0],
timeEnd: timeRange.value?.[1],
wkt: drawnArea.value,
hjType: type.value === '' ? null : type.value,
title: searchTitle.value,
})
}
const clearSelected = () => {
timeRange.value = null
type.value = ''
searchTitle.value = ''
searchHangjing()
}
</script>
<template>
<div class="flex flex-col gap-2 w-h-full" v-loading="isLoading">
<n-date-picker v-model:formatted-value="timeRange" clearable type="datetimerange" :shortcuts="rangeShortcuts"
:update-value-on-close="true" format="yyyy-MM-dd HH:mm:ss" />
<n-date-picker
v-model:formatted-value="timeRange"
clearable
type="datetimerange"
:shortcuts="rangeShortcuts"
:update-value-on-close="true"
format="yyyy-MM-dd HH:mm:ss"
/>
<div class="flex gap-2">
<n-input class="w-auto" v-model:value="searchTitle" />
<n-select class="w-40" v-model:value="type" :options="typeOptions" />
@ -213,8 +189,14 @@ const clearSelected = () => {
flex-height @update:checked-row-keys="handleCheck" /> -->
<!-- :pagination="paginationReactive" -->
<tree :data="data" :key-field="'dataId'" :label-field="'nodeName'" v-model:checked="checkedKeys" showSearch
:renderSuffix="renderSuffix" />
<tree
:data="hjTreeData"
:key-field="'dataId'"
:label-field="'nodeName'"
v-model:checked="checkedKeys"
showSearch
:renderSuffix="renderSuffix"
/>
</div>
</template>

View File

@ -1,86 +1,14 @@
// import { h } from 'vue'
import { NDataTable, NInputNumber, NSwitch } from 'naive-ui'
import { useModal } from '@/views/Content/hooks/modal'
import Detection from '@/views/Payload/Detection.jsx'
import Communication from '@/views/Payload/Communication.jsx'
const { openDetailsModal } = useModal()
export const useMubiaoDetail = () => {
return { showDetailsMubiao }
}
const detectingLoadColumns = [
{
title: '垂直起始角',
key: 'minimumClock',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.minimumClock}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
// {
// title: '',
// key: 'maximumClock',
// width: 180,
// render(row) {
// return (
// <NInputNumber
// v-model:value={row.maximumClock}
// // disabled={['', ''].includes(row.type)}
// ></NInputNumber>
// )
// },
// },
{
title: '水平起始角',
key: 'minimumCone',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.minimumCone}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
{
title: '水平终止角',
key: 'maximumCone',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.maximumCone}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
{
title: '半径',
key: 'radius',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.radius}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
{
title: '是否开启',
key: 'status',
render(row) {
return <NSwitch v-model:value={row.status}></NSwitch>
},
},
]
const data = ref([
{
id: 3,
@ -111,11 +39,7 @@ function renderMubiaoDetailsContent(mbData) {
))}
</div>
<div class="detail-item-title">探测载荷</div>
<NDataTable
key={row => row.id}
columns={detectingLoadColumns}
data={data.value}
/>
<Detection type="radar" data={data.value} />
</div>
)
}

View File

@ -11,6 +11,7 @@ import { useMuBiaoPositionWS } from './hooks/mubiaoPos'
import { useMuBiaoDisappearWS } from './hooks/mubiaoDisappear'
import { useMubiaoDetail } from './hooks/mubiaoDetail'
import { useMBTrajectory } from './components/HisTrajectory/hooks/mbTraj'
import { useDaodan } from '../Daodan/daodan'
const { addMubiao, data } = useMubiao()
const useMbPosWS = useMuBiaoPositionWS()
@ -37,30 +38,35 @@ watch(checkedKeys, val => {
const { showDetailsMubiao } = useMubiaoDetail()
const { showOrHideHisTraj } = useMBTrajectory()
const { showOrHideDdConfig } = useDaodan()
const renderSuffix = ({ option }: { option: TreeOption }) => {
// console.log(option.data)
return option.data
? h('div', { class: 'flex items-center gap-2 pr-2' }, [
if (!option.data) {
return undefined
}
return h('div', { class: 'flex items-center gap-2 pr-2' }, [
h(
NButton,
{
text: true,
size: 'tiny',
type: 'info',
onClick: () => showDetailsMubiao(option.data),
},
{ default: () => '详情' }
),
option.data.targetType === '丙' &&
h(
NButton,
{
text: true,
size: 'tiny',
type: 'info',
onClick: () => showDetailsMubiao(option.data),
onClick: () => showOrHideDdConfig(option.data),
},
{ default: () => '详情' }
{ default: () => '试验配置' }
),
// h(
// NButton,
// {
// text: true,
// size: 'tiny',
// type: 'info',
// onClick: () => showDetailsMubiao(option.data),
// },
// { default: () => '' }
// ),
option.data.targetType !== '丙' &&
h(
NButton,
{
@ -71,8 +77,7 @@ const renderSuffix = ({ option }: { option: TreeOption }) => {
},
{ default: () => '轨迹回放' }
),
])
: undefined
])
}
</script>
@ -99,8 +104,14 @@ const renderSuffix = ({ option }: { option: TreeOption }) => {
@update:checked-row-keys="handleCheck"
/> -->
<div class="h-0 flex-1">
<tree :data="data" :key-field="'dataId'" :label-field="'nodeName'" v-model:checked="checkedKeys" showSearch
:renderSuffix="renderSuffix" />
<tree
:data="data"
:key-field="'dataId'"
:label-field="'nodeName'"
v-model:checked="checkedKeys"
showSearch
:renderSuffix="renderSuffix"
/>
</div>
<!-- :nodeProps="nodeProps" -->

View File

@ -0,0 +1,44 @@
import { NDataTable, NButton, NSelect, NInputNumber, NSwitch } from 'naive-ui'
export default defineComponent({
name: 'DetectionPayload',
props: {},
setup(props) {
const communicationPayloadColumns = [
{
title: '通信对象',
key: 'target',
render(row) {
return (
<NSelect
v-model:value={row.target}
options={[{ label: '3', value: '3' }]}
></NSelect>
)
},
},
{
title: '是否开启',
key: 'status',
width: 120,
render(row) {
return <NSwitch v-model:value={row.status}></NSwitch>
},
},
]
const communicationPayload = ref([
{
id: 3,
target: '3',
status: true,
},
])
return () => (
<NDataTable
key={row => row.id}
columns={communicationPayloadColumns}
data={communicationPayload.value}
/>
)
},
})

View File

@ -0,0 +1,172 @@
import { NDataTable, NButton, NSelect, NInputNumber, NSwitch } from 'naive-ui'
export default defineComponent({
name: 'DetectionPayload',
props: {
type: {
type: String,
default: 'radar',
},
data: {
type: Array,
default: () => [],
},
},
setup(props) {
const detectingPayloadColumns = {
radar: [
{
title: '垂直起始角',
key: 'minimumClock',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.minimumClock}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
// {
// title: '',
// key: 'maximumClock',
// width: 180,
// render(row) {
// return (
// <NInputNumber
// v-model:value={row.maximumClock}
// // disabled={['', ''].includes(row.type)}
// ></NInputNumber>
// )
// },
// },
{
title: '水平起始角',
key: 'minimumCone',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.minimumCone}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
{
title: '水平终止角',
key: 'maximumCone',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.maximumCone}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
{
title: '半径',
key: 'radius',
width: 180,
render(row) {
return (
<NInputNumber
v-model:value={row.radius}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
{
title: '是否开启',
key: 'status',
width: 120,
render(row) {
return <NSwitch v-model:value={row.status}></NSwitch>
},
},
],
corner: [
// {
// title: '',
// key: 'type',
// render(row) {
// return (
// <NSelect
// v-model:value={row.type}
// options={[
// { label: '', value: '' },
// { label: '', value: '' },
// { label: '', value: '' },
// ]}
// ></NSelect>
// )
// },
// },
{
title: '开合角',
key: 'angle',
render(row) {
return (
<NInputNumber
v-model:value={row.angle}
max={120}
min={0}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
// {
// title: '',
// key: 'xHalfAngle',
// width: 120,
// render(row) {
// return (
// <NInputNumber
// v-model:value={row.angle}
// max={120}
// min={0}
// disabled={[''].includes(row.type)}
// ></NInputNumber>
// )
// },
// },
// {
// title: '',
// key: 'yHalfAngle',
// width: 120,
// render(row) {
// return (
// <NInputNumber
// v-model:value={row.angle}
// max={120}
// min={0}
// disabled={[''].includes(row.type)}
// ></NInputNumber>
// )
// },
// },
{
title: '是否开启',
key: 'status',
width: 120,
render(row) {
return <NSwitch v-model:value={row.status}></NSwitch>
},
},
],
}
return () => (
<NDataTable
key={row => row.id}
columns={detectingPayloadColumns[props.type]}
data={props.data}
/>
)
},
})

View File

@ -0,0 +1,152 @@
import { ref } from 'vue'
let viewer = null
const model = ref({
name: null,
lon: null,
lat: null,
level: null,
})
export function usePlaceName() {
const showPlace = ref(true)
function addPlaceName() {
showPlace.value = !showPlace.value
}
return {
model,
showPlace,
addPlaceName,
initViewer,
clickPoint,
}
}
async function initViewer() {
viewer = new Cesium.Viewer('place-earth', {
selectionIndicator: false,
animation: false,
baseLayerPicker: false,
// geocoder: false,
timeline: false,
sceneModePicker: false,
navigationHelpButton: false,
infoBox: false,
fullscreenButton: false,
homeButton: false,
imageryProvider: false,
geocoder: new OpenStreetMapNominatimGeocoder(),
// geocoder: new Cesium.CartographicGeocoderService(),
})
viewer.scene.debugShowFramesPerSecond = true
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(117.48, 30.67, 18000000.0),
})
viewer.clock.shouldAnimate = true
viewer.shadows = false
// viewer.scene.globe.enableLighting = true
if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
viewer.resolutionScale = window.devicePixelRatio
}
// 齿
// viewer.scene.fxaa = true
viewer.scene.postProcessStages.fxaa.enabled = true
viewer.scene.skyBox = new Cesium.SkyBox({
sources: {
positiveX: 'GV/resources/theme/skyBox/PositiveX.jpg',
negativeX: 'GV/resources/theme/skyBox/NegativeX.jpg',
positiveY: 'GV/resources/theme/skyBox/PositiveY.jpg',
negativeY: 'GV/resources/theme/skyBox/NegativeY.jpg',
positiveZ: 'GV/resources/theme/skyBox/PositiveZ.jpg',
negativeZ: 'GV/resources/theme/skyBox/NegativeZ.jpg',
// positiveX: './images/skybox/skyCube2k_px.jpg',
// negativeX: './images/skybox/skyCube2k_mx.jpg',
// positiveY: './images/skybox/skyCube2k_py.jpg',
// negativeY: './images/skybox/skyCube2k_my.jpg',
// positiveZ: './images/skybox/skyCube2k_pz.jpg',
// negativeZ: './images/skybox/skyCube2k_mz.jpg',
},
})
viewer.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
'http://192.168.10.201:2022/api/maptilecache/service/terrain/taiwan-HeightMap-4326',
{
requestVertexNormals: true,
requestWaterMask: true,
}
)
viewer.imageryLayers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url: window.settings.map[3].url,
tilingScheme: new Cesium[window.settings.map[3].tilingScheme](),
})
)
}
let getPointHandler = null
function clickPoint() {
if (getPointHandler) {
return
}
getPointHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
viewer._container.style.cursor = 'crosshair'
getPointHandler.setInputAction(event => {
const cartesian = viewer.scene.pickPosition(event.position)
if (Cesium.defined(cartesian)) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
const longitude = Cesium.Math.toDegrees(cartographic.longitude)
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
model.value.lon = longitude
model.value.lat = latitude
}
getPointHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
getPointHandler = null
viewer._container.style.cursor = ''
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
}
function OpenStreetMapNominatimGeocoder() {}
OpenStreetMapNominatimGeocoder.prototype.geocode = function (input) {
const query = `format=jsonv2&accept-language=zh&polygon_geojson=1&countrycodes=cn&q=${input}`
const requestString = 'https://nominatim.openstreetmap.org/search?' + query
return Cesium.Resource.fetchJson(requestString) //urljson
.then(function (results) {
let bboxDegrees
return results.map(function (resultObject) {
bboxDegrees = resultObject.boundingbox
return {
displayName: resultObject.display_name,
destination: Cesium.Rectangle.fromDegrees(
bboxDegrees[2],
bboxDegrees[0],
bboxDegrees[3],
bboxDegrees[1]
),
}
})
})
}
function AMapNominatimGeocoder() {}
AMapNominatimGeocoder.prototype.geocode = function (input) {
const query = `key=****&keywords=${input}`
const requestString = 'https://restapi.amap.com/v3/place/text?' + query
return Cesium.Resource.fetchJson(requestString) //urljson
.then(function (results) {
return results.pois.map(function (bboxDegrees) {
let [lng, lat] = bboxDegrees['location'].split(',')
return {
displayName: bboxDegrees['name'],
destination: Cesium.Rectangle.fromDegrees(lng, lat, lng, lat),
}
})
})
}

View File

@ -0,0 +1,225 @@
import {
NButton,
NForm,
NFormItem,
NInput,
NInputNumber,
NSelect,
} from 'naive-ui'
import { usePlaceName } from './hooks'
import maplibregl from 'maplibre-gl'
import 'maplibre-gl/dist/maplibre-gl.css'
export default defineComponent({
props: {
data: {
type: Array,
default: () => [],
},
},
setup() {
const { model, showPlace, addPlaceName, initViewer, clickPoint } =
usePlaceName()
onMounted(async () => {
const style = {
version: 8,
sources: {
composite: {
url: 'mapbox://mapbox.mapbox-streets-v8',
type: 'vector',
},
os: {
type: 'vector',
scheme: 'tms',
tiles: [
'http://192.168.10.187:18080/geoserver/gwc/service/tms/1.0.0/osm%3Aosm_admin@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf',
],
},
},
sprite: 'mapbox://sprites/mapbox/streets-v8',
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
layers: [
{
id: 'background',
type: 'background',
paint: {
'background-color': '#ffffff',
},
interactive: true,
},
{
id: 'traffic',
type: 'line',
source: 'os',
'source-layer': 'traffic3857',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#C1FFC1',
'line-width': 2,
},
interactive: true,
},
{
id: 'hydro',
type: 'line',
source: 'os',
'source-layer': 'hydro3857',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#3031ff',
'line-width': 1,
'line-dasharray': [1.5, 3],
},
},
],
}
const map = new maplibregl.Map({
container: 'place-earth', // container id
// style: 'https://demotiles.maplibre.org/style.json', // style URL
style: '/style.json', // style URL
center: [120.85, 23.86], // starting position [lng, lat]
zoom: 7,
})
map.on('click', e => {
const features = map.queryRenderedFeatures(e.point)
// Limit the number of properties we're displaying for
// legibility and performance
const displayProperties = [
'type',
'properties',
'id',
'layer',
'source',
'sourceLayer',
'state',
]
const displayFeatures = features.map(feat => {
console.log(feat)
// const displayFeat = {}
// displayProperties.forEach(prop => {
// displayFeat[prop] = feat[prop]
// })
// return displayFeat
})
// console.log(displayFeatures)
// document.getElementById('features').innerHTML = JSON.stringify(
// displayFeatures,
// null,
// 2
// )
})
// initViewer()
// let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
// handler.setInputAction(async function (event) {
// const pickRay = viewer.camera.getPickRay(event.position)
// const cartesian = viewer.scene.pickPosition(event.position)
// if (Cesium.defined(cartesian)) {
// const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
// const longitude = Cesium.Math.toDegrees(cartographic.longitude)
// const latitude = Cesium.Math.toDegrees(cartographic.latitude)
// const results = new Cesium.CartographicGeocoderService().geocode(
// `${longitude} ${latitude}`
// )
// console.log(results)
// if (results && results.length > 0) {
// console.log('Picked Name:', results[0].displayName)
// }
// }
// }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
})
return () => (
<div class="h-full w-full">
<div id="place-earth" class="h-full w-full"></div>
<div class="absolute left-0 top-0 p-2">
<NButton class="z-10" type="primary" onClick={() => addPlaceName()}>
添加地名
</NButton>
</div>
{showPlace.value && (
<div class="corner-border absolute left-2 top-14 z-30 flex w-[280px] flex-col gap-2 bg-[var(--color-bg)] p-2 p-2">
<NForm
class="p-2 pb-0"
model={model.value}
label-placement="left"
label-width="auto"
>
<NFormItem label="名称" path="name">
<NInput v-model:value={model.value.name} />
</NFormItem>
<NFormItem label="级别" path="level">
<NSelect
v-model:value={model.value.level}
options={[
{ label: '国', value: '2' },
{ label: '省', value: '4' },
{ label: '市', value: '5' },
]}
></NSelect>
</NFormItem>
<NFormItem label="经度" path="lon">
<NInputNumber
v-model:value={model.value.lon}
min={-180}
max={180}
></NInputNumber>
</NFormItem>
<NFormItem label="纬度" path="lat">
<NInputNumber
v-model:value={model.value.lat}
min={-90}
max={90}
></NInputNumber>
</NFormItem>
<NFormItem label=" " path="lat">
<NButton onClick={clickPoint}>坐标拾取</NButton>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2 p-2 pt-0">
<NButton
type="primary"
onClick={() => {
showPlace.value = false
}}
>
保存
</NButton>
<NButton
type="error"
onClick={() => {
showPlace.value = false
}}
>
删除
</NButton>
<NButton
onClick={() => {
showPlace.value = false
}}
>
取消
</NButton>
</div>
</div>
)}
</div>
)
},
})

View File

@ -1,91 +1,20 @@
import { useModal } from '@/views/Content/hooks/modal'
import { NDataTable, NSelect, NInputNumber, NSwitch } from 'naive-ui'
import { NDataTable, NButton, NSelect, NInputNumber, NSwitch } from 'naive-ui'
import Detection from '@/views/Payload/Detection.jsx'
import Communication from '@/views/Payload/Communication.jsx'
const { openDetailsModal } = useModal()
const detectingLoadColumns = [
// {
// title: '',
// key: 'type',
// render(row) {
// return (
// <NSelect
// v-model:value={row.type}
// options={[
// { label: '', value: '' },
// { label: '', value: '' },
// { label: '', value: '' },
// ]}
// ></NSelect>
// )
// },
// },
{
title: '开合角',
key: 'angle',
width: 120,
render(row) {
return (
<NInputNumber
v-model:value={row.angle}
max={120}
min={0}
// disabled={['', ''].includes(row.type)}
></NInputNumber>
)
},
},
// {
// title: '',
// key: 'xHalfAngle',
// width: 120,
// render(row) {
// return (
// <NInputNumber
// v-model:value={row.angle}
// max={120}
// min={0}
// disabled={[''].includes(row.type)}
// ></NInputNumber>
// )
// },
// },
// {
// title: '',
// key: 'yHalfAngle',
// width: 120,
// render(row) {
// return (
// <NInputNumber
// v-model:value={row.angle}
// max={120}
// min={0}
// disabled={[''].includes(row.type)}
// ></NInputNumber>
// )
// },
// },
{
title: '是否开启',
key: 'status',
render(row) {
return <NSwitch v-model:value={row.status}></NSwitch>
},
},
]
const data = ref([
{
id: 3,
angle: 30,
// xHalfAngle: 20,
// yHalfAngle: 25,
// type: '',
status: true,
},
])
export function showDetailsSatellite(option) {
const detectingPayload = ref([
{
id: 3,
angle: 30,
// xHalfAngle: 20,
// yHalfAngle: 25,
// type: '',
status: true,
},
])
openDetailsModal({
titleString: '' + option.name + ' 详情',
contentSlot: () => (
@ -103,17 +32,14 @@ export function showDetailsSatellite(option) {
</div>
</div>
<div class="detail-item-title">探测载荷</div>
<NDataTable
key={row => row.id}
columns={detectingLoadColumns}
data={data.value}
/>
<div class="detail-item-title">通信载荷</div>
{/* <NDataTable
key={row => row.id}
columns={detectingLoadColumns}
data={data.value}
/> */}
<Detection type="corner" data={detectingPayload.value} />
<div class="flex justify-between">
<div class="detail-item-title">通信载荷</div>
<NButton quaternary type="primary">
添加
</NButton>
</div>
<Communication />
</div>
),
})