Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
0fba3fd5
authored
May 24, 2023
by
YunaiV
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
review + mall:商品管理
parent
c296c9e5
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
63 additions
and
59 deletions
+63
-59
build/vite/optimize.ts
+2
-1
src/api/mall/product/spu.ts
+2
-0
src/utils/index.ts
+1
-0
src/views/mall/product/spu/addForm.vue
+0
-1
src/views/mall/product/spu/components/BasicInfoForm.vue
+7
-10
src/views/mall/product/spu/components/OtherSettingsForm.vue
+5
-8
src/views/mall/product/spu/components/SkuList.vue
+32
-18
src/views/mall/product/spu/index.vue
+14
-21
No files found.
build/vite/optimize.ts
View file @
0fba3fd5
...
...
@@ -102,7 +102,8 @@ const include = [
'element-plus/es/components/timeline-item/style/css'
,
'element-plus/es/components/collapse/style/css'
,
'element-plus/es/components/collapse-item/style/css'
,
'element-plus/es/components/button-group/style/css'
'element-plus/es/components/button-group/style/css'
,
'element-plus/es/components/text/style/css'
]
const
exclude
=
[
'@iconify/json'
]
...
...
src/api/mall/product/spu.ts
View file @
0fba3fd5
...
...
@@ -7,6 +7,7 @@ export interface Property {
valueName
?:
string
// 属性值名称
}
// TODO puhui999:是不是直接叫 Sku 更简洁一点哈。type 待后面,总感觉有个类型?
export
interface
SkuType
{
id
?:
number
// 商品 SKU 编号
spuId
?:
number
// SPU 编号
...
...
@@ -24,6 +25,7 @@ export interface SkuType {
salesCount
?:
number
// 商品销量
}
// TODO puhui999:是不是直接叫 Spu 更简洁一点哈。type 待后面,总感觉有个类型?
export
interface
SpuType
{
id
?:
number
name
?:
string
// 商品名称
...
...
src/utils/index.ts
View file @
0fba3fd5
...
...
@@ -174,6 +174,7 @@ export const copyValueToTarget = (target, source) => {
Object
.
assign
(
target
,
newObj
)
}
// TODO @puhui999:返回要带上 .00 哈.例如说 1.00
/**
* 将一个整数转换为分数保留两位小数
* @param num
...
...
src/views/mall/product/spu/addForm.vue
View file @
0fba3fd5
...
...
@@ -146,7 +146,6 @@ const submitForm = async () => {
const
newSliderPicUrls
=
[]
deepCopyFormData
.
sliderPicUrls
.
forEach
((
item
)
=>
{
// 如果是前端选的图
// TODO @puhui999:疑问哈,为啥会是 object 呀?fix
typeof
item
===
'object'
?
newSliderPicUrls
.
push
(
item
.
url
)
:
newSliderPicUrls
.
push
(
item
)
})
deepCopyFormData
.
sliderPicUrls
=
newSliderPicUrls
...
...
src/views/mall/product/spu/components/BasicInfoForm.vue
View file @
0fba3fd5
...
...
@@ -96,7 +96,6 @@
<!-- 多规格添加-->
<el-col
:span=
"24"
>
<el-form-item
v-if=
"formData.specType"
label=
"商品属性"
>
<!-- TODO @puhui999:参考 https://admin.java.crmeb.net/store/list/creatProduct 添加规格好做么?添加的时候,不用输入备注哈 fix-->
<el-button
class=
"mr-15px mb-10px"
@
click=
"attributesAddFormRef.open"
>
添加规格
</el-button>
<ProductAttributes
:propertyList=
"propertyList"
@
success=
"generateSkus"
/>
</el-form-item>
...
...
@@ -137,9 +136,8 @@ const props = defineProps({
},
activeName
:
propTypes
.
string
.
def
(
''
)
})
const
attributesAddFormRef
=
ref
()
// 添加商品属性表单 TODO @puhui999:小写开头哈 fix
const
productSpuBasicInfoRef
=
ref
()
// 表单Ref TODO @puhui999:小写开头哈 fix
// TODO @puhui999:attributeList 改成 propertyList,会更统一一点 fix
const
attributesAddFormRef
=
ref
()
// 添加商品属性表单
const
productSpuBasicInfoRef
=
ref
()
// 表单 Ref
const
propertyList
=
ref
([])
// 商品属性列表
const
skuListRef
=
ref
()
// 商品属性列表Ref
/** 调用 SkuList generateTableData 方法*/
...
...
@@ -180,17 +178,17 @@ const rules = reactive({
watch
(
()
=>
props
.
propFormData
,
(
data
)
=>
{
if
(
!
data
)
return
// fix:三个表单组件监听赋值必须使用 copyValueToTarget 使用 formData.value = data 会监听非常多次
if
(
!
data
)
{
return
}
copyValueToTarget
(
formData
,
data
)
// fix: 多图上传组件需要一个包含url属性的对象才能正常回显
formData
.
sliderPicUrls
=
data
[
'sliderPicUrls'
].
map
((
item
)
=>
({
url
:
item
}))
// TODO @puhui999:if return,减少嵌套层级
// 只有是多规格才处理
if
(
formData
.
specType
)
{
// TODO @puhui999:可以直接拿 propertyName 拼接处规格 id + 属性,可以看下商品 uniapp 详情的做法
// fix: 直接拿返回的 skus 属性逆向生成出 propertyList
// 直接拿返回的 skus 属性逆向生成出 propertyList
const
properties
=
[]
formData
.
skus
.
forEach
((
sku
)
=>
{
sku
.
properties
.
forEach
(({
propertyId
,
propertyName
,
valueId
,
valueName
})
=>
{
...
...
@@ -209,7 +207,6 @@ watch(
}
},
{
// fix: 去掉深度监听只有对象引用发生改变的时候才执行,解决改一动多的问题
immediate
:
true
}
)
...
...
src/views/mall/product/spu/components/OtherSettingsForm.vue
View file @
0fba3fd5
<
template
>
<el-form
ref=
"otherSettingsFormRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
>
<el-row>
<!-- TODO @puhui999:横着三个哈 fix-->
<el-col
:span=
"24"
>
<el-row
:gutter=
"20"
>
<el-col
:span=
"8"
>
...
...
@@ -35,7 +34,7 @@
</el-form-item>
</el-col>
<el-col
:span=
"24"
>
<!-- TODO tag展示暂时不考虑排序
-->
<!-- TODO tag展示暂时不考虑排序 -->
<el-form-item
label=
"活动优先级"
>
<el-tag>
默认
</el-tag>
<el-tag
class=
"ml-2"
type=
"success"
>
秒杀
</el-tag>
...
...
@@ -86,7 +85,6 @@ const rules = reactive({
giveIntegral
:
[
required
],
virtualSalesCount
:
[
required
]
})
// TODO @puhui999:这种叫 recommendOptions 会更合适哈 fix
const
recommendOptions
=
[
{
name
:
'是否热卖'
,
value
:
'recommendHot'
},
{
name
:
'是否优惠'
,
value
:
'recommendBenefit'
},
...
...
@@ -98,7 +96,6 @@ const checkboxGroup = ref<string[]>([]) // 选中的推荐选项
/** 选择商品后赋值 */
const
onChangeGroup
=
()
=>
{
// TODO @puhui999:是不是可以遍历 recommend,然后进行是否选中;fix
recommendOptions
.
forEach
(({
value
})
=>
{
formData
.
value
[
value
]
=
checkboxGroup
.
value
.
includes
(
value
)
})
...
...
@@ -110,21 +107,21 @@ const onChangeGroup = () => {
watch
(
()
=>
props
.
propFormData
,
(
data
)
=>
{
if
(
!
data
)
return
// fix:三个表单组件监听赋值必须使用 copyValueToTarget 使用 formData.value = data 会监听非常多次
if
(
!
data
)
{
return
}
copyValueToTarget
(
formData
.
value
,
data
)
recommendOptions
.
forEach
(({
value
})
=>
{
// TODO 如果先修改其他设置的值,再改变商品详情或是商品信息会重置其他设置页面中的相关值 fix:已修复
if
(
formData
.
value
[
value
]
&&
!
checkboxGroup
.
value
.
includes
(
value
))
{
checkboxGroup
.
value
.
push
(
value
)
}
})
},
{
// fix: 去掉深度监听只有对象引用发生改变的时候才执行,解决改一动多的问题
immediate
:
true
}
)
/**
* 表单校验
*/
...
...
src/views/mall/product/spu/components/SkuList.vue
View file @
0fba3fd5
...
...
@@ -12,7 +12,7 @@
</
template
>
</el-table-column>
<
template
v-if=
"formData.specType && !isBatch"
>
<!-- 根据商品属性动态添加
-->
<!-- 根据商品属性动态添加 -->
<el-table-column
v-for=
"(item, index) in tableHeaders"
:key=
"index"
...
...
@@ -21,17 +21,16 @@
min-width=
"120"
>
<template
#
default=
"
{ row }">
<!-- TODO puhui999:展示成蓝色,有点区分度哈 -->
{{
row
.
properties
[
index
]?.
valueName
}}
</
template
>
</el-table-column>
</template>
<!-- TODO @puhui999: controls-position=" " 可以去掉哈,不然太长了,手动输入更方便 fix -->
<el-table-column
align=
"center"
label=
"商品条码"
min-width=
"168"
>
<
template
#
default=
"{ row }"
>
<el-input
v-model=
"row.barCode"
class=
"w-100%"
/>
</
template
>
</el-table-column>
<!-- TODO @puhui999:用户输入的时候,是按照元;分主要是我们自己用;fix -->
<el-table-column
align=
"center"
label=
"销售价(元)"
min-width=
"168"
>
<
template
#
default=
"{ row }"
>
<el-input-number
v-model=
"row.price"
:min=
"0"
:precision=
"2"
:step=
"0.1"
class=
"w-100%"
/>
...
...
@@ -141,6 +140,7 @@ const skuList = ref<SkuType[]>([
subCommissionSecondPrice
:
0
// 二级分销的佣金
}
])
// 批量添加时的临时数据
// TODO @puhui999:保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。
/** 批量添加 */
const
batchAdd
=
()
=>
{
...
...
@@ -148,6 +148,7 @@ const batchAdd = () => {
copyValueToTarget
(
item
,
skuList
.
value
[
0
])
})
}
/** 删除 sku */
const
deleteSku
=
(
row
)
=>
{
const
index
=
formData
.
value
.
skus
.
findIndex
(
...
...
@@ -159,7 +160,7 @@ const deleteSku = (row) => {
const
tableHeaders
=
ref
<
{
prop
:
string
;
label
:
string
}[]
>
([])
// 多属性表头
/**
* 将传进来的值赋值给skuList
* 将传进来的值赋值给
skuList
*/
watch
(
()
=>
props
.
propFormData
,
...
...
@@ -173,11 +174,10 @@ watch(
}
)
// TODO @芋艿:看看 chatgpt 可以进一步下面几个方法的实现不 fix: 添加相关处理逻辑解决编辑表单时或查看详情时数据回显问题
/** 生成表数据 */
const
generateTableData
=
(
propertyList
:
any
[])
=>
{
// 构建数据结构
fix: 使用map替换多重for循环
const
propert
iesItemList
=
propertyList
.
map
((
item
)
=>
// 构建数据结构
const
propert
yValues
=
propertyList
.
map
((
item
)
=>
item
.
values
.
map
((
v
)
=>
({
propertyId
:
item
.
id
,
propertyName
:
item
.
name
,
...
...
@@ -185,15 +185,16 @@ const generateTableData = (propertyList: any[]) => {
valueName
:
v
.
name
}))
)
const
buildList
=
build
(
propertiesItemList
)
// TODO @puhui:是不是 buildSkuList,这样容易理解一点哈。item 改成 sku
const
buildList
=
build
(
propertyValues
)
// 如果回显的 sku 属性和添加的属性不一致则重置 skus 列表
if
(
!
validateData
(
propertyList
))
{
// 如果不一致则重置表数据,默认添加新的属性重新生成
sku
列表
// 如果不一致则重置表数据,默认添加新的属性重新生成
sku
列表
formData
.
value
!
.
skus
=
[]
}
for
(
const
item
of
buildList
)
{
const
row
=
{
properties
:
Array
.
isArray
(
item
)
?
item
:
[
item
],
// 如果只有一个属性的话返回的是一个
property
对象
properties
:
Array
.
isArray
(
item
)
?
item
:
[
item
],
// 如果只有一个属性的话返回的是一个
property
对象
price
:
0
,
marketPrice
:
0
,
costPrice
:
0
,
...
...
@@ -205,16 +206,17 @@ const generateTableData = (propertyList: any[]) => {
subCommissionFirstPrice
:
0
,
subCommissionSecondPrice
:
0
}
// 如果存在属性相同的 sku 则不做处理
const
index
=
formData
.
value
!
.
skus
.
findIndex
(
(
sku
)
=>
JSON
.
stringify
(
sku
.
properties
)
===
JSON
.
stringify
(
row
.
properties
)
)
// 如果存在属性相同的 sku 则不做处理
if
(
index
!==
-
1
)
{
continue
}
formData
.
value
.
skus
.
push
(
row
)
}
}
/**
* 生成 skus 前置校验
*/
...
...
@@ -232,6 +234,7 @@ const validateData = (propertyList: any[]) => {
const
propertyIds
=
propertyList
.
map
((
item
)
=>
item
.
id
)
return
skuPropertyIds
.
length
===
propertyIds
.
length
}
/** 构建所有排列组合 */
const
build
=
(
propertyValuesList
:
Property
[][])
=>
{
if
(
propertyValuesList
.
length
===
0
)
{
...
...
@@ -255,13 +258,15 @@ const build = (propertyValuesList: Property[][]) => {
}
}
/** 监听属性列表生成相关参数和表头 */
/** 监听属性列表
,
生成相关参数和表头 */
watch
(
()
=>
props
.
propertyList
,
(
propertyList
)
=>
{
// 如果不是多规格则结束
if
(
!
formData
.
value
.
specType
)
return
// 如果当前组件作为批量添加数据使用则重置表数据
if
(
!
formData
.
value
.
specType
)
{
return
}
// 如果当前组件作为批量添加数据使用,则重置表数据
if
(
props
.
isBatch
)
{
skuList
.
value
=
[
{
...
...
@@ -278,8 +283,11 @@ watch(
}
]
}
// 判断代理对象是否为空
if
(
JSON
.
stringify
(
propertyList
)
===
'[]'
)
return
if
(
JSON
.
stringify
(
propertyList
)
===
'[]'
)
{
return
}
// 重置表头
tableHeaders
.
value
=
[]
// 生成表头
...
...
@@ -287,10 +295,16 @@ watch(
// name加属性项index区分属性值
tableHeaders
.
value
.
push
({
prop
:
`name
${
index
}
`
,
label
:
item
.
name
})
})
// 如果回显的 sku 属性和添加的属性一致则不处理
if
(
validateData
(
propertyList
))
return
if
(
validateData
(
propertyList
))
{
return
}
// 添加新属性没有属性值也不做处理
if
(
propertyList
.
some
((
item
)
=>
item
.
values
.
length
===
0
))
return
if
(
propertyList
.
some
((
item
)
=>
item
.
values
.
length
===
0
))
{
return
}
// 生成 table 数据,即 sku 列表
generateTableData
(
propertyList
)
},
{
...
...
@@ -298,6 +312,6 @@ watch(
immediate
:
true
}
)
// 暴露出生成 sku 方法
给添加属性成功时调用 fix: 为了在只有一个属性下 spu 回显 skus 属性和和商品属性个数一致的情况下 添加属性值时添加 sku
// 暴露出生成 sku 方法
,给添加属性成功时调用
defineExpose
({
generateTableData
})
</
script
>
src/views/mall/product/spu/index.vue
View file @
0fba3fd5
...
...
@@ -8,7 +8,7 @@
class=
"-mb-15px"
label-width=
"68px"
>
<!-- TODO @puhui999:
https://admin.java.crmeb.net/store/index,参考,使用分类 + 标题搜索 fix
-->
<!-- TODO @puhui999:
品牌应该是数据下拉哈
-->
<el-form-item
label=
"品牌名称"
prop=
"name"
>
<el-input
v-model=
"queryParams.name"
...
...
@@ -19,6 +19,7 @@
/>
</el-form-item>
<!-- TODO 分类只能选择二级分类目前还没做,还是先以联调通顺为主 -->
<!-- TODO puhui999:我们要不改成支持选择一级。如果选择一级,后端要递归查询下子分类,然后去 in? -->
<el-form-item
label=
"商品分类"
prop=
"categoryId"
>
<el-tree-select
v-model=
"queryParams.categoryId"
...
...
@@ -70,7 +71,12 @@
/>
</el-tabs>
<el-table
v-loading=
"loading"
:data=
"list"
>
<!-- TODO 折叠数据按需增加暂定以下三个 -->
<!-- TODO puhui:这几个属性哈,一行三个
商品分类:服装鞋包/箱包
商品市场价格:100.00
成本价:0.00
收藏:5
虚拟销量:999 -->
<el-table-column
type=
"expand"
width=
"30"
>
<template
#
default=
"
{ row }">
<el-form
class=
"demo-table-expand"
inline
label-position=
"left"
>
...
...
@@ -86,19 +92,13 @@
</el-form>
</
template
>
</el-table-column>
<!-- TODO puhui999: ID 编号的展示 fix-->
<el-table-column
key=
"id"
align=
"center"
label=
"商品编号"
prop=
"id"
/>
<el-table-column
label=
"商品图"
min-width=
"80"
>
<
template
#
default=
"{ row }"
>
<el-image
:src=
"row.picUrl"
style=
"width: 30px; height: 30px"
@
click=
"imagePreview(row.picUrl)"
/>
<el-image
:src=
"row.picUrl"
@
click=
"imagePreview(row.picUrl)"
class=
"w-30px h-30px"
/>
</
template
>
</el-table-column>
<el-table-column
:show-overflow-tooltip=
"true"
label=
"商品名称"
min-width=
"300"
prop=
"name"
/>
<!-- TODO 价格 / 100.0 -->
<el-table-column
align=
"center"
label=
"商品售价"
min-width=
"90"
prop=
"price"
>
<
template
#
default=
"{ row }"
>
{{
formatToFraction
(
row
.
price
)
}}
...
...
@@ -116,7 +116,6 @@
/>
<el-table-column
align=
"center"
label=
"状态"
min-width=
"80"
>
<
template
#
default=
"{ row }"
>
<!-- fix: 修复打开回收站时商品误触改变商品状态的事件,因为el-switch只用0和1没有-1所以当商品状态为-1时状态使用el-tag显示 -->
<template
v-if=
"row.status >= 0"
>
<el-switch
v-model=
"row.status"
...
...
@@ -238,7 +237,6 @@ const tabsData = ref([
/** 获得每个 Tab 的数量 */
const
getTabsCount
=
async
()
=>
{
// TODO @puhui999:这里是不是可以不要 try catch 哈 fix
const
res
=
await
ProductSpuApi
.
getTabsCount
()
for
(
let
objName
in
res
)
{
tabsData
.
value
[
Number
(
objName
)].
count
=
res
[
objName
]
...
...
@@ -251,7 +249,6 @@ const queryParams = ref({
})
// 查询参数
const
queryFormRef
=
ref
()
// 搜索的表单Ref
// TODO @puhui999:可以改成 handleTabClick:更准确一点;fix
const
handleTabClick
=
(
tab
:
TabsPaneContext
)
=>
{
queryParams
.
value
.
tabType
=
tab
.
paneName
getList
()
...
...
@@ -269,7 +266,6 @@ const getList = async () => {
}
}
// TODO @puhui999:是不是 changeStatus 和 addToTrash 调用一个统一的方法,去更新状态。这样逻辑会更干净一些。fix
/**
* 更改 SPU 状态
*
...
...
@@ -277,7 +273,6 @@ const getList = async () => {
* @param status 更改前的值
*/
const
changeStatus
=
async
(
row
,
status
?:
number
)
=>
{
// fix: 将加入回收站功能合并到changeStatus并优化
const
deepCopyValue
=
cloneDeep
(
unref
(
row
))
if
(
typeof
status
!==
'undefined'
)
deepCopyValue
.
status
=
status
try
{
...
...
@@ -293,7 +288,6 @@ const changeStatus = async (row, status?: number) => {
text
=
`加入
${
ProductSpuStatusEnum
.
RECYCLE
.
name
}
`
break
}
// fix: 修复恢复到仓库的信息提示
await
message
.
confirm
(
deepCopyValue
.
status
===
-
1
?
`确认要将[
${
row
.
name
}
]
${
text
}
吗?`
...
...
@@ -331,12 +325,8 @@ const handleDelete = async (id: number) => {
}
catch
{}
}
/**
* 商品图预览
* @param imgUrl
*/
/** 商品图预览 */
const
imagePreview
=
(
imgUrl
:
string
)
=>
{
// fix: 改用https://kailong110120130.gitee.io/vue-element-plus-admin-doc/components/image-viewer.html 预览图片
createImageViewer
({
urlList
:
[
imgUrl
]
})
...
...
@@ -367,19 +357,22 @@ const openForm = (id?: number) => {
// 新增
push
(
'/product/productSpuAdd'
)
}
/**
* 查看商品详情
*/
const
openDetail
=
()
=>
{
message
.
alert
(
'查看详情未完善!!!'
)
}
// 监听路由变化更新列表 TODO @puhui999:这个是必须加的么?fix: 因为编辑表单是以路由的方式打开,保存表单后列表不会刷新
// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。
watch
(
()
=>
currentRoute
.
value
,
()
=>
{
getList
()
}
)
const
categoryList
=
ref
()
// 分类树
/** 初始化 **/
onMounted
(
async
()
=>
{
...
...
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