From 316877c1d9e5b6d92334f30b03d97d7e833cd934 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期二, 02 二月 2021 16:01:57 +0800
Subject: [PATCH] 2021-02-02

---
 src/menu/components/code/sandbox/editorcode/index.scss                            |   20 
 src/templates/zshare/modalform/index.jsx                                          |   11 
 src/tabviews/formtab/index.jsx                                                    |   19 
 src/tabviews/custom/components/editor/braft-editor/index.jsx                      |  191 
 src/tabviews/verupmanage/actionList/index.jsx                                     |   14 
 src/components/editor/index.scss                                                  |   29 
 src/tabviews/zshare/mutilform/index.scss                                          |    5 
 src/menu/components/editor/braft-editor/wrapsetting/index.scss                    |    7 
 src/menu/sysinterface/settingform/index.scss                                      |   65 
 src/tabviews/custom/components/share/braftContent/index.scss                      |   26 
 src/tabviews/scriptmanage/config.jsx                                              |    4 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx                  |   60 
 src/tabviews/zshare/settingcomponent/index.jsx                                    |    4 
 src/templates/zshare/verifycard/customscript/index.jsx                            |   18 
 src/views/menudesign/printmenuform/index.jsx                                      |   10 
 src/locales/zh-CN/model.js                                                        |    3 
 src/menu/components/share/sourcecomponent/inputform/index.scss                    |   60 
 src/menu/pastecontroller/index.scss                                               |    4 
 src/menu/stylecontroller/index.scss                                               |   12 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx |    6 
 src/tabviews/zshare/actionList/newpagebutton/index.jsx                            |    8 
 src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx             |    2 
 src/menu/components/share/sourcecomponent/index.scss                              |   42 
 src/tabviews/commontable/index.jsx                                                |  254 +
 src/menu/components/code/sandbox/wrapsetting/settingform/index.scss               |   15 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx              |    2 
 src/menu/datasource/verifycard/index.jsx                                          |    2 
 src/assets/css/main.scss                                                          |   37 
 src/menu/components/card/cardcellcomponent/index.jsx                              |    1 
 src/menu/components/editor/braft-editor/wrapsetting/index.jsx                     |   83 
 src/menu/picturecontroller/editform/index.scss                                    |   14 
 src/tabviews/verupmanage/actionList/index.scss                                    |   27 
 src/tabviews/zshare/mutilform/customTextArea/index.scss                           |    0 
 src/templates/zshare/verifycard/callbackcustomscript/index.scss                   |    5 
 src/components/editor/index.jsx                                                   |  216 
 src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx    |  206 
 src/menu/sysinterface/settingform/baseform/index.scss                             |   22 
 src/menu/components/code/sandbox/wrapsetting/index.jsx                            |   83 
 src/tabviews/scriptmanage/actionList/index.scss                                   |   27 
 src/templates/subtableconfig/index.jsx                                            |   36 
 src/menu/components/group/groupcomponents/card.jsx                                |    3 
 src/menu/modulesource/dragsource/index.scss                                       |    0 
 src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx           |    3 
 src/tabviews/custom/components/editor/braft-editor/index.scss                     |   35 
 src/tabviews/custom/components/share/braftContent/index.jsx                       |   58 
 src/tabviews/zshare/mutilform/index.jsx                                           |  186 
 src/menu/components/editor/braft-editor/editorcontent/index.jsx                   |   82 
 src/menu/components/share/actioncomponent/actionform/index.jsx                    |  106 
 src/tabviews/custom/index.jsx                                                     |  195 
 src/views/menudesign/index.scss                                                   |   12 
 src/templates/formtabconfig/actionform/index.scss                                 |   10 
 src/templates/zshare/customscript/index.jsx                                       |    4 
 src/menu/components/tabs/tabcomponents/card.jsx                                   |    3 
 src/menu/popview/index.jsx                                                        |  129 
 src/assets/css/viewstyle.scss                                                     |   10 
 src/views/printTemplate/index.jsx                                                 |   13 
 src/menu/components/group/groupcomponents/index.jsx                               |    1 
 src/components/video/index.jsx                                                    |    2 
 src/templates/comtableconfig/index.jsx                                            |   40 
 src/tabviews/zshare/actionList/exceloutbutton/index.jsx                           |   51 
 src/menu/modulesource/dragsource/index.jsx                                        |    0 
 src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.scss |   45 
 src/api/index.js                                                                  |  135 
 src/tabviews/subtable/index.jsx                                                   |  333 +
 src/templates/sharecomponent/actioncomponent/index.scss                           |    6 
 src/menu/components/code/sandbox/wrapsetting/index.scss                           |    7 
 src/templates/zshare/codemirror/index.jsx                                         |    4 
 src/menu/sysinterface/index.jsx                                                   |  215 
 src/menu/sysinterface/settingform/baseform/index.jsx                              |  243 +
 src/tabviews/scriptmanage/actionList/index.jsx                                    |   12 
 src/templates/sharecomponent/tabscomponent/tabform/index.jsx                      |    6 
 src/templates/sharecomponent/settingcomponent/index.scss                          |    1 
 src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss        |   15 
 src/menu/components/share/actioncomponent/index.jsx                               |    3 
 src/setupProxy.js                                                                 |    9 
 src/menu/picturecontroller/video/index.jsx                                        |   29 
 src/menu/picturecontroller/editform/index.jsx                                     |  160 
 src/templates/modalconfig/source.jsx                                              |    6 
 src/tabviews/scriptmanage/index.jsx                                               |   88 
 src/components/sidemenu/index.jsx                                                 |   21 
 src/tabviews/zshare/mutilform/customTextArea/index.jsx                            |   72 
 src/views/login/index.jsx                                                         |   19 
 src/menu/components/code/sandbox/index.scss                                       |   36 
 src/menu/bgcontroller/index.scss                                                  |    6 
 src/menu/modulesource/index.scss                                                  |    0 
 src/utils/utils-datamanage.js                                                     |  248 +
 src/menu/picturecontroller/index.jsx                                              |  320 +
 src/menu/sysinterface/settingform/simplescript/index.jsx                          |  450 ++
 src/tabviews/custom/components/code/sand-box/index.jsx                            |  222 +
 src/templates/zshare/editTable/index.jsx                                          |   18 
 src/menu/stylecombcontrolbutton/index.scss                                        |    3 
 src/tabviews/formtab/actionList/index.jsx                                         |    8 
 src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx  |  488 ++
 src/tabviews/zshare/actionList/popupbutton/index.jsx                              |    4 
 src/tabviews/zshare/normalTable/index.jsx                                         |   36 
 src/templates/sharecomponent/actioncomponent/actionform/index.scss                |   16 
 src/views/main/index.jsx                                                          |    5 
 src/menu/picturecontroller/index.scss                                             |   88 
 src/tabviews/zshare/actionList/excelInbutton/index.jsx                            |   47 
 src/menu/datasource/index.jsx                                                     |    2 
 src/menu/components/tabs/tabcomponents/index.jsx                                  |    1 
 src/menu/components/table/normal-table/columns/editColumn/index.jsx               |    2 
 src/views/billprint/index.jsx                                                     |   46 
 src/menu/menushell/card.jsx                                                       |    6 
 src/templates/sharecomponent/settingcomponent/settingform/index.jsx               |  201 
 src/menu/components/editor/braft-editor/index.scss                                |   36 
 src/tabviews/subtabtable/index.jsx                                                |  285 +
 src/tabviews/zshare/actionList/normalbutton/index.jsx                             |  957 +++-
 src/templates/modalconfig/dragelement/card.jsx                                    |   18 
 src/views/mobdesign/index.jsx                                                     |    7 
 src/tabviews/custom/components/code/sand-box/index.scss                           |   34 
 src/views/menudesign/index.jsx                                                    |  222 
 src/menu/modulesource/option.jsx                                                  |    4 
 src/menu/sysinterface/index.scss                                                  |   20 
 src/tabviews/verupmanage/subtabtable/index.scss                                   |    2 
 src/menu/sysinterface/settingform/index.jsx                                       |  179 
 src/tabviews/subtable/index.scss                                                  |    2 
 src/menu/sysinterface/settingform/simplescript/index.scss                         |   45 
 src/mob/datasource/verifycard/index.jsx                                           |    2 
 src/menu/components/code/sandbox/codecontent/index.jsx                            |   88 
 src/menu/components/share/actioncomponent/actionform/index.scss                   |   16 
 src/menu/components/share/actioncomponent/formconfig.jsx                          |   91 
 src/tabviews/custom/components/group/normal-group/index.jsx                       |   16 
 src/templates/sharecomponent/actioncomponent/actionform/index.jsx                 |  157 
 src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx                |  152 
 src/menu/components/editor/braft-editor/editorcontent/index.scss                  |   14 
 src/views/main/index.scss                                                         |    2 
 src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx         |  199 
 src/menu/pastecontroller/index.jsx                                                |    2 
 src/menu/components/code/sandbox/codecontent/index.scss                           |   26 
 src/menu/components/card/cardcellcomponent/elementform/index.scss                 |    3 
 src/menu/components/code/sandbox/editorcode/index.jsx                             |  110 
 src/tabviews/verupmanage/index.jsx                                                |    7 
 src/components/Image/index.jsx                                                    |   53 
 src/menu/modulesource/index.jsx                                                   |    0 
 src/assets/mobimg/sandbox.png                                                     |    0 
 src/components/tabview/index.jsx                                                  |    2 
 src/tabviews/zshare/actionList/index.scss                                         |   27 
 src/components/tabview/index.scss                                                 |    9 
 src/menu/components/share/sourcecomponent/inputform/index.jsx                     |  212 
 src/components/sidemenu/index.scss                                                |    8 
 src/templates/formtabconfig/index.jsx                                             |    1 
 src/templates/zshare/verifycard/callbackcustomscript/index.jsx                    |  284 +
 src/components/Image/index.scss                                                   |   13 
 src/menu/components/code/sandbox/index.jsx                                        |  188 
 src/views/billprint/index.scss                                                    |   13 
 src/locales/en-US/model.js                                                        |    3 
 src/tabviews/custom/components/share/normalTable/index.jsx                        |    8 
 src/templates/calendarconfig/tabcomponent/tabform/index.jsx                       |    5 
 src/templates/modalconfig/dragelement/index.jsx                                   |    2 
 src/menu/bgcontroller/index.jsx                                                   |   51 
 src/menu/picturecontroller/video/index.scss                                       |   52 
 src/menu/sysinterface/settingform/utils.jsx                                       |   45 
 src/templates/sharecomponent/settingcomponent/settingform/utils.jsx               |   57 
 src/utils/utils.js                                                                | 1796 ++++----
 src/menu/components/share/usercomponent/index.jsx                                 |   18 
 src/tabviews/zshare/actionList/tabbutton/index.jsx                                |    3 
 src/templates/modalconfig/index.scss                                              |    4 
 src/utils/utils-update.js                                                         |  311 
 src/tabviews/custom/components/share/tabtransfer/index.jsx                        |   16 
 src/menu/stylecontroller/index.jsx                                                |   79 
 src/templates/zshare/formconfig.jsx                                               |  153 
 src/templates/zshare/verifycard/index.jsx                                         |  259 
 src/components/querylog/index.jsx                                                 |   80 
 src/menu/components/share/sourcecomponent/index.jsx                               |   81 
 src/templates/formtabconfig/actionform/index.jsx                                  |   12 
 src/tabviews/zshare/actionList/printbutton/index.jsx                              |   16 
 /dev/null                                                                         |   34 
 src/menu/components/card/cardcellcomponent/formconfig.jsx                         |   13 
 src/templates/sharecomponent/actioncomponent/index.jsx                            |    4 
 src/menu/components/editor/braft-editor/index.jsx                                 |  196 
 src/menu/stylecombcontrolbutton/index.jsx                                         |    2 
 src/tabviews/zshare/fileupload/index.jsx                                          |   80 
 src/menu/menushell/index.jsx                                                      |    2 
 src/assets/mobimg/editor.png                                                      |    0 
 src/templates/sharecomponent/columncomponent/columnform/index.jsx                 |    2 
 176 files changed, 10,611 insertions(+), 2,554 deletions(-)

diff --git a/src/api/index.js b/src/api/index.js
index 0c51795..244645a 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -41,13 +41,11 @@
 axios.defaults.withCredentials = true
 
 axios.interceptors.request.use((config) => {
-  config.method = 'post'
   if (config.url.includes('LoginAndRedirect') || config.url.includes('getjsonresult') || config.url.includes('wxNativePay')) {
     config.data = qs.stringify(config.data)
   } else if (config.url.includes('Upload') || config.url.includes('doupload') || config.url.includes('dopreload')) {
     config.headers = { 'Content-Type': 'multipart/form-data' }
-  } else {
-    // config.headers.token = sessionStorage.getItem('TOKEN') || ''
+  } else if (config.method === 'post') {
     config.data = JSON.stringify(config.data)
   }
 
@@ -101,7 +99,7 @@
       duration: 15
     })
   }
-  return Promise.reject(error)
+  return Promise.reject(error.response)
 })
 
 class Api {
@@ -122,6 +120,7 @@
 
     return axios({
       url: '/webapi/dostar',
+      method: 'post',
       data: param
     })
   }
@@ -144,6 +143,30 @@
     })
   }
 
+  /* @description 鐩存帴璇锋眰
+   * @param {Object} param 鏌ヨ鍙婃彁浜ゅ弬鏁�
+   */
+  directRequest (url, method = 'post', param) {
+    let params = { method: 'post' }
+    let _url = url
+
+    if (method === 'get' && param) {
+      let keys = Object.keys(param).map(key => `${key}=${param[key]}`)
+      keys = keys.join('&')
+      if (keys) {
+        _url = _url + '?' + keys
+      }
+    } else if (method === 'post' && param) {
+      params.data = param
+    }
+
+    _url = _url.replace(/&/ig, '%26')
+    // _url = window.btoa(_url)
+    params.url = '/trans/redirect?rd=' + _url + '&method=' + method
+
+    return axios(params)
+  }
+
   /**
    * @description 浣跨敤dostar鎺ュ彛锛岃烦杞嚦dostars
    * @param {Object} param 鏌ヨ鍙婃彁浜ゅ弬鏁�
@@ -156,6 +179,7 @@
 
     return axios({
       url: '/webapi/dostar',
+      method: 'post',
       data: param
     })
   }
@@ -189,6 +213,7 @@
 
     return axios({
       url: '/webapi/dologon',
+      method: 'post',
       data: param
     })
   }
@@ -220,6 +245,7 @@
 
     return axios({
       url: '/webapi/dologon',
+      method: 'post',
       data: param
     })
   }
@@ -250,6 +276,7 @@
 
     return axios({
       url: '/webapi/dologon',
+      method: 'post',
       data: param
     })
   }
@@ -312,6 +339,7 @@
       return new Promise((resolve, reject) => {
         axios({
           url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+          method: 'post',
           data: param
         }).then(res => {
           if (!res.status) {
@@ -442,6 +470,7 @@
 
     return axios({
       url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      method: 'post',
       data: param
     })
   }
@@ -482,6 +511,7 @@
       return new Promise(resolve => {
         axios({
           url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+          method: 'post',
           data: param
         }).then(res => {
           if (res.status) {
@@ -515,6 +545,7 @@
 
     return axios({
       url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      method: 'post',
       data: param
     })
   }
@@ -533,6 +564,7 @@
 
     return axios({
       url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      method: 'post',
       data: param
     })
   }
@@ -586,6 +618,7 @@
             } else {
               axios({
                 url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+                method: 'post',
                 data: param
               }).then(res => {
                 if (res.status) {
@@ -600,6 +633,7 @@
 
             axios({
               url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+              method: 'post',
               data: param
             }).then(res => {
               if (res.status) {
@@ -618,6 +652,7 @@
       return new Promise(resolve => {
         axios({
           url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+          method: 'post',
           data: param
         }).then(res => {
           if (res.status) {
@@ -730,6 +765,7 @@
       return new Promise(resolve => {
         axios({
           url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+          method: 'post',
           data: param
         }).then(res => {
           if (res.status) {
@@ -751,8 +787,8 @@
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
 
-    if (sessionStorage.getItem('isEditState') === 'true') { // HS涓嬭彍鍗�
-      param.userid = sessionStorage.getItem('CloudUserID')
+    if (options.cloudServiceApi && param.rduri === options.cloudServiceApi) { // HS涓嬭彍鍗�
+      param.userid = sessionStorage.getItem('CloudUserID') || ''
       param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
     }
 
@@ -760,6 +796,7 @@
 
     return axios({
       url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      method: 'post',
       data: param
     })
   }
@@ -778,6 +815,7 @@
       axios({
         url: '/webapi/doexcel',
         responseType: 'blob',
+        method: 'post',
         data: param
       }).then(res => {
 
@@ -819,28 +857,15 @@
    * @description 涓婁紶base64
    * @param {String} base64 base64鍥剧墖缂栫爜
    */
-  fileuploadbase64 (base64, service = 'local') {
-    let param = {
-      func: '',
-      BasePath: 'Content/Upload',
-      lang: localStorage.getItem('lang') || '',
-      appkey: window.GLOB.appkey || '',
-      Base64Img: base64
-    }
+  fileuploadbase64 (param) {
+    param.func = ''
+    param.BasePath = 'Content/Upload'
+    param.lang = localStorage.getItem('lang') || ''
+    param.appkey = window.GLOB.appkey || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
 
-    if (service === 'sso' && window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi
-      param.userid = sessionStorage.getItem('UserID')
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    } else if (service === 'cloud' && options.cloudServiceApi) {
-      param.rduri = options.cloudServiceApi
-      param.userid = sessionStorage.getItem('CloudUserID')
-      param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
-    } else {
-      param.userid = sessionStorage.getItem('UserID')
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    }
+    param.userid = param.userid || sessionStorage.getItem('UserID') || ''
+    param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
 
     param = this.encryptParam(param)
 
@@ -849,11 +874,13 @@
 
       return axios({
         url: '/webapi/dostars',
+        method: 'post',
         data: param
       })
     } else {
       return axios({
         url: '/webapi/SaveBase64Image',
+        method: 'post',
         data: param
       })
     }
@@ -865,6 +892,7 @@
   getLargeFileUpload (param) {
     return axios({
       url: '/webapi/doupload',
+      method: 'post',
       data: param
     })
   }
@@ -875,6 +903,7 @@
   getFilePreUpload (param) {
     return axios({
       url: '/webapi/dopreload',
+      method: 'post',
       data: param
     })
   }
@@ -885,53 +914,21 @@
   getWxNativePay (param) {
     return axios({
       url: '/wxpay/wxNativePay',
+      method: 'post',
       data: param
     })
   }
 
-  /**
-   * @description 鏂囦欢涓婁紶
-   */
-  getFileUpload (param) {
-    return axios({
-      url: '/zh-CN/Home/Upload',
-      data: param
-    })
-  }
-
-  /**
-   * @description 閫氱敤鎺ュ彛(鏁版嵁绠$悊)
-   * @param {Object} param 鏌ヨ鍙婃彁浜ゅ弬鏁�
-   */
-  commonInterface (param) {
-    param.userid = sessionStorage.getItem('UserID')
-    param.lang = localStorage.getItem('lang') || ''
-    param.SessionUid = localStorage.getItem('SessionUid') || ''
-    param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    param.BID = param.BID || ''
-    param.debug = param.debug || ''
-    
-    return axios({
-      url: '/webapi/dostar',
-      data: param
-    })
-  }
-
-  /**
-   * @description 閫氱敤鎺ュ彛(鎻愪氦)(鏁版嵁绠$悊)
-   * @param {Object} param 鏌ヨ鍙婃彁浜ゅ弬鏁�
-   */
-  submitInterface (param) {
-    param.userid = sessionStorage.getItem('UserID')
-    param.lang = localStorage.getItem('lang') || ''
-    param.SessionUid = localStorage.getItem('SessionUid') || ''
-    param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-
-    return axios({
-      url: '/webapi/dostar',
-      data: param
-    })
-  }
+  // /**
+  //  * @description 鏂囦欢涓婁紶
+  //  */
+  // getFileUpload (param) {
+  //   return axios({
+  //     url: '/zh-CN/Home/Upload',
+  //     method: 'post',
+  //     data: param
+  //   })
+  // }
 }
 
 export default new Api()
\ No newline at end of file
diff --git a/src/assets/css/main.scss b/src/assets/css/main.scss
index 400975a..3d90a6e 100644
--- a/src/assets/css/main.scss
+++ b/src/assets/css/main.scss
@@ -71,7 +71,7 @@
   background-color: unset;
   color: unset;
 }
-.side-menu.ant-menu-dark .ant-menu-item.ant-menu-item-selected {
+.mk-side-menu.ant-menu-dark .ant-menu-item.ant-menu-item-selected {
   background-color: unset;
   color: unset;
 }
@@ -211,6 +211,9 @@
 .ant-popover.popover-confirm {
   z-index: 1080!important;
 }
+.ant-message {
+  z-index: 1110!important;
+}
 
 .ant-notification-notice.notification-custom-error {
   background: #f5222d;
@@ -258,7 +261,7 @@
 .ant-modal.popview-modal {
   top: 70px;
   .ant-modal-body {
-    min-height: 250px;
+    min-height: 200px;
     max-height: calc(100vh - 210px);
     overflow-y: auto;
   }
@@ -282,7 +285,7 @@
     top: 70px;
   }
   .ant-modal-body {
-    min-height: 250px;
+    min-height: 200px;
     max-height: calc(100vh - 210px);
     overflow-y: auto;
   }
@@ -301,6 +304,34 @@
     background: rgba(0, 0, 0, 0);
   }
 }
+// 璁剧疆妯℃�佹鏍峰紡锛岃瀹氭渶澶ф渶灏忛珮搴︼紝閲嶇疆婊氬姩鏉�
+.action-modal {
+  .ant-modal {
+    max-width: 95vw;
+    top: 70px;
+  }
+  .ant-modal-body {
+    max-height: calc(100vh - 205px);
+    min-height: 150px;
+    overflow-y: auto;
+    padding-bottom: 35px;
+  }
+  .ant-modal-body::-webkit-scrollbar {
+    width: 7px;
+    height: 7px;
+  }
+  .ant-modal-body::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+    background: rgba(0, 0, 0, 0.13);
+  }
+  .ant-modal-body::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0.07);
+    background: rgba(0, 0, 0, 0);
+  }
+}
 
 .ant-drawer {
   z-index: 1080!important;
diff --git a/src/assets/css/viewstyle.scss b/src/assets/css/viewstyle.scss
index 94b6ba0..09c8fb5 100644
--- a/src/assets/css/viewstyle.scss
+++ b/src/assets/css/viewstyle.scss
@@ -53,7 +53,7 @@
     }
   }
 
-  #root > .main-view {
+  #root > .mk-main-view {
     > .header-container {
       background: $bg1;
       color: $font1;
@@ -86,7 +86,7 @@
         }
       }
     }
-    > .side-menu {
+    > .mk-side-menu {
       border-right: 1px solid #d9d9d9;
       background: $bg1;
       > .ant-menu {
@@ -122,7 +122,7 @@
         }
       }
     }
-    > .side-menu:not(.edit) {
+    > .mk-side-menu:not(.edit) {
       > .ant-menu {
         > .ant-menu-submenu {
           > .ant-menu-sub {
@@ -137,7 +137,7 @@
         }
       }
     }
-    >.content-box {
+    >.mk-tabview-wrap {
       >.content-header {
         >.ant-tabs {
           >.ant-tabs-bar {
@@ -321,7 +321,7 @@
 }
 
 @mixin bgblack() {
-  #root > .main-view {
+  #root > .mk-main-view {
     > .header-container {
       box-shadow: none;
       > .header-menu {
diff --git a/src/assets/mobimg/editor.png b/src/assets/mobimg/editor.png
new file mode 100644
index 0000000..993dac9
--- /dev/null
+++ b/src/assets/mobimg/editor.png
Binary files differ
diff --git a/src/assets/mobimg/sandbox.png b/src/assets/mobimg/sandbox.png
new file mode 100644
index 0000000..b77e217
--- /dev/null
+++ b/src/assets/mobimg/sandbox.png
Binary files differ
diff --git a/src/components/Image/index.jsx b/src/components/Image/index.jsx
new file mode 100644
index 0000000..01bd23e
--- /dev/null
+++ b/src/components/Image/index.jsx
@@ -0,0 +1,53 @@
+import React, {Component} from 'react'
+import './index.scss'
+
+class ImageWrap extends Component {
+  componentDidMount () {
+    let Img = new Image()
+    Img.src = this.props.url
+    
+    if (Img.complete) {
+      this.setSize(Img.width, Img.height)
+    } else {
+      Img.onload = () => {
+        this.setSize(Img.width, Img.height)
+      }
+    }
+  }
+
+  shouldComponentUpdate () {
+    return false
+  }
+
+  setSize = (width, height) => {
+    const { clientWidth, clientHeight } = this.ImageWrapDom
+
+    if (!clientWidth || !clientHeight || !width || !height) return
+
+    let ratio = (width / height) / (clientWidth / clientHeight)
+
+    if (ratio > 1.5) {
+      let _height = Math.floor(height * (clientWidth / width))
+      this.ImageDom.style.height = 'auto'
+      this.ImageDom.style.top = ((clientHeight - _height) / 2) + 'px'
+    } else if (ratio > 1 && ratio < 1.5) {
+      let _width = Math.floor(width / (height / clientHeight))
+      this.ImageDom.style.width = _width + 'px'
+      this.ImageDom.style.left = '-' + ((_width - clientWidth) / 2) + 'px'
+    } else if (ratio < 1) {
+      let _height = Math.floor(height / (width / clientWidth))
+      this.ImageDom.style.height = _height + 'px'
+      this.ImageDom.style.top = '-' + ((_height - clientHeight) / 2) + 'px'
+    }
+  }
+
+  render() {
+    return (
+      <div className="mk_image-wrap" ref={dom => { this.ImageWrapDom = dom }}>
+        <img src={this.props.url} ref={dom => { this.ImageDom = dom }} alt=""/>
+      </div>
+    )
+  }
+}
+
+export default ImageWrap
\ No newline at end of file
diff --git a/src/components/Image/index.scss b/src/components/Image/index.scss
new file mode 100644
index 0000000..3e5b646
--- /dev/null
+++ b/src/components/Image/index.scss
@@ -0,0 +1,13 @@
+.mk_image-wrap {
+  display: inline-block;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  height: 100%;
+
+  img {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+  }
+}
diff --git a/src/components/editor/index.jsx b/src/components/editor/index.jsx
index 58bfb60..a94d12e 100644
--- a/src/components/editor/index.jsx
+++ b/src/components/editor/index.jsx
@@ -1,38 +1,242 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
+import BraftEditor from 'braft-editor'
 import 'braft-editor/dist/index.css'
 import 'braft-extensions/dist/table.css'
-import BraftEditor from 'braft-editor'
+// import 'braft-editor/dist/output.css'
 import Table from 'braft-extensions/dist/table'
+import SparkMD5 from 'spark-md5'
+import moment from 'moment'
 
+import Api from '@/api'
 import './index.scss'
 
 BraftEditor.use(Table())
 
+let service = ''
+if (process.env.NODE_ENV === 'production') {
+  service = document.location.origin + '/' + window.GLOB.service
+} else {
+  service = window.GLOB.location + '/' + window.GLOB.service
+}
+
 class NormalEditor extends Component {
   static propTpyes = {
-    card: PropTypes.object,  // 鏉$爜璁剧疆
-    value: PropTypes.any,    // 鏉$爜鍊�
+    Item: PropTypes.object,     // 琛ㄥ崟鍏冪礌
+    onChange: PropTypes.func,   // 琛ㄥ崟鏇存柊
   }
 
+  state = {
+    editorState: '',
+    encryption: 'false',
+  }
+
+  UNSAFE_componentWillMount () {
+    let initVal = null
+    let encryption = 'false'
+
+    if (this.props['data-__meta']) {
+      initVal = this.props['data-__meta'].initialValue || null
+    } else if (this.props.defaultValue) {
+      initVal = this.props.defaultValue || null
+    }
+
+    if (this.props.Item && this.props.Item.encryption === 'true') {
+      encryption = 'true'
+      if (initVal) {
+        try {
+          initVal = window.decodeURIComponent(window.atob(initVal))
+        } catch {
+          initVal = this.props['data-__meta'].initialValue || null
+        }
+      }
+    }
+    
+    this.setState({
+      editorState: BraftEditor.createEditorState(initVal),
+      encryption
+    })
+  }
   shouldComponentUpdate (nextProps, nextState) {
     return !is(fromJS(this.props), fromJS(nextProps))
   }
 
-  handleEditorChange = () => {
+  handleEditorChange = (editorState) => {
+    const { encryption } = this.state
 
+    this.setState({ editorState })
+    if (this.props.onChange) {
+      let val = editorState.toHTML()
+      if (encryption === 'true') {
+        try {
+          val = window.btoa(window.encodeURIComponent(val))
+        } catch {
+          val = editorState.toHTML()
+        }
+      }
+      this.props.onChange(val)
+    }
   }
 
-  submitContent = () => {
+  shardupload = (params, param) => {
+    let _param = params.chunks.shift()
+    let form = new FormData()
 
+    form.append('file', _param.binary)
+    form.append('fileMd5', params.file.fileMd5)
+    form.append('shardingMd5', _param.chunkMd5)
+    form.append('baseDomain', service)
+    form.append('rootPath', 'Content/images/upload/')
+    form.append('fileName', params.file.fileName)
+    form.append('fileExt', params.file.fileType)
+    form.append('shardingCnt', _param.chunks)
+    form.append('shardingNo', _param.chunk)
+
+    Api.getLargeFileUpload(form).then(res => {
+      if (res.status) {
+        if (params.chunks.length > 0) {
+          param.progress(Math.floor(100 * (_param.chunk / _param.chunks)))
+          this.shardupload(params, param)
+        } else {
+          if (res.urlPath) {
+            param.success({
+              url: res.urlPath
+            })
+          } else {
+            param.error({
+              url: '涓婁紶澶辫触锛�'
+            })
+          }
+        }
+      } else {
+        param.error({
+          url: '涓婁紶澶辫触锛�'
+        })
+      }
+    })
+  }
+
+  getuuid = () => {
+    let uuid = []
+    let _options = '0123456789abcdefghigklmnopqrstuv'
+    for (let i = 0; i < 19; i++) {
+      uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
+    }
+    uuid = uuid.join('')
+    return uuid
+  }
+
+  handleUpload(param) {
+    const file = param.file
+
+    let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
+    let chunkSize = 1024 * 1024 * 2                // 鍒囩墖姣忔2M
+    let chunks = Math.ceil(file.size / chunkSize)  // 鍒囩墖鎬绘暟
+    let currentChunk = 0                           // 褰撳墠涓婁紶鐨刢hunk
+    let spark = new SparkMD5.ArrayBuffer()         // 瀵筧rrayBuffer鏁版嵁杩涜md5鍔犲瘑锛屼骇鐢熶竴涓猰d5瀛楃涓�
+    let chunkFileReader = new FileReader()         // 鐢ㄤ簬璁$畻鍑烘瘡涓猚hunkMd5
+    let totalFileReader = new FileReader()         // 鐢ㄤ簬璁$畻鍑烘�绘枃浠剁殑fileMd5
+    let params = {chunks: [], file: {}}            // 鐢ㄤ簬涓婁紶鎵�鏈夊垎鐗囩殑md5淇℃伅
+
+    params.file.fileName = file.name.replace(/\.{1}[^.]*$/ig, '')  // 鏂囦欢鍚嶏紙鍘婚櫎鍚庣紑鍚嶏級
+    params.file.fileType = file.name.replace(/^.*\.{1}/ig, '')     // 鏂囦欢绫诲瀷
+    params.file.fileSize = file.size                               // 鏂囦欢澶у皬
+    params.file.fileChunks = chunks                                // 璁板綍鎵�鏈塩hunks鐨勯暱搴�
+
+    if (!/^[A-Za-z0-9]+$/.test(params.file.fileName)) {            // 鏂囦欢鍚嶇О鍚湁鑻辨枃鍙婃暟瀛椾箣澶栧瓧绗︽椂锛屽悕绉扮郴缁熺敓鎴�
+      params.file.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
+    }
+
+    totalFileReader.readAsArrayBuffer(file)
+    totalFileReader.onload = (e) => {   // 瀵规暣涓猼otalFile鐢熸垚md5
+      spark.append(e.target.result)
+      params.file.fileMd5 = spark.end() // 璁$畻鏁翠釜鏂囦欢鐨刦ileMd5
+
+      let _param = new FormData()
+      _param.append('fileMd5', params.file.fileMd5)
+      
+      Api.getFilePreUpload(_param).then(res => {
+        if (res.status && res.urlPath) {
+          param.success({
+            url: res.urlPath
+          })
+        } else if (res.shardings && res.shardings.length > 0) {
+          res.shardings.forEach(shard => {
+            if (shard.shardingNo && parseInt(shard.shardingNo) > currentChunk) {
+              currentChunk = parseInt(shard.shardingNo)
+            }
+          })
+          loadNext()
+        } else {
+          loadNext()
+        }
+      })
+    }
+
+    chunkFileReader.onload = (e) => {
+      spark.append(e.target.result)      // 瀵规瘡涓�鐗囧垎鐗囪繘琛宮d5鍔犲瘑
+      
+      params.chunks[params.chunks.length - 1].chunkMd5 = spark.end() // 娣诲姞鍒囩墖md5
+      
+      currentChunk++  // 姣忎竴娆″垎鐗噊nload,currentChunk閮介渶瑕佸鍔狅紝浠ヤ究鏉ヨ绠楀垎鐗囩殑娆℃暟
+
+      if (currentChunk < chunks) { // 褰撳墠鍒囩墖鎬绘暟娌℃湁杈惧埌鎬绘暟鏃�
+        loadNext()
+      } else {
+        this.shardupload(params, param)
+      }
+    }
+
+    chunkFileReader.onerror = () => {
+      param.error({
+        url: '涓婁紶澶辫触锛�'
+      })
+      console.warn('File reading failed.')
+    }
+    totalFileReader.onerror = () => {
+      param.error({
+        url: '涓婁紶澶辫触锛�'
+      })
+    }
+
+    let loadNext = () => {
+      let start = currentChunk * chunkSize              // 璁$畻鍒嗙墖鐨勮捣濮嬩綅缃�
+      let end = Math.min(file.size, start + chunkSize)  // 璁$畻鍒嗙墖鐨勭粨鏉熶綅缃�
+
+      let obj = {                                       // 姣忎竴涓垎鐗囬渶瑕佸寘鍚殑淇℃伅
+        chunk: currentChunk + 1,
+        binary: file.slice(start, end),
+        start: start,
+        end: end,
+        chunks
+      }
+
+      params.chunks.push(obj)
+      chunkFileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
+    }
   }
 
   render() {
+    const { editorState } = this.state
 
     return (
       <div className="normal-braft-editor">
-        <BraftEditor value={'<p></p>'} onChange={this.handleEditorChange} onSave={this.submitContent}/>
+        <BraftEditor
+          value={editorState}
+          onChange={this.handleEditorChange}
+          media={{
+            uploadFn: (param) => {
+              this.handleUpload(param)
+            },
+            validate: () => {
+              return true
+            },
+            onInsert: () => {
+
+            }
+          }}
+        />
       </div>
     )
   }
diff --git a/src/components/editor/index.scss b/src/components/editor/index.scss
index e69de29..44e33dc 100644
--- a/src/components/editor/index.scss
+++ b/src/components/editor/index.scss
@@ -0,0 +1,29 @@
+.normal-braft-editor {
+  .bf-content {
+    overflow-x: hidden;
+    overflow-y: auto;
+    height: auto;
+    min-height: 500px;
+    padding-bottom: 0px;
+    .public-DraftEditor-content {
+      padding-bottom: 0;
+    }
+  }
+  .bf-content::-webkit-scrollbar {
+    width: 7px;
+  }
+  .bf-content::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+    background: rgba(0, 0, 0, 0.13);
+  }
+  .bf-content::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0.07);
+    background: rgba(0, 0, 0, 0);
+  }
+  .mk-braft-delete {
+    font-size: 16px;
+  }
+}
diff --git a/src/components/querylog/index.jsx b/src/components/querylog/index.jsx
new file mode 100644
index 0000000..13e2293
--- /dev/null
+++ b/src/components/querylog/index.jsx
@@ -0,0 +1,80 @@
+import { Component } from 'react'
+import moment from 'moment'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import MKEmitter from '@/utils/events.js'
+
+class QueryLog extends Component {
+  state = {
+    logs: []
+  }
+
+  componentDidMount () {
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.addListener('queryTrigger', this.queryTrigger)
+      setTimeout(() => {
+        this.sendLog()
+      }, 300000)
+    }
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('queryTrigger', this.queryTrigger)
+  }
+
+  sendLog = () => {
+    const { logs } = this.state
+
+    if (logs && logs.length > 0 && sessionStorage.getItem('isEditState') !== 'true') {
+      let logMap = new Map()
+
+      logs.forEach(item => {
+        if (logMap.has(item.menuId)) {
+          let _item = logMap.get(item.menuId)
+          _item.times++
+          logMap.set(item.menuId, _item)
+        } else {
+          item.times = 1
+          logMap.set(item.menuId, item)
+        }
+      })
+      let userid = sessionStorage.getItem('UserID') || ''
+      let LText = [...logMap.values()].map(item => `select '${item.menuId}','${item.times}','${item.name}','${window.GLOB.appkey}','${userid}'`)
+
+      let param = {
+        func: 's_get_users_operation_log',
+        exec_type: 'y', // 鍚庡彴瑙g爜
+        LText: LText.join(' union all ')
+      }
+
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt('', param.timestamp)
+      param.LText = Utils.formatOptions(param.LText)
+
+      Api.getSystemConfig(param)
+
+      this.setState({logs: []})
+    }
+
+    setTimeout(() => {
+      this.sendLog()
+    }, 300000)
+  }
+
+  queryTrigger = (item) => {
+    this.setState({logs: [...this.state.logs, item]})
+  }
+
+  render () {
+    return null
+  }
+}
+
+export default QueryLog
\ No newline at end of file
diff --git a/src/components/sidemenu/index.jsx b/src/components/sidemenu/index.jsx
index 84d18f7..fe1c20f 100644
--- a/src/components/sidemenu/index.jsx
+++ b/src/components/sidemenu/index.jsx
@@ -9,6 +9,7 @@
 import { modifyTabview, resetEditLevel, modifyMenuTree, modifyMainMenu } from '@/store/action'
 import { SySMenuList } from './config'
 import options from '@/store/options.js'
+import MKEmitter from '@/utils/events.js'
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Api from '@/api'
@@ -83,21 +84,17 @@
     })
   }
 
-  changemenu(e) {
+  changemenu(e, menu) {
+    e.preventDefault()
     if (this.props.editState && this.props.editLevel !== 'HS') {
-      e.preventDefault()
       return
     }
-    
-    let menu = JSON.parse(e.target.dataset.item)
 
     if (menu.OpenType === 'newpage' || menu.OpenType === 'NewPage') {
-      e.preventDefault()
       window.open(menu.src)
     } else if (menu.OpenType === 'blank') {
       menu.selected = true
       this.props.modifyTabview([menu])
-      e.preventDefault()
     } else {
       let tabs = fromJS(this.props.tabviews).toJS()
       tabs = tabs.filter(tab => {
@@ -114,8 +111,10 @@
         tabs.push(menu)
         this.props.modifyTabview(tabs)
       })
-      
-      e.preventDefault()
+    }
+
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: menu.MenuID, name: '鑿滃崟'})
     }
   }
 
@@ -282,10 +281,10 @@
     const { mainMenu } = this.props
     const editShow = (this.props.editState && !this.props.editLevel) || false
 
-    if (mainMenu === '') return (<span className="side-menu-hidden"></span>)
+    if (mainMenu === '') return (<span className="mk-side-menu-hidden"></span>)
 
     return (
-      <aside className={"side-menu ant-menu-dark" + (this.props.collapse ? ' side-menu-collapsed' : '') + (this.props.isiframe ? ' iframe' : '') + (this.props.editState ? ' edit' : '')}>
+      <aside id="mk-sidemenu-wrap" className={'mk-side-menu ant-menu-dark' + (this.props.collapse ? ' collapsed' : '') + (this.props.isiframe ? ' mk-iframe' : '') + (this.props.editState ? ' mk-edit' : '')}>
         {!(this.props.editLevel === 'level2' || this.props.editLevel === 'level3') &&
           <Menu openKeys={this.state.openKeys} onOpenChange={this.onOpenChange} mode="inline" theme="dark" inlineCollapsed={this.props.collapse}>
           {editShow && <li className="sup-menu"><Icon onClick={this.enterSubEdit} className="edit-check" type="edit" /></li>}
@@ -306,7 +305,7 @@
                 {item.children.map(cell => {
                   return (
                     <Menu.Item key={cell.MenuID}>
-                      <a href={cell.src} id={cell.MenuID} data-item={JSON.stringify(cell)} onClick={this.changemenu.bind(this)}>{cell.MenuName}</a>
+                      <a href={cell.src} id={cell.MenuID} onClick={(e) => this.changemenu(e, cell)}>{cell.MenuName}</a>
                     </Menu.Item>
                   )
                 })}
diff --git a/src/components/sidemenu/index.scss b/src/components/sidemenu/index.scss
index 947f373..e0c6a33 100644
--- a/src/components/sidemenu/index.scss
+++ b/src/components/sidemenu/index.scss
@@ -1,7 +1,7 @@
 @import '../../assets/css/iconfont.css';
 @import '../../assets/css/global.scss';
 
-.side-menu {
+.mk-side-menu {
   flex: 0 0 235px;
   width: 235px;
   padding: 48px 0 40px;
@@ -94,7 +94,7 @@
     left: 187px;
   }
 }
-.side-menu.edit { // 缂栬緫鏃舵帶鍒惰彍鍗曞簳鑹�
+.mk-side-menu.mk-edit { // 缂栬緫鏃舵帶鍒惰彍鍗曞簳鑹�
   .ant-menu-sub.ant-menu-inline {
     > .ant-menu-item.ant-menu-item-selected {
       background: unset;
@@ -104,14 +104,14 @@
     }
   }
 }
-.side-menu.iframe { // tab椤典腑涓篿frame鏃�
+.mk-side-menu.mk-iframe { // tab椤典腑涓篿frame鏃�
   max-height: 100vh;
   overflow-y: scroll;
   &::-webkit-scrollbar {
     display: none;
   }
 }
-.side-menu.side-menu-collapsed { // 宸︿晶鑿滃崟鍚堝苟鏃�
+.mk-side-menu.collapsed { // 宸︿晶鑿滃崟鍚堝苟鏃�
   flex: 0 0 80px;
   width: 80px;
 }
diff --git a/src/components/tabview/index.jsx b/src/components/tabview/index.jsx
index b9afa22..01abafb 100644
--- a/src/components/tabview/index.jsx
+++ b/src/components/tabview/index.jsx
@@ -213,7 +213,7 @@
     const { tabviews, activeId } = this.state
 
     return (
-      <section className={'flex-container content-box' + (this.props.collapse ? ' collapsed' : '')}>
+      <section id="mk-tabview-wrap" className={'mk-tabview-wrap' + (this.props.collapse ? ' collapsed' : '')}>
         <div className="content-header">
           {tabviews && tabviews.length > 0 &&
             <Tabs activeKey={activeId}>
diff --git a/src/components/tabview/index.scss b/src/components/tabview/index.scss
index 69617f6..24f4644 100644
--- a/src/components/tabview/index.scss
+++ b/src/components/tabview/index.scss
@@ -1,4 +1,7 @@
-.content-box {
+.mk-tabview-wrap {
+  display: flex;
+  flex: auto;
+  min-height: 100%;
   padding-top: 48px;
   max-width: calc(100% - 235px);
   transition: max-width 0.2s;
@@ -105,10 +108,10 @@
     right: 30px;
   }
 }
-.content-box.collapsed {
+.mk-tabview-wrap.collapsed {
   max-width: calc(100% - 80px);
 }
-.side-menu-hidden + .content-box, .side-menu-hidden + .content-box.collapsed {
+.mk-side-menu-hidden + .mk-tabview-wrap, .mk-side-menu-hidden + .mk-tabview-wrap.collapsed {
   max-width: 100%;
   >.content-header >.ant-tabs >.ant-tabs-bar {
     display: none;
diff --git a/src/components/video/index.jsx b/src/components/video/index.jsx
index d885278..fef570b 100644
--- a/src/components/video/index.jsx
+++ b/src/components/video/index.jsx
@@ -28,7 +28,7 @@
 
     return (
       <div style={{overflow: 'hidden'}}>
-        <Player poster="" autoPlay={card.autoPlay === 'true'} aspectRatio={card.aspectRatio || '16:9'}>
+        <Player poster="" autoPlay={card.autoPlay === 'true'} aspectRatio={card.aspectRatio || '16:9'} loop={card.loop === 'true'}>
           <source src={value} />
           <BigPlayButton position="center" />
           <ControlBar>
diff --git a/src/locales/en-US/model.js b/src/locales/en-US/model.js
index a806926..799732f 100644
--- a/src/locales/en-US/model.js
+++ b/src/locales/en-US/model.js
@@ -247,6 +247,5 @@
   'model.tooltip.action.guide': '鍦ㄥ乏渚у伐鍏锋爮銆婃寜閽�嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬寜閽嫋鑷虫澶勬坊鍔狅紝濡傞�夋嫨鎸夐挳绫诲瀷涓鸿〃鍗曘�佹柊鏍囩椤电瓑鍚湁閰嶇疆椤甸潰鐨勬寜閽紝鍙湪宸︿晶宸ュ叿鏍�-鎸夐挳-鍙厤缃寜閽锛岀偣鍑绘寜閽畬鎴愮浉鍏抽厤缃�傛敞锛氬綋璁剧疆鎸夐挳鏄剧ず浣嶇疆涓鸿〃鏍兼椂锛屾樉绀哄垪浼氬鍔犳搷浣滃垪銆�',
   'model.tooltip.column.guide': '鍦ㄥ乏渚у伐鍏锋爮銆婃樉绀哄垪銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬樉绀哄垪鎷栬嚦姝ゅ娣诲姞锛涙垨鐐瑰嚮銆婃坊鍔犳樉绀哄垪銆嬫寜閽壒閲忔坊鍔狅紝閫夋嫨鎵归噺娣诲姞鏃讹紝闇�鎻愬墠閫夋嫨浣跨敤琛ㄣ�傛敞锛氭坊鍔犲悎骞跺垪鏃讹紝闇�璁剧疆鍙�夊垪銆�',
   'model.tooltip.tabs.guide': '鍦ㄥ乏渚у伐鍏锋爮銆婃爣绛鹃〉銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬爣绛鹃〉鎷栬嚦姝ゅ娣诲姞銆�',
-  'model.tooltip.func.innerface': '鍐呴儴鎺ュ彛: 鍙嚜瀹氫箟鏁版嵁澶勭悊鍑芥暟锛屽嚱鏁板悕绉伴渶浠ableField绛夊瓧绗﹀紑濮�;',
-  'model.tooltip.func.outface': '澶栭儴鎺ュ彛: 鍙嚜瀹氫箟鏁版嵁澶勭悊鍑芥暟锛屾彁浜ゆ暟鎹粡杩囧唴閮ㄥ嚱鏁板鐞嗗悗锛屼紶鍏ュ閮ㄦ帴鍙o紝鏈缃椂锛屾暟鎹細鐩存帴浼犲叆澶栭儴鎺ュ彛銆�',
+  'model.tooltip.func.innerface': '鍐呴儴鍑芥暟鍚嶇О闇�浠ableField绛夊瓧绗﹀紑濮�;',
 }
\ No newline at end of file
diff --git a/src/locales/zh-CN/model.js b/src/locales/zh-CN/model.js
index bbdcebd..0367a1b 100644
--- a/src/locales/zh-CN/model.js
+++ b/src/locales/zh-CN/model.js
@@ -247,6 +247,5 @@
   'model.tooltip.action.guide': '鍦ㄥ乏渚у伐鍏锋爮銆婃寜閽�嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬寜閽嫋鑷虫澶勬坊鍔狅紝濡傞�夋嫨鎸夐挳绫诲瀷涓鸿〃鍗曘�佹柊鏍囩椤电瓑鍚湁閰嶇疆椤甸潰鐨勬寜閽紝鍙湪宸︿晶宸ュ叿鏍�-鎸夐挳-鍙厤缃寜閽锛岀偣鍑绘寜閽畬鎴愮浉鍏抽厤缃�傛敞锛氬綋璁剧疆鎸夐挳鏄剧ず浣嶇疆涓鸿〃鏍兼椂锛屾樉绀哄垪浼氬鍔犳搷浣滃垪銆�',
   'model.tooltip.column.guide': '鍦ㄥ乏渚у伐鍏锋爮銆婃樉绀哄垪銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬樉绀哄垪鎷栬嚦姝ゅ娣诲姞锛涙垨鐐瑰嚮銆婃坊鍔犳樉绀哄垪銆嬫寜閽壒閲忔坊鍔狅紝閫夋嫨鎵归噺娣诲姞鏃讹紝闇�鎻愬墠閫夋嫨浣跨敤琛ㄣ�傛敞锛氭坊鍔犲悎骞跺垪鏃讹紝闇�璁剧疆鍙�夊垪銆�',
   'model.tooltip.tabs.guide': '鍦ㄥ乏渚у伐鍏锋爮銆婃爣绛鹃〉銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬爣绛鹃〉鎷栬嚦姝ゅ娣诲姞銆�',
-  'model.tooltip.func.innerface': '鍐呴儴鎺ュ彛: 鍙嚜瀹氫箟鏁版嵁澶勭悊鍑芥暟锛屽嚱鏁板悕绉伴渶浠ableField绛夊瓧绗﹀紑濮�;',
-  'model.tooltip.func.outface': '澶栭儴鎺ュ彛: 鍙嚜瀹氫箟鏁版嵁澶勭悊鍑芥暟锛屾彁浜ゆ暟鎹粡杩囧唴閮ㄥ嚱鏁板鐞嗗悗锛屼紶鍏ュ閮ㄦ帴鍙o紝鏈缃椂锛屾暟鎹細鐩存帴浼犲叆澶栭儴鎺ュ彛銆�',
+  'model.tooltip.func.innerface': '鍐呴儴鍑芥暟鍚嶇О闇�浠ableField绛夊瓧绗﹀紑濮�;',
 }
\ No newline at end of file
diff --git a/src/menu/bgcontroller/index.jsx b/src/menu/bgcontroller/index.jsx
index bb8b043..ab2155c 100644
--- a/src/menu/bgcontroller/index.jsx
+++ b/src/menu/bgcontroller/index.jsx
@@ -1,13 +1,15 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Form, Input } from 'antd'
+import { Form } from 'antd'
 
 import zhCN from '@/locales/zh-CN/mob.js'
 import enUS from '@/locales/en-US/mob.js'
-import ColorSketch from '@/mob/colorsketch'
-import FileUpload from '@/tabviews/zshare/fileupload'
+import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
+
+const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
 
 class MobController extends Component {
   static propTpyes = {
@@ -19,7 +21,6 @@
     dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     backgroundColor: '',
     backgroundImage: '',
-    bgimages: [],
   }
 
   UNSAFE_componentWillMount () {
@@ -27,10 +28,7 @@
 
     let bgImg = config.style.backgroundImage || ''
 
-    if (bgImg && /^linear-gradient/.test(bgImg)) {
-      bgImg = bgImg.replace('linear-gradient(', '')
-      bgImg = bgImg.replace(')', '')
-    } else if (bgImg && /^url/.test(bgImg)) {
+    if (bgImg && /^url/.test(bgImg)) {
       bgImg = bgImg.replace('url(', '')
       bgImg = bgImg.replace(')', '')
     }
@@ -59,45 +57,23 @@
     this.props.updateConfig(config)
   }
 
-  /**
-   * @description 鎵嬪姩淇敼璺緞
-   */
-  changeImage = (e) => {
-    let val = e.target.value
+  imgChange = (val) => {
     this.setState({
       backgroundImage: val
     })
 
     let config = fromJS(this.props.config).toJS()
-    val = val.replace(/^\s*|\s*$/ig, '')
 
-    if (/^http|^\/\//.test(val)) {
-      val = `url(${val})`
-    } else if (/,/ig.test(val) && !/^(radial-gradient|linear-gradient)/ig.test(val)) {
-      val = `linear-gradient(${val})`
+    if (val) {
+      config.style.backgroundImage = `url(${val})`
+    } else {
+      delete config.style.backgroundImage
     }
-
-    config.style.backgroundImage = val
     this.props.updateConfig(config)
   }
 
-  imgChange = (list) => {
-    if (list[0] && list[0].response) {
-      this.setState({
-        bgimages: [],
-        backgroundImage: list[0].response
-      })
-
-      let config = fromJS(this.props.config).toJS()
-      config.style.backgroundImage = `url(${list[0].response})`
-      this.props.updateConfig(config)
-    } else {
-      this.setState({bgimages: list})
-    }
-  }
-
   render () {
-    const { backgroundColor, backgroundImage, bgimages } = this.state
+    const { backgroundColor, backgroundImage } = this.state
     const formItemLayout = {
       labelCol: {
         xs: { span: 24 },
@@ -116,8 +92,7 @@
             <ColorSketch value={backgroundColor} onChange={this.changeBackgroundColor} />
           </Form.Item>
           <Form.Item colon={false} label="鍥剧墖">
-            <FileUpload accept=".jpg,.png,.gif,.svg" value={bgimages} maxFile={2} fileType="text" onChange={this.imgChange}/>
-            <Input placeholder="" value={backgroundImage} autoComplete="off" onChange={this.changeImage}/>
+            <SourceComponent value={backgroundImage} type="" placement="right" onChange={this.imgChange}/>
           </Form.Item>
         </Form>
       </div>
diff --git a/src/menu/bgcontroller/index.scss b/src/menu/bgcontroller/index.scss
index cccd02f..0d6ed73 100644
--- a/src/menu/bgcontroller/index.scss
+++ b/src/menu/bgcontroller/index.scss
@@ -6,6 +6,12 @@
     padding-top: 10px;
     line-height: 35px;
   }
+  .mk-source-wrap {
+    height: 32px;
+    .mk-source-item-info {
+      top: 5px;
+    }
+  }
 }
 
 .margin-popover {
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index 16aca5c..f3507e3 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -1,19 +1,21 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { fromJS } from 'immutable'
-import { Form, Row, Col, Input, Select, Icon, Radio, Tooltip, InputNumber, notification } from 'antd'
+import { Form, Row, Col, Input, Select, Icon, Radio, Tooltip, InputNumber } from 'antd'
 
 import { formRule } from '@/utils/option.js'
-import FileUpload from '@/tabviews/zshare/fileupload'
-import ColorSketch from '@/mob/colorsketch'
+import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
+
+const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
 
 const cardTypeOptions = {
   sequence: ['eleType', 'width'],
   text: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix', 'link'],
   number: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix'],
   picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'link'],
-  video: ['eleType', 'datatype', 'width', 'aspectRatio', 'autoPlay'],
+  video: ['eleType', 'datatype', 'width', 'aspectRatio', 'autoPlay', 'loop'],
   icon: ['eleType', 'icon', 'datatype', 'width'],
   slider: ['eleType', 'datatype', 'width', 'color', 'maxValue'],
   splitline: ['eleType', 'color', 'width', 'borderWidth'],
@@ -38,7 +40,6 @@
     link: ''
   }
 
-  
   UNSAFE_componentWillMount () {
     const { card, config } = this.props
     let _options = this.getOptions(card.eleType, card.datatype, card.link)
@@ -74,6 +75,8 @@
           } else if (card.eleType === 'number') {
             item.options = item.oriOptions.filter(op => op.value !== 'YYYY-MM-DD')
           }
+        } else if (item.key === 'url') {
+          item.required = card.eleType !== 'qrcode'
         }
         if (item.key === 'linkurl') {
           item.type = card.link === 'dynamic' ? 'select' : 'text'
@@ -155,6 +158,8 @@
           } else if (value === 'number') {
             item.options = item.oriOptions.filter(op => op.value !== 'YYYY-MM-DD')
           }
+        } else if (item.key === 'url') {
+          item.required = value !== 'qrcode'
         }
 
         return item
@@ -356,22 +361,11 @@
           </Col>
         )
       } else if (item.type === 'file') {
-        let filelist = []
-        if (item.initVal) {
-          filelist = [{
-            uid: `1`,
-            name: item.initVal.slice(item.initVal.lastIndexOf('/') + 1),
-            status: 'done',
-            url: item.initVal,
-            origin: true
-          }]
-        }
-
         fields.push(
           <Col span={12} key={index}>
             <Form.Item label={item.label}>
               {getFieldDecorator(item.key, {
-                initialValue: filelist,
+                initialValue: item.initVal,
                 rules: [
                   {
                     required: !!item.required,
@@ -379,7 +373,7 @@
                   }
                 ]
               })(
-                <FileUpload maxFile={item.maxfile} fileType={'text'} />
+                <SourceComponent type={this.state.eleType} />
               )}
             </Form.Item>
           </Col>
@@ -397,28 +391,14 @@
           values.uuid = this.props.card.uuid
           values.marks = this.props.card.marks || null
 
-          if (values.url) {
-            if (values.url.length > 0) {
-              if (values.url[0].origin && values.url[0].url) {
-                values.url = values.url[0].url
-              } else if (!values.url[0].origin && values.url[0].status === 'done' && values.url[0].response) {
-                values.url = values.url[0].response
-              } else {
-                values.url = ''
-              }
-            } else {
-              values.url = ''
-            }
-          }
-
-          if (values.eleType === 'picture' && values.datatype === 'static' && !values.url) {
-            notification.warning({
-              top: 92,
-              message: '灏氭湭娣诲姞鍥剧墖鎴栧浘鐗囦笂浼犲け璐ワ紝璇烽噸鏂版坊鍔狅紒',
-              duration: 5
-            })
-            return
-          }
+          // if (values.eleType === 'picture' && values.datatype === 'static' && !values.url) {
+          //   notification.warning({
+          //     top: 92,
+          //     message: '灏氭湭娣诲姞鍥剧墖鎴栧浘鐗囦笂浼犲け璐ワ紝璇烽噸鏂版坊鍔狅紒',
+          //     duration: 5
+          //   })
+          //   return
+          // }
 
           resolve(values)
         } else {
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.scss b/src/menu/components/card/cardcellcomponent/elementform/index.scss
index 166f28c..58fef06 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.scss
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.scss
@@ -21,4 +21,7 @@
       }
     }
   }
+  .ant-form-explain, .ant-form-extra {
+    font-size: 13px;
+  }
 }
\ No newline at end of file
diff --git a/src/menu/components/card/cardcellcomponent/formconfig.jsx b/src/menu/components/card/cardcellcomponent/formconfig.jsx
index 39b0ab3..c103921 100644
--- a/src/menu/components/card/cardcellcomponent/formconfig.jsx
+++ b/src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -109,7 +109,7 @@
       label: '鍥剧墖/鏂囦欢',
       initVal: card.url || '',
       maxfile: 1,
-      required: false
+      required: true
     },
     {
       type: 'radio',
@@ -124,6 +124,17 @@
     },
     {
       type: 'radio',
+      key: 'loop',
+      label: '寰幆鎾斁',
+      initVal: card.loop || 'false',
+      required: false,
+      options: [
+        { value: 'true', text: '鏄�' },
+        { value: 'false', text: '鍚�' }
+      ]
+    },
+    {
+      type: 'radio',
       key: 'link',
       label: '閾炬帴',
       initVal: card.link || '',
diff --git a/src/menu/components/card/cardcellcomponent/index.jsx b/src/menu/components/card/cardcellcomponent/index.jsx
index 1eb0989..24433e0 100644
--- a/src/menu/components/card/cardcellcomponent/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/index.jsx
@@ -292,7 +292,6 @@
     let ableField = usefulFields.join(', ')
     let functip = <div>
       <p style={{marginBottom: '5px'}}>{this.state.dict['model.tooltip.func.innerface'].replace('@ableField', ableField)}</p>
-      <p>{this.state.dict['model.tooltip.func.outface']}</p>
     </div>
 
     let menulist = sessionStorage.getItem('fstMenuList')
diff --git a/src/menu/components/code/sandbox/codecontent/index.jsx b/src/menu/components/code/sandbox/codecontent/index.jsx
new file mode 100644
index 0000000..c178772
--- /dev/null
+++ b/src/menu/components/code/sandbox/codecontent/index.jsx
@@ -0,0 +1,88 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Empty, message } from 'antd'
+
+import './index.scss'
+
+class BraftContent extends Component {
+  static propTpyes = {
+    name: PropTypes.string,
+    html: PropTypes.any,
+    css: PropTypes.any,
+    js: PropTypes.any,
+  }
+
+  state = {
+    csselement: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { css } = this.props
+
+    if (css) {
+      // let style = css.replace(/^[^}{]*{|}[^}{]*{/ig, (word) => {
+      //   return word.replace(/}\n*/ig, `}\n#${mark}`).replace(/,/ig, `,#${mark} `)
+      // })
+      // style = `\n/* 鑷畾涔� */\n#${mark} ${style}\n`
+
+      let ele = document.createElement('style')
+      ele.innerHTML = css
+      document.getElementsByTagName('head')[0].appendChild(ele)
+      // document.getElementsByTagName('head')[0].prepend(ele)
+
+      this.setState({csselement: ele})
+    }
+  }
+
+  componentDidMount () {
+    const { js, name } = this.props
+    if (js) {
+      try {
+        // eslint-disable-next-line no-eval
+        eval(js)
+      } catch {
+        message.warning(name + 'JS 鎵ц澶辫触锛�')
+      }
+    }
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (this.props.css !== nextProps.css) {
+      const { csselement } = this.state
+      
+      if (csselement && csselement.remove) {
+        csselement.remove()
+      }
+      if (nextProps.css) {
+        let ele = document.createElement('style')
+        ele.innerHTML = nextProps.css
+        document.getElementsByTagName('head')[0].appendChild(ele)
+
+        this.setState({csselement: ele})
+      }
+    }
+    if (this.props.html !== nextProps.html || this.props.js !== nextProps.js) {
+      if (nextProps.js) {
+        try {
+          // eslint-disable-next-line no-eval
+          eval(nextProps.js)
+        } catch {
+          message.warning(nextProps.name + 'JS 鎵ц澶辫触锛�')
+        }
+      }
+    }
+  }
+
+  render() {
+    const { html } = this.props
+
+    if (!html) return <Empty style={{padding: '10px 0px'}} description={null}/>
+
+    return (
+      <div dangerouslySetInnerHTML={{ __html: html }}></div>
+    )
+  }
+}
+
+
+export default BraftContent
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/codecontent/index.scss b/src/menu/components/code/sandbox/codecontent/index.scss
new file mode 100644
index 0000000..f2f0d33
--- /dev/null
+++ b/src/menu/components/code/sandbox/codecontent/index.scss
@@ -0,0 +1,26 @@
+.braft-content {
+  .media-wrap {
+    max-width: 100%;
+  }
+  img {
+    max-width: 100%;
+  }
+  video {
+    max-width: 100%;
+    width: 100%;
+  }
+  table {
+    width: 100%;
+    border-collapse: collapse;
+    border-spacing: 0;
+    margin: 10px 0px;
+    tr:first-child {
+      background-color: #f0f0f0;
+    }
+    td, th {
+      padding: 5px 14px;
+      font-size: 16px;
+      border: 1px solid #ddd;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/editorcode/index.jsx b/src/menu/components/code/sandbox/editorcode/index.jsx
new file mode 100644
index 0000000..ff65601
--- /dev/null
+++ b/src/menu/components/code/sandbox/editorcode/index.jsx
@@ -0,0 +1,110 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal, Tabs, message } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const CodeMirror = asyncComponent(() => import('@/templates/zshare/codemirror'))
+const { TabPane } = Tabs
+
+class DataSource extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    html: '',
+    css: '',
+    js: ''
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  trigger = () => {
+    const { config } = this.props
+
+    this.setState({
+      visible: true,
+      html: config.html || '',
+      css: config.css || '',
+      js: config.js || '',
+    })
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+    const { html, css, js } = this.state
+
+    this.setState({
+      visible: false
+    })
+    this.props.updateConfig({...config, html, css, js})
+  }
+
+  onHtmlChange = (val) => {
+    this.setState({
+      html: val
+    })
+  }
+
+  onCssChange = (val) => {
+    this.setState({
+      css: val
+    })
+  }
+
+  onJsChange = (val) => {
+    if (/document\.getElementsByTagName/g.test(val)) {
+      message.warning('涓洪槻姝唬鐮佸啿绐侊紝js涓笉鍙娇鐢╠ocument.getElementsByTagName鏂规硶!')
+      return
+    }
+
+    this.setState({
+      js: val
+    })
+  }
+
+  render () {
+    const { visible, dict, html, css, js } = this.state
+
+    return (
+      <div style={{display: 'inline-block'}}>
+        <Icon title="浠g爜缂栬緫" style={{color: 'purple'}} type="form" onClick={() => this.trigger()} />
+        <Modal
+          wrapClassName="popview-modal code-sand-box-code-editor"
+          title="鍐呭缂栬緫"
+          visible={visible}
+          width={950}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <Tabs>
+            <TabPane tab="HTML" key="HTML">
+              <CodeMirror mode="text/xml" theme="cobalt" value={html} onChange={this.onHtmlChange} />
+            </TabPane>
+            <TabPane tab="CSS" key="CSS">
+              <CodeMirror mode="text/css" theme="cobalt" value={css} onChange={this.onCssChange} />
+            </TabPane>
+            <TabPane tab="JS" key="JS">
+              <CodeMirror mode="text/javascript" theme="cobalt" value={js} onChange={this.onJsChange} />
+            </TabPane>
+          </Tabs>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/editorcode/index.scss b/src/menu/components/code/sandbox/editorcode/index.scss
new file mode 100644
index 0000000..2bd3d3d
--- /dev/null
+++ b/src/menu/components/code/sandbox/editorcode/index.scss
@@ -0,0 +1,20 @@
+.code-sand-box-code-editor.popview-modal {
+  .ant-modal-body {
+    padding-top: 0px;
+    .ant-tabs-bar {
+      margin: 0;
+    }
+    .ant-tabs-bar {
+      border: 0;
+    }
+
+    .code-mirror-wrap .code-mirror-area {
+      border-radius: 0;
+
+      .CodeMirror {
+        border-radius: 0;
+        height: 400px;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/index.jsx b/src/menu/components/code/sandbox/index.jsx
new file mode 100644
index 0000000..4b8097d
--- /dev/null
+++ b/src/menu/components/code/sandbox/index.jsx
@@ -0,0 +1,188 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { Icon, Popover } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+
+import MKEmitter from '@/utils/events.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+
+import './index.scss'
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const EditorCode = asyncIconComponent(() => import('./editorcode'))
+const CodeContent = asyncComponent(() => import('./codecontent'))
+
+class CodeSandBox extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    card: null,
+    back: false
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        floor: card.floor,
+        tabId: card.tabId || '',
+        parentId: card.parentId || '',
+        dataName: card.dataName || '',
+        format: 'object',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: false,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        setting: { interType: 'system' },
+        wrap: { name: card.name, width: card.width || 24, encryption: 'true' },
+        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
+        columns: [],
+        scripts: [],
+        html: '',
+        css: '',
+        js: '',
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.wrap = config.wrap
+        _card.wrap.name = card.name
+        _card.style = config.style
+        _card.html = config.html
+        _card.css = config.css
+        _card.js = config.js
+      }
+      
+      this.setState({
+        card: _card
+      })
+      this.props.updateConfig(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+  }
+
+  /**
+   * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
+   */
+  updateComponent = (component) => {
+    this.setState({
+      card: component
+    })
+
+    component.width = component.wrap.width
+    component.name = component.wrap.name
+
+    this.props.updateConfig(component)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+  }
+
+  getStyle = (comIds, style) => {
+    const { card } = this.state
+
+    if (comIds[0] !== card.uuid || comIds.length !== 1) return
+
+    let _card = {...card, style}
+
+    this.setState({
+      card: _card
+    })
+    
+    this.props.updateConfig(_card)
+  }
+
+  /**
+   * @description 鏇存柊鎼滅储鏉′欢閰嶇疆淇℃伅
+   */
+  updateconfig = (config) => {
+    this.setState({
+      card: config
+    })
+    this.props.updateConfig(config)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card)
+    }
+  }
+
+  render() {
+    const { card } = this.state
+    return (
+      <div className="menu-editor-sand-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <CopyComponent type="normaltable" card={card}/>
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <UserComponent config={card}/>
+            <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
+            <EditorCode config={card} updateConfig={this.updateComponent}/>
+            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
+            {card.wrap.datatype === 'static' ? <Icon style={{color: '#eeeeee', cursor: 'not-allowed'}} type="setting"/> : null}
+          </div>
+        } trigger="hover">
+          <Icon type="tool" />
+        </Popover>
+        <CodeContent name={card.name} html={card.html} css={card.css} js={card.js}/>
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    menu: state.customMenu
+  }
+}
+
+const mapDispatchToProps = () => {
+  return {}
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(CodeSandBox)
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/index.scss b/src/menu/components/code/sandbox/index.scss
new file mode 100644
index 0000000..c096ca8
--- /dev/null
+++ b/src/menu/components/code/sandbox/index.scss
@@ -0,0 +1,36 @@
+.menu-editor-sand-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 30px;
+
+  .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);
+  }
+  .empty-content {
+    text-align: center;
+    font-size: 30px;
+    margin: 0;
+    line-height: 90px;
+    color: #bcbcbc;
+  }
+}
+.menu-editor-sand-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-editor-sand-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/code/sandbox/wrapsetting/index.jsx b/src/menu/components/code/sandbox/wrapsetting/index.jsx
new file mode 100644
index 0000000..c949c28
--- /dev/null
+++ b/src/menu/components/code/sandbox/wrapsetting/index.jsx
@@ -0,0 +1,83 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import SettingForm from './settingform'
+import './index.scss'
+
+class DataSource extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    wrap: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    this.setState({wrap: fromJS(config.wrap).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  editDataSource = () => {
+    this.setState({
+      visible: true
+    })
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+
+    this.verifyRef.handleConfirm().then(res => {
+
+      this.setState({
+        wrap: res,
+        visible: false
+      })
+      this.props.updateConfig({...config, wrap: res})
+    })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict, wrap } = this.state
+
+    return (
+      <div className="model-menu-setting-wrap">
+        <Icon type="edit" onClick={() => this.editDataSource()} />
+        <Modal
+          wrapClassName="popview-modal"
+          title="瀵屾枃鏈缃�"
+          visible={visible}
+          width={700}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <SettingForm
+            dict={dict}
+            wrap={wrap}
+            config={config}
+            inputSubmit={this.verifySubmit}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/index.scss b/src/menu/components/code/sandbox/wrapsetting/index.scss
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/menu/components/code/sandbox/wrapsetting/index.scss
@@ -0,0 +1,7 @@
+.model-menu-setting-wrap {
+  display: inline-block;
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx b/src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx
new file mode 100644
index 0000000..76806c6
--- /dev/null
+++ b/src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx
@@ -0,0 +1,152 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
+
+import './index.scss'
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    roleList: [],
+  }
+
+  UNSAFE_componentWillMount () {
+    let roleList = sessionStorage.getItem('sysRoles')
+    if (roleList) {
+      try {
+        roleList = JSON.parse(roleList)
+      } catch {
+        roleList = []
+      }
+    } else {
+      roleList = []
+    }
+
+    this.setState({roleList})
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  render() {
+    const { wrap } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { roleList } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="model-menu-setting-form">
+        <Form {...formItemLayout}>
+          <Row gutter={24}>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
+                  <Icon type="question-circle" />
+                  缁勪欢鍚嶇О
+                </Tooltip>
+              }>
+                {getFieldDecorator('name', {
+                  initialValue: wrap.name,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
+                    }
+                  ]
+                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
+                  <Icon type="question-circle" />
+                  瀹藉害
+                </Tooltip>
+              }>
+                {getFieldDecorator('width', {
+                  initialValue: wrap.width || 24,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '瀹藉害!'
+                    }
+                  ]
+                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
+                  <Icon type="question-circle" />
+                  鏁版嵁鏉ユ簮
+                </Tooltip>
+              }>
+                {getFieldDecorator('datatype', {
+                  initialValue: wrap.datatype || 'dynamic'
+                })(
+                  <Radio.Group>
+                    <Radio value="dynamic">鍔ㄦ��</Radio>
+                    <Radio value="static">闈欐��</Radio>
+                  </Radio.Group>
+                )}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="榛戝悕鍗�">
+                {getFieldDecorator('blacklist', {
+                  initialValue: wrap.blacklist || []
+                })(
+                  <Select
+                    showSearch
+                    mode="multiple"
+                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  >
+                    {roleList.map(option =>
+                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/settingform/index.scss b/src/menu/components/code/sandbox/wrapsetting/settingform/index.scss
new file mode 100644
index 0000000..c530b18
--- /dev/null
+++ b/src/menu/components/code/sandbox/wrapsetting/settingform/index.scss
@@ -0,0 +1,15 @@
+.model-menu-setting-form {
+  position: relative;
+
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+  .color-sketch-block {
+    position: relative;
+    top: 7px;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/editorcontent/index.jsx b/src/menu/components/editor/braft-editor/editorcontent/index.jsx
new file mode 100644
index 0000000..fd1f8a4
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/editorcontent/index.jsx
@@ -0,0 +1,82 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const Editor = asyncComponent(() => import('@/components/editor'))
+
+class EditorContentComponent extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    html: null
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  trigger = () => {
+    const { config } = this.props
+
+    this.setState({
+      visible: true,
+      html: config.html || null
+    })
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+    const { html } = this.state
+
+    this.setState({
+      visible: false
+    })
+    this.props.updateConfig({...config, html})
+  }
+
+  onChange = (val) => {
+    this.setState({
+      html: val
+    })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict, html } = this.state
+
+    if (!config) return null
+
+    return (
+      <div className="model-menu-edit-content-wrap">
+        {config.wrap.datatype === 'static' ? <Icon title="鍐呭缂栬緫" type="form" onClick={() => this.trigger()} /> : null}
+        {config.wrap.datatype !== 'static' ? <Icon title="鍐呭缂栬緫" style={{color: '#eeeeee', cursor: 'not-allowed'}} type="form"/> : null}
+        <Modal
+          wrapClassName="popview-modal model-menu-edit-content-form"
+          title="鍐呭缂栬緫"
+          visible={visible}
+          width={950}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <Editor defaultValue={html} onChange={this.onChange} />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default EditorContentComponent
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/editorcontent/index.scss b/src/menu/components/editor/braft-editor/editorcontent/index.scss
new file mode 100644
index 0000000..dc27e8c
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/editorcontent/index.scss
@@ -0,0 +1,14 @@
+.model-menu-edit-content-wrap {
+  display: inline-block;
+
+  >.anticon-form {
+    color: purple;
+  }
+}
+.model-menu-edit-content-form {
+  .normal-braft-editor {
+    border: 1px solid #d9d9d9;
+    border-radius: 4px;
+    overflow-x: hidden;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/index.jsx b/src/menu/components/editor/braft-editor/index.jsx
new file mode 100644
index 0000000..cac96be
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/index.jsx
@@ -0,0 +1,196 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { Icon, Popover } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+
+import MKEmitter from '@/utils/events.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+
+import './index.scss'
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const EditorContent = asyncIconComponent(() => import('./editorcontent'))
+const BraftContent = asyncComponent(() => import('@/tabviews/custom/components/share/braftContent'))
+
+class BraftEditorComponent extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    card: null,
+    back: false
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        floor: card.floor,
+        tabId: card.tabId || '',
+        parentId: card.parentId || '',
+        dataName: card.dataName || '',
+        format: 'object',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: false,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        setting: { interType: 'system' },
+        wrap: { name: card.name, width: card.width || 24, encryption: 'true' },
+        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
+        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
+        columns: [],
+        scripts: [],
+        html: ''
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.wrap = config.wrap
+        _card.wrap.name = card.name
+        _card.style = config.style
+        _card.headerStyle = config.headerStyle
+        _card.html = config.html
+      }
+      
+      this.setState({
+        card: _card
+      })
+      this.props.updateConfig(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+  }
+
+  /**
+   * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
+   */
+  updateComponent = (component) => {
+    this.setState({
+      card: component
+    })
+
+    component.width = component.wrap.width
+    component.name = component.wrap.name
+
+    this.props.updateConfig(component)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+  }
+
+  getStyle = (comIds, style) => {
+    const { card } = this.state
+
+    if (comIds[0] !== card.uuid) return
+
+    let _card = {}
+    if (comIds.length === 1) {
+      _card = {...card, style}
+    } else {
+      return
+    }
+
+    this.setState({
+      card: _card
+    })
+    
+    this.props.updateConfig(_card)
+  }
+
+  /**
+   * @description 鏇存柊鎼滅储鏉′欢閰嶇疆淇℃伅
+   */
+  updateconfig = (config) => {
+    this.setState({
+      card: config
+    })
+    this.props.updateConfig(config)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card)
+    }
+  }
+
+  render() {
+    const { card } = this.state
+    return (
+      <div className="menu-normal-editor-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
+        <NormalHeader defaultshow="hidden" hideSearch="true" config={card} updateComponent={this.updateComponent}/>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <CopyComponent type="normaltable" card={card}/>
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <UserComponent config={card}/>
+            <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
+            <EditorContent config={card} updateConfig={this.updateComponent}/>
+            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
+            {card.wrap.datatype === 'static' ? <Icon style={{color: '#eeeeee', cursor: 'not-allowed'}} type="setting"/> : null}
+          </div>
+        } trigger="hover">
+          <Icon type="tool" />
+        </Popover>
+        <BraftContent
+          value={card.wrap.datatype !== 'static' ? '<p class="empty-content">瀵屾枃鏈�</p>' : card.html}
+          encryption="false"
+        />
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    menu: state.customMenu
+  }
+}
+
+const mapDispatchToProps = () => {
+  return {}
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(BraftEditorComponent)
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/index.scss b/src/menu/components/editor/braft-editor/index.scss
new file mode 100644
index 0000000..8830797
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/index.scss
@@ -0,0 +1,36 @@
+.menu-normal-editor-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 100px;
+
+  .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);
+  }
+  .empty-content {
+    text-align: center;
+    font-size: 30px;
+    margin: 0;
+    line-height: 90px;
+    color: #bcbcbc;
+  }
+}
+.menu-normal-editor-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-normal-editor-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/index.jsx b/src/menu/components/editor/braft-editor/wrapsetting/index.jsx
new file mode 100644
index 0000000..c949c28
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/wrapsetting/index.jsx
@@ -0,0 +1,83 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import SettingForm from './settingform'
+import './index.scss'
+
+class DataSource extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    wrap: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    this.setState({wrap: fromJS(config.wrap).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  editDataSource = () => {
+    this.setState({
+      visible: true
+    })
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+
+    this.verifyRef.handleConfirm().then(res => {
+
+      this.setState({
+        wrap: res,
+        visible: false
+      })
+      this.props.updateConfig({...config, wrap: res})
+    })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict, wrap } = this.state
+
+    return (
+      <div className="model-menu-setting-wrap">
+        <Icon type="edit" onClick={() => this.editDataSource()} />
+        <Modal
+          wrapClassName="popview-modal"
+          title="瀵屾枃鏈缃�"
+          visible={visible}
+          width={700}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <SettingForm
+            dict={dict}
+            wrap={wrap}
+            config={config}
+            inputSubmit={this.verifySubmit}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/index.scss b/src/menu/components/editor/braft-editor/wrapsetting/index.scss
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/wrapsetting/index.scss
@@ -0,0 +1,7 @@
+.model-menu-setting-wrap {
+  display: inline-block;
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx b/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx
new file mode 100644
index 0000000..b93fffd
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx
@@ -0,0 +1,199 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
+
+import './index.scss'
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    roleList: [],
+    datatype: this.props.wrap.datatype || 'dynamic'
+  }
+
+  UNSAFE_componentWillMount () {
+    let roleList = sessionStorage.getItem('sysRoles')
+    if (roleList) {
+      try {
+        roleList = JSON.parse(roleList)
+      } catch {
+        roleList = []
+      }
+    } else {
+      roleList = []
+    }
+
+    this.setState({roleList})
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  changeDataType = (e) => {
+    this.setState({datatype: e.target.value})
+  }
+
+  render() {
+    const { wrap, config } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { roleList, datatype } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="model-menu-setting-form">
+        <Form {...formItemLayout}>
+          <Row gutter={24}>
+            <Col span={12}>
+              <Form.Item label="鏍囬">
+                {getFieldDecorator('title', {
+                  initialValue: wrap.title || ''
+                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
+                  <Icon type="question-circle" />
+                  缁勪欢鍚嶇О
+                </Tooltip>
+              }>
+                {getFieldDecorator('name', {
+                  initialValue: wrap.name,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
+                    }
+                  ]
+                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
+                  <Icon type="question-circle" />
+                  瀹藉害
+                </Tooltip>
+              }>
+                {getFieldDecorator('width', {
+                  initialValue: wrap.width || 24,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '瀹藉害!'
+                    }
+                  ]
+                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
+                  <Icon type="question-circle" />
+                  鏁版嵁鏉ユ簮
+                </Tooltip>
+              }>
+                {getFieldDecorator('datatype', {
+                  initialValue: datatype
+                })(
+                  <Radio.Group onChange={this.changeDataType}>
+                    <Radio value="dynamic">鍔ㄦ��</Radio>
+                    <Radio value="static">闈欐��</Radio>
+                  </Radio.Group>
+                )}
+              </Form.Item>
+            </Col>
+            {datatype === 'dynamic' ? <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="閫夋嫨鍔ㄦ�佸�兼椂锛岄渶璁剧疆鏂囨湰瀛楁鎵嶅彲鐢熸晥銆�">
+                  <Icon type="question-circle" />
+                  鏂囨湰瀛楁
+                </Tooltip>
+              }>
+                {getFieldDecorator('field', {
+                  initialValue: wrap.field || ''
+                })(
+                  <Select>
+                    {config.columns.map(option =>
+                      <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col> : null}
+            {datatype === 'dynamic' ? <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="浠庢暟鎹簮鑾峰彇鐨勬暟鎹槸鍚﹂渶瑕佽В鐮併��">
+                  <Icon type="question-circle" />
+                  鏁版嵁瑙g爜
+                </Tooltip>
+              }>
+                {getFieldDecorator('encryption', {
+                  initialValue: wrap.encryption || 'true'
+                })(
+                  <Radio.Group>
+                    <Radio value="true">鏄�</Radio>
+                    <Radio value="false">鍚�</Radio>
+                  </Radio.Group>
+                )}
+              </Form.Item>
+            </Col> : null}
+            <Col span={12}>
+              <Form.Item label="榛戝悕鍗�">
+                {getFieldDecorator('blacklist', {
+                  initialValue: wrap.blacklist || []
+                })(
+                  <Select
+                    showSearch
+                    mode="multiple"
+                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  >
+                    {roleList.map(option =>
+                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss b/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss
new file mode 100644
index 0000000..c530b18
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss
@@ -0,0 +1,15 @@
+.model-menu-setting-form {
+  position: relative;
+
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+  .color-sketch-block {
+    position: relative;
+    top: 7px;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/group/groupcomponents/card.jsx b/src/menu/components/group/groupcomponents/card.jsx
index ced290d..99d3e95 100644
--- a/src/menu/components/group/groupcomponents/card.jsx
+++ b/src/menu/components/group/groupcomponents/card.jsx
@@ -10,6 +10,7 @@
 const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
+const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -57,6 +58,8 @@
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'editor') {
+      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
 
diff --git a/src/menu/components/group/groupcomponents/index.jsx b/src/menu/components/group/groupcomponents/index.jsx
index f3de3c9..7a8719e 100644
--- a/src/menu/components/group/groupcomponents/index.jsx
+++ b/src/menu/components/group/groupcomponents/index.jsx
@@ -103,6 +103,7 @@
         line: '鎶樼嚎鍥�',
         pie: '楗煎浘',
         table: '琛ㄦ牸',
+        editor: '瀵屾枃鏈�',
         card: '鍗$墖'
       }
       let i = 1
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index 1240c61..e584aea 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -34,6 +34,7 @@
     openType: null,  // 鎵撳紑鏂瑰紡
     interType: null, // 鎺ュ彛绫诲瀷锛氬唴閮ㄣ�佸閮�
     funcType: null,  // 鍔熻兘绫诲瀷
+    procMode: null,  // 鍙傛暟鏂瑰紡
     requireOptions: [{
       value: 'notRequired',
       text: this.props.dict['header.form.notRequired']
@@ -66,6 +67,19 @@
     }, {
       value: 'custom',
       text: this.props.dict['header.form.custom']
+    }],
+    interTypeOptions: [{
+      value: 'system',
+      text: this.props.dict['model.interface.system']
+    }, {
+      value: 'inner',
+      text: this.props.dict['model.interface.inner']
+    }, {
+      value: 'outer',
+      text: this.props.dict['model.interface.outer']
+    }, {
+      value: 'custom',
+      text: '鑷畾涔�'
     }]
   }
 
@@ -76,16 +90,23 @@
     let _opentype = card.OpenType                // 鎵撳紑鏂瑰紡
     let _intertype = card.intertype || 'system'  // 鎺ュ彛绫诲瀷
     let _funcType = card.funcType || 'print'     // 鍔熻兘鎸夐挳榛樿绫诲瀷
+    let _procMode = card.procMode || 'system'     // 鍙傛暟璇锋眰鏂瑰紡
 
-    let _options = this.getOptions(_opentype, _intertype, _funcType, card.pageTemplate)
+    let _options = this.getOptions(_opentype, _intertype, _funcType, card.pageTemplate, _procMode)
 
     this.setState({
       openType: _opentype,
       interType: _intertype,
+      procMode: _procMode,
       funcType: _funcType,
       formlist: this.props.formlist.map(item => {
         if (item.key === 'class') {
           item.options = btnCustomClasses
+        } else if (item.key === 'innerFunc' && _procMode === 'inner') {
+          item.required = true
+        } else if (item.key === 'intertype') {
+          let iscustom = ['pop', 'prompt', 'exec'].includes(_opentype)
+          item.options = this.state.interTypeOptions.filter(op => (iscustom || op.value !== 'custom'))
         } else if (item.key === 'icon') {
           item.options = btnIcons
         } else if (item.key === 'Ot') {
@@ -116,7 +137,7 @@
     })
   }
 
-  getOptions = (_opentype, _intertype, _funcType, _pageTemplate) => {
+  getOptions = (_opentype, _intertype, _funcType, _pageTemplate, _procMode) => {
     let _options = fromJS(actionTypeOptions[_opentype]).toJS() // 閫夐」鍒楄〃
     
     if (_opentype === 'innerpage') {         // 鏂伴〉闈紝鍙�夋ā鏉�(鑷畾涔夋椂锛屽彲濉叆澶栭儴閾炬帴)
@@ -125,13 +146,13 @@
       }
     } else if (_opentype === 'excelOut') {    // 瀵煎叆瀵煎嚭
       if (_intertype === 'outer') {
-        _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc')
+        _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc')
       } else if (_intertype === 'inner') {
         _options.push('innerFunc')
       }
     } else if (_opentype === 'excelIn') {    // 瀵煎叆瀵煎嚭
       if (_intertype === 'outer') {
-        _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+        _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
       } else if (_intertype === 'inner') {
         _options.push('innerFunc')
       }
@@ -139,14 +160,21 @@
       if (_funcType === 'print') {
         _options.push('execMode', 'intertype', 'Ot', 'execSuccess', 'execError', 'resetPageIndex')
         if (_intertype === 'outer') {
-          _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+          _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
         } else if (_intertype === 'inner') {
           _options.push('innerFunc')
         }
       }
     } else if (_opentype !== 'popview' && _opentype !== 'tab') {
-      if (_intertype === 'outer') {
-        _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+      if (_intertype === 'custom') {
+        _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method')
+        if (_procMode === 'system') {
+          _options.push('sql', 'sqlType')
+        } else {
+          _options.push('innerFunc')
+        }
+      } else if (_intertype === 'outer') {
+        _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
       } else if (_intertype === 'inner') {
         _options.push('innerFunc')
       } else {
@@ -176,21 +204,27 @@
    * 2銆佹樉绀轰綅缃垏鎹紝閲嶇疆閫夋嫨琛�
    * 3銆佸垏鎹㈡爣绛剧被鍨嬶紝閲嶇疆鍙�夋爣绛�
    */
-  openTypeChange = (key, value) => {
+  optionChange = (key, value) => {
     const { card, type } = this.props
+    const { openType, procMode } = this.state
 
     if (key === 'OpenType') {
-      let _options = this.getOptions(value, this.state.interType, this.state.funcType, card.pageTemplate)
+      let _options = this.getOptions(value, 'system', this.state.funcType, card.pageTemplate, 'system')
 
       let _fieldval = {}
       
       let _formlist = this.state.formlist.map(item => {
         item.hidden = !_options.includes(item.key)
 
+        if (item.key === 'intertype') {
+          let iscustom = ['pop', 'prompt', 'exec'].includes(value)
+          item.options = this.state.interTypeOptions.filter(op => (iscustom || op.value !== 'custom'))
+        }
+
         if (item.hidden) return item
 
         if (item.key === 'intertype') {
-          _fieldval.intertype = this.state.interType
+          _fieldval.intertype = 'system'
         } else if (item.key === 'Ot') {
           if (type === 'card') {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
@@ -217,6 +251,8 @@
 
       this.setState({
         openType: value,
+        intertype: 'system',
+        procMode: 'system',
         formlist: _formlist
       }, () => {
         if (value === 'excelIn') {
@@ -230,7 +266,7 @@
         this.props.form.setFieldsValue(_fieldval)
       })
     } else if (key === 'funcType') {
-      let _options = this.getOptions(this.state.openType, this.state.interType, value, card.pageTemplate)
+      let _options = this.getOptions(this.state.openType, this.state.interType, value, card.pageTemplate, procMode)
       let _fieldval = {}
 
       this.setState({
@@ -294,7 +330,7 @@
       })
     } else if (key === 'pageTemplate') {
       let _fieldval = {}
-      let _options = this.getOptions(this.state.openType, this.state.interType, this.state.funcType, value)
+      let _options = this.getOptions(this.state.openType, this.state.interType, this.state.funcType, value, procMode)
 
       this.setState({
         openType: value,
@@ -317,16 +353,8 @@
       }, () => {
         this.props.form.setFieldsValue(_fieldval)
       })
-    }
-  }
-
-  onChange = (e, key) => {
-    const { type } = this.props
-    const { openType } = this.state
-    let value = e.target.value
-
-    if (key === 'intertype') {
-      let _options = this.getOptions(openType, value, this.state.funcType)
+    } else if (key === 'intertype') {
+      let _options = this.getOptions(openType, value, this.state.funcType, '', procMode)
 
       this.setState({
         interType: value,
@@ -346,10 +374,20 @@
           }
           return item
         })
-      }, () => {
-        if (this.props.form.getFieldValue('sqlType') !== undefined) {
-          this.props.form.setFieldsValue({sqlType: ''})
-        }
+      })
+    } else if (key === 'procMode') {
+      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, '', value)
+
+      this.setState({
+        procMode: value,
+        formlist: this.state.formlist.map(item => {
+          item.hidden = !_options.includes(item.key)
+
+          if (item.key === 'innerFunc') {
+            item.required = true
+          }
+          return item
+        })
       })
     } else if (key === 'sysInterface') {
       if (value === 'true') {
@@ -475,7 +513,7 @@
                 <Select
                   showSearch
                   filterOption={(input, option) => option.props.children[2].toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  onChange={(value) => {this.openTypeChange(item.key, value)}}
+                  onChange={(value) => {this.optionChange(item.key, value)}}
                   getPopupContainer={() => document.getElementById('winter')}
                 >
                   {item.options.map((option, index) =>
@@ -501,7 +539,7 @@
                   }
                 ]
               })(
-                <Radio.Group onChange={(e) => {this.onChange(e, item.key)}} disabled={item.readonly}>
+                <Radio.Group onChange={(e) => {this.optionChange(item.key, e.target.value)}} disabled={item.readonly}>
                   {
                     item.options.map(option => {
                       return (
@@ -519,8 +557,14 @@
           <Col span={24} key={index}>
             <Form.Item label={item.label} className="textarea">
               {getFieldDecorator(item.key, {
-                initialValue: item.initVal
-              })(<TextArea rows={4} />)}
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={2} readOnly={item.readonly}/>)}
             </Form.Item>
           </Col>
         )
@@ -612,7 +656,7 @@
       }
     }
     return (
-      <Form {...formItemLayout} className="ant-advanced-search-form commontable-action-form" id="winter">
+      <Form {...formItemLayout} className="menu-action-list-form" id="winter">
         <Row gutter={24}>{this.getFields()}</Row>
       </Form>
     )
diff --git a/src/menu/components/share/actioncomponent/actionform/index.scss b/src/menu/components/share/actioncomponent/actionform/index.scss
index c25cef2..535dd29 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.scss
+++ b/src/menu/components/share/actioncomponent/actionform/index.scss
@@ -1,4 +1,4 @@
-.ant-advanced-search-form.commontable-action-form {
+.menu-action-list-form {
   min-height: 190px;
   .superconfig {
     color: #1890ff;
@@ -10,6 +10,12 @@
     }
     .ant-col-sm-17 {
       width: 86%;
+    }
+  }
+  .ant-radio-group {
+    white-space: nowrap;
+    .ant-radio-wrapper {
+      margin-right: 4px;
     }
   }
   .ant-input-number {
@@ -30,4 +36,12 @@
       top: 4.5px;
     }
   }
+  .ant-input:read-only {
+    background: #fafafa;
+    resize: none;
+  }
+  .ant-input:read-only:hover, .ant-input:read-only:focus {
+    border-color: #d9d9d9;
+    box-shadow: none;
+  }
 }
\ No newline at end of file
diff --git a/src/menu/components/share/actioncomponent/formconfig.jsx b/src/menu/components/share/actioncomponent/formconfig.jsx
index 0444ac7..ce7f6d2 100644
--- a/src/menu/components/share/actioncomponent/formconfig.jsx
+++ b/src/menu/components/share/actioncomponent/formconfig.jsx
@@ -66,7 +66,7 @@
       options: opentypes
     },
     {
-      type: 'select',
+      type: 'radio',
       key: 'funcType',
       label: Formdict['header.form.funcType'],
       initVal: card.funcType || 'print',
@@ -102,23 +102,6 @@
       label: Formdict['header.form.intertype'],
       initVal: card.intertype || 'system',
       required: true,
-      options: [{
-        value: 'system',
-        text: '绯荤粺'
-      }, {
-        value: 'inner',
-        text: Formdict['model.interface.inner']
-      }, {
-        value: 'outer',
-        text: Formdict['model.interface.outer']
-      }]
-    },
-    {
-      type: 'select',
-      key: 'sqlType',
-      label: Formdict['header.form.action.type'],
-      initVal: card.sqlType || '',
-      required: true,
       options: []
     },
     {
@@ -128,6 +111,28 @@
       initVal: card.label,
       required: true,
       readonly: false
+    },
+    {
+      type: 'radio',
+      key: 'procMode',
+      label: '鍙傛暟澶勭悊',
+      initVal: card.procMode || 'system',
+      required: true,
+      options: [{
+        value: 'system',
+        text: '绯荤粺鍑芥暟'
+      }, {
+        value: 'inner',
+        text: '鍐呴儴鍑芥暟'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'sqlType',
+      label: Formdict['header.form.action.type'],
+      initVal: card.sqlType || '',
+      required: true,
+      options: []
     },
     {
       type: 'text',
@@ -194,12 +199,56 @@
       readonly: false
     },
     {
-      type: 'text',
+      type: 'textarea',
       key: 'interface',
-      label: Formdict['header.form.interface'],
+      label: '娴嬭瘯鍦板潃',
       initVal: card.sysInterface === 'true' ? (window.GLOB.mainSystemApi || '') : (card.interface || ''),
       required: true,
       readonly: card.sysInterface === 'true'
+    },
+    {
+      type: 'textarea',
+      key: 'proInterface',
+      label: '姝e紡鍦板潃',
+      initVal: card.proInterface || '',
+      tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
+      required: false
+    },
+    {
+      type: 'radio',
+      key: 'method',
+      label: '璇锋眰鏂瑰紡',
+      initVal: card.method || 'post',
+      required: true,
+      options: [{
+        value: 'get',
+        text: 'GET'
+      }, {
+        value: 'post',
+        text: 'POST'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'callbackType',
+      label: '鍥炶皟鏂瑰紡',
+      initVal: card.callbackType || 'script',
+      tooltip: '浣跨敤榛樿鏂瑰紡鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
+      required: true,
+      options: [{
+        value: 'default',
+        text: '榛樿鑴氭湰'
+      }, {
+        value: 'script',
+        text: '鑷畾涔夎剼鏈�'
+      }]
+    },
+    {
+      type: 'text',
+      key: 'cbTable',
+      label: '鍥炶皟琛ㄥ悕',
+      initVal: card.cbTable || '',
+      required: true
     },
     {
       type: 'text',
@@ -308,7 +357,7 @@
       required: true
     },
     {
-      type: 'select',
+      type: 'radio',
       key: 'show',
       label: "鏄剧ず涓�",
       initVal: card.show || 'icon',
diff --git a/src/menu/components/share/actioncomponent/index.jsx b/src/menu/components/share/actioncomponent/index.jsx
index 3c62910..36e5f09 100644
--- a/src/menu/components/share/actioncomponent/index.jsx
+++ b/src/menu/components/share/actioncomponent/index.jsx
@@ -159,7 +159,6 @@
     let ableField = usefulFields.join(', ')
     let functip = <div>
       <p style={{marginBottom: '5px'}}>{this.state.dict['model.tooltip.func.innerface'].replace('@ableField', ableField)}</p>
-      <p>{this.state.dict['model.tooltip.func.outface']}</p>
     </div>
 
     let menulist = sessionStorage.getItem('fstMenuList')
@@ -466,7 +465,7 @@
         <Modal
           title={dict['model.action'] + '-' + (card && card.copyType === 'action' ? dict['model.copy'] : dict['model.edit'])}
           visible={visible}
-          width={800}
+          width={850}
           maskClosable={false}
           onCancel={this.editModalCancel}
           footer={[
diff --git a/src/menu/components/share/sourcecomponent/index.jsx b/src/menu/components/share/sourcecomponent/index.jsx
new file mode 100644
index 0000000..a5ed3e6
--- /dev/null
+++ b/src/menu/components/share/sourcecomponent/index.jsx
@@ -0,0 +1,81 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Radio, Icon, Modal } from 'antd'
+
+import InputForm from './inputform'
+import './index.scss'
+
+class CopyComponent extends Component {
+  static propTpyes = {
+    type: PropTypes.string,
+    placement: PropTypes.any,
+    onChange: PropTypes.func
+  }
+
+  state = {
+    url: this.props.value,
+    visible: ''
+  }
+
+  UNSAFE_componentWillMount () {
+
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  deleteUrl = () => {
+    this.setState({url: ''})
+    this.props.onChange('')
+  }
+
+  handleChange = (key) => {
+    this.setState({visible: key})
+  }
+
+  popSubmit = () => {
+    let url = ''
+    if (this.inputWrap && this.inputWrap.state.url) {
+      url = this.inputWrap.state.url
+    }
+
+    this.setState({visible: '', url})
+    this.props.onChange(url)
+  }
+
+  render () {
+    const { url, visible } = this.state
+    const { type } = this.props
+    let name = url ? url.slice(url.lastIndexOf('/') + 1) : ''
+
+    return (
+      <div className="mk-source-wrap">
+        {!url ? <Radio.Group>
+          <Radio.Button value="input" size="small" onClick={() => this.handleChange('input')}>杈撳叆</Radio.Button>
+          <Radio.Button value="upload" size="small" onClick={() => this.handleChange('upload')}>涓婁紶</Radio.Button>
+          <Radio.Button value="system" size="small" onClick={() => this.handleChange('system')}>绯荤粺</Radio.Button>
+        </Radio.Group> : null}
+        {url ? <div className="mk-source-item-info">
+          <Icon type="paper-clip" />
+          <a target="_blank" rel="noopener noreferrer" href={url}>{name}</a>
+          <Icon title="鍒犻櫎鏂囦欢" type="delete" onClick={this.deleteUrl}/>
+        </div> : null}
+        <Modal
+          visible={!!visible}
+          width={visible !== 'system' ? 600 : 1000}
+          closable={false}
+          maskClosable={false}
+          onOk={this.popSubmit}
+          onCancel={() => {this.setState({visible: ''})}}
+          destroyOnClose
+        >
+          <InputForm type={type === 'picture' ? 'image' : type} keyword={visible} ref={dom => { this.inputWrap = dom }} />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default CopyComponent
\ No newline at end of file
diff --git a/src/menu/components/share/sourcecomponent/index.scss b/src/menu/components/share/sourcecomponent/index.scss
new file mode 100644
index 0000000..f270254
--- /dev/null
+++ b/src/menu/components/share/sourcecomponent/index.scss
@@ -0,0 +1,42 @@
+.mk-source-wrap {
+  .mk-source-item-info {
+    position: relative;
+    line-height: 1.3;
+    top: 3px;
+    .anticon-paper-clip {
+      position: absolute;
+      top: 3px;
+      color: rgba(0,0,0,.45);
+      font-size: 14px;
+    }
+    .anticon-delete {
+      position: absolute;
+      top: 3px;
+      right: 0px;
+      padding-right: 6px;
+      color: rgba(0,0,0,.45);
+      cursor: pointer;
+      display: none;
+    }
+    a {
+      display: inline-block;
+      width: 100%;
+      padding-left: 22px;
+      padding-right: 14px;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+  }
+  .mk-source-item-info:hover {
+    background-color: #e6f7ff;
+    .anticon-delete {
+      display: inline-block;
+    }
+  }
+
+  .ant-radio-button-wrapper {
+    height: 28px;
+    line-height: 26px;
+  }
+}
diff --git a/src/menu/components/share/sourcecomponent/inputform/index.jsx b/src/menu/components/share/sourcecomponent/inputform/index.jsx
new file mode 100644
index 0000000..0312024
--- /dev/null
+++ b/src/menu/components/share/sourcecomponent/inputform/index.jsx
@@ -0,0 +1,212 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Input, Form, Row, Col, Pagination, Empty, Button, Modal, notification } from 'antd'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const { TextArea } = Input
+const { Search } = Input
+const FileUpload = asyncComponent(() => import('@/tabviews/zshare/fileupload'))
+const Video = asyncComponent(() => import('@/menu/picturecontroller/video'))
+const Image = asyncComponent(() => import('@/components/Image'))
+const EditForm = asyncComponent(() => import('@/menu/picturecontroller/editform'))
+
+class PopSource extends Component {
+  static propTpyes = {
+    btnlog: PropTypes.array,
+    keyword: PropTypes.string,
+    handlelog: PropTypes.func
+  }
+
+  state = {
+    url: '',
+    originlist: [],
+    list: [],
+    pagelist: [],
+    fileList: [],
+    searchKey: '',
+    pageSize: 12,
+    pageIndex: 1,
+    selectId: '',
+    editvisible: false,
+    card: null
+  }
+
+  UNSAFE_componentWillMount () {
+    if (this.props.keyword === 'system') {
+      this.init()
+    }
+  }
+
+  componentDidMount() {
+    try {
+      let _form = document.getElementById('source-input')
+
+      if (_form && _form.focus) {
+        _form.focus()
+      }
+    } catch {}
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  init = () => {
+    const { type } = this.props
+
+    let originlist = []
+    if (type === 'video') {
+      let videos = sessionStorage.getItem('app_videos')
+      try {
+        originlist = JSON.parse(videos)
+      } catch {
+        originlist = []
+      }
+    } else {
+      let pictures = sessionStorage.getItem('app_pictures')
+      try {
+        originlist = JSON.parse(pictures)
+      } catch {
+        originlist = []
+      }
+    }
+
+    let list = originlist
+    let pagelist = list.filter((item, index) => index < this.state.pageSize)
+
+    this.setState({originlist, list, url: '', searchKey: '', pageIndex: 1, fileList: [], pagelist})
+  }
+
+  changeSearch = () => {
+    const { originlist, pageSize, searchKey } = this.state
+    let list = originlist.filter(item => item.remark.indexOf(searchKey) > -1)
+    let pagelist = list.filter((item, index) => index < pageSize)
+
+    this.setState({list, pagelist, pageIndex: 1})
+  }
+
+  changeValue = (e) => {
+    this.setState({url: e.target.value})
+  }
+
+  changeSize = (page) => {
+    const { list, pageSize } = this.state
+    let pagelist = list.filter((item, index) => index < pageSize * page && index >= pageSize * (page - 1))
+    this.setState({pageIndex: page, pagelist})
+  }
+
+  changeFile = (vals) => {
+    this.setState({fileList: vals})
+
+    if (vals && vals[0] && vals[0].status === 'done' && vals[0].response) {
+      this.setState({url: vals[0].response})
+    }
+  }
+
+  selectItem = (item) => {
+    if (item.linkurl) {
+      this.setState({url: item.linkurl, selectId: item.id})
+    }
+  }
+
+  handleSource = (item) => {
+    this.setState({
+      editvisible: true,
+      card: item
+    })
+  }
+
+  save = () => {
+    const { card } = this.state
+    this.editFormRef.handleConfirm().then(res => {
+      res = {...card, ...res}
+
+      if (!res.id) {
+        res.id = Utils.getuuid()
+      }
+
+      Api.getSystemConfig({
+        func: 's_url_db_adduptdel',
+        id: res.id,
+        PageIndex: 0, // 0 浠h〃鍏ㄩ儴
+        PageSize: 0,  // 0 浠h〃鍏ㄩ儴
+        remark: res.remark || '',
+        linkurl: res.linkurl,
+        typecharone: card.typecharone,
+        type: 'add'
+      }).then(result => {
+        if (result.status) {
+          if (card.typecharone === 'image') {
+            sessionStorage.setItem('app_pictures', JSON.stringify(result.data || []))
+            this.init()
+          } else {
+            sessionStorage.setItem('app_videos', JSON.stringify(result.data || []))
+            this.init()
+          }
+          this.setState({editvisible: false})
+        } else {
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+        }
+      })
+    })
+  }
+
+  render () {
+    const { type, keyword } = this.props
+    const { list, url, pagelist, fileList, searchKey, pageIndex, pageSize, selectId, editvisible, card } = this.state
+    
+    return (
+      <div className="mk-source-pop-wrap">
+        {keyword === 'input' ? <Form.Item label="鍦板潃" labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={{xs: { span: 24 }, sm: { span: 20 }}}>
+          <TextArea id="source-input" value={url} rows={4} onChange={this.changeValue}/>
+        </Form.Item> : null}
+        {keyword === 'upload' ? <Form.Item label="涓婁紶" labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={{xs: { span: 24 }, sm: { span: 20 }}}>
+          <FileUpload value={fileList} onChange={this.changeFile} accept={type === 'video' ? '.mp4,.webm,.ogg' : '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp'} maxFile={1} fileType={type === 'video' ? 'text' : 'picture'} />
+        </Form.Item> : null}
+        {keyword === 'system' ?
+          <Search value={searchKey} placeholder="" onChange={(e) => this.setState({searchKey: e.target.value})} onSearch={this.changeSearch} enterButton/> : null}
+        {keyword === 'system' ? <Button className="picture-plus mk-green" icon="plus" onClick={() => this.handleSource({typecharone: type})}>
+          娣诲姞
+        </Button> : null}
+        {keyword === 'system' && list.length ?
+          <Row gutter={16} style={{minHeight: '250px'}}>
+            {pagelist.map(item => (
+              <Col span={4} key={item.id}>
+                <div className={'image-video-box' + (selectId === item.id ? ' active' : '')} onClick={() => this.selectItem(item)}>
+                  <div className="image-video-box-body">
+                    {type !== 'video' ? <Image url={item.linkurl} /> : null}
+                    {type === 'video' ? <Video value={item.linkurl} /> : null}
+                  </div>
+                </div>
+              </Col>
+            ))}
+          </Row> : null}
+        {keyword === 'system' && list.length === 0 ? <Empty description={null}/> : null}
+        {keyword === 'system' && list.length > pageSize ? <Pagination size="small" current={pageIndex} pageSize={pageSize} onChange={this.changeSize} total={list.length} /> : null}
+        <Modal
+          title={'鏂板缓'}
+          wrapClassName="picture-edit-model"
+          visible={editvisible}
+          width={600}
+          maskClosable={false}
+          onOk={this.save}
+          onCancel={() => {this.setState({editvisible: false})}}
+          destroyOnClose
+        >
+          <EditForm card={card} wrappedComponentRef={(inst) => this.editFormRef = inst} inputSubmit={this.save}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default PopSource
\ No newline at end of file
diff --git a/src/menu/components/share/sourcecomponent/inputform/index.scss b/src/menu/components/share/sourcecomponent/inputform/index.scss
new file mode 100644
index 0000000..576da5d
--- /dev/null
+++ b/src/menu/components/share/sourcecomponent/inputform/index.scss
@@ -0,0 +1,60 @@
+.mk-source-pop-wrap {
+  padding: 20px 10px 15px;
+  min-height: 150px;
+
+  .image-video-box {
+    position: relative;
+    padding-top: 75%;
+    cursor: pointer;
+    margin-bottom: 16px;
+    transition: all 0.3s;
+    .image-video-box-body {
+      position: absolute;
+      top: 0px;
+      left: 0px;
+      right: 0px;
+      bottom: 0px;
+      .video-react-big-play-button {
+        display: none;
+      }
+      .video-react-control-bar {
+        display: none;
+      }
+    }
+    .image-video-box-body::after {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+    }
+  }
+  .image-video-box.active {
+    box-shadow: 0px 0px 8px #1890ff;
+  }
+  .ant-input-search {
+    width: 300px;
+    position: relative;
+    top: -15px;
+  }
+  .ant-empty {
+    height: 250px;
+    padding-top: 50px;
+  }
+  .ant-pagination {
+    text-align: right;
+  }
+  .picture-plus {
+    float: right;
+    position: relative;
+    top: -15px;
+  }
+}
+.picture-edit-model {
+  .ant-modal {
+    .ant-modal-body {
+      min-height: 200px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/share/usercomponent/index.jsx b/src/menu/components/share/usercomponent/index.jsx
index fd071da..528006d 100644
--- a/src/menu/components/share/usercomponent/index.jsx
+++ b/src/menu/components/share/usercomponent/index.jsx
@@ -6,6 +6,7 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
+import options from '@/store/options.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import UserForm from './settingform'
@@ -53,6 +54,9 @@
     _config.search = config.search || []
     _config.cols = config.cols || []
     _config.plot = config.plot || {}
+    _config.html = config.html || ''
+    _config.css = config.css || ''
+    _config.js = config.js || ''
 
     _config.width = _config.wrap.width || _config.plot.width || config.width || 24
 
@@ -144,8 +148,17 @@
       setTimeout(() => {
         let template = this.getUserComponent()
         html2canvas(document.getElementById(config.uuid)).then(canvas => {
-          let img = canvas.toDataURL('image/png') // 鑾峰彇鐢熸垚鐨勫浘鐗�
-          Api.fileuploadbase64(img, 'cloud').then(result => {
+          let param = {
+            Base64Img: canvas.toDataURL('image/png') // 鑾峰彇鐢熸垚鐨勫浘鐗�
+          }
+
+          if (options.cloudServiceApi) {
+            param.rduri = options.cloudServiceApi
+            param.userid = sessionStorage.getItem('CloudUserID') || ''
+            param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
+          }
+
+          Api.fileuploadbase64(param).then(result => {
             if (result.status) {
               Api.getSystemConfig({
                 func: 's_custom_components_adduptdel',
@@ -199,7 +212,6 @@
       <div className="user-component-wrap">
         <Icon type="user" title="鐢熸垚鑷畾涔夌粍浠�" onClick={this.trigger} />
         <Modal
-          wrapClassName="popview-modal"
           title="鑷畾涔夌粍浠�"
           visible={visible}
           width={500}
diff --git a/src/menu/components/table/normal-table/columns/editColumn/index.jsx b/src/menu/components/table/normal-table/columns/editColumn/index.jsx
index 6725e03..4d355d9 100644
--- a/src/menu/components/table/normal-table/columns/editColumn/index.jsx
+++ b/src/menu/components/table/normal-table/columns/editColumn/index.jsx
@@ -9,7 +9,7 @@
 
 const columnTypeOptions = {
   text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'blacklist', 'perspective', 'rowspan'],
-  number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum'],
+  number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum', 'rowspan'],
   link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'blacklist', 'nameField'],
   textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'blacklist'],
   picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'blacklist', 'scale', 'lenWidRadio', 'picSort'],
diff --git a/src/menu/components/tabs/tabcomponents/card.jsx b/src/menu/components/tabs/tabcomponents/card.jsx
index 978f69a..416ca65 100644
--- a/src/menu/components/tabs/tabcomponents/card.jsx
+++ b/src/menu/components/tabs/tabcomponents/card.jsx
@@ -13,6 +13,7 @@
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -66,6 +67,8 @@
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'group' && card.subtype === 'normalgroup') {
       return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'editor') {
+      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
 
diff --git a/src/menu/components/tabs/tabcomponents/index.jsx b/src/menu/components/tabs/tabcomponents/index.jsx
index ece6a40..6bf8943 100644
--- a/src/menu/components/tabs/tabcomponents/index.jsx
+++ b/src/menu/components/tabs/tabcomponents/index.jsx
@@ -96,6 +96,7 @@
         search: '鎼滅储',
         table: '琛ㄦ牸',
         group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
         card: '鍗$墖'
       }
       let i = 1
diff --git a/src/menu/datasource/index.jsx b/src/menu/datasource/index.jsx
index 9244b6b..1869282 100644
--- a/src/menu/datasource/index.jsx
+++ b/src/menu/datasource/index.jsx
@@ -115,7 +115,7 @@
 
     return (
       <div className="model-datasource">
-        <Icon type="setting" onClick={() => this.editDataSource()} />
+        <Icon type="setting" title="鏁版嵁婧�" onClick={() => this.editDataSource()} />
         <Modal
           wrapClassName="popview-modal"
           title={'鏁版嵁婧愰厤缃�'}
diff --git a/src/menu/datasource/verifycard/index.jsx b/src/menu/datasource/verifycard/index.jsx
index 2d9c900..1febad6 100644
--- a/src/menu/datasource/verifycard/index.jsx
+++ b/src/menu/datasource/verifycard/index.jsx
@@ -229,7 +229,7 @@
         res.data.forEach(item => {
           let _item = {
             name: item.funcname,
-            value: Utils.UnformatOptions(item.longparam)
+            value: window.decodeURIComponent(window.atob(item.longparam))
           }
 
           _scripts.push(_item)
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index 3d365e8..1413357 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -13,6 +13,8 @@
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
+const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -66,6 +68,10 @@
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'group' && card.subtype === 'normalgroup') {
       return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'editor') {
+      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'code') {
+      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
   return (
diff --git a/src/menu/menushell/index.jsx b/src/menu/menushell/index.jsx
index ecbc6cf..cb51df3 100644
--- a/src/menu/menushell/index.jsx
+++ b/src/menu/menushell/index.jsx
@@ -89,6 +89,8 @@
         search: '鎼滅储',
         table: '琛ㄦ牸',
         group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
+        code: '鑷畾涔�',
         card: '鍗$墖'
       }
       let i = 1
diff --git a/src/menu/modelsource/dragsource/index.jsx b/src/menu/modulesource/dragsource/index.jsx
similarity index 100%
rename from src/menu/modelsource/dragsource/index.jsx
rename to src/menu/modulesource/dragsource/index.jsx
diff --git a/src/menu/modelsource/dragsource/index.scss b/src/menu/modulesource/dragsource/index.scss
similarity index 100%
rename from src/menu/modelsource/dragsource/index.scss
rename to src/menu/modulesource/dragsource/index.scss
diff --git a/src/menu/modelsource/index.jsx b/src/menu/modulesource/index.jsx
similarity index 100%
rename from src/menu/modelsource/index.jsx
rename to src/menu/modulesource/index.jsx
diff --git a/src/menu/modelsource/index.scss b/src/menu/modulesource/index.scss
similarity index 100%
rename from src/menu/modelsource/index.scss
rename to src/menu/modulesource/index.scss
diff --git a/src/menu/modelsource/option.jsx b/src/menu/modulesource/option.jsx
similarity index 87%
rename from src/menu/modelsource/option.jsx
rename to src/menu/modulesource/option.jsx
index 7a55036..9c0a333 100644
--- a/src/menu/modelsource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -9,6 +9,8 @@
 import TableCard from '@/assets/mobimg/table-card.png'
 import NormalTable from '@/assets/mobimg/normal-table.png'
 import Pie from '@/assets/mobimg/pie.png'
+import Editor from '@/assets/mobimg/editor.png'
+import SandBox from '@/assets/mobimg/sandbox.png'
 import Pie1 from '@/assets/mobimg/ring.png'
 import Pie2 from '@/assets/mobimg/nightingale.png'
 import Mainsearch from '@/assets/mobimg/mainsearch.png'
@@ -27,6 +29,8 @@
   { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '鏉″舰鍥�', width: 24 },
   { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '楗煎浘', width: 12 },
   { type: 'menu', url: Pie1, component: 'pie', subtype: 'ring', title: '鐜浘', width: 12 },
+  { type: 'menu', url: Editor, component: 'editor', subtype: 'brafteditor', title: '瀵屾枃鏈�', width: 24 },
+  { type: 'menu', url: SandBox, component: 'code', subtype: 'sandbox', title: '鑷畾涔�', width: 24 },
   { type: 'menu', url: Pie2, component: 'pie', subtype: 'nightingale', title: '鍗椾竵鏍煎皵鍥�', width: 12 },
   { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24, forbid: ['billPrint'] },
 ]
diff --git a/src/menu/pastecontroller/index.jsx b/src/menu/pastecontroller/index.jsx
index f8061f3..9df7a55 100644
--- a/src/menu/pastecontroller/index.jsx
+++ b/src/menu/pastecontroller/index.jsx
@@ -241,7 +241,7 @@
     return (
       <div style={{display: 'inline-block'}}>
         {type !== 'menu' ? <Icon type="snippets" style={{color: 'purple'}} onClick={() => {this.setState({visible: true})}} /> : null}
-        {type === 'menu' ? <Button type="link" style={{padding: '5px'}} icon="snippets" onClick={() => {this.setState({visible: true})}}>绮樿创</Button> : null}
+        {type === 'menu' ? <Button className="menu-config-paste" icon="snippets" onClick={() => {this.setState({visible: true})}}>绮樿创</Button> : null}
         <Modal
           title="绮樿创"
           visible={visible}
diff --git a/src/menu/pastecontroller/index.scss b/src/menu/pastecontroller/index.scss
index e69de29..384a820 100644
--- a/src/menu/pastecontroller/index.scss
+++ b/src/menu/pastecontroller/index.scss
@@ -0,0 +1,4 @@
+.menu-config-paste {
+  border-color: #40a9ff;
+  color: #40a9ff;
+}
\ No newline at end of file
diff --git a/src/menu/picturecontroller/editform/index.jsx b/src/menu/picturecontroller/editform/index.jsx
new file mode 100644
index 0000000..0d4db6b
--- /dev/null
+++ b/src/menu/picturecontroller/editform/index.jsx
@@ -0,0 +1,160 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, notification } from 'antd'
+
+import FileUpload from '@/tabviews/zshare/fileupload'
+import './index.scss'
+
+const { TextArea } = Input
+
+class MainSearch extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    inputSubmit: PropTypes.func // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    urls: [],
+    linkurl: '',
+    plusType: 'upload'
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          if (values.urls && values.urls[0].status === 'error') {
+            notification.warning({
+              top: 92,
+              message: '璇烽噸鏂颁笂浼犳枃浠讹紒',
+              duration: 5
+            })
+            return
+          } else if (values.urls && values.urls[0]) {
+            values.linkurl = values.urls[0].response
+          }
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  changeType = (val) => {
+    const { linkurl, urls } = this.state
+    let _urls = this.props.form.getFieldValue('urls') || ''
+    let _url = this.props.form.getFieldValue('linkurl') || ''
+
+    if (val === 'input') {
+      if (_urls && _urls[0] && _urls[0].status === 'done' && (_urls[0].url || _urls[0].response)) {
+        _url = _urls[0].url || _urls[0].response
+      } else {
+        _url = linkurl || ''
+      }
+    } else {
+      _urls = urls.filter(item => item.status === 'done')
+      _url = linkurl
+    }
+    
+    this.setState({plusType: val, urls: _urls, linkurl: _url})
+  }
+
+  render() {
+    const { getFieldDecorator } = this.props.form
+    const { card } = this.props
+    const { urls, linkurl, plusType } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 5 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 18 }
+      }
+    }
+    return (
+      <Form {...formItemLayout} className="picture-edit-model-form">
+        <Row gutter={24}>
+          {!card.id ? <Col span={24}>
+            <Form.Item label="娣诲姞鏂瑰紡">
+              <Radio.Group value={plusType} onChange={(e) => {this.changeType(e.target.value)}} disabled={false}>
+                <Radio value="upload">涓婁紶</Radio>
+                <Radio value="input">鍦板潃杈撳叆</Radio>
+              </Radio.Group>
+            </Form.Item>
+          </Col> : null}
+          {!card.id && card.typecharone === 'image' && plusType === 'upload' ? <Col span={24}>
+            <Form.Item label="鍥剧墖涓婁紶">
+              {getFieldDecorator('urls', {
+                initialValue: urls,
+                rules: [
+                  {
+                    required: true,
+                    message: '璇蜂笂浼犲浘鐗�!'
+                  }
+                ]
+              })(
+                <FileUpload accept=".jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp" maxFile={1} fileType={'picture'} />
+              )}
+            </Form.Item>
+          </Col> : null}
+          {!card.id && card.typecharone === 'video' && plusType === 'upload' ? <Col span={24}>
+            <Form.Item label="瑙嗛涓婁紶">
+              {getFieldDecorator('urls', {
+                initialValue: urls,
+                rules: [
+                  {
+                    required: true,
+                    message: '璇蜂笂浼犺棰�!'
+                  }
+                ]
+              })(
+                <FileUpload accept=".mp4,.webm,.ogg" maxFile={1} fileType={'text'} />
+              )}
+            </Form.Item>
+          </Col> : null}
+          {!card.id && plusType === 'input' ? <Col span={24}>
+            <Form.Item label="鍦板潃">
+              {getFieldDecorator('linkurl', {
+                initialValue: linkurl,
+                rules: [
+                  {
+                    required: true,
+                    message: '璇锋坊鍔犲湴鍧�淇℃伅!'
+                  },
+                  {
+                    max: 1024,
+                    message: '鍦板潃鏈�澶�1024涓瓧绗�!'
+                  }
+                ]
+              })(<TextArea autoSize={{ minRows: 3 }} onPressEnter={() => this.props.inputSubmit()}/>)}
+            </Form.Item>
+          </Col> : null}
+          {card.id ? <Col span={24}>
+            <Form.Item label="鍦板潃">
+              <TextArea value={card.linkurl} readOnly={true} autoSize={{ minRows: 3 }} />
+            </Form.Item>
+          </Col> : null}
+          <Col span={24}>
+            <Form.Item label="澶囨敞">
+              {getFieldDecorator('remark', {
+                initialValue: card.remark,
+                rules: [
+                  {
+                    max: 50,
+                    message: '澶囨敞鏈�澶�50涓瓧绗�!'
+                  }
+                ]
+              })(<TextArea autoSize={{ minRows: 4 }} onPressEnter={() => this.props.inputSubmit()}/>)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(MainSearch)
\ No newline at end of file
diff --git a/src/menu/picturecontroller/editform/index.scss b/src/menu/picturecontroller/editform/index.scss
new file mode 100644
index 0000000..0074b74
--- /dev/null
+++ b/src/menu/picturecontroller/editform/index.scss
@@ -0,0 +1,14 @@
+.picture-edit-model-form {
+  min-height: 150px;
+  .ant-input:read-only {
+    background: #fafafa;
+    resize: none;
+  }
+  .ant-input:read-only:hover, .ant-input:read-only:focus {
+    border-color: #d9d9d9;
+    box-shadow: none;
+  }
+  .ant-form-explain, .ant-form-extra {
+    font-size: 13px;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/picturecontroller/index.jsx b/src/menu/picturecontroller/index.jsx
new file mode 100644
index 0000000..7d887b7
--- /dev/null
+++ b/src/menu/picturecontroller/index.jsx
@@ -0,0 +1,320 @@
+import React, {Component} from 'react'
+import { Modal, Button, Row, Col, Input, Icon, message, Tabs, Empty, Pagination, notification } from 'antd'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const { Search } = Input
+const { confirm } = Modal
+const { TabPane } = Tabs
+
+const EditForm = asyncComponent(() => import('./editform'))
+const Video = asyncComponent(() => import('./video'))
+const Image = asyncComponent(() => import('@/components/Image'))
+
+class PasteController extends Component {
+  state = {
+    visible: false,
+    editvisible: false,
+    pictures: [],
+    imageKey: '',
+    videoKey: '',
+    videos: [],
+    card: null,
+    pageSize: 12,
+    filpictures: [],
+    filvideos: [],
+    piclist: [],
+    vidlist: [],
+    picIndex: 1,
+    vidIndex: 1,
+  }
+
+  trigger = () => {
+    let pictures = sessionStorage.getItem('app_pictures')
+    let videos = sessionStorage.getItem('app_videos')
+
+    try {
+      pictures = JSON.parse(pictures)
+      videos = JSON.parse(videos)
+    } catch {
+      pictures = []
+      videos = []
+    }
+
+    let filpictures = pictures
+    let filvideos = videos
+
+    let piclist = filpictures.filter((item, index) => index < this.state.pageSize)
+    let vidlist = filvideos.filter((item, index) => index < this.state.pageSize)
+
+    this.setState({
+      visible: true,
+      filpictures,
+      filvideos,
+      pictures,
+      picIndex: 1,
+      vidIndex: 1,
+      imageKey: '',
+      videoKey: '',
+      piclist,
+      vidlist,
+      videos
+    })
+  }
+  
+  handleSource = (item) => {
+    this.setState({
+      editvisible: true,
+      card: item || null
+    })
+  }
+
+  save = () => {
+    const { card } = this.state
+    this.editFormRef.handleConfirm().then(res => {
+      res = {...card, ...res}
+
+      if (!res.id) {
+        res.id = Utils.getuuid()
+      }
+
+      Api.getSystemConfig({
+        func: 's_url_db_adduptdel',
+        id: res.id,
+        PageIndex: 0, // 0 浠h〃鍏ㄩ儴
+        PageSize: 0,  // 0 浠h〃鍏ㄩ儴
+        remark: res.remark || '',
+        linkurl: res.linkurl,
+        typecharone: card.typecharone,
+        type: card.id ? 'upt' : 'add'
+      }).then(result => {
+        if (result.status) {
+          if (card.typecharone === 'image') {
+            sessionStorage.setItem('app_pictures', JSON.stringify(result.data || []))
+            this.resetPicture(result.data || [])
+          } else {
+            sessionStorage.setItem('app_videos', JSON.stringify(result.data || []))
+            this.resetVideo(result.data || [])
+          }
+          this.setState({editvisible: false})
+        } else {
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+        }
+      })
+    })
+  }
+
+  copySource = (item) => {
+    if (item.linkurl) {
+      let oInput = document.createElement('input')
+      oInput.value = item.linkurl
+      document.body.appendChild(oInput)
+      oInput.select()
+      document.execCommand('Copy')
+      document.body.removeChild(oInput)
+
+      message.success('澶嶅埗鎴愬姛銆�')
+    }
+  }
+
+  deleteSource = (item) => {
+    const _this = this
+
+    confirm({
+      title: '纭畾鍒犻櫎鍚楋紵',
+      content: '',
+      onOk() {
+        return new Promise((resolve) => {
+          Api.getSystemConfig({
+            func: 's_url_db_adduptdel',
+            id: item.id,
+            PageIndex: 0, // 0 浠h〃鍏ㄩ儴
+            PageSize: 0,  // 0 浠h〃鍏ㄩ儴
+            remark: '',
+            linkurl: '',
+            typecharone: item.typecharone,
+            type: 'del'
+          }).then(res => {
+            if (res.status) {
+              if (item.typecharone === 'image') {
+                sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
+                _this.resetPicture(res.data || [])
+              } else {
+                sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
+                _this.resetVideo(res.data || [])
+              }
+            } else {
+              notification.warning({
+                top: 92,
+                message: res.message,
+                duration: 5
+              })
+            }
+            resolve()
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  resetPicture = (data) => {
+    const { imageKey, pageSize } = this.state
+
+    let filpictures = data.filter(item => !imageKey || item.remark.indexOf(imageKey) > -1)
+    let piclist = filpictures.filter((item, index) => index < pageSize)
+
+    this.setState({picIndex: 1, filpictures, piclist, pictures: data})
+  }
+  
+  resetVideo = (data) => {
+    const { videoKey, pageSize } = this.state
+
+    let filvideos = data.filter(item => !videoKey || item.remark.indexOf(videoKey) > -1)
+    let vidlist = filvideos.filter((item, index) => index < pageSize)
+
+    this.setState({vidIndex: 1, filvideos, vidlist, videos: data})
+  }
+
+  filterPicture = () => {
+    const { imageKey, pictures, pageSize } = this.state
+
+    let filpictures = pictures.filter(item => !imageKey || item.remark.indexOf(imageKey) > -1)
+    let piclist = filpictures.filter((item, index) => index < pageSize)
+
+    this.setState({picIndex: 1, filpictures, piclist})
+  }
+  
+  filterVideo = () => {
+    const { videoKey, videos, pageSize } = this.state
+
+    let filvideos = videos.filter(item => !videoKey || item.remark.indexOf(videoKey) > -1)
+    let vidlist = filvideos.filter((item, index) => index < pageSize)
+
+    this.setState({vidIndex: 1, filvideos, vidlist})
+  }
+
+  changePicSize = (page) => {
+    const { filpictures, pageSize } = this.state
+    let piclist = filpictures.filter((item, index) => index < pageSize * page && index >= pageSize * (page - 1))
+
+    this.setState({picIndex: page, piclist})
+  }
+
+  changeVidSize = (page) => {
+    const { filvideos, pageSize } = this.state
+    let vidlist = filvideos.filter((item, index) => index < pageSize * page && index >= pageSize * (page - 1))
+    
+    this.setState({vidIndex: page, vidlist})
+  }
+  
+  render() {
+    const { visible, editvisible, card, filpictures, filvideos, piclist, vidlist, imageKey, videoKey, pageSize, picIndex, vidIndex } = this.state
+
+    return (
+      <div style={{display: 'inline-block'}}>
+        <Button className="mk-border-purple" icon="picture" onClick={this.trigger}>璧勬簮绠$悊</Button>
+        <Modal
+          title="绮樿创"
+          wrapClassName="picture-control-model"
+          visible={visible}
+          width={1200}
+          maskClosable={false}
+          onCancel={() => {this.setState({visible: false})}}
+          footer={[
+            <Button key="colse" onClick={() => {this.setState({visible: false})}}>
+              鍏抽棴
+            </Button>
+          ]}
+          destroyOnClose
+        >
+          <Tabs>
+            <TabPane tab="鍥剧墖绠$悊" key="picture">
+              <Row style={{marginBottom: '15px'}}>
+                <Col span={8}>
+                  <Search placeholder="" value={imageKey} onChange={(e) => this.setState({imageKey: e.target.value})} onSearch={this.filterPicture} enterButton />
+                </Col>
+                <Col span={16}>
+                  <Button className="picture-plus mk-green" icon="plus" onClick={() => this.handleSource({typecharone: 'image'})}>
+                    娣诲姞
+                  </Button>
+                </Col>
+              </Row>
+              <Row gutter={16} style={{height: '340px'}}>
+                {piclist.length && piclist.map(item => (
+                  <Col span={4} key={item.id}>
+                    <div className="image-video-box">
+                      <div className="image-video-box-body">
+                        <Image url={item.linkurl} />
+                      </div>
+                      <div className="image-video-control">
+                        <Icon type="copy" onClick={() => this.copySource(item)}/>
+                        <Icon type="edit" onClick={() => this.handleSource(item)}/>
+                        <Icon type="delete" onClick={() => this.deleteSource(item)}/>
+                      </div>
+                    </div>
+                    <p className="image-video-remark">{item.remark}</p>
+                  </Col>
+                ))}
+                {!piclist.length ? <Empty description={null}/> : null}
+              </Row>
+              {filpictures.length > pageSize ? <Pagination size="small" current={picIndex} pageSize={pageSize} onChange={this.changePicSize} total={filpictures.length} /> : null}
+            </TabPane>
+            <TabPane tab="瑙嗛绠$悊" key="video">
+              <Row style={{marginBottom: '15px'}}>
+                <Col span={8}>
+                  <Search placeholder="" value={videoKey} onChange={e => this.setState({videoKey: e.target.value})} onSearch={this.filterVideo} enterButton />
+                </Col>
+                <Col span={16}>
+                  <Button className="picture-plus mk-green" icon="plus" onClick={() => this.handleSource({typecharone: 'video'})}>
+                    娣诲姞
+                  </Button>
+                </Col>
+              </Row>
+              <Row gutter={16} style={{height: '340px'}}>
+                {vidlist.length && vidlist.map(item => (
+                  <Col span={4} key={item.id}>
+                    <div className="image-video-box">
+                      <div className="image-video-box-body">
+                        <Video value={item.linkurl} />
+                      </div>
+                      <div className="image-video-control">
+                        <Icon type="copy" onClick={() => this.copySource(item)}/>
+                        <Icon type="edit" onClick={() => this.handleSource(item)}/>
+                        <Icon type="delete" onClick={() => this.deleteSource(item)}/>
+                      </div>
+                    </div>
+                    <p className="image-video-remark">{item.remark}</p>
+                  </Col>
+                ))}
+                {!vidlist.length ? <Empty description={null}/> : null}
+              </Row>
+              {filvideos.length > pageSize ? <Pagination size="small" current={vidIndex} pageSize={pageSize} onChange={this.changeVidSize} total={filvideos.length} /> : null}
+            </TabPane>
+          </Tabs>
+        </Modal>
+        <Modal
+          title={card ? '缂栬緫' : '鏂板缓'}
+          wrapClassName="picture-edit-model"
+          visible={editvisible}
+          width={600}
+          maskClosable={false}
+          onOk={this.save}
+          onCancel={() => {this.setState({editvisible: false})}}
+          destroyOnClose
+        >
+          <EditForm card={card} wrappedComponentRef={(inst) => this.editFormRef = inst} inputSubmit={this.save}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default PasteController
\ No newline at end of file
diff --git a/src/menu/picturecontroller/index.scss b/src/menu/picturecontroller/index.scss
new file mode 100644
index 0000000..35b9200
--- /dev/null
+++ b/src/menu/picturecontroller/index.scss
@@ -0,0 +1,88 @@
+.picture-control-model {
+  .ant-modal {
+    top: 60px;
+    .ant-modal-body {
+      max-height: calc(100vh - 120px);
+      min-height: 510px;
+      padding-top: 5px;
+    }
+  }
+  .ant-tabs-tabpane {
+    padding-left: 3px;
+    padding-right: 3px;
+  }
+  .picture-plus {
+    float: right;
+    font-size: 16px;
+  }
+  .image-video-box {
+    position: relative;
+    padding-top: 75%;
+    box-shadow: 0 0 2px #bcbcbc;
+    .image-video-box-body {
+      position: absolute;
+      top: 0px;
+      left: 0px;
+      right: 0px;
+      bottom: 0px;
+      .video-react-big-play-button {
+        display: none;
+      }
+      .video-react-control-bar {
+        display: none;
+      }
+    }
+    .image-video-control {
+      position: absolute;
+      top: 0;
+      left: 0px;
+      right: 0px;
+      bottom: 0px;
+      background: rgba(255, 255, 255, 0.8);
+      padding-top: 30%;
+      text-align: center;
+      opacity: 0;
+      transition: all 0.3s;
+      i {
+        font-size: 20px;
+        cursor: pointer;
+      }
+      .anticon-copy {
+        color: rgb(38, 194, 129);
+        margin-right: 15px;
+      }
+      .anticon-edit {
+        color: #1890ff;
+        margin-right: 15px;
+      }
+      .anticon-delete {
+        color: #ff4d4f;
+      }
+    }
+  }
+  .image-video-box:hover {
+    .image-video-control {
+      opacity: 1;
+    }
+  }
+  .image-video-remark {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    word-break: break-all;
+    font-size: 13px;
+    margin-bottom: 0;
+    margin-top: 2px;
+    height: 35px;
+  }
+  .ant-pagination {
+    text-align: right;
+  }
+}
+.picture-edit-model {
+  .ant-modal {
+    .ant-modal-body {
+      min-height: 200px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/menu/picturecontroller/video/index.jsx b/src/menu/picturecontroller/video/index.jsx
new file mode 100644
index 0000000..1ac5f18
--- /dev/null
+++ b/src/menu/picturecontroller/video/index.jsx
@@ -0,0 +1,29 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Player } from 'video-react'
+
+import './index.scss'
+
+class Video extends Component {
+  static propTpyes = {
+    value: PropTypes.string,    // 瑙嗛鍦板潃
+  }
+
+  componentDidMount () {
+    this.player.seek(1)
+  }
+
+  shouldComponentUpdate () {
+    return false
+  }
+
+  render() {
+    return (
+      <Player ref={player => { this.player = player }} aspectRatio={'4:3'}>
+        <source src={this.props.value} />
+      </Player>
+    )
+  }
+}
+
+export default Video
\ No newline at end of file
diff --git a/src/menu/picturecontroller/video/index.scss b/src/menu/picturecontroller/video/index.scss
new file mode 100644
index 0000000..0acabae
--- /dev/null
+++ b/src/menu/picturecontroller/video/index.scss
@@ -0,0 +1,52 @@
+.video-react {
+  display: block;
+  box-sizing: border-box;
+  color: #fff;
+  background-color: #000;
+  position: relative;
+  font-size: 10px;
+  line-height: 1;
+  font-family: serif, Times, "Times New Roman";
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+.video-react *,
+.video-react *:before,
+.video-react *:after {
+  box-sizing: inherit;
+}
+.video-react ul {
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+  list-style-position: outside;
+  margin-left: 0;
+  margin-right: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+.video-react.video-react-fluid, .video-react.video-react-16-9, .video-react.video-react-4-3 {
+  width: 100%;
+  max-width: 100%;
+  height: 0;
+}
+.video-react.video-react-16-9 {
+  padding-top: 56.25%;
+}
+.video-react.video-react-4-3 {
+  padding-top: 75%;
+}
+.video-react.video-react-fill {
+  width: 100%;
+  height: 100%;
+}
+.video-react .video-react-video {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+}
diff --git a/src/menu/popview/index.jsx b/src/menu/popview/index.jsx
index 97061ff..18d2fb6 100644
--- a/src/menu/popview/index.jsx
+++ b/src/menu/popview/index.jsx
@@ -21,7 +21,7 @@
 const { confirm } = Modal
 
 const MenuForm = asyncComponent(() => import('./menuform'))
-const SourceWrap = asyncComponent(() => import('@/menu/modelsource'))
+const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
 const MenuShell = asyncComponent(() => import('@/menu/menushell'))
 const BgController = asyncComponent(() => import('@/menu/bgcontroller'))
 const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
@@ -246,37 +246,40 @@
           traversal(item.components)
         } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
           item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
             buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
             _sort++
           })
           item.subcards.forEach(card => {
             card.elements && card.elements.forEach(cell => {
               if (cell.eleType !== 'button') return
-
+              this.checkBtn(cell)
               buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
               _sort++
             })
             card.backElements && card.backElements.forEach(cell => {
               if (cell.eleType !== 'button') return
-
+              this.checkBtn(cell)
               buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
               _sort++
             })
           })
         } else if (item.type === 'line' || item.type === 'bar') {
           item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
             buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
             _sort++
           })
         } else if (item.type === 'table' && item.subtype === 'normaltable') {
           item.action && item.action.forEach(btn => {
-            if (btn.origin) return
+            this.checkBtn(btn)
             buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
             _sort++
           })
           item.cols && item.cols.forEach(col => {
             if (col.type !== 'action') return
             col.elements.forEach(btn => {
+              this.checkBtn(btn)
               buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
               _sort++
             })
@@ -288,6 +291,26 @@
     traversal(config.components)
 
     return buttons
+  }
+
+  checkBtn = (btn) => {
+    if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+      let hascheck = false
+      btn.verify.scripts.forEach(item => {
+        if (item.status === 'false') return
+  
+        if (/\$check@|@check\$/ig.test(item.sql)) {
+          hascheck = true
+        }
+      })
+      if (hascheck) {
+        notification.warning({
+          top: 92,
+          message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+          duration: 5
+        })
+      }
+    }
   }
 
   filterConfig = (components) => {
@@ -321,52 +344,54 @@
       return
     }
 
-    config.components = this.filterConfig(config.components)
-
-    if (config.enabled && this.verifyConfig()) {
-      config.enabled = false
-    }
-
-    let _config = fromJS(config).toJS()
-    delete _config.tableFields
-
-    let _name = (btn.component.name ? btn.component.name + '-' : '') + btn.label
-
-    let param = {
-      func: 'sPC_ButtonParam_AddUpt',
-      ParentID: btn.config.uuid,
-      MenuID: _config.uuid,
-      MenuNo: _config.MenuNo || '',
-      Template: 'CustomPage',
-      MenuName: _name,
-      PageParam: JSON.stringify({Template: 'CustomPage'}),
-      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
-    }
-
-    if (openEdition) { // 鐗堟湰绠$悊
-      param.open_edition = openEdition
-    }
-
-    let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
-      func: 'sPC_Button_AddUpt',
-      Type: 60,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
-      ParentID: _config.uuid,
-      MenuNo: _config.MenuNo,
-      Template: 'CustomPage',
-      PageParam: '',
-      LongParam: '',
-      LText: []
-    }
-
-    btnParam.LText = this.getMenuMessage()
-    btnParam.LText = btnParam.LText.join(' union all ')
-    btnParam.LText = Utils.formatOptions(btnParam.LText)
-    btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-    btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
-
     this.setState({
       menuloading: true
-    }, () => {
+    })
+
+    setTimeout(() => {
+      config.components = this.filterConfig(config.components)
+
+      if (config.enabled && this.verifyConfig()) {
+        config.enabled = false
+      }
+
+      let _config = fromJS(config).toJS()
+      delete _config.tableFields
+
+      let _name = (btn.component.name ? btn.component.name + '-' : '') + btn.label
+
+      let param = {
+        func: 'sPC_ButtonParam_AddUpt',
+        ParentID: btn.config.uuid,
+        MenuID: _config.uuid,
+        MenuNo: _config.MenuNo || '',
+        Template: 'CustomPage',
+        MenuName: _name,
+        PageParam: JSON.stringify({Template: 'CustomPage'}),
+        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+      }
+
+      if (openEdition) { // 鐗堟湰绠$悊
+        param.open_edition = openEdition
+      }
+
+      let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
+        func: 'sPC_Button_AddUpt',
+        Type: 60,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
+        ParentID: _config.uuid,
+        MenuNo: _config.MenuNo,
+        Template: 'CustomPage',
+        PageParam: '',
+        LongParam: '',
+        LText: []
+      }
+
+      btnParam.LText = this.getMenuMessage()
+      btnParam.LText = btnParam.LText.join(' union all ')
+      btnParam.LText = Utils.formatOptions(btnParam.LText)
+      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
+
       new Promise(resolve => {
         if (delButtons.length === 0) {
           resolve({
@@ -453,7 +478,7 @@
           })
         }
       })
-    })
+    }, 300)
   }
 
   onEnabledChange = () => {
@@ -474,7 +499,7 @@
 
     config.components.forEach(item => {
       if (error) return
-      if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return
+      if (['propcard', 'brafteditor', 'sandbox'].includes(item.subtype) && item.wrap.datatype === 'static') return
 
       if (item.setting) {
         if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
@@ -560,10 +585,10 @@
               {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
                 <SourceWrap components={customComponents} MenuType={MenuType} />
               </Panel> : null}
-              <Panel header={'鑳屾櫙'} key="background">
+              <Panel header={'椤甸潰鑳屾櫙'} key="background">
                 {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
               </Panel>
-              <Panel header={'鍐呰竟璺�'} key="padding">
+              <Panel header={'椤甸潰鍐呰竟璺�'} key="padding">
                 {config ? <PaddingController config={config} updateConfig={this.updateConfig} /> : null}
               </Panel>
             </Collapse>
diff --git a/src/menu/stylecombcontrolbutton/index.jsx b/src/menu/stylecombcontrolbutton/index.jsx
index 328487d..19f3454 100644
--- a/src/menu/stylecombcontrolbutton/index.jsx
+++ b/src/menu/stylecombcontrolbutton/index.jsx
@@ -148,7 +148,7 @@
   render() {
     const { label } = this.state
     return (
-      <Button className="style-control-button" type="link" icon="font-colors" title="璋冩暣鏍峰紡" onClick={this.triggerStyleChange}>{label}</Button>
+      <Button className="style-control-button" icon="font-colors" title="璋冩暣鏍峰紡" onClick={this.triggerStyleChange}>{label}</Button>
     )
   }
 }
diff --git a/src/menu/stylecombcontrolbutton/index.scss b/src/menu/stylecombcontrolbutton/index.scss
index 4b518f3..09668c2 100644
--- a/src/menu/stylecombcontrolbutton/index.scss
+++ b/src/menu/stylecombcontrolbutton/index.scss
@@ -1,6 +1,7 @@
 
-.style-control-button.ant-btn-link, .style-control-button.ant-btn-link:hover, .style-control-button.ant-btn-link:focus {
+.style-control-button, .style-control-button:hover, .style-control-button:focus {
   color: orange;
+  border-color: orange;
   position: relative;
   z-index: 13;
   background: #ffffff;
diff --git a/src/menu/stylecontroller/index.jsx b/src/menu/stylecontroller/index.jsx
index fff73bc..ee460b5 100644
--- a/src/menu/stylecontroller/index.jsx
+++ b/src/menu/stylecontroller/index.jsx
@@ -1,18 +1,19 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Collapse, Form, Input, Col, Icon, InputNumber, Select, Radio, Drawer, Button } from 'antd'
+import { Collapse, Form, Col, Icon, InputNumber, Select, Radio, Drawer, Button } from 'antd'
 
 import MKEmitter from '@/utils/events.js'
 import zhCN from '@/locales/zh-CN/mob.js'
 import enUS from '@/locales/en-US/mob.js'
-import ColorSketch from '@/mob/colorsketch'
+import asyncComponent from '@/utils/asyncComponent'
 import StyleInput from './styleInput'
-import FileUpload from '@/tabviews/zshare/fileupload'
 import './index.scss'
 
 const { Panel } = Collapse
 const { Option } = Select
+const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
 
 class MobController extends Component {
   static propTpyes = {
@@ -24,7 +25,6 @@
     dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     comIds: [],
-    bgimages: [],
     backgroundImage: '',
     options: [],
     borposition: 'outer'
@@ -50,12 +50,8 @@
 
   initStyle = (comIds, options, style = {}) => {
     let backgroundImage = ''
-    if (style.backgroundImage) {
-      if (/^url/ig.test(style.backgroundImage)) {
-        backgroundImage = style.backgroundImage.replace(/^url\(/ig, '').replace(/\)$/ig, '')
-      } else if (/^linear-gradient/ig.test(style.backgroundImage)) {
-        backgroundImage = style.backgroundImage.replace(/^linear-gradient\(/ig, '').replace(/\)$/ig, '')
-      }
+    if (style.backgroundImage && /^url/ig.test(style.backgroundImage)) {
+      backgroundImage = style.backgroundImage.replace(/^url\(/ig, '').replace(/\)$/ig, '')
     }
 
     this.setState({
@@ -69,6 +65,35 @@
   }
 
   onCloseDrawer = () => {
+    let { card } = this.state
+
+    let check = false
+    if (card.borderWidth === '0px') {
+      delete card.borderWidth
+      delete card.borderColor
+      check = true
+    } else if (card.borderLeftWidth === '0px') {
+      delete card.borderLeftWidth
+      delete card.borderLeftColor
+      check = true
+    } else if (card.borderRightWidth === '0px') {
+      delete card.borderRightWidth
+      delete card.borderRightWidth
+      check = true
+    } else if (card.borderTopWidth === '0px') {
+      delete card.borderTopWidth
+      delete card.borderTopWidth
+      check = true
+    } else if (card.borderBottomWidth === '0px') {
+      delete card.borderBottomWidth
+      delete card.borderBottomWidth
+      check = true
+    }
+
+    if (check) {
+      MKEmitter.emit('submitStyle', this.state.comIds, card)
+    }
+
     this.setState({
       visible: false,
       comIds: [],
@@ -175,30 +200,15 @@
     this.updateStyle({shadow: val})
   }
 
-  imgChange = (list) => {
-    if (list[0] && list[0].response) {
-      this.setState({
-        bgimages: [],
-        backgroundImage: list[0].response
-      })
-      this.updateStyle({backgroundImage: `url(${list[0].response})`})
+  imgChange = (val) => {
+    this.setState({
+      backgroundImage: val
+    })
+    if (val) {
+      this.updateStyle({backgroundImage: `url(${val})`})
     } else {
-      this.setState({bgimages: list})
+      this.updateStyle({backgroundImage: ''})
     }
-  }
-
-  changeBackgroundImageInput = (e) => {
-    let val = e.target.value
-    val = val.replace(/^\s*|\s*$/ig, '')
-
-    if (/^http|^\/\//.test(val)) {
-      val = `url(${val})`
-    } else if (/,/ig.test(val) && !/^(radial-gradient|linear-gradient)/ig.test(val)) {
-      val = `linear-gradient(${val})`
-    }
-
-    this.setState({backgroundImage: e.target.value})
-    this.updateStyle({backgroundImage: val})
   }
 
   changeBorderStyle = (val) => {
@@ -272,7 +282,7 @@
   }
 
   render () {
-    const { card, options, backgroundImage, bgimages, borposition } = this.state
+    const { card, options, backgroundImage, borposition } = this.state
     const formItemLayout = {
       labelCol: {
         xs: { span: 24 },
@@ -415,8 +425,7 @@
                     label={<Icon title="鑳屾櫙鍥剧墖" type="picture" />}
                     labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                   >
-                    <FileUpload accept=".jpg,.png,.gif,.svg" value={bgimages} maxFile={2} fileType="text" onChange={this.imgChange}/>
-                    <Input placeholder="" value={backgroundImage} autoComplete="off" onChange={this.changeBackgroundImageInput} />
+                    <SourceComponent value={backgroundImage} type="" placement="right" onChange={this.imgChange}/>
                   </Form.Item>
                 </Col> : null}
               </Panel> : null}
diff --git a/src/menu/stylecontroller/index.scss b/src/menu/stylecontroller/index.scss
index e17070f..082735e 100644
--- a/src/menu/stylecontroller/index.scss
+++ b/src/menu/stylecontroller/index.scss
@@ -28,6 +28,18 @@
         .ant-input-number {
           width: 100%;
         }
+        .mk-source-wrap {
+          height: 32px;
+          .anticon-paper-clip {
+            color:rgba(255, 255, 255, 0.7);
+          }
+          .anticon-delete {
+            color:rgba(255, 255, 255, 0.7);
+          }
+          .mk-source-item-info:hover {
+            background-color: transparent;
+          }
+        }
         .ant-form-item {
           margin-bottom: 2px;
 
diff --git a/src/menu/sysinterface/index.jsx b/src/menu/sysinterface/index.jsx
new file mode 100644
index 0000000..0232ce8
--- /dev/null
+++ b/src/menu/sysinterface/index.jsx
@@ -0,0 +1,215 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Modal, Button, Icon, Popconfirm, message } from 'antd'
+
+import Utils from '@/utils/utils.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const SettingForm = asyncComponent(() => import('./settingform'))
+const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
+
+class InterfaceController extends Component {
+  static propTpyes = {
+    config: PropTypes.object,       // 椤甸潰閰嶇疆
+    updateConfig: PropTypes.func    // 鏇存柊
+  }
+
+  state = {
+    visible: false,
+    setvisible: false,
+    interfaces: [],
+    card: null,
+    columns: [
+      {
+        title: '鎺ュ彛鍚嶇О',
+        dataIndex: 'name',
+        width: '50%'
+      },
+      {
+        title: '鐘舵��',
+        dataIndex: 'status',
+        width: '20%',
+        render: (text, record) => record.status !== 'true' ?
+          (
+            <div>
+              绂佺敤
+              <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" />
+            </div>
+          ) :
+          (
+            <div>
+              鍚敤
+              <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
+            </div>
+          )
+      },
+      {
+        title: '鎿嶄綔',
+        align: 'center',
+        width: '30%',
+        dataIndex: 'operation',
+        render: (text, record) =>
+          (<div style={{textAlign: 'center'}}>
+            <span onClick={() => this.handleEdit(record)} style={{color: '#1890ff', cursor: 'pointer', fontSize: '16px', marginRight: '15px'}}><Icon type="edit" /></span>
+            <span onClick={() => {this.copy(record)}} style={{color: '#26C281', cursor: 'pointer', fontSize: '16px', marginRight: '15px'}}><Icon type="copy" /></span>
+            <Popconfirm
+              overlayClassName="popover-confirm"
+              title="纭畾鍒犻櫎锛�"
+              onConfirm={() => this.deleteScript(record)
+            }>
+              <span style={{color: '#ff4d4f', cursor: 'pointer', fontSize: '16px'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ]
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  copy = (item) => {
+    let msg = { key: 'interface', type: 'line', data: item }
+
+    try {
+      msg = window.btoa(window.encodeURIComponent(JSON.stringify(msg)))
+    } catch {
+      console.warn('Stringify Failure')
+      msg = ''
+    }
+
+    if (msg) {
+      let oInput = document.createElement('input')
+      oInput.value = msg
+      document.body.appendChild(oInput)
+      oInput.select()
+      document.execCommand('Copy')
+      document.body.removeChild(oInput)
+      message.success('澶嶅埗鎴愬姛銆�')
+    }
+  }
+
+  trigger = () => {
+    const { config } = this.props
+    let interfaces = config.interfaces ? fromJS(config.interfaces).toJS() : []
+
+    this.setState({
+      visible: true,
+      interfaces
+    })
+  }
+
+  handleEdit = (record) => {
+    this.setState({card: record, setvisible: true})
+  }
+
+  deleteScript = (record) => {
+    const { config } = this.props
+    let interfaces = this.state.interfaces.filter(item => item.uuid !== record.uuid)
+
+    this.setState({ interfaces })
+    this.props.updateConfig({...config, interfaces})
+  }
+  
+  changeScripts = (interfaces) => {
+    const { config } = this.props
+
+    this.setState({ interfaces })
+    this.props.updateConfig({...config, interfaces})
+  }
+
+  settingSave = () => {
+    const { config } = this.props
+    const { card } = this.state
+    let interfaces = fromJS(this.state.interfaces).toJS()
+
+    this.settingRef.handleConfirm().then(res => {
+      interfaces = interfaces.map(item => {
+        if (item.uuid === card.uuid) {
+          res.uuid = item.uuid
+
+          if (res.procMode !== 'inner' && res.preScripts && res.preScripts.filter(item => item.status !== 'false').length === 0) {
+            message.warning('鏈缃墠缃剼鏈紝涓嶅彲鍚敤锛�')
+            res.status = 'false'
+          } else if (res.callbackType === 'script' && res.cbScripts && res.cbScripts.filter(item => item.status !== 'false').length === 0) {
+            message.warning('鏈缃洖璋冭剼鏈紝涓嶅彲鍚敤锛�')
+            res.status = 'false'
+          }
+
+          return res
+        }
+        return item
+      })
+
+      this.setState({
+        card: null,
+        setvisible: false,
+        interfaces
+      })
+
+      this.props.updateConfig({...config, interfaces})
+    })
+  }
+
+  addInterface = () => {
+    const { config } = this.props
+    let interfaces = fromJS(this.state.interfaces).toJS()
+
+    interfaces.push({
+      uuid: Utils.getuuid(),
+      name: 'interface ' + (interfaces.length + 1),
+      procMode: 'script',
+      callbackType: 'script',
+      preScripts: [],
+      cbScripts: []
+    })
+
+    this.setState({
+      interfaces
+    })
+    this.props.updateConfig({...config, interfaces})
+  }
+
+  render() {
+    const { visible, setvisible, columns, interfaces, card } = this.state
+
+    return (
+      <div style={{display: 'inline-block'}}>
+        <Button className="mk-border-green" icon="api" onClick={this.trigger}>鎺ュ彛绠$悊</Button>
+        <Modal
+          title="鎺ュ彛绠$悊"
+          wrapClassName="interface-controller-modal"
+          visible={visible}
+          width={800}
+          maskClosable={false}
+          onCancel={() => {this.setState({visible: false})}}
+          footer={[
+            <Button key="colse" onClick={() => {this.setState({visible: false})}}>
+              鍏抽棴
+            </Button>
+          ]}
+          destroyOnClose
+        > 
+          <Button key="add-interface" className="mk-border-green" onClick={this.addInterface}> 娣诲姞 </Button>
+          <EditTable key="manage-interface" actions={['move', 'copy']} type="interface" data={interfaces} columns={columns} onChange={this.changeScripts}/>
+        </Modal>
+        <Modal
+          title={card ? card.name : '鎺ュ彛'}
+          wrapClassName="interface-edit-modal"
+          visible={setvisible}
+          width={900}
+          maskClosable={false}
+          onOk={this.settingSave}
+          onCancel={() => { this.setState({ setvisible: false })}}
+          destroyOnClose
+        >
+          <SettingForm config={card} wrappedComponentRef={(inst) => this.settingRef = inst}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default InterfaceController
\ No newline at end of file
diff --git a/src/menu/sysinterface/index.scss b/src/menu/sysinterface/index.scss
new file mode 100644
index 0000000..c4d74b5
--- /dev/null
+++ b/src/menu/sysinterface/index.scss
@@ -0,0 +1,20 @@
+.interface-controller-modal {
+  >.ant-modal >.ant-modal-content >.ant-modal-body {
+    min-height: 400px;
+    >.mk-border-green {
+      float: right;
+      position: relative;
+      z-index: 1;
+      margin-bottom: 10px;
+    }
+  }
+}
+.interface-edit-modal {
+  .ant-modal {
+    top: 70px;
+  }
+  .ant-modal-body {
+    min-height: 300px;
+    padding-top: 5px;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/baseform/index.jsx b/src/menu/sysinterface/settingform/baseform/index.jsx
new file mode 100644
index 0000000..1c4c9b8
--- /dev/null
+++ b/src/menu/sysinterface/settingform/baseform/index.jsx
@@ -0,0 +1,243 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Tooltip, Icon } from 'antd'
+
+import { formRule } from '@/utils/option.js'
+import './index.scss'
+
+const { TextArea } = Input
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,       // 瀛楀吀椤�
+    setting: PropTypes.object,    // 鏁版嵁婧愰厤缃�
+    updateStatus: PropTypes.func, // 鐘舵�佹洿鏂�
+  }
+
+  state = {
+    procMode: 'script',
+    funcTooltip: '',
+    funcRules: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { setting } = this.props
+
+    let usefulFields = sessionStorage.getItem('permFuncField')
+    if (usefulFields) {
+      try {
+        usefulFields = JSON.parse(usefulFields)
+      } catch {
+        usefulFields = []
+      }
+    } else {
+      usefulFields = []
+    }
+    
+    let tooltip = null
+    let rules = []
+
+    if (usefulFields.length > 0) {
+      tooltip = '寮�澶村彲鐢ㄥ瓧绗︼細' + usefulFields.join(', ')
+      let str = '^(' + usefulFields.join('|') + ')'
+      let _patten = new RegExp(str + formRule.func.innerPattern + '$', 'g')
+
+      rules.push({
+        pattern: _patten,
+        message: formRule.func.innerMessage
+      })
+    }
+
+    this.setState({
+      procMode: setting.procMode || 'script',
+      funcTooltip: tooltip,
+      funcRules: rules
+    })
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  onRadioChange = (e, key) => {
+    let value = e.target.value
+
+    if (key === 'procMode') {
+      this.setState({
+        procMode: value
+      })
+    }
+    this.props.updateStatus({[key]: value})
+  }
+
+  render() {
+    const { setting, dict } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { funcRules, funcTooltip, procMode } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="model-table-datasource-setting-form-box">
+        <Form {...formItemLayout} className="model-setting-form">
+          <Row gutter={24}>
+            <Col span={12}>
+              <Form.Item label="鎺ュ彛鍚�">
+                {getFieldDecorator('name', {
+                  initialValue: setting.name || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '鎺ュ彛鍚�!'
+                    },
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="鐘舵��">
+                {getFieldDecorator('status', {
+                  initialValue: setting.status || 'true'
+                })(
+                <Radio.Group>
+                  <Radio value="true">鍚敤</Radio>
+                  <Radio value="false">绂佺敤</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="鍙傛暟澶勭悊">
+                {getFieldDecorator('procMode', {
+                  initialValue: procMode,
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.select'] + '鍙傛暟澶勭悊鏂瑰紡!'
+                    },
+                  ]
+                })(
+                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onRadioChange(e, 'procMode')}}>
+                  <Radio value="script">鍓嶇疆鑴氭湰</Radio>
+                  <Radio value="inner">鍓嶇疆鍑芥暟</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+            {procMode === 'inner' ? <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title={funcTooltip}>
+                  <Icon type="question-circle" />
+                  鍓嶇疆鍑芥暟
+                </Tooltip>
+              }>
+                {getFieldDecorator('prevFunc', {
+                  initialValue: setting.prevFunc || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '鍓嶇疆鍑芥暟!'
+                    },
+                    {
+                      max: formRule.func.max,
+                      message: formRule.func.maxMessage
+                    },
+                    ...funcRules
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            <Col className="data-source" span={24}>
+              <Form.Item label="娴嬭瘯鍦板潃">
+                {getFieldDecorator('interface', {
+                  initialValue: setting.interface || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '娴嬭瘯鍦板潃!'
+                    },
+                  ]
+                })(<TextArea rows={2} />)}
+              </Form.Item>
+            </Col>
+            <Col className="data-source" span={24}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="姝e紡绯荤粺鎵�浣跨敤鐨勭殑鎺ュ彛鍦板潃銆�">
+                  <Icon type="question-circle" />
+                  姝e紡鍦板潃
+                </Tooltip>
+              }>
+                {getFieldDecorator('proInterface', {
+                  initialValue: setting.proInterface || ''
+                })(<TextArea rows={2} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="璇锋眰鏂瑰紡">
+                {getFieldDecorator('method', {
+                  initialValue: setting.method || 'post',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.select'] + '璇锋眰鏂瑰紡!'
+                    },
+                  ]
+                })(
+                <Radio.Group>
+                  <Radio value="get">GET</Radio>
+                  <Radio value="post">POST</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="鍥炶皟鏂瑰紡">
+                {getFieldDecorator('callbackType', {
+                  initialValue: setting.callbackType || 'script'
+                })(
+                <Radio.Group onChange={(e) => {this.onRadioChange(e, 'callbackType')}}>
+                  <Radio value="default">榛樿鑴氭湰</Radio>
+                  <Radio value="script">鑷畾涔夎剼鏈�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="鍥炶皟琛ㄥ悕">
+                {getFieldDecorator('cbTable', {
+                  initialValue: setting.cbTable || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '鍥炶皟琛ㄥ悕!'
+                    },
+                    {
+                      max: formRule.input.max,
+                      message: formRule.input.message
+                    }
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/baseform/index.scss b/src/menu/sysinterface/settingform/baseform/index.scss
new file mode 100644
index 0000000..a6d2df7
--- /dev/null
+++ b/src/menu/sysinterface/settingform/baseform/index.scss
@@ -0,0 +1,22 @@
+.model-table-datasource-setting-form-box {
+  position: relative;
+
+  .model-setting-form {
+    .data-source {
+      .ant-form-item-label {
+        width: 16.5%;
+      }
+      .ant-form-item-control-wrapper {
+        width: 83.5%;
+      }
+      .CodeMirror {
+        height: 150px;
+      }
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/index.jsx b/src/menu/sysinterface/settingform/index.jsx
new file mode 100644
index 0000000..94d9a80
--- /dev/null
+++ b/src/menu/sysinterface/settingform/index.jsx
@@ -0,0 +1,179 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Form, notification, Tabs } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import BaseForm from './baseform'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import './index.scss'
+
+const { TabPane } = Tabs
+const SimpleScript = asyncComponent(() => import('./simplescript'))
+
+class SettingForm extends Component {
+  static propTpyes = {
+    config: PropTypes.object,       // 椤甸潰閰嶇疆淇℃伅
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    formlist: [],
+    btnloading: false,
+    activeKey: 'setting',
+    setting: null,
+    defaultSql: '',
+    status: {}
+  }
+
+  UNSAFE_componentWillMount() {
+    const { config } = this.props
+
+    let _setting = fromJS(config).toJS()
+    let _preScripts = _setting.preScripts || []
+    let _cbScripts = _setting.cbScripts || []
+
+    this.setState({
+      setting: _setting,
+      preScripts: _preScripts,
+      cbScripts: _cbScripts,
+      status: fromJS(_setting).toJS()
+    })
+  }
+
+
+  handleConfirm = () => {
+    const { activeKey, setting, preScripts, cbScripts } = this.state
+
+    let _loading = false
+    if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    }
+
+    if (_loading) {
+      notification.warning({
+        top: 92,
+        message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
+        duration: 5
+      })
+      return Promise.reject()
+    }
+
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    if (activeKey === 'setting') {
+      return new Promise((resolve, reject) => {
+        this.settingForm.handleConfirm().then(res => {
+          resolve({...res, preScripts, cbScripts})
+        }, () => {
+          reject()
+        })
+      })
+    } else {
+      return new Promise((resolve) => {
+        resolve({...setting, preScripts, cbScripts})
+      })
+    }
+  }
+
+  // 鏍囩鍒囨崲
+  changeTab = (val) => {
+    const { activeKey } = this.state
+
+    let _loading = false
+    if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    }
+
+    if (_loading) {
+      notification.warning({
+        top: 92,
+        message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
+        duration: 5
+      })
+      return
+    }
+
+    if (activeKey === 'setting') {
+      this.settingForm.handleConfirm().then(res => {
+        this.setState({
+          setting: res,
+          activeKey: val
+        })
+      })
+    } else {
+      this.setState({
+        activeKey: val
+      })
+    }
+  }
+  
+  // 鍓嶇疆鑴氭湰鏇存柊
+  preScriptsUpdate = (preScripts) => {
+    this.setState({preScripts})
+  }
+  
+  // 鍚庣疆鑴氭湰鏇存柊
+  cbScriptsUpdate = (cbScripts) => {
+    this.setState({cbScripts})
+  }
+
+  updateStatus = (status) => {
+    this.setState({status: {...this.state.status, ...status}})
+  }
+
+  render() {
+    const { dict, activeKey, setting, preScripts, cbScripts, status } = this.state
+
+    return (
+      <div className="model-interface-form-box" id="model-interface-form-body">
+        <Tabs activeKey={activeKey} className="verify-card-box" onChange={this.changeTab}>
+          <TabPane tab="鎺ュ彛璁剧疆" key="setting">
+            <BaseForm
+              dict={dict}
+              setting={setting}
+              updateStatus={this.updateStatus}
+              wrappedComponentRef={(inst) => this.settingForm = inst}
+            />
+          </TabPane>
+          <TabPane tab={
+            <span>
+              鍓嶇疆鑴氭湰
+              {preScripts.length ? <span className="count-tip">{preScripts.length}</span> : null}
+            </span>
+          } disabled={status.procMode !== 'script'} key="prescripts">
+            <SimpleScript
+              dict={dict}
+              type="front"
+              setting={setting}
+              scripts={preScripts}
+              scriptsUpdate={this.preScriptsUpdate}
+              wrappedComponentRef={(inst) => this.preScriptsForm = inst}
+            />
+          </TabPane>
+          <TabPane tab={
+            <span>
+              鍥炶皟鑴氭湰
+              {cbScripts.length ? <span className="count-tip">{cbScripts.length}</span> : null}
+            </span>
+          } disabled={status.callbackType !== 'script'} key="cbscripts">
+            <SimpleScript
+              dict={dict}
+              type="back"
+              setting={setting}
+              scripts={cbScripts}
+              scriptsUpdate={this.cbScriptsUpdate}
+              wrappedComponentRef={(inst) => this.cbScriptsForm = inst}
+            />
+          </TabPane>
+        </Tabs>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/index.scss b/src/menu/sysinterface/settingform/index.scss
new file mode 100644
index 0000000..d4d8d9d
--- /dev/null
+++ b/src/menu/sysinterface/settingform/index.scss
@@ -0,0 +1,65 @@
+.model-interface-form-box {
+  position: relative;
+
+  >.ant-spin {
+    position: absolute;
+    top: 150px;
+    left: calc(50% - 16px);
+  }
+  .count-tip {
+    position: absolute;
+    top: 0px;
+    color: #1890ff;
+    font-size: 12px;
+  }
+  .model-table-setting-form {
+    .textarea {
+      .ant-form-item-label {
+        width: 16.3%;
+      }
+      .ant-form-item-control-wrapper {
+        width: 83.33333333%;
+      }
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+    .text-area {
+      .CodeMirror {
+        height: 150px;
+      }
+    }
+  }
+  .operation-btn {
+    display: inline-block;
+    font-size: 16px;
+    padding: 0 5px;
+    cursor: pointer;
+  }
+  td {
+    word-break: break-all;
+  }
+  .setting-custom-back {
+    position: absolute;
+    top: -20px;
+    left: -10px;
+    font-size: 16px;
+    z-index: 1;
+    cursor: pointer;
+    padding: 10px;
+    color: rgb(24, 144, 255);
+  }
+  .to-custom-script {
+    float: right;
+    color: #1890ff;
+    margin-right: 12px;
+    margin-top: 15px;
+    cursor: pointer;
+    border: 0;
+    box-shadow: unset;
+  }
+  .ant-tabs-nav-wrap {
+    text-align: center;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/simplescript/index.jsx b/src/menu/sysinterface/settingform/simplescript/index.jsx
new file mode 100644
index 0000000..0617137
--- /dev/null
+++ b/src/menu/sysinterface/settingform/simplescript/index.jsx
@@ -0,0 +1,450 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Form, Row, Col, Icon, Button, notification, Select, Popconfirm, Typography, Modal, Radio } from 'antd'
+import moment from 'moment'
+
+import Utils from '@/utils/utils.js'
+import Api from '@/api'
+import SettingUtils from '../utils'
+import CodeMirror from '@/templates/zshare/codemirror'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const { Paragraph } = Typography
+const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
+
+class CustomForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,         // 瀛楀吀椤�
+    type: PropTypes.string,         // 鑴氭湰绫诲瀷
+    setting: PropTypes.object,      // 璁剧疆
+    scripts: PropTypes.array,       // 鑷畾涔夎剼鏈垪琛�
+    scriptsChange: PropTypes.func,  // 鑷畾涔夎剼鏈垏鎹㈡椂楠岃瘉
+    scriptsUpdate: PropTypes.func   // 琛ㄥ崟
+  }
+
+  state = {
+    editItem: null,
+    loading: false,
+    systemScripts: [],
+    scriptsColumns: [
+      {
+        title: 'SQL',
+        dataIndex: 'sql',
+        width: '60%',
+        render: (text) => {
+          let title = text.match(/^\s*\/\*.+\*\//)
+          title = title && title[0] ? title[0] : ''
+          text = title ? text.replace(title, '') : text
+
+          return (
+            <div>
+              {title ? <span style={{color: '#a50'}}>{title}</span> : null}
+              <Paragraph copyable ellipsis={{ rows: 4, expandable: true }}>{text}</Paragraph>
+            </div>
+          )
+        }
+      },
+      {
+        title: '鎵ц浣嶇疆',
+        dataIndex: 'position',
+        width: '13%',
+        render: (text, record) => {
+          if (record.position === 'front') {
+            return 'sql鍓�'
+          } else {
+            return 'sql鍚�'
+          }
+        }
+      },
+      {
+        title: '鐘舵��',
+        dataIndex: 'status',
+        width: '12%',
+        render: (text, record) => record.status === 'false' ?
+          (
+            <div>
+              {this.props.dict['model.status.forbidden']}
+              <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" />
+            </div>
+          ) :
+          (
+            <div>
+              {this.props.dict['model.status.open']}
+              <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
+            </div>
+          )
+      },
+      {
+        title: '鎿嶄綔',
+        align: 'center',
+        width: '15%',
+        dataIndex: 'operation',
+        render: (text, record) =>
+          (<div style={{textAlign: 'center'}}>
+            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record)} style={{color: '#1890ff'}}><Icon type="edit" /></span>
+            <span className="operation-btn" title={this.props.dict['header.form.status.change']} onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><Icon type="swap" /></span>
+            <Popconfirm
+              overlayClassName="popover-confirm"
+              title={this.props.dict['model.query.delete']}
+              onConfirm={() => this.handleDelete(record)
+            }>
+              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount() {
+    const { scripts } = this.props
+
+    let scriptsColumns = fromJS(this.state.scriptsColumns).toJS()
+
+    this.setState({
+      scripts: fromJS(scripts).toJS(),
+      scriptsColumns
+    })
+  }
+
+  componentDidMount () {
+    this.getsysScript()
+  }
+
+  getsysScript = () => {
+    let _scriptSql = `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`
+
+    _scriptSql = Utils.formatOptions(_scriptSql)
+
+    let _sParam = {
+      func: 'sPC_Get_SelectedList',
+      LText: _scriptSql,
+      obj_name: 'data',
+      arr_field: 'funcname,longparam'
+    }
+    
+    _sParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    _sParam.secretkey = Utils.encrypt(_sParam.LText, _sParam.timestamp)
+    _sParam.open_key = Utils.encryptOpenKey(_sParam.secretkey, _sParam.timestamp) // 浜戠鏁版嵁楠岃瘉
+    
+    Api.getSystemConfig(_sParam).then(res => {
+      if (res.status) {
+        let _scripts = res.data.map(item => {
+          let _item = {
+            name: item.funcname,
+            value: window.decodeURIComponent(window.atob(item.longparam))
+          }
+          return _item
+        })
+
+        this.setState({
+          systemScripts: _scripts
+        })
+      } else {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  handleCancel = () => {
+    this.setState({
+      editItem: null
+    })
+    this.props.form.setFieldsValue({
+      sql: ''
+    })
+  }
+
+  handleConfirm = () => {
+    const { scripts, editItem } = this.state
+    
+    let _sql = this.props.form.getFieldValue('sql')
+
+    if (!_sql) {
+      notification.warning({
+        top: 92,
+        message: '璇峰~鍐欒嚜瀹氫箟鑴氭湰锛�',
+        duration: 5
+      })
+      return
+    } else if (/^\s+$/.test(_sql)) {
+      notification.warning({
+        top: 92,
+        message: '鑷畾涔夎剼鏈笉鍙负绌猴紒',
+        duration: 5
+      })
+      return
+    }
+
+    let values = {
+      uuid: editItem && editItem.uuid ? editItem.uuid : Utils.getuuid(),
+      sql: _sql,
+    }
+
+    if (this.props.form.getFieldValue('position')) {
+      values.position = this.props.form.getFieldValue('position')
+    }
+
+    let _quot = values.sql.match(/'{1}/g)
+    let _lparen = values.sql.match(/\({1}/g)
+    let _rparen = values.sql.match(/\){1}/g)
+
+    _quot = _quot ? _quot.length : 0
+    _lparen = _lparen ? _lparen.length : 0
+    _rparen = _rparen ? _rparen.length : 0
+
+    if (_quot % 2 !== 0) {
+      notification.warning({
+        top: 92,
+        message: 'sql涓璡'蹇呴』鎴愬鍑虹幇',
+        duration: 5
+      })
+      return
+    } else if (_lparen !== _rparen) {
+      notification.warning({
+        top: 92,
+        message: 'sql涓�()蹇呴』鎴愬鍑虹幇',
+        duration: 5
+      })
+      return
+    } else if (/--/ig.test(values.sql)) {
+      notification.warning({
+        top: 92,
+        message: '鑷畾涔塻ql璇彞涓紝涓嶅彲鍑虹幇瀛楃 -- 锛屾敞閲婅鐢� /*鍐呭*/',
+        duration: 5
+      })
+      return
+    }
+
+    let error = Utils.verifySql(values.sql, 'customscript')
+
+    if (error) {
+      notification.warning({
+        top: 92,
+        message: 'sql涓笉鍙娇鐢�' + error,
+        duration: 5
+      })
+      return
+    }
+
+    let _scripts = fromJS(scripts).toJS()
+
+    if (editItem && editItem.uuid) {
+      _scripts = _scripts.map(item => {
+        if (item.uuid === values.uuid) {
+          return values
+        } else {
+          return item
+        }
+      })
+    } else {
+      _scripts.push(values)
+    }
+
+    let param = {
+      func: 's_debug_sql',
+      exec_type: 'y',
+      LText: SettingUtils.getCustomDebugSql(_scripts)
+    }
+    param.LText = Utils.formatOptions(param.LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+    
+    this.setState({loading: true})
+    Api.getLocalConfig(param).then(result => {
+      if (result.status) {
+        this.setState({
+          loading: false,
+          scripts: _scripts,
+          editItem: null
+        })
+  
+        this.props.scriptsUpdate(_scripts)
+        this.props.form.setFieldsValue({
+          sql: ''
+        })
+      } else {
+        this.setState({loading: false})
+        Modal.error({
+          title: result.message
+        })
+      }
+    })
+  }
+
+  selectScript = (value, option) => {
+    if (!value || !option) return
+    let _sql = this.props.form.getFieldValue('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
+    })
+  }
+
+  handleEdit = (record) => {
+    const { type } = this.props
+    this.setState({
+      editItem: record
+    })
+
+    if (type === 'front') {
+      this.props.form.setFieldsValue({
+        sql: record.sql
+      })
+    } else {
+      this.props.form.setFieldsValue({
+        sql: record.sql,
+        position: record.position || 'back'
+      })
+    }
+
+    this.scrolltop()
+  }
+
+  scrolltop = () => {
+    let node = document.getElementById('model-interface-form-body').parentNode
+
+    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)
+    }
+  }
+
+  changeScripts = (scripts) => {
+    this.setState({scripts})
+    this.props.scriptsUpdate(scripts)
+  }
+
+  handleStatus = (record) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+    record.status = record.status === 'false' ? 'true' : 'false'
+
+    scripts = scripts.map(item => {
+      if (item.uuid === record.uuid) {
+        return record
+      } else {
+        return item
+      }
+    })
+
+    this.setState({scripts})
+    this.props.scriptsUpdate(scripts)
+  }
+
+  handleDelete = (record) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+    scripts = scripts.filter(item => item.uuid !== record.uuid)
+
+    this.setState({ scripts })
+    this.props.scriptsUpdate(scripts)
+  }
+
+  render() {
+    const { setting, scripts, type } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { scriptsColumns, systemScripts } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="modal-menu-setting-script">
+        <Form {...formItemLayout}>
+          <Row gutter={24}>
+            <Col span={8}>
+              <Form.Item label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+                {setting.cbTable}
+              </Form.Item>
+            </Col>
+            <Col span={16}>
+              <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
+                ErrorCode, retmsg
+              </Form.Item>
+            </Col>
+            <Col span={24} className="sqlfield">
+              <Form.Item label={'鍙敤瀛楁'}>
+                bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id
+              </Form.Item>
+            </Col>
+            {type === 'back' ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
+              <Form.Item style={{marginBottom: 0}} label="鎵ц浣嶇疆">
+                {getFieldDecorator('position', {
+                  initialValue: 'front'
+                })(
+                  <Radio.Group>
+                    <Radio value="front">sql鍓�</Radio>
+                    <Radio value="back">sql鍚�</Radio>
+                  </Radio.Group>
+                )}
+              </Form.Item>
+            </Col> : null}
+            <Col span={10} className="quick-add">
+              <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
+                <Select
+                  allowClear
+                  showSearch
+                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  onChange={this.selectScript}
+                >
+                  {type === 'back' ? <Select.Option key="default" value={`declare @${setting.cbTable} table (mk_api_key nvarchar(100),mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))\n/*@${setting.cbTable}_data table (mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))*/`}>榛樿sql</Select.Option> : null}
+                  {systemScripts.map((option, i) =>
+                    <Select.Option style={{whiteSpace: 'normal'}} key={i} value={option.value}>{option.name}</Select.Option>
+                  )}
+                </Select>
+              </Form.Item>
+            </Col>
+            <Col span={6} className="add">
+              <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginTop: 5, marginBottom: 15, marginLeft: 30}}>
+                淇濆瓨
+              </Button>
+              <Button onClick={this.handleCancel} style={{marginTop: 5, marginBottom: 15, marginLeft: 10}}>
+                鍙栨秷
+              </Button>
+            </Col>
+            <Col span={24} className="sql">
+              <Form.Item label={'sql'}>
+                {getFieldDecorator('sql', {
+                  initialValue: ''
+                })(<CodeMirror />)}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+        <EditTable data={scripts} actions={['move']} columns={scriptsColumns} onChange={this.changeScripts}/>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/simplescript/index.scss b/src/menu/sysinterface/settingform/simplescript/index.scss
new file mode 100644
index 0000000..945809b
--- /dev/null
+++ b/src/menu/sysinterface/settingform/simplescript/index.scss
@@ -0,0 +1,45 @@
+.modal-menu-setting-script {
+  .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%;
+    }
+  }
+  .quick-add {
+    .ant-col-sm-8 {
+      width: 26%;
+    }
+    .ant-col-sm-16 {
+      width: 74%;
+    }
+  }
+  .sql {
+    .ant-col-sm-8 {
+      width: 10.5%;
+    }
+    .ant-col-sm-16 {
+      width: 89.5%;
+      padding-top: 4px;
+    }
+    .CodeMirror {
+      height: 350px;
+    }
+  }
+  div.ant-typography {
+    margin-bottom: 0;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/sysinterface/settingform/utils.jsx b/src/menu/sysinterface/settingform/utils.jsx
new file mode 100644
index 0000000..2c7ddce
--- /dev/null
+++ b/src/menu/sysinterface/settingform/utils.jsx
@@ -0,0 +1,45 @@
+
+export default class SettingUtils {
+  /**
+   * @description 鐢熸垚鍓嶇疆鎴栧悗缃鍙�
+   * @return {String}  scripts       鑴氭湰
+   */
+  static getCustomDebugSql (scripts) {
+    let sql = ''
+    let _customScript = ''
+
+    scripts.forEach(script => {
+      if (script.status === 'false') return
+
+      _customScript += `
+      ${script.sql}
+      `
+    })
+
+    if (_customScript) {
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
+        ${_customScript}
+      `
+    }
+
+    _customScript = _customScript.replace(/@\$|\$@/ig, '')
+    _customScript = _customScript.replace(/@userName@|@fullName@/ig, `''`)
+    // 澶栬仈鏁版嵁搴撴浛鎹�
+    if (window.GLOB.externalDatabase !== null) {
+      _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
+    }
+
+    if (_customScript) {
+      sql = `/* sql 楠岃瘉 */
+        ${_customScript}
+        aaa:
+        if @ErrorCode!=''
+          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+      `
+    }
+    sql = sql.replace(/\n\s{8}/ig, '\n')
+    console.info(sql)
+
+    return sql
+  }
+}
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/index.jsx b/src/mob/datasource/verifycard/index.jsx
index 1150f4a..d02dff0 100644
--- a/src/mob/datasource/verifycard/index.jsx
+++ b/src/mob/datasource/verifycard/index.jsx
@@ -150,7 +150,7 @@
         res.data.forEach(item => {
           let _item = {
             name: item.funcname,
-            value: Utils.UnformatOptions(item.longparam)
+            value: window.decodeURIComponent(window.atob(item.longparam))
           }
 
           _scripts.push(_item)
diff --git a/src/setupProxy.js b/src/setupProxy.js
index f0f93f1..3f0dc0e 100644
--- a/src/setupProxy.js
+++ b/src/setupProxy.js
@@ -49,4 +49,13 @@
     }
     // cookieDomainRewrite: "http://localhost:3000"
   }))
+
+  app.use(proxy('/trans', {
+    target: `${host}/${service}trans`,
+    secure: false,
+    changeOrigin: true,
+    pathRewrite: {
+    '^/trans': '/'
+    }
+  }))
 }
\ No newline at end of file
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index 2139f68..73a85b8 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -8,8 +8,9 @@
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
+import options from '@/store/options.js'
 import UtilsDM from '@/utils/utils-datamanage.js'
-import UtilsUpdate from '@/utils/utils-update.js'
+import { updateCommonTable } from '@/utils/utils-update.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import MKEmitter from '@/utils/events.js'
@@ -64,6 +65,8 @@
     statFields: [],       // 鍚堣瀛楁
     statFValue: [],       // 鍚堣鍊�
     absFields: [],        // 缁濆鍊煎瓧娈�
+    loadCustomApi: true,  // 鍔犺浇澶栭儴璧勬簮
+    hasReqFields: false
   }
 
   /**
@@ -86,6 +89,7 @@
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
         config.MenuID = this.props.MenuID
         config.MenuName = MenuName
+        config.setting.MenuName = MenuName
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -147,7 +151,7 @@
       }
 
       // 鐗堟湰鍏煎
-      config = UtilsUpdate.updateCommonTable(config)
+      config = updateCommonTable(config)
 
       // 鏉冮檺杩囨护
       if (this.props.menuType !== 'HS') {
@@ -210,7 +214,7 @@
       config.setting.execute = config.setting.default !== 'false'     // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
       config.setting.customScript = ''                                // 鑷畾涔夎剼鏈�
 
-      if (config.setting.interType === 'system') {
+      if (config.setting.interType === 'system' || (config.setting.interType === 'custom' && config.setting.requestMode === 'system')) {
         if (config.setting.scripts && config.setting.scripts.length > 0) {
           let _customScript = ''
           config.setting.scripts.forEach(item => {
@@ -338,13 +342,13 @@
       }
 
       let valid = true // 鎼滅储鏉′欢蹇呭~楠岃瘉, 鍒濆鎼滅储鏉′欢, 濡傞�氳繃涓婄骇閫忚锛屽啓鍏ユ悳绱㈡潯浠�
-      let initSearch = config.search.map(item => {
-        let _item = fromJS(item).toJS()
-
-        if (_item.required === 'true' && !_item.initval) {
+      let hasReqFields = false
+      config.search.forEach(item => {
+        if (item.required !== 'true') return
+        if (!item.initval) {
           valid = false
         }
-        return _item
+        hasReqFields = true
       })
 
       this.setState({
@@ -360,11 +364,11 @@
         columns: _columns,
         arr_field: _arrField.join(','),
         BID: param && param.BID ? param.BID : '',
-        search: Utils.initMainSearch(initSearch) // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        search: Utils.initMainSearch(config.search), // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        hasReqFields
       }, () => {
         if (config.setting.onload !== 'false' && valid) { // 鍒濆鍖栧彲鍔犺浇
-          this.loadmaindata()
-          this.getStatFieldsValue()
+          this.loadData()
         }
         this.setShortcut()
       })
@@ -422,12 +426,13 @@
     }
   }
 
-  /**
-   * @description 涓昏〃鏁版嵁鍔犺浇
-   */ 
-  async loadmaindata () {
-    const { setting, arr_field, BIDs, search, orderBy, BID, pageIndex, pageSize, absFields } = this.state
-    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
+  loadData = () => {
+    const { setting, search, BIDs, loadCustomApi, hasReqFields } = this.state
+    let requireFields = []
+
+    if (hasReqFields) {
+      requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
+    }
 
     this.setState({
       selectedData: [],
@@ -448,7 +453,198 @@
         duration: 3
       })
       return
+    } else if (window.GLOB.systemType === 'production' && setting.interType === 'custom' && !setting.proInterface) {
+      notification.warning({
+        top: 92,
+        message: '鏈缃寮忕郴缁熷湴鍧�!',
+        duration: 3
+      })
+      return
     }
+
+    if (setting.interType === 'custom' && loadCustomApi) {
+      if (setting.execTime === 'once') {
+        this.setState({loadCustomApi: false})
+      }
+
+      this.loadOutResource()
+      if (setting.execType === 'async') {
+        this.loadmaindata()
+      }
+    } else {
+      this.loadmaindata()
+    }
+  }
+
+  loadOutResource = () => {
+    const { setting, search, BID } = this.state
+
+    let param = UtilsDM.getPrevQueryParams(setting, search, BID, this.props.menuType)
+
+    if (setting.execType === 'sync') {
+      this.setState({
+        loading: true
+      })
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (res.mk_ex_invoke === 'false') {
+          this.loadmaindata()
+        } else {
+          this.customOuterRequest(res)
+        }
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
+  customOuterRequest = (result) => {
+    const { setting } = this.state
+    let url = ''
+
+    if (window.GLOB.systemType === 'production') {
+      url = setting.proInterface
+    } else {
+      url = setting.interface
+    }
+
+    let mkey = result.mk_api_key || ''
+
+    delete result.mk_ex_invoke
+    delete result.status
+    delete result.message
+    delete result.ErrCode
+    delete result.ErrMesg
+    delete result.mk_api_key
+
+    let param = {}
+
+    Object.keys(result).forEach(key => {
+      key = key.replace(/^mk_/ig, '')
+      param[key] = result[key]
+    })
+
+    Api.directRequest(url, setting.method, param).then(res => {
+      if (typeof(res) !== 'object' || Array.isArray(res)) {
+        let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
+
+        if (typeof(res) === 'string') {
+          error = res.replace(/'/ig, '"')
+        }
+
+        let _result = {
+          mk_api_key: mkey,
+          $ErrCode: 'E',
+          $ErrMesg: error
+        }
+
+        this.customCallbackRequest(_result)
+      } else {
+        res.mk_api_key = mkey
+        this.customCallbackRequest(res)
+      }
+    }, (e) => {
+      let _result = {
+        mk_api_key: mkey,
+        $ErrCode: 'E',
+        $ErrMesg: e && e.statusText ? e.statusText : ''
+      }
+
+      this.customCallbackRequest(_result)
+    })
+  }
+
+  customCallbackRequest = (result) => {
+    const { setting } = this.state
+    let errSql = ''
+    if (result.$ErrCode === 'E') {
+      errSql = `
+        set @ErrorCode='E'
+        set @retmsg='${result.$ErrMesg}'
+      `
+      delete result.$ErrCode
+      delete result.$ErrMesg
+    }
+
+    let lines = UtilsDM.getCallBackSql(setting, result)
+    let param = {}
+
+    if (setting.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈�
+      let sql = lines.map(item => (`
+        ${item.insert}
+        ${item.selects.join(` union all
+        `)}
+      `))
+      sql = sql.join('')
+      
+      param = UtilsDM.getCallBackQueryParams(setting, sql, errSql)
+
+      if (this.state.BID) {
+        param.BID = this.state.BID
+      }
+
+      if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+      }
+    } else {
+      param.func = 's_ex_result_back'
+      param.s_ex_result = lines.map((item, index) => ({
+        MenuID: this.state.config.MenuID,
+        MenuName: this.state.config.MenuName,
+        TableName: item.table,
+        LongText: window.btoa(window.encodeURIComponent(`${item.insert}  ${item.selects.join(` union all `)}`)),
+        Sort: index + 1
+      }))
+
+      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+        let sql = lines.map(item => (`
+          ${item.insert}
+          ${item.selects.join(` union all
+          `)}
+        `))
+        sql = sql.join('')
+        console.info(sql.replace(/\n\s{10}/ig, '\n'))
+      }
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        this.loadmaindata()
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
+  /**
+   * @description 涓昏〃鏁版嵁鍔犺浇
+   */ 
+  async loadmaindata () {
+    const { setting, arr_field, search, orderBy, BID, pageIndex, pageSize, absFields } = this.state
 
     this.setState({
       loading: true
@@ -462,6 +658,9 @@
     }
 
     let result = await Api.genericInterface(param)
+
+    this.getStatFieldsValue()
+    
     if (result.status) {
       this.setState({
         data: result.data.map((item, index) => {
@@ -567,12 +766,7 @@
   getStatFieldsValue = () => {
     const { setting, search, BID, orderBy, statFields } = this.state
 
-    if (statFields.length === 0 || setting.interType !== 'system' || !setting.dataresource) return
-
-    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
-    if (requireFields.length > 0) {
-      return
-    }
+    if (statFields.length === 0 || !(setting.interType === 'system' || (setting.interType === 'custom' && setting.requestMode === 'system')) || !setting.dataresource) return
 
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getStatQueryDataParams(setting, statFields, search, _orderBy, BID, this.props.menuType)
@@ -627,8 +821,7 @@
         search: searches,
         setting: {...setting, onload: 'true'}
       }, () => {
-        this.loadmaindata()
-        this.getStatFieldsValue()
+        this.loadData()
       })
     } else {
       MKEmitter.emit('resetTable', this.props.MenuID + 'mainTable') // 鍒楄〃閲嶇疆
@@ -636,8 +829,7 @@
         pageIndex: 1,
         search: searches
       }, () => {
-        this.loadmaindata()
-        this.getStatFieldsValue()
+        this.loadData()
       })
     }
   }
@@ -659,7 +851,7 @@
       pageSize: pagination.pageSize,
       orderBy: (sorter.field && sorter.order) ? `${sorter.field} ${sorter.order}` : ''
     }, () => {
-      this.loadmaindata()
+      this.loadData()
     })
   }
 
@@ -672,13 +864,11 @@
       this.setState({
         pageIndex: 1
       }, () => {
-        this.loadmaindata()
-        this.getStatFieldsValue()
+        this.loadData()
       })
     } else {
       MKEmitter.emit('resetTable', this.props.MenuID + 'mainTable', 'false') // 鍒楄〃閲嶇疆
-      this.loadmaindata()
-      this.getStatFieldsValue()
+      this.loadData()
     }
   }
 
diff --git a/src/tabviews/custom/components/card/data-card/asyncButtonComponent.jsx b/src/tabviews/custom/components/card/data-card/asyncButtonComponent.jsx
deleted file mode 100644
index 5fb9c1a..0000000
--- a/src/tabviews/custom/components/card/data-card/asyncButtonComponent.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React, {Component} from 'react'
-import { Button } from 'antd'
-
-/**
- * @description 寮傛鍔犺浇妯″潡
- * @param {*} importComponent
- */
-export default function asyncComponent(importComponent) {
-  return class extends Component {
-    constructor(props) {
-      super(props)
-
-      this.state = {
-        component: null
-      }
-    }
-
-    async componentDidMount() {
-      const {default: component} = await importComponent()
-
-      this.setState({component})
-    }
-
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
-    render() {
-      const C = this.state.component
-      const btn = this.props.btn || {}
-
-      return C ?
-        <C {...this.props} /> :
-        <Button icon={btn.OpenType === 'excelOut' ? 'download' : 'upload'} disabled={true} title={btn.label} style={{border: 0, background: 'transparent'}}></Button>
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx b/src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx
deleted file mode 100644
index 5fb9c1a..0000000
--- a/src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React, {Component} from 'react'
-import { Button } from 'antd'
-
-/**
- * @description 寮傛鍔犺浇妯″潡
- * @param {*} importComponent
- */
-export default function asyncComponent(importComponent) {
-  return class extends Component {
-    constructor(props) {
-      super(props)
-
-      this.state = {
-        component: null
-      }
-    }
-
-    async componentDidMount() {
-      const {default: component} = await importComponent()
-
-      this.setState({component})
-    }
-
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
-    render() {
-      const C = this.state.component
-      const btn = this.props.btn || {}
-
-      return C ?
-        <C {...this.props} /> :
-        <Button icon={btn.OpenType === 'excelOut' ? 'download' : 'upload'} disabled={true} title={btn.label} style={{border: 0, background: 'transparent'}}></Button>
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/card/table-card/asyncButtonComponent.jsx b/src/tabviews/custom/components/card/table-card/asyncButtonComponent.jsx
deleted file mode 100644
index 5fb9c1a..0000000
--- a/src/tabviews/custom/components/card/table-card/asyncButtonComponent.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React, {Component} from 'react'
-import { Button } from 'antd'
-
-/**
- * @description 寮傛鍔犺浇妯″潡
- * @param {*} importComponent
- */
-export default function asyncComponent(importComponent) {
-  return class extends Component {
-    constructor(props) {
-      super(props)
-
-      this.state = {
-        component: null
-      }
-    }
-
-    async componentDidMount() {
-      const {default: component} = await importComponent()
-
-      this.setState({component})
-    }
-
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
-    render() {
-      const C = this.state.component
-      const btn = this.props.btn || {}
-
-      return C ?
-        <C {...this.props} /> :
-        <Button icon={btn.OpenType === 'excelOut' ? 'download' : 'upload'} disabled={true} title={btn.label} style={{border: 0, background: 'transparent'}}></Button>
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/code/sand-box/index.jsx b/src/tabviews/custom/components/code/sand-box/index.jsx
new file mode 100644
index 0000000..204c9f7
--- /dev/null
+++ b/src/tabviews/custom/components/code/sand-box/index.jsx
@@ -0,0 +1,222 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Spin, notification } from 'antd'
+
+import Api from '@/api'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+class SandBoxComponent extends Component {
+  static propTpyes = {
+    BID: PropTypes.any,              // 鐖剁骇Id
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+    menuType: PropTypes.any,         // 鑿滃崟绫诲瀷
+  }
+
+  state = {
+    BID: '',                   // 涓婄骇ID
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    data: {},                  // 鏁版嵁
+    html: '',
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data, initdata, BID } = this.props
+    let _config = fromJS(this.props.config).toJS()
+
+    let _data = {}
+    let _sync = false
+    
+    if (_config.setting && _config.wrap.datatype !== 'static') {
+      _sync = _config.setting.sync === 'true'
+
+      if (_sync && data) {
+        _data = data[_config.dataName] || {}
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      } else if (_sync && initdata) {
+        _data = initdata || {}
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      }
+    } else {
+      _data = {}
+    }
+    
+    if (_config.css) {
+      let ele = document.createElement('style')
+      ele.innerHTML = _config.css
+      document.getElementsByTagName('head')[0].appendChild(ele)
+    }
+
+    this.setState({
+      sync: _sync,
+      data: _data,
+      BID: BID || '',
+      config: _config,
+      arr_field: _config.columns.map(col => col.field).join(','),
+    }, () => {
+      if (_config.wrap.datatype !== 'static' && _config.setting && _config.setting.sync !== 'true' && _config.setting.onload === 'true') {
+        this.loadData()
+      }
+      this.renderView()
+    })
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('reloadData', this.reloadData)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+  }
+
+  /**
+   * @description 鍥捐〃鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
+   */
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { sync, config } = this.state
+
+    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
+      let _data = {}
+      if (nextProps.data && nextProps.data[config.dataName]) {
+        _data = nextProps.data[config.dataName]
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0]
+        }
+      }
+
+      this.setState({sync: false, data: _data}, () => {
+        this.renderView()
+      })
+    } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      if (config.wrap.datatype !== 'static' && config.setting.syncRefresh === 'true') {
+        this.setState({}, () => {
+          this.loadData()
+        })
+      }
+    }
+  }
+
+  reloadData = (menuId) => {
+    const { config } = this.state
+
+    if (menuId !== config.uuid) return
+
+    this.loadData()
+  }
+
+  async loadData () {
+    const { mainSearch, menuType } = this.props
+    const { config, arr_field, BID } = this.state
+
+    if (config.wrap.datatype === 'static') {
+      this.setState({
+        data: {},
+        loading: false
+      })
+      return
+    } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        data: {},
+        loading: false
+      })
+      return
+    }
+
+    let searches = []
+    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      searches = mainSearch
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID, menuType)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let _data = result.data && result.data[0] ? result.data[0] : {}
+
+      this.setState({
+        data: _data,
+        loading: false
+      }, () => {
+        this.renderView()
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  renderView = () => {
+    const { data } = this.state
+    const { html, js, wrap, columns } = this.state.config
+
+    let _html = html
+    if (_html && wrap.datatype !== 'static') {
+      columns.forEach(col => {
+        if (col.field) {
+          let val = (data[col.field] || data[col.field] === 0) ? data[col.field] : ''
+          let reg = new RegExp('@' + col.field + '@', 'ig')
+
+          _html = _html.replace(reg, val)
+        }
+      })
+    }
+    this.setState({html: _html}, () => {
+      if (js) {
+        try {
+          // eslint-disable-next-line no-eval
+          eval(js)
+        } catch {
+          console.warn('JS 鎵ц澶辫触锛�')
+        }
+      }
+    })
+  }
+
+  render() {
+    const { config, loading, html } = this.state
+
+    return (
+      <div className="custom-sand-box" style={{...config.style}}>
+        {loading ?
+          <div className="loading-mask">
+            <div className="ant-spin-blur"></div>
+            <Spin />
+          </div> : null
+        }
+        <div dangerouslySetInnerHTML={{ __html: html }}></div>
+      </div>
+    )
+  }
+}
+
+export default SandBoxComponent
\ No newline at end of file
diff --git a/src/tabviews/custom/components/code/sand-box/index.scss b/src/tabviews/custom/components/code/sand-box/index.scss
new file mode 100644
index 0000000..5c9e379
--- /dev/null
+++ b/src/tabviews/custom/components/code/sand-box/index.scss
@@ -0,0 +1,34 @@
+.custom-sand-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  position: relative;
+
+  .loading-mask {
+    position: absolute;
+    left: 40px;
+    top: 0;
+    right: 40px;
+    bottom: 0px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: justify;
+    z-index: 1;
+
+    .ant-spin-blur {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      opacity: 0.5;
+      background: #ffffff;
+    }
+  }
+}
+
+.custom-sand-box::after {
+  content: ' ';
+  display: block;
+  clear: both;
+}
diff --git a/src/tabviews/custom/components/editor/braft-editor/index.jsx b/src/tabviews/custom/components/editor/braft-editor/index.jsx
new file mode 100644
index 0000000..5691f35
--- /dev/null
+++ b/src/tabviews/custom/components/editor/braft-editor/index.jsx
@@ -0,0 +1,191 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Spin, notification } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import Api from '@/api'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const BraftContent = asyncComponent(() => import('@/tabviews/custom/components/share/braftContent'))
+const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
+
+class BraftEditorContent extends Component {
+  static propTpyes = {
+    BID: PropTypes.any,              // 鐖剁骇Id
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+    menuType: PropTypes.any,         // 鑿滃崟绫诲瀷
+  }
+
+  state = {
+    BID: '',                   // 涓婄骇ID
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    data: {}                   // 鏁版嵁
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data, initdata, BID } = this.props
+    let _config = fromJS(this.props.config).toJS()
+
+    let _data = {}
+    let _sync = false
+    
+    if (_config.setting && _config.wrap.datatype !== 'static') {
+      _sync = _config.setting.sync === 'true'
+
+      if (_sync && data) {
+        _data = data[_config.dataName] || {}
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      } else if (_sync && initdata) {
+        _data = initdata || {}
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      }
+    } else {
+      _data = {}
+    }
+
+    this.setState({
+      sync: _sync,
+      data: _data,
+      BID: BID || '',
+      config: _config,
+      arr_field: _config.columns.map(col => col.field).join(','),
+    }, () => {
+      if (_config.wrap.datatype !== 'static' && _config.setting && _config.setting.sync !== 'true' && _config.setting.onload === 'true') {
+        this.loadData()
+      }
+    })
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('reloadData', this.reloadData)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+  }
+
+  /**
+   * @description 鍥捐〃鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
+   */
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { sync, config } = this.state
+
+    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
+      let _data = {}
+      if (nextProps.data && nextProps.data[config.dataName]) {
+        _data = nextProps.data[config.dataName]
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0]
+        }
+      }
+
+      this.setState({sync: false, data: _data})
+    } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      if (config.wrap.datatype !== 'static' && config.setting.syncRefresh === 'true') {
+        this.setState({}, () => {
+          this.loadData()
+        })
+      }
+    }
+  }
+
+  reloadData = (menuId) => {
+    const { config } = this.state
+
+    if (menuId !== config.uuid) return
+
+    this.loadData()
+  }
+
+  async loadData () {
+    const { mainSearch, menuType } = this.props
+    const { config, arr_field, BID } = this.state
+
+    if (config.wrap.datatype === 'static') {
+      this.setState({
+        data: {},
+        loading: false
+      })
+      return
+    } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        data: {},
+        loading: false
+      })
+      return
+    }
+
+    let searches = []
+    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      searches = mainSearch
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID, menuType)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let _data = result.data && result.data[0] ? result.data[0] : {}
+
+      this.setState({
+        data: _data,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  render() {
+    const { config, loading, data } = this.state
+
+    return (
+      <div className="custom-braft-editor-box" style={{...config.style}}>
+        {loading ?
+          <div className="loading-mask">
+            <div className="ant-spin-blur"></div>
+            <Spin />
+          </div> : null
+        }
+        <NormalHeader config={config}/>
+        <BraftContent
+          value={config.wrap.datatype !== 'static' ? (data[config.wrap.field] || '') : config.html}
+          encryption={config.wrap.datatype !== 'static' ? config.wrap.encryption : 'false'}
+        />
+      </div>
+    )
+  }
+}
+
+export default BraftEditorContent
\ No newline at end of file
diff --git a/src/tabviews/custom/components/editor/braft-editor/index.scss b/src/tabviews/custom/components/editor/braft-editor/index.scss
new file mode 100644
index 0000000..5bbfc97
--- /dev/null
+++ b/src/tabviews/custom/components/editor/braft-editor/index.scss
@@ -0,0 +1,35 @@
+.custom-braft-editor-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 20px;
+  position: relative;
+
+  .loading-mask {
+    position: absolute;
+    left: 40px;
+    top: 0;
+    right: 40px;
+    bottom: 0px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: justify;
+    z-index: 1;
+
+    .ant-spin-blur {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      opacity: 0.5;
+      background: #ffffff;
+    }
+  }
+}
+
+.custom-braft-editor-box::after {
+  content: ' ';
+  display: block;
+  clear: both;
+}
diff --git a/src/tabviews/custom/components/group/normal-group/index.jsx b/src/tabviews/custom/components/group/normal-group/index.jsx
index f38e4a2..9eadab0 100644
--- a/src/tabviews/custom/components/group/normal-group/index.jsx
+++ b/src/tabviews/custom/components/group/normal-group/index.jsx
@@ -18,6 +18,8 @@
 const DataCard = asyncComponent(() => import('@/tabviews/custom/components/card/data-card'))
 const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
 const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
+const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
+const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
 
 class TabTransfer extends Component {
   static propTpyes = {
@@ -45,7 +47,7 @@
       if (item.type === 'tabs') return
 
       if (!item.setting || item.setting.interType !== 'system') return
-      if (!item.format || (item.subtype === 'propcard' && item.wrap.datatype === 'static')) return
+      if (!item.format) return
 
       if (item.dataName && (!item.pageable || (item.pageable && !item.setting.laypage)) && item.setting.onload === 'true' && item.setting.sync === 'true') {
         let param = this.getDefaultParam(item, _mainSearch)
@@ -267,6 +269,18 @@
             <TableCard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'editor') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <BraftEditor config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'code') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <SandBox config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
       } else {
         return null
       }
diff --git a/src/tabviews/custom/components/share/braftContent/index.jsx b/src/tabviews/custom/components/share/braftContent/index.jsx
new file mode 100644
index 0000000..ccf8cc9
--- /dev/null
+++ b/src/tabviews/custom/components/share/braftContent/index.jsx
@@ -0,0 +1,58 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+
+import './index.scss'
+
+class BraftContent extends Component {
+  static propTpyes = {
+    value: PropTypes.any,       // 鍐呭
+    encryption: PropTypes.any,  // 鏄惁瑙g爜
+  }
+
+  state = {
+    html: ''
+  }
+
+  UNSAFE_componentWillMount () {
+    const { encryption, value } = this.props
+    let html = value
+
+    if (encryption === 'true' && html) {
+      try {
+        html = window.decodeURIComponent(window.atob(html))
+      } catch {
+        html = value
+      }
+    }
+    
+    this.setState({html})
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (!is(fromJS(this.props), fromJS(nextProps))) {
+      const { encryption, value } = nextProps
+      let html = value
+
+      if (encryption === 'true' && html) {
+        try {
+          html = window.decodeURIComponent(window.atob(html))
+        } catch {
+          html = value
+        }
+      }
+      
+      this.setState({html})
+    }
+  }
+
+  render() {
+    const { html } = this.state
+    return (
+      <div className="braft-content" dangerouslySetInnerHTML={{ __html: html }}></div>
+    )
+  }
+}
+
+
+export default BraftContent
\ No newline at end of file
diff --git a/src/tabviews/custom/components/share/braftContent/index.scss b/src/tabviews/custom/components/share/braftContent/index.scss
new file mode 100644
index 0000000..f2f0d33
--- /dev/null
+++ b/src/tabviews/custom/components/share/braftContent/index.scss
@@ -0,0 +1,26 @@
+.braft-content {
+  .media-wrap {
+    max-width: 100%;
+  }
+  img {
+    max-width: 100%;
+  }
+  video {
+    max-width: 100%;
+    width: 100%;
+  }
+  table {
+    width: 100%;
+    border-collapse: collapse;
+    border-spacing: 0;
+    margin: 10px 0px;
+    tr:first-child {
+      background-color: #f0f0f0;
+    }
+    td, th {
+      padding: 5px 14px;
+      font-size: 16px;
+      border: 1px solid #ddd;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/share/normalTable/index.jsx b/src/tabviews/custom/components/share/normalTable/index.jsx
index e75297d..ca764a7 100644
--- a/src/tabviews/custom/components/share/normalTable/index.jsx
+++ b/src/tabviews/custom/components/share/normalTable/index.jsx
@@ -248,6 +248,10 @@
         content = md5(content)
       }
 
+      if (col.rowspan === 'true') {
+        resProps.rowSpan = record['$$' + col.field]
+      }
+
       if (col.linkThdMenu || col.linkurl) {
         content = (
           <div>
@@ -689,13 +693,13 @@
         let preItem = data[index - 1]
         rowspans.forEach((cell, i) => {
           if (i === 0) {
-            if ((item[cell] || item[cell] === 0) && preItem[cell] === item[cell]) {
+            if (preItem[cell] === item[cell]) {
               item['$' + cell] = preItem['$' + cell] + 1
             } else {
               item['$' + cell] = 1
             }
           } else {
-            if ((item[cell] || item[cell] === 0) && preItem[cell] === item[cell]) {
+            if (preItem[cell] === item[cell]) {
               item['$' + cell] = preItem['$' + cell] + 1
             } else {
               item['$' + cell] = 1
diff --git a/src/tabviews/custom/components/share/tabtransfer/index.jsx b/src/tabviews/custom/components/share/tabtransfer/index.jsx
index 7e584ff..01d2693 100644
--- a/src/tabviews/custom/components/share/tabtransfer/index.jsx
+++ b/src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -21,6 +21,8 @@
 const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
 const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
 const NormalGroup = asyncComponent(() => import('@/tabviews/custom/components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
+const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
 
 class TabTransfer extends Component {
   static propTpyes = {
@@ -58,7 +60,7 @@
       if (item.type === 'tabs' || item.type === 'group') return
 
       if (!item.setting || item.setting.interType !== 'system') return
-      if (!item.format || (item.subtype === 'propcard' && item.wrap.datatype === 'static')) return
+      if (!item.format) return
 
       if (item.dataName && (!item.pageable || (item.pageable && !item.setting.laypage)) && item.setting.onload === 'true' && item.setting.sync === 'true') {
         let param = this.getDefaultParam(item, _mainSearch)
@@ -304,6 +306,18 @@
             <NormalGroup config={item} bids={bids} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'editor') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <BraftEditor config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'code') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <SandBox config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
       } else {
         return null
       }
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index ac068c0..f6c7e40 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -11,6 +11,7 @@
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
+import UtilsDM from '@/utils/utils-datamanage.js'
 import asyncComponent from '@/utils/asyncComponent'
 import MKEmitter from '@/utils/events.js'
 import NotFount from '@/components/404'
@@ -26,6 +27,8 @@
 const MainSearch = asyncComponent(() => import('@/tabviews/zshare/topSearch'))
 const NormalTable = asyncComponent(() => import('./components/table/normal-table'))
 const NormalGroup = asyncComponent(() => import('./components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('./components/editor/braft-editor'))
+const SandBox = asyncComponent(() => import('./components/code/sand-box'))
 const SettingComponent = asyncComponent(() => import('@/tabviews/zshare/settingcomponent'))
 const PagemsgComponent = asyncComponent(() => import('@/tabviews/zshare/pageMessage'))
 
@@ -178,6 +181,8 @@
         if (!this.props.Tab) {
           this.setShortcut()
         }
+
+        this.loadData()
       })
     } else {
       this.setState({
@@ -225,6 +230,178 @@
         return false
       })
     }
+  }
+
+  loadData = () => {
+    const { config } = this.state
+
+    if (!config.interfaces || config.interfaces.length === 0) return
+
+    let inters = []
+
+    config.interfaces.forEach(item => {
+      if (item.status !== 'true') return
+
+      if (window.GLOB.systemType === 'production' && !item.proInterface) {
+        notification.warning({
+          top: 92,
+          message: `銆�${item.name}銆嬫湭璁剧疆姝e紡绯荤粺鍦板潃!`,
+          duration: 3
+        })
+        return
+      }
+
+      inters.push(item)
+    })
+    
+    if (inters.length > 0) {
+      this.loadOutResource(inters)
+    }
+  }
+
+  loadOutResource = (inters) => {
+    let setting = inters.shift()
+    let param = UtilsDM.getPrevQueryParams(setting, [], this.state.BID, this.props.menuType)
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (res.mk_ex_invoke === 'false') {
+          if (inters.length > 0) {
+            this.loadOutResource(inters)
+          }
+        } else {
+          this.customOuterRequest(res, setting, inters)
+        }
+      } else {
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    })
+  }
+
+  customOuterRequest = (result, setting, inters) => {
+    let url = ''
+
+    if (window.GLOB.systemType === 'production') {
+      url = setting.proInterface
+    } else {
+      url = setting.interface
+    }
+
+    let mkey = result.mk_api_key || ''
+
+    delete result.mk_ex_invoke
+    delete result.status
+    delete result.message
+    delete result.ErrCode
+    delete result.ErrMesg
+    delete result.mk_api_key
+
+    let param = {}
+
+    Object.keys(result).forEach(key => {
+      key = key.replace(/^mk_/ig, '')
+      param[key] = result[key]
+    })
+
+    Api.directRequest(url, setting.method, param).then(res => {
+      if (typeof(res) !== 'object' || Array.isArray(res)) {
+        let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
+
+        if (typeof(res) === 'string') {
+          error = res.replace(/'/ig, '"')
+        }
+
+        let _result = {
+          mk_api_key: mkey,
+          $ErrCode: 'E',
+          $ErrMesg: error
+        }
+
+        this.customCallbackRequest(_result, setting, inters)
+      } else {
+        res.mk_api_key = mkey
+        this.customCallbackRequest(res, setting, inters)
+      }
+    }, (e) => {
+      let _result = {
+        mk_api_key: mkey,
+        $ErrCode: 'E',
+        $ErrMesg: e && e.statusText ? e.statusText : ''
+      }
+
+      this.customCallbackRequest(_result, setting, inters)
+    })
+  }
+
+  customCallbackRequest = (result, setting, inters) => {
+    let errSql = ''
+    if (result.$ErrCode === 'E') {
+      errSql = `
+        set @ErrorCode='E'
+        set @retmsg='${result.$ErrMesg}'
+      `
+      delete result.$ErrCode
+      delete result.$ErrMesg
+    }
+
+    let lines = UtilsDM.getCallBackSql(setting, result)
+    let param = {}
+
+    if (setting.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈�
+      let sql = lines.map(item => (`
+        ${item.insert}
+        ${item.selects.join(` union all
+        `)}
+      `))
+      sql = sql.join('')
+      
+      param = UtilsDM.getCallBackQueryParams(setting, sql, errSql)
+
+      if (this.state.BID) {
+        param.BID = this.state.BID
+      }
+
+      if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+      }
+    } else {
+      param.func = 's_ex_result_back'
+      param.s_ex_result = lines.map((item, index) => ({
+        MenuID: this.props.MenuID || '',
+        MenuName: this.props.MenuName || '',
+        TableName: item.table,
+        LongText: window.btoa(window.encodeURIComponent(`${item.insert}  ${item.selects.join(` union all `)}`)),
+        Sort: index + 1
+      }))
+
+      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+        let sql = lines.map(item => (`
+          ${item.insert}
+          ${item.selects.join(` union all
+          `)}
+        `))
+        sql = sql.join('')
+        console.info(sql.replace(/\n\s{10}/ig, '\n'))
+      }
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (inters.length > 0) {
+          this.loadOutResource(inters)
+        }
+      } else {
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    })
   }
 
   filterComponent = (components, roleId, permAction, permMenus) => {
@@ -482,8 +659,12 @@
         return component
       }
 
+      if (['propcard', 'brafteditor', 'sandbox'].includes(component.subtype) && component.wrap.datatype === 'static') {
+        component.format = ''
+      }
+
       if (!component.setting) return component // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
-      if (!component.format || (component.subtype === 'propcard' && component.wrap.datatype === 'static')) return component // 娌℃湁鍔ㄦ�佹暟鎹�  鏁版嵁鏍煎紡 array 鎴� object
+      if (!component.format) return component  // 娌℃湁鍔ㄦ�佹暟鎹�  鏁版嵁鏍煎紡 array 鎴� object
       if (component.setting.interType !== 'system') { // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
         component.setting.sync = 'false'
         component.setting.laypage = component.setting.laypage === 'true'
@@ -822,6 +1003,18 @@
             <NormalGroup config={item} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'editor') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <BraftEditor config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'code') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <SandBox config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
       } else {
         return null
       }
diff --git a/src/tabviews/formtab/actionList/index.jsx b/src/tabviews/formtab/actionList/index.jsx
index 3dfab42..698fcf1 100644
--- a/src/tabviews/formtab/actionList/index.jsx
+++ b/src/tabviews/formtab/actionList/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import moment from 'moment'
 import { Button, Modal, notification, message } from 'antd'
-import Utils from '@/utils/utils.js'
+import Utils, { getSysDefaultSql } from '@/utils/utils.js'
 import options from '@/store/options.js'
 import Api from '@/api'
 import './index.scss'
@@ -121,7 +121,7 @@
 
       if (btn.sql && btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
         param.ID = Utils.getguid()
-        param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data, []) // 鏁版嵁婧�
+        param.LText = getSysDefaultSql(btn, setting, formdata, param, data, []) // 鏁版嵁婧�
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.secretkey = Utils.encrypt('', param.timestamp)
         param.LText = Utils.formatOptions(param.LText)
@@ -129,7 +129,7 @@
         _primaryId = param.ID
       } else if (btn.sql && btn.sqlType === 'insertOrUpdate') { // 绯荤粺鍑芥暟娣诲姞鎴栦慨鏀规椂
         param.ID = primaryId || Utils.getguid()
-        param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data, []) // 鏁版嵁婧�
+        param.LText = getSysDefaultSql(btn, setting, formdata, param, data, []) // 鏁版嵁婧�
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.secretkey = Utils.encrypt('', param.timestamp)
         param.LText = Utils.formatOptions(param.LText)
@@ -137,7 +137,7 @@
         _primaryId = param.ID
       } else if (btn.sql) {
         param.ID = primaryId
-        param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data, []) // 鏁版嵁婧�
+        param.LText = getSysDefaultSql(btn, setting, formdata, param, data, []) // 鏁版嵁婧�
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.secretkey = Utils.encrypt('', param.timestamp)
         param.LText = Utils.formatOptions(param.LText)
diff --git a/src/tabviews/formtab/index.jsx b/src/tabviews/formtab/index.jsx
index 798f9d0..6d7a090 100644
--- a/src/tabviews/formtab/index.jsx
+++ b/src/tabviews/formtab/index.jsx
@@ -673,16 +673,15 @@
                         {_tab.label}
                       </span>
                     } key={`${index}`}>
-                      {_tab.type === 'SubTable' ?
-                        <SubTable
-                          Tab={_tab}
-                          MenuID={_tab.linkTab}
-                          SupMenuID={this.props.MenuID}
-                          ContainerId={this.state.ContainerId}
-                          BID={this.state.BIDs[_tab.supMenu] || ''}
-                          BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
-                          handleTableId={this.handleTableId}
-                        /> : null}
+                      <SubTable
+                        Tab={_tab}
+                        MenuID={_tab.linkTab}
+                        SupMenuID={this.props.MenuID}
+                        ContainerId={this.state.ContainerId}
+                        BID={this.state.BIDs[_tab.supMenu] || ''}
+                        BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
+                        handleTableId={this.handleTableId}
+                      />
                     </TabPane>
                   )
                 })}
diff --git a/src/tabviews/scriptmanage/actionList/index.jsx b/src/tabviews/scriptmanage/actionList/index.jsx
index 677751a..a17cc72 100644
--- a/src/tabviews/scriptmanage/actionList/index.jsx
+++ b/src/tabviews/scriptmanage/actionList/index.jsx
@@ -33,10 +33,6 @@
     configMap: {}
   }
 
-  refreshdata = (item, type) => {
-    this.props.refreshdata(item, type)
-  }
-  
   /**
    * @description 瑙﹀彂鎸夐挳鎿嶄綔
    */
@@ -124,8 +120,6 @@
       LText: values.LongParam
     }
 
-    param.LText = window.btoa(window.encodeURIComponent(JSON.stringify(param.LText)))
-
     if (btn.sqlType === 'delete') {
       param.LText = window.GLOB.appkey || ''
       param.Remark = ''
@@ -179,7 +173,9 @@
       })
     }
 
-    this.refreshdata(btn, 'success')
+    if (btn.execSuccess !== 'never') {
+      this.props.refreshdata()
+    }
   }
 
   /**
@@ -209,8 +205,6 @@
     } else if (res.ErrCode === 'NM') {
       message.error(res.message || res.ErrMesg)
     }
-    
-    this.refreshdata(btn, 'error')
   }
 
 
diff --git a/src/tabviews/scriptmanage/actionList/index.scss b/src/tabviews/scriptmanage/actionList/index.scss
index 3ed2c30..8f7def9 100644
--- a/src/tabviews/scriptmanage/actionList/index.scss
+++ b/src/tabviews/scriptmanage/actionList/index.scss
@@ -15,30 +15,3 @@
     top: calc(50vh - 70px);
   }
 }
-// 璁剧疆妯℃�佹鏍峰紡锛岃瀹氭渶澶ф渶灏忛珮搴︼紝閲嶇疆婊氬姩鏉�
-.action-modal {
-  .ant-modal {
-    max-width: 95vw;
-  }
-  .ant-modal-body {
-    max-height: calc(100vh - 235px);
-    min-height: 150px;
-    overflow-y: auto;
-    padding-bottom: 35px;
-  }
-  .ant-modal-body::-webkit-scrollbar {
-    width: 10px;
-    height: 10px;
-  }
-  .ant-modal-body::-webkit-scrollbar-thumb {
-    border-radius: 5px;
-    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
-    background: rgba(0, 0, 0, 0.13);
-  }
-  .ant-modal-body::-webkit-scrollbar-track {
-    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
-    border-radius: 3px;
-    border: 1px solid rgba(0, 0, 0, 0.07);
-    background: rgba(0, 0, 0, 0);
-  }
-}
\ No newline at end of file
diff --git a/src/tabviews/scriptmanage/config.jsx b/src/tabviews/scriptmanage/config.jsx
index 7a06701..5402b14 100644
--- a/src/tabviews/scriptmanage/config.jsx
+++ b/src/tabviews/scriptmanage/config.jsx
@@ -63,7 +63,7 @@
       {"label":"鍑芥暟","field":"func","type":"text","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":50,"regular":"funcname","supField":"","blacklist":[],"uuid":"1587006164634l397q15t49u2pfq02f5"},
       {"label":"鎺掑簭","field":"Sort","type":"number","initval":0,"decimal":0,"min":"","max":"","readonly":"false","hidden":"false","readin":"true","supField":"","blacklist":[],"uuid":"15870101796149403f2pqfpviuo415m2"},
       {"label":"鎻忚堪","field":"Remark","type":"textarea","initval":"","readonly":"false","required":"false","hidden":"false","readin":"true","fieldlength":512,"maxRows":6,"supField":"","blacklist":[],"uuid":"1587006199263k8hm45cmtomgu6hd881"},
-      {"label":"鑴氭湰","field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":8000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
+      {"label":"鑴氭湰","field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","encryption":"true","hidden":"false","readin":"true","fieldlength":12000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
     ]
   },
   '1587007258155ut4nbggg4r66t9uhut2': {
@@ -84,7 +84,7 @@
       {"label":"鍑芥暟","field":"func","type":"text","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":50,"regular":"funcname","supField":"","blacklist":[],"uuid":"1587006164634l397q15t49u2pfq02f5"},
       {"label":"鎺掑簭","field":"Sort","type":"number","initval":0,"decimal":0,"min":"","max":"","readonly":"false","hidden":"false","readin":"true","supField":"","blacklist":[],"uuid":"1587010196675i9m6ie3tv9kg2rhgfi0"},
       {"label":"鎻忚堪","field":"Remark","type":"textarea","initval":"","readonly":"false","required":"false","hidden":"false","readin":"true","fieldlength":512,"maxRows":6,"supField":"","blacklist":[],"uuid":"1587006199263k8hm45cmtomgu6hd881"},
-      {"label":"鑴氭湰","field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":8000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
+      {"label":"鑴氭湰","field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","encryption":"true","hidden":"false","readin":"true","fieldlength":12000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
     ]
   }
 }
diff --git a/src/tabviews/scriptmanage/index.jsx b/src/tabviews/scriptmanage/index.jsx
index 64395fb..ed0910c 100644
--- a/src/tabviews/scriptmanage/index.jsx
+++ b/src/tabviews/scriptmanage/index.jsx
@@ -134,7 +134,7 @@
     if (result.status) {
       this.setState({
         data: result.data.map((item, index) => {
-          item.LongParam = Utils.UnformatOptions(item.LongParam)
+          // item.LongParam = this.UnformatOptions(item.LongParam)
           item.key = index
           return item
         }),
@@ -152,6 +152,75 @@
       })
     }
   }
+
+  // UnformatOptions = (value) => {
+  //   if (!value) return ''
+  //   let salt = 'minKe' // 鐩愬��
+  //   let _value = ''
+  //   const formatKeys = [
+  //     { key: 'select', value: ' msltk ' },
+  //     { key: 'from', value: ' mfrmk ' },
+  //     { key: 'where', value: ' mwhrk ' },
+  //     { key: 'order by', value: ' modbk ' },
+  //     { key: 'asc', value: ' modack ' },
+  //     { key: 'desc', value: ' moddesk ' },
+  //     { key: 'top', value: ' mtpk ' },
+  //     { key: 'like', value: ' mlkk ' },
+  //     { key: 'not like', value: ' mnlkk ' },
+  //     { key: 'between', value: ' mbtnk ' },
+  //     { key: 'and', value: ' madk ' },
+  //     { key: 'insert', value: ' mistk ' },
+  //     { key: 'into', value: ' mitk ' },
+  //     { key: 'update', value: ' muptk ' },
+  //     { key: 'delete', value: ' mdelk ' },
+  //     { key: 'begin', value: ' mbgink ' },
+  //     { key: 'end', value: ' medk ' },
+  //     { key: 'if', value: ' mefk ' },
+  //     { key: 'while', value: ' mwilk ' },
+  //     { key: 'create', value: ' mcrtk ' },
+  //     { key: 'alter', value: ' matek ' },
+  //     { key: 'len', value: ' mlnk ' },
+  //     { key: 'left', value: ' mlftk ' },
+  //     { key: 'right', value: ' mritk ' },
+  //     { key: 'union', value: ' munok ' },
+  //     { key: 'varchar', value: ' mvcrk ' },
+  //     { key: 'getdate', value: ' mgtdtk ' },
+  //     { key: 'TRY', value: ' mtryonek ' },
+  //     { key: 'TRAN', value: ' mtrnk ' },
+  //     { key: 'goto', value: ' mgtk ' },
+  //     { key: 'set', value: ' mstk ' },
+  //     { key: 'ROLLBACK', value: ' mrlbkk ' }
+  //   ]
+
+  //   try {
+  //     try {
+  //       _value = JSON.parse(window.decodeURIComponent(window.atob(value)))
+  //     } catch {
+  //       _value = ''
+  //     }
+
+  //     if (!_value) {
+  //       _value = window.atob(value)
+  //       _value = _value.replace(salt, '')
+  //       _value = window.decodeURIComponent(window.atob(_value))
+  
+  //       _value = _value.replace(/\smpercent\s/g, '%')
+  
+  //       formatKeys.forEach(item => {
+  //         let reg = new RegExp(item.value, 'g')
+  //         _value = _value.replace(reg, ' ' + item.key + ' ')
+  //       })
+  
+  //       _value = _value.replace(/\s\n\s/ig, '\n')
+  //       _value = _value.replace(/(^\s+|\s+$)/ig, '')
+  //     }
+  //   } catch {
+  //     console.warn('UnFormat Failure')
+  //     _value = ''
+  //   }
+
+  //   return _value
+  // }
 
   /**
    * @description 鑾峰彇绯荤粺瀛樺偍杩囩▼ sPC_Get_TableData 鐨勫弬鏁�
@@ -255,21 +324,6 @@
   }
 
   /**
-   * @description 鎸夐挳鎿嶄綔瀹屾垚鍚庯紙鎴愬姛鎴栧け璐ワ級锛岄〉闈㈠埛鏂帮紝閲嶇疆椤电爜鍙婇�夋嫨椤�
-   */
-  refreshbyaction = (btn, type) => {
-    if (btn.execSuccess === 'grid' && type === 'success') {
-      this.reloadtable()
-    } else if (btn.execError === 'grid' && type === 'error') {
-      this.reloadtable()
-    } else if (btn.execSuccess === 'view' && type === 'success') {
-      this.reloadview()
-    } else if (btn.execError === 'view' && type === 'error') {
-      this.reloadview()
-    }
-  }
-
-  /**
    * @description 琛ㄦ牸閫夋嫨椤瑰垏鎹�
    */
   changeSelectedData = (selectedData) => {
@@ -332,7 +386,7 @@
           MenuID={this.props.MenuID}
           selectedData={selectedData}
           ContainerId={this.state.ContainerId}
-          refreshdata={this.refreshbyaction}
+          refreshdata={this.reloadtable}
         />
         <div className="main-table-box">
           {this.state.data && this.state.data.length > 0 ?
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index 6e798b6..07dbc4c 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -8,8 +8,9 @@
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
+import options from '@/store/options.js'
 import UtilsDM from '@/utils/utils-datamanage.js'
-import UtilsUpdate from '@/utils/utils-update.js'
+import { updateSubTable } from '@/utils/utils-update.js'
 import { modifyTabview } from '@/store/action'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
@@ -61,29 +62,27 @@
     statFields: [],       // 鍚堣瀛楁
     statFValue: [],       // 鍚堣鍊�
     absFields: [],        // 缁濆鍊煎瓧娈�
+    loadCustomApi: true,  // 鍔犺浇澶栭儴璧勬簮
+    hasReqFields: false
   }
 
   /**
    * @description 涓婄骇鑿滃崟id鍙樺寲鏃讹紝鍒锋柊鏁版嵁
    */
   UNSAFE_componentWillReceiveProps(nextProps) {
-    if (this.state.config && this.props.Tab.supMenu && !is(fromJS(this.props.BID), fromJS(nextProps.BID))) {
+    const { config, setting } = this.state
+
+    if (config && setting && this.props.Tab.supMenu && !is(fromJS(this.props.BID), fromJS(nextProps.BID))) {
       MKEmitter.emit('resetTable', this.props.MenuID + this.props.Tab.uuid) // 鍒楄〃閲嶇疆
       this.setState({
         pageIndex: 1
       }, () => {
-        if (this.state.setting) {
-          this.loadmaindata(nextProps.BID, 'refresh')
-          this.getStatFieldsValue(nextProps.BID, 'refresh')
-        }
+        this.loadData()
       })
-    } else if (!this.props.Tab.supMenu && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
-      if (this.state.setting) {
-        this.setState({}, () => {
-          this.loadmaindata()
-          this.getStatFieldsValue()
-        })
-      }
+    } else if (setting && !this.props.Tab.supMenu && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      this.setState({}, () => {
+        this.loadData()
+      })
     }
   }
 
@@ -103,6 +102,7 @@
 
       try { // 閰嶇疆淇℃伅瑙f瀽
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
+        config.setting.MenuName = Tab.label
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -135,7 +135,7 @@
       let absFields = []     // 缁濆鍊煎瓧娈�
 
       // 鐗堟湰鍏煎
-      config = UtilsUpdate.updateSubTable(config)
+      config = updateSubTable(config)
 
       // 鏉冮檺杩囨护
       if (this.props.menuType !== 'HS') {
@@ -262,8 +262,11 @@
       }
 
       let valid = true // 鎼滅储鏉′欢蹇呭~楠岃瘉
+      let hasReqFields = false
       config.search.forEach(field => {
-        if (field.required === 'true' && !field.initval) {
+        if (field.required !== 'true') return
+        hasReqFields = true
+        if (!field.initval) {
           valid = false
         }
       })
@@ -274,7 +277,7 @@
       config.setting.execute = config.setting.default !== 'false'     // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
       config.setting.customScript = ''                                // 鑷畾涔夎剼鏈�
 
-      if (config.setting.interType === 'system') {
+      if (config.setting.interType === 'system' || (config.setting.interType === 'custom' && config.setting.requestMode === 'system')) {
         if (config.setting.scripts && config.setting.scripts.length > 0) {
           let _customScript = ''
           config.setting.scripts.forEach(item => {
@@ -317,11 +320,11 @@
         actions: _actions,
         columns: _columns,
         arr_field: _arrField.join(','),
-        search: Utils.initMainSearch(config.search) // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        search: Utils.initMainSearch(config.search), // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        hasReqFields
       }, () => {
         if (config.setting.onload !== 'false' && (!Tab.supMenu || BID || Tab.isTreeNode) && valid) { // 鍒濆鍖栧彲鍔犺浇
-          this.loadmaindata()
-          this.getStatFieldsValue()
+          this.loadData()
         }
       })
     } else {
@@ -338,24 +341,23 @@
       })
     }
   }
+  
+  loadData = () => {
+    const { mainSearch, BID } = this.props
+    const { setting, search, loadCustomApi, hasReqFields } = this.state
 
-  /**
-   * @description 瀛愯〃鏁版嵁鍔犺浇
-   */
-  async loadmaindata (bid, type) {
-    const { mainSearch } = this.props
-    const { setting, arr_field, search, orderBy, pageIndex, pageSize, absFields } = this.state
-
-    let _BID = this.props.BID
     let searches = fromJS(search).toJS()
     if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
       searches = [...mainSearch, ...searches]
     }
 
-    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
-    let prex = this.props.Tab && this.props.Tab.label ? this.props.Tab.label + '-' : ''
-
+    let requireFields = []
+    if (hasReqFields) {
+      requireFields = searches.filter(item => item.required && (!item.value || item.value.length === 0))
+    }
+    
     if (requireFields.length > 0) {
+      let prex = this.props.Tab && this.props.Tab.label ? this.props.Tab.label + '-' : ''
       let labels = requireFields.map(item => item.label)
       labels = Array.from(new Set(labels))
 
@@ -365,34 +367,235 @@
         duration: 3
       })
       return
+    } else if (this.props.Tab.supMenu && !BID) { // 涓昏〃ID涓嶅瓨鍦ㄦ椂锛屼笉鏌ヨ瀛愯〃
+      this.setState({
+        data: [],
+        selectedData: [],
+        statFValue: [],
+        total: 0
+      })
+      this.handleTableId()
+      return
+    } else if (window.GLOB.systemType === 'production' && setting.interType === 'custom' && !setting.proInterface) {
+      notification.warning({
+        top: 92,
+        message: '鏈缃寮忕郴缁熷湴鍧�!',
+        duration: 3
+      })
+      return
     }
 
-    if (type === 'refresh') {
-      if (!bid) { // 涓昏〃ID涓嶅瓨鍦ㄦ椂锛屼笉鏌ヨ瀛愯〃
-        this.setState({
-          data: [],
-          total: 0
-        })
-        return
+    this.setState({
+      selectedData: []
+    })
+    this.handleTableId()
+
+    if (setting.interType === 'custom' && loadCustomApi) {
+      if (setting.execTime === 'once') {
+        this.setState({loadCustomApi: false})
+      }
+
+      this.loadOutResource(searches)
+      if (setting.execType === 'async') {
+        this.loadmaindata()
+      }
+    } else {
+      this.loadmaindata()
+    }
+  }
+
+  loadOutResource = (searches) => {
+    const { BID } = this.props
+    const { setting } = this.state
+
+    let param = UtilsDM.getPrevQueryParams(setting, searches, BID, this.props.menuType)
+
+    if (setting.execType === 'sync') {
+      this.setState({
+        loading: true
+      })
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (res.mk_ex_invoke === 'false') {
+          this.loadmaindata()
+        } else {
+          this.customOuterRequest(res)
+        }
       } else {
-        _BID = bid
+        this.setState({
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
+  customOuterRequest = (result) => {
+    const { setting } = this.state
+    let url = ''
+
+    if (window.GLOB.systemType === 'production') {
+      url = setting.proInterface
+    } else {
+      url = setting.interface
+    }
+
+    let mkey = result.mk_api_key || ''
+
+    delete result.mk_ex_invoke
+    delete result.status
+    delete result.message
+    delete result.ErrCode
+    delete result.ErrMesg
+    delete result.mk_api_key
+
+    let param = {}
+
+    Object.keys(result).forEach(key => {
+      key = key.replace(/^mk_/ig, '')
+      param[key] = result[key]
+    })
+
+    Api.directRequest(url, setting.method, param).then(res => {
+      if (typeof(res) !== 'object' || Array.isArray(res)) {
+        let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
+
+        if (typeof(res) === 'string') {
+          error = res.replace(/'/ig, '"')
+        }
+
+        let _result = {
+          mk_api_key: mkey,
+          $ErrCode: 'E',
+          $ErrMesg: error
+        }
+
+        this.customCallbackRequest(_result)
+      } else {
+        res.mk_api_key = mkey
+        this.customCallbackRequest(res)
+      }
+    }, (e) => {
+      let _result = {
+        mk_api_key: mkey,
+        $ErrCode: 'E',
+        $ErrMesg: e && e.statusText ? e.statusText : ''
+      }
+
+      this.customCallbackRequest(_result)
+    })
+  }
+
+  customCallbackRequest = (result) => {
+    const { setting } = this.state
+    let errSql = ''
+    if (result.$ErrCode === 'E') {
+      errSql = `
+        set @ErrorCode='E'
+        set @retmsg='${result.$ErrMesg}'
+      `
+      delete result.$ErrCode
+      delete result.$ErrMesg
+    }
+
+    let lines = UtilsDM.getCallBackSql(setting, result)
+    let param = {}
+
+    if (setting.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈�
+      let sql = lines.map(item => (`
+        ${item.insert}
+        ${item.selects.join(` union all
+        `)}
+      `))
+      sql = sql.join('')
+      
+      param = UtilsDM.getCallBackQueryParams(setting, sql, errSql)
+
+      if (this.props.BID) {
+        param.BID = this.props.BID
+      }
+
+      if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+      }
+    } else {
+      param.func = 's_ex_result_back'
+      param.s_ex_result = lines.map((item, index) => ({
+        MenuID: this.props.MenuID,
+        MenuName: this.props.Tab.label,
+        TableName: item.table,
+        LongText: window.btoa(window.encodeURIComponent(`${item.insert}  ${item.selects.join(` union all `)}`)),
+        Sort: index + 1
+      }))
+
+      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+        let sql = lines.map(item => (`
+          ${item.insert}
+          ${item.selects.join(` union all
+          `)}
+        `))
+        sql = sql.join('')
+        console.info(sql.replace(/\n\s{10}/ig, '\n'))
       }
     }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        this.loadmaindata()
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
+  /**
+   * @description 瀛愯〃鏁版嵁鍔犺浇
+   */
+  async loadmaindata () {
+    const { mainSearch, BID } = this.props
+    const { setting, arr_field, search, orderBy, pageIndex, pageSize, absFields } = this.state
+
+    let searches = fromJS(search).toJS()
+    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      searches = [...mainSearch, ...searches]
+    }
+
     this.setState({
-      selectedData: [],
       loading: true
     })
 
     let _orderBy = orderBy || setting.order
-    let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, _BID, this.props.menuType)
-
-    this.handleTableId()
+    let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, this.props.menuType)
 
     if (param.func === 'sPC_Get_TableData') {
       param.menuname = this.props.Tab.label || ''
     }
 
     let result = await Api.genericInterface(param)
+
+    this.getStatFieldsValue(searches)
+
     if (result.status) {
       this.setState({
         data: result.data.map((item, index) => {
@@ -406,7 +609,7 @@
           }
           item.key = index
           item.$$uuid = item[setting.primaryKey] || ''
-          item.$$BID = _BID || ''
+          item.$$BID = BID || ''
 
           return item
         }),
@@ -415,6 +618,7 @@
         loading: false
       })
     } else {
+      let prex = this.props.Tab && this.props.Tab.label ? this.props.Tab.label + '-' : ''
       this.setState({
         loading: false
       })
@@ -501,36 +705,14 @@
   /**
    * @description 鑾峰彇鍚堣瀛楁鍊�
    */
-  getStatFieldsValue = (bid, type) => {
-    const { mainSearch } = this.props
-    const { setting, search, orderBy, statFields } = this.state
+  getStatFieldsValue = (searches) => {
+    const { BID } = this.props
+    const { setting, orderBy, statFields } = this.state
 
-    let _BID = this.props.BID
-    let searches = fromJS(search).toJS()
-    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
-      searches = [...mainSearch, ...searches]
-    }
-
-    if (statFields.length === 0 || setting.interType !== 'system' || !setting.dataresource) return
-
-    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
-    if (requireFields.length > 0) {
-      return
-    }
-
-    if (type === 'refresh') {
-      if (!bid) { // 涓昏〃ID涓嶅瓨鍦ㄦ椂锛屼笉鏌ヨ瀛愯〃鍚堣鍊�
-        this.setState({
-          statFValue: []
-        })
-        return
-      } else {
-        _BID = bid
-      }
-    }
+    if (statFields.length === 0 || !(setting.interType === 'system' || (setting.interType === 'custom' && setting.requestMode === 'system')) || !setting.dataresource) return
 
     let _orderBy = orderBy || setting.order
-    let param = UtilsDM.getStatQueryDataParams(setting, statFields, searches, _orderBy, _BID, this.props.menuType)
+    let param = UtilsDM.getStatQueryDataParams(setting, statFields, searches, _orderBy, BID, this.props.menuType)
 
     if (param.func === 'sPC_Get_TableData') {
       param.menuname = this.props.Tab.label || ''
@@ -579,8 +761,7 @@
       pageIndex: 1,
       search: searches,
     }, () => {
-      this.loadmaindata()
-      this.getStatFieldsValue()
+      this.loadData()
     })
   }
 
@@ -601,7 +782,7 @@
       pageSize: pagination.pageSize,
       orderBy: (sorter.field && sorter.order) ? `${sorter.field} ${sorter.order}` : ''
     }, () => {
-      this.loadmaindata()
+      this.loadData()
     })
   }
 
@@ -614,13 +795,11 @@
       this.setState({
         pageIndex: 1
       }, () => {
-        this.loadmaindata()
-        this.getStatFieldsValue()
+        this.loadData()
       })
     } else {
       MKEmitter.emit('resetTable', this.props.MenuID + this.props.Tab.uuid, 'false') // 鍒楄〃閲嶇疆
-      this.loadmaindata()
-      this.getStatFieldsValue()
+      this.loadData()
     }
   }
 
diff --git a/src/tabviews/subtable/index.scss b/src/tabviews/subtable/index.scss
index 76f5e85..9f855e4 100644
--- a/src/tabviews/subtable/index.scss
+++ b/src/tabviews/subtable/index.scss
@@ -96,7 +96,7 @@
       position: absolute;
       right: 5px;
       top: -22px;
-      z-index: 10;
+      z-index: 1;
     }
   }
   .ant-table-fixed-left, .ant-table-fixed-right {
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
index f1a937b..58ecb38 100644
--- a/src/tabviews/subtabtable/index.jsx
+++ b/src/tabviews/subtabtable/index.jsx
@@ -8,8 +8,9 @@
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
+import options from '@/store/options.js'
 import UtilsDM from '@/utils/utils-datamanage.js'
-import UtilsUpdate from '@/utils/utils-update.js'
+import { updateSubTable } from '@/utils/utils-update.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import NotFount from '@/components/404'
@@ -57,6 +58,8 @@
     statFields: [],       // 鍚堣瀛楁
     statFValue: [],       // 鍚堣鍊�
     absFields: [],        // 缁濆鍊煎瓧娈�
+    loadCustomApi: true,  // 鍔犺浇澶栭儴璧勬簮
+    hasReqFields: false
   }
 
   /**
@@ -75,6 +78,7 @@
 
       try { // 閰嶇疆淇℃伅瑙f瀽
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
+        config.setting.MenuName = Tab.label
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -107,7 +111,7 @@
       let absFields = []     // 缁濆鍊煎瓧娈�
 
       // 鐗堟湰鍏煎
-      config = UtilsUpdate.updateSubTable(config)
+      config = updateSubTable(config)
 
       // 涓嶆敮鎸乫uncbutton銆乸opview 绫诲瀷鎸夐挳
       let labels = []
@@ -232,8 +236,11 @@
       }
 
       let valid = true // 鎼滅储鏉′欢蹇呭~楠岃瘉
+      let hasReqFields = false
       config.search.forEach(field => {
-        if (field.required === 'true' && !field.initval) {
+        if (field.required !== 'true') return
+        hasReqFields = true
+        if (!field.initval) {
           valid = false
         }
       })
@@ -244,7 +251,7 @@
       config.setting.execute = config.setting.default !== 'false'     // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
       config.setting.customScript = ''                                // 鑷畾涔夎剼鏈�
 
-      if (config.setting.interType === 'system') {
+      if (config.setting.interType === 'system' || (config.setting.interType === 'custom' && config.setting.requestMode === 'system')) {
         if (config.setting.scripts && config.setting.scripts.length > 0) {
           let _customScript = ''
           config.setting.scripts.forEach(item => {
@@ -287,11 +294,11 @@
         actions: _actions,
         columns: _columns,
         arr_field: _arrField.join(','),
-        search: Utils.initMainSearch(config.search) // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        search: Utils.initMainSearch(config.search), // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        hasReqFields
       }, () => {
         if (config.setting.onload !== 'false' && valid) { // 鍒濆鍖栧彲鍔犺浇
-          this.loadmaindata()
-          this.getStatFieldsValue()
+          this.loadData()
         }
       })
     } else {
@@ -307,6 +314,223 @@
     }
   }
 
+  loadData = () => {
+    const { mainSearch } = this.props
+    const { setting, search, hasReqFields, loadCustomApi } = this.state
+
+    let searches = fromJS(search).toJS()
+    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      searches = [...mainSearch, ...searches]
+    }
+
+    let requireFields = []
+
+    if (hasReqFields) {
+      requireFields = searches.filter(item => item.required && (!item.value || item.value.length === 0))
+    }
+
+    if (requireFields.length > 0) {
+      let labels = requireFields.map(item => item.label)
+      labels = Array.from(new Set(labels))
+
+      notification.warning({
+        top: 92,
+        message: this.state.dict['form.required.input'] + labels.join('銆�') + ' !',
+        duration: 3
+      })
+      return
+    } else if (window.GLOB.systemType === 'production' && setting.interType === 'custom' && !setting.proInterface) {
+      notification.warning({
+        top: 92,
+        message: '鏈缃寮忕郴缁熷湴鍧�!',
+        duration: 3
+      })
+      return
+    }
+
+    this.setState({
+      selectedData: []
+    })
+
+    if (setting.interType === 'custom' && loadCustomApi) {
+      if (setting.execTime === 'once') {
+        this.setState({loadCustomApi: false})
+      }
+
+      this.loadOutResource(searches)
+      if (setting.execType === 'async') {
+        this.loadmaindata()
+      }
+    } else {
+      this.loadmaindata()
+    }
+  }
+
+  loadOutResource = (searches) => {
+    const { setting } = this.state
+
+    let param = UtilsDM.getPrevQueryParams(setting, searches, this.props.BID, this.props.menuType)
+
+    if (setting.execType === 'sync') {
+      this.setState({
+        loading: true
+      })
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (res.mk_ex_invoke === 'false') {
+          this.loadmaindata()
+        } else {
+          this.customOuterRequest(res)
+        }
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
+  customOuterRequest = (result) => {
+    const { setting } = this.state
+    let url = ''
+
+    if (window.GLOB.systemType === 'production') {
+      url = setting.proInterface
+    } else {
+      url = setting.interface
+    }
+
+    let mkey = result.mk_api_key || ''
+
+    delete result.mk_ex_invoke
+    delete result.status
+    delete result.message
+    delete result.ErrCode
+    delete result.ErrMesg
+    delete result.mk_api_key
+
+    let param = {}
+
+    Object.keys(result).forEach(key => {
+      key = key.replace(/^mk_/ig, '')
+      param[key] = result[key]
+    })
+
+    Api.directRequest(url, setting.method, param).then(res => {
+      if (typeof(res) !== 'object' || Array.isArray(res)) {
+        let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
+
+        if (typeof(res) === 'string') {
+          error = res.replace(/'/ig, '"')
+        }
+
+        let _result = {
+          mk_api_key: mkey,
+          $ErrCode: 'E',
+          $ErrMesg: error
+        }
+
+        this.customCallbackRequest(_result)
+      } else {
+        res.mk_api_key = mkey
+        this.customCallbackRequest(res)
+      }
+    }, (e) => {
+      let _result = {
+        mk_api_key: mkey,
+        $ErrCode: 'E',
+        $ErrMesg: e && e.statusText ? e.statusText : ''
+      }
+
+      this.customCallbackRequest(_result)
+    })
+  }
+
+  customCallbackRequest = (result) => {
+    const { setting } = this.state
+    let errSql = ''
+    if (result.$ErrCode === 'E') {
+      errSql = `
+        set @ErrorCode='E'
+        set @retmsg='${result.$ErrMesg}'
+      `
+      delete result.$ErrCode
+      delete result.$ErrMesg
+    }
+
+    let lines = UtilsDM.getCallBackSql(setting, result)
+    let param = {}
+
+    if (setting.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈�
+      let sql = lines.map(item => (`
+        ${item.insert}
+        ${item.selects.join(` union all
+        `)}
+      `))
+      sql = sql.join('')
+      
+      param = UtilsDM.getCallBackQueryParams(setting, sql, errSql)
+
+      if (this.state.BID) {
+        param.BID = this.state.BID
+      }
+
+      if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+      }
+    } else {
+      let name = this.props.Tab ? (this.props.Tab.label || '') : ''
+      param.func = 's_ex_result_back'
+      param.s_ex_result = lines.map((item, index) => ({
+        MenuID: this.props.MenuID,
+        MenuName: name,
+        TableName: item.table,
+        LongText: window.btoa(window.encodeURIComponent(`${item.insert}  ${item.selects.join(` union all `)}`)),
+        Sort: index + 1
+      }))
+
+      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+        let sql = lines.map(item => (`
+          ${item.insert}
+          ${item.selects.join(` union all
+          `)}
+        `))
+        sql = sql.join('')
+        console.info(sql.replace(/\n\s{10}/ig, '\n'))
+      }
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        this.loadmaindata()
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
   /**
    * @description 瀛愯〃鏁版嵁鍔犺浇
    */
@@ -319,22 +543,7 @@
       searches = [...mainSearch, ...searches]
     }
 
-    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
-
-    if (requireFields.length > 0) {
-      let labels = requireFields.map(item => item.label)
-      labels = Array.from(new Set(labels))
-
-      notification.warning({
-        top: 92,
-        message: this.state.dict['form.required.input'] + labels.join('銆�') + ' !',
-        duration: 3
-      })
-      return
-    }
-
     this.setState({
-      selectedData: [],
       loading: true
     })
 
@@ -346,6 +555,9 @@
     }
 
     let result = await Api.genericInterface(param)
+
+    this.getStatFieldsValue(searches)
+
     if (result.status) {
       this.setState({
         data: result.data.map((item, index) => {
@@ -381,21 +593,11 @@
   /**
    * @description 鑾峰彇鍚堣瀛楁鍊�
    */
-  getStatFieldsValue = () => {
-    const { mainSearch, BID } = this.props
-    const { setting, search, orderBy, statFields } = this.state
+  getStatFieldsValue = (searches) => {
+    const { BID } = this.props
+    const { setting, orderBy, statFields } = this.state
 
-    let searches = fromJS(search).toJS()
-    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
-      searches = [...mainSearch, ...searches]
-    }
-
-    if (statFields.length === 0 || setting.interType !== 'system' || !setting.dataresource) return
-
-    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
-    if (requireFields.length > 0) {
-      return
-    }
+    if (statFields.length === 0 || !(setting.interType === 'system' || (setting.interType === 'custom' && setting.requestMode === 'system')) || !setting.dataresource) return
 
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getStatQueryDataParams(setting, statFields, searches, _orderBy, BID, this.props.menuType)
@@ -447,8 +649,7 @@
       pageIndex: 1,
       search: searches
     }, () => {
-      this.loadmaindata()
-      this.getStatFieldsValue()
+      this.loadData()
     })
   }
 
@@ -469,7 +670,7 @@
       pageSize: pagination.pageSize,
       orderBy: (sorter.field && sorter.order) ? `${sorter.field} ${sorter.order}` : ''
     }, () => {
-      this.loadmaindata()
+      this.loadData()
     })
   }
 
@@ -482,13 +683,11 @@
       this.setState({
         pageIndex: 1
       }, () => {
-        this.loadmaindata()
-        this.getStatFieldsValue()
+        this.loadData()
       })
     } else {
       MKEmitter.emit('resetTable', this.props.MenuID + this.props.Tab.uuid, 'false') // 鍒楄〃閲嶇疆
-      this.loadmaindata()
-      this.getStatFieldsValue()
+      this.loadData()
     }
     
   }
diff --git a/src/tabviews/verupmanage/actionList/index.jsx b/src/tabviews/verupmanage/actionList/index.jsx
index 0a20a3d..5671678 100644
--- a/src/tabviews/verupmanage/actionList/index.jsx
+++ b/src/tabviews/verupmanage/actionList/index.jsx
@@ -3,7 +3,7 @@
 import moment from 'moment'
 import { Button, Modal, notification, message } from 'antd'
 import MutilForm from '@/tabviews/zshare/mutilform'
-import Utils from '@/utils/utils.js'
+import Utils, { getSysDefaultSql } from '@/utils/utils.js'
 import options from '@/store/options.js'
 import Api from '@/api'
 import './index.scss'
@@ -171,7 +171,7 @@
             }
           } else if (btn.sql) {
             param.ID = primaryId
-            param.LText = Utils.getSysDefaultSql(btn, setting, '', param, data[0], columns) // 鏁版嵁婧�
+            param.LText = getSysDefaultSql(btn, setting, '', param, data[0], columns) // 鏁版嵁婧�
             
             param.exec_type = 'y' // 鍚庡彴瑙g爜
             param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -199,7 +199,7 @@
               }
             }
             param.ID = primaryId || Utils.getguid()
-            param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data[0], columns) // 鏁版嵁婧�
+            param.LText = getSysDefaultSql(btn, setting, formdata, param, data[0], columns) // 鏁版嵁婧�
             
             param.exec_type = 'y' // 鍚庡彴瑙g爜
             param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -207,7 +207,7 @@
             param.LText = Utils.formatOptions(param.LText)
           } else if (btn.sql) {
             param.ID = primaryId
-            param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data[0], columns) // 鏁版嵁婧�
+            param.LText = getSysDefaultSql(btn, setting, formdata, param, data[0], columns) // 鏁版嵁婧�
             
             param.exec_type = 'y' // 鍚庡彴瑙g爜
             param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -253,7 +253,7 @@
               }
             } else if (btn.sql) {
               param.ID = primaryId
-              param.LText = Utils.getSysDefaultSql(btn, setting, '', param, cell, columns) // 鏁版嵁婧�
+              param.LText = getSysDefaultSql(btn, setting, '', param, cell, columns) // 鏁版嵁婧�
 
               param.exec_type = 'y' // 鍚庡彴瑙g爜
               param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -277,7 +277,7 @@
               }
             } else if (btn.sql && btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
               param.ID = _formPrimaryId || Utils.getguid()
-              param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, cell, columns) // 鏁版嵁婧�
+              param.LText = getSysDefaultSql(btn, setting, formdata, param, cell, columns) // 鏁版嵁婧�
 
               param.exec_type = 'y' // 鍚庡彴瑙g爜
               param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -294,7 +294,7 @@
               }
 
               param.ID = primaryId
-              param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, cell, columns) // 鏁版嵁婧�
+              param.LText = getSysDefaultSql(btn, setting, formdata, param, cell, columns) // 鏁版嵁婧�
 
               param.exec_type = 'y' // 鍚庡彴瑙g爜
               param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
diff --git a/src/tabviews/verupmanage/actionList/index.scss b/src/tabviews/verupmanage/actionList/index.scss
index e1a1938..9751b46 100644
--- a/src/tabviews/verupmanage/actionList/index.scss
+++ b/src/tabviews/verupmanage/actionList/index.scss
@@ -15,30 +15,3 @@
     top: calc(50vh - 70px);
   }
 }
-// 璁剧疆妯℃�佹鏍峰紡锛岃瀹氭渶澶ф渶灏忛珮搴︼紝閲嶇疆婊氬姩鏉�
-.action-modal {
-  .ant-modal {
-    max-width: 95vw;
-  }
-  .ant-modal-body {
-    max-height: calc(100vh - 235px);
-    min-height: 150px;
-    overflow-y: auto;
-    padding-bottom: 35px;
-  }
-  .ant-modal-body::-webkit-scrollbar {
-    width: 10px;
-    height: 10px;
-  }
-  .ant-modal-body::-webkit-scrollbar-thumb {
-    border-radius: 5px;
-    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
-    background: rgba(0, 0, 0, 0.13);
-  }
-  .ant-modal-body::-webkit-scrollbar-track {
-    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
-    border-radius: 3px;
-    border: 1px solid rgba(0, 0, 0, 0.07);
-    background: rgba(0, 0, 0, 0);
-  }
-}
\ No newline at end of file
diff --git a/src/tabviews/verupmanage/index.jsx b/src/tabviews/verupmanage/index.jsx
index 4c16746..e069f0f 100644
--- a/src/tabviews/verupmanage/index.jsx
+++ b/src/tabviews/verupmanage/index.jsx
@@ -137,6 +137,7 @@
     }
 
     this.setState({
+      selectedData: [],
       pickup: false
     })
 
@@ -285,7 +286,6 @@
     this.setState({
       loading: true,
       pageIndex: 1,
-      selectedData: [],
       search: searches
     }, () => {
       this.loadmaindata()
@@ -306,7 +306,6 @@
 
     this.setState({
       loading: true,
-      selectedData: [],
       pageIndex: pagination.current,
       pageSize: pagination.pageSize,
       orderBy: (sorter.field && sorter.order) ? `${sorter.field} ${sorter.order}` : ''
@@ -322,8 +321,7 @@
     MKEmitter.emit('resetTable', this.props.MenuID + 'mainTable') // 鍒楄〃閲嶇疆
     this.setState({
       pageIndex: 1,
-      loading: true,
-      selectedData: []
+      loading: true
     }, () => {
       this.loadmaindata()
     })
@@ -333,6 +331,7 @@
    * @description 椤甸潰鍒锋柊锛岄噸鏂拌幏鍙栭厤缃�
    */
   reloadview = () => {
+    MKEmitter.emit('resetTable', this.props.MenuID + 'mainTable') // 鍒楄〃閲嶇疆
     this.setState({
       config: {},
       searchlist: [],
diff --git a/src/tabviews/verupmanage/subtabtable/index.scss b/src/tabviews/verupmanage/subtabtable/index.scss
index 7159b95..e2964c1 100644
--- a/src/tabviews/verupmanage/subtabtable/index.scss
+++ b/src/tabviews/verupmanage/subtabtable/index.scss
@@ -41,7 +41,7 @@
       position: absolute;
       right: 5px;
       top: -22px;
-      z-index: 10;
+      z-index: 1;
     }
   }
 }
diff --git a/src/tabviews/zshare/actionList/excelInbutton/index.jsx b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
index 33ecd5d..1312e0d 100644
--- a/src/tabviews/zshare/actionList/excelInbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
@@ -6,7 +6,7 @@
 import { Button, Modal, notification, message } from 'antd'
 
 import ExcelIn from './excelin'
-import Utils from '@/utils/utils.js'
+import Utils, { getExcelInSql } from '@/utils/utils.js'
 import options from '@/store/options.js'
 import Api from '@/api'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -93,25 +93,28 @@
         duration: 5
       })
       return
-    }
-
-    if (btn.verify && btn.verify.sheet && btn.verify.columns && btn.verify.columns.length > 0) {
-      let primaryId = '' // 瀵煎叆鏃惰Id
-      if (btn.Ot === 'requiredSgl') {
-        primaryId = data[0][setting.primaryKey] || ''
-      }
-
-      this.setState({
-        primaryId: primaryId
-      }, () => {
-        this.refs.excelIn.exceltrigger()
-      })
-    } else {
+    } else if (!btn.verify || !btn.verify.sheet || !btn.verify.columns || btn.verify.columns.length === 0) {
       notification.warning({
         top: 92,
         message: 'excel瀵煎叆楠岃瘉淇℃伅鏈缃紒',
         duration: 5
       })
+      return
+    }
+
+    let primaryId = '' // 瀵煎叆鏃惰Id
+    if (btn.Ot === 'requiredSgl') {
+      primaryId = data[0][setting.primaryKey] || ''
+    }
+
+    this.setState({
+      primaryId: primaryId
+    }, () => {
+      this.refs.excelIn.exceltrigger()
+    })
+
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: '瀵煎叆Excel'})
     }
   }
 
@@ -235,7 +238,7 @@
       })
     }
 
-    let result = Utils.getExcelInSql(btn, data, this.state.dict, (this.props.BID || ''))
+    let result = getExcelInSql(btn, data, this.state.dict, (this.props.BID || ''))
 
     if (result.errors) {
       notification.warning({
@@ -342,13 +345,21 @@
           if (btn.sysInterface === 'true' && options.cloudServiceApi) {
             param.rduri = options.cloudServiceApi
           } else if (btn.sysInterface !== 'true') {
-            param.rduri = btn.interface
+            if (window.GLOB.systemType === 'production' && btn.proInterface) {
+              param.rduri = btn.proInterface
+            } else {
+              param.rduri = btn.interface
+            }
           }
         } else {
           if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
             param.rduri = window.GLOB.mainSystemApi
           } else if (btn.sysInterface !== 'true') {
-            param.rduri = btn.interface
+            if (window.GLOB.systemType === 'production' && btn.proInterface) {
+              param.rduri = btn.proInterface
+            } else {
+              param.rduri = btn.interface
+            }
           }
         }
 
diff --git a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
index 8da8de9..f2a67e7 100644
--- a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -111,6 +111,9 @@
     }
 
     MKEmitter.emit('getexceloutparam', btn.$menuId, btn.uuid)
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: '瀵煎嚭Excel'})
+    }
   }
 
   /**
@@ -190,13 +193,21 @@
           if (btn.sysInterface === 'true' && options.cloudServiceApi) {
             param.rduri = options.cloudServiceApi
           } else if (btn.sysInterface !== 'true') {
-            param.rduri = btn.interface
+            if (window.GLOB.systemType === 'production' && btn.proInterface) {
+              param.rduri = btn.proInterface
+            } else {
+              param.rduri = btn.interface
+            }
           }
         } else {
           if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
             param.rduri = window.GLOB.mainSystemApi
           } else if (btn.sysInterface !== 'true') {
-            param.rduri = btn.interface
+            if (window.GLOB.systemType === 'production' && btn.proInterface) {
+              param.rduri = btn.proInterface
+            } else {
+              param.rduri = btn.interface
+            }
           }
         }
   
@@ -228,13 +239,21 @@
               if (btn.sysInterface === 'true' && options.cloudServiceApi) {
                 res.rduri = options.cloudServiceApi
               } else if (btn.sysInterface !== 'true') {
-                res.rduri = btn.interface
+                if (window.GLOB.systemType === 'production' && btn.proInterface) {
+                  res.rduri = btn.proInterface
+                } else {
+                  res.rduri = btn.interface
+                }
               }
             } else {
               if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
                 res.rduri = window.GLOB.mainSystemApi
               } else if (btn.sysInterface !== 'true') {
-                res.rduri = btn.interface
+                if (window.GLOB.systemType === 'production' && btn.proInterface) {
+                  res.rduri = btn.proInterface
+                } else {
+                  res.rduri = btn.interface
+                }
               }
             }
   
@@ -289,13 +308,21 @@
           if (btn.sysInterface === 'true' && options.cloudServiceApi) {
             res.rduri = options.cloudServiceApi
           } else if (btn.sysInterface !== 'true') {
-            res.rduri = btn.interface
+            if (window.GLOB.systemType === 'production' && btn.proInterface) {
+              res.rduri = btn.proInterface
+            } else {
+              res.rduri = btn.interface
+            }
           }
         } else {
           if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
             res.rduri = window.GLOB.mainSystemApi
           } else if (btn.sysInterface !== 'true') {
-            res.rduri = btn.interface
+            if (window.GLOB.systemType === 'production' && btn.proInterface) {
+              res.rduri = btn.proInterface
+            } else {
+              res.rduri = btn.interface
+            }
           }
         }
 
@@ -353,13 +380,21 @@
         if (btn.sysInterface === 'true' && options.cloudServiceApi) {
           param.rduri = options.cloudServiceApi
         } else if (btn.sysInterface !== 'true') {
-          param.rduri = btn.interface
+          if (window.GLOB.systemType === 'production' && btn.proInterface) {
+            param.rduri = btn.proInterface
+          } else {
+            param.rduri = btn.interface
+          }
         }
       } else {
         if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
           param.rduri = window.GLOB.mainSystemApi
         } else if (btn.sysInterface !== 'true') {
-          param.rduri = btn.interface
+          if (window.GLOB.systemType === 'production' && btn.proInterface) {
+            param.rduri = btn.proInterface
+          } else {
+            param.rduri = btn.interface
+          }
         }
       }
       
diff --git a/src/tabviews/zshare/actionList/index.scss b/src/tabviews/zshare/actionList/index.scss
index ad2d2bb..112834c 100644
--- a/src/tabviews/zshare/actionList/index.scss
+++ b/src/tabviews/zshare/actionList/index.scss
@@ -24,30 +24,3 @@
     display: none;
   }
 }
-// 璁剧疆妯℃�佹鏍峰紡锛岃瀹氭渶澶ф渶灏忛珮搴︼紝閲嶇疆婊氬姩鏉�
-.action-modal {
-  .ant-modal {
-    max-width: 95vw;
-  }
-  .ant-modal-body {
-    max-height: calc(100vh - 235px);
-    min-height: 150px;
-    overflow-y: auto;
-    padding-bottom: 35px;
-  }
-  .ant-modal-body::-webkit-scrollbar {
-    width: 10px;
-    height: 10px;
-  }
-  .ant-modal-body::-webkit-scrollbar-thumb {
-    border-radius: 5px;
-    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
-    background: rgba(0, 0, 0, 0.13);
-  }
-  .ant-modal-body::-webkit-scrollbar-track {
-    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
-    border-radius: 3px;
-    border: 1px solid rgba(0, 0, 0, 0.07);
-    background: rgba(0, 0, 0, 0);
-  }
-}
\ No newline at end of file
diff --git a/src/tabviews/zshare/actionList/newpagebutton/index.jsx b/src/tabviews/zshare/actionList/newpagebutton/index.jsx
index f4e8d64..dcf07e8 100644
--- a/src/tabviews/zshare/actionList/newpagebutton/index.jsx
+++ b/src/tabviews/zshare/actionList/newpagebutton/index.jsx
@@ -97,7 +97,10 @@
       Remark = data[0].Remark || ''
     }
 
+    let _name = '鏂伴〉闈�'
+
     if (btn.pageTemplate === 'billprint') {
+      _name = '鍗曟嵁鎵撳嵃'
       if (btn.Ot === 'required' && data && data.length > 0) {
         data.forEach((item, i) => {
           let _id = item[setting.primaryKey] || ''
@@ -118,6 +121,7 @@
     } else if (btn.pageTemplate === 'pay') {
       let _p = `ID=${Id}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID')}&logo=${window.GLOB.doclogo}&name=${sessionStorage.getItem('Full_Name')}&icp=${window.GLOB.ICP}&copyRight=${window.GLOB.copyRight}`
       let url = '#/pay/' +  window.btoa(window.encodeURIComponent(_p))
+      _name = '鏀粯'
       
       confirm({
         title: '璇峰湪浠樻椤甸潰瀹屾垚璁㈠崟鏀粯銆�',
@@ -152,6 +156,10 @@
 
       window.open(url)
     }
+
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: _name})
+    }
   }
 
   render() {
diff --git a/src/tabviews/zshare/actionList/normalbutton/index.jsx b/src/tabviews/zshare/actionList/normalbutton/index.jsx
index c5cb799..792b7ef 100644
--- a/src/tabviews/zshare/actionList/normalbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -6,7 +6,7 @@
 import { Button, Modal, notification, message } from 'antd'
 
 import Api from '@/api'
-import Utils from '@/utils/utils.js'
+import Utils, { getSysDefaultSql } from '@/utils/utils.js'
 import options from '@/store/options.js'
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
@@ -40,7 +40,8 @@
     confirmLoading: false,
     btnconfig: null,
     loading: false,
-    loadingNumber: ''
+    loadingNumber: '',
+    checkParam: null
   }
 
   shouldComponentUpdate (nextProps, nextState) {
@@ -146,13 +147,32 @@
         this.actionSettingError()
         return
       }
+    } else if (btn.intertype === 'custom') {
+      if (btn.callbackType === 'script' && (!btn.verify || !btn.verify.cbScripts || !btn.verify.cbScripts.filter(item => item.status !== 'false').length === 0)) {
+        this.actionSettingError()
+        return
+      } else if (btn.procMode === 'system' && data.length === 0 && btn.verify && btn.verify.voucher && btn.verify.voucher.enabled) {
+        notification.warning({
+          top: 92,
+          message: '浣跨敤鍒涘缓鍑瘉鍑芥暟锛岄渶瑕侀�夋嫨琛岋紒',
+          duration: 5
+        })
+        return
+      } else if (window.GLOB.systemType === 'production' && !btn.proInterface) {
+        notification.warning({
+          top: 92,
+          message: '灏氭湭璁剧疆姝e紡绯荤粺鎺ュ彛鍦板潃锛�',
+          duration: 5
+        })
+        return
+      }
     } else if (btn.intertype === 'outer') {
       // 鎺ュ彛鍦板潃涓嶅瓨鍦ㄦ椂鎶ラ敊
       if (!btn.interface && btn.sysInterface !== 'true') {
         this.actionSettingError()
         return
       }
-    } else if (!['inner', 'outer', 'system'].includes(btn.intertype)) {
+    } else if (!['inner', 'outer', 'system', 'custom'].includes(btn.intertype)) {
       // 鎺ュ彛绫诲瀷閿欒
       this.actionSettingError()
       return
@@ -184,83 +204,218 @@
         this.improveAction()
       })
     }
+
+    if (window.GLOB.systemType === 'production') {
+      let _change = {
+        prompt: '鎻愮ず妗�',
+        exec: '鐩存帴鎵ц',
+        pop: '寮圭獥锛堣〃鍗曪級'
+      }
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: _change[btn.OpenType]})
+    }
   }
 
-  /**
-   * @description 鎸夐挳鎻愪氦鎵ц
-   */
-  execSubmit = (data, _resolve, formdata) => {
+  getSystemParam = (data, formdata, retmsg) => {
     const { setting, columns, btn } = this.props
-    if (btn.intertype === 'inner' || btn.intertype === 'system') {
-      // 鎵ц鏂瑰紡涓哄琛屾嫾鎺ワ紝涓旀墦寮�鏂瑰紡涓鸿〃鍗曟椂锛屼細杞负寰幆鍙戦�佽姹�
-      // 鎵撳紑鏂瑰紡涓烘ā鎬佹锛屼娇鐢ㄥ唴閮ㄥ嚱鏁版坊鍔�(鏈夋壒閲忔坊鍔犲満鏅紝宸插幓闄�)
-      if (
-        btn.Ot === 'notRequired' ||
-        btn.Ot === 'requiredSgl' ||
-        btn.Ot === 'requiredOnce'
-      ) {
-        let param = { // 绯荤粺瀛樺偍杩囩▼
+    let _params = []
+
+    if ( btn.Ot === 'notRequired' || btn.Ot === 'requiredSgl' || btn.Ot === 'requiredOnce' ) {
+      let param = { // 绯荤粺瀛樺偍杩囩▼
+        func: 'sPC_TableData_InUpDe'
+      }
+      let check_param = null
+
+      if (this.props.BID) {
+        param.BID = this.props.BID
+      }
+      
+      let primaryId = ''
+
+      if (btn.Ot === 'requiredSgl' || btn.Ot === 'requiredOnce') {
+        let ids = data.map(d => { return d[setting.primaryKey] || ''})
+        ids = ids.filter(Boolean)
+        primaryId = ids.join(',')
+      }
+
+      if (btn.OpenType === 'prompt' || btn.OpenType === 'exec') { // 鏄惁寮规鎴栫洿鎺ユ墽琛�
+        param.ID = primaryId
+
+        if (retmsg) {
+          const { sql, callbacksql } = getSysDefaultSql(btn, setting, '', param, data[0], columns, this.props.Tab, retmsg) // 鏁版嵁婧�
+          param.LText = sql
+          param.$callbacksql = callbacksql
+        } else {
+          param.LText = getSysDefaultSql(btn, setting, '', param, data[0], columns, this.props.Tab) // 鏁版嵁婧�
+        }
+
+        if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+          param.LText = param.LText.replace(/\$@/ig, '/*')
+          param.LText = param.LText.replace(/@\$/ig, '*/')
+        } else {
+          param.LText = param.LText.replace(/@\$|\$@/ig, '')
+        }
+
+        param.exec_type = 'y' // 鍚庡彴瑙g爜
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        param.secretkey = Utils.encrypt('', param.timestamp)
+
+        if (/\$check@|@check\$/ig.test(param.LText)) {
+          check_param = fromJS(param).toJS()
+          check_param.LText = check_param.LText.replace(/\$check@/ig, '/*')
+          check_param.LText = check_param.LText.replace(/@check\$/ig, '*/')
+          check_param.LText = Utils.formatOptions(check_param.LText)
+
+          param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
+        }
+
+        param.LText = Utils.formatOptions(param.LText)
+      } else if (btn.OpenType === 'pop') { // 琛ㄥ崟
+        if (btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
+          primaryId = ''
+
+          if (formdata && setting.primaryKey) { // 琛ㄥ崟涓瓨鍦ㄤ富閿瓧娈碉紝涓婚敭鍊间互琛ㄥ崟涓殑鍊间负鍑�
+            let _form = formdata.filter(_form => _form.key === setting.primaryKey)[0]
+            if (_form) {
+              primaryId = _form.value
+            }
+          }
+
+          param.ID = primaryId || Utils.getguid()
+
+          if (retmsg) {
+            const { sql, callbacksql } = getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab, retmsg) // 鏁版嵁婧�
+            param.LText = sql
+            param.$callbacksql = callbacksql
+          } else {
+            param.LText = getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab) // 鏁版嵁婧�
+          }
+          
+          if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+            param.LText = param.LText.replace(/\$@/ig, '/*')
+            param.LText = param.LText.replace(/@\$/ig, '*/')
+          } else {
+            param.LText = param.LText.replace(/@\$|\$@/ig, '')
+          }
+
+          param.exec_type = 'y' // 鍚庡彴瑙g爜
+          param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+          param.secretkey = Utils.encrypt('', param.timestamp)
+
+          if (/\$check@|@check\$/ig.test(param.LText)) {
+            check_param = fromJS(param).toJS()
+            check_param.LText = check_param.LText.replace(/\$check@/ig, '/*')
+            check_param.LText = check_param.LText.replace(/@check\$/ig, '*/')
+            check_param.LText = Utils.formatOptions(check_param.LText)
+
+            param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
+          }
+
+          param.LText = Utils.formatOptions(param.LText)
+        } else {
+          param.ID = primaryId
+
+          if (retmsg) {
+            const { sql, callbacksql } = getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab, retmsg) // 鏁版嵁婧�
+            param.LText = sql
+            param.$callbacksql = callbacksql
+          } else {
+            param.LText = getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab) // 鏁版嵁婧�
+          }
+          
+          if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+            param.LText = param.LText.replace(/\$@/ig, '/*')
+            param.LText = param.LText.replace(/@\$/ig, '*/')
+          } else {
+            param.LText = param.LText.replace(/@\$|\$@/ig, '')
+          }
+
+          param.exec_type = 'y' // 鍚庡彴瑙g爜
+          param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+          param.secretkey = Utils.encrypt('', param.timestamp)
+
+          if (/\$check@|@check\$/ig.test(param.LText)) {
+            check_param = fromJS(param).toJS()
+            check_param.LText = check_param.LText.replace(/\$check@/ig, '/*')
+            check_param.LText = check_param.LText.replace(/@check\$/ig, '*/')
+            check_param.LText = Utils.formatOptions(check_param.LText)
+
+            param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
+          }
+
+          param.LText = Utils.formatOptions(param.LText)
+        }
+      }
+
+      if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+        if (check_param) {
+          check_param.open_key = Utils.encryptOpenKey(check_param.secretkey, check_param.timestamp)
+        }
+      }
+
+      param.menuname = btn.logLabel
+
+      if (check_param) {
+        check_param.menuname = btn.logLabel
+        this.setState({checkParam: check_param})
+      }
+
+      _params.push(param)
+    } else if (btn.Ot === 'required') {
+      _params = data.map((cell, index) => {
+        let param = {
           func: 'sPC_TableData_InUpDe'
         }
 
         if (this.props.BID) {
           param.BID = this.props.BID
         }
-        
-        let primaryId = ''
 
-        if (btn.Ot === 'requiredSgl' || btn.Ot === 'requiredOnce') {
-          let ids = data.map(d => { return d[setting.primaryKey] || ''})
-          ids = ids.filter(Boolean)
-          primaryId = ids.join(',')
-        }
+        let primaryId = setting.primaryKey ? cell[setting.primaryKey] || '' : ''
 
         if (btn.OpenType === 'prompt' || btn.OpenType === 'exec') { // 鏄惁寮规鎴栫洿鎺ユ墽琛�
-          if (btn.innerFunc) { // 浣跨敤鑷畾涔夊嚱鏁�
-            param.func = btn.innerFunc
-            if (setting.primaryKey) { // 涓婚敭瀛樺湪鏃讹紝璁剧疆涓婚敭鍙傛暟
-              param[setting.primaryKey] = primaryId
-            }
-          } else if (btn.sql) {
-            param.ID = primaryId
-            param.LText = Utils.getSysDefaultSql(btn, setting, '', param, data[0], columns, this.props.Tab) // 鏁版嵁婧�
+          param.ID = primaryId
 
-            if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-              param.LText = param.LText.replace(/\$@/ig, '/*')
-              param.LText = param.LText.replace(/@\$/ig, '*/')
-            } else {
-              param.LText = param.LText.replace(/@\$|\$@/ig, '')
-            }
-
-            param.exec_type = 'y' // 鍚庡彴瑙g爜
-            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-            param.secretkey = Utils.encrypt('', param.timestamp)
-            param.LText = Utils.formatOptions(param.LText)
+          if (retmsg) {
+            const { sql, callbacksql } = getSysDefaultSql(btn, setting, '', param, cell, columns, this.props.Tab, retmsg) // 鏁版嵁婧�
+            param.LText = sql
+            param.$callbacksql = callbacksql
+          } else {
+            param.LText = getSysDefaultSql(btn, setting, '', param, cell, columns, this.props.Tab) // 鏁版嵁婧�
           }
+          
+          if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+            param.LText = param.LText.replace(/\$@/ig, '/*')
+            param.LText = param.LText.replace(/@\$/ig, '*/')
+          } else {
+            param.LText = param.LText.replace(/@\$|\$@/ig, '')
+          }
+          param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
+
+          param.exec_type = 'y' // 鍚庡彴瑙g爜
+          param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+          param.secretkey = Utils.encrypt('', param.timestamp)
+          param.LText = Utils.formatOptions(param.LText)
         } else if (btn.OpenType === 'pop') { // 琛ㄥ崟
-          if (btn.innerFunc) {
-            param.func = btn.innerFunc
-
-            if (setting.primaryKey) { // 涓婚敭瀛樺湪鏃讹紝璁剧疆涓婚敭鍙傛暟
-              param[setting.primaryKey] = primaryId
-            }
-
-            formdata.forEach(_data => {
-              param[_data.key] = _data.value
-            })
-
-          } else if (btn.sql && btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
-            primaryId = ''
-
-            if (formdata && setting.primaryKey) { // 琛ㄥ崟涓瓨鍦ㄤ富閿瓧娈碉紝涓婚敭鍊间互琛ㄥ崟涓殑鍊间负鍑�
-              let _form = formdata.filter(_form => _form.key === setting.primaryKey)[0]
-              if (_form) {
-                primaryId = _form.value
+          if (index !== 0) {
+            formdata = formdata.map(_data => {
+              if (_data.readin && cell.hasOwnProperty(_data.key)) {
+                _data.value = cell[_data.key]
               }
-            }
+              return _data
+            })
+          }
 
-            param.ID = primaryId || Utils.getguid()
-            param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab) // 鏁版嵁婧�
+          if (btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
+            param.ID = Utils.getguid()
+
+            if (retmsg) {
+              const { sql, callbacksql } = getSysDefaultSql(btn, setting, formdata, param, cell, columns, this.props.Tab, retmsg) // 鏁版嵁婧�
+              param.LText = sql
+              param.$callbacksql = callbacksql
+            } else {
+              param.LText = getSysDefaultSql(btn, setting, formdata, param, cell, columns, this.props.Tab) // 鏁版嵁婧�
+            }
             
             if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
               param.LText = param.LText.replace(/\$@/ig, '/*')
@@ -268,14 +423,22 @@
             } else {
               param.LText = param.LText.replace(/@\$|\$@/ig, '')
             }
+            param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
 
             param.exec_type = 'y' // 鍚庡彴瑙g爜
             param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
             param.secretkey = Utils.encrypt('', param.timestamp)
             param.LText = Utils.formatOptions(param.LText)
-          } else if (btn.sql) {
+          } else {
             param.ID = primaryId
-            param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab) // 鏁版嵁婧�
+
+            if (retmsg) {
+              const { sql, callbacksql } = getSysDefaultSql(btn, setting, formdata, param, cell, columns, this.props.Tab, retmsg) // 鏁版嵁婧�
+              param.LText = sql
+              param.$callbacksql = callbacksql
+            } else {
+              param.LText = getSysDefaultSql(btn, setting, formdata, param, cell, columns, this.props.Tab) // 鏁版嵁婧�
+            }
             
             if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
               param.LText = param.LText.replace(/\$@/ig, '/*')
@@ -283,6 +446,7 @@
             } else {
               param.LText = param.LText.replace(/@\$|\$@/ig, '')
             }
+            param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
 
             param.exec_type = 'y' // 鍚庡彴瑙g爜
             param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -291,11 +455,7 @@
           }
         }
 
-        if (this.props.menuType === 'HS' && param.timestamp) { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
-          param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
-        } else if (this.props.menuType === 'HS' && param.func === 's_sDataDictb_TBBack' && param.LTextOut) { // 鍑芥暟 s_sDataDictb_TBBack 浜戠楠岃瘉
-          param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-          param.secretkey = Utils.encrypt(param.LTextOut, param.timestamp)
+        if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
           param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
         }
 
@@ -303,160 +463,135 @@
           param.menuname = btn.logLabel
         }
 
-        Api.genericInterface(param).then((res) => {
-          if (res.status) {
-            this.execSuccess(res)
-          } else {
-            this.execError(res)
-          }
-          _resolve()
-        }, () => {
-          this.updateStatus('over')
-          _resolve()
+        return param
+      })
+    }
+
+    return _params
+  }
+
+  getInnerParam = (data, formdata) => {
+    const { setting, btn } = this.props
+    let _params = []
+
+    if ( btn.Ot === 'notRequired' || btn.Ot === 'requiredSgl' || btn.Ot === 'requiredOnce' ) {
+      let param = {
+        func: btn.innerFunc
+      }
+
+      if (this.props.BID) {
+        param.BID = this.props.BID
+      }
+      
+      let primaryId = ''
+
+      if (btn.Ot === 'requiredSgl' || btn.Ot === 'requiredOnce') {
+        let ids = data.map(d => { return d[setting.primaryKey] || ''})
+        ids = ids.filter(Boolean)
+        primaryId = ids.join(',')
+      }
+
+      param[setting.primaryKey] = primaryId // 璁剧疆涓婚敭鍙傛暟
+
+      if (btn.OpenType === 'pop') { // 琛ㄥ崟
+        formdata.forEach(_data => {
+          param[_data.key] = _data.value
         })
-      } else if (btn.Ot === 'required') {
-        let _formPrimaryId = ''
-        if (formdata && setting.primaryKey) { // 琛ㄥ崟涓瓨鍦ㄤ富閿瓧娈碉紝涓婚敭鍊间互琛ㄥ崟涓殑鍊间负鍑�
-          let _form = formdata.filter(_form => _form.key === setting.primaryKey)[0]
-          if (_form) {
-            _formPrimaryId = _form.value
-          }
+      }
+
+      if (this.props.menuType === 'HS' && param.func === 's_sDataDictb_TBBack' && param.LTextOut) { // 鍑芥暟 s_sDataDictb_TBBack 浜戠楠岃瘉
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        param.secretkey = Utils.encrypt(param.LTextOut, param.timestamp)
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+      }
+
+      _params.push(param)
+    } else if (btn.Ot === 'required') {
+      _params = data.map((cell, index) => {
+        let param = {
+          func: btn.innerFunc
         }
 
-        let _params = data.map((cell, index) => {
-          let param = {
-            func: 'sPC_TableData_InUpDe'
+        if (this.props.BID) {
+          param.BID = this.props.BID
+        }
+
+        let primaryId = setting.primaryKey ? cell[setting.primaryKey] || '' : ''
+
+        if (btn.OpenType === 'pop') { // 琛ㄥ崟
+          if (index !== 0) {
+            formdata = formdata.map(_data => {
+              if (_data.readin && cell.hasOwnProperty(_data.key)) {
+                _data.value = cell[_data.key]
+              }
+              return _data
+            })
           }
 
-          if (this.props.BID) {
-            param.BID = this.props.BID
-          }
+          formdata.forEach(_data => {
+            param[_data.key] = _data.value
+          })
+        }
+        param[setting.primaryKey] = primaryId
 
-          let primaryId = setting.primaryKey ? cell[setting.primaryKey] || '' : ''
+        if (this.props.menuType === 'HS' && param.func === 's_sDataDictb_TBBack' && param.LTextOut) { // 鍑芥暟 s_sDataDictb_TBBack 浜戠楠岃瘉
+          param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+          param.secretkey = Utils.encrypt(param.LTextOut, param.timestamp)
+          param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+        }
 
-          if (btn.OpenType === 'prompt' || btn.OpenType === 'exec') { // 鏄惁寮规鎴栫洿鎺ユ墽琛�
+        return param
+      })
+    }
 
-            if (btn.innerFunc) {
-              param.func = btn.innerFunc
-              if (setting.primaryKey) {
-                param[setting.primaryKey] = primaryId
-              }
-            } else if (btn.sql) {
-              param.ID = primaryId
-              param.LText = Utils.getSysDefaultSql(btn, setting, '', param, cell, columns, this.props.Tab) // 鏁版嵁婧�
-              
-              if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                param.LText = param.LText.replace(/\$@/ig, '/*')
-                param.LText = param.LText.replace(/@\$/ig, '*/')
-              } else {
-                param.LText = param.LText.replace(/@\$|\$@/ig, '')
-              }
+    return _params
+  }
 
-              param.exec_type = 'y' // 鍚庡彴瑙g爜
-              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              param.secretkey = Utils.encrypt('', param.timestamp)
-              param.LText = Utils.formatOptions(param.LText)
-            }
-          } else if (btn.OpenType === 'pop') { // 琛ㄥ崟
-            if (index !== 0) {
-              formdata = formdata.map(_data => {
-                if (_data.readin && cell.hasOwnProperty(_data.key)) {
-                  _data.value = cell[_data.key]
-                }
-                return _data
-              })
-            }
+  /**
+   * @description 鎸夐挳鎻愪氦鎵ц
+   */
+  execSubmit = (data, _resolve, formdata) => {
+    const { setting, btn } = this.props
+    if (btn.intertype === 'system' || btn.intertype === 'inner') { // 绯荤粺鎺ュ彛
+      let params = []
 
-            if (btn.innerFunc) {
-              param.func = btn.innerFunc
+      if (btn.intertype === 'system') {
+        params = this.getSystemParam(data, formdata)
+      } else {
+        params = this.getInnerParam(data, formdata)
+      }
 
-              formdata.forEach(_data => {
-                param[_data.key] = _data.value
-              })
-
-              if (setting.primaryKey) {
-                param[setting.primaryKey] = primaryId
-              }
-            } else if (btn.sql && btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
-              param.ID = _formPrimaryId || Utils.getguid()
-              param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, cell, columns, this.props.Tab) // 鏁版嵁婧�
-              
-              if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                param.LText = param.LText.replace(/\$@/ig, '/*')
-                param.LText = param.LText.replace(/@\$/ig, '*/')
-              } else {
-                param.LText = param.LText.replace(/@\$|\$@/ig, '')
-              }
-
-              param.exec_type = 'y' // 鍚庡彴瑙g爜
-              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              param.secretkey = Utils.encrypt('', param.timestamp)
-              param.LText = Utils.formatOptions(param.LText)
-            } else if (btn.sql) {
-              param.ID = primaryId
-              param.LText = Utils.getSysDefaultSql(btn, setting, formdata, param, cell, columns, this.props.Tab) // 鏁版嵁婧�
-              
-              if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                param.LText = param.LText.replace(/\$@/ig, '/*')
-                param.LText = param.LText.replace(/@\$/ig, '*/')
-              } else {
-                param.LText = param.LText.replace(/@\$|\$@/ig, '')
-              }
-
-              param.exec_type = 'y' // 鍚庡彴瑙g爜
-              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              param.secretkey = Utils.encrypt('', param.timestamp)
-              param.LText = Utils.formatOptions(param.LText)
-            }
-          }
-
-          if (this.props.menuType === 'HS' && param.timestamp) { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
-            param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
-          } else if (this.props.menuType === 'HS' && param.func === 's_sDataDictb_TBBack' && param.LTextOut) { // 鍑芥暟 s_sDataDictb_TBBack 浜戠楠岃瘉
-            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-            param.secretkey = Utils.encrypt(param.LTextOut, param.timestamp)
-            param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
-          }
-
-          if (param.func === 'sPC_TableData_InUpDe') {
-            param.menuname = btn.logLabel
-          }
-
-          return param
-        })
-
-        if (_params.length <= 20) {
-          let deffers = _params.map(param => {
-            return new Promise(resolve => {
-              Api.genericInterface(param).then(res => {
-                resolve(res)
-              }, () => {
-                this.updateStatus('over')
-                _resolve()
-              })
+      if (params.length <= 20) {
+        let deffers = params.map(param => {
+          return new Promise(resolve => {
+            Api.genericInterface(param).then(res => {
+              resolve(res)
+            }, () => {
+              this.updateStatus('over')
+              _resolve()
             })
           })
-          Promise.all(deffers).then(result => {
-            let iserror = false
-            let errorMsg = ''
-            result.forEach(res => {
-              if (res.status) {
-                errorMsg = res
-              } else {
-                iserror = true
-                errorMsg = res
-              }
-            })
-            if (!iserror) {
-              this.execSuccess(errorMsg)
+        })
+        Promise.all(deffers).then(result => {
+          let iserror = false
+          let errorMsg = ''
+          result.forEach(res => {
+            if (res.status) {
+              errorMsg = res
             } else {
-              this.execError(errorMsg)
+              iserror = true
+              errorMsg = res
             }
-            _resolve()
           })
-        } else { // 瓒呭嚭20涓姹傛椂寰幆鎵ц
-          this.innerLoopRequest(_params, btn, _resolve)
-        }
+          if (!iserror) {
+            this.execSuccess(errorMsg)
+          } else {
+            this.execError(errorMsg)
+          }
+          _resolve()
+        })
+      } else { // 瓒呭嚭20涓姹傛椂寰幆鎵ц
+        this.innerLoopRequest(params, btn, _resolve)
       }
     } else if (btn.intertype === 'outer') {
       /** *********************璋冪敤澶栭儴鎺ュ彛************************* */
@@ -518,15 +653,342 @@
 
       // 寰幆璋冪敤澶栭儴鎺ュ彛锛堝寘鎷唴閮ㄥ強鍥炶皟鍑芥暟锛�
       this.outerLoopRequest(_params, btn, _resolve, _params.length > 20)
+    } else if (btn.intertype === 'custom') { // 绯荤粺鎺ュ彛
+      let params = []
+
+      if (btn.procMode === 'system') {
+        params = this.getSystemParam(data, formdata, true)
+        params = params.map(item => {
+          item.script_type = 'Y'
+          return item
+        })
+      } else {
+        params = this.getInnerParam(data, formdata)
+      }
+
+      this.customLoopRequest(params, _resolve)
     }
+  }
+
+  /**
+   * @description 鑷畾涔夎姹傚惊鐜墽琛�
+   */
+  customLoopRequest = (params, _resolve) => {
+    let param = params.shift()
+
+    this.setState({
+      loadingNumber: params.length || ''
+    })
+
+    let record = {
+      BID: param.BID || '',
+      ID: param.ID || '',
+      callbacksql: param.$callbacksql || ''
+    }
+
+    delete param.$callbacksql
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (res.mk_ex_invoke === 'false' && params.length === 0) {
+          this.execSuccess(res)
+          _resolve()
+        } else if (res.mk_ex_invoke === 'false' && params.length > 0) {
+          this.customLoopRequest(params, _resolve)
+        } else {
+          this.customOuterRequest(params, res, record, _resolve)
+        }
+      } else if (res.ErrCode === 'C' && this.state.checkParam) {
+        const _this = this
+        confirm({
+          title: res.message || res.ErrMesg,
+          content: '缁х画鎵ц锛�',
+          onOk() {
+            return new Promise(resolve => {
+              Api.genericInterface(_this.state.checkParam).then((result) => {
+                if (result.status) {
+                  if (result.mk_ex_invoke === 'false' && params.length === 0) {
+                    _this.execSuccess(result)
+                    _resolve()
+                  } else if (result.mk_ex_invoke === 'false' && params.length > 0) {
+                    _this.customLoopRequest(params, _resolve)
+                  } else {
+                    _this.customOuterRequest(params, result, record, _resolve)
+                  }
+                } else {
+                  _this.execError(result)
+                  _resolve()
+                }
+                resolve()
+              }, () => {
+                _this.updateStatus('over')
+                resolve()
+                _resolve()
+              })
+            })
+          },
+          onCancel() {
+            _this.execError({...res, ErrCode: 'P'})
+            _resolve()
+          }
+        })
+        this.setState({checkParam: null})
+      } else {
+        this.execError(res)
+        _resolve()
+      }
+    }, () => {
+      this.updateStatus('over')
+      _resolve()
+    })
+  }
+
+  /**
+   * @description 鑷畾涔夎姹傚惊鐜墽琛�
+   */
+  customOuterRequest = (params, result, record, _resolve) => {
+    const { btn } = this.props
+
+    let url = ''
+    if (window.GLOB.systemType === 'production') {
+      url = btn.proInterface
+    } else {
+      url = btn.interface
+    }
+
+    let mkey = result.mk_api_key || ''
+
+    delete result.mk_ex_invoke
+    delete result.status
+    delete result.message
+    delete result.ErrCode
+    delete result.ErrMesg
+    delete result.mk_api_key
+
+    let param = {}
+
+    Object.keys(result).forEach(key => {
+      key = key.replace(/^mk_/ig, '')
+      param[key] = result[key]
+    })
+
+    Api.directRequest(url, btn.method, param).then(res => {
+      if (typeof(res) !== 'object' || Array.isArray(res)) {
+        let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
+
+        if (typeof(res) === 'string') {
+          error = res.replace(/'/ig, '"')
+        }
+
+        let result = {
+          mk_api_key: mkey,
+          $ErrCode: 'E',
+          $ErrMesg: error
+        }
+
+        this.customCallbackRequest(params, result, record, _resolve)
+      } else {
+        res.mk_api_key = mkey
+        this.customCallbackRequest(params, res, record, _resolve)
+      }
+    }, (e) => {
+      let result = {
+        mk_api_key: mkey,
+        $ErrCode: 'E',
+        $ErrMesg: e && e.statusText ? e.statusText : ''
+      }
+
+      this.customCallbackRequest([], result, record, _resolve)
+    })
+  }
+
+  /**
+   * @description 鍥炶皟璇锋眰寰幆鎵ц
+   */
+  customCallbackRequest = (params, result, record, _resolve) => {
+    const { btn } = this.props
+    let lines = []
+    let pre = btn.callbackType === 'script' ? '@' : ''
+
+    let errSql = ''
+    if (result.$ErrCode === 'E') {
+      errSql = `
+        set @ErrorCode='E'
+        set @retmsg='${result.$ErrMesg}'
+      `
+      delete result.$ErrCode
+      delete result.$ErrMesg
+    }
+
+    let getDefaultSql = (obj, tb, bid, level) => {
+      let keys = []
+      let vals = []
+      let subObjs = []
+      let id = Utils.getuuid()
+
+      delete obj.$$key
+
+      Object.keys(obj).forEach(key => {
+        let val = obj[key]
+        if (val === null || val === undefined) return
+        if (typeof(val) === 'object') {
+          if (Array.isArray(val)) {
+            val.forEach(item => {
+              if (typeof(item) !== 'object' || Array.isArray(item)) return
+              if (Object.keys(item).length > 0) {
+                item.$$key = tb + '_' + key
+                subObjs.push(item)
+              }
+            })
+          } else if (Object.keys(val).length > 0) {
+            val.$$key = tb + '_' + key
+            subObjs.push(val)
+          }
+        } else {
+          keys.push(key)
+          vals.push(`'${val}'`)
+        }
+      })
+
+      lines.push({
+        table: tb,
+        insert: `Insert into ${pre}${tb} (${keys.join(',')},mk_level,mk_id,mk_bid)`,
+        select: `Select ${vals.join(',')},'${level}','${id}','${bid}'`
+      })
+
+      subObjs.forEach(item => {
+        getDefaultSql(item, item.$$key, id, level + 1)
+      })
+    }
+    
+    getDefaultSql(result, btn.cbTable, '', 1)
+
+    let lineMap = new Map()
+    lines.forEach(line => {
+      if (lineMap.has(line.table)) {
+        let _line = lineMap.get(line.table)
+        _line.selects.push(line.select)
+        lineMap.set(line.table, _line)
+      } else {
+        lineMap.set(line.table, {
+          table: line.table,
+          insert: line.insert,
+          selects: [line.select]
+        })
+      }
+    })
+
+    let param = {}
+
+    if (btn.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈�
+      param.func = 'sPC_TableData_InUpDe'
+      
+      if (record.BID) {
+        param.BID = this.props.BID
+      }
+      if (record.ID) {
+        param.ID = record.ID
+      }
+
+      let _prevCustomScript = `${record.callbacksql}
+        ${errSql}
+      `
+      let _backCustomScript = ''
+
+      btn.verify.cbScripts.forEach(script => {
+        if (script.status === 'false') return
+
+        if (script.position === 'front') {
+          _prevCustomScript += `
+        /* 鑷畾涔夎剼鏈� */
+        ${script.sql}
+        `
+        } else {
+          _backCustomScript += `
+        /* 鑷畾涔夎剼鏈� */
+        ${script.sql}
+        `
+        }
+      })
+
+      _backCustomScript += `
+        aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
+
+      let sql = [...lineMap.values()].map(item => (`
+        ${item.insert}
+        ${item.selects.join(` union all
+        `)}
+      `))
+      sql = sql.join('')
+      sql = _prevCustomScript + sql
+      sql = sql + _backCustomScript
+
+      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+        console.info(sql.replace(/\n\s{8}/ig, '\n'))
+      }
+
+      param.LText = sql
+      
+      if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+        param.LText = param.LText.replace(/\$@/ig, '/*')
+        param.LText = param.LText.replace(/@\$/ig, '*/')
+      } else {
+        param.LText = param.LText.replace(/@\$|\$@/ig, '')
+      }
+      param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
+
+      param.exec_type = 'y' // 鍚庡彴瑙g爜
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt('', param.timestamp)
+      param.LText = Utils.formatOptions(param.LText)
+      param.menuname = btn.logLabel
+
+      if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+        param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+      }
+    } else {
+      param.func = 's_ex_result_back'
+      param.s_ex_result = [...lineMap.values()].map((item, index) => ({
+        MenuID: btn.uuid,
+        MenuName: btn.logLabel,
+        TableName: item.table,
+        LongText: window.btoa(window.encodeURIComponent(`${item.insert}  ${item.selects.join(` union all `)}`)),
+        Sort: index + 1
+      }))
+
+      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+        let sql = [...lineMap.values()].map(item => (`
+          ${item.insert}
+          ${item.selects.join(` union all
+          `)}
+        `))
+        sql = sql.join('')
+        console.info(sql.replace(/\n\s{10}/ig, '\n'))
+      }
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        if (params.length === 0) {
+          this.execSuccess(res)
+          _resolve()
+        } else {
+          this.customLoopRequest(params, _resolve)
+        }
+      } else {
+        this.execError(res)
+        _resolve()
+      }
+    }, () => {
+      this.updateStatus('over')
+      _resolve()
+    })
   }
 
   /**
    * @description 鍐呴儴璇锋眰寰幆鎵ц
    */
   innerLoopRequest = (params, btn, _resolve) => {
-    if (!params && params.length === 0) return
-
     let param = params.shift()
 
     this.setState({
@@ -617,7 +1079,11 @@
         if (btn.sysInterface === 'true' && options.cloudServiceApi) {
           res.rduri = options.cloudServiceApi
         } else if (btn.sysInterface !== 'true') {
-          res.rduri = btn.interface
+          if (window.GLOB.systemType === 'production' && btn.proInterface) {
+            res.rduri = btn.proInterface
+          } else {
+            res.rduri = btn.interface
+          }
         }
 
         // 鍑芥暟 s_sDataDictb_TBBack 浜戠楠岃瘉
@@ -630,7 +1096,11 @@
         if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
           res.rduri = window.GLOB.mainSystemApi
         } else if (btn.sysInterface !== 'true') {
-          res.rduri = btn.interface
+          if (window.GLOB.systemType === 'production' && btn.proInterface) {
+            res.rduri = btn.proInterface
+          } else {
+            res.rduri = btn.interface
+          }
         }
       }
 
@@ -863,10 +1333,53 @@
     } else if (res.ErrCode === 'NM') {
       message.error(res.message || res.ErrMesg)
     }
-
+    
     this.setState({
-      loading: false
+      loading: false,
+      loadingNumber: ''
     })
+
+    if (res.ErrCode === 'C') {
+      const _this = this
+      if (this.state.checkParam) {
+        let param = this.state.checkParam
+        confirm({
+          title: res.message || res.ErrMesg,
+          content: '缁х画鎵ц锛�',
+          onOk() {
+            return new Promise(resolve => {
+              Api.genericInterface(param).then((result) => {
+                if (result.status) {
+                  _this.execSuccess(result)
+                } else {
+                  _this.execError(result)
+                }
+                resolve()
+              }, () => {
+                _this.setState({
+                  visible: false
+                })
+                resolve()
+              })
+            })
+          },
+          onCancel() {
+            _this.setState({
+              visible: false
+            })
+            if (btn.execError !== 'never') {
+              MKEmitter.emit('refreshByButtonResult', btn.$menuId, btn.execError, btn)
+            }
+          }
+        })
+        this.setState({checkParam: null})
+        return
+      } else {
+        Modal.error({
+          title: res.message || res.ErrMesg,
+        })
+      }
+    }
 
     if (btn.execError !== 'never') {
       MKEmitter.emit('refreshByButtonResult', btn.$menuId, btn.execError, btn)
@@ -1104,9 +1617,9 @@
    */
   getModels = () => {
     const { setting, BID, btn } = this.props
-    const { btnconfig } = this.state
+    const { btnconfig, visible } = this.state
 
-    if (!this.state.visible || !btnconfig || !btnconfig.setting) return null
+    if (!btnconfig || !btnconfig.setting) return null
 
     let title = btnconfig.setting.title
     let width = btnconfig.setting.width + 'vw'
@@ -1131,7 +1644,7 @@
         maskClosable={clickouter}
         getContainer={container}
         wrapClassName='action-modal'
-        visible={this.state.visible}
+        visible={visible}
         width={width}
         onOk={this.handleOk}
         confirmLoading={this.state.confirmLoading}
@@ -1154,7 +1667,7 @@
 
   render() {
     const { btn, show, style } = this.props
-    const { loadingNumber, loading } = this.state
+    const { loadingNumber, loading, visible } = this.state
 
     if (show === 'actionList') {
       return <div style={{display: 'inline-block'}} onClick={(e) => e.stopPropagation()}>
@@ -1164,8 +1677,8 @@
           loading={loading}
           className={'mk-btn mk-' + btn.class}
           onClick={() => {this.actionTrigger()}}
-        >{loadingNumber ? `(${loadingNumber})` : '' + btn.label}</Button>
-        {this.getModels()}
+        >{(loadingNumber ? `(${loadingNumber})` : '') + btn.label}</Button>
+        {visible ? this.getModels() : null}
       </div>
     } else if (show && show.indexOf('plus') > -1) {
       return <div className="mk-btn-wrap">
@@ -1176,7 +1689,7 @@
           style={{fontSize: show.substring(4) + 'px'}}
           onClick={() => {this.actionTrigger()}}
         ></Button>
-        {this.getModels()}
+        {visible ? this.getModels() : null}
       </div>
     } else { // icon銆乼ext銆� all 鍗$墖
       return <div style={{display: 'inline-block'}} onClick={(e) => e.stopPropagation()}>
@@ -1188,7 +1701,7 @@
           icon={show === 'text' ? '' : (btn.icon || '')}
           onClick={() => {this.actionTrigger()}}
         >{show === 'icon' && btn.icon ? '' : btn.label}</Button>
-        {this.getModels()}
+        {visible ? this.getModels() : null}
       </div>
     }
   }
diff --git a/src/tabviews/zshare/actionList/popupbutton/index.jsx b/src/tabviews/zshare/actionList/popupbutton/index.jsx
index 8baae5f..539e70c 100644
--- a/src/tabviews/zshare/actionList/popupbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/popupbutton/index.jsx
@@ -144,6 +144,10 @@
       primaryId: primaryId,
       visible: true
     })
+
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: '寮圭獥锛堟爣绛撅級'})
+    }
   }
 
   /**
diff --git a/src/tabviews/zshare/actionList/printbutton/index.jsx b/src/tabviews/zshare/actionList/printbutton/index.jsx
index e80dd27..5557e08 100644
--- a/src/tabviews/zshare/actionList/printbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -160,6 +160,10 @@
     } else {
       this.triggerPrint(data)
     }
+
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: '鏍囩鎵撳嵃'})
+    }
   }
 
   /**
@@ -461,13 +465,21 @@
         if (btn.sysInterface === 'true' && options.cloudServiceApi) {
           res.rduri = options.cloudServiceApi
         } else if (btn.sysInterface !== 'true') {
-          res.rduri = btn.interface
+          if (window.GLOB.systemType === 'production' && btn.proInterface) {
+            res.rduri = btn.proInterface
+          } else {
+            res.rduri = btn.interface
+          }
         }
       } else {
         if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
           res.rduri = window.GLOB.mainSystemApi
         } else if (btn.sysInterface !== 'true') {
-          res.rduri = btn.interface
+          if (window.GLOB.systemType === 'production' && btn.proInterface) {
+            res.rduri = btn.proInterface
+          } else {
+            res.rduri = btn.interface
+          }
         }
       }
 
diff --git a/src/tabviews/zshare/actionList/tabbutton/index.jsx b/src/tabviews/zshare/actionList/tabbutton/index.jsx
index 54e43b7..9223ef1 100644
--- a/src/tabviews/zshare/actionList/tabbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -148,6 +148,9 @@
     })
 
     MKEmitter.emit('openNewTab')
+    if (window.GLOB.systemType === 'production') {
+      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: '鏍囩椤�'})
+    }
   }
 
   render() {
diff --git a/src/tabviews/zshare/fileupload/index.jsx b/src/tabviews/zshare/fileupload/index.jsx
index 925ef09..e8cf594 100644
--- a/src/tabviews/zshare/fileupload/index.jsx
+++ b/src/tabviews/zshare/fileupload/index.jsx
@@ -5,7 +5,6 @@
 import { Upload, Button, Icon, Progress, notification } from 'antd'
 import SparkMD5 from 'spark-md5'
 import Api from '@/api'
-import Utils from '@/utils/utils.js'
 import './index.scss'
 
 let service = ''
@@ -25,51 +24,54 @@
 
   state = {
     percent: 0,
-    showprogress: false
+    showprogress: false,
+    filelist: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { value } = this.props
+    if (!value) return
+
+    this.setState({filelist: fromJS(value).toJS()})
   }
 
   onChange = ({ fileList }) => {
-    const { onChange } = this.props
+    fileList = fileList.map(item => {
+      if (item.status === 'error' && /^<!DOCTYPE html>/.test(item.response)) {
+        item.response = ''
+      }
+      return item
+    })
 
-    if (onChange) {
-      onChange([...fileList])
-    }
+    this.setState({filelist: fileList})
+    this.props.onChange(fileList)
   }
 
   onRemove = file => {
-    const { value, onChange } = this.props
+    const files = this.state.filelist.filter(v => v.uid !== file.uid)
 
-    const files = value.filter(v => v.url !== file.url)
-
-    if (onChange) {
-      onChange(files)
-    }
+    this.setState({filelist: files})
+    this.props.onChange(files)
   }
 
   onUpdate = (url) => {
-    const { value, onChange } = this.props
+    let filelist = fromJS(this.state.filelist).toJS()
 
-    let filelist = fromJS(value).toJS()
-    filelist[filelist.length -1].status = 'done'
-    filelist[filelist.length -1].response = url
-    filelist[filelist.length -1].origin = false
-
-    if (onChange) {
-      onChange([...filelist])
+    if (filelist[filelist.length -1]) {
+      filelist[filelist.length -1].status = 'done'
+      filelist[filelist.length -1].response = url
+      filelist[filelist.length -1].origin = false
     }
+
+    this.setState({filelist})
+    this.props.onChange(filelist)
   }
 
   onDelete = (msg) => {
-    const { value, onChange } = this.props
-    let filelist = value.filter(v => !v.url && !v.response)
+    let filelist = this.state.filelist.filter(v => !v.url && !v.response)
 
-    if (onChange) {
-      onChange([...filelist])
-    }
-
-    this.setState({
-      showprogress: false
-    })
+    this.setState({filelist, showprogress: false})
+    this.props.onChange(filelist)
 
     notification.warning({
       top: 92,
@@ -122,6 +124,16 @@
     })
   }
 
+  getuuid = () => {
+    let uuid = []
+    let _options = '0123456789abcdefghigklmnopqrstuv'
+    for (let i = 0; i < 19; i++) {
+      uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
+    }
+    uuid = uuid.join('')
+    return uuid
+  }
+
   beforeUpload = (file) => {
     const { accept } = this.props
 
@@ -165,7 +177,7 @@
     params.file.fileChunks = chunks                                // 璁板綍鎵�鏈塩hunks鐨勯暱搴�
 
     if (!/^[A-Za-z0-9]+$/.test(params.file.fileName)) {            // 鏂囦欢鍚嶇О鍚湁鑻辨枃鍙婃暟瀛椾箣澶栧瓧绗︽椂锛屽悕绉扮郴缁熺敓鎴�
-      params.file.fileName = moment().format('YYYYMMDDHHmmss') + Utils.getuuid().substr(14)
+      params.file.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
     }
 
     totalFileReader.readAsArrayBuffer(file)
@@ -253,12 +265,12 @@
   }
 
   render() {
-    const { value, maxFile, fileType, accept } = this.props
-    const { showprogress, percent } = this.state
+    const { maxFile, fileType, accept } = this.props
+    const { showprogress, percent, filelist } = this.state
 
     let uploadable = 'fileupload-form-container '
 
-    if (maxFile && maxFile > 0 && value && value.length >= maxFile) {
+    if (maxFile && maxFile > 0 && filelist.length >= maxFile) {
       uploadable += 'limit-fileupload'
     }
 
@@ -266,7 +278,7 @@
       name: 'file',
       disabled: showprogress,
       listType: fileType,
-      fileList: value,
+      fileList: filelist,
       action: null,
       accept: accept || '',
       method: 'post',
diff --git a/src/tabviews/zshare/mutilform/customTextArea/index.jsx b/src/tabviews/zshare/mutilform/customTextArea/index.jsx
new file mode 100644
index 0000000..caed9bf
--- /dev/null
+++ b/src/tabviews/zshare/mutilform/customTextArea/index.jsx
@@ -0,0 +1,72 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Input } from 'antd'
+
+import './index.scss'
+
+const { TextArea } = Input
+
+class CustomTextArea extends Component {
+  static propTpyes = {
+    Item: PropTypes.bool,      // 琛ㄥ崟
+    onChange: PropTypes.func   // 鏁版嵁鍒囨崲
+  }
+
+  state = {
+    value: '',
+    encryption: 'false'
+  }
+
+  UNSAFE_componentWillMount () {
+    let value = ''
+    let encryption = 'false'
+
+    if (this.props['data-__meta']) {
+      value = this.props['data-__meta'].initialValue || ''
+    }
+
+    if (this.props.Item && this.props.Item.encryption === 'true') {
+      encryption = 'true'
+      if (value) {
+        try {
+          value = window.decodeURIComponent(window.atob(value))
+        } catch {
+          value = this.props['data-__meta'].initialValue || ''
+        }
+      }
+    }
+    
+    this.setState({
+      value,
+      encryption
+    })
+  }
+
+  onChange = (e) => {
+    const { encryption } = this.state
+    let val = e.target.value
+
+    this.setState({ value: val })
+
+    let _val = val
+    if (encryption === 'true') {
+      try {
+        _val = window.btoa(window.encodeURIComponent(_val))
+      } catch {
+        _val = val
+      }
+    }
+    this.props.onChange(_val)
+  }
+
+  render() {
+    const { Item } = this.props
+    const { value } = this.state
+
+    return (
+      <TextArea value={value} autoSize={{ minRows: 2, maxRows: Item.maxRows || 6 }} onChange={this.onChange} disabled={Item.readonly === 'true'} />
+    )
+  }
+}
+
+export default CustomTextArea
\ No newline at end of file
diff --git a/src/tabviews/zshare/mutilform/customTextArea/index.scss b/src/tabviews/zshare/mutilform/customTextArea/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tabviews/zshare/mutilform/customTextArea/index.scss
diff --git a/src/tabviews/zshare/mutilform/index.jsx b/src/tabviews/zshare/mutilform/index.jsx
index ce1485e..2c79f04 100644
--- a/src/tabviews/zshare/mutilform/index.jsx
+++ b/src/tabviews/zshare/mutilform/index.jsx
@@ -9,16 +9,17 @@
 import { formRule } from '@/utils/option.js'
 import Utils from '@/utils/utils.js'
 import asyncComponent from '@/utils/asyncComponent'
+import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import './index.scss'
 
 const { MonthPicker } = DatePicker
-const { TextArea } = Input
 
 const CheckCard = asyncComponent(() => import('./checkCard'))
 const CustomSwitch = asyncComponent(() => import('./customSwitch'))
+const CustomTextArea = asyncComponent(() => import('./customTextArea'))
 const FileUpload = asyncComponent(() => import('../fileupload'))
 const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
-// const Editor = asyncComponent(() => import('@/components/editor'))
+const Editor = asyncSpinComponent(() => import('@/components/editor'))
 
 class MainSearch extends Component {
   static propTpyes = {
@@ -39,7 +40,6 @@
     writein: null,   // 鎵ц鏃舵槸鍚﹀~鍏ラ粯璁ql
     fieldlen: null,  // 瀛楁闀垮害
     formlist: [],    // 琛ㄥ崟椤�
-    encrypts: [],    // 鍔犲瘑瀛楁
     intercepts: [],  // 鎴彇瀛楁
     record: {}       // 璁板綍涓嬫媺琛ㄥ崟鍏宠仈瀛楁锛岀敤浜庢暟鎹啓鍏�
   }
@@ -68,7 +68,6 @@
     let writein = {}
     let fieldlen = {}
     let formlist = []
-    let encrypts = []
     let intercepts = []
     let _inputfields = []
 
@@ -99,9 +98,6 @@
         _inputfields.push(item.field)
       } else if (item.type === 'textarea') {
         _inputfields.push(item.field)
-        if (item.encryption === 'true') {                                // 鍔犲瘑瀛楁
-          encrypts.push(item.field)
-        }
       } else if (item.type === 'link') {
         linkFields[item.linkField] = linkFields[item.linkField] || []
         linkFields[item.linkField].push(item.field)
@@ -121,7 +117,7 @@
       }
 
       let _fieldlen = item.fieldlength || 50
-      if (item.type === 'textarea' || item.type === 'fileupload' || item.type === 'multiselect') {
+      if (item.type === 'textarea' || item.type === 'fileupload' || item.type === 'multiselect' || item.type === 'brafteditor') {
         _fieldlen = item.fieldlength || 512
       } else if (item.type === 'number') {
         _fieldlen = item.decimal ? item.decimal : 0
@@ -200,14 +196,7 @@
         }
       }
 
-      // 鍔犲瘑瀛楁锛岃В瀵嗗鐞�
-      if (item.type === 'textarea' && item.encryption === 'true' && newval !== '') {
-        try {
-          newval = window.decodeURIComponent(window.atob(newval))
-        } catch (e) {
-          console.warn(e)
-        }
-      } else if (item.type === 'switch' && newval !== '') { // 寮�鍏冲彧鎺ユ敹鍥哄畾鍊�
+      if (item.type === 'switch' && newval !== '') { // 寮�鍏冲彧鎺ユ敹鍥哄畾鍊�
         if (newval !== item.closeVal && newval !== item.openVal) {
           newval = ''
         } else if (newval === item.openVal) {
@@ -239,6 +228,10 @@
 
       if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(item.type) && item.resourceType === '1') {
         deForms.push(item)
+      } else if (['select', 'link', 'radio'].includes(item.type) && item.resourceType !== '1') { // 閫変腑绗竴椤�
+        if (item.initval && item.initval.indexOf('$first') > -1) {
+          item.initval = item.options[0] ? item.options[0].Value : ''
+        }
       }
 
       return item
@@ -260,14 +253,13 @@
     })
 
     this.setState({
-      readtype: readtype,
-      datatype: datatype,
-      readin: readin,
-      writein: writein,
-      fieldlen: fieldlen,
-      encrypts: encrypts,
-      intercepts: intercepts,
-      formlist: formlist
+      readin,
+      writein,
+      readtype,
+      datatype,
+      fieldlen,
+      intercepts,
+      formlist
     }, () => {
       if (action.setting && action.setting.focus) {
         this.selectInput(action.setting.focus, 'init')
@@ -461,6 +453,7 @@
         }
         return item
       })
+      let values = []
 
       this.setState({
         formlist: _formlist.map(item => {
@@ -469,8 +462,21 @@
           } else if (['select', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(item.type)) {
             item.options = item.oriOptions
           }
+          if (['select', 'link', 'radio'].includes(item.type) && item.initval && item.initval.indexOf('$first') > -1) { // 閫変腑绗竴椤�
+            item.initval = item.options[0] ? item.options[0].Value : ''
+            values.push({field: item.field, value: item.initval})
+          }
           return item
         })
+      }, () => {
+        if (values.length === 0) return
+        let fieldsvalue = {}
+        values.forEach(item => {
+          if (this.props.form.getFieldValue(item.field) !== undefined) {
+            fieldsvalue[item.field] = item.value
+          }
+        })
+        this.props.form.setFieldsValue(fieldsvalue)
       })
     })
   }
@@ -554,6 +560,7 @@
         }
         return item
       })
+      let values = []
 
       this.setState({
         formlist: _formlist.map(item => {
@@ -562,8 +569,21 @@
           } else if (['select', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(item.type)) {
             item.options = item.oriOptions
           }
+          if (['select', 'link', 'radio'].includes(item.type) && item.initval && item.initval.indexOf('$first') > -1) { // 閫変腑绗竴椤�
+            item.initval = item.options[0] ? item.options[0].Value : ''
+            values.push({field: item.field, value: item.initval})
+          }
           return item
         })
+      }, () => {
+        if (values.length === 0) return
+        let fieldsvalue = {}
+        values.forEach(item => {
+          if (this.props.form.getFieldValue(item.field) !== undefined) {
+            fieldsvalue[item.field] = item.value
+          }
+        })
+        this.props.form.setFieldsValue(fieldsvalue)
       })
     })
   }
@@ -633,30 +653,18 @@
     }
 
     if (subfields.length === 0) {
-      if (Object.keys(fieldsvalue).length > 0) {
-        this.props.form.setFieldsValue(fieldsvalue)
-      }
-      if (Object.keys(_record).length > 0) {
-        this.setState({
-          record: {...record, ..._record}
-        })
-      }
+      this.props.form.setFieldsValue(fieldsvalue)
+      this.setState({
+        record: {...record, ..._record}
+      })
     } else {
       let result = this.resetform(formlist, subfields, 0, fieldsvalue)
 
-      if (Object.keys(result.fieldsvalue).length > 0) {
-        this.props.form.setFieldsValue(fieldsvalue)
-      }
-
-      let _param = {
-        formlist: result.formlist
-      }
-
-      if (Object.keys(_record).length > 0) {
-        _param.record = {...record, ..._record}
-      }
-
-      this.setState(_param)
+      this.props.form.setFieldsValue(fieldsvalue)
+      this.setState({
+        formlist: result.formlist,
+        record: {...record, ..._record}
+      })
     }
 
     this.setState({}, () => {
@@ -1182,7 +1190,34 @@
                   },
                   ..._rules
                 ]
-              })(<TextArea autoSize={{ minRows: 2, maxRows: item.maxRows || 6 }} disabled={item.readonly === 'true'} />)}
+              })(<CustomTextArea Item={item} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'brafteditor') {
+        let _max = item.fieldlength || 512
+
+        fields.push(
+          <Col span={24} key={index}>
+            <Form.Item label={item.hidelabel !== 'true' && item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : (item.hidelabel !== 'true' ? item.label : '')
+            }>
+              {getFieldDecorator(item.field, {
+                initialValue: item.initval || '',
+                rules: [
+                  {
+                    required: item.required === 'true',
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  },
+                  {
+                    max: _max,
+                    message: formRule.input.formMessage.replace('@max', _max)
+                  }
+                ]
+              })(<Editor Item={item}/>)}
             </Form.Item>
           </Col>
         )
@@ -1194,7 +1229,6 @@
 
   handleConfirm = () => {
     const { record, intercepts, writein } = this.state
-    let _encrypts = fromJS(this.state.encrypts).toJS()
     let _format = {
       date: 'YYYY-MM-DD',
       datemonth: 'YYYY-MM',
@@ -1225,7 +1259,6 @@
               let _val = item.initval
               if (record.hasOwnProperty(item.field)) {
                 _val = record[item.field]
-                _encrypts = _encrypts.filter(_field => _field !== item.field) // 闅愯棌瀛楁锛屼笉鍙備笌鍔犲瘑澶勭悊
               }
               
               _item = {
@@ -1251,12 +1284,18 @@
 
             if (!_item) return
 
-            if (item.type === 'date' || item.type === 'datemonth' || item.type === 'datetime') {
-              if (_item.value && _item.value.format) {
-                _item.value = _item.value.format(_format[item.type])
-              } else if (!_item.value) {
+            if (_item.value === undefined) {
+              _item.value = ''
+            } else if (item.type === 'date' || item.type === 'datemonth' || item.type === 'datetime') {
+              if (!_item.value) {
                 _item.value = ''
+              } else if (_item.value.format) {
+                _item.value = _item.value.format(_format[item.type])
               }
+            } else if (item.type === 'text' && _item.value && typeof(_item.value) === 'string') { // 鐗规畩瀛楁鏇挎崲
+              _item.value = _item.value.replace(/^(\s*)@appkey@(\s*)$/ig, window.GLOB.appkey)
+              _item.value = _item.value.replace(/^(\s*)@SessionUid@(\s*)$/ig, (localStorage.getItem('SessionUid') || ''))
+              _item.value = _item.value.replace(/^(\s*)@bid@(\s*)$/ig, (this.props.BID || ''))
             }
 
             search.push(_item)
@@ -1279,19 +1318,20 @@
             }
 
             let _value = ''
-            if (this.state.datatype[key] === 'datetime') {
+            let _type = this.state.datatype[key]
+            if (_type === 'datetime') {
               _value = values[key] ? moment(values[key]).format('YYYY-MM-DD HH:mm:ss') : ''
-            } else if (this.state.datatype[key] === 'datemonth') {
+            } else if (_type === 'datemonth') {
               _value = values[key] ? moment(values[key]).format('YYYY-MM') : ''
-            } else if (this.state.datatype[key] === 'date') {
+            } else if (_type === 'date') {
               _value = values[key] ? moment(values[key]).format('YYYY-MM-DD') : ''
-            } else if (this.state.datatype[key] === 'number') {
+            } else if (_type === 'number') {
               _value = values[key]
 
-            } else if (this.state.datatype[key] === 'multiselect' || this.state.datatype[key] === 'checkbox') {
+            } else if (_type === 'multiselect' || _type === 'checkbox') {
               _value = values[key] ? values[key].join(',') : ''
 
-            } else if (this.state.datatype[key] === 'fileupload') {
+            } else if (_type === 'fileupload') {
               let vals = []
 
               if (values[key] && values[key].length > 0) {
@@ -1305,15 +1345,23 @@
               }
 
               _value = vals.join(',')
-            } else if (this.state.datatype[key] === 'text' || this.state.datatype[key] === 'textarea') {
+            } else if (_type === 'text' || _type === 'textarea') {
               _value = values[key].replace(/\t*|\v*/g, '') // 鍘婚櫎鍒惰〃绗�
 
               if (intercepts.includes(key)) {              // 鍘婚櫎棣栧熬绌烘牸
                 _value = _value.replace(/(^\s*|\s*$)/g, '')
               }
+              if (_type === 'text' && _value) { // 鐗规畩瀛楁鏇挎崲
+                _value = _value.replace(/^(\s*)@appkey@(\s*)$/ig, window.GLOB.appkey)
+                _value = _value.replace(/^(\s*)@SessionUid@(\s*)$/ig, (localStorage.getItem('SessionUid') || ''))
+                _value = _value.replace(/^(\s*)@bid@(\s*)$/ig, (this.props.BID || ''))
+              }
             } else {
               _value = values[key]
-              
+            }
+
+            if (_value === undefined) {
+              _value = ''
             }
 
             search.push({
@@ -1326,23 +1374,6 @@
               value: _value
             })
           })
-
-          // 鍚湁鍔犲瘑瀛楁鏃讹紝瀵硅〃鍗曞�艰繘琛屽姞瀵�
-          if (_encrypts && _encrypts.length > 0) {
-            search = search.map(item => {
-              let _value = item.value
-              if (_encrypts.includes(item.key)) {
-                try {
-                  _value = window.btoa(window.encodeURIComponent(_value))
-                } catch (e) {
-                  console.warn(e)
-                }
-              }
-              item.value = _value
-
-              return item
-            })
-          }
 
           resolve(search)
         } else {
@@ -1373,7 +1404,6 @@
     return (
       <Form {...formItemLayout} className="ant-advanced-search-form main-form-field" id="main-form-box">
         <Row className={'cols' + cols} gutter={24}>{this.getFields()}</Row>
-        {/* <Editor /> */}
       </Form>
     )
   }
diff --git a/src/tabviews/zshare/mutilform/index.scss b/src/tabviews/zshare/mutilform/index.scss
index ac212f9..384c310 100644
--- a/src/tabviews/zshare/mutilform/index.scss
+++ b/src/tabviews/zshare/mutilform/index.scss
@@ -81,6 +81,11 @@
       min-width: 100px;
     }
   }
+  .normal-braft-editor {
+    border: 1px solid #d9d9d9;
+    border-radius: 4px;
+    overflow-x: hidden;
+  }
   p {
     color: #1890ff;
     border-bottom: 1px solid #d9d9d9;
diff --git a/src/tabviews/zshare/normalTable/index.jsx b/src/tabviews/zshare/normalTable/index.jsx
index 0d1e6dd..77e74d5 100644
--- a/src/tabviews/zshare/normalTable/index.jsx
+++ b/src/tabviews/zshare/normalTable/index.jsx
@@ -182,7 +182,7 @@
     if (rowspans.length === 0) {
       rowspans = null
     }
-
+    
     this.setState({
       columns: _columns,
       pageSize: pageSize ? pageSize : 10,
@@ -481,6 +481,21 @@
       }
 
       if (item.linkThdMenu || item.linkurl) {
+        if (item.rowspan === 'true') {
+          return {
+            children: (
+              <div className={className}>
+                <div className="baseboard link-menu" onDoubleClick={(e) => this.triggerLink(e, item, record)}></div>
+                <div className="content link-menu" onDoubleClick={(e) => this.triggerLink(e, item, record)}>
+                  {content}
+                </div>
+              </div>
+            ),
+            props: {
+              rowSpan: record['$$' + item.field],
+            }
+          }
+        }
         return (
           <div className={className}>
             <div className="baseboard link-menu" onDoubleClick={(e) => this.triggerLink(e, item, record)}></div>
@@ -490,6 +505,21 @@
           </div>
         )
       } else {
+        if (item.rowspan === 'true') {
+          return {
+            children: (
+              <div className={className}>
+                <div className="baseboard"></div>
+                <div className="content">
+                  {content}
+                </div>
+              </div>
+            ),
+            props: {
+              rowSpan: record['$$' + item.field],
+            }
+          }
+        }
         return (
           <div className={className}>
             <div className="baseboard"></div>
@@ -998,13 +1028,13 @@
         let preItem = data[index - 1]
         rowspans.forEach((cell, i) => {
           if (i === 0) {
-            if ((item[cell] || item[cell] === 0) && preItem[cell] === item[cell]) {
+            if (preItem[cell] === item[cell]) {
               item['$' + cell] = preItem['$' + cell] + 1
             } else {
               item['$' + cell] = 1
             }
           } else {
-            if ((item[cell] || item[cell] === 0) && preItem[cell] === item[cell]) {
+            if (preItem[cell] === item[cell]) {
               item['$' + cell] = preItem['$' + cell] + 1
             } else {
               item['$' + cell] = 1
diff --git a/src/tabviews/zshare/settingcomponent/index.jsx b/src/tabviews/zshare/settingcomponent/index.jsx
index e5820fb..a141601 100644
--- a/src/tabviews/zshare/settingcomponent/index.jsx
+++ b/src/tabviews/zshare/settingcomponent/index.jsx
@@ -5,7 +5,7 @@
 
 import Api from '@/api'
 import MKEmitter from '@/utils/events.js'
-import UtilsUpdate from '@/utils/utils-update.js'
+import { updateSubTable } from '@/utils/utils-update.js'
 import options from '@/store/options.js'
 import asyncComponent from '@/utils/asyncSpinComponent'
 import './index.scss'
@@ -252,7 +252,7 @@
 
           if (!subconfig || !subconfig.enabled) return
 
-          subconfig = UtilsUpdate.updateSubTable(subconfig)
+          subconfig = updateSubTable(subconfig)
 
           let _comp = {title: res.tab.label, uuid: res.tab.uuid, action: []}
 
diff --git a/src/templates/calendarconfig/tabcomponent/tabform/index.jsx b/src/templates/calendarconfig/tabcomponent/tabform/index.jsx
index 2d8b397..a02f6a5 100644
--- a/src/templates/calendarconfig/tabcomponent/tabform/index.jsx
+++ b/src/templates/calendarconfig/tabcomponent/tabform/index.jsx
@@ -23,9 +23,9 @@
   UNSAFE_componentWillMount () {
     const { formlist } = this.props
 
-    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
+    // let type = formlist.filter(cell => cell.key === 'type')[0].initVal
 
-    let _tabs = this.props.tabs.filter(tab => tab.type === type)
+    let _tabs = this.props.tabs.filter(tab => tab.type === 'SubTable')
 
     this.setState({
       formlist: formlist.map(item => {
@@ -247,6 +247,7 @@
           if (!values.linkTab) { // 娌℃湁鍏宠仈鏍囩锛堟柊寤烘椂锛夛紝鍒涘缓鏂版爣绛綢d
             values.linkTab = Utils.getuuid()
           }
+          values.type = 'SubTable' // 绫诲瀷涓哄瓙琛�
 
           resolve(values)
         } else {
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index df83c9e..2c00e6f 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -9,7 +9,7 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
-import UtilsUpdate from '@/utils/utils-update.js'
+import { updateCommonTable } from '@/utils/utils-update.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
@@ -90,7 +90,7 @@
     _config.easyCode = _config.easyCode || ''
 
     // 鐗堟湰鍏煎
-    _config = UtilsUpdate.updateCommonTable(_config)
+    _config = updateCommonTable(_config)
     
     let _oriActions = []
     if (_config.type === 'user') {
@@ -819,7 +819,7 @@
         _view = item.tabTemplate    // 鏂版爣绛鹃〉妯℃澘
         _btnTab = item
       } else if (type === 'button' && item.OpenType === 'popview') {
-        _view = item.tabType        // 鏂板脊绐楁爣绛炬ā鏉�
+        _view = item.tabType || 'SubTable' // 鏂板脊绐楁爣绛炬ā鏉� tabType 灞炴�у凡鍘婚櫎
         uuid = item.linkTab
         isbutton = false
       } else if (type === 'tab') {
@@ -973,9 +973,39 @@
       }
     })
     
+    config.action && config.action.forEach((btn) => {
+      if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+        let hascheck = false
+        btn.verify.scripts.forEach(item => {
+          if (item.status === 'false') return
+    
+          if (/\$check@|@check\$/ig.test(item.sql)) {
+            hascheck = true
+          }
+        })
+        if (hascheck) {
+          notification.warning({
+            top: 92,
+            message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+            duration: 5
+          })
+        }
+      }
+      if (btn.intertype === 'custom' && btn.callbackType === 'script' && (!btn.verify || !btn.verify.cbScripts || !btn.verify.cbScripts.filter(item => item.status !== 'false').length === 0)) {
+        notification.warning({
+          top: 92,
+          message: `鎸夐挳銆�${btn.label}銆嬫湭璁剧疆鍥炶皟鑴氭湰锛� 灏嗕笉浼氱敓鏁堬紒`,
+          duration: 5
+        })
+      }
+    })
 
-    if (config.setting.interType === 'system' && config.setting.default !== 'false' && !config.setting.dataresource) {
-      return '鑿滃崟灏氭湭璁剧疆鏁版嵁婧愶紝涓嶅彲鍚敤锛�'
+    if ((config.setting.interType === 'system' || config.setting.requestMode === 'system') && config.setting.default === 'false' && config.setting.scripts && config.setting.scripts.filter(item => item.status !== 'false').length === 0) {
+      return '鏁版嵁婧愪腑涓嶆墽琛岄粯璁ql锛屼笖鏈坊鍔犺嚜瀹氫箟鑴氭湰锛屼笉鍙惎鐢紒'
+    } else if (config.setting.interType === 'custom' && config.setting.procMode !== 'inner' && config.setting.preScripts && config.setting.preScripts.filter(item => item.status !== 'false').length === 0) {
+      return '鏁版嵁婧愭湭璁剧疆鍓嶇疆鑴氭湰锛屼笉鍙惎鐢紒'
+    } else if (config.setting.interType === 'custom' && config.setting.callbackType === 'script' && config.setting.cbScripts && config.setting.cbScripts.filter(item => item.status !== 'false').length === 0) {
+      return '鏁版嵁婧愭湭璁剧疆鍥炶皟鑴氭湰锛屼笉鍙惎鐢紒'
     } else if (!config.setting.primaryKey) {
       return '鑿滃崟灏氭湭璁剧疆涓婚敭锛屼笉鍙惎鐢紒'
     } else if (config.columns.length === 0) {
diff --git a/src/templates/formtabconfig/actionform/index.jsx b/src/templates/formtabconfig/actionform/index.jsx
index 798f054..db149bb 100644
--- a/src/templates/formtabconfig/actionform/index.jsx
+++ b/src/templates/formtabconfig/actionform/index.jsx
@@ -333,8 +333,14 @@
           <Col span={24} key={index}>
             <Form.Item label={item.label} className="textarea">
               {getFieldDecorator(item.key, {
-                initialValue: item.initVal
-              })(<TextArea rows={4} />)}
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={4} readOnly={item.readonly}/>)}
             </Form.Item>
           </Col>
         )
@@ -387,7 +393,7 @@
       }
     }
     return (
-      <Form {...formItemLayout} className="ant-advanced-search-form commontable-action-form" id="winter">
+      <Form {...formItemLayout} className="formtab-action-list-form" id="winter">
         <Row gutter={24}>{this.getFields()}</Row>
       </Form>
     )
diff --git a/src/templates/formtabconfig/actionform/index.scss b/src/templates/formtabconfig/actionform/index.scss
index c25cef2..2e104bd 100644
--- a/src/templates/formtabconfig/actionform/index.scss
+++ b/src/templates/formtabconfig/actionform/index.scss
@@ -1,4 +1,4 @@
-.ant-advanced-search-form.commontable-action-form {
+.formtab-action-list-form {
   min-height: 190px;
   .superconfig {
     color: #1890ff;
@@ -30,4 +30,12 @@
       top: 4.5px;
     }
   }
+  .ant-input:read-only {
+    background: #fafafa;
+    resize: none;
+  }
+  .ant-input:read-only:hover, .ant-input:read-only:focus {
+    border-color: #d9d9d9;
+    box-shadow: none;
+  }
 }
\ No newline at end of file
diff --git a/src/templates/formtabconfig/index.jsx b/src/templates/formtabconfig/index.jsx
index eab6c34..abd4a79 100644
--- a/src/templates/formtabconfig/index.jsx
+++ b/src/templates/formtabconfig/index.jsx
@@ -507,7 +507,6 @@
     let ableField = usefulFields.join(', ')
     let functip = <div>
       <p style={{marginBottom: '5px'}}>{this.state.dict['model.tooltip.func.innerface'].replace('@ableField', ableField)}</p>
-      <p>{this.state.dict['model.tooltip.func.outface']}</p>
     </div>
 
     this.setState({
diff --git a/src/templates/modalconfig/dragelement/card.jsx b/src/templates/modalconfig/dragelement/card.jsx
index 19e6785..75afee1 100644
--- a/src/templates/modalconfig/dragelement/card.jsx
+++ b/src/templates/modalconfig/dragelement/card.jsx
@@ -3,12 +3,15 @@
 import { Icon, Select, DatePicker, Input, InputNumber, Button, Popover, Switch, Radio, Checkbox } from 'antd'
 import moment from 'moment'
 
-import CheckCard from '../checkCard'
-import ColorSketch from '@/mob/colorsketch'
+import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
 
 const { MonthPicker } = DatePicker
 const { TextArea } = Input
+
+const Editor = asyncComponent(() => import('@/components/editor'))
+const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
+const CheckCard = asyncComponent(() => import('../checkCard'))
 
 const Card = ({ id, card, cols, moveCard, findCard, editCard, closeCard, copyCard, showField }) => {
   const originalIndex = findCard(id).index
@@ -63,7 +66,7 @@
   let wrapCol = 'ant-col-sm-16'
   let isEntireLine = false
 
-  if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard'].includes(card.type)) {
+  if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type)) {
     isEntireLine = true
   }
 
@@ -77,6 +80,9 @@
     } else if (cols === '4') {
       labelCol = 'ant-col-sm-2'
       wrapCol = 'ant-col-sm-22'
+    }
+    if (card.hidelabel === 'true') {
+      wrapCol = 'ant-col-sm-24'
     }
   }
 
@@ -97,6 +103,8 @@
     formItem = (<DatePicker showTime value={card.initval ? moment().subtract(card.initval, 'days') : null} />)
   } else if (card.type === 'textarea') {
     formItem = (<TextArea value={card.initval} autoSize={{ minRows: 2, maxRows: 6 }} />)
+  } else if (card.type === 'brafteditor') {
+    formItem = (<Editor />)
   } else if (card.type === 'fileupload') {
     formItem = (<Button style={{marginTop: '3px'}}><Icon type="upload" /> 鐐瑰嚮涓婁紶 </Button>)
   } else if (card.type === 'funcvar') {
@@ -141,11 +149,11 @@
       <div className="page-card" style={{ opacity: opacity}}>
         <div ref={node => drag(drop(node))}>
           {<div className="ant-row ant-form-item">
-            <div className={'ant-col ant-form-item-label ant-col-xs-24 ' + labelCol}>
+            {card.hidelabel !== 'true' ? <div className={'ant-col ant-form-item-label ant-col-xs-24 ' + labelCol}>
               {card.label ? <label className={card.required === 'true' ? 'required' : ''}>{card.tooltip ? 
                 <Icon type="question-circle" /> : null}
                 {card.label}</label> : null}
-            </div>
+            </div> : null}
             <div className={'ant-col ant-form-item-control-wrapper ant-col-xs-24 ' + wrapCol}>
               {formItem}
               {showField ? card.field : ''}
diff --git a/src/templates/modalconfig/dragelement/index.jsx b/src/templates/modalconfig/dragelement/index.jsx
index 5f978c3..bcfe294 100644
--- a/src/templates/modalconfig/dragelement/index.jsx
+++ b/src/templates/modalconfig/dragelement/index.jsx
@@ -128,7 +128,7 @@
       {cards.map(card => {
         let isEntireLine = false
 
-        if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard'].includes(card.type)) {
+        if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type)) {
           isEntireLine = true
         }
         
diff --git a/src/templates/modalconfig/index.scss b/src/templates/modalconfig/index.scss
index 8d3e188..9dd5236 100644
--- a/src/templates/modalconfig/index.scss
+++ b/src/templates/modalconfig/index.scss
@@ -252,6 +252,10 @@
                   min-width: 100px;
                 }
               }
+              .normal-braft-editor {
+                border: 1px solid #d9d9d9;
+                border-radius: 4px;
+              }
             }
             .ant-form-item-control-wrapper::after {
               content: '';
diff --git a/src/templates/modalconfig/source.jsx b/src/templates/modalconfig/source.jsx
index 0148cdb..9edd2ae 100644
--- a/src/templates/modalconfig/source.jsx
+++ b/src/templates/modalconfig/source.jsx
@@ -170,6 +170,12 @@
   },
   {
     type: 'form',
+    label: '瀵屾枃鏈�',
+    subType: 'brafteditor',
+    url: ''
+  },
+  {
+    type: 'form',
     label: CommonDict['header.form.funcvar'],
     subType: 'funcvar',
     url: ''
diff --git a/src/templates/sharecomponent/actioncomponent/actionform/index.jsx b/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
index 6516f5f..464e035 100644
--- a/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
@@ -36,6 +36,7 @@
     interType: null, // 鎺ュ彛绫诲瀷锛氬唴閮ㄣ�佸閮�
     funcType: null,  // 鍔熻兘绫诲瀷
     position: null,  // 鎸夐挳浣嶇疆
+    procMode: null,  // 澶栭儴鎺ュ彛鍙傛暟澶勭悊鏂瑰紡
     pageTemplate: null,
     requireOptions: [{
       value: 'notRequired',
@@ -69,6 +70,19 @@
     }, {
       value: 'custom',
       text: this.props.dict['header.form.custom']
+    }],
+    interTypeOptions: [{
+      value: 'system',
+      text: this.props.dict['model.interface.system']
+    }, {
+      value: 'inner',
+      text: this.props.dict['model.interface.inner']
+    }, {
+      value: 'outer',
+      text: this.props.dict['model.interface.outer']
+    }, {
+      value: 'custom',
+      text: '鑷畾涔�'
     }]
   }
 
@@ -77,8 +91,9 @@
     const { card } = this.props
 
     let _opentype = card.OpenType               // 鎵撳紑鏂瑰紡
-    let _tabType = card.tabType || 'SubTable'   // 鎸夐挳涓哄脊绐楋紙鏍囩锛夋椂锛屾爣绛剧殑绫诲瀷
+    // let _tabType = card.tabType || 'SubTable'   // 鎸夐挳涓哄脊绐楋紙鏍囩锛夋椂锛屾爣绛剧殑绫诲瀷
     let _intertype = card.intertype || 'system' // 鎺ュ彛绫诲瀷
+    let _procMode = card.procMode || 'system'   // 鍙傛暟澶勭悊鏂瑰紡
     let _funcType = card.funcType || ''         // 鍔熻兘鎸夐挳榛樿绫诲瀷
     let _tabTemplate = card.tabTemplate         // 鎸夐挳涓烘爣绛鹃〉鏃讹紝鏍囩绫诲瀷锛氫笁绾ц彍鍗曟垨琛ㄥ崟鏍囩椤�
     let _pageTemplate = card.pageTemplate       // 鏂伴〉闈㈢被鍨�
@@ -90,20 +105,26 @@
       _opentype = 'tab'
     }
 
-    let _tabs = this.props.tabs.filter(tab => tab.type === _tabType)
-    let _options = this.getOptions(_opentype, _intertype, _funcType, _pageTemplate, _tabTemplate)
+    let _tabs = this.props.tabs.filter(tab => tab.type === 'SubTable')
+    let _options = this.getOptions(_opentype, _intertype, _funcType, _pageTemplate, _tabTemplate, _procMode)
     
     this.setState({
       openType: _opentype,
       pageTemplate: _pageTemplate,
       interType: _intertype,
+      procMode: _procMode,
       position: card.position || 'toolbar',
       funcType: _funcType,
       formlist: this.props.formlist.map(item => {
         if (item.key === 'class') {
           item.options = btnClasses
+        } else if (item.key === 'innerFunc' && _procMode === 'inner') {
+          item.required = true
         } else if (item.key === 'icon') {
           item.options = btnIcons
+        } else if (item.key === 'intertype') {
+          let iscustom = ['pop', 'prompt', 'exec'].includes(_opentype)
+          item.options = this.state.interTypeOptions.filter(op => (iscustom || op.value !== 'custom'))
         } else if (item.key === 'Ot') {
           if (card.position === 'grid' || _pageTemplate === 'pay') { // 琛岀骇鎸夐挳銆佹敮浠樻寜閽紝鍙兘閫夊崟琛�
             item.options = this.state.requireOptions.filter(op => ['requiredSgl'].includes(op.value))
@@ -153,7 +174,7 @@
     }
   }
 
-  getOptions = (_opentype, _intertype, _funcType, _pageTemplate, _tabTemplate) => {
+  getOptions = (_opentype, _intertype, _funcType, _pageTemplate, _tabTemplate, _procMode) => {
     let _options = fromJS(actionTypeOptions[_opentype]).toJS() // 閫夐」鍒楄〃
     
     if (_opentype === 'innerpage') {         // 鏂伴〉闈紝鍙�夋ā鏉�(鑷畾涔夋椂锛屽彲濉叆澶栭儴閾炬帴)
@@ -168,13 +189,13 @@
       }
     } else if (_opentype === 'excelOut') {    // 瀵煎叆瀵煎嚭
       if (_intertype === 'outer') {
-        _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc')
+        _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc')
       } else if (_intertype === 'inner') {
         _options.push('innerFunc')
       }
     } else if (_opentype === 'excelIn') {    // 瀵煎叆瀵煎嚭
       if (_intertype === 'outer') {
-        _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+        _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
       } else if (_intertype === 'inner') {
         _options.push('innerFunc')
       }
@@ -182,14 +203,21 @@
       if (_funcType === 'print') {
         _options.push('execMode', 'intertype', 'Ot', 'execSuccess', 'execError')
         if (_intertype === 'outer') {
-          _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+          _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
         } else if (_intertype === 'inner') {
           _options.push('innerFunc')
         }
       }
     } else if (_opentype !== 'popview') { // 鎵撳紑鏂瑰紡涓嶆槸寮圭獥椤甸潰鏃�
-      if (_intertype === 'outer') {
-        _options.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+      if (_intertype === 'custom') {
+        _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method')
+        if (_procMode === 'system') {
+          _options.push('sql', 'sqlType')
+        } else {
+          _options.push('innerFunc')
+        }
+      } else if (_intertype === 'outer') {
+        _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
       } else if (_intertype === 'inner') {
         _options.push('innerFunc')
       } else {
@@ -205,24 +233,27 @@
   }
 
   /**
-   * @description 涓嬫媺鍒囨崲
-   * 1銆佹墦寮�鏂瑰紡鍒囨崲锛岄噸缃彲瑙佽〃鍗曞拰琛ㄥ崟鍊�
-   * 2銆佹樉绀轰綅缃垏鎹紝閲嶇疆閫夋嫨琛�
-   * 3銆佸垏鎹㈡爣绛剧被鍨嬶紝閲嶇疆鍙�夋爣绛�
+   * @description 鍒囨崲
    */
-  openTypeChange = (key, value) => {
+  optionChange = (key, value) => {
+    const { openType, funcType, procMode } = this.state
     const { card } = this.props
 
     if (key === 'OpenType') {
-      let _options = this.getOptions(value, this.state.interType, this.state.funcType, this.state.pageTemplate, card.tabTemplate)
+      let _options = this.getOptions(value, 'system', '', this.state.pageTemplate, card.tabTemplate, 'system')
       let _fieldval = {}
       let _formlist = this.state.formlist.map(item => {
         item.hidden = !_options.includes(item.key)
 
+        if (item.key === 'intertype') {
+          let iscustom = ['pop', 'prompt', 'exec'].includes(value)
+          item.options = this.state.interTypeOptions.filter(op => (iscustom || op.value !== 'custom'))
+        }
+
         if (item.hidden) return item
 
         if (item.key === 'intertype') {
-          _fieldval.intertype = this.state.interType
+          _fieldval.intertype = 'system'
         } else if (item.key === 'Ot') {
           if (this.state.position === 'grid' || this.state.pageTemplate === 'pay') {
             item.options = this.state.requireOptions.filter(op => ['requiredSgl'].includes(op.value))
@@ -253,6 +284,9 @@
 
       this.setState({
         openType: value,
+        funcType: '',
+        intertype: 'system',
+        procMode: 'system',
         formlist: _formlist
       }, () => {
         if (value === 'excelIn') {
@@ -275,10 +309,10 @@
             if (value === 'grid' || this.state.pageTemplate === 'pay') {
               item.options = this.state.requireOptions.filter(op => ['requiredSgl'].includes(op.value))
               _fieldval.Ot = 'requiredSgl'
-            } else if (this.state.openType === 'innerpage' && this.state.pageTemplate === 'billprint') {
+            } else if (openType === 'innerpage' && this.state.pageTemplate === 'billprint') {
               item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl', 'required'].includes(op.value))
               _fieldval.Ot = 'requiredSgl'
-            } else if (['innerpage', 'blank', 'tab', 'popview'].includes(this.state.openType)) {
+            } else if (['innerpage', 'blank', 'tab', 'popview'].includes(openType)) {
               item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
               _fieldval.Ot = 'requiredSgl'
             } else {
@@ -290,28 +324,28 @@
       }, () => {
         this.props.form.setFieldsValue(_fieldval)
       })
-    } else if (key === 'tabType') {
-      let _tabs = this.props.tabs.filter(tab => tab.type === value)
-      let _fieldval = {}
+    // } else if (key === 'tabType') {
+    //   let _tabs = this.props.tabs.filter(tab => tab.type === value)
+    //   let _fieldval = {}
 
-      this.setState({
-        formlist: this.state.formlist.map(item => {
-          if (item.key === 'linkTab') {
-            item.options = [
-              {
-                value: '',
-                text: '鏂板缓'
-              },
-              ..._tabs
-            ]
-          }
-          return item
-        })
-      }, () => {
-        this.props.form.setFieldsValue(_fieldval)
-      })
+    //   this.setState({
+    //     formlist: this.state.formlist.map(item => {
+    //       if (item.key === 'linkTab') {
+    //         item.options = [
+    //           {
+    //             value: '',
+    //             text: '鏂板缓'
+    //           },
+    //           ..._tabs
+    //         ]
+    //       }
+    //       return item
+    //     })
+    //   }, () => {
+    //     this.props.form.setFieldsValue(_fieldval)
+    //   })
     } else if (key === 'funcType') {
-      let _options = this.getOptions('funcbutton', this.state.interType, value, card.pageTemplate, card.tabTemplate)
+      let _options = this.getOptions('funcbutton', this.state.interType, value, card.pageTemplate, card.tabTemplate, procMode)
       let _fieldval = {}
 
       this.setState({
@@ -365,7 +399,7 @@
         this.props.form.setFieldsValue(_fieldval)
       })
     } else if (key === 'pageTemplate') {
-      let _options = this.getOptions('innerpage', this.state.interType, this.state.funcType, value, card.tabTemplate)
+      let _options = this.getOptions('innerpage', this.state.interType, this.state.funcType, value, card.tabTemplate, procMode)
       let _fieldval = {}
 
       this.setState({
@@ -405,15 +439,8 @@
           return item
         })
       })
-    }
-  }
-
-  onChange = (e, key) => {
-    const { openType, funcType } = this.state
-    let value = e.target.value
-
-    if (key === 'intertype') {
-      let _options = this.getOptions(openType, value, funcType, '', '')
+    } else if (key === 'intertype') {
+      let _options = this.getOptions(openType, value, funcType, '', '', procMode)
 
       this.setState({
         interType: value,
@@ -431,10 +458,20 @@
           }
           return item
         })
-      }, () => {
-        if (this.props.form.getFieldValue('sqlType') !== undefined) {
-          this.props.form.setFieldsValue({sqlType: ''})
-        }
+      })
+    } else if (key === 'procMode') {
+      let _options = this.getOptions(openType, this.state.interType, funcType, '', '', value)
+
+      this.setState({
+        procMode: value,
+        formlist: this.state.formlist.map(item => {
+          item.hidden = !_options.includes(item.key)
+
+          if (item.key === 'innerFunc') {
+            item.required = true
+          }
+          return item
+        })
       })
     } else if (key === 'sysInterface') {
       if (value === 'true') {
@@ -560,7 +597,7 @@
                 <Select
                   showSearch
                   filterOption={(input, option) => option.props.children[2].toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  onChange={(value) => {this.openTypeChange(item.key, value)}}
+                  onChange={(value) => {this.optionChange(item.key, value)}}
                   getPopupContainer={() => document.getElementById('winter')}
                 >
                   {item.options.map((option, index) =>
@@ -586,7 +623,7 @@
                   }
                 ]
               })(
-                <Radio.Group onChange={(e) => {this.onChange(e, item.key)}} disabled={item.readonly}>
+                <Radio.Group onChange={(e) => {this.optionChange(item.key, e.target.value)}} disabled={item.readonly}>
                   {
                     item.options.map(option => {
                       return (
@@ -604,8 +641,14 @@
           <Col span={24} key={index}>
             <Form.Item label={item.label} className="textarea">
               {getFieldDecorator(item.key, {
-                initialValue: item.initVal
-              })(<TextArea rows={4} />)}
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={2} readOnly={item.readonly} />)}
             </Form.Item>
           </Col>
         )
@@ -688,7 +731,7 @@
       }
     }
     return (
-      <Form {...formItemLayout} className="ant-advanced-search-form commontable-action-form" id="winter">
+      <Form {...formItemLayout} className="normal-action-list-form" id="winter">
         <Row gutter={24}>{this.getFields()}</Row>
       </Form>
     )
diff --git a/src/templates/sharecomponent/actioncomponent/actionform/index.scss b/src/templates/sharecomponent/actioncomponent/actionform/index.scss
index c25cef2..653ac93 100644
--- a/src/templates/sharecomponent/actioncomponent/actionform/index.scss
+++ b/src/templates/sharecomponent/actioncomponent/actionform/index.scss
@@ -1,4 +1,4 @@
-.ant-advanced-search-form.commontable-action-form {
+.normal-action-list-form {
   min-height: 190px;
   .superconfig {
     color: #1890ff;
@@ -10,6 +10,12 @@
     }
     .ant-col-sm-17 {
       width: 86%;
+    }
+  }
+  .ant-radio-group {
+    white-space: nowrap;
+    .ant-radio-wrapper {
+      margin-right: 4px;
     }
   }
   .ant-input-number {
@@ -30,4 +36,12 @@
       top: 4.5px;
     }
   }
+  .ant-input:read-only {
+    background: #fafafa;
+    resize: none;
+  }
+  .ant-input:read-only:hover, .ant-input:read-only:focus {
+    border-color: #d9d9d9;
+    box-shadow: none;
+  }
 }
\ No newline at end of file
diff --git a/src/templates/sharecomponent/actioncomponent/index.jsx b/src/templates/sharecomponent/actioncomponent/index.jsx
index 3fe8be4..4392c36 100644
--- a/src/templates/sharecomponent/actioncomponent/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/index.jsx
@@ -154,7 +154,6 @@
     let ableField = usefulFields.join(', ')
     let functip = <div>
       <p style={{marginBottom: '5px'}}>{this.state.dict['model.tooltip.func.innerface'].replace('@ableField', ableField)}</p>
-      <p>{this.state.dict['model.tooltip.func.outface']}</p>
     </div>
 
     let menulist = []
@@ -886,8 +885,9 @@
         {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
         <Modal
           title={dict['model.action'] + '-' + (card && card.copyType === 'action' ? dict['model.copy'] : dict['model.edit'])}
+          wrapClassName="model-table-action-edit-modal"
           visible={visible}
-          width={800}
+          width={850}
           maskClosable={false}
           onCancel={this.editModalCancel}
           footer={[
diff --git a/src/templates/sharecomponent/actioncomponent/index.scss b/src/templates/sharecomponent/actioncomponent/index.scss
index 7a01fd3..2226cde 100644
--- a/src/templates/sharecomponent/actioncomponent/index.scss
+++ b/src/templates/sharecomponent/actioncomponent/index.scss
@@ -34,6 +34,12 @@
   }
 }
 
+.model-table-action-edit-modal {
+  .ant-modal {
+    top: 60px;
+    padding-bottom: 5px;
+  }
+}
 .model-table-action-verify-modal {
   .ant-modal {
     top: 50px;
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
index 3a5c827..3268132 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
@@ -259,9 +259,9 @@
               {btn.sheet}
             </Form.Item>
           </Col> : null}
-          <Col span={16}>
-            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
-              ErrorCode, retmsg
+          <Col span={10}>
+            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0, whiteSpace: 'nowrap'}}>
+              ErrorCode锛堝鍔犲悗缂�NT琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT锛�, retmsg
             </Form.Item>
           </Col>
           {usefulfields ? <Col span={24} className="sqlfield">
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
index 398024f..adca1a9 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
@@ -299,7 +299,7 @@
           systemScripts: res.data.map(item => {
             return {
               name: item.funcname,
-              value: Utils.UnformatOptions(item.longparam)
+              value: window.decodeURIComponent(window.atob(item.longparam))
             }
           })
         })
diff --git a/src/templates/sharecomponent/columncomponent/columnform/index.jsx b/src/templates/sharecomponent/columncomponent/columnform/index.jsx
index 71f3e5a..a955bd2 100644
--- a/src/templates/sharecomponent/columncomponent/columnform/index.jsx
+++ b/src/templates/sharecomponent/columncomponent/columnform/index.jsx
@@ -7,7 +7,7 @@
 
 const columnTypeOptions = {
   text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'fieldlength', 'blacklist', 'perspective', 'rowspan'],
-  number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum'],
+  number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum', 'rowspan'],
   link: ['label', 'field', 'type', 'nameField', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'fieldlength', 'blacklist'],
   textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'fieldlength', 'blacklist'],
   picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'fieldlength', 'blacklist', 'scale', 'maxHeight']
diff --git a/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx b/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx
index 0ec28a2..38a6001 100644
--- a/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx
@@ -9,13 +9,13 @@
 
 import asyncComponent from '@/utils/asyncComponent'
 import ColForm from './columnform'
-import CustomScript from '@/templates/zshare/customscript'
 import SettingForm from './settingform'
 import SettingUtils from './utils'
 import './index.scss'
 
 const { TabPane } = Tabs
 
+const CustomScript = asyncComponent(() => import('@/templates/zshare/customscript'))
 const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
 
 class VerifyCard extends Component {
diff --git a/src/templates/sharecomponent/settingcomponent/index.scss b/src/templates/sharecomponent/settingcomponent/index.scss
index 135e66c..41b2501 100644
--- a/src/templates/sharecomponent/settingcomponent/index.scss
+++ b/src/templates/sharecomponent/settingcomponent/index.scss
@@ -15,6 +15,7 @@
     padding-bottom: 5px;
     .ant-modal-body {
       max-height: calc(100vh - 190px);
+      min-height: 250px;
       overflow-y: auto;
       padding-top: 10px;
     }
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
index c1c0e77..e303c63 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
@@ -9,6 +9,8 @@
 import CodeMirror from '@/templates/zshare/codemirror'
 import './index.scss'
 
+const { TextArea } = Input
+
 class SettingForm extends Component {
   static propTpyes = {
     config: PropTypes.object,     // 椤甸潰閰嶇疆
@@ -17,10 +19,13 @@
     setting: PropTypes.object,    // 鏁版嵁婧愰厤缃�
     columns: PropTypes.array,     // 鍒楄缃�
     scripts: PropTypes.array,     // 鑷畾涔夎剼鏈�
+    updateStatus: PropTypes.func, // 鐘舵�佹洿鏂�
   }
 
   state = {
     interType: 'system',
+    procMode: 'script',
+    requestMode: 'system',
     funcTooltip: '',
     funcRules: []
   }
@@ -55,6 +60,8 @@
 
     this.setState({
       interType: setting.interType || 'system',
+      procMode: setting.procMode || 'script',
+      requestMode: setting.requestMode || 'system',
       funcTooltip: tooltip,
       funcRules: rules
     })
@@ -156,13 +163,20 @@
       this.setState({
         interType: value
       })
-    } else if (key === 'sysInterface') {
-      if (value === 'true') {
-        this.props.form.setFieldsValue({
-          interface: window.GLOB.mainSystemApi || ''
-        })
-      }
+    } else if (key === 'procMode') {
+      this.setState({
+        procMode: value
+      })
+    } else if (key === 'requestMode') {
+      this.setState({
+        requestMode: value
+      })
+    } else if (key === 'sysInterface' && value === 'true') {
+      this.props.form.setFieldsValue({
+        interface: window.GLOB.mainSystemApi || ''
+      })
     }
+    this.props.updateStatus({[key]: value})
   }
 
   primaryKeyChange = (val) => {
@@ -176,7 +190,7 @@
   render() {
     const { setting, dict, menu, config, columns } = this.props
     const { getFieldDecorator } = this.props.form
-    const { interType, funcRules, funcTooltip } = this.state
+    const { interType, funcRules, funcTooltip, procMode, requestMode } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -230,10 +244,11 @@
                     },
                   ]
                 })(
-                <Radio.Group onChange={(e) => {this.onRadioChange(e, 'interType')}}>
+                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onRadioChange(e, 'interType')}}>
                   <Radio value="system">绯荤粺</Radio>
                   <Radio value="inner">鍐呴儴</Radio>
                   <Radio value="outer">澶栭儴</Radio>
+                  <Radio value="custom">鑷畾涔�</Radio>
                 </Radio.Group>)}
               </Form.Item>
             </Col>
@@ -254,7 +269,106 @@
                 </Radio.Group>)}
               </Form.Item>
             </Col> : null}
-            {interType === 'inner' ? <Col span={12}>
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label="鍙傛暟澶勭悊">
+                {getFieldDecorator('procMode', {
+                  initialValue: procMode,
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.select'] + '鍙傛暟澶勭悊鏂瑰紡!'
+                    },
+                  ]
+                })(
+                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onRadioChange(e, 'procMode')}}>
+                  <Radio value="script">鍓嶇疆鑴氭湰</Radio>
+                  <Radio value="inner">鍓嶇疆鍑芥暟</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' && procMode === 'inner' ? <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title={funcTooltip}>
+                  <Icon type="question-circle" />
+                  鍓嶇疆鍑芥暟
+                </Tooltip>
+              }>
+                {getFieldDecorator('prevFunc', {
+                  initialValue: setting.prevFunc || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '鍓嶇疆鍑芥暟!'
+                    },
+                    {
+                      max: formRule.func.max,
+                      message: formRule.func.maxMessage
+                    },
+                    ...funcRules
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'outer' || interType === 'custom' ? <Col className="data-source" span={24}>
+              <Form.Item label="娴嬭瘯鍦板潃">
+                {getFieldDecorator('interface', {
+                  initialValue: setting.interface || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '娴嬭瘯鍦板潃!'
+                    },
+                  ]
+                })(<TextArea rows={2} />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'outer' || interType === 'custom' ? <Col className="data-source" span={24}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="姝e紡绯荤粺鎵�浣跨敤鐨勭殑鎺ュ彛鍦板潃銆�">
+                  <Icon type="question-circle" />
+                  姝e紡鍦板潃
+                </Tooltip>
+              }>
+                {getFieldDecorator('proInterface', {
+                  initialValue: setting.proInterface || ''
+                })(<TextArea rows={2} />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label="璇锋眰鏂瑰紡">
+                {getFieldDecorator('method', {
+                  initialValue: setting.method || 'post',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.select'] + '璇锋眰鏂瑰紡!'
+                    },
+                  ]
+                })(
+                <Radio.Group>
+                  <Radio value="get">GET</Radio>
+                  <Radio value="post">POST</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label="鏁版嵁璇锋眰">
+                {getFieldDecorator('requestMode', {
+                  initialValue: requestMode,
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.select'] + '鏁版嵁璇锋眰鏂瑰紡!'
+                    },
+                  ]
+                })(
+                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onRadioChange(e, 'requestMode')}}>
+                  <Radio value="system">绯荤粺鍑芥暟</Radio>
+                  <Radio value="inner">鍐呴儴鍑芥暟</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'inner' || (interType === 'custom' && requestMode === 'inner') ? <Col span={12}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={funcTooltip}>
                   <Icon type="question-circle" />
@@ -278,19 +392,6 @@
               </Form.Item>
             </Col> : null}
             {interType === 'outer' ? <Col span={12}>
-              <Form.Item label="鎺ュ彛鍦板潃">
-                {getFieldDecorator('interface', {
-                  initialValue: setting.interface || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: dict['form.required.input'] + '鎺ュ彛鍦板潃!'
-                    },
-                  ]
-                })(<Input placeholder={''} autoComplete="off" />)}
-              </Form.Item>
-            </Col> : null}
-            {interType === 'outer' ? <Col span={12}>
               <Form.Item label="澶栭儴鍑芥暟">
                 {getFieldDecorator('outerFunc', {
                   initialValue: setting.outerFunc || '',
@@ -306,7 +407,7 @@
                 })(<Input placeholder={''} autoComplete="off" />)}
               </Form.Item>
             </Col> : null}
-            {interType === 'system' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
+            {interType === 'system' || (interType === 'custom' && requestMode === 'system') ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
               <Form.Item help={'鏁版嵁ID锛�' + menu.MenuID} labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} } label={
                 <Tooltip placement="topLeft" title={'浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩�傛敞锛氭暟鎹潈闄愭浛鎹㈢ $@ -> /* 鎴� \'\'銆� @$ -> */ 鎴� \'\''}>
                   <Icon type="question-circle" />
@@ -318,7 +419,7 @@
                 })(<CodeMirror />)}
               </Form.Item>
             </Col> : null}
-            {interType === 'system' ? <Col span={12}>
+            {interType === 'system' || (interType === 'custom' && requestMode === 'system') ? <Col span={12}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'鏌ヨ鏃讹紝鎼滅储鏉′欢浠here鏉′欢鎷兼帴杩涘叆sql锛岀粺璁℃椂锛屽皢鏁版嵁婧愪腑浠モ�淍+鎼滅储瀛楁+@鈥濈殑鍐呭锛屼互鎼滅储鏉′欢涓殑鍊艰繘琛屾浛鎹㈠悗锛屾彁浜ゆ煡璇紝娉細鏌ヨ绫诲瀷浠呭湪浣跨敤绯荤粺鍑芥暟鏃舵湁鏁堛��'}>
                   <Icon type="question-circle" />
@@ -334,7 +435,7 @@
                 </Radio.Group>)}
               </Form.Item>
             </Col> : null}
-            {interType === 'system' ? <Col span={12}>
+            {interType === 'system' || (interType === 'custom' && requestMode === 'system') ? <Col span={12}>
               <Form.Item label="榛樿sql">
                 {getFieldDecorator('default', {
                   initialValue: setting.default || 'true'
@@ -378,6 +479,61 @@
                 })(<Input placeholder={'ID asc, UID desc'} autoComplete="off" />)}
               </Form.Item>
             </Col>
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label="鍥炶皟鏂瑰紡">
+                {getFieldDecorator('callbackType', {
+                  initialValue: setting.callbackType || 'script'
+                })(
+                <Radio.Group onChange={(e) => {this.onRadioChange(e, 'callbackType')}}>
+                  <Radio value="default">榛樿鑴氭湰</Radio>
+                  <Radio value="script">鑷畾涔夎剼鏈�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label="鍥炶皟琛ㄥ悕">
+                {getFieldDecorator('cbTable', {
+                  initialValue: setting.cbTable || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: dict['form.required.input'] + '鍥炶皟琛ㄥ悕!'
+                    },
+                    {
+                      max: formRule.input.max,
+                      message: formRule.input.message
+                    }
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title={'鍚屾鎵ц锛氬閮ㄦ帴鍙h皟鐢ㄦ垚鍔熷悗鍐嶈姹傛暟鎹紱寮傛鎵ц锛氬閮ㄦ帴鍙h皟鐢ㄤ笌璇锋眰鏁版嵁鍚屾椂杩涜銆�'}>
+                  <Icon type="question-circle" />
+                  鎵ц鏂瑰紡
+                </Tooltip>
+              }>
+                {getFieldDecorator('execType', {
+                  initialValue: setting.execType || 'sync'
+                })(
+                <Radio.Group>
+                  <Radio value="sync">鍚屾</Radio>
+                  <Radio value="async">寮傛</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' ? <Col span={12}>
+              <Form.Item label="鎵ц娆℃暟">
+                {getFieldDecorator('execTime', {
+                  initialValue: setting.execTime || 'once'
+                })(
+                <Radio.Group>
+                  <Radio value="always">濮嬬粓</Radio>
+                  <Radio value="once">涓�娆�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
             <Col span={12}>
               <Form.Item label="鏄惁鍒嗛〉">
                 {getFieldDecorator('laypage', {
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
index f7a193e..f6474c0 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
@@ -7,11 +7,13 @@
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
 import SettingUtils from './utils.jsx'
-import CustomScript from '@/templates/zshare/customscript'
+import asyncComponent from '@/utils/asyncComponent'
 import DataSource from './datasource'
 import './index.scss'
 
 const { TabPane } = Tabs
+const CustomScript = asyncComponent(() => import('@/templates/zshare/customscript'))
+const SimpleScript = asyncComponent(() => import('./simplescript'))
 
 class SettingForm extends Component {
   static propTpyes = {
@@ -29,7 +31,8 @@
     arr_field: '',
     regoptions: [],
     setting: null,
-    defaultSql: ''
+    defaultSql: '',
+    status: {}
   }
 
   UNSAFE_componentWillMount() {
@@ -37,6 +40,8 @@
 
     let _setting = fromJS(config.setting).toJS()
     let _scripts = _setting.scripts || []
+    let _preScripts = _setting.preScripts || []
+    let _cbScripts = _setting.cbScripts || []
 
     _setting.default = _setting.default || 'true'            // 榛樿sql
     _setting.sysInterface = _setting.sysInterface || 'false' // 鏄惁涓虹郴缁熸帴鍙�
@@ -76,13 +81,21 @@
     _search = _search.replace(/@\$@/ig, '')
     _search = _search ? 'where ' + _search : ''
 
+    let status = fromJS(_setting).toJS()
+    status.requestMode = status.requestMode || 'system'
+    status.procMode = status.procMode || 'script'
+    status.callbackType = status.callbackType || 'script'
+
     this.setState({
       setting: _setting,
       search: _search,
       arr_field: arr_field.join(','),
       regoptions: Utils.getRegOptions(search), // 鎼滅储鏉′欢锛屾鍒欐浛鎹�
       columns: columns,
-      scripts: _scripts
+      scripts: _scripts,
+      preScripts: _preScripts,
+      cbScripts: _cbScripts,
+      status
     })
   }
 
@@ -141,13 +154,29 @@
   }
 
   handleConfirm = (trigger) => {
-    const { activeKey, setting, scripts } = this.state
+    const { activeKey, setting, scripts, preScripts, cbScripts } = this.state
+
+    let _loading = false
+    if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    } else if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    }
+
+    if (_loading) {
+      notification.warning({
+        top: 92,
+        message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
+        duration: 5
+      })
+      return Promise.reject()
+    }
 
     if (trigger) {
       this.setState({loading: true})
     }
-
-    let _scripts = scripts.filter(script => script.status !== 'false')
 
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     if (activeKey === 'setting') {
@@ -171,14 +200,6 @@
             this.setState({loading: false})
             reject()
             return
-          } else if (res.interType === 'system' && res.default === 'false' && _scripts.length === 0) {
-            notification.warning({
-              top: 92,
-              message: '涓嶆墽琛岄粯璁ql鏃讹紝璇锋坊鍔犺嚜瀹氫箟鑴氭湰锛�',
-              duration: 5
-            })
-            reject()
-            return
           }
 
           this.setState({
@@ -186,7 +207,7 @@
           }, () => {
             this.sqlverify(() => {
               this.setState({loading: false})
-              resolve({...res, scripts})
+              resolve({...res, scripts, preScripts, cbScripts})
             }, () => {
               this.setState({loading: false})
               reject()
@@ -197,15 +218,8 @@
           reject()
         })
       })
-    } else {
+    } else if (activeKey === 'scripts') {
       return new Promise((resolve, reject) => {
-        let _loading = false
-        if (this.scriptsForm && this.scriptsForm.state.editItem) {
-          _loading = true
-        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
-          _loading = true
-        }
-  
         if (trigger === 'func' && setting.interType !== 'inner') {
           notification.warning({
             top: 92,
@@ -214,31 +228,20 @@
           })
           this.setState({loading: false})
           reject()
-        } else if (_loading) {
-          notification.warning({
-            top: 92,
-            message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
-            duration: 5
-          })
-          this.setState({loading: false})
-          reject()
-        } else if (setting.interType === 'system' && setting.default === 'false' && _scripts.length === 0) {
-          notification.warning({
-            top: 92,
-            message: '涓嶆墽琛岄粯璁ql鏃讹紝璇锋坊鍔犺嚜瀹氫箟鑴氭湰锛�',
-            duration: 5
-          })
-          this.setState({loading: false})
-          reject()
         } else {
           this.sqlverify(() => {
             this.setState({loading: false})
-            resolve({...setting, scripts})
+            resolve({...setting, scripts, preScripts, cbScripts})
           }, () => {
             this.setState({loading: false})
             reject()
           }, 'submit')
         }
+      })
+    } else {
+      this.setState({loading: false})
+      return new Promise((resolve) => {
+        resolve({...setting, scripts, preScripts, cbScripts})
       })
     }
   }
@@ -246,7 +249,7 @@
   sqlverify = (_resolve, _reject, type, uscripts) => {
     const { setting, scripts, arr_field, regoptions, search } = this.state
 
-    if (setting.interType !== 'system') { // 涓嶄娇鐢ㄧ郴缁熸帴鍙f椂锛屼笉闇�瑕乻ql楠岃瘉
+    if (setting.interType !== 'system' && setting.requestMode !== 'system') { // 涓嶄娇鐢ㄧ郴缁熸帴鍙f椂锛屼笉闇�瑕乻ql楠岃瘉
       _resolve()
       return
     }
@@ -287,15 +290,58 @@
 
   // 鏍囩鍒囨崲
   changeTab = (val) => {
-    const { activeKey, search, arr_field } = this.state
+    const { activeKey, search, arr_field, setting } = this.state
+
+    let _loading = false
+    if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    } else if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+      _loading = true
+    }
+
+    if (_loading) {
+      notification.warning({
+        top: 92,
+        message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
+        duration: 5
+      })
+      return
+    }
+
+    if (activeKey !== 'setting') {
+      if (setting.interType !== 'system' && setting.requestMode !== 'system' && val === 'scripts') {
+        notification.warning({
+          top: 92,
+          message: '浣跨敤绯荤粺鎺ュ彛鏃讹紝鎵嶅彲浠ヨ缃嚜瀹氫箟鑴氭湰锛�',
+          duration: 5
+        })
+        return
+      } else if (setting.interType !== 'custom' && (val === 'prescripts' || val === 'cbscripts')) {
+        notification.warning({
+          top: 92,
+          message: '浣跨敤鑷畾涔夋帴鍙f椂锛屾墠鍙互璁剧疆鍓嶇疆鎴栧洖璋冭剼鏈紒',
+          duration: 5
+        })
+        return
+      }
+    }
 
     if (activeKey === 'setting') {
       let _defaultSql = ''
       this.settingForm.handleConfirm().then(res => {
-        if (res.interType !== 'system') {
+        if (res.interType !== 'system' && res.requestMode !== 'system' && val === 'scripts') {
           notification.warning({
             top: 92,
             message: '浣跨敤绯荤粺鎺ュ彛鏃讹紝鎵嶅彲浠ヨ缃嚜瀹氫箟鑴氭湰锛�',
+            duration: 5
+          })
+          return
+        } else if (res.interType !== 'custom' && (val === 'prescripts' || val === 'cbscripts')) {
+          notification.warning({
+            top: 92,
+            message: '浣跨敤鑷畾涔夋帴鍙f椂锛屾墠鍙互璁剧疆鍓嶇疆鎴栧洖璋冭剼鏈紒',
             duration: 5
           })
           return
@@ -330,22 +376,6 @@
         })
       })
     } else if (activeKey === 'scripts') {
-      let _loading = false
-      if (this.scriptsForm && this.scriptsForm.state.editItem) {
-        _loading = true
-      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
-        _loading = true
-      }
-
-      if (_loading) {
-        notification.warning({
-          top: 92,
-          message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
-          duration: 5
-        })
-        return
-      }
-    
       this.setState({loading: true})
       this.sqlverify(() => { // 楠岃瘉鎴愬姛
         this.setState({
@@ -358,6 +388,10 @@
           loading: false
         })
       }, activeKey)
+    } else {
+      this.setState({
+        activeKey: val
+      })
     }
   }
 
@@ -372,10 +406,24 @@
   scriptsUpdate = (scripts) => {
     this.setState({scripts})
   }
+  
+  // 鍓嶇疆鑴氭湰鏇存柊
+  preScriptsUpdate = (preScripts) => {
+    this.setState({preScripts})
+  }
+  
+  // 鍚庣疆鑴氭湰鏇存柊
+  cbScriptsUpdate = (cbScripts) => {
+    this.setState({cbScripts})
+  }
+
+  updateStatus = (status) => {
+    this.setState({status: {...this.state.status, ...status}})
+  }
 
   render() {
     const { config, menu, dict } = this.props
-    const { loading, activeKey, setting, defaultSql, columns, scripts } = this.state
+    const { loading, activeKey, setting, defaultSql, columns, scripts, preScripts, cbScripts, status, regoptions } = this.state
 
     return (
       <div className="model-table-setting-form-box" id="model-setting-form-body">
@@ -389,6 +437,7 @@
               columns={columns}
               setting={setting}
               scripts={scripts}
+              updateStatus={this.updateStatus}
               wrappedComponentRef={(inst) => this.settingForm = inst}
             />
           </TabPane>
@@ -397,7 +446,7 @@
               鑷畾涔夎剼鏈�
               {scripts.length ? <span className="count-tip">{scripts.length}</span> : null}
             </span>
-          } key="scripts">
+          } disabled={!(status.interType === 'system' || (status.interType === 'custom' && status.requestMode === 'system'))} key="scripts">
             <CustomScript
               dict={dict}
               setting={setting}
@@ -409,6 +458,36 @@
               wrappedComponentRef={(inst) => this.scriptsForm = inst}
             />
           </TabPane>
+          <TabPane tab={
+            <span>
+              鍓嶇疆鑴氭湰
+              {preScripts.length ? <span className="count-tip">{preScripts.length}</span> : null}
+            </span>
+          } disabled={status.interType !== 'custom' || status.procMode !== 'script'} key="prescripts">
+            <SimpleScript
+              dict={dict}
+              setting={setting}
+              scripts={preScripts}
+              regoptions={regoptions}
+              searches={this.props.search}
+              scriptsUpdate={this.preScriptsUpdate}
+              wrappedComponentRef={(inst) => this.preScriptsForm = inst}
+            />
+          </TabPane>
+          <TabPane tab={
+            <span>
+              鍥炶皟鑴氭湰
+              {cbScripts.length ? <span className="count-tip">{cbScripts.length}</span> : null}
+            </span>
+          } disabled={status.interType !== 'custom' || status.callbackType !== 'script'} key="cbscripts">
+            <SimpleScript
+              dict={dict}
+              setting={setting}
+              scripts={cbScripts}
+              scriptsUpdate={this.cbScriptsUpdate}
+              wrappedComponentRef={(inst) => this.cbScriptsForm = inst}
+            />
+          </TabPane>
         </Tabs>
       </div>
     )
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
new file mode 100644
index 0000000..3a5c5d5
--- /dev/null
+++ b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
@@ -0,0 +1,488 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Form, Row, Col, Icon, Button, notification, Select, Popconfirm, Typography, Modal, Radio } from 'antd'
+import moment from 'moment'
+
+import Utils from '@/utils/utils.js'
+import Api from '@/api'
+import SettingUtils from '../utils'
+import CodeMirror from '@/templates/zshare/codemirror'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const { Paragraph } = Typography
+const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
+
+class CustomForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,         // 瀛楀吀椤�
+    setting: PropTypes.object,      // 璁剧疆
+    scripts: PropTypes.array,       // 鑷畾涔夎剼鏈垪琛�
+    searches: PropTypes.array,      // 鎼滅储鏉′欢
+    regoptions: PropTypes.any,      // 姝e垯鏇挎崲
+    scriptsChange: PropTypes.func,  // 鑷畾涔夎剼鏈垏鎹㈡椂楠岃瘉
+    scriptsUpdate: PropTypes.func   // 琛ㄥ崟
+  }
+
+  state = {
+    editItem: null,
+    loading: false,
+    usefulFields: '',
+    systemScripts: [],
+    scriptsColumns: [
+      {
+        title: 'SQL',
+        dataIndex: 'sql',
+        width: '60%',
+        render: (text) => {
+          let title = text.match(/^\s*\/\*.+\*\//)
+          title = title && title[0] ? title[0] : ''
+          text = title ? text.replace(title, '') : text
+
+          return (
+            <div>
+              {title ? <span style={{color: '#a50'}}>{title}</span> : null}
+              <Paragraph copyable ellipsis={{ rows: 4, expandable: true }}>{text}</Paragraph>
+            </div>
+          )
+        }
+      },
+      {
+        title: '鎵ц浣嶇疆',
+        dataIndex: 'position',
+        width: '13%',
+        render: (text, record) => {
+          if (record.position === 'front') {
+            return 'sql鍓�'
+          } else {
+            return 'sql鍚�'
+          }
+        }
+      },
+      {
+        title: '鐘舵��',
+        dataIndex: 'status',
+        width: '12%',
+        render: (text, record) => record.status === 'false' ?
+          (
+            <div>
+              {this.props.dict['model.status.forbidden']}
+              <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" />
+            </div>
+          ) :
+          (
+            <div>
+              {this.props.dict['model.status.open']}
+              <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
+            </div>
+          )
+      },
+      {
+        title: '鎿嶄綔',
+        align: 'center',
+        width: '15%',
+        dataIndex: 'operation',
+        render: (text, record) =>
+          (<div style={{textAlign: 'center'}}>
+            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record)} style={{color: '#1890ff'}}><Icon type="edit" /></span>
+            <span className="operation-btn" title={this.props.dict['header.form.status.change']} onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><Icon type="swap" /></span>
+            <Popconfirm
+              overlayClassName="popover-confirm"
+              title={this.props.dict['model.query.delete']}
+              onConfirm={() => this.handleDelete(record)
+            }>
+              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount() {
+    const { searches, scripts } = this.props
+
+    let _usefulFields = []
+    let scriptsColumns = fromJS(this.state.scriptsColumns).toJS()
+
+    if (searches) {
+      searches.forEach(item => {
+        if (!item.field) return
+        if (item.type === 'group') {
+          if (item.transfer === 'true') {
+            _usefulFields.push(item.field)
+          }
+          _usefulFields.push(item.datefield)
+          _usefulFields.push(item.datefield + '1')
+        } else if (['dateweek', 'datemonth', 'daterange'].includes(item.type)) {
+          _usefulFields.push(item.field)
+          _usefulFields.push(item.field + '1')
+        } else if (_usefulFields.includes(item.field)) {
+          _usefulFields.push(item.field + '1')
+        } else {
+          _usefulFields.push(item.field)
+        }
+      })
+      _usefulFields = _usefulFields.join(', ')
+      scriptsColumns = scriptsColumns.filter(item => {
+        if (item.dataIndex === 'sql') {
+          item.width = '70%'
+        }
+        return item.dataIndex !== 'position'
+      })
+    } else {
+      _usefulFields = null
+    }
+
+    this.setState({
+      usefulFields: _usefulFields,
+      scripts: fromJS(scripts).toJS(),
+      scriptsColumns
+    })
+  }
+
+  componentDidMount () {
+    this.getsysScript()
+  }
+
+  getsysScript = () => {
+    let _scriptSql = `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`
+
+    _scriptSql = Utils.formatOptions(_scriptSql)
+
+    let _sParam = {
+      func: 'sPC_Get_SelectedList',
+      LText: _scriptSql,
+      obj_name: 'data',
+      arr_field: 'funcname,longparam'
+    }
+    
+    _sParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    _sParam.secretkey = Utils.encrypt(_sParam.LText, _sParam.timestamp)
+    _sParam.open_key = Utils.encryptOpenKey(_sParam.secretkey, _sParam.timestamp) // 浜戠鏁版嵁楠岃瘉
+    
+    Api.getSystemConfig(_sParam).then(res => {
+      if (res.status) {
+        let _scripts = res.data.map(item => {
+          let _item = {
+            name: item.funcname,
+            value: window.decodeURIComponent(window.atob(item.longparam))
+          }
+          return _item
+        })
+
+        this.setState({
+          systemScripts: _scripts
+        })
+      } else {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  handleCancel = () => {
+    this.setState({
+      editItem: null
+    })
+    this.props.form.setFieldsValue({
+      sql: ''
+    })
+  }
+
+  handleConfirm = () => {
+    const { scripts, editItem } = this.state
+    
+    let _sql = this.props.form.getFieldValue('sql')
+
+    if (!_sql) {
+      notification.warning({
+        top: 92,
+        message: '璇峰~鍐欒嚜瀹氫箟鑴氭湰锛�',
+        duration: 5
+      })
+      return
+    } else if (/^\s+$/.test(_sql)) {
+      notification.warning({
+        top: 92,
+        message: '鑷畾涔夎剼鏈笉鍙负绌猴紒',
+        duration: 5
+      })
+      return
+    }
+
+    let values = {
+      uuid: editItem && editItem.uuid ? editItem.uuid : Utils.getuuid(),
+      sql: _sql,
+    }
+
+    if (this.props.form.getFieldValue('position')) {
+      values.position = this.props.form.getFieldValue('position')
+    }
+
+    let _quot = values.sql.match(/'{1}/g)
+    let _lparen = values.sql.match(/\({1}/g)
+    let _rparen = values.sql.match(/\){1}/g)
+
+    _quot = _quot ? _quot.length : 0
+    _lparen = _lparen ? _lparen.length : 0
+    _rparen = _rparen ? _rparen.length : 0
+
+    if (_quot % 2 !== 0) {
+      notification.warning({
+        top: 92,
+        message: 'sql涓璡'蹇呴』鎴愬鍑虹幇',
+        duration: 5
+      })
+      return
+    } else if (_lparen !== _rparen) {
+      notification.warning({
+        top: 92,
+        message: 'sql涓�()蹇呴』鎴愬鍑虹幇',
+        duration: 5
+      })
+      return
+    } else if (/--/ig.test(values.sql)) {
+      notification.warning({
+        top: 92,
+        message: '鑷畾涔塻ql璇彞涓紝涓嶅彲鍑虹幇瀛楃 -- 锛屾敞閲婅鐢� /*鍐呭*/',
+        duration: 5
+      })
+      return
+    }
+
+    let error = Utils.verifySql(values.sql, 'customscript')
+
+    if (error) {
+      notification.warning({
+        top: 92,
+        message: 'sql涓笉鍙娇鐢�' + error,
+        duration: 5
+      })
+      return
+    }
+
+    let _scripts = fromJS(scripts).toJS()
+
+    if (editItem && editItem.uuid) {
+      _scripts = _scripts.map(item => {
+        if (item.uuid === values.uuid) {
+          return values
+        } else {
+          return item
+        }
+      })
+    } else {
+      _scripts.push(values)
+    }
+
+    let param = {
+      func: 's_debug_sql',
+      exec_type: 'y',
+      LText: SettingUtils.getCustomDebugSql(_scripts, this.props.regoptions)
+    }
+    param.LText = Utils.formatOptions(param.LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+    
+    this.setState({loading: true})
+    Api.getLocalConfig(param).then(result => {
+      if (result.status) {
+        this.setState({
+          loading: false,
+          scripts: _scripts,
+          editItem: null
+        })
+  
+        this.props.scriptsUpdate(_scripts)
+        this.props.form.setFieldsValue({
+          sql: ''
+        })
+      } else {
+        this.setState({loading: false})
+        Modal.error({
+          title: result.message
+        })
+      }
+    })
+  }
+
+  selectScript = (value, option) => {
+    if (!value || !option) return
+    let _sql = this.props.form.getFieldValue('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
+    })
+  }
+
+  handleEdit = (record) => {
+    const { usefulFields } = this.state
+    this.setState({
+      editItem: record
+    })
+
+    if (usefulFields) {
+      this.props.form.setFieldsValue({
+        sql: record.sql
+      })
+    } else {
+      this.props.form.setFieldsValue({
+        sql: record.sql,
+        position: record.position || 'back'
+      })
+    }
+
+    this.scrolltop()
+  }
+
+  scrolltop = () => {
+    let node = document.getElementById('model-setting-form-body').parentNode
+
+    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)
+    }
+  }
+
+  changeScripts = (scripts) => {
+    this.setState({scripts})
+    this.props.scriptsUpdate(scripts)
+  }
+
+  handleStatus = (record) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+    record.status = record.status === 'false' ? 'true' : 'false'
+
+    scripts = scripts.map(item => {
+      if (item.uuid === record.uuid) {
+        return record
+      } else {
+        return item
+      }
+    })
+
+    this.setState({scripts})
+    this.props.scriptsUpdate(scripts)
+  }
+
+  handleDelete = (record) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+    scripts = scripts.filter(item => item.uuid !== record.uuid)
+
+    this.setState({ scripts })
+    this.props.scriptsUpdate(scripts)
+  }
+
+  render() {
+    const { setting, scripts } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { usefulFields, scriptsColumns, systemScripts } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="modal-menu-setting-script">
+        <Form {...formItemLayout}>
+          <Row gutter={24}>
+            <Col span={8}>
+              <Form.Item label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+                {setting.cbTable}
+              </Form.Item>
+            </Col>
+            <Col span={16}>
+              <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
+                ErrorCode, retmsg
+              </Form.Item>
+            </Col>
+            {usefulFields ? <Col span={24} className="sqlfield">
+              <Form.Item label={'鍙敤瀛楁'}>
+                id, bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id{usefulFields ? ', ' + usefulFields : ''}
+              </Form.Item>
+            </Col> : null}
+            {!usefulFields ? <Col span={24} className="sqlfield">
+              <Form.Item label={'鍙敤瀛楁'}>
+                id, bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id
+              </Form.Item>
+            </Col> : null}
+            {!usefulFields ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
+              <Form.Item style={{marginBottom: 0}} label="鎵ц浣嶇疆">
+                {getFieldDecorator('position', {
+                  initialValue: 'front'
+                })(
+                  <Radio.Group>
+                    <Radio value="front">sql鍓�</Radio>
+                    <Radio value="back">sql鍚�</Radio>
+                  </Radio.Group>
+                )}
+              </Form.Item>
+            </Col> : null}
+            <Col span={10} className="quick-add">
+              <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
+                <Select
+                  allowClear
+                  showSearch
+                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  onChange={this.selectScript}
+                >
+                  {!usefulFields ? <Select.Option key="default" value={`declare @${setting.cbTable} table (mk_api_key nvarchar(100),mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))\n/*@${setting.cbTable}_data table (mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))*/`}>榛樿sql</Select.Option> : null}
+                  {systemScripts.map((option, i) =>
+                    <Select.Option style={{whiteSpace: 'normal'}} key={i} value={option.value}>{option.name}</Select.Option>
+                  )}
+                </Select>
+              </Form.Item>
+            </Col>
+            <Col span={6} className="add">
+              <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginTop: 5, marginBottom: 15, marginLeft: 30}}>
+                淇濆瓨
+              </Button>
+              <Button onClick={this.handleCancel} style={{marginTop: 5, marginBottom: 15, marginLeft: 10}}>
+                鍙栨秷
+              </Button>
+            </Col>
+            <Col span={24} className="sql">
+              <Form.Item label={'sql'}>
+                {getFieldDecorator('sql', {
+                  initialValue: ''
+                })(<CodeMirror />)}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+        <EditTable data={scripts} actions={['move']} columns={scriptsColumns} onChange={this.changeScripts}/>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.scss b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.scss
new file mode 100644
index 0000000..945809b
--- /dev/null
+++ b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.scss
@@ -0,0 +1,45 @@
+.modal-menu-setting-script {
+  .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%;
+    }
+  }
+  .quick-add {
+    .ant-col-sm-8 {
+      width: 26%;
+    }
+    .ant-col-sm-16 {
+      width: 74%;
+    }
+  }
+  .sql {
+    .ant-col-sm-8 {
+      width: 10.5%;
+    }
+    .ant-col-sm-16 {
+      width: 89.5%;
+      padding-top: 4px;
+    }
+    .CodeMirror {
+      height: 350px;
+    }
+  }
+  div.ant-typography {
+    margin-bottom: 0;
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
index 9bde95a..09f0df4 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
@@ -99,4 +99,61 @@
 
     return sql
   }
+
+  /**
+   * @description 鐢熸垚鍓嶇疆鎴栧悗缃鍙�
+   * @return {String}  scripts       鑴氭湰
+   * @return {Array}   regoptions    鎼滅储鏉′欢姝e垯鏇挎崲
+   */
+  static getCustomDebugSql (scripts, regoptions) {
+    let sql = ''
+    let _customScript = ''
+
+    scripts.forEach(script => {
+      if (script.status === 'false') return
+
+      _customScript += `
+      ${script.sql}
+      `
+    })
+
+    if (_customScript) {
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
+        ${_customScript}
+      `
+    }
+
+    _customScript = _customScript.replace(/@\$|\$@/ig, '')
+    _customScript = _customScript.replace(/@userName@|@fullName@/ig, `''`)
+    // 澶栬仈鏁版嵁搴撴浛鎹�
+    if (window.GLOB.externalDatabase !== null) {
+      _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
+    }
+    
+    // 姝e垯鏇挎崲
+    if (regoptions) {
+      let _regoptions = regoptions.map(item => {
+        return {
+          reg: new RegExp('@' + item.key + '@', 'ig'),
+          value: `'0'`
+        }
+      })
+      _regoptions.forEach(item => {
+        _customScript = _customScript.replace(item.reg, item.value)
+      })
+    }
+
+    if (_customScript) {
+      sql = `/* sql 楠岃瘉 */
+        ${_customScript}
+        aaa:
+        if @ErrorCode!=''
+          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+      `
+    }
+    sql = sql.replace(/\n\s{8}/ig, '\n')
+    console.info(sql)
+
+    return sql
+  }
 }
\ No newline at end of file
diff --git a/src/templates/sharecomponent/tabscomponent/tabform/index.jsx b/src/templates/sharecomponent/tabscomponent/tabform/index.jsx
index 1b4fb9d..8f2cd9f 100644
--- a/src/templates/sharecomponent/tabscomponent/tabform/index.jsx
+++ b/src/templates/sharecomponent/tabscomponent/tabform/index.jsx
@@ -26,9 +26,9 @@
   UNSAFE_componentWillMount () {
     const { formlist } = this.props
 
-    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
+    // let type = formlist.filter(cell => cell.key === 'type')[0].initVal
 
-    let _tabs = this.props.tabs.filter(tab => tab.type === type)
+    let _tabs = this.props.tabs.filter(tab => tab.type === 'SubTable')
 
     this.setState({
       formlist: formlist.map(item => {
@@ -281,6 +281,8 @@
             return
           }
 
+          values.type = 'SubTable' // 绫诲瀷涓哄瓙琛�
+
           resolve(values)
         } else {
           reject(err)
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
index 285aff8..38552f7 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
@@ -8,10 +8,11 @@
 import Utils from '@/utils/utils.js'
 import SettingUtils from './utils.jsx'
 import DataSource from './datasource'
-import CustomScript from '@/templates/zshare/customscript'
+import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
 
 const { TabPane } = Tabs
+const CustomScript = asyncComponent(() => import('@/templates/zshare/customscript'))
 
 class SettingForm extends Component {
   static propTpyes = {
diff --git a/src/templates/subtableconfig/index.jsx b/src/templates/subtableconfig/index.jsx
index 3acb99e..7676116 100644
--- a/src/templates/subtableconfig/index.jsx
+++ b/src/templates/subtableconfig/index.jsx
@@ -11,7 +11,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import Utils from '@/utils/utils.js'
-import UtilsUpdate from '@/utils/utils-update.js'
+import { updateSubTable } from '@/utils/utils-update.js'
 
 import asyncComponent from '@/utils/asyncComponent'
 import SearchComponent from '@/templates/sharecomponent/searchcomponent'
@@ -106,7 +106,7 @@
     let _activeKey =  editSubTab ? editSubTab.activeKey : editTab.activeKey
 
     // 鐗堟湰鍏煎
-    _config = UtilsUpdate.updateSubTable(_config)
+    _config = updateSubTable(_config)
 
     this.setState({
       openEdition: editSubTab ? (editSubTab.open_edition || '') : (editTab.open_edition || ''),
@@ -195,7 +195,7 @@
     if (editSubTab) {
       _subconfig = tabConfig
       if (editTab.hasOwnProperty('OpenType')) {
-        _tabview = editTab.tabType
+        _tabview = editTab.tabType || 'SubTable'
       } else {
         _tabview = editTab.type
       }
@@ -669,7 +669,7 @@
         if (btn.OpenType === 'pop') {
           _view = 'Modal'             // 琛ㄥ崟椤甸潰
         } else if (btn.OpenType === 'popview') {
-          _view = btn.tabType        // 鏂板脊绐楁爣绛炬ā鏉�
+          _view = btn.tabType || 'SubTable' // 鏂板脊绐楁爣绛炬ā鏉� tabType 灞炴�у凡鍘婚櫎
           _subtab = btn
 
           if (editSubTab) {
@@ -816,8 +816,32 @@
       }
     })
 
-    if (config.setting.interType === 'system' && config.setting.default !== 'false' && !config.setting.dataresource) {
-      return '灏氭湭璁剧疆鏁版嵁婧愶紝涓嶅彲鍚敤锛�'
+    config.action && config.action.forEach((btn) => {
+      if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+        let hascheck = false
+        btn.verify.scripts.forEach(item => {
+          if (item.status === 'false') return
+    
+          if (/\$check@|@check\$/ig.test(item.sql)) {
+            hascheck = true
+          }
+        })
+        if (hascheck) {
+          notification.warning({
+            top: 92,
+            message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+            duration: 5
+          })
+        }
+      }
+    })
+
+    if ((config.setting.interType === 'system' || config.setting.requestMode === 'system') && config.setting.default === 'false' && config.setting.scripts && config.setting.scripts.filter(item => item.status !== 'false').length === 0) {
+      return '鏁版嵁婧愪腑涓嶆墽琛岄粯璁ql锛屼笖鏈坊鍔犺嚜瀹氫箟鑴氭湰锛屼笉鍙惎鐢紒'
+    } else if (config.setting.interType === 'custom' && config.setting.procMode !== 'inner' && config.setting.preScripts && config.setting.preScripts.filter(item => item.status !== 'false').length === 0) {
+      return '鏁版嵁婧愭湭璁剧疆鍓嶇疆鑴氭湰锛屼笉鍙惎鐢紒'
+    } else if (config.setting.interType === 'custom' && config.setting.callbackType === 'script' && config.setting.cbScripts && config.setting.cbScripts.filter(item => item.status !== 'false').length === 0) {
+      return '鏁版嵁婧愭湭璁剧疆鍥炶皟鑴氭湰锛屼笉鍙惎鐢紒'
     } else if (!config.setting.primaryKey) {
       return '灏氭湭璁剧疆涓婚敭锛屼笉鍙惎鐢紒'
     }  else if (config.columns.length === 0) {
diff --git a/src/templates/zshare/codemirror/index.jsx b/src/templates/zshare/codemirror/index.jsx
index 4351acb..edba82c 100644
--- a/src/templates/zshare/codemirror/index.jsx
+++ b/src/templates/zshare/codemirror/index.jsx
@@ -6,6 +6,8 @@
 import {UnControlled as CodeMirror} from 'react-codemirror2'
 import 'codemirror/mode/javascript/javascript'
 import 'codemirror/mode/sql/sql'
+import 'codemirror/mode/xml/xml'
+import 'codemirror/mode/css/css'
 import 'codemirror/addon/display/fullscreen.js'
 
 import 'codemirror/addon/display/fullscreen.css'
@@ -26,7 +28,7 @@
     editor: null,   // code瀵硅薄
     defaultVal: '', // 鍒濆鍊�
     value: '',      // 瀹炴椂鍐呭
-    options: null,  // mode : text/javascript銆乼ext/x-mysql ; theme : cobalt - 榛戝簳
+    options: null,  // mode : text/xml, text/css, text/javascript銆乼ext/x-mysql ; theme : cobalt - 榛戝簳
     fullScreen: false,
     style: {fontSize: '18px', lineHeight: '32px'},
     display: true
diff --git a/src/templates/zshare/customscript/index.jsx b/src/templates/zshare/customscript/index.jsx
index 0a3834e..b7c3ecb 100644
--- a/src/templates/zshare/customscript/index.jsx
+++ b/src/templates/zshare/customscript/index.jsx
@@ -139,7 +139,7 @@
         let _scripts = res.data.map(item => {
           let _item = {
             name: item.funcname,
-            value: Utils.UnformatOptions(item.longparam)
+            value: window.decodeURIComponent(window.atob(item.longparam))
           }
           return _item
         })
@@ -266,6 +266,7 @@
   }
 
   selectScript = (value, option) => {
+    if (!value || !option) return
     let _sql = this.props.form.getFieldValue('sql')
     if (_sql) {
       _sql = _sql + ` 
@@ -379,6 +380,7 @@
             <Col span={10} className="quick-add">
               <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
                 <Select
+                  allowClear
                   showSearch
                   filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                   onChange={this.selectScript}
diff --git a/src/templates/zshare/editTable/index.jsx b/src/templates/zshare/editTable/index.jsx
index feb862e..f78cdb1 100644
--- a/src/templates/zshare/editTable/index.jsx
+++ b/src/templates/zshare/editTable/index.jsx
@@ -172,7 +172,15 @@
     let columns = fromJS(this.props.columns).toJS()
 
     if (actions && (actions.includes('edit') || actions.includes('copy') || actions.includes('del'))) {
-      columns.push({
+      let _operation = null
+      columns = columns.filter(item => {
+        if (item.dataIndex === 'operation') {
+          _operation = item
+        }
+        return item.dataIndex !== 'operation'
+      })
+
+      let operation = {
         title: (<div>
           {eTDict['model.operation']}
           {actions.includes('copy') ? (
@@ -213,7 +221,13 @@
             </div>
           )
         }
-      })
+      }
+
+      if (_operation) {
+        operation.render = _operation.render
+        operation.width = _operation.width
+      }
+      columns.push(operation)
     }
 
     this.setState({
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index 65a1932..828532c 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -687,7 +687,7 @@
       options: opentypes
     },
     {
-      type: 'select',
+      type: 'radio',
       key: 'funcType',
       label: Formdict['header.form.funcType'],
       initVal: card.funcType || '',
@@ -723,19 +723,24 @@
       label: Formdict['header.form.intertype'],
       initVal: card.intertype || 'system',
       required: true,
+      options: []
+    },
+    {
+      type: 'radio',
+      key: 'procMode',
+      label: '鍙傛暟澶勭悊',
+      initVal: card.procMode || 'system',
+      required: true,
       options: [{
         value: 'system',
-        text: Formdict['model.interface.system']
+        text: '绯荤粺鍑芥暟'
       }, {
         value: 'inner',
-        text: Formdict['model.interface.inner']
-      }, {
-        value: 'outer',
-        text: Formdict['model.interface.outer']
+        text: '鍐呴儴鍑芥暟'
       }]
     },
     {
-      type: 'select',
+      type: 'radio',
       key: 'sqlType',
       label: Formdict['header.form.action.type'],
       initVal: card.sqlType || '',
@@ -744,18 +749,18 @@
     },
     {
       type: 'text',
-      key: 'sql',
-      label: Formdict['model.form.tablename'],
-      initVal: card.sql || config.setting.tableName || '',
-      required: true
-    },
-    {
-      type: 'text',
       key: 'label',
       label: '鎸夐挳鍚嶇О',
       initVal: card.label,
       required: true,
       readonly: false
+    },
+    {
+      type: 'text',
+      key: 'sql',
+      label: Formdict['model.form.tablename'],
+      initVal: card.sql || config.setting.tableName || '',
+      required: true
     },
     {
       type: 'text',
@@ -768,17 +773,17 @@
       required: card.intertype === 'inner',
       readonly: false
     },
-    {
-      type: 'select',
-      key: 'tabType',
-      label: Formdict['model.form.tabType'],
-      initVal: card.tabType || 'SubTable',
-      required: true,
-      options: [{
-        value: 'SubTable',
-        text: Formdict['model.menu.tab.subtable']
-      }]
-    },
+    // {
+    //   type: 'select',
+    //   key: 'tabType',
+    //   label: Formdict['model.form.tabType'],
+    //   initVal: card.tabType || 'SubTable',
+    //   required: true,
+    //   options: [{
+    //     value: 'SubTable',
+    //     text: Formdict['model.menu.tab.subtable']
+    //   }]
+    // },
     {
       type: 'select',
       key: 'linkTab',
@@ -848,12 +853,56 @@
       readonly: false
     },
     {
-      type: 'text',
+      type: 'textarea',
       key: 'interface',
-      label: Formdict['header.form.interface'],
+      label: '娴嬭瘯鍦板潃',
       initVal: card.sysInterface === 'true' ? (window.GLOB.mainSystemApi || '') : (card.interface || ''),
       required: true,
       readonly: card.sysInterface === 'true'
+    },
+    {
+      type: 'textarea',
+      key: 'proInterface',
+      label: '姝e紡鍦板潃',
+      initVal: card.proInterface || '',
+      tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
+      required: false
+    },
+    {
+      type: 'radio',
+      key: 'method',
+      label: '璇锋眰鏂瑰紡',
+      initVal: card.method || 'post',
+      required: true,
+      options: [{
+        value: 'get',
+        text: 'GET'
+      }, {
+        value: 'post',
+        text: 'POST'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'callbackType',
+      label: '鍥炶皟鏂瑰紡',
+      initVal: card.callbackType || 'script',
+      tooltip: '浣跨敤榛樿鏂瑰紡鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
+      required: true,
+      options: [{
+        value: 'script',
+        text: '鑷畾涔夎剼鏈�'
+      }, {
+        value: 'default',
+        text: '鍚庡彴鑴氭湰'
+      }]
+    },
+    {
+      type: 'text',
+      key: 'cbTable',
+      label: '鍥炶皟琛ㄥ悕',
+      initVal: card.cbTable || '',
+      required: true
     },
     {
       type: 'text',
@@ -864,7 +913,7 @@
       readonly: false
     },
     {
-      type: 'select',
+      type: 'radio',
       key: 'position',
       label: Formdict['header.form.position'],
       initVal: card.position || 'toolbar',
@@ -1851,7 +1900,7 @@
     })
   }
 
-  if (card.type === 'textarea' || card.type === 'fileupload' || card.type === 'multiselect' || card.type === 'checkbox') {
+  if (['textarea', 'fileupload', 'multiselect', 'checkbox', 'brafteditor'].includes(card.type)) {
     _fieldlength = 512
   }
 
@@ -1927,6 +1976,9 @@
         value: 'color',
         text: Formdict['model.form.color']
       }, {
+        value: 'brafteditor',
+        text: '瀵屾枃鏈�'
+      }, {
         value: 'funcvar',
         text: Formdict['header.form.funcvar']
       },
@@ -1936,7 +1988,7 @@
       type: 'text',
       key: 'initval',
       label: Formdict['header.form.initval'],
-      tooltip: '涓嬫媺澶氶�変笌澶氶�夋锛屾坊鍔犲涓垵濮嬪�艰浣跨敤鈥�,鈥濆彿鍒嗛殧銆�',
+      tooltip: '涓嬫媺澶氶�変笌澶氶�夋锛屾坊鍔犲涓垵濮嬪�艰浣跨敤鈥�,鈥濆彿鍒嗛殧銆傛敞锛氫笅鎷夐�夋嫨銆佽仈鍔ㄨ彍鍗曟垨鍗曢�夋涓�$first琛ㄧず閫夋嫨绗竴椤�',
       initVal: card.initval || '',
       required: false
     },
@@ -1953,14 +2005,14 @@
       key: 'openVal',
       label: '寮�鍚��',
       initVal: card.openVal || '',
-      required: true
+      required: false
     },
     {
       type: 'text',
       key: 'closeVal',
       label: '鍏抽棴鍊�',
       initVal: card.closeVal || '',
-      required: true
+      required: false
     },
     {
       type: 'text',
@@ -2225,6 +2277,19 @@
     },
     {
       type: 'radio',
+      key: 'hidelabel',
+      label: '闅愯棌鍚嶇О',
+      initVal: card.hidelabel || 'false',
+      options: [{
+        value: 'true',
+        text: Formdict['model.true']
+      }, {
+        value: 'false',
+        text: Formdict['model.false']
+      }]
+    },
+    {
+      type: 'radio',
       key: 'readonly',
       label: Formdict['header.form.readonly'],
       initVal: card.readonly || 'false',
@@ -2330,7 +2395,7 @@
       type: 'radio',
       key: 'encryption',
       label: '鍔犲瘑浼犺緭',
-      initVal: card.encryption || 'false',
+      initVal: card.type === 'brafteditor' ? (card.encryption || 'true') : (card.encryption || 'false'),
       options: [{
         value: 'true',
         text: Formdict['model.true']
@@ -2450,17 +2515,17 @@
       initVal: card.label || '',
       required: true
     },
-    {
-      type: 'select',
-      key: 'type',
-      label: Formdict['model.form.tabType'],
-      initVal: card.type || 'SubTable',
-      required: true,
-      options: [{
-        value: 'SubTable',
-        text: Formdict['model.menu.tab.subtable']
-      }]
-    },
+    // {
+    //   type: 'select',
+    //   key: 'type',
+    //   label: Formdict['model.form.tabType'],
+    //   initVal: card.type || 'SubTable',
+    //   required: true,
+    //   options: [{
+    //     value: 'SubTable',
+    //     text: Formdict['model.menu.tab.subtable']
+    //   }]
+    // },
     {
       type: 'select',
       key: 'linkTab',
diff --git a/src/templates/zshare/modalform/index.jsx b/src/templates/zshare/modalform/index.jsx
index 1b9b8cf..b871d33 100644
--- a/src/templates/zshare/modalform/index.jsx
+++ b/src/templates/zshare/modalform/index.jsx
@@ -30,6 +30,7 @@
   textarea: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'maxRows', 'encryption', 'interception', 'tooltip'],
   color: ['initval', 'readonly', 'required', 'hidden', 'readin', 'entireLine', 'tooltip'],
   hint: ['label', 'type', 'blacklist', 'message'],
+  brafteditor: ['required', 'hidelabel', 'hidden', 'readin', 'fieldlength', 'readonly', 'tooltip', 'encryption'],
   funcvar: [],
   linkMain: ['readonly', 'required', 'hidden', 'fieldlength', 'entireLine', 'tooltip']
 }
@@ -231,7 +232,9 @@
             }
           } else if (form.key === 'fieldlength') {
             form.initVal = 50
-            if (value === 'textarea' || value === 'fileupload' || value === 'multiselect' || value === 'checkbox') {
+            if (value === 'textarea') {
+              form.initVal = 8000
+            } else if (value === 'fileupload' || value === 'multiselect' || value === 'checkbox') {
               form.initVal = 512
             }
 
@@ -254,6 +257,10 @@
             form.required = true
             if (value === 'hint') {
               form.required = false
+            }
+          } else if (form.key === 'encryption') {
+            if (value === 'brafteditor') {
+              fieldValue.encryption = 'true'
             }
           }
 
@@ -521,7 +528,7 @@
                   getPopupContainer={() => document.getElementById('modal-fields-form-box')}
                 >
                   {item.options.map((option, i) =>
-                    <Select.Option key={`${i}`} value={option.value || option.field}>
+                    <Select.Option key={`${i}`} value={option.value || option.field || ''}>
                       {item.key === 'icon' && <Icon type={option.text} />} {option.text || option.label}
                     </Select.Option>
                   )}
diff --git a/src/templates/zshare/verifycard/callbackcustomscript/index.jsx b/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
new file mode 100644
index 0000000..aca9271
--- /dev/null
+++ b/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
@@ -0,0 +1,284 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Button, notification, Modal, Tooltip, Icon, Radio, Select } from 'antd'
+import moment from 'moment'
+
+import Utils from '@/utils/utils.js'
+import Api from '@/api'
+import CodeMirror from '@/templates/zshare/codemirror'
+import './index.scss'
+
+class CustomForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,         // 瀛楀吀椤�
+    btn: PropTypes.object,          // 鎸夐挳淇℃伅
+    initsql: PropTypes.string,      // 鍒濆鍖栬剼鏈�
+    usefulfields: PropTypes.string, // 鍙敤瀛楁
+    systemScripts: PropTypes.array, // 绯荤粺鑴氭湰
+    customScripts: PropTypes.array, // 鑷畾涔夎剼鏈�
+    scriptsChange: PropTypes.func   // 琛ㄥ崟
+  }
+
+  state = {
+    editItem: null,
+    loading: false
+  }
+
+  edit = (record) => {
+    this.setState({
+      editItem: record
+    })
+
+    this.props.form.setFieldsValue({
+      sql: record.sql,
+      position: record.position || 'back'
+    })
+  }
+
+  handleConfirm = () => {
+    const { editItem } = this.state
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        values.uuid = editItem ? editItem.uuid : ''
+
+        let _quot = values.sql.match(/'{1}/g)
+        let _lparen = values.sql.match(/\({1}/g)
+        let _rparen = values.sql.match(/\){1}/g)
+
+        _quot = _quot ? _quot.length : 0
+        _lparen = _lparen ? _lparen.length : 0
+        _rparen = _rparen ? _rparen.length : 0
+
+        if (_quot % 2 !== 0) {
+          notification.warning({
+            top: 92,
+            message: 'sql涓璡'蹇呴』鎴愬鍑虹幇',
+            duration: 5
+          })
+          return
+        } else if (_lparen !== _rparen) {
+          notification.warning({
+            top: 92,
+            message: 'sql涓�()蹇呴』鎴愬鍑虹幇',
+            duration: 5
+          })
+          return
+        } else if (/--/ig.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '鑷畾涔塻ql璇彞涓紝涓嶅彲鍑虹幇瀛楃 -- 锛屾敞閲婅鐢� /*鍐呭*/',
+            duration: 5
+          })
+          return
+        }
+
+        let error = Utils.verifySql(values.sql, 'customscript')
+
+        if (error) {
+          notification.warning({
+            top: 92,
+            message: 'sql涓笉鍙娇鐢�' + error,
+            duration: 5
+          })
+          return
+        }
+
+        let tail = `
+          aaa:
+        `
+
+        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}
+            `
+          }
+        })
+
+        let param = {
+          func: 's_debug_sql',
+          exec_type: 'y',
+          LText: this.props.initsql +  _prevCustomScript + _backCustomScript + tail
+        }
+
+        // 鏁版嵁鏉冮檺
+        param.LText = param.LText.replace(/@\$|\$@/ig, '')
+
+        // check
+        param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
+
+        // 澶栬仈鏁版嵁搴撴浛鎹�
+        if (window.GLOB.externalDatabase !== null) {
+          param.LText = param.LText.replace(/@db@/ig, window.GLOB.externalDatabase)
+        }
+
+        console.info(`/* sql 楠岃瘉 */\n${param.LText.replace(/\n\s{6,20}/ig, '\n')}`)
+
+        param.LText = Utils.formatOptions(param.LText)
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        param.secretkey = Utils.encrypt('', param.timestamp)
+        
+        this.setState({loading: true})
+        Api.getLocalConfig(param).then(res => {
+          if (res.status) {
+            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 (_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 { usefulfields, systemScripts, btn } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="verify-form" id="verify-custom-callback-scripts">
+        <Row gutter={24}>
+          <Col span={8}>
+            <Form.Item label={'琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+              {btn.cbTable}
+            </Form.Item>
+          </Col>
+          <Col span={10}>
+            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0, whiteSpace: 'nowrap'}}>
+              ErrorCode锛堝鍔犲悗缂�NT琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT锛�, retmsg
+            </Form.Item>
+          </Col>
+          {usefulfields ? <Col span={24} className="sqlfield">
+            <Form.Item label={'鍙敤瀛楁'}>
+              {usefulfields}
+            </Form.Item>
+          </Col> : null}
+          <Col span={8} style={{whiteSpace: 'nowrap'}}>
+            <Form.Item style={{marginBottom: 0}} label={
+              <Tooltip placement="bottomLeft" title={'鑷畾涔夎剼鏈笌榛樿sql浣嶇疆鍏崇郴銆�'}>
+                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '5px'}} />
+                鎵ц浣嶇疆
+              </Tooltip>
+            }>
+              {getFieldDecorator('position', {
+                initialValue: 'front'
+              })(
+                <Radio.Group>
+                  <Radio value="front">sql鍓�</Radio>
+                  <Radio value="back">sql鍚�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={10}>
+            <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
+              <Select
+                allowClear
+                showSearch
+                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                onChange={this.selectScript}
+                getPopupContainer={() => document.getElementById('verify-custom-callback-scripts')}
+              >
+                <Select.Option key="default" value={`declare @${btn.cbTable} table (mk_api_key nvarchar(100),mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))\n/*@${btn.cbTable}_data table (mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))*/`}>榛樿sql</Select.Option>
+                {systemScripts.map((option, i) =>
+                  <Select.Option key={i} value={option.value}>{option.name}</Select.Option>
+                )}
+              </Select>
+            </Form.Item>
+          </Col>
+          <Col span={6} className="add">
+            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginBottom: 15, marginLeft: 40}}>
+              淇濆瓨
+            </Button>
+            <Button onClick={this.handleCancel} style={{marginBottom: 15, marginLeft: 10}}>
+              鍙栨秷
+            </Button>
+          </Col>
+          <Col span={24} className="sql">
+            <Form.Item label={
+              <Tooltip placement="topLeft" title={'鏁版嵁妫�鏌ユ浛鎹㈢ $check@ -> /* 鎴� \'\'銆� @check$ -> */ 鎴� \'\''}>
+                <Icon type="question-circle" />
+                sql
+              </Tooltip>
+            }>
+              {getFieldDecorator('sql', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.input'] + 'sql!'
+                  }
+                ]
+              })(<CodeMirror />)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/templates/zshare/verifycard/callbackcustomscript/index.scss b/src/templates/zshare/verifycard/callbackcustomscript/index.scss
new file mode 100644
index 0000000..89791c7
--- /dev/null
+++ b/src/templates/zshare/verifycard/callbackcustomscript/index.scss
@@ -0,0 +1,5 @@
+#verify-custom-scripts {
+  .ant-select-dropdown-menu-item {
+    white-space: normal;
+  }
+}
\ No newline at end of file
diff --git a/src/templates/zshare/verifycard/customscript/index.jsx b/src/templates/zshare/verifycard/customscript/index.jsx
index d1a9a85..7cd554e 100644
--- a/src/templates/zshare/verifycard/customscript/index.jsx
+++ b/src/templates/zshare/verifycard/customscript/index.jsx
@@ -122,6 +122,8 @@
 
         // 鏁版嵁鏉冮檺
         param.LText = param.LText.replace(/@\$|\$@/ig, '')
+        // check
+        param.LText = param.LText.replace(/\$check@|@check\$/ig, '')
 
         // 澶栬仈鏁版嵁搴撴浛鎹�
         if (window.GLOB.externalDatabase !== null) {
@@ -169,6 +171,8 @@
   }
 
   selectScript = (value, option) => {
+    if (!value || !option) return
+
     let _sql = this.props.form.getFieldValue('sql')
     if (_sql) {
       _sql = _sql + ` 
@@ -209,9 +213,9 @@
               {btn.sql}
             </Form.Item>
           </Col> : null}
-          <Col span={16}>
-            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
-              ErrorCode, retmsg
+          <Col span={10}>
+            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0, whiteSpace: 'nowrap'}}>
+              ErrorCode锛堝鍔犲悗缂�NT琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT锛�, retmsg
             </Form.Item>
           </Col>
           {usefulfields ? <Col span={24} className="sqlfield">
@@ -241,6 +245,7 @@
             <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
               <Select
                 showSearch
+                allowClear
                 filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                 onChange={this.selectScript}
                 getPopupContainer={() => document.getElementById('verify-custom-scripts')}
@@ -261,7 +266,12 @@
             </Button>
           </Col>
           <Col span={24} className="sql">
-            <Form.Item label={'sql'}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title={'鏁版嵁妫�鏌ユ浛鎹㈢ $check@ -> /* 鎴� \'\'銆� @check$ -> */ 鎴� \'\''}>
+                <Icon type="question-circle" />
+                sql
+              </Tooltip>
+            }>
               {getFieldDecorator('sql', {
                 initialValue: '',
                 rules: [
diff --git a/src/templates/zshare/verifycard/index.jsx b/src/templates/zshare/verifycard/index.jsx
index fab8318..09a4338 100644
--- a/src/templates/zshare/verifycard/index.jsx
+++ b/src/templates/zshare/verifycard/index.jsx
@@ -11,6 +11,7 @@
 import ContrastForm from './contrastform'
 import CustomForm from './customform'
 import CustomScript from './customscript'
+import CallBackCustomScript from './callbackcustomscript'
 import BillcodeForm from './billcodeform'
 import VoucherForm from './voucherform'
 import asyncComponent from '@/utils/asyncComponent'
@@ -355,6 +356,73 @@
           </div>)
       }
     ],
+    cbScriptsColumns: [
+      {
+        title: 'SQL',
+        dataIndex: 'sql',
+        width: '60%',
+        render: (text) => {
+          let title = text.match(/^\s*\/\*.+\*\//)
+          title = title && title[0] ? title[0] : ''
+          text = title ? text.replace(title, '') : text
+
+          return (
+            <div>
+              {title ? <span style={{color: '#a50'}}>{title}</span> : null}
+              <Paragraph copyable ellipsis={{ rows: 4, expandable: true }}>{text}</Paragraph>
+            </div>
+          )
+        }
+      },
+      {
+        title: '鎵ц浣嶇疆',
+        dataIndex: 'position',
+        width: '10%',
+        render: (text, record) => {
+          if (record.position === 'front') {
+            return 'sql鍓�'
+          } else {
+            return 'sql鍚�'
+          }
+        }
+      },
+      {
+        title: '鐘舵��',
+        dataIndex: 'status',
+        width: '10%',
+        render: (text, record) => record.status === 'false' ?
+          (
+            <div>
+              {this.props.dict['model.status.forbidden']}
+              <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" />
+            </div>
+          ) :
+          (
+            <div>
+              {this.props.dict['model.status.open']}
+              <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
+            </div>
+          )
+      },
+      {
+        title: '鎿嶄綔',
+        align: 'center',
+        width: '20%',
+        dataIndex: 'operation',
+        render: (text, record) =>
+          (<div style={{textAlign: 'center'}}>
+            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record, 'cbscripts')} style={{color: '#1890ff'}}><Icon type="edit" /></span>
+            <span className="operation-btn" title={this.props.dict['header.form.status.change']} onClick={() => this.handleStatus(record, 'cbscripts')} style={{color: '#8E44AD'}}><Icon type="swap" /></span>
+            <Popconfirm
+              overlayClassName="popover-confirm"
+              title={this.props.dict['model.query.delete']}
+              onConfirm={() => this.handleDelete(record, 'cbscripts')
+            }>
+              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ],
     orderColumns: [
       {
         title: this.props.dict['header.form.funcvar'],
@@ -485,10 +553,15 @@
     _verify.billcodes = _verify.billcodes || []
     _verify.voucher = _verify.voucher || {enabled: false}
     _verify.scripts = _verify.scripts || []
+    _verify.cbScripts = _verify.cbScripts || []
 
     this.setState({
       verify: _verify
     })
+
+    if (config.Template !== 'FormTab' && (card.intertype === 'inner' || card.intertype === 'outer')) { // 鍐呴儴鎴栧閮ㄦ帴鍙�
+      return
+    }
 
     new Promise(resolve => {
       let _fields = []
@@ -523,11 +596,7 @@
             }
             
             if (!_LongParam) {
-              notification.warning({
-                top: 92,
-                message: '鏈幏鍙栧埌琛ㄥ崟淇℃伅锛岄儴鍒嗛獙璇佸皢鏃犳硶璁剧疆锛�',
-                duration: 5
-              })
+              message.warning('鏈幏鍙栧埌琛ㄥ崟淇℃伅锛岄儴鍒嗛獙璇佸皢鏃犳硶璁剧疆锛�')
             } else {
               if (_LongParam.groups.length > 0) {
                 _LongParam.groups.forEach(group => {
@@ -908,7 +977,7 @@
           systemScripts: res.scripts.map(item => {
             return {
               name: item.funcname,
-              value: Utils.UnformatOptions(item.longparam)
+              value: window.decodeURIComponent(window.atob(item.longparam))
             }
           })
         })
@@ -989,6 +1058,25 @@
     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)
+    }
+
+    this.setState({ verify })
+  }
+
   orderChange = (values) => {
     let verify = fromJS(this.state.verify).toJS()
 
@@ -1050,6 +1138,8 @@
       verify.billcodes = verify.billcodes.filter(item => item.uuid !== record.uuid)
     } else 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 })
@@ -1062,6 +1152,8 @@
       this.orderForm.edit(record)
     } else if (type === 'scripts') {
       this.scriptsForm.edit(record)
+    } else if (type === 'cbscripts') {
+      this.cbscriptsForm.edit(record)
     }
 
     let node = document.getElementById('verify-card-box-tab').parentNode
@@ -1102,6 +1194,14 @@
       })
     } else 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 {
@@ -1267,7 +1367,7 @@
 
   render() {
     const { card } = this.props
-    const { verify, fields, uniqueFields, uniqueColumns, unionFields, onceUniqueColumns, columnsFields, contrastColumns, customColumns, orderColumns, scriptsColumns, orderModular, orderModularDetail, voucher, voucherDetail, notes } = this.state
+    const { verify, fields, uniqueFields, uniqueColumns, unionFields, onceUniqueColumns, columnsFields, contrastColumns, customColumns, orderColumns, scriptsColumns, cbScriptsColumns, orderModular, orderModularDetail, voucher, voucherDetail, notes } = this.state
     const formItemLayout = {
       labelCol: {
         xs: { span: 24 },
@@ -1281,8 +1381,8 @@
 
     return (
       <div id="verify-card-box-tab">
-        {card.intertype === 'system' ? <Tabs defaultActiveKey="1" className="verify-card-box">
-          <TabPane tab="鍩虹楠岃瘉" key="1">
+        <Tabs defaultActiveKey="1" className="verify-card-box">
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab="鍩虹楠岃瘉" key="1">
             <Form {...formItemLayout}>
               <Row gutter={24}>
                 {this.props.card.sqlType !== 'custom' ? <Col span={8}>
@@ -1388,8 +1488,8 @@
                 </Col> : null}
               </Row>
             </Form>
-          </TabPane>
-          <TabPane tab={
+          </TabPane> : null}
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab={
             <span>
               姣旇緝楠岃瘉
               {verify.contrasts.length ? <span className="count-tip">{verify.contrasts.length}</span> : null}
@@ -1397,8 +1497,8 @@
           } key="2x">
             <ContrastForm dict={this.props.dict} contrastChange={this.contrastChange}/>
             <EditTable actions={['edit', 'move', 'copy', 'del']} type="contrastverify" data={verify.contrasts} columns={contrastColumns} onChange={(contrasts) => this.setState({verify: {...verify, contrasts}})}/>
-          </TabPane>
-          <TabPane tab={
+          </TabPane> : null}
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab={
             <span>
               鑷畾涔夐獙璇�
               {verify.customverifys.length ? <span className="count-tip">{verify.customverifys.length}</span> : null}
@@ -1413,8 +1513,8 @@
               wrappedComponentRef={(inst) => this.customForm = inst}
             />
             <EditTable actions={['move']} data={verify.customverifys} columns={customColumns} onChange={(customverifys) => {this.setState({verify: {...verify, customverifys}})}}/>
-          </TabPane>
-          <TabPane tab={
+          </TabPane> : null}
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab={
             <span>
               鍗曞彿鐢熸垚
               {verify.billcodes.length ? <span className="count-tip">{verify.billcodes.length}</span> : null}
@@ -1432,8 +1532,8 @@
               wrappedComponentRef={(inst) => this.orderForm = inst}
             />
             <EditTable actions={['move']} data={verify.billcodes} columns={orderColumns} onChange={(billcodes) => {this.setState({verify: {...verify, billcodes}})}}/>
-          </TabPane>
-          <TabPane tab={
+          </TabPane> : null}
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab={
             <span>
               {card.Ot !== 'requiredOnce' ? '鍞竴鎬ч獙璇�' : '鍚岀被鏁版嵁楠岃瘉'}
               {verify.uniques.length ? <span className="count-tip">{verify.uniques.length}</span> : null}
@@ -1446,8 +1546,8 @@
               uniqueChange={this.uniqueChange}
             />
             <EditTable actions={['edit', 'move', 'del']} data={verify.uniques} columns={card.Ot !== 'requiredOnce' ? uniqueColumns : onceUniqueColumns} onChange={this.changeUniques}/>
-          </TabPane>
-          <TabPane tab={
+          </TabPane> : null}
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab={
             <span>
               鍒涘缓鍑瘉
               {verify.voucher && verify.voucher.enabled ? <span className="count-tip">1</span> : null}
@@ -1462,26 +1562,44 @@
               voucherChange={this.voucherChange}
               wrappedComponentRef={(inst) => this.voucherForm = inst}
             />
-          </TabPane>
-          <TabPane tab={
+          </TabPane> : null}
+          {card.intertype === 'system' || (card.intertype === 'custom' && card.procMode === 'system') ? <TabPane tab={
             <span>
               鑷畾涔夎剼鏈�
               {verify.scripts.length ? <span className="count-tip">{verify.scripts.length}</span> : null}
             </span>
           } key="6">
             <CustomScript
-              usefulfields={this.state.usefulfields}
-              initsql={this.state.initsql}
-              dict={this.props.dict}
               btn={this.props.card}
+              dict={this.props.dict}
+              initsql={this.state.initsql}
               customScripts={verify.scripts}
               defaultsql={this.state.defaultsql}
+              usefulfields={this.state.usefulfields}
               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>
+          </TabPane> : null}
+          {card.callbackType === 'script' && card.intertype === 'custom' ? <TabPane tab={
+            <span>
+              鍥炶皟鑴氭湰
+              {verify.cbScripts.length ? <span className="count-tip">{verify.cbScripts.length}</span> : null}
+            </span>
+          } key="6a">
+            <CallBackCustomScript
+              btn={this.props.card}
+              dict={this.props.dict}
+              initsql={this.state.initsql}
+              customScripts={verify.cbScripts}
+              usefulfields={this.state.usefulfields}
+              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="淇℃伅鎻愮ず" key="7">
             <Form {...formItemLayout}>
               <Row gutter={24}>
@@ -1569,96 +1687,7 @@
               </Row>
             </Form>
           </TabPane>
-        </Tabs> : null}
-        {card.intertype !== 'system' ? <Tabs defaultActiveKey="7" className="verify-card-box">
-          <TabPane tab="淇℃伅鎻愮ず" key="7">
-            <Form {...formItemLayout}>
-              <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"> 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"> -1 </span>
-                    涓嶆彁绀�
-                  </Form.Item>
-                </Col>
-              </Row>
-            </Form>
-          </TabPane>
-        </Tabs> : null}
+        </Tabs>
       </div>
     )
   }
diff --git a/src/utils/utils-datamanage.js b/src/utils/utils-datamanage.js
index a30cbeb..95707ed 100644
--- a/src/utils/utils-datamanage.js
+++ b/src/utils/utils-datamanage.js
@@ -18,7 +18,7 @@
   static getQueryDataParams (setting, arrFields, search = [], orderBy = '', pageIndex = 1, pageSize = 10, BID, menuType, id) {
     let param = null
 
-    if (setting.interType === 'system' || (setting.interType === 'inner' && !setting.innerFunc)) {
+    if (setting.interType === 'system' || (setting.interType === 'custom' && setting.requestMode === 'system')) {
       param = this.getDefaultQueryParam(setting, arrFields, search, orderBy, pageIndex, pageSize, menuType, id)
     } else {
       param = this.getCustomQueryParam(setting, search, orderBy, pageIndex, pageSize, menuType, id)
@@ -52,7 +52,7 @@
       param.PageSize = pageSize
     }
 
-    if (setting.interType === 'inner') {
+    if (setting.interType === 'inner' || (setting.interType === 'custom' && setting.requestMode === 'inner')) {
       param.func = setting.innerFunc
     } else {
       if (menuType === 'HS') {
@@ -323,4 +323,248 @@
 
     return param
   }
+
+  /**
+   * @description 鏁版嵁婧愬悕绉帮紝鐢ㄤ簬缁熶竴鏌ヨ
+   * @param {Object}   setting      鏁版嵁婧愯缃�
+   * @param {String}   arrFields    鏌ヨ瀛楁
+   * @param {Array}    search       鎼滅储鏉′欢
+   * @param {String}   orderBy      鎺掑簭鏂瑰紡
+   * @param {Number}   pageIndex    椤电爜
+   * @param {Number}   pageSize     姣忛〉鏁伴噺
+   * @param {String}   BID          涓婄骇ID
+   * @param {String}   menuType     鑿滃崟绫诲瀷锛屾櫘閫氳彍鍗曚笌HS
+   * @return {Object}  param
+   */
+  static getPrevQueryParams (setting, search = [], BID, menuType) {
+    let param = null
+
+    if (setting.procMode !== 'inner') {
+      param = this.getDefaultPrevQueryParam(setting, search, menuType)
+    } else {
+      param = Utils.formatCustomMainSearch(search)
+      param.func = setting.prevFunc || ''
+    }
+
+    if (BID) {
+      param.BID = BID
+    }
+
+    return param
+  }
+
+  /**
+   * @description 鑾峰彇绯荤粺瀛樺偍杩囩▼ sPC_Get_TableData 鐨勫弬鏁�
+   */
+  static getDefaultPrevQueryParam (setting, search, menuType) {
+    let param = {
+      func: 'sPC_TableData_InUpDe',
+      exec_type: 'y',
+      script_type: 'Y'
+    }
+
+    let sql = ''
+    let userName = sessionStorage.getItem('User_Name') || ''
+    let fullName = sessionStorage.getItem('Full_Name') || ''
+
+    if (sessionStorage.getItem('isEditState') === 'true') {
+      userName = sessionStorage.getItem('CloudUserName') || ''
+      fullName = sessionStorage.getItem('CloudFullName') || ''
+    }
+
+    setting.preScripts.forEach(item => {
+      if (item.status === 'false') return
+      sql += `${item.sql}
+      `
+    })
+
+    if (sql) {
+      sql = `/*鍓嶇疆鑴氭湰*/
+        declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
+        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+        ${sql}
+        aaa:
+          if @ErrorCode!=''
+            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+      `
+
+      let allSearch = Utils.getAllSearchOptions(search)
+      let regoptions = allSearch.map(item => {
+        return {
+          reg: new RegExp('@' + item.key + '@', 'ig'),
+          value: `'${item.value}'`
+        }
+      })
+      regoptions.push({
+        reg: new RegExp('@userName@', 'ig'),
+        value: userName
+      }, {
+        reg: new RegExp('@fullName@', 'ig'),
+        value: fullName
+      })
+
+      regoptions.forEach(item => {
+        sql = sql.replace(item.reg, item.value)
+      })
+
+      // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
+      if ((options.sysType === 'local' && !window.GLOB.systemType) || window.debugger === true) {
+        console.info(sql.replace(/\n\s{8}/ig, '\n'))
+      }
+    }
+
+    if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+      sql = sql.replace(/\$@/ig, '/*')
+      sql = sql.replace(/@\$/ig, '*/')
+    } else {
+      sql = sql.replace(/@\$|\$@/ig, '')
+    }
+
+    param.LText = Utils.formatOptions(sql)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+
+    if (menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉
+      param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp)
+    }
+
+    param.menuname = setting.MenuName || ''
+
+    return param
+  }
+
+  /**
+   * @description 鑾峰彇绯荤粺瀛樺偍杩囩▼ sPC_Get_TableData 鐨勫弬鏁�
+   */
+  static getCallBackQueryParams (setting, sql, errSql) {
+    let param = {
+      func: 'sPC_TableData_InUpDe',
+      exec_type: 'y',
+    }
+
+    let userName = sessionStorage.getItem('User_Name') || ''
+    let fullName = sessionStorage.getItem('Full_Name') || ''
+
+    if (sessionStorage.getItem('isEditState') === 'true') {
+      userName = sessionStorage.getItem('CloudUserName') || ''
+      fullName = sessionStorage.getItem('CloudFullName') || ''
+    }
+
+    let _prevCustomScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
+        Select @ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}'
+        ${errSql}
+    `
+    let _backCustomScript = `
+    `
+
+    setting.cbScripts.forEach(script => {
+      if (script.status === 'false') return
+
+      if (script.position === 'front') {
+        _prevCustomScript += `
+        /* 鑷畾涔夎剼鏈� */
+        ${script.sql}
+        `
+      } else {
+        _backCustomScript += `
+        /* 鑷畾涔夎剼鏈� */
+        ${script.sql}
+        `
+      }
+    })
+
+    _backCustomScript += `
+      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
+
+    sql = _prevCustomScript + sql
+    sql = sql + _backCustomScript
+
+    if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+      console.info(sql.replace(/\n\s{8}/ig, '\n'))
+    }
+
+    param.LText = sql
+    
+    if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+      param.LText = param.LText.replace(/\$@/ig, '/*')
+      param.LText = param.LText.replace(/@\$/ig, '*/')
+    } else {
+      param.LText = param.LText.replace(/@\$|\$@/ig, '')
+    }
+
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+    param.LText = Utils.formatOptions(param.LText)
+    param.menuname = setting.MenuName || ''
+
+    return param
+  }
+
+  /**
+   * @description 鑾峰彇鍥炶皟sql
+   */
+  static getCallBackSql (setting, result) {
+    let lines = []
+    let pre = setting.callbackType === 'script' ? '@' : ''
+
+    let getDefaultSql = (obj, tb, bid, level) => {
+      let keys = []
+      let vals = []
+      let subObjs = []
+      let id = Utils.getuuid()
+
+      delete obj.$$key
+
+      Object.keys(obj).forEach(key => {
+        let val = obj[key]
+        if (val === null || val === undefined) return
+        if (typeof(val) === 'object') {
+          if (Array.isArray(val)) {
+            val.forEach(item => {
+              if (typeof(item) !== 'object' || Array.isArray(item)) return
+              if (Object.keys(item).length > 0) {
+                item.$$key = tb + '_' + key
+                subObjs.push(item)
+              }
+            })
+          } else if (Object.keys(val).length > 0) {
+            val.$$key = tb + '_' + key
+            subObjs.push(val)
+          }
+        } else {
+          keys.push(key)
+          vals.push(`'${val}'`)
+        }
+      })
+
+      lines.push({
+        table: tb,
+        insert: `Insert into ${pre}${tb} (${keys.join(',')},mk_level,mk_id,mk_bid)`,
+        select: `Select ${vals.join(',')},'${level}','${id}','${bid}'`
+      })
+
+      subObjs.forEach(item => {
+        getDefaultSql(item, item.$$key, id, level + 1)
+      })
+    }
+    
+    getDefaultSql(result, setting.cbTable, '', 1)
+
+    let lineMap = new Map()
+    lines.forEach(line => {
+      if (lineMap.has(line.table)) {
+        let _line = lineMap.get(line.table)
+        _line.selects.push(line.select)
+        lineMap.set(line.table, _line)
+      } else {
+        lineMap.set(line.table, {
+          table: line.table,
+          insert: line.insert,
+          selects: [line.select]
+        })
+      }
+    })
+
+    return [...lineMap.values()]
+  }
 }
\ No newline at end of file
diff --git a/src/utils/utils-update.js b/src/utils/utils-update.js
index 7db6fb5..8c06998 100644
--- a/src/utils/utils-update.js
+++ b/src/utils/utils-update.js
@@ -1,169 +1,164 @@
-import { fromJS } from 'immutable'
-import Utils from './utils.js'
+/**
+ * @description 鍗囩骇涓昏〃淇℃伅
+ * @param {Object}   config      椤甸潰閰嶇疆淇℃伅
+ * @return {Object}  config
+ */
+export function updateCommonTable (config) {
+  if (!config.version || config.version < '1.0') {
+    config.version = '1.0'
+    // 鍏煎鏍囩
+    if (!config.tabgroups) {
+      config.tabgroups = [{ uuid: 'tabs', sublist: [] }]
+    } else if (typeof(config.tabgroups[0]) === 'string') {
+      let _tabgroups = []
+      config.tabgroups.forEach(groupId => {
+        let _group = {
+          uuid: groupId,
+          sublist: config[groupId]
+        }
 
-export default class UpdateUtils {
-  /**
-   * @description 鍗囩骇涓昏〃淇℃伅
-   * @param {Object}   config      椤甸潰閰嶇疆淇℃伅
-   * @return {Object}  config
-   */
-  static updateCommonTable (config) {
-    if (!config.version || config.version < '1.0') {
-      config.version = '1.0'
-      // 鍏煎鏍囩
-      if (!config.tabgroups) {
-        config.tabgroups = [{ uuid: 'tabs', sublist: [] }]
-      } else if (typeof(config.tabgroups[0]) === 'string') {
-        let _tabgroups = []
-        config.tabgroups.forEach(groupId => {
-          let _group = {
-            uuid: groupId,
-            sublist: fromJS(config[groupId]).toJS()
-          }
-  
-          delete config[groupId]
-  
-          _tabgroups.push(_group)
-        })
-  
-        config.tabgroups = _tabgroups
-      }
-  
-      // 鍏煎鍥捐〃
-      if (!config.charts) {
-        config.expand = true
-        config.charts = [{ uuid: Utils.getuuid(), label: '', title: '', chartType: 'table', icon: 'table', Hide: 'false', blacklist: [] }]
-      } else {
-        config.charts.forEach(card => {
-          if (card.chartType === 'card') {
-            card.details = card.details.map(_cell => {
-              _cell.fontSize = _cell.fontSize || 14
-              if (!_cell.width) {
-                _cell.width = 100
-              } else if (_cell.width === 'helf') {
-                _cell.width = 50
-              } else if (_cell.width === 'third') {
-                _cell.width = 33
-              }
-              if (!_cell.fontWeight && _cell.bold === 'true') {
-                _cell.fontWeight = 'normal'
-              }
-              _cell.height = _cell.height || 1
-  
-              return _cell
-            })
-          }
-        })
-      }
+        delete config[groupId]
+
+        _tabgroups.push(_group)
+      })
+
+      config.tabgroups = _tabgroups
     }
 
-    if (config.version < '1.1') {
-      config.version = '1.1'
-      if (config.setting.interType === 'inner' && !config.setting.innerFunc) {
-        config.setting.interType = 'system'
-      }
-      // 鍏煎鎺ュ彛绫诲瀷
-      config.action = config.action.map(item => {
-        if (item.intertype === 'inner' && !item.innerFunc) {
-          item.intertype = 'system'
+    // 鍏煎鍥捐〃
+    if (!config.charts) {
+      config.expand = true
+      config.charts = [{ uuid: '$$normaltable', label: '', title: '', chartType: 'table', icon: 'table', Hide: 'false', blacklist: [] }]
+    } else {
+      config.charts.forEach(card => {
+        if (card.chartType === 'card') {
+          card.details = card.details.map(_cell => {
+            _cell.fontSize = _cell.fontSize || 14
+            if (!_cell.width) {
+              _cell.width = 100
+            } else if (_cell.width === 'helf') {
+              _cell.width = 50
+            } else if (_cell.width === 'third') {
+              _cell.width = 33
+            }
+            if (!_cell.fontWeight && _cell.bold === 'true') {
+              _cell.fontWeight = 'normal'
+            }
+            _cell.height = _cell.height || 1
+
+            return _cell
+          })
         }
-        return item
       })
     }
-
-    if (config.version < '1.2') {
-      config.version = '1.2'
-      // 鍏煎鍔熻兘鎸夐挳
-      config.action = config.action.map(item => {
-        if (item.execMode) {
-          item.OpenType = 'funcbutton'
-        }
-        return item
-      })
-    }
-
-    config.Template = 'CommonTable'
-
-    return config
   }
 
-  /**
-   * @description 鍗囩骇瀛愯〃淇℃伅
-   * @param {Object}   config      椤甸潰閰嶇疆淇℃伅
-   * @return {Object}  config
-   */
-  static updateSubTable (config) {
-    if (!config.version || config.version < '1.0') {
-      config.version = '1.0'
-      // 鍏煎鍥捐〃
-      if (!config.charts) {
-        config.expand = false
-        config.charts = [{
-          uuid: Utils.getuuid(),
-          label: '',
-          title: '',
-          chartType: 'table',
-          icon: 'table',
-          Hide: 'false',
-          blacklist: []
-        }]
-      } else {
-        config.charts.forEach(card => {
-          if (card.chartType === 'card') {
-            card.details = card.details.map(_cell => {
-              if (!_cell.fontSize) {
-                _cell.fontSize = 14
-              }
-              if (!_cell.width) {
-                _cell.width = 100
-              } else if (_cell.width === 'helf') {
-                _cell.width = 50
-              } else if (_cell.width === 'third') {
-                _cell.width = 33
-              }
-        
-              if (_cell.bold === 'true') {
-                _cell.fontWeight = 'normal'
-              }
-
-              if (!_cell.height) {
-                _cell.height = 1
-              }
-
-              return _cell
-            })
-          }
-        })
+  if (config.version < '1.1') {
+    config.version = '1.1'
+    if (config.setting.interType === 'inner' && !config.setting.innerFunc) {
+      config.setting.interType = 'system'
+    }
+    // 鍏煎鎺ュ彛绫诲瀷
+    config.action = config.action.map(item => {
+      if (item.intertype === 'inner' && !item.innerFunc) {
+        item.intertype = 'system'
       }
-    }
-
-    if (config.version < '1.1') {
-      config.version = '1.1'
-      if (config.setting.interType === 'inner' && !config.setting.innerFunc) {
-        config.setting.interType = 'system'
-      }
-      // 鍏煎鎺ュ彛绫诲瀷
-      config.action = config.action.map(item => {
-        if (item.intertype === 'inner' && !item.innerFunc) {
-          item.intertype = 'system'
-        }
-        return item
-      })
-    }
-
-    if (config.version < '1.2') {
-      config.version = '1.2'
-      // 鍏煎鍔熻兘鎸夐挳
-      config.action = config.action.map(item => {
-        if (item.execMode) {
-          item.OpenType = 'funcbutton'
-        }
-        return item
-      })
-    }
-    
-    config.Template = 'SubTable'
-
-    return config
+      return item
+    })
   }
+
+  if (config.version < '1.2') {
+    config.version = '1.2'
+    // 鍏煎鍔熻兘鎸夐挳
+    config.action = config.action.map(item => {
+      if (item.execMode) {
+        item.OpenType = 'funcbutton'
+      }
+      return item
+    })
+  }
+
+  config.Template = 'CommonTable'
+
+  return config
+}
+
+/**
+ * @description 鍗囩骇瀛愯〃淇℃伅
+ * @param {Object}   config      椤甸潰閰嶇疆淇℃伅
+ * @return {Object}  config
+ */
+export function updateSubTable (config) {
+  if (!config.version || config.version < '1.0') {
+    config.version = '1.0'
+    // 鍏煎鍥捐〃
+    if (!config.charts) {
+      config.expand = false
+      config.charts = [{
+        uuid: '$$normalsubtable',
+        label: '',
+        title: '',
+        chartType: 'table',
+        icon: 'table',
+        Hide: 'false',
+        blacklist: []
+      }]
+    } else {
+      config.charts.forEach(card => {
+        if (card.chartType === 'card') {
+          card.details = card.details.map(_cell => {
+            if (!_cell.fontSize) {
+              _cell.fontSize = 14
+            }
+            if (!_cell.width) {
+              _cell.width = 100
+            } else if (_cell.width === 'helf') {
+              _cell.width = 50
+            } else if (_cell.width === 'third') {
+              _cell.width = 33
+            }
+      
+            if (_cell.bold === 'true') {
+              _cell.fontWeight = 'normal'
+            }
+
+            if (!_cell.height) {
+              _cell.height = 1
+            }
+
+            return _cell
+          })
+        }
+      })
+    }
+  }
+
+  if (config.version < '1.1') {
+    config.version = '1.1'
+    if (config.setting.interType === 'inner' && !config.setting.innerFunc) {
+      config.setting.interType = 'system'
+    }
+    // 鍏煎鎺ュ彛绫诲瀷
+    config.action = config.action.map(item => {
+      if (item.intertype === 'inner' && !item.innerFunc) {
+        item.intertype = 'system'
+      }
+      return item
+    })
+  }
+
+  if (config.version < '1.2') {
+    config.version = '1.2'
+    // 鍏煎鍔熻兘鎸夐挳
+    config.action = config.action.map(item => {
+      if (item.execMode) {
+        item.OpenType = 'funcbutton'
+      }
+      return item
+    })
+  }
+  
+  config.Template = 'SubTable'
+
+  return config
 }
\ No newline at end of file
diff --git a/src/utils/utils.js b/src/utils/utils.js
index f97d965..ce58c46 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -183,45 +183,6 @@
   }
 
   /**
-   * @description 瑙e瘑
-   * @return {String}   value
-   */
-  static UnformatOptions (value) {
-    if (!value) return ''
-    let salt = 'minKe' // 鐩愬��
-    let _value = ''
-
-    try {
-      try {
-        _value = JSON.parse(window.decodeURIComponent(window.atob(value)))
-      } catch {
-        _value = ''
-      }
-
-      if (!_value) {
-        _value = window.atob(value)
-        _value = _value.replace(salt, '')
-        _value = window.decodeURIComponent(window.atob(_value))
-  
-        _value = _value.replace(/\smpercent\s/g, '%')
-  
-        formatKeys.forEach(item => {
-          let reg = new RegExp(item.value, 'g')
-          _value = _value.replace(reg, ' ' + item.key + ' ')
-        })
-  
-        _value = _value.replace(/\s\n\s/ig, '\n')
-        _value = _value.replace(/(^\s+|\s+$)/ig, '')
-      }
-    } catch {
-      console.warn('UnFormat Failure')
-      _value = ''
-    }
-
-    return _value
-  }
-
-  /**
    * @description sPC_TableData_InUpDe sql鍔犲瘑
    * @return {String}  value
    */
@@ -762,941 +723,962 @@
       field: arrfield
     }
   }
+}
 
-  /**
-   * @description 鑾峰彇excel瀵煎叆鍙傛暟
-   * @return {String} btn   鎸夐挳
-   * @return {String} data  excel鏁版嵁
-   */
-  static getExcelInSql (item, data, dict, BID) {
-    let btn = item.verify
-    let keys = ['delete', 'drop', 'insert', 'truncate', 'update']
-    let userName = sessionStorage.getItem('User_Name') || ''
-    let fullName = sessionStorage.getItem('Full_Name') || ''
+/**
+ * @description 鑾峰彇excel瀵煎叆鍙傛暟
+ * @return {String} btn   鎸夐挳
+ * @return {String} data  excel鏁版嵁
+ */
+export function getExcelInSql (item, data, dict, BID) {
+  let btn = item.verify
+  let keys = ['delete', 'drop', 'insert', 'truncate', 'update']
+  let userName = sessionStorage.getItem('User_Name') || ''
+  let fullName = sessionStorage.getItem('Full_Name') || ''
 
-    if (sessionStorage.getItem('isEditState') === 'true') {
-      userName = sessionStorage.getItem('CloudUserName') || ''
-      fullName = sessionStorage.getItem('CloudFullName') || ''
-    }
-
-    let database = item.sheet.match(/(.*)\.(.*)\./ig) || ''
-    let sheet = item.sheet.replace(/(.*)\.(.*)\./ig, '')
-
-    let errors = []
-    let _topline = btn.range || 0
-    let upId = this.getuuid()
-
-    let _initCustomScript = '' // 鍒濆鍖栬剼鏈�
-    let _prevCustomScript = '' // 榛樿sql鍓嶆墽琛岃剼鏈�
-    let _backCustomScript = '' // 榛樿sql鍚庢墽琛岃剼鏈�
-
-    if (btn.scripts) {
-      btn.scripts.forEach(script => {
-        if (script.status === 'false') return
-
-        if (script.position === 'init') {
-          _initCustomScript += `
-        /* 鑷畾涔夎剼鏈� */
-        ${script.sql}
-        `
-        } else if (script.position === 'front') {
-          _prevCustomScript += `
-        /* 鑷畾涔夎剼鏈� */
-        ${script.sql}
-        `
-        } else {
-          _backCustomScript += `
-        /* 鑷畾涔夎剼鏈� */
-        ${script.sql}
-        `
-        }
-      })
-    }
-
-    // 鎺у埗鍙版墦鍗版暟鎹�
-    let conLtext = []
-    let _Ltext = data.map((item, lindex) => {
-      let vals = []
-      let convals = []
-      btn.columns.forEach((col, cindex) => {
-        if (col.import === 'false') return
-
-        let val = item[col.Column] !== undefined ? item[col.Column] : ''
-        let _position = (_topline + lindex + 1) + dict['main.excel.line'] + ' ' + (cindex + 1) + dict['main.excel.column']  + ' '
-
-        if (/^Nvarchar/ig.test(col.type)) {
-          if (typeof(val) === 'number') {
-            val = val.toString()
-          }
-
-          val = val.replace(/(^\s*$)|\t*|\v*/ig, '')
-
-          if (!val && col.required === 'true') {            // 蹇呭~鏍¢獙
-            errors.push(_position + dict['main.excel.content.emptyerror'])
-          } else if (col.limit && val.length > col.limit) { // 闀垮害鏍¢獙
-            errors.push(_position + dict['main.excel.content.maxlimit'])
-          } else {                                          // 鍏抽敭瀛楁牎楠�
-            keys.forEach(key => {
-              let _patten = new RegExp('(^' + key + '\\s+)|(\\s+' + key + '\\s+)', 'ig')
-              if (_patten.test(val)) {
-                errors.push(_position + dict['main.excel.includekey'] + key)
-              }
-            })
-          }
-        } else if (/^int/ig.test(col.type)) {
-          if (!val && val !== 0) {
-            errors.push(_position + dict['main.excel.content.emptyerror'])
-          } else {
-            let _val = val + ''
-
-            if (!/^(([^0][0-9]+|0)$)|^(([1-9]+)$)/.test(_val)) {               // 妫�楠屾槸鍚︿负鏁存暟
-              errors.push(_position + dict['main.excel.content.interror'])
-            } else if ((col.min || col.min === 0) && val < col.min) {          // 鏈�灏忓�兼楠�
-              errors.push(_position + dict['main.excel.content.limitmin'])
-            } else if ((col.max || col.max === 0) && val > col.max) {          // 鏈�澶у�兼楠�
-              errors.push(_position + dict['main.excel.content.limitmax'])
-            }
-          }
-        } else if (/^Decimal/ig.test(col.type)) {
-          if (!val && val !== 0) {
-            errors.push(_position + dict['main.excel.content.emptyerror'])
-          } else {
-            let _val = val + ''
-            let _vals = _val.split('.')
-
-            if (!/^(([^0][0-9]+|0)\.([0-9]+)$)|^(([^0][0-9]+|0)$)|^(([1-9]+)\.([0-9]+)$)|^(([1-9]+)$)/.test(_val)) {                           // 妫�楠屾槸鍚︿负娴偣鏁�
-              errors.push(_position + dict['main.excel.content.floaterror'])
-            } else if (_vals[0].length > 18) {                          // 妫�楠屾暣鏁颁綅
-              errors.push(_position + dict['main.excel.content.floatIntover'])
-            } else if (_vals[1] && _vals[1].length > col.limit) {       // 鏈�灏忓�兼楠�
-              errors.push(_position + dict['main.excel.content.floatPointover'])
-            } else if ((col.min || col.min === 0) && val < col.min) {   // 鏈�灏忓�兼楠�
-              errors.push(_position + dict['main.excel.content.limitmin'])
-            } else if ((col.max || col.max === 0) && val > col.max) {   // 鏈�澶у�兼楠�
-              errors.push(_position + dict['main.excel.content.limitmax'])
-            }
-          }
-        } else if (col.type === 'date') {
-          if (typeof(val) === 'number') {
-            if (val > 2958465 || val <= 0) {                 // 鏃堕棿杩囧ぇ鎴栧皬浜庣瓑浜�0
-              errors.push(_position + dict['main.excel.content.date.over'])
-            } else {                                         // 鏃堕棿鏍煎紡鍖�
-              if (val < 60) {                                // 1900-2-29锛宔xcel涓瓨鍦紝瀹為檯涓嶅瓨鍦�
-                val++
-              }
-              val = moment('19000101', 'YYYYMMDD').add(Math.floor(val - 2), 'days').format('YYYY-MM-DD')
-            }
-          } else if (typeof(val) === 'string') {
-            val = val.replace(/(^\s*$)|\t*|\v*/ig, '')
-            if (!val && col.required === 'true') {           // 鏃堕棿蹇呭~鏍¢獙
-              errors.push(_position + dict['main.excel.content.emptyerror'])
-            } else if (val && !/^[1-9][0-9]{3}/.test(val)) { // 鏃堕棿姝e垯鏍¢獙
-              errors.push(_position + dict['main.excel.content.date.formatError'])
-            }
-          } else {                                           // 鏃堕棿鏍煎紡閿欒
-            errors.push(_position + dict['main.excel.content.date.formatError'])
-          }
-        }
-
-        vals.push(`'${val}'`)
-
-        if (lindex < 40) {
-          convals.push(`'${val}' as ${col.Column}`)
-        }
-      })
-
-      let _lineIndex = '0000' + (lindex + 1) + '0'
-      _lineIndex = _lineIndex.substring(_lineIndex.length - 6)
-
-      vals.push(`'${upId + _lineIndex}'`)
-      vals.push(`'${BID}'`)
-      
-      if (lindex < 40) {
-        convals.push(`'${upId + _lineIndex}' as jskey`)
-        convals.push(`'${BID}' as BID`)
-        conLtext.push(`Select ${convals.join(',')}`)
-      }
-
-      return `Select ${vals.join(',')}`
-    })
-
-    let result = []
-    for(let i = 0; i < _Ltext.length; i += 20) {
-      result.push(_Ltext.slice(i, i + 20))
-    }
-
-    let _sql = ''
-    let _sqlInsert = ''
-    let _sqlBottom = ''
-
-    if (item.intertype === 'system') {
-      let _uniquesql = ''
-      if (btn.uniques && btn.uniques.length > 0) {
-        btn.uniques.forEach(unique => {
-          if (unique.status === 'false') return
-
-          let _fields = unique.field.split(',')
-          let _fields_ = _fields.map(_field => `a.${_field}=b.${_field}`)
-          let _afields = _fields.map(_field => `a.${_field}`)
-          _fields_ = _fields_.join(' and ')
-
-          if (unique.verifyType !== 'physical') {
-            _fields_ += ' and b.deleted=0'
-          }
-
-          _uniquesql += `
-        /* 閲嶅鎬ч獙璇� */
-        Set @tbid=''
-        Select top 1 @tbid=${_fields.join('+\' \'+')} from (select 1 as n,${unique.field} from @${sheet} ) a group by ${unique.field} having sum(n)>1
-        
-        If @tbid!=''
-        Begin
-          select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 閲嶅'
-          goto aaa
-        end
-        
-        Set @tbid=''
-        Select top 1 @tbid=${_afields.join('+\' \'+')} from  @${sheet} a Inner join ${sheet} b on ${_fields_}
-        
-        If @tbid!=''
-        Begin
-          select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 涓庡凡鏈夋暟鎹噸澶�'
-          goto aaa
-        end
-        `
-        })
-      }
-
-      let declarefields = []
-      let fields = []
-
-      btn.columns.forEach(col => {
-        if (col.import === 'false') return
-        
-        if (col.type === 'date') {
-          declarefields.push(`${col.Column} Nvarchar(50)`)
-        } else {
-          declarefields.push(`${col.Column} ${col.type}`)
-        }
-        fields.push(col.Column)
-      })
-
-      fields = fields.join(',')
-
-      let _insert = ''
-      if (btn.default !== 'false') {
-        _insert = `
-        /* 榛樿sql */
-        Insert into ${database}${sheet} (${fields},createuserid,createuser,createstaff,bid) 
-        Select ${fields},@userid@,@username,@fullname,@BID@ From @${sheet}
-        `
-      }
-
-      _sql = `
-        /* 绯荤粺鐢熸垚 */
-        declare @${sheet} table (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) )
-        Declare @UserName nvarchar(50),@FullName nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
-        
-        Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}'
-        ${_initCustomScript}
-        `
-      _sqlInsert = `Insert into @${sheet} (${fields},jskey,BID)`
-      _sqlBottom = `
-        /* 榛樿sql */
-        delete tmp_excel_in where upid=@upid@
-        
-        delete tmp_excel_in where datediff(day,createdate,getdate())>15
-        ${_uniquesql}
-        ${_prevCustomScript}
-        ${_insert}
-        ${_backCustomScript}
-        Delete @${sheet}
-        
-        aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
-
-      if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
-        let fsql = `
-        ${_sql}
-        ${_sqlInsert}
-        
-        /* excel鏁版嵁锛堝墠40鏉★級 */
-        ${conLtext.join(' Union all ')}
-        ${_sqlBottom}
-        `
-        fsql = fsql.replace(/\n\s{8}/ig, '\n')
-        console.info(fsql)
-      }
-    } else { // s_sDataDictb_excelIn 浜戠瀵嗛挜楠岃瘉鍙傛暟
-      _sql = `
-        /* 绯荤粺鐢熸垚 */
-        declare @${sheet} table (jskey nvarchar(50))
-        Declare @UserName nvarchar(50),@FullName nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
-        
-        Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}'
-        `
-    }
-
-    return {
-      sql: _sql,
-      lines: result.map((list, index) => {
-        return {
-          Ltext: window.btoa(window.encodeURIComponent(list.join(' Union all '))),
-          Sort: (index + 1) * 10
-        }
-      }),
-      insert: _sqlInsert,
-      bottom: _sqlBottom,
-      errors: errors.join('; ')
-    }
+  if (sessionStorage.getItem('isEditState') === 'true') {
+    userName = sessionStorage.getItem('CloudUserName') || ''
+    fullName = sessionStorage.getItem('CloudFullName') || ''
   }
 
-  /**
-   * @description 浣跨敤绯荤粺鍑芥暟鏃讹紙sPC_TableData_InUpDe 锛夛紝鐢熸垚sql璇彞
-   * @return {String} type   鎵ц绫诲瀷
-   * @return {String} table  琛ㄥ悕
-   */
-  static getSysDefaultSql (btn, setting, formdata, param, data, columns, tab) {
-    let primaryId = param.ID
-    let BID = param.BID
-    let verify = btn.verify || {}
-    let datavars = {}                 // 澹版槑鐨勫彉閲忥紝琛ㄥ崟鍙婃樉绀哄垪
-    let _actionType = null
-    let appkey = window.GLOB.appkey || ''
-    let sessionUid = localStorage.getItem('SessionUid') || ''
+  let database = item.sheet.match(/(.*)\.(.*)\./ig) || ''
+  let sheet = item.sheet.replace(/(.*)\.(.*)\./ig, '')
 
-    if (verify.default !== 'false') { // 鍒ゆ柇鏄惁浣跨敤榛樿sql
-      _actionType = btn.sqlType
+  let getuuid = () => {
+    let uuid = []
+    let timestamp = new Date().getTime()
+    let _options = '0123456789abcdefghigklmnopqrstuv'
+    for (let i = 0; i < 19; i++) {
+      uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
     }
+    uuid = timestamp + uuid.join('')
+    return uuid
+  }
 
-    let _initCustomScript = '' // 鍒濆鍖栬剼鏈�
-    let _prevCustomScript = '' // 榛樿sql鍓嶆墽琛岃剼鏈�
-    let _backCustomScript = '' // 榛樿sql鍚庢墽琛岃剼鏈�
+  let errors = []
+  let _topline = btn.range || 0
+  let upId = getuuid()
 
-    verify.scripts && verify.scripts.forEach(item => {
-      if (item.status === 'false') return
+  let _initCustomScript = '' // 鍒濆鍖栬剼鏈�
+  let _prevCustomScript = '' // 榛樿sql鍓嶆墽琛岃剼鏈�
+  let _backCustomScript = '' // 榛樿sql鍚庢墽琛岃剼鏈�
 
-      if (item.position === 'init') {
+  if (btn.scripts) {
+    btn.scripts.forEach(script => {
+      if (script.status === 'false') return
+
+      if (script.position === 'init') {
         _initCustomScript += `
-        /* 鑷畾涔夎剼鏈� */
-        ${item.sql}
-        `
-      } else if (item.position === 'front') {
+      /* 鑷畾涔夎剼鏈� */
+      ${script.sql}
+      `
+      } else if (script.position === 'front') {
         _prevCustomScript += `
-        /* 鑷畾涔夎剼鏈� */
-        ${item.sql}
-        `
+      /* 鑷畾涔夎剼鏈� */
+      ${script.sql}
+      `
       } else {
         _backCustomScript += `
-        /* 鑷畾涔夎剼鏈� */
-        ${item.sql}
-        `
+      /* 鑷畾涔夎剼鏈� */
+      ${script.sql}
+      `
+      }
+    })
+  }
+
+  // 鎺у埗鍙版墦鍗版暟鎹�
+  let conLtext = []
+  let _Ltext = data.map((item, lindex) => {
+    let vals = []
+    let convals = []
+    btn.columns.forEach((col, cindex) => {
+      if (col.import === 'false') return
+
+      let val = item[col.Column] !== undefined ? item[col.Column] : ''
+      let _position = (_topline + lindex + 1) + dict['main.excel.line'] + ' ' + (cindex + 1) + dict['main.excel.column']  + ' '
+
+      if (/^Nvarchar/ig.test(col.type)) {
+        if (typeof(val) === 'number') {
+          val = val.toString()
+        }
+
+        val = val.replace(/(^\s*$)|\t*|\v*/ig, '')
+
+        if (!val && col.required === 'true') {            // 蹇呭~鏍¢獙
+          errors.push(_position + dict['main.excel.content.emptyerror'])
+        } else if (col.limit && val.length > col.limit) { // 闀垮害鏍¢獙
+          errors.push(_position + dict['main.excel.content.maxlimit'])
+        } else {                                          // 鍏抽敭瀛楁牎楠�
+          keys.forEach(key => {
+            let _patten = new RegExp('(^' + key + '\\s+)|(\\s+' + key + '\\s+)', 'ig')
+            if (_patten.test(val)) {
+              errors.push(_position + dict['main.excel.includekey'] + key)
+            }
+          })
+        }
+      } else if (/^int/ig.test(col.type)) {
+        if (!val && val !== 0) {
+          errors.push(_position + dict['main.excel.content.emptyerror'])
+        } else {
+          let _val = val + ''
+
+          if (!/^(([^0][0-9]+|0)$)|^(([1-9]+)$)/.test(_val)) {               // 妫�楠屾槸鍚︿负鏁存暟
+            errors.push(_position + dict['main.excel.content.interror'])
+          } else if ((col.min || col.min === 0) && val < col.min) {          // 鏈�灏忓�兼楠�
+            errors.push(_position + dict['main.excel.content.limitmin'])
+          } else if ((col.max || col.max === 0) && val > col.max) {          // 鏈�澶у�兼楠�
+            errors.push(_position + dict['main.excel.content.limitmax'])
+          }
+        }
+      } else if (/^Decimal/ig.test(col.type)) {
+        if (!val && val !== 0) {
+          errors.push(_position + dict['main.excel.content.emptyerror'])
+        } else {
+          let _val = val + ''
+          let _vals = _val.split('.')
+
+          if (!/^(([^0][0-9]+|0)\.([0-9]+)$)|^(([^0][0-9]+|0)$)|^(([1-9]+)\.([0-9]+)$)|^(([1-9]+)$)/.test(_val)) {                           // 妫�楠屾槸鍚︿负娴偣鏁�
+            errors.push(_position + dict['main.excel.content.floaterror'])
+          } else if (_vals[0].length > 18) {                          // 妫�楠屾暣鏁颁綅
+            errors.push(_position + dict['main.excel.content.floatIntover'])
+          } else if (_vals[1] && _vals[1].length > col.limit) {       // 鏈�灏忓�兼楠�
+            errors.push(_position + dict['main.excel.content.floatPointover'])
+          } else if ((col.min || col.min === 0) && val < col.min) {   // 鏈�灏忓�兼楠�
+            errors.push(_position + dict['main.excel.content.limitmin'])
+          } else if ((col.max || col.max === 0) && val > col.max) {   // 鏈�澶у�兼楠�
+            errors.push(_position + dict['main.excel.content.limitmax'])
+          }
+        }
+      } else if (col.type === 'date') {
+        if (typeof(val) === 'number') {
+          if (val > 2958465 || val <= 0) {                 // 鏃堕棿杩囧ぇ鎴栧皬浜庣瓑浜�0
+            errors.push(_position + dict['main.excel.content.date.over'])
+          } else {                                         // 鏃堕棿鏍煎紡鍖�
+            if (val < 60) {                                // 1900-2-29锛宔xcel涓瓨鍦紝瀹為檯涓嶅瓨鍦�
+              val++
+            }
+            val = moment('19000101', 'YYYYMMDD').add(Math.floor(val - 2), 'days').format('YYYY-MM-DD')
+          }
+        } else if (typeof(val) === 'string') {
+          val = val.replace(/(^\s*$)|\t*|\v*/ig, '')
+          if (!val && col.required === 'true') {           // 鏃堕棿蹇呭~鏍¢獙
+            errors.push(_position + dict['main.excel.content.emptyerror'])
+          } else if (val && !/^[1-9][0-9]{3}/.test(val)) { // 鏃堕棿姝e垯鏍¢獙
+            errors.push(_position + dict['main.excel.content.date.formatError'])
+          }
+        } else {                                           // 鏃堕棿鏍煎紡閿欒
+          errors.push(_position + dict['main.excel.content.date.formatError'])
+        }
+      }
+
+      vals.push(`'${val}'`)
+
+      if (lindex < 40) {
+        convals.push(`'${val}' as ${col.Column}`)
       }
     })
 
-    // 闇�瑕佸0鏄庣殑鍙橀噺闆�
-    let _vars = ['tbid', 'errorcode', 'retmsg', 'billcode', 'bvoucher', 'fibvoucherdate', 'fiyear', 'username', 'fullname', 'modulardetailcode']
+    let _lineIndex = '0000' + (lindex + 1) + '0'
+    _lineIndex = _lineIndex.substring(_lineIndex.length - 6)
 
-    // 涓婚敭瀛楁
-    let primaryKey = setting.primaryKey || 'id'
+    vals.push(`'${upId + _lineIndex}'`)
+    vals.push(`'${BID}'`)
+    
+    if (lindex < 40) {
+      convals.push(`'${upId + _lineIndex}' as jskey`)
+      convals.push(`'${BID}' as BID`)
+      conLtext.push(`Select ${convals.join(',')}`)
+    }
 
-    // sql璇彞
-    let _sql = ''
+    return `Select ${vals.join(',')}`
+  })
 
-    let _initvars = [] // 宸茶祴鍊煎瓧娈甸泦
-    let _initFormfields = []
-    let _initColfields = []
-    let _declarefields = []
+  let result = []
+  for(let i = 0; i < _Ltext.length; i += 20) {
+    result.push(_Ltext.slice(i, i + 20))
+  }
 
-    // 鑾峰彇瀛楁閿�煎
-    formdata && formdata.forEach(form => {
-      if (form.type === 'text') { // 鐗规畩瀛楁鏇挎崲
-        form.value = form.value.replace(/^(\s*)@appkey@(\s*)$/ig, appkey)
-        form.value = form.value.replace(/^(\s*)@SessionUid@(\s*)$/ig, sessionUid)
-        form.value = form.value.replace(/^(\s*)@bid@(\s*)$/ig, BID)
+  let _sql = ''
+  let _sqlInsert = ''
+  let _sqlBottom = ''
+
+  if (item.intertype === 'system') {
+    let _uniquesql = ''
+    if (btn.uniques && btn.uniques.length > 0) {
+      btn.uniques.forEach(unique => {
+        if (unique.status === 'false') return
+
+        let _fields = unique.field.split(',')
+        let _fields_ = _fields.map(_field => `a.${_field}=b.${_field}`)
+        let _afields = _fields.map(_field => `a.${_field}`)
+        _fields_ = _fields_.join(' and ')
+
+        if (unique.verifyType !== 'physical') {
+          _fields_ += ' and b.deleted=0'
+        }
+
+        _uniquesql += `
+      /* 閲嶅鎬ч獙璇� */
+      Set @tbid=''
+      Select top 1 @tbid=${_fields.join('+\' \'+')} from (select 1 as n,${unique.field} from @${sheet} ) a group by ${unique.field} having sum(n)>1
+      
+      If @tbid!=''
+      Begin
+        select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 閲嶅'
+        goto aaa
+      end
+      
+      Set @tbid=''
+      Select top 1 @tbid=${_afields.join('+\' \'+')} from  @${sheet} a Inner join ${sheet} b on ${_fields_}
+      
+      If @tbid!=''
+      Begin
+        select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 涓庡凡鏈夋暟鎹噸澶�'
+        goto aaa
+      end
+      `
+      })
+    }
+
+    let declarefields = []
+    let fields = []
+
+    btn.columns.forEach(col => {
+      if (col.import === 'false') return
+      
+      if (col.type === 'date') {
+        declarefields.push(`${col.Column} Nvarchar(50)`)
+      } else {
+        declarefields.push(`${col.Column} ${col.type}`)
+      }
+      fields.push(col.Column)
+    })
+
+    fields = fields.join(',')
+
+    let _insert = ''
+    if (btn.default !== 'false') {
+      _insert = `
+      /* 榛樿sql */
+      Insert into ${database}${sheet} (${fields},createuserid,createuser,createstaff,bid) 
+      Select ${fields},@userid@,@username,@fullname,@BID@ From @${sheet}
+      `
+    }
+
+    _sql = `
+      /* 绯荤粺鐢熸垚 */
+      declare @${sheet} table (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) )
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
+      
+      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}'
+      ${_initCustomScript}
+      `
+    _sqlInsert = `Insert into @${sheet} (${fields},jskey,BID)`
+    _sqlBottom = `
+      /* 榛樿sql */
+      delete tmp_excel_in where upid=@upid@
+      
+      delete tmp_excel_in where datediff(day,createdate,getdate())>15
+      ${_uniquesql}
+      ${_prevCustomScript}
+      ${_insert}
+      ${_backCustomScript}
+      Delete @${sheet}
+      
+      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
+
+    if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+      let fsql = `
+      ${_sql}
+      ${_sqlInsert}
+      
+      /* excel鏁版嵁锛堝墠40鏉★級 */
+      ${conLtext.join(' Union all ')}
+      ${_sqlBottom}
+      `
+      fsql = fsql.replace(/\n\s{8}/ig, '\n')
+      console.info(fsql)
+    }
+  } else { // s_sDataDictb_excelIn 浜戠瀵嗛挜楠岃瘉鍙傛暟
+    _sql = `
+      /* 绯荤粺鐢熸垚 */
+      declare @${sheet} table (jskey nvarchar(50))
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
+      
+      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}'
+      `
+  }
+
+  return {
+    sql: _sql,
+    lines: result.map((list, index) => {
+      return {
+        Ltext: window.btoa(window.encodeURIComponent(list.join(' Union all '))),
+        Sort: (index + 1) * 10
+      }
+    }),
+    insert: _sqlInsert,
+    bottom: _sqlBottom,
+    errors: errors.join('; ')
+  }
+}
+
+/**
+ * @description 浣跨敤绯荤粺鍑芥暟鏃讹紙sPC_TableData_InUpDe 锛夛紝鐢熸垚sql璇彞
+ * @return {String} type   鎵ц绫诲瀷
+ * @return {String} table  琛ㄥ悕
+ */
+export function getSysDefaultSql (btn, setting, formdata, param, data, columns, tab, retmsg = false) {
+  let primaryId = param.ID
+  let BID = param.BID
+  let verify = btn.verify || {}
+  let datavars = {}                 // 澹版槑鐨勫彉閲忥紝琛ㄥ崟鍙婃樉绀哄垪
+  let _actionType = null
+  let _callbacksql = ''
+
+  if (verify.default !== 'false') { // 鍒ゆ柇鏄惁浣跨敤榛樿sql
+    _actionType = btn.sqlType
+  }
+
+  let _initCustomScript = '' // 鍒濆鍖栬剼鏈�
+  let _prevCustomScript = '' // 榛樿sql鍓嶆墽琛岃剼鏈�
+  let _backCustomScript = '' // 榛樿sql鍚庢墽琛岃剼鏈�
+
+  verify.scripts && verify.scripts.forEach(item => {
+    if (item.status === 'false') return
+
+    if (item.position === 'init') {
+      _initCustomScript += `
+      /* 鑷畾涔夎剼鏈� */
+      ${item.sql}
+      `
+    } else if (item.position === 'front') {
+      _prevCustomScript += `
+      /* 鑷畾涔夎剼鏈� */
+      ${item.sql}
+      `
+    } else {
+      _backCustomScript += `
+      /* 鑷畾涔夎剼鏈� */
+      ${item.sql}
+      `
+    }
+  })
+
+  // 闇�瑕佸0鏄庣殑鍙橀噺闆�
+  let _vars = ['tbid', 'errorcode', 'retmsg', 'billcode', 'bvoucher', 'fibvoucherdate', 'fiyear', 'username', 'fullname', 'modulardetailcode']
+
+  // 涓婚敭瀛楁
+  let primaryKey = setting.primaryKey || 'id'
+
+  // sql璇彞
+  let _sql = ''
+
+  let _initvars = [] // 宸茶祴鍊煎瓧娈甸泦
+  let _initFormfields = []
+  let _initColfields = []
+  let _declarefields = []
+
+  // 鑾峰彇瀛楁閿�煎
+  formdata && formdata.forEach(form => {
+    let _key = form.key.toLowerCase()
+    datavars[_key] = form.value
+
+    if (!_initvars.includes(_key)) {
+      _initvars.push(_key)
+
+      if (form.type === 'number') {
+        let val = form.value
+        if (typeof(val) !== 'number') {
+          val = parseFloat(val)
+          if (isNaN(val)) {
+            val = 0
+          }
+        }
+        _initFormfields.push(`@${_key}=${val}`)
+      } else {
+        _initFormfields.push(`@${_key}='${form.value}'`)
+      }
+    }
+    
+    if (!_vars.includes(_key)) {
+      _vars.push(_key)
+
+      if (form.fieldlen && form.fieldlen > 2048) {
+        form.fieldlen = 'max'
       }
 
-      let _key = form.key.toLowerCase()
-      datavars[_key] = form.value
+      let _type = `nvarchar(${form.fieldlen})`
+
+      if (form.type.match(/date/ig)) {
+        _type = 'datetime'
+      } else if (form.type === 'number') {
+        _type = `decimal(18,${form.fieldlen})`
+      }
+
+      _declarefields.push(`@${_key} ${_type}`)
+    }
+  })
+
+  if (data) {
+    Object.keys(data).forEach(key => {
+      data[key.toLowerCase()] = data[key]
+    })
+  }
+
+  // 娣诲姞鏁版嵁涓瓧娈碉紝琛ㄥ崟鍊间紭鍏�(鎸夐挳涓嶉�夎鎴栧琛屾嫾鎺ユ椂璺宠繃)
+  if (data && btn.Ot !== 'notRequired' && btn.Ot !== 'requiredOnce') {
+    datavars = {...data, ...datavars}
+
+    const setField = (col) => {
+      if (!col.field) return
+      let _key = col.field.toLowerCase()
 
       if (!_initvars.includes(_key)) {
-        _initvars.push(_key)
+        let _val = datavars.hasOwnProperty(_key) ? datavars[_key] : ''
 
-        if (form.type === 'number') {
-          let val = form.value
-          if (typeof(val) !== 'number') {
-            val = parseFloat(val)
-            if (isNaN(val)) {
-              val = 0
-            }
-          }
-          _initFormfields.push(`@${_key}=${val}`)
-        } else {
-          _initFormfields.push(`@${_key}='${form.value}'`)
-        }
+        _initvars.push(_key)
+        _initColfields.push(`@${_key}='${_val}'`)
       }
       
       if (!_vars.includes(_key)) {
         _vars.push(_key)
 
-        if (form.fieldlen && form.fieldlen > 2048) {
-          form.fieldlen = 'max'
+        if (col.datatype) {
+          _declarefields.push(`@${_key} ${col.datatype}`)
+        } else {
+          if (col.fieldlength && col.fieldlength > 2048) {
+            col.fieldlength = 'max'
+          }
+
+          let _type = `nvarchar(${col.fieldlength || 50})`
+
+          if (col.type === 'number') {
+            let _length = col.decimal ? col.decimal : 0
+            _type = `decimal(18,${_length})`
+          } else if (col.type === 'picture' || col.type === 'textarea') {
+            _type = `nvarchar(${col.fieldlength || 512})`
+          }
+
+          _declarefields.push(`@${_key} ${_type}`)
         }
-
-        let _type = `nvarchar(${form.fieldlen})`
-
-        if (form.type.match(/date/ig)) {
-          _type = 'datetime'
-        } else if (form.type === 'number') {
-          _type = `decimal(18,${form.fieldlen})`
-        }
-
-        _declarefields.push(`@${_key} ${_type}`)
       }
-    })
+    }
 
-    if (data) {
-      Object.keys(data).forEach(key => {
-        data[key.toLowerCase()] = data[key]
+    if (columns && columns.length > 0) {
+      columns.forEach(col => {
+        if (col.type === 'colspan' || col.type === 'old_colspan') {
+          col.subcols.forEach(cell => {
+            setField(cell)
+          })
+        } else {
+          setField(col)
+        }
       })
     }
+  }
 
-    // 娣诲姞鏁版嵁涓瓧娈碉紝琛ㄥ崟鍊间紭鍏�(鎸夐挳涓嶉�夎鎴栧琛屾嫾鎺ユ椂璺宠繃)
-    if (data && btn.Ot !== 'notRequired' && btn.Ot !== 'requiredOnce') {
-      datavars = {...data, ...datavars}
+  // 鍙橀噺澹版槑
+  _declarefields = _declarefields.join(',')
+  if (_declarefields) {
+    _declarefields = ',' + _declarefields
+  }
+  _sql = `/* 绯荤粺鐢熸垚 */
+      Declare @tbid nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@BillCode nvarchar(50),@BVoucher nvarchar(50),@FIBVoucherDate nvarchar(50), @FiYear nvarchar(50), @UserName nvarchar(50),@FullName nvarchar(50),@ModularDetailCode nvarchar(50)${_declarefields}
+    `
 
-      const setField = (col) => {
-        if (!col.field) return
-        let _key = col.field.toLowerCase()
+  // 琛ㄥ崟鍙橀噺璧嬪��
+  if (_initFormfields.length > 0) {
+    _sql += `
+      /* 琛ㄥ崟鍙橀噺璧嬪�� */
+      select ${_initFormfields.join(',')}
+      `
+  }
+  // 鏄剧ず鍒楀彉閲忚祴鍊�
+  if (_initColfields.length > 0) {
+    _sql += `
+      /* 鏄剧ず鍒楀彉閲忚祴鍊� */
+      select ${_initColfields.join(',')}
+      `
+  }
 
-        if (!_initvars.includes(_key)) {
-          let _val = datavars.hasOwnProperty(_key) ? datavars[_key] : ''
+  // 鍘婚櫎绂佺敤鐨勯獙璇�
+  if (verify.contrasts) {
+    verify.contrasts = verify.contrasts.filter(item => item.status !== 'false')
+  }
+  if (verify.uniques) {
+    verify.uniques = verify.uniques.filter(item => item.status !== 'false')
+  }
+  if (verify.customverifys) {
+    verify.customverifys = verify.customverifys.filter(item => item.status !== 'false')
+  }
+  if (verify.billcodes) {
+    verify.billcodes = verify.billcodes.filter(item => item.status !== 'false')
+  }
 
-          _initvars.push(_key)
-          _initColfields.push(`@${_key}='${_val}'`)
-        }
-        
-        if (!_vars.includes(_key)) {
-          _vars.push(_key)
+  let userName = sessionStorage.getItem('User_Name') || ''
+  let fullName = sessionStorage.getItem('Full_Name') || ''
 
-          if (col.datatype) {
-            _declarefields.push(`@${_key} ${col.datatype}`)
-          } else {
-            if (col.fieldlength && col.fieldlength > 2048) {
-              col.fieldlength = 'max'
-            }
-  
-            let _type = `nvarchar(${col.fieldlength || 50})`
-  
-            if (col.type === 'number') {
-              let _length = col.decimal ? col.decimal : 0
-              _type = `decimal(18,${_length})`
-            } else if (col.type === 'picture' || col.type === 'textarea') {
-              _type = `nvarchar(${col.fieldlength || 512})`
-            }
-  
-            _declarefields.push(`@${_key} ${_type}`)
-          }
-        }
-      }
+  if (sessionStorage.getItem('isEditState') === 'true') {
+    userName = sessionStorage.getItem('CloudUserName') || ''
+    fullName = sessionStorage.getItem('CloudFullName') || ''
+  }
 
-      if (columns && columns.length > 0) {
-        columns.forEach(col => {
-          if (col.type === 'colspan' || col.type === 'old_colspan') {
-            col.subcols.forEach(cell => {
-              setField(cell)
-            })
-          } else {
-            setField(col)
-          }
-        })
-      }
-    }
-
-    // 鍙橀噺澹版槑
-    _declarefields = _declarefields.join(',')
-    if (_declarefields) {
-      _declarefields = ',' + _declarefields
-    }
-    _sql = `/* 绯荤粺鐢熸垚 */
-        Declare @tbid nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@BillCode nvarchar(50),@BVoucher nvarchar(50),@FIBVoucherDate nvarchar(50), @FiYear nvarchar(50), @UserName nvarchar(50),@FullName nvarchar(50),@ModularDetailCode nvarchar(50)${_declarefields}
+  // 鍒濆鍖栧嚟璇佸強鐢ㄦ埛淇℃伅瀛楁
+  _sql += `
+      /* 鍑瘉鍙婄敤鎴蜂俊鎭垵濮嬪寲璧嬪�� */
+      select @BVoucher='',@FIBVoucherDate='',@FiYear='',@ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @BillCode='', @ModularDetailCode=''
       `
 
-    // 琛ㄥ崟鍙橀噺璧嬪��
-    if (_initFormfields.length > 0) {
-      _sql += `
-        /* 琛ㄥ崟鍙橀噺璧嬪�� */
-        select ${_initFormfields.join(',')}
-        `
+  if (retmsg) {
+    _callbacksql = _sql
+  }
+
+  if (_initCustomScript) {
+    _sql += _initCustomScript
+  }
+
+  // 鍚敤璐︽湡楠岃瘉
+  if (verify.accountdate === 'true') {
+    let orgcode = `''`
+    let date = `''`
+    if (verify.accountfield && _initvars.includes(verify.accountfield.toLowerCase())) {
+      orgcode = '@' + verify.accountfield
     }
-    // 鏄剧ず鍒楀彉閲忚祴鍊�
-    if (_initColfields.length > 0) {
-      _sql += `
-        /* 鏄剧ず鍒楀彉閲忚祴鍊� */
-        select ${_initColfields.join(',')}
-        `
+    if (verify.voucherdate && _initvars.includes(verify.voucherdate.toLowerCase())) {
+      date = '@' + verify.voucherdate
     }
 
-    // 鍘婚櫎绂佺敤鐨勯獙璇�
-    if (verify.contrasts) {
-      verify.contrasts = verify.contrasts.filter(item => item.status !== 'false')
-    }
-    if (verify.uniques) {
-      verify.uniques = verify.uniques.filter(item => item.status !== 'false')
-    }
-    if (verify.customverifys) {
-      verify.customverifys = verify.customverifys.filter(item => item.status !== 'false')
-    }
-    if (verify.billcodes) {
-      verify.billcodes = verify.billcodes.filter(item => item.status !== 'false')
-    }
-
-    let userName = sessionStorage.getItem('User_Name') || ''
-    let fullName = sessionStorage.getItem('Full_Name') || ''
-
-    if (sessionStorage.getItem('isEditState') === 'true') {
-      userName = sessionStorage.getItem('CloudUserName') || ''
-      fullName = sessionStorage.getItem('CloudFullName') || ''
-    }
-
-    // 鍒濆鍖栧嚟璇佸強鐢ㄦ埛淇℃伅瀛楁
     _sql += `
-        /* 鍑瘉鍙婄敤鎴蜂俊鎭垵濮嬪寲璧嬪�� */
-        select @BVoucher='',@FIBVoucherDate='',@FiYear='',@ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @BillCode='', @ModularDetailCode=''
-        `
+      /* 璐︽湡楠岃瘉 */
+      exec s_FIBVoucherDateCheck @OrgCode=${orgcode},@FIBVoucherDate=${date},@ErrorCode=@ErrorCode OUTPUT,@retmsg=@retmsg OUTPUT
+      if @ErrorCode!=''
+        GOTO aaa
+      `
+  }
 
-    if (_initCustomScript) {
-      _sql += _initCustomScript
+  // 澶辨晥楠岃瘉锛屾坊鍔犳暟鎹椂涓嶇敤
+  if (btn.sqlType !== 'insert' && verify.invalid === 'true' && setting.dataresource) {
+    let datasource = setting.dataresource
+    if (/\s/.test(datasource) && !/tb$/.test(datasource)) { // 鎷兼帴鍒悕
+      datasource = '(' + datasource + ') tb'
     }
 
-    // 鍚敤璐︽湡楠岃瘉
-    if (verify.accountdate === 'true') {
-      let orgcode = `''`
-      let date = `''`
-      if (verify.accountfield && _initvars.includes(verify.accountfield.toLowerCase())) {
-        orgcode = '@' + verify.accountfield
+    if (setting.customScript) {
+      _sql += `
+      /* 鏁版嵁婧愯嚜瀹氫箟鑴氭湰锛岃娉ㄦ剰鍙橀噺瀹氫箟鏄惁閲嶅 */
+      ${setting.customScript}
+      `
+    }
+
+    if (btn.Ot === 'requiredOnce') {
+      _sql += `
+      /* 澶辨晥楠岃瘉 */
+      select @tbid='', @ErrorCode='',@retmsg=''
+      select @tbid='X' from ${datasource} right join (select ID from  dbo.SplitComma(@ID@)) sp
+      on tb.id =sp.id where tb.id is null
+
+      If @tbid!=''
+      Begin
+        select @ErrorCode='E',@retmsg='鏁版嵁宸插け鏁�'
+        goto aaa
+      end
+      `
+    } else {
+      _sql += `
+      /* 澶辨晥楠岃瘉 */
+      select @tbid='', @ErrorCode='',@retmsg=''
+      select @tbid=${primaryKey} from ${datasource} where ${primaryKey}=@ID@
+      If @tbid=''
+      Begin
+        select @ErrorCode='E',@retmsg='鏁版嵁宸插け鏁�'
+        goto aaa
+      end
+      `
+    }
+  }
+
+  // 姣旇緝楠岃瘉
+  if (verify.contrasts && verify.contrasts.length > 0) {
+    verify.contrasts.forEach(item => {
+      _sql += `
+      /* 姣旇緝楠岃瘉 */
+      If ${item.frontfield} ${item.operator} ${item.backfield}
+      Begin
+        select @ErrorCode='${item.errorCode}',@retmsg='${item.errmsg}'
+          goto aaa
+      end
+      `
+    })
+  }
+  
+  // 鑷畾涔夐獙璇�
+  verify.customverifys && verify.customverifys.forEach(item => {        
+    _sql += `
+      /* 鑷畾涔夐獙璇� */
+      select @tbid='', @ErrorCode='',@retmsg=''
+      select top 1 @tbid='X' from (${item.sql}) a
+      If @tbid ${item.result === 'true' ? '!=' : '='}''
+      Begin
+        select @ErrorCode='${item.errorCode}',@retmsg='${item.errmsg}'
+        goto aaa
+      end
+      `
+  })
+
+  // 鍗曞彿鐢熸垚锛屼娇鐢ㄤ笂绾d锛圔ID锛夋垨鍒楄〃鏁版嵁锛屽0鏄庡彉閲忥紙妫�楠岋級
+  let _billcodesSql  = ''
+  if (formdata && verify.billcodes && verify.billcodes.length > 0) {
+    let keys = formdata.map(item => item.key.toLowerCase()) // 琛ㄥ崟瀛楁
+
+    verify.billcodes.forEach(item => {
+      let _key = item.field.toLowerCase()
+      let _linkKey = item.linkField ? item.linkField.toLowerCase() : ''
+
+      if (!keys.includes(_key)) return // 琛ㄥ崟涓笉鍚崟鍙风敓鎴愬瓧娈�
+
+      let _ModularDetailCode = ''
+      let _lpline = ''
+      if (item.TypeCharOne === 'Lp') {
+        if (_linkKey === 'bid' && BID) { // 鏇挎崲bid
+          _lpline = `set @ModularDetailCode= 'Lp'+ right('${item.mark || btn.uuid}'+@BID@,48)`
+        } else {
+          _lpline = `set @ModularDetailCode= 'Lp'+ right('${item.mark || btn.uuid}'+@${_linkKey},48)`
+        }
+        _ModularDetailCode = '@ModularDetailCode'
+      } else if (item.TypeCharOne === 'BN') {
+        let _val = ''
+        if (_linkKey === 'bid' && BID) { // 鏇挎崲bid
+          _val = BID
+        } else if (data && data.hasOwnProperty(_linkKey)) {
+          _val = data[_linkKey]
+        }
+        _ModularDetailCode = `'${item.TypeCharOne + _val}'`
+      } else {
+        _ModularDetailCode = `'${item.ModularDetailCode}'`
       }
-      if (verify.voucherdate && _initvars.includes(verify.voucherdate.toLowerCase())) {
-        date = '@' + verify.voucherdate
+
+      let _declare = ''
+
+      if (!_vars.includes(_key)) {
+        _declare = `Declare @${_key} nvarchar(50)`
+        _vars.push(_key)
+      }
+
+      _billcodesSql += `
+      /* 鍗曞彿鐢熸垚 */
+      ${_declare}
+      select @BillCode='', @${_key}='', @ModularDetailCode=''
+      ${_lpline}
+      exec s_get_BillCode
+        @ModularDetailCode=${_ModularDetailCode},
+        @Type=${item.Type},
+        @TypeCharOne='${item.TypeCharOne}',
+        @TypeCharTwo ='${item.TypeCharTwo}',
+        @BillCode =@BillCode output,
+        @ErrorCode =@ErrorCode output, 
+        @retmsg=@retmsg output
+      if @ErrorCode!=''
+        goto aaa
+      set @${_key}=@BillCode
+      `
+    })
+
+    if (_actionType !== 'insertOrUpdate') {
+      _sql += _billcodesSql
+    }
+  }
+
+  // 鍞竴鎬ч獙璇侊紝蹇呴』瀛樺湪琛ㄥ崟锛堣〃鍗曞瓨鍦ㄦ椂锛屼富閿潎涓哄崟鍊硷級,蹇呴』濉啓鏁版嵁婧愶紝澶氳鎷兼帴鏃朵笉鍙敤
+  if (formdata && verify.uniques && verify.uniques.length > 0 && btn.Ot !== 'requiredOnce') {
+    verify.uniques.forEach(item => {
+      let _fieldValue = []                     // 琛ㄥ崟閿�煎field=value
+      let _value = []                          // 琛ㄥ崟鍊硷紝鐢ㄤ簬閿欒鎻愮ず
+      let _labels = item.fieldlabel.split(',') // 琛ㄥ崟鎻愮ず鏂囧瓧
+      let arr = [] // 楠岃瘉涓婚敭
+
+      item.field.split(',').forEach((_field, index) => {
+        let _key = _field.toLowerCase()
+        let _val = datavars[_key] !== undefined ? datavars[_key] : ''
+        let _fval = `'${_val}'`
+
+        if (_key === 'bid' && !datavars.bid) { // 琛ㄥ崟涓病鏈塨id鍒欎娇鐢ㄧ郴缁焍id鍙橀噺
+          _fval = '@BID@'
+        }
+        if (_key === 'bid' && tab && tab.foreignKey) {
+          arr.push(tab.foreignKey.toLowerCase())
+          _fieldValue.push(`${tab.foreignKey}=${_fval}`)
+        } else {
+          arr.push(_key)
+          _fieldValue.push(`${_key}=${_fval}`)
+        }
+        
+        _value.push(`${_labels[index] || ''}锛�${_val || ''}`)
+      })
+
+      let _verifyType = ''
+      if (item.verifyType === 'logic') {
+        _verifyType = ' and deleted=0'
+      }
+
+      if (!arr.includes(primaryKey.toLowerCase())) {
+        _fieldValue.push(`${primaryKey} !='${primaryId}'`)
       }
 
       _sql += `
-        /* 璐︽湡楠岃瘉 */
-        exec s_FIBVoucherDateCheck @OrgCode=${orgcode},@FIBVoucherDate=${date},@ErrorCode=@ErrorCode OUTPUT,@retmsg=@retmsg OUTPUT
-        if @ErrorCode!=''
-          GOTO aaa
-        `
-    }
-
-    // 澶辨晥楠岃瘉锛屾坊鍔犳暟鎹椂涓嶇敤
-    if (btn.sqlType !== 'insert' && verify.invalid === 'true' && setting.dataresource) {
-      let datasource = setting.dataresource
-      if (/\s/.test(datasource) && !/tb$/.test(datasource)) { // 鎷兼帴鍒悕
+      /* 鍞竴鎬ч獙璇� */
+      select @tbid='', @ErrorCode='',@retmsg=''
+      select @tbid='X' from ${btn.sql} where ${_fieldValue.join(' and ')}${_verifyType}
+      If @tbid!=''
+      Begin
+        select @ErrorCode='${item.errorCode}',@retmsg='${_value.join(', ')} 宸插瓨鍦�'
+        goto aaa
+      end
+      `
+    })
+  } else if (verify.uniques && verify.uniques.length > 0 && btn.Ot === 'requiredOnce' && setting.dataresource) {
+    let datasource = setting.dataresource
+    if (/\s/.test(datasource)) { // 鎷兼帴鍒悕
+      if (!/tb$/.test(datasource)) {
         datasource = '(' + datasource + ') tb'
       }
-
-      if (setting.customScript) {
-        _sql += `
-        /* 鏁版嵁婧愯嚜瀹氫箟鑴氭湰锛岃娉ㄦ剰鍙橀噺瀹氫箟鏄惁閲嶅 */
-        ${setting.customScript}
-        `
-      }
-
-      if (btn.Ot === 'requiredOnce') {
-        _sql += `
-        /* 澶辨晥楠岃瘉 */
-        select @tbid='', @ErrorCode='',@retmsg=''
-        select @tbid='X' from ${datasource} right join (select ID from  dbo.SplitComma(@ID@)) sp
-        on tb.id =sp.id where tb.id is null
-
-        If @tbid!=''
-        Begin
-          select @ErrorCode='E',@retmsg='鏁版嵁宸插け鏁�'
-          goto aaa
-        end
-        `
-      } else {
-        _sql += `
-        /* 澶辨晥楠岃瘉 */
-        select @tbid='', @ErrorCode='',@retmsg=''
-        select @tbid=${primaryKey} from ${datasource} where ${primaryKey}=@ID@
-        If @tbid=''
-        Begin
-          select @ErrorCode='E',@retmsg='鏁版嵁宸插け鏁�'
-          goto aaa
-        end
-        `
-      }
+    } else {
+      datasource = datasource + ' tb'
     }
 
-    // 姣旇緝楠岃瘉
-    if (verify.contrasts && verify.contrasts.length > 0) {
-      verify.contrasts.forEach(item => {
-        _sql += `
-        /* 姣旇緝楠岃瘉 */
-        If ${item.frontfield} ${item.operator} ${item.backfield}
-        Begin
-          select @ErrorCode='${item.errorCode}',@retmsg='${item.errmsg}'
-            goto aaa
-        end
-        `
-      })
-    }
-    
-    // 鑷畾涔夐獙璇�
-    verify.customverifys && verify.customverifys.forEach(item => {        
+    if (setting.customScript) {
       _sql += `
-        /* 鑷畾涔夐獙璇� */
-        select @tbid='', @ErrorCode='',@retmsg=''
-        select top 1 @tbid='X' from (${item.sql}) a
-        If @tbid ${item.result === 'true' ? '!=' : '='}''
-        Begin
-          select @ErrorCode='${item.errorCode}',@retmsg='${item.errmsg}'
-          goto aaa
-        end
-        `
-    })
-
-    // 鍗曞彿鐢熸垚锛屼娇鐢ㄤ笂绾d锛圔ID锛夋垨鍒楄〃鏁版嵁锛屽0鏄庡彉閲忥紙妫�楠岋級
-    let _billcodesSql  = ''
-    if (formdata && verify.billcodes && verify.billcodes.length > 0) {
-      let keys = formdata.map(item => item.key.toLowerCase()) // 琛ㄥ崟瀛楁
-
-      verify.billcodes.forEach(item => {
-        let _key = item.field.toLowerCase()
-        let _linkKey = item.linkField ? item.linkField.toLowerCase() : ''
-
-        if (!keys.includes(_key)) return // 琛ㄥ崟涓笉鍚崟鍙风敓鎴愬瓧娈�
-
-        let _ModularDetailCode = ''
-        let _lpline = ''
-        if (item.TypeCharOne === 'Lp') {
-          if (_linkKey === 'bid' && BID) { // 鏇挎崲bid
-            _lpline = `set @ModularDetailCode= 'Lp'+ right('${item.mark || btn.uuid}'+@BID@,48)`
-          } else {
-            _lpline = `set @ModularDetailCode= 'Lp'+ right('${item.mark || btn.uuid}'+@${_linkKey},48)`
-          }
-          _ModularDetailCode = '@ModularDetailCode'
-        } else if (item.TypeCharOne === 'BN') {
-          let _val = ''
-          if (_linkKey === 'bid' && BID) { // 鏇挎崲bid
-            _val = BID
-          } else if (data && data.hasOwnProperty(_linkKey)) {
-            _val = data[_linkKey]
-          }
-          _ModularDetailCode = `'${item.TypeCharOne + _val}'`
-        } else {
-          _ModularDetailCode = `'${item.ModularDetailCode}'`
-        }
-
-        let _declare = ''
-
-        if (!_vars.includes(_key)) {
-          _declare = `Declare @${_key} nvarchar(50)`
-          _vars.push(_key)
-        }
-
-        _billcodesSql += `
-        /* 鍗曞彿鐢熸垚 */
-        ${_declare}
-        select @BillCode='', @${_key}='', @ModularDetailCode=''
-        ${_lpline}
-        exec s_get_BillCode
-          @ModularDetailCode=${_ModularDetailCode},
-          @Type=${item.Type},
-          @TypeCharOne='${item.TypeCharOne}',
-          @TypeCharTwo ='${item.TypeCharTwo}',
-          @BillCode =@BillCode output,
-          @ErrorCode =@ErrorCode output, 
-          @retmsg=@retmsg output
-        if @ErrorCode!=''
-          goto aaa
-        set @${_key}=@BillCode
-        `
-      })
-
-      if (_actionType !== 'insertOrUpdate') {
-        _sql += _billcodesSql
-      }
-    }
-
-    // 鍞竴鎬ч獙璇侊紝蹇呴』瀛樺湪琛ㄥ崟锛堣〃鍗曞瓨鍦ㄦ椂锛屼富閿潎涓哄崟鍊硷級,蹇呴』濉啓鏁版嵁婧愶紝澶氳鎷兼帴鏃朵笉鍙敤
-    if (formdata && verify.uniques && verify.uniques.length > 0 && btn.Ot !== 'requiredOnce') {
-      verify.uniques.forEach(item => {
-        let _fieldValue = []                     // 琛ㄥ崟閿�煎field=value
-        let _value = []                          // 琛ㄥ崟鍊硷紝鐢ㄤ簬閿欒鎻愮ず
-        let _labels = item.fieldlabel.split(',') // 琛ㄥ崟鎻愮ず鏂囧瓧
-        let arr = [] // 楠岃瘉涓婚敭
-
-        item.field.split(',').forEach((_field, index) => {
-          let _key = _field.toLowerCase()
-          let _val = datavars[_key] !== undefined ? datavars[_key] : ''
-          let _fval = `'${_val}'`
-
-          if (_key === 'bid' && !datavars.bid) { // 琛ㄥ崟涓病鏈塨id鍒欎娇鐢ㄧ郴缁焍id鍙橀噺
-            _fval = '@BID@'
-          }
-          if (_key === 'bid' && tab && tab.foreignKey) {
-            arr.push(tab.foreignKey.toLowerCase())
-            _fieldValue.push(`${tab.foreignKey}=${_fval}`)
-          } else {
-            arr.push(_key)
-            _fieldValue.push(`${_key}=${_fval}`)
-          }
-          
-          _value.push(`${_labels[index] || ''}锛�${_val || ''}`)
-        })
-
-        let _verifyType = ''
-        if (item.verifyType === 'logic') {
-          _verifyType = ' and deleted=0'
-        }
-
-        if (!arr.includes(primaryKey.toLowerCase())) {
-          _fieldValue.push(`${primaryKey} !='${primaryId}'`)
-        }
-
-        _sql += `
-        /* 鍞竴鎬ч獙璇� */
-        select @tbid='', @ErrorCode='',@retmsg=''
-        select @tbid='X' from ${btn.sql} where ${_fieldValue.join(' and ')}${_verifyType}
-        If @tbid!=''
-        Begin
-          select @ErrorCode='${item.errorCode}',@retmsg='${_value.join(', ')} 宸插瓨鍦�'
-          goto aaa
-        end
-        `
-      })
-    } else if (verify.uniques && verify.uniques.length > 0 && btn.Ot === 'requiredOnce' && setting.dataresource) {
-      let datasource = setting.dataresource
-      if (/\s/.test(datasource)) { // 鎷兼帴鍒悕
-        if (!/tb$/.test(datasource)) {
-          datasource = '(' + datasource + ') tb'
-        }
-      } else {
-        datasource = datasource + ' tb'
-      }
-
-      if (setting.customScript) {
-        _sql += `
-        /* 鏁版嵁婧愯嚜瀹氫箟鑴氭湰锛岃娉ㄦ剰鍙橀噺瀹氫箟鏄惁閲嶅 */
-        ${setting.customScript}
-        `
-      }
-
-      verify.uniques.forEach(item => {
-        _sql += `
-        /* 鍚岀被鏁版嵁楠岃瘉 */
-        Set @tbid=''
-
-        Select top 1 @tbid='X' from (select distinct ${item.field},1 as n from ${datasource} inner join (select ID from  dbo.SplitComma(@ID@)) sp on tb.${primaryKey}=sp.ID ) a having sum(n)>1
-        
-        If @tbid!=''
-        Begin
-          Set @ErrorCode='E' Set @retmsg='${item.fieldlabel} 鍊间笉鍞竴'
-          goto aaa
-        end
-        `
-      })
-    }
-
-    let hasvoucher = false
-
-    // 鍑瘉-鏄剧ず鍒椾腑閫夊彇,蹇呴』閫夎
-    if (verify.voucher && verify.voucher.enabled && data) {
-      let _voucher = verify.voucher
-
-      hasvoucher = true
-
-      _sql += `
-        /* 鍒涘缓鍑瘉 */
-        exec s_BVoucher_Create
-          @Bill ='${data[_voucher.linkField.toLowerCase()]}',
-          @BVoucherType ='${_voucher.BVoucherType}',
-          @VoucherTypeOne ='${_voucher.VoucherTypeOne}',
-          @VoucherTypeTwo ='${_voucher.VoucherTypeTwo}',
-          @Type =${_voucher.Type},
-          @UserID=@UserID@,
-          @Username=@Username,
-          @FullName=@FullName,
-          @BVoucher =@BVoucher OUTPUT ,
-          @FIBVoucherDate =@FIBVoucherDate OUTPUT ,
-          @FiYear =@FiYear OUTPUT ,
-          @ErrorCode =@ErrorCode OUTPUT, 
-          @retmsg=@retmsg OUTPUT
-        if @ErrorCode!=''
-          GOTO aaa
-        `
-    }
-
-    let _insertsql = ''
-    if (_actionType === 'insert' || _actionType === 'insertOrUpdate') { // 娣诲姞璇彞
-      let keys = []
-      let values = []
-
-      formdata.forEach(item => {
-        if (item.writein === false) return
-        let _key = item.key.toLowerCase()
-
-        keys.push(_key)
-        values.push('@' + _key)
-      })
-
-      if (!keys.includes(primaryKey.toLowerCase())) {
-        keys.push(primaryKey.toLowerCase())
-        values.push('\'' + primaryId + '\'')
-      }
-      if (!keys.includes('createuserid')) {
-        keys.push('createuserid')
-        values.push('@userid@')
-      }
-      if (!keys.includes('createuser')) {
-        keys.push('createuser')
-        values.push('@username')
-      }
-      if (!keys.includes('createstaff')) {
-        keys.push('createstaff')
-        values.push('@fullname')
-      }
-      if (!keys.includes('bid')) {
-        if (tab && tab.foreignKey && !keys.includes(tab.foreignKey.toLowerCase())) {
-          keys.push(tab.foreignKey.toLowerCase())
-        } else {
-          keys.push('bid')
-        }
-        values.push('@BID@')
-      } else if (tab && tab.foreignKey && !keys.includes(tab.foreignKey.toLowerCase())) {
-        keys.push(tab.foreignKey.toLowerCase())
-        values.push('@BID@')
-      }
-
-      keys = keys.join(',')
-      values = values.join(',')
-      _insertsql = `insert into ${btn.sql} (${keys}) select ${values};`
-    }
-
-    let _updatesql = ''
-    if (_actionType === 'update' || _actionType === 'audit' || _actionType === 'insertOrUpdate') { // 淇敼璇彞
-      let _form = []
-      let _arr = []
-
-      formdata.forEach(item => {
-        if (item.writein === false) return
-        let _key = item.key.toLowerCase()
-        
-        _arr.push(_key)
-        _form.push(_key + '=@' + _key)
-      })
-
-      if (_actionType === 'audit') {
-        if (!_arr.includes('submitdate')) {
-          _form.push('submitdate=getdate()')
-        }
-        if (!_arr.includes('submituserid')) {
-          _form.push('submituserid=@userid@')
-        }
-      } else {
-        if (!_arr.includes('modifydate')) {
-          _form.push('modifydate=getdate()')
-        }
-        if (!_arr.includes('modifyuserid')) {
-          _form.push('modifyuserid=@userid@')
-        }
-      }
-      
-      if (hasvoucher) {
-        if (!_arr.includes('bvoucher')) {
-          _form.push('BVoucher=@BVoucher')
-        }
-        if (!_arr.includes('fibvoucherdate')) {
-          _form.push('FIBVoucherDate=@FIBVoucherDate')
-        }
-        if (!_arr.includes('fiyear')) {
-          _form.push('FiYear=@FiYear')
-        }
-      }
-      _form = _form.join(',')
-
-      let _ID = '=@ID@'
-      if (btn.Ot === 'requiredOnce') {
-        _ID = ' in (select ID from  dbo.SplitComma(@ID@))'
-      }
-
-      _updatesql = `update ${btn.sql} set ${_form} where ${primaryKey}${_ID};`
-    }
-
-    if (_prevCustomScript) {
-      _sql += _prevCustomScript
-    }
-
-    // 娣诲姞銆佷慨鏀广�侀�昏緫鍒犻櫎銆佺墿鐞嗗垹闄�
-    if (_actionType === 'insert') {
-      _sql += `
-        /* 榛樿sql */
-        ${_insertsql}`
-    } else if (_actionType === 'update' || _actionType === 'audit') {
-      _sql += `
-        /* 榛樿sql */
-        ${_updatesql}`
-    } else if (_actionType === 'LogicDelete') { // 閫昏緫鍒犻櫎
-      let _ID = '=@ID@'
-      if (btn.Ot === 'requiredOnce') {
-        _ID = ' in (select ID from  dbo.SplitComma(@ID@))'
-      }
-
-      _sql += `
-        /* 榛樿sql */
-        update ${btn.sql} set deleted=1,modifydate=getdate(),modifyuserid=@userid@ where ${primaryKey}${_ID};`
-    
-    } else if (_actionType === 'delete') {      // 鐗╃悊鍒犻櫎
-      let _msg = ''
-      if (data && columns && columns.length > 0) {
-        let _index = 0
-        columns.forEach(col => {
-          if (col.Hide !== 'true' && col.type !== 'colspan' && col.type !== 'old_colspan' && _index < 4) {
-            _msg += col.label + '=' + data[col.field] + ','
-            _index++
-          }
-        })
-      }
-
-      let _ID = '=@ID@'
-      if (btn.Ot === 'requiredOnce') {
-        _ID = ' in (select ID from  dbo.SplitComma(@ID@))'
-      }
-
-      _sql += `
-        /* 榛樿sql */
-        insert into snote (remark,createuserid,CreateUser,CreateStaff) select left('鍒犻櫎琛�:${btn.sql} 鏁版嵁: ${_msg}${primaryKey}='+@ID@,200),@userid@,@username,@fullname
-        delete ${btn.sql} where ${primaryKey}${_ID};`
-    } else if (_actionType === 'insertOrUpdate') {
-      _sql += `
-        /* 榛樿sql */
-        select @tbid=''
-        select @tbid='X' from ${btn.sql} where ${primaryKey}=@ID@
-        if @tbid=''
-          begin
-          ${_billcodesSql}
-          ${_insertsql}
-          end
-        else
-          begin
-          ${_updatesql}
-          end
+      /* 鏁版嵁婧愯嚜瀹氫箟鑴氭湰锛岃娉ㄦ剰鍙橀噺瀹氫箟鏄惁閲嶅 */
+      ${setting.customScript}
       `
     }
 
-    if (_backCustomScript) {
-      _sql += _backCustomScript
+    verify.uniques.forEach(item => {
+      _sql += `
+      /* 鍚岀被鏁版嵁楠岃瘉 */
+      Set @tbid=''
+
+      Select top 1 @tbid='X' from (select distinct ${item.field},1 as n from ${datasource} inner join (select ID from  dbo.SplitComma(@ID@)) sp on tb.${primaryKey}=sp.ID ) a having sum(n)>1
+      
+      If @tbid!=''
+      Begin
+        Set @ErrorCode='E' Set @retmsg='${item.fieldlabel} 鍊间笉鍞竴'
+        goto aaa
+      end
+      `
+    })
+  }
+
+  let hasvoucher = false
+
+  // 鍑瘉-鏄剧ず鍒椾腑閫夊彇,蹇呴』閫夎
+  if (verify.voucher && verify.voucher.enabled && data) {
+    let _voucher = verify.voucher
+
+    hasvoucher = true
+
+    _sql += `
+      /* 鍒涘缓鍑瘉 */
+      exec s_BVoucher_Create
+        @Bill ='${data[_voucher.linkField.toLowerCase()]}',
+        @BVoucherType ='${_voucher.BVoucherType}',
+        @VoucherTypeOne ='${_voucher.VoucherTypeOne}',
+        @VoucherTypeTwo ='${_voucher.VoucherTypeTwo}',
+        @Type =${_voucher.Type},
+        @UserID=@UserID@,
+        @Username=@Username,
+        @FullName=@FullName,
+        @BVoucher =@BVoucher OUTPUT ,
+        @FIBVoucherDate =@FIBVoucherDate OUTPUT ,
+        @FiYear =@FiYear OUTPUT ,
+        @ErrorCode =@ErrorCode OUTPUT, 
+        @retmsg=@retmsg OUTPUT
+      if @ErrorCode!=''
+        GOTO aaa
+      `
+  }
+
+  let _insertsql = ''
+  if (_actionType === 'insert' || _actionType === 'insertOrUpdate') { // 娣诲姞璇彞
+    let keys = []
+    let values = []
+
+    formdata.forEach(item => {
+      if (item.writein === false) return
+      let _key = item.key.toLowerCase()
+
+      keys.push(_key)
+      values.push('@' + _key)
+    })
+
+    if (!keys.includes(primaryKey.toLowerCase())) {
+      keys.push(primaryKey.toLowerCase())
+      values.push('\'' + primaryId + '\'')
+    }
+    if (!keys.includes('createuserid')) {
+      keys.push('createuserid')
+      values.push('@userid@')
+    }
+    if (!keys.includes('createuser')) {
+      keys.push('createuser')
+      values.push('@username')
+    }
+    if (!keys.includes('createstaff')) {
+      keys.push('createstaff')
+      values.push('@fullname')
+    }
+    if (!keys.includes('bid')) {
+      if (tab && tab.foreignKey && !keys.includes(tab.foreignKey.toLowerCase())) {
+        keys.push(tab.foreignKey.toLowerCase())
+      } else {
+        keys.push('bid')
+      }
+      values.push('@BID@')
+    } else if (tab && tab.foreignKey && !keys.includes(tab.foreignKey.toLowerCase())) {
+      keys.push(tab.foreignKey.toLowerCase())
+      values.push('@BID@')
+    }
+
+    keys = keys.join(',')
+    values = values.join(',')
+    _insertsql = `insert into ${btn.sql} (${keys}) select ${values};`
+  }
+
+  let _updatesql = ''
+  if (_actionType === 'update' || _actionType === 'audit' || _actionType === 'insertOrUpdate') { // 淇敼璇彞
+    let _form = []
+    let _arr = []
+
+    formdata.forEach(item => {
+      if (item.writein === false) return
+      let _key = item.key.toLowerCase()
+      
+      _arr.push(_key)
+      _form.push(_key + '=@' + _key)
+    })
+
+    if (_actionType === 'audit') {
+      if (!_arr.includes('submitdate')) {
+        _form.push('submitdate=getdate()')
+      }
+      if (!_arr.includes('submituserid')) {
+        _form.push('submituserid=@userid@')
+      }
+    } else {
+      if (!_arr.includes('modifydate')) {
+        _form.push('modifydate=getdate()')
+      }
+      if (!_arr.includes('modifyuserid')) {
+        _form.push('modifyuserid=@userid@')
+      }
+    }
+    
+    if (hasvoucher) {
+      if (!_arr.includes('bvoucher')) {
+        _form.push('BVoucher=@BVoucher')
+      }
+      if (!_arr.includes('fibvoucherdate')) {
+        _form.push('FIBVoucherDate=@FIBVoucherDate')
+      }
+      if (!_arr.includes('fiyear')) {
+        _form.push('FiYear=@FiYear')
+      }
+    }
+    _form = _form.join(',')
+
+    let _ID = '=@ID@'
+    if (btn.Ot === 'requiredOnce') {
+      _ID = ' in (select ID from  dbo.SplitComma(@ID@))'
+    }
+
+    _updatesql = `update ${btn.sql} set ${_form} where ${primaryKey}${_ID};`
+  }
+
+  if (_prevCustomScript) {
+    _sql += _prevCustomScript
+  }
+
+  // 娣诲姞銆佷慨鏀广�侀�昏緫鍒犻櫎銆佺墿鐞嗗垹闄�
+  if (_actionType === 'insert') {
+    _sql += `
+      /* 榛樿sql */
+      ${_insertsql}`
+  } else if (_actionType === 'update' || _actionType === 'audit') {
+    _sql += `
+      /* 榛樿sql */
+      ${_updatesql}`
+  } else if (_actionType === 'LogicDelete') { // 閫昏緫鍒犻櫎
+    let _ID = '=@ID@'
+    if (btn.Ot === 'requiredOnce') {
+      _ID = ' in (select ID from  dbo.SplitComma(@ID@))'
     }
 
     _sql += `
-        aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
-
-    if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
-      _sql = _sql.replace(/\n\s{8}/ig, '\n')
-      console.info(_sql)
+      /* 榛樿sql */
+      update ${btn.sql} set deleted=1,modifydate=getdate(),modifyuserid=@userid@ where ${primaryKey}${_ID};`
+  
+  } else if (_actionType === 'delete') {      // 鐗╃悊鍒犻櫎
+    let _msg = ''
+    if (data && columns && columns.length > 0) {
+      let _index = 0
+      columns.forEach(col => {
+        if (col.Hide !== 'true' && col.type !== 'colspan' && col.type !== 'old_colspan' && _index < 4) {
+          _msg += col.label + '=' + data[col.field] + ','
+          _index++
+        }
+      })
     }
 
+    let _ID = '=@ID@'
+    if (btn.Ot === 'requiredOnce') {
+      _ID = ' in (select ID from  dbo.SplitComma(@ID@))'
+    }
+
+    _sql += `
+      /* 榛樿sql */
+      insert into snote (remark,createuserid,CreateUser,CreateStaff) select left('鍒犻櫎琛�:${btn.sql} 鏁版嵁: ${_msg}${primaryKey}='+@ID@,200),@userid@,@username,@fullname
+      delete ${btn.sql} where ${primaryKey}${_ID};`
+  } else if (_actionType === 'insertOrUpdate') {
+    _sql += `
+      /* 榛樿sql */
+      select @tbid=''
+      select @tbid='X' from ${btn.sql} where ${primaryKey}=@ID@
+      if @tbid=''
+        begin
+        ${_billcodesSql}
+        ${_insertsql}
+        end
+      else
+        begin
+        ${_updatesql}
+        end
+    `
+  }
+
+  if (_backCustomScript) {
+    _sql += _backCustomScript
+  }
+
+  if (retmsg) {
+    _sql += `
+      aaa: if @ErrorCode!=''
+      insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@`
+  } else {
+    _sql += `
+      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
+  }
+
+  if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+    // _sql = _sql.replace(/\n\s{8}/ig, '\n')
+    console.info(_sql)
+  }
+
+  if (retmsg) {
+    return {
+      sql: _sql,
+      callbacksql: _callbacksql
+    }
+  } else {
     return _sql
   }
 }
\ No newline at end of file
diff --git a/src/views/billprint/index.jsx b/src/views/billprint/index.jsx
index 56f813f..97a31d7 100644
--- a/src/views/billprint/index.jsx
+++ b/src/views/billprint/index.jsx
@@ -21,6 +21,8 @@
 const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
 const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table'))
+const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
+const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
 
 class BillPrint extends Component {
   state = {
@@ -119,26 +121,26 @@
           A4: {
             vertical: 980,
             horizontal: 1200,
-            verticaldefault: 1.45789,
-            verticalwithout: 1.41428,
-            horizontaldefault: 0.683,
-            horizontalwithout: 0.705,
+            verticaldefault: 1.455,
+            verticalwithout: 1.411,
+            horizontaldefault: 0.679,
+            horizontalwithout: 0.701,
           },
           A3: {
             vertical: 1200,
             horizontal: 1600,
-            verticaldefault: 1.44404,
-            verticalwithout: 1.41414,
-            horizontaldefault: 0.6923,
-            horizontalwithout: 0.707,
+            verticaldefault: 1.441,
+            verticalwithout: 1.410,
+            horizontaldefault: 0.688,
+            horizontalwithout: 0.703,
           },
           A5: {
             vertical: 700,
             horizontal: 1000,
-            verticaldefault: 1.482,
-            verticalwithout: 1.417,
-            horizontaldefault: 0.6736,
-            horizontalwithout: 0.7047,
+            verticaldefault: 1.478,
+            verticalwithout: 1.413,
+            horizontaldefault: 0.669,
+            horizontalwithout: 0.700,
           }
         }
 
@@ -153,9 +155,13 @@
           if (component.action) component.action = []
           if (component.search) component.search = []
           component.data = [] // 鍒濆鍖栨暟鎹负绌�
+
+          if (['propcard', 'brafteditor', 'sandbox'].includes(component.subtype) && component.wrap.datatype === 'static') {
+            component.format = ''
+          }
     
           if (!component.setting) return component // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
-          if (!component.format || (component.subtype === 'propcard' && component.wrap.datatype === 'static')) return component // 娌℃湁鍔ㄦ�佹暟鎹�  鏁版嵁鏍煎紡 array 鎴� object
+          if (!component.format) return component  // 娌℃湁鍔ㄦ�佹暟鎹�  鏁版嵁鏍煎紡 array 鎴� object
           if (component.setting.interType !== 'system') { // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
             component.setting.sync = 'false'
             return component
@@ -350,7 +356,7 @@
       let _results = results.filter(Boolean)
 
       let comps = components.map(item => {
-        if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return item
+        if (!item.format) return item
 
         _results.forEach(res => {
           if (res.componentId === item.uuid && res.data) {
@@ -566,6 +572,18 @@
             <NormalTable config={item} initdata={item.data} mainSearch={[]} menuType="" />
           </Col>
         )
+      } else if (item.type === 'editor') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <BraftEditor config={item} initdata={item.data} mainSearch={[]} menuType="" />
+          </Col>
+        )
+      } else if (item.type === 'code') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <SandBox config={item} initdata={item.data} mainSearch={[]} menuType="" />
+          </Col>
+        )
       } else {
         return null
       }
diff --git a/src/views/billprint/index.scss b/src/views/billprint/index.scss
index 3eecf5a..2c5512e 100644
--- a/src/views/billprint/index.scss
+++ b/src/views/billprint/index.scss
@@ -31,6 +31,19 @@
   }
 }
 
+.print-page {
+  table {
+    border-radius: 0!important;
+    .ant-table-column-sorter {
+      display: none!important;
+    }
+  }
+  .ant-table-thead > tr > th {
+    background: transparent!important;
+    border-radius: 0!important;
+  }
+}
+
 .normal-custom-table .main-pickup {
   display: none!important;
 }
diff --git a/src/views/login/index.jsx b/src/views/login/index.jsx
index edeb504..c0cef5d 100644
--- a/src/views/login/index.jsx
+++ b/src/views/login/index.jsx
@@ -43,6 +43,21 @@
     syncing: false
   }
 
+  UNSAFE_componentWillMount() {
+    localStorage.removeItem('UserID')
+    localStorage.removeItem('LoginUID')
+    localStorage.removeItem('User_Name')
+    localStorage.removeItem('Full_Name')
+    localStorage.removeItem('avatar')
+    localStorage.removeItem('dataM')
+    localStorage.removeItem('localDataM')
+    localStorage.removeItem('debug')
+    localStorage.removeItem('role_id')
+    localStorage.removeItem('localRole_id')
+
+    sessionStorage.clear()
+  }
+
   changelang (item) {
     // 鍒囨崲璇█
     this.setState({
@@ -288,10 +303,10 @@
 
     Api.getTouristMsg().then(result => {
       if (result.status) {
-        if (result.UserID) {
+        if (result.UserID && !sessionStorage.getItem('UserID')) {
           sessionStorage.setItem('UserID', result.UserID)
         }
-        if (result.LoginUID) {
+        if (result.LoginUID && !sessionStorage.getItem('LoginUID')) {
           sessionStorage.setItem('LoginUID', result.LoginUID)
         }
 
diff --git a/src/views/main/index.jsx b/src/views/main/index.jsx
index 482423a..718f936 100644
--- a/src/views/main/index.jsx
+++ b/src/views/main/index.jsx
@@ -2,9 +2,11 @@
 import { ConfigProvider } from 'antd'
 import enUS from 'antd/es/locale/en_US'
 import zhCN from 'antd/es/locale/zh_CN'
+
 import Header from '@/components/header'
 import Sidemenu from '@/components/sidemenu'
 import Tabview from '@/components/tabview'
+import QueryLog from '@/components/querylog'
 
 import './index.scss'
 
@@ -13,11 +15,12 @@
 class Main extends Component {
   render () {
     return (
-      <div className="flex-container main-view">
+      <div className="mk-main-view">
         <ConfigProvider locale={_locale}>
           <Header key="header"/>
           <Sidemenu key="sidemenu"/>
           <Tabview key="tabview"/>
+          <QueryLog />
         </ConfigProvider>
       </div>
     )
diff --git a/src/views/main/index.scss b/src/views/main/index.scss
index 0493020..c14dc8e 100644
--- a/src/views/main/index.scss
+++ b/src/views/main/index.scss
@@ -1,4 +1,4 @@
-.flex-container {
+.mk-main-view {
   display: flex;
   flex: auto;
   min-height: 100%;
diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx
index b09c3fb..c79828e 100644
--- a/src/views/menudesign/index.jsx
+++ b/src/views/menudesign/index.jsx
@@ -8,6 +8,7 @@
 import html2canvas from 'html2canvas'
 
 import Api from '@/api'
+import options from '@/store/options.js'
 import Utils from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/mob.js'
 import enUS from '@/locales/en-US/mob.js'
@@ -26,18 +27,20 @@
 
 const MenuForm = asyncComponent(() => import('./menuform'))
 const HomeForm = asyncComponent(() => import('./homeform'))
-const PrintMenuForm = asyncComponent(() => import('./printmenuform'))
 const Header = asyncComponent(() => import('@/menu/header'))
-const SourceWrap = asyncComponent(() => import('@/menu/modelsource'))
 const MenuShell = asyncComponent(() => import('@/menu/menushell'))
+const PrintMenuForm = asyncComponent(() => import('./printmenuform'))
+const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
+const PopviewController = asyncComponent(() => import('@/menu/popview'))
 const BgController = asyncComponent(() => import('@/menu/bgcontroller'))
 const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
 const PaddingController = asyncComponent(() => import('@/menu/padcontroller'))
 const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
+const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
+const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
+const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
 const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
 const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
-const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
-const PopviewController = asyncComponent(() => import('@/menu/popview'))
 const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
 
 sessionStorage.setItem('isEditState', 'true')
@@ -100,7 +103,10 @@
     MKEmitter.addListener('changePopview', this.initPopview)
     MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
-    this.updateCustomComponent()
+    setTimeout(() => {
+      this.updateCustomComponent()
+      this.getAppPictures()
+    }, 1000)
   }
 
   /**
@@ -116,6 +122,32 @@
     MKEmitter.removeListener('changePopview', this.initPopview)
     MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
+  }
+
+  getAppPictures = () => {
+    Api.getSystemConfig({
+      func: 's_url_db_adduptdel',
+      PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+      PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+      typecharone: 'image',
+      type: 'search'
+    }).then(res => {
+      if (res.status) {
+        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
+      }
+
+      Api.getSystemConfig({
+        func: 's_url_db_adduptdel',
+        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+        typecharone: 'video',
+        type: 'search'
+      }).then(res => {
+        if (res.status) {
+          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
+        }
+      })
+    })
   }
 
   updateCustomComponent = () => {
@@ -151,6 +183,7 @@
         })
       }
       this.setState({customComponents: coms})
+      this.getRoleFields()
     })
   }
 
@@ -315,7 +348,6 @@
         })
 
         this.props.modifyCustomMenu(config)
-        this.getRoleFields()
       } else {
         notification.warning({
           top: 92,
@@ -341,37 +373,40 @@
           traversal(item.components)
         } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
           item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
             buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
             _sort++
           })
           item.subcards.forEach(card => {
             card.elements && card.elements.forEach(cell => {
               if (cell.eleType !== 'button') return
-
+              this.checkBtn(cell)
               buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
               _sort++
             })
             card.backElements && card.backElements.forEach(cell => {
               if (cell.eleType !== 'button') return
-
+              this.checkBtn(cell)
               buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
               _sort++
             })
           })
         } else if (item.type === 'line' || item.type === 'bar') {
           item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
             buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
             _sort++
           })
         } else if (item.type === 'table' && item.subtype === 'normaltable') {
           item.action && item.action.forEach(btn => {
-            if (btn.origin) return
+            this.checkBtn(btn)
             buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
             _sort++
           })
           item.cols && item.cols.forEach(col => {
             if (col.type !== 'action') return
             col.elements.forEach(btn => {
+              this.checkBtn(btn)
               buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
               _sort++
             })
@@ -383,6 +418,26 @@
     traversal(config.components)
 
     return buttons
+  }
+
+  checkBtn = (btn) => {
+    if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+      let hascheck = false
+      btn.verify.scripts.forEach(item => {
+        if (item.status === 'false') return
+  
+        if (/\$check@|@check\$/ig.test(item.sql)) {
+          hascheck = true
+        }
+      })
+      if (hascheck) {
+        notification.warning({
+          top: 92,
+          message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+          duration: 5
+        })
+      }
+    }
   }
 
   filterConfig = (components) => {
@@ -429,72 +484,83 @@
       return
     }
 
-    config.components = this.filterConfig(config.components)
-
-    if (config.enabled && this.verifyConfig()) {
-      config.enabled = false
-    }
-
-    let _config = fromJS(config).toJS()
-    delete _config.tableFields
-
-    let param = {
-      func: 'sPC_TrdMenu_AddUpt',
-      FstID: _config.fstMenuId || '',
-      SndID: _config.parentId,
-      ParentID: _config.parentId,
-      MenuID: _config.uuid,
-      MenuNo: _config.MenuNo || '',
-      EasyCode: _config.easyCode || '',
-      Template: 'CustomPage',
-      MenuName: _config.MenuName || '',
-      PageParam: JSON.stringify({Template: 'CustomPage', OpenType: 'newtab'}),
-      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config))),
-      LText: '',
-      LTexttb: ''
-    }
-
-    param.LText = Utils.formatOptions(param.LText)
-    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
-
-    if (openEdition) { // 鐗堟湰绠$悊
-      param.open_edition = openEdition
-    }
-
-    let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
-      func: 'sPC_Button_AddUpt',
-      Type: 40,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
-      ParentID: _config.uuid,
-      MenuNo: _config.MenuNo,
-      Template: 'CustomPage',
-      PageParam: '',
-      LongParam: '',
-      LText: []
-    }
-
-    let btnIds = '' // 鐢ㄤ簬澶嶅埗鎸夐挳鐨勮繃婊�
-    if (MenuType !== 'billPrint') {
-      btnParam.LText = this.getMenuMessage()
-      btnParam.LText = btnParam.LText.join(' union all ')
-
-      btnIds = btnParam.LText
-
-      btnParam.LText = Utils.formatOptions(btnParam.LText)
-      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
-    } else {
-      btnParam.LText = ''
-    }
-
     this.setState({
       menuloading: true
-    }, () => {
+    })
+
+    setTimeout(() => {
+      config.components = this.filterConfig(config.components)
+
+      if (config.enabled && this.verifyConfig()) {
+        config.enabled = false
+      }
+
+      let _config = fromJS(config).toJS()
+      delete _config.tableFields
+
+      let param = {
+        func: 'sPC_TrdMenu_AddUpt',
+        FstID: _config.fstMenuId || '',
+        SndID: _config.parentId,
+        ParentID: _config.parentId,
+        MenuID: _config.uuid,
+        MenuNo: _config.MenuNo || '',
+        EasyCode: _config.easyCode || '',
+        Template: 'CustomPage',
+        MenuName: _config.MenuName || '',
+        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: 'newtab'}),
+        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config))),
+        LText: '',
+        LTexttb: ''
+      }
+
+      param.LText = Utils.formatOptions(param.LText)
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+      if (openEdition) { // 鐗堟湰绠$悊
+        param.open_edition = openEdition
+      }
+
+      let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
+        func: 'sPC_Button_AddUpt',
+        Type: 40,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
+        ParentID: _config.uuid,
+        MenuNo: _config.MenuNo,
+        Template: 'CustomPage',
+        PageParam: '',
+        LongParam: '',
+        LText: []
+      }
+
+      let btnIds = '' // 鐢ㄤ簬澶嶅埗鎸夐挳鐨勮繃婊�
+      if (MenuType !== 'billPrint') {
+        btnParam.LText = this.getMenuMessage()
+        btnParam.LText = btnParam.LText.join(' union all ')
+
+        btnIds = btnParam.LText
+
+        btnParam.LText = Utils.formatOptions(btnParam.LText)
+        btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
+      } else {
+        btnParam.LText = ''
+      }
+
       new Promise(resolve => {
         if (MenuType === 'billPrint') { // 鎵撳嵃鐢熸垚椤甸潰鏁堟灉鍥�
           html2canvas(document.getElementById('menu-shell-inner')).then(canvas => {
-            let img = canvas.toDataURL('image/png') // 鑾峰彇鐢熸垚鐨勫浘鐗�
-            Api.fileuploadbase64(img, 'cloud').then(result => {
+            let param = {
+              Base64Img: canvas.toDataURL('image/png') // 鑾峰彇鐢熸垚鐨勫浘鐗�
+            }
+  
+            if (options.cloudServiceApi) {
+              param.rduri = options.cloudServiceApi
+              param.userid = sessionStorage.getItem('CloudUserID') || ''
+              param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
+            }
+  
+            Api.fileuploadbase64(param).then(result => {
               if (result.status) {
                 Api.getSystemConfig({
                   func: 's_PrintTemplateMSub',
@@ -717,7 +783,7 @@
           })
         }
       })
-    })
+    }, 300)
   }
 
   getRoleFields = () => {
@@ -754,7 +820,7 @@
   onEnabledChange = () => {
     const { config } = this.state
 
-    if (!config.enabled && this.verifyConfig(true)) {
+    if (!config || (!config.enabled && this.verifyConfig(true))) {
       return
     }
 
@@ -769,7 +835,7 @@
 
     config.components.forEach(item => {
       if (error) return
-      if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return
+      if (['propcard', 'brafteditor', 'sandbox'].includes(item.subtype) && item.wrap.datatype === 'static') return
 
       if (item.setting) {
         if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
@@ -837,7 +903,7 @@
 
     return (
       <ConfigProvider locale={_locale}>
-        <div className="pc-menu-view" id="view">
+        <div className={'pc-menu-view ' + (MenuType || '')} id="mk-menu-design-view">
           <Header />
           {!popBtn && !visible ? <DndProvider backend={HTML5Backend}>
             <div className="menu-body">
@@ -875,10 +941,10 @@
                   {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
                     <SourceWrap components={customComponents} MenuType={MenuType} />
                   </Panel> : null}
-                  <Panel header={'鑳屾櫙'} key="background">
+                  <Panel header={'椤甸潰鑳屾櫙'} key="background">
                     {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
                   </Panel>
-                  <Panel header={'鍐呰竟璺�'} key="padding">
+                  <Panel header={'椤甸潰鍐呰竟璺�'} key="padding">
                     {config ? <PaddingController config={config} updateConfig={this.updateConfig} /> : null}
                   </Panel>
                 </Collapse>
@@ -888,9 +954,11 @@
                   <div> {config && config.MenuName} </div>
                 } bordered={false} extra={
                   <div>
+                    <SysInterface config={config} updateConfig={this.updateConfig}/>
+                    <PictureController/>
                     <StyleCombControlButton menu={config} />
                     <PasteController type="menu" Tab={null} insert={this.insert} />
-                    {config ? <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config.enabled} onChange={this.onEnabledChange} /> : null}
+                    <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
                     <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
                     <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
                   </div>
diff --git a/src/views/menudesign/index.scss b/src/views/menudesign/index.scss
index 21de614..65fadca 100644
--- a/src/views/menudesign/index.scss
+++ b/src/views/menudesign/index.scss
@@ -160,3 +160,15 @@
     }
   }
 }
+.pc-menu-view.billPrint {
+  table {
+    border-radius: 0!important;
+    .ant-table-column-sorter {
+      display: none!important;
+    }
+  }
+  .ant-table-thead > tr > th {
+    background: transparent!important;
+    border-radius: 0!important;
+  }
+}
\ No newline at end of file
diff --git a/src/views/menudesign/printmenuform/index.jsx b/src/views/menudesign/printmenuform/index.jsx
index 04dab01..a6de29a 100644
--- a/src/views/menudesign/printmenuform/index.jsx
+++ b/src/views/menudesign/printmenuform/index.jsx
@@ -36,8 +36,12 @@
     this.props.updateConfig({...this.props.config, pageSize: val})
   }
 
-  onRadioChange = (val) => {
+  onLayoutChange = (val) => {
     this.props.updateConfig({...this.props.config, pageLayout: val})
+  }
+  
+  onPaddingChange = (val) => {
+    this.props.updateConfig({...this.props.config, pagePadding: val})
   }
 
   render() {
@@ -87,7 +91,7 @@
                   }
                 ]
               })(
-                <Radio.Group onChange={(e) => {this.onRadioChange(e.target.value)}}>
+                <Radio.Group onChange={(e) => {this.onLayoutChange(e.target.value)}}>
                   <Radio value="vertical">绾靛悜</Radio>
                   <Radio value="horizontal">妯悜</Radio>
                 </Radio.Group>
@@ -105,7 +109,7 @@
                   }
                 ]
               })(
-                <Radio.Group onChange={(e) => {this.onRadioChange(e.target.value)}}>
+                <Radio.Group onChange={(e) => {this.onPaddingChange(e.target.value)}}>
                   <Radio value="default">榛樿</Radio>
                   <Radio value="without">鏃�</Radio>
                 </Radio.Group>
diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx
index 92dbcf0..b090a70 100644
--- a/src/views/mobdesign/index.jsx
+++ b/src/views/mobdesign/index.jsx
@@ -192,13 +192,6 @@
     })
   }
 
-  // save = () => {
-  //   html2canvas(document.getElementById('view')).then(canvas => {
-  //     let imgUri = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream'); // 鑾峰彇鐢熸垚鐨勫浘鐗囩殑url
-  //     window.location.href = imgUri; // 涓嬭浇鍥剧墖
-  //   })
-  // }
-
   editCard = (element) => {
     this.setState({
       editElem: element
diff --git a/src/views/printTemplate/index.jsx b/src/views/printTemplate/index.jsx
index 887525b..e6e46a6 100644
--- a/src/views/printTemplate/index.jsx
+++ b/src/views/printTemplate/index.jsx
@@ -19,6 +19,7 @@
   qrurl,
   imgurl
 } from './option.js'
+import options from '@/store/options.js'
 import Utils from '@/utils/utils.js'
 import printCtrl from './print.js'
 import Api from '@/api'
@@ -775,7 +776,17 @@
 
     new Promise(resolve => {
       printCtrl.sketch(config, null).then(res => {
-        Api.fileuploadbase64(res, 'cloud').then(result => { // 鍥剧墖涓婁紶锛屽苟鑾峰彇鍥剧墖璺緞
+        let param = {
+          Base64Img: res
+        }
+
+        if (options.cloudServiceApi) {
+          param.rduri = options.cloudServiceApi
+          param.userid = sessionStorage.getItem('CloudUserID') || ''
+          param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
+        }
+        
+        Api.fileuploadbase64(param).then(result => { // 鍥剧墖涓婁紶锛屽苟鑾峰彇鍥剧墖璺緞
           if (result.status) {
             resolve(Utils.getcloudurl(result.Images))
           } else {

--
Gitblit v1.8.0