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
9135e75f
Commit
9135e75f
authored
Jul 15, 2026
by
wusiyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 配货分拣规则支持商品类目条件 #1010170
parent
fb0cf8ba
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
368 additions
and
58 deletions
+368
-58
src/api/order.ts
+7
-0
src/types/api/order.ts
+3
-1
src/views/order/allocationSortingRule/components/addDialog.vue
+104
-53
src/views/order/allocationSortingRule/index.vue
+254
-4
No files found.
src/api/order.ts
View file @
9135e75f
...
...
@@ -780,3 +780,10 @@ export function getSortingLogApi(id: number) {
params
:
{
id
},
})
}
// 配货分拣 商品类目
export
function
getCategoryListApi
()
{
return
axios
.
get
<
never
,
BaseRespData
<
OperatorList
[]
>>
(
'allocation-sorting-rule/find-custom-category-list'
,
)
}
src/types/api/order.ts
View file @
9135e75f
...
...
@@ -304,6 +304,8 @@ export interface SortingList {
warehouseName
:
string
autoPrint
:
boolean
defaultWarehouse
:
0
|
1
matchType
:
1
|
2
// 匹配类型:1=所有条件(且) 2=任一条件(或)
conditions
:
SortingRuleCondition
[]
// 配货规则
}
// 配货分拣 详情
...
...
@@ -328,7 +330,7 @@ export interface SortingRuleCondition {
ruleId
?:
number
conditionType
:
string
// 条件
operator
:
string
// 操作符
conditionValue
?:
string
|
number
|
string
[]
conditionValue
?:
string
|
number
|
(
string
|
number
)
[]
}
// 配货分拣 配货区/操作符/条件
...
...
src/views/order/allocationSortingRule/components/addDialog.vue
View file @
9135e75f
...
...
@@ -63,9 +63,9 @@
<el-select
v-model=
"item.operator"
style=
"width: 1
6
0px"
style=
"width: 1
0
0px"
:disabled=
"isDisabled"
placeholder=
"
请选择
关系"
placeholder=
"关系"
>
<el-option
v-for=
"operator in getAvailableOperators(item.conditionType)"
...
...
@@ -79,13 +79,12 @@
<el-select
v-model=
"item.conditionValue"
class=
"platform-select"
style=
"width: 1
6
0px"
style=
"width: 1
9
0px"
:disabled=
"isDisabled"
placeholder=
"请选择平台"
filterable
multiple
collapse-tags
collapse-tags-tooltip
:max-collapse-tags=
"1"
clearable
popper-class=
"customize-select-style"
...
...
@@ -105,11 +104,46 @@
</el-option>
</el-select>
</
template
>
<
template
v-else-if=
"item.conditionType === 'order_category'"
>
<el-cascader
v-model=
"item.conditionValue"
class=
"category-cascader"
style=
"width: 190px"
:options=
"categoryList"
:props=
"
{
label: 'name',
value: 'id',
children: 'children',
multiple: true,
emitPath: true,
}"
collapse-tags
collapse-tags-tooltip
:max-collapse-tags="1"
tag-type="info"
show-checked-strategy="parent"
clearable
:disabled="isDisabled"
>
<template
#
tag=
"
{ data, deleteTag }">
<el-tag
v-for=
"tag in data.slice(0, 1)"
:key=
"tag.key"
type=
"info"
:closable=
"tag.closable"
class=
"category-cascader-tag"
@
close=
"deleteTag(tag)"
>
{{
tag
.
text
}}
</el-tag>
</
template
>
</el-cascader>
</template>
<
template
v-else
>
<el-input-number
v-model=
"item.conditionValue"
style=
"width: 1
6
0px"
style=
"width: 1
9
0px"
:min=
"1"
:max=
"999"
:disabled=
"isDisabled"
...
...
@@ -202,19 +236,7 @@ import type {
SortingRuleCondition
,
SortingRuleDetail
,
}
from
'@/types/api/order'
import
{
getAreaListApi
,
getConditionTypeListApi
,
getOperatorListApi
,
addAreaApi
,
editAreaApi
,
}
from
'@/api/order'
// 配货规则条件
const
pickingRuleList
=
ref
<
OperatorList
[]
>
()
// 配货规则操作符
const
operatorList
=
ref
<
OperatorList
[]
>
()
import
{
getAreaListApi
,
addAreaApi
,
editAreaApi
}
from
'@/api/order'
const
props
=
defineProps
<
{
modelValue
:
boolean
...
...
@@ -223,6 +245,9 @@ const props = defineProps<{
warehouseId
:
number
warehouseName
:
string
isDisabled
?:
boolean
categoryList
:
OperatorList
[]
pickingRuleList
:
OperatorList
[]
operatorList
:
OperatorList
[]
}
>
()
const
emit
=
defineEmits
<
{
(
e
:
'update:modelValue'
,
value
:
boolean
):
void
...
...
@@ -264,33 +289,38 @@ const getAvailablePickingRules = (currentIndex: number) => {
const
selectedFields
=
form
.
value
.
conditions
.
filter
((
_
,
index
)
=>
index
!==
currentIndex
)
.
map
((
item
)
=>
item
.
conditionType
)
return
p
ickingRuleList
.
value
?
.
filter
(
return
p
rops
.
pickingRuleList
.
filter
(
(
rule
)
=>
!
selectedFields
.
includes
(
rule
.
code
),
)
}
// 根据配货规则类型获取可用的操作符
const
getAvailableOperators
=
(
conditionType
:
string
)
=>
{
const
list
=
operatorList
.
value
??
[]
if
(
conditionType
===
'order_total_num'
)
{
return
list
.
filter
((
operator
)
=>
operator
.
code
!==
'in'
)
}
else
if
(
conditionType
===
'order_platform'
)
{
return
list
.
filter
((
operator
)
=>
operator
.
code
===
'in'
)
const
list
=
props
.
operatorList
const
matchers
:
Record
<
string
,
(
code
:
string
)
=>
boolean
>
=
{
order_total_num
:
(
code
)
=>
code
!==
'in'
&&
code
!==
'contain'
,
order_platform
:
(
code
)
=>
code
===
'in'
,
order_category
:
(
code
)
=>
code
===
'contain'
,
}
return
list
const
matcher
=
matchers
[
conditionType
]
return
matcher
?
list
.
filter
((
operator
)
=>
matcher
(
operator
.
code
))
:
list
}
// 配货规则条件改变
const
handleConditionTypeChange
=
(
item
:
SortingRuleCondition
)
=>
{
// 平台 只能选 in属于
if
(
item
.
conditionType
===
'order_platform'
)
{
switch
(
item
.
conditionType
)
{
case
'order_platform'
:
item
.
conditionValue
=
undefined
item
.
operator
=
'in'
}
// 订单总件数 清空字段
else
if
(
item
.
conditionType
===
'order_total_num'
)
{
break
case
'order_total_num'
:
item
.
conditionValue
=
undefined
item
.
operator
=
'gt'
break
case
'order_category'
:
item
.
conditionValue
=
undefined
item
.
operator
=
'contain'
break
}
}
...
...
@@ -311,7 +341,8 @@ const validateConditions = (
for
(
let
i
=
0
;
i
<
items
.
length
;
i
++
)
{
const
item
=
items
[
i
]
const
isValueValid
=
item
.
conditionType
===
'order_platform'
item
.
conditionType
===
'order_platform'
||
item
.
conditionType
===
'order_category'
?
Array
.
isArray
(
item
.
conditionValue
)
&&
item
.
conditionValue
.
length
>
0
:
item
.
conditionValue
!==
null
&&
item
.
conditionValue
!==
undefined
if
(
!
item
.
conditionType
||
!
isValueValid
||
!
item
.
operator
)
{
...
...
@@ -352,14 +383,29 @@ const rules = computed(() => {
const
platformList
=
platformJson
// 将详情返回的字符串条件值转为表单可用格式
const
formatConditionsForForm
=
(
conditions
:
SortingRuleCondition
[])
=>
conditions
.
map
((
item
)
=>
({
conditions
.
map
((
item
)
=>
{
if
(
typeof
item
.
conditionValue
!==
'string'
||
(
item
.
conditionType
!==
'order_platform'
&&
item
.
conditionType
!==
'order_category'
)
)
{
return
item
}
const
values
=
item
.
conditionValue
.
split
(
','
).
filter
(
Boolean
)
// 类目 id 一般为 number,需与级联 options 的 id 类型一致才能回显
if
(
item
.
conditionType
===
'order_category'
)
{
return
{
...
item
,
conditionValue
:
typeof
item
.
conditionValue
===
'string'
?
item
.
conditionValue
.
split
(
','
).
filter
(
Boolean
)
:
item
.
conditionValue
,
}))
conditionValue
:
values
.
map
((
v
)
=>
{
const
num
=
Number
(
v
)
return
Number
.
isNaN
(
num
)
?
v
:
num
}),
}
}
return
{
...
item
,
conditionValue
:
values
}
})
// 添加配货规则
const
addPickingRuleItem
=
()
=>
{
...
...
@@ -397,20 +443,6 @@ const loadAreaList = async () => {
}
}
// 获取配货规则条件
const
getConditionTypeList
=
async
()
=>
{
const
res
=
await
getConditionTypeListApi
()
if
(
res
.
code
!==
200
)
return
pickingRuleList
.
value
=
res
.
data
}
// 获取配货规则操作符
const
getOperatorList
=
async
()
=>
{
const
res
=
await
getOperatorListApi
()
if
(
res
.
code
!==
200
)
return
operatorList
.
value
=
res
.
data
}
// 提交
const
handleSubmit
=
async
()
=>
{
form
.
value
.
warehouseId
=
props
.
warehouseId
...
...
@@ -425,7 +457,9 @@ const handleSubmit = async () => {
(
item
):
SortingRuleCondition
=>
({
...
item
,
conditionValue
:
Array
.
isArray
(
item
.
conditionValue
)
?
item
.
conditionValue
.
join
(
','
)
?
item
.
conditionValue
.
map
((
v
)
=>
(
Array
.
isArray
(
v
)
?
v
[
v
.
length
-
1
]
:
v
))
.
join
(
','
)
:
item
.
conditionValue
,
}),
),
...
...
@@ -442,7 +476,7 @@ const handleSubmit = async () => {
}
onMounted
(
async
()
=>
{
await
Promise
.
all
([
loadAreaList
(),
getConditionTypeList
(),
getOperatorList
()]
)
await
loadAreaList
(
)
if
(
props
.
editData
)
{
form
.
value
=
{
...
props
.
editData
,
...
...
@@ -529,4 +563,21 @@ onMounted(async () => {
flex-wrap
:
wrap
;
}
}
//
cascader
样式,确保单行显示
.category-cascader
{
.el-cascader__tags
{
display
:
flex
!important
;
flex-wrap
:
nowrap
!important
;
align-items
:
center
;
overflow
:
hidden
;
}
.category-cascader-tag
{
flex
:
0
1
auto
!important
;
min-width
:
0
!important
;
max-width
:
100%
;
overflow
:
hidden
;
}
}
</
style
>
src/views/order/allocationSortingRule/index.vue
View file @
9135e75f
...
...
@@ -47,6 +47,61 @@
class=
"table-container"
>
<TableView
:paginated-data=
"sortingList"
:columns=
"columns"
>
<template
#
conditions=
"
{ row }">
<div
v-if=
"row.areaCode === 'AREA_0'"
>
默认分拣口
</div>
<div
v-else
class=
"conditions-text"
>
<div
class=
"match-type"
>
{{
row
.
matchType
===
1
?
'满足以下所有条件:'
:
'满足以下任一条件:'
}}
</div>
<div
v-for=
"(item, index) in row.conditions || []"
:key=
"index"
class=
"condition-item"
>
<span
class=
"condition-text"
:title=
"formatConditionText(item)"
>
{{
index
+
1
}}
、
{{
formatConditionText
(
item
)
}}
</span>
<el-popover
v-if=
"item.conditionType === 'order_category'"
placement=
"bottom"
:width=
"'auto'"
trigger=
"click"
>
<el-cascader-panel
class=
"category-cascader-panel-readonly"
:model-value=
"getCategoryCascaderValue(item)"
:options=
"categoryList"
:props=
"
{
label: 'name',
value: 'id',
children: 'children',
multiple: true,
emitPath: true,
checkOnClickNode: false,
checkOnClickLeaf: false,
}"
/>
<template
#
reference
>
<el-button
type=
"primary"
link
class=
"category-detail-btn"
>
详情
</el-button>
</
template
>
</el-popover>
</div>
</div>
</template>
<
template
#
autoPrint=
"{ row }"
>
{{
row
.
autoPrint
?
'开启'
:
'关闭'
}}
</
template
>
...
...
@@ -78,6 +133,9 @@
:edit-data=
"editData"
:warehouse-id=
"searchForm.warehouseId"
:warehouse-name=
"searchForm.warehouseName"
:category-list=
"categoryList"
:picking-rule-list=
"pickingRuleList"
:operator-list=
"operatorList"
@
submit=
"getSortingRuleList"
/>
<el-dialog
...
...
@@ -97,7 +155,9 @@ import { loadWarehouseListApi } from '@/api/common'
import
type
{
WarehouseListData
}
from
'@/types'
import
type
{
LogListData
,
OperatorList
,
SortingList
,
SortingRuleCondition
,
SortingRuleDetail
,
}
from
'@/types/api/order'
import
type
{
CustomColumn
}
from
'@/types/table'
...
...
@@ -107,6 +167,9 @@ import {
getConfigApi
,
updateConfighApi
,
getAreaInfoApi
,
getCategoryListApi
,
getConditionTypeListApi
,
getOperatorListApi
,
}
from
'@/api/order'
import
TableView
from
'@/components/TableView.vue'
import
LogList
from
'@/components/LogList.vue'
...
...
@@ -128,11 +191,13 @@ const columns: CustomColumn<SortingList>[] = [
align
:
'center'
,
},
{
key
:
'conditions
Text
'
,
prop
:
'conditions
Text
'
,
key
:
'conditions'
,
prop
:
'conditions'
,
label
:
'配货规则'
,
minWidth
:
250
,
align
:
'left'
,
slot
:
'conditions'
,
showOverflowTooltip
:
false
,
},
{
key
:
'areaName'
,
...
...
@@ -174,6 +239,9 @@ const columns: CustomColumn<SortingList>[] = [
const
warehouseList
=
ref
<
WarehouseListData
[]
>
([])
// 仓库列表
const
sortingList
=
ref
<
SortingList
[]
>
([])
// 规则列表
const
categoryList
=
ref
<
OperatorList
[]
>
([])
// 商品类目
const
pickingRuleList
=
ref
<
OperatorList
[]
>
([])
// 配货规则条件
const
operatorList
=
ref
<
OperatorList
[]
>
([])
// 配货规则操作符
const
isEnableSorting
=
ref
(
false
)
// 是否开启配货分拣
const
currentId
=
ref
(
0
)
// 当前操作id
const
isEdit
=
ref
(
false
)
// 是否编辑
...
...
@@ -188,6 +256,139 @@ const searchForm = ref({
warehouseName
:
''
,
})
type
CategoryNode
=
OperatorList
&
{
id
?:
number
|
string
name
?:
string
children
?:
CategoryNode
[]
}
// 获取节点下全部叶子 id
const
getCategoryLeafIds
=
(
node
:
CategoryNode
):
string
[]
=>
{
if
(
!
node
.
children
?.
length
)
return
node
.
id
!=
null
?
[
String
(
node
.
id
)]
:
[]
return
node
.
children
.
flatMap
(
getCategoryLeafIds
)
}
// 格式化已选类目:某项下所有子项都被选中时,只展示该项路径
const
formatSelectedCategories
=
(
list
:
CategoryNode
[],
selectedIds
:
Set
<
string
>
,
path
:
string
[]
=
[],
):
string
[]
=>
{
const
result
:
string
[]
=
[]
for
(
const
item
of
list
)
{
const
currentPath
=
[...
path
,
item
.
name
??
''
]
const
leafIds
=
getCategoryLeafIds
(
item
)
const
allSelected
=
leafIds
.
length
>
0
&&
leafIds
.
every
((
id
)
=>
selectedIds
.
has
(
id
))
if
(
allSelected
)
{
result
.
push
(
currentPath
.
join
(
'->'
))
continue
}
if
(
item
.
children
?.
length
)
{
result
.
push
(
...
formatSelectedCategories
(
item
.
children
,
selectedIds
,
currentPath
),
)
}
else
if
(
item
.
id
!=
null
&&
selectedIds
.
has
(
String
(
item
.
id
)))
{
result
.
push
(
currentPath
.
join
(
'->'
))
}
}
return
result
}
// 根据叶子 id 查找类目完整路径(用于级联回显)
const
findCategoryPath
=
(
list
:
CategoryNode
[],
targetId
:
string
|
number
,
path
:
(
string
|
number
)[]
=
[],
):
(
string
|
number
)[]
|
null
=>
{
for
(
const
item
of
list
)
{
if
(
item
.
id
==
null
)
continue
const
currentPath
=
[...
path
,
item
.
id
]
if
(
String
(
item
.
id
)
===
String
(
targetId
))
return
currentPath
if
(
item
.
children
?.
length
)
{
const
found
=
findCategoryPath
(
item
.
children
,
targetId
,
currentPath
)
if
(
found
)
return
found
}
}
return
null
}
// 将条件值转为级联回显路径
const
getCategoryCascaderValue
=
(
item
:
SortingRuleCondition
)
=>
{
const
ids
=
String
(
item
.
conditionValue
??
''
)
.
split
(
','
)
.
filter
(
Boolean
)
return
ids
.
map
((
id
)
=>
{
const
num
=
Number
(
id
)
return
findCategoryPath
(
categoryList
.
value
as
CategoryNode
[],
Number
.
isNaN
(
num
)
?
id
:
num
,
)
})
.
filter
((
path
):
path
is
(
string
|
number
)[]
=>
!!
path
)
}
// 配货规则展示文案
const
formatConditionText
=
(
item
:
SortingRuleCondition
)
=>
{
// 配货规则条件
const
typeDesc
=
pickingRuleList
.
value
.
find
((
r
)
=>
r
.
code
===
item
.
conditionType
)?.
desc
??
item
.
conditionType
// 配货规则操作符
const
operatorDesc
=
operatorList
.
value
.
find
((
r
)
=>
r
.
code
===
item
.
operator
)?.
desc
??
item
.
operator
let
valueText
=
''
switch
(
item
.
conditionType
)
{
case
'order_category'
:
{
const
selectedIds
=
new
Set
(
String
(
item
.
conditionValue
??
''
)
.
split
(
','
)
.
filter
(
Boolean
),
)
valueText
=
formatSelectedCategories
(
categoryList
.
value
as
CategoryNode
[],
selectedIds
,
).
join
(
'、'
)
break
}
case
'order_platform'
:
valueText
=
String
(
item
.
conditionValue
??
''
)
.
split
(
','
)
.
filter
(
Boolean
)
.
join
(
'、'
)
break
default
:
valueText
=
String
(
item
.
conditionValue
??
''
)
break
}
return
`
${
typeDesc
}${
operatorDesc
}
${
valueText
}
`
}
// 获取商品类目
const
getCategoryList
=
async
()
=>
{
const
res
=
await
getCategoryListApi
()
if
(
res
.
code
!==
200
)
return
categoryList
.
value
=
res
.
data
}
// 获取配货规则条件
const
getConditionTypeList
=
async
()
=>
{
const
res
=
await
getConditionTypeListApi
()
if
(
res
.
code
!==
200
)
return
pickingRuleList
.
value
=
res
.
data
}
// 获取配货规则操作符
const
getOperatorList
=
async
()
=>
{
const
res
=
await
getOperatorListApi
()
if
(
res
.
code
!==
200
)
return
operatorList
.
value
=
res
.
data
}
// 加载仓库列表
const
loadWarehouseList
=
async
()
=>
{
try
{
...
...
@@ -303,9 +504,16 @@ const deleteArea = async (id: number) => {
// }
onMounted
(
async
()
=>
{
await
loadWarehouseList
()
await
getFunctionSwitch
()
loading
.
value
=
true
await
Promise
.
all
([
loadWarehouseList
(),
getFunctionSwitch
(),
getCategoryList
(),
getConditionTypeList
(),
getOperatorList
(),
])
await
getSortingRuleList
()
loading
.
value
=
false
})
</
script
>
...
...
@@ -340,4 +548,46 @@ onMounted(async () => {
margin-top
:
10px
;
}
}
.conditions-text
{
padding
:
4px
0
;
line-height
:
1.6
;
overflow
:
hidden
;
.match-type
{
color
:
#666
;
margin-bottom
:
2px
;
}
.condition-item
{
display
:
flex
;
align-items
:
center
;
color
:
#333
;
overflow
:
hidden
;
min-width
:
0
;
.condition-text
{
flex
:
1
;
min-width
:
0
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.category-detail-btn
{
flex-shrink
:
0
;
margin-left
:
4px
;
padding
:
0
;
height
:
auto
;
}
}
}
</
style
>
<
style
lang=
"scss"
>
//
详情面板只读:可展开,禁止勾选
.category-cascader-panel-readonly
{
.el-checkbox
{
pointer-events
:
none
;
}
}
</
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