king
2020-11-02 c7c3b0cd51c2c9251a11e4b5bc5057cc92f6e9a7
2020-11-02
13个文件已修改
12个文件已添加
1147 ■■■■■ 已修改文件
src/assets/mobimg/mainsearch.png 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/actioncomponent/dragaction/card.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/actioncomponent/dragaction/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dategroup/index.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dategroup/index.scss 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/card.jsx 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/index.jsx 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 407 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.scss 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/index.jsx 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/settingform/index.jsx 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/card.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modelsource/option.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/searchcomponent/dragsearch/card.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/searchcomponent/dragsearch/index.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/popupbutton/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/mainsearch.png
src/menu/actioncomponent/dragaction/card.jsx
@@ -3,7 +3,7 @@
import { Icon, Button, Popover } from 'antd'
import './index.scss'
const Card = ({ id, cardIds, card, moveCard, findCard, editCard, delCard, copyCard, profileCard, doubleClickCard }) => {
const Card = ({ id, card, moveCard, findCard, editCard, delCard, copyCard, profileCard, doubleClickCard }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'action', id, originalIndex },
@@ -16,13 +16,13 @@
    canDrop: () => true,
    drop: () => {},
    hover({ id: draggedId }) {
      if (!draggedId) return
      if (!cardIds.includes(draggedId)) return
      if (!draggedId || draggedId === id) return
      if (draggedId !== id) {
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
      const { index: originIndex } = findCard(id)
      if (originIndex === -1) return
      const { index: overIndex } = findCard(id)
      moveCard(draggedId, overIndex)
    },
  })
  const opacity = isDragging ? 0 : 1
