| | |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-color: #ffffff; |
| | | background-position: center center; |
| | |
| | | } else if (item.key === 'formula') { |
| | | item.fields = [] |
| | | |
| | | config.columns.forEach(col => { |
| | | item.fields.push(col.field) |
| | | }) |
| | | |
| | | if (config.subColumns) { |
| | | config.subColumns.forEach(col => { |
| | | if (side === 'sub') { |
| | | if (config.subColumns) { |
| | | config.subColumns.forEach(col => { |
| | | item.fields.push(col.field) |
| | | }) |
| | | } |
| | | } else { |
| | | config.columns.forEach(col => { |
| | | item.fields.push(col.field) |
| | | }) |
| | | } |
| | |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-color: #ffffff; |
| | | background-position: center center; |
| | |
| | | } |
| | | |
| | | .card-item, .card-item-wrap { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-color: #ffffff; |
| | | background-position: center center; |
| | |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-color: #ffffff; |
| | | background-position: center center; |
| | |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-position: center center; |
| | | background-repeat: no-repeat; |
| | |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-position: center center; |
| | | background-repeat: no-repeat; |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Popover, Button } from 'antd' |
| | | import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons' |
| | | import moment from 'moment' |
| | | |
| | | import Utils from '@/utils/utils.js' |
| | | import asyncIconComponent from '@/utils/asyncIconComponent' |
| | | import { getTables, checkComponent } from '@/utils/utils-custom.js' |
| | | import MKEmitter from '@/utils/events.js' |
| | | import getWrapForm from './options' |
| | | |
| | | import './index.scss' |
| | | |
| | | const NormalForm = asyncIconComponent(() => import('@/components/normalform')) |
| | | const SettingComponent = asyncIconComponent(() => import('@/menu/datasource')) |
| | | |
| | | class Invoice extends Component { |
| | | static propTpyes = { |
| | | card: PropTypes.object, |
| | | deletecomponent: PropTypes.func, |
| | | updateConfig: PropTypes.func, |
| | | } |
| | | |
| | | state = { |
| | | card: null, |
| | | date: moment().format('YYYY年MM月') |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { card } = this.props |
| | | |
| | | if (card.isNew) { |
| | | let _card = { |
| | | uuid: card.uuid, |
| | | type: card.type, |
| | | format: 'object', // 组件属性 - 数据格式 |
| | | pageable: false, // 组件属性 - 是否可分页 |
| | | switchable: false, // 组件属性 - 数据是否可切换 |
| | | width: card.width || 24, |
| | | name: '发票', |
| | | subtype: card.subtype, |
| | | wrap: { name: '发票', width: card.width || 24, datatype: 'static' }, |
| | | style: { paddingLeft: '20px', paddingRight: '20px', paddingTop: '10px', paddingBottom: '10px' }, |
| | | setting: { interType: 'system' }, |
| | | columns: [], |
| | | scripts: [], |
| | | buyer: { |
| | | pageable: true, |
| | | format: 'array', |
| | | subtype: 'invTable', |
| | | setting: { interType: 'system' }, |
| | | columns: [], |
| | | scripts: [], |
| | | search: [ |
| | | {field: 'from_to_name', label: '企业名称', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()}, |
| | | {field: 'from_to_tax_no', label: '企业税号', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()}, |
| | | ], |
| | | }, |
| | | detail: { |
| | | pageable: true, |
| | | format: 'array', |
| | | subtype: 'invTable', |
| | | setting: { interType: 'system' }, |
| | | columns: [], |
| | | scripts: [], |
| | | search: [ |
| | | {field: 'productname', label: '商品名称', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()}, |
| | | {field: 'productcode', label: '商品编码', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()}, |
| | | ], |
| | | } |
| | | } |
| | | |
| | | let buys = [ |
| | | ['企业名称', 'from_to_name'], |
| | | ['企业税号', 'from_to_tax_no'], |
| | | ['地址', 'from_to_addr', 'Nvarchar(100)'], |
| | | ['电话', 'from_to_tel'], |
| | | ['开户行', 'from_to_bank_name'], |
| | | ['银行账号', 'from_to_account_no'], |
| | | ['手机号', 'from_to_mob'], |
| | | ['邮箱', 'from_to_email'], |
| | | ['企业编码', 'from_to_code'] |
| | | ] |
| | | |
| | | buys.forEach((cell, index) => { |
| | | _card.buyer.columns.push({$index: index + 1, datatype: 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()}) |
| | | }) |
| | | |
| | | let details = [ |
| | | ['id', 'id'], |
| | | ['商品编码', 'productcode'], |
| | | ['商品名称', 'productname'], |
| | | ['规格型号', 'spec'], |
| | | ['描述', 'Description'], |
| | | ['计量单位', 'unit'], |
| | | ['单价', 'unitprice', 'Decimal(18,10)'], |
| | | ['税务分类编码', 'tax_classify_code'], |
| | | ['税务分类名称', 'tax_classify_name'], |
| | | ['税率', 'tax_rate'], |
| | | ] |
| | | |
| | | details.forEach((cell, index) => { |
| | | _card.detail.columns.push({$index: index + 1, datatype: cell[2] || 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()}) |
| | | }) |
| | | |
| | | let cols = [ |
| | | ['发票种类', 'invoice_type'], |
| | | ['购买方名称', 'from_to_name'], |
| | | ['购买方税号', 'from_to_tax_no'], |
| | | ['购买方地址', 'from_to_addr', 'Nvarchar(100)'], |
| | | ['购买方电话', 'from_to_tel'], |
| | | ['购买方开户行', 'from_to_bank_name'], |
| | | ['购买方银行账号', 'from_to_account_no'], |
| | | ['购买方手机号', 'from_to_mob'], |
| | | ['购买方邮箱', 'from_to_email'], |
| | | ['购买方编码', 'from_to_code'], |
| | | ['销售方名称', 'orgname'], |
| | | ['销售方税号', 'tax_no'], |
| | | ['销售方地址', 'addr', 'Nvarchar(100)'], |
| | | ['销售方电话', 'tel'], |
| | | ['销售方开户行', 'bank_name'], |
| | | ['销售方银行账号', 'account_no'], |
| | | ['备注', 'remark', 'Nvarchar(512)'], |
| | | ['收款人', 'payee'], |
| | | ['复核人', 'reviewer'], |
| | | ['开票人', 'drawer'], |
| | | ['商品编码', 'productcode'], |
| | | ['商品名称', 'productname'], |
| | | ['规格型号', 'spec'], |
| | | ['计量单位', 'unit'], |
| | | ['数量', 'bill_count', 'Decimal(18,10)'], |
| | | ['单价', 'unitprice', 'Decimal(18,10)'], |
| | | ['金额', 'amount_line', 'Decimal(18,2)'], |
| | | ['税务分类编码', 'tax_classify_code'], |
| | | ['税务分类名称', 'tax_classify_name'], |
| | | ['税率', 'tax_rate', 'Decimal(18,2)'], |
| | | ['税额', 'tax_amount', 'Decimal(18,2)'], |
| | | ] |
| | | |
| | | cols.forEach((cell, index) => { |
| | | _card.columns.push({$index: index + 1, datatype: cell[2] || 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()}) |
| | | }) |
| | | |
| | | this.updateComponent(_card) |
| | | } else { |
| | | let _card = fromJS(card).toJS() |
| | | |
| | | // _card.buyer.format = 'array' |
| | | // _card.detail.format = 'array' |
| | | |
| | | this.setState({ |
| | | card: _card |
| | | }) |
| | | } |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新,清除快捷键设置 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @description 卡片行外层信息更新(数据源,样式等) |
| | | */ |
| | | updateComponent = (card) => { |
| | | card.width = card.wrap.width |
| | | card.name = card.wrap.name |
| | | |
| | | if (window.GLOB.styling && card.errors) { // 样式修改时不做筛查 |
| | | this.setState({ |
| | | card: card |
| | | }) |
| | | |
| | | this.props.updateConfig(card) |
| | | return |
| | | } |
| | | |
| | | card.$c_ds = card.wrap.datatype === 'dynamic' |
| | | card.errors = checkComponent(card) |
| | | |
| | | if (card.errors.length === 0) { |
| | | if (card.buyer.setting.interType === 'system' && card.buyer.setting.execute !== 'false' && !card.buyer.setting.dataresource) { |
| | | card.errors.push({ level: 0, detail: '-购买方 未设置数据源!'}) |
| | | } else if (card.buyer.setting.interType === 'system' && card.buyer.setting.execute === 'false' && card.buyer.scripts.filter(script => script.status !== 'false').length === 0) { |
| | | card.errors.push({ level: 0, detail: '-购买方 数据源中无可用脚本!'}) |
| | | } |
| | | } |
| | | if (card.errors.length === 0) { |
| | | if (card.detail.setting.interType === 'system' && card.detail.setting.execute !== 'false' && !card.detail.setting.dataresource) { |
| | | card.errors.push({ level: 0, detail: '-明细 未设置数据源!'}) |
| | | } else if (card.detail.setting.interType === 'system' && card.detail.setting.execute === 'false' && card.detail.scripts.filter(script => script.status !== 'false').length === 0) { |
| | | card.errors.push({ level: 0, detail: '-明细 数据源中无可用脚本!'}) |
| | | } |
| | | } |
| | | |
| | | if (card.errors.length === 0) { |
| | | card.$tables = getTables(card) |
| | | card.$tables = [...card.$tables, ...getTables(card.buyer)] |
| | | card.$tables = [...card.$tables, ...getTables(card.detail)] |
| | | } |
| | | |
| | | this.setState({ |
| | | card: card |
| | | }) |
| | | |
| | | this.props.updateConfig(card) |
| | | } |
| | | |
| | | changeStyle = () => { |
| | | const { card } = this.state |
| | | |
| | | MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle) |
| | | } |
| | | |
| | | getStyle = (style) => { |
| | | let _card = {...this.state.card, style} |
| | | |
| | | this.updateComponent(_card) |
| | | } |
| | | |
| | | getWrapForms = () => { |
| | | const { card } = this.state |
| | | |
| | | return getWrapForm(card.wrap) |
| | | } |
| | | |
| | | updateWrap = (res) => { |
| | | this.updateComponent({...this.state.card, wrap: res}) |
| | | } |
| | | |
| | | render() { |
| | | const { card, date } = this.state |
| | | |
| | | let style = {...card.style} |
| | | if (card.wrap.invColor) { |
| | | style['--inv-color'] = card.wrap.invColor |
| | | } |
| | | |
| | | return ( |
| | | <div className="menu-invoice-box" style={style} id={card.uuid}> |
| | | <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ |
| | | <div className="mk-popover-control"> |
| | | <NormalForm title="基本设置" width={800} update={this.updateWrap} getForms={this.getWrapForms}> |
| | | <EditOutlined style={{color: '#1890ff'}} title="编辑"/> |
| | | </NormalForm> |
| | | <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle}/> |
| | | <DeleteOutlined className="close" title="删除组件" onClick={() => this.props.deletecomponent(card.uuid)} /> |
| | | {card.wrap.datatype === 'dynamic' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : <SettingOutlined style={{color: '#eeeeee', cursor: 'not-allowed'}}/>} |
| | | </div> |
| | | } trigger="hover"> |
| | | <ToolOutlined /> |
| | | </Popover> |
| | | <div className="inv-action"> |
| | | <Button className="mk-bill">保存单据</Button> |
| | | <Button className="mk-submit">提交开票</Button> |
| | | </div> |
| | | <div className="inv-header"> |
| | | <div className="mk-select">请选择发票种类</div> |
| | | <div className="date">开票日期:{date}</div> |
| | | </div> |
| | | <div className="inv-body"> |
| | | <div className="inv-main-content"> |
| | | <div className="inv-buyer"> |
| | | <div className="inv-label">购买方</div> |
| | | <div className="inv-content"> |
| | | <div className="mk-input"> |
| | | <label>名称:</label> |
| | | <span className="content">请输入购买方名称</span> |
| | | <span className="extra"> |
| | | <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ |
| | | <div className="mk-popover-control"> |
| | | <SettingComponent config={card.buyer} updateConfig={(res) => this.updateComponent({...card, buyer: res})} /> |
| | | </div> |
| | | } trigger="hover"> |
| | | <EllipsisOutlined /> |
| | | </Popover> |
| | | </span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>纳税人识别号:</label> |
| | | <span className="content">请输入购买方纳税人识别号</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>地址、电话:</label> |
| | | <span className="content">请输入购买方地址</span> |
| | | <span className="content">请输入购买方电话</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>开户行及账号:</label> |
| | | <span className="content">请输入购买方开户行</span> |
| | | <span className="content">请输入购买方账号</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-notice"> |
| | | <div className="inv-label">通知到</div> |
| | | <div className="inv-content"> |
| | | <div className="mk-input"> |
| | | <label>手机号:</label> |
| | | <span className="content">请输入购买方手机号</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>邮箱:</label> |
| | | <span className="content">请输入购买方邮箱</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-details"> |
| | | <div className="detail-wrap"> |
| | | <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> |
| | | <div className="mk-tr"> |
| | | <div className="mk-td mk-left"> |
| | | <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ |
| | | <div className="mk-popover-control"> |
| | | <SettingComponent config={card.detail} updateConfig={(res) => this.updateComponent({...card, detail: res})} /> |
| | | </div> |
| | | } trigger="hover"> |
| | | <EllipsisOutlined /> |
| | | </Popover> |
| | | </div> |
| | | <div className="mk-td mk-left"></div> |
| | | <div className="mk-td mk-left"></div> |
| | | <div className="mk-td mk-right"></div> |
| | | <div className="mk-td mk-right"></div> |
| | | <div className="mk-td mk-right"></div> |
| | | <div className="mk-td mk-right"></div> |
| | | <div className="mk-td mk-right"></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">¥12</div> |
| | | <div className="mk-td"></div> |
| | | <div className="mk-td">¥1</div> |
| | | </div> |
| | | <div className="mk-upcase"> |
| | | <div className="mk-td">价税合计(大写)</div> |
| | | <div className="mk-td"></div> |
| | | <div className="mk-td">(小写)¥12</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-main-content"> |
| | | <div className="inv-buyer"> |
| | | <div className="inv-label">销售方</div> |
| | | <div className="inv-content"> |
| | | <div className="mk-input"> |
| | | <label>名称:</label> |
| | | <span className="content">请输入销售方名称</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>纳税人识别号:</label> |
| | | <span className="content">请输入销售方纳税人识别号</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>地址、电话:</label> |
| | | <span className="content">请输入销售方地址</span> |
| | | <span className="content">请输入销售方电话</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>开户行及账号:</label> |
| | | <span className="content">请输入销售方开户行</span> |
| | | <span className="content">请输入销售方账号</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-notice"> |
| | | <div className="inv-label">备注</div> |
| | | <div className="inv-content" style={{paddingTop: '30px'}}> |
| | | <div className="mk-input"> |
| | | <span className="content" style={{height: '80px'}}>请输入备注</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-tail"> |
| | | <div className="mk-input"> |
| | | <label>收款人:</label> |
| | | <span className="content">收款人</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>复核人:</label> |
| | | <span className="content">复核人</span> |
| | | </div> |
| | | <div className="mk-input"> |
| | | <label>开票人:</label> |
| | | <span className="content">开票人</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default Invoice |
New file |
| | |
| | | .menu-invoice-box { |
| | | position: relative; |
| | | box-sizing: border-box; |
| | | background: #ffffff; |
| | | background-position: center center; |
| | | background-repeat: no-repeat; |
| | | background-size: cover; |
| | | color: #000000; |
| | | --inv-color: #13509c; |
| | | |
| | | .anticon-tool { |
| | | position: absolute; |
| | | z-index: 2; |
| | | font-size: 16px; |
| | | right: 1px; |
| | | top: 1px; |
| | | cursor: pointer; |
| | | padding: 5px; |
| | | background: rgba(255, 255, 255, 0.55); |
| | | } |
| | | |
| | | .inv-action { |
| | | text-align: right; |
| | | margin-right: 30px; |
| | | .ant-btn { |
| | | margin-left: 15px; |
| | | margin-bottom: 5px; |
| | | height: 30px; |
| | | } |
| | | .mk-bill:hover, .mk-bill:active, .mk-bill:focus { |
| | | color: var(--mk-sys-color); |
| | | border-color: var(--mk-sys-color); |
| | | } |
| | | .mk-submit, .mk-submit:hover, .mk-submit:active, .mk-submit:focus { |
| | | color: #fff; |
| | | background-color: var(--mk-sys-color); |
| | | border-color: var(--mk-sys-color); |
| | | } |
| | | } |
| | | .inv-header { |
| | | text-align: center; |
| | | position: relative; |
| | | height: 70px; |
| | | margin-right: 30px; |
| | | .mk-select { |
| | | position: relative; |
| | | display: inline-block; |
| | | width: 390px; |
| | | font-size: 25px; |
| | | text-align: center; |
| | | font-family: kaiti; |
| | | color: var(--inv-color, #13509c); |
| | | } |
| | | .mk-select::before, .mk-select::after { |
| | | content: ''; |
| | | display: block; |
| | | width: 100%; |
| | | position: absolute; |
| | | border-top: var(--inv-color, #13509c) 1px solid; |
| | | border-bottom: var(--inv-color, #13509c) 1px solid; |
| | | height: 1px; |
| | | } |
| | | .mk-select::before { |
| | | bottom: -10px; |
| | | } |
| | | .mk-select::after { |
| | | bottom: -15px; |
| | | } |
| | | .date { |
| | | position: absolute; |
| | | right: 100px; |
| | | top: 5px; |
| | | color: var(--inv-color, #13509c); |
| | | } |
| | | } |
| | | |
| | | .mk-input { |
| | | position: relative; |
| | | height: 28px; |
| | | font-size: 13px; |
| | | background: transparent; |
| | | display: flex; |
| | | padding: 0 5px 0 15px; |
| | | margin-bottom: 5px; |
| | | label { |
| | | display: inline-block; |
| | | height: 28px; |
| | | line-height: 28px; |
| | | text-align: right; |
| | | color: var(--inv-color, #13509c); |
| | | } |
| | | span { |
| | | display: inline-block; |
| | | height: 28px; |
| | | line-height: 28px; |
| | | } |
| | | .extra { |
| | | position: absolute; |
| | | right: 0px; |
| | | .anticon-ellipsis { |
| | | padding: 0 15px; |
| | | } |
| | | } |
| | | .content { |
| | | flex: 1; |
| | | border-bottom: 1px solid #d9d9d9; |
| | | color: #b8b8b8; |
| | | transition: all 0.3s; |
| | | padding: 0px 10px; |
| | | } |
| | | } |
| | | .mk-input:hover .content { |
| | | border-color: var(--inv-color, #13509c); |
| | | } |
| | | |
| | | .inv-body { |
| | | border: var(--inv-color, #13509c) 1px solid; |
| | | font-size: 13px; |
| | | margin-right: 30px; |
| | | |
| | | .inv-main-content { |
| | | display: flex; |
| | | |
| | | .inv-buyer, .inv-notice { |
| | | width: 50%; |
| | | display: flex; |
| | | .inv-label { |
| | | color: var(--inv-color, #13509c); |
| | | width: 6.25%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | writing-mode: vertical-rl; |
| | | justify-content: center; |
| | | align-items: center; |
| | | letter-spacing: 5px; |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | } |
| | | .inv-content { |
| | | flex: 1; |
| | | padding: 8px 0; |
| | | } |
| | | } |
| | | .inv-buyer { |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | label { |
| | | width: 95px; |
| | | min-width: 95px; |
| | | } |
| | | } |
| | | .inv-notice { |
| | | .inv-content { |
| | | padding-top: 45px; |
| | | } |
| | | label { |
| | | width: 75px; |
| | | min-width: 75px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .inv-details { |
| | | border-top: var(--inv-color, #13509c) 1px solid; |
| | | border-bottom: var(--inv-color, #13509c) 1px solid; |
| | | } |
| | | } |
| | | .inv-tail { |
| | | display: flex; |
| | | padding-top: 10px; |
| | | margin-right: 30px; |
| | | .mk-input { |
| | | display: flex; |
| | | flex: 1; |
| | | label { |
| | | width: 40%; |
| | | color: rgba(0, 0, 0, 0.85); |
| | | } |
| | | span { |
| | | width: 40%; |
| | | flex: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .detail-wrap { |
| | | position: relative; |
| | | |
| | | .mk-th, .mk-tr, .mk-total { |
| | | position: relative; |
| | | display: flex; |
| | | border-bottom: var(--inv-color, #13509c) 1px solid; |
| | | |
| | | .mk-td { |
| | | width: 10%; |
| | | height: 40px; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding: 0 3px; |
| | | position: relative; |
| | | } |
| | | .mk-td:not(:last-child) { |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | } |
| | | .mk-td:first-child { |
| | | width: 20%; |
| | | } |
| | | .mk-td:nth-child(2), .mk-td:nth-child(6) { |
| | | width: 15%; |
| | | } |
| | | |
| | | .mk-left { |
| | | justify-content: start; |
| | | } |
| | | .mk-right { |
| | | justify-content: end; |
| | | } |
| | | .anticon-ellipsis { |
| | | padding: 0 10px; |
| | | position: absolute; |
| | | right: 0; |
| | | } |
| | | } |
| | | .mk-tr.active, .mk-tr:hover { |
| | | background: var(--mk-sys-color1); |
| | | } |
| | | .mk-upcase { |
| | | display: flex; |
| | | |
| | | .mk-td { |
| | | width: 40%; |
| | | height: 40px; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 20px; |
| | | } |
| | | .mk-td:first-child { |
| | | width: 20%; |
| | | padding: 0; |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | justify-content: center; |
| | | } |
| | | .mk-td:last-child { |
| | | justify-content: end; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .menu-invoice-box::after { |
| | | display: block; |
| | | content: ' '; |
| | | clear: both; |
| | | } |
| | | .menu-invoice-box:hover { |
| | | z-index: 1; |
| | | box-shadow: 0px 0px 4px #1890ff; |
| | | } |
| | | |
| | | |
New file |
| | |
| | | /** |
| | | * @description Wrap表单配置信息 |
| | | */ |
| | | export default function (wrap) { |
| | | let menu = window.GLOB.customMenu |
| | | let books = [] |
| | | let bookids = [] |
| | | menu.components.forEach(item => { |
| | | if (item.subtype === 'account') { |
| | | books.push({ |
| | | value: item.uuid, |
| | | label: item.name |
| | | }) |
| | | bookids.push(item.uuid) |
| | | } |
| | | }) |
| | | |
| | | const wrapForm = [ |
| | | { |
| | | type: 'radio', |
| | | field: 'datatype', |
| | | label: '类型', |
| | | initval: wrap.datatype || 'static', |
| | | required: true, |
| | | options: [ |
| | | {value: 'static', label: '新增发票'}, |
| | | {value: 'dynamic', label: '查看单据'}, |
| | | ] |
| | | }, |
| | | { |
| | | type: 'text', |
| | | field: 'name', |
| | | label: '组件名称', |
| | | initval: wrap.name || '', |
| | | tooltip: '用于组件间的区分。', |
| | | required: true |
| | | }, |
| | | { |
| | | type: 'number', |
| | | field: 'width', |
| | | label: '宽度', |
| | | initval: wrap.width || 24, |
| | | tooltip: '栅格布局,每行等分为24列。', |
| | | min: 1, |
| | | max: 24, |
| | | precision: 0, |
| | | required: true |
| | | }, |
| | | { |
| | | type: 'select', |
| | | field: 'supBook', |
| | | label: '账套', |
| | | initval: wrap.supBook || '', |
| | | required: true, |
| | | options: books, |
| | | allowClear: true |
| | | }, |
| | | { |
| | | type: 'radio', |
| | | field: 'business_type', |
| | | label: '发票类型', |
| | | initval: wrap.business_type || 'sell', |
| | | required: true, |
| | | options: [ |
| | | {value: 'sell', label: '销项发票'}, |
| | | {value: 'buy', label: '进项发票'}, |
| | | ] |
| | | }, |
| | | { |
| | | type: 'color', |
| | | field: 'invColor', |
| | | label: '样式', |
| | | initval: wrap.invColor || '', |
| | | tooltip: '发票组件中边框以及文字的颜色。', |
| | | allowClear: true, |
| | | required: false |
| | | }, |
| | | ] |
| | | |
| | | return wrapForm |
| | | } |
| | |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | // overflow: hidden; |
| | | position: relative; |
| | | background-color: #ffffff; |
| | | background-position: center center; |
| | |
| | | if (config.subtype === 'dualdatacard') { |
| | | _columns = [...columns, ...subColumns] |
| | | } |
| | | let r = SettingUtils.getDebugSql(setting, _scripts, _columns, searches, config.type) |
| | | |
| | | let r = SettingUtils.getDebugSql(setting, _scripts, _columns, searches, config.subtype) |
| | | |
| | | let _debugId = md5(r.sql) |
| | | |
| | |
| | | } |
| | | |
| | | copyColumns = () => { |
| | | const { columns } = this.state |
| | | const { columns, setting } = this.state |
| | | |
| | | let m = [] |
| | | let n = [] |
| | | let s = [] |
| | |
| | | } |
| | | |
| | | let oInput = document.createElement('input') |
| | | oInput.value = `/*${m.join(',')}*/ |
| | | ${n.join(',')}` |
| | | oInput.value = `create table #${setting.tableName || 'tb'} |
| | | (${m.join(',')},sort_id INT IDENTITY(1,1)) |
| | | insert into #${setting.tableName || 'tb'} |
| | | (${n.join(',')}) |
| | | select ${n.join(',')} |
| | | from ${setting.dataresource ? `(${setting.dataresource}) tb` : setting.tableName || 'tb'} |
| | | order by @orderBy@ |
| | | |
| | | declare @mk_total int |
| | | set @mk_total = 0 |
| | | |
| | | select @mk_total = count(1) from #${setting.tableName || 'tb'} |
| | | declare @pageIndex_top int |
| | | set @pageIndex_top=(@pageIndex@-1)*@pageSize@ |
| | | |
| | | if @mk_total > @pageIndex@*@pageSize@ |
| | | delete #${setting.tableName || 'tb'} where sort_id > @pageIndex@*@pageSize@ |
| | | |
| | | if @pageIndex_top > 0 |
| | | delete #${setting.tableName || 'tb'} where sort_id <= @pageIndex_top` |
| | | |
| | | document.body.appendChild(oInput) |
| | | oInput.select() |
| | | document.execCommand('Copy') |
| | |
| | | wrappedComponentRef={(inst) => this.settingForm = inst} |
| | | /> : null} |
| | | </TabPane> |
| | | {config.subtype !== 'basetable' ? <TabPane tab={ |
| | | {!['basetable', 'invoice', 'invTable'].includes(config.subtype) ? <TabPane tab={ |
| | | <span> |
| | | 字段集 |
| | | {columns.length ? <span className="count-tip">{columns.length}</span> : null} |
| | |
| | | </Form.Item> |
| | | </Col> : null} |
| | | {setting.interType === 'system' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}> |
| | | {window.GLOB.process ? <span className="process-btn" onClick={this.addProcess}>工作流</span> : null} |
| | | {window.GLOB.process && !['invoice', 'invTable'].includes(config.subtype) ? <span className="process-btn" onClick={this.addProcess}>工作流</span> : null} |
| | | <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 2 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 22 }} } label={ |
| | | <Tooltip placement="topLeft" title={`使用系统函数时,需填写数据源。注:数据权限替换符 $@ -> /* 或 ''、 @$ -> */ 或 ''。`}> |
| | | <QuestionCircleOutlined className="mk-form-tip" /> |
| | |
| | | </Radio.Group>)} |
| | | </Form.Item> |
| | | </Col> : null} |
| | | <Col span={8}> |
| | | {!['invTable'].includes(config.subtype) ? <Col span={8}> |
| | | <Form.Item label="主键"> |
| | | {getFieldDecorator('primaryKey', { |
| | | initialValue: setting.primaryKey || '' |
| | |
| | | </Select> |
| | | )} |
| | | </Form.Item> |
| | | </Col> |
| | | </Col> : null} |
| | | {/* 数组数据,需设置排序规则 */} |
| | | {config.format === 'array' ? <Col span={8}> |
| | | <Form.Item label={ |
| | |
| | | </Radio.Group>)} |
| | | </Form.Item> |
| | | </Col> : null} |
| | | {!['navbar', 'balcony', 'menubar'].includes(config.type) && (!config.wrap || config.wrap.supType !== 'multi') && MenuType !== 'billPrint' ? <Col span={8}> |
| | | {!['balcony', 'menubar', 'commonbar', 'tabbar', 'invTable'].includes(config.subtype) && (!config.wrap || config.wrap.supType !== 'multi') && MenuType !== 'billPrint' ? <Col span={8}> |
| | | <Form.Item label={ |
| | | <Tooltip placement="topLeft" title={'该组件如果受其他组件控制,请选项相应的组件,没有时选“无”。'}> |
| | | <QuestionCircleOutlined className="mk-form-tip" /> |
| | |
| | | </Form.Item> |
| | | </Col> : null} |
| | | {/* 1、不分页且不存在上级模块 */} |
| | | {!['navbar', 'interface', 'calendar'].includes(config.type) && !['editable', 'basetable', 'dualdatacard'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' && setting.onload !== 'false' ? <Col span={8}> |
| | | {!['navbar', 'interface', 'calendar'].includes(config.type) && !['editable', 'basetable', 'dualdatacard', 'invoice', 'invTable'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' && setting.onload !== 'false' ? <Col span={8}> |
| | | <Form.Item label={ |
| | | <Tooltip placement="topLeft" title={'初始化加载时,是否与其他组件一同加载数据,注:如菜单未使用后端缓存,则查询语句大于8000字符时无效。'}> |
| | | <QuestionCircleOutlined className="mk-form-tip" /> |
| | |
| | | )} |
| | | </Form.Item> |
| | | </Col> : null} |
| | | {!['navbar', 'balcony', 'menubar'].includes(config.type) && !['basetable'].includes(config.subtype) ? <Col span={8}> |
| | | {!['navbar', 'balcony', 'menubar'].includes(config.type) && !['basetable', 'invoice', 'invTable'].includes(config.subtype) ? <Col span={8}> |
| | | <Form.Item label={ |
| | | <Tooltip placement="topLeft" title={config.type === 'interface' ? '单独搜索组件可作为公共数据源的搜索条件。' : '优先使用同级的搜索条件组件,同级搜索不存在时,依次向上选取,与当前组件的搜索条件一同用作数据过滤(当前组件的搜索条件优先)。'}> |
| | | <QuestionCircleOutlined className="mk-form-tip" /> |
| | |
| | | {reg: /@sum\$|\$sum@/ig, value: ''}, |
| | | ] |
| | | |
| | | if (window.GLOB.process) { |
| | | if (window.GLOB.process && type !== 'invoice') { |
| | | regs.push({reg: /@works_flow_code@/ig, value: `'${getuuid()}'`}) |
| | | } |
| | | |
| | |
| | | ` |
| | | } |
| | | |
| | | if (window.GLOB.urlFields) { |
| | | if (window.GLOB.urlFields && type !== 'invoice') { |
| | | window.GLOB.urlFields.forEach(field => { |
| | | let reg = new RegExp('@' + field + '@', 'ig') |
| | | _dataresource = _dataresource.replace(reg, `'0'`) |
| | |
| | | ` |
| | | } |
| | | |
| | | sql = `create table #${sheet} (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) ) |
| | | sql = `create table #${sheet} (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50)) |
| | | Declare @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@mk_departmentcode nvarchar(512),@mk_organization nvarchar(512),@mk_user_type nvarchar(20),@mk_nation nvarchar(50),@mk_province nvarchar(50),@mk_city nvarchar(50),@mk_district nvarchar(50),@mk_address nvarchar(100),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512) |
| | | |
| | | Select @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @mk_departmentcode='${departmentcode}', @mk_organization='${organization}', @mk_user_type='${mk_user_type}', @mk_nation='${nation}', @mk_province='${province}', @mk_city='${city}', @mk_district='${district}', @mk_address='${address}' |
| | |
| | | const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline')) |
| | | const Voucher = asyncComponent(() => import('@/menu/components/module/voucher')) |
| | | const Account = asyncComponent(() => import('@/menu/components/module/account')) |
| | | const Invoice = asyncComponent(() => import('@/menu/components/module/invoice')) |
| | | const Iframe = asyncComponent(() => import('@/menu/components/iframe')) |
| | | const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6')) |
| | | const AntvX6 = asyncComponent(() => import('@/menu/components/chart/antv-X6')) |
| | |
| | | return (<Voucher card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } else if (card.type === 'module' && card.subtype === 'account') { |
| | | return (<Account card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } else if (card.type === 'module' && card.subtype === 'invoice') { |
| | | return (<Invoice card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } |
| | | } |
| | | |
| | |
| | | import Voucher from '@/assets/mobimg/voucher.png' |
| | | import Account from '@/assets/mobimg/account.png' |
| | | import canlendar from '@/assets/mobimg/canlendar.png' |
| | | import Invoice from '@/assets/img/invoice.png' |
| | | |
| | | // 组件配置信息 |
| | | export const menuOptions = [ |
| | |
| | | { type: 'menu', url: Iframe, component: 'iframe', subtype: 'iframe', title: 'iframe', width: 24, forbid: ['billPrint'] }, |
| | | { type: 'menu', url: Account, component: 'module', subtype: 'account', title: '账套', width: 24, forbid: ['billPrint'] }, |
| | | { type: 'menu', url: Voucher, component: 'module', subtype: 'voucher', title: '凭证', width: 24, forbid: ['billPrint'] }, |
| | | { type: 'menu', url: Invoice, component: 'module', subtype: 'invoice', title: '发票', width: 24, forbid: ['billPrint'] }, |
| | | ] |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Select, Form, Input, Button, Modal } from 'antd' |
| | | import { EllipsisOutlined } from '@ant-design/icons' |
| | | import moment from 'moment' |
| | | |
| | | // import Api from '@/api' |
| | | // import Utils from '@/utils/utils.js' |
| | | import MKEmitter from '@/utils/events.js' |
| | | import InvoiceTable from './invoiceTable' |
| | | import SubTable from './subTable' |
| | | import './index.scss' |
| | | |
| | | class InvoiceModule extends Component { |
| | | static propTpyes = { |
| | | config: PropTypes.object |
| | | } |
| | | |
| | | state = { |
| | | BID: '', |
| | | invTypes: [ |
| | | {value: '1', label: '电子发票(增值税专用发票)'}, |
| | | {value: '2', label: '电子发票(普通发票)'}, |
| | | {value: '3', label: '增值税纸质专用发票'}, |
| | | {value: '4', label: '增值税纸质普通发票'}, |
| | | {value: '5', label: '增值税电子普通发票'}, |
| | | {value: '6', label: '增值税电子专用发票'}, |
| | | ], |
| | | invoice_type: '', |
| | | date: moment().format('YYYY年MM月DD日'), |
| | | from_to_name: '', |
| | | from_to_tax_no: '', |
| | | from_to_addr: '', |
| | | from_to_tel: '', |
| | | from_to_bank_name: '', |
| | | from_to_account_no: '', |
| | | from_to_mob: '', |
| | | from_to_email: '', |
| | | from_to_code: '', |
| | | orgname: '', |
| | | tax_no: '', |
| | | addr: '', |
| | | tel: '', |
| | | bank_name: '', |
| | | account_no: '', |
| | | remark: '', |
| | | payee: '', |
| | | reviewer: '', |
| | | drawer: '', |
| | | details: [] |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { config } = this.props |
| | | |
| | | let _config = fromJS(config).toJS() |
| | | let BID = '' |
| | | let BData = '' |
| | | |
| | | if (_config.setting.supModule) { |
| | | BData = window.GLOB.CacheData.get(_config.setting.supModule) |
| | | } else { |
| | | BData = window.GLOB.CacheData.get(_config.$pageId) |
| | | } |
| | | if (BData) { |
| | | BID = BData.$BID || '' |
| | | } |
| | | |
| | | if (_config.wrap.invColor) { |
| | | _config.style['--inv-color'] = _config.wrap.invColor |
| | | } |
| | | |
| | | _config.buyer = this.formatSetting(_config.buyer, 'buyer') |
| | | _config.detail = this.formatSetting(_config.detail, 'detail') |
| | | |
| | | this.setState({ |
| | | BID: BID || '', |
| | | config: _config |
| | | }) |
| | | } |
| | | |
| | | componentDidMount () { |
| | | this.loadData() |
| | | MKEmitter.addListener('reloadData', this.reloadData) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新,清除快捷键设置 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | MKEmitter.removeListener('reloadData', this.reloadData) |
| | | } |
| | | |
| | | formatSetting = (item, type) => { |
| | | item.setting.arr_field = item.columns.map(col => col.field).join(',') |
| | | item.setting.laypage = item.setting.laypage === 'true' |
| | | |
| | | if (type === 'buyer') { |
| | | item.columns = item.columns.map(cell => { |
| | | if (['from_to_tel', 'from_to_account_no', 'from_to_code'].includes(cell.field)) { |
| | | cell.Hide = 'true' |
| | | } else if (['from_to_email', 'from_to_mob'].includes(cell.field)) { |
| | | cell.Width = 80 |
| | | } |
| | | return cell |
| | | }) |
| | | } else { |
| | | item.columns = item.columns.map(cell => { |
| | | if (['Description', 'id'].includes(cell.field)) { |
| | | cell.Hide = 'true' |
| | | } else if (['spec'].includes(cell.field)) { |
| | | cell.Width = 150 |
| | | } else if (['unit', 'unitprice', 'tax_rate'].includes(cell.field)) { |
| | | cell.Width = 80 |
| | | } |
| | | return cell |
| | | }) |
| | | } |
| | | |
| | | if (item.setting.interType !== 'system') { |
| | | item.setting.dataresource = '' |
| | | return item |
| | | } |
| | | |
| | | let regs = [ |
| | | { reg: /@userName@/ig, value: `'${sessionStorage.getItem('User_Name') || ''}'` }, |
| | | { reg: /@fullName@/ig, value: `'${sessionStorage.getItem('Full_Name') || ''}'` } |
| | | ] |
| | | |
| | | if (window.GLOB.externalDatabase !== null) { |
| | | regs.push({ |
| | | reg: /@db@/ig, |
| | | value: window.GLOB.externalDatabase |
| | | }) |
| | | } |
| | | |
| | | let _customScript = '' |
| | | let _tailScript = '' |
| | | item.scripts && item.scripts.forEach(script => { |
| | | if (script.status === 'false') return |
| | | if (script.position !== 'back') { |
| | | _customScript += ` |
| | | ${script.sql} |
| | | ` |
| | | } else { |
| | | _tailScript += ` |
| | | ${script.sql} |
| | | ` |
| | | } |
| | | }) |
| | | delete item.scripts |
| | | |
| | | item.setting.execute = item.setting.execute !== 'false' |
| | | |
| | | if (!item.setting.execute) { |
| | | item.setting.dataresource = '' |
| | | } |
| | | if (/\s/.test(item.setting.dataresource)) { |
| | | item.setting.dataresource = '(' + item.setting.dataresource + ') tb' |
| | | } |
| | | |
| | | if (sessionStorage.getItem('dataM') === 'true') { // 数据权限 |
| | | item.setting.dataresource = item.setting.dataresource.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'') |
| | | _customScript = _customScript.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'') |
| | | _tailScript = _tailScript.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'') |
| | | } else { |
| | | item.setting.dataresource = item.setting.dataresource.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'') |
| | | _customScript = _customScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'') |
| | | _tailScript = _tailScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'') |
| | | } |
| | | |
| | | regs.forEach(cell => { |
| | | item.setting.dataresource = item.setting.dataresource.replace(cell.reg, cell.value) |
| | | _customScript = _customScript.replace(cell.reg, cell.value) |
| | | _tailScript = _tailScript.replace(cell.reg, cell.value) |
| | | }) |
| | | |
| | | item.setting.customScript = _customScript // 整理后自定义脚本 |
| | | item.setting.tailScript = _tailScript // 后置自定义脚本 |
| | | |
| | | item.setting.custompage = /@pageSize@|@orderBy@/i.test(item.setting.dataresource + item.setting.customScript) |
| | | |
| | | return item |
| | | } |
| | | |
| | | reloadData = (menuId) => { |
| | | // const { config } = this.props |
| | | // const { activeItem } = this.state |
| | | |
| | | // if (config.uuid !== menuId) return |
| | | |
| | | // if (activeItem) { |
| | | // MKEmitter.emit('resetSelectLine', config.uuid, activeItem.id, activeItem) |
| | | // } else { |
| | | // this.loadData() |
| | | // } |
| | | } |
| | | |
| | | loadData = () => { |
| | | // let param = { |
| | | // func: 's_get_fcc_book_data', |
| | | // dataM: sessionStorage.getItem('dataM') === 'true' ? 'Y' : '', |
| | | // mk_organization: sessionStorage.getItem('organization') || '' |
| | | // } |
| | | |
| | | // Api.genericInterface(param).then(res => { |
| | | // if (!res.status) { |
| | | // notification.warning({ |
| | | // top: 92, |
| | | // message: res.message, |
| | | // duration: 5 |
| | | // }) |
| | | // return |
| | | // } |
| | | |
| | | // let books = res.book || [] |
| | | // let activeItem = null |
| | | // let map = new Map() |
| | | // books = books.filter(item => { |
| | | // if (!item.id) return false |
| | | // if (map.has(item.id)) return false |
| | | // map.set(item.id, true) |
| | | |
| | | // if (item.selected === 'true' && !activeItem) { |
| | | // activeItem = item |
| | | // } |
| | | // if (item.months) { |
| | | // item.date = item.months.replace('-', '年') + '月' |
| | | // } |
| | | // return true |
| | | // }) |
| | | |
| | | // this.setState({books, activeItem}) |
| | | |
| | | // if (activeItem) { |
| | | // MKEmitter.emit('resetSelectLine', this.props.config.uuid, activeItem.id, activeItem) |
| | | // } |
| | | // }) |
| | | } |
| | | |
| | | changeType = (val) => { |
| | | this.setState({invoice_type: val}) |
| | | } |
| | | |
| | | changeBuyer = (item) => { |
| | | this.setState({ |
| | | visible: false, |
| | | from_to_name: item.from_to_name || '', |
| | | from_to_tax_no: item.from_to_tax_no || '', |
| | | from_to_addr: item.from_to_addr || '', |
| | | from_to_tel: item.from_to_tel || '', |
| | | from_to_bank_name: item.from_to_bank_name || '', |
| | | from_to_account_no: item.from_to_account_no || '', |
| | | from_to_mob: item.from_to_mob || '', |
| | | from_to_email: item.from_to_email || '', |
| | | from_to_code: item.from_to_code || '', |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { config, invTypes, date, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee, details, visible } = this.state |
| | | |
| | | return ( |
| | | <div className="menu-invoice-wrap" style={config.style}> |
| | | <div className="inv-action"> |
| | | <Button className="mk-bill">保存单据</Button> |
| | | <Button className="mk-submit">提交开票</Button> |
| | | </div> |
| | | <div className="inv-header"> |
| | | <Select placeholder="请选择发票种类" onChange={this.changeType} dropdownClassName="inv-type-select"> |
| | | {invTypes.map(item => ( |
| | | <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option> |
| | | ))} |
| | | </Select> |
| | | <div className="date">开票日期:{date}</div> |
| | | </div> |
| | | <div className="inv-body"> |
| | | <div className="inv-main-content"> |
| | | <div className="inv-buyer"> |
| | | <div className="inv-label">购买方</div> |
| | | <div className="inv-content"> |
| | | <Form.Item label={<>名<span></span>称</>} extra={<EllipsisOutlined onClick={() => this.setState({visible: true})}/>}> |
| | | <Input placeholder="请输入购买方名称" allowClear value={from_to_name} autoComplete="off" onChange={(e) => this.setState({from_to_name: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item label="纳税人识别号"> |
| | | <Input placeholder="请输入购买方纳税人识别号" allowClear value={from_to_tax_no} autoComplete="off" onChange={(e) => this.setState({from_to_tax_no: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}> |
| | | <Input placeholder="请输入购买方地址" allowClear value={from_to_addr} autoComplete="off" onChange={(e) => this.setState({from_to_addr: e.target.value})}/> |
| | | <Input placeholder="请输入购买方电话" allowClear value={from_to_tel} autoComplete="off" onChange={(e) => this.setState({from_to_tel: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item className="mutil-input" label="开户行及账号"> |
| | | <Input placeholder="请输入购买方开户行" allowClear value={from_to_bank_name} autoComplete="off" onChange={(e) => this.setState({from_to_bank_name: e.target.value})}/> |
| | | <Input placeholder="请输入购买方账号" allowClear value={from_to_account_no} autoComplete="off" onChange={(e) => this.setState({from_to_account_no: e.target.value})}/> |
| | | </Form.Item> |
| | | </div> |
| | | </div> |
| | | <div className="inv-notice"> |
| | | <div className="inv-label">通知到</div> |
| | | <div className="inv-content"> |
| | | <Form.Item label={<>手<span></span>机<span></span>号</>}> |
| | | <Input placeholder="请输入购买方手机号" allowClear value={from_to_mob} autoComplete="off" onChange={(e) => this.setState({from_to_mob: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item label={<>邮<span></span>箱</>}> |
| | | <Input placeholder="请输入购买方邮箱" allowClear value={from_to_email} autoComplete="off" onChange={(e) => this.setState({from_to_email: e.target.value})}/> |
| | | </Form.Item> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-details"> |
| | | <InvoiceTable data={details} config={config.detail} onChange={(details) => this.setState({details})}/> |
| | | </div> |
| | | <div className="inv-main-content"> |
| | | <div className="inv-buyer"> |
| | | <div className="inv-label">销售方</div> |
| | | <div className="inv-content"> |
| | | <Form.Item label={<>名<span></span>称</>}> |
| | | <Input placeholder="请输入销售方名称" value={orgname} autoComplete="off" onChange={(e) => this.setState({orgname: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item label="纳税人识别号"> |
| | | <Input placeholder="请输入销售方纳税人识别号" disabled value={tax_no} autoComplete="off"/> |
| | | </Form.Item> |
| | | <Form.Item className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}> |
| | | <Input placeholder="请输入销售方地址" value={addr} autoComplete="off" onChange={(e) => this.setState({addr: e.target.value})}/> |
| | | <Input placeholder="请输入销售方电话" value={tel} autoComplete="off" onChange={(e) => this.setState({tel: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item className="mutil-input" label="开户行及账号"> |
| | | <Input placeholder="请输入销售方开户行" value={bank_name} autoComplete="off" onChange={(e) => this.setState({bank_name: e.target.value})}/> |
| | | <Input placeholder="请输入销售方账号" value={account_no} autoComplete="off" onChange={(e) => this.setState({account_no: e.target.value})}/> |
| | | </Form.Item> |
| | | </div> |
| | | </div> |
| | | <div className="inv-notice"> |
| | | <div className="inv-label">备注</div> |
| | | <div className="inv-content" style={{paddingTop: '30px'}}> |
| | | <Form.Item label=""> |
| | | <Input.TextArea placeholder="请输入备注" autoSize={{ minRows: 4, maxRows: 4 }} value={remark} autoComplete="off" onChange={(e) => this.setState({remark: e.target.value})}/> |
| | | </Form.Item> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className="inv-tail"> |
| | | <Form.Item label="收款人"> |
| | | <Input placeholder="收款人" value={payee} autoComplete="off" onChange={(e) => this.setState({payee: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item label="复核人"> |
| | | <Input placeholder="复核人" value={reviewer} autoComplete="off" onChange={(e) => this.setState({reviewer: e.target.value})}/> |
| | | </Form.Item> |
| | | <Form.Item label="开票人"> |
| | | <Input placeholder="开票人" value={drawer} autoComplete="off" onChange={(e) => this.setState({drawer: e.target.value})}/> |
| | | </Form.Item> |
| | | </div> |
| | | <Modal |
| | | title="客户信息" |
| | | visible={visible} |
| | | width="70vw" |
| | | maskClosable={false} |
| | | onCancel={() => { this.setState({ visible: false }) }} |
| | | footer={null} |
| | | > |
| | | <SubTable config={config.buyer} onChange={this.changeBuyer}/> |
| | | </Modal> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default InvoiceModule |
New file |
| | |
| | | .menu-invoice-wrap { |
| | | position: relative; |
| | | box-sizing: border-box; |
| | | background: #ffffff; |
| | | background-position: center center; |
| | | background-repeat: no-repeat; |
| | | background-size: cover; |
| | | color: #000000; |
| | | --inv-color: #13509c; |
| | | |
| | | .inv-action { |
| | | text-align: right; |
| | | margin-right: 30px; |
| | | .ant-btn { |
| | | margin-left: 15px; |
| | | margin-bottom: 5px; |
| | | height: 30px; |
| | | } |
| | | .mk-bill:hover, .mk-bill:active, .mk-bill:focus { |
| | | color: var(--mk-sys-color); |
| | | border-color: var(--mk-sys-color); |
| | | } |
| | | .mk-submit, .mk-submit:hover, .mk-submit:active, .mk-submit:focus { |
| | | color: #fff; |
| | | background-color: var(--mk-sys-color); |
| | | border-color: var(--mk-sys-color); |
| | | } |
| | | } |
| | | .inv-header { |
| | | text-align: center; |
| | | position: relative; |
| | | height: 70px; |
| | | margin-right: 30px; |
| | | .ant-select { |
| | | width: 390px; |
| | | border: none; |
| | | .ant-select-selection { |
| | | border: none; |
| | | box-shadow: none; |
| | | font-size: 25px; |
| | | text-align: center; |
| | | background-color: transparent; |
| | | |
| | | .ant-select-selection-selected-value { |
| | | float: none; |
| | | text-align: center; |
| | | font-family: kaiti; |
| | | color: var(--inv-color, #13509c); |
| | | } |
| | | .ant-select-selection__placeholder { |
| | | text-align: center; |
| | | font-family: kaiti; |
| | | } |
| | | } |
| | | } |
| | | .ant-select::before, .ant-select::after { |
| | | content: ''; |
| | | display: block; |
| | | width: 100%; |
| | | position: absolute; |
| | | border-top: var(--inv-color, #13509c) 1px solid; |
| | | border-bottom: var(--inv-color, #13509c) 1px solid; |
| | | height: 1px; |
| | | } |
| | | .ant-select::before { |
| | | bottom: -10px; |
| | | } |
| | | .ant-select::after { |
| | | bottom: -15px; |
| | | } |
| | | .date { |
| | | position: absolute; |
| | | right: 100px; |
| | | top: 5px; |
| | | color: var(--inv-color, #13509c); |
| | | } |
| | | } |
| | | |
| | | .ant-input { |
| | | border-top: none; |
| | | border-left: none; |
| | | border-right: none; |
| | | border-radius: 0; |
| | | box-shadow: none!important; |
| | | height: 28px; |
| | | font-size: 13px; |
| | | background: transparent; |
| | | } |
| | | .ant-input:hover, .ant-input:active, .ant-input:focus { |
| | | border-color: var(--inv-color, #13509c)!important; |
| | | } |
| | | .ant-input.ant-input-disabled { |
| | | cursor: text; |
| | | background: transparent!important; |
| | | } |
| | | |
| | | .ant-input-number { |
| | | border-top: none; |
| | | border-left: none; |
| | | border-right: none; |
| | | border-radius: 0; |
| | | box-shadow: none!important; |
| | | height: 28px; |
| | | font-size: 13px; |
| | | .ant-input-number-handler-wrap { |
| | | display: none; |
| | | } |
| | | } |
| | | .ant-input-number:hover, .ant-input-number:active, .ant-input-number:focus { |
| | | border-color: var(--inv-color, #13509c)!important; |
| | | } |
| | | |
| | | .inv-body { |
| | | border: var(--inv-color, #13509c) 1px solid; |
| | | font-size: 13px; |
| | | margin-right: 30px; |
| | | |
| | | .inv-main-content { |
| | | display: flex; |
| | | |
| | | .inv-buyer, .inv-notice { |
| | | width: 50%; |
| | | display: flex; |
| | | .inv-label { |
| | | color: var(--inv-color, #13509c); |
| | | width: 6.25%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | writing-mode: vertical-rl; |
| | | justify-content: center; |
| | | align-items: center; |
| | | letter-spacing: 5px; |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | } |
| | | .inv-content { |
| | | flex: 1; |
| | | padding: 6px 0; |
| | | |
| | | .ant-form-item { |
| | | display: flex; |
| | | padding: 0 5px 0 15px; |
| | | font-size: 13px; |
| | | margin-bottom: 5px; |
| | | |
| | | .ant-form-item-label { |
| | | line-height: 30px; |
| | | text-align: justify; |
| | | label { |
| | | display: flex; |
| | | color: var(--inv-color, #13509c); |
| | | span { |
| | | display: inline-block; |
| | | flex: 1; |
| | | } |
| | | } |
| | | } |
| | | .ant-form-item-control-wrapper { |
| | | width: 100%; |
| | | |
| | | .ant-form-item-control { |
| | | line-height: 30px; |
| | | .ant-form-extra { |
| | | position: absolute; |
| | | top: 1px; |
| | | right: 10px; |
| | | font-size: 16px; |
| | | .anticon { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .ant-input-affix-wrapper:not(:hover) { |
| | | .ant-input-suffix { |
| | | opacity: 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .ant-form-item.mutil-input { |
| | | .ant-form-item-children { |
| | | display: flex; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .inv-buyer { |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | .ant-form-item-label { |
| | | width: 95px; |
| | | min-width: 95px; |
| | | } |
| | | } |
| | | .inv-notice { |
| | | .inv-content { |
| | | padding-top: 45px; |
| | | } |
| | | .ant-form-item-label { |
| | | width: 75px; |
| | | min-width: 75px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .inv-details { |
| | | border-top: var(--inv-color, #13509c) 1px solid; |
| | | border-bottom: var(--inv-color, #13509c) 1px solid; |
| | | } |
| | | } |
| | | .inv-tail { |
| | | display: flex; |
| | | padding-top: 10px; |
| | | margin-right: 30px; |
| | | .ant-form-item { |
| | | flex: 1; |
| | | display: flex; |
| | | font-size: 13px; |
| | | .ant-form-item-label, .ant-form-item-control-wrapper { |
| | | width: 40%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .inv-table { |
| | | .ant-table .ant-table-tbody tr:hover td { |
| | | background-color: var(--mk-sys-color1); |
| | | } |
| | | } |
| | | .tb-search-wrap { |
| | | .search-item { |
| | | display: flex; |
| | | float: left; |
| | | width: 350px; |
| | | align-items: center; |
| | | justify-content: flex-end; |
| | | |
| | | .ant-input { |
| | | width: 200px; |
| | | } |
| | | } |
| | | .ant-btn { |
| | | margin-bottom: 20px; |
| | | margin-left: 50px; |
| | | background-color: var(--mk-sys-color); |
| | | border-color: var(--mk-sys-color); |
| | | color: #ffffff; |
| | | } |
| | | .ant-btn:hover, .ant-btn:active { |
| | | background-color: var(--mk-sys-color); |
| | | border-color: var(--mk-sys-color); |
| | | color: #ffffff; |
| | | opacity: 0.9; |
| | | } |
| | | } |
| | | |
| | | .inv-type-select { |
| | | .ant-select-dropdown-menu-item { |
| | | text-align: center; |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | 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 |
New file |
| | |
| | | .detail-wrap { |
| | | position: relative; |
| | | |
| | | .plus-line, .del-line { |
| | | position: absolute; |
| | | right: -40px; |
| | | top: 5px; |
| | | font-size: 26px; |
| | | border: 1px solid #d9d9d9; |
| | | border-radius: 32px; |
| | | width: 30px; |
| | | height: 30px; |
| | | transition: all 0.3s; |
| | | cursor: pointer; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | .plus-line::before, .del-line::before { |
| | | content: ' '; |
| | | position: absolute; |
| | | top: 13px; |
| | | width: 12px; |
| | | height: 1px; |
| | | background: #b8b8b8; |
| | | transition: all 0.3s; |
| | | } |
| | | .plus-line::after { |
| | | content: ' '; |
| | | position: absolute; |
| | | top: 13px; |
| | | width: 12px; |
| | | height: 1px; |
| | | background: #b8b8b8; |
| | | transform: rotate(90deg); |
| | | transition: all 0.3s; |
| | | } |
| | | .plus-line:hover { |
| | | border-color: #26C281; |
| | | } |
| | | .plus-line:hover::before, .plus-line:hover::after { |
| | | background: #26C281; |
| | | } |
| | | .del-line:hover { |
| | | border-color: #ff4d4f; |
| | | } |
| | | .del-line:hover::before { |
| | | background: #ff4d4f; |
| | | } |
| | | |
| | | .mk-th, .mk-tr, .mk-total { |
| | | position: relative; |
| | | display: flex; |
| | | border-bottom: var(--inv-color, #13509c) 1px solid; |
| | | |
| | | .mk-td { |
| | | width: 10%; |
| | | height: 40px; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding: 0 3px; |
| | | } |
| | | .mk-td:not(:last-child) { |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | } |
| | | .mk-td:first-child { |
| | | width: 20%; |
| | | } |
| | | .mk-td:nth-child(2), .mk-td:nth-child(6) { |
| | | width: 15%; |
| | | } |
| | | |
| | | .mk-left { |
| | | justify-content: start; |
| | | } |
| | | .mk-right { |
| | | justify-content: end; |
| | | } |
| | | |
| | | .mk-input { |
| | | position: relative; |
| | | height: 28px; |
| | | width: 100%; |
| | | background: #ffffff; |
| | | border-bottom: 1px solid #d9d9d9; |
| | | transition: all 0.3s; |
| | | |
| | | .anticon-ellipsis { |
| | | position: absolute; |
| | | right: 0px; |
| | | padding: 5px 10px; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .mk-input:hover { |
| | | border-color: var(--inv-color, #13509c); |
| | | } |
| | | |
| | | .ant-input { |
| | | background: #ffffff; |
| | | } |
| | | } |
| | | .mk-tr.active, .mk-tr:hover { |
| | | background: var(--mk-sys-color1); |
| | | } |
| | | .mk-upcase { |
| | | display: flex; |
| | | |
| | | .mk-td { |
| | | width: 40%; |
| | | height: 40px; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 20px; |
| | | } |
| | | .mk-td:first-child { |
| | | width: 20%; |
| | | padding: 0; |
| | | border-right: var(--inv-color, #13509c) 1px solid; |
| | | justify-content: center; |
| | | } |
| | | .mk-td:last-child { |
| | | justify-content: end; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Table, Input, Button } from 'antd' |
| | | |
| | | import Api from '@/api' |
| | | import UtilsDM from '@/utils/utils-datamanage.js' |
| | | // import './index.scss' |
| | | |
| | | class SearchWrap extends Component { |
| | | static propTpyes = { |
| | | search: PropTypes.array, |
| | | onChange: PropTypes.func |
| | | } |
| | | |
| | | state = { |
| | | search: [] |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { search } = this.props |
| | | |
| | | this.setState({ |
| | | search: fromJS(search).toJS() |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | } |
| | | |
| | | searchChange = (val, key) => { |
| | | const { search } = this.state |
| | | |
| | | this.setState({search: search.map(item => { |
| | | if (item.field === key) { |
| | | item.value = val |
| | | } |
| | | return item |
| | | })}) |
| | | } |
| | | |
| | | trigger = () => { |
| | | const { search } = this.state |
| | | |
| | | this.props.onChange(search) |
| | | } |
| | | |
| | | render() { |
| | | const { search } = this.state |
| | | |
| | | return ( |
| | | <div className="tb-search-wrap"> |
| | | {search.map(item => ( |
| | | <div className="search-item" key={item.field}> |
| | | {item.label}: |
| | | <Input autoComplete="off" onChange={(e) => this.searchChange(e.target.value, item.field)}/> |
| | | </div> |
| | | ))} |
| | | <Button onClick={this.trigger}>搜索</Button> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | class SubTable extends Component { |
| | | static propTpyes = { |
| | | config: PropTypes.object |
| | | } |
| | | |
| | | state = { |
| | | pageIndex: 1, // 初始页面索引 |
| | | pageSize: 10, // 每页数据条数 |
| | | columns: null, // 显示列 |
| | | pageOptions: [], |
| | | search: [] |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { config } = this.props |
| | | |
| | | let _columns = [] |
| | | |
| | | config.columns.forEach(item => { |
| | | if (item.Hide === 'true') return |
| | | _columns.push({ |
| | | align: 'center', |
| | | dataIndex: item.field, |
| | | title: item.label, |
| | | sorter: false, |
| | | width: item.Width || 120 |
| | | }) |
| | | }) |
| | | |
| | | let size = (config.setting.pageSize || 10) + '' |
| | | let pageOptions = ['10', '25', '50', '100', '500', '1000'] |
| | | |
| | | if (!pageOptions.includes(size)) { |
| | | pageOptions.push(size) |
| | | pageOptions = pageOptions.sort((a, b) => a - b) |
| | | } |
| | | |
| | | this.setState({ |
| | | pageSize: config.pageSize || 10, |
| | | pageOptions, |
| | | search: fromJS(config.search).toJS(), |
| | | columns: _columns |
| | | }) |
| | | } |
| | | |
| | | componentDidMount() { |
| | | const { config } = this.props |
| | | |
| | | if (config.setting.onload === 'true') { |
| | | this.loadData() |
| | | } |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @description 数据加载 |
| | | */ |
| | | async loadData () { |
| | | const { config } = this.props |
| | | const { search, pageIndex, pageSize } = this.state |
| | | |
| | | this.setState({ |
| | | loading: true |
| | | }) |
| | | |
| | | let searches = search.map(item => ({ |
| | | key: item.field, |
| | | match: item.match, |
| | | type: item.type, |
| | | value: item.value || '' |
| | | })) |
| | | |
| | | let param = UtilsDM.getQueryDataParams(config.setting, searches, config.setting.order, pageIndex, pageSize, '') |
| | | |
| | | this.requestId = config.uuid + new Date().getTime() |
| | | |
| | | let result = await Api.genericInterface(param, config.setting.js_script, '', this.requestId) |
| | | |
| | | if (result.status) { |
| | | if (result.$requestId && this.requestId !== result.$requestId) return |
| | | |
| | | let start = pageSize * (pageIndex - 1) + 1 |
| | | |
| | | let data = result.data.map((item, index) => { |
| | | item.key = index |
| | | item.$Index = start + index + '' |
| | | |
| | | return item |
| | | }) |
| | | |
| | | let total = result.total || 0 |
| | | if (config.setting.custompage && data.length) { |
| | | total = data[data.length - 1].mk_total || 0 |
| | | } |
| | | |
| | | this.setState({ |
| | | data: data, |
| | | total: total, |
| | | loading: false |
| | | }) |
| | | |
| | | UtilsDM.querySuccess(result) |
| | | } else { |
| | | this.setState({ |
| | | loading: false |
| | | }) |
| | | |
| | | UtilsDM.queryFail(result) |
| | | } |
| | | } |
| | | |
| | | changeTable = (pagination) => { |
| | | this.setState({ |
| | | pageIndex: pagination.current, |
| | | pageSize: pagination.pageSize, |
| | | }, () => { |
| | | this.loadData() |
| | | }) |
| | | } |
| | | |
| | | doubleClickLine = (record) => { |
| | | this.props.onChange(record) |
| | | } |
| | | |
| | | searchChange = (search) => { |
| | | this.setState({search: search}, () => { |
| | | this.loadData() |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { pageIndex, pageSize, pageOptions, columns, total, loading, data, search } = this.state |
| | | |
| | | return ( |
| | | <> |
| | | <SearchWrap search={search} onChange={this.searchChange}/> |
| | | <Table |
| | | className="inv-table" |
| | | columns={columns} |
| | | dataSource={data} |
| | | bordered={true} |
| | | loading={loading} |
| | | onRow={(record) => { |
| | | return { |
| | | onDoubleClick: () => {this.doubleClickLine(record)} |
| | | } |
| | | }} |
| | | onChange={this.changeTable} |
| | | pagination={{ |
| | | current: pageIndex, |
| | | pageSize: pageSize, |
| | | pageSizeOptions: pageOptions, |
| | | showSizeChanger: true, |
| | | total: total || 0, |
| | | showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条` |
| | | }} |
| | | /> |
| | | </> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default SubTable |
| | |
| | | let hasSelectKey = false |
| | | |
| | | data.forEach(item => { |
| | | let pval = item[config.wrap.parentField] |
| | | let val = item[config.wrap.valueField] |
| | | let uuid = item[config.setting.primaryKey] || '' |
| | | let pval = item[config.wrap.parentField] + '' |
| | | let val = item[config.wrap.valueField] + '' |
| | | let uuid = item[config.setting.primaryKey] + '' |
| | | |
| | | if (!val || logMap.has(val)) return |
| | | |
| | |
| | | const AntvX6 = asyncComponent(() => import('./components/chart/antv-X6')) |
| | | const Voucher = asyncComponent(() => import('./components/module/voucher')) |
| | | const Account = asyncComponent(() => import('./components/module/account')) |
| | | const Invoice = asyncComponent(() => import('./components/module/invoice')) |
| | | const Iframe = asyncComponent(() => import('./components/iframe')) |
| | | const Calendar = asyncComponent(() => import('./components/calendar')) |
| | | const DebugTable = asyncComponent(() => import('@/tabviews/debugtable')) |
| | |
| | | <Account config={item}/> |
| | | </Col> |
| | | ) |
| | | } else if (item.type === 'module' && item.subtype === 'invoice') { |
| | | return ( |
| | | <Col span={item.width} style={style} key={item.uuid}> |
| | | <Invoice config={item}/> |
| | | </Col> |
| | | ) |
| | | } else if (item.type === 'iframe') { |
| | | return ( |
| | | <Col span={item.width} style={style} key={item.uuid}> |
| | |
| | | |
| | | let cols = _verify.columns.map(col => col.Column.toLowerCase()) |
| | | cols = Array.from(new Set(cols)) |
| | | let error = '' |
| | | |
| | | if (_verify.columns.length === 0) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '请设置Excel列字段!', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | error = '请设置Excel列字段!' |
| | | } else if (_verify.columns.length > cols.length) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: 'Excel列字段名,不可重复!', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | error = 'Excel列字段名,不可重复!' |
| | | } else if (cols.includes('bid')) { |
| | | error = 'bid字段为保留字,不可使用!' |
| | | } else if (cols.includes('jskey')) { |
| | | error = 'jskey字段为保留字,不可使用!' |
| | | } else if (_verify.range === 1) { |
| | | let tEmptys = _verify.columns.filter(op => !op.Text) |
| | | if (tEmptys.length > 0) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '忽略首行时,会使用Text值校验Excel首行内容,Text值与Excel表首行内容相同,且均不可为空!', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | error = '忽略首行时,会使用Text值校验Excel首行内容,Text值与Excel表首行内容相同,且均不可为空!' |
| | | } |
| | | } |
| | | |
| | | if (error) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: error, |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | |
| | | _verify.columns.sort((a, b) => { |
| | | if (a.import === 'init' && b.import !== 'init') { |
| | | return 1 |