// ==UserScript== // @name Bugzilla Script // @namespace xiwenge // @version 0.68.0 // @author xiwenge // @description 美化增强 Bugzilla 页面 // @icon https://www.bugzilla.org/assets/img/logo-header.svg // @match *://192.168.1.31/bugzilla* // @match *://192.168.1.31/bugzilla/show_bug.cgi* // @match *://192.168.1.31/bugzilla/buglist.cgi* // @match *://192.168.1.31/bugzilla/query.cgi* // @match *://192.168.1.31/bugzilla/summarize_time.cgi* // @match *://192.168.1.31/bugzilla/attachment.cgi* // @match *://192.168.1.31/bugzilla/enter_bug.cgi* // @match *://192.168.1.31/bugzilla/describecomponents.cgi* // @match *://192.168.1.31/bugzilla/show_activity.cgi* // @match *://192.168.1.31/bugzilla/process_bug.cgi* // @grant GM_addStyle // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @run-at document-end // ==/UserScript== (function () { 'use strict'; const d=new Set;const importCSS = async e=>{d.has(e)||(d.add(e),(t=>{typeof GM_addStyle=="function"?GM_addStyle(t):(document.head||document.documentElement).appendChild(document.createElement("style")).append(t);})(e));}; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); const indexLess = 'body{margin:0!important;padding:13px!important;--primaryColor: #18A058;--primaryColorHover: #15904e;--textGrayColor: #b1b1b1}::-webkit-scrollbar{width:12px}::-webkit-scrollbar-track{background:#f1f1f1;border-radius:12px}::-webkit-scrollbar-thumb{background:#b9bbbd;border-radius:12px}::-webkit-scrollbar-thumb:hover{cursor:pointer;background:#a6a9ac;transform:scale(1.2)}::-webkit-scrollbar:horizontal{height:12px}::-webkit-scrollbar-track:horizontal{background:#f1f1f1;border-radius:12px}::-webkit-scrollbar-thumb:horizontal{background:#b9bbbd;border-radius:12px}::-webkit-scrollbar-thumb:hover:horizontal{cursor:pointer;background:#a6a9ac;transform:scale(1.2)}#header .links .form form,#footer .links .form form{display:inline-flex!important;align-items:center}#header{position:sticky;top:0;z-index:100}#header #title{display:block;width:270px}.monaco-editor{width:100%!important;height:100%!important}.navigation{padding:2px;display:flex;align-items:center}.bz_query_buttons[valign=middle]{display:flex}.bz_query_buttons[valign=middle] form:not(:last-child){margin-right:10px}.custom-button{display:inline-flex;align-items:center!important;width:fit-content!important;height:28px;background-color:var(--primaryColor);border-radius:4px!important;color:#fff;top:0;right:0;bottom:0;left:0;opacity:1;transition-duration:.3s;transition-property:box-shadow,opacity,color,border;padding:4px 12px!important;font-weight:400;font-size:12px;margin:0 5px;box-sizing:border-box;text-decoration:none;line-height:18px;border:1px solid transparent;-webkit-user-select:none;user-select:none}.custom-button:hover{height:28px;cursor:pointer;color:#fff!important;background-color:var(--primaryColorHover)}.custom-button:visited{color:#fff}.plain-button{background:#fff!important;color:#757575!important;border:1px solid #cccccc!important;text-decoration:none!important}.plain-button:hover{height:28px;color:var(--primaryColor)!important;border:1px solid var(--primaryColor)!important}.text-button{background:transparent!important;color:#4a4a4a!important;border:none!important;text-decoration:none!important}.text-button:hover{cursor:pointer;color:#4a4a4a!important;box-shadow:none!important}.disabled-button{cursor:not-allowed!important;border-color:#d9d9d9!important;color:#00000040!important;background:#0000000a!important;box-shadow:none!important;pointer-events:none!important}.disabled-button:hover{cursor:not-allowed!important;border-color:#d9d9d9!important;color:#00000040!important;box-shadow:none!important}.field_label{vertical-align:middle!important}select{height:28px;border-radius:4px;padding:4px;border-color:#d9d9d9;box-sizing:border-box}select:hover{cursor:pointer}select:focus-visible{outline:#d9d9d9}#available_columns,#selected_columns{height:300px;display:block;margin-right:4px}#assigned_to_list_vue_app,#assigned_to_list_vue_app .n-base-selection-label{height:28px}input:not([class=n-input__input-el])[type=checkbox]:hover{cursor:pointer}input:not([class=n-input__input-el])[type=radio]:hover{cursor:pointer}input:not([class=n-input__input-el]):not([type=radio]):not([type=checkbox]):not([type=range]):not([type=color]):not([type=hidden]):not([type=reset]):not([type=file]){height:28px!important;border-radius:4px;padding:0 6px;outline:0px solid transparent;border:1px solid #d9d9d9;box-sizing:border-box}textarea{border-radius:4px;padding:4px;outline:0px solid transparent;border:1px solid #d9d9d9}#status{margin-bottom:unset;align-items:center;display:flex;margin-top:3px}.knob-buttons:has(input[id=commit]){margin-top:4px}#bug_status{margin:0 6px}.bz_section_spacer{display:none}#bz_assignee_input{width:543px}#set_default_assignee_label,#set_default_assignee{display:none}.bz_assignee_td{display:flex;align-items:center;width:598px}#bz_show_bug_column_1 tr th{min-height:34px!important}#bz_show_bug_column_1 tr:has(th[id=field_label_tag]){transform:translateY(2px)}#bz_show_bug_column_1 tr:has(th[id=field_label_dependson]){transform:translateY(4px)}#bz_show_bug_column_1 tr:has(th[id=field_label_blocked]){transform:translateY(6px)}#bz_show_bug_column_1 tr:has(td[id=show_dependency_tree_or_graph]){display:none}#bz_show_bug_column_1 tr td{min-height:34px!important}#bz_show_bug_column_2 .field_label{vertical-align:middle!important;max-width:200px}#bz_show_bug_column_2 table tr td{max-width:100%}#cf_newfeatureid{width:100%}.global-message{width:fit-content;height:fit-content;padding:8px 24px;box-sizing:border-box;display:inline-flex;align-items:center;position:fixed;left:50%;transform:translate(-50%,-50%);z-index:99999999;background-color:#fff;border-radius:4px;transition:opacity .5s ease-in-out,top .5s ease-in-out;animation:message-fadein .5s ease-in-out forwards;box-shadow:0 0 6px #0003}.global-message_text{margin-left:8px;font-size:14px}@keyframes message-fadein{0%{opacity:.5;top:10px}to{opacity:1;top:30px}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.global-dialog{height:fit-content;max-width:100vw;max-height:100vh;z-index:9999;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background-color:#fff;border-radius:4px}.global-dialog .control-btn:hover{cursor:pointer}.global-dialog .control-btn:not(:last-child){margin-right:8px}.global-dialog .close-btn:hover{filter:drop-shadow(0px 0px 2px #ff5953)}.global-dialog .exit-full-screen-btn:hover{filter:drop-shadow(0px 0px 2px #fabe33)}.global-dialog .full-screen-btn:hover{filter:drop-shadow(0px 0px 2px #53c32b)}.global-dialog-header{padding:6px 12px;background-color:#f5f5f5;border-radius:4px 4px 0 0}.global-dialog-footer{padding:6px 12px;background-color:#f5f5f5;border-radius:0 0 4px 4px;display:flex;justify-content:flex-end;box-sizing:border-box}.global-dialog-body{max-width:100%;max-height:calc(100% - 68px);height:100%;padding:12px 24px;overflow-y:auto;overflow-x:auto;box-sizing:border-box}.global-dialog-mask{width:100vw;height:100vh;position:fixed;top:0;left:0;background-color:#00000080}.global-dialog-mask-transparent{background-color:transparent}.global-loading{width:100%;height:100%;position:relative}.global-loading:after{content:"";width:20px;height:20px;border:2px solid var(--primaryColor);border-left:2px solid transparent;border-radius:50%;position:absolute;top:calc(50% - 10px);left:calc(50% - 10px);transform:translate(-50%,-50%);animation:loading 1.5s infinite linear;background-color:transparent}.self-rotate{animation:loading 1.5s infinite linear}.attachment-container{width:100%;height:100%;overflow-y:auto;overflow-x:auto}.flex{display:flex}.flex-wrap{flex-wrap:wrap}.flex-center{display:flex;justify-content:center;align-items:center}.flex-align-center{display:flex;align-items:center}.flex-justify-center{display:flex;justify-content:center}.flex-justify-space-between{display:flex;justify-content:space-between}.flex-justify-end{display:flex;justify-content:flex-end}.show{display:block}.show-inline-block{display:inline-block}.hide{display:none}.cursor-pointer:hover{cursor:pointer}.text-underline{text-decoration:underline}.font-size-16px{font-size:16px}.font-bold{font-weight:700}.margin-right-8px{margin-right:8px}.margin-left-8px{margin-left:8px}.margin-bottom-8px{margin-bottom:8px}.margin-top-8px{margin-top:8px}.link-title{font-weight:700;transition:all .3s ease-in-out}.link-title:hover{color:var(--primaryColor)!important;text-decoration:unset!important}.separator{margin:0 6px 0 4px;color:#ccc}.links .form a[title="Quicksearch Help"]{margin-left:6px}#attachment_table,#attachment_table td{border:1px solid #d9d9d9}#attachment_table tr td[valign=top]:nth-child(2):has(a){display:flex;justify-content:center;align-items:center}#summary_field.search_field_row input{padding-bottom:unset!important;vertical-align:middle}#short_desc{padding-bottom:unset!important}#summary_field.search_field_row select{padding-bottom:4px;vertical-align:middle}.bz_quip{display:block;color:var(--textGrayColor);font-weight:700;font-size:12px;font-style:italic;margin-top:8px;font-family:黑体}.bz_query_timestamp{color:var(--textGrayColor);font-style:normal;font-size:14px;display:inline-block;font-family:黑体}.n-base-selection__border,.n-base-selection__state-border{display:none}.n-checkbox .n-checkbox-box .n-checkbox-icon{transform:rotate(-12deg) translate(.5px,1px)}#setting-city{display:inline-block;color:var(--primaryColor)}#setting-city:hover{cursor:pointer}#setting-city svg{transition:all .3s;transform:translateY(3px);font-size:16px}#component_list_vue_app{height:28px;width:400px}#component_list_vue_app .n-base-selection-label{height:28px}#version_list_vue_app{height:28px;width:400px}#version_list_vue_app .n-base-selection-label{height:28px}#cc_list_vue_app{height:28px;width:400px}#cc_list_vue_app .n-base-selection-label{height:28px}#newcc_list_vue_app{height:28px;width:400px}#newcc_list_vue_app .n-base-selection-label{height:28px}#feature_list_vue_app{height:28px;min-width:200px;max-width:800px;width:100%}#feature_list_vue_app .n-base-selection-label{height:28px}#search_history_list_vue_app_top,#search_history_list_vue_app_bottom{height:28px;width:120px;max-width:200px}#search_history_list_vue_app_top .n-base-selection-label,#search_history_list_vue_app_bottom .n-base-selection-label{height:28px;border-radius:4px}#comp_desc_container{display:none}#comp_desc{height:fit-content}.validation_error_text{font-size:12px;color:#e80505;font-weight:400}.bug-new_filename{color:gray;font-weight:600;margin-left:6px}.collection-list-title:hover{cursor:pointer;color:var(--primaryColor)}#titles #subtitle{font-weight:600}.bz_alias_short_desc_container.edit_form{padding:10px}.margin-0{margin:0}.mt-5{margin-top:5px}.ml-5{margin-left:5px}.mr-5{margin-right:5px}.mb-5{margin-bottom:5px}.n-base-selection-placeholder{font-size:12px}.bz_comment.bz_first_comment .bz_comment_text p{margin-block-start:0;margin-block-end:0}.clear-br-label{-webkit-user-select:none;user-select:none}.clear-br-label:hover{cursor:pointer;color:var(--primaryColorHover)}'; importCSS(indexLess); var CDNScriptURLEnum = ((CDNScriptURLEnum2) => { CDNScriptURLEnum2["MONACO"] = "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.0/min/vs/loader.min.js"; CDNScriptURLEnum2["VUE"] = "https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.13/vue.global.prod.min.js"; CDNScriptURLEnum2["NAIVE_UI"] = "https://cdnjs.cloudflare.com/ajax/libs/naive-ui/2.40.1/index.prod.js"; CDNScriptURLEnum2["CRYPTO_JS"] = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"; return CDNScriptURLEnum2; })(CDNScriptURLEnum || {}); const PATHNAME = window.location.pathname; const ZHOU_OF_WEEK_MAP = { SUNDAY: "周日", MONDAY: "周一", TUESDAY: "周二", WEDNESDAY: "周三", THURSDAY: "周四", FRIDAY: "周五", SATURDAY: "周六" }; const XINGQI_OF_WEEK_MAP = { SUNDAY: "星期日", MONDAY: "星期一", TUESDAY: "星期二", WEDNESDAY: "星期三", THURSDAY: "星期四", FRIDAY: "星期五", SATURDAY: "星期六" }; var PathNameEnum = ((PathNameEnum2) => { PathNameEnum2["BUG_LIST"] = "/bugzilla/buglist.cgi"; PathNameEnum2["BUG_DETAIL"] = "/bugzilla/show_bug.cgi"; PathNameEnum2["BUG_NEW"] = "/bugzilla/enter_bug.cgi"; PathNameEnum2["BUG_NEW_ENTRY"] = "/bugzilla/describecomponents.cgi"; PathNameEnum2["BUG_ATTACHMENT"] = "/bugzilla/attachment.cgi"; PathNameEnum2["BUG_HISTORY"] = "/bugzilla/show_activity.cgi"; PathNameEnum2["BUG_PROCESS"] = "/bugzilla/process_bug.cgi"; return PathNameEnum2; })(PathNameEnum || {}); function $(selector, context = document) { return context.querySelector(selector); } function $$(selector, context = document) { return Array.from(context.querySelectorAll(selector)); } const renderBugAttachment = () => { const attachmentSubmit = $(".attachment_entry input[value='Submit']"); if (attachmentSubmit) { attachmentSubmit.classList.add("custom-button"); } }; const renderBugAttachmentPage = () => { if (PATHNAME === PathNameEnum.BUG_ATTACHMENT) { renderBugAttachment(); } }; const PERSONS_MAP = { anbin: "安斌", anna: "柏红梅", annabai: "柏红梅", BrukcalLi: "李林浩", lilinhao: "李林浩", cehn: "王笑晨", cehnwang: "王笑晨", cesar: "陈述", cesarchen: "陈述", cherry: "兰春花", Cherrylan: "兰春花", chuwanting: "储婉婷", chenxiaoxia: "陈晓霞", dizizhao: "狄子钊", dingjiaju: "丁佳驹", fiona: "牟卫灵", fionam: "牟卫灵", frank: "席小丁", frankxi: "席小丁", xiwenge: "郗文革", fuge: "付戈", harehe: "何小波", huanglin: "黄林", huangqing: "黄青", huangxudong: "黄旭东", huangyifei: "黄一非", huangyueqi: "黄月琪", huangyou: "黄友", humming: "马云", hummingma: "马云", huqing: "胡青", jenny: "王亮", jennywang: "王亮", jiangqianqian: "姜倩倩", jiangjiacheng: "蒋佳成", jizeyu: "纪泽宇", joe: "周强", zhouqiang: "周强", jose: "赖振海", joselai: "赖振海", kevin: "张亚峰", lidehua: "李德华", lifuyi: "李福一", lihaichuan: "李海川", liju: "李桔", lilei: "李雷", linchao: "林超", lisa: "姚艳丽", lisayao: "姚艳丽", liubowen: "刘博文", liuxu: "刘旭", liuyicheng: "刘伊铖", lixianxiang: "李先祥", lixiaoyi: "李小益", liyandong: "李彦东", lucy: "赵亚兰", lucyzhao: "赵亚兰", luoshengming: "罗圣明", magina: "刘松平", maginaliu: "刘松平", Majun: "马军", majun: "马军", mandyyang: "杨曼", maqingbin: "马庆宾", mijie: "米杰", sandywang: "王硕", shenbei: "申倍", demanshen: "申倍", shenfangshuai: "申方帅", shenxiang: "沈祥", shikun: "石坤", larryshi: "石坤", liutianrui: "刘添瑞", sunfeng: "孙丰", stevensun: "孙丰", shizhipeng: "石志鹏", songjie: "宋杰", steven: "刘洋", stevenliu: "刘洋", talk: "陈涛", talkchen: "陈涛", tangshuangxiao: "唐霜晓", tianmaosheng: "田茂生", una: "高秋涵", Unagao: "高秋涵", vern: "张超", Vernzhang: "张超", wangdandan: "王丹丹", wangfeiyan: "王飞燕", wanghairong: "王海容", wuguangmin: "吴光敏", wangshijie: "王世杰", wangyi: "王屹", weiliangpeng: "韦良鹏", wendy: "吴红省", wuhaidong: "吴海东", wuhongsheng: "吴红省", wendywu: "吴红省", xuhang: "徐航", yanghuanhuan: "杨欢欢", yangmingxuan: "杨明轩", yanieye: "叶伟", yafengzhang: "张亚峰", yihuan: "王义欢", yihuanwang: "王义欢", zengchenglong: "曾成龙", zhangrunsheng: "张润生", gausszhang: "张润生", zhangyingqi: "张莹琦", zhangzheyi: "张喆懿", zhaozuwen: "赵祖雯", zhengsong: "郑松", "zhengsong.ext": "郑松", zhoulin: "周林", zhuhewei: "朱赫伟", test: "永洪测试", yonghong: "永洪测试", yonghongcode: "永洪测试", yonghongdoc: "永洪测试" }; const FORM_ITEM_1 = [ { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(td[id='bz_field_status'])", label: "Status" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(td[id='field_container_product'])", label: "Product" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(td[id='field_container_component'])", label: "Component" }, { show: true, disabled: true, value: "#bz_show_bug_column_1 tr:has(th[id='field_label_version'])", label: "Version" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(th[id='field_label_rep_platform'])", label: "Hardware" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(label[for='priority'])", label: "Importance" }, { show: true, disabled: true, value: "#bz_show_bug_column_1 tr:has(td[class='bz_assignee_td'])", label: "Assigned To" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(th[id='field_label_bug_file_loc'])", label: "URL" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(th[id='field_label_tag'])", label: "Tags" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(th[id='field_label_dependson'])", label: "Depends on" }, { show: true, disabled: false, value: "#bz_show_bug_column_1 tr:has(th[id='field_label_blocked'])", label: "Blocks" } ]; const FORM_ITEM_2 = [ { show: true, disabled: true, value: "#bz_show_bug_column_2 table tr:first-child", label: "Reported" }, { show: true, disabled: true, value: "#bz_show_bug_column_2 table tr:nth-child(2)", label: "Modified" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(3)", label: "CC List" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(5)", label: "See Also" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(6)", label: "Bug类型1" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(7)", label: "Bug类型2" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(8)", label: "请选择Feature号" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(9)", label: "是否有Matrix/Case覆盖" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(10)", label: "TestCase类型" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(11)", label: "客户是否也发现了这个问题" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(12)", label: "公司名称" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(13)", label: "问题复杂度" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(14)", label: "Bug提报时的版本状态" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(15)", label: "Bug产生原因(开发人员填写)" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(16)", label: "Bug产生细节原因(开发人员填写)" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(17)", label: "如果是改出来的Bug,填入是谁改出来的(开发人员填写)" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(18)", label: "如果是改出来的Bug,填入Bug号或Feature号(开发人员填写)" }, { show: true, disabled: true, value: "#bz_show_bug_column_2 table tr:nth-child(19)", label: "影响范围和测试建议(开发人员填写)" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(20)", label: "Bug被Reopen原因" }, { show: true, disabled: false, value: "#bz_show_bug_column_2 table tr:nth-child(21)", label: "对应testlink用例编号" } ]; const COMMENT_DEFAULT_TEMPLATE_1 = ` 📌【复现条件】 ───────────────────────────── 🔍【复现步骤】 ───────────────────────────── 1. 2. ✅【预期结果】 ───────────────────────────── ❌【实际结果】 ───────────────────────────── 💡【备注信息】 ───────────────────────────── `; const COMMENT_DEFAULT_TEMPLATE_2 = ` 📌【复现条件】 ───────────────────────────── 🔍【复现步骤】 ───────────────────────────── 1. 2. ✅【预期结果】 ───────────────────────────── ❌【实际结果】 ───────────────────────────── 💡【备注信息】 ───────────────────────────── `; const BUG_STATUS = { VERIFIED: "VERIFIED", RESOLVED: "RESOLVED", CLOSED: "CLOSED" }; const RESOLUTION = { FIXED: "FIXED" }; const STORAGE_KEY_MAP = { COLLECTED_BUGS: "collectedBugs", PERSONS_MAP: "personsMap", QUIP_TYPE: "quipType", COMPLETED_BUGS: "completedBugs", CITY: "city", FORM_ITEM_CONTROL: "formItemControl", CUSTOM_BRANCHES: "customBranches", NEW_BUG_COMMENT_TEMPLATE: "newBugCommentTemplate", LIST_PAGE_ACTION_BTNS: "listPageActionBtns", HASCLEAR_COMPLETED_BUGS: "hasClearCompletedBugs", SEARCH_HISTORY: "searchHistory", QUICK_SUMMARY: "quickSummary", GITLAB_CONFIG: "gitlabConfig", HAS_AUTO_SYNC_PERSON_MAP: "hasAutoSyncPersonMap", PAGE_DETAIL_CONTROL: "pageDetailControl", AUTO_SYNC_LOCAL_DATA_TO_CLOUD: "autoSyncLocalDataToCloud", KEEP_REPORT_BUGS_DAYS: "keepReportBugsDays" }; const GITLAB_ORIGIN = "http://192.168.1.180:8888"; const FIRE_BASE_SCRIPT_URL = { APP: "https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js", AUTH: "https://www.gstatic.com/firebasejs/10.7.1/firebase-auth-compat.js", DATABASE: "https://www.gstatic.com/firebasejs/10.7.1/firebase-database-compat.js" }; const FIRE_BASE_CONFIG = { apiKey: "AIzaSyCuNIxlkvH6zV-V_Ashi0y7-l1ihAaHEu8", authDomain: "bugzilla-6ecaf.firebaseapp.com", databaseURL: "https://bugzilla-6ecaf-default-rtdb.asia-southeast1.firebasedatabase.app", projectId: "bugzilla-6ecaf", storageBucket: "bugzilla-6ecaf.firebasestorage.app", messagingSenderId: "81688421567", appId: "1:81688421567:web:c94cb82be4131a8540920a" }; const showMessage = (type, message, duration = 2e3) => { const existMessageNode = $(".global-message"); if (existMessageNode) { document.body.removeChild(existMessageNode); } const svgSuccess = ``; const svgError = ``; const svgMap = new Map([ ["success", svgSuccess], ["error", svgError] ]); const innerHTML = ` ${svgMap.get(type)} ${message} `; const messageNode = document.createElement("div"); let timer = null; messageNode.classList.add("global-message"); messageNode.innerHTML = innerHTML; document.body.appendChild(messageNode); timer = setTimeout(() => { const animation = messageNode.animate( [ { opacity: 1, top: "30px" }, { opacity: 0, top: "10px" } ], { duration: 500, easing: "ease-in-out", fill: "forwards" } ); animation.onfinish = () => { var _a; animation.cancel(); messageNode && ((_a = document.body) == null ? void 0 : _a.removeChild(messageNode)); clearTimeout(timer); }; }, duration); return () => { var _a; messageNode && ((_a = document.body) == null ? void 0 : _a.removeChild(messageNode)); clearTimeout(timer); }; }; const showSuccessMessage = (message, duration = 2e3) => { return showMessage("success", message, duration); }; const showErrorMessage = (message, duration = 2e3) => { return showMessage("error", message, duration); }; const getCollectedBugs = () => { var _a; return ((_a = JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.COLLECTED_BUGS) ?? "{}")) == null ? void 0 : _a.list) ?? []; }; const getCollectInfo = (id) => { const collectedBugs = getCollectedBugs(); const hasCollected = collectedBugs.length && collectedBugs.some((bug) => bug.id.includes(id)); return { collectedBugs, hasCollected }; }; const deleteCollectedBug = (id) => { const collectedBugs = getCollectedBugs(); const newCollectedBugs = collectedBugs.filter((bug) => !bug.id.includes(id)); localStorage.setItem(STORAGE_KEY_MAP.COLLECTED_BUGS, JSON.stringify({ list: newCollectedBugs })); }; const setCollectionBug = (id, title, url) => { const { collectedBugs = [], hasCollected } = getCollectInfo(id); if (hasCollected) { showErrorMessage("此bug已收藏"); return; } collectedBugs.push({ id, title, url }); localStorage.setItem(STORAGE_KEY_MAP.COLLECTED_BUGS, JSON.stringify({ list: collectedBugs })); showSuccessMessage("收藏成功"); }; const setCollectionIcon = (collectionNode, id) => { if (!collectionNode) { return; } collectionNode.innerHTML = getCollectInfo(id).hasCollected ? ` ` : ` `; }; const preventBoom = (fun, ...args) => { if (fun && typeof fun === "function") { return fun(...args); } }; const createDialog = ({ title = "", body, footer, maskTransparent = false, okText = "确定", cancelText = "取消", onOk, onCancel, hideOk = false, hideCancel = false, className = "", bodyClassName = "", style = "", bodyStyle = "", autoFullScreen = false, onMount = () => { }, onDestroy = () => { } }) => { let storeDialogWidth = 0; let storeDialogHeight = 0; let isDialogFullScreen = void 0; let isDragging = false; let startX = 0; let startY = 0; let dialogStartX = 0; let dialogStartY = 0; const titleTransformWidthMap = new Map([ [1, 8], [2, 20], [3, 32] ]); const existDialogNodes = $$(".global-dialog"); const length = existDialogNodes.length; const zIndex = 900 + length; const maskNode = document.createElement("div"); maskTransparent && maskNode.classList.add("global-dialog-mask-transparent"); const dialogNode = document.createElement("div"); const header = document.createElement("div"); header.style.display = "flex"; header.style.alignItems = "center"; const dialogBody = document.createElement("div"); const headerBtn = document.createElement("div"); headerBtn.style.display = "inline-block"; const headerTitle = document.createElement("div"); const titleNode = document.createElement("div"); headerTitle.classList.add("flex-center"); headerTitle.style.flex = "1"; if (!autoFullScreen && !isDialogFullScreen) { headerTitle.style.cursor = "move"; } headerTitle.appendChild(titleNode); titleNode.style.userSelect = "none"; titleNode.innerText = title; const closeBtn = document.createElement("span"); closeBtn.title = "关闭弹窗(Esc)"; const exitFullScreenBtn = document.createElement("span"); exitFullScreenBtn.title = "退出全屏"; const fullScreenBtn = document.createElement("span"); fullScreenBtn.title = "全屏显示"; const closeIcon = ` `; const exitFullScreenIcon = ` `; const fullScreenIcon = ` `; closeBtn.innerHTML = closeIcon; closeBtn.classList.add("control-btn"); closeBtn.classList.add("close-btn"); exitFullScreenBtn.innerHTML = exitFullScreenIcon; exitFullScreenBtn.classList.add("control-btn"); exitFullScreenBtn.classList.add("exit-full-screen-btn"); fullScreenBtn.innerHTML = fullScreenIcon; fullScreenBtn.classList.add("control-btn"); fullScreenBtn.classList.add("full-screen-btn"); const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { if (!isDialogFullScreen) { storeDialogWidth = entry.contentRect.width; storeDialogHeight = entry.contentRect.height; } else { dialogBody.style.width = "100%"; if (dialogBody == null ? void 0 : dialogBody.firstChild) { const firstChild = dialogBody.firstChild; firstChild.style = firstChild.style ?? ""; firstChild.style.width = "100%"; firstChild.style.height = "100%"; } } } }); resizeObserver.observe(dialogNode); const closeDialog = () => { document.removeEventListener("keydown", enterEvent); document.removeEventListener("keydown", escEvent); document.removeEventListener("mousemove", dragMove); document.removeEventListener("mouseup", dragEnd); const animation2 = dialogNode.animate( [ { opacity: 1, transition: "transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out;" }, { opacity: 0, transition: "transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out;" } ], { duration: 500, easing: "ease-in-out", fill: "forwards" } ); animation2.onfinish = () => { animation2.cancel(); document.body.removeChild(maskNode); const dialogs = $$(".global-dialog"); if (dialogs.length === 0) { document.body.style.overflow = "auto"; } isDialogFullScreen = false; resizeObserver.disconnect(); onDestroy(); }; }; closeBtn.addEventListener("click", () => { closeDialog(); }); exitFullScreenBtn.addEventListener("click", () => { if (isDialogFullScreen) { headerTitle.style.cursor = "move"; isDialogFullScreen = false; dialogNode.style.width = `${Math.ceil(storeDialogWidth)}px`; dialogNode.style.height = `${Math.ceil(storeDialogHeight)}px`; dialogNode.style.transform = "translate(-50%, -50%)"; dialogNode.style.left = "50%"; dialogNode.style.top = "50%"; dialogNode.style.margin = "0"; } }); fullScreenBtn.addEventListener("click", () => { if (!isDialogFullScreen) { headerTitle.style.cursor = "auto"; isDialogFullScreen = true; dialogNode.style.width = "100%"; dialogNode.style.height = "99.6%"; dialogNode.style.transform = "translate(-50%, -50.4%)"; dialogNode.style.left = ""; dialogNode.style.top = ""; dialogNode.style.margin = "0"; dialogBody.style.width = "100%"; dialogBody.style.height = "100%"; } }); if (autoFullScreen) { fullScreenBtn.click(); } headerBtn.appendChild(closeBtn); if (!autoFullScreen) { headerBtn.appendChild(exitFullScreenBtn); headerBtn.appendChild(fullScreenBtn); } header.appendChild(headerBtn); titleNode.style.transform = `translateX(-${titleTransformWidthMap.get(headerBtn.children.length)}px)`; header.appendChild(headerTitle); header.classList.add("global-dialog-header"); dialogBody.classList.add("global-dialog-body"); const footerInner = document.createElement("div"); maskNode.classList.add("global-dialog-mask"); dialogNode.classList.add("global-dialog"); if (className) { dialogNode.classList.add(className); } if (style) { dialogNode.style = style; } if (bodyClassName) { dialogBody.classList.add(bodyClassName); } if (bodyStyle) { dialogBody.style = bodyStyle; } if (typeof footer === "string") { footerInner.innerHTML = footer; } else if (footer) { footerInner.appendChild(footer); } else if (footer !== false && footer !== null) { footerInner.classList.add("global-dialog-footer"); if (!hideOk) { const footerSaveBtn = document.createElement("div"); footerSaveBtn.classList.add("custom-button"); footerSaveBtn.innerText = okText + " (Enter)"; footerSaveBtn.addEventListener("click", () => { preventBoom(onOk, closeDialog); }); footerInner.appendChild(footerSaveBtn); } if (!hideCancel) { const footerCancelBtn = document.createElement("div"); footerCancelBtn.classList.add("custom-button"); footerCancelBtn.classList.add("plain-button"); footerCancelBtn.innerText = cancelText + " (Esc)"; footerCancelBtn.addEventListener("click", async () => { const res = await preventBoom(onCancel) ?? true; if (res) { closeDialog(); } }); footerInner.appendChild(footerCancelBtn); } } if (typeof body === "string") { dialogBody.innerHTML = body; } else { dialogBody.appendChild(body); } dialogNode.appendChild(header); dialogNode.appendChild(dialogBody); dialogNode.appendChild(footerInner); maskNode.style.zIndex = `${zIndex}`; maskNode.appendChild(dialogNode); document.body.appendChild(maskNode); document.body.style.overflow = "hidden"; const animation = dialogNode.animate( [ { opacity: 0, top: "46%", transition: "transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out;" }, { opacity: 1, top: "50%", transition: "transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out;" } ], { duration: 500, easing: "ease-in-out", fill: "forwards" } ); animation.onfinish = () => { animation.cancel(); }; const escEvent = (e) => { if (e.key === "Escape") { closeDialog(); } }; const enterEvent = (e) => { if (e.key === "Enter") { preventBoom(onOk, closeDialog); } }; const dragStart = (e) => { if (isDialogFullScreen) return; isDragging = true; startX = e.clientX; startY = e.clientY; document.body.style.userSelect = "none"; const rect = dialogNode.getBoundingClientRect(); dialogStartX = rect.left; dialogStartY = rect.top; }; const dragEnd = () => { isDragging = false; document.body.style.userSelect = "auto"; }; const dragMove = (e) => { if (!isDragging || isDialogFullScreen) return; const dx = e.clientX - startX; const dy = e.clientY - startY; const newLeft = dialogStartX + dx; const newTop = dialogStartY + dy; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const dialogWidth = dialogNode.offsetWidth; const dialogHeight = dialogNode.offsetHeight; const clampedLeft = Math.max(0, Math.min(newLeft, windowWidth - dialogWidth)); const clampedTop = Math.max(0, Math.min(newTop, windowHeight - dialogHeight)); dialogNode.style.left = `${clampedLeft}px`; dialogNode.style.top = `${clampedTop}px`; dialogNode.style.transform = "none"; dialogNode.style.margin = "0"; }; document.addEventListener("keydown", enterEvent); document.addEventListener("keydown", escEvent); headerTitle.addEventListener("mousedown", dragStart); document.addEventListener("mousemove", dragMove); document.addEventListener("mouseup", dragEnd); onMount(); return closeDialog; }; const debounce = (func, delay) => { let timer; return (...args) => { if (timer) { clearTimeout(timer); } timer = setTimeout(() => { func.apply(void 0, args); }, delay); }; }; const bugCollectionDialog = () => { let appIns = null; const appId = "bug_collection_vue_app"; createDialog({ title: "收藏列表", hideOk: true, cancelText: "关闭", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onMount: () => { const collectedBugs = getCollectedBugs(); const { createApp, ref, h } = Vue; const app = createApp({ template: `
搜索:
{{data.length}} 条
`, setup() { const inputValue = ref(""); const data = ref(collectedBugs); const columns = ref([ { title: "序号", dataIndex: "index", key: "index", width: 60, render: (_, index) => { return index + 1; } }, { title: "ID", dataIndex: "id", key: "id", width: 100, render(row) { var _a; const id = (_a = row.id.match(/(\d+)$/)) == null ? void 0 : _a[1]; return id; } }, { title: "名称", dataIndex: "title", key: "title", width: "calc(60vw - 550px)", ellipsis: { tooltip: true } }, { title: "URL", dataIndex: "url", key: "url", width: 200, ellipsis: { tooltip: true } }, { title: "操作", key: "actions", width: 180, render: (row) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "打开", onClick: () => window.open(row.url, "_blank") }), h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "删除", onClick: () => { deleteCollectedBug(row.id); data.value = getCollectedBugs(); } }) ] } ); } } ]); const onInput = debounce((value) => { data.value = collectedBugs.filter((item) => item.title.includes(value) || item.id.includes(value)); }, 500); return { inputValue, data, columns, onInput }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const DEFAULT_PAGE_DETAIL_CONTROL = { showSaveConfirmDialog: true, showHintWhenAutoAddReport: true, showAttechmentDialog: true, directOpenGitlabWhenHashRecognized: true, showCommitManualSelectDialog: true }; const setPageDetailControl = (key, value) => { const controlLocal = getPageDetailControl(); localStorage.setItem( STORAGE_KEY_MAP.PAGE_DETAIL_CONTROL, JSON.stringify({ ...controlLocal, [key]: value }) ); }; const getPageDetailControl = (key) => { const controlLocal = JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.PAGE_DETAIL_CONTROL) || "{}"); const control = { ...DEFAULT_PAGE_DETAIL_CONTROL, ...controlLocal }; if (key) { return control[key]; } return control; }; const getTodayDate = () => { const today = new Date(); return today.toISOString().split("T")[0]; }; const getSpecialDayBaseToday = (day) => { const today = new Date(); today.setDate(today.getDate() + day); return today.toISOString().split("T")[0]; }; const getSpecialDayBeforeDate = (target, days) => { const date = new Date(target); date.setDate(date.getDate() - days); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; }; const getDatesInRange = (targetDate, range) => { const dates = []; const target = new Date(targetDate); for (let i = 0; i < range; i++) { dates.push(getSpecialDayBeforeDate(target, i + 1)); } return dates; }; const getDayOfWeek = (textType, dateTime) => { const days = { zhou: Object.values(ZHOU_OF_WEEK_MAP), xingqi: Object.values(XINGQI_OF_WEEK_MAP) }; const date = new Date(dateTime || getTodayDate()); const dayIndex = date.getDay(); return days[textType][dayIndex]; }; function getReportBugsByDate(date = getTodayDate()) { const todayReportLocal = JSON.parse(localStorage.getItem(date) ?? "[]"); return todayReportLocal; } function isBugInReport(bugId, date = getTodayDate()) { const todayReportLocal = getReportBugsByDate(date); return todayReportLocal.some((it) => it.id === bugId); } const addBugWithRepeatCheck = (bug, date = getTodayDate(), isSaveForm = false) => { if (isBugInReport(bug.id, date)) { if (!isSaveForm || getPageDetailControl("showHintWhenAutoAddReport")) { showErrorMessage("该BUG已在日报中"); } return false; } const todayReportLocal = getReportBugsByDate(date); todayReportLocal.push(bug); localStorage.setItem(date, JSON.stringify(todayReportLocal)); if (!isSaveForm || getPageDetailControl("showHintWhenAutoAddReport")) { showSuccessMessage("添加日报成功"); } return true; }; const removeBugFromReport = (bugId, date = getTodayDate()) => { const todayReportLocal = getReportBugsByDate(date); const index = todayReportLocal.findIndex((it) => it.id === bugId); if (index === -1) { showErrorMessage("该BUG不存在"); return false; } todayReportLocal.splice(index, 1); localStorage.setItem(date, JSON.stringify(todayReportLocal)); showSuccessMessage("移除成功"); return true; }; const getKeepReportBugsDays = () => { return Number(localStorage.getItem(STORAGE_KEY_MAP.KEEP_REPORT_BUGS_DAYS) ?? "14"); }; const setKeepReportBugsDays = (days) => { localStorage.setItem(STORAGE_KEY_MAP.KEEP_REPORT_BUGS_DAYS, days.toString()); }; const copyText = (text, showDetail = false) => { if (!text) { showErrorMessage("没有可复制的内容"); return; } if (typeof GM_setClipboard !== "function") { showErrorMessage("当前环境不支持复制功能"); return; } if (!showDetail) { GM_setClipboard(text, "text", () => { showSuccessMessage("复制成功"); }); return; } showTextDetail(text, (body) => { createDialog({ title: "内容详情", body, okText: "确定复制", onOk: (closeDialog) => { GM_setClipboard(text, "text", () => { showSuccessMessage("复制成功"); closeDialog(); }); } }); }); }; const showTextDetail = (text, cb) => { const body = document.createElement("div"); const textareaNode = document.createElement("div"); const texts = text.split("\n"); texts.forEach((item) => { const span = document.createElement("span"); span.textContent = item; textareaNode.appendChild(span); textareaNode.appendChild(document.createElement("br")); }); textareaNode.style.setProperty("width", "98%"); textareaNode.style.setProperty("height", "96%"); textareaNode.style.setProperty("border", "1px solid #d9d9d9"); textareaNode.style.setProperty("outline", "0px solid transparent"); textareaNode.style.setProperty("padding", "6px"); textareaNode.style.setProperty("border-radius", "4px"); textareaNode.style.setProperty("overflow-y", "scroll"); body.appendChild(textareaNode); body.style.setProperty("width", "30vw"); body.style.setProperty("height", "30vh"); preventBoom(cb, body); }; const bugHistoryDialog = () => { let appIns = null; const appId = "bug_history_vue_app"; const closeDialog = createDialog({ title: "历史记录", hideOk: true, cancelText: "关闭", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onMount: () => { const keepDays = getKeepReportBugsDays(); const days = getDatesInRange(getSpecialDayBaseToday(1), keepDays); const bugHistory = []; for (let i = 0; i < days.length; i++) { const date = days[i]; const reportLocal = getReportBugsByDate(date); if (reportLocal.length > 0) { const ids = []; const bugItems = reportLocal; for (let i2 = 0; i2 < bugItems.length; i2++) { const bugItem = bugItems[i2]; if (ids.includes(bugItem.id)) { continue; } ids.push(bugItem.id); bugHistory.push({ date, bugId: bugItem.id, bugName: bugItem.name, bugUrl: bugItem.url }); } } } const { createApp, ref, reactive, h } = Vue; const app = createApp({ template: `
搜索:
前一天 后一天 复制当前 复制当天 复制当周 {{data.length}} 条
保留天数:
重新打开弹窗 设置历史记录的保留天数,默认 14 天,如果你的 localStorage 有数据丢失的情况,请减少保留天数
`, setup() { const keepReportBugsDays = ref(keepDays); const reopenBtnDisabled = ref(true); const data = ref([]); const selectedValue = ref(getTodayDate()); const options = reactive( days.map((day) => { return { label: day + " " + getDayOfWeek("zhou", day), value: day }; }) ); options.unshift({ label: "全部", value: "全部" }); const columns = ref([ { title: "序号", dataIndex: "index", key: "index", width: 60, render: (_, index) => { return index + 1; } }, { title: "日期", dataIndex: "date", key: "date", width: 120 }, { title: "ID", dataIndex: "bugId", key: "bugId", width: 100 }, { title: "名称", dataIndex: "bugName", key: "bugName", width: "calc(60vw - 760px)", ellipsis: { tooltip: true } }, { title: "URL", dataIndex: "bugUrl", key: "bugUrl", width: 200, ellipsis: { tooltip: true } }, { title: "操作", key: "actions", width: 270, render: (row) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "打开", onClick: () => window.open(row.bugUrl, "_blank") }), h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "收藏", onClick: () => { setCollectionBug(row.bugId, row.bugName, row.bugUrl); } }), h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "删除", onClick: () => { removeBugFromReport(row.bugId, selectedValue.value); data.value = data.value.filter((item) => item.bugId !== row.bugId); } }) ] } ); } } ]); const handleUpdateValue = (value) => { if (value === "全部") { data.value = bugHistory; return; } data.value = bugHistory.filter((item) => item.date === value); }; const prevDay = () => { selectedValue.value = getSpecialDayBeforeDate(selectedValue.value, 1); data.value = bugHistory.filter((item) => item.date === selectedValue.value); }; const nextDay = () => { selectedValue.value = getSpecialDayBeforeDate(selectedValue.value, -1); data.value = bugHistory.filter((item) => item.date === selectedValue.value); }; const copyCurrent = () => { const text = data.value.map((item, index) => index + 1 + ". " + item.bugId + " " + item.bugName).join("\n"); copyText(text, true); }; const copyToday = () => { const today = getTodayDate(); const todayData = bugHistory.filter((item) => item.date === today); const text = todayData.map((item, index) => index + 1 + ". " + item.bugId + " " + item.bugName).join("\n"); copyText(text, true); }; const copyWeek = () => { const dayOfWeek = ( new Date()).getDay(); const week = getDatesInRange(getSpecialDayBaseToday(1), dayOfWeek === 0 ? 7 : dayOfWeek); const weekData = bugHistory.filter((item) => week.includes(item.date)); const text = weekData.map((item, index) => index + 1 + ". " + item.bugId + " " + item.bugName).join("\n"); copyText(text, true); }; handleUpdateValue(selectedValue.value); return { days, selectedValue, options, data, columns, keepReportBugsDays, reopenBtnDisabled, handleUpdateValue, copyCurrent, copyToday, copyWeek, prevDay, nextDay, handleUpdateKeepDays: (value) => { keepReportBugsDays.value = value; reopenBtnDisabled.value = false; setKeepReportBugsDays(value); }, reopenDialog: () => { closeDialog(); setTimeout(() => { bugHistoryDialog(); }, 1e3); } }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const updateCustomBranches = (branchName, isAdd, isShowMessage = true) => { let branches = getCustomBranches(); if (isAdd) { if (branches.some((item) => item.title === branchName)) { if (isShowMessage) { showErrorMessage("短语已存在"); } return; } branches.push({ title: branchName }); if (isShowMessage) { showSuccessMessage("添加成功"); } } else { branches = branches.filter((item) => item.title !== branchName); if (isShowMessage) { showSuccessMessage("删除成功"); } } localStorage.setItem(STORAGE_KEY_MAP.CUSTOM_BRANCHES, JSON.stringify(branches)); }; const getCustomBranches = () => { return JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.CUSTOM_BRANCHES) ?? "[]"); }; const replaceCustomBranches = (branches) => { localStorage.setItem(STORAGE_KEY_MAP.CUSTOM_BRANCHES, JSON.stringify(branches)); }; function moveUp(arr, index) { if (index < 1 || index >= arr.length) { return [...arr]; } const newArr = [...arr]; [newArr[index], newArr[index - 1]] = [newArr[index - 1], newArr[index]]; return newArr; } function moveDown(arr, index) { if (index < 0 || index >= arr.length - 1) { return [...arr]; } const newArr = [...arr]; [newArr[index], newArr[index + 1]] = [newArr[index + 1], newArr[index]]; return newArr; } const customBranchDialog = () => { let appIns = null; const appId = "custom_branch_vue_app"; createDialog({ title: "自定义快捷短语", cancelText: "关闭", okText: "刷新页面", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onOk: () => { window.location.reload(); }, onMount: () => { const branches = getCustomBranches(); const { createApp, ref, h } = Vue; const app = createApp({ template: `
搜索:
新增
`, setup() { const searchValue = ref(""); const branchName = ref(""); const data = ref(branches); const columns = ref([ { title: "名称", dataIndex: "title", key: "title", width: "calc(60vw - 380px)", ellipsis: { tooltip: true } }, { title: "排序", dataIndex: "sort", key: "sort", width: 200, render: (_, index) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "↑", disabled: index === 0, onClick: () => { data.value = moveUp(data.value, index); replaceCustomBranches(data.value); } }), h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "↓", disabled: index === data.value.length - 1, onClick: () => { data.value = moveDown(data.value, index); replaceCustomBranches(data.value); } }) ] } ); } }, { title: "操作", key: "actions", width: 180, render: (row) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "删除", onClick: () => { data.value = data.value.filter((item) => item.title !== row.title); updateCustomBranches(row.title, false); } }) ] } ); } } ]); const onSearch = debounce((value) => { if (value) { data.value = data.value.filter((item) => item.title.includes(value)); } else { data.value = getCustomBranches(); } }, 500); const addBranch = debounce(() => { if (branchName.value) { data.value = [...data.value, { title: branchName.value }]; updateCustomBranches(branchName.value, true); branchName.value = ""; } }, 500); return { searchValue, branchName, data, columns, onSearch, addBranch }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const gmFetch = (options) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: options.method || "GET", url: options.url, headers: options.headers, data: options.data, onload: (response) => { resolve({ response: response.responseText, status: response.status, statusText: response.statusText }); }, onerror: (error) => { reject(new Error(`Request failed: ${error.statusText || "Unknown error"}`)); }, ontimeout: () => { reject(new Error("Request timed out")); } }); }); }; const uniqBy = (array, iteratee) => { if (!Array.isArray(array) || array.length === 0) { return []; } const normalizedIteratee = (value) => value[iteratee]; const seenKeys = new Set(); const result = []; for (const item of array) { const key = normalizedIteratee(item); if (!seenKeys.has(key)) { seenKeys.add(key); result.push(item); } } return result; }; const getGitlabConfig = () => { const config = localStorage.getItem(STORAGE_KEY_MAP.GITLAB_CONFIG); if (!config) { return null; } return JSON.parse(config || "{}"); }; const getMergeRequests = async (gitlabUrl, personalAccessToken, options) => { const params = new URLSearchParams(); if (options.projectId) { params.append("project_id", options.projectId.toString()); } if (options.createdAfter) { params.append("created_after", options.createdAfter); } if (options.createdBefore) { params.append("created_before", options.createdBefore); } if (options.state) { params.append("state", options.state); } else { params.append("state", "merged"); } params.append("per_page", "100"); params.append("order_by", "created_at"); params.append("sort", "desc"); const response = await gmFetch({ url: `${gitlabUrl}/api/v4/merge_requests?${params}`, headers: { "PRIVATE-TOKEN": personalAccessToken, "Content-Type": "application/json" } }); if (response.status < 200 || response.status >= 300) { return []; } return JSON.parse(response.response); }; const getMRCommits = async (gitlabUrl, personalAccessToken, projectId, mrIid) => { const response = await gmFetch({ url: `${gitlabUrl}/api/v4/projects/${projectId}/merge_requests/${mrIid}/commits`, headers: { "PRIVATE-TOKEN": personalAccessToken, "Content-Type": "application/json" } }); if (response.status < 200 || response.status >= 300) { return []; } return JSON.parse(response.response); }; const getBugCommits = async (gitlabUrl, personalAccessToken, bugId, options) => { const commits = []; try { const mergeRequests = await getMergeRequests(gitlabUrl, personalAccessToken, { projectId: options.projectId, createdAfter: options.createdAfter, createdBefore: options.createdBefore, state: "merged" }); for (const mr of mergeRequests) { try { const mrCommits = await getMRCommits(gitlabUrl, personalAccessToken, mr.project_id, mr.iid); const filteredCommits = mrCommits.filter((commit) => commit.message.includes(bugId)); filteredCommits.forEach((commit) => { commits.push({ commitHash: commit.id, targetBranch: mr.target_branch, gitlabUrl, projectId: mr.project_id, mrId: mr.iid, commitMessage: commit.message }); }); } catch (error) { console.error(`Failed to process MR ${mr.iid}:`, error); continue; } } } catch (error) { console.error(`Failed to get merge requests:`, error); } return commits; }; const getBugCommitsFromProjects = async (bugId, options = {}, projectIds = [2, 3]) => { const commits = []; const config = getGitlabConfig(); if (!config) { showErrorMessage("请先配置 GitLab 信息"); return commits; } const { gitlabUrl, personalAccessToken, rangeDays = 1, projectIds: defaultProjectIds = [] } = config; const projectIdsToUse = [... new Set([...projectIds, ...defaultProjectIds])]; if (!personalAccessToken) { console.warn("No personal access token provided"); return commits; } if (!projectIdsToUse || projectIdsToUse.length === 0) { console.warn("No project IDs provided"); return commits; } try { const promises = projectIdsToUse.map( (projectId) => getBugCommits(gitlabUrl, personalAccessToken, bugId, { createdAfter: getSpecialDayBeforeDate(getTodayDate(), rangeDays), ...options, projectId }) ); const results = await Promise.all(promises); commits.push(...results.flat()); return uniqBy(commits, "commitHash"); } catch (error) { console.error(`Failed to get commits from multiple projects:`, error); return commits; } }; const getProjectName = (projectId) => { return ( new Map([ [2, "bi"], [3, "core"], [24, "PPTR"], [68, "yh-plugin-workbook"] ])).get(projectId) || ""; }; const getAllCommits = async (projectId, options = {}) => { const commits = []; const config = getGitlabConfig(); if (!config) { showErrorMessage("请先配置 GitLab 信息"); return commits; } const { gitlabUrl, personalAccessToken, rangeDays: defaultRangeDays = 1 } = config; const { rangeDays = defaultRangeDays, createdAfter, createdBefore } = options; if (!personalAccessToken) { console.warn("No personal access token provided"); return commits; } if (!projectId || projectId.length === 0) { console.warn("No project IDs provided"); return commits; } const actualCreatedAfter = createdAfter || getSpecialDayBeforeDate(getTodayDate(), rangeDays); try { for (const pid of projectId) { try { const mergeRequests = await getMergeRequests(gitlabUrl, personalAccessToken, { projectId: pid, createdAfter: actualCreatedAfter, createdBefore: void 0, state: "merged" }); for (const mr of mergeRequests) { try { const mrCommits = await getMRCommits(gitlabUrl, personalAccessToken, mr.project_id, mr.iid); mrCommits.forEach((commit) => { commits.push({ commitHash: commit.id, targetBranch: mr.target_branch, gitlabUrl, projectId: mr.project_id, mrId: mr.iid, commitMessage: commit.message }); }); } catch (error) { console.error(`Failed to process MR ${mr.iid}:`, error); continue; } } } catch (error) { console.error(`Failed to get merge requests for project ${pid}:`, error); continue; } } } catch (error) { console.error("Failed to get commits:", error); } return uniqBy(commits, "commitHash"); }; const invokeFromVueAppIns = (appIns, method, ...args) => { if (appIns) { preventBoom(appIns[method], ...args); } }; const gitlabConfigDialog = () => { let appIns = null; const appId = "gitlab_config_vue_app"; createDialog({ title: "一键填写", cancelText: "关闭", bodyStyle: "width: 40vw; min-width: 615px; height: 480px", body: `
`, onOk: (closeDialog) => { invokeFromVueAppIns(appIns, "onOk", closeDialog); }, onMount: () => { const { createApp, ref } = Vue; const app = createApp({ template: `
自动将指派人指定为报告人 自动填写 commit info 自动将 bug 标记为已修复 去生成 仅限开发人员使用 仅查询已经合并后的 commit 仅查询 BI、Core 两个项目
`, setup() { const formRef = ref(null); const gitlabConfig = getGitlabConfig(); const modelRef = ref({ personalAccessToken: (gitlabConfig == null ? void 0 : gitlabConfig.personalAccessToken) || "", gitlabUrl: (gitlabConfig == null ? void 0 : gitlabConfig.gitlabUrl) || GITLAB_ORIGIN, rangeDays: (gitlabConfig == null ? void 0 : gitlabConfig.rangeDays) || 1, projectIds: (gitlabConfig == null ? void 0 : gitlabConfig.projectIds) || [2, 3], autoSaveForm: (gitlabConfig == null ? void 0 : gitlabConfig.autoSaveForm) || false }); const rules = { personalAccessToken: [ { required: true, message: "请输入 Personal Access Token" } ], gitlabUrl: [ { required: true, message: "请输入 GitLab 地址" } ], rangeDays: [ { required: true, message: "请输入查询范围(天)" } ] }; return { formRef, modelRef, rules, onOk: (closeDialog) => { var _a; (_a = formRef.value) == null ? void 0 : _a.validate((errors) => { if (!errors) { localStorage.setItem(STORAGE_KEY_MAP.GITLAB_CONFIG, JSON.stringify(modelRef.value)); closeDialog(); } }); }, onGenerateClick: () => { window.open(`${modelRef.value.gitlabUrl}/-/profile/personal_access_tokens`, "_blank"); } }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const moreAction = (opts, onSelect) => { const appId = "more_action_vue_app"; const { createApp, ref, nextTick } = Vue; const div = document.createElement("div"); div.id = appId; document.body.appendChild(div); const app = createApp({ template: ` `, setup() { const options = ref(opts); const isVisible = ref(false); const x = ref(0); const y = ref(0); return { options, isVisible, x, y, handleSelect(key) { onSelect(key); div.remove(); }, showDropdown({ posX, posY }) { isVisible.value = false; nextTick().then(() => { isVisible.value = true; x.value = posX; y.value = posY; }); }, onClickoutside() { isVisible.value = false; div.remove(); } }; } }); app.use(naive); return app.mount("#" + appId); }; const getFormItemControls = () => { const formItemControl = JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.FORM_ITEM_CONTROL) || "{}"); const list1 = FORM_ITEM_1.map((item) => { var _a; const localItem = Array.isArray(formItemControl == null ? void 0 : formItemControl.list1) ? (_a = formItemControl == null ? void 0 : formItemControl.list1) == null ? void 0 : _a.find((i) => i.label === item.label) : null; return { ...item, show: localItem ? localItem.show : true }; }); const list2 = FORM_ITEM_2.map((item) => { var _a; const localItem = Array.isArray(formItemControl == null ? void 0 : formItemControl.list2) ? (_a = formItemControl == null ? void 0 : formItemControl.list2) == null ? void 0 : _a.find((i) => i.label === item.label) : null; return { ...item, show: localItem ? localItem.show : true }; }); return { list1, list2 }; }; const showHideFormItemsByData = () => { const { list1, list2 } = getFormItemControls(); [...list1, ...list2].forEach((item) => { if (!item.show) { const elem = $(item.value); if (elem) { elem.style.display = "none"; } } }); }; const pageDetailSettingDialog = () => { let appIns = null; const appId = "page_detail_setting_vue_app"; createDialog({ title: "页面设置", okText: "刷新页面", cancelText: "关闭", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onOk: (closeDialog) => { invokeFromVueAppIns(appIns, "onOk", closeDialog); }, onDestroy: () => { if (appIns && appIns._) { preventBoom(appIns._.setupState.mouseleaveHandler); } }, onMount: () => { const { createApp, ref, h, reactive } = Vue; const app = createApp({ template: ` `, setup() { const activeKey = ref(1); const featureList2 = ref([ { title: "保存表单时校验输入内容是否规范并弹出提示弹窗", name: "showSaveConfirmDialog", show: getPageDetailControl("showSaveConfirmDialog") }, { title: "保存表单时显示日报相关 hint", name: "showHintWhenAutoAddReport", show: getPageDetailControl("showHintWhenAutoAddReport") }, { title: "添加附件按钮默认点击后打开附件上传弹窗", name: "showAttechmentDialog", show: getPageDetailControl("showAttechmentDialog") }, { title: "已识别出所属项目的 hash,点击直接打开 gitlab(关闭此功能则点击永远弹出菜单)", name: "directOpenGitlabWhenHashRecognized", show: getPageDetailControl("directOpenGitlabWhenHashRecognized") }, { title: "当没有查询到相关提交时,自动查询时间范围内所有 MR 的 commit,并弹出手动选择的弹窗", name: "showCommitManualSelectDialog", show: getPageDetailControl("showCommitManualSelectDialog") } ]); const featureColumns = ref([ { title: "功能描述", dataIndex: "title", key: "title", width: "calc(40vw - 180px)", ellipsis: { tooltip: true } }, { title: "是否开启", key: "actions", width: 180, render: (row) => { return h(naive.NSwitch, { "default-value": row.show, "checked-value": true, "unchecked-value": false, "on-update:value": () => { const nShow = !row.show; row.show = nShow; featureList2.value = [...featureList2.value]; setPageDetailControl(row.name, nShow); } }); } } ]); const mouseleaveHandler = () => { const elem = $("#show_hide_formitem_vue_app_rect"); if (elem) { elem.remove(); } }; const { list1, list2 } = getFormItemControls(); const checkboxOptions1 = reactive(list1); const checkboxOptions2 = reactive(list2); return { activeKey, featureList: featureList2, featureColumns, onOk: (closeDialog) => { if (activeKey.value === 1) { window.location.reload(); } else if (activeKey.value === 2) { closeDialog(); } }, checkboxOptions1, checkboxOptions2, mouseleaveHandler, updateHandler: (item) => { item.show = !item.show; const elem = $(item.value); if (item.show) { elem == null ? void 0 : elem.style.setProperty("display", "table-row"); } else { elem == null ? void 0 : elem.style.setProperty("display", "none"); } mouseleaveHandler(); localStorage.setItem( STORAGE_KEY_MAP.FORM_ITEM_CONTROL, JSON.stringify({ list1: checkboxOptions1, list2: checkboxOptions2 }) ); }, mouseenterHandler: (item) => { const elem = $(item.value); const rect = elem == null ? void 0 : elem.getBoundingClientRect(); const div = document.createElement("div"); div.id = `show_hide_formitem_vue_app_rect`; div.style.position = "fixed"; div.style.top = `${(rect == null ? void 0 : rect.top) || 0}px`; div.style.left = `${(rect == null ? void 0 : rect.left) || 0}px`; div.style.width = `${(rect == null ? void 0 : rect.width) || 0}px`; div.style.height = `${(rect == null ? void 0 : rect.height) || 0}px`; div.style.backgroundColor = "#9bc0e3"; div.style.zIndex = "9"; div.style.opacity = "0.8"; document.body.appendChild(div); }, onChangeActiveKey: (key) => { activeKey.value = key; } }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const updateCompleteBugs = (bugId) => { const rowId = "b" + bugId; const completedBugsStr = localStorage.getItem(STORAGE_KEY_MAP.COMPLETED_BUGS) ?? ""; let completedBugs = completedBugsStr.split(";").filter((p) => p); let result = false; if (!completedBugs.includes(rowId)) { completedBugs.push(rowId); result = true; } else { completedBugs = completedBugs.filter((bug) => bug !== rowId); } localStorage.setItem(STORAGE_KEY_MAP.COMPLETED_BUGS, completedBugs.join(";")); return result; }; const isCompletedBugsDataInvalid = () => { const completedBugsStr = localStorage.getItem(STORAGE_KEY_MAP.COMPLETED_BUGS) ?? ""; const completedBugs = completedBugsStr.split(";").filter((p) => p); return completedBugs.some((bug) => !/^b\d+$/.test(bug)); }; const ACTION_BTNS = { REPORT: "report", RESOLVED_MARK: "resolvedMark", COLLECTION: "collection" }; const DEFAULT_BTNS = [ { name: ACTION_BTNS.REPORT, title: "添加日报", show: true }, { name: ACTION_BTNS.RESOLVED_MARK, title: "解决/移除", show: true }, { name: ACTION_BTNS.COLLECTION, title: "收藏/移除", show: true } ]; const getListPageActionBtnsControl = () => { return JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.LIST_PAGE_ACTION_BTNS) || JSON.stringify(DEFAULT_BTNS)); }; const setListPageActionBtnShow = (btnName, show) => { const btns = getListPageActionBtnsControl(); const btnIndex = btns.findIndex((btn) => btn.name === btnName); if (btnIndex !== -1) { btns[btnIndex].show = show; } localStorage.setItem(STORAGE_KEY_MAP.LIST_PAGE_ACTION_BTNS, JSON.stringify(btns)); }; const getListPageActionBtnShow = (btnName) => { const btns = getListPageActionBtnsControl(); const btnIndex = btns.findIndex((btn) => btn.name === btnName); if (btnIndex === -1) { return false; } return btns[btnIndex].show; }; const showReportBtn = (td, bugId, bugName, bugUrl) => { if (!getListPageActionBtnShow(ACTION_BTNS.REPORT)) { return null; } const reportBtn = document.createElement("span"); reportBtn.title = "此功能可以通过页面顶部页面设置进行配置"; if (isBugInReport(bugId)) { reportBtn.classList.add("custom-button"); reportBtn.classList.add("plain-button"); reportBtn.innerText = "移除日报"; } else { reportBtn.classList.add("custom-button"); reportBtn.innerText = "添加日报"; } reportBtn.addEventListener("click", () => { try { if (isBugInReport(bugId)) { removeBugFromReport(bugId); reportBtn.innerText = "添加日报"; reportBtn.classList.remove("plain-button"); } else { addBugWithRepeatCheck({ id: bugId, name: bugName, url: bugUrl }); reportBtn.innerText = "移除日报"; reportBtn.classList.add("plain-button"); } } catch (error) { showErrorMessage("操作失败: " + error); } }); td.appendChild(reportBtn); return reportBtn; }; const showResolvedMarkBtn = (td, bugId) => { if (!getListPageActionBtnShow(ACTION_BTNS.RESOLVED_MARK)) { return null; } const completeBtn = document.createElement("span"); completeBtn.classList.add("custom-button"); completeBtn.innerText = "解决/移除"; completeBtn.title = "此功能可以通过页面顶部页面设置进行配置"; completeBtn.addEventListener("click", () => { try { const rowId = "b" + bugId; const tr = $("#" + rowId); if (isCompletedBugsDataInvalid()) { showErrorMessage("当前 localStorage: completed_bugs 存储数据遭受污染,之前的数据将被清空", 5e3); localStorage.setItem(STORAGE_KEY_MAP.COMPLETED_BUGS, ""); } if (tr) { const isAdd = updateCompleteBugs(bugId); if (isAdd) { tr.style.setProperty("background-color", "#c7c7c7ff"); completeBtn.classList.add("plain-button"); } else { completeBtn.classList.remove("plain-button"); tr.style.removeProperty("background-color"); } } } catch (error) { showErrorMessage("标记失败: " + error); } }); td.appendChild(completeBtn); return completeBtn; }; const showCollectionBtn = (td, bugId, bugText, bugUrl) => { if (!getListPageActionBtnShow(ACTION_BTNS.COLLECTION)) { return null; } const bugIdForCollection = "Bug " + bugId; const collectionBtn = document.createElement("span"); collectionBtn.innerText = "收藏/移除"; collectionBtn.title = "此功能可以通过页面顶部页面设置进行配置"; if (getCollectInfo(bugIdForCollection).hasCollected) { collectionBtn.classList.add("custom-button"); collectionBtn.classList.add("plain-button"); } else { collectionBtn.classList.add("custom-button"); } collectionBtn.addEventListener("click", () => { try { const { collectedBugs, hasCollected } = getCollectInfo(bugIdForCollection); let newBugs = { list: collectedBugs }; if (!hasCollected) { newBugs = { list: [...collectedBugs, { id: bugIdForCollection, title: bugText, url: bugUrl }] }; collectionBtn.classList.add("plain-button"); } else { newBugs = { list: collectedBugs.length > 0 && collectedBugs.filter((bug) => bug.id !== bugIdForCollection) }; collectionBtn.classList.remove("plain-button"); } localStorage.setItem(STORAGE_KEY_MAP.COLLECTED_BUGS, JSON.stringify(newBugs)); } catch { } }); td.appendChild(collectionBtn); return collectionBtn; }; const pageListSettingDialog = () => { let appIns = null; const appId = "page_list_setting_vue_app"; createDialog({ title: "页面设置", cancelText: "关闭", bodyStyle: "width: 60vw; height: 40vh;", body: `
`, onOk: () => { invokeFromVueAppIns(appIns, "onOk"); }, onMount: () => { const { createApp, ref, h } = Vue; const app = createApp({ template: ` `, setup() { const activeKey = ref(1); const featureList2 = ref(getListPageActionBtnsControl()); const featureColumns = ref([ { title: "功能描述", dataIndex: "title", key: "title", width: "calc(40vw - 180px)", ellipsis: { tooltip: true } }, { title: "是否显示", key: "actions", width: 180, render: (row) => { return h(naive.NSwitch, { "default-value": row.show, "checked-value": true, "unchecked-value": false, "on-update:value": () => { const nShow = !row.show; row.show = nShow; featureList2.value = [...featureList2.value]; setListPageActionBtnShow(row.name, nShow); } }); } } ]); return { activeKey, featureList: featureList2, featureColumns, onOk: () => { if (activeKey.value === 1) { window.location.reload(); } }, onChangeActiveKey: (key) => { activeKey.value = key; } }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const ZOOM_STORAGE_KEY = "zoom: " + PATHNAME; const getPageZoom = () => { return Number(localStorage.getItem(ZOOM_STORAGE_KEY) || 1); }; const setPageZoom = () => { const pageZoom = localStorage.getItem(ZOOM_STORAGE_KEY) || "1"; if (document && document.body) { const header = $("#header", document.body); const bugzillaBody = $("#bugzilla-body", document.body); const footer = $("#footer", document.body); if (header) { header.style.zoom = pageZoom; } if (bugzillaBody) { bugzillaBody.style.zoom = pageZoom; } if (footer) { footer.style.zoom = pageZoom; } } }; const updatePageZoom = (zoom) => { localStorage.setItem(ZOOM_STORAGE_KEY, zoom.toString()); setPageZoom(); }; const pageZoomDialog = () => { let appIns = null; const appId = "page_zoom_vue_app"; createDialog({ title: "页面缩放", hideOk: true, cancelText: "关闭", style: "width: 40vw; height: 205px", body: `
`, onMount: () => { const { createApp, ref } = Vue; const app = createApp({ template: `
设置页面缩放比例,以适配屏幕尺寸及分辨率(仅在当前页面生效,仅需要设置一次)
设置完毕后关闭弹窗,此页面不再弹出此提示
`, setup() { const showModal = ref(true); const zoom = ref(getPageZoom()); const updateZoom = (value) => { zoom.value = value; updatePageZoom(value); }; return { showModal, zoom, updateZoom }; } }); app.use(naive); appIns = app.mount("#page_zoom_vue_app"); } }); return appIns; }; const getSearchHistory = () => { const searchHistory = localStorage.getItem(STORAGE_KEY_MAP.SEARCH_HISTORY); if (searchHistory) { return JSON.parse(searchHistory); } return []; }; const addSearchHistory = (searchHistory) => { let history = getSearchHistory(); if (history.includes(searchHistory)) { history = history.filter((item) => item !== searchHistory); } else if (history.length >= 20) { history.pop(); } history.unshift(searchHistory); localStorage.setItem(STORAGE_KEY_MAP.SEARCH_HISTORY, JSON.stringify(history)); }; const applySearch = (searchText) => { const isBugId = /^\d+$/.test(searchText); if (isBugId) { window.open(`http://192.168.1.31/bugzilla/show_bug.cgi?id=${searchText}`, "_blank"); } else { window.open(`http://192.168.1.31/bugzilla/buglist.cgi?quicksearch=${searchText}`, "_blank"); } addSearchHistory(searchText); }; const searchHistoryList = (portal, appId) => { if (!portal) { return; } const div = document.createElement("div"); div.id = appId; portal.parentNode.insertBefore(div, portal.nextSibling); const { createApp, ref } = Vue; const app = createApp({ template: ` `, setup() { const options = ref([]); return { selectedValue: void 0, options, handleSearch(value) { if (!value) { return; } applySearch(value); }, handleShow(show) { if (show) { const searchHistory = getSearchHistory(); options.value = searchHistory.map((history) => { return { label: history, value: history }; }); } } }; } }); app.use(naive); return app.mount(`#${appId}`); }; const isEmail = (str) => { const emailRegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; return emailRegExp.test(str); }; const extractEmail = (text) => { if (!text) return ""; const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g; const result = text.match(emailRegex); return result ? result[0] : ""; }; const getLoginPerson = () => { const footer = $("#footer"); if (!footer) { return ""; } const links = $(".links", footer); if (!links) { return ""; } const person = links.lastElementChild; if (!person) { return ""; } const email = extractEmail(person.innerText.trim()); return email.includes("@") ? email.split("@")[0] : ""; }; const loadScriptByGM = (url, isModule = false) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url, onload: (res) => { const script = document.createElement("script"); script.textContent = res.responseText; if (isModule) { script.type = "module"; } document.head.appendChild(script); resolve(true); }, onerror: reject }); }); }; const readLocalStorage = () => { const res = new Map(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.includes(".") || key.includes("#") || key.includes("$") || key.includes("/") || key.includes("[") || key.includes("]")) { continue; } const data = localStorage.getItem(key); if (data) { res.set(key, data); } } return Object.fromEntries(res); }; const writeLocalStorage = (data) => { for (const key of Object.keys(data)) { if (Object.prototype.hasOwnProperty.call(data, key)) { const element = data[key]; const isJson = typeof element === "string" && (element.startsWith("{") || element.startsWith("[")); const value = isJson ? JSON.stringify(JSON.parse(element)) : element; localStorage.setItem(key, value); } } }; class FireBase { constructor(silent = false) { __publicField(this, "auth", null); __publicField(this, "db", null); __publicField(this, "silent", false); __publicField(this, "personName", ""); __publicField(this, "login", async () => { await loadScriptByGM(FIRE_BASE_SCRIPT_URL.APP); await loadScriptByGM(FIRE_BASE_SCRIPT_URL.AUTH); await loadScriptByGM(FIRE_BASE_SCRIPT_URL.DATABASE); if (!firebase.apps.length) { firebase.initializeApp(FIRE_BASE_CONFIG); } const auth = firebase.auth(); const db = firebase.database(); try { await auth.signInAnonymously(); if (!this.silent) { showSuccessMessage("登录成功"); } this.auth = auth; this.db = db; return this; } catch (error) { if (!this.silent) { showErrorMessage("登录失败"); } return null; } }); __publicField(this, "syncLocalToFirebase", async () => { if (!this.auth || !this.db) { if (!this.silent) { showErrorMessage("请先登录 Firebase"); } return; } if (!this.personName) { if (!this.silent) { showErrorMessage("请先登录 Bugzilla"); } return; } const localData = readLocalStorage(); try { await this.db.ref(this.personName).set(localData); if (!this.silent) { showSuccessMessage("数据写入成功"); } } catch (error) { if (!this.silent) { showErrorMessage("写入失败:" + error); } } }); __publicField(this, "syncFirebaseToLocal", async () => { if (!this.auth || !this.db) { if (!this.silent) { showErrorMessage("请先登录 Firebase"); } return; } if (!this.personName) { if (!this.silent) { showErrorMessage("请先登录 Bugzilla"); } return; } try { const snapshot = await this.db.ref(this.personName).once("value"); const data = snapshot.val(); if (data) { writeLocalStorage(data); if (!this.silent) { showSuccessMessage("数据读取成功"); } } else { if (!this.silent) { showErrorMessage("没有匹配到你的用户数据"); } } } catch (error) { if (!this.silent) { showErrorMessage("读取失败:" + error); } } }); this.auth = null; this.db = null; this.silent = silent; this.personName = getLoginPerson(); } } const cloudDataBase = new FireBase(); const cloudDataBaseSilent = new FireBase(true); const syncDataDialog = () => { let appIns = null; const appId = "sync_data_vue_app"; createDialog({ title: "数据同步", cancelText: "关闭", okText: "刷新页面", bodyStyle: "width: 60vw; height: 330px; max-width: 800px;", body: `
`, onOk: (closeDialog) => { invokeFromVueAppIns(appIns, "onOk", closeDialog); }, onMount: () => { const { createApp, ref } = Vue; const app = createApp({ template: ` 由于历史原因,页面缩放的数据不会被同步到云端
登录数据库 同步本地数据到云端 同步云端数据到本地
`, setup() { const isBtn1Loading = ref(false); const isBtn2Loading = ref(false); const isBtn3Loading = ref(false); const disabledBtn1 = ref(false); const disabledBtn2 = ref(true); const disabledBtn3 = ref(true); const currentStep = ref(1); const autoSyncLocalDataToCloud = ref(localStorage.getItem(STORAGE_KEY_MAP.AUTO_SYNC_LOCAL_DATA_TO_CLOUD) === "true"); return { isBtn1Loading, isBtn2Loading, isBtn3Loading, disabledBtn1, disabledBtn2, disabledBtn3, currentStep, autoSyncLocalDataToCloud, onOk: (closeDialog) => { closeDialog(); window.location.reload(); }, autoLoginDB: async () => { isBtn1Loading.value = true; await cloudDataBase.login(); isBtn1Loading.value = false; disabledBtn2.value = false; disabledBtn3.value = false; disabledBtn1.value = true; currentStep.value++; }, syncDataToCloud: async () => { isBtn2Loading.value = true; disabledBtn3.value = true; await cloudDataBase.syncLocalToFirebase(); isBtn2Loading.value = false; disabledBtn3.value = false; }, syncDataToLocal: async () => { isBtn3Loading.value = true; disabledBtn2.value = true; await cloudDataBase.syncFirebaseToLocal(); isBtn3Loading.value = false; disabledBtn2.value = false; }, updateHandler: (checked) => { autoSyncLocalDataToCloud.value = checked; localStorage.setItem(STORAGE_KEY_MAP.AUTO_SYNC_LOCAL_DATA_TO_CLOUD, checked ? "true" : "false"); } }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const isBugReportPage = () => { return PATHNAME === PathNameEnum.BUG_NEW && !isBugReportEntryPage(); }; const isBugReportEntryPage = () => { return window.location.href === window.location.origin + "/bugzilla/enter_bug.cgi"; }; const clearReportBugsDaysBefore = async (days) => { const clearDays = getDatesInRange(getSpecialDayBeforeDate( new Date(), days), 30); for (let i = 0; i < clearDays.length; i++) { const date = clearDays[i]; const yesterdayReportLocal = localStorage.getItem(date) ?? null; if (yesterdayReportLocal) { localStorage.removeItem(date); } } }; const copyUserLinksToHeader = () => { const headerLinks = $("#header .links"); const footerUserLinkLI = $("#links-saved"); if (headerLinks && footerUserLinkLI) { const footerUserLinkLICloned = footerUserLinkLI.cloneNode(); const footerUserLinkUL = $(".links", footerUserLinkLI); if (footerUserLinkUL) { footerUserLinkLICloned.style.setProperty("display", "block"); footerUserLinkUL.style.setProperty("display", "flex"); footerUserLinkUL.style.setProperty("align-items", "center"); footerUserLinkUL.style.setProperty("flex-wrap", "wrap"); footerUserLinkUL.style.setProperty("width", "100%"); footerUserLinkUL.style.setProperty("border", "none"); footerUserLinkUL.style.setProperty("border-radius", "0"); footerUserLinkUL.style.setProperty("padding", "0"); footerUserLinkUL.style.setProperty("padding-top", ""); } footerUserLinkLICloned.appendChild(footerUserLinkUL); headerLinks.appendChild(footerUserLinkLICloned); } }; const getRemotePersonMap$1 = async () => { const res = await fetch("https://mock.mengxuegu.com/mock/60c2f7ae23b1e401b1724c72/example/personMap"); const data = await res.json(); return data; }; const syncPersonMap$1 = async () => { const localPersonMap = JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.PERSONS_MAP) ?? "{}"); try { const data = await getRemotePersonMap$1(); if (data) { const mergedPersonMap = { ...data.data, ...localPersonMap }; localStorage.setItem(STORAGE_KEY_MAP.PERSONS_MAP, JSON.stringify(mergedPersonMap)); return mergedPersonMap; } return localPersonMap; } catch { return null; } }; const migrateReportBugs = () => { const days = getDatesInRange(getSpecialDayBaseToday(1), 14); for (let i = 0; i < days.length; i++) { const day = days[i]; const report = getReportBugsByDate(day); if (report && !Array.isArray(report) && report.todayReport) { const newFormatBugs = report.todayReport.map((bug) => { var _a, _b, _c; const bugId = (_a = /\s+\d+/.exec(bug)) == null ? void 0 : _a[0]; const bugName = (_b = /Bug\s+\d+\s+([^()]+)\s+\(/.exec(bug)) == null ? void 0 : _b[1].trim(); const bugUrl = (_c = /\((http:\/\/.+?)\)/.exec(bug)) == null ? void 0 : _c[1]; return { id: bugId, name: bugName, url: bugUrl }; }); localStorage.removeItem(day); localStorage.setItem(day, JSON.stringify(newFormatBugs)); } } }; const migrateCollectedBugs = () => { const oldKey = "collected-bugs"; const bugsString = localStorage.getItem(oldKey); localStorage.removeItem(oldKey); if (!bugsString) { return; } localStorage.setItem(STORAGE_KEY_MAP.COLLECTED_BUGS, bugsString); }; const migrateCompletedBugs = () => { const oldKey = "completed_bugs"; const bugsString = localStorage.getItem(oldKey); localStorage.removeItem(oldKey); if (!bugsString) { return; } localStorage.setItem(STORAGE_KEY_MAP.COMPLETED_BUGS, bugsString); }; const getPersonMap = () => { return JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.PERSONS_MAP) ?? "{}"); }; const setPersonMap = (personMap = PERSONS_MAP) => { return localStorage.setItem(STORAGE_KEY_MAP.PERSONS_MAP, JSON.stringify(personMap)); }; const hasPersonMap = () => !!localStorage.getItem(STORAGE_KEY_MAP.PERSONS_MAP); const getRemotePersonMap = async () => { const res = await fetch("https://mock.mengxuegu.com/mock/60c2f7ae23b1e401b1724c72/example/personMap"); const data = await res.json(); return data; }; const syncPersonMap = async () => { const localPersonMap = JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.PERSONS_MAP) ?? "{}"); try { const data = await getRemotePersonMap(); if (data) { const mergedPersonMap = { ...data.data, ...localPersonMap }; localStorage.setItem(STORAGE_KEY_MAP.PERSONS_MAP, JSON.stringify(mergedPersonMap)); return mergedPersonMap; } return localPersonMap; } catch { return null; } }; const createLoading = (style = "width: 400px; height: 225px;") => { const loadingNode = document.createElement("div"); loadingNode.classList.add("global-loading"); loadingNode.style = style; return loadingNode; }; const createEditorDialog = (dialogProps = {}) => { return new Promise(async (resolve) => { const loadingNode = createLoading("width: 100%; height: 100%"); const bodyNode = document.createElement("div"); bodyNode.style.width = "100%"; bodyNode.style.height = "100%"; const editorNode = document.createElement("div"); editorNode.classList.add("attachment-container"); editorNode.classList.add("hide"); bodyNode.appendChild(loadingNode); bodyNode.appendChild(editorNode); createDialog({ body: bodyNode, bodyStyle: "width: 80vw; height: 80vh;", okText: "保存", ...dialogProps }); const createEditor = () => { require.config({ paths: { vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs" } }); require(["vs/editor/editor.main"], () => { loadingNode.classList.add("hide"); editorNode.classList.remove("hide"); editorNode.classList.add("show"); resolve({ monaco, editorNode }); }); }; await loadScriptByGM(CDNScriptURLEnum.MONACO); createEditor(); }); }; const showEmailMapDialog = async () => { let editor = null; const { monaco: monaco2, editorNode } = await createEditorDialog({ bodyStyle: "width: 850px; height: 60vh", title: "邮件名英文名映射中文对照表(每周五自动同步云端数据一次)", onOk: (closeDialog) => { const value = editor.getValue(); if (value) { setPersonMap(JSON.parse(value)); closeDialog(); showSuccessMessage("更新成功"); } }, onMount: () => { const footer = $(".global-dialog-footer"); if (footer) { const syncBtn = document.createElement("button"); syncBtn.innerText = "同步云端数据"; syncBtn.classList.add("custom-button"); syncBtn.addEventListener("click", async () => { syncBtn.innerText = "同步中..."; syncBtn.classList.add("disabled-button"); try { const data = await syncPersonMap(); if (data && editor) { editor.setValue(JSON.stringify(data, null, 2)); showSuccessMessage("同步成功, 记得保存~"); syncBtn.innerText = "同步云端数据"; syncBtn.classList.remove("disabled-button"); } } catch { showErrorMessage("同步失败"); } }); footer.insertBefore(syncBtn, footer.firstChild); } } }); editor = monaco2.editor.create(editorNode, { value: "", language: "json", theme: "vs-dark", fontFamily: "Consolas", fontWeight: 600, fontSize: 14, formatOnPaste: true, hover: { enabled: true }, minimap: { enabled: true } }); editor == null ? void 0 : editor.setValue(JSON.stringify(getPersonMap(), null, 2)); }; const singleTimeDailyTask = (taskKey, callback) => { const STORAGE_KEY = "dailyTask_" + taskKey; const today = getTodayDate(); try { const lastExecutedDate = localStorage.getItem(STORAGE_KEY); if (lastExecutedDate !== today) { callback(); localStorage.setItem(STORAGE_KEY, today); } } catch (error) { console.error(`单日任务 "${taskKey}" 执行失败:`, error); } }; const singleTimeScheduledTask = (dayOfWeek, storageKey, task) => { const dayText = getDayOfWeek("zhou"); const hasExecuted = localStorage.getItem(storageKey) === "true"; if (dayText === dayOfWeek && !hasExecuted) { task(); localStorage.setItem(storageKey, "true"); } else if (dayText === dayOfWeek) { localStorage.setItem(storageKey, "true"); } else { localStorage.setItem(storageKey, "false"); } }; const showTopButtons = !window.location.pathname.includes("attachment.cgi") && !window.location.pathname.includes("show_activity.cgi"); const renderBugCommon = () => { const links = $$(".links a"); const linksUL = $(".links"); const linksLIs = $$(".links li"); const quicksearchTopInput = $("#quicksearch_top"); const quicksearchBottomInput = $("#quicksearch_bottom"); const findTopBtn = $("#find_top"); const findBottomBtn = $("#find_bottom"); const separators = $$(".links .separator"); const searchBtns = $$("input[value='Search']"); clearReportBugsDaysBefore(getKeepReportBugsDays()); migrateReportBugs(); migrateCollectedBugs(); migrateCompletedBugs(); setPageZoom(); if (!hasPersonMap()) { setPersonMap(PERSONS_MAP); syncPersonMap$1(); } if (!localStorage.getItem(STORAGE_KEY_MAP.AUTO_SYNC_LOCAL_DATA_TO_CLOUD)) { localStorage.setItem(STORAGE_KEY_MAP.AUTO_SYNC_LOCAL_DATA_TO_CLOUD, "true"); } const searchBtnHandler = (btn, input) => { btn.value = "查询/刷新"; btn.addEventListener("click", (e) => { e.preventDefault(); if (input.value) { applySearch(input.value); input.value = ""; } else { window.location.reload(); } }); }; if (findTopBtn && quicksearchTopInput) { searchBtnHandler(findTopBtn, quicksearchTopInput); } if (findBottomBtn && quicksearchBottomInput) { searchBtnHandler(findBottomBtn, quicksearchBottomInput); } if (links) { Array.from(links).forEach((link) => { link.classList.add("link-title"); }); } if (separators) { Array.from(separators).forEach((separator) => { separator.innerText = "|"; separator.classList.add("separators"); }); } if (searchBtns) { Array.from(searchBtns).forEach((submitBtn) => { submitBtn.classList.add("custom-button"); submitBtn.classList.add("plain-button"); }); } const createTopBtn = (text = "", clickFn, beforeCreate) => { const li = document.createElement("li"); const btn = document.createElement("span"); btn.innerText = text; btn.classList.add("custom-button"); preventBoom(beforeCreate, btn); btn.addEventListener("click", (e) => clickFn(e)); li.appendChild(btn); linksUL.appendChild(li); return btn; }; if (linksLIs.length > 0) { linksLIs.forEach((item, idx) => { if ([9, 10].includes(idx)) { item.remove(); } }); } if (linksUL && showTopButtons) { createTopBtn("历史记录", async () => { bugHistoryDialog(); }); createTopBtn("收藏列表", () => { const collectedBugs = getCollectedBugs(); if (collectedBugs.length === 0) { showErrorMessage("没有收藏的bug"); return; } bugCollectionDialog(); }); if (!isBugReportEntryPage()) { createTopBtn("页面缩放", async () => { pageZoomDialog(); }); } if (PATHNAME !== PathNameEnum.BUG_DETAIL && PATHNAME !== PathNameEnum.BUG_LIST) { createTopBtn("数据同步", async () => { syncDataDialog(); }); } if (PATHNAME === PathNameEnum.BUG_DETAIL) { const moreActionBtn = createTopBtn("更多操作", () => { const opts = [ { label: "页面设置", key: "pageSetting" }, { label: "中英对照", key: "emailMap" }, { label: "快捷短语", key: "quickSummary" }, { label: "一键填写", key: "gitlabConfig" }, { label: "数据同步", key: "systemSetting" } ]; const appIns = moreAction(opts, (key) => { if (key === "pageSetting") { pageDetailSettingDialog(); } if (key === "emailMap") { showEmailMapDialog(); } if (key === "quickSummary") { customBranchDialog(); } if (key === "gitlabConfig") { gitlabConfigDialog(); } if (key === "systemSetting") { syncDataDialog(); } }); const rect = moreActionBtn.getBoundingClientRect(); invokeFromVueAppIns(appIns, "showDropdown", { posX: rect.left, posY: rect.bottom }); }); } if (PATHNAME === PathNameEnum.BUG_LIST) { const moreActionBtn = createTopBtn("更多操作", () => { const opts = [ { label: "页面设置", key: "pageSetting" }, { label: "中英对照", key: "emailMap" }, { label: "数据同步", key: "systemSetting" } ]; const appIns = moreAction(opts, (key) => { if (key === "pageSetting") { pageListSettingDialog(); } if (key === "emailMap") { showEmailMapDialog(); } if (key === "systemSetting") { syncDataDialog(); } }); if (appIns && appIns._) { const rect = moreActionBtn.getBoundingClientRect(); preventBoom(appIns._.setupState.showDropdown, { posX: rect.left, posY: rect.bottom }); } }); } } searchHistoryList($("#find_top"), "search_history_list_vue_app_top"); searchHistoryList($("#find_bottom"), "search_history_list_vue_app_bottom"); copyUserLinksToHeader(); singleTimeScheduledTask(ZHOU_OF_WEEK_MAP.MONDAY, STORAGE_KEY_MAP.HASCLEAR_COMPLETED_BUGS, () => { localStorage.setItem(STORAGE_KEY_MAP.COMPLETED_BUGS, ""); }); singleTimeScheduledTask(ZHOU_OF_WEEK_MAP.FRIDAY, STORAGE_KEY_MAP.HAS_AUTO_SYNC_PERSON_MAP, syncPersonMap$1); if (localStorage.getItem(STORAGE_KEY_MAP.AUTO_SYNC_LOCAL_DATA_TO_CLOUD) === "true") { singleTimeDailyTask("autoSyncLocalDataToCloud", async () => { await cloudDataBaseSilent.login(); await cloudDataBaseSilent.syncLocalToFirebase(); }); } }; const getPersonEmail = (email) => { var _a; return (_a = email.match(/mailto:([^]+)/)) == null ? void 0 : _a[1]; }; const getReporterEmail = () => { const reporterNode = $("#bz_show_bug_column_2 .vcard .email"); const reporterEmail = getPersonEmail((reporterNode == null ? void 0 : reporterNode.href) ?? ""); return reporterEmail; }; const assignToList = () => { let appIns = null; const persons = getPersonMap(); const assignedTo = $("#assigned_to"); const assignedToOptions = $$("#assigned_to > option"); const assigneeEditBtn = $("#bz_assignee_edit_action"); const reporterEmail = getReporterEmail(); let assignToTesterBtn = null; if (assigneeEditBtn) { assigneeEditBtn.classList.remove("disabled-button"); assigneeEditBtn.innerText = "更改指派人"; } if (PATHNAME === PathNameEnum.BUG_DETAIL && (assignedTo == null ? void 0 : assignedTo.value) !== reporterEmail) { assignToTesterBtn = document.createElement("div"); assignToTesterBtn.id = "assign_to_tester_btn"; assignToTesterBtn.innerText = "转给报告人"; assignToTesterBtn.classList.add("custom-button"); assignToTesterBtn.classList.add("plain-button"); assignToTesterBtn.addEventListener("click", () => { var _a; (_a = assigneeEditBtn == null ? void 0 : assigneeEditBtn.click) == null ? void 0 : _a.call(assigneeEditBtn); invokeFromVueAppIns(appIns, "assignToTester", reporterEmail); if (PATHNAME === PathNameEnum.BUG_DETAIL && assignToTesterBtn) { assignToTesterBtn.remove(); } }); } assigneeEditBtn && assigneeEditBtn.addEventListener("mousedown", () => { assigneeEditBtn.click(); if (PATHNAME === PathNameEnum.BUG_DETAIL && assignToTesterBtn) { assignToTesterBtn.remove(); } }); const div = document.createElement("div"); div.id = "assigned_to_list_vue_app"; div.style.setProperty("max-width", "800px"); if (PATHNAME === PathNameEnum.BUG_DETAIL && assignToTesterBtn && assigneeEditBtn) { assigneeEditBtn.parentNode.insertBefore(assignToTesterBtn, assigneeEditBtn.nextElementSibling); } (assignedTo == null ? void 0 : assignedTo.parentNode).insertBefore(div, assignedTo); assignedTo == null ? void 0 : assignedTo.classList.add("bz_default_hidden"); assignedToOptions.forEach((node) => { const strMatch = node.innerText.match(/<([^]+)@(yonghongtech|test|sina).(com|cn)>/); const englishName = strMatch ? strMatch[1] : ""; node.innerText = englishName && persons[englishName] ? persons[englishName] + " - " + node.innerText.replace("-", "").replace(persons[englishName], "") : node.innerText; }); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref((assignedTo == null ? void 0 : assignedTo.value) || ""); const options = reactive( assignedToOptions.map((option) => { return { label: option.innerText, value: option.value }; }).filter((item) => !!item.label) ); const assignToTester = (newValue) => { selectedValue.value = newValue; if (assignedTo) { assignedTo.value = newValue; } }; return { selectedValue, options, handleUpdateValue(value) { if (assignedTo) { assignedTo.value = value; } }, assignToTester }; } }); app.use(naive); appIns = app.mount("#assigned_to_list_vue_app"); return appIns; }; const componentList = () => { const component = $("#component"); if (!component) { return; } const componentOptions = $$("#component > option"); const div = document.createElement("div"); div.id = "component_list_vue_app"; component.parentNode.insertBefore(div, component); component.classList.add("bz_default_hidden"); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref(component.value); const options = reactive( componentOptions.map((option) => { return { label: option.innerText, value: option.value }; }) ); return { selectedValue, options, handleUpdateValue(value) { component.value = value; const errorComponents = $$(".validation_error_text"); errorComponents.forEach((error) => { if (error.innerText === "You must select a Component for this bug.") { error.classList.add("bz_default_hidden"); } }); } }; } }); app.use(naive); return app.mount("#component_list_vue_app"); }; const featureList = () => { const feature = $("#cf_newfeatureid"); const featureOptions = $$("#cf_newfeatureid > option"); const div = document.createElement("div"); div.id = "feature_list_vue_app"; (feature == null ? void 0 : feature.parentNode).insertBefore(div, feature); feature == null ? void 0 : feature.classList.add("bz_default_hidden"); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref((feature == null ? void 0 : feature.value) || ""); const options = reactive( featureOptions.map((option) => { return { label: option.innerText, value: option.value }; }) ); return { selectedValue, options, handleUpdateValue(value) { if (feature) { feature.value = value; } } }; } }); app.use(naive); return app.mount("#feature_list_vue_app"); }; const newCCList = () => { const persons = getPersonMap(); const ccSelect = $("#newcc"); const ccSelectOptions = $$("#newcc > option"); const div = document.createElement("div"); div.id = "cc_list_vue_app"; (ccSelect == null ? void 0 : ccSelect.parentNode).insertBefore(div, ccSelect); ccSelect == null ? void 0 : ccSelect.classList.add("bz_default_hidden"); ccSelectOptions.forEach((node) => { const strMatch = node.innerText.match(/<([^]+)@(yonghongtech|test|sina).(com|cn)>/); const englishName = strMatch ? strMatch[1] : ""; node.innerText = englishName && persons[englishName] ? persons[englishName] + " - " + node.innerText.replace("-", "").replace(persons[englishName], "") : node.innerText; }); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref((ccSelect == null ? void 0 : ccSelect.value) || ""); const options = reactive( ccSelectOptions.map((option) => { return { label: option.innerText, value: option.value }; }) ); return { selectedValue, options, handleUpdateValue(value) { if (ccSelect) { ccSelect.value = value; } } }; } }); app.use(naive); return app.mount("#cc_list_vue_app"); }; const versionList = () => { const version = $("#version"); if (!version) { return; } const versionOptions = $$("#version > option"); const div = document.createElement("div"); div.id = "version_list_vue_app"; version.parentNode.insertBefore(div, version); version.classList.add("bz_default_hidden"); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref(version.value); const options = reactive( versionOptions.map((option) => { return { label: option.innerText, value: option.value }; }) ); return { selectedValue, options, handleUpdateValue(value) { version.value = value; } }; } }); app.use(naive); return app.mount("#version_list_vue_app"); }; const commitsManualSelectDialog = (commits, commentTextArea) => { let appIns = null; const appId = "commits_manual_select_dialog_vue_app"; const genCommitInfoArr = () => { return commits.map((commit) => { return { commit: getProjectName(commit.projectId) + ": " + commit.targetBranch + ": " + commit.commitHash + " " + commit.commitMessage }; }); }; const commitInfoArr = genCommitInfoArr(); createDialog({ title: "选择提交", cancelText: "关闭", hideOk: true, okText: "选择", bodyStyle: "width: 60vw; height: 60vh", body: `
`, onMount: () => { const { createApp, ref, h } = Vue; const app = createApp({ template: ` 下面是时间范围内的所有 MR 的 commit,请手动选择要填入的提交信息。此功能默认开启,若要关闭可以在页面设置中关闭
搜索:
{{data.length}} 条
`, setup() { const inputValue = ref(""); const columns = ref([ { title: "序号", dataIndex: "index", key: "index", width: 60, render: (_, index) => { return index + 1; } }, { title: "提交信息", dataIndex: "commit", key: "commit" }, { title: "操作", key: "actions", width: 100, render: (row) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "填入", onClick: () => { commentTextArea.value += row.commit + "\n"; showSuccessMessage("填入成功"); } }) ] } ); } } ]); const data = ref(commitInfoArr); return { columns, data, inputValue, onInput: () => { if (!inputValue.value) { data.value = commitInfoArr; return; } data.value = commitInfoArr.filter((commit) => commit.commit.includes(inputValue.value)); } }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const navBtnAddStyle = (node, isDisabled = false) => { node.classList.add("custom-button"); node.classList.add("plain-button"); isDisabled && node.classList.add("disabled-button"); node.style = "margin-left: 2px; padding-top: 5px !important"; if (node.innerText === "First") { node.innerText = "第一个"; } if (node.innerText === "Last") { node.innerText = "最后一个"; } if (node.innerText === "Prev") { node.innerText = "上一个"; } if (node.innerText === "Next") { node.innerText = "下一个"; } if (node.innerText === "Show last search results") { node.innerText = "显示上一次的搜索结果"; } if (node.innerText === "This bug is not in your last search results.") { node.innerText = "这个 bug 不是你上一次搜索的结果"; } }; const extractCommitHash = (str, strict = false) => { if (typeof str !== "string" || str.trim() === "") { return []; } const commitHashRegex = strict ? /[0-9a-fA-F]{40}/g : /[0-9a-fA-F]{40}|[0-9a-fA-F]{7}/g; const matches = str.match(commitHashRegex) || []; return Array.from(new Set(matches)); }; const extractProjectNameFromLine = (line) => { const projectRegex = /(bi|core)(?::|[-/[\s]|$)/i; const match = line.match(projectRegex); return match ? match[1].toLowerCase() : ""; }; const switchCommitHash2ALink = (target, customClickHandler, strict = false) => { const processLine = (line) => { const hashes = extractCommitHash(line, strict); if (hashes.length === 0) return line; const projectName = extractProjectNameFromLine(line); const sortedHashes = [...hashes].sort((a, b) => b.length - a.length); let processedLine = line; sortedHashes.forEach((hash) => { const escapedHash = hash.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const regex = new RegExp(`(^|\\W)(${escapedHash})(\\W|$)`, "g"); processedLine = processedLine.replace( regex, (_match, prefix, hashMatch, suffix) => `${prefix}${hashMatch}${suffix}` ); }); return processedLine; }; const replaceHashInPreContent = (content) => { const lines = content.split("\n"); const processedLines = lines.map((line) => processLine(line)); return processedLines.join("\n"); }; if (typeof target === "string") { return target.replace(/(]*>)([\s\S]*?)(<\/pre>)/g, (_match, preOpen, preContent, preClose) => { const replacedContent = replaceHashInPreContent(preContent); return `${preOpen}${replacedContent}${preClose}`; }); } if (target instanceof HTMLElement) { let domElements; if (["PRE", "DIV"].includes(target.tagName)) { domElements = [target]; } else { domElements = $$("pre", target); if (domElements.length === 0) { domElements = $$("div", target); } } domElements.forEach((domElement) => { const originalHtml = domElement.innerHTML; const newHtml = replaceHashInPreContent(originalHtml); domElement.innerHTML = newHtml; const hashLinks = $$(".commit-hash-link", domElement); hashLinks.forEach((link) => { link.addEventListener("click", (e) => { e.preventDefault(); const hash = link.dataset.hash || ""; const projectName = link.dataset.project || ""; customClickHandler(e, hash, projectName); }); }); }); return; } throw new Error("target 必须是 HTML 字符串或 HTMLElement 类型"); }; const openGitlabByHash = (hash, projectName) => { window.open(`${GITLAB_ORIGIN}/project/${projectName}/-/commit/${hash}`, "_blank"); }; const onFormSubmit = (form, isFinish, id, title) => { if (form) { if (isFinish) { const bug = { id, name: title, url: window.location.href }; addBugWithRepeatCheck(bug, getTodayDate(), true); updateCompleteBugs(id); } form.submit(); } }; const viewImageFullScreen = (parentElement) => { if (!parentElement) { return; } const imgs = $$("img", parentElement); imgs.forEach((img) => { img.style.cursor = "zoom-in"; img.title = "点击图片全屏查看"; const imgCloned = img.cloneNode(true); imgCloned.title = "点击图片关闭弹窗"; img.addEventListener("click", () => { const closeDialog = createDialog({ title: "查看图片(点击图片关闭弹窗)", body: imgCloned, autoFullScreen: true, hideOk: true, cancelText: "关闭" }); imgCloned.addEventListener("click", () => { closeDialog(); }); }); }); }; const createButton = (text, insertFn, clickFn) => { const button = document.createElement("div"); button.classList.add("custom-button"); button.innerText = text; button.addEventListener("click", (e) => clickFn(e, button)); insertFn(button); return button; }; const clearTextNode = (targetNode) => { const childNodes = Array.from(targetNode.childNodes); childNodes.forEach((node) => { var _a; if (node.nodeType === 3) { const textContent = (_a = node == null ? void 0 : node.textContent) == null ? void 0 : _a.trim(); if (textContent) { targetNode.removeChild(node); } else { node.textContent = ""; } } }); }; const downloadResource = (url, filename = "download") => { const a = document.createElement("a"); a.style.display = "none"; a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); }; function iframeCallback(iframeId, callback) { const iframePage = $("#" + iframeId); if (iframePage) { iframePage.onload = () => { const contentDocument = iframePage.contentDocument; callback(contentDocument); }; } } const renderBugDetail = () => { var _a, _b, _c, _d; const persons = getPersonMap(); const table1 = $("#bz_show_bug_column_1"); const table2 = $("#bz_show_bug_column_2"); const reporterNode = $("#bz_show_bug_column_2 .vcard .email"); const table2AllRows = $$("tr", table2); const reporterEmail = getPersonEmail((reporterNode == null ? void 0 : reporterNode.href) ?? ""); const reportNodes = $$(".vcard .email .fn"); const historyBtn = $("#bz_show_bug_column_2 tr:nth-child(2) a"); const editTitleBtn = $("#editme_action"); const assigneeEditBtn = $("#bz_assignee_edit_action"); const assigneeTakeBtn = $("#bz_assignee_take_action"); const markDuplicateBtn = $("#dup_id_discoverable_action"); const markDuplicateBox = $("#dup_id_discoverable"); const duplicateSettings = $("#duplicate_settings"); const replaceTitleInput = $("#summary_alias_input"); const commentTextArea = $("#comment"); const commentBox = $("#add_comment"); const commentBoxLabel = $("#add_comment label"); const attachmentDetails = $$("#attachment_table td[valign='top']:nth-child(2) a"); const saveFormBtn1 = $(".bz_alias_short_desc_container .knob-buttons input"); const saveFormBtn2 = $("#add_comment .knob-buttons input"); const bugStatus = $("#bug_status"); const resolution = $("#resolution"); const resolutionSettings = $("#resolution_settings"); const assigneeInputNode = $("#bz_assignee_input"); const assigneeEditNode = $("#bz_assignee_edit_container"); const assignToSelect = $("#assigned_to"); const bugTitleBox = $("#summary_alias_container"); const bugTitleNode = $("#short_desc_nonedit_display"); const bugIDNode = $(".bz_alias_short_desc_container a b"); const bugId = (_a = ((bugIDNode == null ? void 0 : bugIDNode.innerText) ?? "").match(/(\d+)$/)) == null ? void 0 : _a[1]; const effectNode = $("#cf_effect"); const bzSpacerNodes = $$("#bz_show_bug_column_1 tr:has(td[class='bz_section_spacer'])"); const navigationNode = $(".navigation"); const attachmentNode = $("#attachment_table .bz_attach_footer a:not(a[id='view_all'])"); const attachmentViewAll = $("#attachment_table .bz_attach_footer a[id='view_all']"); const navigationABtns = $$("#bugzilla-body .navigation a"); const navigationFontBtns = $$("#bugzilla-body .navigation i font"); const commentNodes = $$("#comments .bz_comment_table .bz_comment:not(.bz_first_comment) .bz_comment_text"); const firstComment = $(".bz_comment.bz_first_comment .bz_comment_text"); let completeBtn = null; if (table1 && table2) { const table1Width = table1.clientWidth; table2.style.setProperty("width", Math.round(document.body.clientWidth - table1Width) + "px"); } if (historyBtn) { historyBtn.innerText = "流转历史"; historyBtn.classList.add("custom-button"); historyBtn.classList.add("plain-button"); historyBtn.style.margin = "0 6px"; historyBtn.addEventListener("click", (e) => { e.preventDefault(); createDialog({ title: "流转历史", hideOk: true, cancelText: "关闭", bodyStyle: "width: 60vw; height: 60vh;", body: ` `, onMount: () => { iframeCallback("history-iframe", (contentDocument) => { const header = $("#header", contentDocument); const footer = $("#footer", contentDocument); if (header) { header.remove(); } if (footer) { footer.remove(); } }); } }); }); } reportNodes.forEach((node) => { if (Object.keys(persons).includes(node.innerText)) { node.innerText = persons[node.innerText]; } else if (node.innerText.includes("-")) { const splits = node.innerText.split("-"); const left = splits[0]; const right = splits[1]; if (/^[a-zA-Z0-9]+$/.test(left)) { node.innerText = persons[left]; } if (/^[a-zA-Z0-9]+$/.test(right)) { node.innerText = persons[right]; } } }); createButton( "复制报告人姓名", (button) => { var _a2; button.classList.add("plain-button"); (_a2 = reporterNode == null ? void 0 : reporterNode.parentNode) == null ? void 0 : _a2.insertBefore(button, reporterNode.nextSibling); }, () => { copyText(reporterNode.innerText); } ); if (bzSpacerNodes.length) { bzSpacerNodes.forEach((node) => { node.style.display = "none"; }); } if (effectNode && !effectNode.value) { effectNode.value = "本 case"; } if (bugTitleBox && bugTitleNode && bugIDNode) { const reportBtn = createButton( "日报/移除", (button) => { button.classList.add("custom-button"); if (!isBugInReport(bugId)) { button.innerText = "添加日报"; button.style.setProperty("border", "1px solid rgb(200, 200, 200)"); } else { button.classList.add("plain-button"); button.innerText = "移除日报"; button.style.setProperty("border", "1px solid #cccccc"); } bugTitleBox.parentNode.insertBefore(button, bugTitleBox.nextSibling); }, () => { if (!isBugInReport(bugId)) { addBugWithRepeatCheck({ id: bugId, name: bugTitleNode.innerText, url: window.location.href }); reportBtn.classList.add("plain-button"); reportBtn.innerText = "移除日报"; } else { removeBugFromReport(bugId); reportBtn.classList.remove("plain-button"); reportBtn.innerText = "添加日报"; } } ); createButton( "复制 COMMIT", (button) => { button.classList.add("plain-button"); bugTitleBox.parentNode.insertBefore(button, bugTitleBox.nextSibling); }, () => { copyText(`git commit -m "bug: ${bugId} 修复:${bugTitleNode.innerText}"`); } ); createButton( "复制 ID", (button) => { button.classList.add("plain-button"); bugTitleBox.parentNode.insertBefore(button, bugTitleBox.nextSibling); }, () => { copyText(bugId); } ); } if (assigneeInputNode) { assigneeInputNode.parentNode.classList.add("bz_assignee_td"); const backButton = createButton( "取消", (button) => { button.classList.add("plain-button"); button.style.display = "none"; button.style.marginRight = "0"; assigneeInputNode.parentNode.appendChild(button); }, (_, button) => { assigneeInputNode.classList.add("bz_default_hidden"); assigneeEditNode.classList.remove("bz_default_hidden"); if (button.style.display === "block") { button.style.display = "none"; const aLink = $("#bz_assignee_edit_container .email"); if (aLink) { const email = getPersonEmail(aLink.href); if (email) { assignToSelect.value = email; const assignToAppNode = $("#assigned_to_list_vue_app"); if (assignToAppNode) { assignToAppNode.remove(); assignToList(); } } } } } ); if (editTitleBtn) { bugTitleBox.classList.remove("bz_default_hidden"); replaceTitleInput.classList.add("bz_default_hidden"); editTitleBtn.innerText = "编辑标题"; editTitleBtn.classList.add("custom-button"); editTitleBtn.classList.add("plain-button"); editTitleBtn.addEventListener("click", (e) => { e.preventDefault(); bugTitleBox.classList.remove("bz_default_hidden"); replaceTitleInput.classList.add("bz_default_hidden"); const alias = $("#alias", replaceTitleInput); const shortDesc = $("#short_desc", replaceTitleInput); const replaceClone = replaceTitleInput.cloneNode(true); replaceClone.classList.remove("bz_default_hidden"); const aliasInput = $("#alias", replaceClone); const shortDescInput = $("#short_desc", replaceClone); if (aliasInput) { aliasInput.addEventListener("input", (e2) => { alias.value = e2.target.value; }); } if (shortDescInput) { shortDescInput.addEventListener("input", (e2) => { shortDesc.value = e2.target.value; }); } createDialog({ title: "更改标题", body: replaceClone, okText: "保存", onOk: (closeDialog) => { onFormSubmit(saveFormBtn1.form, false); closeDialog(); } }); }); } if (assigneeEditBtn) { assigneeEditBtn.innerText = "加载资源中..."; assigneeEditBtn.classList.add("custom-button"); assigneeEditBtn.classList.add("plain-button"); assigneeEditBtn.classList.add("disabled-button"); assigneeEditBtn.addEventListener("click", () => { var _a2; assigneeInputNode.classList.remove("bz_default_hidden"); assigneeEditNode.classList.add("bz_default_hidden"); if (assignToSelect.nextElementSibling.tagName === "BR") { (_a2 = assignToSelect.nextElementSibling) == null ? void 0 : _a2.remove(); } if (backButton.style.display === "none") { backButton.style.display = "block"; } }); } if (assigneeTakeBtn) { assigneeTakeBtn.classList.add("bz_default_hidden"); const takeNode = document.createElement("span"); takeNode.innerText = "转给自己"; takeNode.classList.add("custom-button"); takeNode.classList.add("plain-button"); const assignToPerson = assignToSelect.value; (_b = assigneeTakeBtn.parentNode) == null ? void 0 : _b.insertBefore(takeNode, assigneeTakeBtn.nextSibling); takeNode.addEventListener("click", () => { createDialog({ body: "确定将此任务转给自己?", bodyStyle: "width: 400px; height: 50px;", okText: "确定", onOk: (closeDialog) => { var _a2; assigneeTakeBtn.click(); closeDialog(); (_a2 = $("br", assigneeInputNode)) == null ? void 0 : _a2.remove(); onFormSubmit(saveFormBtn1.form, false); }, onDestroy: () => { assigneeInputNode.classList.add("bz_default_hidden"); assigneeEditNode.classList.remove("bz_default_hidden"); assignToSelect.value = assignToPerson; } }); }); } } if (bugStatus && resolution && markDuplicateBox) { const status1 = bugStatus.value; const status2 = resolution.value; completeBtn = createButton( "标记完成", (btn) => { var _a2; btn.classList.add("custom-button"); btn.classList.add("plain-button"); (_a2 = duplicateSettings.parentNode) == null ? void 0 : _a2.insertBefore(btn, markDuplicateBox); }, async () => { resolutionSettings.classList.remove("bz_default_hidden"); resolution.value = RESOLUTION.FIXED; bugStatus.value = BUG_STATUS.RESOLVED; cancelBtn.classList.remove("hide"); completeBtn && completeBtn.classList.add("hide"); markDuplicateBox.classList.add("hide"); } ); const cancelBtn = createButton( "取消", (btn) => { var _a2; btn.classList.add("plain-button"); btn.classList.add("hide"); (_a2 = duplicateSettings.parentNode) == null ? void 0 : _a2.appendChild(btn); }, () => { duplicateSettings.classList.add("bz_default_hidden"); markDuplicateBox.classList.remove("bz_default_hidden"); cancelBtn.classList.add("hide"); completeBtn && completeBtn.classList.remove("hide"); markDuplicateBox.classList.remove("hide"); bugStatus.value = status1; if (status2) { resolution.value = RESOLUTION.FIXED; } else { resolutionSettings.classList.add("bz_default_hidden"); } } ); if (markDuplicateBtn) { markDuplicateBtn.classList.add("custom-button"); markDuplicateBtn.classList.add("plain-button"); markDuplicateBtn.innerText = "标记重复"; markDuplicateBtn.addEventListener("click", (e) => { e.preventDefault(); duplicateSettings.classList.remove("bz_default_hidden"); markDuplicateBox.classList.add("bz_default_hidden"); cancelBtn.classList.remove("hide"); completeBtn && completeBtn.classList.add("hide"); }); } } if (commentBox) { clearTextNode(commentBox); const branchBox = document.createElement("div"); branchBox.style.setProperty("max-width", "610px"); branchBox.style.setProperty("display", "flex"); branchBox.style.setProperty("flex-wrap", "wrap"); if (getCustomBranches().length === 0) { replaceCustomBranches([ { title: ": " }, { title: "develop" }, { title: "v10.x" }, { title: "v11.x" }, { title: "custom_v11.0.1_huaweiDIS" }, { title: "_R4" } ]); } getCustomBranches().forEach((item) => { createButton( item.title, (btn) => { btn.classList.add("custom-button"); btn.classList.add("plain-button"); btn.classList.add("mb-5"); branchBox.appendChild(btn); }, () => { commentTextArea.value = commentTextArea.value + item.title; } ); }); (_c = commentBox == null ? void 0 : commentBox.parentElement) == null ? void 0 : _c.insertBefore(branchBox, commentBox); if (commentBoxLabel) { commentBoxLabel.classList.add("hide"); } if (commentTextArea) { commentTextArea.style.setProperty("min-width", "719px"); commentTextArea.addEventListener("focus", () => { commentTextArea.rows = 10; }); } } if (attachmentDetails.length) { const fileNames = $$("#attachment_table td[valign='top']:nth-child(0) b"); const fileTypeNodes = $$("#attachment_table td[valign='top']:nth-child(1) .bz_attach_extra_info"); attachmentDetails.forEach((attachment, index) => { var _a2, _b2, _c2, _d2, _e; attachment.classList.add("custom-button"); attachment.classList.add("plain-button"); attachment.style.margin = "6px 16px"; const url = (_a2 = attachment.href.split("&")) == null ? void 0 : _a2[0]; const fileType = ((_e = (_d2 = (_c2 = (_b2 = fileTypeNodes[index]) == null ? void 0 : _b2.innerText) == null ? void 0 : _c2.trim().match(/\(([^)]+)\)/)) == null ? void 0 : _d2[1].split(", ")) == null ? void 0 : _e[1]) ?? ""; const isViewType = fileType.includes("image") || fileType.includes("video") && !fileType.includes("wmv"); if (isViewType) { attachment.innerText = "查看"; } else { attachment.innerText = "下载"; } attachment.addEventListener("click", (e) => { var _a3; e.preventDefault(); if (fileType.includes("image")) { const loadingNode = createLoading(); const imageBodyNode = document.createElement("div"); imageBodyNode.classList.add("attachment-container"); imageBodyNode.classList.add("hide"); const imageNode = document.createElement("img"); imageNode.src = url; imageNode.style = "width: 100%; height: auto;"; imageNode.addEventListener("load", () => { imageBodyNode.classList.remove("hide"); imageBodyNode.classList.add("show"); loadingNode.classList.add("hide"); }); imageBodyNode.appendChild(imageNode); const bodyNode = document.createElement("div"); bodyNode.appendChild(loadingNode); bodyNode.appendChild(imageBodyNode); createDialog({ title: "图片详情", hideOk: true, cancelText: "关闭", body: bodyNode }); } else if (fileType.includes("video") && !fileType.includes("wmv")) { const loadingNode = createLoading(); const videoBodyNode = document.createElement("div"); videoBodyNode.classList.add("attachment-container"); videoBodyNode.classList.add("hide"); const videoNode = document.createElement("video"); videoNode.controls = true; videoNode.autoplay = true; videoNode.loop = true; videoNode.style = "width: 100%; height: auto;"; const sourceNode = document.createElement("source"); sourceNode.src = url; sourceNode.type = fileType; sourceNode.style = "width: 100%; height: auto;"; videoNode.addEventListener("canplaythrough", () => { videoBodyNode.classList.remove("hide"); videoBodyNode.classList.add("show"); loadingNode.classList.add("hide"); }); videoNode.appendChild(sourceNode); videoBodyNode.appendChild(videoNode); const bodyNode = document.createElement("div"); bodyNode.appendChild(loadingNode); bodyNode.appendChild(videoBodyNode); createDialog({ title: "视频详情", hideOk: true, cancelText: "关闭", body: bodyNode }); } else if (!isViewType) { downloadResource(url, (_a3 = fileNames[index]) == null ? void 0 : _a3.innerText); } else { showErrorMessage("文件不存在,或者不支持从此处" + attachment.innerText); } }); }); } const saveForm = (e, saveFormBtn) => { e.preventDefault(); if (!getPageDetailControl("showSaveConfirmDialog")) { onFormSubmit(saveFormBtn.form, true, bugId, bugTitleNode.innerText); return; } const tips = []; if (assignToSelect.value !== reporterEmail) { tips.push("bug 没有指给报告人"); } if (!commentTextArea.value) { tips.push("bug 没有填写备注"); } if (![BUG_STATUS.RESOLVED, BUG_STATUS.VERIFIED, BUG_STATUS.CLOSED].includes(bugStatus.value)) { tips.push("bug 状态不是 RESOLVED, VERIFIED, CLOSED 其中一个"); } if (tips.length) { createDialog({ title: "请确认", body: `
确定提交表单?
${tips.map((tip, index) => `
${index + 1}. ${tip}
`).join("")}
`, style: "transform: translate(-50%, -70%)", bodyStyle: "width: 500px;", okText: "确定提交", onOk: () => { onFormSubmit(saveFormBtn.form, true, bugId, bugTitleNode.innerText); } }); return; } onFormSubmit(saveFormBtn.form, true, bugId, bugTitleNode.innerText); }; if (saveFormBtn1) { saveFormBtn1.classList.add("custom-button"); saveFormBtn1.value = "保存表单"; saveFormBtn1.title = "此功能可以通过页面顶部页面设置进行配置"; saveFormBtn1.addEventListener("click", (e) => saveForm(e, saveFormBtn1)); } if (saveFormBtn2) { saveFormBtn2.classList.add("custom-button"); saveFormBtn2.value = "保存表单"; saveFormBtn2.title = "此功能可以通过页面顶部页面设置进行配置"; saveFormBtn2.addEventListener("click", (e) => saveForm(e, saveFormBtn2)); saveFormBtn2.parentElement.style.setProperty("width", "170px"); saveFormBtn2.parentElement.style.setProperty("display", "flex"); saveFormBtn2.parentElement.style.setProperty("align-items", "center"); const quickSaveBtn = document.createElement("span"); quickSaveBtn.classList.add("custom-button"); quickSaveBtn.innerText = "一键填写"; quickSaveBtn.title = "自动获取 commit 信息,仅限开发人员使用"; quickSaveBtn.addEventListener("click", async (e) => { const assignToTesterBtn = $("#assign_to_tester_btn"); assignToTesterBtn && assignToTesterBtn.click(); completeBtn && completeBtn.click(); const gitlabConfig = getGitlabConfig(); if (!gitlabConfig) { showErrorMessage("请先配置 GitLab 信息"); gitlabConfigDialog(); return; } const closeMessage = showSuccessMessage("正在查询相关的 commit 记录...", 1e3 * 60 * 60); const commits = await getBugCommitsFromProjects(bugId); closeMessage(); if (!commits.length && getPageDetailControl("showCommitManualSelectDialog")) { const closeMessage2 = showSuccessMessage("没有查找到与此 bug 相关的 commit,正在查询时间范围内的所有 commit...", 1e3 * 60 * 60); const allCommits = await getAllCommits([2, 3, 24, 68]); closeMessage2(); commitsManualSelectDialog(allCommits, commentTextArea); return; } commentTextArea.value = ""; commits.forEach((commit) => { commentTextArea.value += getProjectName(commit.projectId) + ": " + commit.targetBranch + ": " + commit.commitHash + " " + commit.commitMessage + "\n"; }); if (gitlabConfig.autoSaveForm) { saveForm(e, saveFormBtn2); } }); saveFormBtn2.parentElement && ((_d = saveFormBtn2.parentElement) == null ? void 0 : _d.insertBefore(quickSaveBtn, saveFormBtn2)); } if (navigationNode) { const div = document.createElement("div"); div.classList.add("flex-justify-space-between"); const parentNode = navigationNode.parentNode; parentNode.insertBefore(div, navigationNode.nextSibling); div.appendChild(navigationNode); const quickBottomBtn = document.createElement("a"); quickBottomBtn.href = "#footer"; quickBottomBtn.innerText = "快速沉底"; quickBottomBtn.classList.add("custom-button"); quickBottomBtn.classList.add("plain-button"); navigationNode.appendChild(quickBottomBtn); const collectionNode = document.createElement("div"); collectionNode.classList.add("flex-center"); setCollectionIcon(collectionNode, bugIDNode.innerText); div.appendChild(collectionNode); const collectBugNode1 = document.getElementById("collect-bug"); if (collectBugNode1) { const callback = (_, collectBugNode) => { const { collectedBugs, hasCollected } = getCollectInfo(bugIDNode.innerText); let newBugs; if (!hasCollected) { newBugs = { list: [ ...collectedBugs, { id: bugIDNode.innerText, title: bugTitleNode.innerText, url: window.location.href } ] }; if (collectBugNode.dataset.type !== "no-collected") { showErrorMessage("数据与页面状态不一致,页面刷新后重试"); setTimeout(() => { window.location.reload(); }, 1e3); return; } showSuccessMessage("收藏成功"); } else { newBugs = { list: collectedBugs.length > 0 && collectedBugs.filter((bug) => bug.id !== bugIDNode.innerText) }; if (collectBugNode.dataset.type !== "collected") { showErrorMessage("数据与页面状态不一致,页面刷新后重试"); setTimeout(() => { window.location.reload(); }, 1e3); return; } showSuccessMessage("已取消收藏"); } localStorage.setItem(STORAGE_KEY_MAP.COLLECTED_BUGS, JSON.stringify(newBugs)); setCollectionIcon(collectionNode, bugIDNode.innerText); const collectBugNode2 = document.getElementById("collect-bug"); if (collectBugNode2) { collectBugNode2.addEventListener("click", (e) => callback(e, collectBugNode2)); } }; collectBugNode1.addEventListener("click", (e) => callback(e, collectBugNode1)); } } if (attachmentNode) { attachmentNode.classList.add("custom-button"); attachmentNode.innerText = "添加附件"; attachmentNode.title = "此功能可以通过页面顶部页面设置进行配置"; if (getPageDetailControl("showAttechmentDialog")) { attachmentNode.addEventListener("click", (e) => { e.preventDefault(); createDialog({ title: "上传附件", okText: "关闭弹窗 & 刷新页面", cancelText: "关闭", bodyStyle: "width: 60vw; height: 70vh;", body: `
备注:点击 Submit 按钮上传,上传后点击右下角关闭并刷新按钮刷新整个页面,查看上传结果
`, onMount: () => { iframeCallback("attachment-iframe", (contentDocument) => { const header = $("#header", contentDocument); const footer = $("#footer", contentDocument); if (header) { header.remove(); } if (footer) { footer.remove(); } }); }, onOk: () => { window.location.reload(); } }); }); } } if (attachmentViewAll) { attachmentViewAll.remove(); } if (table2AllRows == null ? void 0 : table2AllRows.length) { table2AllRows.forEach((row) => { var _a2; if (row.firstElementChild && row.firstElementChild.classList.contains("bz_hidden_field")) { row.firstElementChild.classList.remove("bz_hidden_field"); } if (((_a2 = row.firstElementChild) == null ? void 0 : _a2.nextElementSibling) && row.firstElementChild.nextElementSibling.classList.contains("bz_hidden_field")) { row.firstElementChild.nextElementSibling.classList.remove("bz_hidden_field"); } }); } if (navigationABtns.length) { navigationABtns.forEach((node) => { navBtnAddStyle(node); }); } if (navigationFontBtns.length) { navigationFontBtns.forEach((node) => { navBtnAddStyle(node, true); }); } showHideFormItemsByData(); if (commentNodes.length) { commentNodes.forEach((node) => { switchCommitHash2ALink(node, (e, hash, projectName) => { if (getPageDetailControl("directOpenGitlabWhenHashRecognized") && projectName) { openGitlabByHash(hash, projectName); return; } const opts = [ { label: "复制 hash", key: "copy" }, { label: "从 bi 打开", key: "bi" }, { label: "从 core 打开", key: "core" }, { label: "从 pptr 打开", key: "PPTR" }, { label: "从 workbook 打开", key: "yh-plugin-workbook" } ]; const appIns = moreAction(opts, (key) => { if (key === "copy") { copyText(hash); return; } openGitlabByHash(hash, key); }); invokeFromVueAppIns(appIns, "showDropdown", { posX: e.clientX, posY: e.clientY }); }); }); } viewImageFullScreen(firstComment); }; const renderBugDetailPage = () => { if (PATHNAME === PathNameEnum.BUG_DETAIL) { renderBugDetail(); assignToList(); newCCList(); featureList(); componentList(); versionList(); } }; const getChineseName = (persons, string) => { if (persons) { if (string.includes("@")) { const name = string.split("@")[0]; return persons[name]; } } return string; }; const renderBugHistory = () => { const table = $("#bugzilla-body table"); const tds = $$("#bugzilla-body table td"); const bugLinks = $$("#bugzilla-body .bz_bug_link"); if (table) { table.style = "border-collapse: collapse"; const persons = getPersonMap(); if (Array.isArray(tds)) { tds.forEach((td) => { td.innerText = getChineseName(persons, td.innerText); }); } } if (bugLinks.length > 0) { bugLinks.forEach((link) => { link.classList.add("hide"); }); } }; const renderBugHistoryPage = () => { if (PATHNAME === PathNameEnum.BUG_HISTORY) { renderBugHistory(); } }; const getCity = () => { return localStorage.getItem(STORAGE_KEY_MAP.CITY) ?? "北京"; }; const setCity = (city) => { if (!city) { showErrorMessage("设置城市的值不能为空"); return; } return localStorage.setItem(STORAGE_KEY_MAP.CITY, city); }; async function getWeatherURL(options) { const { publicKey, privateKey, location } = options; if (!publicKey || !privateKey || !location) { throw new Error("公钥、私钥、城市location不能为空"); } const params = { ts: Math.floor(Date.now() / 1e3).toString(), ttl: options.ttl || 1800, uid: publicKey }; const sortedKeys = Object.keys(params).sort(); const paramString = sortedKeys.map((key) => `${key}=${params[key]}`).join("&"); const hmac = CryptoJS.HmacSHA1(paramString, privateKey); const base64Signature = hmac.toString(CryptoJS.enc.Base64); const sig = encodeURIComponent(base64Signature); const apiType = "now.json"; const fullParams = `${paramString}&sig=${sig}&location=${encodeURIComponent(location)}`; return `https://api.seniverse.com/v3/weather/${apiType}?${fullParams}&language=zh-Hans&unit=c`; } const API_OPTIONS = { publicKey: "Po_TCkQPEmOGYBCye", privateKey: "SyHEWyXh1IRy-0Agl", apiType: "now", ttl: 1800 }; const getWeather = async () => { try { const city = getCity(); const url = await getWeatherURL({ publicKey: API_OPTIONS.publicKey, privateKey: API_OPTIONS.privateKey, location: city, apiType: API_OPTIONS.apiType, ttl: API_OPTIONS.ttl }); const res = await fetch(url); const data = await res.json(); return data; } catch (err) { console.error("天气API错误:", err); return null; } }; const sleep = (s) => new Promise((resolve) => setTimeout(resolve, s * 1e3)); const renderQuip = () => { const quip = $(".bz_quip"); if (quip) { const quipTypes = [ { url: "http://api.qemao.com/api/yulu/?type=1", type: "毒鸡汤语录" }, { url: "http://api.qemao.com/api/yulu/?type=2", type: "祖安语录" }, { url: "http://api.qemao.com/api/yulu/?type=3", type: "社会语录" } ]; localStorage.setItem(STORAGE_KEY_MAP.QUIP_TYPE, "0"); const getText = async (urlObj) => { try { GM_xmlhttpRequest({ method: "GET", url: urlObj.url, onload: function(res) { if (res && res.status === 200) { const text = res.responseText; const quipType2 = parseInt(localStorage.getItem(STORAGE_KEY_MAP.QUIP_TYPE) ?? "0"); const quipHtml = `
${text} (点我刷新)
${quipTypes.map( (quip2, index) => ` ` ).join("")}
`; quip.innerHTML = quipHtml; const quipText = $("#quip-text"); const radios = $$("input[type='radio'][name='quipType']", quip); radios.forEach((radio) => { if (radio.value === quipType2.toString()) { radio.checked = true; } radio.addEventListener("change", () => { const quipType3 = radio.value; localStorage.setItem(STORAGE_KEY_MAP.QUIP_TYPE, quipType3); getText(quipTypes[+quipType3]); }); }); if (quipText) { quipText.addEventListener("click", () => { getText(quipTypes[quipType2]); }); } } } }); } catch (error) { console.error(error); } }; const quipType = parseInt(localStorage.getItem(STORAGE_KEY_MAP.QUIP_TYPE) ?? "0"); getText(quipTypes[quipType]); } }; const renderWeather = () => { const queryInfo = $(".bz_query_timestamp"); if (queryInfo) { queryInfo.innerHTML = "加载中..."; const settingCity = () => { const input = document.createElement("input"); input.style.setProperty("width", "100%"); input.value = getCity(); createDialog({ title: "设置城市", bodyStyle: "width: 400px; height: 60px", body: input, onOk: async (closeDialog) => { const value = input.value; if (value) { setCity(value); showSuccessMessage("正在验证当前城市是否存在", 2e3); const res = await getWeather(); await sleep(2); if (res && res.status_code && res.status_code.includes("AP")) { showErrorMessage("当前城市不存在,为你设置为默认城市:北京", 3e3); input.value = "北京"; setCity(input.value); return; } closeDialog(); getWeatherUI(); } else if (!value) { showErrorMessage("请输入城市名称"); } } }); }; const getWeatherUI = async () => { try { await loadScriptByGM(CDNScriptURLEnum.CRYPTO_JS); const res = await getWeather(); if (!res) { showErrorMessage("获取天气失败"); return; } const now = res.results[0].now; if (!now) { return; } const divider = `|`; const weatherArr = [ `${getCity()} `, getDayOfWeek("zhou"), now.text, now.temperature + "度" ]; queryInfo.innerHTML = weatherArr.map((it, index) => { if (index === weatherArr.length - 1) { return it; } else { return it + divider; } }).join(""); const refreshBtn = document.createElement("span"); refreshBtn.title = "刷新天气"; refreshBtn.style.display = "inline-block"; refreshBtn.style.marginLeft = "10px"; refreshBtn.style.transform = "translateY(2px)"; refreshBtn.innerHTML = ``; refreshBtn.classList.add("cursor-pointer"); refreshBtn.addEventListener("click", async () => { refreshBtn.firstElementChild.classList.add("self-rotate"); await sleep(0.7); await getWeatherUI(); showSuccessMessage("刷新成功"); }); queryInfo.appendChild(refreshBtn); const settingCityBtn = $("#setting-city"); if (settingCityBtn) { settingCityBtn.addEventListener("click", settingCity); } } catch (error) { console.error(error); } }; getWeatherUI(); } }; const renderBugList = () => { const longFormat = $("#long_format"); const xml = $("#xml"); const timeSummary = $("#timesummary"); const remember = $("#remember"); const bzIdColumns = $$(".bz_id_column a"); const bzShortDescColumns = $$(".bz_short_desc_column a"); const buglistRows = $$(".bz_buglist .bz_bugitem"); if (longFormat) { longFormat.classList.add("custom-button"); longFormat.classList.add("plain-button"); longFormat.style.marginLeft = "0"; } if (xml) { xml.classList.add("custom-button"); xml.classList.add("plain-button"); xml.style.marginLeft = "0"; } if (timeSummary) { timeSummary.classList.add("custom-button"); timeSummary.classList.add("plain-button"); timeSummary.style.marginLeft = "0"; } if (remember) { remember.classList.add("custom-button"); remember.classList.add("plain-button"); remember.style.marginLeft = "0"; } if (bzIdColumns) { bzIdColumns.forEach((node) => { const td = node.parentNode; td.style.paddingLeft = "0px"; td.style.width = "100px"; node.classList.add("custom-button"); node.classList.add("plain-button"); node.addEventListener("click", (e) => { e.preventDefault(); window.open(node.href, "_blank"); }); }); } if (bzShortDescColumns) { bzShortDescColumns.forEach((node) => { node.addEventListener("click", (e) => { e.preventDefault(); window.open(node.href, "_blank"); }); }); } if (buglistRows.length) { const persons = getPersonMap(); buglistRows.forEach((node, index) => { const children = Array.from(node.children); children.forEach((child) => { const isSpan = child.firstElementChild && child.firstElementChild.tagName === "SPAN"; if (isEmail(child.innerText)) { child.innerText = getChineseName(persons, child.innerText); } else if (isSpan) { const span = child.firstElementChild; if (span.title && isEmail(span.title)) { span.innerText = getChineseName(persons, span.title); } } }); const bugId = bzIdColumns[index].innerText; const bugTextNode = $(".bz_short_desc_column a", node); const bugText = bugTextNode.innerText; const bugUrl = bugTextNode.href; const td = document.createElement("td"); td.style.minWidth = "90px"; showReportBtn(td, bugId, bugText, bugUrl); const completeBtn = showResolvedMarkBtn(td, bugId); showCollectionBtn(td, bugId, bugText, bugUrl); const widthMap = new Map([ [1, 90], [2, 180], [3, 270] ]); td.style.width = `${widthMap.get(td.children.length) ?? 100}px`; node.appendChild(td); if (getListPageActionBtnShow(ACTION_BTNS.RESOLVED_MARK)) { const completedBugsStr = localStorage.getItem(STORAGE_KEY_MAP.COMPLETED_BUGS) ?? ""; const completedBugs = completedBugsStr.split(";").filter((p) => p); if (completeBtn && completedBugs.includes(node.id)) { completeBtn.classList.add("plain-button"); node.style.setProperty("background-color", "#c7c7c7ff"); } } }); } renderQuip(); renderWeather(); }; const renderBugListPage = () => { if (PATHNAME === PathNameEnum.BUG_LIST) { renderBugList(); } }; const ccList = () => { const persons = getPersonMap(); const ccSelect = $("#cc"); if (!ccSelect) { return; } const ccSelectOptions = $$("#cc > option"); const div = document.createElement("div"); div.id = "cc_list_vue_app"; ccSelect.parentNode.insertBefore(div, ccSelect); ccSelect.classList.add("bz_default_hidden"); ccSelectOptions.forEach((node) => { const strMatch = node.innerText.match(/<([^]+)@(yonghongtech|test|sina).(com|cn)>/); const englishName = strMatch ? strMatch[1] : ""; node.innerText = englishName && persons[englishName] ? persons[englishName] + " - " + node.innerText.replace("-", "").replace(persons[englishName], "") : node.innerText; }); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref(ccSelect.value); const options = reactive( ccSelectOptions.map((option) => { return { label: option.innerText, value: option.value }; }) ); return { selectedValue, options, handleUpdateValue(value) { ccSelect.value = value; } }; } }); app.use(naive); return app.mount("#cc_list_vue_app"); }; const updateQuickSummary = (text, isAdd, isShowMessage = true) => { let quickSummary = getQuickSummary(); if (isAdd) { if (quickSummary.some((item) => item.title === text)) { if (isShowMessage) { showErrorMessage("快捷短语已存在"); } return; } quickSummary.push({ title: text }); if (isShowMessage) { showSuccessMessage("添加成功"); } } else { quickSummary = quickSummary.filter((item) => item.title !== text); if (isShowMessage) { showSuccessMessage("删除成功"); } } localStorage.setItem(STORAGE_KEY_MAP.QUICK_SUMMARY, JSON.stringify(quickSummary)); }; const getQuickSummary = () => { return JSON.parse(localStorage.getItem(STORAGE_KEY_MAP.QUICK_SUMMARY) ?? "[]"); }; const replaceQuickSummary = (quickSummary) => { localStorage.setItem(STORAGE_KEY_MAP.QUICK_SUMMARY, JSON.stringify(quickSummary)); }; const quickSummaryDialog = () => { let appIns = null; const appId = "quick_summary_vue_app"; createDialog({ title: "自定义快捷短语", cancelText: "关闭", okText: "刷新页面", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onOk: () => { window.location.reload(); }, onMount: () => { const quickSummary = getQuickSummary(); const { createApp, ref, h } = Vue; const app = createApp({ template: `
搜索:
新增
`, setup() { const searchValue = ref(""); const quickSummaryText = ref(""); const data = ref(quickSummary); const columns = ref([ { title: "名称", dataIndex: "title", key: "title", width: "calc(60vw - 380px)", ellipsis: { tooltip: true } }, { title: "排序", dataIndex: "sort", key: "sort", width: 200, render: (_, index) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "↑", disabled: index === 0, onClick: () => { data.value = moveUp(data.value, index); replaceQuickSummary(data.value); } }), h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "↓", disabled: index === data.value.length - 1, onClick: () => { data.value = moveDown(data.value, index); replaceQuickSummary(data.value); } }) ] } ); } }, { title: "操作", key: "actions", width: 180, render: (row) => { return h( naive.NSpace, { horizontal: true }, { default: () => [ h(naive.NButton, { strong: true, tertiary: true, size: "small", textContent: "删除", onClick: () => { data.value = data.value.filter((item) => item.title !== row.title); updateQuickSummary(row.title, false); } }) ] } ); } } ]); const onSearch = debounce((value) => { if (value) { data.value = data.value.filter((item) => item.title.includes(value)); } else { data.value = getQuickSummary(); } }, 500); const addQuickSummary = debounce(() => { if (quickSummaryText.value) { data.value = [...data.value, { title: quickSummaryText.value }]; updateQuickSummary(quickSummaryText.value, true); quickSummaryText.value = ""; } }, 500); return { searchValue, quickSummaryText, data, columns, onSearch, addQuickSummary }; } }); app.use(naive); appIns = app.mount("#" + appId); } }); return appIns; }; const countLineBreaks = (str) => { if (!str) { return 0; } return (str.match(/[\n\r]/g) || []).length; }; const validateSummary = (value) => { const errorComponents = $$(".validation_error_text"); if (value) { errorComponents.forEach((error) => { if (error.innerText === "You must enter a Summary for this bug.") { error.classList.add("bz_default_hidden"); } }); } else { errorComponents.forEach((error) => { if (error.innerText === "You must enter a Summary for this bug.") { error.classList.remove("bz_default_hidden"); } }); } }; const renderBugReport = () => { var _a, _b, _c, _d, _e, _f; const newBugForm = $("#Create"); const allTableCells = newBugForm ? $$("table tr td", newBugForm) : null; const descriptionTextArea = $("#comment"); const versionLabel = $("#field_label_version"); const guessNote = $("#os_guess_note"); const commit = $("#commit"); const shortDesc = $("#short_desc"); const attachFalseInput = $("#attachment_false input"); const attachTrueInput = $("#attachment_true input"); const attatchData = $("#attachment_true .attachment_entry input"); const priority = $("#field_label_priority"); const topTip = $("form table tr"); const possibleDuplicatesContainer = $("#possible_duplicates_container"); const assignedToHelpLink = $("#field_label_assigned_to .field_help_link"); if (descriptionTextArea) { descriptionTextArea.onfocus = null; descriptionTextArea.addEventListener("focus", (e) => { e.preventDefault(); e.stopPropagation(); }); const col = descriptionTextArea.parentElement; for (let i = 0; i < col.children.length; i++) { const child = col.children[i]; if (child.tagName === "BR") { child.remove(); } } } if (Array.isArray(allTableCells)) { allTableCells.forEach((cell) => { var _a2; const textContent = ((_a2 = cell == null ? void 0 : cell.textContent) == null ? void 0 : _a2.trim().replace(/\s+/g, "")) || ""; if ((cell.colSpan === 4 || cell.colSpan === 3) && cell.childElementCount === 0 && textContent === "") { cell.remove(); } }); } if (versionLabel) { versionLabel.rowSpan = 1; versionLabel.nextElementSibling.rowSpan = 1; } if (guessNote) { ((_a = guessNote.parentNode) == null ? void 0 : _a.firstElementChild).colSpan = 1; guessNote.firstElementChild.innerText = "已经预填部分字段,请自行检查正确与否"; } if (commit) { commit.classList.add("custom-button"); commit.classList.add("margin-0"); commit.value = "提交 bug"; commit.addEventListener("mouseup", () => { const timer = setTimeout(() => { const errorComponents = $$(".validation_error_text"); errorComponents.forEach((error) => { if (error.innerText === "You must enter a Summary for this bug.") { error.style.setProperty("position", "absolute"); error.style.setProperty("top", "8px"); error.style.setProperty("right", "160px"); } }); clearTimeout(timer); }, 50); }); } if (shortDesc) { const quickSummaryBox = document.createElement("tr"); const quickSummaryBoxTitle = document.createElement("th"); const quickSummaryBoxCell = document.createElement("td"); quickSummaryBoxCell.colSpan = 3; quickSummaryBox.appendChild(quickSummaryBoxTitle); quickSummaryBox.appendChild(quickSummaryBoxCell); quickSummaryBoxCell.style.setProperty("width", "650px"); quickSummaryBoxCell.style.setProperty("display", "flex"); quickSummaryBoxCell.style.setProperty("flex-wrap", "wrap"); if (getQuickSummary().length === 0) { replaceQuickSummary([{ title: "" }]); } getQuickSummary().forEach((item) => { createButton( item.title, (btn) => { btn.classList.add("custom-button"); btn.classList.add("plain-button"); btn.classList.add("margin-0"); btn.classList.add("mr-5"); btn.classList.add("mb-5"); quickSummaryBoxCell.appendChild(btn); }, () => { const descText = shortDesc.value + item.title; shortDesc.value = descText; validateSummary(descText); } ); }); (_b = possibleDuplicatesContainer == null ? void 0 : possibleDuplicatesContainer.parentElement) == null ? void 0 : _b.insertBefore(quickSummaryBox, possibleDuplicatesContainer); shortDesc.parentElement.style.setProperty("display", "flex"); shortDesc.parentElement.style.setProperty("align-items", "center"); shortDesc.parentElement.style.setProperty("width", "800px"); shortDesc.parentElement.style.setProperty("position", "relative"); shortDesc.addEventListener("input", (e) => { var _a2; return validateSummary((_a2 = e == null ? void 0 : e.target) == null ? void 0 : _a2.value); }); const div = document.createElement("div"); div.classList.add("custom-button"); div.innerText = "自定义快捷短语"; div.addEventListener("click", () => { quickSummaryDialog(); }); shortDesc.parentElement.insertBefore(div, shortDesc.nextElementSibling); } const btnGroup = document.createElement("div"); btnGroup.classList.add("mt-5"); btnGroup.style.setProperty("display", "flex"); btnGroup.style.setProperty("justify-content", "flex-start"); btnGroup.style.setProperty("align-items", "center"); (descriptionTextArea == null ? void 0 : descriptionTextArea.parentElement) && descriptionTextArea.parentElement.appendChild(btnGroup); const switchBtn = createButton( "切换为普通输入框", (btn) => { btn.classList.add("custom-button"); btn.classList.add("margin-0"); btn.classList.add("mr-5"); btn.title = "切换为普通输入框后,不支持切回富文本"; if (descriptionTextArea == null ? void 0 : descriptionTextArea.parentElement) { btnGroup.appendChild(btn); } }, () => { switchBtn.remove(); const descriptionCKEEditor = $("#cke_comment"); if (descriptionCKEEditor) { descriptionCKEEditor.style.setProperty("display", "none"); } descriptionTextArea == null ? void 0 : descriptionTextArea.style.setProperty("display", "block"); descriptionTextArea == null ? void 0 : descriptionTextArea.style.setProperty("visibility", "visible"); if (descriptionTextArea) { descriptionTextArea.value = localStorage.getItem(STORAGE_KEY_MAP.NEW_BUG_COMMENT_TEMPLATE) || COMMENT_DEFAULT_TEMPLATE_1; } if (descriptionTextArea) { descriptionTextArea.rows = countLineBreaks((descriptionTextArea == null ? void 0 : descriptionTextArea.value) || "") + 2; } createButton( "将当前描述内容设置为默认模板", (btn) => { btn.classList.add("custom-button"); btn.classList.add("margin-0"); btn.classList.add("mr-5"); btn.classList.add("plain-button"); if (descriptionTextArea == null ? void 0 : descriptionTextArea.parentElement) { btnGroup.appendChild(btn); } }, () => { if (!(descriptionTextArea == null ? void 0 : descriptionTextArea.value)) { showErrorMessage("请输入模板内容"); return; } localStorage.setItem(STORAGE_KEY_MAP.NEW_BUG_COMMENT_TEMPLATE, (descriptionTextArea == null ? void 0 : descriptionTextArea.value) || ""); showSuccessMessage("模板已更新"); } ); createButton( "使用模板1", (btn) => { btn.classList.add("custom-button"); btn.classList.add("plain-button"); btn.title = COMMENT_DEFAULT_TEMPLATE_1; if (descriptionTextArea == null ? void 0 : descriptionTextArea.parentElement) { btnGroup.appendChild(btn); } }, () => { if (descriptionTextArea) { descriptionTextArea.rows = 24; descriptionTextArea.value = COMMENT_DEFAULT_TEMPLATE_1; } } ); createButton( "使用模板2", (btn) => { btn.classList.add("custom-button"); btn.classList.add("plain-button"); btn.title = COMMENT_DEFAULT_TEMPLATE_2; if (descriptionTextArea == null ? void 0 : descriptionTextArea.parentElement) { btnGroup.appendChild(btn); } }, () => { if (descriptionTextArea) { descriptionTextArea.rows = 19; descriptionTextArea.value = COMMENT_DEFAULT_TEMPLATE_2; } } ); } ); if (attachFalseInput && attachTrueInput && attatchData) { attachFalseInput.value = "上传附件"; attachTrueInput.value = "取消上传"; attachFalseInput.classList.add("custom-button"); attachFalseInput.classList.add("margin-0"); attachFalseInput.classList.add("plain-button"); attachTrueInput.classList.add("custom-button"); attachTrueInput.classList.add("margin-0"); attachTrueInput.classList.add("plain-button"); const uploadFileBtn = document.createElement("span"); const fileName = document.createElement("span"); uploadFileBtn.classList.add("custom-button"); uploadFileBtn.classList.add("margin-0"); uploadFileBtn.classList.add("plain-button"); uploadFileBtn.innerText = "重新上传"; fileName.classList.add("bug-new_filename"); attatchData.classList.add("bz_default_hidden"); (_c = attatchData.parentNode) == null ? void 0 : _c.insertBefore(fileName, attatchData.nextElementSibling); (_d = attatchData.parentNode) == null ? void 0 : _d.insertBefore(uploadFileBtn, attatchData.nextElementSibling); attachFalseInput.addEventListener("click", () => { attatchData.click(); }); uploadFileBtn.addEventListener("click", () => { attatchData.click(); }); attatchData.addEventListener("change", (e) => { const pathArr = e.target.value.split("\\"); const sizeM = e.target.files[0].size / 1024; fileName.innerText = "文件名:" + pathArr[pathArr.length - 1] + " 大小:" + sizeM.toFixed(2) + "kb"; }); } if (topTip) { topTip.remove(); } if (priority) { (_f = (_e = priority.parentElement) == null ? void 0 : _e.firstElementChild) == null ? void 0 : _f.remove(); } if (assignedToHelpLink) { assignedToHelpLink.style.setProperty("color", "var(--primaryColor)"); } }; const renderBugReportEntry = () => { const products = $$("table th[valign='top'] a"); const productDescs = $$("table td[valign='top']"); if (products.length) { products.forEach((p) => { p.classList.add("custom-button"); p.addEventListener("click", (e) => { e.preventDefault(); window.open(p.href, "_blank"); }); }); } if (productDescs.length) { productDescs.forEach((pd) => { pd.vAlign = "center"; }); } }; const renderBugReportPage = () => { if (PATHNAME === PathNameEnum.BUG_NEW) { if (isBugReportPage()) { renderBugReport(); componentList(); assignToList(); versionList(); featureList(); ccList(); } if (isBugReportEntryPage()) { renderBugReportEntry(); } } }; const renderBugReportEntryPage = () => { if (PATHNAME === PathNameEnum.BUG_NEW_ENTRY) { renderBugReportEntry(); } }; const renderBugProcess = () => { const bugLink = $("#bugzilla-body .bz_bug_link"); if (bugLink) { bugLink.innerText = "返回 " + bugLink.innerText; bugLink.classList.add("custom-button"); } }; const renderBugProcessPage = () => { if (PATHNAME === PathNameEnum.BUG_PROCESS) { renderBugProcess(); } }; const countDownCallback = (callback, count) => { let timer = null; timer = setInterval(() => { count--; if (count <= 0) { preventBoom(callback); timer && clearInterval(timer); } }, 1e3); return () => clearInterval(timer); }; try { async function setup() { const clearTimer = countDownCallback(() => { showSuccessMessage("加载外部资源时间过长,请检查网络连接或 VPN 状态", 5e3); }, 5); await loadScriptByGM(CDNScriptURLEnum.VUE); await loadScriptByGM(CDNScriptURLEnum.NAIVE_UI); clearTimer(); renderBugCommon(); renderBugListPage(); renderBugDetailPage(); renderBugReportPage(); renderBugReportEntryPage(); renderBugAttachmentPage(); renderBugHistoryPage(); renderBugProcessPage(); } setup(); } catch (error) { console.error(error); } })();