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
2c76d3ae
authored
Jan 11, 2024
by
YunaiV
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
📖
MALL:商品编辑 => 将 detail 还是使用 form 复用,减少维护成本
parent
c0905fdc
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
36 additions
and
234 deletions
+36
-234
src/api/mall/product/spu.ts
+2
-2
src/views/mall/product/spu/form/BasicInfoForm.vue
+28
-67
src/views/mall/product/spu/form/DeliveryForm.vue
+2
-16
src/views/mall/product/spu/form/DescriptionForm.vue
+1
-17
src/views/mall/product/spu/form/OtherSettingsForm.vue
+1
-8
src/views/mall/product/spu/form/SkuForm.vue
+1
-50
src/views/mall/product/spu/form/index.vue
+1
-2
src/views/mall/product/spu/form/spu.data.ts
+0
-72
No files found.
src/api/mall/product/spu.ts
View file @
2c76d3ae
...
...
@@ -33,14 +33,14 @@ export interface GiveCouponTemplate {
export
interface
Spu
{
id
?:
number
name
?:
string
// 商品名称
categoryId
?:
number
|
undefined
// 商品分类
categoryId
?:
number
// 商品分类
keyword
?:
string
// 关键字
unit
?:
number
|
undefined
// 单位
picUrl
?:
string
// 商品封面图
sliderPicUrls
?:
string
[]
// 商品轮播图
introduction
?:
string
// 商品简介
deliveryTemplateId
?:
number
|
undefined
// 运费模版
brandId
?:
number
|
undefined
// 商品品牌编号
brandId
?:
number
// 商品品牌编号
specType
?:
boolean
// 商品规格
subCommissionType
?:
boolean
// 分销类型
skus
?:
Sku
[]
// sku数组
...
...
src/views/mall/product/spu/form/BasicInfoForm.vue
View file @
2c76d3ae
<
template
>
<!-- 情况一:添加/修改 -->
<el-form
v-if=
"!isDetail"
ref=
"productSpuBasicInfoRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
>
<!-- TODO 芋艿:宽度!! -->
<el-form
ref=
"formRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
:disabled=
"isDetail"
>
<el-form-item
label=
"商品名称"
prop=
"name"
>
<el-input
v-model=
"formData.name"
...
...
@@ -17,6 +9,7 @@
maxlength="64"
:show-word-limit="true"
:clearable="true"
class="w-80!"
/>
</el-form-item>
<el-form-item
label=
"商品分类"
prop=
"categoryId"
>
...
...
@@ -24,21 +17,35 @@
v-model=
"formData.categoryId"
:options=
"categoryList"
:props=
"defaultProps"
class=
"w-
1/1
"
class=
"w-
80
"
clearable
placeholder=
"请选择商品分类"
filterable
/>
</el-form-item>
<el-form-item
label=
"商品品牌"
prop=
"brandId"
>
<el-select
v-model=
"formData.brandId"
placeholder=
"请选择商品品牌"
class=
"w-80"
>
<el-option
v-for=
"item in brandList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id as number"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"商品关键字"
prop=
"keyword"
>
<el-input
v-model=
"formData.keyword"
placeholder=
"请输入商品关键字"
/>
<el-input
v-model=
"formData.keyword"
placeholder=
"请输入商品关键字"
class=
"w-80!"
/>
</el-form-item>
<el-form-item
label=
"商品简介"
prop=
"introduction"
>
<el-input
v-model=
"formData.introduction"
:rows=
"3"
placeholder=
"请输入商品简介"
placeholder=
"请输入商品名称"
type=
"textarea"
:autosize=
"
{ minRows: 2, maxRows: 2 }"
maxlength="128"
:show-word-limit="true"
:clearable="true"
class="w-80!"
/>
</el-form-item>
<el-form-item
label=
"商品封面图"
prop=
"picUrl"
>
...
...
@@ -47,67 +54,20 @@
<el-form-item
label=
"商品轮播图"
prop=
"sliderPicUrls"
>
<UploadImgs
v-model:modelValue=
"formData.sliderPicUrls"
/>
</el-form-item>
<el-form-item
label=
"品牌"
prop=
"brandId"
>
<el-select
v-model=
"formData.brandId"
placeholder=
"请选择"
>
<el-option
v-for=
"item in brandList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</el-form-item>
</el-form>
<!-- 情况二:详情 -->
<Descriptions
v-if=
"isDetail"
:data=
"formData"
:schema=
"allSchemas.detailSchema"
>
<template
#
categoryId=
"
{ row }">
{{
formatCategoryName
(
row
.
categoryId
)
}}
</
template
>
<
template
#
brandId=
"{ row }"
>
{{
brandList
.
find
((
item
)
=>
item
.
id
===
row
.
brandId
)?.
name
}}
</
template
>
<
template
#
picUrl=
"{ row }"
>
<el-image
:src=
"row.picUrl"
class=
"h-60px w-60px"
@
click=
"imagePreview(row.picUrl)"
/>
</
template
>
<
template
#
sliderPicUrls=
"{ row }"
>
<el-image
v-for=
"(item, index) in row.sliderPicUrls"
:key=
"index"
:src=
"item.url"
class=
"mr-10px h-60px w-60px"
@
click=
"imagePreview(row.sliderPicUrls)"
/>
</
template
>
</Descriptions>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
import
{
isArray
}
from
'@/utils/is'
import
{
copyValueToTarget
}
from
'@/utils'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
defaultProps
,
handleTree
,
treeToString
}
from
'@/utils/tree'
import
{
createImageViewer
}
from
'@/components/ImageViewer'
import
{
basicInfoSchema
}
from
'./spu.data'
import
type
{
Spu
}
from
'@/api/mall/product/spu'
import
*
as
ProductCategoryApi
from
'@/api/mall/product/category'
import
*
as
ProductBrandApi
from
'@/api/mall/product/brand'
import
*
as
ExpressTemplateApi
from
'@/api/mall/trade/delivery/expressTemplate
'
import
{
BrandVO
}
from
'@/api/mall/product/brand
'
defineOptions
({
name
:
'ProductSpuBasicInfoForm'
})
// ====== 商品详情相关操作 ======
const
{
allSchemas
}
=
useCrudSchemas
(
basicInfoSchema
)
/** 商品图预览 */
const
imagePreview
=
(
args
)
=>
{
const
urlList
=
[]
if
(
isArray
(
args
))
{
args
.
forEach
((
item
)
=>
{
urlList
.
push
(
item
.
url
)
})
}
else
{
urlList
.
push
(
args
)
}
createImageViewer
({
urlList
})
}
// ====== end ======
const
message
=
useMessage
()
// 消息弹窗
const
props
=
defineProps
({
...
...
@@ -118,15 +78,16 @@ const props = defineProps({
activeName
:
propTypes
.
string
.
def
(
''
),
isDetail
:
propTypes
.
bool
.
def
(
false
)
// 是否作为详情组件
})
const
productSpuBasicInfoRef
=
ref
()
// 表单 Ref
const
formRef
=
ref
()
// 表单 Ref
const
formData
=
reactive
<
Spu
>
({
name
:
''
,
// 商品名称
categoryId
:
null
,
// 商品分类
categoryId
:
undefined
,
// 商品分类
keyword
:
''
,
// 关键字
picUrl
:
''
,
// 商品封面图
sliderPicUrls
:
[],
// 商品轮播图
introduction
:
''
,
// 商品简介
brandId
:
null
// 商品品牌
brandId
:
undefined
// 商品品牌
})
const
rules
=
reactive
({
name
:
[
required
],
...
...
@@ -163,8 +124,8 @@ watch(
const
emit
=
defineEmits
([
'update:activeName'
])
const
validate
=
async
()
=>
{
// 校验表单
if
(
!
productSpuBasicInfo
Ref
)
return
return
await
unref
(
productSpuBasicInfo
Ref
).
validate
((
valid
)
=>
{
if
(
!
form
Ref
)
return
return
await
unref
(
form
Ref
).
validate
((
valid
)
=>
{
if
(
!
valid
)
{
message
.
warning
(
'商品信息未完善!!'
)
emit
(
'update:activeName'
,
'basicInfo'
)
...
...
@@ -184,7 +145,7 @@ const formatCategoryName = (categoryId: number) => {
return
treeToString
(
categoryList
.
value
,
categoryId
)
}
const
brandList
=
ref
([])
// 精简商品品牌列表
const
brandList
=
ref
<
BrandVO
[]
>
([])
// 精简商品品牌列表
onMounted
(
async
()
=>
{
// 获得分类树
const
data
=
await
ProductCategoryApi
.
getCategoryList
({})
...
...
src/views/mall/product/spu/form/DeliveryForm.vue
View file @
2c76d3ae
<
template
>
<!-- 情况一:添加/修改 -->
<el-form
v-if=
"!isDetail"
ref=
"productSpuBasicInfoRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
:disabled=
"isDetail"
>
<!-- TODO 芋艿:宽度!! -->
<!-- TODO 芋艿:这里要挪出去 -->
...
...
@@ -20,29 +19,16 @@
</el-select>
</el-form-item>
</el-form>
<!-- 情况二:详情 -->
<Descriptions
v-if=
"isDetail"
:data=
"formData"
:schema=
"allSchemas.detailSchema"
>
<template
#
deliveryTemplateId=
"
{ row }">
{{
deliveryTemplateList
.
find
((
item
)
=>
item
.
id
===
row
.
deliveryTemplateId
)?.
name
}}
</
template
>
</Descriptions>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
import
{
copyValueToTarget
}
from
'@/utils'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
basicInfoSchema
}
from
'./spu.data'
import
type
{
Spu
}
from
'@/api/mall/product/spu'
import
*
as
ExpressTemplateApi
from
'@/api/mall/trade/delivery/expressTemplate'
defineOptions
({
name
:
'ProductSpuBasicInfoForm'
})
// ====== 商品详情相关操作 ======
const
{
allSchemas
}
=
useCrudSchemas
(
basicInfoSchema
)
// ====== end ======
const
message
=
useMessage
()
// 消息弹窗
const
props
=
defineProps
({
...
...
@@ -55,7 +41,7 @@ const props = defineProps({
})
const
productSpuBasicInfoRef
=
ref
()
// 表单 Ref
const
formData
=
reactive
<
Spu
>
({
deliveryTemplateId
:
null
// 运费模版
deliveryTemplateId
:
undefined
// 运费模版
})
const
rules
=
reactive
({
deliveryTemplateId
:
[
required
]
...
...
src/views/mall/product/spu/form/DescriptionForm.vue
View file @
2c76d3ae
<
template
>
<!-- 情况一:添加/修改 -->
<el-form
v-if=
"!isDetail"
ref=
"descriptionFormRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
:disabled=
"isDetail"
>
<!--富文本编辑器组件-->
<el-form-item
label=
"商品详情"
prop=
"description"
>
<Editor
v-model:modelValue=
"formData.description"
/>
</el-form-item>
</el-form>
<!-- 情况二:详情 -->
<Descriptions
v-if=
"isDetail"
:data=
"formData"
:schema=
"allSchemas.detailSchema"
class=
"descriptionFormDescriptions"
>
<!-- 展示 HTML 内容 -->
<template
#
description=
"
{ row }">
<div
v-dompurify-html=
"row.description"
style=
"width: 600px"
></div>
</
template
>
</Descriptions>
</
template
>
<
script
lang=
"ts"
setup
>
import
type
{
Spu
}
from
'@/api/mall/product/spu'
...
...
@@ -32,13 +18,11 @@ import { Editor } from '@/components/Editor'
import
{
PropType
}
from
'vue'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
copyValueToTarget
}
from
'@/utils'
import
{
descriptionSchema
}
from
'./spu.data'
defineOptions
({
name
:
'DescriptionForm'
})
const
message
=
useMessage
()
// 消息弹窗
const
{
allSchemas
}
=
useCrudSchemas
(
descriptionSchema
)
const
props
=
defineProps
({
propFormData
:
{
type
:
Object
as
PropType
<
Spu
>
,
...
...
src/views/mall/product/spu/form/OtherSettingsForm.vue
View file @
2c76d3ae
<
template
>
<!-- 情况一:添加/修改 -->
<el-form
v-if=
"!isDetail"
ref=
"otherSettingsFormRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
:disabled=
"isDetail"
>
<el-form-item
label=
"商品排序"
prop=
"sort"
>
<el-input-number
v-model=
"formData.sort"
:min=
"0"
/>
...
...
@@ -17,23 +16,17 @@
<el-input-number
v-model=
"formData.virtualSalesCount"
:min=
"0"
placeholder=
"请输入虚拟销量"
/>
</el-form-item>
</el-form>
<!-- 情况二:详情 -->
<Descriptions
v-if=
"isDetail"
:data=
"formData"
:schema=
"allSchemas.detailSchema"
/>
</
template
>
<
script
lang=
"ts"
setup
>
import
type
{
Spu
}
from
'@/api/mall/product/spu'
import
{
PropType
}
from
'vue'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
copyValueToTarget
}
from
'@/utils'
import
{
otherSettingsSchema
}
from
'./spu.data'
defineOptions
({
name
:
'OtherSettingsForm'
})
const
message
=
useMessage
()
// 消息弹窗
const
{
allSchemas
}
=
useCrudSchemas
(
otherSettingsSchema
)
const
props
=
defineProps
({
propFormData
:
{
type
:
Object
as
PropType
<
Spu
>
,
...
...
src/views/mall/product/spu/form/SkuForm.vue
View file @
2c76d3ae
<
template
>
<!-- 情况一:添加/修改 -->
<el-form
v-if=
"!isDetail"
ref=
"productSpuSkuRef"
:model=
"formData"
:rules=
"rules"
label-width=
"120px"
:disabled=
"isDetail"
>
<el-row>
<el-col
:span=
"12"
>
...
...
@@ -55,46 +55,16 @@
</el-row>
</el-form>
<!-- 情况二:详情 -->
<Descriptions
v-if=
"isDetail"
:data=
"formData"
:schema=
"allSchemas.detailSchema"
>
<
template
#
specType=
"{ row }"
>
{{
row
.
specType
?
'多规格'
:
'单规格'
}}
</
template
>
<
template
#
subCommissionType=
"{ row }"
>
{{
row
.
subCommissionType
?
'单独设置'
:
'默认设置'
}}
</
template
>
<
template
#
sliderPicUrls=
"{ row }"
>
<el-image
v-for=
"(item, index) in row.sliderPicUrls"
:key=
"index"
:src=
"item.url"
class=
"mr-10px h-60px w-60px"
@
click=
"imagePreview(row.sliderPicUrls)"
/>
</
template
>
<
template
#
skus
>
<SkuList
ref=
"skuDetailListRef"
:is-detail=
"isDetail"
:prop-form-data=
"formData"
:propertyList=
"propertyList"
/>
</
template
>
</Descriptions>
<!-- 商品属性添加 Form 表单 -->
<ProductPropertyAddForm
ref=
"attributesAddFormRef"
:propertyList=
"propertyList"
/>
</template>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
import
{
isArray
}
from
'@/utils/is'
import
{
copyValueToTarget
}
from
'@/utils'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
createImageViewer
}
from
'@/components/ImageViewer'
import
{
getPropertyList
,
RuleConfig
,
SkuList
}
from
'@/views/mall/product/spu/components/index.ts'
import
ProductAttributes
from
'./ProductAttributes.vue'
import
ProductPropertyAddForm
from
'./ProductPropertyAddForm.vue'
import
{
basicInfoSchema
}
from
'./spu.data'
import
type
{
Spu
}
from
'@/api/mall/product/spu'
defineOptions
({
name
:
'ProductSpuSkuForm'
})
...
...
@@ -123,25 +93,6 @@ const ruleConfig: RuleConfig[] = [
}
]
// ====== 商品详情相关操作 ======
const
{
allSchemas
}
=
useCrudSchemas
(
basicInfoSchema
)
/** 商品图预览 */
const
imagePreview
=
(
args
)
=>
{
const
urlList
=
[]
if
(
isArray
(
args
))
{
args
.
forEach
((
item
)
=>
{
urlList
.
push
(
item
.
url
)
})
}
else
{
urlList
.
push
(
args
)
}
createImageViewer
({
urlList
})
}
// ====== end ======
const
message
=
useMessage
()
// 消息弹窗
const
props
=
defineProps
({
...
...
src/views/mall/product/spu/form/index.vue
View file @
2c76d3ae
...
...
@@ -33,8 +33,7 @@
:propFormData=
"formData"
/>
</el-tab-pane>
<!-- TODO 芋艿:物流设置 -->
<el-tab-pane
label=
"其他设置"
name=
"otherSettings"
>
<el-tab-pane
label=
"其它设置"
name=
"otherSettings"
>
<OtherSettingsForm
ref=
"otherSettingsRef"
v-model:activeName=
"activeName"
...
...
src/views/mall/product/spu/form/spu.data.ts
deleted
100644 → 0
View file @
c0905fdc
import
{
CrudSchema
}
from
'@/hooks/web/useCrudSchemas'
export
const
basicInfoSchema
=
reactive
<
CrudSchema
[]
>
([
{
label
:
'商品名称'
,
field
:
'name'
},
{
label
:
'关键字'
,
field
:
'keyword'
},
{
label
:
'商品简介'
,
field
:
'introduction'
},
{
label
:
'商品分类'
,
field
:
'categoryId'
},
{
label
:
'商品品牌'
,
field
:
'brandId'
},
{
label
:
'商品封面图'
,
field
:
'picUrl'
},
{
label
:
'商品轮播图'
,
field
:
'sliderPicUrls'
},
{
label
:
'商品视频'
,
field
:
'videoUrl'
},
{
label
:
'规格类型'
,
field
:
'specType'
},
{
label
:
'分销类型'
,
field
:
'subCommissionType'
},
{
label
:
'物流模版'
,
field
:
'deliveryTemplateId'
},
{
label
:
'商品属性列表'
,
field
:
'skus'
}
])
export
const
descriptionSchema
=
reactive
<
CrudSchema
[]
>
([
{
label
:
'商品详情'
,
field
:
'description'
}
])
export
const
otherSettingsSchema
=
reactive
<
CrudSchema
[]
>
([
{
label
:
'商品排序'
,
field
:
'sort'
},
{
label
:
'赠送积分'
,
field
:
'giveIntegral'
},
{
label
:
'虚拟销量'
,
field
:
'virtualSalesCount'
}
])
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