商品列表篇
创建分支
git checkout -b goods_list
git push -u origin goods_list
1 通过路由加载商品列表组件
//goods下建List.vue
//index.js(router)
import List from '@/components/goods/List'
{ path: '/goods', component: List }
2 渲染商品列表数据
<!-- 面包屑导航区 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<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>
<el-row :gutter="20" class="cat_opt">
<el-col :span="8">
<el-input placeholder="请输入内容">
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</el-col>
<el-col :span="4">
<el-button type="primary">添加商品</el-button>
</el-col>
</el-row>
<!-- table表格区 -->
<el-table :data="goodslist" border stripe>
<el-table-column label="#" type="index"> </el-table-column>
<el-table-column label="商品名称" prop="goods_name"> </el-table-column>
<el-table-column label="商品价格(元)" prop="goods_price" width="95px">
</el-table-column>
<el-table-column label="商品重量" prop="goods_weight" width="70px">
</el-table-column>
<el-table-column label="创建时间" prop="add_time" width="140px"> </el-table-column>
<el-table-column label="操作" width="130px">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini"></el-button>
<el-button type="danger" icon="el-icon-delete" size="mini"></el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
export default {
name: "List",
data() {
return {
// 查询参数
queryInfo:{ query:'', pagenum: 1, pagesize:10 },
// 商品列表
goodslist:[],
// 总数据条数
total:0
}
},
created() {
this.getGoodsList()
},
methods: {
// 根据分页获取对应的商品列表
async getGoodsList() {
const { data: res } = await this.$http.get("goods", {
params: this.queryInfo
})
if (res.meta.status != 200) {
return this.$message.error("获取商品列表失败")
}
// console.log(res.data);
this.goodslist = res.data.goods
this.total = res.data.total
}
}
}
3 自定义格式化时间的全局过滤器
//main.js
// 格式化时间的过滤器
Vue.filter('dateFormat', 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}`
})
//List.vue
<el-table-column label="创建时间" prop="add_time" width="140px">
<template slot-scope="scope">
{{scope.row.add_time | dateFormat}}
</template>
</el-table-column>
4 实现商品列表的分页功能
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryInfo.pagenum"
:page-sizes="[ 5, 10,15,20]"
:page-size="queryInfo.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
>
</el-pagination>
methods:{
//监听 pagesize 改变
handleSizeChange(newSize) {
this.queryInfo.pagesize = newSize
this.getGoodsList()
},
//监听页码值改变
handleCurrentChange(newPage) {
this.queryInfo.pagenum = newPage
this.getGoodsList()
}
}
5 实现搜索与清空的功能
<el-col :span="8">
<el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getGoodsList">
<el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button>
</el-input>
</el-col>
6 根据id删除商品数据
<el-button type="danger" icon="el-icon-delete" size="mini" @click="removeById(scope.row.goods_id)"></el-button>
methods:{
// 根据ID删除商品
async removeById(id) {
const confirmResult = await this.$confirm(
"此操作将永久删除该用户, 是否继续?",
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}
).catch(err => err)
if (confirmResult !== "confirm") {
return this.$message.info("已取消删除")
}
const { data: res } = await this.$http.delete("goods/" + id)
if (res.meta.status != 200) {
return this.$message.error("删除商品失败!")
}
this.$message.success("删除商品成功!")
this.getGoodsList()
}
}
7 通过编程式导航跳转到商品添加页面
//List.vue
<el-button type="primary" @click="goAddpage">添加商品</el-button>
methods:{ goAddpage() { this.$router.push('/goods/add') }} // 跳转到添加页面
//goods下新建Add.vue
//index.js(router下)
import Add from '@/components/goods/Add'
{ path: '/goods/add', component: Add }
8 渲染添加页面的基本结构
<!-- 面包屑导航区 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<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>
<el-alert title="添加商品信息" type="info" center show-icon :closable="false">
</el-alert>
<el-steps :space="200" :active="1" finish-status="success">
<el-step title="已完成"></el-step>
<el-step title="进行中"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
</el-card>
//element.js
import { Steps,Step } from 'element-ui'
Vue.use(Steps)
Vue.use(Step)
9 实现步骤条和tab栏的数据联动效果
<el-steps :space="200" :active="activeIndex" 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>
data() { return { activeIndex:0 } },
//global.css
.el-steps{ margin: 15px 0; }
.el-step__title{ font-size: 13px; }
10 绘制(基本信息)面板的UI结构
11 绘制商品分类的级联选择器
共用同一个数据项
<el-steps :active="activeIndex-0" > //这里需要的是数值型的 (字符串 -0 转数值)
<!-- tab栏区 -->
<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
<el-tabs :tab-position="'left'" v-model="activeIndex">
<el-tab-pane label="基本信息">
<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
:options="catelist"
:props="{expandTrigger: 'hover', label: 'cat_name', value: 'cat_id', children: 'children' }"
v-model="addForm.goods_cat"
@change="handleChange">
</el-cascader>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="配置管理">配置管理</el-tab-pane>
<el-tab-pane label="商品属性">商品属性</el-tab-pane>
<el-tab-pane label="商品图片">商品图片</el-tab-pane>
<el-tab-pane label="商品图片">商品图片</el-tab-pane>
</el-tabs>
</el-form>
data() {
return {
// 激活项
activeIndex: "0",//需要提供默认绑定的的字符串
// 添加商品的数据对象
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" }]
},
// 商品分类列表
catelist:[]
}
},
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() {
//console.log(this.addForm.goods_cat)
if (this.addForm.goods_cat.length !== 3) {
this.addForm.goods_cat = []
}
}
}
12 阻止页签切换
<el-tabs :before-leave="beforeTabLeave">
methods:{
beforeTabLeave(activeName, oldActiveName) {
if (oldActiveName === "0" && this.addForm.goods_cat.length !== 3) {
this.$message.error("请先选择商品分类!")
return false
}
}
}
13 绘制(商品参数)面板中的复选框组
<el-tabs @tab-click="tabClicked">
<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>
data() { return { manyTableData:[] } },
computed: {
// 当前选中的三级分类的id
cateId() {
if (this.addForm.goods_cat.length === 3) { return this.addForm.goods_cat[2] }
return null
}
},
methods:{
//tab发生改变时
async tabClicked() {
// console.log(this.activeIndex);
// 访问的是动态参数面板
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 ? item.attr_vals.split(",") : [] })
this.manyTableData = res.data
}
}
}
.el-checkbox{ margin: 0 10px 0 0 !important; } //对齐
//element.js
import { CheckboxGroup,Checkbox } from 'element-ui'
Vue.use(CheckboxGroup)
Vue.use(Checkbox)
14 渲染(商品属性)面板的UI结构
<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>
data() { return { onlyTableData:[] } },
methods:{
//tab发生改变时
async tabClicked() {
// 访问的是静态属性面板
else if (this.activeIndex === "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.data
}
15 (商品图片)使用upload上传组件
由于Element-UI中的upload组件并没有调用axios,于是需要主动指定请求头中Authorization字段。
<el-tab-pane label="商品图片" name="3">
// <!-- action 表示图片要上传到的后台API地址 -->
<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-dialog title="预览图片":visible.sync="previewDialogVisible" width="50%">
<img :src="previewPath" alt="" class="previewImg" >
</el-dialog>
data() {
return {
// 添加商品的数据对象
addForm: {
....
pics:[] // 图片数组
},
// 上传文件的后台地址
uploadURL:'http://119.23.53.78:8888/api/private/v1/upload'
// 图片上传组件的headers请求头对象
headerObj:{ Authorization: window.sessionStorage.getItem('token') }
// 预览图片的路径
previewPath:'',
// 预览图片对话框显示与隐藏
previewDialogVisible:false
}
}
methods: {
// 处理图片预览效果
handlePreview(file) {
this.previewPath = file.response.data.url
this.previewDialogVisible = true
},
// 处理移除图片操作
handleRemove(file) {
console.log(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)
console.log(this.addForm)
},
// 监听图片上传成功的事件
handleSuccess(response) {
// console.log(response);
// 1. 拼接得到一个图片信息对象
const picInfo = { pic: response.data.tmp_path }
// 2. 将图片信息对象,push 到pics数组中
this.addForm.pics.push(picInfo)
// console.log(this.addForm);
}
}
}
.previewImg{ width: 100%; }
//element.js
import { Upload } from 'element-ui'
Vue.use(Upload)
16 (商品内容)富文本编辑器
安装并配置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)
//Add.vue
<el-tab-pane label="商品内容" name="4">
// <!-- 富文本编辑器组件 -->
<quill-editor v-model="addForm.goods_introduce"></quill-editor>
// <!-- 添加商品按钮 -->
<el-button type="primary" style="margin-top:15px" @click="add">添加商品</el-button>
</el-tab-pane>
data() {
return { addForm: { goods_introduce:'' } // 商品详情描述
},
methods:{
//添加商品
add() { console.log(this.addForm); }
}
//global.css
.ql-editor{ min-height: 300px; }
实现表单数据的预验证
完成商品添加操作
import _ from 'lodash'
//添加商品
add() {
// console.log(this.addForm);
this.$refs.addFormRef.validate(async valid => {
if (!valid) { return this.$message.error("请填写必要的表单项!") }
// 执行添加业务
//lodash cloneDeep(obj)
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
// console.log(form);
// 发起请求添加商品 商品的名称,必须是唯一的
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")
})
}
提交码云
git branch #(goods_list)
git status
git add .
git commit -m "完成了商品功能开发"
git push
git checkout master
git branch #(master)
git merge goods_list
git push