Meteor主题友链页面自研

发布于:Eucalyptus-Blog

Meteor主题虽然设计简约现代,但由于缺乏原生的友情链接管理功能,许多博主只能将友情链接勉强添加在网站底部,这不仅影响页面美观,也不便于访客查找和互动;为了解决这一痛点,本博主对主题进行了深度二次开发,专门打造了一个独立的友情链接页面,该页面不仅实现了友链分类展示、图文混排等基本功能,更创新性地开发了前端提交表单,允许其他站长直接在线提交申请,同时配套开发了完善的后台审核系统,管理员可以便捷地查看申请信息、审核状态,并一键通过或拒绝,整个流程实现了自动化闭环管理,大大提升了友链交换的效率和用户体验。

文件路径:

下述文件需放至主题目录下

page-link.php

下述文件需存放至主题目录下的自建self-innovate目录下

default-avatar.jpg
page-links.css
page-links.js
pending-links.php

以下是核心代码部分,这段代码实现了系统的关键功能,

并且这里对友链头像有三个判断的逻辑

  • 有头像 → 正常显示;
  • 无头像 → 立即显示默认头像;
  • 有头像但 3 秒仍未加载完成 → 自动替换成默认头像。
<?php
/*
 * Template Name: 友情链接
 * Description:  极简友链模板(样式/脚本已分离,含评论)
 */

get_header(); 
?>

<div class="hero-title">
  <?php the_title( '<h1>', '</h1>' ); ?>
  <p class="hero-sub">欢迎交换友链 · 携手点亮彼此的星空</p>
</div>


<!-- 载入独立样式 -->
<link rel="stylesheet" href="<?php echo get_template_directory_uri(); ?>/self-innovate/page-links.css">

<div class="page-links">

  <!-- 搜索框 -->
  <div class="link-search">
    <input type="text" id="link-search" placeholder="搜索站点名称或描述…">
  </div>

  <!-- 友链列表 -->
  <?php
  $cats = get_terms( 'link_category', array( 'hide_empty' => 0 ) );
  foreach ( $cats as $cat ) :
    $bookmarks = get_bookmarks( array(
      'category' => $cat->term_id,
      'orderby'  => 'rating',
      'order'    => 'DESC'
    ) );
    if ( empty( $bookmarks ) ) continue;
  ?>
    <div class="link-cat">
      <h2><?php echo esc_html( $cat->name ); ?></h2>
      <?php if ( ! empty( $cat->description ) ) : ?>
        <p class="link-cat-desc"><?php echo esc_html( $cat->description ); ?></p>
      <?php endif; ?>
      <div class="links">
      <?php foreach ( $bookmarks as $link ) : ?>
        <a class="link-card" href="<?php echo esc_url( $link->link_url ); ?>" target="_blank" rel="noopener">
        <img
          src="<?php echo esc_url( $link->link_image ?: '' ); ?>"
          data-default="<?php echo esc_url( get_template_directory_uri() . '/self-innovate/default-avatar.jpg' ); ?>"
          alt="<?php echo esc_attr( $link->link_name ); ?>"
          onerror="this.src=this.dataset.default"
          onload="clearTimeout(this.t)"
          ontimeout="this.src=this.dataset.default"
      />
        <script>
          (function(img){
            img.t = setTimeout(function(){ img.src = img.dataset.default; }, 3000);
          })(document.currentScript.previousElementSibling);
        </script>
          <div class="info">
            <div class="name"><?php echo esc_html( $link->link_name ); ?></div>
            <div class="desc"><?php echo esc_html( $link->link_description ); ?></div>
          </div>
        </a>
      <?php endforeach; ?>
      </div>
    </div>
  <?php endforeach; ?>

    <!-- 后台内容显示 -->
  <?php if ( get_the_content() ) : ?>
    <div class="link-intro" style="margin-bottom:30px;"><?php the_content(); ?></div>
  <?php endif; ?>

  
  <!-- 友链申请弹窗 -->
  <div class="btn-center">
    <button id="open-link-modal" class="btn-hero">
      <span>申请交换友链</span>
    </button>
  </div>


  <!-- ===== 评论区 ===== -->
    <?php
    // while ( have_posts() ) : the_post();
        if ( comments_open() || get_comments_number() ) : ?>
            <section class="link-comments">
                <h2 class="comments-title"></h2>
                <?php comments_template(); ?>
            </section>
        <?php
        endif;
    // endwhile;
    ?>

  <!-- 弹窗 -->
  <div id="link-modal-overlay" class="modal-fade">
    <div class="modal-dialog">
      <button class="modal-close" aria-label="关闭">×</button>
      <h3 class="modal-title">申请交换友链</h3>
      <form id="link-form" class="modal-form">
        <input type="text"   name="link_name"        placeholder="网站名称 *" required>
        <input type="url"    name="link_url"         placeholder="网站地址 *" required>
        <input type="email"  name="link_owner_email" placeholder="站长邮箱 *" required>
        <input type="url"    name="link_image"       placeholder="头像 / Logo *" required>
        <textarea name="link_description" rows="3" placeholder="一句话描述 *" required></textarea>
        <button type="submit" class="btn-submit">提交申请</button>
        <p id="form-msg" class="form-msg"></p>
      </form>
    </div>
  </div>
