* Move the screen module to open source.

This commit is contained in:
liugang
2022-12-23 14:39:01 +08:00
parent c6630243cb
commit d04fc1fe4a
23 changed files with 1428 additions and 26 deletions
+133 -17
View File
File diff suppressed because one or more lines are too long
+139 -2
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -40,6 +40,7 @@ $lang->company = new stdclass();
$lang->dept = new stdclass();
$lang->group = new stdclass();
$lang->user = new stdclass();
$lang->screen = new stdclass();
$lang->report = new stdclass();
$lang->repo = new stdclass();
$lang->jenkins = new stdclass();
+2 -1
View File
@@ -168,7 +168,8 @@ $lang->devops->common = 'DevOps';
$lang->doc->common = 'Doc';
$lang->repo->common = 'Code';
$lang->repo->codeRepo = 'Code Repo';
$lang->report->common = 'Statistics';
$lang->screen->common = 'Screen';
$lang->report->common = 'BI';
$lang->system->common = 'System';
$lang->admin->common = 'Admin';
$lang->story->common = 'Story';
+2 -1
View File
@@ -168,7 +168,8 @@ $lang->devops->common = 'DevOps';
$lang->doc->common = 'Doc';
$lang->repo->common = 'Code';
$lang->repo->codeRepo = 'Code Repo';
$lang->report->common = 'Statistics';
$lang->screen->common = 'Screen';
$lang->report->common = 'BI';
$lang->system->common = 'System';
$lang->admin->common = 'Admin';
$lang->story->common = 'Story';
+2 -1
View File
@@ -168,7 +168,8 @@ $lang->devops->common = 'DevOps';
$lang->doc->common = 'Doc';
$lang->repo->common = 'Code';
$lang->repo->codeRepo = 'Code Repo';
$lang->report->common = 'Statistics';
$lang->screen->common = 'Screen';
$lang->report->common = 'BI';
$lang->system->common = 'System';
$lang->admin->common = 'Admin';
$lang->story->common = 'Story';
+3 -3
View File
@@ -42,7 +42,7 @@ $lang->mainNav->qa = "{$lang->navIcons['qa']} {$lang->qa->common}|qa|inde
$lang->mainNav->devops = "{$lang->navIcons['devops']} DevOps|repo|browse|";
$lang->mainNav->kanban = "{$lang->navIcons['kanban']} {$lang->kanban->common}|kanban|space|";
$lang->mainNav->doc = "{$lang->navIcons['doc']} {$lang->doc->common}|doc|index|";
$lang->mainNav->report = "{$lang->navIcons['report']} {$lang->report->common}|report|preview|";
$lang->mainNav->report = "{$lang->navIcons['report']} {$lang->report->common}|screen|browse|";
$lang->mainNav->system = "{$lang->navIcons['system']} {$lang->system->common}|my|team|";
$lang->mainNav->admin = "{$lang->navIcons['admin']} {$lang->admin->common}|admin|index|";
@@ -536,11 +536,11 @@ $lang->doc->menu->api['subMenu']->struct = array('link' => "{$lang->doc->apiStru
/* Report menu.*/
$lang->report->menu = new stdclass();
$lang->report->menu->annual = array('link' => "{$lang->report->annual}|report|annualData|year=&dept=&userID=" . (isset($_SESSION['user']) ? zget($_SESSION['user'], 'id', 0) : 0), 'target' => '_blank');
$lang->report->menu->screen = array('link' => "{$lang->screen->common}|screen|browse");
$lang->report->menu->pivotTable = array('link' => "{$lang->report->pivotTable}|report|preview");
/* Report menu order. */
$lang->report->menuOrder[5] = 'annual';
$lang->report->menuOrder[5] = 'screen';
$lang->report->menuOrder[10] = 'pivotTable';
$lang->report->menu->pivotTable['subMenu'] = new stdclass();
+2 -1
View File
@@ -168,7 +168,8 @@ $lang->devops->common = 'DevOps';
$lang->doc->common = '文档';
$lang->repo->common = '代码';
$lang->repo->codeRepo = '代码库';
$lang->report->common = '统计';
$lang->screen->common = '可视化大屏';
$lang->report->common = 'BI';
$lang->system->common = '组织';
$lang->admin->common = '后台';
$lang->story->common = $lang->SRCommon;
+60
View File
@@ -0,0 +1,60 @@
<?php
/**
* The control file of screen module of ZenTaoPMS.
*
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Mengyi Liu <liumengyi@cnezsoft.com>
* @package task
* @version $Id: control.php 5106 2022-11-18 17:15:54Z $
* @link https://www.zentao.net
*/
class screen extends control
{
/**
* Browse screen list.
*
* @param int $dimensionID
* @access public
* @return void
*/
public function browse($dimensionID = 0)
{
$dimensionID = $this->commonAction($dimensionID);
$this->view->title = $this->lang->screen->common;
$this->view->screens = $this->screen->getList($dimensionID);
$this->display();
}
/**
* View screen.
*
* @param int $screenID
* @param int $year
* @param int $dept
* @param string $account
* @access public
* @return void
*/
public function view($screenID, $year = 2022, $dept = 0, $account = '')
{
if($screenID == 5)
{
$this->loadModel('execution');
$this->view->type = 'noweekend';
$this->view->executions = $this->screen->getBurnData();
$this->view->date = date('Y-m-d h:i:s', time());
$this->display('screen', 'burn');
}
else
{
$this->view->title = $this->lang->screen->common;
$this->view->year = $year;
$this->view->dept = $dept;
$this->view->account = $account;
$this->view->screen = $this->screen->getByID($screenID, $year, $dept, $account);
$this->display();
}
}
}
+31
View File
@@ -0,0 +1,31 @@
.cell {overflow:auto !important}
.table-footer .pager:before,
.table-footer .pager,
.table-footer {background: none;}
.row a:hover {color: #3c4353;}
.screen {border-radius: 4px; border: solid 1px #ddd; margin-bottom: 20px; position: relative;}
.screen .top {text-align: center; min-height:182px; position:relative;}
.screen .top.img {height: 182px; text-align: center;}
.screen .top.img img {height:100%; width:auto}
.screen .top .image {height:100%; display:flex; align-items:center; justify-content:center;}
.screen .top .image i {font-size:100px;}
.screen .top .image i:before {content: '\f1c5';}
.screen .bottom {padding: 12px 16px; height: 70px;}
.screen .bottom .screen-title{font-size: 14px; color: #0B0F18;}
.screen .bottom .screen-title a{color: white;}
.screen .bottom div.screen-desc{margin: 0;font-size:12px; color: #838A9D; overflow: hidden; display: -webkit-box; line-clamp: 2; -webkit-line-clamp: 2; -webkit-box-orient: vertical;}
.row a.btn-collect-screen {color: white; position: absolute; right: 10px; top: 117px; z-index: 1;}
.bg1 {background:#1183fb;}
.bg2 {background:#ea644a;}
.bg3 {background:#f1a325;}
.bg4 {background:#0bd6c0;}
.bg5 {background:#0997bb;}
.bg6 {background:#0abbad;}
.main-col {background-color: white;}
.main-col .row {margin:10px 0px;}
+9
View File
@@ -0,0 +1,9 @@
.header {display:flex; justify-content:center; }
.img-header {width:1300px; height:120px; background-image:url("/static/images/screen_header.png"); background-repeat:no-repeat;}
.title {padding-top:39px; padding-left:541px; white-space:nowrap; color:#B2D5E5; font-family: "Helvetica Neue",Helvetica,Tahoma,Arial,'PingFang SC','Source Han Sans CN','Source Han Sans','Source Han Serif','Hiragino Sans GB','WenQuanYi Micro Hei','Microsoft YaHei',sans-serif; letter-spacing:5px;}
.time {position:absolute; right:10px; top:30px; color:#eee}
.content {display:flex; justify-content:center; margin-top: 20px;}
.burn {width: 1300px; display: flex; flex-wrap: wrap; justify-content: space-around; padding: 10px 0;}
body {background:rgb(0,16,40);}
.burn > .container {flex: 0 0 33%; margin-top: 40px; height: 370px; width: 370px;}
+4
View File
@@ -0,0 +1,4 @@
.go-preview-scale {transform: scale(1, 1) !important;}
.go-preview-scale > div {margin: 0 auto !important;}
.go-preview.scrollY {background: #001028 !important;}
.go-preview-entity {width: auto !important; height: auto !important;}
+5
View File
@@ -0,0 +1,5 @@
$(function()
{
initBurnChar();
});
+29
View File
@@ -0,0 +1,29 @@
self["MonacoEnvironment"] = (function(paths) {
return {
globalAPI: false,
getWorkerUrl : function (moduleId, label) {
var result = paths[label];
if (/^((http:)|(https:)|(file:)|(\/\/))/.test(result)) {
var currentUrl = String(window.location);
var currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length);
if (result.substring(0, currentOrigin.length) !== currentOrigin) {
var js = '/*' + label + '*/importScripts("' + result + '");';
var blob = new Blob([js], { type: 'application/javascript' });
return URL.createObjectURL(blob);
}
}
return result;
}
};
})({
"editorWorkerService": "/monacoeditorwork/editor.worker.bundle.js",
"typescript": "/monacoeditorwork/ts.worker.bundle.js",
"json": "/monacoeditorwork/json.worker.bundle.js",
"html": "/monacoeditorwork/html.worker.bundle.js",
"javascript": "/monacoeditorwork/ts.worker.bundle.js",
"handlebars": "/monacoeditorwork/html.worker.bundle.js",
"razor": "/monacoeditorwork/html.worker.bundle.js"
});
location.hash = '#/chart/preview/' + screen.id;
window.chartData = screen.chartData;
+6
View File
@@ -0,0 +1,6 @@
<?php
$lang->screen->browse = 'Browse screen';
$lang->screen->view = 'View screen';
$lang->screen->noscreens = 'No screen yet';
$lang->screen->allDepts = 'All depts';
$lang->screen->allUsers = 'All users';
+6
View File
@@ -0,0 +1,6 @@
<?php
$lang->screen->browse = 'Browse screen';
$lang->screen->view = 'View screen';
$lang->screen->noscreens = 'No screen yet';
$lang->screen->allDepts = 'All depts';
$lang->screen->allUsers = 'All users';
+6
View File
@@ -0,0 +1,6 @@
<?php
$lang->screen->browse = 'Browse screen';
$lang->screen->view = 'View screen';
$lang->screen->noscreens = 'No screen yet';
$lang->screen->allDepts = 'All depts';
$lang->screen->allUsers = 'All users';
+6
View File
@@ -0,0 +1,6 @@
<?php
$lang->screen->browse = '浏览数据大屏';
$lang->screen->view = '查看数据大屏';
$lang->screen->noscreens = '暂时没有可视化大屏';
$lang->screen->allDepts = '所有部门';
$lang->screen->allUsers = '所有用户';
+772
View File
@@ -0,0 +1,772 @@
<?php
/**
* The model file of screen module of ZenTaoPMS.
*
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Mengyi Liu <liumengyi@cnezsoft.com>
* @package task
* @version $Id: control.php 5106 2022-02-08 17:15:54Z $
* @link https://www.zentao.net
*/
class screenModel extends model
{
/**
* Filters
*
* @var object
* @access public
*/
public $filter;
/**
* Construct function.
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
$this->filter = new stdclass();
}
/**
* Get screen list.
*
* @param int $dimensionID
* @access public
* @return array
*/
public function getList($dimensionID)
{
return $this->dao->select('*')->from(TABLE_SCREEN)
->where('dimension')->eq($dimensionID)
->andWhere('deleted')->eq(0)
->fetchAll();
}
/**
* Get screen by id.
*
* @param int $screenID
* @param int $year
* @param int $dept
* @param string $account
* @access public
* @return object
*/
public function getByID($screenID, $year, $dept, $account)
{
$this->filter = new stdclass();
$this->filter->screen = $screenID;
$this->filter->year = $year;
$this->filter->dept = $dept;
$this->filter->account = $account;
$this->filter->charts = array();
$screen = $this->dao->select('*')->from(TABLE_SCREEN)->where('id')->eq($screenID)->fetch();
$screen->chartData = $this->genChartData($screen, $year, $dept, $account);
return $screen;
}
/**
* Generate chartData of screen.
*
* @param object $screen
* @param int $year
* @param int $dept
* @param string $account
* @access public
* @return object
*/
public function genChartData($screen)
{
$config = new stdclass();
$config->width = 1300;
$config->height = 1080;
$config->filterShow = false;
$config->hueRotate = 0;
$config->saturate = 1;
$config->contrast = 1;
$config->brightness = 1;
$config->opacity = 1;
$config->rotateZ = 0;
$config->rotateX = 0;
$config->rotateY = 0;
$config->skewX = 0;
$config->skewY = 0;
$config->blendMode = 'normal';
$config->background = '#001028';
$config->selectColor = true;
$config->chartThemeColor = 'dark';
$config->previewScaleType = 'scrollY';
$componentList = json_decode($screen->scheme);
$componentList = json_decode(file_get_contents('/home/liugang/repo/git/screen/' . $screen->id . '.json'));
if(empty($componentList)) $componentList = array();
/* Reset height of canvas. */
foreach($componentList as $component)
{
if(!isset($component->attr)) continue;
$height = $component->attr->y + $component->attr->h;
if($height > $config->height) $config->height = $height;
}
$config->height += 50;
$chartData = new stdclass();
$chartData->editCanvasConfig = $config;
$chartData->componentList = $this->buildComponentList($componentList);
$chartData->requestGlobalConfig = json_decode('{ "requestDataPond": [], "requestOriginUrl": "", "requestInterval": 30, "requestIntervalUnit": "second", "requestParams": { "Body": { "form-data": {}, "x-www-form-urlencoded": {}, "json": "", "xml": "" }, "Header": {}, "Params": {} } return $chartData; }');
return $chartData;
}
/**
* Build component list.
*
* @param array $componentList
* @access public
* @return array
*/
public function buildComponentList($componentList)
{
$components = array();
foreach($componentList as $component)
{
$components[] = $this->buildComponent($component);
}
return $components;
}
/**
* Build component.
*
* @param object $component
* @access public
* @return object
*/
public function buildComponent($component)
{
/* If chart is builtin, build it. */
if(isset($component->sourceID) and $component->sourceID) return $this->buildChart($component);
if(isset($component->key) and $component->key === 'Select') return $this->buildSelect($component);
if(!$component->isGroup) return $this->setComponentDefaults($component);
$component->groupList = $this->buildComponentList($component->groupList);
return $this->buildGroup($component);
}
/**
* Build chart group.
*
* @param object $component
* @access public
* @return object
*/
public function buildGroup($component)
{
return $this->setComponentDefaults($component);
}
/**
* Set component defaults.
*
* @param object $component
* @access public
* @return object
*/
public function setComponentDefaults($component)
{
if(!isset($component->styles)) $component->styles = json_decode('{"filterShow": false, "hueRotate": 0, "saturate": 1, "contrast": 1, "brightness": 1, "opacity": 1, "rotateZ": 0, "rotateX": 0, "rotateY": 0, "skewX": 0, "skewY": 0, "blendMode": "normal", "animations": []}');
if(!isset($component->status)) $component->status = json_decode('{"lock": false, "hide": false}');
if(!isset($component->request)) $component->request = json_decode('{ "requestDataType": 0, "requestHttpType": "get", "requestUrl": "", "requestIntervalUnit": "second", "requestContentType": 0, "requestParamsBodyType": "none", "requestSQLContent": { "sql": "select * from where" }, "requestParams": { "Body": { "form-data": {}, "x-www-form-urlencoded": {}, "json": "", "xml": "" }, "Header": {}, "Params": {} } }');
return $component;
}
/**
* Build select.
*
* @param object $component
* @access public
* @return object
*/
public function buildSelect($component)
{
switch($component->type)
{
case 'year':
$component->option->value = $this->filter->year;
$begin = $this->dao->select('YEAR(MIN(date)) year')->from(TABLE_ACTION)->where('date')->ne('0000-00-00')->fetch('year');
if($begin < 2009) $begin = 2009;
$options = array();
for($year = date('Y'); $year >= $begin; $year--) $options[] = array('label' => $year, 'value' => $year);
$component->option->dataset = $options;
$url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen. "&year=' + value + '&dept=" . $this->filter->dept . "&account=" . $this->filter->account . "')";
$component->option->onChange = "window.location.href = $url";
break;
case 'dept':
$component->option->value = $this->filter->dept;
$options = array(array('label' => $this->lang->screen->allDepts, 'value' => '0'));
$depts = $this->dao->select('id,name')->from(TABLE_DEPT)->where('grade')->eq(1)->fetchAll();
foreach($depts as $dept)
{
$options[] = array('label' => $dept->name, 'value' => $dept->id);
}
$component->option->dataset = $options;
$url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen . "&year=" . $this->filter->year . "&dept=' + value + '&account=')";
$component->option->onChange = "window.location.href = $url";
break;
case 'account':
$component->option->value = $this->filter->account;
$options = array(array('label' => $this->lang->screen->allUsers, 'value' => ''));
$depts = array();
if($this->filter->dept) $depts = $this->dao->select('id')->from(TABLE_DEPT)->where('path')->like(',' . $this->filter->dept . ',%')->fetchPairs();
$users = $this->dao->select('account,realname')->from(TABLE_USER)
->where('deleted')->eq(0)
->beginIF($this->filter->dept)->andWhere('dept')->in($depts)->fi()
->fetchAll();
foreach($users as $user)
{
$options[] = array('label' => $user->realname, 'value' => $user->account);
}
$component->option->dataset = $options;
$url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen . "&year=" . $this->filter->year . "&dept=" . $this->filter->dept . "&account=' + value)";
$component->option->onChange = "window.location.href = $url";
break;
}
foreach($component->filterCharts as $chart)
{
if(!isset($this->filter->charts[$chart->chart])) $this->filter->charts[$chart->chart] = array();
$this->filter->charts[$chart->chart][$component->type] = $chart->field;
}
return $this->setComponentDefaults($component);
}
/**
* Build chart.
*
* @param object $component
* @access public
* @return object
*/
public function buildChart($component)
{
$chart = $this->dao->select('*')->from(TABLE_CHART)->where('id')->eq($component->sourceID)->fetch();
switch($chart->type)
{
case 'card':
return $this->buildCardChart($component, $chart);
break;
case 'line':
return $this->buildLineChart($component, $chart);
break;
case 'bar':
return $this->buildBarChart($component, $chart);
break;
case 'pie':
return $this->buildPieChart($component, $chart);
break;
case 'radar':
return $this->buildRadarChart($component, $chart);
break;
case 'org':
return $this->buildOrgChart($component, $chart);
break;
case 'funnel':
return $this->buildFunnelChart($component, $chart);
break;
case 'table':
return $this->buildTableChart($component, $chart);
break;
}
}
/**
* Set SQL filter
*
* @param object $chart
* @access public
* @return string
*/
public function setFilterSQL($chart)
{
if(isset($this->filter->charts[$chart->id]))
{
$conditions = array();
foreach($this->filter->charts[$chart->id] as $key => $field)
{
switch($key)
{
case 'year':
$conditions[] = $field . ' = ' . $this->filter->$key;
break;
case 'dept':
if($this->filter->dept and !$this->filter->account)
{
$accountField = $this->filter->charts[$chart->id]['account'];
$users = $this->dao->select('account')->from(TABLE_USER)->alias('t1')
->leftJoin(TABLE_DEPT)->alias('t2')
->on('t1.dept = t2.id')
->where('t2.path')->like(',' . $this->filter->dept . ',%')
->fetchPairs('account');
$accounts = array();
foreach($users as $account) $accounts[] = "'" . $account . "'";
$conditions[] = $accountField . ' IN (' . implode(',', $accounts) . ')';
}
break;
case 'account':
if($this->filter->account) $conditions[] = $field . " = '" . $this->filter->$key . "'";
break;
}
}
if($conditions) return 'SELECT * FROM (' . str_replace(';', '', $chart->sql) . ') AS t1 WHERE ' . implode(' AND ', $conditions);
}
return $chart->sql;
}
/**
* Build card chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildCardChart($component, $chart)
{
if(!$chart->settings)
{
$component->option->dataset = '?';
}
else
{
$value = 0;
if($chart->sql)
{
$settings = json_decode($chart->settings);
if($settings and isset($settings->value))
{
$field = $settings->value->field;
$sql = $this->setFilterSQL($chart);
$results = $this->dao->query($sql)->fetchAll();
if($settings->value->type === 'value')
{
$value = $results[0]->$field;
}
if($settings->value->agg === 'count')
{
$value = count($results);
}
else if($settings->value->agg === 'sum')
{
foreach($results as $result)
{
$value += $result->$field;
}
$value = round($value);
}
}
else
{
$value = '?';
}
}
$component->option->dataset = (string)$value;
}
return $this->setComponentDefaults($component);
}
/**
* Build line chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildLineChart($component, $chart)
{
if(!$chart->settings)
{
$component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
$component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
$component->key = "LineCommon";
$component->chartConfig = json_decode('{"key":"LineCommon","chartKey":"VLineCommon","conKey":"VCLineCommon","title":"折线图","category":"Lines","categoryName":"折线图","package":"Charts","chartFrame":"echarts","image":"/static/png/line-e714bc74.png"}');
$component->option = json_decode('{"legend":{"show":true,"top":"5%","textStyle":{"color":"#B9B8CE"}},"xAxis":{"type":"category"},"yAxis":{"show":true,"axisLine":{"show":true},"type":"value"},"backgroundColor":"rgba(0,0,0,0)"}');
return $this->setComponentDefaults($component);
}
else
{
if($chart->sql)
{
$settings = json_decode($chart->settings);
if($settings and isset($settings->xaxis))
{
$dimensions = array($settings->xaxis[0]->name);
foreach($settings->yaxis as $yaxis) $dimensions[] = $yaxis->name;
$sourceData = array();
$sql = $this->setFilterSQL($chart);
$results = $this->dao->query($sql)->fetchAll();
foreach($results as $result)
{
$key = $settings->xaxis[0]->name;
$field = $settings->xaxis[0]->field;
$row = array($key => $result->$field);
foreach($settings->yaxis as $yaxis)
{
$field = $yaxis->field;
$row[$yaxis->name] = $result->$field;
}
$sourceData[] = $row;
}
$component->option->dataset->dimensions = $dimensions;
$component->option->dataset->source = $sourceData;
}
}
return $this->setComponentDefaults($component);
}
}
/**
* Build table chart
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildTableChart($component, $chart)
{
if(!$chart->settings)
{
$component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
$component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
$component->key = "TableScrollBoard";
$component->chartConfig = json_decode('{"key":"TableScrollBoard","chartKey":"VTableScrollBoard","conKey":"VCTableScrollBoard","title":"轮播列表","category":"Tables","categoryName":"表格","package":"Tables","chartFrame":"common","image":"/static/png/table_scrollboard-fb642e78.png"}');
$component->option = json_decode('{"header":["列1","列2","列3"],"dataset":[["行1列1","行1列2","行1列3"],["行2列1","行2列2","行2列3"],["行3列1","行3列2","行3列3"]],"rowNum":2,"waitTime":2,"headerHeight":35,"carousel":"single","headerBGC":"#00BAFF","oddRowBGC":"#003B51","evenRowBGC":"#0A2732"}');
return $this->setComponentDefaults($component);
}
else
{
if($chart->sql)
{
$settings = json_decode($chart->settings);
if($settings and isset($settings->column))
{
$header = array();
$dataset = array();
foreach($settings->column as $column)
{
$header[$column->field] = $column->name;
}
$sql = $this->setFilterSQL($chart);
$results = $this->dao->query($sql)->fetchAll();
foreach($results as $result)
{
$row = array();
foreach($header as $field => $name)
{
$row[] = $result->$field;
}
$dataset[] = $row;
}
$component->option->header = array_values($header);
$component->option->dataset = $dataset;
}
}
return $this->setComponentDefaults($component);
}
}
/**
* Build bar chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildBarChart($component, $chart)
{
if(!$chart->settings)
{
$component->request = json_decode('{"requestDataType": 0, "requestHttpType": "get", "requestUrl": "", "requestIntervalUnit": "second", "requestContentType": 0, "requestParamsBodyType": "none", "requestSQLContent": { "sql": "select * from where" }, "requestParams": { "Body": { "form-data": {}, "x-www-form-urlencoded": {}, "json": "", "xml": "" }, "Header": {}, "Params": {}}}');
$component->events = json_decode('{"baseEvent": {}, "advancedEvents": {}}');
$component->key = "BarCrossrange";
$component->chartConfig = json_decode('{"key": "BarCrossrange", "chartKey": "VBarCrossrange", "conKey": "VCBarCrossrange", "title": "横向柱状图", "category": "Bars", "categoryName": "柱状图", "package": "Charts", "chartFrame": "echarts", "image": "/static/png/bar_y-05067169.png" }');
$component->option = json_decode('{"xAxis": { "show": true, "type": "category" }, "yAxis": { "show": true, "axisLine": { "show": true }, "type": "value" }, "series": [], "backgroundColor": "rgba(0,0,0,0)"}');
return $this->setComponentDefaults($component);
}
else
{
if($chart->sql)
{
$settings = json_decode($chart->settings);
if($settings and isset($settings->xaxis))
{
$dimensions = array($settings->xaxis[0]->name);
foreach($settings->yaxis as $yaxis) $dimensions[] = $yaxis->name;
$sourceData = array();
$sql = $this->setFilterSQL($chart);
$results = $this->dao->query($sql)->fetchAll();
foreach($results as $result)
{
$key = $settings->xaxis[0]->name;
$field = $settings->xaxis[0]->field;
$row = array($key => $result->$field);
foreach($settings->yaxis as $yaxis)
{
$field = $yaxis->field;
$row[$yaxis->name] = $result->$field;
}
$sourceData[] = $row;
}
$component->option->dataset->dimensions = $dimensions;
$component->option->dataset->source = $sourceData;
}
}
return $this->setComponentDefaults($component);
}
}
/**
* Build pie chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildPieChart($component, $chart)
{
if(!$chart->settings)
{
$component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
$component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
$component->key = "PieCommon";
$component->chartConfig = json_decode('{"key":"PieCommon","chartKey":"VPieCommon","conKey":"VCPieCommon","title":"饼图","category":"Pies","categoryName":"饼图","package":"Charts","chartFrame":"echarts","image":"/static/png/pie-9620f191.png"}');
$component->option = json_decode('{"type":"nomal","series":[{"type":"pie","radius":"70%","roseType":false}],"backgroundColor":"rgba(0,0,0,0)"}');
return $this->setComponentDefaults($component);
}
else
{
if($chart->sql)
{
$settings = json_decode($chart->settings);
if($settings and isset($settings->metric))
{
$dimensions = array($settings->group[0]->name, $settings->metric[0]->field);
$sourceData = array();
$sql = $this->setFilterSQL($chart);
$results = $this->dao->query($sql)->fetchAll();
$group = $settings->group[0]->field;
$groupCount = array();
foreach($results as $result)
{
if($settings->metric[0]->agg == 'count')
{
if(!isset($groupCount[$result->$group])) $groupCount[$result->$group] = 0;
$groupCount[$result->$group]++;
}
}
foreach($groupCount as $groupValue => $groupCount)
{
$sourceData[] = array($settings->group[0]->name => $groupValue, $settings->metric[0]->field => $groupCount);
}
}
$component->option->dataset->dimensions = $dimensions;
$component->option->dataset->source = $sourceData;
}
return $this->setComponentDefaults($component);
}
}
/**
* Build radar chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildRadarChart($component, $chart)
{
if(!$chart->settings)
{
$component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
$component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
$component->key = "Radar";
$component->chartConfig = json_decode('{"key":"Radar","chartKey":"VRadar","conKey":"VCRadar","title":"雷达图","category":"Mores","categoryName":"更多","package":"Charts","chartFrame":"common","image":"/static/png/radar-91567f95.png"}');
$component->option = json_decode('{"radar":{"indicator":[{"name":"数据1","max":6500},{"name":"数据2","max":16000},{"name":"数据3","max":30000},{"name":"数据4","max":38000},{"name":"数据5","max":52000}]},"series":[{"name":"radar","type":"radar","areaStyle":{"opacity":0.1},"data":[{"name":"data1","value":[4200,3000,20000,35000,50000]}]}],"backgroundColor":"rgba(0,0,0,0)"}');
return $this->setComponentDefaults($component);
}
else
{
$indicator = array();
$seriesData = array();
if($chart->sql)
{
$settings = json_decode($chart->settings);
if($settings and isset($settings->metric))
{
$sql = $this->setFilterSQL($chart);
$results = $this->dao->query($sql)->fetchAll();
$group = $settings->group[0]->field;
$metrics = array();
foreach($settings->metric as $metric)
{
$metrics[$metric->key] = array('field' => $metric->field, 'name' => $metric->name, 'value' => 0);
}
$max = 0;
foreach($results as $result)
{
if(isset($metrics[$result->$group]))
{
$field = $metrics[$result->$group]['field'];
$metrics[$result->$group]['value'] = $result->$field;
if($max < $result->$field) $max = $result->$field;
}
}
$data = array('name' => '', 'value' => array());
$value = array();
foreach($metrics as $key => $metric)
{
$indicator[] = array('name' => $metric['name'], 'max' => $max);
$data['value'][] = $metric['value'];
$value[] = $metric['value'];
}
$seriesData[] = $data;
}
$component->option->dataset->radarIndicator = $indicator;
$component->option->radar->indicator = $indicator;
$component->option->dataset->seriesData = $seriesData;
$component->option->series[0]->data[0]->value = $value;
}
return $this->setComponentDefaults($component);
}
}
/**
* Build org chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildOrgChart($component, $chart)
{
//TODO
}
/**
* Build funnel chart.
*
* @param object $component
* @param object $chart
* @access public
* @return object
*/
public function buildFunnelChart($component, $chart)
{
if(!$chart->settings)
{
$component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
$component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
$component->key = "Funnel";
$component->chartConfig = json_decode('{"key":"Funnel","chartKey":"VFunnel","conKey":"VCFunnel","title":"漏斗图","category":"Mores","categoryName":"更多","package":"Charts","chartFrame":"echarts","image":"/static/png/funnel-d032fdf6.png"}');
$component->option = json_decode('{"dataset":{"dimensions":["product","dataOne"],"source":[{"product":"data1","dataOne":20},{"product":"data2","dataOne":40},{"product":"data3","dataOne":60},{"product":"data4","dataOne":80},{"product":"data5","dataOne":100}]},"series":[{"name":"Funnel","type":"funnel","gap":5,"label":{"show":true,"position":"inside","fontSize":12}}],"backgroundColor":"rgba(0,0,0,0)"}');
return $this->setComponentDefaults($component);
}
}
/**
* Get burn data.
*
* @access public
* @return array
*/
public function getBurnData()
{
$type = 'noweekend';
$this->loadModel('execution');
$executions = $this->execution->getPairs(0, 'sprint') + $this->execution->getPairs(0, 'stage');
$executionData = array();
foreach($executions as $executionID => $executionName)
{
$execution = $this->execution->getByID($executionID);
/* Get date list. */
if(((strpos('closed,suspended', $execution->status) === false and helper::today() > $execution->end)
or ($execution->status == 'closed' and substr($execution->closedDate, 0, 10) > $execution->end)
or ($execution->status == 'suspended' and $execution->suspendedDate > $execution->end))
and strpos($type, 'delay') === false)
$type .= ',withdelay';
$deadline = $execution->status == 'closed' ? substr($execution->closedDate, 0, 10) : $execution->suspendedDate;
$deadline = strpos('closed,suspended', $execution->status) === false ? helper::today() : $deadline;
$endDate = strpos($type, 'withdelay') !== false ? $deadline : $execution->end;
list($dateList, $interval) = $this->execution->getDateList($execution->begin, $endDate, $type, 0, 'Y-m-d', $execution->end);
$chartData = $this->execution->buildBurnData($executionID, $dateList, $type, 'left');
$execution->chartData = $chartData;
$executionData[$executionID] = $execution;
}
return $executionData;
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php include $app->getModuleRoot() . 'common/view/header.html.php';?>
<div class='main-row'>
<div id="mainContent">
<?php if(empty($screens)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->screen->noscreens;?></span></p>
</div>
<?php else:?>
<div class='main-col'>
<div class='row'>
<?php $i = 1;?>
<?php foreach ($screens as $screenID => $screen):?>
<?php $viewLink = $this->createLink('screen', 'view', "id=$screen->id");?>
<div class='col-md-3' data-id='<?php echo $screenID;?>'>
<a href='<?php echo $viewLink?>' target="_blank" />
<div class='screen'>
<?php if(!empty($file)):?>
<div class='top img'>
<img src='<?php echo $file->webPath;?>' controls='controls' width='100%'/>
</div>
<?php else:?>
<div class='top img bg<?php echo $i?>'>
</div>
<?php $i++;?>
<?php if($i == 13) $i = 1;?>
<?php endif?>
<div class='bottom'>
<div class='screen-title text-ellipsis' title='<?php echo $screen->name?>'><?php echo $screen->name;?></div>
<div class='screen-desc' title="<?php echo $screen->desc;?>">
<?php echo empty($screen->desc) ? $lang->screen->noDesc : $screen->desc;?>
</div>
</div>
</div>
</a>
</div>
<?php endforeach;?>
</div>
</div>
<?php endif;?>
</div>
</div>
<?php include $app->getModuleRoot() . 'common/view/footer.html.php';?>
+136
View File
@@ -0,0 +1,136 @@
<?php include $app->getModuleRoot() . 'common/view/header.lite.html.php';?>
<?php include $app->getModuleRoot() . 'common/view/chart.html.php';?>
<?php js::import($jsRoot . 'echarts/echarts.common.min.js');?>
<?php js::set('executions', $executions);?>
<?php js::set('type', $type);?>
<div class='header'>
<div class='img-header'>
<h2 class='title'>迭代燃尽图</h2>
<span class='time'><?php echo '更新时间:' . $date;?></span>
</div>
</div>
<div class='content'>
<div class='burn'>
<?php foreach($executions as $executionID => $execution):?>
<div class="container" id= <?php echo 'burn' . $executionID;?>>
</div>
<?php endforeach;?>
</div>
</div>
<script>
function initBurnChar()
{
Object.values(executions).forEach(function(execution)
{
var chartDom = document.getElementById('burn' + execution.id);
var myChart = echarts.init(chartDom);
var option = {
title: {
text: execution.name,
textStyle: {
color: '#a1c4e9'
}
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ["<?php echo $lang->execution->charts->burn->graph->actuality;?>","<?php echo $lang->execution->charts->burn->graph->reference;?>"],
textStyle:{
color: '#dee0e4',
fontSize: 14
},
right: 0
},
color: ['#42526a', '#2567cf'],
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
axisLabel: {
show: true,
textStyle: {
color: '#dee0e4'
}
},
data: execution.chartData['labels']
},
yAxis: {
type: 'value',
axisLabel: {
show: true,
textStyle: {
color: '#dee0e4'
}
},
splitLine: {
show: true,
lineStyle:{
color: ['#182a43'],
width: 2,
type: 'solid'
}
}
},
series: [
{
name: "<?php echo $lang->execution->charts->burn->graph->reference;?>",
symbol: 'circle',
symbolSize: 4,
type: 'line',
areaStyle: {color: '#2667cf00'},
lineStyle: {
"width": 2,
"type": "solid"
},
data: JSON.parse(execution.chartData['baseLine'])
},
{
name: "<?php echo $lang->execution->charts->burn->graph->actuality;?>",
symbol: 'circle',
symbolSize: 4,
type: 'line',
areaStyle: {
"opacity": 0.8,
"color": {
"colorStops": [
{
"offset": 0,
"color": "rgba(73, 146, 255, 0.5)"
},
{
"offset": 1,
"color": "rgba(73, 146, 255, 0.1)"
}
],
"x": 0,
"y": 0,
"x2": 0,
"y2": 1,
"type": "linear",
"global": false
}
},
lineStyle: {
"width": 2,
"type": "solid"
},
data: JSON.parse(execution.chartData['burnLine'])
},
]
};
myChart.setOption(option);
});
}
</script>
<?php include $app->getModuleRoot() . 'common/view/footer.lite.html.php';?>
+16
View File
@@ -0,0 +1,16 @@
<?php include $app->getModuleRoot() . 'common/view/header.lite.html.php';?>
<script type="module" crossorigin src="/static/js/index-3da848c6.js"></script>
<link rel="stylesheet" href="/static/css/index-c34cea1e.css">
<?php js::set('screen', $screen);?>
<?php js::set('year', $year);?>
<?php js::set('dept', $dept);?>
<?php js::set('account', $account);?>
<div id="appProvider" style="display: none;"></div>
<div id="app">
<div class="first-loading-wrp">
<div class="loading-wrp">
<span class="dot dot-spin"></span>
</div>
</div>
</div>
<?php include $app->getModuleRoot() . 'common/view/footer.lite.html.php';?>
+16
View File
@@ -0,0 +1,16 @@
<?php
class screenZen extends screen
{
/**
* Common Action.
*
* @param int $dimensionID
* @param bool $setMenu
* @access public
* @return void
*/
public function commonAction($dimensionID, $setMenu = true)
{
return 1;
}
}