Created
August 1, 2019 10:18
-
-
Save daniilgrigoryev/e11bc9302a20507d612c04548a08e7ac to your computer and use it in GitHub Desktop.
Из ребенка в родитель
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, {Component} from 'react'; | |
| import {compose} from 'redux' | |
| import {FieldArray, reduxForm} from 'redux-form/immutable' | |
| import AppealTable from './table.js' | |
| import {getSessionId} from '../../selectors/common.js' | |
| import {connect} from 'react-redux' | |
| import {Button, Input, Card, Layout} from 'element-react' | |
| import {post} from '../../services/ajax.js' | |
| import {appealLoad} from '../../actions/common.js' | |
| import Immutable from 'immutable' | |
| import {relocate} from '../app/app.js' | |
| import {EInput} from '../components/finput.js' | |
| import {ESelect} from '../components/select.js' | |
| import {EAutocomplete} from '../components/fautocomplete.js' | |
| import {EPicker} from '../components/picker.js' | |
| import {SearchRoot} from '../components/searchRoot.js' | |
| import map from '../../markup/appeal/mapping.js' | |
| import {baseUrl} from '../../services/api.js' | |
| import * as _ from 'lodash' | |
| import {messageSet} from '../../actions/common.js' | |
| import { timingSafeEqual } from 'crypto'; | |
| const MS = map.status; | |
| const MB = map.basicData; | |
| const MC = map.claimantData; | |
| const timeOfs = new Date().getTimezoneOffset() * 60 * 1000; | |
| const im = (obj) => Immutable.fromJS(obj) | |
| const desc = { | |
| info_alias: 'i_obr', | |
| alias: null // 'APPEAL_LIST' or 'APPEAL_LIST_MY' | |
| } | |
| const style = {textAlign: 'center', width: '8em'}; | |
| const mapping = { | |
| REG_NUM: 'Регистрационный номер', | |
| DATE_REG: 'Дата регистрации', | |
| //DATE_CONTROL:'Дата контроля', | |
| NAME: 'Заявитель', | |
| FP_NAME: 'Физ. лицо', | |
| JP_NAME: 'ЮЛ наименование', | |
| STAGE: 'Статус', | |
| ISP_NAME: 'Исполнитель', | |
| ISP_OTD: 'Отдел' | |
| } | |
| const templating = {}; | |
| const condKey = 'i_obr' | |
| class AppealExplorer extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| const fields = [] | |
| this.state = {fields}; | |
| this.where = {}; | |
| this.key = 0; | |
| this.registerGetSelected = this.registerGetSelected.bind(this); | |
| this.conditionGetter = null; | |
| this.dataFromChild = null; | |
| this.conditionRemover = null; | |
| this.search = this.search.bind(this); | |
| this.searchMy = this.searchMy.bind(this); | |
| this.getXFile = this.getXFile.bind(this); | |
| this.remove = this.remove.bind(this); | |
| this.checkRemoteCommands = this.checkRemoteCommands.bind(this); | |
| } | |
| componentDidMount(){ | |
| this.checkRemoteCommands(); | |
| } | |
| async checkRemoteCommands(){ | |
| const alias='TABLE_INFO'; | |
| const table_alias= 'i_obr'; | |
| const orphan = true; | |
| const CS = sessionStorage.getItem('claim_show'); | |
| sessionStorage.removeItem('claim_show'); | |
| if (CS){ // есть внешний запрос на показ Обращения | |
| const {claim_id,theme_id} = JSON.parse(CS); | |
| if (claim_id){ | |
| sessionStorage.setItem('show_theme',theme_id); | |
| this.openRow({ID:claim_id},true)(); | |
| } else if (theme_id) { | |
| const alias = 'GET_CLAIM_ID_BY_THEME'; | |
| const x = await post('db/select', {alias, theme_id,orphan}) | |
| const claim_id = parseInt(x.data); | |
| if (!isNaN(claim_id)){ | |
| sessionStorage.setItem('show_theme',theme_id); | |
| this.openRow({ID:claim_id},true)(); | |
| } | |
| } | |
| } else { // внешнего запроса нет - показ таблицы | |
| const x = await post('db/select',{alias,table_alias,orphan}); | |
| const {data,error} = x; | |
| if (_.size(data)){ | |
| this.setState({fields:data}); | |
| } | |
| } | |
| } | |
| openRow(rowData, holdTab) { | |
| const {dispatch, change, initialize, sid} = this.props; | |
| const alias = 'CLAIM_GET'; | |
| const orphan = true; | |
| return async () => { | |
| const claim_id = rowData.ID; | |
| const x = await post('db/select', {alias, claim_id,orphan}); | |
| dispatch(initialize(im(x.data))); | |
| if (holdTab){ | |
| relocate('appeal_incoming'); | |
| } else { | |
| const key = window.stateSave(); | |
| const href = window.location.href.replace('/explore',`/appeal_incoming&storageKey=${key}`); | |
| window.open(href,'_blank'); | |
| } | |
| } | |
| } | |
| search() { | |
| const s = this.conditionGetter(); | |
| const w = _.chain(s).filter(x=>x.value || x.oper=='NOT NULL' || x.oper=='NULL').value(); | |
| if (!_.size(w)){ | |
| this.key=0; | |
| window.claimMessageAdd('E','Условие для поиска не задано'); | |
| this.forceUpdate(); | |
| return; | |
| } | |
| desc.alias = 'APPEAL_LIST'; | |
| this.where = w; | |
| this.key = 'k' + new Date().getTime(); | |
| this.forceUpdate(); | |
| } | |
| searchMy(){ | |
| const s = this.conditionGetter(); | |
| const w = _.chain(s).filter(x=>x.value || x.oper=='NOT NULL' || x.oper=='NULL').value(); | |
| desc.alias = 'APPEAL_LIST_MY'; | |
| this.where = w; | |
| this.key = 'k' + new Date().getTime(); | |
| this.forceUpdate(); | |
| } | |
| remove() { | |
| const rlyRem = window.confirm('Вы уверены, что хотите очистить условия?'); | |
| if (!rlyRem) { | |
| return; | |
| } | |
| this.conditionRemover(); | |
| } | |
| getXFile() { | |
| const alias = 'I_CLAIM_EXCEL_LIST'; | |
| const {sid} = this.props; | |
| const selected = this.getSelected(); | |
| if (_.isEmpty(selected)) { | |
| const msg = 'Ни одна запись не выбрана'; | |
| messageSet(msg, 'error'); | |
| console.error(msg); | |
| return; | |
| } | |
| const doc_ids = '{'+(selected||[]).map(x=>x.ID).join(',')+'}'; | |
| const params = new URLSearchParams(); | |
| params.append('sessionId',sid); | |
| params.append('doc_ids', doc_ids); | |
| params.append('alias', alias); | |
| const path = 'xls/fill?'; | |
| const tempLink = document.createElement('a'); | |
| var event = document.createEvent('MouseEvents'); | |
| event.initMouseEvent( | |
| 'click', true, false, window, 0, 0, 0, 0, 0 | |
| , false, false, false, false, 0, null | |
| ); | |
| tempLink.href = baseUrl() + path + params.toString(); | |
| tempLink.setAttribute('download', 'test.xls'); | |
| tempLink.dispatchEvent(event); | |
| setTimeout(()=>(tempLink && (tempLink.remove())),5000); | |
| } | |
| registerGetSelected(outerGetSelected) { | |
| this.getSelected = outerGetSelected; | |
| this.forceUpdate(); | |
| } | |
| render() { | |
| const {key,where,state,registerGetSelected, remove} = this; | |
| const {fields} = state; | |
| const noTable = this.key == 0; | |
| const {sid} = this.props; | |
| templating['REG_NUM'] = (rowData, column) => (<a onClick={this.openRow(rowData)}>{rowData.REG_NUM}</a>); // | |
| const actionCol = null && {style, body}; | |
| const setGetter = (getter) => this.conditionGetter = getter; | |
| const setRemover = (remover) => this.conditionRemover = remover; | |
| const myCallback = (dataFromChild) => this.dataFromChild = dataFromChild; | |
| return (<React.Fragment> | |
| <Card className="box-card sectionCard" header={ | |
| <div className="headline"> | |
| <h3>Поиск обращений</h3> | |
| </div> | |
| }> | |
| <SearchRoot {...{fields,setGetter,setRemover,condKey}} callbackFromParent={this.myCallback}/> | |
| <div className='btns align-t mt18'> | |
| <Button className="txt-middle mx6" type="primary" icon="search" onClick={this.search}>Поиск</Button> | |
| <Button className="txt-middle mx6" type="primary" icon="search" onClick={this.searchMy}>Поиск среди своих</Button> | |
| {!noTable && (<Button className="txt-middle mx6" type="primary" onClick={this.getXFile}>xls</Button>)} | |
| <Button size="small" className="txt-middle color-darken75 opacity50 opacity100-on-hover" type="text" onClick={remove}> | |
| <i className="ico load align-t mr12"/> | |
| <span>Очистить</span> | |
| </Button> | |
| </div> | |
| </Card> | |
| { noTable ? <div className='mt60'><h3 className='txt-h3 align-center color-darken10'>Нет результатов поиска</h3></div> | |
| : <Card className="box-card" bodyStyle={{ padding: '0' }}> | |
| <AppealTable {...{key,sid,desc,actionCol,mapping,templating,where, registerGetSelected}} hdelta={'420'} selectable={true} /> | |
| </Card>} | |
| </React.Fragment>); | |
| } // | |
| } | |
| const state2props = (state) =>({sid: getSessionId(state)}) | |
| export default compose( | |
| connect(state2props), | |
| reduxForm({ | |
| form: 'appeal', // <------ same form name | |
| destroyOnUnmount: false, // <------ preserve form data | |
| forceUnregisterOnUnmount: true, // <------ unregister fields on unmount | |
| enableReinitialize: true | |
| //validate | |
| }) | |
| )(AppealExplorer) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, {Component} from 'react'; | |
| import {EInput} from './finput.js' | |
| import {ESelect} from './select.js' | |
| import {EPicker} from './picker.js' | |
| import {ECheckbox} from './checkbox.js' | |
| import {Button} from 'element-react' | |
| import {Field, reduxForm} from 'redux-form/immutable' | |
| import {messageSet} from '../../actions/common.js' | |
| import positions from 'positions' | |
| // import '../../../scss/searchRoot.scss' | |
| import '../../../scss/index.scss'; | |
| const SearchRow = (props)=>{ | |
| const {im,change,remove,label,typ,dict,root,link,oper,i} = props; | |
| let {value} = props; | |
| const nullable =(oper=='NULL' || oper=='NOT NULL'); | |
| let Row = <EInput {...{value}} onChange={(e)=>change(i,'value',e)}/>; | |
| if (nullable){ | |
| Row = <span />; | |
| } else if (!!dict){ | |
| Row= <ESelect {...{value}} onChange={(e)=>change(i,'value',e)} dataKey={dict} />; | |
| } else if (typ=='N'){ | |
| Row = <EInput {...{value}} onChange={(e)=>change(i,'value',e)} type="number"/>; | |
| } else if (typ=='D'){ | |
| Row = <EPicker className="w60" {...{value}} onChange={(e)=>change(i,'value',e)} datepicker='+'/>; | |
| } else if (typ=='B'){ | |
| Row = <ECheckbox {...{value}} onChange={(e)=>change(i,'value',e)} /> | |
| } // | |
| return ( | |
| <div className="row"> | |
| <div className="column"> | |
| <div className="value"> | |
| <EInput value={label} className="readOnly" readOnly/> | |
| </div> | |
| </div> | |
| <div className="column"> | |
| <div className="value"> | |
| <div className="select-container"> | |
| {oper && <select className="select bg-white" | |
| value={oper} | |
| onChange={(evt)=>change(i,'oper',evt.target.value)}> | |
| <option value="=">=</option> | |
| <option value=">=">>=</option> | |
| <option value=">">></option> | |
| <option value="<="><=</option> | |
| <option value="<"><</option> | |
| <option value="<>"><></option> | |
| <option value="NULL">NULL</option> | |
| <option value="NOT NULL">NOT NULL</option> | |
| <option value="LIKE">Контекст</option> | |
| </select>} | |
| <div className='select-arrow'></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="column"> | |
| <div className="value fixedDropDown"> | |
| {Row} | |
| </div> | |
| </div> | |
| <div className="column column--end"> | |
| <div className="value mt0"> | |
| <Button className="py0" size="small" type="text" onClick={()=>remove(i)}> | |
| <i className="ico round minus"/> | |
| </Button> | |
| {/* {disabled ? null : | |
| <Button className="py0" size="small" type="text" onClick={add}> | |
| <i className="ico round plus"/> | |
| </Button> | |
| } */} | |
| </div> | |
| </div> | |
| </div>); | |
| } // | |
| class SearchRoot extends React.Component { | |
| constructor(props){ | |
| super(props); | |
| this.scrollElement = null; | |
| this.state = { | |
| root :[], | |
| defCondName : '', | |
| conditionName : '', | |
| conditionsLables : [], | |
| showSavePort : false, | |
| showLoadPort : false | |
| }; | |
| this.change = this.change.bind(this); | |
| this.remove = this.remove.bind(this); | |
| this.add = this.add.bind(this); | |
| this.saveCondition = this.saveCondition.bind(this) | |
| this.loadCondition = this.loadCondition.bind(this) | |
| this.setName = this.setName.bind(this); | |
| this.retPort = this.retPort.bind(this); | |
| this.changeSavePort = this.changeSavePort.bind(this); | |
| this.changeLoadPort = this.changeLoadPort.bind(this); | |
| this.setDefaultCondition = this.setDefaultCondition.bind(this); | |
| this.deleteCondition = this.deleteCondition.bind(this); | |
| this.removeAll = this.removeAll.bind(this); | |
| } | |
| componentDidMount(){ | |
| const {setGetter, setRemover} = this.props; | |
| if (typeof setGetter == 'function'){ | |
| setGetter(()=>(this.state.root)); | |
| } | |
| if (typeof setRemover == 'function') { | |
| setRemover(this.removeAll); | |
| } | |
| const conditions = this.getConditions(); | |
| const defaultConditionKey = this.getDefaultConditionKey(); | |
| const defaultCondition = conditions[defaultConditionKey]; | |
| this.setState ({ | |
| conditionsLables :_.keys(conditions), | |
| root : defaultCondition ? defaultCondition : [], | |
| conditionName : defaultConditionKey, | |
| defCondName : defaultConditionKey | |
| }); | |
| } | |
| change(key,field,evt){ | |
| const {root} = this.state; | |
| root[key][field]=evt | |
| this.setState({root}); | |
| } | |
| remove(key){ | |
| const root = this.state.root.filter((x,i)=>i!=key); | |
| let {showSavePort, showLoadPort, conditionName} = this.state; | |
| if (_.isEmpty(root)) { | |
| showSavePort = false; | |
| showLoadPort = false; | |
| conditionName = ''; | |
| } | |
| this.setState({showSavePort, showLoadPort, conditionName, root}); | |
| } | |
| removeAll() { | |
| this.setState({ | |
| showSavePort : false, | |
| showLoadPort : false, | |
| conditionName : '', | |
| root : [] | |
| }); | |
| } | |
| add(fieldLabel){ | |
| const {fields} = this.props; | |
| let {root} = this.state; | |
| const field = fields.filter(x=>x.label==fieldLabel)[0]; | |
| let oper = field.typ=='S'? 'LIKE' : '='; | |
| if (oper=='ARRAY_LONG' || oper=='ARRAY_STR'){ | |
| oper = null; | |
| } | |
| const row = Object.assign({oper,value:''},field); | |
| root = [...root,row]; | |
| this.setState({root},()=>{ | |
| setTimeout((this.scrollElement.scrollTop=this.scrollElement.scrollHeight), 0); | |
| }); | |
| this.props.callbackFromParent(); | |
| } | |
| getConditions() { | |
| const {condKey} = this.props | |
| try { | |
| const serializedState = localStorage.getItem(condKey); | |
| if (serializedState) { | |
| return JSON.parse(serializedState); | |
| } | |
| } catch (err) { | |
| } | |
| return {}; | |
| } | |
| getDefaultConditionKey() { | |
| const {condKey} = this.props | |
| try { | |
| const cKey = localStorage.getItem(condKey+'_key'); | |
| if (cKey) { | |
| return cKey; | |
| } | |
| } catch (err) { | |
| } | |
| return ''; | |
| } | |
| saveCondition() { | |
| if (this.hasErrors()) { | |
| return; | |
| } | |
| const {root, conditionName} = this.state; | |
| const {condKey} = this.props | |
| const conditions = this.getConditions(); | |
| if (conditions[conditionName]){ | |
| messageSet('Условие перезаписано', 'info'); | |
| } | |
| conditions[conditionName] = root; | |
| const serializedCond = JSON.stringify(conditions); | |
| localStorage.setItem(condKey, serializedCond); | |
| this.setState({ | |
| showSavePort : false, | |
| conditionsLables : _.keys(conditions), | |
| conditionName : conditionName | |
| }) | |
| } | |
| loadCondition(condName) { | |
| const conditionName = !condName ? '' : condName; | |
| let {root} = this.state | |
| const {condKey} = this.props | |
| const conditions = this.getConditions(); | |
| if (conditions && !_.isEmpty(conditions[conditionName])) { | |
| root = conditions[conditionName]; | |
| } | |
| this.setState({conditionName, root}); | |
| } | |
| deleteCondition() { | |
| const rlyDel = window.confirm('Вы уверены, что хотите удалить условие?'); | |
| if (this.hasErrors() || !rlyDel) { | |
| return; | |
| } | |
| const {root, conditionName, defCondName} = this.state; | |
| const {condKey} = this.props | |
| const conditions = this.getConditions(); | |
| let defCond = defCondName; | |
| if (conditionName == defCondName) { | |
| localStorage.removeItem(condKey+'_key') | |
| defCond = ''; | |
| } | |
| delete conditions[conditionName]; | |
| const serializedCond = JSON.stringify(conditions); | |
| localStorage.setItem(condKey, serializedCond); | |
| this.setState({ | |
| root : [], | |
| conditionsLables : _.keys(conditions), | |
| conditionName : '', | |
| defCondName : defCond | |
| }); | |
| } | |
| hasErrors() { | |
| const {root, conditionName} = this.state; | |
| let msg = null; | |
| if (_.isEmpty((conditionName||'').trim())) { | |
| msg = 'Необходимо указать название'; | |
| } else if (!root || _.isEmpty(root)) { | |
| msg = 'Необходимо указать условие' | |
| } | |
| if (!msg) { | |
| return false; | |
| } | |
| messageSet(msg, 'error'); | |
| console.error(msg); | |
| return true; | |
| } | |
| setDefaultCondition() { | |
| const {conditionName, defCondName, conditionsLables} = this.state; | |
| const {condKey} = this.props | |
| if (this.hasErrors() || conditionName == defCondName) { | |
| return; | |
| } | |
| const conditions = _.without(conditionsLables, conditionName); | |
| conditions.unshift(conditionName); | |
| this.setState({defCondName : conditionName, conditionsLables : conditions}); | |
| localStorage.setItem(condKey+'_key', conditionName); | |
| messageSet('Установлено по умолчанию', 'info'); | |
| } | |
| setName(conditionName) { | |
| this.setState({conditionName}); | |
| } | |
| changeLoadPort() { | |
| this.setState({ | |
| showLoadPort: !this.state.showLoadPort, | |
| showSavePort: false | |
| }); | |
| } | |
| changeSavePort() { | |
| this.setState({ | |
| showSavePort: !this.state.showSavePort, | |
| showLoadPort: false | |
| }); | |
| } | |
| retPort() { | |
| const {loadCondition, changeLoadPort, saveCondition, changeSavePort, setDefaultCondition, deleteCondition, setName} = this; | |
| const {conditionName, conditionsLables, showLoadPort, showSavePort, defCondName} = this.state; | |
| const notDefaultCondition = !defCondName || conditionName != defCondName | |
| if (showLoadPort || showSavePort) { | |
| const INNER = showLoadPort | |
| ?(<React.Fragment> | |
| <ESelect key={conditionName} value={conditionName} onChange={(v) => loadCondition(v)} data={conditionsLables}/> | |
| <Button size="small" className="mx6" type="danger" onClick={deleteCondition}>Удалить</Button> | |
| </React.Fragment>) | |
| :(<React.Fragment> | |
| <EInput value={conditionName} onChange={(v)=>setName(v)} /> | |
| <Button size="small" className="mx6" type="primary" onClick={saveCondition}>Сохранить</Button> | |
| </React.Fragment>); // | |
| return( | |
| <div className="flex-table ml0"> | |
| <div className="row"> | |
| <div className="column"> | |
| <div className="label"> | |
| Сохраненные настройки поиска | |
| </div> | |
| <div className="value"> | |
| <div className="flex-parent flex-parent--center-cross"> | |
| {INNER} | |
| <Button size="small" className="mx6" type="text" onClick={showLoadPort?changeLoadPort:changeSavePort}>отмена</Button> | |
| </div> | |
| <a className={`link txt-petite ml3 ${notDefaultCondition ? 'default' : 'txt-underline txt-bold'}`} onClick={setDefaultCondition}> | |
| {notDefaultCondition ? "Установить по умолчанию" : "Установлено по умолчанию"} | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </div>); | |
| } | |
| return null;// | |
| } | |
| render() { | |
| const {change, remove, add, changeSavePort, changeLoadPort} = this; | |
| const {root} = this.state; | |
| const {fields,condKey} = this.props; | |
| const showSaveBtn = root && !_.isEmpty(root); | |
| let ADD = fields.map(x=><option key={x.label} value={x.label}>{x.label}</option>); | |
| ADD = [<option key='000' value='000'>Добавить поле</option>,...ADD]; // | |
| const showPort = this.retPort(); | |
| return ( | |
| <React.Fragment> | |
| <div className="searchRoot"> | |
| <div className="panel"> | |
| <div> | |
| <div className="searchRoot-item searchRoot__add"> | |
| <div className="wrap"> | |
| <div className="item item--full mb6"> | |
| <small className="label">Добавить поле для поиска</small> | |
| <div className="value"> | |
| <div className="select-container bg-white"> | |
| <select className="select" value='000' onChange={(evt)=>add(evt.target.value)}>{ADD}</select> | |
| <div className='select-arrow'></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="searchRoot-item searchRoot__control" ref={el => (this.scrollElement = el) }> | |
| <div className="flex-table ml0"> | |
| {root.map((x,i)=><SearchRow {...x} {...{change,remove,i}} />)} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="searchRoot-item searchRoot__saver"> | |
| {!condKey ? null: <div className="flex-parent flex-parent--center-cross"> | |
| <Button size="small" onClick={changeLoadPort}>Загрузить условие</Button> | |
| {showSaveBtn && <Button size="small" onClick={changeSavePort}>Сохранить условие</Button>} | |
| </div>} | |
| {showPort} | |
| </div> | |
| </div> | |
| </div> | |
| </React.Fragment> | |
| );// | |
| } | |
| } | |
| export {SearchRoot} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment