一、需求背景
在使用 WordPress 搭建企业展示型网站时,产品详情页经常需要提供 PDF 产品手册、产品目录、技术参数表、安装说明书等资料供访客下载。
如果直接在产品详情页放置附件下载链接,访客无需填写任何信息即可获取资料,企业很难收集潜在客户的联系方式。
一种比较实用的方案是:用户点击下载按钮后,先填写 Elementor 原生表单,提交成功后自动下载当前产品对应的附件,并关闭弹窗。
本文介绍如何使用 Elementor、ACF(Advanced Custom Fields)以及少量 PHP 和 JavaScript 实现这一功能,无需额外安装专门的表单插件。
二、应用场景
这种方案适合以下网站类型:
- 工业制造企业网站: 下载产品目录、技术参数、产品说明书。
- 外贸企业网站: 下载产品宣传册、PDF 产品手册和技术文档。
- 机械设备网站: 下载设备规格书、操作手册和安装指南。
- 电子元器件网站: 下载产品 Datasheet、规格书和技术资料。
- 企业解决方案网站: 下载白皮书、行业报告和解决方案文档。
例如,一个工业设备网站有 100 个产品,每个产品都有不同的 PDF 技术资料。通过 ACF 为每个产品单独设置附件,就可以使用同一套 Elementor 弹窗和表单完成所有产品的资料下载,无需为每个产品单独创建下载表单。
三、技术方案与实现原理
本方案使用以下技术:
| 技术 | 主要用途 |
|---|---|
| WordPress | 网站基础框架 |
| Elementor Pro | 产品详情页、弹窗及原生表单 |
| ACF | 为每个产品配置独立附件 |
| PHP | 获取附件 URL、处理 AJAX 请求 |
| JavaScript / jQuery | 传递产品 ID、监听表单提交、触发下载及关闭弹窗 |
整体流程如下:
- 用户进入产品详情页。
- 点击 Download Leaflet 按钮,打开 Elementor 弹窗。
- JavaScript 获取当前产品 ID,并写入表单隐藏字段。
- 用户填写姓名、邮箱、联系方式等信息并提交表单。
- Elementor 完成表单验证和配置的提交操作。
- JavaScript 监听提交成功事件,向 WordPress AJAX 接口请求附件地址。
- PHP 根据产品 ID 读取 ACF 的
product_file字段。 - 浏览器触发文件下载。
- 下载操作触发后,自动关闭 Elementor 弹窗。
需要注意,Elementor 表单的提交成功事件与 AJAX 获取附件地址是两个不同的过程。只有表单提交成功后,才开始获取附件并触发下载。
四、第一步:使用 ACF 创建产品附件字段
首先,在 ACF 中创建一个文件字段。
建议设置如下:
| 配置项 | 设置值 |
|---|---|
| Field Label | Product File |
| Field Name | product_file |
| Field Type | File |
| Return Format | File URL |
| Location | 对应的产品文章类型 |
如果产品使用 WooCommerce,可以将字段关联到产品(Product);如果使用自定义文章类型,也可以关联到对应的 CPT。
设置完成后,进入产品编辑页面,为不同产品上传对应的 PDF 文件。
例如:
- 产品 A →
product-a.pdf - 产品 B →
product-b.pdf - 产品 C →
product-c.pdf
这样,同一套模板就能根据当前产品动态获取不同附件。
由于字段返回格式设置为 URL,PHP 可以直接使用:
$file_url = get_field( 'product_file', $product_id );
这里返回的是文件 URL 字符串,而不是包含 URL、标题和文件 ID 的数组。
五、第二步:创建 Elementor 弹窗
在 Elementor Pro 的弹窗功能中创建一个下载弹窗。
可以放置以下内容:
- 标题:Download Product Brochure
- 提示文字:Please submit the form below. Your download will start automatically after successful submission.
- Elementor 原生 Form 表单
表单字段可以根据业务需求配置:
- Name
- Phone / WhatsApp / WeChat
- Message
对于工业品和外贸网站,邮箱通常是比较重要的客户线索字段。可以根据实际情况设置必填规则。
配置表单提交操作
在 Actions After Submit 中保留需要的操作,例如:
- Email:发送客户信息通知。
- Collect Submissions:保存提交记录(如已启用)。
不要配置会提前跳转到其他页面的 Redirect 操作,否则可能影响后续下载流程。
六、第三步:给下载按钮和表单设置标识
为了让 JavaScript 准确找到目标按钮与表单,需要设置 CSS 类和 CSS ID。
1. 下载按钮
选中产品详情页中的 Download Leaflet 按钮,在 Advanced → CSS Classes 中填写:
hzt-download-trigger
如果原来已经有 CSS 类,只需要追加这个类名,不要删除原有类名。
2. Elementor 表单
选中弹窗中的 Form 组件,在 Advanced → CSS ID 中填写:
hzt-download-form
注意,这是表单组件的 CSS ID,不是表单字段 ID。
3. 添加隐藏字段
在 Elementor Form 中新增一个 Hidden 字段:
| 配置项 | 设置值 |
|---|---|
| Type | Hidden |
| Label | Product ID |
| ID | product_id |
| Default Value | 留空 |
隐藏字段用于记录当前产品 ID。访客无需手动填写,JavaScript 会在打开弹窗时自动设置其值。
七、第四步:编写 PHP 后端代码
将以下代码添加到子主题的 functions.php,或放入自己的功能插件中。
如果这项功能需要在多个企业主题中复用,建议放入独立插件或主题核心插件,避免后续切换主题时丢失功能。
1. 注册脚本并传递当前产品 ID
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_singular() ) {
return;
}
wp_register_script(
'hzt-product-download',
false,
array( 'jquery' ),
'1.0.0',
true
);
wp_enqueue_script( 'hzt-product-download' );
wp_localize_script(
'hzt-product-download',
'hztProductDownload',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'hzt_product_download' ),
'productId' => get_queried_object_id(),
)
);
wp_add_inline_script(
'hzt-product-download',
hzt_product_download_script()
);
} );
这段代码完成以下工作:
- 注册前端 JavaScript。
- 获取 WordPress AJAX 接口地址。
- 生成用于请求校验的 nonce。
- 将当前查询对象 ID 传递给前端。
2. 创建 AJAX 接口
add_action(
'wp_ajax_hzt_get_product_download_url',
'hzt_get_product_download_url'
);
add_action(
'wp_ajax_nopriv_hzt_get_product_download_url',
'hzt_get_product_download_url'
);
function hzt_get_product_download_url() {
check_ajax_referer(
'hzt_product_download',
'nonce'
);
$product_id = isset( $_POST['product_id'] )
? absint( $_POST['product_id'] )
: 0;
if ( ! $product_id ) {
wp_send_json_error(
array( 'message' => 'Invalid product.' ),
400
);
}
$product = get_post( $product_id );
if (
! $product ||
'publish' !== $product->post_status
) {
wp_send_json_error(
array( 'message' => 'Product not found.' ),
404
);
}
if ( ! function_exists( 'get_field' ) ) {
wp_send_json_error(
array( 'message' => 'ACF is unavailable.' ),
500
);
}
$file_url = get_field(
'product_file',
$product_id
);
if (
! is_string( $file_url ) ||
'' === $file_url
) {
wp_send_json_error(
array(
'message' => 'No download file is configured.'
),
404
);
}
$file_url = esc_url_raw( $file_url );
$file_host = wp_parse_url(
$file_url,
PHP_URL_HOST
);
$site_host = wp_parse_url(
home_url(),
PHP_URL_HOST
);
$file_scheme = wp_parse_url(
$file_url,
PHP_URL_SCHEME
);
if (
! $file_host ||
! $site_host ||
strtolower( $file_host ) !== strtolower( $site_host ) ||
! in_array(
$file_scheme,
array( 'http', 'https' ),
true
)
) {
wp_send_json_error(
array(
'message' => 'Invalid download file URL.'
),
400
);
}
$filename = wp_basename(
(string) wp_parse_url(
$file_url,
PHP_URL_PATH
)
);
wp_send_json_success(
array(
'url' => $file_url,
'filename' => $filename,
)
);
}
这里主要有三个关键点。
第一,获取当前产品的附件。
$file_url = get_field( 'product_file', $product_id );
根据传入的产品 ID,读取该产品对应的 ACF 文件字段。
第二,处理没有附件的情况。
当产品没有设置文件时,接口返回明确的错误信息:
No download file is configured.
前端可以据此显示友好的英文提示,而不是直接触发无效下载。
第三,检查附件 URL。
示例代码限制附件地址为本站同域名的 HTTP 或 HTTPS URL,避免接口被用于返回任意外部链接。
这只是基础校验。如果网站存在多个合法媒体域名或使用 CDN,需要根据实际部署调整域名白名单。
八、第五步:编写 JavaScript,实现表单提交后自动下载
下面的代码负责三个主要功能:
- 点击下载按钮时写入产品 ID。
- 表单提交成功后请求附件地址。
- 下载触发后关闭 Elementor 弹窗。
将此函数与前面的 PHP 注册代码配合使用。
function hzt_product_download_script() {
return <<<'JS'
jQuery(function ($) {
/**
* 点击下载按钮,写入当前产品 ID
*/
$(document).on(
'click',
'.hzt-download-trigger',
function () {
var productId =
hztProductDownload.productId;
var $form = $(
'#hzt-download-form'
).find('form');
var $field = $form.find(
'input[name="form_fields[product_id]"]'
);
if ($field.length) {
$field
.val(productId)
.trigger('input')
.trigger('change');
}
}
);
/**
* 表单提交成功后获取附件
*/
$(document).on(
'submit_success',
function (event) {
var $form = $(
'#hzt-download-form'
).find('form');
if (!$form.length) {
return;
}
if (
event.target &&
event.target.tagName === 'FORM' &&
!$(event.target).is($form)
) {
return;
}
var productId = $form.find(
'input[name="form_fields[product_id]"]'
).val();
if (!productId) {
productId =
hztProductDownload.productId;
}
$.ajax({
url: hztProductDownload.ajaxUrl,
type: 'POST',
dataType: 'json',
data: {
action:
'hzt_get_product_download_url',
nonce:
hztProductDownload.nonce,
product_id: productId
}
}).done(function (response) {
if (
!response.success ||
!response.data ||
!response.data.url
) {
var message =
response.data &&
response.data.message ===
'No download file is configured.'
? 'No attachment is currently available for this product. Please contact us to obtain the file manually or try again later.'
: (
response.data &&
response.data.message
? response.data.message
: 'Unable to retrieve the file. Please try again.'
);
alert(message);
return;
}
/**
* 触发浏览器下载
*/
var link =
document.createElement('a');
link.href = response.data.url;
link.download =
response.data.filename || '';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
link.remove();
/**
* 开始触发下载后关闭弹窗
*/
setTimeout(function () {
var $popup = $form.closest(
'.elementor-popup-modal'
);
if (
$popup.length &&
window.elementorProFrontend
) {
var popupId =
$popup.attr(
'data-elementor-id'
);
if (popupId) {
elementorProFrontend
.modules
.popup
.closePopup({
id: popupId
});
}
}
}, 500);
}).fail(function () {
alert(
'Unable to retrieve the file. Please try again.'
);
});
}
);
});
JS;
}
为什么要监听 submit_success?
Elementor 原生表单有自己的前端验证和提交机制。直接监听按钮点击并下载,可能导致用户还没有完成提交,就已经获取附件。
通过 submit_success 事件,可以将下载流程安排在表单成功提交之后。
此外,附件请求使用独立的 WordPress AJAX 接口,不需要让表单提交本身承担文件下载任务,两者职责更清晰。
为什么要延迟关闭弹窗?
在触发 link.click() 后延迟约 500 毫秒关闭弹窗,可以避免弹窗在触发下载之前就关闭。
但需要明确:浏览器的下载触发并不等于文件已经完整下载。此处关闭弹窗只是让用户体验更流畅,并不代表下载完成。
九、第六步:设置表单提示文字
为了让访客清楚了解操作流程,可以在 Elementor Form 上方添加 Text Editor 组件。
推荐英文提示:
Please submit the form below. Your download will start automatically after successful submission.
中文含义:请填写并提交以下表单,提交成功后将自动开始下载。
如果想强调这是产品手册下载,也可以使用:
Please submit the form to download the leaflet. The download will start automatically.
第一种表达更加通用,适合产品手册、技术资料和 PDF 文件等不同下载场景。
十、第七步:测试没有附件时的提示
在实际网站中,并非所有产品都一定有可供下载的文件。
如果 ACF 字段 product_file 为空,后端接口会返回:
No download file is configured.
前端将它转换为用户友好的提示:
No attachment is currently available for this product. Please contact us to obtain the file manually or try again later.
这样既能告知用户当前无法下载,也为潜在客户提供了后续联系渠道。
其他网络错误或接口异常则继续显示通用提示:
Unable to retrieve the file. Please try again.
这两种提示需要区分处理,避免将所有异常都误认为产品没有附件。
十一、第八步:清理缓存并进行完整测试
如果网站使用 WP Rocket、服务器页面缓存或 CDN,修改代码后应清理相关缓存。
建议按以下清单逐项测试:
- 点击 Download Leaflet,能够正常打开 Elementor 弹窗。
- 表单必填字段验证正常。
- 未完成必填字段时,不能进入下载流程。
- 提交成功后,能够下载当前产品对应的附件。
- 不同产品能够下载各自配置的文件,而不是同一个固定文件。
- 成功触发下载后,弹窗能够自动关闭。
- 没有设置附件的产品能够显示正确的英文提示。
- 表单邮件通知和提交记录功能正常。
- 桌面端与移动端都能够正常操作。
建议至少选择两个附件不同的产品进行测试,再测试一个没有附件的产品。
十二、常见问题
1. 所有产品都下载同一个文件怎么办?
首先检查隐藏字段是否正确传入当前产品 ID。
确认 Elementor 表单中的隐藏字段 ID 是 product_id,下载按钮包含 hzt-download-trigger,表单组件的 CSS ID 为 hzt-download-form。
同时确认产品详情页中的查询对象 ID 与实际产品 ID 一致。对于标准 WordPress 单篇页面和产品详情页,get_queried_object_id() 通常可以正确获取当前对象。
2. 表单提交成功,但没有开始下载怎么办?
可以检查以下项目:
- 浏览器控制台是否存在 JavaScript 错误。
- 网络请求中的
admin-ajax.php是否成功返回 JSON。 - ACF 字段是否已经保存文件 URL。
- 当前页面是否正确加载
hztProductDownload配置。 - Elementor Pro 是否正常触发表单的
submit_success事件。 - 浏览器是否因为跨域或文件响应方式而忽略下载属性。
3. 为什么某些 PDF 文件会直接在浏览器中打开?
浏览器对 PDF 等文件的处理取决于响应头、同源策略和服务器配置。
对于本站同域名的文件,a.download 通常可以提示浏览器下载;但它不是强制下载指令。
如果业务要求始终下载文件,可以进一步改为 PHP 受控下载接口,通过 Content-Disposition: attachment 和 Content-Type 等响应头返回文件。
4. 这种方式能否防止访客绕过表单直接下载?
不能。
当前方案通过表单提交流程引导用户下载,但 ACF 返回的媒体库 URL 仍然是公开地址。知道该地址的人可能直接访问文件。
如果附件涉及内部资料、付费文档或必须收集授权信息后才能访问的文件,应将附件存储在非公开目录,通过 PHP 校验访问权限后再输出文件内容。
5. 是否需要为每个产品单独制作弹窗?
通常不需要。
只要产品详情页使用统一模板,按钮和弹窗的 CSS 标识一致,且每个产品都配置了正确的 ACF 文件字段,就可以让多个产品复用同一套下载流程。
十三、总结
通过 Elementor、ACF、PHP 和 JavaScript,可以在 WordPress 企业网站中构建完整的产品资料下载流程。
这种方案的核心不是简单地给按钮添加附件链接,而是将产品数据、表单提交和文件下载三个环节连接起来:
- ACF 负责维护每个产品对应的附件。
- Elementor 负责产品页面、弹窗和客户信息收集。
- PHP 负责读取附件、验证请求并返回下载地址。
- JavaScript 负责产品 ID 传递、提交成功后的下载触发和弹窗关闭。
对于产品数量较多的工业企业网站或外贸网站,这种实现方式便于复用、维护和扩展。后续还可以进一步增加下载次数统计、客户线索关联、附件权限校验以及不同语言的提示信息。
需要注意的是,前端触发下载并不意味着文件已经完整下载,公开媒体库 URL 也不构成真正的访问权限控制。若涉及敏感资料,应使用受控下载接口,而不是直接向浏览器公开文件地址。
本文由 好主题 原创整理,致力于分享实用的 WordPress 建站知识与主题开发经验。 我们专注于提供高质量的 WordPress企业主题 资源,帮助中小企业轻松构建专业网站。 转载请注明来源,并保留原文链接,感谢您的支持与理解。






