vueCli项目实战(超详细保姆级教学)

文章目录

  • 一、项目创建
    • 1、创建基础文件和路由设置和依赖安装
    • 2、二次封装axios
    • 3、配置反向代理
    • 4、将所有路由组件接口单独放到api文件夹中进行单独管理
    • 5、配置环境变量
    • 6、mock的基础使用方法
    • 7、fastmock 在线mock平台
    • 8、OK!npm run serve 运行项目,就可以请求到数据啦!
  • 二、项目布局的搭建
    • 1、安装element-plus和使用以及国际化的处理
    • 2、src中创建Layout文件夹
    • 3、配置App.vue作为全局组件的出口
    • 4、配置router中的index引入全局组件LayOut
    • 5、单独建立文件取消默认样式并引入到main.js
    • 6、设置css样式查看布局
    • 7、完善侧边栏
    • 8、完善页面头部
    • 9、内容主体部分整体的样式和框架
    • 10、开始写请求接口,进入api文件夹创建请求文件
    • 11、搜索功能
    • 12、分页功能
    • 14、新增数据

一、项目创建

1、创建基础文件和路由设置和依赖安装

  • 对于刚安装好的vue,开始需要添加几个文件夹
    • api / 用于单独管理路由组件当中的所有请求,内部需要定义多个js文件,名称最好和views中的每个组件名一致
    • utils / 项目中多次使用的文件,例如ajax请求
    • .env.development / 自定义环境变量(开发) / 如果使用三目运算辨别是生产还是开发可以不创建该文件(详见5)
    • .env.production / 自定义环境变量 (生产)
  • 脚手架初安装默认文件解释 / 点击这里移到文章最后
  • 安装依赖:
    • axios / 发送ajax请求 官方文档
    • element-plus / ui组件 官方文档
  • vueCli项目实战(超详细保姆级教学)_第1张图片

2、二次封装axios

注意:这里拦截器可以找到官方文档内的interceptor下面的代码直接拷贝过来

axios官方文档 / 点击进入

// 该文件是二次封装axios
import axios from 'axios'
// 定义axios默认配置
const request = axios.create({
  /*
    1、开发环境是跨域环境,上线不会出现跨域问题,baseURL要区分开发和生产
    2、开发环境下,baseURL应该为反向代理请求的前缀,但是请求前缀不能直接在request.js赋值
      应存储在环境变量中
   */
  // proess.env是环境变量,可以在任意位置直接获取,用于判断环境是生产环境还是开发环境
  // 下面有一个属性就是NODE_ENV,可以看到是开发还是生产环境
  // baseURL: '/api'  ⇒ 这样写的话容易固定前缀,无法辨别是开发环境还是生产环境
  // 此处使用的是自定义环境变量
  baseURL: process.env.VUE_APP_BASE_URL
})

// 添加拦截器
axios.interceptors.request.use(function (config) {
  // 获取token 添加到请求头中,发送后端进行接口校验
  return config
}, function (error) {
  return Promise.reject(error)
})

axios.interceptors.response.use(function (response) {
  /*
    后端检验token失败返回错误的code,401:token过期,403:未登录
    在响应拦截器当中需要判断code,如果token过期或者未登录,弹出错误的提示,
    清除本地所有登录相关缓存并重新去登录页
    如果不清除不跳转的话token可以在network当中直接进行修改,跳过验证不安全
  */
  return response
}, function (error) {
  return Promise.reject(error)
})
// 到处request
export default request

3、配置反向代理

  • 找到vue.config.js文件
    注意:反向配置代理中的target目标源是fastmock中的地址,待会会讲如何创建fastmock的在线文件
// 该文件是vue的配置管理
const { defineConfig } = require('@vue/cli-service')
// node内定义path方法
const path = require('path')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    host: 'localhost',
    // 端口号尽量9000以上,防止电脑其他地方占用,或者直接重新修改
    port: 9898,
    open: true,
    /*
      配置服务器反向代理解决开发环境,ajax请求跨域问题
      反向代理,是由服务器替我们请求目标源
      服务器请求target里面的目标源+请求前缀重写后的值+请求中后面的path
      /api/a/b
      http://api.it120.cc/api/a/b
    */
    proxy: {
      // 所有的请求path必须以当前前缀开头
      '/api': {
        // 代理的源
        // 这里请求的是fastmock中的地址
        target: 'https://www.fastmock.site/mock/27f9ed4811870d97ecf603d848a86ec4/api',
        // 是否切换源
        changeOrigin: true,
        // 前缀路径重写
        pathRewrite: {
          '^/api': '/api'
        }
      }
    }
  },
  /*
    关闭eslint 保存就检查代码的格式
    关闭后,vscode需要安装 eslint 插件,用于检查代码
   */
  lintOnSave: false,
  // 重新定义快捷键来定位到指定路径,防止层级过深找不到该文件
  chainWebpack: config => {
    config.resolve.alias
      .set('@', path.join(__dirname, 'src'))
      .set('@a', path.join(__dirname, 'src/api'))
      .set('@c', path.join(__dirname, 'src/components'))
      .set('@r', path.join(__dirname, 'src/router'))
      .set('@s', path.join(__dirname, 'src/store'))
      .set('@u', path.join(__dirname, 'src/utils'))
      .set('@v', path.join(__dirname, 'src/views'))
  }
})

4、将所有路由组件接口单独放到api文件夹中进行单独管理

