项目简介:

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

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

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

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

登录页面

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

路由导航守卫:

// 挂载路由导航守卫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()})

登录页面核心代码:

                                                                                                                                          登录          重置                    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')      })    }  }}.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;}

菜单实现

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

                                电商后台管理系统            退出                        |||                                                                                                                    {{item.authName}}                                                                                                                  {{subItem.authName}}                                                                                          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    }  }}.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;}

用户管理

用户列表

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

核心代码:

                首页      用户管理      用户列表                                                                                          添加用户                                                                                                                                                                                                                                                                                                                                                                                                                                                      取 消        确 定                                                                                                                          取 消        确 定                                

当前的用户:{{userInfo.username}}

当前的角色:{{userInfo.role_name}}

分配新角色:

取 消 确 定 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)" />

权限管理

角色列表

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

                首页      权限管理      角色列表                                          添加角色                                                                                                            {{item1.authName}}                                                                                                                                {{item2.authName}}                                                                            {{item3.authName}}                                                                        <!-- 
              {{scope.row}}            

--> 编辑 删除 分配权限 取 消 确 定 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 } }}.el-tag { margin: 7px;}.bdtop { border-top: 1px solid #eee;}.bdbottom { border-bottom: 1px solid #eee;}.vcenter { display: flex; align-items: center;}

权限列表

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

                首页      权限管理      权限列表                                                                        一级            二级            三级                              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)    }  }}

商品管理

商品分类

                首页      商品管理      商品分类                                    添加分类                                                                                                一级          二级          三级                                  编辑          删除                                                                                                                                                              取 消        确 定            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    }  }}.treeTable {  margin-top: 15px;}.el-cascader {  width: 100%;}

商品列表

                首页      商品管理      商品列表                                                                                    添加商品                                                                                        {{scope.row.add_time | dateFormat}}                                                                                                            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')    }  }}

增加商品

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

                首页      商品管理      添加商品                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                点击上传                                                                                添加商品                                                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 " />.el-checkbox {  margin: 0 10px 0 0 !important;}.previewImg {  width: 100%;}.btnAdd {  margin-top: 15px;}

分类参数

在分类参数中选择一件商品,可以为其添加静态或者动态参数,这个参数可以展示在移动端商品的属性中。

                首页      商品管理      参数列表                                                      选择商品分类:                                                                                            添加参数                                                                                          {{item}}                                                                                + New Tag                                                                                                        编辑                删除                                                                                添加属性                                                                                          {{item}}                                                                                + New Tag                                                                                                        编辑                删除                                                                                                                        取 消        确 定                                                                            取 消        确 定            export default {  data() {    return {      // 商品分类列表      catelist: [],      // 级联选择框的配置对象      cateProps: {        value: 'cat_id',        label: 'cat_name',        children: 'children'      },      // 级联选择框双向绑定到的数组      selectedCateKeys: [],      // 被激活的页签的名称      activeName: 'many',      // 动态参数的数据      manyTableData: [],      // 静态属性的数据      onlyTableData: [],      // 控制添加对话框的显示与隐藏      addDialogVisible: false,      // 添加参数的表单数据对象      addForm: {        attr_name: ''      },      // 添加表单的验证规则对象      addFormRules: {        attr_name: [          { required: true, message: '请输入参数名称', trigger: 'blur' }        ]      },      // 控制修改对话框的显示与隐藏      editDialogVisible: false,      // 修改的表单数据对象      editForm: {},      // 修改表单的验证规则对象      editFormRules: {        attr_name: [          { required: true, message: '请输入参数名称', trigger: 'blur' }        ]      }    }  },  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() {      this.getParamsData()    },    // tab 页签点击事件的处理函数    handleTabClick() {      console.log(this.activeName)      this.getParamsData()    },    // 获取参数的列表数据    async getParamsData() {      // 证明选中的不是三级分类      if (this.selectedCateKeys.length !== 3) {        this.selectedCateKeys = []        this.manyTableData = []        this.onlyTableData = []        return      }      // 证明选中的是三级分类      console.log(this.selectedCateKeys)      // 根据所选分类的Id,和当前所处的面板,获取对应的参数      const { data: res } = await this.$http.get(        `categories/${this.cateId}/attributes`,        {          params: { sel: this.activeName }        }      )      if (res.meta.status !== 200) {        return this.$message.error('获取参数列表失败!')      }      res.data.forEach(item => {        item.attr_vals = item.attr_vals " />.cat_opt {  margin: 15px 0;}.el-tag {  margin: 10px;}.input-new-tag {  width: 120px;}

订单管理

订单列表

订单管理的实现和用户管理有很多类似的地方,都是向后端发送请求然后渲染到页面上

                首页      订单管理      订单列表                                                                                                                                          已付款            未付款                                                              {{scope.row.is_send}}                                                            {{scope.row.create_time | dateFormat}}                                                                                                                                                                                                取 消        确 定                                                {{activity.context}}                    import cityData from './citydata.js'export default {  data() {    return {      queryInfo: {        query: '',        pagenum: 1,        pagesize: 10      },      total: 0,      orderlist: [],      addressVisible: false,      addressForm: {        address1: [],        address2: ''      },      addressFormRules: {        address1: [          { required: true, message: '请选择省市区县', trigger: 'blur' }        ],        address2: [          { required: true, message: '请填写详细地址', trigger: 'blur' }        ]      },      cityData,      progressVisible: false,      progressInfo: []    }  },  created() {    this.getOrderList()  },  methods: {    async getOrderList() {      const { data: res } = await this.$http.get('orders', {        params: this.queryInfo      })      if (res.meta.status !== 200) {        return this.$message.error('获取订单列表失败!')      }      console.log(res)      this.total = res.data.total      this.orderlist = res.data.goods    },    handleSizeChange(newSize) {      this.queryInfo.pagesize = newSize      this.getOrderList()    },    handleCurrentChange(newPage) {      this.queryInfo.pagenum = newPage      this.getOrderList()    },    // 展示修改地址的对话框    showBox() {      this.addressVisible = true    },    addressDialogClosed() {      this.$refs.addressFormRef.resetFields()    },    async showProgressBox() {      const { data: res } = await this.$http.get('/kuaidi/804909574412544580')      if (res.meta.status !== 200) {        return this.$message.error('获取物流进度失败!')      }      this.progressInfo = res.data      this.progressVisible = true      console.log(this.progressInfo)    }  }}@import '../../plugins/timeline/timeline.css';@import '../../plugins/timeline-item/timeline-item.css';.el-cascader {  width: 100%;}

数据统计

数据报表

数据统计部分用到了echarts,从后端获得数据后通过_.merge()将数据组合在一起,最后渲染在页面上

                首页      数据统计      数据报表                              // 1. 导入 echartsimport echarts from 'echarts'import _ from 'lodash'export default {  data() {    return {      // 需要合并的数据      options: {        title: {          text: '用户来源'        },        tooltip: {          trigger: 'axis',          axisPointer: {            type: 'cross',            label: {              backgroundColor: '#E9EEF3'            }          }        },        grid: {          left: '3%',          right: '4%',          bottom: '3%',          containLabel: true        },        xAxis: [          {            boundaryGap: false          }        ],        yAxis: [          {            type: 'value'          }        ]      }    }  },  created() {},  // 此时,页面上的元素,已经被渲染完毕了!  async mounted() {    // 3. 基于准备好的dom,初始化echarts实例    var myChart = echarts.init(document.getElementById('main'))    const { data: res } = await this.$http.get('reports/type/1')    if (res.meta.status !== 200) {      return this.$message.error('获取折线图数据失败!')    }    // 4. 准备数据和配置项    const result = _.merge(res.data, this.options)    // 5. 展示数据    myChart.setOption(result)  },  methods: {}}

项目git地址

目录中有后端以及sql文件,按照说明运行即可

电商系统前端: 电商管理系统前端

学习资源

黑马程序员前端