修改地图

This commit is contained in:
lixiaobang 2026-03-20 10:56:45 +08:00
parent 15fe3b1039
commit 176182da9b
13 changed files with 1010 additions and 6 deletions

280
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "urbantraffic-web",
"version": "0.0.0",
"dependencies": {
"axios": "^1.13.6",
"echarts": "^6.0.0",
"element-plus": "^2.13.5",
"vue": "^3.5.29",
@ -2055,6 +2056,23 @@
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.6",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.6.tgz",
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/baseline-browser-mapping": {
"version": "2.10.7",
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz",
@ -2127,6 +2145,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001778",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz",
@ -2163,6 +2194,18 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/confbox": {
"version": "0.2.4",
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz",
@ -2249,6 +2292,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
@ -2260,6 +2312,20 @@
"node": ">=8"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/echarts": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-6.0.0.tgz",
@ -2324,6 +2390,51 @@
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.27.4",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.4.tgz",
@ -2405,6 +2516,42 @@
}
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
@ -2420,6 +2567,15 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -2430,6 +2586,94 @@
"node": ">=6.9.0"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
@ -2631,12 +2875,42 @@
"url": "https://github.com/sponsors/sxzz"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
"license": "MIT"
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mlly": {
"version": "1.8.1",
"resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.1.tgz",
@ -2823,6 +3097,12 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/quansync": {
"version": "0.2.11",
"resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz",

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -2,13 +2,271 @@
<div class="map-page">
<!-- 地图容器一定要有宽高 -->
<div id="baiduMap" class="map-container"></div>
<!-- 示范区实时交通状态指标弹窗仅在点击事故前时显示 -->
<div class="actual-raffic-conditions" v-show="currentStep === 1">
<div class="actual-traffic-conditions-inner" :style="{ backgroundImage: `url('${ztzbBgUrl}')` }">
<div class="actual-traffic-conditions-header">示范区实时交通状态指标</div>
<div class="actual-traffic-conditions-body">
<div class="actual-traffic-conditions-row">
<img class="actual-traffic-conditions-icon" :src="csIconUrl" alt="" />
<span class="actual-traffic-conditions-label">区域平均车速</span>
<span class="actual-traffic-conditions-value actual-traffic-conditions-value--green">40
km/h</span>
<span
class="actual-traffic-conditions-status actual-traffic-conditions-status--green">(较常态下降)</span>
</div>
<div class="actual-traffic-conditions-row">
<img class="actual-traffic-conditions-icon" :src="zsIconUrl" alt="" />
<span class="actual-traffic-conditions-label">区域拥堵指数</span>
<span class="actual-traffic-conditions-value actual-traffic-conditions-value--red">6.8</span>
<span
class="actual-traffic-conditions-status actual-traffic-conditions-status--red">(中度拥堵)</span>
</div>
<div class="actual-traffic-conditions-row">
<img class="actual-traffic-conditions-icon" :src="ywIconUrl" alt="" />
<span class="actual-traffic-conditions-label">核心区平均延误</span>
<span class="actual-traffic-conditions-value actual-traffic-conditions-value--red">+18
min</span>
</div>
</div>
</div>
</div>
<!-- 事故进度 -->
<div class="accident-progress">
<div class="accident-progress-inner">
<div class="accident-progress-labels">
<div class="accident-progress-label" :class="{ active: currentStep >= 1 }" @click="goToStep(1)">事故前
</div>
<div class="accident-progress-label" :class="{ active: currentStep >= 2 }" @click="goToStep(2)">事故中
</div>
<div class="accident-progress-label" :class="{ active: currentStep >= 3 }" @click="goToStep(3)">恢复中
</div>
</div>
<div class="accident-progress-bar-wrap">
<div class="accident-progress-line accident-progress-line--bg"></div>
<div class="accident-progress-line accident-progress-line--fill"
:style="{ width: progressFillWidth }"></div>
<div class="accident-progress-nodes">
<span class="accident-progress-node" :class="{ active: currentStep >= 1 }"></span>
<span class="accident-progress-node" :class="{ active: currentStep >= 2 }"></span>
<span class="accident-progress-node" :class="{ active: currentStep >= 3 }"></span>
</div>
</div>
</div>
</div>
<!-- 恢复中信息模块仅在恢复中时显示 -->
<div class="recovery-info-module" v-show="currentStep === 3">
<div class="recovery-info-inner">
<div class="recovery-info-header">
<span class="recovery-info-chevrons">&gt;&gt;&gt;</span>
<h3 class="recovery-info-title">示范区事故应急综合概览</h3>
</div>
<div class="recovery-info-details">
<div class="recovery-info-row">
<!-- <span class="recovery-info-icon recovery-info-icon--red">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor"
d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z" />
</svg>
</span> -->
<img class="recovery-info-icon" src="@/assets/images/map/hfz-sg.png" alt="" />
<span class="recovery-info-label">事故:</span>
<span class="recovery-info-value recovery-info-value--red">#A-1024追尾 (已确认)</span>
</div>
<div class="recovery-info-row">
<!-- <span class="recovery-info-icon recovery-info-icon--red">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor"
d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" />
</svg>
</span> -->
<img class="recovery-info-icon" src="@/assets/images/map/hfz-yx.png" alt="" />
<span class="recovery-info-label">影响:</span>
<span class="recovery-info-value recovery-info-value--red">半径1.5km3车道受阻</span>
</div>
<div class="recovery-info-row">
<!-- <span class="recovery-info-icon recovery-info-icon--green">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor"
d="M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66.19-.34.05-.08.07-.12C8.48 10.94 10.42 7.54 13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15C12.96 17.55 11 21 11 21z" />
</svg>
</span> -->
<img class="recovery-info-icon" src="@/assets/images/map/hfz-xy.png" alt="" />
<span class="recovery-info-label">响应:</span>
<span class="recovery-info-value recovery-info-value--green">交警/救护已到位</span>
</div>
<div class="recovery-info-row">
<!-- <span class="recovery-info-icon recovery-info-icon--blue">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor"
d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3zm-1-9v2h2V6h-2zm0 10v2h2v-2h-2zm-8 0h2v-2H3v2zm0-10h2V4H3v2zm8 7v2h2v-2h-2zm8-7v2h2V4h-2zm0 10h2v-2h-2v2z" />
</svg>
</span> -->
<img class="recovery-info-icon" src="@/assets/images/map/hfz-dc.png" alt="" />
<span class="recovery-info-label">对策:</span>
<span class="recovery-info-value recovery-info-value--blue">信号绿波+2条绕行方案启用</span>
</div>
</div>
<div class="recovery-info-chart">
<div class="recovery-info-chart-title">核心区流量/速度趋势(过去1小时)</div>
<!-- <div class="recovery-info-chart-legend">
<span class="legend-item"><i class="legend-line legend-line--speed"></i>核心区速度</span>
<span class="legend-item"><i class="legend-line legend-line--flow"></i>绕行流量</span>
</div> -->
<div class="recovery-info-chart-stage">
<VChart class="recovery-chart" :option="recoveryChartOption" autoresize />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
import { onMounted, onBeforeUnmount, ref, computed, watch } from 'vue'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { LineChart } from 'echarts/charts'
import { GridComponent, TooltipComponent } from 'echarts/components'
import VChart from 'vue-echarts'
use([CanvasRenderer, LineChart, GridComponent, TooltipComponent])
let map = null
// overlay /
let lineOverlay = null
let routeMarkers = []
let accidentOverlays = []
// 1=2=3=
const currentStep = ref(1)
// 0~1 currentStep 1->0, 2->0.5, 3->1
const progressPercent = ref(0)
const progressFillWidth = computed(() => `${progressPercent.value * 100}%`)
//
function goToStep(step) {
currentStep.value = step
progressPercent.value = step === 1 ? 0 : step === 2 ? 0.5 : 1
}
// currentStep 线
function updateMapOverlays(step) {
if (!map) return
routeMarkers.forEach(m => map.removeOverlay(m))
accidentOverlays.forEach(o => map.removeOverlay(o))
if (lineOverlay) map.removeOverlay(lineOverlay)
if (step === 2) {
accidentOverlays.forEach(o => map.addOverlay(o))
} else if (step === 3) {
routeMarkers.forEach(m => map.addOverlay(m))
if (lineOverlay) map.addOverlay(lineOverlay)
}
}
watch(currentStep, step => updateMapOverlays(step))
// / (1: 10:00-11:00)
const timeLabels = ['10:00', '10:05', '10:10', '10:15', '10:20', '10:25', '10:30', '10:35', '10:40', '10:45', '10:50', '10:55', '11:00']
// : 10:00-10:20 50, 10:22, 10:305, 11:0015
const speedData = [50, 50, 48, 49, 50, 35, 5, 6, 8, 10, 12, 14, 15]
// : 10:30, 10:3055
const flowData = [5, 6, 6, 7, 8, 12, 18, 28, 38, 45, 50, 53, 55]
const recoveryChartOption = computed(() => ({
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross' },
},
legend: {
show: true,
top: 4,
right: 8,
},
grid: { left: 28, right: 28, top: 58, bottom: 26 },
xAxis: {
type: 'category',
data: timeLabels,
axisLine: { lineStyle: { color: 'rgba(0,0,0,0.2)' } },
axisLabel: {
color: '#666',
fontSize: 11,
formatter: (v, i) => (v === '10:30' ? v + '\n(事故)' : v),
},
},
yAxis: [
{
type: 'value',
name: '速度 (km/h)',
nameTextStyle: { color: '#ef4444', fontSize: 11 },
min: 0,
max: 60,
splitLine: { lineStyle: { color: 'rgba(0,0,0,0.06)' } },
axisLine: { show: false },
axisLabel: { color: '#666', fontSize: 10 },
},
{
type: 'value',
name: '流量 (辆/小时)',
nameTextStyle: { color: '#3b82f6', fontSize: 11 },
min: 0,
max: 60,
splitLine: { show: false },
axisLine: { show: false },
axisLabel: { color: '#666', fontSize: 10 },
},
],
series: [
{
name: '核心区速度',
type: 'line',
data: speedData,
yAxisIndex: 0,
smooth: true,
symbol: 'none',
lineStyle: { color: '#ef4444', width: 2 },
itemStyle: { color: '#ef4444' },
},
{
name: '绕行流量',
type: 'line',
data: flowData,
yAxisIndex: 1,
smooth: true,
symbol: 'none',
lineStyle: { color: '#3b82f6', width: 2, type: 'dashed' },
itemStyle: { color: '#3b82f6' },
},
],
}))
//
const ztzbBgUrl = new URL('@/assets/images/map/ztzb-bg.png', import.meta.url).href
const csIconUrl = new URL('@/assets/images/map/cs.png', import.meta.url).href
const zsIconUrl = new URL('@/assets/images/map/zs.png', import.meta.url).href
const ywIconUrl = new URL('@/assets/images/map/yw.png', import.meta.url).href
// 线 [{ lat, lng }]便
const linePoints = [
{ lat: 31.87797352705837, lng: 118.82483907602035 },
{ lat: 31.87762090556211, lng: 118.8271746703463 },
{ lat: 31.876417369256504, lng: 118.82670755148105 },
{ lat: 31.87667800610577, lng: 118.8245516182571 },
{ lat: 31.877935185157046, lng: 118.82350958386553 },
{ lat: 31.87797352705837, lng: 118.82483907602035 },
]
// ++便
const sgMarkerPoints = [
{
lat: 31.881235188813914,
lng: 118.82176687117614,
text: '汉中路路与中山路交叉口 (事故点)',
},
// { lat: 31.88, lng: 118.82, text: 'xxx ()' },
]
onMounted(() => {
// BMapGL BMap
@ -20,16 +278,78 @@ onMounted(() => {
//
map = new window.BMapGL.Map('baiduMap')
//
const centerPoint = new window.BMapGL.Point(118.82717467034637, 31.877682244533975)
// Point
const bPoints = linePoints.map(p => new window.BMapGL.Point(p.lng, p.lat))
const centerPoint = new window.BMapGL.Point(
bPoints.reduce((sum, p) => sum + p.lng, 0) / bPoints.length,
bPoints.reduce((sum, p) => sum + p.lat, 0) / bPoints.length
)
map.centerAndZoom(centerPoint, 17)
//
map.enableScrollWheelZoom(true)
//
const marker = new window.BMapGL.Marker(centerPoint)
map.addOverlay(marker)
const markerIcon = new window.BMapGL.Icon(
new URL('@/assets/images/map/redLineMarker.png', import.meta.url).href,
new window.BMapGL.Size(36, 36)
)
// 线
bPoints.forEach(point => {
const marker = new window.BMapGL.Marker(point, { icon: markerIcon })
routeMarkers.push(marker)
})
// 线
lineOverlay = new window.BMapGL.Polyline(bPoints, {
strokeColor: '#1E90FF',
strokeWeight: 4,
strokeOpacity: 0.9,
strokeStyle: 'dashed'
})
//
const sgBgUrl = new URL('@/assets/images/map/sgMarker-Bg.png', import.meta.url).href
const sgMarkerUrl = new URL('@/assets/images/map/sgMarker.png', import.meta.url).href
function SgMarkerOverlay(center, text) {
this._center = center
this._text = text || ''
}
SgMarkerOverlay.prototype = new window.BMapGL.Overlay()
SgMarkerOverlay.prototype.initialize = function (map) {
this._map = map
const div = document.createElement('div')
div.className = 'sg-marker-overlay'
div.innerHTML = `
<div class="sg-marker-wrapper">
<img class="sg-marker-bg" src="${sgBgUrl}" alt="" />
<div class="sg-marker-content">
<img class="sg-marker-img" src="${sgMarkerUrl}" alt="" />
<span class="sg-marker-text">${this._text}</span>
</div>
</div>
`
map.getPanes().markerPane.appendChild(div)
this._div = div
return div
}
SgMarkerOverlay.prototype.draw = function () {
const position = this._map.pointToOverlayPixel(this._center)
this._div.style.left = position.x + 'px'
this._div.style.top = position.y + 'px'
}
sgMarkerPoints.forEach((point) => {
const bPoint = new window.BMapGL.Point(point.lng, point.lat)
const overlay = new SgMarkerOverlay(bPoint, point.text)
accidentOverlays.push(overlay)
})
//
updateMapOverlays(currentStep.value)
//
map.addEventListener('click', function (e) {
const lng = (e.latlng?.lng ?? e.latLng?.lng) || e.point?.lng
@ -45,6 +365,7 @@ onBeforeUnmount(() => {
<style scoped>
.map-page {
position: relative;
width: 100%;
height: 100%;
}
@ -54,4 +375,407 @@ onBeforeUnmount(() => {
height: 100%;
/* 如果页面没有铺满可以设固定高度测试height: 600px; */
}
</style>
<style>
/* 事故点标注样式(非 scoped因 CustomOverlay 挂载到地图内部) */
.sg-marker-overlay {
position: absolute;
z-index: 100;
transform: translate(-50%, -50%);
pointer-events: none;
}
.sg-marker-wrapper {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.sg-marker-bg {
width: 498px;
height: 498px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.sg-marker-content {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 259px;
height: 62px;
}
.sg-marker-img {
position: absolute;
width: 259px;
height: 62px;
left: 0;
top: 0;
}
.sg-marker-text {
position: relative;
z-index: 2;
color: #fff;
font-size: 14px;
white-space: nowrap;
text-align: center;
line-height: 82px;
padding: 0 12px;
margin-bottom: 30px;
}
/* 示范区实时交通状态指标弹窗 - 居中展示在屏幕中间 */
.actual-raffic-conditions {
position: absolute;
inset: 0;
z-index: 101;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
top: -25%;
}
.actual-traffic-conditions-inner {
position: relative;
/* min-width: 320px; */
border-radius: 8px;
overflow: hidden;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.actual-traffic-conditions-header {
padding: 12px 16px;
font-size: 16px;
font-weight: 500;
color: #fff;
text-align: left;
background: linear-gradient(to right, #3b82d9, #60a5fa);
}
.actual-traffic-conditions-body {
padding: 16px;
background: #fff;
}
.actual-traffic-conditions-row {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
font-size: 14px;
}
.actual-traffic-conditions-row:not(:last-child) {
border-bottom: 1px solid #f0f0f0;
}
.actual-traffic-conditions-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.actual-traffic-conditions-label {
color: #333;
}
.actual-traffic-conditions-value {
font-weight: 600;
}
.actual-traffic-conditions-value--green {
color: #22c55e;
}
.actual-traffic-conditions-value--red {
color: #ef4444;
}
.actual-traffic-conditions-status {
font-size: 12px;
}
.actual-traffic-conditions-status--green {
color: #22c55e;
}
.actual-traffic-conditions-status--red {
color: #ef4444;
}
/* 事故进度条 - 按设计图还原 */
.accident-progress {
position: absolute;
bottom: 85px;
left: 50%;
transform: translateX(-50%);
z-index: 102;
padding: 12px 24px;
border-radius: 8px;
pointer-events: auto;
width: 400px;
}
.accident-progress-inner {
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
}
.accident-progress-labels {
display: flex;
justify-content: space-between;
width: 100%;
}
.accident-progress-label {
flex: 1;
text-align: center;
padding: 6px 12px;
margin: 0 30px;
font-size: 14px;
font-weight: 500;
color: #6b7280;
background: #ffffff;
border: 1px solid #d1d5db;
border-radius: 6px;
transition: all 0.2s;
cursor: pointer;
user-select: none;
}
.accident-progress-label:first-child {
margin-left: 0;
}
.accident-progress-label:last-child {
margin-right: 0;
}
.accident-progress-label.active {
color: #3b82f6;
background: #eef2ff;
border-color: #93c5fd;
}
.accident-progress-bar-wrap {
position: relative;
width: 100%;
height: 8px;
}
.accident-progress-line {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 6px;
border-radius: 3px;
transition: width 0.3s ease;
}
.accident-progress-line--bg {
width: 100%;
background: #ffffff;
border: 1px solid #d1d5db;
}
.accident-progress-line--fill {
background: #3b82f6;
border: none;
border-radius: 3px;
z-index: 1;
}
.accident-progress-nodes {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 2;
pointer-events: none;
}
.accident-progress-node {
width: 14px;
height: 14px;
border-radius: 50%;
background: #ffffff;
border: 1px solid #d1d5db;
flex-shrink: 0;
transition: all 0.2s;
}
.accident-progress-node.active {
background: #3b82f6;
border-color: #3b82f6;
}
/* 恢复中信息模块 - 示范区事故应急综合概览 */
.recovery-info-module {
position: absolute;
top: 24px;
left: 24px;
z-index: 102;
pointer-events: auto;
position: absolute;
top: 47.6%;
left: 61.7%;
z-index: 102;
pointer-events: auto;
width: 282px;
height: 478px;
}
.recovery-info-inner {
background: linear-gradient(180deg, rgba(240, 248, 255, 0.98) 0%, rgba(250, 253, 255, 0.98) 100%);
border-radius: 8px;
border: 1px solid rgba(45, 103, 237, 0.25);
box-shadow: 0 4px 16px rgba(97, 112, 146, 0.15);
padding: 14px 18px 18px;
min-width: 100%;
height: 100%;
}
.recovery-info-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 14px;
}
.recovery-info-chevrons {
color: #3b82f6;
font-size: 16px;
font-weight: 600;
letter-spacing: -2px;
}
.recovery-info-title {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #252525;
}
.recovery-info-details {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
.recovery-info-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.recovery-info-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.recovery-info-icon--red {
color: #ef4444;
}
.recovery-info-icon--green {
color: #22c55e;
}
.recovery-info-icon--blue {
color: #3b82f6;
}
.recovery-info-label {
color: #4b5563;
min-width: 40px;
}
.recovery-info-value {
font-weight: 500;
}
.recovery-info-value--red {
color: #ef4444;
}
.recovery-info-value--green {
color: #22c55e;
}
.recovery-info-value--blue {
color: #3b82f6;
}
.recovery-info-chart {
border-top: 1px solid rgba(0, 0, 0, 0.06);
padding-top: 12px;
}
.recovery-info-chart-title {
font-size: 13px;
color: #252525;
font-weight: 500;
margin-bottom: 8px;
}
.recovery-info-chart-legend {
display: flex;
gap: 16px;
margin-bottom: 6px;
font-size: 12px;
color: #6b7280;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
}
.legend-line {
display: inline-block;
width: 20px;
height: 3px;
border-radius: 1px;
}
.legend-line--speed {
background: #ef4444;
}
.legend-line--flow {
background: repeating-linear-gradient(90deg, #3b82f6 0, #3b82f6 4px, transparent 4px, transparent 8px);
}
.recovery-info-chart-stage {
height: 240px;
}
.recovery-chart {
width: 100%;
height: 100%;
}
</style>