实现的功能
功能 | 详述 |
---|---|
商品列表 | 渲染组件和子路由;布局;获取和渲染商品列表;自定义格式化时间全局过滤器;搜索和清空;删除商品 |
添加商品页 | 渲染组件和子路由;布局;步骤条;纵向Tab页;步骤条和标签页联动 |
基本信息表单 | 商品分类使用级联选择器,只允许选中三级;阻止标签页切换 |
动态参数页 | 复选框tag;根据不同tab页执行不同操作 |
图片上传 | 渲染组件;图片上传;图片删除;图片预览 |
富文本编辑器 | |
添加商品 | 深拷贝 |
使用到的Element-ui组件
组件名称_EN | 注册 | 备注 |
---|---|---|
Steps | Vue.use(Steps) | 步骤条 |
Step | Vue.use(Step) | |
CheckboxGroup | Vue.use(CheckboxGroup) | 多选框 |
Checkbox | Vue.use(Checkbox) | |
Upload | Vue.use(Upload) | 上传 |
使用到的依赖
运行依赖,富文本编辑器vue-quill-editor
// [main.js]
// 导入富文本编辑器
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' // for bubble theme
// 注册富文本编辑器
Vue.use(VueQuillEditor)
运行依赖,lodash深拷贝lodash
// [Add.vue -> methods]
import _ from 'lodash'
一、商品列表
1.渲染组件和子路由
2.布局
①面包屑导航
②卡片视图
③栅格系统 搜索框 添加按钮
④表格
⑤分页
3.获取渲染商品列表
①获取商品列表
②渲染商品列表
1️⃣自定义格式化时间全局过滤器
// [main.js]
Vue.filter('dataFormat', function (originVal) {
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})
<!-- [GoodsList.vue] -->
<el-table-column label="创建时间" width="140px">
<template slot-scope="scope">{{scope.row.add_time | dataFormat}}</template>
</el-table-column>
4、搜索和清空
5、根据Id删除商品
6、添加商品页跳转
1️⃣编程式导航
// [GoodsList.vue]
goAddpage(){
this.$router.push('/goods/add')
}
// [index.js]
const routes = [
// ......
{path:'/goods/add',component:Add},
// ......
]
二、添加商品页
1.渲染组件和子路由
2.布局
①面包屑导航
②卡片视图
③警告
④步骤条Steps
:active="activeIndex-0":激活项
<!-- [Add.vue] -->
<!-- 步骤条 -->
<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>
// [Add.vue -> data]
activeIndex: '0'
/* [assets/css/global.css] */
.el-steps{
margin: 15px 0;
}
.el-step__title{
font-size: 13px;
}
⑤纵向标签页Tabs
<!-- [Add.vue] -->
<el-tabs
:tab-position="'left'"
v-model="activeIndex"
:before-leave="beforeTabLeave"
@tab-click="tabClicked"
>
<el-tab-pane label="基本信息" name="0"></el-tab-pane>
<el-tab-pane label="商品参数" name="1"></el-tab-pane>
<el-tab-pane label="商品图片" name="3"></el-tab-pane>
<el-tab-pane label="商品内容" name="4"></el-tab-pane>
</el-tabs>
⑥步骤条和标签页联动
activeIndex: '0',
步骤条:active
转换activeIndex为数值
标签页:v-model
激活的name会绑定到v-model上
⑦表单
使用form包裹tabs,el-tabs必须和el-tab-pane嵌套
3、基本信息
①渲染基本信息表单
1️⃣商品分类使用级联选择器,只允许选中三级
// [Add.vue -> methods]
// 级联选择器选中项变化
handleChange() {
// 选中的不是三级分类
if (this.addForm.goods_cat.length !== 3) {
this.addForm.goods_cat = []
}
}
2️⃣阻止标签页切换
:before-leave="beforeTabLeave":标签页属性,离开时触发
oldActiveName:离开的标签页name
activeName:进入的标签页name
// [Add.vue -> methods]
beforeTabLeave(activeName, oldActiveName) {
if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {
this.$message.error('请先选择商品分类')
return false
}
}
②获取商品分类数据
4、动态参数
①获取动态参数
@tab-click="tabClicked":标签页属性,点击时触发,可以获取activeIndex访问的tab面板
// [Add.vue -> methods]
async tabClicked() {
if (this.activeIndex === '1') {
// 访问动态参数面板
// ......
} else if (this.activeIndex === '2') {
// 访问静态属性面板
// ......
}
},
②渲染动态参数
// [Add.vue -> methods]
async tabClicked() {
if (this.activeIndex === '1') {
// ......
res.data.forEach((item) => {
item.attr_vals =
item.attr_vals.length === 0 ? [] : item.attr_vals.split(',')
})
}
// ......
}
<!-- [Add.vue] -->
<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="cb" v-for="(cb,i) in item.attr_vals" :key="i" border></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-tab-pane>
5、静态属性
①获取静态属性
②渲染静态属性
6、图片上传
action:图片上传的api地址
:on-preview:图片预览
:on-remove:删除图片
:headers:上传的请求头
:on-success:图片上传成功后处理操作
①渲染图片+上传
<!-- [Add.vue] -->
<el-tab-pane label="商品图片" name="3">
<!-- 上传 -->
<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>
// [Add.vue -> data]
uploadURl: 'http://127.0.0.1:8888/api/private/v1/upload',
// 图片上传的headers请求头对象
headerObj: {
Authorization: window.sessionStorage.getItem('token'),
},
// [Add.vue -> methods]
// 监听图片上传成功
handleSuccess(response) {
// 1,拼接得到一个图片信息对象
const picInfo = { pic: response.data.tmp_path }
//2,将图片信息对象,push 到pics数组中
this.addForm.pics.push(picInfo)
},
②图片移除
// 处理移除图片
handleRemove(file) {
// 1,获取将要删除的图片的临时路径
const filePath = file.response.data.tmp_path
// 2.从pics数组中,找到这个图片对应的索引值
const i = this.addForm.pics.findIndex((x) => x.pic === filePath)
// 3.调用数组的 splice方法,把图片信息对象,从pics数组中移除
this.addForm.pics.splice(i, 1)
},
③图片预览
<!-- 图片预览 -->
<el-dialog title="图片预览" :visible.sync="previewDialogVisible" width="50%">
<img :src="previewPath" alt class="previewImg" />
</el-dialog>
// [Add.vue -> data]
previewPath: '',
previewDialogVisible: false
// [Add.vue -> methods]
// 处理图片预览
handlePreview(file) {
this.previewPath = file.response.data.url
this.previewDialogVisible = true
}
7、富文本编辑器
<!-- [Add.vue] -->
<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>
/* [assets/css/global.css] */
.ql-editor{
min-height: 300px;
}
8、添加商品
表单预校验,校验通过发起请求
①预校验
// [Add.vue -> methods]
// 添加商品
async add() {
// 预校验
this.$refs.addFormRulesRef.validate((valid) => {
if (!valid) return this.$message.error('请填写必要的表单项')
})
// ......
}
②深拷贝
级联选择器要求数组,请求要求字符串---->深拷贝
// [Add.vue -> methods]
// 添加商品
async add() {
// ......
// 执行添加的业务逻辑
// 深拷贝 lodash this.addForm双向绑定级联选择器会报错
const form = _.cloneDeep(this.addForm)
form.goods_cat = form.goods_cat.join(',')
// ......
}
③商品参数(动态参数+静态属性)
// [Add.vue -> methods]
// 添加商品
async add() {
// ......
// 处理动态参数
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
// ......
}
④发起请求
// [Add.vue -> methods]
// 添加商品
async add() {
// ......
// 发起请求
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')
}