# PhoneInput

`PhoneInput` 组件提供了一个用户友好的国际电话号码输入字段。它与 `react-hook-form` 无缝集成，并包含一个带有国旗和拨号代码的可搜索国家选择器、自动号码格式化以及由 `google-libphonenumber` 提供支持的强大验证功能。

该组件旨在与其他表单元素（例如 [`CountrySelect`](./components-ui-form-elements-country-select.md) 或 [`AddressForm`](./components-ui-form-elements-address-form.md)）协同工作，通过与共享的表单字段同步其所选国家来实现。

## 工作原理

该组件利用 `react-international-phone` 库实现其核心功能，并对其进行包装，以提供与 `react-hook-form` 和 Material-UI 的深度集成。当用户选择一个国家或输入一个号码时，该组件会更新表单状态中相应的字段。当另一个组件修改表单中链接的国家字段时，它也会自动更新其国家选择。

<!-- DIAGRAM_IMAGE_START:architecture:16:9::1764919317 -->
![PhoneInput](assets/diagram/phone-input-diagram-0.zh.jpg)
<!-- DIAGRAM_IMAGE_END -->

## Props

`PhoneInput` 组件接受所有标准的 Material-UI `TextField` props，以及以下特定 props：

| Prop | 类型 | 必需 | 默认值 | 描述 |
|---|---|---|---|---|
| `name` | `string` | 是 | - | 用于在 `react-hook-form` 中注册电话号码的字段名称。 |
| `countryFieldName` | `string` | 否 | `'billing_address.country'` | 存储所选国家 ISO2 代码的表单字段的名称。这使得电话输入的国家可以与表单的其他部分（例如地址组件）同步。 |

## 用法

要使用 `PhoneInput` 组件，您必须将其包装在 `react-hook-form` 的 `FormProvider` 中。以下示例演示了带验证的基本实现。

首先，您需要库提供的异步验证函数。

```javascript phone-validator.js icon=logos:javascript
// src/libs/phone-validator.js
import { getPhoneUtil } from '@blocklet/payment-react/libs/phone-validator';

export const validatePhoneNumber = async (phoneNumber) => {
  if (!phoneNumber) return true;
  try {
    const util = await getPhoneUtil();
    const parsed = util.parseAndKeepRawInput(phoneNumber);
    return util.isValidNumber(parsed);
  } catch (err) {
    console.error('电话验证错误:', err);
    // 如果库加载失败，则回退到简单的正则表达式
    const pattern = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/im;
    return pattern.test(phoneNumber) || '无效的电话号码';
  }
};
```

现在，您可以在表单组件中使用此验证器。

```jsx MyPaymentForm.tsx icon=logos:react
import { FormProvider, useForm } from 'react-hook-form';
import { Button, Box } from '@mui/material';
import { PhoneInput } from '@blocklet/payment-react';
import { validatePhoneNumber } from '../libs/phone-validator'; // 根据需要调整路径

export default function MyPaymentForm() {
  const methods = useForm({
    mode: 'onBlur',
    defaultValues: {
      phone: '',
      'billing_address.country': 'us', // 默认国家
    },
  });

  const onSubmit = (data) => {
    alert(JSON.stringify(data, null, 2));
  };

  return (
    <FormProvider {...methods}>
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        <Box display="flex" flexDirection="column" gap={2}>
          <PhoneInput
            label="Phone Number"
            name="phone"
            // 链接到表单状态中的国家字段
            countryFieldName="billing_address.country"
            // 添加验证规则
            rules={{
              validate: async (value) => {
                const isValid = await validatePhoneNumber(value);
                return isValid || '请输入有效的电话号码。';
              },
            }}
            fullWidth
          />
          <Button type="submit" variant="contained">
            Submit
          </Button>
        </Box>
      </form>
    </FormProvider>
  );
}
```

### 说明

1.  **`FormProvider`**：整个表单（包括 `PhoneInput`）都包装在 `FormProvider` 中，以提供必要的表单上下文。
2.  **`name="phone"`**：这会将输入字段以 `phone` 的名称注册到表单数据中。
3.  **`countryFieldName="billing_address.country"`**：这告诉 `PhoneInput` 从表单状态的 `billing_address.country` 字段读取数据并向其写入数据。这就是它保持同步的方式。在 `useForm` 中，此字段的默认值设置为 `'us'`。
4.  **`rules`**：我们将一个异步的 `validate` 函数传递给 `rules` prop。`react-hook-form` 将在验证期间等待此函数解析。调用 `validatePhoneNumber` 实用程序来执行检查。

## 与 AddressForm 集成

当 `PhoneInput` 与 `AddressForm` 一起使用时，其真正的强大之处就显现出来了。由于两个组件都可以链接到同一个国家字段（默认为 `billing_address.country`），因此在 `AddressForm` 中更改国家将自动更新 `PhoneInput` 中的国旗和拨号代码。

有关此集成的完整示例，请参阅 [`AddressForm`](./components-ui-form-elements-address-form.md) 的文档。