king
2023-02-03 b59af63c6742fa48de8fcdba67c9b8dc2e42bf94
Merge branch 'develop'
3个文件已删除
5个文件已添加
114个文件已修改
5012 ■■■■■ 已修改文件
public/options.json 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/viewstyle.scss 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.jsx 115 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/resetpwd/index.jsx 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/resetPassword/index.jsx 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/resetPassword/index.scss 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/resetPassword/resetpwd/index.jsx 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/en-US/login.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/en-US/main.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/zh-CN/login.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/zh-CN/main.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/index.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/options.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/options.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/options.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-G6/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/code/sandbox/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/options.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/simple-form/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/step-form/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/tab-form/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/index.jsx 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/iframe/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/index.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/index.scss 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/options.jsx 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/formconfig.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/edit-table/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/timeline/normal-timeline/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/normalCss/index.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/normalCss/index.scss 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecombcontroller/styleInput/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/styleInput/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/styleInput/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/tablenodes/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/search/single-search/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/tabcomponents/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/tabcomponents/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/card.jsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/normal-login/index.jsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/normal-login/options.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/card.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/basetable/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/balcony/index.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardItem/index.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardItem/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/account/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/assistTable/index.jsx 734 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/assistTable/index.scss 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/index.jsx 413 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/index.scss 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/voucherTable/index.jsx 1027 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/voucherTable/index.scss 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/popview/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/changeuserbutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/excelInbutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/exceloutbutton/index.jsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/newpagebutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/normalbutton/index.jsx 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/popupbutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/printbutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkCheckCard/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/tablenodes/index.jsx 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/actionform/index.jsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/fieldscomponent/index.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/modaleditable/index.jsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/modaleditable/index.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/submutilform/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/billprint/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/sidemenu/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/sidemenu/thdmenuplus/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.jsx 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/loginform.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/popview/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/systemfunc/sidemenu/config.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/systemfunc/sidemenu/index.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tabledesign/popview/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/options.json
@@ -1,12 +1,12 @@
{
  "appId": "202108312122504607B107A83F55B40C98CCF",
  "appkey": "20210831212235413F287EC3BF489424496C8",
  "appId": "201912040924165801464FF1788654BC5AC73",
  "appkey": "20191106103859640976D6E924E464D029CF0",
  "mainSystemApi": "http://sso.mk9h.cn/cloud/webapi/dostars",
  "systemType": "",
  "externalDatabase": "",
  "lineColor": "",
  "filter": "false",
  "defaultApp": "mkindustry",
  "defaultApp": "mk",
  "defaultLang": "zh-CN",
  "WXAppID": "",
  "WXminiAppID": "",
@@ -17,6 +17,6 @@
  "transfer": "false",
  "keepPassword": "true",
  "platforms": ["H5", "wechat", "android", "ios", "wxMiniProgram"],
  "host": "http://demo.mk9h.cn",
  "service": "erp_new/"
  "host": "http://qingqiumarket.cn",
  "service": "MKWMS/"
}
src/assets/css/main.scss
@@ -488,4 +488,8 @@
  display: flex;
  flex: auto;
  min-height: 100%;
}
.component-wrap >.ant-col {
  min-height: 0;
}
src/assets/css/viewstyle.scss
@@ -26,19 +26,6 @@
          }
        }
      }
      .ant-tabs-nav .ant-tabs-tab-active {
        color: $color6;
      }
      .ant-tabs-nav .ant-tabs-ink-bar {
        background-color: $color6;
      }
      .ant-tabs-nav .ant-tabs-tab:active {
        color: $color7;
      }
      .ant-tabs-nav .ant-tabs-tab:hover {
        color: $color5;
      }
    }
    >.mk-breadview-wrap {
      >.ant-breadcrumb {
@@ -205,6 +192,19 @@
    }
  }
  .ant-tabs-nav .ant-tabs-tab-active {
    color: $color6;
  }
  .ant-tabs-nav .ant-tabs-ink-bar {
    background-color: $color6;
  }
  .ant-tabs-nav .ant-tabs-tab:active {
    color: $color7;
  }
  .ant-tabs-nav .ant-tabs-tab:hover {
    color: $color5;
  }
  // 系统样式修改
  .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
    background-color: $color1;
