一、设计思路及背景
项目地址:GitHub - JavonHuang/zqWeb2.0
hansontable其官方也有提供Vue的版本,使用过的人应该知道,其实本质还是通过配置config的形式。对于列的动态配置,数据的绑定自定义渲染,还是很不方便。需要进行大量的jq的写法,使得代码不够简洁,维护不易,更不好和第三方UI组件融合。
基于上述问题,借鉴element-ui的思路,对表格进行封装改造,使其编写风格与element-ui风格相似。让代码更加简介,使用方便易于维护及扩展。如图:
<template>
<div>
<el-button v-on:click="handelGetData()" size="small">获取数据</el-button>
<pageVHandsontable
ref="page"
:url="'/gzList/getGzList'"
:nestedHeaders="nestedHeaders"
:manualColumnResize="true"
:default-sort="{data:'CLOSE_PRICE',sort:'asc'}"
:fixedRowsBottom="1"
:countFixedRowsBottom="true"
:fixedColumnsLeft="0"
:columnsFooter="columnsFooter"
v-on:selection-change="selectionChange"
>
<VhTableColumn type="selection"></VhTableColumn>
<VhTableColumn type="text" data="SECURITY_NAME" :readOnly="false" title="代码名" width="200" :show-overflow-tooltip="true">
<template slot="editors" slot-scope="slotProps">
<myselect :slotProps="slotProps"></myselect>
</template>
</VhTableColumn>
<VhTableColumn type="text" data="CLOSE_PRICE" :sortable="true" title="收价" width="200">
<template slot="editors" slot-scope="slotProps">
<vhInput :slotProps="slotProps"></vhInput>
</template>
</VhTableColumn>
<VhTableColumn type="text" data="CHANGE_RATE" :sortable="true" :readOnly="true" title="波动" width="200"></VhTableColumn>
<VhTableColumn type="text" data="TRADE_DATE" :sortable="true" width="200" title="日期">
<template slot="editors" slot-scope="slotProps">
<vhdate :slotProps="slotProps"></vhdate>
</template>
<template slot="header" slot-scope="slotProps">
<div>
<div>{{slotProps.columns.title}}</div>
</div>
</template>
<template slot="headerTips" slot-scope="slotProps">
<div>
<el-tooltip class="item" effect="dark" :content="slotProps.columns.title" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</div>
</template>
<template slot-scope="slotProps">
<div>
<div>{{ dateF(slotProps.rowData.TRADE_DATE,slotProps)}}</div>
</div>
</template>
</VhTableColumn>
<VhTableColumn type="text" data="A_SHARES_RATIO" :readOnly="true" title="占比" width="180" :formatter="(rowData,prop,value,rowIndex)=>{return value +'%'}"></VhTableColumn>
<VhTableColumn type="text" data="HOLD_SHARES" :readOnly="true" title="数量" width="180" :show-overflow-tooltip="true"></VhTableColumn>
<VhTableColumn type="text" data="HOLD_MARKET_CAP" :readOnly="true" title="规模" width="180"></VhTableColumn>
<VhTableColumn title="操作" :width="1000">
<template slot-scope="slotProps">
<div>
<el-button v-on:click="handelClick(slotProps)" type="text" size="small">查看</el-button>
</div>
</template>
</VhTableColumn>
</pageVHandsontable>
</div>
</template>
<script>
import moment from 'moment'
import pageVHandsontable from './../components/handsontable/pageVHandsontable'
import VhTableColumn from './../components/handsontable/vh-table-column'
import myselect from './hansonEditor/vhSelect'
import vhInput from './hansonEditor/vhInput'
import vhdate from './hansonEditor/vhdate'
export default {
components:{
pageVHandsontable,
VhTableColumn,
myselect,
vhInput,
vhdate
},
data(){
return{
nestedHeaders:['A', { label: this.test(), colspan: 2 },'测试',{ label: '合并表头', colspan: 2 },{ label: '合并第二', colspan: 2 }],
columnsFooter:{}
}
},
mounted(){
setTimeout(()=>{
this.columnsFooter = {
SECURITY_CODE:'877979',
CLOSE_PRICE:'合计测试12',
CHANGE_RATE:'213',
A_SHARES_RATIO:'7878'
}
})
},
methods:{
dateF(e,slotProps){
return moment(e).format('YYYY-MM-DD')
},
onColumnsSort(e){
console.log(e)
},
selectionChange(e){
console.log(e)
},
handelClick(e){
console.log(e)
},
test(){
return '879898'
},
changew(){
alert('9898')
},
handelGetData(){
console.log(this.$refs.page.getData())
}
}
}
</script>
<style>
</style>
<style lang="scss" scoped>
.editorsSel{
&.el-select{
height: 100%;
width: 100%;
::v-deep
.el-input{
height: 100%;
.el-input__inner{
height: 100%;
width: 100%;
padding: 0 8px;
border: none;
line-height:1;
box-sizing: border-box;
}
.el-input__suffix{
.el-input__icon{
line-height:1;
}
}
}
}
}
</style>
效果:
二、Options
-
defaultSort [Object] 默认排序 {data:'CLOSE_PRICE',sort:'asc'}
-
width [String] table宽 参考官网配置
-
height [String] table高 参考官网配置
-
rowHeaders [Boolean] 显示列头 参考官网配置
-
rowHeights [Number] 行高 参考官网配置
-
colWidths [Number] 列宽 参考官网配置
-
stretchH [String] 列宽自适应 参考官网配置
-
className [String] 单元格样式 Horizontal: htLeft, htCenter, htRight, htJustify,Vertical: htTop, htMiddle, htBottom
-
currentRowClassName [String] 选中行样式 参考官网配置
-
mergeCellsKey [Array] 合并行key依据 自定义功能,仅适用行并
-
manualColumnResize [Boolean] 拖动改变宽度 参考官网配置
-
nestedHeaders [Array] 多表头自定义 参考官网配置
-
countFixedRowsBottom [Boolean] 底部悬浮合计 自定义功能
-
fixedRowsBottom [Number] 底部悬浮[countFixedRowsBottom开启的时候必须置1] 参考官网配置
-
fixedColumnsLeft [Number] 左悬浮 参考官网配置
-
columnsFooter [Object] 合计行显示 自定义功能,以对应columns key
三、VhTableColumn
-
type [String] 列数据类型
-
width [Number] 指定列宽
-
data [String] 列绑定字段
-
title [String] 列头显示标题
-
readOnly [Boolean] 列可编辑
-
formatter [Function] 列显示回调,类似element-ui
-
showOverflowTooltip [Boolean] 文字溢出省略号,显示提示
-
sortable [Boolean] 列排序
四、VhTableColumn slot-scope
-
editors 编辑器渲染插槽
-
header column列头渲染插槽
-
headerTips column列头额外提示信息
-
default column默认插槽,cell渲染内容
五、editors slot-scope 以结合Element-ui组件为例,自定义编辑器
所有的组件需要统一使用一种组件模板,并实现对应的钩子。
1、el-input
<template>
<el-input class="editors-input" v-model="value" placeholder="请输入内容"></el-input>
</template>
<script>
export default {
props:{
slotProps:Object
},
data(){
return{
value:899898
}
},
created(){
//务必注册这两个钩子
this.slotProps['setValue'] = this.setValue
this.slotProps['getValue'] = this.getValue
},
methods:{
//编辑触发,e是当前cell值
setValue(value){
this.value = value
},
//编辑完成会调用,返回cell显示值并更新对例,
getValue(){
return this.value
}
}
}
</script>
<style lang="scss" scoped>
.editors-input{
height: 100%;
::v-deep
input{
height: 100%;
padding: 0 8px;
box-sizing: border-box;
border: none;
}
}
</style>
效果:
2、el-select
<template>
<el-select :popper-append-to-body="false" class="editorsSel" v-on:change="change" v-model="value" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled">
</el-option>
</el-select>
</template>
<script>
export default {
props:{
slotProps:Object
},
data() {
return {
options: [{
value: '000750',
label: '国海证券'
}, {
value: '600326',
label: '西藏天路'
}],
value: null
}
},
created(){
this.slotProps['setValue'] = this.setValue
this.slotProps['getValue'] = this.getValue
},
methods:{
change(e){
let obj = this.options.find(item=>item.value==e)
this.value = obj.value
},
setValue(e){
//获取row的值,设置下拉默认选中值
let SECURITY_CODE = this.slotProps.hot.getDataAtRowProp(this.slotProps.row,'SECURITY_CODE')
let obj = this.options.find(item=>item.value==SECURITY_CODE)
this.value = obj.value
},
getValue(){
let obj = this.options.find(item=>item.value==this.value)
//由于下拉显示的label和value是不一样的字段,这里需要设置对应的源数据值
this.slotProps.hot.setSourceDataAtCell(this.slotProps.row,'SECURITY_CODE',obj.value)
return obj.label
}
}
}
</script>
<style lang="scss" scoped>
.editorsSel{
&.el-select{
height: 100%;
::v-deep
.el-input{
height: 100%;
.el-input__inner{
height: 100%;
width: 100%;
padding: 0 8px;
border: none;
line-height:1;
box-sizing: border-box;
}
.el-input__suffix{
.el-input__icon{
line-height:1;
}
}
}
}
}
</style>
效果:
3、el-date
<template>
<el-date-picker
class="editors-date"
v-model="value"
v-on:change="change"
type="date"
placeholder="选择日期">
</el-date-picker>
</template>
<script>
export default {
props:{
slotProps:Object
},
data(){
return{
value:899898
}
},
created(){
this.slotProps['setValue'] = this.setValue
this.slotProps['getValue'] = this.getValue
},
methods:{
setValue(value){
this.value = value
},
getValue(){
return this.value
},
change(e){
//由于element-ui的下拉层是挂在body的,这样点击日历后,table会先取消了变状态,在getValue返回的不是当前选中的值
//所以需要这里手动设置值
this.slotProps.hot.setSourceDataAtCell(this.slotProps.row,'TRADE_DATE',e)
}
}
}
</script>
<style lang="scss" scoped>
.editors-date{
height: 100%;
width: 100%;
::v-deep
input{
height: 100%;
padding: 0 8px;
box-sizing: border-box;
border: none;
padding-left: 30px;
}
::v-deep
.el-input__suffix{
.el-input__icon{
line-height: inherit;
}
}
::v-deep
.el-input__prefix{
.el-input__icon{
line-height: inherit;
}
}
}
</style>
效果: