硅谷甄选(8)spu

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-columnlabel="序号"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-paginationv-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: numbermessage: stringok: boolean
}//SPU数据的ts类型:需要修改
export interface SpuData {category3Id: string | numberid?: numberspuName: stringtmId: number | stringdescription: stringspuImageList: nullspuSaleAttrList: null
}
//数组:元素都是已有SPU数据类型
export type Records = SpuData[]
//定义获取已有的SPU接口返回的数据ts类型
export interface HasSpuResponseData extends ResponseData {data: {records: Recordstotal: numbersize: numbercurrent: numbersearchCount: booleanpages: number}
}
7.2.3 添加SPU按钮

7.2.4 表单数据
<el-table border style="margin: 10px 10px" :data="records"><el-table-columnlabel="序号"type="index"align="center"width="80px"></el-table-column><el-table-column label="SPU名称" prop="spuName"></el-table-column><el-table-columnlabel="SPU描述"prop="description"show-overflow-tooltip></el-table-column><el-table-column label="SPU操作"><!-- row:即为已有的SPU对象 --><template #="{ row, $index }"><el-buttontype="primary"size="small"icon="Plus"title="添加SKU"></el-button><el-buttontype="primary"size="small"icon="Edit"title="修改SPU"></el-button><el-buttontype="primary"size="small"icon="View"title="查看SKU列表"></el-button><el-popconfirm :title="`你确定删除${row.spuName}?`" width="200px"><template #reference><el-buttontype="primary"size="small"icon="Delete"title="删除SPU"></el-button></template></el-popconfirm></template></el-table-column>
</el-table>
7.2.5 分页器

注意getHasSpu函数携带的参数。默认为1

<!-- 分页器 --><el-paginationv-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 = pagerlet result: HasSpuResponseData = await reqHasSpu(pageNo.value,pageSize.value,categoryStore.c3Id,)if (result.code == 200) {records.value = result.data.recordstotal.value = result.data.total}
}
//分页器下拉菜单发生变化的时候触发
const changeSize = () => {getHasSpu()
}
7.2.6 watch监听
//监听三级分类ID变化
watch(() => categoryStore.c3Id,() => {//当三级分类发生变化的时候清空对应的数据records.value = []//务必保证有三级分类IDif (!categoryStore.c3Id) returngetHasSpu()},
)

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-uploadv-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-buttonstyle="margin-left: 10px"type="primary"size="default"icon="Plus">添加属性</el-button><!-- table展示销售属性与属性值的地方 --><el-table border style="margin: 10px 0px"><el-table-columnlabel="序号"type="index"align="center"width="80px"></el-table-column><el-table-columnlabel="销售属性名字"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结构->SpuFormscene.value = 1
}//修改已有的SPU的按钮的回调
const updateSpu = () => {//切换为场景1:添加与修改已有SPU结构->SpuFormscene.value = 1
}
7.3.4 子组件中取消按钮的回调

需要改变的是父组件中的scene,因此涉及到父子组件通信。这里使用自定义事件。

父组件:

子组件:

//取消按钮的回调
const cancel = () => {$emit('changeScene', 0)
}

7.4 SPU模块API&&TS类型(修改&&添加)

修改和添加的页面是差不多的。页面1的四个地方都需要发请求拿数据,我们在这一部分分别编写4个部分的API以及ts类型

7.4.1 SPU品牌
  1. 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)
}
  1. ts
  2. src\api\product\spu\type.ts
//品牌数据的TS类型
export interface Trademark {id: numbertmName: stringlogoUrl: string
}
//品牌接口返回的数据ts类型
export interface AllTradeMark extends ResponseData {data: Trademark[]
}
7.4.2 SPU图片
  1. API
//获取某个SPU下的全部的售卖商品的图片数据IMAGE_URL = '/admin/product/spuImageList/',
//获取某一个已有的SPU下全部商品的图片地址
export const reqSpuImageList = (spuId: number) => {return request.get<any, SpuHasImg>(API.IMAGE_URL + spuId)
}
  1. ts
//商品图片的ts类型
export interface SpuImg {id?: numberimgName?: stringimgUrl?: stringcreateTime?: stringupdateTime?: stringspuId?: numbername?: stringurl?: string
}
//已有的SPU的照片墙数据的类型
export interface SpuHasImg extends ResponseData {data: SpuImg[]
}
7.4.3 全部销售属性
  1. API
//获取整个项目全部的销售属性[颜色、版本、尺码]ALLSALEATTR_URL = '/admin/product/baseSaleAttrList',
//获取全部的销售属性
export const reqAllSaleAttr = () => {return request.get<any, HasSaleAttrResponseData>(API.ALLSALEATTR_URL)
}
  1. ts
