Merge branch 'main' of http://123.132.248.154:10000/gitY/LanLingXiangMu
commit
96d1b934e2
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/locales",
|
||||
"src/locales/lang",
|
||||
"public/resource/tinymce/langs"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<Converge />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import Converge from '@/views/sys/exception/Converge.vue';
|
||||
</script>
|
||||
|
|
@ -273,7 +273,7 @@ watchEffect(() => {
|
|||
const MapboxComponent = ref();
|
||||
const countyId = ref();
|
||||
const defaultColor = ref('#FFFFFF');
|
||||
const layerCenterShow = ref(false);
|
||||
const layerCenterShow = ref(true);
|
||||
function getLayerSettings() {
|
||||
let title = '';
|
||||
switch (subject.value) {
|
||||
|
|
@ -325,7 +325,8 @@ watchEffect(() => {
|
|||
break;
|
||||
}
|
||||
getConfig({
|
||||
code: title,
|
||||
// code: title,
|
||||
code:"Subject_WFYD",
|
||||
}).then((res) => {
|
||||
let obj = JSON.parse(res.codeValue);
|
||||
layerSettings.value = obj[0];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,318 @@
|
|||
<template>
|
||||
<a-spin size="large" tip="图层加载中..." :spinning="spinning" wrapperClassName="solid-spin">
|
||||
<div class="statistical" id="bg-pan" ref="container">
|
||||
<Map
|
||||
style="
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: calc(100vh - 80px);
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
"
|
||||
ref="MapboxComponent"
|
||||
@handlerGetDetails="handlerGetDetails"
|
||||
@changeLoading="changeLoading"
|
||||
/>
|
||||
<div class="legend">
|
||||
<div class="legend-item" v-for="(item, index) in legends" :key="index">
|
||||
<div class="legend-dot" :style="{ background: item.color }"></div>
|
||||
<div class="legend-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer-center-container"
|
||||
:style="style">
|
||||
<div class="drag-area"
|
||||
@mousedown="startDrag"></div>
|
||||
<LayerCenter
|
||||
@changeLayer="changeLayer"
|
||||
@changeLabel="changeLabel"
|
||||
ref="LayerCenterComponent"
|
||||
></LayerCenter>
|
||||
</div>
|
||||
|
||||
<a-modal
|
||||
title="详情"
|
||||
width="100%"
|
||||
wrap-class-name="full-modal"
|
||||
v-model:open="showInfoOpen"
|
||||
:footer="null"
|
||||
:destroyOnClose="true">
|
||||
<IdleWatersInsertModal v-if="showInfoType == 5" :showInfoData="showInfoData" :showInfoFlyToPoint="showInfoFlyToPoint" :isRead="true"/>
|
||||
<IdleLandInsertModal v-if="showInfoType == 3" :showInfoData="showInfoData" :showInfoFlyToPoint="showInfoFlyToPoint" :isRead="true"/>
|
||||
<IdleHouseInsertModal v-if="showInfoType == 2" :showInfoData="showInfoData" :showInfoFlyToPoint="showInfoFlyToPoint" :isRead="true"/>
|
||||
<IdleFarmingInsertModal v-if="showInfoType == 1" :showInfoData="showInfoData" :showInfoFlyToPoint="showInfoFlyToPoint" :isRead="true"/>
|
||||
<IdleNongjiInsertModal v-if="showInfoType == 4" :showInfoData="showInfoData" :showInfoFlyToPoint="showInfoFlyToPoint" :isRead="true"/>
|
||||
</a-modal>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import Map from './Converge/index.vue';
|
||||
import useDrag from './drag';
|
||||
import LayerCenter from './mapComponent/layers/index.vue';
|
||||
import { getDetail } from '@/api/tiankongdi/index'
|
||||
import IdleWatersInsertModal from '@/views/demo/IdleWaters/InsertModal.vue'
|
||||
import IdleLandInsertModal from '@/views/demo/IdleLand/InsertModal.vue'
|
||||
import IdleHouseInsertModal from '@/views/demo/IdleHouse/InsertModal.vue'
|
||||
import IdleFarmingInsertModal from '@/views/demo/IdleFarming/InsertModal.vue'
|
||||
import IdleNongjiInsertModal from '@/views/demo/IdleNongji/InsertModal.vue'
|
||||
|
||||
const { style, startDrag } = useDrag();
|
||||
|
||||
const MapboxComponent = ref();
|
||||
const showInfoData = ref()
|
||||
const showInfoType = ref()
|
||||
const showInfoFlyToPoint = ref()
|
||||
const showInfoOpen = ref(false)
|
||||
const spinning = ref(false)
|
||||
const legends = ref([
|
||||
{ label:'房屋', color: '#F70303' },
|
||||
{ label:'土地资源', color: '#0AF703' },
|
||||
{ label:'水域', color: '#0382F7' },
|
||||
{ label:'农业生产设施', color: '#F4E004' },
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
spinning.value = true
|
||||
})
|
||||
const changeLoading = (type: boolean) => {
|
||||
spinning.value = type
|
||||
}
|
||||
function changeLayer(layerList) {
|
||||
MapboxComponent.value.handlerChangeLayer(layerList);
|
||||
}
|
||||
const changeLabel = (type) => {
|
||||
MapboxComponent.value.changeTiandituLabelOpen(type);
|
||||
}
|
||||
const handlerGetDetails = (item, flyPoint) => {
|
||||
console.log('item',item)
|
||||
showInfoType.value = item.Type
|
||||
getDetail( {id:item.Id} , item.Type).then(res => {
|
||||
console.log('res',res)
|
||||
showInfoData.value = res
|
||||
showInfoOpen.value = true
|
||||
showInfoFlyToPoint.value = flyPoint
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.full-modal {
|
||||
.ant-modal {
|
||||
min-width: 100vw;
|
||||
top: 0px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
.ant-modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.ant-modal-body {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.statistical{
|
||||
|
||||
:deep(.ant-modal-header){
|
||||
padding: 0px;
|
||||
}
|
||||
:deep(.ant-modal-wrap){
|
||||
overflow: hidden;
|
||||
pointer-events:none;
|
||||
}
|
||||
:deep(.dragModal){
|
||||
margin: 0 !important;
|
||||
.hidden-show-icon{
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-size: 100% 100%;
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.statistical {
|
||||
// position: relative;
|
||||
// 页面不能被选中
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE/Edge */
|
||||
user-select: none;
|
||||
width:100%;
|
||||
height:calc(-80px + 100vh);
|
||||
position:relative;
|
||||
}
|
||||
|
||||
#bg-pan {
|
||||
|
||||
}
|
||||
#alertOverlay::before,
|
||||
#alertOverlay::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#alertOverlay div::before,
|
||||
#alertOverlay div::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#alertOverlay::before {
|
||||
background: linear-gradient(to right, rgba(0, 0, 0, 0.8), transparent);
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
#alertOverlay::after {
|
||||
background: linear-gradient(to left, rgba(0, 0, 0, 0.8), transparent);
|
||||
top: 0%;
|
||||
left: 100%;
|
||||
transform: rotate(0deg) translate(calc(-1 * 50px), 0px);
|
||||
}
|
||||
|
||||
#alertOverlay div::before {
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
#alertOverlay div::after {
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: rotate(0deg) translate(0px, calc(-1 * 50px));
|
||||
}
|
||||
|
||||
#alertOverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.alert-active {
|
||||
animation: blink 0s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
||||
width: 220px;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 30px;
|
||||
border-radius: 8px;
|
||||
background-image: url('/statistical/left_statistical.png');
|
||||
background-size: 100% 100%;
|
||||
.legend-item {
|
||||
width: 120px;
|
||||
padding: 5px 0px;
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
display: flex;
|
||||
flex: 48% 48%;
|
||||
color: #7ebbff;
|
||||
.legend-dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.legend-label {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
.flh-item{
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
/**图层控制 视频监控**/
|
||||
.layer-center-container {
|
||||
width:285px;
|
||||
position: absolute;
|
||||
}
|
||||
.drag-area{
|
||||
width:245px;
|
||||
height:40px;
|
||||
position:absolute;
|
||||
top:0px;
|
||||
left:0px;
|
||||
z-index:99999;
|
||||
cursor:move;
|
||||
}
|
||||
.TC-videoi-container {
|
||||
position: absolute;
|
||||
bottom: 48px;
|
||||
right: 38px;
|
||||
width: 418px;
|
||||
height: 300px;
|
||||
.close-button {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
text-align: center;
|
||||
line-height: 28px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
z-index: 999999;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.selection-button {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 20px;
|
||||
left: 220px;
|
||||
width: 140px;
|
||||
height: 40px;
|
||||
margin-left: 20px;
|
||||
background-image: url(/map/change-view-btn.png);
|
||||
background-size: 100% 100%;
|
||||
color: #efefef;
|
||||
text-align: center;
|
||||
line-height: 46px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.solid-spin.ant-spin-nested-loading > .ant-spin-blur::after {
|
||||
opacity: 1;
|
||||
}
|
||||
.solid-spin.ant-spin-nested-loading .ant-spin{
|
||||
max-height: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [118.429056,35.387537]
|
||||
},
|
||||
"properties": {
|
||||
"name":"沂南县砖埠镇东岳庄村北可见光",
|
||||
"playUrl":"http://221.2.83.254:7012/live/37130100181328000001.m3u8"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,389 @@
|
|||
<template>
|
||||
<div class="search-container">
|
||||
<div class="search-input">
|
||||
<a-input
|
||||
placeholder="请输入资源编号或地址"
|
||||
v-model:value="keyword"
|
||||
allow-clear
|
||||
@blur="onInputBlue"
|
||||
></a-input>
|
||||
</div>
|
||||
<div class="search-button" @click="searchArea">
|
||||
<img src="/statistical/search-btn.png" alt="" />
|
||||
</div>
|
||||
|
||||
<div class="search-result-container" v-if="showSearchResult">
|
||||
<div class="result-item" v-for="(item, index) in searchResult" @click="toPosition(item)">
|
||||
<div class="search-icon">
|
||||
<img src="/statistical/search-icon.png" alt="" />
|
||||
</div>
|
||||
<div class="level-0">{{ item.type?item.serialNumber: item.id }}</div>
|
||||
<div class="level-1"></div>
|
||||
<div class="level-2">{{ item.type? getTypeName(item.type): `${item.county} ${item.street}` }}</div>
|
||||
</div>
|
||||
<a-empty v-if="searchResult.length == 0" />
|
||||
</div>
|
||||
<!-- <div class="filter-container">
|
||||
<div class="filter-name">{{ currentFilter }}</div>
|
||||
<div class="filter-icon" @click="handlerChangeFilterOptions">
|
||||
<CaretDownOutlined />
|
||||
</div>
|
||||
<div class="filter-item-container" v-if="showFilterOptions">
|
||||
<div
|
||||
class="filter-item"
|
||||
v-for="(item, index) in filters"
|
||||
:key="index"
|
||||
@click="handlerCheckedFilter(item.name)"
|
||||
>{{ item.name }}</div
|
||||
>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, defineExpose, defineEmits, watch } from 'vue';
|
||||
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons-vue';
|
||||
import axios from 'axios';
|
||||
// import { getLoadDroneCaseInfoDetail, getPolygonCenter } from '@/api/tiankongdi/index';
|
||||
// import { homePageLoadCaseInfoTuBanList } from '@/api/demo/system';
|
||||
// import { getLoadCaseInfoMineralsTuBanList } from '@/api/minerals/index';
|
||||
import { WktToGeojson } from '@/components/MapboxMaps/src/WktGeojsonTransform';
|
||||
import { transformGCJ2WGS } from '@/utils/EpsgTransform';
|
||||
import { getPageList, getPolygonCenter } from '@/api/tiankongdi/index'
|
||||
|
||||
const emits = defineEmits(['toPosition', 'handlerFilter', 'flyToLocation']);
|
||||
const props = defineProps(['layer', 'layerSettings']);
|
||||
watch(
|
||||
() => props.layerSettings,
|
||||
() => {
|
||||
subjectName.value = props.layerSettings.subjectname;
|
||||
subjectTable.value = props.layerSettings.tablename;
|
||||
Object.keys(props.layerSettings.legend[0]).forEach((item) => {
|
||||
if (item != '默认') {
|
||||
filters.value.push({ name: item });
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
const subjectName = ref();
|
||||
const subjectTable = ref();
|
||||
async function toPosition(item) {
|
||||
if (item.location) {
|
||||
let newCoord = transformGCJ2WGS(item.location[1], item.location[0]);
|
||||
if (newCoord) {
|
||||
emits('toPosition', [newCoord.lon, newCoord.lat]);
|
||||
}
|
||||
return
|
||||
}
|
||||
let filter = '"Id"=\'' + item.id + "'";
|
||||
let point = await getPolygonCenter({ tablename: 'view_shp_idle', filter: filter });
|
||||
console.log('point',point)
|
||||
if (point.length > 0) {
|
||||
try {
|
||||
let geojson = WktToGeojson(point[0].centroid_point);
|
||||
emits('toPosition', geojson.coordinates);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
const searchResult = ref<any>([]);
|
||||
|
||||
const filters: any = ref([
|
||||
{
|
||||
name: '全部',
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
const showSearchResult = ref(false);
|
||||
const showFilterOptions = ref(false);
|
||||
const address = ref()
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
function onInputFocus() {
|
||||
showSearchResult.value = true;
|
||||
}
|
||||
|
||||
function onInputBlue() {
|
||||
showSearchResult.value = false;
|
||||
}
|
||||
async function searchAddress(){
|
||||
axios({
|
||||
method: 'get',
|
||||
url: `https://restapi.amap.com/v3/geocode/geo?key=ed310f0b1f6cfd93edfba42f1a09d4d9&address=`+address.value,
|
||||
}).then((res) => {
|
||||
if(res.data){
|
||||
let location = res.data.geocodes[0].location.split(',')
|
||||
emits('flyToLocation', location);
|
||||
}
|
||||
});
|
||||
}
|
||||
const keyword = ref('');
|
||||
|
||||
async function searchArea() {
|
||||
if (keyword.value.match(/^[0-9A-Z]+(-[0-9A-Z]+)*$/)) {
|
||||
searchResult.value = [];
|
||||
const params = { page: 1, limit: 10, key: keyword.value};
|
||||
const types = [1,2,3,5]
|
||||
Promise.all(types.map(type => getPageList(params, type)))
|
||||
.then(results => {
|
||||
console.log('所有请求成功', results);
|
||||
for(let index = 0; index < results.length; index ++){
|
||||
let type = 0
|
||||
switch(index){
|
||||
case 0:
|
||||
type = 1;
|
||||
break
|
||||
case 1:
|
||||
type = 2;
|
||||
break
|
||||
case 2:
|
||||
type = 3;
|
||||
break
|
||||
case 3:
|
||||
type = 5;
|
||||
break
|
||||
}
|
||||
results[index].items.forEach(item => {
|
||||
searchResult.value.push({...item,type:type})
|
||||
})
|
||||
}
|
||||
console.log('searchResult',searchResult.value)
|
||||
showSearchResult.value = true;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('至少有一个请求失败', error);
|
||||
});
|
||||
} else {
|
||||
// 地名搜索
|
||||
axios
|
||||
.get(
|
||||
'https://restapi.amap.com/v5/place/text?&key=ee7f561fae9249aeb971bcc661083438&keywords=' +
|
||||
keyword.value +
|
||||
'®ion=371300&citylimit=true&page_num=1&page_size=10',
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.data.info == 'OK') {
|
||||
searchResult.value = [];
|
||||
res.data?.pois?.forEach((item, index) => {
|
||||
let obj = {
|
||||
id: item.name,
|
||||
county: item.cityname,
|
||||
street: item.adname,
|
||||
location: item.location?.split(','),
|
||||
};
|
||||
searchResult.value.push(obj);
|
||||
});
|
||||
showSearchResult.value = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => keyword.value,
|
||||
(newVal) => {
|
||||
if (!newVal) {
|
||||
showSearchResult.value = false;
|
||||
searchResult.value = [];
|
||||
}
|
||||
},
|
||||
);
|
||||
const getTypeName = (type) => {
|
||||
switch(type){
|
||||
case 3:
|
||||
return '土地资源'
|
||||
case 2:
|
||||
return '房屋'
|
||||
case 1:
|
||||
return '农业生产设施'
|
||||
case 5:
|
||||
return '水域'
|
||||
}
|
||||
}
|
||||
// 抛出函数
|
||||
defineExpose({});
|
||||
</script>
|
||||
|
||||
<style type="less" scoped>
|
||||
.addressbox{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background-image: url('/statistical/search-bg.png');
|
||||
background-size: 100% 100%;
|
||||
border-top-right-radius: 8px;
|
||||
position: relative;
|
||||
.search-input {
|
||||
flex: auto;
|
||||
height: 100%;
|
||||
}
|
||||
.search-button {
|
||||
width: 62px;
|
||||
height: 36px;
|
||||
border-top-right-radius: 8px;
|
||||
background: #0a62c6;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background-image: url('/statistical/search-bg.png');
|
||||
background-size: 100% 100%;
|
||||
border-top-right-radius: 8px;
|
||||
position: relative;
|
||||
.search-input {
|
||||
flex: auto;
|
||||
height: 100%;
|
||||
}
|
||||
.search-button {
|
||||
width: 62px;
|
||||
height: 36px;
|
||||
border-top-right-radius: 8px;
|
||||
background: #0a62c6;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.search-result-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 36px;
|
||||
left: 0px;
|
||||
background: rgba(11, 39, 68, 0.8);
|
||||
border: 1px solid #0a62c6;
|
||||
border-top: 0px;
|
||||
padding: 8px 0px;
|
||||
.result-item {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
color: #e1ecf8;
|
||||
display: flex;
|
||||
line-height: 38px;
|
||||
padding: 0px 12px;
|
||||
font-size: 14px;
|
||||
.search-icon {
|
||||
width: 28px;
|
||||
height: 36px;
|
||||
}
|
||||
.level-0 {
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.level-1 {
|
||||
margin: 0px 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.level-2 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: #0f3863;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: -120px;
|
||||
width: 100px;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
background: #0a62c6;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.filter-name {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
padding: 0px 2px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
&::after {
|
||||
content: '';
|
||||
width: 2px;
|
||||
height: 22px;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 0px;
|
||||
background: #0751a5;
|
||||
}
|
||||
}
|
||||
.filter-icon {
|
||||
width: 30px;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
.filter-item-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background: rgba(11, 39, 68, 0.8);
|
||||
border: 1px solid #0a62c6;
|
||||
top: 36px;
|
||||
left: 0px;
|
||||
.filter-item {
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: #0f3863;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .mapboxgl-ctrl-logo {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
::v-deep .ant-input {
|
||||
background: none;
|
||||
border: 0px;
|
||||
color: #5e90e1;
|
||||
line-height: 30px;
|
||||
text-indent: 8px;
|
||||
outline: none;
|
||||
&::placeholder {
|
||||
color: #5e90e1;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .ant-input-affix-wrapper {
|
||||
background: none;
|
||||
border: 0px;
|
||||
color: #5e90e1;
|
||||
line-height: 30px;
|
||||
text-indent: 8px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
::v-deep .anticon svg {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep .ant-input::placeholder {
|
||||
color: #5e90e1;
|
||||
}
|
||||
|
||||
::v-deep .ant-empty-description {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
export const MAPBOX_TOKEN = "pk.eyJ1IjoibGllYmFvIiwiYSI6ImNsMXg1OHdtcTE3eDEza3FmODBmeXhldmIifQ.CYYMuikQnGHMtTNq60B_xA";
|
||||
|
||||
export const TINADITU_TOKEN = "b6585bc41ee16251dbe6b1af64f375d9";
|
||||
|
||||
export const MAP_VIEWER = {
|
||||
"兰山区":{
|
||||
center:{lng: 118.35182370979693, lat: 35.183090621691385},
|
||||
zoom:10.048587811931847
|
||||
},
|
||||
"河东区":{
|
||||
center:{lng: 118.59697279346507, lat: 35.18490235060048},
|
||||
zoom:10,
|
||||
},
|
||||
"罗庄区":{
|
||||
center:{lng: 118.32214269732968, lat: 34.88793861991461},
|
||||
zoom:10.348587811931848,
|
||||
},
|
||||
"高新区":{
|
||||
center:{lng: 118.22321338283429, lat: 35.02848900013085},
|
||||
zoom:11.197175623863698,
|
||||
},
|
||||
"沂河新区":{
|
||||
center:{lng: 118.56406202558459, lat: 35.02402208121399},
|
||||
zoom:9.848587811931848,
|
||||
},
|
||||
"费县":{
|
||||
center:{lng: 118.07423584053117, lat: 35.25200708547465},
|
||||
zoom:9.700000000000005,
|
||||
},
|
||||
"平邑县":{
|
||||
center:{lng: 117.7943998613617, lat: 35.397797381650626},
|
||||
zoom:9.5,
|
||||
},
|
||||
"蒙阴县":{
|
||||
center:{lng: 118.16071230795704, lat: 35.706258658571166},
|
||||
zoom:9.500000000000002,
|
||||
},
|
||||
"沂水县":{
|
||||
center:{lng: 118.7262955596875, lat: 35.875369642007094},
|
||||
zoom:9.500000000000002,
|
||||
},
|
||||
"沂南县":{
|
||||
center:{lng: 118.45908849919756, lat: 35.49273684421736},
|
||||
zoom:9.748587811931854,
|
||||
},
|
||||
"兰陵县":{
|
||||
center:{lng: 118.10841982910361, lat: 34.81986176408728},
|
||||
zoom:9.597175623863698,
|
||||
},
|
||||
"郯城县":{
|
||||
center:{lng: 118.32120708234048, lat: 34.635986538650336},
|
||||
zoom:9.7,
|
||||
},
|
||||
"临沭县":{
|
||||
center:{lng: 118.74187031029359, lat: 34.850952753798644},
|
||||
zoom:9.848587811931848,
|
||||
},
|
||||
"莒南县":{
|
||||
center:{lng: 118.96807389575793, lat: 35.186365164136504},
|
||||
zoom:9.848587811931848,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,498 @@
|
|||
<template>
|
||||
<div :id="mapContainerName" class="map-container">
|
||||
<div class="search-container-box">
|
||||
<SearchComponent
|
||||
@flyToLocation="flyToLocation"
|
||||
@toPosition="toPosition"
|
||||
@handlerFilter="handlerFilter"
|
||||
:layerSettings="layerSettings"
|
||||
></SearchComponent>
|
||||
</div>
|
||||
<div class="home-button" @click="handlerInitialize"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, defineExpose, defineEmits, defineProps, watch } from 'vue';
|
||||
import { generateUUID } from '@/components/MapboxMaps/src/tool';
|
||||
import { WktToGeojson } from '@/components/MapboxMaps/src/WktGeojsonTransform';
|
||||
import mapboxgl, { Map, Popup } from 'mapbox-gl';
|
||||
import { MAPBOX_TOKEN, TINADITU_TOKEN, MAP_VIEWER } from './config.js';
|
||||
import axios from 'axios';
|
||||
import SearchComponent from './SearchComponent.vue';
|
||||
import { getAppEnvConfig } from '@/utils/env';
|
||||
const { VITE_GLOB_API_URL,VITE_GLOB_LAN_API_URL } = getAppEnvConfig();
|
||||
const VITE_GLOB_API_URL_VAR = ref<String>(VITE_GLOB_API_URL);
|
||||
const mapContainerName = ref<String>();
|
||||
mapContainerName.value = 'mapContainer-' + generateUUID();
|
||||
const { VITE_GLOB_YINGXIANG_SERVER, VITE_GLOB_YAOGANYINGXIANG_SERVER } = getAppEnvConfig();
|
||||
import { getUserOrgs } from '@/api/tiankongdi';
|
||||
|
||||
const networkType = ref("WAN");
|
||||
|
||||
|
||||
//
|
||||
import { TableOutlined, GlobalOutlined, HomeOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
// 图层控制 历史影像
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import U from 'mapbox-gl-utils';
|
||||
import { MP } from './src/MP.js';
|
||||
import { GeojsonToWkt } from './src/WktGeojsonTransform.js';
|
||||
import { layers } from '@/views/sys/exception/util'
|
||||
|
||||
const emits = defineEmits([
|
||||
'onload',
|
||||
'handlerGetDetails',
|
||||
'showMonitor',
|
||||
'handlerQueryIntersectTif',
|
||||
'changeLoading'
|
||||
]);
|
||||
|
||||
let isZoomVisible: any = false;
|
||||
let map: Map;
|
||||
let mp: any = null;
|
||||
const props = defineProps({
|
||||
layer: Object,
|
||||
defaultColor: String,
|
||||
});
|
||||
const layerSettings = ref<any>({});
|
||||
const layerDefaultColor = ref<String>();
|
||||
watch(
|
||||
() => props.layer,
|
||||
(val) => {
|
||||
layerSettings.value = val;
|
||||
layerDefaultColor.value = props.defaultColor;
|
||||
},
|
||||
);
|
||||
const initMap = () => {
|
||||
return new mapboxgl.Map({
|
||||
container: mapContainerName.value,
|
||||
language: 'zh-cmn',
|
||||
projection: 'globe', // wgs84参考系
|
||||
style: {
|
||||
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
|
||||
version: 8,
|
||||
sources: {
|
||||
'raster-tiles': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
`http://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${TINADITU_TOKEN}`,
|
||||
],
|
||||
tileSize: 256,
|
||||
minzoom: 1,
|
||||
maxzoom: 17,
|
||||
},
|
||||
'raster-tiles-font': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
`https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${TINADITU_TOKEN}`,
|
||||
],
|
||||
tileSize: 256,
|
||||
}
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: 'tdt-vec-tiles',
|
||||
type: 'raster',
|
||||
source: 'raster-tiles-font',
|
||||
maxZoom: 32,
|
||||
},
|
||||
{
|
||||
id: 'tdt-img-tiles',
|
||||
type: 'raster',
|
||||
source: 'raster-tiles',
|
||||
maxZoom: 32,
|
||||
}
|
||||
// {
|
||||
// id: 'tdt-wms-tiles',
|
||||
// type: 'raster',
|
||||
// source: 'raster-tiles-geo',
|
||||
// },
|
||||
],
|
||||
},
|
||||
maxZoom: 22,
|
||||
minZoom: 8,
|
||||
zoom: 8,
|
||||
pitch: 0,
|
||||
center: [118.30207505530701, 35.30123435040745],
|
||||
});
|
||||
};
|
||||
|
||||
function loadMapInfo(){
|
||||
|
||||
map = initMap();
|
||||
|
||||
map.on('load', () => {
|
||||
//挂载mapbox-gl-utils
|
||||
U.init(map);
|
||||
mp = new MP(map);
|
||||
// 设置2D地图
|
||||
map.easeTo({ pitch: 0, bearing: 0, duration: 1000 });
|
||||
map.dragRotate.disable(); // 禁用拖动旋转
|
||||
map.pitchWithRotate = false; // 禁用旋转时俯仰变化
|
||||
handlerDealStreet()
|
||||
handlerDealCountry();
|
||||
handlerCheckUserOrgs()
|
||||
getMaskData()
|
||||
const sources = map.getStyle().sources;
|
||||
let loadedSources = 0;
|
||||
Object.keys(sources).forEach(sourceId => {
|
||||
map.on('sourcedata', (e) => {
|
||||
if (e.sourceId === sourceId && e.isSourceLoaded) {
|
||||
loadedSources++;
|
||||
if (loadedSources === Object.keys(sources).length) {
|
||||
console.log('所有图层关联的数据源已加载完成');
|
||||
emits('changeLoading',false)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
mapboxgl.accessToken = MAPBOX_TOKEN;
|
||||
|
||||
// 获取网络环境后加载地图
|
||||
|
||||
loadMapInfo();
|
||||
|
||||
})
|
||||
// 回到初始视角
|
||||
const handlerInitialize = () => {
|
||||
map.flyTo({
|
||||
center: [118.30207505530701, 35.30123435040745], // 设置中心点坐标
|
||||
zoom: 8, // 设置缩放等级
|
||||
pitch: 0,
|
||||
bearing: 0,
|
||||
duration: 1000,
|
||||
});
|
||||
};
|
||||
|
||||
function toPosition(item) {
|
||||
handlerLocation(item, 17.2);
|
||||
}
|
||||
function handlerFilter(item) {
|
||||
}
|
||||
async function handlerCheckUserOrgs() {
|
||||
handlerLoadPolygon();
|
||||
}
|
||||
function getMaskData() {
|
||||
map.addLayer({
|
||||
id: "linyishizhezhao",
|
||||
type: "raster",
|
||||
source: {
|
||||
type: "raster",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/wms?service=WMS&version=1.1.0&request=GetMap&layers=linyishi%3Alinyishizhezhao&styles=&bbox={bbox-epsg-3857}&width=256&height=256&srs=EPSG:3857&format=image/png&TRANSPARENT=TRUE",
|
||||
],
|
||||
tileSize: 256,
|
||||
},
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
}
|
||||
});
|
||||
}
|
||||
const handlerGetDetails = (e) => {
|
||||
emits('handlerGetDetails', e.features[0].properties, [e.lngLat.lng, e.lngLat.lat]);
|
||||
}
|
||||
const handlerNongjiGetDetails = (e) => {
|
||||
emits('handlerGetDetails', e.features[0].properties, e.features[0].geometry.coordinates);
|
||||
}
|
||||
function handlerLoadPolygon(code = '', filter = '', type = '', level = '') {
|
||||
let field_filter = '&field="SerialNumber","Type","Id",';
|
||||
map.addSource('tianditu-label', {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
`https://t0.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${TINADITU_TOKEN}`
|
||||
],
|
||||
tileSize: 256
|
||||
});
|
||||
map.addLayer({
|
||||
id: 'tianditu-label-layer',
|
||||
type: 'raster',
|
||||
source: 'tianditu-label',
|
||||
layout: {
|
||||
visibility: 'none',
|
||||
}
|
||||
});
|
||||
map.loadImage('/homepage/nong.png', (error, image) => {
|
||||
if (error) throw error;
|
||||
if (!map.hasImage('nongji-marker')) {
|
||||
map.addImage('nongji-marker', image);
|
||||
}
|
||||
});
|
||||
layers.forEach(layerItem => {
|
||||
const sourceId = `${layerItem.id}Source`
|
||||
map.addSource(sourceId, {
|
||||
type: 'vector',
|
||||
tiles: [
|
||||
VITE_GLOB_API_URL_VAR.value +
|
||||
'/api/IdleLand/QueryVectorTileByTable?z={z}&x={x}&y={y}&table=' +
|
||||
layerItem.tableName +
|
||||
field_filter
|
||||
],
|
||||
minzoom: 1,
|
||||
maxzoom: 20,
|
||||
});
|
||||
if(layerItem.id == 'nongji'){
|
||||
const layerId = `${layerItem.id}SourceLayer`
|
||||
map.addLayer({
|
||||
id: layerId,
|
||||
type: 'symbol',
|
||||
source: sourceId,
|
||||
'source-layer': layerItem.tableName,
|
||||
layout: {
|
||||
'icon-image': 'nongji-marker',
|
||||
'icon-size': 0.8,
|
||||
'icon-allow-overlap': true,
|
||||
'icon-anchor': 'bottom',
|
||||
visibility: 'none'
|
||||
},
|
||||
});
|
||||
map.on('click', layerId, handlerNongjiGetDetails);
|
||||
}else{
|
||||
const lineLayerId = `${layerItem.id}SourceLineLayer`
|
||||
const fillLayerId = `${layerItem.id}SourceFillLayer`
|
||||
map.addLayer({
|
||||
id: lineLayerId,
|
||||
type: 'line',
|
||||
source: sourceId,
|
||||
'source-layer': layerItem.tableName,
|
||||
layout: {
|
||||
'line-join': 'round',
|
||||
'line-cap': 'round',
|
||||
visibility: 'none',
|
||||
},
|
||||
paint: {
|
||||
'line-color': layerItem.color,
|
||||
'line-width': 5,
|
||||
},
|
||||
});
|
||||
map.addLayer({
|
||||
id: fillLayerId,
|
||||
type: 'fill',
|
||||
source: sourceId,
|
||||
'source-layer': layerItem.tableName,
|
||||
layout: {
|
||||
visibility: 'none',
|
||||
},
|
||||
paint: {
|
||||
'fill-color': layerItem.color,
|
||||
'fill-opacity': 0.5,
|
||||
},
|
||||
});
|
||||
map.on('click', fillLayerId, handlerGetDetails);
|
||||
}
|
||||
})
|
||||
}
|
||||
// 获取乡镇数据
|
||||
const handlerDealStreet = (countyName: String = '临沂市'): void => {
|
||||
map.addSource("zhenjie", {
|
||||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Azhenjie@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
],
|
||||
tileSize: 512
|
||||
})
|
||||
map.addLayer({
|
||||
id: "zhenjie",
|
||||
type: "line",
|
||||
source: "zhenjie",
|
||||
'source-layer': 'zhenjie',
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
},
|
||||
paint: {
|
||||
'line-color': '#42befb', // 设置线的颜色
|
||||
'line-width': 1, // 设置线的宽度
|
||||
},
|
||||
minzoom: 10,
|
||||
maxzoom: 24,
|
||||
})
|
||||
map.addSource("zhenjiepoint", {
|
||||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Azhenjiepoint@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
],
|
||||
tileSize: 512,
|
||||
})
|
||||
map.addLayer({
|
||||
id: "zhenjiepoint",
|
||||
type: "symbol",
|
||||
source: "zhenjiepoint",
|
||||
'source-layer': 'zhenjiepoint',
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
"text-field": "{xzqmc}",
|
||||
},
|
||||
paint: {
|
||||
'text-color': '#041B36',
|
||||
'text-halo-color': '#fff',
|
||||
'text-halo-width': 2,
|
||||
},
|
||||
minzoom: 10,
|
||||
maxzoom: 24,
|
||||
});
|
||||
};
|
||||
// 获取县区边界数据
|
||||
const handlerDealCountry = (countyName: String = '临沂市'): void => {
|
||||
map.addSource("wmsSource", {
|
||||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Axianjie@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf"
|
||||
],
|
||||
tileSize: 512
|
||||
})
|
||||
map.addLayer({
|
||||
id: "xianjie",
|
||||
type: "line",
|
||||
source: "wmsSource",
|
||||
'source-layer': 'xianjie',
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
},
|
||||
paint: {
|
||||
"line-color": "#6f7ff4",
|
||||
"line-width": 4,
|
||||
}
|
||||
})
|
||||
map.addSource("xianjiepoint", {
|
||||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Alinyishixianjie_point@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
],
|
||||
tileSize: 512,
|
||||
})
|
||||
map.addLayer({
|
||||
id: "xianjiepoint",
|
||||
type: "symbol",
|
||||
source: "xianjiepoint",
|
||||
'source-layer': 'linyishixianjie_point',
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
"text-field": "{xzqmc}",
|
||||
"text-size": 18
|
||||
},
|
||||
paint: {
|
||||
'text-color': '#041B36',
|
||||
'text-halo-color': '#fff',
|
||||
'text-halo-width': 2,
|
||||
},
|
||||
minzoom: 8,
|
||||
maxzoom: 10,
|
||||
});
|
||||
};
|
||||
// 地图定位
|
||||
function handlerLocation(lngLat, zoom) {
|
||||
map.flyTo({
|
||||
center: lngLat,
|
||||
zoom: zoom,
|
||||
bearing: 0,
|
||||
speed: 1, // 飞行速度
|
||||
curve: 2, // 飞行曲线
|
||||
essential: true,
|
||||
easing(t) {
|
||||
// 飞行动画函数
|
||||
return t;
|
||||
},
|
||||
});
|
||||
}
|
||||
function flyToLocation(e){
|
||||
map.flyTo({
|
||||
center: e,
|
||||
zoom: 12,
|
||||
bearing: 0,
|
||||
speed: 1, // 飞行速度
|
||||
curve: 2, // 飞行曲线
|
||||
essential: true,
|
||||
easing(t) {
|
||||
// 飞行动画函数
|
||||
return t;
|
||||
},
|
||||
});
|
||||
}
|
||||
const handlerChangeLayer = (list) => {
|
||||
const layerIds = layers.map(item => item.id);
|
||||
layerIds.forEach(id => {
|
||||
const visibility = list.includes(id) ? 'visible' : 'none';
|
||||
if(id == 'nongji'){
|
||||
const layerId = `${id}SourceLayer`
|
||||
map.setLayoutProperty(layerId, 'visibility', visibility);
|
||||
}else{
|
||||
const lineLayerId = `${id}SourceLineLayer`
|
||||
const fillLayerId = `${id}SourceFillLayer`
|
||||
map.setLayoutProperty(lineLayerId, 'visibility', visibility);
|
||||
map.setLayoutProperty(fillLayerId, 'visibility', visibility);
|
||||
}
|
||||
})
|
||||
}
|
||||
const changeTiandituLabelOpen = (type) => {
|
||||
map.setLayoutProperty('tianditu-label-layer', 'visibility', type ? 'visible' : 'none');
|
||||
}
|
||||
// 抛出函数
|
||||
defineExpose({
|
||||
handlerChangeLayer,
|
||||
changeTiandituLabelOpen
|
||||
});
|
||||
</script>
|
||||
<style type="less" scoped>
|
||||
.map-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.search-container-box {
|
||||
position: absolute;
|
||||
width: 330px;
|
||||
height: 40px;
|
||||
top: 72px;
|
||||
left: 73px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.map-type-switch-container {
|
||||
width: 208px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
top: 72px;
|
||||
right: 23px;
|
||||
z-index: 9999;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.map-type-switch-container .switch-button {
|
||||
line-height: 40px;
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
float: left;
|
||||
background: url(/map/change-view-btn.png);
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.map-type-switch-container .switch-button span {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
width:40px;
|
||||
height:40px;
|
||||
background: url(/map/home-btn.png);
|
||||
background-size: 100% 100%;
|
||||
float: right;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
z-index: 10;
|
||||
top: 72px;
|
||||
right: 23px;
|
||||
}
|
||||
::v-deep .mapboxgl-ctrl-logo {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,554 @@
|
|||
import U from 'mapbox-gl-utils';
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
type EventType = 'Point' | 'LineString' | 'Polygon';
|
||||
interface GenerateGeoJSONDataInterface {
|
||||
lng: number;
|
||||
lat: number;
|
||||
}
|
||||
|
||||
interface FeatureCollection {
|
||||
type: string;
|
||||
features: Array<any>;
|
||||
}
|
||||
|
||||
const CIRCLE_STYLE = {
|
||||
'circle-color': '#6495ED',
|
||||
'circle-radius': 8,
|
||||
'circle-stroke-width': 2,
|
||||
'circle-stroke-color': '#ffffff',
|
||||
};
|
||||
const LINE_STYLE = {
|
||||
'line-color': '#6495ED',
|
||||
'line-width': 3,
|
||||
};
|
||||
const LINE_IS_DRAW_STYLE = {
|
||||
'line-dasharray': [2, 2],
|
||||
'line-color': '#00FA9A',
|
||||
'line-width': 3,
|
||||
};
|
||||
const POLYGON_STYLE = {
|
||||
'fill-color': '#FAFAD2',
|
||||
'fill-opacity': 0.1,
|
||||
};
|
||||
|
||||
function typeOf(obj: any): any {
|
||||
const toString: any = Object.prototype.toString;
|
||||
const map: any = {
|
||||
'[object Boolean]': 'boolean',
|
||||
'[object Number]': 'number',
|
||||
'[object String]': 'string',
|
||||
'[object Function]': 'function',
|
||||
'[object Array]': 'array',
|
||||
'[object Date]': 'date',
|
||||
'[object RegExp]': 'regExp',
|
||||
'[object Undefined]': 'undefined',
|
||||
'[object Null]': 'null',
|
||||
'[object Object]': 'object',
|
||||
};
|
||||
return map[toString.call(obj)];
|
||||
}
|
||||
|
||||
function deepClone(data: any): any {
|
||||
// 获取传入拷贝函数的数据类型
|
||||
const type = typeOf(data);
|
||||
// 定义一个返回any类型的数据
|
||||
let reData: any;
|
||||
// 递归遍历一个array类型数据,
|
||||
if (type === 'array') {
|
||||
reData = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
reData.push(deepClone(data[i]));
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
//递归遍历一个object类型数据
|
||||
reData = {};
|
||||
for (const i in data) {
|
||||
reData[i] = deepClone(data[i]);
|
||||
}
|
||||
} else {
|
||||
// 返回基本数据类型
|
||||
return data;
|
||||
}
|
||||
// 将any类型的数据return出去,作为deepClone的结果
|
||||
return reData;
|
||||
}
|
||||
|
||||
class BaseMP {
|
||||
map: any;
|
||||
listeners: {};
|
||||
constructor(map: any) {
|
||||
this.map = map;
|
||||
// 初始化mapbox工具类
|
||||
U.init(this.map);
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
//监听
|
||||
on(event: EventType, callback: void) {
|
||||
console.log('on', event);
|
||||
this.listeners[event] = callback;
|
||||
}
|
||||
|
||||
off(event: EventType) {
|
||||
console.log('off', event);
|
||||
if (this.listeners[event]) {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
}
|
||||
|
||||
emit(event: EventType, data: any) {
|
||||
if (this.listeners[event]) {
|
||||
this.listeners[event](data);
|
||||
}
|
||||
}
|
||||
|
||||
//防抖函数
|
||||
debounce = (func: Function, delay: number) => {
|
||||
let timer: any = null;
|
||||
return (...args: any) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
//切换鼠标样式
|
||||
_changeMouseCursor = (cursor: string = 'pointer') => {
|
||||
this.map.getCanvas().style.cursor = cursor;
|
||||
};
|
||||
|
||||
//生成geoJson
|
||||
_generateGeoJSON(data: GenerateGeoJSONDataInterface[], geometryType: EventType) {
|
||||
/**
|
||||
* // 生成点类型的GeoJSON
|
||||
* var data = [{lng: -122.4194, lat: 37.7749}, {lng: -122.408, lat: 37.791}, {lng: -122.431, lat: 37.769}];
|
||||
* var geoJSON = generateGeoJSON(data, "Point");
|
||||
*
|
||||
* // 生成线类型的GeoJSON
|
||||
* var data = [{lng: -122.4194, lat: 37.7749}, {lng: -122.408, lat: 37.791}, {lng: -122.431, lat: 37.769}];
|
||||
* var geoJSON = generateGeoJSON(data, "LineString");
|
||||
*
|
||||
* // 生成面类型的GeoJSON
|
||||
* var data = [{lng: -122.4194, lat: 37.7749}, {lng: -122.408, lat: 37.791}, {lng: -122.431, lat: 37.769}];
|
||||
* var geoJSON = generateGeoJSON(data, "Polygon");
|
||||
* **/
|
||||
|
||||
const featureCollection: FeatureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: [],
|
||||
};
|
||||
switch (geometryType) {
|
||||
case 'Point':
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const feature = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: geometryType,
|
||||
coordinates: [data[i].lng, data[i].lat],
|
||||
},
|
||||
properties: {},
|
||||
};
|
||||
featureCollection.features.push(feature);
|
||||
}
|
||||
break;
|
||||
case 'LineString':
|
||||
const lineFeature = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: geometryType,
|
||||
coordinates: [],
|
||||
},
|
||||
properties: {},
|
||||
};
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
lineFeature['geometry']['coordinates'].push([data[i].lng, data[i].lat]);
|
||||
}
|
||||
featureCollection.features.push(lineFeature);
|
||||
break;
|
||||
case 'Polygon':
|
||||
const polygonFeature = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: geometryType,
|
||||
coordinates: [[]],
|
||||
},
|
||||
properties: {},
|
||||
};
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
polygonFeature['geometry']['coordinates'][0].push([data[i].lng, data[i].lat]);
|
||||
}
|
||||
featureCollection.features.push(polygonFeature);
|
||||
break;
|
||||
}
|
||||
return featureCollection;
|
||||
}
|
||||
}
|
||||
|
||||
export class MP extends BaseMP {
|
||||
drawModelChoose: { LineString: string; Point: string; Polygon: string; DEFAULT: string };
|
||||
drawModel: string;
|
||||
drawLocal: never[];
|
||||
drawCurrentId: {
|
||||
point: string; //点
|
||||
line: string; //线
|
||||
lastLine: string; //鼠标位置的点
|
||||
polygon: string;
|
||||
};
|
||||
currentMouseLocation: null;
|
||||
correctionMouseLocation: any;
|
||||
clickCount: number;
|
||||
clickTimeout: any;
|
||||
constructor(map) {
|
||||
super(map);
|
||||
this.drawModelChoose = {
|
||||
LineString: 'LineString',
|
||||
Point: 'Point',
|
||||
Polygon: 'Polygon',
|
||||
DEFAULT: 'default',
|
||||
};
|
||||
this.drawModel = this.drawModelChoose.DEFAULT;
|
||||
this.drawLocal = [];
|
||||
this.drawCurrentId = {
|
||||
point: 'current-draw-point', //点
|
||||
line: 'current-draw-line', //线
|
||||
lastLine: 'current-draw-last-line', //鼠标位置的点
|
||||
polygon: 'current-draw-polygon',
|
||||
};
|
||||
this.currentMouseLocation = null; //当前鼠标位置
|
||||
this.correctionMouseLocation = null; //校正后的鼠标位置
|
||||
this.clickCount = 0; // 定义一个计数器
|
||||
this.clickTimeout = null; // 定义一个超时变量
|
||||
}
|
||||
|
||||
//添加或更新source
|
||||
_addOrUpdateSource = (gS, id) => {
|
||||
if (this.map.getSource(id)) {
|
||||
this.map.getSource(id).setData(gS);
|
||||
} else {
|
||||
this.map.U.addGeoJSON(id, gS);
|
||||
}
|
||||
};
|
||||
// 添加layer,有则不操作
|
||||
_addLayer = (layerId, sourceId, type, style) => {
|
||||
// 如果图层不存在,添加图层。如果存在,不做操作
|
||||
if (!this.map.getLayer(sourceId)) {
|
||||
switch (type) {
|
||||
case this.drawModelChoose.Point:
|
||||
//addCircleLayer
|
||||
this.map.U.addCircleLayer(layerId, sourceId, style);
|
||||
break;
|
||||
case this.drawModelChoose.LineString:
|
||||
//addLineLayer
|
||||
this.map.U.addLineLayer(layerId, sourceId, style);
|
||||
break;
|
||||
case this.drawModelChoose.Polygon:
|
||||
//addPolygonLayer
|
||||
this.map.U.addFillLayer(layerId, sourceId, style);
|
||||
break;
|
||||
default:
|
||||
console.log('layer类型错误');
|
||||
}
|
||||
}
|
||||
};
|
||||
_currentDrawSource = () => {
|
||||
// currentGSP 点 currentGSL 线 currentGSPL 面
|
||||
const currentGSP = this._generateGeoJSON(this.drawLocal, 'Point');
|
||||
this._addOrUpdateSource(currentGSP, this.drawCurrentId.point);
|
||||
|
||||
if (
|
||||
this.drawModel === this.drawModelChoose.LineString ||
|
||||
this.drawModel === this.drawModelChoose.Polygon
|
||||
) {
|
||||
const currentGSL = this._generateGeoJSON(this.drawLocal, 'LineString');
|
||||
this._addOrUpdateSource(currentGSL, this.drawCurrentId.line);
|
||||
}
|
||||
if (this.drawModel === this.drawModelChoose.Polygon) {
|
||||
const lastGSPL = this._generateGeoJSON(this.drawLocal, 'Polygon');
|
||||
this._addOrUpdateSource(lastGSPL, this.drawCurrentId.polygon);
|
||||
}
|
||||
};
|
||||
//绘制当前的layer
|
||||
_currentDrawLayer = () => {
|
||||
// 如果是点,直接绘制点
|
||||
this._addLayer(this.drawCurrentId.point, this.drawCurrentId.point, 'Point', CIRCLE_STYLE);
|
||||
// 如果是线,在点的基础上,加上线
|
||||
// 如果是面,在线的基础上,加上面
|
||||
if (
|
||||
this.drawModel === this.drawModelChoose.LineString ||
|
||||
this.drawModel === this.drawModelChoose.Polygon
|
||||
) {
|
||||
this._addLayer(this.drawCurrentId.line, this.drawCurrentId.line, 'LineString', LINE_STYLE);
|
||||
}
|
||||
if (this.drawModel === this.drawModelChoose.Polygon) {
|
||||
this._addLayer(
|
||||
this.drawCurrentId.polygon,
|
||||
this.drawCurrentId.polygon,
|
||||
'Polygon',
|
||||
POLYGON_STYLE,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//最后动态的一笔
|
||||
_currentDrawLastLine = () => {
|
||||
if (
|
||||
this.drawModel === this.drawModelChoose.LineString ||
|
||||
this.drawModel === this.drawModelChoose.Polygon
|
||||
) {
|
||||
if (!this.drawLocal.length) {
|
||||
this.map.U.removeLayer(this.drawCurrentId.lastLine);
|
||||
this.map.U.removeSource(this.drawCurrentId.lastLine);
|
||||
return false;
|
||||
}
|
||||
const startPoint = this.drawLocal[this.drawLocal.length - 1];
|
||||
const endPoint = this.getDrawEndPoint();
|
||||
|
||||
// 添加动态线
|
||||
const lastGSL = this._generateGeoJSON([startPoint, endPoint], 'LineString');
|
||||
this.crossesLine(this.drawLocal, [startPoint, endPoint]);
|
||||
this._addOrUpdateSource(lastGSL, this.drawCurrentId.lastLine);
|
||||
this._addLayer(
|
||||
this.drawCurrentId.lastLine,
|
||||
this.drawCurrentId.lastLine,
|
||||
'LineString',
|
||||
LINE_IS_DRAW_STYLE,
|
||||
);
|
||||
}
|
||||
if (this.drawModel === this.drawModelChoose.Polygon) {
|
||||
this._currentDrawLastPolygon();
|
||||
}
|
||||
};
|
||||
//获取矫正的最后点
|
||||
getDrawEndPoint = () => {
|
||||
let endPoint = null;
|
||||
if (this.nearlyClientDistant()) {
|
||||
endPoint = this.correctionMouseLocation;
|
||||
} else {
|
||||
endPoint = this.currentMouseLocation;
|
||||
}
|
||||
return endPoint;
|
||||
};
|
||||
//最后动态的一笔,跟随面生成
|
||||
_currentDrawLastPolygon = () => {
|
||||
// 添加动态面
|
||||
const endPoint = this.getDrawEndPoint();
|
||||
const drawLocalCopy = deepClone(this.drawLocal);
|
||||
drawLocalCopy.push(endPoint);
|
||||
drawLocalCopy.push(drawLocalCopy[0]);
|
||||
const lastGSPL = this._generateGeoJSON(drawLocalCopy, 'Polygon');
|
||||
this._addOrUpdateSource(lastGSPL, this.drawCurrentId.polygon);
|
||||
this._addLayer(
|
||||
this.drawCurrentId.polygon,
|
||||
this.drawCurrentId.polygon,
|
||||
'Polygon',
|
||||
POLYGON_STYLE,
|
||||
);
|
||||
};
|
||||
|
||||
//鼠标点击
|
||||
clickHandler = (e) => {
|
||||
const _this = this;
|
||||
this.clickCount++; // 每次单击事件计数器加1
|
||||
if (this.clickCount === 1) {
|
||||
// 如果是第一次单击
|
||||
this.clickTimeout = setTimeout(function () {
|
||||
// 启动超时变量
|
||||
// 单击事件
|
||||
_this.clickCount = 0; // 计数器清零
|
||||
if (_this.nearlyClientDistant()) {
|
||||
_this.drawLocal.push(_this.correctionMouseLocation);
|
||||
// 如果是第一个点,那么就结束绘制
|
||||
// todo 画面第一点后结束
|
||||
if (
|
||||
_this.drawModel === _this.drawModelChoose.Polygon &&
|
||||
_this.correctionMouseLocation === _this.drawLocal[0]
|
||||
) {
|
||||
_this._currentDrawSource();
|
||||
_this._currentDrawLayer();
|
||||
_this.emit(_this.drawModel, _this.drawLocal);
|
||||
_this.finishDraw();
|
||||
return false;
|
||||
} else {
|
||||
_this.drawLocal.pop();
|
||||
}
|
||||
} else {
|
||||
_this.drawLocal.push(e.lngLat);
|
||||
}
|
||||
_this._currentDrawSource();
|
||||
_this._currentDrawLayer();
|
||||
if (_this.drawModel === _this.drawModelChoose.Point) {
|
||||
_this.drawIsFinish();
|
||||
}
|
||||
}, 200); // 设置超时时间为300毫秒
|
||||
} else {
|
||||
// 如果不是第一次单击
|
||||
clearTimeout(this.clickTimeout); // 清除超时变量
|
||||
this.clickCount = 0; // 计数器清零
|
||||
// 在这里编写双击事件的操作
|
||||
|
||||
// 鼠标双击事件
|
||||
|
||||
if (this.drawModel === this.drawModelChoose.LineString) {
|
||||
_this.drawLocal.push(e.lngLat);
|
||||
_this._currentDrawSource();
|
||||
_this._currentDrawLayer();
|
||||
this.drawIsFinish();
|
||||
}
|
||||
|
||||
if (this.drawModel === this.drawModelChoose.Polygon) {
|
||||
_this.drawLocal.push(e.lngLat);
|
||||
_this._currentDrawSource();
|
||||
_this._currentDrawLayer();
|
||||
this.drawIsFinish();
|
||||
}
|
||||
}
|
||||
};
|
||||
//右键点击
|
||||
contextmenuHandler = (e) => {
|
||||
if (this.drawLocal.length < 1) {
|
||||
return false;
|
||||
}
|
||||
this.drawLocal.pop();
|
||||
this._currentDrawSource();
|
||||
this._currentDrawLayer();
|
||||
this._currentDrawLastLine();
|
||||
};
|
||||
// 鼠标移动
|
||||
mousemoveHandler = this.debounce((e) => {
|
||||
this.currentMouseLocation = e.lngLat;
|
||||
this._currentDrawLastLine();
|
||||
}, 5);
|
||||
// 判断是否吸附,距离12像素
|
||||
nearlyClientDistant = () => {
|
||||
// 判断屏幕距离,鼠标吸附
|
||||
let _this = this;
|
||||
let mousePoint = [this.currentMouseLocation.lng, this.currentMouseLocation.lat];
|
||||
let closestFeature = null;
|
||||
let closestDistance = Infinity;
|
||||
let threshold = 10; // 阈值,单位为像素
|
||||
// 遍历所有特征,找到距离最近的特征
|
||||
let clientPosition = _this.map.project(mousePoint);
|
||||
this.drawLocal.forEach(function (feature) {
|
||||
_this.map.project([feature.lng, feature.lat]);
|
||||
let featurePosition = _this.map.project([feature.lng, feature.lat]);
|
||||
// 计算两个点在屏幕上的像素距离
|
||||
let distance = Math.sqrt(
|
||||
Math.pow(clientPosition.x - featurePosition.x, 2) +
|
||||
Math.pow(clientPosition.y - featurePosition.y, 2),
|
||||
);
|
||||
if (distance < closestDistance && distance < threshold) {
|
||||
closestFeature = feature;
|
||||
closestDistance = distance;
|
||||
}
|
||||
});
|
||||
// 如果距离小于阈值,则将标注吸附到特征上
|
||||
if (closestFeature) {
|
||||
this._changeMouseCursor('pointer');
|
||||
this.correctionMouseLocation = closestFeature;
|
||||
return true;
|
||||
} else {
|
||||
// console.log('范围外')
|
||||
this._changeMouseCursor('crosshair');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// 判断是否相交 true 相交 false 不相交
|
||||
crossesLine = (line1, line2) => {
|
||||
let _this = this;
|
||||
if (this.drawLocal.length >= 3) {
|
||||
let _line1 = line1.map((e) => {
|
||||
return [e.lng, e.lat];
|
||||
});
|
||||
_line1.pop();
|
||||
let _line2 = line2.map((e) => {
|
||||
return [e.lng, e.lat];
|
||||
});
|
||||
//判断是否有交叉
|
||||
let intersect = turf.lineIntersect(turf.lineString(_line1), turf.lineString(_line2));
|
||||
if (intersect.features.length > 0) {
|
||||
let coordinates = intersect.features[0].geometry.coordinates;
|
||||
//判断是否交叉在点上
|
||||
const isExist = _line1.some(
|
||||
(item) => item[0] === coordinates[0] && item[1] === coordinates[1],
|
||||
);
|
||||
if (isExist) {
|
||||
// console.log('no cross,穿过了交点')
|
||||
_this.map.U.setProperty(this.drawCurrentId.lastLine, 'line-color', '#00FA9A');
|
||||
_this.map.on('click', this.clickHandler);
|
||||
} else {
|
||||
// console.log('cross')
|
||||
_this.map.U.setProperty(this.drawCurrentId.lastLine, 'line-color', '#DC143C');
|
||||
_this._changeMouseCursor('no-drop');
|
||||
_this.map.off('click', this.clickHandler);
|
||||
}
|
||||
} else {
|
||||
// console.log('no cross,不沾边')
|
||||
_this.map.U.setProperty(this.drawCurrentId.lastLine, 'line-color', '#00FA9A');
|
||||
_this.map.on('click', this.clickHandler);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
drawStart = () => {
|
||||
//每次绘制都要初始化数据
|
||||
this.drawLocal = [];
|
||||
this.deleteDraw();
|
||||
//禁用鼠标双击放大事件
|
||||
this.map.doubleClickZoom.disable();
|
||||
this._changeMouseCursor('crosshair');
|
||||
this.map.on('click', this.clickHandler);
|
||||
this.map.on('contextmenu', this.contextmenuHandler);
|
||||
this.map.on('mousemove', this.mousemoveHandler);
|
||||
};
|
||||
draw = (shape) => {
|
||||
if(this.drawModelChoose[shape]) {
|
||||
this.drawModel = this.drawModelChoose[shape];
|
||||
this.drawStart();
|
||||
} else {
|
||||
console.log(`暂无${shape}类型`);
|
||||
}
|
||||
};
|
||||
drawIsFinish = () => {
|
||||
let _this = this;
|
||||
this.emit(this.drawModel, this.drawLocal);
|
||||
this.finishDraw();
|
||||
//恢复双击放大功能
|
||||
setTimeout(() => {
|
||||
_this.map.doubleClickZoom.enable();
|
||||
}, 10);
|
||||
};
|
||||
// 结束绘制线和面
|
||||
finishDraw = () => {
|
||||
this.map.U.removeSource(this.drawCurrentId.lastLine);
|
||||
this.map.U.removeLayer(this.drawCurrentId.lastLine);
|
||||
this.drawModel = this.drawModelChoose.DEFAULT;
|
||||
this.unDraw();
|
||||
};
|
||||
unDraw = () => {
|
||||
// console.log('推出绘制模式');
|
||||
this.map.off('click', this.clickHandler);
|
||||
this.map.off('contextmenu', this.contextmenuHandler);
|
||||
this.map.off('mousemove', this.mousemoveHandler);
|
||||
this._changeMouseCursor('pointer');
|
||||
this.drawLocal = [];
|
||||
};
|
||||
//删除绘制的内容
|
||||
deleteDraw = () => {
|
||||
this.map.U.removeSource([
|
||||
this.drawCurrentId.line,
|
||||
this.drawCurrentId.point,
|
||||
this.drawCurrentId.lastLine,
|
||||
this.drawCurrentId.polygon,
|
||||
]);
|
||||
this.map.U.removeLayer([
|
||||
this.drawCurrentId.line,
|
||||
this.drawCurrentId.point,
|
||||
this.drawCurrentId.lastLine,
|
||||
this.drawCurrentId.polygon,
|
||||
]);
|
||||
this.unDraw();
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import WKT from "terraformer-wkt-parser"
|
||||
import { wktToGeoJSON,geojsonToWKT } from "@terraformer/wkt"
|
||||
|
||||
|
||||
|
||||
const WktToGeojson = (wktData)=> {
|
||||
// return WKT.parse(wktData)
|
||||
return wktToGeoJSON(wktData);
|
||||
}
|
||||
|
||||
const GeojsonToWkt = (geojsonData)=> {
|
||||
// return WKT.convert(geojsonData)
|
||||
console.log("geojsonData",geojsonData)
|
||||
return geojsonToWKT(geojsonData)
|
||||
}
|
||||
|
||||
|
||||
export {WktToGeojson,GeojsonToWkt}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
export enum MapboxConfig {
|
||||
ACCESS_TOKEN = 'pk.eyJ1IjoiemhhbmcxMjM4ODk5OSIsImEiOiJja3N5Ync1cXcyMTR2Mm9xempmbGE4MnBtIn0.R-j78CRvbs6JZG-MDSoh8Q',
|
||||
// ACCESS_TOKEN = "1234",
|
||||
TDT_TOKEN = 'b6585bc41ee16251dbe6b1af64f375d9',
|
||||
// add more config options here
|
||||
}
|
||||
export const MapboxDefaultStyle = {
|
||||
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
|
||||
version: 8,
|
||||
sources: {
|
||||
'dianzi': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
`https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${MapboxConfig.TDT_TOKEN}`,
|
||||
],
|
||||
tileSize: 256,
|
||||
minzoom:18,
|
||||
maxzoom:24,
|
||||
},
|
||||
'dianzi-biaozhu': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
`https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${MapboxConfig.TDT_TOKEN}`,
|
||||
],
|
||||
tileSize: 256,
|
||||
},
|
||||
'raster-tiles': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
`https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${MapboxConfig.TDT_TOKEN}`,
|
||||
],
|
||||
tileSize: 256,
|
||||
minzoom:0,
|
||||
maxzoom:18,
|
||||
},
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: 'dianzi-biaozhu',
|
||||
type: 'raster',
|
||||
source: 'dianzi-biaozhu',
|
||||
layout: {
|
||||
visibility: 'visible',
|
||||
},
|
||||
},{
|
||||
id: 'tdt-img-tiles',
|
||||
type: 'raster',
|
||||
source: 'raster-tiles',
|
||||
minzoom: 0,
|
||||
maxzoom: 18,
|
||||
},{
|
||||
id: 'dianzi',
|
||||
type: 'raster',
|
||||
source: 'dianzi',
|
||||
layout: {
|
||||
visibility: 'visible',
|
||||
},
|
||||
minzoom: 18,
|
||||
maxzoom: 24,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
export const MapControlConfig = {
|
||||
DrawPoint: {
|
||||
handler: 'handlerDrawPoint',
|
||||
icon: '/point.png',
|
||||
title: '绘制点',
|
||||
},
|
||||
DrawLineString: {
|
||||
handler: 'handlerDrawLineString',
|
||||
icon: '/line.png',
|
||||
title: '绘制线',
|
||||
},
|
||||
DrawPolygon: {
|
||||
handler: 'handlerDrawPolygon',
|
||||
icon: '/polygon.png',
|
||||
title: '绘制面',
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
.mapboxgl-ctrl-logo {
|
||||
display: none !important;
|
||||
}
|
||||
.map-container {
|
||||
position: relative;
|
||||
}
|
||||
.map-box,
|
||||
.map-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.map-control {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.map-control img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
.mapboxgl-ctrl-logo {
|
||||
display: none !important;
|
||||
}
|
||||
.map-container{
|
||||
position: relative;
|
||||
}
|
||||
.map-box,
|
||||
.map-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-control {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
img:hover {
|
||||
scale: 1.2;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import { ControlOutlined } from '@ant-design/icons-vue';
|
||||
import * as turf from '@turf/turf'
|
||||
|
||||
// js生成UUID
|
||||
const generateUUID = ()=>{
|
||||
|
||||
var d = new Date().getTime(); //Timestamp
|
||||
var d2 = (performance && performance.now && (performance.now()*1000)) || 0;
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16;
|
||||
if(d > 0) {
|
||||
r = (d + r)%16 | 0;
|
||||
d = Math.floor(d/16);
|
||||
} else {
|
||||
r = (d2 + r)%16 | 0;
|
||||
d2 = Math.floor(d2/16);
|
||||
}
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
// js 针对后端post接口参数拼接到url的情况 将json参数转换为 ?key1=value1&key2=value2
|
||||
const ObjectToUrl = (obj)=>{
|
||||
let params = "?";
|
||||
for(let item in obj){
|
||||
params+=item+"="+obj[item]+"&"
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// turf获取几何图形的中心
|
||||
const getGeometryCenter = (geometry)=>{
|
||||
let coordinates = [];
|
||||
switch(geometry.geometry.type.toUpperCase()){
|
||||
case "POINT":
|
||||
|
||||
break;
|
||||
case "MULTIPOINT":
|
||||
|
||||
break;
|
||||
case "LINESTRING":
|
||||
|
||||
break;
|
||||
case "MULTILINESTRING":
|
||||
|
||||
break;
|
||||
case "POLYGON":
|
||||
coordinates = geometry.geometry.coordinates
|
||||
break;
|
||||
case "MULTIPOLYGON":
|
||||
coordinates = geometry.geometry.coordinates[0]
|
||||
break;
|
||||
}
|
||||
|
||||
// let polygon = turf.polygon(coordinates);
|
||||
// let center = turf.centerOfMass(polygon);
|
||||
return [coordinates[0][0][0],coordinates[0][0][1]];
|
||||
// return [117.732878836452,35.1320944773393]
|
||||
}
|
||||
|
||||
export { generateUUID,ObjectToUrl,getGeometryCenter }
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,148 @@
|
|||
<script lang="tsx">
|
||||
import type { PropType } from 'vue';
|
||||
import { Result, Button } from 'ant-design-vue';
|
||||
import { defineComponent, ref, computed, unref } from 'vue';
|
||||
import { ExceptionEnum } from '@/enums/exceptionEnum';
|
||||
import notDataSvg from '@/assets/svg/no-data.svg';
|
||||
import netWorkSvg from '@/assets/svg/net-error.svg';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useDesign } from '@/hooks/web/useDesign';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useGo, useRedo } from '@/hooks/web/usePage';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
|
||||
interface MapValue {
|
||||
title: string;
|
||||
subTitle: string;
|
||||
btnText?: string;
|
||||
icon?: string;
|
||||
handler?: any;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ErrorPage',
|
||||
props: {
|
||||
// 状态码
|
||||
status: {
|
||||
type: Number as PropType<number>,
|
||||
default: ExceptionEnum.PAGE_NOT_FOUND,
|
||||
},
|
||||
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
|
||||
subTitle: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
|
||||
full: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const statusMapRef = ref(new Map<string | number, MapValue>());
|
||||
|
||||
const { query } = useRoute();
|
||||
const go = useGo();
|
||||
const redo = useRedo();
|
||||
const { t } = useI18n();
|
||||
const { prefixCls } = useDesign('app-exception-page');
|
||||
|
||||
const getStatus = computed(() => {
|
||||
const { status: routeStatus } = query;
|
||||
const { status } = props;
|
||||
return Number(routeStatus) || status;
|
||||
});
|
||||
|
||||
const getMapValue = computed((): MapValue => {
|
||||
return unref(statusMapRef).get(unref(getStatus)) as MapValue;
|
||||
});
|
||||
|
||||
const backLoginI18n = t('sys.exception.backLogin');
|
||||
const backHomeI18n = t('sys.exception.backHome');
|
||||
|
||||
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, {
|
||||
title: '403',
|
||||
status: `${ExceptionEnum.PAGE_NOT_ACCESS}`,
|
||||
subTitle: t('sys.exception.subTitle403'),
|
||||
btnText: props.full ? backLoginI18n : backHomeI18n,
|
||||
handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
|
||||
});
|
||||
|
||||
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, {
|
||||
title: '404',
|
||||
status: `${ExceptionEnum.PAGE_NOT_FOUND}`,
|
||||
subTitle: t('sys.exception.subTitle404'),
|
||||
btnText: props.full ? backLoginI18n : backHomeI18n,
|
||||
handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
|
||||
});
|
||||
|
||||
unref(statusMapRef).set(ExceptionEnum.ERROR, {
|
||||
title: '500',
|
||||
status: `${ExceptionEnum.ERROR}`,
|
||||
subTitle: t('sys.exception.subTitle500'),
|
||||
btnText: backHomeI18n,
|
||||
handler: () => go(),
|
||||
});
|
||||
|
||||
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
|
||||
title: t('sys.exception.noDataTitle'),
|
||||
subTitle: '',
|
||||
btnText: t('common.redo'),
|
||||
handler: () => redo(),
|
||||
icon: notDataSvg,
|
||||
});
|
||||
|
||||
unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, {
|
||||
title: t('sys.exception.networkErrorTitle'),
|
||||
subTitle: t('sys.exception.networkErrorSubTitle'),
|
||||
btnText: t('common.redo'),
|
||||
handler: () => redo(),
|
||||
icon: netWorkSvg,
|
||||
});
|
||||
|
||||
return () => {
|
||||
const { title, subTitle, btnText, icon, handler, status } = unref(getMapValue) || {};
|
||||
return (
|
||||
<Result
|
||||
class={prefixCls}
|
||||
status={status as any}
|
||||
title={props.title || title}
|
||||
sub-title={props.subTitle || subTitle}
|
||||
>
|
||||
{{
|
||||
extra: () =>
|
||||
btnText && (
|
||||
<Button type="primary" onClick={handler}>
|
||||
{() => btnText}
|
||||
</Button>
|
||||
),
|
||||
icon: () => (icon ? <img src={icon} /> : null),
|
||||
}}
|
||||
</Result>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-app-exception-page';
|
||||
|
||||
.@{prefix-cls} {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.ant-result-icon {
|
||||
img {
|
||||
max-width: 400px;
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,27 +1,37 @@
|
|||
<template>
|
||||
<div class="search-container">
|
||||
<div class="search-input">
|
||||
<a-input
|
||||
placeholder="请输入图斑号或位置"
|
||||
v-model:value="keyword"
|
||||
allow-clear
|
||||
@blur="onInputBlue"
|
||||
></a-input>
|
||||
<div class="component-container">
|
||||
|
||||
<!-- 分类过滤 -->
|
||||
<div class="search-select">
|
||||
临时用地
|
||||
</div>
|
||||
<div class="search-button" @click="searchArea">
|
||||
<img src="/statistical/search-btn.png" alt="" />
|
||||
</div>
|
||||
<div class="search-result-container" v-if="showSearchResult">
|
||||
<div class="result-item" v-for="(item, index) in searchResult" @click="toPosition(item)">
|
||||
<div class="search-icon">
|
||||
<img src="/statistical/search-icon.png" alt="" />
|
||||
</div>
|
||||
<div class="level-0">{{ item.id }}</div>
|
||||
<div class="level-1">{{ item.county }}</div>
|
||||
<div class="level-2">{{ item.street }}</div>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-container">
|
||||
<div class="search-input">
|
||||
<a-input
|
||||
placeholder="请输入图斑号或位置"
|
||||
v-model:value="keyword"
|
||||
allow-clear
|
||||
@blur="onInputBlue"
|
||||
></a-input>
|
||||
</div>
|
||||
<div class="search-button" @click="searchArea">
|
||||
<img src="/statistical/search-btn.png" alt="" />
|
||||
</div>
|
||||
<div class="search-result-container" v-if="showSearchResult">
|
||||
<div class="result-item" v-for="(item, index) in searchResult" @click="toPosition(item)">
|
||||
<div class="search-icon">
|
||||
<img src="/statistical/search-icon.png" alt="" />
|
||||
</div>
|
||||
<div class="level-0">{{ item.id }}</div>
|
||||
<div class="level-1">{{ item.county }}</div>
|
||||
<div class="level-2">{{ item.street }}</div>
|
||||
</div>
|
||||
<a-empty v-if="searchResult.length == 0" />
|
||||
</div>
|
||||
<a-empty v-if="searchResult.length == 0" />
|
||||
</div>
|
||||
<!-- 图斑过滤 -->
|
||||
<div class="filter-container">
|
||||
<div class="filter-name">{{ currentFilter }}</div>
|
||||
<div class="filter-icon" @click="handlerChangeFilterOptions">
|
||||
|
|
@ -38,6 +48,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, defineExpose, defineEmits, watch } from 'vue';
|
||||
|
|
@ -217,6 +229,74 @@
|
|||
</script>
|
||||
|
||||
<style type="less" scoped>
|
||||
|
||||
.component-container{
|
||||
width:100%;
|
||||
height:100%;
|
||||
display: flex;
|
||||
column-gap: 12px;
|
||||
.search-select{
|
||||
width:320px;
|
||||
line-height:36px;
|
||||
font-weight: bold;
|
||||
height:100%;
|
||||
background-image: url('/statistical/search-bg.png');
|
||||
background-size: 100% 100%;
|
||||
text-align: center;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
width: 100px;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
background: #0a62c6;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.filter-name {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
padding: 0px 2px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
&::after {
|
||||
content: '';
|
||||
width: 2px;
|
||||
height: 22px;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 0px;
|
||||
background: #0751a5;
|
||||
}
|
||||
}
|
||||
.filter-icon {
|
||||
width: 30px;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
.filter-item-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background: rgba(11, 39, 68, 0.8);
|
||||
border: 1px solid #0a62c6;
|
||||
top: 36px;
|
||||
left: 0px;
|
||||
.filter-item {
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: #0f3863;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.search-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
@ -225,8 +305,10 @@
|
|||
background-size: 100% 100%;
|
||||
border-top-right-radius: 8px;
|
||||
position: relative;
|
||||
column-gap: 15px;
|
||||
|
||||
.search-input {
|
||||
flex: auto;
|
||||
flex:auto;
|
||||
height: 100%;
|
||||
}
|
||||
.search-button {
|
||||
|
|
@ -283,59 +365,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: -120px;
|
||||
width: 100px;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
background: #0a62c6;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.filter-name {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
padding: 0px 2px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
&::after {
|
||||
content: '';
|
||||
width: 2px;
|
||||
height: 22px;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 0px;
|
||||
background: #0751a5;
|
||||
}
|
||||
}
|
||||
.filter-icon {
|
||||
width: 30px;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
.filter-item-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background: rgba(11, 39, 68, 0.8);
|
||||
border: 1px solid #0a62c6;
|
||||
top: 36px;
|
||||
left: 0px;
|
||||
.filter-item {
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: #0f3863;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
::v-deep .mapboxgl-ctrl-logo {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import { ref, onBeforeUnmount } from 'vue';
|
||||
|
||||
export default function useDrag() {
|
||||
const isDragging = ref(false); // 是否正在拖动
|
||||
const offsetX = ref(0); // 鼠标按下时的 X 坐标偏移
|
||||
const offsetY = ref(0); // 鼠标按下时的 Y 坐标偏移
|
||||
const style = ref({
|
||||
position: 'absolute', // 设置为绝对定位
|
||||
top: '200px', // 初始位置
|
||||
left: '70px', // 初始位置
|
||||
});
|
||||
|
||||
// 鼠标按下时,记录偏移量并开始拖动
|
||||
const startDrag = (event: MouseEvent) => {
|
||||
isDragging.value = true;
|
||||
offsetX.value = event.clientX - (event.target as HTMLElement).getBoundingClientRect().left;
|
||||
offsetY.value = event.clientY - (event.target as HTMLElement).getBoundingClientRect().top;
|
||||
|
||||
// 添加鼠标移动和鼠标松开事件监听
|
||||
document.addEventListener('mousemove', onDrag);
|
||||
document.addEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
// 鼠标移动时,更新元素的位置
|
||||
const onDrag = (event: MouseEvent) => {
|
||||
if (isDragging.value) {
|
||||
|
||||
if(event.clientX - offsetX.value + 280 > window.innerWidth){
|
||||
return null;
|
||||
}
|
||||
if(event.clientY - offsetY.value + 20 > window.innerHeight){
|
||||
return null;
|
||||
}
|
||||
style.value.left = (event.clientX - offsetX.value - 200) > 0 ? `${event.clientX - offsetX.value - 200}px` : '0px'
|
||||
style.value.top = (event.clientY - offsetY.value - 100) > 0 ? `${event.clientY - offsetY.value - 100}px` : '0px';
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// 鼠标松开时,停止拖动
|
||||
const stopDrag = () => {
|
||||
isDragging.value = false;
|
||||
document.removeEventListener('mousemove', onDrag);
|
||||
document.removeEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
// 在组件卸载时移除事件监听
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousemove', onDrag);
|
||||
document.removeEventListener('mouseup', stopDrag);
|
||||
});
|
||||
|
||||
return {
|
||||
style,
|
||||
startDrag,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as Exception } from './Exception.vue';
|
||||
|
|
@ -69,7 +69,9 @@
|
|||
defaultColor: String,
|
||||
});
|
||||
const layerSettings = ref<any>({});
|
||||
|
||||
const layerDefaultColor = ref<String>();
|
||||
|
||||
watch(
|
||||
() => props.layer,
|
||||
(val) => {
|
||||
|
|
@ -126,9 +128,9 @@
|
|||
},
|
||||
maxZoom: 22,
|
||||
minZoom: 8,
|
||||
zoom: 8,
|
||||
zoom: 9.6,
|
||||
pitch: 0,
|
||||
center: [118.30207505530701, 35.30123435040745],
|
||||
center: [117.97256,34.85481],
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -240,6 +242,7 @@
|
|||
let isLevel = orgs.find((item, index) => {
|
||||
return item.name == '临沂市' || item.parentId == 0 || item.parentName == '根节点';
|
||||
});
|
||||
|
||||
if (isLevel) {
|
||||
isRootLevel.value = true;
|
||||
handlerLoadPolygon('', '', '', '1');
|
||||
|
|
@ -554,7 +557,7 @@
|
|||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Axianjie@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf"
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/lanling%3Axianjie@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf"
|
||||
],
|
||||
tileSize: 512
|
||||
})
|
||||
|
|
@ -585,7 +588,7 @@
|
|||
source: "xianjiepoint",
|
||||
'source-layer': 'linyishixianjie_point',
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
visibility: "none",
|
||||
"text-field": "{xzqmc}",
|
||||
"text-size": 18
|
||||
},
|
||||
|
|
@ -669,7 +672,7 @@
|
|||
source: {
|
||||
type: "raster",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/wms?service=WMS&version=1.1.0&request=GetMap&layers=linyishi%3Alinyishizhezhao&styles=&bbox={bbox-epsg-3857}&width=256&height=256&srs=EPSG:3857&format=image/png&TRANSPARENT=TRUE",
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/wms?service=WMS&version=1.1.0&request=GetMap&layers=lanling%3Alanlingxian_zhezhao&styles=&bbox={bbox-epsg-3857}&width=256&height=256&srs=EPSG:3857&format=image/png&TRANSPARENT=TRUE",
|
||||
],
|
||||
tileSize: 256,
|
||||
},
|
||||
|
|
@ -720,7 +723,7 @@
|
|||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Azhenjie@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/lanling%3Azhenjie@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
],
|
||||
tileSize: 512
|
||||
})
|
||||
|
|
@ -743,7 +746,7 @@
|
|||
"type": "vector",
|
||||
"scheme": "tms",
|
||||
tiles: [
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/linyishi%3Azhenjiepoint@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
"http://175.27.168.120:8080/geoserver/gwc/service/tms/1.0.0/lanling%3Alanlignxian_zhenjie_point@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf",
|
||||
],
|
||||
tileSize: 512,
|
||||
})
|
||||
|
|
@ -751,7 +754,7 @@
|
|||
id: "zhenjiepoint",
|
||||
type: "symbol",
|
||||
source: "zhenjiepoint",
|
||||
'source-layer': 'zhenjiepoint',
|
||||
'source-layer': 'lanlignxian_zhenjie_point',
|
||||
layout: {
|
||||
visibility: "visible",
|
||||
"text-field": "{xzqmc}",
|
||||
|
|
@ -868,8 +871,8 @@
|
|||
// 回到初始视角
|
||||
const handlerInitialize = () => {
|
||||
map.flyTo({
|
||||
center: [118.30207505530701, 35.30123435040745], // 设置中心点坐标
|
||||
zoom: 8, // 设置缩放等级
|
||||
center: [117.97256,34.85481], // 设置中心点坐标
|
||||
zoom: 9.5, // 设置缩放等级
|
||||
pitch: 0,
|
||||
bearing: 0,
|
||||
duration: 1000,
|
||||
|
|
@ -1030,6 +1033,7 @@
|
|||
}
|
||||
}
|
||||
} else if (item.id == 'Data') {
|
||||
|
||||
if (map.getLayer('historyLayerLine')) {
|
||||
if (item.checked) {
|
||||
map.setLayoutProperty('historyLayerLine', 'visibility', 'visible');
|
||||
|
|
@ -1155,7 +1159,7 @@
|
|||
}
|
||||
.search-container-box {
|
||||
position: absolute;
|
||||
width: 330px;
|
||||
width: 600px;
|
||||
height: 40px;
|
||||
top: 72px;
|
||||
left: 73px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
<template>
|
||||
<div :class="`layer-list-container ${openLayer? '': 'layer-list-container-close'}`" >
|
||||
<div class="title">
|
||||
图层资源
|
||||
<div class="hidden-button" @click="openLayer = !openLayer">
|
||||
<img v-if="openLayer" src="/public/map/top.png" alt="">
|
||||
<img v-else src="/public/map/bottom.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layers-container">
|
||||
<p class="cate-name">数据图层</p>
|
||||
<div class="case-list">
|
||||
<div class="case-item" v-for="(item,index) in dataLayerList" :key="index" >
|
||||
<img src="/statistical/prove-icon.png" alt="" />
|
||||
<a-checkbox v-model:checked="item.checked" @change="layerChange(item,'dataLayer')" >
|
||||
<span style="color:#fff;">{{item.name}}</span>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<div class="case-item">
|
||||
<img src="/statistical/prove-icon.png" alt="" />
|
||||
<a-checkbox v-model:checked="tiandituLabelOpen" @change="labelChange" >
|
||||
<span style="color:#fff;">地图注记</span>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
defineEmits,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import { layers } from '@/views/sys/exception/util'
|
||||
const emits = defineEmits(['changeLayer','changeLabel'])
|
||||
const openLayer = ref(true)
|
||||
const tiandituLabelOpen = ref(false)
|
||||
const dataLayerList = ref(layers)
|
||||
|
||||
const layerChange = (item,type)=>{
|
||||
let showLayer = dataLayerList.value.filter(itemLayer => itemLayer.checked).map(itemLayer => itemLayer.id)
|
||||
emits("changeLayer",showLayer);
|
||||
}
|
||||
const labelChange = () => {
|
||||
emits("changeLabel",tiandituLabelOpen.value);
|
||||
}
|
||||
</script>
|
||||
<style type="less" scoped>
|
||||
.layer-list-container-close{
|
||||
height: 41px !important;
|
||||
}
|
||||
.layer-list-container {
|
||||
width: 285px;
|
||||
height: 600px;
|
||||
background: #041b36;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
transition: 0.5s;
|
||||
&::before {
|
||||
content: '';
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
background: url('/map/layer-center-left.png');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
background: url('/map/layer-center-right.png');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.layers-container{
|
||||
width:100%;
|
||||
height: calc( 100% - 60px);
|
||||
overflow-y:auto;
|
||||
}
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-image: url('/videosupervision/title.png');
|
||||
background-size: 100% 100%;
|
||||
line-height: 40px;
|
||||
text-indent: 18px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
}
|
||||
.hidden-button{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
z-index: 2;
|
||||
}
|
||||
.switch-button {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
.case-list {
|
||||
padding: 10px 24px;
|
||||
overflow: auto;
|
||||
.case-item {
|
||||
cursor:pointer;
|
||||
border-bottom: 1px dashed #1d60b4;
|
||||
color:#fff;
|
||||
padding: 5px 0;
|
||||
height:40px;
|
||||
overflow:hidden;
|
||||
img {
|
||||
width: 15px;
|
||||
margin-right:12px;
|
||||
}
|
||||
span {
|
||||
color: #7ebbff;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .ant-empty-image{
|
||||
height:50px;
|
||||
position:relative;
|
||||
}
|
||||
::v-deep .ant-empty-description{
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.cate-name{
|
||||
color:#fff;
|
||||
padding:12px;
|
||||
font-size:14px;
|
||||
margin-bottom:0px;
|
||||
background:rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
::v-deeep .ant-empty-image img{
|
||||
width:50px;
|
||||
height:50px;
|
||||
}
|
||||
|
||||
::v-deep .ant-checkbox-wrapper{
|
||||
width: calc( 100% - 40px);
|
||||
height: 20px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
<template>
|
||||
<div class="county">
|
||||
<div class="county_div">
|
||||
<img class="county_div_leftButton" :src="scrollImgLeft" @click="scrollToElement('left')" />
|
||||
|
||||
<div ref="countyList" id="scrollContainer" class="county_div_list">
|
||||
<div v-for="item in list" :key="item.id" :id="item.id" @click="clickCounty(item)">
|
||||
<span v-if="!item.isClick" class="county_div_list_span_nochoose">{{ item.name }}</span>
|
||||
<img
|
||||
class="county_div_list_nochoose"
|
||||
v-if="!item.isClick"
|
||||
src="/statistical/left_county_nochoose.png"
|
||||
/>
|
||||
<span v-if="item.isClick" class="county_div_list_span_choose">{{ item.name }}</span>
|
||||
<img
|
||||
class="county_div_list_choose"
|
||||
v-if="item.isClick"
|
||||
src="/statistical/left_county_choose.png"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="county_div_rightButton" :src="scrollImgRight" @click="scrollToElement('right')" />
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="/statistical/left_decorative_bottom.png"
|
||||
:style="{
|
||||
width: '840px',
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, computed, defineEmits, watch, defineExpose } from 'vue';
|
||||
import { getChildrenTree } from '@/api/demo/system';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getUserOrgs } from '@/api/tiankongdi';
|
||||
|
||||
// emit
|
||||
const emit = defineEmits(['countyClick']);
|
||||
const props = defineProps(['bi']);
|
||||
|
||||
// 按键控制滚动条
|
||||
let countyWidth: number = 110;
|
||||
const countyList = ref<HTMLElement | null>(null);
|
||||
const scrollImgLeft = ref('/statistical/left_direction_left1.png');
|
||||
const scrollImgRight = ref('/statistical/left_direction_right1.png');
|
||||
|
||||
const scrollToElement = async (type) => {
|
||||
if (countyList.value) {
|
||||
if (type == 'left') {
|
||||
countyList.value.scrollLeft = countyList.value.scrollLeft - countyWidth;
|
||||
}
|
||||
if (type == 'right') {
|
||||
countyList.value.scrollLeft = countyList.value.scrollLeft + countyWidth;
|
||||
}
|
||||
// if (countyList.value.scrollLeft < countyWidth) {
|
||||
// scrollImgLeft.value = '/statistical/left_direction_left1.png';
|
||||
// } else {
|
||||
// scrollImgLeft.value = '/statistical/left_direction_left2.png';
|
||||
// }
|
||||
// if (countyList.value.scrollLeft > (list.value.length - 7 - 1) * countyWidth) {
|
||||
// scrollImgRight.value = '/statistical/left_direction_right1.png';
|
||||
// } else {
|
||||
// scrollImgRight.value = '/statistical/left_direction_right2.png';
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
// 获取用户可以访问的机构信息
|
||||
async function handlerGetOrgs() {
|
||||
let orgs = await getUserOrgs({});
|
||||
let isLevel = orgs.find((item, index) => {
|
||||
return item.name == '临沂市' || item.parentId == 0 || item.parentName == '根节点';
|
||||
});
|
||||
if (isLevel) {
|
||||
// 市级
|
||||
getOptions();
|
||||
} else {
|
||||
// 县区级
|
||||
let levesl = orgs.filter((item, index) => {
|
||||
return item.parentName == '临沂市' || item.parentId == 371300;
|
||||
});
|
||||
list.value = levesl;
|
||||
}
|
||||
// clickCounty(list.value[0])
|
||||
}
|
||||
|
||||
//
|
||||
function selectCounty() {}
|
||||
// 选择区县
|
||||
function clickCounty(record) {
|
||||
list.value.forEach((item) => {
|
||||
if (item.cascadeId == record.cascadeId) {
|
||||
item.isClick = true;
|
||||
} else {
|
||||
item.isClick = false;
|
||||
}
|
||||
});
|
||||
emit('countyClick', record);
|
||||
}
|
||||
|
||||
// 初始数据
|
||||
const list = ref();
|
||||
async function getOptions() {
|
||||
const data = await getChildrenTree({
|
||||
parentId: 371300,
|
||||
});
|
||||
data.forEach((item) => {
|
||||
item.isClick = false;
|
||||
});
|
||||
list.value = data;
|
||||
|
||||
list.value.sort((a, b) => a.id - b.id);
|
||||
|
||||
let linyishi = {
|
||||
id: '',
|
||||
name: '临沂市',
|
||||
};
|
||||
|
||||
console.log('listValue', list.value);
|
||||
list.value.unshift(linyishi);
|
||||
clickCounty(linyishi);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handlerGetOrgs();
|
||||
|
||||
let scrollContainer = document.getElementById('scrollContainer');
|
||||
|
||||
scrollContainer?.addEventListener('wheel', (evt) => {
|
||||
evt.preventDefault();
|
||||
scrollContainer?.scrollBy({
|
||||
left: evt.deltaY * 5,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
selectCounty,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.county {
|
||||
cursor: pointer;
|
||||
|
||||
&_div {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 840px;
|
||||
bottom: 10px;
|
||||
|
||||
&_leftButton {
|
||||
position: relative;
|
||||
top: 42px;
|
||||
left: -4px;
|
||||
height: 31px;
|
||||
width: 31px;
|
||||
}
|
||||
|
||||
&_list {
|
||||
display: flex;
|
||||
max-width: 770px;
|
||||
margin: 0px auto;
|
||||
overflow-x: auto;
|
||||
scroll-behavior: smooth;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
div {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
&_span_nochoose {
|
||||
position: relative;
|
||||
top: 30px;
|
||||
width: 110px;
|
||||
z-index: 999;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-family: Alibaba PuHuiTi;
|
||||
font-size: 15px;
|
||||
color: rgba(255, 255, 255, 0);
|
||||
background: linear-gradient(0deg, #6f81d4 0.146484375%, #ffffff 86.7919921875%);
|
||||
opacity: 0.6;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
color: #fff;
|
||||
text-shadow:
|
||||
-1px -1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
font-weight: 10000;
|
||||
}
|
||||
|
||||
&_nochoose {
|
||||
position: relative;
|
||||
top: -7px;
|
||||
width: 87px;
|
||||
margin-left: 11.5px;
|
||||
margin-right: 11.5px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
&_span_choose {
|
||||
position: relative;
|
||||
top: 30px;
|
||||
width: 110px;
|
||||
z-index: 999;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-family: Alibaba PuHuiTi;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: #014ce2;
|
||||
}
|
||||
|
||||
&_choose {
|
||||
position: relative;
|
||||
width: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
&_rightButton {
|
||||
position: relative;
|
||||
top: 42px;
|
||||
left: 4px;
|
||||
height: 31px;
|
||||
width: 31px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div class="layerbutton">
|
||||
<div
|
||||
class="layerbutton_div"
|
||||
:style="{
|
||||
marginBottom: '15px',
|
||||
}"
|
||||
>
|
||||
<img
|
||||
id="left_layerButton_1"
|
||||
:src="button_1 ? '/statistical/layer-btn1-active.png' : '/statistical/layer-btn1.png'"
|
||||
@click="handlerLayerChange(1)"
|
||||
/>
|
||||
</div>
|
||||
<div class="layerbutton_div">
|
||||
<img
|
||||
id="left_layerButton_2"
|
||||
:src="button_2 ? '/statistical/layer-btn2-active.png' : '/statistical/layer-btn2.png'"
|
||||
@click="handlerLayerChange(2)"
|
||||
/>
|
||||
</div>
|
||||
<div class="layerbutton_div">
|
||||
<img
|
||||
id="left_layerButton_2"
|
||||
:src="button_3 ? '/statistical/layer-btn3-active.png' : '/statistical/layer-btn3.png'"
|
||||
@click="handlerLayerChange(3)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineEmits, defineProps } from 'vue';
|
||||
// emit
|
||||
const emit = defineEmits(['handlerLayerChange']);
|
||||
const button_1 = ref(false);
|
||||
const button_2 = ref(true);
|
||||
const button_3 = ref(false);
|
||||
// 控制图层
|
||||
function handlerLayerChange(type: number) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
button_1.value = true;
|
||||
button_2.value = false;
|
||||
button_3.value = false;
|
||||
break;
|
||||
case 2:
|
||||
button_1.value = false;
|
||||
button_2.value = true;
|
||||
button_3.value = false;
|
||||
break;
|
||||
case 3:
|
||||
button_1.value = false;
|
||||
button_2.value = false;
|
||||
button_3.value = true;
|
||||
break;
|
||||
}
|
||||
emit('handlerLayerChange', type);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layerbutton {
|
||||
position: absolute;
|
||||
top: 130px;
|
||||
left: 60px;
|
||||
.layerbutton_div {
|
||||
margin-bottom: 14px;
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div class="statistical">
|
||||
<img src="/statistical/left_statistical.png" />
|
||||
<div class="statistical_div">
|
||||
<a-row>
|
||||
<a-col :span="10" @click="handlerChangePolygonType('违法')">
|
||||
<span class="statistical_div_dot" style="background: #F70303" />
|
||||
<span class="statistical_div_span"> 违法 </span>
|
||||
</a-col>
|
||||
<a-col :span="10" @click="handlerChangePolygonType('补办手续')">
|
||||
<span class="statistical_div_dot" style="background: #AD04F4" />
|
||||
<span class="statistical_div_span"> 补办手续 </span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="10" @click="handlerChangePolygonType('合法')">
|
||||
<span class="statistical_div_dot" style="background: #0AF703" />
|
||||
<span class="statistical_div_span"> 合法 </span>
|
||||
</a-col>
|
||||
<a-col :span="10" @click="handlerChangePolygonType('拆除复耕')">
|
||||
<span class="statistical_div_dot" style="background: #F4E004" />
|
||||
<span class="statistical_div_span"> 拆除复耕 </span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="10" @click="handlerChangePolygonType('其他')">
|
||||
<span class="statistical_div_dot" style="background: #0382F7" />
|
||||
<span class="statistical_div_span"> 其他 </span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineEmits, defineProps } from 'vue';
|
||||
const emits = defineEmits(['handlerChangePolygonType']);
|
||||
const props = defineProps(['bi']);
|
||||
|
||||
function handlerChangePolygonType(type: String): void {
|
||||
emits('handlerChangePolygonType', type);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.statistical {
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
&_div {
|
||||
position: relative;
|
||||
top: -75px;
|
||||
left: 30px;
|
||||
|
||||
&_dot {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: relative;
|
||||
left: -2px;
|
||||
top: -2px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 17px;
|
||||
// border-radius: 50%;
|
||||
}
|
||||
|
||||
&_span {
|
||||
position: relative;
|
||||
top: -20px;
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
color: #7ebbff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<template>
|
||||
<div class="uav-container">
|
||||
<div class="title">
|
||||
视频监控
|
||||
</div>
|
||||
<div v-show="isShowPlayer">
|
||||
<video
|
||||
class="TCPlayer-video-contaiiner"
|
||||
id="TCPlaeyrContainer"
|
||||
width="235px"
|
||||
height="178px"
|
||||
autoplay
|
||||
preload="auto"
|
||||
playsinline
|
||||
muted
|
||||
webkit-playsinline
|
||||
></video>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import {ref,onMounted,defineProps,onBeforeUnmount} from 'vue';
|
||||
|
||||
const props = defineProps(["playUrl"])
|
||||
|
||||
let player = null;
|
||||
|
||||
const isShowPlayer = ref(false);
|
||||
|
||||
|
||||
function handlerPlayVideo(item){
|
||||
|
||||
isShowPlayer.value = true;
|
||||
|
||||
if (player) {
|
||||
player.src(props.playUrl);
|
||||
} else {
|
||||
player = TCPlayer("TCPlaeyrContainer", {});
|
||||
player.src(props.playUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化视频控件
|
||||
onMounted(()=>{
|
||||
setTimeout(function(){
|
||||
handlerPlayVideo(123);
|
||||
},1000)
|
||||
})
|
||||
|
||||
// 销毁视频控件
|
||||
onBeforeUnmount(()=>{
|
||||
player.dispose();
|
||||
player = null;
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style type="less" scoped>
|
||||
|
||||
.uav-container{
|
||||
width: 418px;
|
||||
height: 300px;
|
||||
background:#041B36;
|
||||
position: relative;
|
||||
|
||||
.title{
|
||||
width:100%;
|
||||
height:40px;
|
||||
background-image:url("/videosupervision/title.png");
|
||||
background-size:100% 100%;
|
||||
line-height:40px;
|
||||
text-indent:18px;
|
||||
font-size:18px;
|
||||
font-weight:bold;
|
||||
color:#fff;
|
||||
}
|
||||
.switch-button{
|
||||
float:right;
|
||||
margin-right:20px;
|
||||
}
|
||||
.uav-list-container{
|
||||
width:100%;
|
||||
height: calc( 100% - 60px);
|
||||
overflow:auto;
|
||||
padding:20px 20px;
|
||||
position:relative;
|
||||
z-index:999;
|
||||
.uav-empty{
|
||||
width:100%;
|
||||
height:100%;
|
||||
text-align:center;
|
||||
color:#999;
|
||||
img{
|
||||
width:120px;
|
||||
margin:40px 0px 18px 0px;
|
||||
}
|
||||
}
|
||||
.uav-item{
|
||||
width:100%;
|
||||
height:40px;
|
||||
padding:0px 15px;
|
||||
line-height:40px;
|
||||
color:#f1f1f1;
|
||||
display:flex;
|
||||
background:rgba(0,0,0,0.2);
|
||||
&:hover{
|
||||
background:rgba(0,0,0,0.4);
|
||||
}
|
||||
div{
|
||||
flex:1;
|
||||
}
|
||||
.position{
|
||||
max-width:30px;
|
||||
cursor:pointer;
|
||||
}
|
||||
.play{
|
||||
max-width:30px;
|
||||
cursor:pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.TCPlayer-video-contaiiner{
|
||||
width: calc( 100% - 30px);
|
||||
height: calc( 100% - 70px);
|
||||
position:absolute;
|
||||
margin:15px;
|
||||
top:40px;
|
||||
right:0px;
|
||||
z-index:1000;
|
||||
background:#041B36;
|
||||
.video-contain {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
.close-video-button{
|
||||
position:absolute;
|
||||
top:40px;
|
||||
right:10px;
|
||||
background:rgba(0,0,0,0.3);
|
||||
z-index:100100;
|
||||
color:#fff;
|
||||
padding:10px;
|
||||
cursor:pointer;
|
||||
&:hover{
|
||||
color:#408eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<div class="county-container">
|
||||
<div class="slider-container" @mousedown="startDrag" @mouseup="endDrag" @mousemove="onDrag"
|
||||
@touchstart="startDrag" @touchend="endDrag" @touchmove="onDrag">
|
||||
<div class="slider-content" :style="{ transform: `translateX(${translateX}px)` }">
|
||||
<!-- 滑动内容项 -->
|
||||
<div class="slider-item" v-for="(item, index) in list" :key="index" :style="{background:url('/statistical/left_county_nochoose.png')}">
|
||||
<div calss="county-name">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, computed, defineEmits, watch, defineExpose } from 'vue';
|
||||
import { getChildrenTree } from '@/api/demo/system';
|
||||
import { getUserOrgs } from '@/api/tiankongdi';
|
||||
|
||||
// emit
|
||||
const emit = defineEmits(['countyClick']);
|
||||
|
||||
// 初始数据
|
||||
const list = ref();
|
||||
|
||||
// 获取用户可以访问的机构信息
|
||||
async function handlerGetOrgs() {
|
||||
let orgs = await getUserOrgs({});
|
||||
let isLevel = orgs.find((item, index) => {
|
||||
return item.name == '临沂市' || item.parentId == 0 || item.parentName == '根节点';
|
||||
});
|
||||
if (isLevel) {
|
||||
// 市级
|
||||
getOptions();
|
||||
} else {
|
||||
// 县区级
|
||||
let levesl = orgs.filter((item, index) => {
|
||||
return item.parentName == '临沂市' || item.parentId == 371300;
|
||||
});
|
||||
list.value = levesl;
|
||||
}
|
||||
// clickCounty(list.value[0])
|
||||
}
|
||||
|
||||
async function getOptions() {
|
||||
const data = await getChildrenTree({
|
||||
parentId: 371300,
|
||||
});
|
||||
data.forEach((item) => {
|
||||
item.isClick = false;
|
||||
});
|
||||
list.value = data;
|
||||
let linyishi = {
|
||||
id: '',
|
||||
name: '临沂市',
|
||||
};
|
||||
console.log("countyList",list.value);
|
||||
list.value.unshift(linyishi);
|
||||
clickCounty(linyishi);
|
||||
}
|
||||
|
||||
//
|
||||
function selectCounty() {}
|
||||
|
||||
// 选择区县
|
||||
function clickCounty(record) {
|
||||
list.value.forEach((item) => {
|
||||
if (item.cascadeId == record.cascadeId) {
|
||||
item.isClick = true;
|
||||
} else {
|
||||
item.isClick = false;
|
||||
}
|
||||
});
|
||||
emit('countyClick', record);
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
handlerGetOrgs();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
selectCounty,
|
||||
});
|
||||
const isDragging = ref(false);
|
||||
const startX = ref(0);
|
||||
const translateX = ref(0);
|
||||
const currentTranslateX = ref(0);
|
||||
const items = ref(['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'])
|
||||
function startDrag(event) {
|
||||
isDragging.value = true;
|
||||
startX.value = event.type.includes('mouse') ? event.clientX : event.touches[0].clientX;
|
||||
currentTranslateX.value = translateX.value;
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
isDragging.value = false;
|
||||
currentTranslateX.value = translateX.value;
|
||||
}
|
||||
|
||||
function onDrag(event) {
|
||||
if (!isDragging.value) return;
|
||||
const currentX = event.type.includes('mouse') ? event.clientX : event.touches[0].clientX;
|
||||
const deltaX = currentX - startX.value;
|
||||
translateX.value = currentTranslateX.value + deltaX;
|
||||
}
|
||||
|
||||
</script>
|
||||
<style type="less" scoped>
|
||||
.county-container{
|
||||
position:absolute;
|
||||
bottom:50px;
|
||||
left:300px;
|
||||
z-index:999999;
|
||||
width:840px;
|
||||
height:80px;
|
||||
background:rgba(0,0,0,0.4);
|
||||
.slider-container {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.slider-content {
|
||||
display: flex;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.slider-item {
|
||||
min-width: 100px;
|
||||
margin: 0px 0px;
|
||||
background-color: #ddd;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
<template>
|
||||
<div class="screen-header-container">
|
||||
<div class="screen-title">
|
||||
<img src="/statistical/logo.png" alt="" /> {{ t('sys.subject.header_title') }}
|
||||
</div>
|
||||
<span class="screen-currentTime">
|
||||
<span class="time">{{ currentTime }}</span>
|
||||
|
||||
<span class="weather">{{ Weather }}</span>
|
||||
</span>
|
||||
<div class="left-category-container">
|
||||
<div
|
||||
class="category-item"
|
||||
v-for="(item, index) in left_categorys"
|
||||
:key="index"
|
||||
:style="{
|
||||
left: `${-(index * 26)}px`,
|
||||
'background-image': item.hovered
|
||||
? `url(/statistical/category-left-active.png)`
|
||||
: `url(/statistical/category-left.png)`,
|
||||
}"
|
||||
@click="getHome(item)"
|
||||
@mouseover="item.hovered = true"
|
||||
@mouseout="item.hovered = false"
|
||||
>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="right_category-container">
|
||||
<div
|
||||
class="category-item"
|
||||
v-for="(item, index) in right_categorys"
|
||||
:key="index"
|
||||
:style="{
|
||||
left: `${-(index * 26 - 55)}px`,
|
||||
'background-image': item.hovered
|
||||
? `url(/statistical/category-right-active.png)`
|
||||
: `url(/statistical/category-right.png)`,
|
||||
}"
|
||||
@click="getHome(item)"
|
||||
@mouseover="item.hovered = true"
|
||||
@mouseout="item.hovered = false"
|
||||
>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-info" style="z-index: 9999">
|
||||
<span style="color: #325e95">{{ fireUserLoginName }}</span>
|
||||
|
||||
<span style="color: #ddd" @click="logout">退出</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right_category-container">
|
||||
<div
|
||||
v-for="(item, index) in right_categorys"
|
||||
:key="item.id"
|
||||
class="category-item"
|
||||
:style="{
|
||||
left: `${-(index * 26)}px`,
|
||||
'background-image': item.hovered
|
||||
? `url(/statistical/category-right-active.png)`
|
||||
: `url(/statistical/category-right.png)`,
|
||||
}"
|
||||
@click="getHome(item)"
|
||||
@mouseover="item.hovered = true"
|
||||
@mouseout="item.hovered = false"
|
||||
>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-info" style="z-index: 9999">
|
||||
<span style="color: #325e95">{{ fireUserLoginName }}</span>
|
||||
|
||||
<span style="color: #ddd" @click="logout">退出</span>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineEmits, onMounted, onUnmounted, watchEffect, h } from 'vue';
|
||||
import axios from 'axios';
|
||||
import dayjs from 'dayjs';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getSpecialData } from '@/api/demo/system';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useMultipleTabStore } from '@/store/modules/multipleTab';
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
// 上方菜单
|
||||
const left_categorys = ref([]);
|
||||
const right_categorys = ref([]);
|
||||
async function getMenu() {
|
||||
const res: any = await getSpecialData();
|
||||
console.log('res123', res);
|
||||
|
||||
// 综合监管
|
||||
let category: any = {
|
||||
title: '综合监管',
|
||||
isDevelop: false,
|
||||
hovered: false,
|
||||
};
|
||||
left_categorys.value?.push(category);
|
||||
res?.forEach((item, index) => {
|
||||
if (index < 2) {
|
||||
// 违法用地、非法采矿
|
||||
item.hovered = false;
|
||||
left_categorys.value?.push(item);
|
||||
} else if (index >= 2 && index < 5) {
|
||||
// 重点问题、耕地非粮化、巡察审计
|
||||
item.hovered = false;
|
||||
right_categorys.value?.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 登陆用户
|
||||
const fireUserLoginName = ref(localStorage.getItem('fireUserLoginName'));
|
||||
function getFireUserLoginName() {
|
||||
fireUserLoginName.value = localStorage.getItem('fireUserLoginName')
|
||||
? localStorage.getItem('fireUserLoginName')
|
||||
: '';
|
||||
}
|
||||
|
||||
const tabStore = useMultipleTabStore();
|
||||
|
||||
function logout() {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: () => h('span', t('sys.navigation.logoutTip')),
|
||||
content: () => h('span', t('sys.navigation.logoutMessage')),
|
||||
onOk: async () => {
|
||||
tabStore.resetState();
|
||||
await router.replace(PageEnum.SUBJECT_HOME);
|
||||
},
|
||||
});
|
||||
}
|
||||
async function getHome(item) {
|
||||
console.log(window.innerWidth);
|
||||
if (!item.isDevelop) {
|
||||
message.warn(item.title + '系统暂未开放!');
|
||||
return;
|
||||
}
|
||||
if (item.isExternal) {
|
||||
if (item.linkOrApi) {
|
||||
window.location.href = item.linkOrApi;
|
||||
} else {
|
||||
message.warn('系统外部链接未配置!');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!item.linkOrApi) {
|
||||
item.linkOrApi = PageEnum.WELCOME_HOME;
|
||||
}
|
||||
userStore.setSubject(item.id, item.title, item.logoTitle, item.linkOrApi);
|
||||
}
|
||||
}
|
||||
|
||||
// 时间和天气
|
||||
const currentTime = ref(dayjs().locale('zh-cn').format('YYYY年M月D日 dddd HH:mm:ss'));
|
||||
const Weather = ref('');
|
||||
async function getWeather() {
|
||||
try {
|
||||
// 百度国内天气查询API
|
||||
const response = await axios.get('https://api.map.baidu.com/weather/v1/', {
|
||||
params: {
|
||||
district_id: 371300,
|
||||
data_type: 'all',
|
||||
ak: 'qbMvuMbWe1fJnOBNLJFsjoeHC3wfWkoB',
|
||||
},
|
||||
timeout: 15000,
|
||||
});
|
||||
Weather.value = response?.result?.now?.text;
|
||||
} catch (error) {
|
||||
// 备用
|
||||
// http://www.tianqiapi.com/index/doc?version=v63
|
||||
// 若次数试用用尽,注册账号后,切换appid和appsecret即可
|
||||
axios({
|
||||
method: 'get',
|
||||
url: `http://v0.yiketianqi.com/api?unescape=1&version=v63&appid=88136471&appsecret=Hw4GLOcA&city=临沂`,
|
||||
}).then((res) => {
|
||||
Weather.value = res.data.wea;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
currentTime.value = dayjs().locale('zh-cn').format('YYYY年M月D日 dddd HH:mm');
|
||||
}
|
||||
|
||||
// 监听每一秒更新时间
|
||||
watchEffect(() => {
|
||||
const intervalId = setInterval(updateTime, 1000);
|
||||
// 清除定时器
|
||||
onUnmounted(() => clearInterval(intervalId));
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
updateTime();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getWeather();
|
||||
getMenu();
|
||||
getFireUserLoginName();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.screen-header-container {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
// background-image: url('/statistical/header.png');
|
||||
// background-size: 100% 96px;
|
||||
background:
|
||||
// linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.2) 80%, rgba(0, 0, 0, 0) 100%)
|
||||
// no-repeat top left / 100% 90px,
|
||||
url('/statistical/mask.png') no-repeat top left / 100% 100px,
|
||||
url('/statistical/header.png') no-repeat top left / 100% 96px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 3;
|
||||
height: 94px;
|
||||
.screen-title {
|
||||
width: 682px;
|
||||
// height: 96px;
|
||||
line-height: 90px;
|
||||
font-family: Alibaba PuHuiTi;
|
||||
font-weight: bold;
|
||||
font-size: 45px;
|
||||
color: #8be5ee;
|
||||
background: linear-gradient(0deg, #c6f3ff 0%, #f3feff 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin: 0px auto;
|
||||
text-align: center;
|
||||
img {
|
||||
position: relative;
|
||||
top: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
.screen-currentTime {
|
||||
position: relative;
|
||||
left: 50px;
|
||||
top: -60px;
|
||||
|
||||
width: 344px;
|
||||
height: 14px;
|
||||
font-family: DengXian;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #8bd8ff;
|
||||
line-height: 3px;
|
||||
font-style: italic;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.time,
|
||||
.weather {
|
||||
display: inline-block;
|
||||
}
|
||||
.separator {
|
||||
margin: 0 50px;
|
||||
}
|
||||
.left-category-container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 50%;
|
||||
transform: translate(calc(-50% - 570px), 0);
|
||||
.category-item {
|
||||
float: left;
|
||||
width: 138px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-image: url('/statistical/category-left.png');
|
||||
background-size: 138px 32px;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.right_category-container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 50%;
|
||||
transform: translate(calc(50% + 570px), 0);
|
||||
.category-item {
|
||||
float: left;
|
||||
width: 138px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-image: url('/statistical/category-right.png');
|
||||
background-size: 138px 32px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
width: 120px;
|
||||
color: #e1e1e1;
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
right: 50px;
|
||||
font-size: 14px;
|
||||
z-index: 9;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: #8be5ee;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
export const layers = [
|
||||
{
|
||||
id: 'house',
|
||||
name:"房屋",
|
||||
checked:false,
|
||||
tableName: 'view_shp_idle_house',
|
||||
color: '#F70303',
|
||||
},
|
||||
{
|
||||
id: 'land',
|
||||
name:"土地资源",
|
||||
checked:false,
|
||||
tableName: 'view_shp_idle_land',
|
||||
color: '#0AF703',
|
||||
},
|
||||
{
|
||||
id: 'waters',
|
||||
name:"水域",
|
||||
checked:false,
|
||||
tableName: 'view_shp_idle_waters',
|
||||
color: '#0382F7',
|
||||
},
|
||||
{
|
||||
id: 'farming',
|
||||
name:"农业生产设施",
|
||||
checked:false,
|
||||
tableName: 'view_shp_idle_farming',
|
||||
color: '#F4E004',
|
||||
},
|
||||
{
|
||||
id: 'nongji',
|
||||
name:"农机组织",
|
||||
checked:false,
|
||||
tableName: 'view_shp_idle_nongji',
|
||||
color: '#F70303',
|
||||
},
|
||||
]
|
||||
Loading…
Reference in New Issue