用react 构建电子表格(5)--给数据插上时间的翅膀

本项目的github地址:https://github.com/whcandle/webExcel.git

使用react,我的一个理念是:数据控制样式 而不是相反。

定位问题:table 容器定义一个position:relative 后面的元素如果需要绝对定位的时候都相对于这个定位。

长度单位:采取pt,这个绝对单位,猜测绝对单位也许对今后要搞的打印有好处?

如何表现被选择的单元格:前面提到:

1、搞个边框线,把选择的单元格框起来,这样做的问题好处是 对表格没啥侵入性,缺陷是略复杂,因为要获取选择的单元个的位置信息(坐标信息),再搞线的定位。

2、修改选择单元格的样式 如修改相关单元格的边线样式、或者修改选择单元格的背景色

修改单元格边线样式?其实要找出选择区域的边界上个各个单元格。

修改选中单元格的背景色,似乎更简单,通过色块即可标记处选择区域。我首先想到采取css滤镜

不知道为什么,我的背景全是lightgrey,那就手动为td加上背景

      return (
            <td  className="ttd"
                 onClick={() => this.clicked()}
                 onDoubleClick={e => this.doubleClicked(e)}
                 className={["ttd", alignCss,cell_font_family_css,cell_font_size_css,
                     cell_font_weight_css, cell_font_style_css,cell_text_decoration_css,
                     cell_border_top_css,cell_border_right_css,cell_border_bootom_css,cell_border_left_css,
                     cell_cell_filter_brightness_css].join(' ')}
                 style={{backgroundColor: this.state.cellBackgroundColor}}
            >
                {this.getCommData(this.state.commData)}
            </td>
        )

cell state中加上:addFilter_bright:1 , //是否被选中的样式,0 是未选择 1是选择,用于控制是否被选中样式。当然,还是通过css控制的

.cell_filter_brightness{
    -webkit-filter:brightness(0.7);
    -o-filter:brightness(0.7);
    -moz-filter:brightness(0.7);
    filter:brightness(0.7);
}

.cell_filter_brightness_1{
    -webkit-filter:brightness(1);
    -o-filter:brightness(1);
    -moz-filter:brightness(1);
    filter:brightness(1);
}

 被选择单元格存在选取范围的情况,脑力不够用,就先静后动,先考虑一个单元格的情况,再考虑选中多个单元格  ,对单元格进行合并等操作后的影响放到后一步考虑,先考虑静态的情况。

现在要做到,就是点击单击单元格引起选中单元格改变样式。

在进一步实现相关功能前,总结一下,目前控制cell已经有如下8个属性,要把相关数据全部提升到table组件中去。

最好把这8个属性,封装成一个对象?算了,es6对象不熟悉的情况,还是写原始的 数据,代码丑陋一点,但也不用那多封装、解封吧。

依靠改变数据来改变样式,效果如下图:

另外一个问题是单元格坐标参考体系:

我从三方面考虑,

  1. 一个是进一步提升数据,到websheet当中,改变一次数据,就形成一个历史记录,这样,在时间轴上,有个参照系,这就是让数据乘上时间列车
  2. 一个是从空间呢上,搞个”绝对”坐标体系,即无论表格如何变化,单元格都由唯一id,且可定位
  3. 一个是单元格都是动态的,需要的时候,需要的时候,均是实时查询其位置

选择2还是3略纠结。

我们希望顶层 table组件展示出一个历史步骤的列表。这个功能需要访问 history 的数据,因此我们把 history 这个 state 放在table 组件中.

history中,记得每一步操作,这样 方便回退,每一步都是history中的一个元素,而一个元素又又另外10个表示数据与样式数组构成。

示意图如下:

[
{
                data_model: {},   //数据
                cell_align_model:{},  //对齐方式
                cell_border_top_model:{},  //边
                cell_border_bootom_model:{},
                cell_border_left_model:{},
                cell_border_right_model:{},
                cell_addFilter_bright_model:{},   //filter
                cell_cellBackgroundColor_model:{}, //背景色
                cell_cell_font_css_model:{},   //字体
                cellSelectStates_model:{}    //是否去掉?

            }  //第一次操作
			
{
                data_model: {},   //数据
                cell_align_model:{},  //对齐方式
                cell_border_top_model:{},  //边
                cell_border_bootom_model:{},
                cell_border_left_model:{},
                cell_border_right_model:{},
                cell_addFilter_bright_model:{},   //filter
                cell_cellBackgroundColor_model:{}, //背景色
                cell_cell_font_css_model:{},   //字体
                cellSelectStates_model:{}    //是否去掉?

            }//第二次操作


{
                data_model: {},   //数据
                cell_align_model:{},  //对齐方式
                cell_border_top_model:{},  //边
                cell_border_bootom_model:{},
                cell_border_left_model:{},
                cell_border_right_model:{},
                cell_addFilter_bright_model:{},   //filter
                cell_cellBackgroundColor_model:{}, //背景色
                cell_cell_font_css_model:{},   //字体
                cellSelectStates_model:{}    //是否去掉?

            }	//第三次操作		
			
			


]

缺陷:每次记得全局数据,这个数据会很大很大!是否只记录影响结构的改变?也许问题不大,我只对改变的值进行深度复制?

先简单处理,记忆每一个步骤。如果出现性能问题 特别是浏览器崩溃问题,再改进。

