feat: 样板库整合材料系列选择(商家选材闭环: 预览对话框系列选择器+offer-price系列成本估算+QuoteDialog选材参考面板)

This commit is contained in:
HHY
2026-10-06 05:28:27 -07:00
parent 64bda5e97b
commit 07eda3ff35
5 changed files with 84 additions and 6 deletions
+31 -2
View File
@@ -1,5 +1,5 @@
<template>
<el-dialog v-model="visible" title="算料报价" width="640px">
<el-dialog v-model="visible" title="算料报价" width="760px">
<div v-if="result" v-loading="loading">
<el-descriptions :column="3" border size="small">
<el-descriptions-item label="窗型尺寸">{{ result.width_mm }} × {{ result.height_mm }} mm</el-descriptions-item>
@@ -10,6 +10,32 @@
type="warning" effect="plain" style="margin-left:6px">脚本计价</el-tag>
</el-descriptions-item>
</el-descriptions>
<!-- 材料系列选材参考 -->
<el-card v-if="result.series_estimate" shadow="never" class="series-card">
<template #header>
<div class="series-head">
<span>材料系列成本参考</span>
<el-tag size="small" type="primary" effect="plain">
{{ result.series.name }} · 系列 {{ result.series.series }} · 壁厚 {{ result.series.wall_thick }}mm
</el-tag>
<span class="dim">按当前窗型 {{ result.series_estimate.width }}×{{ result.series_estimate.height }}mm 估算</span>
</div>
</template>
<el-row :gutter="10">
<el-col :span="6"><el-statistic title="系列成本(元/樘)" :value="result.series_estimate.total" :precision="2" /></el-col>
<el-col :span="6"><el-statistic title="每平成本(元/㎡)" :value="result.series_estimate.per_sqm" :precision="2" /></el-col>
<el-col :span="6"><el-statistic title="建议售价(50%毛利)" :value="result.series_estimate.margin_ladder['50%']" :precision="2" /></el-col>
<el-col :span="6"><el-statistic title="报价总额(元)" :value="result.total" :precision="2" /></el-col>
</el-row>
<el-descriptions :column="4" border size="small" style="margin-top:10px">
<el-descriptions-item label="型材">{{ result.series_estimate.profile_cost }} 元</el-descriptions-item>
<el-descriptions-item label="玻璃">{{ result.series_estimate.glass_cost }} 元</el-descriptions-item>
<el-descriptions-item label="五金">{{ result.series_estimate.hardware_cost }} 元</el-descriptions-item>
<el-descriptions-item label="人工">{{ result.series_estimate.labor_cost }} 元</el-descriptions-item>
</el-descriptions>
</el-card>
<el-table :data="result.rows" size="small" border style="margin-top:10px">
<el-table-column prop="note" label="项目" min-width="110">
<template #default="{ row }">{{ row.note || kindLabel(row.kind) }}</template>
@@ -38,7 +64,7 @@ import { api } from '../api'
const visible = ref(false)
const loading = ref(false)
const result = ref<any>(null)
let source: { drawing_data?: string; template_id?: number; order_id?: number } = {}
let source: { drawing_data?: string; template_id?: number; order_id?: number; series_id?: number } = {}
function kindLabel(k: string) {
return ({ frame: '外框', mullion: '中梃', sash: '扇框', glass: '玻璃', hardware: '五金' } as Record<string, string>)[k] ?? k
@@ -62,5 +88,8 @@ defineExpose({ open })
<style scoped>
.total { color: #e6483d; font-size: 18px; font-weight: 700; }
.series-card { margin-top: 10px; }
.series-head { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.series-head .dim { font-size: 12px; color: #909399; font-weight: 400; }
@media print { .el-dialog__footer { display: none; } }
</style>
+32 -2
View File
@@ -77,14 +77,24 @@
<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-select v-model="seriesId" placeholder="选择材料系列" clearable size="small" style="width:170px"
@change="onSeriesChange">
<el-option v-for="s in seriesList" :key="s.id" :value="s.id"
:label="`${s.name}(${s.wall_thick}mm)`" />
</el-select>
<el-button type="success" size="small" @click="doQuote">算料报价</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">点击玻璃格输入格宽高;点中梃输入位置或拖动;改 W/H 缩放整体;+竖梃/+横梃分割</span>
<span class="live-price" v-if="liveTotal !== null">当前报价 ¥{{ liveTotal }}</span>
<el-button type="success" size="small" @click="quoteRef.open({ template_id: current.id })">算料报价</el-button>
<el-select v-model="seriesId" placeholder="选择材料系列" clearable size="small" style="width:170px"
@change="onSeriesChange">
<el-option v-for="s in seriesList" :key="s.id" :value="s.id"
:label="`${s.name}(${s.wall_thick}mm)`" />
</el-select>
<el-button type="success" size="small" @click="doQuote">算料报价</el-button>
<el-button size="small" @click="cancelEdit">取消</el-button>
<el-button type="primary" size="small" :disabled="!dirty" @click="saveAsTemplate">另存为我的样板</el-button>
<el-button size="small" @click="useInOrder">用此样板下单</el-button>
@@ -122,6 +132,24 @@ const editRef = ref()
const liveTotal = ref<number | null>(null)
const tab = ref<'official' | 'mine' | 'ai'>('official')
let quoteTimer: number | undefined
// 材料系列(成本系列模板): 商家按系列选材, 随算料报价出系列成本参考
const seriesList = ref<any[]>([])
const seriesId = ref<number | undefined>()
const seriesTip = ref('')
async function loadSeries() {
if (seriesList.value.length) return
try { seriesList.value = await api<any>('get', '/api/costbook/series') } catch { /* 无系列时不阻塞 */ }
}
function doQuote() {
quoteRef.value?.open({ template_id: current.value.id, series_id: seriesId.value })
}
function onSeriesChange(id: number | undefined) {
const s = seriesList.value.find(x => x.id === id)
if (s) ElMessage.info(`已选「${s.name}」材料系列,点击算料报价查看该系列成本参考`)
}
// ---- AI 智能推荐 ----
const recLoading = ref(false)
@@ -171,6 +199,8 @@ async function preview(t: any) {
editing.value = true
dirty.value = false
liveTotal.value = null
seriesId.value = undefined
loadSeries()
previewVisible.value = true
}