Vue电商-功能模块

一. 登录功能

1 - 登录-token原理分析

因为http是无状态的,所以我们有两种方式记住登录状态:

  • 如果服务器和客户端同源,通过 cookie 在客户端记录状态,通过 session 在服务器端记录状态
  • 如果服务器和客户端不同源,通过 token 方式维持状态

在登录页面输入账号和密码进行登录,将数据发送给服务器,服务器返回登录的结果,登录成功则返回数据中带有token,客户端得到token并进行保存,后续的请求都需要将此token发送给服务器,服务器会验证token以保证用户身份。

2 - 登录组件布局

通过 Element-UI 组件实现布局

  • el-form
  • el-form-item
  • el-input
  • el-button
  • 字体图标

3 - 实现登录

  1. 通过 axios 调用登录验证接口
  2. 登录成功之后保存用户 token 信息
  3. 跳转到项目主页
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')

4 - 路由导航守卫控制访问权限

如果用户没有登录,但是直接通过 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()
})

5 - Vue 直接操作 DOM

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

6 - 基于 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、跳转到主页) 
})

7 - 退出功能

基于 token 的方式实现退出比较简单,只需要销毁本地的 token 即可。这样,后续的请求就不会携带 token , 必须重新登录生成一个新的 token 之后才可以访问页面。在Home组件中添加一个退出功能按钮,给退出按钮添加点击事件,添加事件处理代码如下:

// 清空token
window.sessionStorage.clear()
// 跳转到登录页
this.$router.push('/login')

二. 主页布局

1 - 整体布局

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

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

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>

3 - 通过接口获取菜单数据

通过 axios 请求拦截器添加 token,保证拥有获取数据的权限。

// axios请求拦截 
axios.interceptors.request.use(config => {
  // 为请求头对象,添加 Token 验证的 Authorization 字段
  config.headers.Authorization = window.sessionStorage.getItem('token')
  return config 
})

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>

三. 用户管理

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. 表格数据分页

分页组件用法:

  1. 当前页码:pagenum
  2. 每页条数:pagesize
  3. 记录总数:total
  4. 页码变化事件
  5. 每页条数变化事件
  6. 分页条菜单控制
<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>

3 - 用户状态控制

开关组件的用法

<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('修改状态失败!')
  } 
}

4 - 添加用户

1. 添加用户表单弹窗布局

  • 弹窗组件用法
  • 控制弹窗显示和隐藏
<el-dialog title="添加用户" :visible.s参数管理ync="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()
})

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
})

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()
},

四. 权限管理

1 - 权限管理业务分析

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

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
}

3 - 角色列表展示

  • 调用后台接口获取角色列表数据
  • 角色列表展示
// 获取所有角色列表
async getRolesList() {
  const { data: res } = await this.$http.get('roles')
  if (res.meta.status !== 200) {
    return this.$message.error('获取角色列表失败!')
  }
  this.rolesList = res.data
},

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.data
  this.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
}

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 的基本使用

<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. 设置默认权限菜单选中

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

9. 完成角色授权

  • 获取所有选中的权限节点 id
  • 调用接口完成角色权限的分配
// 获取树形控件中,所有半选和全选节点的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('分配权限成功!')

五. 分类管理

1 - 商品分类概述

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

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.result
this.total = res.data.total

3 - 树形表格

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

  1. 安装依赖包 (地址: https://github.com/MisterTaki/vue-table-with-tree-grid
npm i vue-table-with-tree-grid -S
  1. 基本使用
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>

4 - 分页功能

  • 实现分页组件效果
  • 分页组件数据处理
<!-- 分页区域 -->
<el-pagination 
  @current-change="handleCurrentChange" 
  :current-page="queryInfo.pagenum" 
  :page-size="queryInfo.pagesize" 
  layout="total, prev, pager, next, jumper" 
  :total="total">
</el-pagination>

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 = 0
    this.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('添加分类成功!')

六. 参数管理

1 - 参数管理概述

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

2 - 商品分类选择

1. 选择商品分类

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

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

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

3 - 实现参数列表

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

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

2. 处理标签数据格式

将字符串形式的数据分隔为数组。

res.data.forEach(item => {
  // 把字符串的可选项,分割为数组,重新赋值给 attr_vals
  item.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_vals
  item.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('更新参数项成功!')

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

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

七. 商品管理

1 - 商品管理概述

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

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

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-upload
  action="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.url
  this.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-editor
npm 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')

八. 订单管理

1 - 订单管理概述

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

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.goods
this.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

九. 数据统计

1 - 数据统计概述

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

2 - 用户来源数据统计报表

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

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

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

  1. 调用接口获取后台接口数据
  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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 主要内容简介: 本文为个人设计电商平台的部分功能模块说明。该电商平台主要实现购买商品,登录注册,商品分类,商品搜索...
    kolarYe阅读 1,646评论 0 1
  • 1. 后端post接口以流的方式返回excel数据,前端下载成excel文件 axios 接口传参否则乱码:res...
    暴躁程序员阅读 1,326评论 0 14
  • 字符串 1.什么是字符串 使用单引号或者双引号括起来的字符集就是字符串。 引号中单独的符号、数字、字母等叫字符。 ...
    mango_2e17阅读 7,493评论 1 7
  • 《闭上眼睛才能看清楚自己》这本书是香海禅寺主持贤宗法师的人生体悟,修行心得及讲学录,此书从六个章节讲述了禅修是什么...
    宜均阅读 9,984评论 1 25
  • 偶然间从公众号里看见了小白训练营的课。就点进去看了看。刚开始的时候我觉得就是骗人的。后来一想,学费那么少。干嘛...
    天天优惠233阅读 3,686评论 0 12