Kaynağa Gözat

新增登陆后的过渡动画

画安 6 ay önce
ebeveyn
işleme
97bbdaa894

+ 1 - 0
.gitignore

@@ -21,3 +21,4 @@ pnpm-debug.log*
 *.njsproj
 *.sln
 *.sw?
+.claude

+ 299 - 3
package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.1.0",
       "dependencies": {
         "axios": "^1.13.5",
+        "cesium": "^1.105.1",
         "china-map-geojson": "^1.0.4",
         "core-js": "^3.8.3",
         "echarts": "^5.6.0",
@@ -1782,6 +1783,74 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@cesium/engine": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@cesium/engine/-/engine-2.4.0.tgz",
+      "integrity": "sha512-U5/m0XSpoxr5uo7rf0+rBNw4AwTMdQRRB5FX5bcunfE6J2eyKWtMp0lRhcTKVDN0O7HZwJIIZt0rtiXt/aC0tQ==",
+      "dependencies": {
+        "@tweenjs/tween.js": "^18.6.4",
+        "@zip.js/zip.js": "2.4.x",
+        "autolinker": "^4.0.0",
+        "bitmap-sdf": "^1.0.3",
+        "dompurify": "^3.0.2",
+        "earcut": "^2.2.4",
+        "grapheme-splitter": "^1.0.4",
+        "jsep": "^1.3.8",
+        "kdbush": "^4.0.1",
+        "ktx-parse": "^0.5.0",
+        "lerc": "^2.0.0",
+        "mersenne-twister": "^1.1.0",
+        "meshoptimizer": "^0.18.1",
+        "pako": "^2.0.4",
+        "protobufjs": "^7.1.0",
+        "rbush": "^3.0.1",
+        "topojson-client": "^3.1.0",
+        "urijs": "^1.19.7"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@cesium/widgets": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@cesium/widgets/-/widgets-2.2.0.tgz",
+      "integrity": "sha512-t15V02J5cJcq2GxJlWy6a5zT1P+kw7cGDOAKbWb/CMNXxc/bxl2FeOoX6PMT+FuPNxREkR+xMxtXig44hvbRJQ==",
+      "dependencies": {
+        "@cesium/engine": "2.3.0",
+        "nosleep.js": "^0.12.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@cesium/widgets/node_modules/@cesium/engine": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@cesium/engine/-/engine-2.3.0.tgz",
+      "integrity": "sha512-2NoBmDvNkvC+m/uKZNaPoMQZEyuqGGBo/I91rHx5i7UEJwAF/zfUTZ1k2Dx8otu6bDHT/azNo5LWocqP4hpwcw==",
+      "dependencies": {
+        "@tweenjs/tween.js": "^18.6.4",
+        "@zip.js/zip.js": "2.4.x",
+        "autolinker": "^4.0.0",
+        "bitmap-sdf": "^1.0.3",
+        "dompurify": "^3.0.2",
+        "earcut": "^2.2.4",
+        "grapheme-splitter": "^1.0.4",
+        "jsep": "^1.3.8",
+        "kdbush": "^4.0.1",
+        "ktx-parse": "^0.5.0",
+        "lerc": "^2.0.0",
+        "mersenne-twister": "^1.1.0",
+        "meshoptimizer": "^0.18.1",
+        "pako": "^2.0.4",
+        "protobufjs": "^7.1.0",
+        "rbush": "^3.0.1",
+        "topojson-client": "^3.1.0",
+        "urijs": "^1.19.7"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
     "node_modules/@discoveryjs/json-ext": {
       "version": "0.5.7",
       "resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@@ -2000,6 +2069,60 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+    },
+    "node_modules/@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+    },
+    "node_modules/@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+    },
+    "node_modules/@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+    },
+    "node_modules/@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "node_modules/@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+    },
+    "node_modules/@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+    },
+    "node_modules/@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+    },
+    "node_modules/@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+    },
+    "node_modules/@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+    },
     "node_modules/@sideway/address": {
       "version": "4.1.5",
       "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz",
@@ -2060,6 +2183,11 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/@tweenjs/tween.js": {
+      "version": "18.6.4",
+      "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
+      "integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ=="
+    },
     "node_modules/@types/body-parser": {
       "version": "1.19.6",
       "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz",
@@ -2219,7 +2347,6 @@
       "version": "25.3.2",
       "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.3.2.tgz",
       "integrity": "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "undici-types": "~7.18.0"
@@ -2323,6 +2450,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/trusted-types": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+      "optional": true
+    },
     "node_modules/@types/ws": {
       "version": "8.18.1",
       "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
@@ -3160,6 +3293,11 @@
       "dev": true,
       "license": "Apache-2.0"
     },
+    "node_modules/@zip.js/zip.js": {
+      "version": "2.4.26",
+      "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.4.26.tgz",
+      "integrity": "sha512-I9HBO3BHIxEMQmltmHM3iqUW6IHqi3gsL9wTSXvHTRpOrA6q2OxtR58EDSaOGjHhDVJ+wIOAxZyKq2x00AVmqw=="
+    },
     "node_modules/accepts": {
       "version": "1.3.8",
       "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz",
@@ -3473,6 +3611,17 @@
         "node": ">= 4.0.0"
       }
     },
+    "node_modules/autolinker": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-4.1.5.tgz",
+      "integrity": "sha512-vEfYZPmvVOIuE567XBVCsx8SBgOYtjB2+S1iAaJ+HgH+DNjAcrHem2hmAeC9yaNGWayicv4yR+9UaJlkF3pvtw==",
+      "dependencies": {
+        "tslib": "^2.8.1"
+      },
+      "engines": {
+        "pnpm": ">=10.10.0"
+      }
+    },
     "node_modules/autoprefixer": {
       "version": "10.4.27",
       "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.27.tgz",
@@ -3679,6 +3828,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/bitmap-sdf": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.4.tgz",
+      "integrity": "sha512-1G3U4n5JE6RAiALMxu0p1XmeZkTeCwGKykzsLTCqVzfSDaN6S7fKnkIkfejogz+iwqBWc0UYAIKnKHNN7pSfDg=="
+    },
     "node_modules/bl": {
       "version": "4.1.0",
       "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
@@ -3982,6 +4136,22 @@
         "node": ">=4"
       }
     },
+    "node_modules/cesium": {
+      "version": "1.105.1",
+      "resolved": "https://registry.npmjs.org/cesium/-/cesium-1.105.1.tgz",
+      "integrity": "sha512-It3TV+9FX/BWHuRdelKr3aGQ3GFepG1DGtpveXrlNLnXNutlhmmTmKUBnawQQ6Xoj1PeTveWNK9u4gJ5EhRUeA==",
+      "workspaces": [
+        "packages/engine",
+        "packages/widgets"
+      ],
+      "dependencies": {
+        "@cesium/engine": "2.4.0",
+        "@cesium/widgets": "2.2.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
     "node_modules/chalk": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz",
@@ -5203,6 +5373,17 @@
         "url": "https://github.com/fb55/domhandler?sponsor=1"
       }
     },
+    "node_modules/dompurify": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz",
+      "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==",
+      "engines": {
+        "node": ">=20"
+      },
+      "optionalDependencies": {
+        "@types/trusted-types": "^2.0.7"
+      }
+    },
     "node_modules/domutils": {
       "version": "2.8.0",
       "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz",
@@ -5267,6 +5448,11 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/earcut": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
+      "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
+    },
     "node_modules/easy-stack": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz",
@@ -6698,6 +6884,11 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/grapheme-splitter": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
+    },
     "node_modules/gzip-size": {
       "version": "6.0.0",
       "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz",
@@ -7477,6 +7668,14 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/jsep": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
+      "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
+      "engines": {
+        "node": ">= 10.16.0"
+      }
+    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
@@ -7551,6 +7750,11 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/kdbush": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
+      "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="
+    },
     "node_modules/keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
@@ -7581,6 +7785,11 @@
         "node": ">= 8"
       }
     },
+    "node_modules/ktx-parse": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.5.0.tgz",
+      "integrity": "sha512-5IZrv5s1byUeDTIee1jjJQBiD5LPDB0w9pJJ0oT9BCKKJf16Tuj123vm1Ps0GOHSHmeWPgKM0zuViCVuTRpqaA=="
+    },
     "node_modules/launch-editor": {
       "version": "2.13.1",
       "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.13.1.tgz",
@@ -7602,6 +7811,11 @@
         "launch-editor": "^2.13.1"
       }
     },
+    "node_modules/lerc": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lerc/-/lerc-2.0.0.tgz",
+      "integrity": "sha512-7qo1Mq8ZNmaR4USHHm615nEW2lPeeWJ3bTyoqFbd35DLx0LUH7C6ptt5FDCTAlbIzs3+WKrk5SkJvw8AFDE2hg=="
+    },
     "node_modules/levn": {
       "version": "0.4.1",
       "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
@@ -7911,6 +8125,11 @@
         "node": ">=4"
       }
     },
+    "node_modules/long": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+      "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
+    },
     "node_modules/lower-case": {
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz",
@@ -8033,6 +8252,16 @@
         "node": ">= 8"
       }
     },
+    "node_modules/mersenne-twister": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mersenne-twister/-/mersenne-twister-1.1.0.tgz",
+      "integrity": "sha512-mUYWsMKNrm4lfygPkL3OfGzOPTR2DBlTkBNHM//F6hGp8cLThY897crAlk3/Jo17LEOOjQUrNAx6DvgO77QJkA=="
+    },
+    "node_modules/meshoptimizer": {
+      "version": "0.18.1",
+      "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
+      "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw=="
+    },
     "node_modules/methods": {
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz",
@@ -8423,6 +8652,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/nosleep.js": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/nosleep.js/-/nosleep.js-0.12.0.tgz",
+      "integrity": "sha512-9d1HbpKLh3sdWlhXMhU6MMH+wQzKkrgfRkYV0EBdvt99YJfj0ilCJrWRDYG2130Tm4GXbEoTCx5b34JSaP+HhA=="
+    },
     "node_modules/npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -8709,6 +8943,11 @@
         "node": ">=6"
       }
     },
