采购件管理修改页面文字大小以及调整文字栏间距

This commit is contained in:
dxc
2026-01-27 16:43:44 +08:00
parent 3afea217b7
commit 7a78975ce7
7 changed files with 104 additions and 116 deletions

View File

@ -44,6 +44,11 @@ class BuyInboundService:
except ValueError:
in_date_val = datetime.utcnow().date()
# --- 修改部分:增加字段兼容性,确保前端传参能被正确读取 ---
in_qty = float(data.get('in_quantity') or data.get('qty_inbound') or 0)
u_price = float(data.get('unit_price') or data.get('price_unit') or 0)
# ---------------------------------------------------
# 3. 创建 StockBuy
new_stock = StockBuy(
base_id=material.id,
@ -53,12 +58,12 @@ class BuyInboundService:
batch_number=data.get('batch_number'),
status='在库',
inspection_status=data.get('inspection_status'),
in_quantity=data.get('in_quantity', 0),
stock_quantity=data.get('in_quantity', 0),
available_quantity=data.get('in_quantity', 0),
warehouse_location=data.get('warehouse_location'),
unit_price=data.get('unit_price', 0),
total_price=data.get('total_price', 0),
in_quantity=in_qty,
stock_quantity=in_qty,
available_quantity=in_qty,
warehouse_location=data.get('warehouse_location') or data.get('warehouse_loc'),
unit_price=u_price,
total_price=in_qty * u_price,
currency=data.get('currency', 'CNY'),
exchange_rate=data.get('exchange_rate', 1.0),
supplier_name=data.get('supplier_name'),
@ -85,10 +90,11 @@ class BuyInboundService:
if not stock:
raise ValueError("记录不存在")
# 1. 更新普通字段
# 1. 更新普通字段 (增加对 warehouse_loc 的兼容)
if 'serial_number' in data: stock.serial_number = data['serial_number']
if 'batch_number' in data: stock.batch_number = data['batch_number']
if 'warehouse_location' in data: stock.warehouse_location = data['warehouse_location']
if 'warehouse_loc' in data: stock.warehouse_location = data['warehouse_loc']
if 'supplier_name' in data: stock.supplier_name = data['supplier_name']
if 'status' in data: stock.status = data['status']
if 'inspection_status' in data: stock.inspection_status = data['inspection_status']
@ -101,13 +107,14 @@ class BuyInboundService:
if 'category' in data: stock.material.category = data['category']
if 'unit' in data: stock.material.unit = data['unit']
# 3. 核心逻辑:数量与价格联动
# 3. 核心逻辑:数量与价格联动 (增加对前端返回字段名 qty_inbound 和 price_unit 的识别)
qty_changed = False
price_changed = False
# (A) 数量变更 -> 更新库存和可用量
if 'in_quantity' in data:
new_qty = float(data['in_quantity'])
new_qty_input = data.get('in_quantity') or data.get('qty_inbound')
if new_qty_input is not None:
new_qty = float(new_qty_input)
old_qty = float(stock.in_quantity)
diff = new_qty - old_qty
@ -118,8 +125,9 @@ class BuyInboundService:
qty_changed = True
# (B) 单价变更
if 'unit_price' in data:
new_price = float(data['unit_price'])
new_price_input = data.get('unit_price') or data.get('price_unit')
if new_price_input is not None:
new_price = float(new_price_input)
if new_price != float(stock.unit_price):
stock.unit_price = new_price
price_changed = True

View File

@ -1,3 +1,3 @@
# .env.development
# 注意:这里必须写你电脑的局域网 IP
VITE_API_BASE_URL=http://172.25.16.1:8000/api/v1
VITE_API_BASE_URL=http://172.16.0.95:8000/api/v1

View File

@ -28,13 +28,6 @@
借库申请
</el-button>
</div>
<div style="margin-top: 20px;">
<el-button text bg size="small" disabled>
<el-icon style="margin-right: 5px"><TrendCharts /></el-icon>
数据大屏 (开发中)
</el-button>
</div>
</div>
</el-card>
</div>

View File

@ -3,7 +3,6 @@
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="title">基础物料管理</span>
<el-button type="primary">
<el-icon style="margin-right: 5px"><Plus /></el-icon>新增物料
</el-button>
@ -13,8 +12,10 @@
<div class="filter-container">
<el-input placeholder="请输入物料名称或编码" style="width: 200px; margin-right: 10px;" />
<el-select placeholder="物料类别" style="width: 150px; margin-right: 10px;">
<el-option label="电子元器件" value="elec" />
<el-option label="结构件" value="struct" />
<el-option label="采购件" value="elec" />
<el-option label="半成品" value="struct" />
<el-option label="成品" value="elec" />
<el-option label="服务权益" value="struct" />
</el-select>
<el-button type="primary" plain>搜索</el-button>
</div>
@ -36,3 +37,5 @@
</div>
</template>
<script setup lang="ts">
</script>

View File

@ -2,19 +2,19 @@
<div class="buy-module">
<div class="header-tools">
<div class="left-actions">
<el-button type="primary" :icon="Plus" @click="handleCreate">全量采购入库登记</el-button>
<el-button :icon="Refresh" @click="fetchData">刷新数据</el-button>
<el-button type="primary" :icon="Plus" @click="handleCreate" class="big-font-btn">采购入库登记</el-button>
<el-button :icon="Refresh" @click="fetchData" class="big-font-btn">刷新数据</el-button>
</div>
<el-popover placement="bottom" title="显示列配置" :width="400" trigger="click">
<template #reference>
<el-button :icon="Setting">自定义表格表头</el-button>
<el-button :icon="Setting" class="big-font-btn">自定义表格表头</el-button>
</template>
<el-checkbox-group v-model="visibleColumnProps" class="column-selector">
<el-divider content-position="left">基础层字段</el-divider>
<el-checkbox v-for="c in baseColumns" :key="c.prop" :value="c.prop">{{ c.label }}</el-checkbox>
<el-checkbox v-for="c in baseColumns" :key="c.prop" :label="c.prop">{{ c.label }}</el-checkbox>
<el-divider content-position="left">库存/财务层字段</el-divider>
<el-checkbox v-for="c in stockColumns" :key="c.prop" :value="c.prop">{{ c.label }}</el-checkbox>
<el-checkbox v-for="c in stockColumns" :key="c.prop" :label="c.prop">{{ c.label }}</el-checkbox>
</el-checkbox-group>
</el-popover>
</div>
@ -25,15 +25,16 @@
border
stripe
style="width: 100%"
size="small"
size="default"
highlight-current-row
class="custom-big-table"
>
<template v-for="col in allColumns" :key="col.prop">
<el-table-column
v-if="visibleColumnProps.includes(col.prop)"
:prop="col.prop"
:label="col.label"
:min-width="col.minWidth || '120'"
:min-width="col.minWidth || '150'"
show-overflow-tooltip
>
<template #default="scope" v-if="col.prop === 'serial_batch'">
@ -47,23 +48,23 @@
</template>
<template #default="scope" v-else-if="col.prop === 'status'">
<el-tag size="small" :type="getStatusType(scope.row.status)">
<el-tag size="large" :type="getStatusType(scope.row.status)" style="font-size: 18px;">
{{ scope.row.status }}
</el-tag>
</template>
<template #default="scope" v-else-if="['unit_price', 'total_price'].includes(col.prop)">
<template #default="scope" v-else-if="['price_unit', 'price_total'].includes(col.prop)">
{{ formatMoney(scope.row[col.prop]) }}
</template>
</el-table-column>
</template>
<el-table-column label="操作" width="150" fixed="right" align="center">
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="handleUpdate(row)">编辑</el-button>
<el-button link type="primary" size="large" @click="handleUpdate(row)" style="font-size: 15px;">编辑</el-button>
<el-popconfirm title="确定删除该条入库记录吗?" @confirm="handleDelete(row)">
<template #reference>
<el-button link type="danger" size="small">删除</el-button>
<el-button link type="danger" size="large" style="font-size: 15px;">删除</el-button>
</template>
</el-popconfirm>
</template>
@ -89,7 +90,7 @@
destroy-on-close
:close-on-click-modal="false"
>
<el-form :model="form" label-width="120px" ref="formRef" :rules="rules" size="default">
<el-form :model="form" label-width="140px" ref="formRef" :rules="rules" size="large">
<el-divider content-position="left"><b>1. 基础核心层 (Material Base)</b></el-divider>
<el-row :gutter="20">
@ -113,7 +114,11 @@
<el-divider content-position="left"><b>2. 实体库存层 (Stock Buy)</b></el-divider>
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="编码/SKU" prop="sku"><el-input v-model="form.sku" placeholder="选填" /></el-form-item></el-col>
<el-col :span="8">
<el-form-item label="编码/SKU" prop="sku">
<el-input v-model="form.sku" placeholder="选填" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入库日期" prop="in_date">
<el-input v-model="form.in_date" disabled placeholder="系统自动生成">
@ -121,7 +126,11 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="8"><el-form-item label="库位" prop="warehouse_location"><el-input v-model="form.warehouse_location" /></el-form-item></el-col>
<el-col :span="8">
<el-form-item label="库位" prop="warehouse_location">
<el-input v-model="form.warehouse_location" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" style="background-color: #fffbf0; border-radius: 4px; padding-top:10px;">
@ -136,7 +145,7 @@
</el-form-item>
</el-col>
<el-col :span="24">
<div style="font-size: 12px; color: #e6a23c; margin-left: 120px; margin-bottom: 10px; line-height: 1;">
<div style="font-size: 14px; color: #e6a23c; margin-left: 140px; margin-bottom: 10px; line-height: 1;">
* 规则序列号与批号互斥且必填其一 (填写一个会自动清空另一个)
</div>
</el-col>
@ -185,8 +194,8 @@
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm">
<el-button @click="visible = false" style="font-size: 15px;">取消</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm" style="font-size: 15px;">
{{ dialogStatus === 'create' ? '确认入库' : '保存修改' }}
</el-button>
</template>
@ -213,25 +222,24 @@ const queryParams = reactive({ page: 1, pageSize: 15 })
// --- 1. 列定义 ---
const baseColumns = [
{ prop: 'material_name', label: '物料名称', minWidth: '150' },
{ prop: 'category', label: '类别', minWidth: '100' },
{ prop: 'spec_model', label: '规格型号', minWidth: '150' },
{ prop: 'unit', label: '单位', minWidth: '70' }
{ prop: 'material_name', label: '物料名称', minWidth: '200' },
{ prop: 'category', label: '类别', minWidth: '120' },
{ prop: 'spec_model', label: '规格型号', minWidth: '200' },
{ prop: 'unit', label: '单位', minWidth: '100' }
]
const stockColumns = [
{ prop: 'sku', label: '编码/SKU', minWidth: '140' },
{ prop: 'inbound_date', label: '入库日期', minWidth: '120' },
// 组合展示列
{ prop: 'serial_batch', label: '序列号/批号', minWidth: '160' },
{ prop: 'stock_quantity', label: '库存数', minWidth: '90' },
{ prop: 'available_quantity', label: '可用数', minWidth: '90' },
{ prop: 'in_quantity', label: '入库量', minWidth: '90' },
{ prop: 'unit_price', label: '价', minWidth: '110' },
{ prop: 'total_price', label: '总价', minWidth: '110' },
{ prop: 'status', label: '状态', minWidth: '90' },
{ prop: 'warehouse_location', label: '库位', minWidth: '100' },
{ prop: 'supplier_name', label: '供应商', minWidth: '150' }
{ prop: 'sku', label: '编码/SKU', minWidth: '180' },
{ prop: 'inbound_date', label: '入库日期', minWidth: '160' },
{ prop: 'serial_batch', label: '序列号/批号', minWidth: '220' },
{ prop: 'qty_stock', label: '库存数', minWidth: '120' },
{ prop: 'qty_available', label: '可用数', minWidth: '120' },
{ prop: 'qty_inbound', label: '入库量', minWidth: '120' },
{ prop: 'price_unit', label: '单价', minWidth: '150' },
{ prop: 'price_total', label: '价', minWidth: '150' },
{ prop: 'status', label: '状态', minWidth: '120' },
{ prop: 'warehouse_loc', label: '库位', minWidth: '150' },
{ prop: 'supplier_name', label: '供应商', minWidth: '200' }
]
const allColumns = [...baseColumns, ...stockColumns]
@ -239,12 +247,12 @@ const allColumns = [...baseColumns, ...stockColumns]
// --- 2. 默认展示列 ---
const visibleColumnProps = ref([
'material_name', 'spec_model', 'inbound_date',
'serial_batch', 'stock_quantity', 'available_quantity', 'status'
'serial_batch', 'qty_stock', 'qty_available', 'status'
])
// --- 3. 表单对象 ---
const form = reactive({
id: undefined, // 编辑时使用
id: undefined,
material_name: '', category: '', spec_model: '', unit: '个',
material_type: '采购件', visibility_level: 0,
sku: '', in_date: '',
@ -255,12 +263,11 @@ const form = reactive({
supplier_name: '', arrival_photo: '', remark: ''
})
// --- 4. 校验逻辑 (互斥且必填其一) ---
// --- 4. 校验逻辑 ---
const validateIdentity = (rule: any, value: any, callback: any) => {
if (!form.serial_number && !form.batch_number) {
callback(new Error('序列号和批号至少填写一项'))
} else {
// 清除交叉报错
if (formRef.value) {
if (rule.field === 'serial_number' && form.batch_number) formRef.value.clearValidate('batch_number')
if (rule.field === 'batch_number' && form.serial_number) formRef.value.clearValidate('serial_number')
@ -278,12 +285,8 @@ const rules = {
}
// --- 5. 监听逻辑 ---
watch(() => form.serial_number, (val) => {
if (val && form.batch_number) form.batch_number = ''
})
watch(() => form.batch_number, (val) => {
if (val && form.serial_number) form.serial_number = ''
})
watch(() => form.serial_number, (val) => { if (val && form.batch_number) form.batch_number = '' })
watch(() => form.batch_number, (val) => { if (val && form.serial_number) form.serial_number = '' })
watch(() => form.in_quantity, (newVal) => {
if (newVal !== undefined) {
@ -297,16 +300,15 @@ watch(() => form.in_quantity, (newVal) => {
watch(() => form.unit_price, (newVal) => {
if (newVal !== undefined) {
form.total_price = Number((newVal * form.in_quantity).toFixed(4))
form.total_price = Number((newVal * (form.in_quantity || 0)).toFixed(4))
}
})
// --- 6. 核心操作 ---
const fetchData = async () => {
loading.value = true
try {
const res = await getBuyList(queryParams)
const res: any = await getBuyList(queryParams)
tableData.value = res.data.items || []
total.value = res.data.total || 0
} finally { loading.value = false }
@ -319,33 +321,27 @@ const handleCreate = () => {
visible.value = true
}
// 核心修改:手动映射后端数据到前端表单
const handleUpdate = (row: any) => {
dialogStatus.value = 'update'
// 先重置表单防止残留
resetForm()
// 1. 基础字段拷贝
Object.assign(form, row)
// 2. 修正字段映射 (后端Key -> 前端Form Key)
form.id = row.id
form.in_quantity = Number(row.qty_inbound) || 1
form.stock_quantity = Number(row.qty_inbound) || 1 // 这里假设库存没变或者应由后端传回stock_quantity
form.available_quantity = Number(row.qty_available) || 1
form.material_name = row.material_name
form.spec_model = row.spec_model
form.category = row.category
form.unit = row.unit
form.sku = row.sku
form.in_date = row.inbound_date
form.in_quantity = Number(row.qty_inbound) || 0
form.stock_quantity = Number(row.qty_stock) || 0
form.available_quantity = Number(row.qty_available) || 0
form.unit_price = Number(row.price_unit) || 0
form.total_price = Number(row.price_total) || 0
form.warehouse_location = row.warehouse_loc || ''
form.serial_number = row.serial_number || ''
form.batch_number = row.batch_number || ''
form.supplier_name = row.supplier_name || ''
form.status = row.status || '在库'
// 3. 计算总价
form.total_price = Number((form.in_quantity * form.unit_price).toFixed(2))
// 4. 补充日期
if (!form.in_date) form.in_date = dayjs().format('YYYY-MM-DD HH:mm:ss')
form.remark = row.remark || ''
visible.value = true
}
@ -355,7 +351,7 @@ const handleDelete = async (row: any) => {
ElMessage.success('删除成功')
fetchData()
} catch (e: any) {
ElMessage.error(e.message || '删除失败')
ElMessage.error('删除失败')
}
}
@ -375,7 +371,7 @@ const submitForm = async () => {
visible.value = false
fetchData()
} catch (e: any) {
ElMessage.error(e.message || '提交失败')
ElMessage.error('提交失败')
} finally { submitting.value = false }
}
})
@ -399,17 +395,27 @@ const getStatusType = (status: string) => {
return status === '在库' ? 'success' : 'info'
}
const formatMoney = (val: number) => {
return val ? `¥ ${Number(val).toFixed(2)}` : '-'
const formatMoney = (val: any) => {
const num = Number(val)
return isNaN(num) ? '-' : `¥ ${num.toFixed(2)}`
}
onMounted(() => fetchData())
</script>
<style scoped>
.buy-module { background: #fff; border-radius: 8px; }
.buy-module { background: #fff; border-radius: 8px; padding: 15px; }
.header-tools { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.column-selector { display: flex; flex-direction: column; padding: 10px; max-height: 400px; overflow-y: auto; }
.pagination-container { margin-top: 20px; display: flex; justify-content: flex-end; }
.pagination-container { margin-top: 15px; display: flex; justify-content: flex-end; }
:deep(.el-divider--horizontal) { margin: 15px 0 15px 0; }
.custom-big-table { font-size: 15px !important; }
:deep(.el-table .el-table__cell) { padding: 12px 0; }
:deep(.el-table th .cell) { font-size: 22px; font-weight: bold; }
:deep(.el-table td .cell) { line-height: 1.5; font-size: 15px; }
.big-font-btn { font-size: 15px !important; padding: 15px 25px !important; }
:deep(.el-form-item__label) { font-size: 15px !important; }
:deep(.el-input__inner) { font-size: 15px !important; height: 45px; }
:deep(.el-divider__text) { font-size: 15px !important; }
</style>

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped>
</style>

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped>
</style>