注意:建议这里文件命名跟views当中对应的请求文件名字相同,方便后期代码维护

  • 此处文件名在api文件夹下叫HomePage
  • 请求在线的fastmock
// 单独管理 HomePage这个路由组件中所有请求
import request from '@u/request'
const fetchCates = (params = {}) => {
  return request.get('请求路径', { params })
}
// 导出fetchCates这个函数,函数方便复用,而且可以携带参数
export {
  fetchCates
}

5、配置环境变量

  • 环境变量在不同环境下值不同,可以通过访问process.env这个对象来查看处于什么环境
  • process.env 该对象可以直接访问,内部有一个内置的属性是NODE_ENV
  • 使用NODE_ENV判断是处于开发环境还是生产环境,需要定义baseURL,在axios内使用
    • 容易跟axios二次封装混淆,注意辨别
    • const request = axios.create({
        baseURL: process.env.NODE_ENV === 'development' ? '/conner' : '生产源'
      })
      
  • 自定义环境变量
    • .env.development和.env.production两个文件夹内定义环境变量

    • 以VUE_APP_环境变量名开头

    • vueCli项目实战(超详细保姆级教学)_第2张图片

    • vueCli项目实战(超详细保姆级教学)_第3张图片

    • 注意要进行重启项目

    • 注意:现在我们还是在fastmock中请求

6、mock的基础使用方法

  • 一般项目刚开始启动前后端联动,同时开发,在后端接口还未搭建完成之前前端需要用一些虚拟的接口来模拟后端接口来发送请求,从而更快的搭建项目
  • 安装mockjs依赖,主要用于拦截ajax的请求,产出随机的数据
  •   npm i mockjs -D
    
  • 引入mock(使用rquire引入mock)因为该依赖在后面要换成后端接口 ,不用import容易混淆,方便后期删除
  • mock产生随机数的语法
    • 量词用于修饰数组、字符串、number等数据长度或大小
    • 用在字段名中( ‘‘字段名|量词’’:初始值 )
      • 固定:“arr|10”:[] ==> 数组长度为0
      • 范围值:“arr|10-20”:[] ==> 数组长度为10-20
      • 注意number一定要给一个范围,用于修饰number大小,不然用该语法就毫无意义,可以直接赋值
    • 不同数据类型的随机数
      • mockjs 提供了不同数据类型的占位符用于产生随机的值,语法“@占位符”
    • 以下是
      - 基础类型
      boolean, natural, integer, float, character, string, range
      日期相关
      date, time, datetime, now
      图片
      img, dataImage
      颜色
      color, hex, rgb, rgba, hsl
      随机文本
      paragraph, sentence, word, title, cparagraph, csentence, cword, ctitle
      名字
      first, last, name, cfirst, clast, cname
      web地址相关
      url, domain, protocol, tld, email, ip
      地址相关
      region, province, city, county, zip
      其他
      capitalize, upper, lower, pick, shuffle
      
  • 用法
    // 引入mockjs
    const Mock = require('mockjs')
    // 第一个参数:拦截接口的path
    // 第二个参数:请求方式
    // 第三个参数:对象,写入接口返回的数据
    Mock.mock('/api/getItemLists', 'get', {
      code: 200,
      msg: 'success',
      data: {
        'total|100-200': 100,
        'lists|10': [
          {
            'id|+1': 1,		//id自增1
            itemName: '@ctitle',	//随机生成商品名
            'price|0-1000': 0,		//随机生成价格,可以用随机@natural,但是不建议,会生成非常大的数,有点离谱
            'sale|0-1000': 0,		//随机生成售卖价格
            desc: '@cparagraph',	//随机生成一段商品描述
            onsale: '@boolean',		//是否售卖,随机生成布尔值判断是不是在售
            thumb: "@img('500x500','@color', '宇宙无敌臀臀')",//随机生成一张图片,随机颜色,固定名称
            createAt: '@date'		//随机日期
          }
        ]
      }
    })
    

7、fastmock 在线mock平台

  • 可以真实的发出请求,产生随机数据也可以使用mockjs依赖,但是缺点是network中没有任何请求
  • 官网(点击这里)
  • 需要注册账号
  • 创建请求vueCli项目实战(超详细保姆级教学)_第4张图片
    • 1、点击加号进入创建
      vueCli项目实战(超详细保姆级教学)_第5张图片 项目基础路径就是我们在vue.config.js中配置代理当中设置的前缀,最好保持一致,方便后期上线

    • 2、点击进入该项目
      vueCli项目实战(超详细保姆级教学)_第6张图片

    • 3、点击进入该项目然后点击新增接口
      vueCli项目实战(超详细保姆级教学)_第7张图片

    • 4、填写以下数据
      vueCli项目实战(超详细保姆级教学)_第8张图片

      注意这里右侧的只能是json格式且不能有注释,左侧接口名就是设置不同的请求接口名称,请求方式,url就是前缀带接口名

8、OK!npm run serve 运行项目,就可以请求到数据啦!

vueCli项目实战(超详细保姆级教学)_第9张图片

二、项目布局的搭建

这里实际项目我按照会员管理系统搭建的,对应商品增加和删除其实同理