+    "node_modules/pako": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+      "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+    },
     "node_modules/param-case": {
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz",
@@ -9645,6 +9884,29 @@
         "node": ">=4"
       }
     },
+    "node_modules/protobufjs": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
+      "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/node": ">=13.7.0",
+        "long": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -9740,6 +10002,11 @@
       ],
       "license": "MIT"
     },
+    "node_modules/quickselect": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
+      "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
+    },
     "node_modules/randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
@@ -9776,6 +10043,14 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/rbush": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
+      "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
+      "dependencies": {
+        "quickselect": "^2.0.0"
+      }
+    },
     "node_modules/read-pkg": {
       "version": "5.2.0",
       "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -11113,6 +11388,24 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/topojson-client": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz",
+      "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==",
+      "dependencies": {
+        "commander": "2"
+      },
+      "bin": {
+        "topo2geo": "bin/topo2geo",
+        "topomerge": "bin/topomerge",
+        "topoquantize": "bin/topoquantize"
+      }
+    },
+    "node_modules/topojson-client/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+    },
     "node_modules/totalist": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz",
@@ -11134,7 +11427,6 @@
       "version": "2.8.1",
       "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
       "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
-      "dev": true,
       "license": "0BSD"
     },
     "node_modules/type-check": {
@@ -11178,7 +11470,6 @@
       "version": "7.18.2",
       "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz",
       "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/unicode-canonical-property-names-ecmascript": {
@@ -11286,6 +11577,11 @@
         "punycode": "^2.1.0"
       }
     },
+    "node_modules/urijs": {
+      "version": "1.19.11",
+      "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
+      "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",

+ 1 - 0
package.json

@@ -9,6 +9,7 @@
   },
   "dependencies": {
     "axios": "^1.13.5",
+    "cesium": "^1.105.1",
     "china-map-geojson": "^1.0.4",
     "core-js": "^3.8.3",
     "echarts": "^5.6.0",

BIN
public/beijing-satellite.jpg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
public/china.json


+ 41 - 0
public/index.html

@@ -5,13 +5,54 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <link rel="preconnect" href="https://services.arcgisonline.com">
+    <link rel="preconnect" href="https://cdnjs.cloudflare.com">
     <title><%= htmlWebpackPlugin.options.title %></title>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/cesium/1.105.1/Cesium.js"></script>
+    <link href="https://cdnjs.cloudflare.com/ajax/libs/cesium/1.105.1/Widgets/widgets.min.css" rel="stylesheet">
   </head>
   <body>
     <noscript>
       <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
     </noscript>
     <div id="app"></div>
+    <!-- 瓦片预加载:页面打开即执行,早于 Vue 初始化 -->
+    <div id="cesium-preload" style="position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;z-index:-1;"></div>
+    <script>
+      (function() {
+        // 预加载宏观和中观瓦片(微观用本地静态图,不需要预加载)
+        var views = [
+          {destination:Cesium.Cartesian3.fromDegrees(-15,35.86,45000000)},
+          {destination:Cesium.Cartesian3.fromDegrees(104.19,35.86,14000000)}
+        ];
+        var idx = 0;
+        var viewer = new Cesium.Viewer('cesium-preload', {
+          animation:false, timeline:false, baseLayerPicker:false, geocoder:false,
+          homeButton:false, sceneModePicker:false, navigationHelpButton:false, infoBox:false,
+          fullscreenButton:false, selectionIndicator:false,
+          imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
+            url:'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
+          })
+        });
+        viewer.cesiumWidget.creditContainer.style.display='none';
+        viewer.camera.setView(views[0]);
+        function check(){
+          if(viewer.isDestroyed())return;
+          if(viewer.scene.globe.tilesLoaded){
+            idx++;
+            if(idx < views.length){
+              viewer.camera.setView(views[idx]);
+              requestAnimationFrame(check);
+            }else{
+              viewer.destroy();
+              var el=document.getElementById('cesium-preload');
+              if(el)el.remove();
+            }
+          }else{requestAnimationFrame(check);}
+        }
+        requestAnimationFrame(check);
+      })();
+    </script>
     <!-- built files will be auto injected -->
   </body>
 </html>

+ 433 - 0
src/components/CesiumTransition.vue

