目录
需求
最近项目上遇到一个需求,设计要求将列表改成这样
而且还要行内编辑
很显然,element-ui中的<el-table> 是很难实现的,光是这些个样式就头疼不已
思来想去,这个东西还是用div+css3自己写最容易,但是项目中又不止这一个列表,总不能全都复制粘贴一遍吧
完全之策略就是写个组件,保留我们使用<el-table>的习惯,我们又是多人开发,再写个小文档,小demo让同事们能用就行。
ok,办法想好了,我们就开干写组件
table和table-column组件
table组件:
<template>
<div class="list">
<div class="table-header">
<div
v-for="item in headers"
:key="item.label"
:style="{
flex: toFlexVal(item.width, item.minWidth),
textAlign: item.align
}"
class="tooltip"
>
{{ item.label }}
</div>
</div>
<div
v-for="(item, index) in data"
:key="item.userId"
style="position: relative"
:class="[
isEdit && index === current && (item[currentRowNoEdit] || !currentRowNoEdit) ? 'current' : '',
isEdit ? 'column-edit' : 'column'
]"
@click.stop="clickCurrent(item, index)"
@mouseenter.stop="enterDelete(item, index)"
@mouseleave.stop="leaveDelete()"
>
<slot :item="item" :edit="current === index && (item[currentRowNoEdit] || !currentRowNoEdit)"> </slot>
<div
v-show="isDelete && hoverCurrent === index && index !== current && isEdit"
class="delete"
@click.stop="clickDelete(item, index)"
>
<i title="删除" class="iconfont icon-a-shanchubeifen2"></i>
</div>
</div>
<div v-if="!data.length" class="dashboard">
<img class="fire-arrow" src="@/assets/svg/fire-arrow.svg" alt="fire arrow" />
<div class="text">暂无数据</div>
</div>
</div>
</template>
<script>
import { toFlexVal } from '@/utils/table'
export default {
name: 'Table',
componentName: 'Table',
components: {},
props: {
// 整个列表的数据
data: {
type: Array,
default: () => []
},
// 是否点击可编辑
isEdit: {
type: Boolean,
default: true
},
// 是否hover删除
isDelete: {
type: Boolean,
default: false
},
// 当前行不可编辑,该值为item的某个属性,item[currentRowNoEdit] = 1(可编辑)/0(不可编辑) (true/false)
currentRowNoEdit: {
type: [String, Boolean],
default: false
}
},
data() {
return {
columns: [],
headers: [],
current: false, // 当前点击行
hoverCurrent: false, // 当前hover行
toFlexVal: toFlexVal
}
},
watch: {
columns(val) {
// 处理表头数据
const info = []
this.headers = val.filter(d => {
if (info.indexOf(d.label) === -1) {
info.push(d.label)
return d
}
})
}
},
methods: {
// 鼠标点击某一项
clickCurrent(item, index) {
if (item[this.currentRowNoEdit] || !this.currentRowNoEdit) {
this.current = index
this.$emit('currentClick', item, index)
} else {
this.closeE()
}
},
// 鼠标悬浮事件
enterDelete(item, index) {
this.hoverCurrent = index
},
// 鼠标移出事件
leaveDelete() {
this.hoverCurrent = false
},
// 点击hover删除
clickDelete(item, index) {
this.$emit('clickDelete', item, index)
},
toCssVal(val) {
if (!val) return null
if (/(px|rem|rem)/.test(val.toString())) return val
else return val + 'px'
},
closeE() {
this.current = false
}
}
}
</script>
<style scoped lang="scss">
/* 样式省略,如有需要请在源码中获取 */
</style>
table-column组件:
<template>
<div
ref="column"
:style="{
flex: toFlexVal(width, minWidth),
textAlign: align
}"
class="single-column tooltip"
>
<el-tooltip
v-if="String(toolTip) && showToolTip"
visible-arrow
effect="dark"
:content="String(toolTip)"
placement="top-start"
>
<span ref="columnSpan">
<slot></slot>
</span>
</el-tooltip>
<slot v-else></slot>
</div>
</template>
<script>
import { toFlexVal } from '@/utils/table'
import elementResizeDetectorMaker from 'element-resize-detector'
export default {
name: 'TableColumn',
props: {
width: [Number, String],
minWidth: [Number, String],
label: {
type: String,
default: () => ''
},
align: {
type: String,
default: () => ''
},
toolTip: {
type: [String, Number],
default: () => ''
}
},
data() {
return {
parent: null,
column: null,
toFlexVal: toFlexVal,
showToolTip: false
}
},
watch: {
width(val) {
this.column.width = val
},
minWidth(val) {
this.column.minWidth = val
},
label(val) {
this.column.label = val
},
align(val) {
this.column.align = val
},
toolTip() {
// 分页时resize()直接调用el.scrollWidth值不正确,需要这样处理
this.$nextTick(this.resize)
}
},
created() {
const column = {
...this.$props
}
this.parent = this.getParent('Table')
this.column = column
},
mounted() {
this.parent.columns.push(this.column)
this.resize()
},
methods: {
getParent(name) {
// this.$parent.$parent可以找到组件的套父级
let parent = this.$parent
while (parent) {
if (parent.$options.componentName !== name) {
parent = parent.$parent
} else {
return parent
}
}
return null
},
// 监听某个div的宽度变化
resize() {
const erd = elementResizeDetectorMaker()
erd.listenTo(this.$refs.column, el => {
if (el.scrollWidth > el.offsetWidth) {
this.showToolTip = true
} else {
this.showToolTip = false
}
})
}
}
}
</script>
<style scoped>
.single-column {
padding: 18px 10px;
flex: 1 0 100px;
cursor: pointer;
line-height: 24px;
}
.el-tooltip {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
table.js
export const toFlexVal = (width, minWidth) => {
if (minWidth) {
return `1 0 ${minWidth}px`
} else if (width) {
return `0 0 ${width}px`
}
return '1 0 80px' // 默认值
}
用法:
引入:
import Table from '@/components/Table/table'
import tableColumn from '@/components/Table/table-column'
不带行内编辑
<template>
<Table
:data="tableData"
:isEdit="false"
>
<template v-slot:default="{ item }">
<table-column
label="日期"
width="180">
{{item.date}}
</table-column>
<table-column
label="姓名"
width="180">
{{item.name}}
</table-column>
<table-column
label="地址">
{{item.address}}
</table-column>
</template>
</Table>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
}
}
</script>
带行内编辑
<template>
<!-- isDelete: 鼠标悬浮是否出现删除按钮 -->
<la-table
:data="tableData"
:isDelete="true"
@currentClick="currentClickRow"
@clickDelete="hoverDeleteRow"
>
<template v-slot:default="{ item, edit }">
<div v-if="edit" class="edit">
行内编辑部分
</div>
<div v-else class="content">
<table-column label="图标">
<img :src="item.icon" class="iconStyle" alt="" />
</table-column>
<table-column label="姓名" :toolTip="item.name">{{ item.name }}</table-column>
<table-column label="时间" :toolTip="item.date">{{ item.date }}</table-column>
<table-column label="地址" :toolTip="item.address">{{ item.address }}</table-column>
</div>
</template>
</la-table>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
icon: 'http://XXXXXX.jpg',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
icon: 'http://XXXXXX.jpg',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
icon: 'http://XXXXXX.jpg',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
icon: 'http://XXXXXX.jpg',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
},
methods: {
// 点击编辑 item: 当前点击数据
currentClickRow(item) {},
// 点击删除回调, items: 当前删除数据
hoverDeleteRow(item) {}
}
}
</script>
文档:
Table Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
data | 显示的数据 | Array | - | - |
isEdit | 是否支持点击编辑 | Boolean | - | true |
isDelete | 是否支持hover删除 | Boolean | - | false |
currentRowNoEdit | 限制某一行不可编辑,该值传入item的某个属性 | string/Boolean | - | - |
Table Events
事件名 | 说明 | 参数 |
---|---|---|
currentClick | 点击某一行触发的事件 | row,index |
clickDelete | 点击hover删除触发的事件( isDelete为true可用 ) | row,index |
Table Methods
方法名 | 说明 | 参数 |
---|---|---|
closeE | 关闭编辑状态 | - |
Table-column Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
width | 对应列的宽度 | string | - | - |
min-width | 对应列的最小宽度,与 width 的区别是 width 是固定的,min-width 会把剩余宽度按比例分配给没设置 width 的列 | string | 80px | - |
label | 显示的标题 | string | - | - |
align | 对齐方式 | String | left/center/right | left |
toolTip | 当内容过长被隐藏时显示 tooltip | Boolean | - | false |
源代码:
GitHub - JerryGit1/custom-table: 表格组件。保留<el-table>的开发习惯,样式更灵活