当前位置:   article > 正文

超详细的vue+element后台项目搭建教程,你要悄悄学习,然后秃头,震惊所有人_vue+element学习

vue+element学习


记录一下我的工作总结吧,回顾我老大第一次带我写后台,保姆级教程,学不会那就秃头吧。

本次项目基于vue.js,react.js可以看这链接

一、项目框架选择

前端框架:vue.js,element-ui/iview,脚手架工具vue-cli3.0+

准备工作

1.官网下载安装node.js,一路回车默认安装就好。附链接:node.js官网
安装完成后打开cmd/powershell查看
在这里插入图片描述

2.安装脚手架工具vue-cli,现在的话使用3.0以上版本的偏多。vue3.0以上有vite支持,有兴趣的可以查看我以前探坑写过的一篇文章vite构建vue3.0项目踩坑
本文采用vue2.6.x版本加最新的vue-cli。

3.全局安装vue-cli(npm与cnpm网上很多,不多讲了)

cnpm install vue-cli -g
  • 1

安装后输入vue -V 查看是否安装成功 (注意这里是大写的V)
在这里插入图片描述
若在此之前已经安装过vue-cli3.0以下版本的,需要先卸载低版本(uninstall),再安装。
cnpm install vue-cli -g

4.我们下一个网易云音乐 NodeJS 版 API
下载到本地后npm i,node app.js运行方便用于我们后面的api封装和调试。

二、 使用脚手架构建项目

至此,就像是打王者,配置好了铭文,装备方案,快捷消息,约上了野王,法王组队,可以开始进入游戏了。
在我们的学习盘新建一个文件夹study,打开powershell,输入vue create my-vue其中 my-vue 是我们创建的项目的名字。
创建好之后如下图:
在这里插入图片描述
按照提示,输入cd my-vue进入项目,再输入npm run serve就可以启动项目了。成功启动之后会看到如下页面:一般默认为 http://localhost:8080/
在这里插入图片描述

至此,项目构建完成了,接着开始我们的开发。

三、 项目开发

3.1安装必要的依赖

安装element-ui,axios,vue-router,less/sass/scss,vuex(根据实际情况看是否需要用到)。
element-ui: element官方
axios: axios使用说明
vue-router: vue-vouter官方
less: npm install less less-loader --save
less文档
vuex: vuex官方文档

对public下的index.html文件简单修改一下默认样式,根据自己实际需要修改或引入reset.css
在这里插入图片描述
cli3.0以后构建的项目需要自己配置vue.config.js文件
内容大致如下

var path = require('path')

const ENV = process.env.NODE_ENV
// console.log(ENV)

function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  //关闭eslint规范
  lintOnSave: false,
  devServer: {
    // port: 8080,
    // host: 'localhost',
    // 代理
    proxy: {
      '/api': {
        target: 'http://localhost:8090',
        pathRewrite: {
          '^/api': '/hock'
        }
      }
    }
  },
  chainWebpack: config => {
    // 参考 https://cli.vuejs.org/zh/guide/webpack.html#%E9%93%BE%E5%BC%8F%E6%93%8D%E4%BD%9C-%E9%AB%98%E7%BA%A7
    // git: https://github.com/neutrinojs/webpack-chain
    // 通用配置区域

    // 配置别名
    config.resolve.alias
      .set('@src', resolve('src')) // 代码根目录
      .set('@components', resolve('src/components')) // 代码组件目录
      .set('@api', resolve('src/api')) // 代码接口层根目录
      .set('@style', resolve('src/style')) // 代码通用样式目录
      .set('@page', resolve('src/page')) // 业务代码目录
      .set('@assets', resolve('src/assets')) // 资源目录
      .set('@public', resolve('public'))
    // 环境配置区域
    if (ENV == 'development') {
      // 开发环境配置
    } else if (ENV == 'production') {
      // 部署环境配置
    }
  }
}

  • 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

3.2构建登录页面

在src目录下新建router文件夹和views文件夹,分别创建index.js和login.vue文件。
在这里插入图片描述
路由分hash模式和history模式,我喜欢干净点,不带#号的,采用history模式。
在index文件中写入:

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

