From 8f78c97ffcde201e2a4d1ffa7ccb97a13f0f045d Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期四, 18 四月 2024 15:34:15 +0800
Subject: [PATCH] 2024-04-18

---
 src/menu/datasource/verifycard/utils.jsx                              |    4 
 src/menu/components/timeline/normal-timeline/index.scss               |    2 
 src/menu/components/carousel/prop-card/index.scss                     |    2 
 src/tabviews/custom/components/tree/antd-tree/index.jsx               |    6 
 src/menu/components/card/prop-card/index.scss                         |    2 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx      |   14 
 src/tabviews/custom/components/module/invoice/invoiceTable/index.scss |  127 +++
 src/menu/components/card/balcony/index.scss                           |    2 
 src/menu/components/card/data-card/index.scss                         |    2 
 src/tabviews/custom/components/module/invoice/index.jsx               |  377 +++++++++
 src/menu/menushell/card.jsx                                           |    3 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx  |   35 
 src/menu/datasource/verifycard/index.jsx                              |   31 
 src/menu/modulesource/option.jsx                                      |    2 
 src/tabviews/custom/components/module/invoice/subTable/index.scss     |    0 
 src/menu/components/module/invoice/options.jsx                        |   81 ++
 src/menu/components/module/invoice/index.jsx                          |  419 ++++++++++
 src/menu/datasource/verifycard/settingform/index.jsx                  |   12 
 src/tabviews/custom/components/module/invoice/invoiceTable/index.jsx  |  394 ++++++++++
 src/tabviews/custom/components/module/invoice/index.scss              |  260 ++++++
 src/tabviews/custom/components/module/invoice/subTable/index.jsx      |  245 ++++++
 src/tabviews/custom/index.jsx                                         |    7 
 src/menu/debug/index.jsx                                              |    2 
 src/menu/components/carousel/data-card/index.scss                     |    2 
 src/menu/components/module/invoice/index.scss                         |  258 ++++++
 src/assets/img/invoice.png                                            |    0 
 src/menu/components/card/double-data-card/index.scss                  |    2 
 27 files changed, 2,243 insertions(+), 48 deletions(-)

diff --git a/src/assets/img/invoice.png b/src/assets/img/invoice.png
new file mode 100644
index 0000000..3a5185d
--- /dev/null
+++ b/src/assets/img/invoice.png
Binary files differ
diff --git a/src/menu/components/card/balcony/index.scss b/src/menu/components/card/balcony/index.scss
index 418b226..efe4ce6 100644
--- a/src/menu/components/card/balcony/index.scss
+++ b/src/menu/components/card/balcony/index.scss
@@ -32,7 +32,7 @@
   }
 
   .card-item {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-color: #ffffff;
     background-position: center center;
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index ae23509..efc66b8 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -118,12 +118,14 @@
         } else if (item.key === 'formula') {
           item.fields = []
 
-          config.columns.forEach(col => {
-            item.fields.push(col.field)
-          })
-          
-          if (config.subColumns) {
-            config.subColumns.forEach(col => {
+          if (side === 'sub') {
+            if (config.subColumns) {
+              config.subColumns.forEach(col => {
+                item.fields.push(col.field)
+              })
+            }
+          } else {
+            config.columns.forEach(col => {
               item.fields.push(col.field)
             })
           }
diff --git a/src/menu/components/card/data-card/index.scss b/src/menu/components/card/data-card/index.scss
index 8c298e3..43ac9dc 100644
--- a/src/menu/components/card/data-card/index.scss
+++ b/src/menu/components/card/data-card/index.scss
@@ -20,7 +20,7 @@
   }
 
   .card-item {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-color: #ffffff;
     background-position: center center;
diff --git a/src/menu/components/card/double-data-card/index.scss b/src/menu/components/card/double-data-card/index.scss
index 40ea29c..722a29f 100644
--- a/src/menu/components/card/double-data-card/index.scss
+++ b/src/menu/components/card/double-data-card/index.scss
@@ -20,7 +20,7 @@
   }
 
   .card-item, .card-item-wrap {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-color: #ffffff;
     background-position: center center;
diff --git a/src/menu/components/card/prop-card/index.scss b/src/menu/components/card/prop-card/index.scss
index 5e0c1e5..21c1ad6 100644
--- a/src/menu/components/card/prop-card/index.scss
+++ b/src/menu/components/card/prop-card/index.scss
@@ -19,7 +19,7 @@
   }
 
   .card-item {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-color: #ffffff;
     background-position: center center;
diff --git a/src/menu/components/carousel/data-card/index.scss b/src/menu/components/carousel/data-card/index.scss
index 7d9fda3..7bbacd7 100644
--- a/src/menu/components/carousel/data-card/index.scss
+++ b/src/menu/components/carousel/data-card/index.scss
@@ -19,7 +19,7 @@
   }
 
   .card-item {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-position: center center;
     background-repeat: no-repeat;
diff --git a/src/menu/components/carousel/prop-card/index.scss b/src/menu/components/carousel/prop-card/index.scss
index 07e359f..6c50965 100644
--- a/src/menu/components/carousel/prop-card/index.scss
+++ b/src/menu/components/carousel/prop-card/index.scss
@@ -19,7 +19,7 @@
   }
 
   .card-item {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-position: center center;
     background-repeat: no-repeat;
diff --git a/src/menu/components/module/invoice/index.jsx b/src/menu/components/module/invoice/index.jsx
new file mode 100644
index 0000000..0885bec
--- /dev/null
+++ b/src/menu/components/module/invoice/index.jsx
@@ -0,0 +1,419 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Popover, Button } from 'antd'
+import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons'
+import moment from 'moment'
+
+import Utils from '@/utils/utils.js'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import { getTables, checkComponent } from '@/utils/utils-custom.js'
+import MKEmitter from '@/utils/events.js'
+import getWrapForm from './options'
+
+import './index.scss'
+
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+
+class Invoice extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    card: null,
+    date: moment().format('YYYY骞碝M鏈�')
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        format: 'object',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: false,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        width: card.width || 24,
+        name: '鍙戠エ',
+        subtype: card.subtype,
+        wrap: { name: '鍙戠エ', width: card.width || 24, datatype: 'static' },
+        style: { paddingLeft: '20px', paddingRight: '20px', paddingTop: '10px', paddingBottom: '10px' },
+        setting: { interType: 'system' },
+        columns: [],
+        scripts: [],
+        buyer: {
+          pageable: true,
+          format: 'array',
+          subtype: 'invTable',
+          setting: { interType: 'system' },
+          columns: [],
+          scripts: [],
+          search: [
+            {field: 'from_to_name', label: '浼佷笟鍚嶇О', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
+            {field: 'from_to_tax_no', label: '浼佷笟绋庡彿', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
+          ],
+        },
+        detail: {
+          pageable: true,
+          format: 'array',
+          subtype: 'invTable',
+          setting: { interType: 'system' },
+          columns: [],
+          scripts: [],
+          search: [
+            {field: 'productname', label: '鍟嗗搧鍚嶇О', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
+            {field: 'productcode', label: '鍟嗗搧缂栫爜', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
+          ],
+        }
+      }
+
+      let buys = [
+        ['浼佷笟鍚嶇О', 'from_to_name'],
+        ['浼佷笟绋庡彿', 'from_to_tax_no'],
+        ['鍦板潃', 'from_to_addr', 'Nvarchar(100)'],
+        ['鐢佃瘽', 'from_to_tel'],
+        ['寮�鎴疯', 'from_to_bank_name'],
+        ['閾惰璐﹀彿', 'from_to_account_no'],
+        ['鎵嬫満鍙�', 'from_to_mob'],
+        ['閭', 'from_to_email'],
+        ['浼佷笟缂栫爜', 'from_to_code']
+      ]
+
+      buys.forEach((cell, index) => {
+        _card.buyer.columns.push({$index: index + 1, datatype: 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()})
+      })
+
+      let details = [
+        ['id', 'id'],
+        ['鍟嗗搧缂栫爜', 'productcode'],
+        ['鍟嗗搧鍚嶇О', 'productname'],
+        ['瑙勬牸鍨嬪彿', 'spec'],
+        ['鎻忚堪', 'Description'],
+        ['璁¢噺鍗曚綅', 'unit'],
+        ['鍗曚环', 'unitprice', 'Decimal(18,10)'],
+        ['绋庡姟鍒嗙被缂栫爜', 'tax_classify_code'],
+        ['绋庡姟鍒嗙被鍚嶇О', 'tax_classify_name'],
+        ['绋庣巼', 'tax_rate'],
+      ]
+
+      details.forEach((cell, index) => {
+        _card.detail.columns.push({$index: index + 1, datatype: cell[2] || 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()})
+      })
+
+      let cols = [
+        ['鍙戠エ绉嶇被', 'invoice_type'],
+        ['璐拱鏂瑰悕绉�', 'from_to_name'],
+        ['璐拱鏂圭◣鍙�', 'from_to_tax_no'],
+        ['璐拱鏂瑰湴鍧�', 'from_to_addr', 'Nvarchar(100)'],
+        ['璐拱鏂圭數璇�', 'from_to_tel'],
+        ['璐拱鏂瑰紑鎴疯', 'from_to_bank_name'],
+        ['璐拱鏂归摱琛岃处鍙�', 'from_to_account_no'],
+        ['璐拱鏂规墜鏈哄彿', 'from_to_mob'],
+        ['璐拱鏂归偖绠�', 'from_to_email'],
+        ['璐拱鏂圭紪鐮�', 'from_to_code'],
+        ['閿�鍞柟鍚嶇О', 'orgname'],
+        ['閿�鍞柟绋庡彿', 'tax_no'],
+        ['閿�鍞柟鍦板潃', 'addr', 'Nvarchar(100)'],
+        ['閿�鍞柟鐢佃瘽', 'tel'],
+        ['閿�鍞柟寮�鎴疯', 'bank_name'],
+        ['閿�鍞柟閾惰璐﹀彿', 'account_no'],
+        ['澶囨敞', 'remark', 'Nvarchar(512)'],
+        ['鏀舵浜�', 'payee'],
+        ['澶嶆牳浜�', 'reviewer'],
+        ['寮�绁ㄤ汉', 'drawer'],
+        ['鍟嗗搧缂栫爜', 'productcode'],
+        ['鍟嗗搧鍚嶇О', 'productname'],
+        ['瑙勬牸鍨嬪彿', 'spec'],
+        ['璁¢噺鍗曚綅', 'unit'],
+        ['鏁伴噺', 'bill_count', 'Decimal(18,10)'],
+        ['鍗曚环', 'unitprice', 'Decimal(18,10)'],
+        ['閲戦', 'amount_line', 'Decimal(18,2)'],
+        ['绋庡姟鍒嗙被缂栫爜', 'tax_classify_code'],
+        ['绋庡姟鍒嗙被鍚嶇О', 'tax_classify_name'],
+        ['绋庣巼', 'tax_rate', 'Decimal(18,2)'],
+        ['绋庨', 'tax_amount', 'Decimal(18,2)'],
+      ]
+
+      cols.forEach((cell, index) => {
+        _card.columns.push({$index: index + 1, datatype: cell[2] || 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()})
+      })
+
+      this.updateComponent(_card)
+    } else {
+      let _card = fromJS(card).toJS()
+
+      // _card.buyer.format = 'array'
+      // _card.detail.format = 'array'
+
+      this.setState({
+        card: _card
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  /**
+   * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
+   */
+  updateComponent = (card) => {
+    card.width = card.wrap.width
+    card.name = card.wrap.name
+    
+    if (window.GLOB.styling && card.errors) { // 鏍峰紡淇敼鏃朵笉鍋氱瓫鏌�
+      this.setState({
+        card: card
+      })
+  
+      this.props.updateConfig(card)
+      return
+    }
+
+    card.$c_ds = card.wrap.datatype === 'dynamic'
+    card.errors = checkComponent(card)
+
+    if (card.errors.length === 0) {
+      if (card.buyer.setting.interType === 'system' && card.buyer.setting.execute !== 'false' && !card.buyer.setting.dataresource) {
+        card.errors.push({ level: 0, detail: '-璐拱鏂� 鏈缃暟鎹簮锛�'})
+      } else if (card.buyer.setting.interType === 'system' && card.buyer.setting.execute === 'false' && card.buyer.scripts.filter(script => script.status !== 'false').length === 0) {
+        card.errors.push({ level: 0, detail: '-璐拱鏂� 鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+      }
+    }
+    if (card.errors.length === 0) {
+      if (card.detail.setting.interType === 'system' && card.detail.setting.execute !== 'false' && !card.detail.setting.dataresource) {
+        card.errors.push({ level: 0, detail: '-鏄庣粏 鏈缃暟鎹簮锛�'})
+      } else if (card.detail.setting.interType === 'system' && card.detail.setting.execute === 'false' && card.detail.scripts.filter(script => script.status !== 'false').length === 0) {
+        card.errors.push({ level: 0, detail: '-鏄庣粏 鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+      }
+    }
+
+    if (card.errors.length === 0) {
+      card.$tables = getTables(card)
+      card.$tables = [...card.$tables, ...getTables(card.buyer)]
+      card.$tables = [...card.$tables, ...getTables(card.detail)]
+    }
+
+    this.setState({
+      card: card
+    })
+
+    this.props.updateConfig(card)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
+  }
+
+  getStyle = (style) => {
+    let _card = {...this.state.card, style}
+    
+    this.updateComponent(_card)
+  }
+
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
+  render() {
+    const { card, date } = this.state
+
+    let style = {...card.style}
+    if (card.wrap.invColor) {
+      style['--inv-color'] = card.wrap.invColor
+    }
+
+    return (
+      <div className="menu-invoice-box" style={style} id={card.uuid}>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <NormalForm title="鍩烘湰璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
+            <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
+            <DeleteOutlined className="close" title="鍒犻櫎缁勪欢" onClick={() => this.props.deletecomponent(card.uuid)} />
+            {card.wrap.datatype === 'dynamic' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : <SettingOutlined style={{color: '#eeeeee', cursor: 'not-allowed'}}/>}
+          </div>
+        } trigger="hover">
+          <ToolOutlined />
+        </Popover>
+        <div className="inv-action">
+          <Button className="mk-bill">淇濆瓨鍗曟嵁</Button>
+          <Button className="mk-submit">鎻愪氦寮�绁�</Button>
+        </div>
+        <div className="inv-header">
+          <div className="mk-select">璇烽�夋嫨鍙戠エ绉嶇被</div>
+          <div className="date">寮�绁ㄦ棩鏈燂細{date}</div>
+        </div>
+        <div className="inv-body">
+          <div className="inv-main-content">
+            <div className="inv-buyer">
+              <div className="inv-label">璐拱鏂�</div>
+              <div className="inv-content">
+                <div className="mk-input">
+                  <label>鍚嶇О锛�</label>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟鍚嶇О</span>
+                  <span className="extra">
+                    <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+                      <div className="mk-popover-control">
+                        <SettingComponent config={card.buyer} updateConfig={(res) => this.updateComponent({...card, buyer: res})} />
+                      </div>
+                    } trigger="hover">
+                      <EllipsisOutlined />
+                    </Popover>
+                  </span>
+                </div>
+                <div className="mk-input">
+                  <label>绾崇◣浜鸿瘑鍒彿锛�</label>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟绾崇◣浜鸿瘑鍒彿</span>
+                </div>
+                <div className="mk-input">
+                  <label>鍦板潃銆佺數璇濓細</label>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟鍦板潃</span>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟鐢佃瘽</span>
+                </div>
+                <div className="mk-input">
+                  <label>寮�鎴疯鍙婅处鍙凤細</label>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟寮�鎴疯</span>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟璐﹀彿</span>
+                </div>
+              </div>
+            </div>
+            <div className="inv-notice">
+              <div className="inv-label">閫氱煡鍒�</div>
+              <div className="inv-content">
+                <div className="mk-input">
+                  <label>鎵嬫満鍙凤細</label>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟鎵嬫満鍙�</span>
+                </div>
+                <div className="mk-input">
+                  <label>閭锛�</label>
+                  <span className="content">璇疯緭鍏ヨ喘涔版柟閭</span>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div className="inv-details">
+            <div className="detail-wrap">
+              <div className="mk-th">
+                <div className="mk-td">璐х墿鎴栧簲绋庡姵鍔°�佹湇鍔″悕绉�</div>
+                <div className="mk-td">瑙勬牸鍨嬪彿</div>
+                <div className="mk-td">鍗曚綅</div>
+                <div className="mk-td">鏁伴噺</div>
+                <div className="mk-td">鍗曚环锛堝惈绋庯級</div>
+                <div className="mk-td">閲戦锛堝惈绋庯級</div>
+                <div className="mk-td">绋庣巼</div>
+                <div className="mk-td">绋庨</div>
+              </div>
+              <div className="mk-tr">
+                <div className="mk-td mk-left">
+                  <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+                    <div className="mk-popover-control">
+                      <SettingComponent config={card.detail} updateConfig={(res) => this.updateComponent({...card, detail: res})} />
+                    </div>
+                  } trigger="hover">
+                    <EllipsisOutlined />
+                  </Popover>
+                </div>
+                <div className="mk-td mk-left"></div>
+                <div className="mk-td mk-left"></div>
+                <div className="mk-td mk-right"></div>
+                <div className="mk-td mk-right"></div>
+                <div className="mk-td mk-right"></div>
+                <div className="mk-td mk-right"></div>
+                <div className="mk-td mk-right"></div>
+              </div>
+              <div className="mk-total">
+                <div className="mk-td">鍚堣</div>
+                <div className="mk-td"></div>
+                <div className="mk-td"></div>
+                <div className="mk-td"></div>
+                <div className="mk-td"></div>
+                <div className="mk-td">锟�12</div>
+                <div className="mk-td"></div>
+                <div className="mk-td">锟�1</div>
+              </div>
+              <div className="mk-upcase">
+                <div className="mk-td">浠风◣鍚堣锛堝ぇ鍐欙級</div>
+                <div className="mk-td"></div>
+                <div className="mk-td">锛堝皬鍐欙級锟�12</div>
+              </div>
+            </div>
+          </div>
+          <div className="inv-main-content">
+            <div className="inv-buyer">
+              <div className="inv-label">閿�鍞柟</div>
+              <div className="inv-content">
+                <div className="mk-input">
+                  <label>鍚嶇О锛�</label>
+                  <span className="content">璇疯緭鍏ラ攢鍞柟鍚嶇О</span>
+                </div>
+                <div className="mk-input">
+                  <label>绾崇◣浜鸿瘑鍒彿锛�</label>
+                  <span className="content">璇疯緭鍏ラ攢鍞柟绾崇◣浜鸿瘑鍒彿</span>
+                </div>
+                <div className="mk-input">
+                  <label>鍦板潃銆佺數璇濓細</label>
+                  <span className="content">璇疯緭鍏ラ攢鍞柟鍦板潃</span>
+                  <span className="content">璇疯緭鍏ラ攢鍞柟鐢佃瘽</span>
+                </div>
+                <div className="mk-input">
+                  <label>寮�鎴疯鍙婅处鍙凤細</label>
+                  <span className="content">璇疯緭鍏ラ攢鍞柟寮�鎴疯</span>
+                  <span className="content">璇疯緭鍏ラ攢鍞柟璐﹀彿</span>
+                </div>
+              </div>
+            </div>
+            <div className="inv-notice">
+              <div className="inv-label">澶囨敞</div>
+              <div className="inv-content" style={{paddingTop: '30px'}}>
+                <div className="mk-input">
+                  <span className="content" style={{height: '80px'}}>璇疯緭鍏ュ娉�</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div className="inv-tail">
+          <div className="mk-input">
+            <label>鏀舵浜猴細</label>
+            <span className="content">鏀舵浜�</span>
+          </div>
+          <div className="mk-input">
+            <label>澶嶆牳浜猴細</label>
+            <span className="content">澶嶆牳浜�</span>
+          </div>
+          <div className="mk-input">
+            <label>寮�绁ㄤ汉锛�</label>
+            <span className="content">寮�绁ㄤ汉</span>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default Invoice
\ No newline at end of file
diff --git a/src/menu/components/module/invoice/index.scss b/src/menu/components/module/invoice/index.scss
new file mode 100644
index 0000000..13e4eb7
--- /dev/null
+++ b/src/menu/components/module/invoice/index.scss
@@ -0,0 +1,258 @@
+.menu-invoice-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  color: #000000;
+  --inv-color: #13509c;
+
+  .anticon-tool {
+    position: absolute;
+    z-index: 2;
+    font-size: 16px;
+    right: 1px;
+    top: 1px;
+    cursor: pointer;
+    padding: 5px;
+    background: rgba(255, 255, 255, 0.55);
+  }
+
+  .inv-action {
+    text-align: right;
+    margin-right: 30px;
+    .ant-btn {
+      margin-left: 15px;
+      margin-bottom: 5px;
+      height: 30px;
+    }
+    .mk-bill:hover, .mk-bill:active, .mk-bill:focus {
+      color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+    }
+    .mk-submit, .mk-submit:hover, .mk-submit:active, .mk-submit:focus {
+      color: #fff;
+      background-color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+    }
+  }
+  .inv-header {
+    text-align: center;
+    position: relative;
+    height: 70px;
+    margin-right: 30px;
+    .mk-select {
+      position: relative;
+      display: inline-block;
+      width: 390px;
+      font-size: 25px;
+      text-align: center;
+      font-family: kaiti;
+      color: var(--inv-color, #13509c);
+    }
+    .mk-select::before, .mk-select::after {
+      content: '';
+      display: block;
+      width: 100%;
+      position: absolute;
+      border-top: var(--inv-color, #13509c) 1px solid;
+      border-bottom: var(--inv-color, #13509c) 1px solid;
+      height: 1px;
+    }
+    .mk-select::before {
+      bottom: -10px;
+    }
+    .mk-select::after {
+      bottom: -15px;
+    }
+    .date {
+      position: absolute;
+      right: 100px;
+      top: 5px;
+      color: var(--inv-color, #13509c);
+    }
+  }
+
+  .mk-input {
+    position: relative;
+    height: 28px;
+    font-size: 13px;
+    background: transparent;
+    display: flex;
+    padding: 0 5px 0 15px;
+    margin-bottom: 5px;
+    label {
+      display: inline-block;
+      height: 28px;
+      line-height: 28px;
+      text-align: right;
+      color: var(--inv-color, #13509c);
+    }
+    span {
+      display: inline-block;
+      height: 28px;
+      line-height: 28px;
+    }
+    .extra {
+      position: absolute;
+      right: 0px;
+      .anticon-ellipsis {
+        padding: 0 15px;
+      }
+    }
+    .content {
+      flex: 1;
+      border-bottom: 1px solid #d9d9d9;
+      color: #b8b8b8;
+      transition: all 0.3s;
+      padding: 0px 10px;
+    }
+  }
+  .mk-input:hover .content {
+    border-color: var(--inv-color, #13509c);
+  }
+
+  .inv-body {
+    border: var(--inv-color, #13509c) 1px solid;
+    font-size: 13px;
+    margin-right: 30px;
+
+    .inv-main-content {
+      display: flex;
+
+      .inv-buyer, .inv-notice {
+        width: 50%;
+        display: flex;
+        .inv-label {
+          color: var(--inv-color, #13509c);
+          width: 6.25%;
+          display: flex;
+          flex-direction: column;
+          writing-mode: vertical-rl;
+          justify-content: center;
+          align-items: center;
+          letter-spacing: 5px;
+          border-right: var(--inv-color, #13509c) 1px solid;
+        }
+        .inv-content {
+          flex: 1;
+          padding: 8px 0;
+        }
+      }
+      .inv-buyer {
+        border-right: var(--inv-color, #13509c) 1px solid;
+        label {
+          width: 95px;
+          min-width: 95px;
+        }
+      }
+      .inv-notice {
+        .inv-content {
+          padding-top: 45px;
+        }
+        label {
+          width: 75px;
+          min-width: 75px;
+        }
+      }
+    }
+
+    .inv-details {
+      border-top: var(--inv-color, #13509c) 1px solid;
+      border-bottom: var(--inv-color, #13509c) 1px solid;
+    }
+  }
+  .inv-tail {
+    display: flex;
+    padding-top: 10px;
+    margin-right: 30px;
+    .mk-input {
+      display: flex;
+      flex: 1;
+      label {
+        width: 40%;
+        color: rgba(0, 0, 0, 0.85);
+      }
+      span {
+        width: 40%;
+        flex: none;
+      }
+    }
+  }
+
+  .detail-wrap {
+    position: relative;
+  
+    .mk-th, .mk-tr, .mk-total {
+      position: relative;
+      display: flex;
+      border-bottom: var(--inv-color, #13509c) 1px solid;
+  
+      .mk-td {
+        width: 10%;
+        height: 40px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        padding: 0 3px;
+        position: relative;
+      }
+      .mk-td:not(:last-child) {
+        border-right: var(--inv-color, #13509c) 1px solid;
+      }
+      .mk-td:first-child {
+        width: 20%;
+      }
+      .mk-td:nth-child(2), .mk-td:nth-child(6) {
+        width: 15%;
+      }
+  
+      .mk-left {
+        justify-content: start;
+      }
+      .mk-right {
+        justify-content: end;
+      }
+      .anticon-ellipsis {
+        padding: 0 10px;
+        position: absolute;
+        right: 0;
+      }
+    }
+    .mk-tr.active, .mk-tr:hover {
+      background: var(--mk-sys-color1);
+    }
+    .mk-upcase {
+      display: flex;
+  
+      .mk-td {
+        width: 40%;
+        height: 40px;
+        display: flex;
+        align-items: center;
+        padding: 0 20px;
+      }
+      .mk-td:first-child {
+        width: 20%;
+        padding: 0;
+        border-right: var(--inv-color, #13509c) 1px solid;
+        justify-content: center;
+      }
+      .mk-td:last-child {
+        justify-content: end;
+      }
+    }
+  }
+}
+.menu-invoice-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-invoice-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
+
+
diff --git a/src/menu/components/module/invoice/options.jsx b/src/menu/components/module/invoice/options.jsx
new file mode 100644
index 0000000..29284b3
--- /dev/null
+++ b/src/menu/components/module/invoice/options.jsx
@@ -0,0 +1,81 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap) {
+  let menu = window.GLOB.customMenu
+  let books = []
+  let bookids = []
+  menu.components.forEach(item => {
+    if (item.subtype === 'account') {
+      books.push({
+        value: item.uuid,
+        label: item.name
+      })
+      bookids.push(item.uuid)
+    }
+  })
+
+  const wrapForm = [
+    {
+      type: 'radio',
+      field: 'datatype',
+      label: '绫诲瀷',
+      initval: wrap.datatype || 'static',
+      required: true,
+      options: [
+        {value: 'static', label: '鏂板鍙戠エ'},
+        {value: 'dynamic', label: '鏌ョ湅鍗曟嵁'},
+      ]
+    },
+    {
+      type: 'text',
+      field: 'name',
+      label: '缁勪欢鍚嶇О',
+      initval: wrap.name || '',
+      tooltip: '鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'width',
+      label: '瀹藉害',
+      initval: wrap.width || 24,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'select',
+      field: 'supBook',
+      label: '璐﹀',
+      initval: wrap.supBook || '',
+      required: true,
+      options: books,
+      allowClear: true
+    },
+    {
+      type: 'radio',
+      field: 'business_type',
+      label: '鍙戠エ绫诲瀷',
+      initval: wrap.business_type || 'sell',
+      required: true,
+      options: [
+        {value: 'sell', label: '閿�椤瑰彂绁�'},
+        {value: 'buy', label: '杩涢」鍙戠エ'},
+      ]
+    },
+    {
+      type: 'color',
+      field: 'invColor',
+      label: '鏍峰紡',
+      initval: wrap.invColor || '',
+      tooltip: '鍙戠エ缁勪欢涓竟妗嗕互鍙婃枃瀛楃殑棰滆壊銆�',
+      allowClear: true,
+      required: false
+    },
+  ]
+
+  return wrapForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/timeline/normal-timeline/index.scss b/src/menu/components/timeline/normal-timeline/index.scss
index 2f88860..51b98ae 100644
--- a/src/menu/components/timeline/normal-timeline/index.scss
+++ b/src/menu/components/timeline/normal-timeline/index.scss
@@ -23,7 +23,7 @@
   }
 
   .card-item {
-    overflow: hidden;
+    // overflow: hidden;
     position: relative;
     background-color: #ffffff;
     background-position: center center;
diff --git a/src/menu/datasource/verifycard/index.jsx b/src/menu/datasource/verifycard/index.jsx
index a8300a1..d083aa3 100644
--- a/src/menu/datasource/verifycard/index.jsx
+++ b/src/menu/datasource/verifycard/index.jsx
@@ -678,7 +678,8 @@
       if (config.subtype === 'dualdatacard') {
         _columns = [...columns, ...subColumns]
       }
-      let r = SettingUtils.getDebugSql(setting, _scripts, _columns, searches, config.type)
+
+      let r = SettingUtils.getDebugSql(setting, _scripts, _columns, searches, config.subtype)
 
       let _debugId = md5(r.sql)
       
@@ -958,7 +959,8 @@
   }
 
   copyColumns = () => {
-    const { columns } = this.state
+    const { columns, setting } = this.state
+
     let m = []
     let n = []
     let s = []
@@ -982,8 +984,27 @@
     }
 
     let oInput = document.createElement('input')
-    oInput.value = `/*${m.join(',')}*/
-      ${n.join(',')}`
+    oInput.value = `create table #${setting.tableName || 'tb'}
+    (${m.join(',')},sort_id INT IDENTITY(1,1))
+    insert into #${setting.tableName || 'tb'}
+    (${n.join(',')})
+    select ${n.join(',')}
+    from ${setting.dataresource ? `(${setting.dataresource}) tb` : setting.tableName || 'tb'}
+    order by @orderBy@
+    
+    declare @mk_total int
+    set @mk_total = 0
+      
+    select @mk_total = count(1) from #${setting.tableName || 'tb'}
+      declare @pageIndex_top int 
+      set @pageIndex_top=(@pageIndex@-1)*@pageSize@ 
+      
+      if @mk_total > @pageIndex@*@pageSize@
+        delete #${setting.tableName || 'tb'} where sort_id > @pageIndex@*@pageSize@ 
+      
+      if @pageIndex_top > 0
+        delete #${setting.tableName || 'tb'} where sort_id <= @pageIndex_top`
+
     document.body.appendChild(oInput)
     oInput.select()
     document.execCommand('Copy')
@@ -1134,7 +1155,7 @@
               wrappedComponentRef={(inst) => this.settingForm = inst}
             /> : null}
           </TabPane>
-          {config.subtype !== 'basetable' ? <TabPane tab={
+          {!['basetable', 'invoice', 'invTable'].includes(config.subtype) ? <TabPane tab={
             <span>
               瀛楁闆�
               {columns.length ? <span className="count-tip">{columns.length}</span> : null}
diff --git a/src/menu/datasource/verifycard/settingform/index.jsx b/src/menu/datasource/verifycard/settingform/index.jsx
index 8433cc5..560e5d9 100644
--- a/src/menu/datasource/verifycard/settingform/index.jsx
+++ b/src/menu/datasource/verifycard/settingform/index.jsx
@@ -375,7 +375,7 @@
               </Form.Item>
             </Col> : null}
             {setting.interType === 'system' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
-              {window.GLOB.process ? <span className="process-btn" onClick={this.addProcess}>宸ヤ綔娴�</span> : null}
+              {window.GLOB.process && !['invoice', 'invTable'].includes(config.subtype) ? <span className="process-btn" onClick={this.addProcess}>宸ヤ綔娴�</span> : null}
               <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 2 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 22 }} } label={
                 <Tooltip placement="topLeft" title={`浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩�傛敞锛氭暟鎹潈闄愭浛鎹㈢ $@ -> /* 鎴� ''銆� @$ -> */ 鎴� ''銆俙}>
                   <QuestionCircleOutlined className="mk-form-tip" />
@@ -409,7 +409,7 @@
                 </Radio.Group>)}
               </Form.Item>
             </Col> : null}
-            <Col span={8}>
+            {!['invTable'].includes(config.subtype) ? <Col span={8}>
               <Form.Item label="涓婚敭">
                 {getFieldDecorator('primaryKey', {
                   initialValue: setting.primaryKey || ''
@@ -423,7 +423,7 @@
                   </Select>
                 )}
               </Form.Item>
-            </Col>
+            </Col> : null}
             {/* 鏁扮粍鏁版嵁锛岄渶璁剧疆鎺掑簭瑙勫垯 */}
             {config.format === 'array' ? <Col span={8}>
               <Form.Item label={
@@ -454,7 +454,7 @@
                 </Radio.Group>)}
               </Form.Item>
             </Col> : null}
-            {!['navbar', 'balcony', 'menubar'].includes(config.type) && (!config.wrap || config.wrap.supType !== 'multi') && MenuType !== 'billPrint' ? <Col span={8}>
+            {!['balcony', 'menubar', 'commonbar', 'tabbar', 'invTable'].includes(config.subtype) && (!config.wrap || config.wrap.supType !== 'multi') && MenuType !== 'billPrint' ? <Col span={8}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'璇ョ粍浠跺鏋滃彈鍏朵粬缁勪欢鎺у埗锛岃閫夐」鐩稿簲鐨勭粍浠讹紝娌℃湁鏃堕�夆�滄棤鈥濄��'}>
                   <QuestionCircleOutlined className="mk-form-tip" />
@@ -516,7 +516,7 @@
               </Form.Item>
             </Col> : null}
             {/* 1銆佷笉鍒嗛〉涓斾笉瀛樺湪涓婄骇妯″潡 */}
-            {!['navbar', 'interface', 'calendar'].includes(config.type) && !['editable', 'basetable', 'dualdatacard'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' && setting.onload !== 'false' ? <Col span={8}>
+            {!['navbar', 'interface', 'calendar'].includes(config.type) && !['editable', 'basetable', 'dualdatacard', 'invoice', 'invTable'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' && setting.onload !== 'false' ? <Col span={8}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'鍒濆鍖栧姞杞芥椂锛屾槸鍚︿笌鍏朵粬缁勪欢涓�鍚屽姞杞芥暟鎹紝娉細濡傝彍鍗曟湭浣跨敤鍚庣缂撳瓨锛屽垯鏌ヨ璇彞澶т簬8000瀛楃鏃舵棤鏁堛��'}>
                   <QuestionCircleOutlined className="mk-form-tip" />
@@ -533,7 +533,7 @@
                 )}
               </Form.Item>
             </Col> : null}
-            {!['navbar', 'balcony', 'menubar'].includes(config.type) && !['basetable'].includes(config.subtype) ? <Col span={8}>
+            {!['navbar', 'balcony', 'menubar'].includes(config.type) && !['basetable', 'invoice', 'invTable'].includes(config.subtype) ? <Col span={8}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={config.type === 'interface' ? '鍗曠嫭鎼滅储缁勪欢鍙綔涓哄叕鍏辨暟鎹簮鐨勬悳绱㈡潯浠躲��' : '浼樺厛浣跨敤鍚岀骇鐨勬悳绱㈡潯浠剁粍浠讹紝鍚岀骇鎼滅储涓嶅瓨鍦ㄦ椂锛屼緷娆″悜涓婇�夊彇锛屼笌褰撳墠缁勪欢鐨勬悳绱㈡潯浠朵竴鍚岀敤浣滄暟鎹繃婊わ紙褰撳墠缁勪欢鐨勬悳绱㈡潯浠朵紭鍏堬級銆�'}>
                   <QuestionCircleOutlined className="mk-form-tip" />
diff --git a/src/menu/datasource/verifycard/utils.jsx b/src/menu/datasource/verifycard/utils.jsx
index b443500..291ef35 100644
--- a/src/menu/datasource/verifycard/utils.jsx
+++ b/src/menu/datasource/verifycard/utils.jsx
@@ -75,7 +75,7 @@
       {reg: /@sum\$|\$sum@/ig, value: ''},
     ]
 
-    if (window.GLOB.process) {
+    if (window.GLOB.process && type !== 'invoice') {
       regs.push({reg: /@works_flow_code@/ig, value: `'${getuuid()}'`})
     }
 
@@ -101,7 +101,7 @@
       `
     }
 
-    if (window.GLOB.urlFields) {
+    if (window.GLOB.urlFields && type !== 'invoice') {
       window.GLOB.urlFields.forEach(field => {
         let reg = new RegExp('@' + field + '@', 'ig')
         _dataresource = _dataresource.replace(reg, `'0'`)
diff --git a/src/menu/debug/index.jsx b/src/menu/debug/index.jsx
index 62a02cb..7d77de4 100644
--- a/src/menu/debug/index.jsx
+++ b/src/menu/debug/index.jsx
@@ -1521,7 +1521,7 @@
       `
     }
 
-    sql = `create table #${sheet} (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) )
+    sql = `create table #${sheet} (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50))
       Declare @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@mk_departmentcode nvarchar(512),@mk_organization nvarchar(512),@mk_user_type nvarchar(20),@mk_nation nvarchar(50),@mk_province nvarchar(50),@mk_city nvarchar(50),@mk_district nvarchar(50),@mk_address nvarchar(100),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
       
       Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @mk_departmentcode='${departmentcode}', @mk_organization='${organization}', @mk_user_type='${mk_user_type}', @mk_nation='${nation}', @mk_province='${province}', @mk_city='${city}', @mk_district='${district}', @mk_address='${address}'
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index b56942a..47269bf 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -30,6 +30,7 @@
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
 const Voucher = asyncComponent(() => import('@/menu/components/module/voucher'))
 const Account = asyncComponent(() => import('@/menu/components/module/account'))
+const Invoice = asyncComponent(() => import('@/menu/components/module/invoice'))
 const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
 const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
 const AntvX6 = asyncComponent(() => import('@/menu/components/chart/antv-X6'))
@@ -135,6 +136,8 @@
       return (<Voucher card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'module' && card.subtype === 'account') {
       return (<Account card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'module' && card.subtype === 'invoice') {
+      return (<Invoice card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
 
diff --git a/src/menu/modulesource/option.jsx b/src/menu/modulesource/option.jsx
index 1607a5a..9013a15 100644
--- a/src/menu/modulesource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -32,6 +32,7 @@
 import Voucher from '@/assets/mobimg/voucher.png'
 import Account from '@/assets/mobimg/account.png'
 import canlendar from '@/assets/mobimg/canlendar.png'
+import Invoice from '@/assets/img/invoice.png'
 
 // 缁勪欢閰嶇疆淇℃伅
 export const menuOptions = [
@@ -72,4 +73,5 @@
   { type: 'menu', url: Iframe, component: 'iframe', subtype: 'iframe', title: 'iframe', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: Account, component: 'module', subtype: 'account', title: '璐﹀', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: Voucher, component: 'module', subtype: 'voucher', title: '鍑瘉', width: 24, forbid: ['billPrint'] },
+  { type: 'menu', url: Invoice, component: 'module', subtype: 'invoice', title: '鍙戠エ', width: 24, forbid: ['billPrint'] },
 ]
diff --git a/src/tabviews/custom/components/module/invoice/index.jsx b/src/tabviews/custom/components/module/invoice/index.jsx
new file mode 100644
index 0000000..6307d89
--- /dev/null
+++ b/src/tabviews/custom/components/module/invoice/index.jsx
@@ -0,0 +1,377 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Select, Form, Input, Button, Modal } from 'antd'
+import { EllipsisOutlined } from '@ant-design/icons'
+import moment from 'moment'
+
+// import Api from '@/api'
+// import Utils from '@/utils/utils.js'
+import MKEmitter from '@/utils/events.js'
+import InvoiceTable from './invoiceTable'
+import SubTable from './subTable'
+import './index.scss'
+
+class InvoiceModule extends Component {
+  static propTpyes = {
+    config: PropTypes.object
+  }
+
+  state = {
+    BID: '',
+    invTypes: [
+      {value: '1', label: '鐢靛瓙鍙戠エ锛堝鍊肩◣涓撶敤鍙戠エ锛�'},
+      {value: '2', label: '鐢靛瓙鍙戠エ锛堟櫘閫氬彂绁級'},
+      {value: '3', label: '澧炲�肩◣绾歌川涓撶敤鍙戠エ'},
+      {value: '4', label: '澧炲�肩◣绾歌川鏅�氬彂绁�'},
+      {value: '5', label: '澧炲�肩◣鐢靛瓙鏅�氬彂绁�'},
+      {value: '6', label: '澧炲�肩◣鐢靛瓙涓撶敤鍙戠エ'},
+    ],
+    invoice_type: '',
+    date: moment().format('YYYY骞碝M鏈圖D鏃�'),
+    from_to_name: '',
+    from_to_tax_no: '',
+    from_to_addr: '',
+    from_to_tel: '',
+    from_to_bank_name: '',
+    from_to_account_no: '',
+    from_to_mob: '',
+    from_to_email: '',
+    from_to_code: '',
+    orgname: '',
+    tax_no: '',
+    addr: '',
+    tel: '',
+    bank_name: '',
+    account_no: '',
+    remark: '',
+    payee: '',
+    reviewer: '',
+    drawer: '',
+    details: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    let _config = fromJS(config).toJS()
+    let BID = ''
+    let BData = ''
+
+    if (_config.setting.supModule) {
+      BData = window.GLOB.CacheData.get(_config.setting.supModule)
+    } else {
+      BData = window.GLOB.CacheData.get(_config.$pageId)
+    }
+    if (BData) {
+      BID = BData.$BID || ''
+    }
+
+    if (_config.wrap.invColor) {
+      _config.style['--inv-color'] = _config.wrap.invColor
+    }
+
+    _config.buyer = this.formatSetting(_config.buyer, 'buyer')
+    _config.detail = this.formatSetting(_config.detail, 'detail')
+
+    this.setState({
+      BID: BID || '',
+      config: _config
+    })
+  }
+
+  componentDidMount () {
+    this.loadData()
+    MKEmitter.addListener('reloadData', this.reloadData)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+  }
+
+  formatSetting = (item, type) => {
+    item.setting.arr_field = item.columns.map(col => col.field).join(',')
+    item.setting.laypage = item.setting.laypage === 'true'
+
+    if (type === 'buyer') {
+      item.columns = item.columns.map(cell => {
+        if (['from_to_tel', 'from_to_account_no', 'from_to_code'].includes(cell.field)) {
+          cell.Hide = 'true'
+        } else if (['from_to_email', 'from_to_mob'].includes(cell.field)) {
+          cell.Width = 80
+        }
+        return cell
+      })
+    } else {
+      item.columns = item.columns.map(cell => {
+        if (['Description', 'id'].includes(cell.field)) {
+          cell.Hide = 'true'
+        } else if (['spec'].includes(cell.field)) {
+          cell.Width = 150
+        } else if (['unit', 'unitprice', 'tax_rate'].includes(cell.field)) {
+          cell.Width = 80
+        }
+        return cell
+      })
+    }
+
+    if (item.setting.interType !== 'system') {
+      item.setting.dataresource = ''
+      return item
+    }
+
+    let regs = [
+      { reg: /@userName@/ig, value: `'${sessionStorage.getItem('User_Name') || ''}'` },
+      { reg: /@fullName@/ig, value: `'${sessionStorage.getItem('Full_Name') || ''}'` }
+    ]
+    
+    if (window.GLOB.externalDatabase !== null) {
+      regs.push({
+        reg: /@db@/ig,
+        value: window.GLOB.externalDatabase
+      })
+    }
+
+    let _customScript = ''
+    let _tailScript = ''
+    item.scripts && item.scripts.forEach(script => {
+      if (script.status === 'false') return
+      if (script.position !== 'back') {
+        _customScript += `
+        ${script.sql}
+        `
+      } else {
+        _tailScript += `
+        ${script.sql}
+        `
+      }
+    })
+    delete item.scripts
+
+    item.setting.execute = item.setting.execute !== 'false'
+    
+    if (!item.setting.execute) {
+      item.setting.dataresource = ''
+    }
+    if (/\s/.test(item.setting.dataresource)) {
+      item.setting.dataresource = '(' + item.setting.dataresource + ') tb'
+    }
+
+    if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+      item.setting.dataresource = item.setting.dataresource.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'')
+      _customScript = _customScript.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'')
+      _tailScript = _tailScript.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'')
+    } else {
+      item.setting.dataresource = item.setting.dataresource.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+      _customScript = _customScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+      _tailScript = _tailScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+    }
+
+    regs.forEach(cell => {
+      item.setting.dataresource = item.setting.dataresource.replace(cell.reg, cell.value)
+      _customScript = _customScript.replace(cell.reg, cell.value)
+      _tailScript = _tailScript.replace(cell.reg, cell.value)
+    })
+
+    item.setting.customScript = _customScript // 鏁寸悊鍚庤嚜瀹氫箟鑴氭湰
+    item.setting.tailScript = _tailScript     // 鍚庣疆鑷畾涔夎剼鏈�
+
+    item.setting.custompage = /@pageSize@|@orderBy@/i.test(item.setting.dataresource + item.setting.customScript)
+
+    return item
+  }
+
+  reloadData = (menuId) => {
+    // const { config } = this.props
+    // const { activeItem } = this.state
+    
+    // if (config.uuid !== menuId) return
+
+    // if (activeItem) {
+    //   MKEmitter.emit('resetSelectLine', config.uuid, activeItem.id, activeItem)
+    // } else {
+    //   this.loadData()
+    // }
+  }
+
+  loadData = () => {
+    // let param = {
+    //   func: 's_get_fcc_book_data',
+    //   dataM: sessionStorage.getItem('dataM') === 'true' ? 'Y' : '',
+    //   mk_organization: sessionStorage.getItem('organization') || ''
+    // }
+
+    // Api.genericInterface(param).then(res => {
+    //   if (!res.status) {
+    //     notification.warning({
+    //       top: 92,
+    //       message: res.message,
+    //       duration: 5
+    //     })
+    //     return
+    //   }
+
+    //   let books = res.book || []
+    //   let activeItem = null
+    //   let map = new Map()
+    //   books = books.filter(item => {
+    //     if (!item.id) return false
+    //     if (map.has(item.id)) return false
+    //     map.set(item.id, true)
+
+    //     if (item.selected === 'true' && !activeItem) {
+    //       activeItem = item
+    //     }
+    //     if (item.months) {
+    //       item.date = item.months.replace('-', '骞�') + '鏈�'
+    //     }
+    //     return true
+    //   })
+
+    //   this.setState({books, activeItem})
+
+    //   if (activeItem) {
+    //     MKEmitter.emit('resetSelectLine', this.props.config.uuid, activeItem.id, activeItem)
+    //   }
+    // })
+  }
+
+  changeType = (val) => {
+    this.setState({invoice_type: val})
+  }
+
+  changeBuyer = (item) => {
+    this.setState({
+      visible: false,
+      from_to_name: item.from_to_name || '',
+      from_to_tax_no: item.from_to_tax_no || '',
+      from_to_addr: item.from_to_addr || '',
+      from_to_tel: item.from_to_tel || '',
+      from_to_bank_name: item.from_to_bank_name || '',
+      from_to_account_no: item.from_to_account_no || '',
+      from_to_mob: item.from_to_mob || '',
+      from_to_email: item.from_to_email || '',
+      from_to_code: item.from_to_code || '',
+    })
+  }
+
+  render() {
+    const { config, invTypes, date, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee, details, visible } = this.state
+
+    return (
+      <div className="menu-invoice-wrap" style={config.style}>
+        <div className="inv-action">
+          <Button className="mk-bill">淇濆瓨鍗曟嵁</Button>
+          <Button className="mk-submit">鎻愪氦寮�绁�</Button>
+        </div>
+        <div className="inv-header">
+          <Select placeholder="璇烽�夋嫨鍙戠エ绉嶇被" onChange={this.changeType} dropdownClassName="inv-type-select">
+            {invTypes.map(item => (
+              <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
+            ))}
+          </Select>
+          <div className="date">寮�绁ㄦ棩鏈燂細{date}</div>
+        </div>
+        <div className="inv-body">
+          <div className="inv-main-content">
+            <div className="inv-buyer">
+              <div className="inv-label">璐拱鏂�</div>
+              <div className="inv-content">
+                <Form.Item label={<>鍚�<span></span>绉�</>} extra={<EllipsisOutlined onClick={() => this.setState({visible: true})}/>}>
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟鍚嶇О" allowClear value={from_to_name} autoComplete="off" onChange={(e) => this.setState({from_to_name: e.target.value})}/>
+                </Form.Item>
+                <Form.Item label="绾崇◣浜鸿瘑鍒彿">
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟绾崇◣浜鸿瘑鍒彿" allowClear value={from_to_tax_no} autoComplete="off" onChange={(e) => this.setState({from_to_tax_no: e.target.value})}/>
+                </Form.Item>
+                <Form.Item className="mutil-input" label={<>鍦�<span></span>鍧�<span></span>銆�<span></span>鐢�<span></span>璇�</>}>
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟鍦板潃" allowClear value={from_to_addr} autoComplete="off" onChange={(e) => this.setState({from_to_addr: e.target.value})}/>
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟鐢佃瘽" allowClear value={from_to_tel} autoComplete="off" onChange={(e) => this.setState({from_to_tel: e.target.value})}/>
+                </Form.Item>
+                <Form.Item className="mutil-input" label="寮�鎴疯鍙婅处鍙�">
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟寮�鎴疯" allowClear value={from_to_bank_name} autoComplete="off" onChange={(e) => this.setState({from_to_bank_name: e.target.value})}/>
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟璐﹀彿" allowClear value={from_to_account_no} autoComplete="off" onChange={(e) => this.setState({from_to_account_no: e.target.value})}/>
+                </Form.Item>
+              </div>
+            </div>
+            <div className="inv-notice">
+              <div className="inv-label">閫氱煡鍒�</div>
+              <div className="inv-content">
+                <Form.Item label={<>鎵�<span></span>鏈�<span></span>鍙�</>}>
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟鎵嬫満鍙�" allowClear value={from_to_mob} autoComplete="off" onChange={(e) => this.setState({from_to_mob: e.target.value})}/>
+                </Form.Item>
+                <Form.Item label={<>閭�<span></span>绠�</>}>
+                  <Input placeholder="璇疯緭鍏ヨ喘涔版柟閭" allowClear value={from_to_email} autoComplete="off" onChange={(e) => this.setState({from_to_email: e.target.value})}/>
+                </Form.Item>
+              </div>
+            </div>
+          </div>
+          <div className="inv-details">
+            <InvoiceTable data={details} config={config.detail} onChange={(details) => this.setState({details})}/>
+          </div>
+          <div className="inv-main-content">
+            <div className="inv-buyer">
+              <div className="inv-label">閿�鍞柟</div>
+              <div className="inv-content">
+                <Form.Item label={<>鍚�<span></span>绉�</>}>
+                  <Input placeholder="璇疯緭鍏ラ攢鍞柟鍚嶇О" value={orgname} autoComplete="off" onChange={(e) => this.setState({orgname: e.target.value})}/>
+                </Form.Item>
+                <Form.Item label="绾崇◣浜鸿瘑鍒彿">
+                  <Input placeholder="璇疯緭鍏ラ攢鍞柟绾崇◣浜鸿瘑鍒彿" disabled value={tax_no} autoComplete="off"/>
+                </Form.Item>
+                <Form.Item className="mutil-input" label={<>鍦�<span></span>鍧�<span></span>銆�<span></span>鐢�<span></span>璇�</>}>
+                  <Input placeholder="璇疯緭鍏ラ攢鍞柟鍦板潃" value={addr} autoComplete="off" onChange={(e) => this.setState({addr: e.target.value})}/>
+                  <Input placeholder="璇疯緭鍏ラ攢鍞柟鐢佃瘽" value={tel} autoComplete="off" onChange={(e) => this.setState({tel: e.target.value})}/>
+                </Form.Item>
+                <Form.Item className="mutil-input" label="寮�鎴疯鍙婅处鍙�">
+                  <Input placeholder="璇疯緭鍏ラ攢鍞柟寮�鎴疯" value={bank_name} autoComplete="off" onChange={(e) => this.setState({bank_name: e.target.value})}/>
+                  <Input placeholder="璇疯緭鍏ラ攢鍞柟璐﹀彿" value={account_no} autoComplete="off" onChange={(e) => this.setState({account_no: e.target.value})}/>
+                </Form.Item>
+              </div>
+            </div>
+            <div className="inv-notice">
+              <div className="inv-label">澶囨敞</div>
+              <div className="inv-content" style={{paddingTop: '30px'}}>
+                <Form.Item label="">
+                  <Input.TextArea placeholder="璇疯緭鍏ュ娉�" autoSize={{ minRows: 4, maxRows: 4 }} value={remark} autoComplete="off" onChange={(e) => this.setState({remark: e.target.value})}/>
+                </Form.Item>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div className="inv-tail">
+          <Form.Item label="鏀舵浜�">
+            <Input placeholder="鏀舵浜�" value={payee} autoComplete="off" onChange={(e) => this.setState({payee: e.target.value})}/>
+          </Form.Item>
+          <Form.Item label="澶嶆牳浜�">
+            <Input placeholder="澶嶆牳浜�" value={reviewer} autoComplete="off" onChange={(e) => this.setState({reviewer: e.target.value})}/>
+          </Form.Item>
+          <Form.Item label="寮�绁ㄤ汉">
+            <Input placeholder="寮�绁ㄤ汉" value={drawer} autoComplete="off" onChange={(e) => this.setState({drawer: e.target.value})}/>
+          </Form.Item>
+        </div>
+        <Modal
+          title="瀹㈡埛淇℃伅"
+          visible={visible}
+          width="70vw"
+          maskClosable={false}
+          onCancel={() => { this.setState({ visible: false }) }}
+          footer={null}
+        >
+          <SubTable config={config.buyer} onChange={this.changeBuyer}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default InvoiceModule
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/invoice/index.scss b/src/tabviews/custom/components/module/invoice/index.scss
new file mode 100644
index 0000000..49e58ea
--- /dev/null
+++ b/src/tabviews/custom/components/module/invoice/index.scss
@@ -0,0 +1,260 @@
+.menu-invoice-wrap {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  color: #000000;
+  --inv-color: #13509c;
+
+  .inv-action {
+    text-align: right;
+    margin-right: 30px;
+    .ant-btn {
+      margin-left: 15px;
+      margin-bottom: 5px;
+      height: 30px;
+    }
+    .mk-bill:hover, .mk-bill:active, .mk-bill:focus {
+      color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+    }
+    .mk-submit, .mk-submit:hover, .mk-submit:active, .mk-submit:focus {
+      color: #fff;
+      background-color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+    }
+  }
+  .inv-header {
+    text-align: center;
+    position: relative;
+    height: 70px;
+    margin-right: 30px;
+    .ant-select {
+      width: 390px;
+      border: none;
+      .ant-select-selection {
+        border: none;
+        box-shadow: none;
+        font-size: 25px;
+        text-align: center;
+        background-color: transparent;
+
+        .ant-select-selection-selected-value {
+          float: none;
+          text-align: center;
+          font-family: kaiti;
+          color: var(--inv-color, #13509c);
+        }
+        .ant-select-selection__placeholder {
+          text-align: center;
+          font-family: kaiti;
+        }
+      }
+    }
+    .ant-select::before, .ant-select::after {
+      content: '';
+      display: block;
+      width: 100%;
+      position: absolute;
+      border-top: var(--inv-color, #13509c) 1px solid;
+      border-bottom: var(--inv-color, #13509c) 1px solid;
+      height: 1px;
+    }
+    .ant-select::before {
+      bottom: -10px;
+    }
+    .ant-select::after {
+      bottom: -15px;
+    }
+    .date {
+      position: absolute;
+      right: 100px;
+      top: 5px;
+      color: var(--inv-color, #13509c);
+    }
+  }
+
+  .ant-input {
+    border-top: none;
+    border-left: none;
+    border-right: none;
+    border-radius: 0;
+    box-shadow: none!important;
+    height: 28px;
+    font-size: 13px;
+    background: transparent;
+  }
+  .ant-input:hover, .ant-input:active, .ant-input:focus {
+    border-color: var(--inv-color, #13509c)!important;
+  }
+  .ant-input.ant-input-disabled {
+    cursor: text;
+    background: transparent!important;
+  }
+
+  .ant-input-number {
+    border-top: none;
+    border-left: none;
+    border-right: none;
+    border-radius: 0;
+    box-shadow: none!important;
+    height: 28px;
+    font-size: 13px;
+    .ant-input-number-handler-wrap {
+      display: none;
+    }
+  }
+  .ant-input-number:hover, .ant-input-number:active, .ant-input-number:focus {
+    border-color: var(--inv-color, #13509c)!important;
+  }
+
+  .inv-body {
+    border: var(--inv-color, #13509c) 1px solid;
+    font-size: 13px;
+    margin-right: 30px;
+
+    .inv-main-content {
+      display: flex;
+
+      .inv-buyer, .inv-notice {
+        width: 50%;
+        display: flex;
+        .inv-label {
+          color: var(--inv-color, #13509c);
+          width: 6.25%;
+          display: flex;
+          flex-direction: column;
+          writing-mode: vertical-rl;
+          justify-content: center;
+          align-items: center;
+          letter-spacing: 5px;
+          border-right: var(--inv-color, #13509c) 1px solid;
+        }
+        .inv-content {
+          flex: 1;
+          padding: 6px 0;
+
+          .ant-form-item {
+            display: flex;
+            padding: 0 5px 0 15px;
+            font-size: 13px;
+            margin-bottom: 5px;
+            
+            .ant-form-item-label {
+              line-height: 30px;
+              text-align: justify;
+              label {
+                display: flex;
+                color: var(--inv-color, #13509c);
+                span {
+                  display: inline-block;
+                  flex: 1;
+                }
+              }
+            }
+            .ant-form-item-control-wrapper {
+              width: 100%;
+
+              .ant-form-item-control {
+                line-height: 30px;
+                .ant-form-extra {
+                  position: absolute;
+                  top: 1px;
+                  right: 10px;
+                  font-size: 16px;
+                  .anticon {
+                    cursor: pointer;
+                  }
+                }
+                .ant-input-affix-wrapper:not(:hover) {
+                  .ant-input-suffix {
+                    opacity: 0;
+                  }
+                }
+              }
+            }
+          }
+          .ant-form-item.mutil-input {
+            .ant-form-item-children {
+              display: flex;
+            }
+          }
+        }
+      }
+      .inv-buyer {
+        border-right: var(--inv-color, #13509c) 1px solid;
+        .ant-form-item-label {
+          width: 95px;
+          min-width: 95px;
+        }
+      }
+      .inv-notice {
+        .inv-content {
+          padding-top: 45px;
+        }
+        .ant-form-item-label {
+          width: 75px;
+          min-width: 75px;
+        }
+      }
+    }
+
+    .inv-details {
+      border-top: var(--inv-color, #13509c) 1px solid;
+      border-bottom: var(--inv-color, #13509c) 1px solid;
+    }
+  }
+  .inv-tail {
+    display: flex;
+    padding-top: 10px;
+    margin-right: 30px;
+    .ant-form-item {
+      flex: 1;
+      display: flex;
+      font-size: 13px;
+      .ant-form-item-label, .ant-form-item-control-wrapper {
+        width: 40%;
+      }
+    }
+  }
+}
+.inv-table {
+  .ant-table .ant-table-tbody tr:hover td {
+    background-color: var(--mk-sys-color1);
+  }
+}
+.tb-search-wrap {
+  .search-item {
+    display: flex;
+    float: left;
+    width: 350px;
+    align-items: center;
+    justify-content: flex-end;
+
+    .ant-input {
+      width: 200px;
+    }
+  }
+  .ant-btn {
+    margin-bottom: 20px;
+    margin-left: 50px;
+    background-color: var(--mk-sys-color);
+    border-color: var(--mk-sys-color);
+    color: #ffffff;
+  }
+  .ant-btn:hover, .ant-btn:active {
+    background-color: var(--mk-sys-color);
+    border-color: var(--mk-sys-color);
+    color: #ffffff;
+    opacity: 0.9;
+  }
+}
+
+.inv-type-select {
+  .ant-select-dropdown-menu-item {
+    text-align: center;
+  }
+}
+
diff --git a/src/tabviews/custom/components/module/invoice/invoiceTable/index.jsx b/src/tabviews/custom/components/module/invoice/invoiceTable/index.jsx
new file mode 100644
index 0000000..791d755
--- /dev/null
+++ b/src/tabviews/custom/components/module/invoice/invoiceTable/index.jsx
@@ -0,0 +1,394 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Input, InputNumber, notification, Modal } from 'antd'
+import { EllipsisOutlined } from '@ant-design/icons'
+
+import Utils from '@/utils/utils.js'
+import MKEmitter from '@/utils/events.js'
+import SubTable from '../subTable'
+import './index.scss'
+
+class DetailLine extends React.Component {
+  state = {
+    bill_count: 0,
+    unitprice: 0,
+    amount_line: 0,
+    visible: false
+  }
+
+  UNSAFE_componentWillMount() {
+    let { line } = this.props
+
+    this.setState({
+      bill_count: line.bill_count || '',
+      unitprice: line.unitprice || '',
+      amount_line: line.amount_line || ''
+    })
+  }
+
+  onChange = (value, key) => {
+    let line = {...this.props.line}
+
+    if (['bill_count', 'unitprice', 'amount_line'].includes(key)) {
+      line[key] = value || 0
+      if (line[key]) {
+        if (key === 'bill_count') {
+          line[key] = Math.round(line[key] * 10000000000) / 10000000000
+          if (line.unitprice) {
+            line.amount_line = Math.round(line.unitprice * line.bill_count * 100) / 100
+          }
+        } else if (key === 'unitprice') {
+          line[key] = Math.round(line[key] * 10000000000) / 10000000000
+          if (line.bill_count) {
+            line.amount_line = Math.round(line.unitprice * line.bill_count * 100) / 100
+          }
+        } else if (key === 'amount_line') {
+          line[key] = Math.round(line[key] * 100) / 100
+          if (line.bill_count) {
+            line.unitprice = Math.round(line.amount_line / line.bill_count * 10000000000) / 10000000000
+          } else if (line.unitprice) {
+            line.bill_count = Math.round(line.amount_line / line.unitprice * 10000000000) / 10000000000
+          }
+        }
+      }
+    } else {
+      line[key] = value
+    }
+
+    this.setState({
+      bill_count: line.bill_count || '',
+      unitprice: line.unitprice || '',
+      amount_line: line.amount_line || ''
+    })
+
+    this.props.changeLine(line, key)
+  }
+
+  render() {
+    const { line, delLine, trigger } = this.props
+    const { bill_count, unitprice, amount_line } = this.state
+    
+    return <div className="mk-tr active">
+      <div className="mk-td">
+        <div className="mk-input">{line.productname || ''}<EllipsisOutlined onClick={trigger}/></div>
+      </div>
+      <div className="mk-td">
+        <Input defaultValue={line.spec || ''} onChange={(e) => this.onChange(e.target.value, 'spec')}/>
+      </div>
+      <div className="mk-td">
+        <Input defaultValue={line.unit || ''} onChange={(e) => this.onChange(e.target.value, 'unit')}/>
+      </div>
+      <div className="mk-td">
+        <InputNumber value={bill_count} onChange={(val) => this.setState({bill_count: val})} onBlur={(e) => this.onChange(e.target.value, 'bill_count')}/>
+      </div>
+      <div className="mk-td">
+        <InputNumber value={unitprice} onChange={(val) => this.setState({unitprice: val})} onBlur={(e) => this.onChange(e.target.value, 'unitprice')}/>
+      </div>
+      <div className="mk-td">
+        <InputNumber value={amount_line} onChange={(val) => this.setState({amount_line: val})} onBlur={(e) => this.onChange(e.target.value, 'amount_line')}/>
+      </div>
+      <div className="mk-td">{line.tax_name}</div>
+      <div className="mk-td mk-right">{line.tax_amount} <span className="del-line" onClick={() => delLine(line.uuid)}></span> </div>
+    </div>
+  }
+}
+
+class InvoiceTable extends Component {
+  static propTpyes = {
+    config: PropTypes.object,
+    data: PropTypes.any,
+    onChange: PropTypes.func
+  }
+
+  state = {
+    data: [],
+    editKey: '',
+    total: {}
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data } = this.props
+
+    let _data = fromJS(data).toJS()
+    if (!_data.length) {
+      _data = [{uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}]
+    }
+
+    this.setState({
+      data: _data
+    }, () => {
+      this.getTotal(_data)
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('resetDetails', this.resetDetails)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('resetDetails', this.resetDetails)
+  }
+  
+  changeMoneyToChinese = (money) => {
+    let cnNums = ['闆�', '澹�', '璐�', '鍙�', '鑲�', '浼�', '闄�', '鏌�', '鎹�', '鐜�']
+    let cnIntRadice = ['', '鎷�', '浣�', '浠�']
+    let cnIntUnits = ['', '涓�', '浜�', '鍏�']
+    let cnDecUnits = ['瑙�', '鍒�', '姣�', '鍘�']
+    let cnInteger = '鏁�'
+    let cnIntLast = '鍏�'
+    let maxNum = 999999999999999.9999 // 鏈�澶у鐞嗙殑鏁板瓧
+    let IntegerNum = null
+    let DecimalNum = null
+    let ChineseStr = ''
+    let parts = null // 鍒嗙閲戦鍚庣敤鐨勬暟缁勶紝棰勫畾涔�
+    let Symbol = ''  // 姝h礋鍊兼爣璁�
+
+    if (money === 0) return ''
+
+    if (money >= maxNum) return '瓒呭嚭鏈�澶у鐞嗘暟瀛�'
+
+    if(money < 0) {
+      money = -money
+      Symbol = '璐�'       
+    }
+    money = money.toString() // 杞崲涓哄瓧绗︿覆
+    if (money.indexOf('.') === -1) {
+      IntegerNum = money
+      DecimalNum = ''
+    } else {
+      parts = money.split('.')
+      IntegerNum = parts[0]
+      DecimalNum = parts[1].substr(0, 4)
+    }
+
+    if (parseInt(IntegerNum, 10) > 0) { // 鑾峰彇鏁村瀷閮ㄥ垎杞崲
+      let zeroCount = 0
+      let IntLen = IntegerNum.length
+      for (let i = 0; i < IntLen; i++) {
+        let n = IntegerNum.substr(i, 1)
+        let p = IntLen - i - 1
+        let q = p / 4
+        let m = p % 4
+        
+        if (n === '0') {
+          zeroCount++
+        } else {
+          if (zeroCount > 0) {
+            ChineseStr += cnNums[0]
+          }
+          zeroCount = 0 // 褰掗浂
+          ChineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
+        }
+
+        if (m === 0 && zeroCount < 4) {
+          ChineseStr += cnIntUnits[q]
+        }
+      }
+      ChineseStr += cnIntLast
+    }
+
+    if (DecimalNum !== '') { // 灏忔暟閮ㄥ垎
+      let decLen = DecimalNum.length
+
+      for (let i = 0; i < decLen; i++) {
+        let n = DecimalNum.substr(i, 1)
+        if (n !== '0') {
+          ChineseStr += cnNums[Number(n)] + cnDecUnits[i]
+        }
+      }
+    }
+    if (ChineseStr === '') {
+      ChineseStr += cnNums[0] + cnIntLast + cnInteger
+    } else if (DecimalNum === '') {
+      ChineseStr += cnInteger
+    }
+
+    ChineseStr = Symbol + ChineseStr
+    
+    return ChineseStr
+  }
+
+  getTotal = (data) => {
+    let price = 0
+    let tax = 0
+
+    data.forEach(item => {
+      if (!item.productcode) return
+
+      price += item.amount_line
+      tax += item.tax_amount
+    })
+
+    this.setState({total: {price, tax, sum: price + tax, sumName: this.changeMoneyToChinese(price + tax)}})
+  }
+
+  resetDetails = (data) => {
+    let _data = fromJS(data).toJS()
+
+    if (!_data.length) {
+      _data = [{uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}]
+    }
+
+    this.setState({data: _data}, () => {
+      this.getTotal(_data)
+    })
+  }
+
+  addLine = () => {
+    const { data } = this.state
+
+    let line = {uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}
+
+    this.setState({data: [...data, line]})
+  }
+
+  delLine = () => {
+    const { editKey, data } = this.state
+
+    if (data.length === 1) {
+      notification.warning({
+        top: 92,
+        message: '鑷冲皯淇濈暀涓�琛屾槑缁嗭紒',
+        duration: 3
+      })
+      return
+    }
+
+    let _data = data.filter(item => item.uuid !== editKey)
+
+    this.setState({data: _data}, () => {
+      this.getTotal(_data)
+    })
+    this.props.onChange(_data)
+  }
+
+  changeLine = (record) => {
+    const { editKey, data } = this.state
+
+    let _data = data.map(item => {
+      if (item.uuid === editKey) {
+        return record
+      } else {
+        return item
+      }
+    })
+
+
+    this.setState({data: _data}, () => {
+      this.getTotal(_data)
+    })
+    this.props.onChange(_data)
+  }
+
+  checkLine = (uuid) => {
+    this.setState({editKey: uuid})
+  }
+
+  changeDetail = (prod) => {
+    const { editKey, data } = this.state
+
+    let _data = data.map(item => {
+      if (item.uuid === editKey) {
+        item.productname = prod.productname
+        item.spec = prod.spec
+        item.unit = prod.unit
+        item.unitprice = prod.unitprice
+        item.tax_rate = prod.tax_rate
+        item.tax_name = prod.tax_rate
+
+        item.productcode = prod.productcode
+        item.Description = prod.Description
+        item.tax_classify_code = prod.tax_classify_code
+        item.tax_classify_name = prod.tax_classify_name
+
+        if (item.bill_count && item.unitprice) {
+          item.amount_line = Math.round(item.unitprice * item.bill_count * 100) / 100
+        }
+        // item.tax_amount = prod.productname
+      }
+
+      return item
+    })
+
+    this.setState({data: _data, editKey: '', visible: false}, () => {
+      this.setState({editKey: editKey})
+      this.getTotal(_data)
+    })
+    this.props.onChange(_data)
+  }
+
+  render() {
+    const { config } = this.props
+    const { editKey, data, total, visible } = this.state
+
+    return (
+      <div className="detail-wrap">
+        <span className="plus-line" onClick={this.addLine}></span>
+        <div className="mk-th">
+          <div className="mk-td">璐х墿鎴栧簲绋庡姵鍔°�佹湇鍔″悕绉�</div>
+          <div className="mk-td">瑙勬牸鍨嬪彿</div>
+          <div className="mk-td">鍗曚綅</div>
+          <div className="mk-td">鏁伴噺</div>
+          <div className="mk-td">鍗曚环锛堝惈绋庯級</div>
+          <div className="mk-td">閲戦锛堝惈绋庯級</div>
+          <div className="mk-td">绋庣巼</div>
+          <div className="mk-td">绋庨</div>
+        </div>
+        {data.map(item => {
+          if (editKey === item.uuid) {
+            return <DetailLine key={item.uuid} line={item} changeLine={this.changeLine} delLine={this.delLine} trigger={() => this.setState({visible: true})}/>
+          }
+
+          return <div className="mk-tr" key={item.uuid} onClick={() => this.checkLine(item.uuid)}>
+            <div className="mk-td mk-left">{item.productname || '**'}</div>
+            <div className="mk-td mk-left">{item.spec || ''}</div>
+            <div className="mk-td mk-left">{item.unit || ''}</div>
+            <div className="mk-td mk-right">{item.bill_count || ''}</div>
+            <div className="mk-td mk-right">{item.unitprice || ''}</div>
+            <div className="mk-td mk-right">{item.amount_line || ''}</div>
+            <div className="mk-td mk-right">{item.tax_name}</div>
+            <div className="mk-td mk-right">{item.tax_amount}</div>
+          </div>
+        })}
+        <div className="mk-total">
+          <div className="mk-td">鍚堣</div>
+          <div className="mk-td"></div>
+          <div className="mk-td"></div>
+          <div className="mk-td"></div>
+          <div className="mk-td"></div>
+          <div className="mk-td">锟total.price}</div>
+          <div className="mk-td"></div>
+          <div className="mk-td">锟total.tax}</div>
+        </div>
+        <div className="mk-upcase">
+          <div className="mk-td">浠风◣鍚堣锛堝ぇ鍐欙級</div>
+          <div className="mk-td">{total.sumName}</div>
+          <div className="mk-td">锛堝皬鍐欙級锟total.sum}</div>
+        </div>
+        <Modal
+          title="鍟嗗搧淇℃伅"
+          visible={visible}
+          width="75vw"
+          maskClosable={false}
+          onCancel={() => { this.setState({ visible: false }) }}
+          footer={null}
+        >
+          <SubTable config={config} onChange={this.changeDetail}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default InvoiceTable
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/invoice/invoiceTable/index.scss b/src/tabviews/custom/components/module/invoice/invoiceTable/index.scss
new file mode 100644
index 0000000..fe39f35
--- /dev/null
+++ b/src/tabviews/custom/components/module/invoice/invoiceTable/index.scss
@@ -0,0 +1,127 @@
+.detail-wrap {
+  position: relative;
+
+  .plus-line, .del-line {
+    position: absolute;
+    right: -40px;
+    top: 5px;
+    font-size: 26px;
+    border: 1px solid #d9d9d9;
+    border-radius: 32px;
+    width: 30px;
+    height: 30px;
+    transition: all 0.3s;
+    cursor: pointer;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  .plus-line::before, .del-line::before {
+    content: ' ';
+    position: absolute;
+    top: 13px;
+    width: 12px;
+    height: 1px;
+    background: #b8b8b8;
+    transition: all 0.3s;
+  }
+  .plus-line::after {
+    content: ' ';
+    position: absolute;
+    top: 13px;
+    width: 12px;
+    height: 1px;
+    background: #b8b8b8;
+    transform: rotate(90deg);
+    transition: all 0.3s;
+  }
+  .plus-line:hover {
+    border-color: #26C281;
+  }
+  .plus-line:hover::before, .plus-line:hover::after {
+    background: #26C281;
+  }
+  .del-line:hover {
+    border-color: #ff4d4f;
+  }
+  .del-line:hover::before {
+    background: #ff4d4f;
+  }
+
+  .mk-th, .mk-tr, .mk-total {
+    position: relative;
+    display: flex;
+    border-bottom: var(--inv-color, #13509c) 1px solid;
+
+    .mk-td {
+      width: 10%;
+      height: 40px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      padding: 0 3px;
+    }
+    .mk-td:not(:last-child) {
+      border-right: var(--inv-color, #13509c) 1px solid;
+    }
+    .mk-td:first-child {
+      width: 20%;
+    }
+    .mk-td:nth-child(2), .mk-td:nth-child(6) {
+      width: 15%;
+    }
+
+    .mk-left {
+      justify-content: start;
+    }
+    .mk-right {
+      justify-content: end;
+    }
+
+    .mk-input {
+      position: relative;
+      height: 28px;
+      width: 100%;
+      background: #ffffff;
+      border-bottom: 1px solid #d9d9d9;
+      transition: all 0.3s;
+
+      .anticon-ellipsis {
+        position: absolute;
+        right: 0px;
+        padding: 5px 10px;
+        cursor: pointer;
+      }
+    }
+    .mk-input:hover {
+      border-color: var(--inv-color, #13509c);
+    }
+
+    .ant-input {
+      background: #ffffff;
+    }
+  }
+  .mk-tr.active, .mk-tr:hover {
+    background: var(--mk-sys-color1);
+  }
+  .mk-upcase {
+    display: flex;
+
+    .mk-td {
+      width: 40%;
+      height: 40px;
+      display: flex;
+      align-items: center;
+      padding: 0 20px;
+    }
+    .mk-td:first-child {
+      width: 20%;
+      padding: 0;
+      border-right: var(--inv-color, #13509c) 1px solid;
+      justify-content: center;
+    }
+    .mk-td:last-child {
+      justify-content: end;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/invoice/subTable/index.jsx b/src/tabviews/custom/components/module/invoice/subTable/index.jsx
new file mode 100644
index 0000000..04ab0e9
--- /dev/null
+++ b/src/tabviews/custom/components/module/invoice/subTable/index.jsx
@@ -0,0 +1,245 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Table, Input, Button } from 'antd'
+
+import Api from '@/api'
+import UtilsDM from '@/utils/utils-datamanage.js'
+// import './index.scss'
+
+class SearchWrap extends Component {
+  static propTpyes = {
+    search: PropTypes.array,
+    onChange: PropTypes.func
+  }
+
+  state = {
+    search: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { search } = this.props
+
+    this.setState({
+      search: fromJS(search).toJS()
+    })
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  searchChange = (val, key) => {
+    const { search } = this.state
+
+    this.setState({search: search.map(item => {
+      if (item.field === key) {
+        item.value = val
+      }
+      return item
+    })})
+  }
+
+  trigger = () => {
+    const { search } = this.state
+
+    this.props.onChange(search)
+  }
+
+  render() {
+    const { search } = this.state
+
+    return (
+      <div className="tb-search-wrap">
+        {search.map(item => (
+          <div className="search-item" key={item.field}>
+            {item.label}锛�
+            <Input autoComplete="off" onChange={(e) => this.searchChange(e.target.value, item.field)}/>
+          </div>
+        ))}
+        <Button onClick={this.trigger}>鎼滅储</Button>
+      </div>
+    )
+  }
+}
+
+class SubTable extends Component {
+  static propTpyes = {
+    config: PropTypes.object
+  }
+
+  state = {
+    pageIndex: 1,         // 鍒濆椤甸潰绱㈠紩
+    pageSize: 10,         // 姣忛〉鏁版嵁鏉℃暟
+    columns: null,        // 鏄剧ず鍒�
+    pageOptions: [],
+    search: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    let _columns = []
+
+    config.columns.forEach(item => {
+      if (item.Hide === 'true') return
+      _columns.push({
+        align: 'center',
+        dataIndex: item.field,
+        title: item.label,
+        sorter: false,
+        width: item.Width || 120
+      })
+    })
+
+    let size = (config.setting.pageSize || 10) + ''
+    let pageOptions = ['10', '25', '50', '100', '500', '1000']
+
+    if (!pageOptions.includes(size)) {
+      pageOptions.push(size)
+      pageOptions = pageOptions.sort((a, b) => a - b)
+    }
+
+    this.setState({
+      pageSize: config.pageSize || 10,
+      pageOptions,
+      search: fromJS(config.search).toJS(),
+      columns: _columns
+    })
+  }
+
+  componentDidMount() {
+    const { config } = this.props
+
+    if (config.setting.onload === 'true') {
+      this.loadData()
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  /**
+   * @description 鏁版嵁鍔犺浇
+   */
+  async loadData () {
+    const { config } = this.props
+    const { search, pageIndex, pageSize } = this.state
+
+    this.setState({
+      loading: true
+    })
+
+    let searches = search.map(item => ({
+      key: item.field,
+      match: item.match,
+      type: item.type,
+      value: item.value || ''
+    }))
+
+    let param = UtilsDM.getQueryDataParams(config.setting, searches, config.setting.order, pageIndex, pageSize, '')
+
+    this.requestId = config.uuid + new Date().getTime()
+
+    let result = await Api.genericInterface(param, config.setting.js_script, '', this.requestId)
+
+    if (result.status) {
+      if (result.$requestId && this.requestId !== result.$requestId) return
+
+      let start = pageSize * (pageIndex - 1) + 1
+
+      let data = result.data.map((item, index) => {
+        item.key = index
+        item.$Index = start + index + ''
+
+        return item
+      })
+
+      let total = result.total || 0
+      if (config.setting.custompage && data.length) {
+        total = data[data.length - 1].mk_total || 0
+      }
+
+      this.setState({
+        data: data,
+        total: total,
+        loading: false
+      })
+
+      UtilsDM.querySuccess(result)
+    } else {
+      this.setState({
+        loading: false
+      })
+      
+      UtilsDM.queryFail(result)
+    }
+  }
+
+  changeTable = (pagination) => {
+    this.setState({
+      pageIndex: pagination.current,
+      pageSize: pagination.pageSize,
+    }, () => {
+      this.loadData()
+    })
+  }
+
+  doubleClickLine = (record) => {
+    this.props.onChange(record)
+  }
+
+  searchChange = (search) => {
+    this.setState({search: search}, () => {
+      this.loadData()
+    })
+  }
+
+  render() {
+    const { pageIndex, pageSize, pageOptions, columns, total, loading, data, search } = this.state
+
+    return (
+      <>
+        <SearchWrap search={search} onChange={this.searchChange}/>
+        <Table
+          className="inv-table"
+          columns={columns}
+          dataSource={data}
+          bordered={true}
+          loading={loading}
+          onRow={(record) => {
+            return {
+              onDoubleClick: () => {this.doubleClickLine(record)}
+            }
+          }}
+          onChange={this.changeTable}
+          pagination={{
+            current: pageIndex,
+            pageSize: pageSize,
+            pageSizeOptions: pageOptions,
+            showSizeChanger: true,
+            total: total || 0,
+            showTotal: (total, range) => `${range[0]}-${range[1]} 鍏� ${total} 鏉
+          }}
+        />
+      </>
+    )
+  }
+}
+
+export default SubTable
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/invoice/subTable/index.scss b/src/tabviews/custom/components/module/invoice/subTable/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tabviews/custom/components/module/invoice/subTable/index.scss
diff --git a/src/tabviews/custom/components/tree/antd-tree/index.jsx b/src/tabviews/custom/components/tree/antd-tree/index.jsx
index 6e7f7b1..1009ba9 100644
--- a/src/tabviews/custom/components/tree/antd-tree/index.jsx
+++ b/src/tabviews/custom/components/tree/antd-tree/index.jsx
@@ -353,9 +353,9 @@
     let hasSelectKey = false
 
     data.forEach(item => {
-      let pval = item[config.wrap.parentField]
-      let val = item[config.wrap.valueField]
-      let uuid = item[config.setting.primaryKey] || ''
+      let pval = item[config.wrap.parentField] + ''
+      let val = item[config.wrap.valueField] + ''
+      let uuid = item[config.setting.primaryKey] + ''
 
       if (!val || logMap.has(val)) return
 
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index c7c3bb6..b8acc23 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -41,6 +41,7 @@
 const AntvX6 = asyncComponent(() => import('./components/chart/antv-X6'))
 const Voucher = asyncComponent(() => import('./components/module/voucher'))
 const Account = asyncComponent(() => import('./components/module/account'))
+const Invoice = asyncComponent(() => import('./components/module/invoice'))
 const Iframe = asyncComponent(() => import('./components/iframe'))
 const Calendar = asyncComponent(() => import('./components/calendar'))
 const DebugTable = asyncComponent(() => import('@/tabviews/debugtable'))
@@ -1698,6 +1699,12 @@
             <Account config={item}/>
           </Col>
         )
+      } else if (item.type === 'module' && item.subtype === 'invoice') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <Invoice config={item}/>
+          </Col>
+        )
       } else if (item.type === 'iframe') {
         return (
           <Col span={item.width} style={style} key={item.uuid}>
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
index 3bba3de..7b86cb8 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
@@ -722,33 +722,32 @@
 
           let cols = _verify.columns.map(col => col.Column.toLowerCase())
           cols = Array.from(new Set(cols))
+          let error = ''
 
           if (_verify.columns.length === 0) {
-            notification.warning({
-              top: 92,
-              message: '璇疯缃瓻xcel鍒楀瓧娈�!',
-              duration: 5
-            })
-            return
+            error = '璇疯缃瓻xcel鍒楀瓧娈�!'
           } else if (_verify.columns.length > cols.length) {
-            notification.warning({
-              top: 92,
-              message: 'Excel鍒楀瓧娈靛悕锛屼笉鍙噸澶�!',
-              duration: 5
-            })
-            return
+            error = 'Excel鍒楀瓧娈靛悕锛屼笉鍙噸澶�!'
+          } else if (cols.includes('bid')) {
+            error = 'bid瀛楁涓轰繚鐣欏瓧锛屼笉鍙娇鐢�!'
+          } else if (cols.includes('jskey')) {
+            error = 'jskey瀛楁涓轰繚鐣欏瓧锛屼笉鍙娇鐢�!'
           } else if (_verify.range === 1) {
             let tEmptys = _verify.columns.filter(op => !op.Text)
             if (tEmptys.length > 0) {
-              notification.warning({
-                top: 92,
-                message: '蹇界暐棣栬鏃讹紝浼氫娇鐢═ext鍊兼牎楠孍xcel棣栬鍐呭锛孴ext鍊间笌Excel琛ㄩ琛屽唴瀹圭浉鍚岋紝涓斿潎涓嶅彲涓虹┖锛�',
-                duration: 5
-              })
-              return
+              error = '蹇界暐棣栬鏃讹紝浼氫娇鐢═ext鍊兼牎楠孍xcel棣栬鍐呭锛孴ext鍊间笌Excel琛ㄩ琛屽唴瀹圭浉鍚岋紝涓斿潎涓嶅彲涓虹┖锛�'
             }
           }
 
+          if (error) {
+            notification.warning({
+              top: 92,
+              message: error,
+              duration: 5
+            })
+            return
+          }
+
           _verify.columns.sort((a, b) => {
             if (a.import === 'init' && b.import !== 'init') {
               return 1

--
Gitblit v1.8.0