@@ -0,0 +1,433 @@
+<template>
+  <div class="cesium-transition-wrapper">
+    <div ref="cesiumContainer" class="cesium-container"></div>
+    <div class="ui-layer">
+      <div
+        ref="poiLabel"
+        class="html-label"
+        style="color: #00ffff; font-size: 20px; opacity: 0; display: none;"
+      >
+        鲁谷西街与政达路交叉口
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+const Cesium = window.Cesium;
+
+function createOuterRingTexture(color) {
+  const size = 1024, cx = size / 2, cy = size / 2, radius = size / 2 - 20;
+  const lineWidth = 16, baseColorStr = color.toCssColorString();
+  const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d');
+  const solidCanvas = document.createElement('canvas'); solidCanvas.width = size; solidCanvas.height = size; const solidCtx = solidCanvas.getContext('2d'); solidCtx.lineWidth = lineWidth; solidCtx.strokeStyle = baseColorStr; solidCtx.shadowBlur = 10; solidCtx.shadowColor = baseColorStr; solidCtx.beginPath(); solidCtx.arc(cx, cy, radius, 0, Math.PI * 2); solidCtx.stroke();
+  const dashedCanvas = document.createElement('canvas'); dashedCanvas.width = size; dashedCanvas.height = size; const dashedCtx = dashedCanvas.getContext('2d'); dashedCtx.lineWidth = lineWidth; dashedCtx.strokeStyle = baseColorStr; dashedCtx.setLineDash([12, 12]); dashedCtx.shadowBlur = 10; dashedCtx.shadowColor = baseColorStr; dashedCtx.beginPath(); dashedCtx.arc(cx, cy, radius, 0, Math.PI * 2); dashedCtx.stroke();
+  const solidMask = ctx.createConicGradient(0, cx, cy); solidMask.addColorStop(0.0, 'rgba(0,0,0,0)'); solidMask.addColorStop(0.10, 'rgba(0,0,0,0)'); solidMask.addColorStop(0.18, 'rgba(0,0,0,1)'); solidMask.addColorStop(0.48, 'rgba(0,0,0,1)'); solidMask.addColorStop(0.50, 'rgba(0,0,0,0)'); solidMask.addColorStop(0.60, 'rgba(0,0,0,0)'); solidMask.addColorStop(0.68, 'rgba(0,0,0,1)'); solidMask.addColorStop(0.98, 'rgba(0,0,0,1)'); solidMask.addColorStop(1.0, 'rgba(0,0,0,0)');
+  const dashedMask = ctx.createConicGradient(0, cx, cy); dashedMask.addColorStop(0.0, 'rgba(0,0,0,0)'); dashedMask.addColorStop(0.02, 'rgba(0,0,0,1)'); dashedMask.addColorStop(0.10, 'rgba(0,0,0,1)'); dashedMask.addColorStop(0.18, 'rgba(0,0,0,0)'); dashedMask.addColorStop(0.50, 'rgba(0,0,0,0)'); dashedMask.addColorStop(0.52, 'rgba(0,0,0,1)'); dashedMask.addColorStop(0.60, 'rgba(0,0,0,1)'); dashedMask.addColorStop(0.68, 'rgba(0,0,0,0)'); dashedMask.addColorStop(1.0, 'rgba(0,0,0,0)');
+  ctx.drawImage(solidCanvas, 0, 0); ctx.globalCompositeOperation = 'destination-in'; ctx.fillStyle = solidMask; ctx.fillRect(0, 0, size, size);
+  const tempCanvas = document.createElement('canvas'); tempCanvas.width = size; tempCanvas.height = size; const tempCtx = tempCanvas.getContext('2d'); tempCtx.drawImage(dashedCanvas, 0, 0); tempCtx.globalCompositeOperation = 'destination-in'; tempCtx.fillStyle = dashedMask; tempCtx.fillRect(0, 0, size, size);
+  ctx.globalCompositeOperation = 'lighter'; ctx.drawImage(tempCanvas, 0, 0);
+  ctx.globalCompositeOperation = 'destination-over'; ctx.shadowBlur = 40; ctx.shadowColor = color.withAlpha(0.3).toCssColorString(); ctx.strokeStyle = color.withAlpha(0.08).toCssColorString(); ctx.lineWidth = 20; ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.stroke();
+  return canvas.toDataURL();
+}
+
+function createScannerTexture(color) {
+  const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512; const ctx = canvas.getContext('2d');
+  const sharpColor = color.withAlpha(1.0).toCssColorString(); ctx.lineWidth = 3; ctx.strokeStyle = sharpColor; ctx.shadowBlur = 20; ctx.shadowColor = sharpColor; ctx.beginPath(); ctx.moveTo(256, 256); ctx.lineTo(506, 256); ctx.stroke(); return canvas.toDataURL();
+}
+
+function createLightBeamTexture(color) {
+  const canvas = document.createElement('canvas'); canvas.width = 64; canvas.height = 256; const ctx = canvas.getContext('2d');
+  const gradient = ctx.createLinearGradient(0, 256, 0, 0);
+  gradient.addColorStop(0, color.withAlpha(0.6).toCssColorString()); gradient.addColorStop(0.4, color.withAlpha(0.1).toCssColorString()); gradient.addColorStop(1, color.withAlpha(0.0).toCssColorString());
+  ctx.fillStyle = gradient; ctx.fillRect(0, 0, 64, 256); return canvas.toDataURL();
+}
+
+const TWO_PI = Math.PI * 2;
+
+export default {
+  name: 'CesiumTransition',
+  data() {
+    return {
+      isAnimating: false
+    };
+  },
+  mounted() {
+    this._viewer = null;
+    this._preRenderListener = null;
+    this._chinaDataSource = null;
+    this._chinaEffectsSource = null;
+    this._microEffectsSource = null;
+    this._macroAlpha = 0.0;
+    this._destroyed = false;
+    this._pendingTimers = [];
+
+    this._coords = { start: [-15.0, 35.86, 45000000], china: [104.19, 35.86, 14000000] };
+    this._poi = { lon: 116.22405, lat: 39.90515, radarRadius: 200 };
+
+    this.$nextTick(() => {
+      if (!this.$refs.cesiumContainer) return;
+      this.initCesium();
+      this.waitForGlobeReady().then(() => {
+        if (!this._destroyed) this.startTransition();
+      });
+    });
+  },
+  beforeDestroy() {
+    this._destroyed = true;
+    this._pendingTimers.forEach(id => clearTimeout(id));
+    this._pendingTimers = [];
+    if (this._viewer) {
+      if (this._preRenderListener) {
+        this._viewer.scene.preRender.removeEventListener(this._preRenderListener);
+      }
+      this._viewer.destroy();
+      this._viewer = null;
+    }
+  },
+  methods: {
+    waitForGlobeReady() {
+      return new Promise(resolve => {
+        const check = () => {
+          if (this._destroyed) { resolve(); return; }
+          if (this._viewer && this._viewer.scene.globe.tilesLoaded) {
+            resolve();
+          } else {
+            requestAnimationFrame(check);
+          }
+        };
+        requestAnimationFrame(check);
+      });
+    },
+
+    initCesium() {
+      this._baseGold = Cesium.Color.GOLD;
+      this._baseWhite = Cesium.Color.WHITE;
+      this._baseBlack = Cesium.Color.BLACK;
+      this._dynamicGold = this._baseGold.withAlpha(0);
+      this._dynamicWhite = this._baseWhite.withAlpha(0);
+      this._dynamicBlack = this._baseBlack.withAlpha(0);
+      this._poiThemeColor = Cesium.Color.DEEPSKYBLUE;
+
+      this._majorRoads = [
+        { name: "阜石路-阜成路快速路", width: 12, glowPower: 0.3, color: Cesium.Color.fromCssColorString('#00ffff'), path: [[116.18, 39.93], [116.22, 39.93], [116.26, 39.928], [116.30, 39.925]] },
+        { name: "石景山路-复兴路", width: 12, glowPower: 0.3, color: Cesium.Color.fromCssColorString('#ff3333'), path: [[116.18, 39.907], [116.224, 39.907], [116.25, 39.906], [116.30, 39.905]] },
+        { name: "莲石东路快速路", width: 12, glowPower: 0.3, color: Cesium.Color.fromCssColorString('#00ff00'), path: [[116.18, 39.89], [116.22, 39.888], [116.26, 39.885], [116.30, 39.88]] },
+        { name: "西五环路", width: 10, glowPower: 0.25, color: Cesium.Color.fromCssColorString('#ffaa00'), path: [[116.205, 39.95], [116.203, 39.92], [116.202, 39.89], [116.20, 39.86]] },
+        { name: "玉泉路", width: 10, glowPower: 0.25, color: Cesium.Color.fromCssColorString('#cc00ff'), path: [[116.25, 39.94], [116.25, 39.907], [116.248, 39.88], [116.245, 39.86]] },
+        { name: "西四环路", width: 10, glowPower: 0.25, color: Cesium.Color.fromCssColorString('#ffff00'), path: [[116.285, 39.94], [116.283, 39.91], [116.28, 39.88], [116.278, 39.85]] }
+      ];
+
+      this._viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
+        animation: false, timeline: false, baseLayerPicker: false, geocoder: false,
+        homeButton: false, sceneModePicker: false, navigationHelpButton: false, infoBox: false,
+        fullscreenButton: false, selectionIndicator: false,
+        imageryProvider: new Cesium.ArcGisMapServerImageryProvider({ url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer' })
+      });
+
+      const cam = this._viewer.scene.screenSpaceCameraController;
+      cam.enableRotate = false;
+      cam.enableTranslate = false;
+      cam.enableZoom = false;
+      cam.enableTilt = false;
+      cam.enableLook = false;
+
+      this._viewer.cesiumWidget.creditContainer.style.display = "none";
+      this._viewer.scene.globe.enableLighting = true;
+      this._viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date('2024-01-01T23:30:00Z'));
+      this._viewer.clock.shouldAnimate = false;
+
+      const baseLayer = this._viewer.imageryLayers.get(0);
+      if (baseLayer) { baseLayer.brightness = 0.45; baseLayer.contrast = 1.3; }
+
+      // 微观区域高清卫星底图(本地静态图,秒加载,替代远程瓦片)
+      this._viewer.imageryLayers.addImageryProvider(
+        new Cesium.SingleTileImageryProvider({
+          url: './beijing-satellite.jpg',
+          rectangle: Cesium.Rectangle.fromDegrees(116.10, 39.80, 116.38, 39.98)
+        })
+      );
+
+      this._viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(...this._coords.start) });
+
+      this.buildMacroLayer();
+      this.buildMicroLayer();
+
+      this._preRenderListener = this._viewer.scene.preRender.addEventListener(() => {
+        if (!this._microEffectsSource || !this._microEffectsSource.show || !this.$refs.poiLabel) return;
+        if (this.$refs.poiLabel.style.opacity === "0") return;
+        const pos3D = Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat);
+        const screenPos = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this._viewer.scene, pos3D);
+        if (screenPos && screenPos.y > 0) {
+          this.$refs.poiLabel.style.display = 'flex';
+          this.$refs.poiLabel.style.left = screenPos.x + 'px';
+          this.$refs.poiLabel.style.top = (screenPos.y - 150) + 'px';
+        } else {
+          this.$refs.poiLabel.style.display = 'none';
+        }
+      });
+    },
+
+    buildMacroLayer() {
+      this._chinaEffectsSource = new Cesium.CustomDataSource('chinaEffects');
+      this._viewer.dataSources.add(this._chinaEffectsSource);
+      this._chinaEffectsSource.show = false;
+
+      Cesium.GeoJsonDataSource.load('./china.json').then(ds => {
+        if (this._destroyed) return;
+        this._viewer.dataSources.add(ds);
+        this._chinaDataSource = ds;
+        this._chinaDataSource.show = false;
+
+        const entities = ds.entities.values;
+        const provinceMainLand = {};
+
+        for (let i = 0; i < entities.length; i++) {
+          const entity = entities[i];
+          if (entity.polygon) {
+            entity.polygon.fill = false;
+            entity.polygon.outline = false;
+            const positions = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
+            entity.polyline = new Cesium.PolylineGraphics({
+              positions, width: 2,
+              material: new Cesium.PolylineGlowMaterialProperty({
+                glowPower: 0.05,
+                color: new Cesium.CallbackProperty(() => this._dynamicGold, false)
+              })
+            });
+            if (entity.name === '北京市') {
+              const pointCount = positions.length;
+              if (!provinceMainLand[entity.name] || pointCount > provinceMainLand[entity.name].pointCount) {
+                provinceMainLand[entity.name] = { pointCount, center: Cesium.BoundingSphere.fromPoints(positions).center };
+              }
+            }
+          }
+        }
+
+        // 实体处理完毕后再添加到 viewer
+        this._viewer.dataSources.add(ds);
+
+        for (const name in provinceMainLand) {
+          const mainCenter = provinceMainLand[name].center;
+          this._chinaEffectsSource.entities.add({
+            position: mainCenter,
+            label: {
+              text: name, font: 'bold 28px Microsoft YaHei',
+              fillColor: new Cesium.CallbackProperty(() => this._dynamicWhite, false),
+              outlineColor: new Cesium.CallbackProperty(() => this._dynamicBlack, false),
+              outlineWidth: 3, style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+              heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
+              pixelOffset: new Cesium.Cartesian2(0, -10),
+              distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 20000000)
+            }
+          });
+          for (let i = 0; i < 3; i++) {
+            const offset = (4000 / 3) * i, startTime = Date.now() + offset;
+            this._chinaEffectsSource.entities.add({
+              position: mainCenter,
+              ellipse: {
+                semiMinorAxis: new Cesium.CallbackProperty(() => 350000 * (((Date.now() - startTime) % 4000) / 4000), false),
+                semiMajorAxis: new Cesium.CallbackProperty(() => 350000 * (((Date.now() - startTime) % 4000) / 4000), false),
+                material: new Cesium.ColorMaterialProperty(new Cesium.CallbackProperty(() => {
+                  const t = ((Date.now() - startTime) % 4000) / 4000;
+                  return Cesium.Color.DEEPSKYBLUE.withAlpha((1.0 - t) * 0.8 * this._macroAlpha);
+                }, false)), height: 5000 + i * 100
+              }
+            });
+          }
+        }
+      });
+    },
+
+    buildMicroLayer() {
+      this._microEffectsSource = new Cesium.CustomDataSource('microEffects');
+      this._viewer.dataSources.add(this._microEffectsSource);
+      this._microEffectsSource.show = false;
+
+      const pos3D = Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat);
+      const slowRingImg = createOuterRingTexture(this._poiThemeColor);
+      const fastScannerImg = createScannerTexture(this._poiThemeColor);
+      const beamImg = createLightBeamTexture(this._poiThemeColor);
+
+      this._microEffectsSource.entities.add({ position: pos3D, ellipse: { semiMinorAxis: this._poi.radarRadius, semiMajorAxis: this._poi.radarRadius, material: new Cesium.ImageMaterialProperty({ image: slowRingImg, transparent: true }), stRotation: new Cesium.CallbackProperty(() => (Date.now() / 5000.0) % TWO_PI, false), height: 20 } });
+      this._microEffectsSource.entities.add({ position: pos3D, ellipse: { semiMinorAxis: this._poi.radarRadius, semiMajorAxis: this._poi.radarRadius, material: new Cesium.ImageMaterialProperty({ image: fastScannerImg, transparent: true }), stRotation: new Cesium.CallbackProperty(() => (Date.now() / 300.0) % TWO_PI, false), height: 21 } });
+      this._microEffectsSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat, 2000.0), cylinder: { length: 4000, topRadius: 20, bottomRadius: 200, material: new Cesium.ImageMaterialProperty({ image: beamImg, transparent: true }) } });
+      this._microEffectsSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat, 50), point: { pixelSize: 15, color: Cesium.Color.WHITE, outlineColor: this._poiThemeColor, outlineWidth: 3 } });
+    },
+
+    fadeMacroLayer(startAlpha, endAlpha, durationMs) {
+      return new Promise(resolve => {
+        if (startAlpha < endAlpha && this._chinaDataSource) {
+          this._chinaDataSource.show = true;
+          this._chinaEffectsSource.show = true;
+        }
+        const startTime = Date.now();
+        const animateFade = () => {
+          if (this._destroyed) { resolve(); return; }
+          let p = (Date.now() - startTime) / durationMs; if (p >= 1.0) p = 1.0;
+          this._macroAlpha = startAlpha + (endAlpha - startAlpha) * p;
+          this._dynamicGold = this._baseGold.withAlpha(this._macroAlpha);
+          this._dynamicWhite = this._baseWhite.withAlpha(this._macroAlpha);
+          this._dynamicBlack = this._baseBlack.withAlpha(this._macroAlpha);
+          if (p < 1.0) {
+            requestAnimationFrame(animateFade);
+          } else {
+            if (endAlpha === 0 && this._chinaDataSource) {
+              this._chinaDataSource.show = false;
+              this._chinaEffectsSource.show = false;
+            }
+            resolve();
+          }
+        };
+        requestAnimationFrame(animateFade);
+      });
+    },
+
+    animatePathGrowing(roadObj, delayTime) {
+      return new Promise(resolve => {
+        const timerId = setTimeout(() => {
+          if (this._destroyed) { resolve(); return; }
+          const positions = roadObj.path.map(p => Cesium.Cartesian3.fromDegrees(p[0], p[1]));
+          const distances = [0]; let totalLength = 0;
+          for (let i = 1; i < positions.length; i++) {
+            totalLength += Cesium.Cartesian3.distance(positions[i - 1], positions[i]);
+            distances.push(totalLength);
+          }
+          let progress = 0.0, isFinished = false;
+
+          this._microEffectsSource.entities.add({
+            polyline: {
+              positions: new Cesium.CallbackProperty(function () {
+                if (isFinished) return positions;
+                progress += 0.025;
+                if (progress >= 1.0) { isFinished = true; resolve(); return positions; }
+                const targetDist = progress * totalLength;
+                const pts = [];
+                for (let i = 1; i < positions.length; i++) {
+                  pts.push(positions[i - 1]);
+                  if (targetDist <= distances[i]) {
+                    const seg = (targetDist - distances[i - 1]) / (distances[i] - distances[i - 1]);
+                    const p = new Cesium.Cartesian3();
+                    Cesium.Cartesian3.lerp(positions[i - 1], positions[i], seg, p);
+                    pts.push(p); break;
+                  }
+                }
+                return pts;
+              }, false),
+              width: roadObj.width,
+              material: new Cesium.PolylineGlowMaterialProperty({ glowPower: roadObj.glowPower, color: roadObj.color })
+            }
+          });
+        }, delayTime);
+        this._pendingTimers.push(timerId);
+      });
+    },
+
+    _safeDelay(ms) {
+      return new Promise(resolve => {
+        const id = setTimeout(resolve, ms);
+        this._pendingTimers.push(id);
+      });
+    },
+
+    async startTransition() {
+      if (this.isAnimating) return;
+      this.isAnimating = true;
+
+      // 阶段1: 飞向中国 (2s),飞行0.8s后开始淡入边界
+      this._viewer.camera.flyTo({
+        destination: Cesium.Cartesian3.fromDegrees(...this._coords.china), duration: 2
+      });
+      const fadeInId = setTimeout(() => {
+        if (!this._destroyed) this.fadeMacroLayer(0.0, 1.0, 1000);
+      }, 800);
+      this._pendingTimers.push(fadeInId);
+
+      await this._safeDelay(3000);
+      if (this._destroyed) return;
+
+      // 阶段2: 俯冲到微观 (2s),同时淡出边界
+      const targetSphere = Cesium.BoundingSphere.fromPoints([Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat)]);
+      this._viewer.camera.flyToBoundingSphere(targetSphere, {
+        offset: new Cesium.HeadingPitchRange(0.0, Cesium.Math.toRadians(-45), 10000.0),
+        duration: 2, easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT
+      });
+      this.fadeMacroLayer(1.0, 0.0, 800);
+
+      // 阶段3: 显示微观雷达 + POI标签
+      await this._safeDelay(1500);
+      if (this._destroyed) return;
+      this._microEffectsSource.show = true;
+      if (this.$refs.poiLabel) this.$refs.poiLabel.style.opacity = 1;
+
+      // 阶段4: 路网生长 (每条间隔150ms),全部画完立即结束
+      await this._safeDelay(300);
+      if (this._destroyed) return;
+      const roadPromises = this._majorRoads.map((road, i) => this.animatePathGrowing(road, i * 150));
+      await Promise.all(roadPromises);
+      if (this._destroyed) return;
+      await this._safeDelay(1000);
+      if (this._destroyed) return;
+      this.$emit('complete');
+    }
+  }
+};
+</script>
+
+<style scoped>
+.cesium-transition-wrapper {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 9999;
+  background: #020813;
+  overflow: hidden;
+  font-family: "Microsoft YaHei", sans-serif;
+}
+
+.cesium-container {
+  width: 100%;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+}
+
+.ui-layer {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+}
+
+.html-label {
+  position: absolute;
+  padding: 4px 10px;
+  border-radius: 2px;
+  pointer-events: none;
+  transition: opacity 0.5s;
+  transform: translate(-50%, -100%);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  white-space: nowrap;
+  font-weight: bold;
+  z-index: 5;
+  border: 1px solid currentColor;
+  background: rgba(0, 20, 30, 0.7);
+  box-shadow: inset 0 0 10px currentColor;
+  text-shadow: 0 0 5px #000;
+}
+
+.html-label::after {
+  content: '';
+  width: 2px;
+  height: 35px;
+  background: currentColor;
+  margin-top: 5px;
+  box-shadow: 0 0 5px currentColor;
+}
+</style>

+ 2 - 2
src/router/index.js

@@ -2,9 +2,9 @@ import Vue from "vue";
 import Router from "vue-router";
 
 import Login from "@/views/Login.vue";
-import Transition from "@/views/Transition.vue";
 import Home from "@/views/Home.vue";
 import Main from "@/views/Main.vue";
+import TransitionPage from "@/views/TransitionPage.vue";
 
 Vue.use(Router);
 
@@ -14,7 +14,7 @@ export default new Router({
     { path: "/", redirect: "/login" },
     { path: "/login", component: Login },
     { path: "/main", component: Main },
-    { path: "/transition", component: Transition },
+    { path: "/transition", component: TransitionPage },
     { path: "/home", component: Home }
   ]
 });

+ 0 - 30
src/views/Transition.vue

@@ -1,30 +0,0 @@
-<template>
-  <div class="page">
-    <GlobeTransition
-      :durationMs="5000"
-      :target="{ lon: 116.657, lat: 39.909, name: '北京·通州' }"
-      @done="goHome"
-    />
-  </div>
-</template>
-
-<script>
-import GlobeTransition from "@/components/GlobeTransition.vue";
-
-export default {
-  name: "Transition",
-  components: { GlobeTransition },
-  methods: {
-    goHome() {
-      this.$router.replace("/main");
-    }
-  }
-};
-</script>
-
-<style scoped>
-.page{
-  width:100vw; height:100vh;
-  background: radial-gradient(circle at 50% 50%, rgba(25,80,180,0.22), rgba(5,10,28,1) 65%);
-}
-</style>

+ 58 - 0
src/views/TransitionPage.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="page">
+    <transition name="fade">
+      <CesiumTransition 
+        v-if="isTransitioning" 
+        :autoStart="true" 
+        @complete="onTransitionComplete" 
+      />
+    </transition>
+  </div>
+</template>
+
+<script>
+import CesiumTransition from '@/components/CesiumTransition.vue';
+
+export default {
+  name: "TransitionPage",
+  components: {
+    CesiumTransition
+  },
+  data() {
+    return {
+      isTransitioning: true // 控制过渡动画状态
+    }
+  },
+  mounted() {
+    // 页面加载完成后,自动触发过渡动画(延迟500ms确保渲染就绪)
+    setTimeout(() => {
+      if (this.$refs.transitionComponent) {
+        this.$refs.transitionComponent.startTransition();
+      }
+    }, 500);
+  },
+  methods: {
+    onTransitionComplete() {
+      // 动画播放完毕,隐藏组件以释放显存,并显示主界面
+      this.isTransitioning = false;
+      console.log("进入系统!");
+      this.$router.replace("/main");
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+/* .page{
+  width:100vw; height:100vh;
+  background: radial-gradient(circle at 50% 50%, rgba(25,80,180,0.22), rgba(5,10,28,1) 65%);
+} */
+/* 增加一个淡出过渡让消失时不突兀 */
+.fade-enter-active, .fade-leave-active {
+  transition: opacity 1.5s ease;
+}
+.fade-enter, .fade-leave-to {
+  opacity: 0;
+}
+</style>

+ 17 - 1
vue.config.js

@@ -1,6 +1,22 @@
 const { defineConfig } = require('@vue/cli-service')
 module.exports = defineConfig({
   transpileDependencies: true,
-  lintOnSave: false
+  lintOnSave: false,
+  // 【核心修复代码】:配置 Webpack fallback
+  configureWebpack: {
+    externals: {
+      cesium: 'Cesium'  // import 'cesium' 时直接使用全局 window.Cesium
+    },
+    resolve: {
+      fallback: {
+        "https": false,
+        "http": false,
+        "zlib": false,
+        "url": false,
+        "assert": false,
+        "stream": false
+      }
+    }
+  }
 })