king
2022-05-12 11745b05b3ecdd3a3dc6f64793dc83a095de2b09
2022-05-12
36个文件已修改
2个文件已添加
879 ■■■■ 已修改文件
src/menu/components/card/cardcellcomponent/elementform/index.jsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/actionform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/formconfig.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/formconfig.jsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/menucomponent/options.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/data-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/prop-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-dashboard/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-pie/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-scatter/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/custom-chart/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/code/sand-box/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/editor/braft-editor/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/normal-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/tab-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/edit-table/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/normal-table/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/timeline/normal-timeline/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tree/antd-tree/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/normalbutton/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload-pice/index.jsx 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload-pice/index.scss 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload/index.jsx 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload/index.scss 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-custom.js 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Radio, Tooltip, InputNumber } from 'antd'
import { Form, Row, Col, Input, Select, Radio, Tooltip, InputNumber, Cascader } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import { formRule } from '@/utils/option.js'
@@ -15,7 +15,7 @@
const cardTypeOptions = {
  sequence: ['eleType', 'width'],
  text: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix', 'link', 'noValue', 'bgImage', 'fixStyle'],
  text: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix', 'link', 'anchors', 'noValue', 'bgImage', 'fixStyle'],
  number: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix', 'noValue', 'fixStyle'],
  picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'maxWidth', 'link', 'noValue'],
  video: ['eleType', 'datatype', 'width', 'aspectRatio', 'autoPlay', 'loop', 'noValue'],
@@ -507,6 +507,29 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'cascader') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || [],
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Cascader options={item.options || []} expandTrigger="hover" placeholder=""/>
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -8,7 +8,7 @@
 * @param {*} card
 * @param {*} type
 */
export function getCardCellForm (card, type, subtype, cardCell) {
export function getCardCellForm (card, type, subtype, cardCell, anchors) {
  let _options = [
    { value: 'text', text: '文本'},
    { value: 'number', text: '数值'},
@@ -326,6 +326,14 @@
      options: []
    },
    {
      type: 'cascader',
      key: 'anchors',
      label: '跳转锚点',
      initVal: card.anchors || [],
      required: false,
      options: anchors
    },
    {
      type: 'number',
      key: 'barHeight',
      min: 5,
@@ -553,7 +561,7 @@
      type: 'number',
      key: 'fixSize',
      min: 10,
      max: 100,
      max: 300,
      label: '字体大小',
      initVal: card.fixSize || 14,
      tooltip: '前缀、后缀的字体大小。',
src/menu/components/card/cardcellcomponent/index.jsx
@@ -269,10 +269,12 @@
    if (card.eleType === 'button') {
      this.handleAction(card)
    } else {
      let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
      this.setState({
        visible: true,
        card: card,
        formlist: getCardCellForm(card, cards.type, cards.subtype, cardCell)
        formlist: getCardCellForm(card, cards.type, cards.subtype, cardCell, anchors)
      })
    }
  }
@@ -311,11 +313,12 @@
    }
    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, cards.uuid) || []
    let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
    this.setState({
      actvisible: true,
      card: card,
      formlist: getActionForm(card, functip, cards, usefulFields, 'card', menulist, modules)
      formlist: getActionForm(card, functip, cards, usefulFields, 'card', menulist, modules, anchors)
    })
  }
src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx
@@ -239,7 +239,7 @@
      label: '字体大小',
      initVal: card.fontSize || 28,
      min: 12,
      max: 200,
      max: 300,
      decimal: 0,
      required: true
    },
src/menu/components/form/formaction/actionform/index.jsx
@@ -54,7 +54,7 @@
    } else if (card.type === 'next') {
      return ['type', 'label', 'enable']
    }
    let _options = ['type', 'label', 'intertype', 'syncComponent', 'linkmenu', 'open', 'enable', 'output', 'reload'] // 选项列表
    let _options = ['type', 'label', 'intertype', 'syncComponent', 'anchors', 'linkmenu', 'open', 'enable', 'output', 'reload'] // 选项列表
    
    if (_intertype === 'custom') {
      _options.pop()
src/menu/components/form/formaction/formconfig.jsx
@@ -9,7 +9,7 @@
 * @param {*} type           按钮类型,用于区分可选的打开方式
 */
export function getActionForm (card, functip, tableName, usefulFields, modules) {
export function getActionForm (card, functip, tableName, usefulFields, modules, anchors) {
  const appType = sessionStorage.getItem('appType')
  let _type = '提交'
  if (card.type === 'prev') {
@@ -287,6 +287,15 @@
      options: modules
    },
    {
      type: 'cascader',
      key: 'anchors',
      label: '跳转锚点',
      initVal: card.anchors || [],
      tooltip: '执行成功后,需要跳转的锚点',
      required: false,
      options: anchors
    },
    {
      type: 'radio',
      key: 'reload',
      label: '上一页',
src/menu/components/form/formaction/index.jsx
@@ -102,11 +102,12 @@
    </div>
    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid) || []
    let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, config.uuid) || []
    this.setState({
      visible: true,
      card: card,
      formlist: getActionForm(card, functip, config.setting.tableName, usefulFields, modules)
      formlist: getActionForm(card, functip, config.setting.tableName, usefulFields, modules, anchors)
    })
  }
src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -11,9 +11,9 @@
const { TextArea } = Input
const MkEditIcon = asyncComponent(() => import('@/components/mkIcon'))
const acTyOptions = {
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'width', 'openmenu', 'open', 'refreshTab', 'position', 'tipTitle', 'hidden'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'width', 'openmenu', 'open', 'refreshTab', 'position', 'tipTitle', 'hidden'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'width', 'openmenu', 'open', 'refreshTab', 'hidden'],
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'open', 'refreshTab', 'position', 'tipTitle', 'hidden'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'open', 'refreshTab', 'position', 'tipTitle', 'hidden'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'open', 'refreshTab', 'hidden'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'sheet', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'switchTab', 'width', 'hidden'],
  excelOut: ['label', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'resetPageIndex', 'pagination', 'search', 'width', 'hidden'],
  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'color', 'popClose', 'resetPageIndex', 'width', 'display', 'ratio', 'syncComponent', 'clickouter', 'hidden'],
src/menu/components/share/actioncomponent/formconfig.jsx
@@ -12,7 +12,7 @@
 * @param {*} usefulFields   存储过程可用的开始字段
 * @param {*} type           按钮类型,用于区分可选的打开方式
 */
export function getActionForm (card, functip, config, usefulFields, type, menulist = [], modules = []) {
export function getActionForm (card, functip, config, usefulFields, type, menulist = [], modules = [], anchors = []) {
  let appType = sessionStorage.getItem('appType')
  let viewType = sessionStorage.getItem('editMenuType') // 弹窗 popview
  let printTemps = sessionStorage.getItem('printTemps')
@@ -646,6 +646,15 @@
    },
    {
      type: 'cascader',
      key: 'anchors',
      label: '跳转锚点',
      initVal: card.anchors || [],
      tooltip: '执行成功后,需要跳转的锚点',
      required: false,
      options: anchors
    },
    {
      type: 'cascader',
      key: 'refreshTab',
      label: '刷新菜单',
      initVal: card.refreshTab || [],
@@ -719,7 +728,7 @@
      precision: 0,
      label: '比例',
      initVal: card.ratio || 85,
      tooltip: '小于100为宽度(或高度)百分比,大于100为像素值。',
      tooltip: '模态框或抽屉的宽度,小于100为窗口宽度(或高度)百分比,大于100为像素值。',
      required: true
    },
    {
src/menu/components/share/actioncomponent/index.jsx
@@ -186,11 +186,12 @@
    }
    
    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid) || []
    let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, config.uuid) || []
    this.setState({
      visible: true,
      card: card,
      formlist: getActionForm(card, functip, config, usefulFields, this.props.type, menulist, modules)
      formlist: getActionForm(card, functip, config, usefulFields, this.props.type, menulist, modules, anchors)
    })
  }
src/menu/stylecontroller/index.jsx
@@ -430,7 +430,7 @@
              {options.includes('font') ? <Panel header="字体" key="font">
                <Col span={12}>
                  <Form.Item colon={false} label={<FontSizeOutlined title="字体大小"/>}>
                    <InputNumber defaultValue={card.fontSize || 14} min={12} max={100} precision={0} onChange={this.changeFontSize} />
                    <InputNumber defaultValue={card.fontSize || 14} min={12} max={300} precision={0} onChange={this.changeFontSize} />
                  </Form.Item>
                </Col>
                <Col span={12}>
src/mob/components/menubar/normal-menubar/menucomponent/options.jsx
@@ -136,7 +136,7 @@
      label: '字体大小',
      initval: setting.iconFont || 20,
      min: 12,
      max: 200,
      max: 300,
      precision: 0,
      required: true
    },
src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -73,6 +73,14 @@
  openNewView = (e, card) => {
    const { cardCell, data, cards } = this.props
    if (data.$disabled) return
    if (card.anchors && card.anchors.length > 0) {
      let id = card.anchors[card.anchors.length - 1]
      let node = document.getElementById('anchor' + id)
      node && node.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'nearest'})
    }
    if (!card.link) return
    e.stopPropagation()
    
@@ -349,10 +357,12 @@
        }
      }
      if (card.link) {
      if (card.link || (card.anchors && card.anchors.length > 0)) {
        _style.cursor = 'pointer'
      }
      if (!card.link)
      if (card.bgImage && data[card.bgImage]) {
        _style.backgroundImage = `url('${data[card.bgImage]}')`
      }
src/tabviews/custom/components/card/data-card/index.jsx
@@ -744,7 +744,7 @@
    }
    return (
      <div className="custom-data-card-box" style={config.style}>
      <div className="custom-data-card-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            {data ? <div className="ant-spin-blur"></div> : null}
src/tabviews/custom/components/card/prop-card/index.jsx
@@ -420,7 +420,7 @@
    if (config.wrap.empty === 'hidden' && (!data || data.$$empty)) return null
    return (
      <div className="custom-prop-card-box" style={config.style}>
      <div className="custom-prop-card-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/card/table-card/index.jsx
@@ -422,7 +422,7 @@
    if (config.wrap.empty === 'hidden' && (!data || data.length === 0)) return null
    
    return (
      <div className="custom-table-card-box" style={{...config.style}}>
      <div className="custom-table-card-box" id={'anchor' + config.uuid} style={{...config.style}}>
        {loading ?
          <div className="loading-mask">
            {data ? <div className="ant-spin-blur"></div> : null}
src/tabviews/custom/components/carousel/data-card/index.jsx
@@ -258,7 +258,7 @@
    if (config.wrap.empty === 'hidden' && (!data || data.length === 0)) return null
    
    return (
      <div className="custom-data-carousel-box" style={config.style}>
      <div className="custom-data-carousel-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            {data ? <div className="ant-spin-blur"></div> : null}
src/tabviews/custom/components/carousel/prop-card/index.jsx
@@ -276,7 +276,7 @@
    if (config.wrap.empty === 'hidden' && (!data || data.$$empty)) return null
    return (
      <div className="custom-prop-carousel-box" style={config.style}>
      <div className="custom-prop-carousel-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/chart/antv-bar-line/index.jsx
@@ -1781,7 +1781,7 @@
    }
    return (
      <div className="custom-line-chart-plot-box" style={style}>
      <div className="custom-line-chart-plot-box" id={'anchor' + config.uuid} style={style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/chart/antv-dashboard/index.jsx
@@ -642,7 +642,7 @@
    const { config, loading } = this.state
    return (
      <div className="custom-dashboard-plot-box" style={config.style}>
      <div className="custom-dashboard-plot-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/chart/antv-pie/index.jsx
@@ -1054,7 +1054,7 @@
    }
    return (
      <div className="custom-pie-chart-plot-box" style={style}>
      <div className="custom-pie-chart-plot-box" id={'anchor' + config.uuid} style={style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/chart/antv-scatter/index.jsx
@@ -447,7 +447,7 @@
    }
    return (
      <div className="custom-scatter-plot-box" style={style}>
      <div className="custom-scatter-plot-box" id={'anchor' + config.uuid} style={style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/chart/custom-chart/index.jsx
@@ -380,7 +380,7 @@
    const { config, loading, empty, BID } = this.state
    return (
      <div className="custom-chart-plot-box" style={config.style}>
      <div className="custom-chart-plot-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/code/sand-box/index.jsx
@@ -208,7 +208,7 @@
    const { config, loading, html } = this.state
    return (
      <div className="custom-sand-box" style={{...config.style}}>
      <div className="custom-sand-box" id={'anchor' + config.uuid} style={{...config.style}}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/editor/braft-editor/index.jsx
@@ -177,7 +177,7 @@
    const { config, loading, data } = this.state
    return (
      <div className="custom-braft-editor-box" style={config.style}>
      <div className="custom-braft-editor-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/form/normal-form/index.jsx
@@ -359,7 +359,7 @@
    const { config, loading, BID, data, group, dict, step } = this.state
    return (
      <div className="custom-normal-form-box" style={{...config.style}}>
      <div className="custom-normal-form-box" id={'anchor' + config.uuid} style={{...config.style}}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/form/tab-form/index.jsx
@@ -307,7 +307,7 @@
    const { config, loading, BID, data, group, dict } = this.state
    return (
      <div className="custom-tab-form-box" style={{...config.style}}>
      <div className="custom-tab-form-box" id={'anchor' + config.uuid} style={{...config.style}}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/custom/components/table/edit-table/index.jsx
@@ -589,7 +589,7 @@
    const { BID, setting, searchlist, actions, config, columns, BData, selectedData, lock } = this.state
    return (
      <div className="custom-edit-table" style={config.style}>
      <div className="custom-edit-table" id={'anchor' + config.uuid} style={config.style}>
        <NormalHeader config={config}/>
        {searchlist && searchlist.length ?
          <MainSearch BID={BID} setting={config.wrap} searchlist={searchlist} menuType={this.props.menuType} refreshdata={this.refreshbysearch}/> : null
src/tabviews/custom/components/table/normal-table/index.jsx
@@ -643,7 +643,7 @@
    }
    return (
      <div className="custom-normal-table" style={style}>
      <div className="custom-normal-table" id={'anchor' + config.uuid} style={style}>
        {config.wrap.collapse === 'true' ? <Collapse bordered={false} defaultActiveKey="1" expandIconPosition="right">
          <Panel forceRender={true} header={<NormalHeader config={config}/>} key="1">
            {searchlist && searchlist.length ?
src/tabviews/custom/components/timeline/normal-timeline/index.jsx
@@ -318,7 +318,7 @@
    const { config, loading, data } = this.state
    return (
      <div className="normal-timeline-box" style={{...config.style}}>
      <div className="normal-timeline-box" id={'anchor' + config.uuid} style={{...config.style}}>
        {loading ?
          <div className="loading-mask">
            {data ? <div className="ant-spin-blur"></div> : null}
src/tabviews/custom/components/tree/antd-tree/index.jsx
@@ -462,7 +462,7 @@
    const { config, loading, treeNodes, expandedKeys, selectedKeys } = this.state
    return (
      <div className="custom-tree-box" style={config.style}>
      <div className="custom-tree-box" id={'anchor' + config.uuid} style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -1556,6 +1556,11 @@
      let node = document.getElementById('tab' + id)
      node && node.click()
    }
    if (btn.anchors && btn.anchors.length > 0) {
      let id = btn.anchors[btn.anchors.length - 1]
      let node = document.getElementById('anchor' + id)
      node && node.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'nearest'})
    }
    if (btn.openmenu && btn.openmenu.length > 0 && btn.MenuID) {
      let newtab = {
src/tabviews/zshare/fileupload-pice/index.jsx
New file
@@ -0,0 +1,498 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import moment from 'moment'
import { Upload, Button, Progress, notification } from 'antd'
import { UploadOutlined } from '@ant-design/icons'
import SparkMD5 from 'spark-md5'
import Api from '@/api'
import './index.scss'
let service = ''
if (process.env.NODE_ENV === 'production') {
  service = document.location.origin + '/' + window.GLOB.service
} else {
  service = window.GLOB.location + '/' + window.GLOB.service
}
class FileUpload extends Component {
  static propTpyes = {
    config: PropTypes.object,  // 表单信息
    onChange: PropTypes.func,  // 表单变化
  }
  state = {
    percent: 0,
    accept: '',
    accepts: null,
    maxFile: null,
    rduri: '',
    limit: 2,
    compress: false,
    fileType: 'text',
    showprogress: false,
    filelist: []
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    let filelist = []
    if (config.initval) {
      if (/^data:image/.test(config.initval)) {
        filelist = [{
          uid: '0',
          name: 'data:image/jpeg;base64',
          status: 'done',
          url: config.initval,
          origin: true
        }]
      } else {
        try {
          filelist = config.initval.split(',').map((url, index) => {
            return {
              uid: `${index}`,
              name: url.slice(url.lastIndexOf('/') + 1),
              status: 'done',
              url: url,
              origin: true
            }
          })
        } catch (e) {
          filelist = []
        }
      }
    }
    let accept = ''
    let accepts = null
    let compress = false
    let maxFile = config.maxfile && config.maxfile > 0 ? config.maxfile : null
    if (config.compress === 'true' || config.compress === 'base64') {
      compress = true
      accepts = ['.jpg', '.png', '.gif', '.jpeg']
      accept = accepts.join(',')
      if (config.compress === 'base64') {
        maxFile = 1
      }
    } else if (config.suffix) {
      accepts = config.suffix.split(',').map(item => {
        if (!/^\./ig.test(item)) {
          item = '.' + item
        }
        return item
      })
      accept = accepts.join(',')
    }
    let rduri = config.rduri || ''
    if (window.GLOB.systemType === 'production') {
      rduri = config.proRduri || ''
    }
    this.setState({
      rduri,
      accept,
      accepts,
      filelist,
      compress,
      limit: config.limit || 2,
      maxFile: maxFile,
      fileType: config.fileType || 'text'
    })
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  onChange = ({ fileList }) => {
    fileList = fileList.map(item => {
      if (item.status === 'error' && /^<!DOCTYPE html>/.test(item.response)) {
        item.response = ''
      }
      return item
    })
    this.setState({filelist: fileList})
  }
  onRemove = file => {
    const files = this.state.filelist.filter(v => v.uid !== file.uid)
    this.setState({filelist: files})
    let vals = []
    files.forEach(item => {
      if (item.origin && item.url) {
        vals.push(item.url)
      } else if (!item.origin && item.status === 'done' && item.response) {
        vals.push(item.response)
      }
    })
    this.props.onChange(vals.join(','))
  }
  onUpdate = (url) => {
    let filelist = fromJS(this.state.filelist).toJS()
    if (filelist[filelist.length -1]) {
      filelist[filelist.length -1].status = 'done'
      filelist[filelist.length -1].response = url
      filelist[filelist.length -1].origin = false
    }
    filelist = filelist.filter(item => !!(item.url || item.response))
    let vals = []
    filelist.forEach(item => {
      if (item.origin && item.url) {
        vals.push(item.url)
      } else if (!item.origin && item.status === 'done' && item.response) {
        vals.push(item.response)
      }
    })
    this.setState({filelist})
    this.props.onChange(vals.join(','))
  }
  onFail = (msg) => {
    let filelist = this.state.filelist.map(item => {
      if (!item.url && !item.response && !item.status) {
        item.status = 'error'
      }
      return item
    })
    this.setState({filelist, showprogress: false, percent: 0})
    notification.warning({
      top: 92,
      message: msg || '文件上传失败!',
      duration: 5
    })
  }
  shardupload = (params) => {
    let param = params.chunks.shift()
    let form = new FormData()
    form.append('file', param.binary)
    form.append('fileMd5', params.file.fileMd5)
    form.append('shardingMd5', param.chunkMd5)
    form.append('baseDomain', service)
    form.append('rootPath', 'Content/images/upload/')
    form.append('fileName', params.file.fileName)
    form.append('fileExt', params.file.fileType)
    form.append('shardingCnt', param.chunks)
    form.append('shardingNo', param.chunk)
    form.append('LoginUID', sessionStorage.getItem('LoginUID') || '')
    form.append('UserID', sessionStorage.getItem('UserID') || '')
    Api.getLargeFileUpload(form).then(res => {
      if (res.status) {
        if (params.chunks.length > 0) {
          this.setState({
            percent: Math.floor(100 * (param.chunk / param.chunks))
          })
          this.shardupload(params)
        } else {
          if (res.urlPath) {
            this.onUpdate(res.urlPath)
          } else {
            this.onFail()
          }
          this.setState({
            percent: 100
          }, () => {
            setTimeout(() => {
              this.setState({
                showprogress: false,
                percent: 0
              })
            }, 200)
          })
        }
      } else {
        this.onFail(res.message)
      }
    })
  }
  getuuid = () => {
    let uuid = []
    let _options = '0123456789abcdefghigklmnopqrstuv'
    for (let i = 0; i < 19; i++) {
      uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
    }
    uuid = uuid.join('')
    return uuid
  }
  beforeUpload = (file) => {
    const { accepts, compress, limit, rduri } = this.state
    if (accepts && file.name) {
      let pass = false
      accepts.forEach(type => {
        if (new RegExp(type + '$', 'ig').test(file.name)) {
          pass = true
        }
      })
      if (!pass) {
        setTimeout(() => {
          this.onFail('文件格式错误!')
        }, 10)
        return false
      }
    }
    this.setState({
      showprogress: true,
      percent: 0
    })
    if (compress) {
      let reader = new FileReader()
      let fileSize = file.size / 1024 / 1024
      let compressRate = 0.9
      if (fileSize / limit > 5) {
        compressRate = 0.4
      } else if (fileSize / limit > 4) {
        compressRate = 0.5
      } else if (fileSize / limit > 3) {
        compressRate = 0.6
      } else if (fileSize / limit > 2) {
        compressRate = 0.7
      } else if (fileSize > limit) {
        compressRate = 0.8
      }
      reader.onload = (e) => {
        let img = new Image()
        let maxW = 640
        img.onload = () => {
          let cvs = document.createElement( 'canvas')
          let ctx = cvs.getContext( '2d')
          if (img.width > maxW) {
            img.height *= maxW / img.width
            img.width = maxW
          }
          cvs.width = img.width
          cvs.height = img.height
          ctx.clearRect(0, 0, cvs.width, cvs.height)
          ctx.drawImage(img, 0, 0, img.width, img.height)
          let param = {Base64Img: cvs.toDataURL('image/jpeg', compressRate)}
          if (this.props.config.compress === 'base64') {
            this.onUpdate(param.Base64Img)
            this.setState({
              percent: 100
            }, () => {
              setTimeout(() => {
                this.setState({
                  showprogress: false,
                  percent: 0
                })
              }, 200)
            })
          } else {
            if (rduri) {
              param.rduri = rduri
            }
            Api.fileuploadbase64(param).then(result => {
              if (result.status && result.Images) {
                let url = service + result.Images
                if (rduri) {
                  url = rduri.replace(/webapi(.*)$/, '') + result.Images
                }
                this.onUpdate(url)
                this.setState({
                  percent: 100
                }, () => {
                  setTimeout(() => {
                    this.setState({
                      showprogress: false,
                      percent: 0
                    })
                  }, 200)
                })
              } else {
                this.onFail(result.message)
              }
            })
          }
        }
        img.onerror = () => {
          this.onFail('图片读取失败!')
        }
        img.src = e.target.result
      }
      reader.onerror = () => {
        this.onFail('文件读取失败!')
      }
      reader.readAsDataURL(file)
      return false
    }
    // 兼容性的处理
    let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
    let chunkSize = 1024 * 1024 * 2                // 切片每次2M
    let chunks = Math.ceil(file.size / chunkSize)  // 切片总数
    let currentChunk = 0                           // 当前上传的chunk
    let spark = new SparkMD5.ArrayBuffer()         // 对arrayBuffer数据进行md5加密,产生一个md5字符串
    let chunkFileReader = new FileReader()         // 用于计算出每个chunkMd5
    let totalFileReader = new FileReader()         // 用于计算出总文件的fileMd5
    let params = {chunks: [], file: {}}            // 用于上传所有分片的md5信息
    params.file.fileName = file.name.replace(/\.{1}[^.]*$/ig, '')  // 文件名(去除后缀名)
    params.file.fileType = file.name.replace(/^.*\.{1}/ig, '')     // 文件类型
    params.file.fileSize = file.size                               // 文件大小
    params.file.fileChunks = chunks                                // 记录所有chunks的长度
    if (!/^[A-Za-z0-9]+$/.test(params.file.fileName)) {            // 文件名称含有英文及数字之外字符时,名称系统生成
      params.file.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
    }
    totalFileReader.readAsArrayBuffer(file)
    totalFileReader.onload = (e) => {   // 对整个totalFile生成md5
      spark.append(e.target.result)
      params.file.fileMd5 = spark.end() // 计算整个文件的fileMd5
      let _param = new FormData()
      _param.append('fileMd5', params.file.fileMd5)
      Api.getFilePreUpload(_param).then(res => {
        if (res.status && res.urlPath) {
          this.onUpdate(res.urlPath)
          this.setState({
            percent: 100
          }, () => {
            setTimeout(() => {
              this.setState({
                showprogress: false,
                percent: 0
              })
            }, 200)
          })
        } else if (res.shardings && res.shardings.length > 0) {
          res.shardings.forEach(shard => {
            if (shard.shardingNo && parseInt(shard.shardingNo) > currentChunk) {
              currentChunk = parseInt(shard.shardingNo)
            }
          })
          loadNext()
        } else {
          loadNext()
        }
      })
    }
    chunkFileReader.onload = (e) => {
      spark.append(e.target.result)      // 对每一片分片进行md5加密
      params.chunks[params.chunks.length - 1].chunkMd5 = spark.end() // 添加切片md5
      currentChunk++  // 每一次分片onload,currentChunk都需要增加,以便来计算分片的次数
      if (currentChunk < chunks) { // 当前切片总数没有达到总数时
        loadNext()
      } else {
        this.shardupload(params)
      }
    }
    chunkFileReader.onerror = () => {
      this.onFail('文件读取失败!')
    }
    totalFileReader.onerror = () => {
      this.onFail('文件读取失败!')
    }
    let loadNext = () => {
      let start = currentChunk * chunkSize              // 计算分片的起始位置
      let end = Math.min(file.size, start + chunkSize)  // 计算分片的结束位置
      let obj = {                                       // 每一个分片需要包含的信息
        chunk: currentChunk + 1,
        binary: file.slice(start, end),
        start: start,
        end: end,
        chunks
      }
      params.chunks.push(obj)
      chunkFileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
    }
    return false
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
    const { showprogress, percent, filelist, maxFile, fileType, accept } = this.state
    let uploadable = 'fileupload-form-container '
    if (maxFile && filelist.length >= maxFile) {
      uploadable += 'limit-fileupload'
    }
    const props = {
      name: 'file',
      disabled: showprogress,
      listType: fileType,
      fileList: filelist,
      action: null,
      accept: accept,
      method: 'post',
      multiple: false,
      onChange: this.onChange,
      onRemove: this.onRemove,
      beforeUpload: this.beforeUpload,
      className: uploadable
    }
    return (
      <Upload {...props}>
        {fileType !== 'picture-card' ? <Button>
          <UploadOutlined /> 点击上传
        </Button> : null}
        {fileType === 'picture-card' ? <span style={{whiteSpace: 'nowrap'}}>
          <UploadOutlined /> 点击上传
        </span> : null}
        {showprogress ? <Progress percent={percent} size="small" /> : null}
      </Upload>
    )
  }
}
export default FileUpload
src/tabviews/zshare/fileupload-pice/index.scss
New file
@@ -0,0 +1,53 @@
.fileupload-form-container {
  .ant-progress-small.ant-progress-line {
    position: absolute;
    bottom: -20px;
    left: 0px;
  }
  .ant-upload-select-picture-card {
    .ant-progress-small.ant-progress-line {
      bottom: 0px;
    }
  }
  .ant-upload-list-picture-card-container {
    width: 90px;
    height: 90px;
  }
  // .ant-upload-list-picture-card .ant-upload-list-item-undefined {
  //   border-color: #f5222d;
  //   .ant-upload-list-item-name {
  //     color: #f5222d;
  //   }
  // }
  .ant-upload-list-picture-card .ant-upload-list-item {
    width: 90px;
    height: 90px;
  }
  .ant-upload.ant-upload-select-picture-card {
    width: 90px;
    height: 90px;
  }
  a[href^="data"] {
    pointer-events: none;
    .anticon-eye-o {
      display: none;
    }
  }
}
.fileupload-form-container.limit-fileupload {
  > .ant-upload {
    display: inline;
    >.ant-upload {
      >input {
        display: none;
      }
      >button {
        display: none;
      }
    }
  }
  > .ant-upload-select-picture-card {
    display: none;
  }
}
src/tabviews/zshare/fileupload/index.jsx
@@ -177,30 +177,23 @@
    })
  }
  shardupload = (params) => {
    let param = params.chunks.shift()
  shardupload = (param) => {
    let form = new FormData()
    form.append('file', param.binary)
    form.append('fileMd5', params.file.fileMd5)
    form.append('shardingMd5', param.chunkMd5)
    form.append('fileMd5', param.fileMd5)
    form.append('shardingMd5', param.fileMd5)
    form.append('baseDomain', service)
    form.append('rootPath', 'Content/images/upload/')
    form.append('fileName', params.file.fileName)
    form.append('fileExt', params.file.fileType)
    form.append('shardingCnt', param.chunks)
    form.append('shardingNo', param.chunk)
    form.append('fileName', param.fileName)
    form.append('fileExt', param.fileType)
    form.append('shardingCnt', 1)
    form.append('shardingNo', 1)
    form.append('LoginUID', sessionStorage.getItem('LoginUID') || '')
    form.append('UserID', sessionStorage.getItem('UserID') || '')
    Api.getLargeFileUpload(form).then(res => {
      if (res.status) {
        if (params.chunks.length > 0) {
          this.setState({
            percent: Math.floor(100 * (param.chunk / param.chunks))
          })
          this.shardupload(params)
        } else {
          if (res.urlPath) {
            this.onUpdate(res.urlPath)
          } else {
@@ -216,7 +209,6 @@
              })
            }, 200)
          })
        }
      } else {
        this.onFail(res.message)
      }
@@ -356,31 +348,25 @@
    }
    // 兼容性的处理
    let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
    let chunkSize = 1024 * 1024 * 2                // 切片每次2M
    let chunks = Math.ceil(file.size / chunkSize)  // 切片总数
    let currentChunk = 0                           // 当前上传的chunk
    let spark = new SparkMD5.ArrayBuffer()         // 对arrayBuffer数据进行md5加密,产生一个md5字符串
    let chunkFileReader = new FileReader()         // 用于计算出每个chunkMd5
    let totalFileReader = new FileReader()         // 用于计算出总文件的fileMd5
    let params = {chunks: [], file: {}}            // 用于上传所有分片的md5信息
    let param = {}
    params.file.fileName = file.name.replace(/\.{1}[^.]*$/ig, '')  // 文件名(去除后缀名)
    params.file.fileType = file.name.replace(/^.*\.{1}/ig, '')     // 文件类型
    params.file.fileSize = file.size                               // 文件大小
    params.file.fileChunks = chunks                                // 记录所有chunks的长度
    param.fileName = file.name.replace(/\.{1}[^.]*$/ig, '')  // 文件名(去除后缀名)
    param.fileType = file.name.replace(/^.*\.{1}/ig, '')     // 文件类型
    if (!/^[A-Za-z0-9]+$/.test(params.file.fileName)) {            // 文件名称含有英文及数字之外字符时,名称系统生成
      params.file.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
    if (!/^[A-Za-z0-9]+$/.test(param.fileName)) {            // 文件名称含有英文及数字之外字符时,名称系统生成
      param.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
    }
    totalFileReader.readAsArrayBuffer(file)
    totalFileReader.onload = (e) => {   // 对整个totalFile生成md5
      spark.append(e.target.result)
      params.file.fileMd5 = spark.end() // 计算整个文件的fileMd5
      param.fileMd5 = spark.end()       // 计算整个文件的fileMd5
      param.binary = file
      let _param = new FormData()
      _param.append('fileMd5', params.file.fileMd5)
      _param.append('fileMd5', param.fileMd5)
      
      Api.getFilePreUpload(_param).then(res => {
        if (res.status && res.urlPath) {
@@ -395,54 +381,14 @@
              })
            }, 200)
          })
        } else if (res.shardings && res.shardings.length > 0) {
          res.shardings.forEach(shard => {
            if (shard.shardingNo && parseInt(shard.shardingNo) > currentChunk) {
              currentChunk = parseInt(shard.shardingNo)
            }
          })
          loadNext()
        } else {
          loadNext()
          this.shardupload(param)
        }
      })
    }
    chunkFileReader.onload = (e) => {
      spark.append(e.target.result)      // 对每一片分片进行md5加密
      params.chunks[params.chunks.length - 1].chunkMd5 = spark.end() // 添加切片md5
      currentChunk++  // 每一次分片onload,currentChunk都需要增加,以便来计算分片的次数
      if (currentChunk < chunks) { // 当前切片总数没有达到总数时
        loadNext()
      } else {
        this.shardupload(params)
      }
    }
    chunkFileReader.onerror = () => {
      this.onFail('文件读取失败!')
    }
    totalFileReader.onerror = () => {
      this.onFail('文件读取失败!')
    }
    let loadNext = () => {
      let start = currentChunk * chunkSize              // 计算分片的起始位置
      let end = Math.min(file.size, start + chunkSize)  // 计算分片的结束位置
      let obj = {                                       // 每一个分片需要包含的信息
        chunk: currentChunk + 1,
        binary: file.slice(start, end),
        start: start,
        end: end,
        chunks
      }
      params.chunks.push(obj)
      chunkFileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
    }
    return false
src/tabviews/zshare/fileupload/index.scss
@@ -14,12 +14,12 @@
    width: 90px;
    height: 90px;
  }
  .ant-upload-list-picture-card .ant-upload-list-item-undefined {
    border-color: #f5222d;
    .ant-upload-list-item-name {
      color: #f5222d;
    }
  }
  // .ant-upload-list-picture-card .ant-upload-list-item-undefined {
  //   border-color: #f5222d;
  //   .ant-upload-list-item-name {
  //     color: #f5222d;
  //   }
  // }
  .ant-upload-list-picture-card .ant-upload-list-item {
    width: 90px;
    height: 90px;
src/templates/zshare/formconfig.jsx
@@ -1405,7 +1405,7 @@
      precision: 0,
      label: '比例',
      initVal: card.ratio || 85,
      tooltip: '小于100为宽度(或高度)百分比,大于100为像素值。',
      tooltip: '模态框或抽屉的宽度,小于100为窗口宽度(或高度)百分比,大于100为像素值。',
      required: true
    },
    {
src/utils/utils-custom.js
@@ -85,6 +85,91 @@
  }
  /**
   * @description 获取下级模块
   * @return {String}  selfId  当前组件id
   */
  static getAnchors (components, selfId) {
    let modules = components.map(item => {
      if (item.uuid === selfId) {
        return {
          children: null
        }
      } else if (item.type === 'tabs') {
        let _item = {
          type: 'tabs',
          value: item.uuid,
          label: item.name,
          children: item.subtabs.map(f_tab => {
            let subItem = {
              type: 'tab',
              value: f_tab.uuid,
              label: f_tab.label,
              children: this.getSubModules(f_tab.components, selfId)
            }
            if (!subItem.children || subItem.children.length === 0) {
              return {children: null}
            }
            return subItem
          })
        }
        _item.children = _item.children.filter(t => t.children !== null)
        if (_item.children.length === 0) {
          return {children: null}
        }
        return _item
      } else if (item.type === 'group') {
        let _item = {
          value: item.uuid,
          label: item.name,
          children: item.components.map(f_tab => {
            if (f_tab.uuid === selfId) {
              return {
                children: null
              }
            } else if (f_tab.format) {
              return {
                value: f_tab.uuid,
                label: f_tab.name
              }
            }
            return {
              children: null
            }
          })
        }
        _item.children = _item.children.filter(t => t.children !== null)
        if (_item.children.length === 0) {
          return {children: null}
        }
        return _item
      } else if (!['login', 'navbar', 'topbar', 'tabs', 'search', 'group', 'balcony'].includes(item.type)) { // 数据格式,存在数据源
        return {
          value: item.uuid,
          label: item.name
        }
      } else {
        return {
          children: null
        }
      }
    })
    modules = modules.filter(mod => mod.children !== null)
    if (modules.length === 0) {
      return null
    }
    return modules
  }
  /**
   * @description 获取上级模块
   * @return {String}  selfId  当前组件id
   */