用react 构建电子表格(9)---单元格编辑与单元格公式

单元格编辑要考虑的因素:

  1. 数据模型本身应该是啥样的:  
  2. 数据模型的数据传达到cell:略
  3. cell数据的显示
  4. 必须有个编辑属性  表示该单元是否可编辑:cell_editing_model
  5. 根据是否可编辑属性,决定如何呈现一个单元格
  6.             if(this.props.commCell_editing_mode==0){
    
                return (
                    <td  className="ttd"
                         onClick={e => this.clicked(e)}
                         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_bottom_css,cell_border_left_css,
                             cell_cell_filter_brightness_css].join(' ')}
                         rowSpan={_rowSpan}
                         colSpan={_colSpan}
                         style={{backgroundColor: this.props.commcellBackgroundColor}}
                    >
                        {this.getCommData(this.props.commValue)}
    
    
                    </td>
                )
    
                }else{
                    return (
                        <td  className="ttd"
                             onClick={e => this.clicked(e)}
                             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_bottom_css,cell_border_left_css,
                                 cell_cell_filter_brightness_css].join(' ')}
                             rowSpan={_rowSpan}
                             colSpan={_colSpan}
                             style={{backgroundColor: this.props.commcellBackgroundColor}}
                        >
                            {this.getCommDataInput(this.props.commValue)}
                        </td>
                    )
                }
  7. 双击事件,要改变单元格对应的edit  及  select 这2个值,
  8. 单击改变了什么:改变单元格对应的select值。

事件体系

  1. onchange
  2. 回车键  除了改变 value_mode外,还应该改变  edit_mode,及选择模型吧
  3. onblur  当当前输入框失去焦点的时候,会调用和回车键一样的事件
  4. 点击其他单元格对当前输入框的影响 点击其他单元格的时候,本来只是改变edit_mode   及select_mode.但会先触发blur事件

输入框回车键,:更改mode_value、edit_mode、selected_mode,按回车键,才能真正修改模型值?

  /**
     * Handles changing a cell, stores the new data state and stores into
     * local model
     */
    handleChangedCell = (i,j, value) =>{
        const history = this.state.history_record;
        const current =history.slice(history.length - 1)
        const table_model = current[0];
        //let cellSelectStates_s=table_model.cell_addFilter_bright_model ;
        //let _cell_editing_model=table_model.cell_editing_model;
        table_model.value_model[i][j].text=value;
        //改变编辑状态和选择状态
        this.transformState_edit(table_model,i,j,0,1);
        this.setState({
            history_record: history.concat(table_model),
        });
    }

选择其他单元格

onchange:到目前为止,我对onchange事件的理解为:改变显示的值,如果不按回车键,不会去修改value_model

ps:

在react中是无法直接更改from表单元素的值的,必须通过setState()去响应用户的输入。例如想要更改input的value,则需要监听onChange()事件,然后通过event.target.value来获取用户的输入,再通过设置一个名为value的state,来告诉react重新渲染。

onChange(event) {

this.setState({value: event.target.value});
}
<input value={this.state.value} onChange={this.onChange}/>

单元格编辑,呈现的时候,应该带上单元格样式,可以考虑带上单元格的宽度、高度,我们先简化处理,让input的width height 为100%

cell_editing_model 也是个二维数组,cell_editing_model:[i][j]=0  或者 cell_editing_model:[i][j]=0
value_model  是值的对象数组,value_model_init[i][j]={text:"xx",type:"String"};

新追加这2个描述数据与可编辑性的值,要相应调整追加行、追加列、插入行、插入列、删除行、删除列的代码。

追加行的时候,也是往cell_editing_model、value_model 追加一行数据。采取深度clone方式应该更保险。

还有个要注意的问题:当存在编辑框的时候,原则上,应该不容许添加 删除 行列,因为 我们编辑框修改数据的时候,要根据行、列坐标值

单元格公式:

核心是 hot-formula-parser的应用。

1、单元格公式的解析必须放到table组件当中,因为公式解析必须了解数据全貌

2、构造函数中初始化这个解析器

3、公式解析前进行必要的检测

4、写解析方法

         constructor(props) {
......
 // Initialize the formula parser on demand
        this.parser = new FormulaParser()   
....}


