vue项目实战-电商后台管理系统

Posted 晓宜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue项目实战-电商后台管理系统相关的知识,希望对你有一定的参考价值。

项目简介:

该项目为电商后台的管理系统。设计了登录页面。

管理人员需要通过输入正确的用户名和密码才能登录。登陆成功之后进入管理页面:

管理页面由五个子模块组成:用户管理,权限管理,商品管理,订单管理,数据统计;

每个子模块有若干子模块组成,用户管理下->用户列表,权限管理->角色列表,权限管理,商品管理->商品列表,分类参数,商品分配,订单管理->订单列表,数据统计->数据报表

登录页面

登录页面中对用户输入的内容进行预校验,如果不符合要求则,则不向后端发送请求,同事挂载路由守卫,防止强制跳转。同时设置令牌校验,避免重复登录。如果用户输入格式正确的用户名以及密码时,向后端发送请求,请求通过则跳转到管理页面,否则返回登录页面。

路由导航守卫:

// 挂载路由导航守卫
router.beforeEach((to, from, next) => 
  // to 将要访问的路径
  // from 代表从哪个路径跳转而来
  // next 是一个函数,表示放行
  //     next()  放行    next('/login')  强制跳转

  if (to.path === '/login') return next()
  // 获取token
  const tokenStr = window.sessionStorage.getItem('token')
  if (!tokenStr) return next('/login')
  next()
)

登录页面核心代码:

<template>
  <div class="login_container">
    <div class="login_box">
      <!-- 头像区域 -->
      <div class="avatar_box">
        <img src="../assets/logo.png" alt="">
      </div>
      <!-- 登录表单区域 -->
      <el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form">
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input v-model="loginForm.password" prefix-icon="iconfont icon-3702mima" type="password"></el-input>
        </el-form-item>
        <!-- 按钮区域 -->
        <el-form-item class="btns">
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default 
  data() 
    return 
      // 这是登录表单的数据绑定对象
      loginForm: 
        username: 'admin',
        password: '123456'
      ,
      // 这是表单的验证规则对象
      loginFormRules: 
        // 验证用户名是否合法
        username: [
           required: true, message: '请输入登录名称', trigger: 'blur' ,
           min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' 
        ],
        // 验证密码是否合法
        password: [
           required: true, message: '请输入登录密码', trigger: 'blur' ,
           min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' 
        ]
      
    
  ,
  methods: 
    // 点击重置按钮,重置登录表单
    resetLoginForm() 
      // console.log(this);
      this.$refs.loginFormRef.resetFields()
    ,
    login() 
      this.$refs.loginFormRef.validate(async valid => 
        if (!valid) return
        const  data: res  = await this.$http.post('login', this.loginForm)
        if (res.meta.status !== 200) return this.$message.error('登录失败!')
        this.$message.success('登录成功')
        // 1. 将登录成功之后的 token,保存到客户端的 sessionStorage 中
        //   1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问
        //   1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中
        window.sessionStorage.setItem('token', res.data.token)
        // 2. 通过编程式导航跳转到后台主页,路由地址是 /home
        this.$router.push('/home')
      )
    
  

</script>

<style lang="less" scoped>
.login_container 
  background-color: #2b4b6b;
  height: 100%;


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

  .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;
    
  


.login_form 
  position: absolute;
  bottom: 0;
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box;


.btns 
  display: flex;
  justify-content: flex-end;

</style>

菜单实现

管理页面有一个侧面的两级菜单,菜单的数据来自于后端,点击二级菜单会跳转到相应的子页面中。在el-menu中设置router属性,即可通过index添加到路由上进行跳转。

