From 4d7f5e886cd50965d643325e2948e17978b28a37 Mon Sep 17 00:00:00 2001 From: sunhao Date: Wed, 26 Apr 2023 14:10:38 +0800 Subject: [PATCH 1/4] * zin: support to fetch content only commands. --- framework/base/router.class.php | 4 ++-- zin/core/dom.class.php | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index edf5628f5a..fda6aeb75c 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -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 { diff --git a/zin/core/dom.class.php b/zin/core/dom.class.php index 4d13506cab..3949c5f162 100644 --- a/zin/core/dom.class.php +++ b/zin/core/dom.class.php @@ -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) { From 577f96adf7ff09c8b70991fd3260f17b7fe70662 Mon Sep 17 00:00:00 2001 From: sunhao Date: Wed, 26 Apr 2023 18:48:00 +0800 Subject: [PATCH 2/4] * zin: add debug bar to zin page in debug mode --- www/js/zui3/zin.js | 118 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/www/js/zui3/zin.js b/www/js/zui3/zin.js index 625ef3aaeb..cf9d3640c1 100644 --- a/www/js/zui3/zin.js +++ b/www/js/zui3/zin.js @@ -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($(`
Fatal error: ${options.url}
`).append($('
').append(data))); + } + + function initZinbar() + { + if(!DEBUG) return; + let $bar = $('#zinbar'); + if($bar.length) return; + + $bar = $( + [ + '
', + '
', + '
', + '
', + '
', + '
' + ].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(`
${options.id === 'page' ? 'PAGE' : 'PARTIAL'}
`).append($('
loading...
')).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(`${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($('
rendering...
')).attr('title', `Renderring ${options.id}`); + } + else if(stage === 'renderEnd') + { + const renderTime = options.renderEnd - options.renderBegin; + $perf.find('.zin-perf-render').addClass('font-bold').html(` ${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'); + $(`
${phpErrType} ${error.message}
vim +${error.line} ${error.file}
`).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(`ERRO ${errors}`); + if(warnings) $count.append(`WARN ${warnings}`); + if(infos) $count.append(`INFO ${infos}`); + + 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; From 7f920398c941ebb4e16d73063a16bb18af113df6 Mon Sep 17 00:00:00 2001 From: zhouxin Date: Wed, 26 Apr 2023 10:49:14 +0000 Subject: [PATCH 3/4] + Add unit test for computeTaskProgress and computeTasksProgress. --- module/task/test/tao/computetaskprogress.php | 33 +++++++++++++++++++ module/task/test/tao/computetasksprogress.php | 32 ++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100755 module/task/test/tao/computetaskprogress.php create mode 100755 module/task/test/tao/computetasksprogress.php diff --git a/module/task/test/tao/computetaskprogress.php b/module/task/test/tao/computetaskprogress.php new file mode 100755 index 0000000000..d5b6d9966a --- /dev/null +++ b/module/task/test/tao/computetaskprogress.php @@ -0,0 +1,33 @@ +#!/usr/bin/env php +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的情况 diff --git a/module/task/test/tao/computetasksprogress.php b/module/task/test/tao/computetasksprogress.php new file mode 100755 index 0000000000..c06e714000 --- /dev/null +++ b/module/task/test/tao/computetasksprogress.php @@ -0,0 +1,32 @@ +#!/usr/bin/env php +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的情况 From 369f51c182f76d7e6162ac00543a312c8b9964ff Mon Sep 17 00:00:00 2001 From: guofeilong Date: Wed, 26 Apr 2023 18:52:26 +0800 Subject: [PATCH 4/4] * Code for testcase project activate. --- module/project/test/model/activate.php | 34 ++++++++++++++++++++------ test/model/project/activate.php | 24 ------------------ 2 files changed, 27 insertions(+), 31 deletions(-) delete mode 100755 test/model/project/activate.php diff --git a/module/project/test/model/activate.php b/module/project/test/model/activate.php index 437fa349f5..366c2a505b 100755 --- a/module/project/test/model/activate.php +++ b/module/project/test/model/activate.php @@ -1,6 +1,6 @@ #!/usr/bin/env php 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的项目 diff --git a/test/model/project/activate.php b/test/model/project/activate.php deleted file mode 100755 index 455c4cef16..0000000000 --- a/test/model/project/activate.php +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env php -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的项目