简单来说:一个表格控件的的每一行都是一个表单,一行里每个单元个都是这个表单的输入项目,每个输入项目都有一个弹出菜单
完整代码参考 gitee
所有从datajson中读取到的数据,都是以一维数组的形式存储在adapter的datas属性中的,只有在在调用toDataSource()
方法后,adapter才会按照指定的nRow
和nColumn
生成antd-table控件可识别的datasource对象
toColumns(onCellHandle)
参数,会将antd-table在绘制每个单元格时的默认参数向外传递,可以通过扩展onCellHandle的返回值,实现单元格绘制的传参和事件监听
import * as _ from "lodash"
/**
* 可编辑表格内部的单元格实体类
*/
class EdittableCellData {
constructor(text = "", style = undefined, editable = false) {
this.text = text
//定义单元格的缺省样式(即在配置文件中没有指定时的样式)
if (style === undefined)
this.style = {
fontWeight: 500,
fontSize: "14px",
fontStyle: "normal",
textAlign: "center",
backgroundColor: "transparent",
color: 'gray',
fontFamily: '微软雅黑',
columnWidth: undefined
}
else {
this.style = style
}
this.editable = editable
}
}
/**
* 可编辑表格的Adapter实体类
*/
class EdittableAdapter {
constructor() {
/**
* 单元格数据与样式数据
*/
this.datas = undefined;
/**
* 当前表格的总行数
*/
this.nRow = undefined;
/**
* 当前表格的总列数
*/
this.nColumn = undefined;
/**
* 当前表格的总体样式
*/
this.style = undefined;
/**
* table的key
*/
this.prefix = undefined;
/**
* 需要跳过不显示的列索引数组
*/
this.skipColums = []
}
/**
* 指定行数和列数,构造具有默认样式的适配器对象
* @param {number} initialRows
* @param {number} initialCols
* @returns 修改后的适配器对象本身
*/
initial(initialRows, initialCols) {
this.datas = new Array(initialRows * initialCols)
this.nRow = initialRows;
this.nColumn = initialCols;
this.style = undefined;
this.datas = _.fill(this.datas, new EdittableCellData())
return this;
}
/**
* 从配置文件中读取配置参数,并实例化当前适配器对象
* @param {object} dataConfig
* @returns 修改后的适配器对象本身
*/
loadConfig(dataConfig) {
this.nRow = dataConfig.nRow
this.nColumn = dataConfig.nColumn
this.style = dataConfig.style
this.datas = dataConfig.datas
this.datas.forEach(value => {
//如果当前单元格没有默认样式,则赋值为表格统一的样式
Object.keys(this.style).forEach(propsName => {
if (value.style.hasOwnProperty(propsName) === false) {
value.style[propsName] = this.style[propsName]
}
})
})
this.prefix = dataConfig.prefix;
this.skipColumns = [...dataConfig.skipColumns];
return this;
}
toJsonConfig() {
return JSON.stringify(this)
}
/**
* 深拷贝方式构建一个新的适配器对象
* @returns 新的适配器对象
*/
toNewInstance() {
return new EdittableAdapter().loadConfig(JSON.parse(this.toJsonConfig()))
}
/**
* 生成一行数据对应的json对象的key值,主要用于table控件的columns进行统一
* @param {number} col 从0开始的列序号
* @returns [prefix+"_"]+"col-"+col
*/
_itemColKey(col) {
if (this.prefix)
return this.prefix + "_col-" + col
else
return "col-" + col
}
/**
* 将ColKey转换为数字形式的列序号
* @param {string} 形如\[prefix+"_"]+"col-"+col
* @returns 从0开始的列序号
*/
_itemColKeyIndex(colkey){
return colkey.split("-")[1] * 1
}
/**
* 按照行列数信息转换为AntD的Table组件可以识别的数据源
* @returns 元素形如{key:0,col-0:a,col-1:b}数组,一个元素为Table组件中的一行
*/
toDataSource() {
let result = []
for (let row = 0; row < this.nRow; row++) {
let tempRow = { "key": row }
for (let col = 0; col < this.nColumn; col++) {
tempRow[this._itemColKey(col)] = this.datas[row * this.nColumn + col]
}
result.push(tempRow)
}
return result;
}
/**
* 生成为AntD的Table组件可以识别的列名数组
* @param {*} onCellHandle 绘制每个单元格的回调,参数为{行号,列名,单元格内容}
* @returns
*/
toColumns(onCellHandle) {
let result = []
for (let col = 0; col < this.nColumn; col++) {
//跳过忽略列
if (this.skipColumns.includes(col) === false) {
result.push({
"title": this._itemColKey(col),
"dataIndex": this._itemColKey(col),
"onCell": (record, rowIndex) => {
if (onCellHandle)
return onCellHandle(rowIndex, this._itemColKey(col), record[this._itemColKey(col)])
},
})
}
}
return result
}
/**
* 内容行列互换,改方法修改当前数据本身
*/
transpose() {
let result = new Array(this.nColumn * this.nRow)
for (let row = 0; row < this.nRow; row++) {
for (let col = 0; col < this.nColumn; col++) {
let { text, style, editable } = this.datas[row * this.nColumn + col]
result[col * this.nRow + row] = new EdittableCellData(text, style, editable)
}
}
this.datas = Object.assign(result)
let temp = this.nRow;
this.nRow = this.nColumn;
this.nColumn = temp
return this;
}
/**
* 利用新数组对元表格的中值进行替换,该方法不修改单元格的样式
* @param {array} newDataArray
* @returns 修改后的适配器本身
*/
fillCellData(newDataArray) {
//只有新值数组和原始数组长度一直时才进行替换,如果
if (newDataArray.length === this.datas.length) {
let newDatas = []
for (let index = 0; index < this.datas.length; index++) {
let { style, editable } = this.datas[index]
newDatas.push(new EdittableCellData(newDataArray[index], style, editable))
}
this.datas = [...newDatas]
}
return this;
}
/**
* 填充或修改表格中一行的值
* @param {number} rowIndex 行序号:从0开始
* @param { \{key:0,col-0:a,col-1:b}} rowData 用于填充或修改的对象:形如{key:0,col-0:a,col-1:b}
* @returns 修改后的适配器本身
*/
fillRowData(rowIndex, rowData) {
for (let row = 0; row < this.nRow; row++) {
if (rowIndex === row) {
for (let col = 0; col < this.nColumn; col++) {
let { style, editable } = this.datas[rowIndex * this.nColumn + col]
this.datas[rowIndex * this.nColumn + col] = new EdittableCellData(rowData[this._itemColKey(col)], style, editable)
}
}
}
return this;
}
/***
* 获取指定行序号和列标签的单元格的样式
* @param {number} rowIndex 行序号:从0开始
* @param {string} colkey 列标签,形如\[prefix+"_"]+"col-"+col
* @returns 指定单元格的样式,以css实体类形式存储
*/
getCellStyle(rowIndex, colkey) {
let colIndex = this._itemColKeyIndex(colkey)
return this.datas[rowIndex * this.nColumn + colIndex].style
}
/***
* 更新指定行序号和列标签的单元格的样式
* @param {number} rowIndex 行序号:从0开始
* @param {string} colkey 列标签,形如\[prefix+"_"]+"col-"+col
* @param {string} cssStyle css实体类形式存储的样式
* @returns 修改后的适配器本身
*/
updateCellStyle(rowIndex, colkey, cssStyle) {
let colIndex = this._itemColKeyIndex(colkey)
if (cssStyle !== undefined) {
let { text, editable } = this.datas[rowIndex * this.nColumn + colIndex]
this.datas[rowIndex * this.nColumn + colIndex] = new EdittableCellData(text, cssStyle, editable)
}
return this;
}
/***
* 更新所有的单元格的样式
* @param {string} cssStyle css实体类形式存储的样式
* @returns 修改后的适配器本身
*/
updateAllCellsStyle(cssStyle) {
this.style = Object.assign(cssStyle)
for (let row = 0; row < this.nRow; row++) {
for (let col = 0; col < this.nColumn; col++) {
let { text, editable } = this.datas[row * this.nColumn + col]
this.datas[row * this.nColumn + col] = new EdittableCellData(text, cssStyle, editable)
}
}
return this;
}
/***
* 更新指定行的单元格的样式
* @param {number} rowIndex 行序号:从0开始
* @param {string} cssStyle css实体类形式存储的样式
* @returns 修改后的适配器本身
*/
updateRowStyle(rowindex, cssStyle) {
for (let col = 0; col < this.nColumn; col++) {
this.updateCellStyle(rowindex, this._itemColKey(col), cssStyle)
}
return this;
}
/***
* 更新指定行的单元格的样式
* @param {string} colkey 列标签,形如\[prefix+"_"]+"col-"+col
* @param {string} cssStyle css实体类形式存储的样式
* @returns 修改后的适配器本身
*/
updateColumnStyle(colindex, cssStyle) {
for (let row = 0; row < this.nRow; row++) {
this.updateCellStyle(row, colindex, cssStyle)
}
return this;
}
/**
* 强制调整指定列内所有单元格的宽度
* @param {string} colkey 列标签,形如\[prefix+"_"]+"col-"+col
* @param {number} columnWidth 列宽的像素数值
* @returns
*/
updateColumnWidth(colkey, columnWidth) {
for (let row = 0; row < this.nRow; row++) {
let style = this.getCellStyle(row, colkey)
let newStyle = { ...style, columnWidth: columnWidth + "px" }
this.updateCellStyle(row, colkey, newStyle)
}
return this;
}
/**
* 从startRow行开始,在其上一行位置插入一行元素
*/
insertRow(startRow) {
let head = _.slice(this.datas, 0, startRow * this.nColumn)
let temp = _.fill(new Array(this.nColumn), new EdittableCellData("", this.style, true))
let tail = _.slice(this.datas, startRow * this.nColumn)
this.datas = Object.assign([...head, ...temp, ...tail])
this.nRow = this.datas.length / this.nColumn
}
/**
* 从startRow行开始,在其下一行的位置追加一行元素
* */
appendRow(startRow) {
let head = _.slice(this.datas, 0, (startRow + 1) * this.nColumn)
let temp = _.fill(new Array(this.nColumn), new EdittableCellData("", this.style, true))
let tail = _.slice(this.datas, (startRow + 1) * this.nColumn)
this.datas = Object.assign([...head, ...temp, ...tail])
this.nRow = this.datas.length / this.nColumn
}
/**
* 删除指定startRow索引行的元素
*/
deleteRow(startRow) {
if (startRow + 1 === this.nRow) {
this.datas = Object.assign(_.slice(this.datas, 0, (startRow) * this.nColumn))
}
else {
let head = _.slice(this.datas, 0, (startRow) * this.nColumn)
let tail = _.slice(this.datas, (startRow + 1) * this.nColumn)
this.datas = Object.assign([...head, ...tail])
}
this.nRow = this.datas.length / this.nColumn
}
insertColumn(startCol) {
startCol = startCol * 1
let result = []
for (let row = 0; row < this.nRow; row++)
for (let col = 0; col < this.nColumn; col++) {
if (startCol === col) {
result.push(new EdittableCellData("", this.style, true))
}
result.push(this.datas[row * this.nColumn + col])
}
this.datas = Object.assign(result)
this.nColumn = this.datas.length / this.nRow
}
appendColumn(startCol) {
startCol = startCol * 1
let result = []
if (startCol + 1 >= this.nColumn) {
for (let row = 0; row < this.nRow; row++) {
for (let col = 0; col < this.nColumn; col++) {
result.push(this.datas[row * this.nColumn + col])
}
result.push(new EdittableCellData("", this.style, true))
}
}
else {
for (let row = 0; row < this.nRow; row++)
for (let col = 0; col < this.nColumn; col++) {
if (startCol + 1 === col) {
result.push(new EdittableCellData("", this.style, true))
}
result.push(this.datas[row * this.nColumn + col])
}
}
this.datas = Object.assign(result)
this.nColumn = this.datas.length / this.nRow
}
deleteColumn(startCol) {
startCol = startCol * 1
let result = []
for (let row = 0; row < this.nRow; row++) {
for (let col = 0; col < this.nColumn; col++) {
if (startCol !== col) {
result.push(this.datas[row * this.nColumn + col])
}
}
}
this.datas = Object.assign(result)
this.nColumn = this.datas.length / this.nRow
}
}
export default EdittableAdapter
props.onStyleChanged
回调将相关参数传递给调用者props.onStructChanged
回调将相关参数传递给调用者import { Select, Radio, Button, Dropdown, Menu, Tooltip } from 'antd';
import {
BoldOutlined,
ItalicOutlined,
AlignLeftOutlined,
AlignCenterOutlined,
AlignRightOutlined,
PlusSquareOutlined,
MinusSquareOutlined,
VerticalLeftOutlined,
VerticalRightOutlined
} from '@ant-design/icons';
import React, { useEffect, useState } from 'react';
import "./EdittableCellMenu.scss"
import ColorPicker from './colorPicker/ColorPicker';
import config from "./EdittableCellMenuConfig.json"
/**
* 单元格弹出菜单组件,基于react-Hook编写
* @param {*} props
* @returns
*/
function EdittableCellContextMenu(props) {
const [cssStyle, SetCssStyle] = useState(props.cssStyle)
const [deleteRowEnable, setDeleteRowEnable] = useState(false)
const [deleteColumnEnable, setDeleteColumnEnable] = useState(false)
useEffect(() => {
setDeleteRowEnable(props.nrow <= 1)
}, [props.nrow])
useEffect(() => {
setDeleteColumnEnable(props.ncolumn <= 1)
}, [props.ncolumn])
function generateFontsizeOption() {
return config.fontSizeList.map(value => {
return <Select.Option key={value} value={value + "px"}>{value}</Select.Option>
})
}
function generateFontOption() {
return config.fonts.map(value => <Select.Option className="fontname-option"
key={value.name}
style={{ fontSize: "5px" }}
value={value.name}>{`${value.alias}`}
</Select.Option>)
}
function genearateAddMenu() {
return <Menu onClick={onAddMenuItemClicked}>
<Menu.Item key="insertRow">上方插入一行</Menu.Item>
<Menu.Item key="appendRow">下方追加一行</Menu.Item>
<Menu.Item key="insertColumn">左侧插入一列</Menu.Item>
<Menu.Item key="appendColumn">右侧追加一列</Menu.Item>
</Menu>
}
function genearateRemoveMenu() {
return <Menu onClick={onDeleteItemClicked}>
<Menu.Item key="deleteRow" disabled={deleteRowEnable}>删除当前行</Menu.Item>
<Menu.Item key="deleteColumn" disabled={deleteColumnEnable}>删除当前列</Menu.Item>
</Menu>
}
function genearateStyleApplyMenu() {
return <Menu onClick={(item) => onStyleApplyedClicked(item.key)}>
<Menu.Item key="row" disabled={deleteRowEnable}>应用到行</Menu.Item>
<Menu.Item key="column" disabled={deleteColumnEnable}>应用到列</Menu.Item>
<Menu.Item key="reset">清除样式</Menu.Item>
</Menu>
}
function executeStructCallback(key) {
if (props.onStructChanged) {
props.onStructChanged(key, props.rowindex, props.colindex)
}
}
function executeStyleCallback(cssStyle) {
if (props.onStyleChanged) {
props.onStyleChanged(cssStyle, props.rowindex, props.colindex)
SetCssStyle(cssStyle)
}
}
function onAlignTypeChanged(e) {
executeStyleCallback({ ...cssStyle, textAlign: e.target.value })
}
function onFontSizeChanged(value) {
executeStyleCallback({ ...cssStyle, fontSize: value })
}
function onFontChanged(value) {
executeStyleCallback({ ...cssStyle, fontFamily: value })
}
function onColorPickerChanged(type, color) {
if (type === "fill") {
executeStyleCallback({ ...cssStyle, backgroundColor: color })
}
else {
executeStyleCallback({ ...cssStyle, color: color })
}
}
function onFontBoldChanged(e) {
let { fontWeight } = cssStyle
let temp = fontWeight === 700 ? 500 : 700;
executeStyleCallback({ ...cssStyle, fontWeight: temp })
}
function onFontItalicChanged(e) {
let { fontStyle } = cssStyle
let temp = fontStyle === "normal" ? "italic" : "normal";
executeStyleCallback({ ...cssStyle, fontStyle: temp })
}
function onAddMenuItemClicked(item) {
executeStructCallback(item.key)
}
function onDeleteItemClicked(item) {
executeStructCallback(item.key)
}
function onStyleApplyedClicked(type) {
if (props.onStyleApplyed) {
props.onStyleApplyed(type, cssStyle, props.rowindex, props.colindex)
}
}
function onColumnWidthClicked(type) {
if (props.onColumnWidthClicked) {
props.onColumnWidthClicked(type, props.rowindex, props.colindex)
}
}
return (
<div className="edittable-toobar-warpper">
<Select
bordered={false}
defaultValue={cssStyle.fontFamily}
size="small"
onSelect={onFontChanged}>
{generateFontOption()}
</Select>
<span className={"edittable-toobar-verticalline"}>|</span>
<Select
bordered={false}
defaultValue={cssStyle.fontSize}
size="small"
onSelect={onFontSizeChanged}>
{generateFontsizeOption()}
</Select>
<span className={"edittable-toobar-verticalline"}>|</span>
<Button
icon={<BoldOutlined />}
className={cssStyle.fontWeight === 700 ? "active" : ""}
size="small"
type="text"
onClick={onFontBoldChanged}>
</Button>
<span className={"edittable-toobar-verticalline"}>|</span>
<Button
icon={<ItalicOutlined />}
className={cssStyle.fontStyle === "italic" ? "active" : ""}
size="small"
type="text"
onClick={onFontItalicChanged}>
</Button>
<span className={"edittable-toobar-verticalline"}>|</span>
<ColorPicker
color={cssStyle.backgroundColor === "transparent" ? "gray" : cssStyle.backgroundColor}
type="fill"
onChange={onColorPickerChanged}></ColorPicker>
<span className={"edittable-toobar-verticalline"}>|</span>
<ColorPicker
color={cssStyle.color}
type="text"
onChange={onColorPickerChanged}></ColorPicker>
<span className={"edittable-toobar-verticalline"}>|</span>
<Radio.Group
size="small"
defaultValue={cssStyle.textAlign}
onChange={onAlignTypeChanged}>
<Radio.Button value="left"><AlignLeftOutlined /></Radio.Button>
<Radio.Button value="center"><AlignCenterOutlined /></Radio.Button>
<Radio.Button value="right"><AlignRightOutlined /></Radio.Button>
</Radio.Group>
<span className={"edittable-toobar-verticalline"}>|</span>
<Radio.Group
size="small">
<Tooltip placement="topLeft" title="加宽列">
<Button
type="text"
onClick={() => onColumnWidthClicked("add")}
icon={<VerticalLeftOutlined />}></Button>
</Tooltip>
<Tooltip placement="topLeft" title="收缩列">
<Button
type="text"
onClick={() => onColumnWidthClicked("reduce")}
icon={<VerticalRightOutlined />}></Button>
</Tooltip>
</Radio.Group>
<span className={"edittable-toobar-verticalline"}>|</span>
<Dropdown
placement="topCenter"
overlay={genearateStyleApplyMenu()}>
<Button
size="small"
type="text">样式应用</Button>
</Dropdown>
<span className={"edittable-toobar-verticalline"}>|</span>
<Dropdown
placement="topCenter"
overlay={genearateAddMenu()}>
<Button
size="small"
type="text"
icon={<PlusSquareOutlined />}></Button>
</Dropdown>
<Dropdown
placement="topCenter"
overlay={genearateRemoveMenu()}>
<Button
size="small"
type="text"
icon={<MinusSquareOutlined />}></Button>
</Dropdown>
</div>
);
}
EdittableCellContextMenu.appendRow = "appendRow"
EdittableCellContextMenu.insertRow = "insertRow"
EdittableCellContextMenu.deleteRow = "deleteRow"
EdittableCellContextMenu.insertColumn = "insertColumn"
EdittableCellContextMenu.appendColumn = "appendColumn"
EdittableCellContextMenu.deleteColumn = "deleteColumn"
export default EdittableCellContextMenu;
_onCellHandle
函数的参数有antd自己提供,返回值将作为EdittableCellComponent
组件的props参数传入
import React, { Component, useContext, useEffect } from 'react';
import { Form, Table, Popover, Input } from "antd"
import EdittableAdapter from './EdittableAdapter';
import "./EdittableComponent.scss"
import EdittableCellContextMenu from './EdittableCellMenu';
const EditableContext = React.createContext(null);
class EdittableComponent extends Component {
constructor(props) {
super(props)
this.state = {
adapter: new EdittableAdapter().loadConfig(this.props.dataSource),
width: props.width ? props.width : 2000,
height: props.height ? props.height : 200,
}
this.onCellDataChanged = props.onCellDataChanged
}
/**
* 自定义绘制单元格的回调,当前函数的返回值,会作为EdittableCellComponent的props参数被传入
* @param {*} rowindex
* @param {*} colindex
* @param {*} cell
* @returns
*/
_onCellHandle = (rowindex, colindex, cell) => {
let { adapter } = this.state;
return {
rowindex,
colindex,
cell,
width: this.state.width,
height: this.state.height,
nRow: adapter.nRow,
nColumn: adapter.nColumn,
//单元格输入框值变化时,发生的回调,参数包括:当前触发行,当前触发行的所有值,当前触发列,当前修改值
onCellDataChanged: (rowindex, values, colindex, value) => {
let { adapter } = this.state;
this.setState({ adapter: adapter.fillRowData(rowindex, values).toNewInstance() }, () => {
//向外传递数据修改回调
if (this.onCellDataChanged) {
this.onCellDataChanged(rowindex, colindex, value, adapter.toDataSource())
}
})
},
onCellStyleChanged: (cssStyle, rowindex, colindex) => {
let { adapter } = this.state;
this.setState({ adapter: adapter.updateCellStyle(rowindex, colindex, cssStyle).toNewInstance() })
},
onTableStructChanged: (key, rowindex, colindex) => {
let { adapter } = this.state;
switch (key) {
case EdittableCellContextMenu.appendRow:
adapter.appendRow(rowindex)
break;
case EdittableCellContextMenu.insertRow:
adapter.insertRow(rowindex)
break;
case EdittableCellContextMenu.insertColumn:
adapter.insertColumn(adapter._itemColKeyIndex(colindex))
break;
case EdittableCellContextMenu.appendColumn:
adapter.appendColumn(adapter._itemColKeyIndex(colindex))
break;
case EdittableCellContextMenu.deleteColumn:
adapter.deleteColumn(adapter._itemColKeyIndex(colindex))
break;
default:
adapter.deleteRow(rowindex)
break;
}
this.setState({ adapter: adapter.toNewInstance() })
},
onStyleApplyed: (type, cssStyle, rowindex, colindex) => {
let { adapter } = this.state;
if (type === "row") {
this.setState({ adapter: adapter.updateRowStyle(rowindex, cssStyle).toNewInstance() })
}
else if (type === "column") {
this.setState({ adapter: adapter.updateColumnStyle(colindex, cssStyle).toNewInstance() })
}
else {
let tableStyle = adapter.style
this.setState({ adapter: adapter.updateAllCellsStyle(tableStyle).toNewInstance() })
}
},
onColumnWidthClicked: (type, rowindex, colindex) => {
let { adapter } = this.state;
let initalWidth = this.state.width / adapter.nColumn
let { columnWidth } = adapter.getCellStyle(rowindex, colindex)
let newWidth = 0;
if (columnWidth === undefined || columnWidth === null) {
newWidth = type === "add" ? initalWidth + 5 : initalWidth - 5
}
else {
columnWidth = columnWidth.split("p")[0] * 1
newWidth = type === "add" ? columnWidth + 5 : columnWidth - 5
}
this.setState({ adapter: adapter.updateColumnWidth(colindex, newWidth).toNewInstance() })
}
}
}
render() {
//注意此处覆盖了默认table的components内容,改用自定义组件完成输入功能
let { adapter, width, height } = this.state;
return (
<div className="edittable-warpper" style={{ width: width + "px", height: height + "px" }}>
<Table
showHeader={false}
pagination={false}
columns={adapter ? adapter.toColumns(this._onCellHandle) : []}
dataSource={adapter ? adapter.toDataSource() : undefined}
components={{
body: {
row: EdittableRowComponent,
cell: EdittableCellComponent,
}
}}>
</Table>
</div>
);
}
}
/**
* 自定义行组件:主要用于在行元素中包裹一个Form组件,便于单元格的赋值与取值
* @param {*} props
* @returns
*/
const EdittableRowComponent = (props) => {
const [form] = Form.useForm();
//这里使用了EditableContext对象,实现组件间的form参数传递
return (
<Form
form={form}
component={false}>
<EditableContext.Provider value={{ form }}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
}
/**
* 自定义单元格组件
* @param {*} 从table的 _onCellHandle函数返回的参数集合 + antd默认的其他props参数
* @returns
*/
const EdittableCellComponent = ({
onCellDataChanged,
onCellStyleChanged,
onTableStructChanged,
onStyleApplyed,
onColumnWidthClicked,
colindex,
rowindex,
cell,
editMode,
height,
nRow,
nColumn,
width,
...props }) => {
//从父容器中获得form对象,方便设置值
const { form } = useContext(EditableContext);
//如果dataSource中有值,则作为默认值显示在input中
useEffect(() => {
if (cell)
form.setFieldsValue({ [colindex]: cell.text })
}, [cell, colindex, form])
//按照设置内容定制每个单元格的样式
function generateStyle(height, nRow, cell) {
let defaultSytle = {
padding: "0px",
height: (height / nRow) + "px"
}
if (cell && cell.style) {
return { ...defaultSytle, ...cell.style }
}
else {
return defaultSytle
}
}
//按照设置内容定制指定列的款段
function process_td_DomStyle(props) {
if (cell && cell.style.columnWidth) {
props.style = { width: cell.style.columnWidth }
}
return props
}
/**
* 依据配置配置文件的data的-editMode单独指定当前单元格的可编辑性
* @param {string} editMode
* @param {object} cell
* @returns
*/
function detectDisenable(editMode, cell) {
if (editMode && editMode === "custom") {
if (cell && cell.editable && cell.editable === true) {
return false
}
return true
}
else {
return false
}
}
//实时记录表单输入的变化值
async function save(rowindex, colindex) {
try {
const values = await form.validateFields();
onCellDataChanged(rowindex, values, colindex, values[colindex])
} catch (errInfo) {
console.error('Save failed:', errInfo);
}
};
/**
* 每一个单元格都包裹一个单元格菜单,每一个单元都是一个Form.Item维护的Input
*/
return <Popover
overlayClassName="edittable-toobar-warpper"
trigger="contextMenu" //右键弹出
content={<EdittableCellContextMenu
cssStyle={cell ? cell.style : {}}
onStyleChanged={onCellStyleChanged}
onStructChanged={onTableStructChanged}
onStyleApplyed={onStyleApplyed}
onColumnWidthClicked={onColumnWidthClicked}
rowindex={rowindex}
colindex={colindex}
nrow={nRow}
ncolumn={nColumn}
></EdittableCellContextMenu>}>
<td {...process_td_DomStyle(props)}>
<Form.Item
name={colindex}>
<Input
placeholder="请输出"
disabled={detectDisenable(editMode, cell)}
bordered={false} //移除input自身border样式,改为style配置修改
id={"" + rowindex + "-" + colindex}
style={generateStyle(height, nRow, cell)}
onPressEnter={() => save(rowindex, colindex)}
onBlur={() => save(rowindex, colindex)}
/>
</Form.Item></td>
</Popover>;
}
export default EdittableComponent;