// ==UserScript== // @name Bugzilla Script // @namespace xiwenge // @version 0.64.0 // @description 美化增强 Bugzilla 页面 // @author xiwenge // @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_xmlhttpRequest // @grant GM_setClipboard // @run-at document-end // ==/UserScript== (function () { "use strict"; const style = ` /** * 全局样式 * 在这里书写的样式会被编译为 style/index.ts,成为一个 js 模块 */ 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: 0px; 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; inset: 0; opacity: 1; transition-duration: 0.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; 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: #ffffff !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: rgba(0, 0, 0, 0.25) !important; background: rgba(0, 0, 0, 0.04) !important; box-shadow: none !important; pointer-events: none !important; } .disabled-button:hover { cursor: not-allowed !important; border-color: #d9d9d9 !important; color: rgba(0, 0, 0, 0.25) !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 { height: 28px; } #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 { display: none; } #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: #ffffff; border-radius: 4px; transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out; animation: message-fadein 0.5s ease-in-out forwards; box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.2); } .global-message_text { margin-left: 8px; font-size: 14px; } @keyframes message-fadein { 0% { opacity: 0.5; top: 10px; } 100% { opacity: 1; top: 30px; } } @keyframes loading { 0% { transform: rotate(0deg); } 100% { 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: #ffffff; 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: rgba(0, 0, 0, 0.5); } .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; } .hide { display: none; } .cursor-pointer:hover { cursor: pointer; } .text-underline { text-decoration: underline; } .font-size-16px { font-size: 16px; } .font-bold { font-weight: bold; } .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: bold; transition: all 0.3s ease-in-out; } .link-title:hover { color: var(--primaryColor) !important; text-decoration: unset !important; } .separator { margin: 0 6px 0 4px; color: #cccccc; } .links .form a[title="Quicksearch Help"] { margin-left: 6px; } #attachment_table { border: 1px solid #d9d9d9; } #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; } .bz_short_desc_column a { color: #595959; font-weight: bold; } #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: bold; 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(0.5px, 1px); } #setting-city { display: inline-block; color: var(--primaryColor); } #setting-city:hover { cursor: pointer; } #setting-city svg { transition: all 0.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: grey; 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 { user-select: none; } .clear-br-label:hover { cursor: pointer; color: var(--primaryColorHover); } `; var MessageTypeEnum = /* @__PURE__ */ ((MessageTypeEnum2) => { MessageTypeEnum2["SUCCESS"] = "success"; MessageTypeEnum2["ERROR"] = "error"; return MessageTypeEnum2; })(MessageTypeEnum || {}); const applyCallBack = (fun, ...args) => { if (fun && typeof fun === "function") { return fun(...args); } }; var CDNScriptURLEnum = /* @__PURE__ */ ((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 createDialog = ({ title = "", body, footer, maskTransparent = false, okText = "确定", cancelText = "取消", onOk, onCancel, hideOk = false, hideCancel = false, className = "", bodyClassName = "", style: style2 = "", bodyStyle = "", 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 existDialogNodes = document.querySelectorAll(".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.width = "calc(100% - 64px)"; headerTitle.style.cursor = "move"; headerTitle.appendChild(titleNode); titleNode.style.transform = "translateX(-32px)"; 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 = document.querySelectorAll(".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) { 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) { 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%"; } }); headerBtn.appendChild(closeBtn); headerBtn.appendChild(exitFullScreenBtn); headerBtn.appendChild(fullScreenBtn); header.appendChild(headerBtn); 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 (style2) { dialogNode.style = style2; } 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", () => { applyCallBack(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 applyCallBack(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") { applyCallBack(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(); }; const createLoading = (style2 = "width: 400px; height: 225px;") => { const loadingNode = document.createElement("div"); loadingNode.classList.add("global-loading"); loadingNode.style = style2; return loadingNode; }; const loadScriptByGM = (url) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url, onload: (res) => { const script = document.createElement("script"); script.textContent = res.responseText; document.head.appendChild(script); resolve(true); }, onerror: reject, }); }); }; 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({ // @ts-ignore monaco, editorNode, }); }); }; await loadScriptByGM(CDNScriptURLEnum.MONACO); createEditor(); }); }; const showMessage = (type, message, duration = 2e3) => { const existMessageNode = document.querySelector(".global-message"); if (existMessageNode) { document.body.removeChild(existMessageNode); } const svgSuccess = ``; const svgError = ``; const svgMap = /* @__PURE__ */ 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 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 COMMENT_DEFAULT_TEMPLATE_3 = ` 📌【复现条件】 ┌──────────────────────────────────────────────────────────┐ 1. └──────────────────────────────────────────────────────────┘ 🔍【复现步骤】 ┌──────────────────────────────────────────────────────────┐ 1. └──────────────────────────────────────────────────────────┘ ✅【预期结果】 ┌──────────────────────────────────────────────────────────┐ 1. └──────────────────────────────────────────────────────────┘ ❌【实际结果】 ┌──────────────────────────────────────────────────────────┐ 1. └──────────────────────────────────────────────────────────┘ 💡【备注信息】 ┌──────────────────────────────────────────────────────────┐ 1. └──────────────────────────────────────────────────────────┘ `; const BUG_STATUS = { NEW: "NEW", ASSIGNED: "ASSIGNED", REOPENED: "REOPENED", VERIFIED: "VERIFIED", RESOLVED: "RESOLVED", CLOSED: "CLOSED", }; const RESOLUTION = { FIXED: "FIXED", INVALID: "INVALID", WONTFIX: "WONTFIX", DUPLICATE: "DUPLICATE", WORKSFORME: "WORKSFORME", MOVED: "MOVED", FIX_NEXT_VER: "FIX_NEXT_VER", }; 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", AUTO_CLEAR_FIRST_COMMENT_BR: "autoClearFirstCommentBr", PAGE_DETAIL_CONTROL: "pageDetailControl", }; const GITLAB_ORIGIN = "http://192.168.1.180:8888"; 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) { showMessage(MessageTypeEnum.ERROR, "此bug已收藏"); return; } collectedBugs.push({ id, title, url }); localStorage.setItem( STORAGE_KEY_MAP.COLLECTED_BUGS, JSON.stringify({ list: collectedBugs }), ); showMessage(MessageTypeEnum.SUCCESS, "收藏成功"); }; const setCollectionIcon = (collectionNode, id) => { if (!collectionNode) { return; } collectionNode.innerHTML = getCollectInfo(id).hasCollected ? ` ` : ` `; }; const copyText = (text, showDetail = false) => { if (!text) { showMessage(MessageTypeEnum.ERROR, "没有可复制的内容"); return; } if (typeof GM_setClipboard !== "function") { showMessage(MessageTypeEnum.ERROR, "当前环境不支持复制功能"); return; } if (!showDetail) { GM_setClipboard(text, "text", () => { showMessage(MessageTypeEnum.SUCCESS, "复制成功"); }); return; } showTextDetail(text, (body) => { createDialog({ title: "内容详情", body, okText: "确定复制", onOk: (closeDialog) => { GM_setClipboard(text, "text", () => { showMessage(MessageTypeEnum.SUCCESS, "复制成功"); 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"); applyCallBack(cb, body); }; 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: "星期六", }; const getTodayDate = () => { const today = /* @__PURE__ */ new Date(); return today.toISOString().split("T")[0]; }; const getSpecialDayBaseToday = (day) => { const today = /* @__PURE__ */ 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]; }; const DEFAULT_PAGE_DETAIL_CONTROL = { showSaveConfirmDialog: true, showHintWhenAutoAddReport: true, showAttechmentDialog: true, }; const setPageDetailControl = (control) => { localStorage.setItem( STORAGE_KEY_MAP.PAGE_DETAIL_CONTROL, JSON.stringify({ ...DEFAULT_PAGE_DETAIL_CONTROL, ...control }), ); }; 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; }; 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")) { showMessage(MessageTypeEnum.ERROR, "该BUG已在日报中"); } return false; } const todayReportLocal = getReportBugsByDate(date); todayReportLocal.push(bug); localStorage.setItem(date, JSON.stringify(todayReportLocal)); if (!isSaveForm || getPageDetailControl("showHintWhenAutoAddReport")) { showMessage(MessageTypeEnum.SUCCESS, "添加日报成功"); } return true; }; const removeBugFromReport = (bugId, date = getTodayDate()) => { const todayReportLocal = getReportBugsByDate(date); const index = todayReportLocal.findIndex((it) => it.id === bugId); if (index === -1) { showMessage(MessageTypeEnum.ERROR, "该BUG不存在"); return false; } todayReportLocal.splice(index, 1); localStorage.setItem(date, JSON.stringify(todayReportLocal)); showMessage(MessageTypeEnum.SUCCESS, "移除成功"); return true; }; const bugHistoryDialog = () => { let appIns = null; const appId = "bug_history_vue_app"; createDialog({ title: "历史记录", hideOk: true, cancelText: "关闭", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onMount: () => { const days = getDatesInRange(getSpecialDayBaseToday(1), 14); 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}} 条
`, setup() { 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 current = selectedValue.value; const currentData = bugHistory.filter( (item) => item.date === current, ); const text = currentData .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 = /* @__PURE__ */ 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, handleUpdateValue, copyCurrent, copyToday, copyWeek, prevDay, nextDay, }; }, }); app.use(naive); appIns = app.mount("#" + appId); }, }); return appIns; }; 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 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 copyUserLinksToHeader = () => { const headerLinks = document.querySelector("#header .links"); const footerUserLinkLI = document.querySelector("#links-saved"); if (headerLinks && footerUserLinkLI) { const footerUserLinkLICloned = footerUserLinkLI.cloneNode(); const footerUserLinkUL = footerUserLinkLI.querySelector(".links"); 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 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 = document.body.querySelector("#header"); const bugzillaBody = document.body.querySelector("#bugzilla-body"); const footer = document.body.querySelector("#footer"); 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: "页面缩放", okText: "不需要且此页面不再提示", cancelText: "关闭", style: "width: 40vw; height: 205px", body: `
`, onOk: (closeDialog) => { if (appIns && appIns._) { applyCallBack(appIns._.setupState.onOk, closeDialog); } }, 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); }; const onOk = (closeDialog) => { showModal.value = false; updateZoom(1); closeDialog(); }; return { showModal, zoom, updateZoom, onOk, }; }, }); app.use(naive); appIns = app.mount("#page_zoom_vue_app"); }, }); 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) { showMessage(MessageTypeEnum.ERROR, "操作失败: " + 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 = document.querySelector("#" + rowId); if (isCompletedBugsDataInvalid()) { showMessage( MessageTypeEnum.ERROR, "当前 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) { showMessage(MessageTypeEnum.ERROR, "标记失败: " + 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: () => { if (appIns && appIns._) { applyCallBack(appIns._.setupState.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; }; var PathNameEnum = /* @__PURE__ */ ((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 || {}); 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 clearReportBugs14DaysBefore = async () => { const clearDays = getDatesInRange( getSpecialDayBeforeDate(/* @__PURE__ */ new Date(), 14), 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 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 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 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 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 = document.querySelector(item.value); elem.style.display = "none"; } }); }; const pageDetailSettingDialog = () => { let appIns = null; const appId = "page_detail_setting_vue_app"; createDialog({ title: "页面设置", cancelText: "关闭", bodyStyle: "width: 60vw; height: 60vh;", body: `
`, onOk: (closeDialog) => { if (appIns && appIns._) { applyCallBack(appIns._.setupState.onOk, closeDialog); } }, onDestroy: () => { if (appIns && appIns._) { applyCallBack(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"), }, ]); 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 = document.querySelector( "#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 = document.querySelector(item.value); if (item.show) { elem.style.display = "table-row"; } else { elem.style.display = "none"; } mouseleaveHandler(); localStorage.setItem( STORAGE_KEY_MAP.FORM_ITEM_CONTROL, JSON.stringify({ list1: checkboxOptions1, list2: checkboxOptions2, }), ); }, mouseenterHandler: (item) => { const elem = document.querySelector(item.value); const rect = elem.getBoundingClientRect(); const div = document.createElement("div"); div.id = `show_hide_formitem_vue_app_rect`; div.style.position = "fixed"; div.style.top = `${rect.top}px`; div.style.left = `${rect.left}px`; div.style.width = `${rect.width}px`; div.style.height = `${rect.height}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 isBugReportPage = () => { return PATHNAME === PathNameEnum.BUG_NEW && !isBugReportEntryPage(); }; const isBugReportEntryPage = () => { return ( window.location.href === window.location.origin + "/bugzilla/enter_bug.cgi" ); }; const showTopButtons = !window.location.pathname.includes("attachment.cgi") && !window.location.pathname.includes("show_activity.cgi"); const renderBugCommon = () => { const links = document.querySelectorAll(".links a"); const linksUL = document.querySelector(".links"); const linksLIs = Array.from(document.querySelectorAll(".links li")); const quicksearchTopInput = document.querySelector("#quicksearch_top"); const quicksearchBottomInput = document.querySelector( "#quicksearch_bottom", ); const findTopBtn = document.querySelector("#find_top"); const findBottomBtn = document.querySelector("#find_bottom"); const separators = document.querySelectorAll(".links .separator"); const searchBtns = document.querySelectorAll("input[value='Search']"); clearReportBugs14DaysBefore(); migrateReportBugs(); migrateCollectedBugs(); migrateCompletedBugs(); if (!hasPersonMap()) { setPersonMap(PERSONS_MAP); syncPersonMap(); } 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"); applyCallBack(beforeCreate, btn); btn.addEventListener("click", () => clickFn(btn)); li.appendChild(btn); linksUL.appendChild(li); }; if (linksLIs.length > 0) { linksLIs.forEach((item, idx) => { if ([6, 8, 9, 10].includes(idx)) { item.remove(); } }); } if (linksUL && showTopButtons) { createTopBtn("历史记录", async () => { bugHistoryDialog(); }); createTopBtn("收藏列表", () => { const collectedBugs = getCollectedBugs(); if (collectedBugs.length === 0) { showMessage(MessageTypeEnum.ERROR, "没有收藏的bug"); return; } bugCollectionDialog(); }); if (!isBugReportEntryPage()) { createTopBtn("页面缩放", async () => { pageZoomDialog(); }); } if (PATHNAME === PathNameEnum.BUG_DETAIL) { createTopBtn("页面设置", () => { pageDetailSettingDialog(); }); } if (PATHNAME === PathNameEnum.BUG_LIST) { createTopBtn("页面设置", () => { pageListSettingDialog(); }); createTopBtn("邮件名英文名映射中文对照表", 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(value); closeDialog(); showMessage(MessageTypeEnum.SUCCESS, "更新成功"); } }, onMount: () => { const footer = document.querySelector(".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)); showMessage( MessageTypeEnum.SUCCESS, "同步成功, 记得保存~", ); syncBtn.innerText = "同步云端数据"; syncBtn.classList.remove("disabled-button"); } } catch { showMessage(MessageTypeEnum.ERROR, "同步失败"); } }); 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)); }); } } searchHistoryList( document.querySelector("#find_top"), "search_history_list_vue_app_top", ); searchHistoryList( document.querySelector("#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, ); }; const getChineseName = (persons, string) => { if (persons) { if (string.includes("@")) { const name = string.split("@")[0]; return persons[name]; } } return string; }; const isEmail = (str) => { const emailRegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; return emailRegExp.test(str); }; const getCity = () => { return localStorage.getItem(STORAGE_KEY_MAP.CITY) ?? "北京"; }; const setCity = (city) => { if (!city) { showMessage(MessageTypeEnum.ERROR, "设置城市的值不能为空"); 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(), // 10位秒级时间戳 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 = options.apiType === "daily" ? "daily.json" : "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", // now:实时天气,daily:7天预报 ttl: 1800, }; const getWeather = async () => { try { const city = localStorage.getItem(STORAGE_KEY_MAP.CITY) ?? "成都"; 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 = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const renderQuip = () => { const quip = document.querySelector(".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 = document.getElementById("quip-text"); const radios = Array.from( quip.querySelectorAll("input[type='radio'][name='quipType']"), ); 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 = document.querySelector(".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); showMessage( MessageTypeEnum.SUCCESS, "正在验证当前城市是否存在", 2e3, ); const res = await getWeather(); await sleep(2e3); if (res && res.status_code && res.status_code.includes("AP")) { showMessage( MessageTypeEnum.ERROR, "当前城市不存在,为你设置为默认城市:北京", 3e3, ); input.value = "北京"; setCity(input.value); return; } closeDialog(); getWeatherUI(); } else if (!value) { showMessage(MessageTypeEnum.ERROR, "请输入城市名称"); } }, }); }; const getWeatherUI = async () => { try { await loadScriptByGM(CDNScriptURLEnum.CRYPTO_JS); const res = await getWeather(); if (!res) { showMessage(MessageTypeEnum.ERROR, "获取天气失败"); 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(700); await getWeatherUI(); showMessage(MessageTypeEnum.SUCCESS, "刷新成功"); }); queryInfo.appendChild(refreshBtn); const settingCityBtn = document.getElementById("setting-city"); if (settingCityBtn) { settingCityBtn.addEventListener("click", settingCity); } } catch (error) { console.error(error); } }; getWeatherUI(); } }; const renderBugList = () => { const longFormat = document.querySelector("#long_format"); const xml = document.querySelector("#xml"); const timeSummary = document.querySelector("#timesummary"); const remember = document.querySelector("#remember"); const bzIdColumns = Array.from( document.querySelectorAll(".bz_id_column a"), ); const bzShortDescColumns = Array.from( document.querySelectorAll(".bz_short_desc_column a"), ); const buglistRows = Array.from( document.querySelectorAll(".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 = node.querySelector(".bz_short_desc_column a"); const bugText = bugTextNode.innerText; const bugUrl = bugTextNode.href; const td = document.createElement("td"); showReportBtn(td, bugId, bugText, bugUrl); const completeBtn = showResolvedMarkBtn(td, bugId); showCollectionBtn(td, bugId, bugText, bugUrl); 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 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 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); }; const getPersonEmail = (email) => { var _a; return (_a = email.match(/mailto:([^]+)/)) == null ? void 0 : _a[1]; }; 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 getReporterEmail = () => { const reporterNode = document.querySelector( "#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 = document.querySelector("#assigned_to"); const assignedToOptions = Array.from( document.querySelectorAll("#assigned_to > option"), ); const assigneeEditBtn = document.querySelector("#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.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", () => { assigneeEditBtn.click(); if (appIns) { 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.parentNode.insertBefore(div, assignedTo); 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.value); const options = reactive( assignedToOptions .map((option) => { return { label: option.innerText, value: option.value, }; }) .filter((item) => !!item.label), ); const assignToTester = (newValue) => { selectedValue.value = newValue; assignedTo.value = newValue; }; return { selectedValue, options, handleUpdateValue(value) { assignedTo.value = value; }, assignToTester, }; }, }); app.use(naive); appIns = app.mount("#assigned_to_list_vue_app"); return appIns; }; function iframeCallback(iframeId, callback) { const iframePage = document.querySelector("#" + iframeId); if (iframePage) { iframePage.onload = () => { const contentDocument = iframePage.contentDocument; callback(contentDocument); }; } } 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 updateCustomBranches = (branchName, isAdd, isShowMessage = true) => { let branches = getCustomBranches(); if (isAdd) { if (branches.some((item) => item.title === branchName)) { if (isShowMessage) { showMessage(MessageTypeEnum.ERROR, "分支已存在"); } return; } branches.push({ title: branchName }); if (isShowMessage) { showMessage(MessageTypeEnum.SUCCESS, "添加成功"); } } else { branches = branches.filter((item) => item.title !== branchName); if (isShowMessage) { showMessage(MessageTypeEnum.SUCCESS, "删除成功"); } } 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 = /* @__PURE__ */ 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) { showMessage(MessageTypeEnum.ERROR, "请先配置 GitLab 信息"); return commits; } const { gitlabUrl, personalAccessToken, rangeDays = 1, projectIds: defaultProjectIds = [], } = config; const projectIdsToUse = [ .../* @__PURE__ */ 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 ( /* @__PURE__ */ new Map([ [2, "bi"], [3, "core"], ]).get(projectId) || "bi" ); }; 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) => { if (appIns && appIns._) { applyCallBack(appIns._.setupState.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 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 = Array.from(target.querySelectorAll("pre")); if (domElements.length === 0) { domElements = Array.from(target.querySelectorAll("div")); } } domElements.forEach((domElement) => { const originalHtml = domElement.innerHTML; const newHtml = replaceHashInPreContent(originalHtml); domElement.innerHTML = newHtml; const hashLinks = domElement.querySelectorAll(".commit-hash-link"); 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 类型"); }; function clearBrAndBlank(rootNode) { if (!rootNode || !(rootNode instanceof Node)) { console.warn("传入的根节点无效,请传入有效的 DOM 节点"); return false; } if (rootNode.nodeType !== Node.ELEMENT_NODE) { return true; } const rootElement = rootNode; const pElements = rootElement.querySelectorAll("p"); if (pElements.length === 0) { return true; } pElements.forEach((pElement) => { const brElements = Array.from(pElement.querySelectorAll("br")); brElements.forEach((br) => { if (br.parentElement === pElement) { br.remove(); } }); }); const childNodes = Array.from(rootElement.childNodes); const pNodeList = Array.from(pElements); childNodes.forEach((node) => { var _a; if (node.nodeType !== Node.TEXT_NODE) return; const textContent = ((_a = node.textContent) == null ? void 0 : _a.trim()) || ""; if (textContent !== "") return; const prevSibling = node.previousSibling; const nextSibling = node.nextSibling; const prevIsP = prevSibling && pNodeList.includes(prevSibling); const nextIsP = nextSibling && pNodeList.includes(nextSibling); if (prevIsP && nextIsP) { node.remove(); } }); return true; } const renderBugDetail = () => { var _a, _b, _c, _d; const persons = getPersonMap(); const table1 = document.querySelector("#bz_show_bug_column_1"); const table2 = document.querySelector("#bz_show_bug_column_2"); const reporterNode = document.querySelector( "#bz_show_bug_column_2 .vcard .email", ); const table2AllRows = Array.from( document.querySelectorAll("#bz_show_bug_column_2 tr"), ); const reporterEmail = getPersonEmail( (reporterNode == null ? void 0 : reporterNode.href) ?? "", ); const reportNodes = Array.from( document.querySelectorAll(".vcard .email .fn"), ); const historyBtn = document.querySelector( "#bz_show_bug_column_2 tr:nth-child(2) a", ); const editTitleBtn = document.querySelector("#editme_action"); const assigneeEditBtn = document.querySelector("#bz_assignee_edit_action"); const assigneeTakeBtn = document.querySelector("#bz_assignee_take_action"); const markDuplicateBtn = document.querySelector( "#dup_id_discoverable_action", ); const markDuplicateBox = document.querySelector("#dup_id_discoverable"); const duplicateSettings = document.querySelector("#duplicate_settings"); const replaceTitleInput = document.querySelector("#summary_alias_input"); const commentTextArea = document.querySelector("#comment"); const commentBox = document.querySelector("#add_comment"); const commentBoxLabel = document.querySelector("#add_comment label"); const attachmentDetails = Array.from( document.querySelectorAll( "#attachment_table td[valign='top']:nth-child(2) a", ), ); const saveFormBtn1 = document.querySelector( ".bz_alias_short_desc_container .knob-buttons input", ); const saveFormBtn2 = document.querySelector( "#add_comment .knob-buttons input", ); const bugStatus = document.querySelector("#bug_status"); const resolution = document.querySelector("#resolution"); const resolutionSettings = document.querySelector("#resolution_settings"); const assigneeInputNode = document.querySelector("#bz_assignee_input"); const assigneeEditNode = document.querySelector( "#bz_assignee_edit_container", ); const assignToSelect = document.querySelector("#assigned_to"); const bugTitleBox = document.querySelector("#summary_alias_container"); const bugTitleNode = document.querySelector("#short_desc_nonedit_display"); const bugIDNode = document.querySelector( ".bz_alias_short_desc_container a b", ); const bugId = (_a = bugIDNode.innerText.match(/(\d+)$/)) == null ? void 0 : _a[1]; const effectNode = document.querySelector("#cf_effect"); const bzSpacerNodes = Array.from( document.querySelectorAll( "#bz_show_bug_column_1 tr:has(td[class='bz_section_spacer'])", ), ); const navigationNode = document.querySelector(".navigation"); const attachmentNode = document.querySelector( "#attachment_table .bz_attach_footer a:not(a[id='view_all'])", ); const attachmentViewAll = document.querySelector( "#attachment_table .bz_attach_footer a[id='view_all']", ); const navigationABtns = Array.from( document.querySelectorAll("#bugzilla-body .navigation a"), ); const navigationFontBtns = Array.from( document.querySelectorAll("#bugzilla-body .navigation i font"), ); const commentNodes = Array.from( document.querySelectorAll( "#comments .bz_comment_table .bz_comment:not(.bz_first_comment) .bz_comment_text", ), ); const firstComment = document.querySelector( ".bz_comment.bz_first_comment .bz_comment_text", ); const firstCommentHead = document.querySelector( ".bz_comment.bz_first_comment .bz_first_comment_head", ); 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 = contentDocument.querySelector("#header"); const footer = contentDocument.querySelector("#footer"); 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) { createButton( "配置一键填写", (button) => { button.classList.add("plain-button"); bugTitleBox.parentNode.insertBefore(button, bugTitleBox.nextSibling); }, () => { gitlabConfigDialog(); }, ); createButton( "自定义快捷分支", (button) => { button.classList.add("plain-button"); bugTitleBox.parentNode.insertBefore(button, bugTitleBox.nextSibling); }, () => customBranchDialog(), ); 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 = document.querySelector( "#bz_assignee_edit_container .email", ); if (aLink) { const email = getPersonEmail(aLink.href); if (email) { assignToSelect.value = email; const assignToAppNode = document.querySelector( "#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 = replaceTitleInput.querySelector("#alias"); const shortDesc = replaceTitleInput.querySelector("#short_desc"); const replaceClone = replaceTitleInput.cloneNode(true); replaceClone.classList.remove("bz_default_hidden"); const aliasInput = replaceClone.querySelector("#alias"); const shortDescInput = replaceClone.querySelector("#short_desc"); 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 = assigneeInputNode.querySelector("br")) == 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", "760px"); commentTextArea.addEventListener("focus", () => { commentTextArea.rows = 10; }); } } if (attachmentDetails.length) { const fileNames = Array.from( document.querySelectorAll( "#attachment_table td[valign='top']:nth-child(0) b", ), ); const fileTypeNodes = Array.from( document.querySelectorAll( "#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 { showMessage( MessageTypeEnum.ERROR, "文件不存在,或者不支持从此处" + 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 = document.querySelector( "#assign_to_tester_btn", ); assignToTesterBtn && assignToTesterBtn.click(); completeBtn && completeBtn.click(); const gitlabConfig = getGitlabConfig(); if (!gitlabConfig) { showMessage(MessageTypeEnum.ERROR, "请先配置 GitLab 信息"); gitlabConfigDialog(); return; } const closeMessage = showMessage( MessageTypeEnum.SUCCESS, "正在查询相关的 commit 记录...", 1e3 * 60 * 60, ); const commits = await getBugCommitsFromProjects(bugId); closeMessage(); if (!commits.length) { showMessage( MessageTypeEnum.ERROR, "没有找到相关的 commit 记录,请检查 Personal Access Token 是否正确,或者修改查询范围", 5e3, ); 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 = { list: collectedBugs, }; if (!hasCollected) { newBugs = { list: [ ...collectedBugs, { id: bugIDNode.innerText, title: bugTitleNode.innerText, url: window.location.href, }, ], }; if (collectBugNode.dataset.type !== "no-collected") { showMessage( MessageTypeEnum.ERROR, "数据与页面状态不一致,页面刷新后重试", ); setTimeout(() => { window.location.reload(); }, 1e3); return; } showMessage(MessageTypeEnum.SUCCESS, "收藏成功"); } else { newBugs = { list: collectedBugs.length > 0 && collectedBugs.filter((bug) => bug.id !== bugIDNode.innerText), }; if (collectBugNode.dataset.type !== "collected") { showMessage( MessageTypeEnum.ERROR, "数据与页面状态不一致,页面刷新后重试", ); setTimeout(() => { window.location.reload(); }, 1e3); return; } showMessage(MessageTypeEnum.SUCCESS, "已取消收藏"); } 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 = contentDocument.querySelector("#header"); const footer = contentDocument.querySelector("#footer"); 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) => { window.open( `${GITLAB_ORIGIN}/project/${projectName || "bi"}/-/commit/${hash}`, "_blank", ); }); }); } if ( localStorage.getItem(STORAGE_KEY_MAP.AUTO_CLEAR_FIRST_COMMENT_BR) === "true" && firstComment ) { clearBrAndBlank(firstComment); } if (firstCommentHead) { const span = document.createElement("span"); span.style.setProperty("display", "inline-flex"); span.style.setProperty("align-items", "center"); span.style.setProperty("transform", "translateY(2px)"); firstCommentHead.appendChild(span); const checkBox = document.createElement("input"); const checkBoxLabel = document.createElement("label"); checkBoxLabel.classList.add("ml-5"); checkBoxLabel.classList.add("clear-br-label"); checkBoxLabel.innerText = "自动调整本评论文本的上下间距"; checkBoxLabel.title = "仅处理富文本评论。设置后下次自动生效"; checkBox.type = "checkbox"; checkBox.classList.add("ml-5"); span.appendChild(checkBox); span.appendChild(checkBoxLabel); checkBox.checked = localStorage.getItem(STORAGE_KEY_MAP.AUTO_CLEAR_FIRST_COMMENT_BR) === "true"; const setCheck = (checked) => { if (checked) { clearBrAndBlank(firstComment); } checkBox.checked = checked; localStorage.setItem( STORAGE_KEY_MAP.AUTO_CLEAR_FIRST_COMMENT_BR, checked ? "true" : "false", ); }; checkBoxLabel.addEventListener("click", () => { setCheck(!checkBox.checked); }); checkBox.addEventListener("change", (e) => { setCheck(e.target.checked); }); firstCommentHead.appendChild(span); } }; const newCCList = () => { const persons = getPersonMap(); const ccSelect = document.querySelector("#newcc"); const ccSelectOptions = Array.from( document.querySelectorAll("#newcc > 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 componentList = () => { const component = document.querySelector("#component"); if (!component) { return; } const componentOptions = Array.from( document.querySelectorAll("#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 = Array.from( document.querySelectorAll(".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 = document.querySelector("#cf_newfeatureid"); const featureOptions = Array.from( document.querySelectorAll("#cf_newfeatureid > option"), ); const div = document.createElement("div"); div.id = "feature_list_vue_app"; feature.parentNode.insertBefore(div, feature); feature.classList.add("bz_default_hidden"); const { createApp, ref, reactive } = Vue; const app = createApp({ template: ` `, setup() { const selectedValue = ref(feature.value); const options = reactive( featureOptions.map((option) => { return { label: option.innerText, value: option.value, }; }), ); return { selectedValue, options, handleUpdateValue(value) { feature.value = value; }, }; }, }); app.use(naive); return app.mount("#feature_list_vue_app"); }; const versionList = () => { const version = document.querySelector("#version"); if (!version) { return; } const versionOptions = Array.from( document.querySelectorAll("#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 renderBugDetailPage = () => { if (PATHNAME === PathNameEnum.BUG_DETAIL) { renderBugDetail(); assignToList(); newCCList(); featureList(); componentList(); versionList(); } }; const renderBugReportEntry = () => { const products = Array.from( document.querySelectorAll("table th[valign='top'] a"), ); const productDescs = Array.from( document.querySelectorAll("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 updateQuickSummary = (text, isAdd, isShowMessage = true) => { let quickSummary = getQuickSummary(); if (isAdd) { if (quickSummary.some((item) => item.title === text)) { if (isShowMessage) { showMessage(MessageTypeEnum.ERROR, "快捷短语已存在"); } return; } quickSummary.push({ title: text }); if (isShowMessage) { showMessage(MessageTypeEnum.SUCCESS, "添加成功"); } } else { quickSummary = quickSummary.filter((item) => item.title !== text); if (isShowMessage) { showMessage(MessageTypeEnum.SUCCESS, "删除成功"); } } 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 = Array.from( document.querySelectorAll(".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 = document.querySelector("#Create"); const allTableCells = newBugForm ? Array.from(newBugForm.querySelectorAll("table tr td")) : null; const descriptionTextArea = document.querySelector("#comment"); const versionLabel = document.querySelector("#field_label_version"); const guessNote = document.querySelector("#os_guess_note"); const commit = document.querySelector("#commit"); const shortDesc = document.querySelector("#short_desc"); const attachFalseInput = document.querySelector("#attachment_false input"); const attachTrueInput = document.querySelector("#attachment_true input"); const attatchData = document.querySelector( "#attachment_true .attachment_entry input", ); const priority = document.querySelector("#field_label_priority"); const topTip = document.querySelector("form table tr"); const possibleDuplicatesContainer = document.querySelector( "#possible_duplicates_container", ); const assignedToHelpLink = document.querySelector( "#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 = Array.from( document.querySelectorAll(".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 = document.querySelector("#cke_comment"); if (descriptionCKEEditor) { descriptionCKEEditor.style.setProperty("display", "none"); } descriptionTextArea.style.setProperty("display", "block"); descriptionTextArea.style.setProperty("visibility", "visible"); descriptionTextArea.value = localStorage.getItem(STORAGE_KEY_MAP.NEW_BUG_COMMENT_TEMPLATE) || COMMENT_DEFAULT_TEMPLATE_3; descriptionTextArea.rows = countLineBreaks(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) ) { showMessage(MessageTypeEnum.ERROR, "请输入模板内容"); return; } localStorage.setItem( STORAGE_KEY_MAP.NEW_BUG_COMMENT_TEMPLATE, (descriptionTextArea == null ? void 0 : descriptionTextArea.value) || "", ); showMessage(MessageTypeEnum.SUCCESS, "模板已更新"); }, ); 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); } }, () => { 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); } }, () => { descriptionTextArea.rows = 19; descriptionTextArea.value = COMMENT_DEFAULT_TEMPLATE_2; }, ); createButton( "使用模板3", (btn) => { btn.classList.add("custom-button"); btn.classList.add("plain-button"); btn.title = COMMENT_DEFAULT_TEMPLATE_3; if ( descriptionTextArea == null ? void 0 : descriptionTextArea.parentElement ) { btnGroup.appendChild(btn); } }, () => { descriptionTextArea.rows = 27; descriptionTextArea.value = COMMENT_DEFAULT_TEMPLATE_3; }, ); }, ); 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 ccList = () => { const persons = getPersonMap(); const ccSelect = document.querySelector("#cc"); if (!ccSelect) { return; } const ccSelectOptions = Array.from( document.querySelectorAll("#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 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 renderBugAttachment = () => { const attachmentSubmit = document.querySelector( ".attachment_entry input[value='Submit']", ); if (attachmentSubmit) { attachmentSubmit.classList.add("custom-button"); } }; const renderBugAttachmentPage = () => { if (PATHNAME === PathNameEnum.BUG_ATTACHMENT) { renderBugAttachment(); } }; const renderBugHistory = () => { const table = document.querySelector("#bugzilla-body table"); const tds = Array.from( document.querySelectorAll("#bugzilla-body table td"), ); const bugLinks = Array.from( document.querySelectorAll("#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 addStyle = (css) => { const style2 = document.createElement("style"); style2.type = "text/css"; style2.innerHTML = css; document.getElementsByTagName("head")[0].appendChild(style2); }; const renderBugProcess = () => { const bugLink = document.querySelector("#bugzilla-body .bz_bug_link"); if (bugLink) { bugLink.innerText = "返回 " + bugLink.innerText; bugLink.classList.add("custom-button"); } }; const renderBugProcessPage = () => { if (PATHNAME === PathNameEnum.BUG_PROCESS) { renderBugProcess(); } }; try { async function setup() { addStyle(style); await loadScriptByGM(CDNScriptURLEnum.VUE); await loadScriptByGM(CDNScriptURLEnum.NAIVE_UI); renderBugCommon(); renderBugListPage(); renderBugDetailPage(); renderBugReportPage(); renderBugReportEntryPage(); renderBugAttachmentPage(); renderBugHistoryPage(); renderBugProcessPage(); } setup(); } catch (error) { console.error(error); } })();