Explorar o código

【看板客戶端】【大量修改】

zhuth %!s(int64=8) %!d(string=hai) anos
pai
achega
57178aa8e9

+ 3 - 2
kanban-client/app/component/Layout.js

@@ -30,11 +30,12 @@ class BasicLayout extends React.Component {
   static defaultProps = {
     className: "layout",
     items: [],
-    rowHeight: 63.7,
     onLayoutChange: (l) => {  },
     cols: 10, // 屏幕横宽最大值为10
     margin: [0, 0], // 元素间隔为0
-    verticalCompact: false
+    verticalCompact: false,
+    useCSSTransforms: false, // 不使用动画
+    autoSize: true
   }
 
   // 创建div元素

+ 129 - 357
kanban-client/app/component/Table.jsx

@@ -2,124 +2,26 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import Table from '../../src/Table/index.js';
 import Animate from 'rc-animate';
-import $ from 'jquery';
-import { sort, remove } from '../utils/ArrayUtils.js';
 import renders from '../utils/RenderUtils.js';
-import { createFormData } from '../utils/FetchUtil.js';
 import '../../assets/Table/index.less';
+import '../../assets/Table/animation.less';
 
 
 class TableModel extends React.Component {
 	constructor(props) {
 		super(props);
-		this.sorts = []; // 排序规则
-		this.groups = []; // 分组规则
+		this.newProps = props;
 		this.columns = this.setColumnsRender(props.columns);
 		this.state = {
-			data: props.data || [],
+			data: []
 		};
-		this.initState();
 		this.timerKeys = []; // 定时器key数组
 	}
-	// 初始化state
-	initState() {
-		let oriState = this.props.state;
-		for (let key in oriState) {
-			this.state[key] = oriState[key];
-		}
-	}
-	// 初始化检索columns,获得排序、分组信息
-	init() {
-		// 先重置
-		this.sorts = [];
-		this.groups = [];
-
-		let cols = this.columns;
-		let i = 0;
-		for (i; i < cols.length; i++) {
-			let col = cols[i];
-			if (Math.abs(col['sort']) > 0) {
-				this.sorts.push({
-					sortKey: col['dataIndex'],
-					sortLevel: Math.abs(col['sort']),
-					direction: col['sort'] > 0 ? 1 : -1
-				});
-			}
-			if (col['group'] == true) {
-				this.groups.push({
-					groupIndex: i
-				});
-			}
-		}
-		sort(this.sorts, [{ key: 'sortLevel', direction: 1 }]);
-	}
-	// 根据sort值分层排序data
-	sortData(data) {
-		let sortArray = this.sorts.map(function (s, i) {
-			return {
-				key: s.sortKey,
-				direction: s.direction
-			};
-		});
-		if (sortArray) { }
-		let sortData = sort(data || [], sortArray);
-		return sortData;
-	}
-	// 合并单元格
-	groupCells() {
-		let groupIndexs = this.groups.map(function (g, i) {
-			return g.groupIndex;
-		});
-		for (let a = 0; a < groupIndexs.length; a++) {
-			let cols = this.columns;
-			let col = cols[groupIndexs[a]];
-			this.grouping(col);
-		}
-	}
-	// 合并单元格实现
-	grouping(col) {
-		let data = this.state.data || [];
-		let dataIndex = col['dataIndex'];
-		let i = 0, j = 0, rowSpan = 1;
-		let p = [];
-		let oriRender = col.render;
-		for (i; i < data.length; i = i + rowSpan) {
-			rowSpan = 1;
-			// 被比较行内容
-			let now_dataValue = oriRender(data[i][dataIndex], data[i]).children;
-			for (j = i + 1; j < data.length; j++) {
-				// 比较行内容
-				let next_dataValue = oriRender(data[j][dataIndex], data[j]).children;
-				// 连续的两行内容相同时合并
-				if (next_dataValue == now_dataValue) {
-					rowSpan++;
-				}
-				// 只要找到一个不一样的马上跳出
-				else if (next_dataValue != now_dataValue) {
-					break;
-				}
-			}
-			// 合并参数
-			p.push({ start: i, rowSpan: rowSpan });
-		}
-		// 重写render方法,设置需要合并的单元格rowSpan
-		col.render = function (value, row, index) {
-			let obj = oriRender(value, row, index);
-			obj.props.rowSpan = 0;
-			for (let i = 0; i < p.length; i++) {
-				if (index == p[i].start) {
-					obj.props.rowSpan = p[i].rowSpan;
-					break;
-				}
-			}
-			return obj;
-		}
-	}
 	// 根据renderName设置每列的render
 	setColumnsRender(columns) {
 		let cols = columns;
 		// 如果需要增加序号列
-		if (this.props.index) {
+		if (this.newProps.index) {
 			cols.unshift({
 				key: 'index',
 				title: '序号',
@@ -131,162 +33,15 @@ class TableModel extends React.Component {
 			let col = cols[i];
 			let renderFunction = typeof (col.render) == 'string' ? renders[col.render] :
 				(typeof (col.render) == 'function' ? col.render :
-				renders[this.props.render] || function (_v, _r, _i) { return { children: _v, props: {} } });
+				renders[this.newProps.render] || function (_v, _r, _i) { return { children: _v, props: {} } });
 			col.render = renderFunction;
 		}
 		return cols;
 	}
 	// 设置排序合并以及表头和表体列对齐
 	onShow() {
-		this.adaptiveScreenSize();
 		this.adaptiveRowSize();
-		this.init();
-		let headers = document.getElementsByTagName('th');
-		let rows = document.getElementsByClassName('rc-table-row');
-		let alignWidth = 0; // 该值等于((@horizontal-padding)*2+(border宽度*2))
-		// 如果需要滚动需要调整表头和表体
-		if (this.props.scroll && this.state.data && this.state.data.length > 0) {
-			if (rows.length > 0) {
-				let cells = rows[0].cells || [];
-				for (let i = 0; i < (cells.length - 1); i++) {
-					headers[i].width = headers[i].width || cells[i].offsetWidth - alignWidth;
-				}
-				for (let i = 0; i < (cells.length - 1); i++) {
-					cells[i].width = headers[i].offsetWidth - alignWidth;
-				}
-				this.autoScroll('y', rows.length);
-			}
-		}
-		// 如果不需要滚动只需要调整表头
-		else {
-			if (rows.length > 0) {
-				let cells = rows[0].cells || [];
-				let oldTrs = document.getElementsByClassName('fade-leave') || [];
-				for (let i = 0; i < (cells.length - 1); i++) {
-					//headers[i].width = headers[i].width || cells[i].offsetWidth - alignWidth;
-
-
-				}
-				let firstRow = rows[0];
-				let rowHeight = firstRow.offsetHeight;
-				let firstRowTop = firstRow.offsetTop;
-				let newTds = firstRow.getElementsByTagName('td');
-				// 动画效果定位实现部分
-				for (let i = 0; i < oldTrs.length; i++) {
-					// 先设置旧tr为绝对布局
-					oldTrs[i].style.position = 'absolute';
-					// 宽度为100%
-					oldTrs[i].style.width = '100%';
-					// 新tr的位置高度
-					let newTrTop = firstRowTop + rowHeight * i;
-					// 将旧tr上移覆盖新的tr
-					oldTrs[i].style.top = `${newTrTop}px`;
-					// 获得旧tds
-					let oldTds = oldTrs[i].getElementsByTagName('td');
-
-					for (let j = 0; j < oldTds.length; j++) {
-						// 设置旧td=新td宽度
-						oldTds[j].width = newTds[j].offsetWidth - alignWidth;
-					}
-				}
-			}
-		}
-		// }else {
-		// 	this.groupCells();
-		// }
-
-	}
-	// 设置自动滚动展示(y轴)
-	autoScroll(dir, count) {
-		let $el = $(".rc-table-body");
-
-		if ($el != undefined) {
-			$el.scrollTop(0);
-			// 默认滚动速度(越小越快)
-			let speed = this.props.scrollSpeed || 5;
-			// 一次移动行数
-			let rows = 2;
-			let _self = this;
-
-			function anim() {
-				stop();
-				// 一行高度
-				let tdHeight = document.getElementsByClassName('rc-table-row')[0].offsetHeight;
-
-				let titleEle = document.getElementsByClassName("rc-table-title")[0];
-				let headerEle = document.getElementsByClassName("rc-table-thead")[0];
-				let screenHeight = titleEle.parentNode.offsetHeight;
-				// body高度
-				let bodyHeight = screenHeight - titleEle.offsetHeight - headerEle.offsetHeight;
-				let st = $el.scrollTop();// 距离顶部高度
-				let sb = $el.prop("scrollHeight") - $el.innerHeight();// 滚动栏总高度
-				let scrollSpeed = sb / (bodyHeight / (speed * tdHeight)) * 100;
-
-				if (st < sb) {
-					$el.animate({ scrollTop: sb }, scrollSpeed * ((sb - st) / sb), 'linear', anim);
-				} else {
-					let data = _self.state.data;
-					// 一次调整两行以规避因单双行颜色不同造成的界面颜色变动
-					for (var i = 0; i < rows; i++) {
-						let firstRow = data[0];
-						data.push(firstRow);
-						data.shift();
-					}
-					_self.setState({
-						data: data
-					}, function () {
-						// 手动设置滚动条上移
-						$el.scrollTop(sb - tdHeight * rows);
-						anim();
-					});
-				}
-			}
-			function stop() {
-				$el.stop();
-			}
-			if ($el.prop("scrollHeight") - $el.innerHeight() > 0) {
-				anim();
-				$el.hover(stop, anim);
-			}
-		}
-	}
-
-	// 请求数据
-	fetchData(url, params, dataKey, load, callbackFunction) {
-		callbackFunction = callbackFunction || function () { };
-		let _self = this;
-		let time = new Date().getTime();
-		fetch(url, {
-			credentials: 'include',
-			method: 'POST',
-			mode: "no-cors",
-			headers: {
-				"Content-Type": 'application/json;charset=UTF-8'
-			},
-			body: createFormData(params),
-		}).then(function (response) {
-			return response.json();
-		}).then(function (json) {
-			console.log(json);
-			let data = json[dataKey];
-			if (load) {
-				_self.setState({
-					data: _self.sortData(data).map(function (record, index) {
-						record.key = index + '_' + time;
-						return record;
-					})
-				}, () => {
-					callbackFunction(_self, data);
-					_self.onShow();
-				})
-			} else {
-				callbackFunction(_self, data);
-			}
-
-			return data;
-		}).catch(function (ex) {
-			console.log('parsing failed', ex);
-		})
+		this.switchAnimate();
 	}
 
 	// 定时任务
@@ -296,108 +51,125 @@ class TableModel extends React.Component {
 		}.bind(this), obj.intervalTime || 3000));
 	}
 
-	// 滚动展示时自适应屏幕大小
-	adaptiveScreenSize() {
-		return;
-		let titleEle = document.getElementsByClassName("rc-table-title")[0];
-		let headerEle = document.getElementsByClassName("rc-table-thead")[0];
-		let screenHeight = titleEle.parentNode.offsetHeight;
-		let bodyHeight = document.getElementsByClassName("rc-table-body")[0].offsetHeight;
-		if (this.state.data.length > 0) {
-			bodyHeight = screenHeight - (titleEle ? titleEle.offsetHeight : 0) - headerEle.offsetHeight;
-		} else {
-			let noDataEle = document.getElementsByClassName("rc-table-placeholder")[0];
-			bodyHeight = screenHeight - (titleEle ? titleEle.offsetHeight : 0) - headerEle.offsetHeight - noDataEle.offsetHeight;
-		}
-		let bodyEle = document.getElementsByClassName("rc-table-body")[0];
-		if (this.props.scroll) {
-			bodyEle.style.height = `${bodyHeight}px`;
-			bodyEle.style.marginTop = "17px";
-		} else {
-			bodyEle.style.height = `${bodyHeight + 40}px`;
-		}
-	}
-
-	// 调整行高
+	// 调整行
 	adaptiveRowSize() {
-		return;
-		let headerEle = document.getElementsByClassName("rc-table-thead")[0];
-		let titleEle = document.getElementsByClassName("rc-table-title")[0];
-		// let screenHeight = window.innerHeight;
-		let screenHeight = titleEle.parentNode.offsetHeight;
-		let bodyHeight = screenHeight - (titleEle ? titleEle.offsetHeight : 0) - headerEle.offsetHeight;
+		var node = this._reactInternalInstance.getHostNode();
+		let bodyHeight = this.cHeight - node.getElementsByClassName('rc-table-title')[0].offsetHeight - node.getElementsByClassName('rc-table-thead')[0].offsetHeight - 4;
 		let count = this.state.data.length;
 		if (count == 0) { return; }
-		let tds = document.getElementsByTagName('td');
-		if (this.props.scroll && this.props.fetch) {
-			if (count < ((bodyHeight / tds[0].offsetHeight) + 2) && count > this.props.fetch.params.pageSize) {
-				for (let i = 0; i < tds.length; i++) {
-					tds[i].style.height = `${bodyHeight / (count - 2) + 0.1}px`;
-				}
+		let trs = node.getElementsByClassName('fade-enter');
+		for (let i = 0; i < trs.length; i++) {
+			// trs[i].style.width = `${bodyHeight/this.rowCount}px`;
+			trs[i].style.height = `${bodyHeight/this.rowCount}px`;
+		}
+	}
 
+	// 切换动画
+	switchAnimate() {
+		var node = this._reactInternalInstance.getHostNode()
+		let headers = node.getElementsByTagName('th');
+		let rows = node.getElementsByClassName('rc-table-row');
+		let alignWidth = 0; // 该值等于((@horizontal-padding)*2+(border宽度*2))
+		if (rows.length > 0) {
+			let cells = rows[0].cells || [];
+			let oldTrs = node.getElementsByClassName('fade-leave') || [];
+			for (let i = 0; i < (cells.length - 1); i++) {
+				//headers[i].width = headers[i].width || cells[i].offsetWidth - alignWidth;
 			}
-		} else {
-			for (let i = 0; i < tds.length; i++) {
-				if (this.props.fetch) {
-					tds[i].style.height = `${bodyHeight / this.state.data.length}px`;
+			let firstRow = rows[0];
+			let rowHeight = firstRow.offsetHeight;
+			let firstRowTop = firstRow.offsetTop + node.getElementsByClassName('rc-table-title')[0].offsetHeight;
+			let newTds = firstRow.getElementsByTagName('td');
+			// 动画效果定位实现部分
+			for (let i = 0; i < oldTrs.length; i++) {
+				// 先设置旧tr为绝对布局
+				oldTrs[i].style.position = 'absolute';
+				// 宽度为100%
+				oldTrs[i].style.width = '100%';
+				// 新tr的位置高度
+				let newTrTop = firstRowTop + rowHeight * i;
+				// 将旧tr上移覆盖新的tr
+				oldTrs[i].style.top = `${newTrTop}px`;
+				// 获得旧tds
+				let oldTds = oldTrs[i].getElementsByTagName('td');
+				for (let j = 0; j < oldTds.length; j++) {
+					// 设置旧td=新td宽度
+					oldTds[j].style.maxWidth = `${newTds[j].offsetWidth - 2}px`;
+					oldTds[j].style.minWidth = `${newTds[j].offsetWidth - 2}px`;
+					oldTds[j].width = newTds[j].offsetWidth - 2;
+					oldTds[j].height = newTds[j].offsetHeight -2;
 				}
 			}
 		}
 	}
-	componentWillMount() {
-		let beforeRenderFunction = this.props.beforeRender;
-		if (beforeRenderFunction) {
-			beforeRenderFunction(this);
+
+	/**
+	 * 根据容器高度分割data展示
+	 */
+	splitData() {
+		this.cHeight = this._reactInternalInstance._hostParent._hostNode.offsetHeight;
+		this.cWidth = this._reactInternalInstance._hostParent._hostNode.offsetWidth;
+		this.rowCount = Math.round(this.cHeight/40);
+		this.rowHeight = this.cHeight/this.rowCount;
+		let a = this.newProps.data;
+		let result = [];
+		let j = 0;
+		for(let i = 0; i<a.length;i = i + j){
+			let arr = [];
+			for(j = 0; j < this.rowCount && a[i+j]; j++) {
+				arr.push(a[i+j]);
+			}
+			result.push(arr);
 		}
+
+		this.dataArr = result;
+		this.dataIndex = 0;
 	}
-	componentDidMount() {
-		this.onShow();
-		if (this.props.timer) {
-			this.props.timer.map(function (obj, index) {
-				this.timing(obj);
-			}.bind(this));
+
+	setRefresh() {
+		this.changeData();
+		if(this.dataArr.length > 1) {
+			this.timing({
+				intervalFunction: function() {
+					this.changeData();
+				}.bind(this),
+				intervalTime: 2000
+			});
 		}
-		// 如果配置了fetch
-		if (this.props.fetch) {
-			// 是否自动加载fetch数据(将会覆盖设定的data)
-			if (this.props.fetch.autoLoad) {
-				this.fetchData(this.props.fetch.url, this.props.fetch.params, this.props.fetch.dataKey, true);
-			}
-			if (this.props.fetch.autoRefresh > 0) {
-				this.fetchParams = Object.assign({}, this.props.fetch.params);
-				this.fetchParams.page = this.props.fetch.autoLoad ? 2 : 1;
-				let func = function () {
+	}
 
-					this.fetchData(this.props.fetch.url, this.fetchParams, this.props.fetch.dataKey, true, function (th) {
-						if (th.state.data.length < th.props.fetch.params.pageSize) {
-							th.fetchParams = Object.assign({}, th.props.fetch.params);
-						} else {
-							th.fetchParams.page = th.fetchParams.page + 1;
-						}
-					});
-				}.bind(this);
-				this.timing({
-					intervalTime: this.props.fetch.autoRefresh,
-					intervalFunction: func.bind(this)
-				});
+	changeData() {
+		this.setState({
+			data: this.dataArr[this.dataIndex]
+		}, ()=>{
+			this.onShow();
+			this.dataIndex++;
+			if(this.dataIndex >= this.dataArr.length){
+				this.dataIndex = 0
 			}
-		}
+		});
 	}
-	componentWillUnmount() {
+
+	clearInterval() {
 		for (let timerKey in this.timerKeys) {
 			clearInterval(this.timerKeys[timerKey]);
-			remove(this.timerKeys, timerKey);
-		}
-		let $el = $(".rc-table-body");
-		if ($el != undefined) {
-			$el.stop();
 		}
 	}
+
+	componentWillMount() {
+	}
+	componentDidMount() {
+		this.splitData();
+		this.setRefresh();
+	}
+	componentWillUnmount() {
+		this.clearInterval();
+	}
 	componentWillReceiveProps(nextProps) {
-		this.setState({
-			columns: nextProps.columns,
-			data: nextProps.data
-		});
+		this.clearInterval();
+		this.newProps = nextProps;
+		this.splitData();
+		this.setRefresh();
 	}
 	getBodyWrapper(body) {
 		return (
@@ -411,7 +183,7 @@ class TableModel extends React.Component {
 	}
 
 	getTitle() {
-		const { title } = this.props;
+		const { title } = this.newProps;
 		if (renders[title]) {
 			return renders[title];
 		} else {
@@ -426,30 +198,30 @@ class TableModel extends React.Component {
 		return (
 			<div style={{height: '100%', overflow: 'hidden'}}>
 				<Table
-					prefixCls={this.props.prefixCls || 'rc-table'}
-					className={this.props.className}
-					useFixedHeader={this.props.useFixedHeader || false}
-					scroll={{ x: false, y: this.props.scroll } || { x: false, y: false }}
-					expandIconAsCell={this.props.expandIconAsCell || false}
-					expandIconColumnIndex={this.props.expandIconColumnIndex || 0}
-					rowKey={this.props.rowKey || 'key'}
-					rowClassName={this.props.rowClassName || function () { }}
-					rowRef={this.props.rowRef || function () { }}
-					defaultExpandedRowKeys={this.props.defaultExpandedRowKeys || []}
-					expandedRowKeys={this.props.expandedRowKeys || []}
-					defaultExpandAllRows={this.props.defaultExpandAllRows || false}
-					onExpandedRowsChange={this.props.onExpandedRowsChange || function () { }}
-					onExpand={this.props.onExpand || function () { }}
-					expandedRowClassName={this.props.expandedRowClassName || function () { }}
-					indentSize={this.props.indentSize || 15}
-					onRowClick={this.props.onRowClick || function () { }}
-					onRowDoubleClick={this.props.onRowDoubleClick || function () { }}
-					onRowMouseEnter={this.props.onRowMouseEnter || function () { }}
-					onRowMouseLeave={this.props.onRowMouseLeave || function () { }}
-					showHeader={this.props.showHeader || true}
+					prefixCls={this.newProps.prefixCls || 'rc-table'}
+					className={this.newProps.className}
+					useFixedHeader={this.newProps.useFixedHeader || false}
+					scroll={{ x: false, y: this.newProps.scroll } || { x: false, y: false }}
+					expandIconAsCell={this.newProps.expandIconAsCell || false}
+					expandIconColumnIndex={this.newProps.expandIconColumnIndex || 0}
+					rowKey={this.newProps.rowKey || 'key'}
+					rowClassName={this.newProps.rowClassName || function () { }}
+					rowRef={this.newProps.rowRef || function () { }}
+					defaultExpandedRowKeys={this.newProps.defaultExpandedRowKeys || []}
+					expandedRowKeys={this.newProps.expandedRowKeys || []}
+					defaultExpandAllRows={this.newProps.defaultExpandAllRows || false}
+					onExpandedRowsChange={this.newProps.onExpandedRowsChange || function () { }}
+					onExpand={this.newProps.onExpand || function () { }}
+					expandedRowClassName={this.newProps.expandedRowClassName || function () { }}
+					indentSize={this.newProps.indentSize || 15}
+					onRowClick={this.newProps.onRowClick || function () { }}
+					onRowDoubleClick={this.newProps.onRowDoubleClick || function () { }}
+					onRowMouseEnter={this.newProps.onRowMouseEnter || function () { }}
+					onRowMouseLeave={this.newProps.onRowMouseLeave || function () { }}
+					showHeader={this.newProps.showHeader || true}
 					title={this.getTitle()}
-					footer={this.props.footer}
-					emptyText={this.props.emptyText || 'No Data'}
+					footer={this.newProps.footer}
+					emptyText={this.newProps.emptyText || 'No Data'}
 					columns={this.columns || []}
 					data={this.state.data || []}
 					getBodyWrapper={this.getBodyWrapper}

+ 168 - 61
kanban-client/app/component/converter.js

@@ -2,9 +2,16 @@ import Renders from '../utils/RenderUtils.js';
 
 function converter(data) {
     let { title, content } = data;
+    // title = {
+    //     config: {
+    //         render: 'titleRender'
+    //     }
+    // }
     let { items } = content;
+    let itemsarr = items instanceof Array ? items : [items];
+	
     let me = this;
-    let newItems = items.map((v, i) => {
+    let newItems = itemsarr.map(function (v, i) {
         let type = v.type;
         if(type == 'form') {
             return formConfig(v);
@@ -42,12 +49,12 @@ function titleConfig(model) {
 }
 
 function formConfig(model) {
-    let { type, config, layout } = model;
-    let { header, fieldstyle, valuestyle, columns, data} = config;
-    
+    let { type, header, config, layout } = model;
+    let { fieldstyle, valuestyle, columns, data} = config;
     let c = {
         type: 'form',
         config: {
+            fontSize: getFontSize(layout),
             header: Renders[header] || header,
             fieldStyle: parseStr(fieldstyle),
             valueStyle: parseStr(valuestyle),
@@ -66,6 +73,7 @@ function tableConfig(model) {
     return {
         type: 'table',
         config: {
+            fontSize: getFontSize(layout),
             title: Renders[title] || title,
             render: Renders[render],
             columns: columns.map((v, i) => {
@@ -84,44 +92,66 @@ function tableConfig(model) {
 function barConfig(model) {
     let { type, config, layout } = model;
     let { title, subtitle, xtitle, xtype, xfields, ytitle, ytype, yfields, series} = config;
-    
+    let f = xfields.replace(['['],'');
+    f = f.replace([']'],'');
+    let xf = f.split(',');
     return {
         type: 'charts',
         config: {
             option: {
-                title: {
-                    show: true,
-                    text: title,
-                    subtext: subtitle,
-                    textAlign: 'center',
-                    left: '50%',
-                    right: '50%',
-                    itemGap: 0,
-                    padding: 0
-                },
+                title: getChartsTitle(layout, title, subtitle),
                 tooltip: {
                     trigger: 'axis',
                     axisPointer: {
                         type: 'shadow'
                     }
                 },
+                grid: {
+                    top: layout.h < 60 ? '30%' : '25%',
+                    bottom: '15%'
+                },
                 legend: {
-                    right: '3%',
+                    top: '15%',
+                    padding: 0,
+                    orient: 'horizontal',
+                    itemGap: layout.w,
+                    textStyle: {
+                        fontSize: getFontSize(layout) * 0.7
+                    },
                     data: series.map((v, i) => {
                         return v.name
                     })
                 },
                 xAxis: [{
-                    type : xtype,
-                    data : xfields,
-                    axisTick: {
-                        alignWithLabel: true
+                    type: xtype,
+                    data: xf,
+                    name: xtitle,
+                    nameGap: 0,
+                    nameRotate: 270,
+                    nameTextStyle: {
+                        fontSize: getFontSize(layout) * .7
+                    },
+                    axisLabel: {
+                        rotate:  getScreenSize().screenWidth * layout.w / xf.length / 100 < 60 ? 45 : 0,
+                        interval: 0,
+                        textStyle: {
+                            fontSize: getFontSize(layout) * .7
+                        }
                     }
                 }],
                 yAxis: [{
-                    type: ytype == 'numeric' ? 'value' : ytype
+                    name: ytitle,
+                    type: ytype == 'numeric' ? 'value' : ytype,
+                    nameTextStyle: {
+                        fontSize: getFontSize(layout) * .7
+                    },
+                    axisLabel: {
+                        textStyle: {
+                            fontSize: getFontSize(layout) * .7
+                        }
+                    }
                 }],
-                series: getBarSeries(series)
+                series: getBarSeries(layout, series)
             }
         },
         layout: getLayout(layout)
@@ -131,39 +161,60 @@ function barConfig(model) {
 function lineConfig(model) {
     let { type, config, layout } = model;
     let { title, subtitle, xtitle, xtype, xfields, ytitle, ytype, yfields, series} = config;
-    
+    let f = xfields.replace(['['],'');
+    f = f.replace([']'],'');
+    let xf = f.split(',');
     return {
         type: 'charts',
         config: {
             option: {
-                title: {
-                    show: true,
-                    text: title,
-                    subtext: subtitle,
-                    textAlign: 'center',
-                    left: '50%',
-                    right: '50%',
-                    itemGap: 0,
-                    padding: 0
-                },
+                title: getChartsTitle(layout, title, subtitle),
                 tooltip: {
                     trigger: 'axis'
                 },
+                grid: {
+                    top: layout.h < 60 ? '30%' : '25%',
+                    bottom: '15%'
+                },
                 legend: {
-                    right: '3%',
+                    top: '15%',
+                    padding: 0,
+                    orient: 'horizontal',
+                    itemGap: layout.w,
+                    textStyle: {
+                        fontSize: getFontSize(layout) * 0.7
+                    },
                     data: series.map((v, i) => {
                         return v.name
                     })
                 },
                 xAxis: [{
                     type : xtype,
-                    data : xfields,
-                    axisTick: {
-                        alignWithLabel: true
+                    data : xf,
+                    name: xtitle,
+                    nameRotate: 270,
+                    nameTextStyle: {
+                        fontSize: getFontSize(layout) * .7
+                    },
+                    axisLabel: {
+                        rotate:  getScreenSize().screenWidth * layout.w / xf.length / 100 < 60 ? 45 : 0,
+                        interval: 0,
+                        textStyle: {
+                            fontSize: getFontSize(layout) * .7
+                        }
                     }
                 }],
                 yAxis: [{
-                    type: ytype == 'numeric' ? 'value' : ytype
+                    name: ytitle,
+                    type: ytype == 'numeric' ? 'value' : ytype,
+                    nameTextStyle: {
+                        fontSize: getFontSize(layout) * .7
+                    },
+                    axisLabel: {
+                        textStyle: {
+                            fontSize: getFontSize(layout) * .7
+                        }
+                    }
                 }],
                 series: getLineSeries(series)
             }
@@ -183,34 +234,42 @@ function pieConfig(model) {
         type: 'charts',
         config: {
             option: {
-                title: {
-                    show: true,
-                    text: title,
-                    subtext: subtitle,
-                    textAlign: 'center',
-                    left: '50%',
-                    right: '50%',
-                    itemGap: 0,
-                    padding: 0
-                },
+                title: getChartsTitle(layout, title, subtitle),
                 tooltip: {
                     trigger: 'item',
                     formatter: '{a} <br/>{b} : {c} ({d}%)'
                 },
-                legend: {
-                    bottom: '5%',
-                    data: series.map((v, i) => {
-                        return v.name
-                    })
-                },
-                series: getPieSeries(series)
+                legend: getPieLegend(layout, series),
+                series: getPieSeries(layout, series)
             }
         },
         layout: getLayout(layout)
     }
 }
 
-function getBarSeries(s) {
+function getChartsTitle(layout, title, subtitle) {
+    var title = {
+        show: true,
+        text: title,
+        subtext: subtitle,
+        textAlign: 'center',
+        textStyle: {
+            verticalAlign: 'top',
+            fontSize: getFontSize(layout) * 1
+        },
+        subtextStyle: {
+            verticalAlign: 'top',
+            fontSize: getFontSize(layout) * 0.75
+        },
+        left: '50%',
+        right: '50%',
+        itemGap: 0,
+        padding: 0
+    }
+    return title;
+}
+
+function getBarSeries(l, s) {
     let series = [];
     const model = {
         type: 'bar',
@@ -218,7 +277,10 @@ function getBarSeries(s) {
             normal: {
                 show: true,
                 position: 'top',
-                formatter: '{c}'
+                formatter: '{c}',
+                textStyle: {
+                    fontSize: getFontSize(l) * .7
+                }
             }
         },
         barGap: 0
@@ -253,13 +315,18 @@ function getLineSeries(s) {
     return series;
 }
 
-function getPieSeries(s) {
+function getPieSeries(l, s) {
+
     const model = {
         type: 'pie',
-        radius: '30%',
+        radius: `${l.w * .7}%`,
+        center: [`${l.w >= 35 ? (l.w >= 60 ? 50 : (l.w / 100 * 50 / 35 + 35)) : 50}%`,`${l.w >= 35 ? 50 : 60}%`],
         label: {
             normal: {
-                formatter: '{b} \n {d}%'
+                textStyle: {
+                    fontSize: getFontSize(l) * 0.7
+                },
+                formatter: '{b}:  {c} \n {d}%'
             }
         }
     }
@@ -269,10 +336,29 @@ function getPieSeries(s) {
     return [series];
 }
 
+function getPieLegend(l, s) {
+
+    let legend = {
+        show: !(l.w < 35 && s.length > 7),
+        right: (s.length <= 7 && l.w < 35) ? 'auto' : `${l.w/30}%`,
+        orient: (s.length <= 7 && l.w < 35) ? 'horizontal' : 'vertical',
+        padding: 0,
+        itemGap: l.w / 10,
+        top: '20%',
+        textStyle: {
+            fontSize: getFontSize(l) * 0.7
+        },
+        data: s.map((v, i) => {
+            return v.name
+        })
+    }
+    return legend;
+}
+
 function getLayout(l) {
     let layout = {};
     for(let k in l) {
-        layout[k] = Number(Math.round(eval(l[k].replace('%','/10'))));
+        layout[k] = l[k]/10
     }
     return layout;
 }
@@ -301,4 +387,25 @@ function parseStr(str) {
     return obj
 }
 
+function getScreenSize() {
+    let root = document.getElementById('root');
+    let screenHeight = root.offsetHeight;
+    let screenWidth = root.offsetWidth;
+    return {screenHeight, screenWidth};
+}
+
+function getFontSize(layout) {
+    const MAX_FONT_SIZE = 25;
+    let {screenHeight, screenWidth} = getScreenSize();
+    if(screenWidth > 800) {
+        return 22;
+    }else if(screenWidth > 500) {
+        return 20;
+    }else if(screenWidth > 300) {
+        return 18;
+    }else {
+        return 16
+    }
+}
+
 export {converter};

+ 84 - 24
kanban-client/app/component/factory.js

@@ -5,9 +5,10 @@ import FixedBox from '../../src/FixedBox/FixedBox.jsx';
 import DateFormatter from '../utils/DateTimeUtils.js';
 import {converter} from '../component/converter.js';
 import RenderUtils from '../utils/RenderUtils.js';
-import config from '../data/cc.json';
 import URL from '../constants/url.json';
 
+import tempdata from '../data/testbar.json';
+
 class Factory extends React.Component {
 
     constructor(props) {
@@ -20,64 +21,123 @@ class Factory extends React.Component {
     getModelConfig(mid) {
         let me = this;
         fetch(URL.path+mid, {
-            method: 'GET'
+            method: 'POST',
+            credentials: 'include'
         }).then(function (response) {
             return (response.json())
         }).then((json) => {
-            return json;
-        }).then(function (json) {
-            let s = ''+json.replace(/\s+/g,"");
-            let t = JSON.parse(s);
+            let instance = json.instance;
+            if(!me.state.instance) {
+                me.setState({
+                    instance: instance
+                },me.setRefresh);
+            }
+            return json.data[0];
+        }).then(function (modelconfig) {
             me.setState({
-                model: converter(t)
+                model: converter(modelconfig),
             });
         }).catch(function (ex) {
             console.log('parsing failed', ex);
         });
     }
 
-    componentDidUpdate() {
+    setTitleHeight() {
         let titleEl = document.getElementsByClassName('rc-title');
-        let titleHeight = titleEl[0].offsetHeight;
-        this.titleHeight = titleHeight;
+        let titleHeight = titleEl.length > 0 ? titleEl[0].offsetHeight : 50;
+        this.titleHeight = titleHeight || 0;
+    }
+
+    setRefresh() {
+        let { instance } = this.state;
+        if(!instance) {return;}
+        let codes = instance.templateCodes;
+        let display = instance.display;
+        let next = {
+            enable: instance.switchFrequency > 0 ? true : false,
+            interval: instance.switchFrequency
+        };
+        let current = {
+            enable: instance.refreshFrequency > 0 ? true : false,
+            interval: instance.refreshFrequency
+        };
+        let refresh = {
+            current: current,
+            next: next
+        };
+        // 刷新
+        if (refresh.current) {
+            if(refresh.current.enable) {
+                this.refreshThis = setInterval(function () {
+                    this.getModelConfig(this.props.code[0] + '?templateCode=' + codes[this.index]);
+                }.bind(this), Number(refresh.current.interval) || 10000)
+            }
+        }
+        // 切换
+        if (refresh.next) {
+            if(refresh.next.enable) {
+                this.refreshNext = setInterval(function () {
+                    if(this.index == codes.length-1) {
+                        this.index = 0;
+                    }else {
+                        this.index ++;
+                    }
+                }.bind(this), Number(refresh.next.interval) || 30000)
+            }
+        }
+    }
+
+    componentDidUpdate() {
+        this.setTitleHeight();
     }
 
     componentWillMount() {
         let {code} = this.props;
         this.getModelConfig(code[0]);
+        // this.setState({
+        //         model: converter(tempdata.data[0]),
+        //     });
     }
 
     componentDidMount() {
-        let {code, refresh} = this.props;
-        if (refresh.interval) {
-            setInterval(function () {
-                if(this.index == code.length) {
-                    this.index = 0;
-                }
-                this.getModelConfig(code[this.index++]);
-
-            }.bind(this), refresh.interval || 3000)
+    }
+    componentWillUnmount() {
+        if(this.refreshThis) {
+            if(this.refreshThis.interval > 0) {
+                window.clearInterval(this.refreshThis);
+            }
+        }
+        if(this.refreshNext) {
+            if(this.refreshNext.interval > 0) {
+                window.clearInterval(this.refreshNext);
+            }
         }
     }
 
+    componentWillReceiveProps(nextProps) {
+        console.log('1');
+        this.setTitleHeight();
+    }
+
     render() {
         if(!this.state.model){
             return <div></div>
         }
         let {model} = this.state;
-        let titleHeight = this.titleHeight || 60;
+        let titleHeight = this.titleHeight || 0;
+        console.log(titleHeight);
         const {title, content, fixedbox} = model;
-        let titleConfig = title.config || {height: 0};
+        let titleConfig = title ? (title.config || {height: 0}) : {height: 0};
         let items = [];
         if(fixedbox) {
            items  = fixedbox.items || [];
         }
         return (
             <div>
-                <Title {...this.state.model.title.config} />
-                <Container items={content.items} rowHeight={(window.innerHeight - titleHeight || 55) / 10} />
+                <Title static={this.props.static} {...titleConfig} />
+                <Container static={this.props.static} items={content.items} rowHeight={(window.innerHeight - titleHeight) / 10} />
                 {items.map((item, index) => {
-                    return <FixedBox key={`${index}`} titleHeight={titleHeight} {...item}/>;
+                    return <FixedBox key={`${index}`} static={this.props.static} titleHeight={titleHeight} {...item}/>;
                 })}
             </div>
         );

+ 56 - 0
kanban-client/app/data/testbar.json

@@ -0,0 +1,56 @@
+{
+    "data": [
+        {
+            "content": {
+                "items": {
+                    "layout": {
+                        "w": 40,
+                        "h": 100,
+                        "y": 0,
+                        "x": 0
+                    },
+                    "config": {
+                        "series": [
+                            {
+                                "name": "已启动",
+                                "data": [
+                                    0,
+                                    2,
+                                    1,
+                                    0,
+                                    0
+                                ]
+                            },
+                            {
+                                "name": "未启动",
+                                "data": [
+                                    1,
+                                    0,
+                                    0,
+                                    1,
+                                    1
+                                ]
+                            }
+                        ],
+                        "title": "按负责人统计项目启动个数(柱状图)",
+                        "ytype": "numeric",
+                        "xtitle": "项目负责人",
+                        "xtype": "category",
+                        "subtitle": "柱状图",
+                        "ytitle": "项目数量统计",
+                        "xfields": "[顾群2, 顾群, 杨若楠, 陈金金, 陈虎, USER1, USER2, USER3, USER4, uS]"
+                    },
+                    "type": "bar"
+                }
+            }
+        }
+    ],
+    "instance": {
+        "templateCodes": [
+            "51FBA074117"
+        ],
+        "switchFrequency": 5000,
+        "display": "AutoSwitch",
+        "refreshFrequency": 5000
+    }
+}

+ 75 - 0
kanban-client/app/data/testform.json

@@ -0,0 +1,75 @@
+{
+    "data": [
+        {
+            "content": {
+                "items": [
+                    {
+                        "layout": {
+                            "w": 50,
+                            "h": 30,
+                            "y": 0,
+                            "x": 0
+                        },
+                        "config": {
+                            "data": [
+                                {
+                                    "field": "字段1",
+                                    "width": 100,
+                                    "value": 11
+                                },
+                                {
+                                    "field": "字段2",
+                                    "value": 3
+                                },
+                                {
+                                    "field": "字段3",
+                                    "value": 5
+                                },
+                                {
+                                    "field": "字段4",
+                                    "width": 100,
+                                    "value": 3
+                                },
+                                {
+                                    "field": "字段5",
+                                    "width": 100,
+                                    "value": 5
+                                },{
+                                    "field": "字段6",
+                                    "value": 3
+                                },
+                                {
+                                    "field": "字段7",
+                                    "value": 511
+                                },
+                                {
+                                    "field": "字段8",
+                                    "width": 100,
+                                    "value": 11
+                                },
+                                {
+                                    "field": "字段9",
+                                    "value": 3
+                                },
+                                {
+                                    "field": "字段10",
+                                    "value": 5
+                                }
+                            ],
+                            "columns": 4
+                        },
+                        "type": "form"
+                    }
+                ]
+            }
+        }
+    ],
+    "instance": {
+        "templateCodes": [
+            "506CFE6D911"
+        ],
+        "switchFrequency": 5000,
+        "display": "AutoSwitch",
+        "refreshFrequency": 5000
+    }
+}

+ 70 - 0
kanban-client/app/data/testline.json

@@ -0,0 +1,70 @@
+{
+    "data": [
+        {
+            "content": {
+                "items": [
+                    {
+                        "layout": {
+                            "w": 100,
+                            "h": 100,
+                            "y": 0,
+                            "x": 0
+                        },
+                        "config": {
+                            "series": [
+                                {
+                                    "name": "已启动",
+                                    "data": [
+                                        0,
+                                        2,
+                                        1,
+                                        0,
+                                        0,
+                                        0,
+                                        0,
+                                        0,
+                                        0,
+                                        0,
+                                        0
+                                    ]
+                                },
+                                {
+                                    "name": "未启动",
+                                    "data": [
+                                        1,
+                                        0,
+                                        0,
+                                        1,
+                                        2,
+                                        1,
+                                        2,
+                                        1,
+                                        0,
+                                        2,
+                                        1
+                                    ]
+                                }
+                            ],
+                            "title": "按负责人统计项目启动个数(拆线图)",
+                            "ytype": "numeric",
+                            "xtitle": "项目负责人",
+                            "xtype": "category",
+                            "subtitle": "拆线图",
+                            "ytitle": "项目数量统计",
+                            "xfields": "[TEST0821, null, 周兵, 刘萌冰, 詹国胜, 顾群2, 顾群, 杨若楠, 陈金金, 陈虎, USER0005]"
+                        },
+                        "type": "line"
+                    }
+                ]
+            }
+        }
+    ],
+    "instance": {
+        "templateCodes": [
+            "506CFE6D911"
+        ],
+        "switchFrequency": 5000,
+        "display": "AutoSwitch",
+        "refreshFrequency": 5000
+    }
+}

+ 53 - 0
kanban-client/app/data/testpie.json

@@ -0,0 +1,53 @@
+{
+    "data": [
+        {
+            "content": {
+                "items": [
+                    {
+                        "layout": {
+                            "w": 30,
+                            "h": 50,
+                            "y": 0,
+                            "x": 0
+                        },
+                        "config": {
+                            "series": [
+                                {
+                                    "name": "刘萌冰",
+                                    "data": 0.0588
+                                },
+                                {
+                                    "name": "杨若楠",
+                                    "data": 0.0588
+                                },
+                                {
+                                    "name": "陈金金",
+                                    "data": 0.0588
+                                },
+                                {
+                                    "name": "陈虎",
+                                    "data": 0.1176
+                                },
+                                {
+                                    "name": "USER0005",
+                                    "data": 0.0588
+                                }
+                            ],
+                            "title": "统计项目负责人占用(饼图)",
+                            "subtitle": "饼图"
+                        },
+                        "type": "pie"
+                    }
+                ]
+            }
+        }
+    ],
+    "instance": {
+        "templateCodes": [
+            "506CFE6D911"
+        ],
+        "switchFrequency": 5000,
+        "display": "AutoSwitch",
+        "refreshFrequency": 5000
+    }
+}

+ 142 - 0
kanban-client/app/data/testtable.json

@@ -0,0 +1,142 @@
+{
+    "data": [
+        {
+            "content": {
+                "items": [
+                    {
+                        "layout": {
+                            "w": 100,
+                            "h": 100,
+                            "y": 0,
+                            "x": 0
+                        },
+                        "config": {
+                            "title": "测试Grid",
+                            "data": [
+                                {
+                                    "prj_end": 1506700800000,
+                                    "prj_start": 1504627200000,
+                                    "prj_assignto": "詹国胜",
+                                    "prj_name": "平板电脑充电器(通用型)"
+                                },
+                                {
+                                    "prj_end": 1506700800000,
+                                    "prj_start": 1504540800000,
+                                    "prj_assignto": "USER0005",
+                                    "prj_name": "测试项目文档权限"
+                                },
+                                {
+                                    "prj_end": 1504108800000,
+                                    "prj_start": 1501776000000,
+                                    "prj_assignto": "詹国胜",
+                                    "prj_name": "无线手机充电器"
+                                },
+                                {
+                                    "prj_end": 1504108800000,
+                                    "prj_start": 1503849600000,
+                                    "prj_assignto": "TEST0821",
+                                    "prj_name": "测试AAA"
+                                },
+                                {
+                                    "prj_end": 1504108800000,
+                                    "prj_start": 1502726400000,
+                                    "prj_assignto": "顾群2",
+                                    "prj_name": "test"
+                                },
+                                {
+                                    "prj_end": 1504886400000,
+                                    "prj_start": 1503936000000,
+                                    "prj_assignto": "顾群",
+                                    "prj_name": "测试测试"
+                                },
+                                {
+                                    "prj_end": 1504108800000,
+                                    "prj_start": 1501776000000,
+                                    "prj_assignto": "詹国胜",
+                                    "prj_name": "法拉利F1赛车车载导航"
+                                },
+                                {
+                                    "prj_end": 1504108800000,
+                                    "prj_start": 1501948800000,
+                                    "prj_assignto": "杨若楠",
+                                    "prj_name": "平板电脑充电器"
+                                },
+                                {
+                                    "prj_end": 1506700800000,
+                                    "prj_start": 1504540800000,
+                                    "prj_assignto": "顾群",
+                                    "prj_name": "测试开启项目"
+                                },
+                                {
+                                    "prj_end": 1505577600000,
+                                    "prj_start": 1504540800000,
+                                    "prj_assignto": "刘萌冰",
+                                    "prj_name": "测试任务的推送"
+                                },
+                                {
+                                    "prj_end": 1506700800000,
+                                    "prj_start": 1504540800000,
+                                    "prj_assignto": "陈虎",
+                                    "prj_name": "测试万利达科技0905"
+                                },
+                                {
+                                    "prj_end": 1506700800000,
+                                    "prj_start": 1504540800000,
+                                    "prj_assignto": "陈虎",
+                                    "prj_name": "测试万利达科技0904"
+                                },
+                                {
+                                    "prj_end": 1506441600000,
+                                    "prj_start": 1504627200000,
+                                    "prj_assignto": "周兵",
+                                    "prj_name": "UU互联测试项目001"
+                                }
+                            ],
+                            "columns": [
+                                {
+                                    "title": "项目负责人",
+                                    "sort": 0,
+                                    "width": 100,
+                                    "dataIndex": "prj_assignto"
+                                },
+                                {
+                                    "title": "计划开始日期",
+                                    "sort": 0,
+                                    "width": 100,
+                                    "dataIndex": "prj_start"
+                                },
+                                {
+                                    "title": "计划结束日期",
+                                    "sort": 0,
+                                    "width": 100,
+                                    "dataIndex": "prj_end"
+                                },
+                                {
+                                    "title": "项目名称",
+                                    "sort": 0,
+                                    "width": 100,
+                                    "dataIndex": "prj_name"
+                                },
+                                {
+                                    "title": "状态",
+                                    "sort": 0,
+                                    "width": 100,
+                                    "render": "prj_istimeout_render"
+                                }
+                            ]
+                        },
+                        "type": "table"
+                    }
+                ]
+            }
+        }
+    ],
+    "instance": {
+        "templateCodes": [
+            "506CFE6D911"
+        ],
+        "switchFrequency": 5000,
+        "display": "AutoSwitch",
+        "refreshFrequency": 5000
+    }
+}

+ 3 - 1
kanban-client/app/main.js

@@ -2,8 +2,10 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import Factory from './component/Factory.js';
 
+var code = window.location.search.substring(6) || '51F6D4F5C16';
+
 ReactDOM.render(
-    <Factory code={['4F75DDBB413','4F75DDBB413']} refresh={{interval: 2000}}/>,
+    <Factory code={[code]} />,
 ( document.getElementById('root')));
 
   

+ 19 - 1
kanban-client/app/utils/RenderUtils.js

@@ -37,7 +37,6 @@ function titleRender2(state) {
                 <td className="logo" width="320" rowSpan="2">
                     <img src={logeImg} alt=""/>
                 </td>
-                <td className="title-center" width="550" rowSpan="2">产线执行综合看板</td>
                 <td className="title-field" width="210">
                    线次
                 </td>
@@ -67,6 +66,16 @@ function descRender(v, r ,i) {
         }
     }
 }
+function prj_istimeout_render(r, v, i) {
+    let value = v.prj_end - new Date().getTime() < 0 ? '超时' : '正常';
+    let color = v.prj_end - new Date().getTime() < 0 ? 'red' : 'green';
+    return {
+        children: value,
+        props: {
+            style: {color: color}
+        }
+    }
+}
 function perRender(value, index) {
     return `${(value * 100)}%`
 }
@@ -127,6 +136,13 @@ function rateRender(value, record, index) {
     }
 }
 
+function dateRender(value, record, index) {
+    return {
+        children: new Date(value).format('yyyy-MM-dd'),
+        props: {}
+    }
+}
+
 let renders = {
     titleRender,
     titleRender2,
@@ -137,6 +153,8 @@ let renders = {
     xaixsFetch,
     tableRender,
     rateRender,
+    dateRender,
+    prj_istimeout_render,
 }
 
 module.exports = renders;

+ 3 - 0
kanban-client/assets/Form/index.css

@@ -35,6 +35,9 @@ body,
   text-align: center;
   transform: none !important;
 }
+.rc-form-header-content {
+  padding: 5px;
+}
 .rc-form-item-field {
   width: 50%;
   float: left;

+ 3 - 0
kanban-client/assets/Form/index.less

@@ -36,6 +36,9 @@ html,body,#root{
     text-align: center;
     transform: none !important;
 }
+.@{prefixCls}-header-content {
+    padding: 5px;
+}
 .@{prefixCls}-item-field {
     width: 50%;
     float: left;

+ 50 - 0
kanban-client/assets/Table/animation.css

@@ -0,0 +1,50 @@
+.fade-enter {
+  animation-duration: .5s;
+  animation-fill-mode: both;
+  opacity: .7;
+  animation-timing-function: linear;
+  animation-play-state: paused;
+}
+.fade-appear {
+  animation-duration: .5s;
+  animation-fill-mode: both;
+  opacity: .7;
+  animation-timing-function: linear;
+  animation-play-state: paused;
+}
+.fade-leave {
+  animation-duration: .5s;
+  animation-fill-mode: both;
+  transform: translateX(0px);
+  opacity: .5;
+  animation-timing-function: ease-out;
+  animation-play-state: paused;
+}
+.fade-enter.fade-enter-active {
+  animation-name: fadeIn;
+  animation-play-state: running;
+}
+.fade-appear.fade-appear-active {
+  animation-name: fadeIn;
+  animation-play-state: running;
+}
+.fade-leave.fade-leave-active {
+  animation-name: fadeOut;
+  animation-play-state: running;
+}
+@keyframes fadeIn {
+  0% {
+    opacity: .7;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+@keyframes fadeOut {
+  0% {
+    opacity: .5;
+  }
+  100% {
+    opacity: 0;
+  }
+}

+ 58 - 0
kanban-client/assets/Table/animation.less

@@ -0,0 +1,58 @@
+@import './motion.less';
+
+.fade-enter {
+  .motion-common();
+  opacity: .7;
+  animation-timing-function: linear;
+  animation-play-state: paused;
+}
+
+.fade-appear {
+  .motion-common();
+  opacity: .7;
+  animation-timing-function: linear;
+  animation-play-state: paused;
+}
+
+.fade-leave {
+  .motion-common();
+  transform: translateX(0px);
+  opacity: .5;
+  animation-timing-function: ease-out;
+  animation-play-state: paused;
+}
+
+.fade-enter.fade-enter-active {
+  animation-name: fadeIn;
+  animation-play-state: running;
+}
+
+.fade-appear.fade-appear-active {
+  animation-name: fadeIn;
+  animation-play-state: running;
+}
+
+.fade-leave.fade-leave-active {
+  animation-name: fadeOut;
+  animation-play-state: running;
+}
+
+@keyframes fadeIn {
+  0% {
+    opacity: .7;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+
+@keyframes fadeOut {
+  0% {
+    opacity: .5;
+    // transform: translateX(0px);
+  }
+  100% {
+    opacity: 0;
+    // transform: translateX(300px);
+  }
+}

+ 7 - 2
kanban-client/assets/Table/index.css

@@ -3,7 +3,6 @@
   color: white;
 }
 .rc-table-main {
-  height: 100%;
   width: 100%;
   border-collapse: collapse;
   text-align: center;
@@ -19,6 +18,7 @@
   text-align: center;
   font-weight: bold;
   font-size: large;
+  padding: 5px;
 }
 .rc-table-content {
   height: 100%;
@@ -28,9 +28,14 @@
   height: 100%;
 }
 .rc-table table {
+  table-layout: fixed;
   border-collapse: collapse;
 }
 .rc-table th,
 .rc-table td {
-  border: 1px solid white;
+  text-align: left;
+  padding-left: 5px;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
 }

+ 9 - 2
kanban-client/assets/Table/index.less

@@ -6,7 +6,7 @@
 }
 
 .@{prefixCls}-main {
-    height: 100%;
+    // height: 100%;
     width: 100%;
     border-collapse: collapse;
     text-align: center;
@@ -25,6 +25,7 @@
     text-align: center;
     font-weight: bold;
     font-size: large;
+    padding: 5px;
 }
 .@{prefixCls}-content {
     height: 100%;
@@ -35,9 +36,15 @@
 }
 .@{prefixCls} {
     table {
+        table-layout: fixed;
         border-collapse: collapse;
     }
     th, td {
-        border: 1px solid white;
+        text-align: left;
+        padding-left: 5px;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        // border: 1px solid white;
     }
   }

+ 0 - 0
kanban-client/assets/Table/motion.css


+ 31 - 0
kanban-client/assets/Table/motion.less

@@ -0,0 +1,31 @@
+.motion-common() {
+  animation-duration: .5s;
+  animation-fill-mode: both;
+}
+
+.make-motion(@className, @keyframeName) {
+  .@{className}-enter {
+    .motion-common();
+    animation-play-state: paused;
+  }
+  .@{className}-appear {
+    .motion-common();
+    animation-play-state: paused;
+  }
+  .@{className}-leave {
+    .motion-common();
+    animation-play-state: paused;
+  }
+  .@{className}-enter.@{className}-enter-active {
+    animation-name: ~"@{keyframeName}In";
+    animation-play-state: running;
+  }
+  .@{className}-leave.@{className}-leave-active {
+    animation-name: ~"@{keyframeName}Out";
+    animation-play-state: running;
+  }
+  .@{className}-appear.@{className}-appear-active {
+    animation-name: ~"@{keyframeName}In";
+    animation-play-state: running;
+  }
+}

+ 0 - 5
kanban-client/src/Charts/ECharts.js

@@ -72,11 +72,6 @@ export class ReactEchart extends React.Component {
 	}
 
     componentDidMount() {
-        // if (this.timeTicket) {
-        //     clearInterval(this.timeTicket);
-        // }
-        // this.timeTicket = setInterval(this.fetchData, 1000);
-
     }
     componentDidUpdate() {
         

+ 150 - 136
kanban-client/src/Form/Form.jsx

@@ -5,147 +5,161 @@ import '../../assets/Form/index.less';
 import renders from '../../app/utils/RenderUtils.js';
 
 export default class Form extends React.Component {
-  static propTypes = {
-    state: PropTypes.object,
-    fieldCls: PropTypes.string,
-    fieldStyle: PropTypes.object,
-    valueCls: PropTypes.string,
-    valueStyle: PropTypes.object,
-    data: PropTypes.array,
-    prefixCls: PropTypes.string,
-    cls: PropTypes.string,
-    style: PropTypes.object,
-    header: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-    columns:  PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-  }
-
-  static defaultProps = {
-    state: {},
-    fieldCls: '',
-    fieldStyle: {},
-    valueCls: '',
-    valueStyle: {},
-    data: [],
-    prefixCls: 'rc-form',
-    cls: '',
-    style: {},
-    header: '',
-    columns: 1
-  }
-
-  constructor(props) {
-    super(props);
-    this.state = props;
-    let oriState = props.state;
-    for (let key in oriState) {
-        this.state[key] = oriState[key];
+    static propTypes = {
+        fontSize: PropTypes.number,
+        state: PropTypes.object,
+        fieldCls: PropTypes.string,
+        fieldStyle: PropTypes.object,
+        valueCls: PropTypes.string,
+        valueStyle: PropTypes.object,
+        data: PropTypes.array,
+        prefixCls: PropTypes.string,
+        cls: PropTypes.string,
+        style: PropTypes.object,
+        header: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+        columns: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
     }
-  }
 
-  getHeader() {
-    const { header, prefixCls, columns } = this.state;
-
-    let hel;
-    if(renders[header]) {
-        hel = renders[header](this.state);
-    }else {
-        hel = header;
+    static defaultProps = {
+        fontSize: 16,
+        state: {},
+        fieldCls: '',
+        fieldStyle: {},
+        valueCls: '',
+        valueStyle: {},
+        data: [],
+        prefixCls: 'rc-form',
+        cls: '',
+        style: {},
+        header: '',
+        columns: 1
     }
-    if (hel) {
-        const headerEl = <thead className={`${prefixCls}-header`} key="form_header">
-            <tr>
-                <td className={`${prefixCls}-header-content`} colSpan={columns}>{hel}</td>
-            </tr>
-        </thead>;
-        return headerEl;
+
+    constructor(props) {
+        super(props);
+        this.state = props;
+        let oriState = props.state;
+        for (let key in oriState) {
+            this.state[key] = oriState[key];
+        }
     }
-  }
-
-  getContent() {
-      const { prefixCls, fieldCls, fieldStyle, valueCls, valueStyle} = this.state;
-      const { columns } = this.state;
-      let columnsData = this.getItemColumns();
-      const contentEl = <tbody className={`${prefixCls}-content`} key="form_content">
-          {
-              columnsData.map((tr, i) => (
-                <tr className={`${prefixCls}-tr`} key={`content_item_tr_${i}`}>
-                    {tr.map((td, i) => (
-                        <FormItem 
-                            prefixCls={prefixCls}
-                            fieldCls={fieldCls}
-                            fieldStyle={fieldStyle}
-                            valueCls={valueCls}
-                            valueStyle={valueStyle}
-                            width={`${100/columns}%`}
-                            key={`content_item_td_${i}`}
-                            {...td}
-                        />
-                    ))}
-                    
+
+    getHeader() {
+        const { fontSize, header, prefixCls, columns } = this.state;
+
+        let hel;
+        if (renders[header]) {
+            hel = renders[header](this.state);
+        } else {
+            hel = header;
+        }
+        if (hel) {
+            const headerEl = <thead style={{fontSize: fontSize}} className={`${prefixCls}-header`} key="form_header">
+                <tr>
+                    <td className={`${prefixCls}-header-content`} colSpan={'100%'}>{hel}</td>
                 </tr>
-              ))
-          }
-      </tbody>;
-      return contentEl;
-  }
-
-  getFooter() {
-      const { footer, prefixCls } = this.state;
-      let fel = typeof (footer) == 'function' ? footer(this.state) : footer;
-      if (fel) {
-          const footerEl = <div className={`${prefixCls}-footer`} key="form_footer">
-              {fel}
-          </div>;
-          return footerEl;
-      }
-  }
-
-  getItemColumns() {
-      const { data, columns } = this.state;
-      let columnsData = [];
-      for (let i = 0; i < data.length;) {
-          let arr = [];
-          for (let j = 0; j < (columns < 0 ? 1 : columns) && i < data.length; j++) {
-              arr.push(data[i]);
-              i++;
-          }
-          columnsData.push(arr);
-      }
-      return columnsData;
-  }
-
-  componentDidMount() {
-  }
-
-  componentWillReceiveProps(nextProps) {
-      this.setState(nextProps);
-  }
-
-  componentDidUpdate(prevProps) {
-  }
-
-  componentWillUnmount() {
-  }
-
-  render() {
-    const props = this.state;
-    const prefixCls = props.prefixCls;
-
-    let className = props.prefixCls;
-    if(props.cls) {
-        className += ` ${props.cls}`;
+            </thead>;
+            return headerEl;
+        }
+    }
+
+    getContent() {
+        const { fontSize, prefixCls, fieldCls, fieldStyle, valueCls, valueStyle } = this.state;
+        const { columns } = this.state;
+        let columnsData = this.getItemColumns();
+        const contentEl = <tbody className={`${prefixCls}-content`} key="form_content">
+            {
+                columnsData.map((tr, i) => (
+                    <tr className={`${prefixCls}-tr`} key={`content_item_tr_${i}`}>
+                        {tr.map((td, i) => {
+                            return (
+                                <FormItem
+                                    prefixCls={prefixCls}
+                                    fieldCls={fieldCls}
+                                    fieldStyle={fieldStyle}
+                                    valueCls={valueCls}
+                                    valueStyle={valueStyle}
+                                    colSpan={td.colSpan}
+                                    key={`content_item_td_${i}`}
+                                    {...td}
+                                />
+                            )
+                        }
+                        )}
+
+                    </tr>
+                ))
+            }
+        </tbody>;
+        return contentEl;
+    }
+
+    getFooter() {
+        const { footer, prefixCls } = this.state;
+        let fel = typeof (footer) == 'function' ? footer(this.state) : footer;
+        if (fel) {
+            const footerEl = <div className={`${prefixCls}-footer`} key="form_footer">
+                {fel}
+            </div>;
+            return footerEl;
+        }
+    }
+
+    getItemColumns() {
+        const { data, columns } = this.state;
+        let columnsData = [];
+        outer:
+        for (let i = 0; i < data.length;) {
+            let arr = [];
+            inner:
+            for (let j = 0; j < (columns < 0 ? 1 : columns) && i < data.length; j++) {
+                let w = data[i].width || 0;
+                arr.push(data[i]);
+                if (w >= 100) {
+                    break inner;
+                }
+            }
+            i++;
+            arr.map((a, i) => {
+                a.colSpan = `${100 / arr.length}%`
+            });
+            columnsData.push(arr);
+        }
+        return columnsData;
     }
 
-    const header = this.getHeader();
-    const content = this.getContent();
-
-    return (
-        <div className={className} style={props.style} key='form'>
-            <table className={`${prefixCls}-container`} style={props.style} key='form'>
-                {header}
-                {content}
-            </table>
-        </div>
-    );
-  }
+    componentDidMount() {
+    }
+
+    componentWillReceiveProps(nextProps) {
+        this.setState(nextProps);
+    }
+
+    componentDidUpdate(prevProps) {
+    }
+
+    componentWillUnmount() {
+    }
+
+    render() {
+        const props = this.state;
+        const prefixCls = props.prefixCls;
+
+        let className = props.prefixCls;
+        if (props.cls) {
+            className += ` ${props.cls}`;
+        }
+
+        const header = this.getHeader();
+        const content = this.getContent();
+
+        return (
+            <div className={className} style={props.style} key='form'>
+                <table className={`${prefixCls}-container`} style={props.style} key='form'>
+                    {header}
+                    {content}
+                </table>
+            </div>
+        );
+    }
 }

+ 4 - 5
kanban-client/src/Form/FormItem.jsx

@@ -8,7 +8,7 @@ export default class FormItem extends React.Component {
       super(props);
 
       this.state = {
-          width: props.width,
+          colSpan: props.colSpan,
           field: typeof props.field === 'object' ? props.field.text : props.field,
           fieldCls: (typeof props.field === 'object' ? (props.field.cls ? props.field.cls : props.fieldCls) : props.fieldCls),
           fieldStyle: (typeof props.field === 'object' ? (props.field.style ? props.field.style : props.fieldStyle) : props.fieldStyle),
@@ -21,7 +21,7 @@ export default class FormItem extends React.Component {
   componentWillReceiveProps(nextProps) {
       this.newProps = nextProps;
       this.setState({
-          width: nextProps.width,
+          colSpan: nextProps.colSpan,
           field: typeof nextProps.field === 'object' ? nextProps.field.text : nextProps.field,
           fieldCls: (typeof nextProps.field === 'object' ? (nextProps.field.cls ? nextProps.field.cls : nextProps.fieldCls) : nextProps.fieldCls),
           fieldStyle: (typeof nextProps.field === 'object' ? (nextProps.field.style ? nextProps.field.style : nextProps.fieldStyle) : nextProps.fieldStyle),
@@ -33,12 +33,11 @@ export default class FormItem extends React.Component {
   }
 
   render() {
-    const {width, field, fieldCls, fieldStyle, value, valueCls, valueStyle} = this.state;
+    const {colSpan, field, fieldCls, fieldStyle, value, valueCls, valueStyle} = this.state;
     
     const prefixCls = this.props.prefixCls;
-
     return (
-        <td className={`${prefixCls}-item`} width={width}>
+        <td className={`${prefixCls}-item`} colSpan={colSpan}>
             <div className={`${prefixCls}-item-field ${fieldCls}`} style={fieldStyle} >{field}</div>
             <div className={`${prefixCls}-item-value ${valueCls}`} style={valueStyle} >{value}</div>
         </td>

+ 2 - 1
kanban-client/src/Title/Title.jsx

@@ -5,6 +5,7 @@ import '../../assets/Title/index.less';
 
 export default class Title extends React.Component {
   static propTypes = {
+    static: PropTypes.object,
     state: PropTypes.object,
     prefixCls: PropTypes.string,
     cls: PropTypes.string,
@@ -42,7 +43,7 @@ export default class Title extends React.Component {
     // style.height = height;
     let tel;
     if (renders[render]) {
-      tel = <div className={cls} style={style}>{renders[render](this.props.state)}</div>;
+      tel = <div className={cls} style={style}>{renders[render](this.props.static)}</div>;
     }else {
       tel = <div className={cls} style={style}>{render}</div>;
     }