1、安装element-plus和使用以及国际化的处理

  • 项目的所有ui组件以及布局都参考element-plus,原理基本一样,在公司开发中使用公司指定的组件库,学会看文档
  • element-plus官网文档地址
  • 以下该文档简称el
  • 安装element-plus:npm install element-plus --save --force
  • src下的main.js中引入element-plus同时配置国际化(el中默认使用的英语,改成中文)
    建议将main.js中的创建app实例和渲染等vue自带写了一段长代码分解,因为很多东西要在创建和渲染之间进行,比如这里的国际化,实例还未创建无法使用国家化,渲染之后再使用也不行,所以得放在两者中间
    // 引入element-plus
    import ElementPlus from 'element-plus'
    import 'element-plus/dist/index.css'
    // 配置element-plus国际化
    import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
    // 创建vue实例
    const app = createApp(App)
    // 使用element-plus的国际化
    app.use(ElementPlus, {
      locale: zhCn
    })
    app.use(store)
    app.use(router)
    app.mount('#app')
    // createApp(App).use(store).use(router).mount('#app')
    

2、src中创建Layout文件夹

  • 该文件夹用于管理页面布局,首页进入后一般会有一个固定布局,基本后期不用改变,为了防止代码的臃肿和不方便维护,该布局可以单独抽离出去,放到该文件夹中管理
  • LayOut中创建一个文件夹components文件夹和index.vue文件,前者又将页面边栏CommonAside.vue(下面简称aside)和页面头CommonHeader.vue(下面简称header)进行分别管理
  • 利用vbase快速生成一个模板放在里面,template中编辑对应简单的文字,比如aside中div内加个侧边栏即可,不然后面查看页面时候会出现没有编辑模块的报错,而且没有内容不方便查看布局效果
    vueCli项目实战(超详细保姆级教学)_第10张图片
    <template>
      <div>
        页面头
      div>
    template>
    
    <script>
    export default {
    
    }
    script>
    
    <style lang="scss" scoped>
    
    style>
    
  • 在LayOut文件夹中的index.vue 引入子组件
    import CommonAside from './components/CommonAside.vue'
    import CommonHeader from './components/CommonHeader.vue'
    
  • 进入el中找到Container 布局容器,找到一个合适的布局cv过来
    注意:后面查看el中的很多组件和功能要自己进行筛选,这里vbase快捷加入模板中已经自带了template了所以只需要复制里面的内容即可
    vueCli项目实战(超详细保姆级教学)_第11张图片
  • 在el-aside和el-header中使用导入的CommonHead,下面注册局部组件,注意这里需要定义名字,在创建的时候会发现template前面有个红色波浪线,定义name后可解决
    <template>
      <div class="common-layout">
        <el-container>
          <el-aside width="200px">
            
            <common-aside>common-aside>
          el-aside>
          <el-container>
            <el-header>
              
              <common-header>common-header>
            el-header>
            <el-main>
              
              <router-view>router-view>
            el-main>
          el-container>
        el-container>
      div>
    template>
    
    import CommonAside from './components/CommonAside.vue'
    import CommonHeader from './components/CommonHeader.vue'
    
    export default {
      // 一定要定义一个name跟文件同名,大驼峰命名法,不然文件最上面会飘红
      name: 'LayOut',
      // 这里引入子组件后在eslint环境下会飘红,注意这里是需要在template中引入组件名才可消除飘红
      components: {
        CommonAside,
        CommonHeader
      }
    }
    

3、配置App.vue作为全局组件的出口

  • 在App.vue中定义路由出口,这里因为安装vue的时候会自带一些内容,进行删除,保留一下
    <template>
      <router-view/>
    template>
    

4、配置router中的index引入全局组件LayOut

  • 再进入路由router文件夹中引入LayOut整个文件夹当成子组件
    可以这么理解,app.vue为整个项目的路由出口,其router-view就是路由router文件夹中的内容,在这内容中又把布局单独拿出去,在这里引入,并注册全局组件,相当于app.vue中包裹了整个LayOut的布局,LayOut中又进行了分离,index中引入头和侧边栏,中间的el-main中的router-view则是下面具体的局部组件
    import { createRouter, createWebHistory } from 'vue-router'
    // 引入首页布局部分
    import LayOut from '@/LayOut'
    
    const routes = [
      {
        path: '/',
        name: 'home',
        /*
          注册全局组件,也就是整个首页的布局放入,同时LayOut中又
          有很多子路由,在el-main中的出口显示
         */
        children: [
    
        ],
        component: LayOut
      }
    ]
    
    const router = createRouter({
      history: createWebHistory(process.env.BASE_URL),
      routes
    })
    
    export default router
    

5、单独建立文件取消默认样式并引入到main.js

  • 由于页面有很多的默认样式,我们在assets当中建立一个css文件夹,内部建立一个reser.css的文件,在网上找到一些合适的默认样式取消的代码cv进来,在main.js中引入

  • 同时可以建立一个imgs文件夹用于存储一些后面要用到的图片
    vueCli项目实战(超详细保姆级教学)_第12张图片

    默认样式的css

    html, body, div, span, object, iframe,
    h1, h2, h3, h4, h5, h6, p, blockquote, pre,
    abbr, address, cite, code,
    del, dfn, em, img, ins, kbd, q, samp,
    small, strong, sub, sup, var,
    b, i,
    dl, dt, dd, ol, ul, li,
    fieldset, form, label, legend,
    table, caption, tbody, tfoot, thead, tr, th, td,
    article, aside, canvas, details, figcaption, figure, 
    footer, header, hgroup, menu, nav, section, summary,
    time, mark, audio, video {
        margin:0;
        padding:0;
        border:0;
        outline:0;
        font-size:100%;
        vertical-align:baseline;
        background:transparent;
    }
    
    body {
        line-height:1;
        /* 取消鼠标双击选中文字 */
        /* 火狐 */
        -moz-user-select: none;
        /* Safari 和 欧朋 */
        -webkit-user-select: none;
        /* IE10+ and Edge */
        -ms-user-select: none;
        /* Standard syntax 标准语法(谷歌) */
        user-select: none;
    }
    
    :focus {
    	outline: 1;
    }
    
    article,aside,canvas,details,figcaption,figure,
    footer,header,hgroup,menu,nav,section,summary { 
        display:block;
    }
    
    nav ul {
        list-style:none;
    }
    
    blockquote, q {
        quotes:none;
    }
    
    blockquote:before, blockquote:after,
    q:before, q:after {
        content:'';
        content:none;
    }
    
    a {
        margin:0;
        padding:0;
        border:0;
        font-size:100%;
        vertical-align:baseline;
        background:transparent;
    }
    
    ins {
        background-color:#ff9;
        color:#000;
        text-decoration:none;
    }
    
    mark {
        background-color:#ff9;
        color:#000; 
        font-style:italic;
        font-weight:bold;
    }
    
    del {
        text-decoration: line-through;
    }
    
    abbr[title], dfn[title] {
        border-bottom:1px dotted #000;
        cursor:help;
    }
    
    table {
        border-collapse:collapse;
        border-spacing:0;
    }
    
    hr {
        display:block;
        height:1px;
        border:0;   
        border-top:1px solid #cccccc;
        margin:1em 0;
        padding:0;
    }
    
    input, select {
        vertical-align:middle;
    }
    
    

    main.js引入默认css

    // 引入css样式表的重置
    import './assets/css/reset.css'
    

6、设置css样式查看布局

  • 以上完成之后项目npm run serve,会出现一个大致的布局,此时可以设置一点css样式让整体布局显示出来,直接红配绿,赛狗屁,后面自己再根据业务进行调整
  • 进入LayeOut的index.vue中最后的scs中设置css样式,页面头的高建议由内容撑开
  • 设置整个布局容器高度为视口高度
    .el-container{
      height: 100vh;
      .el-aside{
        background-color: #0f1a30;
      }
      .el-main{
        background-color:red;
        padding: 0;
      }
      .el-header{
        background-color:green;
      }
    }
    

7、完善侧边栏

  • 进入LayOut中的aside,删除之前定义的侧边栏三个字,引入assets中imgs的logo图片

    <template>
      <div class="logo">
        
        <img src="../../assets/imgs/logo-full.png" alt="">
      div>
    template>
    
  • 设置logo样式让其显示出来

    .logo{
      height: 66px;
      display: flex;
      justify-content: center;
      align-items: center;
      img{
        width: 80%;
      }
    }
    
  • 进入el中找到Menu菜单栏中侧栏部分,因为这里我们要的样式是侧栏右边那一种,所以在下面展开的代码当中找到custom colors标题的部分代码cv其中的el-menu中的内容
    vueCli项目实战(超详细保姆级教学)_第13张图片

  • 放到刚才的div是logo的下面
    vueCli项目实战(超详细保姆级教学)_第14张图片

  • 这里因为代码太多不方便查看可以找到其中的el-sub-menu标签,可以删除几个保留一个

  • 使用el自带的icon图标,找到el文件中的icon图标一栏,这里要注意自带的icon需要下载依赖包

    • npm install @element-plus/icons-vue
    • src目录下的main.js中引入自带的图标
      // 引入element-plus的图标
      import * as ElementPlusIconsVue from '@element-plus/icons-vue'
      // 创建vue实例
      const app = createApp(App)
      // 导入所有图标并进行全局注册
      for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
        app.component(key, component)
      }
      
      • 想要修改图片可以在行内加上:size=’数字‘
      <el-icon :size="15"><House />el-icon>
      
    • 此时回到页面查看发现图标已经添加上了,如果需要更改图标el中icon下面有一些图标,找到对应图标,点击即可复制,回到aside中找到el-sub-menu下面的el-icon将其替换,就可以改图标了
  • el-menu中默认的几个属性

    • active-text-color:鼠标点击后文字变成该颜色
    • background-color:初始背景色
    • class:类名,基本用不着,可以删除,后面需要添加类自己再加删除
    • default-active:页面加载时默认激活菜单的 index
    • text-color:文字颜色
    • @open和@close:用于展开指定的sub-menu删除
  • 接下来进入src下的views文件夹创建后面所有要跳转的路由文件,同时在内部建立index.vue文件,同时在index.vue中vbase建立最简单的一个模板,div中加上中文比如:template中div内写上商品分类文字方便待会查看,添加name,解决飘红问题
    vueCli项目实战(超详细保姆级教学)_第15张图片

  • 回到router的index进行补充,引入这些子路由这里将要写的整个业务所有的子路由写了,注意查看注释

    // 创建三个主要页面的路由,主页路由,登录路由,404路由
    const routes = [
      // 主页路由
      {
        path: '/',
        name: '主页',
        // 主页路由下的所有子组件
        children: [
          {
            // 路由重定向到仪表盘
            path: '/',
            redirect: '/dashBoard'
          },
          // ------- 店铺管理
          {
            path: '/storeManagement',
            name: '店铺管理',
            // es6中的动态引入,实现路由懒加载,组件使用的时候才加载,性能更好
            component: () => import('@v/StoreManagement')
          },
          {
            path: '/storeLists',
            name: '店铺列表',
            component: () => import('@v/StoreLists')
          },
          {
            path: '/staffManagement',
            name: '员工管理',
            component: () => import('@v/StaffManagement')
          },
          // ------- 会员管理
          {
            path: '/memberManagement',
            name: '会员管理',
            component: () => import('@v/MemberManagement')
          },
          {
            path: '/memberLists',
            name: '会员列表',
            component: () => import('@v/MemberLists')
          },
          {
            path: '/memberGrade',
            name: '会员等级',
            component: () => import('@v/MemberGrade')
          },
          {
            path: '/dashBoard',
            name: '仪表盘',
            component: () => import('@v/DashBoard')
          },
          {
            path: '/setting',
            name: '设置',
            component: () => import('@v/SettingPage')
          }
        ],
        component: LayOut
      },
      // 404路由组件
      {
        path: '/:pathMatch(.*)*',
        name: '404',
        component: () => import('@v/NotFound')
      },
      // 登录路由组件
      {
        path: '/login',
        name: '登录',
        component: () => import('@v/LoginPage')
      }
    ]
    
  • 配置el-menu中的:default-active = '$route.path'默认给首页加上激活色

  • 回到aside,将最内部的el-menu-item复制一个放到最外面,作为首页也就是仪表盘,每一次刚进入网站时展示的页面,不需要下拉栏,将内部的index改成路由地址,这里也就是跳转到仪表盘的子路由,路由出口在LayOut内index中的el-main中已经定义了出口

    
    
    <el-menu-item index="/dashBoard">
      
      <el-icon><House />el-icon>
      <span>首页span>
    el-menu-item>
    
  • el-menu标签中添加router启用该模式会在激活导航时以 index 作为 path 进行路由跳转 使用 default-active 来设置加载时的激活项,上面的index本来只是保证唯一性,现在以路由方式启动,所以首页中的index='/dashBoard’会开启路由,自动在router中找到对应的子路由路径进行显示,这时如果点击未定义的一些侧边栏按钮,会跳转到404页面

  • 接下来将所有的下拉栏模板按照样式进行搭建下面以一个为例,其他相同cv更改即可

    
    <el-sub-menu index="1">
      <template #title>
        <el-icon><ShoppingCart />el-icon>
        <span>店铺管理span>
      template>
      <el-menu-item index="/storeLists">
        <template #title><span>店铺列表span>template>
      el-menu-item>
      <el-menu-item index="/staffManagement">
        <template #title><span>员工管理span>template>
      el-menu-item>
    el-sub-menu>
    
  • 注意这里在更改css样式的时候如果希望更改el-sub-menu的悬停样式,要单独写style,取消scoped,其余样式自行设计

    
    
  • 我这里自己设计了自己想要的样式,就是鼠标悬停的时候背景色字体颜色改变,点击后用:before做的线条,接下来就是补充剩下的侧边栏和路由跳转
    vueCli项目实战(超详细保姆级教学)_第16张图片

8、完善页面头部

  • 删除之前占位的页面头

  • 页面头部主要分为三个部分,头部面包屑栏,右侧用户中心栏,下面的标签栏

  • 定义一个头部标签header,内部一个header-top上栏部分,header-bottom下栏标签栏,header-top中又分为左侧和右侧

      <div class="header">
        
        <div class="header-top">
          
          <div class="header-top-fl">
          div>
          
          <div class="header-top-fr">
          div>
        div>
        
        <div class="header-bottom">div>
      div>
    
  • 左侧部分一个可以点击折叠侧边栏的图标,和面包屑,右侧为搜索功能,用户名和头像

    • 图标:直接用el-icon完成,先用固定的图标占位看效果,待会图标要根据点击变化
    • 面包屑,进入el中找到Breadcrumb 面包屑一栏,我这里使用的是面包屑第一栏始终是首页,后面是对应的el-sub-menu的title部分和下面的子栏名称
      首页固定而且有跳转效果是el已经设定好了,只需要改路径即可,后面接着的不需要跳转,所以只需要显示对应路由的name即可,name在路由中定义成中文名了,如果已经跳转到首页,那么后面不需要有任何子栏,所以v-if判断直接隐藏
      
      <div class="header-top-fl">
      
      <el-icon :size="20">
        <Fold />
      el-icon>
      
      
      <el-breadcrumb separator="/">
        
        <el-breadcrumb-item :to="{ path: '/' }">首页el-breadcrumb-item>
        
        <el-breadcrumb-item v-if="$route.path !== '/dashBoard'">
          
          {{ $route.name }}
        el-breadcrumb-item>
      el-breadcrumb>
      div>
      
    • 注意这里面包屑的文字滑过更改跟上面同理,需要去除scoped单独设置
      
      
  • 右侧部分主要是一个搜索框和用户名以及头像

    • 进入el找到自动补全输入框一栏根据要求来cv一个合适的部分拿过来,这里数据有些难处理,后面再进行补全
    • 其次就是定义一个简单的p用户名
    • 头像需要在el中找到dropdown下拉栏和avatar两个进行结合配置
      两个问题:1、头像使用本地图片不进行显示,是因为这里el-avatar是el自己封装的图片,src这里解析本地路径会解析成字符串无法显示,可以给src里面写一个变量,在script中用import在其他里面引入data中定义一下即可。2、下拉栏对应路由跳转的问题,需要给el-dropdown配置@command等于一个变量,el-dropdown-item定义command等于一个值,下面在方法中定义handleCommand接受参数,如果等于a,那么跳转到对应地址

      代码如下:

      
      <div class="header-top-fr">
      
      <el-col :span="12">
        <el-autocomplete
          :fetch-suggestions="querySearch"
          clearable
          placeholder="请输入搜索内容"
          @select="handleSelect"
          prefix-icon="search"
        />
      el-col>
      
      <p>会员管理系统p>
      
      <div class="user">
        
        <el-dropdown @command="handleCommand">
          <div>
            
            <el-avatar :size="35" :src="picture" shape="square"/>
          div>
          
          <template #dropdown>
            <el-dropdown-menu>
              
              <el-dropdown-item command="a">
                <el-icon :size="15"><UserFilled />el-icon>
                <span>个人中心span>
              el-dropdown-item>
              <el-dropdown-item command="b">
                <el-icon :size="15"><Operation />el-icon>
                <span>退出登录span>
              el-dropdown-item>
            el-dropdown-menu>
          template>
        el-dropdown>
      div>
      div>
      
      <script>
      // 引入头像
      import picture from '../../assets/imgs/head-pic.png'
      export default {
        data () {
          return {
            picture: picture
          }
        },
        methods: {
          // 头像下拉栏中点击对应文字跳转到对应路由
          handleCommand (command) {
            console.log(command)
            if (command === 'a') {
              this.$router.push({
                path: '/UserInfo'
              })
            }
          },
          querySearch () {},
          handleSelect () {}
        }
      }
      </script>
      
  • 底部显示标签部分,也就是点击了哪个页面会在这里存储一份标签
    待定!

  • 接下来就是补全一下功能

    • 侧边栏折叠效果,要实现折叠效果的话就涉及到一个问题,怎么实现侧边栏和头部之间的通信,需要用到vuex来实现。
    • 先进入store文件夹中新建一个modules文件夹,这里实现的是vuex中的模块化,内部新建一个base.js文件来写具体的一些操作
    • 进入index.js文件夹,引入刚刚新建的base文件夹并进行使用,注意这里需要开启严格模式,是为了防止state数据被篡改,数据只能由mutation进行数据操作。注意在发布模式的时候要取消严格模式,只是为了让我们在书写代码的时候更加严谨一点
      import { createStore } from 'vuex'
      
      // 引入modules中的base文件
      import base from './modules/base'
      export default createStore({
        // 记得开启严格模式,删除前面四个,保留模块,分别在modules中定义,是为了让state中的数据只能在mutation中进行操作
        strict: true,
        modules: {
          base
        }
      })
      
    • 进入base文件进行导出具体的数据和操作数据的一些方法,注意这里模块化之后一定要定义一个命名空间
      export default {
        // 让该文件变成带命名空间的文件,必须加namespaced
        namespaced: true,
        // 数据
        state: {
          // 导航菜单是否折叠,定义一个变量数据,开始为fasle未折叠
          menuCollapse: false
        },
        // 操作数据
        mutations: {
          // 注意这里尽量进行大写命名函数,因为导出后可能会和其他命名有冲突,特殊一点,来接收上面的state数据进行操作
          TOGGLE_COLLAPSE (state) {
            // 给上面的menuCollapse进行取反即可
            state.menuCollapse = !state.menuCollapse
          }
        }
      }
      
    • 这样我们就可以在header和aside中使用刚刚导出的方法啦!
    • 回到header中,在methods中重新定义一个自己的方法,用commit方法进行触发mutation
      // 触发vuex中的mutations方法
          toggleMenu () {
            // 通过store上的commit方法调用mutations
            this.$store.commit('base/TOGGLE_COLLAPSE')
          }
      
    • 找到折叠图标的icon,绑定点击事件后会发现此时图标已经可以点击进行变换了
      <el-icon :size="20" @click="toggleMenu">
         
         <component :is="$store.state.base.menuCollapse?'Expand':'Fold'">component>
      el-icon>
      
    • 进入aside中,在el中有一个自带的折叠属性Collapse,这里直接定义属性 :collapse="$store.state.base.menuCollapse"在el-menu上
      在这里插入图片描述
    • 此时侧边栏已经可以折叠了,但是宽度不变是因为宽度也不是动态变化的,无法完全折叠,需要对img和侧边栏宽度进行处理,找到img让图片从大图变成小图即可,这里需要用两张照片
      <div class="logo">
      
      
      
      <img :src="$store.state.base.menuCollapse?require('../../assets/imgs/logo-s.png'):require('../../assets/imgs/logo-full.png')" alt="">
      div>
      
    • 最后就是改变侧边栏宽度,同理也需要三目判断,进入LayOut中的index文件,找到el-aside标签,width宽度不是固定200px而是变化的
      <el-aside :width="$store.state.base.menuCollapse?'56px':'200px'" >
      
      这里遗留一个问题,侧边栏卡顿问题,后面进行处理

