大约 9 分钟

1 创建项目

  1. 局部安装 vue-cli 4.5.15
pnpm init
pnpm add -D @vue/cli@4.5.15     # 指定版本 4.5.15 安装
npx vue -V
@vue/cli 4.5.15
npx vue create .
> Default ([Vue 2] babel, eslint)
  1. 配置自动打开浏览器 新建 vue.config.js

    module.exports = {
      devServer: {
        open: true
      }
    }
    
    pnpm serve
    

2 elementui 官网 https://element.eleme.cn/#/zh-CNopen in new window

  1. 安装包
pnpm install element-ui

2.1 element官网 → 组件 → 完整引入

// 在 main.js 中写入以下内容
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);
  1. 项目里多余的删一删, 清一清

改 components 下 HelloWorld.vue 为 Home.vue 删除 <script>, <div> <style> 中多余的内容 修改 App.vue 中 <template>, <script>, <style> 组件相关部分 在 Home.vue 中加入

<el-button>hello</el-button>

2.2 element官网 → 组件 → 按需引入

pnpm add -D babel-plugin-component
  • 修改 babel.config.js
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
  1. 在 main.js 中引入
import Button from 'element-ui'
Vue.use(Button)
  1. 抽取到模块 plugins/element.js (不在 src 里, 在src外)
// plugins/element.js
import Vue from 'vue'
import { Button, Tag } from 'element-ui'
Vue.use(Button)
Vue.use(Tag)

// Home.vue
<el-tag>标签</el-tag>

// main.js
import '../plugins/element'

3 预处理及样式重置

3.1 css 预处理器 sass

cnpm i sass-loader@7 node-sass@4 -S
pnpm add sass-loader@7 node-sass@4
  • 使用
// Home.vue
<style lang="scss">
.hello {
  background: yellow;
  .el-button {
    color: red;
  }

3.2 css 预处理器 less

cnpm i less@3 less-loader@7 -S
pnpm add less@3 less-loader@7 -S
  • 使用
// Home.vue
<style lang="less">

3.3 使用 reset.css 重置样式

  1. 从官网复制open in new window
  2. 创建 assets/css/reset.css , 粘贴
  3. App.vue 使用
<style lang="scss">
@import url('./assets/css/reset.css');

4 图标库

  1. 图标库中文open in new window
pnpm add -D font-awesome
  1. 使用
// main.js
import 'font-awesome/css/font-awesome.min.css'

// Home.vue
<i class="fa fa-users"></i>

5 请求 axios

pnpm add axios
// main.js
import axios from 'axios'

Vue.prototype.axios = axios // 挂载到原型, 可以全局使用

6 路由

pnpm add vue-router@3.5.3
  1. 配置, 新建 src/router/index.js
// src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../components/Home.vue'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      component: Home
    }
  ],
  mode: 'history'
})

// main.js
import router from './router'

new Vue({
  router,

// App.vue  注释掉 Home 组件相关, 添加如下内容
<router-view></router-view>
  1. 路由懒加载 与 异步组件
// src/router/index.js
component: () => import('@/components/Home')  // 路由懒加载
component: resolve => require(['@/components/Home'], resolve) // 异步组件

7 登录页面

  1. 创建 src/components/Login.vue,

产生 template script style 找一个 form 表单, 拿一个 card 包裹

7.1 Login01.vue 简单检验和登录

<template>
  <div class="login">
    <el-card class="box-card">
      <div slot="header" class="clearfix">
        <span>通用后台管理系统</span>
      </div>
      <el-form label-width="80px" :model="form" ref="form">
        <el-form-item label="用户名" prop="username"
        :rules="[
          {requie: true, message: '请输入用户名', trigger: 'blur'},
          {min:4, max: 10, message: '长度在 4-10 位字符之间', trigger: 'blur'}
        ]">
          <el-input v-model="form.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password"
        :rules="[
          {required: true, message: '请输入密码', trigger: 'blur'},
          {min: 6, max: 12, message: '长度在 6-12 位字符', trigger: 'blur'}
        ]">
          <el-input type="password" v-model="form.password"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="login('form')">登录</el-button>
        </el-form-item>
      </el-form>
    </el-card>

  </div>
</template>

<script>
export default {
  data() {
    return {
      form: {
        username: '',
        password: '',
      }
    }
  },
  methods: {
    login(form) {
      this.$refs[form].validate( valid => {
        if (valid) {
          console.log(this.form)
          this.axios.post('http://127.0.0.1:3007/login', this.form)
            .then(res=> {
              console.log(res)  // F12 看一下返回的 data: 有 status, token, username, message
              if(res.data.status === 200) {
                localStorage.setItem('username', res.data.username)
                this.$message({message: res.data.message, type: 'success'})
                this.$router.push('/home')
              }
            })
            .catch( err => { console.error(err)})
        } else {
          console.error(this.form)
        }
      })
    }
  }
}
</script>

