| | |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { DndProvider, DragSource, DropTarget } from 'react-dnd' |
| | | import { Table, Input, InputNumber, Popconfirm, Form, Icon, Select, Radio, Cascader, notification } from 'antd' |
| | | import { Table, Input, InputNumber, Popconfirm, Switch, Form, Select, Radio, Cascader, notification, message, Modal, Typography } from 'antd' |
| | | import { CopyOutlined, EditOutlined, DeleteOutlined, SwapOutlined, PlusOutlined, ConsoleSqlOutlined } from '@ant-design/icons' |
| | | |
| | | import Utils from '@/utils/utils.js' |
| | | import ColorSketch from '@/mob/colorsketch' |
| | | import CusSwitch from './cusSwitch' |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import asyncComponent from '@/utils/asyncComponent' |
| | | import MKEmitter from '@/utils/events.js' |
| | | import './index.scss' |
| | | |
| | | let eTDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS |
| | | const MkEditIcon = asyncComponent(() => import('@/components/mkIcon')) |
| | | const PasteBoard = asyncComponent(() => import('@/components/pasteboard')) |
| | | const EditableContext = React.createContext() |
| | | const { confirm } = Modal |
| | | let dragingIndex = -1 |
| | | const { Paragraph } = Typography |
| | | |
| | | class CusSwitch extends Component { |
| | | static propTpyes = { |
| | | defaultValue: PropTypes.any, |
| | | value: PropTypes.any, |
| | | onChange: PropTypes.func |
| | | } |
| | | state = { |
| | | status: true |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { defaultValue, value } = this.props |
| | | let initVal = 'true' |
| | | |
| | | if (this.props['data-__meta']) { |
| | | initVal = this.props['data-__meta'].initialValue |
| | | } else if (defaultValue) { |
| | | initVal = defaultValue |
| | | } else if (value) { |
| | | initVal = value |
| | | } |
| | | |
| | | if (initVal === 'false') { |
| | | initVal = false |
| | | } else { |
| | | initVal = true |
| | | } |
| | | |
| | | this.setState({status: initVal}) |
| | | } |
| | | |
| | | changeStatus = (val) => { |
| | | this.setState({ status: val }, () => { |
| | | let _val = val ? 'true' : 'false' |
| | | this.props.onChange && this.props.onChange(_val) |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { status } = this.state |
| | | return ( |
| | | <Switch checkedChildren="是" unCheckedChildren="否" checked={status} onChange={this.changeStatus} /> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | class BodyRow extends React.Component { |
| | | render() { |
| | | const { isOver, connectDragSource, connectDropTarget, moveRow, ...restProps } = this.props |
| | | const style = { ...restProps.style, cursor: 'move' } |
| | | const { isOver, moveAble, connectDragSource, connectDropTarget, moveRow, ...restProps } = this.props |
| | | |
| | | let { className } = restProps |
| | | if (isOver) { |
| | | if (isOver && moveAble) { |
| | | if (restProps.index > dragingIndex) { |
| | | className += ' drop-over-downward' |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | return connectDragSource( |
| | | connectDropTarget(<tr {...restProps} className={className} style={style} />), |
| | | ) |
| | | if (moveAble) { |
| | | return connectDragSource( |
| | | connectDropTarget(<tr {...restProps} className={className} style={{ ...restProps.style, cursor: 'move' }} />), |
| | | ) |
| | | } else { |
| | | return (<tr {...restProps} className={className} style={restProps.style} />) |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | class EditableCell extends Component { |
| | | getInput = (form) => { |
| | | const { inputType, options, min, max, unlimit } = this.props |
| | | const { inputType, options, min, max, unlimit, allowClear, typeChange } = this.props |
| | | |
| | | if (inputType === 'number' && unlimit) { |
| | | return <InputNumber onPressEnter={() => this.getValue(form)} /> |
| | | } else if (inputType === 'number') { |
| | | return <InputNumber min={min} max={max} precision={0} onPressEnter={() => this.getValue(form)} /> |
| | | } else if (inputType === 'color') { |
| | | return <ColorSketch /> |
| | | return <ColorSketch allowClear={allowClear} /> |
| | | } else if (inputType === 'icon') { |
| | | return <MkEditIcon allowClear/> |
| | | } else if (inputType === 'switch') { |
| | | return <CusSwitch /> |
| | | } else if (inputType === 'select') { |
| | | return ( |
| | | <Select> |
| | | <Select onChange={typeChange}> |
| | | {options.map((item, i) => (<Select.Option key={i} value={item.field || item.value}> {item.label || item.text} </Select.Option>))} |
| | | </Select> |
| | | ) |
| | |
| | | ) |
| | | } else if (inputType === 'radio') { |
| | | return ( |
| | | <Radio.Group> |
| | | <Radio.Group style={{whiteSpace: 'nowrap'}}> |
| | | {options.map((item, i) => (<Radio key={i} value={item.field || item.value}> {item.label || item.text} </Radio>))} |
| | | </Radio.Group> |
| | | ) |
| | | } else if (inputType === 'textarea') { |
| | | return <Input.TextArea autoSize={true} placeholder=""/> |
| | | } else { |
| | | return <Input onPressEnter={() => this.getValue(form)} /> |
| | | return <Input onPressEnter={() => this.getValue(form)}/> |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | renderCell = (form) => { |
| | | const { getFieldDecorator } = form |
| | | const { editing, dataIndex, title, record, children, className, required, inputType } = this.props |
| | | const { editing, pass, dataIndex, title, record, children, className, required, inputType, rules } = this.props |
| | | |
| | | if (!editing) { |
| | | return ( |
| | | <td className={className}> |
| | | {children} |
| | | </td> |
| | | ) |
| | | } |
| | | return ( |
| | | <td className={className}> |
| | | {editing ? ( |
| | | {pass ? ( |
| | | <Form.Item style={{ margin: 0 }}> |
| | | {getFieldDecorator(dataIndex, { |
| | | rules: [ |
| | | { |
| | | required: required, |
| | | message: ['number', 'text', 'input'].includes(inputType) ? `${eTDict['form.required.input']} ${title}!` : `${eTDict['form.required.select']} ${title}!`, |
| | | } |
| | | message: ['number', 'text', 'input'].includes(inputType) ? `请输入${title}!` : `请选择${title}!`, |
| | | }, |
| | | ...rules |
| | | ], |
| | | initialValue: inputType === 'multiStr' ? (record[dataIndex] ? record[dataIndex].split(',') : []) : record[dataIndex], |
| | | })(this.getInput(form))} |
| | | </Form.Item> |
| | | ) : ( |
| | | children |
| | | null |
| | | )} |
| | | </td> |
| | | ) |
| | |
| | | class EditTable extends Component { |
| | | static propTpyes = { |
| | | actions: PropTypes.any, // 操作项 |
| | | searchKey: PropTypes.any, // 搜索条件 |
| | | data: PropTypes.any, // 数据列表 |
| | | columns: PropTypes.array, // 显示列 |
| | | onChange: PropTypes.func // 数据变化 |
| | |
| | | state = { |
| | | data: [], |
| | | editingKey: '', |
| | | columns: [] |
| | | editLineId: '', |
| | | columns: [], |
| | | keyCol: null, |
| | | keyVal: '' |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { data, actions } = this.props |
| | | let columns = fromJS(this.props.columns).toJS() |
| | | let operation = null |
| | | let extra = null |
| | | let keyCol = null |
| | | |
| | | if (actions) { |
| | | actions.forEach(item => { |
| | | if (/^extra/.test(item)) { |
| | | extra = item.split(':') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | if (!actions || actions.length > 0) { |
| | | columns.push({ |
| | | title: eTDict['model.operation'], |
| | | if (actions && (actions.includes('edit') || actions.includes('copy') || actions.includes('del'))) { |
| | | let _operation = null |
| | | let render = null |
| | | columns = columns.filter(item => { |
| | | if (item.dataIndex === 'operation') { |
| | | _operation = item |
| | | } |
| | | if (item.keyCol) { |
| | | keyCol = item.dataIndex |
| | | } |
| | | if (item.dataIndex === 'sqlRender') { |
| | | render = item.render |
| | | return false |
| | | } |
| | | return item.dataIndex !== 'operation' |
| | | }) |
| | | |
| | | operation = { |
| | | title: (<div> |
| | | 操作 |
| | | <span className="copy-control"> |
| | | {actions.includes('copy') ? <CopyOutlined title="复制" onClick={() => this.copy()} /> : null} |
| | | {actions.includes('copy') ? <PasteBoard getPasteValue={this.pasteSubmit}/> : null} |
| | | {actions.includes('clear') ? <DeleteOutlined title="清空" onClick={this.clear} /> : null} |
| | | </span> |
| | | </div>), |
| | | dataIndex: 'operation', |
| | | width: '140px', |
| | | render: (text, record) => { |
| | | const { editingKey } = this.state |
| | | const editable = this.isEditing(record) |
| | | return editable ? ( |
| | | <span style={{textAlign: 'center', display: 'block'}}> |
| | | <div style={{textAlign: 'center', minWidth: '110px'}}> |
| | | <EditableContext.Consumer> |
| | | {form => ( |
| | | <span onClick={() => this.save(form, record.uuid)} style={{ marginRight: 8 , color: '#1890ff', cursor: 'pointer'}}> |
| | | {eTDict['model.save']} |
| | | 保存 |
| | | </span> |
| | | )} |
| | | </EditableContext.Consumer> |
| | | <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>{eTDict['model.cancel']}</span> |
| | | </span> |
| | | <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>取消</span> |
| | | </div> |
| | | ) : ( |
| | | <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')}> |
| | | {!actions || actions.includes('edit') ? <span className="primary" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><Icon type="edit" /></span> : null} |
| | | {(!actions || actions.includes('del')) && editingKey === '' ? <Popconfirm |
| | | <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')} style={{minWidth: '110px', whiteSpace: 'nowrap'}}> |
| | | {actions.includes('edit') ? <span className="primary" title="编辑" onClick={() => {editingKey === '' && this.edit(record)}}><EditOutlined /></span> : null} |
| | | {extra ? <span className="status" title={extra[2]} onClick={() => {editingKey === '' && this.handleStatus(record, extra[1])}}><SwapOutlined /></span> : null} |
| | | {actions.includes('status') ? <span className="status" title="是否启用" onClick={() => {editingKey === '' && this.handleStatus(record, 'status')}}><SwapOutlined /></span> : null} |
| | | {actions.includes('copy') ? <span className="copy" title="复制" onClick={() => {editingKey === '' && this.copy(record)}}><CopyOutlined /></span> : null} |
| | | {actions.includes('del') && editingKey === '' ? <Popconfirm |
| | | overlayClassName="popover-confirm" |
| | | title={eTDict['model.query.delete']} |
| | | title="确定删除吗?" |
| | | onConfirm={() => this.handleDelete(record.uuid) |
| | | }> |
| | | <span className="danger"><Icon type="delete" /></span> |
| | | }> |
| | | <span className="danger"><DeleteOutlined /></span> |
| | | </Popconfirm> : null} |
| | | {(!actions || actions.includes('del')) && editingKey !== '' ? <span className="danger"><Icon type="delete" /></span> : null} |
| | | {actions.includes('del') && editingKey !== '' ? <span className="danger"><DeleteOutlined /></span> : null} |
| | | {actions.includes('sql') ? <span className="primary" title="SQL" onClick={() => {editingKey === '' && this.showSql(record, render)}}><ConsoleSqlOutlined /></span> : null} |
| | | </div> |
| | | ) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | if (_operation) { |
| | | operation.render = _operation.render |
| | | operation.width = _operation.width |
| | | } |
| | | columns.push(operation) |
| | | } |
| | | |
| | | this.setState({ |
| | | data: data || [], |
| | | oricolumns: fromJS(this.props.columns).toJS(), |
| | | columns |
| | | operation, |
| | | columns, |
| | | keyCol |
| | | }) |
| | | } |
| | | |
| | | UNSAFE_componentWillReceiveProps (nextProps) { |
| | | |
| | | if (!is(fromJS(this.state.data), fromJS(nextProps.data))) { |
| | | this.setState({data: nextProps.data, editingKey: ''}) |
| | | } else if (!is(fromJS(this.state.oricolumns), fromJS(nextProps.columns))) { |
| | | let cols = {} |
| | | nextProps.columns.forEach(col => {cols[col.dataIndex] = col}) |
| | | |
| | | this.setState({ |
| | | oricolumns: fromJS(nextProps.columns).toJS(), |
| | | columns: this.state.columns.map(col => { |
| | | if (cols[col.dataIndex]) { |
| | | return cols[col.dataIndex] |
| | | } |
| | | return col |
| | | } else if (!is(fromJS(this.props.columns), fromJS(nextProps.columns))) { |
| | | if (nextProps.columns.length === this.props.columns.length) { |
| | | let cols = {} |
| | | nextProps.columns.forEach(col => {cols[col.dataIndex] = col}) |
| | | |
| | | this.setState({ |
| | | columns: this.state.columns.map(col => { |
| | | if (cols[col.dataIndex]) { |
| | | return cols[col.dataIndex] |
| | | } |
| | | return col |
| | | }) |
| | | }) |
| | | } else { |
| | | let columns = fromJS(nextProps.columns).toJS() |
| | | if (this.state.operation) { |
| | | columns.push(this.state.operation) |
| | | } |
| | | this.setState({columns}) |
| | | } |
| | | } |
| | | } |
| | | |
| | | componentDidMount () { |
| | | MKEmitter.addListener('editLineId', this.getEditLineId) |
| | | } |
| | | |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | MKEmitter.removeListener('editLineId', this.getEditLineId) |
| | | } |
| | | |
| | | showSql = (record, render) => { |
| | | let list = render(record) |
| | | |
| | | if (list) { |
| | | Modal.info({ |
| | | title: '', |
| | | width: 700, |
| | | className: 'sql-example', |
| | | icon: null, |
| | | content: list.map((n, index) => <div key={index} dangerouslySetInnerHTML={{ __html: n }}></div>) |
| | | }) |
| | | } |
| | | } |
| | | |
| | | getEditLineId = (id) => { |
| | | this.setState({ editLineId: id }) |
| | | } |
| | | |
| | | isEditing = record => record.uuid === this.state.editingKey |
| | | |
| | | cancel = () => { |
| | | this.setState({ editingKey: '' }) |
| | | } |
| | | |
| | | clear = () => { |
| | | const _this = this |
| | | |
| | | confirm({ |
| | | title: '确定清空列表吗?', |
| | | content: '', |
| | | onOk() { |
| | | _this.setState({ data: [], editingKey: '' }, () => { |
| | | _this.props.onChange([]) |
| | | }) |
| | | }, |
| | | onCancel() {} |
| | | }) |
| | | } |
| | | |
| | | copy = (item) => { |
| | | const { type } = this.props |
| | | const { data } = this.state |
| | | |
| | | if (!data || data.length === 0) { |
| | | message.warning('未获取到配置信息') |
| | | return |
| | | } |
| | | |
| | | let msg = { key: type } |
| | | |
| | | if (item) { |
| | | msg.type = 'line' |
| | | msg.data = item |
| | | } else { |
| | | msg.type = 'array' |
| | | msg.data = data |
| | | } |
| | | |
| | | try { |
| | | let srcid = localStorage.getItem(window.location.href.split('#')[0] + 'srcId') |
| | | if (srcid) { |
| | | msg.$srcId = srcid |
| | | } |
| | | |
| | | msg = window.btoa(window.encodeURIComponent(JSON.stringify(msg))) |
| | | } catch (e) { |
| | | console.warn('Stringify Failure') |
| | | msg = '' |
| | | } |
| | | |
| | | if (msg) { |
| | | let oInput = document.createElement('input') |
| | | oInput.value = msg |
| | | document.body.appendChild(oInput) |
| | | oInput.select() |
| | | document.execCommand('Copy') |
| | | document.body.removeChild(oInput) |
| | | message.success('复制成功。') |
| | | } |
| | | } |
| | | |
| | | pasteSubmit = (res, callback) => { |
| | | const { type } = this.props |
| | | const { columns } = this.state |
| | | let data = fromJS(this.state.data).toJS() |
| | | |
| | | if (res.copyType === 'columns' && type === 'datasourcefield') { |
| | | res.type = 'array' |
| | | res.data = [] |
| | | res.columns.forEach(col => { |
| | | if (!col.field) return |
| | | if (col.type === 'number') { |
| | | let datatype = 'Int' |
| | | if (col.decimal) { |
| | | datatype = `Decimal(18,${col.decimal})` |
| | | } |
| | | |
| | | res.data.push({ |
| | | $index: res.data.length + 1, |
| | | datatype: datatype, |
| | | field: col.field, |
| | | decimal: col.decimal, |
| | | label: col.label, |
| | | type: 'number', |
| | | uuid: Utils.getuuid() |
| | | }) |
| | | } else { |
| | | let datatype = 'Nvarchar(50)' |
| | | let fieldlength = 50 |
| | | if (col.fieldlength && [10, 20, 50, 100, 256, 512, 1024, 2048, 4000].includes(col.fieldlength)) { |
| | | fieldlength = col.fieldlength |
| | | datatype = `Nvarchar(${fieldlength})` |
| | | } |
| | | |
| | | res.data.push({ |
| | | $index: res.data.length + 1, |
| | | datatype: datatype, |
| | | field: col.field, |
| | | fieldlength: fieldlength, |
| | | label: col.label, |
| | | type: 'text', |
| | | uuid: Utils.getuuid() |
| | | }) |
| | | } |
| | | }) |
| | | } else if (res.key !== type) { |
| | | message.warning('配置信息格式错误!') |
| | | return |
| | | } |
| | | |
| | | if (res.type === 'line') { |
| | | let unique = true |
| | | res.data.uuid = Utils.getuuid() |
| | | columns.forEach(col => { |
| | | if (col.unique !== true || !unique) return |
| | | |
| | | if (col.uniqueFunc) { |
| | | unique = col.uniqueFunc(data, res.data) |
| | | } else if (col.strict) { |
| | | let key = res.data[col.dataIndex].toLowerCase() |
| | | let _index = data.findIndex(item => key === item[col.dataIndex].toLowerCase()) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | } else { |
| | | let _index = data.findIndex(item => res.data[col.dataIndex] === item[col.dataIndex]) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | } |
| | | }) |
| | | |
| | | if (!unique) return |
| | | |
| | | data.unshift(res.data) |
| | | this.setState({ data, editingKey: '', editLineId: res.data.uuid || '' }, () => { |
| | | this.props.onChange(data) |
| | | }) |
| | | } else if (res.type === 'array') { |
| | | res.data.forEach(cell => { |
| | | let unique = true |
| | | cell.uuid = Utils.getuuid() |
| | | columns.forEach(col => { |
| | | if (col.unique !== true || !unique) return |
| | | |
| | | if (col.uniqueFunc) { |
| | | unique = col.uniqueFunc(data, cell) |
| | | } else if (col.strict) { |
| | | let _index = data.findIndex(item => cell[col.dataIndex].toLowerCase() === item[col.dataIndex].toLowerCase()) |
| | | |
| | | if (_index > -1) { |
| | | unique = false |
| | | } |
| | | } else { |
| | | let _index = data.findIndex(item => cell[col.dataIndex] === item[col.dataIndex]) |
| | | |
| | | if (_index > -1) { |
| | | unique = false |
| | | } |
| | | } |
| | | }) |
| | | |
| | | if (!unique) return |
| | | |
| | | data.push(cell) |
| | | }) |
| | | |
| | | this.setState({ data, editingKey: '' }, () => { |
| | | this.props.onChange(data) |
| | | }) |
| | | } |
| | | |
| | | callback() |
| | | message.success('粘贴成功。') |
| | | } |
| | | |
| | | handleStatus = (record, type) => { |
| | | const { data } = this.state |
| | | |
| | | record[type] = record[type] === 'false' ? 'true' : 'false' |
| | | |
| | | let newData = data.map(item => { |
| | | if (record.uuid === item.uuid) return record |
| | | |
| | | return item |
| | | }) |
| | | |
| | | this.setState({ data: newData }, () => { |
| | | this.props.onChange(newData) |
| | | }) |
| | | } |
| | | |
| | | onSave = (record) => { |
| | |
| | | return |
| | | } |
| | | |
| | | let forbid = false |
| | | |
| | | columns.forEach(col => { |
| | | if (!col.forbids || forbid) return |
| | | |
| | | let key = record[col.dataIndex].toLowerCase() |
| | | if (col.forbids.includes(key)) { |
| | | forbid = col.title + '不可使用' + record[col.dataIndex] |
| | | } |
| | | }) |
| | | |
| | | if (forbid) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: forbid, |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | |
| | | let unique = true |
| | | columns.forEach(col => { |
| | | if (col.unique !== true || !unique) return |
| | | |
| | | let _index = newData.findIndex(item => record.uuid !== item.uuid && record[col.dataIndex] === item[col.dataIndex]) |
| | | if (col.uniqueFunc) { |
| | | unique = col.uniqueFunc(newData, record) |
| | | return |
| | | } else if (col.strict) { |
| | | let key = record[col.dataIndex].toLowerCase() |
| | | let _index = newData.findIndex(item => record.uuid !== item.uuid && key === item[col.dataIndex].toLowerCase()) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | } else { |
| | | let _index = newData.findIndex(item => record.uuid !== item.uuid && record[col.dataIndex] === item[col.dataIndex]) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | } |
| | | }) |
| | | |
| | |
| | | } |
| | | const newData = [...this.state.data] |
| | | const index = newData.findIndex(item => uuid === item.uuid) |
| | | if (index > -1) { |
| | | row = {...newData[index], ...row} |
| | | } else { |
| | | row.uuid = uuid |
| | | } |
| | | |
| | | let forbid = false |
| | | |
| | | columns.forEach(col => { |
| | | if (!col.forbids || forbid) return |
| | | |
| | | let key = row[col.dataIndex].toLowerCase() |
| | | if (col.forbids.includes(key)) { |
| | | forbid = col.title + '不可使用' + row[col.dataIndex] |
| | | } |
| | | }) |
| | | |
| | | if (forbid) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: forbid, |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | |
| | | let unique = true |
| | | columns.forEach(col => { |
| | | if (col.unique !== true || !unique) return |
| | | |
| | | let _index = newData.findIndex(item => uuid !== item.uuid && row[col.dataIndex] === item[col.dataIndex]) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | if (col.uniqueFunc) { |
| | | unique = col.uniqueFunc(newData, row) |
| | | return |
| | | } else if (col.strict) { |
| | | let key = row[col.dataIndex].toLowerCase() |
| | | let _index = newData.findIndex(item => row.uuid !== item.uuid && key === item[col.dataIndex].toLowerCase()) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | } else { |
| | | let _index = newData.findIndex(item => row.uuid !== item.uuid && row[col.dataIndex] === item[col.dataIndex]) |
| | | |
| | | if (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | } |
| | | }) |
| | | |
| | | if (!unique) return |
| | | |
| | | if (index > -1) { |
| | | const item = newData[index] |
| | | newData.splice(index, 1, { |
| | | ...item, |
| | | ...row, |
| | | }) |
| | | newData.splice(index, 1, row) |
| | | this.setState({ data: newData, editingKey: '' }, () => { |
| | | this.props.onChange(newData) |
| | | }) |
| | |
| | | }) |
| | | } |
| | | |
| | | edit(uuid) { |
| | | this.setState({ editingKey: uuid }) |
| | | edit(record) { |
| | | const { keyCol } = this.state |
| | | |
| | | this.setState({ editingKey: record.uuid, keyVal: keyCol ? record[keyCol] : '' }) |
| | | } |
| | | |
| | | typeChange = (val) => { |
| | | this.setState({ keyVal: val }) |
| | | } |
| | | |
| | | moveRow = (dragIndex, hoverIndex) => { |
| | |
| | | }) |
| | | } |
| | | |
| | | handleAdd = () => { |
| | | const { columns } = this.props |
| | | const { data } = this.state |
| | | |
| | | let _index = data.length + 1 |
| | | let item = { |
| | | uuid: Utils.getuuid() |
| | | } |
| | | |
| | | columns.forEach(col => { |
| | | if (!col.dataIndex) return |
| | | |
| | | item[col.dataIndex] = col.initval || '' |
| | | |
| | | if (col.unique) { |
| | | while (data.filter(cell => cell[col.dataIndex] === item[col.dataIndex]).length > 0) { |
| | | _index++ |
| | | item[col.dataIndex] = col.initval + _index |
| | | } |
| | | } |
| | | }) |
| | | |
| | | let _data = [...data, item] |
| | | |
| | | this.setState({ |
| | | data: _data |
| | | }, () => { |
| | | this.props.onChange(_data) |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { actions } = this.props |
| | | const { actions, indexShow, searchKey } = this.props |
| | | const { editLineId, keyVal } = this.state |
| | | |
| | | let components = { |
| | | body: { |
| | | cell: EditableCell |
| | | } |
| | | } |
| | | if (!actions || actions.length === 0 || actions.includes('down') || actions.includes('up')) { |
| | | |
| | | let moveprops = {} |
| | | if (actions.includes('move') && !searchKey) { |
| | | components.body.row = DragableBodyRow |
| | | moveprops.moveAble = !this.state.editingKey |
| | | moveprops.moveRow = this.moveRow |
| | | } |
| | | |
| | | const columns = this.state.columns.map(col => { |
| | | let columns = this.state.columns.map(col => { |
| | | if (col.copy) { |
| | | col.render = (text) => (<Paragraph copyable>{text}</Paragraph>) |
| | | } |
| | | |
| | | if (!col.editable) return col |
| | | |
| | | return { |
| | | ...col, |
| | | onCell: record => ({ |
| | |
| | | inputType: col.inputType, |
| | | dataIndex: col.dataIndex, |
| | | options: col.options || [], |
| | | rules: col.rules || [], |
| | | min: col.min || 0, |
| | | max: col.max || 500, |
| | | unlimit: col.unlimit, |
| | | required: col.required !== false ? true : false, |
| | | allowClear: col.allowClear === true, |
| | | keyCol: col.keyCol === true, |
| | | title: col.title, |
| | | editing: this.isEditing(record), |
| | | pass: col.keyVals ? col.keyVals.includes(keyVal) : true, |
| | | onSave: this.onSave, |
| | | typeChange: this.typeChange, |
| | | }), |
| | | } |
| | | }) |
| | | |
| | | if (indexShow !== false) { |
| | | columns.unshift({ |
| | | title: '序号', |
| | | dataIndex: '$index', |
| | | className: 'mk-index', |
| | | width: '60px', |
| | | }) |
| | | } |
| | | |
| | | const data = this.state.data.map((item, index) => { |
| | | item.$index = index + 1 |
| | | |
| | | return item |
| | | }) |
| | | |
| | | let reg = null |
| | | let regs = [] |
| | | |
| | | if (searchKey) { |
| | | reg = new RegExp(searchKey, 'i') |
| | | this.state.columns.forEach(col => { |
| | | if (col.searchable) { |
| | | regs.push(col.dataIndex) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | return ( |
| | | <EditableContext.Provider value={this.props.form}> |
| | | <div className={'modal-edit-table ' + (this.state.editingKey ? 'editing' : '')}> |
| | | <div className="modal-edit-table"> |
| | | {actions.includes('add') ? <PlusOutlined className="add-row" onClick={this.handleAdd} /> : null} |
| | | <DndProvider> |
| | | <Table |
| | | bordered |
| | | rowKey="uuid" |
| | | components={components} |
| | | dataSource={this.state.data} |
| | | dataSource={data} |
| | | columns={columns} |
| | | rowClassName="editable-row" |
| | | rowClassName={record => { |
| | | let className = 'editable-row' |
| | | if (editLineId && editLineId === record.uuid) { |
| | | className += ' active' |
| | | } |
| | | if (searchKey) { |
| | | if (regs.findIndex(f => reg.test(record[f])) === -1) { |
| | | className += ' hidden' |
| | | } |
| | | } |
| | | return className |
| | | }} |
| | | pagination={false} |
| | | onRow={(record, index) => ({ |
| | | index, |
| | | moveRow: this.moveRow, |
| | | ...moveprops |
| | | })} |
| | | /> |
| | | </DndProvider> |