feat:产品物料信息-新增/编辑添加接口入参

main
黄伟杰 1 day ago
parent 84dc36c5d7
commit 4f16b2a847

@ -565,6 +565,16 @@ export default {
validatorStatusRequired: 'Status is required'
}
},
ErpPurchase: {
Supplier: {
name: 'Supplier Name',
contact: 'Contact',
mobile: 'Mobile',
telephone: 'Telephone',
placeholderName: 'Please enter supplier name',
placeholderMobile: 'Please enter mobile'
}
},
lock: {
lockScreen: 'Lock screen',
lock: 'Lock',
@ -2082,6 +2092,20 @@ export default {
validatorCategoryTypeRequired: 'Type can not be empty',
validatorUnitRequired: 'Unit id can not be empty',
validatorStatusRequired: 'Product status can not be empty',
dialogPackagingSchemeLabel: 'Packaging Scheme',
dialogPackagingSchemeTitle: 'Select Packaging Scheme',
dialogPackagingSchemePlaceholder: 'Please select packaging scheme',
dialogDefaultStatusLabel: 'Default Scheme',
validatorPackagingSchemeRequired: 'Packaging scheme is required',
dialogSupplierLabel: 'Supplier',
dialogSupplierTitle: 'Select Supplier',
dialogSupplierPlaceholder: 'Please select supplier',
validatorSupplierRequired: 'Supplier is required',
dialogFragileFlagLabel: 'Fragile',
dialogSparePartLevelLabel: 'Spare Part Level',
dialogPurchaseCycleLabel: 'Purchase Cycle (Days)',
dialogBrandLabel: 'Brand',
dialogBrandPlaceholder: 'Please enter brand',
categoryTree: 'Category Tree',
addCategory: 'Add Category',
refreshTree: 'Refresh'

@ -565,6 +565,16 @@ export default {
validatorStatusRequired: '状态不能为空'
}
},
ErpPurchase: {
Supplier: {
name: '供应商名称',
contact: '联系人',
mobile: '手机号码',
telephone: '联系电话',
placeholderName: '请输入供应商名称',
placeholderMobile: '请输入手机号码'
}
},
lock: {
lockScreen: '锁定屏幕',
lock: '锁定',
@ -3235,6 +3245,20 @@ export default {
validatorCategoryTypeRequired: '类型不能为空',
validatorUnitRequired: '单位编号不能为空',
validatorStatusRequired: '产品状态不能为空',
dialogPackagingSchemeLabel: '包装方案',
dialogPackagingSchemeTitle: '选择包装方案',
dialogPackagingSchemePlaceholder: '请选择包装方案',
dialogDefaultStatusLabel: '是否默认方案',
validatorPackagingSchemeRequired: '包装方案不能为空',
dialogSupplierLabel: '供应商',
dialogSupplierTitle: '选择供应商',
dialogSupplierPlaceholder: '请选择供应商',
validatorSupplierRequired: '供应商不能为空',
dialogFragileFlagLabel: '是否易损件',
dialogSparePartLevelLabel: '备件等级',
dialogPurchaseCycleLabel: '采购周期(天)',
dialogBrandLabel: '品牌',
dialogBrandPlaceholder: '请输入品牌',
categoryTree: '产品分类树',
addCategory: '新增分类',
refreshTree: '刷新'

@ -236,6 +236,7 @@ export enum DICT_TYPE {
ERP_AUTOCODE_CYCLEMETHOD = "erp_autocode_cyclemethod",
ERP_AUTOCODE_PARTTYPE = "erp_autocode_parttype",
MATERIAL_CLASSIFICATION_TYPE = 'material_classification_type',
SPARE_PARTS_LEVEL = 'spare_parts_level',
// ========== MES - 生产管理模块 ==========

@ -110,6 +110,61 @@
/>
</el-form-item>
</el-col>
<el-col v-if="formData.categoryType === 1" :span="12">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogPackagingSchemeLabel')" prop="packagingSchemes">
<el-input
:model-value="displayPackagingSchemeText"
readonly
clearable
:placeholder="t('FactoryModeling.ProductInformation.dialogPackagingSchemePlaceholder')"
@click="openPackagingSchemeDialog"
@clear="handlePackagingSchemeClear"
/>
</el-form-item>
</el-col>
<el-col v-if="formData.categoryType === 2 || formData.categoryType === 3" :span="12">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogSupplierLabel')" prop="suppliers">
<el-input
:model-value="displaySupplierText"
readonly
clearable
:placeholder="t('FactoryModeling.ProductInformation.dialogSupplierPlaceholder')"
@click="openSupplierDialog"
@clear="handleSupplierClear"
/>
</el-form-item>
</el-col>
<el-col v-if="formData.categoryType === 3" :span="8">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogSparePartLevelLabel')" prop="sparePartLevel">
<el-radio-group v-model="formData.sparePartLevel">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SPARE_PARTS_LEVEL)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="formData.categoryType === 3" :span="8">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogFragileFlagLabel')" prop="fragileFlag">
<el-radio-group v-model="formData.fragileFlag">
<el-radio :label="1">{{ t('common.yes') }}</el-radio>
<el-radio :label="0">{{ t('common.no') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="formData.categoryType === 3" :span="8">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogPurchaseCycleLabel')" prop="purchaseCycle">
<el-input-number v-model="formData.purchaseCycle" :min="0" :precision="0" class="!w-1/1" />
</el-form-item>
</el-col>
<el-col v-if="formData.categoryType === 3" :span="8">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogBrandLabel')" prop="brand">
<el-input v-model="formData.brand" :placeholder="t('FactoryModeling.ProductInformation.dialogBrandPlaceholder')" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogStatusLabel')" prop="status">
<el-radio-group v-model="formData.status">
@ -204,6 +259,99 @@
</el-form>
</template>
</TableSelectDialog>
<Dialog :title="t('FactoryModeling.ProductInformation.dialogPackagingSchemeTitle')" v-model="packagingSchemeDialogVisible" :appendToBody="true" width="1200">
<el-form ref="searchPackagingSchemeFormRef" :model="searchPackagingSchemeParams" :inline="true" class="-mb-15px">
<el-form-item :label="t('ErpStock.PackagingScheme.code')" prop="code">
<el-input v-model="searchPackagingSchemeParams.code" :placeholder="t('ErpStock.PackagingScheme.placeholderCode')" clearable />
</el-form-item>
<el-form-item :label="t('ErpStock.PackagingScheme.name')" prop="name">
<el-input v-model="searchPackagingSchemeParams.name" :placeholder="t('ErpStock.PackagingScheme.placeholderName')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handlePackagingSchemeSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetPackagingSchemeSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
<el-table
ref="packagingSchemeTableRef"
v-loading="packagingSchemeDialogLoading"
:data="packagingSchemeDialogList"
:row-key="(row) => row.id"
:show-overflow-tooltip="true"
:stripe="true"
@selection-change="handlePackagingSchemeSelectionChange"
>
<el-table-column type="selection" width="55" reserve-selection />
<el-table-column :label="t('ErpStock.PackagingScheme.code')" prop="code" minWidth="160" />
<el-table-column :label="t('ErpStock.PackagingScheme.name')" prop="name" minWidth="160" />
<el-table-column :label="t('ErpStock.PackagingScheme.packageQuantity')" prop="packageQuantity" minWidth="120" />
<el-table-column :label="t('ErpStock.PackagingScheme.palletPackageQuantity')" prop="palletPackageQuantity" minWidth="120" />
<el-table-column :label="t('ErpStock.PackagingScheme.palletTotalQuantity')" prop="palletTotalQuantity" minWidth="120" />
<el-table-column :label="t('FactoryModeling.ProductInformation.dialogDefaultStatusLabel')" width="130" align="center">
<template #default="{ row }">
<el-radio v-model="packagingSchemeDefaultId" :label="row.id">&nbsp;</el-radio>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-15px">
<Pagination
v-model:page="packagingSchemeDialogPageNo"
v-model:limit="packagingSchemeDialogPageSize"
:total="packagingSchemeDialogTotal"
@pagination="getPackagingSchemeDialogList"
/>
</div>
<template #footer>
<el-button type="primary" @click="confirmPackagingSchemeSelect">{{ t('common.ok') }}</el-button>
<el-button @click="packagingSchemeDialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
<Dialog :title="t('FactoryModeling.ProductInformation.dialogSupplierTitle')" v-model="supplierDialogVisible" :appendToBody="true" width="1000">
<el-form ref="searchSupplierFormRef" :model="searchSupplierParams" :inline="true" class="-mb-15px">
<el-form-item :label="t('ErpPurchase.Supplier.name')" prop="name">
<el-input v-model="searchSupplierParams.name" :placeholder="t('ErpPurchase.Supplier.placeholderName')" clearable />
</el-form-item>
<el-form-item :label="t('ErpPurchase.Supplier.mobile')" prop="mobile">
<el-input v-model="searchSupplierParams.mobile" :placeholder="t('ErpPurchase.Supplier.placeholderMobile')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSupplierSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetSupplierSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
<el-table
ref="supplierTableRef"
v-loading="supplierDialogLoading"
:data="supplierDialogList"
:row-key="(row) => row.id"
:show-overflow-tooltip="true"
:stripe="true"
@selection-change="handleSupplierSelectionChange"
>
<el-table-column type="selection" width="55" reserve-selection />
<el-table-column :label="t('ErpPurchase.Supplier.name')" prop="name" minWidth="160" />
<el-table-column :label="t('ErpPurchase.Supplier.contact')" prop="contact" minWidth="100" />
<el-table-column :label="t('ErpPurchase.Supplier.mobile')" prop="mobile" minWidth="120" />
<el-table-column :label="t('ErpPurchase.Supplier.telephone')" prop="telephone" minWidth="120" />
<el-table-column :label="t('FactoryModeling.ProductInformation.dialogDefaultStatusLabel')" width="130" align="center">
<template #default="{ row }">
<el-radio v-model="supplierDefaultId" :label="row.id">&nbsp;</el-radio>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-15px">
<Pagination
v-model:page="supplierDialogPageNo"
v-model:limit="supplierDialogPageSize"
:total="supplierDialogTotal"
@pagination="getSupplierDialogList"
/>
</div>
<template #footer>
<el-button type="primary" @click="confirmSupplierSelect">{{ t('common.ok') }}</el-button>
<el-button @click="supplierDialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ProductApi, ProductVO } from '@/api/erp/product/product'
@ -213,6 +361,8 @@ import QrcodeActionCard from '@/components/QrcodeActionCard/index.vue'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
import { DeviceLedgerApi } from '@/api/mes/deviceledger'
import { MoldBrandApi } from '@/api/erp/mold'
import { PackagingSchemeApi } from '@/api/erp/stock/packagingScheme'
import { SupplierApi } from '@/api/erp/purchase/supplier'
import { CommonStatusEnum } from '@/utils/constants'
import { defaultProps, handleTree } from '@/utils/tree'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@ -279,7 +429,13 @@ const formData = ref({
minPrice: undefined,
safetyNumber: undefined,
devices: [] as { id: number; name: string }[],
molds: [] as { id: number; name: string }[]
molds: [] as { id: number; name: string }[],
packagingSchemes: [] as { packagingSchemeId: number | undefined; defaultStatus: number }[],
suppliers: [] as { supplierId: number | undefined; defaultStatus: number }[],
fragileFlag: undefined as number | undefined,
purchaseCycle: undefined as number | undefined,
brand: undefined as string | undefined,
sparePartLevel: undefined as number | undefined
})
const selectedDeviceRows = ref<any[]>([])
const selectedMoldRows = ref<any[]>([])
@ -445,17 +601,252 @@ const validateBarCode = (_rule, value, callback) => {
}
callback()
}
const validatePackagingSchemes = (_rule, value, callback) => {
if (formData.value.categoryType !== 1) {
callback()
return
}
if (!value || !value.length) {
callback(new Error(t('FactoryModeling.ProductInformation.validatorPackagingSchemeRequired')))
return
}
if (value.some((item: any) => !item.packagingSchemeId)) {
callback(new Error(t('FactoryModeling.ProductInformation.validatorPackagingSchemeRequired')))
return
}
callback()
}
const validateSuppliers = (_rule, value, callback) => {
if (formData.value.categoryType !== 2 && formData.value.categoryType !== 3) {
callback()
return
}
if (!value || !value.length) {
callback(new Error(t('FactoryModeling.ProductInformation.validatorSupplierRequired')))
return
}
if (value.some((item: any) => !item.supplierId)) {
callback(new Error(t('FactoryModeling.ProductInformation.validatorSupplierRequired')))
return
}
callback()
}
//
const packagingSchemeDialogVisible = ref(false)
const packagingSchemeDialogLoading = ref(false)
const packagingSchemeDialogList = ref<any[]>([])
const packagingSchemeDialogTotal = ref(0)
const packagingSchemeDialogPageNo = ref(1)
const packagingSchemeDialogPageSize = ref(10)
const packagingSchemeTableRef = ref()
const packagingSchemeSelectedRows = ref<any[]>([])
const packagingSchemeDefaultId = ref<number | undefined>(undefined)
const searchPackagingSchemeParams = reactive({ code: undefined, name: undefined })
const searchPackagingSchemeFormRef = ref()
const getPackagingSchemeDialogList = async () => {
packagingSchemeDialogLoading.value = true
try {
const data = await PackagingSchemeApi.getPackagingSchemePage({
pageNo: packagingSchemeDialogPageNo.value,
pageSize: packagingSchemeDialogPageSize.value,
status: 0,
...searchPackagingSchemeParams
})
packagingSchemeDialogList.value = data.list || []
packagingSchemeDialogTotal.value = data.total || 0
} finally {
packagingSchemeDialogLoading.value = false
}
}
const handlePackagingSchemeSelectionChange = (selection: any[]) => {
packagingSchemeSelectedRows.value = selection
}
const displayPackagingSchemeText = computed(() => {
if (!formData.value.packagingSchemes?.length) return ''
return formData.value.packagingSchemes
.map((item) => {
const scheme = packagingSchemeList.value.find((s) => s.id === item.packagingSchemeId)
return scheme?.name || ''
})
.filter(Boolean)
.join('、')
})
const openPackagingSchemeDialog = async () => {
packagingSchemeDialogVisible.value = true
packagingSchemeDialogPageNo.value = 1
searchPackagingSchemeParams.code = undefined
searchPackagingSchemeParams.name = undefined
packagingSchemeSelectedRows.value = []
// ID
const defaultItem = formData.value.packagingSchemes?.find((item) => item.defaultStatus === 1)
packagingSchemeDefaultId.value = defaultItem?.packagingSchemeId ?? undefined
await getPackagingSchemeDialogList()
//
await nextTick()
if (formData.value.packagingSchemes?.length && packagingSchemeTableRef.value) {
packagingSchemeTableRef.value.clearSelection()
const selectedIds = formData.value.packagingSchemes.map((item) => item.packagingSchemeId)
packagingSchemeDialogList.value.forEach((row) => {
if (selectedIds.includes(row.id)) {
packagingSchemeTableRef.value?.toggleRowSelection(row, true)
}
})
}
}
const confirmPackagingSchemeSelect = () => {
if (!packagingSchemeSelectedRows.value.length) {
message.warning(t('FactoryModeling.ProductInformation.validatorPackagingSchemeRequired'))
return
}
formData.value.packagingSchemes = packagingSchemeSelectedRows.value.map((row) => ({
packagingSchemeId: row.id,
defaultStatus: row.id === packagingSchemeDefaultId.value ? 1 : 0
}))
//
if (!packagingSchemeDefaultId.value && formData.value.packagingSchemes.length > 0) {
formData.value.packagingSchemes[0].defaultStatus = 1
}
//
packagingSchemeSelectedRows.value.forEach((row) => {
if (!packagingSchemeList.value.find((s) => s.id === row.id)) {
packagingSchemeList.value.push(row)
}
})
packagingSchemeDialogVisible.value = false
}
const handlePackagingSchemeClear = () => {
formData.value.packagingSchemes = []
}
const handlePackagingSchemeSearch = () => {
packagingSchemeDialogPageNo.value = 1
getPackagingSchemeDialogList()
}
const resetPackagingSchemeSearch = () => {
searchPackagingSchemeFormRef.value?.resetFields()
handlePackagingSchemeSearch()
}
//
const supplierDialogVisible = ref(false)
const supplierDialogLoading = ref(false)
const supplierDialogList = ref<any[]>([])
const supplierDialogTotal = ref(0)
const supplierDialogPageNo = ref(1)
const supplierDialogPageSize = ref(10)
const supplierTableRef = ref()
const supplierSelectedRows = ref<any[]>([])
const supplierDefaultId = ref<number | undefined>(undefined)
const searchSupplierParams = reactive({ name: undefined, mobile: undefined })
const searchSupplierFormRef = ref()
const getSupplierDialogList = async () => {
supplierDialogLoading.value = true
try {
const data = await SupplierApi.getSupplierPage({
pageNo: supplierDialogPageNo.value,
pageSize: supplierDialogPageSize.value,
status: 0,
...searchSupplierParams
})
supplierDialogList.value = data.list || []
supplierDialogTotal.value = data.total || 0
} finally {
supplierDialogLoading.value = false
}
}
const handleSupplierSelectionChange = (selection: any[]) => {
supplierSelectedRows.value = selection
}
const displaySupplierText = computed(() => {
if (!formData.value.suppliers?.length) return ''
return formData.value.suppliers
.map((item) => {
const supplier = supplierList.value.find((s) => s.id === item.supplierId)
return supplier?.name || ''
})
.filter(Boolean)
.join('、')
})
const openSupplierDialog = async () => {
supplierDialogVisible.value = true
supplierDialogPageNo.value = 1
searchSupplierParams.name = undefined
searchSupplierParams.mobile = undefined
supplierSelectedRows.value = []
const defaultItem = formData.value.suppliers?.find((item) => item.defaultStatus === 1)
supplierDefaultId.value = defaultItem?.supplierId ?? undefined
await getSupplierDialogList()
//
await nextTick()
if (formData.value.suppliers?.length && supplierTableRef.value) {
supplierTableRef.value.clearSelection()
const selectedIds = formData.value.suppliers.map((item) => item.supplierId)
supplierDialogList.value.forEach((row) => {
if (selectedIds.includes(row.id)) {
supplierTableRef.value?.toggleRowSelection(row, true)
}
})
}
}
const confirmSupplierSelect = () => {
if (!supplierSelectedRows.value.length) {
message.warning(t('FactoryModeling.ProductInformation.validatorSupplierRequired'))
return
}
formData.value.suppliers = supplierSelectedRows.value.map((row) => ({
supplierId: row.id,
defaultStatus: row.id === supplierDefaultId.value ? 1 : 0
}))
if (!supplierDefaultId.value && formData.value.suppliers.length > 0) {
formData.value.suppliers[0].defaultStatus = 1
}
supplierSelectedRows.value.forEach((row) => {
if (!supplierList.value.find((s) => s.id === row.id)) {
supplierList.value.push(row)
}
})
supplierDialogVisible.value = false
}
const handleSupplierClear = () => {
formData.value.suppliers = []
}
const handleSupplierSearch = () => {
supplierDialogPageNo.value = 1
getSupplierDialogList()
}
const resetSupplierSearch = () => {
searchSupplierFormRef.value?.resetFields()
handleSupplierSearch()
}
const formRules = reactive({
categoryType: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorCategoryTypeRequired'), trigger: 'change' }],
name: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorNameRequired'), trigger: 'blur' }],
barCode: [{ validator: validateBarCode, trigger: ['blur', 'change'] }],
categoryId: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorCategoryRequired'), trigger: 'blur' }],
unitId: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorUnitRequired'), trigger: 'blur' }],
status: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorStatusRequired'), trigger: 'blur' }]
status: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorStatusRequired'), trigger: 'blur' }],
packagingSchemes: [{ validator: validatePackagingSchemes, trigger: 'change' }],
suppliers: [{ validator: validateSuppliers, trigger: 'change' }]
})
const formRef = ref()
const categoryList = ref<ProductCategoryVO[]>([])
const unitList = ref<ProductUnitVO[]>([])
const packagingSchemeList = ref<any[]>([])
const supplierList = ref<any[]>([])
let openRequestId = 0
@ -494,8 +885,11 @@ const open = async (type: string, id?: number) => {
...formData.value,
...productData,
templateJson: parsedTemplateJson,
sparePartLevel: productData.sparePartLevel != null ? Number(productData.sparePartLevel) : undefined,
devices,
molds
molds,
packagingSchemes: (productData as any).packagingSchemes || [],
suppliers: (productData as any).suppliers || []
}
selectedDeviceRows.value = toDeviceRows(devices)
selectedMoldRows.value = toMoldRows(molds)
@ -506,6 +900,12 @@ const open = async (type: string, id?: number) => {
const categoryData = await ProductCategoryApi.getProductCategorySimpleList(formData.value.categoryType)
categoryList.value = handleTree(categoryData, 'id', 'parentId')
unitList.value = await ProductUnitApi.getProductUnitSimpleList()
//
const schemeData = await PackagingSchemeApi.getPackagingSchemePage({ pageNo: 1, pageSize: 100, status: 0 })
packagingSchemeList.value = schemeData.list || []
//
const supplierData = await SupplierApi.getSupplierPage({ pageNo: 1, pageSize: 100, status: 0 })
supplierList.value = supplierData.list || []
}
defineExpose({ open })
@ -530,6 +930,12 @@ const handleCodeAutoChange = (value: boolean) => {
const handleCategoryTypeChange = async () => {
formData.value.categoryId = undefined
formData.value.packagingSchemes = []
formData.value.suppliers = []
formData.value.fragileFlag = undefined
formData.value.purchaseCycle = undefined
formData.value.brand = undefined
formData.value.sparePartLevel = undefined
const categoryData = await ProductCategoryApi.getProductCategorySimpleList(formData.value.categoryType)
categoryList.value = handleTree(categoryData, 'id', 'parentId')
}
@ -611,7 +1017,13 @@ const resetForm = () => {
salePrice: undefined,
minPrice: undefined,
devices: [],
molds: []
molds: [],
packagingSchemes: [],
suppliers: [],
fragileFlag: undefined,
purchaseCycle: undefined,
brand: undefined,
sparePartLevel: undefined
}
selectedDeviceRows.value = []
selectedMoldRows.value = []

Loading…
Cancel
Save