From 9a11e62adeb8d435b52a361eb62d5b59e1deef2a Mon Sep 17 00:00:00 2001 From: king <18310653075@163.com> Date: 星期四, 23 五月 2024 21:14:50 +0800 Subject: [PATCH] 2024-05-23 --- src/templates/zshare/editTable/index.jsx | 545 +++++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 435 insertions(+), 110 deletions(-) diff --git a/src/templates/zshare/editTable/index.jsx b/src/templates/zshare/editTable/index.jsx index a5af7c8..2c9ecc4 100644 --- a/src/templates/zshare/editTable/index.jsx +++ b/src/templates/zshare/editTable/index.jsx @@ -2,24 +2,67 @@ import PropTypes from 'prop-types' import { is, fromJS } from 'immutable' import { DndProvider, DragSource, DropTarget } from 'react-dnd' -import { Table, Input, InputNumber, Popconfirm, Form, Select, Radio, Cascader, notification, message, Modal, Typography } from 'antd' -import { CopyOutlined, EditOutlined, DeleteOutlined, SnippetsOutlined } from '@ant-design/icons' +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 PasteForm from '@/templates/zshare/pasteform' import asyncComponent from '@/utils/asyncComponent' -import CusSwitch from './cusSwitch' -import zhCN from '@/locales/zh-CN/model.js' -import enUS from '@/locales/en-US/model.js' +import MKEmitter from '@/utils/events.js' import './index.scss' const MkEditIcon = asyncComponent(() => import('@/components/mkIcon')) -let eTDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS +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() { @@ -80,21 +123,21 @@ 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> ) @@ -110,10 +153,12 @@ ) } 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)}/> } @@ -131,24 +176,32 @@ 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> ) @@ -162,6 +215,7 @@ class EditTable extends Component { static propTpyes = { actions: PropTypes.any, // 鎿嶄綔椤� + searchKey: PropTypes.any, // 鎼滅储鏉′欢 data: PropTypes.any, // 鏁版嵁鍒楄〃 columns: PropTypes.array, // 鏄剧ず鍒� onChange: PropTypes.func // 鏁版嵁鍙樺寲 @@ -170,30 +224,50 @@ state = { data: [], editingKey: '', - visible: false, - 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.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> - {eTDict['model.operation']} + 鎿嶄綔 <span className="copy-control"> {actions.includes('copy') ? <CopyOutlined title="澶嶅埗" onClick={() => this.copy()} /> : null} - {actions.includes('copy') ? <SnippetsOutlined title="绮樿创" onClick={this.paste} /> : null} + {actions.includes('copy') ? <PasteBoard getPasteValue={this.pasteSubmit}/> : null} {actions.includes('clear') ? <DeleteOutlined title="娓呯┖" onClick={this.clear} /> : null} </span> </div>), @@ -207,24 +281,27 @@ <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 style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>鍙栨秷</span> </div> ) : ( - <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')} style={{minWidth: '110px'}}> - {actions.includes('edit') ? <span className="primary" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><EditOutlined /></span> : null} - {actions.includes('copy') ? <span className="copy" onClick={() => {editingKey === '' && this.copy(record)}}><CopyOutlined /></span> : null} + <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"><DeleteOutlined /></span> </Popconfirm> : 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> ) } @@ -240,11 +317,13 @@ this.setState({ data: data || [], operation, - columns + 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.props.columns), fromJS(nextProps.columns))) { @@ -268,6 +347,35 @@ 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 @@ -311,6 +419,11 @@ } 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') @@ -328,27 +441,76 @@ } } - paste = () => { - this.setState({visible: true}) - } - - pasteSubmit = () => { + pasteSubmit = (res, callback) => { const { type } = this.props const { columns } = this.state let data = fromJS(this.state.data).toJS() - this.pasteFormRef.handleConfirm().then(res => { - if (res.key !== type) { - message.warning('閰嶇疆淇℃伅鏍煎紡閿欒锛�') - return - } + 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})` + } - if (res.type === 'line') { - let unique = true - res.data.uuid = Utils.getuuid() - columns.forEach(col => { - if (col.unique !== true || !unique) return + 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) { @@ -359,37 +521,66 @@ }) unique = false } - }) + } + }) - if (!unique) return + if (!unique) return - data.unshift(res.data) - this.setState({ data, editingKey: '', visible: false }, () => { - 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 + 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: '', visible: false }, () => { - this.props.onChange(data) - }) - } - message.success('绮樿创鎴愬姛銆�') + 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) }) } @@ -407,19 +598,56 @@ 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 + } } }) @@ -450,31 +678,69 @@ } 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) }) @@ -487,8 +753,14 @@ }) } - 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) => { @@ -506,8 +778,40 @@ }) } + 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: { @@ -516,7 +820,7 @@ } let moveprops = {} - if (actions.includes('move')) { + if (actions.includes('move') && !searchKey) { components.body.row = DragableBodyRow moveprops.moveAble = !this.state.editingKey moveprops.moveRow = this.moveRow @@ -526,7 +830,9 @@ if (col.copy) { col.render = (text) => (<Paragraph copyable>{text}</Paragraph>) } + if (!col.editable) return col + return { ...col, onCell: record => ({ @@ -534,23 +840,30 @@ 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, }), } }) - columns.unshift({ - title: '搴忓彿', - dataIndex: '$index', - className: 'mk-index', - width: '60px', - }) + 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 @@ -558,9 +871,22 @@ 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"> + {actions.includes('add') ? <PlusOutlined className="add-row" onClick={this.handleAdd} /> : null} <DndProvider> <Table bordered @@ -568,7 +894,18 @@ components={components} 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, @@ -576,18 +913,6 @@ })} /> </DndProvider> - {/* 淇℃伅绮樿创 */} - <Modal - title={eTDict['header.form.paste']} - visible={this.state.visible} - width={600} - maskClosable={false} - onOk={this.pasteSubmit} - onCancel={() => {this.setState({visible: false})}} - destroyOnClose - > - <PasteForm dict={eTDict} wrappedComponentRef={(inst) => this.pasteFormRef = inst}/> - </Modal> </div> </EditableContext.Provider> ) -- Gitblit v1.8.0