king
2020-01-16 1a67732f77de8afd138b6e75235edcc4c0e9a166
2020-01-16
5 文件已重命名
26个文件已修改
9个文件已添加
5个文件已删除
3934 ■■■■■ 已修改文件
src/components/header/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/sidemenu/editthdmenu/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.jsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/zh-CN/comtable.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx 89 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/formtab/index.jsx 806 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/formtab/index.scss 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/formtab/mainTable/index.jsx 393 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/formtab/mainTable/index.scss 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtable/index.jsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/tableshare/actionList/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/tabdragelement/card.jsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/tabdragelement/index.scss 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/actionform/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/dragelement/card.jsx 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/dragelement/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/groupform/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.jsx 1014 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.scss 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/modalform/index.jsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/settingform/index.jsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/source.jsx 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/tabdragelement/index.jsx 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/tabform/index.jsx 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/tabform/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/card.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/actionform/index.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/index.jsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/colspanform/index.jsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelement/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelform/card.jsx 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelform/index.jsx 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelform/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelform/itemtypes.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelform/source.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/formconfig.js 289 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/tabdragelement/card.jsx 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/tabdragelement/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/tabdragelement/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/tabform/index.jsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/tabform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.jsx
@@ -182,7 +182,7 @@
          }
          return item
        }),
        systems: result.Systems.filter(sys => sys.LinkUrl1)
        systems: result.Systems.filter(sys => sys.LinkUrl1 && sys.AppKey !== window.GLOB.appkey)
      })
    } else {
      notification.error({
src/components/sidemenu/editthdmenu/index.jsx
@@ -486,7 +486,7 @@
      })
    } else if (type === 'tabview') { // 三级菜单下,打开新标签页或当前页跳转,类型的按钮配置
      let pageParam = ''
      if (config && config.type === 'formTab') {
      if (config && config.type === 'FormTab') {
        pageParam = config
      }
src/components/tabview/index.jsx
@@ -47,12 +47,9 @@
      } else {
        tab.selected = false
      }
      if (menu.type === 'TabForm' || menu.type === 'iframe') {
        return tab.MenuID !== menu.MenuID
      } else {
        return tab.MenuNo !== menu.MenuNo
      }
      return tab.MenuID !== menu.MenuID
    })
    if (menu.MenuID === this.state.selectedTabId) {
      tabs[0] && (tabs[0].selected = true)
    }
@@ -90,6 +87,8 @@
      return (<Comps.RoleManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
    } else if (view.type === 'TabForm') {
      return (<Comps.TabForm MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
    } else if (view.type === 'FormTab') {
      return (<Comps.FormTab MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
    } else if (view.type === 'iframe') {
      return (<Comps.Iframe key={view.MenuID} title={view.MenuName} MenuName={view.MenuName} url={service + view.LinkUrl}/>)
    } else {
@@ -166,6 +165,8 @@
          Comps.RoleManage = asyncComponent(() => import('@/tabviews/rolemanage'))
        } else if (!Comps.TabForm && newtab.type === 'TabForm') {
          Comps.TabForm = asyncComponent(() => import('@/tabviews/tabform'))
        } else if (!Comps.TabForm && newtab.type === 'FormTab') {
          Comps.FormTab = asyncComponent(() => import('@/tabviews/formtab'))
        }
      }
src/locales/zh-CN/comtable.js
@@ -172,6 +172,7 @@
  'header.form.request.method': '请求方式',
  'header.form.readonly': '是否只读',
  'header.form.field.required': '是否必填',
  'header.modal.form.edit': '表单-编辑',
  'header.modal.search.edit': '搜索条件-编辑',
  'header.modal.action.edit': '按钮-编辑',
  'header.modal.action.copy': '按钮-复制',
src/tabviews/commontable/index.jsx
@@ -1,5 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { BackTop, notification, Spin, Tabs, Icon, Switch, Modal, Button} from 'antd'
import moment from 'moment'
@@ -10,6 +11,7 @@
import SubTable from '@/tabviews/subtable'
import NotFount from '@/components/404'
import asyncComponent from '@/utils/asyncLoadComponent'
import {refreshTabView, modifyTabview} from '@/store/action'
import zhCN from '@/locales/zh-CN/main.js'
import enUS from '@/locales/en-US/main.js'
import Utils from '@/utils/utils.js'
@@ -18,7 +20,7 @@
const SubTabTable = asyncComponent(() => import('@/tabviews/subtabtable'))
const { TabPane } = Tabs
export default class NormalTable extends Component {
class NormalTable extends Component {
  static propTpyes = {
    MenuNo: PropTypes.string,    // 菜单参数
    MenuName: PropTypes.string,  // 菜单参数
@@ -58,6 +60,8 @@
   * @description 获取页面配置信息
   */
  async loadconfig () {
    const { permAction } = this.props
    let param = {
      func: 'sPC_Get_LongParam',
      MenuID: this.props.MenuID
@@ -97,6 +101,13 @@
      let _hideCol = []      // 隐藏及合并列中字段的uuid集
      let colMap = new Map()
      // 权限过滤
      config.action = config.action.filter(item => permAction[item.uuid])
      // config.tabgroups.forEach(group => {
      //   if (!config[group]) return
      //   config[group] = config[group].filter(tab => permAction[tab.uuid])
      // })
      // 1、筛选字段集,2、过滤隐藏列及合并列中的字段uuid
      config.columns.forEach(col => {
        if (col.field) {
@@ -129,16 +140,18 @@
        }
      })
      // 添加操作列(存在时)(未经过权限过滤)
      if (config.gridBtn && config.gridBtn.display) {
      let _actions = config.action.filter(item => item.position === 'toolbar') // 过滤工具栏按钮
      let _operations = config.action.filter(item => item.position === 'grid') // 添加操作列(存在时)
      if (config.gridBtn && config.gridBtn.display && _operations.length > 0) {
        _columns.push({
          ...config.gridBtn,
          operations: config.action.filter(item => item.position === 'grid')
          operations: _operations
        })
      }
      let _actions = config.action.filter(item => item.position === 'toolbar') // 过滤工具栏按钮(未经过权限过滤)
      let _isLinkMain = false        // 检查是否有与主表关联的子表
      let _isLinkMain = false // 检查是否有与主表关联的子表
      let supmenus = {}
      config.tabgroups.forEach(group => {
        if (config[group] && config[group].length > 0) {
@@ -209,6 +222,11 @@
      if (item.resourceType === '1' && item.dataSource) {
        let arrfield = item.valueField + ',' + item.valueText
        if (item.valueField === item.valueText) { // value 与 text 字段相同时
          arrfield = item.valueField
        }
        if (item.type === 'link') {
          arrfield = arrfield + ',' + item.linkField
        }
@@ -630,11 +648,41 @@
   * @description 触发按钮弹窗(标签页)
   */
  triggerPopview = (btn, data) => {
    this.setState({
      popAction: btn,
      popData: data[0] ? data[0] : null,
      visible: true
    })
    console.log(btn)
    if (btn.OpenType === 'popview') {
      this.setState({
        popAction: btn,
        popData: data[0] ? data[0] : null,
        visible: true
      })
    } else if (btn.OpenType === 'tab') {
      // const { tabviews, MenuNo, MenuID } = this.props
      // let newtab = {
      //   MenuNo: MenuNo,
      //   MenuID: btn.uuid,
      //   MenuName: btn.label,
      //   type: btn.tabTemplate,
      //   selected: true,
      //   param: {
      //     btn: btn,
      //     data: data
      //   }
      // }
      // let index = 0
      // let tabs = tabviews.map((tab, i) => {
      //   if (tab.MenuID === MenuID) {
      //     index = i
      //   }
      //   tab.selected = false
      //   return tab
      // })
      // tabs.splice(index + 1, 0, newtab)
      // this.props.modifyTabview(tabs)
    }
  }
  popclose = () => {
@@ -770,4 +818,21 @@
      </div>
    )
  }
}
}
const mapStateToProps = (state) => {
  return {
    tabviews: state.tabviews,
    refreshTab: state.refreshTab,
    permAction: state.permAction
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    refreshTabView: (refreshTab) => dispatch(refreshTabView(refreshTab)),
    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalTable)
src/tabviews/formtab/index.jsx
New file
@@ -0,0 +1,806 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { BackTop, notification, Spin, Tabs, Icon, Switch, Modal, Button} from 'antd'
import moment from 'moment'
import Api from '@/api'
import MainTable from './mainTable'
import MainAction from '@/tabviews/tableshare/actionList'
import MainSearch from '@/tabviews/tableshare/topSearch'
import SubTable from '@/tabviews/subtable'
import NotFount from '@/components/404'
import asyncComponent from '@/utils/asyncLoadComponent'
import {refreshTabView} from '@/store/action'
import zhCN from '@/locales/zh-CN/main.js'
import enUS from '@/locales/en-US/main.js'
import Utils from '@/utils/utils.js'
import './index.scss'
const SubTabTable = asyncComponent(() => import('@/tabviews/subtabtable'))
const { TabPane } = Tabs
class NormalTable extends Component {
  static propTpyes = {
    MenuNo: PropTypes.string,    // 菜单参数
    MenuName: PropTypes.string,  // 菜单参数
    MenuID: PropTypes.string     // 菜单Id
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    loadingview: true,    // 页面加载中
    viewlost: false,      // 页面丢失:1、未获取到配置-页面丢失;2、页面未启用
    lostmsg: '',          // 页面丢失时的提示信息
    config: {},           // 页面配置信息,包括按钮、搜索、显示列、标签等
    searchlist: null,     // 搜索条件
    actions: null,        // 按钮集
    columns: null,        // 显示列
    arr_field: '',        // 使用 sPC_Get_TableData 时的查询字段集
    setting: null,        // 页面全局设置:数据源、按钮及显示列固定、主键等
    data: null,           // 列表数据集
    total: 0,             // 总数
    loading: false,       // 列表数据加载中
    pageIndex: 1,         // 页码
    pageSize: 10,         // 每页数据条数
    orderColumn: '',      // 排序字段
    orderType: 'asc',     // 排序方式
    search: '',           // 搜索条件数组,使用时需分场景处理
    configMap: {},        // 页面配置信息:下拉、按钮等
    BIDs: {},             // 上级表id
    setsingle: false,     // 主表单选多选切换
    pickup: false,        // 主表数据隐藏显示切换
    isLinkMain: false,    // 是否存在与主表关联的子表
    popAction: false,     // 弹框页面,按钮信息
    popData: false,       // 弹框页面,所选的表格数据
    visible: false        // 弹框显示隐藏控制
  }
  /**
   * @description 获取页面配置信息
   */
  async loadconfig () {
    const { permAction } = this.props
    let param = {
      func: 'sPC_Get_LongParam',
      MenuID: this.props.MenuID
    }
    let result = await Api.getSystemCacheConfig(param)
    if (result.status) {
      let config = ''
      try { // 配置信息解析
        config = window.decodeURIComponent(window.atob(result.LongParam))
        config = JSON.parse(config)
      } catch (e) {
        config = ''
      }
      // 页面配置解析错误时提示
      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
      }
      let _arrField = []     // 字段集
      let _columns = []      // 显示列
      let _hideCol = []      // 隐藏及合并列中字段的uuid集
      let colMap = new Map()
      // 权限过滤
      config.action = config.action.filter(item => permAction[item.uuid])
      // config.tabgroups.forEach(group => {
      //   if (!config[group]) return
      //   config[group] = config[group].filter(tab => permAction[tab.uuid])
      // })
      // 1、筛选字段集,2、过滤隐藏列及合并列中的字段uuid
      config.columns.forEach(col => {
        if (col.field) {
          _arrField.push(col.field)
        }
        if (col.type === 'colspan' && col.sublist) { // 筛选隐藏列
          _hideCol = _hideCol.concat(col.sublist)
        } else if (col.Hide === 'true') {
          _hideCol.push(col.uuid)
        }
        colMap.set(col.uuid, col)
      })
      // 生成显示列,处理合并列中的字段
      config.columns.forEach(col => {
        if (_hideCol.includes(col.uuid)) return
        if (col.type === 'colspan' && col.sublist) {
          let _col = JSON.parse(JSON.stringify(col))
          let subColumn = []
          _col.sublist.forEach(sub => {
            if (colMap.has(sub)) {
              subColumn.push(colMap.get(sub))
            }
          })
          _col.subColumn = subColumn
          _columns.push(_col)
        } else {
          _columns.push(col)
        }
      })
      let _actions = config.action.filter(item => item.position === 'toolbar') // 过滤工具栏按钮
      let _operations = config.action.filter(item => item.position === 'grid') // 添加操作列(存在时)
      if (config.gridBtn && config.gridBtn.display && _operations.length > 0) {
        _columns.push({
          ...config.gridBtn,
          operations: _operations
        })
      }
      let _isLinkMain = false // 检查是否有与主表关联的子表
      let supmenus = {}
      config.tabgroups.forEach(group => {
        if (config[group] && config[group].length > 0) {
          config[group] = config[group].map(tab => {
            if (tab.subtabs && tab.subtabs.length > 0) {
              tab.subtabs.forEach(id => {
                supmenus[id] = tab.uuid
              })
            }
            if (config.setting.subtabs.includes(tab.uuid)) {
              tab.supMenu = 'mainTable'
              _isLinkMain = true
            } else if (supmenus[tab.uuid]) {
              tab.supMenu = supmenus[tab.uuid]
            }
            return tab
          })
        }
      })
      this.setState({
        loadingview: false,
        config: config,
        setting: config.setting,
        searchlist: config.search,
        actions: _actions,
        columns: _columns,
        isLinkMain: _isLinkMain,
        arr_field: _arrField.join(','),
        search: Utils.initMainSearch(config.search), // 搜索条件初始化(含有时间格式,需要转化)
        loading: true
      }, () => {
        this.improveSearch()
        if (config.setting.onload !== 'false') {
          this.loadmaindata()
        }
      })
    } else {
      this.setState({
        loadingview: false,
        viewlost: true
      })
      notification.warning({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
  /**
   * @description 搜索条件下拉选项预加载
   */
  improveSearch = () => {
    let searchlist = JSON.parse(JSON.stringify(this.state.searchlist))
    let deffers = []
    searchlist.forEach(item => {
      if (item.type !== 'multiselect' && item.type !== 'select' && item.type !== 'link') return
      if (item.setAll === 'true') {
        item.options.unshift({
          key: Utils.getuuid(),
          Value: '',
          Text: this.state.dict['main.all']
        })
      }
      if (item.resourceType === '1' && item.dataSource) {
        let arrfield = item.valueField + ',' + item.valueText
        if (item.valueField === item.valueText) { // value 与 text 字段相同时
          arrfield = item.valueField
        }
        if (item.type === 'link') {
          arrfield = arrfield + ',' + item.linkField
        }
        let _sql = Utils.getSelectQuerySql(item)
        _sql = Utils.formatOptions(_sql)
        let param = {
          func: 'sPC_Get_SelectedList',
          LText: _sql,
          obj_name: 'data',
          arr_field: arrfield
        }
        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
        let defer = new Promise(resolve => {
          Api.getSystemCacheConfig(param).then(res => {
            res.search = item
            resolve(res)
          })
        })
        deffers.push(defer)
      } else if (item.resourceType === '1' && !item.dataSource) {
        notification.warning({
          top: 92,
          message: item.label + ': ' + this.state.dict['main.datasource.settingerror'],
          duration: 10
        })
      }
    })
    if (deffers.length === 0) {
      this.setState({searchlist: JSON.parse(JSON.stringify(searchlist))})
      return
    }
    Promise.all(deffers).then(result => {
      result.forEach(res => {
        if (res.status) {
          searchlist = searchlist.map(item => {
            if (item.uuid === res.search.uuid) {
              res.data.forEach(cell => {
                let _item = {
                  key: Utils.getuuid(),
                  Value: cell[res.search.valueField],
                  Text: cell[res.search.valueText]
                }
                if (res.search.type === 'link') {
                  _item.parentId = cell[res.search.linkField]
                }
                item.options.push(_item)
              })
            }
            return item
          })
        } else {
          notification.warning({
            top: 92,
            message: res.search.label + ':' + res.message,
            duration: 10
          })
        }
      })
      this.setState({searchlist})
    })
  }
  /**
   * @description 主表数据加载
   */
  async loadmaindata () {
    const { setting, BIDs } = this.state
    let param = ''
    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
      param = this.getCustomParam()
    } else {
      param = this.getDefaultParam()
    }
    this.setState({
      pickup: false
    })
    this.handleTableId('mainTable', '')
    let result = await Api.genericInterface(param)
    if (result.status) {
      this.setState({
        data: result.data.map((item, index) => {
          item.key = index
          return item
        }),
        total: result.total,
        loading: false,
        BIDs: {
          ...BIDs,
          mainTable: ''
        }
      })
    } else {
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 15
      })
    }
  }
  /**
   * @description 获取用户自定义存储过程传参
   */
  getCustomParam = () => {
    const { pageIndex, pageSize, orderColumn, orderType, search, setting } = this.state
    let _search = Utils.formatCustomMainSearch(search)
    let param = {
      PageIndex: pageIndex,
      PageSize: pageSize,
      OrderCol: orderColumn,
      OrderType: orderType,
      ..._search
    }
    if (setting.interType === 'inner') {
      param.func = setting.innerFunc
    } else {
      param.rduri = setting.interface
      if (setting.outerFunc) {
        param.func = setting.outerFunc
      }
    }
    return param
  }
  /**
   * @description 获取系统存储过程 sPC_Get_TableData 的参数
   */
  getDefaultParam = () => {
    const { arr_field, pageIndex, pageSize, orderColumn, orderType, search, setting } = this.state
    let _search = Utils.joinMainSearchkey(search)
    _search = _search ? 'where ' + _search : ''
    let param = {
      func: 'sPC_Get_TableData',
      obj_name: 'data',
      arr_field: arr_field
    }
    let orderBy = orderColumn ? (orderColumn + ' ' + orderType) : setting.order
    let _dataresource = setting.dataresource
    if (/\s/.test(_dataresource)) {
      _dataresource = '(' + _dataresource + ') tb'
    }
    let LText = `select top ${pageSize} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable where rows > ${pageSize * (pageIndex - 1)} order by tmptable.rows`
    let DateCount = `select count(1) as total from ${_dataresource} ${_search}`
    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 = Utils.formatOptions(DateCount)
    return param
  }
  /**
   * @description 搜索条件改变时,重置表格数据
   * 含有初始不加载的页面,修改设置
   */
  refreshbysearch = (searches) => {
    const { setting } = this.state
    if (setting.onload === 'false') {
      this.setState({
        loading: true,
        pageIndex: 1,
        search: searches,
        setting: {...setting, onload: 'true'}
      }, () => {
        this.loadmaindata()
      })
    } else {
      this.refs.mainTable.resetTable()
      this.setState({
        loading: true,
        pageIndex: 1,
        search: searches
      }, () => {
        this.loadmaindata()
      })
    }
  }
  /**
   * @description 表格条件改变时重置数据(分页或排序)
   */
  refreshbytable = (pagination, filters, sorter) => {
    if (sorter.order) {
      let _chg = {
        ascend: 'asc',
        descend: 'desc'
      }
      sorter.order = _chg[sorter.order]
    }
    this.setState({
      loading: true,
      pageIndex: pagination.current,
      pageSize: pagination.pageSize,
      orderColumn: sorter.field || this.state.setting.orderColumn,
      orderType: sorter.order || 'asc'
    }, () => {
      this.loadmaindata()
    })
  }
  /**
   * @description 表格刷新
   */
  reloadtable = () => {
    this.refs.mainTable.resetTable()
    this.setState({
      loading: true,
      pageIndex: 1
    }, () => {
      this.loadmaindata()
    })
  }
  /**
   * @description 页面刷新,重新获取配置
   */
  reloadview = () => {
    this.setState({
      loadingview: true,
      viewlost: false,
      lostmsg: '',
      config: {},
      searchlist: null,
      actions: null,
      columns: null,
      arr_field: '',
      setting: null,
      data: null,
      total: 0,
      loading: false,
      pageIndex: 1,
      pageSize: 10,
      orderColumn: '',
      orderType: 'asc',
      search: '',
      configMap: {},
      BIDs: {},
      setsingle: false,
      pickup: false,
      isLinkMain: false
    }, () => {
      this.loadconfig()
    })
  }
  /**
   * @description 按钮操作完成后(成功或失败),页面刷新,重置页码及选择项
   */
  refreshbyaction = (btn, type) => {
    if (btn.execSuccess === 'grid' && type === 'success') {
      this.reloadtable()
    } else if (btn.execError === 'grid' && type === 'error') {
      this.reloadview()
    } else if (btn.execSuccess === 'view' && type === 'success') {
      this.reloadtable()
    } else if (btn.execError === 'view' && type === 'error') {
      this.reloadview()
    } else if (btn.popClose === 'view' && type === 'pop') {
      this.reloadview()
    } else if (btn.popClose === 'grid' && type === 'pop') {
      this.reloadtable()
    } else if (type === 'excelOut') {
      this.handleDefaultExcelout(btn)
    }
  }
  /**
   * @description 子表操作完成后刷新主表
   */
  handleMainTable = () => {
    this.reloadtable()
  }
  /**
   * @description 使用默认存储过程 sPC_Get_TableData 导出excel表格
   */
  handleDefaultExcelout = (btn) => {
    const { MenuName } = this.props
    const { arr_field, orderColumn, orderType, search, setting, config } = this.state
    let _arr_labels = []      // 列名称集
    let _arr_label_field = [] // 列名称字段集
    config.columns.forEach(col => {
      if (col.field) {
        _arr_labels.push(col.label)
        _arr_label_field.push(`${col.field} as ${col.label}`)
      }
    })
    _arr_labels = _arr_labels.join(',')
    _arr_label_field = _arr_label_field.join(',')
    let _search = Utils.joinMainSearchkey(search)
    _search = _search ? 'where ' + _search : ''
    // 获取excel数据,与获取列表数据不同为未设置页码等参数
    let param = {
      func: 'sPC_Get_TableData',
      obj_name: 'data',
      arr_field: _arr_labels
    }
    let orderBy = orderColumn ? (orderColumn + ' ' + orderType) : setting.order
    let _dataresource = setting.dataresource
    if (/\s/.test(_dataresource)) {
      _dataresource = '(' + _dataresource + ') tb'
    }
    let LText = `select ${_arr_label_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable order by tmptable.rows`
    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 = ''
    let name = `${MenuName}${moment().format('YYYYMMDDHHmmss')}.xlsx`
    Api.getExcelOut(param, name).then(res => {
      if (res && res.status === false) {
        this.refs.mainButton.execError(res, btn)
      } else {
        this.refs.mainButton.execSuccess(btn)
      }
    })
  }
  /**
   * @description 获取表格选择项
   */
  gettableselected = () => {
    let data = []
    this.refs.mainTable.state.selectedRowKeys.forEach(item => {
      data.push(this.refs.mainTable.props.data[item])
    })
    return data
  }
  /**
   * @description 表格中,按钮触发事件传递
   */
  buttonTrigger = (btn, record) => {
    this.refs.mainButton.actionTrigger(btn, record)
  }
  /**
   * @description 表格Id变化
   */
  handleTableId = (type, id) => {
    const { BIDs } = this.state
    this.setState({
      BIDs: {
        ...BIDs,
        [type]: id
      }
    })
  }
  /**
   * @description 表格单选多选切换
   */
  checkChange = () => {
    const { setsingle, BIDs } = this.state
    let _BIDs = JSON.parse(JSON.stringify(BIDs))
    _BIDs.mainTable = ''
    this.setState({
      setsingle: !setsingle,
      pickup: false,
      BIDs: _BIDs
    })
  }
  /**
   * @description 数据展开合并切换
   */
  pickupChange = () => {
    const { pickup } = this.state
    this.setState({
      pickup: !pickup
    })
  }
  /**
   * @description 触发按钮弹窗(标签页)
   */
  triggerPopview = (btn, data) => {
    this.setState({
      popAction: btn,
      popData: data[0] ? data[0] : null,
      visible: true
    })
  }
  popclose = () => {
    this.setState({
      visible: false
    })
    this.refreshbyaction(this.state.popAction, 'pop')
  }
  UNSAFE_componentWillMount () {
    // 组件加载时,获取菜单数据
    this.loadconfig()
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
    const { setting, searchlist, actions, columns, loadingview, viewlost, setsingle, pickup, isLinkMain, config } = this.state
    return (
      <div className={'commontable ' + (isLinkMain ? 'pick-control' : '')} id={'commontable' + this.props.MenuID}>
        {loadingview && <Spin size="large" />}
        {searchlist && searchlist.length > 0 ?
          <MainSearch
            dict={this.state.dict}
            searchlist={searchlist}
            refreshdata={this.refreshbysearch}
          /> : null
        }
        {actions && setting.onload !== 'false' ?
          <MainAction
            ref="mainButton"
            BID=""
            type="main"
            setting={setting}
            actions={actions}
            dict={this.state.dict}
            MenuID={this.props.MenuID}
            refreshdata={this.refreshbyaction}
            triggerPopview={this.triggerPopview}
            gettableselected={this.gettableselected}
          /> : null
        }
        {columns && setting.onload !== 'false' ?
          <div className="main-table-box">
            {isLinkMain ?
              <div className="pickchange">
                {setting.tableType === 'checkbox' ? <Switch title="单选切换" checkedChildren="单" unCheckedChildren="多" defaultChecked={setsingle} onChange={this.checkChange} /> : null}
                {this.state.BIDs.mainTable && (setting.tableType === 'radio' || setsingle) ? <Switch title="收起" checkedChildren="开" unCheckedChildren="关" defaultChecked={pickup} onChange={this.pickupChange} /> : null}
              </div> : null
            }
            <MainTable
              ref="mainTable"
              pickup={pickup}
              setting={setting}
              columns={columns}
              setsingle={setsingle}
              dict={this.state.dict}
              data={this.state.data}
              total={this.state.total}
              MenuID={this.props.MenuID}
              loading={this.state.loading}
              refreshdata={this.refreshbytable}
              buttonTrigger={this.buttonTrigger}
              handleTableId={this.handleTableId}
            />
          </div> : null
        }
        {setting && setting.onload !== 'false' &&
          config.tabgroups.map(group => {
            if (config[group].length === 0) return null
            return (
              <Tabs defaultActiveKey="0" key={group}>
                {config[group].map((_tab, index) => {
                  // return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
                  return (
                    <TabPane tab={
                      <span>
                        {_tab.icon ? <Icon type={_tab.icon} /> : null}
                        {_tab.label}
                      </span>
                    } key={`${index}`}>
                      {_tab.type === 'SubTable' ?
                        <SubTable
                          Tab={_tab}
                          MenuID={_tab.linkTab}
                          SupMenuID={this.props.MenuID}
                          BID={this.state.BIDs[_tab.supMenu] || ''}
                          handleTableId={this.handleTableId}
                          handleMainTable={this.handleMainTable}
                        /> : null}
                    </TabPane>
                  )
                })}
              </Tabs>
            )
          })
        }
        <Modal
          className="popview-modal"
          title={this.state.popAction.label}
          width={'80vw'}
          maskClosable={false}
          visible={this.state.visible}
          onCancel={this.popclose}
          footer={[
            <Button key="cancel" onClick={this.popclose}>{this.state.dict['main.close']}</Button>
          ]}
          destroyOnClose
        >
          {<SubTabTable SupMenuID={this.props.MenuID} MenuID={this.state.popAction.linkTab} BID={''} ID={this.state.popData ? this.state.popData[setting.primaryKey] : ''} />}
        </Modal>
        <BackTop>
          <div className="ant-back-top">
            <div className="ant-back-top-content">
              <div className="ant-back-top-icon"></div>
            </div>
          </div>
        </BackTop>
        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    refreshTab: state.refreshTab,
    permAction: state.permAction
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    refreshTabView: (refreshTab) => dispatch(refreshTabView(refreshTab))
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalTable)
src/tabviews/formtab/index.scss
New file
@@ -0,0 +1,88 @@
.commontable {
  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: fixed;
    left: calc(50vw - 22px);
    top: calc(50vh - 70px);
  }
  > .ant-tabs {
    padding: 0px 20px;
    .ant-tabs-tab:not(.ant-tabs-tab-active) {
      cursor: pointer;
    }
  }
  > .ant-card {
    margin: 0 20px 20px;
    > .ant-card-head {
      border: 0;
      padding: 0;
      min-height: 30px;
      .ant-card-head-title {
        padding: 10px 0 0;
        span {
          color: #1890ff;
          display: inline-block;
          padding: 0 10px;
          font-size: 15px;
          border-bottom: 1px solid #1890ff;
          i {
            margin-right: 10px;
          }
        }
      }
    }
    > .ant-card-body {
      padding: 0;
    }
  }
  .main-table-box {
    position: relative;
    .pickchange {
      position: absolute;
      right: 0px;
      top: -25px;
      .ant-switch {
        z-index: 1;
        float: right;
        margin-right: 20px;
        margin-bottom: 5px;
      }
    }
  }
  .ant-tabs + .ant-tabs {
    margin-top: 20px;
  }
}
.commontable.pick-control {
  >.button-list {
    padding-right: 140px;
  }
}
.ant-back-top {
  bottom: 30px;
  right: 30px;
}
.popview-modal {
  .ant-modal-body {
    min-height: 300px;
  }
}
src/tabviews/formtab/mainTable/index.jsx
New file
@@ -0,0 +1,393 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Table, message, Affix, Button, Typography } from 'antd'
import './index.scss'
const { Paragraph } = Typography
export default class MainTable extends Component {
  static propTpyes = {
    dict: PropTypes.object,        // 字典项
    MenuID: PropTypes.string,      // 菜单Id
    setting: PropTypes.object,     // 表格全局设置:tableType(表格是否可选、单选、多选)、columnfixed(列固定)、actionfixed(按钮固定)
    pickup: PropTypes.any,         // 数据收起
    setsingle: PropTypes.any,      // 设置单选多选
    columns: PropTypes.array,      // 表格列
    data: PropTypes.any,           // 表格数据
    total: PropTypes.number,       // 总数
    loading: PropTypes.bool,       // 表格加载中
    refreshdata: PropTypes.func,   // 表格中排序列、页码的变化时刷新
    buttonTrigger: PropTypes.func, // 表格中按钮触发操作
    handleTableId: PropTypes.func  // 数据切换
  }
  state = {
    selectedRowKeys: [],  // 表格中选中行
    pageIndex: 1,         // 初始页面索引
    pageSize: 10,         // 每页数据条数
    columns: null,        // 显示列
    selectId: '',
    isSingleSelect: false
  }
  UNSAFE_componentWillMount () {
    const { columns, setting } = this.props
    let _columns = []
    columns.forEach(item => {
      let cell = {
        align: item.Align,
        dataIndex: item.field || item.uuid,
        title: item.label,
        sorter: item.field && item.IsSort === 'true',
        width: item.Width || 120,
        render: (text, record) => {
          return this.getContent(item, record)
        }
      }
      _columns.push(cell)
    })
    this.setState({
      columns: _columns,
      isSingleSelect: setting.tableType === 'radio'
    })
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!is(fromJS(this.props.setsingle), fromJS(nextProps.setsingle))) {
      this.setState({
        isSingleSelect: nextProps.setsingle,
        selectedRowKeys: [],
        selectId: ''
      })
    }
  }
  getContent = (item, record) => {
    if (item.type === 'text') {
      let content = ''
      let match = false
      if (item.field && record.hasOwnProperty(item.field)) {
        content = `${record[item.field]}`
      }
      if (content && item.matchVal && content.indexOf(item.matchVal) > 0) {
        match = true
      }
      content = (item.prefix || '') + content + (item.postfix || '')
      return (
        <div className={match ? item.color : ''}>
          <div className="background"></div>
          <div className="content" style={{ minWidth: (item.Width || 120) + 'px' }}>
            {content}
          </div>
        </div>
      )
    } else if (item.type === 'number') {
      let content = ''
      let match = false
      if (item.field && record.hasOwnProperty(item.field)) {
        content = +record[item.field]
      }
      if (content && item.match && item.matchVal) {
        if (item.match === '>') {
          if (content > item.matchVal) {
            match = true
          }
        } else if (item.match === '<') {
          if (content < item.matchVal) {
            match = true
          }
        } else if (item.match === '>=') {
          if (content >= item.matchVal) {
            match = true
          }
        } else if (item.match === '<=') {
          if (content <= item.matchVal) {
            match = true
          }
        }
      }
      if (content && item.format === 'thdSeparator') {
        content = `${content}`
        content = content.replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
      }
      content = (item.prefix || '') + content + (item.postfix || '')
      return (
        <div className={match ? item.color : ''}>
          <div className={'background'}></div>
          <div className="content" style={{ minWidth: (item.Width || 120) + 'px' }}>
            {content}
          </div>
        </div>
      )
    } else if (item.type === 'picture') {
      let photos = ''
      if (item.field && record.hasOwnProperty(item.field)) {
        photos = record[item.field].split(',')
      } else {
        photos = ''
      }
      return (
        <div className="picture-col" style={{ minWidth: (item.Width || 120) + 'px' }}>
          {photos && photos.map((url, i) => {
            return <img key={`${i}`} src={url} alt=""/>
          })}
        </div>
      )
    } else if (item.type === 'textarea') {
      let content = ''
      let match = false
      if (item.field && record.hasOwnProperty(item.field)) {
        content = `${record[item.field]}`
      }
      if (content && item.matchVal && content.indexOf(item.matchVal) > 0) {
        match = true
      }
      content = (item.prefix || '') + content + (item.postfix || '')
      return (
        <div className={match ? item.color : ''}>
          <div className="background"></div>
          <div className="content" style={{ minWidth: (item.Width || 120) + 'px' }}>
            <Paragraph copyable ellipsis={{ rows: 3, expandable: true }}>{content}</Paragraph>
          </div>
        </div>
      )
    } else if (item.type === 'action') {
      return (
        <div className={item.style} style={{ minWidth: (item.Width || 120) + 'px' }}>
          {item.operations.map(btn => {
            return <Button
              className={'mk-btn mk-' + btn.class}
              icon={btn.icon}
              key={btn.uuid}
              onClick={(e) => {this.actionTrigger(e, btn, record)}}
            >{btn.label}</Button>
          })}
        </div>
      )
    } else if (item.type === 'colspan') {
      let contents = ''
      if (item.subColumn.length > 0) {
        contents = item.subColumn.map(col => {
          let content = ''
          if (col.type === 'text' || col.type === 'textarea') {
            if (col.field && record.hasOwnProperty(col.field)) {
              content = `${record[col.field]}`
            }
            content = (col.prefix || '') + content + (col.postfix || '')
          } else if (col.type === 'number') {
            if (col.field && record.hasOwnProperty(col.field)) {
              content = +record[col.field]
            }
            if (content && col.format === 'thdSeparator') {
              content = `${content}`
              content = content.replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
            }
            content = (col.prefix || '') + content + (col.postfix || '')
          }
          return content
        })
      }
      if (contents && item.order === 'vertical2') {
        let _contents = []
        for(let i = 0; i < contents.length; i += 2) {
          _contents.push(contents.slice(i, i + 2).join(' '))
        }
        contents = _contents
      }
      return (
        <div>
          <div className="content" style={{ minWidth: (item.Width || 120) + 'px' }}>
            {contents && item.order === 'vertical' && contents.map((content, index) => {
              return (<p key={index}>{content}</p>)
            })}
            {contents && item.order === 'vertical2' && contents.map((content, index) => {
              return (<p key={index}>{content}</p>)
            })}
            {contents && item.order === 'horizontal' && contents.map((content, index) => {
              return (<span key={index}>{content}</span>)
            })}
          </div>
        </div>
      )
    }
  }
  actionTrigger = (e, btn, record) => {
    e.stopPropagation()
    this.props.buttonTrigger(btn, record)
  }
  copycontent = (e, content) => {
    // 表格中内容复制
    e.stopPropagation()
    let oInput = document.createElement('input')
    oInput.value = content
    document.body.appendChild(oInput)
    oInput.select()
    document.execCommand('Copy')
    oInput.className = 'oInput'
    oInput.style.display='none'
    message.success(this.props.dict['main.copy.success'])
  }
  onSelectChange = selectedRowKeys => {
    if (this.props.pickup) return
    let index = ''
    if (selectedRowKeys.length > 0) {
      index = selectedRowKeys[selectedRowKeys.length - 1]
    }
    this.changedata(index)
    this.setState({ selectedRowKeys })
  }
  changeRow = (record, index) => {
    // 点击整行,触发切换,判断是否可选,单选或多选,进行对应操作
    if (!this.props.setting.tableType || this.props.pickup) return
    let newkeys = JSON.parse(JSON.stringify(this.state.selectedRowKeys))
    let _re = newkeys.includes(index)
    if (this.props.setting.tableType === 'radio' || this.state.isSingleSelect) {
      this.changedata(index)
      this.setState({ selectedRowKeys: [index] })
    } else {
      if (_re) {
        newkeys = newkeys.filter(item => item !== index)
        this.changedata('')
      } else {
        newkeys.push(index)
        this.changedata(index)
      }
      this.setState({ selectedRowKeys: newkeys })
    }
  }
  changeTable = (pagination, filters, sorter) => {
    this.setState({
      pageIndex: pagination.current,
      pageSize: pagination.pageSize,
      selectedRowKeys: []
    })
    this.props.refreshdata(pagination, filters, sorter)
  }
  changedata = (index) => {
    const { data, setting } = this.props
    let _id = ''
    if (data && data.length > 0 && index !== '') {
      _id = data[index][setting.primaryKey] || ''
    }
    this.setState({
      selectId: _id
    })
    this.props.handleTableId('mainTable', _id)
  }
  resetTable = () => {
    this.setState({
      pageIndex: 1,
      selectId: '',
      selectedRowKeys: []
    })
  }
  render() {
    const { setting, pickup } = this.props
    let { selectedRowKeys, isSingleSelect, selectId } = this.state
    let rowSelection = null
    if (setting.tableType) {
      rowSelection = {
        selectedRowKeys,
        type: (setting.tableType === 'radio' || isSingleSelect) ? 'radio' : 'checkbox',
        onChange: this.onSelectChange
      }
    }
    let offset = null
    if (setting.columnfixed) {
      // 表格头部固定于顶部时,判断距顶部高度
      if (!setting.actionfixed) {
        offset = 48
      } else {
        let box = document.getElementById(this.props.MenuID + 'mainaction')
        if (box) {
          offset = 48 + box.offsetHeight
        } else {
          offset = 105
        }
      }
    }
    let _data = this.props.data ? this.props.data : []
    if (selectId && pickup && isSingleSelect) {
      _data = _data.filter(item => item[setting.primaryKey] === selectId)
    }
    return (
      <div className="main-table">
        {setting.columnfixed && <Affix offsetTop={offset} className="fix-header">
          <Table
            size="middle"
            bordered={true}
            rowSelection={rowSelection}
            columns={this.state.columns.map(column => {
              return {
                align: column.align,
                dataIndex: column.dataIndex,
                title: column.title,
                width: column.width
              }
            })}
          />
        </Affix>}
        <Table
          size="middle"
          bordered={true}
          rowSelection={rowSelection}
          columns={this.state.columns}
          dataSource={_data}
          loading={this.props.loading}
          scroll={{ x: '100%', y: false }}
          onRow={(record, index) => {
            return {
              onClick: () => {this.changeRow(record, index)}
            }
          }}
          onChange={this.changeTable}
          pagination={{
            current: this.state.pageIndex,
            pageSize: this.state.pageSize,
            pageSizeOptions: ['10', '25', '50', '100', '500', '1000'],
            showSizeChanger: true,
            total: this.props.total,
            showTotal: (total, range) => `${range[0]}-${range[1]} ${this.props.dict['main.pagination.of']} ${total} ${this.props.dict['main.pagination.items']}`
          }}
        />
      </div>
    )
  }
}
src/tabviews/formtab/mainTable/index.scss
New file
@@ -0,0 +1,133 @@
.main-table {
  padding: 0 20px 30px;
  table {
    max-width: 100%;
    width: 100%;
    .ant-table-column-title {
      white-space: nowrap;
    }
    .ant-table-selection-column {
      width: 60px;
      min-width: 60px;
      max-width: 60px;
    }
    // .ant-table-tbody > tr td:not(.ant-table-selection-column) {
    //   padding: 0!important;
    // }
    // .ant-table-tbody > tr td .content {
    //   padding: 12px 8px;
    //   height: 100%;
    //   background: lightblue;
    // }
    .ant-table-tbody > tr.ant-table-row-selected td {
      background-color: #c4ebfd;
    }
    .ant-table-tbody > tr.ant-table-row-selected:hover .ant-table-column-sort {
      background-color: #c4ebfd;
    }
  }
  .ant-table-body {
    overflow-x: auto!important;
    min-height: 90px;
    border: 1px solid #e8e8e8;
    border-radius: 4px;
    border-top: none;
    border-bottom: none;
    table {
      border-left: 0;
      .ant-table-thead > tr > th:last-child {
        border-right: 0;
      }
      .ant-table-tbody > tr > td:last-child {
        border-right: 0;
      }
      .ant-table-tbody > tr > td {
        vertical-align: top;
        .content {
          position: relative;
          z-index: 1;
          word-wrap: break-word;
          word-break: break-word;
        }
        .picture-col {
          img {
            max-width: 100%;
          }
        }
      }
      .ant-table-tbody > tr > td.ant-table-column-has-actions {
        position: relative;
        .background {
          position: absolute;
          top: 0px;
          left: 0px;
          right: 0px;
          bottom: 0px;
        }
        .content {
          position: relative;
          z-index: 1;
          word-wrap: break-word;
          word-break: break-word;
        }
        .red {
          .content {
            color: red;
          }
        }
        .redbg {
          .background {
            background: lightcoral;
          }
        }
      }
      .ant-table-tbody > tr > td .content {
        p {
          margin-bottom: 5px;
        }
        span {
          display: inline-block;
          margin-right: 5px;
        }
      }
      .ant-table-tbody > tr > td .button {
        .ant-btn {
          margin-bottom: 10px;
        }
      }
    }
  }
  .ant-table-body::-webkit-scrollbar {
    width: 8px;
    height: 10px;
  }
  ::-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);
  }
  ::-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);
  }
  .fix-header {
    .ant-table-body {
      min-height: unset
    }
    .ant-table-placeholder {
      display: none;
    }
    .ant-table-wrapper {
      display: none;
    }
    .ant-affix .ant-table-wrapper {
      display: block;
    }
    // .ant-table-column-sorter, .anticon-filter {
    //   display: none;
    // }
  }
}
src/tabviews/subtable/index.jsx
@@ -1,5 +1,6 @@
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} from 'antd'
import moment from 'moment'
@@ -16,7 +17,7 @@
const SubTabTable = asyncComponent(() => import('@/tabviews/subtabtable'))
export default class SubTabViewTable extends Component {
class SubTabViewTable extends Component {
  static propTpyes = {
    Tab: PropTypes.object,           // 标签信息
    BID: PropTypes.string,           // 上级数据ID
@@ -64,6 +65,8 @@
   * @description 获取页面配置信息
   */
  async loadconfig () {
    const { permAction } = this.props
    console.log(permAction)
    let param = {
      func: 'sPC_Get_LongParam',
      MenuID: this.props.MenuID
@@ -103,6 +106,9 @@
      let _hideCol = []      // 隐藏及合并列中字段的uuid集
      let colMap = new Map()
      // 权限过滤
      config.action = config.action.filter(item => permAction[item.uuid])
      // 1、筛选字段集,2、过滤隐藏列及合并列中的字段uuid
      config.columns.forEach(col => {
        if (col.field) {
@@ -135,16 +141,15 @@
        }
      })
      // 添加操作列(存在时)(未经过权限过滤)
      if (config.gridBtn && config.gridBtn.display) {
      let _actions = config.action.filter(item => item.position === 'toolbar') // 过滤工具栏按钮
      let _operations = config.action.filter(item => item.position === 'grid')  // 添加操作列(存在时)
      if (config.gridBtn && config.gridBtn.display && _operations.length > 0) {
        _columns.push({
          ...config.gridBtn,
          operations: config.action.filter(item => item.position === 'grid')
          operations: _operations
        })
      }
      // 过滤工具栏按钮(未经过权限过滤)
      let _actions = config.action.filter(item => item.position === 'toolbar')
      this.setState({
        loadingview: false,
@@ -191,6 +196,11 @@
      if (item.resourceType === '1' && item.dataSource) {
        let arrfield = item.valueField + ',' + item.valueText
        if (item.valueField === item.valueText) { // value 与 text 字段相同时
          arrfield = item.valueField
        }
        if (item.type === 'link') {
          arrfield = arrfield + ',' + item.linkField
        }
@@ -643,4 +653,16 @@
      </div>
    )
  }
}
}
const mapStateToProps = (state) => {
  return {
    permAction: state.permAction
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(SubTabViewTable)
src/tabviews/subtabtable/index.jsx
@@ -1,5 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { notification, Spin} from 'antd'
import moment from 'moment'
@@ -13,7 +14,7 @@
import Utils from '@/utils/utils.js'
import './index.scss'
export default class NormalTable extends Component {
class SubTabModalTable extends Component {
  static propTpyes = {
    BID: PropTypes.string,         // 上级数据ID
    MenuID: PropTypes.string,      // 菜单Id
@@ -46,6 +47,8 @@
   * @description 获取页面配置信息
   */
  async loadconfig () {
    const { permAction } = this.props
    let param = {
      func: 'sPC_Get_LongParam',
      MenuID: this.props.MenuID
@@ -85,6 +88,9 @@
      let _hideCol = []      // 隐藏及合并列中字段的uuid集
      let colMap = new Map()
      // 权限过滤
      config.action = config.action.filter(item => permAction[item.uuid])
      // 1、筛选字段集,2、过滤隐藏列及合并列中的字段uuid
      config.columns.forEach(col => {
        if (col.field) {
@@ -117,16 +123,15 @@
        }
      })
      // 添加操作列(存在时)(未经过权限过滤)
      if (config.gridBtn && config.gridBtn.display) {
      let _actions = config.action.filter(item => item.position === 'toolbar') // 过滤工具栏按钮
      let _operations = config.action.filter(item => item.position === 'grid')  // 添加操作列(存在时)
      if (config.gridBtn && config.gridBtn.display && _operations.length > 0) {
        _columns.push({
          ...config.gridBtn,
          operations: config.action.filter(item => item.position === 'grid')
          operations: _operations
        })
      }
      // 过滤工具栏按钮(未经过权限过滤)
      let _actions = config.action.filter(item => item.position === 'toolbar')
      this.setState({
        loadingview: false,
@@ -173,6 +178,11 @@
      if (item.resourceType === '1' && item.dataSource) {
        let arrfield = item.valueField + ',' + item.valueText
        if (item.valueField === item.valueText) { // value 与 text 字段相同时
          arrfield = item.valueField
        }
        if (item.type === 'link') {
          arrfield = arrfield + ',' + item.linkField
        }
@@ -571,4 +581,16 @@
      </div>
    )
  }
}
}
const mapStateToProps = (state) => {
  return {
    permAction: state.permAction
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(SubTabModalTable)
src/tabviews/tableshare/actionList/index.jsx
@@ -117,6 +117,8 @@
        url = url + '?ID=' + ids
      }
      window.open(url)
    } else if (item.OpenType === 'tab' || item.OpenType === 'blank') {
      this.props.triggerPopview(item, data)
    } else {
      notification.warning({
        top: 92,
@@ -743,6 +745,10 @@
    let deffers = subfields.map(item => {
      let arrfield = item.valueField + ',' + item.valueText
      if (item.valueField === item.valueText) { // value 与 text 字段相同时
        arrfield = item.valueField
      }
      if (item.type === 'link') {
        arrfield = arrfield + ',' + item.linkField
      } else if (item.type === 'select' && item.linkSubField && item.linkSubField.length > 0) {
src/templates/comtableconfig/index.jsx
@@ -15,8 +15,7 @@
import ActionForm from './actionform'
import SettingForm from './settingform'
import TabForm from './tabform'
import TabDragElement from './tabdragelement'
import TabForm from '@/templates/tableshare/tabform'
import SearchForm from '@/templates/tableshare/searchform'
import ColumnForm from '@/templates/tableshare/columnform'
import DragElement from '@/templates/tableshare/dragelement'
@@ -25,6 +24,7 @@
import EditCard from '@/templates/tableshare/editcard'
import VerifyCard from '@/templates/tableshare/verifycard'
import MenuForm from '@/templates/tableshare/menuform'
import TabDragElement from '@/templates/tableshare/tabdragelement'
import SourceElement from '@/templates/tableshare/dragelement/source'
import Source from './source'
import './index.scss'
@@ -347,7 +347,7 @@
    this.setState({
      modaltype: type === 'copy' ? 'actionCopy' : 'actionEdit',
      card: card,
      formlist: getActionForm(card, functip, this.state.config, this.props.permFuncField, 'main')
      formlist: getActionForm(card, functip, this.state.config, this.props.permFuncField)
    })
  }
@@ -612,9 +612,9 @@
      })
    } else if (modaltype === 'tabs') {
      this.tabsFormRef.handleConfirm().then(res => {
        let _tabgroup = config[res.values.groupId].map(item => {
          if (item.uuid === res.values.uuid) {
            return res.values
        let _tabgroup = config[res.groupId].map(item => {
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
@@ -622,7 +622,7 @@
        _tabgroup = _tabgroup.filter(item => !item.origin)
        this.setState({
          config: {...config, [res.values.groupId]: _tabgroup},
          config: {...config, [res.groupId]: _tabgroup},
          modaltype: ''
        })
      })
@@ -1219,9 +1219,15 @@
          }
        }
        // 删除按钮元素
        let _delActions = _this.state.delActions
        if (element.type === 'action') {
          _delActions.push(element.card.uuid)
        }
        _this.setState({
          config: _config,
          delActions: [..._this.state.delActions, element.card.uuid]
          delActions: _delActions
        })
      },
      onCancel() {}
@@ -2162,8 +2168,7 @@
  render () {
    const { modaltype } = this.state
    const configAction = this.state.config.action.filter(_action =>
      !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview')
      // !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview' || _action.OpenType === 'blank' || _action.OpenType === 'tab')
      !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview' || _action.OpenType === 'blank' || _action.OpenType === 'tab')
    )
    let configTabs = []
src/templates/comtableconfig/tabdragelement/card.jsx
File was deleted
src/templates/comtableconfig/tabdragelement/index.scss
File was deleted
src/templates/formtabconfig/actionform/index.jsx
@@ -81,6 +81,16 @@
          item.options = btnIcons
        } else if (item.key === 'sqlType') {
          item.options = this.state.insertUpdateOptions
        } else if (item.key === 'OpenType') {
          item.options = [
            {
              value: 'prompt',
              text: this.props.dict['header.form.prompt']
            }, {
              value: 'exec',
              text: this.props.dict['header.form.exec']
            }
          ]
        }
        item.hidden = !_options.includes(item.key)
        return item
@@ -292,10 +302,7 @@
              duration: 10
            })
          } else {
            resolve({
              type: 'action',
              values
            })
            resolve(values)
          }
        } else {
          reject(err)
src/templates/formtabconfig/dragelement/card.jsx
@@ -1,13 +1,14 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Button, Select, DatePicker, Input } from 'antd'
import { Icon, Button, Select, DatePicker, Input, InputNumber } from 'antd'
import moment from 'moment'
import ItemTypes from './itemtypes'
import './index.scss'
const { MonthPicker, WeekPicker, RangePicker } = DatePicker
const { MonthPicker } = DatePicker
const { TextArea } = Input
const Card = ({ id, type, card, moveCard, findCard, editCard, delCard, copyCard, profileCard, hasDrop }) => {
const Card = ({ id, type, cols, card, moveCard, findCard, editCard, delCard, copyCard, profileCard, hasDrop }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes[type], id, originalIndex },
@@ -74,17 +75,32 @@
    }
  }
  let labelCol = 'ant-col-sm-8'
  let wrapCol = 'ant-col-sm-16'
  if (card.type === 'textarea') {
    if (cols === '2') {
      labelCol = 'ant-col-sm-4'
      wrapCol = 'ant-col-sm-20'
    } else if (cols === '3') {
      labelCol = 'ant-col-cuslabel'
      wrapCol = 'ant-col-cuswrap'
    }
  }
  return (
    <div className="page-card" style={{ opacity: opacity}}>
      <div ref={node => drag(drop(node))}>
        {type === 'search' ?
          <div className="ant-row ant-form-item">
            <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
            <div className={'ant-col ant-form-item-label ant-col-xs-24 ' + labelCol}>
              <label title={card.label}>{card.label}</label>
            </div>
            <div className="ant-col ant-form-item-control-wrapper ant-col-xs-24 ant-col-sm-16">
            <div className={'ant-col ant-form-item-control-wrapper ant-col-xs-24 ' + wrapCol}>
              {card.type === 'text' ?
                <Input style={{marginTop: '4px'}} defaultValue={card.initval} /> : null
              }
              {card.type === 'number' ?
                <InputNumber defaultValue={card.initval} precision={card.decimal} /> : null
              }
              {(card.type === 'multiselect' || card.type === 'select' || card.type === 'link') ?
                <Select defaultValue={_defaultValue}></Select> : null
@@ -92,19 +108,19 @@
              {card.type === 'date' ?
                <DatePicker defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} /> : null
              }
              {card.type === 'dateweek' ?
                <WeekPicker defaultValue={card.initval ? moment().subtract(card.initval * 7, 'days') : null} /> : null
              }
              {card.type === 'datemonth' ?
                <MonthPicker defaultValue={card.initval ? moment().subtract(card.initval, 'month') : null} /> : null
              }
              {card.type === 'daterange' ?
                <RangePicker
                  className="data-range"
                  placeholder={['开始日期', '结束日期']}
                  renderExtraFooter={() => 'extra footer'}
                  defaultValue={_defaultValue}
                /> : null
              {card.type === 'datetime' ?
                <DatePicker showTime defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} /> : null
              }
              {card.type === 'textarea' ?
                <TextArea defaultValue={card.initval} autosize={{ minRows: 2, maxRows: 6 }} /> : null
              }
              {card.type === 'fileupload' ?
                <Button>
                  <Icon type="upload" /> 点击上传
                </Button> : null
              }
              <div className="input-mask"></div>
            </div>
src/templates/formtabconfig/dragelement/index.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import update from 'immutability-helper'
import { is, fromJS } from 'immutable'
import { Col } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
@@ -12,13 +13,19 @@
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    if (!card) return
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    setCards(_cards)
    if (type === 'action') {
      handleList(type, _cards)
    } else {
      handleList(type, _cards, null, groupId)
    }
  }
  if (!is(fromJS(cards), fromJS(list))) {
    setCards(list)
  }
  const findCard = id => {
@@ -50,8 +57,7 @@
    copycard.uuid = Utils.getuuid()
    copycard.origin = false
    copycard.label = copycard.label + '(copy)'
    copycard.originCard = card
    copycard.focus = true
    copyElement(copycard)
  }
@@ -95,38 +101,24 @@
        newcard.type = item.subType
        newcard.resourceType = '0'
        newcard.options = []
        newcard.dataSource = ''
        newcard.setAll = 'false'
        newcard.linkField = ''
        newcard.valueField = ''
        newcard.valueText = ''
        newcard.orderBy = ''
        newcard.orderType = 'asc'
        newcard.match = _match
        newcard.display = 'dropdown'
      } else if (item.type === 'action') {
        newcard.label = 'button'
        newcard.innerFunc = ''
        newcard.outerFunc = ''
        newcard.sql = ''
        newcard.sqlType = ''
        newcard.Ot = 'requiredSgl'
        newcard.OpenType = item.subType
        newcard.tabType = 'SubTable'
        newcard.linkTab = ''
        newcard.icon = ''
        newcard.class = 'default'
        newcard.intertype = 'inner'
        newcard.interface = ''
        newcard.method = 'POST'
        newcard.position = 'toolbar'
        newcard.execSuccess = 'grid'
        newcard.execError = 'never'
        newcard.popClose = 'never'
        newcard.errorTime = 15
        newcard.callbackFunc = ''
        newcard.pageTemplate = ''
        newcard.url = ''
        newcard.verify = null
      }
      
@@ -141,7 +133,6 @@
      targetIndex++
      const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
      setCards(_cards)
      handleList(type, _cards, newcard, groupId)
      target = null
    }
@@ -165,12 +156,13 @@
        />
      ))}
      {type === 'search' && cards.map(card => (
        <Col key={card.uuid} span={24 / setting.cols}>
        <Col key={card.uuid} span={card.type !== 'textarea' ? 24 / setting.cols : 24}>
          <Card
            id={card.uuid}
            key={card.uuid}
            type={type}
            card={card}
            cols={setting.cols}
            moveCard={moveCard}
            editCard={editCard}
            delCard={delCard}
src/templates/formtabconfig/groupform/index.jsx
@@ -45,6 +45,8 @@
          if (group.isDefault) {
            values.isDefault = true
            values.sublist = group.sublist
            resolve(values)
          } else {
            let targetKeys = this.refs['fields-transfer'].state.targetKeys
src/templates/formtabconfig/index.jsx
@@ -4,23 +4,26 @@
import { is, fromJS } from 'immutable'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import { Button, Card, Modal, Collapse, notification, Spin, Select, List, Icon, Empty, Switch, Tooltip } from 'antd'
import moment from 'moment'
import ActionForm from './actionform'
import SettingForm from './settingform'
import TabForm from './tabform'
import TabDragElement from './tabdragelement'
import { Button, Card, Modal, Collapse, notification, Spin, Select, List, Icon, Empty, Switch, Tooltip } from 'antd'
import Api from '@/api'
import SearchForm from './modalform'
import DragElement from './dragelement'
import GroupForm from './groupform'
import EditCard from '@/templates/tableshare/editcard'
import VerifyCard from '@/templates/tableshare/verifycard'
import MenuForm from '@/templates/tableshare/menuform'
import SourceElement from '@/templates/tableshare/dragelement/source'
import zhCN from '@/locales/zh-CN/comtable.js'
import enUS from '@/locales/en-US/comtable.js'
import Utils from '@/utils/utils.js'
import { getModalForm, getActionForm } from '@/templates/tableshare/formconfig'
import ActionForm from './actionform'
import SettingForm from './settingform'
import ModalForm from './modalform'
import DragElement from './dragelement'
import GroupForm from './groupform'
import TabForm from '@/templates/tableshare/tabform'
import EditCard from '@/templates/tableshare/editcard'
import VerifyCard from '@/templates/tableshare/verifycard'
import MenuForm from '@/templates/tableshare/menuform'
import TabDragElement from '@/templates/tableshare/tabdragelement'
import SourceElement from '@/templates/tableshare/dragelement/source'
import Source from './source'
import './index.scss'
@@ -41,18 +44,13 @@
  state = {
    dict: CommonDict,        // 字典
    config: null,            // 页面配置
    visible: false,          // 搜索条件、按钮、显示列,模态框显示控制
    modalTitle: '',          // 模态框的标题
    modaltype: '',           // 模态框类型,控制模态框显示
    tableVisible: false,     // 数据表字段模态框
    tableColumns: [],        // 表格显示列
    fields: null,            // 搜索条件及显示列,可选字段
    menuformlist: null,      // 基本信息表单字段
    formlist: null,          // 搜索条件、按钮、显示列表单字段
    formtemp: '',            // 表单类型,显示列、按钮、搜索条件
    card: null,              // 编辑元素
    searchloading: false,    // 搜索条件加载中
    actionloading: false,    // 按钮加载中
    tabloading: false,       // 标签页加载中
    menuloading: false,      // 菜单保存中
    menucloseloading: false, // 菜单关闭时,选择保存
    loading: false,          // 加载中,页面spin
@@ -78,21 +76,18 @@
    const { menu, editAction, config } = this.props
    console.log(menu)
    let _config = ''
    let _originMenu = ''
    if (!config) {
      _config = JSON.parse(JSON.stringify(Source.baseConfig))
    } else {
      _config = config
      _originMenu = JSON.parse(JSON.stringify(_config))
    }
    // _config.action = _config.action.map(item => {
    //   item.uuid = Utils.getuuid()
    //   return item
    // })
    this.setState({
      config: _config,
      originMenu: JSON.parse(JSON.stringify(menu)),
      originMenu: _originMenu,
      selectedTables: _config.tables,
      menuformlist: [
        {
@@ -239,92 +234,79 @@
  }
  handleList = (type, list, card, groupId, elementId) => {
    let config = JSON.parse(JSON.stringify(this.state.config))
    const { config } = this.state
    if (type === 'tabs') { // 标签页调整顺序或添加元素
      if (list.length > config[card.groupId].length) {
        list = list.filter(item => !item.origin)
        this.setState({
          tabloading: true,
          config: {...config, [card.groupId]: list }
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            tabloading: false
          })
          this.handleTab(card)
        })
      } else {
        this.setState({config: {...config, [card.groupId]: list}})
        this.handleTab(card)
      }
      this.setState({config: {...config, [card.groupId]: list}})
    } else if (type === 'action') {
      if (list.length > config.action.length) {
        list = list.filter(item => !item.origin)
  
        this.setState({
          actionloading: true,
          config: {...config, action: list }
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            actionloading: false
          })
          this.handleAction(card)
        })
      } else {
        this.setState({config: {...config, action: list}})
        this.handleAction(card)
      }
    } else if (type === 'search') {
      let _group = config.groups.filter(group => group.uuid === groupId)[0]
      let isChange = elementId && list.length > _group.sublist.length
      let isAdd = !elementId && list.length > _group.sublist.length
      if (isAdd) {
        _group.sublist = list.filter(item => !item.origin)
        this.handleSearch(card)
      } else if (isChange) {
        // 修改已有元素的分组
        let element = null
        config.groups.forEach(item => {
          item.sublist = item.sublist.filter(cell => {
            if (cell.uuid !== elementId) {
              return true
      this.setState({config: {...config, action: list}})
    } else if (type === 'search') {
      let _groups = null
      if (card) {
        // 元素添加
        if (config.groups.length === 1) {
          _groups = config.groups.map(group => {
            let _list = list.filter(item => !item.origin)
            return {...group, sublist: _list}
          })
        } else {
          _groups = config.groups.map(group => {
            if (group.uuid === groupId) {
              return {...group, sublist: list}
            } else {
              element = cell
              return false
              return group
            }
          })
        })
        _group.sublist.push(element)
      } else {
        _group.sublist = list
      }
      config.groups = config.groups.map(item => {
        if (item.uuid === _group.uuid) {
          return _group
        } else {
          return item
        }
      })
      if (isChange) {
        this.setState({
          searchloading: true,
          config: config
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            searchloading: false
        this.handleSearch(card)
      } else if (elementId) {
        // 修改已有元素的分组
        let element = null
        _groups = config.groups.map(group => {
          group.sublist = group.sublist.filter(item => {
            if (item.uuid === elementId) {
              element = item
              return false
            } else {
              return true
            }
          })
          return group
        })
        _groups = _groups.map(group => {
          if (group.uuid === groupId) {
            group.sublist.push(element)
          }
          return group
        })
      } else {
        this.setState({
          config: config
        // 元素移动
        _groups = config.groups.map(group => {
          if (group.uuid === groupId) {
            return {...group, sublist: list}
          } else {
            return group
          }
        })
      }
      this.setState({
        config: {...config, groups: _groups}
      })
    }
  }
@@ -344,399 +326,23 @@
    }
    this.setState({
      visible: true,
      formtemp: 'search',
      modalTitle: '编辑-表单',
      modaltype: 'search',
      card: card,
      formlist: [
        {
          type: 'text',
          key: 'label',
          label: this.state.dict['header.form.name'],
          initVal: card.label,
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'field',
          label: this.state.dict['header.form.field'],
          initVal: card.field,
          required: true,
          readonly: false
        },
        {
          type: 'select',
          key: 'type',
          label: this.state.dict['header.form.type'],
          initVal: card.type,
          required: true,
          options: [{
            value: 'text',
            text: this.state.dict['header.form.text']
          }, {
            value: 'number',
            text: this.state.dict['header.form.number']
          }, {
            value: 'select',
            text: this.state.dict['header.form.select']
          }, {
            value: 'multiselect',
            text: this.state.dict['header.form.multiselect']
          }, {
            value: 'link',
            text: this.state.dict['header.form.link']
          }, {
            value: 'fileupload',
            text: this.state.dict['header.form.fileupload']
          }, {
            value: 'date',
            text: this.state.dict['header.form.dateday']
          }, {
            value: 'datemonth',
            text: this.state.dict['header.form.datemonth']
          }, {
            value: 'datetime',
            text: this.state.dict['header.form.datetime']
          }, {
            value: 'textarea',
            text: this.state.dict['header.form.textarea']
          }]
        },
        {
          type: 'text',
          key: 'initval',
          label: this.state.dict['header.form.initval'],
          initVal: card.initval,
          required: false
        },
        {
          type: 'radio',
          key: 'resourceType',
          label: this.state.dict['header.form.resourceType'],
          initVal: card.resourceType || '0',
          required: true,
          options: [{
            value: '0',
            text: this.state.dict['header.form.custom']
          }, {
            value: '1',
            text: this.state.dict['header.form.datasource']
          }]
        },
        {
          type: 'radio',
          key: 'setAll',
          label: this.state.dict['header.form.setAll'],
          initVal: card.setAll || 'false',
          options: [{
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'textarea',
          key: 'dataSource',
          label: this.state.dict['header.form.datasource'],
          initVal: card.dataSource || '',
          required: true,
          readonly: false
        },
        {
          type: 'options',
          key: 'options',
          label: '',
          initVal: card.options || [],
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'linkField',
          label: this.state.dict['header.form.linkField'],
          initVal: card.linkField || '',
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'valueField',
          label: this.state.dict['header.form.valueField'],
          initVal: card.valueField || '',
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'valueText',
          label: this.state.dict['header.form.valueText'],
          initVal: card.valueText || '',
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'orderBy',
          label: this.state.dict['header.form.orderBy'],
          initVal: card.orderBy || '',
          required: false,
          readonly: false
        },
        {
          type: 'select',
          key: 'orderType',
          label: this.state.dict['header.form.orderType'],
          initVal: card.orderType || 'asc',
          options: [{
            value: 'asc',
            text: this.state.dict['header.form.asc']
          }, {
            value: 'desc',
            text: this.state.dict['header.form.desc']
          }]
        },
        {
          type: 'number',
          key: 'decimal',
          label: this.state.dict['header.form.decimal'],
          initVal: card.decimal || 0,
          required: false
        },
        {
          type: 'number',
          key: 'min',
          label: '最小值',
          initVal: card.min || '',
          required: false
        },
        {
          type: 'number',
          key: 'max',
          label: '最大值',
          initVal: card.max || '',
          required: false
        },
        {
          type: 'radio',
          key: 'readonly',
          label: this.state.dict['header.form.readonly'],
          initVal: card.readonly || 'false',
          options: [{
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'radio',
          key: 'required',
          label: this.state.dict['header.form.field.required'],
          initVal: card.required || 'false',
          options: [{
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'multiselect',
          key: 'linkSubField',
          label: this.state.dict['header.form.linkForm'],
          initVal: card.linkSubField || [],
          options: _inputfields
        }
      ]
      formlist: getModalForm(card, _inputfields)
    })
  }
  handleAction = (card, type) => {
    let ableField = this.props.permFuncField.join(', ')
    let functip = <div>
      <p style={{marginBottom: '5px'}}>{this.state.dict['header.modal.func.innerface'].replace('@ableField', ableField)}</p>
      <p>{this.state.dict['header.modal.func.outface']}</p>
    </div>
    this.setState({
      visible: true,
      formtemp: 'action',
      modalTitle: type === 'copy' ? '复制-按钮' : '编辑-按钮',
      modaltype: type === 'copy' ? 'actionCopy' : 'actionEdit',
      card: card,
      formlist: [
        {
          type: 'text',
          key: 'label',
          label: this.state.dict['header.form.name'],
          initVal: card.label,
          required: true,
          readonly: false
        },
        {
          type: 'select',
          key: 'OpenType',
          label: this.state.dict['header.form.openType'],
          initVal: card.OpenType,
          required: true,
          options: [{
            value: 'prompt',
            text: this.state.dict['header.form.prompt']
          }, {
            value: 'exec',
            text: this.state.dict['header.form.exec']
          }]
        },
        {
          type: 'radio',
          key: 'intertype',
          label: this.state.dict['header.form.intertype'],
          initVal: card.intertype || 'inner',
          required: true,
          options: [{
            value: 'inner',
            text: this.state.dict['header.form.interface.inner']
          }, {
            value: 'outer',
            text: this.state.dict['header.form.interface.outer']
          }]
        },
        {
          type: 'text',
          key: 'innerFunc',
          label: this.state.dict['header.form.innerFunc'],
          initVal: card.innerFunc,
          tooltip: <div>
            <p>内部接口: 可自定义数据处理函数,函数名称需以{ableField}等字符开始;未设置时会调用系统函数,使用系统函数需完善数据源及操作类型;</p>
            <p>外部接口: 可自定义数据处理函数,提交数据经过内部函数处理后,传入外部接口,未设置时,数据会直接传入外部接口。</p>
          </div>,
          fields: this.props.permFuncField,
          tooltipClass: 'middle',
          required: false,
          readonly: false
        },
        {
          type: 'radio',
          key: 'sysInterface',
          label: this.state.dict['header.form.sysInterface'],
          initVal: card.sysInterface || 'false',
          required: true,
          options: [{
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'text',
          key: 'outerFunc',
          label: this.state.dict['header.form.outerFunc'],
          initVal: card.outerFunc,
          required: false,
          readonly: false
        },
        {
          type: 'text',
          key: 'interface',
          label: this.state.dict['header.form.interface'],
          initVal: card.sysInterface === 'true' ? (window.GLOB.mainSystemApi || window.GLOB.subSystemApi) : card.interface,
          required: true,
          readonly: card.sysInterface === 'true'
        },
        {
          type: 'text',
          key: 'callbackFunc',
          label: this.state.dict['header.form.callbackFunc'],
          initVal: card.callbackFunc,
          required: false,
          readonly: false
        },
        {
          type: 'select',
          key: 'execSuccess',
          label: this.state.dict['header.form.execSuccess'],
          initVal: card.execSuccess || 'never',
          required: true,
          options: [{
            value: 'never',
            text: this.state.dict['header.form.refresh.never']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.refresh.grid']
          }, {
            value: 'view',
            text: this.state.dict['header.form.refresh.view']
          }]
        },
        {
          type: 'select',
          key: 'execError',
          label: this.state.dict['header.form.execError'],
          initVal: card.execError || 'never',
          required: true,
          options: [{
            value: 'never',
            text: this.state.dict['header.form.refresh.never']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.refresh.grid']
          }, {
            value: 'view',
            text: this.state.dict['header.form.refresh.view']
          }]
        },
        {
          type: 'select',
          key: 'popClose',
          label: this.state.dict['header.form.popClose'],
          initVal: card.popClose || 'never',
          required: true,
          options: [{
            value: 'never',
            text: this.state.dict['header.form.refresh.never']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.refresh.grid']
          }, {
            value: 'view',
            text: this.state.dict['header.form.refresh.view']
          }]
        },
        {
          type: 'select',
          key: 'icon',
          label: this.state.dict['header.form.icon'],
          initVal: card.icon,
          required: false,
          options: []
        },
        {
          type: 'select',
          key: 'class',
          label: this.state.dict['header.form.class'],
          initVal: card.class,
          required: false,
          options: []
        },
        {
          type: 'text',
          key: 'sql',
          label: this.state.dict['header.form.datasource'],
          initVal: card.sql || this.state.config.setting.tableName || '',
          tooltip: this.state.dict['header.form.actionhelp.datasource'],
          required: false
        },
        {
          type: 'select',
          key: 'sqlType',
          label: this.state.dict['header.form.action.type'],
          initVal: card.sqlType || '',
          tooltip: this.state.dict['header.form.actionhelp.sqlType'],
          required: false,
          options: []
        }
      ]
      formlist: getActionForm(card, functip, this.state.config, this.props.permFuncField)
    })
  }
@@ -782,9 +388,7 @@
    }
    this.setState({
      visible: true,
      formtemp: 'tabs',
      modalTitle: '编辑-标签页',
      modaltype: 'tabs',
      card: card,
      formlist: [
        {
@@ -855,101 +459,100 @@
   * 3、添加或编辑列,保存时,如按钮位置设置为表格,则修改操作列显示状态
   */
  handleSubmit = () => {
    const { menu } = this.props
    const { card } = this.state
    let _config = JSON.parse(JSON.stringify(this.state.config))
    const { config, modaltype } = this.state
    this.formRef.handleConfirm().then(res => {
      let isupdate = false
      if (res.type === 'action' && card.originCard && res.values.OpenType === 'pop') {
        Api.getSystemConfig({
          func: 'sPC_Get_LongParam',
          MenuID: card.originCard.uuid
        }).then(result => {
          if (result.status && result.LongParam) {
            let param = {
              func: 'sPC_ButtonParam_AddUpt',
              ParentID: menu.MenuID,
              MenuID: res.values.uuid,
              MenuNo: menu.MenuNo,
              Template: 'Modal',
              MenuName: res.values.label,
              PageParam: JSON.stringify({Template: 'Modal'}),
              LongParam: result.LongParam
            }
            Api.getSystemConfig(param).then(response => {
              if (!response.status) {
                notification.warning({
                  top: 92,
                  message: response.message,
                  duration: 10
                })
              }
            })
          }
        })
      }
      if (res.type === 'action') {
        _config.action = _config.action.map(item => {
          if (item.uuid === res.values.uuid) {
            isupdate = true
            return res.values
          } else {
            return item
          }
        })
        _config.action = _config.action.filter(item => !item.origin)
        if (!isupdate) { // 操作不是修改,添加元素至列表
          _config.action.push(res.values)
        }
      } else if (res.type === 'search') {
        _config.groups = _config.groups.map(item => {
          item.sublist = item.sublist.map(cell => {
            if (cell.uuid === res.values.uuid) {
              return res.values
    if (modaltype === 'search') {
      this.modalFormRef.handleConfirm().then(res => {
        let _groups = config.groups.map(group => {
          group.sublist = group.sublist.map(item => {
            if (item.uuid === res.uuid) {
              return res
            } else {
              return cell
              return item
            }
          })
          if (item.isDefault) {
            item.sublist = item.sublist.filter(cell => !cell.origin)
          if (group.isDefault) {
            group.sublist = group.sublist.filter(item => !item.origin)
          }
          return item
          return group
        })
      } else { // 标签页的添加与修改
        _config[res.values.groupId] = _config[res.values.groupId].map(item => {
          if (item.uuid === res.values.uuid) {
            isupdate = true
            return res.values
        this.setState({
          config: {...config, groups: _groups},
          modaltype: ''
        })
      })
    } else if (modaltype === 'actionEdit' || modaltype === 'actionCopy') {
      this.actionFormRef.handleConfirm().then(res => {
        let _action = config.action.map(item => {
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        _config[res.values.groupId] = _config[res.values.groupId].filter(item => !item.origin)
        if (!isupdate) { // 操作不是修改,添加元素至列表
          _config[res.values.groupId].push(res.values)
        _action = _action.filter(item => !item.origin)
        if (modaltype === 'actionCopy') {
          _action.push(res)
        }
        this.setState({
          config: {...config, action: _action},
          modaltype: ''
        })
      })
    } else if (modaltype === 'tabs') {
      this.tabsFormRef.handleConfirm().then(res => {
        let _tabgroup = config[res.groupId].map(item => {
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        _tabgroup = _tabgroup.filter(item => !item.origin)
        this.setState({
          config: {...config, [res.groupId]: _tabgroup},
          modaltype: ''
        })
      })
    }
  }
  editModalCancel = () => {
    const { config, card, modaltype } = this.state
    if (card.focus) {
      let _config = null
      if (modaltype === 'search') {
        let _groups = config.groups.map(group => {
          group.sublist = group.sublist.filter(item => item.uuid !== card.uuid)
          return group
        })
        _config = {...config, groups: _groups}
      } else if (modaltype === 'actionEdit') {
        let _action = config.action.filter(item => item.uuid !== card.uuid)
        _config = {...config, action: _action}
      } else if (modaltype === 'tabs') {
        let _tabgroup = config[card.groupId].filter(item => item.uuid !== card.uuid)
        _config = {...config, [card.groupId]: _tabgroup}
      } else {
        _config = config
      }
      this.setState({
        card: null,
        config: _config,
        searchloading: true,
        actionloading: true,
        tabloading: true,
        visible: false
      }, () => {
        this.setState({
          searchloading: false,
          actionloading: false,
          tabloading: false
        })
        modaltype: ''
      })
    })
    } else {
      this.setState({
        card: null,
        modaltype: ''
      })
    }
  }
  /**
@@ -1228,12 +831,7 @@
        this.setState({
          config: _config,
          actionloading: true,
          funcLoading: false
        }, () => {
          this.setState({
            actionloading: false
          })
        })
      })
    })
@@ -1468,6 +1066,7 @@
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        let _config = JSON.parse(JSON.stringify(_this.state.config))
        let _delActions = _this.state.delActions
        if (element.type === 'tabs') {
          _config[element.card.groupId] = _config[element.card.groupId].filter(item => {
@@ -1477,6 +1076,11 @@
              return true
            }
          })
        } else if (element.type === 'search') {
          _config.groups = _config.groups.map(group => {
            group.sublist = group.sublist.filter(item => item.uuid !== element.card.uuid)
            return group
          })
        } else {
          _config[element.type] = _config[element.type].filter(item => {
            if (item.uuid === element.card.uuid) {
@@ -1485,22 +1089,12 @@
              return true
            }
          })
        }
        let refreshtype = element.type + 'loading'
        if (/^tab/.test(refreshtype)) {
          refreshtype = 'tabloading'
          _delActions.push(element.card.uuid)
        }
        _this.setState({
          config: _config,
          delActions: [..._this.state.delActions, element.card.uuid],
          [refreshtype]: true
        }, () => {
          _this.setState({
            [refreshtype]: false
          })
          delActions: _delActions
        })
      },
      onCancel() {}
@@ -1537,27 +1131,21 @@
      profileVisible: false,
      config: config,
      card: '',
      actionloading: true
    }, () => {
      this.setState({
        actionloading: false
      })
    })
  }
  /**
   * @description 三级菜单保存
   * @description 菜单保存
   */
  submitConfig = () => {
    const { menu } = this.props
    const { originMenu } = this.state
    const { menu, editAction } = this.props
    let config = JSON.parse(JSON.stringify(this.state.config))
    this.menuformRef.handleConfirm().then(res => {
      if (config.search[0] && config.search[0].origin) {
        config.search = config.search.filter(item => !item.origin)
      if (config.groups[0] && config.groups[0].sublist[0] && config.groups[0].sublist[0].origin) {
        config.groups[0].sublist = config.groups[0].sublist.filter(item => !item.origin)
      }
      if (config.action[0] && config.action[0].origin) {
        config.action = config.action.filter(item => !item.origin)
@@ -1568,21 +1156,15 @@
      let _LongParam = ''
      let _config = {...config, tables: this.state.selectedTables}
      let _pageParam = {...menu.PageParam, OpenType: res.opentype}
      // 未设置数据源或标签不合法时,启用状态为false
      if (_config.setting.interType === 'inner' && !_config.setting.innerFunc && !_config.setting.dataresource) {
        _config.enabled = false
      } else if (_config.tabgroups.length > 1) {
      // 标签不合法时,启用状态为false
      if (_config.tabgroups.length > 1) {
        _config.tabgroups.forEach(group => {
          if (_config[group].length === 0) {
            _config.enabled = false
          }
        })
      }
      // 保存时删除配置类型,system 、user
      delete _config.type
      try {
        _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
@@ -1597,7 +1179,7 @@
      let btnParam = { // 添加菜单按钮
        func: 'sPC_Button_AddUpt',
        Type: 60, // 添加按钮表单页下的按钮
        Type: 60,      // 添加按钮表单页下的按钮
        ParentID: menu.MenuID,
        MenuNo: res.menuNo,
        Template: menu.PageParam.Template || '',
@@ -1615,9 +1197,9 @@
      
      let tabParam = { // 添加菜单tab页
        func: 'sPC_sMenusTab_AddUpt',
        MenuID: menu.MenuID,
        MenuID: editAction.uuid,
        LText: config.tabs.map((item, index) => {
          return `select '${menu.MenuID}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${(index + 1) * 10}' as Sort`
          return `select '${editAction.uuid}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${(index + 1) * 10}' as Sort`
        })
      }
      tabParam.LText = tabParam.LText.join(' union all ')
@@ -1626,14 +1208,13 @@
      tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
      let param = {
        func: 'sPC_TrdMenu_AddUpt',
        ParentID: res.parentId,
        MenuID: menu.MenuID,
        MenuNo: res.menuNo,
        Template: menu.PageParam.Template || '',
        MenuName: res.menuName,
        Sort: (this.props.supMenuList.length + 1) * 10,
        PageParam: JSON.stringify(_pageParam),
        func: 'sPC_ButtonParam_AddUpt',
        ParentID: menu.MenuID,
        MenuID: editAction.uuid,
        MenuNo: menu.MenuNo,
        Template: 'FormTab',
        MenuName: editAction.label,
        PageParam: JSON.stringify({Template: 'FormTab'}),
        LongParam: _LongParam
      }
      
@@ -1651,25 +1232,9 @@
        if (response.status) {
          this.setState({
            config: _config,
            originMenu: {
              ...originMenu,
              LongParam: _config,
              PageParam: _pageParam,
              MenuName: res.menuName,
              MenuNo: res.menuNo,
              ParentID: res.parentId
            },
            searchloading: true,
            actionloading: true
          }, () => {
            this.setState({
              searchloading: false,
              actionloading: false
            })
            originMenu: _config
          })
          this.props.reloadmenu()
          this.submitAction(btnParam, tabParam)
        } else {
          this.setState({
@@ -1792,23 +1357,13 @@
  }
  cancelConfig = () => {
    const { menu } = this.props
    const { config, originMenu } = this.state
    let _this = this
    let isAdd = false
    if (
      (config.search[0] && config.search[0].origin) ||
      (config.action[0] && config.action[0].origin) ||
      (config.tabs[0] && config.tabs[0].origin)
    ) {
      isAdd = true
    }
    if (isAdd) {
    if (!originMenu) {
      confirm({
        content: '菜单尚未提交,确定放弃保存吗?',
        content: '按钮配置尚未提交,确定放弃保存吗?',
        okText: this.state.dict['header.confirm'],
        cancelText: this.state.dict['header.cancel'],
        onOk() {
@@ -1817,30 +1372,15 @@
        onCancel() {}
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables}
        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
        let _originMenu = {
          ...originMenu,
          LongParam: _config,
          PageParam: _pageParam,
          MenuName: res.menuName,
          MenuNo: res.menuNo,
          ParentID: res.parentId
        }
      let _config = {...config, tables: this.state.selectedTables}
        if (!is(fromJS(originMenu), fromJS(_originMenu))) {
          this.setState({
            closeVisible: true
          })
        } else {
          this.props.handleConfig('')
        }
      }, () => {
      if (!is(fromJS(_config), fromJS(originMenu))) {
        this.setState({
          closeVisible: true
        })
      })
      } else {
        this.props.handleConfig('')
      }
    }
  }
@@ -1899,7 +1439,6 @@
      columnsMap.set(card.field, card)
    })
    console.log(cards)
    let groups = config.groups.map(group => {
      group.sublist = group.sublist.map(item => {
        if (columnsMap.has(item.field)) {
@@ -1941,17 +1480,12 @@
    })
    this.setState({
      searchloading: true,
      config: {...config, groups: groups}
    }, () => {
      notification.success({
        top: 92,
        message: '操作成功',
        duration: 2
      })
      this.setState({
        searchloading: false
      })
    })
    notification.success({
      top: 92,
      message: '操作成功',
      duration: 2
    })
  }
@@ -2052,20 +1586,14 @@
      this.setState({
        config: {...config, setting: res},
        settingVisible: false,
        tabloading: true
      }, () => {
        this.setState({
          tabloading: false
        })
      })
    })
  }
  /**
   * @description 设置可配置按钮
   * @description 设置可配置标签
   */
  setSubConfig = (btn, type) => {
    const { menu } = this.props
    const { config, originMenu } = this.state
    let isAdd = false
@@ -2087,17 +1615,8 @@
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables}
        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
        let _originMenu = {
          ...originMenu,
          LongParam: _config,
          PageParam: _pageParam,
          MenuName: res.menuName,
          MenuNo: res.menuNo,
          ParentID: res.parentId
        }
        if (!is(fromJS(originMenu), fromJS(_originMenu))) {
        if (!is(fromJS(originMenu), fromJS(_config))) {
          notification.warning({
            top: 92,
            message: '菜单配置已修改,请保存!',
@@ -2169,7 +1688,7 @@
    }
  }
  onEnabledChange = (val, e) => {
  onEnabledChange = () => {
    const { config } = this.state
    let tabinvalid = true
@@ -2216,11 +1735,6 @@
        _this.setState({
          config: _config,
          tabloading: true
        }, () => {
          _this.setState({
            tabloading: false
          })
        })
      },
      onCancel() {}
@@ -2241,12 +1755,7 @@
        delete _config[groupId]
        _this.setState({
          config: _config,
          tabloading: true
        }, () => {
          _this.setState({
            tabloading: false
          })
          config: _config
        })
      },
      onCancel() {}
@@ -2291,11 +1800,6 @@
        _this.setState({
          config: {...config, groups: groups},
          searchloading: true
        }, () => {
          _this.setState({
            searchloading: false
          })
        })
      },
      onCancel() {}
@@ -2316,22 +1820,17 @@
      groups = groups.sort((a, b) => {
        return a.sort - b.sort
      })
      this.setState({
        config: {...config, groups: groups},
        editgroup: '',
        groupVisible: false,
        searchloading: true
      }, () => {
        this.setState({
          searchloading: false
        })
      })
    })
  }
  render () {
    const { config } = this.state
    const { config, modaltype } = this.state
    let _length = config.groups.length
    let configTabs = []
@@ -2454,7 +1953,7 @@
                activeKey={config.groups.map(group => group.uuid)}
                expandIconPosition={'right'}
              >
                {!this.state.searchloading && config.groups.map((group, index) => (
                {config.groups.map((group, index) => (
                  <Panel showArrow={false} header={group.label} key={group.uuid} extra={(
                    <span>
                      {index === _length - 1 ? <Icon
@@ -2488,21 +1987,19 @@
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《按钮》中,选择对应类型的按钮拖至此处添加,如选择按钮类型为表单、新标签页等含有配置页面的按钮,可在左侧工具栏-按钮-可配置按钮处,点击按钮完成相关配置。注:当设置按钮显示位置为表格时,显示列会增加操作列。">
                  <Icon type="question-circle" />
                </Tooltip>
                {!this.state.actionloading ?
                  <DragElement
                    type="action"
                    list={this.state.config.action}
                    handleList={this.handleList}
                    handleMenu={this.handleAction}
                    copyElement={(val) => this.handleAction(val, 'copy')}
                    deleteMenu={this.deleteElement}
                    profileMenu={this.profileAction}
                    placeholder={this.state.dict['header.form.action.placeholder']}
                  /> : null
                }
                <DragElement
                  type="action"
                  list={this.state.config.action}
                  handleList={this.handleList}
                  handleMenu={this.handleAction}
                  copyElement={(val) => this.handleAction(val, 'copy')}
                  deleteMenu={this.deleteElement}
                  profileMenu={this.profileAction}
                  placeholder={this.state.dict['header.form.action.placeholder']}
                />
              </div>
              {/* 标签组 */}
              {!this.state.tabloading && this.state.config.tabgroups.map((groupId, index) => {
              {this.state.config.tabgroups.map((groupId, index) => {
                return (
                  <div key={index} className="tab-list">
                    {index === 0 ? <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《标签页》中,选择对应类型的标签页拖至此处添加。">
@@ -2524,47 +2021,60 @@
            </Card>
          </div>
        </DndProvider>
        {/* 编辑搜索条件、按钮、显示列 */}
        {/* 编辑搜索条件 */}
        <Modal
          title={this.state.modalTitle}
          visible={this.state.visible}
          title={this.state.dict['header.modal.form.edit']}
          visible={modaltype === 'search'}
          width={700}
          onCancel={() => { this.setState({ visible: false }) }}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <ModalForm
            dict={this.state.dict}
            card={this.state.card}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.modalFormRef = inst}
          />
        </Modal>
        {/* 编辑按钮:复制、编辑 */}
        <Modal
          title={modaltype === 'actionEdit' ? this.state.dict['header.modal.action.edit'] : this.state.dict['header.modal.action.copy']}
          visible={modaltype === 'actionEdit' || modaltype === 'actionCopy'}
          width={700}
          onCancel={this.editModalCancel}
          footer={[
            this.state.formtemp === 'action' ?
            <Button key="delete" className="mk-btn mk-purple" onClick={this.creatFunc} loading={this.state.funcLoading}>{this.state.dict['header.menu.func.create']}</Button> : null,
            <Button key="cancel" onClick={() => { this.setState({ visible: false }) }}>{this.state.dict['header.cancel']}</Button>,
            modaltype === 'actionEdit' ? <Button key="delete" className="mk-btn mk-purple" onClick={this.creatFunc} loading={this.state.funcLoading}>{this.state.dict['header.menu.func.create']}</Button> : null,
            <Button key="cancel" onClick={this.editModalCancel}>{this.state.dict['header.cancel']}</Button>,
            <Button key="confirm" type="primary" onClick={this.handleSubmit}>{this.state.dict['header.confirm']}</Button>
          ]}
          destroyOnClose
        >
          {this.state.formtemp === 'search' ?
            <SearchForm
              dict={this.state.dict}
              formlist={this.state.formlist}
              card={this.state.card}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'action' ?
            <ActionForm
              dict={this.state.dict}
              card={this.state.card}
              tabs={this.state.tabviews}
              formlist={this.state.formlist}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'tabs' ?
            <TabForm
              type="tabs"
              tabs={this.state.tabviews}
              dict={this.state.dict}
              card={this.state.card}
              formlist={this.state.formlist}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          <ActionForm
            dict={this.state.dict}
            card={this.state.card}
            tabs={this.state.tabviews}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.actionFormRef = inst}
          />
        </Modal>
        {/* 标签编辑 */}
        <Modal
          title={this.state.dict['header.modal.tabs.edit']}
          visible={modaltype === 'tabs'}
          width={700}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <TabForm
            type="tabs"
            dict={this.state.dict}
            card={this.state.card}
            tabs={this.state.tabviews}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.tabsFormRef = inst}
          />
        </Modal>
        {/* 根据字段名添加显示列及搜索条件 */}
        <Modal
src/templates/formtabconfig/index.scss
@@ -216,7 +216,17 @@
                    white-space: nowrap;
                  }
                }
                .ant-form-item-label.ant-col-cuslabel {
                  width: 11%;
                }
                .ant-form-item-control-wrapper.ant-col-cuswrap {
                  width: 89%;
                }
                .ant-form-item-control-wrapper {
                  .ant-input-number {
                    width: 100%;
                    margin-top: 4px;
                  }
                  .ant-select {
                    width: 100%;
                    margin-top: 4px;
@@ -224,6 +234,9 @@
                  .ant-calendar-picker {
                    margin-top: 4px;
                  }
                  .ant-btn {
                    margin-top: 4px;
                  }
                  .input-mask {
                    position: absolute;
                    top: 0;
src/templates/formtabconfig/modalform/index.jsx
@@ -377,10 +377,7 @@
          }
          if (isvalid) {
            resolve({
              type: 'search',
              values
            })
            resolve(values)
          }
        } else {
          reject(err)
src/templates/formtabconfig/settingform/index.jsx
@@ -23,8 +23,7 @@
  UNSAFE_componentWillMount() {
    const { config, menu } = this.props
    console.log(menu)
    console.log(config)
    let _tabs = []
    let _select = []
    let _tabMap = new Map()
@@ -34,12 +33,24 @@
    try {
      _columns = menu.LongParam.columns.filter(item => item.field && item.type !== 'colspan')
      // config.groups.forEach(group => {
      //   if (group.isDefault) {
      //     _columns.push()
      //   }
      //   group.sublist
      // })
      config.groups.forEach(group => {
        if (group.isDefault) {
          let list = group.sublist.filter(item => !item.origin)
          _columns = [..._columns, ...list]
        } else {
          _columns = [..._columns, ...group.sublist]
        }
      })
      let _colMap = new Map()
      _columns = _columns.filter(item => {
        if (_colMap.has(item.field)) {
          return false
        } else {
          _colMap.set(item.field, true)
          return true
        }
      })
      _interType = menu.LongParam.setting.interType
    } catch {
      notification.warning({
@@ -78,18 +89,7 @@
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          values.actionfixed = values.actionfixed === 'true'
          values.columnfixed = values.columnfixed === 'true'
          if (values.interType === 'inner' && !values.innerFunc && !values.dataresource) {
            notification.warning({
              top: 92,
              message: '请自定义函数或填写数据源!',
              duration: 10
            })
          } else {
            resolve(values)
          }
          resolve(values)
        } else {
          reject(err)
        }
@@ -100,12 +100,6 @@
  onChange = (e) => {
    this.setState({
      interType: e.target.value
    })
  }
  selectChange = (val) => {
    this.props.form.setFieldsValue({
      order: `${val} desc`
    })
  }
@@ -157,6 +151,19 @@
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="列数">
              {getFieldDecorator('cols', {
                initialValue: setting.cols || '2'
              })(
                <Select>
                  <Select.Option value="1">1列</Select.Option>
                  <Select.Option value="2">2列</Select.Option>
                  <Select.Option value="3">3列</Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
@@ -245,7 +252,6 @@
              })(
                <Select
                  getPopupContainer={() => document.getElementById('commontable-setting-form')}
                  onChange={this.selectChange}
                >
                  <Select.Option key='unset' value="">不设置</Select.Option>
                  {columns.length === 0 ?
src/templates/formtabconfig/source.jsx
@@ -6,7 +6,7 @@
class CommonTableBaseData {
  baseConfig = {
    type: 'formTab',
    type: 'FormTab',
    enabled: false,
    setting: {
      tableName: '',
@@ -16,7 +16,7 @@
      innerFunc: '',
      interface: '',
      outerFunc: '',
      cols: 2,
      cols: '2',
      subtabs: []
    },
    tables: [],
@@ -37,13 +37,7 @@
            resourceType: '0',
            setAll: 'false',
            options: [],
            dataSource: '',
            linkField: '',
            valueField: '',
            valueText: '',
            orderBy: '',
            orderType: 'asc',
            match: 'like'
            orderType: 'asc'
          }, {
            origin: true,
            uuid: Utils.getuuid(),
@@ -54,13 +48,7 @@
            resourceType: '0',
            setAll: 'false',
            options: [],
            dataSource: '',
            linkField: '',
            valueField: '',
            valueText: '',
            orderBy: '',
            orderType: 'asc',
            match: 'equal'
            orderType: 'asc'
          }, {
            origin: true,
            uuid: Utils.getuuid(),
@@ -71,69 +59,9 @@
            resourceType: '0',
            setAll: 'false',
            options: [],
            dataSource: '',
            linkField: '',
            valueField: '',
            valueText: '',
            orderBy: '',
            orderType: 'asc',
            match: 'greater'
            orderType: 'asc'
          }
        ]
      }
    ],
    search: [
      {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'label',
        field: '',
        initval: '',
        type: 'text',
        resourceType: '0',
        setAll: 'false',
        options: [],
        dataSource: '',
        linkField: '',
        valueField: '',
        valueText: '',
        orderBy: '',
        orderType: 'asc',
        match: 'like'
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'label',
        field: '',
        initval: '',
        type: 'select',
        resourceType: '0',
        setAll: 'false',
        options: [],
        dataSource: '',
        linkField: '',
        valueField: '',
        valueText: '',
        orderBy: '',
        orderType: 'asc',
        match: 'equal'
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'label',
        field: '',
        initval: '',
        type: 'date',
        resourceType: '0',
        setAll: 'false',
        options: [],
        dataSource: '',
        linkField: '',
        valueField: '',
        valueText: '',
        orderBy: '',
        orderType: 'asc',
        match: 'greater'
      }
    ],
    action: [
src/templates/formtabconfig/tabdragelement/index.jsx
File was deleted
src/templates/formtabconfig/tabform/index.jsx
File was deleted
src/templates/formtabconfig/tabform/index.scss
File was deleted
src/templates/modalconfig/dragelement/card.jsx
@@ -94,7 +94,7 @@
              <DatePicker showTime defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} />
            }
            {card.type === 'textarea' &&
              <TextArea autosize={{ minRows: 2, maxRows: 6 }} />
              <TextArea defaultValue={card.initval} autosize={{ minRows: 2, maxRows: 6 }} />
            }
            {card.type === 'fileupload' &&
              <Button>
src/templates/subtableconfig/actionform/index.jsx
@@ -127,6 +127,28 @@
          if (!initTab) {
            item.initVal = ''
          }
        } else if (item.key === 'OpenType') {
          item.options = [
            {
              value: 'pop',
              text: this.props.dict['header.form.popform']
            }, {
              value: 'prompt',
              text: this.props.dict['header.form.prompt']
            }, {
              value: 'exec',
              text: this.props.dict['header.form.exec']
            }, {
              value: 'excelIn',
              text: this.props.dict['header.form.excelIn']
            }, {
              value: 'excelOut',
              text: this.props.dict['header.form.excelOut']
            }, {
              value: 'popview',
              text: this.props.dict['header.form.popview']
            }
          ]
        }
        item.hidden = !_options.includes(item.key)
        return item
src/templates/subtableconfig/index.jsx
@@ -314,7 +314,7 @@
    this.setState({
      modaltype: type === 'copy' ? 'actionCopy' : 'actionEdit',
      card: card,
      formlist: getActionForm(card, functip, this.state.config, this.props.permFuncField, 'subtable')
      formlist: getActionForm(card, functip, this.state.config, this.props.permFuncField)
    })
  }
@@ -1033,16 +1033,15 @@
          }
        })
        let refreshtype = element.type + 'loading'
        // 删除按钮元素
        let _delActions = _this.state.delActions
        if (element.type === 'action') {
          _delActions.push(element.card.uuid)
        }
        _this.setState({
          config: _config,
          delActions: [..._this.state.delActions, element.card.uuid],
          [refreshtype]: true
        }, () => {
          _this.setState({
            [refreshtype]: false
          })
          delActions: _delActions
        })
      },
      onCancel() {}
@@ -1564,17 +1563,13 @@
    }
    this.setState({
      [addType + 'loading']: true,
      config: {...config, [addType]: items}
    }, () => {
      notification.success({
        top: 92,
        message: '操作成功',
        duration: 2
      })
      this.setState({
        [addType + 'loading']: false
      })
    })
    notification.success({
      top: 92,
      message: '操作成功',
      duration: 2
    })
  }
src/templates/tableshare/colspanform/index.jsx
@@ -48,10 +48,7 @@
          let _card = {...this.props.card, ...values, sublist: targetKeys, subfield: subfield}
          resolve({
            type: 'columns',
            values: _card
          })
          resolve(_card)
        } else {
          reject(err)
        }
src/templates/tableshare/dragelement/index.jsx
@@ -21,6 +21,7 @@
  if (!is(fromJS(cards), fromJS(list))) {
    setCards(list)
  }
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
    return {
src/templates/tableshare/dragelform/card.jsx
New file
@@ -0,0 +1,109 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Select, DatePicker, Input } from 'antd'
import moment from 'moment'
import ItemTypes from './itemtypes'
import './index.scss'
const { MonthPicker, WeekPicker, RangePicker } = DatePicker
const Card = ({ id, type, card, moveCard, findCard, editCard, delCard, hasDrop }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes[type], id, originalIndex },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: ItemTypes[type],
    canDrop: () => true,
    drop: (item) => {
      if (!item.hasOwnProperty('originalIndex')) {
        hasDrop(card)
      }
    },
    hover({ id: draggedId }) {
      if (!draggedId) return
      if (draggedId !== id) {
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
    },
  })
  const opacity = isDragging ? 0 : 1
  const edit = () => {
    editCard(id)
  }
  const del = () => {
    delCard(id)
  }
  let _defaultValue = '' // 下拉搜索、时间范围类型,初始值需要预处理
  if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link') {
    if (card.initval) {
      let _option = card.options.filter(option => option.Value === card.initval)[0]
      if (_option) {
        _defaultValue = _option.Text || ''
      } else {
        _defaultValue = ''
      }
    } else if (card.setAll === 'true') {
      _defaultValue = '全部'
    }
  } else if (card.type === 'daterange') {
    _defaultValue = [null, null]
    if (card.initval) {
      try {
        let _initval = JSON.parse(card.initval)
        _defaultValue = [moment().subtract(_initval[0], 'days'), moment().subtract(_initval[1], 'days')]
      } catch {
        _defaultValue = [null, null]
      }
    }
  }
  return (
    <div className="page-card" style={{ opacity: opacity}}>
      <div ref={node => drag(drop(node))}>
        <div className="ant-row ant-form-item">
          <div className="ant-col ant-form-item-label">
            <label title={card.label}>{card.label}</label>
          </div>
          <div className="ant-col ant-form-item-control-wrapper">
            {card.type === 'text' ?
              <Input style={{marginTop: '4px'}} defaultValue={card.initval} /> : null
            }
            {(card.type === 'multiselect' || card.type === 'select' || card.type === 'link') ?
              <Select defaultValue={_defaultValue}></Select> : null
            }
            {card.type === 'date' ?
              <DatePicker defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} /> : null
            }
            {card.type === 'dateweek' ?
              <WeekPicker defaultValue={card.initval ? moment().subtract(card.initval * 7, 'days') : null} /> : null
            }
            {card.type === 'datemonth' ?
              <MonthPicker defaultValue={card.initval ? moment().subtract(card.initval, 'month') : null} /> : null
            }
            {card.type === 'daterange' ?
              <RangePicker
                className="data-range"
                placeholder={['开始日期', '结束日期']}
                renderExtraFooter={() => 'extra footer'}
                defaultValue={_defaultValue}
              /> : null
            }
            <div className="input-mask"></div>
          </div>
        </div>
      </div>
      <Icon className="edit" title="编辑" type="edit" onClick={edit} />
      <Icon className="edit close" title="删除" type="close" onClick={del} />
    </div>
  )
}
export default Card
src/templates/tableshare/dragelform/index.jsx
New file
@@ -0,0 +1,118 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Col, Icon } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import ItemTypes from './itemtypes'
import './index.scss'
const Container = ({list, type, placeholder, handleList, handleMenu, deleteMenu }) => {
  let target = null
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    handleList(type, _cards)
  }
  if (!is(fromJS(cards), fromJS(list))) {
    setCards(list)
  }
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
    return {
      card,
      index: cards.indexOf(card),
    }
  }
  const editCard = id => {
    const { card } = findCard(id)
    handleMenu(card)
  }
  const delCard = id => {
    const { card } = findCard(id)
    deleteMenu({card: card, type: type})
  }
  const hasDrop = (item) => {
    target = item
  }
  const [, drop] = useDrop({
    accept: ItemTypes[type],
    drop(item) {
      if (item.hasOwnProperty('originalIndex')) {
        return
      }
      let newcard = {}
      newcard.uuid = Utils.getuuid()
      newcard.focus = true
      let _match = 'like'
      if (item.subType === 'select' || item.subType === 'link') {
        _match = '='
      } else if (item.subType === 'date' || item.subType === 'datemonth') {
        _match = '>='
      } else if (item.subType === 'dateweek' || item.subType === 'daterange') {
        _match = 'between'
      }
      newcard.label = 'label'
      newcard.initval = ''
      newcard.type = item.subType
      newcard.resourceType = '0'
      newcard.options = []
      newcard.setAll = 'false'
      newcard.orderType = 'asc'
      newcard.match = _match
      newcard.display = 'dropdown'
      let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
      if (target) {
        targetId = target.uuid
      }
      const { index: overIndex } = findCard(`${targetId}`)
      let targetIndex = overIndex
      targetIndex++
      const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
      handleList(type, _cards, newcard)
      target = null
    }
  })
  return (
    <div ref={drop} className="ant-row">
      {cards.map(card => (
        <Col key={card.uuid} span={6}>
          <Card
            id={`${card.uuid}`}
            type={type}
            card={card}
            moveCard={moveCard}
            editCard={editCard}
            delCard={delCard}
            findCard={findCard}
            hasDrop={hasDrop}
          />
        </Col>
      ))}
      {cards.length === 0 &&
        <div className="common-drawarea-placeholder">
          {placeholder}
        </div>
      }
    </div>
  )
}
export default Container
src/templates/tableshare/dragelform/index.scss
New file
@@ -0,0 +1,15 @@
.common-source-item {
  display: block;
  box-shadow: 0px 0px 2px #bcbcbc;
  padding: 0.4rem 0.7rem;
  background-color: white;
  margin: 0px 0px 10px;
  cursor: move;
  border-radius: 4px;
}
.common-drawarea-placeholder {
  width: 100%;
  line-height: 65px;
  text-align: center;
  color: #bcbcbc;
}
src/templates/tableshare/dragelform/itemtypes.js
New file
@@ -0,0 +1,8 @@
export default {
  CARD: 'card',
  form: 'form',
  search: 'search',
  action: 'action',
  columns: 'columns',
  tab: 'tab'
}
src/templates/tableshare/dragelform/source.jsx
New file
@@ -0,0 +1,13 @@
import React from 'react'
import { useDrag } from 'react-dnd'
import './index.scss'
const SourceElement = ({content}) => {
  const [, drag] = useDrag({ item: content })
  return (
    <div ref={drag} className="common-source-item">
      {content.label}
    </div>
  )
}
export default SourceElement
src/templates/tableshare/formconfig.js
@@ -201,45 +201,7 @@
 * @param {*} config         页面配置
 * @param {*} permFuncField  存储过程可用的开始字段
 */
export function getActionForm (card, functip, config, permFuncField, type) {
  let openTypeOptions = [{
    value: 'pop',
    text: Formdict['header.form.popform']
  }, {
    value: 'prompt',
    text: Formdict['header.form.prompt']
  }, {
    value: 'exec',
    text: Formdict['header.form.exec']
  }, {
    value: 'excelIn',
    text: Formdict['header.form.excelIn']
  }, {
    value: 'excelOut',
    text: Formdict['header.form.excelOut']
  }, {
    value: 'popview',
    text: Formdict['header.form.popview']
  }]
  if (type === 'main') {
    openTypeOptions = [
      ...openTypeOptions,
      {
        value: 'tab',
        text: Formdict['header.form.tab']
      }, {
        value: 'blank',
        text: Formdict['header.form.blank']
      }, {
        value: 'innerpage',
        text: Formdict['header.form.newpage.inner']
      }, {
        value: 'outerpage',
        text: Formdict['header.form.newpage.outer']
      }
    ]
  }
export function getActionForm (card, functip, config, permFuncField) {
  return [
    {
      type: 'text',
@@ -255,7 +217,37 @@
      label: Formdict['header.form.openType'],
      initVal: card.OpenType,
      required: true,
      options: openTypeOptions
      options: [{
        value: 'pop',
        text: Formdict['header.form.popform']
      }, {
        value: 'prompt',
        text: Formdict['header.form.prompt']
      }, {
        value: 'exec',
        text: Formdict['header.form.exec']
      }, {
        value: 'excelIn',
        text: Formdict['header.form.excelIn']
      }, {
        value: 'excelOut',
        text: Formdict['header.form.excelOut']
      }, {
        value: 'popview',
        text: Formdict['header.form.popview']
      }, {
        value: 'tab',
        text: Formdict['header.form.tab']
      }, {
        value: 'blank',
        text: Formdict['header.form.blank']
      }, {
        value: 'innerpage',
        text: Formdict['header.form.newpage.inner']
      }, {
        value: 'outerpage',
        text: Formdict['header.form.newpage.outer']
      }]
    }, {
      type: 'select',
      key: 'tabType',
@@ -379,10 +371,10 @@
      type: 'select',
      key: 'tabTemplate',
      label: '标签模板',
      initVal: card.tabTemplate || 'formTab',
      initVal: card.tabTemplate || 'FormTab',
      required: true,
      options: [{
        value: 'formTab',
        value: 'FormTab',
        text: '带标签表单'
      }]
    },
@@ -674,4 +666,217 @@
      required: false
    }
  ]
}
/**
 * @description 获取表单配置信息
 * @param {*} card
 * @param {*} inputfields
 */
export function getModalForm (card, inputfields) {
  return [
    {
      type: 'text',
      key: 'label',
      label: Formdict['header.form.name'],
      initVal: card.label,
      required: true,
      readonly: false
    },
    {
      type: 'text',
      key: 'field',
      label: Formdict['header.form.field'],
      initVal: card.field,
      required: true,
      readonly: false
    },
    {
      type: 'select',
      key: 'type',
      label: Formdict['header.form.type'],
      initVal: card.type,
      required: true,
      options: [{
        value: 'text',
        text: Formdict['header.form.text']
      }, {
        value: 'number',
        text: Formdict['header.form.number']
      }, {
        value: 'select',
        text: Formdict['header.form.select']
      }, {
        value: 'multiselect',
        text: Formdict['header.form.multiselect']
      }, {
        value: 'link',
        text: Formdict['header.form.link']
      }, {
        value: 'fileupload',
        text: Formdict['header.form.fileupload']
      }, {
        value: 'date',
        text: Formdict['header.form.dateday']
      }, {
        value: 'datemonth',
        text: Formdict['header.form.datemonth']
      }, {
        value: 'datetime',
        text: Formdict['header.form.datetime']
      }, {
        value: 'textarea',
        text: Formdict['header.form.textarea']
      }]
    },
    {
      type: 'text',
      key: 'initval',
      label: Formdict['header.form.initval'],
      initVal: card.initval,
      required: false
    },
    {
      type: 'radio',
      key: 'resourceType',
      label: Formdict['header.form.resourceType'],
      initVal: card.resourceType || '0',
      required: true,
      options: [{
        value: '0',
        text: Formdict['header.form.custom']
      }, {
        value: '1',
        text: Formdict['header.form.datasource']
      }]
    },
    {
      type: 'radio',
      key: 'setAll',
      label: Formdict['header.form.setAll'],
      initVal: card.setAll || 'false',
      options: [{
        value: 'true',
        text: Formdict['header.form.true']
      }, {
        value: 'false',
        text: Formdict['header.form.false']
      }]
    },
    {
      type: 'textarea',
      key: 'dataSource',
      label: Formdict['header.form.datasource'],
      initVal: card.dataSource || '',
      required: true,
      readonly: false
    },
    {
      type: 'options',
      key: 'options',
      label: '',
      initVal: card.options || [],
      required: true,
      readonly: false
    },
    {
      type: 'text',
      key: 'linkField',
      label: Formdict['header.form.linkField'],
      initVal: card.linkField || '',
      required: true,
      readonly: false
    },
    {
      type: 'text',
      key: 'valueField',
      label: Formdict['header.form.valueField'],
      initVal: card.valueField || '',
      required: true,
      readonly: false
    },
    {
      type: 'text',
      key: 'valueText',
      label: Formdict['header.form.valueText'],
      initVal: card.valueText || '',
      required: true,
      readonly: false
    },
    {
      type: 'text',
      key: 'orderBy',
      label: Formdict['header.form.orderBy'],
      initVal: card.orderBy || '',
      required: false,
      readonly: false
    },
    {
      type: 'select',
      key: 'orderType',
      label: Formdict['header.form.orderType'],
      initVal: card.orderType || 'asc',
      options: [{
        value: 'asc',
        text: Formdict['header.form.asc']
      }, {
        value: 'desc',
        text: Formdict['header.form.desc']
      }]
    },
    {
      type: 'number',
      key: 'decimal',
      label: Formdict['header.form.decimal'],
      initVal: card.decimal || 0,
      required: false
    },
    {
      type: 'number',
      key: 'min',
      label: '最小值',
      initVal: card.min || '',
      required: false
    },
    {
      type: 'number',
      key: 'max',
      label: '最大值',
      initVal: card.max || '',
      required: false
    },
    {
      type: 'radio',
      key: 'readonly',
      label: Formdict['header.form.readonly'],
      initVal: card.readonly || 'false',
      options: [{
        value: 'true',
        text: Formdict['header.form.true']
      }, {
        value: 'false',
        text: Formdict['header.form.false']
      }]
    },
    {
      type: 'radio',
      key: 'required',
      label: Formdict['header.form.field.required'],
      initVal: card.required || 'false',
      options: [{
        value: 'true',
        text: Formdict['header.form.true']
      }, {
        value: 'false',
        text: Formdict['header.form.false']
      }]
    },
    {
      type: 'multiselect',
      key: 'linkSubField',
      label: Formdict['header.form.linkForm'],
      initVal: card.linkSubField || [],
      options: inputfields
    }
  ]
}
src/templates/tableshare/tabdragelement/card.jsx
src/templates/tableshare/tabdragelement/index.jsx
File was renamed from src/templates/comtableconfig/tabdragelement/index.jsx
@@ -15,8 +15,9 @@
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    if (!card) return
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    handleList(type, _cards)
    handleList(type, _cards, card)
  }
  if (!is(fromJS(cards), fromJS(list))) {
src/templates/tableshare/tabdragelement/index.scss
src/templates/tableshare/tabform/index.jsx
File was renamed from src/templates/comtableconfig/tabform/index.jsx
@@ -200,10 +200,7 @@
            values.linkTab = Utils.getuuid()
          }
          resolve({
            type: this.props.type,
            values
          })
          resolve(values)
        } else {
          reject(err)
        }
src/templates/tableshare/tabform/index.scss
src/utils/utils.js
@@ -425,19 +425,24 @@
  static getSelectQuerySql (item) {
    let _datasource = item.dataSource
    let sql = ''
    let _field = item.valueField + ',' + item.valueText
    if (item.valueField === item.valueText) {
      _field = item.valueField
    }
    if (/\s/.test(_datasource)) { // 拼接别名
      _datasource = '(' + _datasource + ') tb'
    }
    if (item.type === 'link') {
      sql = 'select ' + item.valueField + ',' + item.valueText + ',' + item.linkField + ' from ' + _datasource
      sql = 'select ' + _field + ',' + item.linkField + ' from ' + _datasource
    } else {
      let _linkSubField = '' // 下拉菜单关联表单
      if (item.linkSubField && item.linkSubField.length > 0) {
        _linkSubField = ',' + item.linkSubField.join(',')
      }
      sql = 'select ' + item.valueField + ',' + item.valueText + _linkSubField + ' from ' + _datasource
      sql = 'select ' + _field + _linkSubField + ' from ' + _datasource
    }
    if (item.orderBy) {
src/views/login/index.jsx
@@ -64,7 +64,7 @@
          sessionStorage.setItem('SessionUid', Utils.getuuid())
          sessionStorage.setItem('LoginUID', res.LoginUID)
          sessionStorage.setItem('User_Name', res.UserName)
          sessionStorage.setItem('avatar', res.icon)
          sessionStorage.setItem('avatar', res.icon || '')
          
          localStorage.setItem('lang', param.lang)