king
2020-11-20 6e106eb13ce404d9955d6c9045d21050d3e08294
2020-11-20
27个文件已修改
10个文件已添加
1668 ■■■■ 已修改文件
src/components/barcode/index.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/barcode/index.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.jsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.scss 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx 124 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/index.jsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/chartcompile/index.jsx 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/card.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.scss 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/settingform/index.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/normalform/index.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/normalform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/cardcomponent/index.jsx 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/cardcomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/cardcomponent/settingform/index.jsx 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/cardcomponent/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.scss 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabsetting/settingform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/search/main-search/index.jsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/search/main-search/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/searchform/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/customscript/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sso/index.jsx 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sso/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/barcode/index.jsx
@@ -11,6 +11,10 @@
    value: PropTypes.any,    // 条码值
  }
  state = {
    error: false
  }
  componentDidMount () {
    this.resetBarcode()
  }
@@ -28,22 +32,30 @@
    let style = card.style || {}
    JsBarcode(this.barcode, value, {
      displayValue: card.displayValue === 'true',
      width: card.interval || 1,
      height: card.barHeight || 25,
      margin: 0,
      fontOptions: `${style.fontWeight || ''} ${style.fontStyle || ''}`,
      textAlign: style.textAlign || 'left',
      fontSize: (style.fontSize || 14) + 'px',
      lineColor: style.color || '#000000'
    })
    try {
      JsBarcode(this.barcode, value, {
        displayValue: card.displayValue === 'true',
        width: card.interval || 1,
        height: card.barHeight || 25,
        margin: 0,
        fontOptions: `${style.fontWeight || ''} ${style.fontStyle || ''}`,
        textAlign: style.textAlign || 'left',
        fontSize: (style.fontSize || 14) + 'px',
        lineColor: style.color || '#000000'
      })
      this.setState({error: false})
    } catch {
      this.setState({error: true})
    }
  }
  render() {
    const { error } = this.state
    return (
      <div className="barcode-box">
        {error ? <span className="barcode-error">字符非法或过长!</span> : null}
        <svg ref={(ref) => { this.barcode = ref }}/>
      </div>
    )
src/components/barcode/index.scss
@@ -1,5 +1,14 @@
.barcode-box {
  overflow-y: hidden;
  height: 100%;
  svg {
    vertical-align: top;
  }
  .barcode-error {
    font-size: 12px;
    vertical-align: top;
  }
  .barcode-error + svg {
    display: none;
  }
}
src/components/header/index.jsx
@@ -193,7 +193,7 @@
          }
          return item
        }),
        systems: result.Systems.filter(sys => sys.LinkUrl1 && sys.AppKey !== window.GLOB.appkey)
        systems: sessionStorage.getItem('isEditState') === 'true' ? [] : result.Systems.filter(sys => sys.LinkUrl1 && sys.AppName && sys.AppKey !== window.GLOB.appkey)
      })
    } else {
      notification.error({
@@ -275,10 +275,10 @@
          })
        }
        resolve({permAction: _permAction})
        resolve(_permAction)
      })
    })
    // 获取主菜单参数
    let promiseMenu = new Promise(resolve => {
      let _param = {func: 'sPC_Get_MainMenu', systemType: options.sysType}
@@ -314,7 +314,7 @@
              }
              return item
            }),
            systems: result.Systems.filter(sys => sys.LinkUrl1 && sys.AppKey !== window.GLOB.appkey)
            systems: result.Systems.filter(sys => sys.LinkUrl1 && sys.AppName && sys.AppKey !== window.GLOB.appkey)
          })
        } else if (result) {
          notification.error({
@@ -333,9 +333,9 @@
      this.props.modifyMainMenu(_mainMenu)
    }
    let _role = await promiseRole
    let permAction = await promiseRole
    this.props.initActionPermission(_role.permAction)
    this.props.initActionPermission(permAction)
  }
  reload = () => {
@@ -547,11 +547,18 @@
  }
  changeSystem = (system) => {
    window.location.href = system.LinkUrl1 + '#/ssologin/' + window.btoa(window.encodeURIComponent(JSON.stringify({
    let href = system.LinkUrl1 + 'index.html#/ssologin/' + window.btoa(window.encodeURIComponent(JSON.stringify({
      UserID: sessionStorage.getItem('UserID'),
      LoginUID: sessionStorage.getItem('LoginUID'),
      User_Name: sessionStorage.getItem('User_Name')
      User_Name: sessionStorage.getItem('User_Name'),
      Full_Name: sessionStorage.getItem('Full_Name'),
      avatar: sessionStorage.getItem('avatar'),
      dataM: system.dataM ? 'true' : '',
      debug: system.debug || '',
      role_id: system.role_id || ''
    })))
    window.open(href)
  }
  dropdownMenuChange = (visible) => {
@@ -677,11 +684,11 @@
          <Switch size="small" className="edit-switch" disabled={!!this.props.editLevel} checked={this.props.editState} onChange={this.changeEditState} />
        </Menu.Item>}
        {!this.props.editState ? <Menu.Item key="password" onClick={this.changePassword}>{this.state.dict['main.password']}</Menu.Item> : null}
        {/* {this.state.systems.length > 0 ? <Menu.SubMenu title="切换系统">
        {this.state.systems.length > 0 ? <Menu.SubMenu className="header-subSystem-box" title="切换系统">
          {this.state.systems.map((system, index) => (
            <Menu.Item className="header-subSystem" key={'sub' + index} onClick={() => {this.changeSystem(system)}}> {system.AppName} </Menu.Item>
          ))}
        </Menu.SubMenu> : null} */}
        </Menu.SubMenu> : null}
        <Menu.Item key="doc" onClick={this.gotoDoc}>{this.state.dict['main.doc']}</Menu.Item>
        {oriVersion ? <Menu.Item key="verup" onClick={this.verup}>
          <Badge dot={oriVersion !== newVersion}>{this.state.dict['main.verup']}</Badge>
src/components/header/index.scss
@@ -194,9 +194,15 @@
  }
}
.header-dropdown {
  li {
  >li {
    padding: 5px 25px;
  }
  >li.ant-dropdown-menu-submenu {
    padding: 0px;
    .ant-dropdown-menu-submenu-title {
      padding: 5px 25px;
    }
  }
}
.header-subSystem {
  min-width: 100px;
src/menu/components/card/data-card/index.scss
@@ -29,8 +29,9 @@
  }
  .card-item {
    overflow-y: hidden;
    overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
src/menu/components/card/prop-card/index.scss
@@ -29,8 +29,9 @@
  }
  .card-item {
    overflow-y: hidden;
    overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
@@ -8,34 +8,8 @@
 * @param {object} card       // 图表对象
 * @param {Array}  columns    // 显示列
 */
export function getBarOrLineChartOptionForm (card, columns, sysRoles = [], MenuType) {
  let shapes = []
export function getBaseForm (card, sysRoles = [], MenuType) {
  let _sysRoles = sysRoles.map(item => ({...item, field: item.value, label: item.text}))
  if (card.chartType === 'line') {
    shapes = [
      { field: 'smooth', label: 'smooth' },
      { field: 'line', label: 'line' },
      { field: 'dot', label: 'dot' },
      { field: 'dash', label: 'dash' },
      { field: 'hv', label: 'hv' },
      { field: 'vh', label: 'vh' },
      { field: 'hvh', label: 'hvh' },
      { field: 'vhv', label: 'vhv' }
    ]
  } else if (card.chartType === 'bar') {
    shapes = [
      { field: 'rect', label: 'rect' },
      { field: 'hollow-rect', label: 'hollow-rect' },
      { field: 'line', label: 'line' },
      { field: 'tick', label: 'tick' },
      { field: 'funnel', label: 'funnel' },
      { field: 'pyramid', label: 'pyramid' }
    ]
  }
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  return [
    {
@@ -74,6 +48,53 @@
      decimal: 0,
      required: true
    },
    {
      type: 'select',
      key: 'blacklist',
      label: '黑名单',
      initVal: card.blacklist || [],
      multi: true,
      required: false,
      forbid: MenuType === 'billPrint',
      options: _sysRoles
    }
  ]
}
/**
 * @description 获取图表视图配置表单
 * @param {object} card       // 图表对象
 * @param {Array}  columns    // 显示列
 */
export function getOptionForm (card, columns, MenuType) {
  let shapes = []
  if (card.chartType === 'line') {
    shapes = [
      { field: 'smooth', label: 'smooth' },
      { field: 'line', label: 'line' },
      { field: 'dot', label: 'dot' },
      { field: 'dash', label: 'dash' },
      { field: 'hv', label: 'hv' },
      { field: 'vh', label: 'vh' },
      { field: 'hvh', label: 'hvh' },
      { field: 'vhv', label: 'vhv' }
    ]
  } else if (card.chartType === 'bar') {
    shapes = [
      { field: 'rect', label: 'rect' },
      { field: 'hollow-rect', label: 'hollow-rect' },
      { field: 'line', label: 'line' },
      { field: 'tick', label: 'tick' },
      { field: 'funnel', label: 'funnel' },
      { field: 'pyramid', label: 'pyramid' }
    ]
  }
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  return [
    {
      type: 'radio',
      key: 'datatype',
@@ -155,7 +176,7 @@
    {
      type: 'radio',
      key: 'tooltip',
      label: '提示信息',
      label: '悬浮提示',
      initVal: card.tooltip || 'true',
      required: false,
      options: [{
@@ -164,20 +185,6 @@
      }, {
        value: 'false',
        text: '隐藏'
      }]
    },
    {
      type: 'radio',
      key: 'coordinate',
      label: '坐标',
      initVal: card.coordinate || 'angle',
      required: false,
      options: [{
        value: 'angle',
        text: '二维坐标'
      }, {
        value: 'polar',
        text: '极坐标'
      }]
    },
    {
@@ -200,6 +207,7 @@
      key: 'transpose',
      label: '变换',
      initVal: card.transpose || 'false',
      tooltip: '横纵坐标轴交换',
      required: false,
      options: [{
        value: 'true',
@@ -212,21 +220,21 @@
    {
      type: 'radio',
      key: 'show',
      label: '显示值',
      label: '格式化',
      initVal: card.show || 'value',
      required: false,
      options: [{
        value: 'value',
        text: '无'
      }, {
        value: 'percent',
        text: '百分比'
      }, {
        value: 'value',
        text: '数值'
      }]
    },
    {
      type: 'radio',
      key: 'label',
      label: '标注-值',
      label: '标注值',
      initVal: card.label || 'false',
      required: false,
      options: [{
@@ -267,6 +275,19 @@
        text: '累加'
      }]
    }, {
      type: 'radio',
      key: 'coordinate',
      label: '坐标',
      initVal: card.coordinate || 'angle',
      required: false,
      options: [{
        value: 'angle',
        text: '二维坐标'
      }, {
        value: 'polar',
        text: '极坐标'
      }]
    }, {
      type: 'number',
      key: 'InfoDefNumber',
      label: '展示数',
@@ -302,15 +323,6 @@
        value: 'white',
        text: '白色'
      }]
    }, {
      type: 'select',
      key: 'blacklist',
      label: '黑名单',
      initVal: card.blacklist || [],
      multi: true,
      required: false,
      forbid: MenuType === 'billPrint',
      options: _sysRoles
    }
  ]
}
src/menu/components/chart/antv-bar/chartcompile/index.jsx
@@ -5,13 +5,14 @@
import Utils from '@/utils/utils.js'
import { chartColors } from '@/utils/option.js'
import { getBarOrLineChartOptionForm } from './formconfig'
import { getBaseForm, getOptionForm } from './formconfig'
import asyncComponent from '@/utils/asyncComponent'
import ColorSketch from '@/mob/colorsketch'
import './index.scss'
const { TabPane } = Tabs
const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const NormalForm = asyncComponent(() => import('@/menu/components/share/normalform'))
class LineChartDrawerForm extends Component {
  static propTpyes = {
@@ -29,6 +30,7 @@
    datatype: '',
    plot: null,
    formlist: null,
    baseFormlist: null,
    fieldName: null,
    colorColumns: [
      {
@@ -166,7 +168,8 @@
      datatype: config.plot.datatype || 'query',
      fieldName: fieldName,
      plot: fromJS(config.plot).toJS(),
      formlist: getBarOrLineChartOptionForm(config.plot, config.columns, sysRoles, MenuType)
      baseFormlist: getBaseForm(config.plot, sysRoles, MenuType),
      formlist: getOptionForm(config.plot, config.columns, MenuType)
    })
  }
@@ -218,7 +221,7 @@
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly}/>)}
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.onSubmit}/>)}
            </Form.Item>
          </Col>
        )
@@ -239,7 +242,7 @@
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<InputNumber min={item.min} max={item.max} precision={item.decimal} />)}
              })(<InputNumber min={item.min} max={item.max} precision={item.decimal} onPressEnter={this.onSubmit}/>)}
            </Form.Item>
          </Col>
        )
