YouKeChuanMei_VUE/src/views/outdoorMedia/index.vue
2025-09-09 14:52:18 +08:00

694 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<div class="searchPanel">
<div class="more-search-pane">
<div class="search-where-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" class="searchPanelForm">
<el-form-item label="媒体类型:">
<el-select v-model="queryParams.mediaTypeOne" placeholder="请选择" @change="getMediaTypeTwo"
clearable style="min-width: 30px">
<el-option v-for="item in mediaTypeOne" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="媒体大类:">
<el-select v-model="queryParams.mediaTypeTwo" placeholder="请选择" @change="getMediaTypeThree"
clearable style="min-width: 30px">
<el-option v-for="item in mediaTypeTwo" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="展示形式:">
<el-select v-model="queryParams.mediaTypeThree" placeholder="请选择" clearable
style="min-width: 30px">
<el-option v-for="item in mediaTypeThree" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
</el-form>
</div>
<div class="search-more-button">
<el-button v-if="!unfoldFlag" text class="foladText" @click="handleFlod">展开
<svg-icon icon-class="unfold" class="ml10" />
</el-button>
<el-button v-else text class="foladText" @click="handleFlod">收起
<svg-icon icon-class="packUp" class="ml10" />
</el-button>
</div>
</div>
</div>
<div class="searchSmallPanel" v-show="unfoldFlag">
<el-form :model="queryParams" ref="queryRef" :inline="true" class="searchSmallPanelForm">
<el-form-item label="城市:">
<el-select v-model="queryParams.provinceId" placeholder="请选择" @change="getCityList" clearable
style="min-width: 30px">
<el-option v-for="item in province" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="">
<el-select v-model="queryParams.cityId" placeholder="请选择" @change="getCountyList" clearable
style="min-width: 30px">
<el-option v-for="item in city" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="">
<el-select v-model="queryParams.countyId" placeholder="请选择" @change="getTownList" clearable
style="min-width: 30px">
<el-option v-for="item in county" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="">
<el-select v-model="queryParams.townId" placeholder="请选择" clearable style="min-width: 30px">
<el-option v-for="item in town" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="商圈:">
<el-select v-model="queryParams.approval_document_status" placeholder="请选择" clearable
style="min-width: 30px;">
<el-option v-for="dict in approval_document_status" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</el-form>
</div>
<div class="choseResultPanel">
<el-form :inline="true" class="searchSmallPanelForm">
<el-form-item label="已选媒体:">
<div v-if="multipleChoseArr.length == 0" class="noChoseLabel">未选择媒体</div>
<template v-else>
<el-tag v-for="tag in multipleChoseArr" :key="tag.id" class="choseResultTag"
@close="handleCloseTag(tag)" closable>
{{ tag.mediaName }}
</el-tag>
</template>
</el-form-item>
</el-form>
</div>
<el-card class="mt10">
<el-row :gutter="10" class="my_row">
<el-col :span="8">
<el-form :model="queryParams" ref="queryRef" :inline="true" class="searchInputForm">
<el-form-item label="">
<el-input v-model="queryParams.postCode" placeholder="请输入媒体名称/媒体编号" :prefix-icon="Search"
style="width: 400px;" />
</el-form-item>
</el-form>
</el-col>
<el-col :span="16" style="text-align: right;">
<el-button type="primary" class="primaryBtn" @click="handleQuery">查询</el-button>
<el-button type="primary" class="primaryBtn" @click="resetQuery">重置</el-button>
<el-button type="primary" class="primaryBtn">PPT批量导出</el-button>
<el-button type="primary" class="primaryBtn">清空所选媒体</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="my_row" style="margin-bottom: 0;">
<el-col :span="12">
<el-table v-loading="loading" :data="outdoorMediaList" @selection-change="handleSelectionChange"
:height="unfoldFlag ? 'calc(100vh - 372px)' : 'calc(100vh - 338px)'">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="实景图片" align="left" prop="mediaName" width="170">
<template #default="scope">
<img :src="otherbg" fit="fill" style="width: 138px;height: 78px; border-radius: 4px" />
</template>
</el-table-column>
<el-table-column label="媒体名称" align="left" prop="mediaName" width="150">
<template #default="scope">
<el-popover popper-class="myImg_popover" placement="right-start">
<img :src="otherbg" />
<template #reference>
<span class="mediaNameLabel" @click="handleOpenDetail">{{ scope.row.mediaName
}}</span>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column label="商圈" align="left" prop="postCode" width="150" />
<el-table-column label="媒体大类" align="left" prop="postCode" width="150" />
<el-table-column label="展示形式" align="left" prop="postCode" width="150" />
<el-table-column label="操作" width="58" align="center" fixed="right">
<template #default="scope">
<el-popover popper-class="my_popover" placement="left-start">
<div class="popBtns" @click="handleLogs(scope.row)"
v-hasPermi="['problemFeedback:edit']">查看
</div>
<div class="popBtns" @click="handleDownFiles(scope.row)"
v-hasPermi="['problemFeedback:edit']">下载PPT</div>
<template #reference>
<img style="cursor: pointer;" :src="scope.row.currentImageSrc"
@mouseenter="scope.row.currentImageSrc = hoverImageSrc"
@mouseleave="scope.row.currentImageSrc = defaultImageSrc" />
</template>
</el-popover>
</template>
</el-table-column>
</el-table>
<pagination :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</el-col>
<el-col :span="12">
<div id="mapContainer" class="mapContainer"></div>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup name="Post">
import { onMounted, onUnmounted, ref } from 'vue';
import { Search } from '@element-plus/icons-vue'
import { sysRegionListByPid } from "@/api/system/administrativeRegion"
import { sysMediaTypeListByPid } from "@/api/system/mediaType"
import AMapLoader from "@amap/amap-jsapi-loader"; // 引入地图服务
import optionIcon from '@/assets/images/optionIcon.png'
import optionIconHover from '@/assets/images/optionIconHover.png'
import { useBackgroundStore } from '@/store/modules/background'
import otherbg from '@/assets/images/otherbg.png'
const bgStore = useBackgroundStore()
const { proxy } = getCurrentInstance()
const { approval_document_status } = proxy.useDict("approval_document_status")
const { apiKey, secretKey } = window._CONFIG
// 省、市、县、镇数据
const province = ref([])
const city = ref([])
const county = ref([])
const town = ref([])
// 媒体类型数据
const mediaTypeOne = ref([])
const mediaTypeTwo = ref([])
const mediaTypeThree = ref([])
// 归属选项
const supplierList = ref([])
// 多选数据
const multipleChoseArr = ref([])
const loading = ref(true)
const total = ref(0)
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
}
})
const { queryParams } = toRefs(data)
// 操作图标
const defaultImageSrc = ref(optionIcon);
const hoverImageSrc = ref(optionIconHover);
// 户外媒体数据
const outdoorMediaList = ref([
{ id: 1, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 2, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 3, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 4, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 5, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 6, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 7, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 8, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 9, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value },
{ id: 10, mediaName: '遵义市海风井铁路桥', currentImageSrc: defaultImageSrc.value }
])
// 折叠展开
const unfoldFlag = ref(false)
const handleFlod = () => {
unfoldFlag.value = !unfoldFlag.value
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
proxy.resetForm("queryRef")
handleQuery()
}
// 多选事件
const handleSelectionChange = (selection) => {
multipleChoseArr.value = selection
}
// 移除选择项
const handleCloseTag = (tag) => {
}
// 获取户外媒介列表数据
const getList = () => {
loading.value = false
// listPost(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
// postList.value = response.rows
// total.value = response.total
// loading.value = false
// })
}
// 获取一级媒体类型
const getMediaTypeOne = () => {
sysMediaTypeListByPid({ parentId: 0 }).then(res => {
mediaTypeOne.value = res.data
})
}
// 获取二级媒体类型
const getMediaTypeTwo = (value) => {
sysMediaTypeListByPid({ parentId: value }).then(res => {
queryParams.value.mediaTypeTwo = undefined
queryParams.value.mediaTypeThree = undefined
mediaTypeTwo.value = res.data
})
}
// 获取三级媒体类型
const getMediaTypeThree = (value) => {
sysMediaTypeListByPid({ parentId: value }).then(res => {
queryParams.value.mediaTypeThree = undefined
mediaTypeThree.value = res.data
})
}
// 获取省/直辖市数据
const getProvinceList = () => {
sysRegionListByPid({ parentId: '0' }).then(res => {
province.value = res.data
})
}
// 获取地级市/区数据
const getCityList = (value) => {
sysRegionListByPid({ parentId: value }).then(res => {
queryParams.value.cityId = undefined
queryParams.value.countyId = undefined
queryParams.value.townId = undefined
city.value = res.data
})
}
// 获取区/县数据
const getCountyList = (value) => {
sysRegionListByPid({ parentId: value }).then(res => {
queryParams.value.countyId = undefined
queryParams.value.townId = undefined
county.value = res.data
})
}
// 获取镇数据
const getTownList = (value) => {
sysRegionListByPid({ parentId: value }).then(res => {
queryParams.value.townId = undefined
town.value = res.data
})
}
// 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 = {
securityJsCode: secretKey
};
AMapLoader.load({
key: apiKey,
plugins: [],
AMapUI: {
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,
center: [116.397428, 39.90923],
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)
},
// {
// url: createPointStyle('#FFA200', 18, 'circle').toDataURL(),
// anchor: new AMap.Pixel(18, 18),
// size: new AMap.Size(18, 18)
// },
// {
// url: createPointStyle('#00C272', 18, 'circle').toDataURL(),
// anchor: new AMap.Pixel(18, 18),
// size: new AMap.Size(18, 18)
// }
];
// 测试数据
const points = [
{ "lnglat": [116.258446, 37.686622], "name": "景县", "style": 1 },
{ "lnglat": [113.559954, 22.124049], "name": "圣方济各堂区", "style": 1 },
{ "lnglat": [116.366794, 39.915309], "name": "西城区", "style": 1 },
{ "lnglat": [116.486409, 39.921489], "name": "朝阳区", "style": 1 },
{ "lnglat": [116.286968, 39.863642], "name": "丰台区", "style": 1 },
{ "lnglat": [116.195445, 39.914601], "name": "石景山区", "style": 1 },
{ "lnglat": [116.310316, 39.956074], "name": "海淀区", "style": 1 },
{ "lnglat": [116.105381, 39.937183], "name": "门头沟区", "style": 0 },
{ "lnglat": [116.139157, 39.735535], "name": "房山区", "style": 0 },
{ "lnglat": [116.658603, 39.902486], "name": "通州区", "style": 0 },
{ "lnglat": [116.653525, 40.128936], "name": "顺义区", "style": 0 },
{ "lnglat": [116.235906, 40.218085], "name": "昌平区", "style": 0 },
{ "lnglat": [116.338033, 39.728908], "name": "大兴区", "style": 0 },
{ "lnglat": [116.637122, 40.324272], "name": "怀柔区", "style": 0 },
{ "lnglat": [117.112335, 40.144783], "name": "平谷区", "style": 0 },
{ "lnglat": [116.843352, 40.377362], "name": "密云区", "style": 0 },
{ "lnglat": [115.985006, 40.465325], "name": "延庆区", "style": 1 },
{ "lnglat": [113.56925, 22.136546], "name": "路凼填海区", "style": 1 },
{ "lnglat": [117.195907, 39.118327], "name": "和平区", "style": 1 },
{ "lnglat": [117.226568, 39.122125], "name": "河东区", "style": 1 },
{ "lnglat": [117.217536, 39.101897], "name": "河西区", "style": 1 },
{ "lnglat": [117.164143, 39.120474], "name": "南开区", "style": 1 },
{ "lnglat": [117.201569, 39.156632], "name": "河北区", "style": 0 },
{ "lnglat": [117.163301, 39.175066], "name": "红桥区", "style": 1 },
{ "lnglat": [117.313967, 39.087764], "name": "东丽区", "style": 0 },
{ "lnglat": [117.012247, 39.139446], "name": "西青区", "style": 0 },
{ "lnglat": [117.382549, 38.989577], "name": "津南区", "style": 0 },
{ "lnglat": [117.13482, 39.225555], "name": "北辰区", "style": 0 },
{ "lnglat": [117.057959, 39.376925], "name": "武清区", "style": 0 },
{ "lnglat": [116.405285, 39.904989], "name": "北京市", "style": 0 }
]
// 创建MassMarks对象
massMarks.value = new AMap.MassMarks(points, {
opacity: 1,
zIndex: 111,
cursor: 'pointer',
style: styles
});
// 将海量点添加到地图
massMarks.value.setMap(mapInstance.value);
// 添加点击事件
massMarks.value.on('click', function (e) {
console.log('点击了节点', e.data)
});
}
// 隐藏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';
});
}
onUnmounted(() => {
mapInstance.value?.destroy()
})
// 初始化
onMounted(() => {
bgStore.setBgImage(otherbg)
getList()
getMediaTypeOne()
getProvinceList()
loadMap()
});
</script>
<style lang="scss">
.noChoseLabel {
height: 24px;
line-height: 24px;
font-family: Microsoft YaHei;
font-weight: 400;
font-size: 16px;
color: #ffffff90;
}
.mapContainer {
width: 100%;
height: 100%;
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>