1. 项目概述

1.1 电商项目基本业务概述

根据不同的应用场景,电商系统一般都提供了 PC 端、移动 APP、移动 Web、微信小程序等多种终端访问方式。

1.2 电商后台管理系统的功能

电商后台管理系统用于管理用户账号、商品分类、商品信息、订单、数据统计等业务功能。

1.3 电商后台管理系统的开发模式(前后端分离)

电商后台管理系统整体采用前后端分离的开发模式,其中前端项目是基于 Vue 技术栈的 SPA 项目。

1.4 电商后台管理系统的技术选型

1. 前端项目技术栈
  • Vue
  • Vue-router
  • Element-UI
  • Axios
  • Echarts
2. 后端项目技术栈
 
  • Node.js
  • Express
  • Jwt
  • Mysql
  • Sequelize

2. 项目初始化

2.1 前端项目初始化步骤

① 安装 Vue 脚手架
② 通过 Vue 脚手架创建项目
③ 配置 Vue 路由
④ 配置 Element-UI 组件库
⑤ 配置 axios 库
⑥ 初始化 git 远程仓库
⑦ 提交项目初始化版本
  1. 在终端命令行输入 vue ui
  2. http://localhost:8000/project/select 创建新项目;手动配置项目;选择功能 Babel,Router,Linter/Formatter,使用配置文件;Vue2.x,ESLint + Standard config,Lint on save
  3. http://localhost:8000/dashboard 点击插件,点击添加插件,查询vue-cli-plugin-element,选中并点击安装;配置插件,import on demand,点击完成安装
  4. 点击依赖,点击安装依赖,查询axios,选中并点击安装
  5. Gitee - 基于 Git 的代码托管和研发协作平台 申请账号;进行配置,点击头像->设置->SSH公钥(没有公钥,点击‘怎样生成公钥’,按教程生成);点击加号->新建仓库,按照提示创建仓库;在本地项目文件夹打开终端,输入git status查看本地仓库状态,若有文件未添加到暂存区,输入git add . 添加所有文件,再输入git commit -m "提示信息",做本地提交
  6. 在项目文件夹中打开终端,输入
    git remote add origin https://gitee.com/vsdeveloper/vue_shop.git
    git push -u origin master

2.2 后台项目环境安装配置

① 安装 MySQL 数据库
② 安装 Node.js 环境
③ 配置项目相关信息
④ 启动项目 npm install 安装所有的依赖包;node app.js 启动项目
⑤ 使用 Postman 测试后台项目接口是否正常

3. 登录/退出功能

3.1 登录概述

1. 登录业务流程
① 在登录页面输入用户名和密码
② 调用后台接口进行验证
③ 通过验证之后,根据后台的响应状态跳转到项目主页
2. 登录业务的相关技术点
  • http 是无状态的
  • 通过 cookie 在客户端记录状态
  • 通过 session 在服务器端记录状态
  • 通过 token 方式维持状态  ------  当前端项目与后台接口项目存在跨域问题时使用token

3.2 登录 — token 原理分析

3.3 登录功能实现

1. 登录组件布局
通过 Element-UI 组件实现布局
  • el-form
  • el-form-item
  • el-input
  • el-button
  • 字体图标

tips: 当开发一个新功能时,将这个新功能放到新的分支上进行开发,当该功能开发完成后,在合并到主分支上。1. 创建名为login的分支并切换到该分支 git checkout -b login;2. 查看当前分支 git branch

查看项目运行效果:点击任务->serve->运行->启动app

删除不要的组件:先启动项目,在删除代码,不然会报错

1. src->mian.js 是整个项目的入口文件;

2. app.vue 根组件 删除id='app'的div标签中的代码,将页面清空,script标签中仅留下export default {  name: 'app'},清空style标签

3. router.js 删除routes数组中的对象;清空views,components文件夹

创建登录组件:

1.在components文件夹新建Login.vue

2. 在router-》index.js文件中导入该组件

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'Vue.use(VueRouter)const routes = [{ path: '/', redirect: '/login' },{ path: '/login', component: Login }
]const router = new VueRouter({ routes })export default router
3. 在App.vue中添加路由占位符<router-view> ,这样,routes数组中的路由都会渲染到根组件中
4. 当用户访问'/'时,重定向到'/login'
5. 添加背景色
6. 添加less-loader,less依赖(开发依赖)
7. 此时背景色没有占满全屏,在asset目录下新建css文件夹,新建global.css全局样式表,在入口文件main.js中导入全局样式表
8. 登录界面中心盒子样式实现:位于屏幕中央

.login_box{width: 450px;height: 300px;background-color: #fff;border-radius: 3px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);
}

9. 头像样式

    .avatar_box{height: 130px;width: 130px;border: 1px solid #eee;border-radius: 50%;padding: 10px;box-shadow: 0 0 10px #ddd;position: absolute;left: 50%;transform: translate(-50%, -50%);background-color: #fff;img {width: 100%;height: 100%;border-radius: 50%;background-color: #eee;}}

10. 登录表单:使用element-ui ;由于该插件设置为按需导入,需要先在element.js中进行组件导入

import Vue from 'vue'
import { Button, Form, FormItem, Input } from 'element-ui'Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
            <!-- 登录表单 --><el-form label-width="0px" class="login_form"><!-- 用户名 --><el-form-item><el-input></el-input></el-form-item><!-- 密码 --><el-form-item><el-input type="password"></el-input></el-form-item><!-- 按钮区域 --><el-form-item class="btns"><el-button type="primary">登录</el-button><el-button type="info">重置</el-button></el-form-item></el-form>
.login_form {position: absolute;bottom: 0;width: 100%;padding: 0 20px;box-sizing: border-box;
}.btns {display: flex;justify-content: flex-end;
}

11. 带icon的输入框: 使用第三方矢量图标库:iconfont-阿里巴巴矢量图标库

                <!-- 用户名 --><el-form-item><el-input prefix-icon="iconfont icon-user"></el-input></el-form-item><!-- 密码 --><el-form-item><el-input prefix-icon="iconfont icon-3702mima"></el-input></el-form-item>

12. 登录组件表单的数据绑定Element - The world's most popular Vue UI framework -》典型表单

13. 登录表单组件的数据验证Element - The world's most popular Vue UI framework -》表单验证

14. 表单的重置:在<el-form>中添加ref属性以获取表单实例对象,为重置按钮绑定点击事件

From Methods -> resetFields

15. 登录前的预验证 From Methods -> validate

        // 登录login () {this.$refs.loginFormRef.validate(async (valid) => {if (!valid) returnconst { data } = await this.$http.post('login', this.loginForm) // data()中的loginFormconsole.log(data)if (data.meta.status !== 200) return console.log('登录失败')console.log('登录成功')})

16. 登录组件的弹窗配置element-ui -> Message 消息提示

if (data.meta.status !== 200) return this.$message.error('登录失败')
this.$message.success('登录成功')

2. 实现登录
① 通过 axios 调用登录验证接口
② 登录成功之后保存用户 token 信息
③ 跳转到项目主页
const {data: res } = await this.$http.post('login', this.loginForm)
if (res.meta.status !== 200)return this.$message.error('登录失败!')
// 提示登录成功
this.$message.success('登录成功!')
// 把登录成功的token保存到sessionStorage
window.sessionStorage.setItem('token', res.data.token)
// 使用编程式导航,跳转到后台主页
this.$router.push('/home')
3. 路由导航守卫控制访问权限
如果用户没有登录,但是直接通过 URL 访问特定页面,需要重新导航到登录页面。
// 为路由对象,添加 beforeEach 导航守卫
router.beforeEach((to, from, next) => {// 如果用户访问的登录页,直接放行if (to.path === '/login') return next()// 从 sessionStorage 中获取到 保存的 token 值const tokenStr = window.sessionStorage.getItem('token')// 没有token,强制跳转到登录页if (!tokenStr) return next('/login')next()
})
4. Vue 直接操作 DOM
  • 通过 ref 标注 DOM 元素

     // 在 DOM 元素上通过 ref 属性标注,属性名称自定义
    <div ref="info">hello</div>
  • 通过 $refs 获取 DOM 元素
    // 通过 Vue 实例的 $refs 获取标记 ref 属性的元素
    let info = this.$refs.info.innerHTML
    console.log(info) // hello

5. 基于 Element-UI 进行表单验证
Element-UI表单验证规则
loginFormRules: {// 登录名称的验证规则username: [{ required: true, message: '请输入用户名称', trigger: 'blur' }],password: [{ required: true, message: '请输入用户密码', trigger: 'blur' }]
}
// 进行表单验证
this.$refs.loginFormRef.validate(async valid => {// 如果验证失败,直接退出后续代码的执行if (!valid) return// 验证通过后这里完成登录成功后的相关操作(保存token、跳转到主页)
})

3.4 退出功能

基于 token 的方式实现退出比较简单,只需要销毁本地的 token 即可。这样,后续的请求就不会携带 token , 必须重新登录生成一个新的 token 之后才可以访问页面。
// 清空tokenwindow.sessionStorage.clear()// 跳转到登录页this.$router.push('/login')

提交登录功能代码:vscode中打开终端->git add .->git commit -m '完成了登录功能'->git branch (当前处于login分支)->  git checkout master-> git merge login -> git push->  git checkout login     -> git push -u origin login 将login分支推送到云端

4. 主页布局

4.1 整体布局

整体布局:先上下划分,再左右划分。

<el-container>
<!-- 头部区域 -->
<el-header></el-header>
<el-container>
<!-- 侧边栏区域 -->
<el-aside></el-aside>
<!-- 右侧主体区域 -->
<el-main></el-main>
</el-container>
</el-container>

取色工具:FastStone Capture中文网_专业截图、录屏软件,可滚动截图和屏幕录像

快捷键:Ctrl+L 复制十六进制颜色值

4.2 左侧菜单布局

菜单分为二级,并且可以折叠。

<el-menu>
<el-submenu>
<!-- 这个 template 是一级菜单的内容模板 -->
<i class="el-icon-menu"></i>
<span>一级菜单</span>
<!-- 在一级菜单中,可以嵌套二级菜单 -->
<el-menu-item>
<i class="el-icon-menu"></i>
<span slot="title">二级菜单</span>
</el-menu-item>
</el-submenu>
</el-menu>

4.3 通过接口获取菜单数据

通过 axios 请求拦截器添加 token,保证拥有获取数据的权限。
// axios请求拦截axios.interceptors.request.use(config => {// 为请求头对象,添加 Token 验证的 Authorization 字段config.headers.Authorization = window.sessionStorage.getItem('token')return config})

4.4 动态渲染菜单数据并进行路由控制

  • 通过 v-for 双层循环分别进行一级菜单和二级菜单的渲染
  • 通过路由相关属性启用菜单的路由功能
<el-menu router><el-submenu :index="item.id + ''" v-for=“item in menus" :key="item.id"><template slot="title"><span>{{item.authName}}</span></template><el-menu-item :index="'/' + subItem.path" v-for="subItem in item.children" :key="subItem.id" ><span slot="title">{{subItem.authName}}</span></el-menu-item></el-submenu>
</el-menu>

5. 用户管理

左侧菜单改造为路由链接:将<el-menu>标签的router属性设置为true;在<el-submenu>的index属性中设置路由链接

点击左侧菜单高亮:1. 点击菜单时,将对应的路由地址保存到sessionStorage中

2. 跳转至该链接时,将值赋值给<el-menu>的default-active属性

1.1 用户管理概述

通过后台管理用户的账号信息,具体包括用户信息的展示、添加、修改、删除、角色分配、账号启用/注销等功能。
  • 用户信息列表展示
  • 添加用户
  • 修改用户
  • 删除用户
  • 启用或禁用用户
  • 用户角色分配

1.2 用户信息列表展示

1. 用户列表布局
  • 面包屑导航 el-breadcrumb
  • Element-UI 栅格系统基本使用 el-row
  • 表格布局 el-table、el-pagination
2. 用户状态列和操作列处理
作用域插槽

<template slot-scope="scope">
<!-- 开关 -->
<el-switch v-model="scope.row.mg_state"
@change="stateChanged(scope.row.id, scope.row.mg_state)">
</el-switch>
</template>
3. 表格数据填充
  • 调用后台接口
  • 表格数据初填充

const { data: res } = await this.$http.get('users', { params: this.queryInfo })
if (res.meta.status !== 200) {
return this.$message.error('查询用户列表失败!')
}
this.total = res.data.total
this.userlist = res.data.users
4. 表格数据分页
分页组件用法:
① 当前页码:pagenum
② 每页条数:pagesize
③ 记录总数:total
④ 页码变化事件
⑤ 每页条数变化事件
⑥ 分页条菜单控制
<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>
5. 搜索功能
将搜索关键字,作为参数添加到列表查询的参数中。

<el-input
placeholder="请输入搜索的内容"
v-model="queryInfo.query"
clearable
@clear="getUserList">
<el-button slot="append"
icon="el-icon-search"
@click="getUserList"></el-button>
</el-input>

1.3 用户状态控制

1. 开关组件的用法
2. 接口调用更改用户的状态

<el-switch
v-model="scope.row.mg_state"
@change="stateChanged(scope.row.id, scope.row.mg_state)">
</el-switch>
async stateChanged(id, newState) {
const { data: res } = await this.$http.put(`users/${id}/state/${newState}`)
if (res.meta.status !== 200) {
return this.$message.error('修改状态失败!')
}
}

1.4 添加用户

1. 添加用户表单弹窗布局
  • 弹窗组件用法
  • 控制弹窗显示和隐藏
<el-dialog title="添加用户" :visible.sync="addDialogVisible" width="50%">
<el-form :model="addForm" label-width="70px">
<el-form-item label="用户名" prop="username">
<el-input v-model="addForm.username"></el-input>
</el-form-item>
<!-- 更多表单项 -->
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="resetAddForm">取 消</el-button>
<el-button type="primary" @click="addUser">确 定</el-button>
</span>
</el-dialog>
2. 表单验证 内置表单验证规则

<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" >
<!-- 表单 -->
</el-form>
addFormRules: {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
}
this.$refs.addFormRef.validate(async valid => {
if (!valid) return
})
自定义表单验证规则

const checkMobile = (rule, value, cb) => {
let reg = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/
if (reg.test(value)) {
cb()
} else {
cb(new Error('手机号码格式不正确'))
}
}
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ validator: checkMobile, trigger: 'blur' }
]
3. 表单提交
将用户信息作为参数,并调用后台接口添加用户。

