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 {
id?: number // SIP 编号
itemId: number // 物料产品 ID
orderNumber: number // 排列顺序
sort: number // 排列顺序
processId?: number // 工序 ID
title: string // 标题
description?: string // 详细描述

View File

@ -4,7 +4,7 @@ import request from '@/config/axios'
export interface MdProductSopVO {
id?: number // SOP 编号
itemId: number // 物料产品 ID
orderNumber: number // 排列顺序
sort: number // 排列顺序
processId?: number // 工序 ID
title: 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 列表 -->
<template>
<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 物料
</el-button>
<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" width="120">
<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>
</template>
</el-table-column>
</el-table>
<!-- 添加 BOM 物料弹窗 -->
<!-- TODO @AI这里实现不太对调整成物料产品选择这个组件我们需要在 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/md/components 里搞一个然后这里使用 -->
<Dialog title="添加 BOM 物料" v-model="addDialogVisible" width="500px">
<el-form ref="addFormRef" :model="addFormData" :rules="addFormRules" label-width="100px">
<el-form-item label="BOM 物料" prop="bomItemId">
<el-select
v-model="addFormData.bomItemId"
filterable
remote
:remote-method="searchItems"
:loading="itemSearchLoading"
placeholder="请搜索物料编码/名称"
class="w-1/1"
>
<el-option
v-for="item in itemOptions"
:key="item.id"
:label="item.code + ' - ' + item.name"
:value="item.id"
/>
</el-select>
<!-- 添加/编辑 BOM 弹窗 -->
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-form-item label="BOM 物料编码" prop="bomItemCode">
<el-input v-model="formData.bomItemCode" readonly />
</el-form-item>
<el-form-item label="BOM 物料名称" prop="bomItemName">
<el-input v-model="formData.bomItemName" readonly />
</el-form-item>
<el-form-item label="规格型号" prop="bomItemSpecification">
<el-input v-model="formData.bomItemSpecification" readonly />
</el-form-item>
<el-form-item label="单位" prop="unitMeasureName">
<el-input v-model="formData.unitMeasureName" readonly />
</el-form-item>
<el-form-item label="用量比例" prop="quantity">
<el-input-number
v-model="addFormData.quantity"
v-model="formData.quantity"
:min="0"
:precision="4"
:step="0.1"
controls-position="right"
class="!w-1/1"
/>
</el-form-item>
<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>
<template #footer>
<el-button @click="submitAddForm" type="primary"> </el-button>
<el-button @click="addDialogVisible = false"> </el-button>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
<!-- 编辑 BOM 弹窗 -->
<Dialog title="编辑 BOM 物料" v-model="editDialogVisible" width="500px">
<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>
<!-- 物料选择弹窗 -->
<ItemProductSelect ref="itemSelectRef" @selected="handleItemSelected" />
</div>
</template>
<script setup lang="ts">
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 ItemProductSelect from '@/views/mes/md/components/ItemProductSelect.vue'
defineOptions({ name: 'MdProductBomForm' })
@ -100,7 +82,8 @@ const props = defineProps<{
itemId: number //
}>()
const message = useMessage()
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref<MdProductBomVO[]>([]) // BOM
@ -114,97 +97,112 @@ const getList = async () => {
}
}
// ==================== ====================
const itemSearchLoading = ref(false) //
const itemOptions = ref<any[]>([]) //
// ==================== ====================
const itemSelectRef = ref() // Ref
/** 远程搜索物料 */
const searchItems = async (query: string) => {
if (!query) {
itemOptions.value = []
/** 点击添加按钮 → 打开物料选择弹窗 */
const handleAdd = () => {
itemSelectRef.value.open()
}
/** 物料选中回调:直接批量创建 BOM默认用量比例为 1 */
const handleItemSelected = async (rows: MdItemVO[]) => {
if (!rows || rows.length === 0) {
return
}
itemSearchLoading.value = true
try {
const data = await MdItemApi.getItemPage({ pageNo: 1, pageSize: 20, code: query })
itemOptions.value = data.list || []
} finally {
itemSearchLoading.value = false
for (const item of rows) {
await MdProductBomApi.createProductBom({
itemId: props.itemId,
bomItemId: item.id,
quantity: 1
} as unknown as MdProductBomVO)
}
}
// ==================== 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
message.success(t('common.createSuccess'))
await getList()
}
// ==================== BOM ====================
const editDialogVisible = ref(false) //
const editFormRef = ref() // Ref
/** 编辑表单数据 */
const editFormData = ref({
// ==================== / BOM ====================
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
id: undefined as number | undefined,
itemId: 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,
remark: undefined as string | undefined
})
const editFormRules = reactive({
}) //
const formRules = reactive({
quantity: [{ required: true, message: '用量比例不能为空', trigger: 'blur' }]
})
}) //
/** 打开编辑弹窗 */
const openEditForm = (row: MdProductBomVO) => {
editDialogVisible.value = true
editFormData.value = {
id: row.id,
itemId: row.itemId,
bomItemId: row.bomItemId,
quantity: row.quantity,
remark: row.remark
/** 打开表单弹窗 */
const openForm = (type: string, row?: MdProductBomVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (type === 'update' && row) {
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 () => {
await editFormRef.value.validate()
await MdProductBomApi.updateProductBom(editFormData.value as unknown as MdProductBomVO)
message.success('编辑成功')
editDialogVisible.value = false
await getList()
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
itemId: props.itemId,
bomItemId: undefined,
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 列表 -->
<template>
<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
</el-button>
<!-- SIP 卡片列表 -->
@ -21,18 +21,14 @@
<!-- 信息区域 -->
<div class="p-10px">
<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">
{{ item.description }}
</div>
<!-- 操作按钮 -->
<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>
@ -47,22 +43,26 @@
</el-row>
<!-- 新增/编辑弹窗 -->
<Dialog :title="formDialogTitle" v-model="formDialogVisible" width="500px">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
<Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item>
<!-- TODO @AI展示顺序 -->
<el-form-item label="排列顺序" prop="orderNumber">
<el-form-item label="展示顺序" prop="sort">
<el-input-number
v-model="formData.orderNumber"
v-model="formData.sort"
:min="0"
controls-position="right"
class="!w-1/1"
/>
</el-form-item>
<!-- TODO @AI内容说明 -->
<el-form-item label="详细描述" prop="description">
<el-form-item label="内容说明" prop="description">
<el-input
v-model="formData.description"
type="textarea"
@ -70,8 +70,10 @@
placeholder="请输入详细描述"
/>
</el-form-item>
<!-- TODO @芋艿工序选择等工序pro_process模块实现后对接下拉选择 -->
<!-- TODO @AI先至少有个 input 手动填写所属工序 -->
<!-- TODO @芋艿所属工序等工序模块完成后改为下拉选择 -->
<el-form-item label="所属工序" prop="processId">
<el-input v-model="formData.processId" placeholder="请输入工序编号" />
</el-form-item>
<el-form-item label="图片" prop="url">
<UploadImg v-model="formData.url" :limit="1" :is-show-tip="false" />
</el-form-item>
@ -80,8 +82,8 @@
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary"> </el-button>
<el-button @click="formDialogVisible = false"> </el-button>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</div>
@ -98,7 +100,8 @@ const props = defineProps<{
itemId: number //
}>()
const message = useMessage()
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref<MdProductSipVO[]>([]) // SIP
@ -123,13 +126,15 @@ const handlePreview = (url?: string) => {
}
// ==================== / ====================
const formDialogVisible = ref(false) //
const formDialogTitle = ref('') //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
id: undefined as number | undefined,
itemId: undefined as number | undefined,
orderNumber: 0,
sort: 0,
processId: undefined as number | undefined,
title: undefined as string | undefined,
description: undefined as string | undefined,
@ -138,52 +143,68 @@ const formData = ref({
}) //
const formRules = reactive({
title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
orderNumber: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }]
sort: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }]
}) //
/** 打开新增/编辑表单 */
const openForm = (row?: MdProductSipVO) => {
formDialogVisible.value = true
if (row) {
formDialogTitle.value = '编辑 SIP'
/** 打开表单弹窗 */
const openForm = (type: string, row?: MdProductSipVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (type === 'update' && row) {
formData.value = {
id: row.id,
itemId: row.itemId,
orderNumber: row.orderNumber,
sort: row.sort,
processId: row.processId,
title: row.title,
description: row.description,
url: row.url,
remark: row.remark
}
} else {
formDialogTitle.value = '添加 SIP'
formData.value = {
id: undefined,
itemId: props.itemId,
orderNumber: 0,
processId: undefined,
title: undefined,
description: undefined,
url: undefined,
remark: undefined
}
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
itemId: props.itemId,
sort: 0,
processId: undefined,
title: undefined,
description: undefined,
url: undefined,
remark: undefined
}
formRef.value?.resetFields()
}
/** 提交表单 */
const submitForm = async () => {
await formRef.value.validate()
if (formData.value.id) {
await MdProductSipApi.updateProductSip(formData.value as unknown as MdProductSipVO)
message.success('编辑成功')
} else {
await MdProductSipApi.createProductSip(formData.value as unknown as MdProductSipVO)
message.success('添加成功')
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
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 列表 -->
<!-- TODO @AI参考 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/md/item/MdProductSipForm.vue 类似改动 -->
<template>
<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
</el-button>
<!-- SOP 卡片列表 -->
@ -22,16 +21,14 @@
<!-- 信息区域 -->
<div class="p-10px">
<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">
{{ item.description }}
</div>
<!-- 操作按钮 -->
<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>
@ -46,21 +43,30 @@
</el-row>
<!-- 新增/编辑弹窗 -->
<Dialog :title="formDialogTitle" v-model="formDialogVisible" width="500px">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
<Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item>
<el-form-item label="排列顺序" prop="orderNumber">
<el-form-item label="展示顺序" prop="sort">
<el-input-number
v-model="formData.orderNumber"
v-model="formData.sort"
:min="0"
controls-position="right"
class="!w-1/1"
/>
</el-form-item>
<!-- TODO @芋艿工序选择等工序pro_process模块实现后对接下拉选择 -->
<el-form-item label="详细描述" prop="description">
<!-- TODO @芋艿所属工序等工序模块完成后改为下拉选择 -->
<el-form-item label="所属工序" prop="processId">
<el-input v-model="formData.processId" placeholder="请输入工序编号" />
</el-form-item>
<el-form-item label="内容说明" prop="description">
<el-input
v-model="formData.description"
type="textarea"
@ -76,8 +82,8 @@
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary"> </el-button>
<el-button @click="formDialogVisible = false"> </el-button>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</div>
@ -94,7 +100,8 @@ const props = defineProps<{
itemId: number //
}>()
const message = useMessage()
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref<MdProductSopVO[]>([]) // SOP
@ -117,13 +124,15 @@ const handlePreview = (url?: string) => {
}
// ==================== / ====================
const formDialogVisible = ref(false) //
const formDialogTitle = ref('') //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formType = ref('') // create - update -
const formLoading = ref(false) // 12
const formRef = ref() // Ref
const formData = ref({
id: undefined as number | undefined,
itemId: undefined as number | undefined,
orderNumber: 0,
sort: 0,
processId: undefined as number | undefined,
title: undefined as string | undefined,
description: undefined as string | undefined,
@ -132,52 +141,68 @@ const formData = ref({
}) //
const formRules = reactive({
title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
orderNumber: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }]
sort: [{ required: true, message: '排列顺序不能为空', trigger: 'blur' }]
}) //
/** 打开新增/编辑表单 */
const openForm = (row?: MdProductSopVO) => {
formDialogVisible.value = true
if (row) {
formDialogTitle.value = '编辑 SOP'
/** 打开表单弹窗 */
const openForm = (type: string, row?: MdProductSopVO) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (type === 'update' && row) {
formData.value = {
id: row.id,
itemId: row.itemId,
orderNumber: row.orderNumber,
sort: row.sort,
processId: row.processId,
title: row.title,
description: row.description,
url: row.url,
remark: row.remark
}
} else {
formDialogTitle.value = '添加 SOP'
formData.value = {
id: undefined,
itemId: props.itemId,
orderNumber: 0,
processId: undefined,
title: undefined,
description: undefined,
url: undefined,
remark: undefined
}
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
itemId: props.itemId,
sort: 0,
processId: undefined,
title: undefined,
description: undefined,
url: undefined,
remark: undefined
}
formRef.value?.resetFields()
}
/** 提交表单 */
const submitForm = async () => {
await formRef.value.validate()
if (formData.value.id) {
await MdProductSopApi.updateProductSop(formData.value as unknown as MdProductSopVO)
message.success('编辑成功')
} else {
await MdProductSopApi.createProductSop(formData.value as unknown as MdProductSopVO)
message.success('添加成功')
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
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()
}
// ==================== ====================