diff --git a/EasyGoAdmin_Gin_AntdVue/.vitepress/config/shared.ts b/EasyGoAdmin_Gin_AntdVue/.vitepress/config/shared.ts
index 904f0b1..22674f2 100644
--- a/EasyGoAdmin_Gin_AntdVue/.vitepress/config/shared.ts
+++ b/EasyGoAdmin_Gin_AntdVue/.vitepress/config/shared.ts
@@ -89,7 +89,7 @@ export const sharedConfig = defineConfig({
socialLinks: [
{
icon: "github",
- link: "https://gitee.com/easygoadmin/EasyGoAdmin_Gin_EleVue",
+ link: "https://gitee.com/easygoadmin/EasyGoAdmin_GoFrame_EleVue",
},
],
@@ -100,7 +100,7 @@ export const sharedConfig = defineConfig({
},
editLink: {
- pattern: "https://gitee.com/easygoadmin/EasyGoAdmin_Gin_EleVue",
+ pattern: "https://gitee.com/easygoadmin/EasyGoAdmin_GoFrame_EleVue",
text: "对本页提出修改建议",
},
diff --git a/EasyGoAdmin_Gin_AntdVue/public/banner.js b/EasyGoAdmin_Gin_AntdVue/public/banner.js
new file mode 100644
index 0000000..85c12c8
--- /dev/null
+++ b/EasyGoAdmin_Gin_AntdVue/public/banner.js
@@ -0,0 +1,257 @@
+((window.BitterBrainsBanner = window.BitterBrainsBanner || {
+ host: "",
+ affiliateKey: "vuerouter",
+ emulateDate: void 0,
+ emulateHour: void 0,
+ date: "03/09/2025, 01:30 PM Central European Standard Time",
+ activePhase: {
+ id: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8",
+ countdownEnds: "2025-03-09T22:59:59.000000Z",
+ cta: "获取官方授权",
+ ends: "2025-03-09T22:59:59.000000Z",
+ title: "新品首发,限时特惠,即刻抢购! ",
+ link: "https://www.easygoadmin.vip",
+ assets: null,
+ static: null,
+ classes: ["bb-campaign-vs-affiliates-freeweekend-urgent"],
+ prelude: "EasyGoAdmin · 企业级旗舰产品",
+ // addendum: "Extended for",
+ subtitle: "赋能开发者,驱动企业增长,提供一站式数据中台解决方案。",
+ couponCode: null,
+ isExtended: !1,
+ hideBanners: !1,
+ assetsFormat: "png",
+ showCountdown: 0,
+ countdownFooter: null,
+ showDaysAsHours: !0,
+ affiliates: [
+ "vuerouter",
+ "pinia",
+ "vueuse",
+ "vuejsforge",
+ "nuxtnation",
+ "vuejsdevelopers",
+ "VUEDEMY",
+ "vs_masteringpinia",
+ "vuejsnation",
+ "SENODEV",
+ "NSCRIPT",
+ ],
+ interlude: null,
+ starts: "2025-03-08T22:59:59.000Z",
+ isRunning: !0,
+ remaining: 37757670,
+ closedKey: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8_CLOSED",
+ countdownRemaining: 37757670,
+ overrides: { vuejs: { hideSeconds: !0 } },
+ logoMobile: "/banners/mark.svg",
+ logoTablet: "/banners/mark.svg",
+ logoDesktop: "/banners/logo.svg",
+ },
+ fromCache: !1,
+ cacheTtl: 0,
+ type: "top",
+ closeable: void 0,
+ isOpen: !1,
+ countdownInterval: null,
+ isInitiated: !1,
+ style: null,
+ allowList: {
+ masteringnuxt: (n) => "/" === n || "/nuxt2" === n || "/nuxt3" === n,
+ vueschool: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons") ||
+ n.startsWith("/vueschool-demo"),
+ vueschool_free: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vueschool_premium: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vs_frontendnation: (n) => "/" === n,
+ },
+ render() {
+ const {
+ countdownFooter: n,
+ prelude: e,
+ interlude: t,
+ addendum: b,
+ title: a,
+ subtitle: i,
+ cta: o,
+ link: r,
+ classes: s,
+ showCountdown: l,
+ } = this.activePhase,
+ d = n ? `
` : "",
+ c = `\n \n \n \n \n
\n ${
+ e ? `
${e}
` : ""
+ }\n
\n
${a}
\n ${
+ i ? `
${i}
` : ""
+ }\n
\n ${
+ t ? `
${t}
` : ""
+ }\n
\n ${
+ o
+ ? `
`
+ : ""
+ }\n
\n ${
+ b ? `
${b}
` : ""
+ }\n ${
+ l
+ ? `
${this.renderCountdown()}
${d}
`
+ : ""
+ }\n
\n
\n \n `,
+ p = document.createElement("a");
+ p.setAttribute("id", "bb-banner");
+ const u = {
+ utm_source: this.affiliateKey,
+ utm_medium: "website",
+ utm_campaign: "affiliate",
+ utm_content: `${this.type}_banner`,
+ banner_type: this.type,
+ },
+ g = "internal" in this.activePhase && !!this.activePhase?.internal;
+ (!g && p.setAttribute("target", "_blank"),
+ "vueschool" !== this.affiliateKey &&
+ (u.friend = this.affiliateKey?.toUpperCase()));
+ const h = new URLSearchParams(g ? {} : u).toString();
+ (p.setAttribute("href", `${r}${g ? "" : "?" + h}`),
+ p.classList.add(...s),
+ (p.innerHTML = c),
+ this.addBodyClasses(),
+ (function () {
+ let n = document.querySelector("#bb-banner-video");
+ (n ||
+ (n = document.querySelector("#bb-banner-container") || document.body),
+ n.append(p));
+ })(),
+ addEventListener("popstate", (n) => {
+ this.handleNavigation();
+ }),
+ (window.history.pushState = new Proxy(window.history.pushState, {
+ apply: (n, e, t) => {
+ const b = n.apply(e, t);
+ return (this.handleNavigation(), b);
+ },
+ })));
+ },
+ handleNavigation() {
+ this.addBodyClasses();
+ },
+ addBodyClasses() {
+ if (!this.isOpen) return;
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.add("has-bb-banner"),
+ n.classList.add(`bb-type-${this.type}`),
+ this.affiliateKey && n.classList.add(`bb-${this.affiliateKey}`));
+ const e = window.location.pathname || "/",
+ t = !!window.BitterBrainsBanner.emulateDate;
+ !this.allowList[this.affiliateKey] ||
+ this.allowList[this.affiliateKey](e) ||
+ t
+ ? n.classList.remove("bb-banner-hidden")
+ : n.classList.add("bb-banner-hidden");
+ },
+ renderCountdown() {
+ const { showDaysAsHours: n, hideSeconds: e } = this.activePhase,
+ t = ["hours", "minutes"];
+ return (
+ n || t.unshift("days"),
+ e || t.push("seconds"),
+ t.map((n) => this.renderCountdownPart(n)).join("")
+ );
+ },
+ renderCountdownPart: (n) =>
+ `\n \n
\n
${
+ n[0]
+ }${n.slice(
+ 1,
+ )}
\n
\n
:
\n
\n `,
+ getType() {
+ if (document.querySelector("#bb-banner-video")) return "video";
+ return document.querySelector("#bb-banner-container")
+ ? "inline"
+ : this.type && ["top", "bottom"].includes(this.type)
+ ? this.type
+ : "top";
+ },
+ insertStyleTag() {
+ const n = document.createElement("style");
+ ((n.textContent = window.BitterBrainsBanner.style),
+ document.head.append(n));
+ },
+ close() {
+ document.querySelector("#bb-banner").remove();
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.remove("has-bb-banner"),
+ n.classList.remove("bb-banner-container"),
+ clearInterval(this.countdownInterval),
+ (this.isOpen = !1));
+ },
+ bindCloseButton() {
+ document.querySelector("#bb-close").addEventListener("click", (n) => {
+ (n.preventDefault(),
+ n.stopPropagation(),
+ this.close(),
+ localStorage.setItem(this.activePhase.closedKey, 1),
+ localStorage.setItem(`${this.activePhase.id}_BANNER`, 1));
+ });
+ },
+ startCountdown() {
+ this.activePhase.countdownRemaining < 0 ||
+ (this.updateCountdown(),
+ this.activePhase.isCountdownStatic ||
+ (this.countdownInterval = setInterval(() => {
+ ((this.activePhase.countdownRemaining -= 1e3),
+ this.activePhase.countdownRemaining > 0
+ ? this.updateCountdown()
+ : this.close());
+ }, 1e3)));
+ },
+ updateCountdown() {
+ if (!this.isOpen) return;
+ const { countdownRemaining: n, showDaysAsHours: e } = this.activePhase,
+ t = {
+ days: Math.floor(n / 864e5),
+ hours: Math.floor((n % 864e5) / 36e5),
+ minutes: Math.floor((n % 36e5) / 6e4),
+ seconds: Math.floor((n % 6e4) / 1e3),
+ };
+ e && ((t.hours = t.hours + 24 * t.days), delete t.days);
+ for (const [n, e] of Object.entries(t)) {
+ const t = document.querySelector(`[data-countdown=${n}]`);
+ t && (t.textContent = e.toString().padStart(2, "0"));
+ }
+ },
+ init() {
+ if (
+ !this.activePhase ||
+ localStorage.getItem(this.activePhase.closedKey) ||
+ this.isInitiated
+ )
+ return console.info("Not initiating");
+ ((this.isOpen = !0), (this.isInitiated = !0));
+ const n = setInterval(() => {
+ "complete" === document.readyState &&
+ (clearInterval(n),
+ (this.type = this.getType()),
+ this.insertStyleTag(),
+ this.render(),
+ this.bindCloseButton(),
+ this.activePhase.showCountdown && this.startCountdown());
+ }, 100);
+ },
+}),
+ (window.BitterBrainsBanner.style =
+ "\n :root { --bb-logo-desktop: url(/banners/logo.svg);--bb-logo-tablet: url(/banners/mark.svg);--bb-logo-mobile: url(/banners/mark.svg); }#bb-banner {\n height: 72px;\n text-decoration: none;\n color: white;\n padding-right: 30px;\n display: flex;\n justify-content: center;\n align-items: center;\n background: #000;\n overflow: hidden;\n line-height: 1.25;\n background: var(--bb-gradient);\n direction: ltr !important;\n}\n\n#bb-banner .bb-logo {\n width: 30px;\n height: 19px;\n left: 16px;\n position: absolute;\n background-size: contain;\n background-repeat: no-repeat;\n background-image: var(--bb-logo-mobile);\n}\n\n#bb-banner .bb-close {\n font-family: sans-serif;\n color: white;\n position: absolute;\n top: 29px;\n right: 8px;\n line-height: 0;\n font-size: 24px;\n height: 14px;\n width: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n#bb-banner .bb-background {\n background-image: var(--bb-bg-mobile);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n position: absolute;\n z-index: 0;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n}\n\n#bb-banner .bb-core {\n display: flex;\n align-items: center;\n text-align: center;\n position: relative;\n z-index: 10;\n flex-direction: column;\n}\n\n#bb-banner .bb-core .bb-slogan {\n display: flex;\n flex-direction: row;\n align-items: center;\n order: 1;\n}\n\n#bb-banner .bb-core .bb-title-container {\n display: flex;\n align-items: center;\n flex-direction: column;\n gap: 2px;\n}\n\n#bb-banner .bb-core .bb-extended {\n display: none;\n}\n\n#bb-banner .bb-core .bb-title {\n font-size: 14px;\n font-weight: bold;\n color: #00dc82;\n}\n\n#bb-banner .bb-core .bb-subtitle {\n color: #FFF;\n font-size: 12px;\n}\n\n#bb-banner .bb-core .bb-button-wrapper {\n order: 2;\n}\n\n#bb-banner .bb-core .bb-button-wrapper .bb-button {\n border-radius: 6px;\n font-weight: bold;\n color: #000;\n background: white;\n font-size: 14px;\n padding: 14px 8px;\n white-space: nowrap;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper {\n display: flex;\n align-items: center;\n flex-direction: row-reverse;\n gap: 6px;\n order: 3;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper .bb-countdown-footer {\n order: 1;\n}\n\n#bb-banner .bb-core .bb-countdown {\n align-items: center;\n line-height: 1;\n display: flex;\n font-weight: bold;\n font-size: 12px;\n color: #00dc82;\n order: 2;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item {\n display: flex;\n align-items: center;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part > div {\n display: inline;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item:last-child .bb-countdown-colon {\n display: none;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part .bb-countdown-text .bb-countdown-text-initial~span {\n display: none;\n}\n\n\n@media screen and (max-width: 768px) {\n #bb-banner {\n padding-right: 0px !important;\n }\n}\n\n@media (min-width: 768px) {\n\n #bb-banner .bb-close {\n right: 12px;\n }\n\n #bb-banner .bb-core {\n gap: 12px;\n flex-direction: row;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-tablet);\n }\n\n #bb-banner .bb-logo {\n background-image: var(--bb-logo-tablet);\n }\n\n #bb-banner .bb-core .bb-title {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-subtitle {\n font-size: 16px;\n }\n\n #bb-banner .bb-core .bb-button-wrapper {\n display: block;\n }\n\n #bb-banner .bb-core .bb-button-wrapper .bb-button {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\n@media (min-width: 1280px) {\n #bb-banner .bb-core .bb-title-container {\n gap: 4px;\n }\n \n #bb-banner .bb-logo {\n width: 152px;\n left: 24px;\n background-image: var(--bb-logo-desktop);\n }\n\n #bb-banner .bb-core .bb-slogan {\n gap: 4px;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-desktop);\n }\n\n #bb-banner .bb-core {\n gap: 32px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\nhtml.has-bb-banner.bb-banner-hidden #bb-banner {\n display: none;\n}\n\n/*\nBANNER TYPES\n*********************************************************/\n\n/* Banner rendered in container element */\n\nhtml.has-bb-banner #bb-banner-container #bb-banner, html.has-bb-banner #bb-banner-video #bb-banner {\n position: relative;\n}\n\nhtml.has-bb-banner #bb-banner-container #bb-banner .bb-close {\n/* display: none;*/\n}\n\n/* Banner Fixed Top */\n\nhtml.has-bb-banner.bb-type-top {\n margin-top: 72px;\n}\n\nhtml.has-bb-banner.bb-type-top.bb-banner-hidden {\n margin-top: 0;\n}\n\nhtml.has-bb-banner.bb-type-top #bb-banner {\n position: fixed;\n top: 0;\n z-index: 60;\n left: 0;\n right: 0;\n}\n\n/* Banner Fixed Bottom */\n\nhtml.has-bb-banner.bb-type-bottom #bb-banner {\n position: fixed;\n bottom: 10px;\n z-index: 60;\n left: 10px;\n right: 10px;\n}\n/* Vue School */\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent {\n background: #100D1C;\n font-family: 'Inter', sans-serif;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent:hover .bb-core .bb-button-wrapper .bb-button {\n background: #0EFAA5;\n color: #100D1C;\n transition: background-color 0.5s, color 0.5s; \n }\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 122px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/logo.svg') !important;\n background-repeat: no-repeat;\n}\n\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 60px;\n transform: translateX(80px);\n\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\ncolor: #FFF;\nfont-size: 24px;\nfont-style: normal;\nfont-weight: 900;\nline-height: normal; \nwidth: auto;\n \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude b {\n background: var(--Gradient, linear-gradient(90deg, #0EFAA5 1.04%, #FAFF00 100%));\n background-clip: text;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent; \n font-style: normal;\n font-weight: 00;\n line-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 60px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title {\n color: white;\ntext-align: center;\nfont-size: 16px;\nfont-style: normal;\nfont-weight: 400;\nline-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title b {\n color: #0EFAA5;\n font-weight: 700;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-subtitle {\n font-weight: 700;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-addendum {color: var(--White, #FFF);\n text-align: center;\n font-size: 10px;\n font-style: normal;\n font-weight: 700;\n line-height: normal;\n letter-spacing: 1px;\n text-transform: uppercase; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n order: 3;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\npadding: 6px 14px;\nborder-radius: 4px;\nbackground: rgba(43, 246, 163, 0.20);\ncolor: white;\ntext-align: center;\nfont-size: 18px;\nfont-style: normal;\nfont-weight: 800;\nline-height: normal;\nletter-spacing: 1.4px; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper .bb-button {\n display: flex;\n color: white;\n align-items: center;\n justify-items: center;\n height: 44px;\n padding: 0px 24px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 50px;\nborder: 2px solid #0EFAA5;\nbackground: rgba(14, 250, 165, 0.40);\nfont-size: 14px;\nfont-style: normal;\nfont-weight: 700;\nline-height: normal; \n}\n\n\n\n@media screen and (max-width: 1440px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n gap: 30px;\n transform: translateX(0px);\n\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 50px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/mark.svg') !important;\n background-repeat: no-repeat !important;\n margin-right: 40px;\n }\n}\n@media screen and (max-width: 1280px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n }\n}\n\n@media screen and (max-width: 1234px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n margin-right: 40px;\n }\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: row;\n justify-items: center;\n position: absolute;\n bottom: 0%;\n left: 50%;\n transform: translate(-35%, 46%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\n color: #0BD88F;\n padding: 0px;\n background: transparent;\ntext-align: center;\nfont-size: 12px;\nfont-style: normal;\nfont-weight: 700;\nline-height: 16px; /* 177.778% */\nletter-spacing: 0.9px; \nmargin-left: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: -10px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 35px;\n height: 40px;\n position: relative;\n display: block;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n width: 160px;\n text-align: center;\n font-size: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 14px !important;\n }\n}\n\n@media screen and (max-width: 768px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n position: relative;\n bottom: 0%;\n left: 0%;\n transform: translate(0%, 0%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n margin-top: 0px;\n gap: 0px;\n flex-direction: column;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: 0px;\n gap: 0px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 0px;\n flex-direction: column;\n \n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 11px !important;\n }\n \n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper {\n display: none;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n font-size: 13px; \n width: auto;\n }\n}\n/* Vue Router and Pinia */\n\nhtml.has-bb-banner .theme,\nhtml.has-bb-banner .theme .nav-bar,\nhtml.has-bb-banner .theme .sidebar,\nhtml.has-bb-banner .theme .page {\n margin-top: 72px;\n}\n\n/* Vite Press */\n\nhtml.has-bb-banner {\n scroll-padding-top: 134px;\n overflow: auto;\n}\n\nhtml.has-bb-banner .VPSidebar,\nhtml.has-bb-banner .VPDoc.has-sidebar.has-aside .aside-container {\n top: 72px;\n}\n\n@media (min-width: 960px) {\n html.has-bb-banner .VPNav {\n top: 72px;\n }\n}\n\n "));
+try {
+ window.BitterBrainsBanner.init();
+} catch {
+ console.warn("Error when trying to render top banner");
+}
diff --git a/EasyGoAdmin_Gin_EleVue/.vitepress/config/shared.ts b/EasyGoAdmin_Gin_EleVue/.vitepress/config/shared.ts
index 904f0b1..22674f2 100644
--- a/EasyGoAdmin_Gin_EleVue/.vitepress/config/shared.ts
+++ b/EasyGoAdmin_Gin_EleVue/.vitepress/config/shared.ts
@@ -89,7 +89,7 @@ export const sharedConfig = defineConfig({
socialLinks: [
{
icon: "github",
- link: "https://gitee.com/easygoadmin/EasyGoAdmin_Gin_EleVue",
+ link: "https://gitee.com/easygoadmin/EasyGoAdmin_GoFrame_EleVue",
},
],
@@ -100,7 +100,7 @@ export const sharedConfig = defineConfig({
},
editLink: {
- pattern: "https://gitee.com/easygoadmin/EasyGoAdmin_Gin_EleVue",
+ pattern: "https://gitee.com/easygoadmin/EasyGoAdmin_GoFrame_EleVue",
text: "对本页提出修改建议",
},
diff --git a/EasyGoAdmin_Gin_EleVue/public/banner.js b/EasyGoAdmin_Gin_EleVue/public/banner.js
new file mode 100644
index 0000000..85c12c8
--- /dev/null
+++ b/EasyGoAdmin_Gin_EleVue/public/banner.js
@@ -0,0 +1,257 @@
+((window.BitterBrainsBanner = window.BitterBrainsBanner || {
+ host: "",
+ affiliateKey: "vuerouter",
+ emulateDate: void 0,
+ emulateHour: void 0,
+ date: "03/09/2025, 01:30 PM Central European Standard Time",
+ activePhase: {
+ id: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8",
+ countdownEnds: "2025-03-09T22:59:59.000000Z",
+ cta: "获取官方授权",
+ ends: "2025-03-09T22:59:59.000000Z",
+ title: "新品首发,限时特惠,即刻抢购! ",
+ link: "https://www.easygoadmin.vip",
+ assets: null,
+ static: null,
+ classes: ["bb-campaign-vs-affiliates-freeweekend-urgent"],
+ prelude: "EasyGoAdmin · 企业级旗舰产品",
+ // addendum: "Extended for",
+ subtitle: "赋能开发者,驱动企业增长,提供一站式数据中台解决方案。",
+ couponCode: null,
+ isExtended: !1,
+ hideBanners: !1,
+ assetsFormat: "png",
+ showCountdown: 0,
+ countdownFooter: null,
+ showDaysAsHours: !0,
+ affiliates: [
+ "vuerouter",
+ "pinia",
+ "vueuse",
+ "vuejsforge",
+ "nuxtnation",
+ "vuejsdevelopers",
+ "VUEDEMY",
+ "vs_masteringpinia",
+ "vuejsnation",
+ "SENODEV",
+ "NSCRIPT",
+ ],
+ interlude: null,
+ starts: "2025-03-08T22:59:59.000Z",
+ isRunning: !0,
+ remaining: 37757670,
+ closedKey: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8_CLOSED",
+ countdownRemaining: 37757670,
+ overrides: { vuejs: { hideSeconds: !0 } },
+ logoMobile: "/banners/mark.svg",
+ logoTablet: "/banners/mark.svg",
+ logoDesktop: "/banners/logo.svg",
+ },
+ fromCache: !1,
+ cacheTtl: 0,
+ type: "top",
+ closeable: void 0,
+ isOpen: !1,
+ countdownInterval: null,
+ isInitiated: !1,
+ style: null,
+ allowList: {
+ masteringnuxt: (n) => "/" === n || "/nuxt2" === n || "/nuxt3" === n,
+ vueschool: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons") ||
+ n.startsWith("/vueschool-demo"),
+ vueschool_free: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vueschool_premium: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vs_frontendnation: (n) => "/" === n,
+ },
+ render() {
+ const {
+ countdownFooter: n,
+ prelude: e,
+ interlude: t,
+ addendum: b,
+ title: a,
+ subtitle: i,
+ cta: o,
+ link: r,
+ classes: s,
+ showCountdown: l,
+ } = this.activePhase,
+ d = n ? `` : "",
+ c = `\n \n \n \n \n
\n ${
+ e ? `
${e}
` : ""
+ }\n
\n
${a}
\n ${
+ i ? `
${i}
` : ""
+ }\n
\n ${
+ t ? `
${t}
` : ""
+ }\n
\n ${
+ o
+ ? `
`
+ : ""
+ }\n
\n ${
+ b ? `
${b}
` : ""
+ }\n ${
+ l
+ ? `
${this.renderCountdown()}
${d}
`
+ : ""
+ }\n
\n
\n \n `,
+ p = document.createElement("a");
+ p.setAttribute("id", "bb-banner");
+ const u = {
+ utm_source: this.affiliateKey,
+ utm_medium: "website",
+ utm_campaign: "affiliate",
+ utm_content: `${this.type}_banner`,
+ banner_type: this.type,
+ },
+ g = "internal" in this.activePhase && !!this.activePhase?.internal;
+ (!g && p.setAttribute("target", "_blank"),
+ "vueschool" !== this.affiliateKey &&
+ (u.friend = this.affiliateKey?.toUpperCase()));
+ const h = new URLSearchParams(g ? {} : u).toString();
+ (p.setAttribute("href", `${r}${g ? "" : "?" + h}`),
+ p.classList.add(...s),
+ (p.innerHTML = c),
+ this.addBodyClasses(),
+ (function () {
+ let n = document.querySelector("#bb-banner-video");
+ (n ||
+ (n = document.querySelector("#bb-banner-container") || document.body),
+ n.append(p));
+ })(),
+ addEventListener("popstate", (n) => {
+ this.handleNavigation();
+ }),
+ (window.history.pushState = new Proxy(window.history.pushState, {
+ apply: (n, e, t) => {
+ const b = n.apply(e, t);
+ return (this.handleNavigation(), b);
+ },
+ })));
+ },
+ handleNavigation() {
+ this.addBodyClasses();
+ },
+ addBodyClasses() {
+ if (!this.isOpen) return;
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.add("has-bb-banner"),
+ n.classList.add(`bb-type-${this.type}`),
+ this.affiliateKey && n.classList.add(`bb-${this.affiliateKey}`));
+ const e = window.location.pathname || "/",
+ t = !!window.BitterBrainsBanner.emulateDate;
+ !this.allowList[this.affiliateKey] ||
+ this.allowList[this.affiliateKey](e) ||
+ t
+ ? n.classList.remove("bb-banner-hidden")
+ : n.classList.add("bb-banner-hidden");
+ },
+ renderCountdown() {
+ const { showDaysAsHours: n, hideSeconds: e } = this.activePhase,
+ t = ["hours", "minutes"];
+ return (
+ n || t.unshift("days"),
+ e || t.push("seconds"),
+ t.map((n) => this.renderCountdownPart(n)).join("")
+ );
+ },
+ renderCountdownPart: (n) =>
+ `\n \n
\n
${
+ n[0]
+ }${n.slice(
+ 1,
+ )}
\n
\n
:
\n
\n `,
+ getType() {
+ if (document.querySelector("#bb-banner-video")) return "video";
+ return document.querySelector("#bb-banner-container")
+ ? "inline"
+ : this.type && ["top", "bottom"].includes(this.type)
+ ? this.type
+ : "top";
+ },
+ insertStyleTag() {
+ const n = document.createElement("style");
+ ((n.textContent = window.BitterBrainsBanner.style),
+ document.head.append(n));
+ },
+ close() {
+ document.querySelector("#bb-banner").remove();
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.remove("has-bb-banner"),
+ n.classList.remove("bb-banner-container"),
+ clearInterval(this.countdownInterval),
+ (this.isOpen = !1));
+ },
+ bindCloseButton() {
+ document.querySelector("#bb-close").addEventListener("click", (n) => {
+ (n.preventDefault(),
+ n.stopPropagation(),
+ this.close(),
+ localStorage.setItem(this.activePhase.closedKey, 1),
+ localStorage.setItem(`${this.activePhase.id}_BANNER`, 1));
+ });
+ },
+ startCountdown() {
+ this.activePhase.countdownRemaining < 0 ||
+ (this.updateCountdown(),
+ this.activePhase.isCountdownStatic ||
+ (this.countdownInterval = setInterval(() => {
+ ((this.activePhase.countdownRemaining -= 1e3),
+ this.activePhase.countdownRemaining > 0
+ ? this.updateCountdown()
+ : this.close());
+ }, 1e3)));
+ },
+ updateCountdown() {
+ if (!this.isOpen) return;
+ const { countdownRemaining: n, showDaysAsHours: e } = this.activePhase,
+ t = {
+ days: Math.floor(n / 864e5),
+ hours: Math.floor((n % 864e5) / 36e5),
+ minutes: Math.floor((n % 36e5) / 6e4),
+ seconds: Math.floor((n % 6e4) / 1e3),
+ };
+ e && ((t.hours = t.hours + 24 * t.days), delete t.days);
+ for (const [n, e] of Object.entries(t)) {
+ const t = document.querySelector(`[data-countdown=${n}]`);
+ t && (t.textContent = e.toString().padStart(2, "0"));
+ }
+ },
+ init() {
+ if (
+ !this.activePhase ||
+ localStorage.getItem(this.activePhase.closedKey) ||
+ this.isInitiated
+ )
+ return console.info("Not initiating");
+ ((this.isOpen = !0), (this.isInitiated = !0));
+ const n = setInterval(() => {
+ "complete" === document.readyState &&
+ (clearInterval(n),
+ (this.type = this.getType()),
+ this.insertStyleTag(),
+ this.render(),
+ this.bindCloseButton(),
+ this.activePhase.showCountdown && this.startCountdown());
+ }, 100);
+ },
+}),
+ (window.BitterBrainsBanner.style =
+ "\n :root { --bb-logo-desktop: url(/banners/logo.svg);--bb-logo-tablet: url(/banners/mark.svg);--bb-logo-mobile: url(/banners/mark.svg); }#bb-banner {\n height: 72px;\n text-decoration: none;\n color: white;\n padding-right: 30px;\n display: flex;\n justify-content: center;\n align-items: center;\n background: #000;\n overflow: hidden;\n line-height: 1.25;\n background: var(--bb-gradient);\n direction: ltr !important;\n}\n\n#bb-banner .bb-logo {\n width: 30px;\n height: 19px;\n left: 16px;\n position: absolute;\n background-size: contain;\n background-repeat: no-repeat;\n background-image: var(--bb-logo-mobile);\n}\n\n#bb-banner .bb-close {\n font-family: sans-serif;\n color: white;\n position: absolute;\n top: 29px;\n right: 8px;\n line-height: 0;\n font-size: 24px;\n height: 14px;\n width: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n#bb-banner .bb-background {\n background-image: var(--bb-bg-mobile);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n position: absolute;\n z-index: 0;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n}\n\n#bb-banner .bb-core {\n display: flex;\n align-items: center;\n text-align: center;\n position: relative;\n z-index: 10;\n flex-direction: column;\n}\n\n#bb-banner .bb-core .bb-slogan {\n display: flex;\n flex-direction: row;\n align-items: center;\n order: 1;\n}\n\n#bb-banner .bb-core .bb-title-container {\n display: flex;\n align-items: center;\n flex-direction: column;\n gap: 2px;\n}\n\n#bb-banner .bb-core .bb-extended {\n display: none;\n}\n\n#bb-banner .bb-core .bb-title {\n font-size: 14px;\n font-weight: bold;\n color: #00dc82;\n}\n\n#bb-banner .bb-core .bb-subtitle {\n color: #FFF;\n font-size: 12px;\n}\n\n#bb-banner .bb-core .bb-button-wrapper {\n order: 2;\n}\n\n#bb-banner .bb-core .bb-button-wrapper .bb-button {\n border-radius: 6px;\n font-weight: bold;\n color: #000;\n background: white;\n font-size: 14px;\n padding: 14px 8px;\n white-space: nowrap;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper {\n display: flex;\n align-items: center;\n flex-direction: row-reverse;\n gap: 6px;\n order: 3;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper .bb-countdown-footer {\n order: 1;\n}\n\n#bb-banner .bb-core .bb-countdown {\n align-items: center;\n line-height: 1;\n display: flex;\n font-weight: bold;\n font-size: 12px;\n color: #00dc82;\n order: 2;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item {\n display: flex;\n align-items: center;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part > div {\n display: inline;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item:last-child .bb-countdown-colon {\n display: none;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part .bb-countdown-text .bb-countdown-text-initial~span {\n display: none;\n}\n\n\n@media screen and (max-width: 768px) {\n #bb-banner {\n padding-right: 0px !important;\n }\n}\n\n@media (min-width: 768px) {\n\n #bb-banner .bb-close {\n right: 12px;\n }\n\n #bb-banner .bb-core {\n gap: 12px;\n flex-direction: row;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-tablet);\n }\n\n #bb-banner .bb-logo {\n background-image: var(--bb-logo-tablet);\n }\n\n #bb-banner .bb-core .bb-title {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-subtitle {\n font-size: 16px;\n }\n\n #bb-banner .bb-core .bb-button-wrapper {\n display: block;\n }\n\n #bb-banner .bb-core .bb-button-wrapper .bb-button {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\n@media (min-width: 1280px) {\n #bb-banner .bb-core .bb-title-container {\n gap: 4px;\n }\n \n #bb-banner .bb-logo {\n width: 152px;\n left: 24px;\n background-image: var(--bb-logo-desktop);\n }\n\n #bb-banner .bb-core .bb-slogan {\n gap: 4px;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-desktop);\n }\n\n #bb-banner .bb-core {\n gap: 32px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\nhtml.has-bb-banner.bb-banner-hidden #bb-banner {\n display: none;\n}\n\n/*\nBANNER TYPES\n*********************************************************/\n\n/* Banner rendered in container element */\n\nhtml.has-bb-banner #bb-banner-container #bb-banner, html.has-bb-banner #bb-banner-video #bb-banner {\n position: relative;\n}\n\nhtml.has-bb-banner #bb-banner-container #bb-banner .bb-close {\n/* display: none;*/\n}\n\n/* Banner Fixed Top */\n\nhtml.has-bb-banner.bb-type-top {\n margin-top: 72px;\n}\n\nhtml.has-bb-banner.bb-type-top.bb-banner-hidden {\n margin-top: 0;\n}\n\nhtml.has-bb-banner.bb-type-top #bb-banner {\n position: fixed;\n top: 0;\n z-index: 60;\n left: 0;\n right: 0;\n}\n\n/* Banner Fixed Bottom */\n\nhtml.has-bb-banner.bb-type-bottom #bb-banner {\n position: fixed;\n bottom: 10px;\n z-index: 60;\n left: 10px;\n right: 10px;\n}\n/* Vue School */\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent {\n background: #100D1C;\n font-family: 'Inter', sans-serif;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent:hover .bb-core .bb-button-wrapper .bb-button {\n background: #0EFAA5;\n color: #100D1C;\n transition: background-color 0.5s, color 0.5s; \n }\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 122px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/logo.svg') !important;\n background-repeat: no-repeat;\n}\n\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 60px;\n transform: translateX(80px);\n\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\ncolor: #FFF;\nfont-size: 24px;\nfont-style: normal;\nfont-weight: 900;\nline-height: normal; \nwidth: auto;\n \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude b {\n background: var(--Gradient, linear-gradient(90deg, #0EFAA5 1.04%, #FAFF00 100%));\n background-clip: text;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent; \n font-style: normal;\n font-weight: 00;\n line-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 60px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title {\n color: white;\ntext-align: center;\nfont-size: 16px;\nfont-style: normal;\nfont-weight: 400;\nline-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title b {\n color: #0EFAA5;\n font-weight: 700;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-subtitle {\n font-weight: 700;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-addendum {color: var(--White, #FFF);\n text-align: center;\n font-size: 10px;\n font-style: normal;\n font-weight: 700;\n line-height: normal;\n letter-spacing: 1px;\n text-transform: uppercase; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n order: 3;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\npadding: 6px 14px;\nborder-radius: 4px;\nbackground: rgba(43, 246, 163, 0.20);\ncolor: white;\ntext-align: center;\nfont-size: 18px;\nfont-style: normal;\nfont-weight: 800;\nline-height: normal;\nletter-spacing: 1.4px; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper .bb-button {\n display: flex;\n color: white;\n align-items: center;\n justify-items: center;\n height: 44px;\n padding: 0px 24px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 50px;\nborder: 2px solid #0EFAA5;\nbackground: rgba(14, 250, 165, 0.40);\nfont-size: 14px;\nfont-style: normal;\nfont-weight: 700;\nline-height: normal; \n}\n\n\n\n@media screen and (max-width: 1440px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n gap: 30px;\n transform: translateX(0px);\n\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 50px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/mark.svg') !important;\n background-repeat: no-repeat !important;\n margin-right: 40px;\n }\n}\n@media screen and (max-width: 1280px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n }\n}\n\n@media screen and (max-width: 1234px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n margin-right: 40px;\n }\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: row;\n justify-items: center;\n position: absolute;\n bottom: 0%;\n left: 50%;\n transform: translate(-35%, 46%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\n color: #0BD88F;\n padding: 0px;\n background: transparent;\ntext-align: center;\nfont-size: 12px;\nfont-style: normal;\nfont-weight: 700;\nline-height: 16px; /* 177.778% */\nletter-spacing: 0.9px; \nmargin-left: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: -10px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 35px;\n height: 40px;\n position: relative;\n display: block;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n width: 160px;\n text-align: center;\n font-size: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 14px !important;\n }\n}\n\n@media screen and (max-width: 768px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n position: relative;\n bottom: 0%;\n left: 0%;\n transform: translate(0%, 0%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n margin-top: 0px;\n gap: 0px;\n flex-direction: column;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: 0px;\n gap: 0px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 0px;\n flex-direction: column;\n \n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 11px !important;\n }\n \n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper {\n display: none;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n font-size: 13px; \n width: auto;\n }\n}\n/* Vue Router and Pinia */\n\nhtml.has-bb-banner .theme,\nhtml.has-bb-banner .theme .nav-bar,\nhtml.has-bb-banner .theme .sidebar,\nhtml.has-bb-banner .theme .page {\n margin-top: 72px;\n}\n\n/* Vite Press */\n\nhtml.has-bb-banner {\n scroll-padding-top: 134px;\n overflow: auto;\n}\n\nhtml.has-bb-banner .VPSidebar,\nhtml.has-bb-banner .VPDoc.has-sidebar.has-aside .aside-container {\n top: 72px;\n}\n\n@media (min-width: 960px) {\n html.has-bb-banner .VPNav {\n top: 72px;\n }\n}\n\n "));
+try {
+ window.BitterBrainsBanner.init();
+} catch {
+ console.warn("Error when trying to render top banner");
+}
diff --git a/EasyGoAdmin_Gin_NaiveVue/.vitepress/config/shared.ts b/EasyGoAdmin_Gin_NaiveVue/.vitepress/config/shared.ts
index 904f0b1..22674f2 100644
--- a/EasyGoAdmin_Gin_NaiveVue/.vitepress/config/shared.ts
+++ b/EasyGoAdmin_Gin_NaiveVue/.vitepress/config/shared.ts
@@ -89,7 +89,7 @@ export const sharedConfig = defineConfig({
socialLinks: [
{
icon: "github",
- link: "https://gitee.com/easygoadmin/EasyGoAdmin_Gin_EleVue",
+ link: "https://gitee.com/easygoadmin/EasyGoAdmin_GoFrame_EleVue",
},
],
@@ -100,7 +100,7 @@ export const sharedConfig = defineConfig({
},
editLink: {
- pattern: "https://gitee.com/easygoadmin/EasyGoAdmin_Gin_EleVue",
+ pattern: "https://gitee.com/easygoadmin/EasyGoAdmin_GoFrame_EleVue",
text: "对本页提出修改建议",
},
diff --git a/EasyGoAdmin_Gin_NaiveVue/public/banner.js b/EasyGoAdmin_Gin_NaiveVue/public/banner.js
new file mode 100644
index 0000000..85c12c8
--- /dev/null
+++ b/EasyGoAdmin_Gin_NaiveVue/public/banner.js
@@ -0,0 +1,257 @@
+((window.BitterBrainsBanner = window.BitterBrainsBanner || {
+ host: "",
+ affiliateKey: "vuerouter",
+ emulateDate: void 0,
+ emulateHour: void 0,
+ date: "03/09/2025, 01:30 PM Central European Standard Time",
+ activePhase: {
+ id: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8",
+ countdownEnds: "2025-03-09T22:59:59.000000Z",
+ cta: "获取官方授权",
+ ends: "2025-03-09T22:59:59.000000Z",
+ title: "新品首发,限时特惠,即刻抢购! ",
+ link: "https://www.easygoadmin.vip",
+ assets: null,
+ static: null,
+ classes: ["bb-campaign-vs-affiliates-freeweekend-urgent"],
+ prelude: "EasyGoAdmin · 企业级旗舰产品",
+ // addendum: "Extended for",
+ subtitle: "赋能开发者,驱动企业增长,提供一站式数据中台解决方案。",
+ couponCode: null,
+ isExtended: !1,
+ hideBanners: !1,
+ assetsFormat: "png",
+ showCountdown: 0,
+ countdownFooter: null,
+ showDaysAsHours: !0,
+ affiliates: [
+ "vuerouter",
+ "pinia",
+ "vueuse",
+ "vuejsforge",
+ "nuxtnation",
+ "vuejsdevelopers",
+ "VUEDEMY",
+ "vs_masteringpinia",
+ "vuejsnation",
+ "SENODEV",
+ "NSCRIPT",
+ ],
+ interlude: null,
+ starts: "2025-03-08T22:59:59.000Z",
+ isRunning: !0,
+ remaining: 37757670,
+ closedKey: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8_CLOSED",
+ countdownRemaining: 37757670,
+ overrides: { vuejs: { hideSeconds: !0 } },
+ logoMobile: "/banners/mark.svg",
+ logoTablet: "/banners/mark.svg",
+ logoDesktop: "/banners/logo.svg",
+ },
+ fromCache: !1,
+ cacheTtl: 0,
+ type: "top",
+ closeable: void 0,
+ isOpen: !1,
+ countdownInterval: null,
+ isInitiated: !1,
+ style: null,
+ allowList: {
+ masteringnuxt: (n) => "/" === n || "/nuxt2" === n || "/nuxt3" === n,
+ vueschool: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons") ||
+ n.startsWith("/vueschool-demo"),
+ vueschool_free: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vueschool_premium: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vs_frontendnation: (n) => "/" === n,
+ },
+ render() {
+ const {
+ countdownFooter: n,
+ prelude: e,
+ interlude: t,
+ addendum: b,
+ title: a,
+ subtitle: i,
+ cta: o,
+ link: r,
+ classes: s,
+ showCountdown: l,
+ } = this.activePhase,
+ d = n ? `` : "",
+ c = `\n \n \n \n \n
\n ${
+ e ? `
${e}
` : ""
+ }\n
\n
${a}
\n ${
+ i ? `
${i}
` : ""
+ }\n
\n ${
+ t ? `
${t}
` : ""
+ }\n
\n ${
+ o
+ ? `
`
+ : ""
+ }\n
\n ${
+ b ? `
${b}
` : ""
+ }\n ${
+ l
+ ? `
${this.renderCountdown()}
${d}
`
+ : ""
+ }\n
\n
\n \n `,
+ p = document.createElement("a");
+ p.setAttribute("id", "bb-banner");
+ const u = {
+ utm_source: this.affiliateKey,
+ utm_medium: "website",
+ utm_campaign: "affiliate",
+ utm_content: `${this.type}_banner`,
+ banner_type: this.type,
+ },
+ g = "internal" in this.activePhase && !!this.activePhase?.internal;
+ (!g && p.setAttribute("target", "_blank"),
+ "vueschool" !== this.affiliateKey &&
+ (u.friend = this.affiliateKey?.toUpperCase()));
+ const h = new URLSearchParams(g ? {} : u).toString();
+ (p.setAttribute("href", `${r}${g ? "" : "?" + h}`),
+ p.classList.add(...s),
+ (p.innerHTML = c),
+ this.addBodyClasses(),
+ (function () {
+ let n = document.querySelector("#bb-banner-video");
+ (n ||
+ (n = document.querySelector("#bb-banner-container") || document.body),
+ n.append(p));
+ })(),
+ addEventListener("popstate", (n) => {
+ this.handleNavigation();
+ }),
+ (window.history.pushState = new Proxy(window.history.pushState, {
+ apply: (n, e, t) => {
+ const b = n.apply(e, t);
+ return (this.handleNavigation(), b);
+ },
+ })));
+ },
+ handleNavigation() {
+ this.addBodyClasses();
+ },
+ addBodyClasses() {
+ if (!this.isOpen) return;
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.add("has-bb-banner"),
+ n.classList.add(`bb-type-${this.type}`),
+ this.affiliateKey && n.classList.add(`bb-${this.affiliateKey}`));
+ const e = window.location.pathname || "/",
+ t = !!window.BitterBrainsBanner.emulateDate;
+ !this.allowList[this.affiliateKey] ||
+ this.allowList[this.affiliateKey](e) ||
+ t
+ ? n.classList.remove("bb-banner-hidden")
+ : n.classList.add("bb-banner-hidden");
+ },
+ renderCountdown() {
+ const { showDaysAsHours: n, hideSeconds: e } = this.activePhase,
+ t = ["hours", "minutes"];
+ return (
+ n || t.unshift("days"),
+ e || t.push("seconds"),
+ t.map((n) => this.renderCountdownPart(n)).join("")
+ );
+ },
+ renderCountdownPart: (n) =>
+ `\n \n
\n
${
+ n[0]
+ }${n.slice(
+ 1,
+ )}
\n
\n
:
\n
\n `,
+ getType() {
+ if (document.querySelector("#bb-banner-video")) return "video";
+ return document.querySelector("#bb-banner-container")
+ ? "inline"
+ : this.type && ["top", "bottom"].includes(this.type)
+ ? this.type
+ : "top";
+ },
+ insertStyleTag() {
+ const n = document.createElement("style");
+ ((n.textContent = window.BitterBrainsBanner.style),
+ document.head.append(n));
+ },
+ close() {
+ document.querySelector("#bb-banner").remove();
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.remove("has-bb-banner"),
+ n.classList.remove("bb-banner-container"),
+ clearInterval(this.countdownInterval),
+ (this.isOpen = !1));
+ },
+ bindCloseButton() {
+ document.querySelector("#bb-close").addEventListener("click", (n) => {
+ (n.preventDefault(),
+ n.stopPropagation(),
+ this.close(),
+ localStorage.setItem(this.activePhase.closedKey, 1),
+ localStorage.setItem(`${this.activePhase.id}_BANNER`, 1));
+ });
+ },
+ startCountdown() {
+ this.activePhase.countdownRemaining < 0 ||
+ (this.updateCountdown(),
+ this.activePhase.isCountdownStatic ||
+ (this.countdownInterval = setInterval(() => {
+ ((this.activePhase.countdownRemaining -= 1e3),
+ this.activePhase.countdownRemaining > 0
+ ? this.updateCountdown()
+ : this.close());
+ }, 1e3)));
+ },
+ updateCountdown() {
+ if (!this.isOpen) return;
+ const { countdownRemaining: n, showDaysAsHours: e } = this.activePhase,
+ t = {
+ days: Math.floor(n / 864e5),
+ hours: Math.floor((n % 864e5) / 36e5),
+ minutes: Math.floor((n % 36e5) / 6e4),
+ seconds: Math.floor((n % 6e4) / 1e3),
+ };
+ e && ((t.hours = t.hours + 24 * t.days), delete t.days);
+ for (const [n, e] of Object.entries(t)) {
+ const t = document.querySelector(`[data-countdown=${n}]`);
+ t && (t.textContent = e.toString().padStart(2, "0"));
+ }
+ },
+ init() {
+ if (
+ !this.activePhase ||
+ localStorage.getItem(this.activePhase.closedKey) ||
+ this.isInitiated
+ )
+ return console.info("Not initiating");
+ ((this.isOpen = !0), (this.isInitiated = !0));
+ const n = setInterval(() => {
+ "complete" === document.readyState &&
+ (clearInterval(n),
+ (this.type = this.getType()),
+ this.insertStyleTag(),
+ this.render(),
+ this.bindCloseButton(),
+ this.activePhase.showCountdown && this.startCountdown());
+ }, 100);
+ },
+}),
+ (window.BitterBrainsBanner.style =
+ "\n :root { --bb-logo-desktop: url(/banners/logo.svg);--bb-logo-tablet: url(/banners/mark.svg);--bb-logo-mobile: url(/banners/mark.svg); }#bb-banner {\n height: 72px;\n text-decoration: none;\n color: white;\n padding-right: 30px;\n display: flex;\n justify-content: center;\n align-items: center;\n background: #000;\n overflow: hidden;\n line-height: 1.25;\n background: var(--bb-gradient);\n direction: ltr !important;\n}\n\n#bb-banner .bb-logo {\n width: 30px;\n height: 19px;\n left: 16px;\n position: absolute;\n background-size: contain;\n background-repeat: no-repeat;\n background-image: var(--bb-logo-mobile);\n}\n\n#bb-banner .bb-close {\n font-family: sans-serif;\n color: white;\n position: absolute;\n top: 29px;\n right: 8px;\n line-height: 0;\n font-size: 24px;\n height: 14px;\n width: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n#bb-banner .bb-background {\n background-image: var(--bb-bg-mobile);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n position: absolute;\n z-index: 0;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n}\n\n#bb-banner .bb-core {\n display: flex;\n align-items: center;\n text-align: center;\n position: relative;\n z-index: 10;\n flex-direction: column;\n}\n\n#bb-banner .bb-core .bb-slogan {\n display: flex;\n flex-direction: row;\n align-items: center;\n order: 1;\n}\n\n#bb-banner .bb-core .bb-title-container {\n display: flex;\n align-items: center;\n flex-direction: column;\n gap: 2px;\n}\n\n#bb-banner .bb-core .bb-extended {\n display: none;\n}\n\n#bb-banner .bb-core .bb-title {\n font-size: 14px;\n font-weight: bold;\n color: #00dc82;\n}\n\n#bb-banner .bb-core .bb-subtitle {\n color: #FFF;\n font-size: 12px;\n}\n\n#bb-banner .bb-core .bb-button-wrapper {\n order: 2;\n}\n\n#bb-banner .bb-core .bb-button-wrapper .bb-button {\n border-radius: 6px;\n font-weight: bold;\n color: #000;\n background: white;\n font-size: 14px;\n padding: 14px 8px;\n white-space: nowrap;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper {\n display: flex;\n align-items: center;\n flex-direction: row-reverse;\n gap: 6px;\n order: 3;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper .bb-countdown-footer {\n order: 1;\n}\n\n#bb-banner .bb-core .bb-countdown {\n align-items: center;\n line-height: 1;\n display: flex;\n font-weight: bold;\n font-size: 12px;\n color: #00dc82;\n order: 2;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item {\n display: flex;\n align-items: center;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part > div {\n display: inline;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item:last-child .bb-countdown-colon {\n display: none;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part .bb-countdown-text .bb-countdown-text-initial~span {\n display: none;\n}\n\n\n@media screen and (max-width: 768px) {\n #bb-banner {\n padding-right: 0px !important;\n }\n}\n\n@media (min-width: 768px) {\n\n #bb-banner .bb-close {\n right: 12px;\n }\n\n #bb-banner .bb-core {\n gap: 12px;\n flex-direction: row;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-tablet);\n }\n\n #bb-banner .bb-logo {\n background-image: var(--bb-logo-tablet);\n }\n\n #bb-banner .bb-core .bb-title {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-subtitle {\n font-size: 16px;\n }\n\n #bb-banner .bb-core .bb-button-wrapper {\n display: block;\n }\n\n #bb-banner .bb-core .bb-button-wrapper .bb-button {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\n@media (min-width: 1280px) {\n #bb-banner .bb-core .bb-title-container {\n gap: 4px;\n }\n \n #bb-banner .bb-logo {\n width: 152px;\n left: 24px;\n background-image: var(--bb-logo-desktop);\n }\n\n #bb-banner .bb-core .bb-slogan {\n gap: 4px;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-desktop);\n }\n\n #bb-banner .bb-core {\n gap: 32px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\nhtml.has-bb-banner.bb-banner-hidden #bb-banner {\n display: none;\n}\n\n/*\nBANNER TYPES\n*********************************************************/\n\n/* Banner rendered in container element */\n\nhtml.has-bb-banner #bb-banner-container #bb-banner, html.has-bb-banner #bb-banner-video #bb-banner {\n position: relative;\n}\n\nhtml.has-bb-banner #bb-banner-container #bb-banner .bb-close {\n/* display: none;*/\n}\n\n/* Banner Fixed Top */\n\nhtml.has-bb-banner.bb-type-top {\n margin-top: 72px;\n}\n\nhtml.has-bb-banner.bb-type-top.bb-banner-hidden {\n margin-top: 0;\n}\n\nhtml.has-bb-banner.bb-type-top #bb-banner {\n position: fixed;\n top: 0;\n z-index: 60;\n left: 0;\n right: 0;\n}\n\n/* Banner Fixed Bottom */\n\nhtml.has-bb-banner.bb-type-bottom #bb-banner {\n position: fixed;\n bottom: 10px;\n z-index: 60;\n left: 10px;\n right: 10px;\n}\n/* Vue School */\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent {\n background: #100D1C;\n font-family: 'Inter', sans-serif;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent:hover .bb-core .bb-button-wrapper .bb-button {\n background: #0EFAA5;\n color: #100D1C;\n transition: background-color 0.5s, color 0.5s; \n }\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 122px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/logo.svg') !important;\n background-repeat: no-repeat;\n}\n\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 60px;\n transform: translateX(80px);\n\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\ncolor: #FFF;\nfont-size: 24px;\nfont-style: normal;\nfont-weight: 900;\nline-height: normal; \nwidth: auto;\n \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude b {\n background: var(--Gradient, linear-gradient(90deg, #0EFAA5 1.04%, #FAFF00 100%));\n background-clip: text;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent; \n font-style: normal;\n font-weight: 00;\n line-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 60px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title {\n color: white;\ntext-align: center;\nfont-size: 16px;\nfont-style: normal;\nfont-weight: 400;\nline-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title b {\n color: #0EFAA5;\n font-weight: 700;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-subtitle {\n font-weight: 700;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-addendum {color: var(--White, #FFF);\n text-align: center;\n font-size: 10px;\n font-style: normal;\n font-weight: 700;\n line-height: normal;\n letter-spacing: 1px;\n text-transform: uppercase; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n order: 3;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\npadding: 6px 14px;\nborder-radius: 4px;\nbackground: rgba(43, 246, 163, 0.20);\ncolor: white;\ntext-align: center;\nfont-size: 18px;\nfont-style: normal;\nfont-weight: 800;\nline-height: normal;\nletter-spacing: 1.4px; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper .bb-button {\n display: flex;\n color: white;\n align-items: center;\n justify-items: center;\n height: 44px;\n padding: 0px 24px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 50px;\nborder: 2px solid #0EFAA5;\nbackground: rgba(14, 250, 165, 0.40);\nfont-size: 14px;\nfont-style: normal;\nfont-weight: 700;\nline-height: normal; \n}\n\n\n\n@media screen and (max-width: 1440px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n gap: 30px;\n transform: translateX(0px);\n\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 50px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/mark.svg') !important;\n background-repeat: no-repeat !important;\n margin-right: 40px;\n }\n}\n@media screen and (max-width: 1280px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n }\n}\n\n@media screen and (max-width: 1234px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n margin-right: 40px;\n }\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: row;\n justify-items: center;\n position: absolute;\n bottom: 0%;\n left: 50%;\n transform: translate(-35%, 46%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\n color: #0BD88F;\n padding: 0px;\n background: transparent;\ntext-align: center;\nfont-size: 12px;\nfont-style: normal;\nfont-weight: 700;\nline-height: 16px; /* 177.778% */\nletter-spacing: 0.9px; \nmargin-left: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: -10px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 35px;\n height: 40px;\n position: relative;\n display: block;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n width: 160px;\n text-align: center;\n font-size: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 14px !important;\n }\n}\n\n@media screen and (max-width: 768px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n position: relative;\n bottom: 0%;\n left: 0%;\n transform: translate(0%, 0%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n margin-top: 0px;\n gap: 0px;\n flex-direction: column;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: 0px;\n gap: 0px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 0px;\n flex-direction: column;\n \n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 11px !important;\n }\n \n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper {\n display: none;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n font-size: 13px; \n width: auto;\n }\n}\n/* Vue Router and Pinia */\n\nhtml.has-bb-banner .theme,\nhtml.has-bb-banner .theme .nav-bar,\nhtml.has-bb-banner .theme .sidebar,\nhtml.has-bb-banner .theme .page {\n margin-top: 72px;\n}\n\n/* Vite Press */\n\nhtml.has-bb-banner {\n scroll-padding-top: 134px;\n overflow: auto;\n}\n\nhtml.has-bb-banner .VPSidebar,\nhtml.has-bb-banner .VPDoc.has-sidebar.has-aside .aside-container {\n top: 72px;\n}\n\n@media (min-width: 960px) {\n html.has-bb-banner .VPNav {\n top: 72px;\n }\n}\n\n "));
+try {
+ window.BitterBrainsBanner.init();
+} catch {
+ console.warn("Error when trying to render top banner");
+}
diff --git a/EasyGoAdmin_GoFrame_AntdVue/.vitepress/config/shared.ts b/EasyGoAdmin_GoFrame_AntdVue/.vitepress/config/shared.ts
index c1e5949..22674f2 100644
--- a/EasyGoAdmin_GoFrame_AntdVue/.vitepress/config/shared.ts
+++ b/EasyGoAdmin_GoFrame_AntdVue/.vitepress/config/shared.ts
@@ -70,6 +70,16 @@ export const sharedConfig = defineConfig({
},
],
+ // Top banner
+ [
+ "script",
+ {
+ src: "/banner.js",
+ // @ts-expect-error: vitepress bug
+ async: true,
+ type: "text/javascript",
+ },
+ ],
],
themeConfig: {
diff --git a/EasyGoAdmin_GoFrame_AntdVue/public/banner.js b/EasyGoAdmin_GoFrame_AntdVue/public/banner.js
new file mode 100644
index 0000000..85c12c8
--- /dev/null
+++ b/EasyGoAdmin_GoFrame_AntdVue/public/banner.js
@@ -0,0 +1,257 @@
+((window.BitterBrainsBanner = window.BitterBrainsBanner || {
+ host: "",
+ affiliateKey: "vuerouter",
+ emulateDate: void 0,
+ emulateHour: void 0,
+ date: "03/09/2025, 01:30 PM Central European Standard Time",
+ activePhase: {
+ id: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8",
+ countdownEnds: "2025-03-09T22:59:59.000000Z",
+ cta: "获取官方授权",
+ ends: "2025-03-09T22:59:59.000000Z",
+ title: "新品首发,限时特惠,即刻抢购! ",
+ link: "https://www.easygoadmin.vip",
+ assets: null,
+ static: null,
+ classes: ["bb-campaign-vs-affiliates-freeweekend-urgent"],
+ prelude: "EasyGoAdmin · 企业级旗舰产品",
+ // addendum: "Extended for",
+ subtitle: "赋能开发者,驱动企业增长,提供一站式数据中台解决方案。",
+ couponCode: null,
+ isExtended: !1,
+ hideBanners: !1,
+ assetsFormat: "png",
+ showCountdown: 0,
+ countdownFooter: null,
+ showDaysAsHours: !0,
+ affiliates: [
+ "vuerouter",
+ "pinia",
+ "vueuse",
+ "vuejsforge",
+ "nuxtnation",
+ "vuejsdevelopers",
+ "VUEDEMY",
+ "vs_masteringpinia",
+ "vuejsnation",
+ "SENODEV",
+ "NSCRIPT",
+ ],
+ interlude: null,
+ starts: "2025-03-08T22:59:59.000Z",
+ isRunning: !0,
+ remaining: 37757670,
+ closedKey: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8_CLOSED",
+ countdownRemaining: 37757670,
+ overrides: { vuejs: { hideSeconds: !0 } },
+ logoMobile: "/banners/mark.svg",
+ logoTablet: "/banners/mark.svg",
+ logoDesktop: "/banners/logo.svg",
+ },
+ fromCache: !1,
+ cacheTtl: 0,
+ type: "top",
+ closeable: void 0,
+ isOpen: !1,
+ countdownInterval: null,
+ isInitiated: !1,
+ style: null,
+ allowList: {
+ masteringnuxt: (n) => "/" === n || "/nuxt2" === n || "/nuxt3" === n,
+ vueschool: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons") ||
+ n.startsWith("/vueschool-demo"),
+ vueschool_free: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vueschool_premium: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vs_frontendnation: (n) => "/" === n,
+ },
+ render() {
+ const {
+ countdownFooter: n,
+ prelude: e,
+ interlude: t,
+ addendum: b,
+ title: a,
+ subtitle: i,
+ cta: o,
+ link: r,
+ classes: s,
+ showCountdown: l,
+ } = this.activePhase,
+ d = n ? `` : "",
+ c = `\n \n \n \n \n
\n ${
+ e ? `
${e}
` : ""
+ }\n
\n
${a}
\n ${
+ i ? `
${i}
` : ""
+ }\n
\n ${
+ t ? `
${t}
` : ""
+ }\n
\n ${
+ o
+ ? `
`
+ : ""
+ }\n
\n ${
+ b ? `
${b}
` : ""
+ }\n ${
+ l
+ ? `
${this.renderCountdown()}
${d}
`
+ : ""
+ }\n
\n
\n \n `,
+ p = document.createElement("a");
+ p.setAttribute("id", "bb-banner");
+ const u = {
+ utm_source: this.affiliateKey,
+ utm_medium: "website",
+ utm_campaign: "affiliate",
+ utm_content: `${this.type}_banner`,
+ banner_type: this.type,
+ },
+ g = "internal" in this.activePhase && !!this.activePhase?.internal;
+ (!g && p.setAttribute("target", "_blank"),
+ "vueschool" !== this.affiliateKey &&
+ (u.friend = this.affiliateKey?.toUpperCase()));
+ const h = new URLSearchParams(g ? {} : u).toString();
+ (p.setAttribute("href", `${r}${g ? "" : "?" + h}`),
+ p.classList.add(...s),
+ (p.innerHTML = c),
+ this.addBodyClasses(),
+ (function () {
+ let n = document.querySelector("#bb-banner-video");
+ (n ||
+ (n = document.querySelector("#bb-banner-container") || document.body),
+ n.append(p));
+ })(),
+ addEventListener("popstate", (n) => {
+ this.handleNavigation();
+ }),
+ (window.history.pushState = new Proxy(window.history.pushState, {
+ apply: (n, e, t) => {
+ const b = n.apply(e, t);
+ return (this.handleNavigation(), b);
+ },
+ })));
+ },
+ handleNavigation() {
+ this.addBodyClasses();
+ },
+ addBodyClasses() {
+ if (!this.isOpen) return;
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.add("has-bb-banner"),
+ n.classList.add(`bb-type-${this.type}`),
+ this.affiliateKey && n.classList.add(`bb-${this.affiliateKey}`));
+ const e = window.location.pathname || "/",
+ t = !!window.BitterBrainsBanner.emulateDate;
+ !this.allowList[this.affiliateKey] ||
+ this.allowList[this.affiliateKey](e) ||
+ t
+ ? n.classList.remove("bb-banner-hidden")
+ : n.classList.add("bb-banner-hidden");
+ },
+ renderCountdown() {
+ const { showDaysAsHours: n, hideSeconds: e } = this.activePhase,
+ t = ["hours", "minutes"];
+ return (
+ n || t.unshift("days"),
+ e || t.push("seconds"),
+ t.map((n) => this.renderCountdownPart(n)).join("")
+ );
+ },
+ renderCountdownPart: (n) =>
+ `\n \n
\n
${
+ n[0]
+ }${n.slice(
+ 1,
+ )}
\n
\n
:
\n
\n `,
+ getType() {
+ if (document.querySelector("#bb-banner-video")) return "video";
+ return document.querySelector("#bb-banner-container")
+ ? "inline"
+ : this.type && ["top", "bottom"].includes(this.type)
+ ? this.type
+ : "top";
+ },
+ insertStyleTag() {
+ const n = document.createElement("style");
+ ((n.textContent = window.BitterBrainsBanner.style),
+ document.head.append(n));
+ },
+ close() {
+ document.querySelector("#bb-banner").remove();
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.remove("has-bb-banner"),
+ n.classList.remove("bb-banner-container"),
+ clearInterval(this.countdownInterval),
+ (this.isOpen = !1));
+ },
+ bindCloseButton() {
+ document.querySelector("#bb-close").addEventListener("click", (n) => {
+ (n.preventDefault(),
+ n.stopPropagation(),
+ this.close(),
+ localStorage.setItem(this.activePhase.closedKey, 1),
+ localStorage.setItem(`${this.activePhase.id}_BANNER`, 1));
+ });
+ },
+ startCountdown() {
+ this.activePhase.countdownRemaining < 0 ||
+ (this.updateCountdown(),
+ this.activePhase.isCountdownStatic ||
+ (this.countdownInterval = setInterval(() => {
+ ((this.activePhase.countdownRemaining -= 1e3),
+ this.activePhase.countdownRemaining > 0
+ ? this.updateCountdown()
+ : this.close());
+ }, 1e3)));
+ },
+ updateCountdown() {
+ if (!this.isOpen) return;
+ const { countdownRemaining: n, showDaysAsHours: e } = this.activePhase,
+ t = {
+ days: Math.floor(n / 864e5),
+ hours: Math.floor((n % 864e5) / 36e5),
+ minutes: Math.floor((n % 36e5) / 6e4),
+ seconds: Math.floor((n % 6e4) / 1e3),
+ };
+ e && ((t.hours = t.hours + 24 * t.days), delete t.days);
+ for (const [n, e] of Object.entries(t)) {
+ const t = document.querySelector(`[data-countdown=${n}]`);
+ t && (t.textContent = e.toString().padStart(2, "0"));
+ }
+ },
+ init() {
+ if (
+ !this.activePhase ||
+ localStorage.getItem(this.activePhase.closedKey) ||
+ this.isInitiated
+ )
+ return console.info("Not initiating");
+ ((this.isOpen = !0), (this.isInitiated = !0));
+ const n = setInterval(() => {
+ "complete" === document.readyState &&
+ (clearInterval(n),
+ (this.type = this.getType()),
+ this.insertStyleTag(),
+ this.render(),
+ this.bindCloseButton(),
+ this.activePhase.showCountdown && this.startCountdown());
+ }, 100);
+ },
+}),
+ (window.BitterBrainsBanner.style =
+ "\n :root { --bb-logo-desktop: url(/banners/logo.svg);--bb-logo-tablet: url(/banners/mark.svg);--bb-logo-mobile: url(/banners/mark.svg); }#bb-banner {\n height: 72px;\n text-decoration: none;\n color: white;\n padding-right: 30px;\n display: flex;\n justify-content: center;\n align-items: center;\n background: #000;\n overflow: hidden;\n line-height: 1.25;\n background: var(--bb-gradient);\n direction: ltr !important;\n}\n\n#bb-banner .bb-logo {\n width: 30px;\n height: 19px;\n left: 16px;\n position: absolute;\n background-size: contain;\n background-repeat: no-repeat;\n background-image: var(--bb-logo-mobile);\n}\n\n#bb-banner .bb-close {\n font-family: sans-serif;\n color: white;\n position: absolute;\n top: 29px;\n right: 8px;\n line-height: 0;\n font-size: 24px;\n height: 14px;\n width: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n#bb-banner .bb-background {\n background-image: var(--bb-bg-mobile);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n position: absolute;\n z-index: 0;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n}\n\n#bb-banner .bb-core {\n display: flex;\n align-items: center;\n text-align: center;\n position: relative;\n z-index: 10;\n flex-direction: column;\n}\n\n#bb-banner .bb-core .bb-slogan {\n display: flex;\n flex-direction: row;\n align-items: center;\n order: 1;\n}\n\n#bb-banner .bb-core .bb-title-container {\n display: flex;\n align-items: center;\n flex-direction: column;\n gap: 2px;\n}\n\n#bb-banner .bb-core .bb-extended {\n display: none;\n}\n\n#bb-banner .bb-core .bb-title {\n font-size: 14px;\n font-weight: bold;\n color: #00dc82;\n}\n\n#bb-banner .bb-core .bb-subtitle {\n color: #FFF;\n font-size: 12px;\n}\n\n#bb-banner .bb-core .bb-button-wrapper {\n order: 2;\n}\n\n#bb-banner .bb-core .bb-button-wrapper .bb-button {\n border-radius: 6px;\n font-weight: bold;\n color: #000;\n background: white;\n font-size: 14px;\n padding: 14px 8px;\n white-space: nowrap;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper {\n display: flex;\n align-items: center;\n flex-direction: row-reverse;\n gap: 6px;\n order: 3;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper .bb-countdown-footer {\n order: 1;\n}\n\n#bb-banner .bb-core .bb-countdown {\n align-items: center;\n line-height: 1;\n display: flex;\n font-weight: bold;\n font-size: 12px;\n color: #00dc82;\n order: 2;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item {\n display: flex;\n align-items: center;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part > div {\n display: inline;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item:last-child .bb-countdown-colon {\n display: none;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part .bb-countdown-text .bb-countdown-text-initial~span {\n display: none;\n}\n\n\n@media screen and (max-width: 768px) {\n #bb-banner {\n padding-right: 0px !important;\n }\n}\n\n@media (min-width: 768px) {\n\n #bb-banner .bb-close {\n right: 12px;\n }\n\n #bb-banner .bb-core {\n gap: 12px;\n flex-direction: row;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-tablet);\n }\n\n #bb-banner .bb-logo {\n background-image: var(--bb-logo-tablet);\n }\n\n #bb-banner .bb-core .bb-title {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-subtitle {\n font-size: 16px;\n }\n\n #bb-banner .bb-core .bb-button-wrapper {\n display: block;\n }\n\n #bb-banner .bb-core .bb-button-wrapper .bb-button {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\n@media (min-width: 1280px) {\n #bb-banner .bb-core .bb-title-container {\n gap: 4px;\n }\n \n #bb-banner .bb-logo {\n width: 152px;\n left: 24px;\n background-image: var(--bb-logo-desktop);\n }\n\n #bb-banner .bb-core .bb-slogan {\n gap: 4px;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-desktop);\n }\n\n #bb-banner .bb-core {\n gap: 32px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\nhtml.has-bb-banner.bb-banner-hidden #bb-banner {\n display: none;\n}\n\n/*\nBANNER TYPES\n*********************************************************/\n\n/* Banner rendered in container element */\n\nhtml.has-bb-banner #bb-banner-container #bb-banner, html.has-bb-banner #bb-banner-video #bb-banner {\n position: relative;\n}\n\nhtml.has-bb-banner #bb-banner-container #bb-banner .bb-close {\n/* display: none;*/\n}\n\n/* Banner Fixed Top */\n\nhtml.has-bb-banner.bb-type-top {\n margin-top: 72px;\n}\n\nhtml.has-bb-banner.bb-type-top.bb-banner-hidden {\n margin-top: 0;\n}\n\nhtml.has-bb-banner.bb-type-top #bb-banner {\n position: fixed;\n top: 0;\n z-index: 60;\n left: 0;\n right: 0;\n}\n\n/* Banner Fixed Bottom */\n\nhtml.has-bb-banner.bb-type-bottom #bb-banner {\n position: fixed;\n bottom: 10px;\n z-index: 60;\n left: 10px;\n right: 10px;\n}\n/* Vue School */\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent {\n background: #100D1C;\n font-family: 'Inter', sans-serif;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent:hover .bb-core .bb-button-wrapper .bb-button {\n background: #0EFAA5;\n color: #100D1C;\n transition: background-color 0.5s, color 0.5s; \n }\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 122px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/logo.svg') !important;\n background-repeat: no-repeat;\n}\n\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 60px;\n transform: translateX(80px);\n\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\ncolor: #FFF;\nfont-size: 24px;\nfont-style: normal;\nfont-weight: 900;\nline-height: normal; \nwidth: auto;\n \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude b {\n background: var(--Gradient, linear-gradient(90deg, #0EFAA5 1.04%, #FAFF00 100%));\n background-clip: text;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent; \n font-style: normal;\n font-weight: 00;\n line-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 60px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title {\n color: white;\ntext-align: center;\nfont-size: 16px;\nfont-style: normal;\nfont-weight: 400;\nline-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title b {\n color: #0EFAA5;\n font-weight: 700;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-subtitle {\n font-weight: 700;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-addendum {color: var(--White, #FFF);\n text-align: center;\n font-size: 10px;\n font-style: normal;\n font-weight: 700;\n line-height: normal;\n letter-spacing: 1px;\n text-transform: uppercase; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n order: 3;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\npadding: 6px 14px;\nborder-radius: 4px;\nbackground: rgba(43, 246, 163, 0.20);\ncolor: white;\ntext-align: center;\nfont-size: 18px;\nfont-style: normal;\nfont-weight: 800;\nline-height: normal;\nletter-spacing: 1.4px; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper .bb-button {\n display: flex;\n color: white;\n align-items: center;\n justify-items: center;\n height: 44px;\n padding: 0px 24px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 50px;\nborder: 2px solid #0EFAA5;\nbackground: rgba(14, 250, 165, 0.40);\nfont-size: 14px;\nfont-style: normal;\nfont-weight: 700;\nline-height: normal; \n}\n\n\n\n@media screen and (max-width: 1440px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n gap: 30px;\n transform: translateX(0px);\n\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 50px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/mark.svg') !important;\n background-repeat: no-repeat !important;\n margin-right: 40px;\n }\n}\n@media screen and (max-width: 1280px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n }\n}\n\n@media screen and (max-width: 1234px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n margin-right: 40px;\n }\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: row;\n justify-items: center;\n position: absolute;\n bottom: 0%;\n left: 50%;\n transform: translate(-35%, 46%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\n color: #0BD88F;\n padding: 0px;\n background: transparent;\ntext-align: center;\nfont-size: 12px;\nfont-style: normal;\nfont-weight: 700;\nline-height: 16px; /* 177.778% */\nletter-spacing: 0.9px; \nmargin-left: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: -10px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 35px;\n height: 40px;\n position: relative;\n display: block;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n width: 160px;\n text-align: center;\n font-size: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 14px !important;\n }\n}\n\n@media screen and (max-width: 768px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n position: relative;\n bottom: 0%;\n left: 0%;\n transform: translate(0%, 0%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n margin-top: 0px;\n gap: 0px;\n flex-direction: column;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: 0px;\n gap: 0px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 0px;\n flex-direction: column;\n \n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 11px !important;\n }\n \n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper {\n display: none;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n font-size: 13px; \n width: auto;\n }\n}\n/* Vue Router and Pinia */\n\nhtml.has-bb-banner .theme,\nhtml.has-bb-banner .theme .nav-bar,\nhtml.has-bb-banner .theme .sidebar,\nhtml.has-bb-banner .theme .page {\n margin-top: 72px;\n}\n\n/* Vite Press */\n\nhtml.has-bb-banner {\n scroll-padding-top: 134px;\n overflow: auto;\n}\n\nhtml.has-bb-banner .VPSidebar,\nhtml.has-bb-banner .VPDoc.has-sidebar.has-aside .aside-container {\n top: 72px;\n}\n\n@media (min-width: 960px) {\n html.has-bb-banner .VPNav {\n top: 72px;\n }\n}\n\n "));
+try {
+ window.BitterBrainsBanner.init();
+} catch {
+ console.warn("Error when trying to render top banner");
+}
diff --git a/EasyGoAdmin_GoFrame_EleVue/.vitepress/config/shared.ts b/EasyGoAdmin_GoFrame_EleVue/.vitepress/config/shared.ts
index c1e5949..22674f2 100644
--- a/EasyGoAdmin_GoFrame_EleVue/.vitepress/config/shared.ts
+++ b/EasyGoAdmin_GoFrame_EleVue/.vitepress/config/shared.ts
@@ -70,6 +70,16 @@ export const sharedConfig = defineConfig({
},
],
+ // Top banner
+ [
+ "script",
+ {
+ src: "/banner.js",
+ // @ts-expect-error: vitepress bug
+ async: true,
+ type: "text/javascript",
+ },
+ ],
],
themeConfig: {
diff --git a/EasyGoAdmin_GoFrame_EleVue/public/banner.js b/EasyGoAdmin_GoFrame_EleVue/public/banner.js
new file mode 100644
index 0000000..85c12c8
--- /dev/null
+++ b/EasyGoAdmin_GoFrame_EleVue/public/banner.js
@@ -0,0 +1,257 @@
+((window.BitterBrainsBanner = window.BitterBrainsBanner || {
+ host: "",
+ affiliateKey: "vuerouter",
+ emulateDate: void 0,
+ emulateHour: void 0,
+ date: "03/09/2025, 01:30 PM Central European Standard Time",
+ activePhase: {
+ id: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8",
+ countdownEnds: "2025-03-09T22:59:59.000000Z",
+ cta: "获取官方授权",
+ ends: "2025-03-09T22:59:59.000000Z",
+ title: "新品首发,限时特惠,即刻抢购! ",
+ link: "https://www.easygoadmin.vip",
+ assets: null,
+ static: null,
+ classes: ["bb-campaign-vs-affiliates-freeweekend-urgent"],
+ prelude: "EasyGoAdmin · 企业级旗舰产品",
+ // addendum: "Extended for",
+ subtitle: "赋能开发者,驱动企业增长,提供一站式数据中台解决方案。",
+ couponCode: null,
+ isExtended: !1,
+ hideBanners: !1,
+ assetsFormat: "png",
+ showCountdown: 0,
+ countdownFooter: null,
+ showDaysAsHours: !0,
+ affiliates: [
+ "vuerouter",
+ "pinia",
+ "vueuse",
+ "vuejsforge",
+ "nuxtnation",
+ "vuejsdevelopers",
+ "VUEDEMY",
+ "vs_masteringpinia",
+ "vuejsnation",
+ "SENODEV",
+ "NSCRIPT",
+ ],
+ interlude: null,
+ starts: "2025-03-08T22:59:59.000Z",
+ isRunning: !0,
+ remaining: 37757670,
+ closedKey: "VS-FREEWEEKEND25_AFFILIATE_SUPERSALE_EXT8_CLOSED",
+ countdownRemaining: 37757670,
+ overrides: { vuejs: { hideSeconds: !0 } },
+ logoMobile: "/banners/mark.svg",
+ logoTablet: "/banners/mark.svg",
+ logoDesktop: "/banners/logo.svg",
+ },
+ fromCache: !1,
+ cacheTtl: 0,
+ type: "top",
+ closeable: void 0,
+ isOpen: !1,
+ countdownInterval: null,
+ isInitiated: !1,
+ style: null,
+ allowList: {
+ masteringnuxt: (n) => "/" === n || "/nuxt2" === n || "/nuxt3" === n,
+ vueschool: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons") ||
+ n.startsWith("/vueschool-demo"),
+ vueschool_free: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vueschool_premium: (n) =>
+ "/" === n ||
+ n.startsWith("/courses") ||
+ n.startsWith("/articles") ||
+ n.startsWith("/lessons"),
+ vs_frontendnation: (n) => "/" === n,
+ },
+ render() {
+ const {
+ countdownFooter: n,
+ prelude: e,
+ interlude: t,
+ addendum: b,
+ title: a,
+ subtitle: i,
+ cta: o,
+ link: r,
+ classes: s,
+ showCountdown: l,
+ } = this.activePhase,
+ d = n ? `` : "",
+ c = `\n \n \n \n \n
\n ${
+ e ? `
${e}
` : ""
+ }\n
\n
${a}
\n ${
+ i ? `
${i}
` : ""
+ }\n
\n ${
+ t ? `
${t}
` : ""
+ }\n
\n ${
+ o
+ ? `
`
+ : ""
+ }\n
\n ${
+ b ? `
${b}
` : ""
+ }\n ${
+ l
+ ? `
${this.renderCountdown()}
${d}
`
+ : ""
+ }\n
\n
\n \n `,
+ p = document.createElement("a");
+ p.setAttribute("id", "bb-banner");
+ const u = {
+ utm_source: this.affiliateKey,
+ utm_medium: "website",
+ utm_campaign: "affiliate",
+ utm_content: `${this.type}_banner`,
+ banner_type: this.type,
+ },
+ g = "internal" in this.activePhase && !!this.activePhase?.internal;
+ (!g && p.setAttribute("target", "_blank"),
+ "vueschool" !== this.affiliateKey &&
+ (u.friend = this.affiliateKey?.toUpperCase()));
+ const h = new URLSearchParams(g ? {} : u).toString();
+ (p.setAttribute("href", `${r}${g ? "" : "?" + h}`),
+ p.classList.add(...s),
+ (p.innerHTML = c),
+ this.addBodyClasses(),
+ (function () {
+ let n = document.querySelector("#bb-banner-video");
+ (n ||
+ (n = document.querySelector("#bb-banner-container") || document.body),
+ n.append(p));
+ })(),
+ addEventListener("popstate", (n) => {
+ this.handleNavigation();
+ }),
+ (window.history.pushState = new Proxy(window.history.pushState, {
+ apply: (n, e, t) => {
+ const b = n.apply(e, t);
+ return (this.handleNavigation(), b);
+ },
+ })));
+ },
+ handleNavigation() {
+ this.addBodyClasses();
+ },
+ addBodyClasses() {
+ if (!this.isOpen) return;
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.add("has-bb-banner"),
+ n.classList.add(`bb-type-${this.type}`),
+ this.affiliateKey && n.classList.add(`bb-${this.affiliateKey}`));
+ const e = window.location.pathname || "/",
+ t = !!window.BitterBrainsBanner.emulateDate;
+ !this.allowList[this.affiliateKey] ||
+ this.allowList[this.affiliateKey](e) ||
+ t
+ ? n.classList.remove("bb-banner-hidden")
+ : n.classList.add("bb-banner-hidden");
+ },
+ renderCountdown() {
+ const { showDaysAsHours: n, hideSeconds: e } = this.activePhase,
+ t = ["hours", "minutes"];
+ return (
+ n || t.unshift("days"),
+ e || t.push("seconds"),
+ t.map((n) => this.renderCountdownPart(n)).join("")
+ );
+ },
+ renderCountdownPart: (n) =>
+ `\n \n
\n
${
+ n[0]
+ }${n.slice(
+ 1,
+ )}
\n
\n
:
\n
\n `,
+ getType() {
+ if (document.querySelector("#bb-banner-video")) return "video";
+ return document.querySelector("#bb-banner-container")
+ ? "inline"
+ : this.type && ["top", "bottom"].includes(this.type)
+ ? this.type
+ : "top";
+ },
+ insertStyleTag() {
+ const n = document.createElement("style");
+ ((n.textContent = window.BitterBrainsBanner.style),
+ document.head.append(n));
+ },
+ close() {
+ document.querySelector("#bb-banner").remove();
+ const n = document.querySelectorAll("html")[0];
+ (n.classList.remove("has-bb-banner"),
+ n.classList.remove("bb-banner-container"),
+ clearInterval(this.countdownInterval),
+ (this.isOpen = !1));
+ },
+ bindCloseButton() {
+ document.querySelector("#bb-close").addEventListener("click", (n) => {
+ (n.preventDefault(),
+ n.stopPropagation(),
+ this.close(),
+ localStorage.setItem(this.activePhase.closedKey, 1),
+ localStorage.setItem(`${this.activePhase.id}_BANNER`, 1));
+ });
+ },
+ startCountdown() {
+ this.activePhase.countdownRemaining < 0 ||
+ (this.updateCountdown(),
+ this.activePhase.isCountdownStatic ||
+ (this.countdownInterval = setInterval(() => {
+ ((this.activePhase.countdownRemaining -= 1e3),
+ this.activePhase.countdownRemaining > 0
+ ? this.updateCountdown()
+ : this.close());
+ }, 1e3)));
+ },
+ updateCountdown() {
+ if (!this.isOpen) return;
+ const { countdownRemaining: n, showDaysAsHours: e } = this.activePhase,
+ t = {
+ days: Math.floor(n / 864e5),
+ hours: Math.floor((n % 864e5) / 36e5),
+ minutes: Math.floor((n % 36e5) / 6e4),
+ seconds: Math.floor((n % 6e4) / 1e3),
+ };
+ e && ((t.hours = t.hours + 24 * t.days), delete t.days);
+ for (const [n, e] of Object.entries(t)) {
+ const t = document.querySelector(`[data-countdown=${n}]`);
+ t && (t.textContent = e.toString().padStart(2, "0"));
+ }
+ },
+ init() {
+ if (
+ !this.activePhase ||
+ localStorage.getItem(this.activePhase.closedKey) ||
+ this.isInitiated
+ )
+ return console.info("Not initiating");
+ ((this.isOpen = !0), (this.isInitiated = !0));
+ const n = setInterval(() => {
+ "complete" === document.readyState &&
+ (clearInterval(n),
+ (this.type = this.getType()),
+ this.insertStyleTag(),
+ this.render(),
+ this.bindCloseButton(),
+ this.activePhase.showCountdown && this.startCountdown());
+ }, 100);
+ },
+}),
+ (window.BitterBrainsBanner.style =
+ "\n :root { --bb-logo-desktop: url(/banners/logo.svg);--bb-logo-tablet: url(/banners/mark.svg);--bb-logo-mobile: url(/banners/mark.svg); }#bb-banner {\n height: 72px;\n text-decoration: none;\n color: white;\n padding-right: 30px;\n display: flex;\n justify-content: center;\n align-items: center;\n background: #000;\n overflow: hidden;\n line-height: 1.25;\n background: var(--bb-gradient);\n direction: ltr !important;\n}\n\n#bb-banner .bb-logo {\n width: 30px;\n height: 19px;\n left: 16px;\n position: absolute;\n background-size: contain;\n background-repeat: no-repeat;\n background-image: var(--bb-logo-mobile);\n}\n\n#bb-banner .bb-close {\n font-family: sans-serif;\n color: white;\n position: absolute;\n top: 29px;\n right: 8px;\n line-height: 0;\n font-size: 24px;\n height: 14px;\n width: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n#bb-banner .bb-background {\n background-image: var(--bb-bg-mobile);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: center;\n position: absolute;\n z-index: 0;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n}\n\n#bb-banner .bb-core {\n display: flex;\n align-items: center;\n text-align: center;\n position: relative;\n z-index: 10;\n flex-direction: column;\n}\n\n#bb-banner .bb-core .bb-slogan {\n display: flex;\n flex-direction: row;\n align-items: center;\n order: 1;\n}\n\n#bb-banner .bb-core .bb-title-container {\n display: flex;\n align-items: center;\n flex-direction: column;\n gap: 2px;\n}\n\n#bb-banner .bb-core .bb-extended {\n display: none;\n}\n\n#bb-banner .bb-core .bb-title {\n font-size: 14px;\n font-weight: bold;\n color: #00dc82;\n}\n\n#bb-banner .bb-core .bb-subtitle {\n color: #FFF;\n font-size: 12px;\n}\n\n#bb-banner .bb-core .bb-button-wrapper {\n order: 2;\n}\n\n#bb-banner .bb-core .bb-button-wrapper .bb-button {\n border-radius: 6px;\n font-weight: bold;\n color: #000;\n background: white;\n font-size: 14px;\n padding: 14px 8px;\n white-space: nowrap;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper {\n display: flex;\n align-items: center;\n flex-direction: row-reverse;\n gap: 6px;\n order: 3;\n}\n\n#bb-banner .bb-core .bb-countdown-wrapper .bb-countdown-footer {\n order: 1;\n}\n\n#bb-banner .bb-core .bb-countdown {\n align-items: center;\n line-height: 1;\n display: flex;\n font-weight: bold;\n font-size: 12px;\n color: #00dc82;\n order: 2;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item {\n display: flex;\n align-items: center;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part > div {\n display: inline;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item:last-child .bb-countdown-colon {\n display: none;\n}\n\n#bb-banner .bb-core .bb-countdown .bb-countdown-item .bb-countdown-part .bb-countdown-text .bb-countdown-text-initial~span {\n display: none;\n}\n\n\n@media screen and (max-width: 768px) {\n #bb-banner {\n padding-right: 0px !important;\n }\n}\n\n@media (min-width: 768px) {\n\n #bb-banner .bb-close {\n right: 12px;\n }\n\n #bb-banner .bb-core {\n gap: 12px;\n flex-direction: row;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-tablet);\n }\n\n #bb-banner .bb-logo {\n background-image: var(--bb-logo-tablet);\n }\n\n #bb-banner .bb-core .bb-title {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-subtitle {\n font-size: 16px;\n }\n\n #bb-banner .bb-core .bb-button-wrapper {\n display: block;\n }\n\n #bb-banner .bb-core .bb-button-wrapper .bb-button {\n font-size: 18px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\n@media (min-width: 1280px) {\n #bb-banner .bb-core .bb-title-container {\n gap: 4px;\n }\n \n #bb-banner .bb-logo {\n width: 152px;\n left: 24px;\n background-image: var(--bb-logo-desktop);\n }\n\n #bb-banner .bb-core .bb-slogan {\n gap: 4px;\n }\n\n #bb-banner .bb-background {\n background-image: var(--bb-bg-desktop);\n }\n\n #bb-banner .bb-core {\n gap: 32px;\n }\n\n #bb-banner .bb-core .bb-countdown-wrapper {\n flex-direction: column-reverse;\n }\n}\n\nhtml.has-bb-banner.bb-banner-hidden #bb-banner {\n display: none;\n}\n\n/*\nBANNER TYPES\n*********************************************************/\n\n/* Banner rendered in container element */\n\nhtml.has-bb-banner #bb-banner-container #bb-banner, html.has-bb-banner #bb-banner-video #bb-banner {\n position: relative;\n}\n\nhtml.has-bb-banner #bb-banner-container #bb-banner .bb-close {\n/* display: none;*/\n}\n\n/* Banner Fixed Top */\n\nhtml.has-bb-banner.bb-type-top {\n margin-top: 72px;\n}\n\nhtml.has-bb-banner.bb-type-top.bb-banner-hidden {\n margin-top: 0;\n}\n\nhtml.has-bb-banner.bb-type-top #bb-banner {\n position: fixed;\n top: 0;\n z-index: 60;\n left: 0;\n right: 0;\n}\n\n/* Banner Fixed Bottom */\n\nhtml.has-bb-banner.bb-type-bottom #bb-banner {\n position: fixed;\n bottom: 10px;\n z-index: 60;\n left: 10px;\n right: 10px;\n}\n/* Vue School */\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent {\n background: #100D1C;\n font-family: 'Inter', sans-serif;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent:hover .bb-core .bb-button-wrapper .bb-button {\n background: #0EFAA5;\n color: #100D1C;\n transition: background-color 0.5s, color 0.5s; \n }\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 122px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/logo.svg') !important;\n background-repeat: no-repeat;\n}\n\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 60px;\n transform: translateX(80px);\n\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\ncolor: #FFF;\nfont-size: 24px;\nfont-style: normal;\nfont-weight: 900;\nline-height: normal; \nwidth: auto;\n \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude b {\n background: var(--Gradient, linear-gradient(90deg, #0EFAA5 1.04%, #FAFF00 100%));\n background-clip: text;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent; \n font-style: normal;\n font-weight: 00;\n line-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 60px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title {\n color: white;\ntext-align: center;\nfont-size: 16px;\nfont-style: normal;\nfont-weight: 400;\nline-height: normal; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-title b {\n color: #0EFAA5;\n font-weight: 700;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-subtitle {\n font-weight: 700;\n}\n\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-addendum {color: var(--White, #FFF);\n text-align: center;\n font-size: 10px;\n font-style: normal;\n font-weight: 700;\n line-height: normal;\n letter-spacing: 1px;\n text-transform: uppercase; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n order: 3;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\npadding: 6px 14px;\nborder-radius: 4px;\nbackground: rgba(43, 246, 163, 0.20);\ncolor: white;\ntext-align: center;\nfont-size: 18px;\nfont-style: normal;\nfont-weight: 800;\nline-height: normal;\nletter-spacing: 1.4px; \n}\n\n#bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper .bb-button {\n display: flex;\n color: white;\n align-items: center;\n justify-items: center;\n height: 44px;\n padding: 0px 24px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 50px;\nborder: 2px solid #0EFAA5;\nbackground: rgba(14, 250, 165, 0.40);\nfont-size: 14px;\nfont-style: normal;\nfont-weight: 700;\nline-height: normal; \n}\n\n\n\n@media screen and (max-width: 1440px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n gap: 30px;\n transform: translateX(0px);\n\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 50px;\n height: 40px;\n position: relative;\n display: block;\n background: url('/banners/mark.svg') !important;\n background-repeat: no-repeat !important;\n margin-right: 40px;\n }\n}\n@media screen and (max-width: 1280px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n display: flex;\n flex-direction: column;\n }\n}\n\n@media screen and (max-width: 1234px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core{\n margin-right: 40px;\n }\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n display: flex;\n flex-direction: row;\n justify-items: center;\n position: absolute;\n bottom: 0%;\n left: 50%;\n transform: translate(-35%, 46%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container .bb-countdown {\n color: #0BD88F;\n padding: 0px;\n background: transparent;\ntext-align: center;\nfont-size: 12px;\nfont-style: normal;\nfont-weight: 700;\nline-height: 16px; /* 177.778% */\nletter-spacing: 0.9px; \nmargin-left: 2px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: -10px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-logo {\n width: 35px;\n height: 40px;\n position: relative;\n display: block;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n gap: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n width: 160px;\n text-align: center;\n font-size: 20px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 14px !important;\n }\n}\n\n@media screen and (max-width: 768px) {\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-countdown-container {\n position: relative;\n bottom: 0%;\n left: 0%;\n transform: translate(0%, 0%);\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan {\n margin-top: 0px;\n gap: 0px;\n flex-direction: column;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container {\n margin-top: 0px;\n gap: 0px;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core {\n gap: 0px;\n flex-direction: column;\n \n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-title, #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-title-container .bb-subtitle {\n font-size: 11px !important;\n }\n \n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-button-wrapper {\n display: none;\n }\n\n #bb-banner.bb-campaign-vs-affiliates-freeweekend-urgent .bb-core .bb-slogan .bb-prelude {\n font-size: 13px; \n width: auto;\n }\n}\n/* Vue Router and Pinia */\n\nhtml.has-bb-banner .theme,\nhtml.has-bb-banner .theme .nav-bar,\nhtml.has-bb-banner .theme .sidebar,\nhtml.has-bb-banner .theme .page {\n margin-top: 72px;\n}\n\n/* Vite Press */\n\nhtml.has-bb-banner {\n scroll-padding-top: 134px;\n overflow: auto;\n}\n\nhtml.has-bb-banner .VPSidebar,\nhtml.has-bb-banner .VPDoc.has-sidebar.has-aside .aside-container {\n top: 72px;\n}\n\n@media (min-width: 960px) {\n html.has-bb-banner .VPNav {\n top: 72px;\n }\n}\n\n "));
+try {
+ window.BitterBrainsBanner.init();
+} catch {
+ console.warn("Error when trying to render top banner");
+}
diff --git a/EasyGoAdmin_GoFrame_EleVue/搜索引擎优化 - 副本.txt b/EasyGoAdmin_GoFrame_EleVue/搜索引擎优化 - 副本.txt
deleted file mode 100644
index 7648c93..0000000
--- a/EasyGoAdmin_GoFrame_EleVue/搜索引擎优化 - 副本.txt
+++ /dev/null
@@ -1,543 +0,0 @@
-================================================================================
-EasyGoAdmin 文档站 SEO / GEO 更新标准
-================================================================================
-版本:1.0(2026-10-25)
-适用:EasyGoAdmin_{Beego,Gin,GoFrame,Iris}_{AntdVue,EleVue,Layui,NaiveVue}
- 等 16 个 VitePress 文档子项目(结构高度同构,可直接照搬)
-基线项目:EasyGoAdmin_GoFrame_EleVue
-本文档定位:后续为其他项目做相同更新时的执行标准(审计清单 → 实施步骤 →
- 验收标准 → 移植注意事项)。
-
---------------------------------------------------------------------------------
-第一部分:概念与目标
---------------------------------------------------------------------------------
-
-1.1 SEO(Search Engine Optimization,搜索引擎优化)
- 面向传统搜索引擎(百度、Google、Bing)的爬虫与排序:
- - 让爬虫能发现、抓取、理解每一个页面(技术 SEO)
- - 让每个页面有独立、准确的标题与摘要(页面元数据)
- - 让页面之间有清晰的权重传递结构(内链、面包屑、sitemap)
- - 用结构化数据告诉搜索引擎"这是什么类型的内容"(JSON-LD)
-
-1.2 GEO(Generative Engine Optimization,生成式引擎优化)
- 面向 AI 引擎(ChatGPT、Claude、Perplexity、豆包、Kimi、AI Overviews)
- 的爬虫与答案引用:
- - 让 AI 爬虫明确被允许抓取(robots.txt 显式放行)
- - 提供"给模型看的站点摘要"(llms.txt,llmstxt.org 标准)
- - 页面内容"答案前置",首段即可被直接引用(:::tip 概述 首句)
- - 结构化 Q/A(FAQPage JSON-LD)便于引擎抽取
- - 时效信号(dateModified / sitemap lastmod)影响引用优先级
-
-1.3 本方案的设计原则
- a) 单一真相源:URL、站点描述、提取逻辑只写一处,多处复用。
- b) 零运行时开销:全部在构建期(build)完成,不进客户端 bundle。
- c) 零新依赖:只用 Node 内置模块 + VitePress 内置能力,
- 不新增 npm 包(llms.txt 脚本在 node_modules 缺失时也能跑)。
- d) 内容不改版:不改动 143 篇正文的结构,SEO 增强全部在配置层注入。
-
---------------------------------------------------------------------------------
-第二部分:审计清单(先查后改,逐项打勾)
---------------------------------------------------------------------------------
-
-对目标项目执行以下检查(grep/ls 即可,全部只读):
-
-[ ] 1. public/robots.txt 是否存在?
-[ ] 2. public/sitemap.xml 或 vitepress sitemap 配置是否存在?
- (查 .vitepress/config 中 grep "sitemap")
-[ ] 3. public/llms.txt 是否存在?
-[ ] 4. canonical 标签是否存在?(grep 'rel="canonical"' .vitepress/)
-[ ] 5. og:image / og:title / JSON-LD 是否存在?
- (grep 'og:image\|application/ld+json' .vitepress/)
-[ ] 6. 页面级 frontmatter description 覆盖率?
- (grep -rl "^description:" zh --include="*.md" | wc -l)
- —— 若为 0,说明所有页面共用同一条 meta description(重复内容信号)
-[ ] 7. 是否带章节编号前缀?(看任意页 H1 形如 "5.10 字典模块")
-[ ] 8. og:url 是否全站固定为首页地址?
-[ ] 9. 是否有 404 页面?(ls 404.md)
-[ ] 10. HTTPS 是否可用?
- curl -sI https://<域名>/ -o /dev/null -w "%{http_code}"
- curl 输出 000 或证书告警 → 说明证书不匹配,暂用 http
-[ ] 11. 是否有站长平台验证 meta?(baidu-site-verification / google-site-verification)
-[ ] 12. :::tip 概述 首句是否适合作为摘要?(抽查 10 页,看是否"答案前置")
-[ ] 13. FAQ 类页面的 Q/A 格式是什么?(决定 extractFaqs 是否要加格式分支)
-[ ] 14. 静态 twitter:image / og:image 指向的文件是否真实存在?
- (Gin 项目 META_IMAGE="/social.png" 但 public/ 下无此文件 →
- twitter:image 404,改为实际存在的 /logo.png)
-[ ] 15. zh.ts 的 META_TITLE / META_DESCRIPTION 是否与项目技术栈一致?
- (Gin 项目误写 "GoFrame v2",属元数据事实错误,一并修正)
-
-审计结论示例(GoFrame_EleVue 基线,2026-10-25 实测):
- robots/sitemap/llms/canonical/og:image/JSON-LD 全部缺失;
- 0/143 页有 description;143 页 title 全带编号;无 404;
- HTTPS 证书为 CDN 默认证书(*.cdn.myqcloud.com)校验失败;
- 内容面强:141/143 页有 :::tip 概述、118 页有表格、139 页有代码块。
-
---------------------------------------------------------------------------------
-第三部分:实施步骤(按序执行)
---------------------------------------------------------------------------------
-
-步骤总览(7 步):
- ① 抽取共享层 extract.mjs → ② SEO 钩子 seo.ts → ③ 挂载到 config/index.ts
- → ④ titleTemplate → ⑤ robots.txt + 404.md → ⑥ llms.txt 脚本
- → ⑦ package.json 接线 + 生成
-
-================================================================================
-步骤 ①:共享提取层 .vitepress/config/extract.mjs(+ extract.d.mts)
-================================================================================
-
-新建纯 ESM、零依赖文件,被两类调用方复用:
- - VitePress 配置(.ts,esbuild 打包时内联它)
- - scripts/generate-llms-txt.mjs(Node 直接运行)
-
-必须导出的五个符号:
-
-1) SITE_URL —— 全站唯一 URL 真相源
- canonical、og:url、twitter:url、JSON-LD、sitemap hostname、
- llms.txt 链接、robots.txt 的 Sitemap 行全部由它派生。
- 格式:不带尾斜杠,如 "http://docs.goframe.elevue.easygoadmin.vip"
- ⚠ 若项目 HTTPS 可用必须用 https;证书有问题才暂用 http,
- 并在注释里写明"证书修复后只改这一处"。
-
-2) SITE_DESCRIPTION —— 站点级描述(llms.txt 头部引用)
- 与 zh.ts 的 META_DESCRIPTION 保持一致(或让 zh.ts 直接 import 它)。
-
-3) stripNumberPrefix(title)
- 剥离 中的章节编号前缀,关键词前置:
- "5.10 字典模块" → "字典模块"
- "K. 贡献指南" → "贡献指南"
- 正则(两分支缺一不可):
- /^(?:\d+(?:\.\d+)+\s+|\d+\.\s+|[A-Z]\.\s+)/
- 分支说明:
- - \d+(?:\.\d+)+\s+ 匹配 "5.10 " "1.6 "(数字.数字 + 空格)
- - \d+\.\s+ 匹配 "1. "(单层数字 + 点 + 空格)
- - [A-Z]\.\s+ 匹配 "A. " "K. "(附录字母编号)
- ⚠ 注意顺序与边界:不能误伤 "404 页面" 这类无编号标题
- (要求编号后必须有点或已是 数字.数字 形态 + 空格)。
- ⚠ 只影响 和 JSON-LD headline 的 SEO 输出;
- 页内 H1 和侧边栏编号保持原样不动。
-
-4) extractDescription(md) —— meta description / llms.txt 摘要的唯一来源
- 提取顺序:
- a. 剥掉 frontmatter(/^---[\s\S]*?\n---\s*/)
- b. 首选 :::tip 容器内正文:/^:::\s*tip[^\n]*\n([\s\S]*?)\n:::/m
- (容器标签后缀不限定"概述",兼容 "说明"、"欢迎贡献" 等变体)
- c. 回退到 H1 后第一个普通段落(跳过标题/容器/代码块/表格行)
- d. 清洗:压缩空白、去行内 markdown(`code` **bold** [链接](url))
- e. 截断:按中文句末标点(。!?!?)取首句,上限 100 字符;
- 截断处避免切碎词(去掉末尾不完整词)
- 输出单行纯文本;失败返回 ""(调用方应回退到站点级描述)。
-
-5) extractFaqs(md) —— FAQPage JSON-LD 的 Q/A 提取
- 支持两种站内常见格式(按需增删分支):
- 格式 A(标题式):### Q:问题\n答案… (切块后答案截到下一个标题)
- 格式 B(加粗式):**Q:问题**\n答案… (答案截到下一个 Q 或标题)
- 共同点:
- - Q/A 文本统一走 cleanText:去代码围栏行、行内 code/bold/链接、压空白
- - 只在命中 ≥1 组时返回,调用方据此决定是否注入 FAQPage
- ⚠ 若目标项目的 FAQ 是表格形式(| 症状 | 原因 |),不产出 Q/A,
- 就不要强行转换——JSON-LD 必须与页面可见内容一致。
-
-同目录新建 extract.d.mts(TypeScript 类型声明,供编辑器识别):
- export declare const SITE_URL: string;
- export declare const SITE_DESCRIPTION: string;
- export declare function stripNumberPrefix(title: string): string;
- export declare function extractDescription(md: string): string;
- export declare function extractFaqs(md: string): { q: string; a: string }[];
-
-⚠ 关键坑(本项目实测踩过):
- 注释里不要出现序列 "*/"。形如 zh/**/*.md 的路径写在块注释中,
- 其中的 "**/*" 会提前终止注释导致 SyntaxError。
- 改写为 "zh 目录下全部 Markdown" 之类的描述。
-
-================================================================================
-步骤 ②:SEO 钩子 .vitepress/config/seo.ts
-================================================================================
-
-导出 createSeoHooks(sidebar),返回三个 VitePress 构建钩子。
-入参传 zhConfig.themeConfig?.sidebar —— 面包屑结构从侧边栏自动派生,
-侧边栏改了面包屑自动跟随,无需第二份清单。
-
---------------------------------------------------------------------------------
-2.1 transformPageData(pageData, ctx) —— 页面级 title / description
---------------------------------------------------------------------------------
-对每页返回增量对象(VitePress 自动 merge):
- - title: stripNumberPrefix(pageData.title)
- (pageData.title 已由 VitePress 从 H1/frontmatter 推导)
- - description: extractDescription(读取 pageData.relativePath 对应源文件)
- 仅当页面没有 frontmatter.description 且提取非空时设置;
- 提取失败保持 "" → 渲染时自动回退站点级描述。
-读文件:fs.readFileSync(path.resolve(ctx.siteConfig.srcDir, relativePath))
-
-效果:
- 5.10 字典模块 | EasyGoAdmin
- → 字典模块 | EasyGoAdmin 开发文档
- 全站统一文案
- → 每页独立(取该页 tip 首句)
-
---------------------------------------------------------------------------------
-2.2 transformHead(ctx) —— canonical / OG / twitter / JSON-LD
---------------------------------------------------------------------------------
-入参 ctx 含:pageData、title(已拼模板)、description、content(渲染后 HTML)、
-siteConfig(root/srcDir)。
-
-前置判断:relativePath 为空或 === "404.md" → 返回 [](404 不做 SEO)。
-
-注入的标签清单(13 项 + 1 个 JSON-LD script):
- ["link", { rel: "canonical", href: url }] ← 每页独立 canonical
- og:type (首页 website,内容页 article)
- og:site_name "EasyGoAdmin"(按项目改)
- og:locale "zh_CN"
- og:title (用去编号后的页标题)
- og:description (用步骤①提取的 description)
- og:url (= canonical)
- og:image (SITE_URL + /logo.png,必须绝对地址)
- twitter:title / twitter:description / twitter:url / twitter:image
- ["script", { type: "application/ld+json" }, json] ← 见 2.3
-
-URL 推导:
- index.md → SITE_URL + "/"
- 404.md → 跳过
- 其他 → SITE_URL + "/" + relativePath 换 .html 后缀
- (cleanUrls 关闭时正文页带 .html,与侧边栏链接一致)
-
-⚠ 与静态 head 的去重机制:
- VitePress mergeHead 按 meta 标签的"第一个属性值"判重
- (property:"og:url" 视为同一 key)。因此:
- - 静态 head 里的 og:url/og:description/twitter:* 会被本钩子的
- 同 key 输出覆盖(构建期),无需手动删除静态项;
- - 静态 twitter:card(本钩子不输出)会保留 —— 正确行为。
- ⚠ 本钩子是 build-only("runs before writing HTML to dist"),
- dev server 页面上看不到这些标签是正常的,用 docs:build 验收。
-
---------------------------------------------------------------------------------
-2.3 JSON-LD 结构化数据(单 script,顶层对象或数组)
---------------------------------------------------------------------------------
-统一转义:JSON.stringify(...).replace(/ 破坏注入;< 在 JSON 字符串内合法。)
-
-【首页 index.md】
- 1. WebSite:name、url、inLanguage、description
- 2. Organization:name、url(官网)、logo、sameAs(Gitee 仓库 + 官网)
-
-【内容页】
- 1. TechArticle:
- headline = 页面 H1 纯文本(去编号!与 一致,关键词前置;
- 提取顺序:源 Markdown H1 → 渲染 HTML → 兜底 title)
- description、url(= canonical)、inLanguage: "zh-CN"
- mainEntityOfPage: { @type: WebPage, @id: url }
- publisher: Organization 引用
- dateModified: git 最后提交时间(见 2.4,无则省略字段)
- 2. BreadcrumbList(侧边栏派生):
- itemListElement = [
- { position:1, name:"首页", item: SITE_URL + "/" },
- ...章节链(每级 name=去 emoji 的侧边栏文案),
- 末级 { name: 页面名, item: 当前页 url }
- ]
- 中间章节级无自己 URL → 只给 name 不给 item(schema.org 合法)。
- 侧边栏文案去前导 emoji:/^[^\p{L}\p{N}]+/u 清洗。
- 3. FAQPage(仅当 extractFaqs 命中):
- mainEntity = [{ @type:Question, name:Q,
- acceptedAnswer:{ @type:Answer, text:A } }]
- ⚠ Google 已收窄 FAQ 富结果资格,但 JSON-LD 对 GEO(AI 抽取)
- 仍然有效,且必须与页面可见 Q/A 一致。
-
---------------------------------------------------------------------------------
-2.4 git 时间线(dateModified + sitemap lastmod 共用)
---------------------------------------------------------------------------------
-单次 git 命令构建全量映射(避免每页一次进程开销):
- git log --format=:::%cI --name-only --relative -n 500 -- .
- cwd = 项目根(ctx.siteConfig.root)
-
-解析:以 ":::" 开头的行是提交时间(ISO 8601),
- 其后普通行是文件路径(git log 新→旧,首见即最新);
- key = 相对路径(--relative 保证与 pageData.relativePath 同形)。
-
-降级策略:git 不可用 / 文件无提交记录 → 静默省略 lastmod/dateModified,
- 不阻塞构建。模块级缓存 Map,整个 build 只跑一次。
-
---------------------------------------------------------------------------------
-2.5 sitemap = { hostname, transformItems }
---------------------------------------------------------------------------------
-VitePress 1.x 内置 sitemap 选项(config 顶层):
- sitemap: {
- hostname: SITE_URL,
- transformItems(items) {
- // ⚠ 关键实测(Gin_EleVue 首次构建暴露):
- // transformItems 收到的 item.url 是【相对路径】——
- // "404.html"、"zh/core/dict.html"、""(首页),
- // 无前导斜杠、无域名;hostname 是之后 SitemapStream 才拼的。
- // 所以 endsWith("/404.html") 永远不命中(冒烟测试若用完整
- // URL 传参会产生假阳性!测试必须模拟真实形态)。
- // 1) 过滤 404:/(^|\/)404\.html$/.test(item.url) → 丢弃
- // 2) 过滤 README:仓库根 README.md 会被 vitepress 当页面收录
- // (README.html 不在侧边栏、属仓库自述),同样 /^...README\.html$/ 过滤
- // 3) url → 相对路径反推:去尾 .html 加 .md;
- // "" 或 "index.html" → "index.md"
- // 4) 查 git 映射注入 lastmod;查不到保持原样
- // (部分新文件/未提交文件无 lastmod 属正常降级,
- // Gin 实测 154 条中 126 条有 lastmod)
- }
- }
-产出:构建后 dist/sitemap.xml(dev 模式不存在,属正常)。
-
-================================================================================
-步骤 ③:挂载到 .vitepress/config/index.ts
-================================================================================
-
- import { createSeoHooks } from "./seo";
- const seo = createSeoHooks(zhConfig.themeConfig?.sidebar);
-
- export default defineConfig({
- ...sharedConfig,
- transformPageData: seo.transformPageData,
- transformHead: seo.transformHead,
- sitemap: seo.sitemap,
- ...
- });
-
-⚠ createSeoHooks 必须在模块顶层调用(sitemap.transformItems 无 ctx,
- 依赖 transformPageData 先把 projectRoot 记下来;兜底 process.cwd())。
-
-================================================================================
-步骤 ④:titleTemplate(config/shared.ts)
-================================================================================
-
- export const sharedConfig = defineConfig({
- title: "EasyGoAdmin",
- titleTemplate: "EasyGoAdmin 开发文档", ← 新增
- ...
- });
-
-效果:字典模块 | EasyGoAdmin 开发文档
-机制:VitePress createTitle 中 页面 frontmatter.titleTemplate 优先,
- 其次站点级 titleTemplate,最后兜底 " | 站点title"。
- 首页 index.md 自带 titleTemplate(frontmatter)→ 不受影响。
-⚠ 站点名若与 titleTemplate 相同会被去重(createTitleTemplate 的
- siteTitle === template 分支),保持两者不同。
-
-================================================================================
-步骤 ⑤:robots.txt + 404.md
-================================================================================
-
-【public/robots.txt】(静态文件,直接进 dist)
- User-agent: *
- Allow: /
-
- # 显式放行生成式引擎 / AI 爬虫(GEO)
- User-agent: GPTBot
- Allow: /
- User-agent: ChatGPT-User
- Allow: /
- User-agent: ClaudeBot
- Allow: /
- User-agent: Claude-Web
- Allow: /
- User-agent: anthropic-ai
- Allow: /
- User-agent: PerplexityBot
- Allow: /
- User-agent: Google-Extended
- Allow: /
- User-agent: Bytespider
- Allow: /
- User-agent: bingbot
- Allow: /
-
- Sitemap: /sitemap.xml
- ⚠ Sitemap 行的地址必须与 extract.mjs 的 SITE_URL 一致(手工同步)。
-
-【404.md】(项目根目录,VitePress 自动识别为 404 页)
- ---
- head:
- - - meta
- - name: robots
- content: noindex
- ---
- # 页面不存在
-
- 抱歉,您访问的页面不存在或已被移动。
-
- [返回首页](/) · [浏览文档](/zh/introduction)
- ⚠ 首个 frontmatter head 例外:正文页约定"无 frontmatter",
- 404 页因需要 noindex 豁免。
- ⚠ 链接风格与全站一致(站内相对/根路径),死链检查才能通过。
-
-================================================================================
-步骤 ⑥:llms.txt 生成脚本 scripts/generate-llms-txt.mjs
-================================================================================
-
-零依赖 Node 脚本,import extract.mjs 复用 SITE_URL/SITE_DESCRIPTION/
-extractDescription(与 meta description 同源,绝不手写两份摘要)。
-
-流程:
- 1. 递归收集 zh/ 下全部 .md(排除 404,404 不在 zh/ 天然排除)
- 2. 每页取:首个 H1(保留编号,便于 AI 理解章节位置)
- extractDescription 首句(作为条目摘要)
- url = SITE_URL + /zh/<相对路径>.html
- 3. 按顶层目录映射到侧边栏章节分组(SECTIONS 常量数组,
- 与 config/zh.ts 的侧边栏章名保持一致;映射不到的目录兜底"附录")
- 4. 组内按 H1 编号排序(数字分支按数值、字母分支按码点、无编号最后)
- 5. 输出 llmstxt.org 标准格式:
-
- # <站点标题>
- > 。<能力一句话>。本文档共 N 篇,按章节组织。
-
- ## <章节名>
- - [H1](url): 首句摘要
- ...
- 6. 写入 public/llms.txt,stdout 打印页数/章节数
-
-⚠ 同一章节名可能由多个目录合并(如 appendix + other 同为"附录"),
- 输出时按 label 去重只打印一次。
-⚠ 脚本内不要在块注释写 "zh/**/*.md"(见步骤①的 */ 坑)。
-
-================================================================================
-步骤 ⑦:package.json 接线 + 首次生成
-================================================================================
-
- "scripts": {
- "docs": "vitepress dev .",
- "docs:llms": "node scripts/generate-llms-txt.mjs",
- "docs:build": "node scripts/generate-llms-txt.mjs && vitepress build .",
- "docs:preview": "vitepress preview ."
- }
-
-- docs:build 前置生成 llms.txt(保证部署产物永远最新)
-- 新增/改名页面后若只要刷新索引:yarn docs:llms
-- ⚠ Windows 下 yarn(cmd)执行 "A && B" 可用;不要改成分号。
-
---------------------------------------------------------------------------------
-步骤 ⑧(收尾):更新 CLAUDE.md
---------------------------------------------------------------------------------
-把以下约定写进项目 CLAUDE.md,防止后续改动破坏 SEO:
- 1. :::tip 首句 = meta description + llms.txt 条目,须"答案前置"。
- 2. 编号冻结:章节编号同时被侧边栏、正文互引("详见 5.10")、
- JSON-LD 面包屑、llms.txt、外链锚文本引用 —— 发布后禁止重排。
- 3. 新增页面的 checklist:写 H1 编号 → 加 tip 概述 →
- zh.ts 侧边栏登记 → (可选)yarn docs:llms。
- 4. 记录 SEO 工具链位置与 SITE_URL 单点修改约定。
-
---------------------------------------------------------------------------------
-第四部分:验收标准
---------------------------------------------------------------------------------
-
-A. 无 node_modules 的快速冒烟(Node ≥ 22.18,type stripping)
- node --experimental-strip-types -e "
- import('./.vitepress/config/seo.ts').then(async ({ createSeoHooks }) => {
- const zh = await import('./.vitepress/config/zh.ts');
- const h = createSeoHooks(zh.zhConfig.themeConfig?.sidebar);
- console.log(typeof h.transformPageData, typeof h.transformHead, typeof h.sitemap);
- })"
-
-B. 完整验收(需 yarn install 后)
- 1) yarn docs:build 无报错(死链检查通过)
- 2) dist/sitemap.xml 存在,含 lastmod,无 404.html 与 README.html 条目
- (lastmod 覆盖率允许 <100%:未提交的新文件无 git 记录,属正常降级)
- 3) dist/robots.txt、dist/llms.txt 存在
- 4) 抽查任意内容页 dist/zh/xxx.html 的 :
- - 无编号前缀、带 titleTemplate 后缀
- - 是该页 tip 首句(非全站统一文案)
- - 指向本页完整 URL
- - og:title/og:description/og:url/og:image 与页面一致且 URL 为绝对地址
- - 破坏注入;< 在 JSON 字符串内合法。)
-
-【首页 index.md】
- 1. WebSite:name、url、inLanguage、description
- 2. Organization:name、url(官网)、logo、sameAs(Gitee 仓库 + 官网)
-
-【内容页】
- 1. TechArticle:
- headline = 页面 H1 纯文本(去编号!与 一致,关键词前置;
- 提取顺序:源 Markdown H1 → 渲染 HTML → 兜底 title)
- description、url(= canonical)、inLanguage: "zh-CN"
- mainEntityOfPage: { @type: WebPage, @id: url }
- publisher: Organization 引用
- dateModified: git 最后提交时间(见 2.4,无则省略字段)
- 2. BreadcrumbList(侧边栏派生):
- itemListElement = [
- { position:1, name:"首页", item: SITE_URL + "/" },
- ...章节链(每级 name=去 emoji 的侧边栏文案),
- 末级 { name: 页面名, item: 当前页 url }
- ]
- 中间章节级无自己 URL → 只给 name 不给 item(schema.org 合法)。
- 侧边栏文案去前导 emoji:/^[^\p{L}\p{N}]+/u 清洗。
- 3. FAQPage(仅当 extractFaqs 命中):
- mainEntity = [{ @type:Question, name:Q,
- acceptedAnswer:{ @type:Answer, text:A } }]
- ⚠ Google 已收窄 FAQ 富结果资格,但 JSON-LD 对 GEO(AI 抽取)
- 仍然有效,且必须与页面可见 Q/A 一致。
-
---------------------------------------------------------------------------------
-2.4 git 时间线(dateModified + sitemap lastmod 共用)
---------------------------------------------------------------------------------
-单次 git 命令构建全量映射(避免每页一次进程开销):
- git log --format=:::%cI --name-only --relative -n 500 -- .
- cwd = 项目根(ctx.siteConfig.root)
-
-解析:以 ":::" 开头的行是提交时间(ISO 8601),
- 其后普通行是文件路径(git log 新→旧,首见即最新);
- key = 相对路径(--relative 保证与 pageData.relativePath 同形)。
-
-降级策略:git 不可用 / 文件无提交记录 → 静默省略 lastmod/dateModified,
- 不阻塞构建。模块级缓存 Map,整个 build 只跑一次。
-
---------------------------------------------------------------------------------
-2.5 sitemap = { hostname, transformItems }
---------------------------------------------------------------------------------
-VitePress 1.x 内置 sitemap 选项(config 顶层):
- sitemap: {
- hostname: SITE_URL,
- transformItems(items) {
- // ⚠ 关键实测(Gin_EleVue 首次构建暴露):
- // transformItems 收到的 item.url 是【相对路径】——
- // "404.html"、"zh/core/dict.html"、""(首页),
- // 无前导斜杠、无域名;hostname 是之后 SitemapStream 才拼的。
- // 所以 endsWith("/404.html") 永远不命中(冒烟测试若用完整
- // URL 传参会产生假阳性!测试必须模拟真实形态)。
- // 1) 过滤 404:/(^|\/)404\.html$/.test(item.url) → 丢弃
- // 2) 过滤 README:仓库根 README.md 会被 vitepress 当页面收录
- // (README.html 不在侧边栏、属仓库自述),同样 /^...README\.html$/ 过滤
- // 3) url → 相对路径反推:去尾 .html 加 .md;
- // "" 或 "index.html" → "index.md"
- // 4) 查 git 映射注入 lastmod;查不到保持原样
- // (部分新文件/未提交文件无 lastmod 属正常降级,
- // Gin 实测 154 条中 126 条有 lastmod)
- }
- }
-产出:构建后 dist/sitemap.xml(dev 模式不存在,属正常)。
-
-================================================================================
-步骤 ③:挂载到 .vitepress/config/index.ts
-================================================================================
-
- import { createSeoHooks } from "./seo";
- const seo = createSeoHooks(zhConfig.themeConfig?.sidebar);
-
- export default defineConfig({
- ...sharedConfig,
- transformPageData: seo.transformPageData,
- transformHead: seo.transformHead,
- sitemap: seo.sitemap,
- ...
- });
-
-⚠ createSeoHooks 必须在模块顶层调用(sitemap.transformItems 无 ctx,
- 依赖 transformPageData 先把 projectRoot 记下来;兜底 process.cwd())。
-
-================================================================================
-步骤 ④:titleTemplate(config/shared.ts)
-================================================================================
-
- export const sharedConfig = defineConfig({
- title: "EasyGoAdmin",
- titleTemplate: "EasyGoAdmin 开发文档", ← 新增
- ...
- });
-
-效果:字典模块 | EasyGoAdmin 开发文档
-机制:VitePress createTitle 中 页面 frontmatter.titleTemplate 优先,
- 其次站点级 titleTemplate,最后兜底 " | 站点title"。
- 首页 index.md 自带 titleTemplate(frontmatter)→ 不受影响。
-⚠ 站点名若与 titleTemplate 相同会被去重(createTitleTemplate 的
- siteTitle === template 分支),保持两者不同。
-
-================================================================================
-步骤 ⑤:robots.txt + 404.md
-================================================================================
-
-【public/robots.txt】(静态文件,直接进 dist)
- User-agent: *
- Allow: /
-
- # 显式放行生成式引擎 / AI 爬虫(GEO)
- User-agent: GPTBot
- Allow: /
- User-agent: ChatGPT-User
- Allow: /
- User-agent: ClaudeBot
- Allow: /
- User-agent: Claude-Web
- Allow: /
- User-agent: anthropic-ai
- Allow: /
- User-agent: PerplexityBot
- Allow: /
- User-agent: Google-Extended
- Allow: /
- User-agent: Bytespider
- Allow: /
- User-agent: bingbot
- Allow: /
-
- Sitemap: /sitemap.xml
- ⚠ Sitemap 行的地址必须与 extract.mjs 的 SITE_URL 一致(手工同步)。
-
-【404.md】(项目根目录,VitePress 自动识别为 404 页)
- ---
- head:
- - - meta
- - name: robots
- content: noindex
- ---
- # 页面不存在
-
- 抱歉,您访问的页面不存在或已被移动。
-
- [返回首页](/) · [浏览文档](/zh/introduction)
- ⚠ 首个 frontmatter head 例外:正文页约定"无 frontmatter",
- 404 页因需要 noindex 豁免。
- ⚠ 链接风格与全站一致(站内相对/根路径),死链检查才能通过。
-
-================================================================================
-步骤 ⑥:llms.txt 生成脚本 scripts/generate-llms-txt.mjs
-================================================================================
-
-零依赖 Node 脚本,import extract.mjs 复用 SITE_URL/SITE_DESCRIPTION/
-extractDescription(与 meta description 同源,绝不手写两份摘要)。
-
-流程:
- 1. 递归收集 zh/ 下全部 .md(排除 404,404 不在 zh/ 天然排除)
- 2. 每页取:首个 H1(保留编号,便于 AI 理解章节位置)
- extractDescription 首句(作为条目摘要)
- url = SITE_URL + /zh/<相对路径>.html
- 3. 按顶层目录映射到侧边栏章节分组(SECTIONS 常量数组,
- 与 config/zh.ts 的侧边栏章名保持一致;映射不到的目录兜底"附录")
- 4. 组内按 H1 编号排序(数字分支按数值、字母分支按码点、无编号最后)
- 5. 输出 llmstxt.org 标准格式:
-
- # <站点标题>
- > 。<能力一句话>。本文档共 N 篇,按章节组织。
-
- ## <章节名>
- - [H1](url): 首句摘要
- ...
- 6. 写入 public/llms.txt,stdout 打印页数/章节数
-
-⚠ 同一章节名可能由多个目录合并(如 appendix + other 同为"附录"),
- 输出时按 label 去重只打印一次。
-⚠ 脚本内不要在块注释写 "zh/**/*.md"(见步骤①的 */ 坑)。
-
-================================================================================
-步骤 ⑦:package.json 接线 + 首次生成
-================================================================================
-
- "scripts": {
- "docs": "vitepress dev .",
- "docs:llms": "node scripts/generate-llms-txt.mjs",
- "docs:build": "node scripts/generate-llms-txt.mjs && vitepress build .",
- "docs:preview": "vitepress preview ."
- }
-
-- docs:build 前置生成 llms.txt(保证部署产物永远最新)
-- 新增/改名页面后若只要刷新索引:yarn docs:llms
-- ⚠ Windows 下 yarn(cmd)执行 "A && B" 可用;不要改成分号。
-
---------------------------------------------------------------------------------
-步骤 ⑧(收尾):更新 CLAUDE.md
---------------------------------------------------------------------------------
-把以下约定写进项目 CLAUDE.md,防止后续改动破坏 SEO:
- 1. :::tip 首句 = meta description + llms.txt 条目,须"答案前置"。
- 2. 编号冻结:章节编号同时被侧边栏、正文互引("详见 5.10")、
- JSON-LD 面包屑、llms.txt、外链锚文本引用 —— 发布后禁止重排。
- 3. 新增页面的 checklist:写 H1 编号 → 加 tip 概述 →
- zh.ts 侧边栏登记 → (可选)yarn docs:llms。
- 4. 记录 SEO 工具链位置与 SITE_URL 单点修改约定。
-
---------------------------------------------------------------------------------
-第四部分:验收标准
---------------------------------------------------------------------------------
-
-A. 无 node_modules 的快速冒烟(Node ≥ 22.18,type stripping)
- node --experimental-strip-types -e "
- import('./.vitepress/config/seo.ts').then(async ({ createSeoHooks }) => {
- const zh = await import('./.vitepress/config/zh.ts');
- const h = createSeoHooks(zh.zhConfig.themeConfig?.sidebar);
- console.log(typeof h.transformPageData, typeof h.transformHead, typeof h.sitemap);
- })"
-
-B. 完整验收(需 yarn install 后)
- 1) yarn docs:build 无报错(死链检查通过)
- 2) dist/sitemap.xml 存在,含 lastmod,无 404.html 与 README.html 条目
- (lastmod 覆盖率允许 <100%:未提交的新文件无 git 记录,属正常降级)
- 3) dist/robots.txt、dist/llms.txt 存在
- 4) 抽查任意内容页 dist/zh/xxx.html 的 :
- - 无编号前缀、带 titleTemplate 后缀
- - 是该页 tip 首句(非全站统一文案)
- - 指向本页完整 URL
- - og:title/og:description/og:url/og:image 与页面一致且 URL 为绝对地址
- -