@@ -361,6 +361,12 @@
      background-color: $color6;
    }
  }
  .ant-tree li .ant-tree-node-content-wrapper:hover {
    background-color: $color1;
  }
  .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
    background-color: $color2;
  }
}
body.hidden-split-line #root { // 去除登录页分割线
src/components/header/index.jsx
@@ -3,7 +3,6 @@
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import moment from 'moment'
import { Dropdown, Menu, Modal, notification, Switch, Input } from 'antd'
import { SearchOutlined, DownOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'
@@ -16,15 +15,13 @@
import Api from '@/api'
import MKEmitter from '@/utils/events.js'
import options from '@/store/options.js'
import zhCN from '@/locales/zh-CN/main.js'
import enUS from '@/locales/en-US/main.js'
import Utils from '@/utils/utils.js'
import avatar from '@/assets/img/avatar.jpg'
import './index.scss'
const { confirm } = Modal
const { Search } = Input
const Resetpwd = asyncComponent(() => import('./resetpwd'))
const Resetpwd = asyncComponent(() => import('@/components/resetPassword'))
const LoginForm = asyncComponent(() => import('./loginform'))
class Header extends Component {
@@ -33,9 +30,6 @@
  }
  state = {
    menulist: null, // 一级菜单
    visible: false, // 修改密码模态框
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    confirmLoading: false,
    userName: '',
    fullName: '',
    logourl: window.GLOB.mainlogo,
@@ -57,60 +51,12 @@
  changePassword = () => {
    // 点击修改密码,显示弹窗
    this.setState({
      visible: true
    })
  }
  resetPwdSubmit = () => {
    if (!this.formRef) return
    this.formRef.handleConfirm().then(res => {
      this.setState({
        confirmLoading: true
      })
      let _param = {
        func: 's_PwdUpt',
        LText: `select '${res.originpwd}','${res.password}'`
      }
      _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 时间戳
      _param.LText = Utils.formatOptions(_param.LText)                   // 关键字符替换,base64加密
      _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5密钥
      Api.getSystemConfig(_param).then(result => {
        this.setState({
          visible: !result.status,
          confirmLoading: false
        })
        if (result.status) {
          notification.success({
            top: 92,
            message: '修改成功,请重新登录。',
            duration: 2
          })
          setTimeout(() => {
            sessionStorage.clear()
            this.props.logout()
            this.props.history.replace('/login')
          }, 2000)
        } else {
          notification.warning({
            top: 92,
            message: result.message,
            duration: 5
          })
        }
      })
    }, () => {})
  }
  handleCancel = () => {
    // 取消时关闭修改密码模态框,清空表单数据
    this.setState({
      visible: false
    MKEmitter.emit('resetpassword', () => {
      setTimeout(() => {
        sessionStorage.clear()
        this.props.logout()
        this.props.history.replace('/login')
      }, 2000)
    })
  }
@@ -118,7 +64,7 @@
    // 退出登录
    let _this = this
    confirm({
      title: this.state.dict['main.logout.hint'],
      title: '您确定要退出吗?',
      content: '',
      onOk() {
        sessionStorage.clear()
@@ -225,6 +171,8 @@
  getMenulist = (result) => {
    let thdMenuList = []
    let menulist = []
    let names = new Map()
    let doublenames = new Map()
    result.fst_menu && result.fst_menu.forEach(fst => {
      let fstItem = {
        MenuID: fst.MenuID,
@@ -268,6 +216,7 @@
                ParentId: snd.MenuID,
                MenuID: trd.MenuID,
                MenuName: trd.MenuName,
                menu_name: trd.MenuName,
                ParentNames: [fst.MenuName, snd.MenuName],
                MenuNo: trd.MenuNo,
                EasyCode: trd.EasyCode,
@@ -296,6 +245,12 @@
              trdItem.OpenType = trdItem.OpenType.toLowerCase() // NewPage为打开外部页面地址
              if (names.has(trdItem.menu_name)) {
                doublenames.set(trdItem.menu_name, true)
              } else {
                names.set(trdItem.menu_name, true)
              }
              thdMenuList.push(trdItem)
              return trdItem
@@ -309,6 +264,13 @@
      }
      menulist.push(fstItem)
    })
    thdMenuList = thdMenuList.map(item => {
      if (doublenames.has(item.menu_name)) {
        item.menu_name += '(' + item.ParentNames.join('-') + ')'
      }
      return item
    })
    return { menulist, thdMenuList }
@@ -594,23 +556,23 @@
    const menu = (
      <Menu className="header-dropdown">
        {debug && <Menu.Item key="switch">
          {this.state.dict['main.edit']}
          编辑
          <Switch size="small" style={{marginLeft: '7px'}} checked={false} onChange={this.changeEditState} />
        </Menu.Item>}
        <Menu.Item key="password" onClick={this.changePassword}>{this.state.dict['main.password']}</Menu.Item>
        <Menu.Item key="password" onClick={this.changePassword}>修改密码</Menu.Item>
        {this.state.systems.length ? <Menu.SubMenu style={{minWidth: '110px'}} title="切换系统">
          {this.state.systems.map((system, index) => (
            <Menu.Item style={{minWidth: '100px', lineHeight: '30px'}} key={'sub' + index} onClick={() => {this.changeSystem(system)}}> {system.AppName} </Menu.Item>
          ))}
        </Menu.SubMenu> : null}
        <Menu.Item key="doc" onClick={this.gotoDoc}>{this.state.dict['main.doc']}</Menu.Item>
        <Menu.Item key="doc" onClick={this.gotoDoc}>文档中心</Menu.Item>
        <Menu.Item key="verup" onClick={this.verup}>
          页面更新
        </Menu.Item>
        {appVersion ? <Menu.Item key="version" onClick={this.about}>
          关于
        </Menu.Item> : null}
        <Menu.Item key="logout" onClick={this.logout}>{this.state.dict['main.logout']}</Menu.Item>
        <Menu.Item key="logout" onClick={this.logout}>退出</Menu.Item>
      </Menu>
    )
@@ -740,12 +702,12 @@
                        option.MenuNo.toLowerCase().indexOf(searchkey.toLowerCase()) >= 0 ||
                        option.EasyCode.toLowerCase().indexOf(searchkey.toLowerCase()) >= 0
                      ) {
                        return <Menu.Item key={option.MenuID} onClick={() => this.changeVerMenu(option)}>{option.MenuName}</Menu.Item>
                        return <Menu.Item key={option.MenuID} onClick={() => this.changeVerMenu(option)}>{option.menu_name}</Menu.Item>
                      } else {
                        return null
                      }
                    }
                    return <Menu.Item key={option.MenuID} onClick={() => this.changeVerMenu(option)}>{option.MenuName}</Menu.Item>
                    return <Menu.Item key={option.MenuID} onClick={() => this.changeVerMenu(option)}>{option.menu_name}</Menu.Item>
                  })}
                </Menu>
              </div>
@@ -763,29 +725,20 @@
            </span>
          </div>
        </Dropdown>
        {/* 修改密码 */}
        <Modal
          title={this.state.dict['main.password']}
          visible={this.state.visible}
          onOk={this.resetPwdSubmit}
          confirmLoading={this.state.confirmLoading}
          onCancel={this.handleCancel}
          destroyOnClose
        >
          <Resetpwd dict={this.state.dict} wrappedComponentRef={(inst) => this.formRef = inst} resetPwdSubmit={this.resetPwdSubmit}/>
        </Modal>
        {/* 编辑状态登录 */}
        <Modal
          title={this.state.dict['main.login.develop']}
          title="登录开发机"
          visible={this.state.loginVisible}
          onOk={this.loginSubmit}
          width={'430px'}
          confirmLoading={this.state.loginLoading}
          onCancel={() => {this.setState({ loginVisible: false, loginLoading: false })}}
          destroyOnClose
        >
          >
          <LoginForm handleSubmit={() => this.loginSubmit()} wrappedComponentRef={(inst) => this.loginRef = inst}/>
        </Modal>
        {/* 修改密码 */}
        <Resetpwd />
      </header>
    )
  }
src/components/header/resetpwd/index.jsx
File was deleted
src/components/resetPassword/index.jsx
New file
@@ -0,0 +1,182 @@
import React, {Component} from 'react'
import { is, fromJS } from 'immutable'
import { Modal, notification } from 'antd'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import Resetpwd from './resetpwd'
import MKEmitter from '@/utils/events.js'
import './index.scss'
class ResetPassword extends Component {
  state = {
    visible: false,
    loading: false,
    type: 'account',
    okText: '确定',
    smsId: '',
    mob: '',
    code: ''
  }
  callback = null
  componentDidMount() {
    MKEmitter.addListener('resetpassword', this.resetpassword)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  componentWillUnmount() {
    MKEmitter.removeListener('resetpassword', this.resetpassword)
  }
  resetpassword = (callback) => {
    this.callback = callback
    this.setState({
      visible: true,
      loading: false,
      type: 'account',
      okText: '确定',
      mob: '',
      code: '',
      smsId: sessionStorage.getItem('mk_sms_id') || ''
    })
  }
  resetPwdSubmit = () => {
    const { type } = this.state
    if (type === 'account') {
      this.formRef.handleConfirm().then(res => {
        this.setState({
          loading: true
        })
        let _param = {
          func: 's_PwdUpt',
          LText: `select '${res.originpwd}','${res.password}'`
        }
        _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 时间戳
        _param.LText = Utils.formatOptions(_param.LText)                   // 关键字符替换,base64加密
        _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5密钥
        Api.getSystemConfig(_param).then(result => {
          if (result.status) {
            this.setState({
              visible: false,
              loading: false
            })
            notification.success({
              top: 92,
              message: '修改成功,请重新登录。',
              duration: 2
            })
            this.callback && this.callback()
          } else {
            this.setState({
              loading: false
            })
            notification.warning({
              top: 92,
              message: result.message,
              duration: 5
            })
          }
        })
      }, () => {})
    } else if (type === 'mob') {
      this.formRef.handleConfirm().then(res => {
        if (!/^1[3456789]\d{9}$/.test(res.phone)) {
          notification.warning({
            top: 92,
            message: '手机号格式错误,请重填!',
            duration: 5
          })
          return
        }
        this.setState({
          mob: res.phone,
          type: 'code'
        })
      }, () => {})
    } else if (type === 'code') {
      this.formRef.handleConfirm().then(res => {
        this.setState({
          code: res.vercode,
          type: 'phonepwd',
          okText: '确定'
        })
      }, () => {})
    } else if (type === 'phonepwd') {
      this.formRef.handleConfirm().then(res => {
        this.setState({
          loading: true
        })
        let _param = {
          func: 's_PwdUpt',
          LText: `select '','${res.password}'`,
          mob: this.state.mob,
          check_code: this.state.code
        }
        _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 时间戳
        _param.LText = Utils.formatOptions(_param.LText)                   // 关键字符替换,base64加密
        _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5密钥
        Api.getSystemConfig(_param).then(result => {
          if (result.status) {
            this.setState({
              visible: false,
              loading: false
            })
            notification.success({
              top: 92,
              message: '修改成功,请重新登录。',
              duration: 2
            })
            this.callback && this.callback()
          } else {
            this.setState({
              loading: false
            })
            notification.warning({
              top: 92,
              message: result.message,
              duration: 5
            })
          }
        })
      }, () => {})
    }
  }
  render() {
    const { visible, loading, okText, type, mob, smsId } = this.state
    return (
      <Modal
        title="修改密码"
        wrapClassName="reset-password-modal"
        visible={visible}
        maskClosable={false}
        onOk={this.resetPwdSubmit}
        onCancel={() => { this.setState({ visible: false })}}
        okText={okText}
        confirmLoading={loading}
        destroyOnClose
      >
        <Resetpwd type={type} mob={mob} smsId={smsId} wrappedComponentRef={(inst) => this.formRef = inst} resetPwdSubmit={this.resetPwdSubmit}/>
        {type === 'account' && smsId ? <div className="forget-pwd"><span onClick={() => this.setState({type: 'mob', okText: '下一步'})}>忘记密码?</span></div> : null}
      </Modal>
    )
  }
}
export default ResetPassword
src/components/resetPassword/index.scss
New file
@@ -0,0 +1,22 @@
.reset-password-modal {
  .ant-modal {
    top: 70px;
  }
  .ant-modal-body {
    min-height: 150px;
    .forget-pwd {
      text-align: right;
      padding-right: 40px;
      color: #1890ff;
      span {
        cursor: pointer;
      }
    }
    .vercode-line {
      .ant-form-item-control-wrapper {
        margin-left: 8.3%;
      }
    }
  }
}
src/components/resetPassword/resetpwd/index.jsx
New file
@@ -0,0 +1,317 @@
import React, {Component} from 'react'
import { Form, Input, Button, message } from 'antd'
import moment from 'moment'
import md5 from 'md5'
import Api from '@/api'
class Resetpwd extends Component {
  state = {
    confirmDirty: false,
    autoCompleteResult: [],
    level: localStorage.getItem(window.location.href.split('#')[0] + 'pwdlevel') || ''
  }
  LoginTimer = null
  onEnterSubmit = (e) => {
    // 表单回车提交
    if (e.key !== 'Enter') return
    if (!this.props.form.getFieldValue('originpwd')) {
      this.focusInput('originpwd')
    } else if (!this.props.form.getFieldValue('password')) {
      this.focusInput('password')
    } else if (!this.props.form.getFieldValue('confirm')) {
      this.focusInput('confirm')
    } else {
      this.props.resetPwdSubmit()
    }
  }
  focusInput = (selectId) => {
    let _form = document.getElementById('reset-password-form')
    let _inputs = _form.getElementsByTagName('input')
    _inputs = [..._inputs]
    _inputs.forEach(input => {
      if (!input || input.id !== selectId) return
      if (input.focus) {
        input.focus()
      }
    })
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleConfirmBlur = e => {
    const { value } = e.target
    this.setState({ confirmDirty: this.state.confirmDirty || !!value })
  }
  compareToFirstPassword = (rule, value, callback) => {
    const { form } = this.props
    if (value && value !== form.getFieldValue('password')) {
      callback('两次输入密码不一致!')
    } else {
      callback()
    }
  }
  validateToNextPassword = (rule, value, callback) => {
    const { form } = this.props
    const { level } = this.state
    if (value && this.state.confirmDirty) {
      form.validateFields(['confirm'], { force: true })
    }
    if (level === 'letter_num' && value && /^[0-9a-zA-Z!@#$%^&*()_]*$/.test(value) && /^([^0-9]*|[^a-zA-Z]*)$/.test(value)) {
      callback('密码中必须含有数字和字母。')
    } else if ((level === 'char_num' || level === 'char_num_90' || level === 'char_num_90_sms') && value && /^[0-9a-zA-Z!@#$%^&*()_]*$/.test(value) && /^([^0-9]*|[^a-zA-Z]*|[^!@#$%^&*()_]*)$/.test(value)) {
      callback('密码中必须含有数字、字母和特殊字符。')
    } else {
      callback()
    }
  }
  getvercode = () => {
    const { mob, smsId } = this.props
    let _param = {
      func: 'mes_sms_send_code_sso',
      send_type: 'reset_pwd',
      mob: mob,
      ID: smsId
    }
    _param.LText = 'minke'
    _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    _param.secretkey = md5(`${_param.LText}mingke${_param.timestamp}`)
    _param.userid = sessionStorage.getItem('visitorUserID') || ''
    _param.LoginUID = sessionStorage.getItem('visitorLoginUID') || ''
    Api.getSystemConfig(_param).then(res => {
      if (!res.status || !res.n_id) {
        message.warning(res.message || '验证码获取失败!')
        return
      }
      let param = {
        func: 'MSN_sms_send_code',
        send_type: 'reset_pwd',
        mob: mob,
        timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
        ID: smsId,
        n_id: res.n_id
      }
      param.LText = md5(`${mob}mingke${window.GLOB.appkey}${param.timestamp}`)
      param.secretkey = md5(`${param.LText}mingke${param.timestamp}`)
      param.rduri = 'https://sso.mk9h.cn/webapi/dostars'
      param.userid = 'bh0bapabtd45epsgra79segbch6c1ibk'
      param.LoginUID = 'bh0bapabtd45epsgra79segbch6c1ibk'
      this.setState({
        verdisabled: true,
        delay: 60
      })
      this.LoginTimer = setTimeout(this.resetVerCodeDelay, 1000)
      Api.genericInterface(param).then(res => {
        if (res.status) {
        } else {
          clearTimeout(this.LoginTimer)
          this.setState({
            verdisabled: false,
            delay: null
          })
          message.warning(res.message)
        }
      }, () => {
        clearTimeout(this.LoginTimer)
        this.setState({
          verdisabled: false,
          delay: null
        })
      })
    })
  }
  resetVerCodeDelay = () => {
    const { delay } = this.state
    if (delay && delay > 1) {
      this.setState({delay: delay - 1})
      this.LoginTimer = setTimeout(this.resetVerCodeDelay, 1000)
    } else {
      this.setState({
        verdisabled: false,
        delay: null
      })
    }
  }
  render() {
    const { type } = this.props
    const { getFieldDecorator } = this.props.form
    const { level, delay, verdisabled } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 6 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    let rules = []
    if (level) {
      rules.push({
        min: 8,
        message: '密码长度不可小于8位!'
      })
    }
    return (
      <>
        {type === 'account' ? <Form {...formItemLayout} onKeyDown={this.onEnterSubmit} id="reset-password-form">
          <Form.Item label="原密码">
            {getFieldDecorator('originpwd', {
              rules: [
                {
                  required: true,
                  message: '请输入原密码!'
                }
              ]
            })(<Input.Password autoFocus/>)}
          </Form.Item>
          <Form.Item label="新密码" hasFeedback>
            {getFieldDecorator('password', {
              rules: [
                {
                  required: true,
                  message: '请输入新密码!'
                },
                {
                  pattern: /^[0-9a-zA-Z!@#$%^&*()_]*$/ig,
                  message: '密码只允许包含数字、字母以及!@#$%&*()_。'
                },
                ...rules,
                {
                  max: 50,
                  message: '最大密码长度为50位!'
                },
                {
                  validator: this.validateToNextPassword
                }
              ]
            })(<Input.Password />)}
          </Form.Item>
          <Form.Item label="确认密码" hasFeedback>
            {getFieldDecorator('confirm', {
              rules: [
                {
                  required: true,
                  message: '请确认密码!'
                },
                {
                  validator: this.compareToFirstPassword
                }
              ]
            })(<Input.Password onBlur={this.handleConfirmBlur} />)}
          </Form.Item>
        </Form> : null}
        {type === 'phonepwd' ? <Form {...formItemLayout} onKeyDown={(e) => e.key === 'Enter' && this.props.resetPwdSubmit()}>
          <Form.Item label="新密码" hasFeedback>
            {getFieldDecorator('password', {
              rules: [
                {
                  required: true,
                  message: '请输入新密码!'
                },
                {
                  pattern: /^[0-9a-zA-Z!@#$%^&*()_]*$/ig,
                  message: '密码只允许包含数字、字母以及!@#$%&*()_。'
                },
                ...rules,
                {
                  max: 50,
                  message: '最大密码长度为50位!'
                },
                {
                  validator: this.validateToNextPassword
                }
              ]
            })(<Input.Password />)}
          </Form.Item>
          <Form.Item label="确认密码" hasFeedback>
            {getFieldDecorator('confirm', {
              rules: [
                {
                  required: true,
                  message: '请确认密码!'
                },
                {
                  validator: this.compareToFirstPassword
                }
              ]
            })(<Input.Password onBlur={this.handleConfirmBlur} />)}
          </Form.Item>
        </Form> : null}
        {type === 'mob' ? <Form {...formItemLayout} onKeyDown={(e) => e.key === 'Enter' && this.props.resetPwdSubmit()}>
          <Form.Item label="手机号">
            {getFieldDecorator('phone', {
              initialValue: '',
              rules: [
                {
                  required: true,
                  message: '请输入手机号!'
                }
              ]
            })(<Input placeholder="请输入手机号" autoComplete="off" />)}
          </Form.Item>
        </Form> : null}
        {type === 'code' ? <Form wrapperCol={{ xs: { span: 24 }, sm: { span: 20 }}} onKeyDown={(e) => e.key === 'Enter' && this.props.resetPwdSubmit()}>
          <Form.Item className="vercode-line" label="">
            {getFieldDecorator('vercode', {
              initialValue: '',
              rules: [
                {
                  required: true,
                  message: '请输入验证码!'
                }
              ]
            })(<Input
              addonAfter={
                <Button type="link" size="small" disabled={verdisabled} onClick={this.getvercode}>
                  {delay ? `${delay}s后重新获取` : '获取验证码'}
                </Button>
              }
              placeholder="请输入验证码"
              autoComplete="off"
            />)}
          </Form.Item>
        </Form> : null}
      </>
    )
  }
}
export default Form.create()(Resetpwd)
src/index.js
@@ -111,6 +111,9 @@
      // GLOB.mainSystemApi = 'https://cloud.positecgroup.com/webapi/dostars'
      GLOB.mainSystemApi = 'https://sso.mk9h.cn/cloud/webapi/dostars'
      if (!/^https/.test(window.location.protocol)) { // https转换
        GLOB.mainSystemApi = 'http://sso.mk9h.cn/cloud/webapi/dostars'
      }
      if (GLOB.watermark) {
        GLOB.dataFormat = true
@@ -287,6 +290,7 @@
    })
    window.GLOB.CacheMap = new Map()     // 缓存配置信息
    window.GLOB.CacheVoucher = new Map() // 缓存凭证信息
    window.GLOB.UserCacheMap = new Map() // 缓存用户自定义设置
    window.GLOB.CacheData = new Map()    // 存储选中数据
src/locales/en-US/login.js
@@ -8,8 +8,6 @@
  'login.phone.empty': 'Please enter your cell phone number!',
  'login.vercode': 'Verification code',
  'login.vercode.empty': 'Please enter your verification code!',
  'login.vercode.query': 'Get verify code',
  'login.vercode.queryagain': 'Obtain again after @s',
  'login.password': 'Password',
  'login.password.empty': 'Please input your Password!',
  'login.remember': 'Remember me',
src/locales/en-US/main.js
@@ -1,17 +1,11 @@
export default {
  'main.search': 'Search',
  'main.reset': 'Reset',
  'main.edit': 'Edit',
  'main.confirm': 'OK',
  'main.submit': 'Submit',
  'main.return': 'Return',
  'main.close': 'Close',
  'main.cancel': 'Cancel',
  'main.logout': 'Logout',
  'main.doc': '文档中心',
  'main.logout.hint': 'Are you sure you want to log out?',
  'main.password': 'Change the password',
  'main.login.develop': 'Login developer',
  'main.revert.default': 'Restore default Settings',
  'main.copy.success': 'Copy success',
  'main.pagination.of': 'of',
src/locales/zh-CN/login.js
@@ -8,8 +8,6 @@
  'login.phone.empty': '请输入手机号!',
  'login.vercode': '验证码',
  'login.vercode.empty': '请输入验证码!',
  'login.vercode.query': '获取验证码',
  'login.vercode.queryagain': '@s后重新获取',
  'login.password': '密码',
  'login.password.empty': '请输入密码!',
  'login.remember': '记住密码',
src/locales/zh-CN/main.js
@@ -1,17 +1,11 @@
export default {
  'main.search': '搜索',
  'main.reset': '重置',
  'main.edit': '编辑',
  'main.confirm': '确定',
  'main.submit': '提交',
  'main.return': '返回',
  'main.close': '关闭',
  'main.cancel': '取消',
  'main.logout': '退出',
  'main.doc': '文档中心',
  'main.logout.hint': '您确定要退出吗?',
  'main.password': '修改密码',
  'main.login.develop': '登录开发机',
  'main.revert.default': '恢复默认设置',
  'main.copy.success': '复制成功',
  'main.pagination.of': '共',
src/menu/components/card/balcony/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Checkbox } from 'antd'
import { Popover, Checkbox, message } from 'antd'
import { PlusOutlined, PlusSquareOutlined, EditOutlined, FontColorsOutlined, DeleteOutlined, SettingOutlined, ToolOutlined, ClockCircleOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -191,7 +191,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -245,9 +245,19 @@
  }
  getWrapForms = () => {
    const { wrap } = this.state.card
    const { card } = this.state
    return getWrapForm(wrap)
    let buttons = []
    card.elements && card.elements.forEach(item => {
      if (item.eleType === 'button') {
        buttons.push({
          value: item.uuid,
          label: item.label
        })
      }
    })
    return getWrapForm(card.wrap, buttons, card.columns)
  }
  updateWrap = (res) => {
@@ -297,7 +307,15 @@
        <CardCellComponent cards={card} cardCell={card} elements={card.elements} updateElement={this.updateCard}/>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/card/balcony/options.jsx
@@ -4,7 +4,7 @@
/**
 * @description Wrap表单配置信息
 */
export default function (wrap) {
export default function (wrap, buttons, columns) {
  let menu = window.GLOB.customMenu
  let modules = MenuUtils.getLinkModules(menu.components) || []
@@ -69,6 +69,7 @@
      controlFields: [
        {field: 'empty', values: ['dynamic']},
        {field: 'publicId', values: ['public']},
        {field: 'bgField', values: ['dynamic', 'public']},
      ]
    },
    {
@@ -248,6 +249,15 @@
      ],
    },
    {
      type: 'select',
      field: 'bgField',
      label: '背景图',
      initval: wrap.bgField || '',
      tooltip: '动态背景,背景图片由字段值控制。请注意调整背景样式。',
      required: false,
      options: columns
    },
    {
      type: 'radio',
      field: 'permission',
      label: '权限验证',
@@ -260,6 +270,14 @@
      forbid: !appType || sessionStorage.getItem('editMenuType') === 'popview'
    },
    {
      type: 'select',
      field: 'linkbtn',
      label: '关联按钮',
      initval: wrap.linkbtn || '',
      required: false,
      options: buttons
    },
    {
      type: 'multiselect',
      field: 'blacklist',
      label: '黑名单',
src/menu/components/card/cardcellcomponent/index.jsx
@@ -149,6 +149,7 @@
        options = ['margin', 'float']
      } else {
        options.push('width', 'minHeight', 'float')
        _style.minHeight = _style.minHeight || '28px'
      }
      if (element.wrapStyle) {
        _style.float = element.wrapStyle.textAlign || 'left'
src/menu/components/card/cardcomponent/options.jsx
@@ -109,7 +109,8 @@
      initval: setting.bgField || '',
      tooltip: '动态背景,背景图片由字段值控制。请注意调整背景样式。',
      required: false,
      options: columns
      options: columns,
      forbid: cardType === 'extendCard'
    },
    {
      type: ops.length === 0 ? 'radio' : 'select',
src/menu/components/card/data-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal, Pagination } from 'antd'
import { Popover, Modal, Pagination, message } from 'antd'
import { PlusOutlined, PlusCircleOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, DownOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -288,7 +288,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['background', 'height', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['background', 'height', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -491,9 +491,9 @@
      let elements = []
      res.elements && res.elements.forEach(cell => {
        if (cell.datatype === 'dynamic') {
          cell.datatype = 'static'
        }
        // if (cell.datatype === 'dynamic') {
        //   cell.datatype = 'static'
        // }
        if (cell.eleType !== 'button') {
          cell.uuid = Utils.getuuid()
@@ -512,9 +512,9 @@
      if (appType !== 'mob') {
        res.backElements && res.backElements.forEach(cell => {
          if (cell.datatype === 'dynamic') {
            cell.datatype = 'static'
          }
          // if (cell.datatype === 'dynamic') {
          //   cell.datatype = 'static'
          // }
          if (cell.eleType !== 'button') {
            cell.uuid = Utils.getuuid()
@@ -623,7 +623,15 @@
        {card.wrap.pagestyle === 'more' && card.setting.laypage === 'true' ? <div className="mk-more">查看更多<DownOutlined/></div> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/card/data-card/options.jsx
@@ -282,6 +282,19 @@
    },
    {
      type: 'radio',
      field: 'parity',
      label: '奇偶背景',
      initval: wrap.parity || 'false',
      tooltip: '偶数行会添加背景色。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'true', label: '有'},
      ],
      forbid: subtype === 'propcard'
    },
    {
      type: 'radio',
      field: 'printType',
      label: '组件类型',
      initval: wrap.printType || 'content',
src/menu/components/card/prop-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal } from 'antd'
import { Popover, Modal, message } from 'antd'
import { PlusOutlined, SettingOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, ClockCircleOutlined, HeatMapOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -304,7 +304,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -530,7 +530,15 @@
        {card.wrap.display === 'hidden' ? <HeatMapOutlined className="prop-hidden"/> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/card/table-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal, Pagination } from 'antd'
import { Popover, Modal, Pagination, message } from 'antd'
import { PlusOutlined, PlusCircleOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, DownOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -254,7 +254,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -513,7 +513,15 @@
        {card.setting.laypage === 'true' && card.wrap.pagestyle === 'more' ? <div className="mk-more">查看更多<DownOutlined/></div> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/carousel/data-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal } from 'antd'
import { Popover, Modal, message } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -268,7 +268,15 @@
        </div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/carousel/prop-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal, Carousel } from 'antd'
import { Popover, Modal, Carousel, message } from 'antd'
import { PlusOutlined, SettingOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, ClockCircleOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -338,7 +338,15 @@
        </div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/chart/antv-G6/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import G6 from '@antv/g6'
@@ -1424,7 +1424,15 @@
        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/chart/antv-bar/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { PlusCircleOutlined, PlusSquareOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import { Chart } from '@antv/g2'
import DataSet from '@antv/data-set'
@@ -1384,7 +1384,15 @@
        /> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/chart/antv-dashboard/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import { Chart, registerShape } from '@antv/g2'
@@ -553,7 +553,15 @@
        <div className="canvas" id={card.uuid + 'dashboard'}></div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/chart/antv-pie/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { ToolOutlined, DeleteOutlined, FontColorsOutlined, PlusCircleOutlined } from '@ant-design/icons'
import { Chart } from '@antv/g2'
import DataSet, { DataView } from '@antv/data-set'
@@ -727,7 +727,15 @@
        <div className="canvas" id={card.uuid + 'canvas'}></div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/chart/antv-scatter/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { ToolOutlined, DeleteOutlined, FontColorsOutlined, PlusCircleOutlined, PlusSquareOutlined } from '@ant-design/icons'
import { Chart } from '@antv/g2'
@@ -399,7 +399,15 @@
        {appType !== 'mob' ? <ActionComponent config={card} updateaction={this.updateComponent}/> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/code/sandbox/index.jsx
@@ -128,7 +128,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
src/menu/components/editor/braft-editor/index.jsx
@@ -46,7 +46,7 @@
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, datatype: 'dynamic', width: card.width || 24, encryption: 'true', minHeight: 100 },
        wrap: { name: card.name, datatype: 'dynamic', width: card.width || 24, encryption: 'true' },
        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        columns: [],
@@ -128,7 +128,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -164,9 +164,6 @@
    const { card } = this.state
    let style = {...card.style}
    if (card.wrap.minHeight) {
      style.minHeight = card.wrap.minHeight
    }
    return (
      <div className="menu-normal-editor-box" style={style} onClick={this.clickComponent} id={card.uuid}>
src/menu/components/editor/braft-editor/options.jsx
@@ -80,16 +80,6 @@
      ]
    },
    {
      type: 'number',
      field: 'minHeight',
      label: '最小高度',
      initval: wrap.minHeight || '',
      min: 0,
      max: 3000,
      precision: 0,
      required: false
    },
    {
      type: 'radio',
      field: 'permission',
      label: '权限验证',
src/menu/components/form/simple-form/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal, Button, Switch, notification } from 'antd'
import { Popover, Modal, Button, Switch, notification, message } from 'antd'
import { PlusOutlined, SettingOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -194,7 +194,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -687,7 +687,15 @@
        </div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/form/step-form/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal, Button, Switch, notification } from 'antd'
import { Popover, Modal, Button, Switch, notification, message } from 'antd'
import { PlusOutlined, SettingOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -201,7 +201,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -770,7 +770,15 @@
        </div> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/form/tab-form/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Modal, Button, Switch, notification } from 'antd'
import { Popover, Modal, Button, Switch, notification, message } from 'antd'
import { PlusOutlined, SettingOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -213,7 +213,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -798,7 +798,15 @@
        </div> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/group/normal-group/index.jsx
@@ -1,8 +1,8 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Button } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, PrinterOutlined } from '@ant-design/icons'
import { Popover, Button, Modal } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, PrinterOutlined, UngroupOutlined } from '@ant-design/icons'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
@@ -11,6 +11,7 @@
import getSettingForm from './options'
import './index.scss'
const { confirm } = Modal
const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
@@ -21,6 +22,7 @@
  static propTpyes = {
    group: PropTypes.object,
    deletecomponent: PropTypes.func,
    unGroup: PropTypes.func,
    updateConfig: PropTypes.func,
  }
@@ -70,7 +72,7 @@
  changeStyle = () => {
    const { group } = this.state
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], group.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], group.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -129,6 +131,36 @@
    this.updateComponent(group)
  }
  unGroup = () => {
    const { group } = this.state
    if (group.components.length === 0) return
    const _this = this
    confirm({
      title: '确定释放分组元素吗?',
      content: '',
      onOk() {
        _this.props.unGroup(group.uuid)
        setTimeout(() => {
          _this.updataGroup()
        }, 10)
      },
      onCancel() {}
    })
  }
  updataGroup = () => {
    const { group } = this.props
    if (group.components.length === 0) {
      this.setState({
        group: fromJS(group).toJS()
      })
    }
  }
  render() {
    const { group } = this.state
    let _style = resetStyle(group.style)
@@ -147,6 +179,7 @@
              <EditOutlined style={{color: '#1890ff'}} title="编辑"/>
            </NormalForm>
            <CopyComponent type="tabs" card={group}/>
            <UngroupOutlined title="释放" style={group.components.length > 0 ? {color: '#32c5d2'} : {color: '#eeeeee', cursor: 'not-allowed'}} onClick={this.unGroup}/>
            <PasteComponent insert={this.insert} />
            <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle}/>
            <DeleteOutlined className="close" title="delete" onClick={() => this.props.deletecomponent(group.uuid)} />
src/menu/components/iframe/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Input } from 'antd'
import { Popover, Input, message } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, SettingOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -173,7 +173,15 @@
        </div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/module/voucher/index.jsx
@@ -39,7 +39,7 @@
        name: '凭证',
        subtype: card.subtype,
        // setting: { interType: 'system' },
        wrap: { name: '凭证', title: '', width: card.width || 12, type: 'edit' },
        wrap: { name: '凭证', title: '', width: card.width || 12, type: 'createVoucher' },
        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px', paddingBottom: '20px' },
        headerStyle: {},
        columns: [],
@@ -84,7 +84,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -101,7 +101,6 @@
  updateWrap = (res) => {
    let _card = {...this.state.card, wrap: res}
    _card.switchable = res.type !== 'edit'
    this.updateComponent(_card)
  }
@@ -123,31 +122,48 @@
          <ToolOutlined />
        </Popover>
        <div className="voucher-box">
          <div className="voucher-header">
            <Button className="add-background header-btn">新增</Button>
          {card.wrap.type === 'createVoucher' ? <div className="voucher-header">
            <Button className="add-background header-btn">保存并新增</Button>
            <Button className="add-background header-btn">保存</Button>
            <Button className="print-background header-btn">打印</Button>
            <Button className="system-background header-btn">导入</Button>
            <Button className="out-background header-btn">导出</Button>
          </div>
            <Button className="out-background header-btn">更多</Button>
          </div> : null}
          {card.wrap.type === 'checkVoucher' ? <div className="voucher-header">
            <Button className="add-background header-btn">保存</Button>
            <Button className="print-background header-btn">打印</Button>
            <Button className="out-background header-btn">关闭</Button>
          </div> : null}
          <div className="voucher-body">
          <div className="pre-wrap">
            <div className="voucher-code">
              <div>记<DownOutlined/></div>
              <div>1</div>
              号
            </div>
            <div className="voucher-date">
              日期:<div>请选择日期 <CalendarOutlined /></div>
            </div>
            <div className="voucher-affix">
              附单据 <div>2</div> 张
              <Button type="link" className="">附件</Button>
              <Button type="link" className="">备注</Button>
            </div>
            {card.wrap.type === 'createVoucher' || card.wrap.type === 'checkVoucher' ? <div className="pre-wrap">
              <div className="voucher-code">
                <div>记<DownOutlined/></div>
                <div>1</div>
                号
              </div>
              <div className="voucher-date">
                日期:<div>请选择日期 <CalendarOutlined /></div>
              </div>
              <div className="voucher-affix">
                附单据 <div>2</div> 张
                <Button type="link" className="">附件</Button>
                <Button type="link" className="">备注</Button>
              </div>
            </div> : null}
            {card.wrap.type === 'createTemp' || card.wrap.type === 'checkTemp' ? <div className="pre-wrap">
              <div className="temp-code">
                模板名称:
                <div>&nbsp;</div>
              </div>
              <div className="temp-code">
                模板类型:
                <div>日常支出<DownOutlined/></div>
              </div>
              <Button className="out-background header-btn">关闭</Button>
              <Button className="add-background header-btn">保存</Button>
            </div> : null}
            <VoucherTable config={card}/>
            {card.wrap.type === 'createVoucher' || card.wrap.type === 'checkVoucher' ? <div className="user">制单人:</div> : null}
          </div>
          <VoucherTable config={card}/>
        </div>
        </div>
      </div>
    )
src/menu/components/module/voucher/index.scss
@@ -30,7 +30,7 @@
    }
  }
  .voucher-body {
    padding: 0 15px;
    padding: 0 30px;
    .voucher-code {
      display: inline-block;
      width: 160px;
@@ -52,6 +52,33 @@
          margin-left: 5px;
        }
      }
    }
    .temp-code {
      display: inline-block;
      width: 180px;
      margin-right: 15px;
      div {
        display: inline-block;
        min-width: 100px;
        margin-right: 10px;
        margin-left: 10px;
        border: 1px solid #d9d9d9;
        padding: 4px 10px;
        border-radius: 4px;
        .anticon-down {
          position: relative;
          left: 3px;
          color: #c8c8c8;
          font-size: 12px;
          margin-left: 5px;
        }
      }
    }
    .header-btn {
      float: right;
      margin-left: 10px;
    }
    .pre-wrap {
      padding: 10px 0px;
@@ -87,6 +114,9 @@
        border-radius: 4px;
      }
    }
    .user {
      padding-top: 15px;
    }
  }
  .add-background {
src/menu/components/module/voucher/options.jsx
@@ -29,14 +29,22 @@
    //   required: false
    // },
    {
      type: 'radio',
      type: 'select',
      field: 'type',
      label: '类型',
      initval: wrap.type || 'edit',
      initval: wrap.type || 'createVoucher',
      required: true,
      options: [
        {value: 'edit', label: '编辑'},
        {value: 'check', label: '查看'},
        {value: 'createVoucher', label: '新增凭证'},
        {value: 'checkVoucher', label: '查看凭证'},
        {value: 'createTemp', label: '新增模板'},
        {value: 'checkTemp', label: '编辑模板'},
      ],
      controlFields: [
        {field: 'businessType', values: ['createVoucher', 'checkVoucher']},
        {field: 'voucherType', values: ['createVoucher', 'checkVoucher']},
        {field: 'voucherTypeText', values: ['createVoucher', 'checkVoucher']},
        {field: 'voucherSign', values: ['createVoucher', 'checkVoucher']},
      ]
    },
    {
@@ -68,6 +76,34 @@
      allowClear: true
    },
    {
      type: 'text',
      field: 'businessType',
      label: '业务类型',
      initval: wrap.businessType || 'fcc01',
      required: true
    },
    {
      type: 'text',
      field: 'voucherType',
      label: '凭证类型',
      initval: wrap.voucherType || 'fcc_keeping',
      required: true
    },
    {
      type: 'text',
      field: 'voucherTypeText',
      label: '凭证类型文本',
      initval: wrap.voucherTypeText || '记账凭证',
      required: true
    },
    {
      type: 'text',
      field: 'voucherSign',
      label: '凭证类型标识',
      initval: wrap.voucherSign || 'fcc_keeping',
      required: true
    },
    {
      type: 'cascader',
      field: 'supModule',
      label: '上级组件',
src/menu/components/search/main-search/index.jsx
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, notification, Popover, Switch } from 'antd'
import { Modal, notification, Popover, Switch, message } from 'antd'
import { PlusOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -413,7 +413,17 @@
        } trigger="hover">
          <ToolOutlined />
        </Popover>
        <div className="component-name"><div className="center">{card.name}</div></div>
        <div className="component-name">
          <div className="center" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
        </div>
        {/* 编辑搜索条件 */}
        <Modal
          title="搜索条件-编辑"
src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -17,7 +17,7 @@
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'position', 'tipTitle', 'hidden'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'hidden'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'sheet', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'width', 'hidden'],
  excelOut: ['label', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'pagination', 'search', 'width', 'hidden'],
  excelOut: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'pagination', 'search', 'width', 'hidden'],
  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'color', 'popClose', 'width', 'display', 'ratio', 'syncComponent', 'clickouter', 'maskStyle', 'closeButton', 'hidden'],
  tab: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'color', 'linkmenu', 'width', 'hidden'],
  innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'show', 'swipe', 'icon', 'class', 'color', 'width', 'hidden'],
@@ -328,6 +328,7 @@
      }
    } else if (openType === 'excelOut') {
      reOptions.intertype = this.state.interTypeOptions.filter(op => op.value !== 'custom')
      reOptions.Ot = requireOptions.filter(op => ['notRequired', 'requiredOnce'].includes(op.value))
      if (this.record.intertype === 'outer') {
        shows.push('innerFunc', 'sysInterface', 'outerFunc')
@@ -352,6 +353,9 @@
      }
      if (this.record.execSuccess === 'grid' || this.record.execError === 'grid') {
        shows.push('resetPageIndex')
      }
      if (this.record.Ot !== 'notRequired' && this.record.Ot !== 'requiredOnce') {
        this.record.Ot = 'notRequired'
      }
    } else if (openType === 'popview') {
      reOptions.Ot = requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
@@ -466,7 +470,7 @@
    }
    
    if (appType === 'mob') {
      if (Ot !== 'notRequired') {
      if (Ot !== 'notRequired' && openType !== 'excelOut') {
        shows.push('control')
        reOptions.control = [
          { value: '', text: '无' },
@@ -481,7 +485,7 @@
        }
      }
    } else {
      if (Ot !== 'notRequired') {
      if (Ot !== 'notRequired' && openType !== 'excelOut') {
        reOptions.control = [
          { value: '', text: '无' },
          { value: 'disabled', text: '禁用' },
@@ -566,6 +570,7 @@
        _fieldval.label = '导出Excel'
        _fieldval.class = 'dgreen'
        _fieldval.execSuccess = 'never'
        _fieldval.Ot = 'notRequired'
        this.record.Ot = 'notRequired'
        this.record.label = '导出Excel'
        this.record.class = 'dgreen'
@@ -930,7 +935,7 @@
              values.icon = 'scan'
            }
          } else if (values.OpenType === 'excelOut') {
            values.Ot = 'notRequired'
            values.Ot = values.Ot || 'notRequired'
          } else if (['pop', 'prompt', 'exec'].includes(values.OpenType) && values.verify) {
            if ((values.Ot === 'requiredOnce' || card.Ot === 'requiredOnce') && card.Ot !== values.Ot) {
              values.verify.uniques = []
src/menu/components/share/actioncomponent/formconfig.jsx
@@ -523,7 +523,7 @@
      key: 'execSuccess',
      label: '成功后',
      initVal: card.execSuccess || 'grid',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。' : '选择刷新行时,如果选择多条数据会刷新表格。注:上级组件在数据源中添加。',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。' : '选择刷新行时,如果选择多条数据会刷新表格。注:上级组件在数据源中添加。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。',
      required: true,
      options: [{
        value: 'never',
@@ -546,7 +546,7 @@
      key: 'execError',
      label: '失败后',
      initVal: card.execError || 'never',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。' : '选择刷新行时,如果选择多条数据会刷新表格,注:上级组件在数据源中添加。',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。' : '选择刷新行时,如果选择多条数据会刷新表格,注:上级组件在数据源中添加。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。',
      required: true,
      options: [{
        value: 'never',
@@ -1468,7 +1468,7 @@
      key: 'execSuccess',
      label: '成功后',
      initVal: card.execSuccess || 'grid',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。' : '选择刷新行时,如果选择多条数据会刷新表格。注:上级组件在数据源中添加。',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。' : '选择刷新行时,如果选择多条数据会刷新表格。注:上级组件在数据源中添加。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。',
      required: true,
      options: [{
        value: 'never',
@@ -1490,7 +1490,7 @@
      key: 'execError',
      label: '失败后',
      initVal: card.execError || 'never',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。' : '选择刷新行时,如果选择多条数据会刷新表格,注:上级组件在数据源中添加。',
      tooltip: refresh.length ? '执行刷新源组件时,请在源按钮中设置关闭后刷新那一项,注:此时会同步刷新当前组件和上级组件-行。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。' : '选择刷新行时,如果选择多条数据会刷新表格,注:上级组件在数据源中添加。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。',
      required: true,
      options: [{
        value: 'never',
src/menu/components/table/edit-table/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { PlusOutlined, PlusCircleOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -228,7 +228,7 @@
    style.fontSize = card.wrap.fontSize || 14
    style.fontWeight = card.wrap.fontWeight || 'normal'
    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], style, this.getStyle)
    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], style, this.getStyle)
  }
  getStyle = (style) => {
@@ -405,7 +405,15 @@
        <ColumnComponent config={card} updatecolumn={this.updatecolumn}/>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/table/normal-table/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { PlusOutlined, PlusCircleOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -280,7 +280,7 @@
    style.fontSize = card.wrap.fontSize || 14
    style.fontWeight = card.wrap.fontWeight || 'normal'
    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], style, this.getStyle)
    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], style, this.getStyle)
  }
  getStyle = (style) => {
@@ -472,7 +472,15 @@
        <ColumnComponent config={card} updatecolumn={this.updateComponent}/>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/tabs/tabcomponents/card.jsx
@@ -29,7 +29,7 @@
const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'menu', id, originalIndex },
@@ -102,7 +102,7 @@
    } else if (card.type === 'table' && card.subtype === 'editable') {
      return (<EditTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
      return (<NormalGroup group={card} updateConfig={updateConfig} unGroup={unGroup} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
src/menu/components/tabs/tabcomponents/index.jsx
@@ -34,6 +34,25 @@
    handleList({...config, components: cards.map(item => item.uuid === element.uuid ? element : item)})
  }
  const unGroup = (id) => {
    let { card, index } = findCard(`${id}`)
    let components = [...card.components].map(item => {
      if (item.wrap) {
        item.wrap.width = card.width
        item.width = card.width
      } else if (item.plot) {
        item.plot.width = card.width
        item.width = card.width
      }
      return item
    })
    card.components = []
    const _cards = update(cards, { $splice: [[index, 1, card, ...components]] })
    handleList({...config, components: _cards})
  }
  const deleteCard = (id) => {
    const { card } = findCard(id)
@@ -146,6 +165,7 @@
          card={card}
          moveCard={moveCard}
          delCard={deleteCard}
          unGroup={unGroup}
          findCard={findCard}
          updateConfig={updateConfig}
        />
src/menu/components/timeline/normal-timeline/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, message } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -165,7 +165,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -217,7 +217,15 @@
        {card.subcards.map(subcard => (<CardSimpleComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard}/>))}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/components/tree/antd-tree/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Tree } from 'antd'
import { Popover, Tree, message } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, FileOutlined, FolderOpenOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -129,7 +129,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -197,7 +197,15 @@
        </div>
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
              message.success('复制成功。')
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/menu/menushell/card.jsx
@@ -32,7 +32,7 @@
const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'menu', id, originalIndex },
@@ -105,7 +105,7 @@
    } else if (card.type === 'table' && card.subtype === 'editable') {
      return (<EditTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
      return (<NormalGroup group={card} updateConfig={updateConfig} unGroup={unGroup} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
src/menu/menushell/index.jsx
@@ -36,6 +36,25 @@
    setCards(_cards)
  }
  const unGroup = (id) => {
    let { card, index } = findCard(`${id}`)
    let components = [...card.components].map(item => {
      if (item.wrap) {
        item.wrap.width = card.width
        item.width = card.width
      } else if (item.plot) {
        item.plot.width = card.width
        item.width = card.width
      }
      return item
    })
    card.components = []
    const _cards = update(cards, { $splice: [[index, 1, card, ...components]] })
    handleList({...menu, components: _cards})
  }
  const deleteCard = (id) => {
    const { card } = findCard(id)
@@ -155,6 +174,7 @@
            card={card}
            moveCard={moveCard}
            delCard={deleteCard}
            unGroup={unGroup}
            findCard={findCard}
            updateConfig={updateConfig}
          />
src/menu/normalCss/index.jsx
New file
@@ -0,0 +1,70 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal } from 'antd'
import { EditOutlined } from '@ant-design/icons'
import CodeMirror from '@/templates/zshare/codemirror'
import './index.scss'
class NormalCss extends Component {
  static propTpyes = {
    config: PropTypes.object,
    updateConfig: PropTypes.func
  }
  state = {
    visible: false,
    normalcss: ''
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  trigger = () => {
    const { config } = this.props
    this.setState({visible: true, normalcss: config.normalcss || ''})
  }
  onCssChange = (val) => {
    this.setState({
      normalcss: val
    })
  }
  submit = () => {
    const { normalcss } = this.state
    this.setState({
      visible: false
    })
    this.props.updateConfig({...this.props.config, normalcss: normalcss})
  }
  render() {
    const { visible, normalcss } = this.state
    return (
      <div className="mk-normal-css">
        全局样式:<EditOutlined onClick={this.trigger} />
        <Modal
          title="全局样式"
          wrapClassName="normal-css-modal"
          visible={visible}
          width={800}
          maskClosable={false}
          onOk={this.submit}
          onCancel={() => { this.setState({ visible: false })}}
          destroyOnClose
        >
          <CodeMirror mode="text/css" theme="cobalt" value={normalcss} onChange={this.onCssChange} />
        </Modal>
      </div>
    )
  }
}
export default NormalCss
src/menu/normalCss/index.scss
New file
@@ -0,0 +1,21 @@
.mk-normal-css {
  padding-left: 18px;
  .anticon-edit {
    color: #1890ff;
    cursor: pointer;
  }
}
.normal-css-modal {
  .ant-modal {
    top: 70px;
  }
  .ant-modal-body {
    min-height: 250px;
    padding-top: 10px;
    .code-mirror-wrap .code-mirror-area .CodeMirror {
      min-height: 400px;
    }
  }
}
src/menu/stylecombcontroller/styleInput/index.scss
@@ -1,7 +1,7 @@
.style-input-box {
  line-height: 32px;
  .ant-select {
    width: 60px!important;
    width: 65px!important;
  }
  .single-unit {
    width: 38px;
src/menu/stylecontroller/index.jsx
@@ -435,12 +435,20 @@
  }
  changeWidth = (val) => {
    let _val = val
    if (_val === '0px') {
      _val = 'auto'
    const { card } = this.state
    let _style = {...card}
    if (val === '0px') {
      delete _style.width
    } else {
      _style.width = val
    }
    this.updateStyle({width: _val})
    this.setState({
      card: _style
    })
    this.callback && this.callback(_style)
  }
  changeHeight = (val) => {
@@ -495,7 +503,7 @@
                    label={<ColumnWidthOutlined title="宽度"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.width || ''} options={['px', 'vh', 'vw', '%']} onChange={this.changeWidth}/>
                    <StyleInput defaultValue={card.width || ''} options={['px', 'vh', 'vw', '%', 'auto']} onChange={this.changeWidth}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
@@ -675,8 +683,14 @@
                  >
                    <Select defaultValue={card.backgroundPosition || 'center'} onChange={this.changeBackgroundPositon}>
                      <Option value="center">center</Option>
                      <Option value="top">top</Option>
                      <Option value="bottom">bottom</Option>
                      <Option value="center top">center top</Option>
                      <Option value="center bottom">center bottom</Option>
                      <Option value="left top">left top</Option>
                      <Option value="left center">left center</Option>
                      <Option value="left bottom">left bottom</Option>
                      <Option value="right top">right top</Option>
                      <Option value="right center">right center</Option>
                      <Option value="right bottom">right bottom</Option>
                    </Select>
                  </Form.Item>
                </Col> : null}
@@ -907,7 +921,7 @@
                    label={<ColumnHeightOutlined title="最小高度"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.minHeight || '28px'} options={['px']} onChange={(val) => this.changeNormalStyle(val, 'minHeight')}/>
                    <StyleInput defaultValue={card.minHeight || ''} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'minHeight')}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
src/menu/stylecontroller/styleInput/index.jsx
@@ -48,6 +48,8 @@
        unit = 'vw'
      } else if (val.indexOf('vh') > -1) {
        unit = 'vh'
      } else if (val === 'auto' && _options.includes('auto')) {
        unit = 'auto'
      }
    }
@@ -135,8 +137,14 @@
    const { value } = this.state
    this.setState({unit: val})
    if (value && this.props.onChange) {
      this.props.onChange(`${value}${val}`)
    if (val === 'auto') {
      this.setState({value: ''})
      this.props.onChange('auto')
    } else {
      if (value && this.props.onChange) {
        this.props.onChange(`${value}${val}`)
      }
    }
  }
@@ -145,7 +153,7 @@
    return (
      <div className="style-input-box">
        <Input value={value} addonAfter={
        <Input value={value} disabled={unit === 'auto'} addonAfter={
          options.length > 1 ?
          <Select value={unit} onChange={this.changeUnit}>
            {options.map(item => <Option key={item} value={item}>{item}</Option>)}
src/menu/stylecontroller/styleInput/index.scss
@@ -1,7 +1,7 @@
.style-input-box {
  line-height: 32px;
  .ant-select {
    width: 60px!important;
    width: 65px!important;
  }
  .single-unit {
    width: 38px;
src/menu/tablenodes/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Button, notification, Spin, Input } from 'antd'
import { Modal, Button, notification, Spin, Input, message } from 'antd'
import { ForkOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -158,6 +158,7 @@
              id: 'par' + i,
              direction: 'left',
              color: '#5AD8A6',
              node: 'table',
              children: []
            }
@@ -232,7 +233,12 @@
    if (menu.direction !== 'right') return
    if (menu.depth === 1) {
      sessionStorage.setItem('mk-table-node', menu.label)
      window.open('#/hs')
      setTimeout(() => {
        sessionStorage.removeItem('mk-table-node')
      }, 50)
    } else if (menu.param) {
      if (menu.param.type === 'admin') {
        if (menu.param.MenuType === 'custom') {
@@ -354,6 +360,23 @@
          }
        })
        if (model.direction === 'left') {
          if (model.node === 'table') {
            model.fontcolor = '#1890ff'
            this.graph.updateItem(item, model, false)
            let oInput = document.createElement('input')
            oInput.value = model.label
            document.body.appendChild(oInput)
            oInput.select()
            document.execCommand('Copy')
            document.body.removeChild(oInput)
            message.success('表名复制成功。')
          }
          return
        }
        model.fontcolor = '#1890ff'
        this.graph.updateItem(item, model, false)
        
src/mob/components/search/single-search/index.jsx
@@ -139,7 +139,16 @@
        } trigger="hover">
          <ToolOutlined />
        </Popover>
        <div className="component-name"><div className="center">{card.name}</div></div>
        <div className="component-name">
          <div className="center" onDoubleClick={() => {
            let oInput = document.createElement('input')
            oInput.value = card.uuid
            document.body.appendChild(oInput)
            oInput.select()
            document.execCommand('Copy')
            document.body.removeChild(oInput)
          }}>{card.name}</div>
        </div>
      </div>
    )
  }
src/mob/components/tabs/tabcomponents/card.jsx
@@ -27,7 +27,7 @@
const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'menu', id, originalIndex },
@@ -98,7 +98,7 @@
    } else if (card.type === 'table' && card.subtype === 'normaltable') {
      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
      return (<NormalGroup group={card} updateConfig={updateConfig} unGroup={unGroup} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'menubar') {
src/mob/components/tabs/tabcomponents/index.jsx
@@ -34,6 +34,25 @@
    handleList({...config, components: cards.map(item => item.uuid === element.uuid ? element : item)})
  }
  const unGroup = (id) => {
    let { card, index } = findCard(`${id}`)
    let components = [...card.components].map(item => {
      if (item.wrap) {
        item.wrap.width = card.width
        item.width = card.width
      } else if (item.plot) {
        item.plot.width = card.width
        item.width = card.width
      }
      return item
    })
    card.components = []
    const _cards = update(cards, { $splice: [[index, 1, card, ...components]] })
    handleList({...config, components: _cards})
  }
  const deleteCard = (id) => {
    const { card } = findCard(id)
@@ -139,6 +158,7 @@
          card={card}
          moveCard={moveCard}
          delCard={deleteCard}
          unGroup={unGroup}
          findCard={findCard}
          updateConfig={updateConfig}
        />
src/mob/mobshell/card.jsx
@@ -33,7 +33,7 @@
const ShareCode = asyncComponent(() => import('@/mob/components/sharecode'))
const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'menu', id, originalIndex },
@@ -72,21 +72,21 @@
  }
  let col = 'ant-col-' + (card.width || 24)
  if (card.type === 'login') {
    let height = ''
    if (card.wrap && card.wrap.height) {
      // scaleview
      height = card.wrap.height.replace(/\d+vw/ig, (word) => {
        return parseFloat(word) * (window.GLOB.winWidth || 420) / 100 + 'px'
        // return parseFloat(word) * 350 / 100 + 'px'
      }).replace(/\d+vh/ig, (word) => {
        return parseFloat(word) * (window.GLOB.winHeight || 738) / 100 + 'px'
        // return parseFloat(word) * 615 / 100 + 'px'
      })
    }
  // if (card.type === 'login') {
  //   let height = ''
  //   if (card.wrap && card.wrap.height) {
  //     // scaleview
  //     height = card.wrap.height.replace(/\d+vw/ig, (word) => {
  //       return parseFloat(word) * (window.GLOB.winWidth || 420) / 100 + 'px'
  //       // return parseFloat(word) * 350 / 100 + 'px'
  //     }).replace(/\d+vh/ig, (word) => {
  //       return parseFloat(word) * (window.GLOB.winHeight || 738) / 100 + 'px'
  //       // return parseFloat(word) * 615 / 100 + 'px'
  //     })
  //   }
    
    style.minHeight = height
  }
  //   style.minHeight = height
  // }
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
@@ -120,7 +120,7 @@
    } else if (card.type === 'table' && card.subtype === 'normaltable') {
      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
      return (<NormalGroup group={card} updateConfig={updateConfig} unGroup={unGroup} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
src/mob/mobshell/index.jsx
@@ -36,6 +36,25 @@
    setCards(_cards)
  }
  const unGroup = (id) => {
    let { card, index } = findCard(`${id}`)
    let components = [...card.components].map(item => {
      if (item.wrap) {
        item.wrap.width = card.width
        item.width = card.width
      } else if (item.plot) {
        item.plot.width = card.width
        item.width = card.width
      }
      return item
    })
    card.components = []
    const _cards = update(cards, { $splice: [[index, 1, card, ...components]] })
    handleList({...menu, components: _cards})
  }
  const deleteCard = (id) => {
    const { card } = findCard(id)
@@ -220,6 +239,7 @@
            card={card}
            moveCard={moveCard}
            delCard={deleteCard}
            unGroup={unGroup}
            findCard={findCard}
            updateConfig={updateConfig}
          />
src/pc/components/login/normal-login/index.jsx
@@ -71,6 +71,11 @@
      if (_card.wrap.classify === 'signin') {
        active = 'signin'
      }
      if (!_card.style.minHeight && _card.wrap.height) {
        _card.style.minHeight = _card.wrap.height
        delete _card.wrap.height
      }
      this.setState({
        active,
        card: _card
@@ -114,7 +119,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['width', 'background', 'border', 'padding', 'margin', 'clear'], card.style, this.getStyle)
    MKEmitter.emit('changeStyle', ['width', 'background', 'border', 'padding', 'margin', 'clear', 'minHeight'], card.style, this.getStyle)
  }
  getStyle = (style) => {
@@ -190,7 +195,14 @@
        {card.wrap.signWays && active === 'signin' ? <SignForm wrap={card.wrap} changeway={() => this.setState({active: 'login'})}/> : null}
        <div className="component-name">
          <div className="center">
            <div className="title">{card.name}</div>
            <div className="title" onDoubleClick={() => {
              let oInput = document.createElement('input')
              oInput.value = card.uuid
              document.body.appendChild(oInput)
              oInput.select()
              document.execCommand('Copy')
              document.body.removeChild(oInput)
            }}>{card.name}</div>
            <div className="content">
              {card.errors && card.errors.map((err, index) => {
                if (err.level === 0) {
src/pc/components/login/normal-login/options.jsx
@@ -70,15 +70,15 @@
    //   precision: 0,
    //   required: false
    // },
    {
      type: 'styleInput',
      field: 'height',
      label: '最小高度',
      initval: wrap.height || '',
      tooltip: '组件占用的最小高度,用于页面布局。',
      required: false,
      options: ['px', 'vh', 'vw', '%']
    },
    // {
    //   type: 'styleInput',
    //   field: 'height',
    //   label: '最小高度',
    //   initval: wrap.height || '',
    //   tooltip: '组件占用的最小高度,用于页面布局。',
    //   required: false,
    //   options: ['px', 'vh', 'vw', '%']
    // },
    // {
    //   type: 'styleInput',
    //   field: 'maxWidth',
src/pc/menushell/card.jsx
@@ -32,7 +32,7 @@
const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'menu', id, originalIndex },
@@ -71,21 +71,6 @@
  }
  
  let col = ' ant-col ant-col-' + (card.width || 24)
  if (card.type === 'login') {
    let height = ''
    if (card.wrap && card.wrap.height) {
      // scaleview
      height = card.wrap.height.replace(/\d+vw/ig, (word) => {
        return parseFloat(word) * (window.GLOB.winWidth || 420) / 100 + 'px'
        // return parseFloat(word) * 350 / 100 + 'px'
      }).replace(/\d+vh/ig, (word) => {
        return parseFloat(word) * (window.GLOB.winHeight || 738) / 100 + 'px'
        // return parseFloat(word) * 615 / 100 + 'px'
      })
    }
    style.minHeight = height
  }
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
@@ -125,7 +110,7 @@
    } else if (card.type === 'table' && card.subtype === 'editable') {
      return (<EditTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
      return (<NormalGroup group={card} updateConfig={updateConfig} unGroup={unGroup} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
src/pc/menushell/index.jsx
@@ -36,6 +36,25 @@
    setCards(_cards)
  }
  const unGroup = (id) => {
    let { card, index } = findCard(`${id}`)
    let components = [...card.components].map(item => {
      if (item.wrap) {
        item.wrap.width = card.width
        item.width = card.width
      } else if (item.plot) {
        item.plot.width = card.width
        item.width = card.width
      }
      return item
    })
    card.components = []
    const _cards = update(cards, { $splice: [[index, 1, card, ...components]] })
    handleList({...menu, components: _cards})
  }
  const deleteCard = (id) => {
    const { card } = findCard(id)
@@ -173,6 +192,7 @@
            card={card}
            moveCard={moveCard}
            delCard={deleteCard}
            unGroup={unGroup}
            findCard={findCard}
            updateConfig={updateConfig}
          />
src/tabviews/basetable/index.scss
@@ -7,10 +7,7 @@
  padding-left: 16px;
  padding-right: 16px;
  background-size: 100%;
  .component-wrap >.ant-col {
    min-height: 0;
  }
  .box404 {
    padding-top: 30px;
  }
src/tabviews/custom/components/card/balcony/index.jsx
@@ -395,20 +395,33 @@
    MKEmitter.emit('mkCheckAll', syncConfig.uuid, e.target.checked)
  }
  triggerButton = () => {
    const { config, data } = this.state
    if (config.wrap.linkbtn) {
      MKEmitter.emit('triggerBtnId', config.wrap.linkbtn, data.$$empty ? [] : [data])
    }
  }
  render() {
    const { config, loading, data, show, syncConfig, syncData, checked } = this.state
    if (config.wrap.empty === 'hidden' && (!data || data.$$empty)) return null
    let style = {...config.style}
    if (config.wrap.bgField) {
      style.backgroundImage = `url('${data[config.wrap.bgField] || ''}')`
    }
    
    return (
      <div className={'custom-balcony-box' + (!show ? ' hidden' : '')} style={config.style}>
      <div className={'custom-balcony-box' + (!show ? ' hidden' : '')} id={'anchor' + config.uuid} style={style} onClick={this.triggerButton}>
        {loading ?
          <div className="loading-mask">
          <div className="loading-mask" onClick={(e) => e.stopPropagation()}>
            <div className="ant-spin-blur"></div>
            <Spin />
          </div> : null
        }
        {config.wrap.checkAll === 'show' ? <div className="check-all"><Checkbox checked={checked} onChange={this.checkAll}>全选</Checkbox></div> : null}
        {config.wrap.checkAll === 'show' ? <div className="check-all" onClick={(e) => e.stopPropagation()}><Checkbox checked={checked} onChange={this.checkAll}>全选</Checkbox></div> : null}
        <CardCellComponent data={data} syncData={syncData || []} cards={syncConfig || config} cardCell={config} elements={config.elements}/>
      </div>
    )
src/tabviews/custom/components/card/cardItem/index.jsx
@@ -164,12 +164,20 @@
    const { card, data, cards } = this.props
    let style = {...card.style}
    let bg = null
    if (card.setting.bgField) {
      style.backgroundImage = `url('${data[card.setting.bgField] || ''}')`
    }
    if (style.backgroundImage) {
      bg = <div className="card-background" style={{backgroundImage: style.backgroundImage}}></div>
      style.backgroundImage = ''
    }
    return (
      <div className={'card-item-box ' + (card.setting.btnControl || '')} style={style} onClick={this.openView} onDoubleClick={this.doubleClick}>
        {bg}
        <CardCellComponent data={data} cards={cards} cardCell={card} elements={card.elements}/>
        {card.setting.type === 'multi' ? <div className={'back-side ' + card.setting.transform} style={card.backStyle}>
          <CardCellComponent data={data} cards={cards} cardCell={card} elements={card.backElements}/>
src/tabviews/custom/components/card/cardItem/index.scss
@@ -38,6 +38,16 @@
  .back-side.scale {
    transform: scale(0, 0);
  }
  .card-background {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-size: inherit;
    background-position: inherit;
    background-repeat: inherit;
  }
}
.card-item-box:hover {
  .back-side.up, .back-side.down, .back-side.left, .back-side.right {
src/tabviews/custom/components/card/data-card/index.jsx
@@ -907,6 +907,11 @@
            ))}
            {data && data.map((item, index) => {
              let className = card.setting.click ? 'mk-card pointer ' : 'mk-card '
              if (config.wrap.parity === 'true') {
                if (index % 2 === 1) {
                  className += 'mk-parity-bg '
                }
              }
              if (item.$disabled) {
                className = 'mk-disabled'
              } else if (activeKey === index) {
src/tabviews/custom/components/card/data-card/index.scss
@@ -32,6 +32,11 @@
        }
      }
    }
    .mk-parity-bg {
      .card-item-box {
        background-color: var(--mk-sys-color1);
      }
    }
  }
  .data-zoom.scale {
    .card-row-list {
src/tabviews/custom/components/card/table-card/index.jsx
@@ -515,7 +515,7 @@
          </div> : null
        }
        <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
        {data && data.length > 0 ? <Row className="card-row-list" style={{height: config.wrap.contentHeight}}>
        {data && data.length > 0 ? <Row className={'card-row-list' + (config.wrap.parity === 'true' ? ' mk-parity' : '')} style={{height: config.wrap.contentHeight}}>
          {data.map(item => this.getLines(item))}
        </Row> : null}
        {data && data.length === 0 ? <div className="card-row-list" style={{height: config.wrap.contentHeight}}>
src/tabviews/custom/components/card/table-card/index.scss
@@ -33,6 +33,13 @@
    clear: both;
  }
  .mk-parity {
    >.ant-col:nth-child(even) {
      .card-item-box {
        background-color: var(--mk-sys-color1);
      }
    }
  }
  .card-row-list {
    overflow-y: auto;
    .card-item-box {
src/tabviews/custom/components/module/account/index.jsx
@@ -3,8 +3,10 @@
import { is, fromJS } from 'immutable'
import { notification, Select, Divider } from 'antd'
import { PlusOutlined } from '@ant-design/icons'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
@@ -39,7 +41,9 @@
  loadData = () => {
    let param = {
      func: 's_get_fcc_book_data'
      func: 's_get_fcc_book_data',
      dataM: sessionStorage.getItem('dataM') === 'true' ? 'Y' : '',
      mk_organization: sessionStorage.getItem('organization') || ''
    }
    Api.genericInterface(param).then(res => {
@@ -69,10 +73,6 @@
        return true
      })
      if (!activeItem && books.length > 0) {
        activeItem = books[0]
      }
      this.setState({books, activeItem})
      if (activeItem) {
@@ -90,6 +90,22 @@
    if (activeItem) {
      MKEmitter.emit('resetSelectLine', this.props.config.uuid, activeItem.id, activeItem)
      let userid = sessionStorage.getItem('UserID') || ''
      let sid = localStorage.getItem('SessionUid') || ''
      let param = {
        func: 'sPC_TableData_InUpDe',
        LText: `delete  tmp_session_show_key where createuserid='${userid}' and createuser='${sid}' and key_type='fcc_years'
          insert into tmp_session_show_key ( key_id,key_type,createuserid,CreateUser,CreateStaff)
          select '${activeItem.id}','fcc_years','${userid}','${sid}','${sessionStorage.getItem('Full_Name') || ''}'`,
        exec_type: 'y'
      }
      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
      param.secretkey = Utils.encrypt('', param.timestamp)
      param.LText = Utils.formatOptions(param.LText)
      Api.genericInterface(param)
    }
  }
@@ -132,11 +148,11 @@
          </div>
        )}>
          {books.map(item => (
            <Option key={item.id}>{item.account_name}</Option>
            <Option disabled={!item.months} key={item.id}>{item.account_name}</Option>
          ))}
        </Select> : <Select value={activeItem ? activeItem.id : ''} placeholder="请选择账套" onChange={this.changeBook}>
          {books.map(item => (
            <Option key={item.id}>{item.account_name}</Option>
            <Option disabled={!item.months} key={item.id}>{item.account_name}</Option>
          ))}
        </Select>}
        {activeItem ? <span className="date">{activeItem.date}</span> : null}
src/tabviews/custom/components/module/voucher/assistTable/index.jsx
File was deleted
src/tabviews/custom/components/module/voucher/assistTable/index.scss
File was deleted
src/tabviews/custom/components/module/voucher/index.jsx
@@ -1,14 +1,17 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Button, Select, Input, DatePicker, notification } from 'antd'
import { Button, Select, Input, Modal, DatePicker, notification, InputNumber } from 'antd'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import asyncComponent from '@/utils/asyncComponent'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { confirm } = Modal
const { TextArea } = Input
const VoucherTable = asyncComponent(() => import('./voucherTable'))
class VoucherModule extends Component {
@@ -18,17 +21,24 @@
  state = {
    BID: '',
    type: 'createVoucher',
    config: null,
    loading: false,
    data: [],
    disableAdd: false,
    disableSave: false,
    tbdata: [],
    typeOptions: [],
    subjects: [],
    charType: '',
    charName: '',
    charInt: '',
    vouDate: null,
    book: null
    book: null,
    username: sessionStorage.getItem('User_Name'),
    remark: '',
    remarkVisible: false,
    attachments: 0,
    title: '',
    delItems: [],
    status: '' // 新建时,empty、change、saved
  }
  UNSAFE_componentWillMount () {
@@ -46,12 +56,17 @@
      BID = BData.$BID || ''
    }
    window.GLOB.CacheVoucher.delete(config.uuid)
    let type = config.wrap.type || 'createVoucher'
    this.setState({
      config: fromJS(config).toJS(),
      type: type,
      BID: BID || '',
      status: 'empty',
      book: window.GLOB.CacheData.get(config.wrap.supBook) || null
    }, () => {
      this.loadData()
      this.getVoucher()
    })
  }
@@ -94,20 +109,21 @@
    if (!config.wrap.supModule || config.wrap.supModule !== MenuID) return
    if (id !== this.state.BID || id !== '') {
      this.setState({ BID: id, BData: data }, () => {
        this.loadData()
        this.getVoucher()
      })
    }
  }
  loadData = () => {
    const { book } = this.state
    const { book, config } = this.state
    if (!book) return
    let param = {
      func: 's_get_fcc_account_data',
      account_code: book.account_code || '',
      fcc_date: book.months ? book.months + '-01' : moment().format('YYYY-MM-DD')
      // account_code: book.account_code || '',
      fcc_date: book.months ? book.months + '-01' : moment().format('YYYY-MM-DD'),
      BID: book.id
    }
    Api.genericInterface(param).then(res => {
@@ -125,91 +141,380 @@
      this.setState({
        typeOptions: typeOptions,
        charType: typeOptions[0] ? typeOptions[0].voucher_class : '',
        charName: typeOptions[0] ? typeOptions[0].voucher_char : '',
        charInt: typeOptions[0] ? typeOptions[0].voucher_char_int : '',
        subjects: res.subjects || [],
      })
      setTimeout(() => {
        this.getVoucher()
      }, 200)
      let names = {}
      let supplier = []
      let customer = []
      let department = []
      let project = []
      let inventory = []
      let employee = []
      let cash_flow = []
      res.sup && res.sup.forEach(item => {
        names[item.sup_type_code] = item.sup_type_name
      })
      res.supplier && res.supplier.forEach(item => {
        supplier.push({value: item.suppliercode, label: item.suppliername})
      })
      res.customer && res.customer.forEach(item => {
        customer.push({value: item.customercode, label: item.customername})
      })
      res.co_pro && res.co_pro.forEach(item => {
        department.push({value: item.co_pro_code, label: item.co_pro_name})
      })
      res.pm && res.pm.forEach(item => {
        project.push({value: item.projectcode, label: item.projectname})
      })
      res.materiel && res.materiel.forEach(item => {
        inventory.push({value: item.productcode, label: item.productname})
      })
      res.workers && res.workers.forEach(item => {
        employee.push({value: item.workercode, label: item.workername})
      })
      res.cash_flow && res.cash_flow.forEach(item => {
        cash_flow.push({value: item.cash_flow_code, label: item.cash_flow_name})
      })
      let message = {
        subjects: res.subjects || [],
        names: names,
        supplier: supplier,
        customer: customer,
        department: department,
        project: project,
        inventory: inventory,
        currency: res.currency || [],
        employee: employee,
        cash_flow: cash_flow,
        orgcode: res.orgcode,
        orgname: res.orgname,
        account_code: res.account_code,
        account_year_code: res.account_year_code
      }
      window.GLOB.CacheVoucher.set(config.uuid, message)
    })
  }
  getVoucher = () => {
    const { BID, type } = this.state
    if (!BID || type === 'createVoucher' || type === 'createTemp') return
    let param = {
      func: 's_get_fcc_voucher',
      BID: BID
    }
    let data = [
      {remark: '提现', subjectscode: '1001', subjectsname: '库存现金', debtor: 124, creditor: ''},
      {remark: '购入固定资产', subjectscode: '1001', subjectsname: '库存现金', debtor: '', creditor: 124},
      {remark: '转结销售成本', subjectscode: '1001', subjectsname: '库存现金', debtor: -524, creditor: ''},
      {remark: '提现', subjectscode: '1001', subjectsname: '库存现金', debtor: 34, creditor: ''},
      {remark: '提现', subjectscode: '1001', subjectsname: '库存现金', debtor: 34, creditor: '', i: Math.random()},
    ]
    this.setState({
      data: data
    Api.genericInterface(param).then(res => {
      if (!res.status) {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
        return
      }
      this.setState({
        data: data,
        tbdata: fromJS(data).toJS()
      })
    })
  }
  triggeradd = () => {
  triggersave = (t) => {
    const { tbdata, delItems } = this.state
    let err = ''
    let tip = ''
    let list = []
    let _Items = [...delItems]
    tbdata.forEach((line, index) => {
      if (err) return
      if (line.type === 'total') {
        if (line.debtor !== line.creditor) {
          err = '借贷不平衡!'
        }
        return
      }
      let _index = index + 1
      if (!line.remark && !line.subjectscode && !line.debtor && line.debtor !== 0 && !line.creditor && line.creditor !== 0) {
        if (_index === 1) {
          err = '第1行不可为空。'
        } else if (line.$origin) {
          _Items.push(line)
        }
        return
      }
      if (!line.remark) {
        err = `第${_index}行,摘要不可为空。`
      } else if (!line.subjectscode) {
        err = `第${_index}行,科目不可为空。`
      } else if (!line.debtor && line.debtor !== 0 && !line.creditor && line.creditor !== 0) {
        err = `第${_index}行,请输入金额。`
      } else if (line.debtor === 0 || line.creditor === 0) {
        err = `第${_index}行,金额不能为0。`
      } else if (line.foreign_currency_type === 'Y' && !line.origin) {
        err = `第${_index}行,原币不可为空或为0。`
      } else if (line.sup_accounting) {
        line.sup_accounting.split(',').forEach(item => {
          if (!line[item]) {
            err = `第${_index}行,请选择辅助核算。`
          }
        })
      }
      if (line.count_type === 'Y' && !err) {
        if (!line.count) {
          tip += `第${_index}行,数量为空或为0!;`
        } else if (line.price) {
          if (line.debtor && line.debtor !== line.count * line.price) {
            tip += `第${_index}行,数量和金额不匹配!;`
          } else if (line.creditor && line.creditor !== line.count * line.price) {
            tip += `第${_index}行,数量和金额不匹配!;`
          }
        }
      }
      list.push(line)
    })
    if (!err && list.length === 0) {
      err = '第1行不可为空。'
    }
    if (err) {
      notification.warning({
        top: 92,
        message: err,
        duration: 5
      })
      return
    }
    if (tip) {
      const _this = this
      confirm({
        content: tip + '确认要保存吗?',
        onOk() {
          _this.voucherSave(list, _Items, t)
        },
        onCancel() {}
      })
    } else {
      this.voucherSave(list, _Items, t)
    }
  }
  triggersave = () => {
  voucherSave = (list, items, t) => {
    const { config, charInt, charType, vouDate, book, remark, charName, attachments, title } = this.state
    if (!book) {
      notification.warning({
        top: 92,
        message: '请选择账套!',
        duration: 5
      })
      return
    }
    let message = window.GLOB.CacheVoucher.get(config.uuid) || {}
    let param = {
      func: 's_fcc_voucher_addupt',
      BID: book.id,
      ID: Utils.getguid(),
      voucher_code: '',
      voucher_text: title,
      remark: remark,
      account_year_code: book.account_year_code || '',
      voucher_type: config.wrap.voucherType || '',
      voucher_type_text: config.wrap.voucherTypeText || '',
      orgcode: message.orgcode || '',
      orgname: message.orgname || '',
      voucher_class: charType,
      years: book.years,
      business_type: config.wrap.businessType || '',
      voucher_sign: config.wrap.voucherSign || '',
      voucher_char: charName,
      voucher_char_int: charInt,
      account_code: book.account_code || '',
      fibvoucherdate: moment(vouDate).format('YYYY-MM-DD'),
      UserName: sessionStorage.getItem('User_Name') || '',
      FullName: sessionStorage.getItem('Full_Name') || '',
      attachments_int: attachments,
      sup_data: '',
      subject_data: ''
    }
    // subject_id,subject_voucher_code,voucher_lp,subject_code,subject_name
    // ,subject_voucher_text,fcc_count,net_unitprice,unit,net_amount,direction_type
    // ,exratecode,exratename,unitratio,sup_accounting ,direction_type_count,src_amount,deleted,local_exratecode
    let subject_data = list.map(item => {
      let count = item.count_type === 'Y'
      let curr = item.foreign_currency_type === 'Y'
      return `${item.uuid},'','','${item.subjectscode}','${item.subjectsname}','${item.remark}',${count ? item.count || 0 : 0},${count ? item.price || 0 : 0},'${item.unit}',${item.debtor || item.creditor},'${item.debtor ? 'debit' : 'credit'}','${curr ? item.exratecode : '01010001'}','${curr ? item.exratename : 'CNY'}',${curr ? item.unitratio || 0 : 0},'${item.sup_accounting}',${item.debtor ? 1 : -1},${curr ? item.origin || 0 : 0},0,'${item.local_currency || ''}'`
    })
    items.forEach(item => {
      let count = item.count_type === 'Y'
      let curr = item.foreign_currency_type === 'Y'
      subject_data.push(`${item.uuid},'','','${item.subjectscode}','${item.subjectsname}','${item.remark}',${count ? item.count || 0 : 0},${count ? item.price || 0 : 0},'${item.unit}',${item.debtor || item.creditor},'${item.debtor ? 'debit' : 'credit'}','${curr ? item.exratecode : '01010001'}','${curr ? item.exratename : 'CNY'}',${curr ? item.unitratio || 0 : 0},'${item.sup_accounting}',${item.debtor ? 1 : -1},${curr ? item.origin || 0 : 0},1,'${item.local_currency || ''}'`)
    })
    param.subject_data = window.btoa(window.encodeURIComponent(subject_data.join(';un')))
    if (param) {
      return
    }
    Api.genericInterface(param).then(res => {
      if (!res.status) {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
        return
      }
      if (t === 'add') {
        this.setState({
          status: 'empty',
          remark: '',
          tbdata: [],
          delItems: [],
          charInt: charInt + 1
        })
        MKEmitter.emit('cleartable', config.uuid)
      } else {
        this.setState({
          status: 'saved',
          delItems: [],
        })
      }
    })
  }
  triggerprint = () => {
    this.setState({remarkVisible: true})
  }
  remarkSubmit = () => {
    const { config } = this.state
    let node = document.getElementById(config.uuid + 'remark')
    let val = node.value
    if (val && val.length > 512) {
      notification.warning({
        top: 92,
        message: '当前内容超长,备注最多512个字符。',
        duration: 5
      })
      return
    }
    this.setState({remark: val, remarkVisible: false})
  }
  dataChange = (data, item) => {
    if (item) {
      this.setState({
        status: 'change',
        tbdata: data,
        delItems: [...this.state.delItems, item]
      })
    } else {
      this.setState({
        status: 'change',
        tbdata: data
      })
    }
  }
  changeAttach = (val) => {
    let _val = val
    if (isNaN(val) || val < 0) {
      _val = 0
    } else {
      _val = parseInt(val)
    }
    this.setState({attachments: _val})
  }
  render() {
    const { config, disableSave, disableAdd, typeOptions, charType, charInt, data, vouDate, subjects } = this.state
    const { type, status, loading, config, typeOptions, charType, charInt, data, vouDate, username, remark, remarkVisible, attachments, title } = this.state
    return (
      <div className="menu-voucher-wrap" style={config.style}>
        <div className="voucher-header">
          <Button className="add-background header-btn" disabled={disableAdd} onClick={this.triggeradd}>新增</Button>
          <Button className="add-background header-btn" disabled={disableSave} onClick={this.triggersave}>保存</Button>
          <Button className="print-background header-btn" disabled={disableSave} onClick={this.triggerprint}>打印</Button>
          <Button className="system-background header-btn" disabled={disableSave} onClick={this.triggerprint}>导入</Button>
          <Button className="out-background header-btn" disabled={disableSave} onClick={this.triggerprint}>导出</Button>
        </div>
        {config.wrap.type === 'edit' ? <div className="voucher-body">
          <div className="pre-wrap">
        {type === 'createVoucher' ? <div className="voucher-header">
          <Button className="add-background header-btn" disabled={status === 'empty'} onClick={() => this.triggersave('add')}>保存并新增</Button>
          <Button className="add-background header-btn" disabled={status === 'empty' || status === 'saved'} onClick={() => this.triggersave()}>保存</Button>
          <Button className="print-background header-btn" disabled={status !== 'saved'} onClick={this.triggerprint}>打印</Button>
          <Button className="out-background header-btn" onClick={this.triggerprint}>更多</Button>
        </div> : null}
        <div className="voucher-body">
          {type === 'createVoucher' ? <div className="pre-wrap">
            <div className="voucher-code">
              <Select value={charType} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({charType: val, charInt: option.props.charint})}>
              <Select value={charType} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({charType: val, charName: option.props.charName, charInt: option.props.charint})}>
                {typeOptions.map(option =>
                  <Select.Option key={option.voucher_char_int} value={option.voucher_class} charint={option.voucher_char_int}>{option.voucher_char}</Select.Option>
                  <Select.Option key={option.voucher_char_int} value={option.voucher_class} charName={option.voucher_char} charint={option.voucher_char_int}>{option.voucher_char}</Select.Option>
                )}
              </Select>
              <Input value={charInt} autoComplete="off" onChange={(e) => this.setState({charInt: e.target.value})}/> 号
              <InputNumber precision={0} min={1} value={charInt} autoComplete="off" onChange={(val) => this.setState({charInt: val})}/> 号
            </div>
            <div className="voucher-date">
              日期:<DatePicker value={vouDate} onChange={(val) => this.setState({vouDate: val})}/>
            </div>
            <div className="voucher-text">
              <Input value={title} placeholder="凭证文本" autoComplete="off" onChange={(e) => this.setState({title: e.target.value})}/>
            </div>
            <div className="voucher-affix">
              附单据 <Input autoComplete="off" /> 张
              附单据 <InputNumber precision={0} value={attachments || 0} autoComplete="off" onChange={this.changeAttach}/> 张
              <Button type="link" className="" onClick={this.triggerprint}>附件</Button>
              <Button type="link" className="" onClick={this.triggerprint}>备注</Button>
            </div>
          </div>
          <VoucherTable config={config} subjects={subjects} data={data}/>
        </div> : null}
        {config.wrap.type === 'check' ? <div className="voucher-body">
          <div className="pre-wrap">
            <div className="voucher-code">
              记 1 号
            </div>
            <div className="voucher-date">
              日期:2022-02-24
            </div>
            <div className="voucher-affix">
              附单据 2 张
              <Button type="link" className="" onClick={this.triggerprint}>附件</Button>
              <Button type="link" className="" onClick={this.triggerprint}>备注</Button>
            </div>
          </div>
          <VoucherTable config={config} data={data}/>
        </div> : null}
          </div> : null}
          <VoucherTable config={config} loading={loading} data={data} onChange={this.dataChange}/>
        </div>
        <div className="user">制单人:{username}</div>
        <Modal
          title="备注"
          visible={remarkVisible}
          width={700}
          maskClosable={false}
          onOk={this.remarkSubmit}
          onCancel={() => { this.setState({ remarkVisible: false })}}
          destroyOnClose
        >
          <TextArea id={config.uuid + 'remark'} defaultValue={remark} rows={6}/>
        </Modal>
      </div>
    )
  }
src/tabviews/custom/components/module/voucher/index.scss
@@ -20,7 +20,7 @@
    }
  }
  .voucher-body {
    padding: 0 15px;
    padding: 0 30px;
    .voucher-code {
      display: inline-block;
      width: 160px;
@@ -33,6 +33,22 @@
      .ant-input {
        width: 60px;
      }
      .ant-input-number {
        display: inline-block;
        width: 60px;
        .ant-input-number-handler-wrap {
          display: none;
        }
      }
    }
    .voucher-affix {
      .ant-input-number {
        display: inline-block;
        width: 50px;
        .ant-input-number-handler-wrap {
          display: none;
        }
      }
    }
    .pre-wrap {
      padding: 10px 0px;
@@ -43,9 +59,14 @@
        width: 120px;
      }
    }
    .voucher-text {
      display: inline-block;
      width: calc(56% - 350px);
      margin-left: 12px;
    }
    .voucher-affix {
      float: right;
      width: 250px;
      width: 240px;
      .ant-input {
        width: 60px;
      }
@@ -73,6 +94,9 @@
    border-color: var(--mk-sys-color);
    color: #ffffff;
  }
  .user {
    padding: 15px 30px 0px;
  }
}
.mk-vcode-dropdown {
src/tabviews/custom/components/module/voucher/voucherTable/index.jsx
@@ -1,13 +1,11 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Table, Modal, Input, InputNumber, notification, message, AutoComplete, Select } from 'antd'
import { Table, Form, Input, InputNumber, notification, AutoComplete, Select, Popover, Button } from 'antd'
import { PlusOutlined, CloseOutlined } from '@ant-design/icons'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import zhCN from '@/locales/zh-CN/main.js'
import enUS from '@/locales/en-US/main.js'
import './index.scss'
class BodyRow extends React.Component {
@@ -24,14 +22,108 @@
  }
}
class Accounting extends React.Component {
  state = {
    subAccounts: [],
    record: null
  }
  UNSAFE_componentWillMount() {
    const { data, tableId } = this.props
    let subAccounts = data.sup_accounting.split(',')
    let msg = window.GLOB.CacheVoucher.get(tableId) || {}
    let names = msg.names || {}
    subAccounts = subAccounts.map(n => {
      let item = {
        field: n,
        label: names[n] || n,
        initval: data[n] || '',
        options: []
      }
      if (msg[n]) {
        item.options = msg[n]
      }
      return item
    })
    this.setState({subAccounts: subAccounts, record: fromJS(data).toJS()})
  }
  selectChange = (val, item) => {
    this.setState({record: {...this.state.record, [item.field]: val || ''}})
  }
  getFields() {
    const { subAccounts } = this.state
    const fields = []
    subAccounts.forEach((item) => {
      fields.push(
        <Form.Item label={item.label} key={item.field}>
          <Select
            showSearch
            allowClear
            defaultValue={item.initval}
            dropdownMatchSelectWidth={false}
            filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
            onChange={(val) => this.selectChange(val, item)}
          >
            {item.options.map((option, i) =>
              <Select.Option key={i} value={option.value}>{option.label}</Select.Option>
            )}
          </Select>
        </Form.Item>
      )
    })
    return fields
  }
  confirm = () => {
    const { subAccounts, record } = this.state
    let empty = ''
    subAccounts.forEach(item => {
      if (!empty && !record[item.field]) {
        empty = item.label
      }
    })
    if (empty) {
      notification.warning({
        top: 92,
        message: '请选择' + empty,
        duration: 5
      })
    } else {
      this.props.confirm(record)
    }
  }
  render() {
    return <div>
      {this.getFields()}
      <div className="footer">
        <Button onClick={this.props.cancel}>取消</Button>
        <Button onClick={this.confirm}>确定</Button>
      </div>
    </div>
  }
}
class BodyCell extends React.Component {
  state = {
    editing: false,
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.record), fromJS(nextProps.record)) ||
      nextState.editing !== this.state.editing
    visible: false,
    counting: false,
    priceing: false,
    curring: false,
    ratioing: false,
    origining: false,
  }
  componentDidMount () {
@@ -62,20 +154,43 @@
    this.setState({editing: false})
    if (value !== record[col.field]) {
      let line = {...record, [col.field]: value}
    let line = {...record}
    line[col.field] = value
      if (col.field === 'debtor') {
        line.creditor = ''
      } else {
    if (col.field === 'debtor') {
      line.creditor = ''
      if (isNaN(line.debtor)) {
        line.debtor = ''
      }
      MKEmitter.emit('changeRecord', col.tableId, line)
    } else {
      line.debtor = ''
      if (isNaN(line.creditor)) {
        line.creditor = ''
      }
    }
    if (line.count_type === 'Y' && line.count) {
      if (line.debtor) {
        line.price = Math.round(line.debtor / line.count * 10000) / 10000
      } else if (line.creditor) {
        line.price = Math.round(line.creditor / line.count * 10000) / 10000
      }
    }
    if (line.foreign_currency_type === 'Y' && line.origin) {
      if (line.debtor) {
        line.unitratio = Math.round(line.debtor / line.origin * 100000) / 100000
      } else if (line.creditor) {
        line.unitratio = Math.round(line.creditor / line.origin * 100000) / 100000
      }
    }
    MKEmitter.emit('changeRecord', col.tableId, line)
    setTimeout(() => {
      if (col.field === 'creditor') {
      if (col.field === 'debtor' && (line.debtor || line.debtor === 0)) {
        MKEmitter.emit('nextLine', col, record)
      } else if (col.field === 'creditor') {
        MKEmitter.emit('nextLine', col, record)
      } else {
        let cl = {remark: 'subjectscode', subjectscode: 'debtor', debtor: 'creditor'}
@@ -91,10 +206,8 @@
    if (col.field === 'subjectscode') {
      this.setState({editing: true}, () => {
        try {
          let node = document.getElementById(col.uuid + record.uuid)
          node.click()
        } catch(e) {}
        let node = document.getElementById(col.uuid + record.uuid)
        node && node.click()
      })
    } else {
      this.setState({editing: true, value: record[col.field]}, () => {
@@ -110,15 +223,35 @@
    this.setState({editing: false})
    if (col.field === 'subjectscode') return
    if (value !== record[col.field]) {
      let line = {...record, [col.field]: value}
      if (col.field === 'debtor') {
        line.creditor = ''
        if (isNaN(line.debtor)) {
          line.debtor = ''
        }
      } else {
        line.debtor = ''
        if (isNaN(line.creditor)) {
          line.creditor = ''
        }
      }
      if (line.count_type === 'Y' && line.count) {
        if (line.debtor) {
          line.price = Math.round(line.debtor / line.count * 10000) / 10000
        } else if (line.creditor) {
          line.price = Math.round(line.creditor / line.count * 10000) / 10000
        }
      }
      if (line.foreign_currency_type === 'Y' && line.origin) {
        if (line.debtor) {
          line.unitratio = Math.round(line.debtor / line.origin * 100000) / 100000
        } else if (line.creditor) {
          line.unitratio = Math.round(line.creditor / line.origin * 100000) / 100000
        }
      }
      MKEmitter.emit('changeRecord', col.tableId, line)
@@ -135,26 +268,382 @@
    })
  }
  plusLine = () => {
    const { col, record } = this.props
    MKEmitter.emit('plusLine', col.tableId, record)
  }
  delRecord = () => {
    const { col, record } = this.props
    MKEmitter.emit('delRecord', col.tableId, record)
  }
  onSelectBlur = () => {
    const { visible } = this.state
    if (visible) return
    this.setState({editing: false})
  }
  onSelectChange = (val, option) => {
    const { col, record } = this.props
    this.setState({editing: false})
    let line = {...record, ...option.props.extra}
    if (record.sup_accounting && !line.sup_accounting) {
      record.sup_accounting.split(',').forEach(item => {
        line[item] = ''
      })
    }
    if (line.foreign_currency_type === 'Y' && line.foreign_currency) {
      let msg = window.GLOB.CacheVoucher.get(col.tableId)
      let cur = msg ? msg.currency.filter(n => n.exratename === line.foreign_currency)[0] : null
      if (cur) {
        line = {...line, ...cur}
      }
      this.currencyChange(line)
    }
    MKEmitter.emit('changeRecord', col.tableId, line)
    if (line.sup_accounting) {
      setTimeout(() => {
        this.setState({visible: true})
      }, 100)
    } else if (line.count_type === 'Y') {
      this.setState({counting: true, value: line.count || 0}, () => {
        let node = document.getElementById(col.uuid + record.uuid + 'count')
        node && node.select()
      })
    } else if (line.foreign_currency_type === 'Y') {
      this.setState({curring: true}, () => {
        let node = document.getElementById(col.uuid + record.uuid + 'currency')
        node && node.click()
      })
    } else {
      this.setState({editing: false, visible: false, counting: false, priceing: false})
      setTimeout(() => {
        MKEmitter.emit('tdFocus', 'debtor' + record.uuid)
      }, 50)
    }
  }
  confirm = (res) => {
    const { col } = this.props
    MKEmitter.emit('changeRecord', col.tableId, fromJS(res).toJS())
    this.setState({editing: false, visible: false})
    if (res.count_type === 'Y') {
      this.setState({counting: true, value: res.count || 0}, () => {
        let node = document.getElementById(col.uuid + res.uuid + 'count')
        node && node.select()
      })
    } else {
      setTimeout(() => {
        MKEmitter.emit('tdFocus', 'debtor' + res.uuid)
      }, 50)
    }
  }
  cancel = () => {
    const { col } = this.props
    let record = fromJS(this.props.record).toJS()
    record.balance_direction = ''
    record.count_type = ''
    record.foreign_currency_type = ''
    record.mnemonic_code = ''
    record.subjectscode = ''
    record.subjectsname = ''
    record.sup_accounting.split(',').forEach(item => {
      record[item] = ''
    })
    record.sup_accounting = ''
    MKEmitter.emit('changeRecord', col.tableId, record)
    this.setState({editing: false, visible: false})
  }
  editCount = (e) => {
    const { col, record } = this.props
    e.stopPropagation()
    this.setState({counting: true, value: record.count || 0}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'count')
      node && node.select()
    })
  }
  editPrice = (e) => {
    const { col, record } = this.props
    e.stopPropagation()
    this.setState({priceing: true, value: record.price || 0}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'price')
      node && node.select()
    })
  }
  countPress = () => {
    const { col, record } = this.props
    const { value } = this.state
    let line = {...record}
    line.count = value || 0
    if (isNaN(line.count)) {
      line.count = 0
    }
    this.countChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
    this.setState({counting: false, priceing: true, value: line.price || 0}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'price')
      node && node.select()
    })
  }
  countBlur = () => {
    const { col, record } = this.props
    const { value } = this.state
    this.setState({counting: false})
    let line = {...record}
    line.count = value || 0
    if (isNaN(line.count)) {
      line.count = 0
    }
    this.countChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
  }
  pricePress = () => {
    const { col, record } = this.props
    const { value } = this.state
    let line = {...record}
    line.price = value || 0
    if (isNaN(line.price)) {
      line.price = 0
    }
    this.countChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
    this.setState({priceing: false})
    setTimeout(() => {
      MKEmitter.emit('tdFocus', 'debtor' + record.uuid)
    }, 50)
  }
  priceBlur = () => {
    const { col, record } = this.props
    const { value } = this.state
    this.setState({priceing: false})
    let line = {...record}
    line.price = value || 0
    if (isNaN(line.price)) {
      line.price = 0
    }
    this.countChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
  }
  editCurrency = (e) => {
    const { col, record } = this.props
    e.stopPropagation()
    this.setState({curring: true}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'currency')
      node && node.click()
    })
  }
  onCurrSelectChange = (val, option) => {
    const { col, record } = this.props
    let line = {...record, ...option.props.extra}
    this.currencyChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
    if (line.exratename === 'CNY') {
      this.setState({curring: false, origining: true, value: line.origin || 0}, () => {
        let node = document.getElementById(col.uuid + record.uuid + 'origin')
        node && node.select()
      })
    } else {
      this.setState({curring: false, ratioing: true, value: line.unitratio || 1}, () => {
        let node = document.getElementById(col.uuid + record.uuid + 'ratio')
        node && node.select()
      })
    }
  }
  editRatio = (e) => {
    const { col, record } = this.props
    e.stopPropagation()
    this.setState({ratioing: true, value: record.unitratio || 1}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'ratio')
      node && node.select()
    })
  }
  ratioPress = () => {
    const { col, record } = this.props
    const { value } = this.state
    let line = {...record}
    line.unitratio = value || 1
    if (isNaN(line.unitratio)) {
      line.unitratio = 1
    }
    this.currencyChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
    this.setState({ratioing: false, origining: true, value: line.origin || 0}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'origin')
        node && node.select()
    })
  }
  ratioBlur = () => {
    const { col, record } = this.props
    const { value } = this.state
    this.setState({ratioing: false})
    let line = {...record}
    line.unitratio = value || 1
    if (isNaN(line.unitratio)) {
      line.unitratio = 1
    }
    this.currencyChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
  }
  editOrigin = (e) => {
    const { col, record } = this.props
    e.stopPropagation()
    this.setState({origining: true, value: record.origin || 1}, () => {
      let node = document.getElementById(col.uuid + record.uuid + 'origin')
      node && node.select()
    })
  }
  originPress = () => {
    const { col, record } = this.props
    const { value } = this.state
    let line = {...record}
    line.origin = value || 0
    if (isNaN(line.origin)) {
      line.origin = 0
    }
    this.currencyChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
    this.setState({origining: false})
    setTimeout(() => {
      MKEmitter.emit('tdFocus', 'debtor' + record.uuid)
    }, 50)
  }
  originBlur = () => {
    const { col, record } = this.props
    const { value } = this.state
    this.setState({origining: false})
    let line = {...record}
    line.origin = value || 0
    if (isNaN(line.origin)) {
      line.origin = 0
    }
    this.currencyChange(line)
    MKEmitter.emit('changeRecord', col.tableId, line)
  }
  countChange = (line) => {
    if (line.count && line.price) {
      if (line.creditor) {
        line.creditor = Math.round(line.count * line.price * 100) / 100
      } else {
        line.debtor = Math.round(line.count * line.price * 100) / 100
      }
      if (line.foreign_currency_type === 'Y' && line.origin) {
        if (line.debtor) {
          line.unitratio = Math.round(line.debtor / line.origin * 100000) / 100000
        } else if (line.creditor) {
          line.unitratio = Math.round(line.creditor / line.origin * 100000) / 100000
        }
      }
    }
  }
  currencyChange = (line) => {
    if (line.unitratio && line.origin) {
      if (line.creditor) {
        line.creditor = Math.round(line.unitratio * line.origin * 100) / 100
      } else {
        line.debtor = Math.round(line.unitratio * line.origin * 100) / 100
      }
      if (line.count_type === 'Y' && line.count) {
        if (line.debtor) {
          line.price = Math.round(line.debtor / line.count * 10000) / 10000
        } else if (line.creditor) {
          line.price = Math.round(line.creditor / line.count * 10000) / 10000
        }
      }
    }
  }
  render() {
    let { col, record, subjects, className } = this.props
    const { editing } = this.state
    let { col, record, className } = this.props
    const { editing, visible, counting, priceing, curring, ratioing, origining } = this.state
    let children = null
    let colSpan = 1
    let extra = null
    if (col.field === 'remark') {
      let val = record.remark || ''
@@ -163,6 +652,8 @@
        children = <div className="content-wrap" style={{lineHeight: '60px'}}>合计: {val}</div>
        colSpan = 2
      } else {
        extra = <PlusOutlined onClick={this.plusLine}/>
        if (editing) {
          let options = ['现金', '发票']
          children = <AutoComplete
@@ -186,60 +677,197 @@
        colSpan = 0
      } else {
        if (editing) {
          children = <Select
            showSearch
            defaultValue={record.subjectscode || ''}
            dropdownClassName="edit-table-dropdown"
            id={col.uuid + record.uuid}
            onBlur={this.onBlur}
            filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
            onSelect={this.onSelectChange}
          >
            {subjects.map((item, i) => (<Select.Option key={i} extra={item} value={item.subjectscode}>{item.subjectscode + ' ' + item.subjectsname}</Select.Option>))}
          </Select>
          let msg = window.GLOB.CacheVoucher.get(col.tableId)
          let subjects = msg ? msg.subjects : []
          children = <>
            <Select
              showSearch
              defaultValue={record.subjectscode || ''}
              dropdownClassName="edit-table-dropdown"
              id={col.uuid + record.uuid}
              onBlur={this.onSelectBlur}
              filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
              onSelect={this.onSelectChange}
            >
              {subjects.map((item, i) => (<Select.Option key={i} extra={item} value={item.subjectscode}>{item.subjectscode + ' ' + item.subjectsname}</Select.Option>))}
            </Select>
            <Popover overlayClassName="subject-pop-wrap" placement="bottom" title="" visible={visible} content={<Accounting confirm={this.confirm} cancel={this.cancel} tableId={col.tableId} data={record}/>}>
              <span className="pop-anchor"></span>
            </Popover>
          </>
        } else {
          let val = ''
          if (record.subjectscode) {
            val = (record.subjectscode || '') + ' ' + (record.subjectsname || '')
            record.sup_accounting && record.sup_accounting.split(',').forEach(item => {
              if (record[item]) {
                val += '_' + record[item]
              }
            })
          }
          children = <div className="content-wrap" onClick={this.focus}>{val}</div>
          let countNode = null
          let currencyNode = null
          if (record.count_type === 'Y') {
            if (counting) {
              countNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={(e) => e.stopPropagation()}>
                  <span>数量:</span>
                  <span><InputNumber precision={4} className="inner-input" id={col.uuid + record.uuid + 'count'} defaultValue={record.count || 0} onChange={(val) => this.onChange(val)} onPressEnter={this.countPress} onBlur={this.countBlur}/></span>
                </span>
                <span onClick={this.editPrice}>
                  <span>单价:</span>
                  <span>{record.price || 0}</span>
                </span>
              </div>
            } else if (priceing) {
              countNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCount}>
                  <span>数量:</span>
                  <span>{record.count || 0}</span>
                </span>
                <span onClick={(e) => e.stopPropagation()}>
                  <span>单价:</span>
                  <span><InputNumber precision={4} className="inner-input" id={col.uuid + record.uuid + 'price'} defaultValue={record.price || 0} onChange={(val) => this.onChange(val)} onPressEnter={this.pricePress} onBlur={this.priceBlur}/></span>
                </span>
              </div>
            } else {
              countNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCount}>
                  <span>数量:</span>
                  <span>{record.count || 0}</span>
                </span>
                <span onClick={this.editPrice}>
                  <span>单价:</span>
                  <span>{record.price || 0}</span>
                </span>
              </div>
            }
          }
          if (record.foreign_currency_type === 'Y') {
            if (curring) {
              let msg = window.GLOB.CacheVoucher.get(col.tableId)
              let currency = msg ? msg.currency : []
              currencyNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={(e) => e.stopPropagation()}>
                  <span>货币:</span>
                  <span>
                    <Select
                      className="currency-select"
                      defaultValue={record.exratename || ''}
                      dropdownClassName="edit-table-dropdown"
                      id={col.uuid + record.uuid + 'currency'}
                      onBlur={() => this.setState({curring: false})}
                      onSelect={this.onCurrSelectChange}
                    >
                      {currency.map((item, i) => (<Select.Option key={i} extra={item} value={item.exratename}>{item.exratename}</Select.Option>))}
                    </Select>
                  </span>
                </span>
                <span style={{marginRight: '5px'}} onClick={this.editRatio}>
                  <span>汇率:</span>
                  <span>{record.unitratio || 1}</span>
                </span>
                <span onClick={this.editOrigin}>
                  <span>原币:</span>
                  <span>{record.origin || 0}</span>
                </span>
              </div>
            } else if (ratioing) {
              currencyNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCurrency}>
                  <span>货币:</span>
                  <span>{record.exratename || ''}</span>
                </span>
                <span style={{marginRight: '5px'}} onClick={(e) => e.stopPropagation()}>
                  <span>汇率:</span>
                  <span><InputNumber precision={5} className="inner-input" id={col.uuid + record.uuid + 'ratio'} defaultValue={record.unitratio || 1} onChange={(val) => this.onChange(val)} onPressEnter={this.ratioPress} onBlur={this.ratioBlur}/></span>
                </span>
                <span onClick={this.editOrigin}>
                  <span>原币:</span>
                  <span>{record.origin || 0}</span>
                </span>
              </div>
            } else if (origining) {
              currencyNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCurrency}>
                  <span>货币:</span>
                  <span>{record.exratename || ''}</span>
                </span>
                <span style={{marginRight: '5px'}} onClick={this.editRatio}>
                  <span>汇率:</span>
                  <span>{record.unitratio || 1}</span>
                </span>
                <span onClick={(e) => e.stopPropagation()}>
                  <span>原币:</span>
                  <span><InputNumber precision={2} className="inner-input" id={col.uuid + record.uuid + 'origin'} defaultValue={record.origin || 0} onChange={(val) => this.onChange(val)} onPressEnter={this.originPress} onBlur={this.originBlur}/></span>
                </span>
              </div>
            } else {
              currencyNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCurrency}>
                  <span>货币:</span>
                  <span>{record.exratename || ''}</span>
                </span>
                <span style={{marginRight: '5px'}} onClick={this.editRatio}>
                  <span>汇率:</span>
                  <span>{record.unitratio || 1}</span>
                </span>
                <span onClick={this.editOrigin}>
                  <span>原币:</span>
                  <span>{record.origin || 0}</span>
                </span>
              </div>
            }
          }
          children = <div className="content-wrap" onClick={this.focus}>
            {val}
            {countNode}
            {currencyNode}
          </div>
        }
      }
    } else if (col.field === 'debtor') {
      let val = record.debtor
      let down = false
      let vals = []
      if (typeof(val) === 'number') {
        if (val < 0) {
          down = true
          val = Math.abs(val)
        }
        vals = (val * 100).toFixed(0).split('').reverse()
      }
      if (editing) {
        children = <InputNumber id={col.uuid + record.uuid} defaultValue={val} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/>
        children = <InputNumber id={col.uuid + record.uuid} precision={2} defaultValue={record.debtor} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/>
      } else {
        let val = record.debtor
        let down = false
        let vals = []
        if (!isNaN(val) && val !== '') {
          if (val < 0) {
            down = true
            val = Math.abs(val)
          }
          vals = (val * 100).toFixed(0).split('').reverse()
        }
        children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}>
          <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
          <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
        </div>
      }
    } else if (col.field === 'creditor') {
      let val = record.creditor
      let down = false
      let vals = []
      if (typeof(val) === 'number') {
        if (val < 0) {
          down = true
          val = Math.abs(val)
        }
        vals = (val * 100).toFixed(0).split('').reverse()
      }
      extra = <CloseOutlined onClick={this.delRecord}/>
      if (editing) {
        children = <InputNumber id={col.uuid + record.uuid} defaultValue={val} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/>
        children = <InputNumber id={col.uuid + record.uuid} precision={2} defaultValue={record.creditor} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/>
      } else {
        let val = record.creditor
        let down = false
        let vals = []
        if (!isNaN(val) && val !== '') {
          if (val < 0) {
            down = true
            val = Math.abs(val)
          }
          vals = (val * 100).toFixed(0).split('').reverse()
        }
        children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}>
        <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
        <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
@@ -249,34 +877,27 @@
    if (!colSpan) return null
    return (<td colSpan={colSpan} className={className}>{children}</td>)
    return (<td colSpan={colSpan} className={className}>{children}{extra}</td>)
  }
}
class VoucherTable extends Component {
  static propTpyes = {
    config: PropTypes.object,        // 菜单Id
    subjects: PropTypes.array,       // 会计科目
    BID: PropTypes.any,              // 主表ID
    data: PropTypes.any,             // 表格数据
    total: PropTypes.any,            // 总数
    loading: PropTypes.bool,         // 表格加载中
    refreshdata: PropTypes.func,     // 表格中排序列、页码的变化时刷新
    onChange: PropTypes.func,        // 表格变动
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    data: [],
    edData: [],
    edColumns: [],
    tableId: '',          // 表格ID
    pageSize: 10,         // 每页数据条数
    columns: null,        // 显示列
    loading: false,
  }
  UNSAFE_componentWillMount () {
    const { config, subjects, data } = this.props
    const { config, data } = this.props
    let columns = [
      {
@@ -296,7 +917,6 @@
        width: '34%',
        onCell: record => ({
          record,
          subjects,
          col: {uuid: 'subjectscode', field: 'subjectscode', tableId: config.uuid},
        })
      },
@@ -347,7 +967,9 @@
  componentDidMount () {
    MKEmitter.addListener('nextLine', this.nextLine)
    MKEmitter.addListener('plusLine', this.plusLine)
    MKEmitter.addListener('delRecord', this.delRecord)
    MKEmitter.addListener('cleartable', this.cleartable)
    MKEmitter.addListener('changeRecord', this.changeRecord)
  }
@@ -359,40 +981,24 @@
      return
    }
    MKEmitter.removeListener('nextLine', this.nextLine)
    MKEmitter.removeListener('plusLine', this.plusLine)
    MKEmitter.removeListener('delRecord', this.delRecord)
    MKEmitter.removeListener('cleartable', this.cleartable)
    MKEmitter.removeListener('changeRecord', this.changeRecord)
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!is(fromJS(this.props.data), fromJS(nextProps.data))) {
      this.resetData(fromJS(nextProps.data).toJS())
    } else if (!is(fromJS(this.props.subjects), fromJS(nextProps.subjects))) {
      this.resetSubjects(nextProps.subjects)
    }
  }
  resetSubjects = (subjects) => {
    const { config } = this.props
    let columns = fromJS(this.state.columns).toJS()
  cleartable = (tbid) => {
    const { tableId } = this.state
    this.setState({
      columns: columns.map(col => {
        if (col.key === 'subjectscode') {
          return {
            title: '会计科目',
            dataIndex: 'subjectscode',
            key: 'subjectscode',
            width: '34%',
            onCell: record => ({
              record,
              subjects,
              col: {uuid: 'subjectscode', field: 'subjectscode', tableId: config.uuid},
            })
          }
        }
        return col
      })
    })
    if (tbid !== tableId) return
    this.resetData([])
  }
  resetData = (data) => {
@@ -400,39 +1006,44 @@
    data.push(this.getTotalLine(data))
    this.setState({
      edData: data
      edData: []
    }, () => {
      this.setState({
        edData: data
      })
    })
  }
  initData = (data) => {
    let _data = data.map((item, i) => {
      item.uuid = Utils.getuuid()
      // item.uuid = Utils.getguid()
      item.index = i
      item.$origin = true
      
      return item
    })
    if (_data.length < 4) {
      for (let i = _data.length - 1; i < 4; i++) {
        _data.push({uuid: Utils.getuuid(), index: i + 1, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''})
        _data.push({uuid: Utils.getguid(), index: i + 1, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''})
      }
    }
    return _data
  }
  getTotalLine = (data) => {
    let totalLine = {uuid: Utils.getuuid(), type: 'total'}
    let totalLine = {uuid: Utils.getguid(), type: 'total'}
    let debtor = ''
    let creditor = ''
    data.forEach(item => {
      if (typeof(item.debtor) === 'number') {
      if (!isNaN(item.debtor) && item.debtor !== '') {
        if (debtor === '') {
          debtor = 0
        }
        debtor += item.debtor
      } else if (typeof(item.creditor) === 'number') {
      } else if (!isNaN(item.creditor) && item.creditor !== '') {
        if (debtor === '') {
          debtor = 0
        }
@@ -543,27 +1154,33 @@
      MKEmitter.emit('tdFocus', 'remark' + edData[record.index + 1].uuid)
    } else {
      let _data = fromJS(edData).toJS()
      let line = {uuid: Utils.getuuid(), index: _data.length - 1, remark: record.remark || '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''}
      let line = {uuid: Utils.getguid(), index: _data.length - 1, remark: record.remark || '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''}
      _data.splice(_data.length - 1, 0, line)
      this.setState({edData: _data}, () => {
        MKEmitter.emit('tdFocus', 'remark' + line.uuid)
      })
      this.props.onChange(_data)
    }
  }
  plusLine = (initEditLine) => {
    const { edData } = this.state
  plusLine = (tid, record) => {
    const { edData, tableId } = this.state
    let item = {...edData[edData.length - 1]}
    if (tid !== tableId) return
    item.key = item.key + 1
    item.$$uuid = '$new'
    let _data = fromJS(edData).toJS()
    let line = {uuid: Utils.getguid(), index: 0, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''}
    this.setState({edData: [...edData, item]}, () => {
      MKEmitter.emit('tdFocus', initEditLine.uuid + item.uuid)
    _data.splice(record.index, 0, line)
    _data = _data.map((item, index) => {
      item.index = index
      return item
    })
    this.setState({edData: _data})
    this.props.onChange(_data)
  }
  delRecord = (id, record) => {
@@ -571,19 +1188,25 @@
    if (id !== tableId) return
    let _data = edData.filter(item => item.uuid !== record.uuid)
    let _data = fromJS(edData).toJS().filter(item => item.uuid !== record.uuid)
    _data.pop()
    if (_data.length < 4) {
      for (let i = _data.length - 1; i < 4; i++) {
        _data.push({uuid: Utils.getuuid(), index: i + 1, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''})
      for (let i = _data.length; i < 4; i++) {
        _data.push({uuid: Utils.getguid(), index: 0, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''})
      }
    }
    _data = _data.map((item, index) => {
      item.index = index
      return item
    })
    _data.push(this.getTotalLine(_data))
    this.setState({edData: _data})
    this.props.onChange(_data, record.$origin ? record : null)
  }
  changeRecord = (tableId, record) => {
@@ -600,187 +1223,13 @@
    _data.pop()
    if (record.index === _data.length - 1) {
      _data.push({uuid: Utils.getuuid(), index: record.index + 1, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''})
      _data.push({uuid: Utils.getguid(), index: record.index + 1, remark: '', subjectscode: '', subjectsname: '', debtor: '', creditor: ''})
    }
    _data.push(this.getTotalLine(_data))
    this.setState({edData: _data})
  }
  addLine = () => {
    const { BID } = this.props
    const { edData } = this.state
    let item = {}
    if (edData.length > 0) {
      item = {...edData[edData.length - 1]}
      item.key = item.key + 1
      item.$$uuid = '$new'
    } else {
      item.key = 0
      item.$$uuid = '$new'
      item.$$BID = BID || ''
    }
    this.setState({edData: [...edData, item]})
  }
  checkData = () => {
    const { edData } = this.state
    if (edData.length === 0) {
      notification.warning({
        top: 92,
        message: '提交数据不可为空!',
        duration: 5
      })
      return
    }
    let err = ''
    let data = fromJS(edData).toJS().map(item => {
      // let line = []
      // fields.forEach(col => {
      //   if (col.editable !== 'true' || item.$deleted) {
      //     if (col.type === 'number') {
      //       item[col.field] = +item[col.field]
      //       if (isNaN(item[col.field])) {
      //         item[col.field] = 0
      //       }
      //     } else {
      //       item[col.field] = item[col.field] !== undefined ? (item[col.field] + '') : ''
      //     }
      //     return
      //   }
      //   if (col.type === 'text') {
      //     let val = item[col.field] !== undefined ? (item[col.field] + '') : ''
      //     if (col.required === 'true' && !val) {
      //       line.push(`${col.label}不可为空`)
      //     }
      //     item[col.field] = val
      //   } else if (col.type === 'number') {
      //     let val = item[col.field]
      //     if (!val && val !== 0) {
      //       line.push(`${col.label}不可为空`)
      //       return
      //     }
      //     val = +val
      //     if (isNaN(val)) {
      //       line.push(`${col.label}数据格式错误`)
      //       return
      //     }
      //     val = +val.toFixed(col.decimal || 0)
      //     if (typeof(col.max) === 'number' && val > col.max) {
      //       line.push(`${col.label}不可大于${col.max}`)
      //     } else if (typeof(col.min) === 'number' && val < col.min) {
      //       line.push(`${col.label}不可小于${col.min}`)
      //     }
      //     item[col.field] = val
      //   }
      // })
      return item
    })
    if (err) {
      notification.warning({
        top: 92,
        message: err,
        duration: 5
      })
    } else {
      this.submit(data)
    }
  }
  submit = (data) => {
    const { BID } = this.props
    let param = {
      // excel_in: result.lines,
      BID: BID || ''
    }
    this.setState({
      loading: true
    })
    param.func = 'submit.innerFunc'
    Api.genericInterface(param).then((res) => {
      if (res.status) {
        this.execSuccess(res)
      } else {
        this.execError(res)
      }
    }, () => {
      this.execError({})
    })
  }
  execSuccess = (res) => {
    const { submit } = this.props
    if (res && res.ErrCode === 'S') { // 执行成功
      notification.success({
        top: 92,
        message: res.ErrMesg || this.state.dict['main.action.confirm.success'],
        duration: submit.stime ? submit.stime : 2
      })
    } else if (res && res.ErrCode === 'Y') { // 执行成功
      Modal.success({
        title: res.ErrMesg || this.state.dict['main.action.confirm.success']
      })
    } else if (res && res.ErrCode === '-1') { // 完成后不提示
    }
    this.setState({
      loading: false
    })
    if (submit.closetab === 'true') {
      MKEmitter.emit('popclose')
    }
    if (submit.execSuccess !== 'never') {
      MKEmitter.emit('refreshByButtonResult', submit.$menuId, submit.execSuccess, submit)
    }
  }
  execError = (res) => {
    const { submit } = this.props
    if (res.ErrCode === 'E') {
      Modal.error({
        title: res.message || res.ErrMesg,
      })
    } else if (res.ErrCode === 'N') {
      notification.error({
        top: 92,
        message: res.message || res.ErrMesg,
        duration: submit.ntime ? submit.ntime : 10
      })
    } else if (res.ErrCode === 'F') {
      notification.error({
        className: 'notification-custom-error',
        top: 92,
        message: res.message || res.ErrMesg,
        duration: submit.ftime ? submit.ftime : 10
      })
    } else if (res.ErrCode === 'NM') {
      message.error(res.message || res.ErrMesg)
    }
    this.setState({
      loading: false
    })
    if (submit.execError !== 'never') {
      MKEmitter.emit('refreshByButtonResult', submit.$menuId, submit.execError, submit)
    }
    this.props.onChange(_data)
  }
  render() {
@@ -801,7 +1250,7 @@
          columns={columns}
          dataSource={edData}
          bordered={true}
          // loading={this.props.loading}
          loading={this.props.loading}
          onRow={(record, index) => {
            return {
              data: record
src/tabviews/custom/components/module/voucher/voucherTable/index.scss
@@ -90,10 +90,30 @@
        vertical-align: top;
        .content-wrap {
          padding: 5px;
          padding: 0px 5px;
          height: 100%;
          font-size: 13px;
          font-weight: bold;
          .count-wrap {
            text-align: right;
            height: 20px;
          }
          .currency-select {
            height: 20px;
            vertical-align: top;
            .ant-select-selection--single {
              height: 20px;
            }
            .ant-select-selection__rendered {
              line-height: 20px;
              margin-left: 8px;
              margin-right: 22px;
            }
            .ant-select-arrow {
              right: 6px;
            }
          }
        }
        .money-uint {
          height: 100%;
@@ -129,7 +149,7 @@
    border-radius: 0;
    resize: none;
  }
  .ant-input-number {
  .ant-input-number:not(.inner-input) {
    height: 60px;
    border-radius: 0;
    
@@ -141,7 +161,22 @@
      height: 60px;
    }
  }
  .ant-select {
  .ant-input-number.inner-input {
    display: inline-block;
    width: 50px;
    border-radius: 0;
    height: 20px;
    .ant-input-number-handler-wrap {
      display: none;
    }
    .ant-input-number-input {
      border-radius: 0;
      height: 18px;
      padding: 0 3px;
    }
  }
  .ant-select:not(.currency-select) {
    padding: 0px;
    position: absolute;
    top: 0px;
@@ -175,12 +210,12 @@
      bottom: 0px;
      border: 1px solid #1890ff;
    }
    .anticon {
      color: #ff4d4f;
      position: absolute;
      right: 3px;
      top: calc(50% - 8px);
    }
    // .anticon {
    //   color: #ff4d4f;
    //   position: absolute;
    //   right: 3px;
    //   top: calc(50% - 8px);
    // }
  }
  td.pointer {
    position: relative;
@@ -196,8 +231,93 @@
      right: 0;
    }
  }
}
  .anticon-plus {
    position: absolute;
    left: 0px;
    height: 60px;
    top: 0px;
    line-height: 60px;
    width: 25px;
    color: #26C281;
    cursor: pointer;
    display: none;
  }
  .anticon-close {
    position: absolute;
    right: 0px;
    height: 60px;
    top: 0px;
    line-height: 60px;
    width: 25px;
    color: #ff4d4f;
    cursor: pointer;
    display: none;
  }
  tr:hover {
    .anticon-plus {
      left: -25px;
      display: inline-block;
    }
    .anticon-close {
      right: -25px;
      display: inline-block;
    }
  }
  .pop-anchor {
    position: absolute;
    bottom: 0;
    left: 50%;
  }
}
.subject-pop-wrap {
  padding-top: 0px;
  .ant-popover-arrow {
    display: none;
  }
  .ant-popover-content {
    position: relative;
    z-index: 1;
  }
  .ant-form-item {
    display: flex;
    margin-bottom: 15px;
    width: 200px;
    .ant-form-item-label {
      width: 60px;
    }
    .ant-form-item-control-wrapper {
      width: calc(100% - 60px);
      .ant-select {
        width: 100%;
      }
    }
  }
  .ant-popover-inner-content {
    padding-right: 30px;
  }
  .footer {
    text-align: right;
    .ant-btn {
      border: none;
      box-shadow: none;
      margin-right: 15px;
    }
    .ant-btn:last-child {
      color: var(--mk-sys-color);
    }
  }
}
.subject-pop-wrap::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.edit-table-dropdown {
  .ant-select-dropdown-menu-item {
    white-space: unset;
src/tabviews/custom/index.jsx
@@ -241,6 +241,15 @@
        config,
        mainSearch
      }, () => {
        if (config.normalcss) {
          let node = document.getElementById(config.uuid)
          node && node.remove()
          let ele = document.createElement('style')
          ele.id = config.uuid
          ele.innerHTML = config.normalcss
          document.getElementsByTagName('head')[0].appendChild(ele)
        }
        if (params.length === 0) {
          setTimeout(() => { // 延时加载状态
            this.setState({
@@ -1314,7 +1323,7 @@
    return (
      <div className={'custom-page-wrap ' + (loadingview || loading ? 'loading' : '')} id={this.state.ContainerId} style={config ? config.style : null}>
        {(loadingview || (loading && !config.$cache)) ? <Spin className="view-spin" size="large" /> : null}
        <Row className="component-wrap">{this.getComponents()}</Row>
        <Row id={config ? 'menu' + config.uuid : ''} className="component-wrap">{this.getComponents()}</Row>
        {config && config.interfaces.length > 0 ? <MkInterfaces BID={BID} interfaces={config.interfaces}/> : null}
        {config && window.GLOB.breakpoint ? <DebugTable /> : null}
        {!window.GLOB.mkHS && window.GLOB.systemType !== 'production' ? <TableNodes config={config} /> : null}
src/tabviews/custom/index.scss
@@ -7,10 +7,7 @@
  padding-left: 16px;
  padding-right: 16px;
  background-size: 100%;
  .component-wrap >.ant-col {
    min-height: 0;
  }
  .box404 {
    padding-top: 30px;
  }
src/tabviews/custom/popview/index.scss
@@ -7,10 +7,7 @@
  padding-left: 16px;
  padding-right: 16px;
  background-size: 100%;
  .component-wrap >.ant-col {
    min-height: 0;
  }
  .box404 {
    padding-top: 30px;
  }
src/tabviews/zshare/actionList/changeuserbutton/index.jsx
@@ -34,7 +34,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -72,7 +72,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
src/tabviews/zshare/actionList/excelInbutton/index.jsx
@@ -39,7 +39,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -77,7 +77,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -18,6 +18,7 @@
  static propTpyes = {
    BID: PropTypes.string,            // 主表ID
    BData: PropTypes.any,             // 主表数据
    selectedData: PropTypes.any,      // 子表中选择数据
    btn: PropTypes.object,            // 按钮
    setting: PropTypes.any,           // 页面通用设置
    updateStatus: PropTypes.func,     // 按钮状态更新
@@ -776,6 +777,7 @@
   * @description 获取用户自定义存储过程传参
   */
  getExcelCustomParam = (orderBy, search, pagination = false, pageIndex = 1, pageSize = 100) => {
    const { btn, selectedData } = this.props
    let _search = Utils.formatCustomMainSearch(search)
    let param = {
@@ -786,6 +788,13 @@
    // 数据管理权限
    if (sessionStorage.getItem('dataM') === 'true') {
      param.dataM = 'Y'
    }
    if (btn.Ot === 'requiredOnce' && selectedData && selectedData.length > 0) {
      let primaryId = selectedData.map(d => d.$$uuid || '').filter(Boolean).join(',')
      if (primaryId) {
        param.ID = primaryId
      }
    }
    if (this.props.BID) {
@@ -804,17 +813,19 @@
   * @description 获取默认存储过程请求参数
   */
  getExcelDefaultParam = (arr_field, orderBy, search, pagination = false, pageIndex = 1, pageSize = 100) => {
    const { setting, btn } = this.props
    const { setting, btn, selectedData, BID } = this.props
    let defaultSql = setting.execute || setting.default || 'true'
    let customScript = setting.customScript || ''
    let _dataresource = setting.dataresource || ''
    let queryType = setting.queryType
    let primaryKey = setting.primaryKey || 'ID'
    if (btn.verify.dataType === 'custom') {
      defaultSql = btn.verify.defaultSql || 'true'
      _dataresource = btn.verify.dataresource || ''
      queryType = btn.verify.queryType
      primaryKey = btn.verify.primaryKey || 'ID'
      if (/\s/.test(_dataresource)) {
        _dataresource = '(' + _dataresource + ') tb'
@@ -857,8 +868,8 @@
      param.dataM = 'Y'
    }
    if (this.props.BID) {
      param.BID = this.props.BID
    if (BID) {
      param.BID = BID
    }
    let userName = sessionStorage.getItem('User_Name') || ''
@@ -914,6 +925,16 @@
      _search = ''
    }
    let primaryId = ''
    if (btn.Ot === 'requiredOnce' && selectedData && selectedData.length > 0) {
      primaryId = selectedData.map(d => d.$$uuid || '').filter(Boolean).join(',')
      if (_search && primaryId) {
        _search += ` and ${primaryKey} in (select ID from  dbo.SplitComma('${primaryId}'))`
      } else if (primaryId) {
        _search = `where ${primaryKey} in (select ID from  dbo.SplitComma('${primaryId}'))`
      }
    }
    let LText = ''
    if (defaultSql !== 'false' && !pagination) {
@@ -949,6 +970,21 @@
      `
    }
    LText = LText.replace(/@ID@/ig, `'${primaryId || ''}'`)
    param.custom_script = param.custom_script.replace(/@ID@/ig, `'${primaryId || ''}'`)
    LText = LText.replace(/@BID@/ig, `'${BID}'`)
    param.custom_script = param.custom_script.replace(/@BID@/ig, `'${BID}'`)
    LText = LText.replace(/@LoginUID@/ig, `'${sessionStorage.getItem('LoginUID') || ''}'`)
    param.custom_script = param.custom_script.replace(/@LoginUID@/ig, `'${sessionStorage.getItem('LoginUID') || ''}'`)
    LText = LText.replace(/@SessionUid@/ig, `'${localStorage.getItem('SessionUid') || ''}'`)
    param.custom_script = param.custom_script.replace(/@SessionUid@/ig, `'${localStorage.getItem('SessionUid') || ''}'`)
    LText = LText.replace(/@UserID@/ig, `'${sessionStorage.getItem('UserID') || ''}'`)
    param.custom_script = param.custom_script.replace(/@UserID@/ig, `'${sessionStorage.getItem('UserID') || ''}'`)
    LText = LText.replace(/@Appkey@/ig, `'${window.GLOB.appkey || ''}'`)
    param.custom_script = param.custom_script.replace(/@Appkey@/ig, `'${window.GLOB.appkey || ''}'`)
    LText = LText.replace(/@typename@/ig, `'admin'`)
    param.custom_script = param.custom_script.replace(/@typename@/ig, `'admin'`)
    // 测试系统打印查询语句
    if (window.GLOB.debugger === true || (window.debugger === true && options.sysType !== 'cloud')) {
      param.custom_script && console.info(`${LText ? '' : '/*不执行默认sql*/\n'}${param.custom_script}`)
src/tabviews/zshare/actionList/index.jsx
@@ -94,6 +94,7 @@
            btn={item}
            BData={BData}
            setting={setting}
            selectedData={selectedData}
          />
        )
      } else if (item.OpenType === 'popview') {
src/tabviews/zshare/actionList/newpagebutton/index.jsx
@@ -30,7 +30,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -68,7 +68,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -52,7 +52,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -107,7 +107,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
@@ -1918,21 +1918,38 @@
   * 4、模态框执行成功后是否关闭
   * 5、通知主列表刷新
   */
  execSuccess = (res) => {
  execSuccess = (res = {}) => {
    const { btn } = this.props
    const { btnconfig, autoMatic } = this.state
    if ((res && (res.ErrCode === 'S' || !res.ErrCode)) || autoMatic) { // 执行成功
    if (res.message && /^@speak@/.test(res.message)) {
      res.message = res.message.replace('@speak@', '')
      let val = res.message.match(/<<.*>>/)
      res.message = res.message.replace(/\s*<<.*>>\s*/g, '')
      val = val ? val[0].replace(/<<|>>/g, '') : ''
      if (/^http/.test(val)) {
        let audio = document.createElement('audio')
        audio.src = val
        audio.play()
      }
      if (!res.message) {
        res.ErrCode = '-1'
      }
    }
    if ((res.ErrCode === 'S' || !res.ErrCode) || autoMatic) { // 执行成功
      notification.success({
        top: 92,
        message: res.ErrMesg || this.state.dict['main.action.confirm.success'],
        message: res.message || '执行成功!',
        duration: btn.verify && btn.verify.stime ? btn.verify.stime : 2
      })
    } else if (res && res.ErrCode === 'Y') { // 执行成功
    } else if (res.ErrCode === 'Y') { // 执行成功
      Modal.success({
        title: res.ErrMesg || this.state.dict['main.action.confirm.success']
        title: res.message || '执行成功!'
      })
    } else if (res && res.ErrCode === '-1') { // 完成后不提示
    } else if (res.ErrCode === '-1') { // 完成后不提示
    }
@@ -2295,25 +2312,42 @@
    const { btn } = this.props
    const { btnconfig, autoMatic } = this.state
    if (res.message && /^@speak@/.test(res.message)) {
      res.message = res.message.replace('@speak@', '')
      let val = res.message.match(/<<.*>>/)
      res.message = res.message.replace(/\s*<<.*>>\s*/g, '')
      val = val ? val[0].replace(/<<|>>/g, '') : ''
      if (/^http/.test(val)) {
        let audio = document.createElement('audio')
        audio.src = val
        audio.play()
      }
      if (!res.message) {
        res.ErrCode = '-1'
      }
    }
    if (res.ErrCode === 'E' && !autoMatic) {
      Modal.error({
        title: res.message || res.ErrMesg,
        title: res.message || '执行失败!',
      })
    } else if (res.ErrCode === 'N' || autoMatic) {
      notification.error({
        top: 92,
        message: res.message || res.ErrMesg,
        message: res.message || '执行失败!',
        duration: btn.verify && btn.verify.ntime ? btn.verify.ntime : 10
      })
    } else if (res.ErrCode === 'F') {
      notification.error({
        className: 'notification-custom-error',
        top: 92,
        message: res.message || res.ErrMesg,
        message: res.message || '执行失败!',
        duration: btn.verify && btn.verify.ftime ? btn.verify.ftime : 10
      })
    } else if (res.ErrCode === 'NM') {
      message.error(res.message || res.ErrMesg)
      message.error(res.message || '执行失败!')
    }
    if (autoMatic) {
src/tabviews/zshare/actionList/popupbutton/index.jsx
@@ -39,7 +39,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -80,7 +80,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -48,7 +48,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -92,7 +92,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -30,7 +30,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
    if (btn.control !== 'parent' && btn.controlField && selectedData && selectedData.length > 0) { // 表格中按钮隐藏控制
      selectedData.forEach(item => {
        let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
        if (s === btn.controlVal || (btn.controlVal && btn.controlVal.split(',').includes(s))) {
@@ -68,7 +68,7 @@
    const { btn, selectedData, BData } = this.props
    let disabled = false
    if (btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
    if (btn.control !== 'parent' && btn.controlField && !is(fromJS(nextProps.selectedData || []), fromJS(selectedData || []))) {
      if (nextProps.selectedData && nextProps.selectedData.length > 0) { // 表格中按钮隐藏控制
        nextProps.selectedData.forEach(item => {
          let s = item[btn.controlField] !== undefined ? item[btn.controlField] + '' : ''
src/tabviews/zshare/mutilform/mkCheckCard/index.jsx
@@ -23,8 +23,11 @@
    let config = fromJS(this.props.config).toJS()
    let selectKeys = config.initval
    let initlength = 0
    if (config.multiple === 'true') {
      selectKeys = config.initval ? config.initval.split(',') : []
      initlength = selectKeys.length
      selectKeys = this.filterVals(config.options, selectKeys)
    }
    if (!config.selectStyle && config.backgroundColor) {
@@ -44,6 +47,10 @@
      config: config,
      options: fromJS(config.options).toJS(),
      selectKeys: selectKeys
    }, () => {
      if (config.multiple === 'true' && selectKeys.length < initlength) {
        this.props.onChange(selectKeys.join(','))
      }
    })
  }
@@ -60,9 +67,20 @@
  }
  UNSAFE_componentWillReceiveProps (nextProps) {
    const { config } = this.state
    const { config, selectKeys } = this.state
    if (!is(fromJS(config.oriOptions), fromJS(nextProps.config.oriOptions))) {
      if (config.multiple === 'true') {
        let keys = this.filterVals(nextProps.config.options, fromJS(selectKeys).toJS())
        if (keys.length < selectKeys.length) {
          this.setState({
            selectKeys: keys
          }, () => {
            this.props.onChange(keys.join(','))
          })
        }
      }
      this.setState({
        config: {...config, oriOptions: nextProps.config.oriOptions},
        options: fromJS(nextProps.config.options).toJS()
@@ -77,6 +95,16 @@
    MKEmitter.removeListener('mkFP', this.mkFormHandle)
  }
  filterVals = (options, vals) => {
    if (options.length === 0 || vals.length === 0) return vals
    let ops = options.map(item => item.$value)
    vals = vals.filter(val => ops.includes(val))
    return vals
  }
  mkFormHandle = (uuid, parentId, level) => {
    if (uuid !== this.state.config.uuid) return
src/tabviews/zshare/tablenodes/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Button, notification, Spin, Input, Typography } from 'antd'
import { Modal, Button, notification, Spin, Input, Typography, message } from 'antd'
import Api from '@/api'
import G6 from "@antv/g6"
@@ -166,6 +166,7 @@
              id: 'par' + i,
              direction: 'left',
              color: '#5AD8A6',
              node: 'table',
              children: []
            }
@@ -345,6 +346,44 @@
        ev.preventDefault();
      },
    });
    G6.registerBehavior('dice-mindmap', {
      getEvents() {
        return {
          'node:dblclick': 'editNode',
        };
      },
      editNode(evt) {
        const item = evt.item;
        const model = item.get('model');
        // 选中节点
        this.graph.getNodes().forEach(node => {
          let _model = node.get('model')
          if (_model.fontcolor === '#1890ff') {
            _model.fontcolor = ''
            this.graph.updateItem(node, _model, false)
          }
        })
        if (model.direction === 'left') {
          if (model.node === 'table') {
            model.fontcolor = '#1890ff'
            this.graph.updateItem(item, model, false)
            let oInput = document.createElement('input')
            oInput.value = model.label
            document.body.appendChild(oInput)
            oInput.select()
            document.execCommand('Copy')
            document.body.removeChild(oInput)
            message.success('表名复制成功。')
          }
          return
        }
      }
    });
    
    const dataTransform = (data) => {
      const changeData = (d, level = 0, color) => {
@@ -420,7 +459,8 @@
            },
          },
          'drag-canvas',
          'zoom-canvas'
          'zoom-canvas',
          'dice-mindmap'
        ],
      },
    });
src/templates/sharecomponent/actioncomponent/actionform/index.jsx
@@ -18,7 +18,7 @@
  prompt: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'openmenu', 'output', 'tipTitle', 'hidden'],
  exec: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'openmenu', 'output', 'hidden'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'icon', 'class', 'sheet', 'execSuccess', 'execError', 'hidden'],
  excelOut: ['label', 'OpenType', 'intertype', 'icon', 'class', 'execSuccess', 'execError', 'pagination', 'search', 'hidden'],
  excelOut: ['label', 'Ot', 'OpenType', 'intertype', 'icon', 'class', 'execSuccess', 'execError', 'pagination', 'search', 'hidden'],
  popview: ['label', 'Ot', 'OpenType', 'icon', 'class', 'position', 'tabType', 'linkTab', 'popClose', 'display', 'ratio', 'clickouter', 'hidden'],
  tab: ['label', 'Ot', 'OpenType', 'icon', 'class', 'position', 'linkmenu', 'hidden'],
  innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'icon', 'class', 'position', 'hidden'],
@@ -232,6 +232,7 @@
      }
    } else if (openType === 'excelOut') {
      reOptions.intertype = this.state.interTypeOptions.filter(op => op.value !== 'custom')
      reOptions.Ot = requireOptions.filter(op => ['notRequired', 'requiredOnce'].includes(op.value))
      if (this.record.intertype === 'outer') {
        shows.push('innerFunc', 'sysInterface', 'interface', 'outerFunc')
@@ -249,6 +250,9 @@
      } else if (this.record.intertype === 'inner') {
        shows.push('innerFunc')
        reRequired.innerFunc = true
      }
      if (this.record.Ot !== 'notRequired' && this.record.Ot !== 'requiredOnce') {
        this.record.Ot = 'notRequired'
      }
    } else if (openType === 'popview') {
      reOptions.Ot = requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
@@ -317,7 +321,7 @@
      reOptions.Ot = requireOptions.filter(op => op.value === 'requiredSgl')
    }
    
    if (Ot !== 'notRequired') {
    if (Ot !== 'notRequired' && openType !== 'excelOut') {
      reOptions.control = [
        { value: '', text: '无' },
        { value: 'disabled', text: '禁用' },
@@ -387,6 +391,7 @@
        _fieldval.label = '导出Excel'
        _fieldval.class = 'dgreen'
        _fieldval.execSuccess = 'never'
        _fieldval.Ot = 'notRequired'
        this.record.Ot = 'notRequired'
        this.record.label = '导出Excel'
        this.record.class = 'dgreen'
@@ -683,7 +688,7 @@
          values.position = values.position || 'toolbar'
          if (values.OpenType === 'excelOut') {
            values.Ot = 'notRequired'
            values.Ot = values.Ot || 'notRequired'
          } else if (values.OpenType === 'popview' && !values.linkTab) { // 没有关联标签(新建时),创建新标签Id
            values.linkTab = Utils.getuuid()
          } else if (['pop', 'prompt', 'exec'].includes(values.OpenType) && values.verify) {
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
@@ -249,7 +249,6 @@
      col.required = col.required || 'true'
      col.type = col.type || 'Nvarchar(50)'
      col.import = col.import || 'true'
      col.required = col.required || 'true'
      if (col.type === 'text' || col.type === 'image') {
        col.type = 'Nvarchar(50)'
@@ -365,7 +364,7 @@
    let _columns = JSON.parse(JSON.stringify(verify.columns))
    let _cols = _columns.map(col => col.Column )
    let _cols = _columns.map(col => col.Column)
    columns.forEach(col => {
      if (col.field && !_cols.includes(col.field)) {
@@ -455,6 +454,13 @@
    const { verify } = this.state
    columns = columns.map(col => {
      col.type = col.type || 'Nvarchar(50)'
      if (col.type === 'text' || col.type === 'image') {
        col.type = 'Nvarchar(50)'
      } else if (col.type === 'number') {
        col.type = 'Decimal(18,2)'
      }
      if (/^Nvarchar/ig.test(col.type)) {
        col.limit = col.type.match(/\d+/) ? col.type.match(/\d+/)[0] : '20000'
      } else if (/^Decimal/ig.test(col.type)) {
@@ -466,6 +472,9 @@
        col.limit = ''
      }
      col.required = col.required || 'true'
      col.import = col.import || 'true'
      return col
    })
src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx
@@ -199,6 +199,13 @@
              </Form.Item>
            </Col> : null}
            {dataType === 'custom' && defaultSql === 'true' ? <Col className="short-label" span={8}>
              <Form.Item label="主键">
                {getFieldDecorator('primaryKey', {
                  initialValue: setting.primaryKey || 'ID',
                })(<Input placeholder={''} autoComplete="off" />)}
              </Form.Item>
            </Col> : null}
            {dataType === 'custom' && defaultSql === 'true' ? <Col className="short-label" span={8}>
              <Form.Item label="排序方式">
                {getFieldDecorator('order', {
                  initialValue: setting.order || '',
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
@@ -811,22 +811,37 @@
  changeColumns = (columns) => {
    const { verify } = this.state
    if (columns[0] && !['image', 'text', 'number'].includes(columns[0].type)) {
      columns = columns.map(col => {
        let _cell = {
          uuid: Utils.getuuid(),
          Column: col.Column,
          Text: col.Text,
          Width: 20,
          abs: 'false',
          output: col.output || 'true',
          required: col.required || 'false',
          type: 'text',
        }
    columns = columns.map(col => {
      col.type = col.type || 'text'
      col.output = col.output || 'true'
      col.required = col.required || 'false'
        return _cell
      })
    }
      if (!['text', 'image', 'number'].includes(col.type)) {
        if (/^Decimal/ig.test(col.type)) {
          col.type = 'number'
        } else {
          col.type = 'text'
        }
      }
      return col
    })
    // if (columns[0] && !['image', 'text', 'number'].includes(columns[0].type)) {
    //   columns = columns.map(col => {
    //     let _cell = {
    //       uuid: Utils.getuuid(),
    //       Column: col.Column,
    //       Text: col.Text,
    //       Width: 20,
    //       abs: 'false',
    //       output: col.output || 'true',
    //       required: col.required || 'false',
    //       type: 'text',
    //     }
    //     return _cell
    //   })
    // }
    this.setState({verify: {...verify, columns}})
  }
src/templates/sharecomponent/fieldscomponent/index.jsx
@@ -169,10 +169,12 @@
      this.props.updatefield(items)
    } else if (type === 'form') {
      let lastItem = config.fields[config.fields.length - 1]
      let firstItem = config.fields[0]
      let span = this.state.appType === 'mob' ? 24 : 12
      if (lastItem && lastItem.span) {
        span = lastItem.span
      let labelwidth = 33.3
      if (firstItem && firstItem.span) {
        span = firstItem.span
        labelwidth = firstItem.labelwidth || 33.3
      }
      selectCards.forEach(item => { // 循环添加新增字段
        let newcard = {
@@ -183,7 +185,7 @@
          type: item.type,
          resourceType: '0',
          span: span,
          labelwidth: 33.3,
          labelwidth: labelwidth,
          options: [],
          dataSource: '',
          decimal: item.decimal,
@@ -195,17 +197,19 @@
        if (item.type === 'text' && item.length >= 256) {
          newcard.type = 'textarea'
          newcard.fieldlength = item.length
          if (lastItem && lastItem.type === newcard.type) {
            newcard.span = lastItem.span
            newcard.labelwidth = lastItem.labelwidth
          } else if (lastItem) {
            newcard.span = 24
            if (lastItem.span === 12) {
              newcard.labelwidth = 16.2
            } else if (lastItem.span === 8) {
              newcard.labelwidth = 10.5
            } else if (lastItem.span === 8) {
              newcard.labelwidth = 7.7
          if (firstItem) {
            if (firstItem.type === newcard.type) {
              newcard.span = firstItem.span
              newcard.labelwidth = firstItem.labelwidth
            } else {
              newcard.span = 24
              if (firstItem.span === 12) {
                newcard.labelwidth = 16.2
              } else if (firstItem.span === 8) {
                newcard.labelwidth = 10.5
              } else if (firstItem.span === 6) {
                newcard.labelwidth = 7.7
              }
            }
          } else {
            newcard.span = 24
src/templates/zshare/formconfig.jsx
@@ -1268,7 +1268,7 @@
      key: 'execSuccess',
      label: '成功后',
      initVal: card.execSuccess || 'grid',
      tooltip: '选择刷新行时,如果选择多条数据会刷新表格。',
      tooltip: '选择刷新行时,如果选择多条数据会刷新表格。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。',
      required: true,
      options: [{
        value: 'never',
@@ -1287,7 +1287,7 @@
      key: 'execError',
      label: '失败后',
      initVal: card.execError || 'never',
      tooltip: '选择刷新行时,如果选择多条数据会刷新表格。',
      tooltip: '选择刷新行时,如果选择多条数据会刷新表格。如需语音播报请以@speak@开头,播报内容或文件放置于<<>>中。',
      required: true,
      options: [{
        value: 'never',
src/templates/zshare/modalform/modaleditable/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Table, Input, Popconfirm, Form, Radio, message } from 'antd'
import { Table, Input, Popconfirm, Form, message } from 'antd'
import { ArrowUpOutlined, ArrowDownOutlined, DeleteOutlined, PlusOutlined, SwapOutlined } from '@ant-design/icons'
import { formRule } from '@/utils/option.js'
@@ -140,27 +140,9 @@
    })
  }
  getColumnSearchProps = column => ({
    filterDropdown: () => (
      <div style={{ padding: 8 }}>
        <Radio.Group onChange={(e) => this.changeDatatype(column, e)} value={column.datatype}>
          <Radio style={{display: 'block', height: '30px', lineHeight: '30px'}} value="string">
            字符串
          </Radio>
          <Radio style={{display: 'block', height: '30px', lineHeight: '30px'}} value="number">
            数字
          </Radio>
        </Radio.Group>
      </div>
    ),
    filterIcon: () => (
      <SwapOutlined style={{ color: column.datatype === 'number' ? '#1890ff' : ''}} />
    )
  })
  changeDatatype = (column, e) => {
  changeDatatype = (column) => {
    const { columns, dataSource } = this.state
    let value = e.target.value
    let value = column.datatype !== 'number' ? 'number' : 'string'
    let _data = dataSource.map(item => {
      let val = item[column.dataIndex]
      if (value === 'number') {
@@ -185,7 +167,16 @@
        }
        if (col.dataIndex !== 'operation') {
          col = {...col, ...this.getColumnSearchProps(col)}
          col.title = <div>
            Value
            <Popconfirm
              title={`确定切换为${col.datatype === 'number' ? '文本' : '数值'}吗?`}
              overlayClassName="popover-confirm"
              onConfirm={() => this.changeDatatype(col)
            }>
              <SwapOutlined style={{ color: col.datatype === 'number' ? '#1890ff' : ''}} />
            </Popconfirm>
          </div>
        }
        return col
@@ -376,7 +367,16 @@
    return {
      columns: columns.map(col => {
        if (col.dataIndex !== 'operation') {
          col = {...col, ...this.getColumnSearchProps(col)}
          col.title = <div>
            Value
            <Popconfirm
              title={`确定切换为${col.datatype === 'number' ? '文本' : '数值'}吗?`}
              overlayClassName="popover-confirm"
              onConfirm={() => this.changeDatatype(col)
            }>
              <SwapOutlined style={{ color: col.datatype === 'number' ? '#1890ff' : ''}} />
            </Popconfirm>
          </div>
        }
        return col
      }),
src/templates/zshare/modalform/modaleditable/index.scss
@@ -17,6 +17,15 @@
  }
  .ant-table-thead > tr > th {
    padding: 10px 16px;
    position: relative;
    .anticon-swap {
      position: absolute;
      right: 16px;
      font-size: 14px;
      top: 12px;
      color: #b8b8b8;
    }
  }
  .ant-table-tbody > tr > td {
    padding: 5px 10px;
src/views/appmanage/index.jsx
@@ -469,6 +469,7 @@
              cell.sysBgColor = _param.sysBgColor || '#ffffff'
              cell.direction = _param.direction || 'vertical'
              cell.adapter = _param.adapter || ''
              cell.topHeight = _param.topHeight || ''
              cell.share = _param.share || 'false' // 分享
              cell.share_des = _param.share_des || '' // 分享描述
              cell.share_url = _param.share_url || '' // 分享图片
@@ -652,7 +653,7 @@
    })
    // 子应用ID、typename、应用ID、CloudUserID、appkey、login_types(是否需要登录,已弃用)、link_type(是否使用短连接,已弃用)、role_type(是否使用角色管理)、lang、css(皮肤)、title(标题)、favicon(图标)、user_binding(用户绑定)、sms_id(短信模板ID)、自定义
    param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', direction: item.direction || 'vertical', adapter: item.adapter || '', share: item.share || '', share_des: item.share_des || '', share_url: item.share_url || '', share_link: item.share_link || ''})))}'`)
    param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', topHeight: item.topHeight || '', sysBgColor: item.sysBgColor || '#ffffff', direction: item.direction || 'vertical', adapter: item.adapter || '', share: item.share || '', share_des: item.share_des || '', share_url: item.share_url || '', share_link: item.share_link || ''})))}'`)
    param.LText = param.LText.join(' union all ')
    param.LText = Utils.formatOptions(param.LText)
    
@@ -898,7 +899,7 @@
          return item
        })
        param.LText = selectApp.sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', direction: item.direction || 'vertical', adapter: item.adapter || '', share: item.share || '', share_des: item.share_des || '', share_url: item.share_url || '', share_link: item.share_link || ''})))}'`)
        param.LText = selectApp.sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', topHeight: item.topHeight || '', sysBgColor: item.sysBgColor || '#ffffff', direction: item.direction || 'vertical', adapter: item.adapter || '', share: item.share || '', share_des: item.share_des || '', share_url: item.share_url || '', share_link: item.share_link || ''})))}'`)
        param.LText = param.LText.join(' union all ')
        param.LText = Utils.formatOptions(param.LText)
      }
@@ -991,7 +992,7 @@
        return item
      })
      param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', direction: item.direction || 'vertical', adapter: item.adapter || '', share: item.share || '', share_des: item.share_des || '', share_url: item.share_url || '', share_link: item.share_link || ''})))}'`)
      param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', topHeight: item.topHeight || '', sysBgColor: item.sysBgColor || '#ffffff', direction: item.direction || 'vertical', adapter: item.adapter || '', share: item.share || '', share_des: item.share_des || '', share_url: item.share_url || '', share_link: item.share_link || ''})))}'`)
      param.LText = param.LText.join(' union all ')
      param.LText = Utils.formatOptions(param.LText)
src/views/appmanage/submutilform/index.jsx
@@ -309,6 +309,18 @@
              )}
            </Form.Item>
          </Col> : null}
          {typename === 'pad' && adapters.includes('app') ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="在明科云APP中,状态栏的最大高度,空值时使用APP获取到的状态栏高度。">
                <QuestionCircleOutlined className="mk-form-tip" />
                状态栏高度
              </Tooltip>
            }>
              {getFieldDecorator('topHeight', {
                initialValue: card ? card.topHeight || '' : ''
              })(<InputNumber min={0} max={5000} precision={0} onPressEnter={this.handleSubmit}/>)}
            </Form.Item>
          </Col> : null}
          <Col span={12}>
            <Form.Item className="sys-bgcolor" label={
              <Tooltip placement="topLeft" title="子应用通用的背景色,子应用页面创建时会默认添加此背景色。">
src/views/billprint/index.jsx
@@ -886,7 +886,7 @@
      <div className="bill-print-wrap" >
        {loadingview && <Spin size="large" />}
        {pages ? <div id="bill-print">
          {pages.map((components, index) => (<div className={'print-page' + (auto ? ' auto' : '')} key={index} style={{...config.style, overflow: 'hidden', boxSizing: 'border-box'}}><Row>{this.getComponents(components)}</Row></div>))}
          {pages.map((components, index) => (<div className={'print-page' + (auto ? ' auto' : '')} key={index} style={{...config.style, overflow: 'hidden', boxSizing: 'border-box'}}><Row className="component-wrap">{this.getComponents(components)}</Row></div>))}
        </div> : null}
        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
        {config && window.GLOB.breakpoint ? <DebugTable /> : null}
src/views/design/index.jsx
@@ -23,7 +23,7 @@
  
  render () {
    return (
      <div className="mk-main-view">
      <div className={'mk-main-view ' + (window.GLOB.systemType || '')}>
        <ConfigProvider locale={_locale}>
          <Sidemenu key="sidemenu"/>
          <Header key="header"/>
src/views/design/index.scss
@@ -3,6 +3,11 @@
    background: #ffffff!important;
  }
}
.mk-main-view.production {
  .edit-check {
    display: none;
  }
}
.mk-popover-control-wrap {
  .anticon-plus {
    color: #26C281;
src/views/design/sidemenu/index.jsx
@@ -109,6 +109,8 @@
  }
  editmenu = (cell) => {
    if (window.GLOB.systemType === 'production') return
    if (cell.type === 'CustomPage') {
      let _param = {
        MenuType: 'custom',
src/views/design/sidemenu/thdmenuplus/index.jsx
@@ -123,6 +123,7 @@
    let sysMenu = {
      MenuID: Utils.getuuid(),
      MenuName: template.title,
      MenuNo: template.MenuNo ? template.MenuNo + '_01' : '',
      Template: template.type,
      fstMenuId: mainMenu.MenuID,
      ParentId: supMenu.MenuID,
src/views/login/index.jsx
@@ -5,6 +5,7 @@
import Api from '@/api'
import Utils from '@/utils/utils.js' 
import MKEmitter from '@/utils/events.js'
import options, { styles } from '@/store/options.js'
import zhCN from '@/locales/zh-CN/login.js'
import enUS from '@/locales/en-US/login.js'
@@ -13,7 +14,7 @@
import './index.scss'
const LoginForm = asyncLoadComponent(() => import('./loginform'))
const Resetpwd = asyncLoadComponent(() => import('@/components/header/resetpwd'))
const Resetpwd = asyncLoadComponent(() => import('@/components/resetPassword'))
const LoginCloudForm = asyncComponent(() => import('./logincloudform'))
const iszhCN = sessionStorage.getItem('lang') !== 'en-US'
@@ -38,8 +39,6 @@
    loginWays: null,
    touristLogin: false,
    syncing: false,
    visible: false,
    resetLoading: false
  }
  changelang (item) {
@@ -139,8 +138,14 @@
        if (visible) {
          message.warning(tip)
          this.setState({
            isDisabled: false,
            visible: true
            isDisabled: false
          })
          MKEmitter.emit('resetpassword', () => {
            const input = document.getElementById('password')
            if (input) {
              input.select()
            }
          })
          return
        }
@@ -647,7 +652,11 @@
                login_ways.push({
                  type: item.way_no,
                  smsId: item.sms_id
                })
                })
                if (item.way_no === 'sms_vcode' && item.sms_id) {
                  sessionStorage.setItem('mk_sms_id', item.sms_id)
                }
              })
            } else {
              login_ways.push({
@@ -828,48 +837,6 @@
    })
  }
  resetPwdSubmit = () => {
    this.formRef.handleConfirm().then(res => {
      this.setState({
        resetLoading: true
      })
      let _param = {
        func: 's_PwdUpt',
        LText: `select '${res.originpwd}','${res.password}'`
      }
      _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 时间戳
      _param.LText = Utils.formatOptions(_param.LText)                   // 关键字符替换,base64加密
      _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5密钥
      Api.getSystemConfig(_param).then(result => {
        this.setState({
          visible: !result.status,
          resetLoading: false
        })
        if (result.status) {
          notification.success({
            top: 92,
            message: '修改成功,请重新登录。',
            duration: 2
          })
          const input = document.getElementById('password')
          if (input) {
            input.select()
          }
        } else {
          notification.warning({
            top: 92,
            message: result.message,
            duration: 5
          })
        }
      })
    }, () => {})
  }
  /**
   * @description 组件销毁,清除state更新
   */
@@ -929,18 +896,7 @@
          <LoginCloudForm handleSubmit={() => this.syncSubmit()} wrappedComponentRef={(inst) => this.logincloudRef = inst}/>
        </Modal>
        {/* 修改密码 */}
        <Modal
          title="修改密码"
          okText={this.state.dict['login.ok']}
          cancelText={this.state.dict['login.cancel']}
          visible={this.state.visible}
          onOk={this.resetPwdSubmit}
          confirmLoading={this.state.resetLoading}
          onCancel={() => this.setState({visible: false, resetLoading: false})}
          destroyOnClose
        >
          <Resetpwd wrappedComponentRef={(inst) => this.formRef = inst} resetPwdSubmit={this.resetPwdSubmit}/>
        </Modal>
        <Resetpwd />
      </div>
    )
  }
src/views/login/loginform.jsx
@@ -452,7 +452,7 @@
              <Input
                addonAfter={
                  <Button type="link" className="vercode" size="small" disabled={verdisabled} onClick={this.getvercode}>
                    {delay ? this.props.dict['login.vercode.queryagain'].replace('@', delay) : this.props.dict['login.vercode.query']}
                    {delay ? `${delay}s后重新获取` : '获取验证码'}
                  </Button>
                }
                placeholder={this.props.dict['login.vercode']}
src/views/menudesign/index.jsx
@@ -37,6 +37,7 @@
const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
const NormalCss = asyncComponent(() => import('@/menu/normalCss'))
const Versions = asyncComponent(() => import('@/menu/versions'))
const TableNodes = asyncComponent(() => import('@/menu/tablenodes'))
const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
@@ -1151,6 +1152,7 @@
                    {/* 表名添加 */}
                    {config ? <TableComponent config={config} updatetable={this.updateConfig}/> : null}
                    {config ? <Paragraph style={{padding: '15px 0px 0px 18px'}} copyable={{ text: MenuId }}>菜单ID</Paragraph> : null}
                    {config ? <NormalCss config={config} updateConfig={this.updateConfig}/> : null}
                  </Panel>
                  {/* 组件添加 */}
                  <Panel header="组件" key="component">
src/views/menudesign/index.scss
@@ -46,6 +46,7 @@
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    overflow: hidden;
    .center {
      position: absolute;
      font-size: 16px;
@@ -56,6 +57,7 @@
      max-width: 70%;
      .title {
        text-align: center;
        white-space: nowrap;
      }
    }
    .error {
src/views/menudesign/popview/index.scss
@@ -21,6 +21,7 @@
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    overflow: hidden;
    .center {
      position: absolute;
      font-size: 16px;
@@ -31,6 +32,7 @@
      max-width: 70%;
      .title {
        text-align: center;
        white-space: nowrap;
      }
    }
    .error {
src/views/mobdesign/index.jsx
@@ -34,6 +34,7 @@
const TableNodes = asyncComponent(() => import('@/menu/tablenodes'))
const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
const NormalCss = asyncComponent(() => import('@/menu/normalCss'))
const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
const Quotecomponent = asyncComponent(() => import('@/pc/quotecomponent'))
const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
@@ -2026,6 +2027,7 @@
                    {config ? <TableComponent config={config} updatetable={this.updateConfig}/> : null}
                    {config ? <Paragraph style={{padding: '15px 0px 0px 18px'}} copyable={{ text: MenuId }}>菜单ID</Paragraph> : null}
                    {config ? <Paragraph style={{padding: '10px 0px 0px 18px'}} copyable={{ text:  `${window.GLOB.baseurl}mob/index.html#/index/${sessionStorage.getItem('kei_no')}/${sessionStorage.getItem('typename')}/${sessionStorage.getItem('lang')}/${MenuId}/@BID@` }}>菜单链接</Paragraph> : null}
                    {config ? <NormalCss config={config} updateConfig={this.updateConfig}/> : null}
                  </Panel>
                  {/* 组件添加 */}
                  <Panel header="组件" className="component" key="component">
src/views/mobdesign/index.scss
@@ -30,6 +30,7 @@
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    overflow: hidden;
    .center {
      position: absolute;
      font-size: 16px;
@@ -40,6 +41,7 @@
      max-width: 70%;
      .title {
        text-align: center;
        white-space: nowrap;
      }
    }
    .error {
src/views/pcdesign/index.jsx
@@ -39,6 +39,7 @@
const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
const NormalCss = asyncComponent(() => import('@/menu/normalCss'))
const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
@@ -1707,6 +1708,7 @@
                    {/* 表名添加 */}
                    {config ? <TableComponent config={config} updatetable={this.updateConfig}/> : null}
                    {config ? <Paragraph style={{padding: '15px 0px 0px 18px'}} copyable={{ text: MenuId }}>菜单ID</Paragraph> : null}
                    {config ? <NormalCss config={config} updateConfig={this.updateConfig}/> : null}
                  </Panel>
                  {/* 组件添加 */}
                  <Panel header="组件" key="component">
src/views/pcdesign/index.scss
@@ -34,6 +34,7 @@
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    overflow: hidden;
    .center {
      position: absolute;
      font-size: 16px;
@@ -44,6 +45,7 @@
      max-width: 70%;
      .title {
        text-align: center;
        white-space: nowrap;
      }
    }
    .error {
src/views/systemfunc/sidemenu/config.jsx
@@ -5,8 +5,8 @@
  PageParam: {Icon: 'folder'},
  children: [{
    src: '',
    PageParam: {OpenType: 'newtab', Template: 'CommonTable'},
    type: 'CommonTable',
    PageParam: {OpenType: 'newtab', Template: 'CustomPage'},
    type: 'CustomPage',
    MenuID: '1581067625930haged11ieaivpavv77k',
    MenuNo: 'sDatasM',
    MenuName: '数据字典',
@@ -77,13 +77,13 @@
    MenuNo: 's_custom_scriptM',
    MenuName: '自定义函数',
  }, {
    src: '',
    PageParam: {OpenType: 'newtab', Template: 'CommonTable'},
    type: 'CommonTable',
    MenuID: '1590458676585agbbr63t6ihighg2i1g',
    MenuNo: 'LdropdownmenuNewM',
    MenuName: '通用下拉菜单',
  }, {
  //   src: '',
  //   PageParam: {OpenType: 'newtab', Template: 'CommonTable'},
  //   type: 'CommonTable',
  //   MenuID: '1590458676585agbbr63t6ihighg2i1g',
  //   MenuNo: 'LdropdownmenuNewM',
  //   MenuName: '通用下拉菜单',
  // }, {
    src: '',
    PageParam: {OpenType: 'newtab', Template: 'CommonTable'},
    type: 'CommonTable',
src/views/systemfunc/sidemenu/index.jsx
@@ -1,5 +1,6 @@
import React, {Component} from 'react'
import { Menu } from 'antd'
import { fromJS } from 'immutable'
import { FolderOutlined } from '@ant-design/icons'
import { SySMenuList } from './config'
@@ -39,10 +40,19 @@
      menulist = menulist.filter(menu => menu.children.length > 0)
    }
    let tb =  sessionStorage.getItem('mk-table-node')
    this.setState({
      subMenulist: menulist,
      rootSubmenuKeys: menulist.map(item => item.MenuID),
      openKeys: [menulist[0].MenuID]
    }, () => {
      if (tb && menulist[0] && menulist[0].children[0] && menulist[0].children[0].MenuNo === 'sDatasM') {
        setTimeout(() => {
          MKEmitter.emit('modifyTabs', fromJS(menulist[0].children[0]).toJS())
        }, 500)
      }
      sessionStorage.removeItem('mk-table-node')
    })
  }
src/views/tabledesign/popview/index.scss
@@ -21,6 +21,7 @@
    bottom: 0;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #1890ff;
    overflow: hidden;
    .center {
      position: absolute;
      font-size: 16px;
@@ -31,6 +32,7 @@
      max-width: 70%;
      .title {
        text-align: center;
        white-space: nowrap;
      }
    }
    .error {