组件封装
创建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>
效果图如下: