feat: v0.5 画布尺寸编辑+自动报价+扇样式保真修复

- 参数化尺寸编辑: W/H输入框 + 拖拽外框右缘/下缘/右下角手柄
- 全部几何按比例自动缩放(型材料宽不变, 最小300mm)
- 拖拽过程静默更新, 松手提交或回滚撤销点
- 自动报价: 画布变化600ms防抖调用offer-price, 实时显示总价
- 修复样板显示为默认矩形的问题: 扇样式推断引擎三级解析
  (有效bd → polyId区域映射双扇对半 → 帧bbox按扇数竖向均分)
- 还原真实分格与开向(od角度/inward空心/门标注)
- 无图纸订单显示引导提示
This commit is contained in:
HHY
2026-09-20 10:39:54 -07:00
parent dffca83508
commit 6ec30c198f
6 changed files with 307 additions and 54 deletions
+24 -4
View File
@@ -24,18 +24,19 @@
<div v-if="current" class="preview">
<div class="canvas-box">
<WindoorCanvas ref="editRef" :drawing-data="current.drawing_data"
:editable="editing" @change="dirty = true" />
:editable="editing" @change="onCanvasChange" />
</div>
<div class="meta">
<template v-if="!editing">
<span>尺寸限制: {{ current.low_width }}-{{ current.high_width }} × {{ current.low_height }}-{{ current.high_height }} mm</span>
<span>引用次数: {{ current.times }}</span>
<el-button type="success" size="small" @click="quoteRef.open({ template_id: current.id })">算料报价</el-button>
<el-button size="small" @click="editing = true">编辑分格</el-button>
<el-button size="small" @click="editing = true">编辑分格/尺寸</el-button>
<el-button type="primary" size="small" @click="useInOrder">用此样板下单</el-button>
</template>
<template v-else>
<span class="tip">左侧工具:+竖梃/+横梃 → 点击玻璃区域完成分割,支持撤销</span>
<span class="tip">改 W/H 或拖右下角手柄缩放;+竖梃/+横梃点击分割</span>
<span class="live-price" v-if="liveTotal !== null">当前报价 ¥{{ liveTotal }}</span>
<el-button size="small" @click="cancelEdit">取消</el-button>
<el-button type="primary" size="small" :disabled="!dirty" @click="saveAsTemplate">另存为我的样板</el-button>
</template>
@@ -69,6 +70,22 @@ const current = ref<any>(null)
const editing = ref(false)
const dirty = ref(false)
const editRef = ref()
const liveTotal = ref<number | null>(null)
let quoteTimer: number | undefined
// 画布变化(改尺寸/加分格)→ 防抖自动重算报价
function onCanvasChange() {
dirty.value = true
clearTimeout(quoteTimer)
quoteTimer = window.setTimeout(async () => {
const drawing = editRef.value?.exportDrawing()
if (!drawing) return
try {
const r = await api<any>('post', '/api/offer-price', { drawing_data: drawing })
liveTotal.value = r.total
} catch { /* 报价失败不打断编辑 */ }
}, 600)
}
async function load() {
loading.value = true
@@ -85,6 +102,7 @@ async function preview(t: any) {
current.value = await api<any>('get', `/api/windoor/templates/${t.id}`)
editing.value = false
dirty.value = false
liveTotal.value = null
previewVisible.value = true
}
@@ -130,5 +148,7 @@ onMounted(async () => {
.pic { width: 100%; height: 110px; }
.cap { font-size: 12px; color: #606266; margin-top: 6px; text-align: center; }
.preview .canvas-box { height: 60vh; border: 1px solid #e4e7ed; border-radius: 6px; overflow: hidden; }
.preview .meta { display: flex; gap: 20px; margin-top: 10px; color: #606266; font-size: 13px; align-items: center; }
.preview .meta { display: flex; gap: 20px; margin-top: 10px; color: #606266; font-size: 13px; align-items: center; flex-wrap: wrap; }
.tip { color: #909399; }
.live-price { color: #e6483d; font-size: 16px; font-weight: 700; }
</style>