king
2021-03-11 c8e680b315ce010905f6b0409d3156218abfe056
2021-03-11
1 文件已重命名
27个文件已修改
1个文件已添加
787 ■■■■■ 已修改文件
src/assets/mobimg/carousel.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/carousel1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/index.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulesource/option.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/option.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/normalbutton/index.jsx 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/printbutton/index.jsx 106 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editthdmenu/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-custom.js 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 257 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/menuform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/carousel.png

src/assets/mobimg/carousel1.png
src/index.js
@@ -67,7 +67,7 @@
  })
  .then(config => {
    if (!config) return
    let GLOB = {}
    GLOB.appId = config.appId || ''
    GLOB.lineColor = config.lineColor || ''
@@ -77,7 +77,6 @@
      GLOB.externalDatabase = config.externalDatabase ? `[${config.externalDatabase}]..` : ''
    } else {
      GLOB.externalDatabase = null
    }
    // 只有业务系统才可以设置为正式系统
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -104,9 +104,9 @@
        if (link === 'dynamic' || link === 'static' || link === 'custom') {
          _options.push('linkurl', 'joint')
        } else if (link === 'page') {
          _options.push('copyMenuId', 'joint')
          _options.push('copyMenuId', 'joint', 'open')
        } else if (link === 'linkpage') {
          _options.push('linkmenu', 'joint')
          _options.push('linkmenu', 'joint', 'open')
        }
      }
    } else if (eleType === 'icon') {
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -150,60 +150,6 @@
      ]
    },
    {
      type: 'radio',
      key: 'link',
      label: '链接',
      initVal: card.link || '',
      tooltip: '动态地址为绑定字段值。',
      required: false,
      forbid: isApp,
      options: [
        { value: '', text: '无' },
        { value: 'dynamic', text: '动态' },
        { value: 'static', text: '静态' }
      ]
    },
    {
      type: 'select',
      key: 'link',
      label: '链接',
      initVal: card.link || '',
      required: false,
      forbid: !isApp,
      options: [
        { value: '', text: '无' },
        { value: 'page', text: '菜单' },
        { value: 'linkpage', text: '关联菜单' },
        { value: 'custom', text: '链接' }
      ]
    },
    {
      type: 'select',
      key: 'linkmenu',
      label: '关联菜单',
      initVal: card.linkmenu || '',
      required: true,
      forbid: !isApp,
      options: appMenus
    },
    {
      type: 'select',
      key: 'copyMenuId',
      label: '复制菜单',
      initVal: card.copyMenuId || '',
      required: false,
      forbid: !isApp,
      options: appMenus
    },
    {
      type: 'select',
      key: 'linkurl',
      label: '链接地址',
      initVal: card.linkurl || '',
      required: true,
      options: []
    },
    {
      type: 'select',
      key: 'format',
      label: '格式化',
@@ -364,6 +310,64 @@
    },
    {
      type: 'radio',
      key: 'link',
      label: '链接',
      initVal: card.link || '',
      tooltip: '动态地址为绑定字段值。',
      required: false,
      forbid: isApp,
      options: [
        { value: '', text: '无' },
        { value: 'dynamic', text: '动态' },
        { value: 'static', text: '静态' }
      ]
    },
    {
      type: 'select',
      key: 'link',
      label: '链接',
      initVal: card.link || '',
      required: false,
      forbid: !isApp,
      options: [
        { value: '', text: '无' },
        { value: 'page', text: '菜单' },
        { value: 'linkpage', text: '关联菜单' },
        { value: 'custom', text: '链接' }
      ]
    },
    {
      type: 'select',
      key: 'linkmenu',
      label: '关联菜单',
      initVal: card.linkmenu || '',
      required: true,
      forbid: !isApp,
      options: appMenus
    },
    {
      type: 'radio',
      key: 'open',
      label: '打开方式',
      initVal: card.open || 'blank',
      required: false,
      forbid: !isApp,
      options: [
        { value: 'blank', text: '新页面' },
        { value: 'self', text: '当前页面' }
      ]
    },
    {
      type: 'select',
      key: 'copyMenuId',
      label: '复制菜单',
      initVal: card.copyMenuId || '',
      required: false,
      forbid: !isApp,
      options: appMenus
    },
    {
      type: 'radio',
      key: 'joint',
      label: Formdict['model.form.paramJoint'],
      initVal: card.joint || 'true',
@@ -376,6 +380,14 @@
        text: Formdict['model.false']
      }]
    },
    {
      type: 'select',
      key: 'linkurl',
      label: '链接地址',
      initVal: card.linkurl || '',
      required: true,
      options: []
    },
  ]
  return forms
src/menu/components/card/cardcellcomponent/index.jsx
@@ -11,7 +11,7 @@
import { getActionForm } from '@/menu/components/share/actioncomponent/formconfig'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import ElementForm from './elementform'
import DragElement from './dragaction'
import './index.scss'
src/menu/components/carousel/prop-card/index.scss
@@ -47,6 +47,11 @@
      width: 5px;
    }
  }
  .ant-carousel {
    .slick-dots li button {
      background: #1890ff;
    }
  }
  
  .card-item:hover {
    box-shadow: 0px 0px 2px #1890ff;
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
@@ -8,10 +8,25 @@
 * @param {object} card       // 图表对象
 */
export function getBaseForm (card) {
  let menulist = sessionStorage.getItem('fstMenuList')
  let isApp = sessionStorage.getItem('appType') === 'pc'
  let menulist = null
  if (isApp) {
    menulist = sessionStorage.getItem('appMenus')
  } else {
    menulist = sessionStorage.getItem('fstMenuList')
  }
  if (menulist) {
    try {
      menulist = JSON.parse(menulist)
      if (isApp) {
        menulist = menulist.map(item => {
          item.value = item.MenuID
          item.text = item.MenuName
          return item
        })
      }
    } catch {
      menulist = []
    }
@@ -83,7 +98,30 @@
      initVal: card.linkmenu || [],
      tooltip: '在使用柱形图且未启用自定义设置时有效。',
      required: false,
      forbid: isApp,
      options: menulist
    },
    {
      type: 'select',
      key: 'linkmenu',
      label: '关联菜单',
      initVal: card.linkmenu || '',
      tooltip: '双击饼图,会打开关联的菜单。',
      required: false,
      forbid: !isApp,
      options: menulist
    },
    {
      type: 'radio',
      key: 'open',
      label: '打开方式',
      initVal: card.open || 'blank',
      required: false,
      forbid: !isApp,
      options: [
        { value: 'blank', text: '新窗口' },
        { value: 'self', text: '当前窗口' }
      ]
    }
  ]
}
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
@@ -8,10 +8,25 @@
 * @param {object} card       // 图表对象
 */
export function getBaseForm (card) {
  let menulist = sessionStorage.getItem('fstMenuList')
  let isApp = sessionStorage.getItem('appType') === 'pc'
  let menulist = null
  if (isApp) {
    menulist = sessionStorage.getItem('appMenus')
  } else {
    menulist = sessionStorage.getItem('fstMenuList')
  }
  if (menulist) {
    try {
      menulist = JSON.parse(menulist)
      if (isApp) {
        menulist = menulist.map(item => {
          item.value = item.MenuID
          item.text = item.MenuName
          return item
        })
      }
    } catch {
      menulist = []
    }
@@ -83,7 +98,30 @@
      initVal: card.linkmenu || [],
      tooltip: '双击饼图,会打开关联的菜单。',
      required: false,
      forbid: isApp,
      options: menulist
    },
    {
      type: 'select',
      key: 'linkmenu',
      label: '关联菜单',
      initVal: card.linkmenu || '',
      tooltip: '双击饼图,会打开关联的菜单。',
      required: false,
      forbid: !isApp,
      options: menulist
    },
    {
      type: 'radio',
      key: 'open',
      label: '打开方式',
      initVal: card.open || 'blank',
      required: false,
      forbid: !isApp,
      options: [
        { value: 'blank', text: '新窗口' },
        { value: 'self', text: '当前窗口' }
      ]
    }
  ]
}
src/menu/components/share/actioncomponent/index.jsx
@@ -12,7 +12,7 @@
import MKEmitter from '@/utils/events.js'
import ActionForm from './actionform'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import CreateFunc from '@/templates/zshare/createfunc'
import DragElement from './dragaction'
import './index.scss'
src/menu/components/tabs/tabcomponents/index.jsx
@@ -6,7 +6,7 @@
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import Card from './card'
import './index.scss'
src/menu/datasource/verifycard/settingform/index.jsx
@@ -4,7 +4,7 @@
import { formRule } from '@/utils/option.js'
import Utils from '@/utils/utils.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
import './index.scss'
src/menu/menushell/index.jsx
@@ -6,7 +6,7 @@
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import Card from './card'
import './index.scss'
src/menu/modulesource/option.jsx
@@ -15,6 +15,7 @@
import Pie2 from '@/assets/mobimg/nightingale.png'
import Mainsearch from '@/assets/mobimg/mainsearch.png'
import Carousel from '@/assets/mobimg/carousel.png'
import Carousel1 from '@/assets/mobimg/carousel1.png'
// 组件配置信息
export const menuOptions = [
@@ -23,7 +24,7 @@
  { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '数据卡', width: 24 },
  { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '属性卡', width: 24 },
  { type: 'menu', url: Carousel, component: 'carousel', subtype: 'datacard', title: '轮播-动态数据', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: Carousel, component: 'carousel', subtype: 'propcard', title: '轮播-静态数据', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: Carousel1, component: 'carousel', subtype: 'propcard', title: '轮播-静态数据', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: NormalTable, component: 'table', subtype: 'normaltable', title: '常用表', width: 24 },
  { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '表格', width: 12 },
  { type: 'menu', url: line, component: 'line', subtype: 'line', title: '折线图', width: 24 },
src/pc/components/navbar/normal-navbar/index.jsx
@@ -13,8 +13,6 @@
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const MenuComponent = asyncIconComponent(() => import('./menusetting'))
const LinkComponent = asyncIconComponent(() => import('./linksetting'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const { SubMenu } = Menu
@@ -141,8 +139,11 @@
  changeMenu = (menu) => {
    MKEmitter.emit('changeEditMenu', {
      fixed: menu.property === 'menu',
      MenuID: menu.property === 'linkmenu' ? menu.linkMenuId : menu.MenuID,
      copyMenuId: menu.property === 'menu' ? menu.copyMenuId : '',
      MenuNo: menu.MenuNo,
      MenuName: menu.name,
    })
  }
@@ -169,9 +170,7 @@
            <MenuComponent config={card} updateConfig={this.updateComponent} />
            <LinkComponent config={card} updateConfig={this.updateComponent} />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="normalnarbar" card={card}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <UserComponent config={card}/>
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
          </div>
        } trigger="hover">
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx
@@ -14,7 +14,6 @@
  state = {
    property: this.props.menu.property || 'link',
    linkIntId: this.props.menu.linkIntId || '',
    appMenus: [],
  }
@@ -47,9 +46,6 @@
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          if (values.linkmenuid) {
            values.linkIntId = this.state.linkIntId || ''
          }
          resolve(values)
        } else {
          reject(err)
@@ -70,10 +66,6 @@
    let val = e.target.value
    this.setState({property: val})
  }
  changeLinkMenu = (val, { props }) => {
    this.setState({linkIntId: props.intid})
  }
  render() {
@@ -157,8 +149,8 @@
                  message: '请选择关联菜单!'
                }]
              })(
                <Select onChange={this.changeLinkMenu}>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} intid={item.menuid_int} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                <Select>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                </Select>
              )}
            </Form.Item>
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx
@@ -14,7 +14,6 @@
  state = {
    property: this.props.menu.property || 'menu',
    linkIntId: this.props.menu.linkIntId || '',
    appMenus: [],
  }