9、内容主体部分整体的样式和框架

  • 我这里线先写一个会员列表,用来管理会员的具体信息,对会员进行增删改查的处理,其他的一些页面基本也都差不多
  • 进入el中找到table一栏,需要在这里导入一个表格,这里后面要做分页处理,所以我直接就用基础表格了。找到之后cv到之前建立的几个MemberLists会员列表中的index的template里面,注意这里外面最好套个div盒子,因为还要写上面的搜索框
  • 再将几个输入框input和选择器select以及按钮button写入一个单独的盒子main-search中,去调整大致的样式。
    结果如下
    vueCli项目实战(超详细保姆级教学)_第17张图片
    html部分
      <div class="main">
        <div class="main-search">
          <div class="search-top">
            <div>
              <span>会员IDspan>
              <el-input v-model="input" placeholder="请输入会员ID" clearable />
            div>
            <div>
              <span>手机号span>
              <el-input v-model="input" placeholder="请输入会员手机号" clearable />
            div>
            <div>
              <span>名称span>
              <el-input v-model="input" placeholder="请输入会员名称" clearable />
            div>
            <div>
              <span>会员等级span>
              <el-select v-model="value" placeholder="会员等级">
                <el-option
                  v-for="item in options1"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              el-select>
            div>
          div>
          <div class="search-bottom">
            <div>
              <span>状态span>
              <el-select v-model="value" placeholder="状态">
                <el-option
                  v-for="item in options2"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              el-select>
            div>
            <el-button :icon="Search" id="search" >搜索el-button>
            <el-button :icon="Refresh" id="refresh">重置el-button>
            <el-button :icon="Plus" id="plus">新增el-button>
          div>
      	div>
      <div class="main-content">
        <el-table :data="member" style="width: 100%">
          
          <el-table-column prop="id" label="会员ID" width="100" />
          <el-table-column prop="icon" label="头像" width="200"/>
          <el-table-column prop="memberName" label="名称" />
          <el-table-column prop="phone" label="手机号" />
          <el-table-column prop="residue" label="余额" />
          <el-table-column prop="date" label="注册时间" />
          <el-table-column prop="enable" label="状态" />
          <el-table-column label="操作" />
        el-table>
      div>
      div>
    
    js部分
    // 用于引入搜索,重置,添加几个图标,注意下面需要导出才能使用
    import { Search, Refresh, Plus } from '@element-plus/icons-vue'
    export default {
      name: 'MemberLists',
      data () {
        return {
          // 会员数据数组
          member: [],
          // 搜索框中两个下拉栏的数据
          options1: [
            {
              value: '铜牌会员'
            },
            {
              value: '白银会员'
            },
            {
              value: '黄金会员'
            },
            {
              value: '白金会员'
            },
            {
              value: '钻石会员'
            },
            {
              value: '黑卡会员'
            }
          ],
          options2: [
            {
              value: '启用'
            },
            {
              value: '禁用'
            }
          ],
          input: null,
          value: null
        }
      },
      setup () {
        return {
          Search,
          Refresh,
          Plus
        }
      }
    }
    

10、开始写请求接口,进入api文件夹创建请求文件

  • 先写一个请求所有商品数据的请求,这里需要对应fastmock中的接口

    // 单独管理memberList这个路由组件中所有请求
    import request from '@/u/request'
    // 请求所有商品数据
    const fetchMemberLists = (params = {}) => {
      return request.get('/memberLists', { params })
    }
    // 导出,函数方便复用,而且可以携带参数
    export {
      fetchMemberLists
    }
    
  • 回到MemberLists的index种引入请求方法fetchMemberLists,发送请求

    // 引入发送请求,和数据更新的接口,在api的catelist中定义的
    import { fetchMemberLists } from '@a/memberLists'
    
  • 在methods中定义方法,发送数据请求

    methods: {
      // 请求会员所有数据
      fetchMemberLists () {
        fetchMemberLists({
        }).then((result) => {
          console.log(result)
        })
      }
    }
    

    这里可能会出现几个问题,打印结果为以下图片
    在这里插入图片描述
    原因可能是在vue.config.vue中的target没改变,写新的自己的接口,注意删除target最后的/api,因为下面重写了,不然这里在mock接口中还要写一次api,地址会拥有两个api,更改完config后一定要记得重启项目

  • 实例创建时也就是created中就调用请求

    created () {
    this.fetchMemberLists()
    }
    
  • mock接口中的数据

    {
      code: 200,
      msg: "success",
      data: {
        "total|100-200": 100,
        "lists|10": [
          {
            "id|+1": 1,
            "memberName":"@cname",
            icon:"https://api.vvhan.com/api/acgimg",
            enable: "@Boolean",
            date: "@date(yyyy-MM-dd hh:mm:ss)",
            "phone":"@string(number,11)",
            "residue|100-500":100,
            "sort|10-200":10,
            "children|3":[
                {
                  "id|+1": 1,
                  enable:"@Boolean",
                  "memberName":"@cname",
                  icon:"https://api.vvhan.com/api/acgimg"
                }
            ]
          }
        ]
      }
    }
    

    这里icon内部是一个随机图片的网址

  • 此时效果渲染出来后发现图片未正常解析,按钮也没有,先解决图片解析的问题,进入el文档中,找到image一栏,这里有图片预览的相关操作,记得先在el-table-column中自定义列模版template,用来接收scope插槽传过来的值,row则是当前数据所有

    <el-table-column prop="icon" label="头像" width="200">
        
        
        <template #default="{ row }">
          <el-image
            style="width: 30px; height: 30px"
            :src="row.icon"
            :zoom-rate="1.2"
            :preview-src-list="[row.icon]"
            fit="cover"
            preview-teleported
            hide-on-click-modal
          />
        template>
      el-table-column>
    
    • style:样式
    • src:路径
    • zoom-rate:缩放事件的缩放速度
    • preview-src-list:开启图片预览功能,文档中要求是一个数组
    • fit:确定图片如何适应容器框
    • preview-teleported:image-viewer 是否插入至 body 元素上,一定要写,不然预览效果会嵌套在页面里面,而不是遮住其他所有
    • hide-on-click-modal:是否可以通过点击遮罩层关闭预览
  • 完善表头样式

    <el-table :data="member" style="width: 100%" :header-cell-style="{background:'rgb(245, 245, 245)'}" :cell-style="{'text-align':'center'}">
    
    ::v-deep .el-table__header-wrapper {
      thead {
        th{
          div{
            font-size: 12px;
            color: #515a6e;
            text-align: center;
          }
        }
      }
    }
    

    效果如下

    vueCli项目实战(超详细保姆级教学)_第18张图片

  • 定义开关按钮,跟图片一样,先自定义列模版,获取所有数据,找到el-switch中对应的开关属性,这里绑定一个@change事件来使用参数开启和关闭

    <el-table-column prop="enable" label="状态">
      <template #default="{ row }">
        <el-switch
          v-model="row.enable"
          inline-prompt
          @change="switchCateState(row.id, row.enable)"
        />
      template>
    el-table-column>
    
    • v-model="row.enable"是开关的初始开关状态,接口给了
    • inline-prompt:无论图标或文本是否显示在点内,只会呈现文本的第一个字符
  • 在methods中定义该方法用于开关的开启和关闭未完善,后面需要在此处发送请求用于更新数据,而且需要弹出提示框dialog来确认是否删除

    // 定义开关的方法,接收数据,id指定开关哪个和enable的布尔值
    switchCateState (id, enable) {
      // 行内修改分类显示隐藏图标,此时控制台可以打印出点击关闭开启对应的id和状态
      console.log(id, enable)
    }
    
  • 操作删除和编辑,原理同上,这里要添加两个方法用于编辑和删除,传参就是row中对应的id,指定编辑和删除谁

    <el-table-column label="操作">
      <template #default="{ row }">
        <div class="change-ope">
          
          <el-icon :size="10" color="#ff6633"><EditPen />el-icon>
          <span @click="showUpdatMemberDialog(row.id)" id="edit">编辑span>
          <el-icon :size="10" color="#ff6633"><Delete />el-icon>
          <span @click="delMember(row.id)">删除span>
        div>
      template>
    el-table-column>
    
    // 定义编辑按钮点击弹出框的方法
    showUpdatMemberDialog () {
    
    },
    // 删除商品分类,接受参数id
    delMember () {
    
    }
    
  • 添加列表分类下子分类,el中找到树形数据与懒加载,给el-table加row-key = “id"和:tree-prop定义是否有children,:tree-props=”{ children: ‘children’ }来实现,参考文档(这里我就不写了)

11、搜索功能

  • 在上面的查询框中绑定一个搜索的方法,data中定义一个数组searchParams搜索参数
    <el-button :icon="Search" id="search" @click="onSearch">搜索el-button>
    

12、分页功能

  • el中找到分页效果Pagination,找到合适的样式cv过来,放在el-table中即可

    
    <div class="demo-pagination-block">
      <el-pagination
        v-model:current-page="page"
        v-model:page-size="pageSize"
        :page-sizes="[10, 20, 30, 40]"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
      />
    div>
    
    • v-model:current-page=“page”:当前页数
    • v-model:page-size=“pageSize”:当前总页数
    • :page-sizes=“[10, 20, 30, 40]”:页数尺寸
    • layout=“total, sizes, prev, pager, next, jumper”:组件布局,子组件名用逗号分隔
    • :total=“total”:总条目数
  • data中定义分页的三个参数

    // 分页的三个数据
    page: 0,
    pageSize: 10,
    total: 0
    
  • 在请求数据中fetchMemberLists加上页数的数据

    // 请求会员所有数据
    fetchMemberLists () {
      fetchMemberLists({
        // 展开运算符将data中的searchParams展开用于接收数据
        ...this.searchParams,
        // 传递过来的参数就是data的page和pageSize
        page: this.page,
        pageSize: this.pageSize
      }).then((result) => {
        // console.log(result)
        // 如果返回码为200
        if (result.data.code === 200) {
          // 空数组中填入数据返回结果中的lists
          this.member = result.data.data.lists
          // total为总条数,可以打印鉴别一下
          this.total = result.data.data.total
          console.log(result.data.data.total)
        }
      })
    }
    
  • 最后利用watch监听数据变化,也就是点击哪一页要重新发送请求

    watch: {
      // 数值只要改变的话就触发watch,重新发送请求,实现分页数据处理
      page () {
        this.fetchMemberLists()
      },
      pageSize () {
        this.fetchMemberLists()
      }
    }
    

14、新增数据

  • 进入memberLists中建立componentsss建立memberAdd文件增加新项目
  • 进入el中找到找到弹出框dialog对话框弹出表单,写入index
    
    <el-dialog v-model="memberAddShow" title="添加分类" width="700">
      <cate-add/>
    el-dialog>
    
  • 给新增按钮绑定@click=“showMemberAddDialog”
  • 进入el中表单找到新增表单

你可能感兴趣的:(javascript,vue.js,前端)