export default class CommTable extends Component {
    constructor(props) {
        super(props);
        this.state = {
            history_record: [{
                data_model: {},   //数据
                cell_align_model:{},  //对齐方式
                cell_border_top_model:{},  //边
                cell_border_bootom_model:{},
                cell_border_left_model:{},
                cell_border_right_model:{},
                cell_addFilter_bright_model:{},   //filter
                cell_cellBackgroundColor_model:{}, //背景色
                cell_cell_font_css_model:{},   //字体
                cellSelectStates_model:{}    //是否去掉?

            }]
        };

        let r = this.props.rowNum;  //行数
        let c = this.props.colNum;  //列数
        let cellSelectStates=[];    //单元格是否被选中
        let data_init=[]; //初始化数据
        let cell_align_init=[]; //单元格初始化数据
        let cell_border_top_init=[];
        let cell_border_bootom_init=[];
        let cell_border_left_init=[];
        let cell_border_right_init=[];
        let cell_addFilter_bright_init=[];
        let cell_cellBackgroundColor_init=[];
        let cell_cell_font_css_init=[];

//数据初始化
        for(let i=0;i<r;i++){
            cellSelectStates[i]=new Array();
            data_init[i]=new Array();
            cell_align_init[i]=new Array();
            cell_border_top_init[i]=new Array();
            cell_border_bootom_init[i]=new Array();
            cell_border_left_init[i]=new Array();
            cell_border_right_init[i]=new Array();
            cell_addFilter_bright_init[i]=new Array();
            cell_cellBackgroundColor_init[i]=new Array();
            cell_cell_font_css_init[i]=new Array();

            for(let j=0;j<c;j++){
                cellSelectStates[i][j]=false;
                data_init[i][j]="12";
                cell_align_init[i][j]=1; //1 左对齐
                cell_border_top_init[i][j]=0;
                cell_border_bootom_init[i][j]=0;
                cell_border_left_init[i][j]=0;
                cell_border_right_init[i][j]=0;
                cell_addFilter_bright_init[i][j]=0;
                cell_cellBackgroundColor_init[i][j]="#ffffff";
                cell_cell_font_css_init[i][j]=['microYH','medium','font-weight_normal','font-sytle_normal','none'];
            }
        }
        data_init[9][5]="test!!!!";
        cell_align_init[10][5]=2;
//初始化
        this.state.history_record[0].data_model=data_init;
        this.state.history_record[0].cell_align_model=cell_align_init;
        this.state.history_record[0].cell_border_top_model=cell_border_top_init;
        this.state.history_record[0].cell_border_bootom_model=cell_border_bootom_init;
        this.state.history_record[0].cell_border_left_model=cell_border_left_init;
        this.state.history_record[0].cell_border_right_model=cell_border_right_init;
        this.state.history_record[0].cell_addFilter_bright_model=cell_addFilter_bright_init;
        this.state.history_record[0].cell_cellBackgroundColor_model=cell_cellBackgroundColor_init;
        this.state.history_record[0].cell_cell_font_css_model=cell_cell_font_css_init;
        this.state.history_record[0].cellSelectStates_model=cellSelectStates;

    }
......

有必要指出的是:目前所有数据都是table对象管理,row、cell对象没有记忆数据,它们state 中没有具体内容了!row、cell对象当中数据,都是通过this.props中获取

我们再回到单元格单击,单击单元格的时候,要改变单击单元格的样式,改变相应的数据即可:

/**table.js**/

//处理单元格选择   主要是改变相关样式数据
    handleCellSelect = (i,j) => {
        //复制一个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);
        }
        cellSelectStates_temp[i][j]=1;  //1表示选中
        table_model.cell_addFilter_bright_model=cellSelectStates_temp;

        this.setState({
            history_record: history.concat(table_model),

        });
    }

注意以上代码中,深度复制的应用。这里就不解释了。

看效果图:

点击单元格,应用filter的时候,上次被选中的单元格的filter必须去掉。

这有二种思路:

  1. 遍历全部filter数据,还原为原始状态;
  2. 记忆上一次加了filter的数据信息,仅仅还原这些被记忆的数据。

先采取思路1:这种选择的优势是:保持全局数据一致,关键是对我来说,so easy!

       for(let i=0;i<cellSelectStates_s.length;i++){
//全部为未选择状态
            cellSelectStates_temp.push(new Array(cellSelectStates_s[i].length).fill(0));
        }

一个单元格的选择的问题到这里算解决了,下一步,解决选择范围的问题,当然,一个范围内含有多个单元格。

选择表格范围的问题:必须记录选择范围内的cell对象信息,记录什么呢,最直接想到的当然是行列坐标。用一个数组存储每个单元格的坐标吧,数组中的每一个元素就是一对坐标值。

下一节将描述这个问题。

单元格背景颜色的设置方式,和前面提到的动态样式,略有不同

//commCell.js
......       
return (
            <td  className="ttd"
                 onClick={() => this.clicked()}
                 onDoubleClick={e => this.doubleClicked(e)}
                 className={["ttd", alignCss,cell_font_family_css,cell_font_size_css,
                     cell_font_weight_css, cell_font_style_css,cell_text_decoration_css,
                     cell_border_top_css,cell_border_right_css,cell_border_bootom_css,cell_border_left_css,
                     cell_cell_filter_brightness_css].join(' ')}
                 style={{backgroundColor: this.props.commcellBackgroundColor}}
            >
                {this.getCommData(this.props.commData)}
            </td>
        )
......

react hooks 是什么鬼?参考:轻松学会 React 钩子:以 useEffect() 为例  Why React Hooks?

高阶组件是什么鬼?如果有了react hooks,高阶组件我暂时不打算用。

ps1:react中使用css的7种方式

ps2:项目github 地址:https://github.com/whcandle/webExcel.git

ps3:教你如何上传项目到GitHub

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值