需求背景:
// FIXME 增加需求实现率字段 $lang->report->projectStoryRate = $lang->projectCommon . ‘需求实现率汇总表’;
// FIXME 依据项目状态进行统计 需求实现情况。 细分为 进行中,已关闭 $lang->report->closedProject = ‘已关闭’ . $lang->projectCommon; $lang->report->processProject = ‘进行中’ . $lang->projectCommon;
// FIXME 增加报表种类超链接 $lang->reportList->project->lists[13] = $lang->projectCommon . ‘需求实现率汇总表|report|projectstoryrate’;
// FIXME 需求实现率字段 $lang->report->storyRate = ‘需求实现率’;
<a name="nCRYv"></a>## controler- 同样,按照规则,扩展目录为 module/report/ext/control/projectstoryrate.php- 此时名称不可任意命名 control 类名对应着 所渲染的视图名称,即,control 类名 是与 所渲染的前端页面名称是一致的,见下图- ```php<?php/*** Create a bug.** @param int $productID* @param string $branch* @param string $extras others params, forexample, projectID=10,moduleID=10* @access public* @return void*/include '../../control.php'; // 将 report模块下的 原生的control类引入class myReport extends report // 采用继承拓展的方式来对control下的方法进行拓展{/* 进行自定义方法补充 *//*** Product information report.** @access public* @return void*/public function projectStoryRate($status = '') // 名称任意,命名规则参照原control类采用驼峰命名法{// 字库读取$this->app->loadLang('project'); // 读取 project模块下的字库$this->app->loadLang('story'); // 读取 story 模块下的字库$this->view->title = $this->lang->report->projectStoryRate; // 字库取值渲染到页面 - title$this->view->position[] = $this->lang->report->projectStoryRate; // 控制左侧边栏选中效果。// 产品查询结果渲染到前端页面并用变量 products 进行保存,可直接在view文件中进行引用 查询ok$this->view->projects = $this->report->getStoryRate($status); //$this->module名->方法名 这种调用方式是直接调用模块下的某个方法$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); // getPairs 取得用户的key/value 数组$this->view->submenu = 'project'; // 传给view 文件中的submenu字段,组成侧边导航栏$this->view->status = $status; // 查询条件,由前端从传进来$this->display(); // 渲染到对应视图文件 即 projectstoryrate.html.php}}
model & sql编写
- 接前面control中调用方法,接着对model层进行扩展,同样按照规则进行拓展,
路径 module/report/ext/model/getStoryRate.php ```php <?php public function getStoryRate($status) { // 编写sql查询到所有的project,用于前端页面展示传值 // conditions 为project_status // 思路 ,按照 project_status 进行筛选展示结果。 /*
- 思路梳理
- 查到所有的项目信息 ok
- 查到所有项目的需求信息 ok
- 项目信息与需求信息进行合并成为一个数组,返回给前端。not yet */
$projects = $this->dao->select(‘‘)->from(TABLE_PROJECT) ->where(‘deleted’)->eq(0) ->beginIF($status)->andWhere(‘status’)->eq($status)->fi() ->fetchAll(‘id’); // 查询到项目关联的需求信息 主要是个数 / select from zt_story as story left join zt_projectstory zp on story.id = zp.story left join zt_project as project on project.id = zp.project where story.deleted = ‘0’ and project.name != ‘’ / // db中查询到结果 // 首先要在db中测试确认sql没有问题,之后转写成禅道php可以识别的方式,如下,下面贴出禅道官方的介绍 // https://devel.easycorp.cn/book/extension/intro-45.html $storys = $this->dao->select(‘story.id,story.status,project.name,project.id as project’)->from(TABLE_STORY)->alias(‘story’)
->leftJoin(TABLE_PROJECTSTORY)->alias('zp')->on('story.id = zp.story')->leftJoin(TABLE_PROJECT)->alias('project')->on('project.id = zp.project')->where('story.deleted')->eq(0)->andWhere('project.name')->ne('')
// ->andWhere(‘project.id’)->in($this->dao->select(‘id’)->from(TABLE_PROJECT))->fi()
->fetchAll();
foreach($storys as $story) { // 遍历拿到的story结果集,需要与每个项目进行关联, // 简单来说,是吧所有找到的需求,找到他的爸爸,然后组成一个数组,传给调用出渲染到前端视图进行处理
// 形如// projects- project (当前项目没有需求)- project- storys- project- storys
// 单个需求信息与项目信息进行对应赋值 // 需求 与 项目如何对应? foreach($projects as $project ){
if ($story->name == $project->name ){// 判断是否为同一项目,是,则添加到当前项目数组的story中// 此处需要多次进行debug以确定结果是否为// 利用项目id 与前面story 查询语句得到的结果中的项目id进行对应,组合数组$projects[$story->project]->storys[$story->id] = $story;}
} }
unset($projects[‘’]); // return $products; return $projects; // 传回调用处,也就是control层
}
<a name="9wIld"></a>## view 视图编写 ,缺失的js方法补充```php<?php include '../../../common/view/header.html.php';?><?php if(common::checkNotCN()):?><style>#conditions .col-xs { width: 126px; }</style><?php endif;?><div id='mainContent' class='main-row'><div class='side-col col-lg'><?php include 'blockreportlist.html.php';?></div><div class='main-col'><?php if(empty($projects)):?><div class="cell"><div class="table-empty-tip"><p><span class="text-muted"><?php echo $lang->error->noData;?></span></p></div></div><?php else:?><div class='cell'><div class='panel'><div class="panel-heading"><div class="panel-title"><div class="table-row" id='conditions'><div class="col-2"><?php echo $title;?></div><div class="col-xs text-right text-gray text-middle"><?php echo $lang->report->conditions?></div><div><div class='col-xs input-group'><span class='input-group-addon'><?php echo $lang->project->status;?></span><?php echo html::select('status', $lang->project->statusList, $status , "class='form-control chosen ' onchange='changeParams(this)'");?></div></div></div></div><nav class="panel-actions btn-toolbar"></nav></div><div data-ride='table'><table class='table table-condensed table-striped table-bordered table-fixed no-margin' id='productList'><thead><tr><!-- 项目名称--><th class='text-center w-200px'><?php echo $lang->project->name;?></th><!-- 团队--><th class='text-center w-200px'><?php echo $lang->project->teamname;?></th><!-- 责任人--><th class='text-center w-70px'><?php echo $lang->project->owner;?></th><!-- 项目所处阶段--><th class="text-center w-70px" ><?php echo $lang->project->projectStage;?></th><!-- 项目开始时间--><th class="text-center w-100px"><?php echo $lang->projectCommon . $lang->project->begin;?></th><!-- 项目结束时间--><th class="text-center w-100px"><?php echo $lang->projectCommon . $lang->project->end;?></th><!-- 需求状态为 草稿--><th class="text-center w-70px"><?php echo $lang->story->statusList['draft'];?></th><!-- 需求状态为 激活--><th class="text-center w-70px"><?php echo $lang->story->statusList['active'];?></th><!-- 需求状态为 已变更--><th class="text-center w-70px"><?php echo $lang->story->statusList['changed'];?></th><!-- 需求状态为 已关闭--><th class="text-center w-70px"><?php echo $lang->story->statusList['closed'];?></th><!-- 需求 总计个数--><th class="text-center w-70px"><?php echo $lang->report->total;?></th><!-- 需求 实现率 计算公式为 已关闭个数/总计个数--><th class="text-center w-200px"><?php echo $lang->report->storyRate;?></th></tr></thead><tbody><?php $color = false;?><!-- 初始化值的地方存在问题,要考虑重复赋值--><?php foreach($projects as $project):?><?php$closedCount = 0;$activeCount = 0;$changedCount = 0;$draftCount = 0;?><tr class="text-center"><!-- 项目名 同时创建a标签供点击跳转--><td class='text-center text-blue' title="<?php echo $project->name;?>" ><?php echo "<p>" . html::a($this->createLink('project', 'view', "project=$project->id"), $project->name) . "</p>";?></td><!-- 所属团队--><td class="<?php echo $class;?>"><?php echo $project->team;?></td><!-- 负责人--><td class="<?php echo $class;?>"><?php echo $project->PO;?></td><!-- 项目所属阶段--><td class="<?php echo $class;?>"><?php echo $lang->project->projectStageList[$project->projectStage];?></td><!-- 项目开始日期--><td class="<?php echo $class;?>"><?php echo $project->begin == '2030-01-01' ? $lang->productplan->future : $project->begin;?></td><!-- 项目截止日期--><td class="<?php echo $class;?>"><?php echo $project->end == '2030-01-01' ? $lang->productplan->future : $project->end;?></td><!-- 如果 产品计划不为空--><!-- 原报表为产品简报,需求与计划挂钩,--><?php foreach($project->storys as $story):?><!-- 需求个数统计 --><!-- switch 根据状态统计,count ++ 然后赋值 --><?phpswitch ($story->status) {case "closed":$closedCount++;break;case "active":$activeCount++;break;case "changed":$changedCount++;break;case "draft":$draftCount++;break;}?><?php endforeach;?><!-- 需求为草稿的个数 --><td class="<?php echo $class;?>"><?php echo $draftCount;?></td><!-- 需求状态为激活的个数--><td class="<?php echo $class;?>"><?php echo $activeCount;?></td><!-- 需求状态为已变更的个数--><td class="<?php echo $class;?>"><?php echo $changedCount;?></td><!-- 需求状态为已关闭的个数--><td class="<?php echo $class;?>"><?php echo $closedCount;?></td><!-- 需求 总个数统计--><td class="<?php echo $class;?>"><?php echo $draftCount + $activeCount + $changedCount + $closedCount;?></td><!-- 需要对 小数尾部进行处理--><td class="<?php echo $class;?>"><?php$total = $draftCount + $changedCount + $closedCount + $activeCount;// 合计不为0 则进入计算,为0直接返回输出0if ($total!=0) {$rate = ($closedCount / $total) * 10000;$rate = floor($rate) / 10000 * 100 . "%";echo $rate;}else{echo '0%';};?></td></tr><?php endforeach;?></tbody></table></div></div></div><?php endif;?></div></div><?php include '../../../common/view/footer.html.php';?>
- view 视图在编写的过程中想要添加一个检索条件,下贴出涉及检索部分的前端代码 以及需要的js代码
- 对应的前端页面组件为下图

<div class="panel-title"><div class="table-row" id='conditions'><div class="col-2"><?php echo $title;?></div><div class="col-xs text-right text-gray text-middle"><?php echo $lang->report->conditions?></div><div><div class='col-xs input-group'><span class='input-group-addon'><?php echo $lang->project->status;?></span><?php echo html::select('status', $lang->project->statusList, $status , "class='form-control chosen ' onchange='changeParams(this)'");?></div></div></div></div>
字段方面不再进行赘述,可以参考其他视图文件中对字库的引用,以及前几篇文章中的记录。
- 可以注意到 select 标签 中 有一个 js方法 onchange=’changeParams(this)’ ,调用了一个js方法,扩展路径如下
- module/report/ext/js/projectstoryrate/projectstoryrate.js
内容 ```javascript function changeParams(obj) {
var status = $(‘#conditions’).find(‘#status’).val();
var link = createLink('report', 'projectStoryRate','status=' + status);location.href=link;
}
```
- 可以看到上面的代码十分的简单,及时 取得 id 为 conditons 的区块,在区块中查询 id 为 status 的变量,并且取到值
- 重点是下一步的方法 ,createLink

- 点开源码可以看到 ,该方法为禅道自己封装的一个js方法,其主要功用是创建一个链接 诸如禅道页面上的各种跳转,其实底层大多是这个方法调用来生成的。其调用方式也比较直观 。
- 参数解释,
- report 为 model名,
- projectStoryRate 还记得这个名字么,是本篇扩展的 control类中方法名,
- status 则是需要传的参数。而status 则是前端页面进行下拉选框进行选择时发生更改的参数值。
- 调用链条: 前端用户选择状态-> onchange=’changeParams(this)’ 方法 -> createLink -> location.href
- 综上,完成根据状态进行不同情况的查询。
总结:本篇需要一定的二开经验基础,对文章有疑问可留言进行交流。
