Merge branch 'master' into release

This commit is contained in:
Jay 2017-04-20 09:34:25 +08:00
commit 9b70a544df
9 changed files with 3 additions and 159460 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
node_modules
public/js/admin_bundle.js
public/js/index_bundle.js

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
import React from 'react';
import {List, Label, Button} from 'semantic-ui-react';
const ActionItem = ({i18n, data, idx, removeAction}) => {
return (
<List.Item>
<Label basic content={`裝置名稱`} /><span>{data.devName}</span>
<Label basic content={`動作`} /><span>{data.actName}</span>
<Button floated="right" color="red" basic size="tiny" content="Delete" type="button" onClick={()=>{
removeAction(idx);
}}/>
</List.Item>
)
}
export default ActionItem;

View File

@ -1,127 +0,0 @@
import React from 'react';
import {Form, Button, Input, Segment} from 'semantic-ui-react';
class ActionSelect extends React.Component{
state = {
showTemp: false,
temp: '0'
}
checkShowTemp = (cmd = '') => {
let c = cmd;
let cs = c.split(' ');
let showTemp = false;
if(cs[0] == 2) {
showTemp = true;
}
this.setState({
showTemp
});
}
updateTemp = (temp) => {
this.setState({
temp
})
}
render(){
let {i18n,list, unit, getList, addAction} = this.props;
let actlist = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.action_list') : [];
return (
<Form.Field>
<label>加入動作</label>
<Segment color="green" className="clearfix">
<Form.Group inline>
<Form.Field>
<label>選擇裝置</label>
<select ref="devType" onChange={e=>{
getList(unit, e.target.value);
}}>
<option value="">選擇裝置類型</option>
<option value="do">DigitOutput</option>
<option value="leone">LeOne</option>
<option value="iogroup">IOGroup</option>
</select>
</Form.Field>
<Form.Field>
<select ref="devName">
<option value="">選擇裝置</option>
{
list.map((t,idx)=>(<option key={idx} value={t.id}>{t.name}</option>))
}
</select>
</Form.Field>
</Form.Group>
<Form.Group inline>
<Form.Field>
<label>選擇動作</label>
<select ref="act" onChange={e=>{
this.checkShowTemp(e.target.value);
}}>
<option value="">選擇動作</option>
{
actlist.map((t,idx) => (
<option key={idx} value={t.cmd}>{t.name}</option>
))
}
</select>
</Form.Field>
{
this.state.showTemp ?
(
<Form.Field>
<Input size="mini" placeholder="請輸入溫度" value={this.state.temp} onChange={(e,d) => {
this.updateTemp(d.value);
}} />
</Form.Field>
) : null
}
</Form.Group>
<Button floated="right" content="加入動作" size="tiny" basic color="orange" onClick={()=>{
let type = this.refs.devType.value;
let dev = this.refs.devName.value;
let devName = this.refs.devName.options[this.refs.devName.selectedIndex].innerHTML;
let act = this.refs.act.value;
let actName = this.refs.act.options[this.refs.act.selectedIndex].innerHTML;
let temp = this.state.temp;
let prefix = '';
switch(type){
case 'do':
prefix = 'do';
break;
case 'leone':
prefix = 'le';
break;
case 'iogroup':
prefix = 'iogroup';
break;
default:
return addAction(null);
}
if(!act) return addAction(null);
if(act == 2) {
act = `${act} ${temp}`;
actName += ` ${temp}`;
}
let data = {
id: `${prefix}${dev}`,
act,
actName,
devName
};
return addAction(data);
}} />
</Segment>
</Form.Field>
)
}
}
export default ActionSelect;

View File

