您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

禅道二开 - 统计报表扩展

  • 需求背景:
    • 禅道开源版没有报表自定义功能,但是接测试负责人的需求,需要在项目维度下对各个项目的需求实现率进行统计掌握,方便构建模型以供后续工作进行参考

扩展效果

image.png

涉及修改的文件&思路

字库

    • 老规矩 按照禅道二开的拓展规则进行拓展
    • module/report/ext/lang/zh-cn/report_extend.php
    • report_extend.php 为扩展字库文件,名称任意。
<?php


// 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 = '需求实现率';

controler

    • 同样,按照规则,扩展目录为 module/report/ext/control/projectstoryrate.php
    • 此时名称不可任意命名 control 类名对应着 所渲染的视图名称,即,control 类名 是与 所渲染的前端页面名称是一致的,见下图
    • image.png
<?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
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层

}

 

view 视图编写 ,缺失的js方法补充

<?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 ++  然后赋值        -->
                                            <?php
                                            switch ($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直接返回输出0
                                            if ($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方法 οnchange='changeParams(this)' ,调用了一个js方法,扩展路径如下
    • module/report/ext/js/projectstoryrate/projectstoryrate.js
  • 内容
function changeParams(obj)
{

    var status  = $('#conditions').find('#status').val();



    var link = createLink('report', 'projectStoryRate','status=' + status);
    location.href=link;
}

  • 可以看到上面的代码十分的简单,及时 取得 id 为 conditons 的区块,在区块中查询 id 为 status 的变量,并且取到值
  • 重点是下一步的方法 ,createLink
  • image.png
  • 点开源码可以看到 ,该方法为禅道自己封装的一个js方法,其主要功用是创建一个链接 诸如禅道页面上的各种跳转,其实底层大多是这个方法调用来生成的。其调用方式也比较直观 。
  • 参数解释,
    • report 为 model名,
    • projectStoryRate 还记得这个名字么,是本篇扩展的 control类中方法名,
    • status 则是需要传的参数。而status 则是前端页面进行下拉选框进行选择时发生更改的参数值。
  • 调用链条: 前端用户选择状态-> οnchange='changeParams(this)' 方法 -> createLink -> location.href
  • 综上,完成根据状态进行不同情况的查询。

 

总结:本篇需要一定的二开经验基础,对文章有疑问可留言进行交流。


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进