Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-dml
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-dml
Commits
080fbef8
提交
080fbef8
authored
11月 03, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 增加商品规格管理功能,支持动态添加、删除规格及其值,并自动生成SKU列表
上级
d96644db
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
342 行增加
和
6 行删除
+342
-6
FormPrice.vue
src/modules/live/product/management/components/FormPrice.vue
+340
-5
Update.vue
src/modules/live/product/management/views/Update.vue
+2
-1
没有找到文件。
src/modules/live/product/management/components/FormPrice.vue
浏览文件 @
080fbef8
<
script
setup
>
import
{
Plus
,
QuestionFilled
}
from
'@element-plus/icons-vue'
import
{
Plus
,
Delete
,
QuestionFilled
}
from
'@element-plus/icons-vue'
import
{
ElMessage
}
from
'element-plus'
import
{
deliveryMode
,
deliveryTime
,
orderStockCount
}
from
'@/utils/dictionary'
const
form
=
inject
(
'form'
)
// 初始化规格列表
if
(
!
form
.
info
.
specs
)
{
form
.
info
.
specs
=
[]
}
// 初始化sku列表
if
(
!
form
.
info
.
sku
)
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
}
// 确保在没有规格时至少有一个SKU
onMounted
(()
=>
{
ensureSkuExists
()
})
function
ensureSkuExists
()
{
const
hasValidSpecs
=
form
.
info
.
specs
&&
form
.
info
.
specs
.
some
((
spec
)
=>
spec
.
name
&&
spec
.
name
.
trim
())
if
(
!
hasValidSpecs
&&
(
!
form
.
info
.
sku
||
form
.
info
.
sku
.
length
===
0
))
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
}
}
// 添加规格
function
addSpec
()
{
form
.
info
.
specs
.
push
({
name
:
''
,
values
:
[
''
],
})
}
// 删除规格
function
removeSpec
(
index
)
{
form
.
info
.
specs
.
splice
(
index
,
1
)
generateSkuList
()
}
// 添加规格值
function
addSpecValue
(
specIndex
)
{
form
.
info
.
specs
[
specIndex
].
values
.
push
(
''
)
}
// 删除规格值
function
removeSpecValue
(
specIndex
,
valueIndex
)
{
if
(
form
.
info
.
specs
[
specIndex
].
values
.
length
>
1
)
{
form
.
info
.
specs
[
specIndex
].
values
.
splice
(
valueIndex
,
1
)
generateSkuList
()
}
}
// 笛卡尔积算法:生成所有规格值的组合
function
cartesianProduct
(
arrays
)
{
if
(
arrays
.
length
===
0
)
return
[]
if
(
arrays
.
length
===
1
)
return
arrays
[
0
].
map
((
item
)
=>
[
item
])
const
[
first
,
...
rest
]
=
arrays
const
restProduct
=
cartesianProduct
(
rest
)
const
result
=
[]
for
(
const
item
of
first
)
{
for
(
const
restItem
of
restProduct
)
{
result
.
push
([
item
,
...
restItem
])
}
}
return
result
}
// 生成SKU列表
function
generateSkuList
()
{
// 过滤掉名称为空的规格和值为空的规格值
const
validSpecs
=
form
.
info
.
specs
.
filter
((
spec
)
=>
spec
.
name
&&
spec
.
name
.
trim
())
// 如果没有有效规格,确保至少有一个SKU
if
(
validSpecs
.
length
===
0
)
{
if
(
!
form
.
info
.
sku
||
form
.
info
.
sku
.
length
===
0
)
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
}
else
if
(
form
.
info
.
sku
.
length
===
1
&&
!
form
.
info
.
sku
[
0
].
specs
)
{
// 保持单个SKU,但不添加specs字段
form
.
info
.
sku
[
0
]
=
{
price
:
form
.
info
.
sku
[
0
].
price
||
''
,
stock
:
form
.
info
.
sku
[
0
].
stock
||
0
,
}
}
else
{
// 如果之前有多个SKU(有规格时),现在没有规格了,只保留第一个或创建一个新的
const
firstSku
=
form
.
info
.
sku
[
0
]
form
.
info
.
sku
=
[
{
price
:
firstSku
?.
price
||
''
,
stock
:
firstSku
?.
stock
||
0
,
},
]
}
return
}
// 获取所有规格的有效值数组
const
specValuesArrays
=
validSpecs
.
map
((
spec
)
=>
spec
.
values
.
filter
((
val
)
=>
val
&&
val
.
trim
()).
map
((
val
)
=>
val
.
trim
())
)
// 如果任何规格没有有效值,确保至少有一个SKU
if
(
specValuesArrays
.
some
((
arr
)
=>
arr
.
length
===
0
))
{
if
(
!
form
.
info
.
sku
||
form
.
info
.
sku
.
length
===
0
)
{
form
.
info
.
sku
=
[{
price
:
''
,
stock
:
0
}]
}
else
if
(
form
.
info
.
sku
.
length
===
1
&&
!
form
.
info
.
sku
[
0
].
specs
)
{
form
.
info
.
sku
[
0
]
=
{
price
:
form
.
info
.
sku
[
0
].
price
||
''
,
stock
:
form
.
info
.
sku
[
0
].
stock
||
0
,
}
}
return
}
// 生成笛卡尔积
const
combinations
=
cartesianProduct
(
specValuesArrays
)
// 生成SKU列表,保留已存在的价格和库存数据
const
existingSkuMap
=
new
Map
()
form
.
info
.
sku
.
forEach
((
sku
)
=>
{
// 如果有specs字段,使用specs作为key;否则使用空数组作为key
const
key
=
JSON
.
stringify
(
sku
.
specs
||
[])
existingSkuMap
.
set
(
key
,
{
price
:
sku
.
price
||
''
,
stock
:
sku
.
stock
||
0
})
})
form
.
info
.
sku
=
combinations
.
map
((
combo
)
=>
{
const
key
=
JSON
.
stringify
(
combo
)
const
existing
=
existingSkuMap
.
get
(
key
)
return
{
specs
:
combo
,
price
:
existing
?.
price
||
''
,
stock
:
existing
?.
stock
||
0
,
}
})
}
// 计算已添加的规格数量
const
specCount
=
computed
(()
=>
form
.
info
.
specs
.
length
)
const
maxSpecCount
=
3
// 最多3个规格
// 判断是否有有效的规格
const
hasSpecs
=
computed
(()
=>
{
return
(
form
.
info
.
specs
&&
form
.
info
.
specs
.
some
((
spec
)
=>
{
if
(
!
spec
.
name
||
!
spec
.
name
.
trim
())
return
false
return
spec
.
values
&&
spec
.
values
.
some
((
val
)
=>
val
&&
val
.
trim
())
})
)
})
// 监听规格变化,当没有规格时确保SKU存在
watch
(
hasSpecs
,
(
newVal
)
=>
{
if
(
!
newVal
)
{
ensureSkuExists
()
}
})
// 批量设置相关
const
batchPrice
=
ref
(
''
)
const
batchStock
=
ref
(
''
)
// 批量设置价格
function
batchSetPrice
()
{
if
(
batchPrice
.
value
===
''
||
batchPrice
.
value
===
null
||
batchPrice
.
value
===
undefined
)
return
const
price
=
batchPrice
.
value
form
.
info
.
sku
.
forEach
((
sku
)
=>
{
sku
.
price
=
price
})
batchPrice
.
value
=
''
}
// 批量设置库存
function
batchSetStock
()
{
if
(
batchStock
.
value
===
''
||
batchStock
.
value
===
null
||
batchStock
.
value
===
undefined
)
return
const
stock
=
Number
(
batchStock
.
value
)
if
(
isNaN
(
stock
)
||
stock
<
0
)
{
ElMessage
({
message
:
'请输入有效的库存数量'
,
type
:
'warning'
})
return
}
form
.
info
.
sku
.
forEach
((
sku
)
=>
{
sku
.
stock
=
stock
})
batchStock
.
value
=
''
}
</
script
>
<
template
>
...
...
@@ -16,7 +202,57 @@ const form = inject('form')
</el-form-item>
<el-form-item
label=
"商品规格"
>
<div
class=
"form-tips"
>
准确填写规格信息,有助于商品在搜索场景获取更多流量
</div>
<el-button
size=
"large"
:icon=
"Plus"
>
添加规格(1/3)
</el-button>
<div
class=
"spec-list"
>
<div
v-for=
"(spec, specIndex) in form.info.specs"
:key=
"specIndex"
class=
"spec-item"
>
<div
class=
"spec-item-header"
>
<el-input
v-model=
"spec.name"
placeholder=
"请输入规格名称(如:颜色、尺寸)"
style=
"flex: 1"
@
blur=
"generateSkuList"
>
</el-input>
<el-button
type=
"danger"
plain
:icon=
"Delete"
circle
size=
"small"
@
click=
"removeSpec(specIndex)"
style=
"margin-left: 12px"
title=
"删除规格"
>
</el-button>
</div>
<div
class=
"spec-values"
>
<div
v-for=
"(value, valueIndex) in spec.values"
:key=
"valueIndex"
class=
"spec-value-item"
>
<el-input
v-model=
"spec.values[valueIndex]"
placeholder=
"请输入规格值"
@
blur=
"generateSkuList"
>
</el-input>
<el-button
type=
"danger"
plain
:icon=
"Delete"
circle
size=
"small"
@
click=
"removeSpecValue(specIndex, valueIndex)"
:disabled=
"spec.values.length
<
=
1
"
style=
"margin-left: 8px"
title=
"删除规格值"
>
</el-button>
</div>
<el-button
type=
"primary"
plain
:icon=
"Plus"
size=
"small"
@
click=
"addSpecValue(specIndex)"
style=
"margin-top: 8px"
>
添加规格值
</el-button>
</div>
</div>
<el-button
type=
"primary"
plain
:icon=
"Plus"
@
click=
"addSpec"
:disabled=
"specCount >= maxSpecCount"
>
添加规格(
{{
specCount
}}
/
{{
maxSpecCount
}}
)
</el-button>
</div>
</el-form-item>
<el-form-item
label=
"发货时效"
prop=
"info.delivery_time"
>
<div
class=
"form-tips"
>
...
...
@@ -27,15 +263,40 @@ const form = inject('form')
</el-radio-group>
</el-form-item>
<el-form-item
label=
"价格与库存"
required
>
<el-table
:data=
"form.info.sku"
border
:header-cell-style=
"
{ backgroundColor: '#f5f7fa' }">
<div
class=
"form-tips"
v-if=
"hasSpecs"
>
根据规格自动生成,请为每个SKU填写价格和库存
</div>
<div
class=
"form-tips"
v-else
>
请填写商品价格和库存
</div>
<div
class=
"batch-set-container"
v-if=
"form.info.sku && form.info.sku.length > 0"
>
<div
class=
"batch-set-item"
>
<el-input
v-model=
"batchPrice"
placeholder=
"批量设置价格"
type=
"number"
style=
"width: 200px"
>
<template
#
prefix
>
¥
</
template
>
</el-input>
<el-button
type=
"primary"
plain
@
click=
"batchSetPrice"
style=
"margin-left: 8px"
>
批量设置价格
</el-button>
</div>
<div
class=
"batch-set-item"
>
<el-input
v-model=
"batchStock"
placeholder=
"批量设置库存"
type=
"number"
style=
"width: 200px"
>
<
template
#
suffix
>
件
</
template
>
</el-input>
<el-button
type=
"primary"
plain
@
click=
"batchSetStock"
style=
"margin-left: 8px"
>
批量设置库存
</el-button>
</div>
</div>
<el-table
:data=
"form.info.sku"
border
:header-cell-style=
"{ backgroundColor: '#f5f7fa' }"
style=
"width: 100%"
>
<el-table-column
v-if=
"hasSpecs"
label=
"规格组合"
align=
"center"
width=
"200"
>
<
template
#
default=
"{ row }"
>
<span>
{{
row
.
specs
?
row
.
specs
.
join
(
' / '
)
:
'-'
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"price"
label=
"价格"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<el-input
v-model=
"row.price"
placeholder=
"请输入"
></el-input>
<el-input
v-model=
"row.price"
placeholder=
"请输入价格"
type=
"number"
>
<template
#
prefix
>
¥
</
template
>
</el-input>
</template>
</el-table-column>
<el-table-column
prop=
"stock"
label=
"库存"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<el-input
v-model=
"row.stock"
placeholder=
"请输入"
></el-input>
<el-input
v-model
.
number=
"row.stock"
placeholder=
"请输入库存"
type=
"number"
>
<template
#
suffix
>
件
</
template
>
</el-input>
</template>
</el-table-column>
</el-table>
...
...
@@ -59,3 +320,77 @@ const form = inject('form')
</el-form-item>
</div>
</template>
<
style
scoped
>
.spec-list
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
16px
;
}
.spec-item
{
flex
:
1
;
min-width
:
300px
;
padding
:
16px
;
border
:
1px
solid
var
(
--el-border-color-lighter
);
border-radius
:
8px
;
background-color
:
#fafafa
;
}
.spec-item-header
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
16px
;
padding-bottom
:
12px
;
border-bottom
:
1px
solid
var
(
--el-border-color-lighter
);
}
.spec-values
{
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
}
.spec-value-item
{
display
:
flex
;
align-items
:
center
;
}
.batch-set-container
{
display
:
flex
;
gap
:
16px
;
margin-bottom
:
16px
;
padding
:
16px
;
background-color
:
#f8f9fa
;
border-radius
:
8px
;
border
:
1px
solid
var
(
--el-border-color-lighter
);
}
.batch-set-item
{
display
:
flex
;
align-items
:
center
;
}
@media
(
max-width
:
768px
)
{
.spec-list
{
flex-direction
:
column
;
}
.spec-item
{
min-width
:
100%
;
}
.batch-set-container
{
flex-direction
:
column
;
gap
:
12px
;
}
.batch-set-item
{
width
:
100%
;
}
.batch-set-item
.el-input
{
flex
:
1
;
}
}
</
style
>
src/modules/live/product/management/views/Update.vue
浏览文件 @
080fbef8
...
...
@@ -34,7 +34,8 @@ const form = reactive({
order_stock_count
:
'1'
,
shipping_template
:
''
,
// 运费模板
after_sales_policy
:
''
,
// 售后政策
sku
:
[{
price
:
''
,
stock
:
0
}],
specs
:
[],
// 规格列表
sku
:
[],
// SKU列表(通过规格笛卡尔积生成)
},
})
provide
(
'form'
,
form
)
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论