CaiYuanYiTiHua/src/components/CloudQueryContent/CloudQueryModal/BasicQuery/index.vue

553 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="show-map-div">
<div class="select-menu">
<div class="add-on-map">
<a-checkbox v-model:checked="addOnMap" @change="(e) => addOnMapChange(e.target.checked)">
叠加到地图
</a-checkbox>
</div>
<a-radio-group v-model:value="selectType" button-style="solid" size="small">
<a-radio-button :value="0">专题图</a-radio-button>
<!-- <a-radio-button :value="1">截图</a-radio-button> -->
<!-- <a-radio-button :value="2">天地图</a-radio-button> -->
</a-radio-group>
</div>
<div class="slider-menu">
<!-- <a-slider
v-if="addOnMap && sliderShow"
v-model:value="nowSliderValue"
vertical="true"
:tooltipOpen="false"
:tip-formatter="getValue"
:marks="marks"
:step="1"
min="0"
max="10"
@afterChange="sliderChange"
>
<template #mark="{ label, point }">
<template v-if="point >= 0">
<span
:style="{
color: 'white',
position: 'relative',
top: `${7}px`,
left: `${3}px`,
}"
>{{ label }}</span
>
</template>
</template>
</a-slider>
<a-tooltip placement="right">
<template #title>
<span>设置叠加到地图的图片的透明度</span>
</template>
<PlusCircleOutlined
v-if="(addOnMap || selectType == 2) && sliderShow"
:style="{
position: 'absolute',
fontSize: '18px',
bottom: '-28px',
right: '8px',
color: 'white',
}"
@click="changeSliderShow"
/>
<MinusCircleOutlined
v-if="(addOnMap || selectType == 2) && !sliderShow"
:style="{
position: 'absolute',
fontSize: '18px',
bottom: '-28px',
right: '8px',
color: 'white',
}"
@click="changeSliderShow"
/>
</a-tooltip> -->
</div>
<div class="expandShow-menu">
<FullscreenOutlined
v-if="addOnMap && !expandShow"
:style="{
position: 'absolute',
fontSize: '28px',
bottom: '-65px',
right: '4px',
color: 'white',
zIndex: 11,
}"
@click="expand(!expandShow)"
/>
<FullscreenExitOutlined
v-if="addOnMap && expandShow"
:style="{
position: 'fixed',
fontSize: '35px',
bottom: '20px',
right: '20px',
color: 'white',
zIndex: 1012,
}"
@click="expand(!expandShow)"
/>
</div>
<template v-if="selectType === 0">
<a-image
v-if="!addOnMap"
:width="470"
:height="470"
:src="convertValidUrl(showData.url)"
:preview="{
src: showData.previewUrl,
}"
/>
</template>
<template v-if="selectType === 1 && !addOnMap">
<a-image
v-if="!addOnMap"
:width="470"
:height="470"
:src="convertValidUrl(showData.urlxian)"
:preview="{
src: showData.previewUrlXian,
}"
/>
</template>
<ModalMap
v-if="addOnMap"
:width="modalMapWidth"
:height="modalMapHeight"
:zIndex="modalMapZIndex"
:position="modalMapPosition"
:centerAndZoom="props.data"
@getMap="getMap"
/>
</div>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="土地分类">
<ShowTableList
:columns="landClassificationColumns"
:data="landClassifyTable"
:title="'土地利用现状查询结果'"
/>
</a-tab-pane>
<a-tab-pane key="2" tab="耕地占用">
<ShowTableList
:columns="landPlanningColumns"
:data="plowLandOccupyTable"
:title="'土地规划查询结果'"
/>
</a-tab-pane>
</a-tabs>
</template>
<script setup lang="ts">
import { ref, defineProps, computed, watch, onMounted } from 'vue';
import {
PlusCircleOutlined,
MinusCircleOutlined,
FullscreenOutlined,
FullscreenExitOutlined,
} from '@ant-design/icons-vue';
import ShowTableList from '@/views/dashboard/test/components/ShowTableList/index.vue';
import ModalMap from './ModalMap/index.vue';
// 图片路径拼接
import { getAppEnvConfig } from '@/utils/env';
const { VITE_GLOB_API_URL } = getAppEnvConfig();
const VITE_GLOB_API_URL_VAR = ref<String>(VITE_GLOB_API_URL + '/');
const props = defineProps(['data']);
let map;
// 土地分类和耕地占用
const activeKey = ref('1');
// 是否叠加到地图
const addOnMap = ref(true);
// 专题图、截图、天地图
const selectType = ref(0);
watch(
// 土地分类和耕地占用
() => activeKey.value,
() => {
// 查看结果进入就显示叠加的图
if (addOnMap.value) {
addOnMapChange(true);
}
},
);
watch(
// 专题图、截图、天地图
() => selectType.value,
() => {
// 查看结果进入就显示叠加的图
if (addOnMap.value) {
addOnMapChange(true);
}
},
);
// 表格数据
const landClassifyTable = computed(() => {
let data;
props.data?.forEach((item, index) => {
if (item.name == '土地分类') {
data = { ...item };
}
// 第一次打开
if (index == 0) {
// 移动位置
let fourpoint = JSON.parse(`[${item.fourpoint}]`);
setTimeout(() => {
moveLocation(fourpoint);
}, 200);
// 查看结果进入就显示叠加的图
addOnMapChange(true);
}
// 生成白色底色的预览图片
if (item.url && !item.previewUrl) {
changeTransparentColorToWhite(convertValidUrl(item.url)).then((resultImageUrl) => {
item.previewUrl = resultImageUrl;
});
}
if (item.urlxian && !item.previewUrlXian) {
changeTransparentColorToWhite(convertValidUrl(item.urlxian)).then((resultImageUrl) => {
item.previewUrlXian = resultImageUrl;
});
}
});
return data && data.list;
});
const plowLandOccupyTable = computed(() => {
let data;
props.data?.forEach((item) => {
if (item.name == '耕地占用') {
data = { ...item };
}
});
return data && data.list;
});
const showData = computed(() => {
let data;
switch (activeKey.value) {
case '1':
props.data?.forEach((item) => {
if (item.name == '土地分类') {
data = { ...item };
}
});
return data;
case '2':
props.data?.forEach((item) => {
if (item.name == '耕地占用') {
data = { ...item };
}
});
return data;
}
});
// 表格结构
const landClassificationColumns = [
{
title: '地类名称',
dataIndex: 'type',
key: 'type',
width: 180,
sorter: (a, b) => a.landName - b.landName,
sortDirections: ['descend', 'ascend'],
},
{
title: '联合属性',
dataIndex: 'stats',
key: 'stats',
width: 100,
sorter: (a, b) => a.stats.length - b.stats.length,
sortDirections: ['descend', 'ascend'],
},
{
title: '面积(亩)',
dataIndex: 'area',
key: 'area',
width: 150,
sorter: (a, b) => a.area - b.area,
sortDirections: ['descend', 'ascend'],
},
];
const landPlanningColumns = [
{
title: '类型',
dataIndex: 'type',
key: 'type',
sorter: (a, b) => a.type.length - b.type.length,
sortDirections: ['descend', 'ascend'],
},
{
title: '面积(亩)',
dataIndex: 'area',
key: 'area',
sorter: (a, b) => a.area - b.area,
sortDirections: ['descend', 'ascend'],
},
];
const getMap = (value) => {
map = value;
};
// 定义预加载图片的方法------------------------------------------------------------------------------------------------
const addOnMapChange = (value) => {
setTimeout(async () => {
let url: any = '';
let fourpoint = '';
if (value) {
let data: any = {};
if (activeKey.value == '1') {
props.data.forEach((item) => {
if (item.name == '土地分类') {
data = { ...item };
}
});
} else if (activeKey.value == '2') {
props.data.forEach((item) => {
if (item.name == '耕地占用') {
data = { ...item };
}
});
}
if (selectType.value == 0 || selectType.value == 2) {
url = convertValidUrl(data.url);
fourpoint = JSON.parse(`[${data.fourpoint}]`);
}
if (selectType.value == 1) {
url = convertValidUrl(data.urlxian);
fourpoint = JSON.parse(`[${data.fourpoint}]`);
}
// 图片预加载
let image = new Image();
image.crossOrigin = 'anonymous';
image.src = url;
image.onload = () => {
// 创建Canvas元素
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
// 获取Canvas绘图上下文
const ctx = canvas.getContext('2d');
// 在Canvas上绘制图片
ctx.drawImage(image, 0, 0, image.width, image.height);
// 将Canvas内容转换为图片的数据URL
const dataURL = canvas.toDataURL('image/png');
// 移除旧的图层和源
if (map) {
if (map.getLayer('radar-layer')) {
map.removeLayer('radar-layer');
}
if (map.getSource('radar')) {
map.removeSource('radar');
}
}
// 添加新的源和图层
map.addSource('radar', {
type: 'image',
url: dataURL,
coordinates: fourpoint,
});
map.addLayer({
id: 'radar-layer',
type: 'raster',
source: 'radar',
paint: {
'raster-opacity': parseFloat(getValue(nowSliderValue.value)),
},
});
moveLocation(fourpoint);
};
}
sliderShow.value = false;
// console.log(337, props.data);
}, 500);
};
// 移动位置
function moveLocation(fourpoint) {
let x = 0;
let y = 0;
let length = fourpoint.length;
for (let i = 0; i < fourpoint.length; i++) {
x = x + fourpoint[i][0];
y = y + fourpoint[i][1];
}
x = x / length;
y = y / length;
if (map) {
map.jumpTo({
center: [x, y],
zoom: calculateZoom(fourpoint),
});
}
return [x, y];
}
// 计算合适的缩放级别
function calculateZoom(points) {
// 计算边界框
const nw = points[0];
const ne = points[1];
const se = points[2];
const latRange = Math.abs(nw[1] - se[1]);
const lngRange = Math.abs(nw[0] - ne[0]);
// 计算缩放级别
const zoomLat = Math.log2(360 / latRange) - 1;
const zoomLng = Math.log2(360 / lngRange) - 1;
// 返回最小的缩放级别,并保留小数后两位
return Math.min(zoomLat * 1.05, zoomLng * 1.05).toFixed(2);
}
// 全屏--------------------------------------------------------------------------------------------------------------
const modalMapWidth = ref('470px');
const modalMapHeight = ref('470px');
const modalMapZIndex = ref(10);
const modalMapPosition = ref('absolute');
const expandShow = ref(false);
// 全屏
function expand(value) {
if (value) {
modalMapWidth.value = '100%';
modalMapHeight.value = '100%';
modalMapZIndex.value = 1011;
modalMapPosition.value = 'fixed';
} else {
modalMapWidth.value = '470px';
modalMapHeight.value = '470px';
modalMapZIndex.value = 10;
modalMapPosition.value = 'absolute';
}
setTimeout(() => {
map.resize();
}, 100);
expandShow.value = value;
}
// 图片------------------------------------------------------------------------------------------------------------
// 正则表达式验证URL转换
function convertValidUrl(url: string): boolean {
const regex =
/^(?:http|ftp)s?:\/\/(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|localhost|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[?[A-F0-9]*:[A-F0-9:]+\]?)(?::\d+)?(?:\/?|[\/?]\S+)$/i;
let result: any = regex.test(url) ? url : VITE_GLOB_API_URL_VAR.value + url;
return result;
}
// 预览变成白色的底色
async function changeTransparentColorToWhite(imageUrl) {
return new Promise((resolve, reject) => {
// 创建一个新的 Image 对象
const img = new Image();
img.crossOrigin = 'Anonymous'; // 处理跨域问题
img.src = imageUrl;
img.onload = () => {
// 创建一个 canvas 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 设置 canvas 的尺寸与图片相同
canvas.width = img.width;
canvas.height = img.height;
// 将图片绘制到 canvas 上
ctx.drawImage(img, 0, 0);
// 获取 canvas 的图像数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// 遍历每个像素,将透明部分变成白色
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] === 0) {
// 如果 alpha 通道为 0完全透明
data[i] = 255; // 红色通道
data[i + 1] = 255; // 绿色通道
data[i + 2] = 255; // 蓝色通道
data[i + 3] = 255; // alpha 通道(不透明)
}
}
// 将修改后的图像数据放回 canvas
ctx.putImageData(imageData, 0, 0);
// 将 canvas 内容转换为图片数据 URL
const resultImageUrl = canvas.toDataURL('image/png');
// 返回处理后的新图片的 URL
resolve(resultImageUrl);
};
img.onerror = (error) => {
reject(error);
};
});
}
// 透明度-----------------------------------------------------------------------------------------------------------
const nowSliderValue = ref<number>(10);
// 标识
const marks = ref<Record<number, any>>({
0: 0,
1: 0.1,
2: 0.2,
3: 0.3,
4: 0.4,
5: 0.5,
6: 0.6,
7: 0.7,
8: 0.8,
9: 0.9,
10: 1,
});
// 获取标识值
const getValue = (key: number) => {
if (marks.value) {
return marks.value[key];
}
};
// 发生改变
const sliderChange = (key: number) => {
map.setPaintProperty('radar-layer', 'raster-opacity', parseFloat(getValue(key)));
};
// 设置叠加到地图的图片的透明度
const sliderShow = ref(false);
function changeSliderShow() {
sliderShow.value = !sliderShow.value;
}
</script>
<style lang="scss" scoped>
.select-menu {
display: flex;
position: absolute;
top: 10px;
right: 10px;
z-index: 100;
.add-on-map {
width: 110px;
background: rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
}
.slider-menu {
position: absolute;
right: 10px;
top: 90px;
height: 300px;
z-index: 100;
}
.show-map-div {
width: 470px;
height: 470px;
position: relative;
}
.expandShow-menu {
position: absolute;
right: 10px;
bottom: 80px;
}
</style>