const routes = [{
    path: '',
    name: 'login',
    component: (resolve) => require(['../views/login.vue'], resolve)
}]

const router = new Router({
    mode: 'history',
    routes,
})

export default router
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在main.js文件引入router(顺便把element也引了吧,反正后面要用)

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

再对我们的App.vue文件修改一下,删除#app下的所有,引入router-view

<template>
  <div id="app">
    <router-view />
  </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5

登录应该都会写,先来个简单的看效果:
login.vue

<style lang="less">
.login {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>
<template>
    <div class="login">
        这是登录
    </div>
</template>
<script>
export default {
    data() {
        return {

        }
    },
    created() {},
    mounted() {}
}
</script>
  • 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

如图所示
在这里插入图片描述
稍微改一改,做个简单的登录
在这里插入图片描述

点击登录跳转到我们的主界面,需要在views下新增home.vue文件,并添加对应路由(router/index.js)

this.$router.replace({ name: "home" });
  • 1

3.3构建home界面(header,aside-menu,main-container)

使用element的Container 布局容器结合菜单组件一起使用,完成之后如下:
在这里插入图片描述
这里的el-aside和el-menu需要自己绑定一下样式,menu组件都是element的被我删减了,代码如下

<style lang="less" scoped>
.home-container {
  width: 100%;
  height: 100%;
  .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    .header-right {
      display: flex;
      align-items: center;
      .icon-box {
        width: 46px;
        height: 60px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 24px;
      }
      .avatar {
        width: 60px;
        height: 60px;
        display: flex;
        align-items: center;
        justify-content: center;
        img {
          width: 50px;
          height: 50px;
          display: block;
          border-radius: 25px;
          cursor: pointer;
        }
      }
      .nickname {
          height: 60px;
          line-height: 60px;
          padding: 0 10px;
          box-sizing: border-box;
      }
    }
  }
  .main {
    background: #f6f8f9;
  }
}
</style>
<style lang="less">
.home-container .el-container {
  height: 100vh;
}
.el-aside {
    transition: all 0.5s ease;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
</style>
<template>
  <section class="home-container">
    <el-container>
      <el-aside :width="`${isCollapse ? '66px' : '202px'}`">
        <el-menu
          default-active="1-4-1"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose"
          :collapse="isCollapse"
        >
          <el-submenu index="1">
            <template slot="title">
              <i class="el-icon-location"></i>
              <span slot="title">导航一</span>
            </template>
          </el-submenu>
        </el-menu></el-aside
      >
      <el-container>
        <el-header class="header">
          <div class="header-left">
            <i
              v-show="!isCollapse"
              @click="isCollapse = !isCollapse"
              class="el-icon-s-fold"
              style="cursor: pointer"
            />
            <i
              v-show="isCollapse"
              @click="isCollapse = !isCollapse"
              class="el-icon-s-unfold"
              style="cursor: pointer"
            />
          </div>
          <div class="header-right">
            <div class="icon-box">
              <el-badge :value="6" class="item">
                <i class="el-icon-message" />
              </el-badge>
            </div>
            <div class="avatar">
              <img class="avatar" src="../assets/avatar_01.png" />
            </div>
            <div class="nickname">五更月</div>
            <div class="icon-box">
              <i class="el-icon-arrow-down" />
            </div>
          </div>
        </el-header>
        <el-main class="main">
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </section>
</template>
<script>
export default {
  data() {
    return {
      isCollapse: false,
    };
  },
  created() {},
  mounted() {},
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
  },
};
</script>
  • 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

可以把aside,header单独拎出来做组件,后面组件化再说吧。
接下来就是我们的菜单跳转了,点击menu跳转对应的界面。

3.4菜单跳转

这里涉及到子路由children了。这里我以网易云音乐api为调用举例,页面命名采用music。
在views文件夹下新建music文件夹,新建index.vue,search.vue,playMusic.vue三个文件。

playmusic和search先不写内容,写上两个文案即可,用于验证路由跳转是否成功。

在这里插入图片描述
index.vue文件写入

<style lang="less">
.music-container {
  position: relative;
}
</style>
<template>
  <div class="music-container">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'music',
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

