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