bug fix and performance improvements
This commit is contained in:
527
src/views/website/home/comp/index0.vue
Normal file
527
src/views/website/home/comp/index0.vue
Normal file
@ -0,0 +1,527 @@
|
||||
<script setup>
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
import { computed, onMounted, ref, shallowRef, toRefs, watch } from "vue";
|
||||
import { init, registerMap } from "echarts";
|
||||
import {
|
||||
countAchievementByArea,
|
||||
countAchievementByCity,
|
||||
countAchievementByProvince,
|
||||
countDemandByArea,
|
||||
countDemandByCity,
|
||||
countDemandByProvince,
|
||||
countEnterpriseByArea,
|
||||
countEnterpriseByCity,
|
||||
countEnterpriseByProvince,
|
||||
countExpertByArea,
|
||||
countExpertByCity,
|
||||
countExpertByProvince,
|
||||
} from "@/api/website/home";
|
||||
import backBtnPng from "@/assets/images/map_back.png";
|
||||
import RegionPagine from "@/views/website/home/comp/RegionPagine.vue";
|
||||
import html2canvas from "html2canvas";
|
||||
import anime from "animejs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import geoJSONRU from "@/assets/custom.geo.json";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const leftBoxPageNum = ref(1);
|
||||
const rightBoxPageNum = ref(1);
|
||||
const mapRef = ref(null);
|
||||
const districtSearch = shallowRef(null);
|
||||
const myEcharts = shallowRef(null);
|
||||
const map = shallowRef(null);
|
||||
const mapData = ref([]);
|
||||
const props = defineProps({
|
||||
mapIndex: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const { mapIndex } = toRefs(props);
|
||||
const emit = defineEmits(["changeMapIndex"]);
|
||||
const methods = [
|
||||
{
|
||||
name: "expert",
|
||||
title: computed(() => t("map.expertMap")),
|
||||
byProvince: countExpertByProvince,
|
||||
byCity: countExpertByCity,
|
||||
byArea: countExpertByArea,
|
||||
},
|
||||
{
|
||||
name: "technology",
|
||||
// title: "技术分布地图",
|
||||
title: computed(() => t("map.technologyMap")),
|
||||
byProvince: countAchievementByProvince,
|
||||
byCity: countAchievementByCity,
|
||||
byArea: countAchievementByArea,
|
||||
},
|
||||
{
|
||||
name: "demand",
|
||||
// title: "需求分布地图",
|
||||
title: computed(() => t("map.demandMap")),
|
||||
byProvince: countDemandByProvince,
|
||||
byCity: countDemandByCity,
|
||||
byArea: countDemandByArea,
|
||||
},
|
||||
{
|
||||
name: "enterprise",
|
||||
// title: "企业分布地图",
|
||||
title: computed(() => t("map.enterpriseMap")),
|
||||
byProvince: countEnterpriseByProvince,
|
||||
byCity: countEnterpriseByCity,
|
||||
byArea: countEnterpriseByArea,
|
||||
},
|
||||
];
|
||||
|
||||
const areaCount = ref([]); // 按行政区划统计
|
||||
const industryCount = ref([]); // 按领域统计
|
||||
const areaCountPaged = computed(() =>
|
||||
areaCount.value.slice(
|
||||
(leftBoxPageNum.value - 1) * 5,
|
||||
leftBoxPageNum.value * 5
|
||||
)
|
||||
);
|
||||
const industryCountPaged = computed(() =>
|
||||
industryCount.value.slice(
|
||||
(rightBoxPageNum.value - 1) * 5,
|
||||
rightBoxPageNum.value * 5
|
||||
)
|
||||
);
|
||||
const options = {
|
||||
// nameProperty: 'adcode',
|
||||
visualMap: {
|
||||
type: "piecewise",
|
||||
left: "center", //组件离容器左侧的距离,'left', 'center', 'right','20%'
|
||||
bottom: "30",
|
||||
orient: "horizontal", //图例排列方向
|
||||
padding: 5,
|
||||
pieces: [
|
||||
{ gte: 0, lte: 99, label: "99", color: "#CAE9FD" },
|
||||
{ gte: 100, lte: 299, label: "100-299", color: "#7ED2F7" },
|
||||
{ gte: 300, lte: 499, label: "299-499", color: "#039DDD" },
|
||||
{ gte: 500, label: "500", color: "#0D4884" },
|
||||
],
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
visibility: "off",
|
||||
},
|
||||
tooltip: {
|
||||
//提示框信息
|
||||
trigger: "item",
|
||||
formatter: "{b}\n{c}人",
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "map",
|
||||
name: "中国",
|
||||
map: "map",
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
const loadAMap = async () => {
|
||||
const AMap = await AMapLoader.load({
|
||||
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "2.0", // 指定要加载的 JS API 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
/*"AMap.DistrictSearch", "AMap.DistrictExplorer"*/
|
||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 缺省 1.1
|
||||
plugins: [], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
});
|
||||
map.value = new AMap.Map("container", {
|
||||
zoom: 4, //级别
|
||||
center: [108.946609, 34.262324], //中心点坐标
|
||||
// viewMode: '3D' //使用3D视图
|
||||
});
|
||||
map.value.on("click", mapClick);
|
||||
// districtSearch.value = new AMap.DistrictSearch({
|
||||
// level: 'province',
|
||||
// extensions: 'all',
|
||||
// subdistrict: 1,
|
||||
// showbiz: false,
|
||||
// })
|
||||
};
|
||||
const loading = ref(false);
|
||||
const loadChinaDistrict = (adcode) => {
|
||||
loading.value = true;
|
||||
AMapUI.loadUI(["geo/DistrictExplorer"], (DistrictExplorer) => {
|
||||
const districtExplorer = new DistrictExplorer({
|
||||
map: map, //关联的地图实例
|
||||
});
|
||||
|
||||
districtExplorer.loadAreaNode(adcode, async (error, areaNode) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
const parentAdcode = areaNode.getAdcode();
|
||||
// 绘制载入的区划节点
|
||||
const geoJSON = {
|
||||
type: "FeatureCollection",
|
||||
features: areaNode.getSubFeatures(),
|
||||
};
|
||||
const level = getLevel(adcode);
|
||||
let result;
|
||||
try {
|
||||
if (level === 0) {
|
||||
result = await methods[mapIndex.value]["byProvince"]();
|
||||
} else if (level === 1) {
|
||||
result = await methods[mapIndex.value]["byCity"](adcode);
|
||||
} else if (level === 2) {
|
||||
result = await methods[mapIndex.value]["byArea"](adcode);
|
||||
}
|
||||
} catch (e) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
if (result.industry)
|
||||
industryCount.value = Object.keys(result.industry).map((key) => ({
|
||||
name: key,
|
||||
count: result.industry[key],
|
||||
}));
|
||||
areaCount.value = result.count ?? [];
|
||||
mapData.value = geoJSON.features.map((el) => {
|
||||
const areaProp = el.properties;
|
||||
return {
|
||||
adcode: areaProp.adcode,
|
||||
name: areaProp.name,
|
||||
value:
|
||||
result.count.find((el) => el.code == areaProp.adcode)?.count ?? 0,
|
||||
};
|
||||
});
|
||||
|
||||
registerMap("map", { geoJSON, specialAreas: {} });
|
||||
options.series[0].data = mapData.value;
|
||||
myEcharts.value.setOption(options);
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const loadRussiaDistrict = () => {
|
||||
registerMap("map", { geoJSON: geoJSONRU, specialAreas: {} });
|
||||
myEcharts.value.setOption(options);
|
||||
// TODO:get count
|
||||
};
|
||||
// 返回地图上一级
|
||||
const backMap = () => {
|
||||
// loadChinaDistrict(parentAdcode)
|
||||
loadChinaDistrict("100000");
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await loadAMap();
|
||||
myEcharts.value = init(mapRef.value);
|
||||
myEcharts.value.on("click", mapClick);
|
||||
if (locale.value === "zh") {
|
||||
loadChinaDistrict("100000");
|
||||
} else {
|
||||
loadRussiaDistrict();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 根据adcode判断省市区级别
|
||||
* @param adcode
|
||||
* @return {number} 国家: 0 省: 1 市: 2 区: 3
|
||||
*/
|
||||
const getLevel = (adcode) => {
|
||||
adcode = adcode.toString();
|
||||
if (adcode === "100000") {
|
||||
return 0;
|
||||
}
|
||||
const splitPattern = /(\d{2})(\d{2})(\d{2})/;
|
||||
const resultArray = adcode.match(splitPattern).slice(1);
|
||||
// 国家: 0 省: 1 市: 2 区: 3
|
||||
let result = resultArray.indexOf("00");
|
||||
if (result === -1) {
|
||||
result = 3;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const mapClick = (ev) => {
|
||||
const level = getLevel(ev.data.adcode);
|
||||
if (level === 3) {
|
||||
return;
|
||||
}
|
||||
loadChinaDistrict(ev.data.adcode);
|
||||
};
|
||||
|
||||
const playScrollAnimation = (direction, start) => {
|
||||
const pageWrap = document.querySelector(".page-wrap");
|
||||
const rootWrap = document.querySelector(".root-container");
|
||||
html2canvas(pageWrap).then((canvas) => {
|
||||
canvas.addEventListener("wheel", (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
});
|
||||
if (direction === "down") {
|
||||
rootWrap.insertBefore(canvas, pageWrap);
|
||||
} else if (direction === "up") {
|
||||
rootWrap.appendChild(canvas);
|
||||
rootWrap.style.transform = "translateY(-100%)";
|
||||
}
|
||||
anime({
|
||||
targets: rootWrap,
|
||||
translateY: direction === "down" ? "-100%" : "0",
|
||||
duration: 300,
|
||||
easing: "linear",
|
||||
begin: start,
|
||||
complete: () => {
|
||||
rootWrap.removeChild(canvas);
|
||||
rootWrap.style.transform = "translateY(0%)";
|
||||
isScrolling.value = false;
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const isScrolling = ref(false);
|
||||
const handleScroll = async (ev) => {
|
||||
let direction = ev.deltaY > 0 ? "down" : "up";
|
||||
// 判断滚轮滚动方向
|
||||
if (
|
||||
(direction === "down" && mapIndex.value >= methods.length - 1) ||
|
||||
(direction === "up" && mapIndex.value <= 0)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
if (isScrolling.value) {
|
||||
return;
|
||||
}
|
||||
isScrolling.value = true;
|
||||
playScrollAnimation(direction, () => {});
|
||||
if (direction === "down" && mapIndex.value < methods.length - 1) {
|
||||
emit("changeMapIndex", mapIndex.value + 1);
|
||||
} else if (direction === "up" && mapIndex.value > 0) {
|
||||
emit("changeMapIndex", mapIndex.value - 1);
|
||||
}
|
||||
};
|
||||
|
||||
watch(mapIndex, (newVal, oldVal) => {
|
||||
console.log(`mapIndex change from ${oldVal} to ${newVal}`);
|
||||
if (newVal === oldVal) {
|
||||
return;
|
||||
}
|
||||
isScrolling.value = true;
|
||||
const direction = newVal > oldVal ? "down" : "up";
|
||||
playScrollAnimation(direction, () => {});
|
||||
if (locale.value === "zh") {
|
||||
loadChinaDistrict("100000");
|
||||
} else {
|
||||
loadRussiaDistrict();
|
||||
}
|
||||
});
|
||||
|
||||
watch(locale, (newVal) => {
|
||||
console.log(newVal);
|
||||
if (newVal === "zh") {
|
||||
loadChinaDistrict("100000");
|
||||
} else if (newVal === "ru") {
|
||||
loadRussiaDistrict();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="root-wrap">
|
||||
<div class="root-container">
|
||||
<div class="page-wrap" @wheel="handleScroll">
|
||||
<div class="title">{{ methods[mapIndex].title.value }}</div>
|
||||
<div v-if="loading" class="loading-modal"></div>
|
||||
<!-- 返回上一级按钮 -->
|
||||
<div class="back-btn" @click="backMap">
|
||||
<img :src="backBtnPng" alt="back" />
|
||||
</div>
|
||||
<!-- 人数表格 -->
|
||||
<div class="count-table area">
|
||||
<div class="table">
|
||||
<div class="head">
|
||||
<div class="th">
|
||||
<div class="title">地区</div>
|
||||
<div class="count">人数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div v-for="item in areaCountPaged" :key="item.adcode" class="tr">
|
||||
<div class="title">{{ item.name }}</div>
|
||||
<div class="count">{{ item.count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RegionPagine
|
||||
v-model:page="leftBoxPageNum"
|
||||
:total="areaCount.length"
|
||||
/>
|
||||
</div>
|
||||
<!-- 领域 -->
|
||||
<div class="count-table industry">
|
||||
<div class="table">
|
||||
<div class="head">
|
||||
<div class="th">
|
||||
<div class="title">领域</div>
|
||||
<div class="count">人数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div
|
||||
v-for="item in industryCountPaged"
|
||||
:key="item.adcode"
|
||||
class="tr"
|
||||
>
|
||||
<div class="title">{{ item.name }}</div>
|
||||
<div class="count">{{ item.count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RegionPagine
|
||||
v-model:page="rightBoxPageNum"
|
||||
:total="industryCount.length"
|
||||
/>
|
||||
</div>
|
||||
<div id="map-container" ref="mapRef"></div>
|
||||
<div
|
||||
id="container"
|
||||
style="
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(0, 0, 0);
|
||||
display: none;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-to-top {
|
||||
animation: 1s linear infinite page-scroll;
|
||||
}
|
||||
|
||||
.root-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.root-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
> canvas {
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
//top: 0;
|
||||
//left: 0;
|
||||
position: relative;
|
||||
//transition: all 0.3s linear;
|
||||
//animation: 1s linear forwards page-scroll;
|
||||
.loading-modal {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
#map-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(0deg, #010101, #041744);
|
||||
}
|
||||
|
||||
> .title {
|
||||
position: absolute;
|
||||
top: 75px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 36px;
|
||||
font-family: Source Han Sans CN, sans-serif;
|
||||
font-weight: 300;
|
||||
color: #ffffff;
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
position: absolute;
|
||||
left: 120px;
|
||||
top: 120px;
|
||||
z-index: 99;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.count-table {
|
||||
position: absolute;
|
||||
|
||||
z-index: 101;
|
||||
|
||||
&.area {
|
||||
top: 200px;
|
||||
left: 120px;
|
||||
}
|
||||
|
||||
&.industry {
|
||||
top: 200px;
|
||||
right: 120px;
|
||||
}
|
||||
|
||||
.table {
|
||||
border: 1px solid #0054ff;
|
||||
|
||||
.head,
|
||||
.body {
|
||||
.tr,
|
||||
.th {
|
||||
display: flex;
|
||||
color: white;
|
||||
|
||||
.title,
|
||||
.count {
|
||||
width: 100px;
|
||||
padding: 8px 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
border-right: 1px solid #0054ff;
|
||||
}
|
||||
}
|
||||
|
||||
.th {
|
||||
border-bottom: 1px solid #0054ff;
|
||||
}
|
||||
|
||||
.tr {
|
||||
color: rgb(161, 192, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@keyframes page-scroll {
|
||||
// 0% {
|
||||
// transform: translateY(0);
|
||||
// }
|
||||
// 100% {
|
||||
// transform: translateY(-100%);
|
||||
// }
|
||||
//}
|
||||
</style>
|
Reference in New Issue
Block a user