From 5ca389a3b7bfc54067547e2b8663ce413f602f3f Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期日, 31 一月 2021 20:41:30 +0800
Subject: [PATCH] 2021-01-31

---
 src/menu/sysinterface/index.scss                                    |   20 
 src/menu/sysinterface/settingform/simplescript/index.jsx            |  450 ++++++++++++++++++
 src/templates/zshare/editTable/index.jsx                            |   18 
 src/menu/sysinterface/settingform/baseform/index.jsx                |  243 +++++++++
 src/menu/sysinterface/settingform/index.jsx                         |  179 +++++++
 src/menu/sysinterface/settingform/simplescript/index.scss           |   45 +
 src/menu/sysinterface/settingform/index.scss                        |   65 ++
 src/menu/sysinterface/settingform/baseform/index.scss               |   22 
 src/tabviews/custom/index.jsx                                       |  175 +++++++
 src/menu/sysinterface/settingform/utils.jsx                         |   45 +
 src/menu/sysinterface/index.jsx                                     |  215 ++++++++
 src/templates/sharecomponent/settingcomponent/settingform/utils.jsx |    6 
 src/views/menudesign/index.jsx                                      |    2 
 13 files changed, 1,478 insertions(+), 7 deletions(-)

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..e2682cd
--- /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/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index c772dd4..98e030c 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'
@@ -180,6 +181,8 @@
         if (!this.props.Tab) {
           this.setShortcut()
         }
+
+        this.loadData()
       })
     } else {
       this.setState({
@@ -229,6 +232,178 @@
     }
   }
 
+  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 = (params) => {
+    let setting = params.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 (params.length > 0) {
+            this.loadOutResource(params)
+          }
+        } else {
+          this.customOuterRequest(res, setting, params)
+        }
+      } else {
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    })
+  }
+
+  customOuterRequest = (result, setting, params) => {
+    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, params)
+      } else {
+        res.mk_api_key = mkey
+        this.customCallbackRequest(res, setting, params)
+      }
+    }, (e) => {
+      let _result = {
+        mk_api_key: mkey,
+        $ErrCode: 'E',
+        $ErrMesg: e && e.statusText ? e.statusText : ''
+      }
+
+      this.customCallbackRequest(_result, setting, params)
+    })
+  }
+
+  customCallbackRequest = (result, setting, params) => {
+    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 (params.length > 0) {
+          this.loadOutResource(params)
+        }
+      } else {
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    })
+  }
+
   filterComponent = (components, roleId, permAction, permMenus) => {
     return components.filter(item => {
       if (item.type === 'tabs') {
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
index a32d2b9..09f0df4 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
@@ -117,12 +117,8 @@
       `
     })
 
-    if (_customScript && regoptions) {
+    if (_customScript) {
       _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
-        ${_customScript}
-      `
-    } else if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
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/views/menudesign/index.jsx b/src/views/menudesign/index.jsx
index 5d09c45..c79828e 100644
--- a/src/views/menudesign/index.jsx
+++ b/src/views/menudesign/index.jsx
@@ -36,6 +36,7 @@
 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'))
@@ -953,6 +954,7 @@
                   <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} />

--
Gitblit v1.8.0