大屏项目遇到的问题
地图选择
离线瓦片下载
https://gitcode.com/open-source-toolkit/5ac8a
框架选择
选择用 openlayers
初始化地图
ts
mapObjRef.value = new Map({
target: mapRef.value, // 地图容器
view: new View({
center: [替换为经度, 替换为纬度],
zoom: 13, // 缩放
projection: "EPSG:4326", // 坐标系 这个坐标系代表经纬度 另外一个3857 用的不是经纬度
}),
});加载离线瓦片,转换为暗色的瓦片,瓦片默认是白色的 注意,瓦片数据一般式按照放大倍数,z/x/y 这个目录结构放置,其实都是一些图片,地图引擎根据放大倍数查询某个网格应该加载哪个目录的图
ts
const offlineMapLayer = new TileLayer({
source: new XYZ({
url: "/public/tiles" + "/{z}/{x}/{y}.png", // 设置本地离线瓦片所在路径,前面的地址是你输入http-server之后的服务地址
tileLoadFunction: (imageTile, src) => {
// 使用滤镜 将白色修改为深色
let img = new Image();
// img.crossOrigin = ''
// 设置图片不从缓存取,从缓存取可能会出现跨域,导致加载失败
img.setAttribute("crossOrigin", "anonymous");
(img.onload = () => {
let canvas = document.createElement("canvas");
let w = img.width;
let h = img.height;
canvas.width = w;
canvas.height = h;
let context = canvas.getContext("2d");
if (context) {
context.filter =
"grayscale(98%) invert(100%) sepia(20%) hue-rotate(180deg) saturate(1600%) brightness(80%) contrast(90%)";
context.drawImage(img, 0, 0, w, h, 0, 0, w, h);
}
(imageTile as any).getImage().src = canvas.toDataURL("image/png");
}),
(img.onerror = () => {
(imageTile as any).getImage().src = import("@/assets/logo.svg");
});
img.src = src;
},
}),
});标记地点,用图片标记
ts
const createIconStyle = (name?: string, isImportant?: boolean) => {
return new Style({
text: name
? new Text({
text: name,
font: "14px",
placement: "point",
offsetY: -75, // 距离图标底部的偏移量
// 文本背景样式
backgroundFill: new Fill({
color: isImportant ? "#DD9344" : "#1F58A5", // 标准站点#1F58A5 重点站点#DD9344
}),
backgroundStroke: new Stroke({
color: isImportant ? "#E0553F" : "#295EA4", // 边框颜色
width: 1,
}),
// 文本填充和边框
fill: new Fill({
color: "#FCFFFD", // 文本颜色
}),
// stroke: new Stroke({ color: 'white', width: 2 }),
// 文本周围的内边距
padding: [0, 4, 0, 4],
textAlign: "center",
textBaseline: "middle",
})
: undefined,
image: new Icon({
src: isImportant ? markActiveIcon : markIcon,
// 确保图片URL有效,使用稳定的CDN资源
scale: 0.5, // 适当缩放,避免图标过小
anchor: [0.5, 1], // 锚点设置在底部中心,与Circle对齐
crossOrigin: "anonymous", // 解决跨域问题
}),
});
};创建一个标记点
ts
const createMarker = (lon: number, lat: number, other: any) => {
const iconFeature = new Feature({
geometry: new Point([lon, lat]),
...other,
});
const iconStyle = createIconStyle(other.name, false);
iconFeature.setStyle(iconStyle);
return iconFeature;
};创建标记点图层
ts
const createMarkerLayer = () => {
const markerSource = new olSource.Vector();
// 添加标记点到数据源
stationList.value.forEach((marker) => {
const { coordinates, ...other } = marker;
const iconFeature = createMarker(
marker.coordinates[0],
marker.coordinates[1],
marker
);
markerSource.addFeature(iconFeature);
});
// 创建矢量图层并添加到地图
const markerLayer = new olLayer.Vector({
source: markerSource,
zIndex: 88,
// name: 'markerLayer', // 设置图层名称用于事件过滤
});
mapObjRef.value.addLayer(markerLayer);
console.log("创建marker");
return markerSource;
};绑定点击事件
ts
// 添加点击事件处理
const addMarkerClickEvent = () => {
const selectedStyle = createIconStyle(undefined, true);
// 点击交互
const selectClick = new Select({
condition: click,
style: selectedStyle,
// zIndex: 100 // 确保交互层在最上层
});
mapObjRef.value.addInteraction(selectClick);
// 弹出框处理
// const popup = document.getElementById('popup');
selectClick.on("select", (e) => {
const selectedFeatures = e.target.getFeatures();
const feature = selectedFeatures.item(0);
console.log(feature);
if (feature) {
emit("selectMark", feature.values_);
console.log("选中了", feature, feature.get("name"), feature.get("id"));
} else {
emit("selectMark", undefined);
console.log("没有选中");
}
});
};屏幕适配
选择用 scale 方案等比缩放, 设计稿比例为 长比高 为 4:1
ts
// 设计稿原始尺寸
const DESIGN_WIDTH = 4320;
const DESIGN_HEIGHT = 1080;
function calculateScale() {
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
// 计算宽度和高度方向的缩放比例
const scaleX = screenWidth / DESIGN_WIDTH;
const scaleY = screenHeight / DESIGN_HEIGHT;
// 使用较小的缩放比例,确保内容完全可见
// return Math.min(scaleX, scaleY);
return scaleY; //只返回一个比例就行,因为长宽都是等比缩放,都一样
}
function applyScale() {
const designContainer = document.getElementById("designContainer");
const scale = calculateScale();
if (designContainer) {
designContainer.style.transform = `scale(${scale})`;
}
}
onMounted(() => {
// // 组件卸载时移除监听
applyScale();
window.addEventListener("resize", applyScale);
});
onUnmounted(() => {
window.removeEventListener("resize", applyScale);
});动态卡片配置
使用 vue3 components 配合 import.meta.glob 批量注册组件 使用动态插槽 动态插入 v-slot=[slotName]
vue
<Bigscreen ref="bigscreenRef">
<template v-for="(compName, slotName,) in cardConfig" :key="slotName" v-slot:[slotName]>
<component :is="compName" />
</template>
</Bigscreen>使用暗色 element-plus 主题
只需要在 main 文件引入 import '@/styles/dark/css-vars.css'
批量注册组件
ts
const components = import.meta.glob("@/components/cards/**/*.vue", {
eager: true,
});
Object.entries(components).forEach(([path, module]) => {
// 获取组件名称(如:GlobalButton.vue → GlobalButton)
const componentName = path
.split("/")
.pop()
?.replace(/\.\w+$/, "");
// 注册组件
if (componentName) {
app.component(componentName, (module as any).default);
}
});使用 unocss
需要再首页 main 引入import 'virtual:uno.css'
vite.config.ts 中添加插件 import UnoCSS from 'unocss/vite'
ts
省略...
plugins: [
UnoCSS({
configFile: 'uno.config.ts',
})
]
....使用 ProPlusComponents 高级组件
引入 resolver 避免手动引入 import { PlusProComponentsResolver } from "@plus-pro-components/resolver";
引入 vue-echarts
echarts 就不过多介绍了 vue-echarts 是基于 echars 封装的 vue3 组件 https://vue-echarts.dev/ 一般式按需引入的,对于不知需要用到哪些组件,官方提供了一个工具,将 echarts 的 options 配置拷贝进,就可以返回需要用到哪些组件 https://vue-echarts.dev/#codegen