修改采购件页面金额显示,修改权限管理页面非字段级内容可见与可编辑联动

This commit is contained in:
dxc
2026-02-28 09:10:51 +08:00
parent 281a41c549
commit 8f6d0cd40b
2 changed files with 119 additions and 37 deletions

View File

@ -420,17 +420,21 @@
</el-row> </el-row>
<div class="divider-text">商务与采购信息</div> <div class="divider-text">商务与采购信息</div>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6"> <el-col :span="8">
<el-form-item label="币种"> <el-form-item label="币种">
<el-autocomplete v-model="form.currency" :fetch-suggestions="querySearchCurrency" placeholder="币种" style="width: 100%" :trigger-on-focus="true"> <el-autocomplete v-model="form.currency" :fetch-suggestions="querySearchCurrency" placeholder="币种" style="width: 100%" :trigger-on-focus="true">
<template #default="{ item }"><span>{{ item.value }}</span><span style="float:right; color:#999; font-size:12px">{{ item.desc }}</span></template> <template #default="{ item }"><span>{{ item.value }}</span><span style="float:right; color:#999; font-size:12px">{{ item.desc }}</span></template>
</el-autocomplete> </el-autocomplete>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"><el-form-item label="汇率"><el-input-number v-model="form.exchange_rate" :precision="2" controls-position="right" style="width:100%"/></el-form-item></el-col> <el-col :span="8">
<el-form-item label="汇率">
<el-col :span="6"> <el-input-number v-model="form.exchange_rate" :precision="2" controls-position="right" style="width:100%"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="税率"> <el-form-item label="税率">
<el-select v-model="form.tax_rate" style="width:100%" @change="updatePrices('tax')"> <el-select v-model="form.tax_rate" style="width:100%" @change="updatePrices('tax')">
<el-option label="0%" :value="0" /> <el-option label="0%" :value="0" />
@ -439,13 +443,49 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"><el-form-item label="税前单价" prop="unit_price"><el-input-number v-model="form.unit_price" :precision="4" controls-position="right" style="width:100%" @change="updatePrices('pre')"/></el-form-item></el-col>
<el-col :span="6"><el-form-item label="税后单价"><el-input-number v-model="form.post_tax_unit_price" :precision="4" controls-position="right" style="width:100%" @change="updatePrices('post')"/></el-form-item></el-col>
<el-col :span="6"><el-form-item label="总价"><el-input-number v-model="form.total_price" :precision="2" disabled :controls="false" style="width:100%" class="total-price-input"/></el-form-item></el-col>
</el-row> </el-row>
<el-row :gutter="20">
<el-row :gutter="20" style="margin-top: 15px;">
<el-col :span="8">
<el-form-item label="不含税单价" prop="unit_price">
<el-input-number
v-model="form.unit_price"
:precision="2"
:controls="false"
style="width:100%"
placeholder="请输入"
@change="updatePrices('pre')"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="含税单价">
<el-input-number
v-model="form.post_tax_unit_price"
:precision="2"
:controls="false"
style="width:100%"
placeholder="请输入"
@change="updatePrices('post')"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总价">
<el-input-number
v-model="form.total_price"
:precision="2"
disabled
:controls="false"
style="width:100%"
class="total-price-input"
placeholder="自动计算"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 15px;">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="供应商"> <el-form-item label="供应商">
<el-autocomplete <el-autocomplete
@ -840,7 +880,9 @@ const form = reactive({
material_name: '', spec_model: '', category: '', unit: '', material_type: '', material_name: '', spec_model: '', category: '', unit: '', material_type: '',
sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', inspection_status: '未检', sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', inspection_status: '未检',
in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '',
unit_price: 0, post_tax_unit_price: 0, total_price: 0, unit_price: undefined as number | undefined,
post_tax_unit_price: undefined as number | undefined,
total_price: undefined as number | undefined,
tax_rate: 0, tax_rate: 0,
currency: 'CNY', exchange_rate: 1.00, currency: 'CNY', exchange_rate: 1.00,
supplier_name: '', purchaser: '', purchaser_email: '', source_link: '', detail_link: '', supplier_name: '', purchaser: '', purchaser_email: '', source_link: '', detail_link: '',
@ -848,7 +890,6 @@ const form = reactive({
print_copies: 1 print_copies: 1
}) })
// ------------------------------------ // ------------------------------------
// 建议/Autocomplete 逻辑 // 建议/Autocomplete 逻辑
// ------------------------------------ // ------------------------------------
@ -1037,24 +1078,44 @@ const handleEntryModeChange = (val: string) => {
if(formRef.value) formRef.value.clearValidate('batch_number') if(formRef.value) formRef.value.clearValidate('batch_number')
} }
} }
// 价格联动计算 // 价格联动计算 (精确到小数点后2位并支持空值)
const updatePrices = (source: string) => { const updatePrices = (source: string) => {
const taxMultiplier = 1 + (form.tax_rate || 0) / 100; const taxMultiplier = 1 + (form.tax_rate || 0) / 100;
if (source === 'pre') { if (source === 'pre') {
form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(4)); if (form.unit_price !== undefined && form.unit_price !== null) {
} else if (source === 'post') { form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(2));
form.unit_price = Number((form.post_tax_unit_price / taxMultiplier).toFixed(4)); } else {
} else if (source === 'tax') { form.post_tax_unit_price = undefined;
form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(4)); }
} else if (source === 'post') {
if (form.post_tax_unit_price !== undefined && form.post_tax_unit_price !== null) {
form.unit_price = Number((form.post_tax_unit_price / taxMultiplier).toFixed(2));
} else {
form.unit_price = undefined;
}
} else if (source === 'tax') {
if (form.unit_price !== undefined && form.unit_price !== null) {
form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(2));
}
}
if (form.in_quantity !== undefined && form.unit_price !== undefined && form.unit_price !== null) {
form.total_price = Number((form.in_quantity * form.unit_price).toFixed(2));
} else {
form.total_price = undefined;
} }
form.total_price = Number((form.in_quantity * form.unit_price).toFixed(4));
} }
watch(() => [form.in_quantity, form.unit_price], () => { watch(() => [form.in_quantity, form.unit_price], () => {
form.total_price = Number((form.in_quantity * form.unit_price).toFixed(4)); if (form.unit_price !== undefined && form.unit_price !== null) {
// 同时更新税后单价 form.total_price = Number((form.in_quantity * form.unit_price).toFixed(2));
// 同时更新含税单价
const taxMultiplier = 1 + (form.tax_rate || 0) / 100; const taxMultiplier = 1 + (form.tax_rate || 0) / 100;
form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(4)); form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(2));
} else {
form.total_price = undefined;
form.post_tax_unit_price = undefined;
}
}) })
const fetchData = async () => { const fetchData = async () => {
@ -1114,16 +1175,19 @@ const handleUpdate = (row: any) => {
unit: row.unit, material_type: row.material_type, sku: row.sku, barcode: row.barcode, in_date: row.inbound_date, unit: row.unit, material_type: row.material_type, sku: row.sku, barcode: row.barcode, in_date: row.inbound_date,
warehouse_location: row.warehouse_loc, status: row.status, inspection_status: row.inspection_status, warehouse_location: row.warehouse_loc, status: row.status, inspection_status: row.inspection_status,
in_quantity: Number(row.qty_inbound), stock_quantity: Number(row.qty_stock), available_quantity: Number(row.qty_available), in_quantity: Number(row.qty_inbound), stock_quantity: Number(row.qty_stock), available_quantity: Number(row.qty_available),
unit_price: Number(row.unit_price), total_price: Number(row.total_price), unit_price: (row.unit_price !== null && row.unit_price !== undefined) ? Number(row.unit_price) : undefined,
total_price: (row.total_price !== null && row.total_price !== undefined) ? Number(row.total_price) : undefined,
tax_rate: Number(row.tax_rate), tax_rate: Number(row.tax_rate),
currency: row.currency, exchange_rate: Number(row.exchange_rate), currency: row.currency, exchange_rate: Number(row.exchange_rate),
supplier_name: row.supplier_name, purchaser: row.purchaser, purchaser_email: row.purchaser_email, supplier_name: row.supplier_name, purchaser: row.purchaser, purchaser_email: row.purchaser_email,
source_link: row.source_link, detail_link: row.detail_link, source_link: row.source_link, detail_link: row.detail_link,
arrival_photo: row.arrival_photo || [], inspection_report: row.inspection_report || [] arrival_photo: row.arrival_photo || [], inspection_report: row.inspection_report || []
}) })
// 计算税单价 // 计算税单价
if (form.unit_price !== undefined && form.unit_price !== null) {
const taxMultiplier = 1 + (form.tax_rate || 0) / 100; const taxMultiplier = 1 + (form.tax_rate || 0) / 100;
form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(4)); form.post_tax_unit_price = Number((form.unit_price * taxMultiplier).toFixed(2));
}
arrivalFileList.value = form.arrival_photo.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) })) arrivalFileList.value = form.arrival_photo.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) }))
const reports = form.inspection_report || [] const reports = form.inspection_report || []
@ -1150,9 +1214,9 @@ const submitForm = async () => {
const payload = { const payload = {
...form, ...form,
inspection_report: onlyImages, inspection_report: onlyImages,
in_quantity: Number(form.in_quantity), in_quantity: Number(form.in_quantity || 0),
unit_price: Number(form.unit_price), unit_price: Number(form.unit_price || 0),
post_tax_unit_price: Number(form.post_tax_unit_price) post_tax_unit_price: Number(form.post_tax_unit_price || 0)
} }
try { try {
if (dialogStatus.value === 'create') { if (dialogStatus.value === 'create') {
@ -1319,14 +1383,22 @@ const resetForm = () => {
id: undefined, base_id: undefined, id: undefined, base_id: undefined,
company_name: '', company_name: '',
material_name: '', spec_model: '', category: '', unit: '', material_type: '', sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', inspection_status: '未检', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', material_name: '', spec_model: '', category: '', unit: '', material_type: '', sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', inspection_status: '未检', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '',
unit_price: 0, total_price: 0, unit_price: undefined, post_tax_unit_price: undefined, total_price: undefined,
tax_rate: 0, tax_rate: 0,
currency: 'CNY', exchange_rate: 1.00, supplier_name: '', purchaser: '', purchaser_email: '', source_link: '', detail_link: '', arrival_photo: [], inspection_report: [], currency: 'CNY', exchange_rate: 1.00, supplier_name: '', purchaser: '', purchaser_email: '', source_link: '', detail_link: '', arrival_photo: [], inspection_report: [],
print_copies: 1 print_copies: 1
}) })
} }
const getStatusType = (status: string) => { const map: any = {'在库': 'success', '出库': 'info', '损耗': 'danger'}; return map[status] || 'warning' } const getStatusType = (status: string) => { const map: any = {'在库': 'success', '出库': 'info', '损耗': 'danger'}; return map[status] || 'warning' }
const formatMoney = (val: any, currency = '¥') => { const num = Number(val); return isNaN(num) ? '-' : `${currency} ${num.toFixed(2)}` }
// 列表金额显示增加千分位处理并保留2位小数
const formatMoney = (val: any, currency = '¥') => {
const num = Number(val);
if (isNaN(num)) return '-';
const parts = num.toFixed(2).split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return `${currency} ${parts.join('.')}`;
}
onMounted(() => { onMounted(() => {
// 先根据权限初始化列显示状态 // 先根据权限初始化列显示状态
@ -1511,6 +1583,11 @@ onMounted(() => {
.camera-card:hover { border-color: #409EFF; color: #409EFF; } .camera-card:hover { border-color: #409EFF; color: #409EFF; }
.camera-card .text { font-size: 12px; margin-top: 5px; } .camera-card .text { font-size: 12px; margin-top: 5px; }
.camera-card .el-icon { font-size: 24px; } .camera-card .el-icon { font-size: 24px; }
/* 自定义千分位无箭头输入框样式,用于强迫症优化显示 */
:deep(.el-input-number .el-input__inner) {
text-align: left;
}
</style> </style>
<style> <style>

View File

@ -308,6 +308,11 @@ const handleReadChange = (val: boolean, row: PermissionNode) => {
// 如果开启可读,默认全选字段 (提升体验) // 如果开启可读,默认全选字段 (提升体验)
// row.checkedElements = row.elements.map(e => e.code) // row.checkedElements = row.elements.map(e => e.code)
// updateCheckAllStatus(row) // updateCheckAllStatus(row)
// 【新增功能】如果没有字段级控制,且存在操作权限,则自动联动勾选可编辑
if ((!row.elements || row.elements.length === 0) && row.operationCode) {
row.hasWrite = true
}
} }
// 联动子菜单:如果父级关闭,子级是否关闭?通常不强制,但可以做 // 联动子菜单:如果父级关闭,子级是否关闭?通常不强制,但可以做