</div>

<!-- 载入独立脚本 -->
<script>
  var ajax_comment_obj = <?php echo wp_json_encode(array('ajax_url' => admin_url('admin-ajax.php'))); ?>;
</script>
<script src="<?php echo get_template_directory_uri(); ?>/self-innovate/page-links.js" defer></script>

<?php get_footer(); ?>

下面是核心代码的css样式

/**
 *  * page-links.css
 *   * 版本:1.0.0
 *    * 描述:友情链接页面专用样式
 *     * 作者:Eucalyptus
 *      * 创建日期:2025-08-30
 *       * 修改记录:
 *        *   - 2025-08-30:首版,含友链卡片、搜索、弹窗
 *         */

/* ========= 1. 页面通用 ========= */
.page-links{
  max-width:960px;
  margin:0 auto;
  padding:40px 20px;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;
}

/* ============ 顶部标题(跟随深浅 + 移动端适配) ============ */
/* 友情链接横幅 */
.hero-title{
  padding:3.5rem 1.5rem 2.5rem;
  border-radius:0 0 1.5rem 1.5rem;
  text-align:center;
}
.hero-title h1{
  font-size:2.2rem;
  font-weight:700;
  letter-spacing:.5px;
  position:relative;
}
.hero-title h1::after{
  content:'';
  display:block;
  width:60px;
  height:3px;
  background:#667eea;
  margin:8px auto 0;
  border-radius:2px;
}
.hero-sub{
  font-size:1rem;
  color:#666;
  margin-top:.75rem;
}


/* ========= 玻璃态卡片 3.0 ========= */
.links{
  display:grid;
  grid-template-columns:repeat(3, 1fr); /* 固定 3 列 */
  gap:24px;
}

.link-card{
  position:relative;
  padding:20px;
  background:transparent;                /* 跟随深浅 */
  border:1px solid rgba(var(--color-border,0 0 0),.1);
  border-radius:20px;
  box-shadow:0 8px 32px rgba(0,0,0,.08);  /* 深浅通用阴影 */
  transition:.4s cubic-bezier(.175,.885,.32,1.275);
  overflow:hidden;
}

:root{
  --color-border: 0 0 0;       /* 深色模式 */
}
[data-theme="light"]{
  --color-border: 255 255 255; /* 浅色模式 */
}

.link-card::before{                               /* 光泽渐变 */
  content:'';
  position:absolute;
  inset:0;
  background:linear-gradient(135deg,transparent 40%,rgba(255,255,255,.3));
  pointer-events:none;
}

.link-card:hover{
  transform:translateY(-6px) scale(1.02);
  box-shadow:0 12px 48px rgba(102,126,234,.25);
}

/* 头像 */
.link-card img{
  width:64px;
  height:64px;
  border-radius:50%;
  border:2px solid #fff;
  box-shadow:0 4px 12px rgba(0,0,0,.08);
  object-fit:cover;
}

/* 文字区 */
.link-card .info{
  margin-left:16px;
}
.link-card .name{
  font-size:18px;
  font-weight:700;
  letter-spacing:.5px;
  margin-bottom:6px;
}
.link-card .desc{
  font-size:14px;
  color:#555;
  line-height:1.5;
}

/* 右上角角标(纯 CSS) */
.link-card{
  position: relative;           /* 为伪元素定位 */
}
.link-card::after{
  content: "友链";              /* 角标文字 */
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 6px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg,#ea66d4,#ac5196);
  border-radius: 4px;
  opacity: 0;
  transition: opacity .3s;
  pointer-events: none;         /* 不影响点击 */
}
.link-card:hover::after{
  opacity: 1;
}

