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

This commit is contained in:
dxc
2026-02-28 09:23:07 +08:00
parent 8f6d0cd40b
commit b85f28fc72
3 changed files with 84 additions and 35 deletions

View File

@ -471,7 +471,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="总价"> <el-form-item label="不含税总价">
<el-input-number <el-input-number
v-model="form.total_price" v-model="form.total_price"
:precision="2" :precision="2"
@ -778,7 +778,7 @@ const stockColumns = [
{prop: 'tax_rate', label: '税率', minWidth: '80'}, {prop: 'tax_rate', label: '税率', minWidth: '80'},
{prop: 'unit_price', label: '不含税单价', minWidth: '120'}, {prop: 'unit_price', label: '不含税单价', minWidth: '120'},
{prop: 'total_price', label: '总价', minWidth: '120'}, {prop: 'total_price', label: '不含税总价', minWidth: '120'},
{prop: 'currency', label: '币种', minWidth: '80'}, {prop: 'currency', label: '币种', minWidth: '80'},
{prop: 'exchange_rate', label: '汇率', minWidth: '80'}, {prop: 'exchange_rate', label: '汇率', minWidth: '80'},
{prop: 'supplier_name', label: '供应商', minWidth: '150'}, {prop: 'supplier_name', label: '供应商', minWidth: '150'},

View File

@ -380,7 +380,13 @@
<el-autocomplete v-model="form.production_manager" :fetch-suggestions="querySearchManager" placeholder="输入或选择负责人" style="width: 100%" clearable :trigger-on-focus="true" @select="handleManagerSelect"/> <el-autocomplete v-model="form.production_manager" :fetch-suggestions="querySearchManager" placeholder="输入或选择负责人" style="width: 100%" clearable :trigger-on-focus="true" @select="handleManagerSelect"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"><el-form-item label="产品定价"><el-input-number v-model="form.sale_price" :precision="2" style="width:100%"><template #prefix>¥</template></el-input-number></el-form-item></el-col> <el-col :span="8">
<el-form-item label="产品定价">
<el-input-number v-model="form.sale_price" :precision="2" :controls="false" style="width:100%" placeholder="请输入">
<template #prefix>¥</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row :gutter="24"> <el-row :gutter="24">
@ -389,8 +395,16 @@
<el-date-picker v-model="form.production_time_range" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" /> <el-date-picker v-model="form.production_time_range" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"><el-form-item label="原料成本"><el-input-number v-model="form.raw_material_cost" :precision="2" style="width:100%" /></el-form-item></el-col> <el-col :span="6">
<el-col :span="6"><el-form-item label="人工成本"><el-input-number v-model="form.manual_cost" :precision="2" style="width:100%" /></el-form-item></el-col> <el-form-item label="原料成本">
<el-input-number v-model="form.raw_material_cost" :precision="2" :controls="false" style="width:100%" placeholder="请输入"/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="人工成本">
<el-input-number v-model="form.manual_cost" :precision="2" :controls="false" style="width:100%" placeholder="请输入"/>
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row :gutter="24" style="margin-top:10px"> <el-row :gutter="24" style="margin-top:10px">
<el-col :span="24"><el-form-item label="详情链接"><el-input v-model="form.detail_link" /></el-form-item></el-col> <el-col :span="24"><el-form-item label="详情链接"><el-input v-model="form.detail_link" /></el-form-item></el-col>
@ -628,7 +642,9 @@ const form = reactive({
warehouse_location: '', status: '在库', quality_status: '合格', warehouse_location: '', status: '在库', quality_status: '合格',
bom_code: '', bom_version: '', work_order_code: '', order_id: '', bom_code: '', bom_version: '', work_order_code: '', order_id: '',
production_manager: '', production_time_range: [] as string[], production_manager: '', production_time_range: [] as string[],
raw_material_cost: 0, manual_cost: 0, sale_price: 0, raw_material_cost: undefined as number | undefined,
manual_cost: undefined as number | undefined,
sale_price: undefined as number | undefined,
quality_report_link: [] as string[], inspection_report_link: [] as string[], product_photo: [] as string[], detail_link: '' quality_report_link: [] as string[], inspection_report_link: [] as string[], product_photo: [] as string[], detail_link: ''
}) })
@ -837,9 +853,9 @@ const handleUpdate = (row: any) => {
quality_report_link: row.quality_report_link || [], quality_report_link: row.quality_report_link || [],
inspection_report_link: row.inspection_report_link || [], inspection_report_link: row.inspection_report_link || [],
in_quantity: Number(row.qty_inbound), in_quantity: Number(row.qty_inbound),
raw_material_cost: Number(row.raw_material_cost), raw_material_cost: (row.raw_material_cost !== null && row.raw_material_cost !== undefined) ? Number(row.raw_material_cost) : undefined,
manual_cost: Number(row.manual_cost), manual_cost: (row.manual_cost !== null && row.manual_cost !== undefined) ? Number(row.manual_cost) : undefined,
sale_price: Number(row.sale_price) sale_price: (row.sale_price !== null && row.sale_price !== undefined) ? Number(row.sale_price) : undefined
}) })
if(row.production_start_time && row.production_end_time) { form.production_time_range = [row.production_start_time, row.production_end_time] } else { form.production_time_range = [] } if(row.production_start_time && row.production_end_time) { form.production_time_range = [row.production_start_time, row.production_end_time] } else { form.production_time_range = [] }
productPhotoList.value = form.product_photo.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) })) productPhotoList.value = form.product_photo.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) }))
@ -943,7 +959,16 @@ const submitForm = async () => {
const iImages = iList.filter(item => !isExternalLink(item)) const iImages = iList.filter(item => !isExternalLink(item))
if (inspection_url.value && !iList.includes(inspection_url.value)) iImages.push(inspection_url.value) if (inspection_url.value && !iList.includes(inspection_url.value)) iImages.push(inspection_url.value)
else if (inspection_url.value) iImages.push(inspection_url.value) else if (inspection_url.value) iImages.push(inspection_url.value)
const payload = { ...form, quality_report_link: qImages, inspection_report_link: iImages, production_start_time: form.production_time_range?.[0], production_end_time: form.production_time_range?.[1] } const payload = {
...form,
quality_report_link: qImages,
inspection_report_link: iImages,
raw_material_cost: Number(form.raw_material_cost || 0),
manual_cost: Number(form.manual_cost || 0),
sale_price: Number(form.sale_price || 0),
production_start_time: form.production_time_range?.[0],
production_end_time: form.production_time_range?.[1]
}
delete payload.production_time_range delete payload.production_time_range
try { try {
if(dialogStatus.value === 'create') { if(dialogStatus.value === 'create') {
@ -969,7 +994,7 @@ const handlePrint = async (row: any) => {
const confirmPrint = async () => { printing.value = true; try { await executePrint(currentPrintData.value); ElMessage.success('已发送'); printVisible.value = false } catch (e: any) { ElMessage.error('打印失败') } finally { printing.value = false } } const confirmPrint = async () => { printing.value = true; try { await executePrint(currentPrintData.value); ElMessage.success('已发送'); printVisible.value = false } catch (e: any) { ElMessage.error('打印失败') } finally { printing.value = false } }
const resetForm = () => { const resetForm = () => {
materialOptions.value = []; bomOptions.value = []; productPhotoList.value = []; qualityFileList.value = []; inspectionFileList.value = []; quality_url.value = ''; inspection_url.value = '' materialOptions.value = []; bomOptions.value = []; productPhotoList.value = []; qualityFileList.value = []; inspectionFileList.value = []; quality_url.value = ''; inspection_url.value = ''
Object.assign(form, { id: undefined, base_id: undefined, material_name: '', spec_model: '', material_type: '', category: '', unit: '', sku: '', barcode: '', serial_number: '', in_date: '', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', status: '在库', quality_status: '合格', bom_code: '', bom_version: '', work_order_code: '', order_id: '', production_manager: '', production_time_range: [], raw_material_cost: 0, manual_cost: 0, sale_price: 0, quality_report_link: [], inspection_report_link: [], product_photo: [], detail_link: '' }) Object.assign(form, { id: undefined, base_id: undefined, material_name: '', spec_model: '', material_type: '', category: '', unit: '', sku: '', barcode: '', serial_number: '', in_date: '', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', status: '在库', quality_status: '合格', bom_code: '', bom_version: '', work_order_code: '', order_id: '', production_manager: '', production_time_range: [], raw_material_cost: undefined, manual_cost: undefined, sale_price: undefined, quality_report_link: [], inspection_report_link: [], product_photo: [], detail_link: '' })
} }
const getStatusType = (s:string) => ({'在库':'success','出库':'info','借库':'warning','损耗':'danger'}[s]||'warning') const getStatusType = (s:string) => ({'在库':'success','出库':'info','借库':'warning','损耗':'danger'}[s]||'warning')
const getQualityType = (s:string) => ({'合格':'success','不合格':'danger','待检':'info'}[s]||'info') const getQualityType = (s:string) => ({'合格':'success','不合格':'danger','待检':'info'}[s]||'info')
@ -1047,6 +1072,9 @@ onMounted(() => {
/* [新增] 纯文本样式 */ /* [新增] 纯文本样式 */
.is-text-view :deep(.el-input__wrapper) { box-shadow: none !important; background-color: transparent !important; border-bottom: 1px dashed #dcdfe6; border-radius: 0; padding-left: 0; } .is-text-view :deep(.el-input__wrapper) { box-shadow: none !important; background-color: transparent !important; border-bottom: 1px dashed #dcdfe6; border-radius: 0; padding-left: 0; }
.is-text-view :deep(.el-input__inner) { color: #303133; font-weight: 600; font-size: 14px; cursor: text; } .is-text-view :deep(.el-input__inner) { color: #303133; font-weight: 600; font-size: 14px; cursor: text; }
/* 左对齐数字框 */
:deep(.el-input-number .el-input__inner) { text-align: left; }
</style> </style>
<style> <style>

View File

@ -468,9 +468,21 @@
<div class="divider-text">成本核算 (单件)</div> <div class="divider-text">成本核算 (单件)</div>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"><el-form-item label="原材料成本"><el-input-number v-model="form.raw_material_cost" :precision="2" controls-position="right" style="width:100%"/></el-form-item></el-col> <el-col :span="8">
<el-col :span="8"><el-form-item label="手动/工时"><el-input-number v-model="form.manual_cost" :precision="2" controls-position="right" style="width:100%"/></el-form-item></el-col> <el-form-item label="原材料成本">
<el-col :span="8"><el-form-item label="单件总成本"><el-input-number v-model="form.unit_total_cost" :precision="2" disabled :controls="false" style="width:100%" class="total-price-input"/></el-form-item></el-col> <el-input-number v-model="form.raw_material_cost" :precision="2" :controls="false" style="width:100%" placeholder="请输入"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="手动/工时">
<el-input-number v-model="form.manual_cost" :precision="2" :controls="false" style="width:100%" placeholder="请输入"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单件总成本">
<el-input-number v-model="form.unit_total_cost" :precision="2" :controls="false" style="width:100%" placeholder="请输入"/>
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row :gutter="24" style="margin-top:10px"> <el-row :gutter="24" style="margin-top:10px">
@ -516,7 +528,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, reactive, onMounted, watch} from 'vue' import {ref, reactive, onMounted} from 'vue'
import {Plus, Setting, Refresh, Search, Lock, Box, House, InfoFilled, Link, Printer, Camera, Picture} from '@element-plus/icons-vue' import {Plus, Setting, Refresh, Search, Lock, Box, House, InfoFilled, Link, Printer, Camera, Picture} from '@element-plus/icons-vue'
import {ElMessage, ElLoading} from 'element-plus' import {ElMessage, ElLoading} from 'element-plus'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -677,26 +689,19 @@ const permissionMap: Record<string, string> = {
// 根据用户权限初始化列显示状态 // 根据用户权限初始化列显示状态
const initColumnPermissions = () => { const initColumnPermissions = () => {
// 超级管理员跳过权限检查,显示所有列
if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') { if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') {
return return
} }
// 普通用户:严格执行列级权限控制,没有权限的列必须隐藏
// 遍历 allColumns将没有权限的列从 visibleColumnProps 中移除
const allowedColumns = allColumns.filter(col => { const allowedColumns = allColumns.filter(col => {
const code = permissionMap[col.prop] const code = permissionMap[col.prop]
if (code) { if (code) {
return userStore.hasPermission(code) return userStore.hasPermission(code)
} }
// 如果没有映射,默认隐藏
return false return false
}).map(col => col.prop) }).map(col => col.prop)
// 更新 visibleColumnProps只保留有权限的列
// 同时保持用户之前已经选择的有权限的列
const currentVisible = visibleColumnProps.value.filter(prop => allowedColumns.includes(prop)) const currentVisible = visibleColumnProps.value.filter(prop => allowedColumns.includes(prop))
// 如果当前没有可见列,则使用 allowedColumns 作为默认
if (currentVisible.length === 0) { if (currentVisible.length === 0) {
visibleColumnProps.value = allowedColumns visibleColumnProps.value = allowedColumns
} else { } else {
@ -718,8 +723,12 @@ const visibleColumnProps = ref(defaultColumns)
const form = reactive({ const form = reactive({
id: undefined, base_id: undefined as number | undefined, id: undefined, base_id: undefined as number | undefined,
company_name: '', // [新增] company_name: '',
material_name: '', spec_model: '', category: '', unit: '', material_type: '', sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', quality_status: '合格', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', bom_code: '', bom_version: '', work_order_code: '', raw_material_cost: 0, manual_cost: 0, unit_total_cost: 0, production_manager: '', production_time_range: [] as string[], arrival_photo: [] as string[], quality_report_link: [] as string[], detail_link: '' material_name: '', spec_model: '', category: '', unit: '', material_type: '', sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', quality_status: '合格', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', bom_code: '', bom_version: '', work_order_code: '',
raw_material_cost: undefined as number | undefined,
manual_cost: undefined as number | undefined,
unit_total_cost: undefined as number | undefined,
production_manager: '', production_time_range: [] as string[], arrival_photo: [] as string[], quality_report_link: [] as string[], detail_link: ''
}) })
// ------------------------------------ // ------------------------------------
@ -842,11 +851,9 @@ const rules = {
// 表单字段权限检查 // 表单字段权限检查
// ------------------------------------ // ------------------------------------
const hasFormFieldPermission = (fieldName: string) => { const hasFormFieldPermission = (fieldName: string) => {
// 超级管理员直接返回true
if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') { if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') {
return true return true
} }
// 根据字段名映射到权限码
const map: Record<string, string> = { const map: Record<string, string> = {
company_name: 'inbound_semi:company_name', company_name: 'inbound_semi:company_name',
material_name: 'inbound_semi:material_name', material_name: 'inbound_semi:material_name',
@ -872,14 +879,13 @@ const hasFormFieldPermission = (fieldName: string) => {
manual_cost: 'inbound_semi:manual_cost', manual_cost: 'inbound_semi:manual_cost',
unit_total_cost: 'inbound_semi:unit_total_cost', unit_total_cost: 'inbound_semi:unit_total_cost',
production_manager: 'inbound_semi:production_manager', production_manager: 'inbound_semi:production_manager',
production_time_range: 'inbound_semi:production_start_time', // 使用开始时间权限 production_time_range: 'inbound_semi:production_start_time',
arrival_photo: 'inbound_semi:arrival_photo', arrival_photo: 'inbound_semi:arrival_photo',
quality_report_link: 'inbound_semi:quality_report_link', quality_report_link: 'inbound_semi:quality_report_link',
detail_link: 'inbound_semi:detail_link', detail_link: 'inbound_semi:detail_link',
} }
const code = map[fieldName] const code = map[fieldName]
if (!code) { if (!code) {
// 没有映射的字段默认显示
return true return true
} }
return userStore.hasPermission(code) return userStore.hasPermission(code)
@ -910,7 +916,6 @@ const handleEntryModeChange = (val: string) => {
if (val === 'batch') { form.serial_number = ''; form.batch_number = '000001'; if(formRef.value) formRef.value.clearValidate('serial_number') } if (val === 'batch') { form.serial_number = ''; form.batch_number = '000001'; if(formRef.value) formRef.value.clearValidate('serial_number') }
else { form.batch_number = ''; if(formRef.value) formRef.value.clearValidate('batch_number') } else { form.batch_number = ''; if(formRef.value) formRef.value.clearValidate('batch_number') }
} }
watch(() => [form.raw_material_cost, form.manual_cost], () => { form.unit_total_cost = Number((form.raw_material_cost + form.manual_cost).toFixed(2)) })
const fetchData = async () => { const fetchData = async () => {
loading.value = true loading.value = true
@ -967,7 +972,9 @@ const handleUpdate = (row: any) => {
warehouse_location: row.warehouse_loc, status: row.status, quality_status: row.quality_status, warehouse_location: row.warehouse_loc, status: row.status, quality_status: row.quality_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),
bom_code: row.bom_code, bom_version: row.bom_version, work_order_code: row.work_order_code, bom_code: row.bom_code, bom_version: row.bom_version, work_order_code: row.work_order_code,
raw_material_cost: Number(row.raw_material_cost) || 0, manual_cost: Number(row.manual_cost) || 0, raw_material_cost: (row.raw_material_cost !== null && row.raw_material_cost !== undefined) ? Number(row.raw_material_cost) : undefined,
manual_cost: (row.manual_cost !== null && row.manual_cost !== undefined) ? Number(row.manual_cost) : undefined,
unit_total_cost: (row.unit_total_cost !== null && row.unit_total_cost !== undefined) ? Number(row.unit_total_cost) : undefined,
production_manager: row.production_manager, production_manager: row.production_manager,
production_time_range: (row.production_start_time && row.production_end_time) ? [row.production_start_time, row.production_end_time] : [], production_time_range: (row.production_start_time && row.production_end_time) ? [row.production_start_time, row.production_end_time] : [],
detail_link: row.detail_link, detail_link: row.detail_link,
@ -1065,7 +1072,16 @@ const submitForm = async () => {
if (quality_report_url.value && !finalReportList.includes(quality_report_url.value)) finalReportList.push(quality_report_url.value) if (quality_report_url.value && !finalReportList.includes(quality_report_url.value)) finalReportList.push(quality_report_url.value)
const onlyImages = finalReportList.filter(item => !isExternalLink(item)) const onlyImages = finalReportList.filter(item => !isExternalLink(item))
if (quality_report_url.value) onlyImages.push(quality_report_url.value) if (quality_report_url.value) onlyImages.push(quality_report_url.value)
const payload: any = { ...form, quality_report_link: onlyImages, in_quantity: Number(form.in_quantity), raw_material_cost: Number(form.raw_material_cost), manual_cost: Number(form.manual_cost), production_start_time: form.production_time_range?.[0] || null, production_end_time: form.production_time_range?.[1] || null } const payload: any = {
...form,
quality_report_link: onlyImages,
in_quantity: Number(form.in_quantity),
raw_material_cost: Number(form.raw_material_cost || 0),
manual_cost: Number(form.manual_cost || 0),
unit_total_cost: Number(form.unit_total_cost || 0),
production_start_time: form.production_time_range?.[0] || null,
production_end_time: form.production_time_range?.[1] || null
}
delete payload.production_time_range delete payload.production_time_range
try { try {
if (dialogStatus.value === 'create') { if (dialogStatus.value === 'create') {
@ -1098,11 +1114,13 @@ const resetForm = () => {
Object.assign(form, { Object.assign(form, {
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: '在库', quality_status: '合格', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', bom_code: '', bom_version: '', work_order_code: '', raw_material_cost: 0, manual_cost: 0, unit_total_cost: 0, production_manager: '', production_time_range: [], arrival_photo: [], quality_report_link: [], detail_link: '' }) material_name: '', spec_model: '', category: '', unit: '', material_type: '', sku: '', barcode: '', in_date: '', serial_number: '', batch_number: '', status: '在库', quality_status: '合格', in_quantity: 1, stock_quantity: 1, available_quantity: 1, warehouse_location: '', bom_code: '', bom_version: '', work_order_code: '',
raw_material_cost: undefined, manual_cost: undefined, unit_total_cost: undefined,
production_manager: '', production_time_range: [], arrival_photo: [], quality_report_link: [], detail_link: '' })
} }
const getStatusType = (status: string) => { const map: any = { '在库': 'success', '出库': 'info', '借库': 'warning', '损耗': 'danger' }; return map[status] || 'warning' } const getStatusType = (status: string) => { const map: any = { '在库': 'success', '出库': 'info', '借库': 'warning', '损耗': 'danger' }; return map[status] || 'warning' }
const getQualityType = (status: string) => { const map: any = { '合格': 'success', '不合格': 'danger', '待检': 'info', '返修中': 'warning' }; return map[status] || 'info' } const getQualityType = (status: string) => { const map: any = { '合格': 'success', '不合格': 'danger', '待检': 'info', '返修中': 'warning' }; return map[status] || 'info' }
const formatMoney = (val: any) => { const num = Number(val); return isNaN(num) ? '-' : `¥ ${num.toFixed(2)}` } const formatMoney = (val: any) => isNaN(Number(val)) ? '-' : `¥ ${Number(val).toFixed(2)}`
onMounted(() => { onMounted(() => {
// 先根据权限初始化列显示状态 // 先根据权限初始化列显示状态
@ -1178,6 +1196,9 @@ onMounted(() => {
.opt-spec { color: #999; font-size: 12px; } .opt-spec { color: #999; font-size: 12px; }
.opt-tags { display: flex; gap: 5px; flex-shrink: 0; } .opt-tags { display: flex; gap: 5px; flex-shrink: 0; }
.company-tag { font-weight: bold; } .company-tag { font-weight: bold; }
/* 左对齐数字框 */
:deep(.el-input-number .el-input__inner) { text-align: left; }
</style> </style>
<style> <style>