商品添加

编程式导航

  • 点击添加按钮前往商品添加页面
1
<el-button type="primary" @click="goAddpage">添加商品</el-button>
  • 处理函数
1
2
3
4
// 前往商品添加页面
goAddpage(){
this.$router.push('/goods/add')
}
  • 然后创建Add.vue组件,配置路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: Login },
{
path: '/home',
component: Home,
redirect: '/welcome',
children: [
// ...
{ path: '/goods', component: Goods },
{ path: '/goods/add', component: Add }
]
}
]
})

渲染步骤条

  • 使用到了Steps 步骤条组件,记得按需引入。实现Tag和步骤条的联动,同样一个activeIndex索引即可,但是一个是数值型一个是字符串型,注意转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<el-card>
<!--提示区-->
<el-alert title="添加商品信息" type="success" center show-icon :closable="false">
</el-alert>
<!--步骤条 activeIndex-0表示字符串转为数值型 -->
<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>
<!--标签-->
<el-tabs v-model="activeIndex" :tab-position="'left'" style="height: 200px">
<el-tab-pane label="基本信息" name="0">基本信息</el-tab-pane>
<el-tab-pane label="商品参数" name="1">商品参数</el-tab-pane>
<el-tab-pane label="商品属性" name="2">商品属性</el-tab-pane>
<el-tab-pane label="商品图片" name="3">商品图片</el-tab-pane>
<el-tab-pane label="商品内容" name="4">商品内容</el-tab-pane>
</el-tabs>
</el-card>
1
2
3
4
5
6
7
8
9
10
export default {
name: 'Add',
data() {
return {
activeIndex: 0,
}
},
created() {},
methods() {},
}
  • 在全局样式表global.css中自定义样式
1
2
3
4
5
6
.el-steps{
margin-top: 15px;
}
.el-step__title{
font-size: 13px !important;
}

基本信息面板

  • el-form要包裹标签页,因为数据全部提交才能添加。其中通过标签页的:before-leave="beforeTabLeave"属性指定回调函数,判断是否切换下一页标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

<!--由于数据必须全部填写才行,所以需要包裹tag--->
<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
<!--标签 定义name并绑定activeIndex实现联动-->
<el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave">
<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-cascader v-model="addForm.goods_cat" :options="cateList" :props="cateProps" @change="handleChange">
</el-cascader>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="商品参数" name="1">商品参数</el-tab-pane>
<el-tab-pane label="商品属性" name="2">商品属性</el-tab-pane>
<el-tab-pane label="商品图片" name="3">商品图片</el-tab-pane>
<el-tab-pane label="商品内容" name="4">商品内容</el-tab-pane>
</el-tabs>
</el-form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
export default {
name: 'Add',
data() {
return {
activeIndex: '0',
// 商品分类列表
cateList: [],
// 添加商品表单数据对象
addForm: {
goods_name: '',
goods_price: 0,
goods_weight: 0,
goods_number: 0,
// 商品所属分类
goods_cat: [],
},
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' },
],
},
cateProps: {
expandTrigger: 'hover',
label: 'cat_name',
value: 'cat_id',
children: 'children',
},
selectedKeys: [],
}
},
created() {
this.getCateList()
},
methods: {
// 获取分类信息
async getCateList() {
const { data: res } = await this.$http.get('categories')
if (res.meta.status !== 200) {
return this.$message.error('获取分类信息失败')
}
this.cateList = res.data
},
// 级联选择器选项变化触发
handleChange() {
if (this.addForm.goods_cat.length !== 3) {
this.addForm.goods_cat = []
}
},
// 切换标签页时的钩子
beforeTabLeave(activeName,oldActiveName){
// 处于第一个面板标签分类未选择三级时 不允许切换
if(oldActiveName === '0' && this.addForm.goods_cat.length !== 3){
this.$message.error('请先选择商品分类')
return false
}
}
},
}

商品参数面板

  • 当切换到下一个标签时发送请求,获取数据。通过标签的属性@tab-click="tabClicked"绑定标签切换的点击事件。注意是根据第一个面板的三级分类里的数据进行展示
  • 使用到了el-checkbox-group复选框组,其接收一个数组。使用需要把数据拆分从字符串数组
1
2
3
4
5
6
7
<el-tab-pane label="商品参数" name="1">
<el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id">
<el-checkbox-group v-model="item.attr_vals">
<el-checkbox :label="item1" v-for="(item1,index) in item.attr_vals" :key="index" border></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-tab-pane>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
export default {
name: 'Add',
data() {
return {
// ...
manyTableData: [], // 动态参数列表数据
}
},
created() {
this.getCateList()
},
computed: {
cateId() {
if (this.addForm.goods_cat.length === 3) {
return this.addForm.goods_cat[2]
}
return null
},
},
methods: {
// ...
// 切换标签页时的点击事件
async tabClicked() {
if (this.activeIndex === '1') {
// 商品参数面板,发送请求获取数据
const { data: res } = await this.$http.get(
`categories/${this.cateId}/attributes`,
{ params: { sel: 'many' } }
)
if (res.meta.status !== 200) {
this.$message.error('获取参数信息失败')
}
// 字符串拆分成数组
res.data.forEach(item=>{
item.attr_vals = item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')
})
console.log(res.data)
this.manyTableData = res.data
}
},
},
}