/* ===== 分类标题 3.0 ===== */
.link-cat{
  position:relative;
  margin-bottom:40px;
}
.link-cat h2{
  display:inline-flex;
  align-items:center;
  gap:8px;
  padding:8px 18px;
  font-size:20px;
  margin-bottom: 15px;
  font-weight:700;
  color:#fff;
  background:linear-gradient(135deg,#667eea,#764ba2);
  border-radius:20px;
  box-shadow:0 4px 12px rgba(102,126,234,.25);
  letter-spacing:.5px;
}

/* 分类描述 */
.link-cat-desc{
  margin: -8px 0 24px 18px;   /* 负值贴紧标题,左侧与标题文字对齐 */
  font-size: 0.9rem;
  color: #555;
  letter-spacing: .4px;
  line-height: 1.5;
  max-width: 540px;
}

/* ========= 移动端适配 ========= */
@media (max-width: 768px) {
  .links {
    grid-template-columns: 1fr; /* 1 列 */
    gap: 20px;
  }

  .link-card {
    padding: 16px;
  }

  .link-card img {
    width: 48px;
    height: 48px;
  }

  .link-card .name {
    font-size: 16px;
  }

  .link-card .desc {
    font-size: 13px;
  }
}
/* ========= 4. 搜索框 ========= */
.link-search{
  display:flex;
  margin-bottom:40px;
}

.link-search input{
  width:100%;
  max-width:320px;
  padding:12px 40px 12px 16px;
  font-size:15px;
  color:#333;
  background:#fff;
  border:2px solid transparent;
  border-radius:30px;
  box-shadow:0 4px 12px rgba(102,126,234,.15);
  transition:border-color .3s, box-shadow .3s;
  outline:none;
  background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23667eea" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>');
  background-repeat:no-repeat;
  background-position:right 14px center;
  background-size:20px 20px;
}

/* 悬浮/聚焦高亮 */
.link-search input:focus{
  border-color:#667eea;
  box-shadow:0 0 0 4px rgba(102,126,234,.25);
}

/* placeholder 样式 */
.link-search input::placeholder{
  color:#7f8c8d;
  font-size:14px;
}

/* ========= 5. 按钮 & 弹窗 ========= */
/* 5.1 触发按钮居中容器 */
.btn-center{
  display:flex;
  justify-content:center;
  margin:30px 0;
}

/* 5.2 主按钮样式 */
.btn-hero{
  position:relative;
  padding:12px 32px;
  font-size:16px;
  font-weight:600;
  color:#fff;
  background:linear-gradient(135deg,#667eea 0%, #764ba2 100%);
  border:none;
  border-radius:30px;
  cursor:pointer;
  box-shadow:0 6px 20px rgba(102,126,234,.4);
  transition:all .3s;
  overflow:hidden;
}
.btn-hero:hover{
  transform:translateY(-3px) scale(1.03);
  box-shadow:0 10px 30px rgba(102,126,234,.55);
}

/* 5.3 弹窗遮罩 */
.modal-fade{
  position:fixed;
  inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  background:rgba(0,0,0,.55);
  backdrop-filter:blur(4px);
  opacity:0;
  visibility:hidden;
  transition:.35s;
  z-index:9999;
}
.modal-fade.show{
  opacity:1;
  visibility:visible;
}

/* 5.4 弹窗主体 */
.modal-dialog{
  width:92%;
  max-width:420px;
  background:#fff;
  border-radius:16px;
  padding:32px 36px 36px;
  position:relative;
  transform:translateY(-30px) scale(.95);
  transition:transform .35s;
}
.modal-fade.show .modal-dialog{
  transform:none;
}

/* 5.5 关闭按钮 */
.modal-close{
  position:absolute;
  top:14px;
  right:18px;
  font-size:26px;
  background:none;
  border:none;
  color:#999;
  cursor:pointer;
}

/* 5.6 表单元素 */
.modal-form{
  display:flex;
  flex-direction:column;
  gap:14px;
}
.modal-form input,
.modal-form textarea{
  padding:12px 14px;
  border:1px solid #e1e5e9;
  border-radius:8px;
}
.btn-submit{
  margin-top:8px;
  padding:12px;
  color:#fff;
  border:none;
  border-radius:30px;
  background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);
  cursor:pointer;
}

/* 移动端补丁,溢出锁死 */
.page-links{
  overflow-x: hidden;   /* 关键:禁止横向滚动 */
}

以下是JavaScript代码,主要用来实现前端友链页面的实时搜索功能、友链提交弹窗(使用模态框组件展示表单,包含必填字段验证和样式反馈)以及后端提交申请内容到后端的功能

/* ========= 实时搜索 ========= */
document.getElementById('link-search').addEventListener('input', function () {
  const kw = this.value.toLowerCase();
  document.querySelectorAll('.link-card').forEach(card => {
    card.style.display = card.textContent.toLowerCase().includes(kw) ? 'flex' : 'none';
  });
});

/* ========= 弹窗控制 ========= */
const overlay = document.getElementById('link-modal-overlay');
document.getElementById('open-link-modal').addEventListener('click', () => overlay.classList.add('show'));
overlay.addEventListener('click', e => {
  if (e.target === overlay || e.target.classList.contains('modal-close')) overlay.classList.remove('show');
});

/* ========= Ajax 提交友链 ========= */
document.getElementById('link-form').addEventListener('submit', function (e) {
  e.preventDefault();
  const data = new FormData(this);
  data.append('action', 'submit_link_apply');
  fetch(ajax_comment_obj.ajax_url, { method: 'POST', body: data })
    .then(r => r.json())
    .then(res => {
      document.getElementById('form-msg').textContent = res.data;
      if (res.success) {
        this.reset();
        setTimeout(() => overlay.classList.remove('show'), 1500);
      }
    });
});

最后是后端处理前端请求并为link_manager新增待审核列的代码,审核逻辑基于链接是否在前端隐藏, 关闭私密功能即可前端展示

<?php
/**
 * 待审核友链提示
 * 包含:后台列表新增“待审核”列 + 红色气泡菜单提示 + Ajax 接收
 */

/* --------------------------------------------------
 * 1. 后台列表新增“待审核”列,纯文本,系统默认样式
 * -------------------------------------------------- */
/* 新增“待审核”列(键名 review) */
add_filter( 'manage_link-manager_columns', function ( $cols ) {
    $cols['review'] = '待审核';
    return $cols;
} );

/* 单元格输出红色气泡 */
add_action( 'manage_link_custom_column', function ( $col, $link_id ) {
    if ( $col === 'review' ) {
        echo get_bookmark_field( 'link_visible', $link_id ) === 'N'
            ? '<span style="display:inline-block;background:#e60026;color:#fff;font-size:11px;padding:2px 6px;border-radius:10px;margin-left:4px;">待审</span>'
            : '';
    }
}, 10, 2 );


/* --------------------------------------------------
 * 2. 后台“链接”菜单右上角红色数字气泡
 * -------------------------------------------------- */
add_action( 'admin_menu', function () {
    $count = 0;
    foreach ( get_bookmarks( [ 'hide_invisible' => 0 ] ) as $link ) {
        if ( $link->link_visible === 'N' ) $count++;
    }
    if ( $count ) {
        global $menu;
        foreach ( $menu as &$item ) {
            if ( $item[2] === 'link-manager.php' ) {
                $item[0] .= ' <span class="awaiting-mod"><span class="pending-count">' . $count . '</span></span>';
                break;
            }
        }
    }
} );

/* --------------------------------------------------
 * 3. Ajax 接收端(无邮件)
 * -------------------------------------------------- */
add_action( 'wp_ajax_nopriv_submit_link_apply', 'handle_link_apply' );
add_action( 'wp_ajax_submit_link_apply',        'handle_link_apply' );
function handle_link_apply() {
    $name  = sanitize_text_field( $_POST['link_name'] ?? '' );
    $url   = esc_url_raw        ( $_POST['link_url']  ?? '' );
    $email = sanitize_email      ( $_POST['link_owner_email'] ?? '' );
    $desc  = sanitize_text_field( $_POST['link_description'] ?? '' );
    $img   = esc_url_raw        ( $_POST['link_image']       ?? '' );

    if ( empty( $name ) || empty( $url ) || ! is_email( $email ) ) {
        wp_send_json_error( '请完整填写必填项' );
    }

    wp_insert_link( [
        'link_name'        => $name,
        'link_url'         => $url,
        'link_description' => $desc,
        'link_image'       => $img,
        'link_owner_email' => $email,
        'link_visible'     => 'N',
    ] );

    wp_send_json_success( '已收到申请,审核后显示,感谢!' );
}

上传代码至Meteor主题目录完成部署,最后在functions.php中启用link_manager并调用pending-links.php处理后台任务。

WordPress的functions.php文件中添加指定代码可实现特定功能,需注意代码正确性和备份以防出错。


/* 开启后台链接 */
add_filter( 'pre_option_link_manager_enabled', '__return_true' );

 /* 后台新增友链提示功能 */
require_once get_theme_file_path( '/self-innovate/pending-links.php' );

最终展示

Eucayptus-友链

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值