this.$refs.addFormRef.validate(async valid => {
if (!valid) return
const { data: res } = await this.$http.post('users', this.addForm)
if (res.meta.status !== 201) {
return this.$message.error('添加用户失败!')
}
this.$message.success('添加用户成功!')
this.addDialogVisible = false
this.getUserList()
})

1.5 修改用户

1. 根据 ID 查询用户信息

<el-button type="primary" size="mini" icon="el-icon-edit"
@click="showEditDialog(scope.row.id)"></el-button>
async showEditDialog(id) {
const { data: res } = await this.$http.get('users/' + id)
if (res.meta.status !== 200) {
return this.$message.error('查询用户信息失败!')
}
// 把获取到的用户信息对象,保存到 编辑表单数据对象中
this.editForm = res.data
this.editDialogVisible = true
}
2. 编辑提交表单

this.$refs.editFormRef.validate(async valid => {
if (!valid) return
// 发起修改的请求
const { data: res } = await this.$http.put('users/' + this.editForm.id, {
email: this.editForm.email,
mobile: this.editForm.mobile
})
if (res.meta.status !== 200) {
return this.$message.error('编辑用户信息失败!')
}
this.$message.success('编辑用户信息成功!')
this.getUserList()
this.editDialogVisible = false
})

1.6 删除用户