src/menu/actioncomponent/dragaction/index.jsx
@@ -85,8 +85,6 @@
    handleList(_cards, copycard)
  }
  let cardIds = cards.map(card => card.uuid)
  const [, drop] = useDrop({
    accept: 'action',
    drop() {}
@@ -130,7 +128,6 @@
        <Card
          id={card.uuid}
          key={card.uuid}
          cardIds={cardIds}
          card={card}
          moveCard={moveCard}
          copyCard={copyCard}
src/menu/components/search/main-search/dategroup/index.jsx
New file
@@ -0,0 +1,32 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Tag } from 'antd'
import './index.scss'
const { CheckableTag } = Tag
class DateGroup extends Component {
  static propTpyes = {
    card: PropTypes.object    // 字典项
  }
  render() {
    const { card } = this.props
    let tabs = {day: '日', week: '周', month: '月', quarter: '季', year: '年', customized: '自定义'}
    return (
      <div className="model-date-group">
        {card.items.map(tab => (
          <CheckableTag
            key={tab}
            checked={card.initval && card.initval.includes(tab)}
          >
            {tabs[tab]}
          </CheckableTag>
        ))}
      </div>
    )
  }
}
export default DateGroup
src/menu/components/search/main-search/dategroup/index.scss
New file
@@ -0,0 +1,38 @@
.model-date-group {
  white-space: nowrap;
  line-height: 40px;
  position: relative;
  z-index: 1;
  .ant-tag-checkable {
    border-color: #d1d5d9;
    border-radius: 2px;
    margin-right: 2px;
    padding: 2px 6px;
  }
  .ant-tag-checkable-checked {
    border-color: #1890ff;
  }
}
@media screen and (min-width: 1440px) {
  .model-date-group {
    .ant-tag-checkable {
      padding: 2px 7px;
    }
  }
}
@media screen and (min-width: 1600px) {
  .model-date-group {
    .ant-tag-checkable {
      padding: 2px 9px;
    }
  }
}
@media screen and (min-width: 1920px) {
  .model-date-group {
    .ant-tag-checkable {
      padding: 2px 11px;
    }
  }
}
src/menu/components/search/main-search/dragsearch/card.jsx
New file
@@ -0,0 +1,104 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Select, DatePicker, Input, Popover } from 'antd'
import moment from 'moment'
import DateGroup from '../dategroup'
import './index.scss'
const { MonthPicker, WeekPicker, RangePicker } = DatePicker
const Card = ({ id, card, moveCard, copyCard, findCard, editCard, delCard }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'search', id, originalIndex },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: 'search',
    canDrop: () => true,
    drop: ({ id: draggedId }) => {
      if (!draggedId) return
      if (draggedId !== id) {
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
    },
  })
  const opacity = isDragging ? 0 : 1
  let _defaultValue = '' // 下拉搜索、时间范围类型,初始值需要预处理
  if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link') {
    if (card.initval) {
      let _option = card.options.filter(option => option.Value === card.initval)[0]
      if (_option) {
        _defaultValue = _option.Text || ''
      } else {
        _defaultValue = ''
      }
    } else if (card.setAll === 'true') {
      _defaultValue = 'All'
    }
  } else if (card.type === 'daterange') {
    _defaultValue = [null, null]
    if (card.initval) {
      try {
        let _initval = JSON.parse(card.initval)
        _defaultValue = [moment().subtract(_initval[0], 'days'), moment().subtract(_initval[1], 'days')]
      } catch {
        _defaultValue = [null, null]
      }
    }
  }
  return (
    <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
      <div className="mk-popover-control">
        <Icon className="edit" title="edit" type="edit" onClick={() => editCard(id)} />
        <Icon className="copy" title="copy" type="copy" onClick={() => copyCard(id)} />
        <Icon className="close" title="delete" type="close" onClick={() => delCard(id)} />
      </div>
    } trigger="hover">
      <div className="page-card" style={{ opacity: opacity}}>
        <div ref={node => drag(drop(node))}>
          <div className="ant-row ant-form-item">
            <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
              <label className={card.required === 'true' ? 'ant-form-item-required' : ''} title={card.label}>{card.label}</label>
            </div>
            <div className="ant-col ant-form-item-control-wrapper ant-col-xs-24 ant-col-sm-16">
              {card.type === 'text' ?
                <Input style={{marginTop: '4px'}} value={card.initval} /> : null
              }
              {(card.type === 'multiselect' || card.type === 'select' || card.type === 'link') ?
                <Select value={_defaultValue}></Select> : null
              }
              {card.type === 'date' ?
                <DatePicker value={card.initval ? moment().subtract(card.initval, 'days') : null} /> : null
              }
              {card.type === 'dateweek' ?
                <WeekPicker value={card.initval ? moment().subtract(card.initval * 7, 'days') : null} /> : null
              }
              {card.type === 'datemonth' ?
                <MonthPicker value={card.initval ? moment().subtract(card.initval, 'month') : null} /> : null
              }
              {card.type === 'daterange' ?
                <RangePicker
                  className="data-range"
                  placeholder={['BeginTime', 'EndTime']}
                  renderExtraFooter={() => 'extra footer'}
                  value={_defaultValue}
                /> : null
              }
              {card.type === 'group' ? <DateGroup card={card} /> : null }
              <div className="input-mask"></div>
            </div>
          </div>
        </div>
      </div>
    </Popover>
  )
}
export default Card
src/menu/components/search/main-search/dragsearch/index.jsx
New file
@@ -0,0 +1,105 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Col } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
const Container = ({list, placeholder, handleList, handleMenu, deleteMenu }) => {
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    handleList(_cards)
  }
  if (!is(fromJS(cards), fromJS(list))) {
    setCards(list)
  }
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
    return {
      card,
      index: cards.indexOf(card),
    }
  }
  const editCard = id => {
    const { card } = findCard(id)
    handleMenu(card)
  }
  const delCard = id => {
    const { card } = findCard(id)
    deleteMenu(card)
  }
  const copyCard = id => {
    const { card } = findCard(id)
    let copycard = fromJS(card).toJS()
    copycard.uuid = Utils.getuuid()
    copycard.origin = false
    copycard.copyType = 'search'
    copycard.focus = true
    let _val = fromJS(copycard).toJS()
    try {
      _val.uuid = Utils.getuuid()
      _val = window.btoa(window.encodeURIComponent(JSON.stringify(_val)))
    } catch {
      console.warn('Stringify Failure')
      _val = ''
    }
    if (_val) {
      let oInput = document.createElement('input')
      oInput.value = _val
      document.body.appendChild(oInput)
      oInput.select()
      document.execCommand('Copy')
      document.body.removeChild(oInput)
    }
    const { index: overIndex } = findCard(id)
    const _cards = update(cards, { $splice: [[overIndex + 1, 0, copycard]] })
    handleList(_cards, copycard)
  }
  const [, drop] = useDrop({
    accept: 'search',
    drop() {}
  })
  return (
    <div ref={drop} className="ant-row">
      {cards.map(card => (
        <Col key={card.uuid} span={card.ratio || 6}>
          <Card
            id={`${card.uuid}`}
            card={card}
            moveCard={moveCard}
            copyCard={copyCard}
            editCard={editCard}
            delCard={delCard}
            findCard={findCard}
          />
        </Col>
      ))}
      {cards.length === 0 ?
        <div className="common-drawarea-placeholder">
          {placeholder}
        </div> : null
      }
    </div>
  )
}
export default Container
src/menu/components/search/main-search/dragsearch/index.scss
New file
@@ -0,0 +1,6 @@
.common-drawarea-placeholder {
  width: 100%;
  line-height: 65px;
  text-align: center;
  color: #bcbcbc;
}
src/menu/components/search/main-search/index.jsx
New file
@@ -0,0 +1,407 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { is, fromJS } from 'immutable'
import { Modal, notification, Popover, Icon } from 'antd'
import moment from 'moment'
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 { getSearchForm } from '@/templates/zshare/formconfig'
import asyncIconComponent from '@/utils/asyncIconComponent'
import SearchForm from '@/templates/sharecomponent/searchcomponent/searchform'
import DragElement from './dragsearch'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { confirm } = Modal
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
class MainSearchComponent extends Component {
  static propTpyes = {
    card: PropTypes.object,
    updateConfig: PropTypes.func,
    deletecomponent: PropTypes.func,
    menu: PropTypes.object,          // 当前菜单信息
    config: PropTypes.object,        // 配置信息
    pasteContent: PropTypes.object,  // 粘贴配置信息
    sysRoles: PropTypes.array,       // 角色列表,黑名单
    updatesearch: PropTypes.func     // 更新
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    searchlist: null,    // 搜索条件集
    sqlVerifing: false,  // sql验证中
    visible: false,      // 模态框控制
    editcard: null       // 编辑中元素
  }
  /**
   * @description 搜索条件初始化
   */
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        tabId: card.tabId || '',
        parentId: card.parentId || '',
        width: 24,
        name: card.name,
        subtype: card.subtype,
        wrap: { name: card.name, width: 24 },
        style: {
          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
        },
        search: []
      }
      this.setState({
        card: _card
      })
      this.props.updateConfig(_card)
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds.length !== 1 || comIds[0] !== card.uuid) return
    let _card = {...card, style}
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
  }
  /**
   * @description 卡片行外层信息更新(数据源,样式等)
   */
  updateComponent = (component) => {
    this.setState({
      card: component
    })
    component.width = component.wrap.width
    component.name = component.wrap.name
    this.props.updateConfig(component)
  }
  /**
   * @description 搜索条件顺序调整,或拖拽添加
   */
  handleList = (list, card) => {
    const { config } = this.props
    if (card) {
      this.setState({searchlist: list})
      this.handleSearch(card)
    } else {
      this.setState({searchlist: list}, ()=> {
        this.props.updatesearch({...config, search: list})
      })
    }
  }
  /**
   * @description 搜索条件编辑,获取搜索条件表单信息
   */
  handleSearch = (cell) => {
    const { card } = this.state
    let linkableFields = []
    card.search.forEach(item => {
      if (item.uuid !== cell.uuid && (item.type === 'select' || item.type === 'link')) {
        linkableFields.push({
          value: item.field,
          text: item.label
        })
      }
    })
    this.setState({
      visible: true,
      editcard: cell,
      formlist: getSearchForm(cell, this.props.sysRoles, linkableFields)
    })
  }
  /**
   * @description 取消保存,如果元素为新添元素,则从序列中删除
   */
  editModalCancel = () => {
    const { card } = this.state
    if (card.focus) {
      let searchlist = fromJS(this.state.searchlist).toJS()
      searchlist = searchlist.filter(item => item.uuid !== card.uuid)
      this.setState({
        card: null,
        searchlist: searchlist,
        visible: false
      })
    } else {
      this.setState({
        card: null,
        visible: false
      })
    }
  }
  /**
   * @description 搜索修改后提交保存
   * 1、去除系统默认搜索条件
   * 2、字段及提示文字重复校验
   * 3、更新下拉菜单可选集合
   * 4、下拉菜单数据源语法验证
   */
  handleSubmit = () => {
    const { config } = this.props
    let _searchlist = fromJS(this.state.searchlist).toJS()
    this.searchFormRef.handleConfirm().then(res => {
      let fieldrepet = false // 字段重复
      let labelrepet = false // 提示文字重复
      _searchlist = _searchlist.filter(item => !item.origin || item.uuid === res.uuid) // 去除系统项
      _searchlist = _searchlist.map(item => { // 数据更新及重复检测
        if (item.uuid !== res.uuid && res.field && item.field) {
          let itemFields = []
          if (item.type === 'text') {
            itemFields = item.field.split(',')
          } else if (item.type === 'group') {
            itemFields = [item.field, item.datefield]
          } else {
            itemFields = [item.field]
          }
          let resFields = []
          if (res.type === 'text') {
            resFields = res.field.split(',')
          } else if (res.type === 'group') {
            resFields = [res.field, res.datefield]
          } else {
            resFields = [res.field]
          }
          let setFields = Array.from(new Set([...itemFields, ...resFields]))
          if (setFields.length < itemFields.length + resFields.length && (res.type !== 'date' || item.type !== 'date')) {
            fieldrepet = true
          } else if (item.label === res.label) {
            labelrepet = true
          }
        }
        if (item.uuid === res.uuid) {
          return res
        } else {
          return item
        }
      })
      if (fieldrepet) {
        notification.warning({
          top: 92,
          message: this.state.dict['model.field.exist'] + ' !',
          duration: 5
        })
        return
      } else if (labelrepet) {
        notification.warning({
          top: 92,
          message: this.state.dict['model.name.exist'] + ' !',
          duration: 5
        })
        return
      }
      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',
          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.LText, 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,
              searchlist: _searchlist,
              visible: false
            }, ()=> {
              this.props.updatesearch({...config, search: _searchlist})
            })
          } else {
            this.setState({sqlVerifing: false})
            Modal.error({
              title: result.message
            })
          }
        })
      } else {
        this.setState({
          searchlist: _searchlist,
          visible: false
        }, ()=> {
          this.props.updatesearch({...config, search: _searchlist})
        })
      }
    })
  }
  /**
   * @description 搜索条件删除
   */
  deleteElement = (card) => {
    const { config } = this.props
    const { dict } = this.state
    let _this = this
    confirm({
      content: dict['model.confirm'] + dict['model.delete'] + ` - ${card.label} ?`,
      onOk() {
        let _searchlist = fromJS(_this.state.searchlist).toJS()
        _searchlist = _searchlist.filter(item => item.uuid !== card.uuid)
        _this.setState({
          searchlist: _searchlist
        }, () => {
          _this.props.updatesearch({...config, search: _searchlist})
        })
      },
      onCancel() {}
    })
  }
  addSearch = () => {
    let card = fromJS(this.state.card).toJS()
    let item = {
      uuid: Utils.getuuid(),
      label: '搜索',
      field: '',
      initval: '',
      type: 'text',
      resourceType: '0',
      match: 'like',
      focus: true
    }
    card.search.push(item)
    this.setState({card})
  }
  render() {
    const { dict, card, visible, sqlVerifing } = this.state
    return (
      <div className="main-search-edit-list" style={card.style}>
        <DragElement
          list={card.search}
          handleList={this.handleList}
          handleMenu={this.handleSearch}
          deleteMenu={this.deleteElement}
          placeholder={dict['header.form.search.placeholder']}
        />
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加" onClick={this.addSearch} type="plus" />
            <WrapComponent config={card} updateConfig={this.updateComponent}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {/* 编辑搜索条件 */}
        <Modal
          title={dict['model.searchCriteria'] + '-' + dict['model.edit']}
          visible={visible}
          width={850}
          maskClosable={false}
          onOk={this.handleSubmit}
          confirmLoading={sqlVerifing}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <SearchForm
            dict={dict}
            card={this.state.card}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
            wrappedComponentRef={(inst) => this.searchFormRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    menu: state.customMenu
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(MainSearchComponent)
src/menu/components/search/main-search/index.scss
New file
@@ -0,0 +1,102 @@
.main-search-edit-list {
  min-height: 50px;
  position: relative;
  >.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);
  }
  > .ant-row {
    min-height: 65px;
  }
  .ant-row .ant-col-6 {
    padding: 0 12px!important;
  }
  .ant-row.ant-form-item .ant-col {
    padding: 0;
  }
  .page-card {
    position: relative;
    background: #ffffff;
    border-radius: 2px;
    padding-bottom: 15px;
    .ant-form-item {
      cursor: move;
      display: flex;
      margin-bottom: 0px;
      .ant-form-item-label {
        height: 40px;
        label {
          width: 100%;
          cursor: move;
          overflow: hidden;
          display: inline-block;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
      }
      .ant-form-item-control-wrapper {
        flex: 1 1;
        .ant-select {
          width: 100%;
          margin-top: 4px;
        }
        .ant-calendar-picker {
          margin-top: 4px;
        }
        .input-mask {
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          opacity: 0;
          z-index: 2;
        }
        .data-range .ant-calendar-picker-input {
          padding: 4px 20px 4px 5px;
          font-size: 13px;
        }
      }
    }
    .edit {
      position: absolute;
      left: 0;
      top: 5px;
      color: #1890ff;
      cursor: pointer;
      display: none;
    }
    .edit.copy {
      left: 20px;
      color: #26C281;
    }
    .edit.close {
      left: 40px;
      color: #ff4d4f;
    }
  }
  .page-card:hover {
    .edit {
      display: inline-block;
    }
  }
  .ant-calendar-picker {
    min-width: 100px!important;
    width: 100%;
  }
}
.main-search-edit-list::after {
  display: block;
  content: ' ';
  clear: both;
}
.main-search-edit-list:hover {
  box-shadow: 0px 0px 2px #e8e8e8;
}
src/menu/components/search/main-search/wrapsetting/index.jsx
New file
@@ -0,0 +1,80 @@
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: localStorage.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 { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title={'搜索设置'}
          visible={visible}
          width={700}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/menu/components/search/main-search/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/search/main-search/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,88 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Tooltip, Icon, InputNumber } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    wrap: PropTypes.object,      // 数据源配置
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  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={
                <Tooltip placement="topLeft" title="用于组件间的区分。">
                  <Icon type="question-circle" />
                  组件名称
                </Tooltip>
              }>
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '组件名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: wrap.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} />)}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/search/main-search/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/menu/components/tabs/tabcomponents/card.jsx
@@ -5,6 +5,7 @@
import './index.scss'
const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
@@ -26,10 +27,14 @@
      if (originalIndex === undefined) {
        item.dropTargetId = id
      } else if (draggedId && floor === card.floor) {
        if (draggedId !== id) {
          const { index: overIndex } = findCard(id)
          moveCard(draggedId, overIndex)
        }
        if (draggedId === id) return
        const { index: originIndex } = findCard(draggedId)
        if (originIndex === -1) return
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
    }
  })