@ -1,279 +0,0 @@
import React from 'react';
import {Grid, Form, Segment, Input} from 'semantic-ui-react';
const ops = [
{
"code": "0",
"name": "等於"
}, {
"code": "1",
"name": "大於"
}, {
"code": "2",
"name": "小於"
}, {
"code": "3",
"name": "大於等於"
}, {
"code": "4",
"name": "小於等於"
}, {
"code": "5",
"name": "不等於"
}, {
"code": "8",
"name": "AND"
}, {
"code": "9",
"name": "OR"
}
]
const stype = {
di: {
prefix: 'di',
dev: '',
op: '',
value: '',
mode: 'di_status'
},
leone: {
prefix: 'le',
dev: '',
op: '',
value: '',
mode: ''
},
unit: {
dev: ''
},
time: {
op: '',
dev: 'dt0',
mode: 'mode0',
year: '-',
week: '-',
month: '-',
day: '-',
hour: '-',
min: '-'
}
}
class UnitItem extends React.Component {
state = {
st: '',
data:{}
}
handleDataChange = (tag, value) => {
let {data} = this.state;
let {updateData} = this.props;
if(tag in data) {
data[tag] = value;
}
this.setState({
data
}, () => {
let {data} = this.state
let d = {...this.props.data.data}
let {st} = this.props.data;
if(st == 'di' || st == 'leone'){
d.id = `${data.prefix}${data.dev},${data.mode}`;
d.op = data.op;
d.value = data.value;
}
if(st == 'unit') {
d.dev = data.dev;
}
if(st == 'time') {
d.id = `${data.dev},${data.mode}`;
d.op = data.op;
d.value = `${data.min}|${data.hour}|${data.day}|${data.month}|${data.week}|${data.year}`;
}
updateData(this.props.unit, d);
});
}
componentWillReceiveProps(np) {
let data = {...this.state.data};
if(np.data.st != this.state.st){
if(np.data.st in stype) {
data = {...stype[np.data.st]};
}else {
data = {}
}
}
this.setState({
data,
st: np.data.st
})
}
render() {
let {unit, data, getList} = this.props;
return (
<Grid.Column>
<Segment>
<Form.Field>
<label>選擇元件</label>
<select
value={data.st}
onChange={e => {
getList(unit, e.target.value);
}}>
<option value="">請選擇元件</option>
<option value="leone">LeOne</option>
<option value="di">DigitInput</option>
<option value="time">時間</option>
<option value="unit">已建立群組</option>
</select>
</Form.Field>
{
!data.st
? null
: (<UnitPanel data={data} state={this.state.data} dataChange={this.handleDataChange}/>)
}
</Segment>
</Grid.Column>
)
}
}
const UnitPanel = ({data, state, dataChange}) => {
if (data.st == 'di') {
return (
<div>
<select value={state.dev} onChange={(e) => {
dataChange('dev', e.target.value);
}}>
<option value="">選擇裝置</option>
{
data.list.map((t, idx) => (
<option key={idx} value={t.id}>{t.name}</option>
))
}
</select>
<select value={state.op} onChange={e => {
dataChange('op', e.target.value);
}}>
<option value="">選擇條件</option>
{
ops.map((t,idx) => {
if(t.code == 8 || t.code == 9) return ;
return (<option key={idx} value={t.code}>{t.name}</option>)
})
}
</select>
<select value={state.value} onChange={e => {
dataChange('value', e.target.value);
}}>
<option value="">選擇狀態</option>
<option value="0">0</option>
<option value="1">1</option>
</select>
</div>
)
}
if(data.st == 'leone') {
return (
<div>
<select value={state.dev} onChange={e => {
dataChange('dev', e.target.value);
}}>
<option value="">選擇裝置</option>
{
data.list.map((t, idx) => (
<option key={idx} value={t.id}>{t.name}</option>
))
}
</select>
<select value={state.mode} onChange={e => {
dataChange('mode', e.target.value);
}}>
<option value="">選擇感測器</option>
<option value="leone_ttrigger1">溫度</option>
<option value="leone_htrigger1">濕度</option>
</select>
<select value={state.op} onChange={e => {
dataChange('op', e.target.value);
}}>
<option value="">選擇條件</option>
{
ops.map((t,idx) => {
if(t.code == 8 || t.code == 9) return ;
return (<option key={idx} value={t.code}>{t.name}</option>)
})
}
</select>
<Input size="mini" placeholder="請輸入數值" value={state.value} onChange={(e,d) => {
dataChange('value', d.value);
}} />
</div>
)
}
if(data.st == 'unit') {
return (
<div>
<select value={state.dev} onChange={e => {
dataChange('dev', e.target.value);
}}>
<option value="">選擇群組</option>
{
data.list.map((t,idx) => (
<option key={idx} value={t.id}>{t.name}</option>
))
}
</select>
</div>
)
}
if(data.st == 'time') {
return (
<div>
<select value={state.op} onChange={e => {
dataChange('op', e.target.value);
}}>
<option value="">選擇條件</option>
{
ops.map((t,idx) => {
if(t.code == 8 || t.code == 9) return ;
return (<option key={idx} value={t.code}>{t.name}</option>)
})
}
</select>
<Input size="mini" placeholder="年(不指定請留空)" value={state.year == '-' ? '' : state.year} onChange={(e,d) => {
dataChange('year', d.value.length == 0 ? '-' : d.value);
}} />
<Input size="mini" placeholder="週(不指定請留空)" value={state.week == '-' ? '' : state.week} onChange={(e,d) => {
dataChange('week', d.value.length == 0 ? '-' : d.value);
}} />
<Input size="mini" placeholder="月(不指定請留空)" value={state.month == '-' ? '' : state.month} onChange={(e,d) => {
dataChange('month', d.value.length == 0 ? '-' : d.value);
}} />
<Input size="mini" placeholder="日(不指定請留空)" value={state.day == '-' ? '' : state.day} onChange={(e,d) => {
dataChange('day', d.value.length == 0 ? '-' : d.value);
}} />
<Input size="mini" placeholder="時(不指定請留空)" value={state.hour == '-' ? '' : state.hour} onChange={(e,d) => {
dataChange('hour', d.value.length == 0 ? '-' : d.value);
}} />
<Input size="mini" placeholder="分(不指定請留空)" value={state.min == '-' ? '' : state.min} onChange={(e,d) => {
dataChange('min', d.value.length == 0 ? '-' : d.value);
}} />
</div>
)
}
return null;
}
export default UnitItem;

