自己封装Icon选择器组件(基于vue-element-admin框架)

4,105 阅读1分钟

展示组件图片

先把封装完的组件图片放在这里给大家看一下。

相信熟悉后台管理系统的前端开发者一看这个图片就能明白我开发的是什么模块了。在开发后台管理系统的时候常会遇到需要选择icon的时候,虽然网上也有很多封装好的组件,但是如果自己能封装一个类似的组件是最好不过的了。

展示代码

​首先,在src/componets文件夹下面新建一个文件夹用于封装这个组件。我新建的是一个SvgSelect文件夹,这样方便我们语义化,看文件夹的名称就知道组件是什么用处。

​然后,我们要新建一个.vue文件和一个.js文件。我这里用的是index.vue和requireicons.js。index.vue文件么一看就知道是用来封装组件的,requireicons.js文件是为了获取到所有的svg文件信息。

​说了这么多接下来直接给大家贴代码,然后再给大家讲思路。

<!-- index.vue -->
<template>
  <div class="icon-body">
      <div class="icon-list">
          <div v-for="(item,index) in iconList" :key="index" @click="selectedIcon(item)">
              <svg-icon :icon-class="item" style="height:30px;width:16px;" />
              <span>{{item}}</span>
          </div>
      </div>
  </div>
</template>

<script>
import icons from './requireicons'

export default {
    name: 'IconSelect',
    data() {
        return {
            iconList: icons
        }
    },
    methods: {
        selectedIcon(name) {
            this.$emit('selected', name)
            document.body.click()
        }
    }
}
</script>

<style lang="scss" scoped>
.icon-body{
    width: 750px;
    padding: 10px;
    .icon-list {
        overflow: scroll;
        div {
            display: inline-block;
            width: 25%;
            height: 30px;
            line-height: 30px;
            margin-bottom: -5px;
            cursor: pointer;
        }
        span{
            display: inline-block;
            margin-left: 10px;
            line-height: 30px;
            fill: currentColor;
            overflow: hidden;
        }
    }
}
</style>
// requireicons.js
const req = require.context('../../icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()

const re = /\.\/(.*)\.svg/

const icons = requireAll(req).map(i => {
    return i.match(re)[1]
})

export default icons;

思路

​我们在封装一个组件的时候一定要把复杂的问题简单化,在这里我们就当做是要封装一个可以点击的列表就可以了(其实事实也就是这样)。这样大家去看index.vue文件就会很容易了(其实本身就没什么难的)。svg-icon的话这是vue-element-admin框架封装好的组件,简单理解成一个img标签就好了。然后我们给列表项一个点击事件,点击的时候把icon的name传给父组件,在父组件中展示到input框中就可以了。大家可能会好奇为什么要点击一下body,就是document.body.click()这行代码。这是因为我在父组件中用的是elemnt-ui中的Popover组件,必须要点击一下组件范围之外的地方才会隐藏起来。

​requireicons.js文件总体来说就是为了获取到所有.svg图片文件准备的。首先我们使用require.context()方法,相信大家有很多人可能对这个方法很陌生,我也是为了实现这个功能去网上找到的这个方法。

 require.context('../../icons/svg', false, /\.svg$/)
// 参数一:要查询的目录,上述代码指的是当前目录
// 参数二: 是否要查询子孙目录,方法默认的值为false
// 参数三:要匹配的文件的后缀,是一个正则表达式,上述我要查询的是.svg文件
// require.context模块返回一个函数,这个函数可以接收一个参数(这里我们没有用到所以不去深究)
// 导出的方法有 3 个属性: resolve, keys, id。
// resolve 是一个函数,它返回请求被解析后得到的模块 id。
// keys 也是一个函数,它返回一个数组,由所有可能被上下文模块处理的请求组成。
// id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到

然后我们定义了一个requireAll方法和正则验证

const requireAll = requireContext => requireContext.keys()
// 该方法接收了一个参数,并使用keys()方法导出了svg文件信息组成的数组
// 这里要与es6的keys()做区分  
// 功能虽然相似,但用法却大不相同。
const re = /\.\/(.*)\.svg/
// 用来在下面验证取到的文件是否正确

最后我们把上面拿到的在一起调用一下取到我们需要的值

const icons = requireAll(req).map(i => {
    return i.match(re)[1]
})
// 我们通过requireAll(req)拿到vsg文件信息组成的数组
// map遍历这个数组通过正则判断是不是.svg结尾
// 返回正确的值

这样我们就通过requireicons.js文件拿到了整理好的svg文件信息。

调用

​我们在调用该组件时使用了elemen-ui的popover组件,它可以方便我们做IconSelect组件的显示隐藏。这里主要运用了需要插槽的知识,如果大家看不懂可以去看看插槽的知识和element-ui中对插槽字段定义。我们在IconSelect组件上定义selected方法,把组件内部抛出的值赋给我们的输入框和svg-icon组件。

<template>
    <el-popover placement="bottom-start" trigger="click">
        <icon-select @selected="selected" />
        <el-input slot="reference" v-model="form.menu_icon" placeholder="点击选择图标" readonly>
            <svg-icon v-if="form.menu_icon" slot="prefix" :icon-class="form.menu_icon" style="height:40px;width:16px;" />
            <span v-else slot="prefix" />
         </el-input>
    </el-popover>
</template>

<script>
import IconSelect from '@/components/IconSelect/index'

export default {
    components: { IconSelect },
    data() {
        return {
            form: {
                menu_icon: ''
            }
        }
    },
    methods: {
        selected(name) {
            this.form.menu_icon = name
        }
    }
}
</script>

这样我们就完成了最基本的需求。

附言

​作为一个刚开始写博客的程序员,写这些许文字还是废了我很大功夫。想要做出来容易,想要写出来真就不是那么回事了,希望我的文章可以对大家有帮助。如果大家有什么问题和建议可以留言评论给我,我看到会回复大家的

原创不易,点个赞鼓励一下吧!