HarmonyOS ArkTS 实战:实现一个校园社团招新与成员管理应用

项目效果

本文使用 HarmonyOS 和 ArkTS 实现一个校园社团招新与成员管理应用。

应用可以发布社团招新信息,查看社团介绍,在线报名加入社团,管理社团成员,审核报名申请,并提供社团分类筛选、报名统计和成员管理等功能。

项目使用 DevEco Studio 开发,适配 API 23 及以上版本。

运行效果

校园社团招新与成员管理应用

功能介绍

本项目实现了以下功能:

  • 发布社团招新信息
  • 填写社团名称和介绍
  • 设置社团分类和招新人数
  • 设置报名要求和联系方式
  • 浏览社团列表
  • 在线报名加入社团
  • 填写报名信息
  • 审核报名申请
  • 通过/拒绝报名
  • 管理社团成员
  • 设置成员职位
  • 按社团分类筛选
  • 统计社团总数、待审核、成员数
  • 撤销报名
  • 移除成员

定义数据结构

首先定义社团和报名申请的数据结构:

interface ClubMember {
  studentId: string;
  name: string;
  department: string;
  position: string;
  joinTime: string;
}

interface ClubApplication {
  id: number;
  studentId: string;
  name: string;
  department: string;
  phone: string;
  introduce: string;
  status: string;
  applyTime: string;
}

interface Club {
  id: number;
  name: string;
  description: string;
  category: string;
  president: string;
  contact: string;
  requirement: string;
  maxMembers: number;
  logo: string;
  members: ClubMember[];
  applications: ClubApplication[];
  createTime: string;
}

字段说明如下:

  • ClubMember:社团成员
    • studentId:学号
    • name:姓名
    • department:院系
    • position:职位
    • joinTime:加入时间
  • ClubApplication:报名申请
    • id:申请编号
    • studentId:学号
    • name:姓名
    • department:院系
    • phone:联系方式
    • introduce:自我介绍
    • status:申请状态(待审核/已通过/已拒绝)
    • applyTime:申请时间
  • Club:社团信息
    • id:社团编号
    • name:社团名称
    • description:社团介绍
    • category:分类
    • president:社长
    • contact:联系方式
    • requirement:报名要求
    • maxMembers:招新人数上限
    • logo:社团logo颜色
    • members:成员列表
    • applications:报名申请列表
    • createTime:创建时间

初始化页面状态

使用 @State 保存输入内容、分类选择、筛选条件和当前用户:

@State private clubNameText: string = '';
@State private descText: string = '';
@State private presidentText: string = '';
@State private contactText: string = '';
@State private requirementText: string = '';
@State private maxMembersText: string = '';
@State private applyNameText: string = '';
@State private applyIdText: string = '';
@State private applyDeptText: string = '';
@State private applyPhoneText: string = '';
@State private applyIntroText: string = '';
@State private selectedCategory: string = '学术科技';
@State private filterCategory: string = '全部';
@State private currentUserId: string = '2023001';
@State private nextClubId: number = 5;
@State private nextApplyId: number = 10;

准备一些初始社团数据:

@State private clubs: Club[] = [
  {
    id: 1,
    name: '鸿蒙开发社',
    description: '学习HarmonyOS开发,参加各类计算机竞赛,做开源项目',
    category: '学术科技',
    president: '张社长',
    contact: '微信:harmonydev',
    requirement: '对编程感兴趣,每周能参加社团活动',
    maxMembers: 50,
    logo: '#3B82F6',
    members: [
      { studentId: '2022001', name: '张同学', department: '计算机学院', position: '社长', joinTime: '2025-09-01' },
      { studentId: '2022002', name: '李同学', department: '信息学院', position: '副社长', joinTime: '2025-09-01' }
    ],
    applications: [
      {
        id: 1,
        studentId: '2023001',
        name: '王同学',
        department: '计算机学院',
        phone: '138****1234',
        introduce: '学过ArkTS,想参加鸿蒙开发比赛',
        status: '待审核',
        applyTime: '2026-07-15'
      }
    ],
    createTime: '2025-09-01'
  },
  {
    id: 2,
    name: '篮球协会',
    description: '组织篮球比赛,日常训练,参加校际联赛',
    category: '体育竞技',
    president: '刘会长',
    contact: 'QQ:123456789',
    requirement: '热爱篮球,有一定基础',
    maxMembers: 100,
    logo: '#F59E0B',
    members: [
      { studentId: '2022003', name: '刘同学', department: '体育学院', position: '会长', joinTime: '2025-09-01' }
    ],
    applications: [],
    createTime: '2025-09-01'
  },
  {
    id: 3,
    name: '吉他社',
    description: '吉他教学,乐队排练,校园演出',
    category: '文化艺术',
    president: '陈社长',
    contact: '微信:guitar2026',
    requirement: '有无基础均可,热爱音乐',
    maxMembers: 60,
    logo: '#8B5CF6',
    members: [],
    applications: [
      {
        id: 2,
        studentId: '2023002',
        name: '赵同学',
        department: '文学院',
        phone: '139****5678',
        introduce: '学过一年吉他,想加入乐队',
        status: '已通过',
        applyTime: '2026-07-10'
      }
    ],
    createTime: '2025-09-01'
  },
  {
    id: 4,
    name: '志愿者协会',
    description: '组织各类志愿活动,进博会、马拉松等大型活动志愿服务',
    category: '公益志愿',
    president: '周会长',
    contact: '电话:021-12345678',
    requirement: '有爱心,能抽出时间参加志愿活动',
    maxMembers: 200,
    logo: '#EC4899',
    members: [
      { studentId: '2022004', name: '周同学', department: '社会学院', position: '会长', joinTime: '2025-09-01' }
    ],
    applications: [],
    createTime: '2025-09-01'
  }
];