<template>
  <el-container class="home-container">
    <!-- 头部区域 -->
    <el-header>
      <div>
        <img src="../assets/heima.png" alt="">
        <span>电商后台管理系统</span>
      </div>
      <el-button type="info" @click="logout">退出</el-button>
    </el-header>
    <!-- 页面主体区域 -->
    <el-container>
      <!-- 侧边栏 -->
      <el-aside :width="isCollapse ? '64px' : '200px'">
        <div class="toggle-button" @click="toggleCollapse">|||</div>
        <!-- 侧边栏菜单区域 -->
        <el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF" unique-opened :collapse="isCollapse" :collapse-transition="false" router :default-active="activePath">
          <!-- 一级菜单 -->
          <el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id">
            <!-- 一级菜单的模板区域 -->
            <template slot="title">
              <!-- 图标 -->
              <i :class="iconsObj[item.id]"></i>
              <!-- 文本 -->
              <span>item.authName</span>
            </template>

            <!-- 二级菜单 -->
            <el-menu-item :index="'/' + subItem.path" v-for="subItem in item.children" 
            :key="subItem.id" @click="saveNavState('/' + subItem.path)">
              <template slot="title">
                <!-- 图标 -->
                <i class="el-icon-menu"></i>
                <!-- 文本 -->
                <span>subItem.authName</span>
              </template>
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>
      <!-- 右侧内容主体 -->
      <el-main>
        <!-- 路由占位符 -->
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default 
  data() 
    return 
      // 左侧菜单数据
      menulist: [],
      iconsObj: 
        '125': 'iconfont icon-user',
        '103': 'iconfont icon-tijikongjian',
        '101': 'iconfont icon-shangpin',
        '102': 'iconfont icon-danju',
        '145': 'iconfont icon-baobiao'
      ,
      // 是否折叠
      isCollapse: false,
      // 被激活的链接地址
      activePath: ''
    
  ,
  created() 
    this.getMenuList()
    this.activePath = window.sessionStorage.getItem('activePath')
  ,
  methods: 
    logout() 
      window.sessionStorage.clear()
      this.$router.push('/login')
    ,
    // 获取所有的菜单
    async getMenuList() 
      const  data: res  = await this.$http.get('menus')
      if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
      this.menulist = res.data
      console.log(res)
    ,
    // 点击按钮,切换菜单的折叠与展开
    toggleCollapse() 
      this.isCollapse = !this.isCollapse
    ,
    // 保存链接的激活状态
    saveNavState(activePath) 
      window.sessionStorage.setItem('activePath', activePath)
      this.activePath = activePath
    
  

</script>

<style lang="less" scoped>
.home-container 
  height: 100%;

.el-header 
  background-color: #373d41;
  display: flex;
  justify-content: space-between;
  padding-left: 0;
  align-items: center;
  color: #fff;
  font-size: 20px;
  > div 
    display: flex;
    align-items: center;
    span 
      margin-left: 15px;
    
  


.el-aside 
  background-color: #333744;
  .el-menu 
    border-right: none;
  


.el-main 
  background-color: #eaedf1;


.iconfont 
  margin-right: 10px;


.toggle-button 
  background-color: #4a5064;
  font-size: 10px;
  line-height: 24px;
  color: #fff;
  text-align: center;
  letter-spacing: 0.2em;
  cursor: pointer;

</style>

用户管理

用户列表

用户管理下有用户列表,这里渲染了后端的用户列表,可以编辑用户信息,删除用户,为用户分配角色,还可以对用户是否禁用进行管理;除此之外,还添加了查询用户,添加用户,和分页功能。

核心代码:

<template>
  <div>
    <!-- 面包屑导航区域 -->
    <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">
        <el-col :span="8">
          <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList">
            <el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
          </el-input>
        </el-col>
        <el-col :span="4">
          <el-button type="primary" @click="addDialogVisible = true">添加用户</el-button>
        </el-col>
      </el-row>

      <!-- 用户列表区域 -->
      <el-table :data="userlist" border stripe>
        <el-table-column type="index"></el-table-column>
        <el-table-column label="姓名" prop="username"></el-table-column>
        <el-table-column label="邮箱" prop="email"></el-table-column>
        <el-table-column label="电话" prop="mobile"></el-table-column>
        <el-table-column label="角色" prop="role_name"></el-table-column>
        <el-table-column label="状态">
          <template slot-scope="scope">
            <el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)">
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="180px">
          <template slot-scope="scope">
            <!-- 修改按钮 -->
            <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button>
            <!-- 删除按钮 -->
            <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.id)"></el-button>
            <!-- 分配角色按钮 -->
            <el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false">
              <el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)"></el-button>
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>

      <!-- 分页区域 -->
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
      </el-pagination>
    </el-card>

    <!-- 添加用户的对话框 -->
    <el-dialog title="添加用户" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed">
      <!-- 内容主体区域 -->
      <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="addForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input v-model="addForm.password"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addForm.email"></el-input>
        </el-form-item>
        <el-form-item label="手机" prop="mobile">
          <el-input v-model="addForm.mobile"></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="addUser">确 定</el-button>
      </span>
    </el-dialog>

    <!-- 修改用户的对话框 -->
    <el-dialog title="修改用户" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed">
      <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="70px">
        <el-form-item label="用户名">
          <el-input v-model="editForm.username" disabled></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="editForm.email"></el-input>
        </el-form-item>
        <el-form-item label="手机" prop="mobile">
          <el-input v-model="editForm.mobile"></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="editUserInfo">确 定</el-button>
      </span>
    </el-dialog>

    <!-- 分配角色的对话框 -->
    <el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" @close="setRoleDialogClosed">
      <div>
        <p>当前的用户:userInfo.username</p>
        <p>当前的角色:userInfo.role_name</p>
        <p>分配新角色:
          <el-select v-model="selectedRoleId" placeholder="请选择">
            <el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id">
            </el-option>
          </el-select>
        </p>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="setRoleDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveRoleInfo">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default 
  data() 
    // 验证邮箱的规则
    var checkEmail = (rule, value, cb) => 
      // 验证邮箱的正则表达式
      const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\\.[a-zA-Z0-9_-])+/

      if (regEmail.test(value)) 
        // 合法的邮箱
        return cb()
      

      cb(new Error('请输入合法的邮箱'))
    

    // 验证手机号的规则
    var checkMobile = (rule, value, cb) => 
      // 验证手机号的正则表达式
      const regMobile = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]8$/

      if (regMobile.test(value)) 
        return cb()
      

      cb(new Error('请输入合法的手机号'))
    

    return 
      // 获取用户列表的参数对象
      queryInfo: 
        query: '',
        // 当前的页数
        pagenum: 1,
        // 当前每页显示多少条数据
        pagesize: 2
      ,
      userlist: [],
      total: 0,
      // 控制添加用户对话框的显示与隐藏
      addDialogVisible: false,
      // 添加用户的表单数据
      addForm: 
        username: '',
        password: '',
        email: '',
        mobile: ''
      ,
      // 添加表单的验证规则对象
      addFormRules: 
        username: [
           required: true, message: '请输入用户名', trigger: 'blur' ,
          
            min: 3,
            max: 10,
            message: '用户名的长度在3~10个字符之间',
            trigger: 'blur'
          
        ],
        password: [
           required: true, message: '请输入密码', trigger: 'blur' ,
          
            min: 6,
            max: 15,
            message: '用户名的长度在6~15个字符之间',
            trigger: 'blur'
          
        ],
        email: [
           required: true, message: '请输入邮箱', trigger: 'blur' ,
           validator: checkEmail, trigger: 'blur' 
        ],
        mobile: [
           required: true, message: '请输入手机号', trigger: 'blur' ,
           validator: checkMobile, trigger: 'blur' 
        ]
      ,
      // 控制修改用户对话框的显示与隐藏
      editDialogVisible: false,
      // 查询到的用户信息对象
      editForm: ,
      // 修改表单的验证规则对象
      editFormRules: 
        email: [
           required: true, message: '请输入用户邮箱', trigger: 'blur' ,
           validator: checkEmail, trigger: 'blur' 
        ],
        mobile: [
           required: true, message: '请输入用户手机', trigger: 'blur' ,
           validator: checkMobile, trigger: 'blur' 
        ]
      ,
      // 控制分配角色对话框的显示与隐藏
      setRoleDialogVisible: false,
      // 需要被分配角色的用户信息
      userInfo: ,
      // 所有角色的数据列表
      rolesList: [],
      // 已选中的角色Id值
      selectedRoleId: ''
    
  ,
  created() 
    this.getUserList()
  ,
  methods: 
    async getUserList() 
      const  data: res  = await this.$http.get('users', 
        params: this.queryInfo
      )
      if (res.meta.status !== 200) 
        return this.$message.error('获取用户列表失败!')
      
      this.userlist = res.data.users
      this.total = res.data.total
      console.log(res)
    ,
    // 监听 pagesize 改变的事件
    handleSizeChange(newSize) 
      // console.log(newSize)
      this.queryInfo.pagesize = newSize
      this.getUserList()
    ,
    // 监听 页码值 改变的事件
    handleCurrentChange(newPage) 
      console.log(newPage)
      this.queryInfo.pagenum = newPage
      this.getUserList()
    ,
    // 监听 switch 开关状态的改变
    async userStateChanged(userinfo) 
      console.log(userinfo)
      const  data: res  = await this.$http.put(
        `users/$userinfo.id/state/$userinfo.mg_state`
      )
      if (res.meta.status !== 200) 
        userinfo.mg_state = !userinfo.mg_state
        return this.$message.error('更新用户状态失败!')
      
      this.$message.success('更新用户状态成功!')
    ,
    // 监听添加用户对话框的关闭事件
    addDialogClosed() 
      this.$refs.addFormRef.resetFields()
    ,
    // 点击按钮,添加新用户
    addUser() 
      this.$refs.addFormRef.validate(async valid => 
        if (!valid) return
        // 可以发起添加用户的网络请求
        const  data: res  = await this.$http.post('users', this.addForm)

        if (res.meta.status !== 201) 
          this.$message.error('添加用户失败!')
        

        this.$message.success('添加用户成功!')
        // 隐藏添加用户的对话框
        this.addDialogVisible = false
        // 重新获取用户列表数据
        this.getUserList()
      )
    ,
    // 展示编辑用户的对话框
    async showEditDialog(id) 
      // console.log(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
    ,
    // 监听修改用户对话框的关闭事件
    editDialogClosed() 
      this.$refs.editFormRef.resetFields()
    ,
    // 修改用户信息并提交
    editUserInfo() 
      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.editDialogVisible = false
        // 刷新数据列表
        this.getUserList()
        // 提示修改成功
        this.$message.success('更新用户信息成功!')
      )
    ,
    // 根据Id删除对应的用户信息
    async removeUserById(id) 
      // 弹框询问用户是否删除数据
      const confirmResult = await this.$confirm(
        '此操作将永久删除该用户, 是否继续?',
        '提示',
        
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        
      ).catch(err => err)

      // 如果用户确认删除,则返回值为字符串 confirm
      // 如果用户取消了删除,则返回值为字符串 cancel
      // console.log(confirmResult)
      if (confirmResult !== 'confirm') 
        return this.$message.info('已取消删除')
      

      const  data: res  = await this.$http.delete('users/' + id)

      if (res.meta.status !== 200) 
        return this.$message.error('删除用户失败!')
      

      this.$message.success('删除用户成功!')
      this.getUserList()
    ,
    // 展示分配角色的对话框
    async setRole(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.setRoleDialogVisible = true
    ,
    // 点击按钮,分配角色
    async saveRoleInfo() 
      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.setRoleDialogVisible = false
    ,
    // 监听分配角色对话框的关闭事件
    setRoleDialogClosed() 
      this.selectedRoleId = ''
      this.userInfo = 
    
  

</script>

<style lang="less" scoped>
</style>

权限管理

角色列表

角色列表中可以创建新的角色,创建的新的角色可以在用户管理中赋予用户,同时可以为已有的角色赋予权限

<template>
  <div>
    <!-- 面包屑导航区域 -->
    <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>
        <el-col>
          <el-button type="primary">添加角色</el-button>
        </el-col>
      </el-row>

      <!-- 角色列表区域 -->
      <el-table :data="rolelist" border stripe>
        <!-- 展开列 -->
        <el-table-column type="expand">
          <template slot-scope="scope">
            <el-row :class="['bdbottom', i1 === 0 ? 'bdtop' : '', 'vcenter']" v-for="(item1, i1) in scope.row.children" :key="item1.id">
              <!-- 渲染一级权限 -->
              <el-col :span="5">
                <el-tag closable @close="removeRightById(scope.row, item1.id)">item1.authName</el-tag>
                <i class="el-icon-caret-right"></i>
              </el-col>
              <!-- 渲染二级和三级权限 -->
              <el-col :span="19">
                <!-- 通过 for 循环 嵌套渲染二级权限 -->
                <el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item2, i2) in item1.children" :key="item2.id">
                  <el-col :span="6">
                    <el-tag type="success" closable @close="removeRightById(scope.row, item2.id)">item2.authName</el-tag>
                    <i class="el-icon-caret-right"></i>
                  </el-col>
                  <el-col :span="18">
                    <el-tag type="warning" v-for="item3 in item2.children" :key="item3.id" closable @close="removeRightById(scope.row, item3.id)">item3.authName</el-tag>
                  </el-col>
                </el-row>
              </el-col>
            </el-row>

            <!-- <pre>
              scope.row
            </pre> -->
          </template>
        </el-table-column>
        <!-- 索引列 -->
        <el-table-column type="index"></el-table-column>
        <el-table-column label="角色名称" prop="roleName"></el-table-column>
        <el-table-column label="角色描述" prop="roleDesc"></el-table-column>
        <el-table-column label="操作" width="300px">
          <template slot-scope="scope">
            <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button>
            <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button>
            <el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog(scope.row)">分配权限</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- 分配权限的对话框 -->
    <el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClosed">
      <!-- 树形控件 -->
      <el-tree :data="rightslist" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree>
      <span slot="footer" class="dialog-footer">
        <el-button @click="setRightDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="allotRights">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default 
  data() 
    return 
      // 所有角色列表数据
      rolelist: [],
      // 控制分配权限对话框的显示与隐藏
      setRightDialogVisible: false,
      // 所有权限的数据
      rightslist: [],
      // 树形控件的属性绑定对象
      treeProps: 
        label: 'authName',
        children: 'children'
      ,
      // 默认选中的节点Id值数组
      defKeys: [],
      // 当前即将分配权限的角色id
      roleId: ''
    
  ,
  created() 
    this.getRolesList()
  ,
  methods: 
    // 获取所有角色的列表
    async getRolesList() 
      const  data: res  = await this.$http.get('roles')

      if (res.meta.status !== 200) 
        return this.$message.error('获取角色列表失败!')
      

      this.rolelist = res.data

      console.log(this.rolelist)
    ,
    // 根据Id删除对应的权限
    async removeRightById(role, rightId) 
      // 弹框提示用户是否要删除
      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(
        `roles/$role.id/rights/$rightId`
      )

      if (res.meta.status !== 200) 
        return this.$message.error('删除权限失败!')
      

      // this.getRolesList()
      role.children = res.data
    ,
    // 展示分配权限的对话框
    async showSetRightDialog(role) 
      this.roleId = role.id
      // 获取所有权限的数据
      const  data: res  = await this.$http.get('rights/tree')

      if (res.meta.status !== 200) 
        return this.$message.error('获取权限数据失败!')
      

      // 把获取到的权限数据保存到 data 中
      this.rightslist = res.data
      console.log(this.rightslist)

      // 递归获取三级节点的Id
      this.getLeafKeys(role, this.defKeys)

      this.setRightDialogVisible = true
    ,
    // 通过递归的形式,获取角色下所有三级权限的id,并保存到 defKeys 数组中
    getLeafKeys(node, arr) 
      // 如果当前 node 节点不包含 children 属性,则是三级节点
      if (!node.children) 
        return arr.push(node.id)
      

      node.children.forEach(item => this.getLeafKeys(item, arr))
    ,
    // 监听分配权限对话框的关闭事件
    setRightDialogClosed() 
      this.defKeys = []
    ,
    // 点击为角色分配权限
    async allotRights() 
      const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
      ]

      const idStr = keys.join(',')

      const  data: res  = await this.$http.post(
        `roles/$this.roleId/rights`,
         rids: idStr 
      )

      if (res.meta.status !== 200) 
        return this.$message.error('分配权限失败!')
      

      this.$message.success('分配权限成功!')
      this.getRolesList()
      this.setRightDialogVisible = false
    
  

