Vue Vue+VueRouter+Axios电商管理系统项目总结

392 阅读4分钟

电商管理系统

1 项目需求

现在有功能完善的一套电商后端系统数据,并有配套的接口文档,要求根据接口文档和项目实际需求编写前端界面,沟通后端数据实现总体功能。

使用技术栈:Vue+VueRouter+Axios

UI:Element UI

编译工具:VSCode

前端效果预览:在这里插入图片描述

2 项目设计与实现

2.1 后端

2.1.1 下载依赖

后端数据文件夹为vue_api_server,功能完善,首先下载项目的依赖包。

npm install

2.1.2 配置数据库

添加MySQL数据库,导入sql文件,在这里使用的是小皮面板代替。

在这里插入图片描述

  • 出现问题:连接数据库时显示错误,提示找不到服务。
  • 解决方案:把电脑自身的MySQL服务关掉,按照小皮面板提示重新安装一个集成版本的数据库,安装之后就显示成功了。接着就继续创建一个新的库,并导入sql文件。

2.1.3 运行后端服务

使用node命令运行后端数据服务。

node ./app.js

2.2 前端

2.2.1 新建项目

使用vue命令新建一个项目

vue create vue_new_shop

2.2.2 项目结构

src文件夹为全部的源码。

api文件夹里面的index.js配置baseURL和请求拦截器等。

assets文进家里面的文件用来对项目的公共样式进行配置。

components文件夹来存放所有的组件,components里面又分为common、goods、orders、Permission、reports、user各个功能模块要用到的组件。

router文件夹里面的index.js用来配置路由。

utils文件夹里面的check.js用来配置所有的表单验证规则。

views文件夹里面的文件是整个项目的登录界面和整体页面等。

main.js是项目的入口文件。

在这里插入图片描述

2.2.3 运行项目

使用命令运行整个项目。

npm run serve

3 项目重点

3.1 配置路由

在router文件夹下面的index.js文件中进行路由的配置。

  1. 引入文件组件
  2. 配置当前路由信息的path路径和组件名

3.2 单文件组件

一个文件就是一个组件,一个组件就是一个文件。单文件组件模板主要分三部分

<template>
	<div>单文件组件</div>    
</template>
<script>
export default{
    
}
</script>
<style lang="less" scope>

</style>
  1. template标签内为文件的主要显示内容
  2. script标签内用来引入其他组件和编写各种业务逻辑处理数据,其中包含data、methods、computed和生命周期钩子等等。
  3. style标签内主要编写当前组件页面的css样式。

3.3 父子通信

父子组件之间的通信一般使用props$emit()来实现。

