1216 lines
41 KiB
Vue
1216 lines
41 KiB
Vue
<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.provinceCode" 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.cityCode" 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.areaCode" 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.townCode" placeholder="请选择" @change="getbusinessAreaList"
|
||
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.businessDistrictId" placeholder="请选择" clearable
|
||
style="min-width: 30px;">
|
||
<el-option v-for="item in businessAreaList" :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.mediaType" 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.mediaCategory" 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.displayForm" 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="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.keyword" @keydown.enter.prevent="handleQuery"
|
||
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" v-hasPermi="['outdoorMedia:query']"
|
||
@click="handleQuery">查询</el-button>
|
||
<el-button type="primary" class="primaryBtn" v-hasPermi="['outdoorMedia:reset']"
|
||
@click="resetQuery">重置</el-button>
|
||
<el-button type="primary" class="primaryBtn" v-hasPermi="['outdoorMedia:exportPPTS']"
|
||
@click="handleExportMore">PPT批量导出</el-button>
|
||
<el-button type="primary" class="primaryBtn" v-hasPermi="['outdoorMedia:clear']"
|
||
@click="handleClearChoseMedia">清空所选媒体</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="10" class="my_row" style="margin-bottom: 0;">
|
||
<el-col :span="12">
|
||
<el-table ref="tableRef" v-loading="loading" :data="outdoorMediaList"
|
||
@selection-change="handleSelectionChange"
|
||
:min-height="unfoldFlag ? 'calc(100vh - 372px)' : 'calc(100vh - 338px)'">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="实景图片" align="left" prop="mediaFileList" width="170">
|
||
<template #default="scope">
|
||
<img v-if="scope.row.mediaFileList.length > 0"
|
||
:src="baseUrl + scope.row.mediaFileList[0].fileName" fit="fill"
|
||
style="width: 138px;height: 78px; border-radius: 4px" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="媒体名称" align="left" prop="mediaName" width="150" />
|
||
<el-table-column label="商圈" align="left" prop="businessDistrictName" width="150" />
|
||
<el-table-column label="媒体大类" align="left" prop="mediaCategoryStr" width="150" />
|
||
<el-table-column label="展示形式" align="left" prop="displayFormStr" 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="handleViewMedia(scope.row.id)"
|
||
v-hasPermi="['outdoorMedia:detail']">查看
|
||
</div>
|
||
<div class="popBtns" @click="handleDownPPT(scope.row)"
|
||
v-hasPermi="['outdoorMedia:exportPPT']">下载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="getOutMediaPageList" />
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<div id="mapContainer" class="mapContainer"></div>
|
||
</el-col>
|
||
</el-row>
|
||
</el-card>
|
||
|
||
<detail ref="detailFormRef" />
|
||
</div>
|
||
</template>
|
||
<script setup name="Post">
|
||
import { onMounted, onUnmounted, nextTick, ref } from 'vue';
|
||
import { Search } from '@element-plus/icons-vue'
|
||
import { sysRegionListByPid } from "@/api/system/administrativeRegion"
|
||
import { sysMediaTypeListByPid } from "@/api/system/mediaType"
|
||
import { busTradingAreaPage } from "@/api/system/businessArea"
|
||
import { outMediaPageList } from "@/api/mediaLibrary"
|
||
import { pptTemplatePage } from "@/api/system/pptTemplate"
|
||
import { exportMediaPPT } from "@/api/mediaLibrary"
|
||
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'
|
||
|
||
import detail from './detail.vue';
|
||
|
||
const bgStore = useBackgroundStore()
|
||
const { proxy } = getCurrentInstance()
|
||
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
||
const { apiKey, secretKey } = window._CONFIG
|
||
const detailFormRef = ref(null)
|
||
// 操作图标
|
||
const defaultImageSrc = ref(optionIcon);
|
||
const hoverImageSrc = ref(optionIconHover);
|
||
// 省、市、县、镇数据
|
||
const province = ref([])
|
||
const city = ref([])
|
||
const county = ref([])
|
||
const town = ref([])
|
||
// 商圈信息列表
|
||
const businessAreaList = ref([])
|
||
// 媒体类型数据
|
||
const mediaTypeOne = ref([])
|
||
const mediaTypeTwo = ref([])
|
||
const mediaTypeThree = ref([])
|
||
// 媒体多选数据
|
||
const multipleChoseArr = ref([])
|
||
// 户外媒体数据
|
||
const outdoorMediaList = ref([])
|
||
// 是否折叠
|
||
const unfoldFlag = ref(false)
|
||
const loading = ref(true)
|
||
const total = ref(0)
|
||
const data = reactive({
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
keyword: undefined,
|
||
mediaType: undefined,
|
||
mediaCategory: undefined,
|
||
displayForm: undefined,
|
||
provinceCode: undefined,
|
||
cityCode: undefined,
|
||
areaCode: undefined,
|
||
townCode: undefined,
|
||
businessDistrictId: undefined
|
||
}
|
||
})
|
||
const { queryParams } = toRefs(data)
|
||
// map实例
|
||
const mapInstance = ref(null)
|
||
const massMarks = ref(null)
|
||
// 当前激活的工具实例
|
||
const activeTool = ref(null)
|
||
// 当前地图模式(2D/3D)
|
||
const mapMode = ref('2D')
|
||
// 是否全屏状态
|
||
const isFullscreen = ref(false)
|
||
const geocoder = ref(null)
|
||
const geocoderMarker = ref(null)
|
||
const provinceName = ref(null)
|
||
const ciytName = ref(null)
|
||
const countyName = ref(null)
|
||
const townName = ref(null)
|
||
// 地图数据点
|
||
const points = ref([])
|
||
// 初始化变量用于存储极值
|
||
let minLng = Infinity; // 经度的最小值(最西边)
|
||
let minLat = Infinity; // 纬度的最小值(最南边)
|
||
let maxLng = -Infinity; // 经度的最大值(最东边)
|
||
let maxLat = -Infinity; // 纬度的最大值(最北边)
|
||
// 添加表格引用
|
||
const tableRef = ref(null)
|
||
// 添加一个标志位来区分是用户操作还是程序设置
|
||
const isSettingSelection = ref(false)
|
||
|
||
// 导出提交信息
|
||
const exportForm = ref({
|
||
mediaIds: undefined,
|
||
templateId: undefined,
|
||
exportFields: [
|
||
'mediaName',
|
||
'address',
|
||
'mediaSize',
|
||
'mediaOrientation',
|
||
'businessDistrictName',
|
||
'dailyAvgVehicleFlow',
|
||
'advantages'
|
||
]
|
||
})
|
||
|
||
// 获取PPT模板ID
|
||
const getpptTemplatePageList = () => {
|
||
pptTemplatePage(queryParams.value).then(res => {
|
||
const pptTemplate = res.data.list.filter(item => item.type == 3)
|
||
if (pptTemplate.length == 1) exportForm.value.templateId = pptTemplate[0].id
|
||
})
|
||
}
|
||
// 下载ppt
|
||
const handleDownPPT = (row) => {
|
||
exportForm.value.mediaIds = [row.id]
|
||
handleSubmitExportPPT()
|
||
}
|
||
// 批量下载PPT
|
||
const handleExportMore = () => {
|
||
if (multipleChoseArr.value.length == 0) {
|
||
proxy.$modal.msgWarning("请选择要导出的媒体!!!")
|
||
return false
|
||
}
|
||
exportForm.value.mediaIds = multipleChoseArr.value.map(item => item.id);
|
||
handleSubmitExportPPT()
|
||
}
|
||
// 导出ppt实现代码
|
||
const handleSubmitExportPPT = () => {
|
||
exportMediaPPT(exportForm.value).then(res => {
|
||
const downLoadName = '媒体信息_' + getCurrentTime() + '.pptx'
|
||
// 通过a标签打开新页面下载文件
|
||
const a = document.createElement('a')
|
||
a.href = URL.createObjectURL(res)
|
||
// a标签里有download属性可以自定义文件名
|
||
a.setAttribute('download', downLoadName)
|
||
document.body.appendChild(a)
|
||
a.click()
|
||
document.body.removeChild(a)
|
||
})
|
||
}
|
||
|
||
const getCurrentTime = () => {
|
||
//获取当前时间并打印
|
||
var getTime = new Date().getTime(); //获取到当前时间戳
|
||
var time = new Date(getTime); //创建一个日期对象
|
||
var year = time.getFullYear(); // 年
|
||
var month = (time.getMonth() + 1).toString().padStart(2, '0'); // 月
|
||
var date = time.getDate().toString().padStart(2, '0'); // 日
|
||
var hour = time.getHours().toString().padStart(2, '0'); // 时
|
||
var minute = time.getMinutes().toString().padStart(2, '0'); // 分
|
||
var second = time.getSeconds().toString().padStart(2, '0'); // 秒
|
||
var gettime = year + month + date + hour + minute + second
|
||
return gettime
|
||
}
|
||
|
||
// 获取一级媒体类型
|
||
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
|
||
})
|
||
ciytName.value = undefined
|
||
countyName.value = undefined
|
||
townName.value = undefined
|
||
// 定位到省中心
|
||
var choseProvince = province.value.filter(item => item.id == value)[0]
|
||
if (choseProvince) {
|
||
provinceName.value = choseProvince.name
|
||
geocoderCityCenter()
|
||
}
|
||
getbusinessAreaList()
|
||
|
||
}
|
||
// 获取区/县数据
|
||
const getCountyList = (value) => {
|
||
sysRegionListByPid({ parentId: value }).then(res => {
|
||
queryParams.value.countyId = undefined
|
||
queryParams.value.townId = undefined
|
||
county.value = res.data
|
||
})
|
||
countyName.value = undefined
|
||
townName.value = undefined
|
||
var choseCity = city.value.filter(item => item.id == value)[0]
|
||
if (choseCity) {
|
||
ciytName.value = choseCity.name
|
||
geocoderCityCenter()
|
||
}
|
||
getbusinessAreaList()
|
||
}
|
||
// 获取镇数据
|
||
const getTownList = (value) => {
|
||
sysRegionListByPid({ parentId: value }).then(res => {
|
||
queryParams.value.townId = undefined
|
||
town.value = res.data
|
||
})
|
||
townName.value = undefined
|
||
var choseCounty = county.value.filter(item => item.id == value)[0]
|
||
if (choseCounty) {
|
||
countyName.value = choseCounty.name
|
||
geocoderCityCenter()
|
||
}
|
||
getbusinessAreaList()
|
||
}
|
||
// 依据省市县镇查询商圈
|
||
const getbusinessAreaList = (val) => {
|
||
const _params = {
|
||
pageIndex: 1,
|
||
pageSize: 200,
|
||
provinceId: queryParams.value.provinceCode,
|
||
cityId: queryParams.value.cityCode,
|
||
countyId: queryParams.value.areaCode,
|
||
townId: queryParams.value.townCode,
|
||
}
|
||
busTradingAreaPage(_params).then(res => {
|
||
if (res.code == 200) {
|
||
businessAreaList.value = res.data.list
|
||
}
|
||
})
|
||
|
||
var choseTown = town.value.filter(item => item.id == val)[0]
|
||
if (choseTown) {
|
||
townName.value = choseTown.name
|
||
geocoderCityCenter()
|
||
}
|
||
}
|
||
|
||
// 查找指定城市中心点,地图移动到中心点 放大视野
|
||
const geocoderCityCenter = () => {
|
||
// 构建查询地址
|
||
var address = provinceName.value;
|
||
if (ciytName.value) address += ciytName.value;
|
||
if (countyName.value) address += countyName.value;
|
||
if (townName.value) address += townName.value;
|
||
|
||
// 使用地理编码获取坐标
|
||
geocoder.value = new AMap.Geocoder({
|
||
city: ciytName.value || provinceName.value
|
||
});
|
||
|
||
geocoder.value.getLocation(address, function (status, result) {
|
||
if (status === 'complete' && result.geocodes.length > 0) {
|
||
var location = result.geocodes[0].location;
|
||
|
||
// 根据行政级别设置缩放级别
|
||
var zoomLevel = getZoomLevel(provinceName.value, ciytName.value, countyName.value, townName.value);
|
||
|
||
// 平滑移动到新位置
|
||
mapInstance.value.setZoomAndCenter(zoomLevel, [location.lng, location.lat], true);
|
||
|
||
// 添加标记
|
||
addMarker([location.lng, location.lat], address);
|
||
|
||
}
|
||
});
|
||
}
|
||
|
||
// 添加标记
|
||
const addMarker = (position, title) => {
|
||
// 清除现有标记
|
||
mapInstance.value.clearMap();
|
||
|
||
// 添加新标记
|
||
geocoderMarker.value = new AMap.Marker({
|
||
position: position,
|
||
title: title
|
||
});
|
||
|
||
mapInstance.value.add(geocoderMarker.value);
|
||
}
|
||
|
||
// 根据行政级别获取缩放级别
|
||
const getZoomLevel = (province, city, county, town) => {
|
||
if (town) return 15; // 乡镇级别
|
||
if (county) return 13; // 区县级别
|
||
if (city) return 11; // 城市级别
|
||
return 8; // 省份级别
|
||
}
|
||
|
||
// 折叠展开
|
||
const handleFlod = () => {
|
||
unfoldFlag.value = !unfoldFlag.value
|
||
}
|
||
// 获取户外媒介列表数据
|
||
const getOutMediaPageList = () => {
|
||
loading.value = true
|
||
outMediaPageList(queryParams.value).then(res => {
|
||
points.value = []
|
||
res.data.rows.forEach(itemPoint => {
|
||
itemPoint.currentImageSrc = defaultImageSrc.value;
|
||
if (itemPoint.mapX && itemPoint.mapX !== '' && itemPoint.mapY && itemPoint.mapY !== '') {
|
||
if (itemPoint.dataScopeDeptId == 220) points.value.push({ "lnglat": [itemPoint.mapX, itemPoint.mapY], "name": itemPoint.mediaName, "mediaId": itemPoint.id, "style": 0 })
|
||
else if (itemPoint.dataScopeDeptId == 219) points.value.push({ "lnglat": [itemPoint.mapX, itemPoint.mapY], "name": itemPoint.mediaName, "mediaId": itemPoint.id, "style": 1 })
|
||
// 同时更新极值
|
||
const lng = itemPoint.mapX;
|
||
const lat = itemPoint.mapY;
|
||
if (lng < minLng) minLng = lng;
|
||
if (lat < minLat) minLat = lat;
|
||
if (lng > maxLng) maxLng = lng;
|
||
if (lat > maxLat) maxLat = lat;
|
||
}
|
||
});
|
||
outdoorMediaList.value = res.data.rows
|
||
total.value = res.data.total
|
||
loading.value = false
|
||
|
||
// 数据加载完成后,设置选中状态
|
||
nextTick(() => {
|
||
setTableSelection()
|
||
// 在地图完全加载后执行点数据处理
|
||
renderMassMarks();
|
||
})
|
||
})
|
||
}
|
||
/** 搜索按钮操作 */
|
||
const handleQuery = () => {
|
||
queryParams.value.pageNum = 1
|
||
getOutMediaPageList()
|
||
}
|
||
/** 重置按钮操作 */
|
||
const resetQuery = () => {
|
||
queryParams.value = {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
keyword: undefined,
|
||
mediaType: undefined,
|
||
mediaCategory: undefined,
|
||
displayForm: undefined,
|
||
provinceCode: undefined,
|
||
cityCode: undefined,
|
||
areaCode: undefined,
|
||
townCode: undefined,
|
||
businessDistrictId: undefined
|
||
}
|
||
if (geocoderMarker.value) {
|
||
mapInstance.value.remove(geocoderMarker.value);
|
||
geocoderMarker.value = null
|
||
}
|
||
handleQuery()
|
||
}
|
||
// 选择媒体事件
|
||
const handleSelectionChange = (selection) => {
|
||
// 如果是程序设置的选中状态,不更新 multipleChoseArr
|
||
if (isSettingSelection.value) {
|
||
return
|
||
}
|
||
|
||
// 获取当前页所有行的ID
|
||
const currentPageIds = outdoorMediaList.value.map(item => item.id)
|
||
|
||
// 如果当前页没有选中的数据,说明是页面切换导致的清空,不处理
|
||
if (selection.length === 0 && currentPageIds.length > 0) {
|
||
// 检查当前页是否有应该选中的数据
|
||
const shouldHaveSelections = currentPageIds.some(id =>
|
||
multipleChoseArr.value.some(item => item.id === id)
|
||
)
|
||
|
||
// 如果当前页有应该选中的数据但selection为空,说明是页面切换,不处理
|
||
if (shouldHaveSelections) {
|
||
return
|
||
}
|
||
}
|
||
|
||
// 移除当前页中已取消选中的项目
|
||
const currentSelectedIds = selection.map(item => item.id)
|
||
multipleChoseArr.value = multipleChoseArr.value.filter(item =>
|
||
!currentPageIds.includes(item.id) || currentSelectedIds.includes(item.id)
|
||
)
|
||
|
||
// 添加新选中的项目(去重)
|
||
selection.forEach(newItem => {
|
||
if (!multipleChoseArr.value.some(item => item.id === newItem.id)) {
|
||
multipleChoseArr.value.push(newItem)
|
||
}
|
||
})
|
||
}
|
||
// 设置表格选中状态 - 完善版本
|
||
const setTableSelection = () => {
|
||
if (tableRef.value && outdoorMediaList.value.length > 0) {
|
||
// 设置标志位,避免触发 handleSelectionChange
|
||
isSettingSelection.value = true
|
||
|
||
// 使用 nextTick 确保DOM已更新
|
||
nextTick(() => {
|
||
// 先清除所有选中状态
|
||
tableRef.value.clearSelection()
|
||
|
||
// 延迟设置选中状态,确保清除操作完成
|
||
setTimeout(() => {
|
||
outdoorMediaList.value.forEach(row => {
|
||
const isSelected = multipleChoseArr.value.some(item => item.id === row.id)
|
||
if (isSelected) {
|
||
tableRef.value.toggleRowSelection(row, true)
|
||
}
|
||
})
|
||
|
||
// 恢复标志位
|
||
setTimeout(() => {
|
||
isSettingSelection.value = false
|
||
}, 100)
|
||
}, 50)
|
||
})
|
||
}
|
||
}
|
||
// 移除选择项
|
||
const handleCloseTag = (tag) => {
|
||
const rowIndex = multipleChoseArr.value.findIndex(item => item.id == tag.id)
|
||
multipleChoseArr.value.splice(rowIndex, 1)
|
||
|
||
// 清除对应行的复选框选中状态
|
||
if (tableRef.value) {
|
||
// 设置标志位,避免触发 handleSelectionChange
|
||
isSettingSelection.value = true
|
||
|
||
// 找到对应的行数据
|
||
const row = outdoorMediaList.value.find(item => item.id === tag.id)
|
||
if (row) {
|
||
tableRef.value.toggleRowSelection(row, false)
|
||
}
|
||
|
||
// 恢复标志位
|
||
setTimeout(() => {
|
||
isSettingSelection.value = false
|
||
}, 100)
|
||
}
|
||
}
|
||
// 清空已选媒体
|
||
const handleClearChoseMedia = () => {
|
||
multipleChoseArr.value = []
|
||
if (tableRef.value) {
|
||
// 设置标志位,避免触发 handleSelectionChange
|
||
isSettingSelection.value = true
|
||
tableRef.value.clearSelection()
|
||
// 恢复标志位
|
||
setTimeout(() => {
|
||
isSettingSelection.value = false
|
||
}, 100)
|
||
}
|
||
}
|
||
// 查看媒体详情
|
||
const handleViewMedia = (_mediaId) => {
|
||
detailFormRef.value.initMediaDetail(_mediaId)
|
||
}
|
||
|
||
|
||
|
||
// 初始化地图
|
||
const loadMap = () => {
|
||
return new Promise((resolve, reject) => {
|
||
// 设置安全密钥
|
||
window._AMapSecurityConfig = {
|
||
securityJsCode: secretKey
|
||
};
|
||
|
||
AMapLoader.load({
|
||
key: apiKey,
|
||
plugins: ["AMap.Geocoder", "AMap.MouseTool"],
|
||
version: '2.0', // 指定API版本
|
||
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,
|
||
pitch: initialPitch, // 倾斜角度决定2D/3D模式
|
||
rotation: initialRotation, // 旋转角度
|
||
zoom: 16,
|
||
zooms: [3, 16],
|
||
viewMode: mapMode.value // 启用3D视图
|
||
});
|
||
|
||
// // 先添加基本控件
|
||
mapInstance.value.setZoom(16);
|
||
|
||
// 添加自定义控件容器
|
||
addCustomControls(AMap);
|
||
|
||
// 地图加载完成后隐藏Logo
|
||
setTimeout(() => {
|
||
hideAmapLogo();
|
||
}, 1000);
|
||
|
||
// 监听地图渲染完成事件
|
||
mapInstance.value.on('render', hideAmapLogo);
|
||
|
||
// 添加缩放变化监听
|
||
mapInstance.value.on('zoomchange', handleZoomChange);
|
||
|
||
//// 在地图完全加载后执行点数据处理
|
||
//renderMassMarks();
|
||
|
||
resolve();
|
||
}).catch(e => {
|
||
console.log(e, "高德地图加载失败");
|
||
reject(e);
|
||
});
|
||
});
|
||
}
|
||
// 处理地图缩放事件
|
||
const handleZoomChange = () => {
|
||
const currentZoomLevel = mapInstance.value.getZoom();
|
||
// 如果缩放级别超过16,自动调整回16
|
||
if (currentZoomLevel > 16) {
|
||
mapInstance.value.setZoom(16);
|
||
return;
|
||
}
|
||
}
|
||
// 辅助函数:结束当前正在使用的鼠标工具
|
||
const stopActiveTool = () => {
|
||
if (activeTool.value !== null) {
|
||
clearAll()
|
||
activeTool.value = null; // 释放引用以便垃圾回收
|
||
}
|
||
};
|
||
// 添加自定义控件
|
||
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;
|
||
|
||
// 工具箱测距按钮
|
||
const toolRangingBtn = document.createElement('div');
|
||
toolRangingBtn.className = 'map-control-btn map-ranging';
|
||
toolRangingBtn.setAttribute('title', '测距');
|
||
toolRangingBtn.onclick = startRuler;
|
||
|
||
// 工具箱测面积按钮
|
||
const toolMeasureAreaBtn = document.createElement('div');
|
||
toolMeasureAreaBtn.className = 'map-control-btn map-measureAreaBtn';
|
||
toolMeasureAreaBtn.setAttribute('title', '测面积');
|
||
toolMeasureAreaBtn.onclick = startMeasureArea;
|
||
|
||
// 工具箱添加点按钮
|
||
const toolPointBtn = document.createElement('div');
|
||
toolPointBtn.className = 'map-control-btn map-addPointBtn';
|
||
toolPointBtn.setAttribute('title', '绘制点');
|
||
toolPointBtn.onclick = startDrawMarker;
|
||
|
||
// 工具箱添加折线按钮
|
||
const toolLineBtn = document.createElement('div');
|
||
toolLineBtn.className = 'map-control-btn map-addLineBtn';
|
||
toolLineBtn.setAttribute('title', '绘制折线');
|
||
toolLineBtn.onclick = startDrawPolyline;
|
||
|
||
// 工具箱添加圆按钮
|
||
const toolCircleBtn = document.createElement('div');
|
||
toolCircleBtn.className = 'map-control-btn map-addCircleBtn';
|
||
toolCircleBtn.setAttribute('title', '绘制圆');
|
||
toolCircleBtn.onclick = startDrawCircle;
|
||
|
||
// 工具箱添加矩形按钮
|
||
const toolRactBtn = document.createElement('div');
|
||
toolRactBtn.className = 'map-control-btn map-addRactBtn';
|
||
toolRactBtn.setAttribute('title', '绘制矩形');
|
||
toolRactBtn.onclick = startDrawRectangle;
|
||
|
||
// 工具箱清除按钮
|
||
const toolClearAllBtn = document.createElement('div');
|
||
toolClearAllBtn.className = 'map-control-btn map-clearAllBtn';
|
||
toolClearAllBtn.setAttribute('title', '清除');
|
||
toolClearAllBtn.onclick = clearAll;
|
||
|
||
controlContainer.appendChild(toggle2D3DBtn);
|
||
controlContainer.appendChild(fullscreenBtn);
|
||
controlContainer.appendChild(toolRangingBtn);
|
||
controlContainer.appendChild(toolMeasureAreaBtn);
|
||
controlContainer.appendChild(toolPointBtn);
|
||
controlContainer.appendChild(toolLineBtn);
|
||
controlContainer.appendChild(toolCircleBtn);
|
||
controlContainer.appendChild(toolRactBtn);
|
||
controlContainer.appendChild(toolClearAllBtn);
|
||
|
||
// 添加到地图容器
|
||
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';
|
||
});
|
||
}
|
||
}
|
||
}
|
||
// 测距方法
|
||
const startRuler = () => {
|
||
if (!mapInstance.value) return;
|
||
stopActiveTool();
|
||
activeTool.value = new AMap.MouseTool(mapInstance.value);
|
||
activeTool.value.rule({
|
||
startMarkerOptions: {//可缺省
|
||
icon: new AMap.Icon({
|
||
size: new AMap.Size(19, 31),//图标大小
|
||
imageSize: new AMap.Size(19, 31),
|
||
image: "//webapi.amap.com/theme/v1.3/markers/b/start.png"
|
||
}),
|
||
offset: new AMap.Pixel(-9, -31)
|
||
},
|
||
endMarkerOptions: {//可缺省
|
||
icon: new AMap.Icon({
|
||
size: new AMap.Size(19, 31),//图标大小
|
||
imageSize: new AMap.Size(19, 31),
|
||
image: "//webapi.amap.com/theme/v1.3/markers/b/end.png"
|
||
}),
|
||
offset: new AMap.Pixel(-9, -31)
|
||
},
|
||
midMarkerOptions: {//可缺省
|
||
icon: new AMap.Icon({
|
||
size: new AMap.Size(19, 31),//图标大小
|
||
imageSize: new AMap.Size(19, 31),
|
||
image: "//webapi.amap.com/theme/v1.3/markers/b/mid.png"
|
||
}),
|
||
offset: new AMap.Pixel(-9, -31)
|
||
},
|
||
lineOptions: {//可缺省
|
||
strokeStyle: "solid",
|
||
strokeColor: "#FF33FF",
|
||
strokeOpacity: 1,
|
||
strokeWeight: 2
|
||
}
|
||
});
|
||
}
|
||
// 测面积方法
|
||
const startMeasureArea = () => {
|
||
if (!mapInstance.value) return;
|
||
stopActiveTool();
|
||
activeTool.value = new AMap.MouseTool(mapInstance.value);
|
||
activeTool.value.measureArea({
|
||
strokeColor: '#80d8ff',
|
||
fillColor: '#80d8ff',
|
||
fillOpacity: 0.3
|
||
//同 Polygon 的 Option 设置
|
||
});
|
||
}
|
||
// 添加点
|
||
const startDrawMarker = () => {
|
||
if (!mapInstance.value) return;
|
||
stopActiveTool();
|
||
activeTool.value = new AMap.MouseTool(mapInstance.value);
|
||
activeTool.value.marker({
|
||
//同Marker的Option设置
|
||
});
|
||
}
|
||
// 添加线
|
||
const startDrawPolyline = () => {
|
||
if (!mapInstance.value) return;
|
||
stopActiveTool();
|
||
activeTool.value = new AMap.MouseTool(mapInstance.value);
|
||
activeTool.value.polyline({
|
||
strokeColor: '#80d8ff'
|
||
//同Polyline的Option设置
|
||
});
|
||
}
|
||
// 添加圆
|
||
const startDrawCircle = () => {
|
||
if (!mapInstance.value) return;
|
||
stopActiveTool();
|
||
activeTool.value = new AMap.MouseTool(mapInstance.value);
|
||
activeTool.value.circle({
|
||
fillColor: '#00b0ff',
|
||
strokeColor: '#80d8ff'
|
||
//同Circle的Option设置
|
||
});
|
||
}
|
||
// 画矩形
|
||
const startDrawRectangle = () => {
|
||
if (!mapInstance.value) return;
|
||
stopActiveTool();
|
||
activeTool.value = new AMap.MouseTool(mapInstance.value);
|
||
activeTool.value.rectangle({
|
||
fillColor: '#00b0ff',
|
||
strokeColor: '#80d8ff'
|
||
//同Polygon的Option设置
|
||
});
|
||
}
|
||
// 清除
|
||
const clearAll = () => {
|
||
activeTool.value.close(true)//关闭,并清除覆盖物
|
||
}
|
||
// 监听全屏变化事件
|
||
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)
|
||
},
|
||
];
|
||
|
||
// 如果有点存在,则创建 MassMarks 实例
|
||
if (points.value.length > 0 && mapInstance.value) {
|
||
// 创建MassMarks对象
|
||
massMarks.value = new AMap.MassMarks(points.value, {
|
||
opacity: 1,
|
||
zIndex: 111,
|
||
cursor: 'pointer',
|
||
style: styles
|
||
});
|
||
|
||
// 将海量点添加到地图
|
||
massMarks.value.setMap(mapInstance.value);
|
||
|
||
// 添加点击事件
|
||
massMarks.value.on('click', function (e) {
|
||
handleViewMedia(e.data.mediaId)
|
||
});
|
||
|
||
// ==================================================
|
||
// 新增:计算边界并设置地图视野
|
||
// ==================================================
|
||
const southWest = new AMap.LngLat(minLng, minLat); // 西南角
|
||
const northEast = new AMap.LngLat(maxLng, maxLat); // 东北角
|
||
const bounds = new AMap.Bounds(southWest, northEast);
|
||
|
||
// 设置地图显示范围,会自动适配最佳视图
|
||
mapInstance.value.setBounds(bounds);
|
||
}
|
||
|
||
}
|
||
// 隐藏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(() => {
|
||
stopActiveTool()
|
||
if (mapInstance.value) {
|
||
mapInstance.value.destroy();
|
||
}
|
||
})
|
||
// 初始化
|
||
onMounted(() => {
|
||
bgStore.setBgImage(otherbg)
|
||
loadMap()
|
||
// 获取模板id
|
||
getpptTemplatePageList()
|
||
getOutMediaPageList()
|
||
getMediaTypeOne()
|
||
getProvinceList()
|
||
});
|
||
</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');
|
||
}
|
||
|
||
.map-ranging {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapRanging.png');
|
||
}
|
||
|
||
.map-ranging:hover {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapRanginghover.png');
|
||
}
|
||
|
||
.map-measureAreaBtn {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapArea.png');
|
||
}
|
||
|
||
.map-measureAreaBtn:hover {
|
||
background: url('../../assets/images/mapAreaHover.png');
|
||
}
|
||
|
||
.map-addPointBtn {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapPoint.png');
|
||
}
|
||
|
||
.map-addPointBtn:hover {
|
||
background: url('../../assets/images/mapPointHover.png');
|
||
}
|
||
|
||
.map-addLineBtn {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapLine.png');
|
||
}
|
||
|
||
.map-addLineBtn:hover {
|
||
background: url('../../assets/images/mapLineHover.png');
|
||
}
|
||
|
||
.map-addCircleBtn {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapCircle.png');
|
||
}
|
||
|
||
.map-addCircleBtn:hover {
|
||
background: url('../../assets/images/mapCircleHover.png');
|
||
}
|
||
|
||
.map-addRactBtn {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapRect.png');
|
||
}
|
||
|
||
.map-addRactBtn:hover {
|
||
background: url('../../assets/images/mapRectHover.png');
|
||
}
|
||
|
||
.map-clearAllBtn {
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url('../../assets/images/mapClearAll.png');
|
||
}
|
||
|
||
.map-clearAllBtn:hover {
|
||
background: url('../../assets/images/mapClearAllHover.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> |