king
2019-12-30 84e806945ef8982508a79e0b62a7bcb0881b108c
2019-12-30
16个文件已修改
2个文件已添加
1230 ■■■■ 已修改文件
src/assets/css/main.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/sidemenu/editthdmenu/index.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/sidemenu/editthdmenu/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtable/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.scss 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.jsx 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.scss 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/settingform/index.jsx 68 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/source.jsx 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/tabdragelement/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/tabform/index.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/tabform/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/index.scss 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/transferform/index.jsx 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/transferform/index.scss 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss
@@ -46,11 +46,13 @@
html::-webkit-scrollbar-thumb {
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
  background: rgba(0, 0, 0, 0.13);
  border-radius: 5px;
}
html::-webkit-scrollbar-track {
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(0, 0, 0, 0.07);
  background: rgba(0, 0, 0, 0);
  border-radius: 3px;
}
input[type="button"], input[type="submit"], input[type="search"], input[type="reset"] {
@@ -129,6 +131,22 @@
  border: 1px solid rgba(0, 0, 0, 0.07);
  background: rgba(0, 0, 0, 0);
}
// 重置模态框滚动条
.ant-modal-wrap::-webkit-scrollbar {
  width: 7px;
}
.ant-modal-wrap::-webkit-scrollbar-thumb {
  border-radius: 5px;
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
  background: rgba(0, 0, 0, 0.13);
}
.ant-modal-wrap::-webkit-scrollbar-track {
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, 0.07);
  background: rgba(0, 0, 0, 0);
}
.ant-modal-mask {
  z-index: 1080!important;
}
src/components/sidemenu/editthdmenu/index.jsx
@@ -138,6 +138,8 @@
            editMenu: _menu,
            loading: false,
            tabview: _Template.length > 0 ? _menu.PageParam.Template : 'template'
          }, () => {
            document.getElementById('root').style.overflowY = 'hidden'
          })
          // 模板不存在时错误提示
