10. 数字编码:身份证的秘密

知识点:数字编码的意义(教材中的实践活动)。
功能:输入身份证号码,系统解析并高亮显示地址码、出生日期码、顺序码,让学生理解数字编码的规范性。
在这里插入图片描述

// IdCodeDecoder.ets
// IdCodeDecoder.ets

interface IdCardInfo {
  addressCode: string;
  addressInfo: string;
  birthYear: string;
  birthMonth: string;
  birthDay: string;
  orderCode: string;
  gender: string;
  checkCode: string;
  age: number;
  isValid: boolean;
  validationMessage: string;
}

@Entry
@Component
struct IdCodeDecoder {
  @State idInput: string = '110101201203020066'
  @State analysis: string[] = []
  @State idCardInfo: IdCardInfo | null = null
  @State showHelp: boolean = false

  private provinceCodes = new Map<string, string>([
    ['11', '北京市'], ['12', '天津市'], ['13', '河北省'], ['14', '山西省'], ['15', '内蒙古自治区'],
    ['21', '辽宁省'], ['22', '吉林省'], ['23', '黑龙江省'], ['31', '上海市'], ['32', '江苏省'],
    ['33', '浙江省'], ['34', '安徽省'], ['35', '福建省'], ['36', '江西省'], ['37', '山东省'],
    ['41', '河南省'], ['42', '湖北省'], ['43', '湖南省'], ['44', '广东省'], ['45', '广西壮族自治区'],
    ['46', '海南省'], ['50', '重庆市'], ['51', '四川省'], ['52', '贵州省'], ['53', '云南省'],
    ['54', '西藏自治区'], ['61', '陕西省'], ['62', '甘肃省'], ['63', '青海省'], ['64', '宁夏回族自治区'],
    ['65', '新疆维吾尔自治区'], ['71', '台湾省'], ['81', '香港特别行政区'], ['82', '澳门特别行政区']
  ])

  build() {
    Scroll() {
      Column({ space: 20 }) {
        // 标题栏
        Row() {
          Text('🆔 数字编码:身份证的秘密')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#2C3E50')
          Blank()
          Button('帮助')
            .fontSize(14)
            .width(60)
            .height(35)
            .backgroundColor('#95A5A6')
            .onClick(() => {
              this.showHelp = !this.showHelp;
            })
        }
        .width('95%')
        .padding(15)
        .backgroundColor('#ECF0F1')
        .borderRadius(15)

        // 帮助信息
        if (this.showHelp) {
          Column({ space: 10 }) {
            Text('💡 身份证号码结构说明')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .fontColor('#2C3E50')
              .width('100%')
              .textAlign(TextAlign.Start)

            Text('1. 前6位:地址码(省、市、区)')
              .fontSize(16)
              .fontColor('#34495E')

            Text('2. 第7-14位:出生日期码(年、月、日)')
              .fontSize(16)
              .fontColor('#34495E')

            Text('3. 第15-17位:顺序码(奇数为男,偶数为女)')
              .fontSize(16)
              .fontColor('#34495E')

            Text('4. 第18位:校验码(0-9或X)')
              .fontSize(16)
              .fontColor('#34495E')
          }
          .padding(15)
          .backgroundColor('#FFF9E6')
          .borderRadius(10)
          .width('95%')
        }

        // 输入区域
        Column({ space: 15 }) {
          Text('📝 请输入身份证号码')
            .fontSize(20)
            .fontColor('#2C3E50')
            .fontWeight(FontWeight.Bold)
            .width('100%')
            .textAlign(TextAlign.Start)

          Row() {
            TextInput({
              placeholder: '请输入18位身份证号',
              text: this.idInput
            })
              .maxLength(18)
              .type(InputType.Number)
              .width('100%')
              .height(50)
              .border({ width: 2, color: '#3498DB' })
              .borderRadius(10)
              .padding(15)
          }
          .width('95%')

          Row({ space: 10 }) {
            Button('解析')
              .fontSize(16)
              .width('30%')
              .height(40)
              .backgroundColor('#3498DB')
              .onClick(() => this.decode())

            Button('示例')
              .fontSize(16)
              .width('30%')
              .height(40)
              .backgroundColor('#27AE60')
              .onClick(() => {
                this.idInput = '110101201203020066';
                this.decode();
              })

            Button('清空')
              .fontSize(16)
              .width('30%')
              .height(40)
              .backgroundColor('#E74C3C')
              .onClick(() => {
                this.idInput = '';
                this.analysis = [];
                this.idCardInfo = null;
              })
          }
          .width('95%')
          .justifyContent(FlexAlign.SpaceBetween)
        }

        // 解析结果
        if (this.idCardInfo) {
          Column({ space: 20 }) {
            Text('📊 解析结果')
              .fontSize(22)
              .fontWeight(FontWeight.Bold)
              .fontColor('#2C3E50')
              .width('100%')
              .textAlign(TextAlign.Start)

            // 基本信息卡片
            Column({ space: 15 }) {
              // 身份证号码信息
              Row() {
                Text('身份证号:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(this.idInput)
                  .fontSize(16)
                  .fontColor('#2C3E50')
                  .fontWeight(FontWeight.Bold)
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 地址信息
              Row() {
                Text('📍 地址:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(this.idCardInfo.addressInfo)
                  .fontSize(16)
                  .fontColor('#2C3E50')
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 出生日期
              Row() {
                Text('🎂 出生日期:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(`${this.idCardInfo.birthYear}${this.idCardInfo.birthMonth}${this.idCardInfo.birthDay}`)
                  .fontSize(16)
                  .fontColor('#2C3E50')
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 年龄
              Row() {
                Text('🎈 年龄:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(`${this.idCardInfo.age}`)
                  .fontSize(16)
                  .fontColor('#2C3E50')
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 性别
              Row() {
                Text('👤 性别:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(this.idCardInfo.gender)
                  .fontSize(16)
                  .fontColor('#2C3E50')
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 校验码
              Row() {
                Text('🔍 校验码:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(this.idCardInfo.checkCode)
                  .fontSize(16)
                  .fontColor('#2C3E50')
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 验证结果
              Row() {
                Text('✅ 验证结果:')
                  .fontSize(16)
                  .fontColor('#7F8C8D')
                  .fontWeight(FontWeight.Bold)
                Text(this.idCardInfo.isValid ? '有效' : '无效')
                  .fontSize(16)
                  .fontColor(this.idCardInfo.isValid ? '#27AE60' : '#E74C3C')
                  .fontWeight(FontWeight.Bold)
                  .layoutWeight(1)
                  .textAlign(TextAlign.End)
              }
              .width('100%')

              // 验证信息
              if (!this.idCardInfo.isValid) {
                Text(this.idCardInfo.validationMessage)
                  .fontSize(14)
                  .fontColor('#E74C3C')
                  .width('100%')
                  .textAlign(TextAlign.Start)
              }
            }
            .padding(20)
            .backgroundColor('#FFFFFF')
            .borderRadius(15)
            .width('95%')
            .shadow({
              radius: 5,
              color: '#0000001A',
              offsetX: 0,
              offsetY: 2
            })

            // 编码规则说明
            Column({ space: 15 }) {
              Text('📚 编码规则')
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
                .fontColor('#2C3E50')
                .width('100%')
                .textAlign(TextAlign.Start)

              Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Start }) {
                this.CodeSection('1-6', '地址码', '#3498DB')
                this.CodeSection('7-14', '出生日期码', '#27AE60')
                this.CodeSection('15-17', '顺序码', '#F39C12')
                this.CodeSection('18', '校验码', '#E74C3C')
              }
              .width('100%')
            }
            .padding(20)
            .backgroundColor('#F8F9FA')
            .borderRadius(15)
            .width('95%')
          }
        }

        // 历史记录
        Column({ space: 15 }) {
          Text('📋 解析记录')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .fontColor('#2C3E50')
            .width('100%')
            .textAlign(TextAlign.Start)

          if (this.analysis.length > 0) {
            List({ space: 10 }) {
              ForEach(this.analysis, (item: string, index: number) => {
                ListItem() {
                  Row({ space: 10 }) {
                    Text(`${index + 1}.`)
                      .fontSize(16)
                      .fontColor('#7F8C8D')
                      .fontWeight(FontWeight.Bold)
                    Text(item)
                      .fontSize(16)
                      .fontColor('#2C3E50')
                      .layoutWeight(1)
                  }
                  .width('100%')
                  .padding(15)
                  .backgroundColor('#FFFFFF')
                  .borderRadius(10)
                  .border({ width: 0, color: '#3498DB' })
                  .borderWidth(4)
                  .borderColor('#3498DB')
                }
              })
            }
            .width('100%')
            .height(200)
          } else {
            Text('暂无解析记录')
              .fontSize(16)
              .fontColor('#95A5A6')
              .width('100%')
              .textAlign(TextAlign.Center)
              .padding(20)
          }
        }
        .padding(20)
        .backgroundColor('#E8F4F8')
        .borderRadius(15)
        .width('95%')
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#F5F6FA')
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  CodeSection(range: string, name: string, color: string) {
    Column({ space: 5 }) {
      Text(range)
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2C3E50')
      Text(name)
        .fontSize(12)
        .fontColor('#7F8C8D')
    }
    .width('23%')
    .height(80)
    .padding(10)
    .backgroundColor(color)
    .borderRadius(10)
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }

  decode() {
    if (this.idInput.length !== 18) {
      this.analysis = ['请输入正确的18位身份证号码'];
      return;
    }

    // 验证格式
    if (!/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[0-9Xx]$/.test(this.idInput)) {
      this.analysis = ['身份证号码格式不正确'];
      return;
    }

    // 解析各个部分
    const addressCode = this.idInput.substring(0, 6);
    const birthYear = this.idInput.substring(6, 10);
    const birthMonth = this.idInput.substring(10, 12);
    const birthDay = this.idInput.substring(12, 14);
    const orderCode = this.idInput.substring(14, 17);
    const checkCode = this.idInput.substring(17, 18).toUpperCase();
    const gender = parseInt(orderCode) % 2 === 1 ? '男' : '女';

    // 计算年龄
    const now = new Date();
    const birthDate = new Date(parseInt(birthYear), parseInt(birthMonth) - 1, parseInt(birthDay));
    let age = now.getFullYear() - birthDate.getFullYear();
    const monthDiff = now.getMonth() - birthDate.getMonth();
    if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < birthDate.getDate())) {
      age--;
    }

    // 获取地址信息
    const provinceCode = addressCode.substring(0, 2);
    const provinceName = this.provinceCodes.get(provinceCode) || '未知地区';
    const addressInfo = `${provinceName}${addressCode}`;

    // 验证校验码
    const isValid = this.validateCheckCode(this.idInput);
    const validationMessage = isValid ? '身份证号码有效' : '校验码错误,身份证号码无效';

    // 构建信息
    this.idCardInfo = {
      addressCode,
      addressInfo,
      birthYear,
      birthMonth,
      birthDay,
      orderCode,
      gender,
      checkCode,
      age,
      isValid,
      validationMessage
    };

    // 添加到历史记录
    const record = `${this.idInput} - ${birthYear}${birthMonth}${birthDay}日 - ${gender}`;
    this.analysis.unshift(record);
    if (this.analysis.length > 5) {
      this.analysis.pop();
    }
  }

  validateCheckCode(idCard: string): boolean {
    const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
    
    let sum = 0;
    for (let i = 0; i < 17; i++) {
      sum += parseInt(idCard[i]) * weights[i];
    }
    
    const checkCodeIndex = sum % 11;
    return idCard[17].toUpperCase() === checkCodes[checkCodeIndex];
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