View File

@ -1,41 +0,0 @@
import React from 'react';
import {Modal, Form, Button} from 'semantic-ui-react';
const ViewTree = ({i18n, open, data, closeView, removeNode}) => {
let seNode = null;
let nodeKey = Object.keys(data);
return (
<Modal open={open} onClose={()=>{closeView()}} >
<Modal.Header content="連動群組資料" />
<Modal.Content>
<Form as="div">
<Form.Group inline>
<Form.Field>
<label>刪除節點</label>
<select ref={node=>seNode=node}>
{
nodeKey.map((t,idx)=>(
<option key={idx} value={t}>{data[t].name}</option>
))
}
</select>
</Form.Field>
<Form.Field>
<Button type="button" basic size="tiny" content="刪除節點" onClick={()=>{
if(!seNode) return ;
removeNode(seNode.value);
}} />
</Form.Field>
</Form.Group>
</Form>
<pre>
{
JSON.stringify(data, null, 4)
}
</pre>
</Modal.Content>
</Modal>
)
}
export default ViewTree;

View File

@ -1,353 +0,0 @@
import React from 'react';
import {
Container,
Segment,
Form,
Input,
Button,
Checkbox,
Grid,
Menu,
List
} from 'semantic-ui-react';
import {getRequest} from '../../../actions';
import UnitItem from './UnitItem';
import ActionSelect from './ActionSelect';
import ActionItem from './ActionItem';
import ViewTree from './ViewTreeModal';
const defState = () => ({
gidx: 0,
groups: {},
active: false,
edit: {
name: '',
type: 'ln',
op: '',
id1: {
st: '',
list: [],
data: {}
},
id2: {
st: '',
list: [],
data: {}
}
},
actDevlist: [],
items: [],
showTree: false
})
const gtype = {
lc: {
type: 'lc',
id: '',
op: '',
value: ''
},
ln: {
type: 'ln',
dev: ''
}
}
class ActionLinkAdd extends React.Component {
state = {
...defState()
}
getSelectList = (unit, type = '') => {
if (!unit) {
return;
}
if(unit != 'act'){
let edit = {
...this.state.edit
};
if (!(unit in edit)) {
return;
}
edit[unit].st = type;
edit[unit].data = {
...gtype.lc
}
if (type != 'di' && type != 'leone' && type != 'unit') {
return this.setState({edit});
}
if (type == 'di' || type == 'leone') {
this.props.toggleLoading(1);
let json = {
type
};
fetch('/api/system/getselectlist', getRequest(json))
.then(response => response.json())
.then(json => {
if (json.status != 1)
return this.props.showDialog(json.message);
edit[unit].list = json.data.record || [];
this.setState({edit}, () => {
this.props.toggleLoading(0);
});
});
}
if(type == 'unit') {
let list = [];
for(let i in this.state.groups){
list.push({id: i, name: this.state.groups[i].name});
}
edit[unit].list = list;
edit[unit].data = {
...gtype.ln
}
this.setState({edit})
}
}else{
let actDevlist = [];
if(!type) {
this.setState({
actDevlist
});
return ;
}
this.props.toggleLoading(1);
fetch('/api/system/getselectlist', getRequest({type}))
.then(response => response.json())
.then(json => {
if (json.status != 1)
return this.props.showDialog(json.message);
actDevlist = json.data.record || []
this.setState({
actDevlist
}, ()=>{
this.props.toggleLoading(0);
})
})
}
}
updateData = (unit, data) => {
let {edit} = this.state;
if(!(unit in edit)) return ;
edit[unit].data = data;
this.setState({
edit
})
}
updateEditData = (tag, value) => {
let {edit} = this.state;
if(tag in edit){
edit[tag] = value;
}
this.setState({
edit
})
}
updateActive = (active = false) => {
this.setState({
active: active ? 1 : 0
})
}
joinUnit = () => {
let {edit} = this.state;
let {i18n} = this.props;
if(!edit.name || edit.op.length == 0 || Object.keys(edit.id1.data).length == 0 || Object.keys(edit.id2.data).length == 0) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(edit.id1.data.type == 'lc' && (edit.id1.data.id.length == 0 || edit.id1.data.op.length == 0 || edit.id1.data.value.length == 0)) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(edit.id2.data.type == 'lc' && (edit.id2.data.id.length == 0 || edit.id2.data.op.length == 0 || edit.id2.data.value.length == 0)) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(edit.id1.data.type == 'ln' && edit.id1.data.dev.length == 0) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(edit.id2.data.type == 'ln' && edit.id2.data.dev.length == 0) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
let unitKey = [];
if(edit.id1.data.type == 'lc'){
edit.id1 = {
...edit.id1.data
}
}else if(edit.id1.data.type == 'ln'){
unitKey.push(edit.id1.data.dev)
edit.id1 = {
...this.state.groups[edit.id1.data.dev]
}
}
if(edit.id2.data.type == 'lc'){
edit.id2 = {
...edit.id2.data
}
}else if(edit.id2.data.type == 'ln'){
unitKey.push(edit.id2.data.dev)
edit.id2 = {
...this.state.groups[edit.id2.data.dev]
}
}
let groups = {...this.state.groups};
for(let i in unitKey){
if(unitKey[i] in groups) delete groups[unitKey[i]];
}
groups[`unit${this.state.gidx}`] = {
...edit
}
this.setState({
gidx: this.state.gidx+1,
groups: {...groups}
}, ()=>{
this.resetEdit()
})
}
resetEdit = () => {
this.setState({
edit:{
...defState().edit
}
})
}
addAction = (item) => {
if(!item) return this.props.showDialog('動作請選擇完整');
let cmds = item.act.split(' ');
if(cmds.length != 2) return this.props.showDialog('動作選擇錯誤');
if(cmds[0] == 2 && (cmds[1] < 16 || cmds[1] > 30)) return this.props.showDialog('溫度範圍錯誤(16-30)');
this.setState({
items: [...this.state.items, item]
})
}
removeAction = (idx) => {
let items = [...this.state.items];
items.splice(idx, 1);
this.setState({
items
})
}
toggleView = (sw = false) => {
this.setState({
showTree: sw ? true : false
})
}
removeNode = (node) => {
if(!node) return ;
let groups = {...this.state.groups};
if(node in groups){
delete groups[node];
this.setState({
groups: {...groups}
})
}
}
saveLink = () => {
if(Object.keys(this.state.groups).length == 0) return this.props.showDialog('請先建立群組');
if(Object.keys(this.state.groups).length > 1) return this.props.showDialog('根節點數量大於一');
if(this.state.items.length == 0) return this.props.showDialog('請選擇動作');
let json = {
active: this.state.active,
link: this.state.groups[Object.keys(this.state.groups)[0]],
action: ''
}
let acts = this.state.items.map(t => {
return `${t.id},${t.act.replace(' ', ',')},0`
})
json.action = acts.join('|')
this.props.toggleLoading(1);
fetch('/api/link/addlink', getRequest(json))
.then(response => response.json())
.then(json => {
this.props.toggleLoading(0);
if(json.status != 1) return this.props.showDialog(json.message);
this.resetAll();
})
}
resetAll = () => {
this.setState({
...defState()
})
}
render() {
let {i18n} = this.props;
return (
<Container>
<Menu >
<Menu.Menu position="right">
<Menu.Item content="ShowTree" onClick={()=>{
this.toggleView(1)
}}/>
<Menu.Item content="Save" onClick={()=>{
this.saveLink();
}}/>
</Menu.Menu>
</Menu>
<Form as={Segment}>
<Form.Field>
<Checkbox label="啟用連動" checked={this.state.active == 1} onChange={(e,d) =>{
this.updateActive(d.checked ? 1 : 0);
}}/>
</Form.Field>
<Form.Field>
<label>建立條件群組</label>
<Segment color="red">
<Form.Field>
<Input label="節點名稱" value={this.state.edit.name} onChange={(e,d) => {
this.updateEditData('name', d.value);
}}/>
</Form.Field>
<Form.Field>
<label>觸發條件</label>
<select value={this.state.edit.op} onChange={(e) => {
this.updateEditData('op', e.target.value);
}}>
<option value="">請選擇觸發條件</option>
<option value="8">AND</option>
<option value="9">OR</option>
</select>
</Form.Field>
<Grid columns={2} padded>
<UnitItem unit="id1" data={this.state.edit.id1} getList={this.getSelectList} updateData={this.updateData}/>
<UnitItem unit="id2" data={this.state.edit.id2} getList={this.getSelectList} updateData={this.updateData}/>
</Grid>
<div
style={{
textAlign: 'right'
}}>
<Button type="button" basic size="tiny" color="blue" content="加入" onClick={()=>{
this.joinUnit();
}}/>
<Button type="button" basic size="tiny" color="green" content="清除" onClick={()=>{this.resetEdit()}}/>
</div>
</Segment>
</Form.Field>
<ActionSelect i18n={i18n} unit="act" list={this.state.actDevlist} getList={this.getSelectList} addAction={this.addAction} />
<Form.Field>
<label>裝置動作列表</label>
<Segment color="blue">
<List>
{
this.state.items.map((t,idx) => (
<ActionItem key={idx} i18n={i18n} data={t} idx={idx} removeAction={this.removeAction}/>
))
}
</List>
</Segment>
</Form.Field>
</Form>
<ViewTree i18n={i18n} data={this.state.groups} open={this.state.showTree} closeView={this.toggleView} removeNode={this.removeNode} />
</Container>
)
}
}
export default ActionLinkAdd;

View File

@ -15,7 +15,7 @@ const loginForm = ({i18n, page, onSubmit}) => {
}}>
<Segment stacked={true} >
<Form.Field>
<Input icon="user" iconPosition="left" name="acc" placeholder={typeof i18n.t == 'function' ? i18n.t(`${page}.input.placeholder.account`) : ''} />
<Input icon="user" iconPosition="left" autoFocus={true} name="acc" placeholder={typeof i18n.t == 'function' ? i18n.t(`${page}.input.placeholder.account`) : ''} />
</Form.Field>
<Form.Field>
<Input icon="lock" iconPosition="left" name="pass" type="password" placeholder={typeof i18n.t == 'function' ? i18n.t(`${page}.input.placeholder.password`) : ''} />