@@ -174,6 +176,7 @@
        return
      }
      this.setState({tabview: 'template', type: 'add'})
      document.getElementById('root').style.overflowY = 'hidden'
    } else if (type === 'thaw') {
      if (previewList && !is(fromJS(previewList), fromJS(this.state.subMenulist))) {
        notification.warning({
@@ -426,11 +429,16 @@
  exittabview = () => {
    this.setState({tabview: ''})
    document.getElementById('root').style.overflowY = 'unset'
  }
  handleConfig = (type) => {
    this.setState({tabview: type})
    document.getElementById('root').style.overflowY = 'unset'
    if (type) {
      document.getElementById('root').style.overflowY = 'hidden'
    } else {
      document.getElementById('root').style.overflowY = 'unset'
    }
  }
  tabHandleConfig = () => {
src/components/sidemenu/editthdmenu/index.scss
@@ -162,6 +162,20 @@
        cursor: zoom-in;
      }
    }
    .workplace::-webkit-scrollbar {
      width: 7px;
    }
    .workplace::-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);
    }
    .workplace::-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);
    }
  }
  > .loading-thdmenu {
    position: fixed;
src/tabviews/commontable/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { BackTop, notification, Spin, Tabs, Icon, Card, Switch} from 'antd'
import { BackTop, notification, Spin, Tabs, Icon, Switch} from 'antd'
import moment from 'moment'
import Api from '@/api'
import MainTable from './mainTable'
@@ -9,11 +9,13 @@
import MainSearch from '@/tabviews/tableshare/topSearch'
import SubTable from '@/tabviews/subtable'
import NotFount from '@/components/404'
// import asyncComponent from '@/utils/asyncLoadComponent'
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
export default class NormalTable extends Component {
@@ -32,7 +34,6 @@
    searchlist: null,
    actions: null,
    columns: null,
    tabviews: null,
    arr_field: '',
    setting: null,
    data: null,
@@ -144,7 +145,6 @@
        searchlist: config.search,
        actions: _actions,
        columns: _columns,
        tabviews: config.tabs,
        isLinkMain: _tab.length > 0,
        arr_field: _arrField.join(','),
        search: Utils.initMainSearch(config.search), // 搜索条件初始化(含有时间格式,需要转化)
@@ -552,7 +552,7 @@
  }
  render() {
    const { setting, searchlist, actions, columns, loadingview, viewlost, tabviews, setsingle, pickup, isLinkMain } = this.state
    const { setting, searchlist, actions, columns, loadingview, viewlost, setsingle, pickup, isLinkMain, config } = this.state
    return (
      <div className="commontable" id={'commontable' + this.props.MenuID}>
@@ -598,34 +598,28 @@
            />
          </div> : null
        }
        {setting && setting.onload !== 'false' && setting.tabshow !== 'vertical' && tabviews && tabviews.length > 0 &&
          <Tabs defaultActiveKey="0" tabPosition="top">
            {tabviews.map((_tab, index) => {
              return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
                <TabPane tab={
                  <span>
                    {_tab.icon ? <Icon type={_tab.icon} /> : null}
                    {_tab.label}
                  </span>
                } key={`${index}`}>
                  {_tab.type === 'SubTable' ? <SubTable SupMenuID={this.props.MenuID} MenuID={_tab.linkTab} Tab={_tab} BID={this.state.BIDs[_tab.supMenu] || ''} /> : null}
                </TabPane> : null
            })}
          </Tabs>
        }
        {setting && setting.onload !== 'false' && setting.tabshow === 'vertical' && tabviews && tabviews.length > 0 &&
          tabviews.map((_tab, index) => {
            return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
              <Card bordered={false} title={
                <span>
                  {_tab.icon ? <Icon type={_tab.icon} /> : null}
                  {_tab.label}
                </span>
              } key={`${index}`}>
                {_tab.type === 'SubTable' ? <SubTable SupMenuID={this.props.MenuID} MenuID={_tab.linkTab} Tab={_tab} BID={this.state.BIDs[_tab.supMenu] || ''} /> : null}
              </Card> : null
        {setting && setting.onload !== 'false' &&
          config.tabgroups.map(group => {
            if (config[group].length === 0) return null
            return (
              <Tabs defaultActiveKey="0" tabPosition="top" key={group}>
                {config[group].map((_tab, index) => {
                  return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
                    <TabPane tab={
                      <span>
                        {_tab.icon ? <Icon type={_tab.icon} /> : null}
                        {_tab.label}
                      </span>
                    } key={`${index}`}>
                      {_tab.type === 'SubTable' ? <SubTable SupMenuID={this.props.MenuID} MenuID={_tab.linkTab} Tab={_tab} BID={this.state.BIDs[_tab.supMenu] || ''} /> : null}
                    </TabPane> : null
                })}
              </Tabs>
            )
          })
        }
        {/* {<SubTabTable SupMenuID={this.props.SupMenuID} MenuID={this.props.MenuID} Tab={this.props.Tab} BID={this.props.BID} />} */}
        <BackTop>
          <div className="ant-back-top">
            <div className="ant-back-top-content">
src/tabviews/commontable/index.scss
@@ -63,6 +63,9 @@
      margin-bottom: 5px;
    }
  }
  .ant-tabs + .ant-tabs {
    margin-top: 20px;
  }
}
.ant-back-top {
  bottom: 30px;
src/tabviews/subtable/index.jsx
@@ -7,11 +7,14 @@
import SubTable from './subTable'
import SubAction from '@/tabviews/tableshare/actionList'
import SubSearch from '@/tabviews/tableshare/topSearch'
// import asyncComponent from '@/utils/asyncLoadComponent'
import NotFount from '@/components/404'
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'))
export default class NormalTable extends Component {
  static propTpyes = {
@@ -532,6 +535,7 @@
            buttonTrigger={this.buttonTrigger}
          />
        }
        {/* {<SubTabTable SupMenuID={this.props.SupMenuID} MenuID={this.props.MenuID} Tab={this.props.Tab} BID={this.props.BID} />} */}
        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
      </div>
    )
src/tabviews/subtabtable/index.jsx
New file
@@ -0,0 +1,539 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { notification, Spin} from 'antd'
import moment from 'moment'
import Api from '@/api'
import SubTable from '@/tabviews/subtable/subTable'
import SubAction from '@/tabviews/tableshare/actionList'
import SubSearch from '@/tabviews/tableshare/topSearch'
import NotFount from '@/components/404'
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'
export default class NormalTable extends Component {
  static propTpyes = {
    Tab: PropTypes.object,       // 标签信息
    BID: PropTypes.string,       // 上级数据ID
    MenuID: PropTypes.string,    // 菜单Id
    SupMenuID: 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: '',
    setting: null,
    data: [],
    total: 0,
    loading: false,
    pageIndex: 1,
    pageSize: 10,
    orderColumn: '',
    orderType: 'asc',
    search: '',
    configMap: {}
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.props.Tab.supMenu && !is(fromJS(this.props.BID), fromJS(nextProps.BID))) {
      this.loadmaindata()
    }
  }
  /**
   * @description 获取页面配置信息
   */
  async loadconfig () {
    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()
      // 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)
        }
      })
      // 添加操作列(存在时)(未经过权限过滤)
      if (config.gridBtn && config.gridBtn.display) {
        _columns.push({
          ...config.gridBtn,
          operations: config.action.filter(item => item.position === 'grid')
        })
      }
      // 过滤工具栏按钮(未经过权限过滤)
      let _actions = config.action.filter(item => item.position === 'toolbar')
      this.setState({
        loadingview: false,
        config: config,
        setting: config.setting,
        searchlist: config.search,
        actions: _actions,
        columns: _columns,
        arr_field: _arrField.join(','),
        search: Utils.initMainSearch(config.search), // 搜索条件初始化(含有时间格式,需要转化)
        loading: true
      }, () => {
        this.improveSearch()
        this.loadmaindata()
      })
    } else {
      this.setState({
        loadingview: false,
        viewlost: true
      })
      notification.warning({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
  improveSearch = () => {
    let searchlist = JSON.parse(JSON.stringify(this.state.searchlist))
    let deffers = []
    searchlist.forEach(item => {
      if (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.type === 'link') {
          arrfield = arrfield + ',' + item.linkField
        }
        let param = {
          func: 'sPC_Get_SelectedList',
          LText: item.dataSourceSql,
          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
        })
      }
    })
    this.setState({searchlist: JSON.parse(JSON.stringify(searchlist))})
    if (deffers.length === 0) 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 => {
                item.options.push({
                  key: Utils.getuuid(),
                  Value: cell[res.search.valueField],
                  Text: cell[res.search.valueText]
                })
              })
            }
            return item
          })
        } else {
          notification.warning({
            top: 92,
            message: res.search.label + ':' + res.message,
            duration: 10
          })
        }
      })
      this.setState({searchlist})
    })
  }
  async loadmaindata () {
    const { setting } = this.state
    let param = ''
    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
      param = this.getCustomParam()
    } else {
      param = this.getDefaultParam()
    }
    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
      })
    } else {
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 15
      })
    }
  }
  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
  }
  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,
      BID: this.props.BID
    }
    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
  }
  refreshbysearch = (searches) => {
    // 搜索条件变化
    this.refs.subTable.resetTable()
    this.setState({
      loading: true,
      pageIndex: 1,
      search: searches
    }, () => {
      this.loadmaindata()
    })
  }
  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()
    })
  }
  reloadtable = () => {
    this.refs.subTable.resetTable()
    this.setState({
      loading: true,
      pageIndex: 1
    }, () => {
      this.loadmaindata()
    })
  }
  reloadview = () => {
    this.setState({
      loadingview: true,    // 页面加载中
      viewlost: false,      // 页面丢失:1、未获取到配置-页面丢失;2、页面未启用
      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: {}
    }, () => {
      this.loadconfig()
    })
  }
  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 (type === 'excelOut') {
      this.handleDefaultExcelout(btn)
    }
  }
  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 + ')' : ''
    // 获取列表数据
    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.subButton.execError(res, btn)
      } else {
        this.refs.subButton.execSuccess(btn)
      }
    })
  }
  gettableselected = () => {
    // 获取表格选择项
    let data = []
    this.refs.subTable.state.selectedRowKeys.forEach(item => {
      data.push(this.refs.subTable.props.data[item])
    })
    return data
  }
  buttonTrigger = (btn, record) => {
    this.refs.subButton.actionTrigger(btn, record)
  }
  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 = (state, callback) => {
      return
    }
  }
  render() {
    const { setting, searchlist, actions, columns, loadingview, viewlost } = this.state
    return (
      <div className="subtable" id={'subtable' + this.props.MenuID}>
        {loadingview && <Spin size="large" />}
        {searchlist && searchlist.length > 0 ?
          <SubSearch
            refreshdata={this.refreshbysearch}
            searchlist={searchlist}
            dict={this.state.dict}
          /> : null
        }
        {actions &&
          <SubAction
            ref="subButton"
            type="sub"
            setting={setting}
            actions={actions}
            BID={this.props.BID}
            dict={this.state.dict}
            MenuID={this.props.SupMenuID}
            refreshdata={this.refreshbyaction}
            gettableselected={this.gettableselected}
          />
        }
        {columns &&
          <SubTable
            ref="subTable"
            dict={this.state.dict}
            MenuID={this.props.MenuID}
            setting={setting}
            columns={columns}
            data={this.state.data}
            total={this.state.total}
            loading={this.state.loading}
            refreshdata={this.refreshbytable}
            buttonTrigger={this.buttonTrigger}
          />
        }
        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
      </div>
    )
  }
}
src/tabviews/subtabtable/index.scss
New file
@@ -0,0 +1,36 @@
.subtable {
  position: relative;
  min-height: 200px;
  padding-top: 16px;
  > .top-search {
    padding: 0 0px 20px;
  }
  > .button-list {
    padding: 10px 0px 5px;
  }
  .box404 {
    padding-top: 30px;
  }
  .ant-modal-mask {
    position: absolute;
  }
  .ant-modal-wrap {
    position: absolute;
  }
  .action-modal .ant-modal {
    top: 40px;
    max-width: 95%;
    .ant-modal-body {
      max-height: calc(100vh - 265px);
    }
  }
  > .ant-spin {
    position: absolute;
    left: calc(50% - 22px);
    top: 100px;
  }
}
.ant-back-top {
  bottom: 30px;
  right: 30px;
}
src/templates/comtableconfig/index.jsx
@@ -109,6 +109,8 @@
    }
    _config.tabs = _config.tabs || []
    _config.tabgroups = _config.tabgroups || ['tabs']
    _config.setting.subtabs = _config.setting.subtabs || []
    this.setState({
      originActions: _oriActions,
@@ -290,30 +292,49 @@
  handleList = (type, list, card) => {
    const { config } = this.state
    if (list.length > config[type].length) {
      list = list.filter(item => !item.origin)
      this.setState({
        [type + 'loading']: true,
        config: {...config, [type]: list }
      }, () => {
        // 刷新对应的配置信息
    if (type === 'tabs') { // 标签页调整顺序或添加元素
      if (list.length > config[card.groupId].length) {
        list = list.filter(item => !item.origin)
        this.setState({
          [type + 'loading']: false
        })
        if (type === 'search') {
          this.handleSearch(card)
        } else if (type === 'action') {
          this.handleAction(card)
        } else if (type === 'columns') {
          this.handleColumn(card)
        } else if (type === 'tabs') {
          tabloading: true,
          config: {...config, [card.groupId]: list }
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            tabloading: false
          })
          this.handleTab(card)
        }
      })
        })
      } else {
        this.setState({config: {...config, [card.groupId]: list}})
      }
    } else {
      this.setState({config: {...config, [type]: list}})
      if (list.length > config[type].length) {
        list = list.filter(item => !item.origin)
        this.setState({
          [type + 'loading']: true,
          config: {...config, [type]: list }
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            [type + 'loading']: false
          })
          if (type === 'search') {
            this.handleSearch(card)
          } else if (type === 'action') {
            this.handleAction(card)
          } else if (type === 'columns') {
            this.handleColumn(card)
          } else if (type === 'tabs') {
            this.handleTab(card)
          }
        })
      } else {
        this.setState({config: {...config, [type]: list}})
      }
    }
  }
