Bootstrap

element-ui一些常用组件的二次封装

组件封装

创建components文件夹,并且创建封装组件

 

1.Table——表格封装

<template>
  <div>
    <el-table :data="tableData" style="width: 100%" 
    :stripe="stripe" :border="border" :size="size"
    v-loading="loading"
    @selection-change="handleSelectionChange"
    >
    <!-- 是否支持复选 -->
    <el-table-column v-if="isSelection" width="55" type="selection" />
      <el-table-column
        :prop="item.param"
        :label="item.lable"
        v-for="(item, index) in tableColumns"
        :key="index"
        :sortable="item.sortable"
        :width="item.width"
      >
        <template slot-scope="scope">
          <span v-if="item.render">{{item.render(scope.row)}}</span>
          <slot v-else-if="item.slotName" :name="item.slotName" :scope="scope"></slot>
          <span v-else>{{scope.row[item.param]}}</span>
        </template>
      </el-table-column>
      <!-- 操作 -->
      <el-table-column v-if="tableOperation.label" :label="tableOperation.label">
        <template slot-scope="scope">
            <slot :name="tableOperation.param" :scope="scope">
              <el-button
                size="small"
                v-for="(item, index) in tableOperation.btnList"
                :key="index"
                @click="handleClick(scope.row, item.type)">
                {{item.label}}
              </el-button>
            </slot>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
export default {
  name: "Table",
  props: {
    tableColumns: {
      type: Array,
      required: true,
      default: () => {
        return []
      }
    },
    tableData: {
      type: Array,
      required: true,
      default: () => {
        return []
      }
    },
    tableOperation: {
      type: Object,
      default: () => {
        return {}
      }
    },
    stripe: {
      type: Boolean,
      default: false
    },
    border: {
      type: Boolean,
      default: false
    },
    size: {
      type: String,
      default: 'small'
    },
    loading: {
      type: Boolean,
      default: false
    },
    isSelection: {
      type: Boolean,
      default: false,
    }
  },
  data() {
    return {}
  },
  methods: {
    handleClick(row, type) {
      this.$emit('handleClick', row, type)
    },
    handleSelectionChange(val) {
      this.$emit('handleSelectionChange', val)
    }
  }
}
</script>

2.Select——选择器

<template>
	<el-select v-model="selectValue" :multiple="multiple" :disabled="disabled" :clearable="clearable"
		:filterable="filterable" :placeholder="placeholder" style="width: 100%" @change="change"
		@visible-change="visibleChange">
		<el-option v-for="item in optionLists" :key="optionKey ? item[optionKey.value] : item.value"
			:label="optionKey ? item[optionKey.label] : item.label" :disabled="isDisabled(item)"
			:value="optionKey ? item[optionKey.value] : item.value">
			<!-- 如果下拉框要显示多个文本,循环’showLabel‘ -->
			<span v-if="showLabel.length > 0" class="labelAll">
				<span v-for="itemIndex in showLabel" :key="itemIndex+''">{{item[itemIndex]}}</span>
			</span>
			<span v-else>{{ optionKey ? item[optionKey.value] : item.label }}</span>
		</el-option>
	</el-select>