@@ -47,9 +46,6 @@
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          if (values.linkmenuid) {
            values.linkIntId = this.state.linkIntId || ''
          }
          resolve(values)
        } else {
          reject(err)
@@ -70,10 +66,6 @@
    let val = e.target.value
    this.setState({property: val})
  }
  changeLinkMenu = (val, { props }) => {
    this.setState({linkIntId: props.intid})
  }
  render() {
@@ -184,8 +176,8 @@
                  message: '请选择关联菜单!'
                }]
              })(
                <Select onChange={this.changeLinkMenu}>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} intid={item.menuid_int} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                <Select>
                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                </Select>
              )}
            </Form.Item>
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
@@ -16,8 +16,7 @@
  }
  state = {
    appMenus: [],
    logointid: this.props.wrap.linkIntId || ''
    appMenus: []
  }
  UNSAFE_componentWillMount () {
@@ -36,17 +35,10 @@
  }
  handleConfirm = () => {
    const { logointid } = this.state
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          values.linkIntId = ''
          if (values.logolink && logointid) {
            values.linkIntId = logointid
          }
          resolve(values)
        } else {
          reject(err)
@@ -67,7 +59,6 @@
    const { wrap } = this.props
    const { getFieldDecorator } = this.props.form
    const { appMenus } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -156,12 +147,11 @@
                })(
                  <Select
                    showSearch
                    onChange={(val, { props }) => this.setState({logointid: props.intid})}
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    <Select.Option key="empty" intid={''} value={''}>无</Select.Option>
                    {appMenus.map(option =>
                      <Select.Option key={option.MenuID} intid={option.menuid_int} value={option.MenuID}>{option.MenuName}</Select.Option>
                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
                    )}
                  </Select>
                )}
