You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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;
};

最后要确认的几个细节

  1. 确保你的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() );
    } );
} );
  1. 测试REST接口是否正常工作:直接访问https://你的域名/wp-json/my-blocks/v1/terminals/LT,看是否返回正确的自提点数据。
  2. 把代码里的你的配送方式ID替换成你实际要监听的配送方式ID(比如flat_rate或者你自定义的ID)。

这样就能实现你想要的效果:用户切换配送国家时,自提点选项自动更新,且只在指定配送方式选中时显示。

备注:内容来源于stack exchange,提问作者markak

火山引擎 最新活动