WooCommerce区块结账页面:根据配送国家动态更新自定义自提点字段的实现问题
WooCommerce区块结账页面:根据配送国家动态更新自定义自提点字段的实现问题
嘿,我完全懂你现在卡在哪了——页面加载时能拿到对应国家的自提点,但用户中途改了配送国家,自提点选项就没法跟着动态更新,对吧?咱们一步步拆解解决这个问题。
首先得明确:你当前用get_script_data()传数据的方式是一次性初始化的,没法响应页面交互中的国家变化。要实现动态更新,得结合「前端监听国家变化」+「后端提供实时查询接口」这两部分来做。
第一步:后端新增REST接口,支持实时查询国家对应的自提点
咱们先在你的My_Blocks_Integration.php里加一个自定义REST路由,让前端能随时传国家代码,拿到最新的自提点列表:
<?php use \Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface; class My_Blocks_Integration implements IntegrationInterface { // ... 你已有的其他代码 ... // 注册钩子,添加REST路由 public function register_hooks() { add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) ); // 保留你原来的其他钩子 } // 注册自定义REST路由 public function register_rest_routes() { register_rest_route( 'my-blocks/v1', '/terminals/(?P<country>[A-Z]{2})', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_terminals_for_country' ), 'permission_callback' => function() { // 限制只有结账页面或管理员能访问,按需调整 return is_checkout() || current_user_can( 'manage_woocommerce' ); }, ) ); } // 根据国家代码返回格式化后的自提点列表 public function get_terminals_for_country( $request ) { $country = sanitize_text_field( $request['country'] ); $terminals = \Terminals::get_terminals_list( $country, 'terminal' ); if ( empty( $terminals ) || ! is_array( $terminals ) ) { return rest_ensure_response( array() ); } $prepared_terminals = array(); foreach ( $terminals as $terminal_key => $terminal_name ) { $prepared_terminals[] = array( 'label' => $terminal_name, 'value' => $terminal_key ); } return rest_ensure_response( $prepared_terminals ); } // ... 你已有的get_script_data()等代码 ... }
第二步:前端监听国家变化,动态拉取自提点
接下来修改block.js,咱们要监听结账页面里配送国家的变化,然后调用上面的接口更新选项。另外,你原来的options.js可以直接删掉了,因为现在是动态获取选项:
/** * External dependencies */ import { useEffect, useState, useCallback } from '@wordpress/element'; import { SelectControl } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { debounce } from 'lodash'; import apiFetch from '@wordpress/api-fetch'; // 新增:用于调用REST接口 /** * Internal dependencies */ import { txt } from './text'; export const Block = ({ checkoutExtensionData, extensions }) => { const { setExtensionData } = checkoutExtensionData; const debouncedSetExtensionData = useCallback( debounce((namespace, key, value) => { setExtensionData(namespace, key, value); }, 1000), [setExtensionData] ); const terminalValidationErrorId = 'terminal'; const { setValidationErrors, clearValidationError } = useDispatch('wc/store/validation'); const validationError = useSelect((select) => select('wc/store/validation').getValidationError(terminalValidationErrorId)); // 1. 获取当前配送国家(优先用配送地址,没有则用账单地址) const shippingCountry = useSelect( ( select ) => { const cartStore = select('wc/store/cart'); const shippingAddr = cartStore.getShippingAddress(); return shippingAddr.country || cartStore.getBillingAddress().country || 'LT'; }, [] ); // 2. 状态管理:选中的自提点、自提点选项列表 const [selectedTerminal, setSelectedTerminal] = useState(''); const [terminalOptions, setTerminalOptions] = useState( wcSettings["my-blocks_data"].terminals ); // 3. 监听国家变化,动态拉取自提点 useEffect( () => { const fetchTerminals = async () => { try { const terminals = await apiFetch({ path: `/my-blocks/v1/terminals/${shippingCountry}`, method: 'GET' }); setTerminalOptions( terminals ); // 如果当前选中的自提点不在新列表里,清空选择 if ( selectedTerminal && !terminals.some(opt => opt.value === selectedTerminal) ) { setSelectedTerminal(''); } } catch (error) { console.error('获取自提点失败:', error); setTerminalOptions( [] ); } }; fetchTerminals(); }, [ shippingCountry, selectedTerminal ] ); // 4. 处理自提点选择的逻辑(保留你原来的逻辑) useEffect(() => { setExtensionData('terminals', 'alternateShippingInstruction', selectedTerminal); if ( selectedTerminal !== '' ) { clearValidationError(terminalValidationErrorId); return; } setValidationErrors({ [terminalValidationErrorId]: { message: txt.error_terminal, hidden: false } }); }, [ setExtensionData, selectedTerminal, setValidationErrors, clearValidationError ] ); // 5. 可选:只在特定配送方式选中时显示自提点 const selectedShippingMethods = useSelect( ( select ) => select('wc/store/cart').getCartData().shippingRates || [] ); const isTargetShippingSelected = selectedShippingMethods.some(method => method.method_id === '你的配送方式ID'); // 替换成你要监听的配送方式ID return isTargetShippingSelected ? ( <div className="terminal_select_container"> <SelectControl label={txt.title_terminal} value={selectedTerminal} options={terminalOptions} onChange={setSelectedTerminal} disabled={terminalOptions.length === 0} /> {(validationError?.hidden || selectedTerminal !== '') ? null : ( <div className="wc-block-components-validation-error terminal-error"> <span>{validationError?.message}</span> </div> )} {terminalOptions.length === 0 && ( <p className="terminal-no-options">当前国家暂无可用自提点</p> )} </div> ) : null; };
最后要确认的几个细节
- 确保你的
My_Blocks_Integration已经正确注册到WooCommerce Blocks集成中,比如在插件主文件或主题functions.php里加:
add_action( 'woocommerce_blocks_loaded', function() { add_action( 'woocommerce_blocks_checkout_block_registration', function( $registry ) { $registry->register( new My_Blocks_Integration() ); } ); } );
- 测试REST接口是否正常工作:直接访问
https://你的域名/wp-json/my-blocks/v1/terminals/LT,看是否返回正确的自提点数据。 - 把代码里的
你的配送方式ID替换成你实际要监听的配送方式ID(比如flat_rate或者你自定义的ID)。
这样就能实现你想要的效果:用户切换配送国家时,自提点选项自动更新,且只在指定配送方式选中时显示。
备注:内容来源于stack exchange,提问作者markak




