feat: EBOM选择物料弹窗增加左侧分类树(issues-0311-3)
- form.vue: 选择物料弹窗从el-select远程搜索改为左右布局 左侧el-tree加载物料分类树(getItemTypeTreeselect) 右侧el-table展示物料列表,支持编码/名称搜索 点击树节点按分类过滤物料,参考Item管理页实现 - form.vue: 表格和输入框去掉size=small,恢复默认高度 - list.vue: 列表列宽从固定width改为min-width,增加show-overflow-tooltip Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -147,7 +147,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="lines" border size="small" max-height="420">
|
||||
<el-table :data="lines" border max-height="420">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="物料编码" min-width="160">
|
||||
<template #default="{ row, $index }">
|
||||
@@ -162,7 +162,7 @@
|
||||
<el-table-column prop="unitName" label="主计量" width="90" align="center" />
|
||||
<el-table-column label="计划线路" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.supplyType" :disabled="readonly" size="small" style="width: 100%">
|
||||
<el-select v-model="row.supplyType" :disabled="readonly" style="width: 100%">
|
||||
<el-option label="采购" value="PURCHASE" />
|
||||
<el-option label="自制" value="PRODUCE" />
|
||||
<el-option label="委外" value="OUTSOURCE" />
|
||||
@@ -171,7 +171,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="领料方式" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.attr1" :disabled="readonly" size="small" style="width: 100%">
|
||||
<el-select v-model="row.attr1" :disabled="readonly" style="width: 100%">
|
||||
<el-option label="按单领用" value="ORDER" />
|
||||
<el-option label="直接领用" value="DIRECT" />
|
||||
</el-select>
|
||||
@@ -179,12 +179,12 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" label="分子" width="90" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.quantity" :min="0" :precision="4" :disabled="readonly" size="small" style="width: 100%" />
|
||||
<el-input-number v-model="row.quantity" :min="0" :precision="4" :disabled="readonly" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用量方式" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.attr2" :disabled="readonly" size="small" style="width: 100%">
|
||||
<el-select v-model="row.attr2" :disabled="readonly" style="width: 100%">
|
||||
<el-option label="按比例" value="RATIO" />
|
||||
<el-option label="固定用量" value="FIXED" />
|
||||
<el-option label="变动用量" value="VARIABLE" />
|
||||
@@ -226,36 +226,80 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选择子件物料 -->
|
||||
<el-dialog v-model="itemDialogVisible" title="选择物料" width="640px" destroy-on-close>
|
||||
<el-select
|
||||
v-model="selectedItemId"
|
||||
filterable
|
||||
remote
|
||||
placeholder="输入编码或名称搜索"
|
||||
:remote-method="searchItems"
|
||||
:loading="itemLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="it in itemOptions"
|
||||
:key="it.itemId"
|
||||
:label="`${it.itemCode} - ${it.itemName}`"
|
||||
:value="it.itemId"
|
||||
<el-dialog v-model="itemDialogVisible" title="选择物料" width="980px" destroy-on-close>
|
||||
<div class="item-dialog-body">
|
||||
<div class="item-tree-panel" v-loading="itemTypeTreeLoading">
|
||||
<el-input
|
||||
v-model="itemTypeFilterText"
|
||||
placeholder="请输入分类名称"
|
||||
clearable
|
||||
:prefix-icon="Search"
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
</el-select>
|
||||
<template #footer>
|
||||
<el-button @click="itemDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmItem">确定</el-button>
|
||||
<el-tree
|
||||
ref="itemTypeTreeRef"
|
||||
:data="itemTypeTreeData"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterItemTypeNode"
|
||||
@node-click="handleItemTypeNodeClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-table-panel">
|
||||
<div class="item-search-bar">
|
||||
<el-form :inline="true" @submit.prevent="loadSelectableItems">
|
||||
<el-form-item label="物料编码">
|
||||
<el-input v-model="itemSearchCode" placeholder="请输入" clearable style="width: 140px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="物料名称">
|
||||
<el-input v-model="itemSearchName" placeholder="请输入" clearable style="width: 140px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="itemLoading" @click="loadSelectableItems">搜索</el-button>
|
||||
<el-button @click="resetItemSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button link type="primary" @click="handleQueryAll">查询所有</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="itemLoading"
|
||||
:data="itemOptions"
|
||||
border
|
||||
stripe
|
||||
max-height="380"
|
||||
>
|
||||
<el-table-column prop="itemCode" label="物料编码" width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="itemName" label="物料名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="型号规格" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="unitName" label="主计量" width="80" align="center" />
|
||||
<el-table-column prop="itemOrProduct" label="供应方式" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.itemOrProduct === 'PRODUCT' ? '自制' : '采购' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="70" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="selectItem(row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="item-table-pagination">
|
||||
<span>共 {{ itemOptions.length }} 条</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowDown, Plus } from '@element-plus/icons-vue'
|
||||
import { ArrowDown, Plus, Search } from '@element-plus/icons-vue'
|
||||
import {
|
||||
addBomHeader,
|
||||
addBomLine,
|
||||
@@ -268,6 +312,7 @@ import {
|
||||
type BomLine
|
||||
} from '@/api/masterdata/bom'
|
||||
import { listMdItem, type MdItem } from '@/api/masterdata/item'
|
||||
import { getItemTypeTreeselect } from '@/api/masterdata/itemtype'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -552,44 +597,95 @@ function confirmProduct() {
|
||||
const itemDialogVisible = ref(false)
|
||||
const itemLoading = ref(false)
|
||||
const itemOptions = ref<MdItem[]>([])
|
||||
const selectedItemId = ref<number | undefined>()
|
||||
const editingLineIndex = ref<number>(-1)
|
||||
const itemSearchCode = ref('')
|
||||
const itemSearchName = ref('')
|
||||
const itemTypeTreeLoading = ref(false)
|
||||
const itemTypeTreeData = ref<any[]>([])
|
||||
const selectedItemTypeId = ref<number | undefined>()
|
||||
const itemTypeFilterText = ref('')
|
||||
const itemTypeTreeRef = ref<any>(null)
|
||||
|
||||
function openItemDialog(idx: number) {
|
||||
editingLineIndex.value = idx
|
||||
selectedItemId.value = lines.value[idx]?.bomItemId
|
||||
itemDialogVisible.value = true
|
||||
function filterItemTypeNode(value: string, data: any) {
|
||||
if (!value) return true
|
||||
return data.label?.indexOf(value) !== -1
|
||||
}
|
||||
|
||||
async function searchItems(query: string) {
|
||||
if (!query) {
|
||||
itemOptions.value = []
|
||||
return
|
||||
watch(itemTypeFilterText, (val) => {
|
||||
itemTypeTreeRef.value?.filter(val)
|
||||
})
|
||||
|
||||
async function openItemDialog(idx: number) {
|
||||
editingLineIndex.value = idx
|
||||
itemSearchCode.value = ''
|
||||
itemSearchName.value = ''
|
||||
selectedItemTypeId.value = undefined
|
||||
itemTypeFilterText.value = ''
|
||||
itemDialogVisible.value = true
|
||||
await loadItemTypeTree()
|
||||
await loadSelectableItems()
|
||||
}
|
||||
|
||||
async function loadItemTypeTree() {
|
||||
itemTypeTreeLoading.value = true
|
||||
try {
|
||||
const res = await getItemTypeTreeselect()
|
||||
itemTypeTreeData.value = res.data || []
|
||||
} finally {
|
||||
itemTypeTreeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSelectableItems() {
|
||||
itemLoading.value = true
|
||||
try {
|
||||
const res = await listMdItem({ itemCode: query, enableFlag: 'Y', pageNum: 1, pageSize: 50 })
|
||||
const params: Record<string, any> = {
|
||||
enableFlag: 'Y',
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
}
|
||||
if (selectedItemTypeId.value) params.itemTypeId = selectedItemTypeId.value
|
||||
if (itemSearchCode.value.trim()) params.itemCode = itemSearchCode.value.trim()
|
||||
if (itemSearchName.value.trim()) params.itemName = itemSearchName.value.trim()
|
||||
const res = await listMdItem(params)
|
||||
itemOptions.value = res.rows.filter(it => it.itemId !== form.itemId)
|
||||
} finally {
|
||||
itemLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function confirmItem() {
|
||||
function resetItemSearch() {
|
||||
itemSearchCode.value = ''
|
||||
itemSearchName.value = ''
|
||||
selectedItemTypeId.value = undefined
|
||||
itemTypeTreeRef.value?.setCurrentKey(null)
|
||||
loadSelectableItems()
|
||||
}
|
||||
|
||||
function handleQueryAll() {
|
||||
itemSearchCode.value = ''
|
||||
itemSearchName.value = ''
|
||||
selectedItemTypeId.value = undefined
|
||||
itemTypeTreeRef.value?.setCurrentKey(null)
|
||||
loadSelectableItems()
|
||||
}
|
||||
|
||||
function handleItemTypeNodeClick(node: any) {
|
||||
selectedItemTypeId.value = node?.id ?? node?.itemTypeId
|
||||
loadSelectableItems()
|
||||
}
|
||||
|
||||
function selectItem(selected: MdItem) {
|
||||
const idx = editingLineIndex.value
|
||||
const selected = itemOptions.value.find(i => i.itemId === selectedItemId.value)
|
||||
if (idx < 0 || !selected) {
|
||||
itemDialogVisible.value = false
|
||||
return
|
||||
}
|
||||
if (idx < 0) return
|
||||
const row = lines.value[idx]
|
||||
row.bomItemId = selected.itemId
|
||||
row.bomItemCode = selected.itemCode
|
||||
row.bomItemName = selected.itemName
|
||||
row.bomItemSpec = selected.specification
|
||||
row.unitOfMeasure = selected.unitOfMeasure || '' // 必填
|
||||
row.unitOfMeasure = selected.unitOfMeasure || ''
|
||||
row.unitName = selected.unitName || ''
|
||||
row.itemOrProduct = selected.itemOrProduct || 'ITEM' // 必填
|
||||
row.itemOrProduct = selected.itemOrProduct || 'ITEM'
|
||||
itemDialogVisible.value = false
|
||||
}
|
||||
|
||||
@@ -652,5 +748,57 @@ onMounted(load)
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.item-dialog-body {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
min-height: 460px;
|
||||
}
|
||||
|
||||
.item-tree-panel {
|
||||
width: 220px;
|
||||
min-width: 220px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
overflow: auto;
|
||||
max-height: 460px;
|
||||
}
|
||||
|
||||
.item-table-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.item-search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.item-search-bar :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.item-table-pagination {
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.lines-card :deep(.el-table__body .el-table__cell) {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.lines-card :deep(.el-input__wrapper),
|
||||
.lines-card :deep(.el-input-number .el-input__wrapper),
|
||||
.lines-card :deep(.el-select .el-input__wrapper) {
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -86,10 +86,11 @@
|
||||
:data="bomList"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
@row-dblclick="goView"
|
||||
>
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column prop="bomCode" label="单据编码" width="140">
|
||||
<el-table-column prop="bomCode" label="单据编码" min-width="200" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="goView(row)">{{ row.bomCode }}</el-link>
|
||||
</template>
|
||||
@@ -102,9 +103,9 @@
|
||||
<el-table-column prop="version" label="版本号" width="90" align="center">
|
||||
<template #default="{ row }">{{ row.version || '1.00' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="versionDesc" label="版本说明" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="drawingNo" label="图纸号" width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="单据状态" width="100" align="center">
|
||||
<el-table-column prop="versionDesc" label="版本说明" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="drawingNo" label="图纸号" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="单据状态" width="100" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span :class="getStatusClass(row.status)">{{ getStatusLabel(row.status) }}</span>
|
||||
</template>
|
||||
@@ -127,22 +128,23 @@
|
||||
:data="bomList"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column prop="bomCode" label="单据编码" width="140" align="center" />
|
||||
<el-table-column prop="bomCode" label="单据编码" min-width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="createTime" label="单据日期" width="110" align="center">
|
||||
<template #default="{ row }">{{ formatDate(row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="单据状态" width="100" align="center">
|
||||
<el-table-column prop="status" label="单据状态" width="100" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span :class="getStatusClass(row.status)">{{ getStatusLabel(row.status) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="businessType" label="业务类型" width="120" align="center" />
|
||||
<el-table-column prop="businessDept" label="业务部门" width="120" align="center" />
|
||||
<el-table-column prop="businessUser" label="业务人员" width="120" align="center" />
|
||||
<el-table-column prop="businessStatus" label="业务状态" width="120" align="center" />
|
||||
<el-table-column prop="businessType" label="业务类型" min-width="120" align="center" />
|
||||
<el-table-column prop="businessDept" label="业务部门" min-width="120" align="center" />
|
||||
<el-table-column prop="businessUser" label="业务人员" min-width="120" align="center" />
|
||||
<el-table-column prop="businessStatus" label="业务状态" min-width="120" align="center" />
|
||||
<el-table-column prop="approveTime" label="审核日期" width="110" align="center" />
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
|
||||
Reference in New Issue
Block a user