king
2021-06-29 3f85ff9067b806df7848ce7b852ff357a7bac7f5
2021-06-29
11个文件已修改
6个文件已添加
1202 ■■■■■ 已修改文件
src/mob/components/formdragelement/card.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.scss 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/controller.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/index.jsx 478 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/index.scss 320 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/pastecomponent/index.jsx 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/pastecomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/source.jsx 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/exceloutbutton/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.jsx 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/formdragelement/card.jsx
@@ -60,7 +60,7 @@
  let formItem = null
  if (card.type === 'text' || card.type === 'number') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className={'am-input-control ' + card.cursor}>{card.initval}</div>{card.scan === 'true' ? <div className="am-list-extra"><Icon type="scan" /></div> : null}</div></div>)
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className={'am-input-control ' + card.cursor}>{card.initval}</div>{card.scan && card.scan !== 'false' ? <div className="am-list-extra"><Icon type="scan" /></div> : null}</div></div>)
  } else if (card.type === 'number') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className={'am-input-control ' + card.cursor}>{card.initval}</div></div></div>)
  } else if (card.type === 'select' || card.type === 'link') {
src/mob/components/topbar/normal-navbar/index.jsx
@@ -35,7 +35,7 @@
        floor: card.floor,
        width: card.width || 24,
        subtype: card.subtype,
        wrap: { height: 50, title: 'NavBar', back: 'true', logout: 'false' },
        wrap: { type: 'navbar', height: 50, title: 'NavBar', back: 'true', search: 'false', logout: 'false' },
        style: {borderBottomColor: '#bcbcbc', borderBottomWidth: '1px', paddingLeft: '10px', paddingRight: '10px', lineHeight: '2.8', fontSize: '18px' },
      }
@@ -130,6 +130,10 @@
    }
  }
  setSearch = () => {
  }
  render() {
    const { card } = this.state
@@ -154,9 +158,13 @@
          <div className="am-navbar-left">
            {card.wrap.back !== 'false' ? <Icon type="left" /> : null}
          </div>
          <div className="am-navbar-title">{card.wrap.title || ''}</div>
          {card.wrap.type !== 'search' ?
            <div className="am-navbar-title">{card.wrap.title || ''}</div> :
            <div className="am-navbar-search" onDoubleClick={this.setSearch}><div className="search-bar"><Icon type="search" /></div></div>
          }
          <div className="am-navbar-right">
            {card.wrap.logout === 'true' ? <span>退出</span> : null}
            {card.wrap.search === 'true' ? <Icon type="search" onDoubleClick={this.setSearch}/> : null}
            {card.wrap.logout === 'true' ? <Icon type="ellipsis" /> : null}
          </div>
        </div>
      </div>
src/mob/components/topbar/normal-navbar/index.scss
@@ -28,7 +28,7 @@
    font-style: inherit;
    font-weight: inherit;
    .am-navbar-left {
      flex: 1;
      width: 30px;
      text-align: left;
      color: #1890ff;
    }
@@ -38,10 +38,32 @@
      font-weight: inherit;
      flex: 1;
    }
    .am-navbar-right {
    .am-navbar-search {
      flex: 1;
      padding: 0 5px;
      .search-bar {
        height: 35px;
        line-height: 35px;
        padding: 0 10px;
        color:rgba(0, 0, 0, 0.65);
        border: 1px solid #e8e8e8;
        background: #efefef;
        border-radius: 4px;
        position: relative;
        top: 50%;
        transform: translateY(-50%);
        cursor: pointer;
      }
    }
    .am-navbar-right {
      text-align: right;
      color: #1890ff;
      min-width: 30px;
      .anticon-search {
        margin-right: 5px;
        padding: 5px;
        cursor: pointer;
      }
    }
  }
}
src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx
@@ -59,7 +59,7 @@
          wrapClassName="popview-modal"
          title="导航栏设置"
          visible={visible}
          width={600}
          width={750}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx
@@ -11,7 +11,9 @@
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {}
  state = {
    type: this.props.wrap.type || 'navbar'
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
@@ -37,6 +39,7 @@
  render() {
    const { wrap } = this.props
    const { getFieldDecorator } = this.props.form
    const { type } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -52,14 +55,26 @@
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={22}>
            <Col span={12}>
              <Form.Item label="类型">
                {getFieldDecorator('type', {
                  initialValue: wrap.type || 'navbar'
                })(
                  <Radio.Group onChange={(e) => this.setState({type: e.target.value})}>
                    <Radio value="navbar">导航栏</Radio>
                    <Radio value="search">搜索栏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {type === 'navbar' ? <Col span={12}>
              <Form.Item label="标题">
                {getFieldDecorator('title', {
                  initialValue: wrap.title || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={22}>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="高度">
                {getFieldDecorator('height', {
                  initialValue: wrap.height || 50,
@@ -72,19 +87,31 @@
                })(<InputNumber min={30} max={200} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={24}>
            <Col span={12}>
              <Form.Item label="返回">
                {getFieldDecorator('back', {
                  initialValue: wrap.back || 'true'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">不显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={24}>
            {type === 'navbar' ? <Col span={12}>
              <Form.Item label="搜索">
                {getFieldDecorator('search', {
                  initialValue: wrap.search || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="点击退出时,返回第一个页面。">
                  <Icon type="question-circle" />
@@ -96,7 +123,7 @@
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">不显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
src/mob/modalconfig/controller.jsx
New file
@@ -0,0 +1,64 @@
import React, {Component} from 'react'
import { is, fromJS } from 'immutable'
import MKEmitter from '@/utils/events.js'
import ModalConfig from '@/mob/modalconfig'
class ModalController extends Component {
  state = {
    btn: null,
    config: null,
    visible: false
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  componentDidMount () {
    MKEmitter.addListener('changeModal', this.initConfig)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('changeModal', this.initConfig)
  }
  initConfig = (config, btn) => {
    this.setState({
      visible: true,
      config: fromJS(config).toJS(),
      btn: fromJS(btn).toJS()
    })
  }
  handleBack = () => {
    this.setState({
      visible: false,
      config: null,
      btn: null
    })
  }
  handleSave = (modal) => {
    const { config, btn } = this.state
    MKEmitter.emit('submitModal', config, btn, modal)
  }
  render () {
    const { config, btn, visible } = this.state
    if (!visible) return null
    return (
      <ModalConfig btn={btn} componentConfig={config} handleBack={this.handleBack} handleSave={this.handleSave}/>
    )
  }
}
export default ModalController
src/mob/modalconfig/index.jsx
New file
@@ -0,0 +1,478 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import moment from 'moment'
import { Button, Modal, Collapse, notification, Switch, Icon } from 'antd'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import SourceElement from '@/templates/modalconfig/dragelement/source'
import SettingForm from '@/templates/modalconfig/settingform'
import asyncComponent from '@/utils/asyncComponent'
import { SearchItems } from './source'
import './index.scss'
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const PasteComponent = asyncComponent(() => import('./pastecomponent'))
const DragElement = asyncComponent(() => import('@/mob/components/formdragelement'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
class ComModalConfig extends Component {
  static propTpyes = {
    btn: PropTypes.object,
    handleSave: PropTypes.func,
    handleBack: PropTypes.func
  }
  state = {
    dict: CommonDict,      // 字典
    config: null,          // 页面配置,包括模板类型、模态框设置、添加表名、表单列表
    visible: false,        // 表单编辑模态框,显示控制
    tableVisible: false,   // 数据表字段列表模态框,显示控制
    tableColumns: [],      // 表格字段名列表
    fields: null,          // 表单,可选字段(去重后)
    formlist: null,        // 表单编辑模态框,可编辑字段
    card: null,            // 编辑元素
    closeloading: false,   // 菜单保存中
    settingVisible: false, // 全局配置模态框
    closeVisible: false,   // 关闭模态框
    tables: [],            // 可用表名
    selectedTables: [],    // 已选表名
    originConfig: null,    // 原始菜单
    sources: null,         // 表单类型
    sqlVerifing: false,    // sql验证
    openEdition: '',       // 编辑版本标记,防止多人操作
    showField: false,      // 显示表单字段值
    standardform: null
  }
  /**
   * @description 数据预处理
   */
  UNSAFE_componentWillMount () {
    const { btn } = this.props
    let _config = btn.modal
    _config.version = '1.0'
    this.setState({
      config: _config,
      originConfig: fromJS(_config).toJS()
    })
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  /**
   * @description 表单变化
   * 1、表单拖拽添加时,检查是否存在示例表单,如存在则去除示例
   * 2、表单移动后,保存移动后的顺序
   * 3、新增表单时,直接打开编辑框
   */
  handleList = (list, newcard) => {
    let _config = fromJS(this.state.config).toJS()
    if (list.length > _config.fields.length) {
      _config.fields = list.filter(item => !item.origin)
      this.setState({
        config: _config
      }, () => {
        this.handleForm(newcard)
      })
    } else {
      _config.fields = list
      this.setState({config: _config})
    }
  }
  /**
   * @description 表单编辑
   * 1、显示编辑弹窗-visible
   * 2、保存编辑项-card
   * 3、设置编辑参数项-formlist
   */
  handleForm = (_card) => {
    const { componentConfig } = this.props
    const { config } = this.state
    let card = fromJS(_card).toJS()
    let _inputfields = []
    let _tabfields = []
    let _linkableFields = []
    let _linksupFields = [{
      value: '',
      text: '空'
    }]
    let standardform = null
    _inputfields = config.fields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
    _tabfields = config.fields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
    _tabfields.unshift({field: '', text: '原表单'})
    let uniq = new Map()
    uniq.set(card.field, true)
    let index = null
    config.fields.forEach((item, i) => {
      if (card.uuid === item.uuid) {
        index = i
      }
      if (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') return
      if (item.field && !uniq.has(item.field)) {
        uniq.set(item.field, true)
        _linkableFields.push({
          value: item.field,
          text: item.label + ' (表单)'
        })
        _linksupFields.push({
          value: item.field,
          text: item.label
        })
      }
    })
    if (index !== null) {
      if (index === 0) {
        standardform = config.fields[index + 1] || null
      } else {
        standardform = config.fields[index - 1] || null
      }
    }
    componentConfig.columns.forEach(col => {
      if (col.field && !uniq.has(col.field)) {
        uniq.set(col.field, true)
        _linkableFields.push({
          value: col.field,
          text: col.label + ' (显示列)'
        })
      }
    })
    if (card.linkSubField && card.linkSubField.length > 0) {
      let fields = _inputfields.map(item => item.field)
      card.linkSubField = card.linkSubField.filter(item => fields.includes(item))
    }
    if (!card.span && standardform && standardform.span) {
      card.span = standardform.span
      card.labelwidth = standardform.labelwidth
    }
    this.setState({
      standardform,
      visible: true,
      card: card,
      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, !!this.props.editTab)
    })
  }
  /**
   * @description 编辑后提交
   * 1、获取编辑后的表单信息
   * 2、去除可能存在的示例表单
   * 3、通过loading刷新
   */
  handleSubmit = () => {
    this.formRef.handleConfirm().then(res => {
      let _config = fromJS(this.state.config).toJS()
      let fieldrepet = false // 字段重复
      let labelrepet = false // 提示文字重复
      _config.fields = _config.fields.map(item => {
        if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
          fieldrepet = true
        } else if (res.label && item.uuid !== res.uuid && item.label === res.label) {
          labelrepet = true
        }
        if (item.uuid === res.uuid) {
          return res
        } else {
          return item
        }
      })
      if (fieldrepet) {
        notification.warning({
          top: 92,
          message: '字段已存在!',
          duration: 10
        })
        return
      } else if (labelrepet) {
        notification.warning({
          top: 92,
          message: '名称已存在!',
          duration: 10
        })
        return
      }
      _config.fields = _config.fields.filter(item => !item.origin)
      if ((res.type === 'select' || res.type === 'multiselect' || res.type === 'link') && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
        let param = {
          func: 's_debug_sql',
          exec_type: 'y',
          LText: res.dataSource
        }
        param.LText = param.LText.replace(/@\$|\$@/ig, '')
        param.LText = Utils.formatOptions(param.LText)
        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
        param.secretkey = Utils.encrypt('', param.timestamp)
        if (window.GLOB.mainSystemApi && res.database === 'sso') {
          param.rduri = window.GLOB.mainSystemApi
        }
        Api.getLocalConfig(param).then(result => {
          if (result.status) {
            this.setState({
              sqlVerifing: false,
              config: _config,
              card: null,
              visible: false
            })
          } else {
            this.setState({sqlVerifing: false})
            Modal.error({
              title: result.message
            })
          }
        })
      } else {
        this.setState({
          config: _config,
          card: null,
          visible: false
        })
      }
    })
  }
  /**
   * @description 表单删除并刷新
   */
  closeForm = (card) => {
    let _this = this
    confirm({
      content: `确定删除${card.label ? `<<${card.label}>>` : ''}吗?`,
      onOk() {
        let _config = fromJS(_this.state.config).toJS()
        _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
        _this.setState({
          config: _config,
        })
      },
      onCancel() {}
    })
  }
  submitConfig = () => {
    const { config } = this.state
    this.setState({originConfig: fromJS(config).toJS(), closeVisible: false})
    this.props.handleSave(config)
    notification.success({
      top: 92,
      message: '保存成功。',
      duration: 2
    })
  }
  cancelConfig = () => {
    const { config, originConfig } = this.state
    if (!is(fromJS(config), fromJS(originConfig))) {
      this.setState({
        closeVisible: true
      })
    } else {
      this.props.handleBack()
    }
  }
  /**
   * @description 全局设置模态框
   */
  changeSetting = () => {
    this.setState({
      settingVisible: true
    })
  }
  /**
   * @description 保存全局设置
   */
  settingSave = () => {
    const {config} = this.state
    this.settingRef.handleConfirm().then(res => {
      this.setState({
        config: {...config, setting: res},
        settingVisible: false
      })
    })
  }
  editModalCancel = () => {
    const { config, card } = this.state
    if (card.focus) {
      let _fields = config.fields.filter(item => item.uuid !== card.uuid)
      let _config = {...config, fields: _fields}
      this.setState({
        card: null,
        config: _config,
        visible: false
      })
    } else {
      this.setState({
        card: null,
        visible: false
      })
    }
  }
  /**
   * @description 更新
   */
  updateConfig = (config) => {
    this.setState({
      config
    })
  }
  render () {
    const { btn } = this.props
    const { config, dict } = this.state
    return (
      <div className="mob-form-board">
        <DndProvider backend={HTML5Backend}>
          <div className="tools">
            <Collapse accordion defaultActiveKey="1" bordered={false}>
              <Panel header={dict['header.menu.form']} key="1">
                <div className="search-element">
                  {SearchItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <FieldsComponent
                  config={config}
                  type="form"
                  updatefield={this.updateConfig}
                />
              </Panel>
            </Collapse>
          </div>
          <div className="modal-control">
            <Button icon="setting" onClick={this.changeSetting}>设置</Button>
            <Button type="primary" onClick={this.submitConfig}>保存</Button>
            <Button onClick={this.cancelConfig}>返回</Button>
            <PasteComponent type="menu" Tab={null} insert={this.insert} />
            <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
          </div>
          <div className="setting">
            <div className="mob-shell" style={{width: window.GLOB.shellWidth, height: window.GLOB.shellHeight}}>
              <div className="mob-shell-inner">
                <div className="am-navbar">
                  <Icon type="close" />
                  <div className="am-navbar-title">{btn.label}</div>
                </div>
                <DragElement
                  list={config.fields}
                  setting={config.setting}
                  showField={this.state.showField}
                  placeholder={this.state.dict['header.form.modal.placeholder']}
                  handleList={this.handleList}
                  handleForm={this.handleForm}
                  closeForm={this.closeForm}
                />
                <Button className="modal-submit" type="primary">确定</Button>
              </div>
            </div>
          </div>
        </DndProvider>
        <Modal
          title={this.state.dict['model.edit']}
          visible={this.state.visible}
          width={850}
          onCancel={this.editModalCancel}
          onOk={this.handleSubmit}
          confirmLoading={this.state.sqlVerifing}
          destroyOnClose
        >
          <ModalForm
            dict={this.state.dict}
            card={this.state.card}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
            standardform={this.state.standardform}
            wrappedComponentRef={(inst) => this.formRef = inst}
          />
        </Modal>
        <Modal
          title={this.state.dict['model.edit']}
          visible={this.state.settingVisible}
          width={850}
          maskClosable={false}
          onOk={this.settingSave}
          onCancel={() => { this.setState({ settingVisible: false }) }}
          destroyOnClose
        >
          <SettingForm
            config={config}
            dict={this.state.dict}
            isSubTab={!!this.props.editTab}
            inputSubmit={this.settingSave}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
        <Modal
          bodyStyle={{textAlign: 'center', color: '#000000', fontSize: '16px'}}
          closable={false}
          maskClosable={false}
          visible={this.state.closeVisible}
          onCancel={() => { this.setState({closeVisible: false}) }}
          footer={[
            <Button key="save" className="mk-btn mk-green" loading={this.state.closeloading} onClick={this.submitConfig}>{this.state.dict['model.save']}</Button>,
            <Button key="confirm" className="mk-btn mk-yellow" onClick={this.props.handleBack}>{this.state.dict['model.notsave']}</Button>,
            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['model.cancel']}</Button>
          ]}
          destroyOnClose
        >
          {this.state.dict['header.menu.config.placeholder']}
        </Modal>
      </div>
    )
  }
}
export default ComModalConfig
src/mob/modalconfig/index.scss
New file
@@ -0,0 +1,320 @@
.mob-form-board {
  position: fixed;
  z-index: 1070;
  top: 48px;
  left: 0px;
  right: 0px;
  bottom: 0px;
  background: rgba(0, 0, 0, 1);
  .tools {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    width: 235px;
    background: #ffffff;
    border-right: 1px solid #d9d9d9;
    height: 100%;
    overflow-y: auto;
    padding-bottom: 30px;
    .ant-collapse-borderless {
      background-color: #ffffff;
    }
    .ant-collapse-item {
      border: 0;
    }
    .ant-input-search {
      margin-top: 10px;
    }
    .ant-collapse-item.ant-collapse-item-active {
      border-bottom: 1px solid #d9d9d9;
    }
    .ant-collapse .ant-collapse-header {
      padding: 11px 16px 10px 40px;
      border-bottom: 1px solid #d9d9d9;
      background: #1890ff;
      color: #ffffff;
    }
    .ant-collapse-content-box {
      .ant-form-item {
        margin-bottom: 10px;
        .ant-form-item-label {
          text-align: left;
          height: 25px;
          line-height: 25px;
        }
      }
      .ant-btn {
        margin-bottom: 10px;
      }
    }
    .search-element {
      padding-top: 10px;
      li {
        padding: 0px 16px 10px;
        div {
          cursor: move;
        }
      }
    }
    .tables {
      .ant-select-selection-selected-value {
        opacity: 0.4!important;
      }
    }
    .ant-list {
      margin-top: 20px;
      .ant-list-item {
        display: -webkit-box;
        padding-right: 20px;
        position: relative;
        padding-left: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        width: 100%;
        .anticon {
          position: absolute;
          top: 0px;
          right: 0px;
          padding: 3px 3px 10px 10px;
          cursor: pointer;
        }
        .bottom-mask {
          position: absolute;
          width: 100%;
          height: 8px;
          bottom: 0;
          left: 0;
          background: #ffffff;
          border-radius: 8px;
        }
      }
    }
  }
  .tools::-webkit-scrollbar {
    width: 4px;
  }
  .tools::-webkit-scrollbar-thumb {
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
    background: rgba(0, 0, 0, 0.08);
  }
  .tools::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.07);
    background: rgba(0, 0, 0, 0);
  }
  .modal-control {
    position: absolute;
    right: 0;
    top: 0;
    width: 120px;
    height: 100vh;
    padding: 20px 10px;
    background: #ffffff;
    z-index: 10;
    transition: right 0.3s;
    div:not(.draw), button:not(.ant-switch) {
      display: block!important;
      margin-bottom: 15px;
      width: 100%;
    }
    .ant-switch.big {
      min-width: 60px;
      height: 24px;
      line-height: 24px;
      margin-bottom: 15px;
      .ant-switch-inner {
        font-size: 14px;
      }
    }
    .ant-switch.big:after {
      width: 22px;
      height: 22px;
    }
  }
  .setting {
    position: relative;
    width: 100vw;
    height: 100%;
    background: #959595;
    .mob-shell {
      margin: 0 auto;
      background: #000000;
      background-size: 100% 100%;
      padding: 25px 13px 40px;
      border-radius: 30px;
      .am-navbar {
        height: 45px;
        line-height: 45px;
        position: relative;
        border-bottom: 1px solid #f0f0f0;
        margin-bottom: 10px;
        .anticon-close {
          position: absolute;
          line-height: 45px;
          font-size: 18px;
          left: 10px;
        }
        .am-navbar-title {
          font-size: 16px;
          text-align: center;
        }
      }
      .modal-submit {
        width: 100%;
        border-radius: 0;
        opacity: 0.5;
        cursor: default;
        font-size: 18px;
        height: 40px;
      }
      .modal-fields-row {
        min-height: calc(100% - 95px);
      }
      .modal-form {
        padding: 0px 24px;
        min-height: 87px;
        .group-title {
          position: relative;
          min-height: 22px;
          margin-bottom: 10px;
          padding-top: 10px;
          border-bottom: 1px solid #e8e8e8;
          span {
            padding: 0 5px 5px;
          }
        }
        > .ant-row {
          min-height: 120px;
        }
        .ant-row .ant-col-6 {
          padding: 0 12px!important;
        }
        .ant-row.ant-form-item .ant-col {
          padding: 0;
        }
        .textarea2, .textarea4 {
          padding-left: 7px;
        }
        .page-card {
          position: relative;
          background: #ffffff;
          border-radius: 2px;
          margin-bottom: 15px;
          .ant-form-item {
            cursor: move;
            display: flex;
            margin-bottom: 0px;
            .ant-form-item-label {
              overflow: visible;
              position: relative;
              height: 40px;
              label {
                width: 100%;
                cursor: move;
                overflow: hidden;
                display: inline-block;
                text-overflow: ellipsis;
                white-space: nowrap;
              }
            }
            .ant-form-item-control-wrapper {
              position: relative;
              .ant-select {
                width: 100%;
                margin-top: 4px;
              }
              .ant-calendar-picker {
                width: 100%;
                margin-top: 4px;
              }
              .ant-input-number {
                width: 100%;
                margin-top: 4px;
              }
              .normal-braft-editor {
                border: 1px solid #d9d9d9;
                border-radius: 4px;
              }
            }
            .ant-form-item-control-wrapper::after {
              content: '';
              position: absolute;
              top: 0;
              left: 0;
              right: 0;
              bottom: 0;
              opacity: 0;
              z-index: 1;
            }
            .ant-col-cuslabel {
              width: 10.5%;
            }
            .ant-col-cuswrap {
              width: 89.5%;
            }
          }
        }
        .ant-calendar-picker {
          min-width: 100px!important;
        }
      }
    }
  }
  .setting {
    overflow-y: auto;
  }
  .setting::-webkit-scrollbar {
    width: 7px;
  }
  .setting::-webkit-scrollbar-thumb {
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
    background: rgba(0, 0, 0, 0.13);
  }
  .setting::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.07);
    background: rgba(0, 0, 0, 0);
  }
}
.modal-fields {
  .ant-modal {
    top: 50px;
    padding-bottom: 5px;
    .ant-modal-body {
      max-height: calc(100vh - 190px);
      overflow-y: auto;
      .ant-empty {
        margin: 15vh 8px;
      }
    }
    .ant-modal-body::-webkit-scrollbar {
      width: 7px;
    }
    .ant-modal-body::-webkit-scrollbar-thumb {
      border-radius: 5px;
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
      background: rgba(0, 0, 0, 0.13);
    }
    .ant-modal-body::-webkit-scrollbar-track {
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
      border-radius: 3px;
      border: 1px solid rgba(0, 0, 0, 0.07);
      background: rgba(0, 0, 0, 0);
    }
  }
}
src/mob/modalconfig/pastecomponent/index.jsx
New file
@@ -0,0 +1,76 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Button, Modal, notification } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const PasteForm = asyncComponent(() => import('@/templates/zshare/pasteform'))
class PasteController extends Component {
  static propTpyes = {
    config: PropTypes.object,        // 组件配置
    updateConfig: PropTypes.func
  }
  state = {
    visible: false
  }
  pasteSubmit = () => {
    const { config } = this.props
    this.pasteFormRef.handleConfirm().then(res => {
      if (res.copyType !== 'form') {
        notification.warning({ top: 92, message: '配置信息格式错误!', duration: 5 })
        return
      }
      let keys = config.fields.map(item => item.field ? item.field.toLowerCase() : '$emp_ty$')
      if (['multiselect', 'color', 'brafteditor'].includes(res.type)) {
        res.type = 'text'
      }
      if (res.field && keys.includes(res.field.toLowerCase())) {
        notification.warning({
          top: 92,
          message: '搜索字段已存在!',
          duration: 5
        })
        return
      }
      this.props.updateConfig(config)
      this.setState({visible: false})
      notification.success({
        top: 92,
        message: '粘贴成功!',
        duration: 2
      })
    })
  }
  render() {
    const { visible } = this.state
    return (
      <div style={{display: 'inline-block'}}>
        <Button icon="snippets" style={{color: 'purple'}} onClick={() => {this.setState({visible: true})}} >粘贴</Button>
        <Modal
          title="粘贴"
          visible={visible}
          width={600}
          maskClosable={false}
          onOk={this.pasteSubmit}
          onCancel={() => {this.setState({visible: false})}}
          destroyOnClose
        >
          <PasteForm wrappedComponentRef={(inst) => this.pasteFormRef = inst} inputSubmit={this.pasteSubmit}/>
        </Modal>
      </div>
    )
  }
}
export default PasteController
src/mob/modalconfig/pastecomponent/index.scss
src/mob/modalconfig/source.jsx
New file
@@ -0,0 +1,104 @@
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
export const SearchItems = [
  {
    type: 'form',
    label: CommonDict['model.form.text'],
    subType: 'text',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.number'],
    subType: 'number',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.select'],
    subType: 'select',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.link'],
    subType: 'link',
    url: ''
  },
  {
    type: 'form',
    label: '开关',
    subType: 'switch',
    url: ''
  },
  {
    type: 'form',
    label: '多选框',
    subType: 'checkbox',
    url: ''
  },
  {
    type: 'form',
    label: '单选框',
    subType: 'radio',
    url: ''
  },
  {
    type: 'form',
    label: '选项卡',
    subType: 'checkcard',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['header.form.fileupload'],
    subType: 'fileupload',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.dateday'],
    subType: 'date',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.datemonth'],
    subType: 'datemonth',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.datetime'],
    subType: 'datetime',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.textarea'],
    subType: 'textarea',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['header.form.funcvar'],
    subType: 'funcvar',
    url: ''
  },
  {
    type: 'form',
    label: '提示',
    subType: 'hint',
    url: ''
  },
  {
    type: 'form',
    label: '分割线',
    subType: 'split',
    url: ''
  }
]
src/tabviews/custom/components/chart/antv-bar-line/index.jsx
@@ -925,7 +925,7 @@
    } else {
      _chart.color(_typefield)
    }
    if (plot.label === 'true') {
    if (plot.label !== 'false') {
      _chart.label(_valfield, (value) => {
        if (plot.show === 'percent') {
          value = value + '%'
@@ -1160,7 +1160,7 @@
        } else {
          _chart.color('key')
        }
        if (plot.label === 'true') {
        if (plot.label !== 'false') {
          _chart.label('value*key', (value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
@@ -1211,7 +1211,7 @@
        } else {
          _chart.color('key')
        }
        if (plot.label === 'true') {
        if (plot.label !== 'false') {
          _chart.label('value*key', (value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
@@ -1269,7 +1269,7 @@
        if (plot.barSize) {
          _chart.size(plot.barSize || 35)
        }
        if (item.label === 'true') {
        if (item.label !== 'false') {
          _chart.label(item.name, (value) => {
            if (plot.show === 'percent') {
              value = value + '%'
@@ -1514,7 +1514,7 @@
      } else {
        _chart.color(_typefield)
      }
      if (plot.label === 'true') {
      if (plot.label !== 'false') {
        _chart.label(`${_valfield}*${_typefield}`, (value, key) => {
          if (plot.show === 'percent') {
            value = value + '%'
@@ -1572,7 +1572,7 @@
      } else {
        _chart.color(_typefield)
      }
      if (plot.label === 'true') {
      if (plot.label !== 'false') {
        _chart.label(`${_valfield}*${_typefield}`, (value, key) => {
          if (plot.show === 'percent') {
            value = value + '%'
src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -130,9 +130,7 @@
    if (btn.search === 'true' && viewParam.search && viewParam.search.length > 0) {
      let valid = false
      viewParam.search.forEach(item => {
        if (Array.isArray(item.value) && item.value.length > 0) {
          valid = true
        } else if (item.value || item.value === 0) {
        if (item.value || item.value === 0) {
          valid = true
        }
      })
src/templates/modalconfig/settingform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, InputNumber, Select } from 'antd'
import { Form, Row, Col, Input, Radio, InputNumber, Select, Tooltip, Icon } from 'antd'
import { formRule } from '@/utils/option.js'
import './index.scss'
@@ -20,6 +20,7 @@
  UNSAFE_componentWillMount () {
    const { config } = this.props
    const { appType, display } = this.state
    let fields = []
    config.fields.forEach(f => {
@@ -28,8 +29,14 @@
      }
    })
    let _display = display
    if (appType === 'mob' && display !== 'prompt' && display !== 'drawer') {
      _display = 'drawer'
    }
    this.setState({
      fields: fields
      fields: fields,
      display: _display
    })
  }
@@ -87,13 +94,25 @@
              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
          <Col span={12}>
          {appType !== 'mob' ? <Col span={12}>
            <Form.Item label="宽度(%)">
              {getFieldDecorator('width', {
                initialValue: config.setting.width
              })(<InputNumber min={10} max={90} precision={0} onPressEnter={this.handleSubmit}/>)}
            </Form.Item>
          </Col>
          </Col> : null}
          {appType === 'mob' ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="宽度小于100时为百分率,大于100时为绝对值。">
                <Icon type="question-circle" />
                宽度
              </Tooltip>
            }>
              {getFieldDecorator('width', {
                initialValue: config.setting.width
              })(<InputNumber min={10} max={2000} precision={0} onPressEnter={this.handleSubmit}/>)}
            </Form.Item>
          </Col> : null}
          <Col span={12}>
            <Form.Item label="焦点">
              {getFieldDecorator('focus', {
@@ -115,7 +134,7 @@
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
          {appType !== 'mob' ? <Col span={12}>
            <Form.Item label="表单排列">
              {getFieldDecorator('align', {
                initialValue: config.setting.align || 'left_right'
@@ -126,7 +145,7 @@
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          </Col> : null}
          <Col span={12}>
            <Form.Item label="完成后">
              {getFieldDecorator('finish', {
@@ -154,17 +173,17 @@
          <Col span={12}>
            <Form.Item label="显示方式">
              {getFieldDecorator('display', {
                initialValue: config.setting.display || 'modal'
                initialValue: display || 'modal'
              })(
                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({display: e.target.value})}>
                  <Radio value="modal">模态框</Radio>
                  {appType !== 'mob' ? <Radio value="modal">模态框</Radio> : null}
                  <Radio value="prompt">是否框</Radio>
                  <Radio value="drawer">抽屉</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {!this.props.isSubTab && appType !== 'pc' && display === 'modal' ? <Col span={12}>
          {!this.props.isSubTab && !appType && display === 'modal' ? <Col span={12}>
            <Form.Item label="挂载对象">
              {getFieldDecorator('container', {
                initialValue: config.setting.container || 'tab'
src/templates/modalconfig/settingform/index.scss
@@ -10,4 +10,9 @@
  .ant-input-number {
    width: 100%;
  }
  .anticon-question-circle {
    color: #c49f47;
    position: relative;
    left: -3px;
  }
}
src/templates/zshare/formconfig.jsx
@@ -2629,11 +2629,14 @@
      label: '扫码',
      initVal: card.scan || 'false',
      options: [{
        value: 'true',
        text: '启用'
      }, {
        value: 'false',
        text: '不启用'
        text: '禁用'
      }, {
        value: 'simple',
        text: '单次扫码'
      }, {
        value: 'multi',
        text: '连续扫码'
      }],
      forbid: appType !== 'mob'
    },
src/views/mobdesign/index.jsx
@@ -34,7 +34,7 @@
const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
const ModalController = asyncComponent(() => import('@/mob/modalconfig/controller'))
const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
@@ -1477,7 +1477,7 @@
              <div className="pc-setting-tools">
                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
                  {/* 基本信息 */}
                  <Panel header={dict['mob.basemsg']} key="basedata">
                  <Panel header={dict['mob.basemsg']} forceRender key="basedata">
                    {/* 菜单信息 */}
                    {config ? <MenuForm
                      dict={dict}