创建社团

用户可以创建新社团:

private createClub(): void {
  const name: string = this.clubNameText.trim();
  const desc: string = this.descText.trim();
  const president: string = this.presidentText.trim();
  const contact: string = this.contactText.trim();
  const max: number = parseInt(this.maxMembersText);

  if (name.length === 0 || desc.length === 0 || president.length === 0 || isNaN(max)) {
    return;
  }

  const colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#06B6D4'];
  const randomColor = colors[Math.floor(Math.random() * colors.length)];

  const club: Club = {
    id: this.nextClubId,
    name,
    description: desc,
    category: this.selectedCategory,
    president,
    contact,
    requirement: this.requirementText.trim() || '无特殊要求',
    maxMembers: max,
    logo: randomColor,
    members: [{
      studentId: this.currentUserId,
      name: president,
      department: '创建者',
      position: '社长',
      joinTime: new Date().toISOString().split('T')[0]
    }],
    applications: [],
    createTime: new Date().toISOString().split('T')[0]
  };

  this.clubs = [club, ...this.clubs];
  this.nextClubId += 1;
  this.clubNameText = '';
  this.descText = '';
  this.presidentText = '';
  this.contactText = '';
  this.requirementText = '';
  this.maxMembersText = '';
}

创建社团的人自动成为社长。

报名加入社团

用户可以报名加入社团:

private applyClub(clubId: number): void {
  const name: string = this.applyNameText.trim();
  const studentId: string = this.applyIdText.trim();
  const dept: string = this.applyDeptText.trim();
  const phone: string = this.applyPhoneText.trim();

  if (name.length === 0 || studentId.length === 0 || dept.length === 0 || phone.length === 0) {
    return;
  }

  this.clubs = this.clubs.map((club: Club) => {
    if (club.id === clubId) {
      const alreadyApplied = club.applications.some(a => a.studentId === studentId && a.status === '待审核');
      const alreadyMember = club.members.some(m => m.studentId === studentId);
      
      if (!alreadyApplied && !alreadyMember) {
        const newApplication: ClubApplication = {
          id: this.nextApplyId,
          studentId,
          name,
          department: dept,
          phone,
          introduce: this.applyIntroText.trim(),
          status: '待审核',
          applyTime: new Date().toISOString().split('T')[0]
        };
        this.nextApplyId += 1;
        
        return {
          id: club.id,
          name: club.name,
          description: club.description,
          category: club.category,
          president: club.president,
          contact: club.contact,
          requirement: club.requirement,
          maxMembers: club.maxMembers,
          logo: club.logo,
          members: club.members,
          applications: [newApplication, ...club.applications],
          createTime: club.createTime
        };
      }
    }
    return club;
  });

  this.applyNameText = '';
  this.applyIdText = '';
  this.applyDeptText = '';
  this.applyPhoneText = '';
  this.applyIntroText = '';
}

已经是成员或有待审核申请的不能重复报名。

审核报名申请

社长可以审核报名申请:

private approveApplication(clubId: number, applyId: number, approved: boolean): void {
  this.clubs = this.clubs.map((club: Club) => {
    if (club.id === clubId) {
      const updatedApplications = club.applications.map((app: ClubApplication) => {
        if (app.id === applyId && app.status === '待审核') {
          return {
            id: app.id,
            studentId: app.studentId,
            name: app.name,
            department: app.department,
            phone: app.phone,
            introduce: app.introduce,
            status: approved ? '已通过' : '已拒绝',
            applyTime: app.applyTime
          };
        }
        return app;
      });

      let updatedMembers = [...club.members];
      if (approved && club.members.length < club.maxMembers) {
        const app = club.applications.find(a => a.id === applyId);
        if (app) {
          updatedMembers.push({
            studentId: app.studentId,
            name: app.name,
            department: app.department,
            position: '社员',
            joinTime: new Date().toISOString().split('T')[0]
          });
        }
      }

      return {
        id: club.id,
        name: club.name,
        description: club.description,
        category: club.category,
        president: club.president,
        contact: club.contact,
        requirement: club.requirement,
        maxMembers: club.maxMembers,
        logo: club.logo,
        members: updatedMembers,
        applications: updatedApplications,
        createTime: club.createTime
      };
    }
    return club;
  });
}

审核通过后自动加入成员列表,审核拒绝则标记为已拒绝。

筛选社团

页面支持按分类筛选社团:

private getFilteredClubs(): Club[] {
  if (this.filterCategory === '全部') {
    return this.clubs;
  }
  return this.clubs.filter(
    (club: Club) => club.category === this.filterCategory
  );
}

分类选择按钮封装:

@Builder
CategoryButton(name: string) {
  Button(name)
    .height(32)
    .padding({ left: 14, right: 14 })
    .fontSize(12)
    .fontColor(this.selectedCategory === name ? Color.White : '#344054')
    .backgroundColor(
      this.selectedCategory === name ? '#0D9488' : '#F3F4F6'
    )
    .borderRadius(16)
    .onClick(() => {
      this.selectedCategory = name;
    });
}

@Builder
FilterCategoryButton(name: string) {
  Button(name)
    .height(32)
    .padding({ left: 14, right: 14 })
    .fontSize(12)
    .fontColor(this.filterCategory === name ? Color.White : '#344054')
    .backgroundColor(
      this.filterCategory === name ? '#0D9488' : '#CCFBF1'
    )
    .borderRadius(16)
    .onClick(() => {
      this.filterCategory = name;
    });
}

统计社团数据

统计社团总数、待审核申请和总成员数:

private getTotalPendingApps(): number {
  return this.clubs.reduce((sum: number, club: Club) => {
    return sum + club.applications.filter(a => a.status === '待审核').length;
  }, 0);
}

private getTotalMembers(): number {
  return this.clubs.reduce((sum: number, club: Club) => {
    return sum + club.members.length;
  }, 0);
}

顶部统计区域展示社团总数、待审核、总成员数:

this.StatCard(
  '社团总数',
  `${this.clubs.length}`,
  '#0D9488',
  '#F0FDFA'
);
this.StatCard(
  '待审核',
  `${this.getTotalPendingApps()}`,
  '#D97706',
  '#FFF7E8'
);
this.StatCard(
  '总成员',
  `${this.getTotalMembers()}`,
  '#7C3AED',
  '#F5F3FF'
);

设置状态颜色

不同申请状态使用不同颜色:

private getStatusColor(status: string): ResourceColor {
  if (status === '待审核') {
    return '#D97706';
  }
  if (status === '已通过') {
    return '#059669';
  }
  return '#DC2626';
}

private getStatusBgColor(status: string): ResourceColor {
  if (status === '待审核') {
    return '#FEF3C7';
  }
  if (status === '已通过') {
    return '#DCFCE7';
  }
  return '#FEE2E2';
}

颜色含义如下:

  • 橙色:待审核
  • 绿色:已通过
  • 红色:已拒绝
  • 蓝绿色:页面主题色

使用列表展示社团

使用 ListForEach 渲染社团列表:

List({ space: 12 }) {
  ForEach(
    this.getFilteredClubs(),
    (club: Club) => {
      ListItem() {
        this.ClubCard(club)
      }
    },
    (club: Club) => club.id.toString()
  );
}
.width('100%')
.layoutWeight(1)
.scrollBar(BarState.Off);

每个社团使用独立卡片展示,卡片中包含社团logo、名称、分类、介绍、社长、成员数、申请数和操作按钮。

页面设计说明

应用使用蓝绿色作为主题色,适合社团青春活力的氛围。

页面主要分为以下区域:

  1. 顶部标题和招新季提示
  2. 社团数据统计区域
  3. 创建社团表单
  4. 社团分类筛选区域
  5. 社团列表
  6. 报名申请弹窗和成员管理

页面采用浅灰色背景和白色卡片。不同社团使用不同颜色的圆形logo,申请状态使用对应颜色标签,成员数和申请数使用角标展示,便于快速了解社团情况。

SDK 配置

本项目使用 HarmonyOS API 24,满足 API 23 及以上要求:

{
  "name": "default",
  "compatibleSdkVersion": "6.1.1(24)",
  "runtimeOS": "HarmonyOS",
  "targetSdkVersion": "6.1.1(24)",
  "compileSdkVersion": "6.1.1(24)"
}

entry 模块中的运行系统也要保持一致:

{
  "apiType": "stageMode",
  "targets": [
    {
      "name": "default",
      "runtimeOS": "HarmonyOS"
    }
  ]
}

运行项目

使用 DevEco Studio 打开项目,然后找到:

entry/src/main/ets/pages/Index.ets

等待项目同步完成,点击右侧的 Preview 按钮即可查看应用效果。

项目总结

本文使用 HarmonyOS 和 ArkTS 实现了一个校园社团招新与成员管理应用。

项目实现了社团创建、分类筛选、在线报名、申请审核、成员管理、职位设置、报名统计等功能。

通过这个项目可以掌握:

  • ArkTS 接口定义和嵌套数据结构
  • @State 状态管理
  • 复杂数组操作和状态更新
  • ListForEach 列表渲染
  • mapreduce 数组方法
  • 报名审核流程
  • 成员管理逻辑
  • 自定义 @Builder 组件
  • HarmonyOS 社交类应用布局

后续还可以加入社团活动发布、社团公告、消息通知、社团相册、经费管理、换届选举、社团评分和活动签到等功能。

Logo

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

更多推荐