当前位置:   article > 正文

【超详细前后端项目搭建】前端vue3+ts项目(引入ElementPlus、Axios)、后端springboot搭建(创建接口操作mysql数据库)实现前后端联调_vue3+ts项目搭建

vue3+ts项目搭建


前言

项目为简易的全栈项目,其中前端项目使用到了vue3、ts、Element Plus、axios等技术栈;后端项目使用到了springboot、jdbc、mysql、maven等技术


附上开源代码gitee地址:
前端代码:https://gitee.com/smileagain-lg/vue3-element-demo.git
后端代码:https://gitee.com/smileagain-lg/spring-boot-demo.git

一、前端项目

1、使用vue脚手架创建项目

1.1检查vue版本

输入命令vue -V(V要大写),版本需要在4.5.1之后

1

1.2 使用vue脚手架创建项目

1、使用命令vue create vue3-element-demo 创建Vue项目。
2、通过上下键选择,选择 Manually select features,按回车,手动进行配置。
在这里插入图片描述
3、通过上下键选择,通过空格键选中(使用Babel,TypeScript,Router、Vuex、CSS Pre-processors、Linter / Formatter),按回车
在这里插入图片描述

4、选择vue3按回车
在这里插入图片描述
5、是否使用 Class 风格装饰器,此处输入【y】,然后回车确认;
在这里插入图片描述
6、Babel 和 TS 是否一起使用,用于现代模式、自动检测的 polyfills 和转译 JSX,此处输入【y】,然后回车确认;
在这里插入图片描述
7、是否使用history路由模式,此处输入【n】,即使用默认的 hash 模式,然后回车确认;
在这里插入图片描述
8、选择 CSS 预处理器,按上下方向键来选择,笔者习惯了 Less,然后回车确认;
在这里插入图片描述
9、选择第一个ESLint with error prevention only(仅具有错误预防功能的ESLint)
在这里插入图片描述
在这里插入图片描述
10、Babel、ESLint 等插件的配置是单独的文件进行配置,还是都在 package.json 文件里面?此处输选择 package.json 咯,并不想太多零散的配置文件,然后回车确认;
在这里插入图片描述
11、是否保存当前创建 Vue3 项目的特性配置,下次再创建 Vue 项目的时候,可以选择该特性,然后回车确认即可创建完成。这个可以不用保存,输入【n】,然后回车确认;
在这里插入图片描述12、项目创建中;
在这里插入图片描述
项目创建成功:
在这里插入图片描述
在这里插入图片描述
运行项目
在这里插入图片描述
在这里插入图片描述

2、删除项目多余文件,修改配置项目

2.1、删除以下文件

在这里插入图片描述

2.1、在views下创建index文件

在这里插入图片描述

<template>
    <div>我的首页</div>
</template>
  
<script lang="ts">
  export default {
    name: 'Index'
  }
</script>

<style scoped></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.2、修改router/index.ts路由文件:

在这里插入图片描述
编译时会有报错:
vue eslint报错:Component name “index“ should always be multi-word.eslintvue/multi-word-component-names
在这里插入图片描述
原因:除了根组件(App.vue)外,自定义组件名称应该由多单词组成,防止和html标签冲突。
解决方法,关闭eslint校验,重新编译就正常了
lintOnSave: false

在这里插入图片描述

2.3、修改App.vue文件:

在这里插入图片描述

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<style lang="less">
html,
body,
#app {
  width: 100%;
  height: 100%;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

运行效果:
在这里插入图片描述

2.4、初始化页面样式以及清除浮动

新建css/resset.css文件,并在index.html文件中引入,初始化样式
在这里插入图片描述

