import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Input, InputNumber, notification, Modal } from 'antd'
|
import { EllipsisOutlined } from '@ant-design/icons'
|
|
import Utils from '@/utils/utils.js'
|
import MKEmitter from '@/utils/events.js'
|
import SubTable from '../subTable'
|
import './index.scss'
|
|
class DetailLine extends React.Component {
|
state = {
|
bill_count: 0,
|
unitprice: 0,
|
amount_line: 0,
|
visible: false
|
}
|
|
UNSAFE_componentWillMount() {
|
let { line } = this.props
|
|
this.setState({
|
bill_count: line.bill_count || '',
|
unitprice: line.unitprice || '',
|
amount_line: line.amount_line || ''
|
})
|
}
|
|
onChange = (value, key) => {
|
let line = {...this.props.line}
|
|
if (['bill_count', 'unitprice', 'amount_line'].includes(key)) {
|
line[key] = value || 0
|
if (line[key]) {
|
if (key === 'bill_count') {
|
line[key] = Math.round(line[key] * 10000000000) / 10000000000
|
if (line.unitprice) {
|
line.amount_line = Math.round(line.unitprice * line.bill_count * 100) / 100
|
}
|
} else if (key === 'unitprice') {
|
line[key] = Math.round(line[key] * 10000000000) / 10000000000
|
if (line.bill_count) {
|
line.amount_line = Math.round(line.unitprice * line.bill_count * 100) / 100
|
}
|
} else if (key === 'amount_line') {
|
line[key] = Math.round(line[key] * 100) / 100
|
if (line.bill_count) {
|
line.unitprice = Math.round(line.amount_line / line.bill_count * 10000000000) / 10000000000
|
} else if (line.unitprice) {
|
line.bill_count = Math.round(line.amount_line / line.unitprice * 10000000000) / 10000000000
|
}
|
}
|
}
|
} else {
|
line[key] = value
|
}
|
|
this.setState({
|
bill_count: line.bill_count || '',
|
unitprice: line.unitprice || '',
|
amount_line: line.amount_line || ''
|
})
|
|
this.props.changeLine(line, key)
|
}
|
|
render() {
|
const { line, delLine, trigger } = this.props
|
const { bill_count, unitprice, amount_line } = this.state
|
|
return <div className="mk-tr active">
|
<div className="mk-td">
|
<div className="mk-input">{line.productname || ''}<EllipsisOutlined onClick={trigger}/></div>
|
</div>
|
<div className="mk-td">
|
<Input defaultValue={line.spec || ''} onChange={(e) => this.onChange(e.target.value, 'spec')}/>
|
</div>
|
<div className="mk-td">
|
<Input defaultValue={line.unit || ''} onChange={(e) => this.onChange(e.target.value, 'unit')}/>
|
</div>
|
<div className="mk-td">
|
<InputNumber value={bill_count} onChange={(val) => this.setState({bill_count: val})} onBlur={(e) => this.onChange(e.target.value, 'bill_count')}/>
|
</div>
|
<div className="mk-td">
|
<InputNumber value={unitprice} onChange={(val) => this.setState({unitprice: val})} onBlur={(e) => this.onChange(e.target.value, 'unitprice')}/>
|
</div>
|
<div className="mk-td">
|
<InputNumber value={amount_line} onChange={(val) => this.setState({amount_line: val})} onBlur={(e) => this.onChange(e.target.value, 'amount_line')}/>
|
</div>
|
<div className="mk-td">{line.tax_name}</div>
|
<div className="mk-td mk-right">{line.tax_amount} <span className="del-line" onClick={() => delLine(line.uuid)}></span> </div>
|
</div>
|
}
|
}
|
|
class InvoiceTable extends Component {
|
static propTpyes = {
|
config: PropTypes.object,
|
data: PropTypes.any,
|
onChange: PropTypes.func
|
}
|
|
state = {
|
data: [],
|
editKey: '',
|
total: {}
|
}
|
|
UNSAFE_componentWillMount () {
|
const { data } = this.props
|
|
let _data = fromJS(data).toJS()
|
if (!_data.length) {
|
_data = [{uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}]
|
}
|
|
this.setState({
|
data: _data
|
}, () => {
|
this.getTotal(_data)
|
})
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
componentDidMount () {
|
MKEmitter.addListener('resetDetails', this.resetDetails)
|
}
|
|
/**
|
* @description 组件销毁,清除state更新
|
*/
|
componentWillUnmount () {
|
this.setState = () => {
|
return
|
}
|
MKEmitter.removeListener('resetDetails', this.resetDetails)
|
}
|
|
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 === 0) return ''
|
|
if (money >= maxNum) return '超出最大处理数字'
|
|
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
|
}
|
|
getTotal = (data) => {
|
let price = 0
|
let tax = 0
|
|
data.forEach(item => {
|
if (!item.productcode) return
|
|
price += item.amount_line
|
tax += item.tax_amount
|
})
|
|
this.setState({total: {price, tax, sum: price + tax, sumName: this.changeMoneyToChinese(price + tax)}})
|
}
|
|
resetDetails = (data) => {
|
let _data = fromJS(data).toJS()
|
|
if (!_data.length) {
|
_data = [{uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}]
|
}
|
|
this.setState({data: _data}, () => {
|
this.getTotal(_data)
|
})
|
}
|
|
addLine = () => {
|
const { data } = this.state
|
|
let line = {uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}
|
|
this.setState({data: [...data, line]})
|
}
|
|
delLine = () => {
|
const { editKey, data } = this.state
|
|
if (data.length === 1) {
|
notification.warning({
|
top: 92,
|
message: '至少保留一行明细!',
|
duration: 3
|
})
|
return
|
}
|
|
let _data = data.filter(item => item.uuid !== editKey)
|
|
this.setState({data: _data}, () => {
|
this.getTotal(_data)
|
})
|
this.props.onChange(_data)
|
}
|
|
changeLine = (record) => {
|
const { editKey, data } = this.state
|
|
let _data = data.map(item => {
|
if (item.uuid === editKey) {
|
return record
|
} else {
|
return item
|
}
|
})
|
|
|
this.setState({data: _data}, () => {
|
this.getTotal(_data)
|
})
|
this.props.onChange(_data)
|
}
|
|
checkLine = (uuid) => {
|
this.setState({editKey: uuid})
|
}
|
|
changeDetail = (prod) => {
|
const { editKey, data } = this.state
|
|
let _data = data.map(item => {
|
if (item.uuid === editKey) {
|
item.productname = prod.productname
|
item.spec = prod.spec
|
item.unit = prod.unit
|
item.unitprice = prod.unitprice
|
item.tax_rate = prod.tax_rate
|
item.tax_name = prod.tax_rate
|
|
item.productcode = prod.productcode
|
item.Description = prod.Description
|
item.tax_classify_code = prod.tax_classify_code
|
item.tax_classify_name = prod.tax_classify_name
|
|
if (item.bill_count && item.unitprice) {
|
item.amount_line = Math.round(item.unitprice * item.bill_count * 100) / 100
|
}
|
// item.tax_amount = prod.productname
|
}
|
|
return item
|
})
|
|
this.setState({data: _data, editKey: '', visible: false}, () => {
|
this.setState({editKey: editKey})
|
this.getTotal(_data)
|
})
|
this.props.onChange(_data)
|
}
|
|
render() {
|
const { config } = this.props
|
const { editKey, data, total, visible } = this.state
|
|
return (
|
<div className="detail-wrap">
|
<span className="plus-line" onClick={this.addLine}></span>
|
<div className="mk-th">
|
<div className="mk-td">货物或应税劳务、服务名称</div>
|
<div className="mk-td">规格型号</div>
|
<div className="mk-td">单位</div>
|
<div className="mk-td">数量</div>
|
<div className="mk-td">单价(含税)</div>
|
<div className="mk-td">金额(含税)</div>
|
<div className="mk-td">税率</div>
|
<div className="mk-td">税额</div>
|
</div>
|
{data.map(item => {
|
if (editKey === item.uuid) {
|
return <DetailLine key={item.uuid} line={item} changeLine={this.changeLine} delLine={this.delLine} trigger={() => this.setState({visible: true})}/>
|
}
|
|
return <div className="mk-tr" key={item.uuid} onClick={() => this.checkLine(item.uuid)}>
|
<div className="mk-td mk-left">{item.productname || '**'}</div>
|
<div className="mk-td mk-left">{item.spec || ''}</div>
|
<div className="mk-td mk-left">{item.unit || ''}</div>
|
<div className="mk-td mk-right">{item.bill_count || ''}</div>
|
<div className="mk-td mk-right">{item.unitprice || ''}</div>
|
<div className="mk-td mk-right">{item.amount_line || ''}</div>
|
<div className="mk-td mk-right">{item.tax_name}</div>
|
<div className="mk-td mk-right">{item.tax_amount}</div>
|
</div>
|
})}
|
<div className="mk-total">
|
<div className="mk-td">合计</div>
|
<div className="mk-td"></div>
|
<div className="mk-td"></div>
|
<div className="mk-td"></div>
|
<div className="mk-td"></div>
|
<div className="mk-td">¥{total.price}</div>
|
<div className="mk-td"></div>
|
<div className="mk-td">¥{total.tax}</div>
|
</div>
|
<div className="mk-upcase">
|
<div className="mk-td">价税合计(大写)</div>
|
<div className="mk-td">{total.sumName}</div>
|
<div className="mk-td">(小写)¥{total.sum}</div>
|
</div>
|
<Modal
|
title="商品信息"
|
visible={visible}
|
width="75vw"
|
maskClosable={false}
|
onCancel={() => { this.setState({ visible: false }) }}
|
footer={null}
|
>
|
<SubTable config={config} onChange={this.changeDetail}/>
|
</Modal>
|
</div>
|
)
|
}
|
}
|
|
export default InvoiceTable
|