</script>

<style lang="less" scoped>
.el-tag 
  margin: 7px;


.bdtop 
  border-top: 1px solid #eee;


.bdbottom 
  border-bottom: 1px solid #eee;


.vcenter 
  display: flex;
  align-items: center;

</style>

权限列表

权限列表对不同的权限做出展示,只发送一个请求即可获取所有需要的数据

<template>
  <div>
    <!-- 面包屑导航区域 -->
    <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-table :data="rightsList" border stripe>
        <el-table-column type="index"></el-table-column>
        <el-table-column label="权限名称" prop="authName"></el-table-column>
        <el-table-column label="路径" prop="path"></el-table-column>
        <el-table-column label="权限等级" prop="level">
          <template slot-scope="scope">
            <el-tag v-if="scope.row.level === '0'">一级</el-tag>
            <el-tag type="success" v-else-if="scope.row.level === '1'">二级</el-tag>
            <el-tag type="warning" v-else>三级</el-tag>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
  </div>
</template>

<script>
export default 
  data() 
    return 
      // 权限列表
      rightsList: []
    
  ,
  created() 
    // 获取所有的权限
    this.getRightsList()
  ,
  methods: 
    // 获取权限列表
    async getRightsList() 
      const  data: res  = await this.$http.get('rights/list')
      if (res.meta.status !== 200) 
        return this.$message.error('获取权限列表失败!')
      

      this.rightsList = res.data
      console.log(this.rightsList)
    
  

