Spu模块
SPU(Standard Product Unit):标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
7.1 Spu模块的静态页面
<template>
<div>
<!-- 三级分类 -->
<Category :scene="scene"></Category>
<el-card style="margin: 10px 10px">
<el-button type="primary" size="default" icon="Plus">添加SPU</el-button>
<el-table border style="margin: 10px 10px">
<el-table-column
label="序号"
type="index"
align="center"
width="80px"
></el-table-column>
<el-table-column label="SPU名称"></el-table-column>
<el-table-column label="SPU描述"></el-table-column>
<el-table-column label="SPU操作"></el-table-column>
</el-table>
</el-card>
<!-- 分页器 -->
<el-pagination
v-model:current-page="pageNo"
v-model:page-size="pageSize"
:page-sizes="[3, 5, 7, 9]"
:background="true"
layout=" prev, pager, next, jumper,->, sizes,total"
:total="400"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onBeforeUnmount } from 'vue'
//场景的数据
let scene = ref<number>(0)
//分页器默认页码
let pageNo = ref<number>(1)
//每一页展示几条数据
let pageSize = ref<number>(3)
</script>
<style lang="scss" scoped></style>
7.2 Spu模块展示已有数据
7.2.1 API
//SPU管理模块的接口
import request from '@/utils/request'
import type { HasSpuResponseData } from './type'
enum API {
//获取已有的SPU的数据
HASSPU_URL = '/admin/product/',
}
//获取某一个三级分类下已有的SPU数据
export const reqHasSpu = (
page: number,
limit: number,
category3Id: string | number,
) => {
return request.get<any, HasSpuResponseData>(
API.HASSPU_URL + `${page}/${limit}?category3Id=${category3Id}`,
)
}
7.2.2 type
src\api\product\spu\index.ts
//服务器全部接口返回的数据类型
export interface ResponseData {
code: number
message: string
ok: boolean
}
//SPU数据的ts类型:需要修改
export interface SpuData {
category3Id: string | number
id?: number
spuName: string
tmId: number | string
description: string
spuImageList: null
spuSaleAttrList: null
}
//数组:元素都是已有SPU数据类型
export type Records = SpuData[]
//定义获取已有的SPU接口返回的数据ts类型
export interface HasSpuResponseData extends ResponseData {
data: {
records: Records
total: number
size: number
current: number
searchCount: boolean
pages: number
}
}
7.2.3 添加SPU按钮
7.2.4 表单数据
<el-table border style="margin: 10px 10px" :data="records">
<el-table-column
label="序号"
type="index"
align="center"
width="80px"
></el-table-column>
<el-table-column label="SPU名称" prop="spuName"></el-table-column>
<el-table-column
label="SPU描述"
prop="description"
show-overflow-tooltip
></el-table-column>
<el-table-column label="SPU操作">
<!-- row:即为已有的SPU对象 -->
<template #="{ row, $index }">
<el-button
type="primary"
size="small"
icon="Plus"
title="添加SKU"
></el-button>
<el-button
type="primary"
size="small"
icon="Edit"
title="修改SPU"
></el-button>
<el-button
type="primary"
size="small"
icon="View"
title="查看SKU列表"
></el-button>
<el-popconfirm :title="`你确定删除${row.spuName}?`" width="200px">
<template #reference>
<el-button
type="primary"
size="small"
icon="Delete"
title="删除SPU"
></el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
7.2.5 分页器
注意getHasSpu函数携带的参数。默认为1
<!-- 分页器 -->
<el-pagination
v-model:current-page="pageNo"
v-model:page-size="pageSize"
:page-sizes="[3, 5, 7, 9]"
:background="true"
layout=" prev, pager, next, jumper,->, sizes,total"
:total="total"
@current-change="getHasSpu"
@size-change="changeSize"
/>
//此方法执行:可以获取某一个三级分类下全部的已有的SPU
const getHasSpu = async (pager = 1) => {
//修改当前页码
pageNo.value = pager
let result: HasSpuResponseData = await reqHasSpu(
pageNo.value,
pageSize.value,
categoryStore.c3Id,
)
if (result.code == 200) {
records.value = result.data.records
total.value = result.data.total
}
}
//分页器下拉菜单发生变化的时候触发
const changeSize = () => {
getHasSpu()
}
7.2.6 watch监听
//监听三级分类ID变化
watch(
() => categoryStore.c3Id,
() => {
//当三级分类发生变化的时候清空对应的数据
records.value = []
//务必保证有三级分类ID
if (!categoryStore.c3Id) return
getHasSpu()
},
)
7.3 SPU场景一的静态&&场景切换
7.3.1 子组件搭建
由于SPU模块需要在三个场景进行切换,全都放在一个组件里面的话会显得很臃肿。因此我们将它放到三个组件当中。
使用v-show来展示页面:v-if是销毁组件,v-show是隐藏组件。在初加载的时候v-if比较快,但是在频繁切换的时候v-if任务重。
7.3.2 SPU场景一子组件静态src\views\product\spu\spuForm.vue
<template>
<el-form label-width="100px">
<el-form-item label="SPU名称">
<el-input placeholder="请你输入SPU名称"></el-input>
</el-form-item>
<el-form-item label="SPU品牌">
<el-select>
<el-option label="华为"></el-option>
<el-option label="oppo"></el-option>
<el-option label="vivo"></el-option>
</el-select>
</el-form-item>
<el-form-item label="SPU描述">
<el-input type="textarea" placeholder="请你输入SPU描述"></el-input>
</el-form-item>
<el-form-item label="SPU图片">
<el-upload
v-model:file-list="fileList"
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
>
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</el-form-item>
<el-form-item label="SPU销售属性" size="normal">
<!-- 展示销售属性的下拉菜单 -->
<el-select>
<el-option label="华为"></el-option>
<el-option label="oppo"></el-option>
<el-option label="vivo"></el-option>
</el-select>
<el-button
style="margin-left: 10px"
type="primary"
size="default"
icon="Plus"
>
添加属性
</el-button>
<!-- table展示销售属性与属性值的地方 -->
<el-table border style="margin: 10px 0px">
<el-table-column
label="序号"
type="index"
align="center"
width="80px"
></el-table-column>
<el-table-column
label="销售属性名字"
width="120px"
prop="saleAttrName"
></el-table-column>
<el-table-column label="销售属性值">
<!-- row:即为当前SPU已有的销售属性对象 -->
</el-table-column>
<el-table-column label="操作" width="120px"></el-table-column>
</el-table>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default">保存</el-button>
<el-button type="primary" size="default" @click="cancel">取消</el-button>
</el-form-item>
</el-form>
</template>
7.3.3 父组件中添加SPU按钮&&修改按钮
这两个按钮都是跳转到场景一.下面是对应的回调
//添加新的SPU按钮的回调
const addSpu = () => {
//切换为场景1:添加与修改已有SPU结构->SpuForm
scene.value = 1
}
//修改已有的SPU的按钮的回调
const updateSpu = () => {
//切换为场景1:添加与修改已有SPU结构->SpuForm
scene.value = 1
}
7.3.4 子组件中取消按钮的回调
需要改变的是父组件中的scene,因此涉及到父子组件通信。这里使用自定义事件。
父组件:
子组件:
//取消按钮的回调
const cancel = () => {
$emit('changeScene', 0)
}
7.4 SPU模块API&&TS类型(修改&&添加)
修改和添加的页面是差不多的。页面1的四个地方都需要发请求拿数据,我们在这一部分分别编写4个部分的API以及ts类型
7.4.1 SPU品牌
- API:src\api\product\spu\index.ts
//获取全部品牌的数据
ALLTRADEMARK_URL = '/admin/product/baseTrademark/getTrademarkList',
//获取全部的SPU的品牌的数据
export const reqAllTradeMark = () => {
return request.get<any, AllTradeMark>(API.ALLTRADEMARK_URL)
}
- ts
- src\api\product\spu\type.ts
//品牌数据的TS类型
export interface Trademark {
id: number
tmName: string
logoUrl: string
}
//品牌接口返回的数据ts类型
export interface AllTradeMark extends ResponseData {
data: Trademark[]
}
7.4.2 SPU图片
- API
//获取某个SPU下的全部的售卖商品的图片数据
IMAGE_URL = '/admin/product/spuImageList/',
//获取某一个已有的SPU下全部商品的图片地址
export const reqSpuImageList = (spuId: number) => {
return request.get<any, SpuHasImg>(API.IMAGE_URL + spuId)
}
- ts
//商品图片的ts类型
export interface SpuImg {
id?: number
imgName?: string
imgUrl?: string
createTime?: string
updateTime?: string
spuId?: number
name?: string
url?: string
}
//已有的SPU的照片墙数据的类型
export interface SpuHasImg extends ResponseData {
data: SpuImg[]
}
7.4.3 全部销售属性
- API
//获取整个项目全部的销售属性[颜色、版本、尺码]
ALLSALEATTR_URL = '/admin/product/baseSaleAttrList',
//获取全部的销售属性
export const reqAllSaleAttr = () => {
return request.get<any, HasSaleAttrResponseData>(API.ALLSALEATTR_URL)
}
- ts
//已有的全部SPU的返回数据ts类型
export interface HasSaleAttr {
id: number
name: string
}
export interface HasSaleAttrResponseData extends ResponseData {
data: HasSaleAttr[]
}
7.4.4 已有的销售属性
- API
//获取某一个SPU下全部的已有的销售属性接口地址
SPUHASSALEATTR_URL = '/admin/product/spuSaleAttrList/',
//获取某一个已有的SPU拥有多少个销售属性
export const reqSpuHasSaleAttr = (spuId: number) => {
return request.get<any, SaleAttrResponseData>(API.SPUHASSALEATTR_URL + spuId)
}
- ts
//销售属性对象ts类型
export interface SaleAttr {
id?: number
createTime?: null
updateTime?: null
spuId?: number
baseSaleAttrId: number | string
saleAttrName: string
spuSaleAttrValueList: SpuSaleAttrValueList
flag?: boolean
saleAttrValue?: string
}
//SPU已有的销售属性接口返回数据ts类型
export interface SaleAttrResponseData extends ResponseData {
data: SaleAttr[]
}
7.5 获取SPU的数据
首先:SPU的数据应该分为5部分:第一部分:是父组件里的展示的数据,也是我们点击修改按钮时的那个数据。其余4个部分的数据需要我们发请求得到。
问题1:子组件需要用到父组件中的数据,应该怎么办?答:要传递的数据是指定的,也就是我们点击修改时的数据。通过ref的方式,拿到子组件时的实例,再调用子组件暴露的方法将数据做为参数传递过去。(有点类似于反向的自定义事件)
问题2:其余4个部分的数据什么时候获取。答:同样的在点击修改按钮时获取,问题一中通过调用子组件的函数传递数据,我们同时也在这个函数中发请求得到数据
7.5.1 第一部分数据的传递
- 父组件拿到子组件实例
- 子组件暴露对外函数
- 修改按钮点击函数中调用子组件函数,并传递第一部分数据
//修改已有的SPU的按钮的回调
const updateSpu = (row: SpuData) => {
//切换为场景1:添加与修改已有SPU结构->SpuForm
scene.value = 1
//调用子组件实例方法获取完整已有的SPU的数据
spu.value.initHasSpuData(row)
}
7.5.2 其余数据
子组件中直接发起请求,并且将服务器返回的四个数据存储,加上参数传递的第一部分数据,这样子组件拿到了全部的数据。
//子组件书写一个方法
const initHasSpuData = async (spu: SpuData) => {
//spu:即为父组件传递过来的已有的SPU对象[不完整]
//获取全部品牌的数据
let result: AllTradeMark = await reqAllTradeMark()
//获取某一个品牌旗下全部售卖商品的图片
let result1: SpuHasImg = await reqSpuImageList(spu.id as number)
//获取已有的SPU销售属性的数据
let result2: SaleAttrResponseData = await reqSpuHasSaleAttr(spu.id as number)
//获取整个项目全部SPU的销售属性
let result3: HasSaleAttrResponseData = await reqAllSaleAttr()
//存储全部品牌的数据
MYAllTradeMark.value = result.data
//SPU对应商品图片
imgList.value = result1.data.map((item) => {
return {
name: item.imgName,
url: item.imgUrl,
}
})
//存储已有的SPU的销售属性
saleAttr.value = result2.data
//存储全部的销售属性
allSaleAttr.value = result3.data
}
7.6 修改与添加的接口&&TS
7.6.1 接口(API)
/追加一个新的SPU
ADDSPU_URL = '/admin/product/saveSpuInfo',
//更新已有的SPU
UPDATESPU_URL = '/admin/product/updateSpuInfo',
//添加一个新的SPU的
//更新已有的SPU接口
//data:即为新增的SPU|或者已有的SPU对象
export const reqAddOrUpdateSpu = (data: any) => {
//如果SPU对象拥有ID,更新已有的SPU
if (data.id) {
return request.post<any, any>(API.UPDATESPU_URL, data)
} else {
return request.post<any, any>(API.ADDSPU_URL, data)
}
}
7.6.2 ts
//SPU数据的ts类型:需要修改
export interface SpuData {
category3Id: string | number
id?: number
spuName: string
tmId: number | string
description: string
spuImageList: null | SpuImg[]
spuSaleAttrList: null | SaleAttr[]
}
7.7 展示与收集已有的数据
7.7.1 存储父组件传递过来的数据
//存储已有的SPU对象
let SpuParams = ref<SpuData>({
category3Id: '', //收集三级分类的ID
spuName: '', //SPU的名字
description: '', //SPU的描述
tmId: '', //品牌的ID
spuImageList: [],
spuSaleAttrList: [],
})
//子组件书写一个方法
const initHasSpuData = async (spu: SpuData) => {
//存储已有的SPU对象,将来在模板中展示
SpuParams.value = spu
。。。。。。
}
7.7.2 展示SPU名称
7.7.3 展示SPU品牌
注意:下方的红框展示的是所有品牌,上方的绑定的是一个数字也就是下方的第几个
7.7.4 SPU描述
7.7.5 照片墙PART
照片墙部分我们使用了element-plus的el-upload组件。下面详细介绍组件的功能及作用
- 整体结构
上面el-upload是上传照片的照片墙,下面是查看照片的对话框
- v-model:file-list
//商品图片
let imgList = ref<SpuImg[]>([])
//子组件书写一个方法
const initHasSpuData = async (spu: SpuData) => {
。。。。。。
//获取某一个品牌旗下全部售卖商品的图片
let result1: SpuHasImg = await reqSpuImageList(spu.id as number)
......
//SPU对应商品图片
imgList.value = result1.data.map((item) => {
return {
name: item.imgName,
url: item.imgUrl,
}
})
......
}
这部分是一个双向绑定的数据,我们从服务器得到数据会展示到照片墙上。得到数据的过程我们使用了数组的map方法,这是因为组件对于数据的格式有要求。
- action
action是指图片上传的地址。组件还会将返回的数据放到对应的img的数据中
- list-type:照片墙的形式
- :on-preview
预览的钩子,预览照片时会触发。会注入对应图片的数据。
//控制对话框的显示与隐藏
let dialogVisible = ref<boolean>(false)
//存储预览图片地址
let dialogImageUrl = ref<string>('')
//照片墙点击预览按钮的时候触发的钩子
const handlePictureCardPreview = (file: any) => {
dialogImageUrl.value = file.url
//对话框弹出来
dialogVisible.value = true
}
- :on-remove
移除图片前的钩子
- :before-upload
上传前的钩子,我们用来对数据做预处理
//照片钱上传成功之前的钩子约束文件的大小与类型
const handlerUpload = (file: any) => {
if (
file.type == 'image/png' ||
file.type == 'image/jpeg' ||
file.type == 'image/gif'
) {
if (file.size / 1024 / 1024 < 3) {
return true
} else {
ElMessage({
type: 'error',
message: '上传文件务必小于3M',
})
return false
}
} else {
ElMessage({
type: 'error',
message: '上传文件务必PNG|JPG|GIF',
})
return false
}
}
7.8 展示已有的销售属性与属性值
数据结构如下:
7.8.1 展示销售属性与属性值
其实就是4列,对应好每一列以及对应的数据就好
<!-- table展示销售属性与属性值的地方 -->
<el-table border style="margin: 10px 0px" :data="saleAttr">
<el-table-column
label="序号"
type="index"
align="center"
width="80px"
></el-table-column>
<el-table-column
label="销售属性名字"
width="120px"
prop="saleAttrName"
></el-table-column>
<el-table-column label="销售属性值">
<!-- row:即为当前SPU已有的销售属性对象 -->
<template #="{ row, $index }">
<el-tag
class="mx-1"
closable
style="margin: 0px 5px"
@close="row.spuSaleAttrValueList.splice(index, 1)"
v-for="(item, index) in row.spuSaleAttrValueList"
:key="row.id"
>
{{ item.saleAttrValueName }}
</el-tag>
<el-button type="primary" size="small" icon="Plus"></el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="120px">
<template #="{ row, $index }">
<el-button
type="primary"
size="small"
icon="Delete"
@click="saleAttr.splice($index, 1)"
></el-button>
</template>
</el-table-column>
</el-table>
7.8.2 删除操作
<el-table-column label="操作" width="120px">
<template #="{ row, $index }">
<el-button
type="primary"
size="small"
icon="Delete"
@click="saleAttr.splice($index, 1)"
></el-button>
</template>
</el-table-column>
7.9 完成收集新增销售属性业务
7.9.1 计算出还未拥有的销售属性
//计算出当前SPU还未拥有的销售属性
let unSelectSaleAttr = computed(() => {
//全部销售属性:颜色、版本、尺码
//已有的销售属性:颜色、版本
let unSelectArr = allSaleAttr.value.filter((item) => {
return saleAttr.value.every((item1) => {
return item.name != item1.saleAttrName
})
})
return unSelectArr
})
7.9.2 收集你选择的属性的id以及name
7.9.3 添加属性按钮的回调
//添加销售属性的方法
const addSaleAttr = () => {
/*
"baseSaleAttrId": number,
"saleAttrName": string,
"spuSaleAttrValueList": SpuSaleAttrValueList
*/
const [baseSaleAttrId, saleAttrName] = saleAttrIdAndValueName.value.split(':')
//准备一个新的销售属性对象:将来带给服务器即可
let newSaleAttr: SaleAttr = {
baseSaleAttrId,
saleAttrName,
spuSaleAttrValueList: [],
}
//追加到数组当中
saleAttr.value.push(newSaleAttr)
//清空收集的数据
saleAttrIdAndValueName.value = ''
}
7.10 销售属性值的添加删除业务
其实销售属性值和之前的添加属性业务差不多。最重要的是熟悉数据的结构。步骤分为:组件收集数据->回调中将数据整理后push到对应的数组中。
7.10.1 添加按钮与input框的切换
通过flag属性。一上来是没有的,点击按钮添加。输入框输入完毕blur时再将flag变为false
//属性值按钮的点击事件
const toEdit = (row: SaleAttr) => {
//点击按钮的时候,input组件不就不出来->编辑模式
row.flag = true
row.saleAttrValue = ''
}
7.10.2 收集&&添加属性值
收集的数据有俩个
saleAttrValue:点击添加按钮时初始化为空,收集输入的信息
baseSaleAttrId:所在的数据的id。由row给出
其余做的事就是:非法数据的过滤
//表单元素失却焦点的事件回调
const toLook = (row: SaleAttr) => {
//整理收集的属性的ID与属性值的名字
const { baseSaleAttrId, saleAttrValue } = row
//整理成服务器需要的属性值形式
let newSaleAttrValue: SaleAttrValue = {
baseSaleAttrId,
saleAttrValueName: saleAttrValue as string,
}
//非法情况判断
if ((saleAttrValue as string).trim() == '') {
ElMessage({
type: 'error',
message: '属性值不能为空的',
})
return
}
//判断属性值是否在数组当中存在
let repeat = row.spuSaleAttrValueList.find((item) => {
return item.saleAttrValueName == saleAttrValue
})
if (repeat) {
ElMessage({
type: 'error',
message: '属性值重复',
})
return
}
//追加新的属性值对象
row.spuSaleAttrValueList.push(newSaleAttrValue)
//切换为查看模式
row.flag = false
}
7.10.3 删除属性值
7.12 保存
整理数据+发送请求+通知父组件更新页面
//保存按钮的回调
const save = async () => {
//整理参数
//发请求:添加SPU|更新已有的SPU
//成功
//失败
//1:照片墙的数据
SpuParams.value.spuImageList = imgList.value.map((item: any) => {
return {
imgName: item.name, //图片的名字
imgUrl: (item.response && item.response.data) || item.url,
}
})
//2:整理销售属性的数据
SpuParams.value.spuSaleAttrList = saleAttr.value
let result = await reqAddOrUpdateSpu(SpuParams.value)
if (result.code == 200) {
ElMessage({
type: 'success',
message: SpuParams.value.id ? '更新成功' : '添加成功',
})
//通知父组件切换场景为0
$emit('changeScene', {
flag: 0,
params: SpuParams.value.id ? 'update' : 'add',
})
} else {
ElMessage({
type: 'success',
message: SpuParams.value.id ? '更新成功' : '添加成功',
})
}
}
7.13 添加spu业务&&收尾工作
7.13.1 添加spu业务
添加spu业务我们要做什么?收集数据(发请求得到的、自己添加的)放到对应的数据(存储数据用的容器)中,发起请求(保存按钮已经做完了),更新页面
- 父组件添加按钮回调
添加和修改按钮不同的地方在于对于数据的来源不同,修改按钮是一部分(spuParams)来源于父组件传递的数据,将他们与组件绑定,在数据上展示。添加按钮父组件只需要传递category3Id就行,其他的自己收集。
//添加新的SPU按钮的回调
const addSpu = () => {
//切换为场景1:添加与修改已有SPU结构->SpuForm
scene.value = 1
//点击添加SPU按钮,调用子组件的方法初始化数据
spu.value.initAddSpu(categoryStore.c3Id)
}
- 子组件收集数据
注意要对外暴露,让父组件可以使用
//添加一个新的SPU初始化请求方法
const initAddSpu = async (c3Id: number | string) => {
//存储三级分类的ID
SpuParams.value.category3Id = c3Id
//获取全部品牌的数据
let result: AllTradeMark = await reqAllTradeMark()
let result1: HasSaleAttrResponseData = await reqAllSaleAttr()
//存储数据
MYAllTradeMark.value = result.data
allSaleAttr.value = result1.data
}
//对外暴露
defineExpose({ initHasSpuData, initAddSpu })
- 整理数据与发送请求
这部分通过保存按钮的回调已经做完了。
7.13.2 清空数据
我们应该在每次添加spu前清空上次的数据。
//添加一个新的SPU初始化请求方法
const initAddSpu = async (c3Id: number | string) => {
//清空数据
Object.assign(SpuParams.value, {
category3Id: '', //收集三级分类的ID
spuName: '', //SPU的名字
description: '', //SPU的描述
tmId: '', //品牌的ID
spuImageList: [],
spuSaleAttrList: [],
})
//清空照片
imgList.value = []
//清空销售属性
saleAttr.value = []
saleAttrIdAndValueName.value = ''
、、、、、、
}
7.13.3 跳转页面
在添加和修改spu属性后,跳转的页面不一样。修改应该跳转到当前页面,添加应该跳转到第一页。如何区分?SpuParams.value.id属性修改按钮的SpuParams是自带这个属性的,而添加按钮没有这个属性。因此在保存的时候通过这个属性告知父组件。
子组件:
//保存按钮的回调
const save = async () => {
。。。。。。。
//通知父组件切换场景为0
$emit('changeScene', {
flag: 0,
params: SpuParams.value.id ? 'update' : 'add',
})
。。。。。。
}
父组件:
//子组件SpuForm绑定自定义事件:目前是让子组件通知父组件切换场景为0
const changeScene = (obj: any) => {
//子组件Spuform点击取消变为场景0:展示已有的SPU
scene.value = obj.flag
if (obj.params == 'update') {
//更新留在当前页
getHasSpu(pageNo.value)
} else {
//添加留在第一页
getHasSpu()
}
}
7.14添加SKU的静态
7.14.1 绑定回调
//添加SKU按钮的回调
const addSku = (row: SpuData) => {
//点击添加SKU按钮切换场景为2
scene.value = 2
}
7.14.2 静态页面
<template>
<el-form label-width="100px">
<el-form-item label="SKU名称">
<el-input placeholder="SKU名称"></el-input>
</el-form-item>
<el-form-item label="价格(元)">
<el-input placeholder="价格(元)" type="number"></el-input>
</el-form-item>
<el-form-item label="重量(g)">
<el-input placeholder="重量(g)" type="number"></el-input>
</el-form-item>
<el-form-item label="SKU描述">
<el-input placeholder="SKU描述" type="textarea"></el-input>
</el-form-item>
<el-form-item label="平台属性">
<el-form :inline="true">
<el-form-item label="内存" size="normal">
<el-select>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
</el-select>
</el-form-item>
<el-form-item label="内存" size="normal">
<el-select>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
</el-select>
</el-form-item>
<el-form-item label="内存" size="normal">
<el-select>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
</el-select>
</el-form-item>
<el-form-item label="内存" size="normal">
<el-select>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
</el-select>
</el-form-item>
</el-form>
</el-form-item>
<el-form-item label="销售属性">
<el-form :inline="true">
<el-form-item label="颜色" size="normal">
<el-select>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
<el-option label="213"></el-option>
</el-select>
</el-form-item>
</el-form>
</el-form-item>
<el-form-item label="图片名称" size="normal">
<el-table border>
<el-table-column
type="selection"
width="80px"
align="center"
></el-table-column>
<el-table-column label="图片"></el-table-column>
<el-table-column label="名称"></el-table-column>
<el-table-column label="操作"></el-table-column>
</el-table>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default">保存</el-button>
<el-button type="primary" size="default" @click="cancel">取消</el-button>
</el-form-item>
</el-form>
</template>
7.14.3 取消按钮
//自定义事件的方法
let $emit = defineEmits(['changeScene'])
//取消按钮的回调
const cancel = () => {
$emit('changeScene', { flag: 0, params: '' })
}
7.15 获取添加SKU数据并展示
7.15.2 父组件添加按钮回调->调用子组件函数收集数据
父组件
//添加SKU按钮的回调
const addSku = (row: SpuData) => {
//点击添加SKU按钮切换场景为2
scene.value = 2
//调用子组件的方法初始化添加SKU的数据
sku.value.initSkuData(categoryStore.c1Id, categoryStore.c2Id,row)
}
子组件暴露:
//对外暴露方法
defineExpose({ initSkuData })
7.15.2 子组件函数收集数据(平台属性、销售属性、图片名称)
//当前子组件的方法对外暴露
const initSkuData = async (
c1Id: number | string,
c2Id: number | string,
spu: any,
) => {
//获取平台属性
let result: any = await reqAttr(c1Id, c2Id, spu.category3Id)
//获取对应的销售属性
let result1: any = await reqSpuHasSaleAttr(spu.id)
//获取照片墙的数据
let result2: any = await reqSpuImageList(spu.id)
//平台属性
attrArr.value = result.data
//销售属性
saleArr.value = result1.data
//图片
imgArr.value = result2.data
}
7.15.3 模板展示(以图片为例)
<el-form-item label="图片名称" size="normal">
<el-table border :data="imgArr" ref="table">
<el-table-column
type="selection"
width="80px"
align="center"
></el-table-column>
<el-table-column label="图片">
<template #="{ row, $index }">
<img :src="row.imgUrl" alt="" style="width: 100px; height: 100px" />
</template>
</el-table-column>
<el-table-column label="名称" prop="imgName"></el-table-column>
<el-table-column label="操作">
<template #="{ row, $index }">
<el-button type="primary" size="small">设置默认</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
7.16 sku收集总数据
使用skuParams将sku模块的所有数据全都存储下来
7.16.1 API&&Ts
API:
//追加一个新增的SKU地址
ADDSKU_URL = '/admin/product/saveSkuInfo',
}
//添加SKU的请求方法
export const reqAddSku = (data: SkuData) => {
request.post<any, any>(API.ADDSKU_URL, data)
}
ts:
export interface Attr {
attrId: number | string //平台属性的ID
valueId: number | string //属性值的ID
}
export interface saleArr {
saleAttrId: number | string //属性ID
saleAttrValueId: number | string //属性值的ID
}
export interface SkuData {
category3Id: string | number //三级分类的ID
spuId: string | number //已有的SPU的ID
tmId: string | number //SPU品牌的ID
skuName: string //sku名字
price: string | number //sku价格
weight: string | number //sku重量
skuDesc: string //sku的描述
skuAttrValueList?: Attr[]
skuSaleAttrValueList?: saleArr[]
skuDefaultImg: string //sku图片地址
}
7.16.2 收集父组件传递过来的数据
这部分数据包括三级id,spuid还有品牌id。由于是父组件传递过来的,我们可以直接在添加按钮调用的那个函数中收集
//当前子组件的方法对外暴露
const initSkuData = async (
c1Id: number | string,
c2Id: number | string,
spu: any,
) => {
//收集数据
skuParams.category3Id = spu.category3Id
skuParams.spuId = spu.id
skuParams.tmId = spu.tmId
。。。。。。
}
7.16.3 input框收集数据
sku名称、价格、重量、sku描述都是收集的用户输入的数据。我们直接使用v-model
7.16.4 收集平台属性以及销售属性
我们在数据绑定的时候将这俩个属性所选择的数据绑定到自身。之后整合数据的时候通过遍历得到
7.16.5 img 数据&&设置默认图片
//设置默认图片的方法回调
const handler = (row: any) => {
//点击的时候,全部图片的的复选框不勾选
imgArr.value.forEach((item: any) => {
table.value.toggleRowSelection(item, false)
})
//选中的图片才勾选
table.value.toggleRowSelection(row, true)
//收集图片地址
skuParams.skuDefaultImg = row.imgUrl
}
7.17 完成添加sku
7.17.1 整合数据&&发请求
//收集SKU的参数
let skuParams = reactive<SkuData>({
//父组件传递过来的数据
category3Id: '', //三级分类的ID
spuId: '', //已有的SPU的ID
tmId: '', //SPU品牌的ID
//v-model收集
skuName: '', //sku名字
price: '', //sku价格
weight: '', //sku重量
skuDesc: '', //sku的描述
skuAttrValueList: [
//平台属性的收集
],
skuSaleAttrValueList: [
//销售属性
],
skuDefaultImg: '', //sku图片地址
})
//保存按钮的方法
const save = async () => {
//整理参数
//平台属性
skuParams.skuAttrValueList = attrArr.value.reduce((prev: any, next: any) => {
if (next.attrIdAndValueId) {
let [attrId, valueId] = next.attrIdAndValueId.split(':')
prev.push({
attrId,
valueId,
})
}
return prev
}, [])
//销售属性
skuParams.skuSaleAttrValueList = saleArr.value.reduce(
(prev: any, next: any) => {
if (next.saleIdAndValueId) {
let [saleAttrId, saleAttrValueId] = next.saleIdAndValueId.split(':')
prev.push({
saleAttrId,
saleAttrValueId,
})
}
return prev
},
[],
)
//添加SKU的请求
let result: any = await reqAddSku(skuParams)
if (result.code == 200) {
ElMessage({
type: 'success',
message: '添加SKU成功',
})
//通知父组件切换场景为零
$emit('changeScene', { flag: 0, params: '' })
} else {
ElMessage({
type: 'error',
message: '添加SKU失败',
})
}
}
7.17.2 bug
bug1:在发送请求的时候返回时undefined:注意;这种情况一般是由于API的请求函数没有写返回值(格式化之后)
bug2:平台属性和销售属性收集不到。可能时element-plus自带的table校验。前面数据填的格式不对(比如重量和价格input确定是数字但是可以输入字母e,这时候会导致错误)或者没有填写会导致后面的数据出问题。
7.18 sku展示
7.18.1 API&&type
API:
//查看某一个已有的SPU下全部售卖的商品
SKUINFO_URL = '/admin/product/findBySpuId/',
//获取SKU数据
export const reqSkuList = (spuId: number | string) => {
return request.get<any, SkuInfoData>(API.SKUINFO_URL + spuId)
}
TYPE
//获取SKU数据接口的ts类型
export interface SkuInfoData extends ResponseData {
data: SkuData[]
}
7.18.2 绑定点击函数&&回调
//存储全部的SKU数据
let skuArr = ref<SkuData[]>([])
let show = ref<boolean>(false)
//查看SKU列表的数据
const findSku = async (row: SpuData) => {
let result: SkuInfoData = await reqSkuList(row.id as number)
if (result.code == 200) {
skuArr.value = result.data
//对话框显示出来
show.value = true
}
}
7.18.3 模板展示
其实就是弹出一个对话框dialog,然后里面是一个form
<!-- dialog对话框:展示已有的SKU数据 -->
<el-dialog v-model="show" title="SKU列表">
<el-table border :data="skuArr">
<el-table-column label="SKU名字" prop="skuName"></el-table-column>
<el-table-column label="SKU价格" prop="price"></el-table-column>
<el-table-column label="SKU重量" prop="weight"></el-table-column>
<el-table-column label="SKU图片">
<template #="{ row, $index }">
<img
:src="row.skuDefaultImg"
style="width: 100px; height: 100px"
/>
</template>
</el-table-column>
</el-table>
</el-dialog>
7.19 删除spu业务
7.19.1 API
type为any,因此没有写专门的type
//删除已有的SPU
REMOVESPU_URL = '/admin/product/deleteSpu/',
//删除已有的SPU
export const reqRemoveSpu = (spuId: number | string) => {
return request.delete<any, any>(API.REMOVESPU_URL + spuId)
}
7.19.2 绑定点击函数
7.19.3 回调函数
//删除已有的SPU按钮的回调
const deleteSpu = async (row: SpuData) => {
let result: any = await reqRemoveSpu(row.id as number)
if (result.code == 200) {
ElMessage({
type: 'success',
message: '删除成功',
})
//获取剩余SPU数据
getHasSpu(records.value.length > 1 ? pageNo.value : pageNo.value - 1)
} else {
ElMessage({
type: 'error',
message: '删除失败',
})
}
}
7.20 spu业务完成
//路由组件销毁前,清空仓库关于分类的数据
onBeforeUnmount(() => {
categoryStore.$reset()
})