封装组件的步骤
- 建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。
- 准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。
- 准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。
- 封装完毕了,直接调用即可。
Vue组件间的参数传递
1.父组件与子组件传值
父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit方法传递参数
2.非父子组件间的数据传递,兄弟组件传值
Vue 官方提供的 Vuex 框架
发布订阅模式(总线机制 / Bus / 观察者模式)
eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。
我写table组件的思路:
1.样式定义
- 内容间距(innerSpacing):big(16px)、middle(12px)、small(8px)、min(4px); 默认:middle
- 有无边框(frame):true/false; 默认:false
- 有无(rowHover):true/false; 默认:false
- 是否为斑马纹(stripe):true/false; 默认:true
- 是否固定表头(isfixedThead); 默认:true
- 对其方式 内容(align)/表头(headerAlign):left center right; 默认:center
2. 选中数据(checkbox)父组件调用:selected-item.sync="selectedItem";selectedItem=[];
- 通过单向数据流,外界好传入一个selectedItem,table接受这个selectedItem,默认为空数组
- 单选:对表单内容的checkbox添加change事件onChangeItem把每一列行的’索引‘,’项‘还有原生event,
- 如果是选中状态就把传入的所有数据的数组tableTbodyData作为参数通过refs.a.indeterminate = true,其他情况都等于false
3.slot插槽:可用于操作、按钮等等
- 例如:表头<th slot="operating-name">操作</th>
- 列 <td slot="operating-trigger" slot-scope="row"> <button>{{row.item.name}}</button></td>
代码
建立组件的模板
<template>
<div
:class="[
isfixedThead?'D-fixed-thead-style':'D-default-table',
frame?'border-frame':'',
rowHover?'rowhover-bgcolor':'',
align+'AlignInner',
headerAlign+'HeaderAlignInner'
]"
>
<div class="table-head">
<table class="default-head">
<!-- <colgroup>
<col :span="Object.keys(this.tableTbodyData[0]).length" :style="'width:'+(100/Object.keys(this.tableTbodyData[0]).length)+'%'">
</colgroup>-->
<thead v-if="isShowThead" :class="innerSpacing">
<slot v-if="this.tableTheadData.length==0">
<tr>
<!-- 无数据显示 -->
<th>{{$t('NoData')}}</th>
</tr>
</slot>
<tr>
<th v-if="isCheck" style="width:40px">
<input
type="checkbox"
@change="onChangeItemAll($event)"
ref="all"
:checked="areAllItemChecked"
/>
</th>
<th v-for="(thead,i) in tableTheadData" :key="'thead-'+i">
<input v-show="isCheckItem" type="checkbox" @change="onChangeColumn(thead, i, $event)" :checked="onCheckedColumnItem(thead)"/>
{{thead.name}}
</th>
<slot name="operating-name"></slot>
</tr>
</thead>
</table>
</div>
<div class="table-body">
<table class="default-body">
<tbody :class="innerSpacing">
<slot v-if="this.tableTbodyData.length==0">
<tr>
<td>{{$t('NoData')}}</td>
</tr>
</slot>
<slot v-show="changeShowType" name="table-tr" v-for="(tbody_td,tr) in tableTbodyData" :item="tbody_td"></slot>
<tr
v-show="!changeShowType"
v-for="(tbody_td,tr) in tableTbodyData"
:key="'tbody-tr-'+tr"
:class="tr%2&&stripe?'table-interval-background':''"
>
<td v-if="isCheck" style="width:40px;">
<input
type="checkbox"
@change="onChangeItem(tbody_td, tr, $event)"
:checked="onChecked(tbody_td)"
/>
</td>
<td v-for="(thead,td) in tableTheadData" :key="'tbody-td-'+td">{{tbody_td[thead.val]}}</td>
<slot name="operating-trigger" :item="tbody_td"></slot>
</tr>
</tbody>
</table>
</div>
</div>
</template>
props传值,以及逻辑
<script>
export default {
props: {
isShowThead: {
//是否显示表头
type: Boolean,
default: true
},
//更改行内某一项的显示形式
changeShowType: {
type: Boolean,
default: false
},
isfixedThead: {
//是否固定表头
type: Boolean,
default: true
},
isCheck: {
//是否显示选中数据(单选/全选)
type: Boolean,
default: false
},
isCheckItem: {
//是否显示选中数据(单选/全选)
type: Boolean,
default: false
},
selectedItem: {
type: Array,
default: () => []
},
align: {
type: String,
default: "center"
},
headerAlign: {
type: String,
default: "center"
},
innerSpacing: {
type: String,
default: "middle"
},
frame: {
//是否显示边框
type: Boolean,
default: false
},
rowHover: {
//Hover
type: Boolean,
default: false
},
stripe: {
//斑马纹
type: Boolean,
default: true
},
tableTheadData: {
type: [Object, Array],
default: () => []
},
tableTbodyData: {
type: [Object, Array],
default: () => []
}
//选中的列
// onColumnItem: {
// type: Array,
// default: () => []
// }
},
data() {
return {
tdWidth: 0,
tdLength: [],
onColumnItem:[]
};
},
watch: {
// 用于监听checkbox 改变选中的行状态
selectedItem() {
// if (this.selectedItem.length === this.tableTbodyData.length) {
// this.$refs.all.indeterminate = false;
// } else if (this.selectedItem.length === 0) {
// this.$refs.all.indeterminate = false;
// } else {
// this.$refs.all.indeterminate = true;
// }
},
onColumnItem(){}
},
computed: {
// 判断这两个数组里的所有项的id是否都相等来判断是否全选
// 先对这两个数组里的id进行排序,sort会改变原来的数组,所以我们需要先用map生成一个新的数组,然后在排序
areAllItemChecked() {
const a = this.tableTbodyData.map(n => n.id).sort();
const b = this.selectedItem.map(n => n.id).sort();
if (a.length === b.length) {
let result = false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
result = false;
break;
} else {
result = true;
}
}
return result;
} else {
return false;
}
}
},
mounted() {},
methods: {
// checkbox全选方法
onChangeItemAll(e) {
console.log("checkbox checked", e);
if (e.target.checked) {
this.$emit("update:selectedItem", this.tableTbodyData);
} else {
this.$emit("update:selectedItem", []);
}
},
// checkbox 行选择
onChangeItem(item, index, e) {
let copy = JSON.parse(JSON.stringify(this.selectedItem));
if (e.target.checked) {
copy.push(item);
} else {
//取消选中状态:点击当前的checkbox保留数组中id不等于当前id的项
copy = copy.filter(i => i.id !== item.id);
}
this.$emit("update:selectedItem", copy);
},
onChecked(item) {
return this.selectedItem.filter(n => n.id === item.id).length > 0
? true
: false;
},
// 选择表头的列
onChangeColumn(item, index, e){
if (e.target.checked) {
this.onColumnItem.push(item);
} else {
//取消选中状态:点击当前的checkbox保留数组中id不等于当前id的项
this.onColumnItem = this.onColumnItem.filter(i => i.val !== item.val);
}
this.$emit("onChangeColumn", this.onColumnItem);
},
onCheckedColumnItem(item) {
return this.onColumnItem.filter(n => n.val === item.val).length > 0
? true
: false;
}
}
};
</script>
stylus样式
@import '../index'; //调用的是公用样式,这里就不写了
// peiyu: 2019.12.24 --> 2019.12.27
// 内容间距(innerSpacing):big(16px)、middle(12px)、small(8px)、min(4px); 默认:middle
$innerSpacing-big = 16px 8px;
$innerSpacing-middle = 12px 6px;
$innerSpacing-small = 8px 4px;
$innerSpacing-min = 4px 2px;
.big{
th,td{
padding: $innerSpacing-big
}
}
.middle{
th,td{
padding: $innerSpacing-middle
}
}
.small{
th,td{
padding: $innerSpacing-small
}
}
.min{
th,td{
padding: $innerSpacing-min
}
}
// 有无边框
.border-frame{
tr{
border: 1px solid rgba(255, 255, 255, 0.32);
}
}
// 有无hover
.rowhover-bgcolor {
.table-body{
tr{
&:hover{
background: $table-hover-bgColor
}
}
}
}
// 斑马纹
.table-interval-background{
background: $table-interval-bgColor
}
// 对其方式 内容(align)/表头(headerAlign):left center right; 默认:center
.centerAlignInner{
th{
text-align: center
}
}
.leftAlignInner{
th{
text-align: left
}
}
.rightAlignInner{
td{
text-align: right
}
}
.centerHeaderAlignInner{
td{
text-align: center
}
}
.leftHeaderAlignInner{
td{
text-align: left
}
}
.rightHeaderAlignInner{
td{
text-align: right
}
}
// 不换行
th,td{
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
// 默认样式
.D-default-table {
height : 100%;
.table-head{
font-size: $font-prompt-size;
color: $font-coloc-Secondary;
.default-head{
table-layout: fixed;
border-collapse: collapse;
width: 100%;
border-bottom: 1px solid $dividing-line-color;
}
}
.table-body{
font-size: $font-secondary-size;
color: $font-coloc-ordinary
.default-body{
table-layout: fixed;
width: 100%;
border-collapse: collapse;
}
}
}
// 固定表头
.D-fixed-thead-style{
scrollBar();
height : 100%;
width: 100%;
overflow : hidden;
display : flex;
flex-direction: column;
.table-head{
font-size: $font-prompt-size;
color: $font-coloc-Secondary;
padding-right: 6px;
// 默认样式
.default-head{
table-layout: fixed;
border-collapse: collapse;
width: 100%;
color: $font-coloc-Secondary
border-bottom: 1px solid $dividing-line-color;
}
}
.table-body{
font-size: $font-secondary-size;
color: $font-coloc-ordinary
flex-grow : 1;
overflow-y : scroll;
// overflow-y: auto;
.default-body{
table-layout: fixed;
width: 100%;
border-collapse: collapse;
}
}
}
// checkbox
input[type="checkbox"]{
-webkit-appearance: none;
vertical-align:middle;
margin-top:0;
background:rgba(255,255,255,0.1);
border:$font-coloc-Secondary solid 1px;
border-radius: 3px;
min-height: 16px;
min-width: 16px;
cursor:pointer;
}
input[type="checkbox"]:checked {
background: $color-caption;
}
input[type=checkbox]:checked::after{
content: '';
position: absolute;
background: transparent;
border: #fff solid 2px;
border-top: none;
border-right: none;
height: 0.45rem;
width: 0.78rem;
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.table-head{
input[type=checkbox]:checked::after{
content: '';
position: absolute;
background: transparent;
border: #fff solid 2px;
border-top: none;
border-right: none;
height: 0.45rem;
width: 0.78rem;
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
}