</script>

<style lang="less" scoped>
</style>

商品管理

商品分类

<template>
  <div>
    <!-- 面包屑导航区域 -->
    <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>
        <el-col>
          <el-button type="primary" @click="showAddCateDialog">添加分类</el-button>
        </el-col>
      </el-row>

      <!-- 表格 -->
      <tree-table class="treeTable" :data="catelist" :columns="columns" :selection-type="false" :expand-type="false" show-index index-text="#" border :show-row-hover="false">
        <!-- 是否有效 -->
        <template slot="isok" slot-scope="scope">
          <i class="el-icon-success" v-if="scope.row.cat_deleted === false" style="color: lightgreen;"></i>
          <i class="el-icon-error" v-else style="color: red;"></i>
        </template>
        <!-- 排序 -->
        <template slot="order" slot-scope="scope">
          <el-tag size="mini" v-if="scope.row.cat_level===0">一级</el-tag>
          <el-tag type="success" size="mini" v-else-if="scope.row.cat_level===1">二级</el-tag>
          <el-tag type="warning" size="mini" v-else>三级</el-tag>
        </template>
        <!-- 操作 -->
        <template slot="opt">
          <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>
      </tree-table>

      <!-- 分页区域 -->
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="querInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="querInfo.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="父级分类:">
          <!-- options 用来指定数据源 -->
          <!-- props 用来指定配置对象 -->
          <el-cascader expand-trigger="hover" :options="parentCateList" :props="cascaderProps" v-model="selectedKeys" @change="parentCateChanged" clearable change-on-select>
          </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 
      // 查询条件
      querInfo: 
        type: 3,
        pagenum: 1,
        pagesize: 5
      ,
      // 商品分类的数据列表,默认为空
      catelist: [],
      // 总数据条数
      total: 0,
      // 为table指定列的定义
      columns: [
        
          label: '分类名称',
          prop: 'cat_name'
        ,
        
          label: '是否有效',
          // 表示,将当前列定义为模板列
          type: 'template',
          // 表示当前这一列使用模板名称
          template: 'isok'
        ,
        
          label: '排序',
          // 表示,将当前列定义为模板列
          type: 'template',
          // 表示当前这一列使用模板名称
          template: 'order'
        ,
        
          label: '操作',
          // 表示,将当前列定义为模板列
          type: 'template',
          // 表示当前这一列使用模板名称
          template: 'opt'
        
      ],
      // 控制添加分类对话框的显示与隐藏
      addCateDialogVisible: false,
      // 添加分类的表单数据对象
      addCateForm: 
        // 将要添加的分类的名称
        cat_name: '',
        // 父级分类的Id
        cat_pid: 0,
        // 分类的等级,默认要添加的是1级分类
        cat_level: 0
      ,
      // 添加分类表单的验证规则对象
      addCateFormRules: 
        cat_name: [ required: true, message: '请输入分类名称', trigger: 'blur' ]
      ,
      // 父级分类的列表
      parentCateList: [],
      // 指定级联选择器的配置对象
      cascaderProps: 
        value: 'cat_id',
        label: 'cat_name',
        children: 'children'
      ,
      // 选中的父级分类的Id数组
      selectedKeys: []
    
  ,
  created() 
    this.getCateList()
  ,
  methods: 
    // 获取商品分类数据
    async getCateList() 
      const  data: res  = await this.$http.get('categories', 
        params: this.querInfo
      )

      if (res.meta.status !== 200) 
        return this.$message.error('获取商品分类失败!')
      

      console.log(res.data)
      // 把数据列表,赋值给 catelist
      this.catelist = res.data.result
      // 为总数据条数赋值
      this.total = res.data.total
    ,
    // 监听 pagesize 改变
    handleSizeChange(newSize) 
      this.querInfo.pagesize = newSize
      this.getCateList()
    ,
    // 监听 pagenum 改变
    handleCurrentChange(newPage) 
      this.querInfo.pagenum = newPage
      this.getCateList()
    ,
    // 点击按钮,展示添加分类的对话框
    showAddCateDialog() 
      // 先获取父级分类的数据列表
      this.getParentCateList()
      // 再展示出对话框
      this.addCateDialogVisible = true
    ,
    // 获取父级分类的数据列表
    async getParentCateList() 
      const  data: res  = await this.$http.get('categories', 
        params:  type: 2 
      )

      if (res.meta.status !== 200) 
        return this.$message.error('获取父级分类数据失败!')
      

      console.log(res.data)
      this.parentCateList = res.data
    ,
    // 选择项发生变化触发这个函数
    parentCateChanged() 
      console.log(this.selectedKeys)
      // 如果 selectedKeys 数组中的 length 大于0,证明选中的父级分类
      // 反之,就说明没有选中任何父级分类
      if (this.selectedKeys.length > 0) 
        // 父级分类的Id
        this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]
        // 为当前分类的等级赋值
        this.addCateForm.cat_level = this.selectedKeys.length
       else 
        // 父级分类的Id
        this.addCateForm.cat_pid = 0
        // 为当前分类的等级赋值
        this.addCateForm.cat_level = 0
      
    ,
    // 点击按钮,添加新的分类
    addCate() 
      this.$refs.addCateFormRef.validate(async valid => 
        if (!valid) return
        const  data: res  = await this.$http.post('categories', this.addCateForm)

        if (res.meta.status !== 201) 
          return this.$message.error('添加分类失败!')
        

        this.$message.success('添加分类成功!')
        this.getCateList()
        this.addCateDialogVisible = false
      )
    ,
    // 监听对话框的关闭事件,重置表单数据
    addCateDialogClosed() 
      this.$refs.addCateFormRef.resetFields()
      this.selectedKeys = []
      this.addCateForm.cat_level = 0
      this.addCateForm.cat_pid = 0
    
  