<style lang="scss">
.login {
  width: 100%;
  height: 100%;
  position: absolute;
  background: #409EFF;
  .box-card {
    width: 450px;
    margin: 200px auto;
    .el-card__header {
      font-size: 33px;
    }
    .el-button {
      width: 100%;
    }
  }
}
</style>

7.2 Login.vue 检验和登录

  1. vscode 的 any-rule 插件写正则, F1 输入'姓名', 选英文姓名

  2. 另一种检验规则

<el-form label-width="80px" :model="form" ref="form" :rules="rules">

export default {
  data() {
    const validateName = (rule, value, callback) => {
      // 请输入 4-10 位昵称
      let reg = /(^[a-zA-Z][a-zA-Z1-9\s]{0,20}[a-zA-Z]$)/
      if (value === '') {
        callback(new Error('请输入用户名'))
      } else if (!reg.test(value)) {
        callback(new Error('请输入 4-10 位用户名'))
      } else {
        callback()
      }
    }
    const validatePass = (rule, value, callback) => {
      // 请输入 6-12 位包含大小写字母和数字以及特殊符号
      let regPass = /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/
      if (value === '') {
        callback(new Error('请输入密码'))
      } else if (!regPass.test(value)) {
        callback(new Error('6-12 位密码需要包含大小写字母数字和特殊符号'))
      } else {
        callback()
      }
    }
    return {
      form: {
        username: '',
        password: '',
      },
      rules: {
        username: [{validator: validateName, trigger: 'blur'}],
        password: [{validator: validatePass, trigger: 'blur'}]
      }
    }
  },
}
  1. 封装 新建 src/utils/validate.js
// 用户名匹配

export function nameRule(rule, value, callback) {
  // 请输入 4-10 位昵称
  let reg = /(^[a-zA-Z][a-zA-Z1-9\s]{0,20}[a-zA-Z]$)/
  if (value === '') {
    callback(new Error('请输入用户名'))
  } else if (!reg.test(value)) {
    callback(new Error('请输入 4-10 位用户名'))
  } else {
    callback()
  }
}

// 密码正则匹配
export function passRule(rule, value, callback) {
  // 请输入 6-12 位包含大小写字母和数字以及特殊符号
  let regPass = /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/
  if (value === '') {
    callback(new Error('请输入密码'))
  } else if (!regPass.test(value)) {
    callback(new Error('6-12 位密码需要包含大小写字母数字和特殊符号'))
  } else {
    callback()
  }
}
  • Login.vue 中导入使用
import { nameRule, passRule } from '../utils/validate'
// 使用 nameRule 作为验证规则,在输入框失去焦点时触发验证
// trigger: 规定了何时触发验证。  翻译为: 触发
// blur: 表示在失去焦点时进行验证 翻译为:  变模糊, 即失去焦点
  rules: {
    username: [{validator: nameRule, trigger: 'blur'}],
    password: [{validator: passRule, trigger: 'blur'}]
  }

7.3 token 封装

  1. 新建 src/utils/setToken.js
// src/utils/setToken.js
export function setToken(tokenKey, token) {
  return localStorage.setItem(tokenKey, token)
}

export function getToken(tokenKey) {
  return localStorage.getItem(tokenKey)
}

export function removeToken(tokenKey) {
  return localStorage.removeItem(tokenKey)
}

// Login.vue
import { setToken } from '@/utils/setToken.js'

setToken("username", res.data.username)

8 postman 测试接口 和 axios 二次封装

  1. 配置代理跨域
