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
7dd60816
Commit
7dd60816
authored
Mar 31, 2026
by
qinjianhui
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 快速创建出库单功能开发
parent
8bdf60a3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
520 additions
and
18 deletions
+520
-18
.cursor/rules/factory-rule.mdc
+1
-0
src/views/order/factoryOrderNew/component/CreateOutboundDialog.vue
+455
-0
src/views/order/factoryOrderNew/component/PickFailDialog.vue
+64
-18
No files found.
.cursor/rules/factory-rule.mdc
View file @
7dd60816
...
@@ -13,6 +13,7 @@ Vue3 + Element Plus + Pinia + Axios + TypeScript + Vite
...
@@ -13,6 +13,7 @@ Vue3 + Element Plus + Pinia + Axios + TypeScript + Vite
- 当有上下两个子表时,使用 `src/components/splitDiv/splitDiv.vue` 组件,使用时注意传递对应的 props 和插槽
- 当有上下两个子表时,使用 `src/components/splitDiv/splitDiv.vue` 组件,使用时注意传递对应的 props 和插槽
- 表格相关布局时,使用 `src/components/TableView.vue` 组件,使用时注意传递对应的 props 和插槽
- 表格相关布局时,使用 `src/components/TableView.vue` 组件,使用时注意传递对应的 props 和插槽
- `ElDialog`组件中`footer`插槽都放在中间位置
- `ElDialog`组件中`footer`插槽都放在中间位置
- 写入表格`columns`时,如果需要对齐,使用`align`属性,数字相关的列, `align`属性值为`right`,长度一致的列, `align`属性值为`center`, 长度不一致的列, `align`属性值为`left`
## 接口相关
## 接口相关
...
...
src/views/order/factoryOrderNew/component/CreateOutboundDialog.vue
0 → 100644
View file @
7dd60816
<
template
>
<ElDialog
v-model=
"newDialogVisible"
title=
"创建出库单"
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
placeholder=
"请选择仓库"
style=
"width: 160px"
>
<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"
label=
"所属客户"
prop=
"userMark"
/>
<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-button
type=
"danger"
size=
"small"
@
click=
"deleteOtherWarehousing()"
>
删除
</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>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
BigNumber
from
'bignumber.js'
import
{
ElMessage
,
ElLoading
}
from
'element-plus'
import
ImageView
from
'@/components/ImageView.vue'
import
{
type
InterWarehouseDetail
,
type
InterProductList
,
type
InterskuList
,
type
ILocation
,
}
from
'@/types/api/warehouse'
import
{
warehouseInfoGetAll
,
type
warehouseInfo
,
getBySkuAndWarehouseIdApi
,
getByWareHouseIdAndCodeApi
,
addOutRecordApi
,
}
from
'@/api/warehouse'
interface
OpenParamsItem
{
thirdSkuCode
:
string
suggestOutQuantity
:
number
}
interface
OpenParams
{
warehouseId
:
number
|
string
warehouseName
?:
string
items
:
OpenParamsItem
[]
}
const
emit
=
defineEmits
<
{
success
:
[]
}
>
()
const
warehouseList
=
ref
<
warehouseInfo
[]
>
([])
const
locationList
=
ref
<
ILocation
[]
>
([])
const
newDialogVisible
=
ref
(
false
)
const
editFormRef
=
ref
()
const
editForm
=
ref
<
InterWarehouseDetail
>
({
outNo
:
''
,
warehouseId
:
''
,
warehouseName
:
''
,
remark
:
''
,
factoryCode
:
''
,
factoryId
:
0
,
productList
:
[],
})
const
rules
=
{
warehouseId
:
[{
required
:
true
,
message
:
'请选择仓库'
,
trigger
:
'change'
}],
}
const
otherPurchaseData
=
ref
<
InterProductList
[]
>
([])
const
otherWarehouseSelection
=
ref
<
InterProductList
[]
>
([])
const
setCostPrice
=
(
item
:
InterProductList
)
=>
{
if
(
item
.
costPrice
!==
0
&&
!
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
fetchWarehouseList
=
async
()
=>
{
if
(
warehouseList
.
value
.
length
)
return
try
{
const
res
=
await
warehouseInfoGetAll
()
warehouseList
.
value
=
res
.
data
||
[]
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
fetchLocationList
=
async
(
warehouseId
:
number
|
string
|
undefined
)
=>
{
if
(
!
warehouseId
)
return
try
{
const
res
=
await
getByWareHouseIdAndCodeApi
(
warehouseId
,
''
)
const
result
=
res
.
data
||
[]
locationList
.
value
=
result
.
map
((
item
:
ILocation
)
=>
{
return
{
locationId
:
item
.
id
,
locationCode
:
item
.
locationCode
,
}
})
}
catch
(
e
)
{
console
.
error
(
e
)
locationList
.
value
=
[]
}
}
const
handleLocationChange
=
(
val
:
number
,
row
:
InterProductList
)
=>
{
const
found
=
locationList
.
value
.
find
(
(
item
:
ILocation
)
=>
item
.
locationId
===
val
,
)
row
.
locationCode
=
found
?
found
.
locationCode
:
''
}
const
productSelectionChange
=
(
v
:
InterProductList
[])
=>
{
otherWarehouseSelection
.
value
=
v
}
const
deleteOtherWarehousing
=
()
=>
{
const
arr
=
otherWarehouseSelection
.
value
if
(
!
arr
.
length
)
return
const
idList
=
arr
.
map
((
v
:
InterProductList
)
=>
v
.
warehouseSku
)
otherPurchaseData
.
value
=
otherPurchaseData
.
value
.
filter
(
(
item
:
InterProductList
)
=>
!
idList
.
includes
(
item
.
warehouseSku
),
)
}
const
addOtherCurrency
=
async
()
=>
{
try
{
await
editFormRef
.
value
?.
validate
()
}
catch
{
return
}
const
arr
=
otherPurchaseData
.
value
if
(
!
arr
.
length
)
{
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
}
}
const
params
:
InterWarehouseDetail
=
{
...
editForm
.
value
,
productList
:
otherPurchaseData
.
value
,
}
try
{
await
addOutRecordApi
(
params
)
ElMessage
.
success
(
'保存成功'
)
newDialogVisible
.
value
=
false
emit
(
'success'
)
}
catch
(
e
)
{
console
.
error
(
e
)
}
}
const
open
=
async
(
params
:
OpenParams
)
=>
{
editForm
.
value
.
outNo
=
''
editForm
.
value
.
warehouseId
=
params
.
warehouseId
editForm
.
value
.
warehouseName
=
params
.
warehouseName
||
warehouseList
.
value
.
find
(
(
item
:
warehouseInfo
)
=>
item
.
id
===
params
.
warehouseId
,
)?.
name
||
''
editForm
.
value
.
remark
=
''
const
userJson
=
localStorage
.
getItem
(
'user'
)
if
(
userJson
)
{
try
{
const
userData
=
JSON
.
parse
(
userJson
)
editForm
.
value
.
factoryCode
=
userData
.
factoryCode
||
''
editForm
.
value
.
factoryId
=
userData
.
factoryId
||
0
}
catch
{
// ignore
}
}
otherPurchaseData
.
value
=
[]
const
skuList
=
params
.
items
.
map
((
item
)
=>
item
.
thirdSkuCode
)
.
filter
((
sku
)
=>
!!
sku
)
if
(
!
skuList
.
length
)
{
newDialogVisible
.
value
=
true
return
}
const
skuToSuggestQuantity
=
new
Map
<
string
,
number
>
()
params
.
items
.
forEach
((
item
)
=>
{
if
(
item
.
thirdSkuCode
)
{
skuToSuggestQuantity
.
set
(
item
.
thirdSkuCode
,
item
.
suggestOutQuantity
||
0
)
}
})
const
loading
=
ElLoading
.
service
({
text
:
'加载库存中...'
,
background
:
'rgba(0, 0, 0, 0.3)'
,
})
try
{
const
res
=
await
getBySkuAndWarehouseIdApi
(
params
.
warehouseId
,
'JM240915008_LBU_L'
,
)
if
(
res
.
code
!==
200
)
return
await
fetchWarehouseList
()
await
fetchLocationList
(
params
.
warehouseId
)
const
arr
:
InterskuList
[]
=
res
.
data
||
[]
const
mergedProductList
=
arr
.
map
((
skuItem
)
=>
{
const
warehouseSku
=
skuItem
.
warehouseSku
||
''
const
suggestOutQuantity
=
skuToSuggestQuantity
.
get
(
warehouseSku
)
??
0
const
outCount
=
suggestOutQuantity
const
costPrice
=
skuItem
.
price
??
0
const
totalPrice
=
new
BigNumber
(
outCount
)
.
multipliedBy
(
costPrice
||
0
)
.
toNumber
()
return
{
skuImage
:
skuItem
.
image
,
customerId
:
skuItem
.
customerId
,
userMark
:
skuItem
.
userMark
,
customerName
:
skuItem
.
customerName
,
currencyName
:
skuItem
.
currencyName
??
undefined
,
currencyCode
:
skuItem
.
currencyCode
??
undefined
,
warehouseSku
:
skuItem
.
warehouseSku
,
skuName
:
skuItem
.
skuName
,
productNo
:
skuItem
.
productNumber
,
locationCode
:
skuItem
.
locationCode
??
''
,
locationId
:
skuItem
.
locationId
??
null
,
costPrice
,
outCount
,
totalPrice
,
usableInventory
:
skuItem
.
usableInventory
,
inventoryId
:
skuItem
.
id
,
remark
:
skuItem
.
remark
??
null
,
}
as
InterProductList
})
otherPurchaseData
.
value
=
mergedProductList
}
catch
(
e
)
{
console
.
error
(
e
)
otherPurchaseData
.
value
=
[]
}
finally
{
loading
.
close
()
}
newDialogVisible
.
value
=
true
}
defineExpose
({
open
})
</
script
>
<
style
scoped
lang=
"scss"
>
.product-dialog-footer
{
display
:
flex
;
justify-content
:
space-between
;
margin
:
8px
0
;
}
</
style
>
src/views/order/factoryOrderNew/component/PickFailDialog.vue
View file @
7dd60816
...
@@ -19,7 +19,9 @@
...
@@ -19,7 +19,9 @@
<TableView
<TableView
:paginated-data=
"tableData"
:paginated-data=
"tableData"
:columns=
"columns"
:columns=
"columns"
selectionable
serial-numberable
serial-numberable
@
selection-change=
"handleSelectionChange"
>
>
<template
#
skuImage=
"
{ row }">
<template
#
skuImage=
"
{ row }">
<el-image
<el-image
...
@@ -53,39 +55,43 @@
...
@@ -53,39 +55,43 @@
<ElButton
@
click=
"visible = false"
>
取消
</ElButton>
<ElButton
@
click=
"visible = false"
>
取消
</ElButton>
</span>
</span>
<span
class=
"item"
>
<span
class=
"item"
>
<ElButton
<ElButton
type=
"primary"
@
click=
"handleCreateOutbound"
>
type=
"primary"
:loading=
"submitLoading"
@
click=
"handleCreateOutbound"
>
快速创建出库单
快速创建出库单
</ElButton>
</ElButton>
</span>
</span>
</div>
</div>
</
template
>
</
template
>
</ElDialog>
</ElDialog>
<CreateOutboundDialog
ref=
"createOutboundDialogRef"
@
success=
"() => {
visible = false
emit('success')
}"
/>
</template>
</template>
<
script
setup
lang=
"tsx"
>
<
script
setup
lang=
"tsx"
>
import
{
ref
}
from
'vue'
import
{
ref
}
from
'vue'
import
{
ElMessage
}
from
'element-plus'
import
{
ElMessage
}
from
'element-plus'
import
{
import
{
createOutboundOrderApi
,
applyForReplenishByIdApi
,
applyForReplenishByIdApi
,
}
from
'@/api/factoryOrderNew'
}
from
'@/api/factoryOrderNew'
import
type
{
operateOrderListData
}
from
'@/types/api/factoryOrderNew'
import
type
{
operateOrderListData
}
from
'@/types/api/factoryOrderNew'
import
TableView
from
'@/components/TableView.vue'
import
TableView
from
'@/components/TableView.vue'
import
_
from
'lodash'
import
_
from
'lodash'
import
CreateOutboundDialog
from
'./CreateOutboundDialog.vue'
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
success
:
[]
success
:
[]
}
>
()
}
>
()
const
visible
=
ref
(
false
)
const
visible
=
ref
(
false
)
const
submitLoading
=
ref
(
false
)
const
tableData
=
ref
<
operateOrderListData
[]
>
([])
const
tableData
=
ref
<
operateOrderListData
[]
>
([])
const
orderIds
=
ref
<
(
number
|
string
)[]
>
([])
const
orderIds
=
ref
<
(
number
|
string
)[]
>
([])
const
dialogTitle
=
ref
(
'拣胚失败'
)
const
dialogTitle
=
ref
(
'拣胚失败'
)
const
selections
=
ref
<
operateOrderListData
[]
>
([])
const
columns
=
[
const
columns
=
[
{
{
key
:
'warehouseName'
,
key
:
'warehouseName'
,
...
@@ -196,6 +202,10 @@ const columns = [
...
@@ -196,6 +202,10 @@ const columns = [
},
},
]
]
const
handleSelectionChange
=
(
selection
:
operateOrderListData
[])
=>
{
selections
.
value
=
selection
}
const
open
=
async
(
const
open
=
async
(
ids
:
(
number
|
string
)[],
ids
:
(
number
|
string
)[],
options
?:
{
title
?:
string
;
submitType
?:
string
},
options
?:
{
title
?:
string
;
submitType
?:
string
},
...
@@ -224,18 +234,54 @@ const handleClose = () => {
...
@@ -224,18 +234,54 @@ const handleClose = () => {
tableData
.
value
=
[]
tableData
.
value
=
[]
}
}
const
handleCreateOutbound
=
async
()
=>
{
const
createOutboundDialogRef
=
ref
<
InstanceType
<
submitLoading
.
value
=
true
typeof
CreateOutboundDialog
try
{
>
|
null
>
(
null
)
await
createOutboundOrderApi
(
orderIds
.
value
)
ElMessage
.
success
(
'创建出库单成功'
)
const
handleCreateOutbound
=
()
=>
{
visible
.
value
=
false
if
(
selections
.
value
.
length
===
0
)
{
emit
(
'success'
)
ElMessage
.
warning
(
'请至少选择一条数据'
)
}
catch
(
e
:
unknown
)
{
return
ElMessage
.
error
((
e
as
Error
)?.
message
||
'创建出库单失败'
)
}
}
finally
{
submitLoading
.
value
=
false
const
warehouseIds
=
_
.
uniq
(
selections
.
value
.
map
((
item
)
=>
item
.
warehouseId
)
.
filter
((
id
)
=>
id
!==
undefined
&&
id
!==
null
),
)
if
(
warehouseIds
.
length
!==
1
)
{
ElMessage
.
warning
(
'请选择相同仓库的库存SKU!'
)
return
}
const
warehouseId
=
warehouseIds
[
0
]
as
number
|
string
const
firstSelection
=
selections
.
value
[
0
]
as
operateOrderListData
&
{
warehouseName
?:
string
}
}
const
warehouseName
=
firstSelection
.
warehouseName
const
items
=
selections
.
value
.
map
((
item
)
=>
{
const
row
=
item
as
operateOrderListData
&
{
thirdSkuCode
?:
string
inventory
?:
number
producingQuantity
?:
number
}
return
{
thirdSkuCode
:
row
.
thirdSkuCode
||
''
,
suggestOutQuantity
:
_
.
subtract
(
Number
(
row
.
inventory
??
0
),
Number
(
row
.
producingQuantity
??
0
),
),
}
})
createOutboundDialogRef
.
value
?.
open
({
warehouseId
,
warehouseName
,
items
,
})
}
}
defineExpose
({
open
})
defineExpose
({
open
})
...
...
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