king
2024-04-23 59002c8182d10c21a1becdff4e566ee11cd1cd91
2024-04-23
7个文件已修改
8个文件已添加
1924 ■■■■■ 已修改文件
src/menu/components/module/invoice/index.jsx 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/baseform/index.jsx 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/baseform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/customscript/index.jsx 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/customscript/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/index.jsx 690 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/index.scss 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/index.jsx 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/index.scss 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tree/antd-tree/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Button } from 'antd'
import { Popover, Button, Modal } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -9,6 +9,7 @@
import asyncIconComponent from '@/utils/asyncIconComponent'
import { getTables, checkComponent } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import VerifyCard from './verifycard'
import getWrapForm from './options'
import './index.scss'
@@ -25,7 +26,8 @@
  state = {
    card: null,
    date: moment().format('YYYY年MM月')
    date: moment().format('YYYY年MM月'),
    btn: null
  }
  UNSAFE_componentWillMount () {
@@ -69,7 +71,9 @@
            {field: 'productname', label: '商品名称', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
            {field: 'productcode', label: '商品编码', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
          ],
        }
        },
        billSaveBtn: {type: 'billsave', intertype: 'system', label: '保存单据'},
        billOutBtn: {type: 'billout', intertype: 'custom', label: '提交开票', procMode: 'system'},
      }
      let buys = [
@@ -149,8 +153,8 @@
    } else {
      let _card = fromJS(card).toJS()
      // _card.buyer.format = 'array'
      // _card.detail.format = 'array'
      // _card.billSaveBtn = _card.billSaveBtn || {type: 'billsave', intertype: 'system', label: '保存单据'}
      // _card.billOutBtn = _card.billOutBtn || {type: 'billout', intertype: 'custom', label: '提交开票', procMode: 'system'}
      this.setState({
        card: _card
@@ -205,6 +209,14 @@
      }
    }
    if (!card.billSaveBtn.scripts || card.billSaveBtn.scripts.length === 0) {
      card.errors.push({ level: 0, detail: '未添加单据保存脚本!'})
    // } else if (!card.billOutBtn.scripts || card.billOutBtn.scripts.length === 0) {
    //   card.errors.push({ level: 0, detail: '未添加提交开票前置脚本!'})
    // } else if (card.billSaveBtn.cbScripts.length === 0) {
    //   card.errors.push({ level: 0, detail: '未添加提交开票回调脚本!'})
    }
    if (card.errors.length === 0) {
      card.$tables = getTables(card)
      card.$tables = [...card.$tables, ...getTables(card.buyer)]
@@ -240,8 +252,20 @@
    this.updateComponent({...this.state.card, wrap: res})
  }
  verifySubmit = () => {
    this.verifyRef.handleConfirm().then(res => {
      if (res.type === 'billout') {
        this.updateComponent({...this.state.card, billOutBtn: res})
      } else {
        this.updateComponent({...this.state.card, billSaveBtn: res})
      }
      this.setState({ btn: null })
    })
  }
  render() {
    const { card, date } = this.state
    const { card, date, btn } = this.state
    let style = {...card.style}
    if (card.wrap.invColor) {
@@ -263,8 +287,20 @@
          <ToolOutlined />
        </Popover>
        <div className="inv-action">
          <Button className="mk-bill">保存单据</Button>
          <Button className="mk-submit">提交开票</Button>
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <EditOutlined style={{color: '#1890ff'}} onClick={() => this.setState({btn: card.billSaveBtn})} title="编辑"/>
            </div>
          } trigger="hover">
            <Button className="mk-bill">保存单据</Button>
          </Popover>
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <EditOutlined style={{color: '#1890ff'}} onClick={() => this.setState({btn: card.billOutBtn})} title="编辑"/>
            </div>
          } trigger="hover">
            <Button className="mk-submit">提交开票</Button>
          </Popover>
        </div>
        <div className="inv-header">
          <div className="mk-select">请选择发票种类</div>
@@ -413,6 +449,26 @@
            <span className="content">开票人</span>
          </div>
        </div>
        <Modal
          wrapClassName="mk-pop-modal"
          visible={btn !== null}
          width={'90vw'}
          maskClosable={false}
          okText="提交"
          onOk={this.verifySubmit}
          onCancel={() => {
            if (this.verifyRef.handleCancel) {
              this.verifyRef.handleCancel().then(() => {
                this.setState({ btn: null })
              })
            } else {
              this.setState({ btn: null })
            }
          }}
          destroyOnClose
        >
          <VerifyCard card={btn} wrappedComponentRef={(inst) => this.verifyRef = inst}/>
        </Modal>
      </div>
    )
  }
