From 5ca389a3b7bfc54067547e2b8663ce413f602f3f Mon Sep 17 00:00:00 2001 From: king <18310653075@163.com> Date: 星期日, 31 一月 2021 20:41:30 +0800 Subject: [PATCH] 2021-01-31 --- src/menu/sysinterface/index.scss | 20 src/menu/sysinterface/settingform/simplescript/index.jsx | 450 ++++++++++++++++++ src/templates/zshare/editTable/index.jsx | 18 src/menu/sysinterface/settingform/baseform/index.jsx | 243 +++++++++ src/menu/sysinterface/settingform/index.jsx | 179 +++++++ src/menu/sysinterface/settingform/simplescript/index.scss | 45 + src/menu/sysinterface/settingform/index.scss | 65 ++ src/menu/sysinterface/settingform/baseform/index.scss | 22 src/tabviews/custom/index.jsx | 175 +++++++ src/menu/sysinterface/settingform/utils.jsx | 45 + src/menu/sysinterface/index.jsx | 215 ++++++++ src/templates/sharecomponent/settingcomponent/settingform/utils.jsx | 6 src/views/menudesign/index.jsx | 2 13 files changed, 1,478 insertions(+), 7 deletions(-) diff --git a/src/menu/sysinterface/index.jsx b/src/menu/sysinterface/index.jsx new file mode 100644 index 0000000..0232ce8 --- /dev/null +++ b/src/menu/sysinterface/index.jsx @@ -0,0 +1,215 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import { is, fromJS } from 'immutable' +import { Modal, Button, Icon, Popconfirm, message } from 'antd' + +import Utils from '@/utils/utils.js' +import asyncComponent from '@/utils/asyncComponent' +import './index.scss' + +const SettingForm = asyncComponent(() => import('./settingform')) +const EditTable = asyncComponent(() => import('@/templates/zshare/editTable')) + +class InterfaceController extends Component { + static propTpyes = { + config: PropTypes.object, // 椤甸潰閰嶇疆 + updateConfig: PropTypes.func // 鏇存柊 + } + + state = { + visible: false, + setvisible: false, + interfaces: [], + card: null, + columns: [ + { + title: '鎺ュ彛鍚嶇О', + dataIndex: 'name', + width: '50%' + }, + { + title: '鐘舵��', + dataIndex: 'status', + width: '20%', + render: (text, record) => record.status !== 'true' ? + ( + <div> + 绂佺敤 + <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" /> + </div> + ) : + ( + <div> + 鍚敤 + <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" /> + </div> + ) + }, + { + title: '鎿嶄綔', + align: 'center', + width: '30%', + dataIndex: 'operation', + render: (text, record) => + (<div style={{textAlign: 'center'}}> + <span onClick={() => this.handleEdit(record)} style={{color: '#1890ff', cursor: 'pointer', fontSize: '16px', marginRight: '15px'}}><Icon type="edit" /></span> + <span onClick={() => {this.copy(record)}} style={{color: '#26C281', cursor: 'pointer', fontSize: '16px', marginRight: '15px'}}><Icon type="copy" /></span> + <Popconfirm + overlayClassName="popover-confirm" + title="纭畾鍒犻櫎锛�" + onConfirm={() => this.deleteScript(record) + }> + <span style={{color: '#ff4d4f', cursor: 'pointer', fontSize: '16px'}}><Icon type="delete" /></span> + </Popconfirm> + </div>) + } + ] + } + + shouldComponentUpdate (nextProps, nextState) { + return !is(fromJS(this.state), fromJS(nextState)) + } + + copy = (item) => { + let msg = { key: 'interface', type: 'line', data: item } + + try { + msg = window.btoa(window.encodeURIComponent(JSON.stringify(msg))) + } catch { + console.warn('Stringify Failure') + msg = '' + } + + if (msg) { + let oInput = document.createElement('input') + oInput.value = msg + document.body.appendChild(oInput) + oInput.select() + document.execCommand('Copy') + document.body.removeChild(oInput) + message.success('澶嶅埗鎴愬姛銆�') + } + } + + trigger = () => { + const { config } = this.props + let interfaces = config.interfaces ? fromJS(config.interfaces).toJS() : [] + + this.setState({ + visible: true, + interfaces + }) + } + + handleEdit = (record) => { + this.setState({card: record, setvisible: true}) + } + + deleteScript = (record) => { + const { config } = this.props + let interfaces = this.state.interfaces.filter(item => item.uuid !== record.uuid) + + this.setState({ interfaces }) + this.props.updateConfig({...config, interfaces}) + } + + changeScripts = (interfaces) => { + const { config } = this.props + + this.setState({ interfaces }) + this.props.updateConfig({...config, interfaces}) + } + + settingSave = () => { + const { config } = this.props + const { card } = this.state + let interfaces = fromJS(this.state.interfaces).toJS() + + this.settingRef.handleConfirm().then(res => { + interfaces = interfaces.map(item => { + if (item.uuid === card.uuid) { + res.uuid = item.uuid + + if (res.procMode !== 'inner' && res.preScripts && res.preScripts.filter(item => item.status !== 'false').length === 0) { + message.warning('鏈缃墠缃剼鏈紝涓嶅彲鍚敤锛�') + res.status = 'false' + } else if (res.callbackType === 'script' && res.cbScripts && res.cbScripts.filter(item => item.status !== 'false').length === 0) { + message.warning('鏈缃洖璋冭剼鏈紝涓嶅彲鍚敤锛�') + res.status = 'false' + } + + return res + } + return item + }) + + this.setState({ + card: null, + setvisible: false, + interfaces + }) + + this.props.updateConfig({...config, interfaces}) + }) + } + + addInterface = () => { + const { config } = this.props + let interfaces = fromJS(this.state.interfaces).toJS() + + interfaces.push({ + uuid: Utils.getuuid(), + name: 'interface ' + (interfaces.length + 1), + procMode: 'script', + callbackType: 'script', + preScripts: [], + cbScripts: [] + }) + + this.setState({ + interfaces + }) + this.props.updateConfig({...config, interfaces}) + } + + render() { + const { visible, setvisible, columns, interfaces, card } = this.state + + return ( + <div style={{display: 'inline-block'}}> + <Button className="mk-border-green" icon="api" onClick={this.trigger}>鎺ュ彛绠$悊</Button> + <Modal + title="鎺ュ彛绠$悊" + wrapClassName="interface-controller-modal" + visible={visible} + width={800} + maskClosable={false} + onCancel={() => {this.setState({visible: false})}} + footer={[ + <Button key="colse" onClick={() => {this.setState({visible: false})}}> + 鍏抽棴 + </Button> + ]} + destroyOnClose + > + <Button key="add-interface" className="mk-border-green" onClick={this.addInterface}> 娣诲姞 </Button> + <EditTable key="manage-interface" actions={['move', 'copy']} type="interface" data={interfaces} columns={columns} onChange={this.changeScripts}/> + </Modal> + <Modal + title={card ? card.name : '鎺ュ彛'} + wrapClassName="interface-edit-modal" + visible={setvisible} + width={900} + maskClosable={false} + onOk={this.settingSave} + onCancel={() => { this.setState({ setvisible: false })}} + destroyOnClose + > + <SettingForm config={card} wrappedComponentRef={(inst) => this.settingRef = inst}/> + </Modal> + </div> + ) + } +} + +export default InterfaceController \ No newline at end of file diff --git a/src/menu/sysinterface/index.scss b/src/menu/sysinterface/index.scss new file mode 100644 index 0000000..c4d74b5 --- /dev/null +++ b/src/menu/sysinterface/index.scss @@ -0,0 +1,20 @@ +.interface-controller-modal { + >.ant-modal >.ant-modal-content >.ant-modal-body { + min-height: 400px; + >.mk-border-green { + float: right; + position: relative; + z-index: 1; + margin-bottom: 10px; + } + } +} +.interface-edit-modal { + .ant-modal { + top: 70px; + } + .ant-modal-body { + min-height: 300px; + padding-top: 5px; + } +} \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/baseform/index.jsx b/src/menu/sysinterface/settingform/baseform/index.jsx new file mode 100644 index 0000000..1c4c9b8 --- /dev/null +++ b/src/menu/sysinterface/settingform/baseform/index.jsx @@ -0,0 +1,243 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import { Form, Row, Col, Input, Radio, Tooltip, Icon } from 'antd' + +import { formRule } from '@/utils/option.js' +import './index.scss' + +const { TextArea } = Input + +class SettingForm extends Component { + static propTpyes = { + dict: PropTypes.object, // 瀛楀吀椤� + setting: PropTypes.object, // 鏁版嵁婧愰厤缃� + updateStatus: PropTypes.func, // 鐘舵�佹洿鏂� + } + + state = { + procMode: 'script', + funcTooltip: '', + funcRules: [] + } + + UNSAFE_componentWillMount () { + const { setting } = this.props + + let usefulFields = sessionStorage.getItem('permFuncField') + if (usefulFields) { + try { + usefulFields = JSON.parse(usefulFields) + } catch { + usefulFields = [] + } + } else { + usefulFields = [] + } + + let tooltip = null + let rules = [] + + if (usefulFields.length > 0) { + tooltip = '寮�澶村彲鐢ㄥ瓧绗︼細' + usefulFields.join(', ') + let str = '^(' + usefulFields.join('|') + ')' + let _patten = new RegExp(str + formRule.func.innerPattern + '$', 'g') + + rules.push({ + pattern: _patten, + message: formRule.func.innerMessage + }) + } + + this.setState({ + procMode: setting.procMode || 'script', + funcTooltip: tooltip, + funcRules: rules + }) + } + + handleConfirm = () => { + // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭� + return new Promise((resolve, reject) => { + this.props.form.validateFieldsAndScroll((err, values) => { + if (!err) { + resolve(values) + } else { + reject(err) + } + }) + }) + } + + onRadioChange = (e, key) => { + let value = e.target.value + + if (key === 'procMode') { + this.setState({ + procMode: value + }) + } + this.props.updateStatus({[key]: value}) + } + + render() { + const { setting, dict } = this.props + const { getFieldDecorator } = this.props.form + const { funcRules, funcTooltip, procMode } = this.state + + const formItemLayout = { + labelCol: { + xs: { span: 24 }, + sm: { span: 8 } + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 } + } + } + + return ( + <div className="model-table-datasource-setting-form-box"> + <Form {...formItemLayout} className="model-setting-form"> + <Row gutter={24}> + <Col span={12}> + <Form.Item label="鎺ュ彛鍚�"> + {getFieldDecorator('name', { + initialValue: setting.name || '', + rules: [ + { + required: true, + message: dict['form.required.input'] + '鎺ュ彛鍚�!' + }, + ] + })(<Input placeholder={''} autoComplete="off" />)} + </Form.Item> + </Col> + <Col span={12}> + <Form.Item label="鐘舵��"> + {getFieldDecorator('status', { + initialValue: setting.status || 'true' + })( + <Radio.Group> + <Radio value="true">鍚敤</Radio> + <Radio value="false">绂佺敤</Radio> + </Radio.Group>)} + </Form.Item> + </Col> + <Col span={12}> + <Form.Item label="鍙傛暟澶勭悊"> + {getFieldDecorator('procMode', { + initialValue: procMode, + rules: [ + { + required: true, + message: dict['form.required.select'] + '鍙傛暟澶勭悊鏂瑰紡!' + }, + ] + })( + <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onRadioChange(e, 'procMode')}}> + <Radio value="script">鍓嶇疆鑴氭湰</Radio> + <Radio value="inner">鍓嶇疆鍑芥暟</Radio> + </Radio.Group>)} + </Form.Item> + </Col> + {procMode === 'inner' ? <Col span={12}> + <Form.Item label={ + <Tooltip placement="topLeft" title={funcTooltip}> + <Icon type="question-circle" /> + 鍓嶇疆鍑芥暟 + </Tooltip> + }> + {getFieldDecorator('prevFunc', { + initialValue: setting.prevFunc || '', + rules: [ + { + required: true, + message: dict['form.required.input'] + '鍓嶇疆鍑芥暟!' + }, + { + max: formRule.func.max, + message: formRule.func.maxMessage + }, + ...funcRules + ] + })(<Input placeholder={''} autoComplete="off" />)} + </Form.Item> + </Col> : null} + <Col className="data-source" span={24}> + <Form.Item label="娴嬭瘯鍦板潃"> + {getFieldDecorator('interface', { + initialValue: setting.interface || '', + rules: [ + { + required: true, + message: dict['form.required.input'] + '娴嬭瘯鍦板潃!' + }, + ] + })(<TextArea rows={2} />)} + </Form.Item> + </Col> + <Col className="data-source" span={24}> + <Form.Item label={ + <Tooltip placement="topLeft" title="姝e紡绯荤粺鎵�浣跨敤鐨勭殑鎺ュ彛鍦板潃銆�"> + <Icon type="question-circle" /> + 姝e紡鍦板潃 + </Tooltip> + }> + {getFieldDecorator('proInterface', { + initialValue: setting.proInterface || '' + })(<TextArea rows={2} />)} + </Form.Item> + </Col> + <Col span={12}> + <Form.Item label="璇锋眰鏂瑰紡"> + {getFieldDecorator('method', { + initialValue: setting.method || 'post', + rules: [ + { + required: true, + message: dict['form.required.select'] + '璇锋眰鏂瑰紡!' + }, + ] + })( + <Radio.Group> + <Radio value="get">GET</Radio> + <Radio value="post">POST</Radio> + </Radio.Group>)} + </Form.Item> + </Col> + <Col span={12}> + <Form.Item label="鍥炶皟鏂瑰紡"> + {getFieldDecorator('callbackType', { + initialValue: setting.callbackType || 'script' + })( + <Radio.Group onChange={(e) => {this.onRadioChange(e, 'callbackType')}}> + <Radio value="default">榛樿鑴氭湰</Radio> + <Radio value="script">鑷畾涔夎剼鏈�</Radio> + </Radio.Group>)} + </Form.Item> + </Col> + <Col span={12}> + <Form.Item label="鍥炶皟琛ㄥ悕"> + {getFieldDecorator('cbTable', { + initialValue: setting.cbTable || '', + rules: [ + { + required: true, + message: dict['form.required.input'] + '鍥炶皟琛ㄥ悕!' + }, + { + max: formRule.input.max, + message: formRule.input.message + } + ] + })(<Input placeholder={''} autoComplete="off" />)} + </Form.Item> + </Col> + </Row> + </Form> + </div> + ) + } +} + +export default Form.create()(SettingForm) \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/baseform/index.scss b/src/menu/sysinterface/settingform/baseform/index.scss new file mode 100644 index 0000000..a6d2df7 --- /dev/null +++ b/src/menu/sysinterface/settingform/baseform/index.scss @@ -0,0 +1,22 @@ +.model-table-datasource-setting-form-box { + position: relative; + + .model-setting-form { + .data-source { + .ant-form-item-label { + width: 16.5%; + } + .ant-form-item-control-wrapper { + width: 83.5%; + } + .CodeMirror { + height: 150px; + } + } + .anticon-question-circle { + color: #c49f47; + margin-right: 3px; + } + } + +} \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/index.jsx b/src/menu/sysinterface/settingform/index.jsx new file mode 100644 index 0000000..e2682cd --- /dev/null +++ b/src/menu/sysinterface/settingform/index.jsx @@ -0,0 +1,179 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import { fromJS } from 'immutable' +import { Form, notification, Tabs } from 'antd' + +import asyncComponent from '@/utils/asyncComponent' +import BaseForm from './baseform' +import zhCN from '@/locales/zh-CN/model.js' +import enUS from '@/locales/en-US/model.js' +import './index.scss' + +const { TabPane } = Tabs +const SimpleScript = asyncComponent(() => import('./simplescript')) + +class SettingForm extends Component { + static propTpyes = { + config: PropTypes.object, // 椤甸潰閰嶇疆淇℃伅 + } + + state = { + dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, + formlist: [], + btnloading: false, + activeKey: 'setting', + setting: null, + defaultSql: '', + status: {} + } + + UNSAFE_componentWillMount() { + const { config } = this.props + + let _setting = fromJS(config).toJS() + let _preScripts = _setting.preScripts || [] + let _cbScripts = _setting.cbScripts || [] + + this.setState({ + setting: _setting, + preScripts: _preScripts, + cbScripts: _cbScripts, + status: fromJS(_setting).toJS() + }) + } + + + handleConfirm = () => { + const { activeKey, setting, preScripts, cbScripts } = this.state + + let _loading = false + if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) { + _loading = true + } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) { + _loading = true + } + + if (_loading) { + notification.warning({ + top: 92, + message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒', + duration: 5 + }) + return Promise.reject() + } + + // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭� + if (activeKey === 'setting') { + return new Promise((resolve, reject) => { + this.settingForm.handleConfirm().then(res => { + resolve({...res, preScripts, cbScripts}) + }, () => { + reject() + }) + }) + } else { + return new Promise((resolve) => { + resolve({...setting, preScripts, cbScripts}) + }) + } + } + + // 鏍囩鍒囨崲 + changeTab = (val) => { + const { activeKey } = this.state + + let _loading = false + if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) { + _loading = true + } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) { + _loading = true + } + + if (_loading) { + notification.warning({ + top: 92, + message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒', + duration: 5 + }) + return + } + + if (activeKey === 'setting') { + this.settingForm.handleConfirm().then(res => { + this.setState({ + setting: res, + activeKey: val + }) + }) + } else { + this.setState({ + activeKey: val + }) + } + } + + // 鍓嶇疆鑴氭湰鏇存柊 + preScriptsUpdate = (preScripts) => { + this.setState({preScripts}) + } + + // 鍚庣疆鑴氭湰鏇存柊 + cbScriptsUpdate = (cbScripts) => { + this.setState({cbScripts}) + } + + updateStatus = (status) => { + this.setState({status: {...this.state.status, ...status}}) + } + + render() { + const { dict, activeKey, setting, preScripts, cbScripts, status } = this.state + + return ( + <div className="model-interface-form-box" id="model-interface-form-body"> + <Tabs activeKey={activeKey} className="verify-card-box" onChange={this.changeTab}> + <TabPane tab="鏁版嵁婧�" key="setting"> + <BaseForm + dict={dict} + setting={setting} + updateStatus={this.updateStatus} + wrappedComponentRef={(inst) => this.settingForm = inst} + /> + </TabPane> + <TabPane tab={ + <span> + 鍓嶇疆鑴氭湰 + {preScripts.length ? <span className="count-tip">{preScripts.length}</span> : null} + </span> + } disabled={status.procMode !== 'script'} key="prescripts"> + <SimpleScript + dict={dict} + type="front" + setting={setting} + scripts={preScripts} + scriptsUpdate={this.preScriptsUpdate} + wrappedComponentRef={(inst) => this.preScriptsForm = inst} + /> + </TabPane> + <TabPane tab={ + <span> + 鍥炶皟鑴氭湰 + {cbScripts.length ? <span className="count-tip">{cbScripts.length}</span> : null} + </span> + } disabled={status.callbackType !== 'script'} key="cbscripts"> + <SimpleScript + dict={dict} + type="back" + setting={setting} + scripts={cbScripts} + scriptsUpdate={this.cbScriptsUpdate} + wrappedComponentRef={(inst) => this.cbScriptsForm = inst} + /> + </TabPane> + </Tabs> + </div> + ) + } +} + +export default Form.create()(SettingForm) \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/index.scss b/src/menu/sysinterface/settingform/index.scss new file mode 100644 index 0000000..d4d8d9d --- /dev/null +++ b/src/menu/sysinterface/settingform/index.scss @@ -0,0 +1,65 @@ +.model-interface-form-box { + position: relative; + + >.ant-spin { + position: absolute; + top: 150px; + left: calc(50% - 16px); + } + .count-tip { + position: absolute; + top: 0px; + color: #1890ff; + font-size: 12px; + } + .model-table-setting-form { + .textarea { + .ant-form-item-label { + width: 16.3%; + } + .ant-form-item-control-wrapper { + width: 83.33333333%; + } + } + .anticon-question-circle { + color: #c49f47; + margin-right: 3px; + } + .text-area { + .CodeMirror { + height: 150px; + } + } + } + .operation-btn { + display: inline-block; + font-size: 16px; + padding: 0 5px; + cursor: pointer; + } + td { + word-break: break-all; + } + .setting-custom-back { + position: absolute; + top: -20px; + left: -10px; + font-size: 16px; + z-index: 1; + cursor: pointer; + padding: 10px; + color: rgb(24, 144, 255); + } + .to-custom-script { + float: right; + color: #1890ff; + margin-right: 12px; + margin-top: 15px; + cursor: pointer; + border: 0; + box-shadow: unset; + } + .ant-tabs-nav-wrap { + text-align: center; + } +} \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/simplescript/index.jsx b/src/menu/sysinterface/settingform/simplescript/index.jsx new file mode 100644 index 0000000..0617137 --- /dev/null +++ b/src/menu/sysinterface/settingform/simplescript/index.jsx @@ -0,0 +1,450 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import { fromJS } from 'immutable' +import { Form, Row, Col, Icon, Button, notification, Select, Popconfirm, Typography, Modal, Radio } from 'antd' +import moment from 'moment' + +import Utils from '@/utils/utils.js' +import Api from '@/api' +import SettingUtils from '../utils' +import CodeMirror from '@/templates/zshare/codemirror' +import asyncComponent from '@/utils/asyncComponent' +import './index.scss' + +const { Paragraph } = Typography +const EditTable = asyncComponent(() => import('@/templates/zshare/editTable')) + +class CustomForm extends Component { + static propTpyes = { + dict: PropTypes.object, // 瀛楀吀椤� + type: PropTypes.string, // 鑴氭湰绫诲瀷 + setting: PropTypes.object, // 璁剧疆 + scripts: PropTypes.array, // 鑷畾涔夎剼鏈垪琛� + scriptsChange: PropTypes.func, // 鑷畾涔夎剼鏈垏鎹㈡椂楠岃瘉 + scriptsUpdate: PropTypes.func // 琛ㄥ崟 + } + + state = { + editItem: null, + loading: false, + systemScripts: [], + scriptsColumns: [ + { + title: 'SQL', + dataIndex: 'sql', + width: '60%', + render: (text) => { + let title = text.match(/^\s*\/\*.+\*\//) + title = title && title[0] ? title[0] : '' + text = title ? text.replace(title, '') : text + + return ( + <div> + {title ? <span style={{color: '#a50'}}>{title}</span> : null} + <Paragraph copyable ellipsis={{ rows: 4, expandable: true }}>{text}</Paragraph> + </div> + ) + } + }, + { + title: '鎵ц浣嶇疆', + dataIndex: 'position', + width: '13%', + render: (text, record) => { + if (record.position === 'front') { + return 'sql鍓�' + } else { + return 'sql鍚�' + } + } + }, + { + title: '鐘舵��', + dataIndex: 'status', + width: '12%', + render: (text, record) => record.status === 'false' ? + ( + <div> + {this.props.dict['model.status.forbidden']} + <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" /> + </div> + ) : + ( + <div> + {this.props.dict['model.status.open']} + <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" /> + </div> + ) + }, + { + title: '鎿嶄綔', + align: 'center', + width: '15%', + dataIndex: 'operation', + render: (text, record) => + (<div style={{textAlign: 'center'}}> + <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record)} style={{color: '#1890ff'}}><Icon type="edit" /></span> + <span className="operation-btn" title={this.props.dict['header.form.status.change']} onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><Icon type="swap" /></span> + <Popconfirm + overlayClassName="popover-confirm" + title={this.props.dict['model.query.delete']} + onConfirm={() => this.handleDelete(record) + }> + <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span> + </Popconfirm> + </div>) + } + ] + } + + UNSAFE_componentWillMount() { + const { scripts } = this.props + + let scriptsColumns = fromJS(this.state.scriptsColumns).toJS() + + this.setState({ + scripts: fromJS(scripts).toJS(), + scriptsColumns + }) + } + + componentDidMount () { + this.getsysScript() + } + + getsysScript = () => { + let _scriptSql = `Select distinct func+Remark as funcname,longparam, s.Sort from聽 s_custom_script s inner join (select OpenID from sapp where ID=@Appkey@) p on s.openid = case when s.appkey='' then s.openid else p.OpenID end order by s.Sort` + + _scriptSql = Utils.formatOptions(_scriptSql) + + let _sParam = { + func: 'sPC_Get_SelectedList', + LText: _scriptSql, + obj_name: 'data', + arr_field: 'funcname,longparam' + } + + _sParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + _sParam.secretkey = Utils.encrypt(_sParam.LText, _sParam.timestamp) + _sParam.open_key = Utils.encryptOpenKey(_sParam.secretkey, _sParam.timestamp) // 浜戠鏁版嵁楠岃瘉 + + Api.getSystemConfig(_sParam).then(res => { + if (res.status) { + let _scripts = res.data.map(item => { + let _item = { + name: item.funcname, + value: window.decodeURIComponent(window.atob(item.longparam)) + } + return _item + }) + + this.setState({ + systemScripts: _scripts + }) + } else { + notification.warning({ + top: 92, + message: res.message, + duration: 5 + }) + } + }) + } + + handleCancel = () => { + this.setState({ + editItem: null + }) + this.props.form.setFieldsValue({ + sql: '' + }) + } + + handleConfirm = () => { + const { scripts, editItem } = this.state + + let _sql = this.props.form.getFieldValue('sql') + + if (!_sql) { + notification.warning({ + top: 92, + message: '璇峰~鍐欒嚜瀹氫箟鑴氭湰锛�', + duration: 5 + }) + return + } else if (/^\s+$/.test(_sql)) { + notification.warning({ + top: 92, + message: '鑷畾涔夎剼鏈笉鍙负绌猴紒', + duration: 5 + }) + return + } + + let values = { + uuid: editItem && editItem.uuid ? editItem.uuid : Utils.getuuid(), + sql: _sql, + } + + if (this.props.form.getFieldValue('position')) { + values.position = this.props.form.getFieldValue('position') + } + + let _quot = values.sql.match(/'{1}/g) + let _lparen = values.sql.match(/\({1}/g) + let _rparen = values.sql.match(/\){1}/g) + + _quot = _quot ? _quot.length : 0 + _lparen = _lparen ? _lparen.length : 0 + _rparen = _rparen ? _rparen.length : 0 + + if (_quot % 2 !== 0) { + notification.warning({ + top: 92, + message: 'sql涓璡'蹇呴』鎴愬鍑虹幇', + duration: 5 + }) + return + } else if (_lparen !== _rparen) { + notification.warning({ + top: 92, + message: 'sql涓�()蹇呴』鎴愬鍑虹幇', + duration: 5 + }) + return + } else if (/--/ig.test(values.sql)) { + notification.warning({ + top: 92, + message: '鑷畾涔塻ql璇彞涓紝涓嶅彲鍑虹幇瀛楃 -- 锛屾敞閲婅鐢� /*鍐呭*/', + duration: 5 + }) + return + } + + let error = Utils.verifySql(values.sql, 'customscript') + + if (error) { + notification.warning({ + top: 92, + message: 'sql涓笉鍙娇鐢�' + error, + duration: 5 + }) + return + } + + let _scripts = fromJS(scripts).toJS() + + if (editItem && editItem.uuid) { + _scripts = _scripts.map(item => { + if (item.uuid === values.uuid) { + return values + } else { + return item + } + }) + } else { + _scripts.push(values) + } + + let param = { + func: 's_debug_sql', + exec_type: 'y', + LText: SettingUtils.getCustomDebugSql(_scripts) + } + param.LText = Utils.formatOptions(param.LText) + param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + param.secretkey = Utils.encrypt('', param.timestamp) + + this.setState({loading: true}) + Api.getLocalConfig(param).then(result => { + if (result.status) { + this.setState({ + loading: false, + scripts: _scripts, + editItem: null + }) + + this.props.scriptsUpdate(_scripts) + this.props.form.setFieldsValue({ + sql: '' + }) + } else { + this.setState({loading: false}) + Modal.error({ + title: result.message + }) + } + }) + } + + selectScript = (value, option) => { + if (!value || !option) return + let _sql = this.props.form.getFieldValue('sql') + if (_sql) { + _sql = _sql + ` + + ` + } + + _sql = _sql.replace(/\s{6}$/, '') + _sql = _sql + `/*${option.props.children}*/ + ` + _sql = _sql.replace(/\s{4}$/, '') + _sql = _sql + value + + this.props.form.setFieldsValue({ + sql: _sql + }) + } + + handleEdit = (record) => { + const { type } = this.props + this.setState({ + editItem: record + }) + + if (type === 'front') { + this.props.form.setFieldsValue({ + sql: record.sql + }) + } else { + this.props.form.setFieldsValue({ + sql: record.sql, + position: record.position || 'back' + }) + } + + this.scrolltop() + } + + scrolltop = () => { + let node = document.getElementById('model-interface-form-body').parentNode + + if (node && node.scrollTop) { + let inter = Math.ceil(node.scrollTop / 10) + + let timer = setInterval(() => { + if (node.scrollTop - inter > 0) { + node.scrollTop = node.scrollTop - inter + } else { + node.scrollTop = 0 + clearInterval(timer) + } + }, 10) + } + } + + changeScripts = (scripts) => { + this.setState({scripts}) + this.props.scriptsUpdate(scripts) + } + + handleStatus = (record) => { + let scripts = fromJS(this.state.scripts).toJS() + record.status = record.status === 'false' ? 'true' : 'false' + + scripts = scripts.map(item => { + if (item.uuid === record.uuid) { + return record + } else { + return item + } + }) + + this.setState({scripts}) + this.props.scriptsUpdate(scripts) + } + + handleDelete = (record) => { + let scripts = fromJS(this.state.scripts).toJS() + scripts = scripts.filter(item => item.uuid !== record.uuid) + + this.setState({ scripts }) + this.props.scriptsUpdate(scripts) + } + + render() { + const { setting, scripts, type } = this.props + const { getFieldDecorator } = this.props.form + const { scriptsColumns, systemScripts } = this.state + const formItemLayout = { + labelCol: { + xs: { span: 24 }, + sm: { span: 8 } + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 } + } + } + + return ( + <div className="modal-menu-setting-script"> + <Form {...formItemLayout}> + <Row gutter={24}> + <Col span={8}> + <Form.Item label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}> + {setting.cbTable} + </Form.Item> + </Col> + <Col span={16}> + <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}> + ErrorCode, retmsg + </Form.Item> + </Col> + <Col span={24} className="sqlfield"> + <Form.Item label={'鍙敤瀛楁'}> + bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id + </Form.Item> + </Col> + {type === 'back' ? <Col span={8} style={{whiteSpace: 'nowrap'}}> + <Form.Item style={{marginBottom: 0}} label="鎵ц浣嶇疆"> + {getFieldDecorator('position', { + initialValue: 'front' + })( + <Radio.Group> + <Radio value="front">sql鍓�</Radio> + <Radio value="back">sql鍚�</Radio> + </Radio.Group> + )} + </Form.Item> + </Col> : null} + <Col span={10} className="quick-add"> + <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}> + <Select + allowClear + showSearch + filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} + onChange={this.selectScript} + > + {type === 'back' ? <Select.Option key="default" value={`declare @${setting.cbTable} table (mk_api_key nvarchar(100),mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))\n/*@${setting.cbTable}_data table (mk_level nvarchar(10),mk_id nvarchar(50),mk_bid nvarchar(50))*/`}>榛樿sql</Select.Option> : null} + {systemScripts.map((option, i) => + <Select.Option style={{whiteSpace: 'normal'}} key={i} value={option.value}>{option.name}</Select.Option> + )} + </Select> + </Form.Item> + </Col> + <Col span={6} className="add"> + <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginTop: 5, marginBottom: 15, marginLeft: 30}}> + 淇濆瓨 + </Button> + <Button onClick={this.handleCancel} style={{marginTop: 5, marginBottom: 15, marginLeft: 10}}> + 鍙栨秷 + </Button> + </Col> + <Col span={24} className="sql"> + <Form.Item label={'sql'}> + {getFieldDecorator('sql', { + initialValue: '' + })(<CodeMirror />)} + </Form.Item> + </Col> + </Row> + </Form> + <EditTable data={scripts} actions={['move']} columns={scriptsColumns} onChange={this.changeScripts}/> + </div> + ) + } +} + +export default Form.create()(CustomForm) \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/simplescript/index.scss b/src/menu/sysinterface/settingform/simplescript/index.scss new file mode 100644 index 0000000..945809b --- /dev/null +++ b/src/menu/sysinterface/settingform/simplescript/index.scss @@ -0,0 +1,45 @@ +.modal-menu-setting-script { + .sqlfield { + .ant-form-item { + margin-bottom: 5px; + } + .ant-form-item-control { + line-height: 24px; + } + .ant-form-item-label { + line-height: 25px; + } + .ant-form-item-children { + line-height: 22px; + } + .ant-col-sm-8 { + width: 10.5%; + } + .ant-col-sm-16 { + width: 89.5%; + } + } + .quick-add { + .ant-col-sm-8 { + width: 26%; + } + .ant-col-sm-16 { + width: 74%; + } + } + .sql { + .ant-col-sm-8 { + width: 10.5%; + } + .ant-col-sm-16 { + width: 89.5%; + padding-top: 4px; + } + .CodeMirror { + height: 350px; + } + } + div.ant-typography { + margin-bottom: 0; + } +} \ No newline at end of file diff --git a/src/menu/sysinterface/settingform/utils.jsx b/src/menu/sysinterface/settingform/utils.jsx new file mode 100644 index 0000000..2c7ddce --- /dev/null +++ b/src/menu/sysinterface/settingform/utils.jsx @@ -0,0 +1,45 @@ + +export default class SettingUtils { + /** + * @description 鐢熸垚鍓嶇疆鎴栧悗缃鍙� + * @return {String} scripts 鑴氭湰 + */ + static getCustomDebugSql (scripts) { + let sql = '' + let _customScript = '' + + scripts.forEach(script => { + if (script.status === 'false') return + + _customScript += ` + ${script.sql} + ` + }) + + if (_customScript) { + _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg ='' + ${_customScript} + ` + } + + _customScript = _customScript.replace(/@\$|\$@/ig, '') + _customScript = _customScript.replace(/@userName@|@fullName@/ig, `''`) + // 澶栬仈鏁版嵁搴撴浛鎹� + if (window.GLOB.externalDatabase !== null) { + _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase) + } + + if (_customScript) { + sql = `/* sql 楠岃瘉 */ + ${_customScript} + aaa: + if @ErrorCode!='' + insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@ + ` + } + sql = sql.replace(/\n\s{8}/ig, '\n') + console.info(sql) + + return sql + } +} \ No newline at end of file diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx index c772dd4..98e030c 100644 --- a/src/tabviews/custom/index.jsx +++ b/src/tabviews/custom/index.jsx @@ -11,6 +11,7 @@ import zhCN from '@/locales/zh-CN/main.js' import enUS from '@/locales/en-US/main.js' import Utils from '@/utils/utils.js' +import UtilsDM from '@/utils/utils-datamanage.js' import asyncComponent from '@/utils/asyncComponent' import MKEmitter from '@/utils/events.js' import NotFount from '@/components/404' @@ -180,6 +181,8 @@ if (!this.props.Tab) { this.setShortcut() } + + this.loadData() }) } else { this.setState({ @@ -229,6 +232,178 @@ } } + loadData = () => { + const { config } = this.state + + if (!config.interfaces || config.interfaces.length === 0) return + + let inters = [] + + config.interfaces.forEach(item => { + if (item.status !== 'true') return + + if (window.GLOB.systemType === 'production' && !item.proInterface) { + notification.warning({ + top: 92, + message: `銆�${item.name}銆嬫湭璁剧疆姝e紡绯荤粺鍦板潃!`, + duration: 3 + }) + return + } + + inters.push(item) + }) + + if (inters.length > 0) { + this.loadOutResource(inters) + } + } + + loadOutResource = (params) => { + let setting = params.shift() + let param = UtilsDM.getPrevQueryParams(setting, [], this.state.BID, this.props.menuType) + + Api.genericInterface(param).then(res => { + if (res.status) { + if (res.mk_ex_invoke === 'false') { + if (params.length > 0) { + this.loadOutResource(params) + } + } else { + this.customOuterRequest(res, setting, params) + } + } else { + notification.error({ + top: 92, + message: res.message, + duration: 10 + }) + } + }) + } + + customOuterRequest = (result, setting, params) => { + let url = '' + + if (window.GLOB.systemType === 'production') { + url = setting.proInterface + } else { + url = setting.interface + } + + let mkey = result.mk_api_key || '' + + delete result.mk_ex_invoke + delete result.status + delete result.message + delete result.ErrCode + delete result.ErrMesg + delete result.mk_api_key + + let param = {} + + Object.keys(result).forEach(key => { + key = key.replace(/^mk_/ig, '') + param[key] = result[key] + }) + + Api.directRequest(url, setting.method, param).then(res => { + if (typeof(res) !== 'object' || Array.isArray(res)) { + let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒' + + if (typeof(res) === 'string') { + error = res.replace(/'/ig, '"') + } + + let _result = { + mk_api_key: mkey, + $ErrCode: 'E', + $ErrMesg: error + } + + this.customCallbackRequest(_result, setting, params) + } else { + res.mk_api_key = mkey + this.customCallbackRequest(res, setting, params) + } + }, (e) => { + let _result = { + mk_api_key: mkey, + $ErrCode: 'E', + $ErrMesg: e && e.statusText ? e.statusText : '' + } + + this.customCallbackRequest(_result, setting, params) + }) + } + + customCallbackRequest = (result, setting, params) => { + let errSql = '' + if (result.$ErrCode === 'E') { + errSql = ` + set @ErrorCode='E' + set @retmsg='${result.$ErrMesg}' + ` + delete result.$ErrCode + delete result.$ErrMesg + } + + let lines = UtilsDM.getCallBackSql(setting, result) + let param = {} + + if (setting.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈� + let sql = lines.map(item => (` + ${item.insert} + ${item.selects.join(` union all + `)} + `)) + sql = sql.join('') + + param = UtilsDM.getCallBackQueryParams(setting, sql, errSql) + + if (this.state.BID) { + param.BID = this.state.BID + } + + if (this.props.menuType === 'HS') { // 鍑芥暟 sPC_TableData_InUpDe 浜戠楠岃瘉 + param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp) + } + } else { + param.func = 's_ex_result_back' + param.s_ex_result = lines.map((item, index) => ({ + MenuID: this.props.MenuID, + MenuName: this.props.MenuName, + TableName: item.table, + LongText: window.btoa(window.encodeURIComponent(`${item.insert} ${item.selects.join(` union all `)}`)), + Sort: index + 1 + })) + + if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) { + let sql = lines.map(item => (` + ${item.insert} + ${item.selects.join(` union all + `)} + `)) + sql = sql.join('') + console.info(sql.replace(/\n\s{10}/ig, '\n')) + } + } + + Api.genericInterface(param).then(res => { + if (res.status) { + if (params.length > 0) { + this.loadOutResource(params) + } + } else { + notification.error({ + top: 92, + message: res.message, + duration: 10 + }) + } + }) + } + filterComponent = (components, roleId, permAction, permMenus) => { return components.filter(item => { if (item.type === 'tabs') { diff --git a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx index a32d2b9..09f0df4 100644 --- a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx +++ b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx @@ -117,12 +117,8 @@ ` }) - if (_customScript && regoptions) { + if (_customScript) { _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg ='' - ${_customScript} - ` - } else if (_customScript) { - _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg ='' ${_customScript} ` } diff --git a/src/templates/zshare/editTable/index.jsx b/src/templates/zshare/editTable/index.jsx index feb862e..f78cdb1 100644 --- a/src/templates/zshare/editTable/index.jsx +++ b/src/templates/zshare/editTable/index.jsx @@ -172,7 +172,15 @@ let columns = fromJS(this.props.columns).toJS() if (actions && (actions.includes('edit') || actions.includes('copy') || actions.includes('del'))) { - columns.push({ + let _operation = null + columns = columns.filter(item => { + if (item.dataIndex === 'operation') { + _operation = item + } + return item.dataIndex !== 'operation' + }) + + let operation = { title: (<div> {eTDict['model.operation']} {actions.includes('copy') ? ( @@ -213,7 +221,13 @@ </div> ) } - }) + } + + if (_operation) { + operation.render = _operation.render + operation.width = _operation.width + } + columns.push(operation) } this.setState({ diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx index 5d09c45..c79828e 100644 --- a/src/views/menudesign/index.jsx +++ b/src/views/menudesign/index.jsx @@ -36,6 +36,7 @@ const PasteController = asyncComponent(() => import('@/menu/pastecontroller')) const PaddingController = asyncComponent(() => import('@/menu/padcontroller')) const StyleController = asyncComponent(() => import('@/menu/stylecontroller')) +const SysInterface = asyncComponent(() => import('@/menu/sysinterface')) const PictureController = asyncComponent(() => import('@/menu/picturecontroller')) const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller')) const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller')) @@ -953,6 +954,7 @@ <div> {config && config.MenuName} </div> } bordered={false} extra={ <div> + <SysInterface config={config} updateConfig={this.updateConfig}/> <PictureController/> <StyleCombControlButton menu={config} /> <PasteController type="menu" Tab={null} insert={this.insert} /> -- Gitblit v1.8.0