</script>

<style lang="less" scoped>
.treeTable 
  margin-top: 15px;


.el-cascader 
  width: 100%;

</style>

商品列表

<template>
  <div>
    <!-- 面包屑导航区域 -->
    <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">
        <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>
        <el-col :span="4">
          <el-button type="primary" @click="goAddpage">添加商品</el-button>
        </el-col>
      </el-row>

      <!-- table表格区域 -->
      <el-table :data="goodslist" border stripe>
        <el-table-column 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">
          <template slot-scope="scope">
            scope.row.add_time | dateFormat
          </template>
        </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" @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="[5, 10, 15, 20]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total" background>
      </el-pagination>
    </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.$message.error('获取商品列表失败!')
      

      this.$message.success('获取商品列表成功!')
      console.log(res.data)
      this.goodslist = res.data.goods
      this.total = res.data.total
    ,
    handleSizeChange(newSize) 
      this.queryInfo.pagesize = newSize
      this.getGoodsList()
    ,
    handleCurrentChange(newPage) 
      this.queryInfo.pagenum = newPage
      this.getGoodsList()
    ,
    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()
    ,
    goAddpage() 
      this.$router.push('/goods/add')
    
  

</script>

<style lang="less" scoped>
</style>

增加商品

在商品分类中点击新增商品,则跳转到新增商品窗口

<template>
  <div>
    <!-- 面包屑导航区域 -->
    <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="activeIndex - 0" finish-status="success" align-center>
        <el-step title="基本信息"></el-step>
        <el-step title="商品参数"></el-step>
        <el-step title="商品属性"></el-step>
        <el-step title="商品图片"></el-step>
        <el-step title="商品内容"></el-step>
        <el-step title="完成"></el-step>
      </el-steps>

      <!-- tab栏区域 -->

      <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" 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 expand-trigger="hover" :options="catelist" :props="cateProps" v-model="addForm.goods_cat" @change="handleChange">
              </el-cascader>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品参数" name="1">
            <!-- 渲染表单的Item项 -->
            <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>
          <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>
          <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-tab-pane label="商品内容" name="4">
            <!-- 富文本编辑器组件 -->
            <quill-editor v-model="addForm.goods_introduce"></quill-editor>
            <!-- 添加商品的按钮 -->
            <el-button type="primary" class="btnAdd" @click="add">添加商品</el-button>
          </el-tab-pane>
        </el-tabs>
      </el-form>

    </el-card>

    <!-- 图片预览 -->
    <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%">
      <img :src="previewPath" alt="" class="previewImg">
    </el-dialog>
  </div>
</template>

<script>
import _ from 'lodash'