.... // check

        // When a formula contains a cell value, this event lets us
        // hook and return an error value if necessary
        //Fired while retrieving cell value by its label (eq: B3, B$3, B$3, $B$3).
        this.parser.on('callCellValue', (cellCoord, done) => {
             const x = cellCoord.column.index + 1
             const y = cellCoord.row.index + 1
            //const x = cellCoord.column.index
            //const y = cellCoord.row.index

            // Check that the cell is not self referencing
            if (this.parser.cell.x === x && this.parser.cell.y === y) {
                throw this.parser.Error(this.parser.ERROR_REF)
            }
            let _value_model=
                this.state.history_record[this.state.history_record.length-1].value_model;
            // if (!this.state.data[y] || !this.state.data[y][x]) {
            //     return done('')
            // }
            if (!_value_model[y] || !_value_model[y][x]) {
                return done('')
            }


            // All fine
            //let r=this.state.data[y][x];
            let t1=this.state.history_record[this.state.history_record.length-1].value_model;
            console.log(t1);
            let t2=t1[y][x];
            console.log(t2)
            return done(t2)
        })

        // When a formula contains a range value, this event lets us
        // hook and return an error value if necessary
        this.parser.on('callRangeValue', (startCellCoord, endCellCoord, done) => {
            const sx = startCellCoord.column.index + 1
            const sy = startCellCoord.row.index + 1
            const ex = endCellCoord.column.index + 1
            const ey = endCellCoord.row.index + 1
            const fragment = []
            let _value_model=this.state.history_record[this.state.history_record.length-1].value_model;
            for (let y = sy; y <= ey; y += 1) {
                //const row = this.state.data[y]
                const row = _value_model[y]

                if (!row) {
                    continue
                }

                const colFragment = []

                for (let x = sx; x <= ex; x += 1) {
                    let value = row[x]
                    if (!value) {
                        value = ''
                    }

                    if (value.slice(0, 1) === '=') {
                        const res = this.executeFormula({ x, y }, value.slice(1))
                        if (res.error) {
                            throw this.parser.Error(res.error)
                        }
                        value = res.result
                    }

                    colFragment.push(value)
                }
                fragment.push(colFragment)
            }

            if (fragment) {
                done(fragment)
            }
        })



    }


....



 /**
     * Executes the formula on the `value` usign the FormulaParser object
     */
    executeFormula = (cell, value) => {
        this.parser.cell = cell
        console.log(value);
        let res = this.parser.parse(value)
        if (res.error != null) {
            console.log(res.error);
            return res // tip: returning `res.error` shows more details
        }
        if (res.result.toString() === '') {
            return res
        }
        if (res.result.toString().slice(0, 1) === '=') {
            // formula points to formula
            res = this.executeFormula(cell, res.result.slice(1))
        }
        return res
    }

5、cell 显示的时候,调用解析函数 

    /**
     * Executes the formula calculation on the cell value
     */
    determineDisplay = ({ x, y }, valueObject) => {
        //if (valueObject.text.slice(0, 1) === '=') {
            if (valueObject.slice(0, 1) === '=') {
            //const res = this.props.executeFormula({ x, y }, valueObject.text.slice(1))
            const res = this.props.executeFormula({ x, y }, valueObject.slice(1))
            if (res.error !== null) {
                return 'INVALID'
            }
            return res.result
        }
        //return valueObject.text
        return valueObject;
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您,${username}!</h1> <p><a href="UserServlet?action=logout">退出登录</a></p要动态合并单元格,需要在渲染表格之前对表格数据进行处理,合并需要合并> </body> </html> ``` 6. 配置web.xml文件 在`web.xml`文件中配置Servlet和JSP的行或列,并将处理后的数据传递给 `react-bootstrap-table-next` 组件进行渲染。 下面是一个例页面的映射关系: ```xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="子,演示如何在渲染表格之前动态合并单元格: ```javascript import BootstrapTable from 'http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" react-bootstrap-table-next'; import 'react-bootstrap-table-next/dist/react-bootstrap-table2.min.css'; function mergeCells(data, mergeColumns) { xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web // 处理需要合并的列 const merged = []; let i = 0; while (i < data.length)-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>UserServlet</s { const row = data[i]; let j = i + 1; while (j < data.length && mergeColumns.every((ervlet-name> <servlet-class>com.example.servlet.UserServlet</servlet-class> </servlet> <servlet-mcol) => data[j][col] === row[col])) { j++; } if (j - i > 1) { apping> <servlet-name>UserServlet</servlet-name> <url-pattern>/UserServlet</url-pattern> </servlet // 合并需要合并的行或列 const mergedRow = { ...row }; mergeColumns.forEach((col) =>-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web mergedRow[col] = { value: row[col], rowSpan: j - i }); merged.push(mergedRow); } else { -app> ``` 7. 运行项目 将项目部署到Tomcat服务器上,启动服务器后在浏览器中 // 不需要合并的行或列 merged.push(row); } i = j; } return merged; 访问`http://localhost:8080/`即可进入主页,点击“立即开始”按钮进入登录页面} const data = [ { id: 1, name: 'John', age: 30, gender: ',输入正确的用户名和密码后,进入欢迎页面,点击“退出登录”按钮即可注销用户。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值