src/menu/components/module/invoice/verifycard/baseform/index.jsx
New file
@@ -0,0 +1,110 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input } from 'antd'
// import { QuestionCircleOutlined } from '@ant-design/icons'
// import './index.scss'
const { TextArea } = Input
class BaseForm extends Component {
  static propTpyes = {
    verify: PropTypes.object,
    onChange: PropTypes.func
  }
  state = {}
  handleConfirm = () => {
    const { verify } = this.props
    if (verify.type === 'billout') {
      return new Promise((resolve, reject) => {
        this.props.form.validateFieldsAndScroll((err, values) => {
          if (!err) {
            resolve(values)
          }
        })
      })
    } else {
      return Promise.resolve()
    }
  }
  // onOptionChange = (value, key) => {
  //   const { verify } = this.props
  //   let _verify = {...verify, [key]: value}
  //   this.props.onChange(_verify)
  // }
  render() {
    const { getFieldDecorator } = this.props.form
    const { verify } = this.props
    return (
      <Form className="base-form">
        <Row gutter={24}>
          <Col span={8}>
            <Form.Item label="按钮名称">
              <Input value={verify.label} disabled={true}/>
            </Form.Item>
          </Col>
          {/* <Col span={8}>
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="">
                <QuestionCircleOutlined className="mk-form-tip" />
                接口类型
              </Tooltip>
            }>
              <Radio.Group value={verify.intertype} disabled={true}>
                <Radio value="system">系统</Radio>
                <Radio value="custom">自定义</Radio>
              </Radio.Group>
            </Form.Item>
          </Col> */}
          {/* {verify.type === 'billout' ? <Col span={8}>
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="">
                <QuestionCircleOutlined className="mk-form-tip" />
                参数处理
              </Tooltip>
            }>
              {getFieldDecorator('procMode', {
                initialValue: verify.procMode || 'system',
              })(
                <Radio.Group onChange={(e) => {this.onOptionChange(e.target.value, 'procMode')}}>
                  <Radio value="system">系统函数</Radio>
                  <Radio value="none">无</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null} */}
          {verify.type === 'billout' ? <Col span={24}>
            <Form.Item label="测试地址">
              {getFieldDecorator('interface', {
                initialValue: verify.interface || '',
                rules: [
                  { required: true, message: '请输入测试地址!' }
                ]
              })(
                <TextArea rows={2}/>
              )}
            </Form.Item>
          </Col> : null}
          {verify.type === 'billout' ? <Col span={24}>
            <Form.Item label="正式地址">
              {getFieldDecorator('proInterface', {
                initialValue: verify.proInterface || '',
              })(
                <TextArea rows={2}/>
              )}
            </Form.Item>
          </Col> : null}
        </Row>
      </Form>
    )
  }
}
export default Form.create()(BaseForm)
src/menu/components/module/invoice/verifycard/baseform/index.scss
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx
New file
@@ -0,0 +1,294 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Button, Modal, Tooltip, Radio, Select, Switch, notification } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import Api from '@/api'
import { checkSQL } from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
// import './index.scss'
class CustomForm extends Component {
  static propTpyes = {
    systemScripts: PropTypes.array,
    customScripts: PropTypes.array,
    scriptsChange: PropTypes.func
  }
  state = {
    editItem: null,
    loading: false,
    skip: false
  }
  edit = (record) => {
    this.setState({
      editItem: record
    })
    if (this.props.type) {
      this.props.form.setFieldsValue({
        sql: record.sql
      })
    } else {
      this.props.form.setFieldsValue({
        sql: record.sql,
        position: record.position || 'back'
      })
    }
  }
  handleConfirm = () => {
    const { type } = this.props
    const { editItem, skip } = this.state
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (type === 'fullscreen' && err) {
        notification.warning({
          top: 92,
          message: '请输入sql!',
          duration: 5
        })
        return
      }
      if (!err) {
        if (/^[\s\n]+$/.test(values.sql)) {
          notification.warning({
            top: 92,
            message: '请输入sql!',
            duration: 5
          })
          return
        }
        values.uuid = editItem ? editItem.uuid : ''
        values.position = values.position || (editItem ? editItem.position : 'front')
        if (type === 'fullscreen' && editItem) {
          values.status = editItem.status || 'true'
        }
        let pass = checkSQL(values.sql, 'customscript')
        if (!pass) return
        let sql = `
          /* 系统字段 */
          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), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
          Select @UserName='', @FullName='', @RoleID='', @mk_departmentcode='', @mk_organization='', @mk_user_type='', @mk_nation='', @mk_province='', @mk_city='', @mk_district='', @mk_address='', @ErrorCode='', @retmsg='', @account_id='', @account_year_id='', @account_code='', @account_year_code='', @bid=''
        `
        let _prevCustomScript = '' // 默认sql前执行脚本
        let _backCustomScript = '' // 默认sql后执行脚本
        this.props.customScripts.forEach(item => {
          if (item.status === 'false') return
          if (item.position === 'front') {
            _prevCustomScript += `
            /* 默认sql前脚本 */
            ${values.uuid === item.uuid ? values.sql : item.sql}
            `
          } else {
            _backCustomScript += `
            /* 默认sql后脚本 */
            ${values.uuid === item.uuid ? values.sql : item.sql}
            `
          }
        })
        if (!values.uuid) {
          if (values.position === 'front') {
            _prevCustomScript += `
            /* 默认sql前脚本 */
            ${values.sql}
            `
          } else {
            _backCustomScript += `
            /* 默认sql后脚本 */
            ${values.sql}
            `
          }
        }
        sql += _prevCustomScript + _backCustomScript
        sql += `
          aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg
        `
        // 数据权限
        sql = sql.replace(/@\$|\$@/ig, '')
        sql = sql.replace(/@datam@/ig, `''`)
        sql = sql.replace(/@typename@/ig, `'debug'`)
        if (skip) {
          this.setState({
            skip: false,
            editItem: null
          }, () => {
            this.props.scriptsChange(values)
          })
          this.props.form.setFieldsValue({
            sql: ' '
          })
        } else {
          this.setState({loading: true})
          Api.sDebug(sql).then(res => {
            if (res.status || res.ErrCode === '-2') {
              this.setState({
                loading: false,
                editItem: null
              }, () => {
                this.props.scriptsChange(values)
              })
              this.props.form.setFieldsValue({
                sql: ' '
              })
            } else {
              this.setState({loading: false})
              Modal.error({
                title: res.message
              })
            }
          })
        }
      }
    })
  }
  handleCancel = () => {
    this.setState({
      editItem: null
    })
    this.props.form.setFieldsValue({
      sql: ' '
    })
  }
  selectScript = (value, option) => {
    if (!value || !option) return
    let _sql = this.props.form.getFieldValue('sql')
    if (/^\s+$/.test(_sql)) {
      _sql = ''
    }
    if (_sql) {
      _sql = _sql + `
      `
    }
    _sql = _sql.replace(/\s{6}$/, '')
    _sql = _sql + `/*${option.props.children}*/
    `
    _sql = _sql.replace(/\s{4}$/, '')
    _sql = _sql + value
    this.props.form.setFieldsValue({
      sql: _sql
    })
  }
  render() {
    const { systemScripts, type } = this.props
    const { getFieldDecorator } = this.props.form
    const { editItem, skip } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form">
        <Row gutter={24}>
          {!type ? <Col span={8}>
            <Form.Item label="报错字段" style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
            <Form.Item label="可用字段">
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'公共值,请按照@xxx@格式使用。'}><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename, datam</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'系统变量,系统会定义变量并赋值。'}><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address</span></Tooltip>,&nbsp;
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
            <Form.Item style={{marginBottom: 0}} label={
              <Tooltip placement="bottomLeft" title={'自定义脚本与默认sql位置关系。'}>
                <QuestionCircleOutlined className="mk-form-tip" />
                执行位置
              </Tooltip>
            }>
              {getFieldDecorator('position', {
                initialValue: 'front'
              })(
                <Radio.Group>
                  <Radio value="front">sql前</Radio>
                  <Radio value="back">sql后</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={8}>
            <Form.Item label="快捷添加" style={{marginBottom: 0}}>
              <Select
                showSearch
                dropdownMatchSelectWidth={false}
                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                onChange={this.selectScript}
              >
                <Select.Option key="default" value="defaultSql">默认sql</Select.Option>
                {systemScripts.map((option, i) =>
                  <Select.Option key={i} value={option.value}>{option.name}</Select.Option>
                )}
              </Select>
            </Form.Item>
          </Col> : null}
          <Col span={5} className="add" style={{whiteSpace: 'nowrap'}}>
            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginBottom: 15, marginLeft: 40}}>
              {type === 'fullscreen' && !editItem ? '添加' : '保存'}
            </Button>
            <Button onClick={this.handleCancel} style={{marginBottom: 15, marginLeft: 10}}>
              取消
            </Button>
          </Col>
          <Col span={3} className="forced" style={{paddingTop: '12px', fontSize: '12px', whiteSpace: 'nowrap'}}>
            强制保存:
            <Switch checked={skip} size="small" onChange={() => this.setState({skip: !skip})}/>
          </Col>
          <Col span={24} className="sql">
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="数据权限替换符 $@ -> /* 或 \'\'、 @$ -> */ 或 \'\'">
                <QuestionCircleOutlined className="mk-form-tip" />
                sql
              </Tooltip>
            }>
              {getFieldDecorator('sql', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: '请输入sql!'
                  }
                ]
              })(<CodeMirror />)}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(CustomForm)
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss
src/menu/components/module/invoice/verifycard/customscript/index.jsx
New file
@@ -0,0 +1,284 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Button, notification, Modal, Tooltip, Select, Switch } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import Api from '@/api'
import { checkSQL } from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
// import './index.scss'
class CustomForm extends Component {
  static propTpyes = {
    type: PropTypes.any,
    systemScripts: PropTypes.array,
    customScripts: PropTypes.array,
    scriptsChange: PropTypes.func
  }
  state = {
    editItem: null,
    loading: false,
    skip: false
  }
  edit = (record) => {
    this.setState({
      editItem: record
    })
    this.props.form.setFieldsValue({
      sql: record.sql
    })
  }
  handleConfirm = () => {
    const { type } = this.props
    const { editItem, skip } = this.state
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (type === 'fullscreen' && err) {
        notification.warning({
          top: 92,
          message: '请输入sql!',
          duration: 5
        })
        return
      }
      if (!err) {
        if (/^[\s\n]+$/.test(values.sql)) {
          notification.warning({
            top: 92,
            message: '请输入sql!',
            duration: 5
          })
          return
        }
        values.uuid = editItem ? editItem.uuid : ''
        if (type === 'fullscreen' && editItem) {
          values.status = editItem.status || 'true'
        }
        let pass = checkSQL(values.sql, 'customscript')
        if (!pass) return
        let sql = `
          /* 系统字段 */
          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), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
          Select @UserName='', @FullName='', @RoleID='', @mk_departmentcode='', @mk_organization='', @mk_user_type='', @mk_nation='', @mk_province='', @mk_city='', @mk_district='', @mk_address='', @ErrorCode='', @retmsg='', @account_id='', @account_year_id='', @account_code='', @account_year_code='', @bid=''
          /* 发票主表字段 */
          Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50)
          Select @invoice_type='', @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=''
          /* 发票明细临时表 */
          Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2))
          Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount)
          Select '', '', '', '', 0, 0, 0, '', '', 0, 0
        `
        this.props.customScripts.forEach(item => {
          let _item = values.uuid === item.uuid ? values : item
          if (_item.status === 'false') return
          sql += `
          ${_item.sql}
          `
        })
        if (!values.uuid) {
          sql += `
          ${values.sql}
          `
        }
        sql += `
          aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg
        `
        // 数据权限
        sql = sql.replace(/@\$|\$@/ig, '')
        sql = sql.replace(/@datam@/ig, `''`)
        sql = sql.replace(/@typename@/ig, `'debug'`)
        if (skip) {
          this.setState({
            skip: false,
            editItem: null
          }, () => {
            this.props.scriptsChange(values)
          })
          this.props.form.setFieldsValue({
            sql: ' '
          })
        } else {
          this.setState({loading: true})
          Api.sDebug(sql).then(res => {
            if (res.status || res.ErrCode === '-2') {
              this.setState({
                loading: false,
                editItem: null
              }, () => {
                this.props.scriptsChange(values)
              })
              this.props.form.setFieldsValue({
                sql: ' '
              })
            } else {
              this.setState({loading: false})
              Modal.error({
                title: res.message
              })
            }
          })
        }
      }
    })
  }
  handleCancel = () => {
    this.setState({
      editItem: null
    })
    this.props.form.setFieldsValue({
      sql: ' '
    })
  }
  selectScript = (value, option) => {
    if (!value || !option) return
    let _sql = this.props.form.getFieldValue('sql')
    if (/^\s+$/.test(_sql)) {
      _sql = ''
    }
    if (_sql) {
      _sql = _sql + `
      `
    }
    _sql = _sql.replace(/\s{6}$/, '')
    _sql = _sql + `/*${option.props.children}*/
    `
    _sql = _sql.replace(/\s{4}$/, '')
    _sql = _sql + value
    this.props.form.setFieldsValue({
      sql: _sql
    })
  }
  render() {
    const { systemScripts, type } = this.props
    const { getFieldDecorator } = this.props.form
    const { editItem, skip } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form">
        <Row gutter={24}>
          {!type ? <Col span={8}>
            <Form.Item label={'报错字段'} style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
            <Form.Item label={'可用字段'}>
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="公共值,请按照@xxx@格式使用。"><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename, datam</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="系统变量,系统会定义变量并赋值。"><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="账套字段,系统会定义变量并赋值。"><span style={{color: '#13c2c2'}}>account_id, account_year_id, account_code, account_year_code </span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="主表字段,系统会定义变量并赋值。"><span style={{color: '#8E44AD'}}>invoice_type, 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</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="子表字段(商品明细),系统会定义变量并赋值。">productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount</Tooltip>
            </Form.Item>
          </Col> : null}
          {/* {!_type ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
            <Form.Item style={{marginBottom: 0}} label={
              <Tooltip placement="bottomLeft" title="自定义脚本与默认sql位置关系。">
                <QuestionCircleOutlined className="mk-form-tip" />
                执行位置
              </Tooltip>
            }>
              {getFieldDecorator('position', {
                initialValue: 'front'
              })(
                <Radio.Group>
                  <Radio value="init">初始化</Radio>
                  <Radio value="front">sql前</Radio>
                  <Radio value="back">sql后</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null} */}
          {!type ? <Col span={8}>
            <Form.Item label={'快捷添加'} style={{marginBottom: 0}}>
              <Select
                showSearch
                dropdownMatchSelectWidth={false}
                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                onSelect={this.selectScript}
              >
                {systemScripts.map((option, i) =>
                  <Select.Option key={i} value={option.value}>{option.name}</Select.Option>
                )}
              </Select>
            </Form.Item>
          </Col> : null}
          <Col span={5} className="add" style={{whiteSpace: 'nowrap'}}>
            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginBottom: 15, marginLeft: 30}}>
              {type === 'fullscreen' && !editItem ? '添加' : '保存'}
            </Button>
            <Button onClick={this.handleCancel} style={{marginBottom: 15, marginLeft: 10}}>
              取消
            </Button>
          </Col>
          <Col span={3} className="forced" style={{paddingTop: '12px', fontSize: '12px', whiteSpace: 'nowrap'}}>
            强制保存:
            <Switch checked={skip} size="small" onChange={() => this.setState({skip: !skip})}/>
          </Col>
          <Col span={24} className="sql">
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="数据权限替换符 $@ -> /* 或 \'\'、 @$ -> */ 或 \'\'">
                <QuestionCircleOutlined className="mk-form-tip" />
                sql
              </Tooltip>
            }>
              {getFieldDecorator('sql', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: '请输入sql!'
                  }
                ]
              })(<CodeMirror />)}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(CustomForm)
