您的位置:首页 > 健康 > 养生 > Vue2 基础八电商后台管理项目——中

Vue2 基础八电商后台管理项目——中

2024/10/7 6:41:37 来源:https://blog.csdn.net/jiuchabaikaishui/article/details/140047254  浏览:    关键词:Vue2 基础八电商后台管理项目——中

代码下载

商品分类页

新商品分类组件 goods/Cate.vue,在router.js中导入子级路由组件 Cate.vue,并设置路由规则。

绘制商品分类基本结构

在Cate.vue组件中添加面包屑导航以及卡片视图中的添加分类按钮:

<template><div><!-- 面包屑导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home/welcome' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>商品分类</el-breadcrumb-item></el-breadcrumb><!-- 卡片 --><el-card><!-- 添加 --><el-button type="primary" @click="showAddCateDialog">添加分类</el-button><!-- 分类列表 --><tree-table class="tree-table" :data="catelist" :columns="columns" :selection-type = "false" :expand-type="false" :show-index="true" :index-text="'#'" border :show-row-hover="false"><template slot="isOk" slot-scope="scope"><i class="el-icon-error" style="color: red;" v-if="scope.row.cat_deleted"></i><i class="el-icon-success" style="color: lightgreen;" v-else></i></template><template slot="order" slot-scope="scope"><el-tag v-if="scope.row.cat_level === 0">一级</el-tag><el-tag type="success" v-else-if="scope.row.cat_level === 1">二级</el-tag><el-tag type="warning" v-else>三级</el-tag></template><template slot="opt" 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" @click="removeCateById(scope.row.cat_id)">删除</el-button></template></tree-table><!-- 分页 --><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="queryInfo.pagenum":page-sizes="[2, 3, 5, 10]":page-size="queryInfo.pagesize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></el-card><!-- 添加分类对话框 --><el-dialog title="添加商品分类" :visible.sync="addCateDialogVisible" width="50%" @close="addCateDialogClosed"><el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRef" label-width="100px"><el-form-item label="分类名称:" prop="cat_name"><el-input v-model="addCateForm.cat_name"></el-input></el-form-item><el-form-item label="父级分类:"><el-cascader v-model="selectedKeys" :options="parentCateList" :props="cascaderProps" @change="parentCateChange" clearable></el-cascader></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addCateDialogVisible = false">取 消</el-button><el-button type="primary" @click="addCate">确 定</el-button></span></el-dialog></div>
</template>

主要功能实现

<script>
export default {data() {return {catelist: [], queryInfo: {type: 3,pagenum: 1,pagesize: 5},total: 0,columns: [{label: '分类名称',prop: 'cat_name'},{label: '是否有效',// 列类型,可选值有 'template'(自定义列模板)type: 'template',// 列类型为 'template'(自定义列模板) 时,对应的作用域插槽(它可以获取到 row, rowIndex, column, columnIndex)名称template: 'isOk'},{label: '排序',type: 'template',template: 'order'},{label: '操作',type: 'template',template: 'opt'}],addCateDialogVisible: false,addCateForm: {cat_pid: 0,cat_name: '',cat_level: 0},// 添加商品分类表单验证addCateFormRules: {cat_name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]},parentCateList: [],cascaderProps: {expandTrigger: 'hover',checkStrictly: true,value: 'cat_id',label: 'cat_name',children: 'children'},selectedKeys: []}},created() {this.getCateList()},methods: {async getCateList() {const { data: res } = await this.$http.get('categories', { params: this.queryInfo })if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.catelist = res.data.resultthis.total = res.data.totalthis.$msg.success('商品分类获取成功')},handleSizeChange(size) {console.log('size: ', size);this.queryInfo.pagesize = sizeconst maxN = parseInt(this.total / this.queryInfo.pagesize + '') + (this.total % this.queryInfo.pagesize > 0 ? 1 : 0)this.queryInfo.pagenum = this.queryInfo.pagenum > maxN ? maxN : this.queryInfo.pagenumthis.getCateList()},handleCurrentChange(page) {console.log('page: ', page);this.queryInfo.pagenum = pagethis.getCateList()},// 展示添加商品分类对话框async showAddCateDialog() {// 获取父级分类const { data: res } = await this.$http.get('categories', { params: { type: 2 } })if (res.meta.status !== 200) return this.$msg.error(res.meta.error)this.parentCateList = res.datathis.addCateDialogVisible = true},// 关闭添加商品分类对话框addCateDialogClosed() {this.$refs.addCateFormRef.resetFields()this.addCateForm.cat_pid = 0this.addCateForm.cat_level = 0this.addCateForm.cat_name = ''this.selectedKeys = []},// 添加商品分类async addCate() {// 验证表单this.$refs.addFormRef.validate(async valid => {if (!valid) return this.$msg.error('请填写正确的分类名称')const { data: res } = await this.$http.post('categories', this.addCateForm)if (res.meta.status !== 201) return this.$msg.error(res.meta.msg)this.$msg.success('添加商品分类成功')this.getCateList()// 关闭对话框,重置数据this.addCateDialogVisible = false})},parentCateChange(v) {console.log('change: ', v);// 处理父分类id和分类级别if (this.selectedKeys.length > 0) {this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]this.addCateForm.cat_level = this.selectedKeys.length} else {this.addCateForm.cat_pid = 0this.addCateForm.cat_level = 0}},async removeCateById(uid) {const confirm = await this.$confirm('此操作将永久删除该分类, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(e => e);// 如果用户确认删除,则返回值为字符串 confirm;如果用户取消了删除,则返回值为字符串 cancelconsole.log('confirm: ', confirm);if (confirm !== 'confirm') returnconst { data: res } = await this.$http.delete('categories/' + uid)if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('删除商品分类成功')if (this.queryInfo.pagenum > 1 && this.catelist.length === 1) this.queryInfo.pagenum -= 1this.getCateList()}}
}
</script>

1、请求分类数据,请求分类数据并将数据保存在data中

2、使用插件展示数据,使用第三方插件 vue-table-with-tree-grid 展示分类数据:

  • 在vue 控制台中点击依赖->安装依赖->运行依赖->输入vue-table-with-tree-gird->点击安装
  • 打开main.js,导入vue-table-with-tree-grid import TreeTable from 'vue-table-with-tree-grid',并全局注册组件 Vue.component('tree-table', TreeTable)

3、自定义数据列,使用vue-table-with-tree-grid定义模板列并添加自定义列

4、完成分页功能

5、完成添加分类,添加级联菜单显示父级分类。先导入Cascader组件,并注册;然后添加使用级联菜单组件

参数管理页

只允许给三级分类内容设置参数,参数分为动态参数和静态参数属性。

添加 goods/Params.vue 子组件,并在router.js中引入该组件并设置路由规则。

绘制参数管理基本结构

完成Params.vue组件的基本布局,其中警告提示信息使用了el-alert,在element.js引入该组件并注册:

<template><div><!-- 面包屑导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home/welcome' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>参数列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片 --><el-card><el-alert title="注意:只允许为第三级分类设置相关参数!" type="warning" show-icon :closable="false"></el-alert><!-- 商品分类 --><div class="cat_opt"><span>请选择商品分类:</span><el-cascader v-model="selectedCateKeys" :options="cateList" :props="cateProps"  @change="handleChange" clearable></el-cascader></div><el-tabs v-model="activeName" @tab-click="handleTabClick"><el-tab-pane label="动态参数" name="many"><el-button type="primary" size="mini" :disabled="selectedCateKeys.length!==3" @click="addDialogVisible = true">添加参数</el-button><!-- 动态参数列表 --><el-table :data="manyTableData" style="width: 100%" border stripe><el-table-column type="expand"><template slot-scope="scope"><el-tag v-for="(v, i) in scope.row.attr_vals" :key="i" closable @close="handleClose(scope.row, i)">{{v}}</el-tag><el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"></el-input><el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button></template></el-table-column><el-table-column type="index" label="#"></el-table-column><el-table-column  prop="attr_name" label="属性名称"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.attr_id)">编辑</el-button><el-button type="danger" icon="el-icon-delete" size="mini" @click="removeParams(scope.row.attr_id)">删除</el-button></template></el-table-column></el-table></el-tab-pane><el-tab-pane label="静态属性" name="only"><el-button type="primary" size="mini" :disabled="selectedCateKeys.length!==3" @click="addDialogVisible = true">添加属性</el-button><!-- 静态属性列表 --><el-table :data="onlyTableData" style="width: 100%" border stripe><el-table-column type="expand"><template slot-scope="scope"><el-tag v-for="(v, i) in scope.row.attr_vals" :key="i" closable @close="handleClose(scope.row, i)">{{v}}</el-tag><el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"></el-input><el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button></template></el-table-column><el-table-column type="index" label="#"></el-table-column><el-table-column  prop="attr_name" label="属性名称"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.attr_id)">编辑</el-button><el-button type="danger" icon="el-icon-delete" size="mini" @click="removeParams(scope.row.attr_id)">删除</el-button></template></el-table-column></el-table></el-tab-pane></el-tabs></el-card><!-- 添加参数对话框 --><el-dialog :title="'添加' + titleText" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px"><el-form-item :label="titleText" prop="attr_name"><el-input v-model="addForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addDialogVisible = false">取 消</el-button><el-button type="primary" @click="addParams">确 定</el-button></span></el-dialog><!-- 编辑参数对话框 --><el-dialog :title="'编辑' + titleText" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"><el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px"><el-form-item :label="titleText" prop="attr_name"><el-input v-model="editForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editDialogVisible = false">取 消</el-button><el-button type="primary" @click="editParams">确 定</el-button></span></el-dialog></div>
</template>

主要功能实现

<script>
export default {data() {return {cateList: [],selectedCateKeys: [],cateProps: {expandTrigger: 'hover',value: 'cat_id',label: 'cat_name',children: 'children'},// 被激活的标签页名activeName: 'many',// 动态参数数据manyTableData: [],// 静态属性数据onlyTableData: [],// 是否展示添加参数对话框addDialogVisible: false,// 添加参数对话框数据addForm: {attr_name: ''},// 添加参数对话框验证规则addFormRules: {attr_name: [{ required: true, message: '请输入参数名称', trigger: 'blur' }]},// 是否展示编辑参数对话框editDialogVisible: false,// 编辑参数对话框数据editForm: {attr_name: ''},// 编辑参数对话框验证规则editFormRules: {attr_name: [{ required: true, message: '请输入参数名称', trigger: 'blur' }]}}},created() {this.getCateList()},methods: {// 获取分类数据async getCateList() {const { data: res } = await this.$http.get('categories', { params: { type: 3 } })if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('获取商品分类成功')this.cateList = res.data},// 获取参数列表数据async getParamsData() {const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`, { params: { sel: this.activeName } })if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('获取参数列表成功')// 将 attr_vals 转换为数组res.data.forEach(item => {item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []item.inputVisible = falseitem.inputValue = ''});if (this.activeName === 'many') this.manyTableData = res.datathis.onlyTableData = res.data},// 分类改变handleChange() {if (this.selectedCateKeys.length !== 3) {this.selectedCateKeys = []this.manyTableData = []this.onlyTableData = []return}console.log('change: ', this.selectedCateKeys);this.getParamsData()},// 点击标签页handleTabClick() {if (this.selectedCateKeys.length === 3) this.getParamsData()},// 关闭添加参数对话框addDialogClosed() {this.$refs.addFormRef.resetFields()},// 添加商品参数addParams() {// 验证表单this.$refs.addFormRef.validate(async valid => {if (!valid) return this.$msg.error('请填写正确的参数名称')const { data: res } = await this.$http.post(`categories/${this.cateId}/attributes`, { attr_name: this.addForm.attr_name,attr_sel: this.activeName})if (res.meta.status !== 201) return this.$msg.error(res.meta.msg)this.$msg.success(`添加${this.titleText}成功`)this.addDialogVisible = falsethis.getParamsData()})},// 展示编辑参数对话框async showEditDialog(attrId) {const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes/${attrId}`, { params: { attr_sel: this.activeName } })if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success(`获取${this.titleText}成功`)this.editForm = res.datathis.editDialogVisible = true},// 关闭编辑参数对话框editDialogClosed() {this.$refs.editFormRef.resetFields()},// 编辑商品参数editParams() {// 验证表单this.$refs.editFormRef.validate(async valid => {if (!valid) return this.$msg.error('请填写正确的参数名称')const { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${this.editForm.attr_id}`, { attr_name: this.editForm.attr_name,attr_sel: this.activeName})if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success(`添加${this.titleText}成功`)this.editDialogVisible = falsethis.getParamsData()})},// 删除商品参数async removeParams(attrId) {const confirm = await this.$confirm(`此操作将永久删除该${this.titleText}, 是否继续?`, '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(e => e);// 如果用户确认删除,则返回值为字符串 confirm;如果用户取消了删除,则返回值为字符串 cancelconsole.log('confirm: ', confirm);if (confirm !== 'confirm') returnconst { data: res } = await this.$http.delete(`categories/${this.cateId}/attributes/${attrId}`)if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success(`删除${this.titleText}成功`)this.getParamsData()},// 保存参数可选项async saveAttrVals(row, attrVals, isAdd) {const { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${row.attr_id}`, { attr_name: row.attr_name,attr_sel: this.activeName,attr_vals: attrVals})if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success(`${isAdd ? '添加' : '删除'}${this.titleText}可选项成功`)this.getParamsData()},// 删除参数可选项handleClose(row, i) {// 删除元素const attrVals = [...row.attr_vals.slice(0, i), ...row.attr_vals.slice(i + 1)].join(' ')console.log('attrVals: ', attrVals, '\ni: ', i);this.saveAttrVals(row, attrVals, false)},// 展示添加参数可选项输入框showInput(row) {row.inputVisible = true// 让文本框自动获得焦点// $nextTick 方法的作用,就是当页面上元素被重新渲染之后,才会指定回调函数中的代码this.$nextTick(_ => {this.$refs.saveTagInput.$refs.input.focus();});},// handleInputConfirm(row) {if (row.inputValue.trim().length === 0) {row.inputVisible = falserow.inputValue = ''return}// 添加元素const attrVals = row.attr_vals.concat(row.inputValue.trim()).join(' ')console.log('attrVals: ', attrVals);this.saveAttrVals(row, attrVals, true)}},// 计算属性computed: {// 选中的分类idcateId() {if (this.selectedCateKeys.length === 3) return this.selectedCateKeys[2]return null},// 动态计算标题的文本titleText() {if (this.activeName === 'many') {return '动态参数'}return '静态属性'}}
}
</script>

1、完成级联选择框,完成商品分类级联选择框

2、展示参数,展示动态参数数据以及静态属性数据

3、添加参数,完成添加参数或属性

4、编辑参数,完成编辑参数或属性

5、删除参数,删除参数或属性

6、动态参数和静态属性管理:

  • 展示动态参数可选项,动态参数可选项展示及操作在获取动态参数的方法中进行处理。
  • 添加/删除动态参数可选项
  • 展示静态属性可选项,静态属性可选项展示及操作在获取动态参数的方法中进行处理。
  • 添加/删除静态属性可选项

注意:当用户在级联选择框中选中了非三级分类时,需要清空表格中数据

商品列表页

添加 goods/List.vue 子组件,并在router.js中引入该组件并设置路由规则

绘制商品列表页基本结构

略,详情参考如下代码:

<template><div><!-- 面包屑导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home/welcome' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>商品列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片 --><el-card><!-- 搜索与添加 --><el-row :gutter="20"><el-col :span="8"><el-input placeholder="请输入内容" v-model.trim="queryInfo.query" clearable @clear="getGoodsList"><el-button slot="append" icon="el-icon-search" @click="queryGoodsList" :disabled="queryInfo.query ? false : true"></el-button></el-input></el-col><el-col :span="4"><el-button type="primary" @click="goAddPage">添加商品</el-button></el-col></el-row><!-- 用户列表 --><el-table :data="goodsList" style="width: 100%" border stripe><el-table-column type="index" label="#"></el-table-column><el-table-column  prop="goods_name" label="商品名称"></el-table-column><el-table-column prop="goods_price" label="商品价格(元)" width="95"></el-table-column><el-table-column prop="goods_weight" label="商品重量" width="70"></el-table-column><el-table-column prop="add_time" label="创建时间" width="140"><template slot-scope="scope">{{scope.row.add_time | dateFormatter}}</template></el-table-column><el-table-column label="操作" width="130"><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" @click="removeById(scope.row.goods_id)"></el-button></template></el-table-column></el-table><!-- 分页 --><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="queryInfo.pagenum":page-sizes="[2, 3, 5, 10]" :page-size="queryInfo.pagesize"layout="total, sizes, prev, pager, next, jumper":total="total" background></el-pagination><pre>{{goodsList}}</pre></el-card></div>
</template>

主要功能实现

<script>
export default {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.$msg.error(res.meta.msg)this.goodsList = res.data.goodsthis.total = res.data.totalthis.$msg.success('获取商品列表成功')},// 查询商品queryGoodsList() {this.queryInfo.pagenum = 1this.getGoodsList()},// pagesize 改变handleSizeChange(size) {console.log('size: ', size);this.queryInfo.pagesize = sizeconst maxN = parseInt(this.total / this.queryInfo.pagesize + '') + (this.total % this.queryInfo.pagesize > 0 ? 1 : 0)this.queryInfo.pagenum = this.queryInfo.pagenum > maxN ? maxN : this.queryInfo.pagenumthis.getGoodsList()},// 页码值 改变handleCurrentChange(num) {console.log('num: ', num);this.queryInfo.pagenum = numthis.getGoodsList()},// 删除商品async removeById(gid) {const confirm = await this.$confirm('此操作将永久删除该商品, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(e => e);// 如果用户确认删除,则返回值为字符串 confirm;如果用户取消了删除,则返回值为字符串 cancelconsole.log('confirm: ', confirm);if (confirm !== 'confirm') returnconst { data: res } = this.$http.delete('goods/' + gid)if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('删除商品成功')this.getGoodsList()},// 添加商品goAddPage() {console.log('goAddPage');this.$router.push('/home/addGoods')}}
}
</script>

1、数据展示,添加数据表格展示数据以及分页功能的实现,搜索功能的实现。在main.js中添加过滤器:

Vue.filter('dateFormatter', (ov) => {const date = new Date(ov)const y = date.getFullYear()const m = (date.getMonth() + 1 + '').padStart(2, '0')const d = (date.getDate() + '').padStart(2, '0')const hh = (date.getHours() + '').padStart(2, '0')const mm = (date.getHours() + '').padStart(2, '0')const ss = (date.getHours() + '').padStart(2, '0')return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

2、实现删除商品

3、添加商品,添加编程式导航,在List.vue中添加编程式导航,并创建添加商品路由组件及规则

添加商品页

添加 goods/Add.vue 子组件,并在router.js中引入该组件并设置路由规则。

绘制添加商品页基本结构

  • 布局过程中需要使用Steps组件,在element.js中引入并注册该组件,并在global.css中给组件设置全局样式
  • 其他略……
<template><div><!-- 面包屑导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home/welcome' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>添加商品</el-breadcrumb-item></el-breadcrumb><!-- 卡片 --><el-card><!-- 警告 --><el-alerttitle="添加商品信息"type="info"centershow-icon :closable="false"></el-alert><!-- 步骤条 --><el-steps :space="200" :active="activeIndex * 1" align-center finish-status="success"><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-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px" label-position="top"><el-tabs v-model="activeIndex" tab-position="left" :before-leave="beforeTabLeave" @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-cascader v-model="addForm.goods_cat" :options="cateList" :props="cateProps"  @change="handleChange" clearable></el-cascader></el-form-item></el-tab-pane><el-tab-pane label="商品参数" name="1"><el-form-item v-for="(v, i) in manyTableData" :key="i" :label="v.attr_name"><el-checkbox-group v-model="v.attr_vals"><el-checkbox v-for="(item, index) in v.attr_vals" :key="index" :label="item" border></el-checkbox></el-checkbox-group></el-form-item><pre>{{manyTableData}}</pre></el-tab-pane><el-tab-pane label="商品属性" name="2"><el-form-item v-for="(v, i) in onlyTableData" :key="i" :label="v.attr_name"><el-input v-model="v.attr_vals"></el-input></el-form-item><pre>{{onlyTableData}}</pre></el-tab-pane><el-tab-pane label="商品图片" name="3"><el-upload action="http://127.0.0.1:8888/api/private/v1/upload":on-preview="handlePreview":on-remove="handleRemove" :headers="headerObj" :on-success="handleSuccess"list-type="picture"><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" @blur="onEditorBlur"></quill-editor><el-button class="btnAdd" type="primary" @click="add">添加商品</el-button></el-tab-pane></el-tabs></el-form><!-- 预览对话框 --><el-dialogtitle="图片预览":visible.sync="previewVisible"width="50%"><img class="previewImg" :src="previewPath" alt=""></el-dialog></el-card></div>
</template>

主要功能实现

<script>
import _ from 'lodash';export default {data() {return {activeIndex: '0',addForm: {goods_name: '',goods_price: '',goods_weight: '',goods_number: '',goods_cat: [],// 商品图片pics: [],// 描述goods_introduce: '',// 参数attrs: []},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: 'change' }]},// 商品分类cateList: [],cateProps: {expandTrigger: 'hover',value: 'cat_id',label: 'cat_name',children: 'children'},// 动态参数列表manyTableData: [],// 静态属性列表onlyTableData: [],// 请求头headerObj: { Authorization: window.sessionStorage.getItem('token') },// 是否展示预览图previewVisible: false,// 预览图片路径previewPath: ''}},created() {this.getCateList()},methods: {// 获取分类数据async getCateList() {const { data: res } = await this.$http.get('categories', { params: { type: 3 } })if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('获取商品分类成功')this.cateList = res.data},// 选择分类handleChange(v) {if (this.addForm.goods_cat.length !== 3) {this.addForm.goods_cat = []return}console.log('handleChange value: ', v);},// 标签页钩子函数beforeTabLeave(ai, oai) {console.log('ai: ', ai, ', oai: ', oai);if (oai === '0' && this.addForm.goods_cat.length !== 3) {this.$msg.error('请选择商品分类')return false}},// 点击标签栏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) return this.$msg.error(res.meta.msg)this.$msg.success('获取动态参数成功')res.data.forEach(item => {item.attr_vals = item.attr_vals.length > 0 ? item.attr_vals.split(' ') : []});this.manyTableData = res.data} 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.$msg.error(res.meta.msg)this.$msg.success('获取动态参数成功')this.onlyTableData = res.data}},// 点击预览图片handlePreview(file) {console.log('handlePreview: ', file);this.previewPath = file.response.data.urlthis.previewVisible = true},// 点击删除图片handleRemove(file, fileList) {// console.log('handleRemove:\nfile: ', file, '\nfileList: ', fileList);const index = this.addForm.pics.findIndex(v => v.pic === file.response.data.tmp_path)if (index !== -1) this.addForm.pics.splice(index, 1)console.log('addForm: ', this.addForm);},// 上传文件成功handleSuccess(response, file, fileList) {// console.log('handleSuccess:\nresponse: ', response, '\nfile: ', file, '\nfileList: ', fileList);this.addForm.pics.push({ pic: response.data.tmp_path })console.log('addForm: ', this.addForm);},// 富文本编辑器失去焦点onEditorBlur() {console.log('content: ', this.addForm.goods_introduce);},// 添加商品add() {this.$refs.addFormRef.validate(async valid => {if (!valid) return this.$msg.error('请填写必要的表单项')// lodash 深拷贝const addForm = _.cloneDeep(this.addForm)// 处理分类数据addForm.goods_cat = addForm.goods_cat.join(',')// 处理动态参数this.manyTableData.forEach(item => {const newInfo = {attr_id: item.attr_id,attr_value: item.attr_vals.join(' ')}addForm.attrs.push(newInfo)});// 处理静态属性this.onlyTableData.forEach(item => {const newInfo = {attr_id: item.attr_id,attr_value: item.attr_vals}addForm.attrs.push(newInfo)});console.log('addForm: ', addForm);// 发起请求const { data: res } = await this.$http.post('goods', addForm)if (res.meta.status !== 201) this.$msg.error(res.meta.msg)this.$msg.success('添加商品成功')this.$router.push('/home/goods')})}},computed: {cateId() {if (this.addForm.goods_cat.length === 3) return this.addForm.goods_cat[2]return null}}
}
</script>

1、添加tab栏切换验证,也就是说不输入某些内容,无法切换到别的tab栏

2、展示信息,展示商品参数信息、商品属性信息,在商品参数信息展示中使用的el-checkbox,el-checkbox-group组件,打开element.js引入组件并注册组件

3、完成图片上传,使用upload组件完成图片上传,在element.js中引入upload组件,并注册。因为upload组件进行图片上传的时候并不是使用axios发送请求,所以需要手动为上传图片的请求添加token,即为upload组件添加headers属性

4、使用富文本插件,想要使用富文本插件vue-quill-editor,就必须先从依赖安装该插件,引入并注册vue-quill-editor

5、添加商品,完成添加商品的操作,在添加商品之前,为了避免goods_cat数组转换字符串之后导致级联选择器报错需要打开vue控制条,点击依赖,安装lodash,把addForm进行深拷贝

订单列表页

创建订单列表路由组件并添加路由规则。

绘制订单列表页基本结构

略,详情参考如下代码:

<template><div><!-- 面包屑导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home/welcome' }">首页</el-breadcrumb-item><el-breadcrumb-item>订单管理</el-breadcrumb-item><el-breadcrumb-item>订单列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片 --><el-card><!-- 搜索与添加 --><el-row :gutter="20"><el-col :span="8"><el-input placeholder="请输入内容" v-model.trim="queryInfo.query" clearable @clear="getOrderList"><el-button slot="append" icon="el-icon-search" @click="queryOrderList" :disabled="queryInfo.query ? false : true"></el-button></el-input></el-col></el-row><!-- 用户列表 --><el-table :data="orderList" style="width: 100%" border stripe><el-table-column type="index" label="#"></el-table-column><el-table-column  prop="order_number" label="订单编号"></el-table-column><el-table-column prop="order_price" label="订单价格"></el-table-column><el-table-column prop="order_pay" label="是否付款"><template slot-scope="scope"><el-tag type="danger" v-if="scope.row.pay_status === '0'">未付款</el-tag><el-tag type="success" v-else>已付款</el-tag></template></el-table-column><el-table-column prop="is_send" label="是否发货"></el-table-column><el-table-column prop="create_time" label="下单时间"><template slot-scope="scope">{{scope.row.create_time | dateFormatter}}</template></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button type="primary" icon="el-icon-edit" size="mini" @click="addressVisible = true"></el-button><el-button type="success" icon="el-icon-location" size="mini" @click="showProgressBox(scope.row.order_number)"></el-button></template></el-table-column></el-table><!-- 分页 --><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="queryInfo.pagenum":page-sizes="[2, 3, 5, 10]" :page-size="queryInfo.pagesize"layout="total, sizes, prev, pager, next, jumper":total="total" background></el-pagination><pre>{{orderList}}</pre></el-card><!-- 修改地址对话框 --><el-dialog title="添加商品分类" :visible.sync="addressVisible" width="50%" @close="addressDialogClosed"><el-form :model="addressForm" :rules="addressFormRules" ref="addressFormRef" label-width="120px"><el-form-item label="省市区/县:" prop="address1"><el-cascader v-model="addressForm.address1" :options="cityData" clearable></el-cascader></el-form-item><el-form-item label="详细地址:" prop="address2"><el-input v-model="addressForm.address2"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addressVisible = false">取 消</el-button><el-button type="primary" @click="addressConfirm">确 定</el-button></span></el-dialog><!-- 物流进度对话框 --><el-dialog title="物流进度" :visible.sync="progressVisible" width="50%"><el-timeline><el-timeline-itemv-for="(activity, index) in progressInfo":key="index":color="index === 0 ? '#00ff00' : ''":timestamp="activity.time">{{activity.context}}</el-timeline-item></el-timeline></el-dialog></div>
</template>

主要功能实现

<script>
import cityData from './citydata.js'export default {data() {return {queryInfo: {query: '',pagenum: 1,pagesize: 10},total: 0,// 订单列表orderList: [],// 省市区/县数据cityData,// 级联选择器属性cascaderProps: {expandTrigger: 'hover'},// 修改地址对话框addressVisible: false,addressForm: {address1: [],address2: ''},addressFormRules: {address1: [{ required: true, message: '请选择省市区县', trigger: 'change' }],address2: [{ required: true, message: '请填写详细地址', trigger: 'blur' }]},// 物流进度对话框progressVisible: false,// 物流进度数据progressInfo: []}},created() {this.getOrderList()},methods: {// 获取订单列表async getOrderList() {const { data: res } = await this.$http.get('orders', { params: this.queryInfo })if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('获取订单列表成功')this.orderList = res.data.goodsthis.total = res.data.total},// 查询订单列表queryOrderList() {this.queryInfo.pagenum = 1this.getOrderList()},// pagesize 改变handleSizeChange(size) {console.log('size: ', size);this.queryInfo.pagesize = sizeconst maxN = parseInt(this.total / this.queryInfo.pagesize + '') + (this.total % this.queryInfo.pagesize > 0 ? 1 : 0)this.queryInfo.pagenum = this.queryInfo.pagenum > maxN ? maxN : this.queryInfo.pagenumthis.getOrderList()},// 页码值 改变handleCurrentChange(num) {console.log('num: ', num);this.queryInfo.pagenum = numthis.getOrderList()},// 关闭修改地址对话框addressDialogClosed() {// console.log('addressForm: ', this.addressForm);this.$refs.addressFormRef.resetFields()},// 修改地址addressConfirm() {// console.log('addressForm: ', this.addressForm);this.addressVisible = false},// 展示物流进度对话框async showProgressBox(orderNumber) {// const { data: res } = await this.$http.get('kuaidi/' + orderNumber)// if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)// this.$msg.success('获取物流进度成功')// this.progressInfo = res.dataconst data = `[{"time": "2018-05-10 09:39:00","ftime": "2018-05-10 09:39:00","context": "已签收,感谢使用顺丰,期待再次为您服务","location": ""},{"time": "2018-05-10 08:23:00","ftime": "2018-05-10 08:23:00","context": "[北京市]北京海淀育新小区营业点派件员 顺丰速运 95338正在为您派件","location": ""},{"time": "2018-05-10 07:32:00","ftime": "2018-05-10 07:32:00","context": "快件到达 [北京海淀育新小区营业点]","location": ""},{"time": "2018-05-10 02:03:00","ftime": "2018-05-10 02:03:00","context": "快件在[北京顺义集散中心]已装车,准备发往 [北京海淀育新小区营业点]","location": ""},{"time": "2018-05-09 23:05:00","ftime": "2018-05-09 23:05:00","context": "快件到达 [北京顺义集散中心]","location": ""},{"time": "2018-05-09 21:21:00","ftime": "2018-05-09 21:21:00","context": "快件在[北京宝胜营业点]已装车,准备发往 [北京顺义集散中心]","location": ""},{"time": "2018-05-09 13:07:00","ftime": "2018-05-09 13:07:00","context": "顺丰速运 已收取快件","location": ""},{"time": "2018-05-09 12:25:03","ftime": "2018-05-09 12:25:03","context": "卖家发货","location": ""},{"time": "2018-05-09 12:22:24","ftime": "2018-05-09 12:22:24","context": "您的订单将由HLA(北京海淀区清河中街店)门店安排发货。","location": ""},{"time": "2018-05-08 21:36:04","ftime": "2018-05-08 21:36:04","context": "商品已经下单","location": ""}]`this.progressInfo = JSON.parse(data)this.progressVisible = true}}
}
</script>

1、实现数据展示及分页

2、制作省市区/县联动,制作 citydata.js 数据文件放到到components/order文件夹中,然后导入citydata.js文件

3、制作物流进度对话框,因为使用的是element-ui中提供的Timeline组件,所以需要导入并注册组件

数据统计页

创建数据统计路由组件并添加路由规则。

导入ECharts并使用,具体实现如下:

<template><div><!-- 面包屑导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home/welcome' }">首页</el-breadcrumb-item><el-breadcrumb-item>数据统计</el-breadcrumb-item><el-breadcrumb-item>数据报表</el-breadcrumb-item></el-breadcrumb><!-- 卡片 --><el-card><div id="main" style="width: 750px; height:400px;"></div></el-card></div>
</template><script>
// 只会导出(export default mutations)这个默认的对象返回
// import echarts from 'echarts'// 将 若干export导出的内容组合成一个对象返回
import * as echarts from 'echarts'import _ from 'lodash'export default {data() {return {options: {title: {text: '用户来源'},tooltip: {trigger: 'axis',axisPointer: {type: 'cross',label: {backgroundColor: '#E9EEF3'}}},grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},xAxis: [{boundaryGap: false}],yAxis: [{type: 'value'}]}}},async mounted() {const { data: res } = await this.$http.get('reports/type/1')if (res.meta.status !== 200) return this.$msg.error(res.meta.msg)this.$msg.success('获取折线图数据成功')// 初始化var myChart = echarts.init(document.getElementById('main'))// 设置配置项myChart.setOption(_.merge(this.options, res.data))}
}
</script>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com