表头/内容/脚/ 三者中的宽度如何保持一致
摘要:198
科目:增大
借:207
不管增加还是变小,只有科目变化,其他不变
方案一:其他值固定,根据页面 百分比 - (摘要 + 借 + 币别 + 辅助核算), 使用display:flex;flex-direction:column;
参考表格:他们有一个默认宽度 80,header,body,footer都调用统一的函数, 所以会上下对齐
1.1滚动条的问题?
方案一:手写一个滚动条,那么之后鼠标按下之后才会计算滚动的距离,那如果滚轮滚动就无法实现了
滚动事件解决方案???
https://blog.csdn.net/seimeii/article/details/123092157
e.wheelDelta > 0 向上滚动
e.wheelDelta < 0 向下滚动
滚动条跟内容如何联动
// 左侧的内容区跟右侧的滑块关联
handleContentRelationBar() {
const N = this.zingContent_DIV.offsetHeight - this.zingBody_DIV.offsetHeight, // 400 = 600 - 200
n = this.zingDuration_DIV.offsetHeight - this.zingBar_DIV.offsetHeight,// 200 -30 = 170
n1 = this.getTranslateYValue(this.zingBar_DIV), // transform = 20 距离顶部距离 20px
contentScrollHeight = Math.floor(N / (n / n1))
// N n
// ---- = ----
// x n1
this.zingContent_DIV.style.transform = `translateY(${-contentScrollHeight}px)`
this.handleStartAndEndPosition(contentScrollHeight)
},
虚拟数据如何截取
// 虚拟列表
handleVirtual() {
200 / 60 = 3.3 ~~ 4条
content.length === 4 ? endIndex = 4
content[4 + 2] === undefined ? endIndex = 4
endIndex = 4 + 2 + 0
this.bodyStoreRowCount = ~~(this.zingBody_DIV.offsetHeight / this.rowHeight)
this.bodyShowRowCount = this.bodyStoreRowCount + 2
const contentListLength = this.contentList && this.contentList.length
if (this.bodyStoreRowCount === contentListLength) {
this.isTableMixinShowScrollBar = false
return this.endIndex = contentListLength
}
this.isTableMixinShowScrollBar = true
if (!this.contentList[this.bodyShowRowCount]) {
return this.endIndex = contentListLength - 1
}
this.endIndex = this.bodyShowRowCount + this.startIndex
},
开始下标如何获取
// 根据滚动条看表格中的内容,开始div跟结束div的位置
handleStartAndEndPosition(ScrollHeight) {
// 通过滚动的高度 / 行高 = 开始下标
this.startIndex = ~~(ScrollHeight / this.rowHeight)
let _endIndex = this.bodyShowRowCount + this.startIndex
if (!this.contentList[_endIndex]) {
this.endIndex = this.contentList.length - 1
}
this.endIndex = _endIndex
},
方案二:使用默认的overflow:auto:然后右侧专门留好固定的距离, windiw 跟 mac 滚动条的宽度有出入
页面数据报错,也面中的数据还没有显示,但是需要获取div的高度,显示为0, 就报错
1.2滚动条的问题?
计算滚动条速度??
当通过内容跟固定的 div 计算出滚动条高度, 当数据也多滚动条高度 越小, 当滑动时每次高度 + 10 太小了,如何按比例加快速度
滚动条高度 280px 页面数据 6条
滚动条高度 260px 页面数据 7条
滚动条高度 240px 页面数据 8条
滚动条高度 220px 页面数据 10条
滚动条高度 200px 页面数据 11条
滚动条高度 190px 页面数据 12条
这些数据滚动条滚动的匀速 是 10 px
1.3、页面分辨率问题, 是全屏还是部分屏???
1.4、div中滚动事件,不影响整个表格外面的事件 onmousewheel ???
https://www.ynpxrz.com/n823454c2025.aspx
e.preventDefault() // 取消事件的默认动作。阻止事件冒泡到全局
1.5、使用div布局之后,表格上下边线无法对齐??
因为每一列增加了右边框,导致样式错位,不准确 如何解决?
排除法1、先看下全局是否设置 boder-sizing:border-box; 这样给每个div增加边框都会计入在内部,不会影响外面边框
2、看下适配的内容,必须使用 display: flex; flex-grow:1; 自动分配其他单据修改后的宽度
1.6 当自己手写滚动条之后, 页面中其他滚动条事件被禁用,如何处理
1.7overflow:hidden同样会隐藏position:absolute的子元素
https://blog.csdn.net/bclz_vs/article/details/13002301
解决方案:只能将要显示的div插入到全局,使用position:fixed; lef:;top的距离,滚动右侧滚动条需要重新计算
类似于element中 Popover ,绑定在当前页面document中,滚动条滚动,他也跟随滚动,先找到绑定他div的位置,然后向下或者向上绑定,应该取的 pageY / pageX轴的距离
当用户点击的时候才记录这个div的位置,然后给 popover, 其中遇到的问题,小屏幕聚焦以后会跟着移动,怎么做??
首先获取点击某个元素当前位置,这时候获取的是offsetLeft距离是固定的,在然后就是top的距离,全局的也行/offsetTop也行,看它定位在哪里,然后添加一个固定的距离,就定位到当前元素下边了。

