| ---
|
| import { adConfig1, adConfig2 } from "@/config/adConfig";
|
| import type { AdConfig } from "@/types/config";
|
| import { url } from "@/utils/url-utils";
|
|
|
| export interface Props {
|
| class?: string;
|
| configId?: string;
|
| }
|
|
|
| const { class: className = "", configId } = Astro.props as Props;
|
|
|
|
|
| const getAdConfig = (id?: string): AdConfig => {
|
| switch (id) {
|
| case "ad1":
|
| return adConfig1;
|
| case "ad2":
|
| return adConfig2;
|
| default:
|
| return adConfig1;
|
| }
|
| };
|
|
|
| const currentAdConfig = getAdConfig(configId);
|
|
|
|
|
| const isExpired = currentAdConfig.expireDate
|
| ? new Date() > new Date(currentAdConfig.expireDate)
|
| : false;
|
|
|
|
|
| if (isExpired) {
|
| return null;
|
| }
|
|
|
|
|
| const getPaddingStyle = () => {
|
| if (!currentAdConfig.padding) return "p-4";
|
|
|
| if (currentAdConfig.padding.all !== undefined) {
|
|
|
| return currentAdConfig.padding.all === "0" ? "p-0" : "";
|
| }
|
|
|
|
|
| const { top, right, bottom, left } = currentAdConfig.padding;
|
| return (
|
| [
|
| top !== undefined ? `pt-[${top}]` : "",
|
| right !== undefined ? `pr-[${right}]` : "",
|
| bottom !== undefined ? `pb-[${bottom}]` : "",
|
| left !== undefined ? `pl-[${left}]` : "",
|
| ]
|
| .filter(Boolean)
|
| .join(" ") || "p-4"
|
| );
|
| };
|
|
|
| const paddingClass = getPaddingStyle();
|
| ---
|
|
|
| <div
|
| class:list={[
|
| "card-base",
|
| "advertisement-widget",
|
| "relative",
|
| "overflow-hidden",
|
| className,
|
| ]}
|
| data-display-count={currentAdConfig.displayCount}
|
| data-closable={currentAdConfig.closable}
|
| >
|
|
|
| {
|
| currentAdConfig.closable && (
|
| <button
|
| class="absolute top-2 right-2 w-6 h-6 flex items-center justify-center rounded-full bg-neutral-200 hover:bg-neutral-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 text-neutral-500 hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200 transition-all duration-200 z-10 close-ad-btn"
|
| title="关闭广告"
|
| aria-label="关闭广告"
|
| >
|
| <svg
|
| class="w-4 h-4"
|
| fill="none"
|
| viewBox="0 0 24 24"
|
| stroke="currentColor"
|
| >
|
| <path
|
| stroke-linecap="round"
|
| stroke-linejoin="round"
|
| stroke-width="2"
|
| d="M6 18L18 6M6 6l12 12"
|
| />
|
| </svg>
|
| </button>
|
| )
|
| }
|
|
|
|
|
| <div class={paddingClass}>
|
|
|
| {
|
| currentAdConfig.title && (
|
| <h3 class="text-lg font-bold mb-3 text-center text-neutral-900 dark:text-neutral-50 transition">
|
| {currentAdConfig.title}
|
| </h3>
|
| )
|
| }
|
|
|
|
|
| {
|
| currentAdConfig.image && (
|
| <div
|
| class={
|
| currentAdConfig.title ||
|
| currentAdConfig.content ||
|
| currentAdConfig.link
|
| ? "mb-3"
|
| : ""
|
| }
|
| >
|
| {currentAdConfig.image.link ? (
|
| <a
|
| href={currentAdConfig.image.link}
|
| target={currentAdConfig.image.external ? "_blank" : "_self"}
|
| rel={currentAdConfig.image.external ? "noopener noreferrer" : ""}
|
| class="block group"
|
| >
|
| <img
|
| src={currentAdConfig.image.src.startsWith('/') ? url(currentAdConfig.image.src) : currentAdConfig.image.src}
|
| alt={currentAdConfig.image.alt || "广告图片"}
|
| class={`w-full h-auto transition-transform duration-300 group-hover:scale-105 ${
|
| paddingClass === "p-0"
|
| ? "rounded-(--radius-large)"
|
| : "rounded-lg"
|
| }`}
|
| loading="lazy"
|
| />
|
| </a>
|
| ) : (
|
| <img
|
| src={currentAdConfig.image.src.startsWith('/') ? url(currentAdConfig.image.src) : currentAdConfig.image.src}
|
| alt={currentAdConfig.image.alt || "广告图片"}
|
| class={`w-full h-auto ${
|
| paddingClass === "p-0"
|
| ? "rounded-(--radius-large)"
|
| : "rounded-lg"
|
| }`}
|
| loading="lazy"
|
| />
|
| )}
|
| </div>
|
| )
|
| }
|
|
|
|
|
| {
|
| currentAdConfig.content && (
|
| <p class="text-sm text-center mb-3 leading-relaxed text-neutral-600 dark:text-neutral-300 transition">
|
| {currentAdConfig.content}
|
| </p>
|
| )
|
| }
|
|
|
|
|
| {
|
| currentAdConfig.link && (
|
| <div class="text-center">
|
| <a
|
| href={currentAdConfig.link.url}
|
| target={currentAdConfig.link.external ? "_blank" : "_self"}
|
| rel={currentAdConfig.link.external ? "noopener noreferrer" : ""}
|
| class="inline-flex items-center gap-2 px-4 py-2 bg-[oklch(0.75_0.14_var(--hue))] hover:bg-[oklch(0.7_0.16_var(--hue))] text-white rounded-lg transition-all duration-200 font-medium text-sm shadow-lg shadow-[oklch(0.75_0.14_var(--hue))]/25 hover:shadow-xl hover:shadow-[oklch(0.75_0.14_var(--hue))]/30 hover:scale-105 transform"
|
| >
|
| <span>{currentAdConfig.link.text}</span>
|
| {currentAdConfig.link.external && (
|
| <svg
|
| class="w-4 h-4"
|
| fill="none"
|
| viewBox="0 0 24 24"
|
| stroke="currentColor"
|
| >
|
| <path
|
| stroke-linecap="round"
|
| stroke-linejoin="round"
|
| stroke-width="2"
|
| d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
| />
|
| </svg>
|
| )}
|
| </a>
|
| </div>
|
| )
|
| }
|
| </div>
|
| </div>
|
|
|
| <script>
|
| document.addEventListener("DOMContentLoaded", () => {
|
|
|
| localStorage.removeItem("ad-closed");
|
|
|
| const adWidgets = document.querySelectorAll(
|
| ".advertisement-widget"
|
| ) as NodeListOf<HTMLElement>;
|
|
|
| adWidgets.forEach((widget) => {
|
| const closeBtn = widget.querySelector(".close-ad-btn");
|
| const displayCount = parseInt(
|
| widget.getAttribute("data-display-count") || "-1"
|
| );
|
| const isClosable = widget.getAttribute("data-closable") === "true";
|
|
|
|
|
| if (displayCount > 0) {
|
| const storageKey = "ad-display-count";
|
| const currentCount = parseInt(localStorage.getItem(storageKey) || "0");
|
|
|
| if (currentCount >= displayCount) {
|
| widget.style.display = "none";
|
| return;
|
| }
|
|
|
| localStorage.setItem(storageKey, (currentCount + 1).toString());
|
| }
|
|
|
|
|
| if (closeBtn && isClosable) {
|
| closeBtn.addEventListener("click", (e) => {
|
| e.preventDefault();
|
| e.stopPropagation();
|
|
|
|
|
| widget.style.transform = "translateX(100%)";
|
| widget.style.opacity = "0";
|
| widget.style.transition = "all 0.3s ease-out";
|
|
|
| setTimeout(() => {
|
| widget.style.display = "none";
|
|
|
| }, 300);
|
| });
|
| }
|
| });
|
| });
|
| </script>
|
|
|
| <style>
|
| .advertisement-widget {
|
|
|
| background: var(--card-bg);
|
| border: 1px solid var(--line-divider);
|
| border-radius: var(--radius-large);
|
| color: var(--primary-text-color);
|
|
|
| transition: all 0.3s ease;
|
|
|
|
|
| box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
|
|
|
| backdrop-filter: blur(10px);
|
| -webkit-backdrop-filter: blur(10px);
|
| }
|
|
|
|
|
| :global(.dark) .advertisement-widget {
|
| box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
| }
|
|
|
|
|
|
|
| .advertisement-widget:hover {
|
|
|
| box-shadow:
|
| 0 4px 12px rgba(0, 0, 0, 0.08),
|
| 0 0 0 1px var(--line-divider);
|
| transform: translateY(-2px);
|
| border-color: oklch(0.75 0.14 var(--hue));
|
| }
|
|
|
|
|
| :global(.dark) .advertisement-widget:hover {
|
| box-shadow:
|
| 0 4px 20px rgba(0, 0, 0, 0.3),
|
| 0 0 0 1px oklch(0.75 0.14 var(--hue));
|
| background: color-mix(
|
| in srgb,
|
| var(--card-bg),
|
| oklch(0.75 0.14 var(--hue)) 5%
|
| );
|
| }
|
|
|
| .close-ad-btn {
|
| opacity: 0;
|
| transition: all 0.2s ease;
|
| backdrop-filter: blur(8px);
|
| }
|
|
|
| .advertisement-widget:hover .close-ad-btn {
|
| opacity: 1;
|
| }
|
|
|
|
|
| .advertisement-widget a[class*="bg-[oklch"] {
|
| background: oklch(0.75 0.14 var(--hue));
|
| transition: all 0.2s ease;
|
| }
|
|
|
| .advertisement-widget a[class*="bg-[oklch"]:hover {
|
| background: oklch(0.7 0.16 var(--hue));
|
| box-shadow: 0 4px 12px
|
| color-mix(in srgb, oklch(0.75 0.14 var(--hue)), transparent 70%);
|
| }
|
|
|
|
|
| .advertisement-widget img {
|
| transition: all 0.3s ease;
|
| }
|
|
|
| .advertisement-widget:hover img {
|
| filter: brightness(1.05) saturate(1.1);
|
| }
|
|
|
| :global(.dark) .advertisement-widget:hover img {
|
| filter: brightness(1.1) saturate(1.15);
|
| }
|
|
|
|
|
| @media (max-width: 768px) {
|
| .advertisement-widget {
|
| margin: 0;
|
| border-radius: var(--radius-large);
|
|
|
| backdrop-filter: blur(8px);
|
| }
|
|
|
| .advertisement-widget:hover {
|
| transform: none;
|
| box-shadow:
|
| 0 2px 8px rgba(0, 0, 0, 0.06),
|
| 0 0 0 1px var(--line-divider);
|
| }
|
|
|
| :global(.dark) .advertisement-widget:hover {
|
| box-shadow:
|
| 0 2px 12px rgba(0, 0, 0, 0.2),
|
| 0 0 0 1px oklch(0.75 0.14 var(--hue));
|
| }
|
| }
|
|
|
|
|
| @media (prefers-contrast: high) {
|
| .advertisement-widget {
|
| border-width: 2px;
|
| }
|
|
|
| .advertisement-widget:hover {
|
| border-color: oklch(0.6 0.2 var(--hue));
|
| }
|
| }
|
|
|
|
|
| @media (prefers-reduced-motion: reduce) {
|
| .advertisement-widget,
|
| .close-ad-btn,
|
| .advertisement-widget img,
|
| .advertisement-widget a {
|
| transition: none;
|
| }
|
|
|
| .advertisement-widget:hover {
|
| transform: none;
|
| }
|
| }
|
| </style>
|
| |