Merge branch 'master' of https://git.zcorp.cc/easycorp/zentaopms
This commit is contained in:
@@ -2886,8 +2886,8 @@ class baseRouter
|
||||
{
|
||||
if(isset($this->config->zin) || isset($_SERVER['HTTP_X_ZIN_OPTIONS']))
|
||||
{
|
||||
if(!isset($this->zinErrors)) $this->zinErrors = [];
|
||||
$this->zinErrors[] = ['file' => $file, 'line' => $line, 'message' => $message];
|
||||
if(!isset($this->zinErrors)) $this->zinErrors = array();
|
||||
$this->zinErrors[] = array('file' => $file, 'line' => $line, 'message' => $message, 'level' => $level);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
@@ -9,16 +9,36 @@ title=测试 projectModel->activate();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
激活id为66状态是closed的项目 >> status,closed,doing
|
||||
激活id为73状态是suspended的项目 >> status,suspended,doing
|
||||
激活id为20的项目 >> object
|
||||
激活id为20的项目 >> object
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
|
||||
$changes1 = $tester->project->activate(66);
|
||||
$changes2 = $tester->project->activate(73);
|
||||
$project = new Project();
|
||||
|
||||
$data1 = array(
|
||||
'id' => 2,
|
||||
'begin'=> '2023-04-26',
|
||||
'end'=> '10001-01-07',
|
||||
'readjustTime'=> 1,
|
||||
'readjustTask'=> 1,
|
||||
'status'=> 'doing',
|
||||
'comment'=> 'sdfsdf'
|
||||
);
|
||||
|
||||
$data2 = array(
|
||||
'id' => 3,
|
||||
'begin'=> '2023-04-26',
|
||||
'end'=> '10001-01-07',
|
||||
'readjustTime'=> 1,
|
||||
'readjustTask'=> 1,
|
||||
'status'=> 'doing',
|
||||
'comment'=> 'sdfsdf'
|
||||
);
|
||||
|
||||
r($project->activate($data1)) && p('1:field,old,new') && e('status,closed,doing'); // 激活id为2状态是closed的项目
|
||||
r($project->activate($data2)) && p('1:field,old,new') && e('status,suspended,doing'); // 激活id为3状态是suspended的项目
|
||||
|
||||
r($changes1) && p('1:field,old,new') && e('status,closed,doing'); // 激活id为66状态是closed的项目
|
||||
r($changes2) && p('1:field,old,new') && e('status,suspended,doing'); // 激活id为73状态是suspended的项目
|
||||
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
|
||||
function initData()
|
||||
{
|
||||
$task = zdTable('task');
|
||||
$task->id->range('1-5');
|
||||
$task->name->prefix('任务')->range('1-5');
|
||||
$task->consumed->range('0,10,15,20,25');
|
||||
$task->left->range('0,0,1,5,10');
|
||||
|
||||
$task->gen(5);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试computeTaskProgress
|
||||
cid=1
|
||||
|
||||
*/
|
||||
initData();
|
||||
|
||||
$tester->loadModel('task');
|
||||
|
||||
$taskIDList = range(1,5);
|
||||
$taskList = $tester->task->getByList($taskIDList);
|
||||
|
||||
r($tester->task->computeTaskProgress($taskList[1])) && p() && e('0'); //测试任务消耗工时为0,剩余工时为0的情况
|
||||
r($tester->task->computeTaskProgress($taskList[2])) && p() && e('100'); //测试任务消耗工时不为0,剩余工时为0的情况
|
||||
r($tester->task->computeTaskProgress($taskList[3])) && p() && e('94'); //测试任务消耗工时为15,剩余工时为1的情况
|
||||
r($tester->task->computeTaskProgress($taskList[4])) && p() && e('80'); //测试任务消耗工时为20,剩余工时为5的情况
|
||||
r($tester->task->computeTaskProgress($taskList[5])) && p() && e('71'); //测试任务消耗工时为25,剩余工时为10的情况
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
|
||||
function initData()
|
||||
{
|
||||
$task = zdTable('task');
|
||||
$task->id->range('1-5');
|
||||
$task->name->prefix('任务')->range('1-5');
|
||||
$task->consumed->range('0,10,15,20,25');
|
||||
$task->left->range('0,0,1,5,10');
|
||||
|
||||
$task->gen(5);
|
||||
}
|
||||
|
||||
/**
|
||||
title=测试computeTasksProgress
|
||||
cid=2
|
||||
|
||||
*/
|
||||
$tester->loadModel('task');
|
||||
|
||||
initData();
|
||||
|
||||
$taskIDList = range(1,5);
|
||||
$taskList = $tester->task->getByList($taskIDList);
|
||||
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('1:progress') && e('0'); //测试任务消耗工时为0,剩余工时为0的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('2:progress') && e('100'); //测试任务消耗工时不为0,剩余工时为0的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('3:progress') && e('94'); //测试任务消耗工时为15,剩余工时为1的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('4:progress') && e('80'); //测试任务消耗工时为20,剩余工时为5的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('5:progress') && e('71'); //测试任务消耗工时为25,剩余工时为10的情况
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 projectModel->activate();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
激活id为66状态是closed的项目 >> status,closed,doing
|
||||
激活id为73状态是suspended的项目 >> status,suspended,doing
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
|
||||
$changes1 = $tester->project->activate(66);
|
||||
$changes2 = $tester->project->activate(73);
|
||||
|
||||
r($changes1) && p('1:field,old,new') && e('status,closed,doing'); // 激活id为66状态是closed的项目
|
||||
r($changes2) && p('1:field,old,new') && e('status,suspended,doing'); // 激活id为73状态是suspended的项目
|
||||
+105
-13
@@ -1,8 +1,10 @@
|
||||
(function(){
|
||||
(function()
|
||||
{
|
||||
let DEBUG = true;
|
||||
const currentCode = window.name.substring(4);
|
||||
const isInAppTab = parent.window !== window;
|
||||
const fetchTasks = new Map();
|
||||
const phpErrTypes = {'1': 'error', '2': 'warning', '4': 'parse', '8': 'notice', '16': 'core-error', '32': 'core-warning', '64': 'compile-error', '128': 'compile-warning', '256': 'user-error', '512': 'user-warning', '1024': 'user-notice', '2048': 'strict', '4096': 'recoverable-error', '8192': 'deprecated', '16384': 'user-deprecated', '32767': 'all'};
|
||||
let currentAppUrl = '';
|
||||
|
||||
$.apps = $.extend(
|
||||
@@ -23,7 +25,7 @@
|
||||
reloadApp: function(code, url)
|
||||
{
|
||||
loadPage(url);
|
||||
}
|
||||
},
|
||||
}, parent.window.$.apps);
|
||||
|
||||
const renderMap =
|
||||
@@ -39,12 +41,86 @@
|
||||
activeFeature: (data) => activeNav(data, '#featureBar'),
|
||||
activeMenu: activeNav,
|
||||
table: updateTable,
|
||||
zinErrors: showZinErrors
|
||||
fatal: showFatalError,
|
||||
zinErrors: (data, info, options) => showErrors(data, options.id === 'page'),
|
||||
};
|
||||
|
||||
function showZinErrors(data)
|
||||
function showFatalError(data, info, options)
|
||||
{
|
||||
if(DEBUG && Array.isArray(data) && data.length) console.log('[ZIN] errors:', data);
|
||||
$('body').empty().append($(`<div class="panel danger shadow-xl mx-auto my-4 rounded-lg" style="max-width: 1000px"><div class="panel-heading"><div class="panel-title font-bold text-lg">Fatal error: ${options.url}</div></div></div>`).append($('<div class="panel-body mono"></div>').append(data)));
|
||||
}
|
||||
|
||||
function initZinbar()
|
||||
{
|
||||
if(!DEBUG) return;
|
||||
let $bar = $('#zinbar');
|
||||
if($bar.length) return;
|
||||
|
||||
$bar = $(
|
||||
[
|
||||
'<div id="zinbar" class="shadow-xl h-5 bg-inverse text-canvas fixed row items-center text-base" style="right:0;bottom:0;z-index: 99999">',
|
||||
'<div id="zinErrors" class="mono row items-center"></div>',
|
||||
'<div id="pagePerf" class="row items-center"></div>',
|
||||
'<div id="partPerf" class="row items-center"></div>',
|
||||
'<div id="zinErrorList" class="mono absolute shadow-xl ring rounded fade-from-right" style="bottom:100%;right:0;margin-bottom:1px"></div>',
|
||||
'</div>'
|
||||
].join('')).insertAfter('body');
|
||||
|
||||
$('#zinErrorList').on('click', '.zin-error-item', function()
|
||||
{
|
||||
const error = $(this).data('error');
|
||||
navigator.clipboard.writeText(`vim +${error.line} ${error.file}`).then(() => {zui.Messager.show({content: 'Copied vim command to clipboard.', type: 'success'});});
|
||||
});
|
||||
}
|
||||
|
||||
function updatePerfInfo(options, stage, error)
|
||||
{
|
||||
console.log('updatePerfInfo', options, stage, error);
|
||||
options[stage] = performance.now();
|
||||
const $perf = options.id === 'page' ? $('#pagePerf') : $('#partPerf');
|
||||
if(stage === 'requestBegin')
|
||||
{
|
||||
$perf.html(`<div class="opacity-50 pl-2">${options.id === 'page' ? 'PAGE' : 'PARTIAL'}</div>`).append($('<div class="px-2 zin-perf-load">loading...</div>')).attr('title', `Loading from ${options.url}`);
|
||||
}
|
||||
else if(stage === 'requestEnd')
|
||||
{
|
||||
const loadTime = options.requestEnd - options.requestBegin;
|
||||
$perf.find('.zin-perf-load').addClass('font-bold').html(`<i class="icon icon-arrow-down"></i>${loadTime.toFixed(2)}ms`).addClass(loadTime > 400 ? 'text-danger' : (loadTime > 100 ? 'text-warning' : 'text-success')).attr('title', `Load time for ${options.url}`);
|
||||
if(error) showErrors([{message: error.message}]);
|
||||
}
|
||||
else if(stage === 'renderBegin')
|
||||
{
|
||||
$perf.append($('<div class="px-2 zin-perf-render">rendering...</div>')).attr('title', `Renderring ${options.id}`);
|
||||
}
|
||||
else if(stage === 'renderEnd')
|
||||
{
|
||||
const renderTime = options.renderEnd - options.renderBegin;
|
||||
$perf.find('.zin-perf-render').addClass('font-bold').html(`<i class="icon icon-${renderTime > 200 ? 'frown' : (renderTime > 50 ? 'meh' : 'smile')}"></i> ${renderTime.toFixed(2)}ms`).addClass(renderTime > 200 ? 'text-danger' : (renderTime > 50 ? 'text-warning' : 'text-success')).attr('title', `Render time for ${options.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
function showErrors(data, clear)
|
||||
{
|
||||
if(!DEBUG) return;
|
||||
|
||||
const $count = $('#zinErrors').empty();
|
||||
const $list = $('#zinErrorList');
|
||||
if(clear) $list.empty();
|
||||
data.forEach((error) =>
|
||||
{
|
||||
const phpErrType = phpErrTypes[error.level] || 'info';
|
||||
const errType = phpErrType.includes('error') ? 'error' : (phpErrType.includes('warning') ? 'warning' : 'info');
|
||||
$(`<div class="zin-error-item ${errType}-pale text-fore px-2 py-1 ring ring-darker" style=""><div class="zin-error-msg font-bold"><strong class="text-${errType}" style="text-transform: uppercase;">${phpErrType}</strong> ${error.message}</div><div class="zin-error-info ellipsis text-sm opacity-60"><strong>vim +${error.line}</strong> ${error.file}</div></div>`).data('error', error).appendTo($list);
|
||||
});
|
||||
|
||||
const warnings = $list.find('.zin-error-item.warning-pale').length;
|
||||
const errors = $list.find('.zin-error-item.error-pale').length;
|
||||
const infos = $list.find('.zin-error-item.info-pale').length;
|
||||
if(errors) $count.append(`<span class="error font-bold px-2">ERRO ${errors}</span>`);
|
||||
if(warnings) $count.append(`<span class="warning font-bold px-2">WARN ${warnings}</span>`);
|
||||
if(infos) $count.append(`<span class="info font-bold px-2">INFO ${infos}</span>`);
|
||||
|
||||
if(clear && (warnings || errors || infos)) $list.addClass('in');
|
||||
}
|
||||
|
||||
function updatePageWithHtml(data)
|
||||
@@ -90,12 +166,12 @@
|
||||
dtable.render(props);
|
||||
}
|
||||
|
||||
function renderPartial(info)
|
||||
function renderPartial(info, options)
|
||||
{
|
||||
if(window.config.onRenderPage && window.config.onRenderPage(info)) return;
|
||||
|
||||
const render = renderMap[info.name];
|
||||
if(render) return render(info.data);
|
||||
if(render) return render(info.data, info, options);
|
||||
|
||||
/* Common render */
|
||||
const selector = parseSelector(info.selector);
|
||||
@@ -132,10 +208,10 @@
|
||||
else $target.replaceWith(info.data);
|
||||
}
|
||||
|
||||
function renderPage(list)
|
||||
function renderPage(list, options)
|
||||
{
|
||||
if(DEBUG) console.log('[APP] ', 'render:', list);
|
||||
list.forEach(renderPartial);
|
||||
list.forEach(item => renderPartial(item, options));
|
||||
$.apps.updateApp(currentCode, currentAppUrl, document.title);
|
||||
}
|
||||
|
||||
@@ -172,23 +248,37 @@
|
||||
const target = options.target || '#main';
|
||||
const selectors = Array.isArray(options.selectors) ? options.selectors : options.selectors.split(',');
|
||||
const url = options.url;
|
||||
|
||||
if(DEBUG) console.log('[APP]', 'request', options);
|
||||
if(DEBUG && !selectors.includes('zinErrors()')) selectors.push('zinErrors()');
|
||||
const isDebugRequest = DEBUG && selectors.length !== 1 || selectors[0] !== 'zinErrors()';
|
||||
return $.ajax(
|
||||
{
|
||||
url: url,
|
||||
headers: {'X-ZIN-Options': JSON.stringify($.extend({selector: selectors, type: 'list'}, options.zinOptions)), 'X-ZIN-App': currentCode},
|
||||
beforeSend: () => toggleLoading(target, true),
|
||||
beforeSend: () =>
|
||||
{
|
||||
updatePerfInfo(options, 'requestBegin');
|
||||
if(!isDebugRequest) return;
|
||||
toggleLoading();
|
||||
},
|
||||
success: (data) =>
|
||||
{
|
||||
try{data = JSON.parse(data);}catch(e){data = [{name: 'html', data: data}];}
|
||||
updatePerfInfo(options, 'requestEnd');
|
||||
options.result = 'success';
|
||||
try{data = JSON.parse(data);}catch(e){data = [{name: data.includes('Fatal error') ? 'fatal' : 'html', data: data}];}
|
||||
if(options.updateUrl !== false) currentAppUrl = url;
|
||||
data.forEach((item, idx) => item.selector = selectors[idx]);
|
||||
renderPage(data);
|
||||
updatePerfInfo(options, 'renderBegin');
|
||||
renderPage(data, options);
|
||||
updatePerfInfo(options, 'renderEnd');
|
||||
$(document).trigger('pagerender.app');
|
||||
if(options.success) options.success(data);
|
||||
if(onFinish) onFinish(null, data);
|
||||
},
|
||||
error: (xhr, type, error) =>
|
||||
{
|
||||
updatePerfInfo(options, 'requestEnd', error);
|
||||
if(type === 'abort') return console.log('[ZIN] ', 'Abord fetch data from ' + url, {xhr, type, error});;
|
||||
if(DEBUG) console.error('[ZIN] ', 'Fetch data failed from ' + url, {xhr, type, error});
|
||||
zui.Messager.show('ZIN: Fetch data failed from ' + url);
|
||||
@@ -262,7 +352,6 @@
|
||||
if(!selector)
|
||||
{
|
||||
selector = ($('#main').length ? '#main>*,#pageCSS>*,#pageJS,#configJS>*,title>*,activeMenu()' : 'body>*,title>*');
|
||||
if(DEBUG) selector += ',zinErrors()';
|
||||
}
|
||||
fetchContent(url, selector, id);
|
||||
}
|
||||
@@ -426,7 +515,10 @@
|
||||
|
||||
$(() =>
|
||||
{
|
||||
initZinbar();
|
||||
|
||||
if(window.defaultAppUrl) loadPage(window.defaultAppUrl);
|
||||
else if(DEBUG) loadCurrentPage('zinErrors()');
|
||||
|
||||
DEBUG = window.config.debug;
|
||||
|
||||
|
||||
@@ -123,6 +123,8 @@ class dom
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
if(empty($this->selectors) && !empty($this->dataCommands)) return [];
|
||||
|
||||
$list = [];
|
||||
$children = $this->renderInner ? $this->wg->children() : $this->children;
|
||||
|
||||
@@ -149,9 +151,7 @@ class dom
|
||||
|
||||
public function renderJson()
|
||||
{
|
||||
$list = $this->build();
|
||||
if(empty($list)) return '{}';
|
||||
|
||||
$list = $this->build();
|
||||
$output = [];
|
||||
foreach($list as $name => $item)
|
||||
{
|
||||
@@ -188,9 +188,7 @@ class dom
|
||||
|
||||
public function renderList()
|
||||
{
|
||||
$list = $this->build();
|
||||
if(empty($list)) return '[]';
|
||||
|
||||
$list = $this->build();
|
||||
$output = [];
|
||||
foreach($list as $name => $item)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user