king
2021-01-19 f9ba5907ddff56da7c38b1c7d5a9c5ba6aadfa05
2021-01-19
17个文件已修改
13个文件已添加
3个文件已删除
1162 ■■■■■ 已修改文件
src/assets/css/main.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/editor.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/editor/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/editorcontent/index.jsx 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/editorcontent/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/index.jsx 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/index.scss 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupcomponents/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupcomponents/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/usercomponent/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modelsource/option.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/popview/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/asyncButtonComponent.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/asyncButtonComponent.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/editor/braft-editor/index.jsx 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/editor/braft-editor/index.scss 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/group/normal-group/index.jsx 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/braftContent/index.jsx 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/braftContent/index.scss 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/tabtransfer/index.jsx 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/billprint/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss
@@ -258,7 +258,7 @@
.ant-modal.popview-modal {
  top: 70px;
  .ant-modal-body {
    min-height: 250px;
    min-height: 200px;
    max-height: calc(100vh - 210px);
    overflow-y: auto;
  }
@@ -282,7 +282,7 @@
    top: 70px;
  }
  .ant-modal-body {
    min-height: 250px;
    min-height: 200px;
    max-height: calc(100vh - 210px);
    overflow-y: auto;
  }
src/assets/mobimg/editor.png
src/components/editor/index.jsx
@@ -38,6 +38,8 @@
    if (this.props['data-__meta']) {
      initVal = this.props['data-__meta'].initialValue || null
    } else if (this.props.defaultValue) {
      initVal = this.props.defaultValue || null
    }
    if (this.props.Item && this.props.Item.encryption === 'true') {
src/menu/components/editor/braft-editor/editorcontent/index.jsx
New file
@@ -0,0 +1,82 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const Editor = asyncComponent(() => import('@/components/editor'))
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    html: null
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  trigger = () => {
    const { config } = this.props
    this.setState({
      visible: true,
      html: config.html || null
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    const { html } = this.state
    this.setState({
      visible: false
    })
    this.props.updateConfig({...config, html})
  }
  onChange = (val) => {
    this.setState({
      html: val
    })
  }
  render () {
    const { config } = this.props
    const { visible, dict, html } = this.state
    if (!config) return null
    return (
      <div className="model-menu-edit-content-wrap">
        {config.wrap.datatype === 'static' ? <Icon title="内容编辑" type="form" onClick={() => this.trigger()} /> : null}
        {config.wrap.datatype !== 'static' ? <Icon title="内容编辑" style={{color: '#eeeeee', cursor: 'not-allowed'}} type="form"/> : null}
        <Modal
          wrapClassName="popview-modal model-menu-edit-content-form"
          title="内容编辑"
          visible={visible}
          width={950}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <Editor defaultValue={html} onChange={this.onChange} />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/menu/components/editor/braft-editor/editorcontent/index.scss
New file
@@ -0,0 +1,14 @@
.model-menu-edit-content-wrap {
  display: inline-block;
  >.anticon-form {
    color: purple;
  }
}
.model-menu-edit-content-form {
  .normal-braft-editor {
    border: 1px solid #d9d9d9;
    border-radius: 4px;
    overflow-x: hidden;
  }
}
src/menu/components/editor/braft-editor/index.jsx
New file
@@ -0,0 +1,196 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { Icon, Popover } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import MKEmitter from '@/utils/events.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import './index.scss'
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const EditorContent = asyncIconComponent(() => import('./editorcontent'))
const BraftContent = asyncComponent(() => import('@/tabviews/custom/components/share/braftContent'))
class TableCardEditComponent extends Component {
  static propTpyes = {
    card: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        tabId: card.tabId || '',
        parentId: card.parentId || '',
        dataName: card.dataName || '',
        format: 'object',   // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: false,  // 组件属性 - 数据是否可切换
        width: card.width || 24,
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, width: card.width || 24, encryption: 'true' },
        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        columns: [],
        scripts: [],
        html: ''
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _card.wrap.name = card.name
        _card.style = config.style
        _card.headerStyle = config.headerStyle
        _card.html = config.html
      }
      this.setState({
        card: _card
      })
      this.props.updateConfig(_card)
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  /**
   * @description 卡片行外层信息更新(数据源,样式等)
   */
  updateComponent = (component) => {
    this.setState({
      card: component
    })
    component.width = component.wrap.width
    component.name = component.wrap.name
    this.props.updateConfig(component)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds[0] !== card.uuid) return
    let _card = {}
    if (comIds.length === 1) {
      _card = {...card, style}
    } else {
      return
    }
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  /**
   * @description 更新搜索条件配置信息
   */
  updateconfig = (config) => {
    this.setState({
      card: config
    })
    this.props.updateConfig(config)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card)
    }
  }
  render() {
    const { card } = this.state
    return (
      <div className="menu-normal-editor-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader defaultshow="hidden" hideSearch="true" config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="normaltable" card={card}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <UserComponent config={card}/>
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
            <EditorContent config={card} updateConfig={this.updateComponent}/>
            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
            {card.wrap.datatype === 'static' ? <Icon style={{color: '#eeeeee', cursor: 'not-allowed'}} type="setting"/> : null}
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <BraftContent
          value={card.wrap.datatype !== 'static' ? '<p class="empty-content">富文本</p>' : card.html}
          encryption="false"
        />
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    menu: state.customMenu
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(TableCardEditComponent)
src/menu/components/editor/braft-editor/index.scss
New file
@@ -0,0 +1,36 @@
.menu-normal-editor-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 100px;
  .anticon-tool {
    position: absolute;
    z-index: 2;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .empty-content {
    text-align: center;
    font-size: 30px;
    margin: 0;
    line-height: 90px;
    color: #bcbcbc;
  }
}
.menu-normal-table-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-normal-table-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/editor/braft-editor/wrapsetting/index.jsx
New file
@@ -0,0 +1,83 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import './index.scss'
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    wrap: null
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    this.setState({wrap: fromJS(config.wrap).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  editDataSource = () => {
    this.setState({
      visible: true
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    this.verifyRef.handleConfirm().then(res => {
      this.setState({
        wrap: res,
        visible: false
      })
      this.props.updateConfig({...config, wrap: res})
    })
  }
  render () {
    const { config } = this.props
    const { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title="富文本设置"
          visible={visible}
          width={700}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            config={config}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/menu/components/editor/braft-editor/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,199 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    config: PropTypes.object,    // 卡片行信息
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    roleList: [],
    datatype: this.props.wrap.datatype || 'dynamic'
  }
  UNSAFE_componentWillMount () {
    let roleList = sessionStorage.getItem('sysRoles')
    if (roleList) {
      try {
        roleList = JSON.parse(roleList)
      } catch {
        roleList = []
      }
    } else {
      roleList = []
    }
    this.setState({roleList})
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  changeDataType = (e) => {
    this.setState({datatype: e.target.value})
  }
  render() {
    const { wrap, config } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList, datatype } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label="标题">
                {getFieldDecorator('title', {
                  initialValue: wrap.title || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="用于组件间的区分。">
                  <Icon type="question-circle" />
                  组件名称
                </Tooltip>
              }>
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '组件名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: wrap.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择静态值,无需配置数据源。">
                  <Icon type="question-circle" />
                  数据来源
                </Tooltip>
              }>
                {getFieldDecorator('datatype', {
                  initialValue: datatype
                })(
                  <Radio.Group onChange={this.changeDataType}>
                    <Radio value="dynamic">动态</Radio>
                    <Radio value="static">静态</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {datatype === 'dynamic' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择动态值时,需设置文本字段才可生效。">
                  <Icon type="question-circle" />
                  文本字段
                </Tooltip>
              }>
                {getFieldDecorator('field', {
                  initialValue: wrap.field || ''
                })(
                  <Select>
                    {config.columns.map(option =>
                      <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col> : null}
            {datatype === 'dynamic' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="从数据源获取的数据是否需要解码。">
                  <Icon type="question-circle" />
                  数据解码
                </Tooltip>
              }>
                {getFieldDecorator('encryption', {
                  initialValue: wrap.encryption || 'true'
                })(
                  <Radio.Group>
                    <Radio value="true">是</Radio>
                    <Radio value="false">否</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
                  initialValue: wrap.blacklist || []
                })(
                  <Select
                    showSearch
                    mode="multiple"
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    {roleList.map(option =>
                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,15 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
  .color-sketch-block {
    position: relative;
    top: 7px;
  }
}
src/menu/components/group/groupcomponents/card.jsx
@@ -10,6 +10,7 @@
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -57,6 +58,8 @@
      return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'table' && card.subtype === 'normaltable') {
      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
src/menu/components/group/groupcomponents/index.jsx
@@ -103,6 +103,7 @@
        line: '折线图',
        pie: '饼图',
        table: '表格',
        editor: '富文本',
        card: '卡片'
      }
      let i = 1
src/menu/components/share/usercomponent/index.jsx
@@ -53,6 +53,7 @@
    _config.search = config.search || []
    _config.cols = config.cols || []
    _config.plot = config.plot || {}
    _config.html = config.html || ''
    _config.width = _config.wrap.width || _config.plot.width || config.width || 24
src/menu/components/tabs/tabcomponents/card.jsx
@@ -13,6 +13,7 @@
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -66,6 +67,8 @@
      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
src/menu/components/tabs/tabcomponents/index.jsx
@@ -96,6 +96,7 @@
        search: '搜索',
        table: '表格',
        group: '分组',
        editor: '富文本',
        card: '卡片'
      }
      let i = 1
src/menu/datasource/index.jsx
@@ -115,7 +115,7 @@
    return (
      <div className="model-datasource">
        <Icon type="setting" onClick={() => this.editDataSource()} />
        <Icon type="setting" title="数据源" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title={'数据源配置'}
src/menu/menushell/card.jsx
@@ -13,6 +13,7 @@
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -66,6 +67,8 @@
      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
  return (
src/menu/menushell/index.jsx
@@ -89,6 +89,7 @@
        search: '搜索',
        table: '表格',
        group: '分组',
        editor: '富文本',
        card: '卡片'
      }
      let i = 1
src/menu/modelsource/option.jsx
@@ -9,6 +9,7 @@
import TableCard from '@/assets/mobimg/table-card.png'
import NormalTable from '@/assets/mobimg/normal-table.png'
import Pie from '@/assets/mobimg/pie.png'
import Editor from '@/assets/mobimg/editor.png'
import Pie1 from '@/assets/mobimg/ring.png'
import Pie2 from '@/assets/mobimg/nightingale.png'
import Mainsearch from '@/assets/mobimg/mainsearch.png'
@@ -27,6 +28,7 @@
  { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '条形图', width: 24 },
  { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '饼图', width: 12 },
  { type: 'menu', url: Pie1, component: 'pie', subtype: 'ring', title: '环图', width: 12 },
  { type: 'menu', url: Editor, component: 'editor', subtype: 'brafteditor', title: '富文本', width: 24 },
  { type: 'menu', url: Pie2, component: 'pie', subtype: 'nightingale', title: '南丁格尔图', width: 12 },
  { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '分组', width: 24, forbid: ['billPrint'] },
]
src/menu/popview/index.jsx
@@ -474,7 +474,7 @@
    config.components.forEach(item => {
      if (error) return
      if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return
      if ((item.subtype === 'propcard' || item.subtype === 'brafteditor') && item.wrap.datatype === 'static') return
      if (item.setting) {
        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
src/tabviews/custom/components/card/data-card/asyncButtonComponent.jsx
File was deleted
src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx
File was deleted
src/tabviews/custom/components/card/table-card/asyncButtonComponent.jsx
File was deleted
src/tabviews/custom/components/editor/braft-editor/index.jsx
New file
@@ -0,0 +1,196 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const BraftContent = asyncComponent(() => import('@/tabviews/custom/components/share/braftContent'))
const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
class BraftEditorContent extends Component {
  static propTpyes = {
    BID: PropTypes.any,              // 父级Id
    data: PropTypes.array,           // 统一查询数据
    config: PropTypes.object,        // 组件配置信息
    mainSearch: PropTypes.any,       // 外层搜索条件
    menuType: PropTypes.any,         // 菜单类型
  }
  state = {
    BID: '',                   // 上级ID
    config: null,              // 图表配置信息
    loading: false,            // 数据加载状态
    sync: false,               // 是否统一请求数据
    data: {}                   // 数据
  }
  UNSAFE_componentWillMount () {
    const { data, initdata, BID } = this.props
    let _config = fromJS(this.props.config).toJS()
    let _data = {}
    let _sync = false
    if (_config.setting && _config.wrap.datatype !== 'static') {
      _sync = _config.setting.sync === 'true'
      if (_sync && data) {
        _data = data[_config.dataName] || {}
        if (_data && Array.isArray(_data)) {
          _data = _data[0] || {}
        }
        _sync = false
      } else if (_sync && initdata) {
        _data = initdata || {}
        if (_data && Array.isArray(_data)) {
          _data = _data[0] || {}
        }
        _sync = false
      }
    } else {
      _data = {}
    }
    this.setState({
      sync: _sync,
      data: _data,
      BID: BID || '',
      config: _config,
      arr_field: _config.columns.map(col => col.field).join(','),
    }, () => {
      if (_config.wrap.datatype !== 'static' && _config.setting && _config.setting.sync !== 'true' && _config.setting.onload === 'true') {
        this.loadData()
      }
    })
  }
  componentDidMount () {
    MKEmitter.addListener('reloadData', this.reloadData)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('reloadData', this.reloadData)
  }
  /**
   * @description 图表数据更新,刷新内容
   */
  UNSAFE_componentWillReceiveProps (nextProps) {
    const { sync, config } = this.state
    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
      let _data = {}
      if (nextProps.data && nextProps.data[config.dataName]) {
        _data = nextProps.data[config.dataName]
        if (_data && Array.isArray(_data)) {
          _data = _data[0]
        }
      }
      this.setState({sync: false, data: _data})
    } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
      if (config.wrap.datatype !== 'static' && config.setting.syncRefresh === 'true') {
        this.setState({}, () => {
          this.loadData()
        })
      }
    }
  }
  reloadData = (menuId) => {
    const { config } = this.state
    if (menuId !== config.uuid) return
    this.loadData()
  }
  async loadData () {
    const { mainSearch, menuType } = this.props
    const { config, arr_field, BID } = this.state
    if (config.wrap.datatype === 'static') {
      this.setState({
        data: {},
        loading: false
      })
      return
    } else if (config.setting.supModule && !BID) { // BID 不存在时,不做查询
      this.setState({
        data: {},
        loading: false
      })
      return
    }
    let searches = []
    if (mainSearch && mainSearch.length > 0) { // 主表搜索条件
      let keys = searches.map(item => item.key)
      mainSearch.forEach(item => {
        if (!keys.includes(item.key)) {
          searches.push(item)
        }
      })
    }
    this.setState({
      loading: true
    })
    let _orderBy = config.setting.order || ''
    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID, menuType)
    let result = await Api.genericInterface(param)
    if (result.status) {
      let _data = result.data && result.data[0] ? result.data[0] : {}
      this.setState({
        data: _data,
        loading: false
      })
    } else {
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
  render() {
    const { config, loading, data } = this.state
    return (
      <div className="custom-braft-editor-box" style={{...config.style}}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
            <Spin />
          </div> : null
        }
        <NormalHeader config={config}/>
        <BraftContent
          value={config.wrap.datatype !== 'static' ? (data[config.wrap.field] || '') : config.html}
          encryption={config.wrap.datatype !== 'static' ? config.wrap.encryption : 'false'}
        />
      </div>
    )
  }
}
export default BraftEditorContent
src/tabviews/custom/components/editor/braft-editor/index.scss
New file
@@ -0,0 +1,76 @@
.custom-braft-editor-box {
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 20px;
  position: relative;
  .card-row-list::after {
    content: ' ';
    display: block;
    clear: both;
  }
  .card-row-list {
    .card-item-box {
      background-color: #ffffff;
      transition: all 0.3s;
    }
    >.active >.card-item-box {
      border-color: #1890ff!important;
      box-shadow: 0 0 4px #1890ff;
    }
  }
  .card-row-list.radio, .card-row-list.checkbox {
    >.ant-col:not(.active):not(.selected):hover {
      >.card-item-box {
        border-color: #69c0ff!important;
        box-shadow: 0 0 4px #69c0ff!important;
      }
    }
  }
  .card-row-list.true {
    >.ant-col:hover {
      >.card-item-box {
        z-index: 1;
        transform: scale(1.05);
      }
    }
  }
  .card-item-box {
    position: relative;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
    transition: all 0.3s;
  }
  .loading-mask {
    position: absolute;
    left: 40px;
    top: 0;
    right: 40px;
    bottom: 0px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: justify;
    z-index: 1;
    .ant-spin-blur {
      position: absolute;
      width: 100%;
      height: 100%;
      opacity: 0.5;
      background: #ffffff;
    }
  }
}
.custom-braft-editor-box::after {
  content: ' ';
  display: block;
  clear: both;
}
src/tabviews/custom/components/group/normal-group/index.jsx
@@ -18,6 +18,7 @@
const DataCard = asyncComponent(() => import('@/tabviews/custom/components/card/data-card'))
const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
class TabTransfer extends Component {
  static propTpyes = {
@@ -45,7 +46,7 @@
      if (item.type === 'tabs') return
      if (!item.setting || item.setting.interType !== 'system') return
      if (!item.format || (item.subtype === 'propcard' && item.wrap.datatype === 'static')) return
      if (!item.format) return
      if (item.dataName && (!item.pageable || (item.pageable && !item.setting.laypage)) && item.setting.onload === 'true' && item.setting.sync === 'true') {
        let param = this.getDefaultParam(item, _mainSearch)
@@ -267,6 +268,12 @@
            <TableCard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
          </Col>
        )
      } else if (item.type === 'editor') {
        return (
          <Col span={item.width} key={item.uuid}>
            <BraftEditor config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
          </Col>
        )
      } else {
        return null
      }
src/tabviews/custom/components/share/braftContent/index.jsx
New file
@@ -0,0 +1,58 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import './index.scss'
class BraftContent extends Component {
  static propTpyes = {
    value: PropTypes.any,       // 内容
    encryption: PropTypes.any,  // 是否解码
  }
  state = {
    html: ''
  }
  UNSAFE_componentWillMount () {
    const { encryption, value } = this.props
    let html = value
    if (encryption === 'true' && html) {
      try {
        html = window.decodeURIComponent(window.atob(html))
      } catch {
        html = value
      }
    }
    this.setState({html})
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!is(fromJS(this.props), fromJS(nextProps))) {
      const { encryption, value } = nextProps
      let html = value
      if (encryption === 'true' && html) {
        try {
          html = window.decodeURIComponent(window.atob(html))
        } catch {
          html = value
        }
      }
      this.setState({html})
    }
  }
  render() {
    const { html } = this.state
    return (
      <div className="braft-content" dangerouslySetInnerHTML={{ __html: html }}></div>
    )
  }
}
export default BraftContent
src/tabviews/custom/components/share/braftContent/index.scss
New file
@@ -0,0 +1,25 @@
.braft-content {
  .media-wrap {
    max-width: 100%;
  }
  img {
    max-width: 100%;
  }
  video {
    max-width: 100%;
  }
  table {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
    margin: 10px 0px;
    tr:first-child {
      background-color: #f0f0f0;
    }
    td, th {
      padding: 5px 14px;
      font-size: 16px;
      border: 1px solid #ddd;
    }
  }
}
src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -21,6 +21,7 @@
const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
const NormalGroup = asyncComponent(() => import('@/tabviews/custom/components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
class TabTransfer extends Component {
  static propTpyes = {
@@ -58,7 +59,7 @@
      if (item.type === 'tabs' || item.type === 'group') return
      if (!item.setting || item.setting.interType !== 'system') return
      if (!item.format || (item.subtype === 'propcard' && item.wrap.datatype === 'static')) return
      if (!item.format) return
      if (item.dataName && (!item.pageable || (item.pageable && !item.setting.laypage)) && item.setting.onload === 'true' && item.setting.sync === 'true') {
        let param = this.getDefaultParam(item, _mainSearch)
@@ -304,6 +305,12 @@
            <NormalGroup config={item} bids={bids} mainSearch={mainSearch} menuType={menuType} />
          </Col>
        )
      } else if (item.type === 'editor') {
        return (
          <Col span={item.width} key={item.uuid}>
            <BraftEditor config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} />
          </Col>
        )
      } else {
        return null
      }
src/tabviews/custom/index.jsx
@@ -26,6 +26,7 @@
const MainSearch = asyncComponent(() => import('@/tabviews/zshare/topSearch'))
const NormalTable = asyncComponent(() => import('./components/table/normal-table'))
const NormalGroup = asyncComponent(() => import('./components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('./components/editor/braft-editor'))
const SettingComponent = asyncComponent(() => import('@/tabviews/zshare/settingcomponent'))
const PagemsgComponent = asyncComponent(() => import('@/tabviews/zshare/pageMessage'))
@@ -482,8 +483,12 @@
        return component
      }
      if ((component.subtype === 'propcard' || component.subtype === 'brafteditor') && component.wrap.datatype === 'static') {
        component.format = ''
      }
      if (!component.setting) return component // 不使用系统函数时
      if (!component.format || (component.subtype === 'propcard' && component.wrap.datatype === 'static')) return component // 没有动态数据  数据格式 array 或 object
      if (!component.format) return component  // 没有动态数据  数据格式 array 或 object
      if (component.setting.interType !== 'system') { // 不使用系统函数时
        component.setting.sync = 'false'
        component.setting.laypage = component.setting.laypage === 'true'
@@ -822,6 +827,12 @@
            <NormalGroup config={item} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
          </Col>
        )
      } else if (item.type === 'editor') {
        return (
          <Col span={item.width} key={item.uuid}>
            <BraftEditor config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
          </Col>
        )
      } else {
        return null
      }
src/views/billprint/index.jsx
@@ -21,6 +21,7 @@
const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table'))
const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
class BillPrint extends Component {
  state = {
@@ -153,9 +154,13 @@
          if (component.action) component.action = []
          if (component.search) component.search = []
          component.data = [] // 初始化数据为空
          if ((component.subtype === 'propcard' || component.subtype === 'brafteditor') && component.wrap.datatype === 'static') {
            component.format = ''
          }
    
          if (!component.setting) return component // 不使用系统函数时
          if (!component.format || (component.subtype === 'propcard' && component.wrap.datatype === 'static')) return component // 没有动态数据  数据格式 array 或 object
          if (!component.format) return component  // 没有动态数据  数据格式 array 或 object
          if (component.setting.interType !== 'system') { // 不使用系统函数时
            component.setting.sync = 'false'
            return component
@@ -350,7 +355,7 @@
      let _results = results.filter(Boolean)
      let comps = components.map(item => {
        if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return item
        if (!item.format) return item
        _results.forEach(res => {
          if (res.componentId === item.uuid && res.data) {
@@ -566,6 +571,12 @@
            <NormalTable config={item} initdata={item.data} mainSearch={[]} menuType="" />
          </Col>
        )
      } else if (item.type === 'editor') {
        return (
          <Col span={item.width} key={item.uuid}>
            <BraftEditor config={item} initdata={item.data} mainSearch={[]} menuType="" />
          </Col>
        )
      } else {
        return null
      }
src/views/menudesign/index.jsx
@@ -769,7 +769,7 @@
    config.components.forEach(item => {
      if (error) return
      if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return
      if ((item.subtype === 'propcard' || item.subtype === 'brafteditor') && item.wrap.datatype === 'static') return
      if (item.setting) {
        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {