Commit 800be6cd by hfpp2012 yinsigan

用es6来重写spd

1 parent 8390dd24
...@@ -7,10 +7,16 @@ ...@@ -7,10 +7,16 @@
"start": "webpack-dev-server" "start": "webpack-dev-server"
}, },
"dependencies": { "dependencies": {
"halogen": "^0.2.0",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^2.28.0",
"jquery": "^3.2.1",
"path": "^0.12.7", "path": "^0.12.7",
"react": "^15.5.4", "react": "^15.5.4",
"react-addons-update": "^15.5.2",
"react-alert": "^2.0.1",
"react-bootstrap": "^0.31.0",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-router-dom": "^4.1.1",
"webpack": "^2.5.1", "webpack": "^2.5.1",
"webpack-dev-server": "^2.4.5" "webpack-dev-server": "^2.4.5"
}, },
......
import React from 'react'
import Move from '../libs/move'
import EventEmitter from '../libs/eventEmitter'
import { hashHistory } from 'react-router'
export default class Element extends React.Component {
static get getDefaultProps() {
return {
// allow the initial position to be passed in as a prop
initialPos: {x: 0, y: 0}
}
}
constructor(props){
super(props);
this.state = {
pos: this.props.initialPos,
dragging: false,
rel: null, // position relative to the cursor
zIndex: 2,
parent_id: null,
hasMoved: false,
edit: false,
name: this.props.name,
prev_name: this.props.name
};
}
componentDidUpdate(props, state) {
if (this.state.dragging && !state.dragging) {
document.addEventListener('mousemove', this.onMouseMove)
document.addEventListener('mouseup', this.onMouseUp)
} else if (!this.state.dragging && state.dragging) {
document.removeEventListener('mousemove', this.onMouseMove)
document.removeEventListener('mouseup', this.onMouseUp)
}
}
onMouseDown(event) {
if (event.button !== 0) return
if (!this.props.isDropZoneChild) {
this.handleCloneElement()
}
this.setState({
dragging: true,
rel: {
x: event.pageX - this.element().offsetLeft,
y: event.pageY - this.element().offsetTop
},
zIndex: this.state.zIndex++,
hasMoved: false
})
event.stopPropagation()
event.preventDefault()
}
onMouseMove(event) {
if (!this.state.dragging) return
let pageWidth
if (this.props.isDropZoneChild) {
pageWidth = this.dropzone().clientWidth
} else {
pageWidth = document.documentElement.clientWidth + 20
}
//var pageHeight = document.documentElement.clientHeight
const elementWidth = this.element().offsetWidth
const elementHeight = this.element().offsetHeight
const maxX = pageWidth - elementWidth
const maxY = this.dropzone().clientHeight - elementHeight
let moveX = event.pageX - this.state.rel.x
let moveY = event.pageY - this.state.rel.y
moveX = Math.min( maxX, Math.max(0, moveX) )
moveY = Math.min( maxY, Math.max(0, moveY) )
this.setState({
pos: {
x: moveX,
y: moveY
},
hasMoved: true
})
event.stopPropagation()
event.preventDefault()
}
onMouseUp(event) {
this.setState({ dragging: false })
if (this.props.isDropZoneChild) {
const element = {
id: this.props.id,
pos_x: this.state.pos.x,
pos_y: this.state.pos.y,
}
if (this.state.hasMoved) {
EventEmitter.dispatch('element:update', element)
}
} else if (this.jl(this.element(), this.dropzone())) {
this.handleMoveDropZone()
} else {
Move( this.element() , { left: this.props.initialPos.x , top: this.props.initialPos.y } )
this.handleDeleteLastElement()
}
event.stopPropagation()
event.preventDefault()
}
element() {
return this.refs.meta_element
}
dropzone() {
return document.getElementById('dropzone')
}
sidebar() {
return document.getElementById('sidebar')
}
handleCloneElement() {
if (this.props.handleCloneElement) {
const element = {
id: this.props.id + 3,
name: this.props.name,
initialPos: {
x: this.props.initialPos.x,
y: this.props.initialPos.y
},
target_type: this.props.target_type
}
this.props.handleCloneElement(element)
}
}
handleDeleteLastElement() {
if (this.props.handleDeleteLastElement) {
this.props.handleDeleteLastElement()
}
}
handleMoveDropZone() {
const element = {
name: this.props.name,
target_type: this.props.target_type,
initialPos: {
x: this.state.pos.x - this.sidebar().clientWidth,
y: this.state.pos.y - this.sidebar().clientHeight + 55
}
}
EventEmitter.dispatch('moveDropZone', element)
if (this.props.handleDeleteElement) {
this.props.handleDeleteElement(this.props.meta_element)
}
}
// 碰撞
// pz: function(obj1, obj2) {
// var L1 = obj1.offsetLeft
// var R1 = obj1.offsetLeft + obj1.offsetWidth
// var T1 = obj1.offsetTop
// var B1 = obj1.offsetTop + obj1.offsetHeight
//
// var L2 = obj2.offsetLeft
// var R2 = obj2.offsetLeft + obj2.offsetWidth
// var T2 = obj2.offsetTop
// var B2 = obj2.offsetTop + obj2.offsetHeight
//
// if( R1<L2 || L1>R2 || B1<T2 || T1>B2 ){
// return false
// }
// else{
// return true
// }
// },
jl(obj1, obj2){
let result = obj1.offsetLeft - obj2.offsetLeft - this.props.initialPos.x + 65
return result > 0 ? true : false
}
handleClick() {
if (this.props.isDropZoneChild) {
this.setState({ edit: true })
}
}
handleChange(e) {
this.setState({ name: e.target.value })
}
handleBlur() {
this.handleUpdateName()
}
handleKeyPress(e) {
if (e.key === 'Enter') {
this.handleUpdateName()
}
}
handleUpdateName() {
this.setState({ edit: false })
const element = {
id: this.props.id,
name: this.state.name
}
if (this.state.name != this.state.prev_name) {
if (this.state.name.trim() == "") {
this.setState({ name: this.state.prev_name })
} else {
EventEmitter.dispatch('element:update', element)
this.setState({ prev_name: this.state.name })
}
}
}
handleDoubleClick() {
const childrenObjectType = this.props.childrenObjectType
const url = `/regional_design/${this.props.id}?object=${childrenObjectType}`
if (childrenObjectType != 'drug_brandreth_grid') {
hashHistory.push(url)
}
}
divBlock() {
return (
<div style={{ position: 'absolute', left: this.state.pos.x + 'px', top: this.state.pos.y + 'px', zIndex: this.state.zIndex }} ref='meta_element' className="meta-element text-center" onMouseDown={ this.onMouseDown } onDoubleClick={ this.handleDoubleClick }>
<span onClick={ this.handleClick }>{this.state.name}</span>
</div>
)
}
inputText() {
return (
<div style={{ position: 'absolute', left: this.state.pos.x + 'px', top: this.state.pos.y + 'px', zIndex: this.state.zIndex }} className="input-meta-element text-center">
<input autoFocus type="text" id="formHorizontalName" className="form-control" ref='name' value={ this.state.name } onChange={ this.handleChange } onBlur={ this.handleBlur } onKeyPress={ this.handleKeyPress } />
</div>
)
}
render() {
if (this.state.edit) {
return this.inputText()
} else {
return this.divBlock()
}
}
}
import React from 'react'
import Element from './Element'
import ReactAddonsUpdate from 'react-addons-update'
export default class ElementGroup extends React.Component {
constructor(props){
super(props);
this.state = {
elements: [
{ id: 1, name: "药库", initialPos: {x: 80, y: 70}, target_type: 'RegionalDesign::Repo' },
{ id: 2, name: "药架", initialPos: {x: 80, y: 70 + 60}, target_type: 'RegionalDesign::DrugRepo' },
{ id: 3, name: "药架格", initialPos: {x: 80, y: 70 + 60 * 2}, target_type: 'RegionalDesign::DrugBrandreth' }
]
};
}
cloneElement(element) {
const elements = ReactAddonsUpdate(this.state.elements, { $push: [element] })
this.setState({elements: elements})
}
deleteElement(element) {
const index = this.state.elements.indexOf(element)
const elements = ReactAddonsUpdate(this.state.elements, { $splice: [[index, 1]] })
this.replaceState({elements: elements})
}
// 返回element的index为-1, 删除最后一个元素
deleteLastElement() {
const elements = ReactAddonsUpdate(this.state.elements, { $splice: [[-1, 1]] })
this.replaceState({elements: elements})
}
sidebar() {
return document.getElementById('sidebar')
}
render() {
return (
<div>
{
this.state.elements.map(function(element) {
return (
<Element meta_element={ element } key={ "key_" + element.id } id={ element.id } name={ element.name } initialPos={ element.initialPos } target_type={ element.target_type } handleCloneElement={ this.cloneElement } handleDeleteElement={ this.deleteElement } handleDeleteLastElement={ this.deleteLastElement } />
)
}.bind(this))
}
</div>
)
}
}
import React from 'react'
import ModalRepoForm from './ModalRepoForm'
import ReactAddonsUpdate from 'react-addons-update'
import Loader from 'halogen/PulseLoader'
import { Link } from 'react-router'
export default class Header extends React.Component {
constructor(props){
super(props);
this.state = {
data: null, loading: true, error: null
};
}
componentDidMount() {
this.props.promise.then(
value => this.setState({loading: false, data: value}),
error => this.setState({loading: false, error: error}))
}
addRepo(repo) {
const data = ReactAddonsUpdate(this.state.data, { $push: [repo] })
this.setState({ data: data })
}
render() {
return (
<div className="text-left top-header">
<ModalRepoForm handleNewRepo={ this.addRepo } />
<div className='col-md-10'>
{function(){
if (this.state.loading) {
return (
<Loader color="#286090" size="12px" margin="4px"/>
)
}
else if (this.state.error !== null) {
return <span>Error: {this.state.error.message}</span>
}
else {
const data = this.state.data.map(function(repo) {
const url = `/regional_design/${repo.id}?object=repo`
return (
<Link key={ repo.id } activeClassName='btn-primary' className='btn btn-default mr15' to={ url }>{ repo.name }</Link>
)
})
return data
}
}.call(this)}
</div>
<div className="clearfix"></div>
</div>
)
}
}
import React from 'react'
import { Modal, Button } from 'react-bootstrap'
import { Form, FormGroup, Col, FormControl, ControlLabel } from 'react-bootstrap'
import EventEmitter from '../libs/eventEmitter'
import $ from 'jquery'
export default class ModalRepoForm extends React.Component {
constructor(props){
super(props);
this.state = {
showModal: false, name: ''
};
}
close() {
this.setState({ showModal: false })
}
open() {
this.setState({ showModal: true })
}
handleChange(e) {
return this.setState({ name: e.target.value })
}
save(e) {
if (this.refs.name.value.trim().length === 0) {
this.setState({ showNameError: true })
return false
} else {
this.setState({ showNameError: false })
e.preventDefault()
$.post('/regional_design/repos.json', { repo: this.state }, function(data) {
this.props.handleNewRepo(data)
this.setState(this.getInitialState())
EventEmitter.dispatch('alert', '操作成功')
}.bind(this), 'JSON')
}
}
render() {
return (
<div className='col-md-2'>
<Button
bsStyle="primary"
bsSize="small"
onClick={this.open}
>
新建仓库
</Button>
<Modal backdrop='static' show={this.state.showModal} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>新建仓库</Modal.Title>
</Modal.Header>
<Modal.Body className='clearfix'>
<form className='form-horizontal'>
<label htmlFor="formHorizontalName" className="col-sm-2 control-label">仓库名</label>
<div className="col-sm-5">
<input autoFocus type="text" id="formHorizontalName" className="form-control" ref='name' value={ this.state.name } onChange={ this.handleChange } />
{function(){
if (this.state.showNameError) {
return <p className="text-danger">仓库名不能为空</p>
}
}.call(this)}
</div>
</form>
</Modal.Body>
<Modal.Footer>
<Button bsStyle="primary" onClick={this.save}>保存</Button>
<Button onClick={this.close}>关闭</Button>
</Modal.Footer>
</Modal>
</div>
)
}
}
import React from 'react'
import Loader from 'halogen/PulseLoader'
import Element from './Element'
import ReactAddonsUpdate from 'react-addons-update'
import EventEmitter from '../libs/eventEmitter'
import $ from 'jquery'
export default class RegionalDesign extends React.Component {
constructor(props){
super(props);
this.state = {
data: null, loading: true, error: null, object_name: '', object_type: '', children_object_type: ''
};
}
getConfig(props, key) {
const config = {
repo: {
object_url: '/regional_design/repos',
children_object_url: '/regional_design/drug_repos',
children_param_name: 'drug_repo'
},
drug_repo: {
object_url: '/regional_design/drug_repos',
children_object_url: '/regional_design/drug_brandrethes',
children_param_name: 'drug_brandreth'
},
drug_brandreth: {
object_url: '/regional_design/drug_brandrethes',
children_object_url: '/regional_design/drug_brandreth_grids',
children_param_name: 'drug_brandreth_grid'
}
}
return config[props.location.query.object][key]
}
componentDidMount() {
const url = this.getConfig(this.props, 'object_url') + '/' + this.props.params.id + '.json'
this.getJSON(url)
EventEmitter.subscribe('moveDropZone', function(element){
if (element.target_type == this.state.object_type) {
this.addElement(element)
} else {
EventEmitter.dispatch('alert', '不能移动该元件')
}
}.bind(this))
EventEmitter.subscribe('element:update', function(element){
this.updateElement(element)
}.bind(this))
}
addElement(element) {
const new_element = {
name: element.name,
pos_x: element.initialPos.x,
pos_y: element.initialPos.y,
parent_id: this.props.params.id
}
const url = this.getConfig(this.props, 'children_object_url') + '.json'
let data = {}
data[this.getConfig(this.props, 'children_param_name')] = new_element
$.post(url, data, function(data) {
EventEmitter.dispatch('alert', '添加元件成功')
element.id = data.id
element.children_object_type = this.state.children_object_type
const elements = ReactAddonsUpdate(this.state.data, { $push: [element] })
this.setState({data: elements})
}.bind(this), 'JSON')
}
updateElement(element) {
let data = {}
data[this.getConfig(this.props, 'children_param_name')] = element
$.ajax({
method: 'PUT',
url: this.getConfig(this.props, 'children_object_url') + '/' + element.id,
dataType: 'JSON',
data: data,
success: function(data) {
}.bind(this)
})
}
componentWillUnmount() {
EventEmitter.unSubscribe('moveDropZone')
EventEmitter.unSubscribe('element:update')
}
componentWillReceiveProps(nextProps) {
this.setState(this.getInitialState)
const url = this.getConfig(nextProps, 'object_url') + '/' + nextProps.params.id + '.json'
this.getJSON(url)
}
getJSON(url) {
$.getJSON(url).then(
value => this.setState({loading: false, data: value.children, object_name: value.name, object_type: value.type, children_object_type: value.children_object_type}),
error => this.setState({loading: false, error: error}))
}
render() {
if (this.state.loading) {
return (
<Loader color="#286090" size="12px" margin="4px"/>
)
}
else if (this.state.error !== null) {
return <span>Error: {this.state.error}</span>
}
else {
const data = this.state.data.map(function(element) {
return (
<Element childrenObjectType={ this.state.children_object_type } isDropZoneChild={ true } key={ element.id } id={ element.id } name={ element.name } initialPos={ element.initialPos } />
)
}.bind(this))
return (
<div>
<h1 className='text-center'>{ this.state.object_name }</h1>
<div>{ data }</div>
</div>
)
}
}
}
import React from 'react'
import ElementGroup from '../components/ElementGroup'
import Header from '../components/Header'
import AlertContainer from 'react-alert'
import EventEmitter from '../libs/eventEmitter'
import $ from 'jquery'
export default class Spd extends React.Component {
componentDidMount() {
EventEmitter.subscribe('alert', function(info){
this.showAlert(info)
}.bind(this))
}
componentWillUnmount() {
EventEmitter.unSubscribe('alert')
}
componentWillMount() {
this.alertOptions = {
offset: 14,
position: 'top right',
theme: 'dark',
time: 5000,
transition: 'scale'
}
}
showAlert(info) {
this.msg.show(info, {
time: 2000,
type: 'success',
icon: <img src="/images/success.png" />
})
}
render() {
return (
<div id="app">
<AlertContainer ref={a => this.msg = a} {...this.alertOptions} />
<div className="row">
<Header promise={$.getJSON('/regional_design/repos.json')} />
</div>
<div className="row">
<div className="col-md-2 sidebar" id='sidebar'>
<h3 className='sidebar-header'>元件</h3>
<ElementGroup />
</div>
<div id="dropzone" className="col-md-10 dropzone">
{this.props.children}
</div>
</div>
</div>
)
}
}
No preview for this file type
import React from 'react'
import ReactDOM from 'react-dom'
import Spd from './containers/Spd'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
import RegionalDesign from './components/RegionalDesign'
ReactDOM.render(
<Router handler={Spd} history={Router}>
<Route path="/" component={Spd}>
<Route path="/regional_design/:id" component={RegionalDesign} />
</Route>
</Router>,
document.getElementById('root')
);
export default {
_events: {},
dispatch: function (event, data) {
if (!this._events[event]) return // no one is listening to this event
for (let i = 0; i < this._events[event].length; i++)
this._events[event][i](data)
},
subscribe: function (event, callback) {
if (!this._events[event]) this._events[event] = [] // new event
this._events[event].push(callback)
},
unSubscribe: function(event){
if(this._events && this._events[event]) {
delete this._events[event]
}
}
}
function startMove(obj, json, endFn) {
clearInterval(obj.timer)
obj.timer = setInterval(function() {
let bBtn = true
for (let attr in json) {
let iCur = 0
if (attr == 'opacity') {
if (Math.round(parseFloat(getStyle(obj, attr)) * 100) == 0) {
iCur = Math.round(parseFloat(getStyle(obj, attr)) * 100)
} else {
iCur = Math.round(parseFloat(getStyle(obj, attr)) * 100) || 100
}
} else {
iCur = parseInt(getStyle(obj,attr)) || 0
}
let iSpeed = (json[attr] - iCur)/8
iSpeed = iSpeed >0 ? Math.ceil(iSpeed) : Math.floor(iSpeed)
if (iCur != json[attr]) {
bBtn = false
}
if (attr == 'opacity') {
obj.style.filter = 'alpha(opacity=' + (iCur + iSpeed) + ')'
obj.style.opacity = (iCur + iSpeed)/100
} else {
obj.style[attr] = iCur + iSpeed + 'px'
}
}
if (bBtn) {
clearInterval(obj.timer)
if (endFn) {
endFn.call(obj)
}
}
}, 30)
}
function getStyle(obj, attr) {
if (obj.currentStyle) {
return obj.currentStyle[attr]
} else {
return getComputedStyle(obj,false)[attr]
}
}
export default startMove
...@@ -27,14 +27,19 @@ const extractSass = ExtractTextPlugin.extract({ ...@@ -27,14 +27,19 @@ const extractSass = ExtractTextPlugin.extract({
const OpenBrowserPlugin = require('open-browser-webpack-plugin'); const OpenBrowserPlugin = require('open-browser-webpack-plugin');
module.exports = { module.exports = {
entry: './client/index.js', entry: './src/index.js',
output: { output: {
path: path.resolve('dist'), path: path.resolve('dist'),
filename: 'index_bundle.js' filename: 'index_bundle.js'
}, },
devServer: { devServer: {
inline: true, inline: true,
port: 3000 port: 3000,
proxy: {
"/regional_design/repos": {
target: "http://localhost:3001",
}
}
}, },
module: { module: {
loaders: [ loaders: [
......
This diff could not be displayed because it is too large.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!