Vue组件库Element

819 阅读8分钟

1. Element介绍

Element:是饿了么公司前端开发团队提供的一套基于 Vue 的网站组件库,用于快速构建网页。

Element 提供了很多组件(组成网页的部件)供我们使用。例如 超链接、按钮、图片、表格等等。如下图所示就是我们开发的页面和ElementUI提供的效果对比:可以发现ElementUI提供的各式各样好看的按钮

官网: element.eleme.cn/#/zh-CN

1669357961971.png

2. 快速入门

首先,我们先要安装ElementUI的组件库,打开VS Code,停止之前的项目,然后在命令行输入如下命令:

npm install element-ui@2.15.3 

具体操作如下图所示:

1669358653297.png

然后我们需要在main.js这个入口js文件中引入ElementUI的组件库,其代码如下:

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI);

图片.png

然后我们需要按照vue项目的开发规范,在src/views目录下创建一个vue组件文件,注意组件名称后缀是.vue,并且在组件文件中编写之前介绍过的基本组件语法,代码如下:

图片.png

最后我们只需要去ElementUI的官网,找到组件库,然后找到按钮组件,抄写代码即可,具体操作如下图所示:

图片.png

找到相应部分的代码复制就OK了

图片.png

最后,我们需要在默认访问的根组件src/App.vue中引入我们自定义的组件,具体操作步骤如下:

图片.png

然后App.vue组件中的具体代码如下,代码是我们通过上述步骤引入element-view组件时自动生成的

<template>
  <div id="app">
    {{ message }}
    <element-view></element-view>
  </div>
</template>

<script>
    import ElementView from './views/ElementView.vue';
    export default {
      components: { ElementView },
      data() {
        return {
          message: "Hello Vue",
        };
      },
    };
</script>

<style>

</style>

然后运行我们的vue项目,浏览器直接访问之前的7000端口,展示效果如下图所示:

图片.png

到此,我们ElementUI的入门程序编写成功

3. 案例

图片.png

图片.png

该案例将使用 王者 数据,具体数据可查看 juejin.cn/post/742141…

在此基础上进行改造,我们再添加新的API,主要添加分页插件

添加全局统一返回结果类Result和统一返回结果状态信息枚举类ResultCodEnum

图片.png

ResultCodEnum代码如下:

package org.example.utils;  
  
/**  
* @Author 烔  
* @date 2024/10/7  
* @Description 统一返回结果状态信息枚举类  
*/  
public enum ResultCodEnum {  
    SUCCESS(200,"success"),  
    USERNAME_ERROR(501,"usernameError"),  
    PASSWORD_ERROR(503,"passwordError"),  
    NOTLOGIN(504,"notLogin"),  
    USERNAME_USED(505,"userNameUsed"),  
    UPDATE_FAILED(400, "failed");  

    private Integer code;  
    private String message;  

    private ResultCodEnum(Integer code, String message) {  
        this.code = code;  
        this.message = message;  
    }  

    public Integer getCode() {  
        return code;  
    }  

    public String getMessage() {  
        return message;  
    }  
}

Result代码如下:

package org.example.utils;  
  
/**  
* @Author 烔  
* @date 2024/10/7  
* @Description 全局统一返回结果类  
*/  
public class Result<T> {  
    private Integer code;  
    private String message;  

    private T data;  

    public Result() {  
    }  

    protected static <T> Result<T> build(T data) {  
        Result<T> result = new Result<>();  
        if (data != null) {  
            result.setData(data);  
        }  
        return result;  
    }  

    public static <T> Result<T> build(T body, Integer code, String message) {  
        Result<T> result = build(body);  
        result.setCode(code);  
        result.setMessage(message);  
        return result;  
    }  

    public static <T> Result<T> build(T body, ResultCodEnum resultCodEnum) {  
        Result<T> result = build(body);  
        result.setCode(resultCodEnum.getCode());  
        result.setMessage(resultCodEnum.getMessage());  
        return result;  
    }  

    /**  
    * 操作成功  
    * @return  
    */  
    public static <T> Result<T> ok(T data){  
        Result<T> result = build(data);  
        return build(data,ResultCodEnum.SUCCESS);  
    }  

