YouKeChuanMei_VUE/src/views/mediaMap/index.vue

527 lines
15 KiB
Vue
Raw Normal View History

2025-09-02 22:55:31 +08:00
<template>
<div class="app-container">
<el-card class="mediaCard">
<el-row :gutter="10" class="my_row" style="padding: 0 20px;">
<el-col :span="12">
<el-form :model="queryParams" ref="queryRef" :inline="true" class="searchInputForm">
<el-form-item label="" prop="templateName">
2025-09-09 14:52:18 +08:00
<el-input v-model="queryParams.keyword" placeholder="请输入媒体名称/媒体编号/关键字" :prefix-icon="Search"
style="width: 400px;" />
2025-09-02 22:55:31 +08:00
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" style="text-align: right;">
<el-dropdown placement="bottom-start">
<el-button type="primary" class="mediaMapBtn">距离{{ distanceLable }}</el-button>
<template #dropdown>
<el-dropdown-menu style="min-width: 122px;">
<template v-for="item in distanceLableArray" :key="item.value">
<el-dropdown-item
:class="activeLableIndex === item.value ? 'distanceItemActive' : 'distanceItem'"
@click="handleChangeDistance(item)">{{ item.label }}</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
2025-09-11 22:54:53 +08:00
<div class="clearBtn" @click="resetQuery">清除条件</div>
2025-09-02 22:55:31 +08:00
</el-col>
</el-row>
<div id="mapContainer" class="mediaMapContainer"></div>
</el-card>
</div>
</template>
<script setup name="Post">
2025-09-03 20:21:01 +08:00
import { onMounted, onUnmounted, ref } from 'vue';
2025-09-02 22:55:31 +08:00
import { Search } from '@element-plus/icons-vue'
import AMapLoader from "@amap/amap-jsapi-loader"; // 引入地图服务
import { useBackgroundStore } from '@/store/modules/background'
import otherbg from '@/assets/images/otherbg.png'
2025-09-11 22:54:53 +08:00
import { mediaByMap } from "@/api/mediaLibrary"
import { rAF } from 'element-plus/es/utils/raf.mjs';
2025-09-02 22:55:31 +08:00
const bgStore = useBackgroundStore()
const { proxy } = getCurrentInstance()
2025-09-09 14:52:18 +08:00
const { apiKey, secretKey } = window._CONFIG
2025-09-02 22:55:31 +08:00
// 距离显示文本
const distanceLable = ref('请选择')
2025-09-11 22:54:53 +08:00
// 选择的距离值
const activeLableIndex = ref(null)
2025-09-02 22:55:31 +08:00
const distanceLableArray = ref([
2025-09-11 22:54:53 +08:00
{ label: '1公里内', value: 1000 },
{ label: '2公里内', value: 2000 },
{ label: '3公里内', value: 3000 },
{ label: '5公里内', value: 5000 }
2025-09-02 22:55:31 +08:00
])
2025-09-11 22:54:53 +08:00
const points = ref([])
2025-09-02 22:55:31 +08:00
const data = reactive({
queryParams: {
keyword: undefined,
2025-09-11 22:54:53 +08:00
x: undefined, //中心点经度
y: undefined, //中心点纬度
2025-09-02 22:55:31 +08:00
distance: undefined,
}
})
const { queryParams } = toRefs(data)
/** 清除条件操作 */
const resetQuery = () => {
2025-09-11 22:54:53 +08:00
queryParams.value = {
keyword: undefined,
x: undefined, //中心点经度
y: undefined, //中心点纬度
distance: undefined,
}
2025-09-02 22:55:31 +08:00
handleQuery()
}
// 选择距离
const handleChangeDistance = (itemDistance) => {
queryParams.value.distance = itemDistance.value
activeLableIndex.value = itemDistance.value
2025-09-03 20:21:01 +08:00
distanceLable.value = itemDistance.label
2025-09-02 22:55:31 +08:00
}
// map实例
const mapInstance = ref(null)
const massMarks = ref(null)
// 当前地图模式2D/3D
const mapMode = ref('2D')
// 是否全屏状态
const isFullscreen = ref(false)
// 初始化地图
const loadMap = () => {
return new Promise((resolve, reject) => {
// 设置安全密钥
window._AMapSecurityConfig = {
2025-09-09 14:52:18 +08:00
securityJsCode: secretKey
2025-09-02 22:55:31 +08:00
};
AMapLoader.load({
2025-09-09 14:52:18 +08:00
key: apiKey,
2025-09-02 22:55:31 +08:00
plugins: [],
2025-09-09 14:52:18 +08:00
AMapUI: {
2025-09-02 22:55:31 +08:00
plugins: []
}
}).then(AMap => {
// 根据当前模式设置初始参数
const initialPitch = mapMode.value === '3D' ? 65 : 0;
const initialRotation = mapMode.value === '3D' ? -15 : 0;
mapInstance.value = new AMap.Map("mapContainer", {
resizeEnable: true,
zoom: 14,
2025-09-11 22:54:53 +08:00
center: [116.397428, 39.90923], // 默认中心点
2025-09-02 22:55:31 +08:00
pitch: initialPitch, // 倾斜角度决定2D/3D模式
rotation: initialRotation, // 旋转角度
buildingAnimation: true,
expandZoomRange: true,
zooms: [3, 20],
viewMode: mapMode.value // 启用3D视图
});
// 先添加基本控件
mapInstance.value.setZoom(14);
// 添加自定义控件容器
addCustomControls(AMap);
// 地图加载完成后隐藏Logo
setTimeout(() => {
hideAmapLogo();
}, 1000);
// 监听地图渲染完成事件
mapInstance.value.on('render', hideAmapLogo);
// 在地图完全加载后执行点数据处理
renderMassMarks();
resolve();
}).catch(e => {
console.log(e, "高德地图加载失败");
reject(e);
});
});
}
// 添加自定义控件
const addCustomControls = (AMap) => {
// 创建控件容器
const controlContainer = document.createElement('div');
controlContainer.className = 'custom-map-controls';
// 2D/3D切换按钮
const toggle2D3DBtn = document.createElement('div');
toggle2D3DBtn.className = mapMode.value === '2D' ? 'map-control-btn map-control-3dbtn' : 'map-control-btn map-control-2dbtn';
toggle2D3DBtn.onclick = toggle2D3DMode;
// 全屏按钮
const fullscreenBtn = document.createElement('div');
fullscreenBtn.className = isFullscreen.value === true ? 'map-control-btn map-full-screen' : 'map-control-btn map-nofull-screen';
fullscreenBtn.onclick = toggleFullscreen;
controlContainer.appendChild(toggle2D3DBtn);
controlContainer.appendChild(fullscreenBtn);
// 添加到地图容器
document.getElementById('mapContainer').appendChild(controlContainer);
}
// 切换2D/3D模式
const toggle2D3DMode = () => {
if (!mapInstance.value) return;
const currentPitch = mapInstance.value.getPitch();
if (currentPitch === 0) {
// 切换到3D模式
mapInstance.value.setPitch(65);
mapInstance.value.setRotation(-15);
mapMode.value = '3D';
loadMap()
} else {
// 切换到2D模式
mapInstance.value.setPitch(0);
mapInstance.value.setRotation(0);
mapMode.value = '2D';
loadMap()
}
}
// 切换全屏模式
const toggleFullscreen = () => {
const mapContainer = document.getElementById('mapContainer');
if (!document.fullscreenElement) {
// 进入全屏
if (mapContainer.requestFullscreen) {
mapContainer.requestFullscreen().then(() => {
isFullscreen.value = true;
const buttons = document.querySelectorAll('.map-control-btn');
buttons[1].className = 'map-control-btn map-full-screen';
}).catch(err => {
console.error('全屏模式错误:', err);
});
}
} else {
// 退出全屏
if (document.exitFullscreen) {
document.exitFullscreen().then(() => {
isFullscreen.value = false;
const buttons = document.querySelectorAll('.map-control-btn');
buttons[1].className = 'map-control-btn map-nofull-screen';
});
}
}
}
// 监听全屏变化事件
document.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
isFullscreen.value = false;
}
});
// 创建自定义点样式
const createPointStyle = (color, size, styleType) => {
const canvas = document.createElement('canvas');
canvas.width = size * 2;
canvas.height = size * 2;
const ctx = canvas.getContext('2d');
// 根据样式类型绘制不同的图形
switch (styleType) {
case 'circle':
ctx.beginPath();
ctx.arc(size, size, size, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
break;
case 'ring':
ctx.beginPath();
ctx.arc(size, size, size, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fill();
ctx.beginPath();
ctx.arc(size, size, size * 0.7, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
break;
case 'square':
ctx.fillStyle = color;
ctx.fillRect(0, 0, size * 2, size * 2);
break;
case 'star':
ctx.fillStyle = color;
ctx.beginPath();
for (let i = 0; i < 5; i++) {
const angle = (i * 2 * Math.PI / 5) - Math.PI / 2;
const x = size + size * Math.cos(angle);
const y = size + size * Math.sin(angle);
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
const innerAngle = angle + Math.PI / 5;
const innerX = size + size * 0.4 * Math.cos(innerAngle);
const innerY = size + size * 0.4 * Math.sin(innerAngle);
ctx.lineTo(innerX, innerY);
}
ctx.closePath();
ctx.fill();
break;
}
return canvas;
}
// 渲染海量点
const renderMassMarks = () => {
// 清除现有的海量点
if (massMarks.value) {
massMarks.value.clear();
}
// 创建三种不同大小的样式
const styles = [
// 优势媒体颜色-红色
{
url: createPointStyle('#EC1B60', 18, 'circle').toDataURL(),
anchor: new AMap.Pixel(18, 18),
size: new AMap.Size(18, 18)
},
// 网络媒体颜色-蓝色
{
url: createPointStyle('#058DED', 18, 'circle').toDataURL(),
anchor: new AMap.Pixel(18, 18),
size: new AMap.Size(18, 18)
},
];
2025-09-11 22:54:53 +08:00
// 调用接口,获取数据点
points.value = []
mediaByMap(queryParams.value).then(res => {
if (res.code == 200) {
res.data.forEach(itemPoint => {
if (itemPoint.businessType == 1) points.value.push({ "lnglat": [itemPoint.x, itemPoint.y], "name": itemPoint.mediaId, "style": 0 })
if (itemPoint.businessType == 2) points.value.push({ "lnglat": [itemPoint.x, itemPoint.y], "name": itemPoint.mediaId, "style": 1 })
});
}
}).then(res => {
console.log('points', points.value)
// 创建MassMarks对象
massMarks.value = new AMap.MassMarks(points.value, {
opacity: 1,
zIndex: 111,
cursor: 'pointer',
style: styles
});
2025-09-02 22:55:31 +08:00
2025-09-11 22:54:53 +08:00
// 将海量点添加到地图
massMarks.value.setMap(mapInstance.value);
2025-09-02 22:55:31 +08:00
2025-09-11 22:54:53 +08:00
// 添加点击事件
massMarks.value.on('click', function (e) {
console.log('点击了节点', e.data)
});
})
2025-09-02 22:55:31 +08:00
}
// 隐藏Logo的函数
const hideAmapLogo = () => {
const logos = document.querySelectorAll('.amap-logo, .amap-copyright');
logos.forEach(logo => {
logo.style.display = 'none';
logo.style.visibility = 'hidden';
logo.style.opacity = '0';
});
}
2025-09-03 20:21:01 +08:00
onUnmounted(() => {
2025-09-09 14:52:18 +08:00
mapInstance.value?.destroy()
2025-09-03 20:21:01 +08:00
})
2025-09-02 22:55:31 +08:00
// 初始化
onMounted(() => {
bgStore.setBgImage(otherbg)
loadMap()
});
</script>
<style lang="scss">
.mediaCard .el-card__body {
padding: 0px !important;
}
.mediaMapBtn {
min-width: 122px;
height: 34px;
border-radius: 4px;
background: #1a75e6;
font-family: Microsoft YaHei;
font-weight: 400;
font-size: 16px;
text-align: left;
color: #FFFFFF;
padding: 10px;
}
.mediaMapBtn>span {
align-items: center;
display: inline-flex;
width: 100%;
}
.mediaMapBtn:hover {
background: #1a75e6;
}
.distanceItem {
position: relative;
padding-left: 40px;
padding-right: 30px;
font-family: Microsoft YaHei;
font-weight: 400;
font-size: 16px;
color: #1E1E1E;
}
.distanceItem:hover {
font-weight: 600;
color: #1A75E6;
}
.distanceItemActive {
position: relative;
padding-left: 40px;
padding-right: 30px;
font-family: Microsoft YaHei;
font-weight: 600;
font-size: 16px;
color: #1A75E6;
}
.distanceItem:hover::before,
.distanceItemActive::before {
content: "√";
position: absolute;
left: 17px;
top: 6px;
width: 13px;
height: 10px;
font-family: Microsoft YaHei;
font-weight: 600;
color: #1A75E6;
}
.clearBtn {
display: inline-flex;
margin-left: 20px;
padding-left: 18px;
position: relative;
min-width: 82px;
height: 34px;
line-height: 34px;
font-family: Microsoft YaHei;
font-weight: 400;
font-size: 16px;
color: #87898E;
cursor: pointer;
}
2025-09-09 14:52:18 +08:00
2025-09-02 22:55:31 +08:00
.clearBtn::before {
content: "";
position: absolute;
left: 0px;
top: 10px;
width: 14px;
2025-09-09 14:52:18 +08:00
height: 14px;
2025-09-02 22:55:31 +08:00
background-image: url('../../assets/images/iconClearWhere.png');
background-repeat: no-repeat;
}
.mediaMapContainer {
width: 100%;
height: calc(100vh - 170px);
background: #3f8bff;
}
.custom-map-controls {
position: absolute;
top: 5px;
right: 5px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 0px;
}
.map-control-btn {
cursor: pointer;
}
.map-control-2dbtn {
width: 40px;
height: 40px;
background: url('../../assets/images/icon-2D-btn.png');
}
.map-control-2dbtn:hover {
background: url('../../assets/images/icon-2D-active-btn.png');
}
.map-control-3dbtn {
width: 40px;
height: 40px;
background: url('../../assets/images/icon-3D-btn.png');
}
.map-control-3dbtn:hover {
background: url('../../assets/images/icon-3D-active-btn.png');
}
.map-nofull-screen {
width: 40px;
height: 40px;
background: url('../../assets/images/icon-full-screen.png');
}
.map-nofull-screen:hover {
background: url('../../assets/images/icon-full-screen-active.png');
}
.map-full-screen {
width: 40px;
height: 40px;
background: url('../../assets/images/icon-map-nofull-screen.png');
}
.map-full-screen:hover {
background: url('../../assets/images/icon-map-nofull-screen-active.png');
}
/* 全屏样式 */
.mapContainer:-webkit-full-screen {
width: 100% !important;
height: 100% !important;
}
.mapContainer:-moz-full-screen {
width: 100% !important;
height: 100% !important;
}
.mapContainer:-ms-fullscreen {
width: 100% !important;
height: 100% !important;
}
.mapContainer:fullscreen {
width: 100% !important;
height: 100% !important;
}
</style>