折腾:
【未解决】AntD Pro中支持剧本剧本编写时拖动排序单个对话
期间,已实现可以拖动的表格了:

但是此Antd Pro的Reactjs中,是基于react-dnd实现的可拖动实现排序的Table的cell,是不可编辑的
此处希望可以实时编辑。
记得之前Antd Pro中Table的cell,是支持实时编辑的
需要想办法加进来这个功能,且同时注意不要和现有的react-dnd冲突了。
如果实在不行,再去考虑是否可以做成:
通过额外按钮实现:
可编辑的Table的cell
和
只读的但是可以拖动排序的table的cell
之前去切换,也算可以实现想要的效果。
先去试试,参考:

集成进来看看效果
不过刚发现,好像可编辑行,不支持当内容超过宽度就自动换行:

而可编辑单元格,是支持的:

所以还是换成可编辑单元格试试
但是看起来两种row:
const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
sourceClientOffset: monitor.getSourceClientOffset(),
}))(
DragSource('row', rowSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
dragRow: monitor.getItem(),
clientOffset: monitor.getClientOffset(),
initialClientOffset: monitor.getInitialClientOffset(),
}))(BodyRow)
);和:
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);貌似很难合并到一起啊
看来要单独切换: 可拖动状态 和 可编辑状态
不过要先去合并进来 可以编辑的cell
【已解决】Reactjs的Antd Pro中实现表格中支持可编辑的单元格
然后再去想办法,实现在可编辑和可拖动模式之前切换
【总结】
最终,用如下代码实现了 编辑模式 和 拖拽模式 之间切换:
/src/routes/Script/ScriptCreate.js
import React, { PureComponent } from 'react'
import { connect } from 'dva'
import { routerRedux } from 'dva/router'
import {
Col,
Form,
Input,
Button,
Card,
InputNumber,
Select,
message,
Modal,
Checkbox,
} from 'antd'
import PageHeaderLayout from '../../layouts/PageHeaderLayout'
import MongodbFileList from './MongodbFileList'
import DragableTable from '../../components/DragableTable'
import EditableTable from '../../components/EditableTable'
const FormItem = Form.Item;
const InputGroup = Input.Group;
const { TextArea } = Input;
const { Option } = Select;
@connect(({ loading, script, topic }) => ({
submitting: loading.effects['script/submitRegularForm'],
script,
topic,
}))
@Form.create()
export default class ScriptCreate extends PureComponent {
constructor(props) {
super(props)
this.onDialogListChange = this.onDialogListChange.bind(this)
this.onEditableCellChange = this.onEditableCellChange.bind(this)
this.onDragableCheckChange = this.onDragableCheckChange.bind(this)
this.state = {
// dialogList: [],
dialogList: this.demoItemList,
dragableChecked: false,
speakers: [],
contents: [],
audioIDs:[],
modalVisible: false,
first_level_topic: [],
isSubmitting: false,
isDisableSubmit: false,
}
}
/* eslint-disable */
columns = [
{
title: '序号',
width: "8%",
editable: false,
dataIndex: 'number',
key: 'number',
// rowKey: 'number',
// fixed: 'left',
render(text, record, index) {
return index + 1;
},
},
{
width: "15%",
editable: true,
title: 'Speaker/Song',
dataIndex: 'speakerOrSong',
key: 'speakerOrSong',
},
{
width: "75%",
editable: true,
title: 'Content/Name',
dataIndex: 'contentOrName',
key: 'contentOrName',
},
]
demoItemList = [
{
key: '1',
speakerOrSong: 'A',
contentOrName: 'hi boy',
editable: true,
},
{
key: '2',
speakerOrSong: 'B',
contentOrName: 'hello',
editable: true,
},
{
key: '3',
speakerOrSong: 'A',
contentOrName: 'what are you doing?',
editable: true,
},
{
key: '4',
speakerOrSong: 'B',
contentOrName: 'I am singing',
editable: true,
},
{
key: '5',
speakerOrSong: 'Song',
contentOrName: 'this is a apple.mp3',
editable: false,
},
]
componentDidMount() {
...
}
...
handleAddDialog(){
console.log("handleAddDialog")
const curDialogList = this.state.dialogList
console.log("curDialogList=", curDialogList)
let newDialogList = curDialogList
const curDialogNum = curDialogList.length
const newDialogNum = curDialogNum + 1
let newDialog = {
key: `${newDialogNum}`,
speakerOrSong: `speaker ${newDialogNum}`,
contentOrName: `content ${newDialogNum}`,
}
console.log("newDialog=", newDialog)
newDialogList.push(newDialog)
console.log("newDialogList=", newDialogList)
this.setState({ dialogList: newDialogList})
console.log("this.state.dialogList=", this.state.dialogList)
// Note: due to use redux manage state, so componentWillReceiveProps not work
// so here need forceUpdate to update ui
this.forceUpdate()
}
onDialogListChange(newDialogList){
console.log("onDialogListChange: newDialogList=", newDialogList)
console.log("before change: this.state.dialogList=", this.state.dialogList)
this.setState({dialogList: newDialogList})
console.log("after change: this.state.dialogList=", this.state.dialogList)
}
onEditableCellChange(newDialogList){
console.log("onEditableCellChange: newDialogList=", newDialogList)
console.log("before change: this.state.dialogList=", this.state.dialogList)
this.setState({dialogList: newDialogList})
console.log("after change: this.state.dialogList=", this.state.dialogList)
}
...
onDragableCheckChange(e) {
console.log('onDragableCheckChange: e.target.checked=', e.target.checked);
this.setState({
dragableChecked: e.target.checked,
})
}
render() {
const { submitting, form } = this.props;
const topicList = this.props.topic.topics;
const { getFieldDecorator } = this.props.form;
const firstLevelOptions = Object.keys(topicList).map(first => <Option key={first}>{first}</Option>);
const secondLevelOptions = this.state.first_level_topic.map(second => <Option key={second}>{second}</Option>);
...
return (
<PageHeaderLayout
title="新建剧本"
>
<Card bordered={false}>
<Form style={{ marginTop: 8 }} hideRequiredMark>
...
</FormItem>
{/* {this.buildDialog()} */}
{
this.state.dragableChecked ? (
<DragableTable
columns={this.columns}
itemList={this.state.dialogList}
onDragableListChange={this.onDialogListChange}
/>
) : (
<EditableTable
columns={this.columns}
itemList={this.state.dialogList}
onEditableCellChange={this.onEditableCellChange}
form={this.props.form}
/>
)
}
<FormItem {...submitFormLayout} style={{ marginTop: 32 }}>
<Checkbox
style={{ marginLeft: "1%" }}
checked={this.state.dragableChecked}
onChange={this.onDragableCheckChange}
>
开启拖拽模式
</Checkbox>
...
</FormItem>
</Form>
</Card>
</PageHeaderLayout>
);
}
}可编辑:
/src/components/EditableTable/index.js
import React from 'react'
import { EditableFormRow, EditableCell } from '../EditableCell'
import { Table } from 'antd'
export default class EditableTable extends
React.Component
{
constructor(props){
super(props)
console.log("EditableTable constructor: props=", props)
console.log("this.props.columns=", this.props.columns)
console.log("this.props.itemList=", this.props.itemList)
console.log("this.props.onEditableCellChange=", this.props.onEditableCellChange)
this.handleSaveEditableCell = this.handleSaveEditableCell.bind(this)
this.state.itemList = this.props.itemList;
this.state.columns = this.props.columns;
}
state = {
columns: [],
itemList: [],
}
// componentWillReceiveProps(nextProps){
// console.log("EditableTable componentWillReceiveProps: nextProps=", nextProps)
// console.log("this.props.itemList=", this.props.itemList)
// if (this.props.itemList) {
// this.setState({itemList: this.props.itemList})
// console.log("updated this.state.itemList=", this.state.itemList)
// }
// }
components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
}
handleSaveEditableCell = (row) => {
console.log("handleSaveEditableCell: row=", row)
const newItemList = [...this.state.itemList]
console.log("newItemList=", newItemList)
const index = newItemList.findIndex(item => row.key === item.key)
console.log("index=", index)
const item = newItemList[index]
console.log("item=", item)
newItemList.splice(index, 1, {
...item,
...row,
})
console.log("newItemList=", newItemList)
this.setState({ itemList: newItemList })
console.log("after: this.state.itemList=", this.state.itemList)
this.props.onEditableCellChange(this.state.itemList)
// console.log("newItemList=", newItemList)
// this.props.onEditableCellChange(newItemList)
}
render() {
console.log("EditableTable render: this.state.itemList=", this.state.itemList)
const curColumns = this.state.columns.map((col) => {
console.log("curColumns: col=", col)
if (!col.editable) {
return col
}
return {
...col,
onCell: (record) => {
/*
* Note:
* 1. each cell is editable is set by:
* first priority: cell editable by each item/cell in this.props.itemList
* then check: column editable by each column in this.props.columns
*/
let cellIsEditable
if (record.editable !== undefined) {
cellIsEditable = record.editable
} else {
cellIsEditable = col.editable
}
console.log("cellIsEditable=", cellIsEditable, ", record=", record)
return {
record,
editable: cellIsEditable,
dataIndex: col.dataIndex,
title: col.title,
form: this.props.form,
handleSave: this.handleSaveEditableCell,
}
},
}
})
return (
<Table
bordered
columns={curColumns}
dataSource={this.state.itemList}
components={
this.components
}
onRow={(record, index) => ({
index,
})}
/>
);
}
}
EditableTable.defaultProps = {
itemList: [],
onEditableCellChange: (newItemList) => {},
};/src/components/EditableCell/index.js
import React, { PureComponent } from 'react';
import styles from './index.less';
import { Form, Input } from 'antd';
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
export const EditableFormRow = Form.create()(EditableRow);
// export default class EditableCell extends PureComponent {
export class EditableCell extends PureComponent {
state = {
editing: false,
}
componentDidMount() {
console.log("EditableCell componentDidMount: this.props.editable", this.props.editable)
if (this.props.editable) {
document.addEventListener('click', this.handleClickOutside, true);
}
}
componentWillUnmount() {
if (this.props.editable) {
document.removeEventListener('click', this.handleClickOutside, true);
}
}
toggleEdit = () => {
console.log("toggleEdit=")
console.log("this.state.editing=", this.state.editing)
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
}
handleClickOutside = (e) => {
const { editing } = this.state;
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
this.save();
}
}
save = () => {
console.log("save")
const { record, handleSave } = this.props;
console.log("record=", record)
this.form.validateFields((error, values) => {
console.log("form.validateFields=: error", error, ", values=", values)
if (error) {
return
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
}
render() {
const { editing } = this.state;
const {
editable,
dataIndex,
title,
record,
index,
// handleSave,
...restProps
} = this.props;
console.log("editing=", editing, ", editable=", editable, ", record=", record)
return (
<td ref={node => (this.cell = node)} {...restProps}>
{editable ? (
<EditableContext.Consumer>
{(form) => {
console.log("EditableContext.Consumer: form=", form)
this.form = form;
return (
editing ? (
<FormItem style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `${title} is required.`,
}],
initialValue: record[dataIndex],
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
/>
)}
</FormItem>
) : (
<div
className={styles.editableCellValueWrap}
// className={styles.nonEditableCellValueWrap}
style={{ paddingRight: 5 }}
onClick={this.toggleEdit}
>
{restProps.children}
</div>
)
);
}}
</EditableContext.Consumer>
) : restProps.children}
</td>
);
}
}/src/components/EditableCell/index.less
@import '~antd/lib/style/themes/default.less';
.editable-cell {
position: relative;
}
// .nonEditableCellValueWrap {
// padding: 5px 12px;
// cursor: pointer;
// background: grey;
// }
.editableCellValueWrap {
padding: 5px 12px;
// padding: 2px 4px;
cursor: pointer;
}
// .editable-row:hover .editableCellValueWrap {
// .editableRow:hover .editableCellValueWrap {
.editableCellValueWrap:hover {
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 4px 11px;
// padding: 2px 4px;
}可拖动:
/src/components/DragableTable/index.js
import React from 'react'
import { DragDropContext } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import update from 'immutability-helper'
import DragableBodyRow from '../DragableBodyRow'
import { Table } from 'antd'
@DragDropContext(HTML5Backend)
export default class DragableTable extends
React.Component
{
constructor(props){
super(props)
console.log("DragableTable constructor: props=", props)
console.log("this.props.columns=", this.props.columns)
console.log("this.props.itemList=", this.props.itemList)
console.log("this.props.onDragableListChange=", this.props.onDragableListChange)
this.state.itemList = this.props.itemList;
this.state.columns = this.props.columns;
}
state = {
columns: [],
itemList: [],
}
// componentWillReceiveProps(nextProps){
// console.log("DragableTable componentWillReceiveProps: nextProps=", nextProps)
// console.log("this.props.itemList=", this.props.itemList)
// if (this.props.itemList) {
// this.setState({itemList: this.props.itemList})
// console.log("updated this.state.itemList=", this.state.itemList)
// }
// }
components = {
body: {
row: DragableBodyRow,
},
}
moveRow = (dragIndex, hoverIndex) => {
const { itemList } = this.state;
const dragRow = itemList[dragIndex];
this.setState(
update(this.state, {
itemList: {
$splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]],
},
}),
)
console.log("moveRow: this.state.itemList=", this.state.itemList)
this.props.onDragableListChange(this.state.itemList)
}
render() {
console.log("DragableTable render: this.state.itemList=", this.state.itemList)
return (
<Table
bordered
columns={this.state.columns}
dataSource={this.state.itemList}
components={
this.components
}
onRow={(record, index) => ({
index,
moveRow: this.moveRow,
})}
/>
);
}
}
DragableTable.defaultProps = {
itemList: [],
onDragableListChange: (dragSortedList) => {},
};/src/components/DragableBodyRow/index.js
import React from 'react';
import { DragSource, DropTarget } from 'react-dnd';
function dragDirection(
dragIndex,
hoverIndex,
initialClientOffset,
clientOffset,
sourceClientOffset,
) {
const hoverMiddleY = (initialClientOffset.y - sourceClientOffset.y) / 2;
const hoverClientY = clientOffset.y - sourceClientOffset.y;
if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) {
return 'downward';
}
if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) {
return 'upward';
}
}
class BodyRow extends
React.Component
{
render() {
const {
isOver,
connectDragSource,
connectDropTarget,
moveRow,
dragRow,
clientOffset,
sourceClientOffset,
initialClientOffset,
...restProps
} = this.props;
const style = { ...restProps.style, cursor: 'move' };
let className = restProps.className;
if (isOver && initialClientOffset) {
const direction = dragDirection(
dragRow.index,
restProps.index,
initialClientOffset,
clientOffset,
sourceClientOffset
);
if (direction === 'downward') {
className += ' drop-over-downward';
}
if (direction === 'upward') {
className += ' drop-over-upward';
}
}
return connectDragSource(
connectDropTarget(
<tr
{...restProps}
className={className}
style={style}
/>
)
);
}
}
const rowSource = {
beginDrag(props) {
return {
index: props.index,
};
},
};
const rowTarget = {
drop(props, monitor) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Time to actually perform the action
props.moveRow(dragIndex, hoverIndex);
// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
monitor.getItem().index = hoverIndex;
},
};
// const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
sourceClientOffset: monitor.getSourceClientOffset(),
}))(
DragSource('row', rowSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
dragRow: monitor.getItem(),
clientOffset: monitor.getClientOffset(),
initialClientOffset: monitor.getInitialClientOffset(),
}))(BodyRow)
);
export default DragableBodyRow效果:



去拖动:


