fix: v2.2 前端完整性治理(vue-tsc 全量清零)

- 组合画布 3D 扇面板解构键名错误(rect/s→rect/sash)——3D 混乱根因之一
- WindoorCanvas isDoor 标注 cx 作用域泄漏修复
- WindoorThreed rebuild 函数定义补全(墙体开关可用)
- 量尺页 openCreate 补定义(点击不再报错)
- 状态筛选 ref 类型纳入空串语义(4 页面)
- CombineCanvas 孤儿 @drop 移除+容器 dragover.prevent(拖放入库修复)
- AIRenderCanvas style 属性名保留处理+表单联合类型
- 零散隐式 any 参数补类型
- 新增 check-frontend.cjs: 未定义引用扫描+API 路径一致性比对
- 全量回归: 18 GET+4 POST+7 SPA 全部通过
This commit is contained in:
HHY
2026-09-21 04:27:16 -07:00
parent 5eb93f043e
commit cf8c0c65ce
15 changed files with 247 additions and 19 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div class="cc-wrap" ref="wrapRef">
<canvas ref="cvRef" @wheel.prevent="onWheel" @mousedown="onDown" @mousemove="onMove"
@mouseup="onUp" @mouseleave="onUp" @dblclick="reset" @dragover.prevent @drop="onDrop" />
@mouseup="onUp" @mouseleave="onUp" @dblclick="reset" />
<div class="hud">
<span>{{ hint }}</span>
<el-button size="small" text @click="reset">重置视图</el-button>
+1 -1
View File
@@ -257,6 +257,7 @@ function drawFrame(
ctx.lineWidth = 1.3
ctx.strokeRect(bx0 + 2, by0 + 2, w - 4, h - 4)
ctx.setLineDash([])
const cx = (bx0 + bx1) / 2, cy = (by0 + by1) / 2
if (g.wtf) {
// 双向开启门:对角虚线交叉符号(对齐原站图纸样式)
ctx.strokeStyle = 'rgba(70,76,82,0.7)'
@@ -267,7 +268,6 @@ function drawFrame(
ctx.stroke()
} else {
const ang = ((g.od || 90) * Math.PI) / 180
const cx = (bx0 + bx1) / 2, cy = (by0 + by1) / 2
const L = Math.min(w, h) * 0.32
const ax = cx + Math.cos(ang) * L, ay = cy - Math.sin(ang) * L
const wx = Math.cos(ang + Math.PI / 2) * L * 0.38
+20 -4
View File
@@ -34,7 +34,7 @@
<!-- 开合控制 -->
<div class="ctrl" v-if="hasOpenable">
<span>开合</span>
<el-slider v-model="openPct" style="width: 150px" :min="0" :max="100" :format-tooltip="v => v + '%'" />
<el-slider v-model="openPct" style="width: 150px" :min="0" :max="100" :format-tooltip="(v: number) => v + '%'" />
<el-checkbox v-model="autoPlay" size="small">自动演示</el-checkbox>
<el-button size="small" text @click="openPct = openPct > 0 ? 0 : 100">{{ openPct > 0 ? '全关' : '全开' }}</el-button>
</div>
@@ -110,6 +110,12 @@ function overlapArea(a: BBox, b: BBox): number {
return w > 0 && h > 0 ? w * h : 0
}
/** 型材面宽(世界单位) */
function fwOf(shape: any, S: number): number {
const ew = shape.fm?.ew ?? [60, 60, 60, 60]
return Math.max(0.018, (ew.reduce((a: number, b: number) => a + b, 0) / ew.length) * S)
}
/** 型材截面: 多腔体 + 玻璃槽(比例依据真实 DXF 截面学习: 深度46-90/面宽65-130/腔体3-6/壁厚≈深度1/30) */
function barX(parent: THREE.Group, mat: THREE.Material, len: number, fw: number,
depth: number, cx: number, cy: number, cz: number, chambers = 2) {
@@ -166,6 +172,16 @@ function applyView() {
orbit.phi = p.phi
}
/** 重建场景(墙体开关等触发) */
function rebuild() {
try {
if (props.drawingData) build(parseDrawing(props.drawingData))
} catch (e) {
console.error('[WindoorThreed] rebuild 失败:', e)
info.value = '解析失败: ' + (e as Error).message
}
}
function build(drawing: DrawingData) {
dispose()
scene = new THREE.Scene()
@@ -227,7 +243,7 @@ function build(drawing: DrawingData) {
transparent: true, opacity: 0.4, roughness: 0.06, metalness: 0.1, side: THREE.DoubleSide,
})
: glassMat
const fw = Math.max(0.018, fwOf(shape))
const fw = Math.max(0.018, fwOf(shape, S))
const regionBBs: BBox[] = regions.map(r => bboxOf(regionPoints(r)))
const used = new Set<number>()
@@ -261,7 +277,7 @@ function build(drawing: DrawingData) {
})
// 扇面板(可开合)
panels.forEach(({ rect, s }, k) => {
panels.forEach(({ rect, sash: s }, k) => {
const w = (rect.x1 - rect.x0) * S, h = (rect.y1 - rect.y0) * S
const cxr = ((rect.x0 + rect.x1) / 2 - cx) * S
const cyr = ((rect.y0 + rect.y1) / 2 - cy) * S
@@ -314,7 +330,7 @@ function build(drawing: DrawingData) {
const hh = gh * S / 2 + 0.05 // 窗洞半高
const side = 0.55 // 上下墙高度/左右墙宽度
const wt = DEPTH * 3
const wallBoxes: [number, number, number, number, number][] = [
const wallBoxes: number[][] = [
[-hw - side, hh, side * 2 + hw * 2, side], // 上
[-hw - side, -hh - side, side * 2 + hw * 2, side], // 下
[-hw - side, -hh, side, hh * 2], // 左
+6 -1
View File
@@ -59,7 +59,12 @@ const progress = ref(0)
const stageText = ref('')
const variants = ref<{ label: string; style: string; time: string; seed: number }[]>([])
const pickIdx = ref(0)
const form = reactive({ ai_style: 'modern', ai_time: 'day', scene: 'indoor', seed: 1 })
const form = reactive<{
ai_style: 'modern' | 'chinese' | 'european' | 'nordic'
ai_time: 'day' | 'dusk' | 'night'
scene: 'indoor' | 'outdoor' | 'studio'
seed: number
}>({ ai_style: 'modern', ai_time: 'day', scene: 'indoor', seed: 1 })
const variantCvs: (HTMLCanvasElement | null)[] = []
function setVariantRef(el: any, i: number) { variantCvs[i] = el as HTMLCanvasElement }
+1 -1
View File
@@ -53,7 +53,7 @@ const rows = ref<any[]>([])
const total = ref(0)
const page = ref(1)
const page_size = 15
const status = ref<number | undefined>()
const status = ref<number | '' | undefined>(undefined)
const loading = ref(false)
const createVisible = ref(false)
const form = reactive({ title: '', description: '', order_no: '' })
+3 -3
View File
@@ -48,7 +48,7 @@
</div>
</div>
</template>
<div class="canvas-box" @drop="onDrop" style="position:relative">
<div class="canvas-box" @dragover.prevent @drop="onDrop" style="position:relative">
<CombineCanvas ref="canvasRef" :shapes="shapes" @select="onSelect" @change="onCanvasChange" />
</div>
<div class="save-bar">
@@ -98,7 +98,7 @@ import { parseDrawing } from '../types/drawing'
import CombineCanvas from '../components/CombineCanvas.vue'
import WindoorThreed from '../components/WindoorThreed.vue'
interface UnitMeta { uid: number; name: string; pic: string; templateId: number }
interface UnitMeta { uid: number; name: string; pic: string; templateId: number; w: number; h: number }
const lib = ref<any[]>([])
const keyword = ref('')
@@ -145,7 +145,7 @@ function translate(shapes: any[], dx: number, dy: number) {
}
function registerUnit(detail: any, uid: number, name: string) {
units.value = [...units.value, { uid, name, pic: detail.pic_url || '', templateId: detail.id }]
units.value = [...units.value, { uid, name, pic: detail.pic_url || '', templateId: detail.id, w: 0, h: 0 }]
}
function addShapesAt(detail: any, cx: number | null, cy: number | null, name: string) {
+1 -1
View File
@@ -70,7 +70,7 @@ const total = ref(0)
const page = ref(1)
const page_size = 15
const keyword = ref('')
const status = ref<number | undefined>()
const status = ref<number | '' | undefined>(undefined)
const loading = ref(false)
const createVisible = ref(false)
const customerOptions = ref<any[]>([])
+2 -2
View File
@@ -41,7 +41,7 @@
<el-carousel-item v-for="a in announcements.slice(0, 4)" :key="a.id">
<div class="ann">
<b>{{ a.name }}</b>
<p>{{ a.desc }}</p>
<p>{{ (a as any).desc }}</p>
</div>
</el-carousel-item>
</el-carousel>
@@ -50,7 +50,7 @@
</el-col>
<el-col :xs="24" :md="10">
<el-card header="业务导航">
<el-menu class="nav-menu" @select="p => $router.push(p)">
<el-menu class="nav-menu" @select="(p: string) => $router.push(p)">
<el-menu-item index="/templates"><el-icon><Grid /></el-icon>浏览 254 套官方样板库</el-menu-item>
<el-menu-item index="/quotations"><el-icon><PriceTag /></el-icon>手动报价单</el-menu-item>
<el-menu-item index="/ai_render"><el-icon><MagicStick /></el-icon>AI 炫图生成效果图</el-menu-item>
+1 -1
View File
@@ -57,7 +57,7 @@
<el-icon size="18" @click="annOpen = !annOpen"><Bell /></el-icon>
</el-tooltip>
</el-badge>
<el-dropdown @command="cmd => cmd === 'logout' ? doLogout() : $router.push('/personal')">
<el-dropdown @command="(cmd: string) => cmd === 'logout' ? doLogout() : $router.push('/personal')">
<span class="user">
<el-avatar :size="28" style="background:#6b8cae">{{ (auth.nickname || '用')[0] }}</el-avatar>
{{ auth.nickname || '用户' }}
+8 -1
View File
@@ -82,7 +82,7 @@ const rows = ref<any[]>([])
const total = ref(0)
const page = ref(1)
const page_size = 15
const status = ref<number | undefined>()
const status = ref<number | '' | undefined>(undefined)
const loading = ref(false)
const editVisible = ref(false)
const editingId = ref(0)
@@ -99,6 +99,13 @@ async function load() {
} finally { loading.value = false }
}
function openCreate() {
Object.assign(form, { customer_name: '', phone: '', address: '', measure_date: null,
surveyor: '', status: 0, data: [], note: '' })
editingId.value = 0
editVisible.value = true
}
function openEdit(row?: any) {
editingId.value = row?.id ?? 0
Object.assign(form, row ?? { customer_name: '', phone: '', address: '', measure_date: null,
+1 -1
View File
@@ -149,7 +149,7 @@ function printOrder() { window.print() }
const statusLabels: Record<number, string> = { 0: '意向单', 1: '正式单', 2: '生产中', 3: '已完工', 4: '已取消' }
const query = reactive({ page: 1, page_size: 15, keyword: '', status: undefined as number | undefined,
const query = reactive({ page: 1, page_size: 15, keyword: '', status: undefined as number | '' | undefined,
start_date: '', end_date: '' })
const dateRange = ref<[string, string] | null>(null)