king
2022-10-25 5891206952e2ff63e87aed2f47df5324b019d32e
2022-10-25
50个文件已修改
4个文件已添加
1962 ■■■■■ 已修改文件
src/menu/components/card/cardcellcomponent/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/simple-form/options.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/step-form/options.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/formconfig.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/base-table/columns/editColumn/formconfig.jsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/base-table/columns/editColumn/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/base-table/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/base-table/options.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/edit-table/columns/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/edit-table/index.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/table-tabs/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/table-tabs/index.scss 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/table-tabs/options.jsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/controller.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulecell/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/index.jsx 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/tableshell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/controller.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/basetable/index.jsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.jsx 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/normalTable/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/index.jsx 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/treepageconfig/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-custom.js 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/imdesign/index.jsx 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/popview/index.jsx 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/popview/index.scss 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/popview/menuform/index.jsx 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/popview/menuform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/popdesign/index.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tabledesign/index.jsx 280 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tabledesign/index.scss 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tabledesign/source.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx
@@ -505,7 +505,6 @@
      profVisible: true,
      card: element
    })
    MKEmitter.emit('modalStatus', '验证信息')
  }
  /**
@@ -529,8 +528,6 @@
      }, () => {
        this.props.updateElement(_elements)
      })
      MKEmitter.emit('modalStatus', false)
    })
  }
@@ -563,18 +560,18 @@
    }
  }
  handleSave = (_cards, btn, modal) => {
  handleSave = (componentId, btnId, modal) => {
    const { cards } = this.props
    const { elements } = this.state
    if (cards.uuid !== _cards.uuid) return
    if (cards.uuid !== componentId) return
    
    let _index = elements.findIndex(cell => cell.uuid === btn.uuid)
    let _index = elements.findIndex(cell => cell.uuid === btnId)
    if (_index === -1) return
    let _elements = elements.map(cell => {
      if (cell.uuid === btn.uuid) {
      if (cell.uuid === btnId) {
        cell.modal = modal
      }
@@ -751,11 +748,9 @@
              if (this.verifyRef.handleCancel) {
                this.verifyRef.handleCancel().then(() => {
                  this.setState({ profVisible: false })
                  MKEmitter.emit('modalStatus', false)
                })
              } else {
                this.setState({ profVisible: false })
                MKEmitter.emit('modalStatus', false)
              }
            }}
            destroyOnClose
src/menu/components/card/data-card/index.jsx
@@ -140,10 +140,6 @@
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitModal', this.handleSave)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
@@ -155,7 +151,6 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitModal', this.handleSave)
  }
  /**
@@ -401,26 +396,6 @@
    } else if (btn.OpenType === 'popview' && appType !== 'mob') {
      MKEmitter.emit('changePopview', card, btn)
    }
  }
  handleSave = (_cards, btn, modal) => {
    let card = fromJS(this.state.card).toJS()
    if (card.uuid !== _cards.uuid) return
    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
    if (_index === -1) return
    card.action = card.action.map(cell => {
      if (cell.uuid === btn.uuid) {
        cell.modal = modal
      }
      return cell
    })
    this.updateComponent(card)
  }
  addCard = (copy) => {
src/menu/components/form/formaction/index.jsx
@@ -157,7 +157,6 @@
    this.setState({
      profVisible: true
    })
    MKEmitter.emit('modalStatus', '验证信息')
  }
  /**
@@ -173,7 +172,6 @@
        profVisible: false
      })
      this.props.updateconfig(group)
      MKEmitter.emit('modalStatus', false)
    })
  }
@@ -256,11 +254,9 @@
            if (this.verifyRef.handleCancel) {
              this.verifyRef.handleCancel().then(() => {
                this.setState({ profVisible: false })
                MKEmitter.emit('modalStatus', false)
              })
            } else {
              this.setState({ profVisible: false })
              MKEmitter.emit('modalStatus', false)
            }
          }}
          destroyOnClose
src/menu/components/form/simple-form/options.jsx
@@ -140,6 +140,19 @@
    },
    {
      type: 'radio',
      field: 'goback',
      label: '空值返回',
      initval: wrap.goback || 'false',
      tooltip: '当查询数据为空时,返回上一界面。',
      required: false,
      options: [
        {value: 'true', label: '是'},
        {value: 'false', label: '否'},
      ],
      forbid: appType !== 'mob'
    },
    {
      type: 'radio',
      field: 'permission',
      label: '权限验证',
      initval: wrap.permission || 'false',
src/menu/components/form/step-form/options.jsx
@@ -103,6 +103,19 @@
    },
    {
      type: 'radio',
      field: 'goback',
      label: '空值返回',
      initval: wrap.goback || 'false',
      tooltip: '当查询数据为空时,返回上一界面。',
      required: false,
      options: [
        {value: 'true', label: '是'},
        {value: 'false', label: '否'},
      ],
      forbid: appType !== 'mob'
    },
    {
      type: 'radio',
      field: 'permission',
      label: '权限验证',
      initval: wrap.permission || 'false',
src/menu/components/share/actioncomponent/formconfig.jsx
@@ -1068,6 +1068,8 @@
  ]
  let pageTemps = [
    // { value: 'print', text: '标签打印模板' },
    // { value: 'billprintTemp', text: '单据打印模板' },
    { value: 'billprint', text: '单据打印' },
    { value: 'pay', text: '支付' },
    { value: 'custom', text: '自定义' }
src/menu/components/share/actioncomponent/index.jsx
@@ -60,17 +60,9 @@
  }
  componentDidMount () {
    MKEmitter.addListener('revert', this.revert)
    MKEmitter.addListener('addButton', this.addButton)
  }
  /**
   * @description 监听到按钮复制时,触发按钮编辑
   */
  UNSAFE_componentWillReceiveProps (nextProps) {
    const { actionlist } = this.state
    if (!is(fromJS(nextProps.config.action), fromJS(this.props.config.action)) && !is(fromJS(nextProps.config.action), fromJS(actionlist))) {
      this.setState({actionlist: fromJS(nextProps.config.action).toJS()})
    }
    MKEmitter.addListener('submitModal', this.handleSave)
  }
  shouldComponentUpdate (nextProps, nextState) {
@@ -84,7 +76,41 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('revert', this.revert)
    MKEmitter.removeListener('addButton', this.addButton)
    MKEmitter.removeListener('submitModal', this.handleSave)
  }
  revert = () => {
    this.setState({
      actionlist: fromJS(this.props.config.action).toJS()
    })
  }
  handleSave = (componentId, btnId, modal) => {
    const { config } = this.props
    if (config.uuid !== componentId) return
    const { actionlist } = this.state
    let _index = actionlist.findIndex(cell => cell.uuid === btnId)
    if (_index === -1) return
    let _actionlist = actionlist.map(cell => {
      if (cell.uuid === btnId) {
        cell.modal = modal
      }
      return cell
    })
    this.setState({
      actionlist: _actionlist
    }, () => {
      this.props.updateaction({...config, action: _actionlist})
    })
  }
  getStyle = (style) => {
@@ -323,7 +349,6 @@
      profVisible: true,
      card: element
    })
    MKEmitter.emit('modalStatus', '验证信息')
  }
  /**
@@ -351,7 +376,6 @@
      }, () => {
        this.props.updateaction({...config, action: _actionlist})
      })
      MKEmitter.emit('modalStatus', false)
    })
  }
@@ -555,11 +579,9 @@
            if (this.verifyRef.handleCancel) {
              this.verifyRef.handleCancel().then(() => {
                this.setState({ profVisible: false })
                MKEmitter.emit('modalStatus', false)
              })
            } else {
              this.setState({ profVisible: false })
              MKEmitter.emit('modalStatus', false)
            }
          }}
          destroyOnClose
src/menu/components/table/base-table/columns/editColumn/formconfig.jsx
@@ -36,6 +36,9 @@
    value: 'picture',
    text: '图片'
  }, {
    value: 'video',
    text: '视频'
  }, {
    value: 'link',
    text: '链接'
  }, {
@@ -163,6 +166,26 @@
      }]
    },
    {
      type: 'number',
      key: 'startTime',
      precision: 0,
      label: '开始时间',
      initVal: card.startTime || 0,
      tooltip: '视频开始播放的时间,用于调整视频初始化展示的界面。',
      required: false
    },
    {
      type: 'select',
      key: 'aspectRatio',
      label: '长宽比',
      initVal: card.aspectRatio || '16:9',
      required: true,
      options: [
        { value: '4:3', text: '4:3' },
        { value: '16:9', text: '16:9' }
      ]
    },
    {
      type: 'radio',
      key: 'rowspan',
      label: '行合并',
src/menu/components/table/base-table/columns/editColumn/index.jsx
@@ -10,11 +10,12 @@
const { TextArea } = Input
const columnTypeOptions = {
  text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'blacklist', 'perspective', 'rowspan'],
  text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'fieldlength', 'blacklist', 'perspective', 'rowspan'],
  number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum', 'rowspan'],
  link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'blacklist', 'nameField'],
  textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'blacklist'],
  picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'blacklist', 'scale', 'lenWidRadio', 'span'],
  link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'fieldlength', 'blacklist', 'nameField'],
  textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'fieldlength', 'prefix', 'postfix', 'blacklist'],
  picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'fieldlength', 'blacklist', 'scale', 'lenWidRadio', 'span'],
  video: ['label', 'field', 'type', 'Align', 'Hide', 'startTime', 'Width', 'fieldlength', 'blacklist', 'aspectRatio'],
  colspan: ['label', 'type', 'Align', 'Hide', 'blacklist'],
  custom: ['label', 'type', 'Align', 'Hide', 'Width', 'blacklist'],
  action: ['label', 'type', 'Align', 'Width'],
src/menu/components/table/base-table/index.jsx
@@ -78,7 +78,6 @@
  }
  componentDidMount () {
    MKEmitter.addListener('submitModal', this.handleSave)
    MKEmitter.addListener('completeSave', this.completeSave)
  }
@@ -93,7 +92,6 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitModal', this.handleSave)
    MKEmitter.removeListener('completeSave', this.completeSave)
  }
@@ -262,26 +260,6 @@
    }
  }
  handleSave = (_cards, btn, modal) => {
    let card = fromJS(this.state.card).toJS()
    if (card.uuid !== _cards.uuid) return
    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
    if (_index === -1) return
    card.action = card.action.map(cell => {
      if (cell.uuid === btn.uuid) {
        cell.modal = modal
      }
      return cell
    })
    this.updateComponent(card)
  }
  getWrapForms = () => {
    const { wrap, action, columns, cols } = this.state.card
@@ -317,7 +295,7 @@
    config.absFields = []
    config.cols.forEach(col => {
      if (!col.field) return
      if (['text', 'picture', 'textarea'].includes(col.type)) {
      if (['text', 'picture', 'video', 'textarea'].includes(col.type)) {
        config.columns.push({
          datatype: `Nvarchar(${col.fieldlength || 50})`,
          field: col.field,
src/menu/components/table/base-table/options.jsx
@@ -72,7 +72,7 @@
      field: 'tableMode',
      label: '加载模式',
      initval: wrap.tableMode || 'compatible',
      tooltip: '使用急速模式时,表格中的标记、双击事件、格式化、行合并、前缀、后缀、字段透视等效果将无效,且数据都会以文本格式显示。',
      tooltip: '使用急速模式时,表格中的标记、双击事件、格式化、行合并、列合并、前缀、后缀、字段透视等效果将无效,且数据都会以文本格式显示。',
      required: false,
      options: [
        {value: 'compatible', label: '兼容'},
src/menu/components/table/edit-table/columns/index.jsx
@@ -262,14 +262,12 @@
    let _columns = fromJS(this.state.columns).toJS()
    let type = item.subType
    if (item.subType === 'link' || item.subType === 'colspan' || item.subType === 'picture') {
    if (!['text', 'number', 'textarea', 'custom', 'action', 'formula', 'index'].includes(item.subType)) {
      type = 'text'
    }
    let col = { focus: true, uuid: Utils.getuuid(), label: 'label', field: '', type: type, elements: [] }
    if (col.type === 'colspan') {
      col.subcols = []
    } else if (col.type === 'action') {
    if (col.type === 'action') {
      col.label = '操作'
    } else if (col.type === 'index') {
      col.label = '序号'
src/menu/components/table/edit-table/index.jsx
@@ -119,7 +119,6 @@
  }
  componentDidMount () {
    MKEmitter.addListener('submitModal', this.handleSave)
    MKEmitter.addListener('completeSave', this.completeSave)
  }
@@ -134,7 +133,6 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitModal', this.handleSave)
    MKEmitter.removeListener('completeSave', this.completeSave)
  }
@@ -314,26 +312,6 @@
    } else if (btn.OpenType === 'popview') {
      MKEmitter.emit('changePopview', card, btn)
    }
  }
  handleSave = (_cards, btn, modal) => {
    let card = fromJS(this.state.card).toJS()
    if (card.uuid !== _cards.uuid) return
    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
    if (_index === -1) return
    card.action = card.action.map(cell => {
      if (cell.uuid === btn.uuid) {
        cell.modal = modal
      }
      return cell
    })
    this.updateComponent(card)
  }
  getWrapForms = () => {
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
@@ -43,6 +43,9 @@
    value: 'picture',
    text: '图片'
  }, {
    value: 'video',
    text: '视频'
  }, {
    value: 'link',
    text: '链接'
  }, {
@@ -175,6 +178,26 @@
      }]
    },
    {
      type: 'number',
      key: 'startTime',
      precision: 0,
      label: '开始时间',
      initVal: card.startTime || 0,
      tooltip: '视频开始播放的时间,用于调整视频初始化展示的界面。',
      required: false
    },
    {
      type: 'select',
      key: 'aspectRatio',
      label: '长宽比',
      initVal: card.aspectRatio || '16:9',
      required: true,
      options: [
        { value: '4:3', text: '4:3' },
        { value: '16:9', text: '16:9' }
      ]
    },
    {
      type: 'radio',
      key: 'rowspan',
      label: '行合并',
src/menu/components/table/normal-table/columns/editColumn/index.jsx
@@ -15,6 +15,7 @@
  link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'blacklist', 'nameField'],
  textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'blacklist'],
  picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'blacklist', 'scale', 'lenWidRadio', 'span'],
  video: ['label', 'field', 'type', 'Align', 'Hide', 'startTime', 'Width', 'blacklist', 'aspectRatio'],
  colspan: ['label', 'type', 'Align', 'Hide', 'blacklist'],
  custom: ['label', 'type', 'Align', 'Hide', 'Width', 'blacklist'],
  action: ['label', 'type', 'Align', 'Width'],
src/menu/components/table/normal-table/index.jsx
@@ -138,7 +138,6 @@
  }
  componentDidMount () {
    MKEmitter.addListener('submitModal', this.handleSave)
    MKEmitter.addListener('completeSave', this.completeSave)
  }
@@ -153,7 +152,6 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitModal', this.handleSave)
    MKEmitter.removeListener('completeSave', this.completeSave)
  }
@@ -366,26 +364,6 @@
    } else if (btn.OpenType === 'popview') {
      MKEmitter.emit('changePopview', card, btn)
    }
  }
  handleSave = (_cards, btn, modal) => {
    let card = fromJS(this.state.card).toJS()
    if (card.uuid !== _cards.uuid) return
    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
    if (_index === -1) return
    card.action = card.action.map(cell => {
      if (cell.uuid === btn.uuid) {
        cell.modal = modal
      }
      return cell
    })
    this.updateComponent(card)
  }
  getWrapForms = () => {
src/menu/components/tabs/table-tabs/index.jsx
@@ -188,7 +188,7 @@
    editab.label = res.label
    editab.icon = res.icon
    editab.hide = res.hide || 'false'
    editab.blacklist = res.blacklist
    editab.permission = res.permission || 'false'
    editab.components[0].name = res.label
    if (editab.uuid) {
@@ -225,7 +225,7 @@
    const { tabs } = this.state
    return (
      <div className="menu-tabs-edit-box" style={tabs.style} id={tabs.uuid}>
      <div className="table-tabs-edit-box" style={tabs.style} id={tabs.uuid}>
        <DraggableTabs tabsMove={this.moveSwitch} tabsDrop={this.dropTable}>
          {tabs.subtabs.map((tab, i) => (
            <TabPane tab={
src/menu/components/tabs/table-tabs/index.scss
@@ -1,11 +1,4 @@
.menu-tabs-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
.table-tabs-edit-box {
  .ant-tabs-top-bar {
    margin-bottom: 0;
  }
src/menu/components/tabs/table-tabs/options.jsx
@@ -2,18 +2,6 @@
 * @description tab表单配置信息
 */
