king
2020-08-17 df565b506ddb2bed918befceefddcd529eb58782
2020-08-17
17个文件已修改
2个文件已添加
961 ■■■■■ 已修改文件
src/components/404/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/calendar/index.jsx 647 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/calendar/index.scss 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/calendar/index.jsx 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/calendar/index.scss 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/calcomponent/calendarform/index.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/calcomponent/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/index.jsx 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/index.scss 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/source.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/tabcomponent/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/option.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/404/index.scss
@@ -1,4 +1,9 @@
.box404 {
  max-height: calc(100vh - 110px);
  text-align: center;
}
.ant-tabs-tabpane {
  >.box404 {
    padding-top: 40px;
  }
}
src/components/tabview/index.jsx
@@ -19,6 +19,7 @@
const Home = asyncComponent(() => import('@/tabviews/home'))
const CommonTable = asyncComponent(() => import('@/tabviews/commontable'))
const CalendarPage = asyncComponent(() => import('@/tabviews/calendar'))
const TreePage = asyncComponent(() => import('@/tabviews/treepage'))
const VerupTable = asyncComponent(() => import('@/tabviews/verupmanage'))
const ScriptTable = asyncComponent(() => import('@/tabviews/scriptmanage'))
@@ -105,6 +106,8 @@
      return (<CommonTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
    } else if (view.type === 'TreePage') {
      return (<TreePage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
    } else if (view.type === 'CalendarPage') {
      return (<CalendarPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
    } else if (view.type === 'VerupTable') {
      return (<VerupTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
    } else if (view.type === 'ScriptTable') {
@@ -210,7 +213,7 @@
                    <Tabs.TabPane
                      tab={
                        <span className="tab-control">
                          {['CommonTable', 'FormTab', 'TreePage'].includes(view.type) ?
                          {['CommonTable', 'FormTab', 'TreePage', 'CalendarPage'].includes(view.type) ?
                            <Icon type="redo" onClick={(e) => {this.refreshTabview(e, view)}}/> : null
                          }
                          <span className="tab-name" onClick={(e) => {this.changeTab(e, view)}}>
@@ -224,7 +227,7 @@
                      key={view.MenuID}
                    >
                      {this.selectcomponent(view)}
                      {options.sysType !== 'cloud' && !['CommonTable', 'TreePage', 'ManageTable'].includes(view.type) ?
                      {options.sysType !== 'cloud' && !['CommonTable', 'TreePage', 'ManageTable', 'CalendarPage'].includes(view.type) ?
                        <Button
                          icon="copy"
                          shape="circle"
src/tabviews/calendar/index.jsx
New file
@@ -0,0 +1,647 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { notification, Spin, Modal, Button, message, Tree, Typography } from 'antd'
import moment from 'moment'
import Api from '@/api'
import options from '@/store/options.js'
import zhCN from '@/locales/zh-CN/main.js'
import enUS from '@/locales/en-US/main.js'
import Utils from '@/utils/utils.js'
import asyncSpinComponent from '@/utils/asyncSpinComponent'
import { refreshTabView } from '@/store/action'
import MainSearch from '@/tabviews/zshare/topSearch'
import NotFount from '@/components/404'
import './index.scss'
// 通用组件
const CalendarComponent = asyncSpinComponent(() => import('@/tabviews/zshare/calendar'))
const SubTabTable = asyncSpinComponent(() => import('@/tabviews/subtabtable'))
const { TreeNode } = Tree
const { Paragraph } = Typography
class NormalTable extends Component {
  static propTpyes = {
    param: PropTypes.any,        // 其他页面传递的搜索条件等参数
    MenuID: PropTypes.string,    // 菜单Id
    MenuNo: PropTypes.string,    // 菜单参数
    MenuName: PropTypes.string   // 菜单名称
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    ContainerId: Utils.getuuid(), // 菜单外层html Id
    BID: null,            // 页面跳转时携带ID
    loadingview: true,    // 页面加载中
    viewlost: false,      // 页面丢失:1、未获取到配置-页面丢失;2、页面未启用
    lostmsg: '',          // 页面丢失时的提示信息
    config: {},           // 页面配置信息,包括按钮、搜索、显示列、标签等
    userConfig: null,     // 用户自定义设置
    searchlist: null,     // 搜索条件
    arr_field: '',        // 使用 sPC_Get_TableData 时的查询字段集
    setting: null,        // 页面全局设置:数据源、按钮及显示列固定、主键等
    data: null,           // 列表数据集
    loading: false,       // 列表数据加载中
    search: '',           // 搜索条件数组,使用时需分场景处理
    visible: false,       // 标签页控制
    triggerTime: '',      // 点击时间
    treevisible: false,   // 菜单结构树弹框显示隐藏控制
    calendarYear: moment().format('YYYY') // 日历年份
  }
  /**
   * @description 获取页面配置信息
   */
  async loadconfig () {
    const { permAction, param } = this.props
    let _param = {
      func: 'sPC_Get_LongParam',
      MenuID: this.props.MenuID
    }
    let result = await Api.getCacheConfig(_param)
    if (result.status) {
      let config = ''
      let userConfig = null
      let _curUserConfig = ''
      try { // 配置信息解析
        config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
      } catch (e) {
        console.warn('Parse Failure')
        config = ''
      }
      // HS不使用自定义设置
      if (result.LongParamUser && this.props.menuType !== 'HS') {
        try { // 配置信息解析
          userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
          _curUserConfig = userConfig[this.props.MenuID]
        } catch (e) {
          console.warn('Parse Failure')
          userConfig = null
        }
      }
      // 页面配置解析错误时提示
      if (!config) {
        this.setState({
          loadingview: false,
          viewlost: true
        })
        return
      }
      // 页面未启用时,显示未启用页面
      if (!config.enabled) {
        this.setState({
          loadingview: false,
          viewlost: true,
          lostmsg: this.state.dict['main.view.unenabled']
        })
        return
      }
      // 权限过滤
      if (this.props.menuType !== 'HS') {
        if (config.tab && !permAction[config.tab.linkTab]) {
          config.tab = null
        }
      }
      // 字段权限黑名单
      config.search = config.search.map(item => {
        item.oriInitval = item.initval
        if (['text', 'select', 'link'].includes(item.type) && param) {
          if (param.searchkey === item.field) {
            item.initval = param.searchval
          } else if (param.BID && item.field === 'BID') {
            item.initval = param.BID
          }
        }
        if (!item.blacklist || item.blacklist.length === 0) return item
        let _black = item.blacklist.filter(v => {
          return this.props.permRoles.indexOf(v) !== -1
        })
        if (_black.length > 0) {
          item.Hide = 'true'
        }
        return item
      })
      if (_curUserConfig) {
        config.setting = {...config.setting, ..._curUserConfig.setting}
        config.easyCode = _curUserConfig.easyCode || config.easyCode || ''
      }
      // 透视字段处理,初始化处理
      let valid = true
      config.search = config.search.map(item => {
        item.oriInitval = item.initval
        if (['text', 'select', 'link'].includes(item.type) && param) {
          if (param.searchkey === item.field) {
            item.initval = param.searchval
          } else if (param.BID && item.field === 'BID') {
            item.initval = param.BID
          }
        }
        if (item.required === 'true' && !item.initval) {
          valid = false
        }
        return item
      })
      this.setState({
        BID: param && param.BID ? param.BID : '',
        loadingview: false,
        config: config,
        userConfig: userConfig,
        setting: config.setting,
        searchlist: config.search,
        arr_field: config.columns.map(item => item.field).join(','),
        search: Utils.initMainSearch(config.search) // 搜索条件初始化(含有时间格式,需要转化)
      }, () => {
        if (config.setting.onload !== 'false' && valid) { // 初始化可加载
          this.loadmaindata()
        }
      })
    } else {
      this.setState({
        loadingview: false,
        viewlost: true
      })
      notification.warning({
        top: 92,
        message: result.message,
        duration: 5
      })
    }
  }
  /**
   * @description 主表数据加载
   */
  async loadmaindata () {
    const { setting, search, BID } = this.state
    let param = ''
    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
    if (requireFields.length > 0) {
      let labels = requireFields.map(item => item.label)
      labels = Array.from(new Set(labels))
      notification.warning({
        top: 92,
        message: this.state.dict['form.required.input'] + labels.join('、') + ' !',
        duration: 3
      })
      return
    }
    this.setState({
      loading: true
    })
    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
      param = this.getCustomParam()
    } else {
      param = this.getDefaultParam()
    }
    if (BID) {
      param.BID = BID
    }
    // 数据管理权限
    if (this.props.dataManager) {
      param.dataM = 'Y'
    }
    let result = await Api.genericInterface(param)
    if (result.status) {
      this.setState({
        data: result.data.map((item, index) => {
          item.key = index
          return item
        }),
        loading: false
      })
    } else {
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
  /**
   * @description 获取用户自定义存储过程传参
   */
  getCustomParam = () => {
    const { search, setting, calendarYear, config } = this.state
    let _search = Utils.formatCustomMainSearch(search)
    let param = {
      ..._search
    }
    if (config.calendar.refresh === 'true') {
      param.calendarDate = calendarYear
    }
    if (setting.interType === 'inner') {
      param.func = setting.innerFunc
    } else {
      if (this.props.menuType === 'HS') {
        if (setting.sysInterface === 'true' && options.cloudServiceApi) {
          param.rduri = options.cloudServiceApi
        } else if (setting.sysInterface !== 'true') {
          param.rduri = setting.interface
        }
      } else {
        if (setting.sysInterface === 'true' && window.GLOB.mainSystemApi) {
          param.rduri = window.GLOB.mainSystemApi
        } else if (setting.sysInterface !== 'true') {
          param.rduri = setting.interface
        }
      }
      if (setting.outerFunc) {
        param.func = setting.outerFunc
      }
    }
    return param
  }
  /**
   * @description 获取系统存储过程 sPC_Get_TableData 的参数
   */
  getDefaultParam = () => {
    const { arr_field, search, setting, config, calendarYear } = this.state
    let _search = Utils.joinMainSearchkey(search)
    _search = _search ? 'where ' + _search : ''
    let param = {
      func: 'sPC_Get_TableData',
      obj_name: 'data',
      arr_field: arr_field,
      custom_script: setting.customScript || '',
      default_sql: setting.default || 'true'
    }
    let _dataresource = setting.dataresource
    if (/\s/.test(_dataresource)) {
      _dataresource = '(' + _dataresource + ') tb'
    }
    if (this.props.dataManager) { // 数据权限
      _dataresource = _dataresource.replace(/\$@/ig, '/*')
      _dataresource = _dataresource.replace(/@\$/ig, '*/')
      param.custom_script = param.custom_script.replace(/\$@/ig, '/*')
      param.custom_script = param.custom_script.replace(/@\$/ig, '*/')
    } else {
      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
      param.custom_script = param.custom_script.replace(/@\$|\$@/ig, '')
    }
    let regoptions = null
    if (setting.queryType === 'statistics' || param.custom_script) {
      let allSearch = Utils.getAllSearchOptions(search)
      regoptions = allSearch.map(item => {
        return {
          reg: new RegExp('@' + item.key + '@', 'ig'),
          value: `'${item.value}'`
        }
      })
    }
    if (config.calendar.refresh === 'true' && regoptions) {
      regoptions.push({
        reg: new RegExp('@calendarDate@', 'ig'),
        value: `${calendarYear}-01-01 00:00:00.000`
      })
      regoptions.push({
        reg: new RegExp('@calendarDate1@', 'ig'),
        value: `${calendarYear}-12-31 23:59:59.999`
      })
    }
    if (setting.queryType === 'statistics' && setting.default !== 'false') { // 统计数据源,内容替换
      regoptions.forEach(item => {
        _dataresource = _dataresource.replace(item.reg, item.value)
      })
      _search = ''
    }
    let LText = ''
    if (setting.default !== 'false') {
      LText = `select ${arr_field} from ${_dataresource} ${_search}`
    }
    if (param.custom_script) {
      regoptions.forEach(item => {
        param.custom_script = param.custom_script.replace(item.reg, item.value)
      })
      if (LText) {
        LText += `
          aaa:
          if @ErrorCode!=''
            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
        `
      } else {
        param.custom_script += `
          aaa:
          if @ErrorCode!=''
            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
        `
      }
    }
    // 测试系统打印查询语句
    if (options.sysType === 'local' && !window.GLOB.systemType) {
      param.custom_script &&  console.log(`${LText ? '' : '/*不执行默认sql*/\n'}${param.custom_script}`)
      LText &&  console.log(LText)
    }
    param.custom_script = Utils.formatOptions(param.custom_script)
    param.LText = Utils.formatOptions(LText)
    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
    param.DateCount = ''
    if (this.props.menuType === 'HS') { // 云端数据验证
      param.open_key = Utils.encrypt(param.secretkey, param.timestamp, true)
    }
    return param
  }
  /**
   * @description 搜索条件改变时,重置表格数据
   * 含有初始不加载的页面,修改设置
   */
  refreshbysearch = (searches) => {
    const { setting } = this.state
    if (setting.onload === 'false') {
      this.setState({
        search: searches,
        setting: {...setting, onload: 'true'}
      }, () => {
        this.loadmaindata()
      })
    } else {
      this.setState({
        search: searches
      }, () => {
        this.loadmaindata()
      })
    }
  }
  /**
   * @description 页面刷新,重新获取配置
   */
  reloadview = () => {
    this.setState({ loadingview: true, viewlost: false, lostmsg: '', data: null, loading: false, search: ''
    }, () => {
      this.loadconfig()
    })
  }
  handleviewconfig = (e) => {
    e.stopPropagation()
    const { MenuNo } = this.props
    const { config } = this.state
    if (config && config.funcs && config.funcs.length > 0) {
      this.setState({
        treevisible: true
      })
    } else {
      let oInput = document.createElement('input')
      oInput.value = MenuNo || ''
      document.body.appendChild(oInput)
      oInput.select()
      document.execCommand('Copy')
      document.body.removeChild(oInput)
      message.success(this.state.dict['main.copy.success'])
    }
  }
  getTreeNode = (data) => {
    let _type = {
      view: '页面',
      btn: '按钮',
      tab: '标签'
    }
    return data.map(item => {
      let _title = _type[item.subtype]
      let _others = []
      _others.push(
        (item.menuNo ? item.menuNo + '(菜单参数)' : ''),
        (item.tableName ? item.tableName + '(表名) ' : ''),
        (item.innerFunc ? item.innerFunc + '(内部函数) ' : ''),
        (item.outerFunc ? item.outerFunc + '(外部函数)' : '')
      )
      _others = _others.filter(Boolean)
      _others = _others.join('、')
      if (item.label) {
        _title = _title + '(' + item.label + ')'
      }
      if (_others) {
        _title = _title + ': ' + _others
      }
      if (item.subfuncs && item.subfuncs.length > 0) {
        return (
          <TreeNode title={_title} key={item.uuid} dataRef={item} selectable={false}>
            {this.getTreeNode(item.subfuncs)}
          </TreeNode>
        )
      }
      return <TreeNode key={item.uuid} title={_title} isLeaf selectable={false} />
    })
  }
  UNSAFE_componentWillMount () {
    // 组件加载时,获取菜单数据
    this.loadconfig()
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.refreshTab && nextProps.refreshTab.MenuID === this.props.MenuID) {
      if (nextProps.refreshTab.position === 'grid') {
        this.loadmaindata()
      } else if (nextProps.refreshTab.position === 'view') {
        this.reloadview()
      }
      this.props.refreshTabView('')
    } else if (nextProps.param && !is(fromJS(this.props.param), fromJS(nextProps.param))) {
      let search = this.state.search.map(item => {
        if (item.type === 'text' && item.key === nextProps.param.searchkey) {
          item.value = nextProps.param.searchval
        }
        return item
      })
      this.refreshbysearch(search)
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  changeDate = (value) => {
    this.setState({calendarYear: value}, () => {
      this.loadmaindata()
    })
  }
  triggerDate = (item) => {
    const { config } = this.state
    let time = ''
    if (!config.tab) return
    if (item.time.length === 6) {
      time = item.time.substr(0, 4) + '-' + item.time.substr(4, 2)
    } else {
      time = item.time.substr(0, 4) + '-' + item.time.substr(4, 2) + '-' + item.time.substr(6, 2)
    }
    this.setState({
      visible: true,
      triggerTime: time
    })
  }
  closeTab = () => {
    this.setState({
      visible: false,
      triggerTime: ''
    })
  }
  render() {
    const { BID, searchlist, loadingview, viewlost, config, loading, data } = this.state
    return (
      <div className="calendar-page" id={this.state.ContainerId}>
        {loadingview && <Spin size="large" />}
        {searchlist && searchlist.length > 0 ?
          <MainSearch
            BID={BID}
            dict={this.state.dict}
            searchlist={searchlist}
            menuType={this.props.menuType}
            dataManager={this.props.dataManager}
            refreshdata={this.refreshbysearch}
          /> : null
        }
        {config && config.calendar ? <CalendarComponent calendar={config.calendar} loading={loading} data={data} triggerDate={this.triggerDate} changeDate={this.changeDate}/> : null}
        {options.sysType !== 'cloud' ? <Button
          icon="copy"
          shape="circle"
          className="common-table-copy"
          onClick={this.handleviewconfig}
        /> : null}
        <Modal
          className="menu-tree-modal"
          title={'菜单结构树'}
          width={'650px'}
          maskClosable={false}
          visible={this.state.treevisible}
          onCancel={() => this.setState({treevisible: false})}
          footer={[
            <Button key="close" onClick={() => this.setState({treevisible: false})}>{this.state.dict['main.close']}</Button>
          ]}
          destroyOnClose
        >
          <div className="menu-header">
            <span>菜单名称:{this.props.MenuName}</span>
            <span>菜单参数:{<Paragraph copyable>{this.props.MenuNo}</Paragraph>}</span>
          </div>
          {this.state.treevisible ? <Tree defaultExpandAll showLine={true}>
            {this.getTreeNode(config.funcs)}
          </Tree> : null}
        </Modal>
        <Modal
          title={config.tab ? config.tab.label : ''}
          width={'80vw'}
          maskClosable={false}
          visible={this.state.visible}
          onCancel={this.closeTab}
          footer={[
            <Button key="close" onClick={this.closeTab}>{this.state.dict['main.close']}</Button>
          ]}
          destroyOnClose
        >
          {config.tab ? <SubTabTable
            SupMenuID={this.props.MenuID}
            MenuID={config.tab.linkTab}
            refreshSupView={this.loadmaindata}
            closeModalView={this.closeTab}
          /> : null}
        </Modal>
        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    menuType: state.editLevel,
    tabviews: state.tabviews,
    refreshTab: state.refreshTab,
    permAction: state.permAction,
    permRoles: state.permRoles,
    dataManager: state.dataManager
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    refreshTabView: (refreshTab) => dispatch(refreshTabView(refreshTab))
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalTable)
src/tabviews/calendar/index.scss
New file
@@ -0,0 +1,58 @@
.calendar-page {
  position: relative;
  min-height: calc(100vh - 94px);
  padding-top: 16px;
  padding-bottom: 80px;
  .box404 {
    padding-top: 30px;
  }
  .ant-modal-mask {
    position: absolute;
  }
  .ant-modal-wrap {
    position: absolute;
  }
  .action-modal .ant-modal {
    top: 40px;
    max-width: 95%;
    .ant-modal-body {
      max-height: calc(100vh - 265px);
    }
  }
  > .ant-spin {
    position: absolute;
    z-index: 10;
    left: calc(50% - 22px);
    top: calc(50vh - 70px);
  }
  .common-table-copy {
    position: fixed;
    z-index: 2;
    bottom: 65px;
    right: 30px;
    width: 40px;
    height: 40px;
  }
}
.menu-tree-modal {
  .ant-modal-body {
    min-height: 300px;
    .menu-header {
      text-align: center;
      span {
        font-weight: 600;
        margin-right: 20px;
      }
      .ant-typography {
        font-weight: 600;
        display: inline-block;
      }
    }
    .ant-tree li .ant-tree-node-content-wrapper {
      cursor: default;
    }
  }
}
src/tabviews/subtabtable/index.jsx
@@ -26,11 +26,13 @@
class SubTabModalTable extends Component {
  static propTpyes = {
    type: PropTypes.any,             // 类型,calendar需特殊处理
    BID: PropTypes.string,           // 上级数据ID
    BData: PropTypes.any,            // 上级数据
    MenuID: PropTypes.string,        // 菜单Id
    SupMenuID: PropTypes.string,     // 上级菜单Id
    refreshSupView: PropTypes.any    // 刷新上级菜单
    refreshSupView: PropTypes.any,   // 刷新上级菜单
    closeModalView: PropTypes.any    // 关闭模态框
  }
  state = {
@@ -62,7 +64,7 @@
   * @description 获取页面配置信息
   */
  async loadconfig () {
    const { permAction } = this.props
    const { permAction, type } = this.props
    let param = {
      func: 'sPC_Get_LongParam',
@@ -135,7 +137,11 @@
      }
      // 仅支持exec、prompt、pop 三种类型按钮
      config.action = config.action.filter(item => ['exec', 'prompt', 'pop'].includes(item.OpenType))
      if (type === 'calendar') {
        config.action = config.action.filter(item => ['exec', 'prompt', 'pop', 'tab'].includes(item.OpenType))
      } else {
        config.action = config.action.filter(item => ['exec', 'prompt', 'pop'].includes(item.OpenType))
      }
      // 权限过滤
      if (this.props.menuType !== 'HS') {
@@ -589,6 +595,8 @@
    } else if (position === 'view') {
      this.reloadview()
      this.props.refreshSupView()
    } else if (position === 'trigger') { // 日历子表触发标签点击事件
      this.props.closeModalView && this.props.closeModalView()
    }
  }
src/tabviews/zshare/actionList/index.jsx
@@ -67,6 +67,8 @@
      if (!unclose) {
        this.setState({running: false})
      }
    } else if (type === 'trigger') { // 日历中的新标签页触发事件
      this.props.refreshdata('trigger')
    }
  }
@@ -139,6 +141,7 @@
            setting={setting}
            selectedData={selectedData}
            triggerBtn={this.state.triggerBtn}
            updateStatus={this.updateStatus}
          />
        )
      } else if (item.OpenType === 'innerpage' || item.OpenType === 'outerpage') {
src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -17,6 +17,7 @@
    selectedData: PropTypes.any,      // 子表中选择数据
    setting: PropTypes.any,           // 页面通用设置
    triggerBtn: PropTypes.any,
    updateStatus: PropTypes.any
  }
  state = {
@@ -131,6 +132,10 @@
      tabs.splice(index + 1, 0, newtab)
    }
    if (this.props.updateStatus) {
      this.props.updateStatus('trigger')
    }
    this.props.modifyTabview(tabs)
  }
src/tabviews/zshare/calendar/index.jsx
@@ -1,10 +1,9 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Select, Radio, Row, Col, Popover, Badge } from 'antd'
import { Select, Radio, Row, Col, Popover, Badge, Spin } from 'antd'
import moment from 'moment'
// import Utils from '@/utils/utils.js'
import './index.scss'
const { Option } = Select
@@ -13,6 +12,8 @@
  static propTpyes = {
    data: PropTypes.any,            // 事件数据
    calendar: PropTypes.any,
    changeDate: PropTypes.func,
    triggerDate: PropTypes.func,
    loading: false
  }
@@ -63,7 +64,7 @@
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!is(fromJS(this.props.data), fromJS(nextProps.data))) {
      let datelist = this.mountdata(this.state.datelist, nextProps.data)
      let datelist = this.mountdata(this.state.datelist, nextProps.data || [])
      let monthlist = null
      if (this.state.levels.includes('month')) {
@@ -89,18 +90,8 @@
    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
    }
    let levels = { red: 1, orange: 2, yellow: 3, green: 4, cyan: 5, blue: 6, purple: 7, gray: 8 }
    let colors = { red: '#d0021b', orange: '#f5a623', yellow: '#f8e71c', green: '#7ed321', cyan: '#50e3c2', blue: '#1890ff', purple: '#bd10e0', gray: '#9b9b9b' }
    data.forEach(item => {
      let startTime = item[calendar.startfield]
@@ -112,7 +103,7 @@
      if (!item[calendar.remarkfield]) return
      datalist.push({
        color: color,
        color: colors[color] || '',
        level: color && levels[color] ? levels[color] : 100,
        remark: item[calendar.remarkfield],
        startMonth: startTime.substr(0, 4) + startTime.substr(5, 2),
@@ -129,16 +120,14 @@
    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)'},
      {background: '#d0021b', color: '#ffffff'},
      {background: '#f5a623', color: '#ffffff'},
      {background: '#f8e71c', color: '#ffffff'},
      {background: '#7ed321', color: '#ffffff'},
      {background: '#50e3c2', color: '#ffffff'},
      {background: '#1890ff', color: '#ffffff'},
      {background: '#bd10e0', color: '#ffffff'},
      {background: '#9b9b9b', color: '#ffffff'},
    ]
    return datelist.map(month => {
@@ -147,6 +136,9 @@
          month.subData.push(item)
        }
      })
      if (month.subData[0]) {
        month.style = styles[month.subData[0].level - 1] || null
      }
      month.sublist = month.sublist.map(week => {
        week.sublist = week.sublist.map(day => {
          if (!day) return null
@@ -221,6 +213,7 @@
  }
  yearChange = (value) => {
    const { calendar, data } = this.props
    const { levels, selectMonth } = this.state
    let datelist = this.getDateList(value)
    let monthlist = null
@@ -228,10 +221,14 @@
    if (levels.includes('month')) {
      monthlist = datelist.filter(item => item.month === selectMonth)[0]
    }
    if (calendar.refresh !== 'true') {
      datelist = this.mountdata(datelist, data)
    this.setState({ selectYear: value, datelist, monthlist })
      this.setState({ selectYear: value, datelist, monthlist })
    } else {
      this.setState({ selectYear: value, datelist, monthlist })
    if (this.props.changeDate) {
      this.props.changeDate(value)
    }
  }
@@ -246,16 +243,19 @@
  }
  triggerDay = (item) => {
    if (this.props.triggerDate) {
      this.props.triggerDate(item)
    }
  }
  render() {
    const { loading } = this.props
    const { level, selectMonth, selectYear, yearlist, levels, datelist, monthlist } = this.state
    const _levelName = {day: '日', month: '月', year: '年'}
    console.log(loading)
    return (
      <div className="mk-calendar">
        {loading ? <div className="loading-data"><Spin /></div> : null}
        <div className="mk-calendar-control">
          <Select value={selectYear} onChange={this.yearChange}>
            {yearlist.map(item => (<Option key={item} value={item}>{item}年</Option>))}
@@ -341,8 +341,8 @@
                          </div>
                          <ul className="content">
                            {d.subData.map((data, index) => (
                              <li key={index} className="message" title={data.remark}>
                                <Badge color={data.color} text={data.remark} />
                              <li key={index} className="message">
                                <Badge color={d.style ? (data.color === d.style.background ? '#ffffff' : data.color) : data.color} text={data.remark} />
                              </li>
                            ))}
                          </ul>
@@ -357,14 +357,14 @@
          {level === 'year' && monthlist ? <Row className="year-calendar">
            {datelist.map(item => (
              <Col span={8} key={item.month}>
                <div className="year-wrap">
                  <div className="header">
                <div className="year-wrap" style={item.style || null} onClick={() => this.triggerDay(item)}>
                  <div className="header" style={item.style ? null : {color: '#1890ff'}}>
                    {item.label}
                  </div>
                  <ul className="content">
                    {item.subData.map((data, index) => (
                      <li key={index} className="message" title={data.remark}>
                        <Badge color={data.color} text={data.remark} />
                      <li key={index} className="message">
                        <Badge color={item.style ? (data.color === item.style.background ? '#ffffff' : data.color) : data.color} text={`${data.remark}(${data.startTime} ~ ${data.endTime})`}/>
                      </li>
                    ))}
                  </ul>
src/tabviews/zshare/calendar/index.scss
@@ -1,7 +1,23 @@
.mk-calendar {
  position: relative;
  width: 100%;
  padding: 20px;
  .loading-data {
    position: absolute;
    top: 0;
    left: 20px;
    right: 20px;
    bottom: 0;
    z-index: 2;
    opacity: 0.5;
    background: #ffffff;
    .ant-spin-spinning {
      position: absolute;
      left: 50%;
      top: 270px;
    }
  }
  .mk-calendar-control {
    text-align: right;
    .ant-select {
@@ -69,8 +85,10 @@
              .month-wrap {
                cursor: pointer;
                height: 120px;
                width: 100%;
                width: calc(100% - 2px);
                transition: background 0.1s;
                margin-bottom: 2px;
                box-shadow: 0px 0px 1px #f7f7f7;
                .header {
                  text-align: center;
                  font-size: 16px;
@@ -97,8 +115,8 @@
                }
                .content::-webkit-scrollbar-thumb {
                  border-radius: 5px;
                  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.09);
                  background: rgba(0, 0, 0, 0.09);
                  box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.7);
                  background: rgba(255, 255, 255, 0.7);
                }
                .content::-webkit-scrollbar-track {
                  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
@@ -117,17 +135,22 @@
    }
    .year-calendar {
      .year-wrap {
        width: calc(100% - 2px);
        cursor: pointer;
        transition: background 0.1s;
        box-shadow: 0px 0px 1px #f7f7f7;
        .header {
          text-align: center;
          font-size: 16px;
          color: #1890ff;
        }
        .content {
          padding: 5px 15px 10px;
          height: 110px;
          overflow-y: auto;
          margin-bottom: 2px;
          .ant-badge-status-text {
            color: inherit;
          }
          .message {
            width: 100%;
            white-space: nowrap;
@@ -140,8 +163,8 @@
        }
        .content::-webkit-scrollbar-thumb {
          border-radius: 5px;
          box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.09);
          background: rgba(0, 0, 0, 0.09);
          box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.7);
          background: rgba(255, 255, 255, 0.7);
        }
        .content::-webkit-scrollbar-track {
          box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
src/tabviews/zshare/mutilform/index.jsx
@@ -579,7 +579,7 @@
              })(
                <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.Option key={option.name} style={{background: option.value, color: '#ffffff'}} value={option.name}>{option.name}</Select.Option>
                  )}
                </Select>
              )}
src/templates/calendarconfig/calcomponent/calendarform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Select, Checkbox } from 'antd'
import { Form, Row, Col, Select, Checkbox, Tooltip, Icon, Radio } from 'antd'
import './index.scss'
class MainTab extends Component {
@@ -130,6 +130,23 @@
              )}
            </Form.Item>
          </Col>
          <Col span={24}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="开启后,使用系统函数时会自动替换数据源及自定义脚本中的calendarDate与calendarDate1,其值分别为选择年份的开始和结束时间,使用自定义函数时,会增加calendarDate传参,其值为选择年份。">
                <Icon type="question-circle" />
                数据刷新
              </Tooltip>
            }>
              {getFieldDecorator('refresh', {
                initialValue: calendar.refresh || 'false',
              })(
                <Radio.Group>
                  <Radio key="true" value="true">开启</Radio>
                  <Radio key="false" value="false">关闭</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
src/templates/calendarconfig/calcomponent/index.scss
@@ -2,6 +2,7 @@
  position: absolute;
  right: 0;
  top: 0;
  z-index: 2;
  >.anticon-edit {
    font-size: 18px;
    padding: 5px;
src/templates/calendarconfig/index.jsx
@@ -94,6 +94,12 @@
      })
    }
    if (_config.type === 'user') {
      if (_config.tab) {
        _config.tab.linkTab = ''
      }
    }
    this.setState({
      openEdition: menu.open_edition || '',
      optionLibs: optionLibs,
@@ -120,10 +126,10 @@
      {color: 'orange', remark: '系统异常,请及时处理!'},
      {color: 'yellow', remark: '您的订单异常,请联系客服!'},
      {color: 'green', remark: '您的订单已完成。'},
      {color: 'lightgreen', remark: '消息已发送,请及时查收。'},
      {color: 'cyan', remark: '您有一条新的消息。'},
      {color: 'blue', remark: '任务未完成,请注意后续工作。'},
      {color: 'purple', remark: '您有新的任务等待处理!'}
      {color: 'purple', remark: '您有新的任务等待处理!'},
      {color: 'gray', remark: '您有一封未读邮件。'}
    ]
    let mockdata = []
@@ -269,6 +275,12 @@
    this.menuformRef.handleConfirm().then(res => {
      if (config.isAdd) {
        config.search = config.search.filter(item => !item.origin)
      }
      if (config.type === 'user') { // 使用已有菜单时,默认添加关联标签id
        if (config.tab && !config.tab.linkTab) {
          config.tab.linkTab = Utils.getuuid()
        }
      }
      let _LongParam = ''
@@ -682,15 +694,26 @@
   */
  verifyconfig = (config) => {
    let hasKey = false
    let chartcols = []
    let cols = []
    config.columns.forEach(col => {
      if (col.field) {
        chartcols.push(col.field)
        cols.push(col.field)
      }
      if (config.setting.primaryKey === col.field) {
        hasKey = true
      }
    })
    let calvaild = true
    if (!cols.includes(config.calendar.startfield)) {
      calvaild = false
    } else if (!cols.includes(config.calendar.endfield)) {
      calvaild = false
    } else if (!cols.includes(config.calendar.colorfield)) {
      calvaild = false
    } else if (!cols.includes(config.calendar.remarkfield)) {
      calvaild = false
    }
    if (config.setting.interType === 'inner' && !config.setting.innerFunc && config.setting.default !== 'false' && !config.setting.dataresource) {
      return '菜单尚未设置数据源,不可启用!'
@@ -698,6 +721,8 @@
      return '菜单尚未设置主键,不可启用!'
    } else if (!hasKey) {
      return '显示列中不存在主键字段,不可启用!'
    } else if (!calvaild) {
      return '日历关联字段未设置,不可启用!'
    } else {
      return true
    }
@@ -778,7 +803,7 @@
    const { activeKey, config, tabviews, mockdata, mockloading } = this.state
    return (
      <div className="model-subtable-board">
      <div className="model-calendar-board">
        <DndProvider backend={HTML5Backend}>
          {/* 工具栏 */}
          <div className="tools">
src/templates/calendarconfig/index.scss
@@ -1,4 +1,4 @@
.model-subtable-board {
.model-calendar-board {
  position: fixed;
  z-index: 1070;
  padding-top: 48px;
@@ -50,16 +50,7 @@
        }
      }
    }
    .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 {
@@ -159,21 +150,6 @@
      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;
src/templates/calendarconfig/source.jsx
@@ -32,7 +32,8 @@
      startfield: '',
      endfield: '',
      colorfield: '',
      remarkfield: ''
      remarkfield: '',
      refresh: 'false'
    },
    search: [
      {
src/templates/calendarconfig/tabcomponent/index.scss
@@ -1,6 +1,7 @@
.model-calendar-tab {
  position: absolute;
  top: 0;
  z-index: 2;
  >.anticon-plus {
    position: absolute;
    font-size: 18px;
src/templates/sharecomponent/datasourcecomponent/verifycard/customscript/index.jsx
@@ -185,7 +185,7 @@
          </Col>
          <Col span={24} className="sqlfield">
            <Form.Item label={'可用字段'}>
              id, bid, loginuid, sessionuid, userid, appkey, time_id{usefulFields ? ', ' + usefulFields : ''}
              id, bid, loginuid, sessionuid, userid, appkey, time_id, calendarDate, calendarDate1{usefulFields ? ', ' + usefulFields : ''}
            </Form.Item>
          </Col>
          <Col span={10}>
src/templates/sharecomponent/datasourcecomponent/verifycard/settingform/index.jsx
@@ -236,10 +236,10 @@
                {getFieldDecorator('primaryKey', {
                  initialValue: setting.primaryKey || ''
                })(
                  <Select onChange={(value) => {this.selectChange('primaryKey', value)}}>
                  <Select>
                    {columns.map((option, i) =>
                      <Select.Option key={i} value={option.value}>
                        {option.text}
                      <Select.Option key={i} value={option.field}>
                        {option.label}
                      </Select.Option>
                    )}
                  </Select>
src/utils/option.js
@@ -309,16 +309,14 @@
}]
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)'}
  {name: 'red', value: '#d0021b'},
  {name: 'orange', value: '#f5a623'},
  {name: 'yellow', value: '#f8e71c'},
  {name: 'green', value: '#7ed321'},
  {name: 'cyan', value: '#50e3c2'},
  {name: 'blue', value: '#1890ff'},
  {name: 'purple', value: '#bd10e0'},
  {name: 'gray', value: '#9b9b9b'},
]
// 显示列标记色系