src/menu/components/module/invoice/verifycard/customscript/index.scss
src/menu/components/module/invoice/verifycard/index.jsx
New file
@@ -0,0 +1,690 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Form, Tabs, Row, Col, Button, Popconfirm, notification, Modal, message, Typography, InputNumber } from 'antd'
import { CheckCircleOutlined, StopOutlined, EditOutlined, SwapOutlined, DeleteOutlined } from '@ant-design/icons'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import BaseForm from './baseform'
import CustomScript from './customscript'
import CallBackCustomScript from './callbackcustomscript'
import asyncComponent from '@/utils/asyncComponent'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { TabPane } = Tabs
const { confirm } = Modal
const { Paragraph } = Typography
const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const FullScripts = asyncComponent(() => import('@/templates/zshare/verifycard/fullScripts'))
class VerifyCard extends Component {
  static propTpyes = {
    config: PropTypes.any,
    card: PropTypes.object,
    columns: PropTypes.array
  }
  state = {
    activeKey: 'base',
    verify: {},
    systemScripts: [],
    scriptsColumns: [
      {
        title: 'SQL',
        dataIndex: 'sql',
        width: '70%',
        render: (text) => {
          let title = text.match(/^\s*\/\*.+\*\//)
          title = title && title[0] ? title[0] : ''
          let _text = title ? text.replace(title, '') : text
          return (
            <div>
              {title ? <span style={{color: '#a50'}}>{title}<span style={{fontSize: '12px', marginLeft: '5px'}}>{_text.length}</span></span> : null}
              <Paragraph copyable={{ text: text }} ellipsis={{ rows: 4, expandable: true }}>{_text}</Paragraph>
            </div>
          )
        }
      },
      // {
      //   title: '执行位置',
      //   dataIndex: 'position',
      //   width: '10%',
      //   render: (text, record) => {
      //     if (record.position === 'init') {
      //       return <span style={{color: 'orange'}}>初始化</span>
      //     } else if (record.position === 'front') {
      //       return <span style={{color: '#26C281'}}>sql前</span>
      //     } else {
      //       return <span style={{color: '#1890ff'}}>sql后</span>
      //     }
      //   }
      // },
      {
        title: '状态',
        dataIndex: 'status',
        width: '10%',
        render: (text, record) => record.status === 'false' ?
          (
            <div style={{color: '#ff4d4f'}}>
              禁用
              <StopOutlined style={{marginLeft: '5px'}} />
            </div>
          ) :
          (
            <div style={{color: '#26C281'}}>
              启用
              <CheckCircleOutlined style={{marginLeft: '5px'}}/>
            </div>
          )
      },
      {
        title: '操作',
        align: 'center',
        width: '140px',
        dataIndex: 'operation',
        render: (_, record) =>
          (<div style={{textAlign: 'center'}}>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record, 'scripts')} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record, 'scripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <Popconfirm
              overlayClassName="popover-confirm"
              title="确定删除吗?"
              onConfirm={() => this.handleDelete(record, 'scripts')
            }>
              <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
            </Popconfirm>
          </div>)
      }
    ],
    cbScriptsColumns: [
      {
        title: 'SQL',
        dataIndex: 'sql',
        width: '60%',
        render: (text) => {
          let title = text.match(/^\s*\/\*.+\*\//)
          title = title && title[0] ? title[0] : ''
          let _text = title ? text.replace(title, '') : text
          return (
            <div>
              {title ? <span style={{color: '#a50'}}>{title}<span style={{fontSize: '12px', marginLeft: '5px'}}>{_text.length}</span></span> : null}
              <Paragraph copyable={{ text: text }} ellipsis={{ rows: 4, expandable: true }}>{_text}</Paragraph>
            </div>
          )
        }
      },
      {
        title: '执行位置',
        dataIndex: 'position',
        width: '10%',
        render: (_, record) => {
          if (record.position === 'front') {
            return <span style={{color: '#26C281'}}>sql前</span>
          } else {
            return <span style={{color: '#1890ff'}}>sql后</span>
          }
        }
      },
      {
        title: '状态',
        dataIndex: 'status',
        width: '10%',
        render: (_, record) => record.status === 'false' ?
          (
            <div style={{color: '#ff4d4f'}}>
              禁用
              <StopOutlined style={{marginLeft: '5px'}} />
            </div>
          ) :
          (
            <div style={{color: '#26C281'}}>
              启用
              <CheckCircleOutlined style={{marginLeft: '5px'}}/>
            </div>
          )
      },
      {
        title: '操作',
        align: 'center',
        width: '20%',
        dataIndex: 'operation',
        render: (text, record) =>
          (<div style={{textAlign: 'center'}}>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record, 'cbscripts')} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record, 'cbscripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <Popconfirm
              overlayClassName="popover-confirm"
              title="确定删除吗?"
              onConfirm={() => this.handleDelete(record, 'cbscripts')
            }>
              <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
            </Popconfirm>
          </div>)
      }
    ]
  }
  UNSAFE_componentWillMount() {
    const { card } = this.props
    let _verify = fromJS(card).toJS()
    // _verify.intertype = _verify.intertype || 'system'
    _verify.scripts = _verify.scripts || []
    _verify.cbScripts = _verify.cbScripts || []
    _verify.scripts.forEach((item, i) => {
      item.$index = i + 1
    })
    _verify.cbScripts.forEach((item, i) => {
      item.$index = i + 1
    })
    this.setState({
      activeKey: 'base',
      verify: _verify,
      oriVerify: fromJS(_verify).toJS()
    })
  }
  componentDidMount() {
    let mutilForms = [
      {
        obj_name: 'scripts',
        arr_field: 'funcname,longparam',
        LText: window.btoa(window.encodeURIComponent(`Select distinct func+Remark as funcname,longparam, s.Sort from s_custom_script s inner join (select OpenID from sapp where ID=@Appkey@) p on s.openid = case when s.appkey='' then s.openid else p.OpenID end order by s.Sort`))
      }
    ]
    mutilForms = mutilForms.map(item => `select '${item.obj_name}' as obj_name,'${item.arr_field}' as arr_field,'${item.LText}' as LText`)
    let mutilparam = {
      func: 'sPC_Get_SelectedList',
      LText: mutilForms.join(' union all '),
      obj_name: '',
      arr_field: '',
      table_type: 'Y',
      exec_type: 'x'
    }
    mutilparam.LText = Utils.formatOptions(mutilparam.LText, 'x')
    mutilparam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    mutilparam.secretkey = Utils.encrypt('', mutilparam.timestamp)
    mutilparam.open_key = Utils.encryptOpenKey(mutilparam.secretkey, mutilparam.timestamp)
    if (window.GLOB.cloudServiceApi) { // 云端请求
      mutilparam.rduri = window.GLOB.cloudServiceApi
      mutilparam.userid = sessionStorage.getItem('CloudUserID') || ''
      mutilparam.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
    }
    Api.getSystemCacheConfig(mutilparam).then(res => {
      if (res.status) {
        this.setState({
          systemScripts: res.scripts.map(item => {
            return {
              name: item.funcname,
              value: window.decodeURIComponent(window.atob(item.longparam))
            }
          })
        })
      } else {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
      }
    })
  }
  scriptsChange = (values) => {
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.scripts = verify.scripts.map(item => {
        if (item.uuid === values.uuid) {
          return values
        } else {
          return item
        }
      })
    } else {
      values.uuid = Utils.getuuid()
      verify.scripts.push(values)
    }
    MKEmitter.emit('editLineId', values.uuid)
    this.setState({ verify })
  }
  cbScriptsChange = (values) => {
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.cbScripts = verify.cbScripts.map(item => {
        if (item.uuid === values.uuid) {
          return values
        } else {
          return item
        }
      })
    } else {
      values.uuid = Utils.getuuid()
      verify.cbScripts.push(values)
    }
    MKEmitter.emit('editLineId', values.uuid)
    this.setState({ verify })
  }
  handleDelete = (record, type) => {
    const { verify } = this.state
    if (type === 'scripts') {
      verify.scripts = verify.scripts.filter(item => item.uuid !== record.uuid)
    } else if (type === 'cbscripts') {
      verify.cbScripts = verify.cbScripts.filter(item => item.uuid !== record.uuid)
    }
    this.setState({ verify })
  }
  handleEdit = (record, type) => {
    let node = null
    if (type === 'scripts') {
      this.scriptsForm.edit(record)
      node = document.getElementById('mk-normal-script')
    } else if (type === 'cbscripts') {
      this.cbscriptsForm.edit(record)
      node = document.getElementById('mk-callback-script')
    }
    if (node && node.scrollTop) {
      let inter = Math.ceil(node.scrollTop / 10)
      let timer = setInterval(() => {
        if (node.scrollTop - inter > 0) {
          node.scrollTop = node.scrollTop - inter
        } else {
          node.scrollTop = 0
          clearInterval(timer)
        }
      }, 10)
    }
  }
  handleStatus = (record, type) => {
    let verify = fromJS(this.state.verify).toJS()
    record.status = record.status === 'false' ? 'true' : 'false'
    if (type === 'scripts') {
      verify.scripts = verify.scripts.map(item => {
        if (item.uuid === record.uuid) {
          return record
        } else {
          return item
        }
      })
    } else if (type === 'cbscripts') {
      verify.cbScripts = verify.cbScripts.map(item => {
        if (item.uuid === record.uuid) {
          return record
        } else {
          return item
        }
      })
    }
    this.setState({ verify })
  }
  showError = (errorType) => {
    const { verify } = this.state
    if (errorType === 'S') {
      let time = verify.stime || 2
      notification.success({
        top: 92,
        message: '执行成功!',
        duration: time
      })
    } else if (errorType === 'Y') {
      Modal.success({
        title: '执行成功!'
      })
    } else if (errorType === 'F') {
      notification.error({
        className: 'notification-custom-error',
        top: 92,
        message: '执行失败!',
        duration: verify.ftime || 10
      })
    } else if (errorType === 'N') {
      notification.error({
        top: 92,
        message: '执行失败!',
        duration: verify.ntime || 10
      })
    } else if (errorType === 'E') {
      Modal.error({
        title: '执行失败!'
      })
    } else if (errorType === 'NM') {
      message.error('执行失败!')
    }
  }
  timeChange = (val, type) => {
    const { verify } = this.state
    this.setState({
      verify: {...verify, [type]: val}
    })
  }
  handleConfirm = () => {
    const { activeKey } = this.state
    let verify = fromJS(this.state.verify).toJS()
    let msg = ''
    if (this.scriptsForm && this.scriptsForm.state.editItem) {
      msg = '前置脚本'
    } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
      msg = '前置脚本'
    } else if (this.cbscriptsForm && this.cbscriptsForm.state.editItem) {
      msg = '回调脚本'
    } else if (this.cbscriptsForm && this.cbscriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.cbscriptsForm.props.form.getFieldValue('sql'))) {
      msg = '回调脚本'
    }
    return new Promise((resolve, reject) => {
      if (activeKey === 'base' && verify.type === 'billout') {
        this.baseForm.handleConfirm().then(res => {
          let _verify = {...verify, ...res}
          if (msg) {
            confirm({
              content: msg + '未保存,确定提交吗?',
              onOk() {
                resolve(_verify)
              },
              onCancel() {}
            })
          } else {
            resolve(_verify)
          }
        })
      } else {
        if (msg) {
          confirm({
            content: msg + '未保存,确定提交吗?',
            onOk() {
              resolve(verify)
            },
            onCancel() {}
          })
        } else {
          resolve(verify)
        }
      }
    })
  }
  handleCancel = () => {
    const { verify, oriVerify } = this.state
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      if (!is(fromJS(verify), fromJS(oriVerify))) {
        confirm({
          content: '按钮信息已修改,确定取消吗?',
          onOk() {
            resolve()
          },
          onCancel() {}
        })
      } else {
        resolve()
      }
    })
  }
  changeTab = (val) => {
    const { activeKey, verify } = this.state
    if (activeKey === 'base') {
      this.baseForm.handleConfirm().then(res => {
        this.setState({
          verify: {...verify, ...res},
          activeKey: val
        })
      })
    } else {
      this.setState({
        activeKey: val
      })
    }
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
    const { card, columns } = this.props
    const { activeKey, verify, scriptsColumns, cbScriptsColumns } = this.state
    return (
      <div>
        <div className="mk-com-name">{card.label}</div>
        <Tabs activeKey={activeKey} className="mk-invoice-tabs" onChange={this.changeTab}>
          <TabPane tab="基础设置" key="base">
            <BaseForm columns={columns} verify={verify} onChange={(verify) => this.setState({verify})} wrappedComponentRef={(inst) => this.baseForm = inst}/>
          </TabPane>
          <TabPane tab={
            <span>
              前置脚本
              {verify.scripts.length ? <span className="count-tip">{verify.scripts.length}</span> : null}
            </span>
          } key="scripts" id="mk-normal-script">
            <FullScripts
              scripts={verify.scripts}
              getScriptsFullForm={() => this.scriptsFullForm}
              getScriptsForm={() => this.scriptsForm}
              handleStatus={this.handleStatus}
              handleDelete={this.handleDelete}
            >
              <CustomScript
                type="fullscreen"
                customScripts={verify.scripts}
                systemScripts={this.state.systemScripts}
                scriptsChange={this.scriptsChange}
                wrappedComponentRef={(inst) => this.scriptsFullForm = inst}
              />
            </FullScripts>
            <CustomScript
              customScripts={verify.scripts}
              systemScripts={this.state.systemScripts}
              scriptsChange={this.scriptsChange}
              wrappedComponentRef={(inst) => this.scriptsForm = inst}
            />
            <EditTable actions={['move']} data={verify.scripts} columns={scriptsColumns} onChange={(scripts) => {this.setState({verify: {...verify, scripts}})}}/>
          </TabPane>
          {card.type === 'billout' ? <TabPane tab={
            <span>
              回调脚本
              {verify.cbScripts.length ? <span className="count-tip">{verify.cbScripts.length}</span> : null}
            </span>
          } key="cbScripts" id="mk-callback-script">
            <FullScripts
              scripts={verify.cbScripts}
              getScriptsFullForm={() => this.cbscriptsFullForm}
              getScriptsForm={() => this.cbscriptsForm}
              handleStatus={(item) => this.handleStatus(item, 'cbscripts')}
              handleDelete={(item) => this.handleDelete(item, 'cbscripts')}
            >
              <CallBackCustomScript
                type="fullscreen"
                customScripts={verify.cbScripts}
                systemScripts={this.state.systemScripts}
                scriptsChange={this.cbScriptsChange}
                wrappedComponentRef={(inst) => this.cbscriptsFullForm = inst}
              />
            </FullScripts>
            <CallBackCustomScript
              customScripts={verify.cbScripts}
              systemScripts={this.state.systemScripts}
              scriptsChange={this.cbScriptsChange}
              wrappedComponentRef={(inst) => this.cbscriptsForm = inst}
            />
            <EditTable actions={['move']} data={verify.cbScripts} columns={cbScriptsColumns} onChange={(cbScripts) => {this.setState({verify: {...verify, cbScripts}})}}/>
          </TabPane> : null}
          <TabPane tab="sql示例" key="sql">
            <div className="mk-sql-wrap">
              <p className="note">{`/* 系统字段 */`}</p>
              <p>
                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), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
              </p>
              <p>
                Select @UserName='', @FullName='', @RoleID='', @mk_departmentcode='', @mk_organization='', @mk_user_type='', @mk_nation='', @mk_province='', @mk_city='', @mk_district='', @mk_address='', @ErrorCode='', @retmsg='', @account_id='', @account_year_id='', @account_code='', @account_year_code='', @bid=''
              </p>
              <p className="note">{`/* 发票主表字段 */`}</p>
              <p>
                Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50)
              </p>
              <p>
                Select @invoice_type='', @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=''
              </p>
              <p className="note">{`/* 发票明细临时表 */`}</p>
              <p>
                Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2))
              </p>
              <p>
                Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount)
              </p>
              <p>
                Select '', '', '', '', 0, 0, 0, '', '', 0, 0
              </p>
              <p className="note">{`/* 前置脚本 */`}</p>
              <p>
                ......
              </p>
              <p>
                aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg
              </p>
            </div>
          </TabPane>
          <TabPane tab="信息提示" key="tip">
            <Form className="tip-form">
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> S </span>
                    <Button onClick={() => {this.showError('S')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
                <Col span={8}>
                  <Form.Item label="停留时间">
                    <InputNumber defaultValue={verify.stime || 2} min={1} max={10000} precision={0} onChange={(val) => {this.timeChange(val, 'stime')}} />
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> Y </span>
                    <Button onClick={() => {this.showError('Y')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> -1 </span>
                    执行成功无提示。
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> N </span>
                    <Button onClick={() => {this.showError('N')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
                <Col span={8}>
                  <Form.Item label="停留时间">
                    <InputNumber defaultValue={verify.ntime || 10} min={1} max={10000} precision={0} onChange={(val) => {this.timeChange(val, 'ntime')}} />
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> F </span>
                    <Button onClick={() => {this.showError('F')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
                <Col span={8}>
                  <Form.Item label="停留时间">
                    <InputNumber defaultValue={verify.ftime || 10} min={1} max={10000} precision={0} onChange={(val) => {this.timeChange(val, 'ftime')}} />
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> E </span>
                    <Button onClick={() => {this.showError('E')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> NM </span>
                    <Button onClick={() => {this.showError('NM')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> -2 </span>
                    执行失败无提示
                  </Form.Item>
                </Col>
              </Row>
            </Form>
          </TabPane>
        </Tabs>
      </div>
    )
  }
}
export default Form.create()(VerifyCard)
src/menu/components/module/invoice/verifycard/index.scss
New file
@@ -0,0 +1,124 @@
.mk-invoice-tabs {
  .ant-tabs-nav .ant-tabs-tab {
    margin-right: 25px;
  }
  .ant-tabs-nav-scroll {
    text-align: center;
  }
  .ant-tabs-content {
    min-height: 40vh;
  }
  table tr td {
    word-wrap: break-word;
    word-break: break-word;
  }
  .count-tip {
    position: absolute;
    top: 0px;
    color: #1890ff;
    font-size: 12px;
  }
  .ant-input-disabled {
    color: rgba(0, 0, 0, 0.85);
    background-color: #ffffff;
    cursor: text;
  }
  .ant-radio-disabled + span {
    color: rgba(0, 0, 0, 0.85);
  }
  .base-form {
    padding-right: 50px;
    .ant-form-item {
      display: flex;
      .ant-form-item-control-wrapper {
        flex: 1;
      }
    }
    .ant-col-8 {
      .ant-form-item-label {
        width: 33.33%;
      }
    }
    .ant-col-24 {
      .ant-form-item-label {
        width: 10.8%;
      }
    }
  }
  .tip-form {
    .ant-form-item {
      display: flex;
    }
  }
  .mk-sql-wrap {
    color: rgba(0, 0, 0, 0.85);
    .note {
      margin-bottom: 0px;
    }
  }
  .verify-form {
    .sql {
      .ant-col-sm-8 {
        width: 10.5%;
      }
      .ant-col-sm-16 {
        width: 89.5%;
        padding-top: 4px;
      }
      .CodeMirror {
        height: 350px;
      }
    }
    .sqlfield {
      .ant-form-item {
        margin-bottom: 5px;
      }
      .ant-form-item-control {
        line-height: 24px;
      }
      .ant-form-item-label {
        line-height: 25px;
      }
      .ant-form-item-children {
        line-height: 22px;
      }
      .ant-col-sm-8 {
        width: 10.5%;
      }
      .ant-col-sm-16 {
        width: 89.5%;
      }
    }
    .add {
      padding-top: 4px;
      .mk-green {
        margin-bottom: 15px;
      }
    }
  }
  .custom-table .ant-empty {
    margin: 20px 8px!important;
  }
  .errorval {
    display: inline-block;
    width: 30px;
  }
  .operation-btn {
    display: inline-block;
    font-size: 16px;
    padding: 0 5px;
    cursor: pointer;
  }
  .operation-btn:not(:first-child) {
    margin-left: 5px;
  }
  .full-scripts {
    position: absolute;
    right: 24px;
    top: 0px;
    font-size: 16px;
    color: #1890ff;
    z-index: 1;
  }
}
src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -602,6 +602,7 @@
   * 3、切换标签类型,重置可选标签
   */
  optionChange = (key, value) => {
    const { type } = this.props
    const { hasclass, appType, requireOptions } = this.state
    this.record[key] = value
@@ -636,9 +637,16 @@
        _fieldval.label = '导出Excel'
        _fieldval.class = 'dgreen'
        _fieldval.execSuccess = 'never'
        _fieldval.Ot = 'requiredOnce'
        _fieldval.control = ''
        this.record.Ot = 'requiredOnce'
        if (type !== 'card') {
          _fieldval.Ot = 'requiredOnce'
          this.record.Ot = 'requiredOnce'
        } else {
          _fieldval.Ot = 'notRequired'
          this.record.Ot = 'notRequired'
        }
        this.record.label = '导出Excel'
        this.record.class = 'dgreen'
        this.record.execSuccess = 'never'
src/menu/components/share/actioncomponent/index.jsx
@@ -718,6 +718,7 @@
          destroyOnClose
        >
          <ActionForm
            type={config.type === 'tree' ? 'card' : ''}
            card={card}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
src/menu/components/tree/antd-tree/index.jsx
@@ -88,6 +88,7 @@
    card.name = card.wrap.name
    card.$c_ds = true
    card.$c_ac = true
      
    card.errors = checkComponent(card)
src/tabviews/custom/components/module/invoice/index.jsx
@@ -1,13 +1,13 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Select, Form, Input, Button, Modal, Spin } from 'antd'
import { Select, Form, Input, Button, Modal, Spin, notification } from 'antd'
import { EllipsisOutlined } from '@ant-design/icons'
import moment from 'moment'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
// import Utils from '@/utils/utils.js'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import InvoiceTable from './invoiceTable'
import SubTable from './subTable'
@@ -52,7 +52,10 @@
    details: [],
    book: null,
    loading: false,
    tax_type: ''
    saveType: '',
    tax_type: '',
    reqfields: [],
    requireds: []
  }
  UNSAFE_componentWillMount () {
@@ -242,8 +245,17 @@
  }
  resetParam = (book) => {
    let invTypes = book.invoice_type || []
    let invoice_type = sessionStorage.getItem('pre_invoice_type') || ''
    if (invoice_type && invTypes.findIndex(item => item.value === invoice_type) === -1) {
      invoice_type = ''
    }
    this.getRequired(invoice_type)
    return {
      invTypes: book.invoice_type || [],
      invoice_type,
      invTypes: invTypes,
      orgname: book.orgname || '',
      tax_no: book.tax_no || '',
      addr: book.addr || '',
@@ -295,7 +307,267 @@
  }
  changeType = (val) => {
    sessionStorage.setItem('pre_invoice_type', val)
    this.setState({invoice_type: val})
    this.getRequired(val)
  }
  getRequired = (invoice_type) => {
    if (!invoice_type) return
    let reqfields = []
    let requireds = []
    let rds = [
      {value: 'from_to_name', label: '购买方名称'},
      {value: 'from_to_tax_no', label: '购买方纳税人识别号'},
      {value: 'from_to_addr', label: '购买方地址'},
      {value: 'from_to_tel', label: '购买方电话'},
      {value: 'from_to_bank_name', label: '购买方开户行'},
      {value: 'from_to_account_no', label: '购买方账号'},
      {value: 'from_to_mob', label: '购买方手机号'},
      {value: 'from_to_email', label: '购买方邮箱'},
      {value: 'orgname', label: '销售方名称'},
      {value: 'tax_no', label: '销售方纳税人识别号'},
      {value: 'addr', label: '销售方地址'},
      {value: 'tel', label: '销售方电话'},
      {value: 'bank_name', label: '销售方开户行'},
      {value: 'account_no', label: '销售方账号'},
    ]
    if (invoice_type === 'e_general') {
      reqfields = ['from_to_name', 'from_to_tax_no', 'orgname', 'tax_no']
      rds.forEach(item => {
        if (reqfields.includes(item.value)) {
          requireds.push(item)
        }
      })
    } else if (invoice_type === 'e_special') {
      reqfields = ['from_to_name', 'from_to_tax_no', 'from_to_addr', 'from_to_tel', 'from_to_bank_name', 'from_to_account_no', 'orgname', 'tax_no', 'addr', 'tel', 'bank_name', 'account_no']
      rds.forEach(item => {
        if (reqfields.includes(item.value)) {
          requireds.push(item)
        }
      })
    }
    this.setState({
      reqfields,
      requireds
    })
  }
  saveBill = () => {
    const { config, saveType } = this.state
    if (saveType) return
    setTimeout(() => {
      this.getBillMsg().then(() => {
        let sql = this.getPreSql(config.billSaveBtn)
        let param = {
          func: 'sPC_TableData_InUpDe',
          LText: sql,
          exec_type: window.GLOB.execType || 'y',
          timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
        }
        param.secretkey = Utils.encrypt('', param.timestamp)
        param.LText = Utils.formatOptions(param.LText, param.exec_type)
        this.setState({
          saveType: 'bill'
        })
        Api.genericInterface(param).then(res => {
          if (res.status) {
            notification.success({
              top: 92,
              message: '保存成功。',
              duration: 5
            })
          } else {
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
          }
          this.setState({
            saveType: ''
          })
        })
      }, (error) => {
        notification.warning({
          top: 92,
          message: error,
          duration: 5
        })
        return
      })
    }, 20)
  }
  outBill = () => {
    const { config, saveType } = this.state
    if (saveType) return
    setTimeout(() => {
      this.getBillMsg().then(() => {
        let sql = this.getPreSql(config.billOutBtn)
        let param = {
          func: 'sPC_TableData_InUpDe',
          // BID: BID || '',
          LText: sql,
          key_back_type: 'Y',
          exec_type: window.GLOB.execType || 'y',
          timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
        }
        param.secretkey = Utils.encrypt('', param.timestamp)
        param.LText = Utils.formatOptions(param.LText, param.exec_type)
        console.info(sql)
      }, (error) => {
        notification.warning({
          top: 92,
          message: error,
          duration: 5
        })
        return
      })
    })
  }
  getPreSql = (btn) => {
    const { book, details, invoice_type, 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, reviewer, drawer, payee } = this.state
    let userName = sessionStorage.getItem('User_Name') || ''
    let fullName = sessionStorage.getItem('Full_Name') || ''
    let RoleID = sessionStorage.getItem('role_id') || ''
    let departmentcode = sessionStorage.getItem('departmentcode') || ''
    let organization = sessionStorage.getItem('organization') || ''
    let mk_user_type = sessionStorage.getItem('mk_user_type') || ''
    let nation = sessionStorage.getItem('nation') || ''
    let province = sessionStorage.getItem('province') || ''
    let city = sessionStorage.getItem('city') || ''
    let district = sessionStorage.getItem('district') || ''
    let address = sessionStorage.getItem('address') || ''
    let lines = details.map(line => `Select '${line.productcode}', '${line.productname}', '${line.spec}', '${line.unit}', ${line.bill_count}, ${line.unitprice}, ${line.amount_line}, '${line.tax_classify_code}', '${line.tax_classify_name}', ${line.tax_rate}, ${line.tax_amount}`)
    lines = lines.join(' union all ')
    let _script = ''
    btn.scripts.forEach(item => {
      if (item.status === 'false') return
      _script += `
      ${item.sql}
      `
    })
    let sql = `/* 系统字段 */
      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), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
      Select @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}', @ErrorCode='', @retmsg='', @account_id='${book.account_id || ''}', @account_year_id='${book.account_year_id || ''}', @account_code='${book.account_code || ''}', @account_year_code='${book.account_year_code || ''}', @bid=''
      /* 发票主表字段 */
      Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50)
      Select @invoice_type='${invoice_type}', @from_to_name='${from_to_name}', @from_to_tax_no='${from_to_tax_no}', @from_to_addr='${from_to_addr}', @from_to_tel='${from_to_tel}', @from_to_bank_name='${from_to_bank_name}', @from_to_account_no='${from_to_account_no}', @from_to_mob='${from_to_mob}', @from_to_email='${from_to_email}', @from_to_code='${from_to_code}', @orgname='${orgname}', @tax_no='${tax_no}', @addr='${addr}', @tel='${tel}', @bank_name='${bank_name}', @account_no='${account_no}', @remark='${remark}', @payee='${payee}', @reviewer='${reviewer}', @drawer='${drawer}'
      /* 发票明细临时表 */
      Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2))
      Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount)
      ${lines}
      /* 自定义脚本 */
      ${_script}
      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
    sql = sql.replace(/@ID@/ig, `''`)
    sql = sql.replace(/@BID@/ig, `''`)
    sql = sql.replace(/@LoginUID@/ig, `'${sessionStorage.getItem('LoginUID') || ''}'`)
    sql = sql.replace(/@SessionUid@/ig, `'${localStorage.getItem('SessionUid') || ''}'`)
    sql = sql.replace(/@UserID@/ig, `'${sessionStorage.getItem('UserID') || ''}'`)
    sql = sql.replace(/@Appkey@/ig, `'${window.GLOB.appkey || ''}'`)
    sql = sql.replace(/@typename@/ig, `'admin'`)
    if (window.GLOB.externalDatabase !== null) {
      sql = sql.replace(/@db@/ig, window.GLOB.externalDatabase)
    }
    if (sessionStorage.getItem('dataM') === 'true') { // 数据权限
      sql = sql.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, `'Y'`)
    } else {
      sql = sql.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, `''`)
    }
    if (window.GLOB.debugger === true) {
      console.info(sql.replace(/\n\s{6}/ig, '\n'))
    }
    return sql
  }
  getBillMsg = () => {
    const { requireds, invoice_type, details, remark, from_to_addr, addr } = this.state
    return new Promise((resolve, reject) => {
      let error = ''
      if (!invoice_type) {
        error = '请选择发票类型!'
      }
      if (!error) {
        requireds.forEach(item => {
          if (!this.state[item.value] && !error) {
            error = '请输入' + item.label + '!'
          }
        })
      }
      if (!error && remark.length > 512) {
        error = '备注不可超过512个字符!'
      }
      if (!error && from_to_addr.length > 100) {
        error = '购买方地址不可超过100个字符!'
      }
      if (!error && addr.length > 100) {
        error = '销售方地址不可超过100个字符!'
      }
      if (!error) {
        if (details.length === 0) {
          error = '请添加明细!'
        } else {
          details.forEach((line, index) => {
            if (error) return
            if (line.productcode) {
              if (!line.bill_count) {
                error = '明细第' + (index + 1) + '行,请输入数量!'
              } else if (!line.unitprice) {
                error = '明细第' + (index + 1) + '行,请输入单价!'
              }
            } else {
              error = '明细第' + (index + 1) + '行,请选择货物或应税劳务、服务名称!'
            }
          })
        }
      }
      if (error) {
        reject(error)
      } else {
        resolve()
      }
    })
  }
  changeBuyer = (item) => {
@@ -314,7 +586,7 @@
  }
  render() {
    const { config, book, loading, 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, tax_type } = this.state
    const { config, book, loading, invTypes, reqfields, saveType, date, invoice_type, 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, tax_type } = this.state
    if (!book || (config.wrap.datatype === 'dynamic' && !tax_no)) {
      return <div className="menu-invoice-wrap" style={config.style}>
@@ -334,15 +606,19 @@
          </div> : null
        }
        <div className="inv-action">
          <Button className="mk-bill">保存单据</Button>
          <Button className="mk-submit">提交开票</Button>
          <Button className="mk-bill" loading={saveType === 'bill'} onClick={this.saveBill}>保存单据</Button>
          <Button className="mk-submit" loading={saveType === 'out'} onClick={this.outBill}>提交开票</Button>
        </div>
        <div className="inv-header">
          <Select placeholder="请选择发票种类" onChange={this.changeType} dropdownClassName="inv-type-select">
          {invoice_type ? <Select defaultValue={invoice_type} onChange={this.changeType} dropdownClassName="inv-type-select">
            {invTypes.map(item => (
              <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
            ))}
          </Select>
          </Select> : <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">
@@ -350,17 +626,17 @@
            <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})}/>}>
                <Form.Item required={reqfields.includes('from_to_name')} 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="纳税人识别号">
                <Form.Item required={reqfields.includes('from_to_tax_no')} 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>话</>}>
                <Form.Item required={reqfields.includes('from_to_addr')} 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="开户行及账号">
                <Form.Item required={reqfields.includes('from_to_bank_name')} 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>
@@ -369,10 +645,10 @@
            <div className="inv-notice">
              <div className="inv-label">通知到</div>
              <div className="inv-content">
                <Form.Item label={<>手<span></span>机<span></span>号</>}>
                <Form.Item required={reqfields.includes('from_to_mob')} 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>箱</>}>
                <Form.Item required={reqfields.includes('from_to_email')} 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>
@@ -385,17 +661,17 @@
            <div className="inv-buyer">
              <div className="inv-label">销售方</div>
              <div className="inv-content">
                <Form.Item label={<>名<span></span>称</>}>
                <Form.Item required={reqfields.includes('orgname')} label={<>名<span></span>称</>}>
                  <Input placeholder="请输入销售方名称" value={orgname} autoComplete="off" onChange={(e) => this.setState({orgname: e.target.value})}/>
                </Form.Item>
                <Form.Item label="纳税人识别号">
                <Form.Item required={reqfields.includes('tax_no')} 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>话</>}>
                <Form.Item required={reqfields.includes('addr')} 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="开户行及账号">
                <Form.Item required={reqfields.includes('bank_name')} 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>
src/tabviews/custom/components/module/invoice/index.scss
@@ -191,8 +191,14 @@
      .inv-buyer {
        border-right: var(--inv-color, #13509c) 1px solid;
        .ant-form-item-label {
          width: 95px;
          min-width: 95px;
          width: 103px;
          min-width: 103px;
          label:not(.ant-form-item-required) {
            padding-left: 11px;
          }
          .ant-form-item-required::before {
            line-height: 30px;
          }
        }
      }
      .inv-notice {
src/tabviews/custom/components/tree/antd-tree/index.jsx
@@ -254,7 +254,7 @@
   * @param {*} position   // 刷新位置
   * @param {*} btn        // 执行的按钮
   */
  refreshByButtonResult = (menuId, position, btn) => {
  refreshByButtonResult = (menuId, position) => {
    const { config, BID } = this.state
    if (config.uuid !== menuId) return
@@ -591,7 +591,7 @@
  }
  render() {
    const { config, loading, treeNodes, expandedKeys, selectedKeys } = this.state
    const { BID, BData, config, loading, treeNodes, expandedKeys, selectedKeys } = this.state
    let extra = config.action && config.action.length > 0
@@ -607,6 +607,14 @@
          <span className={'title ' + (config.wrap.searchable !== 'true' ? 'search-unable' : '')}>{config.wrap.title}</span>
          {config.wrap.searchable === 'true' ? <Search allowClear onSearch={this.treeFilter} /> : null}
        </div> : null}
        {extra ? <MainAction
          BID={BID}
          setting={config.setting}
          actions={config.action}
          BData={BData}
          columns={config.columns}
          selectedData={[]}
        /> : null}
        {treeNodes && treeNodes.length > 0 ? <div className="tree-box" style={{height: config.wrap.contentHeight}}>
          <Tree
            blockNode