export function getTabForm(tab) {
  let roleList = sessionStorage.getItem('sysRoles')
  if (roleList) {
    try {
      roleList = JSON.parse(roleList)
    } catch (e) {
      roleList = []
    }
  } else {
    roleList = []
  }
  const tabForm = [
    {
      type: 'text',
@@ -43,13 +31,16 @@
      ],
    },
    {
      type: 'multiselect',
      field: 'blacklist',
      label: '黑名单',
      initval: tab.blacklist || [],
      type: 'radio',
      field: 'permission',
      label: '权限验证',
      initval: tab.permission || 'false',
      required: false,
      options: roleList
    },
      options: [
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ]
    }
  ]
  return tabForm
src/menu/datasource/index.jsx
@@ -5,7 +5,6 @@
import { SettingOutlined } from '@ant-design/icons'
import VerifyCard from './verifycard'
import MKEmitter from '@/utils/events.js'
import './index.scss'
class DataSource extends Component {
@@ -140,8 +139,6 @@
      visible: true,
      mainSearch: search
    })
    MKEmitter.emit('modalStatus', '数据源')
  }
  verifySubmit = () => {
@@ -203,8 +200,6 @@
      this.setState({loading: false, visible: false})
      this.props.updateConfig({...config, ...res})
      MKEmitter.emit('modalStatus', false)
    }, () => {
      this.setState({loading: false})
    })
@@ -226,7 +221,7 @@
          okText="提交"
          onOk={this.verifySubmit}
          confirmLoading={loading}
          onCancel={() => { MKEmitter.emit('modalStatus', false);this.setState({ visible: false }) }}
          onCancel={() => {this.setState({ visible: false }) }}
          destroyOnClose
        >
          <VerifyCard
src/menu/modalconfig/controller.jsx
@@ -47,7 +47,7 @@
  
  handleSave = (modal) => {
    const { config, btn } = this.state
    MKEmitter.emit('submitModal', config, btn, modal)
    MKEmitter.emit('submitModal', config.uuid, btn.uuid, modal)
  }
  render () {
src/menu/modulecell/index.jsx
@@ -92,6 +92,7 @@
          { subType: 'text', text: '文本', type: 'col', $init: true },
          { subType: 'number', text: '数字', type: 'col', $init: true },
          { subType: 'picture', text: '图片', type: 'col', $init: true },
          { subType: 'video', text: '视频', type: 'col', $init: true },
          { subType: 'link', text: '链接', type: 'col', $init: true },
          { subType: 'textarea', text: '多行文本', type: 'col', $init: true },
          { subType: 'custom', text: '自定义列', type: 'col', $init: true },
@@ -139,7 +140,7 @@
    return (
      <div className="mk-source-box">
        <div className="tip">注:当拖动类型不受支持时会被重置。</div>
        <div className="tip">注:当元素类型不受支持时会被重置。</div>
        {options.map((item, index) => (<div className="mk-class" span={item.span} key={index}>
          <div className="title">{item.title}</div>
          {item.children.map(cell => <SourceWrap key={cell.value || cell.subType} item={cell}/>)}
src/menu/sysinterface/index.jsx
@@ -160,8 +160,44 @@
  changeScripts = (interfaces) => {
    const { config } = this.props
    interfaces = interfaces.map(item => {
      item.$tables = this.getTables(item)
      return item
    })
    this.setState({ interfaces })
    this.props.updateConfig({...config, interfaces})
  }
  getTables = (record) => {
    let tables = []
    let cuts = []
    let cutreg = /(from|update|insert\s+into)\s+(@db@)?[a-z_]+/ig
    let trimreg = /(from|update|insert\s+into)\s+(@db@)?/ig
    if (record.setting.interType === 'system') {
      if (record.setting.execute !== 'false') {
        let tbs = record.setting.dataresource.match(cutreg)
        tbs && cuts.push(...tbs)
      }
      record.scripts && record.scripts.forEach(script => {
        if (script.status === 'false') return
        let tbs = script.sql.match(cutreg)
        tbs && cuts.push(...tbs)
      })
    } else {
      let tb = record.setting.tableName.replace(/@db@|\s+/ig, '')
      if (/[a-z_]+/ig.test(tb)) {
        tables.push(tb)
      }
    }
    cuts = cuts.map(item => item.replace(trimreg, ''))
    tables.push(...cuts)
    tables = tables.filter(Boolean)
    tables = Array.from(new Set(tables))
    return tables
  }
  update = (record) => {
@@ -173,6 +209,7 @@
      record.status = 'false'
    }
    record.name = record.setting.name
    record.$tables = this.getTables(record)
    let interfaces = this.state.interfaces.map(item => {
      if (item.uuid !== record.uuid) {
src/menu/tableshell/index.jsx
@@ -95,6 +95,7 @@
    drop(item) {
      if (item.added || item.index) {
        delete item.added // 删除组件添加标记
        delete item.dropTargetId
        return
      }
      
src/mob/modalconfig/controller.jsx
@@ -47,7 +47,7 @@
  
  handleSave = (modal) => {
    const { config, btn } = this.state
    MKEmitter.emit('submitModal', config, btn, modal)
    MKEmitter.emit('submitModal', config.uuid, btn.uuid, modal)
  }
  render () {
src/tabviews/basetable/index.jsx
@@ -238,10 +238,7 @@
      if (item.type === 'tabs') {
        item.subtabs = item.subtabs.filter(tab => {
          if (
            tab.blacklist && tab.blacklist.length > 0 &&
            tab.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
          ) {
          if (!skip && !permAction[tab.components[0].uuid] && tab.permission === 'true') {
            return false
          } else if (tab.hide === 'true') {
            return false
src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -833,6 +833,7 @@
            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
              <TabButton
                btn={card}
                BID={data.$$BID}
                BData={data.$$BData || ''}
                disabled={_disabled}
                selectedData={_data}
src/tabviews/custom/components/share/normalTable/index.jsx
@@ -15,8 +15,9 @@
import './index.scss'
const { Paragraph } = Typography
const CardCellComponent = asyncComponent(() => import('@/tabviews/custom/components/card/cardcellList'))
const Video = asyncComponent(() => import('@/components/video'))
const MkPicture = asyncComponent(() => import('@/components/mkPicture'))
const CardCellComponent = asyncComponent(() => import('@/tabviews/custom/components/card/cardcellList'))
const PicRadio = {
  '4:3': '75%', '3:2': '66.67%', '16:9': '56.25%', '2:1': '50%', '3:1': '33.33%', '4:1': '25%',
  '5:1': '20%', '6:1': '16.67%', '7:1': '14.29%', '8:1': '12.5%', '9:1': '11.11%',
@@ -24,7 +25,7 @@
}
class BodyRow extends React.Component {
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.data), fromJS(nextProps.data)) || !is(fromJS(this.props.className), fromJS(nextProps.className))
    return !is(fromJS(this.props.data), fromJS(nextProps.data)) || this.props.className !== nextProps.className
  }
  render() {
@@ -259,6 +260,14 @@
          ))}
        </div>
      )
    } else if (col.type === 'video') {
      let url = record[col.field] || ''
      resProps.children = (
        <div className="video-wrap">
          {url ? <Video card={col} value={url}/> : null}
        </div>
      )
    } else if (col.type === 'textarea') {
      let content = ''
      if (record[col.field] !== undefined) {
@@ -434,52 +443,69 @@
      }
    }
    let getColumns = (cols) => {
      return cols.map(item => {
        let cell = null
        if (item.type === 'colspan') {
          cell = { title: item.label, align: item.Align }
          cell.children = getColumns(item.subcols)
        } else {
          if (item.rowspan === 'true') {
            rowspans.push(item.field)
          }
          if (item.type === 'index') {
            item.field = '$Index'
            item.type = 'text'
          } else if (_format && !Math.floor(Math.random() * radio)) {
            item.blur = true
          }
          if (item.marks && item.marks.length === 0) {
            item.marks = ''
          }
    let _columns = []
          if (item.field) {
            orderfields[item.uuid] = item.field
          }
          cell = {
            align: item.Align,
            dataIndex: item.uuid,
            title: item.label,
            sorter: item.field && item.IsSort === 'true',
            width: item.Width || 120,
            onCell: record => ({
              record,
              col: item,
              config: item.type === 'custom' || item.type === 'action' ? {setting, columns: fields} : null,
              triggerLink: this.triggerLink
            })
          }
        }
    if (setting.tableMode !== 'fast') {
      let getColumns = (cols) => {
        return cols.map(item => {
          let cell = null
          if (item.type === 'colspan') {
            cell = { title: item.label, align: item.Align }
            cell.children = getColumns(item.subcols)
          } else {
            if (item.rowspan === 'true') {
              rowspans.push(item.field)
            }
            if (item.type === 'index') {
              item.field = '$Index'
              item.type = 'text'
            } else if (_format && !Math.floor(Math.random() * radio)) {
              item.blur = true
            }
            if (item.marks && item.marks.length === 0) {
              item.marks = ''
            }
  
        return cell
            if (item.field) {
              orderfields[item.uuid] = item.field
            }
            cell = {
              align: item.Align,
              dataIndex: item.uuid,
              title: item.label,
              sorter: item.field && item.IsSort === 'true',
              width: item.Width || 120,
              onCell: record => ({
                record,
                col: item,
                config: item.type === 'custom' || item.type === 'action' ? {setting, columns: fields} : null,
                triggerLink: this.triggerLink
              })
            }
          }
          return cell
        })
      }
      _columns = getColumns(columns)
    } else {
      let fields = []
      columns.forEach(item => {
        if (!item.field || fields.includes(item.field)) return
        fields.push(item.field)
        _columns.push({
          align: item.Align,
          dataIndex: item.field,
          title: item.label,
          sorter: item.IsSort === 'true',
          width: item.Width || 120
        })
      })
    }
    let _columns = getColumns(columns)
    if (rowspans.length === 0) {
      rowspans = null
@@ -493,13 +519,6 @@
      }
      return uuid.join('')
    }) ()
    // if (setting.borderColor) { // 边框颜色
    //   let style = `#${tableId} table, #${tableId} tr, #${tableId} th, #${tableId} td {border-color: ${setting.borderColor}}`
    //   let ele = document.createElement('style')
    //   ele.innerHTML = style
    //   document.getElementsByTagName('head')[0].appendChild(ele)
    // }
    let size = (setting.pageSize || 10) + ''
    let pageOptions = ['10', '25', '50', '100', '500', '1000']
@@ -856,11 +875,14 @@
      }
    }
    const components = {
    let components = {
      body: {
        row: BodyRow,
        cell: BodyCell
        row: BodyRow
      }
    }
    if (setting.tableMode !== 'fast') {
      components.body.cell = BodyCell
    }
    // 数据收起时,过滤已选数据
@@ -899,7 +921,6 @@
        }
        <Table
          components={components}
          // style={setting.style}
          size={setting.size || 'middle'}
          bordered={setting.bordered !== 'false'}
          rowSelection={rowSelection}
@@ -909,7 +930,7 @@
          scroll={{ x: '100%', y: height }}
          onRow={(record, index) => {
            return {
              lineMarks,
              lineMarks: setting.tableMode !== 'fast' ? lineMarks : null,
              data: record,
              className: index === activeIndex ? ' mk-row-active ' : '',
              onClick: () => {this.changeRow(record, index)},
src/tabviews/custom/components/share/normalTable/index.scss
@@ -100,6 +100,11 @@
          position: relative;
          margin: 2px;
        }
        .video-wrap {
          .video-react-playback-rate {
            display: none;
          }
        }
        .action-col {
          .ant-btn > .anticon + span {
            margin-left: 3px;
src/tabviews/zshare/actionList/index.jsx
@@ -99,6 +99,7 @@
            show={item.show || 'actionList'}
            disabled={lock || false}
            btn={item}
            BID={BID}
            BData={BData}
            MenuID={MenuID}
            selectedData={selectedData}
src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -12,6 +12,7 @@
class TabButton extends Component {
  static propTpyes = {
    BData: PropTypes.any,             // 主表数据
    BID: PropTypes.any,
    MenuID: PropTypes.string,         // 菜单ID
    btn: PropTypes.object,            // 按钮
    selectedData: PropTypes.any,      // 子表中选择数据
@@ -108,7 +109,7 @@
   * @description 触发按钮操作
   */
  actionTrigger = (triggerId, record, type) => {
    const { btn, selectedData } = this.props
    const { btn, selectedData, BID } = this.props
    const { disabled } = this.state
    if (disabled) return
@@ -134,8 +135,8 @@
    if (btn.Ot === 'requiredSgl') {
      primaryId = data[0].$$uuid || ''
    } else if (btn.Ot === 'notRequired' && data[0]) {
      primaryId = data[0].$$BID || ''
    } else if (btn.Ot === 'notRequired' && BID) {
      primaryId = BID
    }
    let newtab = {}
src/tabviews/zshare/normalTable/index.jsx
@@ -780,6 +780,7 @@
                  btn={btn}
                  disabled={record.$disabled}
                  selectedData={[record]}
                  BID={record.$$BID}
                  BData={this.props.BData}
                  MenuID={this.props.MenuID}
                />
src/templates/calendarconfig/index.jsx
@@ -52,7 +52,6 @@
    activeKey: '0',          // 默认展开基本信息
    openEdition: '',         // 编辑版本标记,防止多人操作
    mockdata: [],            // 测试数据
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  /**
@@ -126,10 +125,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -142,7 +154,6 @@
        return false
      }
    }
    MKEmitter.addListener('modalStatus', this.modalStatus)
  }
  getMockData = (year) => {
@@ -245,11 +256,6 @@
      return
    }
    document.onkeydown = () => {}
    MKEmitter.removeListener('modalStatus', this.modalStatus)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  // 页面返回
src/templates/comtableconfig/index.jsx
@@ -61,7 +61,6 @@
    activeKey: '0',          // 默认展开基本信息
    chartview: null,         // 当前视图
    openEdition: '',         // 编辑版本标记,防止多人操作
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  /**
@@ -163,10 +162,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -179,8 +191,6 @@
        return false
      }
    }
    MKEmitter.addListener('modalStatus', this.modalStatus)
  }
  /**
@@ -191,11 +201,6 @@
      return
    }
    document.onkeydown = () => {}
    MKEmitter.removeListener('modalStatus', this.modalStatus)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  /**
src/templates/sharecomponent/actioncomponent/index.jsx
@@ -674,8 +674,6 @@
      profVisible: true,
      card: element
    })
    MKEmitter.emit('modalStatus', '验证信息')
  }
  /**
@@ -703,8 +701,6 @@
      }, () => {
        this.props.updateaction({...config, action: _actionlist})
      })
      MKEmitter.emit('modalStatus', false)
    })
  }
@@ -950,11 +946,9 @@
            if (this.verifyRef.handleCancel) {
              this.verifyRef.handleCancel().then(() => {
                this.setState({ profVisible: false })
                MKEmitter.emit('modalStatus', false)
              })
            } else {
              this.setState({ profVisible: false })
              MKEmitter.emit('modalStatus', false)
            }
          }}
          destroyOnClose
src/templates/sharecomponent/settingcalcomponent/index.jsx
@@ -5,7 +5,6 @@
import { SettingOutlined } from '@ant-design/icons'
import VerifyCard from './verifycard'
import MKEmitter from '@/utils/events.js'
import './index.scss'
class DataSource extends Component {
@@ -36,7 +35,6 @@
    this.setState({
      visible: true
    })
    MKEmitter.emit('modalStatus', '数据源')
  }
  verifySubmit = () => {
@@ -65,8 +63,6 @@
      this.setState({loading: false, visible: false})
      this.props.updateConfig({...config, ...res})
      MKEmitter.emit('modalStatus', false)
    }, () => {
      this.setState({loading: false})
    })
@@ -87,7 +83,7 @@
          okText="提交"
          onOk={this.verifySubmit}
          confirmLoading={loading}
          onCancel={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false }) }}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <VerifyCard
src/templates/sharecomponent/settingcomponent/index.jsx
@@ -5,7 +5,6 @@
import { SettingOutlined } from '@ant-design/icons'
import Utils, { FuncUtils } from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import SettingForm from './settingform'
import CreateFunc from '@/templates/zshare/createfunc'
import CreateInterface from '@/templates/zshare/createinterface'
@@ -48,8 +47,6 @@
      search: _search,
      menu: menu
    })
    MKEmitter.emit('modalStatus', '数据源')
  }
  /**
@@ -70,8 +67,6 @@
      res.actionfixed = res.actionfixed === 'true'
      this.props.updatesetting({...config, setting: res})
      MKEmitter.emit('modalStatus', false)
    }, () => {
      this.setState({
        loading: false
@@ -195,11 +190,11 @@
          visible={visible}
          width={'75vw'}
          maskClosable={false}
          onCancel={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false, loading: false })}}
          onCancel={() => { this.setState({ visible: false, loading: false })}}
          footer={[
            record && record.interType === 'system' ? <CreateInterface key="interface" loading={this.state.interloading} ref="tableCreatInterface" trigger={this.tableCreatInterface}/> : null,
            record && record.interType === 'inner' ? <CreateFunc key="create" ref="funcCreatComponent" trigger={this.tableCreatFunc}/> : null,
            <Button key="cancel" onClick={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false, loading: false }) }}>取消</Button>,
            <Button key="cancel" onClick={() => { this.setState({ visible: false, loading: false }) }}>取消</Button>,
            <Button key="confirm" type="primary" loading={this.state.loading} onClick={this.settingSave}>确定</Button>
          ]}
          destroyOnClose
src/templates/sharecomponent/treesettingcomponent/index.jsx
@@ -4,7 +4,6 @@
import { SettingOutlined } from '@ant-design/icons'
import SettingForm from './settingform'
import MKEmitter from '@/utils/events.js'
import './index.scss'
@@ -32,7 +31,6 @@
      visible: true,
      menu: menu
    })
    MKEmitter.emit('modalStatus', '数据源')
  }
  /**
@@ -69,8 +67,6 @@
      res.searchLwidth = config.setting.searchLwidth !== undefined ? config.setting.searchLwidth : 33.3
      this.props.updatesetting({...config, setting: res})
      MKEmitter.emit('modalStatus', false)
    }, () => {
      this.setState({
        loading: false
@@ -100,7 +96,7 @@
          visible={visible}
          width={'75vw'}
          maskClosable={false}
          onCancel={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false })}}
          onCancel={() => { this.setState({ visible: false })}}
          confirmLoading={loading}
          onOk={this.settingSave}
          destroyOnClose
src/templates/subtableconfig/index.jsx
@@ -63,7 +63,6 @@
    activeKey: '0',          // 默认展开基本信息
    chartview: null,         // 当前视图
    openEdition: '',         // 编辑版本标记,防止多人操作
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  /**
@@ -149,10 +148,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -165,7 +177,6 @@
        return false
      }
    }
    MKEmitter.addListener('modalStatus', this.modalStatus)
  }
  /**
@@ -226,11 +237,6 @@
      return
    }
    document.onkeydown = () => {}
    MKEmitter.removeListener('modalStatus', this.modalStatus)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  // 页面返回
src/templates/treepageconfig/index.jsx
@@ -10,7 +10,6 @@
import Api from '@/api'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import MenuForm from '@/templates/comtableconfig/menuform'
import SourceElement from '@/templates/zshare/dragsource'
@@ -43,7 +42,6 @@
    tabviews: [],            // 所有标签页
    activeKey: '0',          // 默认展开基本信息
    openEdition: '',         // 编辑版本标记,防止多人操作
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  /**
@@ -127,10 +125,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -143,7 +154,6 @@
        return false
      }
    }
    MKEmitter.addListener('modalStatus', this.modalStatus)
  }
  /**
@@ -154,11 +164,6 @@
      return
    }
    document.onkeydown = () => {}
    MKEmitter.removeListener('modalStatus', this.modalStatus)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  /**
src/utils/utils-custom.js
@@ -939,7 +939,7 @@
/**
 * @description 获取表名
 */
export function getTables (config) {
export function getTables (config, pops) {
  let tables = []
  let cuts = []
  let cutreg = /(from|update|insert\s+into)\s+(@db@)?[a-z_]+/ig
@@ -973,15 +973,21 @@
  } else if (config.subcards) {
    config.subcards.forEach(item => {
      item.elements.forEach(cell => {
        if (cell.eleType === 'button' && ['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
        if (cell.eleType !== 'button') return
        if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
          action.push(cell)
        } else if (pops && cell.OpenType === 'popview') {
          pops.push({...cell, parentId: config.uuid})
        }
      })
  
      if (item.backElements && item.setting.type === 'multi') {
        item.backElements.forEach(cell => {
          if (cell.eleType === 'button' && ['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
          if (cell.eleType !== 'button') return
          if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
            action.push(cell)
          } else if (pops && cell.OpenType === 'popview') {
            pops.push({...cell, parentId: config.uuid})
          }
        })
      }
@@ -992,20 +998,27 @@
      col.elements.forEach(cell => {
        if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
          action.push(cell)
        } else if (pops && cell.OpenType === 'popview') {
          pops.push({...cell, parentId: config.uuid})
        }
      })
    }
  })
  config.elements && config.elements.forEach(cell => {
    if (cell.eleType === 'button' && ['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
    if (cell.eleType !== 'button') return
    if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
      action.push(cell)
    } else if (pops && cell.OpenType === 'popview') {
      pops.push({...cell, parentId: config.uuid})
    }
  })
  config.action && config.action.forEach(cell => {
    if (['pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
      action.push(cell)
    } else if (pops && cell.OpenType === 'popview') {
      pops.push({...cell, parentId: config.uuid})
    }
  })
src/views/imdesign/index.jsx
@@ -188,41 +188,39 @@
  }
  getAppPictures = () => {
    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
    Api.getSystemConfig({
    if (sessionStorage.getItem('app_pictures')) return
    let deffers = []
    let param = {
      func: 's_url_db_adduptdel',
      PageIndex: 0,  // 0 代表全部
      PageSize: 0,   // 0 代表全部
      typecharone: 'image',
      type: 'search'
    }).then(res => {
      if (res.status) {
        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
      }
    }
    deffers = [new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
          resolve(res.data)
        })
      }, 500)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
          resolve(res.data)
        })
      }, 1000)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
          resolve(res.data)
        })
      }, 1500)
    })]
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'video',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
        }
      })
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'color',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
        }
      })
    Promise.all(deffers).then(response => {
      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
    })
  }