/**
 * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
 * http://cssreset.com
 */
 
 html, body, div, span, applet, object, iframe,
 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
 a, abbr, acronym, address, big, cite, code,
 del, dfn, em, img, ins, kbd, q, s, samp,
 small, strike, strong, sub, sup, tt, var,
 b, u, i, center,
 dl, dt, dd, ol, ul, li,
 fieldset, form, label, legend,
 table, caption, tbody, tfoot, thead, tr, th, td,
 article, aside, canvas, details, embed, 
 figure, figcaption, footer, header, hgroup, 
 menu, nav, output, ruby, section, summary,
 time, mark, audio, video{
   margin: 0;
   padding: 0;
   border: 0;
   font-size: 100%;
   font: inherit;
   font-weight: normal;
   vertical-align: baseline;
 }
 /* HTML5 display-role reset for older browsers */
 article, aside, details, figcaption, figure, 
 footer, header, hgroup, menu, nav, section{
   display: block;
 }
 ol, ul, li{
   list-style: none;
 }
 blockquote, q{
   quotes: none;
 }
 blockquote:before, blockquote:after,
 q:before, q:after{
   content: '';
   content: none;
 }
 table{
   border-collapse: collapse;
   border-spacing: 0;
 }
  
 /* custom */
 a{
   color: #7e8c8d;
   text-decoration: none;
   -webkit-backface-visibility: hidden;
 }
 ::-webkit-scrollbar{
   width: 5px;
   height: 5px;
 }
 ::-webkit-scrollbar-track-piece{
   background-color: rgba(0, 0, 0, 0.2);
   -webkit-border-radius: 6px;
 }
 ::-webkit-scrollbar-thumb:vertical{
   height: 5px;
   background-color: rgba(125, 125, 125, 0.7);
   -webkit-border-radius: 6px;
 }
 ::-webkit-scrollbar-thumb:horizontal{
   width: 5px;
   background-color: rgba(125, 125, 125, 0.7);
   -webkit-border-radius: 6px;
 }
 html, body{
   width: 100%;
   font-family: "Arial", "Microsoft YaHei", "黑体", "宋体", "微软雅黑", sans-serif;
 }
 body{
   line-height: 1;
   -webkit-text-size-adjust: none;
   -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
 }
 html{
   overflow-y: scroll;
 }
  
 /*清除浮动*/
 .clearfix:before,
 .clearfix:after{
   content: " ";
   display: inline-block;
   height: 0;
   clear: both;
   visibility: hidden;
 }
 .clearfix{
   *zoom: 1;
 }
  
 /*隐藏*/
 .dn{
   display: none;
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

index.html中引用:
在这里插入图片描述

3、引入ElementPlus 组件库

3.1、导入依赖包

npm i element-plus -D
在这里插入图片描述

第一种:全局挂载ElementPlus,在main.js中添加

在这里插入图片描述

第一种:按需导入组件

vue.config.js中引入(按需导入方式),添加如下配置
在这里插入图片描述

const { defineConfig } = require('@vue/cli-service');
const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack');
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers');
module.exports = defineConfig({
  transpileDependencies: true,
  //关闭eslint校验
  lintOnSave: false,
  configureWebpack: {
    plugins: [
      AutoImport({
        resolvers: [ElementPlusResolver()],
      }),
      Components({
        resolvers: [ElementPlusResolver()],
      }),
    ],
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在页面中使用ElementPlus组件

在这里插入图片描述
效果:
在这里插入图片描述

3、创建登录页面

在view下新建Login.vue文件

在这里插入图片描述

<template>
    <div class="container" :class="{ 'sign-up-mode': signUpMode }">
      <!-- form表单容器 -->
      <div class="form-container">
        <div class="signin-signup">
          <!-- 登录 -->
            <el-form
            ref="ruleFormRef"
            :model="loginUser"
            :rules="rules"
            label-width="100px"
            class="login-form sign-in-form"
            >
            <el-form-item label="邮箱" prop="email">
                <el-input v-model="loginUser.email" placeholder="Enter Email..." />
            </el-form-item>
            <el-form-item label="密码" prop="password">
                <el-input
                v-model="loginUser.password"
                type="password"
                placeholder="Enter Password..."
                />
            </el-form-item>
            <el-form-item>
                <el-button type="primary" class="submit-btn" @click="submitForm(ruleFormRef)">提交</el-button>
            </el-form-item>
            <!-- 找回密码 -->
            <el-form-item>
                <p class="tiparea">忘记密码<a>立即找回</a></p>
            </el-form-item>
            </el-form>
        </div>
      </div>
      <!-- 左右切换动画 -->
      <div class="panels-container">
        <div class="panel left-panel">
          <div class="content">
            <h3>Row,row,row your boat</h3>
            <p>Gentlely down the stream</p>
            <button @click="signUpMode = !signUpMode" class="btn transparent">
              注册
            </button>
          </div>
        </div>
        <div class="panel right-panel">
          <div class="content">
            <h3>Merrily,merrily,merrily,merrily,</h3>
            <p>Life is but a dream</p>
            <button @click="signUpMode = !signUpMode" class="btn transparent">
              登录
            </button>
          </div>
        </div>
      </div>
    </div>
  </template>

  <script lang="ts" setup>
  import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
  import type { FormInstance, FormRules } from 'element-plus'
  // Vue3语法糖
  // 通过解构getCurrentInstance,获取this,这里的this就是ctx
  // const { ctx } = getCurrentInstance()
  const { proxy } = getCurrentInstance()
  // 登录/注册模式
  const signUpMode = ref(false)
  const ruleFormRef = ref<FormInstance>()
  // 登录表单
  const loginUser = reactive({
    email: '',
    password: ''
  })
  // 校验规则
  const rules = reactive({
    email: [
          {
            required: true,
            type: 'email',
            message: 'email格式错误',
            trigger: 'blur'
          }
        ],
        password: [
          { required: true, message: '密码不得为空', trigger: 'blur' },
          { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' }
        ]
      })
      // 触发登录方法
      const submitForm  = (formEl: FormInstance | undefined) => {
        if(!formEl!) return
        formEl.validate((valid) => {
          if (valid) {
            console.log('submit!')
            const data = proxy.$http.getUserPassword(loginUser)
            console.log('data', data)
          } else {
            console.log('error submit!')
            return false
          }
        })
      }
  </script>
  <style scoped>
  .container {
    position: relative;
    width: 100%;
    min-height: 100vh;
    background-color: #fff;
    overflow: hidden;
  }
  .form-container {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }
  .signin-signup {
    position: relative;
    top: 50%;
    left: 75%;
    transform: translate(-50%, -50%);
    width: 44%;
    transition: 1s 0.7s ease-in-out;
    display: grid;
    grid-template-columns: 1fr;
    z-index: 5;
  }

  /* 左右切换动画 */
  .social-text {
    padding: 0.7rem 0;
    font-size: 1rem;
  }

  .social-media {
    display: flex;
    justify-content: center;
  }

  .social-icon {
    height: 46px;
    width: 46px;
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0 0.45rem;
    color: #333;
    border-radius: 50%;
    border: 1px solid #333;
    text-decoration: none;
    font-size: 1.1rem;
    transition: 0.3s;
  }

  .social-icon:hover {
    color: #4481eb;
    border-color: #4481eb;
  }

  .btn {
    width: 150px;
    background-color: #5995fd;
    border: none;
    outline: none;
    height: 49px;
    border-radius: 49px;
    color: #fff;
    text-transform: uppercase;
    font-weight: 600;
    margin: 10px 0;
    cursor: pointer;
    transition: 0.5s;
  }

  .btn:hover {
    background-color: #4d84e2;
  }
  .panels-container {
    position: absolute;
    height: 100%;
    width: 100%;
    top: 0;
    left: 0;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }

  .container:before {
    content: '';
    position: absolute;
    height: 2000px;
    width: 2000px;
    top: -10%;
    right: 48%;
    transform: translateY(-50%);
    background-image: linear-gradient(-45deg, #4481eb 0%, #04befe 100%);
    transition: 1.8s ease-in-out;
    border-radius: 50%;
    z-index: 6;
  }

  .image {
    width: 100%;
    transition: transform 1.1s ease-in-out;
    transition-delay: 0.4s;
  }

  .panel {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: space-around;
    text-align: center;
    z-index: 6;
  }

  .left-panel {
    pointer-events: all;
    padding: 3rem 17% 2rem 12%;
  }

  .right-panel {
    pointer-events: none;
    padding: 3rem 12% 2rem 17%;
  }

  .panel .content {
    color: #fff;
    transition: transform 0.9s ease-in-out;
    transition-delay: 0.6s;
  }

  .panel h3 {
    font-weight: 600;
    line-height: 1;
    font-size: 1.5rem;
  }

  .panel p {
    font-size: 0.95rem;
    padding: 0.7rem 0;
  }

  .btn.transparent {
    margin: 0;
    background: none;
    border: 2px solid #fff;
    width: 130px;
    height: 41px;
    font-weight: 600;
    font-size: 0.8rem;
  }

  .right-panel .image,
  .right-panel .content {
    transform: translateX(800px);
  }

  /* ANIMATION */

  .container.sign-up-mode:before {
    transform: translate(100%, -50%);
    right: 52%;
  }

  .container.sign-up-mode .left-panel .image,
  .container.sign-up-mode .left-panel .content {
    transform: translateX(-800px);
  }

  .container.sign-up-mode .signin-signup {
    left: 25%;
  }

  .container.sign-up-mode form.sign-up-form {
    opacity: 1;
    z-index: 2;
  }

  .container.sign-up-mode form.sign-in-form {
    opacity: 0;
    z-index: 1;
  }

  .container.sign-up-mode .right-panel .image,
  .container.sign-up-mode .right-panel .content {
    transform: translateX(0%);
  }

  .container.sign-up-mode .left-panel {
    pointer-events: none;
  }

  .container.sign-up-mode .right-panel {
    pointer-events: all;
  }

  @media (max-width: 870px) {
    .container {
      min-height: 800px;
      height: 100vh;
    }
    .signin-signup {
      width: 100%;
      top: 95%;
      transform: translate(-50%, -100%);
      transition: 1s 0.8s ease-in-out;
    }

    .signin-signup,
    .container.sign-up-mode .signin-signup {
      left: 50%;
    }

    .panels-container {
      grid-template-columns: 1fr;
      grid-template-rows: 1fr 2fr 1fr;
    }

    .panel {
      flex-direction: row;
      justify-content: space-around;
      align-items: center;
      padding: 2.5rem 8%;
      grid-column: 1 / 2;
    }

    .right-panel {
      grid-row: 3 / 4;
    }

    .left-panel {
      grid-row: 1 / 2;
    }

    .image {
      width: 200px;
      transition: transform 0.9s ease-in-out;
      transition-delay: 0.6s;
    }

    .panel .content {
      padding-right: 15%;
      transition: transform 0.9s ease-in-out;
      transition-delay: 0.8s;
    }

    .panel h3 {
      font-size: 1.2rem;
    }

    .panel p {
      font-size: 0.7rem;
      padding: 0.5rem 0;
    }

    .btn.transparent {
      width: 110px;
      height: 35px;
      font-size: 0.7rem;
    }

    .container:before {
      width: 1500px;
      height: 1500px;
      transform: translateX(-50%);
      left: 30%;
      bottom: 68%;
      right: initial;
      top: initial;
      transition: 2s ease-in-out;
    }

    .container.sign-up-mode:before {
      transform: translate(-50%, 100%);
      bottom: 32%;
      right: initial;
    }

    .container.sign-up-mode .left-panel .image,
    .container.sign-up-mode .left-panel .content {
      transform: translateY(-300px);
    }

    .container.sign-up-mode .right-panel .image,
    .container.sign-up-mode .right-panel .content {
      transform: translateY(0px);
    }

    .right-panel .image,
    .right-panel .content {
      transform: translateY(300px);
    }

    .container.sign-up-mode .signin-signup {
      top: 5%;
      transform: translate(-50%, 0);
    }
  }

  @media (max-width: 570px) {
    form {
      padding: 0 1.5rem;
    }

    .image {
      display: none;
    }
    .panel .content {
      padding: 0.5rem 1rem;
    }
    .container {
      padding: 1.5rem;
    }

    .container:before {
      bottom: 72%;
      left: 50%;
    }

    .container.sign-up-mode:before {
      bottom: 28%;
      left: 50%;
    }
  }

  /* 控制login & register显示 */
  form {
    padding: 0rem 5rem;
    transition: all 0.2s 0.7s;
    overflow: hidden;
  }

  form.sign-in-form {
    z-index: 2;
  }

  form.sign-up-form {
    opacity: 0;
    z-index: 1;
  }

  /* register */
  .loginForm,
  .registerForm {
    margin-top: 20px;
    background-color: #fff;
    padding: 20px 40px 20px 20px;
    border-radius: 5px;
    box-shadow: 0px 5px 10px #cccc;
  }

  .submit-btn {
    width: 100%;
  }
  .tiparea {
    text-align: right;
    font-size: 12px;
    color: #333;
    width: 100%;
  }
  .tiparea a {
    color: #409eff;
  }
  </style>


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468

在这里插入图片描述
在这里插入图片描述

4、封装并使用 Axios

4.1、安装Axios

npm i axios
在这里插入图片描述

4.2、安装NProgress顶部进度条

npm i --save-dev @types/nprogress
在这里插入图片描述

4.3、封装请求拦截

在 src 目录新建 utils 文件夹,再新建 requestUtil.ts 文件,写上以下代码
在这里插入图片描述

import axios from 'axios'
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import { ElMessage } from 'element-plus'

const http = axios.create({
  baseURL: 'http://localhost:9000',
  timeout: 300 * 1000, // 请求超时时间设置为300秒
})

const NETWORK_ERROR = '网络错误,请联系开发人员'

/**
 * 请求拦截器
 */
http.interceptors.request.use((req) => {
  console.log('请求拦截器 =>', req)
  Nprogress.start()
  return req;
}, (error) => {
  Nprogress.done()
  return Promise.reject(error);
});

/**
 * 响应拦截器
 */
http.interceptors.response.use(function (res) {
  console.log('响应拦截器 =>', res)
  Nprogress.done()
  if (res.status == 200) {
    return res.data
  } else {
    ElMessage.error((NETWORK_ERROR))
    return Promise.reject(NETWORK_ERROR)
  }
});

export default http

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4.4、前端设置跨域

在vue.config.js中配置如下代码:
在这里插入图片描述

const { defineConfig } = require('@vue/cli-service');
// const AutoImport = require('unplugin-auto-import/webpack');
// const Components = require('unplugin-vue-components/webpack');
// const { ElementPlusResolver } = require('unplugin-vue-components/resolvers');
module.exports = defineConfig({
  transpileDependencies: true,
  //关闭eslint校验
  lintOnSave: false,
  // ElementPlus按需导入方式
  // configureWebpack: {
  //   plugins: [
  //     AutoImport({
  //       resolvers: [ElementPlusResolver()],
  //     }),
  //     Components({
  //       resolvers: [ElementPlusResolver()],
  //     }),
  //   ],
  // }
  devServer: {
    open: true,
    host: 'localhost',
    port: 8080,
    https: false,
    // 设置跨域
    proxy: {
      '/api': {
        target: 'http://localhost:9000',
        ws: true,
        changeOrigin: true,
        pathRewrite: {
          '^api': ''
        }
      }
    }
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

4.5、配置接口api

在src目录下创建api文件夹,里面创建index.ts
在这里插入图片描述

import http from '@/utils/requestUtils'

 
export default {
  /**
   * 根据用户邮箱、密码查询用户信息
   */
  getUserPassword(data: any) {
    return http.post(
      '/api/getUserPassword',
      data,
      {
        headers: {
          'Content-Type': 'application/json'
        },
      }
    )
  },
 
  /**
   * 保存用户信息
   */
  saveUser(data: any) {
    return http.post(
      '/api/saveUser',
      data,
      {
        headers: {
          'Content-Type': 'application/json'
        },
      }
    )
  },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

4.6、将http请求全局封装

在 main.ts 文件引入HTTP请求工具并配置为全局方法
在这里插入图片描述

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import axios from 'axios'
import apiServe from '@/api'


const app = createApp(App)

app.use(store)
app.use(router)
app.use(ElementPlus)
app.mount('#app')

app.config.globalProperties.$http = apiServe
app.config.globalProperties.$axios = axios
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

二、后端项目

1、检查JDK和maven的安装版本

在cmd输入 java -version
和mvn -v检查对应的安装情况
在这里插入图片描述

2、创建springboot项目

通过idea的spring initializr创建工程,不选择maven而是选择spring initializr快捷创建。然后去勾选相关依赖。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3、创建springboot项目成功

项目创建成功

在这里插入图片描述

4、配置maven和maven库

配置本地maven库
在这里插入图片描述

5、加载maven库

在这里插入图片描述

6、创建application.yml

在resources下面新建application.yml,并配置数据库名,密码,以及端口,端口尽量不要使用8080,避免和前端端口相同了

在这里插入图片描述
注释掉另外一个配置
在这里插入图片描述

# mysql
spring:
  datasource:
    #MySQL配置
    driverClassName:  com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/easyproject?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
    #数据库名和密码
    username: root
    password: 920724

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.demo.model
server:
  port: 9000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

7、运行项目

在这里插入图片描述
项目运行成功,端口9000
在这里插入图片描述
在这里插入图片描述

8、新建WebConfig文件处理跨域

创建utils文件夹,在utils文件夹下创建WebConfig,并添加以下配置
在这里插入图片描述

package com.springboot.userlogin.springbootdemo.utils;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

// 使用注解说明是全局配置类
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter { // 继承跨域请求的类

    @Override
    public void addCorsMappings(CorsRegistry registry) { // 跨域处理的方法
        registry.addMapping("/**") // 任意访问都允许跨域
                .allowedOrigins("http://localhost:8080", "null") // 跨域来源
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") // 跨域请求类型
                .maxAge(3600) // 超时时间
                .allowCredentials(true); // 允许携带信息
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

alt+insert快捷键可以弹出
在这里插入图片描述

9、使用idea连接mysql

mysql安装配置方法可以参照我写的另外一个文档:
https://blog.csdn.net/m0_47791238/article/details/134811414?spm=1001.2014.3001.5501

在这里插入图片描述
在这里插入图片描述
输入mysql用户名和密码:
在这里插入图片描述
在这里插入图片描述

10、在pom文件添加lombok依赖

目的:为了使用@Data注解
在这里插入图片描述

<dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
 </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

11、创建bean文件夹,用于放置实体对象

在项目中创建bean文件夹,新建user实体类,使用Data注解,创建构造方法和get\set方法
在这里插入图片描述

package com.springboot.userlogin.springbootdemo.bean;

import lombok.Data;

@Data
public class User {
    private int id;
    private String username;
    private String password;
    private String email;
    private String role;
    private boolean state;

//    public User() {
//    }

//    public User(String username, String password, String email, String role, boolean state) {
//        this.username = username;
//        this.password = password;
//        this.email = email;
//        this.role = role;
//        this.state = state;
//    }
//
//    public int getId() {
//        return id;
//    }
//
//    public String getUsername() {
//        return username;
//    }
//
//    public String getPassword() {
//        return password;
//    }
//
//    public String getEmail() {
//        return email;
//    }
//
//    public String getRole() {
//        return role;
//    }
//
//    public boolean getState() {
//        return state;
//    }
//
//    public void setId(int id) {
//        this.id = id;
//    }
//
//    public void setUsername(String username) {
//        this.username = username;
//    }
//
//    public void setPassword(String password) {
//        this.password = password;
//    }
//
//    public void setEmail(String email) {
//        this.email = email;
//    }
//
//    public void setRole(String role) {
//        this.role = role;
//    }
//
//    public void setState(boolean state) {
//        this.state = state;
//    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

12、查看构造成功成功的实体对象

快捷键 alt + 7
在这里插入图片描述

13、创建controller接口

在这里插入图片描述

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {
    @Autowired
    UserDao userDao;

    @PostMapping("/api/getUserPassword") // @RequestMapping注解创建接口
    public String userLogin(@RequestBody User user) { // @RequestBody注解方便找到user实体

        System.out.println("User : " + user);
        String str = "error";
        int count = userDao.getUserByMassage(user.getEmail(), user.getPassword());
        if (count > 0) {
            str = "ok";
        }
        return str;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

14、创建dao接口

在这里插入图片描述

package com.springboot.userlogin.springbootdemo.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface UserDao {

    int getUserByMassage(@Param("email") String email, @Param("password") String password);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

15、创建mapper映射文件

在resources下面创建mapper文件夹,用于存放数据库的映射文件
在mapper文件夹下新建UserMapper.xml
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--对应dao层接口文件的目录-->
<mapper namespace="com.springboot.userlogin.springbootdemo.dao.UserDao">
    <!--  id值为UserDao接口方法名; -->
    <select id="getUserByMassage" resultType="java.lang.Integer">
       	SELECT count(id) FROM easyUser
       	WHERE email=#{email} AND password=#{password}
    </select>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

三、测试前后端功能

1、前端页面接口请求

在这里插入图片描述

2、后端控制台日志打印

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/524776
推荐阅读
相关标签
  

闽ICP备14008679号