# 自动补全地址表单所在地区

在填写地址表单场景时，当应用使用了所在地区的省市区选择器，需要开发者对表单中的地址字段进行开发。

*   需要在module.json5文件中设置模糊位置权限：ohos.permission.APPROXIMATELY\_LOCATION。
    
*   所在地区地址选择器需要开通地图服务。
    
*   需要配置签名和指纹。
    

#### 效果图

地址表单中的所在地区能根据详细地址内容自动补全，当补全内容不符合预期时，可通过地址选择器进行修改。

#### 示例代码

```
import { util } from '@kit.ArkTS';
import { i18n } from '@kit.LocalizationKit';
import { sceneMap, site } from '@kit.MapKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { geoLocationManager } from '@kit.LocationKit';
import { abilityAccessCtrl, autoFillManager, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit';
import { FunctionalInput, functionalInputComponentManager } from '@kit.ScenarioFusionKit';
import { TextInputModifier } from '@kit.ArkUI';

const AUTHED = 0;
const TIME_OUT = 100;
// 默认经度和纬度。以下以北京天安门的经纬度为例。
const INIT_LAT = 39.5;
const INIT_LON = 116.2;
const ENGLISH = 'en';
const SIMPLIFIED_CHINESE = 'zh_CN';
const PERMISSIONS: Permissions[] = ['ohos.permission.APPROXIMATELY_LOCATION'];
const ADMINISTRATIVE_REGION: string[] =
  ['countryName', 'adminLevel1', 'adminLevel2', 'adminLevel3', 'adminLevel4'];

interface PersonInfo {
  name?: string;
  phone?: string;
  email?: string;
  idCard?: string;
  region?: string;
  streetAddress?: string;
}

interface RequestParam {
  requestTag: string;
  requestText: string;
}

interface Location {
  latitude: number;
  longitude: number;
}

// 显示授权弹出窗口。
async function reqPermissionsFromUser(permissions: Array<Permissions>,
  context: common.UIAbilityContext): Promise<PermissionRequestResult> {
  let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  return await atManager.requestPermissionsFromUser(context, permissions);
}

// 节流函数。
function debounce(func: () => void, wait: number = TIME_OUT): Function {
  let timeout: number | null = null;
  return () => {
    timeout && clearTimeout(timeout);
    timeout = setTimeout(() => {
      func();
      clearTimeout(timeout);
    }, wait);
  };
}

@Extend(Text)
function textStyle() {
  .width(64)
  .textAlign(TextAlign.End)
}

@Entry
@Component
struct Index {
  @State personInfo: PersonInfo = {};
  @State isClicked: boolean = false;
  // 用户是否已触发信息输入。
  private isUserInput: boolean = false;
  private location: Location = {
    latitude: INIT_LAT,
    longitude: INIT_LON,
  };
  private currentRequestTag: string = '';
  private handleAddressChange = (request: RequestParam) => {
    return debounce(async () => {
      this.autoCompleteAddress(request);
    });
  };

  aboutToAppear() {
    reqPermissionsFromUser(PERMISSIONS, this.getUIContext().getHostContext() as common.UIAbilityContext)
      .then((permissionRequestResult: PermissionRequestResult) => {
        if (permissionRequestResult.authResults[0] === AUTHED) {
          // 获取位置信息的API只能在授权的情况下被调用。
          geoLocationManager.getCurrentLocation((err, location: geoLocationManager.Location) => {
            if (err) {
              hilog.error(0x0000, 'testTag', `Failed to get location, code: ${err?.code}, message: ${err?.message}`);
              return;
            }
            hilog.info(0x0000, 'testTag', `Succeeded in obtaining the current location of the user`);
            this.location.latitude = location.latitude;
            this.location.longitude = location.longitude;
          })
        }
      })
      .catch((err: BusinessError) => {
        hilog.error(0x0000, 'testTag', `Failed request permissions, code: ${err?.code}, message: ${err?.message}`);
      })
  }

  public isUsLanguage(): boolean {
    let result: string = '';
    try {
      result = i18n.System.getSystemLanguage();
    } catch (error) {
      hilog.error(0x0000, 'testTag', 'Failed to get system language');
    }
    return result.toLowerCase() === 'en-latn-us';
  }

  async autoCompleteAddress(request: RequestParam): Promise<void> {
    try {
      let params: site.SearchByTextParams = {
        query: request.requestText,
        // 搜索结果需要偏向的经纬度。
        location: {
          latitude: this.location.latitude,
          longitude: this.location.longitude
        },
        language: this.isUsLanguage() ? ENGLISH : SIMPLIFIED_CHINESE,
        isChildren: true
      };
      const result = await site.searchByText(params);
      if (result.sites) {
        let region: string = '';
        let addressComponent = result.sites[0].addressComponent;
        // 遍历当前地址的行政区划层级。
        for (let item of ADMINISTRATIVE_REGION) {
          if (addressComponent[item] === undefined) {
            break;
          }
          region += addressComponent[item];
        }
        // 防止可能导致结果不一致的重复搜索。
        if (request.requestTag === this.currentRequestTag) {
          this.personInfo.region = region;
        }
      }
    } catch (error) {
      hilog.error(0x0000, 'testTag', `Failed to search location, code: ${error.code}, message: ${error.message}`);
    }
    hilog.info(0x0000, 'testTag', 'Succeeded in searching location');
  }

  onRegionClick(): void {
    // 用户选择行政区域后，仅显示该区域的搜索结果，以防止查询时间过长。
    this.currentRequestTag = util.generateRandomUUID();
    let districtSelectOptions: sceneMap.DistrictSelectOptions = {
      countryCode: 'CN',
    };
    sceneMap.selectDistrict(this.getUIContext().getHostContext(), districtSelectOptions).then((data) => {
      hilog.info(0x0000, 'testTag', 'SelectDistrict', 'Succeeded  in selecting district.');
      let region = '';
      for (let i = 0; i < data?.districts?.length; i++) {
        region += data.districts[i].name;
      }
      this.personInfo.region = region;
    }).catch((err: BusinessError) => {
      hilog.error(0x0000, 'testTag', `Failed to select district, code: ${err.code}, message: ${err.message}`);
    });
  }

  searchRegionByAddress(val: string): void {
    let tag: string = util.generateRandomUUID();
    this.currentRequestTag = tag;
    let param: RequestParam = {
      requestTag: tag,
      requestText: val
    }
    // 用户输入的场景需要加做抖动处理，智能填充回填场景直接查询即可。
    if (this.personInfo.region && this.personInfo.region !== '') {
      return;
    }
    if (this.isUserInput) {
      this.handleAddressChange(param)();
    } else {
      this.autoCompleteAddress(param);
    }
  }

  build() {
// ...
      Column({ space: 8 }) {
        Row({ space: 8 }) {
          Text('姓名').textStyle()
          TextInput({ text: this.personInfo.name, placeholder: '姓名' })
            .layoutWeight(1)
            .contentType(ContentType.PERSON_FULL_NAME)
            .onChange((val: string) => {
              this.personInfo.name = val;
            })
        }

        Row({ space: 8 }) {
          Text('联系电话').textStyle()
          TextInput({ text: this.personInfo.phone, placeholder: '手机号码' })
            .layoutWeight(1)
            .contentType(ContentType.PHONE_NUMBER)
            .onChange((val: string) => {
              this.personInfo.phone = val;
            })
        }

        Row({ space: 8 }) {
          Text('身份证号').textStyle()
          TextInput({ text: this.personInfo.idCard, placeholder: '身份证信息' })
            .layoutWeight(1)
            .contentType(ContentType.ID_CARD_NUMBER)
            .onChange((val: string) => {
              this.personInfo.idCard = val;
            })
        }

        Row({ space: 8 }) {
          Text('邮件地址').textStyle()
          TextInput({ text: this.personInfo.email, placeholder: '电子邮件信息' })
            .layoutWeight(1)
            .contentType(ContentType.EMAIL_ADDRESS)
            .onChange((val: string) => {
              this.personInfo.email = val;
            })
        }

        Row({ space: 8 }) {
          Text('所在地区').textStyle()
          FunctionalInput({
            params: {
              // InputType.SELECT_DISTRICT表示输入类型为省/市/区选择器类型。
              inputType: functionalInputComponentManager.InputType.SELECT_DISTRICT,
              textInputValue: {
                text: this.personInfo.region,
                placeholder: '省、市、区、街道地址',
              },
              // 调整TextInput样式。
              inputAttributeModifier: new TextInputModifier()
                .backgroundColor(Color.Transparent)
                .onChange((value) => {
                  if (value !== this.personInfo.region) {
                    this.personInfo.region = value;
                  }
                })
            },
            // 当InputType为SELECT_DISTRICT时，回调必须为onSelectDistrict。
            controller: new functionalInputComponentManager.FunctionalInputController().onSelectDistrict((err,
              data: functionalInputComponentManager.DistrictSelectResult) => {
              if (err) {
                // 错误日志处理。
                hilog.error(0x0000, 'testTag', 'Failed to select district, error: %{public}d %{public}s', err.code, err.message);
                return;
              }
              // 成功日志处理。
              hilog.info(0x0000, 'testTag', 'succeeded in selecting district');
              this.personInfo.region = data.inputContent;
            })
          })
        }

        Row({ space: 8 }) {
          Text('详细地址').textStyle()
          TextInput({ text: this.personInfo.streetAddress, placeholder: '小区门牌信息' })
            .layoutWeight(1)
            .contentType(ContentType.DETAIL_INFO_WITHOUT_STREET)
            .onDidInsert(() => {
              // 当用户通过输入方法输入数据时触发。
              this.isUserInput = true;
            })
            .onDidDelete((val: DeleteValue) => {
              // 当用户通过输入方法删除数据时触发。
              if (val?.deleteValue?.length > 0) {
                this.isUserInput = true;
              }
            })
            .onChange((val: string) => {
              this.personInfo.streetAddress = val;
              if (val && val.trim().length > 0) {
                this.searchRegionByAddress(val);
              } else {
                this.currentRequestTag = util.generateRandomUUID();
                this.personInfo.region = '';
              }
              this.isUserInput = false;
            })
        }

        Button('保存')
          .width('50%')
          .onClick(() => {
            if (!this.isClicked) {
              this.isClicked = true;
              autoFillManager.requestAutoSave(this.getUIContext(), {
                onSuccess: () => {
                  hilog.info(0x0000, 'testTag', 'Succeeded in saving request');
                },
                onFailure: () => {
                  hilog.info(0x0000, 'testTag', 'Failed to save request');
                }
              });
              setTimeout(() => {
                this.isClicked = false;
              }, 2000);
            }
          })
      }
      .padding({ left: 16, right: 16 })
      .backgroundColor($r('sys.color.ohos_id_color_list_card_bg'))
      .alignItems(HorizontalAlign.Center)
      .height('100%')
      .width('100%')
// ...
  }
}
```

该示例中，使用了场景化Input作为所在地区的地址选择器，智能填充支持对该地址选择器进行填充。