Table.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import React, { useState, useMemo, useCallback } from 'react'
  21. import { Table, Popconfirm, Button } from 'antd'
  22. import { TableComponents } from 'antd/lib/table'
  23. import { FormComponentProps } from 'antd/lib/form'
  24. import Form, { WrappedFormUtils } from 'antd/lib/form/Form'
  25. import EditableCell from './Cell'
  26. import { IEditableColumnProps } from './types'
  27. import { EditableContext } from './util'
  28. interface IEditableTableProps<T> extends FormComponentProps {
  29. data: T[]
  30. dataKey: string
  31. columns: Array<IEditableColumnProps<T>>
  32. showConfirm?: boolean
  33. onSave: (newData: T[]) => void
  34. }
  35. const tableComponents: TableComponents = {
  36. body: {
  37. cell: EditableCell
  38. }
  39. }
  40. export const EditableTable: <T extends object>(
  41. props: IEditableTableProps<T>
  42. ) => React.ReactElement = (props) => {
  43. const { columns, data, dataKey, form, showConfirm, onSave } = props
  44. const [editingIdx, setEditingIdx] = useState(-1)
  45. const isEditing = useCallback((idx: number) => editingIdx === idx, [
  46. editingIdx
  47. ])
  48. const save = useCallback(
  49. (form: WrappedFormUtils, idx: number) => {
  50. form.validateFields((err, row) => {
  51. if (err) {
  52. return
  53. }
  54. const newData = [...data]
  55. const existsIdx = newData.findIndex(
  56. (record, recordIdx) =>
  57. record[dataKey] === row[dataKey] && recordIdx !== idx
  58. )
  59. newData.splice(idx, 1, { ...newData[idx], ...row })
  60. if (existsIdx > -1) {
  61. newData.splice(existsIdx, 1)
  62. }
  63. onSave(newData)
  64. setEditingIdx(-1)
  65. })
  66. },
  67. [data, editingIdx, onSave]
  68. )
  69. const cancel = useCallback(() => {
  70. setEditingIdx(-1)
  71. }, [])
  72. const deleteRecord = useCallback(
  73. (idx: number) => {
  74. const newData = [...data]
  75. newData.splice(idx, 1)
  76. onSave(newData)
  77. },
  78. [data, onSave]
  79. )
  80. const tableColumns = columns
  81. .map((col, columnIdx) => {
  82. if (!col.editable) {
  83. return col
  84. }
  85. return {
  86. ...col,
  87. onCell: (record: object, rowIdx: number) => ({
  88. record,
  89. inputType: col.inputType,
  90. dataIndex: col.dataIndex,
  91. title: col.title,
  92. editing: isEditing(rowIdx),
  93. autoFocus: columnIdx === 0
  94. })
  95. }
  96. })
  97. .concat({
  98. title: '操作',
  99. dataIndex: 'operation',
  100. align: 'center',
  101. width: 130,
  102. editable: false,
  103. inputType: 'none',
  104. render: (_1, _2, idx) =>
  105. isEditing(idx) ? (
  106. <>
  107. <EditableContext.Consumer>
  108. {(form) => (
  109. <Button
  110. type="primary"
  111. size="small"
  112. style={{ marginRight: 8 }}
  113. onClick={() => save(form, idx)}
  114. >
  115. 保存
  116. </Button>
  117. )}
  118. </EditableContext.Consumer>
  119. {showConfirm ? (
  120. <Popconfirm title="确定取消?" onConfirm={cancel}>
  121. <Button size="small">取消</Button>
  122. </Popconfirm>
  123. ) : (
  124. <Button size="small" onClick={cancel}>
  125. 取消
  126. </Button>
  127. )}
  128. </>
  129. ) : (
  130. <>
  131. <Button
  132. type="primary"
  133. size="small"
  134. disabled={editingIdx !== -1 && !isEditing(idx)}
  135. style={{ marginRight: 8 }}
  136. onClick={() => setEditingIdx(idx)}
  137. >
  138. 编辑
  139. </Button>
  140. {showConfirm ? (
  141. <Popconfirm
  142. title="确定删除?"
  143. onConfirm={() => deleteRecord(idx)}
  144. >
  145. <Button type="danger" size="small">
  146. 删除
  147. </Button>
  148. </Popconfirm>
  149. ) : (
  150. <Button
  151. type="danger"
  152. size="small"
  153. onClick={() => deleteRecord(idx)}
  154. >
  155. 删除
  156. </Button>
  157. )}
  158. </>
  159. )
  160. })
  161. return (
  162. <EditableContext.Provider value={form}>
  163. <Table
  164. bordered
  165. components={tableComponents}
  166. dataSource={data}
  167. pagination={false}
  168. columns={tableColumns}
  169. />
  170. </EditableContext.Provider>
  171. )
  172. }
  173. // @FIXME typescript generic typing (object to T)
  174. export default Form.create<IEditableTableProps<object>>()(EditableTable)