@@ -42,6 +47,8 @@
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
    } else if (card.type === 'search') {
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tabs') {
src/menu/menushell/card.jsx
@@ -5,6 +5,7 @@
import './index.scss'
const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
@@ -28,6 +29,7 @@
      } else if (draggedId && floor === card.floor) {
        if (draggedId !== id) {
          const { index: overIndex } = findCard(id)
          console.log(item)
          moveCard(draggedId, overIndex)
        }
      }
@@ -42,6 +44,8 @@
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'search') {
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tabs') {
src/menu/menushell/index.jsx
@@ -80,6 +80,7 @@
        line: '折线图',
        tabs: '标签组',
        pie: '饼图',
        search: '搜索',
        card: '卡片'
      }
      let i = 1
src/menu/modelsource/option.jsx
@@ -10,12 +10,14 @@
import Pie from '@/assets/mobimg/pie.png'
import Pie1 from '@/assets/mobimg/ring.png'
import Pie2 from '@/assets/mobimg/nightingale.png'
import Mainsearch from '@/assets/mobimg/mainsearch.png'
// const _dict =  sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
// 组件配置信息
export const menuOptions = [
  { type: 'menu', url: tabs, component: 'tabs', subtype: 'tabs', title: '标签页', width: 24 },
  { type: 'menu', url: Mainsearch, component: 'search', subtype: 'mainsearch', title: '搜索条件', width: 24 },
  { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '数据卡', config: `[{"uuid":"160135809128212dm7i29fim9ksto9od","setting":{"width":6},"style":{"paddingTop":"15px","marginTop":"4px","paddingRight":"15px","marginRight":"8px","marginLeft":"8px","backgroundColor":"rgba(255, 255, 255, 1)","borderColor":"#e8e8e8","paddingLeft":"15px","marginBottom":"4px","borderWidth":"1px","paddingBottom":"10px"},"backStyle":{},"elements":[{"datatype":"static","width":12,"marks":null,"height":1,"value":"关单","style":{},"prefix":"","postfix":"","format":"","eleType":"text","uuid":"160231860159931untbea62sgokunc5s"},{"datatype":"dynamic","width":12,"marks":null,"style":{"color":"rgba(250, 219, 20, 1)","textAlign":"right"},"btnstyle":{},"eleType":"icon","icon":"question-circle","field":"nvarchar2","uuid":"1602318768361nv8ql4t47sgcsn88b0u"},{"datatype":"static","width":24,"marks":null,"height":1,"innerHeight":36,"value":"100","style":{"fontSize":"24px","fontWeight":"500","color":"rgba(0, 0, 0, 1)"},"prefix":"","btnstyle":{},"postfix":"","format":"","eleType":"text","uuid":"1602318817884v70gtgb65ubnm8mbcvv"},{"color":"#1890ff","width":24,"marks":null,"maxValue":100,"style":{"color":"rgba(250, 140, 22, 1)","paddingTop":"20px","paddingBottom":"10px"},"btnstyle":{},"eleType":"slider","field":"int1","uuid":"16023188871233rkktuvpp1h077igrsu"},{"eleType":"splitline","width":24,"color":"#e8e8e8","uuid":"1602320017038n31bk9o831ggug0tu0b","marks":null,"style":{"marginTop":"10px","marginBottom":"10px"},"btnstyle":{}},{"datatype":"static","width":12,"marks":null,"height":1,"value":"100","style":{"marginTop":"6px"},"prefix":"关单","btnstyle":{},"postfix":"","format":"","eleType":"text","uuid":"1602320061243drd7lf3agvn04kgr175"}],"backElements":[]}]` },
  { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '属性卡', config: `[{"uuid":"1603681387259qaqf1127f72esmtchge","setting":{"width":6,"type":"simple"},"style":{"paddingTop":"15px","marginTop":"8px","paddingRight":"15px","marginRight":"8px","marginLeft":"8px","borderColor":"#e8e8e8","paddingLeft":"15px","marginBottom":"8px","borderWidth":"1px","paddingBottom":"15px"},"backStyle":{},"elements":[{"datatype":"static","width":12,"marks":null,"height":1,"value":"超时工单","style":{"color":"rgba(67, 67, 67, 0.51)"},"prefix":"","postfix":"","format":"","eleType":"text","uuid":"1603681402945qnkgm7q8cng65evn5ev"},{"eleType":"icon","datatype":"static","width":12,"icon":"question-circle","tooltip":"超时工单","uuid":"1603681473384i2crkbtofg4pu76k06a","marks":null,"style":{"textAlign":"right","color":"rgba(250, 219, 20, 1)"}},{"datatype":"static","width":24,"marks":null,"height":1,"innerHeight":36,"value":"100","style":{"fontSize":"24px","color":"rgba(0, 0, 0, 1)"},"prefix":"","postfix":"","format":"","eleType":"number","uuid":"1603681539870d704ufqf98kc6t7537t"},{"color":"rgba(250, 219, 20, 1)","datatype":"static","width":24,"marks":null,"maxValue":100,"value":50,"style":{"paddingTop":"10px","paddingBottom":"10px"},"eleType":"slider","uuid":"1603683067556mvupau0odvrtv45u7o8"},{"eleType":"splitline","width":24,"color":"#e8e8e8","uuid":"1603683117981t9k55k8an430fuppmci","marks":null,"style":{"paddingTop":"5px","paddingBottom":"5px"}},{"datatype":"static","width":12,"marks":null,"height":1,"value":"100","style":{"color":"rgba(0, 0, 0, 0.65)","marginTop":"10px"},"prefix":"超时工单  ","postfix":"","format":"","eleType":"text","uuid":"1603683136553uvsmkfohkft9idbfkhu"}],"backElements":[]}]` },
  { type: 'menu', url: line, component: 'line', subtype: 'line', title: '折线图' },
src/menu/searchcomponent/dragsearch/card.jsx
@@ -8,7 +8,7 @@
const { MonthPicker, WeekPicker, RangePicker } = DatePicker
const Card = ({ id, cardIds, card, moveCard, findCard, copyCard, editCard, delCard }) => {
const Card = ({ id, card, moveCard, findCard, copyCard, editCard, delCard }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'search', id, originalIndex },
@@ -21,12 +21,14 @@
    canDrop: () => true,
    drop: () => {},
    hover({ id: draggedId }) {
      if (!draggedId) return
      if (!cardIds.includes(draggedId)) return
      if (draggedId !== id) {
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
      if (!draggedId || draggedId === id) return
      const { index: originIndex } = findCard(draggedId)
      if (originIndex === -1) return
      const { index: overIndex } = findCard(id)
      moveCard(draggedId, overIndex)
    },
  })
  const opacity = isDragging ? 0 : 1
src/menu/searchcomponent/dragsearch/index.jsx
@@ -39,7 +39,6 @@
    copycard.uuid = Utils.getuuid()
    copycard.origin = false
    copycard.copyType = 'search'
    copycard.label = copycard.label + '(copy)'
    copycard.focus = true
    let _val = fromJS(copycard).toJS()
@@ -73,41 +72,10 @@
    deleteMenu(card)
  }
  let cardIds = cards.map(card => card.uuid)
  const [, drop] = useDrop({
    accept: 'search',
    drop() {}
  })
  // const addsearch = (e) => {
  //   e.stopPropagation()
  //   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 = '='
  //   let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
  //   const { index: overIndex } = findCard(`${targetId}`)
  //   let targetIndex = overIndex
  //   targetIndex++
  //   const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
  //   handleList(_cards, newcard)
  // }
  return (
    <div ref={drop} className="ant-row">
@@ -115,7 +83,6 @@
        <Col key={card.uuid} span={card.ratio || 6}>
          <Card
            id={`${card.uuid}`}
            cardIds={cardIds}
            card={card}
            moveCard={moveCard}
            editCard={editCard}
@@ -125,7 +92,6 @@
          />
        </Col>
      ))}
      {/* <Icon type="plus" onClick={addsearch}/> */}
    </div>
  )
}
src/tabviews/commontable/index.jsx
@@ -28,9 +28,6 @@
const CardComponent = asyncSpinComponent(() => import('@/tabviews/zshare/cardcomponent'))
const ChartComponent = asyncSpinComponent(() => import('@/tabviews/zshare/chartcomponent'))
// 自定义标签
const SecretKeyTable = asyncSpinComponent(() => import('./secretKeyTable'))
const { TabPane } = Tabs
const { TreeNode } = Tree
const { Paragraph } = Typography
@@ -142,18 +139,6 @@
      }
      // 去除空行标签
      config.tabgroups = config.tabgroups.filter(group => group.sublist.length > 0)
      // HS下自定义处理的标签
      if (this.props.menuType === 'HS') {
        config.tabgroups.forEach(group => {
          group.sublist = group.sublist.map(tab => {
            if (tab.linkTab === '1586577325055l2ng7t75g7i4ek2ng8o') {
              tab.type = 'SecretKeyTable'
            }
            return tab
          })
        })
      }
      // 视图权限
      config.charts = config.charts.filter(item => {
@@ -504,6 +489,15 @@
    const { setting, arr_field, BIDs, search, orderBy, BID, pageIndex, pageSize } = this.state
    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
    this.setState({
      selectedData: [],
      BIDs: {
        ...BIDs,
        mainTable: '',
        mainTabledata: ''
      }
    })
    if (requireFields.length > 0) {
      let labels = requireFields.map(item => item.label)
      labels = Array.from(new Set(labels))
@@ -517,13 +511,7 @@
    }
    this.setState({
      selectedData: [],
      loading: true,
      BIDs: {
        ...BIDs,
        mainTable: '',
        mainTabledata: ''
      }
      loading: true
    })
    let _orderBy = orderBy || setting.order
@@ -999,31 +987,19 @@
                      {_tab.label}
                    </span>
                  } key={_tab.uuid}>
                    {_tab.type === 'SubTable' ?
                      <SubTable
                        Tab={_tab}
                        MenuID={_tab.linkTab}
                        mainSearch={_tab.searchPass === 'true' ? search : null}
                        userConfig={userConfig ? userConfig[_tab.uuid] : null}
                        SupMenuID={this.props.MenuID}
                        refreshtabs={this.state.refreshtabs}
                        ContainerId={this.state.ContainerId}
                        BID={this.state.BIDs[_tab.supMenu] || ''}
                        BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
                        handleTableId={this.handleTableId}
                        handleMainTable={(type) => this.handleMainTable(type, _tab)}
                      /> : null}
                    {_tab.type === 'SecretKeyTable' ?
                      <SecretKeyTable
                        Tab={_tab}
                        MenuID={_tab.linkTab}
                        SupMenuID={this.props.MenuID}
                        refreshtabs={this.state.refreshtabs}
                        ContainerId={this.state.ContainerId}
                        BID={this.state.BIDs[_tab.supMenu] || ''}
                        BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
                        handleMainTable={(type) => this.handleMainTable(type, _tab)}
                      /> : null}
                    <SubTable
                      Tab={_tab}
                      MenuID={_tab.linkTab}
                      mainSearch={_tab.searchPass === 'true' ? search : null}
                      userConfig={userConfig ? userConfig[_tab.uuid] : null}
                      SupMenuID={this.props.MenuID}
                      refreshtabs={this.state.refreshtabs}
                      ContainerId={this.state.ContainerId}
                      BID={this.state.BIDs[_tab.supMenu] || ''}
                      BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
                      handleTableId={this.handleTableId}
                      handleMainTable={(type) => this.handleMainTable(type, _tab)}
                    />
                  </TabPane>
                )
              })}
