| | |
| | | import React, { Component } from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { fromJS } from 'immutable' |
| | | import { Table, Form, Icon, Select, Cascader, notification, message } from 'antd' |
| | | import { Table, Form, Select, Cascader } from 'antd' |
| | | import { EditOutlined } from '@ant-design/icons' |
| | | |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import './index.scss' |
| | | |
| | | let eTDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS |
| | | const EditableContext = React.createContext() |
| | | const shortkeycode = { |
| | | 65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M', |
| | | 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', 90: 'Z' |
| | | } |
| | | |
| | | class EditableCell extends Component { |
| | | class CustomEditableCell extends Component { |
| | | getInput = () => { |
| | | const { inputType, options } = this.props |
| | | const { inputType, options, record } = this.props |
| | | |
| | | if (inputType === 'select') { |
| | | let _options = [] |
| | | if (record.$port) { |
| | | _options = window.GLOB.UserCacheMap.get(record.$port) || [] |
| | | } |
| | | return ( |
| | | <Select> |
| | | {options.map((item, i) => (<Select.Option key={i} value={item.value}> {item.text} </Select.Option>))} |
| | | <Select allowClear> |
| | | {_options.map((item, i) => (<Select.Option key={i} title={item.value} value={item.value}> {item.text} </Select.Option>))} |
| | | </Select> |
| | | ) |
| | | } else if (inputType === 'cascader') { |
| | | return ( |
| | | <Cascader options={options} placeholder=""/> |
| | | <Cascader allowClear options={options} placeholder=""/> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | renderCell = (form) => { |
| | | const { getFieldDecorator } = form |
| | | const { editing, dataIndex, title, record, children, className, required, inputType } = this.props |
| | | const { editing, editable, dataIndex, record, children, className } = this.props |
| | | |
| | | return ( |
| | | <td className={className}> |
| | | {editing ? ( |
| | | {editing && editable ? ( |
| | | <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}!`, |
| | | } |
| | | ], |
| | | initialValue: record[dataIndex], |
| | | initialValue: record[dataIndex] || '', |
| | | })(this.getInput())} |
| | | </Form.Item> |
| | | ) : ( |
| | |
| | | } |
| | | } |
| | | |
| | | class EditTable extends Component { |
| | | class CustomEditTable extends Component { |
| | | static propTpyes = { |
| | | data: PropTypes.any, // 数据列表 |
| | | columns: PropTypes.array, // 显示列 |
| | | onChange: PropTypes.func // 数据变化 |
| | | } |
| | | |
| | |
| | | data: [], |
| | | editingKey: '', |
| | | visible: false, |
| | | columns: [] |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { data } = this.props |
| | | let columns = fromJS(this.props.columns).toJS() |
| | | |
| | | columns.push({ |
| | | title: '操作', |
| | | columns: [{ |
| | | title: window.GLOB.dict['name'] || '名称', |
| | | dataIndex: 'label', |
| | | width: '25%' |
| | | }, { |
| | | title: window.GLOB.dict['shortcut'] || '快捷键', |
| | | dataIndex: 'shortcut', |
| | | inputType: 'cascader', |
| | | editable: true, |
| | | options: [], |
| | | width: '25%', |
| | | render: (text) => { |
| | | if (!text) return '' |
| | | return text[0] + '+' + shortkeycode[text[1]] |
| | | } |
| | | }, { |
| | | title: window.GLOB.dict['printer'] || '打印机', |
| | | dataIndex: 'printer', |
| | | inputType: 'select', |
| | | editable: true, |
| | | options: [], |
| | | width: '25%' |
| | | }, { |
| | | title: window.GLOB.dict['operation'] || '操作', |
| | | dataIndex: 'operation', |
| | | width: '140px', |
| | | render: (text, record) => { |
| | | render: (_, record) => { |
| | | const { editingKey } = this.state |
| | | const editable = this.isEditing(record) |
| | | return editable ? ( |
| | |
| | | <EditableContext.Consumer> |
| | | {form => ( |
| | | <span onClick={() => this.save(form, record.uuid)} style={{ marginRight: 8 , color: '#1890ff', cursor: 'pointer'}}> |
| | | {eTDict['model.save']} |
| | | {window.GLOB.dict['save'] || '保存'} |
| | | </span> |
| | | )} |
| | | </EditableContext.Consumer> |
| | | <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>{eTDict['model.cancel']}</span> |
| | | <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>{window.GLOB.dict['cancel'] || '取消'}</span> |
| | | </div> |
| | | ) : ( |
| | | <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')} style={{minWidth: '110px'}}> |
| | | <span className="primary" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><Icon type="edit" /></span> |
| | | <span className="primary" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><EditOutlined /></span> |
| | | </div> |
| | | ) |
| | | } |
| | | }], |
| | | printTypeColumns: [ |
| | | { |
| | | title: window.GLOB.dict['print_type'] || '打印类型', |
| | | dataIndex: 'Text', |
| | | width: '26.1%' |
| | | }, |
| | | { |
| | | title: window.GLOB.dict['printer'] || '打印机', |
| | | dataIndex: 'printer', |
| | | inputType: 'select', |
| | | editable: true, |
| | | options: [], |
| | | }, |
| | | { |
| | | title: window.GLOB.dict['operation'] || '操作', |
| | | dataIndex: 'operation', |
| | | width: '153px', |
| | | render: (_, record) => { |
| | | const { editingKey } = this.state |
| | | const editable = this.isEditing(record) |
| | | return editable ? ( |
| | | <div style={{textAlign: 'center', minWidth: '110px'}}> |
| | | <EditableContext.Consumer> |
| | | {form => ( |
| | | <span onClick={() => this.save(form, record.uuid, record.parentId)} style={{ marginRight: 8 , color: '#1890ff', cursor: 'pointer'}}> |
| | | {window.GLOB.dict['save'] || '保存'} |
| | | </span> |
| | | )} |
| | | </EditableContext.Consumer> |
| | | <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>{window.GLOB.dict['cancel'] || '取消'}</span> |
| | | </div> |
| | | ) : ( |
| | | <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')} style={{minWidth: '110px'}}> |
| | | <span className="primary" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><EditOutlined /></span> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { data } = this.props |
| | | |
| | | let keys = ['shift', 'ctrl', 'alt'] |
| | | let _options = [] |
| | | |
| | | keys.forEach(item => { |
| | | let _op = { |
| | | value: item, |
| | | label: item, |
| | | children: [] |
| | | } |
| | | Object.keys(shortkeycode).forEach(key => { |
| | | if (item === 'ctrl' && ['65', '67', '68', '69', '70', '71', '72', '74', '75', '76', '78', '79', '80', '82', '83', '84', '85', '86', '87', '88', '90'].includes(key)) return |
| | | if (item === 'alt' && ['65', '68', '69', '70'].includes(key)) return |
| | | |
| | | _op.children.push({ |
| | | value: +key, |
| | | label: shortkeycode[key] |
| | | }) |
| | | }) |
| | | |
| | | _options.push(_op) |
| | | }) |
| | | |
| | | this.setState({ |
| | | data: data || [], |
| | | columns |
| | | data: data, |
| | | columns: this.state.columns.map(item => { |
| | | if (item.dataIndex === 'shortcut') { |
| | | item.options = _options |
| | | } |
| | | return item |
| | | }) |
| | | }) |
| | | } |
| | | |
| | |
| | | |
| | | cancel = () => { |
| | | this.setState({ editingKey: '' }) |
| | | } |
| | | |
| | | 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 { |
| | | msg = window.btoa(window.encodeURIComponent(JSON.stringify(msg))) |
| | | } catch { |
| | | 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('复制成功。') |
| | | } |
| | | } |
| | | |
| | | onSave = (record) => { |
| | | const { columns } = this.state |
| | | const newData = [...this.state.data] |
| | | const index = newData.findIndex(item => record.uuid === item.uuid) |
| | | |
| | | if (index === -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '数据错误,无法找到行ID!', |
| | | 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 (_index > -1) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: col.title + '不可重复!', |
| | | duration: 5 |
| | | }) |
| | | unique = false |
| | | } |
| | | }) |
| | | |
| | | if (!unique) return |
| | | |
| | | newData.splice(index, 1, record) |
| | | this.setState({ data: newData, editingKey: '' }, () => { |
| | | this.props.onChange(newData) |
| | | }) |
| | | } |
| | | |
| | | handleDelete = (uuid) => { |
| | |
| | | }) |
| | | } |
| | | |
| | | save(form, uuid) { |
| | | const { columns } = this.state |
| | | save(form, uuid, parentId) { |
| | | form.validateFields((error, row) => { |
| | | if (error) { |
| | | return; |
| | | } |
| | | const newData = [...this.state.data] |
| | | const index = newData.findIndex(item => uuid === item.uuid) |
| | | |
| | | let unique = true |
| | | columns.forEach(col => { |
| | | if (col.unique !== true || !unique) return |
| | | let newData = null |
| | | |
| | | 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 (!unique) return |
| | | |
| | | if (index > -1) { |
| | | const item = newData[index] |
| | | newData.splice(index, 1, { |
| | | ...item, |
| | | ...row, |
| | | }) |
| | | this.setState({ data: newData, editingKey: '' }, () => { |
| | | this.props.onChange(newData) |
| | | if (parentId) { |
| | | newData = this.state.data.map(item => { |
| | | if (parentId === item.uuid) { |
| | | item.verify.printerTypeList = item.verify.printerTypeList.map(cell => { |
| | | if (uuid === cell.uuid) { |
| | | cell = {...cell, ...row} |
| | | } |
| | | return cell |
| | | }) |
| | | } |
| | | return item |
| | | }) |
| | | } else { |
| | | newData.push(row); |
| | | this.setState({ data: newData, editingKey: '' }, () => { |
| | | this.props.onChange(newData) |
| | | newData = this.state.data.map(item => { |
| | | if (uuid === item.uuid) { |
| | | item = {...item, ...row} |
| | | } |
| | | return item |
| | | }) |
| | | } |
| | | |
| | | this.setState({ data: newData, editingKey: '' }, () => { |
| | | this.props.onChange(newData) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | |
| | | this.setState({ editingKey: uuid }) |
| | | } |
| | | |
| | | moveRow = (dragIndex, hoverIndex) => { |
| | | const { editingKey } = this.state |
| | | let _data = fromJS(this.state.data).toJS() |
| | | |
| | | if (editingKey) return |
| | | |
| | | _data.splice(hoverIndex, 0, ..._data.splice(dragIndex, 1)) |
| | | |
| | | this.setState({ |
| | | data: _data |
| | | }, () => { |
| | | this.props.onChange(_data) |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | let components = { |
| | | body: { |
| | | cell: EditableCell |
| | | cell: CustomEditableCell |
| | | } |
| | | } |
| | | |
| | |
| | | inputType: col.inputType, |
| | | dataIndex: col.dataIndex, |
| | | options: col.options || [], |
| | | required: col.required !== false ? true : false, |
| | | title: col.title, |
| | | editable: col.dataIndex === 'shortcut' || record.funcType === 'print', |
| | | editing: this.isEditing(record), |
| | | onSave: this.onSave, |
| | | }), |
| | | } |
| | | }) |
| | | |
| | | const printTypeColumns = this.state.printTypeColumns.map(col => { |
| | | if (!col.editable) return col |
| | | return { |
| | | ...col, |
| | | onCell: record => ({ |
| | | record, |
| | | inputType: col.inputType, |
| | | dataIndex: col.dataIndex, |
| | | options: [], |
| | | title: col.title, |
| | | editable: true, |
| | | editing: this.isEditing(record), |
| | | }), |
| | | } |
| | | }) |
| | | |
| | | return ( |
| | | <EditableContext.Provider value={this.props.form}> |
| | | <div className="modal-edit-table"> |
| | | <div className="modal-custom-edit-table"> |
| | | <Table |
| | | bordered |
| | | rowKey="uuid" |
| | | components={components} |
| | | dataSource={this.state.data} |
| | | columns={columns} |
| | | rowClassName="editable-row" |
| | | rowClassName={(record) => 'editable-row' + (record.$expanded ? ' print' : '')} |
| | | pagination={false} |
| | | onRow={(record, index) => ({ |
| | | index, |
| | | moveAble: !this.state.editingKey, |
| | | moveRow: this.moveRow, |
| | | })} |
| | | expandedRowRender={record => ( |
| | | record.$expanded ? |
| | | <Table |
| | | bordered |
| | | rowKey="key" |
| | | size="small" |
| | | components={components} |
| | | rowClassName="editable-row" |
| | | dataSource={record.verify.printerTypeList} |
| | | columns={printTypeColumns} |
| | | pagination={false} |
| | | /> : null |
| | | )} |
| | | /> |
| | | </div> |
| | | </EditableContext.Provider> |
| | |
| | | } |
| | | } |
| | | |
| | | export default Form.create()(EditTable) |
| | | export default Form.create()(CustomEditTable) |