    public Result<T> message(String msg){  
        this.setCode(code);  
        return this;  
    }  

    public Result<T> code(Integer code){  
        this.setCode(code);  
        return this;  
    }  

    public Integer getCode() {  
        return code;  
    }  

    public void setCode(Integer code) {  
        this.code = code;  
    }  

    public String getMessage() {  
        return message;  
    }  

    public void setMessage(String message) {  
        this.message = message;  
    }  

    public T getData() {  
        return data;  
    }  

    public void setData(T data) {  
        this.data = data;  
    }  
}

在Main中添加分页拦截器

@Bean  
public MybatisPlusInterceptor mybatisPlusInterceptor() {  
    // 创建 MybatisPlusInterceptor 对象  
    MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();  
    // 添加分页拦截器  
    mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  
    // 返回配置好的 MybatisPlusInterceptor 对象  
    return mybatisPlusInterceptor;  
}

图片.png

在HeroController中添加以下代码:

@GetMapping("SelectPage")  
public Result selectPage(@RequestParam("page") Integer page, @RequestParam("limit") Integer limit) {  
    Page<Hero> heroPage = new Page<>(page, limit);  
    Page<Hero> ipage = heroMapper.selectPage(heroPage, null);  
    Map<String, Object> result = new HashMap<>();  
    result.put("total", ipage.getTotal());  
    result.put("page", ipage.getPages());  
    result.put("pageSize", ipage.getSize());  
    result.put("currentPage", ipage.getCurrent());  
    List<Hero> records = ipage.getRecords();  
    result.put("records", ipage.getRecords());  

    return Result.ok(result);  
}

访问 http://localhost:8080/hero/SelectPage 后添加请求参数后就获取到数据了,eg:http://localhost:8080/hero/SelectPage?page=1&limit=20

图片.png

后端代码到此最终完成

下面开始前端代码(完整代码将附在最后):

创建HeroView.vue

图片.png

选择心仪的布局容器样式并复制相应代码:

图片.png

<el-container>
  <el-header>Header</el-header>
  <el-container>
    <el-aside width="200px">Aside</el-aside>
    <el-main>Main</el-main>
  </el-container>
</el-container>

在App.vue中引用HeroView.vue

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

<script>
    import HeroView from './views/HeroView.vue';
    export default {
      components: { HeroView },
      data() {
      },
    };
</script>

<style>

</style>

到这里基本的容器布局完成

<template>
  <el-container>
    <el-header>王者英雄信息</el-header>
    <el-container>
      <el-aside width="200px">Aside</el-aside>
      <el-main>Main</el-main>
    </el-container>
  </el-container>
</template>

<script>
</script>

<style>
.el-header,
.el-footer {
  background-color: #b3c0d1;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #d3dce6;
  color: #333;
  text-align: center;
  line-height: 200px;
}

.el-main {
  background-color: #e9eef3;
  color: #333;
  text-align: center;
  line-height: 160px;
}

body > .el-container {
  margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
  line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
  line-height: 320px;
}
</style>

图片.png

设置Aside代码:

<el-menu :default-openeds="['1', '3']">
      <el-submenu index="2">
        <template slot="title"><i class="el-icon-menu"></i>导航二</template>
        <el-menu-item index="2-1">选项1</el-menu-item>
        <el-menu-item index="2-2">选项2</el-menu-item>
      </el-submenu>
</el-menu>

图片.png

图片.png

设置Main

<el-container>
      <el-header style="text-align: right; font-size: 12px">
        <el-dropdown>
          <i class="el-icon-setting" style="margin-right: 15px"></i>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>查看</el-dropdown-item>
            <el-dropdown-item>新增</el-dropdown-item>
            <el-dropdown-item>删除</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
        <span>用户登录名</span>
      </el-header>

      <el-main>
        <el-table :data="tableData">
          <el-table-column prop="heroId" label="英雄ID" width="140">
          </el-table-column>
          <el-table-column prop="name" label="英雄姓名" width="120">
          </el-table-column>
          <el-table-column prop="cover" label="英雄图片"> </el-table-column>
        </el-table>
      </el-main>
      <div class="block">
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="currentPage4"
          :page-sizes="[100, 200, 300, 400]"
          :page-size="100"
          layout="total, sizes, prev, pager, next, jumper"
          :total="400"
        >
        </el-pagination>
      </div>
