基本功能已完成

This commit is contained in:
2024-07-15 02:19:48 +08:00
parent 84425462ab
commit 78bc464a76
6 changed files with 109 additions and 137 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -58,7 +58,8 @@ window.$waitFor = (target, t = 500) => new Promise(resolve => {
window.evenRowBGC = (color = "#09265B") => `transparent;background-image: linear-gradient(-45deg, ${color} 0, ${color} 10%, transparent 10%, transparent 50%,${color} 50%, ${color} 60%, transparent 60%, transparent);background-size: 10px 10px;`
Vue.prototype.$marketBoard = Vue.observable({
search: {"groupCodeList": ["20011061"], "currentDate": "20240701", "compareDate": "20240630", "hourNum": "18"}
screenId: '5b1849ac-4fc3-451a-844c-3362b47341ef',
search: {"groupCodeList": [], "currentDate": "20240701", "compareDate": "20240630", "hourNum": "18"}
})
Vue.prototype.$multipleStoreBoard = Vue.observable({
search: {"groupCodeList": [], "hourNum": "", type: "1"}

View File

@@ -8,12 +8,10 @@ export default {
name: "AppNavbar",
label: "标题栏",
data() {
return {
screens,
groupId: "5b1849ac-4fc3-451a-844c-3362b47341ef"
}
return {screens}
},
computed: {
groupId: v => v.$marketBoard.screenId,
backgroundImage() {
const item = this.screens.find(e => e.id === this.groupId)
return `url(${item.bg})`
@@ -25,11 +23,9 @@ export default {
handler(val) {
if (val) {
$glob.group = this.groupId
} else {
this.groupId = $glob.group
}
}
}
},
}
}
</script>

View File

@@ -6,7 +6,8 @@ export default {
return {
geoMap: null,
layers: [],
font: null
font: null,
geoJson: null
}
},
computed: {
@@ -17,29 +18,33 @@ export default {
const {$waitFor, THREE, $loadScript} = window
return $waitFor(THREE).then(() => Promise.all([
`http://10.0.97.209/presource/datascreen/js/three/js/controls/OrbitControls.js`,
`http://10.0.97.209/presource/datascreen/js/three/js/renderers/CSS2DRenderer.js`,
`http://10.0.97.209/presource/datascreen/js/three/js/renderers/CSS3DRenderer.js`,
`http://10.0.97.209/presource/datascreen/js/three/js/loaders/FontLoader.js`,
`http://10.0.97.209/presource/datascreen/js/three/js/geometries/TextGeometry.js`,
'/presource/datascreen/js/turf.min.js'
].map(e => $loadScript('js', e))))
},
initMap() {
const {THREE, d3, axios, TWEEN} = window
const {THREE, d3, TWEEN, turf} = window
const rootEl = this.$el
const root = this
const scale = 4
const scale = 8
class GeoMap {
constructor() {
this.cameraPosition = {x: 40, y: 0, z: 40}; // 相机位置
this.cameraPosition = {x: 40, y: 0, z: 80}; // 相机位置
this.scene = null; // 场景
this.camera = null; // 相机
this.renderer = null; // 渲染器
this.CSS3DRenderer = null; // css渲染器
this.controls = null; // 控制器
this.mapGroup = new THREE.Group(); // 组
this.mouse = new THREE.Vector2();
this.font = null;
this.tips = new THREE.Group()
this.loop = 0
this.markers = new THREE.Group();
this.center = turf.center(root.geoJson).geometry.coordinates
}
/**
@@ -48,47 +53,31 @@ export default {
init() {
this.setScene();
this.setCamera();
this.setLight()
this.setAxes();
// this.setAxes();
this.setRenderer();
this.setControl();
this.makeGround();
this.getMap('http://10.0.97.209/blade-visual/map/data?id=1456');
this.addMarkers()
this.setMapData(root.geoJson)
this.animation();
this.bindMouseEvent()
}
setLight() {
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1).normalize();
this.scene.add(directionalLight);
}
/**
* @desc 动画循环
* */
animation() {
requestAnimationFrame(this.animation.bind(this));
this.loop = (this.loop * 100 + 2) % 100 * 0.01
this.markers.children.forEach(e => {
e.scale = {
x: Math.asin(this.loop) + 1,
y: Math.asin(this.loop) + 1,
z: Math.asin(this.loop) + 1
};
})
TWEEN.update();
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
/**
* @desc 获取地图
* */
getMap(url) {
const that = this;
axios.get(url).then(function (res) {
if (res.status === 200) {
const data = res.data;
that.geoJson = data
that.setMapData(data)
}
})
this.CSS3DRenderer.render(this.scene, this.camera);
}
/**
@@ -96,7 +85,7 @@ export default {
* @params geojson
* */
setMapData(data) {
const that = this, {turf} = window;
const that = this
const getLnglat = (arr, cb) => {
arr?.map(e => {
if (e.length === 2 && typeof e[0] === 'number' && typeof e[1] === 'number') {
@@ -108,7 +97,7 @@ export default {
}
let vector3json = [],
vector3border = []
const maxBoundaryPoints = turf.union(this.geoJson)
const maxBoundaryPoints = turf.union(data)
getLnglat(maxBoundaryPoints.geometry.coordinates, p => {
const lnglat = that.lnglatToVector3(p);
const vector3 = new THREE.Vector3(lnglat[0], lnglat[1], lnglat[2]).multiplyScalar(1.2);
@@ -116,7 +105,7 @@ export default {
})
data.features.forEach(function (features, featuresIndex) {
const areaItems = features.geometry.coordinates;
features.properties.cp = that.lnglatToVector3(features.properties.centroid);
features.properties.cp = that.lnglatToVector3(turf.center(features).geometry.coordinates);
vector3json[featuresIndex] = {
data: features.properties,
mercator: []
@@ -140,9 +129,8 @@ export default {
drawMap(data, border) {
let that = this;
this.mapGroup.position.y = 0;
this.scene.add(that.mapGroup);
const extrudeSettings = {
depth: 0.2,
depth: 0.1,
steps: 1,
bevelSegments: 0,
curveSegments: 1,
@@ -161,7 +149,6 @@ export default {
ctx.fillRect(0, 0, canvas.width, canvas.height);
const blockMaterial = new THREE.MeshBasicMaterial({
map: new THREE.CanvasTexture(canvas),
// side: THREE.DoubleSide,
transparent: true, wireframe: false
});
const lineMaterial = new THREE.LineBasicMaterial({color: '#97CAE6'});
@@ -173,7 +160,7 @@ export default {
// Draw Line
let lineGeometry = new THREE.BufferGeometry().setFromPoints(areaItem);
let lineMesh = new THREE.Line(lineGeometry, lineMaterial);
lineMesh.position.z = 0.201;
lineMesh.position.z = 0.101;
areaGroup.add(lineMesh);
});
const {name, cp} = areaData.data
@@ -184,11 +171,9 @@ export default {
that.mapGroup.add(that.tips)
const shape = new THREE.Shape(border);
const areaGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const mesh = new THREE.Mesh(areaGeometry, blockMaterial);
const borderMaterial = new THREE.MeshBasicMaterial({color: "#1B4C92", transparent: true, opacity: 0.8});
const mesh = new THREE.Mesh(areaGeometry, [blockMaterial, borderMaterial]);
that.mapGroup.add(mesh)
that.mapGroup.scale.set(scale, scale, scale)
that.mapGroup.position.set(0, 0, 0)
that.scene.add(that.mapGroup);
}
transLayer(item = {}) {
@@ -197,24 +182,18 @@ export default {
latitude = Number(latitude || 0).toFixed(6);
const markerGeometry = new THREE.CircleGeometry(0.015, 32);
const markerMaterial = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending,
wireframe: false,
color: bakeStockAmt > 0 ? "#66FFFF" : "#FFD15C",
depthTest: false,
transparent: true,
opacity: 1
});
const marker = new THREE.Mesh(markerGeometry, markerMaterial);
const lnglat = this.lnglatToVector3([longitude, latitude]);
let v3 = new THREE.Vector3(lnglat[0], lnglat[1], lnglat[2]).multiplyScalar(1.2);
marker.data = item
marker.position.set(v3.x, v3.y, 0.201)
marker.renderOrder = 3
marker.position.set(v3.x, v3.y, 0.11)
return marker
}
addMarkers() {
this.markers = new THREE.Group();
root.layers.map(layer => {
const marker = this.transLayer(layer)
this.markers.add(marker)
@@ -224,7 +203,7 @@ export default {
lnglatToVector3(lnglat = []) {
if (!this.projection) {
this.projection = d3.geoMercator().center([113.665412, 34.757975]).scale(100).translate([0.3, 0]);
this.projection = d3.geoMercator().center(this.center).scale(100).translate([0, 0]);
}
const [x, y] = this.projection([lnglat[0], lnglat[1]])
const z = 0;
@@ -236,6 +215,9 @@ export default {
* */
setScene() {
this.scene = new THREE.Scene();
this.mapGroup.scale.set(scale, scale, scale)
this.mapGroup.position.set(0, 0, 0)
this.scene.add(this.mapGroup);
}
/**
@@ -260,11 +242,15 @@ export default {
this.renderer.setClearColor(0xffffff, 0);
this.renderer.setSize(rootEl.offsetWidth, rootEl.offsetHeight);
rootEl.appendChild(this.renderer.domElement);
this.CSS3DRenderer = new THREE.CSS3DRenderer();
this.CSS3DRenderer.setSize(rootEl.offsetWidth, rootEl.offsetHeight);
rootEl.appendChild(this.CSS3DRenderer.domElement);
function onWindowResize() {
this.camera.aspect = rootEl.offsetWidth / rootEl.offsetHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(rootEl.offsetWidth, rootEl.offsetHeight);
this.CSS3DRenderer.setSize(rootEl.offsetWidth, rootEl.offsetHeight);
}
rootEl.addEventListener('resize', onWindowResize.bind(this), false);
@@ -275,7 +261,7 @@ export default {
* */
setControl() {
this.controls = new THREE.OrbitControls(this.camera, rootEl);
// this.controls.enableRotate = false
this.controls.enableRotate = false
this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z);
}
@@ -287,21 +273,6 @@ export default {
this.scene.add(axes);
}
/**
* @desc 鼠标 hover 事件
* */
makeGround() {
const material = new THREE.MeshBasicMaterial({opacity: 0.5, transparent: true, color: '#07193D'});
const geometry = new THREE.PlaneGeometry(100, 100, 1, 1);
let ground = new THREE.Mesh(geometry, material);
ground.position.x = 0;
ground.position.y = 0;
ground.position.z = -1;
this.scene.add(ground);
ground.receiveShadow = true;
ground.castShadow = true;
}
setTips(text, x, y) {
const textGeometry = new THREE.TextGeometry(text, {
font: root.font,
@@ -310,8 +281,8 @@ export default {
});
textGeometry.center()
textGeometry.rotateZ(Math.PI / 2)
textGeometry.rotateY(Math.PI / 4)
textGeometry.translate(x, y, 0.25)
textGeometry.rotateY(Math.PI / 6)
textGeometry.translate(x, y, 0.15)
const textMaterial = new THREE.MeshBasicMaterial({color: 0xffffff, transparent: true, opacity: 0.8});
const textMesh = new THREE.Mesh(textGeometry, textMaterial);
this.tips.add(textMesh)
@@ -321,63 +292,58 @@ export default {
const raycaster = new THREE.Raycaster();
const onPointerMove = (event) => {
const {clientWidth: width, clientHeight: height} = rootEl;
this.mouse.x = (event.clientX / width) * 2 - 1; //标准设备横坐标
this.mouse.y = -(event.clientY / height) * 2 + 1; //标准设备纵坐标
const {offsetWidth: width, offsetHeight: height} = rootEl, {left, top} = rootEl.getBoundingClientRect();
this.mouse.x = ((event.clientX - left) / width) * 2 - 1; //标准设备横坐标
this.mouse.y = -((event.clientY - top) / height) * 2 + 1; //标准设备纵坐标
raycaster.setFromCamera(this.mouse, this.camera);
const intersects = raycaster.intersectObjects(this.markers.children)
intersects.forEach(e => {
if (e.object?.data) {
this.showHoverPanel(e.object)
}
})
}
const onClick = () => {
// const {x, y} = this.mouse
// const standardVector = new THREE.Vector3(x, y, 0.5); //标准设备坐标
// //标准设备坐标转世界坐标
// const worldVector = standardVector.unproject(that.camera);
// //射线投射方向单位向量(worldVector坐标减相机位置坐标)
// const ray = worldVector.sub(that.camera.position).normalize();
// //创建射线投射器对象
// let raycaster = new THREE.Raycaster(that.camera.position, ray);
// //返回射线选中的对象
// let intersects = raycaster.intersectObjects(that.meshList);
// if (intersects.length) {
// if (intersects[0].object.parent && intersects[0].object.parent._groupType === 'areaBlock') {
// if (that.selectObject !== intersects[0].object.parent) {
// if (that.selectObject) {
// transiform(that.selectObject.position, {
// x: that.selectObject.position.x,
// y: that.selectObject.position.y,
// z: 0
// }, 100);
// transiform(intersects[0].object.parent.position, {
// x: intersects[0].object.parent.position.x,
// y: intersects[0].object.parent.position.y,
// z: 0.8
// }, 100);
// that.selectObject = intersects[0].object.parent;
// } else {
// transiform(intersects[0].object.parent.position, {
// x: intersects[0].object.parent.position.x,
// y: intersects[0].object.parent.position.y,
// z: 0.8
// }, 100);
// that.selectObject = intersects[0].object.parent;
// }
// }
// }
// }
}
const onClick = () => {
// const worldVector = standardVector.unproject(this.camera);
// const ray = worldVector.sub(this.camera.position).normalize();
// 创建一个射线投射器
raycaster.setFromCamera(this.mouse, this.camera);
console.table(raycaster.ray)
const intersects = raycaster.intersectObjects(this.markers.children)
console.log(intersects)
intersects.forEach(e => {
if (e.visible) {
const {$glob} = window
root.$storeBoard.search.storeCode = marker.data?.storeCode
$glob.group = '9f299712-5549-413b-a93b-7c3e3b5bfadb'
if (e.object?.data) {
const marker = e.object?.data
root.$storeBoard.search.storeCode = marker.storeCode
root.$marketBoard.screenId = 'a90522ef-869b-40ea-8542-d1fc9674a1e8'
}
})
}
rootEl.addEventListener('pointermove', onPointerMove);
rootEl.addEventListener('click', onClick);
}
showHoverPanel({data: marker, position} = {}) {
const hoverPanelElement = document.createElement('div')
Object.entries({
background: 'linear-gradient( 90deg, rgba(9,63,107,0.79) 0%, rgba(13,58,99,0.03) 100%)',
borderRadius: '2px',
color: '#fff',
fontSize: '10px',
lineHeight: '14px',
position: 'absolute',
top: 0,
}).forEach(([key, value]) => {
hoverPanelElement.style[key] = value
})
const span = document.createElement('span')
span.innerHTML = [marker.storeName, `现烤库存金额:${marker.bakeStockAmt}`, `现烤销售机会:${marker.bakeStockAmt}`].join('<br/>')
hoverPanelElement.appendChild(span)
const hoverPanel = new THREE.CSS3DObject(hoverPanelElement)
hoverPanel.position.set(position)
this.scene.add(hoverPanel)
}
}
return new GeoMap()
@@ -385,20 +351,26 @@ export default {
getData() {
const {$http, $waitFor} = window
const {groupCodeList, currentDate} = this.search
return $waitFor($http).then(() => $http.post("/data-boot/la/screen/marketBoard/storeReport", {
groupCodeList, currentDate
})).then(res => {
if (res?.data) {
return this.layers = res.data || []
}
})
return $waitFor($http).then(() => Promise.all([
$http.post("/data-boot/la/screen/marketBoard/storeReport", {
groupCodeList, currentDate
}).then(res => {
if (res?.data) {
return this.layers = res.data || []
}
}),
axios.get('http://10.0.97.209/blade-visual/map/data?id=1456').then(res => {
if (res?.data) {
return this.geoJson = res.data
}
})
]))
}
},
watch: {
search: {
immediate: true, deep: true, handler() {
const {$waitFor} = window
this.getData().then(() => $waitFor(this.geoMap)).then(() => this.geoMap.addMarkers())
deep: true, handler() {
this.getData().then(() => this.geoMap.addMarkers())
}
}
},
@@ -410,8 +382,11 @@ export default {
resolve()
})
})).then(() => {
return !this.geoJson && this.getData()
}).then(() => {
this.geoMap = this.initMap();
this.geoMap.init();
this.geoMap.addMarkers()
})
}
}
@@ -421,7 +396,8 @@ export default {
</template>
<style>
.AppThreeMap {
width: 100%;
height: 100%;
width: 900px;
height: 500px;
position: relative;
}
</style>