Zentao Utils

7/27/2021, 4:53:18 PM

  1. // ==UserScript==
  2. // @name Zentao Utils
  3. // @namespace Violentmonkey Scripts
  4. // @match http://zentao.inovance.com/pro/*
  5. // @match http://10.44.244.63/pro/*
  6. // @icon https://blog.sbw.so/favicon.ico
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @version 1.10
  10. // @license MIT
  11. // @author sbw <sbw@sbw.so>
  12. // @description 7/27/2021, 4:53:18 PM
  13. // ==/UserScript==
  14.  
  15.  
  16. (function() {
  17. // 显示 alert 对话框
  18. function show_alert(message)
  19. {
  20. bootAlert(message);
  21. }
  22. // 显示对话框
  23. function update_dialog(title, content)
  24. {
  25. $('#dialog .modal-title').html(title);
  26. $('#dialog .modal-body').html(content);
  27. }
  28. // 获取当前项目中的模块分类
  29. function update_module_on_dialog()
  30. {
  31. var url = "/tree-ajaxGetOptionMenu-149-bug-0-0-html--true.html";
  32. $.get(url, function(data, status){
  33. update_dialog('更新模块', data);
  34. // 清除事件
  35. $('.form-control').off('change');
  36. $('.form-control').removeAttr('onchange');
  37. });
  38. }
  39. // 更新任务所属模块
  40. function update_jobs_module(e)
  41. {
  42. // 禁止默认的响应
  43. e.preventDefault();
  44. update_module_on_dialog();
  45. // 绑定对话框确定按钮的事件
  46. $('#dialog .btn-primary').off('click');
  47. var basicInfoPanel = $('#legendBasicInfo');
  48. if (basicInfoPanel.length > 0) {
  49. $('#dialog .btn-primary').click(submit_jobs_module_inline);
  50. } else {
  51. $('#dialog .btn-primary').click(submit_jobs_module);
  52. }
  53. }
  54. function submit_modify_job(jobId, fun)
  55. {
  56. // 修改任务的地址
  57. var url = '/pro/bug-edit-' + jobId + '.html';
  58. $.get(url, function(data, status) {
  59. // 从任务修改界面获取初始数据集
  60. var resData = $.parseHTML(data, true);
  61. var formData = new FormData($(resData).find('form')[0]);
  62. fun(resData, formData);
  63.  
  64. // 发送数据
  65. $.ajax({
  66. type: 'post',
  67. url: url,
  68. data: formData,
  69. contentType: false,
  70. processData: false,
  71. success: function(data, status) {
  72. /*$('html').html(data);*/
  73. new $.zui.Messager('任务 ' + jobId + ' 更新完成', { icon: 'bell' }).show();
  74. // // 刷新当前页面
  75. // location.reload();
  76. }
  77. });
  78. });
  79. }
  80. function submit_jobs_module()
  81. {
  82. // 获取选择的数据
  83. var choose = $('#module').find(":selected").val();
  84. // 隐藏对话框
  85. $('#dialog').modal('hide');
  86. // 获取选中的任务列表
  87. $('#bugList tr').filter('.checked').each(function () {
  88. var id = $(this).find('.c-id input').val();
  89. var title = $(this).find('.text-left').attr('title');
  90. if (typeof(title) === "undefined") {
  91. title = $(this).find('.text-left > a').attr('title');
  92. }
  93. submit_modify_job(id, function(resData, formData) {
  94. // 修改所属模块
  95. formData.set('module', choose);
  96. });
  97. });
  98. }
  99. function submit_jobs_module_inline()
  100. {
  101. // 获取选择的数据
  102. var choose = $('#module').find(":selected").val();
  103. // 隐藏对话框
  104. $('#dialog').modal('hide');
  105. // 当前任务 id
  106. var id = $('.label-id').text();
  107. submit_modify_job(id, function(resData, formData) {
  108. // 修改所属模块
  109. formData.set('module', choose);
  110. });
  111. }
  112. // 从 bug 页面获取关键词列表
  113. function get_keywords_from_bug_page()
  114. {
  115. var keywords = $('#legendBasicInfo tr:nth-child(25) td:first')[0].childNodes[0].nodeValue;
  116. keywords = split_keywords(keywords);
  117. return keywords;
  118. }
  119. // 从 GM 中获取存储的最近使用的关键词列表
  120. function get_last_recent_used_keywords()
  121. {
  122. var default_keywords = ["待评审", "无效", "延期处理", "不复现"]
  123. var keywords = GM_getValue('last_recent_used_keywords', default_keywords);
  124. if (!Array.isArray(keywords))
  125. return default_keywords;
  126. return keywords;
  127. }
  128. // 更新最近使用的关键词列表
  129. function update_last_recent_used_keywords(keywords)
  130. {
  131. // bug 中的关键词列表
  132. var bugKeywords = get_keywords_from_bug_page();
  133. // 上次的关键字列表
  134. var lastKeywords = get_last_recent_used_keywords();
  135. // 提交的关键词中去掉 bug 中的
  136. var newKeywords = keywords.filter(x => !bugKeywords.includes(x));
  137. // 最后的列表中移除新的,并把新的添加到前面
  138. lastKeywords = lastKeywords.filter(x => !newKeywords.includes(x));
  139. lastKeywords = newKeywords.concat(lastKeywords);
  140. if (lastKeywords.length > 10)
  141. lastKeywords.length = 10;
  142. GM_setValue('last_recent_used_keywords', lastKeywords);
  143. }
  144. // 分隔关键词列表
  145. function split_keywords(keywords)
  146. {
  147. if (!keywords || keywords.trim().length === 0)
  148. return [];
  149.  
  150. // 分隔已有的关键词列表
  151. var re = /\s+|,|,|;|;/;
  152. var keywords = keywords.split(re).map(x => x.trim()).filter(x => x && x.length > 0);
  153. return keywords;
  154. }
  155. // 更新输入框元素列表
  156. function update_keywords_input()
  157. {
  158. // 是否自定义
  159. var custom = $('.custom-keywords').is(":checked");
  160. $('#keywords-input').prop('disabled', !custom);
  161. // 非自定义,由用户输入决定结果
  162. if (!custom) {
  163. // 非自定义,由标签决定结果,遍历所有标签
  164. var keywords = [];
  165. $('.checkbox input:checked').each(function() {
  166. keywords.push($(this)[0].nextSibling.nodeValue.trim());
  167. });
  168. // 输入框数据
  169. $('#keywords-input').val(keywords.join(", "));
  170. }
  171. }
  172. // 用户提交编辑后的关键字列表
  173. function submit_keywords()
  174. {
  175. var keywords = $('#keywords-input').val();
  176. keywords = split_keywords(keywords);
  177. // 隐藏对话框
  178. $('#dialog').modal('hide');
  179. // 关键词列表提交保存
  180. update_last_recent_used_keywords(keywords);
  181. // 提交修改
  182. var keywords = keywords.join(", ");
  183. // 当前任务 id
  184. var id = $('.label-id').text();
  185. submit_modify_job(id, function(resData, formData) {
  186. // 修改关键字数据
  187. formData.set('keywords', keywords);
  188. });
  189. }
  190. function assign_back(e) {
  191. // 禁止默认的响应
  192. e.preventDefault();
  193. // 获取选中的任务列表
  194. $('#bugList tr').filter('.checked').each(function () {
  195. var id = $(this).find('.c-id input').val();
  196. submit_modify_job(id, function(resData, formData) {
  197. // 找到创建者名字和 id
  198. var creater = $(resData).find('.detail:nth-child(3) > .table-form td:first').text();
  199. var creater_id = '';
  200. var options = $(resData).find('#assignedTo > option');
  201. for (var i = 0; i != options.length; ++i) {
  202. if (options[i].title == creater) {
  203. creater_id = options[i].value;
  204. break;
  205. }
  206. }
  207. formData.set('assignedTo', creater_id);
  208. });
  209. });
  210. }
  211. function update_jobs_keywords(e)
  212. {
  213. // 禁止默认的响应
  214. e.preventDefault();
  215. // 获取当前关键词列表
  216. var keywords = get_keywords_from_bug_page();
  217. // 最后使用的 keywords
  218. var last_keywords = get_last_recent_used_keywords();
  219. // 合并去重后的选项列表
  220. var options = keywords.filter(x => !last_keywords.includes(x)).concat(last_keywords);
  221. // 创建选项组
  222. var divBox = $("<div></div>");
  223. options.forEach(x => {
  224. var select = $("<div class='checkbox'><label><input type='checkbox'>" + x + "</label></div>");
  225. // 勾选事件
  226. $(select).find('input').change(update_keywords_input);
  227. if (keywords.includes(x)) {
  228. $(select).find('input').prop('checked', true);
  229. }
  230. $(divBox).append(select);
  231. });
  232. // 输入框
  233. var inputGroup = $("<div class='checkbox'><label><input class='custom-keywords' type='checkbox'>自定义</label>&nbsp;<div class='input-control'><input id='keywords-input' type='text' class='form-control'></div></div>");
  234. // 标签和输入框居中
  235. $(inputGroup).css({"display": "flex", "align-items": "center"});
  236. // 输入框伸长
  237. $(inputGroup).find('.input-control').css({"flex-grow": "1"});
  238. // 输入框勾选自定义
  239. $(inputGroup).find('.custom-keywords').change(update_keywords_input);
  240. $(divBox).append(inputGroup);
  241. // 更新对话框
  242. update_dialog('更新关键词', divBox);
  243. // 更新对话框的提交事件
  244. $('#dialog .btn-primary').off('click');
  245. $('#dialog .btn-primary').click(submit_keywords);
  246. // 更新元素状态
  247. update_keywords_input();
  248. }
  249. // 添加对话框
  250. $("body").append("<div class='modal fade' id='dialog'><div class='modal-dialog modal-sm'><div class='modal-content'><div class='modal-header'><h4 class='modal-title'></h4></div><div class='modal-body'></div><div class='modal-footer'><button type='button' class='btn btn-default' data-dismiss='modal'>关闭</button><button type='button' class='btn btn-primary'>保存</button></div></div></div></div>");
  251.  
  252. // 添加模块修改按钮
  253. $('.table-actions').append('<button class="btn" id="update-module" data-toggle="modal" data-target="#dialog">修改所属模块</button>');
  254. // 更新模块点击的事件
  255. $('#update-module').click(update_jobs_module);
  256. // 添加一键指回 bug 提出人按钮
  257. $('.table-actions').append('<button class="btn" id="assign-back">指派给提出人</button>');
  258. // 事件
  259. $('#assign-back').click(assign_back);
  260.  
  261. // 如果在 bug view 界面,增加快速修改按钮
  262. var basicInfoPanel = $('#legendBasicInfo');
  263. if (basicInfoPanel.length > 0)
  264. {
  265. // 更新模块
  266. var btn = '<button class="btn" id="update-module-x" data-toggle="modal" data-target="#dialog">更新模块</button>';
  267. $('#legendBasicInfo tr:nth-child(2) td:first').append(btn);
  268. $('#update-module-x').click(update_jobs_module);
  269. // 更新关键词
  270. var btn = '<button class="btn" id="update-keywords-x" data-toggle="modal" data-target="#dialog">更新关键词</button>';
  271. $('#legendBasicInfo tr:nth-child(25) td:first').append(btn);
  272. $('#update-keywords-x').click(update_jobs_keywords);
  273. }
  274. // 如果在 bug 列表界面,计算 bug 解决率
  275. if ($('.table-footer > .text').length > 0)
  276. {
  277. var items = $('.table-footer > .text > strong');
  278. var total = parseInt($(items[0]).text(), 10);
  279. var unresolved = parseInt($(items[1]).text(), 10);
  280. $('.table-footer > .text > strong:last-child').after(',解决率 <strong>' + (100.0 * (total - unresolved) / total).toFixed(2) + '%</strong>');
  281. }
  282. })();