@@ -341,8 +344,10 @@
          if (values.datatype === 'statistics' || values.datatype !== plot.datatype) {
            _plot.enabled = 'false'
            _plot.customs = []
          } else if (!values.Yaxis || !plot.Yaxis || !is(fromJS(values.Yaxis), fromJS(plot.Yaxis))) {
            _plot.enabled = 'false'
            _plot.customs = []
            _plot.colors = null
          }
@@ -357,6 +362,17 @@
          this.props.plotchange({...config, plot: _plot})
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        let _plot = {...plot, ...res}
        this.setState({
          plot: _plot,
          visible: false
        })
        this.props.plotchange({...config, plot: _plot})
      })
    } else {
      this.setState({
@@ -397,7 +413,7 @@
                name: labels[item] || item,
                axis: i === 0 ? 'true' : 'false',
                label: 'false',
                shape: _plot.chartType === 'bar' ? ['bar', 'rect'] : ['line', 'smooth']
                shape: _plot.chartType === 'bar' && i === 0 ? ['bar', 'rect'] : ['line', 'smooth']
              }
            })
          }
@@ -423,6 +439,13 @@
            view: tab
          })
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        this.setState({
          plot: {...plot, ...res},
          view: tab
        })
      })
    } else {
      this.setState({
@@ -457,7 +480,7 @@
  }
  render() {
    const { view, visible, datatype, plot, colorColumns, statColorColumns, cusColumns } = this.state
    const { view, visible, datatype, plot, colorColumns, statColorColumns, cusColumns, baseFormlist } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -483,7 +506,10 @@
          destroyOnClose
        >
          <Tabs activeKey={view} className="menu-chart-edit-box" onChange={this.changeTab}>
            <TabPane tab="基础设置" key="normal">
            <TabPane tab="组件设置" key="base">
              <NormalForm dict={this.props.dict} formlist={baseFormlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.baseRef = inst}/>
            </TabPane>
            <TabPane tab="图表设置" key="normal">
              <Form {...formItemLayout}>
                <Row gutter={16}>{this.getFields()}</Row>
              </Form>
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
@@ -8,48 +8,11 @@
 * @param {object} card       // 图表对象
 * @param {Array}  columns    // 显示列
 */
export function getPieChartOptionForm (card, columns, sysRoles = [], MenuType) {
export function getOptionForm (card, columns) {
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  let _sysRoles = sysRoles.map(item => ({...item, field: item.value, label: item.text}))
  return [
    {
      type: 'text',
      key: 'title',
      label: '标题',
      initVal: card.title,
      required: false
    },
    {
      type: 'text',
      key: 'name',
      label: '组件名称',
      initVal: card.name,
      tooltip: '用于组件间的区分。',
      required: true
    },
    {
      type: 'number',
      key: 'width',
      label: '宽度',
      initVal: card.width,
      tooltip: '栅格布局,每行等分为24列。',
      min: 1,
      max: 24,
      decimal: 0,
      required: true
    },
    {
      type: 'number',
      key: 'height',
      label: '高度',
      initVal: card.height,
      min: 100,
      max: 1000,
      decimal: 0,
      required: true
    },
    {
      type: 'radio',
      key: 'shape',
@@ -126,7 +89,7 @@
    {
      type: 'radio',
      key: 'tooltip',
      label: '提示信息',
      label: '悬浮提示',
      initVal: card.tooltip || 'true',
      required: false,
      options: [{
@@ -140,21 +103,21 @@
    {
      type: 'radio',
      key: 'show',
      label: '显示值',
      label: '格式化',
      initVal: card.show || 'value',
      required: false,
      options: [{
        value: 'value',
        text: '无'
      }, {
        value: 'percent',
        text: '百分比'
      }, {
        value: 'value',
        text: '数值'
      }]
    },
    {
      type: 'radio',
      key: 'label',
      label: '标注',
      label: '标注值',
      initVal: card.label || 'false',
      required: false,
      options: [{
@@ -197,15 +160,6 @@
        value: 'white',
        text: '白色'
      }]
    }, {
      type: 'select',
      key: 'blacklist',
      label: '黑名单',
      initVal: card.blacklist || [],
      multi: true,
      required: false,
      forbid: MenuType === 'billPrint',
      options: _sysRoles
    }
  ]
}
src/menu/components/chart/antv-pie/chartcompile/index.jsx
@@ -4,7 +4,8 @@
import { Modal, Form, Row, Col, Select, Icon, Radio, Tooltip, Input, InputNumber, Tabs, Button } from 'antd'
import Utils from '@/utils/utils.js'
import { getPieChartOptionForm } from './formconfig'
import { getOptionForm } from './formconfig'
import { getBaseForm } from '../../antv-bar/chartcompile/formconfig'
import asyncComponent from '@/utils/asyncComponent'
import ColorSketch from '@/mob/colorsketch'
import './index.scss'
@@ -12,6 +13,7 @@
const { TabPane } = Tabs
const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const NormalForm = asyncComponent(() => import('@/menu/components/share/normalform'))
class LineChartDrawerForm extends Component {
  static propTpyes = {
@@ -27,6 +29,7 @@
    visible: false,
    plot: null,
    formlist: null,
    baseFormlist: null,
    view: 'normal',
    colorColumns: [
      {
@@ -56,7 +59,8 @@
      visible: true,
      view: 'normal',
      plot: fromJS(config.plot).toJS(),
      formlist: getPieChartOptionForm(config.plot, config.columns, sysRoles, MenuType)
      baseFormlist: getBaseForm(config.plot, sysRoles, MenuType),
      formlist: getOptionForm(config.plot, config.columns)
    })
  }
@@ -216,26 +220,6 @@
    return fields
  }
  axisChange = (e) => {
    const { plot } = this.state
    let val = e.target.value
    let fieldvalue = {}
    plot.customs.forEach(item => {
      if (this.props.form.getFieldValue(item.field + '$axis') === val) {
        fieldvalue[item.field + '$axis'] = 'unset'
      }
    })
    this.props.form.setFieldsValue(fieldvalue)
  }
  enabledChange = (e) => {
    let val = e.target.value
    this.setState({enabled: val})
  }
  onSubmit = () => {
    const { config } = this.props
    const { plot, view } = this.state
@@ -253,6 +237,17 @@
          this.props.plotchange({...config, plot: _plot})
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        let _plot = {...plot, ...res}
        this.setState({
          plot: _plot,
          visible: false
        })
        this.props.plotchange({...config, plot: _plot})
      })
    } else {
      this.setState({
        visible: false
@@ -263,9 +258,9 @@
  }
  changeTab = (tab) => {
    const { plot } = this.state
    const { plot, view } = this.state
    if (tab === 'color') {
    if (view === 'normal') {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          let _plot = {...plot, ...values}
@@ -275,6 +270,15 @@
            view: tab
          })
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        let _plot = {...plot, ...res}
        this.setState({
          plot: _plot,
          view: tab
        })
      })
    } else {
      this.setState({
@@ -303,7 +307,7 @@
  }
  render() {
    const { visible, plot, colorColumns, view } = this.state
    const { visible, plot, colorColumns, view, baseFormlist } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -329,7 +333,10 @@
          destroyOnClose
        >
          <Tabs activeKey={view} className="menu-chart-edit-box" onChange={this.changeTab}>
            <TabPane tab="基础设置" key="normal">
            <TabPane tab="组件设置" key="base">
              <NormalForm dict={this.props.dict} formlist={baseFormlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.baseRef = inst}/>
            </TabPane>
            <TabPane tab="图表设置" key="normal">
              <Form {...formItemLayout}>
                <Row gutter={16}>{this.getFields()}</Row>
              </Form>
src/menu/components/search/main-search/dragsearch/card.jsx
@@ -62,7 +62,7 @@
        <Icon className="close" title="delete" type="close" onClick={() => delCard(id)} />
      </div>
    } trigger="hover">
      <div className="page-card" style={{ opacity: opacity}}>
      <div className={'page-card ' + card.labelShow} style={{ opacity: opacity}}>
        <div ref={node => drag(drop(node))}>
          <div className="ant-row ant-form-item">
            <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
src/menu/components/search/main-search/dragsearch/index.jsx
@@ -2,7 +2,7 @@
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Col } from 'antd'
import { Col, Button } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
@@ -92,7 +92,17 @@
          />
        </Col>
      ))}
      {cards.length > 0 ? <Col key="action" className="action" span={6}>
        <div className="ant-row ant-form-item" style={{lineHeight: '40px', height: '55px', marginBottom: 0}}>
          <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
          </div>
          <div className="ant-col ant-form-item-control-wrapper ant-col-xs-24 ant-col-sm-16">
            <Button type="primary">搜索</Button>
            <Button style={{ marginLeft: 8 }}>重置</Button>
            <div style={{position: 'absolute', top: 0, bottom: 0, left: 0, right: 0}}></div>
          </div>
        </div>
      </Col> : null}
      {cards.length === 0 ?
        <div className="common-drawarea-placeholder">
          {placeholder}
src/menu/components/search/main-search/index.jsx
@@ -343,7 +343,7 @@
    const { dict, card, visible, sqlVerifing } = this.state
    return (
      <div className="main-search-edit-list" style={card.style}>
      <div className={`main-search-edit-list ${card.wrap.float} ${card.wrap.show}`} style={card.style}>
        <DragElement
          list={card.search}
          handleList={this.handleList}
src/menu/components/search/main-search/index.scss
@@ -1,10 +1,11 @@
.main-search-edit-list {
  min-height: 50px;
  position: relative;
  background: #ffffff;
  >.anticon-tool {
    position: absolute;
    z-index: 1;
    z-index: 3;
    font-size: 16px;
    right: 1px;
    top: 1px;
@@ -23,7 +24,6 @@
  }
  .page-card {
    position: relative;
    background: #ffffff;
    border-radius: 2px;
    padding-bottom: 15px;
    .ant-form-item {
@@ -65,21 +65,10 @@
        }
      }
    }
    .edit {
      position: absolute;
      left: 0;
      top: 5px;
      color: #1890ff;
      cursor: pointer;
  }
  .page-card.false {
    .ant-form-item-label {
      display: none;
    }
    .edit.copy {
      left: 20px;
      color: #26C281;
    }
    .edit.close {
      left: 40px;
      color: #ff4d4f;
    }
  }
  .page-card:hover {
@@ -92,6 +81,23 @@
    width: 100%;
  }
}
.main-search-edit-list.right {
  >.ant-row {
    >.ant-col {
      float: right;
    }
    >.ant-col.action {
      display: none;
    }
  }
}
.main-search-edit-list.false {
  >.ant-row {
    >.ant-col.action {
      display: none;
    }
  }
}
.main-search-edit-list::after {
  display: block;
  content: ' ';
src/menu/components/search/main-search/wrapsetting/settingform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Tooltip, Icon, InputNumber, Select } from 'antd'
import { Form, Row, Col, Input, Tooltip, Icon, InputNumber, Select, Radio } from 'antd'
import './index.scss'
@@ -10,6 +10,10 @@
    wrap: PropTypes.object,      // 数据源配置
    sysRoles: PropTypes.array,   // 角色列表
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    float: this.props.wrap.float
  }
  handleConfirm = () => {
@@ -36,6 +40,7 @@
  render() {
    const { wrap, sysRoles } = this.props
    const { getFieldDecorator } = this.props.form
    const { float } = this.state
    const formItemLayout = {
      labelCol: {
@@ -89,6 +94,35 @@
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="右对齐时,隐藏搜索按钮。">
                  <Icon type="question-circle" />
                  对齐
                </Tooltip>
              }>
                {getFieldDecorator('float', {
                  initialValue: wrap.float || 'left'
                })(
                  <Radio.Group onChange={(e) => this.setState({float: e.target.value})}>
                    <Radio value="left">左对齐</Radio>
                    <Radio value="right">右对齐</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {float !== 'right' ? <Col span={12}>
              <Form.Item label="搜索按钮">
                {getFieldDecorator('show', {
                  initialValue: wrap.show || 'true'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
                  initialValue: wrap.blacklist || []
src/menu/components/share/normalform/index.jsx
New file
@@ -0,0 +1,170 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Tooltip, Icon, InputNumber, Select, Radio } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    formlist: PropTypes.array,   // 表单
    inputSubmit: PropTypes.func  // 回车事件
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  getFields() {
    const { formlist } = this.props
    const { getFieldDecorator } = this.props.form
    const fields = []
    if (!formlist) return []
    formlist.forEach((item, index) => {
      if (item.hidden || item.forbid) return
      if (item.type === 'text') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit}/>)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'number') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<InputNumber min={item.min} max={item.max} precision={item.decimal} onPressEnter={this.handleSubmit}/>)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'select') { // 下拉
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Select mode={item.multi ? 'multiple' : ''}>
                  {item.options.map((option, index) =>
                    <Select.Option key={index} value={option.field}>
                      {option.label}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Radio.Group disabled={item.readonly} onChange={(e) => this.radioChange(e, item.key)}>
                  {item.options.map(option => {
                    return (
                      <Radio key={option.value} value={option.value}>{option.text}</Radio>
                    )
                  })}
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
  }
  render() {
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-normal-form">
        <Form {...formItemLayout}><Row gutter={16}>{this.getFields()}</Row></Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/share/normalform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-normal-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/menu/components/table/normal-table/cardcomponent/index.jsx
New file
@@ -0,0 +1,208 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Popover, Icon } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const CardCellComponent = asyncComponent(() => import('../../cardcellcomponent'))
class CardBoxComponent extends Component {
  static propTpyes = {
    cards: PropTypes.object,         // 卡片行配置信息
    card: PropTypes.object,          // 卡片配置信息
    deleteElement: PropTypes.func,   // 卡片删除
    updateElement: PropTypes.func    // 菜单配置更新
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,            // 卡片信息,包括正反面
    formlist: null,        // 设置表单信息
    elements: null,        // 编辑组
    visible: false,        // 模态框控制
    settingVisible: false,
  }
  /**
   * @description 搜索条件初始化
   */
  UNSAFE_componentWillMount () {
    const { card } = this.props
    this.setState({
      card: fromJS(card).toJS(),
      elements: fromJS(card.elements).toJS(),
    })
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.cards), fromJS(nextProps.cards)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  getStyle = (comIds, style) => {
    const { cards } = this.props
    const { card } = this.state
    if (comIds.length !== 2 || comIds[0] !== cards.uuid || comIds[1] !== card.uuid) return
    let _card = fromJS(card).toJS()
    _card.style = style
    this.setState({
      card: _card
    })
    this.props.updateElement(_card)
  }
  updateCard = (elements) => {
    const { card } = this.state
    let _card = {...card, elements: elements}
    this.setState({
      card: _card
    })
    this.props.updateElement(_card)
  }
  addElement = () => {
    const { cards } = this.props
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.eleType = 'text'
    newcard.datatype = 'dynamic'
    newcard.color = 'rgba(0,0,0,0.85)'
    newcard.padding = '5px'
    newcard.align = 'left'
    // 注册事件-添加元素
    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
  }
  addButton = () => {
    const { cards } = this.props
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.eleType = 'button'
    newcard.label = 'button'
    newcard.sqlType = ''
    newcard.Ot = 'requiredSgl'
    newcard.OpenType = 'prompt'
    newcard.icon = ''
    newcard.class = 'primary'
    newcard.intertype = 'system'
    newcard.method = 'POST'
    newcard.execSuccess = 'grid'
    newcard.execError = 'never'
    newcard.popClose = 'never'
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'link'
    // 注册事件-添加元素
    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
  }
  changeStyle = () => {
    const { cards } = this.props
    const { card } = this.state
    let _style = null
    let options = ['height', 'background', 'border', 'padding', 'margin']
    _style = card.style ? fromJS(card.style).toJS() : {}
    MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, _style)
  }
  settingSubmit = () => {
    const { card } = this.state
    this.settingRef.handleConfirm().then(res => {
      this.setState({
        settingVisible: false,
        card: {...card, setting: res}
      })
      this.props.updateElement({...card, setting: res})
    })
  }
  render() {
    const { cards } = this.props
    const { card, elements, settingVisible, dict } = this.state
    return (
      <div className="ant-col ant-col-24">
        <div className="card-item" style={card.style}>
          <CardCellComponent cards={cards} cardCell={card} elements={elements} updateElement={this.updateCard}/>
          <div className="card-control">
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
              <div className="mk-popover-control">
                <Icon className="plus" title="添加元素" onClick={this.addElement} type="plus" />
                <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
                <Icon className="edit" type="edit" onClick={() => this.setState({settingVisible: true})} />
                <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
                <Icon className="close" title="删除卡片" type="delete" onClick={() => this.props.deleteElement(card)} />
              </div>
            } trigger="hover">
              <Icon type="tool" />
            </Popover>
          </div>
        </div>
        <Modal
          wrapClassName="popview-modal"
          title={'行设置'}
          visible={settingVisible}
          width={700}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.settingSubmit}
          onCancel={() => { this.setState({ settingVisible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            cards={cards}
            setting={card.setting}
            inputSubmit={this.settingSubmit}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default CardBoxComponent
src/menu/components/table/normal-table/cardcomponent/index.scss
src/menu/components/table/normal-table/cardcomponent/settingform/index.jsx
New file
@@ -0,0 +1,131 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Radio, Tooltip, Icon, Input, Select } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    cards: PropTypes.object,     // 卡片集
    setting: PropTypes.object,   // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    condition: this.props.setting.condition || 'false'
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  render() {
    const { setting, cards } = this.props
    const { getFieldDecorator } = this.props.form
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="当选择“有”时,只有符合条件的数据才会展示。">
                  <Icon type="question-circle" />
                  显示条件
                </Tooltip>
              }>
                {getFieldDecorator('condition', {
                  initialValue: setting.condition || 'false'
                })(
                  <Radio.Group onChange={(e) => this.setState({ condition: e.target.value })}>
                    <Radio value="true">有</Radio>
                    <Radio value="false">无</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {this.state.condition === 'true' ? <Col span={12}>
              <Form.Item label="控制字段">
                {getFieldDecorator('controlField', {
                  initialValue: setting.controlField || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '控制字段!'
                    }
                  ]
                })(
                  <Select>
                    {cards.columns.map((option, index) =>
                      <Select.Option key={index} value={option.field}>
                        {option.label}
                      </Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col> : null}
            {this.state.condition === 'true' ? <Col span={12}>
              <Form.Item label="对比方式">
                {getFieldDecorator('controlType', {
                  initialValue: setting.controlType || '=',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '对比方式!'
                    }
                  ]
                })(
                  <Radio.Group>
                    <Radio value="=">=</Radio>
                    <Radio value="!=">!=</Radio>
                    <Radio value=">">&gt;</Radio>
                    <Radio value="<">&lt;</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {this.state.condition === 'true' ? <Col span={12}>
              <Form.Item label="对比值">
                {getFieldDecorator('controlValue', {
                  initialValue: setting.controlValue || ''
                })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col> : null}
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/table/normal-table/cardcomponent/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/menu/components/table/normal-table/index.jsx
New file
@@ -0,0 +1,289 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { Icon, Popover, Modal } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import './index.scss'
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const WrapComponent = asyncIconComponent(() => import('../data-card/wrapsetting'))
const SearchComponent = asyncComponent(() => import('@/menu/searchcomponent'))
const CardComponent = asyncComponent(() => import('./cardcomponent'))
const { confirm } = Modal
class TableCardEditComponent extends Component {
  static propTpyes = {
    card: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let subcards = null
      if (card.config) {
        subcards = JSON.parse(card.config)
        subcards = subcards.map(scard => {
          scard.uuid = Utils.getuuid()
          scard.elements = scard.elements.map(elem => {
            elem.uuid = Utils.getuuid()
            return elem
          })
          return scard
        })
      } else {
        subcards = [{
          uuid: Utils.getuuid(),
          setting: { width: 24, type: 'simple'},
          style: {
            paddingTop: '5px', paddingBottom: '5px', paddingLeft: '15px', paddingRight: '15px',
          },
          elements: []
        }]
      }
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        tabId: card.tabId || '',
        parentId: card.parentId || '',
        format: 'array',    // 组件属性 - 数据格式
        pageable: true,     // 组件属性 - 是否可分页
        switchable: false,  // 组件属性 - 数据是否可切换
        dataName: card.dataName || '',
        width: 12,
        search: [],
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, width: 12 },
        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
        headerStyle: { fontSize: '16px' },
        columns: [],
        scripts: [],
        subcards: subcards
      }
      this.setState({
        card: _card
      })
      this.props.updateConfig(_card)
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  /**
   * @description 卡片行外层信息更新(数据源,样式等)
   */
  updateComponent = (component) => {
    this.setState({
      card: component
    })
    component.width = component.wrap.width
    component.name = component.wrap.name
    this.props.updateConfig(component)
  }
  /**
   * @description 单个卡片信息更新
   */
  updateCard = (cell) => {
    let card = fromJS(this.state.card).toJS()
    card.subcards = card.subcards.map(item => {
      if (item.uuid === cell.uuid) return cell
      return item
    })
    this.setState({card})
    this.props.updateConfig(card)
  }
  /**
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    let card = fromJS(this.state.card).toJS()
    let _this = this
    confirm({
      content: '确定删除卡片吗?',
      onOk() {
        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
        _this.setState({card})
        _this.props.updateConfig(card)
      },
      onCancel() {}
    })
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
  }
  changeTitleStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid, 'header'], ['font', 'border'], card.headerStyle)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds[0] !== card.uuid) return
    let _card = {}
    if (comIds.length === 1) {
      _card = {...card, style}
    } else if (comIds.length === 2 && comIds[1] === 'header') {
      _card = {...card, headerStyle: style}
    } else {
      return
    }
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  addCard = () => {
    let card = fromJS(this.state.card).toJS()
    let newcard = {
      uuid: Utils.getuuid(),
      setting: { width: 6, type: 'simple'},
      style: {
        paddingTop: '5px', paddingBottom: '5px', paddingLeft: '15px', paddingRight: '15px',
      },
      elements: []
    }
    if (card.subcards.length > 0) {
      newcard = fromJS(card.subcards[card.subcards.length - 1]).toJS()
      newcard.uuid = Utils.getuuid()
      newcard.elements = newcard.elements.map(elem => {
        elem.uuid = Utils.getuuid()
        return elem
      })
    }
    card.subcards.push(newcard)
    this.setState({card})
    this.props.updateConfig(card)
  }
  addSearch = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.label = 'label'
    newcard.initval = ''
    newcard.type = 'select'
    newcard.resourceType = '0'
    newcard.options = []
    newcard.setAll = 'false'
    newcard.orderType = 'asc'
    newcard.display = 'dropdown'
    newcard.match = '='
    // 注册事件-添加搜索
    MKEmitter.emit('addSearch', card.uuid, newcard)
  }
  render() {
    const { menu } = this.props
    const { card } = this.state
    return (
      <div className="menu-table-card-edit-box" style={{...card.style, height: card.wrap.height}}>
        <div className="table-header" style={card.headerStyle}>
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <Icon className="style" title="调整样式" onClick={this.changeTitleStyle} type="font-colors" />
            </div>
          } trigger="hover">
            <span className="table-title">{card.wrap.title || ''}</span>
          </Popover>
          <SearchComponent config={card} updatesearch={this.updateComponent}/>
        </div>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加卡片" onClick={this.addCard} type="plus" />
            <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" />
            {menu ? <WrapComponent config={card} sysRoles={menu.sysRoles} MenuType={menu.MenuType} updateConfig={this.updateComponent} /> : null}
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.subcards.map(subcard => (<CardComponent key={subcard.uuid} MenuType={menu ? menu.MenuType : ''} cards={card} card={subcard} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    menu: state.customMenu
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(TableCardEditComponent)
src/menu/components/table/normal-table/index.scss
New file
@@ -0,0 +1,92 @@
.menu-table-card-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 100px;
  .table-header {
    position: relative;
    height: 45px;
    overflow: hidden;
    padding-right: 35px;
    .table-title {
      text-decoration: inherit;
      font-weight: inherit;
      font-style: inherit;
      float: left;
      line-height: 45px;
      margin-left: 10px;
      position: relative;
      z-index: 1;
    }
  }
  .card-control {
    position: absolute;
    top: 0px;
    left: 0px;
    .anticon-tool {
      right: auto;
      left: 1px;
      padding: 1px;
    }
  }
  .anticon-tool {
    position: absolute;
    z-index: 1;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .card-item {
    overflow-y: hidden;
    position: relative;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
    min-height: 20px;
  }
  .card-item:hover {
    box-shadow: 0px 0px 2px #e8e8e8;
  }
  .model-menu-card-cell-list .card-detail-row > .anticon-plus {
    position: absolute;
    right: -30px;
    font-size: 16px;
  }
  .model-menu-action-list {
    line-height: 40px;
    .ant-row > .anticon-plus {
      position: absolute;
      right: -30px;
      font-size: 16px;
    }
  }
  .card-add-button {
    text-align: right;
    clear: left;
    .anticon-plus {
      font-size: 20px;
      color: #26C281;
      padding: 5px;
      margin-right: 10px;
    }
  }
}
.menu-table-card-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-table-card-edit-box:hover {
  box-shadow: 0px 0px 2px #e8e8e8;
}
src/menu/components/tabs/tabsetting/settingform/index.jsx
@@ -14,10 +14,6 @@
  state = {}
  UNSAFE_componentWillMount () {
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
src/router/index.js
@@ -7,6 +7,7 @@
import asyncLoadComponent from '@/utils/asyncLoadComponent'
const Pay = asyncLoadComponent(() => import('@/views/pay'))
const Sso = asyncLoadComponent(() => import('@/views/sso'))
const Main = asyncLoadComponent(() => import('@/views/main'))
const Login = asyncLoadComponent(() => import('@/views/login'))
const NotFound = asyncComponent(() => import('@/views/404'))
@@ -20,7 +21,7 @@
  {path: '/login', name: 'login', component: Login, auth: false},
  {path: '/pay/:param', name: 'pay', component: Pay, auth: false},
  {path: '/print/:param', name: 'print', component: PrintT, auth: false},
  {path: '/ssologin/:param', name: 'ssologin', auth: true},
  {path: '/ssologin/:param', name: 'ssologin', component: Sso, auth: false},
  {path: '/main', name: 'main', component: Main, auth: true},
  {path: '/mobmanage', name: 'mobmanage', component: MobManage, auth: true},
  {path: '/mobdesign/:appId/:appType/:appCode/:appName', name: 'mobdesign', component: MobDesign, auth: true},
@@ -31,7 +32,7 @@
export default class RouteConfig extends Component {
  controlRoute (item, props) {
    if (!item.auth) {            // 不需要授权,直接跳转(登录页)
    if (!item.auth) {            // 不需要授权,直接跳转
      return (<item.component {...props}/>)
    }
@@ -75,25 +76,6 @@
      }
      return (<item.component {...props}/>)
    } else if (item.name === 'ssologin') {
      try {
        let _param = JSON.parse(window.decodeURIComponent(window.atob(props.match.params.param)))
        if (typeof(_param) === 'object') {
          if (_param.UserID) {
            sessionStorage.setItem('UserID', _param.UserID)
          }
          if (_param.LoginUID) {
            sessionStorage.setItem('LoginUID', _param.LoginUID)
          }
          if (_param.User_Name) {
            sessionStorage.setItem('User_Name', _param.User_Name)
          }
        }
      } catch {
        console.warn('菜单参数解析错误!')
      }
      return (<Redirect to={{ pathname: '/main'}}/>)
    }
    
    let userId = sessionStorage.getItem('UserID') // 判断是否存在userid
src/tabviews/custom/components/card/data-card/index.scss
@@ -39,6 +39,7 @@
    flex: 10;
    .card-item-box {
      background-color: #ffffff;
      transition: all 0.3s;
    }
    >.active >.card-item-box {
src/tabviews/custom/components/card/prop-card/index.scss
@@ -14,6 +14,7 @@
  .card-row-list {
    .card-item-box {
      background-color: #ffffff;
      transition: all 0.3s;
    }
    >.active >.card-item-box {
src/tabviews/custom/components/card/table-card/index.scss
@@ -36,6 +36,7 @@
  .card-row-list {
    overflow-y: auto;
    .card-item-box {
      background-color: #ffffff;
      transition: all 0.3s;
    }
    >.active >.card-item-box {
src/tabviews/custom/components/search/main-search/index.jsx
@@ -434,6 +434,7 @@
  getFields() {
    const { getFieldDecorator } = this.props.form
    const { config } = this.props
    const fields = []
    this.state.searchlist.forEach((item, index) => {
@@ -442,7 +443,7 @@
      if (item.type === 'text') { // 文本搜索
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field, {
                initialValue: item.initval,
                rules: [
@@ -458,7 +459,7 @@
      } else if (item.type === 'select') { // 下拉搜索
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field, {
                initialValue: item.initval,
                rules: [
@@ -486,7 +487,7 @@
        let _initval = item.initval ? item.initval.split(',').filter(Boolean) : []
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field, {
                initialValue: _initval,
                rules: [
@@ -514,7 +515,7 @@
      } else if (item.type === 'date') { // 时间搜索
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field, {
                initialValue: item.initval ? moment().subtract(item.initval, 'days') : null,
                rules: [
@@ -532,7 +533,7 @@
      } else if (item.type === 'datemonth') {
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field, {
                initialValue: item.initval ? moment().subtract(item.initval, 'month') : null,
                rules: [
@@ -550,7 +551,7 @@
      } else if (item.type === 'dateweek') {
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field, {
                initialValue: item.initval ? moment().subtract(item.initval * 7, 'days') : null,
                rules: [
@@ -579,7 +580,7 @@
        fields.push(
          <Col className="daterange" span={item.ratio || 6} key={index}>
            <Form.Item label={item.label}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''}>
              {getFieldDecorator(item.field,
                {
                  initialValue: _defaultValue,
@@ -603,7 +604,7 @@
      } else if (item.type === 'group') {
        fields.push(
          <Col span={item.ratio || 6} key={index}>
            <Form.Item label={item.label} className={item.required === 'true' ? 'group-required' : ''}>
            <Form.Item label={item.labelShow !== 'false' ? item.label : ''} className={item.required === 'true' ? 'group-required' : ''}>
              <DateGroup ref={item.uuid} position={index} card={item} onGroupChange={this.searchChange} />
            </Form.Item>
          </Col>
@@ -611,18 +612,20 @@
      }
    })
    fields.push(
      <Col span={6} style={{ whiteSpace: 'nowrap' }} key="actions">
        <Form.Item label={' '} colon={false} style={{ minHeight: '40px' }}>
          <Button type="primary" htmlType="submit">
            {this.state.dict['main.search']}
          </Button>
          <Button style={{ marginLeft: 8 }} onClick={this.handleReset}>
            {this.state.dict['main.reset']}
          </Button>
        </Form.Item>
      </Col>
    )
    if (config.wrap.float !== 'right' && config.wrap.show !== 'false') {
      fields.push(
        <Col span={6} style={{ whiteSpace: 'nowrap' }} key="actions">
          <Form.Item label={' '} colon={false} style={{ minHeight: '40px' }}>
            <Button type="primary" htmlType="submit">
              {this.state.dict['main.search']}
            </Button>
            <Button style={{ marginLeft: 8 }} onClick={this.handleReset}>
              {this.state.dict['main.reset']}
            </Button>
          </Form.Item>
        </Col>
      )
    }
    
    return fields
  }
@@ -770,6 +773,7 @@
  }
  render() {
    const { config } = this.props
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -782,7 +786,7 @@
    }
    return (
      <Form {...formItemLayout} className="custom-main-search" id={this.state.formId} onSubmit={this.handleSearch}>
      <Form {...formItemLayout} className={`custom-main-search ${config.wrap.float} ${config.wrap.show}`} style={config.style} id={this.state.formId} onSubmit={this.handleSearch}>
        <Row gutter={24}>{this.getFields()}</Row>
      </Form>
    )
src/tabviews/custom/components/search/main-search/index.scss
@@ -1,4 +1,5 @@
.custom-main-search {
  background: #ffffff;
  .ant-form-item {
    display: flex;
    margin-bottom: 0px;
@@ -35,4 +36,11 @@
      content: '*';
    }
  }
}
.custom-main-search.right {
  >.ant-row {
    >.ant-col {
      float: right;
    }
  }
}
src/templates/sharecomponent/searchcomponent/searchform/index.jsx
@@ -106,14 +106,14 @@
    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
    let _items = formlist.filter(cell => cell.key === 'items')[0].initVal
    let resourceType = formlist.filter(cell => cell.key === 'resourceType')[0].initVal
    let _options = ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide']                // 默认显示项
    let _options = ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow']                // 默认显示项
    if ((type === 'multiselect' || type === 'select' || type === 'link') && resourceType === '0') {        // 下拉选择类型、选项为自定义资源
      _options = [..._options, 'resourceType', 'options', 'display']
    } else if ((type === 'multiselect' || type === 'select' || type === 'link') && resourceType === '1') { // 下拉选择类型、选项为后台数据源中获取
      _options = [..._options, 'resourceType', 'dataSource', 'valueField', 'valueText', 'orderBy', 'orderType', 'display', 'database']
    } else if (type === 'group') {
      _options = ['label', 'type', 'field', 'datefield', 'initval', 'blacklist', 'ratio', 'items', 'required', 'transfer']
      _options = ['label', 'type', 'field', 'datefield', 'initval', 'blacklist', 'ratio', 'items', 'required', 'transfer', 'labelShow']
    }
    if (type === 'select' || type === 'link') {
@@ -182,14 +182,14 @@
    const { resourceType, items } = this.state
    if (key === 'type') {
      let _options = ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide']
      let _options = ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow']
      if ((value === 'multiselect' || value === 'select' || value === 'link') && resourceType === '0') {        // 下拉选择类型、选项为自定义资源
        _options = [..._options, 'resourceType', 'options', 'display']
      } else if ((value === 'multiselect' || value === 'select' || value === 'link') && resourceType === '1') { // 下拉选择类型、选项为后台数据源中获取
        _options = [..._options, 'resourceType', 'dataSource', 'valueField', 'valueText', 'orderBy', 'orderType', 'display', 'database']
      } else if (value === 'group') {
        _options = ['label', 'type', 'field', 'datefield', 'initval', 'items', 'ratio', 'blacklist', 'required', 'transfer']
        _options = ['label', 'type', 'field', 'datefield', 'initval', 'items', 'ratio', 'blacklist', 'required', 'transfer', 'labelShow']
      }
      if (value === 'select' || value === 'link') {
@@ -263,7 +263,7 @@
    let value = e.target.value
    if (key === 'resourceType') {
      let _options = ['label', 'field', 'initval', 'type', 'match', 'resourceType', 'display', 'ratio', 'blacklist', 'required', 'Hide']
      let _options = ['label', 'field', 'initval', 'type', 'match', 'resourceType', 'display', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow']
      if (value === '0') {
        _options = [..._options, 'options']
src/templates/sharecomponent/settingcomponent/settingform/index.jsx
@@ -100,6 +100,8 @@
    let newsearches = []
    searches.forEach(search => {
      if (!search.field) return
      let item = {
        key: search.field,
        match: search.match,
src/templates/zshare/customscript/index.jsx
@@ -83,6 +83,7 @@
    let _usefulFields = []
    searches.forEach(item => {
      if (!item.field) return
      if (item.type === 'group') {
        if (item.transfer === 'true') {
          _usefulFields.push(item.field)
src/templates/zshare/formconfig.jsx
@@ -496,20 +496,20 @@
        text: '>='
      }]
    },
    {
      type: 'select',
      key: 'display',
      label: Formdict['header.form.display'],
      initVal: card.display || 'dropdown',
      required: true,
      options: [{
        value: 'dropdown',
        text: Formdict['header.form.dropdown']
      // }, {
      //   value: 'button',
      //   text: Formdict['header.form.button']
      }]
    },
    // {
    //   type: 'select',
    //   key: 'display',
    //   label: Formdict['header.form.display'],
    //   initVal: card.display || 'dropdown',
    //   required: true,
    //   options: [{
    //     value: 'dropdown',
    //     text: Formdict['header.form.dropdown']
    //   // }, {
    //   //   value: 'button',
    //   //   text: Formdict['header.form.button']
    //   }]
    // },
    {
      type: 'radio',
      key: 'database',
@@ -574,6 +574,19 @@
      }]
    },
    {
      type: 'radio',
      key: 'labelShow',
      label: '显示名称',
      initVal: card.labelShow || 'true',
      options: [{
        value: 'true',
        text: Formdict['model.true']
      }, {
        value: 'false',
        text: Formdict['model.false']
      }]
    },
    {
      type: 'multiselect',
      key: 'blacklist',
      label: Formdict['header.form.blacklist'],
src/views/menudesign/index.jsx
@@ -373,7 +373,6 @@
            })
          } else {
            this.setState({
              openEdition: response.open_edition || '',
              menuloading: false
            })
            notification.warning({
src/views/sso/index.jsx
New file
@@ -0,0 +1,152 @@
import React, {Component} from 'react'
import { Spin } from 'antd'
import { connect } from 'react-redux'
import md5 from 'md5'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import options from '@/store/options.js'
import { modifyMemberLevel } from '@/store/action'
import './index.scss'
class SSOLogin extends Component {
  UNSAFE_componentWillMount() {
    try {
      let _param = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
      if (typeof(_param) === 'object') {
        _param.UserID && sessionStorage.setItem('UserID', _param.UserID)
        _param.LoginUID && sessionStorage.setItem('LoginUID', _param.LoginUID)
        _param.User_Name && sessionStorage.setItem('User_Name', _param.User_Name)
        _param.Full_Name && sessionStorage.setItem('Full_Name', _param.Full_Name)
        _param.debug && sessionStorage.setItem('debug', _param.debug)
        _param.dataM && sessionStorage.setItem('dataM', _param.dataM)
        _param.avatar && sessionStorage.setItem('avatar', _param.avatar)
        _param.role_id && sessionStorage.setItem('role_id', _param.role_id)
      } else {
        this.props.history.replace('/login')
      }
      this.getMessage()
    } catch {
      this.props.history.replace('/login')
    }
  }
  getMessage = () => {
    let _param = {
      func: 's_Get_style',
      TypeCharOne: 'PC',
      LText: `select '${window.GLOB.appkey}'`,
    }
    _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
    Api.getSystemConfig(_param).then(res => {
      if (res.status) {
        let _url = window.location.href.split('#')[0] + 'system'
        let systemMsg = {
          favicon: res.titlelogo || '',
          platTitle: res.titleName || '',
          platName: res.SysName || '',
          bgImage: res.Banner || '',
          loginlogo: res.loginlogo || '',
          copyRight: res.CopyRightYear && res.CopyRightOrg ? `Copyright©${res.CopyRightYear}    所有相关版权归    ${res.CopyRightOrg}` : '',
          ICP: res.ICP || '',
          mainlogo: res.indexlogo || '',
          doclogo: res.doclogo || '',
          style: res.CSS || '',
          webSite: res.WebSite || ''
        }
        // url标题
        document.title = systemMsg.platTitle
        try {
          localStorage.setItem(_url, window.btoa(window.encodeURIComponent(JSON.stringify(systemMsg))))
        } catch {
          localStorage.removeItem(_url)
        }
        let _loginurl = window.location.href.split('#')[0] + 'loginways'
        let login_ways = []
        let login_types = []
        if (res.login_ways && res.login_ways.length > 0) {
          res.login_ways.forEach(item => {
            // 短信验证码登录,必须设置短信Id
            if (item.way_no === 'sms_vcode' && !item.sms_id) return
            if (login_types.includes(item.way_no)) return
            login_types.push(item.way_no)
            login_ways.push({
              label: item.way_name,
              type: item.way_no,
              smsId: item.sms_id
            })
          })
        } else {
          login_ways.push({
            label: '账号密码登录',
            type: 'uname_pwd',
            smsId: ''
          })
        }
        try {
          localStorage.setItem(_loginurl, window.btoa(window.encodeURIComponent(JSON.stringify(login_ways))))
        } catch {
          localStorage.removeItem(_loginurl)
        }
        window.GLOB.mainlogo = systemMsg.mainlogo
        window.GLOB.style = systemMsg.style
        if (window.GLOB.style && options.styles[window.GLOB.style]) {
          document.getElementById('root').className = options.styles[window.GLOB.style]
        }
        if (res.titlelogo && window.GLOB.favicon !== res.titlelogo) {
          let link = document.querySelector("link[rel*='icon']") || document.createElement('link')
          link.type = 'image/x-icon'
          link.rel = 'shortcut icon'
          link.href = res.titlelogo
          document.getElementsByTagName('head')[0].appendChild(link)
        }
        let memberLevel = res.member_level
        if (typeof(memberLevel) === 'number' && memberLevel > 10 && parseInt(memberLevel / 10) * 10 === memberLevel) {
          sessionStorage.setItem('Member_Level', md5('mksoft' + moment().format('YYYYMM') + memberLevel))
          this.props.modifyMemberLevel(memberLevel)
        }
        this.props.history.replace('/main')
      } else {
        this.props.history.replace('/login')
      }
    }, () => {
      this.props.history.replace('/login')
    })
  }
  render () {
    return (
      <div className="sso-login">
        <Spin size="large" />
      </div>
    )
  }
}
const mapStateToProps = () => {
  return {}
}
const mapDispatchToProps = (dispatch) => {
  return {
    modifyMemberLevel: (memberLevel) => dispatch(modifyMemberLevel(memberLevel))
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(SSOLogin)
src/views/sso/index.scss
New file
@@ -0,0 +1,7 @@
.sso-login {
  .ant-spin {
    position: absolute;
    left: calc(50vw - 22px);
    top: 45vh;
  }
}