refactor(mes): 重构产品 BOM/SIP/SOP 表单,统一代码风格并修复 TODO

1. 统一表单方法风格,对齐 WorkstationWorkerPanel 的写法:
   - 弹窗变量统一命名为 dialogVisible/dialogTitle
   - dialogTitle 使用 t('action.' + type) 国际化
   - submitForm 添加 formRef 校验守卫,使用 t() 国际化提示
   - create/update 判断顺序统一为先 create 后 update
2. SIP/SOP 的 orderNumber 字段全局重命名为 sort
3. BOM 物料选择简化为无论选择几条都直接批量创建
4. 修复 formData 展开赋值的 TS 类型错误,改为逐字段赋值
5. 清理所有 TODO @AI 注释
pull/871/MERGE
YunaiV 2026-02-16 17:46:33 +08:00
parent b0548b329d
commit cb56866deb
6 changed files with 465 additions and 224 deletions

View File

@ -4,7 +4,7 @@ import request from '@/config/axios'
export interface MdProductSipVO { export interface MdProductSipVO {
id?: number // SIP 编号 id?: number // SIP 编号
itemId: number // 物料产品 ID itemId: number // 物料产品 ID
orderNumber: number // 排列顺序 sort: number // 排列顺序
processId?: number // 工序 ID processId?: number // 工序 ID
title: string // 标题 title: string // 标题
description?: string // 详细描述 description?: string // 详细描述

View File

@ -4,7 +4,7 @@ import request from '@/config/axios'
export interface MdProductSopVO { export interface MdProductSopVO {
id?: number // SOP 编号 id?: number // SOP 编号
itemId: number // 物料产品 ID itemId: number // 物料产品 ID
orderNumber: number // 排列顺序 sort: number // 排列顺序
processId?: number // 工序 ID processId?: number // 工序 ID
title: string // 标题 title: string // 标题
description?: string // 详细描述 description?: string // 详细描述

View File

@ -0,0 +1,197 @@
<!-- MES 物料产品 弹窗选择器 -->
<template>
<Dialog title="物料产品选择" v-model="dialogVisible" width="80%">
<el-row :gutter="20">
<!-- 左侧分类树 -->
<el-col :span="5">
<el-input
v-model="filterText"
placeholder="搜索分类"
clearable
class="mb-12px"
:prefix-icon="iconSearch"
/>
<el-tree
ref="treeRef"
:data="itemTypeTree"
:props="treeProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
default-expand-all
highlight-current
@node-click="handleNodeClick"
/>
</el-col>
<!-- 右侧物料表格 -->
<el-col :span="19">
<!-- 搜索表单 -->
<el-form :inline="true" :model="queryParams" class="mb-10px" label-width="80px">
<el-form-item label="物料编码">
<el-input
v-model="queryParams.code"
placeholder="请输入物料编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="物料名称">
<el-input
v-model="queryParams.name"
placeholder="请输入物料名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="物料编码" align="center" prop="code" width="120" />
<el-table-column label="物料名称" align="center" prop="name" min-width="120" />
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="单位" align="center" prop="unitMeasureName" width="80" />
<el-table-column label="物料/产品" align="center" prop="itemOrProduct" width="100">
<template #default="scope">
{{ getItemOrProductLabel(scope.row.itemOrProduct) }}
</template>
</el-table-column>
<el-table-column label="所属分类" align="center" prop="itemTypeName" width="120" />
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
<template #footer>
<el-button type="primary" @click="confirmSelect"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { MdItemApi, MdItemVO } from '@/api/mes/md/item'
import { MdItemTypeApi, MdItemTypeVO } from '@/api/mes/md/item/type'
import { handleTree } from '@/utils/tree'
import { getItemOrProductLabel } from '@/views/mes/utils/constants'
import { Search as iconSearch } from '@element-plus/icons-vue'
defineOptions({ name: 'ItemProductSelect' })
const message = useMessage() //
const emit = defineEmits<{
selected: [rows: MdItemVO[]] //
}>()
const dialogVisible = ref(false) //
const loading = ref(false) //
const list = ref<MdItemVO[]>([]) //
const total = ref(0) //
const selectedRows = ref<MdItemVO[]>([]) //
// ==================== ====================
const treeRef = ref() // Ref
const filterText = ref('') //
const itemTypeTree = ref<MdItemTypeVO[]>([]) //
const treeProps = { children: 'children', label: 'name' } //
/** 过滤树节点 */
const filterNode = (value: string, data: MdItemTypeVO) => {
if (!value) return true
return data.name?.includes(value)
}
/** 监听筛选文本变化 */
watch(filterText, (val) => {
treeRef.value?.filter(val)
})
/** 点击树节点 */
const handleNodeClick = (data: MdItemTypeVO) => {
queryParams.itemTypeId = data.id
handleQuery()
}
// ==================== ====================
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
code: undefined as string | undefined,
name: undefined as string | undefined,
itemTypeId: undefined as number | undefined
})
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await MdItemApi.getItemPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置 */
const resetQuery = () => {
queryParams.code = undefined
queryParams.name = undefined
queryParams.itemTypeId = undefined
handleQuery()
}
/** 选中变化 */
const handleSelectionChange = (rows: MdItemVO[]) => {
selectedRows.value = rows
}
/** 确认选择 */
const confirmSelect = () => {
if (selectedRows.value.length === 0) {
message.warning('请至少选择一条数据')
return
}
emit('selected', selectedRows.value)
dialogVisible.value = false
}
// ==================== ====================
/** 打开弹窗 */
const open = async () => {
dialogVisible.value = true
selectedRows.value = []
//
const typeList = await MdItemTypeApi.getItemTypeSimpleList()
itemTypeTree.value = handleTree(typeList)
//
await getList()
}
defineExpose({ open }) // open
</script>

View File

@ -1,7 +1,7 @@
<!-- MES 产品BOM 列表 --> <!-- MES 产品BOM 列表 -->
<template> <template>
<div> <div>
<el-button type="primary" plain size="small" @click="openAddForm" class="mb-10px"> <el-button type="primary" plain size="small" @click="handleAdd" class="mb-10px">
<Icon icon="ep:plus" class="mr-5px" /> 添加 BOM 物料 <Icon icon="ep:plus" class="mr-5px" /> 添加 BOM 物料
</el-button> </el-button>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border>
@ -18,81 +18,63 @@
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" width="120"> <el-table-column label="操作" align="center" width="120">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="openEditForm(scope.row)"></el-button> <el-button link type="primary" @click="openForm('update', scope.row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)"></el-button> <el-button link type="danger" @click="handleDelete(scope.row.id)"></el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 添加 BOM 物料弹窗 --> <!-- 添加/编辑 BOM 弹窗 -->
<!-- TODO @AI这里实现不太对调整成物料产品选择这个组件我们需要在 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/md/components 里搞一个然后这里使用 --> <Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
<Dialog title="添加 BOM 物料" v-model="addDialogVisible" width="500px"> <el-form
<el-form ref="addFormRef" :model="addFormData" :rules="addFormRules" label-width="100px"> ref="formRef"
<el-form-item label="BOM 物料" prop="bomItemId"> :model="formData"
<el-select :rules="formRules"
v-model="addFormData.bomItemId" label-width="120px"
filterable v-loading="formLoading"
remote >
:remote-method="searchItems" <el-form-item label="BOM 物料编码" prop="bomItemCode">
:loading="itemSearchLoading" <el-input v-model="formData.bomItemCode" readonly />
placeholder="请搜索物料编码/名称" </el-form-item>
class="w-1/1" <el-form-item label="BOM 物料名称" prop="bomItemName">
> <el-input v-model="formData.bomItemName" readonly />
<el-option </el-form-item>
v-for="item in itemOptions" <el-form-item label="规格型号" prop="bomItemSpecification">
:key="item.id" <el-input v-model="formData.bomItemSpecification" readonly />
:label="item.code + ' - ' + item.name" </el-form-item>
:value="item.id" <el-form-item label="单位" prop="unitMeasureName">
/> <el-input v-model="formData.unitMeasureName" readonly />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="用量比例" prop="quantity"> <el-form-item label="用量比例" prop="quantity">
<el-input-number <el-input-number
v-model="addFormData.quantity" v-model="formData.quantity"
:min="0" :min="0"
:precision="4" :precision="4"
:step="0.1"
controls-position="right" controls-position="right"
class="!w-1/1" class="!w-1/1"
/> />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="addFormData.remark" type="textarea" placeholder="请输入备注" /> <el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitAddForm" type="primary"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="addDialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
<!-- 编辑 BOM 弹窗 --> <!-- 物料选择弹窗 -->
<Dialog title="编辑 BOM 物料" v-model="editDialogVisible" width="500px"> <ItemProductSelect ref="itemSelectRef" @selected="handleItemSelected" />
<el-form ref="editFormRef" :model="editFormData" :rules="editFormRules" label-width="100px">
<el-form-item label="用量比例" prop="quantity">
<el-input-number
v-model="editFormData.quantity"
:min="0"
:precision="4"
controls-position="right"
class="!w-1/1"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="editFormData.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitEditForm" type="primary"> </el-button>
<el-button @click="editDialogVisible = false"> </el-button>
</template>
</Dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { MdProductBomApi, MdProductBomVO } from '@/api/mes/md/item/productBom' import { MdProductBomApi, MdProductBomVO } from '@/api/mes/md/item/productBom'
import { MdItemApi } from '@/api/mes/md/item' import { MdItemVO } from '@/api/mes/md/item'
import { getItemOrProductLabel } from '@/views/mes/utils/constants' import { getItemOrProductLabel } from '@/views/mes/utils/constants'
import ItemProductSelect from '@/views/mes/md/components/ItemProductSelect.vue'
defineOptions({ name: 'MdProductBomForm' }) defineOptions({ name: 'MdProductBomForm' })
@ -100,7 +82,8 @@ const props = defineProps<{
itemId: number // itemId: number //
}>() }>()
const message = useMessage() const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<MdProductBomVO[]>([]) // BOM const list = ref<MdProductBomVO[]>([]) // BOM
@ -114,97 +97,112 @@ const getList = async () => {
} }
} }
// ==================== ==================== // ==================== ====================
const itemSearchLoading = ref(false) // const itemSelectRef = ref() // Ref
const itemOptions = ref<any[]>([]) //
/** 远程搜索物料 */ /** 点击添加按钮 → 打开物料选择弹窗 */
const searchItems = async (query: string) => { const handleAdd = () => {
if (!query) { itemSelectRef.value.open()
itemOptions.value = [] }
/** 物料选中回调:直接批量创建 BOM默认用量比例为 1 */
const handleItemSelected = async (rows: MdItemVO[]) => {
if (!rows || rows.length === 0) {
return return
} }
itemSearchLoading.value = true for (const item of rows) {
try { await MdProductBomApi.createProductBom({
const data = await MdItemApi.getItemPage({ pageNo: 1, pageSize: 20, code: query }) itemId: props.itemId,
itemOptions.value = data.list || [] bomItemId: item.id,
} finally { quantity: 1
itemSearchLoading.value = false } as unknown as MdProductBomVO)
} }
} message.success(t('common.createSuccess'))
// ==================== BOM ====================
const addDialogVisible = ref(false) //
const addFormRef = ref() // Ref
/** 添加表单数据 */
const addFormData = ref({
itemId: undefined as number | undefined,
bomItemId: undefined as number | undefined,
quantity: 1,
remark: undefined as string | undefined
})
const addFormRules = reactive({
bomItemId: [{ required: true, message: 'BOM 物料不能为空', trigger: 'change' }],
quantity: [{ required: true, message: '用量比例不能为空', trigger: 'blur' }]
})
/** 打开添加弹窗 */
const openAddForm = () => {
addDialogVisible.value = true
addFormData.value = {
itemId: props.itemId,
bomItemId: undefined,
quantity: 1,
remark: undefined
}
itemOptions.value = []
addFormRef.value?.resetFields()
}
/** 提交添加表单 */
const submitAddForm = async () => {
await addFormRef.value.validate()
await MdProductBomApi.createProductBom(addFormData.value as unknown as MdProductBomVO)
message.success('添加成功')
addDialogVisible.value = false
await getList() await getList()
} }
// ==================== BOM ==================== // ==================== / BOM ====================
const editDialogVisible = ref(false) // const dialogVisible = ref(false) //
const editFormRef = ref() // Ref const dialogTitle = ref('') //
/** 编辑表单数据 */ const formType = ref('') // create - update -
const editFormData = ref({ const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
id: undefined as number | undefined, id: undefined as number | undefined,
itemId: undefined as number | undefined, itemId: undefined as number | undefined,
bomItemId: undefined as number | undefined, bomItemId: undefined as number | undefined,
bomItemCode: undefined as string | undefined, //
bomItemName: undefined as string | undefined, //
bomItemSpecification: undefined as string | undefined, //
unitMeasureName: undefined as string | undefined, //
quantity: 1, quantity: 1,
remark: undefined as string | undefined remark: undefined as string | undefined
}) }) //
const editFormRules = reactive({ const formRules = reactive({
quantity: [{ required: true, message: '用量比例不能为空', trigger: 'blur' }] quantity: [{ required: true, message: '用量比例不能为空', trigger: 'blur' }]
}) }) //
/** 打开编辑弹窗 */ /** 打开表单弹窗 */
const openEditForm = (row: MdProductBomVO) => { const openForm = (type: string, row?: MdProductBomVO) => {
editDialogVisible.value = true dialogVisible.value = true
editFormData.value = { dialogTitle.value = t('action.' + type)
id: row.id, formType.value = type
itemId: row.itemId, resetForm()
bomItemId: row.bomItemId, //
quantity: row.quantity, if (type === 'update' && row) {
remark: row.remark formData.value = {
id: row.id,
itemId: row.itemId,
bomItemId: row.bomItemId,
bomItemCode: row.bomItemCode,
bomItemName: row.bomItemName,
bomItemSpecification: row.bomItemSpecification,
unitMeasureName: row.unitMeasureName,
quantity: row.quantity,
remark: row.remark
}
} }
editFormRef.value?.resetFields()
} }
/** 提交编辑表单 */ /** 重置表单 */
const submitEditForm = async () => { const resetForm = () => {
await editFormRef.value.validate() formData.value = {
await MdProductBomApi.updateProductBom(editFormData.value as unknown as MdProductBomVO) id: undefined,
message.success('编辑成功') itemId: props.itemId,
editDialogVisible.value = false bomItemId: undefined,
await getList() bomItemCode: undefined,
bomItemName: undefined,
bomItemSpecification: undefined,
unitMeasureName: undefined,
quantity: 1,
remark: undefined
}
formRef.value?.resetFields()
}
/** 提交表单 */
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formData.value as unknown as MdProductBomVO
if (formType.value === 'create') {
await MdProductBomApi.createProductBom(data)
message.success(t('common.createSuccess'))
} else {
await MdProductBomApi.updateProductBom(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
await getList()
} finally {
formLoading.value = false
}
} }
// ==================== ==================== // ==================== ====================