@@ -531,23 +552,23 @@
            MenuID: 'pop',
            text: this.state.dict['header.form.popform']
          }, {
            MenuID: 'popview',
            text: this.state.dict['header.form.popview']
          }, {
            MenuID: 'prompt',
            text: this.state.dict['header.form.prompt']
          }, {
            MenuID: 'exec',
            text: this.state.dict['header.form.exec']
          }, {
            MenuID: 'tab',
            text: this.state.dict['header.form.tab']
          }, {
            MenuID: 'excelIn',
            text: this.state.dict['header.form.excelIn']
          }, {
            MenuID: 'excelOut',
            text: this.state.dict['header.form.excelOut']
          }, {
            MenuID: 'popview',
            text: this.state.dict['header.form.popview']
          }, {
            MenuID: 'tab',
            text: this.state.dict['header.form.tab']
          }, {
            MenuID: 'blank',
            text: this.state.dict['header.form.blank']
@@ -788,11 +809,11 @@
              MenuID: 'text',
              text: this.state.dict['header.form.text']
            }, {
              MenuID: 'picture',
              text: this.state.dict['header.form.picture']
            }, {
              MenuID: 'number',
              text: this.state.dict['header.form.number']
            }, {
              MenuID: 'picture',
              text: this.state.dict['header.form.picture']
            }, {
              MenuID: 'textarea',
              text: this.state.dict['header.form.textarea']
@@ -964,6 +985,7 @@
      this.setState({
        visible: true,
        formtemp: 'columns',
        modalTitle: '编辑-合并列',
        card: card
      })
    }
@@ -972,24 +994,42 @@
  handleTab = (card) => {
    const { config } = this.state
    let menus = []
    config.tabs.forEach(item => {
      if (item.origin || card.uuid === item.uuid) return
      let menu = {
        value: item.uuid,
        text: item.label
    let index = 0 // 筛选下一组
    config.tabgroups.forEach((groupId, i) => {
      if (groupId === card.groupId) {
        index = i
      }
      menus.push(menu)
    })
    if (card.supMenu && card.supMenu !== 'mainTable') {
      let _menu = menus.filter(item => item.value === card.supMenu)[0]
      if (!_menu) {
        card.supMenu = ''
      }
    let menus = []
    let subtabs = card.subtabs || []
    let nextTabId = config.tabgroups[index + 1]
    if (nextTabId) {
      let _tabMap = new Map()
      let _usedTabMap = new Map()
      config[nextTabId].forEach(tab => { // 下级所有的标签
        menus.push(tab)
        _tabMap.set(tab.uuid, true)
      })
      config[card.groupId].forEach(tab => { // 同级标签已选的下级标签
        if (tab.uuid === card.uuid) return
        tab.subtabs.forEach(subtab => {
          _usedTabMap.set(subtab, true)
        })
      })
      console.log(config)
      config.setting.subtabs.forEach(subtab => { // 主表已选的下级标签
        _usedTabMap.set(subtab, true)
      })
      subtabs = subtabs.filter(tab => _tabMap.has(tab.uuid) && !_usedTabMap.has(tab.uuid))
      menus = menus.filter(tab => !_usedTabMap.has(tab.uuid))
    } else {
      subtabs = []
    }
    this.setState({
@@ -1048,21 +1088,12 @@
          }]
        },
        {
          type: 'select',
          key: 'supMenu',
          label: '关联菜单',
          initVal: card.supMenu || '',
          type: 'mutilselect',
          key: 'subtabs',
          label: '下级标签',
          initVal: subtabs,
          required: false,
          options: [
            {
              value: '',
              text: this.state.dict['header.form.empty']
            }, {
              value: 'mainTable',
              text: '主表'
            },
            ...menus
          ]
          options: menus
        }
      ]
    })
@@ -1134,19 +1165,36 @@
          })
        }
  
        _config[res.type] = _config[res.type].map(item => {
          if (item.uuid === res.values.uuid) {
            isupdate = true
            return res.values
          } else {
            return item
        if (res.type !== 'tabs') {
          _config[res.type] = _config[res.type].map(item => {
            if (item.uuid === res.values.uuid) {
              isupdate = true
              return res.values
            } else {
              return item
            }
          })
          _config[res.type] = _config[res.type].filter(item => !item.origin)
          if (!isupdate) { // 操作不是修改,添加元素至列表
            _config[res.type].push(res.values)
          }
        })
        _config[res.type] = _config[res.type].filter(item => !item.origin)
        if (!isupdate) { // 操作不是修改,添加元素至列表
          _config[res.type].push(res.values)
        } else { // 标签页的添加与修改
          _config[res.values.groupId] = _config[res.values.groupId].map(item => {
            if (item.uuid === res.values.uuid) {
              isupdate = true
              return res.values
            } else {
              return item
            }
          })
          _config[res.values.groupId] = _config[res.values.groupId].filter(item => !item.origin)
          if (!isupdate) { // 操作不是修改,添加元素至列表
            _config[res.values.groupId].push(res.values)
          }
        }
  
        if (res.type === 'action') {
          let gridbtn = _config.action.filter(act => act.position === 'grid')
@@ -1172,7 +1220,7 @@
            }
          }
        }
        this.setState({
          config: _config,
          searchloading: true,
@@ -1833,6 +1881,7 @@
      
      let tabParam = { // 添加菜单tab页
        func: 'sPC_sMenusTab_AddUpt',
        MenuID: menu.MenuID,
        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`
        })
@@ -2567,6 +2616,59 @@
    })
  }
  addTabGroup = () => {
    let _this = this
    let _config = JSON.parse(JSON.stringify(this.state.config))
    confirm({
      content: `确定新建标签组吗?`,
      okText: this.state.dict['header.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        let newgroup = 'tabs' + Utils.getuuid()
        _config.tabgroups.push(newgroup)
        _config[newgroup] = []
        _this.setState({
          config: _config,
          tabloading: true
        }, () => {
          _this.setState({
            tabloading: false
          })
        })
      },
      onCancel() {}
    })
  }
  delTabGroup = (groupId) => {
    let _this = this
    let _config = JSON.parse(JSON.stringify(this.state.config))
    confirm({
      content: `确定删除标签组吗?`,
      okText: this.state.dict['header.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        _config.tabgroups = _config.tabgroups.filter(group => group !== groupId)
        delete _config[groupId]
        _this.setState({
          config: _config,
          tabloading: true
        }, () => {
          _this.setState({
            tabloading: false
          })
        })
      },
      onCancel() {}
    })
  }
  render () {
    const configAction = this.state.config.action.filter(_action =>
      !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview' || _action.OpenType === 'blank' || _action.OpenType === 'tab')
@@ -2740,6 +2842,7 @@
                  /> : null
                }
              </div>
              {/* 显示列 */}
              <div className="column-list">
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《显示列》中,选择对应类型的显示列拖至此处添加;或点击《添加显示列》按钮批量添加,选择批量添加时,需提前选择使用表。注:添加合并列时,需设置可选列。">
                  <Icon type="question-circle" />
@@ -2760,22 +2863,26 @@
                  /> : null
                }
              </div>
              <div className="tab-list">
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《标签页》中,选择对应类型的标签页拖至此处添加。">
                  <Icon type="question-circle" />
                </Tooltip>
                {!this.state.tabloading ?
                  <TabDragElement
                    type="tabs"
                    list={this.state.config.tabs}
                    setting={this.state.config.setting}
                    handleList={this.handleList}
                    handleMenu={this.handleTab}
                    deleteMenu={this.deleteElement}
                    placeholder={this.state.dict['header.form.tab.placeholder']}
                  /> : null
                }
              </div>
              {/* 标签组 */}
              {!this.state.tabloading && this.state.config.tabgroups.map((groupId, index) => {
                return (
                  <div key={index} className="tab-list">
                    {index === 0 ? <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《标签页》中,选择对应类型的标签页拖至此处添加。">
                      <Icon type="question-circle" />
                    </Tooltip> : null}
                    {index === 0 ? <Icon type="plus" onClick={this.addTabGroup} /> : null}
                    {index !== 0 ? <Icon type="delete" onClick={() => {this.delTabGroup(groupId)}} /> : null}
                    <TabDragElement
                      type="tabs"
                      groupId={groupId}
                      list={this.state.config[groupId]}
                      handleList={this.handleList}
                      handleMenu={this.handleTab}
                      deleteMenu={this.deleteElement}
                      placeholder={this.state.dict['header.form.tab.placeholder']}
                    />
                  </div>)
              })}
            </Card>
          </div>
        </DndProvider>
@@ -2888,6 +2995,7 @@
          <SettingForm
            dict={this.state.dict}
            menu={this.props.menu}
            config={this.state.config}
            data={this.state.config.setting}
            columns={this.state.config.columns}
            usefulFields={this.props.permFuncField}
src/templates/comtableconfig/index.scss
@@ -126,7 +126,7 @@
    position: relative;
    width: calc(100vw - 235px);
    height: 100%;
    overflow-y: hidden;
    // overflow-y: hidden;
    background: #ffffff;
    .ant-switch.big {
      min-width: 60px;
@@ -156,7 +156,7 @@
    }
    .ant-card-body {
      position: relative;
      padding: 0;
      padding: 0 0 40px;
      .search-list {
        padding: 1px 24px 20px;
@@ -409,18 +409,38 @@
            }
          }
        }
        > .anticon-setting {
        > .anticon-question-circle {
          position: absolute;
          font-size: 18px;
          right: 15px;
          top: 30px;
          left: 5px;
          top: 20px;
        }
        > .anticon-plus {
          position: absolute;
          font-size: 24px;
          right: 25px;
          top: 50px;
          z-index: 1;
          cursor: pointer;
        }
        > .anticon-delete {
          position: absolute;
          font-size: 24px;
          right: 25px;
          top: 50px;
          z-index: 1;
          cursor: pointer;
        }
        .ant-tabs-nav-container-scrolling {
          margin-right: 50px;
        }
      }
      > .anticon-setting {
        position: absolute;
        font-size: 18px;
        right: 15px;
        right: 5px;
        top: 10px;
        padding: 10px;
        z-index: 1;
      }
    }
    .anticon-question-circle {
@@ -430,8 +450,8 @@
      top: 5px;
    }
  }
  .setting:hover {
    overflow-y: auto;
  .setting {
    overflow-y: scroll;
  }
  .setting::-webkit-scrollbar {
    width: 7px;
@@ -440,6 +460,7 @@
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
    background: rgba(0, 0, 0, 0.13);
    display: none;
  }
  .setting::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
@@ -447,6 +468,9 @@
    border: 1px solid rgba(0, 0, 0, 0.07);
    background: rgba(0, 0, 0, 0);
  }
  .setting:hover::-webkit-scrollbar-thumb {
    display: block;
  }
  .ant-spin {
    position: absolute;
    margin-left: calc(50vw - 22px);
src/templates/comtableconfig/settingform/index.jsx
@@ -10,13 +10,43 @@
    dict: PropTypes.object, // 字典项
    menu: PropTypes.object,
    data: PropTypes.object,
    config: PropTypes.object,
    columns: PropTypes.array,
    usefulFields: PropTypes.array
  }
  state = {
    interType: this.props.data.interType || 'inner',
    columns: this.props.columns.filter(item => item.field && item.type !== 'colspan')
    columns: this.props.columns.filter(item => item.field && item.type !== 'colspan'),
    currentTabs: null,
    selectTabs: []
  }
  UNSAFE_componentWillMount() {
    const { config, data } = this.props
    let _tabs = []
    let _select = []
    let _tabMap = new Map()
    config.tabgroups.forEach(groupname => {
      config[groupname].forEach(tab => {
        if (tab.origin) return
        _tabs.push(tab)
        _tabMap.set(tab.uuid, true)
      })
    })
    data.subtabs && data.subtabs.forEach(tabId => {
      if (_tabMap.has(tabId)) {
        _select.push(tabId)
      }
    })
    this.setState({
      currentTabs: _tabs,
      selectTabs: _select
    })
  }
  handleConfirm = () => {
@@ -52,7 +82,7 @@
  render() {
    const { data, dict, menu, usefulFields } = this.props
    const { getFieldDecorator } = this.props.form
    const { interType, columns } = this.state
    const { interType, columns, selectTabs } = this.state
    const formItemLayout = {
      labelCol: {
@@ -229,18 +259,6 @@
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="标签">
              {getFieldDecorator('tabshow', {
                initialValue: data.tabshow || 'horizontal'
              })(
                <Select>
                  <Select.Option value="horizontal">横向显示</Select.Option>
                  <Select.Option value="vertical">纵向显示</Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="初始化">
              {getFieldDecorator('onload', {
                initialValue: data.onload || 'true'
@@ -252,6 +270,28 @@
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="主表可选取关联标签,标签关联后,主表数据切换时,下级标签会跟随主表主键值变化。">
                <Icon type="question-circle" />
                {'下级标签'}
              </Tooltip>
            }>
              {getFieldDecorator('subtabs', {
                initialValue: selectTabs
              })(
                <Select
                  mode="multiple"
                  style={{ width: '100%' }}
                  placeholder="Please select"
                >
                  {this.state.currentTabs.map((option, index) =>
                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
src/templates/comtableconfig/source.jsx
@@ -21,7 +21,7 @@
      interface: '',
      outerFunc: '',
      onload: 'true',
      tabshow: 'horizontal'
      subtabs: []
    },
    tables: [],
    search: [
@@ -206,24 +206,29 @@
    },
    tabs: [
      {
        origin: true,
        origin: true,          // 是否为示例
        groupId: 'tabs',
        uuid: Utils.getuuid(),
        label: 'tab1',
        icon: '',
        type: 'SubTable',
        linkTab: '',
        subtabs: [],
        supMenu: ''
      },
      {
        origin: true,
        groupId: 'tabs',
        uuid: Utils.getuuid(),
        label: 'tab2',
        icon: '',
        type: 'SubTable',
        linkTab: '',
        subtabs: [],
        supMenu: ''
      }
    ]
    ],
    tabgroups: ['tabs']
  }
  searchItems = [
@@ -274,12 +279,6 @@
    },
    {
      type: 'action',
      label: CommonDict['header.form.popview'],
      subType: 'popview',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.prompt'],
      subType: 'prompt',
      url: ''
@@ -292,12 +291,6 @@
    },
    {
      type: 'action',
      label: CommonDict['header.form.tab'],
      subType: 'tab',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.excelIn'],
      subType: 'excelIn',
      url: ''
@@ -306,6 +299,18 @@
      type: 'action',
      label: CommonDict['header.form.excelOut'],
      subType: 'excelOut',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.popview'],
      subType: 'popview',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.tab'],
      subType: 'tab',
      url: ''
    },
    {
@@ -337,14 +342,14 @@
    },
    {
      type: 'columns',
      label: CommonDict['header.form.picture'],
      subType: 'picture',
      label: CommonDict['header.form.number'],
      subType: 'number',
      url: ''
    },
    {
      type: 'columns',
      label: CommonDict['header.form.number'],
      subType: 'number',
      label: CommonDict['header.form.picture'],
      subType: 'picture',
      url: ''
    },
    {
src/templates/comtableconfig/tabdragelement/index.jsx
@@ -12,11 +12,14 @@
  SubTable: subtable
}
const Container = ({list, type, setting, placeholder, handleList, handleMenu, deleteMenu }) => {
const Container = ({list, type, groupId, placeholder, handleList, handleMenu, deleteMenu }) => {
  let target = null
  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)
    handleList(type, _cards)
@@ -48,7 +51,9 @@
      newcard.icon = ''
      newcard.type = item.subType
      newcard.linkTab = Utils.getuuid()
      newcard.subtabs = []
      newcard.supMenu = ''
      newcard.groupId = groupId
      
      let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
      if (target) {
src/templates/comtableconfig/tabform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Select, Icon } from 'antd'
import { Form, Row, Col, Input, Select, Icon, Tooltip } from 'antd'
import Utils from '@/utils/utils.js'
import './index.scss'
@@ -150,6 +150,31 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'mutilselect') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="标签可关联下级标签,下级标签可选范围:相邻的下侧标签组中未被同级或主表关联的标签。">
                <Icon type="question-circle" />
                {item.label}
              </Tooltip>
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(
                <Select
                  mode="multiple"
                  style={{ width: '100%' }}
                  placeholder="Please select"
                >
                  {item.options.map((option, index) =>
                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      }
    })
@@ -163,6 +188,7 @@
        if (!err) {
          values.uuid = this.props.card.uuid
          values.groupId = this.props.card.groupId
          if (!values.linkTab) { // 没有关联标签(新建时),创建新标签Id
            values.linkTab = Utils.getuuid()
src/templates/comtableconfig/tabform/index.scss
@@ -1,3 +1,7 @@
.ant-advanced-search-form.commontable-tab-form {
  min-height: 180px;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
}
src/templates/subtableconfig/index.scss
@@ -126,7 +126,7 @@
    position: relative;
    width: calc(100vw - 235px);
    height: 100%;
    overflow-y: hidden;
    // overflow-y: hidden;
    background: #ffffff;
    .ant-switch.big {
      min-width: 60px;
@@ -425,8 +425,8 @@
      top: 5px;
    }
  }
  .setting:hover {
    overflow-y: auto;
  .setting {
    overflow-y: scroll;
  }
  .setting::-webkit-scrollbar {
    width: 7px;
@@ -435,6 +435,7 @@
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
    background: rgba(0, 0, 0, 0.13);
    display: none;
  }
  .setting::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
@@ -442,6 +443,9 @@
    border: 1px solid rgba(0, 0, 0, 0.07);
    background: rgba(0, 0, 0, 0);
  }
  .setting:hover::-webkit-scrollbar-thumb {
    display: block;
  }
  .ant-spin {
    position: absolute;
    margin-left: calc(50vw - 22px);
src/templates/tableshare/transferform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Transfer } from 'antd'
import { Transfer, Icon } from 'antd'
import './index.scss'
class TransferForm extends Component {
@@ -17,6 +17,9 @@
  }
  handleChange = (nextTargetKeys, direction, moveKeys) => {
    console.log(direction)
    console.log(moveKeys)
    console.log(nextTargetKeys)
    this.setState({ targetKeys: nextTargetKeys })
  }
@@ -46,6 +49,7 @@
        return {
          key: item.uuid,
          title: item.label,
          field: item.field,
          description: ''
        }
      }),
@@ -53,8 +57,47 @@
    })
  }
  changeorder = (e, item, type) => {
    const { targetKeys } = this.state
    e.stopPropagation()
    let _index = 0
    let keys = targetKeys.filter((key, i) => {
      if (key === item.key) {
        _index = i
        return false
      } else {
        return true
      }
    })
    if ((_index === 0 && type === 'up') || (_index === targetKeys.length - 1 && type === 'down')) {
      return
    }
    if (type === 'up') {
      keys.splice(_index - 1, 0, item.key)
    } else {
      keys.splice(_index + 1, 0, item.key)
    }
    this.setState({
      targetKeys: keys
    })
  }
  getItem = (item) => {
    let content = item.title + '(' + item.field + ')'
    return <span title={content}>
      {content}
      <Icon type="arrow-up" onClick={(e) => {this.changeorder(e, item, 'up')}} />
      <Icon type="arrow-down" onClick={(e) => {this.changeorder(e, item, 'down')}} />
    </span>
  }
  render() {
    const { targetKeys, selectedKeys } = this.state
    return (
      <div className="common-table-columns-transfer">
        <Transfer
@@ -65,7 +108,7 @@
          selectedKeys={selectedKeys}
          onChange={this.handleChange}
          onSelectChange={this.handleSelectChange}
          render={item => item.title}
          render={this.getItem}
        />
      </div>
    )
src/templates/tableshare/transferform/index.scss
@@ -2,5 +2,26 @@
  padding-left: 18px;
  .ant-transfer-list {
    width: 296px;
    i {
      display: none;
    }
  }
  .ant-transfer-operation + .ant-transfer-list {
    .ant-transfer-list-content-item {
      margin-right: 50px;
      i {
        display: inline-block;
      }
      .anticon {
        position: absolute;
        right: 10px;
        color: #ff4d4f;
      }
      .anticon-arrow-up {
        position: absolute;
        right: 30px;
        color: #1890ff;
      }
    }
  }
}