From 41239717c4446af79268b968557274f88a0afaeb Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期六, 15 八月 2020 10:16:07 +0800
Subject: [PATCH] 2020-08-15

---
 src/tabviews/treepage/index.jsx                                                     |    2 
 src/templates/zshare/modalform/index.jsx                                            |    2 
 src/templates/sharecomponent/datasourcecomponent/verifycard/index.jsx               |  524 +++++++
 src/templates/calendarconfig/calcomponent/index.scss                                |   40 
 src/templates/sharecomponent/datasourcecomponent/index.jsx                          |   88 +
 src/templates/sharecomponent/actioncomponent/verifyexcelin/columnform/index.jsx     |    6 
 src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.scss  |   22 
 src/templates/sharecomponent/datasourcecomponent/verifycard/index.scss              |   74 +
 src/templates/calendarconfig/calcomponent/calendarform/index.scss                   |    7 
 src/templates/comtableconfig/index.jsx                                              |   25 
 src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.jsx    |  145 ++
 src/templates/sharecomponent/datasourcecomponent/verifycard/utils.jsx               |   99 +
 src/templates/calendarconfig/tabcomponent/index.scss                                |   85 +
 public/options.json                                                                 |    2 
 src/templates/sharecomponent/datasourcecomponent/index.scss                         |   94 +
 src/templates/menuconfig/editthdmenu/index.jsx                                      |   14 
 src/templates/calendarconfig/tabcomponent/tabform/index.jsx                         |  278 +++
 src/tabviews/subtable/index.jsx                                                     |    2 
 src/tabviews/subtabtable/index.jsx                                                  |    2 
 src/tabviews/commontable/index.jsx                                                  |    2 
 src/templates/calendarconfig/source.jsx                                             |  139 +
 src/templates/calendarconfig/tabcomponent/tabform/index.scss                        |   16 
 src/templates/modalconfig/dragelement/card.jsx                                      |   59 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx                |    2 
 src/templates/sharecomponent/tabscomponent/index.jsx                                |    2 
 src/utils/utils.js                                                                  |    6 
 src/templates/calendarconfig/calcomponent/index.jsx                                 |   98 +
 src/index.js                                                                        |   31 
 src/templates/calendarconfig/calcomponent/calendarform/index.jsx                    |  139 +
 src/templates/calendarconfig/index.scss                                             |  215 +++
 src/tabviews/zshare/calendar/index.jsx                                              |  171 +
 src/templates/zshare/formconfig.jsx                                                 |   11 
 src/templates/calendarconfig/index.jsx                                              |  890 ++++++++++++
 src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.jsx  |  231 +++
 src/tabviews/zshare/mutilform/index.jsx                                             |   24 
 src/views/login/index.scss                                                          |   15 
 src/templates/sharecomponent/actioncomponent/dragaction/index.jsx                   |    1 
 src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.scss   |    0 
 src/templates/calendarconfig/tabcomponent/index.jsx                                 |  122 +
 src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.jsx   |  278 +++
 src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.scss |   34 
 src/utils/option.js                                                                 |   86 
 src/tabviews/zshare/calendar/index.scss                                             |   21 
 src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/utils.jsx   |   84 +
 44 files changed, 3,999 insertions(+), 189 deletions(-)

diff --git a/public/options.json b/public/options.json
index a918828..f8821ac 100644
--- a/public/options.json
+++ b/public/options.json
@@ -1,7 +1,7 @@
 {
   "appId": "201912040924165801464FF1788654BC5AC73",
   "appkey": "20191106103859640976D6E924E464D029CF0",
-  "mainSystemApi": "http://cloud.mk9h.cn/webapi/dostars",
+  "mainSystemApi": "http://sso.mk9h.cn/cloud/webapi/dostars",
   "systemType": "",
   "lineColor": "",
   "filter": "false"
diff --git a/src/index.js b/src/index.js
index 88e31d7..d9926a8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -86,21 +86,28 @@
       if (config.mainSystemApi) {
         let systemApi = config.mainSystemApi
       
-        if (/^(http|https):\/\//ig.test(systemApi)) {
-          let _systemApi = /^(http|https):\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}|(:[0-9]{1,4}))+\.?/ig.exec(systemApi)
+        // if (/^(http|https):\/\//ig.test(systemApi)) {
+        //   let _systemApi = /^(http|https):\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}|(:[0-9]{1,4}))+\.?/ig.exec(systemApi)
       
-          systemApi = _systemApi ? _systemApi[0] : ''
-        } else {
+        //   systemApi = _systemApi ? _systemApi[0] : ''
+        // } else {
+        //   systemApi = ''
+        // }
+      
+        // // 涓氬姟绯荤粺杩炴帴浜戠鏃讹紝鏍煎紡鍖栧鐞�
+        // if (systemApi && systemApi === /^(http|https):\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}|(:[0-9]{1,4}))+\.?/ig.exec(options.cloudServiceApi)[0]) {
+        //   window.GLOB.dataFormat = true
+        // }
+      
+        // if (systemApi) {
+        //   systemApi = systemApi + '/webapi/dostars'
+        // }
+
+        // 涓氬姟绯荤粺涓嶅厑璁歌繛鎺ヤ簯绔紝涓氬姟绯荤粺杩炴帴sso.mk9h.cn鏃讹紝鏁版嵁铏氬寲澶勭悊
+        if (systemApi && systemApi.indexOf('cloud.mk9h.cn') > -1) {
           systemApi = ''
-        }
-      
-        // 涓氬姟绯荤粺杩炴帴浜戠鏃讹紝鏍煎紡鍖栧鐞�
-        if (systemApi && systemApi === /^(http|https):\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}|(:[0-9]{1,4}))+\.?/ig.exec(options.cloudServiceApi)[0]) {
+        } else if (systemApi && systemApi.indexOf('sso.mk9h.cn') > -1) {
           window.GLOB.dataFormat = true
-        }
-      
-        if (systemApi) {
-          systemApi = systemApi + '/webapi/dostars'
         }
       
         window.GLOB.mainSystemApi = systemApi
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index c5c91f9..2813a4b 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -735,8 +735,8 @@
 
     // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
     if (options.sysType === 'local' && !window.GLOB.systemType) {
+      param.custom_script &&  console.log(`${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${param.custom_script}`)
       LText &&  console.log(LText)
-      param.custom_script &&  console.log(param.custom_script)
     }
     
     param.custom_script = Utils.formatOptions(param.custom_script)
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index 210ef59..ca99320 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -612,8 +612,8 @@
 
     // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
     if (options.sysType === 'local' && !window.GLOB.systemType) {
+      param.custom_script &&  console.log(`${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${param.custom_script}`)
       LText &&  console.log(LText)
-      param.custom_script &&  console.log(param.custom_script)
     }
 
     param.custom_script = Utils.formatOptions(param.custom_script)
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
index 2a052d4..7575104 100644
--- a/src/tabviews/subtabtable/index.jsx
+++ b/src/tabviews/subtabtable/index.jsx
@@ -493,8 +493,8 @@
     
     // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
     if (options.sysType === 'local' && !window.GLOB.systemType) {
+      param.custom_script &&  console.log(`${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${param.custom_script}`)
       LText &&  console.log(LText)
-      param.custom_script &&  console.log(param.custom_script)
     }
     
     param.custom_script = Utils.formatOptions(param.custom_script)
diff --git a/src/tabviews/treepage/index.jsx b/src/tabviews/treepage/index.jsx
index 2c154ab..952f1cd 100644
--- a/src/tabviews/treepage/index.jsx
+++ b/src/tabviews/treepage/index.jsx
@@ -533,8 +533,8 @@
     
     // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
     if (options.sysType === 'local' && !window.GLOB.systemType) {
+      param.custom_script &&  console.log(`${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${param.custom_script}`)
       LText &&  console.log(LText)
-      param.custom_script &&  console.log(param.custom_script)
     }
     
     param.custom_script = Utils.formatOptions(param.custom_script)
diff --git a/src/tabviews/zshare/calendar/index.jsx b/src/tabviews/zshare/calendar/index.jsx
index abb13ee..6616507 100644
--- a/src/tabviews/zshare/calendar/index.jsx
+++ b/src/tabviews/zshare/calendar/index.jsx
@@ -12,7 +12,8 @@
 class Calendar extends Component {
   static propTpyes = {
     data: PropTypes.any,            // 浜嬩欢鏁版嵁
-    levels: PropTypes.any
+    calendar: PropTypes.any,
+    loading: false
   }
 
   state = {
@@ -22,11 +23,11 @@
     selectYear: moment().format('YYYY'),
     selectMonth: moment().format('MM'),
     datelist: null,
-    monthlist: null,
+    monthlist: null
   }
 
   UNSAFE_componentWillMount() {
-    const { levels } = this.props
+    const { calendar, data } = this.props
 
     let yearlist = []
     let _selectYear = +this.state.selectYear
@@ -38,17 +39,14 @@
     }
 
     let datelist = this.getDateList(this.state.selectYear)
-    let _levels = null
 
-    if (!levels) {
-      _levels = ['day', 'month', 'year']
-    } else {
-      _levels = levels
+    if (data && data.length > 0) {
+      datelist = this.mountdata(datelist, data)
     }
-
+    
+    let _levels = calendar.levels
     let level = _levels[0]
     let monthlist = null
-    level = 'month'
 
     if (_levels.includes('month')) {
       monthlist = datelist.filter(item => item.month === moment().format('MM'))[0]
@@ -65,12 +63,112 @@
 
   UNSAFE_componentWillReceiveProps(nextProps) {
     if (!is(fromJS(this.props.data), fromJS(nextProps.data))) {
-      
+      let datelist = this.mountdata(this.state.datelist, nextProps.data)
+      let monthlist = null
+
+      if (this.state.levels.includes('month')) {
+        monthlist = datelist.filter(item => item.month === this.state.selectMonth)[0]
+      }
+
+      this.setState({
+        datelist: datelist,
+        monthlist
+      })
+    } else if (!is(fromJS(this.props.calendar), fromJS(nextProps.calendar))) {
+      this.setState({
+        levels: nextProps.calendar.levels
+      })
     }
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState))
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  mountdata = (datelist, data) => {
+    const { calendar } = this.props
+
+    let datalist = []
+    let levels = {
+      black: 1,
+      red: 2,
+      orange: 3,
+      yellow: 4,
+      green: 5,
+      lightgreen: 6,
+      cyan: 7,
+      blue: 8,
+      purple: 9,
+      white: 10
+    }
+
+    data.forEach(item => {
+      let startTime = item[calendar.startfield]
+      let endTime = item[calendar.endfield]
+      let color = item[calendar.colorfield]
+
+      if (!startTime || !/^(1|2)\d{3}(-|\/)\d{2}(-|\/)\d{2}/.test(startTime)) return
+      if (!endTime || !/^(1|2)\d{3}(-|\/)\d{2}(-|\/)\d{2}/.test(endTime)) return
+      if (!item[calendar.remarkfield]) return
+
+      datalist.push({
+        color: color,
+        level: color && levels[color] ? levels[color] : 100,
+        remark: item[calendar.remarkfield],
+        startMonth: startTime.substr(0, 4) + startTime.substr(5, 2),
+        endMonth: endTime.substr(0, 4) + endTime.substr(5, 2),
+        start: startTime.substr(0, 4) + startTime.substr(5, 2) + startTime.substr(8, 2),
+        startTime: `${startTime.substr(5, 2)}-${startTime.substr(8, 2)}`,
+        end: endTime.substr(0, 4) + endTime.substr(5, 2) + endTime.substr(8, 2),
+        endTime: `${endTime.substr(5, 2)}-${endTime.substr(8, 2)}`
+      })
+    })
+
+    if (datalist.length === 0) return datelist
+
+    datalist.sort((a, b) => a.level - b.level)
+
+    let styles = [
+      {background: 'black', color: '#ffffff'},
+      {background: 'red', color: '#ffffff'},
+      {background: 'orange', color: '#ffffff'},
+      {background: 'yellow', color: 'rgba(0,0,0,.65)'},
+      {background: 'green', color: '#ffffff'},
+      {background: 'lightgreen', color: 'rgba(0,0,0,.65)'},
+      {background: 'cyan', color: 'rgba(0,0,0,.65)'},
+      {background: 'blue', color: '#ffffff'},
+      {background: 'purple', color: '#ffffff'},
+      {background: 'white', color: 'rgba(0,0,0,.65)'},
+    ]
+
+    return datelist.map(month => {
+      datalist.forEach(item => {
+        if (item.startMonth <= month.time && item.endMonth >= month.time) {
+          month.subData.push(item)
+        }
+      })
+      month.sublist = month.sublist.map(week => {
+        week.sublist = week.sublist.map(day => {
+          if (!day) return null
+
+          datalist.forEach(item => {
+            if (item.start <= day.time && item.end >= day.time) {
+              day.subData.push(item)
+            }
+          })
+
+          if (day.subData[0]) {
+            day.style = styles[day.subData[0].level - 1] || null
+          }
+
+          return day
+        })
+
+        return week
+      })
+
+      return month
+    })
   }
 
   getDateList = (selectYear) => {
@@ -91,10 +189,12 @@
       }
 
       for (let i = 1; i <= end; i++) {
+        let _day = i < 10 ? `0${i}` : `${i}`
+
         if (_weeklist[_weeklist.length - 1].sublist.length < 7) {
-          _weeklist[_weeklist.length - 1].sublist.push({day: i < 10 ? `0${i}` : `${i}`, label: i})
+          _weeklist[_weeklist.length - 1].sublist.push({day: _day, time: selectYear + month + _day, label: i, subData: []})
         } else {
-          let _week = {week: _weeklist.length + 1, sublist: [{day: i < 10 ? `0${i}` : `${i}`, label: i}]}
+          let _week = {week: _weeklist.length + 1, sublist: [{day: _day, time: selectYear + month + _day, label: i, subData: []}]}
           _weeklist.push(_week)
         }
       }
@@ -106,8 +206,10 @@
 
       datelist.push({
         month: month,
+        time: selectYear + month,
         label: monthName[month],
-        sublist: _weeklist
+        sublist: _weeklist,
+        subData: []
       })
     })
 
@@ -128,6 +230,10 @@
     }
 
     this.setState({ selectYear: value, datelist, monthlist })
+
+    if (this.props.changeDate) {
+      this.props.changeDate(value)
+    }
   }
 
   monthChange = (value) => {
@@ -144,14 +250,10 @@
   }
 
   render() {
+    const { loading } = this.props
     const { level, selectMonth, selectYear, yearlist, levels, datelist, monthlist } = this.state
     const _levelName = {day: '鏃�', month: '鏈�', year: '骞�'}
-    const listData = [
-      { type: 'orange', content: 'This is error event 2.' },
-      { type: 'cyan', content: 'This is error event 3.' },
-      { type: 'green', content: 'This is error event 4.' },
-    ]
-    
+    console.log(loading)
     return (
       <div className="mk-calendar">
         <div className="mk-calendar-control">
@@ -194,12 +296,17 @@
                       <tr key={cell.week}>
                         {cell.sublist.map((d, i) => (
                           <td key={i}>
-                            {d ? <div className={'day-wrap ' + d.class} onClick={() => this.triggerDay(d)}>
-                              {d.label ? <Popover mouseEnterDelay={0.3} overlayClassName="calendar-day-pop" content={
+                            {d ? <div className={'day-wrap ' + d.class} style={d.style || null} onClick={() => this.triggerDay(d)}>
+                              {d.subData.length > 0 ? <Popover mouseEnterDelay={0.3} overlayClassName="calendar-day-pop" content={
                                 <div>
-                                  {listData.map((data, index) => (
+                                  {d.subData.map((data, index) => (
                                     <div key={index} className="message">
-                                      <Badge color={data.type} text={data.content} />
+                                      <Badge color={data.color} text={
+                                        <span>
+                                          {data.remark}
+                                          <span style={{color: 'rgba(0,0,0,.45)'}}>({data.startTime + ' ~ ' + data.endTime})</span>
+                                        </span>}
+                                      />
                                     </div>
                                   ))}
                                 </div>
@@ -228,14 +335,14 @@
                   <tr key={cell.week}>
                     {cell.sublist.map((d, i) => (
                       <td key={i}>
-                        {d ? <div className="month-wrap" onClick={() => this.triggerDay(d)}>
+                        {d ? <div className="month-wrap" style={d.style || null} onClick={() => this.triggerDay(d)}>
                           <div className="header">
                             {d.label}
                           </div>
                           <ul className="content">
-                            {[1,3,7,18,22].includes(d.label) && listData.map((data, index) => (
-                              <li key={index} className="message" title={data.content}>
-                                <Badge color={data.type} text={data.content} />
+                            {d.subData.map((data, index) => (
+                              <li key={index} className="message" title={data.remark}>
+                                <Badge color={data.color} text={data.remark} />
                               </li>
                             ))}
                           </ul>
@@ -255,9 +362,9 @@
                     {item.label}
                   </div>
                   <ul className="content">
-                    {['01', '05', '10'].includes(item.month) && listData.map((data, index) => (
-                      <li key={index} className="message" title={data.content}>
-                        <Badge color={data.type} text={data.content} />
+                    {item.subData.map((data, index) => (
+                      <li key={index} className="message" title={data.remark}>
+                        <Badge color={data.color} text={data.remark} />
                       </li>
                     ))}
                   </ul>
diff --git a/src/tabviews/zshare/calendar/index.scss b/src/tabviews/zshare/calendar/index.scss
index b51378a..a3a0122 100644
--- a/src/tabviews/zshare/calendar/index.scss
+++ b/src/tabviews/zshare/calendar/index.scss
@@ -1,6 +1,6 @@
 .mk-calendar {
   width: 100%;
-  padding: 10px;
+  padding: 20px;
 
   .mk-calendar-control {
     text-align: right;
@@ -84,9 +84,12 @@
                   right: 0;
                   .message {
                     width: 100%;
-                    white-space: nowrap;
-                    overflow: hidden;
-                    text-overflow: ellipsis;
+                    // white-space: nowrap;
+                    // overflow: hidden;
+                    // text-overflow: ellipsis;
+                    .ant-badge-status-text {
+                      color: inherit;
+                    }
                   } 
                 }
                 .content::-webkit-scrollbar {
@@ -157,12 +160,12 @@
   .message {
     .ant-badge-status-text {
       display: inline-block;
-      min-width: 100px;
-      max-width: 200px;
-      white-space: nowrap;
+      min-width: 200px;
+      max-width: 350px;
+      // white-space: nowrap;
       vertical-align: middle;
-      overflow: hidden;
-      text-overflow: ellipsis;
+      // overflow: hidden;
+      // text-overflow: ellipsis;
     }
   }
   .ant-popover-inner-content {
diff --git a/src/tabviews/zshare/mutilform/index.jsx b/src/tabviews/zshare/mutilform/index.jsx
index 3926e06..5046aa5 100644
--- a/src/tabviews/zshare/mutilform/index.jsx
+++ b/src/tabviews/zshare/mutilform/index.jsx
@@ -5,7 +5,7 @@
 import moment from 'moment'
 
 import Api from '@/api'
-import { formRule } from '@/utils/option.js'
+import { formRule, calendarColors } from '@/utils/option.js'
 import Utils from '@/utils/utils.js'
 import FileUpload from '../fileupload'
 import './index.scss'
@@ -564,6 +564,28 @@
             </Form.Item>
           </Col>
         )
+      } else if (item.type === 'color') { // 棰滆壊閫夋嫨
+        fields.push(
+          <Col span={24 / cols} key={index}>
+            <Form.Item label={item.label}>
+              {getFieldDecorator(item.field, {
+                initialValue: item.initval,
+                rules: [
+                  {
+                    required: item.required === 'true',
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
+              })(
+                <Select onChange={(value, option) => {this.selectChange(item, value, option)}} disabled={item.readonly === 'true'}>
+                  {calendarColors.map(option =>
+                    <Select.Option key={option.name} style={{background: option.name, color: option.value}} value={option.name}>{option.name}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
       } else if (item.type === 'select' || item.type === 'link') { // 涓嬫媺鎼滅储
         let hasSubField = false
         if (item.linkSubField && item.linkSubField.length > 0) { // 瀛樺湪鍏宠仈瀛楁锛屾暟鎹瓨鍌�
diff --git a/src/templates/calendarconfig/calcomponent/calendarform/index.jsx b/src/templates/calendarconfig/calcomponent/calendarform/index.jsx
new file mode 100644
index 0000000..5f3e05b
--- /dev/null
+++ b/src/templates/calendarconfig/calcomponent/calendarform/index.jsx
@@ -0,0 +1,139 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Select, Checkbox } from 'antd'
+import './index.scss'
+
+class MainTab extends Component {
+  static propTpyes = {
+    config: PropTypes.object,    // 椤甸潰閰嶇疆
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    calendar: PropTypes.any      // 鏃ュ巻閰嶇疆淇℃伅
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  render() {
+    const { getFieldDecorator } = this.props.form
+    const { calendar, config } = this.props
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 10 }
+      }
+    }
+    return (
+      <Form {...formItemLayout} className="model-calendar-form">
+        <Row gutter={24}>
+          <Col span={24}>
+            <Form.Item label={'寮�濮嬫椂闂�'}>
+              {getFieldDecorator('startfield', {
+                initialValue: calendar.startfield,
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.select'] + '寮�濮嬫椂闂�!'
+                  }
+                ]
+              })(
+                <Select>
+                  {config.columns.map(option =>
+                    <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={'缁撴潫鏃堕棿'}>
+              {getFieldDecorator('endfield', {
+                initialValue: calendar.endfield,
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.select'] + '缁撴潫鏃堕棿!'
+                  }
+                ]
+              })(
+                <Select>
+                  {config.columns.map(option =>
+                    <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={'淇℃伅瀛楁'}>
+              {getFieldDecorator('remarkfield', {
+                initialValue: calendar.remarkfield,
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.select'] + '淇℃伅瀛楁!'
+                  }
+                ]
+              })(
+                <Select>
+                  {config.columns.map(option =>
+                    <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={'棰滆壊瀛楁'}>
+              {getFieldDecorator('colorfield', {
+                initialValue: calendar.colorfield,
+                rules: [
+                  {
+                    required: false,
+                    message: this.props.dict['form.required.select'] + '棰滆壊瀛楁!'
+                  }
+                ]
+              })(
+                <Select>
+                  {config.columns.map(option =>
+                    <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={'鏃ュ巻绛夌骇'}>
+              {getFieldDecorator('levels', {
+                initialValue: calendar.levels,
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.select'] + '鏃ュ巻绛夌骇!'
+                  }
+                ]
+              })(
+                <Checkbox.Group options={[{ value: 'day', label: '鏃�' }, { value: 'month', label: '鏈�' }, { value: 'year', label: '骞�' }]}/>
+              )}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(MainTab)
\ No newline at end of file
diff --git a/src/templates/calendarconfig/calcomponent/calendarform/index.scss b/src/templates/calendarconfig/calcomponent/calendarform/index.scss
new file mode 100644
index 0000000..1d2e501
--- /dev/null
+++ b/src/templates/calendarconfig/calcomponent/calendarform/index.scss
@@ -0,0 +1,7 @@
+.model-calendar-form {
+  min-height: 180px;
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+}
\ No newline at end of file
diff --git a/src/templates/calendarconfig/calcomponent/index.jsx b/src/templates/calendarconfig/calcomponent/index.jsx
new file mode 100644
index 0000000..c23c868
--- /dev/null
+++ b/src/templates/calendarconfig/calcomponent/index.jsx
@@ -0,0 +1,98 @@
+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 CalendarForm from './calendarform'
+
+import './index.scss'
+
+class SettingComponent extends Component {
+  static propTpyes = {
+    config: PropTypes.any,         // 鏍囩
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    calendar: null,  // 鏃ュ巻璁剧疆
+    visible: false,  // 妯℃�佹鎺у埗
+  }
+
+  /**
+   * @description 淇濆瓨椤甸潰閰嶇疆淇℃伅
+   */
+  calendarSave = () => {
+    const { config } = this.props
+
+    this.calendarRef.handleConfirm().then(res => {
+      this.setState({
+        visible: false
+      })
+      this.props.updateConfig({...config, calendar: res})
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  /**
+   * @description 娣诲姞鎴栦慨鏀规爣绛�
+   */
+  handleTab = (e) => {
+    const { config } = this.props
+    e.stopPropagation()
+
+    let calendar = fromJS(config.calendar).toJS()
+
+    this.setState({
+      visible: true,
+      calendar
+    })
+  }
+
+  render() {
+    const { config } = this.props
+    const { dict, visible, calendar } = this.state
+
+    return (
+      <div className="model-calendar-setting">
+        <Icon type="edit" onClick={this.handleTab} />
+        {/* 璁剧疆鍏ㄥ眬閰嶇疆鍙婂垪琛ㄦ暟鎹簮 */}
+        <Modal
+          wrapClassName="model-calendar-setting-modal"
+          title={dict['model.edit']}
+          visible={visible}
+          width={700}
+          maskClosable={false}
+          onCancel={() => { this.setState({ visible: false })}}
+          cancelText={this.state.dict['model.cancel']}
+          okText={this.state.dict['model.confirm']}
+          onOk={this.calendarSave}
+          destroyOnClose
+        >
+          <CalendarForm
+            dict={dict}
+            config={config}
+            calendar={calendar}
+            wrappedComponentRef={(inst) => this.calendarRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default SettingComponent
\ No newline at end of file
diff --git a/src/templates/calendarconfig/calcomponent/index.scss b/src/templates/calendarconfig/calcomponent/index.scss
new file mode 100644
index 0000000..c1425e5
--- /dev/null
+++ b/src/templates/calendarconfig/calcomponent/index.scss
@@ -0,0 +1,40 @@
+.model-calendar-setting {
+  position: absolute;
+  right: 0;
+  top: 0;
+  >.anticon-edit {
+    font-size: 18px;
+    padding: 5px;
+    margin-right: 10px;
+    color: #1890ff;
+    cursor: pointer;
+  }
+}
+
+.model-calendar-setting-modal {
+  .ant-modal {
+    top: 50px;
+    padding-bottom: 5px;
+    .ant-modal-body {
+      max-height: calc(100vh - 190px);
+      overflow-y: auto;
+    }
+    .ant-modal-body::-webkit-scrollbar {
+      width: 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-empty-normal {
+      margin: 5px 0px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/calendarconfig/index.jsx b/src/templates/calendarconfig/index.jsx
new file mode 100644
index 0000000..dd221d5
--- /dev/null
+++ b/src/templates/calendarconfig/index.jsx
@@ -0,0 +1,890 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { DndProvider } from 'react-dnd'
+import HTML5Backend from 'react-dnd-html5-backend'
+import { Button, Card, Modal, Collapse, notification, Spin, Icon, Switch } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import Utils from '@/utils/utils.js'
+import { getMainMenuForm } from '@/templates/zshare/formconfig'
+
+import asyncComponent from '@/utils/asyncComponent'
+import SearchComponent from '@/templates/sharecomponent/searchcomponent'
+
+import MenuForm from '@/templates/zshare/menuform'
+// import EditComponent from '@/templates/zshare/editcomponent'
+import SourceElement from '@/templates/zshare/dragsource'
+import Source from './source'
+import './index.scss'
+
+const { Panel } = Collapse
+const { confirm } = Modal
+
+const SettingComponent = asyncComponent(() => import('@/templates/sharecomponent/datasourcecomponent'))
+const TabComponent = asyncComponent(() => import('./tabcomponent'))
+const CalComponent = asyncComponent(() => import('./calcomponent'))
+const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
+const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
+const CalendarComponent = asyncComponent(() => import('@/tabviews/zshare/calendar'))
+
+class SubTableConfig extends Component {
+  static propTpyes = {
+    menu: PropTypes.any,
+    optionLibs: PropTypes.any,
+    reloadmenu: PropTypes.func,
+    handleView: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
+    config: null,            // 椤甸潰閰嶇疆
+    visible: false,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪锛屾ā鎬佹鏄剧ず鎺у埗
+    tableFields: [],         // 宸查�夎〃瀛楁闆�
+    fields: null,            // 鎼滅储鏉′欢鍙婃樉绀哄垪锛屽彲閫夊瓧娈�
+    menuformlist: null,      // 鍩烘湰淇℃伅琛ㄥ崟瀛楁
+    formlist: null,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪琛ㄥ崟瀛楁
+    menuloading: false,      // 鑿滃崟淇濆瓨涓�
+    menucloseloading: false, // 鑿滃崟鍏抽棴鏃讹紝閫夋嫨淇濆瓨
+    loading: false,          // 鍔犺浇涓紝椤甸潰spin
+    closeVisible: false,     // 鍏抽棴妯℃�佹
+    originConfig: null,      // 鍘熼厤缃�
+    tabviews: [],            // 鎵�鏈夋爣绛鹃〉
+    optionLibs: null,        // 鑷畾涔変笅鎷夐�夐」搴�
+    activeKey: '0',          // 榛樿灞曞紑鍩烘湰淇℃伅
+    pasteContent: null,      // 绮樿创鍐呭
+    openEdition: '',         // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
+    mockdata: [],            // 娴嬭瘯鏁版嵁
+    mockloading: false       // 鏁版嵁鍔犺浇涓�
+  }
+
+  /**
+   * @description 鏁版嵁棰勫鐞�
+   * 1銆佽缃〉闈㈤厤缃俊鎭紝鏂板缓鎴栨棤閰嶇疆淇℃伅鏃讹紙鍒囨崲妯℃澘鍚庢棤閰嶇疆淇℃伅锛夛紝浣跨敤妯℃澘榛樿閰嶇疆
+   * 2銆佽缃搷浣滅被鍨嬨�佸師濮嬭彍鍗曚俊鎭紙姣忔淇濆瓨鍚庨噸缃級銆佸凡浣跨敤琛ㄥ強鍩烘湰淇℃伅琛ㄥ崟
+   */
+  UNSAFE_componentWillMount () {
+    const { menu, optionLibs } = this.props
+    let _LongParam = menu.LongParam
+    let _config = ''
+
+    if (!_LongParam) {
+      _config = fromJS(Source.baseConfig).toJS()
+      _config.isAdd = true
+    } else {
+      _config = _LongParam
+      _config.search.forEach(item => {
+        if (
+          (item.type === 'select' || item.type === 'multiselect' || item.type === 'link') &&
+          item.resourceType === '0' &&
+          item.options && item.options.length > 0
+        ) {
+          optionLibs.set(menu.MenuID + item.uuid, {
+            uuid: menu.MenuID + item.uuid,
+            label: item.label,
+            parname: menu.MenuName,
+            type: 'search',
+            options: item.options
+          })
+        }
+      })
+    }
+
+    this.setState({
+      openEdition: menu.open_edition || '',
+      optionLibs: optionLibs,
+      activeKey: menu.activeKey || '0',
+      config: _config,
+      originMenu: fromJS(menu).toJS(),
+      menuformlist: getMainMenuForm(menu, _config),
+      mockdata: this.getMockData(moment().format('YYYY'))
+    })
+  }
+
+  /**
+   * @description 鍔犺浇瀹屾垚鍚�
+   * 1銆佽幏鍙栫郴缁熷彲浣跨敤琛�
+   * 2銆佹牴鎹厤缃俊鎭腑宸蹭娇鐢ㄨ〃鑾峰彇鐩稿叧瀛楁淇℃伅
+   */
+  componentDidMount () {
+    this.reloadTab(false)
+  }
+
+  getMockData = (year) => {
+    let msgs = [
+      {color: 'red', remark: '鏈嶅姟鍣ㄥ紓甯革紝璇疯仈绯昏繍缁翠汉鍛橈紒'},
+      {color: 'orange', remark: '绯荤粺寮傚父锛岃鍙婃椂澶勭悊锛�'},
+      {color: 'yellow', remark: '鎮ㄧ殑璁㈠崟寮傚父锛岃鑱旂郴瀹㈡湇锛�'},
+      {color: 'green', remark: '鎮ㄧ殑璁㈠崟宸插畬鎴愩��'},
+      {color: 'lightgreen', remark: '娑堟伅宸插彂閫侊紝璇峰強鏃舵煡鏀躲��'},
+      {color: 'cyan', remark: '鎮ㄦ湁涓�鏉℃柊鐨勬秷鎭��'},
+      {color: 'blue', remark: '浠诲姟鏈畬鎴愶紝璇锋敞鎰忓悗缁伐浣溿��'},
+      {color: 'purple', remark: '鎮ㄦ湁鏂扮殑浠诲姟绛夊緟澶勭悊锛�'}
+    ]
+    let mockdata = []
+
+    for (let i = 1; i <= 12; i++) {
+      if (Math.random() > 0.5) {
+        let cell = {uuid: Utils.getuuid()}
+        let msg = msgs[Math.floor(Math.random() * 8)]
+        let day = Math.floor(Math.random() * 28 + 1)
+
+        cell.color = msg.color
+        cell.remark = msg.remark
+        cell.start = `${year}-${i < 10 ? '0' + i : i}-${day < 10 ? '0' + day : day}`
+        cell.end = moment(cell.start, 'YYYY-MM-DD').add(Math.floor(Math.random() * 10), 'days').format('YYYY-MM-DD')
+
+        mockdata.push(cell)
+
+        if (Math.random() > 0.5) {
+          let _cell = {uuid: Utils.getuuid()}
+          let _msg = msgs[Math.floor(Math.random() * 8)]
+          let _day = Math.floor(Math.random() * 28 + 1)
+
+          _cell.color = _msg.color
+          _cell.remark = _msg.remark
+          _cell.start = `${year}-${i < 10 ? '0' + i : i}-${_day < 10 ? '0' + _day : _day}`
+          _cell.end = moment(_cell.start, 'YYYY-MM-DD').add(Math.floor(Math.random() * 10), 'days').format('YYYY-MM-DD')
+
+          mockdata.push(_cell)
+        }
+      }
+    }
+
+    return mockdata
+  }
+
+  /**
+   * @description 鍔犺浇鎴栧埛鏂版爣绛句俊鎭�
+   */
+  reloadTab = (type) => {
+    this.setState({
+      loading: type,
+      tabviews: []
+    })
+    Api.getSystemConfig({func: 'sPC_Get_UserTemp', TypeCharTwo: 'tab'}).then(res => {
+      if (res.status) {
+        let _tabviews = []
+        res.UserTemp.forEach(temp => {
+          let item = {
+            uuid: temp.MenuID,
+            value: temp.MenuID,
+            text: temp.MenuName,
+            type: temp.Template,
+            MenuNo: temp.MenuNo
+          }
+
+          _tabviews.push(item)
+        })
+
+        this.setState({
+          loading: false,
+          tabviews: _tabviews
+        })
+
+        if (type) {
+          notification.success({
+            top: 92,
+            message: '鍒锋柊鎴愬姛銆�',
+            duration: 2
+          })
+        }
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  // 椤甸潰杩斿洖
+  handleViewBack = () => {
+    let param = {
+      editMenu: null,
+      optionLibs: null,
+      editTab: null,
+      tabConfig: null,
+      subTabConfig: null,
+      btnTab: null,
+      btnTabConfig: null,
+      editAction: null,
+      subConfig: null,
+      tabview: ''
+    }
+
+    this.props.handleView(param)
+  }
+
+  getFuncNames = (data, funcNames, tableNames) => {
+    data.forEach(item => {
+      if (item.subfuncs) {
+        this.getFuncNames(item.subfuncs, funcNames, tableNames)
+      } else {
+        if (item.tableName) {
+          tableNames.push(item.tableName)
+        }
+        if (item.innerFunc) {
+          funcNames.push({func: item.innerFunc, label: item.label || ''})
+        }
+
+        if (item.callbackFunc) {
+          funcNames.push({func: item.callbackFunc, label: item.label || ''})
+        }
+      }
+    })
+
+    return {
+      func: funcNames,
+      table: tableNames
+    }
+  }
+
+  /**
+   * @description 涓夌骇鑿滃崟淇濆瓨
+   */
+  submitConfig = () => {
+    const { menu } = this.props
+    const { originMenu, openEdition } = this.state
+
+    let config = fromJS(this.state.config).toJS()
+
+    this.menuformRef.handleConfirm().then(res => {
+      if (config.isAdd) {
+        config.search = config.search.filter(item => !item.origin)
+      }
+
+      let _LongParam = ''
+      let _config = {...config, easyCode: res.easyCode}
+      let _pageParam = {...menu.PageParam, OpenType: res.opentype}
+
+      // 鏈缃暟鎹簮鎴栨爣绛句笉鍚堟硶鏃讹紝鍚敤鐘舵�佷负false
+      let vresult = this.verifyconfig(_config)
+      if (vresult !== true) {
+        _config.enabled = false
+      }
+
+      _config.funcs = [] // 椤甸潰鍙婂瓙椤甸潰瀛樺偍杩囩▼闆�
+
+      _config.funcs.push({
+        type: 'view',
+        subtype: 'view',
+        uuid: menu.MenuID,
+        intertype: _config.setting.interType || 'inner',
+        interface: _config.setting.interface || '',
+        tableName: _config.setting.tableName || '',
+        innerFunc: _config.setting.innerFunc || '',
+        outerFunc: _config.setting.outerFunc || ''
+      })
+
+      if (_config.tab) {
+        _config.funcs.push({
+          type: 'tab',
+          subtype: 'tab',
+          uuid: _config.tab.uuid,
+          label: _config.tab.label,
+          linkTab: _config.tab.linkTab
+        })
+      }
+
+      if (this.state.closeVisible) { // 鏄剧ず鍏抽棴瀵硅瘽妗嗘椂锛屾ā鎬佹涓繚瀛樻寜閽紝鏄剧ず淇濆瓨涓姸鎬�
+        this.setState({
+          menucloseloading: true
+        })
+      } else {
+        this.setState({
+          menuloading: true
+        })
+      }
+
+      new Promise(resolve => {
+        if (_config.tab) {
+          Api.getSystemConfig({
+            func: 'sPC_Get_LongParam',
+            MenuID: _config.tab.linkTab
+          }).then(result => {
+            if (result.status && result.LongParam) {
+              let _LongParam = ''
+    
+              if (result.LongParam) {
+                try {
+                  _LongParam = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
+                } catch (e) {
+                  console.warn('Parse Failure')
+                  _LongParam = ''
+                }
+              }
+  
+              if (_LongParam) {
+                _config.funcs[1].menuNo = _LongParam.tabNo || ''
+                _config.funcs[1].subfuncs = _LongParam.funcs || []
+              }
+            }
+            resolve()
+          })
+        } else {
+          resolve()
+        }
+      }).then(() => {
+        // 淇濆瓨鏃跺垹闄ら厤缃被鍨嬶紝system 銆乽ser
+        delete _config.type
+        delete _config.isAdd
+  
+        try {
+          _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+        } catch (e) {
+          notification.warning({
+            top: 92,
+            message: '缂栬瘧閿欒',
+            duration: 5
+          })
+          this.setState({
+            menucloseloading: false,
+            menuloading: false
+          })
+          return
+        }
+  
+        let tabParam = { // 娣诲姞鑿滃崟tab椤�
+          func: 'sPC_sMenusTab_AddUpt',
+          MenuID: menu.MenuID
+        }
+
+        if (_config.tab) {
+          tabParam.LText = Utils.formatOptions(`select '${menu.MenuID}' as MenuID ,'${_config.tab.linkTab}' as Tabid,'${_config.tab.label}' as TabName ,'0' as Sort`)
+        } else {
+          tabParam.LText = Utils.formatOptions(`select '${menu.MenuID}' as MenuID ,'' as Tabid,'' as TabName ,'0' as Sort`)
+        }
+
+        tabParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+        tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
+  
+        let _vals = this.getFuncNames(_config.funcs, [], [])
+        let _tables = Array.from(new Set(_vals.table))
+
+        let param = {
+          func: 'sPC_TrdMenu_AddUpt',
+          FstID: res.fstMenuId,
+          SndID: res.parentId,
+          ParentID: res.parentId,
+          MenuID: menu.MenuID,
+          MenuNo: res.MenuNo,
+          EasyCode: res.easyCode,
+          Template: menu.PageParam.Template || '',
+          MenuName: res.MenuName,
+          PageParam: JSON.stringify(_pageParam),
+          LongParam: _LongParam,
+          LText: _vals.func.map(item => `select '${menu.MenuID}' as MenuID,'${item.func}' as ProcName,'${item.label}' as MenuName`),
+          LTexttb: _tables.map(item => `select '${menu.MenuID}' as MenuID,'${item}' as tbName`)
+        }
+
+        if (menu.menuSort) { // 鑿滃崟鏂板缓鏃惰缃帓搴�
+          param.Sort = menu.menuSort
+        }
+
+        param.LText = param.LText.join(' union all ')
+        param.LText = Utils.formatOptions(param.LText)
+        param.LTexttb = param.LTexttb.join(' union all ')
+        param.LTexttb = Utils.formatOptions(param.LTexttb)
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+        if (openEdition) { // 鐗堟湰绠$悊
+          param.open_edition = openEdition
+        }
+
+        // 淇濆瓨鏈湴
+        let localParam = fromJS(param).toJS()
+
+        Api.getSystemConfig(param).then(response => {
+          if (response.status) {
+            let _FMenu = originMenu.fstMenuList.filter(fstM => fstM.MenuID === res.fstMenuId)[0]
+            let _supMenuList = []
+            if (_FMenu) {
+              _supMenuList = _FMenu.options
+            }
+
+            this.setState({
+              config: _config,
+              openEdition: response.open_edition || '',
+              originMenu: {
+                ...originMenu,
+                LongParam: _config,
+                PageParam: _pageParam,
+                MenuName: res.MenuName,
+                MenuNo: res.MenuNo,
+                ParentID: res.parentId,
+                fstMenuId: res.fstMenuId,
+                supMenuList: _supMenuList
+              }
+            })
+
+            this.props.reloadmenu()
+
+            // 鏍囩淇℃伅淇濆瓨
+            Api.getSystemConfig(tabParam).then(result => {
+              if (result.status) {
+                notification.success({
+                  top: 92,
+                  message: '淇濆瓨鎴愬姛',
+                  duration: 2
+                })
+                if (this.state.closeVisible) {
+                  this.handleViewBack()
+                } else {
+                  this.setState({
+                    menuloading: false,
+                    menucloseloading: false
+                  })
+                }
+              } else {
+                notification.warning({
+                  top: 92,
+                  message: result.message,
+                  duration: 5
+                })
+                this.setState({
+                  menuloading: false,
+                  menucloseloading: false
+                })
+              }
+            })
+
+            localParam.func = 'sPC_TrdMenu_AddUpt_For_Local'
+            delete localParam.LongParam
+            delete localParam.PageParam
+            delete localParam.Template
+            delete localParam.Sort
+            delete localParam.EasyCode
+            delete localParam.open_edition
+
+            Api.getLocalConfig(localParam)
+          } else {
+            this.setState({
+              menuloading: false,
+              menucloseloading: false
+            })
+            notification.warning({
+              top: 92,
+              message: response.message,
+              duration: 5
+            })
+          }
+        })
+      })
+    }, () => {
+      notification.warning({
+        top: 92,
+        message: this.state.dict['model.menu.basemsg'],
+        duration: 5
+      })
+    })
+  }
+
+  cancelConfig = () => {
+    const { menu } = this.props
+    const { config, originMenu } = this.state
+
+    let _this = this
+
+    if (config.isAdd) {
+      confirm({
+        content: '鑿滃崟灏氭湭鎻愪氦锛岀‘瀹氭斁寮冧繚瀛樺悧锛�',
+        okText: this.state.dict['model.confirm'],
+        cancelText: this.state.dict['model.cancel'],
+        onOk() {
+          _this.props.handleView()
+        },
+        onCancel() {}
+      })
+    } else {
+      this.menuformRef.handleConfirm().then(res => {
+        let _config = {...config, easyCode: res.easyCode}
+        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
+        let _originMenu = {
+          ...originMenu,
+          LongParam: _config,
+          PageParam: _pageParam,
+          MenuName: res.MenuName,
+          MenuNo: res.MenuNo,
+          ParentID: res.parentId,
+          fstMenuId: res.fstMenuId
+        }
+
+        if (!is(fromJS(originMenu), fromJS(_originMenu))) {
+          this.setState({
+            closeVisible: true
+          })
+        } else {
+          this.props.handleView()
+        }
+      }, () => {
+        this.setState({
+          closeVisible: true
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 璁剧疆鍙厤缃寜閽�
+   */
+  setSubConfig = () => {
+    const { menu } = this.props
+    const { config, originMenu, optionLibs, activeKey, openEdition } = this.state
+
+    if (config.isAdd) { // 鏂板缓鑿滃崟锛屾彁绀鸿彍鍗曞皻鏈繚瀛�
+      notification.warning({
+        top: 92,
+        message: this.state.dict['header.menu.config.notsave'],
+        duration: 5
+      })
+    } else {
+      this.menuformRef.handleConfirm().then(res => {
+        let _config = {...config, easyCode: res.easyCode}
+        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
+        let _originMenu = {
+          ...originMenu,
+          LongParam: _config,
+          PageParam: _pageParam,
+          MenuName: res.MenuName,
+          MenuNo: res.MenuNo,
+          ParentID: res.parentId,
+          fstMenuId: res.fstMenuId
+        }
+
+        if (!is(fromJS(originMenu), fromJS(_originMenu))) { // 鑿滃崟淇℃伅鍙樺寲鏃讹紝鎻愮ず淇濆瓨
+          notification.warning({
+            top: 92,
+            message: this.state.dict['header.menu.config.update'],
+            duration: 5
+          })
+          return
+        }
+
+        // 鑿滃崟淇℃伅楠岃瘉閫氳繃鍚庯紝璺宠浆瀛愰厤缃〉闈�
+        _originMenu.activeKey = activeKey       // 淇濆瓨褰撳墠鎵撳紑椤电
+        _originMenu.open_edition = openEdition  // 鏇存柊鐗堟湰鍙�
+
+        let param = {
+          optionLibs: optionLibs,
+          editMenu: _originMenu,
+          editTab: fromJS(config.tab).toJS(),
+          tabConfig: null,
+          editSubTab: null,
+          subTabConfig: null,
+          btnTab: null,
+          btnTabConfig: null,
+          editAction: '',
+          subConfig: '',
+          tabview: config.tab.type
+        }
+        
+        // 褰撳瓙琛ㄤ娇鐢ㄤ富椤垫悳绱㈡潯浠舵椂锛屽皢涓婚〉鎼滅储鍚戜笅浼犻��
+        if (param.editTab && param.editTab.searchPass === 'true') {
+          param.editTab.mainsearch = fromJS(_config.search).toJS()
+        }
+
+        this.setState({
+          loading: true
+        })
+
+        Api.getSystemConfig({
+          func: 'sPC_Get_LongParam',
+          MenuID: config.tab.linkTab
+        }).then(res => {
+          if (res.status) {
+            this.setState({
+              loading: false
+            })
+            let _LongParam = ''
+            if (res.LongParam) {
+              try {
+                _LongParam = JSON.parse(window.decodeURIComponent(window.atob(res.LongParam)))
+              } catch (e) {
+                console.warn('Parse Failure')
+                _LongParam = ''
+              }
+            }
+
+            if (_LongParam && param.tabview === 'SubTable' && _LongParam.Template === 'SubTable') {
+              param.subConfig = _LongParam
+            }
+
+            if (param.editTab) {
+              param.editTab.open_edition = res.open_edition || ''
+            }
+
+            this.props.handleView(param)
+          } else {
+            this.setState({
+              loading: false
+            })
+            notification.warning({
+              top: 92,
+              message: res.message,
+              duration: 5
+            })
+          }
+        })
+      }, () => {
+        notification.warning({
+          top: 92,
+          message: this.state.dict['header.menu.config.update'],
+          duration: 5
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 鍒囨崲鏍囩鏄惁鍚敤
+   */
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    let _enabled = !config.enabled
+    let result = this.verifyconfig(config)
+
+    if (_enabled && result !== true) {
+      notification.warning({
+        top: 92,
+        message: result,
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: _enabled}
+    })
+  }
+
+  /**
+   * @description 鏍¢獙閰嶇疆淇℃伅鐨勫悎娉曟��
+   */
+  verifyconfig = (config) => {
+    let hasKey = false
+    let chartcols = []
+    config.columns.forEach(col => {
+      if (col.field) {
+        chartcols.push(col.field)
+      }
+      if (config.setting.primaryKey === col.field) {
+        hasKey = true
+      }
+    })
+
+    if (config.setting.interType === 'inner' && !config.setting.innerFunc && config.setting.default !== 'false' && !config.setting.dataresource) {
+      return '鑿滃崟灏氭湭璁剧疆鏁版嵁婧愶紝涓嶅彲鍚敤锛�'
+    } else if (!config.setting.primaryKey) {
+      return '鑿滃崟灏氭湭璁剧疆涓婚敭锛屼笉鍙惎鐢紒'
+    } else if (!hasKey) {
+      return '鏄剧ず鍒椾腑涓嶅瓨鍦ㄤ富閿瓧娈碉紝涓嶅彲鍚敤锛�'
+    } else {
+      return true
+    }
+  }
+
+  /**
+   * @description 缂栬緫鍔熻兘瀹屾垚鏇存柊锛屽寘鎷В鍐绘寜閽�佺矘璐淬�佹浛鎹㈢瓑
+   */
+  updateConfig = (res) => {
+    if (res.type === 'thaw') {
+      this.setState({
+        config: res.config
+      })
+    } else if (res.type === 'paste') {
+      this.setState({
+        pasteContent: res.content
+      }, () => {
+        this.setState({
+          pasteContent: null
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 鏇存柊鎼滅储鏉′欢閰嶇疆淇℃伅
+   */
+  updatesearch = (config, options) => {
+    const { optionLibs } = this.state
+
+    this.setState({
+      config: config,
+      optionLibs: options || optionLibs
+    })
+  }
+
+  /**
+   * @description 鏇存柊鏄剧ず鍒楅厤缃俊鎭�
+   */
+  updateconfig = (config) => {
+    this.setState({
+      config: config
+    })
+  }
+  
+  /**
+   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
+   */
+  updatetable = (config, fields) => {
+    const { tableFields } = this.state
+
+    this.setState({
+      config: config,
+      tableFields: fields ? fields : tableFields
+    })
+  }
+
+  /**
+   * @description 鎵归噺娣诲姞锛屾洿鏂伴厤缃俊鎭�
+   */
+  updatefield = (config) => {
+    this.setState({
+      config: config
+    })
+  }
+
+  // 骞村垏鎹㈡椂閲嶆柊鐢熸垚鏁版嵁
+  changeDate = (year) => {
+    this.setState({mockloading: true}, () => {
+      this.setState({
+        mockloading: false,
+        mockdata: this.getMockData(year)
+      })
+    })
+  }
+
+  render () {
+    const { activeKey, config, tabviews, mockdata, mockloading } = this.state
+
+    return (
+      <div className="model-subtable-board">
+        <DndProvider backend={HTML5Backend}>
+          {/* 宸ュ叿鏍� */}
+          <div className="tools">
+            <Collapse accordion defaultActiveKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
+              {/* 鍩烘湰淇℃伅 */}
+              <Panel forceRender={true} header={'鏍囩鍩烘湰淇℃伅'} key="0" id="subtable-basedata">
+                {/* 鑿滃崟淇℃伅 */}
+                <MenuForm
+                  dict={this.state.dict}
+                  formlist={this.state.menuformlist}
+                  wrappedComponentRef={(inst) => this.menuformRef = inst}
+                />
+                {/* 琛ㄥ悕娣诲姞 */}
+                <TableComponent
+                  config={config}
+                  containerId="subtable-basedata"
+                  updatetable={this.updatetable}
+                />
+              </Panel>
+              {/* 鎼滅储鏉′欢娣诲姞 */}
+              <Panel header={this.state.dict['header.menu.search']} key="1">
+                <div className="search-element">
+                  {Source.searchItems.map((item, index) => {
+                    return (<SourceElement key={index} content={item}/>)
+                  })}
+                </div>
+                <FieldsComponent
+                  config={config}
+                  type="search"
+                  tableFields={this.state.tableFields}
+                  updatefield={this.updatefield}
+                />
+              </Panel>
+            </Collapse>
+          </div>
+          <div className="setting">
+            <Card title={
+              <div>
+                鏃ュ巻椤甸潰閰嶇疆 
+                <Icon type="redo" style={{marginLeft: '10px'}} title="鍒锋柊鏍囩鍒楄〃" onClick={() => this.reloadTab(true)} />
+              </div>
+            } bordered={false} extra={
+              <div>
+                {/* <EditComponent dict={this.state.dict} type="subtable" config={this.state.config} refresh={this.updateConfig}/> */}
+                <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={this.state.config.enabled} onChange={this.onEnabledChange} />
+                <Button type="primary" onClick={this.submitConfig} loading={this.state.menuloading}>{this.state.dict['model.save']}</Button>
+                <Button onClick={this.cancelConfig}>{this.state.dict['model.back']}</Button>
+              </div>
+            } style={{ width: '100%' }}>
+              <SettingComponent
+                type="subtable"
+                config={config}
+                MenuID={config.uuid}
+                menuformRef={this.menuformRef}
+                permFuncField={this.props.permFuncField}
+                updateConfig={this.updateconfig}
+              />
+              <SearchComponent
+                menu={{MenuID: config.uuid, MenuName: config.tabName}}
+                config={config}
+                pasteContent={this.state.pasteContent}
+                sysRoles={this.props.sysRoles}
+                optionLibs={this.state.optionLibs}
+                updatesearch={this.updatesearch}
+              />
+              <div className="calendar-wrap">
+                <TabComponent config={config} updateConfig={this.updateconfig} tabviews={tabviews} setSubConfig={this.setSubConfig} />
+                <CalComponent config={config} updateConfig={this.updateconfig} />
+                <CalendarComponent calendar={{
+                  levels: config.calendar.levels, startfield: 'start', endfield: 'end', colorfield: 'color', remarkfield: 'remark'
+                }} loading={mockloading} data={mockdata} changeDate={this.changeDate}/>
+              </div>
+            </Card>
+          </div>
+        </DndProvider>
+        <Modal
+          bodyStyle={{textAlign: 'center', color: '#000000', fontSize: '16px'}}
+          closable={false}
+          maskClosable={false}
+          visible={this.state.closeVisible}
+          onCancel={() => { this.setState({closeVisible: false}) }}
+          footer={[
+            <Button key="save" className="mk-btn mk-green" loading={this.state.menucloseloading} onClick={this.submitConfig}>{this.state.dict['model.save']}</Button>,
+            <Button key="confirm" className="mk-btn mk-yellow" onClick={this.handleViewBack}>{this.state.dict['model.notsave']}</Button>,
+            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['model.cancel']}</Button>
+          ]}
+          destroyOnClose
+        >
+          {this.state.dict['header.menu.config.placeholder']}
+        </Modal>
+        {this.state.loading && <Spin size="large" />}
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    sysRoles: state.sysRoles,
+    permFuncField: state.permFuncField,
+    memberLevel: state.memberLevel
+  }
+}
+
+const mapDispatchToProps = () => {
+  return {}
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(SubTableConfig)
diff --git a/src/templates/calendarconfig/index.scss b/src/templates/calendarconfig/index.scss
new file mode 100644
index 0000000..4f1d62f
--- /dev/null
+++ b/src/templates/calendarconfig/index.scss
@@ -0,0 +1,215 @@
+.model-subtable-board {
+  position: fixed;
+  z-index: 1070;
+  padding-top: 48px;
+  top: 0px;
+  left: 0px;
+  right: 0px;
+  bottom: 0px;
+  background: rgba(0, 0, 0, 0.35);
+  display: flex;
+  .tools {
+    flex: 1;
+    background: #ffffff;
+    border-right: 1px solid #d9d9d9;
+    height: 100%;
+    overflow-y: hidden;
+    padding-bottom: 30px;
+    .ant-collapse-item {
+      position: relative;
+      border: 0;
+    }
+    .ant-input-search {
+      margin-top: 10px;
+    }
+    .ant-collapse-item.ant-collapse-item-active {
+      border-bottom: 1px solid #d9d9d9;
+    }
+    .ant-collapse .ant-collapse-header {
+      padding: 11px 16px 10px 40px;
+      border-bottom: 1px solid #d9d9d9;
+      background: #1890ff;
+      color: #ffffff;
+    }
+    .ant-collapse-content-box {
+      .ant-form-item {
+        margin-bottom: 10px;
+        .ant-form-item-label {
+          text-align: left;
+          height: 25px;
+          line-height: 25px;
+        }
+      }
+    }
+    .search-element {
+      padding-top: 10px;
+      li {
+        padding: 0px 16px 10px;
+        div {
+          cursor: move;
+        }
+      }
+    }
+    .config-btn {
+      position: relative;
+
+      .config-btn-title {
+        margin-top: 20px;
+        margin-bottom: 10px;
+        color: #1890ff;
+        border-bottom: 1px solid #e8e8e8;
+      }
+    }
+    .ant-list {
+      margin-top: 20px;
+      .ant-list-item {
+        display: -webkit-box;
+        padding-right: 20px;
+        position: relative;
+        padding-left: 5px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        -webkit-line-clamp: 2;
+        -webkit-box-orient: vertical;
+        min-height: 55px;
+        width: 100%;
+        .anticon {
+          position: absolute;
+          top: 0px;
+          right: 0px;
+          padding: 3px 3px 10px 10px;
+          cursor: pointer;
+        }
+        .bottom-mask {
+          position: absolute;
+          width: 100%;
+          height: 8px;
+          bottom: 0;
+          left: 0;
+          background: #ffffff;
+          border-radius: 8px;
+        }
+      }
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+    .config-button {
+      min-width: 65px;
+    }
+  }
+  .tools:hover {
+    overflow-y: auto;
+  }
+  .tools::-webkit-scrollbar {
+    width: 7px;
+  }
+  .tools::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0);
+    background: rgba(0, 0, 0, 0);
+  }
+  .tools::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0);
+    background: rgba(0, 0, 0, 0);
+  }
+  .tools:hover::-webkit-scrollbar-thumb {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+    background: rgba(0, 0, 0, 0.13);
+  }
+  .tools:hover::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border: 1px solid rgba(0, 0, 0, 0.07);
+  }
+  .setting {
+    position: relative;
+    width: calc(100vw - 235px);
+    height: 100%;
+    background: #ffffff;
+    .ant-switch.big {
+      min-width: 60px;
+      height: 28px;
+      line-height: 28px;
+      margin-top: -2px;
+      .ant-switch-inner {
+        font-size: 14px;
+      }
+    }
+    .ant-switch.big::after {
+      width: 24px;
+      height: 24px;
+    }
+    .ant-card-head {
+      min-height: 44px;
+    }
+    .ant-card-head-title {
+      padding: 5px 0;
+      color: #1890ff;
+    }
+    .ant-card-extra {
+      padding: 5px 0;
+      button {
+        margin-left: 20px;
+      }
+    }
+    .ant-card-body {
+      position: relative;
+      padding: 0;
+      
+      .chart-view {
+        margin-bottom: 70px;
+
+        .chart-title {
+          position: relative;
+          color: rgba(0, 0, 0, 0.65);
+          font-weight: 400;
+          font-size: 16px;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          overflow: hidden;
+          margin: 0 20px;
+          padding: 10px 5px 5px;
+        }
+      }
+      > .anticon-setting {
+        position: absolute;
+        font-size: 18px;
+        right: 7px;
+        top: 10px;
+      }
+      .calendar-wrap {
+        position: relative;
+        padding-top: 10px;
+      }
+    }
+  }
+  .setting {
+    overflow-y: scroll;
+  }
+  .setting::-webkit-scrollbar {
+    width: 7px;
+  }
+  .setting::-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);
+    display: none;
+  }
+  .setting::-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);
+  }
+  .setting:hover::-webkit-scrollbar-thumb {
+    display: block;
+  }
+  .ant-spin {
+    position: absolute;
+    margin-left: calc(50vw - 22px);
+    margin-top: 30vh;
+  }
+}
diff --git a/src/templates/calendarconfig/source.jsx b/src/templates/calendarconfig/source.jsx
new file mode 100644
index 0000000..3a9328e
--- /dev/null
+++ b/src/templates/calendarconfig/source.jsx
@@ -0,0 +1,139 @@
+import Utils from '@/utils/utils.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+
+const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+
+class CalendarBaseData {
+  baseConfig = {
+    version: '1.0',
+    type: 'system',
+    Template: 'CalendarPage',
+    enabled: false,
+    tabName: '',
+    tabNo: '',
+    Remark: '',
+    uuid: Utils.getuuid(),
+    setting: {
+      tableName: '',
+      primaryKey: '',
+      dataresource: '',
+      interType: 'inner',
+      innerFunc: '',
+      interface: '',
+      outerFunc: ''
+    },
+    columns: [],
+    scripts: [],
+    tables: [],
+    tab: null,
+    calendar: {
+      levels: ['day', 'month', 'year'],
+      startfield: '',
+      endfield: '',
+      colorfield: '',
+      remarkfield: ''
+    },
+    search: [
+      {
+        origin: true,
+        uuid: Utils.getuuid(),
+        label: 'label',
+        field: '',
+        initval: '',
+        type: 'text',
+        resourceType: '0',
+        setAll: 'false',
+        options: [],
+        orderType: 'asc',
+        match: 'like',
+        display: 'dropdown'
+      }, {
+        origin: true,
+        uuid: Utils.getuuid(),
+        label: 'label',
+        field: '',
+        initval: '',
+        type: 'select',
+        resourceType: '0',
+        setAll: 'false',
+        options: [],
+        orderType: 'asc',
+        match: 'equal',
+        display: 'dropdown'
+      }, {
+        origin: true,
+        uuid: Utils.getuuid(),
+        label: 'label',
+        field: '',
+        initval: '',
+        type: 'date',
+        resourceType: '0',
+        setAll: 'false',
+        options: [],
+        orderType: 'asc',
+        match: 'greater',
+        display: 'dropdown'
+      }
+    ]
+  }
+
+  searchItems = [
+    {
+      type: 'search',
+      label: CommonDict['model.form.text'],
+      subType: 'text',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.select'],
+      subType: 'select',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.multiselect'],
+      subType: 'multiselect',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.link'],
+      subType: 'link',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.dateday'],
+      subType: 'date',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.dateweek'],
+      subType: 'dateweek',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.datemonth'],
+      subType: 'datemonth',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.daterange'],
+      subType: 'daterange',
+      url: ''
+    },
+    {
+      type: 'search',
+      label: CommonDict['model.form.dategroup'],
+      subType: 'group',
+      url: ''
+    }
+  ]
+}
+
+export default new CalendarBaseData()
diff --git a/src/templates/calendarconfig/tabcomponent/index.jsx b/src/templates/calendarconfig/tabcomponent/index.jsx
new file mode 100644
index 0000000..0f0a1f9
--- /dev/null
+++ b/src/templates/calendarconfig/tabcomponent/index.jsx
@@ -0,0 +1,122 @@
+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 { getTabForm } from '@/templates/zshare/formconfig'
+import TabForm from './tabform'
+import './index.scss'
+
+const { confirm } = Modal
+
+class SettingComponent extends Component {
+  static propTpyes = {
+    config: PropTypes.any,         // 鏍囩
+    tabviews: PropTypes.array,     // 鏍囩闆�
+    updateConfig: PropTypes.func,
+    setSubConfig: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    menu: null,          // 鑿滃崟淇℃伅
+    formlist: null,      // 琛ㄥ崟淇℃伅
+    visible: false       // 妯℃�佹鎺у埗
+  }
+
+  /**
+   * @description 淇濆瓨椤甸潰閰嶇疆淇℃伅
+   */
+  tabSave = () => {
+    const { config } = this.props
+
+    this.tabRef.handleConfirm().then(res => {
+      this.setState({
+        visible: false
+      })
+      this.props.updateConfig({...config, tab: res})
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) || !is(fromJS(this.props.config.tab), fromJS(nextProps.config.tab))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  /**
+   * @description 娣诲姞鎴栦慨鏀规爣绛�
+   */
+  handleTab = (e) => {
+    e.stopPropagation()
+    const { config } = this.props
+
+    this.setState({
+      visible: true,
+      formlist: getTabForm(config.tab || {}, '', [], '', [], config.Template)
+    })
+  }
+
+  closeTab = (e) => {
+    const { config } = this.props
+    const _this = this
+    e.stopPropagation()
+
+    confirm({
+      content: '纭畾鍒犻櫎鏍囩鍚楋紵',
+      okText: this.state.dict['model.confirm'],
+      cancelText: this.state.dict['model.cancel'],
+      onOk() {
+        _this.props.updateConfig({...config, tab: ''})
+      },
+      onCancel() {}
+    })
+  }
+
+  render() {
+    const { tabviews, config } = this.props
+    const { dict, visible } = this.state
+
+    return (
+      <div className="model-calendar-tab">
+        {config.tab ? <div className="tab-control">
+          <span onDoubleClick={this.props.setSubConfig}>{config.tab.label}</span>
+          <Icon type="edit" onClick={this.handleTab} />
+          <Icon type="close" onClick={this.closeTab} />
+        </div> : <Icon title="娣诲姞鏍囩" type="plus" onClick={this.handleTab} />}
+        {/* 璁剧疆鍏ㄥ眬閰嶇疆鍙婂垪琛ㄦ暟鎹簮 */}
+        <Modal
+          wrapClassName="model-calendar-tab-modal"
+          title={dict['model.edit']}
+          visible={visible}
+          width={900}
+          maskClosable={false}
+          onCancel={() => { this.setState({ visible: false })}}
+          cancelText={this.state.dict['model.cancel']}
+          okText={this.state.dict['model.confirm']}
+          onOk={this.tabSave}
+          destroyOnClose
+        >
+          <TabForm
+            tabs={tabviews}
+            dict={dict}
+            inputSubmit={this.tabSave}
+            formlist={this.state.formlist}
+            wrappedComponentRef={(inst) => this.tabRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default SettingComponent
\ No newline at end of file
diff --git a/src/templates/calendarconfig/tabcomponent/index.scss b/src/templates/calendarconfig/tabcomponent/index.scss
new file mode 100644
index 0000000..821081e
--- /dev/null
+++ b/src/templates/calendarconfig/tabcomponent/index.scss
@@ -0,0 +1,85 @@
+.model-calendar-tab {
+  position: absolute;
+  top: 0;
+  >.anticon-plus {
+    position: absolute;
+    font-size: 18px;
+    left: 10px;
+    top: 10px;
+    color: #26c281;
+    cursor: pointer;
+  }
+  .tab-control {
+    position: absolute;
+    padding: 15px 0px 0px 10px;
+    left: 0px;
+    top: 0px;
+
+    span {
+      display: inline-block;
+      white-space: nowrap;
+      font-size: 16px;
+      cursor: pointer;
+      padding: 0 10px;
+      border-bottom: 2px solid #1890ff;
+    }
+
+    .anticon-edit {
+      position: absolute;
+      font-size: 14px;
+      left: 10px;
+      top: 2px;
+      color: #1890ff;
+      cursor: pointer;
+      display: none;
+    }
+    .anticon-close {
+      position: absolute;
+      font-size: 14px;
+      left: 35px;
+      top: 2px;
+      color: #ff4d4f;
+      cursor: pointer;
+      display: none;
+    }
+  }
+  .tab-control:hover {
+    .anticon-edit {
+      display: inline-block;
+    }
+    .anticon-close {
+      display: inline-block;
+    }
+  }
+}
+
+.model-calendar-tab-modal {
+  .ant-modal {
+    top: 50px;
+    padding-bottom: 5px;
+    .ant-modal-body {
+      max-height: calc(100vh - 190px);
+      overflow-y: auto;
+      // .ant-empty {
+      //   margin: 15vh 8px;
+      // }
+    }
+    .ant-modal-body::-webkit-scrollbar {
+      width: 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-empty-normal {
+      margin: 5px 0px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/calendarconfig/tabcomponent/tabform/index.jsx b/src/templates/calendarconfig/tabcomponent/tabform/index.jsx
new file mode 100644
index 0000000..f751d42
--- /dev/null
+++ b/src/templates/calendarconfig/tabcomponent/tabform/index.jsx
@@ -0,0 +1,278 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Select, Icon, Tooltip, Radio, InputNumber } from 'antd'
+import { formRule } from '@/utils/option.js'
+import Utils from '@/utils/utils.js'
+import './index.scss'
+
+class MainTab extends Component {
+  static propTpyes = {
+    tabs: PropTypes.array,       // 鍙叧鑱旀爣绛鹃泦
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    formlist: PropTypes.any,     // 琛ㄥ崟
+    inputSubmit: PropTypes.any   // 鍥炶溅鎻愪氦浜嬩欢
+  }
+
+  state = {
+    formlist: null // 琛ㄥ崟
+  }
+
+  /**
+   * @description 琛ㄥ崟棰勫鐞�
+   */
+  UNSAFE_componentWillMount () {
+    const { formlist } = this.props
+
+    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
+
+    let _tabs = this.props.tabs.filter(tab => tab.type === type)
+
+    this.setState({
+      formlist: formlist.map(item => {
+        if (item.key === 'linkTab') {
+          item.options = [
+            {
+              value: '',
+              text: '鏂板缓'
+            },
+            ..._tabs
+          ]
+        }
+
+        return item
+      })
+    })
+  }
+
+  /**
+   * @description 鏍囩椤电被鍨嬪垏鎹�
+   */
+  openTypeChange = (key, value) => {
+    const { formlist } = this.state
+
+    if (key === 'type') {
+      let _tabs = this.props.tabs.filter(tab => tab.type === value)
+
+      this.setState({
+        formlist: formlist.map(item => {
+          if (item.key === 'linkTab') {
+            item.options = [
+              {
+                value: '',
+                text: '鏂板缓'
+              },
+              ..._tabs
+            ]
+            item.initVal = ''
+          }
+          return item
+        })
+      }, () => {
+        if (this.props.form.getFieldValue('linkTab') !== undefined) {
+          this.props.form.setFieldsValue({linkTab: ''})
+        }
+      })
+    }
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  getFields() {
+    const { getFieldDecorator } = this.props.form
+    const fields = []
+    
+    this.state.formlist.forEach((item, index) => {
+      if (item.hidden || item.forbid) return
+
+      if (item.type === 'text') {
+        let rules = []
+
+        if (item.key === 'foreignKey') {
+          rules.push({
+            pattern: /^[a-zA-Z_]*$/ig,
+            message: item.label + '瀛楁鍙厑璁稿寘鍚瓧姣嶅強涓嬪垝绾匡紒'
+          })
+        }
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={
+              item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  },
+                  {
+                    max: formRule.input.max,
+                    message: formRule.input.message
+                  },
+                  ...rules
+                ]
+              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'number') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={
+              item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<InputNumber disabled={item.readonly} min={item.min} max={item.max} precision={0} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'select') { // 涓嬫媺鎼滅储
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.label}>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
+              })(
+                <Select
+                  showSearch
+                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  onChange={(value) => {this.openTypeChange(item.key, value)}}
+                >
+                  {item.options.map((option, i) =>
+                    <Select.Option id={'mk' + i} title={option.text} key={'mk' + i} value={option.value}>
+                      {item.key === 'icon' && i !== 0 ? <Icon type={option.text} /> : option.text}
+                    </Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'mutilselect') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={
+              item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal
+              })(
+                <Select
+                  mode="multiple"
+                  style={{ width: '100%' }}
+                  placeholder=""
+                >
+                  {item.options.map((option, index) =>
+                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'radio') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
+              })(
+                <Radio.Group>
+                  {
+                    item.options.map(option => {
+                      return (
+                        <Radio key={option.value} value={option.value}>{option.text}</Radio>
+                      )
+                    })
+                  }
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      }
+    })
+
+    return fields
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+
+          if (!values.linkTab) { // 娌℃湁鍏宠仈鏍囩锛堟柊寤烘椂锛夛紝鍒涘缓鏂版爣绛綢d
+            values.linkTab = Utils.getuuid()
+          }
+
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  render() {
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+    return (
+      <Form {...formItemLayout} className="model-tab-form">
+        <Row gutter={24}>{this.getFields()}</Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(MainTab)
\ No newline at end of file
diff --git a/src/templates/calendarconfig/tabcomponent/tabform/index.scss b/src/templates/calendarconfig/tabcomponent/tabform/index.scss
new file mode 100644
index 0000000..d9327f1
--- /dev/null
+++ b/src/templates/calendarconfig/tabcomponent/tabform/index.scss
@@ -0,0 +1,16 @@
+.model-tab-form {
+  min-height: 180px;
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+  .ant-input-number {
+    width: 100%;
+    .ant-input-number-input:read-only {
+      color: red;
+      :hover {
+        border-color: #d9d9d9;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index 3c18dcf..dbba872 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -541,6 +541,11 @@
 
         _LText = _LText.join(' union all ')
 
+        // 娓呯┖鑿滃崟涓嬪叧鑱旂殑鏍囩
+        if (!_LText) {
+          _LText = `select '${menu.MenuID}' as MenuID ,'' as Tabid,'' as TabName ,'0' as Sort`
+        }
+
         tabParam.LText = Utils.formatOptions(_LText)
         tabParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
         tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
@@ -751,14 +756,12 @@
     new Promise(resolve => {
       let deffers = []
 
-      if (tabParam.LText) {
-        let defer = new Promise(resolve => {
-          Api.getSystemConfig(tabParam).then(result => {
-            resolve(result)
-          })
+      let defer = new Promise(resolve => {
+        Api.getSystemConfig(tabParam).then(result => {
+          resolve(result)
         })
-        deffers.push(defer)
-      }
+      })
+      deffers.push(defer)
 
       if (btnParam.LText) {
         let defer = new Promise(resolve => {
@@ -978,7 +981,7 @@
     const { menu } = this.props
     const { config, originMenu, optionLibs, activeKey, openEdition } = this.state
 
-    if (!originMenu.MenuID) { // menuID涓嶅瓨鍦ㄦ椂锛屼负鏂板缓鑿滃崟锛屾彁绀鸿彍鍗曞皻鏈繚瀛�
+    if (config.isAdd) { // 鏂板缓鑿滃崟锛屾彁绀鸿彍鍗曞皻鏈繚瀛�
       notification.warning({
         top: 92,
         message: this.state.dict['header.menu.config.notsave'],
@@ -1281,12 +1284,10 @@
   /**
    * @description 鏇存柊鏍囩閰嶇疆淇℃伅
    */
-  updatetabs = (config, delcards) => {
-    const { delActions } = this.state
+  updatetabs = (config) => {
 
     this.setState({
-      config: config,
-      delActions: delcards ? [...delActions, ...delcards] : delActions
+      config: config
     })
   }
 
diff --git a/src/templates/menuconfig/editthdmenu/index.jsx b/src/templates/menuconfig/editthdmenu/index.jsx
index 6850d6b..42b66ba 100644
--- a/src/templates/menuconfig/editthdmenu/index.jsx
+++ b/src/templates/menuconfig/editthdmenu/index.jsx
@@ -29,6 +29,7 @@
 
 const ComTableConfig = asyncLoadComponent(() => import('@/templates/comtableconfig'))
 const TreePageConfig = asyncLoadComponent(() => import('@/templates/treepageconfig'))
+const CalendarPageConfig = asyncLoadComponent(() => import('@/templates/calendarconfig'))
 const FormTabConfig = asyncLoadComponent(() => import('@/templates/formtabconfig'))
 const ModalConfig = asyncLoadComponent(() => import('@/templates/modalconfig'))
 const SubTable = asyncLoadComponent(() => import('@/templates/subtableconfig'))
@@ -39,7 +40,8 @@
 
 const illust = { // 妯℃澘鍥剧墖锛岀敤浜庡凡浣跨敤妯℃澘
   CommonTable: mainsubtable,
-  TreePage: treepage
+  TreePage: treepage,
+  CalendarPage: treepage
 }
 
 class EditMenu extends Component {
@@ -78,7 +80,7 @@
     btnTabConfig: null,     // 鎵撳紑鏂版爣绛炬寜閽厤缃�
     handleMVisible: false,  // 娣诲姞鎴栦慨鏀硅彍鍗曟ā鎬佹锛堣鑹叉潈闄愬垎閰嶇瓑锛�
     sysMenu: false,         // 娣诲姞鎴栫紪杈戣彍鍗曪紙瑙掕壊鏉冮檺鍒嗛厤绛夛級
-    optionLibs: [],         // 鑷畾涔変笅鎷夐�夐」搴�
+    optionLibs: null,       // 鑷畾涔変笅鎷夐�夐」搴�
     fstMenuId: null,        // 涓�绾ц彍鍗旾d
     fstMenuList: null       // 涓�绾ц彍鍗曞垪琛�
   }
@@ -854,6 +856,14 @@
             handleView={this.handleView}
           /> : null
         }
+        {this.state.tabview === 'CalendarPage' ?
+          <CalendarPageConfig
+            menu={this.state.editMenu}
+            optionLibs={this.state.optionLibs}
+            reloadmenu={() => {this.props.reload()}}
+            handleView={this.handleView}
+          /> : null
+        }
         {this.state.tabview === 'CommonTable' ?
           <ComTableConfig
             menu={this.state.editMenu}
diff --git a/src/templates/modalconfig/dragelement/card.jsx b/src/templates/modalconfig/dragelement/card.jsx
index 53bd4b8..aaf80ec 100644
--- a/src/templates/modalconfig/dragelement/card.jsx
+++ b/src/templates/modalconfig/dragelement/card.jsx
@@ -74,6 +74,29 @@
     }
   }
 
+  let formItem = null
+  if (card.type === 'text') {
+    formItem = (<Input style={{marginTop: '4px'}} defaultValue={card.initval} />)
+  } else if (card.type === 'number') {
+    formItem = (<InputNumber defaultValue={card.initval} precision={card.decimal} />)
+  } else if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link' || card.type === 'color') {
+    formItem = (<Select defaultValue={selectval}></Select>)
+  } else if (card.type === 'date') {
+    formItem = (<DatePicker defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} />)
+  } else if (card.type === 'datemonth') {
+    formItem = (<MonthPicker defaultValue={card.initval ? moment().subtract(card.initval, 'month') : null} />)
+  } else if (card.type === 'datetime') {
+    formItem = (<DatePicker showTime defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} />)
+  } else if (card.type === 'textarea') {
+    formItem = (<TextArea defaultValue={card.initval} autosize={{ minRows: 2, maxRows: 6 }} />)
+  } else if (card.type === 'fileupload') {
+    formItem = (<Button style={{marginTop: '3px'}}><Icon type="upload" /> 鐐瑰嚮涓婁紶 </Button>)
+  } else if (card.type === 'funcvar') {
+    formItem = (<Input style={{marginTop: '4px'}} defaultValue={card.linkfield} />)
+  } else if (card.type === 'linkMain') {
+    formItem = (<Input style={{marginTop: '4px'}} />)
+  }
+
   return (
     <div className="page-card" style={{ opacity: opacity}}>
       <div ref={node => drag(drop(node))}>
@@ -85,45 +108,11 @@
             <Icon className="edit copy" type="copy" onClick={copy} />
           </div>
           <div className={'ant-col ant-form-item-control-wrapper ant-col-xs-24 ' + wrapCol}>
-            {card.type === 'text' &&
-              <Input style={{marginTop: '4px'}} defaultValue={card.initval} />
-            }
-            {card.type === 'number' &&
-              <InputNumber defaultValue={card.initval} precision={card.decimal} />
-            }
-            {(card.type === 'multiselect' || card.type === 'select' || card.type === 'link') &&
-              <Select defaultValue={selectval}></Select>
-            }
-            {card.type === 'date' &&
-              <DatePicker defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} />
-            }
-            {card.type === 'datemonth' ?
-              <MonthPicker defaultValue={card.initval ? moment().subtract(card.initval, 'month') : null} /> : null
-            }
-            {card.type === 'datetime' &&
-              <DatePicker showTime defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} />
-            }
-            {card.type === 'textarea' &&
-              <TextArea defaultValue={card.initval} autosize={{ minRows: 2, maxRows: 6 }} />
-            }
-            {card.type === 'fileupload' &&
-              <Button style={{marginTop: '3px'}}>
-                <Icon type="upload" /> 鐐瑰嚮涓婁紶
-              </Button>
-            }
-            {card.type === 'funcvar' &&
-              <Input style={{marginTop: '4px'}} defaultValue={card.linkfield} />
-            }
-            {card.type === 'linkMain' &&
-              <Input style={{marginTop: '4px'}} />
-            }
+            {formItem}
             <div className="input-mask"></div>
           </div>
         </div>}
       </div>
-      {/* <Icon className="edit" type="edit" onClick={edit} />
-      <Icon className="edit close" type="close" onClick={close} />
-      <Icon className="edit copy" type="copy" onClick={copy} /> */}
     </div>
   )
 }
diff --git a/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx b/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx
index 85c2419..a30105b 100644
--- a/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx
@@ -125,6 +125,7 @@
         // 瀵煎叆鍜屽鍑篹xcel锛屾寜閽悕绉扮洿鎺ヤ负瀵煎叆銆佸鍑�
         newcard.label = item.label
         newcard.class = 'border-dgreen'
+        newcard.Ot = 'notRequired'
       } else if (item.subType === 'excelOut') {
         newcard.label = item.label
         newcard.intertype = setting.interType
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/columnform/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/columnform/index.jsx
index d262c0b..2cf05f4 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/columnform/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/columnform/index.jsx
@@ -75,7 +75,7 @@
         values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
 
         if (/^Nvarchar/ig.test(values.type)) {
-          values.limit = values.type.match(/\d+/)[0]
+          values.limit = values.type.match(/\d+/) ? values.type.match(/\d+/)[0] : '20000'
         } else if (/^Decimal/ig.test(values.type)) {
           values.limit = values.type.match(/\d+/ig)[1]
         } else {
@@ -154,7 +154,11 @@
                   <Select.Option value="Nvarchar(20)"> Nvarchar(20) </Select.Option>
                   <Select.Option value="Nvarchar(50)"> Nvarchar(50) </Select.Option>
                   <Select.Option value="Nvarchar(100)"> Nvarchar(100) </Select.Option>
+                  <Select.Option value="Nvarchar(256)"> Nvarchar(256) </Select.Option>
                   <Select.Option value="Nvarchar(512)"> Nvarchar(512) </Select.Option>
+                  <Select.Option value="Nvarchar(1024)"> Nvarchar(1024) </Select.Option>
+                  <Select.Option value="Nvarchar(2048)"> Nvarchar(2048) </Select.Option>
+                  <Select.Option value="Nvarchar(max)"> Nvarchar(max) </Select.Option>
                   <Select.Option value="Int"> Int </Select.Option>
                   <Select.Option value="Decimal(18,0)"> Decimal(18,0) </Select.Option>
                   <Select.Option value="Decimal(18,2)"> Decimal(18,2) </Select.Option>
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
index c13f4a2..da33d91 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
@@ -220,7 +220,7 @@
 
   UNSAFE_componentWillMount() {
     const { columns, card } = this.props
-    let _verify = fromJS(card.verify || {}).toJS()
+    let _verify = fromJS(card.verify || {range: 1}).toJS()
     let _columns = _verify.columns || []
 
     // 鍚屾鏄剧ず鍒�
diff --git a/src/templates/sharecomponent/datasourcecomponent/index.jsx b/src/templates/sharecomponent/datasourcecomponent/index.jsx
new file mode 100644
index 0000000..ab3c8f1
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/index.jsx
@@ -0,0 +1,88 @@
+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 VerifyCard from './verifycard'
+import './index.scss'
+
+class DataSource extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    sourcelist: [],
+    visible: false,
+    loading: false,
+    setting: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    this.setState({setting: fromJS(config.setting).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.setState({loading: true})
+    this.verifyRef.submitDataSource().then(res => {
+
+      this.setState({loading: false, visible: false})
+      this.props.updateConfig({...config, ...res})
+    }, () => {
+      this.setState({loading: false})
+    })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict, loading } = this.state
+
+    return (
+      <div className="model-datasource">
+        <Icon type="setting" onClick={() => this.editDataSource()} />
+        <Modal
+          wrapClassName="model-datasource-verify-modal popview-modal"
+          title={'鏁版嵁婧愰厤缃�'}
+          visible={visible}
+          width={'75vw'}
+          maskClosable={false}
+          style={{minWidth: '900px', maxWidth: '1200px'}}
+          okText={dict['model.submit']}
+          cancelText={dict['model.cancel']}
+          onOk={this.verifySubmit}
+          confirmLoading={loading}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <VerifyCard
+            dict={dict}
+            config={config}
+            menuId={this.props.config.uuid}
+            searches={config.search}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/index.scss b/src/templates/sharecomponent/datasourcecomponent/index.scss
new file mode 100644
index 0000000..c994031
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/index.scss
@@ -0,0 +1,94 @@
+.model-datasource {
+  position: absolute;
+  right: 7px;
+  top: 5px;
+  z-index: 1;
+
+  >.anticon-setting {
+    font-size: 18px;
+    padding: 10px;
+  }
+
+  .model-input-group-wrapper {
+    padding: 0 20px;
+    display: inline-block;
+    width: 100%;
+    text-align: start;
+    vertical-align: top;
+    margin-bottom: 15px;
+
+    .model-input-wrapper {
+      position: relative;
+      display: table;
+      width: 100%;
+      border-collapse: separate;
+      border-spacing: 0;
+
+      .model-input-value {
+        display: table-cell;
+        width: 100%;
+        border: 1px solid #d9d9d9;
+        border-radius: 4px 0px 0px 4px;
+        overflow: hidden;
+        text-overflow:ellipsis;
+        white-space: nowrap;
+        padding: 2px 10px;
+        color: #ffffff;
+      }
+
+      .model-input-group-addon {
+        display: table-cell;
+        width: 1px;
+        position: relative;
+        padding: 0 11px;
+        color: rgba(0, 0, 0, 0.65);
+        font-weight: normal;
+        font-size: 14px;
+        line-height: 1;
+        text-align: center;
+        background-color: #fafafa;
+        border: 1px solid #d9d9d9;
+        border-radius: 0px 4px 4px 0px;
+        white-space: nowrap;
+      }
+
+      .model-input-insert {
+        display: table-cell;
+        width: 100%;
+        border: 1px dotted #d9d9d9;
+        border-radius: 4px;
+        text-align: center;
+        cursor: pointer;
+
+        .anticon-plus {
+          padding: 6px;
+          font-size: 16px;
+          color: rgb(38, 194, 129);
+        }
+      }
+    }
+    .anticon-setting {
+      margin-right: 5px;
+      padding: 6px;
+      cursor: pointer;
+    }
+    .anticon-setting:hover {
+      color: #1890ff;
+    }
+    .anticon-close {
+      padding: 6px;
+      cursor: pointer;
+    }
+    .anticon-close:hover {
+      color: #ff4d4f;
+    }
+  }
+}
+.model-datasource-verify-modal {
+  .ant-modal {
+    top: 50px;
+    .ant-modal-body {
+      max-height: calc(100vh - 190px);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.jsx b/src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.jsx
new file mode 100644
index 0000000..c8ef7bb
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.jsx
@@ -0,0 +1,145 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Select, Button, Input } from 'antd'
+import './index.scss'
+
+
+class UniqueForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,       // 瀛楀吀椤�
+    columnChange: PropTypes.func  // 淇敼鍑芥暟
+  }
+
+  state = {
+    editItem: null // 缂栬緫鍏冪礌
+  }
+
+  edit = (record) => {
+    this.setState({
+      editItem: record
+    })
+
+    this.props.form.setFieldsValue({
+      label: record.label,
+      field: record.field,
+      datatype: record.datatype
+    })
+  }
+
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
+
+        this.setState({
+          editItem: null
+        }, () => {
+          this.props.columnChange(values)
+        })
+        this.props.form.setFieldsValue({
+          label: '',
+          field: ''
+        })
+      }
+    })
+  }
+
+  render() {
+    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="verifycard1">
+        <Row gutter={24}>
+          <Col span={7}>
+            <Form.Item label={'鍚嶇О'}>
+              {getFieldDecorator('label', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.input'] + '鍚嶇О!'
+                  }
+                ]
+              })(<Input placeholder="" autoComplete="off" />)}
+            </Form.Item>
+          </Col>
+          <Col span={7}>
+            <Form.Item label={'瀛楁'}>
+              {getFieldDecorator('field', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.input'] + '瀛楁!'
+                  }
+                ]
+              })(<Input placeholder="" autoComplete="off" />)}
+            </Form.Item>
+          </Col>
+          <Col span={7}>
+            <Form.Item label={'鏁版嵁绫诲瀷'}>
+              {getFieldDecorator('datatype', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.select'] + '鏁版嵁绫诲瀷!'
+                  }
+                ]
+              })(
+                <Select>
+                  <Select.Option value="Nvarchar(50)"> Nvarchar(50) </Select.Option>
+                  <Select.Option value="Nvarchar(100)"> Nvarchar(100) </Select.Option>
+                  <Select.Option value="Nvarchar(512)"> Nvarchar(512) </Select.Option>
+                  <Select.Option value="Nvarchar(1024)"> Nvarchar(1024) </Select.Option>
+                  <Select.Option value="Nvarchar(2048)"> Nvarchar(2048) </Select.Option>
+                  <Select.Option value="Nvarchar(max)"> Nvarchar(max) </Select.Option>
+                  <Select.Option value="Int"> Int </Select.Option>
+                  <Select.Option value="Decimal(18,0)"> Decimal(18,0) </Select.Option>
+                  <Select.Option value="Decimal(18,1)"> Decimal(18,1) </Select.Option>
+                  <Select.Option value="Decimal(18,2)"> Decimal(18,2) </Select.Option>
+                  <Select.Option value="Decimal(18,3)"> Decimal(18,3) </Select.Option>
+                  <Select.Option value="Decimal(18,4)"> Decimal(18,4) </Select.Option>
+                  <Select.Option value="Decimal(18,5)"> Decimal(18,5) </Select.Option>
+                  <Select.Option value="Decimal(18,6)"> Decimal(18,6) </Select.Option>
+                  <Select.Option value="Decimal(18,7)"> Decimal(18,7) </Select.Option>
+                  <Select.Option value="Decimal(18,8)"> Decimal(18,8) </Select.Option>
+                  <Select.Option value="Decimal(18,9)"> Decimal(18,9) </Select.Option>
+                  <Select.Option value="Decimal(18,10)"> Decimal(18,10) </Select.Option>
+                  <Select.Option value="Decimal(18,11)"> Decimal(18,11) </Select.Option>
+                  <Select.Option value="Decimal(18,12)"> Decimal(18,12) </Select.Option>
+                  <Select.Option value="Decimal(18,13)"> Decimal(18,13) </Select.Option>
+                  <Select.Option value="Decimal(18,14)"> Decimal(18,14) </Select.Option>
+                  <Select.Option value="Decimal(18,15)"> Decimal(18,15) </Select.Option>
+                  <Select.Option value="Decimal(18,16)"> Decimal(18,16) </Select.Option>
+                  <Select.Option value="Decimal(18,17)"> Decimal(18,17) </Select.Option>
+                  <Select.Option value="Decimal(18,18)"> Decimal(18,18) </Select.Option>
+                  {/* <Select.Option value="date"> date </Select.Option> */}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={3} className="add">
+            <Button onClick={this.handleConfirm} type="primary" className="mk-green">
+              淇濆瓨
+            </Button>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(UniqueForm)
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.scss b/src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/columnform/index.scss
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.jsx b/src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.jsx
new file mode 100644
index 0000000..7b822fd
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.jsx
@@ -0,0 +1,231 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Button, notification, Select } from 'antd'
+
+import Utils from '@/utils/utils.js'
+import CodeMirror from '@/templates/zshare/codemirror'
+import './index.scss'
+
+class CustomForm extends Component {
+  static propTpyes = {
+    type: PropTypes.string,         // 鑿滃崟绫诲瀷
+    dict: PropTypes.object,         // 瀛楀吀椤�
+    setting: PropTypes.object,      // 璁剧疆
+    searches: PropTypes.array,      // 鎼滅储鏉′欢
+    swhere: PropTypes.string,       // where鏉′欢
+    arr_field: PropTypes.string,    // 鍒楀瓧娈�
+    regoptions: PropTypes.array,    // 姝e垯鏇挎崲
+    systemScripts: PropTypes.array, // 绯荤粺鑴氭湰
+    scriptSubmit: PropTypes.func,   // 鑴氭湰楠岃瘉鍚庢彁浜�
+    scriptsChange: PropTypes.func   // 鑴氭湰楠岃瘉
+  }
+
+  state = {
+    editItem: null,
+    loading: false,
+    usefulFields: ''
+  }
+
+  UNSAFE_componentWillMount() {
+    const { searches } = this.props
+
+    let _usefulFields = []
+    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)
+      }
+    })
+
+    this.setState({
+      usefulFields: _usefulFields.join(', ')
+    })
+  }
+
+  edit = (record) => {
+    this.setState({
+      editItem: record
+    })
+
+    this.props.form.setFieldsValue({
+      sql: record.sql
+    })
+  }
+
+  handleCancel = () => {
+    this.setState({
+      editItem: null
+    })
+    this.props.form.setFieldsValue({
+      sql: ''
+    })
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        values.uuid = this.state.editItem ? this.state.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
+        }
+
+        this.setState({loading: true})
+        this.props.scriptsChange(values).then(() => {
+          this.setState({
+            editItem: null,
+            loading: false
+          })
+          this.props.form.setFieldsValue({
+            sql: ''
+          })
+          this.props.scriptSubmit(values)
+        })
+      }
+    })
+  }
+
+  selectScript = (value, option) => {
+    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 { systemScripts, setting } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { usefulFields } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="modal-menu-setting-script">
+        <Row gutter={24}>
+          {setting.tableName ? <Col span={8}>
+            <Form.Item label={'琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+              {setting.tableName}
+            </Form.Item>
+          </Col> : null}
+          <Col span={16}>
+            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
+              ErrorCode, retmsg
+            </Form.Item>
+          </Col>
+          <Col span={24} className="sqlfield">
+            <Form.Item label={'鍙敤瀛楁'}>
+              id, bid, loginuid, sessionuid, userid, appkey, time_id{usefulFields ? ', ' + usefulFields : ''}
+            </Form.Item>
+          </Col>
+          <Col span={10}>
+            <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
+              <Select
+                showSearch
+                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                onChange={this.selectScript}
+              >
+                {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: '',
+                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/sharecomponent/datasourcecomponent/verifycard/customscript/index.scss b/src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.scss
new file mode 100644
index 0000000..2a1d2d8
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.scss
@@ -0,0 +1,34 @@
+.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%;
+    }
+  }
+  .sql {
+    .ant-col-sm-8 {
+      width: 10.5%;
+    }
+    .ant-col-sm-16 {
+      width: 89.5%;
+      padding-top: 4px;
+    }
+    .CodeMirror {
+      height: 350px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/index.jsx b/src/templates/sharecomponent/datasourcecomponent/verifycard/index.jsx
new file mode 100644
index 0000000..2ef6c65
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/index.jsx
@@ -0,0 +1,524 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Form, Tabs, Table, Popconfirm, Icon, notification, Modal, Typography, Spin } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+
+import ColForm from './columnform'
+import CustomScriptsForm from './customscript'
+import SettingForm from './settingform'
+import SettingUtils from './utils'
+import './index.scss'
+
+const { TabPane } = Tabs
+const { Paragraph } = Typography
+
+class VerifyCard extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,     // 瀛楀吀椤�
+    config: PropTypes.object,   // 鏁版嵁婧愪俊鎭�
+    menuId: PropTypes.string,   // 鑿滃崟Id
+    searches: PropTypes.array,  // 鎼滅储鏉′欢
+  }
+
+  state = {
+    columns: [],
+    activeKey: 'setting',
+    loading: false,
+    initsql: '',          // sql楠岃瘉鏃跺彉閲忓0鏄庡強璧嬪��
+    usefulfields: '',
+    defaultsql: '',         // 榛樿Sql
+    systemScripts: [{
+      name: '榛樿sql',
+      value: ''
+    }],
+    colColumns: [
+      {
+        title: '鍚嶇О',
+        dataIndex: 'label',
+        width: '25%'
+      },
+      {
+        title: '瀛楁',
+        dataIndex: 'field',
+        width: '25%'
+      },
+      {
+        title: '鏁版嵁绫诲瀷',
+        dataIndex: 'datatype',
+        width: '25%',
+      },
+      {
+        title: '鎿嶄綔',
+        align: 'center',
+        width: '25%',
+        dataIndex: 'operation',
+        render: (text, record) =>
+          (<div>
+            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record, 'columns')} style={{color: '#1890ff'}}><Icon type="edit" /></span>
+            <Popconfirm
+              title={this.props.dict['model.query.delete']}
+              okText={this.props.dict['model.confirm']}
+              cancelText={this.props.dict['model.cancel']}
+              onConfirm={() => this.deleteColumn(record)
+            }>
+              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ],
+    scriptsColumns: [
+      {
+        title: 'SQL',
+        dataIndex: 'sql',
+        width: '60%',
+        render: (text) => (
+          <Paragraph copyable ellipsis={{ rows: 5, expandable: true }}>{text}</Paragraph>
+        )
+      },
+      {
+        title: '鐘舵��',
+        dataIndex: 'status',
+        width: '20%',
+        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>
+            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record, 'scripts')} style={{color: '#1890ff'}}><Icon type="edit" /></span>
+            <span className="operation-btn" onClick={() => this.handleUpDown(record, 'up')} style={{color: '#1890ff'}}><Icon type="arrow-up" /></span>
+            <span className="operation-btn" onClick={() => this.handleUpDown(record, 'down')} style={{color: '#ff4d4f'}}><Icon type="arrow-down" /></span>
+            <span className="operation-btn" title={this.props.dict['model.status.change']} onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><Icon type="swap" /></span>
+            <Popconfirm
+              title={this.props.dict['model.query.delete']}
+              okText={this.props.dict['model.confirm']}
+              cancelText={this.props.dict['model.cancel']}
+              onConfirm={() => this.deleteScript(record)
+            }>
+              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount() {
+    const { config } = this.props
+
+    this.setState({
+      columns: fromJS(config.columns).toJS(),
+      setting: fromJS(config.setting).toJS(),
+      scripts: fromJS(config.scripts).toJS()
+    })
+
+    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') + '.000'
+    _sParam.secretkey = Utils.encrypt(_sParam.LText, _sParam.timestamp)
+
+    _sParam.open_key = Utils.encrypt(_sParam.secretkey, _sParam.timestamp, true) // 浜戠鏁版嵁楠岃瘉
+    
+    Api.getSystemConfig(_sParam).then(res => {
+      if (res.status) {
+        let _scripts = []
+
+        res.data.forEach(item => {
+          let _item = {
+            name: item.funcname,
+            value: Utils.formatOptions(item.longparam, true)
+          }
+
+          _scripts.push(_item)
+        })
+
+        this.setState({
+          systemScripts: [...this.state.systemScripts, ..._scripts]
+        })
+      } else {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  columnChange = (values) => {
+    let columns = fromJS(this.state.columns).toJS()
+
+    if (values.uuid) {
+      columns = columns.map(item => {
+        if (item.uuid === values.uuid) {
+          return values
+        } else {
+          return item
+        }
+      })
+    } else {
+      values.uuid = Utils.getuuid()
+      columns.push(values)
+    }
+
+    this.setState({ columns })
+  }
+
+  deleteColumn = (record) => {
+    this.setState({ columns: this.state.columns.filter(item => item.uuid !== record.uuid) })
+  }
+
+  deleteScript = (record) => {
+    this.setState({ scripts: this.state.scripts.filter(item => item.uuid !== record.uuid) })
+  }
+
+  handleEdit = (record, type) => {
+    if (type === 'scripts') {
+      this.scriptsForm.edit(record)
+    } else if (type === 'columns') {
+      this.contrastForm.edit(record)
+    }
+
+    let node = document.getElementById('model-verify-card-box-tab').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)
+    }
+  }
+
+  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 })
+  }
+
+  handleUpDown = (record, direction) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+    let index = 0
+
+    scripts = scripts.filter((item, i) => {
+      if (item.uuid === record.uuid) {
+        index = i
+      }
+
+      return item.uuid !== record.uuid
+    })
+    if ((index === 0 && direction === 'up') || (index === scripts.length && direction === 'down')) {
+      return
+    }
+
+    if (direction === 'up') {
+      scripts.splice(index - 1, 0, record)
+    } else {
+      scripts.splice(index + 1, 0, record)
+    }
+
+    this.setState({ scripts })
+  }
+
+  scriptsChange = (values) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+
+    if (values.uuid) {
+      scripts = scripts.map(item => {
+        if (item.uuid === values.uuid) {
+          return values
+        } else {
+          return item
+        }
+      })
+    } else {
+      scripts.push(values)
+    }
+
+    return new Promise((resolve, reject) => {
+      this.sqlverify(resolve, reject, false, scripts)
+    })
+  }
+
+  scriptSubmit = (values) => {
+    let scripts = fromJS(this.state.scripts).toJS()
+
+    if (values.uuid) {
+      scripts = scripts.map(item => {
+        if (item.uuid === values.uuid) {
+          return values
+        } else {
+          return item
+        }
+      })
+    } else {
+      values.uuid = Utils.getuuid()
+      scripts.push(values)
+    }
+
+    this.setState({ scripts })
+  }
+
+  changeTab = (val) => {
+    const { activeKey } = this.state
+
+    this.setState({loading: true})
+    if (activeKey === 'setting') {
+      this.settingForm.handleConfirm().then(res => {
+        this.setState({
+          setting: res
+        }, () => {
+          this.sqlverify(() => { // 楠岃瘉鎴愬姛
+            this.setState({
+              activeKey: val,
+              loading: false
+            })
+          }, () => {             // 楠岃瘉澶辫触
+            this.setState({
+              loading: false
+            })
+          }, true)
+        })
+      }, () => {
+        this.setState({loading: false})
+      })
+    } else if (activeKey === 'columns') {
+      this.sqlverify(() => { // 楠岃瘉鎴愬姛
+        this.setState({
+          activeKey: val,
+          loading: false
+        })
+      }, () => {             // 楠岃瘉澶辫触
+        this.setState({
+          loading: false
+        })
+      }, true)
+    } 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
+        })
+        this.setState({
+          loading: false
+        })
+        return
+      }
+
+      this.sqlverify(() => { // 楠岃瘉鎴愬姛
+        this.setState({
+          activeKey: val,
+          loading: false
+        })
+      }, () => {             // 楠岃瘉澶辫触
+        this.setState({
+          loading: false
+        })
+      }, true)
+    }
+  }
+
+  submitDataSource = () => {
+    const { activeKey, setting, columns, scripts } = this.state
+
+    return new Promise((resolve, reject) => {
+      if (activeKey === 'setting') {
+        this.settingForm.handleConfirm().then(res => {
+          this.setState({
+            setting: res
+          }, () => {
+            this.sqlverify(() => { resolve({setting: res, columns, scripts }) }, reject, false)
+          })
+        }, () => {
+          reject()
+        })
+      } else if (activeKey === 'columns') {
+        this.sqlverify(() => { resolve({setting, columns, scripts }) }, reject, false)
+      } 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
+          })
+          reject()
+          return
+        }
+
+        this.sqlverify(() => { resolve({setting, columns, scripts }) }, reject, false)
+      }
+    })
+  }
+
+  sqlverify = (resolve, reject, change = false, testScripts) => {
+    const { searches } = this.props
+    const { columns, setting, scripts } = this.state
+
+    let _scripts = scripts.filter(item => item.status !== 'false')
+
+    if (testScripts) {
+      _scripts = testScripts.filter(item => item.status !== 'false')
+    }
+    if (!change && setting.interType === 'inner' && !setting.innerFunc && setting.execute === 'false' && _scripts.length === 0) {
+      notification.warning({
+        top: 92,
+        message: '涓嶆墽琛岄粯璁ql鏃讹紝璇锋坊鍔犺嚜瀹氫箟鑴氭湰锛�',
+        duration: 5
+      })
+      reject()
+      return
+    }
+
+    if ((setting.interType === 'inner' && !setting.innerFunc && setting.execute !== 'false') || _scripts.length > 0) {
+      let param = {
+        func: 's_debug_sql',
+        LText: SettingUtils.getDebugSql(setting, _scripts, columns, searches)
+      }
+      param.LText = Utils.formatOptions(param.LText)
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+      param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+      
+      Api.getLocalConfig(param).then(result => {
+        if (result.status) {
+          resolve()
+        } else {
+          reject()
+          Modal.error({
+            title: result.message
+          })
+        }
+      })
+    } else {
+      resolve()
+    }
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  render() {
+    const { columns, setting, scripts, colColumns, scriptsColumns, activeKey, loading } = this.state
+
+    return (
+      <div id="model-verify-card-box-tab">
+        {loading && <Spin size="large" />}
+        <Tabs activeKey={activeKey} className="verify-card-box" onChange={this.changeTab}>
+          <TabPane tab="鏁版嵁婧�" key="setting">
+            <SettingForm
+              menuId={this.props.menuId}
+              dict={this.props.dict}
+              columns={columns}
+              setting={setting}
+              scripts={scripts}
+              wrappedComponentRef={(inst) => this.settingForm = inst}
+            />
+          </TabPane>
+          <TabPane tab="瀛楁闆�" key="columns">
+            <ColForm
+              dict={this.props.dict}
+              columnChange={this.columnChange}
+              wrappedComponentRef={(inst) => this.contrastForm = inst}
+            />
+            <Table
+              bordered
+              rowKey="uuid"
+              className="custom-table"
+              dataSource={columns}
+              columns={colColumns}
+              pagination={false}
+            />
+          </TabPane>
+          <TabPane tab="鑷畾涔夎剼鏈�" key="scripts">
+            <CustomScriptsForm
+              setting={setting}
+              searches={this.props.searches}
+              initsql={this.state.initsql}
+              dict={this.props.dict}
+              customScripts={scripts}
+              systemScripts={this.state.systemScripts}
+              scriptsChange={this.scriptsChange}
+              scriptSubmit={this.scriptSubmit}
+              wrappedComponentRef={(inst) => this.scriptsForm = inst}
+            />
+            <Table
+              bordered
+              rowKey="uuid"
+              className="custom-table"
+              dataSource={scripts}
+              columns={scriptsColumns}
+              pagination={false}
+            />
+          </TabPane>
+        </Tabs>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(VerifyCard)
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/index.scss b/src/templates/sharecomponent/datasourcecomponent/verifycard/index.scss
new file mode 100644
index 0000000..90df82e
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/index.scss
@@ -0,0 +1,74 @@
+#model-verify-card-box-tab {
+  .ant-spin {
+    position: absolute;
+    left: calc(50% - 16px);
+    top: 220px;
+    z-index: 1;
+  }
+  .verify-card-box {
+    .ant-tabs-nav-scroll {
+      text-align: center;
+    }
+    .ant-tabs-content {
+      min-height: 40vh;
+    }
+    table tr td {
+      word-wrap: break-word;
+      word-break: break-word;
+    }
+    .verify-form {
+      .ant-input-number {
+        width: 100%;
+      }
+      .sql {
+        .ant-col-sm-8 {
+          width: 10.5%;
+        }
+        .ant-col-sm-16 {
+          width: 89.5%;
+          padding-top: 4px;
+        }
+      }
+      .sqlfield {
+        .ant-form-item {
+          margin-bottom: 5px;
+        }
+        .ant-form-item-control {
+          line-height: 24px;
+        }
+        .ant-form-item-label {
+          line-height: 25px;
+        }
+        .ant-form-item-children {
+          line-height: 22px;
+        }
+        .ant-col-sm-8 {
+          width: 10.5%;
+        }
+        .ant-col-sm-16 {
+          width: 89.5%;
+        }
+      }
+      .add {
+        padding-top: 4px;
+      }
+      .anticon-question-circle {
+        color: #c49f47;
+        margin-right: 3px;
+      }
+    }
+    .custom-table .ant-empty {
+      margin: 20px 8px!important;
+    }
+    .errorval {
+      display: inline-block;
+      width: 30px;
+    }
+    .operation-btn {
+      display: inline-block;
+      font-size: 16px;
+      padding: 0 5px;
+      cursor: pointer;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.jsx b/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.jsx
new file mode 100644
index 0000000..11e4b7f
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.jsx
@@ -0,0 +1,278 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Select, Tooltip, Icon, notification } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import CodeMirror from '@/templates/zshare/codemirror'
+import './index.scss'
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    menuId: PropTypes.string,    // 鑿滃崟Id
+    setting: PropTypes.object,   // 鏁版嵁婧愰厤缃�
+    columns: PropTypes.array,    // 鍒楄缃�
+    scripts: PropTypes.array,    // 鑷畾涔夎剼鏈�
+  }
+
+  state = {
+    interType: this.props.setting.interType || 'inner',
+  }
+
+  handleConfirm = () => {
+    const { setting } = this.props
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          // 鏁版嵁婧愬墠绔獙璇�
+          if (values.interType === 'inner' && !values.innerFunc && values.execute !== 'false' && !values.dataresource) {
+            notification.warning({
+              top: 92,
+              message: '璇峰~鍐欏唴閮ㄥ嚱鏁版垨鏁版嵁婧愶紒',
+              duration: 5
+            })
+            reject()
+            return
+          } else if (values.interType === 'inner' && !values.innerFunc && values.execute !== 'false' && values.dataresource) {
+            let _quot = values.dataresource.match(/'{1}/g)
+            let _lparen = values.dataresource.match(/\({1}/g)
+            let _rparen = values.dataresource.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: '鏁版嵁婧愪腑\'蹇呴』鎴愬鍑虹幇',
+                duration: 5
+              })
+              reject()
+              return
+            } else if (_lparen !== _rparen) {
+              notification.warning({
+                top: 92,
+                message: '鏁版嵁婧愪腑()蹇呴』鎴愬鍑虹幇',
+                duration: 5
+              })
+              reject()
+              return
+            } else if (/--/ig.test(values.dataresource)) {
+              notification.warning({
+                top: 92,
+                message: '鏁版嵁婧愪腑锛屼笉鍙嚭鐜板瓧绗� -- 锛屾敞閲婅鐢� /*鍐呭*/',
+                duration: 5
+              })
+              reject()
+              return
+            }
+
+            let error = Utils.verifySql(values.dataresource)
+
+            if (error) {
+              notification.warning({
+                top: 92,
+                message: '鏁版嵁婧愪腑涓嶅彲浣跨敤' + error,
+                duration: 5
+              })
+              reject()
+              return
+            }
+          }
+
+          // 鏁版嵁婧愪繚瀛�
+          if (
+            values.interType === 'inner' && !values.innerFunc && values.execute !== 'false' &&
+            /[^\s]+\s+[^\s]+/ig.test(values.dataresource) && setting.dataresource !== values.dataresource
+          ) {
+            let param = {
+              func: 's_DataSrc_Save',
+              LText: values.dataresource,
+              MenuID: this.props.menuId
+            }
+    
+            param.LText = Utils.formatOptions(param.LText)
+            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+            param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+    
+            Api.getLocalConfig(param)
+          }
+
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  onRadioChange = (e, key) => {
+    let value = e.target.value
+
+    if (key === 'interType') {
+      this.setState({
+        interType: value
+      })
+    }
+  }
+
+  render() {
+    const { columns, setting } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { interType } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="model-datasource-setting-form-box">
+        <Form {...formItemLayout} className="model-setting-form">
+          <Row gutter={24}>
+            <Col span={8}>
+              <Form.Item label="琛ㄥ悕">
+                {getFieldDecorator('tableName', {
+                  initialValue: setting.tableName,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '琛ㄥ悕!'
+                    },
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col>
+            <Col span={8}>
+              <Form.Item label="鎺ュ彛绫诲瀷">
+                {getFieldDecorator('interType', {
+                  initialValue: interType,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.select'] + '鎺ュ彛绫诲瀷!'
+                    },
+                  ]
+                })(
+                <Radio.Group onChange={(e) => {this.onRadioChange(e, 'interType')}}>
+                  <Radio value="inner">鍐呴儴</Radio>
+                  <Radio value="outer">澶栭儴</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+            {interType === 'inner' ? <Col span={8}>
+              <Form.Item label="鍐呴儴鍑芥暟">
+                {getFieldDecorator('innerFunc', {
+                  initialValue: setting.innerFunc || '',
+                  rules: [
+                    
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'outer' ? <Col span={8}>
+              <Form.Item label="鎺ュ彛鍦板潃">
+                {getFieldDecorator('interface', {
+                  initialValue: setting.interface || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '鎺ュ彛鍦板潃!'
+                    },
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'outer' ? <Col span={8}>
+              <Form.Item label="澶栭儴鍑芥暟">
+                {getFieldDecorator('outerFunc', {
+                  initialValue: setting.outerFunc || '',
+                  rules: [
+
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'inner' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
+              <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 2 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 22 }} } label={
+                <Tooltip placement="topLeft" title={'浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩�傛敞锛氭暟鎹潈闄愭浛鎹㈢ $@ -> /* 鎴� \'\'銆� @$ -> */ 鎴� \'\''}>
+                  <Icon type="question-circle" />
+                  鏁版嵁婧�
+                </Tooltip>
+              }>
+                {getFieldDecorator('dataresource', {
+                  initialValue: setting.dataresource || ''
+                })(<CodeMirror />)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'inner' ? <Col span={8}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title={'鏌ヨ鏃讹紝鎼滅储鏉′欢浠here鏉′欢鎷兼帴杩涘叆sql锛岀粺璁℃椂锛屽皢鏁版嵁婧愪腑浠モ�淍+鎼滅储瀛楁+@鈥濈殑鍐呭锛屼互鎼滅储鏉′欢涓殑鍊艰繘琛屾浛鎹㈠悗锛屾彁浜ゆ煡璇紝娉細鏌ヨ绫诲瀷浠呭湪浣跨敤绯荤粺鍑芥暟鏃舵湁鏁堛��'}>
+                  <Icon type="question-circle" />
+                  鏌ヨ绫诲瀷
+                </Tooltip>
+              }>
+                {getFieldDecorator('queryType', {
+                  initialValue: setting.queryType || 'query'
+                })(
+                <Radio.Group>
+                  <Radio value="query">鏌ヨ</Radio>
+                  <Radio value="statistics">缁熻</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            <Col span={8}>
+              <Form.Item label="涓婚敭">
+                {getFieldDecorator('primaryKey', {
+                  initialValue: setting.primaryKey || ''
+                })(
+                  <Select onChange={(value) => {this.selectChange('primaryKey', value)}}>
+                    {columns.map((option, i) =>
+                      <Select.Option key={i} value={option.value}>
+                        {option.text}
+                      </Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col>
+            {interType === 'inner' ? <Col span={8}>
+              <Form.Item label="榛樿sql">
+                {getFieldDecorator('execute', {
+                  initialValue: setting.execute || 'true'
+                })(
+                <Radio.Group>
+                  <Radio value="true">鎵ц</Radio>
+                  <Radio value="false">涓嶆墽琛�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            <Col span={8}>
+              <Form.Item label="鍒濆鍖�">
+                {getFieldDecorator('onload', {
+                  initialValue: setting.onload || 'true'
+                })(
+                <Radio.Group>
+                  <Radio value="true">鍔犺浇鏁版嵁</Radio>
+                  <Radio value="false">涓嶅姞杞芥暟鎹�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.scss b/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.scss
new file mode 100644
index 0000000..fcd8fff
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.scss
@@ -0,0 +1,22 @@
+.model-datasource-setting-form-box {
+  position: relative;
+
+  .model-setting-form {
+    .data-source {
+      .ant-form-item-label {
+        width: 11%;
+      }
+      .ant-form-item-control-wrapper {
+        width: 89%;
+      }
+      .CodeMirror {
+        height: 150px;
+      }
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/utils.jsx b/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/utils.jsx
new file mode 100644
index 0000000..ebd508d
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/utils.jsx
@@ -0,0 +1,84 @@
+
+export default class SettingUtils {
+  /**
+   * @description 鐢熸垚椤甸潰鏌ヨ璇彞
+   * @return {String}  arr_field     鏄剧ず鍒楀瓧娈�
+   * @return {String}  search        鎼滅储鏉′欢
+   * @return {Object}  setting       椤甸潰璁剧疆
+   * @return {Array}   regoptions    鎼滅储鏉′欢姝e垯鏇挎崲
+   */
+  static getDebugSql (setting, arr_field, regoptions, search) {
+    let sql = ''
+    let _dataresource = setting.dataresource
+    let _customScript = setting.customScript
+
+    if (setting.interType === 'inner' && !setting.innerFunc && setting.default === 'false') {
+      _dataresource = ''
+    }
+    
+    if (_dataresource) {
+      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
+    }
+    if (_customScript) {
+      _customScript = _customScript.replace(/@\$|\$@/ig, '')
+    }
+    
+    // 姝e垯鏇挎崲
+    let _regoptions = regoptions.map(item => {
+      return {
+        reg: new RegExp('@' + item.key + '@', 'ig'),
+        value: `'${item.value}'`
+      }
+    })
+    let _search = search
+
+    if (setting.queryType === 'statistics' && _dataresource) {
+      _regoptions.forEach(item => {
+        _dataresource = _dataresource.replace(item.reg, item.value)
+      })
+
+      _search = ''
+    }
+
+    if (_customScript) {
+      _regoptions.push({
+        reg: new RegExp('@orderBy@', 'ig'),
+        value: setting.order
+      })
+      if (setting.laypage !== 'false') {
+        _regoptions.push({
+          reg: new RegExp('@pageSize@', 'ig'),
+          value: 10
+        }, {
+          reg: new RegExp('@pageIndex@', 'ig'),
+          value: 1
+        })
+      }
+      _regoptions.forEach(item => {
+        _customScript = _customScript.replace(item.reg, item.value)
+      })
+    }
+
+    // 鏁版嵁婧愬鐞�, 瀛樺湪鏄剧ず鍒楁椂 
+    if (arr_field && _dataresource) {
+      if (/\s/.test(_dataresource)) {
+        _dataresource = '(' + _dataresource + ') tb'
+      }
+
+      _dataresource = `select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
+    }
+
+    if (_customScript) {
+      sql = `${_customScript}
+        ${_dataresource}
+        aaa:
+        if @ErrorCode!=''
+          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+      `
+    } else {
+      sql = _dataresource
+    }
+    
+    return sql
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/datasourcecomponent/verifycard/utils.jsx b/src/templates/sharecomponent/datasourcecomponent/verifycard/utils.jsx
new file mode 100644
index 0000000..483ccf1
--- /dev/null
+++ b/src/templates/sharecomponent/datasourcecomponent/verifycard/utils.jsx
@@ -0,0 +1,99 @@
+
+export default class SettingUtils {
+  /**
+   * @description 鐢熸垚椤甸潰鏌ヨ璇彞
+   * @return {String}  scripts       鑷畾涔夎剼鏈�
+   * @return {String}  searches      鎼滅储鏉′欢
+   * @return {Object}  setting       椤甸潰璁剧疆
+   * @return {Array}   columns       鏄剧ず瀛楁
+   */
+  static getDebugSql (setting, scripts, columns, searches) {
+    let sql = ''
+    let _dataresource = ''
+    let _customScript = ''
+    let arr_field = columns.map(item => item.field).join(',')
+
+    if (scripts.length > 0) {
+      scripts.forEach(item => {
+        _customScript += `
+          ${item.sql}
+        `
+      })
+    }
+
+    if (_customScript) {
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
+        ${_customScript}
+      `
+    }
+
+    if (setting.interType === 'inner' && !setting.innerFunc && setting.execute !== 'false') {
+      _dataresource = setting.dataresource
+    }
+    
+    if (_dataresource) {
+      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
+    }
+    if (_customScript) {
+      _customScript = _customScript.replace(/@\$|\$@/ig, '')
+    }
+    
+    // 姝e垯鏇挎崲
+    let _regoptions = searches.map(item => {
+      return {
+        reg: new RegExp('@' + item.key + '@', 'ig'),
+        value: `'${item.value}'`
+      }
+    })
+    let _search = ''
+
+    if (setting.queryType === 'statistics' && _dataresource) {
+      _regoptions.forEach(item => {
+        _dataresource = _dataresource.replace(item.reg, item.value)
+      })
+
+      _search = ''
+    }
+
+    if (_customScript) {
+      _regoptions.push({
+        reg: new RegExp('@orderBy@', 'ig'),
+        value: setting.order
+      })
+      if (setting.laypage !== 'false') {
+        _regoptions.push({
+          reg: new RegExp('@pageSize@', 'ig'),
+          value: 10
+        }, {
+          reg: new RegExp('@pageIndex@', 'ig'),
+          value: 1
+        })
+      }
+      _regoptions.forEach(item => {
+        _customScript = _customScript.replace(item.reg, item.value)
+      })
+    }
+
+    // 鏁版嵁婧愬鐞�, 瀛樺湪鏄剧ず鍒楁椂 
+    if (arr_field && _dataresource) {
+      if (/\s/.test(_dataresource)) {
+        _dataresource = '(' + _dataresource + ') tb'
+      }
+
+      _dataresource = `select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
+    }
+
+    if (_customScript) {
+      sql = `${_customScript}
+        ${_dataresource}
+        aaa:
+        if @ErrorCode!=''
+          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+      `
+    } else {
+      sql = _dataresource
+    }
+    
+    return sql
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/tabscomponent/index.jsx b/src/templates/sharecomponent/tabscomponent/index.jsx
index 901394a..1f0b759 100644
--- a/src/templates/sharecomponent/tabscomponent/index.jsx
+++ b/src/templates/sharecomponent/tabscomponent/index.jsx
@@ -250,7 +250,7 @@
         _this.setState({
           tabgroups: tabgroups
         }, () => {
-          _this.props.updatetabs({...config, tabgroups: tabgroups}, [card])
+          _this.props.updatetabs({...config, tabgroups: tabgroups})
         })
       },
       onCancel() {}
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index 0f3440f..db38fbd 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -2505,7 +2505,8 @@
       }, {
         value: 'line-chart',
         text: 'line-chart'
-      }]
+      }],
+      forbid: type === 'CalendarPage',
     },
     {
       type: 'select',
@@ -2513,7 +2514,8 @@
       label: Formdict['header.form.supTab'],
       initVal: supMenu,
       required: false,
-      options: menus
+      options: menus,
+      forbid: type === 'CalendarPage',
     },
     {
       type: 'mutilselect',
@@ -2522,7 +2524,8 @@
       tooltip: '濡傛灉瀛愭爣绛句腑鍚湁鍒锋柊鍚岀骇鏍囩鐨勬寜閽紝鍦ㄦ澶勬坊鍔犻渶瑕佸埛鏂扮殑鏍囩銆�',
       initVal: equalTab,
       required: false,
-      options: equalTabs
+      options: equalTabs,
+      forbid: type === 'CalendarPage',
     },
     {
       type: 'text',
@@ -2550,7 +2553,7 @@
       initVal: card.searchPass || 'false',
       tooltip: '浣跨敤涓昏〃鎼滅储鏉′欢鏃讹紝涓昏〃鐨勬悳绱㈡潯浠朵細浼犲叆瀛愯〃涓��',
       required: false,
-      forbid: type !== 'CommonTable',
+      forbid: type !== 'CommonTable' && type !== 'CalendarPage',
       options: [{
         value: 'true',
         text: '浣跨敤'
diff --git a/src/templates/zshare/modalform/index.jsx b/src/templates/zshare/modalform/index.jsx
index ea264fd..fe6507a 100644
--- a/src/templates/zshare/modalform/index.jsx
+++ b/src/templates/zshare/modalform/index.jsx
@@ -19,7 +19,7 @@
   datemonth: ['label', 'field', 'initval', 'type', 'readonly', 'required', 'hidden', 'readin', 'blacklist'],
   datetime: ['label', 'field', 'initval', 'type', 'readonly', 'required', 'hidden', 'readin', 'blacklist'],
   textarea: ['label', 'field', 'initval', 'type', 'readonly', 'required', 'hidden', 'readin', 'blacklist', 'fieldlength', 'maxRows', 'encryption', 'interception'],
-  color: ['label', 'field', 'type', 'blacklist', 'required', 'hidden', 'readin'],
+  color: ['label', 'field', 'type', 'blacklist', 'readonly', 'required', 'hidden', 'readin'],
   funcvar: ['label', 'field', 'type', 'blacklist', 'hidden'],
   linkMain: ['label', 'field', 'type', 'readonly', 'required', 'hidden', 'fieldlength', 'blacklist']
 }
diff --git a/src/utils/option.js b/src/utils/option.js
index 6caa214..45f2ff8 100644
--- a/src/utils/option.js
+++ b/src/utils/option.js
@@ -70,6 +70,13 @@
     isSystem: true
   },
   {
+    title: '鏃ュ巻',
+    type: 'CalendarPage',
+    url: treepage,
+    baseconfig: '',
+    isSystem: true
+  },
+  {
     title: '瑙掕壊鏉冮檺鍒嗛厤',
     type: 'RolePermission',
     url: rolemanage,
@@ -301,6 +308,20 @@
   text: '鐧藉簳绱'
 }]
 
+export const calendarColors = [
+  {name: 'black', value: '#ffffff'},
+  {name: 'red', value: '#ffffff'},
+  {name: 'orange', value: '#ffffff'},
+  {name: 'yellow', value: 'rgba(0,0,0,.65)'},
+  {name: 'green', value: '#ffffff'},
+  {name: 'lightgreen', value: 'rgba(0,0,0,.65)'},
+  {name: 'cyan', value: 'rgba(0,0,0,.65)'},
+  {name: 'blue', value: '#ffffff'},
+  {name: 'purple', value: '#ffffff'},
+  {name: 'white', value: 'rgba(0,0,0,.65)'}
+]
+
+// 鏄剧ず鍒楁爣璁拌壊绯�
 export const colorTransform = {
   'dust-red-1': '#fff1f0',
   'dust-red-2': '#ffccc7',
@@ -439,15 +460,10 @@
     value: 'DustRed',
     label: '钖勬毊',
     children: [
-      // { value: 'dust-red-1', label: 'dust-red-1' },
       { value: 'dust-red-2', label: 'dust-red-2' },
-      // { value: 'dust-red-3', label: 'dust-red-3' },
       { value: 'dust-red-4', label: 'dust-red-4' },
-      // { value: 'dust-red-5', label: 'dust-red-5' },
       { value: 'dust-red-6', label: 'dust-red-6' },
-      // { value: 'dust-red-7', label: 'dust-red-7' },
       { value: 'dust-red-8', label: 'dust-red-8' },
-      // { value: 'dust-red-9', label: 'dust-red-9' },
       { value: 'dust-red-10', label: 'dust-red-10' }
     ]
   },
@@ -455,15 +471,10 @@
     value: 'Volcano',
     label: '鐏北',
     children: [
-      // { value: 'volcano-1', label: 'volcano-1' },
       { value: 'volcano-2', label: 'volcano-2' },
-      // { value: 'volcano-3', label: 'volcano-3' },
       { value: 'volcano-4', label: 'volcano-4' },
-      // { value: 'volcano-5', label: 'volcano-5' },
       { value: 'volcano-6', label: 'volcano-6' },
-      // { value: 'volcano-7', label: 'volcano-7' },
       { value: 'volcano-8', label: 'volcano-8' },
-      // { value: 'volcano-9', label: 'volcano-9' },
       { value: 'volcano-10', label: 'volcano-10' }
     ],
   },
@@ -471,15 +482,10 @@
     value: 'SunsetOrange',
     label: '鏃ユ毊',
     children: [
-      // { value: 'orange-1', label: 'orange-1' },
       { value: 'orange-2', label: 'orange-2' },
-      // { value: 'orange-3', label: 'orange-3' },
       { value: 'orange-4', label: 'orange-4' },
-      // { value: 'orange-5', label: 'orange-5' },
       { value: 'orange-6', label: 'orange-6' },
-      // { value: 'orange-7', label: 'orange-7' },
       { value: 'orange-8', label: 'orange-8' },
-      // { value: 'orange-9', label: 'orange-9' },
       { value: 'orange-10', label: 'orange-10' }
     ]
   },
@@ -487,15 +493,10 @@
     value: 'CalendulaGold',
     label: '閲戠洀鑺�',
     children: [
-      // { value: 'gold-1', label: 'gold-1' },
       { value: 'gold-2', label: 'gold-2' },
-      // { value: 'gold-3', label: 'gold-3' },
       { value: 'gold-4', label: 'gold-4' },
-      // { value: 'gold-5', label: 'gold-5' },
       { value: 'gold-6', label: 'gold-6' },
-      // { value: 'gold-7', label: 'gold-7' },
       { value: 'gold-8', label: 'gold-8' },
-      // { value: 'gold-9', label: 'gold-9' },
       { value: 'gold-10', label: 'gold-10' }
     ]
   },
@@ -503,15 +504,10 @@
     value: 'SunriseYellow',
     label: '鏃ュ嚭',
     children: [
-      // { value: 'yellow-1', label: 'yellow-1' },
       { value: 'yellow-2', label: 'yellow-2' },
-      // { value: 'yellow-3', label: 'yellow-3' },
       { value: 'yellow-4', label: 'yellow-4' },
-      // { value: 'yellow-5', label: 'yellow-5' },
       { value: 'yellow-6', label: 'yellow-6' },
-      // { value: 'yellow-7', label: 'yellow-7' },
       { value: 'yellow-8', label: 'yellow-8' },
-      // { value: 'yellow-9', label: 'yellow-9' },
       { value: 'yellow-10', label: 'yellow-10' }
     ]
   },
@@ -519,15 +515,10 @@
     value: 'Lime',
     label: '闈掓煚',
     children: [
-      // { value: 'lime-1', label: 'lime-1' },
       { value: 'lime-2', label: 'lime-2' },
-      // { value: 'lime-3', label: 'lime-3' },
       { value: 'lime-4', label: 'lime-4' },
-      // { value: 'lime-5', label: 'lime-5' },
       { value: 'lime-6', label: 'lime-6' },
-      // { value: 'lime-7', label: 'lime-7' },
       { value: 'lime-8', label: 'lime-8' },
-      // { value: 'lime-9', label: 'lime-9' },
       { value: 'lime-10', label: 'lime-10' }
     ]
   },
@@ -535,15 +526,10 @@
     value: 'PolarGreen',
     label: '鏋佸厜缁�',
     children: [
-      // { value: 'green-1', label: 'green-1' },
       { value: 'green-2', label: 'green-2' },
-      // { value: 'green-3', label: 'green-3' },
       { value: 'green-4', label: 'green-4' },
-      // { value: 'green-5', label: 'green-5' },
       { value: 'green-6', label: 'green-6' },
-      // { value: 'green-7', label: 'green-7' },
       { value: 'green-8', label: 'green-8' },
-      // { value: 'green-9', label: 'green-9' },
       { value: 'green-10', label: 'green-10' }
     ]
   },
@@ -551,15 +537,10 @@
     value: 'Cyan',
     label: '鏄庨潚',
     children: [
-      // { value: 'cyan-1', label: 'cyan-1' },
       { value: 'cyan-2', label: 'cyan-2' },
-      // { value: 'cyan-3', label: 'cyan-3' },
       { value: 'cyan-4', label: 'cyan-4' },
-      // { value: 'cyan-5', label: 'cyan-5' },
       { value: 'cyan-6', label: 'cyan-6' },
-      // { value: 'cyan-7', label: 'cyan-7' },
       { value: 'cyan-8', label: 'cyan-8' },
-      // { value: 'cyan-9', label: 'cyan-9' },
       { value: 'cyan-10', label: 'cyan-10' }
     ]
   },
@@ -567,15 +548,10 @@
     value: 'DaybreakBlue',
     label: '鎷傛檽钃�',
     children: [
-      // { value: 'blue-1', label: 'blue-1' },
       { value: 'blue-2', label: 'blue-2' },
-      // { value: 'blue-3', label: 'blue-3' },
       { value: 'blue-4', label: 'blue-4' },
-      // { value: 'blue-5', label: 'blue-5' },
       { value: 'blue-6', label: 'blue-6' },
-      // { value: 'blue-7', label: 'blue-7' },
       { value: 'blue-8', label: 'blue-8' },
-      // { value: 'blue-9', label: 'blue-9' },
       { value: 'blue-10', label: 'blue-10' }
     ]
   },
@@ -583,15 +559,10 @@
     value: 'GeekBlue',
     label: '鏋佸钃�',
     children: [
-      // { value: 'geekblue-1', label: 'geekblue-1' },
       { value: 'geekblue-2', label: 'geekblue-2' },
-      // { value: 'geekblue-3', label: 'geekblue-3' },
       { value: 'geekblue-4', label: 'geekblue-4' },
-      // { value: 'geekblue-5', label: 'geekblue-5' },
       { value: 'geekblue-6', label: 'geekblue-6' },
-      // { value: 'geekblue-7', label: 'geekblue-7' },
       { value: 'geekblue-8', label: 'geekblue-8' },
-      // { value: 'geekblue-9', label: 'geekblue-9' },
       { value: 'geekblue-10', label: 'geekblue-10' }
     ]
   },
@@ -599,15 +570,10 @@
     value: 'GoldenPurple',
     label: '閰辩传',
     children: [
-      // { value: 'purple-1', label: 'purple-1' },
       { value: 'purple-2', label: 'purple-2' },
-      // { value: 'purple-3', label: 'purple-3' },
       { value: 'purple-4', label: 'purple-4' },
-      // { value: 'purple-5', label: 'purple-5' },
       { value: 'purple-6', label: 'purple-6' },
-      // { value: 'purple-7', label: 'purple-7' },
       { value: 'purple-8', label: 'purple-8' },
-      // { value: 'purple-9', label: 'purple-9' },
       { value: 'purple-10', label: 'purple-10' }
     ]
   },
@@ -615,15 +581,10 @@
     value: 'Magenta',
     label: '娉曞紡娲嬬孩',
     children: [
-      // { value: 'magenta-1', label: 'magenta-1' },
       { value: 'magenta-2', label: 'magenta-2' },
-      // { value: 'magenta-3', label: 'magenta-3' },
       { value: 'magenta-4', label: 'magenta-4' },
-      // { value: 'magenta-5', label: 'magenta-5' },
       { value: 'magenta-6', label: 'magenta-6' },
-      // { value: 'magenta-7', label: 'magenta-7' },
       { value: 'magenta-8', label: 'magenta-8' },
-      // { value: 'magenta-9', label: 'magenta-9' },
       { value: 'magenta-10', label: 'magenta-10' }
     ]
   },
@@ -631,15 +592,10 @@
     value: 'Gray',
     label: '涓�ц壊',
     children: [
-      // { value: 'gray-1', label: 'gray-1' },
       { value: 'gray-2', label: 'gray-2' },
-      // { value: 'gray-3', label: 'gray-3' },
       { value: 'gray-4', label: 'gray-4' },
-      // { value: 'gray-5', label: 'gray-5' },
       { value: 'gray-6', label: 'gray-6' },
-      // { value: 'gray-7', label: 'gray-7' },
       { value: 'gray-8', label: 'gray-8' },
-      // { value: 'gray-9', label: 'gray-9' },
       { value: 'gray-10', label: 'gray-10' }
     ]
   }
diff --git a/src/utils/utils.js b/src/utils/utils.js
index 7808f1e..e033b10 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -765,11 +765,11 @@
 
           val = val.replace(/(^\s*$)|\t*|\v*/ig, '')
 
-          if (!val && col.required === 'true') { // 蹇呭~鏍¢獙
+          if (!val && col.required === 'true') {            // 蹇呭~鏍¢獙
             errors.push(_position + dict['main.excel.content.emptyerror'])
-          } else if (val.length > col.limit) {    // 闀垮害鏍¢獙
+          } else if (col.limit && val.length > col.limit) { // 闀垮害鏍¢獙
             errors.push(_position + dict['main.excel.content.maxlimit'])
-          } else {                               // 鍏抽敭瀛楁牎楠�
+          } else {                                          // 鍏抽敭瀛楁牎楠�
             keys.forEach(key => {
               let _patten = new RegExp('(^' + key + '\\s+)|(\\s+' + key + '\\s+)', 'ig')
               if (_patten.test(val)) {
diff --git a/src/views/login/index.scss b/src/views/login/index.scss
index 3f3b132..7ae35f1 100644
--- a/src/views/login/index.scss
+++ b/src/views/login/index.scss
@@ -41,7 +41,7 @@
         margin-right: 0;
         border-radius: 0;
         text-align: center;
-        font-size: 18px;
+        font-size: 17px;
         white-space: nowrap;
         text-overflow: ellipsis;
         overflow: hidden;
@@ -52,10 +52,15 @@
     }
   }
   .login-form-1 {
-    .ant-tabs-tab {
-      text-align: left!important;
-      padding-left: 1.6vw!important;
-      line-height: 60px!important;
+    .ant-tabs.ant-tabs-card {
+      .ant-tabs-card-bar {
+        .ant-tabs-tab {
+          font-size: 18px;
+          text-align: left!important;
+          padding-left: 1.6vw!important;
+          line-height: 60px!important;
+        }
+      }
     }
   }
   .login-middle {

--
Gitblit v1.8.0