src/views/menudesign/index.jsx
@@ -4,7 +4,7 @@
import { is, fromJS } from 'immutable'
import moment from 'moment'
import HTML5Backend from 'react-dnd-html5-backend'
import { ConfigProvider, notification, Modal, Collapse, Card, Switch, Button, Typography } from 'antd'
import { ConfigProvider, notification, Modal, Collapse, Card, Switch, Button, Typography, Spin } from 'antd'
import { DoubleLeftOutlined, DoubleRightOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'
import html2canvas from 'html2canvas'
import md5 from 'md5'
@@ -12,10 +12,9 @@
import Api from '@/api'
import options from '@/store/options.js'
import Utils, { setGLOBFuncs } from '@/utils/utils.js'
// import antdEnUS from 'antd/es/locale/en_US'
import antdZhCN from 'antd/es/locale/zh_CN'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/utils/utils-custom.js'
import MenuUtils, { getTables } from '@/utils/utils-custom.js'
import asyncComponent from '@/utils/asyncComponent'
import '@/assets/css/design.scss'
@@ -28,6 +27,7 @@
const MenuForm = asyncComponent(() => import('./menuform'))
const HomeForm = asyncComponent(() => import('./homeform'))
const PopView = asyncComponent(() => import('./popview'))
const Header = asyncComponent(() => import('@/menu/header'))
const MenuShell = asyncComponent(() => import('@/menu/menushell'))
const PrintMenuForm = asyncComponent(() => import('./printmenuform'))
@@ -68,7 +68,8 @@
    comloading: false,
    settingshow: true,
    eyeopen: false,
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
    view: '',
    popConfig: null,
  }
  UNSAFE_componentWillMount() {
@@ -106,7 +107,6 @@
  componentDidMount () {
    MKEmitter.addListener('delButtons', this.delButtons)
    MKEmitter.addListener('modalStatus', this.modalStatus)
    MKEmitter.addListener('copyButtons', this.copyButtons)
    MKEmitter.addListener('changePopview', this.initPopview)
    MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
@@ -145,10 +145,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -175,15 +188,10 @@
      return
    }
    MKEmitter.removeListener('delButtons', this.delButtons)
    MKEmitter.removeListener('modalStatus', this.modalStatus)
    MKEmitter.removeListener('copyButtons', this.copyButtons)
    MKEmitter.removeListener('changePopview', this.initPopview)
    MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  triggerMenuSave = () => {
@@ -235,41 +243,39 @@
  }
  getAppPictures = () => {
    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
    if (sessionStorage.getItem('app_pictures')) return
    
    Api.getSystemConfig({
    let deffers = []
    let param = {
      func: 's_url_db_adduptdel',
      PageIndex: 0,  // 0 代表全部
      PageSize: 0,   // 0 代表全部
      typecharone: 'image',
      type: 'search'
    }).then(res => {
      if (res.status) {
        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
      }
    }
    deffers = [new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
          resolve(res.data)
        })
      }, 500)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
          resolve(res.data)
        })
      }, 1000)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
          resolve(res.data)
        })
      }, 1500)
    })]
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'video',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
        }
      })
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'color',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
        }
      })
    Promise.all(deffers).then(response => {
      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
    })
  }
@@ -330,22 +336,30 @@
  }
  initPopview = (card, btn) => {
    const { oriConfig, config } = this.state
    if (!is(fromJS(oriConfig), fromJS(config))) {
      notification.warning({
        top: 92,
        message: '配置已修改,请保存!',
        duration: 5
      })
      return
    }
    const { config } = this.state
    let _btn = fromJS(btn).toJS()
    _btn.MenuName = config.MenuName + '-' + card.name + '-' + btn.label
    _btn.ParentMenuID = config.uuid
    this.props.history.push('/popdesign/' + window.btoa(window.encodeURIComponent((JSON.stringify(_btn)))))
    if (_btn.config) {
      _btn.config.uuid = _btn.uuid
      _btn.config.MenuID = _btn.uuid
      _btn.config.ParentId = card.uuid
    } else {
      _btn.config = {
        uuid: _btn.uuid,
        MenuID: _btn.uuid,
        ParentId: card.uuid,
        enabled: false,
        MenuName: btn.label,
        tables: config.tables || [],
        Template: 'CustomPage',
        components: [],
        viewType: 'popview',
        style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
      }
    }
    this.setState({view: 'popview', popConfig: _btn})
  }
  closeView = () => {
@@ -391,7 +405,7 @@
        if (!config) {
          config = {
            version: 1.0,
            version: 2.0,
            uuid: MenuId,
            MenuID: MenuId,
            parentId: ParentId,
@@ -437,11 +451,19 @@
        config.open_edition = result.open_edition || ''
        window.GLOB.urlFields = config.urlFields || []
        this.setState({
          oriConfig: config,
          config: fromJS(config).toJS()
        })
        window.GLOB.customMenu = config
        // if (config.version !== 2.0) {
        //   this.setState({
        //     oriConfig: fromJS(config).toJS(),
        //     comloading: true
        //   })
        //   this.updatePage(config)
        // } else {
          this.setState({
            oriConfig: fromJS(config).toJS(),
            config: config
          })
          window.GLOB.customMenu = config
        // }
      } else {
        notification.warning({
          top: 92,
@@ -450,6 +472,26 @@
        })
      }
    })
  }
  updatePage = (config) => {
    let popBtns = []
    let traversal = (components) => {
      components.forEach(item => {
        if (item.type === 'tabs') {
          item.subtabs.forEach(tab => {
            traversal(tab.components)
          })
        } else if (item.type === 'group') {
          traversal(item.components)
        } else if (item.type !== 'search') {
          item.$tables = getTables(item, popBtns)
        }
      })
    }
    traversal(config.components)
  }
  getMenuMessage = (delButtons, tbs) => {
@@ -1054,11 +1096,11 @@
  }
  render () {
    const { activeKey, comloading, MenuType, MenuId, config, settingshow, ParentId, menuloading, customComponents, eyeopen } = this.state
    const { view, activeKey, comloading, MenuType, MenuId, config, settingshow, ParentId, menuloading, customComponents, eyeopen } = this.state
    return (
      <ConfigProvider locale={_locale}>
        <div className={'pc-menu-view ' + (MenuType || '')}>
        {view !== 'popview' ? <div className={'pc-menu-view ' + (MenuType || '')}>
          <Header />
          <DndProvider backend={HTML5Backend}>
            <div className="menu-body">
@@ -1125,15 +1167,15 @@
                    <Button type="default" onClick={this.closeView}>关闭</Button>
                  </div>
                } style={{ width: '100%' }}>
                  {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
                  {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : <Spin className="loading-config" size="large" />}
                </Card>
              </div>
            </div>
          </DndProvider>
          <StyleController />
          <StyleCombController />
          <ModalController />
        </div>
        </div> : <PopView btn={this.state.popConfig}/>}
        <ModalController />
        <StyleController />
        <StyleCombController />
      </ConfigProvider>
    )
  }
src/views/menudesign/index.scss
@@ -5,6 +5,12 @@
.pc-menu-view {
  background: #000;
  min-height: 100vh;
  .loading-config {
    position: absolute;
    left: calc(50% - 22px);
    top: calc(50vh - 70px);
    z-index: 10;
  }
  .mk-hidden {
    text-decoration: line-through!important;
    span {
src/views/menudesign/popview/index.jsx
New file
@@ -0,0 +1,283 @@
import React, { Component } from 'react'
import { DndProvider } from 'react-dnd'
import { is, fromJS } from 'immutable'
import PropTypes from 'prop-types'
import HTML5Backend from 'react-dnd-html5-backend'
import { notification, Modal, Collapse, Card, Switch, Button } from 'antd'
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const { Panel } = Collapse
const { confirm } = Modal
const MenuForm = asyncComponent(() => import('./menuform'))
const Header = asyncComponent(() => import('@/menu/header'))
const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
const Modulecell = asyncComponent(() => import('@/menu/modulecell'))
const MenuShell = asyncComponent(() => import('@/menu/menushell'))
const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
class PopViewDesign extends Component {
  static propTpyes = {
    btn: PropTypes.object,
    updateConfig: PropTypes.func
  }
  state = {
    MenuId: '',
    activeKey: 'basedata',
    menuloading: false,
    oriConfig: null,
    config: null,
    comloading: false,
    eyeopen: false
  }
  UNSAFE_componentWillMount() {
    const { btn } = this.props
    sessionStorage.setItem('editMenuType', 'popview')
    let config = fromJS(btn.config).toJS()
    window.GLOB.urlFields = []               // url变量
    window.GLOB.customMenu = config          // 保存菜单信息
    this.setState({config: config, oriConfig: fromJS(config).toJS()})
  }
  componentDidMount () {
    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
  }
  closeView = () => {
    const { oriConfig, config } = this.state
    if (!is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置已修改,放弃保存吗?',
        content: '',
        onOk() {
          // window.history.back()
        },
        onCancel() {}
      })
    } else {
      // window.history.back()
    }
  }
  getMenuMessage = (tbs) => {
    const { config } = this.state
    let traversal = (components) => {
      components.forEach(item => {
        if (item.$tables) {
          tbs.push(...item.$tables)
        }
        if (item.type === 'tabs') {
          item.subtabs.forEach(tab => {
            traversal(tab.components)
          })
        } else if (item.type === 'group') {
          traversal(item.components)
        }
      })
    }
    traversal(config.components)
  }
  submitConfig = () => {
    let config = fromJS(this.state.config).toJS()
    if (config.cacheUseful === 'true' && !config.cacheTime) {
      notification.warning({
        top: 92,
        message: '请完善缓存设置!',
        duration: 5
      })
      return
    }
    if (config.enabled && this.verifyConfig()) {
      config.enabled = false
    }
    this.setState({
      menuloading: true
    })
  }
  onEnabledChange = () => {
    const { config } = this.state
    if (!config.enabled && this.verifyConfig(true)) {
      return
    }
    this.setState({
      config: {...config, enabled: !config.enabled}
    })
  }
  verifyConfig = (show) => {
    const { config } = this.state
    let error = ''
    let check = (components) => {
      components.forEach(item => {
        if (error) return
        if (item.type === 'tabs') {
          item.subtabs.forEach(tab => {
            check(tab.components)
          })
          return
        } else if (item.type === 'group') {
          check(item.components)
          return
        } else if (!item.errors || item.errors.length === 0) {
          return
        }
        item.errors.forEach(err => {
          if (err.level !== 0 || error) return
          error = `组件《${item.name}》${err.detail}`
        })
      })
    }
    check(config.components)
    if (show && error) {
      notification.warning({
        top: 92,
        message: error,
        duration: 5
      })
    }
    return error
  }
  // 更新配置信息
  updateConfig = (config) => {
    this.setState({
      config: config
    })
    window.GLOB.customMenu = config
  }
  resetConfig = (config) => {
    this.setState({
      config,
      comloading: true
    }, () => {
      this.setState({
        comloading: false
      })
    })
    window.GLOB.customMenu = config
  }
  /**
   * @description 更新常用表信息,快捷添加后更新配置信息
   */
  updatetable = (config) => {
    this.setState({ config })
    window.GLOB.customMenu = config
  }
  insert = (item) => {
    let config = fromJS(this.state.config).toJS()
    config.components.push(item)
    this.setState({config})
    window.GLOB.customMenu = config
    notification.success({
      top: 92,
      message: '粘贴成功!',
      duration: 2
    })
  }
  render () {
    const { activeKey, comloading, config, menuloading, eyeopen } = this.state
    return (
      <div className="pc-poper-view">
        <Header />
        <DndProvider backend={HTML5Backend}>
          <div className="menu-body">
            <div className="menu-setting">
              <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
                {/* 基本信息 */}
                <Panel header="基本信息" key="basedata">
                  {/* 菜单信息 */}
                  <MenuForm config={config} updateConfig={this.updateConfig}/>
                  {/* 表名添加 */}
                  <TableComponent config={config} updatetable={this.updatetable}/>
                </Panel>
                {/* 组件添加 */}
                <Panel header="组件" key="component">
                  <SourceWrap MenuType="" />
                </Panel>
                <Panel header="元素" key="element">
                  <Modulecell />
                </Panel>
                <Panel header="页面样式" key="background">
                  <BgController config={config} updateConfig={this.updateConfig} />
                </Panel>
              </Collapse>
            </div>
            <div className={'menu-view' + (menuloading ? ' saving' : '') + (eyeopen ? ' eye-open' : '')}>
              <Card title={
                <div> {config.MenuName} </div>
              } bordered={false} extra={
                <div>
                  <Button className="mk-border-purple" onClick={() => this.setState({eyeopen: !eyeopen})}>{!eyeopen ? <EyeOutlined /> : <EyeInvisibleOutlined />} 组件名</Button>
                  <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
                  <StyleCombControlButton menu={config} />
                  <PasteController insert={this.insert} />
                  <Switch className="big" checkedChildren="启" unCheckedChildren="停" checked={config.enabled} onChange={this.onEnabledChange} />
                  <Button type="primary" id="save-pop-config" onClick={this.submitConfig} loading={menuloading}>保存</Button>
                  <Button type="default" onClick={this.closeView}>返回</Button>
                </div>
              } style={{ width: '100%' }}>
                {!comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
              </Card>
            </div>
          </div>
        </DndProvider>
      </div>
    )
  }
}
export default PopViewDesign
src/views/menudesign/popview/index.scss
New file
@@ -0,0 +1,174 @@
.pc-poper-view {
  background: #000;
  min-height: 100vh;
  .eye-open {
    .component-name {
      display: block;
    }
    .anticon-tool {
      display: none;
    }
  }
  .component-name {
    position: absolute;
    z-index: 9;
    display: none;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    .center {
      position: absolute;
      font-size: 16px;
      left: 50%;
      top: 50%;
      color: #1890ff;
      transform: translate(-50%, -50%);
      max-width: 70%;
      .title {
        text-align: center;
      }
    }
    .error {
      text-align: center;
      color: red;
      display: block;
    }
    .waring {
      color: orange;
    }
  }
  >.menu-body {
    width: 100vw;
    height: 100vh;
    overflow-x: hidden;
    position: relative;
    background: #ffffff;
    padding: 50px 0px 0px 0px;
    .menu-setting {
      position: fixed;
      left: 0;
      top: 48px;
      z-index: 10;
      height: calc(100vh - 48px);
      width: 300px;
      background: #ffffff;
      box-shadow: 0px 2px 5px #bcbcbc;
      overflow-y: auto;
      overflow-x: hidden;
      > .ant-collapse {
        background-color: #ffffff;
        .ant-collapse-item.ant-collapse-item-active {
          border-bottom: 1px solid #d9d9d9;
        }
        .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-tabs {
        >.ant-tabs-bar {
          border-bottom: 1px solid #181F29;
          margin-bottom: 0px;
          min-height: 48px;
          .ant-tabs-tab {
            padding: 14px 16px;
            color: rgba(255, 255, 255, 0.85);
          }
          .ant-tabs-tab-active.ant-tabs-tab {
            color: #1890ff;
          }
        }
      }
    }
    .menu-setting::-webkit-scrollbar {
      width: 4px;
    }
    .menu-setting::-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);
    }
    .menu-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);
    }
    .menu-view {
      position: relative;
      width: calc(100vw - 300px);
      margin-left: 300px;
      height: calc(100vh - 50px);
      overflow-y: auto;
      > .ant-card {
        >.ant-card-head {
          margin-bottom: 0px;
          position: relative;
          .ant-card-head-title {
            color: #1890ff;
            padding: 5px 0;
          }
          .ant-card-extra {
            padding: 5px 0;
            button {
              margin-left: 20px;
            }
            .ant-switch.big {
              min-width: 60px;
              height: 28px;
              line-height: 28px;
              margin-top: -2px;
              .ant-switch-inner {
                font-size: 14px;
              }
            }
            .ant-switch.big:after {
              width: 24px;
              height: 24px;
            }
          }
        }
        >.ant-card-body {
          padding: 0px;
        }
      }
    }
    .menu-view.saving {
      .anticon-tool {
        display: none;
      }
    }
    .menu-view::-webkit-scrollbar {
      width: 7px;
    }
    .menu-view::-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);
    }
    .menu-view::-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/views/menudesign/popview/menuform/index.jsx
