前端源码

Signed-off-by: 烈日下的从容 <wfh45678@163.com>
This commit is contained in:
烈日下的从容
2019-09-23 18:01:53 +08:00
parent bd4a5deb10
commit 6a995b7a67
58 changed files with 8891 additions and 0 deletions

15
webapp/component/App.jsx Normal file
View File

@@ -0,0 +1,15 @@
import React, { Component } from 'react';
export default class App extends Component{
constructor(props){
super(props);
}
render(){
return (
<div>
{this.props.children}
</div>
);
}
}

View File

@@ -0,0 +1,48 @@
import React from 'react';
import {Link} from 'react-router';
import {Menu,Breadcrumb,Icon,Tooltip} from 'antd';
const SubMenu = Menu.SubMenu;
import './Index.less';
import {FetchUtil} from './utils/fetchUtil';
export default class Index extends React.Component{
constructor(props){
super(props);
}
handleLogout=()=>{
FetchUtil('/merchant/logout','GET','',
(data) => {
window.location.href="#/login";
});
}
render(){
return (
<div className="ant-layout-ceiling-demo">
<div className="ant-layout-header">
<div className="ant-layout-wrapper">
<div className="ant-layout-logo">
<Tooltip title="回到首页"><Link to="/modelList"><Icon type="home" /></Link></Tooltip>
</div>
<div className="ant-layout-logo">
<Tooltip title="统计报表"><Link to="/report"><Icon type="line-chart" /></Link></Tooltip>
</div>
<div className="ant-layout-logo" style={{float:"right",marginRight:0}}>
<Tooltip title="退出登录"><a onClick={this.handleLogout}><Icon type="logout" /></a></Tooltip>
</div>
</div>
</div>
<div className="ant-layout-main">
{this.props.children}
</div>
</div>
);
}
}

View File

@@ -0,0 +1,69 @@
.ant-layout-ceiling-demo {
height: 100%;
}
.ant-layout-ceiling {
font-size: 12px;
height: 30px;
line-height: 30px;
background-color: #242736;
color: #ddd;
}
.ant-layout-ceiling .right {
float: right;
}
.ant-layout-ceiling ul li {
display: inline-block;
margin: 0 4px;
}
.ant-layout-ceiling-demo .ant-layout-wrapper {
padding: 0 50px;
}
.ant-layout-ceiling-demo .ant-layout-header {
background: #fff;
height: 64px;
}
.ant-layout-ceiling-demo .ant-layout-logo {
width:36px;
height: 32px;
border-radius: 6px;
margin: 16px 28px 16px 0;
float: left;
font-size: 26px;
text-align:center;
}
.ant-layout-wrapper {
padding: 0 50px;
}
.ant-layout-breadcrumb {
margin: 7px 0 -17px 24px;
}
.ant-layout-container {
background: #fff;
margin: 24px 0;
position: relative;
padding-top: 24px;
overflow: hidden;
}
.ant-layout-content {
background: #fff;
padding:0 24px 24px;
}
.ant-divider {
margin: 0 4px;
color: #999;
display: inline-block;
height: 8px;
width: 1px;
background: #ccc;
}

138
webapp/component/Login.jsx Normal file
View File

@@ -0,0 +1,138 @@
import React from 'react';
import {Card,Form,Input,Button,Icon,Alert,Col,message} from 'antd';
const FormItem = Form.Item;
import './Login.less';
import {fetchVersion} from './utils/fetchUtil';
import {trim} from './utils/validateUtil';
export default class Login extends React.Component{
state={
username:'',
password:'',
captcha:'',
rd:Math.random(),
showMsg:false,
msg:''
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleLogin=()=>{
if(!this.state.username){
this.setState({
showMsg:true,
msg:'请输入用户名'
});
return false;
}
if(!this.state.password){
this.setState({
showMsg:true,
msg:'请输入密码'
});
return false;
}
if(!this.state.captcha){
this.setState({
showMsg:true,
msg:'请输入验证码'
});
return false;
}
let formData = new FormData();
formData.append("loginName",this.state.username);
formData.append("passwd",this.state.password);
formData.append("captcha",this.state.captcha);
const hide = message.loading('正在执行中...', 0);
fetch(fetchVersion+'/merchant/login',{credentials: 'include',method: 'POST',
body:formData})
.then((res) => {
hide();
if(res.ok){
return res.json();
}
else{
Modal.error({
title: '系统错误',
content: '请检查是否有参数配置错误',
});
}
})
.then((data)=>{
if(data.success){
window.location.href="#/modelList";
}
else{
this.refs.captcha.click();
this.setState({
msg:data.msg,
showMsg:true
});
}
})
.catch((e) => {
console.log(e.message);
});
}
handleClick=(event)=>{
this.setState({
rd:Math.random()
})
}
handleKeyDown=(event)=>{
if(event.keyCode==13){
this.handleLogin();
}
}
render(){
const formItemLayout = {
labelCol: { span: 7 },
wrapperCol: { span: 13 },
};
return (
<div className="middleBox" onKeyDown={this.handleKeyDown}>
<Card>
<h2 style={{textAlign:"center",paddingBottom:10,borderBottom:"1px dashed #ececec"}}><Icon type="lock" />&nbsp;&nbsp;反欺诈系统管理平台</h2>
<Form horizontal style={{marginTop:30}}>
<FormItem {...formItemLayout} label="用户名">
<Input size="large" type="text" name="username" value={this.state.username} onChange={this.handleChange}/>
</FormItem>
<FormItem {...formItemLayout} label="密码">
<Input type="password" name="password" value={this.state.password} onChange={this.handleChange}/>
</FormItem>
<FormItem {...formItemLayout} label="验证码" style={{marginBottom:12}}>
<Col span="8">
<Input style={{width:60}} size="large" type="text" name="captcha" value={this.state.captcha} onChange={this.handleChange}/>
</Col>
<Col span="12">
<span><img id="captcha" ref="captcha" src={fetchVersion+"/common/getCaptcha?"+this.state.rd} onClick={this.handleClick}/> </span>
</Col>
</FormItem>
<FormItem wrapperCol={{ span: 13, offset: 7 }}>
<div style={this.state.showMsg?{display:"block"}:{display:"none"}}><Alert message={this.state.msg} type="error" /></div>
<Button type="primary" onClick={this.handleLogin}>登录</Button>
</FormItem>
</Form>
</Card>
</div>
);
}
}

View File

@@ -0,0 +1,10 @@
.middleBox{
width:440px;
margin:0 auto;
padding-top:200px;
}
#captcha{
cursor:pointer;
}

View File

@@ -0,0 +1,405 @@
import React from 'react';
import {Form,Input,Breadcrumb,Row,Col,Icon,Card,Select,Button,Cascader,Tooltip,message,Modal} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const OptGroup = Select.OptGroup;
import './Abstraction.less';
import ComplexCondition from './ComplexCondition';
import {generateScript,validateRules} from '../utils/groovyUtil';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class Abstraction extends React.Component{
constructor(props){
super(props);
if(props.abstraction!=undefined){
const abstraction=props.abstraction;
this.state={
name:abstraction.name,
label:abstraction.label,
aggregateType:abstraction.aggregateType+'',
searchField:abstraction.searchField,
searchIntervalType:abstraction.searchIntervalType+'',
searchIntervalValue:abstraction.searchIntervalValue,
functionField:abstraction.functionField,
comment:abstraction.comment,
ruleDefinition:abstraction.ruleDefinition==undefined?null:JSON.parse(abstraction.ruleDefinition),
ruleScript:abstraction.ruleScript
}
}
else{
this.state={
name:'',
label:'',
aggregateType:'',
searchField:'',
searchIntervalType:'',
searchIntervalValue:'',
functionField:'',
comment:'',
ruleDefinition:null,
ruleScript:''
}
}
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
if(this.props.abstraction.id!=0){
param.id=this.props.abstraction.id;
}
if(!this.state.ruleDefinition){
let fieldList=this.props.fieldList;
let fieldArr=this.state.searchField.split('.');
let fieldType='STRING';
for(let i=0;i<fieldList.length;i++){
if(fieldList[i].value==fieldArr[0]){
for(let j=0;j<fieldList[i].children.length;j++){
if(fieldList[i].children[j].value==fieldArr[1]){
if(fieldArr.length==2){
fieldType=fieldList[i].children[j].type;
}
else{
for(let k=0;k<fieldList[i].children[j].children.length;k++){
if(fieldList[i].children[j].children[k].value==fieldArr[2]){
fieldType=fieldList[i].children[j].children[k].type;
break;
}
}
}
break;
}
}
break;
}
}
this.state.ruleDefinition={
"linking":"All",
"conditions":[
{
"class":"SMPL",
"expressions":[
{
"column":this.state.searchField,
"type":fieldType,
"class":"ENTATTR"
}],
"enabled":true,
"operator":"IsNotNull"
}],
"class":"PDCT",
"enabled":true
}
}
if(!validateRules(this.state.ruleDefinition)){
Modal.error({
title:'请检查过滤条件是否配置完整'
});
return false;
}
param.modelId=this.props.modelId;
param.name=this.state.name;
param.label=this.state.label;
param.aggregateType=this.state.aggregateType;
param.searchField=this.state.searchField;
param.searchIntervalType=this.state.searchIntervalType;
param.searchIntervalValue=this.state.searchIntervalValue;
param.functionField=this.state.functionField;
param.comment=this.state.comment;
param.ruleDefinition=this.state.ruleDefinition;
param.ruleScript=generateScript(this.state.ruleDefinition,"Abstraction");
param.status=1;
FetchUtil('/abstraction/','PUT',JSON.stringify(param),
(data) => {
if(data.success==true){
message.success('保存成功!');
}
else{
message.error('保存失败!');
}
this.props.reload();
});
}
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
displayRender = (labels, selectedOptions) => labels.map((label, i) => {
const option = selectedOptions[i];
if (i === labels.length - 1) {
return (
<span key={option.value+i}>
{label}
</span>
);
}
return <span key={option.value+i}>{label} / </span>;
});
handleCascader=(name,value,selectedOptions)=>{
var state = this.state;
state[name] = trim(value.join('.'));
this.setState(state);
}
handleChangeCondition=(condition,index)=>{
let ruleDefinition=this.state.ruleDefinition;
if(condition==null){
ruleDefinition=null;
}
else{
ruleDefinition=condition;
}
this.setState({
ruleDefinition:ruleDefinition
})
}
handleAddCondition=()=>{
let ruleDefinition=this.state.ruleDefinition;
if(ruleDefinition==null){
ruleDefinition={
"class": "PDCT",
"enabled": true,
"linking": "All",
"conditions": [
{
"class": "SMPL",
"enabled": true,
"operator": "",
"expressions": [
{
"class": "ENTATTR",
"type": "",
"column": ""
}
]
}
]
}
}
else{
ruleDefinition.conditions.push(
{
"class": "SMPL",
"enabled": true,
"operator": "",
"expressions": [
{
"class": "ENTATTR",
"type": "",
"column": ""
}
]
}
);
}
this.setState({
ruleDefinition:ruleDefinition
})
}
render(){
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
};
let ruleDefinition=this.state.ruleDefinition;
let validate={
label:{
help:'',
status:'success'
},
aggregateType:{
help:'',
status:'success'
},
searchField:{
help:'',
status:'success'
},
searchInterval:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入指标名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的指标名';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.aggregateType){
validate.aggregateType.help='请选择聚合条件';
validate.aggregateType.status='warning';
isValidated=false;
}
if(!this.state.searchField){
validate.searchField.help='请输入搜索字段';
validate.searchField.status='warning';
isValidated=false;
}
if(!this.state.searchIntervalValue){
validate.searchInterval.help='请输入时间片';
validate.searchInterval.status='warning';
isValidated=false;
}else if(!/^[0-9]+$/.test(this.state.searchIntervalValue)){
validate.searchInterval.help='时间片必须为数字';
validate.searchInterval.status='error';
isValidated=false;
}else if(!this.state.searchIntervalType){
validate.searchInterval.help='请选择时间单位';
validate.searchInterval.status='warning';
isValidated=false;
}
return (
<div>
<div style={{width:750}}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="指标名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'指标显示名称,一般为中文,如"100秒内设备注册数"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="聚合条件:" help={validate.aggregateType.help} validateStatus={validate.aggregateType.status}>
<Select value={this.state.aggregateType} onChange={this.handleSelect.bind(this,'aggregateType')}>
<Option value="">请选择</Option>
<Option value="1">求总数</Option>
<Option value="2">求不同项总数</Option>
<Option value="3">求和</Option>
<Option value="4">求平均值</Option>
<Option value="5">求最大值</Option>
<Option value="6">求最小值</Option>
</Select>
</FormItem>
<FormItem required={true} {...formItemLayout} label="搜索字段:" help={validate.searchField.help} validateStatus={validate.searchField.status}>
<Cascader
title={this.state.searchField.split(".")}
options={this.props.fieldList}
allowClear={false}
value={this.state.searchField.split(".")}
displayRender={this.displayRender}
onChange={this.handleCascader.bind(this,'searchField')}
/>
</FormItem>
<FormItem {...formItemLayout} label="统计字段:">
<Cascader
title={this.state.functionField.split(".")}
options={this.props.fieldList}
allowClear={false}
value={this.state.functionField.split(".")}
displayRender={this.displayRender}
onChange={this.handleCascader.bind(this,'functionField')}
/>
<Button style={{marginTop:10}} onClick={()=>{this.setState({functionField:''})}}>清空统计字段</Button>
</FormItem>
<FormItem required={true} {...formItemLayout} label="时间片:" help={validate.searchInterval.help} validateStatus={validate.searchInterval.status}>
<Row>
<Col span={4} style={{marginTop:1}}>
<Input type="text" name="searchIntervalValue" value={this.state.searchIntervalValue} onChange={this.handleChange}/>
</Col>
<Col span={4} offset={1}>
<Select value={this.state.searchIntervalType} onChange={this.handleSelect.bind(this,'searchIntervalType')}>
<Option value="">请选择</Option>
<Option value="1"></Option>
<Option value="2"></Option>
<Option value="5"></Option>
<Option value="11"></Option>
<Option value="12"></Option>
<Option value="13"></Option>
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'搜索某段时间的数据,数值为正数,代表范围区间,如 3 ,再配合后面的时间单位,如 月就表示3个月内的数据'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="备注:">
<Input type="textarea" name="comment" value={this.state.comment} onChange={this.handleChange}/>
</FormItem>
</Form>
</div>
<Row>
<Col span={4} offset={4}>
<Tooltip title="添加过滤条件" onClick={this.handleAddCondition}><span className="addRule"><Icon type="plus" />&nbsp;添加过滤条件</span></Tooltip>
<br/>
&nbsp;
</Col>
</Row>
<ComplexCondition fieldList={this.props.fieldList} dataList={this.props.dataList} condition={this.state.ruleDefinition} changeParentCondition={this.handleChangeCondition} index={0}/>
<div className="separate"></div>
<Row>
<Col span={4} offset={4}>
<Button type="primary" onClick={this.handleSubmit.bind(this,isValidated)}>保存</Button>{' '}
<Button type="primary" onClick={this.props.delete}>删除</Button>
</Col>
</Row>
</div>
);
}
}

View File

@@ -0,0 +1,22 @@
.addRule{
color:rgb(102, 102, 102);
cursor:pointer;
font-size:16px;
}
.addRule:hover{
color:black;
}
.separate{
width:100%;
height:0;
border-bottom:rgb(102, 102, 102) 1px dashed;
margin-top:5px;
margin-bottom:10px;
}
.condition-row{
height:26px;
margin-bottom:10px;
}

View File

@@ -0,0 +1,191 @@
import React,{Component} from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Spin} from 'antd';
import { Link } from 'react-router';
const FormItem = Form.Item;
const Option = Select.Option;
import CollapseGroup from '../common/CollapseGroup';
import Collapse from '../common/Collapse';
import Abstraction from './Abstraction';
import {FetchUtil} from '../utils/fetchUtil';
export default class AbstractionList extends Component{
constructor(props){
super(props);
this.state={
name:'',
label:'',
status:"1",
tData:[],
pageNo:1,
rowCount:0,
loading:true,
model:null,
fieldList:[],
dataList:[],
}
FetchUtil('/model/'+this.props.params.id,'GET','',
(data) => {
const model=data.data.model;
this.setState({
model:model
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.params.id;
param.aggregateType=this.state.aggregateType;
param.name=this.state.name;
param.label=this.state.label;
param.status=this.state.status;
FetchUtil('/abstraction','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
this.setState({
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = value;
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
handleAdd=()=>{
let tData=this.state.tData;
tData.push({
id:0,
name:'',
label:'',
aggregateType:'',
searchField:'',
searchIntervalType:'',
searchIntervalValue:'',
functionField:'',
comment:'',
ruleDefinition:null,
ruleScript:''
});
this.setState({
tData:tData
})
}
handleDelete=(index)=>{
let tData=this.state.tData;
let id=tData[index].id;
if(id!=0){
FetchUtil('/abstraction/','DELETE','['+id+']',
(data) => {
if(data.success==true){
message.success('删除成功!');
}
else{
message.error('删除失败!');
}
this.fetchTableData();
});
}
else{
tData.splice(index,1);
this.setState({
tData:tData
});
}
}
componentDidMount() {
this.fetchTableData();
FetchUtil('/abstraction/datacolumns/'+this.props.params.id,'GET','',
(data) => {
this.setState({
fieldList:data.data.list
});
});
FetchUtil('/datalist/list/'+this.props.params.id,'GET','',
(data) => {
this.setState({
dataList:data.data.list
});
});
}
render(){
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="指标名:">
<Input value={this.state.name} name="name" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<Button type="primary" onClick={this.handleAdd}>新增</Button>
</Form>
</div>
<Spin size="large" spinning={this.state.loading}>
<CollapseGroup>
{this.state.tData.filter((info,index)=>{
if(this.state.name){
var reg = new RegExp('('+ this.state.name+')','gi');
return reg.test(info.label);
}else {
return true;
}
}).map((info,index)=>{
return (<Collapse key={info.id+""+index} title={info.label?info.label:'新增(请保存)'}>
<Abstraction abstraction={info} modelId={this.props.params.id} fieldList={this.state.fieldList} dataList={this.state.dataList} reload={this.fetchTableData} delete={this.handleDelete.bind(this,index)}/>
</Collapse>);
})
}
</CollapseGroup>
</Spin>
<div>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,126 @@
import React from 'react';
import {Form,Input,Row,Col,Card,Select,Button,Cascader,Tooltip,Icon} from 'antd';
const Option = Select.Option;
import SimpleCondition from './SimpleCondition';
export default class ComplexCondition extends React.Component{
constructor(props){
super(props);
}
handleSelect=(name,value)=>{
let condition=this.props.condition;
let index=this.props.index;
condition.linking=value;
this.props.changeParentCondition(condition,index);
}
handleChangeCondition=(childCondition,childIndex)=>{
let condition=this.props.condition;
let index=this.props.index;
if(childCondition==null){
condition.conditions.splice(childIndex,1);
}
else{
condition.conditions[childIndex]=childCondition;
}
if(condition.conditions.length==0){
this.props.changeParentCondition(null,index);
}
else{
this.props.changeParentCondition(condition,index);
}
}
handleSimpleCondition=()=>{
let condition=this.props.condition;
let index=this.props.index;
condition.conditions.push(
{
"class": "SMPL",
"enabled": true,
"operator": "",
"expressions": [
{
"class": "ENTATTR",
"type": "STRING",
"column": ""
}
]
}
)
this.props.changeParentCondition(condition,index);
}
handleComplexCondition=()=>{
let condition=this.props.condition;
let index=this.props.index;
condition.conditions.push(
{
"class": "PDCT",
"enabled": true,
"linking": "All",
"conditions": [
{
"class": "SMPL",
"enabled": true,
"operator": "",
"expressions": [
{
"class": "ENTATTR",
"type": "STRING",
"column": ""
}
]
}
]
}
);
this.props.changeParentCondition(condition,index);
}
render(){
if(this.props.condition==null){
return (<div/>);
}
else{
return (
<div style={{marginLeft:20}}>
<div className="condition-row">
<Select style={{width:100}} readOnly={this.props.readOnly} value={this.props.condition.linking} defaultValue={'All'} onChange={this.handleSelect.bind(this,'linking')}>
<Option value="All">All</Option>
<Option value="Any">Any</Option>
<Option value="NotAll">NotAll</Option>
<Option value="None">None</Option>
</Select>条件成立 &nbsp;&nbsp;
{this.props.readOnly!=true?<Tooltip title="添加简单条件" onClick={this.handleSimpleCondition}><span style={{cursor:"pointer",marginRight:"10px"}}><Icon type="plus" />简单</span></Tooltip>:''}
{this.props.readOnly!=true?<Tooltip title="添加复杂条件" onClick={this.handleComplexCondition}><span style={{cursor:"pointer"}}><Icon type="plus" />复杂</span></Tooltip>:''}
</div>
{this.props.condition.conditions.map((info,index)=>{
if(info.class=='SMPL'){
return (
<SimpleCondition key={index} readOnly={this.props.readOnly} fieldList={this.props.fieldList} dataList={this.props.dataList} condition={info} changeParentCondition={this.handleChangeCondition} index={index} />
);
}
else{
return (
<ComplexCondition key={index} readOnly={this.props.readOnly} fieldList={this.props.fieldList} dataList={this.props.dataList} condition={info} changeParentCondition={this.handleChangeCondition} index={index}/>
);
}
})}
</div>
);
}
}
}

View File

@@ -0,0 +1,166 @@
import React from 'react';
import {Form,Input,Row,Col,Card,Select,Button,Cascader,Tooltip,Icon} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const OptGroup = Select.OptGroup;
import {Operator,operatorMap} from '../utils/operatorUtil';
export default class SimpleCondition extends React.Component{
constructor(props){
super(props);
}
handleConditionColumn=(name,value,selectedOptions)=>{
let type=selectedOptions[selectedOptions.length-1].type;
let condition=this.props.condition;
let index=this.props.index;
if(name=='expression'){
condition.operator='';
condition.expressions=condition.expressions.slice(0,1);
condition.expressions[0]={
class: "ENTATTR",
type: type,
column: value.join('.')
}
}
else if(name=='expressionOption'){
if(condition.expressions.length<=1){
condition.expressions.push({
class: "ENTATTR",
type: type,
column: value.join('.')
})
}
else{
condition.expressions[1]={
class: "ENTATTR",
type: type,
column: value.join('.')
}
}
}
this.props.changeParentCondition(condition,index);
}
handleOperator=(value)=>{
let condition=this.props.condition;
let index=this.props.index;
condition.operator=value;
condition.expressions=condition.expressions.slice(0,1);
this.props.changeParentCondition(condition,index);
}
handleDataList=(value)=>{
let condition=this.props.condition;
let index=this.props.index;
if(condition.expressions.length<=1){
condition.expressions.push({
class: "CONST",
type: "LIST",
value: value
})
}
else{
condition.expressions[1]={
class: "CONST",
type: "LIST",
value: value
}
}
this.props.changeParentCondition(condition,index);
}
handleInput=(e)=>{
var name = e.target.name;
var value = e.target.value;
let condition=this.props.condition;
let index=this.props.index;
const type=condition.expressions[0].type;
if(condition.expressions.length<=1){
condition.expressions.push({
class: "CONST",
type: type,
value: value
})
}
else{
condition.expressions[1]={
class: "CONST",
type: type,
value: value
}
}
this.props.changeParentCondition(condition,index);
}
handleDelete=()=>{
this.props.changeParentCondition(null,this.props.index);
}
displayRender = (labels, selectedOptions) => labels.map((label, i) => {
const option = selectedOptions[i];
if (i === labels.length - 1) {
return (
<span key={option.value+i}>
{label}
</span>
);
}
return <span key={option.value+i}>{label} / </span>;
})
render(){
let condition=this.props.condition;
let expression=condition.expressions[0];
let expressionOption=condition.expressions[1];
return (
<div className="condition-row" style={{marginLeft:20}}>
<Cascader
options={this.props.fieldList}
allowClear={false}
value={expression.column.split('.')}
displayRender={this.displayRender}
onChange={this.handleConditionColumn.bind(this,'expression')}
readOnly={this.props.readOnly}
/>
<Select dropdownMatchSelectWidth={false} readOnly={this.props.readOnly} value={condition.operator} onChange={this.handleOperator}>
{operatorMap[expression.type].map(op=>
<Option key={op.value} value={op.value}>{op.label}</Option>
)}
</Select>
{condition.operator==''?'':
Operator[condition.operator].nextType=='input'?(
<Input style={{width:200}} type="text" readOnly={this.props.readOnly} value={expressionOption==undefined?'':expressionOption.value} onChange={this.handleInput}/>):
Operator[condition.operator].nextType=='list'?(
<Select dropdownMatchSelectWidth={false} readOnly={this.props.readOnly} value={expressionOption==undefined?'':expressionOption.value} onChange={this.handleDataList}>
{this.props.dataList.map(op=>
<Option key={op.id} value={op.name}>{op.label}</Option>
)}
</Select>
):
Operator[condition.operator].nextType=='field'?(
<Cascader
options={this.props.fieldList}
allowClear={false}
value={expressionOption==undefined?'':expressionOption.column.split('.')}
displayRender={this.displayRender}
onChange={this.handleConditionColumn.bind(this,'expressionOption')}
readOnly={this.props.readOnly}
/>
):''
}
{this.props.readOnly!=true?<Tooltip title="删除" onClick={this.handleDelete}><Icon type="delete" style={{fontSize:16,lineHeight:"22px"}}/></Tooltip>:''}
</div>
);
}
}

View File

@@ -0,0 +1,171 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message} from 'antd';
import { Link } from 'react-router';
const FormItem = Form.Item;
const Option = Select.Option;
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
import AddActivation from './modal/AddActivation';
import EditActivation from './modal/EditActivation';
export default class Activation extends React.Component{
constructor(props){
super(props);
this.state={
name:'',
label:'',
tData:[],
pageNo:1,
rowCount:0,
loading:true,
model:null
}
FetchUtil('/model/'+this.props.params.id,'GET','',
(data) => {
const model=data.data.model;
this.setState({
model:model
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.params.id;
param.name=this.state.name;
param.label=this.state.label;
FetchUtil('/activation','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
this.setState({
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
componentDidMount() {
this.fetchTableData();
}
deleteActivation=(id)=>{
FetchUtil('/activation/','DELETE','['+id+']',
(data) => {
message.info('删除成功!');
this.fetchTableData();
});
}
render(){
/*定义表格列*/
const columns = [
{
title: '序号',
dataIndex: 'id',
render:(t,r,i)=>{
return i+1;
}
},
{
title: '策略名',
dataIndex: 'label'
},{
title: '备注',
dataIndex: 'comment'
},{
title: '警戒值',
dataIndex: 'median'
},{
title: '拒绝值',
dataIndex: 'high'
},{
title: '操作',
dataIndex: 'handle',
render:
(t,r,i) => {
return(
<span>
<EditActivation modelId={this.props.params.id} row={r} reload={this.fetchTableData}/>
<span className="ant-divider"></span>
<Tooltip title="管理规则"><Link to={"/ruleList/"+r.modelId+"/"+r.id}>管理规则</Link></Tooltip>
<span className="ant-divider"></span>
<Popconfirm placement="bottomRight" title={'确认删除该字段吗?'} onConfirm={this.deleteActivation.bind(this,r.id)}>
<Tooltip title="删除"><a style={{color:'#FD5B5B'}}>删除</a></Tooltip>
</Popconfirm>
</span>
);
}
}];
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="策略名:">
<Input value={this.state.name} name="name" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<AddActivation modelId={this.props.params.id} reload={this.fetchTableData} />
</Form>
</div>
<div id="table">
<Table
dataSource={this.state.tData.filter((item,index,array)=>{
var reg = new RegExp('('+ this.state.name+')','gi');
if(this.state.name){
return (reg.test(item.label));
}else {
return true;
}
})}
columns={columns}
size="middle"
pagination={false}
loading={this.state.loading}
/>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,144 @@
import React from 'react';
import {Form,Input,InputNumber,Breadcrumb,Row,Col,Icon,Card,Select,Button,Cascader,Tooltip,message,Modal} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
import ComplexCondition from '../abstraction/ComplexCondition';
export default class HistoryRecord extends React.Component{
constructor(props){
super(props);
this.state={
height:40
}
}
slideDown=()=>{
if(this.state.height=="auto"){
return;
}
let height = this.refs.content1.offsetHeight+this.refs.content2.offsetHeight+40;
if(this.state.height<height){
this.setState({
height:this.state.height+15
},()=>{
setTimeout(this.slideDown,1);
})
}else{
this.setState({
height:"auto"
})
this.refs.content.className = 'up'
}
}
slideUp=()=>{
let height = this.refs.content1.offsetHeight+this.refs.content2.offsetHeight+40;
if(this.state.height=="auto"){
this.state.height=height;
}
if(this.state.height>55){
this.setState({
height:this.state.height-15
},()=>{
setTimeout(this.slideUp,1);
})
}
else{
this.setState({
height:40
})
this.refs.content.className = 'down'
}
}
handleClick=()=>{
if(this.refs.content.className === 'down'){
this.slideDown();
}
if(this.refs.content.className === 'up'){
this.slideUp();
}
}
render(){
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
};
let abstractionList=[];
if(this.props.fieldList.length>0){
abstractionList=this.props.fieldList.filter(x=>x.value=='abstractions')[0].children;
}
const ruleHistory=this.props.ruleHistory;
return (
<div ref="content" className="down" style={{border:'1px solid #d9d9d9',borderRadius:'5px',padding:'10px',height:this.state.height,overflow:'hidden'}}>
<div style={{padding:'0 0 20px 20px',cursor:'pointer'}} onClick={this.handleClick}>用户{ruleHistory.merchantCode}修改</div>
<div ref="content1" style={{width:750}}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="显示名称:">
<Row>
<Col span={20}>
<Input type="text" name="label" value={ruleHistory.label} readOnly/>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="命中初始得分:">
<Row>
<Col span={4}>
<InputNumber name="initScore" value={ruleHistory.initScore} readOnly/>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="命中基数:">
<Row>
<Col span={4}>
<InputNumber name="baseNum" value={ruleHistory.baseNum} readOnly/>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="操作符:">
<Select value={ruleHistory.operator} readOnly>
<Option value="NONE"></Option>
<Option value="ADD"></Option>
<Option value="DEC"></Option>
<Option value="MUL"></Option>
<Option value="DIV"></Option>
</Select>
</FormItem>
<FormItem {...formItemLayout} label="指标字段:">
<Select value={ruleHistory.abstractionName} readOnly>
{abstractionList==undefined?null:abstractionList.map((x,index)=><Option key={x.value+index} value={x.value}>{x.label}</Option>)}
</Select>
</FormItem>
<FormItem required={true} {...formItemLayout} label="比率:">
<Row>
<Col span={4}>
<InputNumber name="rate" value={ruleHistory.rate} readOnly/>
</Col>
</Row>
</FormItem>
</Form>
</div>
<div ref="content2">
<div>
<Tooltip title="添加过滤条件" onClick={this.handleAddCondition}><span className="addRule"><Icon type="plus" />&nbsp;添加过滤条件</span></Tooltip>
</div>
<ComplexCondition readOnly fieldList={this.props.fieldList} dataList={this.props.dataList} condition={ruleHistory.ruleDefinition==undefined?null:JSON.parse(ruleHistory.ruleDefinition)} changeParentCondition={()=>{}} index={0}/>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,121 @@
import React,{Component} from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Timeline,Icon} from 'antd';
import CollapseGroup from '../common/CollapseGroup';
import Collapse from '../common/Collapse';
import { Link } from 'react-router';
import moment from 'moment';
const FormItem = Form.Item;
const Option = Select.Option;
import HistoryRecord from './HistoryRecord';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class HistoryRecordList extends Component{
constructor(props){
super(props);
this.state={
label:'',
tData:[],
pageNo:1,
rowCount:0,
loading:true,
model:null,
fieldList:[],
dataList:[]
}
FetchUtil('/model/'+this.props.params.id,'GET','',
(data) => {
const model=data.data.model;
this.setState({
model:model
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.ruleId=this.props.params.ruleId;
//此处为单条策略历史记录接口的获取参数中应该有本条策略的id否则无法区分
FetchUtil('/ruleHistory','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
this.setState({
label:data.data.page.list[0].label,
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
componentWillMount() {
this.fetchTableData();
FetchUtil('/activation/datacolumns/'+this.props.params.id,'GET','',
(data) => {
this.setState({
fieldList:data.data.list
});
});
FetchUtil('/datalist/list/'+this.props.params.id,'GET','',
(data) => {
this.setState({
dataList:data.data.list
});
});
}
render(){
return (
<div className="ant-layout-content">
<div id="header" style={{fontSize:'16px',marginBottom:'20px'}}>
<Link to={"/ruleList/"+this.props.params.id+"/"+this.props.params.activationId}><Icon type="left-circle" style={{marginRight:'10px'}} />{this.state.label}</Link><span style={{marginLeft:'20px'}}>历史记录</span>
</div>
<Timeline style={{marginLeft:'150px'}}>
{this.state.tData.map((info,index)=>{
return (
<Timeline.Item key={index}>
<span style={{position:'absolute',left:'-120px'}}>{moment(info.updateTime).format('YYYY-MM-DD HH:mm:ss')}</span>
<HistoryRecord ruleHistory={info} modelId={this.props.params.id} activationId={this.props.params.activationId} fieldList={this.state.fieldList} dataList={this.state.dataList} reload={this.fetchTableData}/>
</Timeline.Item>
);
})
}
</Timeline>
</div>
);
}
}

View File

@@ -0,0 +1,341 @@
import React from 'react';
import {Form,Input,InputNumber,Breadcrumb,Row,Col,Icon,Card,Select,Button,Cascader,Tooltip,message,Modal} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
import ComplexCondition from '../abstraction/ComplexCondition';
import {generateScript,validateRules} from '../utils/groovyUtil';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class Rule extends React.Component{
constructor(props){
super(props);
if(props.rule!=undefined){
const rule=props.rule;
this.state={
label:rule.label,
initScore:rule.initScore,
baseNum:rule.baseNum,
operator:rule.operator,
abstractionName:rule.abstractionName,
rate:rule.rate,
ruleDefinition:rule.ruleDefinition==undefined?null:JSON.parse(rule.ruleDefinition),
scripts:rule.scripts
}
}
else{
this.state={
label:'',
initScore:'0',
baseNum:'0',
operator:'NONE',
abstractionName:'',
rate:'100',
ruleDefinition:null,
scripts:''
}
}
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
//state[name] = trim(value);
state[name] = value;
if(name=='operator'&&value=='NONE'){
state['abstractionName']='';
}
this.setState(state);
}
handleChangeCondition=(condition,index)=>{
let ruleDefinition=this.state.ruleDefinition;
if(condition==null){
ruleDefinition=null;
}
else{
ruleDefinition=condition;
}
this.setState({
ruleDefinition:ruleDefinition
})
}
handleAddCondition=()=>{
let ruleDefinition=this.state.ruleDefinition;
if(ruleDefinition==null){
ruleDefinition={
"class": "PDCT",
"enabled": true,
"linking": "All",
"conditions": [
{
"class": "SMPL",
"enabled": true,
"operator": "",
"expressions": [
{
"class": "ENTATTR",
"type": "",
"column": ""
}
]
}
]
}
}
this.setState({
ruleDefinition:ruleDefinition
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
if(!this.state.ruleDefinition){
Modal.error({
title:'请至少配置一条过滤规则'
});
return false;
}
if(!validateRules(this.state.ruleDefinition)){
Modal.error({
title:'请检查过滤条件是否配置完整'
});
return false;
}
var param={};
if(this.props.rule.id!=0){
param.id=this.props.rule.id;
}
param.modelId=this.props.modelId;
param.activationId=this.props.activationId;
param.label=this.state.label;
param.initScore=this.state.initScore,
param.baseNum=this.state.baseNum,
param.operator=this.state.operator,
param.abstractionName=this.state.abstractionName,
param.rate=this.state.rate,
param.ruleDefinition=this.state.ruleDefinition;
param.scripts=generateScript(this.state.ruleDefinition,"Activation");
param.status=1;
FetchUtil('/rule/','PUT',JSON.stringify(param),
(data) => {
if(data.success==true){
if(this.props.rule.id==0){
message.success('添加成功!');
}
else{
message.success('修改成功!');
}
}
else{
message.error('修改失败!');
}
this.props.reload();
});
}
}
render(){
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
};
let abstractionList=[];
if(this.props.fieldList.length>0){
abstractionList=this.props.fieldList.filter(x=>x.value=='abstractions')[0].children;
}
let validate={
label:{
help:'',
status:'success'
},
initScore:{
help:'',
status:'success'
},
baseNum:{
help:'',
status:'success'
},
abstractionName:{
help:'',
status:'success'
},
rate:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入显示名称';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.initScore){
validate.initScore.help='请输入初始得分';
validate.initScore.status='warning';
isValidated=false;
}
if(this.state.baseNum < 0){
validate.baseNum.help='请输入基数';
validate.baseNum.status='warning';
isValidated=false;
}
if(this.state.operator!='NONE'&&!this.state.abstractionName){
validate.abstractionName.help='请选择抽象字段';
validate.abstractionName.status='warning';
isValidated=false;
}
if(this.state.rate < 0 ){
validate.rate.help='请输入rate';
validate.rate.status='warning';
isValidated=false;
}
// if(!/^[0-9]+$/.test(this.state.initScore)){
// validate.initScore.help='initScore必须为数字';
// validate.initScore.status='error';
// isValidated=false;
// }
// if(!/^[0-9]+$/.test(this.state.baseNum)){
// validate.baseNum.help='baseNum必须为数字';
// validate.baseNum.status='error';
// isValidated=false;
// }
// if(!/^[0-9]+$/.test(this.state.rate)){
// validate.rate.help='rate必须为数字';
// validate.rate.status='error';
// isValidated=false;
// }
return (
<div>
<div style={{width:750}}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="显示名称:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'规则名称,一般为中文,如"1天内设备注册次数过多或注册时间间隔过短"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="命中初始得分:" help={validate.initScore.help} validateStatus={validate.initScore.status}>
<Row>
<Col span={4}>
<InputNumber name="initScore" value={this.state.initScore} onChange={this.handleSelect.bind(this,'initScore')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'初始得分,在此基础上进行累加计算'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="命中基数:" help={validate.baseNum.help} validateStatus={validate.baseNum.status}>
<Row>
<Col span={4}>
<InputNumber name="baseNum" value={this.state.baseNum} onChange={this.handleSelect.bind(this,'baseNum')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'配合操作符,与指标字段进行运算'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="操作符:">
<Select value={this.state.operator} onChange={this.handleSelect.bind(this,'operator')}>
<Option value="NONE"></Option>
<Option value="ADD"></Option>
<Option value="DEC"></Option>
<Option value="MUL"></Option>
<Option value="DIV"></Option>
</Select>
</FormItem>
<FormItem {...formItemLayout} label="指标字段:" help={validate.abstractionName.help} validateStatus={validate.abstractionName.status}>
<Select disabled={this.state.operator=='NONE'} value={this.state.abstractionName} onChange={this.handleSelect.bind(this,'abstractionName')}>
{abstractionList==undefined?null:abstractionList.map((x,index)=><Option key={x.value+index} value={x.value}>{x.label}</Option>)}
</Select>
</FormItem>
<FormItem required={true} {...formItemLayout} label="比率:" help={validate.rate.help} validateStatus={validate.rate.status}>
<Row>
<Col span={4}>
<InputNumber name="rate" value={this.state.rate} onChange={this.handleSelect.bind(this,'rate')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'当指标字段值过大或者过小时,对指标字段进行放大或者缩小,使命中分数更加合理'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</div>
<div>
<div>
<Tooltip title="添加过滤条件" onClick={this.handleAddCondition}><span className="addRule"><Icon type="plus" />&nbsp;添加过滤条件</span></Tooltip>
</div>
<ComplexCondition fieldList={this.props.fieldList} dataList={this.props.dataList} condition={this.state.ruleDefinition} changeParentCondition={this.handleChangeCondition} index={0}/>
<div className="separate"></div>
<Row>
<Col span={4} offset={4}>
<Button type="primary" onClick={this.handleSubmit.bind(this,isValidated)}>保存</Button>{' '}
<Button type="primary" onClick={this.props.delete}>删除</Button>
</Col>
</Row>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,304 @@
import React,{Component} from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Icon,Spin} from 'antd';
import CollapseGroup from '../common/CollapseGroup';
import Collapse from '../common/Collapse';
const FormItem = Form.Item;
const Option = Select.Option;
import Rule from './Rule';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
import {fetchVersion} from '../utils/fetchUtil';
export default class RuleList extends Component{
constructor(props){
super(props);
this.state={
label:'',
status:"1",
tData:[],
pageNo:1,
rowCount:0,
loading:true,
model:null,
fieldList:[],
dataList:[],
activation:null,
ruleOrder:[]
}
FetchUtil('/model/'+this.props.params.id,'GET','',
(data) => {
const model=data.data.model;
this.setState({
model:model
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.activationId=this.props.params.activationId;
param.label=this.state.label;
FetchUtil('/rule','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
let ruleOrder=data.data.ruleOrder?data.data.ruleOrder.split(','):[];
let ruleList=this.getOrderedRules(ruleOrder,data.data.page.list);
if(data.data.page.list.length>0||ruleOrder.length>ruleList.length){
let unOrderedList=data.data.page.list;
ruleList=ruleList.concat(unOrderedList);
ruleOrder=ruleList.map(x=>x.id+'');
this.handleReOrder(ruleOrder);
}
this.setState({
tData:ruleList,
ruleOrder:ruleOrder
})
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
handleAdd=()=>{
let tData=this.state.tData;
tData.push({
id:0,
label:'',
initScore:'0',
baseNum:'0',
operator:'NONE',
abstractionName:'',
rate:'100',
ruleDefinition:null,
scripts:''
});
this.setState({
tData:tData
})
}
handleDelete=(index)=>{
let tData=this.state.tData;
let id=tData[index].id;
if(id!=0){
FetchUtil('/rule/','DELETE','['+id+']',
(data) => {
if(data.success==true){
message.success('删除成功!');
}
else{
message.error('删除失败!');
}
this.fetchTableData();
});
}
else{
tData.splice(index,1);
this.setState({
tData:tData
});
}
}
handleSwitch=(rule)=>{
rule.status=(rule.status==0?1:0);
FetchUtil('/rule/','PUT',JSON.stringify(rule),
(data) => {
if(data.success==true){
if(rule.status==1){
message.success('启用成功!');
}
else{
message.success('禁用成功!');
}
}
else{
message.error(data.msg);
}
this.setState({});
});
}
handleReOrder=(ruleOrder=this.state.ruleOrder,showMessage=false)=>{
let formData = new FormData();
formData.append("activationId",this.props.params.activationId);
formData.append("ruleOrder",ruleOrder.join(','));
fetch(fetchVersion+'/activation/updateOrder',{credentials: 'include',method: 'POST',
body:formData})
.then((res) => {
if(res.ok){
return res.json();
}
else{
Modal.error({
title: '系统错误',
content: '请检查是否有参数配置错误',
});
}
})
.then((data)=>{
if(showMessage){
message.success('排序成功!');
}
})
.catch((e) => {
console.log(e.message);
});
}
getOrderedRules=(ruleOrder,ruleData)=>{
let resultList=[];
for(let i=0;i<ruleOrder.length;i++){
for(let j=0;j<ruleData.length;j++){
if(ruleOrder[i]==ruleData[j].id){
resultList.push(ruleData.splice(j,1)[0]);
break;
}
}
}
return resultList;
}
handleDrag=(ruleId,pos)=>{
let ruleOrder=this.state.ruleOrder;
let ruleList=this.state.tData;
let index=ruleOrder.indexOf(ruleId+'');
if(index==pos){
return;
}
let order=ruleOrder.splice(index,1);
let rule=ruleList.splice(index,1);
ruleOrder.splice(pos,0,order[0]);
ruleList.splice(pos,0,rule[0]);
this.state.ruleOrder=ruleOrder;
this.state.tData=ruleList;
this.setState({
});
// let moveY=0;
// if(distance>=0){
// moveY=Math.floor(distance/48);
// }
// else{
// moveY=Math.ceil(distance/48);
// }
// if(moveY==0){return;}
// if(moveY<0){
// let order=ruleOrder.splice(index,1);
// let rule=ruleList.splice(index,1);
// ruleOrder.splice(index+moveY,0,order[0]);
// ruleList.splice(index+moveY,0,rule[0]);
// this.setState({
// ruleOrder:ruleOrder,
// tData:ruleList
// });
// }
// else{
// let order=ruleOrder.splice(index,1);
// let rule=ruleList.splice(index,1);
// ruleOrder.splice(index+moveY,0,order[0]);
// ruleList.splice(index+moveY,0,rule[0]);
// this.setState({
// ruleOrder:ruleOrder,
// tData:ruleList
// });
// }
}
componentDidMount() {
this.fetchTableData();
FetchUtil('/activation/datacolumns/'+this.props.params.id,'GET','',
(data) => {
this.setState({
fieldList:data.data.list
});
});
FetchUtil('/datalist/list/'+this.props.params.id,'GET','',
(data) => {
this.setState({
dataList:data.data.list
});
});
}
render(){
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="显示名称:">
<Input value={this.state.label} name="label" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<Button type="primary" onClick={this.handleAdd}>新增</Button>
</Form>
</div>
<Spin size="large" spinning={this.state.loading}>
<CollapseGroup handleDrag={this.handleDrag} handleReOrder={this.handleReOrder} draggable={!this.state.label}>
{this.state.tData.filter((info,index)=>{
if(this.state.label){
var reg = new RegExp('('+ this.state.label+')','gi');
return reg.test(info.label);
}else {
return true;
}
}).map((info,index)=>{
return (<Collapse ruleId={info.id} ruleOrder={this.state.ruleOrder} switcher={info.status=='1'} modelId={this.props.params.id} activationId={this.props.params.activationId} type="calendar" onSwitch={this.handleSwitch.bind(this,info)} key={info.id?info.id:index} title={info.label?info.label:'新增(请保存)'}>
<Rule rule={info} modelId={this.props.params.id} activationId={this.props.params.activationId} fieldList={this.state.fieldList} dataList={this.state.dataList} reload={this.fetchTableData} delete={this.handleDelete.bind(this,index)}/>
</Collapse>);
})
}
</CollapseGroup>
</Spin>
{/*
<div>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
*/}
</div>
);
}
}

View File

@@ -0,0 +1,200 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class AddActivation extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
activationName:'',
label:'',
comment:'',
bottom:'',
median:'',
high:'',
}
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = value;
this.setState(state);
}
showModal=()=>{
this.setState({
activationName:'',
label:'',
comment:'',
bottom:'',
median:'',
high:'',
visible:true
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.modelId=this.props.modelId;
param.activationName=this.state.activationName;
param.label=this.state.label;
param.comment=this.state.comment;
param.bottom=this.state.bottom;
param.median=this.state.median;
param.high=this.state.high;
param.status=1;
FetchUtil('/activation/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('添加成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
label:{
help:'',
status:'success'
},
median:{
help:'',
status:'success'
},
high:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入策略名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的策略名';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.median){
validate.median.help='请输入警戒值';
validate.median.status='warning';
isValidated=false;
}else if(!/^\d{1,3}/.test(this.state.median)){
validate.median.help='警戒值必须为数字';
validate.median.status='error';
isValidated=false;
}
if(!this.state.high){
validate.high.help='请输入拒绝值';
validate.high.status='warning';
isValidated=false;
}else if(!/^\d{1,3}/.test(this.state.high)){
validate.high.help='拒绝值必须为数字';
validate.high.status='error';
isValidated=false;
}
return (
<span>
<Button onClick={this.showModal} type="primary">新增</Button>
<Modal title="新增字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="策略名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'显示名称,一般为中文,如"异常注册"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="备注:">
<Input type="text" name="comment" value={this.state.comment} onChange={this.handleChange}/>
</FormItem>
<FormItem required={true} {...formItemLayout} label="警戒值:" help={validate.median.help} validateStatus={validate.median.status}>
<Row>
<Col span={6}>
<InputNumber min={1} max={100} name="median" value={this.state.median} onChange={this.handleSelect.bind(this,'median')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'如果实际风险分数大于此数字,此交易则需要人工审核'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="拒绝值:" help={validate.high.help} validateStatus={validate.high.status}>
<Row>
<Col span={6}>
<InputNumber min={1} max={100} name="high" value={this.state.high} onChange={this.handleSelect.bind(this,'high')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'如果实际风险分数大于此数字,此交易则直接拒绝'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,228 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditActivation extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
activationName:'',
label:'',
comment:'',
bottom:'0',
median:'',
high:'',
}
}
// 获取表格数据
fetchData=()=>{
FetchUtil('/activation/'+this.props.row.id,'GET','',
(data) => {
const activation=data.data.activation;
this.setState({
activationName:activation.activationName,
label:activation.label,
comment:activation.comment,
bottom:activation.bottom,
median:activation.median,
high:activation.high,
});
});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = value;
this.setState(state);
}
showModal=()=>{
this.fetchData();
this.setState({
visible:true
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.id=this.props.row.id;
param.modelId=this.props.modelId;
param.activationName=this.state.activationName;
param.label=this.state.label;
param.comment=this.state.comment;
param.bottom='0';
param.median=this.state.median;
param.high=this.state.high;
param.status=1;
FetchUtil('/activation/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('修改成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
activationName:{
help:'',
status:'success'
},
label:{
help:'',
status:'success'
},
median:{
help:'',
status:'success'
},
high:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.activationName){
validate.activationName.help='请输入策略名';
validate.activationName.status='warning';
isValidated=false;
}else {
let reg = /^[a-zA-z]\w{1,29}$/;
let activationName = this.state.activationName;
if(!reg.test(activationName)){
validate.activationName.help='按照提示输入正确的策略名';
validate.activationName.status='error';
isValidated=false;
}
}
if(!this.state.label){
validate.label.help='请输入显示名称';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.median){
validate.median.help='请输入警戒值';
validate.median.status='warning';
isValidated=false;
}else if(!/^\d{1,3}/.test(this.state.median)){
validate.median.help='警戒值必须为数字';
validate.median.status='error';
isValidated=false;
}
if(!this.state.high){
validate.high.help='请输入拒绝值';
validate.high.status='warning';
isValidated=false;
}else if(!/^\d{1,3}/.test(this.state.high)){
validate.high.help='拒绝值必须为数字';
validate.high.status='error';
isValidated=false;
}
return (
<span>
<Tooltip title="编辑" onClick={this.showModal}><a>编辑</a></Tooltip>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="策略名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'显示名称,一般为中文,如"异常注册"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="备注:">
<Input type="text" name="comment" value={this.state.comment} onChange={this.handleChange}/>
</FormItem>
<FormItem required={true} {...formItemLayout} label="警戒值:" help={validate.median.help} validateStatus={validate.median.status}>
<Row>
<Col span={6}>
<InputNumber min={1} max={100} name="median" value={this.state.median} onChange={this.handleSelect.bind(this,'median')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'如果实际风险分数大于此数字,此交易则需要人工审核'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="拒绝值:" help={validate.high.help} validateStatus={validate.high.status}>
<Row>
<Col span={6}>
<InputNumber min={1} max={100} name="high" value={this.state.high} onChange={this.handleSelect.bind(this,'high')}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'如果实际风险分数大于此数字,此交易则直接拒绝'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,121 @@
import React from 'react';
import {Switch,Icon} from 'antd';
import { Link } from 'react-router';
import './Collapse.less';
export default class Collapse extends React.Component{
constructor(props){
super(props);
this.state={
height:0,
index:-1,
pos:-1
}
}
slideDown=()=>{
if(this.state.height=="auto"){
return;
}
if(this.state.height<this.refs.pChild.offsetHeight){
this.setState({
height:this.state.height+15
},()=>{
setTimeout(this.slideDown,1);
})
}
else{
this.setState({
height:"auto"
})
}
}
slideUp=()=>{
if(this.state.height=="auto"){
this.state.height=this.refs.pChild.offsetHeight;
}
if(this.state.height>0){
this.setState({
height:this.state.height-15
},()=>{
setTimeout(this.slideUp,1);
})
}
else{
this.setState({
height:0
})
}
}
handleClick=()=>{
this.props.handleClick();
}
componentWillReceiveProps(nextProps){
if(nextProps.slide){
this.slideDown();
}
else{
this.slideUp();
}
}
handleDragEnd=(e)=>{
this.setState({
index:-1,
pos:-1
})
this.props.handleReOrder();
}
handleDrag=(e)=>{
if(!this.props.draggable){return;}
if(e.pageY==0){return;}
let pos=Math.floor((e.pageY-300)/48);
let index=this.props.ruleOrder.indexOf(this.props.ruleId+'');
if(index==pos){
return;
}
if(index==this.state.index&&pos==this.state.pos){
return;
}
this.state.index=index;
this.state.pos=pos;
this.props.handleDrag(this.props.ruleId,pos);
}
switchClick=(e)=>{
e.stopPropagation();
}
render(){
return (
<div style={this.state.index!=-1?{"visibility":"hidden"}:{}} className="p-block" draggable={this.props.draggable} onDragEnd={this.handleDragEnd} onDrag={this.handleDrag}>
<div className={'p-block-titles'+(this.props.slide?' p-block-title-select':'')} onClick={this.handleClick}>
<div className='p-block-title-left'>{this.props.title}</div>
{this.props.switcher!=undefined?
<div className='p-block-title-right' onClick={this.switchClick}><Switch checked={this.props.switcher} onChange={this.props.onSwitch}/></div>
:''}
{
this.props.type!=undefined?
<div className='p-block-title-right'><Link to={"/historyRecordList/"+this.props.modelId+"/" + this.props.activationId+"/"+this.props.ruleId}><Icon type="calendar" style={{fontSize:'24px',lineHeight:1.5}}/></Link></div>
:''
}
</div>
<div className={'p-block-contents'+(this.props.slide?' p-block-content-select':'')} style={{height:this.state.height}} ref="pContent">
<div ref="pChild" className="p-block-main">
{this.props?this.props.children:''}
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,48 @@
.p-block{
margin-bottom:8px;
}
.p-block-titles{
height: 40px;
border:1px solid #d9d9d9;
padding-left: 30px;
cursor:pointer;
border-radius: 7px;
&:hover{
background-color:#FAFAFA;
}
}
.p-block-title-left{
float:left;
line-height:38px;
}
.p-block-title-right{
float:right;
line-height:34px;
margin-right:20px;
}
.p-block-title-select{
background-color:#f7f7f7;
border-bottom-right-radius:0;
border-bottom-left-radius:0;
border-bottom:none;
}
.p-block-contents{
clear:both;
background-color:white;
overflow: hidden;
}
.p-block-content-select{
border:1px solid #d9d9d9;
border-top:none;
}
.p-block-main{
padding:20px
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
export default class CollapseGroup extends React.Component{
constructor(props){
super(props);
this.state={
activeKey:'',
}
}
getItems=()=>{
if(!this.props){
return '';
}
return this.props.children.map((child,index)=>{
const key=child.key;
const props={
slide:child.key==this.state.activeKey?true:false,
index:index,
draggable:this.props.draggable&&this.state.activeKey=='',
handleDrag:(ruleId,pos)=>{
this.props.handleDrag(ruleId,pos);
},
handleReOrder:()=>{
this.props.handleReOrder();
},
handleClick:()=>{
if(this.state.activeKey==key){
this.setState({
activeKey:''
})
}
else{
this.setState({
activeKey:key
});
}
}
}
return React.cloneElement(child,props);
})
}
allowDrop=(e)=>{
e.preventDefault();
}
render(){
return (
<div style={{position:"relative"}} onDragOver={(this.props.draggable&&this.state.activeKey=='')?this.allowDrop:null}>
{this.getItems()}
</div>
);
}
}

View File

@@ -0,0 +1,166 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message} from 'antd';
import { Link } from 'react-router';
const FormItem = Form.Item;
const Option = Select.Option;
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
import AddDataList from './modal/AddDataList';
import EditDataList from './modal/EditDataList';
import EditDataListMeta from './modal/EditDataListMeta';
export default class Datalist extends React.Component{
constructor(props){
super(props);
this.state={
name:'',
label:'',
listType:'',
status:1,
tData:[],
pageNo:1,
rowCount:0,
loading:true
}
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.params.id;
param.name=this.state.name;
param.label=this.state.label;
param.listType=this.state.listType;
param.status=this.state.status;
FetchUtil('/datalist','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
this.setState({
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
componentDidMount() {
this.fetchTableData();
}
deleteModel=(id)=>{
FetchUtil('/datalist/','DELETE','['+id+']',
(data) => {
message.info('删除成功!');
this.fetchTableData();
});
}
render(){
/*定义表格列*/
const columns = [
{
title: '序号',
dataIndex: 'id',
render:(t,r,i)=>{
return i+1;
}
},
{
title: '列表名',
dataIndex: 'label'
}, {
title: '备注',
dataIndex: 'comment'
},{
title: '名单类型',
dataIndex: 'listType'
},{
title: '操作',
dataIndex: 'handle',
render:
(t,r,i) => {
return(
<span>
<EditDataList modelId={this.props.params.id} row={r} reload={this.fetchTableData}/>
<span className="ant-divider"></span>
<EditDataListMeta modelId={this.props.params.id} row={r}/>
<span className="ant-divider"></span>
<Tooltip title="管理黑/白名单内容"><Link to={"/datalistRecord/"+this.props.params.id+"/"+r.id}>管理内容</Link></Tooltip>
<span className="ant-divider"></span>
<Popconfirm placement="bottomRight" title={'确认删除该模型吗?'} onConfirm={this.deleteModel.bind(this,r.id)}>
<Tooltip title="删除"><a style={{color:'#FD5B5B'}}>删除</a></Tooltip>
</Popconfirm>
</span>
);
}
}];
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="列表名:">
<Input value={this.state.name} name="name" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<AddDataList modelId={this.props.params.id} reload={this.fetchTableData} />
</Form>
</div>
<div id="table">
<Table
dataSource={this.state.tData.filter((item,index,array)=>{
var reg = new RegExp('('+ this.state.name+')','gi');
if(this.state.name){
return (reg.test(item.label));
}else {
return true;
}
})}
columns={columns}
size="middle"
pagination={false}
loading={this.state.loading}
/>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,151 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Modal} from 'antd';
import { Link } from 'react-router';
const FormItem = Form.Item;
const Option = Select.Option;
import {FetchUtil} from '../utils/fetchUtil';
import AddDataListRecord from './modal/AddDataListRecord';
import EditDataListRecord from './modal/EditDataListRecord';
export default class DatalistRecord extends React.Component{
constructor(props){
super(props);
this.state={
tData:[],
pageNo:1,
rowCount:0,
pageSize:30,
metaList:[],
loading:true,
}
FetchUtil('/datalistmeta/list/'+this.props.params.datalistId,'GET','',
(data) => {
if(data.data.list.length==0){
Modal.warning({
title: '警告',
content: '黑/白名单字段未定义,请前往上级菜单点击管理字段按钮进行管理。点击按钮返回',
maskClosable:false,
onOk:this.handleRedirect
});
}
this.setState({
metaList:data.data.list
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=20;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.dataListId=this.props.params.datalistId;
FetchUtil('/datalistrecord','POST',JSON.stringify(param),
(data) => {
this.setState({
loading:false,
tData:data.data.page.list,
pageNo:data.data.page.pageNum
});
if(data.data.page.rowCount > 9990){
this.setState({
rowCount:9990
});
}else {
this.setState({
rowCount:data.data.page.rowCount
});
}
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
componentDidMount() {
this.fetchTableData();
}
deleteModel=(id)=>{
FetchUtil('/datalistrecord/','DELETE','['+id+']',
(data) => {
message.info('删除成功!');
this.fetchTableData();
});
}
handleRedirect=()=>{
window.history.back();
}
render(){
/*定义表格列*/
const columns = [
{
title: 'No.',
dataIndex: 'id',
render:(t,r,i)=>{
return i+1;
}
},
{
title: 'Data Record',
dataIndex: 'dataRecord'
},{
title: '操作',
dataIndex: 'handle',
render:
(t,r,i) => {
return(
<span>
<EditDataListRecord metaList={this.state.metaList} dataListId={this.props.params.datalistId} row={r} reload={this.fetchTableData}/>
<span className="ant-divider"></span>
<Popconfirm placement="bottomRight" title={'确认删除该模型吗?'} onConfirm={this.deleteModel.bind(this,r.id)}>
<Tooltip title="删除"><a style={{color:'#FD5B5B'}}>删除</a></Tooltip>
</Popconfirm>
</span>
);
}
}];
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<AddDataListRecord metaList={this.state.metaList} dataListId={this.props.params.datalistId} reload={this.fetchTableData} />
</Form>
</div>
<div id="table">
<Table
dataSource={this.state.tData}
columns={columns}
size="middle"
pagination={false}
loading={this.state.loading}
/>
<div style={{width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} defaultPageSize={this.state.pageSize} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,163 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class AddDataList extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
name:'',
label:'',
comment:'',
listType:'',
status:"1",
}
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.setState({
visible:true,
name:'',
label:'',
comment:'',
listType:'',
status:"1",
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.modelId=this.props.modelId;
param.name=this.state.name;
param.label=this.state.label;
param.comment=this.state.comment;
param.listType=this.state.listType;
param.status=this.state.status;
FetchUtil('/datalist/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('添加成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
label:{
help:'',
status:'success'
},
listType:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入列表名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.listType){
validate.listType.help='请选择名单类型';
validate.listType.status='warning';
isValidated=false;
}
return (
<span>
<Button onClick={this.showModal} type="primary">新增</Button>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem {...formItemLayout} label="列表名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'列表显示名称,一般为中文,如"注册手机黑名单"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="备注:">
<Input type="text" name="comment" value={this.state.comment} onChange={this.handleChange}/>
</FormItem>
<FormItem {...formItemLayout} label="名单类型:" help={validate.listType.help} validateStatus={validate.listType.status}>
<Select value={this.state.listType} style={{ width: 120 }} onChange={this.handleSelect.bind(this,'listType')}>
<Option value="">请选择</Option>
<Option value="BLACK">黑名单</Option>
<Option value="WHITE">白名单</Option>
</Select>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,100 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class AddDataListRecord extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
dataRecord:'',
fieldNum:this.props.metaList.length
}
}
handleChange=(index,e)=>{
var value=e.target.value;
var valueArr=this.state.dataRecord.split(',');
if(valueArr.length<this.state.fieldNum){
var newArr=new Array(this.state.fieldNum);
for(var i=0;i<this.state.fieldNum;i++){
newArr[i]=valueArr[i]!=undefined?valueArr[i]:'';
}
valueArr=newArr;
}
valueArr[index]=trim(value);
this.setState({
dataRecord:valueArr.join(',')
});
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.setState({
dataRecord:'',
visible:true
})
}
handleSubmit=()=>{
var param={};
param.dataListId=this.props.dataListId;
param.dataRecord=this.state.dataRecord;
FetchUtil('/datalistrecord/','PUT',JSON.stringify(param),
(data) => {
this.setState({
visible:false
});
this.props.reload();
});
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let valueArr=this.state.dataRecord.split(',');
return (
<span>
<Button onClick={this.showModal} type="primary">新增</Button>
<Modal title="新增记录" visible={this.state.visible} onOk={this.handleSubmit} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
{this.props.metaList.map(function(info,i){
return (
<FormItem {...formItemLayout} key={'meta'+info.id} label={info.label}>
<Input type="text" value={valueArr[i]} onChange={this.handleChange.bind(this,i)}/>
</FormItem>
);
}.bind(this))}
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,179 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditDataList extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
name:'',
label:'',
comment:'',
listType:'',
status:"1",
}
}
// 获取数据
fetchData=()=>{
FetchUtil('/datalist/'+this.props.row.id,'GET','',
(data) => {
const datalist=data.data.datalist;
this.setState({
name:datalist.name,
label:datalist.label,
comment:datalist.comment,
listType:datalist.listType,
status:datalist.status+""
});
})
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.fetchData();
this.setState({
visible:true
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.id=this.props.row.id;
param.modelId=this.props.modelId;
param.name=this.state.name;
param.label=this.state.label;
param.comment=this.state.comment;
param.listType=this.state.listType;
param.status=this.state.status;
FetchUtil('/datalist/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('修改成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
label:{
help:'',
status:'success'
},
listType:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入列表名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.listType){
validate.listType.help='请选择名单类型';
validate.listType.status='warning';
isValidated=false;
}
return (
<span>
<Tooltip title="编辑" onClick={this.showModal}><a>编辑</a></Tooltip>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem {...formItemLayout} label="列表名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'列表显示名称,一般为中文,如"注册手机黑名单"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="备注:">
<Input type="text" name="comment" value={this.state.comment} onChange={this.handleChange}/>
</FormItem>
<FormItem {...formItemLayout} label="名单类型:" help={validate.listType.help} validateStatus={validate.listType.status}>
<Select value={this.state.listType} style={{ width: 120 }} onChange={this.handleSelect.bind(this,'listType')}>
<Option value="">请选择</Option>
<Option value="BLACK">黑名单</Option>
<Option value="WHITE">白名单</Option>
</Select>
</FormItem>
<FormItem {...formItemLayout} label="状态:">
<Select value={this.state.status} name="status" style={{ width: 120 }} onChange={this.handleSelect.bind(this,'status')}>
<Option value="1">正常</Option>
</Select>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,176 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
const OptGroup = Select.OptGroup;
const InputGroup = Input.Group;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditDataListMeta extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
metaList:[],
initialList:[]
}
}
// 获取数据
fetchData=()=>{
FetchUtil('/datalistmeta/list/'+this.props.row.id,'GET','',
(data) => {
let initialData = data.data.list.map((item)=>{
return item.label
});
this.setState({
metaList:data.data.list,
initialList:initialData
})
});
}
handleChange=(index,e)=>{
var name = e.target.name;
var value = e.target.value;
var metaList=this.state.metaList;
metaList[index][name]=trim(value);
this.setState({
metaList:metaList
});
}
addField=()=>{
let metaList=this.state.metaList;
metaList.push({
dataListId:this.props.row.id,
fieldName:'',
label:'',
seqNum:1
});
this.setState({
metaList:metaList
})
}
deleteField=(index)=>{
let metaList=this.state.metaList;
metaList.splice(index,1);
this.setState({
metaList:metaList
})
}
showModal=()=>{
this.fetchData();
this.setState({
visible:true
})
}
handleSubmit=()=>{
let labelIsNull = this.state.metaList.some((item)=>{
if(!item.label){
return true;
}
});
let labelIsChange = this.state.metaList.some((item,index,array)=>{
if( (array.length > this.state.initialList.length) || (item.label !== this.state.initialList[index]) ){
return true;
}
});
let reg = /^[\u4e00-\u9fa5 \w]{2,10}$/;
let labelReg = this.state.metaList.every((item,index,array)=>{
if(reg.test(item.label)){
return true;
}
});
if(this.state.metaList.length==0){
Modal.error({
title: '提交失败',
content: '请添加至少一个字段'
});
return false;
}else if(labelIsNull){
Modal.error({
title: '提交失败',
content: '字段名不能为空!'
});
return false;
}else if(!labelReg){
Modal.error({
title: '提交失败',
content: '字段名含有特殊字符,或者字符长度不符合!'
});
return false;
}else if(labelIsChange){
FetchUtil('/datalistmeta/','PUT',JSON.stringify(this.state.metaList),
(data) => {
this.setState({
visible:false
});
});
}else {
this.setState({
visible:false
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
return (
<span>
<Tooltip title="管理黑/白名单字段" onClick={this.showModal}><a>管理字段</a></Tooltip>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit} onCancel={this.handleCancel}>
<Row>
{this.state.initialList.length ?'':<Col span={6} offset={10}>
<span className="addRule" style={{display:"block",marginBottom:10}} onClick={this.addField}><Icon type="plus" />&nbsp;添加字段</span>
</Col>
}
{this.state.initialList.length ?<Col span={25} offset={2} style={{fontSize:14,marginBottom:10,color:'#f00'}}><span >现有字段不能删除若需要删除字段则建议直接删除列表</span></Col>:<Col span={1} offset={1}>
<Tooltip placement="right" title={'现有字段不能删除,若需要删除字段,则建议直接删除列表!'}>
<Icon style={{fontSize:16,marginBottom:10}} type="question-circle-o" />
</Tooltip>
</Col>}
</Row>
<Form horizontal form={this.props.form}>
{this.state.metaList.map(function(info,i){
return (
<FormItem key={i+'meta'} label='字段名' labelCol={{span:10}}>
<Col span={4} offset={1}>
<Input name="label" value={info.label} placeholder={'字段名'} onChange={this.handleChange.bind(this,i)}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段名,一般为中文,如"手机号码"2-10位可由中文、英文字母、数字、下划线的组合'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
<Col span={1} offset={1}>
<i onClick={this.deleteField.bind(this,i)} className="fa fa-trash" style={{fontSize:16}}/>
</Col>
</FormItem>
);
}.bind(this))}
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,99 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditDataListRecord extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
dataRecord:this.props.row.dataRecord,
fieldNum:this.props.metaList.length
}
}
handleChange=(index,e)=>{
var value=e.target.value;
var valueArr=this.state.dataRecord.split(',');
if(valueArr.length<this.state.fieldNum){
var newArr=new Array(this.state.fieldNum);
for(var i=0;i<this.state.fieldNum;i++){
newArr[i]=valueArr[i]!=undefined?valueArr[i]:'';
}
valueArr=newArr;
}
valueArr[index]=trim(value);
this.setState({
dataRecord:valueArr.join(',')
});
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.setState({
visible:true
})
}
handleSubmit=()=>{
var param={};
param.id=this.props.row.id;
param.dataListId=this.props.dataListId;
param.dataRecord=this.state.dataRecord;
FetchUtil('/datalistrecord/','PUT',JSON.stringify(param),
(data) => {
this.setState({
visible:false
});
this.props.reload();
});
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let valueArr=this.state.dataRecord.split(',');
return (
<span>
<Tooltip title="编辑" onClick={this.showModal}><a>编辑</a></Tooltip>
<Modal title="编辑记录" visible={this.state.visible} onOk={this.handleSubmit} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
{this.props.metaList.map(function(info,i){
return (
<FormItem {...formItemLayout} key={'meta'+info.id} label={info.label}>
<Input type="text" value={valueArr[i]} onChange={this.handleChange.bind(this,i)}/>
</FormItem>
);
}.bind(this))}
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,204 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Menu,Icon} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup;
import AddField from './modal/AddField';
import EditField from './modal/EditField';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class Field extends React.Component{
constructor(props){
super(props);
this.state={
fieldName:'',
label:'',
fieldType:'',
indexedState:'',
tData:[],
pageNo:1,
rowCount:0,
loading:true,
model:null
}
FetchUtil('/model/'+this.props.params.id,'GET','',
(data) => {
const model=data.data.model;
this.setState({
model:model
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.params.id;
param.fieldName=this.state.fieldName;
param.label=this.state.label;
param.fieldType=this.state.fieldType;
FetchUtil('/field','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
let indexedAll = data.data.page.list;
let num = 0;
let sum = indexedAll.reduce((pre,cur,index,array)=>{
let preNum =pre.indexed ? 1 : 0;
num += preNum ;
return (num += array[index].indexed);
});
//console.log(sum);
this.setState({
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount,
indexedAll:sum
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
componentDidMount() {
this.fetchTableData();
}
deleteModel=(id)=>{
FetchUtil('/field/','DELETE','['+id+']',
(data) => {
message.info('删除成功!');
this.fetchTableData();
});
}
render(){
/*定义表格列*/
const columns = [
{
title: '序号',
dataIndex: 'id',
render:(t,r,i)=>{
return i+1;
}
},
{
title: '字段名',
dataIndex: 'fieldName'
},
{
title: '显示名称',
dataIndex: 'label'
},
{
title: '字段类型',
dataIndex: 'fieldType',
render:(t)=>{
switch(t){
case 'STRING': return '字符串';
case 'INTEGER': return '整型';
case 'LONG': return '长整型';
case 'DOUBLE': return '浮点型';
default: return '';
}
}
},
{
title: '是否索引',
dataIndex: 'indexed',
render:(t)=>{
if(t){
return '是';
}else {
return '否'
}
}
},
{
title: '操作',
dataIndex: 'handle',
render:
(t,r,i) => {
return(
<span>
<EditField modelId={this.props.params.id} indexedAll={this.state.indexedAll} row={r} reload={this.fetchTableData}/>
<span className="ant-divider"></span>
<Popconfirm placement="bottomRight" title={'确认删除该字段吗?'} onConfirm={this.deleteModel.bind(this,r.id)}>
<Tooltip title="删除"><a style={{color:'#FD5B5B'}}>删除</a></Tooltip>
</Popconfirm>
</span>
);
}
}];
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="字段名:">
<Input value={this.state.fieldName} name="fieldName" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<AddField modelId={this.props.params.id} indexedAll={this.state.indexedAll} reload={this.fetchTableData} />
</Form>
</div>
<div id="table">
<Table
dataSource={this.state.tData.filter((item,index,array)=>{
var reg = new RegExp('('+ this.state.fieldName+')','gi');
if(this.state.fieldName){
return (reg.test(item.label));
}else {
return true;
}
})}
columns={columns}
size="middle"
pagination={false}
loading={this.state.loading}
/>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,228 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class AddField extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
fieldName:'',
label:'',
fieldType:'',
indexed:false,
fieldTypes:[]
}
FetchUtil('/common/fieldtypes','GET','',
(data) => {
this.setState({
fieldTypes:data.data.fields
})
});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
onCheck=(e)=>{
if(e.target.checked && this.props.indexedAll>=8){
Modal.warning({
title: '提示信息',
content: '索引已超过8项',
});
}else {
this.setState({
indexed:e.target.checked
});
}
};
showModal=()=>{
this.setState({
visible:true,
fieldName:'',
label:'',
fieldType:''
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param = {};
param.modelId = this.props.modelId;
param.fieldName = this.state.fieldName;
param.label = this.state.label;
param.fieldType = this.state.fieldType;
param.indexed = this.state.indexed;
FetchUtil('/field/', 'PUT', JSON.stringify(param),
(data) => {
if (data.success) {
message.success('添加成功');
} else {
message.error(data.msg);
}
this.setState({
visible: false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
fieldName:{
help:'',
status:'success'
},
label:{
help:'',
status:'success'
},
fieldType:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.fieldName){
validate.fieldName.help='请输入字段名';
validate.fieldName.status='warning';
isValidated=false;
}else {
let reg = /^[a-zA-z]\w{1,29}$/;
let fieldName = this.state.fieldName;
if(!reg.test(fieldName)){
validate.fieldName.help='按照提示输入正确的字段名';
validate.fieldName.status='error';
isValidated=false;
}
}
if(!this.state.label){
validate.label.help='请输入显示名称';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.fieldType){
validate.fieldType.help='请选择字段类型';
validate.fieldType.status='warning';
isValidated=false;
}
return (
<span>
<Button onClick={this.showModal} type="primary">新增</Button>
<Modal title="新建字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="字段名:" help={validate.fieldName.help} validateStatus={validate.fieldName.status}>
<Row>
<Col span={20}>
<Input type="text" name="fieldName" value={this.state.fieldName} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'2-30位英文字母、数字、下划线的组合以英文字母开头如"deviceId"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="显示名称:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段显示名称,一般为中文,如"设备ID"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="字段类型:" help={validate.fieldType.help} validateStatus={validate.fieldType.status}>
<Row>
<Col span={10}>
<Select value={this.state.fieldType} onChange={this.handleSelect.bind(this,'fieldType')}>
<Option value="">请选择</Option>
{
this.state.fieldTypes.map(x=><Option key={x.name} value={x.name}>{x.desc}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段类型,目前有四种类型,分别为字符串(如"你好""abc"等),整数(其范围为 -2147483648 到 2147483647 之间),长整数(其范围为 -9223372036854775808 到 9223372036854775807 之间),浮点数(如 3.14)。'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="是否索引:">
<Row>
<Col span={1}>
<Checkbox checked={this.state.indexed} onChange={this.onCheck}></Checkbox>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'如果勾选,则为该字段创建索引'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,239 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditField extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
fieldName:'',
label:'',
fieldType:'',
fieldTypes:[],
indexed:false
}
FetchUtil('/common/fieldtypes','GET','',
(data) => {
this.setState({
fieldTypes:data.data.fields
})
});
}
// 获取表格数据
fetchData=()=>{
FetchUtil('/field/'+this.props.row.id,'GET','',
(data) => {
const field=data.data.field;
this.setState({
fieldName:field.fieldName,
label:field.label,
fieldType:field.fieldType,
indexed:field.indexed
});
});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.fetchData();
this.setState({
visible:true
})
}
onCheck=(e)=>{
if(e.target.checked && this.props.indexedAll>=8){
Modal.warning({
title: '提示信息',
content: '索引已超过8项',
});
}else {
this.setState({
indexed:e.target.checked
});
}
};
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param = {};
param.id = this.props.row.id;
param.modelId = this.props.modelId;
param.fieldName = this.state.fieldName;
param.label = this.state.label;
param.fieldType = this.state.fieldType;
param.indexed = this.state.indexed;
FetchUtil('/field/', 'PUT', JSON.stringify(param),
(data) => {
if (data.success) {
message.success('修改成功');
} else {
message.error(data.msg);
}
this.setState({
visible: false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
fieldName:{
help:'',
status:'success'
},
label:{
help:'',
status:'success'
},
fieldType:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.fieldName){
validate.fieldName.help='请输入字段名';
validate.fieldName.status='warning';
isValidated=false;
}else {
let reg = /^[a-zA-z]\w{1,29}$/;
let fieldName = this.state.fieldName;
if(!reg.test(fieldName)){
validate.fieldName.help='按照提示输入正确的字段名';
validate.fieldName.status='error';
isValidated=false;
}
}
if(!this.state.label){
validate.label.help='请输入显示名称';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.fieldType){
validate.fieldType.help='请选择字段类型';
validate.fieldType.status='warning';
isValidated=false;
}
return (
<span>
<Tooltip title="编辑" onClick={this.showModal}><a>编辑</a></Tooltip>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="字段名:" help={validate.fieldName.help} validateStatus={validate.fieldName.status}>
<Row>
<Col span={20}>
<Input type="text" name="fieldName" value={this.state.fieldName} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'2-30位英文字母、数字、下划线的组合以英文字母开头如"deviceId"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="显示名称:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段显示名称,一般为中文,如"设备ID"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="字段类型:" help={validate.fieldType.help} validateStatus={validate.fieldType.status}>
<Row>
<Col span={10}>
<Select value={this.state.fieldType} onChange={this.handleSelect.bind(this,'fieldType')}>
<Option value="">请选择</Option>
{
this.state.fieldTypes.map(x=><Option key={x.name} value={x.name}>{x.desc}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段类型,目前有四种类型,分别为字符串(如"你好""abc"等),整数(其范围为 -2147483648 到 2147483647 之间),长整数(其范围为 -9223372036854775808 到 9223372036854775807 之间),浮点数(如 3.14'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="是否索引:">
<Row>
<Col span={1}>
<Checkbox checked={this.state.indexed} onChange={this.onCheck}></Checkbox>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'如果勾选,则为该字段创建索引'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,199 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Switch,Modal,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Menu,Icon} from 'antd';
import { Link } from 'react-router';
import moment from 'moment';
const FormItem = Form.Item;
const Option = Select.Option;
const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup;
const confirm = Modal.confirm;
import {FetchUtil} from '../utils/fetchUtil';
export default class Model extends React.Component{
constructor(props){
super(props);
this.state={
model:null,
current:'',
disable: false,
visible: false
}
}
// 获取表格数据
fetchData=()=>{
FetchUtil('/model/'+this.props.params.id,'GET','',
(data)=>{
if(data.data.model.status === 1){
this.setState({
checked:true,
disable:true
});
}else if(data.data.model.status === 2){
this.setState({
checked:false,
disable:true
});
}else if(data.data.model.status === 0){
this.setState({
checked:false,
disable:false
});
}
this.setState({loading:false});
this.setState({
model:data.data.model
});
});
};
componentDidMount() {
let key='';
switch(this.props.location.pathname.split('/')[1]){
case 'field':
key='field';break;
case 'preItem':
key='preItem';break;
case 'datalist':
case 'datalistRecord':
key='datalist';break;
case 'abstractionList':
key='abstractionList';break;
case 'activation':
case 'ruleList':
case 'historyRecordList':
key='activation';break;
}
this.setState({
current:key
});
this.fetchData();
}
handleClick=(e)=>{
window.location.href='/#/'+e.key+'/'+this.props.params.id;
this.setState({
current:e.key
})
};
handleBuild=()=>{
this.setState({
visible: true
});
var _self = this;
let paramsId = this.props.params.id;
//var success = false;
confirm({
title: '是否重新构建模型?',
content: '确认重新构建模型将清空历史数据!',
onOk() {
FetchUtil('/model/build/'+paramsId,'POST','',
(data)=>{
console.log(data);
if(!data.success){
Modal.error({
title: '构建状态',
content: data.msg,
});
//alert(data.msg);
}else {
Modal.success({
title: '构建状态',
content: '构建成功!',
});
_self.setState({
checked:true,
disable:true
});
console.log(_self);
}
});
},
onCancel() {
}
});
};
onChange=()=>{
this.setState({
checked: !this.state.checked,
});
if(!this.state.checked){
FetchUtil('/model/enable/'+this.props.params.id,'POST','',
(data)=>{
console.log(data);
if(!data.success){
alert(data.msg);
}
});
}else {
FetchUtil('/model/disable/'+this.props.params.id,'POST','',
(data)=>{
console.log(data);
if(!data.success){
alert(data.msg);
}
});
}
};
render(){
return (
<div className="ant-layout-wrapper">
<div className="ant-layout-breadcrumb">
<Breadcrumb>
<Breadcrumb.Item>首页</Breadcrumb.Item>
<Breadcrumb.Item>{this.state.model==null?'':<Link to={"/model/"+this.state.model.id}>模型"{this.state.model.label}"</Link>}</Breadcrumb.Item>
</Breadcrumb>
</div>
<div className="ant-layout-container">
<div style={{lineHeight:"46px",padding:"0 20px 0",margin:"0 24px",borderBottom:"1px solid #e9e9e9"}}>
{this.state.model==null?'':<span style={{marginRight:20}}>模型"{this.state.model.label}"</span>}
{this.state.model==null?'':<span style={{marginRight:20}}>模型创建时间{moment(this.state.model.createTime).format('YYYY-MM-DD HH:mm:ss')}</span>}
{this.state.disable===false?'':<Switch checkedChildren={'开'} unCheckedChildren={'关'} defaultChecked={this.state.checked} checked={this.state.checked} disabled={this.state.disabled} onChange={this.onChange} />}
<Button type="primary" onClick={this.handleBuild} style={{float:"right",marginTop:9}}>构建模型</Button>
</div>
<div className="ant-layout-header" style={{padding:"0 24px 24px"}}>
<Menu onClick={this.handleClick}
selectedKeys={[this.state.current]}
mode="horizontal"
>
<Menu.Item key="field">
<Icon type="file-text" />字段管理
</Menu.Item>
<Menu.Item key="preItem">
<Icon type="copy" />预处理管理
</Menu.Item>
<Menu.Item key="datalist">
<Icon type="appstore" />/白名单管理
</Menu.Item>
<Menu.Item key="abstractionList">
<Icon type="picture" />抽象处理
</Menu.Item>
<Menu.Item key="activation">
<Icon type="solution" />策略管理
</Menu.Item>
</Menu>
</div>
{this.props.children}
</div>
</div>
);
}
}

View File

@@ -0,0 +1,12 @@
#header {
margin-bottom: 10px;
padding: 10px 0;
padding-left: 20px;
background: #ECECEC;
border-radius: 5px;
overflow: hidden;
}
i{
cursor:pointer
}

View File

@@ -0,0 +1,225 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message,Modal,Icon} from 'antd';
import { Link } from 'react-router';
const FormItem = Form.Item;
const Option = Select.Option;
import AddModel from './modal/AddModel';
import EditModel from './modal/EditModel';
import StaticField from './modal/StaticField';
import CopyModel from './modal/CopyModel';
import './Model.less';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class ModelList extends React.Component{
constructor(props){
super(props);
this.state={
modelName:'',
status:"1",
tData:[],
pageNo:1,
rowCount:0,
loading:true
}
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.label=this.state.modelName;
//param.status=this.state.status; 默认查询不传递该参数,否则只有单一数据,不完整
FetchUtil('/model','POST',JSON.stringify(param),
(data)=>{
this.setState({loading:false});
this.setState({
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
copyModel=(r)=>{
var param={};
param.id=r.id;
param.modelName=r.modelName;
param.label=r.label;
param.entityName=r.entityName;
param.entryName=r.entryName;
param.guid=r.guid;
param.referenceDate=r.referenceDate;
param.status=r.status;
FetchUtil('/model/copy','POST',JSON.stringify(param),
(data) => {
if(data.success){
message.success('复制成功!');
}
else{
message.error(data.msg);
}
this.fetchTableData();
})
}
componentDidMount() {
this.fetchTableData();
}
deleteModel=(id)=>{
FetchUtil('/model/','DELETE','['+id+']',
(data) => {
if(data.success){
message.success('删除成功!');
}
else{
message.error(data.msg);
}
this.fetchTableData();
})
}
render(){
let showGuid=function(guid){
Modal.info({
title: '通过接口上传请携带此参数',
content: 'guid:'+guid,
});
}
/*定义表格列*/
const columns = [
{
title: '序号',
dataIndex: 'id',
render:(t,r,i)=>{
return i+1;
}
},
{
title: '模型名',
dataIndex: 'label'
},
{
title: '实体名',
dataIndex: 'entityName'
}, {
title: '事件ID',
dataIndex: 'entryName'
}, {
title: '唯一标识',
dataIndex: 'guid',
render:(t,r,i)=>{
return <a onClick={showGuid.bind(this,r.guid)}>查看</a>;
}
},{
title: '事件时间',
dataIndex: 'referenceDate'
},{
title: '操作',
dataIndex: 'handle',
render:
(t,r,i) => {
return(
<span>
{r.entityName?
<Tooltip title="进入模型"><Link to={"/field/"+r.id}>进入模型</Link></Tooltip>
:
<StaticField row={r} reload={this.fetchTableData}/>
}
<span className="ant-divider"></span>
{r.entityName?
<CopyModel tData={this.state.tData} row={r} reload={this.fetchTableData}/>
:
''
}
{r.entityName?<span className="ant-divider"></span>:''}
<EditModel tData={this.state.tData} row={r} reload={this.fetchTableData}/><span className="ant-divider"></span>
<Popconfirm placement="bottomRight" title={'确认删除该模型吗?'} onConfirm={this.deleteModel.bind(this,r.id)}>
<Tooltip title="删除"><a style={{color:'#FD5B5B'}}>删除</a></Tooltip>
</Popconfirm>
</span>
);
}
}];
return (
<div className="ant-layout-wrapper">
<div className="ant-layout-breadcrumb">
<Breadcrumb>
<Breadcrumb.Item>首页</Breadcrumb.Item>
<Breadcrumb.Item>模型列表</Breadcrumb.Item>
</Breadcrumb>
</div>
<div className="ant-layout-container">
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="模型名:">
<Input value={this.state.modelName} name="modelName" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<AddModel tData={this.state.tData} reload={this.fetchTableData} />
</Form>
</div>
<div id="table">
<Table
dataSource={this.state.tData.filter((item,index,array)=>{
let reg = new RegExp('('+ this.state.modelName+')','gi');
if(this.state.modelName){
return (reg.test(item.label));
}else {
return true;
}
})}
columns={columns}
size="middle"
pagination={false}
loading={this.state.loading}
/>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,193 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class AddModel extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
label:'',
templateList:[],
templateId:''
}
}
handleChange=(e)=>{
var tData = this.props.tData.some((item)=>{
if(item.label===e.target.value){
return true;
};
});
if(tData){
Modal.error({
title: '信息提示',
content: '模型名重复',
});
}
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.setState({
visible:true,
label:'',
});
FetchUtil('/model/list/template','GET','',
(data) => {
this.setState({
templateList:data.data.modelList
})
});
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else if(this.state.templateId!=""){
var param={};
param.id=this.state.templateId;
param.modelName='';
param.label=this.state.label;
param.entityName='';
param.entryName='';
param.referenceDate='';
FetchUtil('/model/copy','POST',JSON.stringify(param),
(data) => {
if(data.success){
message.success('添加成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
else{
var param={};
param.modelName='';
param.label=this.state.label;
param.entityName='';
param.entryName='';
param.referenceDate='';
FetchUtil('/model/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('添加成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
label:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入模型名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的模型名';
validate.label.status='error';
isValidated=false;
}
}
return (
<span>
<Button onClick={this.showModal} type="primary">新增</Button>
<Modal title="编辑模型" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="模型名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input size="large" type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型显示名称,一般为中文,如"注册模型"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem {...formItemLayout} label="模板:">
<Row>
<Col span={20}>
<Select value={this.state.templateId} onChange={this.handleSelect.bind(this,'templateId')}>
<Option value="">新建模型</Option>
{
this.state.templateList.map((info,index)=>{
return <Option key={index} value={info.id+''}>{'[系统]'+info.label}</Option>
})
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'可选择从模板一键创建模型,也可不选择模板而自行创建模型'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,146 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class CopyModel extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
modelName:'',
label:'',
}
}
handleChange=(e)=>{
var tData = this.props.tData.some((item)=>{
if(item.label===e.target.value){
return true;
};
});
if(tData){
Modal.error({
title: '信息提示',
content: '模型名重复',
});
}
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.setState({
visible:true,
modelName:'',
label:'',
});
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.id=this.props.row.id;
param.modelName='';
param.label=this.state.label;
FetchUtil('/model/copy','POST',JSON.stringify(param),
(data) => {
if(data.success){
message.success('复制成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
label:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入模型名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的模型名';
validate.label.status='error';
isValidated=false;
}
}
return (
<span>
<Tooltip title="复制模型" onClick={this.showModal}><a>复制</a></Tooltip>
<Modal title="复制模型" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem {...formItemLayout} label="复制模型:">
<label>{this.props.row.label}</label>
</FormItem>
<FormItem required={true} {...formItemLayout} label="新模型名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input size="large" type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型显示名称,一般为中文,如"注册模型"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,167 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const createForm = Form.create;
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditModel extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
modelName:'',
label:'',
entityName:'',
entryName:'',
guid:'',
referenceDate:'',
status:'',
}
}
// 获取表格数据
fetchData=()=>{
FetchUtil('/model/'+this.props.row.id,'GET',null,
(data) => {
const model=data.data.model;
this.setState({
modelName:model.modelName,
label:model.label,
entityName:model.entityName,
entryName:model.entryName,
guid:model.guid,
referenceDate:model.referenceDate,
status:model.status+''
});
}
)
}
handleChange=(e)=>{
var tData = this.props.tData.some((item)=>{
if(item.label===e.target.value){
return true;
};
});
if(tData){
Modal.error({
title: '信息提示',
content: '模型名重复',
});
}
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
showModal=()=>{
this.fetchData();
this.setState({
visible:true
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.id=this.props.row.id;
param.modelName=this.state.modelName;
param.label=this.state.label;
param.entityName=this.state.entityName;
param.entryName=this.state.entryName;
param.guid=this.state.guid;
param.referenceDate=this.state.referenceDate;
param.status=this.state.status;
FetchUtil('/model/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('修改成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
label:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.label){
validate.label.help='请输入模型名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的模型名';
validate.label.status='error';
isValidated=false;
}
}
return (
<span>
<Tooltip title="编辑" onClick={this.showModal}><a>编辑</a></Tooltip>
<Modal title="编辑模型" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="模型名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input size="large" type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型显示名称,一般为中文,如"注册模型"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,402 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class StaticField extends React.Component{
constructor(props){
super(props);
this.state={
entityName:'',
entityType:'',
entityLabel:'',
entryName:'',
entryType:'',
entryLabel:'',
referenceDate:'',
referenceDateType:'LONG',
referenceDateLabel:'',
visible:false,
fieldTypes:[]
}
FetchUtil('/common/fieldtypes','GET','',
(data) => {
this.setState({
fieldTypes:data.data.fields
})
});
}
showModal=()=>{
this.setState({
visible:true,
entityName:'',
entityType:'',
entityLabel:'',
entryName:'',
entryType:'',
entryLabel:'',
referenceDate:'',
referenceDateType:'LONG',
referenceDateLabel:'',
})
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
let model=this.props.row;
model.entityName=this.state.entityName;
model.entryName=this.state.entryName;
model.referenceDate=this.state.referenceDate;
FetchUtil('/model/','PUT',JSON.stringify(model),
(data) => {
this.setState({
visible:false
});
this.props.reload();
});
var entity={};
entity.modelId=this.props.row.id;
entity.fieldName=this.state.entityName;
entity.label=this.state.entityLabel;
entity.fieldType=this.state.entityType;
entity.indexed=true;
FetchUtil('/field/','PUT',JSON.stringify(entity),() => {});
var entry={};
entry.modelId=this.props.row.id;
entry.fieldName=this.state.entryName;
entry.label=this.state.entryLabel;
entry.fieldType=this.state.entryType;
entry.indexed=true;
FetchUtil('/field/','PUT',JSON.stringify(entry),() => {});
var referenceDate={};
referenceDate.modelId=this.props.row.id;
referenceDate.fieldName=this.state.referenceDate;
referenceDate.label=this.state.referenceDateLabel;
referenceDate.fieldType=this.state.referenceDateType;
referenceDate.indexed=true;
FetchUtil('/field/','PUT',JSON.stringify(referenceDate),() => {});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 19 },
};
let validate={
entityName:{
help:'',
status:'success'
},
entityLabel:{
help:'',
status:'success'
},
entityType:{
help:'',
status:'success'
},
entryName:{
help:'',
status:'success'
},
entryLabel:{
help:'',
status:'success'
},
entryType:{
help:'',
status:'success'
},
referenceDate:{
help:'',
status:'success'
},
referenceDateLabel:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.entityName){
validate.entityName.help='请输入实体名';
validate.entityName.status='warning';
isValidated=false;
}else {
let reg = /^[a-zA-z]\w{1,29}$/;
let entityName = this.state.entityName;
if(!reg.test(entityName)){
validate.entityName.help='按照提示输入正确的实体名';
validate.entityName.status='error';
isValidated=false;
}
}
if(!this.state.entityLabel){
validate.entityLabel.help='请输入实体显示名';
validate.entityLabel.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let entityLabel = this.state.entityLabel;
if(!reg.test(entityLabel)){
validate.entityLabel.help='按照提示输入正确的实体显示名';
validate.entityLabel.status='error';
isValidated=false;
}
}
if(!this.state.entityType){
validate.entityType.help='请选择实体类型';
validate.entityType.status='warning';
isValidated=false;
}
if(!this.state.entryName){
validate.entryName.help='请输入事件ID';
validate.entryName.status='warning';
isValidated=false;
}else {
let reg = /^[a-zA-z]\w{1,29}$/;
let entryName = this.state.entryName;
if(!reg.test(entryName)){
validate.entryName.help='按照提示输入正确的事件ID';
validate.entryName.status='error';
isValidated=false;
}
}
if(this.state.entityName&&(this.state.entityName==this.state.entryName)){
validate.entryName.help='事件ID不能与实体名相同!';
validate.entryName.status='error';
isValidated=false;
}
if(!this.state.entryLabel){
validate.entryLabel.help='请输入事件ID显示名';
validate.entryLabel.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let entryLabel = this.state.entryLabel;
if(!reg.test(entryLabel)){
validate.entryLabel.help='按照提示输入正确的事件ID显示名';
validate.entryLabel.status='error';
isValidated=false;
}
}
if(!this.state.entryType){
validate.entryType.help='请选择事件ID类型';
validate.entryType.status='warning';
isValidated=false;
}
if(this.state.entityName&&(this.state.entityName==this.state.referenceDate)){
validate.referenceDate.help='事件时间不能与实体名相同!';
validate.referenceDate.status='error';
isValidated=false;
}
if(this.state.entryName&&(this.state.entryName==this.state.referenceDate)){
validate.referenceDate.help='事件时间不能与事件ID相同!';
validate.referenceDate.status='error';
isValidated=false;
}
if(!this.state.referenceDate){
validate.referenceDate.help='请输入事件时间';
validate.referenceDate.status='warning';
isValidated=false;
}else {
let reg = /^[a-zA-z]\w{1,29}$/;
let referenceDate = this.state.referenceDate;
if(!reg.test(referenceDate)){
validate.referenceDate.help='按照提示输入正确的事件时间';
validate.referenceDate.status='error';
isValidated=false;
}
}
if(!this.state.referenceDateLabel){
validate.referenceDateLabel.help='请输入事件时间显示名';
validate.referenceDateLabel.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let referenceDateLabel = this.state.referenceDateLabel;
if(!reg.test(referenceDateLabel)){
validate.referenceDateLabel.help='按照提示输入正确的事件时间显示名';
validate.referenceDateLabel.status='error';
isValidated=false;
}
}
return (
<span>
<Tooltip title="创建必备字段"><a onClick={this.showModal}>创建必备字段</a></Tooltip>
<Modal title="创建必备字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="实体名:" help={validate.entityName.help} validateStatus={validate.entityName.status}>
<Row>
<Col span={20}>
<Input size="large" style={{marginTop:2}} type="text" name="entityName" value={this.state.entityName} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型目标实体字段,如"deviceId"(2-30位英文字母、数字、下划线的组合以英文字母开头)'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="实体显示名:" help={validate.entityLabel.help} validateStatus={validate.entityLabel.status}>
<Row>
<Col span={20}>
<Input size="large" style={{marginTop:2}} type="text" name="entityLabel" value={this.state.entityLabel} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型目标实体显示名称,可以输入中文'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="实体类型:" help={validate.entityType.help} validateStatus={validate.entityType.status}>
<Row>
<Col span={20}>
<Select size="large" name="entityType" value={this.state.entityType} onChange={this.handleSelect.bind(this,'entityType')}>
<Option value="">请选择</Option>
{
this.state.fieldTypes.map(x=><Option key={x.name} value={x.name}>{x.desc}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型目标实体类型,请选择类型'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="事件ID" help={validate.entryName.help} validateStatus={validate.entryName.status}>
<Row>
<Col span={20}>
<Input size="large" style={{marginTop:2}} type="text" name="entryName" value={this.state.entryName} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型时间ID字段一般为模型主键如"id"(2-30位英文字母、数字、下划线的组合以英文字母开头)'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="事件ID显示" help={validate.entryLabel.help} validateStatus={validate.entryLabel.status}>
<Row>
<Col span={20}>
<Input size="large" style={{marginTop:2}} type="text" name="entryLabel" value={this.state.entryLabel} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型目标实体显示名称,可以输入中文'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="事件ID类型" help={validate.entryType.help} validateStatus={validate.entryType.status}>
<Row>
<Col span={20}>
<Select size="large" name="entryType" value={this.state.entryType} onChange={this.handleSelect.bind(this,'entryType')}>
<Option value="">请选择</Option>
{
this.state.fieldTypes.map(x=><Option key={x.name} value={x.name}>{x.desc}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型目标实体类型,请选择类型'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="事件时间:" help={validate.referenceDate.help} validateStatus={validate.referenceDate.status}>
<Row>
<Col span={20}>
<Input size="large" style={{marginTop:2}} type="text" name="referenceDate" value={this.state.referenceDate} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型事件时间字段,如"accessTime"(2-30位英文字母、数字、下划线的组合以英文字母开头)'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="事件时间显示:" help={validate.referenceDateLabel.help} validateStatus={validate.referenceDateLabel.status}>
<Row>
<Col span={20}>
<Input size="large" style={{marginTop:2}} type="text" name="referenceDateLabel" value={this.state.referenceDateLabel} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'模型事件时间显示名称,可以输入中文'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="事件时间类型:">
<Row>
<Col span={20}>
<Select disabled size="large" value={this.state.referenceDateType}>
<Option value="LONG">长整数</Option>
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'事件时间类型,固定为长整数'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,191 @@
import React from 'react';
import {Breadcrumb,Form,Row,Col,Input,Button,Table,Tooltip,Pagination,Select,Popconfirm,message} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
import AddPreItem from './modal/AddPreItem';
import EditPreItem from './modal/EditPreItem';
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class PreItem extends React.Component{
constructor(props){
super(props);
this.state={
destField:'',
label:'',
plugin:"",
status:1,
tData:[],
pageNo:1,
rowCount:0,
loading:true,
model:null,
filedList:[],
plugins:[]
}
FetchUtil('/model/'+this.props.params.id,'GET','',
(data) => {
const model=data.data.model;
this.setState({
model:model
});
});
FetchUtil('/field/list/'+this.props.params.id,'GET','',
(data) => {
this.setState({
fieldList:data.data.field
});
});
FetchUtil('/common/plugins','GET','',
(data) => {
this.setState({
plugins:data.data.plugins
});
});
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=1000;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.params.id;
param.destField=this.state.destField;
param.label=this.state.label;
param.plugin=this.state.plugin;
param.status=this.state.status;
FetchUtil('/preitem','POST',JSON.stringify(param),
(data) => {
this.setState({loading:false});
this.setState({
tData:data.data.page.list,
pageNo:data.data.page.pageNum,
rowCount:data.data.page.rowCount
});
});
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSearch=()=>{
this.setState({
pageNo:1
},()=>{this.fetchTableData()});
}
componentDidMount() {
this.fetchTableData();
}
deletePreItem=(id)=>{
FetchUtil('/preitem/','DELETE','['+id+']',
(data) => {
message.info('删除成功!');
this.fetchTableData();
});
}
render(){
/*定义表格列*/
const columns = [
{
title: '序号',
dataIndex: 'id',
render:(t,r,i)=>{
return i+1;
}
},{
title: '字段名',
dataIndex: 'label'
},{
title: '来源字段名',
dataIndex: 'sourceLabel'
},{
title: '插件',
dataIndex: 'plugin',
render:
(t) =>{
let plugin=this.state.plugins.filter(x=>x.method==t);
return plugin.length!=0?plugin[0].desc:'';
}
},{
title: '插件参数(可选)',
dataIndex: 'args'
},{
title: '操作',
dataIndex: 'handle',
render:
(t,r,i) => {
return(
<span>
<EditPreItem modelId={this.props.params.id} row={r} fieldList={this.state.fieldList} plugins={this.state.plugins} reload={this.fetchTableData}/>
<span className="ant-divider"></span>
<Popconfirm placement="bottomRight" title={'确认删除该字段吗?'} onConfirm={this.deletePreItem.bind(this,r.id)}>
<Tooltip title="删除"><a style={{color:'#FD5B5B'}}>删除</a></Tooltip>
</Popconfirm>
</span>
);
}
}];
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="字段名:">
<Input value={this.state.destField} name="destField" id="blue" onChange={this.handleChange}/>
</FormItem>
{' '}
<AddPreItem modelId={this.props.params.id} fieldList={this.state.fieldList} plugins={this.state.plugins} reload={this.fetchTableData} />
</Form>
</div>
<div id="table">
<Table
dataSource={this.state.tData.filter((item,index,array)=>{
var reg = new RegExp('('+ this.state.destField+')','gi');
if(this.state.destField){
return (reg.test(item.label));
}else {
return true;
}
})}
columns={columns}
size="middle"
pagination={false}
loading={this.state.loading}
/>
<div style={{display:"none",width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} total={this.state.rowCount} />
</div>
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,265 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class AddPreItem extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
destField:'',
label:'',
sourceField:'',
sourceLabel:'',
plugin:'',
status:1,
args:''
}
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
if(name=='plugin'){
state['sourceField']='';
state['sourceLabel']='';
state['args']='';
}
state[name] = trim(value);
if(name=='sourceField'){
state['sourceLabel']=this.props.fieldList.filter(x=>x.fieldName==value)[0].label;
}
this.setState(state);
}
handleMultiSelect=(name,value)=>{
var state = this.state;
if(value==''){
state[name]='';
}
else{
state[name] = trim(value.join(','));
state['sourceLabel'] = value.map((info)=>{
return this.props.fieldList.filter(x=>x.fieldName==info)[0].label;
}).join(',');
}
this.setState(state);
}
showModal=()=>{
this.setState({
destField:'',
label:'',
sourceField:'',
sourceLabel:'',
plugin:'',
status:1,
args:'',
visible:true
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.modelId=this.props.modelId;
param.destField=this.state.destField;
param.label=this.state.label;
param.sourceField=this.state.sourceField;
param.sourceLabel=this.state.sourceLabel;
param.plugin=this.state.plugin;
param.status=this.state.status;
param.args=this.state.args;
FetchUtil('/preitem/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('添加成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
plugin:{
help:'',
status:'success'
},
label:{
help:'',
status:'success'
},
sourceField:{
help:'',
status:'success'
},
args:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.plugin){
validate.plugin.help='请选择插件';
validate.plugin.status='warning';
isValidated=false;
}
if(!this.state.label){
validate.label.help='请输入目标字段名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的目标显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.sourceField){
validate.sourceField.help='请选择原始字段名';
validate.sourceField.status='warning';
isValidated=false;
}
if(!this.state.args){
validate.args.help='请输入截取字段位数';
validate.args.status='warning';
if(this.state.plugin=='SUBSTRING'){
isValidated=false;
}
}
const plugin=this.state.plugin;
let fieldArr=this.state.sourceField==''?[]:this.state.sourceField.split(',');
return (
<span>
<Button onClick={this.showModal} type="primary">新增</Button>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="插件种类:" help={validate.plugin.help} validateStatus={validate.plugin.status}>
<Row>
<Col span={20}>
<Select value={this.state.plugin} onChange={this.handleSelect.bind(this,'plugin')}>
<Option value="">请选择</Option>
{
this.props.plugins.map(x=><Option key={x.key} value={x.method}>{x.desc}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'插件种类,如 IP转换成地址将IP地址转换成详细的实际地址字段合并将多个原始字段合并起来字符串截短例将手机号码截取部分进行筛选如前七位0,7等等'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="目标字段名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段显示名称,一般为中文,如"IP归属地"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="原始字段名:" style={plugin!='ALLINONE'?{}:{display:"none"}} help={validate.sourceField.help} validateStatus={validate.sourceField.status}>
<Row>
<Col span={20}>
<Select value={this.state.sourceField} onChange={this.handleSelect.bind(this,'sourceField')}>
<Option value="">请选择</Option>
{
this.props.fieldList==null?'':this.props.fieldList.map(x=><Option key={x.id} value={x.fieldName}>{x.label}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'原始字段名,均为自己定义的字段名'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="原始字段名:" style={plugin=='ALLINONE'?{}:{display:"none"}} help={validate.sourceField.help} validateStatus={validate.sourceField.status}>
<Row>
<Col span={20}>
<Select multiple placeholder="Please select" value={fieldArr} onChange={this.handleMultiSelect.bind(this,'sourceField')}>
{
this.props.fieldList==null?'':this.props.fieldList.map(x=><Option key={x.id} value={x.fieldName}>{x.label}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'原始字段名,均为自己定义的字段名'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="截取位数" style={plugin=='SUBSTRING'?{}:{display:"none"}} help={validate.args.help} validateStatus={validate.args.status}>
<Row>
<Col span={20}>
<Input type="text" name="args" value={this.state.args} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'截取位数属于字符串截短插件的参数如筛选手机号码前7位填写 0,7'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,280 @@
import React from 'react';
import {Button,Checkbox,Select,Radio,Switch,Form,Row,Col,Icon,Modal,Input,InputNumber,Cascader,Tooltip,message } from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
import {FetchUtil} from '../../utils/fetchUtil';
import {trim} from '../../utils/validateUtil';
export default class EditPreItem extends React.Component{
constructor(props){
super(props);
this.state={
visible:false,
destField:'',
label:'',
sourceField:'',
sourceLabel:'',
plugin:'',
status:1,
args:'',
preItem:null
}
}
// 获取表格数据
fetchData=()=>{
FetchUtil('/preitem/'+this.props.row.id,'GET','',
(data) => {
const preItem=data.data.preItem;
this.setState({
destField:preItem.destField,
label:preItem.label,
sourceField:preItem.sourceField,
sourceLabel:preItem.sourceLabel,
plugin:preItem.plugin,
status:preItem.status,
args:preItem.args
});
});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
if(name=='plugin'){
state['sourceField']='';
state['sourceLabel']='';
state['args']='';
}
state[name] = trim(value);
if(name=='sourceField'){
state['sourceLabel']=this.props.fieldList.filter(x=>x.fieldName==value)[0].label;
}
this.setState(state);
}
handleMultiSelect=(name,value)=>{
var state = this.state;
if(value==''){
state[name]='';
}
else{
state[name] = trim(value.join(','));
state['sourceLabel'] = value.map((info)=>{
return this.props.fieldList.filter(x=>x.fieldName==info)[0].label;
}).join(',');
}
this.setState(state);
}
showModal=()=>{
this.fetchData();
this.setState({
visible:true
})
}
handleSubmit=(validated)=>{
if(!validated){
Modal.error({
title: '提交失败',
content: '请确认表单内容输入正确',
});
}
else{
var param={};
param.id=this.props.row.id;
param.modelId=this.props.modelId;
param.destField=this.state.destField;
param.label=this.state.label;
param.sourceField=this.state.sourceField;
param.sourceLabel=this.state.sourceLabel;
param.plugin=this.state.plugin;
param.status=this.state.status;
param.args=this.state.args;
FetchUtil('/preitem/','PUT',JSON.stringify(param),
(data) => {
if(data.success){
message.success('修改成功');
}else{
message.error(data.msg);
}
this.setState({
visible:false
});
this.props.reload();
});
}
}
handleCancel=()=>{
this.setState({
visible:false
})
}
render(){
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
let validate={
plugin:{
help:'',
status:'success'
},
label:{
help:'',
status:'success'
},
sourceField:{
help:'',
status:'success'
},
args:{
help:'',
status:'success'
}
};
let isValidated=true;
if(!this.state.plugin){
validate.plugin.help='请选择插件';
validate.plugin.status='warning';
isValidated=false;
}
if(!this.state.label){
validate.label.help='请输入目标字段名';
validate.label.status='warning';
isValidated=false;
}else {
let reg = /^[\u4e00-\u9fa5 \w]{2,20}$/;
let label = this.state.label;
if(!reg.test(label)){
validate.label.help='按照提示输入正确的目标显示名称';
validate.label.status='error';
isValidated=false;
}
}
if(!this.state.sourceField){
validate.sourceField.help='请选择原始字段名';
validate.sourceField.status='warning';
isValidated=false;
}
if(!this.state.args){
validate.args.help='请输入截取字段位数';
validate.args.status='warning';
if(this.state.plugin=='SUBSTRING'){
isValidated=false;
}
}
const plugin=this.state.plugin;
let fieldArr=this.state.sourceField==''?[]:this.state.sourceField.split(',');
return (
<span>
<Tooltip title="编辑" onClick={this.showModal}><a>编辑</a></Tooltip>
<Modal title="编辑字段" visible={this.state.visible} onOk={this.handleSubmit.bind(this,isValidated)} onCancel={this.handleCancel}>
<Form horizontal form={this.props.form}>
<FormItem required={true} {...formItemLayout} label="插件种类:" help={validate.plugin.help} validateStatus={validate.plugin.status}>
<Row>
<Col span={20}>
<Select value={this.state.plugin} onChange={this.handleSelect.bind(this,'plugin')}>
<Option value="">请选择</Option>
{
this.props.plugins.map(x=><Option key={x.key} value={x.method}>{x.desc}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'插件种类,如 IP转换成地址将IP地址转换成详细的实际地址字段合并将多个原始字段合并起来字符串截短例将手机号码截取部分进行筛选如前七位0,7等等'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="目标字段名:" help={validate.label.help} validateStatus={validate.label.status}>
<Row>
<Col span={20}>
<Input type="text" name="label" value={this.state.label} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'字段显示名称,一般为中文,如"IP归属地"'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="原始字段名:" style={plugin!='ALLINONE'?{}:{display:"none"}} help={validate.sourceField.help} validateStatus={validate.sourceField.status}>
<Row>
<Col span={20}>
<Select value={this.state.sourceField} onChange={this.handleSelect.bind(this,'sourceField')}>
<Option value="">请选择</Option>
{
this.props.fieldList.map(x=><Option key={x.id} value={x.fieldName}>{x.label}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'原始字段名,均为自己定义的字段名'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="原始字段名:" style={plugin=='ALLINONE'?{}:{display:"none"}} help={validate.sourceField.help} validateStatus={validate.sourceField.status}>
<Row>
<Col span={20}>
<Select multiple placeholder="Please select" value={fieldArr} onChange={this.handleMultiSelect.bind(this,'sourceField')}>
{
this.props.fieldList.map(x=><Option key={x.id} value={x.fieldName}>{x.label}</Option>)
}
</Select>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'原始字段名,均为自己定义的字段名'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
<FormItem required={true} {...formItemLayout} label="截取位数" style={plugin=='SUBSTRING'?{}:{display:"none"}} help={validate.args.help} validateStatus={validate.args.status}>
<Row>
<Col span={20}>
<Input type="text" name="args" value={this.state.args} onChange={this.handleChange}/>
</Col>
<Col span={2} offset={1}>
<Tooltip placement="right" title={'截取位数属于字符串截短插件的参数如筛选手机号码前7位填写 0,7'}>
<Icon style={{fontSize:16}} type="question-circle-o" />
</Tooltip>
</Col>
</Row>
</FormItem>
</Form>
</Modal>
</span>
);
}
}

View File

@@ -0,0 +1,509 @@
import React from 'react';
import {Form,Button,Table,Pagination,Input,Select,Modal,DatePicker,Cascader} from 'antd';
import moment from 'moment';
const FormItem=Form.Item;
const Option = Select.Option;
const RangePicker = DatePicker.RangePicker;
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
import './ListEvent.less';
import ExportField from './modal/ExportField';
export default class ListEvent extends React.Component{
constructor(props){
super(props);
this.state={
fieldName:'',
fieldValue:'',
activationName:'',
ruleId:'',
risk:'',
beginTime:moment().add(-3,'days'),
endTime:moment(),
rangeSelect:'-3',
tData:[],
loading:true,
pageNo:1,
rowCount:0,
pageSize:30,
exportDisabled:true,
showAdvance:false,
searchType:''
}
if(this.props.params.modelId){
this.state.showAdvance=true;
this.state.searchType='rule';
this.state.activationName=this.props.params.activationName;
this.state.ruleId=this.props.params.ruleId+'';
}
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=30;
let param={};
let errMsg='';
switch(this.state.searchType){
case '':param.fieldName='';param.fieldValue='';break;
case 'field':
if(!this.state.fieldName){
errMsg='请选择字段!';
break;
}
if(!this.state.fieldValue){
errMsg='请输入字段值!';
break;
}
param.fieldName=this.state.fieldName;
param.fieldValue=this.state.fieldValue;
break;
case 'rule':
if(!this.state.activationName){
errMsg='请选择策略!';
break;
}
if(!this.state.ruleId){
errMsg='请选择规则!';
break;
}
param.fieldName='hitsDetail.'+this.state.activationName+'.rule_'+this.state.ruleId+'.key';
param.fieldValue=this.state.ruleId;
break;
case 'risk':
if(!this.state.activationName){
errMsg='请选择策略!';
break;
}
if(!this.state.risk){
errMsg='请选择评估结果!';
break;
}
param.fieldName='activations.'+this.state.activationName+'.risk';
param.fieldValue=this.state.risk;
break;
}
if(errMsg){
Modal.error({
title:errMsg
});
return;
}
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.modelId;
param.beginTime=this.state.beginTime.format('YYYY-MM-DD HH:mm:ss');
param.endTime=this.state.endTime.format('YYYY-MM-DD HH:mm:ss');
this.setState({loading:true});
FetchUtil('/event/search','POST',JSON.stringify(param),
(data) => {
this.setState({
tData:data.data.page.list.map((info)=>{
info=info.replace(/":(-?\d+)/g, "\":\"$1\"");
return JSON.parse(info)}),
pageNo:data.data.page.pageNum
});
if(data.data.page.rowCount > 9990){
this.setState({
rowCount:9990
});
}else {
this.setState({
rowCount:data.data.page.rowCount
});
}
},
()=>{
this.setState({loading:false,exportDisabled:false});
});
}
componentDidMount(){
this.fetchTableData();
}
componentWillReceiveProps(nextProps){
if(nextProps.modelId!=this.props.modelId){
this.setState({
fieldName:'',
fieldValue:'',
activationName:'',
ruleId:'',
risk:'',
pageNo:1,
fieldName:'',
fieldValue:'',
beginTime:moment().add(-3,'days'),
endTime:moment(),
rangeSelect:'-3',
showAdvance:false,
searchType:''
},this.fetchTableData());
}
}
toggleAdvance=()=>{
this.setState({
showAdvance:!this.state.showAdvance,
searchType:'',
fieldName:'',
fieldValue:'',
activationName:'',
ruleId:'',
risk:'',
});
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
state['exportDisabled']=true;
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name]=value;
state['exportDisabled']=true;
this.setState(state);
}
handleCalendar=(dates,dateStrings)=>{
this.setState({
beginTime:dates[0],
endTime:dates[1],
rangeSelect:'',
exportDisabled:true
});
}
handleChangeDate=(value)=>{
//console.log(value);
if(value === '-1'){
this.setState({
beginTime:moment().add(value,'months'),
endTime:moment()
});
}else {
this.setState({
beginTime:moment().add(value,'days'),
endTime:moment()
});
}
this.setState({
rangeSelect:value,
exportDisabled:true
});
}
handleField=(value)=>{
this.setState({
fieldName:value.join("."),
fieldValue:'',
exportDisabled:true
})
}
handleSearch=()=>{
this.fetchTableData();
if(this.props.location.pathname.indexOf('ruleid')!=-1){
window.location.href='/#/event';
}
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
displayRender = (labels, selectedOptions) => labels.map((label, i) => {
const option = selectedOptions[i];
if (i === labels.length - 1) {
return (
<span key={option.value+i}>
{label}
</span>
);
}
return <span key={option.value+i}>{label} / </span>;
})
showModal=(record)=>{
const hitsDetail=record.hitsDetail;
const activations=record.activations;
const columns=[{
title: '序号',
dataIndex: 'id',
key:'id',
width:50,
render:(t,r,i)=>{
return i+1;
}
},{
title: '命中规则',
dataIndex: 'desc',
key: 'rule'
},{
title: '得分',
dataIndex:'value'
}];
let data=[];
for(var Key in hitsDetail){
for(var subKey in hitsDetail[Key]){
data.push(hitsDetail[Key][subKey]);
}
}
const columnsActivation=[{
title: '序号',
dataIndex: 'id',
key:'id',
width:50,
render:(t,r,i)=>{
return i+1;
}
},{
title: '策略名称',
dataIndex: 'name',
},{
title: '得分',
dataIndex:'score'
},{
title: '处理结果',
dataIndex:'risk'
}];
let dataActivation=[];
for(var Keys in activations){
activations[Keys].name = Keys;
if(activations[Keys].risk === 'pass'){
activations[Keys].risk = '通过';
}else if(activations[Keys].risk === 'review'){
activations[Keys].risk = '人工审核';
}else if(activations[Keys].risk === 'reject'){
activations[Keys].risk = '拒绝';
}
dataActivation.push(activations[Keys]);
}
Modal.info({
title: '风险详情',
width:600,
content: (
<div style={{paddingTop:20}}>
<h3>命中明细</h3>
<Table
dataSource={data}
columns={columns}
size="middle"
bordered
pagination={false}
loading={this.state.loading}
/>
<h3 style={{paddingTop:20}}>策略明细</h3>
<Table
dataSource={dataActivation}
columns={columnsActivation}
size="middle"
bordered
pagination={false}
loading={this.state.loading}
/>
</div>
)
});
}
render(){
let columns = [
{
title: '序号',
dataIndex: 'id',
key:'id',
width:50,
fixed:'left',
render:(t,r,i)=>{
return i+1;
}
}];
let getChildren=(valueArr,children)=>{
return children.map((info)=>{
let va=valueArr.concat(info.value);
if(info.children==undefined){
let column={
title:info.label,
dataIndex:va.join(''),
key:va.join(''),
rowSpan:4-va.length,
className:'fixed-table'
};
if(this.state.model!=null&&this.state.model.referenceDate==info.value){
column.render=(t)=>{
return moment(parseInt(t)).format('YYYY-MM-DD HH:mm:ss');
}
}
return column;
}
else{
return {
title:info.label,
children:getChildren(va,info.children)
}
}
})
}
columns=columns.concat(getChildren([],this.props.fieldList));
let dataList=[];
this.state.tData.map((info)=>{
let data={};
for(var Key in info.fields){
data['fields'+Key]=info.fields[Key];
}
for(var Key in info.preItems){
if(typeof info.preItems[Key]=='object'){
for(var subKey in info.preItems[Key]){
data['preItems'+Key+subKey]=info.preItems[Key][subKey];
}
}
else{
data['preItems'+Key]=info.preItems[Key];
}
}
data['hitsDetail']=info.hitsDetail;
data['activations']=info.activations;
dataList.push(data);
});
const actList=this.props.activationList.filter(x=>x.value==this.state.activationName);
let ruleList=[];
if(actList.length!=0){
ruleList=actList[0].children;
}
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="起始时间">
<Select dropdownMatchSelectWidth={false} showSearch placeholder="选择时间段" value={this.state.rangeSelect} onChange={this.handleChangeDate} style={{width:100,marginRight:10}}>
<Option value='-3'>三天内</Option>
<Option value='-7'>七天内</Option>
<Option value='-1'>一月内</Option>
</Select>
<RangePicker value={[this.state.beginTime,this.state.endTime]} showTime format="YYYY/MM/DD HH:mm:ss" onChange={this.handleCalendar} />
</FormItem>
<Button type="primary" onClick={this.handleSearch}>查询</Button>
{' '}
<ExportField eventFieldList={this.props.eventFieldList} disabled={this.state.exportDisabled}/>
&nbsp;&nbsp;&nbsp;&nbsp;
<a onClick={this.toggleAdvance}>高级搜索>></a>
</Form>
{this.state.showAdvance?
<Form inline style={{marginTop:5}}>
<FormItem label="搜索种类">
<Select dropdownMatchSelectWidth={false} placeholder="选择时间段" value={this.state.searchType} onChange={this.handleSelect.bind(this,'searchType')} style={{marginRight:10}}>
<Option value=''>请选择搜索种类</Option>
<Option value='field'>按字段搜索</Option>
<Option value='rule'>按规则搜索</Option>
<Option value='risk'>按评估结果搜索</Option>
</Select>
</FormItem>
{this.state.searchType==''?''
:this.state.searchType=='field'?
<span>
<FormItem label="选择字段:">
<Cascader
options={this.props.fieldList}
value={this.state.fieldName.split('.')}
displayRender={this.displayRender}
onChange={this.handleField}
allowClear
/>
</FormItem>
<FormItem label="字段值:">
<Input value={this.state.fieldValue} name="fieldValue" id="blue" onChange={this.handleChange}/>
</FormItem>
</span>
:this.state.searchType=='rule'?
<span>
<FormItem label="选择策略:">
<Select dropdownMatchSelectWidth={false} value={this.state.activationName} onChange={this.handleSelect.bind(this,'activationName')} style={{width:100}}>
{this.props.activationList.map((info,i)=>{
return <Option key={info.label} value={info.value}>{info.label}</Option>
})}
</Select>
</FormItem>
<FormItem label="选择规则:">
<Select dropdownMatchSelectWidth={false} value={this.state.ruleId} onChange={this.handleSelect.bind(this,'ruleId')} style={{width:100}}>
{ruleList==undefined?'':ruleList.map((info,i)=>{
return <Option key={info.label} value={info.type}>{info.label}</Option>
})}
</Select>
</FormItem>
</span>
:this.state.searchType=='risk'?
<span>
<FormItem label="选择策略:">
<Select dropdownMatchSelectWidth={false} value={this.state.activationName} onChange={this.handleSelect.bind(this,'activationName')} style={{width:100}}>
{this.props.activationList.map((info,i)=>{
return <Option key={info.label} value={info.value}>{info.label}</Option>
})}
</Select>
</FormItem>
<FormItem label="处理结果:">
<Select dropdownMatchSelectWidth={false} value={this.state.risk} onChange={this.handleSelect.bind(this,'risk')} style={{width:100}}>
<Option value='pass'>通过</Option>
<Option value='review'>人工审核</Option>
<Option value='reject'>拒绝</Option>
</Select>
</FormItem>
</span>
:''}
</Form>
:''
}
</div>
<div id="table" className="fixed-table">
<Table
dataSource={dataList}
columns={columns}
size="middle"
bordered
onRowClick={this.showModal}
pagination={false}
loading={this.state.loading}
scroll={{ x: true }}
/>
<div style={{width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} defaultPageSize={this.state.pageSize} total={this.state.rowCount} />
</div>
</div>
</div>
</div>);
}
}

View File

@@ -0,0 +1,3 @@
.fixed-table{
white-space: nowrap;
}

View File

@@ -0,0 +1,413 @@
import React from 'react';
import {Form,Button,Table,Pagination,Input,Select,Modal,DatePicker,Cascader} from 'antd';
import moment from 'moment';
const FormItem=Form.Item;
const Option = Select.Option;
const RangePicker = DatePicker.RangePicker;
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
import './ListEvent.less';
export default class Rule extends React.Component{
constructor(props){
super(props);
this.state={
beginTime:moment().add(-3,'days'),
endTime:moment(),
rangeSelect:'-3',
endOpen:false,
tData:[],
loading:true,
pageNo:1,
rowCount:0,
pageSize:30,
activationName:'',
ruleId:'',
risk:[],
activationNameOne:'' //存储策略中的第一个value值便于按处理结果查询
}
if(this.props.params.modelId){
this.state.activationName=this.props.params.activationName;
this.state.ruleId=this.props.params.ruleId+'';
}
}
// 获取表格数据
fetchTableData=()=>{
const pageSize=30;
this.setState({loading:true});
var param={};
param.pageNo=this.state.pageNo;
param.pageSize=pageSize;
param.modelId=this.props.modelId;
if(this.state.risk.length != 0){
if(this.state.activationName!=''){
param.fieldName='activations.'+this.state.activationName+'.risk';
param.fieldValue=this.state.risk;
}else {
param.fieldName='activations.'+this.state.activationNameOne+'.risk';
param.fieldValue=this.state.risk;
}
}else{
if(this.state.activationName!=''){
param.fieldName='hitsDetail.'+this.state.activationName+'.rule_'+this.state.ruleId+'.key';
param.fieldValue=this.state.ruleId;
}
}
param.beginTime=this.state.beginTime.format('YYYY-MM-DD HH:mm:ss');
param.endTime=this.state.endTime.format('YYYY-MM-DD HH:mm:ss');
FetchUtil('/event/search','POST',JSON.stringify(param),
(data) => {
this.setState({
tData:data.data.page.list.map((info)=>{return JSON.parse(info)}),
pageNo:data.data.page.pageNum
});
if(data.data.page.rowCount > 9990){
this.setState({
rowCount:9990
});
}else {
this.setState({
rowCount:data.data.page.rowCount
});
}
},
()=>{
this.setState({loading:false});
});
}
componentDidMount=()=>{
this.fetchTableData();
}
componentWillReceiveProps(nextProps){
if(nextProps.modelId!=this.props.modelId){
this.setState({
beginTime:moment().add(-3,'days'),
endTime:moment(),
rangeSelect:'-3',
activationName:'',
ruleId:'',
},this.fetchTableData());
}
}
handleChange=(e)=>{
var name = e.target.name;
var value = e.target.value;
var state = this.state;
state[name] = trim(value);
this.setState(state);
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
// if(name=='modelId'){
// this.setState({
// tData:[]
// });
// FetchUtil('/abstraction/datacolumns/'+value,'GET','',
// (data) => {
// this.setState({
// fieldList:data.data.list
// });
// });
// FetchUtil('/activation/rulecolumns/'+value,'GET','',
// (data) => {
// this.setState({
// activationList:data.data.list
// });
// });
// FetchUtil('/model/'+value,'GET','',
// (data) => {
// this.setState({
// model:data.data.model
// })
// }
// )
// this.setState({
// activationName:'',
// ruleId:''
// })
// }
if(name=='activationName'){
const activation=this.props.activationList.filter(x=>x.value==value)[0];
if(activation.children&&activation.children.length>0){
this.setState({
ruleId:activation.children[0].type
});
}
}
}
handleCalendar=(dates,dateStrings)=>{
this.setState({
beginTime:dates[0],
endTime:dates[1]
});
}
handleChangeDate=(value)=>{
//console.log(value);
if(value === '-1'){
this.setState({
beginTime:moment().add(value,'months'),
endTime:moment()
});
}else {
this.setState({
beginTime:moment().add(value,'days'),
endTime:moment()
});
}
}
handleSearch=()=>{
this.fetchTableData();
}
selectPage=(page)=>{
this.setState({
pageNo:page
},()=>{this.fetchTableData()});
}
showModal=(record)=>{
const hitsDetail=record.hitsDetail;
const activations=record.activations;
const columns=[{
title: '序号',
dataIndex: 'id',
key:'id',
width:50,
render:(t,r,i)=>{
return i+1;
}
},{
title: '命中规则',
dataIndex: 'desc',
key: 'rule'
},{
title: '得分',
dataIndex:'value'
}];
let data=[];
for(var Key in hitsDetail){
for(var subKey in hitsDetail[Key]){
data.push(hitsDetail[Key][subKey]);
}
}
const columnsActivation=[{
title: '序号',
dataIndex: 'id',
key:'id',
width:50,
render:(t,r,i)=>{
return i+1;
}
},{
title: '策略名称',
dataIndex: 'name',
},{
title: '得分',
dataIndex:'score'
},{
title: '处理结果',
dataIndex:'risk'
}];
let dataActivation=[];
for(var Keys in activations){
activations[Keys].name = Keys;
if(activations[Keys].risk === 'pass'){
activations[Keys].risk = '通过';
}else if(activations[Keys].risk === 'review'){
activations[Keys].risk = '人工审核';
}else if(activations[Keys].risk === 'reject'){
activations[Keys].risk = '拒绝';
}
dataActivation.push(activations[Keys]);
}
Modal.info({
title: '风险详情',
width:600,
content: (
<div style={{paddingTop:20}}>
<h3>命中明细</h3>
<Table
dataSource={data}
columns={columns}
size="middle"
bordered
pagination={false}
loading={this.state.loading}
/>
<h3 style={{paddingTop:20}}>策略明细</h3>
<Table
dataSource={dataActivation}
columns={columnsActivation}
size="middle"
bordered
pagination={false}
loading={this.state.loading}
/>
</div>
)
});
}
render(){
let columns = [
{
title: '序号',
dataIndex: 'id',
key:'id',
width:50,
fixed:'left',
render:(t,r,i)=>{
return i+1;
}
}];
let getChildren=(valueArr,children)=>{
return children.map((info)=>{
let va=valueArr.concat(info.value);
if(info.children==undefined){
let column={
title:info.label,
dataIndex:va.join(''),
key:va.join(''),
rowSpan:4-va.length,
className:'fixed-table'
};
if(this.state.model!=null&&this.state.model.referenceDate==info.value){
column.render=(t)=>{
return moment(t).format('YYYY-MM-DD HH:mm:ss');
}
}
return column;
}
else{
return {
title:info.label,
children:getChildren(va,info.children)
}
}
})
}
columns=columns.concat(getChildren([],this.props.fieldList));
let dataList=[];
this.state.tData.map((info)=>{
let data={};
for(var Key in info.fields){
data['fields'+Key]=info.fields[Key];
}
for(var Key in info.preItems){
if(typeof info.preItems[Key]=='object'){
for(var subKey in info.preItems[Key]){
data['preItems'+Key+subKey]=info.preItems[Key][subKey];
}
}
else{
data['preItems'+Key]=info.preItems[Key];
}
}
data['hitsDetail']=info.hitsDetail;
data['activations']=info.activations;
dataList.push(data);
});
const actList=this.props.activationList.filter(x=>x.value==this.state.activationName);
let ruleList=[];
if(actList.length!=0){
ruleList=actList[0].children;
}
return (
<div className="ant-layout-content">
<div id="header">
<Form inline>
<FormItem label="选择策略:">
<Select dropdownMatchSelectWidth={false} value={this.state.activationName} onChange={this.handleSelect.bind(this,'activationName')} style={{width:100}}>
{this.props.activationList.map((info,i)=>{
return <Option key={info.label} value={info.value}>{info.label}</Option>
})}
</Select>
</FormItem>
<FormItem label="选择规则:">
<Select dropdownMatchSelectWidth={false} value={this.state.ruleId} onChange={this.handleSelect.bind(this,'ruleId')} style={{width:100}}>
{ruleList==undefined?'':ruleList.map((info,i)=>{
return <Option key={info.label} value={info.type}>{info.label}</Option>
})}
</Select>
</FormItem>
&nbsp;&nbsp;&nbsp;&nbsp;
<FormItem label="处理结果:">
<Select dropdownMatchSelectWidth={false} value={this.state.risk} onChange={this.handleSelect.bind(this,'risk')} style={{width:100}}>
<Option value='pass'>通过</Option>
<Option value='review'>人工审核</Option>
<Option value='reject'>拒绝</Option>
</Select>
</FormItem>
&nbsp;&nbsp;&nbsp;&nbsp;
<FormItem label="起始时间">
<Select dropdownMatchSelectWidth={false} showSearch defaultValue='-3' onChange={this.handleChangeDate} style={{width:100,marginRight:10}}>
<Option value='-3'>三天内</Option>
<Option value='-7'>七天内</Option>
<Option value='-1'>一月内</Option>
</Select>
<RangePicker value={[this.state.beginTime,this.state.endTime]} showTime format="YYYY/MM/DD HH:mm:ss" onChange={this.handleCalendar} />
</FormItem>
<Button type="primary" onClick={this.handleSearch}>查询</Button>
{' '}
</Form>
</div>
<div id="table" className="fixed-table">
<Table
dataSource={dataList}
columns={columns}
size="middle"
bordered
onRowClick={this.showModal}
pagination={false}
loading={this.state.loading}
scroll={{ x: true }}
/>
<div style={{width:"100%",marginTop:16,height:40}}>
<div style={{float:"right"}}>
<Pagination onChange={this.selectPage} defaultCurrent={this.state.pageNo} defaultPageSize={this.state.pageSize} total={this.state.rowCount} />
</div>
</div>
</div>
</div>);
}
}

View File

@@ -0,0 +1,143 @@
import React from 'react';
import {Breadcrumb,Menu,Icon,Form,Select} from 'antd';
const FormItem=Form.Item;
const Option = Select.Option;
import {FetchUtil} from '../utils/fetchUtil';
export default class Report extends React.Component{
constructor(props){
super(props);
this.state={
current:'',
modelList:[],
modelId:'',
fieldList:[],
eventFieldList:[],
activationList:[]
}
}
handleClick=(e)=>{
window.location.href='/#/'+e.key;
this.setState({
current:e.key
})
}
handleSelect=(name,value)=>{
let state=this.state;
state[name]=value;
this.setState(state);
FetchUtil('/abstraction/datacolumns/'+value,'GET','',
(data) => {
this.setState({
fieldList:data.data.list
});
});
FetchUtil('/event/datacolumns/'+value,'GET','',
(data) => {
this.setState({
eventFieldList:data.data.list
});
});
FetchUtil('/activation/rulecolumns/'+value,'GET','',
(data) => {
this.setState({
activationList:data.data.list,
});
});
}
componentDidMount(){
let key='';
let modelId='';
switch(this.props.location.pathname.split('/')[1]){
case 'event':
key='event';break;
case 'graph':
key='graph';break;
case 'ruleid':
key='event';
modelId=this.props.location.pathname.split('/')[2];
break;
case 'dashboard':
key='dashboard';break;
}
this.setState({
current:key
});
FetchUtil('/model/list','GET',{},
(data) => {
this.setState({
modelList:data.data.modelList
},()=>{
if(this.state.modelList.length>0&&modelId==''){
this.handleSelect('modelId',this.state.modelList[0].id+'');
}
else{
this.handleSelect('modelId',modelId);
}
});
});
}
getItems=()=>{
if(this.state.modelId==''){
return '请选择模型!';
}
const props={
modelId:this.state.modelId,
model:this.state.modelList.filter(x=>x.id==this.state.modelId)[0],
fieldList:this.state.fieldList,
eventFieldList:this.state.eventFieldList,
activationList:this.state.activationList
}
return React.cloneElement(this.props.children,props);
}
render() {
return (
<div className="ant-layout-wrapper">
<div className="ant-layout-breadcrumb">
<Breadcrumb>
<Breadcrumb.Item>首页</Breadcrumb.Item>
<Breadcrumb.Item>报表查询</Breadcrumb.Item>
</Breadcrumb>
</div>
<div className="ant-layout-container">
<div style={{lineHeight:"46px",padding:"0 20px 0",margin:"0 24px",borderBottom:"1px solid #e9e9e9"}}>
<Form inline>
<FormItem label="模型:">
<Select dropdownMatchSelectWidth={false} value={this.state.modelId} onChange={this.handleSelect.bind(this,'modelId')} style={{width:100}}>
{this.state.modelList.map((info)=>{
return <Option key={info.id} value={info.id+''}>{info.label}</Option>
})}
</Select>
</FormItem>
</Form>
</div>
<div className="ant-layout-header" style={{padding:"0 24px 24px"}}>
<Menu onClick={this.handleClick} selectedKeys={[this.state.current]} mode="horizontal">
<Menu.Item key="event">
<Icon type="file-text"/>调用查询
</Menu.Item>
<Menu.Item key="graph">
<Icon type="pushpin-o"/>规则命中
</Menu.Item>
<Menu.Item key="dashboard">
<Icon type="file-text"/>指示板
</Menu.Item>
</Menu>
</div>
{this.getItems()}
</div>
</div>
);
}
}

View File

@@ -0,0 +1,171 @@
import React from 'react';
import {Form,Button,Table,Pagination,Input,Select,Modal,DatePicker,Cascader} from 'antd';
//import moment from 'moment';
import { Link } from 'react-router';
import echarts from 'echarts';
const FormItem=Form.Item;
const Option = Select.Option;
/*
const RangePicker = DatePicker.RangePicker;
*/
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
export default class RuleGraph extends React.Component{
constructor(props){
super(props);
this.state={
modelId:'',
tData:[],
loading:true,
pageNo:1,
rowCount:0,
ruleLabelList:[],
countList:[],
modelList:[],
fieldList:[]
}
}
// 获取表格数据
fetchTableData=(value)=>{
this.setState({loading:true});
FetchUtil('/rule/hitsSort/'+value,'GET',{},
(data) => {
this.setState({
countList:data.data.hits.map((hit)=>{return hit.count}),
ruleLabelList:data.data.hits.map((hit)=>{return hit.ruleLable}),
tData:data.data.hits,
loading:false
});
}
);
}
componentDidMount=()=>{
this.fetchTableData(this.props.modelId);
}
componentWillReceiveProps(nextProps){
if(nextProps.modelId!=this.props.modelId){
this.fetchTableData(nextProps.modelId);
}
}
handleSelect=(name,value)=>{
var state = this.state;
state[name] = trim(value);
this.setState(state);
FetchUtil('/rule/hitsSort/'+value,'GET',{},
(data) => {
console.log(data.data.hits);
this.setState({
countList:data.data.hits.map((hit)=>{return hit.count}),
ruleLabelList:data.data.hits.map((hit)=>{return hit.ruleLable}),
tData:data.data.hits
});
this.setState({loading:false});
}
);
}
render(){
/*定义表格列*/
const columns = [
{
title: '字段名',
dataIndex: 'ruleLable'
},
{
title: '命中数',
dataIndex: 'count'
},
{
title: '操作',
dataIndex: 'handle',
render:(t,r)=>{
return <Link to={"/ruleid/"+this.props.modelId+"/"+r.id+"/"+r.activationName} target="_blank">查看明细</Link>;
}
}
];
return (
<div className="ant-layout-content">
<div id="echartsMain" style={{width:900,height:400}}></div>
<div id="table">
<Table
dataSource={this.state.tData}
columns={columns}
size="middle"
bordered
pagination={false}
loading={this.state.loading}
/>
</div>
</div>
);
}
componentDidUpdate=()=>{
// 基于准备好的dom元素初始化echarts实例
let myChart = echarts.init(document.getElementById('echartsMain'));
// 绘制图表
myChart.setOption({
tooltip : {
trigger: 'axis',
axisPointer : { // 坐标轴指示器,坐标轴触发有效
type : 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
legend: {
data: ['命中数']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value'
},
yAxis: {
type: 'category',
data: this.state.ruleLabelList
},
series: [
{
name: '命中数',
type: 'bar',
stack: '总量',
label: {
normal: {
show: true,
position: 'right'
}
},
data: this.state.countList
}
]
});
}
}

View File

@@ -0,0 +1,52 @@
import React from 'react';
import {Form,Button,Table,Pagination,Input,Select,Modal,DatePicker,Cascader} from 'antd';
import moment from 'moment';
const FormItem=Form.Item;
const Option = Select.Option;
const RangePicker = DatePicker.RangePicker;
import {FetchUtil} from '../utils/fetchUtil';
import {trim} from '../utils/validateUtil';
import './ListEvent.less';
export default class DashBoard extends React.Component{
constructor(props){
super(props);
this.state={
modelId:'',
loading:true,
modelList:[],
dashboardUrl:''
}
}
componentDidMount(){
if(!this.props.model.dashboardUrl){
Modal.warning({
title: '信息提醒',
content: '该模型统计报表未初始化!',
});
}
}
componentWillReceiveProps(nextProps){
if(this.props.modelId!=nextProps.modelId&&!nextProps.model.dashboardUrl){
Modal.warning({
title: '信息提醒',
content: '该模型统计报表未初始化!',
});
}
}
render(){
return (
<div className="ant-layout-content">
<iframe ref="dashBoard" name="dashBoard" src={this.props.model.dashboardUrl} style={{minHeight:'800px',width:'100%',border:'0px'}}></iframe>
</div>);
}
}

View File

@@ -0,0 +1,99 @@
import React from 'react';
import {Button,Modal,Tree,Tooltip} from 'antd';
const TreeNode = Tree.TreeNode;
import {FetchUtil,fetchVersion} from '../../utils/fetchUtil';
export default class ExportField extends React.Component{
constructor(props){
super(props);
this.state={
loading:false,
visible:false,
selectedKeys:[]
};
console.log(this.props.eventFieldList);
}
showModal=()=>{
this.setState({
visible:true
});
}
handleOk=()=>{
if(this.state.selectedKeys.length==0){
Modal.error({
title: '请选择需要导出的字段'
});
}
let param={
fields:[],
preItems:[],
activations:[],
rules:[]
};
this.state.selectedKeys.forEach((info)=>{
if(info&&info.indexOf('.')!=-1){
let arr=info.split('.');
param[arr[0]].push(arr[1]);
}
});
FetchUtil('/event/export','POST',JSON.stringify(param),
(data) => {
var url=fetchVersion+"/event/download";
var a = document.createElement('a');
var filename = 'download.xlsx';
a.href = url;
a.download = filename;
a.click();
});
}
handleCancel=()=>{
this.setState({
selectedKeys:[],
visible:false
})
}
onSelect=(selectedKeys)=>{
this.setState({ selectedKeys });
}
render(){
return (
<span>
{this.props.disabled?
<Tooltip title="导出前请先执行查询!">
<Button disabled type="primary" loading={this.state.loading} onClick={this.showModal}>导出</Button>
</Tooltip>
:<Button type="primary" loading={this.state.loading} onClick={this.showModal}>导出</Button>
}
<Modal title="选择需要导出的字段" visible={this.state.visible} maskClosable={false} closable={false} onOk={this.handleOk} okText={'导出报表'} onCancel={this.handleCancel}>
{this.props.eventFieldList.length==0?'该模型无字段可选':
<Tree checkable onCheck={this.onCheck} onCheck={this.onSelect} selectedKeys={this.state.selectedKeys}>
{this.props.eventFieldList.map((info,i)=>{
return (
<TreeNode key={info.value} title={info.label}>
{info.children.map((child,i)=>{
return <TreeNode key={info.value+'.'+child.value} title={child.label}/>
})}
</TreeNode>
);
})}
</Tree>
}
</Modal>
</span>
)
}
}

View File

@@ -0,0 +1,93 @@
import React from 'react';
import {Switch} from 'antd';
import './Collapse.less';
export default class Collapse extends React.Component{
constructor(props){
super(props);
this.state={
height:0
}
//this.props.handleClick(this);
}
slideDown=()=>{
if(this.state.height=="auto"){
return;
}
if(this.state.height<this.refs.pChild.offsetHeight){
this.setState({
height:this.state.height+15
},()=>{
setTimeout(this.slideDown,1);
})
}
else{
this.setState({
height:"auto"
})
}
}
slideUp=()=>{
if(this.state.height=="auto"){
this.state.height=this.refs.pChild.offsetHeight;
}
if(this.state.height>0){
this.setState({
height:this.state.height-15
},()=>{
setTimeout(this.slideUp,1);
})
}
else{
this.setState({
height:0
})
}
}
handleClick=()=>{
this.props.handleClick();
}
componentWillReceiveProps(nextProps){
if(nextProps.slide){
this.slideDown();
}
else{
this.slideUp();
}
}
handleDragStart=()=>{
console.log(2);
}
switchClick=(e)=>{
e.stopPropagation();
}
render(){
return (
<div className="p-block" draggable="true" onDragStart={this.handleDragStart}>
<div className={'p-block-titles'+(this.props.slide?' p-block-title-select':'')} onClick={this.handleClick}>
<div className='p-block-title-left'>{this.props.title}</div>
{this.props.switcher!=undefined?
<div className='p-block-title-right' onClick={this.switchClick}><Switch /></div>
:''}
</div>
<div className={'p-block-contents'+(this.props.slide?' p-block-content-select':'')} style={{height:this.state.height}} ref="pContent">
<div ref="pChild" className="p-block-main">
{this.props.children}
</div>
</div>
</div>
);
}
}

View File

@@ -0,0 +1,48 @@
.p-block{
margin-bottom:8px;
}
.p-block-titles{
height: 40px;
border:1px solid #d9d9d9;
padding-left: 30px;
cursor:pointer;
border-radius: 7px;
}
.p-block-titles:hover{
background-color:#FAFAFA;
}
.p-block-title-left{
float:left;
line-height:38px;
}
.p-block-title-right{
float:right;
line-height:34px;
margin-right:20px;
}
.p-block-title-select{
background-color:#f7f7f7;
border-bottom-right-radius:0;
border-bottom-left-radius:0;
border-bottom:none;
}
.p-block-contents{
clear:both;
background-color:white;
overflow: hidden;
}
.p-block-content-select{
border:1px solid #d9d9d9;
border-top:none;
}
.p-block-main{
padding:20px
}

View File

@@ -0,0 +1,48 @@
import React from 'react';
export default class CollapseGroup extends React.Component{
constructor(props){
super(props);
this.state={
activeKey:'',
}
}
getItems=()=>{
return this.props.children.map((child,index)=>{
const key=child.key;
const props={
slide:child.key==this.state.activeKey?true:false,
index:index,
handleClick:()=>{
if(this.state.activeKey==key){
this.setState({
activeKey:''
})
}
else{
this.setState({
activeKey:key
});
}
}
}
return React.cloneElement(child,props);
})
}
render(){
return (
<div>
{this.getItems()}
</div>
);
}
}

View File

@@ -0,0 +1,80 @@
import React from 'react';
import {Icon,Popover} from 'antd';
import CollapseGroup from './CollapseGroup';
import Collapse from './Collapse';
import './Test.less';
import {FetchUtil} from '../utils/fetchUtil';
export default class Test extends React.Component{
constructor(props){
super(props);
this.state={
slide:false,
height:0
}
}
slideDown=()=>{
if(this.state.height<this.refs.pChild.offsetHeight){
this.setState({
height:this.state.height+=5
})
setTimeout(this.slideDown,10);
}
else{
this.setState({
height:"auto",
slide:true
})
}
}
slideUp=()=>{
if(this.state.height>0){
this.setState({
height:this.state.height-=5
})
setTimeout(this.slideUp,10);
}
else{
this.setState({
height:0,
slide:false
})
}
}
handleClick=()=>{
// if(this.state.slide==false){
// this.slideDown();
// }
// else{
// let height=this.refs.pContent.clientHeight;
// this.setState({
// height:height
// },this.slideUp)
// }
// this.setState({
// slide:!this.state.slide
// })
}
render(){
var content=<span>hello world</span>;
return (
<div className="ant-layout-content">
<CollapseGroup>
<Collapse key="a" title="a" ><div>ad<br/>asfdasdflasdf</div></Collapse>
<Collapse key="b" title="b"><div>bd</div></Collapse>
<Collapse key="c" title="c"><div>cd</div></Collapse>
</CollapseGroup>
</div>
);
}
}

View File

@@ -0,0 +1,18 @@
.p-block-title{
height: 40px;
border:1px solid #d9d9d9;
line-height: 40px;
padding-left: 30px;
cursor:pointer;
border-radius: 3px;
}
.p-block-title:hover{
background-color:#f7f7f7;
}
.p-block-content{
border:1px solid #d9d9d9;
background-color:white;
overflow: hidden;
}

View File

@@ -0,0 +1,67 @@
import {Modal,message} from 'antd';
import 'whatwg-fetch';
import 'es6-promise/dist/es6-promise.min.js';
import 'fetch-ie8/fetch.js';
export var fetchVersion='/services/v1';
export var FetchUtil=function(url,method,param,callback,done=()=>{}){
let config={
credentials: 'include'
};
let hide=null;
if(method!='GET'){
config.method=method;
config.headers={"Content-Type": "application/json"};
config.body=param;
hide = message.loading('正在执行中...', 0);
}
return fetch(fetchVersion+url,config)
.then((res) => {
if(method!='GET'){hide();}
if(res.ok){
return res.json();
}
else{
if(window.modal==undefined){
window.modal=Modal.error({
title: '系统错误',
content: '请检查是否有参数配置错误',
onOk:()=>{
window.modal=undefined;
}
});
}
}
})
.then((data)=>{
if(!data.success&&data.code==600){
if(window.modal==undefined){
window.modal=Modal.error({
title: '您尚未登录',
content: '请返回登录页面重新登录',
onOk:()=>{
window.modal=undefined;
window.location.href="#/login";
}
});
}
}
else if(!data.success){
if(window.modal==undefined){
window.modal=Modal.error({
title: '系统错误',
content: data.msg,
});
}
}
else{
callback(data);
}
done();
})
.catch((e) => {
console.log(e.message);
});
}

View File

@@ -0,0 +1,191 @@
function getIndent(level) {
var result = "";
for (var i = 0; i < level; i++) {
result = result + " ";
}
return result;
}
function getExpression(expreObject) {
if(!expreObject){
return null;
}
var className = expreObject["class"];
var type = expreObject["type"];
var column = expreObject["column"];
var value = expreObject["value"];
//console.log(expreObject);
//console.log(value);
if (className == "ENTATTR") // 选择的数据类型
return "data." + column;
else if (className == "CONST") {
if (type == "STRING") {
return "'" + value + "'";
} else if (type == "DOUBLE") {
return "" + value;
} else {
return "" + value;
}
}
}
function processRule(ruleObject, level) {
var className = ruleObject["class"];
var enabled = ruleObject["enabled"];
var operator = ruleObject["operator"];
var expressions = ruleObject["expressions"];
if (className == "PDCT") {
return processRules(ruleObject, level + 1);
}
// if (operator=="Equal") {
// return getExpression(expressions[0]) + "==" + getExpression(expressions[1]);
// } else if (operator=="InList") {
// return getExpression(expressions[0]) + " in blackList["+getExpression(expressions[1])+"]";
// } else if (operator=="IsNull") {
// return getExpression(expressions[0]) + " is null";
// } else if (operator=="Field_Equal") {
// return getExpression(expressions[0]) + "==" + getExpression(expressions[1]);
// } else if (operator=="StartsWith") {
// return getExpression(expressions[0]) + ".startsWith("+getExpression(expressions[1])+")";
// }
//console.log(ruleObject);
switch (operator) {
case "StartsWith":
return getExpression(expressions[0]) + ".startsWith(" + getExpression(expressions[1]) + ")";
case "NotStartsWith":
return "!" + getExpression(expressions[0]) + ".startsWith(" + getExpression(expressions[1]) + ")";
case "Contains":
return getExpression(expressions[0]) + ".contains(" + getExpression(expressions[1]) + ")";
case "NotContains":
return "!" + getExpression(expressions[0]) + ".contains(" + getExpression(expressions[1]) + ")";
case "Equal":
case "Field_Equal":
return getExpression(expressions[0]) + "==" + getExpression(expressions[1]);
case "NotEqual":
case "Field_Not_Equal":
return getExpression(expressions[0]) + "!=" + getExpression(expressions[1]);
case "Less":
case "Field_Less":
return getExpression(expressions[0]) + "<" + getExpression(expressions[1]);
case "Less_Equal":
case "Field_Less_Equal":
return getExpression(expressions[0]) + "<=" + getExpression(expressions[1]);
case "Greater":
case "Field_Greater":
return getExpression(expressions[0]) + ">" + getExpression(expressions[1]);
case "Greater_Equal":
case "Field_Greater_Equal":
return getExpression(expressions[0]) + ">=" + getExpression(expressions[1]);
case "InList":
return "lists." + getExpression(expressions[1]) + ".containsKey(" + getExpression(expressions[0]) + ")";
case "NotInList":
return "!lists." + getExpression(expressions[1]) + ".containsKey(" + getExpression(expressions[0]) + ")";
case "IsNull":
return "!" + getExpression(expressions[0]);
case "IsNotNull":
return getExpression(expressions[0]);
}
}
function processRules(jsonObject, level) {
var className = jsonObject["class"];
var enabled = jsonObject["enabled"];
var linking = jsonObject["linking"];
var conditions = jsonObject["conditions"];
var length = conditions.length;
var result="";
if (linking == "NotAll" || linking == "None") {
result += "!";
}
result += "(";
for (var i in conditions) {
result = result + processRule(conditions[i], level);
if (i != length - 1) {
// if (linking=="All") {
// result = result + "\n"+getIndent(level)+"and ";
// } else if (linking=="Any") {
// result = result + "\n"+getIndent(level)+"or ";
// }
switch (linking) {
case "All":
case "NotAll":
result += "&&";
break;
case "Any":
case "None":
result += "||";
break;
}
}
}
return result + ")";
}
export var generateScript = function(jsonObject, className) {
if(jsonObject==null){
return '';
}
var script = "class " + className + "CheckScript {" + "\n";
script += " public boolean check(def data, def lists) {";
script += " if (" + processRules(jsonObject, 1) + ")\n";
script += " return true;" + "\n";
script += " else" + "\n";
script += " return false;" + "\n";
script += "}";
script += "}";
return script;
}
export let validateRules = function(jsonObject) {
if(jsonObject==null){
return true;
}
let conditions = jsonObject["conditions"];
for (let i in conditions) {
if(!validateRule(conditions[i])){
return false;
}
}
return true;
}
function validateRule(ruleObject){
var className = ruleObject["class"];
var operator = ruleObject["operator"];
var expressions = ruleObject["expressions"];
if (className == "PDCT") {
return validateRules(ruleObject);
}
if(!operator){
return false;
}
switch (operator) {
case "StartsWith":
case "NotStartsWith":
case "Contains":
case "NotContains":
case "Equal":
case "Field_Equal":
case "NotEqual":
case "Field_Not_Equal":
case "Less":
case "Field_Less":
case "Less_Equal":
case "Field_Less_Equal":
case "Greater":
case "Field_Greater":
case "Greater_Equal":
case "Field_Greater_Equal":
case "InList":
case "NotInList":
return getExpression(expressions[1])&&getExpression(expressions[0]);
case "IsNull":
case "IsNotNull":
return getExpression(expressions[0])&&true;
}
return true;
}

View File

@@ -0,0 +1,99 @@
export var Operator = {
StartsWith:{label:'以...开始',value:'StartsWith',nextType:'input'},
Contains:{label:'包含',value:'Contains',nextType:'input'},
Equal : {label:'等于',value:'Equal',nextType:'input'},
Less : {label:'小于',value:'Less',nextType:'input'},
Less_Equal : {label:'小于等于',value:'Less_Equal',nextType:'input'},
Greater : {label:'大于',value:'Greater',nextType:'input'},
Greater_Equal : {label:'大于等于',value:'Greater_Equal',nextType:'input'},
InList : {label:'在列表...中',value:'InList',nextType:'list'},
NotStartsWith : {label:'不以...开始',value:'NotStartsWith',nextType:'input'},
NotContains : {label:'不包含',value:'NotContains',nextType:'input'},
NotEqual : {label:'不等于',value:'NotEqual',nextType:'input'},
NotInList : {label:'不在列表...中',value:'NotInList',nextType:'list'},
IsNull : {label:'为空',value:'IsNull',nextType:'empty'},
IsNotNull : {label:'不为空',value:'IsNotNull',nextType:'empty'},
Field_Greater : {label:'大于(字段)',value:'Field_Greater',nextType:'field'},
Field_Less : {label:'小于(字段)',value:'Field_Less',nextType:'field'},
Field_Greater_Equal : {label:'大于等于(字段)',value:'Field_Greater_Equal',nextType:'field'},
Field_Less_Equal : {label:'小于等于(字段)',value:'Field_Less_Equal',nextType:'field'},
Field_Equal : {label:'等于(字段)',value:'Field_Equal',nextType:'field'},
Field_Not_Equal : {label:'不等于(字段)',value:'Field_Not_Equal',nextType:'field'}
};
/*nextType:{
input,list,empty,field
}
*/
export var operatorMap={
'STRING':[
Operator.StartsWith,
Operator.NotStartsWith,
Operator.Contains,
Operator.NotContains,
Operator.Equal,
Operator.NotEqual,
Operator.InList,
Operator.NotInList,
Operator.IsNull,
Operator.IsNotNull,
Operator.Field_Equal,
Operator.Field_Not_Equal
],
'INTEGER':[
Operator.Equal,
Operator.NotEqual,
Operator.InList,
Operator.NotInList,
Operator.Less,
Operator.Less_Equal,
Operator.Greater,
Operator.Greater_Equal,
Operator.IsNull,
Operator.IsNotNull,
Operator.Field_Greater,
Operator.Field_Less,
Operator.Field_Greater_Equal,
Operator.Field_Less_Equal,
Operator.Field_Equal,
Operator.Field_Not_Equal
],
'DOUBLE':[
Operator.Equal,
Operator.NotEqual,
Operator.InList,
Operator.NotInList,
Operator.Less,
Operator.Less_Equal,
Operator.Greater,
Operator.Greater_Equal,
Operator.IsNull,
Operator.IsNotNull,
Operator.Field_Greater,
Operator.Field_Less,
Operator.Field_Greater_Equal,
Operator.Field_Less_Equal,
Operator.Field_Equal,
Operator.Field_Not_Equal
],
'LONG':[
Operator.Equal,
Operator.NotEqual,
Operator.InList,
Operator.NotInList,
Operator.Less,
Operator.Less_Equal,
Operator.Greater,
Operator.Greater_Equal,
Operator.IsNull,
Operator.IsNotNull,
Operator.Field_Greater,
Operator.Field_Less,
Operator.Field_Greater_Equal,
Operator.Field_Less_Equal,
Operator.Field_Equal,
Operator.Field_Not_Equal
],
'':[]
};

View File

@@ -0,0 +1,3 @@
export var trim=function(str){
   return str.replace(/(^\s*)|(\s*$)/g, "");
}

124
webapp/index.jsx Normal file
View File

@@ -0,0 +1,124 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Link, hashHistory,browserHistory,IndexRoute,IndexRedirect } from 'react-router';
import Abstraction from './component/abstraction/Abstraction';
import AbstractionList from './component/abstraction/AbstractionList';
import Activation from './component/activation/Activation';
import Datalist from './component/datalist/Datalist';
import DatalistRecord from './component/datalist/DatalistRecord';
import Field from './component/field/Field';
import ModelList from './component/model/ModelList';
import Model from './component/model/Model';
import PreItem from './component/preItem/PreItem';
import RuleList from './component/activation/RuleList';
import HistoryRecordList from './component/activation/HistoryRecordList';
import Report from './component/report/Report';
import ListEvent from './component/report/ListEvent';
import RuleGraph from './component/report/RuleGraph';
import ListRule from './component/report/ListRule';
import Rule from './component/report/ListRule';
import DashBoard from './component/report/DashBoard';
//import Test from './component/test/Test';
import Index from './component/Index';
import Login from './component/Login';
// 引入Ant-Design样式 & Animate.CSS样式
import 'antd/dist/antd.min.css';
import 'animate.css/animate.min.css';
import './main.less';
class NotFound extends React.Component{
render() {
return (
<div>
<div className="ibox">
<div className="ibox-content">
<div style={{
width:450,margin:"200px auto",fontSize:26
}}>功能尚未完成或页面未找到敬请期待</div>
</div>
</div>
</div>
);
}
};
class Welcome extends React.Component{
render() {
return (
<div>
<div className="ibox">
<div className="ibox-content">
<h2>欢迎登录风控引擎管理平台</h2>
</div>
</div>
</div>
);
}
};
class App extends React.Component{
render(){
return (
<div>
{this.props.children}
</div>
);
}
}
class AppRoutes extends React.Component{
render() {
return (
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRedirect to="login"/>
<Route path="/welcome" component={Welcome}>
</Route>
<Route path="/login" component={Login}>
</Route>
<Route path="/index" component={Index}>
<Route path="/modelList" component={ModelList}/>
<Route path="/model/:id" component={Model}>
<IndexRedirect to="/field/:id"/>
<Route path="/field/:id" component={Field}/>
<Route path="/activation/:id" component={Activation}/>
<Route path="/datalist/:id" component={Datalist}/>
<Route path="/datalistRecord/:id/:datalistId" component={DatalistRecord}/>
<Route path="/preItem/:id" component={PreItem}/>
<Route path="/ruleList/:id/:activationId" component={RuleList}/>
<Route path="/historyRecordList/:id/:activationId/:ruleId" component={HistoryRecordList}/>
<Route path="/abstractionList/:id" component={AbstractionList}/>
{/*<Route path="/test/:id" component={Test}/>*/}
</Route>
<Route path="/report" component={Report}>
<IndexRedirect to="/event"/>
<Route path="/event" component={ListEvent}/>
<Route path="/graph" component={RuleGraph}/>
<Route path="/rule" component={ListRule}/>
<Route path="/ruleid/:modelId/:ruleId/:activationName" component={ListEvent}/>
<Route path="/dashboard" component={DashBoard}/>
</Route>
</Route>
<Route path="*" component={Index}>
<IndexRoute component={NotFound} />
</Route>
</Route>
</Router>
);
}
};
ReactDOM.render(
<AppRoutes />
, document.getElementById("react-content"));

14
webapp/main.less Normal file
View File

@@ -0,0 +1,14 @@
body{
margin:0;
padding:0;
}
html,body{
height:100%;
background: #ececec;
}
#react-content{
height:100%;
}