</template>
<script>
	export default {
		name: 'Select',
		props: {
			value: [String, Array],
			options: String, // option选项列表JSON串,如此项有值,则直接用此选项中的数据,不会进行数据请求去获取数据。即优先级options>url。默认为空,非必传。
			url: { // 请求下拉框URL地址,默认数据字典请求接口地址,非必传。
				type: String,
				default: '/getDictionary',
			},
			urlCode: String, // 请求数据字典时的参数值,若url为数据字典请求地址,则必传,其余情况非必传。
			urlParams: String, // 接口其余请求参数的JSON串,非必传。
			multiple: { // 是否多选,默认false,非必传。
				type: Boolean,
				default: false,
			},
			disabled: { // 是否禁用,默认false,非必传。
				type: Boolean,
				default: false,
			},
			clearable: { // 是否可清空选项,默认true,非必传。
				type: Boolean,
				default: true,
			},
			filterable: { // 是否可搜索, 默认false,非必传。
				type: Boolean,
				default: false,
			},
			placeholder: { // 选择框提示文字,默认‘请选择’,非必传。
				type: String,
				default: '请选择',
			},
			optionKeys: String, // 指定显示的vaule和label的key值JSON串,若此项有值,则不再用'value'和'label'作为属性名,以此项设置为准,默认空,非必传。
			showLabels: String, // 下拉选项展示值字符串,逗号隔开,若此项有值,则下拉选项所展示文字不再用'label'的值,以此项设置为准,展示时按照顺序展示出来,如展示两个值,则在下拉框中是左右排列,如展示三个值及以上,则第一个和最后一个左右排列,其余值中间分布显示。默认空,非必传。
			disables: String, // 禁止选择的属性值,逗号隔开,非必传。
		},
		data() {
			return {
				selectValue: this.value,
				optionLists: [],
				optionKey: null,
				requestParams: null,
				showLabel: [],
			};
		},
		watch: {
			options() {
				if (this.options) {
					this.optionLists = JSON.parse(this.options)
				}
			},
			urlCode() {
				this.getOptions();
			},
			urlParams() {
				this.requestParams = this.urlParams ? JSON.parse(this.urlParams) : null;
				this.getOptions();
			},
			url() {
				this.getOptions();
			},
			value() {
				this.selectValue = this.value
			},
			optionKeys() {
				this.optionKey = this.optionKeys ? JSON.parse(this.optionKeys) : null;
			},
			showLabels() {
				this.showLabel = this.showLabels ? this.showLabels.split(',') : [];
			},
		},
		created() {
			this.optionKey = this.optionKeys ? JSON.parse(this.optionKeys) : null;
			this.showLabel = this.showLabels ? this.showLabels.split(',') : [];
			this.requestParams = this.urlParams ? JSON.parse(this.urlParams) : null;
			if (this.options) { // 如果父组件有下拉选项数据,则不进行数据请求
				this.optionLists = JSON.parse(this.options)
			} else {
				this.getOptions()
			}
		},
		methods: {
			/**
			* 获取下拉框的选择项数据
			*/
			getOptions() {
				let params = {}
				if (this.urlCode)
					params.code = this.urlCode
				if (this.requestParams) {
					params = Object.assign(params, this.requestParams)
				}
				this.axios.get(this.url, { params })
					.then(res => {
						this.optionLists = res.list // 假设返回的数据封装在list中
					}).catch(() => { })
			},
			/**
			* 判定该选项是否禁用
			* @item {Object} 该选项所有信息所在对象
			**/
			isDisabled(item) {
				if (this.disables) {
					const dicList = this.disables.split(',');
					const itemValue = this.optionKey ? item[this.optionKey.value] : item.value
					const findItemIndex = dicList.findIndex(itemD => itemD === itemValue);
					if (findItemIndex > -1)
						return true
					return false
				}
				return false
			},
			/**
			* 选择框change事件,将所选值和所选值所在的对象回传给父组件
			* @val {String} 当前选中值
			**/
			change(val) {
				let valueKey = 'value'
				if (this.optionKey)
					valueKey = this.optionKey.value

				if (this.multiple) {
					const valObjList = [];
					val.forEach(element => {
						const valObj = this.optionLists.find(itemO => itemO[valueKey] === element);
						valObjList.push(valObj)
					})
					this.$emit('change', val, valObjList)
				} else {
					const valOption = this.optionLists.filter(itemO => itemO[valueKey] === val);
					const [valObj] = valOption;
					this.$emit('change', val, valObj);
				}
			},
			/**
			* 下拉框出现/隐藏时触发
			* @isShow {Boolean} 标识:true为下拉框展开;false为下拉框隐藏
			**/
			visibleChange(isShow) {
				this.$emit('visibleChange', isShow)
			},
		},
	}
</script>
<style lang="less" scoped>
	.labelAll {
		display: flex;
		align-content: center;
		justify-content: space-between;

		.span:last-child {
			font-size: 13px;
			color: #8492a6;
		}
	}
</style>

3.Breadcrumb——面包屑

<template>
    <div>
      <el-breadcrumb separator-class="el-icon-arrow-right">
        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item>{{ level1 }}</el-breadcrumb-item>
        <el-breadcrumb-item>{{ level2 }}</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
  </template>
  
  <script>
  export default {
    name: "Breadcrumb",
    props: {
      level1: {
        type: String,
        default: "一级"
      },
      level2: {
        type: String,
        default: "二级"
      }
    }
  };
  </script>
  

4.Pagination——分页器

<template>
  <div class="pagination">
    <el-pagination background
                   :page-sizes="pageSizes"
                   :page-size.sync="size"
                   :current-page.sync="current"
                   :layout=" layout"
                   :total="total"
                   :disabled="disabled"
                   @current-change="handleCurrentChange"
                   @size-change="handleSizeChange"
                   class="right">
    </el-pagination>
  </div>

</template>

<script>
export default {
  name:"Pagination",
  props: {
    //每页显示条数
    pageSize: {
      type: [String, Number],
      default: 10,
    },
    //默认在第几页
    currentPage: {
      type: [String, Number],
      default: 1,
    },
    //总条数
    total: {
      type: [String, Number],
      default: 0,
    },
    //每页可选显示条数
    pageSizes: {
      type: Array,
      default: () => {
        return [10, 20, 50, 100]
      },
    },
    //布局设计
    layout: {
      type: String,
      default: 'total,prev, pager, next, sizes',
    },
    disabled:{
      type:Boolean,
      default:false
    }
  },
  computed: {
    current: {
      get() {
        return this.currentPage
      },
      set(val) {
        this.$emit('update:currentPage', val) //改变当前为第几页的值赋值给父组件
      },
    },
    size: {
      get() {
        return this.pageSize
      },
      set(val) {
        this.$emit('update:pageSize', val) //改变当前页显示几条数据的值赋值给父组件
      },
    },
  },
  methods: {
    handleSizeChange(val) {
      this.$emit('pagination', { currentPage: 1, pageSize: val })
    },
    handleCurrentChange(val) {
      this.$emit('pagination', { currentPage: val, pageSize: this.pageSize })
    },
  },
}
</script>

5.Input——输入框

<template>
    <div class="base-input-wraper">
      <el-input
        v-bind="$attrs"
        v-on="$listeners"
        class="e-input"
        :style="inputStyle"
        :value="value"
        :size="size"
        @input="$emit('input', $event)"
      ></el-input>
    </div>
  </template>
  
  <script>
  const sizeList = {
    medium: '36px',
    small: '32px',
    mini: '28px',
  };
  export default {
    name: 'Input',
    props: {
      value: String,
      width: {
        type: Number,
        default: 270,
      },
      size: {
        type: String,
        default: 'medium',
      },
    },
    computed: {
      inputStyle() {
        return {
          width: `${this.width}px`,
        };
      },
      wraperStyle() {
        return {
          height: sizeList[this.size],
        };
      },
    },
  };
  </script>
  <style lang="less" scoped>
  .base-input-wraper {
    .e-input /deep/ {
      input {
        border: none;
        outline: none;
        background: transparent;
        // height: 36px;
  
        &:hover {
          border: none;
          outline: none;
          background: transparent;
        }
      }
    }
  }
  </style>
  
  

6.Button——按钮

<template>
    <el-button :style="buttonStyle">
      <slot name="title"></slot>
    </el-button>
</template>

<script>
export default {
  name: "Button",
  props:{
    type:{
        type:String,
        default:"default"
    },
    size:{
        type:String,
        default:"mini"
    },
    borderColor:{
        type:String,
        default:"pink"
    }
  },
  data() {
    return {};
  },
  computed:{
    buttonStyle(){
     let background = ""
     switch (this.type){
         case "error":
            background = "red";
            break;
         case "default": 
            background = "pink";
            break;  
         case "success": 
            background = "green";
            break;     
     }
     const fontSize =
     this.size == "mini" ? "12px" : this.size == "big" ? "16px" : this.size == "nomal" ? "14px" : "14px"
     const borderColor = this.borderColor
     return {background, fontSize, borderColor}
    }
  },
  mounted() {},
  methods: {},
};
</script>

<style lang="less"scoped></style>

7.Dialog——对话框

