HarmonyOS ArkTS 实战:实现一个校园三好学生优秀干部评选应用
·
HarmonyOS ArkTS 实战:从零实现校园评奖评优管理系统
目录
- 一、项目背景与效果预览
- 二、技术栈与开发环境
- 三、需求分析与功能架构
- 四、数据结构与服务层设计
- 五、核心功能实现(完整代码)
- 六、UI 界面设计与实现(完整组件)
- 七、完整主页面代码(Index.ets)及子页面
- 八、运行与调试
- 九、项目总结与扩展思路
一、项目背景与效果预览
1.1 痛点场景
每年评奖评优季,纸质材料堆积、投票统计繁琐、进度不透明、证书发放延迟……学生和辅导员都备受困扰。本应用将 三好学生、优秀干部、奖学金 等评选全流程数字化,包括通知发布、在线申请、材料上传、投票、进度跟踪、结果公示、电子证书生成,提升评选效率和公正性。
1.2 运行效果(模拟器预览)
- 主界面:顶部为滚动通知横幅(显示最新评选动态);中部为奖项列表(卡片式,含奖项名称、金额、截止日期,点击“申请”按钮);下方为候选人列表(当前活跃奖项的申请者,带投票按钮,投票时有“+1”动画)。
- 底部Tabs:“首页”、“我的申请”、“结果公示”、“历年获奖”。
- 我的申请:显示本人所有申请记录及状态(审核中、初选通过、公示中、已获奖),可查看进度条。
- 结果公示:按奖项展示最终获奖名单(带等级颜色:金奖/银奖/铜奖)。
- 历年获奖:展示往年获奖情况,含证书缩略图(可点击查看大图)。
主题色采用深靛蓝(#1E3A8A),象征公正、严谨、信任,搭配金色(#F59E0B)点缀突出荣誉感。
二、技术栈与开发环境
| 技术项 | 说明 |
|---|---|
| 开发语言 | ArkTS |
| UI 框架 | ArkUI 声明式开发 |
| 状态管理 | @State / @Provide / @Consume |
| 布局方式 | List + Grid + Tabs + Stack |
| 数据持久化 | @ohos.data.preferences |
| 路由管理 | @ohos.router |
| 图表组件 | @ohos.arkui.advanced.Chart |
| 弹窗/提示 | @ohos.prompt / @ohos.dialog |
| 动画 | animateTo / transition |
| 开发工具 | DevEco Studio 5.0+ |
| SDK 版本 | API 24 及以上 |
三、需求分析与功能架构
3.1 核心功能清单
- 评选通知:顶部轮播/滚动通知,展示最新公告。
- 奖项浏览:卡片展示所有奖项(名称、奖金、要求、截止日期),支持申请。
- 在线申请:填写个人信息、绩点、申请理由,上传附件(模拟)。
- 材料上传:支持多文件(图片/文档)模拟选择,展示文件列表。
- 投票系统:对候选人投票,每人每奖项限投1票(防刷),投票动画。
- 状态流转:申请 → 审核中 → 初选通过 → 公示中 → 获奖/未获奖,进度条展示。
- 结果公示:按奖项分组,展示获奖者名单及等级。
- 获奖证书:动态生成证书卡片(含姓名、奖项、等级、日期)。
- 数据统计:各奖项申请人数、投票分布饼图/柱状图。
- 历年获奖:历史记录查询。
3.2 业务流程图(申请到获奖)
用户浏览奖项 → 点击申请 → 填写表单 + 上传材料 → 提交(状态:审核中)
→ 管理员后台初审(模拟自动通过)→ 状态:初选通过 → 进入投票环节
→ 投票截止 → 计算票数 → 公示(状态:公示中)→ 最终获奖(状态:已获奖)或未获奖
四、数据结构与服务层设计
4.1 数据模型
// model/Award.ets
export interface Award {
id: number;
name: string; // 如“三好学生”
description: string;
requirement: string; // 申请要求
bonus: number; // 奖金
applyDeadline: string; // 申请截止日期
isActive: boolean; // 是否当前可申请
}
// model/Application.ets
export interface Application {
id: number;
awardId: number; // 关联奖项
awardName: string;
applicantId: string; // 学号
applicantName: string;
college: string;
major: string;
gpa: number;
description: string; // 申请理由
attachments: string[]; // 附件文件名列表
applyTime: string;
votes: number;
status: '审核中' | '初选通过' | '公示中' | '已获奖' | '未获奖';
awardLevel?: '金奖' | '银奖' | '铜奖'; // 最终等级
voteLimit: number; // 每人可投几票(默认1)
}
// model/VoteRecord.ets
export interface VoteRecord {
id: number;
voterId: string; // 投票人学号
applicationId: number;
awardId: number;
voteTime: string;
}
4.2 服务层(Service)
沿用 BaseService 模式,实现 AwardService、ApplicationService、VoteService。
// service/BaseService.ets(同前)
export abstract class BaseService<T> {
protected prefName: string;
protected key: string;
constructor(prefName: string, key: string) { this.prefName = prefName; this.key = key; }
protected async getPreferences(): Promise<preferences.Preferences> { /* ... */ }
protected async loadData(): Promise<T[]> { /* ... */ }
protected async saveData(data: T[]): Promise<void> { /* ... */ }
abstract fetch(): Promise<T[]>;
abstract add(item: T): Promise<T[]>;
abstract update(id: number, newItem: T): Promise<T[]>;
abstract delete(id: number): Promise<T[]>;
}
// service/AwardService.ets
import { BaseService } from './BaseService';
import { Award } from '../model/Award';
class AwardService extends BaseService<Award> {
constructor() { super('AwardPrefs', 'awards'); }
async fetch(): Promise<Award[]> {
const data = await this.loadData();
if (data.length === 0) {
const mock: Award[] = [
{ id: 1, name: '三好学生', description: '品学兼优,全面发展', requirement: 'GPA≥3.5,无违纪', bonus: 2000, applyDeadline: '2026-12-31', isActive: true },
{ id: 2, name: '优秀学生干部', description: '工作突出,服务同学', requirement: '担任班干部或社团负责人', bonus: 1500, applyDeadline: '2026-11-30', isActive: true },
{ id: 3, name: '一等奖学金', description: '学业成绩优异', requirement: 'GPA≥3.8', bonus: 3000, applyDeadline: '2027-01-15', isActive: false },
];
await this.saveData(mock);
return mock;
}
return data;
}
async add(item: Award): Promise<Award[]> { /* ... */ }
async update(id: number, newItem: Award): Promise<Award[]> { /* ... */ }
async delete(id: number): Promise<Award[]> { /* ... */ }
}
export const awardService = new AwardService();
// service/ApplicationService.ets(略,类似)
// service/VoteService.ets(略)
五、核心功能实现(完整代码)
5.1 页面状态与数据加载(主页面 Index.ets)
使用 Tabs 切换首页、我的申请、结果公示、历年获奖。以首页为主。
// pages/Index.ets
import { Award } from '../model/Award';
import { Application } from '../model/Application';
import { VoteRecord } from '../model/VoteRecord';
import { awardService } from '../service/AwardService';
import { applicationService } from '../service/ApplicationService';
import { voteService } from '../service/VoteService';
import prompt from '@ohos.prompt';
import router from '@ohos.router';
import { Chart, ChartType } from '@ohos.arkui.advanced';
@Entry
@Component
struct Index {
@State awards: Award[] = [];
@State applications: Application[] = [];
@State voteRecords: VoteRecord[] = [];
@State currentTabIndex: number = 0;
@State isLoading: boolean = true;
// 申请弹窗
@State isApplyDialogVisible: boolean = false;
@State selectedAwardId: number = -1;
@State applyGpa: string = '';
@State applyDesc: string = '';
@State applyAttachments: string[] = [];
// 通知横幅
@State notice: string = '📢 2026年度评奖评优已启动,请及时申请!';
// 投票防刷:记录当前用户已投的申请ID(假设用户ID为'me')
private votedIds: Set<number> = new Set();
aboutToAppear() {
this.loadData();
}
async loadData() {
this.isLoading = true;
try {
this.awards = await awardService.fetch();
this.applications = await applicationService.fetch();
this.voteRecords = await voteService.fetch();
// 加载已投票记录
this.voteRecords.filter(v => v.voterId === 'me').forEach(v => this.votedIds.add(v.applicationId));
} catch (e) {
prompt.showToast({ message: '数据加载失败' });
} finally {
this.isLoading = false;
}
}
// 获取某个奖项的申请列表(用于投票)
private getApplicationsByAward(awardId: number): Application[] {
return this.applications.filter(app => app.awardId === awardId && (app.status === '初选通过' || app.status === '公示中'));
}
// 获取当前活跃奖项
private getActiveAwards(): Award[] {
return this.awards.filter(a => a.isActive);
}
// ... 其他方法
}
5.2 奖项展示与申请(含表单弹窗)
奖项卡片展示,点击“申请”打开弹窗。
@Builder AwardCard(award: Award) {
Column() {
Row() {
Text(award.name).fontSize(18).fontWeight(FontWeight.Bold);
Blank();
Text(`¥${award.bonus}`).fontSize(16).fontColor('#F59E0B');
}.width('100%');
Text(award.description).fontSize(14).fontColor('#666').margin({ top: 4 });
Text(`截止:${award.applyDeadline}`).fontSize(12).fontColor('#999').margin({ top: 4 });
Row() {
if (award.isActive) {
Button('立即申请')
.backgroundColor('#1E3A8A')
.fontSize(12)
.height(30)
.onClick(() => this.openApplyDialog(award.id));
} else {
Text('已结束').fontSize(12).fontColor('#999');
}
}
.width('100%')
.justifyContent(FlexAlign.End)
.margin({ top: 8 });
}
.width('100%')
.padding(12)
.backgroundColor('#FFF')
.borderRadius(8)
.shadow({ radius: 4, color: '#00000020' })
.margin({ bottom: 10 });
}
private openApplyDialog(awardId: number) {
this.selectedAwardId = awardId;
this.applyGpa = '';
this.applyDesc = '';
this.applyAttachments = [];
this.isApplyDialogVisible = true;
}
private async submitApplication() {
const award = this.awards.find(a => a.id === this.selectedAwardId);
if (!award) return;
if (!this.applyDesc.trim()) {
prompt.showToast({ message: '请填写申请理由' });
return;
}
const gpa = parseFloat(this.applyGpa);
if (isNaN(gpa) || gpa < 0 || gpa > 4.0) {
prompt.showToast({ message: '请输入有效绩点(0~4.0)' });
return;
}
const app: Application = {
id: Date.now(),
awardId: award.id,
awardName: award.name,
applicantId: 'me',
applicantName: '我',
college: '计算机学院',
major: '计算机科学与技术',
gpa: gpa,
description: this.applyDesc.trim(),
attachments: this.applyAttachments,
applyTime: new Date().toISOString(),
votes: 0,
status: '审核中',
voteLimit: 1
};
try {
this.applications = await applicationService.add(app);
prompt.showToast({ message: '申请提交成功!' });
this.isApplyDialogVisible = false;
} catch (e) {
prompt.showToast({ message: '提交失败' });
}
}
申请弹窗内容(含附件上传模拟):
@Builder ApplyDialogContent() {
Column() {
Text('申请').fontSize(18).fontWeight(FontWeight.Bold).margin(12);
Text(`奖项:${this.awards.find(a => a.id === this.selectedAwardId)?.name || ''}`).margin(4);
TextInput({ placeholder: '绩点(如3.8)', text: this.applyGpa }).type(InputType.Number).onChange(v => this.applyGpa = v).margin(6);
TextArea({ placeholder: '申请理由(简要说明)', text: this.applyDesc }).onChange(v => this.applyDesc = v).height(80).margin(6);
// 附件上传(模拟)
Row() {
Text('附件:').width(60);
Button('添加文件(模拟)').onClick(() => {
// 模拟选择文件
prompt.showDialog({
title: '选择文件',
message: '请选择材料文件(图片/文档)',
buttons: [{ text: '选择图片' }, { text: '选择文档' }, { text: '取消' }]
}).then(result => {
if (result.index !== 2) {
const file = result.index === 0 ? '成绩单.jpg' : '推荐信.docx';
this.applyAttachments = [...this.applyAttachments, file];
prompt.showToast({ message: `已添加:${file}` });
}
});
}).height(30).fontSize(12);
}.margin(6);
if (this.applyAttachments.length > 0) {
Column() {
ForEach(this.applyAttachments, (file: string) => {
Row() {
Text('📎 ' + file).fontSize(12).layoutWeight(1);
Button('×').onClick(() => {
this.applyAttachments = this.applyAttachments.filter(f => f !== file);
}).height(20).fontSize(12).backgroundColor('#DC2626');
}.width('100%');
});
}.width('100%').margin({ top: 4 });
}
Row() {
Button('取消').onClick(() => this.isApplyDialogVisible = false).backgroundColor('#999');
Button('提交申请').onClick(() => this.submitApplication()).backgroundColor('#1E3A8A').margin({ left: 20 });
}.margin(16);
}
.padding(20)
.width('90%')
.backgroundColor('#FFF')
.borderRadius(16);
}
5.3 在线投票(含动画与防刷)
在首页展示候选列表,每个候选人带投票按钮,投票后按钮禁用且显示“已投”。
@Builder CandidateList() {
Column() {
Text('📊 候选人投票').fontSize(16).fontWeight(FontWeight.Medium).margin({ top: 12, bottom: 8 }).alignSelf(ItemAlign.Start);
// 只展示当前有申请且状态为“初选通过”的奖项,简化:取第一个活跃奖项
const activeAward = this.getActiveAwards()[0];
if (!activeAward) {
Text('暂无投票环节').fontSize(14).fontColor('#999');
return;
}
const candidates = this.getApplicationsByAward(activeAward.id);
if (candidates.length === 0) {
Text('暂无候选人').fontSize(14).fontColor('#999');
return;
}
List() {
ForEach(candidates, (app: Application) => {
ListItem() {
Row() {
Column({ space: 4 }) {
Text(app.applicantName).fontSize(16).fontWeight(FontWeight.Medium);
Text(`绩点${app.gpa} | 票数:${app.votes}`).fontSize(12).fontColor('#666');
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1);
if (this.votedIds.has(app.id)) {
Text('已投').fontSize(14).fontColor('#999');
} else {
Button('投票')
.backgroundColor('#F59E0B')
.fontSize(14)
.height(32)
.onClick(() => this.doVote(app.id));
}
}
.width('100%')
.padding(10)
.border({ width: { bottom: 1 }, color: '#F0F0F0' });
}
});
}
.width('100%')
.height(200)
}
.width('100%')
.padding(10)
}
private async doVote(applicationId: number) {
if (this.votedIds.has(applicationId)) {
prompt.showToast({ message: '您已投过票' });
return;
}
// 每人每奖项限投1票,已通过voteRecords检查,这里再查一次
const app = this.applications.find(a => a.id === applicationId);
if (!app) return;
// 检查是否已经投过该奖项
const alreadyVoted = this.voteRecords.some(v => v.voterId === 'me' && v.awardId === app.awardId);
if (alreadyVoted) {
prompt.showToast({ message: '您已对此奖项投过票' });
return;
}
// 执行投票
const updatedApp = { ...app, votes: app.votes + 1 };
try {
this.applications = await applicationService.update(applicationId, updatedApp);
// 记录投票
const vote: VoteRecord = {
id: Date.now(),
voterId: 'me',
applicationId: applicationId,
awardId: app.awardId,
voteTime: new Date().toISOString()
};
this.voteRecords = await voteService.add(vote);
this.votedIds.add(applicationId);
prompt.showToast({ message: '投票成功!' });
// 动画效果:票数+1的漂浮动画可后续实现
} catch (e) {
prompt.showToast({ message: '投票失败' });
}
}
5.4 评选进度与状态流转
在“我的申请”Tab中展示每个申请的状态和进度条。
// 在Tabs的第二个TabContent中
TabContent() {
Column() {
Text('我的申请').fontSize(18).fontWeight(FontWeight.Bold).margin(12).alignSelf(ItemAlign.Start);
List() {
ForEach(this.applications.filter(a => a.applicantId === 'me'), (app: Application) => {
ListItem() {
Column({ space: 6 }) {
Row() {
Text(app.awardName).fontSize(16).fontWeight(FontWeight.Medium);
Blank();
Text(this.getStatusText(app.status)).fontSize(12).fontColor(this.getStatusColor(app.status));
}.width('100%');
// 进度条
Progress({ value: this.getStatusProgress(app.status), total: 100 })
.width('100%')
.height(6)
.color(this.getStatusColor(app.status));
Text(`申请时间:${app.applyTime.slice(0,10)}`).fontSize(11).fontColor('#999').width('100%');
}
.width('100%')
.padding(12)
.backgroundColor('#FFF')
.borderRadius(8)
.margin({ bottom: 8 });
}
});
}
.width('100%')
.layoutWeight(1)
}
.padding(10)
}
.tabBar('📝 我的申请')
辅助方法:
private getStatusText(status: string): string {
const map: Record<string, string> = {
'审核中': '⏳ 审核中',
'初选通过': '✅ 初选通过',
'公示中': '📢 公示中',
'已获奖': '🏆 已获奖',
'未获奖': '❌ 未获奖'
};
return map[status] || status;
}
private getStatusColor(status: string): string {
const map: Record<string, string> = {
'审核中': '#F59E0B',
'初选通过': '#3B82F6',
'公示中': '#8B5CF6',
'已获奖': '#10B981',
'未获奖': '#EF4444'
};
return map[status] || '#999';
}
private getStatusProgress(status: string): number {
const map: Record<string, number> = {
'审核中': 25,
'初选通过': 50,
'公示中': 75,
'已获奖': 100,
'未获奖': 100
};
return map[status] || 0;
}
5.5 结果公示与获奖证书(动态生成)
在“结果公示”Tab中展示获奖名单,并支持点击查看证书。
TabContent() {
Column() {
Text('🏅 结果公示').fontSize(18).fontWeight(FontWeight.Bold).margin(12).alignSelf(ItemAlign.Start);
// 按奖项分组
const awardIds = [...new Set(this.applications.filter(a => a.status === '已获奖').map(a => a.awardId))];
if (awardIds.length === 0) {
Text('暂无公示结果').fontSize(14).fontColor('#999').margin(20);
} else {
ForEach(awardIds, (awardId: number) => {
const award = this.awards.find(a => a.id === awardId);
if (!award) return;
Column() {
Text(`${award.name} 获奖名单`).fontSize(16).fontWeight(FontWeight.Medium).margin({ bottom: 8 }).alignSelf(ItemAlign.Start);
ForEach(this.applications.filter(a => a.awardId === awardId && a.status === '已获奖'), (app: Application) => {
Row() {
Text(app.applicantName).layoutWeight(1);
Text(app.awardLevel || '金奖').fontSize(12).fontColor('#F59E0B');
Button('查看证书')
.onClick(() => {
router.pushUrl({ url: 'pages/Certificate', params: { applicationId: app.id } });
})
.height(28).fontSize(12).backgroundColor('#1E3A8A');
}
.width('100%')
.padding(8)
.border({ width: { bottom: 1 }, color: '#F0F0F0' });
});
}
.width('100%')
.margin({ bottom: 16 });
});
}
}
.padding(10)
}
.tabBar('📋 结果公示')
证书页面 Certificate.ets(简化):
// pages/Certificate.ets
import router from '@ohos.router';
import { Application } from '../model/Application';
import { applicationService } from '../service/ApplicationService';
@Entry
@Component
struct Certificate {
@State app: Application | null = null;
private appId: number = -1;
aboutToAppear() {
const params = router.getParams() as { applicationId: number };
if (params) {
this.appId = params.applicationId;
this.loadApp();
}
}
async loadApp() {
const list = await applicationService.fetch();
this.app = list.find(a => a.id === this.appId) || null;
}
build() {
Column() {
if (this.app) {
Stack() {
Column() {
Text('🏆 获奖证书').fontSize(24).fontWeight(FontWeight.Bold).fontColor('#1E3A8A').margin(20);
Text(`兹证明`).fontSize(16).fontColor('#333');
Text(this.app.applicantName).fontSize(28).fontWeight(FontWeight.Bold).fontColor('#F59E0B').margin(10);
Text(`荣获 “${this.app.awardName}”`).fontSize(20).margin(10);
Text(`等级:${this.app.awardLevel || '金奖'}`).fontSize(18).fontColor('#1E3A8A').margin(10);
Text(`颁发日期:${new Date().toLocaleDateString()}`).fontSize(14).fontColor('#666').margin(20);
}
.padding(30)
.width('85%')
.backgroundColor('#FFF')
.borderRadius(16)
.shadow({ radius: 8, color: '#00000030' })
.border({ width: 4, color: '#F59E0B' });
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#F3F4F6');
} else {
Text('证书不存在').margin(20);
}
}
.width('100%')
.height('100%')
}
}
5.6 历年获奖与统计图表
在“历年获奖”Tab中展示往年记录,并附带统计图表。
TabContent() {
Column() {
Text('📊 历年获奖统计').fontSize(18).fontWeight(FontWeight.Bold).margin(12).alignSelf(ItemAlign.Start);
// 统计各奖项获奖人数
const awardNames = [...new Set(this.applications.filter(a => a.status === '已获奖').map(a => a.awardName))];
const counts = awardNames.map(name =>
this.applications.filter(a => a.awardName === name && a.status === '已获奖').length
);
if (awardNames.length > 0) {
Chart({
type: ChartType.Bar,
datasets: [{ data: counts, color: '#1E3A8A' }],
options: {
xAxis: { labels: awardNames, color: '#999' },
yAxis: { min: 0, step: 1, color: '#999' }
}
}).width('100%').height(150).margin({ bottom: 16 });
}
Text('📜 历年获奖记录').fontSize(16).fontWeight(FontWeight.Medium).margin({ top: 8 }).alignSelf(ItemAlign.Start);
List() {
ForEach(this.applications.filter(a => a.status === '已获奖'), (app: Application) => {
ListItem() {
Row() {
Text(`${app.awardName} - ${app.applicantName}`).layoutWeight(1);
Text(app.awardLevel || '金奖').fontSize(12).fontColor('#F59E0B');
}
.width('100%')
.padding(8)
.border({ width: { bottom: 1 }, color: '#F0F0F0' });
}
});
}
.width('100%')
.layoutWeight(1)
}
.padding(10)
}
.tabBar('📅 历年获奖')
5.7 数据持久化(Preferences)
已在服务层实现,每次修改自动保存。
六、UI 界面设计与实现(完整组件)
6.1 顶部通知横幅
使用 Marquee 或滚动文字展示通知。
@Builder NoticeBanner() {
Row() {
Text(this.notice)
.fontSize(14)
.fontColor('#FFF')
.padding(8)
.width('100%')
.textOverflow({ overflow: TextOverflow.Marquee })
.marquee({ loop: -1, start: true })
}
.width('100%')
.backgroundColor('#1E3A8A')
.padding({ left: 16, right: 16 })
}
6.2 奖项列表卡片
见 AwardCard 构建器。
6.3 候选人列表(可投票)
见 CandidateList 构建器。
6.4 我的申请状态跟踪
见“我的申请”Tab内容。
6.5 获奖证书展示(卡片翻转)
在证书页面使用 Stack 和动画实现翻转效果(简化)。
七、完整主页面代码(Index.ets)及子页面
由于篇幅,此处提供 Index.ets 完整骨架,所有Builder和逻辑已在前文给出,只需组合。项目结构如下:
entry/src/main/ets/
├── model/
│ ├── Award.ets
│ ├── Application.ets
│ └── VoteRecord.ets
├── service/
│ ├── BaseService.ets
│ ├── AwardService.ets
│ ├── ApplicationService.ets
│ └── VoteService.ets
└── pages/
├── Index.ets
└── Certificate.ets
Index.ets 最终 build 方法:
build() {
Column() {
// 顶部通知(仅首页显示)
if (this.currentTabIndex === 0) {
this.NoticeBanner();
}
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
Column() {
if (this.isLoading) LoadingProgress().color('#1E3A8A');
else {
Scroll() {
Column() {
// 奖项列表
ForEach(this.getActiveAwards(), (award: Award) => {
this.AwardCard(award);
});
// 候选人投票
this.CandidateList();
}
.padding(10)
}
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
.backgroundColor('#F3F4F6')
}
.tabBar('🏠 首页')
TabContent() { /* 我的申请内容 */ }
.tabBar('📝 我的申请')
TabContent() { /* 结果公示内容 */ }
.tabBar('📋 结果公示')
TabContent() { /* 历年获奖内容 */ }
.tabBar('📅 历年获奖')
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#FFF')
.dialog($$this.isApplyDialogVisible, this.ApplyDialogContent())
}
八、运行与调试
8.1 环境
- DevEco Studio 5.0+,API 24。
- 无需特殊权限。
8.2 运行
- 导入完整项目,确保所有服务层和模型文件存在。
- 同步并运行模拟器。
- 测试申请、投票、查看状态、结果公示、证书等全部流程。
8.3 调试
- 使用
HiLog查看数据变更。 - 模拟投票防刷逻辑,可尝试重复投票验证。
九、项目总结与扩展思路
9.1 项目总结
- 全流程覆盖:从申请到公示、证书,完整闭环。
- 状态管理清晰:使用进度条直观展示申请进程。
- 交互丰富:弹窗、动画、图表、卡片,提升用户体验。
- 数据持久化:所有状态保存,重启不丢失。
- 工程化:分层服务,便于扩展。
9.2 扩展方向
- 管理员后台:添加审核、发布奖项、设置投票时间等功能。
- 导师推荐:增加推荐人字段,需导师确认。
- 综测加分:自动计算综测分数并关联。
- 答辩环节:线上答辩预约与评分。
- 异议申诉:对结果提出申诉,流程跟踪。
- 推送通知:状态变更时推送消息。
运行效果

更多推荐

所有评论(0)