New file
@@ -0,0 +1,98 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Radio, Tooltip, InputNumber } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
// import './index.scss'
class CustomMenuForm extends Component {
  static propTpyes = {
    config: PropTypes.object,
    updateConfig: PropTypes.func
  }
  changeCacheDay = (val) => {
    if (typeof(val) !== 'number') {
      val = ''
    }
    this.props.updateConfig({...this.props.config, cacheTime: val})
  }
  selectChange = (key, value) => {
    const { config } = this.props
    if (key === 'cacheUseful') {
      this.props.updateConfig({...config, cacheUseful: value})
    } else if (key === 'timeUnit') {
      this.props.updateConfig({...config, timeUnit: value})
    }
  }
  render() {
    const { config } = this.props
    const { getFieldDecorator } = this.props.form
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout}>
        <Row>
          <Col span={24}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="对于不经常性变动的信息,缓存数据有助于提高查询效率。">
                <QuestionCircleOutlined className="mk-form-tip" />
                缓存数据
              </Tooltip>
            }>
              {getFieldDecorator('cacheUseful', {
                initialValue: config.cacheUseful || 'false'
              })(
                <Radio.Group onChange={(e) => {this.selectChange('cacheUseful', e.target.value)}}>
                  <Radio value="true">使用</Radio>
                  <Radio value="false">不使用</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {config.cacheUseful === 'true' ? <Col span={24}>
            <Form.Item label="单位">
              {getFieldDecorator('timeUnit', {
                initialValue: config.timeUnit || 'day'
              })(
                <Radio.Group onChange={(e) => {this.selectChange('timeUnit', e.target.value)}}>
                  <Radio value="day">天</Radio>
                  <Radio value="hour">小时</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {config.cacheUseful === 'true' ? <Col span={24}>
            <Form.Item label="时长">
              {getFieldDecorator('cacheTime', {
                initialValue: config.cacheTime,
                rules: [
                  {
                    required: true,
                    message: '请输入时长!'
                  }
                ]
              })(
                <InputNumber min={1} max={config.timeUnit !== 'hour' ? 7 : 23} precision={0} onChange={this.changeCacheDay}/>
              )}
            </Form.Item>
          </Col> : null}
        </Row>
      </Form>
    )
  }
}
export default Form.create()(CustomMenuForm)
src/views/menudesign/popview/menuform/index.scss
src/views/mobdesign/index.jsx
@@ -76,7 +76,6 @@
    adapters: [],
    viewType: 'menu',
    eyeopen: false,
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  UNSAFE_componentWillMount() {
@@ -149,7 +148,6 @@
      document.getElementById('mk-mob-design-view').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh; height: 100vh; background: #fff;">本应用没有PC端页面的编辑权限,请联系管理员!</div>'
      return
    }
    MKEmitter.addListener('modalStatus', this.modalStatus)
    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
    MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
@@ -187,10 +185,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -216,14 +227,9 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('modalStatus', this.modalStatus)
    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
    MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  getSmStemp = () => {
@@ -369,41 +375,39 @@
  }
  getAppPictures = () => {
    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
    Api.getSystemConfig({
    if (sessionStorage.getItem('app_pictures')) return
    let deffers = []
    let param = {
      func: 's_url_db_adduptdel',
      PageIndex: 0,  // 0 代表全部
      PageSize: 0,   // 0 代表全部
      typecharone: 'image',
      type: 'search'
    }).then(res => {
      if (res.status) {
        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
      }
    }
    deffers = [new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
          resolve(res.data)
        })
      }, 500)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
          resolve(res.data)
        })
      }, 1000)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
          resolve(res.data)
        })
      }, 1500)
    })]
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'video',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
        }
      })
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'color',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
        }
      })
    Promise.all(deffers).then(response => {
      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
    })
  }
src/views/pcdesign/index.jsx
@@ -70,7 +70,6 @@
    controlshow: sessionStorage.getItem('controlshow') !== 'false',
    comloading: false,
    eyeopen: false,
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  UNSAFE_componentWillMount() {
@@ -127,7 +126,6 @@
      return
    }
    MKEmitter.addListener('delButtons', this.delButtons)
    MKEmitter.addListener('modalStatus', this.modalStatus)
    MKEmitter.addListener('copyButtons', this.copyButtons)
    MKEmitter.addListener('changePopview', this.initPopview)
    MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
@@ -167,10 +165,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -200,16 +211,11 @@
      return
    }
    MKEmitter.removeListener('delButtons', this.delButtons)
    MKEmitter.removeListener('modalStatus', this.modalStatus)
    MKEmitter.removeListener('copyButtons', this.copyButtons)
    MKEmitter.removeListener('changePopview', this.initPopview)
    MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
    MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  triggerMenuSave = () => {
@@ -396,59 +402,39 @@
  }
  getAppPictures = () => {
    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
    Api.getSystemConfig({
    if (sessionStorage.getItem('app_pictures')) return
    let deffers = []
    let param = {
      func: 's_url_db_adduptdel',
      PageIndex: 0,  // 0 代表全部
      PageSize: 0,   // 0 代表全部
      typecharone: 'image',
      type: 'search'
    }).then(res => {
      if (res.status) {
        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
      } else if (!res.status) {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
    }
    deffers = [new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
          resolve(res.data)
        })
      }
      }, 500)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
          resolve(res.data)
        })
      }, 1000)
    }), new Promise(resolve => {
      setTimeout(() => {
        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
          resolve(res.data)
        })
      }, 1500)
    })]
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'video',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
        } else if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
        }
      })
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'color',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
        } else if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
        }
      })
    Promise.all(deffers).then(response => {
      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
    })
  }