// vue.config.js
  devServer: {
    open: true,
    proxy: {
      '/api': {
        target: 'http://127.0.0.1:3007',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  },
  1. axios 封装, 新建 src/service.js
import axios from 'axios'
import { getToken } from './utils/setToken'
import { Message } from 'element-ui'

const service = axios.create({
  baseURL: '/api', // baseURL 会自动加在请求地址上
  timeout: 3000
})

// 添加请求拦截器
service.interceptors.request.use( req => {
  req.headers['token'] = getToken('token') // 等同在 postman 里设置 token
  return req
}, error => {
  return Promise.reject(error)
})

// 添加响应拦截器
service.interceptors.response.use(res => {

  let { status, message } = res.data
  if (status !== 200) {
    Message({message: message || 'error', type: 'warning'}) // 处理错误
  }
  return res
}, error => {
  return Promise.reject(error)
})

export default service
// main.js
import service from './service'
Vue.prototype.service = service // 挂载到原型, 可以全局使用
  • 发送请求
  methods: {
    login(form) {
      this.$refs[form].validate( valid => {
        if (valid) {
          console.log(this.form)
          this.service.post('/login', this.form)
          .then(res => {
            if (res.data.status === 200) {
              setToken('username', res.data.username)  // 为了在 header 中展示
              setToken('token', res.data.token)        // 以后每次发请求要带上
              this.$message({message: res.data.message, type: 'success'})
              this.$router.push('/home')
            }
            console.log(res)
          })
        } else {
          console.error(this.form)
        }
      })
    }
  }
  • 把登录方法封装成 api 进行调用 src/api/api.js
// 项目中都会把对应的接口请求封装成 api 来调用
import service from "../service";

// 登录接口
export function login(data) {
  return service({
    method: 'post',
    url: '/login',
    data
  })
}
// Login.vue
import { login } from '@/api/api.js'

login(this.form).then(res => {
  if (res.data.status === 200) {
    setToken('username', res.data.username)
    setToken('token', res.data.token)
    this.$message({message: res.data.message, type: 'success'})
    this.$router.push('/home')
  }
})
  1. 完善一下
// E:\77vue\Vue项目实战金渡教育\项目vue2+ts+vue3源码\project-v2\src\assets 加资源图片
// Login.vue
background: url('../assets/bg.jpg') center no-repeat;
.el-card {
 background: #65768557;
}

color: #fff;
.el-form .el-form-item__label {
color: #fff;
}
  1. 404 页面 新建 src/components/NotFound.vue
<template>
  <div class="notfound">
    <div class="wrapper">
        <div class="big">页面不见了!</div>
        <div>首页瞧瞧,点击<router-link to="/">这里</router-link>进入首页.</div>
    </div>
  </div>
</template>
  • 路由配置
// src/router/index.js
{
  path: '*',
  name: 'NotFound',
  component: () => import('@/components/NotFound')
},
  • 404 页面样式不显示
// App.vue
html, body {
  width: 100%;
  height: 100%;
}
#app {
  width:100%;
  height: 100%;
}
  1. src/components/common 下创建 Header.vue, Footer.vue, Menu.vue
// src/componets/common/Header.vue , 其它参考这个来改
<template>
  <div>
    header
  </div>
</template>

<script>
export default {
  data() {
    return {}
  }
}
</script>

<style lang="scss" scoped>

</style>
  1. 主页面 Home.vue 导入组件
<template>
  <div class="home">
    <Header/>
    <Footer/>
    <Menu/>
  </div>
</template>

<script>
import Header from './common/Header.vue'
import Footer from './common/Footer.vue'
import Menu from './common/Menu.vue'
export default {
  components: {
    Header,
    Footer,
    Menu
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss">
.home {
  width: 100%;
  height: 100%;
}
</style>
  1. elementUI Container 布局容器

9.1 Header.vue 布局

<template>
  <div class="header">
    <el-header>
      <div class="title">通用管理系统</div>
      <div>{{ name }}</div>
    </el-header>
  </div>
</template>

<script>
import { getToken } from '@/utils/setToken.js'
export default {
  data() {
    return {
      name: ''
    }
  },
  created() {
    this.name = getToken('username')
  }
}
</script>

<style lang="scss" scoped>
.header {
  .el-header {
    background: #2578b5;
    color: #fff;
    line-height: 60px;
    display: flex;
    justify-content: space-between;
    .title {
      width: 200px;
      font-size: 24px;
    }
  }
}
</style>
<template>
  <div class="footer">
    <el-card>
      Frontend 2023 jiyun
    </el-card>
  </div>
</template>

<script>
export default {
  data() {
    return {}
  }
}
</script>

9.3 Menu.vue 布局

<template>
  <div class="menu">
    <el-aside width="200px">
      <el-menu
        default-active="2"
        class="el-menu-vertical-demo"
        background-color="#2578b5"
        text-color="#fff"
        active-text-color="#ffd04b"
      >
        <el-submenu index="1">
          <template slot="title">
            <i class="el-icon-location"></i>
            <span>导航一</span>
          </template>
          <el-menu-item-group>
            <el-menu-item index="1-1">选项1</el-menu-item>
            <el-menu-item index="1-2">选项2</el-menu-item>
          </el-menu-item-group>
        </el-submenu>
      </el-menu>
    </el-aside>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
};
</script>

<style lang="scss" scoped>
.menu {
  .el-aside {
    height: 100%;
    .el-menu {
      height: 100%;
    }
    .el-submenu .el-menu-item {
      min-width: 0;
    }
  }
}
</style>

10 基础菜单和模块路由

  1. 创建其它模块 在 src/components

students 目录下 StudentList.vue InfoList.vue InfoLists.vue WorkList.vue WorkMent.vue dataAnalysis 目录 DataView.vue MapView.vue TravelMap.vue ScoreMap.vue users 目录 User.vue

<template>
  <div>
    <!-- 学生管理-->
    信息列表   <!-- src/components/students/InfoList.vue -->
    信息管理   <!-- src/components/students/InfoLists.vue -->
    学生列表   <!-- src/components/students/StudentList.vue -->
    作业列表   <!-- src/components/students/WorkList.vue -->
    作业管理   <!-- src/components/students/WorkMent.vue -->

    <!-- 数据分析-->
    数据概览   <!-- src/components/dataAnalysis/DataView.vue -->
    地图预览   <!-- src/components/dataAnalysis/MapView.vue -->
    分数地图   <!-- src/components/dataAnalysis/ScoreMap.vue -->
    旅游地图   <!-- src/components/dataAnalysis/TravelMap.vue -->

    用户中心   <!-- src/components/users/User.vue -->
  </div>
</template>
  1. 设计路由
    {
      path: '/home',
      name: '学生管理',
      iconClass: 'fa fa-users',
      redirect: '/home/student',
      component: () => import('@/components/Home'),
      children: [
        {
          path: '/home/student',
          name: '学生列表',
          iconClass: 'fa fa-list',
          component: () => import('@/components/students/StudentList')
        },
      ]
    },
    {
      path: '/home',
      name: '数据分析',
      iconClass: 'fa fa-bar-chart',
      redirect: '/home/student',
      component: () => import('@/components/Home'),
      children: [
        {
          path: '/home/dataview',
          name: '数据概览',
          iconClass: 'fa fa-line-chart',
          component: () => import('@/components/dataAnalysis/DataView')
        },
      ]
    },
    {
      path: '/users',    // 这里不一定非得是 /home
      name: '用户中心',
      iconClass: 'fa fa-users',
      redirect: '/home/student',
      component: () => import('@/components/Home'),
      children: [
        {
          path: '/users/user',
          name: '权限管理',
          iconClass: 'fa fa-line-chart',
          component: () => import('@/components/users/User.vue')
        },
      ]
    }
  1. Menu.vue
export default {
  data() {
    return {
      menus: []
    };
  },
  created() {
    console.log(this.$router.options.routes)
    this.menus = [...this.$router.options.routes]
  }
};

<template v-for="(item, index) in menus">
 <el-submenu :index="index +''" :key="index" v-if="!item.hidden">
   <template slot="title">
     <i :class="item.iconClass"></i>
     <span>{{item.name}}</span>
   </template>
   <el-menu-item-group v-for="(child, index) in item.children" :key="index">
     <el-menu-item :index="child.path">
       <i :class="child.iconClass"></i>
       {{child.name}}
     </el-menu-item>
   </el-menu-item-group>
 </el-submenu>
</template>

<el-menu
  router   // 设置后绑定路由, 点击菜单, url 地址会跟着变
  default-active="2"
  class="el-menu-vertical-demo"
  background-color="#2578b5"
  text-color="#fff"
  active-text-color="#ffd04b">
  1. 路由变,并且右侧的内容也跟着变,路由出口,路由在哪显示,出口给到哪
// Home.vue
<el-main><div class="cont"><router-view/></div></el-main>

.cont {
margin: 20px 0;
}

11 菜单路由和面包屑

  1. 新建 common/Breadcrumb.vue
// Breadcrumb.vue
<template>
  <div>
    <el-card>
      <el-breadcrumb separator-class="el-icon-arrow-right">
        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item>活动管理</el-breadcrumb-item>
        <el-breadcrumb-item>活动列表</el-breadcrumb-item>
        <el-breadcrumb-item>活动详情</el-breadcrumb-item>
      </el-breadcrumb>
    </el-card>
  </div>
</template>

// Home.vue
import Bread from "./common/Breadcrumb.vue";

components: { Bread,},

<el-main>
 <Bread/>
 <div class="cont">
   <router-view/>
 </div>
</el-main>
  1. 结合路由修改 Breadcrumb.vue
<template>
  <div>
    <el-card>
      <el-breadcrumb separator-class="el-icon-arrow-right">
        <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item v-for="(item, index) in $route.matched" :key="index">
          {{ item.name }}
        </el-breadcrumb-item>
      </el-breadcrumb>
    </el-card>
  </div>
</template>

12 学生列表, 表格数据处理, 前端分页

  1. 表格
test
上次编辑于:
贡献者: jiyunqq