src/pc/menushell/index.jsx
@@ -6,7 +6,7 @@
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import Card from './card'
import './index.scss'
src/pc/modulesource/option.jsx
@@ -16,6 +16,7 @@
import Mainsearch from '@/assets/mobimg/mainsearch.png'
import Navbar from '@/assets/mobimg/navbar.png'
import Carousel from '@/assets/mobimg/carousel.png'
import Carousel1 from '@/assets/mobimg/carousel1.png'
// 组件配置信息
export const menuOptions = [
@@ -25,7 +26,7 @@
  { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '数据卡', width: 24 },
  { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '属性卡', width: 24 },
  { type: 'menu', url: Carousel, component: 'carousel', subtype: 'datacard', title: '轮播-动态数据', width: 24 },
  { type: 'menu', url: Carousel, component: 'carousel', subtype: 'propcard', title: '轮播-静态数据', width: 24 },
  { type: 'menu', url: Carousel1, component: 'carousel', subtype: 'propcard', title: '轮播-静态数据', width: 24 },
  { type: 'menu', url: NormalTable, component: 'table', subtype: 'normaltable', title: '常用表', width: 24 },
  { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '表格', width: 12 },
  { type: 'menu', url: line, component: 'line', subtype: 'line', title: '折线图', width: 24 },
src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -197,9 +197,14 @@
    } else if (btn.OpenType === 'pop') {
      this.updateStatus('start')
      let modal = this.state.btnconfig
      if (!modal && btn.modal) {
        modal = this.handleModelConfig(btn.modal)
      }
      this.setState({
        tabledata: data,
        btnconfig: btn.modal ? btn.modal : this.state.btnconfig
        btnconfig: modal
      }, () => {
        this.improveAction()
      })
@@ -1397,6 +1402,57 @@
    })
  }
  handleModelConfig = (config) => {
    let roleId = sessionStorage.getItem('role_id') || '' // 角色ID
    if (config.groups.length > 0) {
      config.groups.forEach(group => {
        group.sublist = group.sublist.map(cell => {
          // 数据源sql语句,预处理, 权限黑名单字段设置为隐藏表单
          if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
            let _option = Utils.getSelectQueryOptions(cell)
            // 外联数据库替换
            if (window.GLOB.externalDatabase !== null) {
              _option.sql = _option.sql.replace(/@db@/ig, window.GLOB.externalDatabase)
            }
            cell.data_sql = Utils.formatOptions(_option.sql)
            cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
            cell.arr_field = _option.field
          }
          // 字段权限黑名单
          if (!cell.blacklist || cell.blacklist.length === 0) return cell
          if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
            cell.hidden = 'true'
          }
          return cell
        })
      })
    } else {
      config.fields = config.fields.map(cell => {
        // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
        if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
          let _option = Utils.getSelectQueryOptions(cell)
          cell.data_sql = Utils.formatOptions(_option.sql)
          cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
          cell.arr_field = _option.field
        }
        // 字段权限黑名单
        if (!cell.blacklist || cell.blacklist.length === 0) return cell
        if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
          cell.hidden = 'true'
        }
        return cell
      })
    }
    return config
  }
  /**
   * @description 获取按钮配置信息
   */
@@ -1443,54 +1499,8 @@
          })
          this.updateStatus('over')
        } else {
          let roleId = sessionStorage.getItem('role_id') || '' // 角色ID
          if (_LongParam.groups.length > 0) {
            _LongParam.groups.forEach(group => {
              group.sublist = group.sublist.map(cell => {
                // 数据源sql语句,预处理, 权限黑名单字段设置为隐藏表单
                if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
                  let _option = Utils.getSelectQueryOptions(cell)
                  // 外联数据库替换
                  if (window.GLOB.externalDatabase !== null) {
                    _option.sql = _option.sql.replace(/@db@/ig, window.GLOB.externalDatabase)
                  }
                  cell.data_sql = Utils.formatOptions(_option.sql)
                  cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
                  cell.arr_field = _option.field
                }
                // 字段权限黑名单
                if (!cell.blacklist || cell.blacklist.length === 0) return cell
                if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
                  cell.hidden = 'true'
                }
                return cell
              })
            })
          } else {
            _LongParam.fields = _LongParam.fields.map(cell => {
              // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
              if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
                let _option = Utils.getSelectQueryOptions(cell)
                cell.data_sql = Utils.formatOptions(_option.sql)
                cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
                cell.arr_field = _option.field
              }
              // 字段权限黑名单
              if (!cell.blacklist || cell.blacklist.length === 0) return cell
              if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
                cell.hidden = 'true'
              }
              return cell
            })
          }
          _LongParam = this.handleModelConfig(_LongParam)
          this.setState({
            btnconfig: _LongParam
          }, () => {
src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -140,9 +140,14 @@
    if (btn.execMode === 'pop') {
      this.updateStatus('start')
      let modal = this.state.btnconfig
      if (!modal && btn.modal) {
        modal = this.handleModelConfig(btn.modal)
      }
      this.setState({
        tabledata: data,
        btnconfig: btn.modal ? btn.modal : this.state.btnconfig
        btnconfig: modal
      }, () => {
        this.improveAction()
      })
@@ -1022,6 +1027,57 @@
    })
  }
  handleModelConfig = (config) => {
    let roleId = sessionStorage.getItem('role_id') || '' // 角色ID
    if (config.groups.length > 0) {
      config.groups.forEach(group => {
        group.sublist = group.sublist.map(cell => {
          // 数据源sql语句,预处理, 权限黑名单字段设置为隐藏表单
          if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
            let _option = Utils.getSelectQueryOptions(cell)
            // 外联数据库替换
            if (window.GLOB.externalDatabase !== null) {
              _option.sql = _option.sql.replace(/@db@/ig, window.GLOB.externalDatabase)
            }
            cell.data_sql = Utils.formatOptions(_option.sql)
            cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
            cell.arr_field = _option.field
          }
          // 字段权限黑名单
          if (!cell.blacklist || cell.blacklist.length === 0) return cell
          if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
            cell.hidden = 'true'
          }
          return cell
        })
      })
    } else {
      config.fields = config.fields.map(cell => {
        // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
        if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
          let _option = Utils.getSelectQueryOptions(cell)
          cell.data_sql = Utils.formatOptions(_option.sql)
          cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
          cell.arr_field = _option.field
        }
        // 字段权限黑名单
        if (!cell.blacklist || cell.blacklist.length === 0) return cell
        if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
          cell.hidden = 'true'
        }
        return cell
      })
    }
    return config
  }
  /**
   * @description 获取按钮配置信息
   */