商品属性面板

  • 点击切换商品属性面板需要发送请求获取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async tabClicked() {
if (this.activeIndex === '1') {
//..
}
else if(this.activeIndex === '2'){
// 访问商品属性面板
// 商品参数面板,发送请求获取数据
const { data: res } = await this.$http.get(
`categories/${this.cateId}/attributes`,
{ params: { sel: 'only' } }
)
if (res.meta.status !== 200) {
this.$message.error('获取参数信息失败')
}
this.onlyTableData = res.data
}
}
  • 然后遍历获取表单
1
2
3
4
5
<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>

图片上传面板

  • 使用到了Upload上传组件,由于Upload内部封装并没有携带token会导致上传失败。需要手动为Headers添加token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<el-tab-pane label="商品图片" name="3">
<!--action:表示图片上传的API地址-->
<el-upload
:action="uploadUrl"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-success="handleSuccess"
list-type="picture"
:headers="headerObj"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>

<!--对话框,用于预览图片-->
<el-dialog
title="图片预览"
:visible.sync="previewDialogVisible"
width="50%"
>
<img :src="previewPath" class="previewImg">
</el-dialog>
  • 编写图片上传组件的回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
export default {
name: 'Add',
data() {
return {
// ...
// 添加商品表单数据对象
addForm: {
goods_name: '',
goods_price: 0,
goods_weight: 0,
goods_number: 0,
// 商品所属分类
goods_cat: [],
// 图片数组
pics: [],
},
// 上传图片的URL地址
uploadUrl: 'http://127.0.0.1:8888/api/private/v1/upload',
// 图片上传组件的对象
headerObj: {
Authorization: window.sessionStorage.getItem('token'),
},
// 预览的图片地址
previewPath: '',
previewDialogVisible: false,
}
},
methods: {
// ...
// 处理图片预览的效果
handlePreview(file) {
this.previewPath = file.response.data.url
this.previewDialogVisible = true
},
// 处理移除图片的操作
handleRemove(file) {
// 1. 获取将要删除的图片的临时路径
const filePath = file.response.data.tmp_path
// 2. 从pics数组中找到这个图片对应的索引值
const index = this.addForm.pics.findIndex((x) => {
x.pic === filePath
})
// 3. 调用数组的splice方法,把图片信息对象,从pics数组中删除
this.addForm.pics.splice(index, 1)
this.$message.success('移除成功')
},
// 上传图片成功的钩子函数
handleSuccess(response) {
// 1. 拼接得到一个图片信息对象
const picInfo = {
pic: response.data.tmp_path,
}
// 2. 将图片信息对象push到pics数组中
this.addForm.pics.push(picInfo)
},
},
}

商品内容面板

  • 需要安装vue-quill-editor富文本编辑器,在【依赖】中搜索安装,且为运行依赖

1
2
3
4
5
6
7
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // fo

// 注册为全局可用的组件
Vue.use(VueQuillEditor)
  • 然后在页面中使用
1
2
3
4
5
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
export default {
name: 'Add',
data() {
return {
// 添加商品表单数据对象
addForm: {
goods_name: '',
goods_price: 0,
goods_weight: 0,
goods_number: 0,
// 商品所属分类
goods_cat: [],
// 图片数组
pics: [],
// 商品详情描述
goods_introduce: '',
}
}
},
methods: {
//...
// 处理添加商品
add(){
console.log(this.addForm)
}
},
}

  • 点击添加按钮后发现表单双向绑定了内容值

添加商品完善

  • 在对数据处理中需要使用到深拷贝,这里要用到一个lodash包,在【依赖】中下载

  • 然后在使用的组件中导入即可,注意 是在组件中导入
1
import _ from "lodash"
  • 把数组goods_cat转成逗号分隔的字符串,通过深拷贝出一个全新的对象来操作,防止页面显示出现问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 处理添加商品
add(){
this.$refs.addFormRef.validate(valid=>{
// 表单预验证
if(!valid){
return this.$message.error('请填写必要的表单项!')
}
// 执行添加的业务逻辑
// 把goods_cat从数组转为字符串
// lodash包中的cloneDeep(obj)方法 进行深拷贝出一个全新的对象
const form = _.cloneDeep(this.addForm)
form.goods_cat = form.goods_cat.join(',')
console.log(form)
})
}

  • 继续把静态属性和动态参数修改后,发送请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 处理添加商品
add() {
this.$refs.addFormRef.validate( async (valid) => {
if (!valid) {
return this.$message.error('请填写必要的表单项!')
}
const form = _.cloneDeep(this.addForm)
form.goods_cat = form.goods_cat.join(',')
// 处理动态参数
this.manyTableData.forEach((item) => {
const newInfo = {
attr_id: item.attr_id,
attr_value: item.attr_vals.join(''),
}
this.addForm.attrs.push(newInfo)
})
// 处理静态属性
this.onlyTableData.forEach((item) => {
const newInfo = {
attr_id: item.attr_id,
attr_value: item.attr_vals,
}
this.addForm.attrs.push(newInfo)
})
form.attrs = this.addForm.attrs
// 发送请求添加商品
const { data: res } = await this.$http.post('goods',form)
if(res.meta.status !== 201){
return this.$message.error('添加商品失败')
}
this.$message.success('添加商品成功')
this.$router.push('/goods')
})
}

提交码云

  • goods_list分支更新提交到码云
1
2
3
4
5
git branch
git status
git add .
git commit -m "完成了商品添加功能"
git push
  • 然后在master分支合并goods_list分支并推送到码云
1
2
3
4
git checkout master
git branch
git merge goods_list
git push