</el-container>

图片.png

访问数据(script标签代码)

import axios from "axios";

export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 6,
      total: 400,
    };
  },

  methods: {
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageSize = val;
     this.feachHeroInfo();
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.currentPage = val;
      this.feachHeroInfo();
    },

    feachHeroInfo() {
      axios
        .get("http://localhost:8080/hero/SelectPage", {
          params: {
            page: this.currentPage,
            limit: this.pageSize,
          },
        })
        .then((res) => {
          this.tableData = res.data.data.records;
          this.total = res.data.data.total;
        });
    },
  },

  mounted(){
    this.feachHeroInfo();
  }
};

至此,数据访问成功!!!

图片.png

下面进行页面样式调整(根据自己喜好进行调整)

图片.png

<template>
  <el-container>
    <el-header style="font-size: 40px">王者英雄信息</el-header>
    <el-container>
      <el-aside width="200px">
        <el-menu :default-openeds="['1', '3']">
          <el-submenu index="2">
            <template slot="title"><i class="el-icon-menu"></i>导航二</template>
            <el-menu-item index="2-1">选项1</el-menu-item>
            <el-menu-item index="2-2">选项2</el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>

      <el-main>
        <el-container>
          <el-header style="text-align: right; font-size: 12px">
            <el-dropdown>
              <i class="el-icon-setting" style="margin-right: 15px"></i>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item>查看</el-dropdown-item>
                <el-dropdown-item>新增</el-dropdown-item>
                <el-dropdown-item>删除</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
            <span>用户登录名</span>
          </el-header>
          <div class="user-info-table">
          <el-main>
            <el-table :data="tableData" border :row-class-name="tableRowClassName" :header-cell-style="{ height: '50px', lineHeight: '50px' }">
              <el-table-column prop="heroId" label="英雄ID" width="140">
              </el-table-column>
              <el-table-column prop="name" label="英雄姓名" width="120">
              </el-table-column>
              <el-table-column prop="cover" label="英雄图片"> </el-table-column>
            </el-table>
          </el-main>
        </div>
          
          <div class="block">
            <el-pagination
              @size-change="handleSizeChange"
              @current-change="handleCurrentChange"
              :current-page="1"
              :page-sizes="[5, 10, 15, 20]"
              :page-size="5"
              layout="total, sizes, prev, pager, next, jumper"
              :total="total"
            >
            </el-pagination>
          </div>
        </el-container>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
import axios from "axios";
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 6,
      total: 400,
    };
  },

  methods: {
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageSize = val;
      this.feachHeroInfo();
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.currentPage = val;
      this.feachHeroInfo();
    },
    feachHeroInfo() {
      axios
        .get("http://localhost:8080/hero/SelectPage", {
          params: {
            page: this.currentPage,
            limit: this.pageSize,
          },
        })
        .then((res) => {
          this.tableData = res.data.data.records;
          this.total = res.data.data.total;
        });
    },
    tableRowClassName({ rowIndex }) {
      if (rowIndex % 2 === 1) {
        return "warning-row";
      } else if (rowIndex % 2 === 0) {
        return "success-row";
      }
      return "";
    },
  },

  mounted() {
    this.feachHeroInfo();
  },
};
</script>

<style>
html,
body {
  width: 100%;
  height: 100vh;
  margin: 0;
  padding: 0;
  /* overflow: hidden; */
}

.el-header,
.el-footer {
  background-color: #b3c0d1;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #d3dce6;
  color: #333;
  text-align: center;
  line-height: 200px;
}

.el-main {
  background-color: #e9eef3;
  color: #333;
  text-align: center;
  line-height: 160px;
}

body > .el-container {
  margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
  line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
  line-height: 320px;
}

.el-table .warning-row {
  background: oldlace;
}

.el-table .success-row {
  background: #f0f9eb;
}

.user-info-table {
  flex: 1;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 440px;
  overflow: auto;
}

.block{
  padding: 0;
  margin: 0;
  width: 100%;
}

</style>

图片内容展示修复