例如父组件userlist.vue与子组件addUser.vue之间的数据通信。

  • 要求实现目的:点击父组件的“编辑修改用户”按钮,弹出一个对话框,填写完对话框内的表单信息之后,点击“确定”按钮,完成膝盖当前用户信息的操作。

  • 具体实现步骤:

    1. 第一步:点击“编辑修改用户”按钮,弹出对话框,渲染对话框内的表单。
    2. 第二步:输入数据,验证表单合法性。
    3. 第三步:点击“确定”按钮,对话框消失;用户数据重新渲染,显示当前被修改用户的新信息。
  • 代码实现:

    代码太长,看完太费劲,不如以一言语之。假设一个情景剧,父组件是一名数学老师A,子组件作为学生B,是数学课代表。

    话说这天是周末,A老师仰头一看,豁,天气晴朗,空气清新,好安逸啊。想着不能辜负了这大好天气,于是给班级学生划了新的知识点,布置了家庭作业。让课代表B同学给班级学生传达一下,到了周一收作业,还要检查作业完成情况。

    接下来要解决问题就是:A老师怎么在周末这天联系B?周末休息,不像在学校里面直接去班级就能找得到。

    所以A老师拿出电话簿(组件实例占位符)找到了B的手机号码(ref),B同学此时正在家里打游戏呢,接到A老师的电话也是一脸懵,结果是告知自己留新作业!同学当场就站了起来,咬了咬牙也不敢言语,只能应和着说一定会通知到班级学生完成家庭作业的。

    挂了电话,B同学顿时口吐芬芳,唱起了国粹,wc,sb...但还是不得不拿出了自己的课本,找到了老师留的作业(props),此时又是一口国粹。良久,酝酿好了情绪,来到了班级的群里,告知了大家这一件不幸的事。B同学表示自己是占同学们这一边的,一曲大合唱之后,大家接受了这个事实,虽不时有余音袅袅,但却奈何不得,只得作罢。

    到了周一,课代表B同学抱着大家完成的作业踉踉跄跄迈进了办公室的大门,放在A老师的办公桌上,请老师批改作业。A老师也非常人。一边跟B同学聊着理想一边批改作业,蹭蹭蹭就批改完了,就让B同学把作业抱回去给同学们分发一下,还鼓励同学们继续加油。

    现在回顾整个情景剧。父组价A老师,子组件B同学。A老师翻电话簿找B同学手机号码。

    <!-- 父组件 -->
    <template>
    	<div>
    		<template slot-scope="scope">
                <el-button type="primary" @click="showAddDialog(scope.row.id)"></el-button>
            </template>
            <add-user :user="currentUser" ref="addUser" @updatelist="getUserList"></add-user>
    	</div>    
    </template>
    <script>
    import addUser from './addUser.vue'
    export default{
        data(){
            return{
                currentUser: {},
                userlist: []
            }
        }
        components: {
        	addUser
      	},
        methods:{
    		async showAddDialog(uid) {
              const { data: res } = await this.$http.get(`users/${uid}`)
              this.currentUser = res.data
              this.$refs.addUser.showDialog()
            },
        }
    }
    </script>
    
    • import引入组件addUser,注册addUser,并用<addUser></addUser>标签实例化组件。ref="addUser"存的就是子组件B同学的电话号码,使用:user="currentUser"传递的就是布置的作业。

    • 点击编辑按钮,触发showAddDialog(uid)方法并传递过去当前项的id,方法发起数据请求,用currentUser接收获得的数据对象。

    • 给B同学打电话告知留的作业在哪,要写些什么。使用this.$refs.addUser就是打电话,电话打通之后,this.$refs.addUser.showDialog()就是让B同学通知学生们写作业。

    继续回顾:B同学通知学生做作业,写完作业后,B同学抱着作业去找A老师批改。

    <template>
    	<el-dialog title="编辑用户信息" :visible.sync="dialogVisible" width="50%">
        <el-form :model="addUserForm" :rules="addUserRules" ref="addUserRef" label-width="100px">
          <el-form-item label="用户名" prop="username">
            <el-input v-model="addUserForm.username" disabled></el-input>
          </el-form-item>
          <el-form-item label="邮箱" prop="email">
            <el-input v-model="addUserForm.email"></el-input>
          </el-form-item>
          <el-form-item label="手机号" prop="mobile">
            <el-input v-model="addUserForm.mobile"></el-input>
          </el-form-item>
        </el-form>
        <span slot="footer" class="dialog-footer">
          <el-button @click="hideDialog">取 消</el-button>
          <el-button type="primary" @click="saveUserInfo">确 定</el-button>
        </span>
      </el-dialog>
    </template>
    <script>
    export default{
        props: ['user'],
        computed: {
            addUserForm() {
              	return this.user
            }
        },
        data() {
            return {
              dialogVisible: false,
              addUserRules: {
                username: [
                  { required: true, message: '请输入用户名', trigger: 'blur' },
                  { min: 3, max: 10, message: '请输入3到10个字符', trigger: 'blur' }
                ],
                email: [
                  { required: true, message: '请输入邮箱', trigger: 'blur' },
                  { validator: checkObj.checkeEmail, trigger: 'blur' }
                ],
                mobile: [
                  { required: true, message: '请输入手机号', trigger: 'blur' },
                  { validator: checkObj.checkeMobile, trigger: 'blur' }
                ]
             }
           }
        },
        methods: {
            showDialog() {
              this.dialogVisible = true
            },
            hideDialog() {
              this.dialogVisible = false
            },
            saveUserInfo() {
              this.$refs.addUserRef.validate(async valid => {
                if (valid) {
                  var flag = true
                  for (var key in this.addUserForm) {
                    if (this.addUserForm[key] !== this.user[key]) {
                      flag = false
                      if (flag) {
                        this.$message.warning('您的内容没有进行修改,请修改之后再提交')
                        return null
                      }
                    }
                  }
                  // 校验成功,请求保存
                  const { data: res } = await this.$http.put(`users/${this.user.id}`, this.addUserForm)
                  if (res.meta.status === 200) {
                    this.$message.success('更新成功')
                    this.$emit('updatelist')
                    this.hideDialog()
                  } else {
                    this.$message.error('更新失败')
                  }
                } else {
                  this.$message.error('校验失败')
                }
             })
           }
        }
    }
    </script>
    
    • 对话框的:visible.sync="dialogVisible"指定的dialogVisible默认为false,表示对话框是隐藏不显示的。由于之前父组件调用了子组件的showDialog方法,改变dialogVisible为true,将对话框显示出来,渲染了对话框中的表单项和按钮。

    • 父组件A老师使用ref打电话,用:user="currentUser"传递给子组件B同学的数据,子组件使用props: ['user']接收过来,并且使用计算属性把接收到的数据对象保存到了addUserForm上面。

    • 使用:model="addUserForm"把数据赋给了表单。

    • 在表单的每一项中使用v-model="addUserForm.username"双向绑定了从父组件传递过来的数据对象的对应属性值。

    • Element UI为表单提供有表单验证功能,在表单中使用prop属性设置需要校验的字段名,然后在:rules="addUserRules"指定的addUserRules中配置对应的校验规则。

    • 输入信息后,经校验符合规则后点击确定按钮,触发saveUserInfo方法,this.$refs.addUserRef.validate对整个表单进行校验,校验无误后发起保存数据请求,请求成功后由子组件使用$emit()向父组件发送一个自定义事件,this.$emit('updatelist'),在父组件里面,有个自定义事件@updatelist="getUserList",将触发父组件中的getUserList方法,由父组件重新获取最新的用户数据列表并渲染出来。隐藏对话框。

    回顾continuing:到此为止,老师留了作业,学生做完,老师也给批改好了。

    再简单整体回顾一下父子组件之间通信的过程:

    1、点击父组件的编辑按钮,父组件找到子组件的ref属性值,通过它调用了子组件的showDialog方法,渲染出了对话框。

    2、点击子组件的确定按钮,使用prop指定的对应校验规则进行校验,无误后发起数据请求。子组件使用$emit()触发自定义事件向父组件通信,调用父组件中的getUserList方法重新获取并渲染用户数据列表。隐藏对话框。

  • 完整源码如下

    • 父组件userlist.vue

      <template>
      	<div>
      		<template slot-scope="scope">
                  <el-button type="primary" icon="el-icon-edit" @click="showAddDialog(scope.row.id)"></el-button>
              </template>
      		<!-- 编辑用户组件 -->
              <add-user :user="currentUser" ref="addUser" @updatelist="getUserList"></add-user>
      	</div>    
      </template>
      <script>
      // 添加用户组件
      import addUser from './addUser.vue'
      export default{
          components: {
          	addUser
        	},
          data() {
              return {
                currentUser: {},
                userlist: [],
                total:0
              }
          },
          methods:{
              // 1、发起请求 请求用户数据
              async getUserList() {
                const { data: res } = await this.$http.get('users', { params: this.queryInfo })
                if (res.meta.status === 200) {
                  this.userlist = res.data.users
                  this.total = res.data.total
                } else {
                  this.$message.error('获取用户数据失败')
                }
              },
              // 2、显示要修改的当前用户信息
      		async showAddDialog(uid) {
                const { data: res } = await this.$http.get(`users/${uid}`)
                this.currentUser = res.data
                this.$refs.addUser.showDialog()
              },
          }
      }
      </script>
      <style lang="less" scope>
      
      </style>
      
    • 子组件addUser.vue

      <template>
      	<el-dialog title="编辑用户信息" :visible.sync="dialogVisible" width="50%">
          <el-form :model="addUserForm" :rules="addUserRules" ref="addUserRef" label-width="100px" class="demo-ruleForm">
            <el-form-item label="用户名" prop="username">
              <el-input v-model="addUserForm.username" disabled></el-input>
            </el-form-item>
            <el-form-item label="邮箱" prop="email">
              <el-input v-model="addUserForm.email"></el-input>
            </el-form-item>
            <el-form-item label="手机号" prop="mobile">
              <el-input v-model="addUserForm.mobile"></el-input>
            </el-form-item>
          </el-form>
          <span slot="footer" class="dialog-footer">
            <el-button @click="hideDialog">取 消</el-button>
            <el-button type="primary" @click="saveUserInfo">确 定</el-button>
          </span>
        </el-dialog>
      </template>
      <script>
      import checkObj from '@/utils/check.js'
      export default{
          props: ['user'],
          	computed: {
              addUserForm() {
                	return this.user
              }
          },
          data() {
              return {
                dialogVisible: false,
                addUserRules: {
                  username: [
                    { required: true, message: '请输入用户名', trigger: 'blur' },
                    { min: 3, max: 10, message: '请输入3到10个字符', trigger: 'blur' }
                  ],
                  email: [
                    { required: true, message: '请输入邮箱', trigger: 'blur' },
                    { validator: checkObj.checkeEmail, trigger: 'blur' }
                  ],
                  mobile: [
                    { required: true, message: '请输入手机号', trigger: 'blur' },
                    { validator: checkObj.checkeMobile, trigger: 'blur' }
                  ]
               }
             }
          },
          methods: {
          showDialog() {
            this.dialogVisible = true
          },
          hideDialog() {
            this.dialogVisible = false
          },
          saveUserInfo() {
            this.$refs.addUserRef.validate(async valid => {
              if (valid) {
                var flag = true
                for (var key in this.addUserForm) {
                  if (this.addUserForm[key] !== this.user[key]) {
                    flag = false
                    if (flag) {
                      this.$message.warning('您的内容没有进行修改,请修改之后再提交')
                      return null
                    }
                  }
                }
                // 校验成功,请求保存
                const { data: res } = await this.$http.put(`users/${this.user.id}`, this.addUserForm)
                if (res.meta.status === 200) {
                  this.$message.success('更新成功')
                  this.$emit('updatelist')
                  this.hideDialog()
                } else {
                  this.$message.error('更新失败')
                }
              } else {
                this.$message.error('校验失败')
              }
            })
          }
        }
      }
      </script>
      <style lang="less" scope>
      
      </style>
      

4 项目总结

在这里插入图片描述

此电商管理系统是使用Vue+VueRouter+Axios+Element UI开发,通过本次项目实战,对于Vue跟Element UI的使用有了进一步的认知和掌握。

其中使用较频繁的就是组件之间的通信,此外还有其它重点操作,比如组件之间的引入,也就是模块化规范,使用的是ES6模块化规范。

  • ES6模块化规范
  • 组件间通信
  • 数据双向绑定
  • Element UI的使用
  • Flex布局