feat: 工程大样图支持上传手绘量尺登记表(逐窗转换保留分格/开启扇/手写尺寸, 纯草图模式无样板, 横版1536x1024)
This commit is contained in:
@@ -87,20 +87,26 @@
|
||||
<!-- ============ 工程大样图(2D 图纸 → 黑白 CAD 大样图, 整合自 autocad 项目草图标准化能力) ============ -->
|
||||
<template v-else-if="tab === 'blueprint'">
|
||||
<div class="controls">
|
||||
<el-select v-model="templateId" filterable placeholder="选择样板" style="width:220px" @change="loadTemplate">
|
||||
<el-option v-for="t in templates" :key="t.id" :label="t.name || `样板#${t.id}`" :value="t.id" />
|
||||
</el-select>
|
||||
<span class="tip">以 2D 图纸为底图,AI 重绘为黑白 CAD 工程大样图(含尺寸线/标题栏,可作报价与合同附件)</span>
|
||||
<el-upload :show-file-list="false" accept="image/*" :http-request="onSketchUpload">
|
||||
<el-button type="primary" plain>{{ sketchPhoto ? '更换手绘草图' : '上传手绘草图' }}</el-button>
|
||||
</el-upload>
|
||||
<el-button v-if="sketchPhoto" size="small" text type="danger" @click="sketchPhoto = ''">移除草图</el-button>
|
||||
<span class="tip">{{ sketchPhoto ? '已上传手绘草图,AI 将逐窗转为规范大样图' : '上传现场手绘量尺登记表照片;也可不传,用所选样板的 2D 图纸生成' }}</span>
|
||||
<div style="flex:1" />
|
||||
<el-button type="primary" :disabled="!drawing || generating" :loading="generating" @click="generateBlueprint">
|
||||
<el-button type="primary" :disabled="(!sketchPhoto && !drawing) || generating" :loading="generating"
|
||||
@click="generateBlueprint">
|
||||
{{ generating ? 'AI 绘制中…' : '✦ AI 生成大样图' }}</el-button>
|
||||
</div>
|
||||
<el-progress v-if="generating" :percentage="progress" :stroke-width="10" style="margin: 10px 0"
|
||||
:format="() => stageText" />
|
||||
<div v-if="drawing" class="bp-stage">
|
||||
<!-- 底图: 优先上传的手绘草图, 否则 2D 图纸 -->
|
||||
<div v-if="sketchPhoto" class="bp-stage bp-photo">
|
||||
<img :src="sketchPhoto" class="sketch-img" />
|
||||
</div>
|
||||
<div v-else-if="drawing" class="bp-stage">
|
||||
<WindoorCanvas ref="bpCanvasRef" :drawing-data="drawing" :editable="false" />
|
||||
</div>
|
||||
<el-empty v-else description="请先选择样板" />
|
||||
<el-empty v-else description="上传手绘草图照片,或先选择样板" />
|
||||
</template>
|
||||
|
||||
<div v-if="variants.length" class="gallery-foot">
|
||||
@@ -252,14 +258,14 @@ const aiImage = ref('')
|
||||
const aiPrompt = ref('')
|
||||
const aiFeatures = ref('')
|
||||
|
||||
async function generateCloud(mode: 'quick' | 'photo' | 'blueprint', sketch_b64?: string) {
|
||||
if (!drawing.value) return
|
||||
async function generateCloud(mode: 'quick' | 'photo' | 'blueprint', sketch_b64?: string, withoutTemplate = false) {
|
||||
if (!drawing.value && !(mode === 'blueprint' && sketch_b64)) return
|
||||
if (mode === 'photo' && (!photo.value || !rect.value)) return
|
||||
generating.value = true
|
||||
aiImage.value = ''
|
||||
variants.value = []
|
||||
progress.value = 8
|
||||
stageText.value = mode === 'photo' ? '上传照片与窗型特征' : mode === 'blueprint' ? '上传 2D 图纸与分格特征' : '解析窗型结构'
|
||||
stageText.value = mode === 'photo' ? '上传照片与窗型特征' : mode === 'blueprint' ? '上传手绘草图' : '解析窗型结构'
|
||||
const timer = window.setInterval(() => {
|
||||
progress.value = Math.min(92, progress.value + 2)
|
||||
if (progress.value > 40) stageText.value = 'AI 绘制中(约 30~90 秒, 请勿关闭页面)'
|
||||
@@ -275,7 +281,8 @@ async function generateCloud(mode: 'quick' | 'photo' | 'blueprint', sketch_b64?:
|
||||
}
|
||||
}
|
||||
const d = await api<any>('post', '/api/ai-render/generate', {
|
||||
template_id: templateId.value, mode,
|
||||
// 上传手绘草图时脱离样板上下文(登记表窗型与样板无关, 避免特征误导)
|
||||
template_id: withoutTemplate ? undefined : templateId.value, mode,
|
||||
style: form.ai_style, time: form.ai_time,
|
||||
scene: mode === 'photo' ? scene.value : form.scene, photo_b64, rect: rectPayload, sketch_b64,
|
||||
}, { timeout: 200000 })
|
||||
@@ -291,8 +298,28 @@ async function generateCloud(mode: 'quick' | 'photo' | 'blueprint', sketch_b64?:
|
||||
}
|
||||
}
|
||||
|
||||
/** 工程大样图: 以 2D 图纸画布截图为底图, AI 重绘为黑白 CAD 大样图 */
|
||||
/** 工程大样图: 底图优先上传的手绘草图照片, 否则 2D 图纸画布截图; AI 重绘为黑白 CAD 大样图 */
|
||||
const sketchPhoto = ref('')
|
||||
|
||||
function onSketchUpload(e: { file: unknown }) {
|
||||
// el-upload http-request: options.file 即原始 File(部分版本包 raw)
|
||||
const opt = e.file as { raw?: File } | File
|
||||
const file = (opt as { raw?: File }).raw ?? (opt as File)
|
||||
if (!(file instanceof File)) return
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
sketchPhoto.value = String(reader.result)
|
||||
ElMessage.success('草图已上传')
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
async function generateBlueprint() {
|
||||
// 上传的手绘草图优先(脱离样板上下文); 无上传时用 2D 图纸画布截图
|
||||
if (sketchPhoto.value) {
|
||||
await generateCloud('blueprint', sketchPhoto.value.split(',')[1], true)
|
||||
return
|
||||
}
|
||||
await nextTick()
|
||||
const dataUrl = bpCanvasRef.value?.exportImage?.()
|
||||
if (!dataUrl) { ElMessage.warning('图纸截图失败,请稍后重试'); return }
|
||||
@@ -396,6 +423,8 @@ onMounted(async () => {
|
||||
background: rgba(0,0,0,0.5); padding: 4px 10px; border-radius: 10px; pointer-events: none; }
|
||||
.preview { height: 58vh; }
|
||||
.bp-stage { height: 58vh; border: 1px solid #e4e7ed; border-radius: 6px; overflow: hidden; }
|
||||
.bp-photo { display: flex; align-items: center; justify-content: center; background: #f5f6f8; }
|
||||
.sketch-img { max-width: 100%; max-height: 100%; object-fit: contain; }
|
||||
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; margin-top: 14px; }
|
||||
.gallery-foot { margin-top: 8px; }
|
||||
.ai-result { margin-top: 14px; text-align: center; }
|
||||
|
||||
Reference in New Issue
Block a user