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

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

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/a5/v3/_T9KRkyGTDqAKUMoaVlI8Q/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073645Z&HW-CC-Expire=86400&HW-CC-Sign=82ED67FFB996938346736D793170236045BD3E950DF395100E16CB43E4053D39)

*   需要在module.json5文件中设置模糊位置权限：[ohos.permission.APPROXIMATELY\_LOCATION](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/permissions-for-all-user#ohospermissionapproximately_location)。
    
*   所在地区地址选择器需要[开通地图服务](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/map-config-agc#开通地图服务)。
    
*   需要[配置签名和指纹](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/account-sign-fingerprints)。
    

#### 效果图

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

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/83/v3/cI9BGXpwStCcnTPZh4XXXw/zh-cn_image_0000002625188829.png?HW-CC-KV=V1&HW-CC-Date=20260612T073645Z&HW-CC-Expire=86400&HW-CC-Sign=2864C56B3BC0C5DB1335E9425773804535C5D392F6A78709EC66B0672D791DFC)

#### 示例代码

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: Array<Permissions> = \['ohos.permission.APPROXIMATELY\_LOCATION'\];
const ADMINISTRATIVE\_REGION: Array<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%')
  }
}

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/17/v3/Qm3v9ne4SfCEcsvExOO7vw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073645Z&HW-CC-Expire=86400&HW-CC-Sign=8B568B5B6A346E78DB75415283E754BBA5651FACC570207445D22C52D502CD3F)

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