<template>
    <el-dialog
      custom-class="uq-dialog-custom"
      :title="$slots.title ? '' : title"
      :visible.sync="visible"
      :width="width"
      :append-to-body="appendToBody"
      :modal="modal"
      :close-on-click-modal="false"
      :fullscreen="fullscreen"
      :destroy-on-close="destroyOnClose"
      :modal-append-to-body="modalAppendToBody"
      :before-close="handleClose"
      @open="open"
      @opened="opened"
      @close="close"
      @closed="closed"
    >
      <template v-if="$slots.title">
        <span slot="title">
          <slot name="title" />
        </span>
      </template>
      <slot />
      <span slot="footer" class="dialog-footer">
        <slot name="footer" />
      </span>
    </el-dialog>
  </template>
   
  <script>
  export default {
    name: 'Dialog',
    props: {
      show: {
        type: Boolean,
        default: false
      },
      title: {
        type: String,
        default: '提示'
      },
      appendToBody: {
        // Dialog 自身是否插入至 body 元素上。嵌套的 Dialog 必须指定该属性并赋值为 true
        type: Boolean,
        default: true
      },
      modalAppendToBody: {
        // 遮罩层是否插入至 body 元素上,若为 false,则遮罩层会插入至 Dialog 的父元素上
        type: Boolean,
        default: true
      },
      modal: {
        // 是否需要遮罩层
        type: Boolean,
        default: true
      },
      fullscreen: {
        // 是否全屏
        type: Boolean,
        default: false
      },
      destroyOnClose: {
        // 关闭时销毁 Dialog 中的元素
        type: Boolean,
        default: true
      },
      width: {
        type: String,
        default: '30%'
      }
    },
    computed: {
      visible: {
        get() {
          return this.show
        },
        set(val) {
          console.log(val)
          this.$emit('update:show', val) // visible 改变的时候通知父组件
        }
      }
    },
    data() {
      return {
      }
    },
    methods: {
      handleClose(done) {
        // 关闭前回调
        console.log('beforeClose')
        this.$emit('beforeClose')
        done()
      },
      open() {
        // Dialog 打开的回调
        this.$emit('open')
      },
      opened() {
        // Dialog 打开动画结束时的回调
        this.$emit('opened')
      },
      close() {
        // Dialog 关闭的回调
        this.$emit('close')
        console.log('close')
      },
      closed() {
        // Dialog 关闭动画结束时的回调
        this.$emit('closed')
        console.log('closed')
      }
    }
  }
  </script>
   
  <style scoped lang="less">
  .uq-dialog-custom{
    .el-dialog__header{
   
    }
  }
  </style>

8.From——表单

<template>
    <div class="serachform-conatiner">
      <el-form
        :model="queryForm"
        :inline="true"
        ref="queryForm"
        :label-width="labelWidth"
        class="query-form"
      >
        <slot>
          <el-form-item label="编码:" prop="code">
            <el-input type="text" v-model="queryForm.code"></el-input>
          </el-form-item>
          <el-form-item label="名称:" prop="name">
            <el-input type="text" v-model="queryForm.name"></el-input>
          </el-form-item>
        </slot>
  
        <el-form-item>
          <el-button
            class="submit_btn"
            type="primary"
            @click="submitForm('queryForm')"
            >查询</el-button
          >
          <el-button class="reset_btn" plain @click="resetForm('queryForm')"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
    </div>
  </template>
  <script>
  export default {
    props: {
      queryForm: {
        type: Object
      },
      labelWidth: { //labelWidth默认80px,特殊的地方,可通过prop方式传它的值,做单独处理
        type: String,
        default() {
          return "80px";
        }
      }
    },
    methods: {
      submitForm(formName) {
        //填写完成时,传回表单数据
        this.$refs[formName].validate(valid => {
          if (valid) {
            this.$emit("submitForm", this.queryForm);
          } else {
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
  };
  </script>
  <style lang="less" scoped>
  .serachform-conatiner {
    /* border-top: 1px solid $border_color; */
    padding-top: 10px;
    clear: both;
    .query-form {
      .el-input__inner {
        border-radius: 0;
        width: 172px;
      }
    }
    .el-form-item--mini.el-form-item,
    .el-form-item--small.el-form-item {
      margin-bottom: 10px;
    }
  }
  </style>
  

全局导入组件 在main.js中导入

// 按钮、表格、面包屑、分页器、输入框、选择器、对话框、表单组件导入
import Breadcrumb from './components/BreadCrumb.vue'
import Button from './components/Button.vue';
import Table from './components/Table.vue';
import Select from './components/Select.vue'
import Pagination from './components/Pagination.vue'
import Input from './components/Input.vue'
import Dialog from './components/Dialog.vue'
import Form from './components/Form.vue'
const Loading = {
  install:(Vue =>{
    // 按钮、表格、面包屑、分页器、输入框、选择器、对话框、表单组件
    Vue.component('Button',Button)
    Vue.component('Table',Table)
    Vue.component('Breadcrumb',Breadcrumb)
    Vue.component('Select',Select)
    Vue.component('Pagination',Pagination)
    Vue.component('Input',Input)
    Vue.component('Dialog',Dialog)
    Vue.component('Form',Form)
  })
}
Vue.use(Loading);

在页面中使用

<template>
    <div id="app">
        <el-card style="margin-bottom: 20px;">
            <!-- 表格 -->
            <Table :tableColumns="tableColumns" :tableData="tableData" :tableOperation="tableOperation"
                @handleClick="handleClick" :isSelection="true">
                <template v-slot:status="props">
                    <el-button type="danger" size="small">{{props.scope.row.status === 0 ? '启用' : '禁用'}}</el-button>
                </template>
                <!-- 自定义操作部分 -->
                <!-- <template v-slot:[tableOperation.param]="props">
          <span v-for="(item, index) in tableOperation.btnList" :key="index" @click="handleClick(props.scope.row, item.type)">{{item.label}}</span>
        </template> -->
            </Table>
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 下拉框 -->
            <Select v-model="model" :options="options" :urlParams="urlParams" :multiple="true"
                placeholder="这是placeholder" :optionKeys="optionKeys" :showLabels="showLabels" :disables="disables"
                @change="selectChange">
            </Select>
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 面包屑 -->
            <Breadcrumb level1="权限管理" level2="角色列表"></Breadcrumb>
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 分页器 -->
            <Pagination :total="total" :currentPage.sync="currentPage" :pageSize.sync="pageSize"
                @pagination="handlePageChange" />
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 输入框 -->
            <Input />
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 按钮 -->
            <Button type="default" size="mini" borderColor="pink">
                <span slot="title">点击</span>
            </Button>
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 对话框 -->
            <Dialog :show.sync="activePopShow">
                <span>你好</span>
                <div slot="title">这是title</div>
                <div slot="footer">这是底部</div>
            </Dialog>
        </el-card>
        <el-card style="margin-bottom: 20px;">
            <!-- 表单 -->
            <Form :queryForm="queryForm" @submitForm="submitForm">
            </Form>
        </el-card>
    </div>
</template>

<script>
    export default {
        name: 'App',
        data() {
            return {
                // 表单参数
                queryForm: {
                    name: "",
                    code: ""
                },
                // 对话框
                activePopShow: false,
                // 分页器参数
                total: 0,
                pageSize: 10,
                currentPage: 1,
                // 表格参数
                tableColumns: [
                    {
                        param: 'date',
                        lable: '日期',
                        sortable: true
                    },
                    {
                        param: 'name',
                        lable: '姓名',
                    },
                    {
                        param: 'status',
                        lable: '状态',
                        slotName: 'status',
                    },
                    {
                        param: 'address',
                        lable: '地址',
                        width: '400px'
                    },
                    {
                        param: 'gender',
                        lable: '性别',
                        render: (row) => {
                            return row.gender === 0 ? '女' : '男'
                        }
                    },

                ],
                tableData: [{
                    date: '2016-05-02',
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1518 弄',
                    gender: 0,
                    status: 0,
                }, {
                    date: '2016-05-04',
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1517 弄',
                    gender: 1,
                    status: 1,
                }, {
                    date: '2016-05-01',
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1519 弄',
                    gender: 0,
                    status: 1
                }, {
                    date: '2016-05-03',
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                    gender: 0,
                    status: 0
                }],
                tableOperation: {
                    label: '操作',
                    param: 'operate',
                    childDefault: false,
                    btnList: [
                        {
                            label: '编辑',
                            type: 'edit'
                        },
                        {
                            label: '删除',
                            type: 'del'
                        }
                    ]
                },
                model: '',
                // 选择器参数
                options: JSON.stringify([
                    {
                        valueKey: '01',
                        labelKey: '选项1',
                        dis: '说明1',
                        disA: '说明2',
                    },
                    {
                        valueKey: '02',
                        labelKey: '选项2',
                        dis: '说明3',
                        disA: '说明4',
                    }
                ]),
                urlParams: JSON.stringify({
                    key1: '01',
                    key2: 'Y',
                }),
                optionKeys: JSON.stringify({
                    value: 'valueKey',
                    label: 'labelKey'
                }),
                showLabels: 'labelKey,dis,disA',
                disables: '02'
            }
        },
        methods: {
            //   按钮点击
            submit(done) {
                // 这里供业务组件处理一些事情,比如ajax请求,此处用setTimeout模拟,    执行done()方法消失loading
                setTimeout(() => {
                    done()
                }, 1000)
            },
            // 分页器
            handlePageChange(data) {
                this.currentPage = data.currentPage;
                this.pageSize = data.pageSize;
                this.getDataAsync();
            },
            // 表格
            handleClick(row, type) {
                console.log(row, type)
                if (type === 'edit') {
                    // 调用编辑逻辑
                } else if (type === 'del') {
                    // 调用删除逻辑
                }
            },
            // 选择器方法
            selectChange(val, valObj) {
                this.model = val
                // console.log("valObj=>", valObj)
            },
            // 表单提交
            submitForm(queryForm) {
                console.log("queryForm", queryForm);
            }
        }
    }
</script>

效果图如下:

 

;