Files
electron-4/src/renderer/src/views/components/propertyBox/imagePop.vue
2025-09-25 16:22:36 +08:00

222 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Dialog
ref="baseDialog"
title="模型预览器"
class="imagePop"
left="180px"
top="100px"
:closeCallback="closeCallBack"
>
<template #content>
<div class="content">
<span class="custom-divider"></span>
<div class="imageCon" ref="threeCanvas">
<!-- <img class="image" :src="rowData.thumbnail" alt="" /> -->
</div>
<div class="inputCon">
<span class="label">模型名称</span>
<input class="input" maxlength="40" type="text" v-model="newData.name" />
</div>
</div>
</template>
<template #footer>
<div style="position: absolute; left: -400px; display: flex">
<button @click="setImage">设置预览图</button>
</div>
<button @click="save">保存</button>
<button @click="close">关闭</button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { inject } from 'vue'
import Dialog from '@/components/dialog/baseDialog.vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { debounce } from '@/utils'
import { ModelApi } from '@/api/model/index'
import { ElMessage } from 'element-plus'
const baseDialog: any = ref(null)
const eventBus: any = inject('bus')
const viewPointHeight: any = ref(1.8)
var rowData: any = reactive([])
var imageData: any = ref(null)
var newData: any = reactive({
name: ''
})
eventBus.on('imagePopDialog', (data) => {
rowData = data
rowData.name = data.modelName
baseDialog.value?.open()
setTimeout(() => {
initThreeJS()
loadModel()
animate()
})
})
//----------------模型预览---------------
const threeCanvas = ref(null)
let scene, camera, renderer, model, controls
const initThreeJS = () => {
// 创建场景
scene = new THREE.Scene()
scene.background = new THREE.Color(0xbfd1e5)
// scene.fog = new THREE.Fog(0xbfd1e5, 200, 1000)
// 创建相机
camera = new THREE.PerspectiveCamera(
75,
threeCanvas.value.clientWidth / threeCanvas.value.clientHeight,
0.1,
1000
)
camera.position.set(0, 1, 5)
camera.lookAt(0, 0, 0)
// 创建渲染器并添加到DOM中
renderer = new THREE.WebGLRenderer({
preserveDrawingBuffer: true,
antialias: true
})
renderer.setSize(threeCanvas.value.clientWidth, threeCanvas.value.clientHeight)
renderer.setPixelRatio(window.devicePixelRatio)
threeCanvas.value.appendChild(renderer.domElement)
// 控制器
controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 启用阻尼,增加平滑度
controls.dampingFactor = 0.05 // 阻尼系数默认为0.25
controls.screenSpacePanning = false // 平移时是否在屏幕空间中移动默认false即在世界空间中移动
controls.minDistance = 1 // 最小距离,默认无限制
controls.maxDistance = 500 // 最大距离,默认无限制
controls.enableZoom = true // 是否可以缩放默认true
controls.zoomSpeed = 1.0 // 缩放速度默认1.0
controls.rotateSpeed = 1.0 // 旋转速度默认1.0
controls.addEventListener('change', render) // 监听控制器变化进行重渲染
// 添加环境光和点光源
const ambientLight = new THREE.AmbientLight(0x404040, 1) // 环境光
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
scene.add(directionalLight)
const light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1)
scene.add(light)
const pointLight = new THREE.PointLight(0xffffff, 0.8) // 点光源
pointLight.position.set(10, 10, 10)
pointLight.lookAt(0, 0, 0)
scene.add(pointLight)
}
const loadModel = () => {
const loader = new GLTFLoader()
loader.load(
'http://127.0.0.1:8848' + rowData.data, // 模型路径
function (gltf) {
// onLoad回调函数
model = gltf.scene
model.position.set(0, 0, 0)
scene.add(model) // 将模型添加到场景中
},
undefined, // onProgress回调函数可选参数用于处理加载进度等这里不使用所以设置为undefined或提供具体实现函数。
function (error) {
// onError回调函数处理加载错误等。
console.error('An error happened while loading the model', error)
}
)
}
const render = () => {
renderer.render(scene, camera) // 重渲染当前场景和相机状态,通常用于控制器监听事件中调用
}
const animate = () => {
requestAnimationFrame(animate) // 循环调用animate函数实现动画效果。
controls.update() // 更新控制器状态
renderer.render(scene, camera) // 渲染场景和相机。
}
const clangeViewPointHeight = () => {}
const viewPointHeightInput = () => {
let dom: any = document.getElementById('viewPointHeight')
if (viewPointHeight.value < dom.min * 1) {
viewPointHeight.value = dom.min * 1
} else if (viewPointHeight.value > dom.max * 1) {
viewPointHeight.value = dom.max * 1
}
}
const closeCallBack = (e) => {
renderer.domElement.remove() // 从DOM中移除渲染器。
imageData.value = null
rowData = []
newData.name = ''
//打开系统设置弹框
eventBus.emit('settingPop', true)
}
var posterLoading: any = ref(false)
const setImage = (e) => {
renderer.render(scene, camera) // 确保场景已渲染
const canvas = renderer.domElement
canvas.toBlob((blob) => {
imageData.value = blob
ElMessage.warning('点击保存是会应用当前图片')
}, 'image/png')
}
const close = (e) => {
baseDialog.value?.close()
}
const save = (e) => {
const formData = new FormData()
formData.append('modelId', rowData.id)
newData.name && formData.append('modelName', newData.name)
imageData.value && formData.append('file', imageData.value)
ModelApi.updatePoster(formData).then((res) => {
if (res.code == 0 || res.code == 200) {
ElMessage.success('设置成功')
}
})
baseDialog.value?.close()
}
</script>
<style scoped lang="scss">
.imageCon {
width: 400px;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
float: left;
}
.image {
width: 100%;
height: 100%;
object-fit: contain;
}
.inputCon {
width: 180px;
float: left;
margin-left: 10px;
margin-top: 10px;
}
.inputCon span {
display: block;
width: 60px;
height: 32px;
line-height: 32px;
float: left;
}
.inputCon .input {
float: left;
width: 120px;
}
.content {
width: 600px;
height: 400px;
}
</style>