本文约 13,220 字,预计阅读 34 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:案例是按行业找的,不是按时间翻的
一个做自动化集成的公司,官网案例页有 30 多个项目,按发布时间倒序排列,每页 10 条。销售反馈最多的一句是:「客户是做医药的,我得自己在页面上找医药相关的案例发给他,每次都要翻半天。」
问题的本质是分类维度错位。访客(以及帮他找资料的销售)是按「行业」找案例的,而不是按「什么时候做的」。行业内还关心规模(产值多少、几条产线)和成效(提升多少百分比)。按时间排的列表把这三个维度全部埋掉了。
案例模块在企业站里的作用非常明确:它是采购决策链上的「社会证明」环节。访客已经确认你有这类产品,接下来要看的是「有没有和我相似的人用过、效果如何」。所以案例模块的三个核心能力是:能按行业筛出来、详情页讲得清背景与成效、并且能反向链接回所用产品形成闭环。
原理分析:筛选该用链接还是 AJAX
这是案例模块第一个技术决策,影响上线后的收录与流量。
| 方案 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| 分类归档链接 | 可被收录、可分享、无 JS 依赖 | 每次筛选刷新页面 | 绝大多数企业站 |
| 纯 AJAX 筛选 | 体验流畅 | URL 不变、不被收录、需维护 JS | 数量少且无需 SEO |
| 链接 + AJAX 增强 | 兼顾 | 实现复杂度高 | 有开发资源 |
企业站一律推荐第一种。用分类法归档页承载筛选结果,URL 形如 /cases/industry/pharmaceutical/,每个行业组合都是一个可被搜索引擎收录的页面。这类页面带来的是「医药 自动化 案例」这样的长尾词,转化质量通常很高。
第二个决策是案例与产品的关联方式。两种做法:一是用共享分类法(产品和案例共用同一个行业分类法),二是用自定义字段存关联的产品 ID。前者简单但粗糙,后者精确。推荐后者——一个案例用了三个不同产品,靠共享分类无法表达这种精确到产品的关联。
第三个决策是图集怎么做。用自定义字段存附件 ID 数组最简单可控;直接把图片插入正文则编辑体验好但结构与展示分离困难。折中方案:正文写方案描述,图集单独用字段管理,模板里分区渲染。
保姆级实操
步骤 1:注册案例类型与行业分类
<?php
add_action( 'init', 'mmf_register_case' );
function mmf_register_case() {
register_post_type( 'case', array(
'labels' => array(
'name' => '客户案例',
'singular_name' => '案例',
'add_new_item' => '添加案例',
),
'public' => true,
'has_archive' => 'cases',
'rewrite' => array( 'slug' => 'cases', 'with_front' => false ),
'menu_icon' => 'dashicons-awards',
'menu_position' => 22,
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'show_in_rest' => true,
) );
register_taxonomy( 'case_industry', 'case', array(
'labels' => array( 'name' => '行业', 'singular_name' => '行业' ),
'public' => true,
'hierarchical' => true,
'rewrite' => array( 'slug' => 'cases/industry', 'with_front' => false ),
'show_in_rest' => true,
'show_admin_column' => true,
) );
// 案例规模标签,用于细分筛选
register_taxonomy( 'case_scale', 'case', array(
'labels' => array( 'name' => '项目规模' ),
'public' => true,
'hierarchical' => false,
'rewrite' => array( 'slug' => 'cases/scale' ),
'show_in_rest' => true,
) );
}
步骤 2:案例归档页,用链接筛选
<?php
// archive-case.php
get_header();
$industries = get_terms( array(
'taxonomy' => 'case_industry',
'hide_empty' => true,
'parent' => 0,
) );
$scales = get_terms( array(
'taxonomy' => 'case_scale',
'hide_empty' => true,
) );
$current = is_tax() ? get_queried_object() : null;
?>
<main class="archive-case">
<h1><?php post_type_archive_title(); ?></h1>
<div class="case-filters">
<div class="filter-row">
<span class="filter-label">行业:</span>
<a class="<?php echo is_post_type_archive( 'case' ) ? 'is-current' : ''; ?>"
href="<?php echo esc_url( get_post_type_archive_link( 'case' ) ); ?>">全部</a>
<?php foreach ( $industries as $t ) : ?>
<a class="<?php echo ( $current && $current->term_id === $t->term_id ) ? 'is-current' : ''; ?>"
href="<?php echo esc_url( get_term_link( $t ) ); ?>">
<?php echo esc_html( $t->name ); ?>
<span class="count"><?php echo (int) $t->count; ?></span>
</a>
<?php endforeach; ?>
</div>
<div class="filter-row">
<span class="filter-label">规模:</span>
<?php foreach ( $scales as $t ) : ?>
<a href="<?php echo esc_url( get_term_link( $t ) ); ?>"><?php echo esc_html( $t->name ); ?></a>
<?php endforeach; ?>
</div>
</div>
<div class="case-grid">
<?php if ( have_posts() ) : while ( have_posts() ) : the_post();
$result = get_post_meta( get_the_ID(), '_case_result', true );
?>
<article class="case-card">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail( 'mmf-card', array( 'loading' => 'lazy' ) ); ?>
<h2><?php the_title(); ?></h2>
</a>
<?php
$ind = get_the_terms( get_the_ID(), 'case_industry' );
if ( $ind && ! is_wp_error( $ind ) ) {
echo '<p class="case-meta">' . esc_html( $ind[0]->name ) . '</p>';
}
if ( $result ) {
echo '<p class="case-result">' . esc_html( $result ) . '</p>';
}
?>
</article>
<?php endwhile; else : ?>
<p>该分类下暂无案例。</p>
<?php endif; ?>
</div>
<?php the_posts_pagination(); ?>
</main>
<?php get_footer();
「规模」那一行如果也想做成独立可收录的组合页,需要注册组合 rewrite 规则;简单场景下让它跳转到单分类归档即可,不要追求全组合(组合爆炸会产生大量内容稀薄页)。
步骤 3:案例详情页的四个板块
<?php
// single-case.php 主体
while ( have_posts() ) : the_post();
$gallery = get_post_meta( get_the_ID(), '_case_gallery', true );
$gallery = is_array( $gallery ) ? array_filter( $gallery, 'is_numeric' ) : array();
$products = get_post_meta( get_the_ID(), '_case_products', true );
$products = is_array( $products ) ? array_filter( $products, 'is_numeric' ) : array();
?>
<article <?php post_class( 'single-case' ); ?>>
<h1><?php the_title(); ?></h1>
<dl class="case-facts">
<?php
$ind = get_the_terms( get_the_ID(), 'case_industry' );
if ( $ind && ! is_wp_error( $ind ) ) : ?>
<dt>行业</dt><dd><?php echo esc_html( $ind[0]->name ); ?></dd>
<?php endif; ?>
<?php $scale = get_the_terms( get_the_ID(), 'case_scale' );
if ( $scale && ! is_wp_error( $scale ) ) : ?>
<dt>规模</dt><dd><?php echo esc_html( $scale[0]->name ); ?></dd>
<?php endif; ?>
</dl>
<section class="case-body">
<h2>项目背景与方案</h2>
<?php the_content(); ?>
</section>
<?php if ( $gallery ) : ?>
<section class="case-gallery">
<h2>现场图片</h2>
<div class="gallery-grid">
<?php foreach ( $gallery as $att_id ) {
echo wp_get_attachment_image(
(int) $att_id,
'mmf-card',
false,
array( 'loading' => 'lazy', 'class' => 'case-img' )
);
} ?>
</div>
</section>
<?php endif; ?>
<?php if ( $products ) : ?>
<section class="case-products">
<h2>本案例使用的产品</h2>
<ul>
<?php foreach ( $products as $pid ) :
$p = get_post( (int) $pid );
if ( ! $p || 'product' !== $p->post_type ) { continue; } ?>
<li>
<a href="<?php echo esc_url( get_permalink( $p ) ); ?>">
<?php echo esc_html( $p->post_title ); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</section>
<?php endif; ?>
</article>
<?php endwhile;
wp_get_attachment_image() 会自动输出 srcset 与 sizes,比手写 <img> 好得多,响应式和懒加载都能白拿。
步骤 4:相关案例推荐(同行业)
<?php
$related = new WP_Query( array(
'post_type' => 'case',
'posts_per_page' => 3,
'post__not_in' => array( get_the_ID() ),
'no_found_rows' => true,
'tax_query' => array(
array(
'taxonomy' => 'case_industry',
'field' => 'term_id',
'terms' => $ind && ! is_wp_error( $ind ) ? $ind[0]->term_id : 0,
),
),
) );
if ( $related->have_posts() ) : ?>
<aside class="related-cases">
<h2>同行业案例</h2>
<ul>
<?php while ( $related->have_posts() ) : $related->the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endwhile; wp_reset_postdata(); ?>
</ul>
</aside>
<?php endif; ?>
no_found_rows => true 可以跳过总数统计,省一次 SQL,在不需要分页的场景下必加。
步骤 5:图集与关联产品的元框
<?php
add_action( 'add_meta_boxes', function() {
add_meta_box( 'mmf-case-extra', '案例附加信息', 'mmf_case_box', 'case', 'normal', 'default' );
} );
function mmf_case_box( $post ) {
wp_nonce_field( 'mmf_save_case', 'mmf_case_nonce' );
$products = get_post_meta( $post->ID, '_case_products', true );
$products = is_array( $products ) ? array_map( 'intval', $products ) : array();
$all = get_posts( array(
'post_type' => 'product',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
'fields' => 'ids',
) );
?>
<p>
<label for="mmf_case_result">成效一句话(如:产能提升 32%,减少 4 名操作工)</label><br>
<input type="text" id="mmf_case_result" name="mmf_case_result"
value="<?php echo esc_attr( get_post_meta( $post->ID, '_case_result', true ) ); ?>"
style="width:100%">
</p>
<p><strong>关联产品(可多选)</strong></p>
<select name="mmf_case_products[]" multiple size="8" style="width:100%">
<?php foreach ( $all as $pid ) : ?>
<option value="<?php echo (int) $pid; ?>" <?php selected( in_array( (int) $pid, $products, true ) ); ?>>
<?php echo esc_html( get_the_title( $pid ) ); ?>
</option>
<?php endforeach; ?>
</select>
<?php
}
add_action( 'save_post_case', 'mmf_save_case' );
function mmf_save_case( $post_id ) {
if ( ! isset( $_POST['mmf_case_nonce'] )
|| ! wp_verify_nonce( sanitize_key( $_POST['mmf_case_nonce'] ), 'mmf_save_case' ) ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( isset( $_POST['mmf_case_result'] ) ) {
update_post_meta(
$post_id,
'_case_result',
sanitize_text_field( wp_unslash( $_POST['mmf_case_result'] ) )
);
}
$ids = isset( $_POST['mmf_case_products'] )
? array_map( 'intval', (array) $_POST['mmf_case_products'] )
: array();
update_post_meta( $post_id, '_case_products', $ids );
}
图集字段可以用 WordPress 媒体上传器(3.5 之后有 wp.media),也可以让编辑直接把图片插入正文后用 get_attached_media( 'image', $post_id ) 取出来渲染,后者省掉一套 JS。
步骤 6:给案例页加结构化数据
<?php
add_action( 'wp_head', 'mmf_case_schema' );
function mmf_case_schema() {
if ( ! is_singular( 'case' ) ) {
return;
}
$data = array(
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => wp_strip_all_tags( get_the_title() ),
'datePublished' => get_the_date( 'c' ),
);
if ( has_post_thumbnail() ) {
$data['image'] = get_the_post_thumbnail_url( null, 'full' );
}
echo '<script type="application/ld+json">'
. wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
. '</script>';
}
常见踩坑
- 筛选用 AJAX 且 URL 不变:行业页无法被收录,等于白做。
- 所有行业组合都生成页面:组合爆炸后产生大量内容稀薄的页面,反而拉低整站质量评分。只做单维度归档。
- 案例正文里没有成效数字:只有「提升了效率」这类定性描述,说服力极弱。必须量化。
- 忘了
wp_reset_postdata():相关案例循环后,后续的the_title()会取到错误的文章。 - 案例: 某站点用共享分类法关联产品与案例(都用
case_industry),上线后发现「医药」分类下既有产品又有案例,归档页混在一起,产品页和案例页的 URL 也互相冲突。后来改成:案例独立用case_industry,产品关联改用自定义字段存产品 ID。教训:分类法负责「分类」,跨类型关系用字段表达,不要混用。 - 图集用
<img>手写且没有 srcset:移动端下载原图,页面体积几十 MB。一律用wp_get_attachment_image()。 - 案例图没写 alt:对无障碍与图片搜索都不友好。用
wp_get_attachment_image()时会自动取附件的 alt,前提是上传时填了。
总结
案例模块的三要素:按行业(而非时间)分类、筛选走可收录的分类归档链接、详情页包含背景—方案—成效—关联产品四段,并通过相关案例形成同行业内链。
验收清单:每个行业分类页有独立 URL 且收录正常;详情页首图 eager、图集 lazy;关联产品链接可点击且有效;相关案例至少 2 条;所有输出点做了转义。
产品与案例互链构成站内权重主干,产品侧的做法见 企业站产品中心CPT搭建教程;模块上线后用 WordPress主题性能度量方法 跑一次归档页与详情页,确认图集没有把 LCP 拖垮。
