fix: v2.6 组合画布选中根因修复+右键菜单+3D 三场景

- 选中失效根因: Vue3 布尔 prop 缺省转换(editable 缺省→false)
  withDefaults 默认 true 修复, 左键选中/右键选中/菜单动作全链路打通
- 右键菜单: 右键单元弹操作菜单(顺/逆旋转/镜像/底边对齐/删除)
  动作 emit('ctx') 由父组件统一执行, document 级委托
- 3D 三场景: 室内(窗后墙+木地板)/室外(外墙+草地)/影棚(网格)
  场景联动背景色, 真实浏览器截图验证三场景渲染
- 菜单动作行为级验证: 旋转后 1600x1800 宽高互换✓ 删除清空✓
This commit is contained in:
HHY
2026-09-22 05:11:47 -07:00
parent b6d7db58d6
commit e3b4056855
24 changed files with 520 additions and 13 deletions
+24 -5
View File
@@ -26,7 +26,11 @@
</div>
<div class="p-row">
<span class="p-label">场景</span>
<el-checkbox v-model="showWall" size="small" @change="rebuild">墙体</el-checkbox>
<el-radio-group v-model="scene3d" size="small" @change="rebuild">
<el-radio-button value="indoor">室内</el-radio-button>
<el-radio-button value="outdoor">室外</el-radio-button>
<el-radio-button value="studio">影棚</el-radio-button>
</el-radio-group>
<el-checkbox v-model="autoRotate" size="small">自动旋转</el-checkbox>
<el-button size="small" text type="primary" @click="screenshot">截图</el-button>
</div>
@@ -69,7 +73,8 @@ const autoPlay = ref(false)
const hasOpenable = ref(false)
const lightMode = ref<'day' | 'dusk' | 'night'>('day')
const viewMode = ref<'front' | 'deg45' | 'side' | 'indoor'>('deg45')
const showWall = ref(true)
const scene3d = ref<'indoor' | 'outdoor' | 'studio'>('outdoor')
const SCENE_BG = { indoor: 0xf5efe6, outdoor: 0xbcd9f0, studio: 0xf2f4f7 } as const
const autoRotate = ref(false)
let renderer: THREE.WebGLRenderer | null = null
@@ -153,7 +158,7 @@ function applyLight() {
dirLight.position.set(p.pos[0], p.pos[1], p.pos[2])
}
if (scene) {
scene.background = new THREE.Color(p.bg)
scene.background = new THREE.Color(SCENE_BG[scene3d.value])
scene.traverse(o => {
if ((o as THREE.AmbientLight).isAmbientLight) (o as THREE.AmbientLight).intensity = p.ambient
})
@@ -219,6 +224,7 @@ function build(drawing: DrawingData) {
const sashMat = new THREE.MeshStandardMaterial({ color: 0x84898f, roughness: 0.4, metalness: 0.55 })
const wallMat = new THREE.MeshStandardMaterial({ color: 0xe8e2d6, roughness: 0.95 })
const groundMat = new THREE.MeshStandardMaterial({ color: 0xcfc9bd, roughness: 1 })
const floorMat = new THREE.MeshStandardMaterial({ color: 0xb59b74, roughness: 0.9 })
const glassMat = new THREE.MeshPhysicalMaterial({
color: 0xd8ecf5, transparent: true, opacity: 0.4, roughness: 0.06,
metalness: 0.1, side: THREE.DoubleSide, envMapIntensity: 1.2,
@@ -324,8 +330,9 @@ function build(drawing: DrawingData) {
}
}
// ---- 墙体洞口 + 地面(世界单位:窗体约 1 单位见方)----
if (showWall.value) {
// ---- 环境场景(室内/室外/影棚)----
if (scene3d.value === 'outdoor') {
// 室外: 建筑外墙围绕窗洞 + 草地
const hw = gw * S / 2 + 0.05 // 窗洞半宽
const hh = gh * S / 2 + 0.05 // 窗洞半高
const side = 0.55 // 上下墙高度/左右墙宽度
@@ -347,7 +354,19 @@ function build(drawing: DrawingData) {
ground.position.set(0, -hh - side - 0.02, DEPTH * 2)
ground.receiveShadow = true
sceneRoot.add(ground)
} else if (scene3d.value === 'indoor') {
// 室内: 窗后整墙 + 室内地板(木色)
const wall = new THREE.Mesh(new THREE.BoxGeometry(gw * S + 1.4, gh * S + 1.2, 0.02), wallMat)
wall.position.set(0, 0, -DEPTH)
wall.receiveShadow = true
wall.userData.isWall = true
sceneRoot.add(wall)
const floor = new THREE.Mesh(new THREE.BoxGeometry(gw * S + 1.4, 0.02, gh * S + 1.2), floorMat)
floor.position.set(0, -gh * S / 2 - DEPTH, 0)
floor.receiveShadow = true
sceneRoot.add(floor)
}
// 影棚(studio): 仅网格地面(下方已添加)
sceneRoot.traverse(o => {
const m = o as THREE.Mesh
if (m.isMesh) { m.castShadow = !m.userData.isWall; m.receiveShadow = true }