| | |
| | | } |
| | | |
| | | .ant-tabs .ant-tabs-left-bar .ant-tabs-tab { |
| | | padding: 0px; |
| | | text-align: right; |
| | | > span { |
| | | display: inline-block; |
| | |
| | | } |
| | | } |
| | | .ant-tabs .ant-tabs-right-bar .ant-tabs-tab { |
| | | padding: 0px; |
| | | text-align: left; |
| | | > span { |
| | | display: inline-block; |
| | |
| | | } |
| | | } |
| | | .ant-tabs-tab { |
| | | padding: 0px; |
| | | padding: 0px!important; |
| | | text-align: center; |
| | | > span { |
| | | display: inline-block; |
| | |
| | | |
| | | .ant-tabs-card-bar { |
| | | .ant-tabs-tab { |
| | | padding: 0px; |
| | | > span { |
| | | display: inline-block; |
| | | padding: 0px 16px; |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Icon, Popover } from 'antd' |
| | | |
| | | import asyncIconComponent from '@/utils/asyncIconComponent' |
| | | |
| | | import MKEmitter from '@/utils/events.js' |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import './index.scss' |
| | | |
| | | const WrapComponent = asyncIconComponent(() => import('./wrapsetting')) |
| | | const MenuComponent = asyncIconComponent(() => import('./menusetting')) |
| | | |
| | | class NormalNavbar extends Component { |
| | | static propTpyes = { |
| | | card: PropTypes.object, |
| | | deletecomponent: PropTypes.func, |
| | | updateConfig: PropTypes.func, |
| | | } |
| | | |
| | | state = { |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, |
| | | card: null, |
| | | back: false |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { card } = this.props |
| | | |
| | | if (card.isNew) { |
| | | let _card = { |
| | | uuid: card.uuid, |
| | | type: card.type, |
| | | floor: card.floor, |
| | | dataName: '', |
| | | width: card.width || 24, |
| | | name: card.name, |
| | | subtype: card.subtype, |
| | | wrap: { name: card.name, width: card.width || 1200 }, |
| | | style: { }, |
| | | menus: [], |
| | | } |
| | | |
| | | if (card.config) { |
| | | let config = fromJS(card.config).toJS() |
| | | |
| | | _card.wrap = config.wrap |
| | | _card.wrap.name = card.name |
| | | _card.style = config.style |
| | | } |
| | | |
| | | this.setState({ |
| | | card: _card |
| | | }) |
| | | this.props.updateConfig(_card) |
| | | } else { |
| | | this.setState({ |
| | | card: fromJS(card).toJS() |
| | | }) |
| | | } |
| | | } |
| | | |
| | | componentDidMount () { |
| | | MKEmitter.addListener('submitStyle', this.getStyle) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新,清除快捷键设置 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | MKEmitter.removeListener('submitStyle', this.getStyle) |
| | | } |
| | | |
| | | /** |
| | | * @description 卡片行外层信息更新(数据源,样式等) |
| | | */ |
| | | updateComponent = (component) => { |
| | | this.setState({ |
| | | card: component |
| | | }) |
| | | |
| | | component.width = component.wrap.width |
| | | component.name = component.wrap.name |
| | | |
| | | this.props.updateConfig(component) |
| | | } |
| | | |
| | | getStyle = (comIds, style) => { |
| | | const { card } = this.state |
| | | |
| | | if (comIds[0] !== card.uuid) return |
| | | |
| | | let _card = {...card} |
| | | if (comIds.length === 1) { |
| | | _card = {...card, style} |
| | | } |
| | | |
| | | this.setState({ |
| | | card: _card |
| | | }) |
| | | |
| | | this.props.updateConfig(_card) |
| | | } |
| | | |
| | | changeStyle = () => { |
| | | const { card } = this.state |
| | | |
| | | MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'shadow'], card.style) |
| | | } |
| | | |
| | | clickComponent = (e) => { |
| | | if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') { |
| | | e.stopPropagation() |
| | | MKEmitter.emit('clickComponent', this.state.card) |
| | | } |
| | | } |
| | | |
| | | changeMenu = (menu) => { |
| | | if (menu.property === 'link') { |
| | | window.open(menu.link) |
| | | return |
| | | } |
| | | 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, |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { card } = this.state |
| | | |
| | | let _style = {...card.style} |
| | | if (_style.shadow) { |
| | | _style.boxShadow = '0 0 4px ' + _style.shadow |
| | | } |
| | | |
| | | return ( |
| | | <div className="normal-navbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}> |
| | | <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ |
| | | <div className="mk-popover-control"> |
| | | <MenuComponent config={card} updateConfig={this.updateComponent} /> |
| | | <WrapComponent config={card} updateConfig={this.updateComponent} /> |
| | | <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" /> |
| | | <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} /> |
| | | </div> |
| | | } trigger="hover"> |
| | | <Icon type="tool" /> |
| | | </Popover> |
| | | <div className="menu"> |
| | | {card.menus.map(menu => { |
| | | return ( |
| | | <div class="am-tab-bar-tab"> |
| | | <div class="am-tab-bar-tab-icon"> |
| | | <span class="am-badge am-tab-bar-tab-badge tab-badge"> |
| | | <Icon type="font-colors" /> |
| | | <sup class="am-badge-text">1</sup> |
| | | </span> |
| | | </div> |
| | | <p class="am-tab-bar-tab-title">{menu.name}</p> |
| | | </div> |
| | | ) |
| | | })} |
| | | </div> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default NormalNavbar |
New file |
| | |
| | | .normal-navbar-edit-box { |
| | | position: fixed; |
| | | bottom: 0px; |
| | | left: 0px; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | background: #ffffff; |
| | | background-position: center center; |
| | | background-repeat: no-repeat; |
| | | background-size: cover; |
| | | min-height: 50px; |
| | | z-index: 3; |
| | | |
| | | .menu { |
| | | display: block; |
| | | font-size: inherit; |
| | | color: inherit; |
| | | .ant-menu { |
| | | background: transparent; |
| | | line-height: inherit; |
| | | font-size: inherit; |
| | | color: inherit; |
| | | border: 0; |
| | | .ant-menu-item:hover, .ant-menu-item-active { |
| | | color: unset; |
| | | } |
| | | .ant-menu-item span { |
| | | display: inline-block; |
| | | } |
| | | } |
| | | .ant-menu-horizontal > .ant-menu-item:hover, .ant-menu-horizontal > .ant-menu-item-active, .ant-menu-horizontal > .ant-menu-item-open, .ant-menu-horizontal > .ant-menu-item-selected { |
| | | color: unset; |
| | | } |
| | | } |
| | | .card-control { |
| | | position: absolute; |
| | | top: 0px; |
| | | left: 0px; |
| | | .anticon-tool { |
| | | right: auto; |
| | | left: 1px; |
| | | padding: 1px; |
| | | } |
| | | } |
| | | .anticon-tool { |
| | | position: absolute; |
| | | z-index: 2; |
| | | font-size: 16px; |
| | | right: 1px; |
| | | top: 1px; |
| | | cursor: pointer; |
| | | padding: 5px; |
| | | background: rgba(255, 255, 255, 0.55); |
| | | } |
| | | |
| | | .card-item { |
| | | overflow: hidden; |
| | | position: relative; |
| | | background-color: #ffffff; |
| | | background-position: center center; |
| | | background-repeat: no-repeat; |
| | | background-size: cover; |
| | | min-height: 20px; |
| | | } |
| | | |
| | | .card-item:hover { |
| | | box-shadow: 0px 0px 2px #1890ff; |
| | | } |
| | | |
| | | .model-menu-card-cell-list .card-detail-row > .anticon-plus { |
| | | position: absolute; |
| | | right: -30px; |
| | | font-size: 16px; |
| | | } |
| | | .model-menu-action-list { |
| | | line-height: 40px; |
| | | .ant-row > .anticon-plus { |
| | | position: absolute; |
| | | right: -30px; |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | .card-add-button { |
| | | text-align: right; |
| | | clear: left; |
| | | .anticon-plus { |
| | | font-size: 20px; |
| | | color: #26C281; |
| | | padding: 5px; |
| | | margin-right: 10px; |
| | | } |
| | | } |
| | | .ant-pagination { |
| | | float: right; |
| | | margin: 10px; |
| | | } |
| | | |
| | | .model-menu-action-list { |
| | | .page-card { |
| | | line-height: 55px; |
| | | } |
| | | } |
| | | } |
| | | .normal-navbar-edit-box::after { |
| | | display: block; |
| | | content: ' '; |
| | | clear: both; |
| | | } |
| | | .normal-navbar-edit-box:hover { |
| | | box-shadow: 0px 0px 4px #1890ff; |
| | | } |
| | | .top-menu-popover { |
| | | padding-top: 0!important; |
| | | } |
| | | .normal-navbar-submenu { |
| | | .ant-menu-item-group { |
| | | float: left; |
| | | } |
| | | .ant-menu-item { |
| | | height: 32px; |
| | | line-height: 32px; |
| | | span { |
| | | display: inline-block; |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 0 16px 0 28px; |
| | | } |
| | | padding: 0; |
| | | } |
| | | .ant-menu .ant-menu-item-selected { |
| | | background-color: #ffffff; |
| | | } |
| | | } |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Icon, Modal } from 'antd' |
| | | |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import MenuTable from './menutable' |
| | | import './index.scss' |
| | | |
| | | class DataSource extends Component { |
| | | static propTpyes = { |
| | | config: PropTypes.any, |
| | | updateConfig: PropTypes.func |
| | | } |
| | | |
| | | state = { |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, |
| | | visible: false |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | verifySubmit = () => { |
| | | const { config } = this.props |
| | | let menus = this.mTable.state.data || [] |
| | | |
| | | this.props.updateConfig({...config, menus}) |
| | | this.setState({visible: false}) |
| | | } |
| | | |
| | | render () { |
| | | const { config } = this.props |
| | | const { visible, dict } = this.state |
| | | |
| | | return ( |
| | | <div className="model-menu-setting-wrap"> |
| | | <Icon type="menu" title="菜单" onClick={() => this.setState({ visible: true })}/> |
| | | <Modal |
| | | wrapClassName="popview-modal" |
| | | title="菜单编辑" |
| | | visible={visible} |
| | | width={950} |
| | | maskClosable={false} |
| | | okText={dict['model.submit']} |
| | | onOk={this.verifySubmit} |
| | | onCancel={() => { this.setState({ visible: false }) }} |
| | | destroyOnClose |
| | | > |
| | | <MenuTable |
| | | menus={config.menus} |
| | | ref={(ref) => { this.mTable = ref }} |
| | | /> |
| | | </Modal> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default DataSource |
New file |
| | |
| | | .model-menu-setting-wrap { |
| | | display: inline-block; |
| | | |
| | | >.anticon-menu { |
| | | color: purple; |
| | | } |
| | | |
| | | >.anticon-edit { |
| | | color: #1890ff; |
| | | } |
| | | } |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { Form, Row, Col, Input, Radio, Tooltip, Icon, Select } from 'antd' |
| | | |
| | | import './index.scss' |
| | | |
| | | const { TextArea } = Input |
| | | |
| | | class SettingForm extends Component { |
| | | static propTpyes = { |
| | | menu: PropTypes.object, // 卡片行信息 |
| | | inputSubmit: PropTypes.func // 回车事件 |
| | | } |
| | | |
| | | state = { |
| | | property: this.props.menu.property || 'menu', |
| | | appMenus: [], |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | let appMenus = sessionStorage.getItem('appMenus') |
| | | if (appMenus) { |
| | | try { |
| | | appMenus = JSON.parse(appMenus) |
| | | } catch { |
| | | appMenus = [] |
| | | } |
| | | } else { |
| | | appMenus = [] |
| | | } |
| | | |
| | | this.setState({appMenus}) |
| | | } |
| | | |
| | | componentDidMount() { |
| | | const { menu } = this.props |
| | | |
| | | if (!menu.MenuID) { |
| | | let _form = document.getElementById('name') |
| | | _form && _form.select() |
| | | } |
| | | } |
| | | |
| | | handleConfirm = () => { |
| | | // 表单提交时检查输入值是否正确 |
| | | return new Promise((resolve, reject) => { |
| | | this.props.form.validateFieldsAndScroll((err, values) => { |
| | | if (!err) { |
| | | resolve(values) |
| | | } else { |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | handleSubmit = (e) => { |
| | | e.preventDefault() |
| | | |
| | | if (this.props.inputSubmit) { |
| | | this.props.inputSubmit() |
| | | } |
| | | } |
| | | |
| | | changeProperty = (e) => { |
| | | let val = e.target.value |
| | | |
| | | this.setState({property: val}) |
| | | } |
| | | |
| | | render() { |
| | | const { menu } = this.props |
| | | const { getFieldDecorator } = this.props.form |
| | | const { property, appMenus } = this.state |
| | | |
| | | const formItemLayout = { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 8 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 16 } |
| | | } |
| | | } |
| | | |
| | | return ( |
| | | <Form {...formItemLayout}> |
| | | <Row gutter={24}> |
| | | <Col span={22}> |
| | | <Form.Item label="菜单名称"> |
| | | {getFieldDecorator('name', { |
| | | initialValue: menu.name, |
| | | rules: [ |
| | | { |
| | | required: true, |
| | | message: '请输入菜单名称!' |
| | | } |
| | | ] |
| | | })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)} |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={22}> |
| | | <Form.Item label="菜单参数"> |
| | | {getFieldDecorator('MenuNo', { |
| | | initialValue: menu.MenuNo || '', |
| | | rules: [ |
| | | { |
| | | required: true, |
| | | message: '请输入菜单参数!' |
| | | } |
| | | ] |
| | | })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)} |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={22}> |
| | | <Form.Item label="图标"> |
| | | {getFieldDecorator('icon', { |
| | | initialValue: menu.icon || '' |
| | | })( |
| | | <Select> |
| | | {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))} |
| | | </Select> |
| | | )} |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={22}> |
| | | <Form.Item label="菜单属性"> |
| | | {getFieldDecorator('property', { |
| | | initialValue: menu.property || 'menu' |
| | | })( |
| | | <Radio.Group onChange={this.changeProperty}> |
| | | <Radio value="menu">菜单</Radio> |
| | | <Radio value="link">链接</Radio> |
| | | <Radio value="linkmenu">关联菜单</Radio> |
| | | </Radio.Group> |
| | | )} |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={22}> |
| | | <Form.Item label="隐藏"> |
| | | {getFieldDecorator('hidden', { |
| | | initialValue: menu.hidden || 'false' |
| | | })( |
| | | <Radio.Group> |
| | | <Radio value="false">否</Radio> |
| | | <Radio value="true">是</Radio> |
| | | </Radio.Group> |
| | | )} |
| | | </Form.Item> |
| | | </Col> |
| | | {property === 'link' ? <Col span={22}> |
| | | <Form.Item label="链接地址"> |
| | | {getFieldDecorator('link', { |
| | | initialValue: menu.link || '', |
| | | rules: [{ |
| | | required: true, |
| | | message: '请输入链接地址!' |
| | | }] |
| | | })(<TextArea rows={2} />)} |
| | | </Form.Item> |
| | | </Col> : null} |
| | | {property === 'linkmenu' ? <Col span={22}> |
| | | <Form.Item label={ |
| | | <Tooltip placement="topLeft" title="关联当前app中已有的菜单。"> |
| | | <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/> |
| | | 关联菜单 |
| | | </Tooltip> |
| | | }> |
| | | {getFieldDecorator('linkMenuId', { |
| | | initialValue: menu.linkMenuId || '', |
| | | rules: [{ |
| | | required: true, |
| | | message: '请选择关联菜单!' |
| | | }] |
| | | })( |
| | | <Select> |
| | | {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))} |
| | | </Select> |
| | | )} |
| | | </Form.Item> |
| | | </Col> : null} |
| | | {property === 'menu' ? <Col span={22}> |
| | | <Form.Item label={ |
| | | <Tooltip placement="topLeft" title="复制菜单仅在当前菜单不存在时有效。"> |
| | | <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/> |
| | | 复制菜单 |
| | | </Tooltip> |
| | | }> |
| | | {getFieldDecorator('copyMenuId', { |
| | | initialValue: menu.copyMenuId || '' |
| | | })( |
| | | <Select> |
| | | {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))} |
| | | </Select> |
| | | )} |
| | | </Form.Item> |
| | | </Col> : null} |
| | | </Row> |
| | | </Form> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default Form.create()(SettingForm) |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Table, Button, Modal, Icon } from 'antd' |
| | | |
| | | import MenuForm from '../menuform' |
| | | import Utils from '@/utils/utils.js' |
| | | import MKEmitter from '@/utils/events.js' |
| | | import './index.scss' |
| | | |
| | | const { confirm } = Modal |
| | | |
| | | class MenuTable extends Component { |
| | | static propTpyes = { |
| | | menus: PropTypes.object, // 卡片行信息 |
| | | } |
| | | |
| | | state = { |
| | | data: [], |
| | | editMenu: null, |
| | | columns: [ |
| | | { title: '菜单名称', dataIndex: 'name', key: 'name' }, |
| | | { title: '菜单参数', dataIndex: 'MenuNo', key: 'MenuNo' }, |
| | | { title: '菜单属性', dataIndex: 'property', key: 'property', render: text => { |
| | | const trans = {menu: '菜单', link: '链接', linkmenu: '关联菜单'} |
| | | |
| | | return trans[text] |
| | | }}, |
| | | { title: '是否隐藏', dataIndex: 'hidden', key: 'hidden', render: (text, record) => { |
| | | const trans = {'true': '是', 'false': '否'} |
| | | return trans[text] || '否' |
| | | }}, |
| | | { title: '操作', key: 'operation', align: 'center', width: '190px', render: (text, record) => |
| | | (<div> |
| | | <Button type="link" style={{padding: '0 5px', marginRight: '5px'}} onClick={() => this.editMenu(record)}>编辑</Button> |
| | | <Button type="link" style={{color: '#ff4d4f', padding: '0 5px', marginRight: '5px'}} onClick={() => this.delMenu(record)}>删除</Button> |
| | | <Icon type="arrow-up" style={{color: '#26C281', cursor: 'pointer', padding: '0 5px', marginRight: '5px'}} onClick={() => this.moveUp(record)}/> |
| | | <Icon type="arrow-down" style={{color: '#ff4d4f', cursor: 'pointer', padding: '0 5px'}} onClick={() => this.moveDown(record)}/> |
| | | </div>) |
| | | } |
| | | ] |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { menus } = this.props |
| | | |
| | | this.setState({data: fromJS(menus).toJS()}) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | moveUp = (record) => { |
| | | let data = fromJS(this.state.data).toJS() |
| | | |
| | | let dragIndex = data.findIndex(c => c.MenuID === record.MenuID) |
| | | let hoverIndex = dragIndex - 1 |
| | | |
| | | if (hoverIndex === -1) return |
| | | |
| | | data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1)) |
| | | this.setState({data}) |
| | | } |
| | | |
| | | moveDown = (record) => { |
| | | let data = fromJS(this.state.data).toJS() |
| | | |
| | | let dragIndex = data.findIndex(c => c.MenuID === record.MenuID) |
| | | let hoverIndex = dragIndex + 1 |
| | | |
| | | if (hoverIndex === data.length) return |
| | | |
| | | data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1)) |
| | | this.setState({data}) |
| | | } |
| | | |
| | | delMenu = (record) => { |
| | | const { data } = this.state |
| | | const _this = this |
| | | |
| | | confirm({ |
| | | title: (record.property === 'classify' && record.sublist.length > 0 ? '菜单下含有子菜单,' : '') + '确定删除吗?', |
| | | content: '', |
| | | onOk() { |
| | | _this.setState({data: data.filter(item => item.MenuID !== record.MenuID)}) |
| | | |
| | | let uuids = [record.MenuID] |
| | | record.sublist && record.sublist.forEach(item => { |
| | | uuids.push(item.MenuID) |
| | | |
| | | item.sublist && item.sublist.forEach(cell => { |
| | | uuids.push(cell.MenuID) |
| | | }) |
| | | }) |
| | | MKEmitter.emit('delButtons', uuids) |
| | | }, |
| | | onCancel() {} |
| | | }) |
| | | } |
| | | |
| | | editMenu = (record) => { |
| | | this.setState({editMenu: record, visible: true}) |
| | | } |
| | | |
| | | plusMenu = () => { |
| | | let _menu = { |
| | | name: '菜单', |
| | | property: 'menu' |
| | | } |
| | | |
| | | this.setState({editMenu: _menu, visible: true}) |
| | | } |
| | | |
| | | menuSubmit = () => { |
| | | const { editMenu, data } = this.state |
| | | |
| | | this.menuRef.handleConfirm().then(res => { |
| | | let _menu = {...editMenu, ...res} |
| | | if (!_menu.MenuID) { |
| | | _menu.MenuID = Utils.getuuid() |
| | | this.setState({data: [...data, _menu], editMenu: null, visible: false}) |
| | | } else { |
| | | this.setState({ |
| | | editMenu: null, |
| | | visible: false, |
| | | data: data.map(item => { |
| | | if (item.MenuID === _menu.MenuID) { |
| | | return _menu |
| | | } else { |
| | | return item |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | menuUpdate = (res) => { |
| | | const { data } = this.state |
| | | |
| | | this.setState({ |
| | | data: data.map(item => { |
| | | if (item.MenuID === res.MenuID) { |
| | | return res |
| | | } else { |
| | | return item |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { columns, data, visible, editMenu } = this.state |
| | | |
| | | return ( |
| | | <div className="menu-control-wrap"> |
| | | <Button className="menu-plus mk-green" onClick={this.plusMenu}>添加</Button> |
| | | <Table |
| | | rowKey="MenuID" |
| | | columns={columns} |
| | | dataSource={data} |
| | | pagination={false} |
| | | /> |
| | | <Modal |
| | | title="编辑" |
| | | visible={visible} |
| | | width={600} |
| | | maskClosable={false} |
| | | onOk={this.menuSubmit} |
| | | onCancel={() => { this.setState({ visible: false }) }} |
| | | destroyOnClose |
| | | > |
| | | <MenuForm |
| | | menu={editMenu} |
| | | inputSubmit={this.menuSubmit} |
| | | wrappedComponentRef={(inst) => this.menuRef = inst} |
| | | /> |
| | | </Modal> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default MenuTable |
New file |
| | |
| | | .menu-control-wrap { |
| | | position: relative; |
| | | |
| | | .menu-plus { |
| | | float: right; |
| | | position: relative; |
| | | z-index: 1; |
| | | margin-bottom: 5px; |
| | | } |
| | | .ant-empty { |
| | | margin: 5px 0; |
| | | } |
| | | thead tr { |
| | | background: #fbfbfb; |
| | | } |
| | | tbody > tr { |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .ant-table-body { |
| | | margin: 0!important; |
| | | } |
| | | } |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Icon, Modal } from 'antd' |
| | | |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import SettingForm from './settingform' |
| | | import './index.scss' |
| | | |
| | | class DataSource extends Component { |
| | | static propTpyes = { |
| | | config: PropTypes.any, |
| | | updateConfig: PropTypes.func |
| | | } |
| | | |
| | | state = { |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, |
| | | visible: false, |
| | | wrap: null |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { config } = this.props |
| | | |
| | | this.setState({wrap: fromJS(config.wrap).toJS()}) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | | return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | editDataSource = () => { |
| | | this.setState({ |
| | | visible: true |
| | | }) |
| | | } |
| | | |
| | | verifySubmit = () => { |
| | | const { config } = this.props |
| | | |
| | | this.verifyRef.handleConfirm().then(res => { |
| | | |
| | | this.setState({ |
| | | wrap: res, |
| | | visible: false |
| | | }) |
| | | this.props.updateConfig({...config, wrap: res}) |
| | | }) |
| | | } |
| | | |
| | | render () { |
| | | const { config } = this.props |
| | | const { visible, dict, wrap } = this.state |
| | | |
| | | return ( |
| | | <div className="model-menu-setting-wrap"> |
| | | <Icon type="edit" title="编辑" onClick={() => this.editDataSource()} /> |
| | | <Modal |
| | | wrapClassName="popview-modal" |
| | | title={config.type === 'table' ? '表格设置' : '卡片设置'} |
| | | visible={visible} |
| | | width={800} |
| | | maskClosable={false} |
| | | okText={dict['model.submit']} |
| | | onOk={this.verifySubmit} |
| | | onCancel={() => { this.setState({ visible: false }) }} |
| | | destroyOnClose |
| | | > |
| | | <SettingForm |
| | | dict={dict} |
| | | wrap={wrap} |
| | | config={config} |
| | | inputSubmit={this.verifySubmit} |
| | | wrappedComponentRef={(inst) => this.verifyRef = inst} |
| | | /> |
| | | </Modal> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default DataSource |
New file |
| | |
| | | .model-menu-setting-wrap { |
| | | display: inline-block; |
| | | |
| | | >.anticon-edit { |
| | | color: #1890ff; |
| | | } |
| | | } |
New file |
| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { Form, Row, Col, Input, InputNumber } from 'antd' |
| | | |
| | | import './index.scss' |
| | | |
| | | class SettingForm extends Component { |
| | | static propTpyes = { |
| | | dict: PropTypes.object, // 字典项 |
| | | config: PropTypes.object, // 卡片行信息 |
| | | wrap: PropTypes.object, // 数据源配置 |
| | | inputSubmit: PropTypes.func // 回车事件 |
| | | } |
| | | |
| | | state = {} |
| | | |
| | | handleConfirm = () => { |
| | | // 表单提交时检查输入值是否正确 |
| | | return new Promise((resolve, reject) => { |
| | | this.props.form.validateFieldsAndScroll((err, values) => { |
| | | if (!err) { |
| | | resolve(values) |
| | | } else { |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | handleSubmit = (e) => { |
| | | e.preventDefault() |
| | | |
| | | if (this.props.inputSubmit) { |
| | | this.props.inputSubmit() |
| | | } |
| | | } |
| | | |
| | | render() { |
| | | const { wrap } = this.props |
| | | const { getFieldDecorator } = this.props.form |
| | | const formItemLayout = { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 8 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 16 } |
| | | } |
| | | } |
| | | |
| | | return ( |
| | | <div className="model-menu-setting-form"> |
| | | <Form {...formItemLayout}> |
| | | <Row gutter={24}> |
| | | <Col span={12}> |
| | | <Form.Item label="导航栏名称"> |
| | | {getFieldDecorator('name', { |
| | | initialValue: wrap.name, |
| | | rules: [ |
| | | { |
| | | required: true, |
| | | message: this.props.dict['form.required.input'] + '导航栏名称!' |
| | | } |
| | | ] |
| | | })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)} |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={12}> |
| | | <Form.Item label="菜单参数"> |
| | | {getFieldDecorator('MenuNo', { |
| | | initialValue: wrap.MenuNo, |
| | | rules: [ |
| | | { |
| | | required: true, |
| | | message: this.props.dict['form.required.input'] + '菜单参数!' |
| | | } |
| | | ] |
| | | })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)} |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={12}> |
| | | <Form.Item label="高度"> |
| | | {getFieldDecorator('height', { |
| | | initialValue: wrap.height || 50, |
| | | rules: [ |
| | | { |
| | | required: true, |
| | | message: this.props.dict['form.required.input'] + '高度!' |
| | | } |
| | | ] |
| | | })(<InputNumber min={50} max={200} precision={0} onPressEnter={this.handleSubmit} />)} |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | </Form> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | export default Form.create()(SettingForm) |
New file |
| | |
| | | .model-menu-setting-form { |
| | | position: relative; |
| | | |
| | | .anticon-question-circle { |
| | | color: #c49f47; |
| | | margin-right: 3px; |
| | | } |
| | | .ant-input-number { |
| | | width: 100%; |
| | | } |
| | | } |
| | |
| | | const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group')) |
| | | const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox')) |
| | | const NormalLogin = asyncComponent(() => import('@/pc/components/login/normal-login')) |
| | | const NormalNavbar = asyncComponent(() => import('@/mob/components/navbar/normal-navbar')) |
| | | |
| | | const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => { |
| | | const originalIndex = findCard(id).index |
| | |
| | | style = { opacity: 0.3} |
| | | } |
| | | |
| | | let col = 'ant-col-' + (card.width || 24) |
| | | if (card.type === 'navbar') { |
| | | col = '' |
| | | } else 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) * 420 / 100 + 'px' |
| | | // return parseFloat(word) * 350 / 100 + 'px' |
| | | }).replace(/\d+vh/ig, (word) => { |
| | | return parseFloat(word) * 738 / 100 + 'px' |
| | | // return parseFloat(word) * 615 / 100 + 'px' |
| | | }) |
| | | } |
| | | |
| | | style.minHeight = height |
| | | } |
| | | |
| | | const getCardComponent = () => { |
| | | if (card.type === 'bar' || card.type === 'line') { |
| | | return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | |
| | | return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } else if (card.type === 'login') { |
| | | return (<NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } else if (card.type === 'navbar') { |
| | | return (<NormalNavbar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } |
| | | } |
| | | |
| | | 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) * 420 / 100 + 'px' |
| | | // return parseFloat(word) * 350 / 100 + 'px' |
| | | }).replace(/\d+vh/ig, (word) => { |
| | | return parseFloat(word) * 738 / 100 + 'px' |
| | | // return parseFloat(word) * 615 / 100 + 'px' |
| | | }) |
| | | } |
| | | |
| | | style.minHeight = height |
| | | } |
| | | |
| | | return ( |
| | | <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}> |
| | | <div className={'ant-col mk-component-card ' + col} ref={node => drag(drop(node))} style={style}> |
| | | {getCardComponent()} |
| | | </div> |
| | | ) |
| | |
| | | let col = ' ant-col ant-col-' + (card.width || 24) |
| | | if (card.type === 'navbar') { |
| | | col = '' |
| | | } else 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) * 420 / 100 + 'px' |
| | | // return parseFloat(word) * 350 / 100 + 'px' |
| | | }).replace(/\d+vh/ig, (word) => { |
| | | return parseFloat(word) * 738 / 100 + 'px' |
| | | // return parseFloat(word) * 615 / 100 + 'px' |
| | | }) |
| | | } |
| | | |
| | | style.minHeight = height |
| | | } |
| | | |
| | | const getCardComponent = () => { |
| | |
| | | } else if (card.type === 'code') { |
| | | return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } else if (card.type === 'login') { |
| | | return (<> |
| | | <NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/> |
| | | <div style={{float: 'right', height: card.wrap ? card.wrap.height : ''}}></div> |
| | | </>) |
| | | return (<NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) |
| | | } |
| | | } |
| | | return ( |
| | |
| | | |
| | | const editCard = id => { |
| | | const { card } = findCard(id) |
| | | delete card.focus |
| | | handleForm(card) |
| | | } |
| | | |
| | |
| | | 'step-forward', |
| | | 'fast-backward', |
| | | 'fast-forward', |
| | | 'shrink', |
| | | 'arrows-alt', |
| | | 'up-circle', |
| | | 'down-circle', |
| | | 'left-circle', |
| | | 'right-circle', |
| | | 'double-right', |
| | | 'double-left', |
| | | 'vertical-left', |
| | | 'vertical-right', |
| | | 'vertical-align-top', |
| | | 'vertical-align-middle', |
| | | 'vertical-align-bottom', |
| | | 'forward', |
| | | 'backward', |
| | | 'rollback', |
| | |
| | | 'swap-left', |
| | | 'swap-right', |
| | | 'play-circle', |
| | | 'up-square', |
| | | 'down-square', |
| | | 'left-square', |
| | | 'right-square', |
| | | 'login', |
| | | 'logout', |
| | | 'menu-fold', |
| | | 'menu-unfold', |
| | | 'border-bottom', |
| | | 'border-horizontal', |
| | | 'border-inner', |
| | | 'border-outer', |
| | | 'border-left', |
| | | 'border-right', |
| | | 'border-top', |
| | | 'border-verticle', |
| | | 'pic-center', |
| | | 'pic-left', |
| | | 'pic-right', |
| | | 'radius-bottomleft', |
| | | 'radius-bottomright', |
| | | 'radius-upleft', |
| | | 'radius-upright', |
| | | 'fullscreen', |
| | | 'fullscreen-exit' |
| | | ], |
| | |
| | | 'question-circle', |
| | | 'plus', |
| | | 'plus-circle', |
| | | 'pause', |
| | | 'pause-circle', |
| | | 'minus', |
| | | 'minus-circle', |
| | | 'plus-square', |
| | | 'minus-square', |
| | | 'info', |
| | | 'info-circle', |
| | | 'exclamation', |
| | | 'exclamation-circle', |
| | | 'close', |
| | | 'close-circle', |
| | | 'close-square', |
| | | 'check', |
| | | 'check-circle', |
| | | 'check-square', |
| | | 'clock-circle', |
| | | 'warning', |
| | | 'issues-close', |
| | | 'stop' |
| | | ], |
| | | edit: [ |
| | |
| | | 'scissor', |
| | | 'delete', |
| | | 'snippets', |
| | | 'diff', |
| | | 'highlight', |
| | | 'align-center', |
| | | 'align-left', |
| | | 'align-right', |
| | | 'bg-colors', |
| | | 'bold', |
| | | 'italic', |
| | | 'underline', |
| | | 'strikethrough', |
| | | 'redo', |
| | | 'undo', |
| | | 'zoom-in', |
| | | 'zoom-out', |
| | | 'font-colors', |
| | | 'font-size', |
| | | 'line-height', |
| | | 'dash', |
| | | 'small-dash', |
| | | 'sort-ascending', |
| | | 'sort-descending', |
| | | 'drag', |
| | | 'ordered-list', |
| | | 'unordered-list', |
| | | 'radius-setting', |
| | | 'column-width', |
| | | 'column-height' |
| | | 'sort-descending' |
| | | ], |
| | | data: [ |
| | | 'area-chart', |
| | |
| | | 'dot-chart', |
| | | 'line-chart', |
| | | 'radar-chart', |
| | | 'heat-map', |
| | | 'fall', |
| | | 'rise', |
| | | 'stock', |
| | | 'box-plot', |
| | | 'fund', |
| | | 'sliders' |
| | | ], |
| | | trademark: [ |
| | | 'android', |
| | | 'apple', |
| | | 'windows', |
| | | 'ie', |
| | | 'chrome', |
| | | 'github', |
| | | 'aliwangwang', |
| | | 'dingding', |
| | | 'weibo-square', |
| | | 'weibo-circle', |
| | | 'taobao-circle', |
| | | 'html5', |
| | | 'weibo', |
| | | 'twitter', |
| | | 'wechat', |
| | | 'youtube', |
| | | 'alipay-circle', |
| | | 'taobao', |
| | | 'skype', |
| | | 'qq', |
| | | 'medium-workmark', |
| | | 'gitlab', |
| | | 'medium', |
| | | 'linkedin', |
| | | 'google-plus', |
| | | 'dropbox', |
| | | 'facebook', |
| | | 'codepen', |
| | | 'code-sandbox', |
| | | 'amazon', |
| | | 'google', |
| | | 'codepen-circle', |
| | | 'alipay', |
| | | 'ant-design', |
| | | 'ant-cloud', |
| | | 'aliyun', |
| | | 'zhihu', |
| | | 'slack', |
| | | 'slack-square', |
| | | 'behance', |
| | | 'behance-square', |
| | | 'dribbble', |
| | | 'dribbble-square', |
| | | 'instagram', |
| | | 'yuque', |
| | | 'alibaba', |
| | | 'yahoo', |
| | | 'reddit', |
| | | 'sketch' |
| | | ], |
| | | normal: [ |
| | | 'account-book', |
| | |
| | | 'audio', |
| | | 'bank', |
| | | 'bell', |
| | | 'book', |
| | | 'bug', |
| | | 'bulb', |
| | | 'calculator', |
| | | 'build', |
| | | 'calendar', |
| | | 'camera', |
| | | 'car', |
| | | 'carry-out', |
| | | 'cloud', |
| | | 'code', |
| | | 'compass', |
| | | 'contacts', |
| | | 'container', |
| | | 'control', |
| | | 'credit-card', |
| | | 'crown', |
| | | 'customer-service', |
| | | 'dashboard', |
| | | 'database', |
| | | 'dislike', |
| | | 'environment', |
| | | 'experiment', |
| | | 'eye-invisible', |
| | | 'eye', |
| | | 'file-add', |
| | | 'file-excel', |
| | | 'file-exclamation', |
| | | 'file-image', |
| | | 'file-markdown', |
| | | 'file-pdf', |
| | | 'file-ppt', |
| | | 'file-text', |
| | | 'file-unknown', |
| | | 'file-word', |
| | | 'file-zip', |
| | | 'file', |
| | | 'filter', |
| | | 'fire', |
| | |
| | | 'folder-add', |
| | | 'folder', |
| | | 'folder-open', |
| | | 'frown', |
| | | 'funnel-plot', |
| | | 'gift', |
| | | 'hdd', |
| | | 'heart', |
| | | 'home', |
| | | 'hourglass', |
| | | 'idcard', |
| | | 'insurance', |
| | | 'interaction', |
| | | 'layout', |
| | | 'like', |
| | | 'lock', |
| | | 'mail', |
| | | 'medicine-box', |
| | | 'meh', |
| | | 'message', |
| | | 'mobile', |
| | | 'phone', |
| | |
| | | 'smile', |
| | | 'star', |
| | | 'thunderbolt', |
| | | 'trophy', |
| | | 'unlock', |
| | | 'barcode', |
| | | 'key', |