1.8 使用 position:absolute; 会让页面重排跟重绘,改为 transform
https://zhuanlan.zhihu.com/p/78230297
https://www.coder.work/article/1119523
1.9 页面展示过多的数据, input输入内容有卡顿,是因为v-model绑定每次都重新渲染,怎么解决?
解决办法:把页面中的内容分离成组件,比如把输入框抽离成组件,组件之间的render是不影响的。
1.10 页面输入框很卡顿,
优化方案:1、v-model绑定lazy不可以,因为input框需要实时监测摘要的数据
2、列表虚拟滚动
1.11 实现虚拟滚动了, 可是左边的下标不对,如何解决呢?
每次新增删除一条数据都初始化 indexMap{ id: index }, 然后遍历对象,根据id查找下标
1.12 当表格删除一条数据后,会空出原来 表格的 高度, 需要将数据挤下来

思路:根据表格容器跟内容改变滚动条的高度,然后移动滚动条的距离,计算出内容区域可以移动的距离,
如何判断兜底呢,当表格内容的长度 等于虚拟列表最后一个长度的下标 或者 开始的下标 + 一页可以存放几条数据 等于 虚拟列表的长度
contentListLength === this.endIndex
遇到的问题:当最后一条为11条,删除最后一条页面没有变,怎么办?
当数据删除的时候,当前页面的startIndex 还是原来的, 6 + 4 = 10, 需要回到最底部,原理:先根据表格高跟内容求比例,在改变滚动条的高度,在移动滚动条的距离顶部的距离,在获取 startIndex下标,然后重新渲染,页面
相当与 向上 / 向下 按钮点击
原先一页10 条数据600px,增加1条,此时内容表格高度为 660px,
求出大容器 比例 = body(300) / content(660)
求小滚动条的高度 = 滚动条的高度 * 比例
求出虚拟列表:
// 虚拟列表
handleVirtual() {
一页可以放几条数据 = 容器高度 / 每行高度(60)
this.bodyStoreRowCount = ~~(this.zingBody_DIV.offsetHeight / this.rowHeight)
this.bodyShowRowCount = this.bodyStoreRowCount + 2
const contentListLength = this.contentList && this.contentList.length
如果每页面存放的数据相等了,那么隐藏滚动条
if (this.bodyStoreRowCount === contentListLength) {
this.isTableMixinShowScrollBar = false
return this.endIndex = contentListLength
}
this.isTableMixinShowScrollBar = true
获取超出的数据是否存在,不存在说明最大就是每页条数
if (!this.contentList[this.bodyShowRowCount]) {
return this.endIndex = contentListLength - 1
}
this.endIndex = this.bodyShowRowCount + this.startIndex
},
const COLUMN_WIDTH_MAP = {
'serialNumberW': '40',
'abstractW': '200',
'subjectW': '428',
// 'amountAndCurrencyW': '174',
'borrowerW': '208',
'lenderW': '208',
'caozuoW': '80'
}
const tableMixin = {
data() {
return {
tableMixinColumnWidthMap: COLUMN_WIDTH_MAP,
tableAddVoucher_DIV: null,
tableBody_DIV: null,
tableContent_DIV: null,
tableDuration_DIV: null,
tableBar_DIV: null,
tableVoucher_DIV: null,
tableSubjectContent_DIV: null,
tableAbstractContent_DIV: null,
rowHeight: 60,
bodyStoreRowCount: 0, // 页面存放的数量
bodyShowRowCount: 0, // 页面需要显示的数量
startIndex: 0, // 页面开始显示的div位置
endIndex: 0, // 页面开始结尾的div位置
virtualMapKey: {}, // 虚拟列表voucherId在原始数组中的位置
isTableMixinShowScrollBar: false
}
},
computed: {
showVirtualData() {
return this.contentList.slice(this.startIndex, this.endIndex)
},
blankFillPadding() {
return {
paddingTop: this.startIndex * this.rowHeight + 'px',
paddingBottom: (this.contentList.length - this.endIndex) * this.rowHeight + 'px'
}
}
},
methods: {
/**
* @description: 初始化表格滚动条的高度,跟滚轮滑动速度
* @param {number} speed
*/
async initTableMixin(speed) {
this.$nextTick(() => {
this.tableBody_DIV = document.getElementById('tableBody')
this.handleMixinTableContainerHeight()
this.tableContent_DIV = document.getElementById('tableContent')
this.tableDuration_DIV = document.getElementById('tableDuration')
this.tableBar_DIV = document.getElementById('tableBar')
this.tableSubjectContent_DIV = document.getElementById('subjectContent')
this.tableAbstractContent_DIV = document.getElementById('abstractContent')
const container_proportion = this.tableBody_DIV.offsetHeight / this.tableContent_DIV.offsetHeight // 大容器的比例
this.tableBar_DIV.style.height = Math.floor(this.tableDuration_DIV.offsetHeight * container_proportion) + 'px' // 滚动条的高度
this.handleSetTableW()
this.handleVirtual()
this.handleWheel(speed)
let page_Y = 0
//拖拽: 鼠标按下,鼠标拖动,鼠标离开
this.tableBar_DIV.onmousedown = (e) => {
e.preventDefault()
page_Y = e.pageY
document.onmousemove = (e) => {
let page_Proportion = e.pageY - page_Y
this.tableBar_DIV.style.transform = `translateY(${this.getTranslateYValue(this.tableBar_DIV) + page_Proportion}px)`
page_Y = e.pageY
this.handleBarMove()
// 内容移动
this.handleContentRelationBar()
}
document.onmouseup = () => {
document.onmousemove = null
}
}
})
},
// 鼠标按在滚动条开始移动内容
handleBarMove() {
const translateY = this.getTranslateYValue(this.tableBar_DIV)
let barScrollHeight = translateY
if (translateY <= 0) {
barScrollHeight = 0
this.tableBar_DIV.style.transform = 'translateY(0px)'
} else if (this.tableBar_DIV.offsetHeight + translateY > this.tableDuration_DIV.offsetHeight){
barScrollHeight = this.tableDuration_DIV.offsetHeight - this.tableBar_DIV.offsetHeight
this.tableBar_DIV.style.transform = `translateY(${barScrollHeight}px)`
}
},
// 左侧的内容区跟右侧的滑块关联
handleContentRelationBar() {
const N = this.tableContent_DIV.offsetHeight - this.tableBody_DIV.offsetHeight,
n = this.tableDuration_DIV.offsetHeight - this.tableBar_DIV.offsetHeight,
n1 = this.getTranslateYValue(this.tableBar_DIV),
contentScrollHeight = Math.floor(N / (n / n1))
this.tableContent_DIV.style.transform = `translateY(${-contentScrollHeight}px)`
this.handleStartAndEndPosition(contentScrollHeight)
},
/**
* @description: 页面滚动事件,会随着数量的增加移速增加
* @param {number} speed
*/
handleWheel(speed = 10) {
if (!this.isTableMixinShowScrollBar) {
return
}
this.tableBody_DIV.onmousewheel = (e) => {
e.preventDefault()
if (e.wheelDelta) {
const translateY = this.getTranslateYValue(this.tableBar_DIV)
// console.log('translateY', translateY)
// / console.log('e.wheelDelta ', e.wheelDelta )
if (e.wheelDelta > 0) { //当滑轮向上滚动时
this.tableBar_DIV.style.transform = `translateY(${translateY - speed}px)`
this.handleBarMove()
} else if (e.wheelDelta < 0) { //当滑轮向下滚动时
//当滚动的时候页面还会重新加载,优化
this.tableBar_DIV.style.transform = `translateY(${translateY + speed}px)`
this.handleBarMove()
}
this.handleContentRelationBar()
}
}
},
// 计算科目的宽度
handleSetTableW() {
this.tableVoucher_DIV = document.getElementById('addVoucherContentTop')
const tableVoucher_W = Math.floor(this.tableVoucher_DIV.offsetWidth)
let surplus = 0
const filterTitleList = this.titleList.filter(item => item.key !== 'subject')
filterTitleList.forEach(item => {
surplus += +this.tableMixinColumnWidthMap[item.key + 'W']
})
this.tableMixinColumnWidthMap.subjectW = Math.floor(tableVoucher_W - surplus + 21)// 21(外边距 ) filterTitleList.length(每列会有1px边线,导致样式错乱)
},
/**
* @description: 获取TranslateY移动的距离
* @param {Object} ele 页面元素
* @return {number} 返回距离
*/
getTranslateYValue(ele){
if (ele && ele.style && ele.style.transform) {
const transform = ele.style.transform,
n = transform.indexOf("("),
n1 = transform.indexOf(")")
return parseInt(transform.slice(n + 1, n1 - 2))
}
return 0
},
// 虚拟列表
handleVirtual() {
this.bodyStoreRowCount = ~~(this.tableBody_DIV.offsetHeight / this.rowHeight)
this.bodyShowRowCount = this.bodyStoreRowCount + 2
const contentListLength = this.contentList && this.contentList.length
// console.log('startIndex---', this.startIndex)
// console.log('bodyStoreRowCount---', this.bodyStoreRowCount)
// console.log('contentListLength---', contentListLength)
// console.log('contentList[this.bodyShowRowCount]---: ', this.contentList[this.bodyShowRowCount]);
if (this.bodyStoreRowCount === contentListLength) {
this.isTableMixinShowScrollBar = false
return this.endIndex = contentListLength
}
this.isTableMixinShowScrollBar = true
if (!this.contentList[this.bodyShowRowCount]) {
return this.endIndex = contentListLength - 1
}
this.endIndex = this.bodyShowRowCount + this.startIndex
},
// 根据滚动条看表格中的内容,开始div跟结束div的位置
handleStartAndEndPosition(ScrollHeight) {
// console.log('ScrollHeight', ScrollHeight)
// console.log('33399--->', ~~(ScrollHeight / this.rowHeight))
this.startIndex = ~~(ScrollHeight / this.rowHeight)
let _endIndex = this.bodyShowRowCount + this.startIndex
if (!this.contentList[_endIndex]) {
this.endIndex = this.contentList.length - 1
}
this.endIndex = _endIndex
},
/**
* @description: 保存原始数组下标,序列号使用
* @param {Array} arr
*/
handleTableMixinResetShowDataMap(){
const arr = this.contentList
this.virtualMapKey = {}
if (arr && arr.length > 0) {
let arrLenght = arr.length
for (let i = 0; i < arrLenght; i ++) {
this.virtualMapKey[arr[i].voucherId] = i
}
}
},
// 根据页面计算出表格最大高度
handleMixinTableContainerHeight() {
const tableAddVoucherBody_DIV = document.getElementById('addVoucherBody'),
contentHeight = ~~(tableAddVoucherBody_DIV.offsetHeight - 350),
contentListLenght = this.contentList && this.contentList.length
if (contentHeight <= contentListLenght * 60) {
this.tableBody_DIV.style.height = contentHeight + 'px'// 头部 + 制单人高度
} else {
this.tableBody_DIV.style.height = contentListLenght * 60 + 'px'// 默认内容的高度
}
},
// 计算滚轮滚动速度
handleMixinTableComputedSpeed() {
let speed = 10
if (this.contentList && this.contentList.length) {
speed += Math.floor(this.contentList.length / 10)
}
this.initTableMixin(speed)
},
// 内容区域跟滚动条滚动到最底部
handleDown() {
const scrollTop = this.tableDuration_DIV.offsetHeight - this.tableBar_DIV.offsetHeight
this.tableBar_DIV.style.transform = `translateY(${scrollTop}px)`
this.handleContentRelationBar()
},
// 内容区域跟滚动条滚动到最顶部
handleUp() {
this.tableBar_DIV.style.transform = `translateY(0px)`
this.handleContentRelationBar()
},
// 判断是调用顶部函数,还是底部函数
handleContentAndBarScroll() {
// if(!this.startIndex) {
// this.handleUp()
// return
// }
const contentListLength = this.contentList && this.contentList.length
if (contentListLength === this.endIndex || (this.startIndex + this.bodyStoreRowCount) === contentListLength) {
this.handleDown()
return
}
}
}
}
export default tableMixin