king
2021-04-19 4751cbd63d9f24ab2aaa5c5930a7aca0263471c0
2021-04-19
5个文件已修改
12个文件已添加
2个文件已删除
1366 ■■■■ 已修改文件
src/menu/components/tabs/antv-tabs/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/index.jsx 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/index.scss 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/index.jsx 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.scss 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/home/index.jsx 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/home/index.scss 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/card.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/card.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/option.js 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.scss
@@ -37,7 +37,6 @@
  }
  
  .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
    padding: 0px;
    text-align: right;
    > span {
      display: inline-block;
@@ -45,7 +44,6 @@
    }
  }
  .ant-tabs .ant-tabs-right-bar .ant-tabs-tab {
    padding: 0px;
    text-align: left;
    > span {
      display: inline-block;
@@ -53,7 +51,7 @@
    }
  }
  .ant-tabs-tab {
    padding: 0px;
    padding: 0px!important;
    text-align: center;
    > span {
      display: inline-block;
@@ -78,7 +76,6 @@
    
    .ant-tabs-card-bar {
      .ant-tabs-tab {
        padding: 0px;
        > span {
          display: inline-block;
          padding: 0px 16px;
src/mob/components/navbar/normal-navbar/index.jsx
New file
@@ -0,0 +1,181 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover } from 'antd'
import asyncIconComponent from '@/utils/asyncIconComponent'
import MKEmitter from '@/utils/events.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import './index.scss'
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const MenuComponent = asyncIconComponent(() => import('./menusetting'))
class NormalNavbar extends Component {
  static propTpyes = {
    card: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        dataName: '',
        width: card.width || 24,
        name: card.name,
        subtype: card.subtype,
        wrap: { name: card.name, width: card.width || 1200 },
        style: { },
        menus: [],
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _card.wrap.name = card.name
        _card.style = config.style
      }
      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))
  }
  /**
   * @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)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds[0] !== card.uuid) return
    let _card = {...card}
    if (comIds.length === 1) {
      _card = {...card, style}
    }
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'shadow'], card.style)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card)
    }
  }
  changeMenu = (menu) => {
    if (menu.property === 'link') {
      window.open(menu.link)
      return
    }
    MKEmitter.emit('changeEditMenu', {
      fixed: menu.property === 'menu',
      MenuID: menu.property === 'linkmenu' ? menu.linkMenuId : menu.MenuID,
      copyMenuId: menu.property === 'menu' ? menu.copyMenuId : '',
      MenuNo: menu.MenuNo,
      MenuName: menu.name,
    })
  }
  render() {
    const { card } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    return (
      <div className="normal-navbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <MenuComponent config={card} updateConfig={this.updateComponent} />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <div className="menu">
          {card.menus.map(menu => {
            return (
              <div class="am-tab-bar-tab">
                <div class="am-tab-bar-tab-icon">
                  <span class="am-badge am-tab-bar-tab-badge tab-badge">
                    <Icon type="font-colors" />
                    <sup class="am-badge-text">1</sup>
                  </span>
                </div>
                <p class="am-tab-bar-tab-title">{menu.name}</p>
              </div>
            )
          })}
        </div>
      </div>
    )
  }
}
export default NormalNavbar
src/mob/components/navbar/normal-navbar/index.scss
New file
@@ -0,0 +1,133 @@
.normal-navbar-edit-box {
  position: fixed;
  bottom: 0px;
  left: 0px;
  width: 100%;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 50px;
  z-index: 3;
  .menu {
    display: block;
    font-size: inherit;
    color: inherit;
    .ant-menu {
      background: transparent;
      line-height: inherit;
      font-size: inherit;
      color: inherit;
      border: 0;
      .ant-menu-item:hover, .ant-menu-item-active {
        color: unset;
      }
      .ant-menu-item span {
        display: inline-block;
      }
    }
    .ant-menu-horizontal > .ant-menu-item:hover, .ant-menu-horizontal > .ant-menu-item-active, .ant-menu-horizontal > .ant-menu-item-open, .ant-menu-horizontal > .ant-menu-item-selected {
      color: unset;
    }
  }
  .card-control {
    position: absolute;
    top: 0px;
    left: 0px;
    .anticon-tool {
      right: auto;
      left: 1px;
      padding: 1px;
    }
  }
  .anticon-tool {
    position: absolute;
    z-index: 2;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .card-item {
    overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
    min-height: 20px;
  }
  .card-item:hover {
    box-shadow: 0px 0px 2px #1890ff;
  }
  .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;
    }
  }
  .ant-pagination {
    float: right;
    margin: 10px;
  }
  .model-menu-action-list {
    .page-card {
      line-height: 55px;
    }
  }
}
.normal-navbar-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.normal-navbar-edit-box:hover {
  box-shadow: 0px 0px 4px #1890ff;
}
.top-menu-popover {
  padding-top: 0!important;
}
.normal-navbar-submenu {
  .ant-menu-item-group {
    float: left;
  }
  .ant-menu-item {
    height: 32px;
    line-height: 32px;
    span {
      display: inline-block;
      width: 100%;
      height: 100%;
      padding: 0 16px 0 28px;
    }
    padding: 0;
  }
  .ant-menu .ant-menu-item-selected {
    background-color: #ffffff;
  }
}
src/mob/components/navbar/normal-navbar/menusetting/index.jsx
New file
@@ -0,0 +1,62 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import MenuTable from './menutable'
import './index.scss'
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  verifySubmit = () => {
    const { config } = this.props
    let menus = this.mTable.state.data || []
    this.props.updateConfig({...config, menus})
    this.setState({visible: false})
  }
  render () {
    const { config } = this.props
    const { visible, dict } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="menu" title="菜单" onClick={() => this.setState({ visible: true })}/>
        <Modal
          wrapClassName="popview-modal"
          title="菜单编辑"
          visible={visible}
          width={950}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <MenuTable
            menus={config.menus}
            ref={(ref) => { this.mTable = ref }}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/mob/components/navbar/normal-navbar/menusetting/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-menu {
    color: purple;
  }
  >.anticon-edit {
    color: #1890ff;
  }
}
src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx
New file
@@ -0,0 +1,205 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, Select } from 'antd'
import './index.scss'
const { TextArea } = Input
class SettingForm extends Component {
  static propTpyes = {
    menu: PropTypes.object,    // 卡片行信息
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    property: this.props.menu.property || 'menu',
    appMenus: [],
  }
  UNSAFE_componentWillMount () {
    let appMenus = sessionStorage.getItem('appMenus')
    if (appMenus) {
      try {
        appMenus = JSON.parse(appMenus)
      } catch {
        appMenus = []
      }
    } else {
      appMenus = []
    }
    this.setState({appMenus})
  }
  componentDidMount() {
    const { menu } = this.props
    if (!menu.MenuID) {
      let _form = document.getElementById('name')
      _form && _form.select()
    }
  }
  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()
    }
  }
  changeProperty = (e) => {
    let val = e.target.value
    this.setState({property: val})
  }
  render() {
    const { menu } = this.props
    const { getFieldDecorator } = this.props.form
    const { property, appMenus } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout}>
        <Row gutter={24}>
          <Col span={22}>
            <Form.Item label="菜单名称">
              {getFieldDecorator('name', {
                initialValue: menu.name,
                rules: [
                  {
                    required: true,
                    message: '请输入菜单名称!'
                  }
                ]
              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
          <Col span={22}>
            <Form.Item label="菜单参数">
              {getFieldDecorator('MenuNo', {
                initialValue: menu.MenuNo || '',
                rules: [
                  {
                    required: true,
                    message: '请输入菜单参数!'
                  }
                ]
              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
          <Col span={22}>
            <Form.Item label="图标">
              {getFieldDecorator('icon', {
                initialValue: menu.icon || ''
              })(
                <Select>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={22}>
            <Form.Item label="菜单属性">
              {getFieldDecorator('property', {
                initialValue: menu.property || 'menu'
              })(
                <Radio.Group onChange={this.changeProperty}>
                  <Radio value="menu">菜单</Radio>
                  <Radio value="link">链接</Radio>
                  <Radio value="linkmenu">关联菜单</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          <Col span={22}>
            <Form.Item label="隐藏">
              {getFieldDecorator('hidden', {
                initialValue: menu.hidden || 'false'
              })(
                <Radio.Group>
                  <Radio value="false">否</Radio>
                  <Radio value="true">是</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {property === 'link' ? <Col span={22}>
            <Form.Item label="链接地址">
              {getFieldDecorator('link', {
                initialValue: menu.link || '',
                rules: [{
                  required: true,
                  message: '请输入链接地址!'
                }]
              })(<TextArea rows={2} />)}
            </Form.Item>
          </Col> : null}
          {property === 'linkmenu' ? <Col span={22}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="关联当前app中已有的菜单。">
                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
                关联菜单
              </Tooltip>
            }>
              {getFieldDecorator('linkMenuId', {
                initialValue: menu.linkMenuId || '',
                rules: [{
                  required: true,
                  message: '请选择关联菜单!'
                }]
              })(
                <Select>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                </Select>
              )}
            </Form.Item>
          </Col> : null}
          {property === 'menu' ? <Col span={22}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="复制菜单仅在当前菜单不存在时有效。">
                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
                复制菜单
              </Tooltip>
            }>
              {getFieldDecorator('copyMenuId', {
                initialValue: menu.copyMenuId || ''
              })(
                <Select>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                </Select>
              )}
            </Form.Item>
          </Col> : null}
        </Row>
      </Form>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx
New file
@@ -0,0 +1,185 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Table, Button, Modal, Icon } from 'antd'
import MenuForm from '../menuform'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { confirm } = Modal
class MenuTable extends Component {
  static propTpyes = {
    menus: PropTypes.object,    // 卡片行信息
  }
  state = {
    data: [],
    editMenu: null,
    columns: [
      { title: '菜单名称', dataIndex: 'name', key: 'name' },
      { title: '菜单参数', dataIndex: 'MenuNo', key: 'MenuNo' },
      { title: '菜单属性', dataIndex: 'property', key: 'property',  render: text => {
        const trans = {menu: '菜单', link: '链接', linkmenu: '关联菜单'}
        return trans[text]
      }},
      { title: '是否隐藏', dataIndex: 'hidden', key: 'hidden',  render: (text, record) => {
        const trans = {'true': '是', 'false': '否'}
        return trans[text] || '否'
      }},
      { title: '操作', key: 'operation', align: 'center', width: '190px', render: (text, record) =>
        (<div>
          <Button type="link" style={{padding: '0 5px', marginRight: '5px'}} onClick={() => this.editMenu(record)}>编辑</Button>
          <Button type="link" style={{color: '#ff4d4f', padding: '0 5px', marginRight: '5px'}} onClick={() => this.delMenu(record)}>删除</Button>
          <Icon type="arrow-up" style={{color: '#26C281', cursor: 'pointer', padding: '0 5px', marginRight: '5px'}} onClick={() => this.moveUp(record)}/>
          <Icon type="arrow-down" style={{color: '#ff4d4f', cursor: 'pointer', padding: '0 5px'}} onClick={() => this.moveDown(record)}/>
        </div>)
      }
    ]
  }
  UNSAFE_componentWillMount () {
    const { menus } = this.props
    this.setState({data: fromJS(menus).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  moveUp = (record) => {
    let data = fromJS(this.state.data).toJS()
    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
    let hoverIndex = dragIndex - 1
    if (hoverIndex === -1) return
    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
    this.setState({data})
  }
  moveDown = (record) => {
    let data = fromJS(this.state.data).toJS()
    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
    let hoverIndex = dragIndex + 1
    if (hoverIndex === data.length) return
    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
    this.setState({data})
  }
  delMenu = (record) => {
    const { data } = this.state
    const _this = this
    confirm({
      title: (record.property === 'classify' && record.sublist.length > 0 ? '菜单下含有子菜单,' : '') + '确定删除吗?',
      content: '',
      onOk() {
        _this.setState({data: data.filter(item => item.MenuID !== record.MenuID)})
        let uuids = [record.MenuID]
        record.sublist && record.sublist.forEach(item => {
          uuids.push(item.MenuID)
          item.sublist && item.sublist.forEach(cell => {
            uuids.push(cell.MenuID)
          })
        })
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
  }
  editMenu = (record) => {
    this.setState({editMenu: record, visible: true})
  }
  plusMenu = () => {
    let _menu = {
      name: '菜单',
      property: 'menu'
    }
    this.setState({editMenu: _menu, visible: true})
  }
  menuSubmit = () => {
    const { editMenu, data } = this.state
    this.menuRef.handleConfirm().then(res => {
      let _menu = {...editMenu, ...res}
      if (!_menu.MenuID) {
        _menu.MenuID = Utils.getuuid()
        this.setState({data: [...data, _menu], editMenu: null, visible: false})
      } else {
        this.setState({
          editMenu: null,
          visible: false,
          data: data.map(item => {
            if (item.MenuID === _menu.MenuID) {
              return _menu
            } else {
              return item
            }
          })
        })
      }
    })
  }
  menuUpdate = (res) => {
    const { data } = this.state
    this.setState({
      data: data.map(item => {
        if (item.MenuID === res.MenuID) {
          return res
        } else {
          return item
        }
      })
    })
  }
  render() {
    const { columns, data, visible, editMenu } = this.state
    return (
      <div className="menu-control-wrap">
        <Button className="menu-plus mk-green" onClick={this.plusMenu}>添加</Button>
        <Table
          rowKey="MenuID"
          columns={columns}
          dataSource={data}
          pagination={false}
        />
        <Modal
          title="编辑"
          visible={visible}
          width={600}
          maskClosable={false}
          onOk={this.menuSubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <MenuForm
            menu={editMenu}
            inputSubmit={this.menuSubmit}
            wrappedComponentRef={(inst) => this.menuRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default MenuTable
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.scss
New file
@@ -0,0 +1,23 @@
.menu-control-wrap {
  position: relative;
  .menu-plus {
    float: right;
    position: relative;
    z-index: 1;
    margin-bottom: 5px;
  }
  .ant-empty {
    margin: 5px 0;
  }
  thead tr {
    background: #fbfbfb;
  }
  tbody > tr {
    background: #ffffff;
  }
  .ant-table-body {
    margin: 0!important;
  }
}
src/mob/components/navbar/normal-navbar/wrapsetting/index.jsx
New file
@@ -0,0 +1,83 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import './index.scss'
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    wrap: null
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    this.setState({wrap: fromJS(config.wrap).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  editDataSource = () => {
    this.setState({
      visible: true
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    this.verifyRef.handleConfirm().then(res => {
      this.setState({
        wrap: res,
        visible: false
      })
      this.props.updateConfig({...config, wrap: res})
    })
  }
  render () {
    const { config } = this.props
    const { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" title="编辑" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title={config.type === 'table' ? '表格设置' : '卡片设置'}
          visible={visible}
          width={800}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            config={config}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/mob/components/navbar/normal-navbar/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/mob/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,102 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, InputNumber } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    config: PropTypes.object,    // 卡片行信息
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {}
  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 { wrap } = 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="导航栏名称">
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '导航栏名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="菜单参数">
                {getFieldDecorator('MenuNo', {
                  initialValue: wrap.MenuNo,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '菜单参数!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="高度">
                {getFieldDecorator('height', {
                  initialValue: wrap.height || 50,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '高度!'
                    }
                  ]
                })(<InputNumber min={50} max={200} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/components/navbar/normal-navbar/wrapsetting/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/mob/home/index.jsx
File was deleted
src/mob/home/index.scss
File was deleted
src/mob/mobshell/card.jsx
@@ -18,6 +18,7 @@
const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
const NormalLogin = asyncComponent(() => import('@/pc/components/login/normal-login'))
const NormalNavbar = asyncComponent(() => import('@/mob/components/navbar/normal-navbar'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -52,6 +53,25 @@
    style = { opacity: 0.3}
  }
  let col = 'ant-col-' + (card.width || 24)
  if (card.type === 'navbar') {
    col = ''
  } else if (card.type === 'login') {
    let height = ''
    if (card.wrap && card.wrap.height) {
      // scaleview
      height = card.wrap.height.replace(/\d+vw/ig, (word) => {
        return parseFloat(word) * 420 / 100 + 'px'
        // return parseFloat(word) * 350 / 100 + 'px'
      }).replace(/\d+vh/ig, (word) => {
        return parseFloat(word) * 738 / 100 + 'px'
        // return parseFloat(word) * 615 / 100 + 'px'
      })
    }
    style.minHeight = height
  }
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
@@ -81,27 +101,13 @@
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'login') {
      return (<NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'navbar') {
      return (<NormalNavbar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
  if (card.type === 'login') {
    let height = ''
    if (card.wrap && card.wrap.height) {
      // scaleview
      height = card.wrap.height.replace(/\d+vw/ig, (word) => {
        return parseFloat(word) * 420 / 100 + 'px'
        // return parseFloat(word) * 350 / 100 + 'px'
      }).replace(/\d+vh/ig, (word) => {
        return parseFloat(word) * 738 / 100 + 'px'
        // return parseFloat(word) * 615 / 100 + 'px'
      })
    }
    style.minHeight = height
  }
  return (
    <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}>
    <div className={'ant-col mk-component-card ' + col} ref={node => drag(drop(node))} style={style}>
      {getCardComponent()}
    </div>
  )
src/pc/menushell/card.jsx
@@ -58,6 +58,20 @@
  let col = ' ant-col ant-col-' + (card.width || 24)
  if (card.type === 'navbar') {
    col = ''
  } else if (card.type === 'login') {
    let height = ''
    if (card.wrap && card.wrap.height) {
      // scaleview
      height = card.wrap.height.replace(/\d+vw/ig, (word) => {
        return parseFloat(word) * 420 / 100 + 'px'
        // return parseFloat(word) * 350 / 100 + 'px'
      }).replace(/\d+vh/ig, (word) => {
        return parseFloat(word) * 738 / 100 + 'px'
        // return parseFloat(word) * 615 / 100 + 'px'
      })
    }
    style.minHeight = height
  }
  const getCardComponent = () => {
@@ -96,10 +110,7 @@
    } else if (card.type === 'code') {
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'login') {
      return (<>
        <NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/>
        <div style={{float: 'right', height: card.wrap ? card.wrap.height : ''}}></div>
      </>)
      return (<NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
  return (
src/templates/modalconfig/dragelement/index.jsx
@@ -33,6 +33,7 @@
  const editCard = id => {
    const { card } = findCard(id)
    delete card.focus
    handleForm(card)
  }
src/utils/option.js
@@ -681,19 +681,8 @@
    'step-forward',
    'fast-backward',
    'fast-forward',
    'shrink',
    'arrows-alt',
    'up-circle',
    'down-circle',
    'left-circle',
    'right-circle',
    'double-right',
    'double-left',
    'vertical-left',
    'vertical-right',
    'vertical-align-top',
    'vertical-align-middle',
    'vertical-align-bottom',
    'forward',
    'backward',
    'rollback',
@@ -703,29 +692,8 @@
    'swap-left',
    'swap-right',
    'play-circle',
    'up-square',
    'down-square',
    'left-square',
    'right-square',
    'login',
    'logout',
    'menu-fold',
    'menu-unfold',
    'border-bottom',
    'border-horizontal',
    'border-inner',
    'border-outer',
    'border-left',
    'border-right',
    'border-top',
    'border-verticle',
    'pic-center',
    'pic-left',
    'pic-right',
    'radius-bottomleft',
    'radius-bottomright',
    'radius-upleft',
    'radius-upright',
    'fullscreen',
    'fullscreen-exit'
  ],
@@ -734,25 +702,18 @@
    'question-circle',
    'plus',
    'plus-circle',
    'pause',
    'pause-circle',
    'minus',
    'minus-circle',
    'plus-square',
    'minus-square',
    'info',
    'info-circle',
    'exclamation',
    'exclamation-circle',
    'close',
    'close-circle',
    'close-square',
    'check',
    'check-circle',
    'check-square',
    'clock-circle',
    'warning',
    'issues-close',
    'stop'
  ],
  edit: [
@@ -762,33 +723,14 @@
    'scissor',
    'delete',
    'snippets',
    'diff',
    'highlight',
    'align-center',
    'align-left',
    'align-right',
    'bg-colors',
    'bold',
    'italic',
    'underline',
    'strikethrough',
    'redo',
    'undo',
    'zoom-in',
    'zoom-out',
    'font-colors',
    'font-size',
    'line-height',
    'dash',
    'small-dash',
    'sort-ascending',
    'sort-descending',
    'drag',
    'ordered-list',
    'unordered-list',
    'radius-setting',
    'column-width',
    'column-height'
    'sort-descending'
  ],
  data: [
    'area-chart',
@@ -797,64 +739,23 @@
    'dot-chart',
    'line-chart',
    'radar-chart',
    'heat-map',
    'fall',
    'rise',
    'stock',
    'box-plot',
    'fund',
    'sliders'
  ],
  trademark: [
    'android',
    'apple',
    'windows',
    'ie',
    'chrome',
    'github',
    'aliwangwang',
    'dingding',
    'weibo-square',
    'weibo-circle',
    'taobao-circle',
    'html5',
    'weibo',
    'twitter',
    'wechat',
    'youtube',
    'alipay-circle',
    'taobao',
    'skype',
    'qq',
    'medium-workmark',
    'gitlab',
    'medium',
    'linkedin',
    'google-plus',
    'dropbox',
    'facebook',
    'codepen',
    'code-sandbox',
    'amazon',
    'google',
    'codepen-circle',
    'alipay',
    'ant-design',
    'ant-cloud',
    'aliyun',
    'zhihu',
    'slack',
    'slack-square',
    'behance',
    'behance-square',
    'dribbble',
    'dribbble-square',
    'instagram',
    'yuque',
    'alibaba',
    'yahoo',
    'reddit',
    'sketch'
  ],
  normal: [
    'account-book',
@@ -864,42 +765,19 @@
    'audio',
    'bank',
    'bell',
    'book',
    'bug',
    'bulb',
    'calculator',
    'build',
    'calendar',
    'camera',
    'car',
    'carry-out',
    'cloud',
    'code',
    'compass',
    'contacts',
    'container',
    'control',
    'credit-card',
    'crown',
    'customer-service',
    'dashboard',
    'database',
    'dislike',
    'environment',
    'experiment',
    'eye-invisible',
    'eye',
    'file-add',
    'file-excel',
    'file-exclamation',
    'file-image',
    'file-markdown',
    'file-pdf',
    'file-ppt',
    'file-text',
    'file-unknown',
    'file-word',
    'file-zip',
    'file',
    'filter',
    'fire',
@@ -907,22 +785,13 @@
    'folder-add',
    'folder',
    'folder-open',
    'frown',
    'funnel-plot',
    'gift',
    'hdd',
    'heart',
    'home',
    'hourglass',
    'idcard',
    'insurance',
    'interaction',
    'layout',
    'like',
    'lock',
    'mail',
    'medicine-box',
    'meh',
    'message',
    'mobile',
    'phone',
@@ -931,7 +800,6 @@
    'smile',
    'star',
    'thunderbolt',
    'trophy',
    'unlock',
    'barcode',
    'key',