New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Table, Modal, Input, InputNumber, notification, message } from 'antd' |
| | | // import { EditOutlined } from '@ant-design/icons' |
| | | |
| | | import Api from '@/api' |
| | | import MKEmitter from '@/utils/events.js' |
| | | import zhCN from '@/locales/zh-CN/main.js' |
| | | import enUS from '@/locales/en-US/main.js' |
| | | import '@/assets/css/table.scss' |
| | | import './index.scss' |
| | | |
| | | class BodyRow extends React.Component { |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.props.data), fromJS(nextProps.data)) |
| | | } |
| | | |
| | | render() { |
| | | let { data, ...resProps } = this.props |
| | | let style = {} |
| | | let className = '' |
| | | |
| | | return <tr {...resProps} className={className} style={style}/> |
| | | } |
| | | } |
| | | |
| | | class BodyCell extends React.Component { |
| | | state = { |
| | | editing: false, |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.props.record), fromJS(nextProps.record)) || |
| | | nextState.editing !== this.state.editing |
| | | } |
| | | |
| | | componentDidMount () { |
| | | MKEmitter.addListener('tdFocus', this.tdFocus) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新,清除快捷键设置 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | MKEmitter.removeListener('tdFocus', this.tdFocus) |
| | | } |
| | | |
| | | tdFocus = (id) => { |
| | | const { col, record } = this.props |
| | | |
| | | if (id !== col.uuid + record.uuid) return |
| | | |
| | | this.focus() |
| | | } |
| | | |
| | | enterPress = () => { |
| | | const { col, record } = this.props |
| | | const { value } = this.state |
| | | |
| | | this.setState({editing: false}) |
| | | setTimeout(() => { |
| | | if (col.enter === '$next') { |
| | | MKEmitter.emit('nextLine', col, record.uuid) |
| | | } else { |
| | | MKEmitter.emit('tdFocus', col.enter + record.uuid) |
| | | } |
| | | }, 50) |
| | | |
| | | if (value !== record[col.field]) { |
| | | MKEmitter.emit('changeRecord', col.tableId, {...record, [col.field]: value}) |
| | | } |
| | | } |
| | | |
| | | focus = () => { |
| | | const { col, record } = this.props |
| | | |
| | | if (record.type === 'total') return |
| | | |
| | | let val = record[col.field] !== undefined ? record[col.field] : '' |
| | | |
| | | this.setState({editing: true, value: val}, () => { |
| | | let node = document.getElementById(col.uuid + record.uuid) |
| | | node && node.select() |
| | | }) |
| | | } |
| | | |
| | | onBlur = () => { |
| | | const { col, record } = this.props |
| | | const { value } = this.state |
| | | |
| | | this.setState({editing: false}) |
| | | |
| | | if (value !== record[col.field]) { |
| | | MKEmitter.emit('changeRecord', col.tableId, {...record, [col.field]: value}) |
| | | } |
| | | } |
| | | |
| | | onChange = (val) => { |
| | | this.setState({value: val}) |
| | | } |
| | | |
| | | render() { |
| | | let { col, record, className } = this.props |
| | | const { editing } = this.state |
| | | |
| | | let children = null |
| | | let colSpan = 1 |
| | | |
| | | if (col.field === 'remark') { |
| | | if (record.type === 'total') { |
| | | children = <div className="content-wrap" style={{lineHeight: '60px'}}>合计: {record.total}</div> |
| | | colSpan = 2 |
| | | } else { |
| | | let val = record.remark || '' |
| | | |
| | | if (editing) { |
| | | children = <Input.TextArea id={col.uuid + record.uuid} autoSize={false} defaultValue={val} onChange={(e) => this.onChange(e.target.value)} onPressEnter={this.enterPress} onBlur={this.onBlur}/> |
| | | } else { |
| | | children = <div className="content-wrap" onClick={this.focus}>{val}</div> |
| | | } |
| | | } |
| | | } else if (col.field === 'subject') { |
| | | if (record.type === 'total') { |
| | | colSpan = 0 |
| | | } else { |
| | | let val = record.subject || '' |
| | | |
| | | if (editing) { |
| | | children = <Input.TextArea id={col.uuid + record.uuid} autoSize={false} defaultValue={val} onChange={(e) => this.onChange(e.target.value)} onPressEnter={this.enterPress} onBlur={this.onBlur}/> |
| | | } else { |
| | | children = <div className="content-wrap" onClick={this.focus}>{val}</div> |
| | | } |
| | | } |
| | | } else if (col.field === 'debtor') { |
| | | let val = record.debtor |
| | | let down = false |
| | | let vals = [] |
| | | if (typeof(val) === 'number') { |
| | | if (val < 0) { |
| | | down = true |
| | | val = Math.abs(val) |
| | | } |
| | | vals = (val * 100).toFixed(0).split('').reverse() |
| | | } |
| | | |
| | | if (editing) { |
| | | children = <InputNumber id={col.uuid + record.uuid} defaultValue={val} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/> |
| | | } else { |
| | | children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}> |
| | | <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span> |
| | | <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span> |
| | | </div> |
| | | } |
| | | } else if (col.field === 'creditor') { |
| | | let val = record.creditor |
| | | let down = false |
| | | let vals = [] |
| | | if (typeof(val) === 'number') { |
| | | if (val < 0) { |
| | | down = true |
| | | val = Math.abs(val) |
| | | } |
| | | vals = (val * 100).toFixed(0).split('').reverse() |
| | | } |
| | | |
| | | if (editing) { |
| | | children = <InputNumber id={col.uuid + record.uuid} defaultValue={val} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/> |
| | | } else { |
| | | children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}> |
| | | <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span> |
| | | <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span> |
| | | </div> |
| | | } |
| | | } |
| | | |
| | | if (!colSpan) return null |
| | | |
| | | return (<td colSpan={colSpan} className={className}>{children}</td>) |
| | | } |
| | | } |
| | | |
| | | class VoucherTable extends Component { |
| | | static propTpyes = { |
| | | config: PropTypes.object, // 菜单Id |
| | | BID: PropTypes.any, // 主表ID |
| | | data: PropTypes.any, // 表格数据 |
| | | total: PropTypes.any, // 总数 |
| | | loading: PropTypes.bool, // 表格加载中 |
| | | refreshdata: PropTypes.func, // 表格中排序列、页码的变化时刷新 |
| | | } |
| | | |
| | | state = { |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, |
| | | data: [], |
| | | edData: [], |
| | | edColumns: [], |
| | | tableId: '', // 表格ID |
| | | pageSize: 10, // 每页数据条数 |
| | | columns: null, // 显示列 |
| | | loading: false, |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | let tableId = (() => { |
| | | let uuid = [] |
| | | let _options = 'abcdefghigklmnopqrstuv' |
| | | for (let i = 0; i < 19; i++) { |
| | | uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1)) |
| | | } |
| | | return uuid.join('') |
| | | }) () |
| | | |
| | | this.setState({ |
| | | data: [ |
| | | {uuid: 'dsfdsfdsfs', remark: '提现', subject: '1001 库存现金', debtor: 124, creditor: ''}, |
| | | {uuid: 'dsfdsfsdfdsfs', remark: '购入固定资产', subject: '1001 库存现金', debtor: '', creditor: 124}, |
| | | {uuid: 'dsfdsfsdgdrsfs', remark: '转结销售成本', subject: '1001 库存现金', debtor: -524, creditor: ''}, |
| | | {uuid: 'dsfdsfsdgfdsgdsfs', remark: '提现', subject: '1001 库存现金', debtor: 34, creditor: ''}, |
| | | {uuid: 'dsfdsfsdgfdsswgdsfs', type: 'total', total: this.changeMoneyToChinese(354.24), debtor: 354.24, creditor: ''}, |
| | | ], |
| | | columns: [ |
| | | { |
| | | title: '摘要', |
| | | dataIndex: 'remark', |
| | | key: 'remark', |
| | | width: '22%', |
| | | onCell: record => ({ |
| | | record, |
| | | col: {uuid: 'remark', field: 'remark', tableId: tableId}, |
| | | }) |
| | | }, |
| | | { |
| | | title: '会计科目', |
| | | dataIndex: 'subject', |
| | | key: 'subject', |
| | | width: '34%', |
| | | onCell: record => ({ |
| | | record, |
| | | col: {uuid: 'subject', field: 'subject', tableId: tableId}, |
| | | }) |
| | | }, |
| | | { |
| | | title: () => (<> |
| | | <div className="money-title">借方金额</div> |
| | | <div className="money-uint"> |
| | | <span>亿</span> <span>千</span> <span>百</span> <span>十</span> <span>万</span> <span>千</span> |
| | | <span>百</span> <span>十</span> <span>元</span> <span>角</span> <span className="last">分</span> |
| | | </div> |
| | | </>), |
| | | dataIndex: 'debtor', |
| | | key: 'debtor', |
| | | width: '22%', |
| | | onCell: record => ({ |
| | | record, |
| | | col: {uuid: 'debtor', field: 'debtor', tableId: tableId}, |
| | | }) |
| | | }, |
| | | { |
| | | title: () => (<> |
| | | <div className="money-title">贷方金额</div> |
| | | <div className="money-uint"> |
| | | <span>亿</span> <span>千</span> <span>百</span> <span>十</span> <span>万</span> <span>千</span> |
| | | <span>百</span> <span>十</span> <span>元</span> <span>角</span> <span className="last">分</span> |
| | | </div> |
| | | </>), |
| | | dataIndex: 'creditor', |
| | | key: 'creditor', |
| | | width: '22%', |
| | | onCell: record => ({ |
| | | record, |
| | | col: {uuid: 'creditor', field: 'creditor', tableId: tableId}, |
| | | }) |
| | | } |
| | | ], |
| | | tableId |
| | | }) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | componentDidMount () { |
| | | MKEmitter.addListener('nextLine', this.nextLine) |
| | | MKEmitter.addListener('delRecord', this.delRecord) |
| | | MKEmitter.addListener('changeRecord', this.changeRecord) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | MKEmitter.removeListener('nextLine', this.nextLine) |
| | | MKEmitter.removeListener('delRecord', this.delRecord) |
| | | MKEmitter.removeListener('changeRecord', this.changeRecord) |
| | | } |
| | | |
| | | UNSAFE_componentWillReceiveProps(nextProps) { |
| | | if (!is(fromJS(this.props.data), fromJS(nextProps.data))) { |
| | | this.setState({data: nextProps.data || []}) |
| | | } |
| | | } |
| | | |
| | | changeMoneyToChinese = (money) => { |
| | | let cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] |
| | | let cnIntRadice = ['', '拾', '佰', '仟'] |
| | | let cnIntUnits = ['', '万', '亿', '兆'] |
| | | let cnDecUnits = ['角', '分', '毫', '厘'] |
| | | let cnInteger = '整' |
| | | let cnIntLast = '元' |
| | | let maxNum = 999999999999999.9999 // 最大处理的数字 |
| | | let IntegerNum = null |
| | | let DecimalNum = null |
| | | let ChineseStr = '' |
| | | let parts = null // 分离金额后用的数组,预定义 |
| | | let Symbol = '' // 正负值标记 |
| | | |
| | | if (money === '') return '' |
| | | |
| | | if (money >= maxNum) return '超出最大处理数字' |
| | | |
| | | if (money === 0) { |
| | | ChineseStr = cnNums[0] + cnIntLast + cnInteger; |
| | | return ChineseStr |
| | | } |
| | | if(money < 0) { |
| | | money = -money |
| | | Symbol = '负' |
| | | } |
| | | money = money.toString() // 转换为字符串 |
| | | if (money.indexOf('.') === -1) { |
| | | IntegerNum = money |
| | | DecimalNum = '' |
| | | } else { |
| | | parts = money.split('.') |
| | | IntegerNum = parts[0] |
| | | DecimalNum = parts[1].substr(0, 4) |
| | | } |
| | | |
| | | if (parseInt(IntegerNum, 10) > 0) { // 获取整型部分转换 |
| | | let zeroCount = 0 |
| | | let IntLen = IntegerNum.length |
| | | for (let i = 0; i < IntLen; i++) { |
| | | let n = IntegerNum.substr(i, 1) |
| | | let p = IntLen - i - 1 |
| | | let q = p / 4 |
| | | let m = p % 4 |
| | | |
| | | if (n === '0') { |
| | | zeroCount++ |
| | | } else { |
| | | if (zeroCount > 0) { |
| | | ChineseStr += cnNums[0] |
| | | } |
| | | zeroCount = 0 // 归零 |
| | | ChineseStr += cnNums[parseInt(n)] + cnIntRadice[m] |
| | | } |
| | | |
| | | if (m === 0 && zeroCount < 4) { |
| | | ChineseStr += cnIntUnits[q] |
| | | } |
| | | } |
| | | ChineseStr += cnIntLast |
| | | } |
| | | |
| | | if (DecimalNum !== '') { // 小数部分 |
| | | let decLen = DecimalNum.length |
| | | |
| | | for (let i = 0; i < decLen; i++) { |
| | | let n = DecimalNum.substr(i, 1) |
| | | if (n !== '0') { |
| | | ChineseStr += cnNums[Number(n)] + cnDecUnits[i] |
| | | } |
| | | } |
| | | } |
| | | if (ChineseStr === '') { |
| | | ChineseStr += cnNums[0] + cnIntLast + cnInteger |
| | | } else if (DecimalNum === '') { |
| | | ChineseStr += cnInteger |
| | | } |
| | | |
| | | ChineseStr = Symbol + ChineseStr |
| | | |
| | | return ChineseStr |
| | | } |
| | | |
| | | nextLine = (col, index) => { |
| | | const { setting } = this.props |
| | | const { edData, initEditLine, tableId } = this.state |
| | | |
| | | if (col.tableId !== tableId) return |
| | | |
| | | index = +index |
| | | |
| | | if (index < edData.length && initEditLine) { |
| | | MKEmitter.emit('tdFocus', initEditLine.uuid + (index + 1)) |
| | | } else if (col.footEnter === 'add' && setting.addable === 'true') { |
| | | setTimeout(() => { |
| | | this.plusLine(initEditLine) |
| | | }, 10) |
| | | } else if (col.footEnter === 'sub') { |
| | | setTimeout(() => { |
| | | this.checkData() |
| | | }, 10) |
| | | } |
| | | } |
| | | |
| | | plusLine = (initEditLine) => { |
| | | const { edData } = this.state |
| | | |
| | | let item = {...edData[edData.length - 1]} |
| | | |
| | | item.key = item.key + 1 |
| | | item.$$uuid = '$new' |
| | | |
| | | this.setState({edData: [...edData, item]}, () => { |
| | | MKEmitter.emit('tdFocus', initEditLine.uuid + item.uuid) |
| | | }) |
| | | } |
| | | |
| | | delRecord = (id, record) => { |
| | | const { tableId, edData } = this.state |
| | | |
| | | if (id !== tableId) return |
| | | |
| | | let _data = [] |
| | | |
| | | if (record.$$uuid === '$new') { |
| | | _data = edData.filter(item => item.uuid !== record.uuid) |
| | | _data = _data.map((item, index) => { |
| | | item.key = index |
| | | return item |
| | | }) |
| | | } else { |
| | | _data = edData.map(item => { |
| | | if (item.uuid === record.uuid) { |
| | | record.$deleted = true |
| | | return record |
| | | } else { |
| | | return item |
| | | } |
| | | }) |
| | | } |
| | | |
| | | this.setState({edData: _data}) |
| | | } |
| | | |
| | | changeRecord = (id, record) => { |
| | | const { tableId } = this.state |
| | | |
| | | if (id !== tableId) return |
| | | |
| | | let _data = this.state.edData.map(item => { |
| | | if (item.uuid === record.uuid) { |
| | | return record |
| | | } else { |
| | | return item |
| | | } |
| | | }) |
| | | |
| | | this.setState({edData: _data}) |
| | | } |
| | | |
| | | addLine = () => { |
| | | const { BID } = this.props |
| | | const { edData } = this.state |
| | | |
| | | let item = {} |
| | | if (edData.length > 0) { |
| | | item = {...edData[edData.length - 1]} |
| | | item.key = item.key + 1 |
| | | item.$$uuid = '$new' |
| | | } else { |
| | | item.key = 0 |
| | | item.$$uuid = '$new' |
| | | item.$$BID = BID || '' |
| | | } |
| | | |
| | | this.setState({edData: [...edData, item]}) |
| | | } |
| | | |
| | | checkData = () => { |
| | | const { edData } = this.state |
| | | |
| | | if (edData.length === 0) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '提交数据不可为空!', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | let err = '' |
| | | let data = fromJS(edData).toJS().map(item => { |
| | | // let line = [] |
| | | // fields.forEach(col => { |
| | | // if (col.editable !== 'true' || item.$deleted) { |
| | | // if (col.type === 'number') { |
| | | // item[col.field] = +item[col.field] |
| | | // if (isNaN(item[col.field])) { |
| | | // item[col.field] = 0 |
| | | // } |
| | | // } else { |
| | | // item[col.field] = item[col.field] !== undefined ? (item[col.field] + '') : '' |
| | | // } |
| | | // return |
| | | // } |
| | | // if (col.type === 'text') { |
| | | // let val = item[col.field] !== undefined ? (item[col.field] + '') : '' |
| | | // if (col.required === 'true' && !val) { |
| | | // line.push(`${col.label}不可为空`) |
| | | // } |
| | | // item[col.field] = val |
| | | // } else if (col.type === 'number') { |
| | | // let val = item[col.field] |
| | | // if (!val && val !== 0) { |
| | | // line.push(`${col.label}不可为空`) |
| | | // return |
| | | // } |
| | | // val = +val |
| | | // if (isNaN(val)) { |
| | | // line.push(`${col.label}数据格式错误`) |
| | | // return |
| | | // } |
| | | |
| | | // val = +val.toFixed(col.decimal || 0) |
| | | |
| | | // if (typeof(col.max) === 'number' && val > col.max) { |
| | | // line.push(`${col.label}不可大于${col.max}`) |
| | | // } else if (typeof(col.min) === 'number' && val < col.min) { |
| | | // line.push(`${col.label}不可小于${col.min}`) |
| | | // } |
| | | |
| | | // item[col.field] = val |
| | | // } |
| | | // }) |
| | | |
| | | return item |
| | | }) |
| | | |
| | | if (err) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: err, |
| | | duration: 5 |
| | | }) |
| | | } else { |
| | | this.submit(data) |
| | | } |
| | | } |
| | | |
| | | submit = (data) => { |
| | | const { BID } = this.props |
| | | |
| | | let param = { |
| | | // excel_in: result.lines, |
| | | BID: BID || '' |
| | | } |
| | | |
| | | this.setState({ |
| | | loading: true |
| | | }) |
| | | |
| | | param.func = 'submit.innerFunc' |
| | | |
| | | Api.genericInterface(param).then((res) => { |
| | | if (res.status) { |
| | | this.execSuccess(res) |
| | | } else { |
| | | this.execError(res) |
| | | } |
| | | }, () => { |
| | | this.execError({}) |
| | | }) |
| | | } |
| | | |
| | | execSuccess = (res) => { |
| | | const { submit } = this.props |
| | | |
| | | if (res && res.ErrCode === 'S') { // 执行成功 |
| | | notification.success({ |
| | | top: 92, |
| | | message: res.ErrMesg || this.state.dict['main.action.confirm.success'], |
| | | duration: submit.stime ? submit.stime : 2 |
| | | }) |
| | | } else if (res && res.ErrCode === 'Y') { // 执行成功 |
| | | Modal.success({ |
| | | title: res.ErrMesg || this.state.dict['main.action.confirm.success'] |
| | | }) |
| | | } else if (res && res.ErrCode === '-1') { // 完成后不提示 |
| | | |
| | | } |
| | | |
| | | this.setState({ |
| | | loading: false |
| | | }) |
| | | |
| | | if (submit.closetab === 'true') { |
| | | MKEmitter.emit('popclose') |
| | | } |
| | | if (submit.execSuccess !== 'never') { |
| | | MKEmitter.emit('refreshByButtonResult', submit.$menuId, submit.execSuccess, submit) |
| | | } |
| | | } |
| | | |
| | | execError = (res) => { |
| | | const { submit } = this.props |
| | | |
| | | if (res.ErrCode === 'E') { |
| | | Modal.error({ |
| | | title: res.message || res.ErrMesg, |
| | | }) |
| | | } else if (res.ErrCode === 'N') { |
| | | notification.error({ |
| | | top: 92, |
| | | message: res.message || res.ErrMesg, |
| | | duration: submit.ntime ? submit.ntime : 10 |
| | | }) |
| | | } else if (res.ErrCode === 'F') { |
| | | notification.error({ |
| | | className: 'notification-custom-error', |
| | | top: 92, |
| | | message: res.message || res.ErrMesg, |
| | | duration: submit.ftime ? submit.ftime : 10 |
| | | }) |
| | | } else if (res.ErrCode === 'NM') { |
| | | message.error(res.message || res.ErrMesg) |
| | | } |
| | | |
| | | this.setState({ |
| | | loading: false |
| | | }) |
| | | |
| | | if (submit.execError !== 'never') { |
| | | MKEmitter.emit('refreshByButtonResult', submit.$menuId, submit.execError, submit) |
| | | } |
| | | } |
| | | |
| | | render() { |
| | | const { tableId, data, columns} = this.state |
| | | |
| | | const components = { |
| | | body: { |
| | | row: BodyRow, |
| | | cell: BodyCell |
| | | } |
| | | } |
| | | |
| | | // 数据收起时,过滤已选数据 |
| | | let _data = data |
| | | |
| | | return ( |
| | | <div className="voucher-table-wrap" id={tableId}> |
| | | <Table |
| | | rowKey="uuid" |
| | | components={components} |
| | | columns={columns} |
| | | dataSource={_data} |
| | | bordered={true} |
| | | // loading={this.props.loading} |
| | | onRow={(record, index) => { |
| | | return { |
| | | data: record |
| | | } |
| | | }} |
| | | pagination={false} |
| | | /> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default VoucherTable |