feat(mes): 添加物料产品状态更新功能,优化物料分类选择器

pull/871/MERGE
YunaiV 2026-03-28 10:08:17 +08:00
parent db6a9aeabe
commit b6bc58d104
6 changed files with 74 additions and 36 deletions

View File

@ -1,8 +1,10 @@
<!-- MES 物料批次属性配置 -->
<template>
<el-form ref="formRef" :model="formData" v-loading="loading">
<div class="flex justify-end mb-10px">
<el-button type="primary" size="small" @click="handleSave" :loading="loading">保存批次属性</el-button>
<el-form ref="formRef" :model="formData" v-loading="loading" :disabled="isReadOnly">
<div v-if="!isReadOnly" class="flex justify-end mb-10px">
<el-button type="primary" size="small" @click="handleSave" :loading="loading">
保存批次属性
</el-button>
</div>
<div class="grid grid-cols-5 gap-x-20px">
<!-- 通用属性ITEM PRODUCT 都可见 -->
@ -39,14 +41,14 @@ import { MesItemOrProductEnum } from '@/views/mes/utils/constants'
defineOptions({ name: 'MdItemBatchConfigForm' })
const props = defineProps<{
itemId: number //
itemOrProduct: string // /'ITEM' | 'PRODUCT'
itemId: number
itemOrProduct: string
formType?: string
}>()
const message = useMessage() //
const loading = ref(false) //
const formRef = ref() // Ref
/** 表单数据 */
const formData = ref<MdItemBatchConfigVO>({
itemId: props.itemId,
produceDateFlag: false,
@ -63,7 +65,8 @@ const formData = ref<MdItemBatchConfigVO>({
moldFlag: false,
lotNumberFlag: false,
qualityStatusFlag: false
})
}) //
const isReadOnly = computed(() => props.formType === 'detail') //
/** 加载已有配置 */
const loadData = async () => {

View File

@ -7,13 +7,14 @@
:rules="formRules"
label-width="120px"
v-loading="formLoading"
:disabled="isDetail"
>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="物料编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入物料编码">
<template #append>
<el-button @click="generateCode" :disabled="formType === 'update'">
<el-button @click="generateCode" :disabled="isDetail || formType === 'update'">
生成
</el-button>
</template>
@ -97,27 +98,33 @@
</el-col>
</el-row>
</el-form>
<!-- 底部 Tab修改时展示 -->
<el-tabs v-model="activeTab" v-if="formType === 'update' && formData.id">
<!-- 底部 Tab修改/详情时展示 -->
<el-tabs v-model="activeTab" v-if="formType !== 'create' && formData.id">
<el-tab-pane label="BOM 组成" name="bom" lazy>
<MdProductBomForm :itemId="formData.id!" />
<MdProductBomForm :itemId="formData.id!" :formType="formType" />
</el-tab-pane>
<el-tab-pane label="批次属性" name="batch" lazy v-if="formData.batchFlag">
<MdItemBatchConfigForm :itemId="formData.id!" :itemOrProduct="currentItemOrProduct" />
<MdItemBatchConfigForm
:itemId="formData.id!"
:itemOrProduct="currentItemOrProduct"
:formType="formType"
/>
</el-tab-pane>
<!-- TODO @芋艿对齐替代品目前没这个可忽略 -->
<el-tab-pane label="替代品" name="substitute" lazy>
<el-empty description="替代品(待实现)" />
</el-tab-pane>
<el-tab-pane label="SIP" name="sip" lazy>
<MdProductSipForm :itemId="formData.id!" />
<MdProductSipForm :itemId="formData.id!" :formType="formType" />
</el-tab-pane>
<el-tab-pane label="SOP" name="sop" lazy>
<MdProductSopForm :itemId="formData.id!" />
<MdProductSopForm :itemId="formData.id!" :formType="formType" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button v-if="!isDetail" @click="submitForm" type="primary" :disabled="formLoading">
</el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
@ -148,7 +155,8 @@ const dialogTitle = computed(() => {
return '查看物料/产品'
}) //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formType = ref('') // create - update - detail -
const isDetail = computed(() => formType.value === 'detail') //
const activeTab = ref('bom') // Tab
const formData = ref({
id: undefined,

View File

@ -1,7 +1,14 @@
<!-- MES 产品BOM 列表 -->
<template>
<div>
<el-button type="primary" plain size="small" @click="handleAdd" class="mb-10px">
<el-button
v-if="!isReadOnly"
type="primary"
plain
size="small"
@click="handleAdd"
class="mb-10px"
>
<Icon icon="ep:plus" class="mr-5px" /> 添加 BOM 物料
</el-button>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border>
@ -16,7 +23,7 @@
</el-table-column>
<el-table-column label="用量比例" align="center" prop="quantity" width="100" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" width="120">
<el-table-column v-if="!isReadOnly" label="操作" align="center" width="120">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)"></el-button>
@ -79,13 +86,15 @@ import MdItemSelectDialog from '@/views/mes/md/item/components/MdItemSelectDialo
defineOptions({ name: 'MdProductBomForm' })
const props = defineProps<{
itemId: number //
itemId: number
formType?: string
}>()
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref<MdProductBomVO[]>([]) // BOM
const isReadOnly = computed(() => props.formType === 'detail') //
/** 加载 BOM 列表 */
const getList = async () => {
@ -124,7 +133,7 @@ const handleItemSelected = async (rows: MdItemVO[]) => {
// ==================== / BOM ====================
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formType = ref('') // create - update -
const dialogFormType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
@ -146,7 +155,7 @@ const formRules = reactive({
const openForm = (type: string, row?: MdProductBomVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
dialogFormType.value = type
resetForm()
//
if (type === 'update' && row) {
@ -190,7 +199,7 @@ const submitForm = async () => {
formLoading.value = true
try {
const data = formData.value as unknown as MdProductBomVO
if (formType.value === 'create') {
if (dialogFormType.value === 'create') {
await MdProductBomApi.createProductBom(data)
message.success(t('common.createSuccess'))
} else {

View File

@ -1,7 +1,14 @@
<!-- MES 产品SIP 列表 -->
<template>
<div>
<el-button type="primary" plain size="small" @click="openForm('create')" class="mb-10px">
<el-button
v-if="!isReadOnly"
type="primary"
plain
size="small"
@click="openForm('create')"
class="mb-10px"
>
<Icon icon="ep:plus" class="mr-5px" /> 添加 SIP
</el-button>
<!-- SIP 卡片列表 -->
@ -25,7 +32,7 @@
{{ item.description }}
</div>
<!-- 操作按钮 -->
<div class="flex justify-end mt-8px">
<div v-if="!isReadOnly" class="flex justify-end mt-8px">
<el-button link type="primary" size="small" @click="openForm('update', item)">
编辑
</el-button>
@ -97,13 +104,15 @@ import { createImageViewer } from '@/components/ImageViewer'
defineOptions({ name: 'MdProductSipForm' })
const props = defineProps<{
itemId: number //
itemId: number
formType?: string
}>()
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref<MdProductSipVO[]>([]) // SIP
const isReadOnly = computed(() => props.formType === 'detail') //
/** 加载 SIP 列表 */
const getList = async () => {
@ -128,7 +137,7 @@ const handlePreview = (url?: string) => {
// ==================== / ====================
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formType = ref('') // create - update -
const dialogFormType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
@ -150,7 +159,7 @@ const formRules = reactive({
const openForm = (type: string, row?: MdProductSipVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
dialogFormType.value = type
resetForm()
//
if (type === 'update' && row) {
@ -192,7 +201,7 @@ const submitForm = async () => {
formLoading.value = true
try {
const data = formData.value as unknown as MdProductSipVO
if (formType.value === 'create') {
if (dialogFormType.value === 'create') {
await MdProductSipApi.createProductSip(data)
message.success(t('common.createSuccess'))
} else {

View File

@ -1,7 +1,14 @@
<!-- MES 产品SOP 列表 -->
<template>
<div>
<el-button type="primary" plain size="small" @click="openForm('create')" class="mb-10px">
<el-button
v-if="!isReadOnly"
type="primary"
plain
size="small"
@click="openForm('create')"
class="mb-10px"
>
<Icon icon="ep:plus" class="mr-5px" /> 添加 SOP
</el-button>
<!-- SOP 卡片列表 -->
@ -25,7 +32,7 @@
{{ item.description }}
</div>
<!-- 操作按钮 -->
<div class="flex justify-end mt-8px">
<div v-if="!isReadOnly" class="flex justify-end mt-8px">
<el-button link type="primary" size="small" @click="openForm('update', item)">
编辑
</el-button>
@ -97,9 +104,12 @@ import { createImageViewer } from '@/components/ImageViewer'
defineOptions({ name: 'MdProductSopForm' })
const props = defineProps<{
itemId: number //
itemId: number
formType?: string
}>()
const isReadOnly = computed(() => props.formType === 'detail') //
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
@ -126,7 +136,7 @@ const handlePreview = (url?: string) => {
// ==================== / ====================
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formType = ref('') // create - update -
const dialogFormType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
@ -148,7 +158,7 @@ const formRules = reactive({
const openForm = (type: string, row?: MdProductSopVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
dialogFormType.value = type
resetForm()
//
if (type === 'update' && row) {
@ -190,7 +200,7 @@ const submitForm = async () => {
formLoading.value = true
try {
const data = formData.value as unknown as MdProductSopVO
if (formType.value === 'create') {
if (dialogFormType.value === 'create') {
await MdProductSopApi.createProductSop(data)
message.success(t('common.createSuccess'))
} else {

View File

@ -68,8 +68,7 @@ const handleFilter = (query: string) => {
const keyword = query.toLowerCase()
filteredList.value = allList.value.filter(
(item) =>
item.name?.toLowerCase().includes(keyword) ||
item.code?.toLowerCase().includes(keyword)
item.name?.toLowerCase().includes(keyword) || item.code?.toLowerCase().includes(keyword)
)
}