src/views/popdesign/index.jsx
@@ -115,10 +115,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
src/views/tabledesign/index.jsx
@@ -52,10 +52,8 @@
    menuloading: false,
    oriConfig: null,
    config: null,
    customComponents: [],
    comloading: false,
    settingshow: true,
    modalStatus: false       // 弹窗是否开启,判断ctrl+s是否可用
  }
  UNSAFE_componentWillMount() {
@@ -91,20 +89,9 @@
  }
  componentDidMount () {
    MKEmitter.addListener('modalStatus', this.modalStatus)
    MKEmitter.addListener('changePopview', this.initPopview)
    MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
    setTimeout(() => {
      if (sessionStorage.getItem('app_custom_components')) {
        let list = sessionStorage.getItem('app_custom_components')
        list = JSON.parse(list)
        this.setCustomComponent(list)
      } else {
        this.updateCustomComponent()
      }
      this.getAppPictures()
      this.getPrintTemp()
      this.getRoleFields()
      setGLOBFuncs()
@@ -129,10 +116,23 @@
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        if (this.state.modalStatus) {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + this.state.modalStatus,
            message: '请保存' + msg,
            duration: 5
          })
          return false
@@ -158,14 +158,8 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('modalStatus', this.modalStatus)
    MKEmitter.removeListener('changePopview', this.initPopview)
    MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
  }
  modalStatus = (val) => {
    this.setState({modalStatus: val})
  }
  triggerMenuSave = () => {
@@ -214,93 +208,6 @@
        }
      })
    }
  }
  getAppPictures = () => {
    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
    Api.getSystemConfig({
      func: 's_url_db_adduptdel',
      PageIndex: 0,  // 0 代表全部
      PageSize: 0,   // 0 代表全部
      typecharone: 'image',
      type: 'search'
    }).then(res => {
      if (res.status) {
        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
      }
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'video',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
        }
      })
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'color',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
        }
      })
    })
  }
  updateCustomComponent = () => {
    Api.getSystemConfig({
      func: 's_get_custom_components',
      typename: '',
      typecharone: ''
    }).then(res => {
      if (!res.status) {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
      } else if (res.cus_list) {
        sessionStorage.setItem('app_custom_components', JSON.stringify(res.cus_list))
        this.setCustomComponent(res.cus_list)
      }
    })
  }
  setCustomComponent = (cus_list) => {
    let coms = []
    cus_list.forEach(item => {
      let config = ''
      try {
        config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
      } catch (e) {
        console.warn('Parse Failure')
        config = ''
      }
      if (!config || !item.c_name) return
      window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
      coms.push({
        uuid: item.c_id,
        type: 'menu',
        title: item.c_name,
        url: item.images,
        component: config.type,
        subtype: config.subtype,
        config
      })
    })
    this.setState({customComponents: coms})
  }
  initPopview = (card, btn) => {
@@ -424,7 +331,7 @@
    })
  }
  getMenuMessage = (delButtons, tbs) => {
  getMenuMessage = (tbs) => {
    const { config } = this.state
    let buttons = []
    let _sort = 1
@@ -435,23 +342,27 @@
          if (tab.components[0].$tables) {
            tbs.push(...tab.components[0].$tables)
          }
          if (tab.permission !== 'true' || tab.hide === 'true') return
          buttons.push(`select '${tab.components[0].uuid}' as menuid, '${tab.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
          _sort++
          let _s = 1
          tab.components[0].action.forEach(btn => {
            if (btn.hidden === 'true') {
              delButtons.push(btn.uuid)
              return
            }
            buttons.push(`select '${btn.uuid}' as menuid, '${tab.label + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
            _sort++
            if (btn.hidden === 'true') return
            buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_s * 10}' as Sort, '${tab.components[0].uuid}' as parentid, 60 as Type`)
            _s++
          })
          tab.components[0].cols.forEach(col => {
            if (col.type !== 'action') return
            col.elements.forEach(btn => {
              if (btn.hidden === 'true') {
                delButtons.push(btn.uuid)
                return
              }
              buttons.push(`select '${btn.uuid}' as menuid, '${tab.label + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
              _sort++
              if (btn.hidden === 'true') return
              buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_s * 10}' as Sort, '${tab.components[0].uuid}' as parentid, 60 as Type`)
              _s++
            })
          })
        })
@@ -460,21 +371,17 @@
          tbs.push(...item.$tables)
        }
        item.action.forEach(btn => {
          if (btn.hidden === 'true') {
            delButtons.push(btn.uuid)
            return
          }
          buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_sort * 10}' as Sort`)
          if (btn.hidden === 'true') return
          buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
          _sort++
        })
        item.cols.forEach(col => {
          if (col.type !== 'action') return
          col.elements.forEach(btn => {
            if (btn.hidden === 'true') {
              delButtons.push(btn.uuid)
              return
            }
            buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_sort * 10}' as Sort`)
            if (btn.hidden === 'true') return
            buttons.push(`select '${btn.uuid}' as menuid,  '${btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
            _sort++
          })
        })
@@ -506,8 +413,7 @@
      }
      let tbs = []
      let delButtons = []
      let btns = this.getMenuMessage(delButtons, tbs)
      let btns = this.getMenuMessage(tbs)
      let arr = []
      tbs = tbs.filter(tb => {
        let _tb = tb.toLowerCase()
@@ -567,100 +473,54 @@
        ParentID: config.uuid,
        MenuNo: config.MenuNo,
        Template: 'BaseTable',
        PageParam: '',
        LongParam: '',
        LText: []
        button_proc_edition: 'Y'
      }
      btnParam.LText = btns
      btnParam.LText = btnParam.LText.join(' union all ')
      btnParam.LText = btns.join(' union all ')
      btnParam.LText = Utils.formatOptions(btnParam.LText)
      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
      new Promise(resolve => {
        resolve(true)
      }).then(res => { // 按钮删除
        if (delButtons.length === 0) {
          return {
            status: true
          }
        } else {
          let _param = {
            func: 'sPC_MainMenu_Del',
            MenuID: delButtons.join(',')
          }
          return Api.getSystemConfig(_param)
        }
      }).then(res => { // 页面保存
        if (!res) return
        if (res.status) {
          return Api.getSystemConfig(param)
        } else {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
          return false
        }
      }).then(res => { // 页面按钮关系保存
        if (!res) return
        if (res.status) {
          let ori = this.state.oriConfig
          if (config.MenuName !== ori.MenuName || config.MenuNo !== ori.MenuNo || config.parentId !== ori.parentId) {
            localStorage.setItem('menuUpdate', new Date().getTime())
          }
          config.open_edition = res.open_edition || ''
          this.setState({
            config,
            oriConfig: fromJS(config).toJS(),
          })
          if (btnParam.LText) {
            return Api.getSystemConfig(btnParam)
          } else {
            return {
              status: true
            }
          }
        } else {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
          return false
        }
      }).then(res => { // 按钮复制
        if (!res) return
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
          return false
        }
        Api.getSystemConfig(param).then(res => {
          resolve(res)
        })
      }).then(res => {
        if (res && res.status) {
          this.setState({
            menuloading: false
          })
        if (!res || !res.status) return res
        let ori = this.state.oriConfig
        if (config.MenuName !== ori.MenuName || config.MenuNo !== ori.MenuNo || config.parentId !== ori.parentId) {
          localStorage.setItem('menuUpdate', new Date().getTime())
        }
        config.open_edition = res.open_edition || ''
        this.setState({
          config,
          oriConfig: fromJS(config).toJS(),
        })
        return Api.getSystemConfig(btnParam)
      }).then(res => {
        this.setState({
          menuloading: false
        })
        if (!res) return
        if (res.status) {
          notification.success({
            top: 92,
            message: '保存成功',
            duration: 2
          })
          MKEmitter.emit('completeSave')
        } else {
          this.setState({
            menuloading: false
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
        }
        MKEmitter.emit('completeSave')
      })
    }, 300 + (+sessionStorage.getItem('mkDelay')))
  }
src/views/tabledesign/index.scss
@@ -2,6 +2,14 @@
  overflow-x: hidden;
  overflow-y: hidden;
}
.mk-source-wrap {
  .ant-radio-button-wrapper:last-child {
    display: none;
  }
  .ant-radio-button-wrapper:not(:first-child) {
    border-radius: 0 4px 4px 0;
  }
}
.pc-table-view {
  background: #000;
  min-height: 100vh;
@@ -17,37 +25,6 @@
    }
    .anticon-tool {
      display: none;
    }
  }
  .component-name {
    position: absolute;
    z-index: 9;
    display: none;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    .center {
      position: absolute;
      font-size: 16px;
      left: 50%;
      top: 50%;
      color: #1890ff;
      transform: translate(-50%, -50%);
      max-width: 70%;
      .title {
        text-align: center;
      }
    }
    .error {
      text-align: center;
      color: red;
      display: block;
    }
    .waring {
      color: orange;
    }
  }
  >.menu-body {
@@ -137,7 +114,7 @@
    }
    .menu-setting.hidden {
      left: -300px;
      left: -280px;
    }
    .menu-setting.hidden + .menu-view {
      width: 100vw;
src/views/tabledesign/source.jsx
@@ -140,6 +140,12 @@
    },
    {
      type: 'col',
      label: '视频',
      subType: 'video',
      $init: true
    },
    {
      type: 'col',
      label: '链接',
      subType: 'link',
      $init: true