上传静态代码

This commit is contained in:
2026-05-21 16:40:50 +08:00
parent 9275c7950e
commit 6ca67ec941

609
assetsMap/map.html Normal file
View 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>