@@ -5,7 +5,7 @@
< div class = "page-header" >
< div >
< h2 > 仓储管理 < / h2 >
< p > 库区 、 库位 、 容器 、 物料主数据 , 以及容器位置和容器物料关系 。 < / p >
< p > 库区 、 库位 、 容器 、 物料主数据 , 以及容器位置和搬运任务配置 。 < / p >
< / div >
< el-button :icon = "Refresh" :loading = "loading" @click ="loadAll" > 刷新 < / el -button >
< / div >
@@ -15,54 +15,82 @@
< el -tab -pane label = "基础资料" name = "master" >
< el-tabs v-model = "activeMaster" lazy >
< el-tab-pane label = "库区" name = "areas" >
< div class = "toolbar" > < el-input v-model = "keyword" placeholder="搜索编码/名称" clearable /><el-button type="primary" @click="openArea()" > 新增库区 < / el -button > < / div >
< el-table :data = "filteredAreas" border size = "small" >
< el-table-column prop = "code" label = "编码" width = "140" / >
< div class = "toolbar" >
< el-input v-model = "keyword" placeholder="搜索编码/名称" clearable / >
< el-button type = "primary" @click ="openArea()" > 新增库区 < / el -button >
< el-button type = "danger" : disabled = "batchAreas.batchCount === 0" @click ="batchDeleteAreas" > 批量删除 ( {{ batchAreas.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredAreas" border size = "small" @ selection -change = " batchAreas.onSelectionChange " >
< el-table-column type = "selection" width = "42" :selectable = "batchAreas.selectable" / >
< el-table-column prop = "code" label = "编码" width = "120" / >
< el-table-column prop = "name" label = "名称" / >
< el-table-column prop = "type" label = "类型" width = "12 0" / >
< el-table-column prop = "enabled " label = "启用 " width = "9 0" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column prop = "isLock " label = "锁定 " width = "9 0" > < template # default = "{ row }" > < el-tag : type = "row.isLock ? 'warning' : 'info' " > { { row . isLock ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column prop = "remark " label = "备注" / >
< el-table-column label = "操作 " width = "170" > < template # default = "{ row }" > < el-button size = "small" :disabled = "row.isLock" @click ="openArea(row)" > 编辑 < / el -button > < el-button size = "small" type = "danger" :disabled = "row.isLock" @click ="removeArea(row)" > 删除 < / el -button > < / template > < / el-table-column >
< el-table-column prop = "type" label = "类型" width = "10 0" / >
< el-table-column prop = "sortOrder " label = "排序 " width = "7 0" / >
< el-table-column prop = "enabled " label = "启用 " width = "8 0" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" size = "small "> { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column prop = "isLock" label = "锁定" width = "80" > < template # default = "{ row }" > < el-tag : type = "row.isLock ? 'warning' : 'info' " size = "small" > { { row . isLock ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "备注 " show -overflow -tooltip > < template # default = "{ row }" > { { truncate ( row . remark ) } } < / template > < / el-table-column >
< el-table-column label = "操作" width = "150" fixed = "right" > < template # default = "{ row }" > < el-button size = "small" link :disabled = "row.isLock" @click ="openArea(row)" > 编辑 < / el -button > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeArea(row)" > 删除 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "库位" name = "storages" >
< div class = "toolbar" > < el-input v-model = "keyword" placeholder="搜索编码/名称/站点" clearable /><el-button type="primary" @click="openStorage()" > 新增库位 < / el -button > < / div >
< el-table :data = "filteredStorages" border size = "small" >
< el-table-column prop = "code" label = "编码" width = "140" / >
< el-table-column prop = "name" label = "名称" / >
< el-table-column label = "库区" width = "140" > < template # default = "{ row }" > { { areaName ( row . areaId ) } } < / template > < / el-table-column >
< el-table-column prop = "storageType" label = "类型" width = "120" / >
< el-table-column prop = "siteId " label = "站点 " width = "100 " / >
< el-table-column prop = "capacity " label = "容量 " width = "9 0" / >
< el-table-column prop = "e nabled " label = "启用 " width = "90" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "操作 " width = "17 0" > < template # default = "{ row }" > < el-button size = "small" :disabled = "row.isLock" @click ="openStorage(row)" > 编辑 < / el -button > < el-button size = "small" type = "danger" :disabled = "row.isLock" @click ="removeStorage(row)" > 删除 < / el -button > < / template > < / el-table-column >
< div class = "toolbar" >
< el-input v-model = "keyword" placeholder="搜索编码/名称/站点" clearable / >
< el-button type = "primary" @click ="openStorage()" > 新增库位 < / el -button >
< el-button type = "danger" : disabled = "batchStorages.batchCount === 0" @click ="batchDeleteStorages" > 批量删除 ( {{ batchStorages.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredStorages" border size = "small" @ selection -change = " batchStorages.onSelectionChange " >
< el-table-column type = "selection " width = "42 " :selectable = "batchStorages.selectable " / >
< el-table-column prop = "code " label = "编码 " width = "11 0" / >
< el-table-column prop = "name " label = "名称 " min -width = " 100 " / >
< el-table-column label = "库区 " width = "10 0" > < template # default = "{ row }" > { { areaName ( row . areaId ) } } < / template > < / el-table-column >
< el-table-column label = "类型" width = "100" > < template # default = "{ row }" > { { labelOf ( STORAGE _TYPES , row . storageType ) } } < / template > < / el-table-column >
< el-table-column prop = "siteId" label = "站点" width = "90" / >
< el-table-column label = "状态" width = "90" > < template # default = "{ row }" > { { labelOf ( STORAGE _STATUSES , row . status ) } } < / template > < / el-table-column >
< el-table-column prop = "priority" label = "优先级" width = "70" / >
< el-table-column prop = "zoneCode" label = "区域" width = "80" / >
< el-table-column label = "占用" width = "70" > < template # default = "{ row }" > < el-tag : type = "isStorageOccupied(row.id) ? 'warning' : 'success'" size = "small" > { { isStorageOccupied ( row . id ) ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "入库" width = "60" > < template # default = "{ row }" > { { row . allowInbound ? '是' : '否' } } < / template > < / el-table-column >
< el-table-column label = "出库" width = "60" > < template # default = "{ row }" > { { row . allowOutbound ? '是' : '否' } } < / template > < / el-table-column >
< el-table-column prop = "enabled" label = "启用" width = "70" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" size = "small" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "操作" width = "150" fixed = "right" > < template # default = "{ row }" > < el-button size = "small" link :disabled = "row.isLock" @click ="openStorage(row)" > 编辑 < / el -button > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeStorage(row)" > 删除 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "容器" name = "containers" >
< div class = "toolbar" > < el-input v-model = "keyword" placeholder="搜索编码/名称" clearable /><el-button type="primary" @click="openContainer()" > 新增容器 < / el -button > < / div >
< el-table :data = "filteredContainers" border size = "small" >
< el-table-column prop = "code" label = "编码" width = "140" / >
< div class = "toolbar" >
< el-input v-model = "keyword" placeholder="搜索编码/名称" clearable / >
< el-button type = "primary" @click ="openContainer()" > 新增容器 < / el -button >
< el-button type = "danger" : disabled = "batchContainers.batchCount === 0" @click ="batchDeleteContainers" > 批量删除 ( {{ batchContainers.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredContainers" border size = "small" @ selection -change = " batchContainers.onSelectionChange " >
< el-table-column type = "selection" width = "42" :selectable = "batchContainers.selectable" / >
< el-table-column prop = "code" label = "编码" width = "120" / >
< el-table-column prop = "name" label = "名称" / >
< el-table-column prop = "containerType " label = "类型" width = "120" / >
< el-table-column prop = "status " label = "状态" width = "120" / >
< el-table-column label = "当前位置" > < template # default = "{ row }" > { { containerLocationLabel ( row . id ) } } < / template > < / el-table-column >
< el-table-column prop = "enabled" label = "启用" width = "9 0" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "操作 " width = "1 70" > < template # default = "{ row }" > < el-button size = "small" :disabled = "row.isLock" @click ="openContainer(row)" > 编辑 < / el -button > < el-button size = "small" type = "danger" :disabled = "row.isLock" @click ="removeContainer(row)" > 删除 < / el -button > < / template > < / el-table-column >
< el-table-column label = "类型 " width = "90" > < template # default = "{ row }" > { { labelOf ( CONTAINER _TYPES , row . containerType ) } } < / template > < / el-table-column >
< el-table-column label = "状态 " width = "90" > < template # default = "{ row }" > { { labelOf ( CONTAINER _STATUSES , row . status ) } } < / template > < / el-table-column >
< el-table-column label = "当前位置" min -width = " 120 " > < template # default = "{ row }" > { { containerLocationLabel ( row . id ) } } < / template > < / el-table-column >
< el-table-column prop = "enabled" label = "启用" width = "7 0" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" size = "small" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column prop = "isLock" label = "锁定 " width = "70" > < template # default = "{ row }" > < el-tag : type = "row.isLock ? 'warning' : 'info'" size = "small" > { { row . isLock ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "操作" width = "150" fixed = "right" > < template # default = "{ row }" > < el-button size = "small" link :disabled = "row.isLock" @click ="openContainer(row)" > 编辑 < / el -button > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeContainer(row)" > 删除 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "物料" name = "materials" >
< div class = "toolbar" > < el-input v-model = "keyword" placeholder="搜索编码/名称/规格" clearable /><el-button type="primary" @click="openMaterial()" > 新增物料 < / el -button > < / div >
< el-table :data = "filteredMaterials" border size = "small" >
< el-table-column prop = "code" label = "编码" width = "140" / >
< div class = "toolbar" >
< el-input v-model = "keyword" placeholder="搜索编码/名称/规格" clearable / >
< el-button type = "primary" @click ="openMaterial()" > 新增物料 < / el -button >
< el-button type = "danger" : disabled = "batchMaterials.batchCount === 0" @click ="batchDeleteMaterials" > 批量删除 ( {{ batchMaterials.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredMaterials" border size = "small" @ selection -change = " batchMaterials.onSelectionChange " >
< el-table-column type = "selection" width = "42" :selectable = "batchMaterials.selectable" / >
< el-table-column prop = "code" label = "编码" width = "120" / >
< el-table-column prop = "name" label = "名称" / >
< el-table-column prop = "spec" label = "规格" / >
< el-table-column prop = "unit" label = "单位" width = "9 0" / >
< el-table-column prop = "category" label = "类别" width = "12 0" / >
< el-table-column label = "操作 " width = "1 70" > < template # default = "{ row }" > < el-button size = "small" :disabled = "row.isLock" @click ="openMaterial(row)" > 编辑 < / el -button > < el-button size = "small" type = "danger" :disabled = "row.isLock" @click ="removeMaterial(row)" > 删除 < / el -button > < / template > < / el-table-column >
< el-table-column prop = "unit" label = "单位" width = "7 0" / >
< el-table-column prop = "category" label = "类别" width = "10 0" / >
< el-table-column prop = "enabled" label = "启用 " width = "70" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" size = "small" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "操作" width = "150" fixed = "right" > < template # default = "{ row }" > < el-button size = "small" link :disabled = "row.isLock" @click ="openMaterial(row)" > 编辑 < / el -button > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeMaterial(row)" > 删除 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< / el-tabs >
@@ -75,38 +103,119 @@
< el-select v-model = "locationFilter" clearable placeholder="位置类型"><el-option label="库位" value="Storage" /><el-option label="车辆" value="Car" / > < / el-select >
< el-input v-model = "keyword" placeholder="搜索位置编码/名称" clearable / >
< el-button type = "primary" @click ="openLocation()" > 绑定 / 转移 < / el -button >
< el-button type = "danger" : disabled = "batchLocations.batchCount === 0" @click ="batchUnbindLocations" > 批量解绑 ( {{ batchLocations.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredContainerLocations" border size = "small" >
< el-table-column label = "容器" width = "160" > < template # default = "{ row }" > { { containerName ( row . containerId ) } } < / template > < / el-table-column >
< el-table-column prop = "locationType " label = "位置类型" width = "100" / >
< el-table-column prop = "locationCod e" label = "位置编码 " width = "14 0" / >
< el-table :data = "filteredContainerLocations" border size = "small" @ selection -change = " batchLocations.onSelectionChange " >
< el-table-column type = "selection" width = "42" :selectable = "batchLocations.selectable" / >
< el-table-column label = "容器 " width = "140" > < template # default = "{ row }" > { { containerName ( row . containerId ) } } < / template > < / el-table-column >
< el-table-column prop = "locationTyp e" label = "位置类型 " width = "9 0" / >
< el-table-column prop = "locationCode" label = "位置编码" width = "120" / >
< el-table-column prop = "locationName" label = "位置名称" / >
< el-table-column prop = "status " label = "状态" width = "110" / >
< el-table-column prop = "enteredAt" label = "进入时间" width = "18 0" / >
< el-table-column label = "操作" width = "120 " > < template # default = "{ row }" > < el-button size = "small" type = "danger" :disabled = "row.isLock" @click ="removeLocation(row)" > 解绑 < / el -button > < / template > < / el-table-column >
< el-table-column label = "状态 " width = "80" > < template # default = "{ row }" > { { labelOf ( CONTAINER _LOCATION _STATUSES , row . status ) } } < / template > < / el-table-column >
< el-table-column prop = "enteredAt" label = "进入时间" width = "17 0" / >
< el-table-column label = "操作" width = "90" fixed = "right "> < template # default = "{ row }" > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeLocation(row)" > 解绑 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "容器物料" name = "materials" >
< div class = "toolbar" > < el-input v-model = "keyword" placeholder="搜索" clearable /><el-button type="primary" @click="openContainerMaterial()" > 装料 / 调整 < / el -button > < / div >
< el-table :data = "containerMaterials" border size = "small" >
< el-table-column label = "容器" width = "160" > < template # default = "{ row } "> { { containerName ( row . containerId ) } } < / template > < / el-table-column >
< el-table-column label = "物料" width = "160" > < template # default = "{ row } "> { { materialName ( row . materialId ) } } < / template > < / el-table-column >
< el-table-column prop = "quantity" label = "数量" width = "100" / >
< el-table-column prop = "batchNo" label = "批次" width = "140" / >
< el-table-column prop = "serialNo " label = "序列号" width = "140 " / >
< el-table-column prop = "status " label = "状态" width = "110" / >
< el-table-column label = "操作 " width = "12 0" > < template # default = "{ row }" > < el-button size = "small" type = "danger" :disabled = "row.isLock" @click ="removeContainerMaterial(row)" > 卸料 < / el -button > < / template > < / el-table-column >
< div class = "toolbar" >
< el-input v-model = "keyword" placeholder="搜索" clearable / >
< el-button type = "primary" @click ="openContainerMaterial() "> 装料 / 调整 < / el -button >
< el-button type = "danger" : disabled = "batchContainerMaterials.batchCount === 0" @click ="batchUnloadMaterials "> 批量卸料 ( {{ batchContainerMaterials.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredContainerMaterials" border size = "small" @ selection -change = " batchContainerMaterials.onSelectionChange " >
< el-table-column type = "selection " width = "42" :selectable = "batchContainerMaterials.selectable " / >
< el-table-column label = "容器 " width = "140" > < template # default = "{ row }" > { { containerName ( row . containerId ) } } < / template > < / el-table-column >
< el-table-column label = "物料 " width = "14 0" > < template # default = "{ row }" > { { materialName ( row . materialId ) } } < / template > < / el-table-column >
< el-table-column prop = "quantity" label = "数量" width = "90" / >
< el-table-column prop = "batchNo" label = "批次" width = "120" / >
< el-table-column prop = "serialNo" label = "序列号" width = "120" / >
< el-table-column label = "状态" width = "90" > < template # default = "{ row }" > { { labelOf ( CONTAINER _MATERIAL _STATUSES , row . status ) } } < / template > < / el-table-column >
< el-table-column label = "操作" width = "90" fixed = "right" > < template # default = "{ row }" > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeContainerMaterial(row)" > 卸料 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "历史记录" name = "history" >
< el-table :data = "historyRows" border size = "small" >
< el-table-column prop = "kind" label = "类型" width = "11 0" / >
< el-table-column prop = "eventType" label = "事件" width = "12 0" / >
< el-table-column prop = "operator " label = "操作人" width = "120" / >
< el-table-column prop = "operatedAt" label = "时间" width = "180" / >
< el-table-column prop = "reason " label = "原因 " / >
< el-table-column prop = "kind" label = "类型" width = "8 0" / >
< el-table-column prop = "eventType" label = "事件" width = "10 0" / >
< el-table-column label = "容器 " width = "120" > < template # default = "{ row }" > { { row . containerId ? containerName ( row . containerId ) : '—' } } < / template > < / el-table-column >
< el-table-column label = "库位/位置" min -width = " 120 " > < template # default = "{ row }" > { { historyLocationLabel ( row ) } } < / template > < / el-table-column >
< el-table-column prop = "operator " label = "操作人" width = "100 " / >
< el-table-column prop = "operatedAt" label = "时间" width = "170" / >
< el-table-column prop = "reason" label = "原因" show -overflow -tooltip / >
< / el-table >
< / el-tab-pane >
< / el-tabs >
< / el-tab-pane >
< el-tab-pane label = "搬运配置" name = "transport" lazy >
< el-tabs v-model = "activeTransport" lazy >
< el-tab-pane label = "搬运规则" name = "rules" >
< div class = "toolbar" >
< el-select v-model = "ruleTriggerFilter" clearable placeholder="触发类型" >
< el -option label = "呼叫物料" value = "MaterialCall" / >
< el-option label = "成品下线" value = "FinishedGoodsOffline" / >
< el-option label = "自动转运" value = "AutoTransfer" / >
< / el-select >
< el-button type = "primary" @click ="openRule()" > 新增规则 < / el -button >
< el-button type = "danger" : disabled = "batchRules.batchCount === 0" @click ="batchDeleteRules" > 批量删除 ( {{ batchRules.batchCount }} ) < / el -button >
< / div >
< el-table :data = "filteredRules" border size = "small" @ selection -change = " batchRules.onSelectionChange " >
< el-table-column type = "selection" width = "42" :selectable = "batchRules.selectable" / >
< el-table-column prop = "code" label = "编码" width = "120" / >
< el-table-column prop = "name" label = "名称" / >
< el-table-column label = "触发类型" width = "120" > < template # default = "{ row }" > { { TRANSPORT _TRIGGER _LABELS [ row . triggerType ] ? ? row . triggerType } } < / template > < / el-table-column >
< el-table-column prop = "priority" label = "优先级" width = "80" / >
< el-table-column prop = "enabled" label = "启用" width = "70" > < template # default = "{ row }" > < el-tag : type = "row.enabled ? 'success' : 'info'" size = "small" > { { row . enabled ? '是' : '否' } } < / el-tag > < / template > < / el-table-column >
< el-table-column label = "操作" width = "150" fixed = "right" > < template # default = "{ row }" > < el-button size = "small" link :disabled = "row.isLock" @click ="openRule(row)" > 编辑 < / el -button > < el-button size = "small" link type = "danger" :disabled = "row.isLock" @click ="removeRule(row)" > 删除 < / el -button > < / template > < / el-table-column >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "候选预览" name = "candidates" >
< el-form inline class = "preview-form" >
< el-form-item label = "触发类型" > < el-select v-model = "previewForm.triggerType"><el-option label="呼叫物料" value="MaterialCall" /><el-option label="成品下线" value="FinishedGoodsOffline" /><el-option label="自动转运" value="AutoTransfer" / > < / el-select > < / el-form-item >
< el-form-item label = "呼叫站点" > < el-input v-model = "previewForm.requestSiteId" placeholder="站点 ID" / > < / el-form-item >
< el-form-item label = "物料" > < el-select v-model = "previewForm.materialId" clearable><el-option v-for="m in materials" :key="m.id" :label="m.code" :value="m.id" / > < / el-select > < / el-form-item >
< el-form-item label = "数量" > < el-input-number v-model = "previewForm.quantity" :min="0" :precision="4" / > < / el-form-item >
< el-form-item label = "容器" > < el-select v-model = "previewForm.containerId" clearable><el-option v-for="c in containers" :key="c.id" :label="c.code" :value="c.id" / > < / el-select > < / el-form-item >
< el-form-item > < el-button type = "primary" :loading = "previewLoading" @click ="runPreview" > 预览候选 < / el -button > < el-button type = "success" :loading = "previewLoading" @click ="generateFromPreview" > 生成任务 < / el -button > < / el-form-item >
< / el-form >
< el-table : data = "previewResult?.candidates ?? []" border size = "small" >
< el-table-column prop = "ruleCode" label = "规则" width = "110" / >
< el-table-column prop = "sourceStorageCode" label = "起点库位" width = "110" / >
< el-table-column prop = "targetStorageCode" label = "终点库位" width = "110" / >
< el-table-column prop = "containerCode" label = "容器" width = "100" / >
< el-table-column prop = "materialCode" label = "物料" width = "100" / >
< el-table-column prop = "quantity" label = "数量" width = "80" / >
< el-table-column prop = "score" label = "评分" width = "70" / >
< / el-table >
< / el-tab-pane >
< el-tab-pane label = "搬运任务" name = "tasks" >
< div class = "toolbar" >
< el-select v-model = "taskStatusFilter" clearable placeholder="状态" @change="loadTransportTasks"><el-option v-for="s in taskStatuses" :key="s" :label="s" :value="s" / > < / el-select >
< el-button @click ="loadTransportTasks" > 刷新 < / el -button >
< el-button type = "danger" : disabled = "batchTasks.batchCount === 0" @click ="batchCancelTasks" > 批量取消 ( {{ batchTasks.batchCount }} ) < / el -button >
< / div >
< el-table :data = "transportTasks" border size = "small" @ selection -change = " batchTasks.onSelectionChange " >
< el-table-column type = "selection" width = "42" :selectable = "taskSelectable" / >
< el-table-column label = "业务类型" width = "110" > < template # default = "{ row }" > { { TRANSPORT _TRIGGER _LABELS [ row . businessType ] ? ? row . businessType } } < / template > < / el-table-column >
< el-table-column label = "起点" width = "100" > < template # default = "{ row }" > { { storageCode ( row . sourceStorageId ) } } < / template > < / el-table-column >
< el-table-column label = "终点" width = "100" > < template # default = "{ row }" > { { storageCode ( row . targetStorageId ) } } < / template > < / el-table-column >
< el-table-column label = "容器" width = "100" > < template # default = "{ row }" > { { containerName ( row . containerId ) } } < / template > < / el-table-column >
< el-table-column label = "状态" width = "100" > < template # default = "{ row }" > < el-tag : type = "TRANSPORT_TASK_STATUS_TYPE[row.status] ?? 'info'" size = "small" > { { row . status } } < / el-tag > < / template > < / el-table-column >
< el-table-column prop = "taskPriority" label = "优先级" width = "70" / >
< el-table-column prop = "createdAt" label = "创建时间" width = "170" / >
< el-table-column prop = "reason" label = "原因" show -overflow -tooltip / >
< el-table-column prop = "errorMessage" label = "错误" show -overflow -tooltip / >
< el-table-column label = "操作" width = "220" fixed = "right" >
< template # default = "{ row }" >
< el-button v-if = "row.status === 'Pending'" size="small" link @click="reserveTask(row)" > 预占 < / el -button >
< el-button v-if = "row.status === 'Reserved'" size="small" link @click="completeTask(row)" > 完成 < / el -button >
< el-button v-if = "['Pending','Reserved','Failed'].includes(row.status)" size="small" link type="danger" @click="cancelTask(row)" > 取消 < / el -button >
< / template >
< / el-table-column >
< / el-table >
< / el-tab-pane >
< / el-tabs >
@@ -114,55 +223,23 @@
< / el-tabs >
< / el-card >
< el-dialog v-model = "dialogVisible" :title="dialogTitle" width="560px" >
< el -form label -width = " 96px " >
< template v-if = "dialogKind === 'area' || dialogKind === 'container'" >
< el -form -item label = "编码" > < el-input v-model = "masterForm.code" / > < / el-form-item >
< el-form-item label = "名称" > < el-input v-model = "masterForm.name" / > < / el-form-item >
< el-form-item label = "类型" > < el-input v-model = "masterForm.type" / > < / el-form-item >
< el-form-item v-if = "dialogKind === 'container'" label="状态"><el-input v-model="masterForm.status" / > < / el-form-item >
< el-form-item label = "启用" > < el-switch v-model = "mas terForm.enabled" / > < / el-form-item >
< el-form-item label = "锁定" > < el-switch v-model = "masterForm.isLock" / > < / el-form-item >
< / template >
< template v-if = "dialogKind === ' storage'" >
< el -form -item label = "库区" > < el-select v-model = "storageForm.areaId"><el-option v-for="a in areas" :key="a.id" :label="a.name" :value="a.id" / > < / el-select > < / el-form-item >
< el-form-item label = "编码" > < el-input v-model = "storageForm.code" / > < / el-form-item >
< el-form-item label = "名称" > < el-input v-model = "storageForm.name" / > < / el-form-item >
< el-form-item label = "类型" > < el-input v-model = "storageForm.storageType" / > < / el-form-item >
< el-form-item label = "站点" > < el-input v-model = "storageForm.siteId" / > < / el-form-item >
< el-form-item label = "容量" > < el-input-number v-model = "storageForm.capacity" :min="0" / > < / el-form-item >
< el-form-item label = "启用" > < el-switch v-model = "storageForm.enabled" / > < / el-form-item >
< el-form-item label = "锁定" > < el-switch v-model = "storageForm.isLock" / > < / el-form-item >
< / template >
< template v-if = "dialogKind === 'material'" >
< el -form -item label = "编码" > < el-input v-model = "materialForm.code" / > < / el-form-item >
< el-form-item label = "名称" > < el-input v-model = "materialForm.name" / > < / el-form-item >
< el-form-item label = "规格" > < el-input v-model = "materialForm.spec" / > < / el-form-item >
< el-form-item label = "单位" > < el-input v-model = "materialForm.unit" / > < / el-form-item >
< el-form-item label = "类别" > < el-input v-model = "materialForm.category" / > < / el-form-item >
< el-form-item label = "启用" > < el-switch v-model = "materialForm.enabled" / > < / el-form-item >
< el-form-item label = "锁定" > < el-switch v-model = "materialForm.isLock" / > < / el-form-item >
< / template >
< template v-if = "dialogKind === 'location'" >
< el -form -item label = "容器" > < el-select v-model = "locationForm.containerId"><el-option v-for="c in containers" :key="c.id" :label="c.code" :value="c.id" / > < / el-select > < / el-form-item >
< el-form-item label = "位置类型" > < el-select v-model = "locationForm.locationType"><el-option label="库位" value="Storage" /><el-option label="车辆" value="Car" / > < / el-select > < / el-form-item >
< el-form-item v-if = "locationForm.locationType === 'Storage'" label="库位"><el-select v-model="locationForm.locationId"><el-option v-for="s in storages" :key="s.id" :label="`${s.code} ${s.name}`" :value="s.id" / > < / el-select > < / el-form-item >
< el-form-item v-else label = "车辆 ID" > < el-input v-model = "locationForm.locationId" / > < / el-form-item >
< el-form-item label = "状态" > < el-select v-model = "locationForm.status"><el-option label="Active" value="Active" /><el-option label="Locked" value="Locked" /><el-option label="Exception" value="Exception" / > < / el-select > < / el-form-item >
< / template >
< template v-if = "dialogKind === 'containerMaterial'" >
< el -form -item label = "容器" > < el-select v-model = "containerMaterialForm.containerId"><el-option v-for="c in containers" :key="c.id" :label="c.code" :value="c.id" / > < / el-select > < / el-form-item >
< el-form-item label = "物料" > < el-select v-model = "containerMaterialForm.materialId"><el-option v-for="m in materials" :key="m.id" :label="m.code" :value="m.id" / > < / el-select > < / el-form-item >
< el-form-item label = "数量" > < el-input-number v-model = "containerMaterialForm.quantity" :min="0.0001" :precision="4" / > < / el-form-item >
< el-form-item label = "批次" > < el-input v-model = "containerMaterialForm.batchNo" / > < / el-form-item >
< el-form-item label = "序列号" > < el-input v-model = "containerMaterialForm.serialNo" / > < / el-form-item >
< el-form-item label = "状态" > < el-select v-model = "containerMaterialForm.status"><el-option label="Loaded" value="Loaded" /><el-option label="Adjusted" value="Adjusted" /><el-option label="Frozen" value="Frozen" / > < / el-select > < / el-form-item >
< / template >
< el-form-item label = "备注" > < el-input v-model = "currentRemark" type="textarea" / > < / el-form-item >
< el-form-item label = "扩展 JSON" > < el-input v-model = "currentExtend" type="textarea" / > < / el-form-item >
< / el-form >
< template # footer > < el-button @click ="dialogVisible = false" > 取消 < / el -button > < el-button type = "primary" :loading = "saving" @click ="submitDialog" > 保存 < / el -button > < / template >
< / el-dialog >
< WmsEntityDialog
v -model :visible = "dialogVisible"
:kind = "dialogKind"
:saving = "saving"
:areas = "areas"
:storages = "storages"
:containers = "containers"
:materials = "materials"
:occupied-storage-ids = "occupiedStorageIds"
:master-form = "masterForm"
:storage-form = " storageForm"
:material-form = "materialForm"
:location-form = "locationForm"
:container-material-form = "containerMaterialForm"
:rule-form = "ruleForm"
@save ="submitDialog"
/ >
< / div >
< / template >
@@ -171,19 +248,30 @@ import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage , ElMessageBox } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue'
import * as wmsApi from '@/api/wms'
import WmsEntityDialog , { type DialogKind } from '@/views/admin/warehouse/WmsEntityDialog.vue'
import { useWmsBatchActions } from '@/views/admin/warehouse/useWmsBatchActions'
import {
STORAGE _TYPES , STORAGE _STATUSES , CONTAINER _TYPES , CONTAINER _STATUSES ,
CONTAINER _LOCATION _STATUSES , CONTAINER _MATERIAL _STATUSES ,
TRANSPORT _TRIGGER _LABELS , TRANSPORT _TASK _STATUS _TYPE , labelOf , truncate
} from '@/views/admin/warehouse/wmsOptions'
import type {
WarehouseArea , Storage , Container , Material , ContainerLocation , ContainerMaterial ,
ContainerLocationHistory , ContainerMaterialHistory , MasterDataPayload , StoragePayload ,
MaterialPayload , ContainerLocationPayload , ContainerMaterialPayload
MaterialPayload , ContainerLocationPayload , ContainerMaterialPayload ,
WmsTransportRule , WmsTransportTask , TransportRulePayload , TransportRequestQuery , TransportCandidatePreview
} from '@/types/wms'
type DialogKind = 'area' | 'storage' | 'container' | 'material' | 'location' | 'containerMaterial'
const activeGroup = ref ( 'master' )
const activeMaster = ref ( 'areas' )
const activeRelation = ref ( 'locations' )
const activeTransport = ref ( 'rules' )
const keyword = ref ( '' )
const locationFilter = ref ( '' )
const ruleTriggerFilter = ref ( '' )
const taskStatusFilter = ref ( '' )
const previewLoading = ref ( false )
const taskStatuses = [ 'Pending' , 'Reserved' , 'Dispatched' , 'InTransit' , 'Completed' , 'Failed' , 'Cancelled' ]
const loading = ref ( false )
const saving = ref ( false )
const dialogVisible = ref ( false )
@@ -197,29 +285,33 @@ const containerLocations = ref<ContainerLocation[]>([])
const containerMaterials = ref < ContainerMaterial [ ] > ( [ ] )
const locationHistory = ref < ContainerLocationHistory [ ] > ( [ ] )
const materialHistory = ref < ContainerMaterialHistory [ ] > ( [ ] )
const transportRules = ref < WmsTransportRule [ ] > ( [ ] )
const transportTasks = ref < WmsTransportTask [ ] > ( [ ] )
const previewResult = ref < TransportCandidatePreview | null > ( null )
const masterForm = reactive < MasterDataPayload > ( baseMaster ( ) )
const storageForm = reactive < StoragePayload > ( baseStorage ( ) )
const materialForm = reactive < MaterialPayload > ( baseMaterial ( ) )
const locationForm = reactive < ContainerLocationPayload > ( baseLocation ( ) )
const containerMaterialForm = reactive < ContainerMaterialPayload > ( baseContainerMaterial ( ) )
const ruleForm = reactive < TransportRulePayload > ( baseRule ( ) )
const previewForm = reactive < TransportRequestQuery > ( { triggerType : 'MaterialCall' , priority : 0 , reason : '' } )
const dialogTitle = computed ( ( ) = > ( {
area : '库区' ,
storage : '库位' ,
container : '容器' ,
material : '物料' ,
location : '容器位置' ,
containerMaterial : '容器物料'
} [ dialogKind . value ] ) )
const batchAreas = useWmsBatchActions < WarehouseArea > ( )
const batchStorages = useWmsBatchActions < Storage > ( )
const batchContainers = useWmsBatchActions < Container > ( )
const batchMaterials = useWmsBatchActions < Material > ( )
const batchRules = useWmsBatchActions < WmsTransportRule > ( )
const batchLocations = useWmsBatchActions < ContainerLocation > ( )
const batchContainerMaterials = useWmsBatchActions < ContainerMaterial > ( )
const batchTasks = useWmsBatchActions < WmsTransportTask > ( )
const currentRemark = computed ( {
get : ( ) => formOf ( dialogKind . value ) . remark ,
set : ( v : string ) => { formOf ( dialogKind . value ) . remark = v }
} )
const currentExtend = computed ( {
get : ( ) => formOf ( dialogKind . value ) . extend ,
set : ( v : string ) => { formOf ( dialogKind . value ) . extend = v }
const occupiedStorageIds = computed ( ( ) => {
const set = new Set < string > ( )
for ( const loc of containerLocations . value ) {
if ( loc . locationType === 'Storage' ) set . add ( loc . locationId )
}
return set
} )
const filteredAreas = computed ( ( ) => filterRows ( areas . value , keyword . value ) )
@@ -229,11 +321,18 @@ const filteredMaterials = computed(() => filterRows(materials.value, keyword.val
const filteredContainerLocations = computed ( ( ) => containerLocations . value . filter ( ( r ) =>
( ! locationFilter . value || r . locationType === locationFilter . value ) &&
( ! keyword . value || ` ${ r . locationCode } ${ r . locationName } ` . toLowerCase ( ) . includes ( keyword . value . toLowerCase ( ) ) ) ) )
const filteredContainerMaterials = computed ( ( ) => filterRows ( containerMaterials . value , keyword . value ) )
const filteredRules = computed ( ( ) => transportRules . value . filter ( ( r ) => ! ruleTriggerFilter . value || r . triggerType === ruleTriggerFilter . value ) )
const historyRows = computed ( ( ) => [
... locationHistory . value . map ( ( x ) => ( { ... x , kind : '位置' } ) ) ,
... materialHistory . value . map ( ( x ) => ( { ... x , kind : '物料' } ) )
] . sort ( ( a , b ) => String ( b . operatedAt ) . localeCompare ( String ( a . operatedAt ) ) ) )
function isStorageOccupied ( id : string ) { return occupiedStorageIds . value . has ( id ) }
function taskSelectable ( row : WmsTransportTask ) {
return ! row . isLock && [ 'Pending' , 'Reserved' , 'Failed' ] . includes ( row . status )
}
async function loadAll ( ) {
loading . value = true
try {
@@ -243,6 +342,8 @@ async function loadAll() {
] )
areas . value = a ; storages . value = s ; containers . value = c ; materials . value = m
containerLocations . value = cl ; containerMaterials . value = cm ; locationHistory . value = lh ; materialHistory . value = mh
await loadTransportRules ( )
await loadTransportTasks ( )
} finally {
loading . value = false
}
@@ -250,24 +351,60 @@ async function loadAll() {
function openArea ( row ? : WarehouseArea ) { dialogKind . value = 'area' ; Object . assign ( masterForm , baseMaster ( row ) ) ; dialogVisible . value = true }
function openStorage ( row ? : Storage ) { dialogKind . value = 'storage' ; Object . assign ( storageForm , baseStorage ( row ) ) ; dialogVisible . value = true }
function openContainer ( row ? : Container ) { dialogKind . value = 'container' ; Object . assign ( masterForm , baseMaster ( row ) ) ; dialogVisible . value = true }
function openContainer ( row ? : Container ) {
dialogKind . value = 'container'
Object . assign ( masterForm , baseMaster ( row ) )
if ( row ) masterForm . type = row . containerType
dialogVisible . value = true
}
function openMaterial ( row ? : Material ) { dialogKind . value = 'material' ; Object . assign ( materialForm , baseMaterial ( row ) ) ; dialogVisible . value = true }
function openLocation ( row ? : ContainerLocation ) { dialogKind . value = 'location' ; Object . assign ( locationForm , baseLocation ( row ) ) ; dialogVisible . value = true }
function openContainerMaterial ( row ? : ContainerMaterial ) { dialogKind . value = 'containerMaterial' ; Object . assign ( containerMaterialForm , baseContainerMaterial ( row ) ) ; dialogVisible . value = true }
function openRule ( row ? : WmsTransportRule ) { dialogKind . value = 'rule' ; Object . assign ( ruleForm , baseRule ( row ) ) ; dialogVisible . value = true }
async function loadTransportRules ( ) { transportRules . value = await wmsApi . listTransportRules ( ) }
async function loadTransportTasks ( ) { transportTasks . value = await wmsApi . listTransportTasks ( { status : taskStatusFilter . value || undefined } ) }
async function runPreview ( ) {
previewLoading . value = true
try {
previewResult . value = await wmsApi . previewTransportCandidates ( { ... previewForm } )
ElMessage . success ( ` 找到 ${ previewResult . value . candidates . length } 个候选 ` )
} finally { previewLoading . value = false }
}
async function generateFromPreview ( ) {
previewLoading . value = true
try {
await wmsApi . generateTransportTask ( { ... previewForm } )
ElMessage . success ( '任务已生成' )
await loadTransportTasks ( )
activeGroup . value = 'transport'
activeTransport . value = 'tasks'
} finally { previewLoading . value = false }
}
async function submitDialog ( ) {
saving . value = true
const kind = dialogKind . value
try {
JSON . parse ( currentExtend . value || '{}' )
if ( dialogKind . value === 'area' ) await wmsApi . saveArea ( masterForm )
else if ( dialogKind . value === 'container' ) await wmsApi . saveContainer ( masterForm )
else if ( dialogKind . value === 'storage' ) await wmsApi . saveStorage ( storageForm )
else if ( dialogKind . value === 'material' ) await wmsApi . saveMaterial ( materialForm )
else if ( dialogKind . value === 'location' ) await wmsApi . saveContainerLocation ( locationForm )
JSON . parse ( ( kind === 'storage' ? storageForm : kind === 'material' ? materialForm : kind === 'rule' ? ruleForm : masterForm ) . extend || '{}' )
if ( kind === 'area' ) await wmsApi . saveArea ( masterForm )
else if ( kind === 'container' ) await wmsApi . saveContainer ( masterForm )
else if ( kind === 'storage' ) await wmsApi . saveStorage ( storageForm )
else if ( kind === 'material' ) await wmsApi . saveMaterial ( materialForm )
else if ( kind === 'location' ) await wmsApi . saveContainerLocation ( locationForm )
else if ( kind === 'rule' ) {
JSON . parse ( ruleForm . sourceSelectorJson || '{}' )
JSON . parse ( ruleForm . targetSelectorJson || '{}' )
JSON . parse ( ruleForm . taskOptionsJson || '{}' )
await wmsApi . saveTransportRule ( ruleForm )
}
else await wmsApi . saveContainerMaterial ( containerMaterialForm )
ElMessage . success ( '已保存' )
dialogVisible . value = false
await loadAll ( )
if ( kind === 'rule' ) await loadTransportRules ( )
} catch ( e ) {
ElMessage . error ( e instanceof Error ? e . message : String ( e ) )
} finally {
@@ -278,40 +415,107 @@ async function submitDialog() {
async function confirmDelete ( message : string ) {
await ElMessageBox . confirm ( message , '确认操作' , { type : 'warning' } )
}
async function removeArea ( row : WarehouseArea ) { await confirmDelete ( '删除该库区?' ) ; await wmsApi . deleteArea ( row . id , row . version ) ; await loadAll ( ) }
async function removeStorage ( row : Storage ) { await confirmDelete ( '删除该库位?' ) ; await wmsApi . deleteStorage ( row . id , row . version ) ; await loadAll ( ) }
async function removeContainer ( row : Container ) { await confirmDelete ( '删除该容器?' ) ; await wmsApi . deleteContainer ( row . id , row . version ) ; await loadAll ( ) }
async function removeMaterial ( row : Material ) { await confirmDelete ( '删除该物料?' ) ; await wmsApi . deleteMaterial ( row . id , row . version ) ; await loadAll ( ) }
async function removeLocation ( row : ContainerLocation ) { await confirmDelete ( '解绑该容器位置?' ) ; await wmsApi . deleteContainerLocation ( row . containerId ) ; await loadAll ( ) }
async function removeContainerMaterial ( row : ContainerMaterial ) { await confirmDelete ( '卸载该容器物料?' ) ; await wmsApi . deleteContainerMaterial ( row . id ) ; await loadAll ( ) }
async function removeRule ( row : WmsTransportRule ) { await confirmDelete ( '删除该规则?' ) ; await wmsApi . deleteTransportRule ( row . id , row . version ) ; await loadTransportRules ( ) }
async function reserveTask ( row : WmsTransportTask ) { await wmsApi . reserveTransportTask ( row . id ) ; ElMessage . success ( '已预占' ) ; await loadTransportTasks ( ) }
async function completeTask ( row : WmsTransportTask ) { await wmsApi . completeTransportTask ( row . id ) ; ElMessage . success ( '已完成' ) ; await loadAll ( ) }
async function cancelTask ( row : WmsTransportTask ) { await wmsApi . cancelTransportTask ( row . id ) ; ElMessage . success ( '已取消' ) ; await loadTransportTasks ( ) }
async function batchDeleteAreas ( ) {
const r = await batchAreas . runBatch ( { actionLabel : '批量删除' , confirmMessage : '确认删除选中的 {n} 个库区?' , run : ( row ) => wmsApi . deleteArea ( row . id , row . version ) } )
if ( r . ok > 0 ) await loadAll ( )
}
async function batchDeleteStorages ( ) {
const r = await batchStorages . runBatch ( { actionLabel : '批量删除' , confirmMessage : '确认删除选中的 {n} 个库位?' , run : ( row ) => wmsApi . deleteStorage ( row . id , row . version ) } )
if ( r . ok > 0 ) await loadAll ( )
}
async function batchDeleteContainers ( ) {
const r = await batchContainers . runBatch ( { actionLabel : '批量删除' , confirmMessage : '确认删除选中的 {n} 个容器?' , run : ( row ) => wmsApi . deleteContainer ( row . id , row . version ) } )
if ( r . ok > 0 ) await loadAll ( )
}
async function batchDeleteMaterials ( ) {
const r = await batchMaterials . runBatch ( { actionLabel : '批量删除' , confirmMessage : '确认删除选中的 {n} 个物料?' , run : ( row ) => wmsApi . deleteMaterial ( row . id , row . version ) } )
if ( r . ok > 0 ) await loadAll ( )
}
async function batchDeleteRules ( ) {
const r = await batchRules . runBatch ( { actionLabel : '批量删除' , confirmMessage : '确认删除选中的 {n} 条规则?' , run : ( row ) => wmsApi . deleteTransportRule ( row . id , row . version ) } )
if ( r . ok > 0 ) await loadTransportRules ( )
}
async function batchUnbindLocations ( ) {
const r = await batchLocations . runBatch ( { actionLabel : '批量解绑' , confirmMessage : '确认解绑选中的 {n} 条容器位置?' , run : ( row ) => wmsApi . deleteContainerLocation ( row . containerId ) } )
if ( r . ok > 0 ) await loadAll ( )
}
async function batchUnloadMaterials ( ) {
const r = await batchContainerMaterials . runBatch ( { actionLabel : '批量卸料' , confirmMessage : '确认卸载选中的 {n} 条容器物料?' , run : ( row ) => wmsApi . deleteContainerMaterial ( row . id ) } )
if ( r . ok > 0 ) await loadAll ( )
}
async function batchCancelTasks ( ) {
const r = await batchTasks . runBatch ( {
actionLabel : '批量取消' ,
confirmMessage : '确认取消选中的 {n} 个搬运任务?' ,
canRun : ( row ) => [ 'Pending' , 'Reserved' , 'Failed' ] . includes ( row . status ) ,
run : ( row ) => wmsApi . cancelTransportTask ( row . id )
} )
if ( r . ok > 0 ) await loadTransportTasks ( )
}
function storageCode ( id : string ) { return storages . value . find ( ( s ) => s . id === id ) ? . code ? ? id }
function areaName ( id : string ) { return areas . value . find ( ( a ) => a . id === id ) ? . name ? ? id }
function containerName ( id : string ) { const c = containers . value . find ( ( x ) => x . id === id ) ; return c ? ` ${ c . code } ${ c . name } ` : id }
function materialName ( id : string ) { const m = materials . value . find ( ( x ) => x . id === id ) ; return m ? ` ${ m . code } ${ m . name } ` : id }
function containerName ( id : string ) { const c = containers . value . find ( ( x ) => x . id === id ) ; return c ? ` ${ c . code } ` : id }
function materialName ( id : string ) { const m = materials . value . find ( ( x ) => x . id === id ) ; return m ? ` ${ m . code } ` : id }
function containerLocationLabel ( id : string ) {
const loc = containerLocations . value . find ( ( x ) => x . containerId === id )
return loc ? ` ${ loc . locationType } : ${ loc . locationCode || loc . locationId } ` : '未绑定'
}
function historyLocationLabel ( row : ContainerLocationHistory | ContainerMaterialHistory ) {
if ( 'toLocationId' in row && row . toLocationId ) return ` ${ row . toLocationType } : ${ row . toLocationId } `
if ( 'fromLocationId' in row && row . fromLocationId ) return ` ${ row . fromLocationType } : ${ row . fromLocationId } `
return '—'
}
function filterRows < T extends Record < string , unknown > > ( rows : T [ ] , q : string ) {
if ( ! q ) return rows
const s = q . toLowerCase ( )
return rows . filter ( ( r ) => Object . values ( r ) . some ( ( v ) => String ( v ? ? '' ) . toLowerCase ( ) . includes ( s ) ) )
}
function formOf ( kind : DialogKind ) : { remark : string ; extend : string } {
if ( kind === 'storage ') return storageForm
if ( kind === 'material' ) return materialForm
if ( kind === 'location' ) return locationForm
if ( kind === 'containerMaterial' ) return containerMaterialForm
return masterForm
}
function baseCommon ( row ? : { id : string ; version : number ; isLock : boolean ; remark : string ; extend : string } ) {
return { id : row ? . id , version : row ? . version , isLock : row ? . isLock ? ? false , remark : row ? . remark ? ? '' , extend : row ? . extend ? ? '{}' }
function baseCommon ( row ? : { id : string ; version : number ; isLock : boolean ; remark : string ; extend : string ; updatedAt ? : string } ) {
return { id : row ? . id , version : row ? . version , isLock : row ? . isLock ? ? false , remark : row ? . remark ? ? ' ', extend : row ? . extend ? ? '{}' , updatedAt : row ? . updatedAt }
}
function baseMaster ( row ? : WarehouseArea | Container ) : MasterDataPayload {
return { ... baseCommon ( row ) , code : row ? . code ? ? '' , name : row ? . name ? ? '' , type : 'type' in ( row ? ? { } ) ? ( row as WarehouseArea ) . type : ( row as Container | undefined ) ? . containerType ? ? '' , status : ( row as Container | undefined ) ? . status ? ? 'Idle' , enabled : row ? . enabled ? ? true , sortOrder : ( row as WarehouseArea | undefined ) ? . sortOrder ? ? 0 }
return {
... baseCommon ( row ) ,
code : row ? . code ? ? '' ,
name : row ? . name ? ? '' ,
type : 'type' in ( row ? ? { } ) ? ( row as WarehouseArea ) . type : ( row as Container | undefined ) ? . containerType ? ? 'Box' ,
status : ( row as Container | undefined ) ? . status ? ? 'Idle' ,
enabled : row ? . enabled ? ? true ,
sortOrder : ( row as WarehouseArea | undefined ) ? . sortOrder ? ? 0
}
}
function baseStorage ( row ? : Storage ) : StoragePayload {
return { ... baseCommon ( row ) , areaId : row ? . areaId ? ? areas . value [ 0 ] ? . id ? ? '' , code : row ? . code ? ? '' , name : row ? . name ? ? '' , storageType : row ? . storageType ? ? 'Storage' , siteId : row ? . siteId ? ? '' , capacity : row ? . capacity ? ? 0 , enabled : row ? . enabled ? ? true }
return {
... baseCommon ( row ) , areaId : row ? . areaId ? ? areas . value [ 0 ] ? . id ? ? '' , code : row ? . code ? ? '' , name : row ? . name ? ? '' ,
storageType : row ? . storageType ? ? 'Storage' , siteId : row ? . siteId ? ? '' , capacity : row ? . capacity ? ? 0 ,
status : row ? . status ? ? 'Available' , usage : row ? . usage ? ? '' , priority : row ? . priority ? ? 0 , zoneCode : row ? . zoneCode ? ? '' ,
allowInbound : row ? . allowInbound ? ? true , allowOutbound : row ? . allowOutbound ? ? true , enabled : row ? . enabled ? ? true
}
}
function baseRule ( row ? : WmsTransportRule ) : TransportRulePayload {
const defaultSource = JSON . stringify ( { schemaVersion : 1 , storage : { requireOccupied : true , allowOutbound : true } , container : { excludeReserved : true } , material : { statusIn : [ 'Loaded' ] } , ranking : [ { field : 'enteredAt' , direction : 'asc' } ] } , null , 2 )
const defaultTarget = JSON . stringify ( { schemaVersion : 1 , storage : { requireEmpty : true , allowInbound : true , requireSiteId : true } , ranking : [ { field : 'storagePriority' , direction : 'desc' } ] } , null , 2 )
const defaultOptions = JSON . stringify ( { autoReserve : true , requireManualConfirm : false , allowSameStorage : false , reservationTtlSeconds : 600 , dispatchMode : 'Manual' , maxCandidateCount : 20 } , null , 2 )
return {
... baseCommon ( row ) , code : row ? . code ? ? '' , name : row ? . name ? ? '' , triggerType : row ? . triggerType ? ? 'MaterialCall' ,
enabled : row ? . enabled ? ? true , priority : row ? . priority ? ? 0 ,
sourceSelectorJson : row ? . sourceSelectorJson ? ? defaultSource ,
targetSelectorJson : row ? . targetSelectorJson ? ? defaultTarget ,
taskOptionsJson : row ? . taskOptionsJson ? ? defaultOptions
}
}
function baseMaterial ( row ? : Material ) : MaterialPayload {
return { ... baseCommon ( row ) , code : row ? . code ? ? '' , name : row ? . name ? ? '' , spec : row ? . spec ? ? '' , unit : row ? . unit ? ? 'pcs' , category : row ? . category ? ? '' , enabled : row ? . enabled ? ? true }
@@ -332,7 +536,8 @@ onMounted(loadAll)
. page - header { display : flex ; justify - content : space - between ; align - items : center ; gap : 16 px ; }
. page - header h2 { margin : 0 0 4 px ; font - size : 20 px ; color : var ( -- mg - text - light ) ; }
. page - header p { margin : 0 ; color : var ( -- mg - text - muted ) ; font - size : 13 px ; }
. toolbar { display : flex ; gap : 10 px ; align - items : center ; margin : 0 0 12 px ; }
. toolbar { display : flex ; gap : 10 px ; align - items : center ; margin : 0 0 12 px ; flex - wrap : wrap ; }
. toolbar . el - input { max - width : 260 px ; }
. toolbar . el - select { width : 160 px ; }
. preview - form { margin - bottom : 12 px ; flex - wrap : wrap ; }
< / style >