然后配置路由文件index.js,基于home往下配置children

const routes = [{
    path: '',
    name: 'login',
    component: (resolve) => require(['../views/login.vue'], resolve)
},{
    path: '/home',
    name: 'home',
    component: (resolve) => require(['../views/home.vue'], resolve),
    children: [
        {
            path: '/home/music',
            name: 'music',
            // redirect: '/home/music/findMusic',
            component: (resolve) => require(['../views/music/index.vue'], resolve),
            children: [
                {
                    path: '/home/music/findMusic',
                    name: 'searchMusic',
                    component: (resolve) => require(['../views/music/search.vue'], resolve),
                },
                {
                    path: '/home/music/playMusic',
                    name: 'playMusic',
                    component: (resolve) => require(['../views/music/playMusic.vue'], resolve),
                }
            ]
        }
    ]
}]
  • 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

OK,回到我们的home.vue文件,配置菜单开始验证跳转
给el-menu添加router属性,暂时先不考虑三级菜单的情况对menu稍微改造一下:

        <el-menu
          :default-active="activePath"
          class="el-menu-vertical-demo"
          router
          @open="handleOpen"
          @close="handleClose"
          :collapse="isCollapse"
        >
          <el-submenu index="1" v-for="(item, index) in menuList" :key="index">
            <template slot="title">
              <i :class="`${item.icon}`"></i>
              <span>{{ item.title }}</span>
            </template>
            <el-menu-item-group>
              <el-menu-item
                v-for="items in item.submenu"
                :key="items.id"
                :index="`${items.path}`"
                >{{ items.title }}</el-menu-item
              >
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

默认展开在mounted添加一段代码

this.activePath = this.$route.path
  • 1

menuList配置:

      menuList: [
        {
          name: "findMusic",
          title: "网易云",
          icon: "el-icon-service",
          id: "0",
          submenu: [
            {
              name: "findMusic",
              title: "发现音乐",
              icon: "",
              id: "0-1",
              path: "/home/music/findMusic",
            },
            {
              name: "playMusic",
              title: "播放音乐",
              icon: "",
              id: "0-2",
              path: "/home/music/playMusic",
            },
          ],
        },
      ],
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

好了,完成之后打开可以正常跳转了,效果如下:
在这里插入图片描述
到此,就可以按照这个顺序编写页面样式开发了,接下来说一下axios的封装和使用。

四、axios封装和api使用

4.1封装(这里只做最基本的封装,根据实际应用情况调整)

在src下新建api文件夹,创建request.js文件

import axios from 'axios'
// import store from '@src/store' 暂时不用
// import { HTTP_CODE } from '@lib/const.js'  暂时不用
import Vue from 'vue'
import Router from '@src/router'
// import { LOGIN_OUT } from '@src/store/mutation-types'  暂时不用
// import auth from '@lib/utils/auth.js'
const env = process.env.NODE_ENV
// 根据域名判断是否测试环境;替换vue.manageplatform.com为你的线上域名
function IsTest() {
  let hostUrl = window.location.href
  return hostUrl.indexOf('vue.manageplatform.com') == -1
}
//请求的接口地址--自定义
const HOST =
  env === 'development'
    ? 'http://localhost:3000'
    : IsTest() == true
    ? 'https://api.mtnhao.com/'
    : 'https://api.mtnhao.com/'

const CancelToken = axios.CancelToken
const source = CancelToken.source()

const request = axios.create({
  baseURL: HOST,
  timeout: 30000
})

request.defaults.headers.post['Content-Type'] = 'application/json'

request.interceptors.request.use(config => {
  if (config.cancel) {
    config.cancelToken = source.token
    source.cancel()
  }

  config.url = subSplash(config.url)
  // token验证 -- 根据自己的实际情况写入,我这里后端需要每次请求默认带上token
//   if (store.getters.isLogin == true) {
//     const token = store.state.user.user.token
//     if (token && config.noToken !== true) {
//       if (config.method === 'get') {
//         config.params = { ...config.params, token }
//       } else if (config.method === 'post') {
//         if (config.headers['Content-Type'] == 'multipart/form-data') {
//           config.data.append('token', token)
//         } else {
//           config.data = { ...config.data, token }
//         }
//       }
//     }
//   }

//   if (config.needSign) {
//     if (!config.data) {
//       config.params = auth.Auth(config.params)
//     } else {
//       config.data = auth.Auth(config.data)
//     }
//   }

  return config
})

