New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Modal, Button, notification, Spin } from 'antd' |
| | | import { ForkOutlined } from '@ant-design/icons' |
| | | |
| | | import Api from '@/api' |
| | | import G6 from "@antv/g6" |
| | | import Utils from '@/utils/utils.js' |
| | | import MKEmitter from '@/utils/events.js' |
| | | import './index.scss' |
| | | |
| | | class TableNodes extends Component { |
| | | static propTpyes = { |
| | | config: PropTypes.object |
| | | } |
| | | |
| | | state = { |
| | | visible: false, |
| | | loading: false, |
| | | empty: false |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | getTbs = (config) => { |
| | | let tbs = [] |
| | | let ptbs = [] |
| | | |
| | | let traversal = (components) => { |
| | | components.forEach(item => { |
| | | if (item.$tables) { |
| | | ptbs.push(...item.$tables) |
| | | item.$tables.forEach(tb => { |
| | | tbs.push({ |
| | | label: item.name, |
| | | table: tb, |
| | | color: '#5AD8A6', |
| | | id: Utils.getuuid(), |
| | | direction: 'left' |
| | | }) |
| | | }) |
| | | } |
| | | if (item.type === 'tabs') { |
| | | item.subtabs.forEach(tab => { |
| | | traversal(tab.components) |
| | | }) |
| | | } else if (item.type === 'group') { |
| | | traversal(item.components) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | traversal(config.components) |
| | | |
| | | return {tbs, ptbs} |
| | | } |
| | | |
| | | trigger = () => { |
| | | const { config } = this.props |
| | | |
| | | this.setState({visible: true, loading: true, empty: false}, () => { |
| | | let param = { |
| | | func: 's_get_menus_tb_list', |
| | | TypeCharOne: sessionStorage.getItem('kei_no') || '', |
| | | typename: sessionStorage.getItem('typename') || '', |
| | | MenuID: config.uuid |
| | | } |
| | | |
| | | Api.getSystemConfig(param).then(result => { |
| | | if (!result.status) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: result.message, |
| | | duration: 5 |
| | | }) |
| | | |
| | | this.setState({empty: true, loading: false}) |
| | | |
| | | return |
| | | } |
| | | |
| | | let data = { |
| | | label: config.MenuName || '空', |
| | | id: config.uuid, |
| | | MenuID: config.MenuID, |
| | | children: [] |
| | | } |
| | | |
| | | let { tbs, ptbs } = this.getTbs(config) |
| | | |
| | | ptbs = Array.from(new Set(ptbs)) |
| | | ptbs.sort() |
| | | if (ptbs.length && sessionStorage.getItem('mk_tb_names')) { |
| | | let names = sessionStorage.getItem('mk_tb_names') |
| | | ptbs = ptbs.filter(tb => names.indexOf(',' + tb.toLowerCase() + ',') > -1) |
| | | } |
| | | |
| | | if (ptbs.length) { |
| | | ptbs.forEach((item, i) => { |
| | | let cell = { |
| | | label: item, |
| | | id: 'par' + i, |
| | | direction: 'left', |
| | | color: '#5AD8A6', |
| | | children: [] |
| | | } |
| | | |
| | | tbs.forEach(t => { |
| | | if (t.table === item) { |
| | | cell.children.push(t) |
| | | } |
| | | }) |
| | | |
| | | data.children.push(cell) |
| | | }) |
| | | } |
| | | |
| | | if (ptbs.length) { |
| | | ptbs.forEach((item, i) => { |
| | | let cell = { |
| | | label: item, |
| | | id: 'menu' + i, |
| | | direction: 'right', |
| | | color: '#1890ff', |
| | | // children: [] |
| | | } |
| | | |
| | | data.children.push(cell) |
| | | }) |
| | | } |
| | | |
| | | if (data.children.length === 0) { |
| | | this.setState({empty: true, loading: false}) |
| | | } else { |
| | | this.setState({loading: false}) |
| | | this.getForks(data) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | changeMenu = (menu) => { |
| | | if (menu.depth === 0) return |
| | | |
| | | MKEmitter.emit('changeEditMenu', menu) |
| | | } |
| | | |
| | | getForks = (data) => { |
| | | const { Util } = G6 |
| | | const that = this |
| | | |
| | | G6.registerNode( |
| | | 'dice-mind-map-root', { |
| | | jsx: (cfg) => { |
| | | const width = Util.getTextSize(cfg.label, 14)[0] + 12; |
| | | const stroke = cfg.style.stroke || '#096dd9'; |
| | | |
| | | return ` |
| | | <group> |
| | | <rect draggable="true" style={{width: ${width}, height: 30, stroke: ${stroke}, radius: 4}} keyshape> |
| | | <text style={{ fontSize: 14, marginLeft: 6, marginTop: 6 }}>${cfg.label}</text> |
| | | </rect> |
| | | </group> |
| | | `; |
| | | }, |
| | | getAnchorPoints() { |
| | | return [ |
| | | [0, 0.5], |
| | | [1, 0.5], |
| | | ]; |
| | | }, |
| | | }, |
| | | 'single-node', |
| | | ); |
| | | |
| | | G6.registerNode( |
| | | 'dice-mind-map-leaf', { |
| | | jsx: (cfg) => { |
| | | const width = Util.getTextSize(cfg.label, 12)[0] + 24; |
| | | const color = cfg.color; |
| | | |
| | | return ` |
| | | <group> |
| | | <rect draggable="true" style={{width: ${width}, height: 26, cursor: ${cfg.direction !== 'left' ? 'pointer' : 'default'}, fill: 'transparent' }}> |
| | | <text style={{ fontSize: 12, fill: black, cursor: ${cfg.direction !== 'left' ? 'pointer' : 'default'}, marginLeft: 12, marginTop: 6 }}>${cfg.label}</text> |
| | | </rect> |
| | | <rect style={{ fill: ${color}, width: ${width}, cursor: ${cfg.direction !== 'left' ? 'pointer' : 'default'}, height: 2, x: 0, y: 32 }} /> |
| | | </group> |
| | | `; |
| | | }, |
| | | getAnchorPoints() { |
| | | return [ |
| | | [0, 0.965], |
| | | [1, 0.965], |
| | | ]; |
| | | }, |
| | | }, |
| | | 'single-node', |
| | | ); |
| | | G6.registerBehavior('dice-mindmap', { |
| | | getEvents() { |
| | | return { |
| | | 'node:dblclick': 'editNode', |
| | | }; |
| | | }, |
| | | editNode(evt) { |
| | | const item = evt.item; |
| | | const model = item.get('model'); |
| | | |
| | | that.changeMenu(model) |
| | | } |
| | | }); |
| | | G6.registerBehavior('scroll-canvas', { |
| | | getEvents: function getEvents() { |
| | | return { |
| | | wheel: 'onWheel', |
| | | }; |
| | | }, |
| | | |
| | | onWheel: function onWheel(ev) { |
| | | const { |
| | | graph |
| | | } = this; |
| | | if (!graph) { |
| | | return; |
| | | } |
| | | if (ev.ctrlKey) { |
| | | const canvas = graph.get('canvas'); |
| | | const point = canvas.getPointByClient(ev.clientX, ev.clientY); |
| | | let ratio = graph.getZoom(); |
| | | if (ev.wheelDelta > 0) { |
| | | ratio += ratio * 0.05; |
| | | } else { |
| | | ratio *= ratio * 0.05; |
| | | } |
| | | graph.zoomTo(ratio, { |
| | | x: point.x, |
| | | y: point.y, |
| | | }); |
| | | } else { |
| | | const x = ev.deltaX || ev.movementX; |
| | | const y = ev.deltaY || ev.movementY || (-ev.wheelDelta * 125) / 3; |
| | | graph.translate(-x, -y); |
| | | } |
| | | ev.preventDefault(); |
| | | }, |
| | | }); |
| | | |
| | | const dataTransform = (data) => { |
| | | const changeData = (d, level = 0, color) => { |
| | | const data = { |
| | | ...d, |
| | | }; |
| | | switch (level) { |
| | | case 0: |
| | | data.type = 'dice-mind-map-root'; |
| | | break; |
| | | default: |
| | | data.type = 'dice-mind-map-leaf'; |
| | | break; |
| | | } |
| | | |
| | | data.hover = false; |
| | | |
| | | if (color) { |
| | | data.color = color; |
| | | } |
| | | |
| | | if (d.children) { |
| | | data.children = d.children.map((child) => changeData(child, level + 1, data.color)); |
| | | } |
| | | return data; |
| | | }; |
| | | return changeData(data); |
| | | }; |
| | | |
| | | const tree = new G6.TreeGraph({ |
| | | container: 'mountNode', |
| | | width: this.wrap.offsetWidth, |
| | | height: this.wrap.offsetHeight, |
| | | fitView: true, |
| | | fitViewPadding: [10, 20], |
| | | layout: { |
| | | type: 'mindmap', |
| | | direction: 'H', |
| | | getHeight: () => { |
| | | return 16; |
| | | }, |
| | | getWidth: (node) => { |
| | | return node.level === 0 ? |
| | | Util.getTextSize(node.label, 16)[0] + 12 : |
| | | Util.getTextSize(node.label, 12)[0]; |
| | | }, |
| | | getVGap: () => { |
| | | return 10; |
| | | }, |
| | | getHGap: () => { |
| | | return 60; |
| | | }, |
| | | getSide: (node) => { |
| | | return node.data.direction; |
| | | }, |
| | | }, |
| | | defaultEdge: { |
| | | type: 'cubic-horizontal', |
| | | style: { |
| | | lineWidth: 2, |
| | | }, |
| | | }, |
| | | minZoom: 0.5, |
| | | modes: { |
| | | default: ['drag-canvas', 'zoom-canvas', 'dice-mindmap'], |
| | | }, |
| | | }); |
| | | |
| | | tree.data(dataTransform(data)); |
| | | |
| | | tree.render(); |
| | | } |
| | | |
| | | render() { |
| | | const { visible, loading, empty } = this.state |
| | | |
| | | return ( |
| | | <div style={{display: 'inline-block'}}> |
| | | <Button style={{borderColor: '#8E44AD', color: '#8E44AD'}} onClick={this.trigger}><ForkOutlined /> 表关系图</Button> |
| | | <Modal |
| | | title="" |
| | | wrapClassName="view-table-modal" |
| | | visible={visible} |
| | | width={'90vw'} |
| | | closable={false} |
| | | maskClosable={false} |
| | | footer={[]} |
| | | destroyOnClose |
| | | > |
| | | <div className="header">页面关系图</div> |
| | | <div className="wrap"> |
| | | {loading ? <Spin size="large" /> : null} |
| | | {empty ? <div className="empty">未查询到页面关联菜单。</div> : null} |
| | | <div className="mountNode" id="mountNode" ref={ref => this.wrap = ref}></div> |
| | | </div> |
| | | <div className="footer"> |
| | | <Button key="cancel" onClick={() => { this.setState({ visible: false })}}>关闭</Button> |
| | | </div> |
| | | </Modal> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default TableNodes |
New file |
| | |
| | | .view-table-modal { |
| | | .ant-modal { |
| | | top: 55px; |
| | | } |
| | | .ant-modal-body { |
| | | padding: 20px 50px 20px; |
| | | user-select: none; |
| | | } |
| | | .ant-modal-footer { |
| | | display: none; |
| | | } |
| | | .wrap { |
| | | position: relative; |
| | | height: calc(100vh - 200px); |
| | | margin: 10px 0px; |
| | | overflow: hidden; |
| | | |
| | | .mountNode { |
| | | height: 100%; |
| | | } |
| | | |
| | | .ant-spin { |
| | | position: absolute; |
| | | top: calc(50% - 16px); |
| | | left: calc(50% - 16px); |
| | | } |
| | | .empty { |
| | | position: relative; |
| | | text-align: center; |
| | | color: #959595; |
| | | padding-top: 200px; |
| | | } |
| | | .empty + .mountNode { |
| | | opacity: 0; |
| | | } |
| | | } |
| | | .header { |
| | | color: #1890ff; |
| | | font-weight: 500; |
| | | text-align: center; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .footer { |
| | | text-align: center; |
| | | } |
| | | } |
| | |
| | | res.TBName = item.TbName |
| | | resolve(res) |
| | | }) |
| | | }, i * 50) |
| | | }, (i + 1) * 100) |
| | | }) |
| | | }) |
| | | Promise.all(deffers).then(response => { |
| | |
| | | return ( |
| | | <div> |
| | | {card.label ? <div className="mk-com-name">{card.label} - 验证信息</div> : null} |
| | | <Tabs activeKey={activeKey} className="verify-card-box" onChange={this.changeTab}> |
| | | <Tabs activeKey={activeKey} className="mk-verify-tabs" onChange={this.changeTab}> |
| | | {verifyInter === 'system' || card.intertype === 'inner' ? <TabPane tab={ |
| | | <span> |
| | | 基础验证 |
| | |
| | | .verify-card-box { |
| | | .mk-verify-tabs { |
| | | .ant-tabs-nav .ant-tabs-tab { |
| | | margin-right: 25px; |
| | | } |
| | | .ant-tabs-nav-scroll { |
| | | text-align: center; |
| | | } |
| | |
| | | color: #1890ff; |
| | | border-top: 1px solid #e9e9e9; |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: 1500px) { |
| | | .mk-verify-tabs { |
| | | .ant-tabs-nav .ant-tabs-tab { |
| | | margin: 0 15px 0 0; |
| | | padding: 12px 12px; |
| | | } |
| | | } |
| | | } |
| | |
| | | let end = new RegExp('@breakpoint_end_' + window.GLOB.breakpoint + '\\$\\*\\/', 'ig') |
| | | |
| | | _sql = _sql.replace(start, '').replace(end, '') |
| | | if (retmsg) { |
| | | _callbacksql = _callbacksql.replace(start, '').replace(end, '') |
| | | } |
| | | _sql += ` |
| | | z_debug_end: select @ErrorCode='E',@retmsg='debug_end' goto aaa` |
| | | } |
| | | |
| | | if (window.GLOB.debugger === true || (window.debugger === true && options.sysType !== 'cloud')) { |
| | |
| | | const StyleController = asyncComponent(() => import('@/menu/stylecontroller')) |
| | | const ReplaceField = asyncComponent(() => import('@/menu/replaceField')) |
| | | const Versions = asyncComponent(() => import('@/menu/versions')) |
| | | const TableNodes = asyncComponent(() => import('@/menu/tablenodes')) |
| | | const SysInterface = asyncComponent(() => import('@/menu/sysinterface')) |
| | | const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent')) |
| | | const PictureController = asyncComponent(() => import('@/menu/picturecontroller')) |
| | |
| | | componentDidMount () { |
| | | MKEmitter.addListener('delButtons', this.delButtons) |
| | | MKEmitter.addListener('modalStatus', this.modalStatus) |
| | | // MKEmitter.addListener('thawButtons', this.thawButtons) |
| | | MKEmitter.addListener('copyButtons', this.copyButtons) |
| | | MKEmitter.addListener('changePopview', this.initPopview) |
| | | MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave) |
| | |
| | | } |
| | | MKEmitter.removeListener('delButtons', this.delButtons) |
| | | MKEmitter.removeListener('modalStatus', this.modalStatus) |
| | | // MKEmitter.removeListener('thawButtons', this.thawButtons) |
| | | MKEmitter.removeListener('copyButtons', this.copyButtons) |
| | | MKEmitter.removeListener('changePopview', this.initPopview) |
| | | MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave) |
| | |
| | | copyButtons = (items) => { |
| | | this.setState({copyButtons: [...this.state.copyButtons, ...items]}) |
| | | } |
| | | |
| | | // thawButtons = (item) => { |
| | | // this.setState({thawButtons: [...this.state.thawButtons, item]}) |
| | | // } |
| | | |
| | | initPopview = (card, btn) => { |
| | | const { oriConfig, config } = this.state |
| | |
| | | } |
| | | return Api.getSystemConfig(_param) |
| | | } |
| | | // }).then(res => { // 按钮解除冻结 |
| | | // if (!res) return |
| | | // if (!res.status) { |
| | | // notification.warning({ |
| | | // top: 92, |
| | | // message: res.message, |
| | | // duration: 5 |
| | | // }) |
| | | // return false |
| | | // } |
| | | |
| | | // let ids = thawButtons.filter(item => btnIds.indexOf(item) !== -1) |
| | | // if (ids.length === 0) { |
| | | // return { |
| | | // status: true |
| | | // } |
| | | // } else { |
| | | // return Api.getSystemConfig({ |
| | | // func: 'sPC_MainMenu_ReDel', |
| | | // MenuID: ids.join(',') |
| | | // }) |
| | | // } |
| | | }).then(res => { // 页面保存 |
| | | if (!res) return |
| | | |
| | |
| | | </Collapse> |
| | | </div> |
| | | <div className={'menu-view' + (menuloading ? ' saving' : '') + (eyeopen ? ' eye-open' : '')}> |
| | | <Card title={ |
| | | <div style={{paddingLeft: '15px'}}> {config && config.MenuName} </div> |
| | | } bordered={false} extra={ |
| | | <div> |
| | | <Card title={config ? config.MenuName : ''} bordered={false} extra={ |
| | | <div className="mk-opeartion-list"> |
| | | <Button className="mk-border-purple" onClick={() => this.setState({eyeopen: !eyeopen})}>{!eyeopen ? <EyeOutlined /> : <EyeInvisibleOutlined />} 组件名</Button> |
| | | <Versions MenuId={MenuId} open_edition={config ? config.open_edition : ''}/> |
| | | <TableNodes config={config} /> |
| | | <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/> |
| | | <SysInterface config={config} updateConfig={this.updateConfig}/> |
| | | <PictureController/> |
| | |
| | | background: transparent!important; |
| | | border-radius: 0!important; |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: 1500px) { |
| | | .mk-opeartion-list .ant-btn { |
| | | min-width: 65px; |
| | | .anticon { |
| | | display: none; |
| | | } |
| | | .anticon + span { |
| | | margin-left: 0px; |
| | | } |
| | | } |
| | | } |