king
2020-09-03 af02b8f3c3ec9e5684be1084904d673429421d2b
src/tabviews/custom/index.jsx
@@ -2,7 +2,7 @@
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { notification, Spin, Modal, Button, message, Tree, Typography } from 'antd'
import { notification, Spin, Row, Col, Button, message } from 'antd'
import moment from 'moment'
import Api from '@/api'
@@ -13,16 +13,11 @@
import asyncSpinComponent from '@/utils/asyncSpinComponent'
import { refreshTabView } from '@/store/action'
import MainSearch from '@/tabviews/zshare/topSearch'
import NotFount from '@/components/404'
import './index.scss'
// 通用组件
const CalendarComponent = asyncSpinComponent(() => import('@/tabviews/zshare/calendar'))
const SubTabTable = asyncSpinComponent(() => import('@/tabviews/subtabtable'))
const { TreeNode } = Tree
const { Paragraph } = Typography
const AntvBarAndLine = asyncSpinComponent(() => import('./components/chart/antv-bar-line'))
class NormalTable extends Component {
  static propTpyes = {
@@ -39,18 +34,15 @@
    loadingview: true,    // 页面加载中
    viewlost: false,      // 页面丢失:1、未获取到配置-页面丢失;2、页面未启用
    lostmsg: '',          // 页面丢失时的提示信息
    config: {},           // 页面配置信息,包括按钮、搜索、显示列、标签等
    config: null,         // 页面配置信息,包括组件等
    mainSearch: null,
    userConfig: null,     // 用户自定义设置
    searchlist: null,     // 搜索条件
    arr_field: '',        // 使用 sPC_Get_TableData 时的查询字段集
    setting: null,        // 页面全局设置:数据源、按钮及显示列固定、主键等
    data: null,           // 列表数据集
    loading: false,       // 列表数据加载中
    search: '',           // 搜索条件数组,使用时需分场景处理
    visible: false,       // 标签页控制
    triggerTime: '',      // 点击时间
    treevisible: false,   // 菜单结构树弹框显示隐藏控制
    calendarYear: moment().format('YYYY') // 日历年份
  }
  /**
@@ -68,7 +60,6 @@
    if (result.status) {
      let config = ''
      let userConfig = null
      let _curUserConfig = ''
      try { // 配置信息解析
        config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
@@ -81,7 +72,6 @@
      if (result.LongParamUser && this.props.menuType !== 'HS') {
        try { // 配置信息解析
          userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
          _curUserConfig = userConfig[this.props.MenuID]
        } catch (e) {
          console.warn('Parse Failure')
          userConfig = null
@@ -96,7 +86,7 @@
        })
        return
      }
      // 页面未启用时,显示未启用页面
      if (!config.enabled) {
        this.setState({
@@ -107,62 +97,103 @@
        return
      }
      // 获取主搜索条件
      let mainSearch = []
      config.components.forEach(component => {
        if (component.type === 'search') {
          component.search = component.search.map(item => {
            item.oriInitval = item.initval
            if (['text', 'select', 'link'].includes(item.type) && param) {
              if (param.searchkey === item.field) {
                item.initval = param.searchval
              } else if (param.BID && item.field.toLowerCase() === 'bid') {
                item.initval = param.BID
              } else if (param.data && param.data[item.field]) {
                item.initval = param.data[item.field]
              }
            }
            if (!item.blacklist || item.blacklist.length === 0) return item
            let _black = item.blacklist.filter(v => {
              return this.props.permRoles.indexOf(v) !== -1
            })
            if (_black.length > 0) {
              item.Hide = 'true'
            }
            return item
          })
          mainSearch = Utils.initMainSearch(component.search)
        }
      })
      // 权限过滤
      if (this.props.menuType !== 'HS') {
        if (config.tab && !permAction[config.tab.linkTab]) {
          config.tab = null
        }
        config.components.forEach(component => {
          if (component.action) {
            component.action = component.action.filter(item => permAction[item.uuid])
          }
        })
      }
      if (_curUserConfig) {
        config.setting = {...config.setting, ..._curUserConfig.setting}
        config.easyCode = _curUserConfig.easyCode || config.easyCode || ''
      }
      let params = []
      config.components.forEach(component => {
        let _customScript = ''
        component.scripts && component.scripts.forEach(script => {
          if (script.status !== 'false') {
            _customScript += `
            ${script.sql}
            `
          }
        })
      // 字段权限黑名单、必填、字段透视
      let valid = true
      config.search = config.search.map(item => {
        item.oriInitval = item.initval
        if (['text', 'select', 'link'].includes(item.type) && param) {
          if (param.searchkey === item.field) {
            item.initval = param.searchval
          } else if (param.BID && item.field.toLowerCase() === 'bid') {
            item.initval = param.BID
          } else if (param.data && param.data[item.field]) {
            item.initval = param.data[item.field]
        if (component.setting && component.setting.interType === 'system') { // 使用系统函数
          component.setting.execute = component.setting.execute !== 'false'  // 默认sql是否执行,转为boolean 统一格式
          component.setting.laypage = component.setting.laypage === 'true'   // 是否分页,转为boolean 统一格式
          if (!component.setting.execute) {
            component.setting.dataresource = ''
          }
          if (/\s/.test(component.setting.dataresource)) {
            component.setting.dataresource = '(' + component.setting.dataresource + ') tb'
          }
          if (this.props.dataManager) { // 数据权限
            component.setting.dataresource = component.setting.dataresource.replace(/\$@/ig, '/*')
            component.setting.dataresource = component.setting.dataresource.replace(/@\$/ig, '*/')
            _customScript = _customScript.replace(/\$@/ig, '/*')
            _customScript = _customScript.replace(/@\$/ig, '*/')
          } else {
            component.setting.dataresource = component.setting.dataresource.replace(/@\$|\$@/ig, '')
            _customScript = _customScript.replace(/@\$|\$@/ig, '')
          }
        }
        if (item.required === 'true' && !item.initval) {
          valid = false
        component.customScript = _customScript // 整理后自定义脚本
        // format   数据格式 array 或 object
        // dataName 系统生成的数据源名称
        // laypage  是否分页,组件属性,不分页的组件才可以统一查询
        if (component.format && component.dataName && !component.laypage && component.setting.interType === 'system' && component.setting.onload === 'true' && component.setting.sync === 'true') {
          let param = this.getDefaultParam(component, mainSearch)
          params.push(param)
        } else {
          component.setting.sync = 'false'
        }
        if (!item.blacklist || item.blacklist.length === 0) return item
        let _black = item.blacklist.filter(v => {
          return this.props.permRoles.indexOf(v) !== -1
        })
        if (_black.length > 0) {
          item.Hide = 'true'
        }
        return item
      })
      this.setState({
        BID: param && param.BID ? param.BID : '',
        loadingview: false,
        config: config,
        userConfig: userConfig,
        setting: config.setting,
        searchlist: config.search,
        arr_field: config.columns.map(item => item.field).join(','),
        search: Utils.initMainSearch(config.search) // 搜索条件初始化(含有时间格式,需要转化)
        config,
        mainSearch
      }, () => {
        if (config.setting.onload !== 'false' && valid) { // 初始化可加载
          this.loadmaindata()
        }
        this.loadmaindata(params)
      })
    } else {
      this.setState({
@@ -178,212 +209,139 @@
  }
  /**
   * @description 主表数据加载
   */
  async loadmaindata () {
    const { setting, search, BID } = this.state
    let param = ''
    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
    if (requireFields.length > 0) {
      let labels = requireFields.map(item => item.label)
      labels = Array.from(new Set(labels))
      notification.warning({
        top: 92,
        message: this.state.dict['form.required.input'] + labels.join('、') + ' !',
        duration: 3
      })
      return
    }
    this.setState({
      loading: true
    })
    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
      param = this.getCustomParam()
    } else {
      param = this.getDefaultParam()
    }
    if (BID) {
      param.BID = BID
    }
    // 数据管理权限
    if (this.props.dataManager) {
      param.dataM = 'Y'
    }
    let result = await Api.genericInterface(param)
    if (result.status) {
      this.setState({
        data: result.data.map((item, index) => {
          item.key = index
          return item
        }),
        loading: false
      })
    } else {
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
  /**
   * @description 获取用户自定义存储过程传参
   */
  getCustomParam = () => {
    const { search, setting, calendarYear, config } = this.state
    let _search = Utils.formatCustomMainSearch(search)
    let param = {
      ..._search
    }
    if (config.calendar.refresh === 'true') {
      param.calendarDate = calendarYear
    }
    if (setting.interType === 'inner') {
      param.func = setting.innerFunc
    } else {
      if (this.props.menuType === 'HS') {
        if (setting.sysInterface === 'true' && options.cloudServiceApi) {
          param.rduri = options.cloudServiceApi
        } else if (setting.sysInterface !== 'true') {
          param.rduri = setting.interface
        }
      } else {
        if (setting.sysInterface === 'true' && window.GLOB.mainSystemApi) {
          param.rduri = window.GLOB.mainSystemApi
        } else if (setting.sysInterface !== 'true') {
          param.rduri = setting.interface
        }
      }
      if (setting.outerFunc) {
        param.func = setting.outerFunc
      }
    }
    return param
  }
  /**
   * @description 获取系统存储过程 sPC_Get_TableData 的参数
   */
  getDefaultParam = () => {
    const { arr_field, search, setting, config, calendarYear } = this.state
    let _search = Utils.joinMainSearchkey(search)
    _search = _search ? 'where ' + _search : ''
  getDefaultParam = (component, mainSearch) => {
    const { columns, search, setting, dataName, format, customScript } = component
    
    let param = {
      func: 'sPC_Get_TableData',
      obj_name: 'data',
      arr_field: arr_field,
      custom_script: setting.customScript || '',
      default_sql: setting.default || 'true'
    let searchlist = []
    if (search && search.length > 0) {
      searchlist = Utils.initMainSearch(search)
    }
    if (setting.useMSearch === 'true') {
      searchlist = [...mainSearch, ...searchlist]
    }
    let arr_field = columns.map(col => col.field)
    let _dataresource = setting.dataresource
    let _customScript = customScript
    if (/\s/.test(_dataresource)) {
      _dataresource = '(' + _dataresource + ') tb'
    }
    if (this.props.dataManager) { // 数据权限
      _dataresource = _dataresource.replace(/\$@/ig, '/*')
      _dataresource = _dataresource.replace(/@\$/ig, '*/')
      param.custom_script = param.custom_script.replace(/\$@/ig, '/*')
      param.custom_script = param.custom_script.replace(/@\$/ig, '*/')
    } else {
      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
      param.custom_script = param.custom_script.replace(/@\$|\$@/ig, '')
    }
    let regoptions = null
    if (setting.queryType === 'statistics' || param.custom_script) {
      let allSearch = Utils.getAllSearchOptions(search)
      regoptions = allSearch.map(item => {
    if (setting.queryType === 'statistics' || _customScript) {
      let allSearch = Utils.getAllSearchOptions(searchlist)
      let regoptions = allSearch.map(item => {
        return {
          reg: new RegExp('@' + item.key + '@', 'ig'),
          value: `'${item.value}'`
        }
      })
    }
    if (config.calendar.refresh === 'true' && regoptions) {
      regoptions.push({
        reg: new RegExp('@calendarDate@', 'ig'),
        value: `${calendarYear}-01-01 00:00:00.000`
      })
      regoptions.push({
        reg: new RegExp('@calendarDate1@', 'ig'),
        value: `${calendarYear}-12-31 23:59:59.999`
      })
    }
    if (setting.queryType === 'statistics' && setting.default !== 'false') { // 统计数据源,内容替换
      regoptions.forEach(item => {
        _dataresource = _dataresource.replace(item.reg, item.value)
        if (_dataresource && setting.queryType === 'statistics') {
          _dataresource = _dataresource.replace(item.reg, item.value)
        }
        if (_customScript) {
          _customScript = _customScript.replace(item.reg, item.value)
        }
      })
      _search = ''
    }
    let LText = ''
    if (setting.default !== 'false') {
      LText = `select ${arr_field} from ${_dataresource} ${_search}`
    let _search = ''
    if (setting.queryType !== 'statistics' && _dataresource) {
      _search = Utils.joinMainSearchkey(searchlist)
      _search = _search ? 'where ' + _search : ''
    }
    if (param.custom_script) {
      regoptions.forEach(item => {
        param.custom_script = param.custom_script.replace(item.reg, item.value)
      })
      if (LText) {
        LText += `
          aaa:
          if @ErrorCode!=''
            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
        `
      } else {
        param.custom_script += `
          aaa:
          if @ErrorCode!=''
            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
        `
      }
    if (setting.order && _dataresource) {
      _dataresource = `select top 1000 ${arr_field.join(',')} from (select ${arr_field.join(',')} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable order by tmptable.rows `
    } else if (_dataresource) {
      _dataresource = `select top 1000 ${arr_field.join(',')} from ${_dataresource} ${_search} `
    }
    // 测试系统打印查询语句
    if ((options.sysType === 'local' && !window.GLOB.systemType) || window.debugger === true) {
      param.custom_script &&  console.log(`${LText ? '' : '/*不执行默认sql*/\n'}${param.custom_script}`)
      LText &&  console.log(LText)
      _customScript &&  console.log(`${_dataresource ? '' : '/*不执行默认sql*/\n'}${_customScript}`)
      _dataresource &&  console.log(_dataresource)
    }
    return {
      name: dataName,
      columns: columns,
      par_tablename: '',
      type: format === 'array' ? format : '',
      primaryKey: setting.primaryKey || '',
      foreign_key: '',
      sql: _dataresource,
      script: _customScript
    }
  }
  /**
   * @description 主表数据加载
   */
  loadmaindata = (params) => {
    let LText_field = []
    let LText = params.map((item, index) => {
      let _sql = item.sql
      let _script = item.script
      // if (index === 0) {
      //   _script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
      //     ${_script}
      //   `
      // }
      // if (params.length === index + 1) {
      //   _sql = `${_sql}
      //     aaa:
      //       if @ErrorCode!=''
      //       begin
      //         insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
      //         goto bbb
      //       end
      //   `
      // }
      item.columns.forEach(cell => {
        LText_field.push(`Select '${item.name}' as tablename,'${cell.field}' as fieldname,'${cell.datatype}' as field_type`)
      })
      return `Select '${item.name}' as tablename,'${window.btoa(window.encodeURIComponent(_sql))}' as LText,'${window.btoa(window.encodeURIComponent(_script))}' as Lcustomize,'${item.type}' as table_type,'${item.primaryKey}' as primary_key,'${item.par_tablename}' as par_tablename,'${item.foreign_key}' as foreign_key,'${index}' as Sort`
      // return `Select '${item.name}' as tablename,'${_sql}' as LText,'${_script}' as Lcustomize,'${item.type}' as table_type,'${item.primaryKey}' as primary_key,'${item.par_tablename}' as par_tablename,'${item.foreign_key}' as foreign_key,'${index}' as Sort`
    })
    let param = {
      func: 'sPC_Get_structured_data',
      LText: LText.join(' union all '),
      LText_field: LText_field.join(' union all ')
    }
    
    param.custom_script = Utils.formatOptions(param.custom_script)
    param.LText = Utils.formatOptions(LText)
    param.LText = Utils.formatOptions(param.LText)
    param.LText_field = Utils.formatOptions(param.LText_field)
    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
    param.DateCount = ''
    if (this.props.menuType === 'HS') { // 云端数据验证
      param.open_key = Utils.encrypt(param.secretkey, param.timestamp, true)
    }
    Api.getLocalConfig(param).then(result => {
      if (result.status) {
        delete result.status
        delete result.message
        delete result.ErrMesg
        delete result.ErrCode
    return param
        this.setState({
          data: result,
          loading: false
        })
      } else {
        this.setState({
          data: '',
          loading: false
        })
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
    })
  }
  /**
@@ -391,22 +349,9 @@
   * 含有初始不加载的页面,修改设置
   */
  refreshbysearch = (searches) => {
    const { setting } = this.state
    if (setting.onload === 'false') {
      this.setState({
        search: searches,
        setting: {...setting, onload: 'true'}
      }, () => {
        this.loadmaindata()
      })
    } else {
      this.setState({
        search: searches
      }, () => {
        this.loadmaindata()
      })
    }
    this.setState({
      mainSearch: searches
    })
  }
@@ -441,44 +386,6 @@
    }
  }
  getTreeNode = (data) => {
    let _type = {
      view: '页面',
      btn: '按钮',
      tab: '标签'
    }
    return data.map(item => {
      let _title = _type[item.subtype]
      let _others = []
      _others.push(
        (item.menuNo ? item.menuNo + '(菜单参数)' : ''),
        (item.tableName ? item.tableName + '(表名) ' : ''),
        (item.innerFunc ? item.innerFunc + '(内部函数) ' : ''),
        (item.outerFunc ? item.outerFunc + '(外部函数)' : '')
      )
      _others = _others.filter(Boolean)
      _others = _others.join('、')
      if (item.label) {
        _title = _title + '(' + item.label + ')'
      }
      if (_others) {
        _title = _title + ': ' + _others
      }
      if (item.subfuncs && item.subfuncs.length > 0) {
        return (
          <TreeNode title={_title} key={item.uuid} dataRef={item} selectable={false}>
            {this.getTreeNode(item.subfuncs)}
          </TreeNode>
        )
      }
      return <TreeNode key={item.uuid} title={_title} isLeaf selectable={false} />
    })
  }
  UNSAFE_componentWillMount () {
    // 组件加载时,获取菜单数据
    this.loadconfig()
@@ -486,20 +393,10 @@
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.refreshTab && nextProps.refreshTab.MenuID === this.props.MenuID) {
      if (nextProps.refreshTab.position === 'grid') {
        this.loadmaindata()
      } else if (nextProps.refreshTab.position === 'view') {
      if (nextProps.refreshTab.position === 'view') {
        this.reloadview()
      }
      this.props.refreshTabView('')
    } else if (nextProps.param && !is(fromJS(this.props.param), fromJS(nextProps.param))) {
      let search = this.state.search.map(item => {
        if (item.type === 'text' && item.key === nextProps.param.searchkey) {
          item.value = nextProps.param.searchval
        }
        return item
      })
      this.refreshbysearch(search)
    }
  }
@@ -516,94 +413,39 @@
    }
  }
  changeDate = (value) => {
    this.setState({calendarYear: value}, () => {
      this.loadmaindata()
  getComponents = () => {
    const { menuType, dataManager } = this.props
    const { config, BID, data, mainSearch } = this.state
    if (!config || !config.components) return
    return config.components.map(item => {
      if (item.type === 'bar' || item.type === 'line') {
        return (
          <Col key={item.uuid}>
            <AntvBarAndLine config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} dataManager={dataManager} />
          </Col>
        )
      } else {
        return null
      }
    })
  }
  triggerDate = (item) => {
    const { config } = this.state
    if (!config.tab) return
    this.setState({
      visible: true,
      triggerTime: item.time.substr(0, 4) + '-' + item.time.substr(4, 2) + '-' + item.time.substr(6, 2)
    })
  }
  closeTab = () => {
    this.setState({
      visible: false,
      triggerTime: ''
    })
  }
  render() {
    const { BID, searchlist, loadingview, viewlost, config, loading, data, triggerTime } = this.state
    const { loadingview, viewlost } = this.state
    return (
      <div className="calendar-page" id={this.state.ContainerId}>
        {loadingview && <Spin size="large" />}
        {searchlist && searchlist.length > 0 ?
          <MainSearch
            BID={BID}
            dict={this.state.dict}
            searchlist={searchlist}
            menuType={this.props.menuType}
            dataManager={this.props.dataManager}
            refreshdata={this.refreshbysearch}
          /> : null
        }
        {config && config.calendar ? <CalendarComponent calendar={config.calendar} loading={loading} data={data} triggerDate={this.triggerDate} changeDate={this.changeDate}/> : null}
        <Row>{this.getComponents()}</Row>
        {options.sysType !== 'cloud' ? <Button
          icon="copy"
          shape="circle"
          className="common-table-copy"
          onClick={this.handleviewconfig}
        /> : null}
        <Modal
          className="menu-tree-modal"
          title={'菜单结构树'}
          width={'650px'}
          maskClosable={false}
          visible={this.state.treevisible}
          onCancel={() => this.setState({treevisible: false})}
          footer={[
            <Button key="close" onClick={() => this.setState({treevisible: false})}>{this.state.dict['main.close']}</Button>
          ]}
          destroyOnClose
        >
          <div className="menu-header">
            <span>菜单名称:{this.props.MenuName}</span>
            <span>菜单参数:{<Paragraph copyable>{this.props.MenuNo}</Paragraph>}</span>
          </div>
          {this.state.treevisible ? <Tree defaultExpandAll showLine={true}>
            {this.getTreeNode(config.funcs)}
          </Tree> : null}
        </Modal>
        <Modal
          title={config.tab ? config.tab.label : ''}
          width={'80vw'}
          maskClosable={false}
          visible={this.state.visible}
          onCancel={this.closeTab}
          footer={[
            <Button key="close" onClick={this.closeTab}>{this.state.dict['main.close']}</Button>
          ]}
          destroyOnClose
        >
          {config.tab ? <SubTabTable
            type="calendar"
            BID={triggerTime}
            Tab={config.tab}
            SupMenuID={this.props.MenuID}
            MenuID={config.tab.linkTab}
            refreshSupView={() => this.loadmaindata()}
            closeModalView={this.closeTab}
          /> : null}
        </Modal>
        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
      </div>
    )