<el-table-column prop="cover" label="英雄图片">
        <template slot-scope="scope">
          <img :src="scope.row.cover" width="100px" height="100px">
        </template>
 </el-table-column>

图片.png

将表格绑定在导航栏中


<div class="user-info-table">
    表格和分页插件代码
</div>


<script>
    method:{
        handleMenuSelect(index) {
          if (index === "2-1") {
            this.showHeroInfoTable = true;
            this.showimg = false;
          } else {
            this.showHeroInfoTable = false;
            this.showimg = true;
          }
        },
    }
</script>

图片.png

完整效果图:

图片.png

图片.png

4.Vue路由

vue官方提供了路由插件Vue Router,其主要组成如下:

  • VueRouter:路由器类,根据路由请求在路由视图中动态渲染选中的组件
  • :请求链接组件,浏览器会解析成<a>
  • :动态视图组件,用来渲染展示与路由路径对应的组件

其工作原理如下图所示:

1669386261570.png

前端路由:URL中的hash(#号之后的内容)与组件之间的对应关系,

首先VueRouter根据我们配置的url的hash片段和路由的组件关系去维护一张路由表;

然后我们页面提供一个组件,用户点击,发出路由请求;

接着我们的VueRouter根据路由请求,在路由表中找到对应的vue组件;

最后VueRouter会切换中的组件,从而进行视图的更新

路由入门

接下来我们来演示vue的路由功能。

首先我们需要先安装vue-router插件,可以通过如下命令

    npm install vue-router@3.5.1

但是我们不需要安装,因为当初我们再创建项目时,已经勾选了路由功能,已经安装好了。

然后我们需要在src/router/index.js文件中定义路由表,根据其提供的模板代码进行修改,最终代码如下:

import Vue from 'vue'  
import VueRouter from 'vue-router'  
import ElementView from '../views/ElementView.vue'  
  
  
Vue.use(VueRouter)  
  
const routes = [  
    {  
        path: '/elementView',  
        name: 'elementView',  
        component: ElementView  
    },  
    {  
        path: '/hero',  
        name: 'hero',  
        component: () => import('../views/HeroView.vue')  
    }  
]  
  
const router = new VueRouter({  
    routes  
})  
  
export default router

注意需要去掉没有引用的import模块。

在main.js中,我们已经引入了router功能,如下图所示:

图片.png

路由基本信息配置好了,路由表已经被加载,此时我们还缺少2个东西,就是<router-lin>和<router-view>,所以我们需要修改2个页面(HeroView.vue和ElementView.vue)我们左侧栏的2个按钮为router-link,其代码如下:

<el-menu-item index="2-1">  
<router-link to="/hero">王者</router-link>  
</el-menu-item>  
<el-menu-item index="2-2">  
<router-link to="elementView">elementView</router-link>  
</el-menu-item>

然后我们还需要在内容展示区域即App.vue中定义route-view,作为组件的切换,其App.vue的完整代码如下:

图片.png 然后我们还需要在内容展示区域即App.vue中定义route-view,作为组件的切换,其App.vue的完整代码如下:

<template>  
    <div id="app">  
    <!-- <hero-view></hero-view>-->  
        <router-view></router-view>  
    </div>  
</template>  
  
<script>  
    // import HeroView from './views/HeroView.vue';  
    export default {  
        components: {},  

        data() {  

        },  
    };  
</script>  
  
<style>  
</style>

但是我们浏览器打开地址: http://localhost:7000/ ,发现一片空白,因为我们默认的路由路径是/,但是路由配置中没有对应的关系,

所以我们需要在路由配置中/对应的路由组件,代码如下:

import Vue from 'vue'  
import VueRouter from 'vue-router'  
import ElementView from '../views/ElementView.vue'  
  
  
Vue.use(VueRouter)  
  
const routes = [  
    {  
        path: '/elementView',  
        name: 'elementView',  
        component: ElementView  
    },  
    {  
        path: '/hero',  
        name: 'hero',  
        component: () => import('../views/HeroView.vue')  
    }, {  
        path: '/',  
        redirect: '/hero' // 表示重定向到/hero即可  
    }  
]  
  
const router = new VueRouter({  
    routes  
})  
  
export default router

到此我们的路由实现成功。