request.interceptors.response.use(
  response => {
    // 错误统一拦截--根据自己的实际情况来修改
    if (parseInt(response.data.status) === 403) {
     // store.commit(LOGIN_OUT) // vuex登出处理
      new Vue().$message.error({
        message: '登录已过期,请重新登录',
        onClose: () => {
          Router.push({
            name: 'login'
          })
        }
      })
      return Promise.reject(response.data.msg)
    }
    return response
  },
  error => {
    return Promise.reject(error)
  }
)

// 删除restapi末尾的反斜杠
function subSplash(url) {
  return url.endsWith('/') ? url.slice(0, -1) : url
}

export default request
  • 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

4.2接口封装+调用

首先启动我们下好的网易云api,成功后运行在 http://localhost:3000 端口
对应我们views文件夹下的music,在api文件夹下也创建一个music的文件夹,创建search.js文件,写入代码

import request from '../request'

// 搜索歌曲
const SearchSongs = ({ pageSize, start, keywords }) => {
    return request
        .get('/search', {
            params: {
                limit: pageSize,
                offset: start,
                keywords,
            },
        })
        .then((result) => {
            return result.data
        })
}

export default {
    SearchSongs
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

对search.vue简单做个搜索功能

    <div class="search-bar">
      <div class="search-title">{{ keywords }}的搜索结果:</div>
      <el-input
        class="search-input"
        v-model="keywords"
        placeholder="请输入"
        @change="SearchInfo"
      ></el-input>
    </div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

引入api
import searchApi from "../../api/music/search";

  methods: {
    SearchInfo() {
      searchApi
        .SearchSongs({
          pageSize: 10,
          start: 0,
          keywords: this.keywords,
        })
        .then((res) => {
          console.log("res", res);
        });
    },
  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

完成之后结果如下:
在这里插入图片描述
好啦,基本流程大致如下,后面再更新vuex,组件封装。

五、vuex使用(2021.08.11)

这里有一篇很详细的使用介绍可以参考;可以尝试把搜索出来的结果存到store试手。
vue中使用vuex(超详细)

六、组件封装(2021.08.11)

拿menu举例,
把el-menu剪切出来,到component文件夹下新建home文件夹,再新建menu.vue,
创建好vue模板后往template粘贴。

<style lang="less" scoped>
</style>
<template>
  <el-menu
    :default-active="activePath"
    class="el-menu-vertical-demo"
    router
    @open="handleOpen"
    @close="handleClose"
    :collapse="isCollapse"
  >
    <el-submenu
      :index="`${index + 1}`"
      v-for="(item, index) in menuList"
      :key="index"
    >
      <template slot="title">
        <i :class="`${item.icon}`"></i>
        <span>{{ item.title }}</span>
      </template>
      <el-menu-item-group>
        <el-menu-item
          v-for="items in item.submenu"
          :key="items.id"
          :index="`${items.path}`"
          >{{ items.title }}</el-menu-item
        >
      </el-menu-item-group>
    </el-submenu>
  </el-menu>
</template>
<script>
export default {
  props: {
    menuList: {
      type: Array,
      default: [],
    },
    isCollapse: {
      type: Boolean,
      default: false,
    },
    activePath: {
      type: String,
      default: "/home/music/findMusic",
    },
  },
  data() {
    return {};
  },
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
  },
};
</script>
  • 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

在home.vue文件使用组件

      <el-aside :width="`${isCollapse ? '66px' : '202px'}`">
        <home-menu :isCollapse="isCollapse" :menuList="menuList" :activePath="activePath"></home-menu>
      </el-aside>
  • 1
  • 2
  • 3
import HomeMenu from "../components/home/menu.vue"
  components: {
    HomeHeader,
    HomeMenu
  },
  • 1
  • 2
  • 3
  • 4
  • 5

完成之后依然OK
在这里插入图片描述

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

闽ICP备14008679号