故事背景:
结束了四年的大学生活,阿花成功信心满满的投递出自己的简历,最终加入了A公司成为A公司的一员。
今天是阿花第一天入职,在简单熟悉了一下项目代码后,项目经理给阿花分了一个利用Element UI 实现一个支持固定列、标签列、内容超出省略、内容操作等功能的表格
整体ui展示图例
一、表格功能实现
阿花接到需求后,当即利用Element UI着手实现了完整功能的table组件
<template>
<div>
<el-table
v-if="tableData.length != 0"
:data="tableData"
border
stripe
class="active_table"
style="width:100%">
<el-table-column
fixed
prop="active_name"
label="活动名称"
width="100">
</el-table-column>
<el-table-column
prop="active_duty"
label="活动负责人"
width="100">
</el-table-column>
<el-table-column
prop="club_name"
label="申请社团"
width="100">
</el-table-column>
<el-table-column
prop="active_tem"
label="负责人联系方式"
width="100">
</el-table-column>
<el-table-column
prop="active_date"
label="活动时间"
width="100">
</el-table-column>
<el-table-column
prop="active_region"
label="活动地点"
width="100">
</el-table-column>
<el-table-column
prop="active_num"
label="活动人数"
width="100">
</el-table-column>
<el-table-column
prop="state"
label="活动状态"
width="100">
<template slot-scope="scope">
<el-tag
:type="judge(scope.row.state)"
disable-transitions>{{scope.row.state}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="active_desc"
show-overflow-tooltip
label="活动简介"
width="300">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="80">
<template slot-scope="scope">
<el-button @click="handleActive(scope.row.id)" type="text" size="small">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<el-empty description="暂无活动申请" v-else></el-empty>
</div>
</template>
<script>
export default {
methods: {
handleActive(id) {
this.$router.push({ name: 'CheckActive', params: { id: id }})
},
// 初始化
loadActiveApply () {
this.$api.active
.findAllActives(this.$store.state.userStore.userType)
.then((result) => {
if (result.msg == "ok") {
for(let item in result.data){
result.data[item].active_date = result.data[item].active_date.split("T")[0]
}
this.tableData = result.data
}
})
},
judge(state){
if(state == '审核通过'){
return 'success'
}else if(state == '审核不通过'){
return 'danger'
}else {
return 'paimary'
}
}
},
mounted(){
this.loadActiveApply()
},
data() {
return {
tableData: []
}
}
}
于是准备着手将代码提交到代码仓库,这时一位同事乔木看到阿花准备提交的代码,摇摇头说:“阿花,稍等一下,先不要提交”
“怎么了?”
“你为什么不把这个表格封装成一个组件呢”
“需求上的功能我已经实现了阿”
“假如之后再需要使用类似的table表格你每次都重新再写一份吗?这样代码太冗余了”
阿花瞬间不知所措起来,乔木说的有道理,但是封装组件他并不是很熟悉。。。。
组件封装的优势
乔木见状连忙拍拍阿花的肩膀笑道:“那我给你讲讲在项目开发中组件封装的优越性吧”
乔木道: “ 我们把⼀个功能的模板(template)封装 在⼀个.vue 文件中。把每个组件的逻辑和样式,也就是把HTML、JavaScript 和 CSS 封装在⼀起,这样在项目中任何地方都可以再次复用整个组件的代码,减少代码冗余”
“ 可是,我这个代码的.vue文件如果其他地方需要复用,引入不也是可以的吗…”
“是的,你上面写出的代码在功能上没有问题,在需求完全一致时也能复用,但是如果新的需求时不需要表格中有标签列或者列定位呢?”
“ 那,那可能需要重新写功能匹配的表格代码了 ”
“ 所以你就需要将其封装成一个组件,根据业务需求对组件进行定制 ”
功能拆分
“ 可是我该怎么做呢?” 阿花迫不及待的问道
“ 那我就简单的给你分析一下怎么进行组件封装 ”
“ 既然要根据业务需求对组件进行定制,那么在实现所有功能之后,肯定是将不同业务对应的功能抽离出来 ”
实现上面的表格需求我们可以将单元格拆分为 :
① 常规列
② 溢出省略功能列
③ 标签功能列
④ 左表格固定列
⑤ 表格内容操作列
“ 然后根据不同的需求展示不同列中的内容,那你知道怎么实现单元格内容按需求展示吗? ”
“ v-if,v-if 可以条件性地渲染一块内容,判断指令返回truthy值时进行渲染。
数据传递
“ 是的,但是指令和渲染的数据从哪里来呢? ”
“ 当然是从父组件那里传过去了,父组件根据需求给子组件传递对应的数据,子组件对收到的数据进行个性化展示。 ”,阿花激动道
“ 那我考考你,你还记得父子组件之间传递数据有什么方法吗? ” ,乔木问
“ 记得记得 ,父传子用prop、子传父用emit、多组件共享数据用全局事件总线、消息的订阅与发布、vuex状态管理库”
“ 是的,那这种情况下我们使用什么方法传递数据比较合适? ”
“ prop,组件的封装然后复用 数据流是父 ==> 子,在子组件指定接收的验证需求,然后父组件传递符合验证需求的数据 ”
“ 没错,如果需要父子组件交互还会使用到emit自定义事件,但是我们这个需求不需要了,那现在你看看能不能优化一下你上面的代码 ”
于是阿花拍了一下脑袋,在乔木的指教下写出了如下代码
子组件
<template>
<div>
<el-table
:v-loading="loading"
v-if="tableData.length != 0"
:data="tableData"
border
stripe
class="active_table"
style="width: 100%"
max-height="600"
>
<!-- 左侧固定列名 -->
<el-table-column
v-if="fixedLeft"
:prop="fixedLeftName"
:label="fixedLeftLable"
fixed
min-width="100"
></el-table-column>
<!-- 正常列 -->
<el-table-column
v-for="(value, name) in lableObject"
:key="name"
:prop="name"
:label="value"
fit="true"
min-width="100"
></el-table-column>
<!-- 标签列 -->
<el-table-column v-if="tableTag" :prop="tagName" :label="tagLable" min-width="100" >
<template slot-scope="scope">
<el-tag :type="judge(scope.row[tagName])" disable-transitions>
{{ scope.row[tagName] }}
</el-tag>
</template>
</el-table-column>
<!-- 超出省略列 -->
<el-table-column
v-if="overflowTooltip"
:prop="overflowTooltipName"
:label="overflowTooltipLable"
:width="overflowTooltipWidth"
show-overflow-tooltip
></el-table-column>
<!-- 操作表格列 -->
<el-table-column v-if="operateTable" fixed="right" :label="operateTable.lable" min-width="100">
<template slot-scope="scope">
<el-button @click="handleActive(scope.row.id)" type="text" size="small"
>{{operateTable.value}}</el-button
>
</template>
</el-table-column>
</el-table>
<el-empty :description="emptyTable" v-else></el-empty>
</div>
</template>
<script>
export default {
props: {
// 常规表格数据
tableData: {
type: Array,
required: true,
},
// 表格数据对应的lable
lableObject: {
type: Object,
required: true,
},
// 有表格操作时使用
operateTable: {
type: Object,
},
// 数据为空时展示的结果
emptyTable: {
type: String,
default: "数据为空",
},
// 溢出省略列
overflowTooltip: {
type: Object,
},
// 左定位列
fixedLeft: {
type: Object,
},
// 标签列
tableTag: {
type: Object,
}
},
methods: {
judge(state) {
if (state == `${this.tableTag.success}`) {
return "success";
} else if (state == `${this.tableTag.danger}`) {
return "danger";
} else {
return "paimary";
}
},
handleActive(id) {
this.$router.push({ name: this.operateTable.url, params: { id: id }})
this.loading = !this.loading
},
loadTable() {
// 初始化标签列
if(this.tableTag){
this.tagName = Object.keys(this.tableTag)[0];
this.tagLable = this.tableTag[this.tagName];
}
// 初始化fixed定位列
if(this.fixedLeft){
this.fixedLeftName = Object.keys(this.fixedLeft)[0];
this.fixedLeftLable = this.fixedLeft[this.fixedLeftName];
}
// 初始化省略行列
if(this.overflowTooltip){
this.overflowTooltipName = Object.keys(this.overflowTooltip)[0];
this.overflowTooltipLable = this.overflowTooltip[this.overflowTooltipName];
this.overflowTooltipWidth = this.overflowTooltip.width
}
}
},
data() {
return {
tagName: "",
tagLable: "",
fixedLeftName: "",
fixedLeftLable: "",
overflowTooltipName:'',
overflowTooltipLable:'',
overflowTooltipWidth: '',
loading: true
}
},
mounted() {
this.loadTable()
}
};
</script>
父组件
<template>
<div>
<v-Table
:tableData="tableData"
:lableObject="lableObject"
:operateTable="operateTable"
:tableTag="tableTag"
:fixedLeft="fixedLeft"
:overflowTooltip="overflowTooltip"
emptyTable="暂无活动申请"
></v-Table>
</div>
</template>
<script>
import vTable from "@/components/vTable.vue";
export default {
components: {
vTable,
},
methods: {
// 从后端获取表格信息的ajax请求
loadActiveApply() {
this.$api.active
.findAllActives()
.then((result) => {
if (result.msg == "ok") {
this.tableData = result.data;
}
});
},
},
mounted() {
this.loadActiveApply();
},
data() {
return {
tableData: [],
// 表数据是通过后端返回的,如果返回的格式中lable和value的对应关系明确则 不需要lableObject的定义
// 表数据(key)和表头(value)对应关系
lableObject: {
active_duty: "活动负责人",
club_name: "申请社团",
active_tem: "联系方式",
active_date: "活动时间",
active_region: "活动地点",
active_num: "活动人数",
},
// 标签列的表头和标签状态
tableTag: {
state: "活动状态",
success: "审核通过",
danger: "审核不通过"
},
// 左fixed的列
fixedLeft: {
active_name: "活动名称",
},
// 溢出省略的列
overflowTooltip: {
active_desc: "活动简介",
width: '300'
},
// 可以跳转的列
operateTable: {
lable: "操作",
value: "查看详情",
// 跳转路径
url: 'CheckActive'
},
};
},
};
</script>
自此阿花的第一个组件封装算是基本完成了,接下来他的求职路上又会出现什么问题呢,让我们拭目以待吧。