export default 
  data() 
    return 
      activeIndex: '0',
      // 添加商品的表单数据对象
      addForm: 
        goods_name: '',
        goods_price: 0,
        goods_weight: 0,
        goods_number: 0,
        // 商品所属的分类数组
        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: 'blur' 
        ]
      ,
      // 商品分类列表
      catelist: [],
      cateProps: 
        label: 'cat_name',
        value: 'cat_id',
        children: 'children'
      ,
      // 动态参数列表数据
      manyTableData: [],
      // 静态属性列表数据
      onlyTableData: [],
      // 上传图片的URL地址
      uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
      // 图片上传组件的headers请求头对象
      headerObj: 
        Authorization: window.sessionStorage.getItem('token')
      ,
      previewPath: '',
      previewVisible: false
    
  ,
  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
      console.log(this.catelist)
    ,
    // 级联选择器选中项变化,会触发这个函数
    handleChange() 
      console.log(this.addForm.goods_cat)
      if (this.addForm.goods_cat.length !== 3) 
        this.addForm.goods_cat = []
      
    ,
    beforeTabLeave(activeName, oldActiveName) 
      // console.log('即将离开的标签页名字是:' + oldActiveName)
      // console.log('即将进入的标签页名字是:' + activeName)
      // return false
      if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) 
        this.$message.error('请先选择商品分类!')
        return false
      
    ,
    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('获取动态参数列表失败!')
        

        console.log(res.data)
        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.$message.error('获取静态属性失败!')
        

        console.log(res.data)
        this.onlyTableData = res.data
      
    ,
    // 处理图片预览效果
    handlePreview(file) 
      console.log(file)
      this.previewPath = file.response.data.url
      this.previewVisible = 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)
    ,
    // 添加商品
    add() 
      this.$refs.addFormRef.validate(async valid => 
        if (!valid) 
          return this.$message.error('请填写必要的表单项!')
        
        // 执行添加的业务逻辑
        // lodash   cloneDeep(obj)
        const form = _.cloneDeep(this.addForm)
        form.goods_cat = form.

基于 Vue2 的尚品汇电商前后台项目

开发时间:2022.11.07-2022.12.07

前台项目后台管理
atguigu-store-frontendatguigu-store-backend

文章目录

一、快速开始

二、系统概述

2.1 项目简介

此项目为尚品汇在线电商应用,前后端分离开发。前端项目包括前台应用系统及后台管理系统两大部分。前台项目基于 Vue 和前端三件套,后台项目基于 vue-admin-template 和 element-ui 组件库。

采用模块化、组件化、工程化的模式开发,基于最新最热的前端技术如 Vue 全家桶、ES6、Webpack、Axios 等,包括首页、商品搜索列表、商品详情、购物车、订单、支付、用户登录与注册等多个子模块,功能齐全。

2.2 技术选型

三、功能展示

3.1 前台项目

  1. 三级联动导航分类

  2. swiper 轮播图

  3. 商品搜索

  4. 面包屑搜索与结果排序过滤

  5. 分页条

  6. 商品详情查看

  7. 商品图片轮播与细节查看

  8. 商品加入游客购物车

  9. 购物车详情

  10. 用户注册

  11. 用户登录

  12. 购物车商品下单

  13. 订单提交

  14. 订单支付

  15. 支付成功

  16. 订单列表

3.2 后台管理

  1. 管理员登录

  2. 数据可视化

  3. 品牌管理

  4. 平台售卖属性管理

  5. SPU(Standard Product Unit) 管理

  6. SPU 详情查看

  7. SKU(Stock Keeping Unit ) 管理

以上是关于vue项目实战-电商后台管理系统的主要内容,如果未能解决你的问题,请参考以下文章

Vue项目实战:电商后台管理系统(Vue+VueRouter+Axios+Element)

vue后台管理系统

vue项目实战-电商后台管理系统

VUE项目实战2项目开发模式概述

mall-admin-web是一个电商后台管理系统的前端项目,基于Vue+Element实现

电商后台管理系统的前端技术栈-----vue