✨ feat(mes): 添加物料选择弹窗组件,优化物料编码生成逻辑
新增物料选择弹窗组件,支持物料分类树和物料列表的搜索与选择功能。优化物料编码生成方法,使用自动编码规则生成物料编码,提升用户体验。pull/871/MERGE
parent
856e8f6a76
commit
3860970490
|
|
@ -37,6 +37,7 @@
|
|||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="物料分类" prop="itemTypeId">
|
||||
<!-- TODO @AI:在 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/md/item/type 增加一个物料的 select;注意,只允许选择子节点; -->
|
||||
<el-tree-select
|
||||
v-model="formData.itemTypeId"
|
||||
:data="itemTypeTree"
|
||||
|
|
@ -48,6 +49,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- TODO @AI:1)新建时,默认为【禁用】后端设置;2)这里只负责展示,后端的 save vo 不要接收该参数; -->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
|
|
@ -82,7 +84,7 @@
|
|||
v-model="formData.minStock"
|
||||
placeholder="请输入最低库存量"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
:precision="2"
|
||||
class="!w-1/1"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
@ -93,7 +95,7 @@
|
|||
v-model="formData.maxStock"
|
||||
placeholder="请输入最高库存量"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
:precision="2"
|
||||
class="!w-1/1"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
@ -113,7 +115,7 @@
|
|||
<el-tab-pane label="批次属性" name="batch" lazy v-if="formData.batchFlag">
|
||||
<MdItemBatchConfigForm :itemId="formData.id!" :itemOrProduct="currentItemOrProduct" />
|
||||
</el-tab-pane>
|
||||
<!-- TODO @芋艿:替代品,等替代品模块实现后对接 -->
|
||||
<!-- TODO @芋艿:【对齐】替代品,目前没这个,可忽略 -->
|
||||
<el-tab-pane label="替代品" name="substitute" lazy>
|
||||
<el-empty description="替代品(待实现)" />
|
||||
</el-tab-pane>
|
||||
|
|
@ -132,9 +134,9 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { generateRandomStr } from '@/utils'
|
||||
import { MdItemApi, MdItemVO } from '@/api/mes/md/item'
|
||||
import { MdItemTypeApi, MdItemTypeVO } from '@/api/mes/md/item/type'
|
||||
import { AutoCodeRecordApi } from '@/api/mes/md/autocode/record'
|
||||
import MdItemBatchConfigForm from './MdItemBatchConfigForm.vue'
|
||||
import MdProductBomForm from './MdProductBomForm.vue'
|
||||
import MdProductSopForm from './MdProductSopForm.vue'
|
||||
|
|
@ -142,6 +144,7 @@ import MdProductSipForm from './MdProductSipForm.vue'
|
|||
import MdUnitMeasureSelect from '@/views/mes/md/unitmeasure/components/MdUnitMeasureSelect.vue'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import { MesAutoCodeRuleCode } from '@/views/mes/utils/constants'
|
||||
|
||||
/** MES 物料产品 表单 */
|
||||
defineOptions({ name: 'MdItemForm' })
|
||||
|
|
@ -150,6 +153,7 @@ const { t } = useI18n() // 国际化
|
|||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
// TODO @AI:标题对齐下,使用 compute ,然后有新增物料/产品;修改物料/产品;查看物料/产品;
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
|
|
@ -161,7 +165,7 @@ const formData = ref({
|
|||
specification: undefined,
|
||||
unitMeasureId: undefined,
|
||||
itemTypeId: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
status: CommonStatusEnum.DISABLE,
|
||||
safeStockFlag: false,
|
||||
minStock: 0,
|
||||
maxStock: 0,
|
||||
|
|
@ -190,9 +194,8 @@ const currentItemOrProduct = computed(() => {
|
|||
})
|
||||
|
||||
/** 生成物料编码 */
|
||||
const generateCode = () => {
|
||||
// TODO @芋艿:后续对接后端编码生成接口
|
||||
formData.value.code = 'IF' + generateRandomStr(12)
|
||||
const generateCode = async () => {
|
||||
formData.value.code = await AutoCodeRecordApi.generateAutoCode(MesAutoCodeRuleCode.ITEM_CODE)
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
|
|
@ -233,6 +236,7 @@ const submitForm = async () => {
|
|||
await MdItemApi.updateItem(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
// TODO @AI:【对齐】应该都不需要自动关闭;用户按需添加;
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
|
|
@ -250,7 +254,7 @@ const resetForm = () => {
|
|||
specification: undefined,
|
||||
unitMeasureId: undefined,
|
||||
itemTypeId: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
status: CommonStatusEnum.DISABLE,
|
||||
safeStockFlag: false,
|
||||
minStock: 0,
|
||||
maxStock: 0,
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
</Dialog>
|
||||
|
||||
<!-- 物料选择弹窗 -->
|
||||
<ItemProductSelect ref="itemSelectRef" @selected="handleItemSelected" />
|
||||
<MdItemSelectDialog ref="itemSelectRef" @selected="handleItemSelected" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
import { MdProductBomApi, MdProductBomVO } from '@/api/mes/md/item/productBom'
|
||||
import { MdItemVO } from '@/api/mes/md/item'
|
||||
import { getItemOrProductLabel } from '@/views/mes/utils/constants'
|
||||
import ItemProductSelect from '@/views/mes/md/components/ItemProductSelect.vue'
|
||||
import MdItemSelectDialog from '@/views/mes/md/item/components/MdItemSelectDialog.vue'
|
||||
|
||||
defineOptions({ name: 'MdProductBomForm' })
|
||||
|
||||
|
|
|
|||
|
|
@ -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: 'MdItemSelectDialog' })
|
||||
|
||||
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>
|
||||
|
|
@ -89,6 +89,7 @@
|
|||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<!-- TODO @AI:增加点击进入详情;formType = detail 这种; -->
|
||||
<el-table-column label="物料编码" align="center" prop="code" />
|
||||
<el-table-column label="物料名称" align="center" prop="name" />
|
||||
<el-table-column label="规格型号" align="center" prop="specification" />
|
||||
|
|
@ -104,6 +105,7 @@
|
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.safeStockFlag" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @AI:status 改成 switch 单独一个开关; -->
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
|
|
@ -134,6 +136,7 @@
|
|||
>
|
||||
删除
|
||||
</el-button>
|
||||
<!-- TODO @芋艿:【HiPrint】标签打印 -->
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
|
|
@ -253,12 +256,7 @@ const handleImport = () => {
|
|||
/** 查看物料条码 */
|
||||
const barcodeDetailRef = ref()
|
||||
const handleBarcode = async (row: MdItemVO) => {
|
||||
await barcodeDetailRef.value.openByBusiness(
|
||||
row.id,
|
||||
BarcodeBizTypeEnum.ITEM,
|
||||
row.code,
|
||||
row.name
|
||||
)
|
||||
await barcodeDetailRef.value.openByBusiness(row.id, BarcodeBizTypeEnum.ITEM, row.code, row.name)
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
|
|
|
|||
|
|
@ -413,6 +413,7 @@ export const MesAutoCodePaddedMethodEnum = {
|
|||
|
||||
/** MES 自动编码规则 Code 枚举 */
|
||||
export const MesAutoCodeRuleCode = {
|
||||
ITEM_CODE: 'MD_ITEM_CODE', // 物料编码
|
||||
SN_CODE: 'WM_SN_CODE', // SN 码
|
||||
PACKAGE_CODE: 'WM_PACKAGE_CODE', // 装箱单编码
|
||||
BATCH_CODE: 'WM_BATCH_CODE', // 批次编码
|
||||
|
|
|
|||
Loading…
Reference in New Issue