View File

@ -1,7 +1,7 @@
<!-- MES 产品SIP 列表 --> <!-- MES 产品SIP 列表 -->
<template> <template>
<div> <div>
<el-button type="primary" plain size="small" @click="openForm(undefined)" class="mb-10px"> <el-button type="primary" plain size="small" @click="openForm('create')" class="mb-10px">
<Icon icon="ep:plus" class="mr-5px" /> 添加 SIP <Icon icon="ep:plus" class="mr-5px" /> 添加 SIP
</el-button> </el-button>
<!-- SIP 卡片列表 --> <!-- SIP 卡片列表 -->
@ -21,18 +21,14 @@
<!-- 信息区域 --> <!-- 信息区域 -->
<div class="p-10px"> <div class="p-10px">
<div class="font-bold text-14px mb-4px truncate">{{ item.title }}</div> <div class="font-bold text-14px mb-4px truncate">{{ item.title }}</div>
<!-- TODO @AI是不是改成 sort 字段保持全局统一如果别的地方也有 orderNumber也调整下 -->
<!-- TODO @AI下面的序号工序都不展示 -->
<div class="text-12px color-gray mb-4px">序号{{ item.orderNumber }}</div>
<div v-if="item.processName" class="text-12px color-gray mb-4px">
工序{{ item.processName }}
</div>
<div v-if="item.description" class="text-12px color-gray truncate"> <div v-if="item.description" class="text-12px color-gray truncate">
{{ item.description }} {{ item.description }}
</div> </div>
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="flex justify-end mt-8px"> <div class="flex justify-end mt-8px">
<el-button link type="primary" size="small" @click="openForm(item)"></el-button> <el-button link type="primary" size="small" @click="openForm('update', item)">
编辑
</el-button>
<el-button link type="danger" size="small" @click="handleDelete(item.id!)"> <el-button link type="danger" size="small" @click="handleDelete(item.id!)">
删除 删除
</el-button> </el-button>
@ -47,22 +43,26 @@
</el-row> </el-row>
<!-- 新增/编辑弹窗 --> <!-- 新增/编辑弹窗 -->
<Dialog :title="formDialogTitle" v-model="formDialogVisible" width="500px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px"> <el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="标题" prop="title"> <el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入标题" /> <el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item> </el-form-item>
<!-- TODO @AI展示顺序 --> <el-form-item label="展示顺序" prop="sort">
<el-form-item label="排列顺序" prop="orderNumber">
<el-input-number <el-input-number
v-model="formData.orderNumber" v-model="formData.sort"
:min="0" :min="0"
controls-position="right" controls-position="right"
class="!w-1/1" class="!w-1/1"
/> />
</el-form-item> </el-form-item>
<!-- TODO @AI内容说明 --> <el-form-item label="内容说明" prop="description">
<el-form-item label="详细描述" prop="description">
<el-input <el-input
v-model="formData.description" v-model="formData.description"
type="textarea" type="textarea"
@ -70,8 +70,10 @@
placeholder="请输入详细描述" placeholder="请输入详细描述"
/> />
</el-form-item> </el-form-item>
<!-- TODO @芋艿工序选择等工序pro_process模块实现后对接下拉选择 --> <!-- TODO @芋艿所属工序等工序模块完成后改为下拉选择 -->
<!-- TODO @AI先至少有个 input 手动填写所属工序 --> <el-form-item label="所属工序" prop="processId">
<el-input v-model="formData.processId" placeholder="请输入工序编号" />
</el-form-item>
<el-form-item label="图片" prop="url"> <el-form-item label="图片" prop="url">
<UploadImg v-model="formData.url" :limit="1" :is-show-tip="false" /> <UploadImg v-model="formData.url" :limit="1" :is-show-tip="false" />
</el-form-item> </el-form-item>
@ -80,8 +82,8 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="formDialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</div> </div>
@ -98,7 +100,8 @@ const props = defineProps<{
itemId: number // itemId: number //
}>() }>()
const message = useMessage() const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<MdProductSipVO[]>([]) // SIP const list = ref<MdProductSipVO[]>([]) // SIP
@ -123,13 +126,15 @@ const handlePreview = (url?: string) => {
} }
// ==================== / ==================== // ==================== / ====================
const formDialogVisible = ref(false) // const dialogVisible = ref(false) //
const formDialogTitle = ref('') // const dialogTitle = ref('') //
const formType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref const formRef = ref() // Ref
const formData = ref({ const formData = ref({
id: undefined as number | undefined, id: undefined as number | undefined,
itemId: undefined as number | undefined, itemId: undefined as number | undefined,
orderNumber: 0, sort: 0,
processId: undefined as number | undefined, processId: undefined as number | undefined,
title: undefined as string | undefined, title: undefined as string | undefined,
description: undefined as string | undefined, description: undefined as string | undefined,
@ -138,52 +143,68 @@ const formData = ref({
}) // }) //
const formRules = reactive({ const formRules = reactive({
title: [{ required: true, message: '标题不能为空', trigger: 'blur' }], title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
orderNumber: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }] sort: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }]
}) // }) //
/** 打开新增/编辑表单 */ /** 打开表单弹窗 */
const openForm = (row?: MdProductSipVO) => { const openForm = (type: string, row?: MdProductSipVO) => {
formDialogVisible.value = true dialogVisible.value = true
if (row) { dialogTitle.value = t('action.' + type)
formDialogTitle.value = '编辑 SIP' formType.value = type
resetForm()
//
if (type === 'update' && row) {
formData.value = { formData.value = {
id: row.id, id: row.id,
itemId: row.itemId, itemId: row.itemId,
orderNumber: row.orderNumber, sort: row.sort,
processId: row.processId, processId: row.processId,
title: row.title, title: row.title,
description: row.description, description: row.description,
url: row.url, url: row.url,
remark: row.remark remark: row.remark
} }
} else { }
formDialogTitle.value = '添加 SIP' }
formData.value = {
id: undefined, /** 重置表单 */
itemId: props.itemId, const resetForm = () => {
orderNumber: 0, formData.value = {
processId: undefined, id: undefined,
title: undefined, itemId: props.itemId,
description: undefined, sort: 0,
url: undefined, processId: undefined,
remark: undefined title: undefined,
} description: undefined,
url: undefined,
remark: undefined
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/** 提交表单 */ /** 提交表单 */
const submitForm = async () => { const submitForm = async () => {
await formRef.value.validate() //
if (formData.value.id) { if (!formRef) return
await MdProductSipApi.updateProductSip(formData.value as unknown as MdProductSipVO) const valid = await formRef.value.validate()
message.success('编辑成功') if (!valid) return
} else { //
await MdProductSipApi.createProductSip(formData.value as unknown as MdProductSipVO) formLoading.value = true
message.success('添加成功') try {
const data = formData.value as unknown as MdProductSipVO
if (formType.value === 'create') {
await MdProductSipApi.createProductSip(data)
message.success(t('common.createSuccess'))
} else {
await MdProductSipApi.updateProductSip(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
await getList()
} finally {
formLoading.value = false
} }
formDialogVisible.value = false
await getList()
} }
// ==================== ==================== // ==================== ====================

View File

@ -1,8 +1,7 @@
<!-- MES 产品SOP 列表 --> <!-- MES 产品SOP 列表 -->
<!-- TODO @AI参考 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/md/item/MdProductSipForm.vue 类似改动 -->
<template> <template>
<div> <div>
<el-button type="primary" plain size="small" @click="openForm(undefined)" class="mb-10px"> <el-button type="primary" plain size="small" @click="openForm('create')" class="mb-10px">
<Icon icon="ep:plus" class="mr-5px" /> 添加 SOP <Icon icon="ep:plus" class="mr-5px" /> 添加 SOP
</el-button> </el-button>
<!-- SOP 卡片列表 --> <!-- SOP 卡片列表 -->
@ -22,16 +21,14 @@
<!-- 信息区域 --> <!-- 信息区域 -->
<div class="p-10px"> <div class="p-10px">
<div class="font-bold text-14px mb-4px truncate">{{ item.title }}</div> <div class="font-bold text-14px mb-4px truncate">{{ item.title }}</div>
<div class="text-12px color-gray mb-4px">序号{{ item.orderNumber }}</div>
<div v-if="item.processName" class="text-12px color-gray mb-4px">
工序{{ item.processName }}
</div>
<div v-if="item.description" class="text-12px color-gray truncate"> <div v-if="item.description" class="text-12px color-gray truncate">
{{ item.description }} {{ item.description }}
</div> </div>
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="flex justify-end mt-8px"> <div class="flex justify-end mt-8px">
<el-button link type="primary" size="small" @click="openForm(item)"></el-button> <el-button link type="primary" size="small" @click="openForm('update', item)">
编辑
</el-button>
<el-button link type="danger" size="small" @click="handleDelete(item.id!)"> <el-button link type="danger" size="small" @click="handleDelete(item.id!)">
删除 删除
</el-button> </el-button>
@ -46,21 +43,30 @@
</el-row> </el-row>
<!-- 新增/编辑弹窗 --> <!-- 新增/编辑弹窗 -->
<Dialog :title="formDialogTitle" v-model="formDialogVisible" width="500px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px"> <el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="标题" prop="title"> <el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入标题" /> <el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item> </el-form-item>
<el-form-item label="排列顺序" prop="orderNumber"> <el-form-item label="展示顺序" prop="sort">
<el-input-number <el-input-number
v-model="formData.orderNumber" v-model="formData.sort"
:min="0" :min="0"
controls-position="right" controls-position="right"
class="!w-1/1" class="!w-1/1"
/> />
</el-form-item> </el-form-item>
<!-- TODO @芋艿工序选择等工序pro_process模块实现后对接下拉选择 --> <!-- TODO @芋艿所属工序等工序模块完成后改为下拉选择 -->
<el-form-item label="详细描述" prop="description"> <el-form-item label="所属工序" prop="processId">
<el-input v-model="formData.processId" placeholder="请输入工序编号" />
</el-form-item>
<el-form-item label="内容说明" prop="description">
<el-input <el-input
v-model="formData.description" v-model="formData.description"
type="textarea" type="textarea"
@ -76,8 +82,8 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="formDialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</div> </div>
@ -94,7 +100,8 @@ const props = defineProps<{
itemId: number // itemId: number //
}>() }>()
const message = useMessage() const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<MdProductSopVO[]>([]) // SOP const list = ref<MdProductSopVO[]>([]) // SOP
@ -117,13 +124,15 @@ const handlePreview = (url?: string) => {
} }
// ==================== / ==================== // ==================== / ====================
const formDialogVisible = ref(false) // const dialogVisible = ref(false) //
const formDialogTitle = ref('') // const dialogTitle = ref('') //
const formType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref const formRef = ref() // Ref
const formData = ref({ const formData = ref({
id: undefined as number | undefined, id: undefined as number | undefined,
itemId: undefined as number | undefined, itemId: undefined as number | undefined,
orderNumber: 0, sort: 0,
processId: undefined as number | undefined, processId: undefined as number | undefined,
title: undefined as string | undefined, title: undefined as string | undefined,
description: undefined as string | undefined, description: undefined as string | undefined,
@ -132,52 +141,68 @@ const formData = ref({
}) // }) //
const formRules = reactive({ const formRules = reactive({
title: [{ required: true, message: '标题不能为空', trigger: 'blur' }], title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
orderNumber: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }] sort: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }]
}) // }) //
/** 打开新增/编辑表单 */ /** 打开表单弹窗 */
const openForm = (row?: MdProductSopVO) => { const openForm = (type: string, row?: MdProductSopVO) => {
formDialogVisible.value = true dialogVisible.value = true
if (row) { dialogTitle.value = t('action.' + type)
formDialogTitle.value = '编辑 SOP' formType.value = type
resetForm()
//
if (type === 'update' && row) {
formData.value = { formData.value = {
id: row.id, id: row.id,
itemId: row.itemId, itemId: row.itemId,
orderNumber: row.orderNumber, sort: row.sort,
processId: row.processId, processId: row.processId,
title: row.title, title: row.title,
description: row.description, description: row.description,
url: row.url, url: row.url,
remark: row.remark remark: row.remark
} }
} else { }
formDialogTitle.value = '添加 SOP' }
formData.value = {
id: undefined, /** 重置表单 */
itemId: props.itemId, const resetForm = () => {
orderNumber: 0, formData.value = {
processId: undefined, id: undefined,
title: undefined, itemId: props.itemId,
description: undefined, sort: 0,
url: undefined, processId: undefined,
remark: undefined title: undefined,
} description: undefined,
url: undefined,
remark: undefined
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/** 提交表单 */ /** 提交表单 */
const submitForm = async () => { const submitForm = async () => {
await formRef.value.validate() //
if (formData.value.id) { if (!formRef) return
await MdProductSopApi.updateProductSop(formData.value as unknown as MdProductSopVO) const valid = await formRef.value.validate()
message.success('编辑成功') if (!valid) return
} else { //
await MdProductSopApi.createProductSop(formData.value as unknown as MdProductSopVO) formLoading.value = true
message.success('添加成功') try {
const data = formData.value as unknown as MdProductSopVO
if (formType.value === 'create') {
await MdProductSopApi.createProductSop(data)
message.success(t('common.createSuccess'))
} else {
await MdProductSopApi.updateProductSop(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
await getList()
} finally {
formLoading.value = false
} }
formDialogVisible.value = false
await getList()
} }
// ==================== ==================== // ==================== ====================