src/tabviews/custom/components/card/cardcellList/index.scss
@@ -62,7 +62,6 @@
    position: relative;
    height: 12px;
    padding: 3px 0;
    cursor: pointer;
    touch-action: none;
    .ant-mk-slider-track {
@@ -89,7 +88,6 @@
      background-color: #fff;
      border: solid 2px #91d5ff;
      border-radius: 50%;
      cursor: pointer;
      transition: border-color 0.3s, box-shadow 0.6s, transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28), -webkit-box-shadow 0.6s, -webkit-transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
    }
  }
src/tabviews/subtabtable/index.jsx
@@ -28,7 +28,7 @@
class SubTabModalTable extends Component {
  static propTpyes = {
    type: PropTypes.any,             // 类型,calendar需特殊处理
    Tab: PropTypes.any,              // 日历标签信息
    Tab: PropTypes.any,              // 日历标签信息或标签按钮信息
    BID: PropTypes.string,           // 上级数据ID
    BData: PropTypes.any,            // 上级数据
    MenuID: PropTypes.string,        // 菜单Id
src/tabviews/zshare/actionList/popupbutton/index.jsx
@@ -163,10 +163,11 @@
          destroyOnClose
        >
          <SubTabTable
            Tab={btn}
            MenuID={btn.linkTab}
            BID={popData ? primaryId : this.props.BID}
            BData={popData || this.props.BData}
            SupMenuID={this.props.MenuID}
            MenuID={btn.linkTab}
            refreshSupView={this.reloadtable}
          />
        </Modal>
src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx
@@ -47,7 +47,6 @@
    copycard.uuid = Utils.getuuid()
    copycard.origin = false
    copycard.copyType = 'search'
    copycard.label = copycard.label + '(copy)'
    copycard.focus = true
    let _val = fromJS(copycard).toJS()