• 需求背景

    • 禅道开源版本对线上问题处理流程支持不是很友好,有很多限制,公司决定投入人力进行二次开发。

      需求清单

  • Bug Status 扩容

    • 禅道原生只有 已激活,已解决,已关闭三种状态。无法满足线上问题扭转需求。
    • 扩容后为
      • active,resolved,closed,fixed,in_process,postpone
  • Bug 描述字段扩容
    • 为了满足线上问题的需求,新增字段如下图· ``sql -- 新增bug描述字段 2021.2.1 -- ALTER tablezt_bug` ADD COLUMN /**/ — ‘customerPackage’ = ‘客户产品包’; — ‘customer’ = ‘客户’; — ‘env’ = ‘环境’; — ‘issueTime’ = ‘问题所属时间’; — ‘founded’ = ‘发现问题方’; — ‘issueModule’ = ‘问题fix所属模块’; — ‘issueApp’ = ‘问题所属数栈应用’; — ‘issueAppModule’ = ‘产品包所属应用模块’; — ‘engine’ = ‘计算引擎’; — ‘version’ = ‘目标版本’;
  1. <a name="am7jn"></a>
  2. # 二开思路 & 线上问题字段扩容支持
  3. <a name="a1c5a"></a>
  4. ## DB 方面
  5. - -> 找到 维护bug信息的表,在禅道中为 zt_bug 。观察 status字段,发现其为enum,即枚举格式,需要对其进行修改,就是需要增加枚举的值。
  6. ```sql
  7. -- 测试环境集成 增量sql 2021.1.29
  8. ALTER TABLE `zt_bug`ADD
  9. (
  10. `customerPackage` varchar(50) default null comment '客户产品包',
  11. `customer` varchar(50) default null comment '客户',
  12. `env` varchar(50) default null comment 'env',
  13. `mergeTime` date default null comment '问题所属时间',
  14. `founded` varchar(50) default null comment '发现问题方',
  15. `issueModule` varchar(50) default null comment '问题fix所属模块',
  16. `issueApp` varchar(50) default null comment '问题所属数栈应用',
  17. `issueAppModule` varchar(50) default null comment '产品包所属应用模块',
  18. `engine` varchar(50) default null comment '计算引擎',
  19. `version` varchar(50) default null comment '目标版本'
  20. );

后端方面

  • 首先要根据url访问路径大致确认要修改的代码在源代码的 module所处位置。
  • 直接看实例
  • image.pnghttp://localhost:8888/www/bug-create-3-0-moduleID=0.html
  • 这样一个链接,首先可以确定到 module 为 bug,涉及到的视图文件名为 create 。

    找到模块

  • 进入到源码目录下,找bug module

  • image.png
  • 目录解释

    • bug - > module 名
    • css - > 层叠样式表文件
    • js -> js方法代码
    • lang -> 该模块主要使用的字段存放
    • view -> 该模块的视图文件存放
    • model -> 该模块主要的后端方法存放

      前端方面

      找到视图文件

      image.png
  • 在视图文件中去寻找自己要修改的目标字段,因为本例中为添加新的字段,因此是没有类似的字段,但是可以参考源码中已经有的字段的写法。下见图

  • image.png
  • 可以看到 os ,browse 字段的编写模式,按照源码的方式增加自定义的字段。
  • $customerList 为下拉框的值展示,其支持存在于 $lang->customerList 中,需要事前维护,或者是联用custom 模块对其字段进行自定义维护,后面涉及到custom模块的改造的时候会详细解释

    修改字库

  • 找到bug字段对应的字库,对其进行拓展,要注意的是

    • 需要有单数形式的字库支持,同时也要定义 单数字库名List 的数组,来支持下拉框框选

image.png
image.png

修改方法

  • 创建bug , 即create方法。
  • 找到方法之后,就需要研读代码。读懂代码最好的方式是打断点进行debug,一个bug D上一天,虽愚也会有所闻的。
  • 按照 规定 在 zentaopms/module/bug/ext/model/create.php 下进行修改,没有的话请创建,这是前面几张文章提到的开发规范

    1. <?php
    2. /**
    3. * Create a bug.
    4. * 对 bug module 下的创建方法进行扩展
    5. *
    6. * @param string $from object that is transfered to bug.
    7. * @access public
    8. * @return int|bool
    9. */
    10. public function create($from = '')
    11. {
    12. $now = helper::now();
    13. $bug = fixer::input('post')
    14. // 取到当前登陆用户account信息
    15. ->setDefault('openedBy', $this->app->user->account)
    16. // 取当前时间为 bug创建时间
    17. ->setDefault('openedDate', $now)
    18. // 项目,需求,任务 默认值设置为0
    19. ->setDefault('project,story,task', 0)
    20. // openedBuild
    21. ->setDefault('openedBuild', '')
    22. // 默认截止时间
    23. ->setDefault('deadline', '0000-00-00')
    24. ->setIF(strpos($this->config->bug->create->requiredFields, 'deadline') !== false, 'deadline', $this->post->deadline)
    25. ->setIF($this->post->assignedTo != '', 'assignedDate', $now)
    26. ->setIF($this->post->story != false, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story))
    27. ->setIF(strpos($this->config->bug->create->requiredFields, 'project') !== false, 'project', $this->post->project)
    28. /* FIXME 新增线上问题支持字段
    29. $lang->bug->customer = '客户';
    30. $lang->bug->customerPackage = '客户产品包';
    31. $lang->bug->env = '环境';
    32. $lang->bug->mergeTime = '代码合并时间';
    33. $lang->bug->founded = '发现问题方';
    34. $lang->bug->issueModule = '问题fix所属模块';
    35. $lang->bug->issueAppModule = '产品包所属应用模块';
    36. $lang->bug->issueApp = '所属数栈应用';
    37. $lang->bug->engine = '计算引擎';
    38. $lang->bug->version = '目标版本';
    39. */
    40. // 如果不为空,则进行赋值
    41. ->setIF($this->post->customer != '','customer',$this->post->customer)
    42. ->setIF($this->post->customerPackage != '','customerPackage',$this->post->customerPackage)
    43. ->setIF($this->post->env != '','env',$this->post->env)
    44. ->setIF($this->post->mergeTime != '','mergeTime',$this->post->mergeTime)
    45. ->setIF($this->post->founded != '','founded',$this->post->founded)
    46. ->setIF($this->post->issueModule != '','issueModule',$this->post->issueModule)
    47. ->setIF($this->post->issueAppModule != '','issueAppModule',$this->post->issueAppModule)
    48. ->setIF($this->post->issueApp != '','issueApp',$this->post->issueApp)
    49. ->setIF($this->post->engine != '','engine',$this->post->engine)
    50. ->setIF($this->post->version != '','version',$this->post->version)
    51. // ------end --------
    52. ->stripTags($this->config->bug->editor->create['id'], $this->config->allowedTags)
    53. ->cleanInt('product,project,module,severity')
    54. ->join('openedBuild', ',')
    55. ->join('mailto', ',')
    56. ->remove('files, labels,uid,oldTaskID,contactListMenu')
    57. ->get();
    58. /* Check repeat bug. */
    59. $result = $this->loadModel('common')->removeDuplicate('bug', $bug, "product={$bug->product}");
    60. if($result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
    61. $bug = $this->loadModel('file')->processImgURL($bug, $this->config->bug->editor->create['id'], $this->post->uid);
    62. $this->dao->insert(TABLE_BUG)->data($bug)->autoCheck()->batchCheck($this->config->bug->create->requiredFields, 'notempty')->exec();
    63. if(!dao::isError())
    64. {
    65. $bugID = $this->dao->lastInsertID();
    66. $this->file->updateObjectID($this->post->uid, $bugID, 'bug');
    67. $this->file->saveUpload('bug', $bugID);
    68. empty($bug->case) ? $this->loadModel('score')->create('bug', 'create', $bugID) : $this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
    69. /* Callback the callable method to process the related data for object that is transfered to bug. */
    70. if($from && is_callable(array($this, $this->config->bug->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->bug->fromObjects[$from]['callback']), $bugID);
    71. return array('status' => 'created', 'id' => $bugID);
    72. }
    73. return false;
    74. }
  • 上面的代码是已经修改了的代码,其中 this->post->customer 等值是前端页面发送回来的 bug 线上字段 相关信息,因为php的语法有些特殊,所以看起来有些奇怪。

  • 同时需要注意的时,由前端发送回的信息,需要与create中后端方法接收的变量一直,否则会出现问题。

    衍生需求

    在去对线上问题字段进行支持的时候,发现很多原生字段支持,但是新增字段未进行支持的字段。大概涉及下面几种,

  • Bug 详情页展示,展示包含线上问题字段。

  • Bug 搜索,支持对线上问题字段信息进行检索。
  • Custom 模块对 线上问题相关字段的维护,增删查改。
  • Bug 导出功能 对线上问题相关字段的导出支持。
  • 表单自定义显示字段支持线上问题。

    Bug 详情页展示,包含线上问题字段

  • image.png

  • 上图是已经开发完成的效果,来理顺一下开发流程与思路。

    根据Url找到对应源码

  • bug-view bug模块下的view视图文件。

    观察原生代码实现,tab点击切换原理

    image.png

  • 上图中为 视图中的tabimage.png分析之后发现点击之后跳转至 href后的id,对应表单在下方进行了定义。

  • image.png
  • 去字库中查询对应信息。

image.png

  • 对字库信息进行拓展,这里因为两个视图中都用到此字段,于是一个字段设置解决
  • image.png

    模仿原生的写法,添加线上问题的tab,并进行显示

    1. <!-- FIXME 线上问题 tab -->
    2. <?php if ($config->global->flow != 'onlyTest' and $productName == '线上问题统计'): ?>
    3. <div class='tab-pane' id='onlineIssue'>
    4. <table class='table table-data'>
    5. <tbody class="text-center">
    6. <!-- 需要展示的字段
    7. $lang->bug->customer = '客户';
    8. $lang->bug->customerPackage = '客户产品包';
    9. $lang->bug->env = '环境';
    10. $lang->bug->mergeTime = '代码合并时间';
    11. $lang->bug->founded = '发现问题方';
    12. $lang->bug->issueModule = '问题fix所属模块';
    13. $lang->bug->issueAppModule = '产品包所属应用模块';
    14. $lang->bug->issueApp = '所属数栈应用';
    15. $lang->bug->engine = '计算引擎';
    16. $lang->bug->version = '目标版本';
    17. -->
    18. <tr>
    19. <th class='w-120px '><?php echo $lang->bug->customer; ?></th>
    20. 这里调用的是字库中的信息,$bug对象中存放的customer 之类的信息全部都是key,需要去字库的list中key value对应取值
    21. <td><?php if (isset($lang->bug->customerList[$bug->customer])) echo $lang->bug->customerList[$bug->customer]; else echo $bug->customer; ?></td>
    22. </tr>
    23. <tr>
    24. <th class='w-120px'><?php echo $lang->bug->customerPackage; ?></th>
    25. <td><?php if (isset($lang->bug->customerPackageList[$bug->customerPackage])) echo $lang->bug->customerPackageList[$bug->customerPackage]; else echo $bug->customerPackage; ?></td>
    26. </tr>
    27. <tr>
    28. <th class='w-120px'><?php echo $lang->bug->env; ?></th>
    29. <td><?php if (isset($lang->bug->envList[$bug->env])) echo $lang->bug->envList[$bug->env]; else echo $bug->env; ?></td>
    30. </tr>
    31. <tr>
    32. <th class='w-120px'><?php echo $lang->bug->mergeTime; ?></th>
    33. <td><?php echo $bug->mergeTime ?></td>
    34. </tr>
    35. <tr>
    36. <th class='w-120px'><?php echo $lang->bug->founded; ?></th>
    37. <td><?php if (isset($lang->bug->foundedList[$bug->founded])) echo $lang->bug->foundedList[$bug->founded]; else echo $bug->founded; ?></td>
    38. </tr>
    39. <tr>
    40. <th class='w-120px'><?php echo $lang->bug->issueModule; ?></th>
    41. <td><?php if (isset($lang->bug->issueModuleList[$bug->issueModule])) echo $lang->bug->issueModuleList[$bug->issueModule]; else echo $bug->issueModule; ?></td>
    42. </tr>
    43. <tr>
    44. <th class='w-140px'><?php echo $lang->bug->issueAppModule; ?></th>
    45. <td><?php if (isset($lang->bug->issueAppModuleList[$bug->issueAppModule])) echo $lang->bug->issueAppModuleList[$bug->issueAppModule]; else echo $bug->issueAppModule; ?></td>
    46. </tr>
    47. <tr>
    48. <th class='w-120px'><?php echo $lang->bug->issueApp; ?></th>
    49. <td><?php if (isset($lang->bug->issueAppList[$bug->issueApp])) echo $lang->bug->issueAppList[$bug->issueApp]; else echo $bug->issueApp; ?></td>
    50. </tr>
    51. <tr>
    52. <th class='w-120px'><?php echo $lang->bug->engine; ?></th>
    53. <td><?php if (isset($lang->bug->engineList[$bug->engine])) echo $lang->bug->engineList[$bug->engine]; else echo $bug->engine; ?></td>
    54. </tr>
    55. <tr>
    56. <th class='w-120px'><?php echo $lang->bug->version; ?></th>
    57. <td><?php if (isset($lang->bug->versionList[$bug->version])) echo $lang->bug->versionList[$bug->version]; else echo $bug->version; ?></td>
    58. </tr>
    59. </tbody>
    60. </table>
    61. </div>
    62. <?php endif; ?>
    63. <!-- FIXME 线上问题 tab end -->

    Bug 搜索,支持对线上问题字段信息进行检索

    image.png

  • 同上面所叙述,原生字段支持,而新增字段不支持,需要对其进行修改。

    确定要修改的文件

  • 浏览 config 文件下发现search相关字段

  • image.png
  • 对其进行拓展,以支持新增线上字段 ```php <?php // TODO global lang 搜索字段在线添加,以支持搜索字段 $config->bug->search[‘fields’][‘status’] = $lang->bug->status; $config->bug->search[‘fields’][‘confirmed’] = $lang->bug->confirmed; $config->bug->search[‘fields’][‘customer’] = $lang->bug->customer; $config->bug->search[‘fields’][‘customerPackage’] = $lang->bug->customerPackage; $config->bug->search[‘fields’][‘env’] = $lang->bug->env; $config->bug->search[‘fields’][‘founded’] = $lang->bug->founded; $config->bug->search[‘fields’][‘issueModule’] = $lang->bug->issueModule; $config->bug->search[‘fields’][‘issueAppModule’] = $lang->bug->issueAppModule; $config->bug->search[‘fields’][‘issueApp’] = $lang->bug->issueApp; $config->bug->search[‘fields’][‘engine’] = $lang->bug->engine; $config->bug->search[‘fields’][‘version’] = $lang->bug->version;

// 字段后的搜索显示为下拉框 $config->bug->search[‘params’][‘customer’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->customerList); $config->bug->search[‘params’][‘customerPackage’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->customerPackageList); $config->bug->search[‘params’][‘env’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->envList); $config->bug->search[‘params’][‘founded’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->foundedList); $config->bug->search[‘params’][‘issueModule’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->issueModuleList); $config->bug->search[‘params’][‘issueAppModule’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->issueAppModuleList); $config->bug->search[‘params’][‘issueApp’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->issueAppList); $config->bug->search[‘params’][‘engine’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->engineList); $config->bug->search[‘params’][‘version’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->versionList);

  1. <a name="y5dlf"></a>
  2. ## Custom模块对线上问题相关字段维护,CRUD
  3. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1612495016757-8c886c8b-3fea-4337-83f6-ef4030204861.png#align=left&display=inline&height=772&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1544&originWidth=1876&size=805334&status=done&style=none&width=938)
  4. - 上图为已经修改之后的效果。
  5. <a name="4Zb6m"></a>
  6. ### 根据Url找到对应源码
  7. - custom 模块 set 视图,![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1612495492995-d5ac0394-9747-4f5b-b838-652b82d07b8a.png#align=left&display=inline&height=242&margin=%5Bobject%20Object%5D&name=image.png&originHeight=484&originWidth=319&size=104928&status=done&style=none&width=159.5)
  8. <a name="GXfQ2"></a>
  9. ### 对config进行拓展
  10. - config 文件下配置了canadd 也就是可以增加的字段配置
  11. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1612495601830-0be9b9c2-9188-44dd-a9a0-fea16e6ed69d.png#align=left&display=inline&height=774&margin=%5Bobject%20Object%5D&name=image.png&originHeight=774&originWidth=1051&size=839135&status=done&style=none&width=1051)
  12. - 对其进行拓展
  13. ```php
  14. <?php
  15. // FIXME 增加自定义页面下可以编辑添加的字段
  16. $config->custom->canAdd['bug'] = 'priList,severityList,osList,browserList,typeList,
  17. resolutionList,customerPackageList,customerList,envList,foundedList,issueModuleList,issueAppList,issueAppModuleList,engineList,versionList';
  18. // FIXME ============end-----------------------------------

对字库进行拓展

  1. <?php
  2. // FIXME 考虑redmine线上问题迁移过来需要控制的字段。
  3. $lang->custom->bug->fields['customerPackageList'] = '客户产品包';
  4. $lang->custom->bug->fields['customerList'] = '客户';
  5. $lang->custom->bug->fields['envList'] = '环境';
  6. $lang->custom->bug->fields['foundedList'] = '发现问题方';
  7. $lang->custom->bug->fields['issueModuleList'] = '问题fix所属模块';
  8. $lang->custom->bug->fields['issueAppList'] = '问题所属数栈应用';
  9. $lang->custom->bug->fields['issueAppModuleList'] = '产品包所属应用模块';
  10. $lang->custom->bug->fields['engineList'] = '计算引擎';
  11. $lang->custom->bug->fields['versionList'] = '目标版本';
  12. // --------------------------------------------

custom模块总结

  • 对 字段的维护,存放在 zt_lang 表中
  • image.png
  • 每次对字段进行自定义更新,都会存放在 zt_lang下,如果没有对其进行更新的话,默认取值为上面bug新增字段中定义的customerList中的值
  • image.png
  • 添加 custom 自定义支持之后,对客户,环境,发现问题方等字段可以通过页面ui进行维护

    Bug 导出功能,对线上问题相关字段进行支持

  • 直接通过查看前端源码拿到url

  • image.png

    分析Url 找到源码

  • bug 模块下 export 视图

  • image.png

    没有线索,延伸查找control层

    image.png

  • 发现目标。模仿源码进行扩展修改。

  • image.png

    config模块下扩展输出字段控制

    image.png

    注意点

    值得一提的是,bug字段信息在禅道中的流转都是按照key来存储的,值是存放在 lang 或者是 zt_lang扩展的字段下,所以不按照数组取值的方式去输出,或者进行前端展示的话,展示出来的都只是key。而非值

  • 注意:开源版的功能中导出Bug不能导出相关截图,留作日后研发课题。

    Bug表单自定义显示支持线上字段

    image.png

    分析调用

    image.png

    找到源码

  • datatable 模块 ajaxCustom 视图

  • image.png
  • 顺着调用去找到module层下面的 getFieldList方法
  • image.png
  • 查看到该方法为 module 为参数,返回为读取 config->module->datatable->fieldList

    查找bug模块下config下面是否有相关配置信息

    image.png

  • 对其进行拓展,然后测试

  • image.png

    测试结果

    image.png

    但是经此修改之后,有列显示,但没有值

  • 继续进行debug,发现是bug 模块下的 printcell方法控制了值的显示,对其进行扩展修改之后正常显示内容。 ```php <?php /**

    • Print cell data. *
    • @param object $col
    • @param object $bug
    • @param array $users
    • @param array $builds
    • @param array $branches
    • @param array $modulePairs
    • @param array $projects
    • @param array $plans
    • @param array $stories
    • @param array $tasks
    • @param string $mode
    • @access public
    • @return void */ public function printCell($col, $bug, $users, $builds, $branches, $modulePairs, $projects = array(), $plans = array(), $stories = array(), $tasks = array(), $mode = ‘datatable’) { $canBatchEdit = common::hasPriv(‘bug’, ‘batchEdit’); $canBatchConfirm = common::hasPriv(‘bug’, ‘batchConfirm’); $canBatchClose = common::hasPriv(‘bug’, ‘batchClose’); $canBatchActivate = common::hasPriv(‘bug’, ‘batchActivate’); $canBatchChangeBranch = common::hasPriv(‘bug’, ‘batchChangeBranch’); $canBatchChangeModule = common::hasPriv(‘bug’, ‘batchChangeModule’); $canBatchResolve = common::hasPriv(‘bug’, ‘batchResolve’); $canBatchAssignTo = common::hasPriv(‘bug’, ‘batchAssignTo’);

      $canBatchAction = $canBatchEdit or $canBatchConfirm or $canBatchClose or $canBatchActivate or $canBatchChangeBranch or $canBatchChangeModule or $canBatchResolve or $canBatchAssignTo;

      $canView = common::hasPriv(‘bug’, ‘view’);

      $hasCustomSeverity = false; foreach ($this->lang->bug->severityList as $severityKey => $severityValue) {

      1. if (!empty($severityKey) and (string)$severityKey != (string)$severityValue) {
      2. $hasCustomSeverity = true;
      3. break;
      4. }

      }

      $bugLink = inlink(‘view’, “bugID=$bug->id”); $account = $this->app->user->account; $id = $col->id; if ($col->show) {

      1. $class = "c-$id";
      2. $title = '';
      3. if ($id == 'id') $class .= ' cell-id';
      4. if ($id == 'status') {
      5. $class .= ' bug-' . $bug->status;
      6. $title = "title='" . $this->processStatus('bug', $bug) . "'";
      7. }
      8. if ($id == 'confirmed') {
      9. $class .= ' text-center';
      10. }
      11. if ($id == 'title') {
      12. $class .= ' text-left';
      13. $title = "title='{$bug->title}'";
      14. }
      15. if ($id == 'type') {
      16. $title = "title='" . zget($this->lang->bug->typeList, $bug->type) . "'";
      17. }
      18. if ($id == 'assignedTo') {
      19. $class .= ' has-btn text-left';
      20. if ($bug->assignedTo == $account) $class .= ' red';
      21. }
      22. if ($id == 'deadline' && isset($bug->delay)) $class .= ' delayed';
      23. if (strpos(',project,story,plan,task,openedBuild,', ",{$id},") !== false) $class .= ' text-ellipsis';
      24. echo "<td class='" . $class . "' $title>";
      25. if (isset($this->config->bizVersion)) $this->loadModel('flow')->printFlowCell('bug', $bug, $id);
      26. switch ($id) {
      27. case 'id':
      28. if ($canBatchAction) {
      29. echo html::checkbox('bugIDList', array($bug->id => '')) . html::a(helper::createLink('bug', 'view', "bugID=$bug->id"), sprintf('%03d', $bug->id));
      30. } else {
      31. printf('%03d', $bug->id);
      32. }
      33. break;
      34. case 'severity':
      35. if ($hasCustomSeverity) {
      36. echo "<span class='label-severity-custom' data-severity='{$bug->severity}' title='" . zget($this->lang->bug->severityList, $bug->severity) . "'>" . zget($this->lang->bug->severityList, $bug->severity) . "</span>";
      37. } else {
      38. echo "<span class='label-severity' data-severity='{$bug->severity}' title='" . zget($this->lang->bug->severityList, $bug->severity) . "'></span>";
      39. }
      40. break;
      41. case 'pri':
      42. echo "<span class='label-pri label-pri-" . $bug->pri . "' title='" . zget($this->lang->bug->priList, $bug->pri, $bug->pri) . "'>";
      43. echo zget($this->lang->bug->priList, $bug->pri, $bug->pri);
      44. echo "</span>";
      45. break;
      46. case 'confirmed':
      47. $class = 'confirm' . $bug->confirmed;
      48. echo "<span class='$class'>" . zget($this->lang->bug->confirmedList, $bug->confirmed, $bug->confirmed) . "</span> ";
      49. break;
      50. case 'title':
      51. if ($bug->branch and isset($branches[$bug->branch])) echo "<span class='label label-outline label-badge'>{$branches[$bug->branch]}</span> ";
      52. if ($bug->module and isset($modulePairs[$bug->module])) echo "<span class='label label-gray label-badge'>{$modulePairs[$bug->module]}</span> ";
      53. echo $canView ? html::a($bugLink, $bug->title, null, "style='color: $bug->color'") : "<span style='color: $bug->color'>{$bug->title}</span>";
      54. break;
      55. case 'branch':
      56. echo zget($branches, $bug->branch, '');
      57. break;
      58. case 'project':
      59. echo zget($projects, $bug->project, '');
      60. break;
      61. case 'plan':
      62. echo zget($plans, $bug->plan, '');
      63. break;
      64. case 'story':
      65. if (isset($stories[$bug->story])) {
      66. $story = $stories[$bug->story];
      67. echo common::hasPriv('story', 'view') ? html::a(helper::createLink('story', 'view', "storyID=$story->id", 'html', true), $story->title, '', "class='iframe'") : $story->title;
      68. }
      69. break;
      70. case 'task':
      71. if (isset($tasks[$bug->task])) {
      72. $task = $tasks[$bug->task];
      73. echo common::hasPriv('task', 'view') ? html::a(helper::createLink('task', 'view', "taskID=$task->id", 'html', true), $task->name, '', "class='iframe'") : $task->name;
      74. }
      75. break;
      76. case 'type':
      77. echo zget($this->lang->bug->typeList, $bug->type);
      78. break;
      79. case 'status':
      80. echo "<span class='status-bug status-{$bug->status}'>";
      81. echo $this->processStatus('bug', $bug);
      82. echo '</span>';
      83. break;
      84. case 'activatedCount':
      85. echo $bug->activatedCount;
      86. break;
      87. case 'activatedDate':
      88. echo substr($bug->activatedDate, 5, 11);
      89. break;
      90. case 'keywords':
      91. echo $bug->keywords;
      92. break;
      93. case 'os':
      94. echo zget($this->lang->bug->osList, $bug->os);
      95. break;
      96. case 'browser':
      97. echo zget($this->lang->bug->browserList, $bug->browser);
      98. break;
      99. case 'customer':
      100. echo zget($this->lang->bug->customerList, $bug->customer);
      101. break;
      102. case 'customerPackage':
      103. echo zget($this->lang->bug->customerPackageList, $bug->customerPackage);
      104. break;
      105. case 'env':
      106. echo zget($this->lang->bug->envList, $bug->env);
      107. break;
      108. case 'founded':
      109. echo zget($this->lang->bug->foundedList, $bug->founded);
      110. break;
      111. case 'issueModule':
      112. echo zget($this->lang->bug->issueModuleList, $bug->issueModule);
      113. break;
      114. case 'issueAppModule':
      115. echo zget($this->lang->bug->issueAppModuleList, $bug->issueAppModule);
      116. break;
      117. case 'issueApp':
      118. echo zget($this->lang->bug->issueAppList, $bug->issueApp);
      119. break;
      120. case 'engine':
      121. echo zget($this->lang->bug->engineList, $bug->engine);
      122. break;
      123. case 'version':
      124. echo zget($this->lang->bug->versionList, $bug->version);
      125. break;
      126. case 'mergeTime':
      127. echo $bug->mergeTime;
      128. break;
      129. case 'mailto':
      130. $mailto = explode(',', $bug->mailto);
      131. foreach ($mailto as $account) {
      132. $account = trim($account);
      133. if (empty($account)) continue;
      134. echo zget($users, $account) . " &nbsp;";
      135. }
      136. break;
      137. case 'found':
      138. echo zget($users, $bug->found);
      139. break;
      140. case 'openedBy':
      141. echo zget($users, $bug->openedBy);
      142. break;
      143. case 'openedDate':
      144. echo substr($bug->openedDate, 5, 11);
      145. break;
      146. case 'openedBuild':
      147. $builds = array_flip($builds);
      148. foreach (explode(',', $bug->openedBuild) as $build) {
      149. $buildID = zget($builds, $build, '');
      150. if ($buildID == 'trunk') {
      151. echo $build;
      152. } elseif ($buildID and common::hasPriv('build', 'view')) {
      153. echo html::a(helper::createLink('build', 'view', "buildID=$buildID"), $build, '', "title='$bug->openedBuild'");
      154. } else {
      155. echo $build;
      156. }
      157. }
      158. break;
      159. case 'assignedTo':
      160. $this->printAssignedHtml($bug, $users);
      161. break;
      162. case 'assignedDate':
      163. echo substr($bug->assignedDate, 5, 11);
      164. break;
      165. case 'deadline':
      166. echo $bug->deadline;
      167. break;
      168. case 'resolvedBy':
      169. echo zget($users, $bug->resolvedBy, $bug->resolvedBy);
      170. break;
      171. case 'resolution':
      172. echo zget($this->lang->bug->resolutionList, $bug->resolution);
      173. break;
      174. case 'resolvedDate':
      175. echo substr($bug->resolvedDate, 5, 11);
      176. break;
      177. case 'resolvedBuild':
      178. echo $bug->resolvedBuild;
      179. break;
      180. case 'closedBy':
      181. echo zget($users, $bug->closedBy);
      182. break;
      183. case 'closedDate':
      184. echo substr($bug->closedDate, 5, 11);
      185. break;
      186. case 'lastEditedBy':
      187. echo zget($users, $bug->lastEditedBy);
      188. break;
      189. case 'lastEditedDate':
      190. echo substr($bug->lastEditedDate, 5, 11);
      191. break;
      192. case 'actions':
      193. $params = "bugID=$bug->id";
      194. common::printIcon('bug', 'confirmBug', $params, $bug, 'list', 'confirm', '', 'iframe', true);
      195. common::printIcon('bug', 'resolve', $params, $bug, 'list', 'checked', '', 'iframe', true);
      196. common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true);
      197. common::printIcon('bug', 'edit', $params, $bug, 'list');
      198. common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=$params", $bug, 'list', 'copy');
      199. break;
      200. }
      201. echo '</td>';

      } }

```

本期未实现的衍生需求

  • 批量编辑bug,批量创建bug 支持线上问题字段
  • 报表功能中,对线上问题字段支持统计。

    思路

  • 从思路上来讲

    • 前端发送给后端的数据
    • 前端需要进行展示的字段支持 — 也就是模块下的lang文件。
    • 前端需要进行控制的字段,涉及到php控制的一些方法,这些在后续文章中陆续分享。
    • 本篇中涉及到前端的部分比较简单,所以略述,详细的爬坑记录等待下次

      涉及修改文件总结

  • 前端 module/bug/ext/view/create.html.php

  • 后端 module/bug/ext/model/create.php
  • 字库 module/bug/lang/zh-cn.php