Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
factory_front
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
qinjianhui
factory_front
Commits
38d2938d
Commit
38d2938d
authored
Nov 12, 2025
by
wusiyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 新增备货计划页面1000860
parent
1cf1eea5
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2116 additions
and
4 deletions
+2116
-4
src/router/index.ts
+9
-1
src/router/menu.ts
+6
-2
src/types/api/warehouse.ts
+10
-1
src/views/warehouse/stockingPlan.vue
+2091
-0
No files found.
src/router/index.ts
View file @
38d2938d
...
...
@@ -31,6 +31,7 @@ import receiptDoc from '@/views/warehouse/receiptDoc.vue'
import
issueDoc
from
'@/views/warehouse/issueDoc.vue'
import
ExternalAuthorisationPage
from
'@/views/system/externalAuthorisationPage.vue'
import
CustomersPage
from
'@/views/system/CustomersPage.vue'
import
stockingPlan
from
'@/views/warehouse/stockingPlan.vue'
const
router
=
createRouter
({
history
:
createWebHistory
(),
routes
:
[
...
...
@@ -129,7 +130,7 @@ const router = createRouter({
meta
:
{
title
:
'下载生产客户端'
,
},
component
:
()
=>
{},
component
:
()
=>
{
},
beforeEnter
()
{
// 假设你的下载链接是这个
const
downloadLink
=
'/exeFiles/JomallProductionAssistantSetup.exe'
...
...
@@ -280,6 +281,13 @@ const router = createRouter({
component
:
issueDoc
,
},
{
path
:
'/warehouse/stocking-plan'
,
meta
:
{
title
:
'备货计划'
,
},
component
:
stockingPlan
,
},
{
path
:
'/warehouse/warning'
,
meta
:
{
title
:
'仓库预警'
,
...
...
src/router/menu.ts
View file @
38d2938d
...
...
@@ -88,7 +88,6 @@ const menu: MenuItem[] = [
id
:
124
,
label
:
'出库单'
,
},
{
index
:
'/warehouse/manage'
,
id
:
121
,
...
...
@@ -99,6 +98,11 @@ const menu: MenuItem[] = [
id
:
122
,
label
:
'库位管理'
,
},
{
index
:
'/warehouse/stocking-plan'
,
id
:
124
,
label
:
'备货计划'
,
},
],
},
...
...
@@ -216,7 +220,7 @@ const menu: MenuItem[] = [
label
:
'工厂设置'
,
},
],
},
},
// {
// index: '',
// id: 3,
...
...
src/types/api/warehouse.ts
View file @
38d2938d
...
...
@@ -55,7 +55,7 @@ export interface InterWarehouseBase {
supplierItemNo
?:
string
|
null
}
// 主表列表ts
export
interface
InterWarehousePage
extends
InterWarehouseBase
{}
export
interface
InterWarehousePage
extends
InterWarehouseBase
{
}
// 子表列表ts
export
interface
InterWarehouseDetail
extends
InterWarehouseBase
{
productList
:
InterProductList
[]
...
...
@@ -117,3 +117,12 @@ export interface ILocation {
locationId
?:
number
|
null
locationCode
?:
string
|
null
}
export
interface
stockingPlanSearchForm
{
warehouseId
?:
number
|
string
warehouseSku
?:
string
outNo
?:
string
timeType
?:
string
startTime
?:
string
endTime
?:
string
}
src/views/warehouse/stockingPlan.vue
0 → 100644
View file @
38d2938d
<
template
>
<div
class=
"page card h-100 flex-gap-10 overflow-hidden flex"
>
<div
class=
"left"
>
<ElTree
ref=
"treeRef"
default-expand-all
:expand-on-click-node=
"false"
:default-expanded-keys=
"[]"
:highlight-current=
"true"
node-key=
"code"
:data=
"treeData"
:props=
"
{ children: 'children', label: 'name' }"
@node-click="nodeClick"
>
<template
#
default=
"
{ data }">
<div
class=
"tree-node"
>
<div
class=
"tree-node-label"
>
{{
data
.
name
}}
</div>
<div
v-if=
"data.countQuantity || data.countQuantity === 0"
class=
"tree-node-count"
>
{{
`(${data.countQuantity
}
)`
}}
<
/div
>
<
/div
>
<
/template
>
<
/ElTree
>
<
/div
>
<
div
class
=
"right"
>
<
div
class
=
"delivery-note-page flex-column card h-100 overflow-hidden"
>
<
splitDiv
size
=
"50"
>
<
template
#
top
>
<
div
class
=
"header-filter-form"
>
<
ElForm
:
model
=
"searchForm"
inline
ref
=
"searchFormRef"
>
<
ElFormItem
label
=
"仓库"
>
<
ElSelect
v
-
model
=
"searchForm.warehouseId"
clearable
placeholder
=
"请选择仓库"
style
=
"width: 160px"
>
<
ElOption
v
-
for
=
"item in warehouseList"
:
key
=
"item.id"
:
label
=
"item.name"
:
value
=
"item.id"
><
/ElOption
>
<
/ElSelect
>
<
/ElFormItem
>
<
ElFormItem
label
=
"出库时间"
>
<
el
-
date
-
picker
v
-
model
=
"tradingTime"
:
shortcuts
=
"pickerOptions.shortcuts"
:
default
-
time
=
"[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
type
=
"datetimerange"
start
-
placeholder
=
"开始时间"
end
-
placeholder
=
"结束时间"
clearable
style
=
"width: 260px"
format
=
"YYYY-MM-DD HH:mm:ss"
value
-
format
=
"YYYY-MM-DD HH:mm:ss"
/>
<
/ElFormItem
>
<
ElFormItem
label
=
"出库单号"
>
<
ElInput
v
-
model
.
trim
=
"searchForm.outNo"
clearable
placeholder
=
"请输入出库单号"
style
=
"width: 160px"
/>
<
/ElFormItem
>
<
ElFormItem
style
=
"margin-right: 10px"
label
=
"库存SKU"
>
<
ElInput
v
-
model
.
trim
=
"searchForm.warehouseSku"
clearable
placeholder
=
"请输入库存SKU"
style
=
"width: 160px"
/>
<
/ElFormItem
>
<
ElFormItem
style
=
"margin-right: 10px"
label
=
"出库单ID"
>
<
ElInput
v
-
model
.
trim
=
"searchForm.id"
clearable
placeholder
=
"请输入出库单ID"
style
=
"width: 160px"
/>
<
/ElFormItem
>
<
ElFormItem
>
<
ElButton
type
=
"primary"
@
click
=
"search"
ref
=
"searchBtnRef"
>
查询
<
/ElButto
n
>
<
/ElFormItem
>
<
ElFormItem
>
<
ElButton
@
click
=
"resetSearchForm"
>
重置
<
/ElButton
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'PENDING_SUBMIT'"
>
<
el
-
button
type
=
"primary"
@
click
=
"addDialog(1, null)"
>
新增
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'PENDING_AUDIT'"
>
<
el
-
button
type
=
"danger"
@
click
=
"rejectedInRecord"
>
驳回
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'PENDING_SUBMIT'"
>
<
el
-
button
type
=
"danger"
@
click
=
"handleBatchDelete"
>
删除
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
>
<
el
-
button
type
=
"success"
@
click
=
"handleExport"
>
导出
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'PENDING_AUDIT'"
>
<
el
-
button
type
=
"warning"
@
click
=
"auditOrder('audit')"
>
审核
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'PENDING_SUBMIT'"
>
<
el
-
button
type
=
"success"
@
click
=
"auditOrder('submitAudit')"
>
提交审核
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'PENDING_AUDIT'"
>
<
el
-
button
type
=
"danger"
@
click
=
"auditOrder('invalid')"
>
作废
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
v
-
if
=
"nodeCode === 'COMPLETED'"
>
<
el
-
button
type
=
"success"
@
click
=
"auditOrder('archiving')"
>
归档
<
/el-button
>
<
/ElFormItem
>
<
ElFormItem
>
<
el
-
button
type
=
"primary"
@
click
=
"printProductTag"
>
打印库存
SKU
标签
<
/el-button
>
<
/ElFormItem
>
<
/ElForm
>
<
/div
>
<
div
class
=
"delivery-note-content flex-1 flex-column overflow-hidden"
>
<
div
class
=
"delivery-note-list flex-1 overflow-hidden"
>
<
ElTable
ref
=
"singleTableRef"
highlight
-
current
-
row
:
data
=
"tableData"
default
-
expand
-
all
size
=
"small"
style
=
"width: 100%; height: 100%"
border
@
current
-
change
=
"rowClick"
@
selection
-
change
=
"handleSelectionChange"
>
<
ElTableColumn
type
=
"selection"
width
=
"70"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
type
=
"index"
label
=
"序号"
width
=
"60"
fixed
=
"left"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"出库单号"
show
-
overflow
-
tooltip
prop
=
"outNo"
width
=
"130"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"单据状态"
width
=
"130"
prop
=
"billStatusTxt"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"仓库名称"
show
-
overflow
-
tooltip
prop
=
"warehouseName"
width
=
"130"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"工厂编号"
show
-
overflow
-
tooltip
prop
=
"factoryCode"
width
=
"90"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"币种"
show
-
overflow
-
tooltip
width
=
"80"
prop
=
"currencyName"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"总金额"
show
-
overflow
-
tooltip
width
=
"120"
prop
=
"totalPrice"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"SKU数量"
header
-
align
=
"center"
prop
=
"skuAmount"
width
=
"90"
align
=
"center"
show
-
overflow
-
tooltip
>
<
/ElTableColumn
>
<
ElTableColumn
label
=
"总数量"
header
-
align
=
"center"
prop
=
"total"
width
=
"90"
align
=
"center"
show
-
overflow
-
tooltip
>
<
/ElTableColumn
>
<
ElTableColumn
label
=
"驳回原因"
show
-
overflow
-
tooltip
prop
=
"rejectReason"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
label
=
"备注"
show
-
overflow
-
tooltip
prop
=
"remark"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
v
-
if
=
"nodeCode === 'PENDING_SUBMIT'"
width
=
"100"
align
=
"center"
header
-
align
=
"center"
label
=
"操作"
>
<
template
#
default
=
"{ row
}
"
>
<
ElButton
type
=
"primary"
link
@
click
=
"addDialog(2, row)"
>
编辑
<
/ElButton
>
<
/template
>
<
/ElTableColumn
>
<
/ElTable
>
<
/div
>
<
ElPagination
v
-
model
:
current
-
page
=
"currentPage"
v
-
model
:
page
-
size
=
"pageSize"
:
page
-
sizes
=
"[100, 200, 300, 400, 500]"
background
layout
=
"total, sizes, prev, pager, next, jumper"
:
total
=
"total"
style
=
"margin: 10px auto 0; text-align: right"
@
size
-
change
=
"handleSizeChange"
@
current
-
change
=
"handleCurrentChange"
><
/ElPagination
>
<
/div
>
<
/template
>
<
template
#
bottom
>
<
el
-
tabs
v
-
model
=
"tabsValue"
@
tab
-
click
=
"tabsClick"
>
<
el
-
tab
-
pane
name
=
"0"
label
=
"出库商品"
>
<
div
class
=
"table-wrap"
>
<
ElTable
size
=
"small"
:
data
=
"detailList"
height
=
"100%"
border
>
<
ElTableColumn
show
-
overflow
-
tooltip
width
=
"60"
align
=
"center"
label
=
"序号"
type
=
"index"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
width
=
"100"
label
=
"SKU图片"
prop
=
"factory_order_number"
>
<
template
#
default
=
"{ row
}
"
>
<
ImageView
:
src
=
"row.skuImage"
width
=
"40px"
height
=
"40px"
/>
<
/template
>
<
/ElTableColumn
>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"库存SKU"
prop
=
"warehouseSku"
width
=
"180"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"库存SKU"
prop
=
"skuName"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"款号"
prop
=
"productNo"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"出库数量"
prop
=
"outCount"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"库位"
prop
=
"locationCode"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"币种"
width
=
"80"
prop
=
"currencyName"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"成本价"
prop
=
"costPrice"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"总成本"
prop
=
"totalPrice"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
width
=
"240"
label
=
"备注"
prop
=
"remark"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"创建时间"
width
=
"130"
prop
=
"createTime"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"更新时间"
width
=
"130"
prop
=
"updateTime"
/>
<
/ElTable
>
<
/div
>
<
/el-tab-pane
>
<
el
-
tab
-
pane
name
=
"1"
label
=
"操作日志"
>
<
ul
style
=
"
color: #333;
font-size: 12px;
height: 100%;
overflow: auto;
"
>
<
li
v
-
for
=
"(item, index) in logList"
:
key
=
"index"
style
=
"display: flex"
>
<
span
style
=
"display: inline-block"
>
{{
item
.
createdTime
}}
&
emsp
;
&
emsp
;
<
/span
>
<
span
style
=
"display: inline-block"
>
{{
item
.
description
}}
<
/span
>
<
/li
>
<
/ul
>
<
/el-tab-pane
>
<
/el-tabs
>
<
/template
>
<
/splitDiv
>
<
/div
>
<
/div
>
<
/div
>
<
ElDialog
v
-
model
=
"importDialogVisible"
title
=
"导入出库单"
width
=
"500px"
:
close
-
on
-
click
-
modal
=
"false"
>
<
div
class
=
"import-dialog"
>
<
div
class
=
"import-content"
>
<
UploadExcel
v
-
model
=
"importedFileUrl"
:
import
-
type
=
"'localAndXlsx'"
:
import
-
name
=
"'出库单'"
:
import
-
url
=
"'/files/outboundOrder.xlsx'"
@
imported
=
"handleLocalImport"
/>
<
/div
>
<
/div
>
<
/ElDialog
>
<
ElDialog
v
-
model
=
"newDialogVisible"
:
title
=
"formId ? '编辑' : '新增'"
width
=
"80%"
:
close
-
on
-
click
-
modal
=
"false"
>
<
div
class
=
"dialog-form"
>
<
ElForm
ref
=
"editFormRef"
:
model
=
"editForm"
:
rules
=
"rules"
inline
label
-
width
=
"90px"
>
<
ElFormItem
label
=
"出库单号"
prop
=
"account"
>
<
ElInput
v
-
model
.
trim
=
"editForm.outNo"
clearable
disabled
/>
<
/ElFormItem
>
<
ElFormItem
label
=
"工厂:"
prop
=
"factoryCode"
>
<
span
>
{{
editForm
.
factoryCode
}}
<
/span
>
<
/ElFormItem
>
<
ElFormItem
label
=
"仓库"
prop
=
"warehouseId"
required
>
<
ElSelect
v
-
model
=
"editForm.warehouseId"
clearable
:
disabled
=
"formId"
placeholder
=
"请选择仓库"
style
=
"width: 160px"
@
change
=
"handleWarehouseChange(editForm.warehouseId)"
>
<
ElOption
v
-
for
=
"item in warehouseList"
:
key
=
"item.id"
:
label
=
"item.name"
:
value
=
"item.id"
><
/ElOption
>
<
/ElSelect
>
<
/ElFormItem
>
<
ElFormItem
label
=
"备注"
prop
=
"remark"
style
=
"width: 45%"
>
<
ElInput
v
-
model
.
trim
=
"editForm.remark"
placeholder
=
"请输入备注"
clearable
/>
<
/ElFormItem
>
<
/ElForm
>
<
ElTable
size
=
"small"
:
data
=
"otherPurchaseData"
height
=
"500px"
border
@
selection
-
change
=
"productSelectionChange"
>
<
ElTableColumn
type
=
"selection"
width
=
"70"
header
-
align
=
"center"
align
=
"center"
><
/ElTableColumn
>
<
ElTableColumn
show
-
overflow
-
tooltip
width
=
"60"
align
=
"center"
label
=
"序号"
type
=
"index"
><
/ElTableColumn
>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
width
=
"100"
label
=
"SKU图片"
prop
=
"skuImage"
>
<
template
#
default
=
"{ row
}
"
>
<
ImageView
:
src
=
"row.skuImage"
width
=
"40px"
height
=
"40px"
/>
<
/template
>
<
/ElTableColumn
>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"库存SKU"
prop
=
"warehouseSku"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"商品名称"
prop
=
"skuName"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"款号"
prop
=
"productNo"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"可用库存数量"
prop
=
"usableInventory"
/>
<
ElTableColumn
align
=
"center"
label
=
"出库数量"
prop
=
"outCount"
>
<
template
#
default
=
"{ row
}
"
>
<
el
-
input
v
-
model
.
number
=
"row.outCount"
placeholder
=
"出库数量"
style
=
"width: 120px"
clearable
size
=
"small"
@
input
=
"setCostPrice(row)"
><
/el-input
>
<
/template
>
<
/ElTableColumn
>
<
ElTableColumn
width
=
"80"
align
=
"center"
label
=
"币种"
prop
=
"currencyName"
/>
<
ElTableColumn
width
=
"100"
align
=
"center"
label
=
"成本价"
prop
=
"costPrice"
/>
<
ElTableColumn
align
=
"center"
width
=
"100"
label
=
"总成本"
prop
=
"totalPrice"
/>
<
ElTableColumn
align
=
"center"
label
=
"库位"
prop
=
"locationCode"
>
<
template
#
default
=
"{ row
}
"
>
<
span
v
-
if
=
"row.locationCode"
>
{{
row
.
locationCode
}}
<
/span
>
<
ElSelect
v
-
else
v
-
model
=
"row.locationId"
clearable
placeholder
=
"请输入库位"
style
=
"width: 120px"
filterable
@
change
=
"handleLocationChange(row.locationId, row)"
>
<
ElOption
v
-
for
=
"item in locationList"
:
key
=
"item.locationId"
:
label
=
"item.locationCode"
:
value
=
"item.locationId"
><
/ElOption
>
<
/ElSelect
>
<
/template
>
<
/ElTableColumn
>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
width
=
"240"
label
=
"备注"
prop
=
"remark"
>
<
template
#
default
=
"{ row
}
"
>
<
ElInput
v
-
model
.
trim
=
"row.remark"
clearable
size
=
"small"
/>
<
/template
>
<
/ElTableColumn
>
<
/ElTable
>
<
/div
>
<
template
#
footer
>
<
div
class
=
"product-dialog-footer"
>
<
div
>
<
el
-
input
v
-
model
.
trim
=
"selectSku"
placeholder
=
"库存SKU"
style
=
"width: 200px; margin: 0 10px"
clearable
size
=
"small"
><
/el-input
>
<
el
-
popover
placement
=
"top-start"
width
=
"1000"
trigger
=
"click"
>
<
div
v
-
if
=
"skuData.length > 0"
style
=
"height: 50vh"
>
<
ElTable
size
=
"small"
:
data
=
"skuData"
height
=
"100%"
border
>
<
ElTableColumn
show
-
overflow
-
tooltip
width
=
"60"
align
=
"center"
label
=
"序号"
type
=
"index"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"SKU图片"
width
=
"100"
prop
=
"image"
>
<
template
#
default
=
"{ row
}
"
>
<
ImageView
:
src
=
"row.image"
width
=
"40px"
height
=
"40px"
/>
<
/template
>
<
/ElTableColumn
>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"库存SKU"
width
=
"200"
prop
=
"skuName"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
width
=
"200"
label
=
"库存SKU"
prop
=
"warehouseSku"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"款号"
prop
=
"productNo"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"币种"
width
=
"80"
prop
=
"currencyName"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"成本价"
width
=
"80"
prop
=
"price"
/>
<
ElTableColumn
show
-
overflow
-
tooltip
align
=
"center"
label
=
"库位"
prop
=
"locationCode"
/>
<
ElTableColumn
width
=
"80"
align
=
"center"
header
-
align
=
"center"
label
=
"操作"
>
<
template
#
default
=
"{ row
}
"
>
<
el
-
icon
:
size
=
"32"
color
=
"#67C23A"
class
=
"cursor-pointer"
><
CirclePlusFilled
@
click
=
"skudblclick(row)"
/><
/el-icon
>
<
/template
>
<
/ElTableColumn
>
<
/ElTable
>
<
/div
>
<
template
#
reference
>
<
el
-
button
type
=
"primary"
size
=
"small"
style
=
"width: 90px"
@
click
=
"selectbySku()"
>
查询
<
/el-button
>
<
/template
>
<
/el-popover
>
<
el
-
button
style
=
"margin-left: 6px"
type
=
"success"
size
=
"small"
@
click
=
"addPurchase"
>
批量新增
<
/el-button
>
<
el
-
button
type
=
"danger"
style
=
"margin-left: 10px"
size
=
"small"
@
click
=
"deleteOtherWarehousing()"
>
删除
<
/el-button
>
<
el
-
button
type
=
"primary"
style
=
"margin-left: 10px"
size
=
"small"
@
click
=
"importData"
>
导入
<
/el-button
>
<
/div
>
<
div
>
<
el
-
button
size
=
"small"
style
=
"margin-left: 10px"
@
click
=
"newDialogVisible = false"
>
取消
<
/el-button
>
<
el
-
button
type
=
"primary"
size
=
"small"
@
click
=
"addOtherCurrency()"
>
保存
<
/el-button
>
<
/div
>
<
/div
>
<
/template
>
<
/ElDialog
>
<
ElDialog
v
-
model
=
"exportVisible"
title
=
"导出选项"
width
=
"500px"
:
close
-
on
-
click
-
modal
=
"false"
>
<
el
-
form
:
model
=
"exportForm"
label
-
width
=
"80px"
>
<
el
-
form
-
item
label
=
""
prop
=
"resource"
>
<
el
-
radio
-
group
v
-
model
=
"exportForm.resource"
@
change
=
"isAllExport"
>
<
el
-
radio
:
label
=
"0"
>
导出本页
<
/el-radio
>
<
el
-
radio
:
label
=
"1"
>
导出选中
<
/el-radio
>
<
el
-
radio
:
label
=
"2"
>
全部
<
/el-radio
>
<
/el-radio-group
>
<
/el-form-item
>
<
el
-
form
-
item
label
=
""
>
<
el
-
checkbox
v
-
model
=
"exportForm.delivery"
>
包含详情
<
/el-checkbox
>
<
/el-form-item
>
<
/el-form
>
<
template
#
footer
>
<
span
class
=
"dialog-footer"
>
<
el
-
button
@
click
=
"exportVisible = false"
>
取消
<
/el-button
>
<
el
-
button
type
=
"primary"
@
click
=
"submitExportForm"
>
确认
<
/el-button
>
<
/span
>
<
/template
>
<
/ElDialog
>
<
el
-
dialog
v
-
model
=
"showPrintDialog"
title
=
"打印参数设置"
>
<
el
-
table
height
=
"400px"
:
data
=
"printData"
border
>
<
el
-
table
-
column
width
=
"60"
align
=
"center"
type
=
"index"
label
=
"序号"
><
/el-table-column
>
<
el
-
table
-
column
align
=
"center"
prop
=
"skuName"
label
=
"商品名称"
><
/el-table-column
>
<
el
-
table
-
column
align
=
"center"
prop
=
"warehouseSku"
label
=
"库存SKU"
><
/el-table-column
>
<
el
-
table
-
column
align
=
"center"
prop
=
"locationName"
label
=
"库位编码"
><
/el-table-column
>
<
el
-
table
-
column
align
=
"center"
prop
=
"supplierItemNo"
label
=
"款号"
><
/el-table-column
>
<
el
-
table
-
column
align
=
"center"
prop
=
"number"
label
=
"打印数量"
>
<
template
#
default
=
"{ row
}
"
>
<
el
-
input
v
-
model
.
number
=
"row.number"
oninput
=
"value=value.replace(/[^
\
-?
\
d.]/g,'')"
placeholder
=
"打印数量"
clearable
><
/el-input
>
<
/template
>
<
/el-table-column
>
<
/el-table
>
<
template
#
footer
>
<
el
-
button
@
click
=
"showPrintDialog = false"
>
取消
<
/el-button
>
<
el
-
button
type
=
"primary"
@
click
=
"handlePrintProductTag"
>
打印
<
/el-button
>
<
/template
>
<
/el-dialog
>
<
ElDialog
v
-
model
=
"addPurchaseVisible"
title
=
"批量添加"
width
=
"500px"
:
close
-
on
-
click
-
modal
=
"false"
>
<
div
>
<
el
-
input
v
-
model
.
trim
=
"purchaseTextarea"
type
=
"textarea"
placeholder
=
"请输入库存 SKU"
:
rows
=
"5"
minlength
=
"1"
maxlength
=
"1000"
show
-
word
-
limit
/>
<
div
style
=
"margin-top: 12px; color: #777"
>
{{
'多个字段使用_##_,_##_隔开'
}}
<
/div
>
<
/div
>
<
template
#
footer
>
<
span
class
=
"dialog-footer"
>
<
el
-
button
@
click
=
"addPurchaseVisible = false"
>
取消
<
/el-button
>
<
el
-
button
type
=
"primary"
@
click
=
"submitPurchase"
>
确认
<
/el-button
>
<
/span
>
<
/template
>
<
/ElDialog
>
<
/template
>
<
script
setup
lang
=
"ts"
>
import
{
ElMessage
,
ElRadioGroup
,
ElTree
}
from
'element-plus'
import
{
CirclePlusFilled
}
from
'@element-plus/icons-vue'
import
splitDiv
from
'@/components/splitDiv/splitDiv.vue'
import
{
ElTable
}
from
'element-plus'
import
usePageList
from
'@/utils/hooks/usePageList'
import
{
checkUpdateParams
,
AnyObject
}
from
'@/utils/hooks/commonUtil'
import
{
useValue
}
from
'@/utils/hooks/useValue'
import
{
getOutRecordStatusTree
,
warehouseOutRecordListPage
,
getWarehouseOutRecordDetailApi
,
getItemListByIdApi
,
getBySkuAndWarehouseIdApi
,
warehouseInfoGetAll
,
getByWareHouseIdAndCodeApi
,
addOutRecordApi
,
updateOutRecordApi
,
deleteWarehouseOutRecordApi
,
auditOrderApi
,
getOutRecordLogApi
,
rejectOutRecordApi
,
LogListData
,
warehouseInfo
,
warehouseOutRecordExport
,
factoryWarehouseInventoryPrint
,
OutRecordBatchCheckPrintApi
,
}
from
'@/api/warehouse'
import
{
filePath
}
from
'@/api/axios'
import
BigNumber
from
'bignumber.js'
import
{
ref
,
onMounted
,
watch
,
nextTick
}
from
'vue'
import
'element-plus/dist/index.css'
import
{
warehouseSearchForm
,
InterWarehousePage
,
InterWarehouseTree
,
InterProductList
,
InterskuList
,
ILocation
,
InterWarehouseDetail
,
}
from
'@/types/api/warehouse'
import
ImageView
from
'@/components/ImageView.vue'
import
{
useEnterKeyTrigger
}
from
'@/utils/hooks/useEnterKeyTrigger.ts'
import
UploadExcel
from
'@/components/UploadExcel.vue'
// import
{
debounce
}
from
'lodash-es'
const
warehouseList
=
ref
<
warehouseInfo
[]
>
([])
const
pickerOptions
=
{
shortcuts
:
[
{
text
:
'今日'
,
value
:
()
=>
{
const
start
=
new
Date
(
new
Date
(
getStartTime
()).
getTime
())
const
end
=
new
Date
()
return
[
start
,
end
]
}
,
}
,
{
text
:
'昨天'
,
value
:
()
=>
{
const
start
=
new
Date
()
const
end
=
new
Date
(
new
Date
(
getStartTime
()).
getTime
()
-
1
)
start
.
setTime
(
end
.
getTime
()
-
3600
*
1000
*
24
*
1
+
1
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'最近7天'
,
value
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
(
getStartTime
())
start
.
setTime
(
start
.
getTime
()
-
3600
*
1000
*
24
*
6
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'最近14天'
,
value
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
(
getStartTime
())
start
.
setTime
(
start
.
getTime
()
-
3600
*
1000
*
24
*
13
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'最近30天'
,
value
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
(
getStartTime
())
start
.
setTime
(
start
.
getTime
()
-
3600
*
1000
*
24
*
29
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'本星期'
,
value
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
()
const
nowDay
=
new
Date
().
getDay
()
-
1
start
.
setTime
(
new
Date
(
getStartTime
()).
getTime
()
-
3600
*
1000
*
24
*
nowDay
,
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'上星期'
,
value
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
()
const
nowDay
=
new
Date
().
getDay
()
-
1
end
.
setTime
(
new
Date
(
getStartTime
()).
getTime
()
-
3600
*
1000
*
24
*
nowDay
-
1
,
)
start
.
setTime
(
end
.
getTime
()
-
3600
*
1000
*
24
*
7
+
1
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'这个月'
,
value
:
()
=>
{
const
end
=
new
Date
()
const
start
=
new
Date
()
const
nowDate
=
new
Date
().
getDate
()
-
1
start
.
setTime
(
new
Date
(
getStartTime
()).
getTime
()
-
3600
*
1000
*
24
*
nowDate
,
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'上个月'
,
value
:
()
=>
{
const
date
=
new
Date
()
let
year
=
date
.
getFullYear
()
let
month
=
date
.
getMonth
()
const
end
=
new
Date
(
new
Date
(
`${year
}
-${month + 1
}
-1 00:00:00`
).
getTime
()
-
1
,
)
if
(
month
===
0
)
{
month
=
12
year
=
year
-
1
}
const
start
=
new
Date
(
new
Date
(
`${year
}
-${month
}
-1 00:00:00`
).
getTime
(),
)
return
[
start
,
end
]
}
,
}
,
{
text
:
'历史'
,
value
:
()
=>
{
return
[
''
,
''
]
}
,
}
,
],
}
function
getStartTime
()
{
const
date
=
new
Date
()
const
year
=
date
.
getFullYear
()
const
month
=
date
.
getMonth
()
+
1
const
day
=
date
.
getDate
()
return
`${year
}
-${month
}
-${day
}
00:00:00`
}
const
selectSku
=
ref
(
''
)
const
treeData
=
ref
<
InterWarehouseTree
[]
>
()
const
[
searchForm
,
resetSearchForm
]
=
useValue
<
warehouseSearchForm
>
({
}
)
const
tradingTime
=
ref
<
string
[]
>
([])
const
selections
=
ref
<
InterWarehousePage
[]
>
([])
const
detailList
=
ref
<
InterProductList
[]
>
([])
const
tabsValue
=
ref
<
string
>
(
'0'
)
const
singleTableRef
=
ref
<
InstanceType
<
typeof
ElTable
>>
()
const
currentRow
=
ref
<
InterWarehousePage
|
null
>
(
null
)
const
logList
=
ref
<
LogListData
[]
>
([])
const
rules
=
{
warehouseId
:
[{
required
:
true
,
message
:
'请选择仓库'
,
trigger
:
'change'
}
],
}
const
isAllExport
=
()
=>
{
if
(
exportForm
.
value
.
resource
==
'2'
)
{
ElMessage
.
warning
(
'最多支持5000条!'
)
}
}
const
nodeCode
=
ref
<
string
>
(
sessionStorage
.
getItem
(
'InRecord_NodeCode'
)
||
'all'
,
)
const
treeRef
=
ref
<
InstanceType
<
typeof
ElTree
>>
()
const
{
currentPage
,
pageSize
,
total
,
data
:
tableData
,
refresh
:
search
,
onCurrentPageChange
:
handleCurrentChange
,
onPageSizeChange
:
handleSizeChange
,
}
=
usePageList
({
query
:
(
page
,
pageSize
)
=>
warehouseOutRecordListPage
(
{
...
searchForm
.
value
,
billStatus
:
nodeCode
.
value
==
'all'
?
'all'
:
nodeCode
.
value
,
startTime
:
tradingTime
.
value
&&
tradingTime
.
value
[
0
],
endTime
:
tradingTime
.
value
&&
tradingTime
.
value
[
1
],
}
,
page
,
pageSize
,
).
then
((
res
)
=>
res
.
data
),
}
)
const
setCostPrice
=
(
item
:
InterProductList
)
=>
{
if
(
!
item
.
costPrice
)
{
ElMessage
.
warning
(
'商品成本价为空,请完善商品成本价'
)
return
}
if
(
item
)
{
const
outCount
=
item
.
outCount
??
0
const
costPrice
=
item
.
costPrice
??
0
const
amount
=
new
BigNumber
(
outCount
).
multipliedBy
(
costPrice
).
toFixed
(
2
)
item
.
totalPrice
=
Number
(
amount
)
}
}
const
getTreeNum
=
async
()
=>
{
try
{
const
res
=
await
getOutRecordStatusTree
()
res
.
data
=
[{
code
:
'all'
,
name
:
'全部'
,
children
:
res
.
data
}
]
treeData
.
value
=
res
.
data
await
nextTick
(()
=>
{
treeRef
.
value
!
.
setCurrentKey
(
nodeCode
.
value
,
true
)
}
)
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
showPrintDialog
=
ref
(
false
)
const
printData
=
ref
([])
const
printProductTag
=
async
()
=>
{
if
(
!
selections
.
value
.
length
)
{
return
ElMessage
.
warning
(
'请选择要操作的数据'
)
}
const
str
=
selections
.
value
.
map
((
el
:
InterWarehousePage
)
=>
el
.
id
).
join
(
','
)
try
{
const
res
=
await
OutRecordBatchCheckPrintApi
(
str
)
if
(
res
.
code
===
200
)
{
printData
.
value
=
res
.
data
showPrintDialog
.
value
=
true
}
else
{
ElMessage
.
error
(
res
.
message
)
}
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
async
function
handlePrintProductTag
()
{
const
flag
=
printData
.
value
.
every
(
(
el
:
InterWarehousePage
)
=>
el
.
number
&&
el
.
number
!=
'0'
,
)
if
(
!
flag
)
{
return
ElMessage
.
warning
(
'打印数量需大于0'
)
}
const
list
=
printData
.
value
.
map
(
({
skuName
=
''
,
warehouseSku
=
''
,
supplierItemNo
=
''
,
number
=
''
,
locationName
=
''
,
}
)
=>
({
skuName
,
warehouseSku
,
supplierItemNo
,
number
,
locationName
,
}
),
)
const
res
=
await
factoryWarehouseInventoryPrint
({
list
,
code
:
'PT002'
,
}
)
showPrintDialog
.
value
=
false
window
.
open
(
filePath
+
res
.
message
,
'_blank'
)
}
const
batchAddCommodity
=
async
(
sku
:
string
):
Promise
<
InterskuList
[]
>
=>
{
if
(
!
editForm
.
value
.
warehouseId
)
{
ElMessage
.
error
(
'请选择仓库'
)
return
[]
}
try
{
const
res
=
await
getBySkuAndWarehouseIdApi
(
editForm
.
value
.
warehouseId
,
sku
)
const
arr
:
InterskuList
[]
=
res
.
data
||
[]
const
ids
:
Record
<
string
,
boolean
>
=
{
}
// 过滤掉商品列表已经加了的
for
(
const
item
of
otherPurchaseData
.
value
)
{
if
(
item
.
warehouseSku
!==
undefined
)
{
ids
[
item
.
warehouseSku
]
=
true
}
}
// 使用 filter 方法过滤掉已经存在的 SKU
const
filteredArr
=
arr
.
filter
((
currentItem
:
InterskuList
)
=>
{
return
(
currentItem
.
warehouseSku
===
undefined
||
!
ids
[
currentItem
.
warehouseSku
]
)
}
)
return
filteredArr
}
catch
(
e
)
{
console
.
error
(
e
)
return
[]
}
}
interface
InterImportData
{
warehouseSku
:
string
remark
?:
string
|
null
outCount
?:
string
|
number
|
null
|
object
// 扩大 outCount 类型,以兼容原始数据
[
propName
:
string
]:
string
|
number
|
boolean
|
undefined
|
unknown
}
// 前端导入Excel
const
excelFieldMap
:
Record
<
string
,
keyof
InterImportData
>
=
{
// SKU图片: 'skuImage',
库存
SKU
:
'warehouseSku'
,
// SKU名称: 'skuName',
出库数量
:
'outCount'
,
// 可用库存数量: 'usableInventory',
// '成本价(¥)': 'costPrice',
// '总成本(¥)': 'totalPrice',
// 库位: 'locationCode',
备注
:
'remark'
,
}
const
handleLocalImport
=
async
({
path
,
data
,
}
:
{
path
:
string
data
:
Record
<
string
,
unknown
>
[]
}
)
=>
{
// 1. 将原始导入数据映射到 InterImportData[]
const
importedData
:
InterImportData
[]
=
data
.
map
((
item
)
=>
{
const
obj
:
InterImportData
=
{
warehouseSku
:
''
}
Object
.
keys
(
excelFieldMap
).
forEach
((
excelKey
)
=>
{
const
field
=
excelFieldMap
[
excelKey
]
const
value
=
item
[
excelKey
]
if
(
field
===
'warehouseSku'
)
{
obj
[
field
]
=
typeof
value
===
'string'
?
value
:
String
(
value
??
''
)
}
else
if
(
field
===
'remark'
)
{
obj
[
field
]
=
typeof
value
===
'string'
?
value
:
value
===
null
||
value
===
undefined
?
null
:
String
(
value
)
}
else
if
(
field
===
'outCount'
)
{
// 处理 outCount: 确保它是一个数字、数字字符串,否则设置为 null
if
(
typeof
value
===
'number'
)
{
obj
[
field
]
=
String
(
value
)
// 将数字转换为字符串
}
else
if
(
typeof
value
===
'string'
&&
!
isNaN
(
Number
(
value
)))
{
obj
[
field
]
=
value
// 保持有效的数字字符串
}
else
{
// 如果不是数字或有效的数字字符串,则设置为 null
obj
[
field
]
=
null
}
}
else
{
obj
[
field
]
=
value
}
}
)
return
obj
}
)
.
filter
((
item
)
=>
item
.
warehouseSku
)
if
(
importedData
.
length
===
0
)
{
ElMessage
.
warning
(
'导入数据中没有有效的库存SKU'
)
importDialogVisible
.
value
=
false
return
}
// 2. 提取导入的 SKU 列表
const
importedSkus
=
importedData
.
map
((
item
)
=>
item
.
warehouseSku
).
join
(
','
)
// 3. 调用 batchAddCommodity 获取商品的完整信息并过滤掉已有的 SKU
const
filteredSkusList
=
await
batchAddCommodity
(
importedSkus
)
if
(
filteredSkusList
.
length
===
0
)
{
ElMessage
.
warning
(
'导入的库存SKU已存在或无效'
)
importedFileUrl
.
value
=
path
importDialogVisible
.
value
=
false
return
}
// 4. 将备注信息合并到获取到的商品列表中
const
mergedProductList
=
filteredSkusList
.
map
((
skuItem
)
=>
{
const
importedItem
=
importedData
.
find
(
(
item
)
=>
item
.
warehouseSku
===
skuItem
.
warehouseSku
,
)
let
outCountValueForBigNumber
:
string
|
number
=
'0'
// 初始化为安全默认值
console
.
log
(
importedItem
)
if
(
importedItem
?.
outCount
!==
null
&&
importedItem
?.
outCount
!==
undefined
)
{
if
(
typeof
importedItem
.
outCount
===
'string'
&&
!
isNaN
(
Number
(
importedItem
.
outCount
))
)
{
outCountValueForBigNumber
=
importedItem
.
outCount
}
else
if
(
typeof
importedItem
.
outCount
===
'number'
)
{
outCountValueForBigNumber
=
importedItem
.
outCount
}
else
{
// 如果是对象或其他意外类型,则默认为 '0'
outCountValueForBigNumber
=
'0'
}
}
const
calculatedOutCount
=
new
BigNumber
(
outCountValueForBigNumber
,
).
toNumber
()
return
{
skuImage
:
skuItem
.
image
,
warehouseSku
:
skuItem
.
warehouseSku
,
skuName
:
skuItem
.
skuName
,
productNo
:
skuItem
.
productNumber
,
locationCode
:
skuItem
.
locationCode
??
''
,
locationId
:
skuItem
.
locationId
??
null
,
costPrice
:
skuItem
.
price
,
outCount
:
calculatedOutCount
,
totalPrice
:
new
BigNumber
(
calculatedOutCount
)
.
multipliedBy
(
skuItem
.
price
??
0
)
.
toNumber
(),
usableInventory
:
skuItem
.
usableInventory
,
inventoryId
:
skuItem
.
id
,
remark
:
importedItem
?.
remark
??
skuItem
.
remark
??
null
,
}
as
InterProductList
}
)
// 5. 更新 otherPurchaseData
otherPurchaseData
.
value
=
[...
otherPurchaseData
.
value
,
...
mergedProductList
]
importedFileUrl
.
value
=
path
importDialogVisible
.
value
=
false
}
const
exportVisible
=
ref
(
false
)
const
exportForm
=
ref
({
delivery
:
false
,
resource
:
''
,
}
)
const
handleExport
=
()
=>
{
exportVisible
.
value
=
true
}
const
submitExportForm
=
async
()
=>
{
if
(
exportForm
.
value
.
resource
===
''
)
{
return
ElMessage
.
error
(
'请选择导出类型'
)
}
let
purchaseIds
:
number
[]
=
[]
let
exportTotal
:
number
|
undefined
=
undefined
const
params
:
AnyObject
=
{
}
const
resourceType
=
Number
(
exportForm
.
value
.
resource
)
if
(
resourceType
===
0
)
{
purchaseIds
=
(
tableData
.
value
as
InterWarehousePage
[]).
map
(
(
el
:
InterWarehousePage
)
=>
Number
(
el
.
id
),
)
}
else
if
(
resourceType
===
1
)
{
purchaseIds
=
selections
.
value
.
map
((
el
:
InterWarehousePage
)
=>
Number
(
el
.
id
),
)
}
else
if
(
resourceType
===
2
)
{
purchaseIds
=
[]
exportTotal
=
total
.
value
params
.
billStatus
=
nodeCode
.
value
==
'all'
?
'all'
:
nodeCode
.
value
}
params
.
idList
=
purchaseIds
if
(
exportTotal
!==
undefined
)
{
params
.
total
=
exportTotal
}
try
{
const
res
=
await
warehouseOutRecordExport
({
showDetail
:
exportForm
.
value
.
delivery
,
...
params
,
...
searchForm
.
value
,
}
)
window
.
open
(
filePath
+
res
.
message
,
'_blank'
)
exportVisible
.
value
=
false
}
catch
(
e
)
{
exportVisible
.
value
=
false
}
}
const
getWarehouseList
=
async
()
=>
{
try
{
const
res
=
await
warehouseInfoGetAll
()
warehouseList
.
value
=
res
.
data
await
nextTick
(()
=>
{
treeRef
.
value
!
.
setCurrentKey
(
nodeCode
.
value
,
true
)
}
)
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
rowClick
=
(
row
:
InterWarehousePage
)
=>
{
if
(
!
row
)
{
currentRow
.
value
=
null
}
currentRow
.
value
=
row
tabsClick
()
}
const
skuData
=
ref
<
InterskuList
[]
>
([])
const
selectbySku
=
async
()
=>
{
if
(
!
editForm
.
value
.
warehouseId
)
return
ElMessage
.
error
(
'请选择仓库'
)
try
{
const
res
=
await
getBySkuAndWarehouseIdApi
(
editForm
.
value
.
warehouseId
,
selectSku
.
value
,
)
const
arr
:
InterskuList
[]
=
res
.
data
||
[]
const
ids
:
Record
<
string
,
boolean
>
=
{
}
// 过滤掉商品列表已经加了的
for
(
const
item
of
otherPurchaseData
.
value
)
{
if
(
item
.
warehouseSku
!==
undefined
)
{
ids
[
item
.
warehouseSku
]
=
true
}
}
for
(
let
i
=
0
;
i
<
arr
.
length
;
i
++
)
{
const
currentItem
:
InterskuList
=
arr
[
i
]
if
(
currentItem
.
warehouseSku
!==
undefined
&&
ids
[
currentItem
.
warehouseSku
]
)
{
arr
.
splice
(
i
,
1
)
i
--
}
}
skuData
.
value
=
arr
||
[]
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
skudblclick
=
(
val
:
InterskuList
)
=>
{
// 使用可选链和空值合并运算符处理可能的null值
const
{
locationCode
=
''
,
price
=
null
,
productNo
=
''
,
warehouseSku
=
''
,
skuName
=
''
,
image
=
''
,
locationId
=
null
,
usableInventory
=
null
,
id
=
null
,
currencyName
=
''
,
currencyCode
=
''
,
}
=
val
||
{
}
// 币种一致性校验
const
lastItem
=
otherPurchaseData
.
value
[
otherPurchaseData
.
value
.
length
-
1
]
||
null
if
(
lastItem
&&
lastItem
.
currencyName
)
{
if
(
!
currencyName
||
currencyName
!==
lastItem
.
currencyName
)
{
ElMessage
.
error
(
`添加的商品币种需一致`
)
return
}
}
otherPurchaseData
.
value
=
[
...
otherPurchaseData
.
value
,
{
skuImage
:
image
,
warehouseSku
,
skuName
,
productNo
,
locationCode
:
locationCode
??
''
,
// 确保空值处理
locationId
:
locationId
??
null
,
// 确保空值处理
costPrice
:
price
,
outCount
:
null
,
//出库数量
totalPrice
:
null
,
usableInventory
,
//可用库存数量
inventoryId
:
id
,
currencyName
,
currencyCode
,
}
,
]
// 使用filter代替forEach+splice,时间复杂度从O(n^2)降到O(n)
const
skuSet
=
new
Set
(
otherPurchaseData
.
value
.
map
((
item
:
InterProductList
)
=>
item
.
warehouseSku
),
)
skuData
.
value
=
skuData
.
value
.
filter
(
(
item
:
InterskuList
)
=>
!
skuSet
.
has
(
item
.
warehouseSku
),
)
}
const
tabsClick
=
async
()
=>
{
if
(
!
currentRow
.
value
)
{
detailList
.
value
=
[]
logList
.
value
=
[]
return
}
await
nextTick
()
if
(
tabsValue
.
value
===
'0'
)
{
searchDetail
()
}
else
if
(
tabsValue
.
value
===
'1'
)
{
getLogList
()
}
}
const
[
editForm
,
resetEditForm
]
=
useValue
<
InterWarehouseDetail
>
({
outNo
:
''
,
warehouseId
:
''
,
warehouseName
:
''
,
remark
:
''
,
factoryCode
:
''
,
factoryId
:
0
,
productList
:
[],
}
)
const
newDialogVisible
=
ref
(
false
)
const
editFormRef
=
ref
()
const
editForm2
=
ref
({
}
)
const
formId
=
ref
<
number
|
undefined
>
(
undefined
)
const
otherPurchaseData
=
ref
<
InterProductList
[]
>
([])
const
addDialog
=
async
(
i
:
number
,
v
:
InterWarehousePage
|
null
)
=>
{
if
(
i
===
2
)
{
if
(
v
)
formId
.
value
=
v
.
id
if
(
v
)
getProduct
(
v
.
id
)
if
(
!
formId
.
value
)
return
ElMessage
(
'请勾选至少一条记录'
)
// editForm.value = JSON.parse(JSON.stringify(v))
}
else
{
await
nextTick
()
editForm
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
editForm2
.
value
))
resetEditForm
()
const
userJson
=
localStorage
.
getItem
(
'user'
)
if
(
userJson
)
{
try
{
const
userData
=
JSON
.
parse
(
userJson
)
editForm
.
value
.
factoryCode
=
userData
.
factoryCode
||
''
editForm
.
value
.
factoryId
=
userData
.
factoryId
||
0
const
defaultWarehouse
=
warehouseList
.
value
.
find
(
(
item
:
warehouseInfo
)
=>
item
.
defaulted
===
1
,
)
if
(
defaultWarehouse
)
{
editForm
.
value
.
warehouseId
=
defaultWarehouse
.
id
editForm
.
value
.
warehouseName
=
defaultWarehouse
.
name
}
}
catch
{
// ignore
}
}
otherPurchaseData
.
value
=
[]
formId
.
value
=
undefined
}
fetchLocationList
(
''
)
selectSku
.
value
=
''
newDialogVisible
.
value
=
true
}
const
getProduct
=
async
(
id
:
number
|
undefined
)
=>
{
try
{
const
res
=
await
getWarehouseOutRecordDetailApi
(
id
)
const
productList
=
res
.
data
?.
productList
const
newProductList
=
(
Array
.
isArray
(
productList
)
?
productList
:
[]).
map
(
(
item
:
InterProductList
)
=>
{
const
{
inventory
,
...
rest
}
=
item
return
{
...
rest
,
usableInventory
:
inventory
?.
usableInventory
??
null
,
}
}
,
)
if
(
res
.
data
)
{
res
.
data
.
productList
=
newProductList
}
editForm
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
res
.
data
))
otherPurchaseData
.
value
=
newProductList
||
[]
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
handleSelectionChange
=
(
v
:
InterWarehousePage
[])
=>
{
selections
.
value
=
v
}
const
otherWarehouseSelection
=
ref
<
InterProductList
[]
>
([])
const
productSelectionChange
=
(
v
:
InterProductList
[])
=>
{
otherWarehouseSelection
.
value
=
v
}
const
auditOrder
=
(
key
:
string
)
=>
{
if
(
selections
.
value
.
length
===
0
)
{
return
ElMessage
.
warning
(
'请选择要操作的数据'
)
}
let
url
=
''
let
text
=
''
switch
(
key
)
{
case
'invalid'
:
url
=
'factory/warehouseOutRecord/invalid'
text
=
'作废'
break
case
'archiving'
:
url
=
'factory/warehouseOutRecord/archive'
text
=
'归档'
break
case
'submitAudit'
:
url
=
'factory/warehouseOutRecord/submitAudit'
text
=
'提交审核'
break
case
'audit'
:
url
=
'factory/warehouseOutRecord/audit'
text
=
'审核'
break
}
const
confimText
=
key
===
'audit'
?
'确定进行审核?点"确定"将会直接更改库存数量,请在审核前确认数量是否正确。'
:
`确定对选中的信息进行${text
}
?`
ElMessageBox
.
confirm
(
confimText
,
'重要提示'
,
{
confirmButtonText
:
'确定'
,
type
:
'warning'
,
}
).
then
(
async
()
=>
{
const
data
=
selections
.
value
.
map
(
({
id
,
dataVersion
}
:
InterWarehousePage
)
=>
({
id
,
dataVersion
,
}
),
)
await
auditOrderApi
(
url
,
data
)
ElMessage
.
success
(
'操作成功'
)
search
()
await
getTreeNum
()
}
)
}
const
rejectedInRecord
=
()
=>
{
if
(
selections
.
value
.
length
===
0
)
{
return
ElMessage
.
warning
(
'请选择要操作的数据'
)
}
ElMessageBox
.
prompt
(
'请输入驳回原因'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
inputPattern
:
/.+/
,
customClass
:
'reject'
,
inputErrorMessage
:
'请输入驳回原因'
,
inputPlaceholder
:
'驳回原因'
,
}
).
then
(
async
({
value
}
:
{
value
:
string
}
)
=>
{
const
data
=
selections
.
value
.
map
(
({
id
,
dataVersion
}
:
InterWarehousePage
)
=>
({
id
,
dataVersion
,
}
),
)
try
{
await
rejectOutRecordApi
({
list
:
data
,
rejectReason
:
value
}
)
ElMessage
.
success
(
'操作成功'
)
search
()
await
getTreeNum
()
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
)
}
watch
(
()
=>
tableData
.
value
,
()
=>
{
if
(
tableData
.
value
&&
tableData
.
value
.
length
>
0
)
{
if
(
singleTableRef
.
value
)
{
singleTableRef
.
value
!
.
setCurrentRow
(
tableData
.
value
[
0
])
currentRow
.
value
=
(
tableData
.
value
as
never
)[
0
]
}
}
else
{
detailList
.
value
=
[]
}
}
,
{
immediate
:
true
}
,
)
watch
(
()
=>
editForm
.
value
.
warehouseId
,
(
newVal
:
number
|
string
|
undefined
)
=>
{
if
(
newVal
)
{
fetchLocationList
(
''
)
}
}
,
)
const
addOtherCurrency
=
async
()
=>
{
try
{
await
editFormRef
.
value
?.
validate
()
}
catch
{
return
}
const
arr
=
otherPurchaseData
.
value
if
(
arr
.
length
===
0
)
{
ElMessage
.
error
(
'请至少选择一条数据'
)
return
}
for
(
let
i
=
0
;
i
<
arr
.
length
;
i
++
)
{
if
(
!
arr
[
i
].
outCount
)
{
ElMessage
.
error
(
'请输入出库数量'
)
return
}
const
usableInventory
=
arr
[
i
].
usableInventory
||
0
if
((
arr
[
i
].
outCount
as
number
)
>
usableInventory
)
{
ElMessage
.
error
(
'出库数量不能大于可用库存数量'
)
return
}
if
(
!
arr
[
i
].
locationId
)
{
ElMessage
.
error
(
'请选择库位'
)
return
}
}
// 看新增后要不要打印标签
// try
{
// if (!editId.value)
{
// await addUserApi(
{
// ...editForm.value,
// supperMark: Number(editForm.value.supperMark),
// status: Number(editForm.value.status),
//
}
)
//
}
else
{
// await updateUserApi(
{
// ...editForm.value,
// supperMark: Number(editForm.value.supperMark),
// status: Number(editForm.value.status),
//
}
)
//
}
// ElMessage(
{
// message: '保存成功',
// type: 'success',
// offset: window.innerHeight / 2,
//
}
)
// newDialogVisible.value = false
// search()
//
}
catch
(
e
)
{
// return
//
}
if
(
!
formId
.
value
)
{
addSection
()
}
else
{
upSection
()
}
}
const
addSection
=
async
()
=>
{
const
params
=
{
...
editForm
.
value
}
params
.
productList
=
otherPurchaseData
.
value
try
{
await
addOutRecordApi
(
params
)
ElMessage
.
success
(
'保存成功'
)
newDialogVisible
.
value
=
false
search
()
await
getTreeNum
()
}
catch
(
e
)
{
console
.
error
(
e
)
}
// post(url, params).then((res: any) =>
{
// if (res.code === 200)
{
// otherDialogVisible.value = false;
// ElConfirm('保存成功,是否打印标签?', '提示',
{
// confirmButtonText: '确定',
// cancelButtonText: '取消',
// type: 'warning',
//
}
)
// .then(() =>
{
// if (res.message)
{
// getPrintData(res.message, true);
//
}
else
{
// ElMessage.warning('入口单号为空,无法打印');
//
}
//
}
)
// .catch((e: any) =>
{
// console.error(e);
//
}
);
// getList();
// getStatusAmount();
//
}
else
{
// ElAlert(res.message, '错误提示',
{
// dangerouslyUseHTMLString: true,
//
}
);
//
}
//
}
);
}
const
upSection
=
async
()
=>
{
const
params
=
{
...
editForm
.
value
}
const
result
=
checkUpdateParams
(
{
...
params
,
productList
:
otherPurchaseData
.
value
}
,
editForm
.
value
as
unknown
as
AnyObject
,
'id'
,
{
productList
:
'id'
,
}
,
)
try
{
await
updateOutRecordApi
(
result
)
newDialogVisible
.
value
=
false
ElMessage
.
success
(
'修改成功'
)
search
()
await
getTreeNum
()
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
addPurchaseVisible
=
ref
(
false
)
const
purchaseTextarea
=
ref
(
null
)
const
addPurchase
=
async
()
=>
{
addPurchaseVisible
.
value
=
true
}
const
submitPurchase
=
async
()
=>
{
if
(
!
purchaseTextarea
.
value
)
{
ElMessage
.
warning
(
'请输入库存 SKU'
)
return
}
const
filteredSkusList
=
await
batchAddCommodity
(
purchaseTextarea
.
value
)
const
mergedProductList
=
filteredSkusList
.
map
((
skuItem
)
=>
{
return
{
skuImage
:
skuItem
.
image
,
warehouseSku
:
skuItem
.
warehouseSku
,
skuName
:
skuItem
.
skuName
,
productNo
:
skuItem
.
productNumber
,
locationCode
:
skuItem
.
locationCode
??
''
,
locationId
:
skuItem
.
locationId
??
null
,
costPrice
:
skuItem
.
price
,
outCount
:
null
,
totalPrice
:
null
,
usableInventory
:
skuItem
.
usableInventory
,
inventoryId
:
skuItem
.
id
,
remark
:
null
,
}
as
InterProductList
}
)
otherPurchaseData
.
value
=
[...
otherPurchaseData
.
value
,
...
mergedProductList
]
addPurchaseVisible
.
value
=
false
}
const
deleteOtherWarehousing
=
()
=>
{
const
arr
=
otherWarehouseSelection
.
value
if
(
arr
.
length
===
0
)
return
const
idList
=
arr
.
map
((
v
:
InterProductList
)
=>
v
.
warehouseSku
)
otherPurchaseData
.
value
=
otherPurchaseData
.
value
.
filter
(
(
item
:
InterProductList
)
=>
!
idList
.
includes
(
item
.
warehouseSku
),
)
}
const
importDialogVisible
=
ref
(
false
)
const
importedFileUrl
=
ref
(
''
)
const
importData
=
async
()
=>
{
importDialogVisible
.
value
=
true
importedFileUrl
.
value
=
''
}
const
handleBatchDelete
=
async
()
=>
{
if
(
!
selections
.
value
.
length
)
{
return
ElMessage
.
warning
(
'请选择要删除的数据'
)
}
await
ElMessageBox
.
confirm
(
'确定要删除吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
)
const
str
=
selections
.
value
.
map
((
el
:
InterWarehousePage
)
=>
el
.
id
).
join
(
','
)
await
deleteWarehouseOutRecordApi
(
str
)
ElMessage
.
success
(
'删除成功'
)
search
()
await
getTreeNum
()
}
const
nodeClick
=
(
data
:
InterWarehouseTree
)
=>
{
nodeCode
.
value
=
data
.
code
??
''
sessionStorage
.
setItem
(
'InRecord_NodeCode'
,
data
.
code
??
''
)
search
()
}
const
searchDetail
=
async
()
=>
{
try
{
const
res
=
await
getItemListByIdApi
(
currentRow
.
value
?.
id
)
detailList
.
value
=
res
.
data
||
[]
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
getLogList
=
async
()
=>
{
try
{
const
res
=
await
getOutRecordLogApi
(
currentRow
.
value
?.
id
)
logList
.
value
=
res
.
data
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
locationList
=
ref
<
ILocation
[]
>
([])
const
locationLoading
=
ref
(
false
)
const
fetchLocationList
=
async
(
query
:
string
)
=>
{
if
(
!
editForm
.
value
.
warehouseId
)
return
locationLoading
.
value
=
true
try
{
const
res
=
await
getByWareHouseIdAndCodeApi
(
editForm
.
value
.
warehouseId
,
query
,
)
const
result
=
res
.
data
||
[]
locationList
.
value
=
result
.
map
((
item
:
ILocation
)
=>
{
return
{
locationId
:
item
.
id
,
locationCode
:
item
.
locationCode
,
}
}
)
}
catch
(
e
)
{
locationList
.
value
=
[]
}
finally
{
locationLoading
.
value
=
false
}
}
// 输入2秒后再调用接口(节流)
// const handleLocationSearch = debounce(fetchLocationList, 2000)
const
handleLocationChange
=
(
val
:
number
,
row
:
InterProductList
)
=>
{
const
found
=
locationList
.
value
.
find
(
(
item
:
InterProductList
)
=>
item
.
locationId
===
val
,
)
row
.
locationCode
=
found
?
found
.
locationCode
:
''
}
const
handleWarehouseChange
=
(
val
:
number
|
string
|
undefined
)
=>
{
const
found
=
warehouseList
.
value
.
find
(
(
item
:
warehouseInfo
)
=>
item
.
id
===
val
,
)
editForm
.
value
.
warehouseName
=
found
?
found
.
name
:
''
}
/**
* @description: 页面添加回车监听
*/
const
searchFormRef
=
ref
()
const
searchBtnRef
=
ref
()
useEnterKeyTrigger
({
formRef
:
searchFormRef
,
btnRef
:
searchBtnRef
,
callback
:
(
event
:
KeyboardEvent
)
=>
{
console
.
log
(
'回车键被按下'
,
event
)
search
()
}
,
}
)
onMounted
(()
=>
{
getTreeNum
()
getWarehouseList
()
}
)
<
/script
>
<
style
lang
=
"scss"
scoped
>
.
dialog
-
footer
{
display
:
inline
-
block
;
width
:
500
px
;
text
-
align
:
center
;
}
.
cursor
-
pointer
{
cursor
:
pointer
;
}
.
header
-
filter
-
form
{
:
deep
(.
el
-
form
-
item
)
{
margin
-
right
:
14
px
;
margin
-
bottom
:
10
px
;
}
}
.
product
-
dialog
-
footer
{
display
:
flex
;
justify
-
content
:
space
-
between
;
margin
:
8
px
0
;
}
$border
:
solid
1
px
#
ddd
;
.
send
-
order
-
list
{
display
:
grid
;
grid
-
template
-
columns
:
2
fr
1
fr
;
border
-
left
:
$border
;
border
-
top
:
$border
;
}
.
send
-
order
-
column
{
padding
:
10
px
16
px
;
line
-
height
:
1.5
;
}
.
send
-
order
-
header
{
font
-
weight
:
bold
;
text
-
align
:
center
;
background
-
color
:
#
f8f8f9
;
}
.
send
-
order
-
product
-
item
{
display
:
flex
;
justify
-
content
:
space
-
between
;
gap
:
20
px
;
&
:
not
(:
first
-
child
)
{
border
-
top
:
$border
;
padding
:
10
px
0
;
}
.
send
-
order
-
prop
-
list
{
flex
:
1
;
}
}
.
send
-
order
-
product
-
image
{
width
:
100
px
;
}
.
delivery
-
note
-
list
{
:
deep
(.
vertical
-
align
-
top
)
{
vertical
-
align
:
top
;
}
}
.
left
{
width
:
160
px
;
:
deep
(.
el
-
tree
-
node__content
)
{
height
:
30
px
;
line
-
height
:
30
px
;
}
:
deep
(.
el
-
tree
-
node__label
)
{
font
-
size
:
13
px
;
cursor
:
pointer
;
display
:
inline
-
block
;
width
:
100
%
;
color
:
black
!
important
;
padding
:
3
px
7
px
;
}
:
deep
(.
el
-
tree
-
node__expand
-
icon
)
{
display
:
none
;
}
:
deep
(.
is
-
current
)
{
.
tree
-
node
-
label
,
.
tree
-
node
-
count
{
background
-
color
:
#
ecf5ff
;
color
:
#
409
eff
!
important
;
}
.
el
-
tree
-
node__children
{
.
tree
-
node
-
label
,
.
tree
-
node
-
count
{
background
-
color
:
transparent
!
important
;
color
:
black
!
important
;
}
}
}
}
.
tree
-
node
{
display
:
flex
;
color
:
#
333
;
font
-
weight
:
500
;
}
.
right
{
flex
:
1
;
flex
-
shrink
:
0
;
background
:
white
;
overflow
:
hidden
;
}
::
v
-
deep
(.
el
-
tree
-
node__label
)
{
font
-
size
:
13
px
;
color
:
rgb
(
96
,
98
,
102
);
cursor
:
pointer
;
}
::
v
-
deep
(.
el
-
tree
-
node__expand
-
icon
)
{
display
:
none
;
}
::
v
-
deep
(.
el
-
tree
-
node__label
)
{
display
:
inline
-
block
;
width
:
100
%
;
color
:
black
!
important
;
padding
:
3
px
7
px
;
}
::
v
-
deep
(.
is
-
current
)
{
.
el
-
tree
-
node__label
{
background
-
color
:
#
ecf5ff
;
color
:
#
409
eff
!
important
;
}
.
el
-
tree
-
node__children
{
.
el
-
tree
-
node__label
{
background
-
color
:
transparent
!
important
;
color
:
black
!
important
;
}
}
}
::
v
-
deep
(.
splitpanes__pane
)
{
display
:
flex
;
flex
-
direction
:
column
;
}
::
v
-
deep
(.
splitpanes
--
horizontal
>
.
splitpanes__splitter
)
{
min
-
height
:
5
px
;
margin
-
top
:
10
px
;
}
.
draw
-
line
{
width
:
100
%
;
height
:
5
px
;
background
:
#
eff3f6
;
}
.
btn
-
list
{
margin
-
bottom
:
10
px
;
}
::
v
-
deep
(.
el
-
tree
-
node
)
{
cursor
:
pointer
;
margin
-
bottom
:
5
px
;
}
::
v
-
deep
(.
el
-
tree
-
node__label
)
{
font
-
size
:
14
px
;
}
::
v
-
deep
(.
el
-
tabs
)
{
display
:
flex
;
// flex-direction: column;
height
:
100
%
;
.
el
-
tabs__content
{
flex
:
1
;
flex
-
shrink
:
0
;
overflow
:
hidden
;
.
el
-
tab
-
pane
{
height
:
100
%
;
display
:
flex
;
flex
-
direction
:
column
;
overflow
:
hidden
;
}
}
}
.
delivery
-
note
-
page
{
:
deep
(
#
top
)
{
height
:
100
%
;
}
}
.
import
-
dialog
{
.
import
-
content
{
padding
-
bottom
:
20
px
;
}
}
.
import
-
success
{
text
-
align
:
center
;
padding
:
20
px
0
;
.
success
-
icon
{
font
-
size
:
48
px
;
margin
-
bottom
:
16
px
;
}
.
success
-
text
{
font
-
size
:
16
px
;
color
:
#
67
c23a
;
margin
-
bottom
:
16
px
;
}
.
file
-
link
{
a
{
color
:
#
409
eff
;
text
-
decoration
:
none
;
&
:
hover
{
text
-
decoration
:
underline
;
}
}
}
}
<
/style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment