您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
make old apollo better
// ==UserScript== // @name apollo-enhance // @namespace apollo-enhance // @version 0.9.16 // @description make old apollo better // @homepage https://github.com/xyz327/old-apollo-portal-enhance // @website https://github.com/xyz327/old-apollo-portal-enhance // @source https://github.com/xyz327/old-apollo-portal-enhance // @author xizhou // @match *://*/config.html* // @resource highlight_xcode_css https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/highlight.js/9.18.5/styles/xcode.min.css // @require https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/diff_match_patch/20121119/diff_match_patch_uncompressed.js // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/highlight.js/9.18.5/highlight.min.js // @require https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/highlight.js/9.18.5/languages/json.min.js // @resource text_different_css https://cdn.jsdelivr.net/npm/[email protected]/build/style/text-different.min.css // @require https://cdn.jsdelivr.net/combine/npm/[email protected]/build/text-different.min.js,npm/[email protected]/build/text-different-for-html.min.js // @noframes // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_addElement // ==/UserScript== (function (_$1) { 'use strict'; var allFeature = [ { name: "fixEnvTab", desc: "固定左侧菜单", defaultEnabled: true }, { name: "fixNiceScroll", desc: "修复 CTRL+F 搜索不能跳转的问题", defaultEnabled: true }, { name: "gotoNamespace", desc: "一键跳转到对应的 namespace", defaultEnabled: true }, { name: "releaseDiff", desc: "发布界面差异对比", defaultEnabled: true }, { name: "releaseModal", desc: "发布界面增强", defaultEnabled: true }, { name: "showText", desc: "单 key 差异对比", defaultEnabled: true }, { name: "stash", more: true, desc: "", defaultEnabled: false, enabledWarn: "实验性功能,请谨慎操作" }, { name: "copyNamespace", desc: "复制namespace", defaultEnabled: true }, { name: "prodWarnDisable", desc: "谨慎使用", more: true, defaultEnabled: false, enabledWarn: "实验性功能,请谨慎操作" }, { name: "valueCodeEditor", desc: "配置值编辑器增强", defaultEnabled: false } ]; var enhanceNavId = "apollo-enhance-nav"; var featureId = "apollo-enhance-feature"; var allFeatureMap = {}; allFeature.forEach((feature) => { allFeatureMap[feature.name] = feature; }); const BASE_INFO = {}; function appendNavBar(child) { $(`#${enhanceNavId}`).append(child); } loadFeature("nav", false, function () { var $navBar = $("#bs-example-navbar-collapse-1"); $navBar.append(` <ul id="${enhanceNavId}" class="nav navbar-nav navbar-right"> </ul> `); return true; }); const loadedJs = {}; const srcMapping = { "bootstrap-switch": "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-switch.min.js", "bootstrap-switch.css": "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap3/bootstrap-switch.min.css", }; function require(deps) { deps = _.isArray(deps) ? deps : [deps]; return Promise.all(deps.map(dep => loadJs(dep))) } function loadJs(src) { src = srcMapping[src] || src; if (_.endsWith(src, '.css')) { return loadCss(src) } if (loadedJs[src]) { return loadedJs[src]; } const loader = new Promise(function (resolve, reject) { const gmAdd = GM_addElement("script", { src, type: "text/javascript" }); if (gmAdd) { gmAdd.onload = function () { resolve(); }; } else { resolve(); } }); loadedJs[src] = loader; return loader; } function loadCss(href) { GM_addElement("link", { href, rel: "stylesheet", }); } (function () { initFeatureId(); initDiffModal(); // 绑定复制事件 $(document).on("click", "[data-copy]", function (e) { copy($(e.currentTarget).attr("data-copy-value")).then(function () { var $icon = $(e.target).parent().find(".glyphicon"); $icon.removeClass("glyphicon-duplicate").addClass("glyphicon-ok"); setTimeout(function () { $icon.addClass("glyphicon-duplicate").removeClass("glyphicon-ok"); }, 2000); }); }); // 加载 layer 因为依赖 $ 所以在代码里面进行加载 loadJs("https://cdn.jsdelivr.net/npm/[email protected]/src/layer.js"); loadCss("https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap3/bootstrap-switch.min.css"); loadJs("https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-switch.min.js"); const highlight_xcode_css = GM_getResourceText("highlight_xcode_css"); const text_different_css = GM_getResourceText("text_different_css"); GM_addStyle(highlight_xcode_css); GM_addStyle(text_different_css); })(); function getAllFeaturenMap() { return allFeatureMap; } var onNamesacpeLoadedCbs = []; var firsetNamespaceLoaded = false; function onNamesacpeLoaded(cb) { if (typeof cb === "function") { onNamesacpeLoadedCbs.push(cb); } return { then: function (cb) { if (firsetNamespaceLoaded) { cb(); } onNamesacpeLoadedCbs.push(cb); }, }; } loadFeature("onNamesacpeLoaded", true, function () { var $namespaces = $(".namespace-name"); if ($namespaces.length == 0) { return false; } console.log("trigger namespaceLoaded"); $("body").on("namespaceLoaded", () => { onNamesacpeLoadedCbs.forEach((cb) => cb()); }); $("body").trigger("namespaceLoaded"); const observer = new MutationObserver(function () { console.log("rebuild", arguments); $("body").trigger("namespaceLoaded"); }); $.each($(".config-item-container"), (index, el) => { observer.observe(el, { childList: true }); }); firsetNamespaceLoaded = true; }); function getAppId() { let hash = location.hash; if (hash) { hash = hash.substring(2); const url = new URL("http://localhost?" + hash); return url.searchParams.get("appid"); } } function loadFeature(name, options, feature) { options = typeof options === "object" ? options : { switch: true, reloadOnHashChange: options, }; if (options.switch) { // allFeature.push(name); if (isFeatureDisabled(name)) { console.log(`loadFeature: ${name} has disabled`); return; } } var reloadOnHashChange = !!options.reloadOnHashChange; loadFeature0(name, feature, false); if (reloadOnHashChange) { $(window).on("hashchange", function (e) { console.log("hashchange"); loadFeature0(name, feature, true); }); } } function isFeatureDisabled(name) { var state = featureState(name); if (state === true) { // 明确设置过为启用 return false; } if (state === false) { // 明确设置过为不启用 return true; } // 默认开关 if (allFeatureMap[name] && !allFeatureMap[name].defaultEnabled) { console.log(`loadFeature: ${name} has disabled by deafult`); return true; } return false; } function featureState(name, state) { return featureTypeState(name, null, state); } function featureTypeState(name, subtype, state) { var feature = subtype ? name + "-" + subtype : name; var stateMap = JSON.parse(localStorage.getItem("featureState")) || {}; if (state == undefined) { // get return stateMap[feature]; } //set stateMap[feature] = state; localStorage.setItem("featureState", JSON.stringify(stateMap)); } function switchFeature(name, enabled) { featureState(name, enabled); } function copy(content) { return new Promise(function (res, rej) { let copy = function (e) { try { e.preventDefault(); e.clipboardData.setData("text/plain", content); document.removeEventListener("copy", copy); console.log("copy value:", content); res(); } catch (e) { rej(); } }; document.addEventListener("copy", copy); document.execCommand("Copy"); }); } function toPerttyJson(val) { try { return JSON.stringify(JSON.parse(val), null, 2); } catch (e) { return val; } } function loadFeature0(name, feature, isReloadByHash) { try { if (!isReloadByHash && $("#feature-" + name).length !== 0) { // 已经加载过了 console.log(`loadFeature: ${name} has loaded`); return; } var clear = setInterval(function () { if (feature(isReloadByHash) !== false) { console.log(`loadFeature: ${name} finished`); $(`${"#" + featureId}`).append(`<div id="feature-${name}"></div>`); clearInterval(clear); } }, 1000); } catch (e) { console.error(`load feature failed :${name}`, e.message); } } function initFeatureId() { $("body").prepend(`<div id="${featureId}" class="hidden"></div>`); } function showDiffModal(key, newVal, oldVal) { const tdfh = new TextDifferentForHtml( $("#diff-container")[0], // The dom used to render the display code "json" // Type of code ); $("#diff-detail-title").html(`${key}`); $("#copyOld").attr("data-copy-value", oldVal); $("#copyNew").attr("data-copy-value", newVal); tdfh.render({ oldCode: toPerttyJson(oldVal), // Old code newCode: toPerttyJson(newVal), // New code hasLineNumber: false, // Whether to display the line number }); $("#diffModal").modal(); } function initDiffModal() { $("body").append(` <!-- Modal --> <div class="modal" id="diffModal" tabindex="-1" role="dialog" aria-labelledby="diffModal"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title"><span class="text-danger" id="diff-detail-title"></span> 差异对比</h4> </div> <div class="modal-body" > <div class="row"> <div class="col-xs-6 text-center"> <span data-tooltip="tooltip" title="点击复制" id="copyOld" data-copy="copy" data-copy-value="" class="label label-default">旧值 <label class="glyphicon glyphicon-duplicate"></label> </span> </div> <div class="col-xs-6 text-center"> <span data-tooltip="tooltip" title="点击复制" id="copyNew" data-copy="copy" data-copy-value="" class="label label-success">新值 <label class="glyphicon glyphicon-duplicate"></label> </span> </div> </div> <div id="diff-container" style="display:flex"></div> </div> </div> </div> </div> `); } loadFeature("copyNamespace", false, function () { onNamesacpeLoaded(function () { $("header.panel-heading .header-namespace>span:first-child") .toArray() .forEach(function (el) { var name = el.innerText.trim(); var $el = $(el); if ($el.nextAll(".copyNamespace").length != 0) { return; } $el.next("span").after(` <span data-tooltip="tooltip" title="点击复制namespace" data-copy="copy" data-copy-value="${name}" class="copyNamespace label label-success">复制 <label class="glyphicon glyphicon-duplicate"></label> </span> `); }); }); return true; }); loadFeature("fixEnvTab", true, function (isReloadByHash) { var $tab = $(".J_appFound"); if ($tab.length == 0) { return false; } const infoVal = sessionStorage[getAppId()]; const infoObj = JSON.parse(infoVal); infoObj.cluster; infoObj.env; var $panelHeader = $tab.find(".panel-heading:first"); var $curEnvInfo = $panelHeader.find("#curEnvInfo"); if ($curEnvInfo.length == 0) { $tab.find(".panel-heading>span").after(` <button type="button" class="slideBtn btn btn-primary btn-xs">(点击展开/收缩)</button> `); $panelHeader.append(`<div id="curEnvInfo"></div>`); $curEnvInfo = $panelHeader.find("#curEnvInfo"); } var $scope = $('div[ng-controller="ConfigBaseInfoController"]').scope(); var pageContext = $scope.pageContext; $curEnvInfo.html(` <span class="label label-success">${pageContext.env}</span> - <span class="label label-info">${pageContext.clusterName}</span> `); if (!isReloadByHash) { // 不是通过 hash change reload 的才需要绑定事件 $tab.find(".panel-heading .slideBtn").on("click", function (e) { const $header = $(e.target).parent(".panel-heading"); $header.next("div").slideToggle("normal", function () {}); }); $tab = $tab.parent(); $tab.on("affixed.bs.affix", function (e) { $tab.css({ position: "fixed" }); $tab .find(".panel-heading") .next("div") .slideUp("normal", function () {}); }); $tab.on("affixed-top.bs.affix ", function (e) { $tab.css({ position: "" }); $tab .find(".panel-heading") .next("div") .slideDown("normal", function () {}); }); $tab.affix({ offset: { top: 60, }, }); } return true; }); loadFeature("fixNiceScroll", false, function () { $(document).ready(function () { $().niceScroll; $.prototype.niceScroll = function(){ }; // 放在初始化之后执行 setTimeout(function () { $("html").css("overflow", ""); var htmlScroll = $("html").getNiceScroll && $("html").getNiceScroll(0); htmlScroll && htmlScroll.remove(); }, 200); }); return true; }); function scrollTo(el) { $(el)[0].scrollIntoView({ behavior: 'smooth' }); } let inited = false; loadFeature("gotoNamespace", false, () => { if ($("#affixPlaceholder").length == 0) { $("body>nav.navbar").after( '<div id="affixPlaceholder" style="height:60px"></div>' ); $("body>nav.navbar") .width("100%") .css({ "z-index": 999, position: "fixed" }); } goToNamespace0(); return true; }); function goToNamespace0() { onNamesacpeLoaded().then(() => { if (!inited) { buildGotoNamespace(); inited = true; } refreshGogoNamespace(); }); } function refreshGogoNamespace() { var $select = $("#namespaceSelecter"); if ($select.hasClass("select2-hidden-accessible")) { // Select2 has been initialized $select.select2("destroy"); } // init $select.select2({ placeholder: "跳转到 Namespace", templateResult: formatOptions, templateSelection: formatOptions, }); } function formatOptions(state) { if (state.disabled) { return state.text; } var namespaceScope = $( 'div[ng-controller="ConfigNamespaceController"]' ).scope(); var namespace = namespaceScope.namespaces.find((namespace) => { return namespace.viewName === state.id; }); if (namespace.itemModifiedCnt > 0) { return $( `<label>${state.text} <span class="label label-warning ">改</span></label>` ); } return state.text; } var compiled = _$1.template(`<% _.forEach(namespaces, function(namespace) { %> <option value="<%- namespace.viewName%>"><%- namespace.viewName %></option><% }); %>`); function buildGotoNamespace() { var $select = $("#namespaceSelecter"); if ($select.length > 0) { return; } var namespaceScope = $( 'div[ng-controller="ConfigNamespaceController"]' ).scope(); var optionsTpl = compiled({ namespaces: namespaceScope.namespaces }); appendNavBar(` <li id="goToNamespace" style="margin-top: 10px;"> <select id="namespaceSelecter">${optionsTpl}</select> </li> `); $select = $("#namespaceSelecter"); $select.on("select2:open", function (e) { $("#select2-namespaceSelecter-results").css({ "max-height": "600px" }); }); //var htmlScroll = $("html").getNiceScroll && $("html").getNiceScroll(0); // 修改选项时 滚动页面到对应位置 $select.on("select2:select", function (e) { var namespaceId = $select.val(); var namespaceEl = $(".namespace-name") .toArray() .find((el) => el.innerHTML == namespaceId); scrollTo(namespaceEl); // htmlScroll && htmlScroll.doScrollTop($(namespaceEl).offset().top - 100, 1000); }); // 滚动页面时同步改变 当前选择的 namespace 选项 changeSelectedOnScroll($select); } function changeSelectedOnScroll($select) { var selectedVal; // 防抖 var listener = _$1.debounce(function (entries) { if (entries.length == 0) { return; } var entry = entries[0]; if (!entry.isIntersecting) { // 从可视区移出 return; } var el = entry.target; var curNamespace = $(el).text(); if (selectedVal != curNamespace) { selectedVal = curNamespace; $select.val(selectedVal).trigger("change"); } }, 200); const io = new IntersectionObserver(listener, { threshold: 1.0 }); $(".namespace-name").each((i, el) => { io.observe(el); }); } loadFeature("prodWarnDisable", false, function () { prodWarnDisable(); }); function prodWarnDisable() { var $btn = $("#releaseModal div.modal-footer").find("button[type=submit]"); $btn.click(function (e) { var namespaceScope = $( 'div[ng-controller="ConfigNamespaceController"]' ).scope(); var env = namespaceScope.pageContext.env; if (!isProd(env)) { return; } // var my = namespaceScope.$root.userName // var toReleaseNamespace = $(releaseForm).isolateScope()?.toReleaseNamespace; // var selfModify = true; // if (toReleaseNamespace) { // selfModify = toReleaseNamespace.items.filter(item => item.isModified).find(item => item.item.dataChangeLastModifiedBy === my) // } if (!isFeatureDisabled("prodWarnDisable")) { if (confirm("已关闭生产环境发布校验,是否继续?")) { namespaceScope.$root.userName = "disabledProdWarn"; } } }); } function isProd(env) { return env && env === "PRO"; } var DiffMatch = new diff_match_patch(); loadFeature("releaseDiff", false, function () { var releaseModalNode = document.querySelector("#releaseModal"); if (releaseModalNode == null) { return false; } bindDiffInfo(releaseModalNode); return true; }); function bindDiffInfo(node) { var observer = new MutationObserver(function () { initChangeInfoHeader(); // 每次都需要隐藏 initChangeInfoDetail(); var $cols = $("#releaseModal table tr.ng-scope"); var kvInfo = {}; for (const col of $cols) { var $col = $(col); var tds = $(col).find("td"); kvInfo = { key: tds[0].title, oldVal: tds[1].title, newVal: tds[2].title, }; buildDiffHtml( $col.find("td.diff-text"), kvInfo.key, kvInfo.oldVal, kvInfo.newVal ); } }); observer.observe(node, { attributeFilter: ["style"], }); } function toggleDiff() { $(".change-diff").toggle(); var needShow = $(".change-diff").is(":hidden"); if (needShow) { $(".change-detail").show(); } else { $(".change-detail").hide(); } } function initChangeInfoDetail() { $(".change-detail").hide(); var $cols = $("#releaseModal table tr.ng-scope"); for (var col of $cols) { var $col = $(col); if ($col.hasClass("diff-info-inited")) { return; } initChageCol(); $col.addClass("diff-info-inited"); } } function initChageCol() { var bodyRows = $("#releaseModal table tr.ng-scope"); for (var row of bodyRows) { var $row = $(row); if ($row.find("td.change-diff").length == 0) { $row.find("td:gt(0)").addClass("change-detail x-detail").hide(); $row.append( '<td class="change-diff diff-text" data-toggle="tooltip" data-placement="top" title="点击查看详细差异对比"></td>' ); } } $(".change-diff.diff-text").tooltip(); } function initChangeInfoHeader() { if ($("#releaseModal table thead tr>th").length == 0) { return; } if ($("#toggleDiff").length != 0) { return; } // 隐藏原有信息 $("#releaseModal table thead tr>th:gt(0)").addClass("change-detail").hide(); // 增加差异信息展示 var headCol = $("#releaseModal table thead tr"); headCol.append('<th class="change-diff">差异(点击查看新旧值对比)</th>'); $("#releaseModal table thead tr>th:eq(0)").append( '<button id="toggleDiff">切换显示</button>' ); $("#toggleDiff").click(function () { toggleDiff(); return false; }); } function buildDiffHtml($node, key, oldVal, newVal) { // 新增或删除 var diff = DiffMatch.diff_main(oldVal, newVal); DiffMatch.diff_cleanupSemantic(diff); var html = DiffMatch.diff_prettyHtml(diff); $node.html(html); var errorJson = isErrorJson(newVal); if (errorJson) { var $td = $node.parent().find("td:first"); var errorJsonLabelId = `${key}-errorJson`; if ($(`#${errorJsonLabelId}`).length == 0) { $td.append( `<span id="${errorJsonLabelId}" class="label label-danger">错误的json</span>` ); $td.addClass("alert alert-danger"); } } $node.on("click", function () { showDiffModal(key, newVal, oldVal); }); } function isErrorJson(val) { val = val.trim(); if (val.startsWith("{") || val.startsWith("[")) { try { JSON.parse(val); return false; } catch (e) { return true; } } return false; } loadFeature("releaseModal", true, function () { if ($("#goReleaseMoadlBottom").length == 0) { $('#releaseModal div.modal-header .modal-title:not(".ng-hide")').append( `<span id="goReleaseMoadlBottom" class="glyphicon glyphicon-circle-arrow-down" data-tooltip="tooltip" data-placement="top" title="定位到发布按钮"></span>` ); } if ($("#goReleaseMoadlTop").length == 0) { $("#releaseModal div.modal-footer").prepend(` <span id="goReleaseMoadlTop" class="pull-left glyphicon glyphicon-circle-arrow-up" data-tooltip="tooltip" data-placement="top" title="回到顶部"></span>`); } // for scroll $("#goReleaseMoadlBottom").on("click", function () { scrollTo($("#goReleaseMoadlTop")); }); $("#goReleaseMoadlTop").on("click", function () { scrollTo($("#goReleaseMoadlBottom")); }); return true; }); loadFeature( "settings", { switch: false, reloadOnHashChange: false }, function () { require(["bootstrap-switch"]) .then(() => { buildSettings(); }); } ); function buildSettings() { initSettingsModal(); $("[data-toggle=switch]") .bootstrapSwitch({ onText: "开启", offText: "关闭", onSwitchChange: function (event, state) { var $el = $(this); var featureName = $el.val(); var feature = getAllFeaturenMap()[featureName]; if ( feature && state && !featureTypeState(featureName, "enabledWarn") && feature.enabledWarn ) { layer.confirm( feature.enabledWarn, { icon: 3, btn: ["确定", "取消"] }, function (index) { switchFeature(featureName, true); featureTypeState(featureName, "enabledWarn", true); $el.bootstrapSwitch("state", true); layer.close(index); layer.confirm('切换成功,刷新生效。是否立即刷新页面?', function (idx) { location.reload(); }); }, function () { } ); return false; } else { switchFeature(featureName, state); layer.confirm('切换成功,刷新生效。是否立即刷新页面?', function (idx) { location.reload(); }); } }, }); appendNavBar(` <li> <a href="javascript:void(0);" id="showSettings"> <span class="glyphicon glyphicon-cog"></span> </a> </li> `); $("#showSettings").on("click", showSettings); } function showSettings() { $("#settingsModal").modal(); } function initSettingsModal() { var tpl = ""; var moreTpl = ""; allFeature.forEach((feature) => { var key = feature.name.replace(".", "-"); var checked = isFeatureDisabled(feature.name) ? "" : "checked"; var _tpl = ` <div class="form-group" style="width:45%;margin:5px 0px;"> <label class="col-sm-6 control-label" for="feature-switch-${key}">${feature.name} <span class="glyphicon glyphicon-question-sign" data-tooltip="tooltip" title="${feature.desc}"></span> </label> <div class="col-sm-6"> <input type="checkbox" data-toggle="switch" value="${feature.name}" id="feature-switch-${key}" ${checked}/> </div> </div> `; if (feature.more) { moreTpl += _tpl; } else { tpl += _tpl; } }); $("body").append(` <!-- Modal --> <div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title"><span class="text-danger" id="diff-detail-title"></span> 设置 (修改后刷新生效) <a href="javascript:void(0);" id="showFeatureInfo"> <span class="glyphicon glyphicon-question-sign"></span> </a> </h4> </div> <div class="modal-body" > <form class="form-inline"> ${tpl} </form> <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="headingOne"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#accordion" onclick="$('#collapseOne').collapse('toggle');" aria-controls="collapseOne"> 更多功能 </a> </h4> </div> <div id="collapseOne" class="panel-collapse collapse out" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <form class="form-inline"> ${moreTpl} </form> </div> </div> </div> </div> </div> <div class="modal-footer"> <div><a href="https://greasyfork.org/zh-CN/scripts/447045-apollo-enhance" target="_blank" title="更新检测">${BASE_INFO.version}</a> </div> <div class="center-block"> 企微反馈👉 <a href="wxwork://message?username=xizhouxi">@xizhouxi</a> </div> </div> </div> </div> </div> `); } loadFeature("showText", true, function () { var $namespaces = $(".namespace-view-table"); if ($namespaces.length == 0) { return false; } var currItem; $("#showTextModal .modal-body") .tooltip({ title: "点击查看差异对比", }) .on("click", function () { if (currItem && currItem.isModified) { showDiffModal(currItem.item.key, currItem.newValue, currItem.oldValue); } }); // 点击查看时 选择当前的 item $('body').on('click', "td.cursor-pointer", function(e){ var $target = $(e.currentTarget); if($target.prev('td.cursor-pointer').length == 0){ // 说明点击的是 key 忽略 return; } var $tr = $target.parent(); var key = $tr.find("td:eq(1)").find('span:eq(0)').text().trim(); var namespace = $tr .parents("section.master-panel-body.ng-scope") .find("b.namespace-name.ng-binding") .text() .trim(); var namespaceScope = $( 'div[ng-controller="ConfigNamespaceController"]' ).scope(); var namesapce = namespaceScope.namespaces.find( (e) => e.baseInfo.namespaceName === namespace ); console.log('show key:', namespace, key); currItem = namesapce.items.find((e) => e.item.key === key); }); }); loadFeature("stash", true, function () { onNamesacpeLoaded(function () { console.log("stash"); $.each( $("section.master-panel-body.ng-scope>.panel-heading>"), function (idx, el) { var $panel = $(el); if ($panel.find(".stashFeature").length > 0) { return; } $panel.append(`<label class="stashFeature hidden"/>`); var namespaceName = $panel .find("b.namespace-name.ng-binding") .text() .trim(); var namespaceScope = $( 'div[ng-controller="ConfigNamespaceController"]' ).scope(); var ConfigService = angular .injector(["application"]) .get("ConfigService"); var namespace = namespaceScope.namespaces.find( (e) => e.baseInfo.namespaceName === namespaceName ); var $menu = $(el).find(".dropdown-menu"); $menu.append('<li role="separator" class="divider"></li>'); $(` <li><a href="javascript:void(0);" ><span class="glyphicon glyphicon-save"></span>暂存改动</a></li> `) .on("click", function () { layer.confirm("是否要暂存改动?", function (index) { console.log(namespaceName); console.log(namespace); console.log(ConfigService); var items = namespace.items.filter((item) => item.isModified); if (items.length === 0) { layer.close(index); layer.msg("没有改动"); return; } localStorage.setItem( getStashKey(namespaceScope, namespace), JSON.stringify(items) ); // recovery old value Promise.all( items.map((item) => { var isNewValue = item.isModified && !item.oldValue; var isDeleted = item.isDeleted; if (isNewValue) { // 新增的配置 删除 return ConfigService.delete_item( namespace.baseInfo.appId, namespaceScope.pageContext.env, namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName, item.item.id ); } if (isDeleted) { // 被删除的数据 拿不到原来的 item 信息 // 那就 create return ConfigService.create_item( namespace.baseInfo.appId, namespaceScope.pageContext.env, namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName, { key: item.item.key, value: item.oldValue, tableViewOperType: "create", addItemBtnDisabled: true, } ); } // 更新的 key return ConfigService.update_item( namespace.baseInfo.appId, namespaceScope.pageContext.env, namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName, $.extend(item.item, { value: item.oldValue, tableViewOperType: "update", }) ); }) ).then(() => { layer.close(index); layer.msg("改动暂存成功!"); // 直接 reload location.reload(); }); }); }) .appendTo($menu); $(` <li><a href="javascript:void(0);"><span class="glyphicon glyphicon-open"></span>恢复暂存改动</a></li> `) .on("click", function () { var items = JSON.parse( localStorage.getItem(getStashKey(namespaceScope, namespace)) ); if (!items || items.length === 0) { layer.msg("没有暂存数据"); return; } layer.open({ content: getUnstashTable(items), yes: function (index) { // recovery old value Promise.all( items.map((item) => { var isNewValue = item.isModified && !item.oldValue; var isDeleted = item.isDeleted; if (isDeleted) { // 重新拿一次 itemid item = namespace.items.filter( (v) => v.item.key === item.item.key )[0]; // 取第一个 // 新增的配置 删除 return ConfigService.delete_item( namespace.baseInfo.appId, namespaceScope.pageContext.env, namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName, item.item.id ); } if (isNewValue) { // 被删除的数据 拿不到原来的 item 信息 // 那就 create return ConfigService.create_item( namespace.baseInfo.appId, namespaceScope.pageContext.env, namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName, { key: item.item.key, value: item.newValue, tableViewOperType: "create", addItemBtnDisabled: true, } ); } // 更新的 key return ConfigService.update_item( namespace.baseInfo.appId, namespaceScope.pageContext.env, namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName, $.extend(item.item, { value: item.newValue, tableViewOperType: "update", }) ); }) ).then(() => { layer.close(index); layer.msg("恢复暂存成功!"); localStorage.removeItem( getStashKey(namespaceScope, namespace) ); // 直接 reload location.reload(); }); }, }); }) .appendTo($menu); console.log("add stash", namespaceName); } ); }); }); function getStashKey(namespaceScope, namespace) { var baseInfo = namespace.baseInfo; return `stash_${namespaceScope.pageContext.env}_${baseInfo.appId}_${baseInfo.clusterName}_${baseInfo.namespaceName}`; } function getUnstashTable(changedItems) { var tab = ""; for (var item of changedItems) { tab += `<tr> <td>${item.item.key}</td> <td data-value="${item.newValue}"> ${ item.newValue.length < 150 ? item.newValue : item.newValue.substring(0, 150) + "..." } </td> <td>${item.item.comment ? item.item.comment : ""}</td> <td>${ item.item.dataChangeLastModifiedTime ? item.item.dataChangeLastModifiedTime : "" }</td> </tr>`; } return ` <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th> Key </th> <th> new Value </th> <th> 备注 </th> <th> 最后修改时间 </th> </tr> </thead> <tbody id="unstashTable"> ${tab} </tbody> </table>`; } const DateType = { isJson(val) { try { JSON.parse(val); return true } catch (e) { return false } } }; const cm_modules = { core: { name: 'core', js: "https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/codemirror.min.js", css: "https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/codemirror.min.css" }, mode: { json: { alias: "javascript", mode: "application/json", addons: ['json-lint'] }, javascript: { js: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/mode/javascript/javascript.min.js", mode: "application/javascript", addons: ['matchbrackets'] } }, addon: { dialog: { preload: true, css: "https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/dialog/dialog.min.css", js: "https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/dialog/dialog.min.js" }, panel: { preload: true, js: "https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/display/panel.min.js" }, matchbrackets: { preload: true, js: "https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/edit/matchbrackets.min.js" }, foldcode: { js: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/fold/foldcode.min.js" }, foldgutter: { preload: true, js: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/fold/foldgutter.min.js", css: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/fold/foldgutter.min.css" }, "indent-fold": { preload: true, js: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/fold/indent-fold.min.js" }, "json-lint": { js: "https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/lint/json-lint.min.js" }, "active-line": { preload: true, js: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/selection/active-line.min.js" }, "annotatescrollbar": { preload: true, js: "https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/scroll/annotatescrollbar.min.js" }, "search": { preload: true, js: "https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/search/search.js" }, "searchcursor": { preload: true, js: "https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/search/searchcursor.min.js" }, "matchesonscrollbar": { preload: true, css: "https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/search/matchesonscrollbar.min.css", js: "https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/search/matchesonscrollbar.min.js" }, "match-highlighter": { preload: true, js: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/search/match-highlighter.min.js" }, "jump-to-line": { preload: true, js: "https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/codemirror/5.65.2/addon/search/jump-to-line.min.js" }, "simplescrollbars": { preload: true, css: "https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/codemirror/5.65.2/addon/scroll/simplescrollbars.css", js: "https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-y/codemirror/5.65.2/addon/scroll/simplescrollbars.min.js" } } }; const preLoadModule = []; (function () { ['mode', 'addon'].forEach(type => { for (let module of Object.keys(cm_modules[type])) { const modeO = cm_modules[type][module]; modeO.name = module; if (modeO.preload) { preLoadModule.push(modeO); } } }); })(); const panels = []; var cm = { detectMode(val) { let mode; if (DateType.isJson(val)) { mode = 'json'; } return mode; }, detectModeAndLoad(val) { const mode = this.detectMode(val); if (!mode) { return Promise.resolve(); } return this._getAndloadMode(mode).then((modeO) => modeO?.mode) }, init(cmObj) { cmObj.setOption("scrollbarStyle", "overlay"); const panel = cmObj.addPanel($(` <div style="min-height:20px" class="clearfix"> <div class="pull-right"> <button class="btn btn-xs" type="button" data-cm-edit-type="format">json格式化</button> <button class="btn btn-xs" type="button" data-cm-edit-type="zip">json压缩</button> </div> </div> `)[0], { position: 'top', stable: true }); panels.push(panel); $(document).on('click', 'button[data-cm-edit-type]', function (event) { const type = $(this).attr('data-cm-edit-type'); let value = cmObj.getValue(); const isJson = DateType.isJson(value); if (!isJson) { return } switch (type) { case 'format': value = JSON.stringify(JSON.parse(value), null, 2); break case 'zip': value = JSON.stringify(JSON.parse(value)); break } cmObj.setValue(value); }); }, destory(cmObj) { for (const panel of panels) { panel.clear(); } cmObj && cmObj.toTextArea(); }, autoMode(cmObj) { }, _getAndloadMode(mode) { let modeO = cm_modules.mode[mode]; if (!modeO) { console.error(`不支持的mode:${mode}`); return Promise.resolve() } const alias = modeO.alias; if (alias) { return this._loadModeAddon(modeO).then(() => this._getAndloadMode(alias)) } else { return this._loadModeAddon(modeO).then(() => this._loadModule(modeO)) } }, _loadModule(moduleO) { if (moduleO.loaded) { return Promise.resolve(moduleO); } moduleO.css && loadCss(moduleO.css); return loadJs(moduleO.js) .then(() => { moduleO.loaded = true; return moduleO; }) }, _loadAddon(addOn) { const addonO = cm_modules.addon[addOn]; return this._loadModule(addonO) }, _loadModeAddon(modeO) { if (modeO.addons) { return Promise.all(modeO.addons.map(addon => this._loadAddon(addon))) } return Promise.resolve() } }; $(function () { cm._loadModule(cm_modules.core).then(() => { preLoadModule.forEach(moduleO => { cm._loadModule(moduleO); }); }); }); loadFeature("valueCodeEditor", false, function () { let cmObj; function sync() { if (cmObj) { cmObj.save(); $(cmObj.getTextArea()).trigger('change'); // 触发angular.js } } $("#itemModal") .on("shown.bs.modal", function () { const $textarea = $('#itemModal textarea[name=value]'); cm.detectModeAndLoad($textarea.val()) .then(mode => { console.log(mode); cmObj = CodeMirror.fromTextArea($textarea[0], { lineNumbers: true, lineWrapping: true, styleActiveLine: true, mode: mode }); cmObj.setSize('auto', '500px'); cmObj.on('changes', function () { sync(); }); cm.init(cmObj); }); //$("html").css("overflow", "hidden"); //htmlScroller.hide(); }) .on("hidden.bs.modal", function () { cm.destory(cmObj); console.log(cmObj); }); var $btn = $("#itemModal div.modal-footer").find("button[type=submit]"); $btn.click(function (e) { sync(); }); }); BASE_INFO.version = "0.9.16"; loadFeature("main", { switch: false }, function () { $("body").trigger("featureLoaded"); console.log("trigger featureLoaded v:", BASE_INFO); }); })(_);