//已有的全部SPU的返回数据ts类型
export interface HasSaleAttr {id: numbername: string
}
export interface HasSaleAttrResponseData extends ResponseData {data: HasSaleAttr[]
}
7.4.4 已有的销售属性
  1. API
//获取某一个SPU下全部的已有的销售属性接口地址SPUHASSALEATTR_URL = '/admin/product/spuSaleAttrList/',
//获取某一个已有的SPU拥有多少个销售属性
export const reqSpuHasSaleAttr = (spuId: number) => {return request.get<any, SaleAttrResponseData>(API.SPUHASSALEATTR_URL + spuId)
}
  1. ts
//销售属性对象ts类型
export interface SaleAttr {id?: numbercreateTime?: nullupdateTime?: nullspuId?: numberbaseSaleAttrId: number | stringsaleAttrName: stringspuSaleAttrValueList: SpuSaleAttrValueListflag?: booleansaleAttrValue?: string
}
//SPU已有的销售属性接口返回数据ts类型
export interface SaleAttrResponseData extends ResponseData {data: SaleAttr[]
}

7.5 获取SPU的数据

首先:SPU的数据应该分为5部分:第一部分:是父组件里的展示的数据,也是我们点击修改按钮时的那个数据。其余4个部分的数据需要我们发请求得到。

问题1:子组件需要用到父组件中的数据,应该怎么办?答:要传递的数据是指定的,也就是我们点击修改时的数据。通过ref的方式,拿到子组件时的实例,再调用子组件暴露的方法将数据做为参数传递过去。(有点类似于反向的自定义事件)

问题2:其余4个部分的数据什么时候获取。答:同样的在点击修改按钮时获取,问题一中通过调用子组件的函数传递数据,我们同时也在这个函数中发请求得到数据

7.5.1 第一部分数据的传递
  1. 父组件拿到子组件实例

  1. 子组件暴露对外函数

  1. 修改按钮点击函数中调用子组件函数,并传递第一部分数据

//修改已有的SPU的按钮的回调
const updateSpu = (row: SpuData) => {//切换为场景1:添加与修改已有SPU结构->SpuFormscene.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)
/追加一个新的SPUADDSPU_URL = '/admin/product/saveSpuInfo',//更新已有的SPUUPDATESPU_URL = '/admin/product/updateSpuInfo',
//添加一个新的SPU的
//更新已有的SPU接口
//data:即为新增的SPU|或者已有的SPU对象
export const reqAddOrUpdateSpu = (data: any) => {//如果SPU对象拥有ID,更新已有的SPUif (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 | numberid?: numberspuName: stringtmId: number | stringdescription: stringspuImageList: null | SpuImg[]spuSaleAttrList: null | SaleAttr[]
}

7.7 展示与收集已有的数据

7.7.1 存储父组件传递过来的数据
//存储已有的SPU对象
let SpuParams = ref<SpuData>({category3Id: '', //收集三级分类的IDspuName: '', //SPU的名字description: '', //SPU的描述tmId: '', //品牌的IDspuImageList: [],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组件。下面详细介绍组件的功能及作用

  1. 整体结构

上面el-upload是上传照片的照片墙,下面是查看照片的对话框

  1. 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方法,这是因为组件对于数据的格式有要求。

  1. action

action是指图片上传的地址。组件还会将返回的数据放到对应的img的数据中

  1. list-type:照片墙的形式

  1. :on-preview

预览的钩子,预览照片时会触发。会注入对应图片的数据。

//控制对话框的显示与隐藏
let dialogVisible = ref<boolean>(false)
//存储预览图片地址
let dialogImageUrl = ref<string>('')
//照片墙点击预览按钮的时候触发的钩子
const handlePictureCardPreview = (file: any) => {dialogImageUrl.value = file.url//对话框弹出来dialogVisible.value = true
}
  1. :on-remove

移除图片前的钩子

  1. :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-columnlabel="序号"type="index"align="center"width="80px"></el-table-column><el-table-columnlabel="销售属性名字"width="120px"prop="saleAttrName"></el-table-column><el-table-column label="销售属性值"><!-- row:即为当前SPU已有的销售属性对象 --><template #="{ row, $index }"><el-tagclass="mx-1"closablestyle="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-buttontype="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-buttontype="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 = truerow.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.valuelet 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业务我们要做什么?收集数据(发请求得到的、自己添加的)放到对应的数据(存储数据用的容器)中,发起请求(保存按钮已经做完了),更新页面

  1. 父组件添加按钮回调

添加和修改按钮不同的地方在于对于数据的来源不同,修改按钮是一部分(spuParams)来源于父组件传递的数据,将他们与组件绑定,在数据上展示。添加按钮父组件只需要传递category3Id就行,其他的自己收集。

//添加新的SPU按钮的回调
const addSpu = () => {//切换为场景1:添加与修改已有SPU结构->SpuFormscene.value = 1//点击添加SPU按钮,调用子组件的方法初始化数据spu.value.initAddSpu(categoryStore.c3Id)
}
  1. 子组件收集数据

注意要对外暴露,让父组件可以使用

//添加一个新的SPU初始化请求方法
const initAddSpu = async (c3Id: number | string) => {//存储三级分类的IDSpuParams.value.category3Id = c3Id//获取全部品牌的数据let result: AllTradeMark = await reqAllTradeMark()let result1: HasSaleAttrResponseData = await reqAllSaleAttr()//存储数据MYAllTradeMark.value = result.dataallSaleAttr.value = result1.data
}
//对外暴露
defineExpose({ initHasSpuData, initAddSpu })
  1. 整理数据与发送请求

这部分通过保存按钮的回调已经做完了。

7.13.2 清空数据

我们应该在每次添加spu前清空上次的数据。

//添加一个新的SPU初始化请求方法
const initAddSpu = async (c3Id: number | string) => {//清空数据Object.assign(SpuParams.value, {category3Id: '', //收集三级分类的IDspuName: '', //SPU的名字description: '', //SPU的描述tmId: '', //品牌的IDspuImageList: [],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:展示已有的SPUscene.value = obj.flagif (obj.params == 'update') {//更新留在当前页getHasSpu(pageNo.value)} else {//添加留在第一页getHasSpu()}
}

7.14添加SKU的静态

7.14.1 绑定回调

//添加SKU按钮的回调
const addSku = (row: SpuData) => {//点击添加SKU按钮切换场景为2scene.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-columntype="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按钮切换场景为2scene.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-columntype="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 //平台属性的IDvalueId: number | string //属性值的ID
}
export interface saleArr {saleAttrId: number | string //属性IDsaleAttrValueId: number | string //属性值的ID
}
export interface SkuData {category3Id: string | number //三级分类的IDspuId: string | number //已有的SPU的IDtmId: string | number //SPU品牌的IDskuName: 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.category3IdskuParams.spuId = spu.idskuParams.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: '', //三级分类的IDspuId: '', //已有的SPU的IDtmId: '', //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()
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1774.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【element el-date-picker限制时间选择范围】

问题场景 在一次项目中,有一个这样的需求就是填报时间需要在上一次提交信息之后,在限制时间时发现,el-date-picker中的pickerOption中的disableDate是基于日期限制的,若限制日期为今日凌晨,那么今天之后的日期都不能填 <el-date-pickerv-model="selectedDateTime&…

我也谈AI

“随着人工智能技术的不断发展&#xff0c;我们已经看到了它在各行业带来的巨大变革。在医疗行业中&#xff0c;人工智能技术正在被应用于病例诊断、药物研发等方面&#xff0c;为医学研究和临床治疗提供了新的思路和方法&#xff1b;在企业中&#xff0c;人工智能技术可以通过…

机器人对人工智能未来发展的影响

机器人作为人工智能&#xff08;AI&#xff09;技术的一个重要应用领域&#xff0c;对人工智能的未来发展具有深远的影响。机器人和人工智能在技术上存在深度的交叉和融合。人工智能为机器人提供了强大的感知、决策和执行能力&#xff0c;而机器人则成为人工智能技术的最佳载体…

大数据-199 数据挖掘 机器学习理论 - 决策树 模型 决策与条件 香农熵计算

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

ES入门:查询和聚合

安装完ElasticSearch 和 Kibana后我们开始学习 为了方便测试&#xff0c;使用kibana的dev tool来进行学习测试&#xff1a; 测试工具 从索引文档开始 插入 向 Elasticsearch 索引 customer 的 _doc 类型的文档 id 为 1 的文档发送 PUT 请求的例子。 请求体为 JSON 格式&am…

力扣——二叉树的后序遍历(C语言)

1.题目&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;返回其节点值的后序遍历。 2.原理&#xff1a; 这里的遍历&#xff0c;是要存入到数组中&#xff0c;所以需要建立数组&#xff0c;这里传参有*returnSize&#xff0c;需要求节点个数&#xff0c;可以调用前面Tr…

软件测试学习笔记丨Flask操作数据库-数据库和表的管理

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/23427 结构分析 Runner是中间件sqlachemy去驱动Runner&#xff0c;根据不同的数据库去驱动不同的中间件 demo # 导入Flask的类 from flask import Flask # 实例化 Flask的类&#xff0c;并且…

一个小程序如何对接多个收款账户?

背景 我又来了&#xff0c;之前对接过网约巴士系统 网约巴士旅游专线平台搭建历程&#xff0c;运营了两年多了。在运营中完善、在完善中学习&#xff0c;一直是不变的真理。有一句话说得好&#xff1a;先做一个垃圾、用起来再说。 今天又需要升级了&#xff0c;需求是&#…

模型 定位地图

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。心智导航现实的空间图。 1 定位地图模型的应用 1.1 小玉的职业定位与发展规划 小玉&#xff0c;24岁&#xff0c;市场营销专业本科毕业生&#xff0c;有半年汽车销售实习经历。毕业后&#xff0c;她…

java项目之协力服装厂服装生产管理系统的设计与实现(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的协力服装厂服装生产管理系统的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; …

[MySQL#7] CRUD(2) | 更新 | 删除 | 聚合函数 | group by

目录 3. 更新 4. 删除 截断表 日志的作用 5. (实验) 插入查询结果 6. 聚合函数 7. 分组查询 接着上篇文章[MySQL#6] 表的CRUD (1) | Create | Retrieve(查) | where继续讲解~ 3. 更新 语法&#xff1a; UPDATE table_name SET column expr [, column expr ...][WHE…

RT-Thread PIN设备 UART设备

引脚简介 芯片上的引脚一般分为四类&#xff1a;电源、时钟、控制与I/O。 I/O口在使用模式上又分为General Purpose Input Output&#xff08;通用输入/输出&#xff09;&#xff0c;简称GPIO&#xff0c;与功能复用I/O&#xff08;如SPI/I2C/UART等&#xff09;。 大多数MCU…

【element ui系列】分享几种实现el-table表格单选的方法

在实际的开发中&#xff0c;经常会用到从表格中选择一条记录的情况&#xff0c;虽然官方给出的例子&#xff0c;但是给人感觉看起来不明显&#xff0c;于是&#xff0c;在此基础上做了改进。接下来&#xff0c;介绍两种常见的实现方法&#xff1a; 1、采用复选框(checkbox)实现…

FastAPI中如果async def和def 路由的区别

在python的整体生态中&#xff0c;虽然已经有很多库支持了异步调用&#xff0c;如可以使用httpx或者aiohttp代替requests库发起http请求&#xff0c;使用asyncio.sleep 代替time.sleep&#xff0c; 但是依然还有很多优秀的第三方库是不支持异步调用也没有可代替的库&#xff0c…

架构师备考-非关系型数据库

基础理论 CAP 理论 C&#xff08;Consistency&#xff09;一致性。一致性是指更新操作成功并返回客户端完成后&#xff0c;所有的节点在同一时间的数据完全一致&#xff0c;与ACID 的 C 完全不同。A &#xff08;Availability&#xff09;可用性。可用性是指服务一直可用&…

奥云学院应邀参加“第二届中国县域经济投资高峰论坛”

论坛聚焦战略&#xff0c;县域经济迎来新机遇 10月28日&#xff0c;由中国投资协会主办的第二届中国县域经济投资高峰论坛在北京盛大召开。本次论坛以“产业资本助力县域经济高质量发展”为主题&#xff0c;汇聚政府、企业、金融机构和学术专家等多方资源&#xff0c;集中探讨…

飞牛NAS docker compose环境下自建远程桌面服务:rustdesk

&#x1f6e9;️前言 由于国内向日葵、todesk等应用的日渐模糊&#xff0c;恰巧我们已经实现了ipv6的内网穿透&#xff0c;而且在国内ipv6的延迟极低&#xff0c;加上本次介绍的开源远程桌面项目Rustdesk&#xff0c;简直是绝配。 这个项目比较简单&#xff0c;话不多说&…

算法:查找

算法 1. 顺序查找和折半查找1.1 顺序查找1.2 折半查找1.3 索引顺序查找 2. 树表查找2.1 查找2.2 插入 3. 哈希表及哈希查找3.1 哈希造表3.2 处理冲突开放定址法链地址法 3.3 哈希查找 查找是非数值数据处理中一种基本运算&#xff0c;查找运算的效率与查找表所采用的数据结构和…

Istio基本概念及部署

一、Istio架构及组件 Istio服务网格在逻辑上分为数据平面和控制平面。 控制平面&#xff1a;使用全新的部署模式&#xff1a;Istiod&#xff0c;这个组件负责处理Sidecar注入&#xff0c;证书颁发&#xff0c;配置管理等功能&#xff0c;替代原有组件&#xff0c;降低复杂度&…

OpenCV视觉分析之目标跟踪(8)目标跟踪函数CamShift()使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 找到物体的中心、大小和方向。 CamShift&#xff08;Continuously Adaptive Mean Shift&#xff09;是 OpenCV 中的一种目标跟踪算法&#xff0…