This commit is contained in:
jingna 2025-07-02 14:01:02 +08:00
commit 296a1e9585
27 changed files with 11278 additions and 1054 deletions

View File

@ -4,13 +4,13 @@ export default {
'/api/f': { '/api/f': {
target: 'http://192.168.1.58:8100', target: 'http://192.168.1.58:8100',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/api\/f/, '') rewrite: path => path.replace(/^\/api\/f/, 'de2api')
}, },
// 使用 proxy 实例 // 使用 proxy 实例
'/api': { '/api': {
target: 'http://192.168.1.58:8100', target: 'http://192.168.1.58:8100',
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '') rewrite: path => path.replace(/^\/api/, 'de2api')
} }
}, },
port: 8080 port: 8080

View File

@ -16,6 +16,7 @@
"dependencies": { "dependencies": {
"@antv/g2plot": "^2.4.29", "@antv/g2plot": "^2.4.29",
"@antv/l7": "^2.22.0", "@antv/l7": "^2.22.0",
"@antv/l7-three": "^2.22.5",
"@antv/l7plot": "^0.5.5", "@antv/l7plot": "^0.5.5",
"@antv/s2": "^1.49.0", "@antv/s2": "^1.49.0",
"@codemirror/lang-sql": "^6.4.0", "@codemirror/lang-sql": "^6.4.0",
@ -26,6 +27,7 @@
"@npkg/tinymce-plugins": "^0.0.7", "@npkg/tinymce-plugins": "^0.0.7",
"@tinymce/tinymce-vue": "^5.1.0", "@tinymce/tinymce-vue": "^5.1.0",
"@turf/centroid": "^7.0.0", "@turf/centroid": "^7.0.0",
"@types/three": "^0.160.0",
"@videojs-player/vue": "^1.0.0", "@videojs-player/vue": "^1.0.0",
"@vue/compiler-sfc": "^3.5.15", "@vue/compiler-sfc": "^3.5.15",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^9.13.0",
@ -59,6 +61,7 @@
"qs": "^6.11.0", "qs": "^6.11.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"snowflake-id": "^1.1.0", "snowflake-id": "^1.1.0",
"three": "^0.160.0",
"tinymce": "^5.8.2", "tinymce": "^5.8.2",
"vant": "^4.8.3", "vant": "^4.8.3",
"video.js": "^7.21.6", "video.js": "^7.21.6",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,14 @@
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# 创建的文件:23.12.2020 17:11:48
newmtl 02___Default
Ns 10.0000
Ni 1.5000
d 0.3000
Tr 0.7000
Tf 0.3000 0.3000 0.3000
illum 2
Ka 0.5961 0.4196 0.0745
Kd 0.5961 0.4196 0.0745
Ks 0.5400 0.5400 0.5400
Ke 0.0000 0.0000 0.0000

View File

@ -0,0 +1,86 @@
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# ´´½¨µÄÎļþ:23.12.2020 17:11:48
mtllib XiaoSanJiao.mtl
#
# object Cone001
#
v 0.1414 0.4000 -0.1411
v 0.1414 0.4000 0.1418
v 0.0943 0.2667 0.0946
v 0.0943 0.2667 -0.0939
v -0.1414 0.4000 0.1418
v -0.0943 0.2667 0.0946
v -0.1414 0.4000 -0.1411
v -0.0943 0.2667 -0.0939
v 0.0471 0.1333 0.0475
v 0.0471 0.1333 -0.0468
v -0.0471 0.1333 0.0475
v -0.0471 0.1333 -0.0468
v 0.0000 0.0000 0.0004
# 13 vertices
vn 0.9428 -0.3333 -0.0000
vn 0.9428 -0.3333 0.0000
vn -0.0000 -0.3333 0.9428
vn -0.9428 -0.3333 -0.0000
vn -0.0000 -0.3333 -0.9428
vn 0.7071 0.0000 -0.7071
vn 0.0000 1.0000 0.0000
# 7 vertex normals
vt 0.7500 0.0000 1.0000
vt 1.0000 0.0000 1.0000
vt 1.0000 0.3333 0.6667
vt 0.7500 0.3333 0.6667
vt 0.0000 0.0000 1.0000
vt 0.2500 0.0000 1.0000
vt 0.2500 0.3333 0.6667
vt 0.0000 0.3333 0.6667
vt 0.5000 0.0000 1.0000
vt 0.5000 0.3333 0.6667
vt 1.0000 0.6667 0.3333
vt 0.7500 0.6667 0.3333
vt 0.2500 0.6667 0.3333
vt 0.0000 0.6667 0.3333
vt 0.5000 0.6667 0.3333
vt 0.5000 1.0000 0.0000
vt 0.5000 0.0000 -0.2500
vt 0.0000 0.5000 -0.2500
vt 0.5000 1.0000 -0.2500
vt 1.0000 0.5000 -0.2500
# 20 texture coords
g Cone001
usemtl 02___Default
s 1
f 1/1/1 2/2/2 3/3/2 4/4/1
s 2
f 2/5/3 5/6/3 6/7/3 3/8/3
s 1
f 5/6/4 7/9/4 8/10/4 6/7/4
s 2
f 7/9/5 1/1/5 4/4/5 8/10/5
s 1
f 4/4/1 3/3/2 9/11/1 10/12/1
s 2
f 3/8/3 6/7/3 11/13/3 9/14/3
s 1
f 6/7/4 8/10/4 12/15/4 11/13/4
s 2
f 8/10/5 4/4/5 10/12/5 12/15/5
s 1
f 10/12/1 9/11/1 13/16/1 13/16/6
s 2
f 9/14/3 11/13/3 13/16/3 13/16/6
s 1
f 11/13/4 12/15/4 13/16/4 13/16/6
s 2
f 12/15/5 10/12/5 13/16/5 13/16/6
s 4
f 5/17/7 2/18/7 1/19/7 7/20/7
f 13/16/6 13/16/6 13/16/6 13/16/6
# 14 polygons

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -34,6 +34,9 @@ import { GaodeMap } from '@antv/l7-maps';
import * as THREE from 'three'; import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js' // import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js' //
import { ThreeLayer, ThreeRender } from '@antv/l7-three';
const { t } = useI18n() const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, embeddedCallBack, inMobile } = const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, embeddedCallBack, inMobile } =
@ -122,11 +125,8 @@ const state = reactive({
let chartData = shallowRef<Partial<Chart['data']>>({ let chartData = shallowRef<Partial<Chart['data']>>({
fields: [] fields: []
}) })
// Three.js
let threeScene: THREE.Scene | null = null;
let threeRenderer: THREE.WebGLRenderer | null = null;
let threeCamera: THREE.PerspectiveCamera | null = null;
let model: THREE.Object3D | null = null;
const containerId = 'container-' + showPosition.value + '-' + view.value.id + '-' + suffixId.value const containerId = 'container-' + showPosition.value + '-' + view.value.id + '-' + suffixId.value
const viewTrack = ref(null) const viewTrack = ref(null)
@ -200,7 +200,7 @@ const calcData = async (view, callback) => {
isError.value = false isError.value = false
const v = JSON.parse(JSON.stringify(view)) const v = JSON.parse(JSON.stringify(view))
getData(v) getData(v)
.then(async res => { .then(async (res: any) => {
if (res.code && res.code !== 0) { if (res.code && res.code !== 0) {
isError.value = true isError.value = true
errMsg.value = res.msg errMsg.value = res.msg
@ -279,7 +279,7 @@ const renderChart = async (view, callback?) => {
} }
} }
let myChart = null let myChart = null
let g2Timer: number let g2Timer: any
const renderG2Plot = async (chart, chartView: G2PlotChartView<any, any>) => { const renderG2Plot = async (chart, chartView: G2PlotChartView<any, any>) => {
g2Timer && clearTimeout(g2Timer) g2Timer && clearTimeout(g2Timer)
g2Timer = setTimeout(async () => { g2Timer = setTimeout(async () => {
@ -308,7 +308,7 @@ const country = ref('')
const appStore = useAppStoreWithOut() const appStore = useAppStoreWithOut()
const chartContainer = ref<HTMLElement>(null) const chartContainer = ref<HTMLElement>(null)
let scope let scope
let mapTimer: number let mapTimer: any
const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any>, callback) => { const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any>, callback) => {
const map = parseJson(chart.customAttr).map const map = parseJson(chart.customAttr).map
let areaId = map.id let areaId = map.id
@ -387,6 +387,7 @@ class SatelliteControl extends Control {
let mapL7Timer: any let mapL7Timer: any
let scaleControl: Scale | null = null // let scaleControl: Scale | null = null //
let fullscreenControl let fullscreenControl
let content = null
let satelliteControlInstance = null; // let satelliteControlInstance = null; //
const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => { const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => {
mapL7Timer && clearTimeout(mapL7Timer); mapL7Timer && clearTimeout(mapL7Timer);
@ -424,12 +425,190 @@ const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callb
myChart.getScene()?.addControl(satelliteControlInstance); myChart.getScene()?.addControl(satelliteControlInstance);
} }
// ====== ====== // ====== ======
threel7()
myChart?.render(); myChart?.render();
callback?.(); callback?.();
emit('resetLoading'); emit('resetLoading');
}, 500); }, 500);
}; };
//
let threeJSLayerRef:any = null
const threel7 = () => {
// ====== ======
const scene = myChart.getScene(); //
scene.registerRenderService(ThreeRender);
threeJSLayerRef = new ThreeLayer({
enableMultiPassRenderer: false,
onAddMeshes: (threeScene: any, layer: any) => {
//
const camera = new THREE.PerspectiveCamera(
75,
scene.width / scene.height,
0.1,
1000000000
);
camera.position.set(0, 0, 10000000);
threeScene.add(camera);
layer.camera = camera; //
threeScene.add(new THREE.AmbientLight(0xffffff));
const sunlight: any = new THREE.DirectionalLight(0xffffff, 0.25);
sunlight.position.set(0, 80000000, 100000000);
sunlight.matrixWorldNeedsUpdate = true;
threeScene.add(sunlight);
// 使 Three.js glTFLoader
const manager: any = new THREE.LoadingManager()
manager.setURLModifier((url: any, path: any) => {
return (path || '') + url
})
const loader = new GLTFLoader(manager);
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('./static/js/');
dracoLoader.setDecoderConfig({ type: 'js' });
loader.setDRACOLoader(dracoLoader);
loader.load(
`./static/3DModel/scene.glb`,
(gltf) => {
// debugger
const gltfScene: any = gltf.scene
// setDouble(gltfScene);
content = gltfScene
layer.adjustMeshToMap(gltfScene);
// gltfScene.scale.set(1000, 1000, 1000)
layer.setMeshScale(gltfScene, 10, 10, 10);
layer.setObjectLngLat(gltfScene, [116.397428, 39.91923], 0);
//
threeScene.add(new THREE.AmbientLight(0xffffff, 0.3));
// UV Mapping
gltfScene.traverse((node) => {
if (node.isMesh) {
const material = node.material;
if (material.map) {
const uv = node.geometry.attributes.uv;
for (let i = 0; i < uv.count; i++) {
uv.setX(i, /* 新的 U 值 */);
uv.setY(i, /* 新的 V 值 */);
}
uv.needsUpdate = true;
}
}
});
//
gltfScene.traverse((node) => {
if (node.isMesh) {
const material = node.material;
if (material.map) {
material.map.repeat.set(1, 1);
material.map.offset.set(0, 0);
material.map.wrapS = THREE.ClampToEdgeWrapping;
material.map.wrapT = THREE.ClampToEdgeWrapping;
}
}
});
//
const sunlight = new THREE.DirectionalLight(0xffffff, 0.4);
sunlight.position.set(50, 100, 50);
sunlight.target.position.set(0, 0, 0);
sunlight.matrixWorldNeedsUpdate = true;
threeScene.add(sunlight);
//
const pointLight = new THREE.PointLight(0xffffff, 0.6, 1000);
pointLight.position.set(10, 15, -5);
threeScene.add(pointLight);
//
const spotLight = new THREE.SpotLight(0xffffff, 0.8);
spotLight.position.set(10, 25, 15);
spotLight.angle = Math.PI / 4;
threeScene.add(spotLight);
//
scene.on('click', handleModelClick);
//
threeScene.add(gltfScene);
// //
layer.render();
},
);
},
}).animate(true);
scene.addLayer(threeJSLayerRef);
// ====== ======
}
//
const handleModelClick = (e: any) => {
// debugger
//
if (!threeJSLayerRef || !threeJSLayerRef.camera) {
console.warn('Camera not initialized');
return;
}
// 1.
const point = e.containerPoint || { x: e.originEvent.x, y: e.originEvent.y }; //
const scene = myChart.getScene();
const container = scene.getContainer();
const width = container.clientWidth;
const height = container.clientHeight;
// 2.
const mouse = new THREE.Vector2();
mouse.x = (point.x / width) * 2 - 1;
mouse.y = -(point.y / height) * 2 + 1;
// 3. 线
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, threeJSLayerRef.camera);
// 4.
const allObjects = [];
content.traverse(child => {
if (child.isMesh && child.visible) {
allObjects.push(child);
}
});
const intersects = raycaster.intersectObjects(allObjects);
// 5.
if (intersects.length > 0) {
const closest = intersects[0];
console.log('模型被点击:', closest.object);
// 6.
highlightSelectedObject(closest.object);
} else {
//
clearSelection();
}
};
//
const highlightSelectedObject = (object) => {
//
if (!object.userData.originalMaterial) {
object.userData.originalMaterial = object.material;
}
//
object.material = new THREE.MeshBasicMaterial({
color: 0xff0000,
transparent: true,
opacity: 0.7
});
};
//
const clearSelection = () => {
content.traverse(child => {
if (child.userData.originalMaterial) {
child.material = child.userData.originalMaterial;
delete child.userData.originalMaterial;
}
});
};
const pointClickTrans = () => { const pointClickTrans = () => {
if (embeddedCallBack.value === 'yes') { if (embeddedCallBack.value === 'yes') {
trackClick('pointClick') trackClick('pointClick')
@ -736,140 +915,11 @@ defineExpose({
clearLinkage clearLinkage
}) })
// three.js
// - Three.js
const initThree = (container: HTMLElement) => {
//
threeScene = new THREE.Scene();
//
threeCamera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
);
threeCamera.position.z = 5;
//
threeRenderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
threeRenderer.setSize(container.clientWidth, container.clientHeight);
//
threeRenderer.domElement.style.position = 'absolute';
threeRenderer.domElement.style.top = '0';
threeRenderer.domElement.style.left = '0';
threeRenderer.domElement.style.zIndex = '1'; //
container.appendChild(threeRenderer.domElement);
//
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
threeScene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 10);
threeScene.add(directionalLight);
};
// -
const loadModel = (filePath: string) => {
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('./static/js/')
dracoLoader.setDecoderConfig({ type: 'js' })
loader.setDRACOLoader(dracoLoader)
//
if (!myChart?.getScene()) {
console.warn('地图场景尚未初始化,延迟加载模型');
//
setTimeout(() => loadModel(filePath), 500);
return;
}
const scene = myChart.getScene();
//
if (scene.loaded) {
//
loadModelInternal(loader, filePath);
} else {
//
const loadedHandler = () => {
scene.off('loaded', loadedHandler); //
loadModelInternal(loader, filePath);
};
scene.on('loaded', loadedHandler);
}
};
//
const loadModelInternal = (loader, filePath) => {
loader.load(
filePath,
(gltf) => {
console.log('Model loaded successfully:', gltf);
model = gltf.scene;
threeScene?.add(model);
// animate();
placeModelByLngLat(116.3974, 39.9093); //
},
(progress) => {
console.log('Loading progress:', progress);
},
(error) => {
console.error('模型加载失败:', error);
}
);
};
// -
const placeModelByLngLat = (lng: number, lat: number) => {
if (!model || !myChart?.getScene()) {
console.warn('模型或地图场景未初始化');
return;
}
const scene = myChart.getScene();
console.log('地图场景状态:', scene);
try {
const containerPoint = scene.lngLatToContainer([lng, lat]);
console.log('容器坐标:', containerPoint);
const container = document.getElementById(containerId);
if (!container) {
console.error('容器元素未找到');
return;
}
// Three.js
const x = (containerPoint.x / container.clientWidth) * 2 - 1;
const y = -(containerPoint.y / container.clientHeight) * 2 + 1;
console.log('Three.js坐标:', { x, y });
//
model.position.set(x * 5, y * 5, 0);
//
if (threeCamera) {
threeCamera.lookAt(model.position);
console.log('相机已调整朝向模型');
}
} catch (e) {
console.error('坐标转换错误:', e);
}
};
//
const animate = () => {
requestAnimationFrame(animate);
if (threeRenderer && threeScene && threeCamera) {
threeRenderer.render(threeScene, threeCamera);
}
};
let resizeObserver let resizeObserver
const TOLERANCE = 0.01 const TOLERANCE = 0.01
const RESIZE_MONITOR_CHARTS = ['map', 'bubble-map', 'flow-map', 'heat-map'] const RESIZE_MONITOR_CHARTS = ['map', 'bubble-map', 'flow-map', 'heat-map']
onMounted(() => { onMounted(() => {
console.log('Three.js实际版本:', THREE.REVISION);
const containerDom = document.getElementById(containerId) const containerDom = document.getElementById(containerId)
const { offsetWidth, offsetHeight } = containerDom const { offsetWidth, offsetHeight } = containerDom
const preSize = [offsetWidth, offsetHeight] const preSize = [offsetWidth, offsetHeight]
@ -893,14 +943,7 @@ onMounted(() => {
useEmitt({ name: 'l7-prepare-picture', callback: preparePicture }) useEmitt({ name: 'l7-prepare-picture', callback: preparePicture })
useEmitt({ name: 'l7-unprepare-picture', callback: unPreparePicture }) useEmitt({ name: 'l7-unprepare-picture', callback: unPreparePicture })
// Three.js // Three.js
// const container = document.getElementById(containerId);
// if (container) {
// initThree(container);
// //
// // loadModel(`./static/3DModel/Camera1.glb`);
// // loadModel('static/3DModel/scene.glb');
// }
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
try { try {
@ -909,9 +952,7 @@ onBeforeUnmount(() => {
} catch (e) { } catch (e) {
console.warn(e) console.warn(e)
} }
if (threeRenderer) {
threeRenderer.dispose();
}
}) })
</script> </script>
@ -953,16 +994,19 @@ onBeforeUnmount(() => {
fill: #fff !important; fill: #fff !important;
color: #fff !important; color: #fff !important;
} }
:deep(.l7-control-zoom){
position:fixed !important; :deep(.l7-control-zoom) {
position: fixed !important;
right: 0px; right: 0px;
bottom: 65px; bottom: 65px;
} }
:deep(.l7-control-button){
position:fixed !important; :deep(.l7-control-button) {
position: fixed !important;
right: 0px; right: 0px;
bottom: 35px; bottom: 35px;
} }
// :deep(.l7-button-control){ // :deep(.l7-button-control){
// position:fixed; // position:fixed;
// right: 0px; // right: 0px;

File diff suppressed because it is too large Load Diff