From 59002c8182d10c21a1becdff4e566ee11cd1cd91 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期二, 23 四月 2024 18:13:08 +0800
Subject: [PATCH] 2024-04-23

---
 src/menu/components/module/invoice/verifycard/customscript/index.jsx          |  284 ++++++++
 src/menu/components/module/invoice/verifycard/customscript/index.scss         |    0 
 src/menu/components/module/invoice/verifycard/baseform/index.jsx              |  110 +++
 src/menu/components/module/invoice/index.jsx                                  |   72 +
 src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss |    0 
 src/menu/components/module/invoice/verifycard/baseform/index.scss             |    0 
 src/tabviews/custom/components/tree/antd-tree/index.jsx                       |   12 
 src/menu/components/share/actioncomponent/index.jsx                           |    1 
 src/tabviews/custom/components/module/invoice/index.scss                      |   10 
 src/menu/components/share/actioncomponent/actionform/index.jsx                |   12 
 src/tabviews/custom/components/module/invoice/index.jsx                       |  314 ++++++++
 src/menu/components/tree/antd-tree/index.jsx                                  |    1 
 src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx  |  294 ++++++++
 src/menu/components/module/invoice/verifycard/index.scss                      |  124 +++
 src/menu/components/module/invoice/verifycard/index.jsx                       |  690 ++++++++++++++++++++
 15 files changed, 1,891 insertions(+), 33 deletions(-)

diff --git a/src/menu/components/module/invoice/index.jsx b/src/menu/components/module/invoice/index.jsx
index 344641e..6902bb1 100644
--- a/src/menu/components/module/invoice/index.jsx
+++ b/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骞碝M鏈�')
+    date: moment().format('YYYY骞碝M鏈�'),
+    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>
     )
   }
diff --git a/src/menu/components/module/invoice/verifycard/baseform/index.jsx b/src/menu/components/module/invoice/verifycard/baseform/index.jsx
new file mode 100644
index 0000000..e36d470
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/baseform/index.jsx
@@ -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="姝e紡鍦板潃">
+              {getFieldDecorator('proInterface', {
+                initialValue: verify.proInterface || '',
+              })(
+                <TextArea rows={2}/>
+              )}
+            </Form.Item>
+          </Col> : null}
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(BaseForm)
\ No newline at end of file
diff --git a/src/menu/components/module/invoice/verifycard/baseform/index.scss b/src/menu/components/module/invoice/verifycard/baseform/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/baseform/index.scss
diff --git a/src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx b/src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx
new file mode 100644
index 0000000..dbe1850
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx
@@ -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: '璇疯緭鍏ql!',
+          duration: 5
+        })
+        return
+      }
+
+      if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            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琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT銆丆NT銆�-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: '璇疯緭鍏ql!'
+                  }
+                ]
+              })(<CodeMirror />)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss b/src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss
diff --git a/src/menu/components/module/invoice/verifycard/customscript/index.jsx b/src/menu/components/module/invoice/verifycard/customscript/index.jsx
new file mode 100644
index 0000000..d7b10ff
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/customscript/index.jsx
@@ -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: '璇疯緭鍏ql!',
+          duration: 5
+        })
+        return
+      }
+      if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            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琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT銆丆NT銆�-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: '璇疯緭鍏ql!'
+                  }
+                ]
+              })(<CodeMirror />)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/menu/components/module/invoice/verifycard/customscript/index.scss b/src/menu/components/module/invoice/verifycard/customscript/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/customscript/index.scss
diff --git a/src/menu/components/module/invoice/verifycard/index.jsx b/src/menu/components/module/invoice/verifycard/index.jsx
new file mode 100644
index 0000000..b61b9ec
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/index.jsx
@@ -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)
\ No newline at end of file
diff --git a/src/menu/components/module/invoice/verifycard/index.scss b/src/menu/components/module/invoice/verifycard/index.scss
new file mode 100644
index 0000000..c05f0e1
--- /dev/null
+++ b/src/menu/components/module/invoice/verifycard/index.scss
@@ -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;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index a8028cf..7c83d79 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/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'
diff --git a/src/menu/components/share/actioncomponent/index.jsx b/src/menu/components/share/actioncomponent/index.jsx
index 4029b06..a70ce71 100644
--- a/src/menu/components/share/actioncomponent/index.jsx
+++ b/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}
diff --git a/src/menu/components/tree/antd-tree/index.jsx b/src/menu/components/tree/antd-tree/index.jsx
index b093ad7..6ae5917 100644
--- a/src/menu/components/tree/antd-tree/index.jsx
+++ b/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)
 
diff --git a/src/tabviews/custom/components/module/invoice/index.jsx b/src/tabviews/custom/components/module/invoice/index.jsx
index 7be4d48..17eda8d 100644
--- a/src/tabviews/custom/components/module/invoice/index.jsx
+++ b/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>
diff --git a/src/tabviews/custom/components/module/invoice/index.scss b/src/tabviews/custom/components/module/invoice/index.scss
index 3774eff..2d09086 100644
--- a/src/tabviews/custom/components/module/invoice/index.scss
+++ b/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 {
diff --git a/src/tabviews/custom/components/tree/antd-tree/index.jsx b/src/tabviews/custom/components/tree/antd-tree/index.jsx
index 1009ba9..361b59a 100644
--- a/src/tabviews/custom/components/tree/antd-tree/index.jsx
+++ b/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

--
Gitblit v1.8.0