上传静态代码
This commit is contained in:
609
assetsMap/map.html
Normal file
609
assetsMap/map.html
Normal file
@@ -0,0 +1,609 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>资产地图(聚合版)</title>
|
||||
|
||||
<!-- Leaflet -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
|
||||
<!-- MarkerCluster -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css" />
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 顶部 */
|
||||
#topStats {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.statInline {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 10px 20px;
|
||||
border-radius: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.statItem {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.statNumber {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* 不同颜色 */
|
||||
.statNumber.total {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.statNumber.rented {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.statNumber.vacant {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* 分隔线 */
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background: #eee;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
/* 搜索 */
|
||||
.searchInput {
|
||||
margin-left: 20px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ddd;
|
||||
width: 280px; /* 这里加长了 */
|
||||
}
|
||||
|
||||
.searchBtn {
|
||||
margin-left: 8px;
|
||||
padding: 6px 12px;
|
||||
background: #27ae60;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 搜索结果 */
|
||||
.searchPanel {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 50%;
|
||||
transform: translateX(5%);
|
||||
width: 280px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.searchItem {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.searchItem:hover {
|
||||
background: #f5f5f5
|
||||
}
|
||||
|
||||
/* 右侧面板 */
|
||||
#assetPanel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 360px;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.15);
|
||||
transform: translateX(100%);
|
||||
transition: 0.3s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#assetPanel.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.panelHeader {
|
||||
padding: 15px;
|
||||
background: #f7f8fa;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.panelTitle {
|
||||
font-size: 17px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.closeBtn {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.panelMeta {
|
||||
padding: 10px 15px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.assetList {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.assetItem {
|
||||
cursor: pointer;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.marker-cluster-small {
|
||||
background-color: rgba(231, 76, 60, 0.6) !important;
|
||||
}
|
||||
|
||||
.marker-cluster-small div {
|
||||
background-color: rgba(231, 76, 60, 0.8) !important;
|
||||
}
|
||||
|
||||
|
||||
.marker-cluster-medium {
|
||||
background-color: rgba(231, 76, 60, 0.6) !important;
|
||||
}
|
||||
|
||||
.marker-cluster-medium div {
|
||||
background-color: rgba(231, 76, 60, 0.8) !important;
|
||||
}
|
||||
|
||||
|
||||
.marker-cluster-large {
|
||||
background-color: rgba(192, 57, 43, 0.6) !important;
|
||||
}
|
||||
|
||||
.marker-cluster-large div {
|
||||
background-color: rgba(192, 57, 43, 0.85) !important;
|
||||
}
|
||||
|
||||
.marker-cluster-custom {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.marker-cluster-custom .cluster-inner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: rgba(231, 76, 60, 0.85);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.marker-cluster-custom .cluster-inner span {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.assetTenant,
|
||||
.assetManager,
|
||||
.assetOwner,
|
||||
.assetStatus,
|
||||
.assetPurpose,
|
||||
.assetAddress,
|
||||
.assetRemark,
|
||||
.assetArea {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
margin-top: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
|
||||
<!-- 顶部统计 -->
|
||||
|
||||
<!-- 顶部 -->
|
||||
<div id="topStats">
|
||||
<div class="statInline">
|
||||
|
||||
<div class="statItem">
|
||||
<span class="statNumber total">总资产: {{allAssetsCount}}</span>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="statItem">
|
||||
<span class="statNumber rented">已出租: {{rentedCount}}</span>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="statItem">
|
||||
<span class="statNumber vacant">未出租: {{vacantCount}}</span>
|
||||
</div>
|
||||
|
||||
<!-- 搜索 -->
|
||||
<input v-model="searchKeyword" @keyup.enter="searchLocation" class="searchInput" placeholder="输入详细地址">
|
||||
|
||||
<button @click="searchLocation" class="searchBtn">搜索</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<div v-if="searchResults.length" class="searchPanel">
|
||||
<div class="searchItem" v-for="(item,index) in searchResults" :key="index"
|
||||
@click="selectSearchResult(item)">
|
||||
{{item.assetName || item.address}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<!-- 右侧资产面板 -->
|
||||
<div id="assetPanel" :class="{show:panelVisible}">
|
||||
<div class="panelHeader">
|
||||
<span class="panelTitle">{{currentRegion.address}}</span>
|
||||
<span class="closeBtn" @click="closePanel">✕</span>
|
||||
</div>
|
||||
|
||||
<div class="panelMeta">
|
||||
资产数量:{{currentRegion.assets ? currentRegion.assets.length : 0}}
|
||||
</div>
|
||||
|
||||
<div class="assetList">
|
||||
<div class="assetItem" v-for="(a,index) in currentRegion.assets" :key="index" @click="goToDetail(a)">
|
||||
<div class="assetManager">运营单位:{{a.manager}}</div>
|
||||
<div class="assetOwner">产权单位:{{a.owner}}</div>
|
||||
<div class="assetPurpose">用途:{{a.purpose}}</div>
|
||||
<div class="assetAddress">地址:{{a.address}}</div>
|
||||
<div class="assetStatus">资产状态:{{a.status || 未知}}</div>
|
||||
<div class="assetArea">面积:{{a.area}}㎡</div>
|
||||
<div class="assetRemark">备注:{{a.remark || '暂无'}}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
|
||||
el: "#app",
|
||||
|
||||
data: {
|
||||
host: 'http://' + 'www.ycgongxiao.xyz:52100',
|
||||
searchKeyword: "",
|
||||
searchResults: [],
|
||||
searchMarker: null,
|
||||
map: null,
|
||||
markerGroup: null,
|
||||
regions: [],
|
||||
allAssetsCount: 0,
|
||||
rentedCount: 0,
|
||||
vacantCount: 0,
|
||||
panelVisible: false,
|
||||
currentRegion: {}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initMap()
|
||||
this.queryAssetsRegionsInfo()
|
||||
this.countAllAssets()
|
||||
this.countRentedAssets()
|
||||
this.countRestAssets()
|
||||
},
|
||||
watch: {
|
||||
// 输入清空自动隐藏
|
||||
searchKeyword(val) {
|
||||
if (!val) {
|
||||
this.searchResults = []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 🗺️ 初始化地图
|
||||
initMap() {
|
||||
|
||||
this.map = L.map('map').setView([30.630608, 111.375863], 10)
|
||||
|
||||
L.tileLayer('./map/{z}/{x}/{y}/tile.png', {
|
||||
maxZoom: 18,
|
||||
minZoom: 8
|
||||
}).addTo(this.map)
|
||||
|
||||
// ⭐ 核心:自定义聚合
|
||||
this.markerGroup = L.markerClusterGroup({
|
||||
|
||||
iconCreateFunction: (cluster) => {
|
||||
|
||||
let total = 0
|
||||
|
||||
let markers = cluster.getAllChildMarkers()
|
||||
|
||||
markers.forEach(m => {
|
||||
let region = m.options.regionData
|
||||
total += (region && region.assets ? region.assets.length : 0)
|
||||
})
|
||||
|
||||
return L.divIcon({
|
||||
html: `
|
||||
<div class="cluster-inner">
|
||||
<span>${total}</span>
|
||||
</div>
|
||||
`,
|
||||
className: 'marker-cluster-custom',
|
||||
iconSize: L.point(40, 40)
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
this.map.addLayer(this.markerGroup)
|
||||
|
||||
// 点击地图关闭弹窗
|
||||
// 点击地图空白关闭面板
|
||||
this.map.on('click', () => {
|
||||
this.searchResults = []
|
||||
this.panelVisible = false
|
||||
})
|
||||
},
|
||||
countAllAssets() {
|
||||
fetch(this.host + "/seeyon/rest/assetsRegion/countAssets", {
|
||||
method: 'GET'
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
|
||||
if (res.code !== 0) {
|
||||
console.error("接口错误", res.message)
|
||||
return
|
||||
}
|
||||
|
||||
this.allAssetsCount = res.data || 0
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("加载失败", err)
|
||||
})
|
||||
},
|
||||
countRentedAssets() {
|
||||
fetch(this.host + "/seeyon/rest/assetsRegion/countAssets?type=1", {
|
||||
method: 'GET'
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
|
||||
if (res.code !== 0) {
|
||||
console.error("接口错误", res.message)
|
||||
return
|
||||
}
|
||||
|
||||
this.rentedCount = res.data || 0
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("加载失败", err)
|
||||
})
|
||||
},
|
||||
countRestAssets() {
|
||||
fetch(this.host + "/seeyon/rest/assetsRegion/countAssets?type=2", {
|
||||
method: 'GET'
|
||||
}).then(res => res.json())
|
||||
.then(res => {
|
||||
|
||||
if (res.code !== 0) {
|
||||
console.error("接口错误", res.message)
|
||||
return
|
||||
}
|
||||
|
||||
this.vacantCount = res.data || 0
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("加载失败", err)
|
||||
})
|
||||
},
|
||||
// 📡 请求数据
|
||||
queryAssetsRegionsInfo() {
|
||||
|
||||
fetch(this.host + "/seeyon/rest/assetsRegion/getall", {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
|
||||
if (res.code !== 0) {
|
||||
console.error("接口错误", res.message)
|
||||
return
|
||||
}
|
||||
|
||||
this.regions = res.data || []
|
||||
this.renderMarkers()
|
||||
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("加载失败", err)
|
||||
})
|
||||
},
|
||||
|
||||
// 📊 面积计算
|
||||
calcArea(list) {
|
||||
let sum = 0
|
||||
list.forEach(a => {
|
||||
sum += Number(a.area || 0)
|
||||
})
|
||||
return sum
|
||||
},
|
||||
|
||||
// 📍 渲染 marker(核心)
|
||||
renderMarkers() {
|
||||
|
||||
// 清空旧数据
|
||||
this.markerGroup.clearLayers()
|
||||
|
||||
this.regions.forEach(r => {
|
||||
|
||||
let marker = L.marker([r.lat, r.lng])
|
||||
|
||||
marker.options.regionData = r
|
||||
|
||||
// 点击打开右侧面板
|
||||
marker.on("click", () => {
|
||||
this.showAssetPanel(r)
|
||||
})
|
||||
|
||||
// hover提示
|
||||
marker.on("mouseover", () => {
|
||||
|
||||
let area = this.calcArea(r.assets || [])
|
||||
let owner = r.owner || ""
|
||||
let manager = r.manager || ""
|
||||
let count = (r.assets || []).length
|
||||
|
||||
let html =
|
||||
"<b>位置:" + r.address + "</b><br>" +
|
||||
"运营单位:" + manager + "<br>" +
|
||||
"产权单位:" + owner + "<br>" +
|
||||
"面积:" + area + "㎡<br>" +
|
||||
"资产数量:" + count
|
||||
|
||||
marker.bindPopup(html).openPopup()
|
||||
})
|
||||
|
||||
marker.on("mouseout", () => {
|
||||
marker.closePopup()
|
||||
})
|
||||
|
||||
// ⭐ 加入聚合组
|
||||
this.markerGroup.addLayer(marker)
|
||||
})
|
||||
},
|
||||
// 搜索
|
||||
searchLocation() {
|
||||
|
||||
if (!this.searchKeyword) return
|
||||
|
||||
fetch(this.host + "/seeyon/rest/assetsRegion/search", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
keyword: this.searchKeyword
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.code !== 0) return
|
||||
|
||||
let list = res.data || []
|
||||
|
||||
if (!Array.isArray(list)) {
|
||||
list = [list]
|
||||
}
|
||||
|
||||
this.searchResults = list
|
||||
// this.selectSearchResult(list[0])
|
||||
})
|
||||
},
|
||||
goToDetail(item) {
|
||||
let id = item.id
|
||||
let baseUrl =
|
||||
`http://www.ycgongxiao.xyz:52100/seeyon/cap4/businessTemplateController.do?v=V10_0SP1_251022_222110_0&method=formContent&type=browse&rightId=-51300951786173064.-7277875919159408408&moduleId=${id}&formTemplateId=3171356881354704902&viewConditionId=&conditionIdDeal=&columnId=3171356881354704902&moduleType=42&isUnFlowDrawer=true&needUpdate=1&tplId=850010&btnId=&comeFrom=list&_v=1776323480999`
|
||||
window.open(baseUrl)
|
||||
},
|
||||
// 选择搜索结果
|
||||
selectSearchResult(item) {
|
||||
|
||||
// 定位
|
||||
this.map.setView([item.lat, item.lng], 18);
|
||||
|
||||
// 移除之前的搜索标记
|
||||
if (this.searchMarker) {
|
||||
this.markerGroup.removeLayer(this.searchMarker);
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// ✅ 最佳方案:找到原有的那个 marker,直接高亮/打开弹窗
|
||||
// ==============================================
|
||||
let targetMarker = null;
|
||||
|
||||
// 遍历聚合组里所有原有 marker,匹配数据
|
||||
this.markerGroup.eachLayer((layer) => {
|
||||
if (layer.regionData && layer.regionData.id === item.id) {
|
||||
targetMarker = layer;
|
||||
}
|
||||
});
|
||||
// 直接打开原有 marker 的弹窗(不新增任何标记)
|
||||
if (targetMarker) {
|
||||
targetMarker.fire('mouseover');
|
||||
return;
|
||||
}
|
||||
// 如果没找到(新地点),再创建一个底层标记
|
||||
// this.searchMarker = L.marker([item.lat, item.lng], {
|
||||
// zIndexOffset: -1000,
|
||||
// }).addTo(this.markerGroup);
|
||||
this.searchResults = []
|
||||
},
|
||||
// 📌 打开面板
|
||||
showAssetPanel(region) {
|
||||
this.currentRegion = region
|
||||
this.panelVisible = true
|
||||
},
|
||||
|
||||
// ❌ 关闭面板
|
||||
closePanel() {
|
||||
this.panelVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user