<el-button type="danger" size="mini" icon="el-icon-delete" @click="remove(scope.row.id)"></el-button>
async remove(id) {// 询问是否要删除const confirmResult = await this.$confirm('此操作将永久删除该用户, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(err => err)const { data: res } = await this.$http.delete('users/' + id)if (res.meta.status !== 200) return this.$message.error('删除用户失败!')this.$message.success('删除用户成功!')this.getUserList()},

6.权限管理

6.1 权限管理业务分析

通过权限管理模块控制不同的用户可以进行哪些操作,具体可以通过角色的方式进行控制,即每个用户分配 一个特定的角色,角色包括不同的功能权限。

6.2 权限列表展示

 <template slot-scope="scope"><el-tag size="small" v-if="scope.row.level == 0">一级</el-tag><el-tag type="success" size="small" v-else-if="scope.row.level == 1">二级</el-tag><el-tag type="warning" size="small" v-else>三级</el-tag></template>
// 获取权限列表数据async getRightsList() {const { data: res } = await this.$http.get('rights/list')if (res.meta.status !== 200) {return this.$message.error('获取权限列表失败!')}this.rightsList = res.data}

6.3 角色列表展示

// 获取所有角色列表async getRolesList() {const { data: res } = await this.$http.get('roles')if (res.meta.status !== 200) {return this.$message.error('获取角色列表失败!')}this.rolesList = res.data}

6.4 用户角色分配

1. 展示角色对话框

  1. 实现用户角色对话框布局
  2. 控制角色对话框显示和隐藏
  3. 角色对话框显示时,加载角色列表数据
async showSetRoleDialog(userInfo) {this.userInfo = userInfo// 发起请求,获取所有角色的列表const { data: res } = await this.$http.get('roles')if (res.meta.status !== 200) {return this.$message.error('获取角色列表失败!')}this.rolesList = res.datathis.setRoleDialogVidible = true}

2. 完成角色分配功能

async saveNewRole() {if (this.selectedRoleId === '') {return this.$message.error('请选择新角色后再保存!')}const { data: res } = await this.$http.put(`users/${this.userInfo.id}/role`, {rid: this.selectedRoleId})if (res.meta.status !== 200) {return this.$message.error('分配角色失败!')}this.$message.success('分配角色成功!')this.getUserList()this.setRoleDialogVidible = false}

6.5 角色权限分配

1. 表格行展开效果

通过 el-table-column 组件的 type =“expand” 方式实现表格行展开效果
<el-table :data="rolesList" border stripe><!-- 展开行的列 --><el-table-column type="expand"><template slot-scope="scope"><!-- 展开行内容填充 --></template></el-table-column>
</el-table>

2. 渲染一级权限菜单

在表格展开行中渲染一级菜单
<el-row v-for="(item1, i1) in scope.row.children" :key="item1.id" class="centerRow"><!-- 这一列,专门渲染 一级权限 --><el-col :span="5"><el-tag closable>{{item1.authName}}</el-tag><i class="el-icon-caret-right"></i></el-col><!-- 还剩余 19 列,分配给二三级权限 --><el-col :span="19"><!-- 这里显示二三级权限 --></el-col>
</el-row>

3. 渲染二、三级权限菜单

在表格展开行中渲染二、三级菜单
<el-row v-for="(item2, i2) in item1.children" :key="item2.id" class="centerRow"><!-- 放二级权限 --><el-col :span="6"><el-tag closable type="success">{{item2.authName}}</el-tag><i class="el-icon-caret-right"></i></el-col><!-- 放三级权限 --><el-col :span="18"><el-tag closable type="warning" v-for="item3 in item2.children" :key="item3.id"> {{item3.authName}}</el-tag></el-col>
</el-row>

4. 删除角色下的权限

点击权限菜单的删除按钮后,调用后台接口删除对应权限和其下的子权限。
<el-col :span="5"><el-tag closable @close="removeRight(scope.row, item1.id)">{{item1.authName}}</el-tag><i class="el-icon-caret-right"></i>
</el-col>
const { data: res } = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)if (res.meta.status !== 200) {return this.$message.error('删除权限失败!')}this.$message.success('删除权限成功!')

5. 给角色分配权限流程

  1. 实现角色分配权限对话框布局
  2. 控制对话框的显示和隐藏
  3. 对话框显示时调用后台接口加载权限列表数据
  4. 完成树形权限菜单的展示
  5. 选中默认的权限
  6. 保存选中的权限,调用后台接口完成角色权限的分配

6. 实现权限分配对话框布局

 <!-- 分配权限的对话框 --><el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="resetSetRightDialog"><!-- 权限菜单 --><span slot="footer" class="dialog-footer"><el-button @click="setRightDialogVisible = false">取 消</el-button><el-button type="primary" @click="saveRight">确 定</el-button></span></el-dialog>

7. 渲染权限的树形结构

<el-tree ref="tree" :data="rightTree" :props="treeConfig" show-checkbox node-key="id" default-expand-all :default-checked-keys="defaultCheckedKeys"></el-tree>
 // 在展示对话框之前,先获取到权限的树形结构数据const { data: res } = await this.$http.get('rights/tree')if (res.meta.status !== 200) return this.$message.error('初始化权限失败!')// 把权限的树形结构数据,保存到data中,供页面渲染使用this.rightTree = res.data

8. 设置默认权限菜单选中

// 根据指定的节点和keys数组,递归获取所有三级节点的IdgetLeafIds(node, keys) {if (!node.children) {keys.push(node.id)} else {node.children.forEach(item => this.getLeafIds(item, keys))}}
 const keys = [] // 专门存放所有三级节点的Idthis.getLeafIds(role, keys)this.defaultCheckedKeys = keys

9. 完成角色授权

// 获取树形控件中,所有半选和全选节点的Id数组const arr1 = this.$refs.tree.getCheckedKeys()const arr2 = this.$refs.tree.getHalfCheckedKeys()const rids = [...arr1, ...arr2].join(',')
const { data: res } = await this.$http.post(`roles/${this.selectedRoleId}/rights`, { rids })if (res.meta.status !== 200) {return this.$message.error('分配权限失败!')}
this.$message.success('分配权限成功!')

7. 分类管理

7.1 商品分类概述

商品分类用于在购物时,快速找到所要购买的商品,可以通过电商平台主页直观的看到。

7.2 商品分类列表

实现基本布局与数据获取
 const { data: res } = await this.$http.get('categories', { params: this.queryInfo })if (res.meta.status !== 200) {return this.$message.error('获取商品分类失败!')}this.cateList = res.data.resultthis.total = res.data.total

7.3 树形表格

1.第三方树形表格的基本使用

安装依赖包 (地址: https://github.com/MisterTaki/vue-table-with-tree-grid)
 npm i vue-table-with-tree-grid -S

基本使用

 import Vue from 'vue'import ZkTable from 'vue-table-with-tree-grid'Vue.use(ZkTable)

2. 实现分类树形列表

<tree-table :data="cateList" :columns="columns" border :selection-type="false"
:expand-type="false" show-index index-text="#" class="tree-table"><!-- 操作的模板列 --><!-- 排序的模板列 --><!-- 是否有效的模板列 --><template slot="isok" slot-scope="scope"><i class="el-icon-success" style="color:#20B2AA;" v-if="scope.row.cat_deleted
=== false"></i><i class="el-icon-error" style="color:#F92672;" v-else></i></template>
</tree-table>

7.4 分页功能

 <!-- 分页区域 --><el-pagination @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-size="queryInfo.pagesize" layout="total, prev, pager, next, jumper" :total="total"></el-pagination>

7.5 添加分类

1. 实现分类树形列表

<el-dialog title="添加分类" :visible.sync="addDialogVisible" width="50%" @close="resetForm"><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px"><el-form-item label="分类名称:" prop="cat_name"><el-input v-model="addForm.cat_name"></el-input></el-form-item><el-form-item label="父级分类:"><!-- 分类菜单 --></el-form-item></el-form>
</el-dialog>

2. 实现分类级联菜单效果

<el-cascader expand-trigger="hover" :options="parentCateList" :props="cascaderConfig" v-model="selectedCateList" @change="handleChange" change-on-select clearable>
</el-cascader>
// 先获取所有父级分类的数据列表const { data: res } = await this.$http.get('categories', { params: { type: 2 } })if (res.meta.status !== 200) return this.$message.error('获取父级分类失败!')// 把父级分类数据,挂载到data中this.parentCateList = res.data

3. 控制父级分类的选择

父级分类选择时,获取对应的分类 id

 handleChange() {if (this.selectedCateList.length === 0) {// 证明没有选中任何父级分类this.addForm.cat_pid = 0this.addForm.cat_level = 0} else {// 选中父级分类this.addForm.cat_pid = this.selectedCateList[this.selectedCateList.length - 1]// 设置分类等级this.addForm.cat_level = this.selectedCateList.length}}

4. 完成分类添加

将分类名称、分类等级和父分类 id 提交到后台,完成分类添加。
const { data: res } = await this.$http.post('categories', this.addForm)if (res.meta.status !== 201) {return this.$message.error('添加分类失败!')}this.$message.success('添加分类成功!')

8. 参数管理

8.1 参数管理概述

商品参数用于显示商品的固定的特征信息,可以通过电商平台商品详情页面直观的看到。

8.2 商品分类选择

1. 选择商品分类

  1. 页面基本布局
  2. 加载商品分类数据
  3. 实现商品分类的级联选择效果
// 获取所有商品的分类列表async getAllCateList() {const { data: res } = await this.$http.get('categories')if (res.meta.status !== 200) {return this.$message.error('获取商品分类列表失败!')}this.cateList = res.data}

2. 控制级联菜单分类选择

  • 只允许选择三级分类
cascaderChanged() {if (this.selectedCateList.length !== 3) {// 没有选中三级分类,把分类重置为空this.selectedCateList = []this.manyTableData = []this.onlyTableData = []} else {// 选中了三级分类后,获取该分类对应的参数列表数据this.getParamsList()}}
  • 通过计算属性的方式获取分类 ID
cateId() {if (this.selectedCateList.length === 3) {return this.selectedCateList[this.selectedCateList.length - 1]} else {return null}}

8.3 实现参数列表

1. 根据选择的商品分类加载对应的参数数据

  • 参数列表布局
  • 根据分类 id 加载参数列表数据
// 获取所有商品的分类列表const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`, {params: { sel: this.activeName }})

2. 处理标签数据格式

将字符串形式的数据分隔为数组。
res.data.forEach(item => {// 把字符串的可选项,分割为数组,重新赋值给 attr_valsitem.attr_vals = item.attr_vals.length > 0 ? item.attr_vals.split(‘,') : []})

3. 控制添加标签文本框的显示

$nextTick 的执行时机: DOM 更新完毕之后
<el-button size="small" v-else @click="showTagInput(scope.row)">+ New Tag</el-button>
showTagInput(row) {row.tagInputVisible = true// 当我们修改了 data 中 tagInputVisible 的值以后,如果要操作文本框,必须等页面重新渲染完毕之后才可以,所以,必须把操作文本框的代码放到 $nextTick 中,当作回调去执行($nextTick 的执行时机,是在DOM 更新完毕之后)this.$nextTick(() => {this.$refs.saveTagInput.$refs.input.focus()})}

4. 实现标签动态添加的文本框控制逻辑

  • 控制标签输入域的显示和隐藏
  • 对输入的内容进行数据绑定
res.data.forEach(item => {// 把字符串的可选项,分割为数组,重新赋值给 attr_valsitem.attr_vals = item.attr_vals.length > 0 ? item.attr_vals.split(‘,') : []// 为每个数据行,添加自己的 tagInputVisible ,从而控制自己展开行中的输入框的显示与隐藏item.tagInputVisible = false// 把文本框中输入的值,双向绑定到 item.tagInputValue 上item.tagInputValue = ''})

5. 实现标签的添加和删除操作

添加标签和删除标签使用的是同一个接口,参数是一样的。
const { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${row.attr_id}`,{attr_name: row.attr_name,attr_sel: row.attr_sel,attr_vals: row.attr_vals.join(' ')})if (res.meta.status !== 200) {return this.$message.error('更新参数项失败!')}
this.$message.success('更新参数项成功!')

8.4 实现动态参数与静态属性添加

  • 动态参数与静态属性表单重用
  • 添加动态参数与静态属性使用的是同一个接口,参数是一样的
const { data: res } = await this.$http.post(`categories/${this.cateId}/attributes`, {// 参数的名称attr_name: this.addForm.attr_name,// 参数类型 many onlyattr_sel: this.activeName})if (res.meta.status !== 201) return this.$message.error('添加参数失败!')this.$message.success('添加参数成功!')

9. 商品管理

9.1 商品管理概述

商品管理模块用于维护电商平台的商品信息,包括商品的类型、参数、图片、详情等信息。通过商品管理模块可以实现商品的添加、修改、展示和删除等功能。

9.2 商品列表

  • 实现商品列表布局效果
  • 调用后台接口获取商品列表数据
const { data: res } = await this.$http.get('goods', { params: this.queryInfo })if (res.meta.status !== 200) {return this.$message.error('初始化商品列表失败!')}// 为商品列表赋值this.goodsList = res.data.goods// 为总数量赋值this.total = res.data.total

9.3 添加商品

1. 基本布局与分布条效果

  • 添加商品基本布局
  • 分布条组件用法
<el-steps :active="activeName-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>

2. 商品信息选项卡Tab布局效果

Tab 组件的基本使用
<el-tabs tab-position="left" v-model="activeName" :before-leave="beforeTabLeave"><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>

3. 商品基本信息

  • 商品基本信息表单布局
  • 表单数据绑定
  • 表单验证
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' }]
}

4. 商品分类信息

  • 商品分类布局
  • 商品分类数据加载
<el-cascader expand-trigger="hover" :options="cateList" :props="cascaderConfig"
v-model="addForm.goods_cat" @change="handleCascaderChange"></el-cascader>
const { data: res } = await this.$http.get('categories')if (res.meta.status !== 200) {return this.$message.error('初始化商品分类失败!')}this.cateList = res.data

5. 商品动态参数

  • 获取商品动态参数数据
  • 商品动态参数布局
const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`, {params: { sel: 'many' }})if (res.meta.status !== 200) return this.$message.error('获取动态参数列表失败!')// 把动态参数中的每一项数据中的 attr_vals,都从字符串分割为数组res.data.forEach(item => {item.attr_vals = item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')})this.manyData = res.data

6. 商品静态属性

  • 获取商品静态属性数据
  • 商品静态属性布局
const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`, {params: { sel: 'only' }})if (res.meta.status !== 200) {return this.$message.error('获取动态参数列表失败!')}this.onlyData = res.data

7. 商品图片上传

图片上传组件基本使用
<el-uploadaction="http://47.96.21.88:8888/api/private/v1/upload":headers="uploadHeaders":on-preview="handlePreview":on-remove="handleRemove":on-success="handleSuccess"list-type="picture"><el-button size="small" type="primary">点击上传</el-button>
</el-upload>

图片预览

// 预览图片时候,触发的方法handlePreview(result) {this.previewImgSrc = result.response.data.urlthis.previewVisible = true}

图片删除

// 当移除图片,会触发这个方法handleRemove(result) {// 根据 result.response.data.temp_path 从 addForm.pics 数组中,找到要删除那个对象的索引值const index = this.addForm.pics.findIndex(item => item.pic === result.response.data.tmp_path)// 根据索引删除对应的图片信息对象this.addForm.pics.splice(index, 1)}

完成图片上传

// 图片上传成功handleSuccess(result) {if (result.meta.status === 200) {// 把上传成功后,图片的临时路径,保存到 addForm.pics 数组中,作为对象来保存this.addForm.pics.push({pic: result.data.tmp_path})}}

8. 商品详情

富文本编辑器基本使用
// 安装vue-quill-editornpm install vue-quill-editor -S
import VueQuillEditor from 'vue-quill-editor'
Vue.use(VueQuillEditor)
<quill-editor v-model="addForm.goods_introduce"></quill-editor>

9. 完成商品添加

  • 处理商品相关数据格式
  • 调用接口完成商品添加
// 先处理好商品相关的数据格式,然后再提交
const newForm = _.cloneDeep(this.addForm)newForm.goods_cat = newForm.goods_cat.join(',')// 到此位置,商品相关数据已经准备好,可以提交了const { data: res } = await this.$http.post('goods', newForm)if (res.meta.status !== 201) return this.$message.error(res.meta.msg)this.$message.success('添加商品成功!')// 跳转到商品列表页this.$router.push('/goods/list')

10. 订单管理

10.1 订单管理概述

订单管理模块用于维护商品的订单信息,可以查看订单的商品信息、物流信息,并且可以根据实际的运营情况对订单做适当的调整。

10.2 订单列表

1. 订单列表展示

  • 订单数据加载
  • 订单列表布局
const { data: res } = await this.$http.get('orders', { params: this.queryInfo })if (res.meta.status !== 200) {return this.$message.error('获取订单列表失败!')}this.orderList = res.data.goodsthis.total = res.data.total

2. 查看订单地址信息

  • 省市区三级联动效果
  • 省市区数据格式分析
<el-cascader :options="cityOptions" v-model="selectedArea" @change="changeProvince" change-on-select style="width: 100%;">
</el-cascader>

3. 查看订单物流信息

  • 调用接口获取物流数据
  • 实现物流信息列表效果
 const { data: res } = await this.$http.get('/kuaidi/110121212622')if (res.meta.status !== 200) {return this.$message.error('获取物流进度失败!')}this.wlList = res.data

11. 数据统计

11.1 数据统计概述

数据统计模块主要用于统计电商平台运营过程的中的各种统计数据,并通过直观的可视化方式展示出来,方便相关运营和管理人员查看

11.2 用户来源数据统计报表

1. Echarts 第三方可视化库的基本使用

// 安装echarts库npm install echarts -S
// 导入echarts接口import echarts from 'echarts'

2. 实现用户来源数据统计报表

  • 调用接口获取后台接口数据
  • 通过echarts的api实现报表效果
 // 基于准备好的dom,初始化echarts实例var myChart = echarts.init(this.$refs.main)const { data: res } = await this.$http.get('reports/type/1')if (res.meta.status !== 200) return this.$message.error('初始化折线图失败!')const data = _.merge(res.data, this.options)// 绘制图表myChart.setOption(data)

Vue项目踩坑

1. eslint格式校验问题

报错:缩进应为2格而不是4格;文件的最后一行需要有一个空行; 组件名应为多个单词

由于安装格式化插件的原因,一保存就会报格式错误

解决方法:修改.editorconfig文件如下

修改.eslintrc.js文件如下

 2. 500 Inernet Server error

原因: 服务器端出错

解决办法:const 变量不能改变它的值,且必须赋初值

3. 400 Bad Request

原因:当服务器端返回的状态码不为2xx时,都会被axios拦截,不能接收

解决办法:在main.js中配置axios

    // 状态码 在 大于 500时才会reject
axios.defaults.validateStatus = function(status) {return status <= 500
}

4. element-ui cascader

问题1:级联选择器内容过长 超出屏幕

解决方案:global.css文件中添加

.el-cascader-menu {height: 300px;
}

问题二:默认只能选中级联选择器的最后一级

解决方案:在props对象中设置checkStrictly属性为true

<el-cascader :props=“{ checkStrictly: true }”> </el-cascader> //选择任意一级选项
<el-cascader :show-all-levels=“false”> </el-cascader> // 仅显示最后一级

电商后台管理---Vue实战相关推荐

  1. 电商后台管理---Vue项目运行问题记录

    初学vue项目的小白,记录在github下载电商管理项目运行过程中碰到的问题. mysql数据库安装 推荐使用Navicat Premium,可以管理大部分的数据库,推荐使用. 配好mysql数据库环 ...

  2. 电商项目总结java_Vue 电商后台管理项目阶段性总结(推荐)

    一.阶段总结 该项目偏向前端更多一点,后端 API 服务是已经搭建好了的,我们只需要用既可以,(但是作为一个 全栈开发人员,它的数据库的表设计,Restful API 的设计是我们要着重学习的!!!) ...

  3. scrm电商后台管理、公司管理、店铺管理、特卖活动、营销活动、订单管理、优惠券管理、商品管理、会员管理、标签管理、会员卡、签到配置、积分、拼团、砍价、快递配置、短信配置、充值、运营活动

    scrm电商后台管理.公司管理.店铺管理.特卖活动.营销活动.订单管理.优惠券管理.商品管理.会员管理.标签管理.会员卡.签到配置.积分.拼团.砍价.快递配置.短信配置.充值.运营活动 Axure原型 ...

  4. 超市百货电商app移动端原型+通用模块全局规则说明+超市电商后台管理web端原型+超市电商产品原型及需求文档+业务后台(商品管理+广告管理+活动管理)

    作品介绍:Axure原型内容主要包括:超市百货电商app移动端原型+文档变更记录+名词术语说明+产品业务功能框架+通用模块和全局规则说明(消息推送机制+输入提交规则+图片加载机制+权限类提示说明+搜索 ...

  5. 10.4 Vue电商后台管理 完善订单模块--用Tab切换对订单进行分类

    1. 效果如下: 2. 实现代码 四个el-tab-pane中,< el-table >是一样的: <el-tab-pane name="all" label=& ...

  6. Vue电商后台管理系统项目开发实战(一)

    前言 当下根据不同的应用场景,电商系统一般都提供了PC端,移动APP,移动Web,微信等多种访问方式.如下图. 不同的客户端共用同一个服务器,数据库,API.本次项目着重设计PC后台管理,供电商后台管 ...

  7. Vue+Element-UI 电商后台管理系统详细总结

    一.概述 基于 Vue 和 Element-UI 的电商后台管理系统 1.1 实现功能 用户登录/退出 用户管理 用户列表 实现用户的增删改查.分页查询以及分配角色功能 权限管理 角色列表 实现角色的 ...

  8. 基于Vue实现智慧社区电商后台管理系统

    作者主页:编程指南针 作者简介:Java领域优质创作者.CSDN博客专家 .掘金特邀作者.多年架构师设计经验.腾讯课堂常驻讲师 主要内容:Java项目.前端项目.小程序开发.Python开发.大数据和 ...

  9. 计算机毕业设计node.js+vue+Element电商后台管理系统

    项目介绍 网络的广泛应用给生活带来了十分的便利.所以把电商后台管理与现在网络相结合,利用node技术建设电商后台管理系统,实现电商后台管理的信息化.则对于进一步提高电商后台管理发展,丰富电商后台管理经 ...

最新文章

  1. 如何实现有效的团队管理?
  2. 自然语言处理基础技术之词性标注
  3. django模型sqlserver_让Django模型在MSSQL上工作的麻烦?
  4. dup和dup2以及cat函数函数
  5. python爬虫的scrapy安装+pymongo的安装
  6. mysqlsla日志分析工具
  7. 2018年4月更新70多个公司dnc招聘职位
  8. Eclispe创建jsp文件,提示错误:“The import Xxx cannot be resolved“的解决方案
  9. Photoshop修复和合成梦幻人物
  10. 存储:Cookie/localStorage
  11. 《ASCE1885的源码分析》の简单的进程封装类
  12. python 生成器装饰器_对Python生成器、装饰器、递归的使用详解
  13. JVM——类的加载过程
  14. 2012蓝桥杯【初赛试题】 取球游戏
  15. 王炸 | 海康汽车来了 商机还是冒进?
  16. PHP学生学校在线考试管理系统,MYSQL数据库网页设计
  17. 一、mybatis-generator
  18. 今天拿到软件设计师证书
  19. 【单片机笔记】基于2G、4G通信的物联网数据方案及扫码支付方案
  20. Markdown进阶

热门文章

  1. 概率论自学day4-布尔不等式和Bonferroni不等式
  2. Python判断能否组成三角形
  3. 关于 Windows 10 下安装 Linux 过程及报 'WSL...' 错误的解决办法
  4. 【电子综合实践与创新】助听器的综合实现(四)——元器件焊接和电路调试与维修
  5. 青龙面板 ---- 省钱钱
  6. 2021年 美国大学生数学建模竞赛 赛题解析!
  7. Android实现Mtp访问浏览手机存储(二) 禁止DocumentsUI文件直接弹出
  8. 打印折痕(左神2020)
  9. 下载bilibi视频 下载B站视频
  10. 微软活动目录的物理结构