@@ -1068,53 +1124,7 @@
          })
          this.updateStatus('over')
        } else {
          let roleId = sessionStorage.getItem('role_id') || '' // 角色ID
          if (_LongParam.groups.length > 0) {
            _LongParam.groups.forEach(group => {
              group.sublist = group.sublist.map(cell => {
                // 数据源sql语句,预处理
                if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
                  let _option = Utils.getSelectQueryOptions(cell)
                  // 外联数据库替换
                  if (window.GLOB.externalDatabase !== null) {
                    _option.sql = _option.sql.replace(/@db@/ig, window.GLOB.externalDatabase)
                  }
                  cell.data_sql = Utils.formatOptions(_option.sql)
                  cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
                  cell.arr_field = _option.field
                }
                // 字段权限黑名单
                if (!cell.blacklist || cell.blacklist.length === 0) return cell
                if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
                  cell.hidden = 'true'
                }
                return cell
              })
            })
          } else {
            _LongParam.fields = _LongParam.fields.map(cell => {
              // 数据源sql语句,预处理
              if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
                let _option = Utils.getSelectQueryOptions(cell)
                cell.data_sql = Utils.formatOptions(_option.sql)
                cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
                cell.arr_field = _option.field
              }
              // 字段权限黑名单
              if (!cell.blacklist || cell.blacklist.length === 0) return cell
              if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
                cell.hidden = 'true'
              }
              return cell
            })
          }
          _LongParam = this.handleModelConfig(_LongParam)
          this.setState({
            btnconfig: _LongParam
src/templates/menuconfig/editthdmenu/index.jsx
@@ -21,7 +21,7 @@
import MenuForm from './menuform'
import TransferForm from '@/templates/zshare/basetransferform'
import Utils from '@/utils/utils.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import DragElement from '../menuelement'
import asyncLoadComponent from '@/utils/asyncLoadComponent'
import './index.scss'
src/templates/modalconfig/settingform/index.jsx
@@ -13,7 +13,8 @@
  }
  state = {
    fields: null
    fields: null,
    appType: sessionStorage.getItem('appType')
  }
  UNSAFE_componentWillMount () {
@@ -65,7 +66,7 @@
  render() {
    const { config, dict } = this.props
    const { fields } = this.state
    const { fields, appType } = this.state
    const { getFieldDecorator } = this.props.form
    const formItemLayout = {
@@ -161,7 +162,7 @@
              )}
            </Form.Item>
          </Col>
          {!this.props.isSubTab ? <Col span={12}>
          {!this.props.isSubTab && appType !== 'pc' ? <Col span={12}>
            <Form.Item label="挂载对象">
              {getFieldDecorator('container', {
                initialValue: config.setting.container || 'tab'
src/utils/utils-custom.js
File was renamed from src/menu/utils/menuUtils.js
@@ -183,7 +183,7 @@
          getUuids(c)
        })
      } else {
        if (item.action && item.action.length) {
        if (item.action && item.action.length > 0) {
          item.action.forEach(act => {
            if (!act.origin) {
              uuids.push(act.uuid)
@@ -198,6 +198,14 @@
              }
            })
            _card.backElements && _card.backElements.forEach(cell => {
              if (cell.eleType === 'button') {
                uuids.push(cell.uuid)
              }
            })
          })
        } else if (item.type === 'carousel') {
          item.subcards.forEach(_card => {
            _card.elements && _card.elements.forEach(cell => {
              if (cell.eleType === 'button') {
                uuids.push(cell.uuid)
              }
@@ -248,6 +256,10 @@
   */
  static resetConfig = (components) => {
    return components.map(item => {
      if (item.type === 'navbar') {
        return item
      }
      item.uuid = this.getuuid()
      if (item.type === 'tabs') {
@@ -268,7 +280,7 @@
          return cell
        })
        item.components = this.resetConfig(item.components)
      } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
      } else if (item.type === 'card' || item.type === 'carousel' || (item.type === 'table' && item.subtype === 'tablecard')) {
        item.subcards.forEach(card => {
          card.uuid = this.getuuid()
          if (card.elements) {
src/views/menudesign/index.jsx
@@ -15,7 +15,7 @@
import antdEnUS from 'antd/es/locale/en_US'
import antdZhCN from 'antd/es/locale/zh_CN'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import asyncComponent from '@/utils/asyncComponent'
import { modifyCustomMenu } from '@/store/action'
src/views/pcdesign/index.jsx
@@ -5,7 +5,7 @@
import { is, fromJS } from 'immutable'
import moment from 'moment'
import HTML5Backend from 'react-dnd-html5-backend'
import { ConfigProvider, notification, Modal, Collapse, Switch, Button, Icon } from 'antd'
import { ConfigProvider, notification, Modal, Collapse, Switch, Button, Icon, message, Spin } from 'antd'
import Api from '@/api'
import Utils from '@/utils/utils.js'
@@ -14,7 +14,7 @@
import antdEnUS from 'antd/es/locale/en_US'
import antdZhCN from 'antd/es/locale/zh_CN'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/menu/utils/menuUtils.js'
import MenuUtils from '@/utils/utils-custom.js'
import asyncComponent from '@/utils/asyncComponent'
import { modifyCustomMenu } from '@/store/action'
@@ -48,6 +48,7 @@
  state = {
    localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    loading: true,
    MenuId: '',
    MenuName: '',
    MenuNo: '',
@@ -76,7 +77,7 @@
        sessionStorage.setItem('link_type', param.link_type || 'true')
        sessionStorage.setItem('role_type', param.role_type || 'true')
        sessionStorage.setItem('login_types', param.login_types || 'true')
        this.setState({
          localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
          dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
@@ -86,7 +87,7 @@
        this.setState({
          MenuId: param.MenuID
        }, () => {
          this.getMenuParam()
          this.getMenuParam(param)
        })
      }
    } catch {
@@ -141,16 +142,28 @@
  changeEditMenu = (menu) => {
    const { oriConfig, config } = this.state
    if (!is(fromJS(oriConfig), fromJS(config))) {
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      notification.warning({
        top: 92,
        message: '配置已修改,请保存!',
        message: '配置信息未保存!',
        duration: 5
      })
      return
    }
    this.props.history.push('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: menu.MenuID, copyMenuId: menu.copyMenuId || '', type: 'view'}))))
    let param = {
      MenuID: menu.MenuID,
      copyMenuId: menu.copyMenuId || '',
      type: 'view'
    }
    if (menu.fixed && menu.MenuNo && menu.MenuName) {
      param.fixed = true
      param.MenuNo = menu.MenuNo
      param.MenuName = menu.MenuName
    }
    this.props.history.push('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify(param))))
  }
  getAppMessage = () => {
@@ -213,11 +226,13 @@
            })
          } else {
            sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
            sessionStorage.setItem('appHomeId', homeId)
            this.props.history.replace('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
          }
        })
      } else {
        sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
        sessionStorage.setItem('appHomeId', homeId)
        this.props.history.replace('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
      }
    })
@@ -329,10 +344,10 @@
  initPopview = (card, btn) => {
    const { oriConfig, config } = this.state
    if (!is(fromJS(oriConfig), fromJS(config))) {
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      notification.warning({
        top: 92,
        message: '配置已修改,请保存!',
        message: '配置信息未保存!',
        duration: 5
      })
      return
@@ -354,9 +369,9 @@
      return
    }
    if (!is(fromJS(oriConfig), fromJS(config))) {
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置已修改,放弃保存吗?',
        title: '配置信息未保存,确定关闭吗?',
        content: '',
        onOk() {
          window.close()
@@ -368,7 +383,7 @@
    }
  }
  getMenuParam = () => {
  getMenuParam = (urlParam) => {
    const { MenuId } = this.state
    let param = {
@@ -379,8 +394,19 @@
    }
    Api.getSystemConfig(param).then(result => {
      if (result.status) {
      if (!result.status) {
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
        this.setState({loading: false})
        return
      } else if (!result.LongParam && urlParam.copyMenuId) {
        this.getCopyParam(urlParam)
      } else {
        let config = null
        let isCreate = false
        try {
          config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
@@ -390,6 +416,7 @@
        }
        if (!config) {
          isCreate = true
          config = {
            version: 1.0,
            uuid: MenuId,
@@ -411,6 +438,12 @@
        config.MenuID = MenuId
        config.open_edition = result.open_edition || ''
        if (urlParam.fixed) {
          config.fixed = true
          config.MenuName = urlParam.MenuName
          config.MenuNo = urlParam.MenuNo
        }
        let indeComs = []
        config.components.forEach(item => {
          if (item.type === 'navbar') {
@@ -420,23 +453,21 @@
        if (indeComs.length === 0) {
          this.setState({
            oriConfig: config,
            oriConfig: isCreate ? null : config,
            config: fromJS(config).toJS(),
            loading: false
          })
  
          this.props.modifyCustomMenu(config)
        } else {
          this.jointComponents(config, indeComs)
          this.jointComponents(config, indeComs, isCreate)
        }
      } else {
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
      }
    })
    this.getAppMenus()
  }
  getAppMenus = () => {
    let _param = {
      func: 's_get_app_menus',
      TypeCharOne: sessionStorage.getItem('kei_no'),
@@ -466,7 +497,90 @@
    })
  }
  jointComponents = (config, indeComs) => {
  getCopyParam = (urlParam) => {
    const { MenuId } = this.state
    let param = {
      func: 'sPC_Get_LongParam',
      TypeCharOne: sessionStorage.getItem('kei_no'),
      typename: 'pc',
      MenuID: urlParam.copyMenuId
    }
    Api.getSystemConfig(param).then(result => {
      if (!result.status) {
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
        this.setState({loading: false})
        return
      } else if (!result.LongParam) {
        notification.warning({
          top: 92,
          message: '未查询到复制菜单配置信息!',
          duration: 5
        })
      }
      let config = null
      try {
        config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
      } catch (e) {
        console.warn('Parse Failure')
        config = null
      }
      if (!config) {
        config = {
          version: 1.0,
          uuid: MenuId,
          MenuID: MenuId,
          Template: 'webPage',
          enabled: false,
          MenuName: '',
          MenuNo: '',
          tables: [],
          components: [],
          viewType: 'menu',
          style: {
            backgroundColor: '#ffffff', backgroundImage: '', paddingLeft: '20px', paddingRight: '20px'
          }
        }
      } else {
        config.components = MenuUtils.resetConfig(config.components)
        message.success('复制成功,保存后生效。')
      }
      config.uuid = MenuId
      config.MenuID = MenuId
      config.open_edition = ''
      let indeComs = []
      config.components.forEach(item => {
        if (item.type === 'navbar') {
          indeComs.push(fromJS(item).toJS())
        }
      })
      if (indeComs.length === 0) {
        this.setState({
          oriConfig: null,
          config: fromJS(config).toJS(),
          loading: false
        })
        this.props.modifyCustomMenu(config)
      } else {
        this.jointComponents(config, indeComs, true)
      }
    })
  }
  jointComponents = (config, indeComs, isCreate) => {
    let deffers = indeComs.map(item => {
      return new Promise(resolve => {
        Api.getSystemConfig({
@@ -483,7 +597,6 @@
              message: res.message,
              duration: 5
            })
            return
          }
          
          resolve(res)
@@ -531,8 +644,9 @@
      }
      this.setState({
        oriConfig: fromJS(config).toJS(),
        config: config
        oriConfig: isCreate ? null : fromJS(config).toJS(),
        config: config,
        loading: false
      })
      this.props.modifyCustomMenu(config)
@@ -648,6 +762,11 @@
        message: '请完善菜单基本信息!',
        duration: 5
      })
      sessionStorage.setItem('settingshow', 'true')
      this.setState({
        settingshow: true,
        activeKey: 'basedata'
      })
      return
    }
@@ -750,7 +869,7 @@
                LText: '',
                LTexttb: ''
              }
              _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(item)))
              _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
              _param.secretkey = Utils.encrypt('', _param.timestamp)
@@ -901,13 +1020,12 @@
        if (!res) return
        if (delButtons.length === 0) {
          return {
            status: true
          }
          return { status: true, nonexec: true }
        } else {
          let appHomeId = sessionStorage.getItem('appHomeId')
          let _param = {
            func: 'sPC_MainMenu_Del',
            MenuID: delButtons.join(',')
            MenuID: delButtons.filter(id => id !== appHomeId).join(',')
          }
          return Api.getSystemConfig(_param)
        }
@@ -920,13 +1038,13 @@
            duration: 5
          })
          return false
        } else if (!res.nonexec) { // 执行删除后刷新菜单列表
          this.getAppMenus()
        }
        let ids = thawButtons.filter(item => btnIds.indexOf(item) !== -1)
        if (ids.length === 0) {
          return {
            status: true
          }
          return { status: true }
        } else {
          return Api.getSystemConfig({
            func: 'sPC_MainMenu_ReDel',
@@ -1191,14 +1309,19 @@
  refreshView = () => {
    const { oriConfig, config } = this.state
    if (!is(fromJS(oriConfig), fromJS(config))) {
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      notification.warning({
        top: 92,
        message: '配置已修改,请保存!',
        message: '配置信息未保存!',
        duration: 5
      })
      return
    }
    // Api.getSystemConfig({
    //   func: 'sPC_MainMenu_Del',
    //   MenuID: '1614740497468ku800sbg853vupf65v4'
    // })
    sessionStorage.removeItem('sysRoles')
    sessionStorage.removeItem('permFuncField')
@@ -1208,12 +1331,73 @@
    window.location.reload()
  }
  setHomeView = () => {
    const { oriConfig, config } = this.state
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      notification.warning({
        top: 92,
        message: '配置信息未保存!',
        duration: 5
      })
      return
    }
    let param = {
      func: 's_kei_link_keyids_addupt',
      BID: sessionStorage.getItem('appId'),
      exec_type: 'y',
      LText: ''
    }
    let appViewList = sessionStorage.getItem('appViewList')
    appViewList = appViewList ? JSON.parse(appViewList) : []
    appViewList = appViewList.filter(item => item.keys_type !== 'index')
    appViewList.unshift({
      appkey: window.GLOB.appkey || '',
      bid: sessionStorage.getItem('appId') || '',
      kei_no: sessionStorage.getItem('kei_no') || '',
      keys_id: config.MenuID,
      keys_type: 'index',
      remark: config.MenuName
    })
    param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
    param.LText = param.LText.join(' union all ')
    param.LText = Utils.formatOptions(param.LText)
    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    param.secretkey = Utils.encrypt('', param.timestamp)
    confirm({
      title: '确定设置本页面为首页吗?',
      content: '',
      onOk() {
        Api.getSystemConfig(param).then(result => {
          if (!result.status) {
            notification.warning({
              top: 92,
              message: result.message,
              duration: 5
            })
          } else {
            sessionStorage.setItem('appHomeId', config.MenuID)
            sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
          }
        })
      },
      onCancel() {}
    })
  }
  render () {
    const { localedict, activeKey, settingshow, controlshow, dict, MenuId, config, menuloading, customComponents } = this.state
    const { localedict, loading, activeKey, settingshow, controlshow, dict, MenuId, config, menuloading, customComponents } = this.state
    return (
      <ConfigProvider locale={localedict}>
        <div className={'mk-pc-view '} id="mk-menu-design-view">
          {loading ? <Spin className="view-spin" size="large" /> : null}
          <DndProvider backend={HTML5Backend}>
            <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
              <div className="draw">
@@ -1259,6 +1443,7 @@
              <SysInterface config={config} updateConfig={this.updateConfig}/>
              <PictureController/>
              <Quotecomponent config={config} updateConfig={this.updateConfig}/>
              <Button className="mk-border-green" icon="home" onClick={this.setHomeView}>设为首页</Button>
              <Button className="mk-border-danger" icon="redo" onClick={this.refreshView}>强制刷新</Button>
              <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
            </div>
src/views/pcdesign/index.scss
@@ -1,6 +1,16 @@
.mk-pc-view {
  background: #000;
  min-height: 100vh;
  >.view-spin {
    position: absolute;
    z-index: 3;
    left: calc(50% - 16px);
    top: calc(50vh - 70px);
  }
  .modal-form-board {
    padding-top: 0;
  }
  .menu-setting {
    position: fixed;
    left: 0;
src/views/pcdesign/menuform/index.jsx
@@ -69,7 +69,7 @@
                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.name'] + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" onChange={this.changeName}/>)}
              })(<Input placeholder="" disabled={!!(config.fixed && config.MenuName)} autoComplete="off" onChange={this.changeName}/>)}
            </Form.Item>
          </Col>
          <Col span={24}>
@@ -82,7 +82,7 @@
                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.param'] + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" onChange={this.changeNo}/>)}
              })(<Input placeholder="" disabled={!!(config.fixed && config.MenuName)} autoComplete="off" onChange={this.changeNo}/>)}
            </Form.Item>
          </Col>
          <Col span={24}>