(1)通过编程式导航跳转到商品添加页面
1. 在List.vue页面。点击添加商品,实现页面跳转
2. 创建商品添加组件Add.vue
3. 为Add组件添加路由
(2) 渲染添加页面的基本UI结构
使用到Alter警告和Steps 步骤条组件
1. 引入组件
2. add组件的基本UI结构
(3)美化步骤条组件
1. 增加进度,并修改文字
2. css样式
(4)渲染tab栏区域
(5)实现步骤条和tab栏的数据联动效果
通过让进度条和tab标签共用activeIndex的值,实现联动效果:
1. 当选择商品参数时,v-model绑定该选项对应的name值1,
2. 从而修改activeIndex的值为1
3. 进而使进度条当前所在的进度转到下标为1的项:商品参数
注意: v-model 绑定的是当前选中项的name值;
active 绑定的是当前进度的下标值
(6)绘制“基本信息”面板的UI结构
补充:在el-form标签中,添加下面属性,使输入框标题和输入框分行显示
(7)获取商品分类的数据
(8)绘制商品分类的级联选择器
1. 创建级联选择器
2. 相关的数据
3. 方法
(9)只允许选中三级分类
(10)阻止页签切换
1. 首先为tabs标签页添加属性before-leave,其值为一个事件函数。
1.1 该函数有两个参数(activeName将要跳转的标签项name,oldActiveName现在的标签项name)。
1.2 若该事件函数的返回值为false,则阻止跳转到下一个标签项
2. 在事件函数中,
如果当前页的name为0(在“基本信息”标签项)并且 选中项id的数组长度不等于3,则禁止跳转,返回fals
目的:实现只有选中三级分类后,才可跳转
(11)获取动态参数列表数据(当点击商品参数页签时)
1. 为tabs页签添加点击事件,选择页签时触发该方法
2. 在该方法中,判断当前所在的页签是不是商品参数。
3. 如果是,则进行数据请求,获取动态参数列表
4. 为了便于获取当前选中分类的id,定义计算属性cateId
点击商品参数时,获取到的数据为:
(12)绘制商品参数面板中的复选框组
1. 引入复选框组件
2. 首先在获取动态参数列表的方法中,先将每个参数所对应的标签字符串attr_vals 转化为 数组
3. 通过第一个for循环,依次显示每一个参数名
4. 再通过嵌套的第二个for循环,以复选框的形式,依次显示当前参数对应的所有标签
补充:优化复选框的样式,使复选框开头对齐
(13)获取静态属性列表数据
(14)渲染商品属性面板的UI结构
(15)初步使用upload上传组件
1. 实现以下功能:
1.1 点击上传,选择本地图片进行上传
1.2 上传成功后,显示:(也可以点击右上角的x,进行移除)
1.3 点击上传的图片,进行预览
2. 用到upload上传组件,引入该组件
3. 使用upload上传组件
效果图:
(16)手动为upload绑定headers请求头
问题:在Network中显示是无效的ttoken。
说明上传图片时没有使用axios发送请求,而是使用其内部的封装的一套ajax。其内部封装的ajax没有Authorization字段。
解决:通过headers为其指定Authorization字段
(17)监听upload组件的on-success事件(上传成功事件)
1. API文档中相关的信息
2. 为upload组件添加on-success(上传成功时,触发该方法)
3. 上传成功后,返回的数据中有图片的临时路径。将该路径保存到pics数组中
上传成功后返回的数据:
上传图片临时路径的pics数组:
(18)监听upload上传组件的on-remove事件(删除图片)
删除图片时,会返回数据:
(19)实现图片的预览效果
1. 监听图片的预览事件on-preview
2. 点击图片时,触发预览事件,该事件会传入数据。
3. 将数据中url(图片地址)赋值给变量previewPath,并且显示对话框(对话框中存放图片)
4. 使用对话框组件,显示图片预览。内容部分放当前点击的图片
(20)安装并配置vue-quill-editor(富文本编辑器)
1. 安装插件vue-quill-editor
2. 导入该插件(富文本编辑器),并全局注册
// 导入富文本编辑器对应的样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'// 3.将富文本编辑器注册为全局可用的组件
Vue.use(VueQuillEditor)
3. 使用富文本编辑器
在全局css文件中
4. 添加商品按钮,查看addForm中的商品介绍是否与富文本编辑器输入的内容双向绑定
5. 效果图
(21)实现表单数据的预验证
1. 点击添加按钮,通过add方法,对提交的表单数据进行验证
(22)把选中分类的id数组(goods_cat)转化为字符串
1. 安装运行依赖lodash,调用方法cloneDeep(要拷贝的对象),实现深拷贝(对赋值的addForm操作,避免与级联选择器中绑定的goods_cat发生冲突)
2. 导入lodash
3. 使用其方法cloneDeep(要拷贝的对象)进行深拷贝,并将拷贝对象中的goods_cat数组转化为字符串
将上图中修改为打印form
(23)在add方法中处理attrs数组(商品的参数(包括动态参数和静态属性))
addForm. attrs:表示商品的参数(包括动态参数和静态属性)
1. 首先添加为addForm添加attrs属性
2. 在add方法中,获取动态参数和静态属性的attr_id和attr_value,存到addForm. attrs中
(24)完成添加商品的操作
Add.vue
<template><div><!-- 面包屑 导航区 --><el-breadcrumb separator="/"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>商品列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区 --><el-card><!-- alert警告 --><el-alerttitle="添加商品信息"type="info"centershow-icon></el-alert><!-- 进度条 --><el-steps :space="200" :active="activeIndex-0" finish-status="success" align-center><el-step title="基本信息"></el-step><el-step title="商品参数"></el-step><el-step title="商品属性"></el-step><el-step title="商品图片"></el-step><el-step title="商品内容"></el-step><el-step title="完成"></el-step></el-steps><!-- tabs标签页区域. 在外面添加表单便于数据的显示注意:tabs和tabs-pane标签之间不能有其它标签,所以将form放在最外面--><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top"><el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTagLeave" @tab-click="tabClicked"><el-tab-pane label="基本信息" name="0"><el-form-item label="商品名称" prop="goods_name"><el-input v-model="addForm.goods_name"></el-input></el-form-item><el-form-item label="商品价格" prop="goods_price"><el-input v-model="addForm.goods_price" type="number"></el-input></el-form-item><el-form-item label="商品重量" prop="goods_weight"><el-input v-model="addForm.goods_weight" type="number"></el-input></el-form-item><el-form-item label="商品数量" prop="goods_number"><el-input v-model="addForm.goods_number" type="number"></el-input></el-form-item><el-form-item label="商品分类" prop="goods_cat"><el-cascaderexpand-trigger="hover":options="catelist":props="cateProp"v-model="addForm.goods_cat"@change="handleChange"></el-cascader></el-form-item></el-tab-pane><el-tab-pane label="商品参数" name="1"><!-- 通过for循环,显示当前选中分类下的每个参数名item.attr_name --><el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id"><!-- 通过for循环,显示当前参数下所对应的所有取值attr_vals --><el-checkbox-group v-model="item.attr_vals"><el-checkbox :label="cb" v-for="(cb,i) in item.attr_vals" :key="i" border></el-checkbox></el-checkbox-group></el-form-item></el-tab-pane><el-tab-pane label="商品属性" name="2"><el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id"><el-input v-model="item.attr_vals"></el-input></el-form-item></el-tab-pane><el-tab-pane label="商品图片" name="3"><!-- 1. action:图片要上传到的后台API地址2. on-preview 预览图片操作3. on-remove 移除图片操作4. list-type="picture" 以图片的形式显示--><el-upload:action="uploadUrl":on-preview="handlePreview":on-remove="handleRemove"list-type="picture":headers="headerObj":on-success="handleSuccess"><el-button size="small" type="primary">点击上传</el-button></el-upload></el-tab-pane><el-tab-pane label="商品内容" name="4"><!-- 富文本编辑器组件 --><quill-editor v-model="addForm.goods_introduce"></quill-editor><!-- 添加商品按钮 --><el-button type="primary" class="btnAdd" @click="add">添加商品</el-button></el-tab-pane></el-tabs></el-form></el-card><!-- 图片预览框 --><el-dialogtitle="图片预览":visible.sync="previewVisible"width="50%"><img :src="previewPath" class="previewImg"></el-dialog></div>
</template>
<script>
// 导入lodash,将其命名为_。以进行深拷贝
import _ from 'lodash'
export default {data () {return {// 默认选中进度条第一项(第一项对应0),也是选中tab标签中的name值activeIndex: '0',// 添加商品的数据对象addForm: {goods_name: '',goods_price: 0,goods_weight: 0,goods_number: 0,goods_cat: [], // 添加商品所属的分类数组(存放选中的分类id)pics: [], // 图片临时地址的对象数组goods_introduce: '', // 商品的详情描述attrs: [] // 商品参数(包括动态参数和静态属性的attr_id和attr_value)},// 表单的验证规则addFormRules: {goods_name: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],goods_price: [{ required: true, message: '请输入商品价格', trigger: 'blur' }],goods_weight: [{ required: true, message: '请输入商品重量', trigger: 'blur' }],goods_number: [{ required: true, message: '请输入商品数量', trigger: 'blur' }],goods_cat: [{ required: true, message: '请选中商品分类', trigger: 'blur' }]},// 商品分类列表catelist: [],// 级联选择框的属性配置cateProp: {value: 'cat_id', // 选中的是什么label: 'cat_name', // 看到的是什么children: 'children' // 具有父子关系的数组},// 动态参数列表数据manyTableData: [],// 静态属性列表数据onlyTableData: [],// 图片要上传到的后台API地址(/upload前面的地址为根路径)uploadUrl: 'http://127.0.0.1:8888/api/private/v1/upload',// 图片上传组件的headers请求头对象headerObj: {Authorization: window.sessionStorage.getItem('token')},// 被点击图片的url地址previewPath: '',// 控制图片预览框的显示与隐藏previewVisible: false}},created () {this.getCateList()},methods: {// 1. 获取商品分类数据(以在商品选择中显示)async getCateList() {const { data: res } = await this.$http.get('categories')if (res.meta.status !== 200) {return this.$message.error('获取商品分类数据失败!')}this.catelist = res.dataconsole.log(res.data)},// 2.级联选择器选中项发生变化时,会触发这个函数handleChange() {console.log(this.addForm.goods_cat)// 没有选中三级分类。将存放选中id的数组置空if (this.addForm.goods_cat.length !== 3) {this.addForm.goods_cat = []}},// 3.控制标签页的跳转(只有选中第一个标签页的三级分类后,才可跳转)beforeTagLeave(activeName, oldActiveName) {// 当前在第一个标签页,但是没有选中三级分类if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {this.$message.error('请选择商品分类!')return false}},// 4. 点击页签时触发该方法async tabClicked() {// 4.1 选中商品参数页签, 获取动态参数列表数据if (this.activeIndex === '1') {// 发送数据请求const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{ params: { sel: 'many' } })if (res.meta.status !== 200) {return this.$message.error('获取参数列表失败')}// 先将获取的参数列表中每一个参数所对应的标签字符串,转化为 标签数组res.data.forEach(item => {item.attr_vals = item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')})// 将获取的动态参数列表保存this.manyTableData = res.dataconsole.log(res.data)} else if (this.activeIndex === '2') { // 4.2 选中商品属性页签,获取静态属性列表数据// 发送数据请求const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{ params: { sel: 'only' } })if (res.meta.status !== 200) {return this.$message.error('获取属性列表失败')}// 将获取的属性列表保存this.onlyTableData = res.dataconsole.log(res.data)}},// 5. 处理预览图片效果handlePreview(file) {// 1.获取点击图片的url地址this.previewPath = file.response.data.url// 2.使对话框(图片预览)可见this.previewVisible = true},// 6. 处理移除图片的操作handleRemove(file) {// 6.1 获取将要删除的图片的临时路径const filePath = file.response.data.tmp_path// 6.2 从pics 数组中,找到这个图片对应的索引值(使用foreach依次遍历pics数组,寻找filePath所对应的索引值)const i = this.addForm.pics.forEach(item =>item.pic === filePath)// 6.3 调用数组的splice方法,把图片信息对象,从pics数组中移除this.addForm.pics.splice(i, 1)console.log(this.addForm.pics)},// 7. 监听图片上传成功的事件(response:上传成功后返回的数据,包括图片的临时路径)handleSuccess(response) {// 1.拼接得到一个图片信息对象(图片临时路径)const pinInfo = { pic: response.data.tmp_path }// 2.将获取到的图片对象 加入到 pics数组this.addForm.pics.push(pinInfo)console.log(this.addForm.pics)},// 8.点击添加商品按钮,触发add() {this.$refs.addFormRef.validate(async valid => {// 8.1 预验证未通过if (!valid) {return this.$message.error('请填写必要的表单项')}// 8.2 预验证通过。执行添加的业务逻辑// 8.2.1 首先将选中的分类id数组( goods_cat),转化为字符串// 为了避免与级联选择器中绑定的 goods_cat 产生冲突,对原始的addForm进行深拷贝const form = _.cloneDeep(this.addForm)// 将选中分类id数组,转化为字符串form.goods_cat = form.goods_cat.join(',')// 8.3 获取动态参数数组中每个参数的attr_id和 attr_valuethis.manyTableData.forEach(item => {const newInfo = {attr_id: item.attr_id,attr_value: item.attr_vals.join(' ') // 对应参数的标签是用空格连起来的字符串}this.addForm.attrs.push(newInfo)})// 8.4 获取静态属性数组中每个属性的attr_id和 attr_valuethis.onlyTableData.forEach(item => {const newInfo = {attr_id: item.attr_id,attr_value: item.attr_vals}this.addForm.attrs.push(newInfo)})// 8.5 将获取的addForm的attrs赋值给form的attrsform.attrs = this.addForm.attrsconsole.log(form)// 8.6 发送添加商品请求(注意:商品名称是唯一的)const {data : res} = await this.$http.post('goods',form)if (res.meta.status !== 201) {return this.$message.error('添加商品失败!')}// 8.7 添加商品成功,回到商品列表页面this.$message.success('添加商品成功')this.$router.push('/goods')})}},computed: {// 1.获取当前选中分类的idcateId() {// 选中三级分类,返回三级分类的idif (this.addForm.goods_cat.length === 3) {return this.addForm.goods_cat[2]}// 没有选中return null}}
}
</script>
<style lang="less" scoped>.el-checkbox {margin: 0 10px 0 0 !important;}.previewImg {width: 100%;}/* 为添加商品按钮添加外边距*/.btnAdd {margin-top: 15px;}
</style>