范围选择:先点击选择一个单元格,再按Ctrl键点击另外一个单元格,我们认为是范围选择动作。而且这2个动作必须是顺序且连续的。
点击的第一个单元格,记录在SelectCell1属性当中;
顺序?搞个属性,记录动作类型即可。
//table.js
//处理单元格选择 主要是改变相关样式数据
handleCellSelect = (e,i,j) => {
if(e.ctrlKey){ // 按下ctrl key 不用重新
if(this.state.userActionType==1){ //上次动作点击的是单元格
const current_table_model =this.state.history_record[this.state.history_record.length -1];
this.state.selectCell2=new Array(i, j); //当前选中的单元格坐标
let row_start= this.state.selectCell1[0]<i ? this.state.selectCell1[0] :i;
let row_end= this.state.selectCell1[0]>i ? this.state.selectCell1[0] :i;
let col_start= this.state.selectCell1[1]<j ? this.state.selectCell1[1] :j;
let col_end= this.state.selectCell1[1]>j ? this.state.selectCell1[1] :j;
for(let i=0;i<current_table_model.cell_addFilter_bright_model.length;i++){
for (let j=0;j<current_table_model.cell_addFilter_bright_model[i].length;j++){
current_table_model.cell_addFilter_bright_model[i][j]=0; //范围内全部家filter
}
}
for (let i=row_start;i<=row_end;i++)
{
for (let j=col_start;j<=col_end;j++){
current_table_model.cell_addFilter_bright_model[i][j]=1; //范围内全部家filter
}
}
//setState 即可引发渲染
this.setState({
history_record: this.state.history_record,
});
return;
}
}
//复制一个history 元素,改变这个元素相关属性
const history = this.state.history_record;
const current =history.slice(history.length - 1)
const table_model = current[0];
//复制一个数组
//let cellSelectStates_s=this.state.cellSelectStates;
let cellSelectStates_s=table_model.cell_addFilter_bright_model ;
let cellSelectStates_temp=[];
// for(let i=0;i<cellSelectStates_s.length;i++){
// let [...cellSelectStates_r]=cellSelectStates_s[i];
// cellSelectStates_temp.push(cellSelectStates_r);
// }
for(let i=0;i<cellSelectStates_s.length;i++){
cellSelectStates_temp.push(new Array(cellSelectStates_s[i].length).fill(0));
}
cellSelectStates_temp[i][j]=1; //1表示选中
table_model.cell_addFilter_bright_model=cellSelectStates_temp;
this.state.selectCell1=new Array(i, j); //当前选中的单元格坐标
this.state.userActionType=1; //这个动作记录用户上次的操作
this.setState({
history_record: history.concat(table_model),
});
}
到这一步,我们就可以在表格上,选择范围了。效果如图:
追加行、追加列 插入行 插入列的实现:
先不考虑合并单元格带来的影响。
我甚至设想,最后一行,与最后一列,保留给系统专用,以它们作为坐标参考系。
在这个前提下,我们来实现追加动作。
追加行,就把当前表格最后一行数据复制一份,插入到数据的集合当中,当然,这些数据包括样式数据,目前好像有9、10项吧
table.js
//追加行
appendRow=()=>{
//复制一个history 元素,改变这个元素相关属性
const history = this.state.history_record;
const current =history.slice(history.length - 1)
const table_model = current[0];
let newTable_model=this.appendRowInModel(table_model);
this.setState({
history_record: history.concat(newTable_model),
});
}
appendRowInModel=(table_model)=>{
let data_model=table_model.data_model ; //是个2维数组
//复制最后一行
let lastRowData=data_model.slice(data_model.length - 1);
var newRowData = [...lastRowData];
//复制的数据加入到当前模型当中
table_model.data_model=data_model.concat(newRowData)
//2
let cell_align_model=table_model.cell_align_model ; //是个2维数组
//复制最后一行
let lastRowcell_align_model=cell_align_model.slice(cell_align_model.length - 1);
var newRowlastRowcell_align_model = [...lastRowcell_align_model];
//复制的数据加入到当前模型当中
table_model.cell_align_model=cell_align_model.concat(newRowlastRowcell_align_model)
//3
let cell_border_top_model=table_model.cell_border_top_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_top_model=cell_border_top_model.slice(cell_border_top_model.length - 1);
var newRowlastRowcell_border_top_model = [...lastRowcell_border_top_model];
//复制的数据加入到当前模型当中
table_model.cell_border_top_model=cell_border_top_model.concat(newRowlastRowcell_border_top_model)
//4
let cell_border_bootom_model=table_model.cell_border_bootom_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_bootom_model=cell_border_bootom_model.slice(cell_border_bootom_model.length - 1);
var newRowcell_border_bootom_model = [...lastRowcell_border_bootom_model];
//复制的数据加入到当前模型当中
table_model.cell_border_bootom_model=cell_border_bootom_model.concat(newRowcell_border_bootom_model)
//5
let cell_border_left_model=table_model.cell_border_left_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_left_model=data_model.slice(cell_border_left_model.length - 1);
var newRowcell_border_left_model = [...lastRowcell_border_left_model];
//复制的数据加入到当前模型当中
table_model.cell_border_left_model=cell_border_left_model.concat(newRowcell_border_left_model)
//6
let cell_border_right_model=table_model.cell_border_right_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_right_model=cell_border_right_model.slice(cell_border_right_model.length - 1);
var newRowcell_border_right_model = [...lastRowcell_border_right_model];
//复制的数据加入到当前模型当中
table_model.cell_border_right_model=cell_border_right_model.concat(newRowcell_border_right_model)
//7
let cell_addFilter_bright_model=table_model.cell_addFilter_bright_model ; //是个2维数组
//复制最后一行
let lastRowcell_addFilter_bright_model=cell_addFilter_bright_model.slice(cell_addFilter_bright_model.length - 1);
var newRowcell_addFilter_bright_model = [...lastRowcell_addFilter_bright_model];
//复制的数据加入到当前模型当中
table_model.cell_addFilter_bright_model=cell_addFilter_bright_model.concat(newRowcell_addFilter_bright_model)
//8
let cell_cellBackgroundColor_model=table_model.cell_cellBackgroundColor_model ; //是个2维数组
//复制最后一行
let lastRowcell_cellBackgroundColor_model=cell_cellBackgroundColor_model.slice(cell_cellBackgroundColor_model.length - 1);
var newRowcell_cellBackgroundColor_model = [...lastRowcell_cellBackgroundColor_model];
//复制的数据加入到当前模型当中
table_model.cell_cellBackgroundColor_model=cell_cellBackgroundColor_model.concat(newRowcell_cellBackgroundColor_model)
//9
let cell_cell_font_css_model=table_model.cell_cell_font_css_model ; //是个2维数组
//复制最后一行
let lastRowcell_cell_font_css_model=cell_cell_font_css_model.slice(cell_cell_font_css_model.length - 1);
var newRowcell_cell_font_css_model = [...lastRowcell_cell_font_css_model];
//复制的数据加入到当前模型当中
table_model.cell_cell_font_css_model=cell_cell_font_css_model.concat(newRowcell_cell_font_css_model);
return table_model;
}
整个过程,没考虑优化、没考虑引用可能的影响,先这样吧。
追加行按钮在父组件当,点击按钮,需要调用子组件追加行的的方法。
父组件调用子组件过程分4步:
首先向下传递一个方法,这个方法的目的在于获取子组件的引用。子组件在生命周期方法 componentDidMount 中,会用这个函数传递自身的信息。这样,父组件就可以获取到子组件实例的的引用了.
以worksheet。js为例基本步骤如下:
1、定义workTableRef函数,把这个方法向子组件传递,以获取workTable的引用
2、把workTableRef函数传递给子组件
workshhet.js
//1、定义workTableRef方法,把这个方法向子组件传递,以获取workTable的引用
workTableRef=(ref)=>{
this.workTable = ref
}
.....
2、传递给子组件
<CommTable rowNum={rowNum} colNum={colNum} onRef={this.workTableRef}/>;
3子组件在生命周期方法 componentDidMount 中,调用onRef方法,把自身传递给调用者。
//worktable.js
componentDidMount(){
this.props.onRef(this) //这个就是把自身传给调用者
}
4、调用者调用子类的方法:
workTableRef=(ref)=>{
this.workTable = ref
}
插入行的处理:取得当前行位置 即读取点击的位置,如果没有点击数据 有2种处理,1、啥都不干 2、追加到最后一行,我们先选择方式1进行处理。
插入行、删除行,必须确定当前选中的行。但为简化问题起见,我们只能一次删除一行 插入一行。需要的时候,我们再扩充相关功能。
插入、删除行的时候,额外要做2个处理:
1、清除选中行的记录 cell1、cell2; 如果不清除这个数据,再次点击删除的时候,还会删除一行,这是因为当前行数据记录还存在。
插入行的时候,系统记录的当前行是新插入的行,但被选择样式显示的在老行上,这种情况,有三种处理办法:
- 清除选中信息
- 选中信息移到新插入的行上
- 选择信息向下移动一行
第一种方法,对我来说最简单吧,
2、最后一行原则上不能删除,因为我想把它留着当坐标。
//insertRowType=-1 是追加行
// insertRowType=-2 是插入行 当前位置从state当中取出
//insertRowType=-3 是删除行
appendRowInModel=(table_model,insertRowType)=>{
//取得当前行位置 即读取点击的位置,如果没有点击数据 有2种处理,1、啥都不干 2、追加到最后一行
let p1=-1;
let p2=-1;
if(this.state.selectCell1.length==2){
p1=this.state.selectCell1[0];
}
if(this.state.selectCell2.length==2){
p2=this.state.selectCell2[0];
}
//插入行 删除行 必须指定位置
if(insertRowType==-2 || insertRowType==-3){
if (p1==-1){
alert("请选择一行再执行相关操作")
return -1;}
if(p2!=-1 && p1!=p2){
alert("仅能选择一行再执行相关操作")
return -1;
}
};
let data_model=table_model.data_model ; //是个2维数组
let l=data_model.length - 1;
if(p1==l && insertRowType==-3){
alert('最后一行不能删除');
return -1;
}
//复制最后一行
let lastRowData=data_model.slice(data_model.length - 1);
let newRowData = [...lastRowData];
//1、复制的数据加入到当前模型当中
if(insertRowType==-1){
table_model.data_model=data_model.concat(newRowData);
}else if(insertRowType==-2){
table_model.data_model.splice(p1,0,newRowData[0]);
this.state.selectCell1=[];
this.state.selectCell2=[];
//全表清除选择样式
}else if(insertRowType==-3){ //只删除一行
table_model.data_model.splice(p1,1);
this.state.selectCell1=[];
this.state.selectCell2=[];
}
//2
let cell_align_model=table_model.cell_align_model ; //是个2维数组
//复制最后一行
let lastRowcell_align_model=cell_align_model.slice(cell_align_model.length - 1);
var newRowlastRowcell_align_model = [...lastRowcell_align_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_align_model = cell_align_model.concat(newRowlastRowcell_align_model);
}else if(insertRowType==-2){
cell_align_model.splice(p1,0,newRowlastRowcell_align_model[0]);
}else if(insertRowType==-3){
cell_align_model.splice(p1,1);
}
//3
let cell_border_top_model=table_model.cell_border_top_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_top_model=cell_border_top_model.slice(cell_border_top_model.length - 1);
var newRowlastRowcell_border_top_model = [...lastRowcell_border_top_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_border_top_model = cell_border_top_model.concat(newRowlastRowcell_border_top_model)
}else if(insertRowType==-2){
cell_border_top_model.splice(p1,0,newRowlastRowcell_border_top_model[0]);
}else if(insertRowType==-3){
cell_border_top_model.splice(p1,1);
}
//4
let cell_border_bootom_model=table_model.cell_border_bootom_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_bootom_model=cell_border_bootom_model.slice(cell_border_bootom_model.length - 1);
var newRowcell_border_bootom_model = [...lastRowcell_border_bootom_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_border_bootom_model=cell_border_bootom_model.concat(newRowcell_border_bootom_model);
}else if(insertRowType==-2){
cell_border_bootom_model.splice(p1,0,newRowcell_border_bootom_model[0]);
}else if(insertRowType==-3){
cell_border_bootom_model.splice(p1,1);
}
//5
let cell_border_left_model=table_model.cell_border_left_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_left_model=data_model.slice(cell_border_left_model.length - 1);
var newRowcell_border_left_model = [...lastRowcell_border_left_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_border_left_model=cell_border_left_model.concat(newRowcell_border_left_model);
}else if(insertRowType==-2){
cell_border_left_model.splice(p1,0,newRowcell_border_left_model[0]);
}else if(insertRowType==-3){
cell_border_left_model.splice(p1,1);
}
//6
let cell_border_right_model=table_model.cell_border_right_model ; //是个2维数组
//复制最后一行
let lastRowcell_border_right_model=cell_border_right_model.slice(cell_border_right_model.length - 1);
var newRowcell_border_right_model = [...lastRowcell_border_right_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_border_right_model=cell_border_right_model.concat(newRowcell_border_right_model);
}else if(insertRowType==-2){
cell_border_right_model.splice(p1,0,newRowcell_border_right_model[0]);
}else if(insertRowType==-3){
cell_border_right_model.splice(p1,1);
}
//7
let cell_addFilter_bright_model=table_model.cell_addFilter_bright_model ; //是个2维数组
//复制最后一行
let lastRowcell_addFilter_bright_model=cell_addFilter_bright_model.slice(cell_addFilter_bright_model.length - 1);
var newRowcell_addFilter_bright_model = [...lastRowcell_addFilter_bright_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_addFilter_bright_model=cell_addFilter_bright_model.concat(newRowcell_addFilter_bright_model);
}else if(insertRowType==-2){
cell_addFilter_bright_model.splice(p1,0,newRowcell_addFilter_bright_model[0]);
}else if(insertRowType==-3){
cell_addFilter_bright_model.splice(p1,1);
}
//8
let cell_cellBackgroundColor_model=table_model.cell_cellBackgroundColor_model ; //是个2维数组
//复制最后一行
let lastRowcell_cellBackgroundColor_model=cell_cellBackgroundColor_model.slice(cell_cellBackgroundColor_model.length - 1);
var newRowcell_cellBackgroundColor_model = [...lastRowcell_cellBackgroundColor_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_cellBackgroundColor_model=cell_cellBackgroundColor_model.concat(newRowcell_cellBackgroundColor_model);
}else if(insertRowType==-2){
cell_cellBackgroundColor_model.splice(p1,0,newRowcell_cellBackgroundColor_model[0]);
}else if(insertRowType==-3){
cell_cellBackgroundColor_model.splice(p1,1);
}
//9
let cell_cell_font_css_model=table_model.cell_cell_font_css_model ; //是个2维数组
//复制最后一行
let lastRowcell_cell_font_css_model=cell_cell_font_css_model.slice(cell_cell_font_css_model.length - 1);
var newRowcell_cell_font_css_model = [...lastRowcell_cell_font_css_model];
//复制的数据加入到当前模型当中
if(insertRowType==-1) {
table_model.cell_cell_font_css_model=cell_cell_font_css_model.concat(newRowcell_cell_font_css_model);
}else if(insertRowType==-2){
cell_cell_font_css_model.splice(p1,0,newRowcell_cell_font_css_model[0]);
}else if(insertRowType==-3){
cell_cell_font_css_model.splice(p1,1);
}
if(insertRowType==-2 || insertRowType==-3){
this.clearCssFilter(table_model);
}
return table_model;
}
表格的坐标一直是心里的痛:
一个是搞个和具体表格无关的坐标;但这要搞实际表格和坐标间的转换
一个是全表动态计算坐标
一个是保留特殊的行、列作为坐标参考,例如首行首列,末行、末列。这个似乎最简单,搞首行首列、末行、末列作为保留行列吧,不能在首行首列末行、末列上,做任何操作。
也许用th作为参考更合理?
表格线的样式,我们大概总结了6-7种表格线,每种表格线需要设置一个单元格的4条边的样式。