feat: P1批次2 报价/产品域(型材系列管理/副梃/成本口径毛利/报价分享)
This commit is contained in:
@@ -3,34 +3,85 @@
|
||||
<el-tabs v-model="tab">
|
||||
<!-- ============ 产品价格本 ============ -->
|
||||
<el-tab-pane label="产品价格本" name="products">
|
||||
<div class="toolbar">
|
||||
<el-input v-model="keyword" placeholder="名称/规格" clearable style="width:200px" @keyup.enter="load" />
|
||||
<el-select v-model="material" placeholder="全部类型" clearable style="width:130px" @change="load">
|
||||
<el-option value="profile" label="型材" /><el-option value="glass" label="玻璃" />
|
||||
<el-option value="hardware" label="五金" /><el-option value="addon" label="配件" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<div style="flex:1" />
|
||||
<el-button type="primary" plain @click="openEdit()">新增产品</el-button>
|
||||
</div>
|
||||
<el-table :data="rows" v-loading="loading" stripe>
|
||||
<el-table-column prop="name" label="名称" min-width="140" />
|
||||
<el-table-column label="类型" width="80">
|
||||
<template #default="{ row }">{{ label(row.material) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="spec" label="规格" width="120" />
|
||||
<el-table-column prop="unit" label="单位" width="70" />
|
||||
<el-table-column prop="price" label="售价" width="90" />
|
||||
<el-table-column prop="cost" label="成本" width="90" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" plain @click="remove(row)">删</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination style="margin-top:12px; justify-content:flex-end" background layout="total, prev, pager, next"
|
||||
:total="total" :page-size="page_size" v-model:current-page="page" @current-change="load" />
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="never" header="型材系列">
|
||||
<el-tree ref="catTreeRef" :data="catelogs" node-key="id"
|
||||
:props="{ label: 'name', children: 'children' }" default-expand-all
|
||||
highlight-current @node-click="onCatClick" />
|
||||
<div class="cat-ops">
|
||||
<el-input v-model="newCat" size="small" placeholder="新系列名" style="width:100px" />
|
||||
<el-button size="small" type="primary" plain @click="addCat(0)">加系列</el-button>
|
||||
<el-button v-if="currentCat" size="small" plain @click="addChild">加子类</el-button>
|
||||
</div>
|
||||
<div class="cat-ops" v-if="currentCat">
|
||||
<el-button size="small" text type="primary" @click="renameCat">改名</el-button>
|
||||
<el-button size="small" text type="danger" @click="removeCat">删除</el-button>
|
||||
<span class="tip">已选:{{ currentCat.name }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<div class="toolbar">
|
||||
<el-input v-model="keyword" placeholder="名称/规格" clearable style="width:180px" @keyup.enter="load" />
|
||||
<el-select v-model="material" placeholder="全部类型" clearable style="width:120px" @change="load">
|
||||
<el-option value="profile" label="型材" /><el-option value="glass" label="玻璃" />
|
||||
<el-option value="hardware" label="五金" /><el-option value="addon" label="配件" />
|
||||
<el-option value="subbar" label="副梃" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<el-button v-if="currentCat" text @click="currentCat = null; load()">清除系列筛选</el-button>
|
||||
<div style="flex:1" />
|
||||
<el-button type="primary" plain @click="openEdit()">新增产品</el-button>
|
||||
</div>
|
||||
<el-table :data="rows" v-loading="loading" stripe>
|
||||
<el-table-column prop="name" label="名称" min-width="130" />
|
||||
<el-table-column label="类型" width="75">
|
||||
<template #default="{ row }">{{ label(row.material) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="spec" label="规格" width="110" />
|
||||
<el-table-column prop="unit" label="单位" width="65" />
|
||||
<el-table-column prop="price" label="售价" width="85" />
|
||||
<el-table-column prop="cost" label="成本" width="85" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" plain @click="remove(row)">删</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination style="margin-top:12px; justify-content:flex-end" background layout="total, prev, pager, next"
|
||||
:total="total" :page-size="page_size" v-model:current-page="page" @current-change="load" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 产品编辑 -->
|
||||
<el-dialog v-model="editVisible" :title="form.id ? '编辑产品' : '新增产品'" width="480px" append-to-body>
|
||||
<el-form :model="form" label-width="70px">
|
||||
<el-form-item label="名称"><el-input v-model="form.name" /></el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="form.material" style="width:100%">
|
||||
<el-option value="profile" label="型材" /><el-option value="glass" label="玻璃" />
|
||||
<el-option value="hardware" label="五金" /><el-option value="addon" label="配件" />
|
||||
<el-option value="subbar" label="副梃" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="系列">
|
||||
<el-tree-select v-model="form.catelog_id" :data="catelogs" node-key="id"
|
||||
:props="{ label: 'name', children: 'children' }" check-strictly clearable
|
||||
style="width:100%" placeholder="所属系列(可选)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格"><el-input v-model="form.spec" /></el-form-item>
|
||||
<el-form-item label="单位"><el-input v-model="form.unit" style="width:100px" /></el-form-item>
|
||||
<el-form-item label="售价"><el-input-number v-model="form.price" :min="0" :precision="2" /></el-form-item>
|
||||
<el-form-item label="成本"><el-input-number v-model="form.cost" :min="0" :precision="2" /></el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="form.note" type="textarea" /></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="editVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ============ 价格脚本 ============ -->
|
||||
@@ -107,12 +158,55 @@ const loading = ref(false)
|
||||
const editVisible = ref(false)
|
||||
const form = reactive<any>({ id: 0, name: '', material: 'profile', spec: '', unit: '米', price: 0, cost: 0, note: '' })
|
||||
|
||||
const label = (m: string) => ({ profile: '型材', glass: '玻璃', hardware: '五金', addon: '配件' } as Record<string, string>)[m] ?? m
|
||||
const label = (m: string) => ({ profile: '型材', glass: '玻璃', hardware: '五金', addon: '配件', subbar: '副梃' } as Record<string, string>)[m] ?? m
|
||||
|
||||
// ---- 型材系列(product-catelog) ----
|
||||
const catelogs = ref<any[]>([])
|
||||
const currentCat = ref<any>(null)
|
||||
const newCat = ref('')
|
||||
const catTreeRef = ref()
|
||||
|
||||
async function loadCats() {
|
||||
catelogs.value = (await api<any[]>('get', '/api/product-catelogs')) ?? []
|
||||
}
|
||||
|
||||
function onCatClick(node: any) {
|
||||
currentCat.value = node
|
||||
load()
|
||||
}
|
||||
|
||||
async function addCat(fid: number) {
|
||||
if (!newCat.value.trim()) return ElMessage.warning('请填写系列名')
|
||||
await api('post', '/api/product-catelogs', { name: newCat.value.trim(), fid })
|
||||
newCat.value = ''
|
||||
loadCats()
|
||||
}
|
||||
|
||||
async function addChild() {
|
||||
if (!newCat.value.trim()) return ElMessage.warning('请填写子类名')
|
||||
await addCat(currentCat.value.id)
|
||||
}
|
||||
|
||||
async function renameCat() {
|
||||
const name = await ElMessageBox.prompt('修改系列名称', '改名', { inputValue: currentCat.value.name })
|
||||
await api('put', `/api/product-catelogs/${currentCat.value.id}`, { name: name.value, fid: currentCat.value.fid })
|
||||
ElMessage.success('已改名')
|
||||
loadCats()
|
||||
}
|
||||
|
||||
async function removeCat() {
|
||||
await ElMessageBox.confirm(`确定删除系列「${currentCat.value.name}」?`, '提示', { type: 'warning' })
|
||||
try {
|
||||
await api('delete', `/api/product-catelogs/${currentCat.value.id}`)
|
||||
currentCat.value = null
|
||||
loadCats(); load()
|
||||
} catch { /* 拦截器已提示(系列下有产品) */ }
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const qs = `page=${page.value}&page_size=${page_size}&keyword=${keyword.value}${material.value ? `&material=${material.value}` : ''}`
|
||||
const qs = `page=${page.value}&page_size=${page_size}&keyword=${keyword.value}${material.value ? `&material=${material.value}` : ''}${currentCat.value ? `&catelog_id=${currentCat.value.id}` : ''}`
|
||||
const d = await api<any>('get', `/api/products?${qs}`)
|
||||
rows.value = d.data
|
||||
total.value = d.total
|
||||
@@ -120,7 +214,7 @@ async function load() {
|
||||
}
|
||||
|
||||
function openEdit(row?: any) {
|
||||
Object.assign(form, row ?? { id: 0, name: '', material: 'profile', spec: '', unit: '米', price: 0, cost: 0, note: '' })
|
||||
Object.assign(form, row ?? { id: 0, name: '', material: 'profile', spec: '', unit: '米', price: 0, cost: 0, note: '', catelog_id: currentCat.value?.id ?? null })
|
||||
editVisible.value = true
|
||||
}
|
||||
|
||||
@@ -188,7 +282,7 @@ async function test(row: any) {
|
||||
ElMessage.success(`试算结果 ¥${d.result}`)
|
||||
}
|
||||
|
||||
onMounted(() => { load(); loadScripts() })
|
||||
onMounted(() => { load(); loadScripts(); loadCats() })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -196,4 +290,5 @@ onMounted(() => { load(); loadScripts() })
|
||||
.tip { font-size: 12px; color: #909399; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
|
||||
.var-tag { font-family: Consolas, monospace; }
|
||||
.formula-help { font-size: 12px; color: #909399; line-height: 1.5; margin-top: 4px; }
|
||||
.cat-ops { display: flex; gap: 6px; align-items: center; margin-top: 10px; flex-wrap: wrap; }
|
||||
</style>
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="glass_area" label="玻璃㎡" width="90" />
|
||||
<el-table-column prop="total" label="总价" width="110" />
|
||||
<el-table-column label="成本/毛利" width="150">
|
||||
<template #default="{ row }">
|
||||
<span class="cost">¥{{ row.cost_total ?? 0 }}</span>
|
||||
<span class="profit"> / ¥{{ row.profit ?? row.total }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="(['info', 'primary', 'success', 'danger'] as const)[row.status]">
|
||||
|
||||
Reference in New Issue
Block a user