HarmonyOS智慧农业管理应用开发教程--高高种地---第6篇:地块管理系统(上)- 数据模型与列表
✅ 地块数据模型设计(枚举、接口)✅ 地块服务实现(单例模式、CRUD操作)✅ 地块列表页面(统计、搜索、筛选)✅ 测试数据添加✅ 页面路由配置关键技术点技术点说明数据模型使用 enum 和 interface 定义清晰的数据结构单例模式服务层采用单例确保全局唯一实例组件复用使用 @Builder 构建可复用的UI组件状态管理使用 @Local 管理页面状态数据持久化通过 StorageUtil
第6篇:地块管理系统(上)- 数据模型与列表
效果

教程目标
通过本篇教程,你将学会:
- 设计地块数据模型
- 实现地块服务层(单例模式)
- 实现地块列表页面
- 实现地块筛选与搜索
- 展示地块统计信息
- 实现数据持久化
完成本教程后,你将拥有一个完整的地块列表管理功能。
一、地块数据模型设计
在开发地块管理功能之前,我们需要先设计好数据模型。数据模型是应用的基础,它决定了数据的结构和关系。
1.1 创建 ProfessionalAgricultureModels.ets
操作步骤:
- 在 DevEco Studio 中,展开项目目录
entry/src/main/ets - 右键点击
models文件夹,选择 New → ArkTS File - 输入文件名
ProfessionalAgricultureModels,点击 OK - 在文件中输入以下代码
/**
* 专业农业模式数据模型
* 包含地块、作物、农事操作、成本、销售等核心数据结构
*/
/**
* 地块类型枚举
* 定义四种常见的农业地块类型
*/
export enum FieldType {
FARMLAND = 'farmland', // 农田 - 种植粮食作物的大田
ORCHARD = 'orchard', // 果园 - 种植果树的区域
VEGETABLE = 'vegetable', // 菜地 - 种植蔬菜的区域
GREENHOUSE = 'greenhouse' // 温室大棚 - 人工控制的种植环境
}
/**
* 灌溉系统枚举
* 定义常见的农业灌溉方式
*/
export enum IrrigationSystem {
NONE = 'none', // 无灌溉系统
DRIP = 'drip', // 滴灌 - 节水灌溉方式
SPRINKLER = 'sprinkler', // 喷灌 - 适合大面积灌溉
FLOOD = 'flood' // 漫灌 - 传统灌溉方式
}
/**
* 作物类型枚举
* 按用途分类的作物类型
*/
export enum CropType {
GRAIN = 'grain', // 粮食作物 - 如水稻、小麦、玉米
VEGETABLE = 'vegetable', // 蔬菜 - 如白菜、萝卜、番茄
FRUIT = 'fruit', // 水果 - 如苹果、柑橘、葡萄
CASH_CROP = 'cash_crop' // 经济作物 - 如棉花、油菜、甘蔗
}
/**
* 作物生长阶段枚举
* 描述作物从种植到收获的各个阶段
*/
export enum CropGrowthStage {
SEEDLING = 'seedling', // 育苗�� - 种子发芽到幼苗期
GROWING = 'growing', // 生长期 - 营养生长旺盛期
FLOWERING = 'flowering', // 开花期 - 生殖生长阶段
FRUITING = 'fruiting', // 结果期 - 果实成熟期
HARVESTING = 'harvesting', // 收获期 - 可以采收的阶段
FALLOW = 'fallow' // 休耕期 - 土地休息恢复期
}
/**
* 作物健康状态枚举
* 用于评估作物当前的健康状况
*/
export enum CropHealthStatus {
HEALTHY = 'healthy', // 健康 - 生长正常
ATTENTION = 'attention', // 需关注 - 有轻微问题
DISEASE = 'disease', // 病害 - 感染病害
PEST = 'pest' // 虫害 - 发生虫害
}
/**
* 作物信息接口
* 记录单个作物的完整信息
*/
export interface CropInfo {
id: string; // 作物唯一标识
name: string; // 作物名称,如"水稻"、"小麦"
type: CropType; // 作物类型
variety: string; // 品种,如"杂交稻"、"冬小麦"
plantingDate: number; // 种植日期(时间戳)
expectedHarvestDate: number; // 预计收获日期(时间戳)
growthStage: CropGrowthStage; // 当前生长阶段
healthStatus: CropHealthStatus; // 健康状态
area: number; // 种植面积(亩)
expectedYield: number; // 预计产量(公斤)
notes?: string; // 备注信息(可选)
}
/**
* 地块信息接口
* 记录单个地块的完整信息
*/
export interface FieldInfo {
id: string; // 地块唯一标识
name: string; // 地块名称,如"东区1号田"
type: FieldType; // 地块类型
area: number; // 面积(亩)
location: string; // 位置描述,如"湖北省武汉市东西湖区"
latitude?: number; // 纬度坐标(可选,用于地图定位)
longitude?: number; // 经度坐标(可选,用于地图定位)
soilType: string; // 土壤类型,如"黏土"、"沙壤土"
irrigationSystem: IrrigationSystem; // 灌溉系统类型
currentCrop?: CropInfo; // 当前种植的作物(可选)
status: string; // 地块状态:种植中、闲置、准备中
createdAt: number; // 创建时间(时间戳)
lastUpdatedAt: number; // 最后更新时间(时间戳)
notes?: string; // 备注信息(可选)
}
/**
* 农事操作类型枚举
* 定义常见的农事操作
*/
export enum FarmOperationType {
PLOWING = 'plowing', // 耕地 - 翻松土壤
SOWING = 'sowing', // 播种 - 种植作物
FERTILIZING = 'fertilizing', // 施肥 - 提供养分
WATERING = 'watering', // 浇水 - 灌溉
WEEDING = 'weeding', // 除草 - 清除杂草
PEST_CONTROL = 'pest_control', // 病虫害防治
HARVESTING = 'harvesting', // 收获 - 采收作物
OTHER = 'other' // 其他操作
}
/**
* 农事操作记录接口
* 记录一次农事操作的详细信息
*/
export interface FarmOperation {
id: string; // 操作记录唯一标识
fieldId: string; // 关联的地块ID
type: FarmOperationType; // 操作类型
description: string; // 操作描述
operationDate: number; // 操作日期(时间戳)
operator: string; // 操作人姓名
cost?: number; // 操作成本(元,可选)
notes?: string; // 备注信息(可选)
images?: string[]; // 操作图片(可选)
}
/**
* 成本记录接口
* 记录农业生产中的各项成本
*/
export interface CostRecord {
id: string; // 成本记录唯一标识
fieldId: string; // 关联的地块ID
category: string; // 成本类别:种子、肥料、农药、人工、机械等
amount: number; // 金额(元)
date: number; // 发生日期(时间戳)
description: string; // 描述信息
notes?: string; // 备注信息(可选)
}
/**
* 销售记录接口
* 记录农产品的销售信息
*/
export interface SalesRecord {
id: string; // 销售记录唯一标识
fieldId: string; // 关联的地块ID
cropName: string; // 作物名称
quantity: number; // 销售数量(公斤)
unitPrice: number; // 单价(元/公斤)
totalAmount: number; // 总金额(元)
saleDate: number; // 销售日期(时间戳)
buyer?: string; // 买家信息(可选)
notes?: string; // 备注信息(可选)
}
设计要点说明:
| 设计要点 | 说明 |
|---|---|
| 枚举类型 | 使用 enum 定义固定选项,避免输入错误,提高代码可读性 |
| 可选属性 | 使用 ? 标记非必填字段,如 latitude?、longitude? |
| 关联关系 | 通过ID建立关联,如 fieldId 关联地块,避免深层嵌套 |
| 时间存储 | 统一使用时间戳(number类型)存储日期时间,便于计算和排序 |
| 扩展字段 | 预留 notes、images 字段,方便后续扩展功能 |
二、创建地块服务
服务层采用单例模式,负责处理地块相关的业务逻辑和数据操作。这种设计确保了全局只有一个服务实例,便于状态管理和数据一致性。
2.1 创建 FieldService.ets
操作步骤:
- 在
entry/src/main/ets/services/目录下创建新文件 - 命名为
FieldService.ets - 输入以下代码
import { FieldInfo, FarmOperation, CostRecord, SalesRecord } from '../models/ProfessionalAgricultureModels';
import { StorageUtil } from '../utils/StorageUtil';
/**
* 地块统计信息接口
* 用于展示地块的整体情况
*/
export interface FieldStats {
totalFields: number; // 地块总数
totalArea: number; // 总面积(亩)
activeFields: number; // 种植中的地块数量
idleFields: number; // 闲置的地块数量
totalCost: number; // 总成本(元)
totalIncome: number; // 总收入(元)
}
/**
* 地块管理服务
* 采用单例模式,提供地块和农事记录相关的业务逻辑
*/
export class FieldService {
// 单例实例
private static instance: FieldService;
// 私有构造函数,防止外部直接创建实例
private constructor() {}
/**
* 获取单例实例
* @returns FieldService 单例对象
*/
public static getInstance(): FieldService {
if (!FieldService.instance) {
FieldService.instance = new FieldService();
}
return FieldService.instance;
}
/**
* 获取所有地块列表
* @returns Promise<FieldInfo[]> 地块列表
*/
async getAllFields(): Promise<FieldInfo[]> {
// 从本地存储获取地块数据
const fields = await StorageUtil.getObject<FieldInfo[]>('fields_list', []);
return fields || [];
}
/**
* 根据ID获取单个地块信息
* @param id 地块ID
* @returns Promise<FieldInfo | null> 地块信息,未找到返回null
*/
async getFieldById(id: string): Promise<FieldInfo | null> {
const fields = await this.getAllFields();
return fields.find(f => f.id === id) || null;
}
/**
* 添加新地块
* @param field 地块信息对象
* @returns Promise<boolean> 成功返回true,失败返回false
*/
async addField(field: FieldInfo): Promise<boolean> {
try {
// 获取现有地块列表
const fields = await this.getAllFields();
// 添加新地块到列表末尾
fields.push(field);
// 保存到本地存储
await StorageUtil.saveObject('fields_list', fields);
console.info('[FieldService] Field added:', field.id);
return true;
} catch (error) {
console.error('[FieldService] Failed to add field:', error);
return false;
}
}
/**
* 更新地块信息
* @param field 要更新的地块信息对象
* @returns Promise<boolean> 成功返回true,失败返回false
*/
async updateField(field: FieldInfo): Promise<boolean> {
try {
// 获取现有地块列表
const fields = await this.getAllFields();
// 查找要更新的地块索引
const index = fields.findIndex(f => f.id === field.id);
if (index !== -1) {
// 更新地块信息
fields[index] = field;
// 保存到本地存储
await StorageUtil.saveObject('fields_list', fields);
console.info('[FieldService] Field updated:', field.id);
return true;
}
// 未找到对应地块
return false;
} catch (error) {
console.error('[FieldService] Failed to update field:', error);
return false;
}
}
/**
* 删除地块
* @param id 要删除的地块ID
* @returns Promise<boolean> 成功返回true,失败返回false
*/
async deleteField(id: string): Promise<boolean> {
try {
// 获取现有地块列表
const fields = await this.getAllFields();
// 过滤掉要删除的地块
const filteredFields = fields.filter(f => f.id !== id);
// 保存到本地存储
await StorageUtil.saveObject('fields_list', filteredFields);
console.info('[FieldService] Field deleted:', id);
return true;
} catch (error) {
console.error('[FieldService] Failed to delete field:', error);
return false;
}
}
/**
* 获取地块统计信息
* @returns Promise<FieldStats> 统计信息对象
*/
async getFieldStats(): Promise<FieldStats> {
// 获取所有地块
const fields = await this.getAllFields();
// 计算统计数据
const stats: FieldStats = {
totalFields: fields.length, // 地块总数
// 总面积:累加所有地块面积
totalArea: fields.reduce((sum, f) => sum + f.area, 0),
// 种植中:筛选状态为"种植中"的地块数量
activeFields: fields.filter(f => f.status === '种植中').length,
// 闲置:筛选状态为"闲置"的地块数量
idleFields: fields.filter(f => f.status === '闲置').length,
totalCost: 0, // 成本统计(后续实现)
totalIncome: 0 // 收入统计(后续实现)
};
return stats;
}
/**
* 搜索地块
* 根据关键词搜索地块名称、位置或作物
* @param keyword 搜索关键词
* @returns Promise<FieldInfo[]> 搜索结果列表
*/
async searchFields(keyword: string): Promise<FieldInfo[]> {
const fields = await this.getAllFields();
// 如果关键词为空,返回所有地块
if (!keyword) {
return fields;
}
// 过滤匹配的地块
return fields.filter(f =>
f.name.includes(keyword) || // 匹配地块名称
f.location.includes(keyword) || // 匹配位置
(f.currentCrop && f.currentCrop.name.includes(keyword)) // 匹配作物名称
);
}
/**
* 按状态筛选地块
* @param status 地块状态(全部、种植中、闲置、准备中)
* @returns Promise<FieldInfo[]> 筛选结果列表
*/
async filterFieldsByStatus(status: string): Promise<FieldInfo[]> {
const fields = await this.getAllFields();
// 如果状态为"全部"或为空,返回所有地块
if (!status || status === '全部') {
return fields;
}
// 返回指定状态的地块
return fields.filter(f => f.status === status);
}
}
服务设计要点:
| 特性 | 说明 |
|---|---|
| 单例模式 | 确保全局只有一个服务实例,避免数据不一致 |
| 异步方法 | 所有数据操作都是异步的,使用 async/await |
| 错误处理 | try-catch 捕获异常,返回 boolean 表示操作结果 |
| 日志记录 | 使用 console.info/error 记录操作日志,便于调试 |
三、创建地块列表页面
地块列表页面是用户查看和管理地块的主要入口。我们将创建一个包含统计信息、搜索筛选和地块列表的完整页面。
3.1 创建 Management 目录
操作步骤:
- 在
entry/src/main/ets/pages/下创建Management文件夹 - 右键点击
pages文件夹 → New → Directory - 输入
Management,点击 OK
3.2 创建 FieldManagementPage.ets
操作步骤:
- 右键点击
Management文件夹 → New → ArkTS File - 输入文件名
FieldManagementPage,点击 OK - 输入以下代码
import { router } from '@kit.ArkUI';
import { FieldInfo, FieldType, IrrigationSystem, CropType, CropGrowthStage, CropHealthStatus } from '../../models/ProfessionalAgricultureModels';
import { FieldService, FieldStats } from '../../services/FieldService';
import { CommonCard, StatusBadge, InfoRow, EmptyView, LoadingView, getBadgeStyle, BadgeType } from '../../components/CommonComponents';
/**
* 地块管理页面
* 显示地块列表、统计信息,支持搜索和筛选功能
*/
@Entry
@ComponentV2
struct FieldManagementPage {
// 状态变量:地块列表
@Local fields: FieldInfo[] = [];
// 状态变量:筛选后的地块列表
@Local filteredFields: FieldInfo[] = [];
// 状态变量:统计信息
@Local stats: FieldStats | null = null;
// 状态变量:加载状态
@Local isLoading: boolean = true;
// 状态变量:搜索关键词
@Local searchKeyword: string = '';
// 状态变量:选中的状态筛选
@Local selectedStatus: string = '全部';
// ���务实例
private fieldService = FieldService.getInstance();
// 状态选项
private statusOptions: string[] = ['全部', '种植中', '闲置', '准备中'];
/**
* 页面即将出现时加载数据
*/
async aboutToAppear(): Promise<void> {
await this.loadData();
}
build() {
Column() {
// 标题栏
Row() {
Text('地块管理')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor($r('app.color.text_primary'))
.layoutWeight(1)
Button('添加地块')
.fontSize(14)
.backgroundColor($r('app.color.primary_professional'))
.borderRadius(8)
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.onClick(() => {
this.onAddField();
})
}
.width('100%')
.padding(16)
// 统计卡片
if (this.stats) {
this.buildStatsCard()
}
// 搜索和筛选区域
Column({ space: 12 }) {
// 搜索框
TextInput({ placeholder: '搜索地块名称、位置或作物' })
.onChange((value: string) => {
// 用户输入时更新关键词并筛选
this.searchKeyword = value;
this.filterFields();
})
// 状态筛选按钮组
Row({ space: 8 }) {
ForEach(this.statusOptions, (status: string) => {
Button(status)
.fontSize(14)
// 选中状态显示主题色,未选中显示背景色
.backgroundColor(this.selectedStatus === status ?
$r('app.color.primary_professional') : $r('app.color.background'))
// 选中状态文字白色,未选中显示文字色
.fontColor(this.selectedStatus === status ?
Color.White : $r('app.color.text_primary'))
.borderRadius(16)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.onClick(() => {
// 点击切换筛选状态
this.selectedStatus = status;
this.filterFields();
})
})
}
}
.width('100%')
.padding({ left: 16, right: 16 })
// 地块列表区域
if (this.isLoading) {
// 加载中状态
LoadingView({ message: '加载中...' })
.layoutWeight(1)
} else if (this.filteredFields.length === 0) {
// 空状态提示
EmptyView({
icon: '🌾',
message: '还没有地块数据',
actionText: '添加地块',
onAction: () => {
this.onAddField();
}
})
.layoutWeight(1)
} else {
// 地块列表
List({ space: 12 }) {
ForEach(this.filteredFields, (field: FieldInfo) => {
ListItem() {
this.buildFieldCard(field)
}
})
}
.width('100%')
.layoutWeight(1)
.padding(16)
}
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.background'))
}
/**
* 构建统计卡片
* 显示地块总数、总面积、种植中数量、闲置数量
*/
@Builder
buildStatsCard() {
CommonCard({ title: '统计概览', icon: '📊' }) {
Row({ space: 16 }) {
this.buildStatItem('地块总数', `${this.stats!.totalFields}`, '个')
this.buildStatItem('总面积', `${this.stats!.totalArea.toFixed(1)}`, '亩')
this.buildStatItem('种植中', `${this.stats!.activeFields}`, '个')
this.buildStatItem('闲置', `${this.stats!.idleFields}`, '个')
}
.width('100%')
}
.margin({ left: 16, right: 16, bottom: 12 })
}
/**
* 构建单个统计项
* @param label 标签文字
* @param value 数值
* @param unit 单位
*/
@Builder
buildStatItem(label: string, value: string, unit: string) {
Column({ space: 4 }) {
// 标签
Text(label)
.fontSize(12)
.fontColor($r('app.color.text_secondary'))
// 数值和单位
Row({ space: 4 }) {
Text(value)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor($r('app.color.primary_professional'))
Text(unit)
.fontSize(12)
.fontColor($r('app.color.text_secondary'))
}
}
.layoutWeight(1)
}
/**
* 构建地块卡片
* @param field 地块信息
*/
@Builder
buildFieldCard(field: FieldInfo) {
CommonCard({
onClick: () => {
this.onFieldClick(field);
}
}) {
Column({ space: 12 }) {
// 标题行:地块名称 + 状态标签
Row() {
Text(field.name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor($r('app.color.text_primary'))
.layoutWeight(1)
// 状态徽章
StatusBadge({
text: field.status,
textColor: this.getStatusColor(field.status).textColor,
backgroundColor: this.getStatusColor(field.status).backgroundColor
})
}
.width('100%')
// 信息行
InfoRow({ label: '面积', value: `${field.area} 亩`, icon: '📏' })
InfoRow({ label: '位置', value: field.location, icon: '📍' })
// 如果有作物,显示作物信息
if (field.currentCrop) {
InfoRow({
label: '当前作物',
value: field.currentCrop.name,
icon: '🌱'
})
}
}
}
}
/**
* 根据状态获取颜色样式
* @param status 地块状态
* @returns 文字颜色和背景颜色
*/
private getStatusColor(status: string): { textColor: string, backgroundColor: string } {
switch (status) {
case '种植中':
return getBadgeStyle(BadgeType.SUCCESS); // 绿色
case '闲置':
return getBadgeStyle(BadgeType.INFO); // 蓝色
case '准备中':
return getBadgeStyle(BadgeType.WARNING); // 橙色
default:
return getBadgeStyle(BadgeType.DEFAULT); // 灰色
}
}
/**
* 加载数据
* 获取地块列表和统计信息
*/
async loadData(): Promise<void> {
try {
this.isLoading = true;
// 获取所有地块
this.fields = await this.fieldService.getAllFields();
// 获取统计信息
this.stats = await this.fieldService.getFieldStats();
// 应用筛选
this.filterFields();
} catch (error) {
console.error('[FieldManagementPage] Load data failed:', error);
} finally {
this.isLoading = false;
}
}
/**
* 筛选地块
* 根据状态和关键词进行筛选
*/
filterFields(): void {
let result = this.fields;
// 按状态筛选
if (this.selectedStatus !== '全部') {
result = result.filter(f => f.status === this.selectedStatus);
}
// 按关键词搜索
if (this.searchKeyword) {
result = result.filter(f =>
f.name.includes(this.searchKeyword) ||
f.location.includes(this.searchKeyword) ||
(f.currentCrop && f.currentCrop.name.includes(this.searchKeyword))
);
}
this.filteredFields = result;
}
/**
* 添加地块按钮点击事件
*/
onAddField(): void {
router.pushUrl({
url: 'pages/Management/AddFieldPage'
});
}
/**
* 地块卡片点击事件
* @param field 被点击的地块
*/
onFieldClick(field: FieldInfo): void {
router.pushUrl({
url: 'pages/Management/FieldDetailPage',
params: { fieldId: field.id }
});
}
}
页面结构说明:
┌─────────────────────────────────────┐
│ 地块管理 [+ 添加地块] │ ← 标题栏
├─────────────────────────────────────┤
│ 📊 统计概览 │ ← 统计卡片
│ [10] 个 [120.5] 亩 [6] 种植 [4] │
├─────────────────────────────────────┤
│ [搜索框] │ ← 搜索区域
│ [全部] [种植中] [闲置] [准备中] │ ← 筛选按钮
├─────────────────────────────────────┤
│ ┌─────────────────────────────┐ │
│ │ 东区1号田 [种植中] │ │ ← 地块卡片
│ │ 📏 面积: 10.5 亩 │ │
│ │ 📍 位置: 湖北省武汉市... │ │
│ │ 🌱 当前作物: 水稻 │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ 西区果园 [闲置] │ │
│ │ 📏 面积: 15.0 亩 │ │
│ │ 📍 位置: 湖北省武汉市... │ │
│ └─────────────────────────────┘ │
│ ... │
└─────────────────────────────────────┘
四、配置页面路由
在 HarmonyOS 中,所有可访问的页面都需要在路由配置文件中注册。
4.1 修改 main_pages.json
操作步骤:
- 打开
entry/src/main/resources/base/profile/main_pages.json - 在
src数组中添加新页面路由
{
"src": [
"pages/WelcomePage",
"pages/Index",
"pages/Map/FieldMapPage",
"pages/Management/FieldManagementPage",
"pages/OnboardingFlow/ModeSelectionPage",
"pages/OnboardingFlow/LocationPage",
"pages/OnboardingFlow/GoalsPage"
]
}
路由配置说明:
- 每个页面路径必须以
pages/开头 - 路径与文件系统的目录结构对应
- 页面按注册顺序可用于返回栈导航
五、添加测试数据
为了方便测试和演示,我们添加一些测试数据。
5.1 在 FieldManagementPage 中添加测试数据方法
操作步骤:
在 FieldManagementPage.ets 中的 aboutToAppear 方法前添加以下代码:
/**
* 添加测试数据
* 仅在首次运行时添加,用于演示和测试
*/
async addTestData(): Promise<void> {
// 检查是否已有数据
const fields = await this.fieldService.getAllFields();
if (fields.length === 0) {
// 创建测试地块数据
const testFields: FieldInfo[] = [
{
id: '1',
name: '东区1号田',
type: FieldType.FARMLAND,
area: 10.5,
location: '湖北省武汉市东西湖区',
latitude: 30.6228,
longitude: 114.1377,
soilType: '黏土',
irrigationSystem: IrrigationSystem.DRIP,
status: '种植中',
createdAt: Date.now(),
lastUpdatedAt: Date.now(),
currentCrop: {
id: 'c1',
name: '水稻',
type: CropType.GRAIN,
variety: '杂交稻',
plantingDate: Date.now() - 30 * 24 * 60 * 60 * 1000, // 30天前种植
expectedHarvestDate: Date.now() + 90 * 24 * 60 * 60 * 1000, // 90天后收获
growthStage: CropGrowthStage.GROWING,
healthStatus: CropHealthStatus.HEALTHY,
area: 10.5,
expectedYield: 5000
}
},
{
id: '2',
name: '西区果园',
type: FieldType.ORCHARD,
area: 15.0,
location: '湖北省武汉市江夏区',
soilType: '沙壤土',
irrigationSystem: IrrigationSystem.SPRINKLER,
status: '闲置',
createdAt: Date.now(),
lastUpdatedAt: Date.now()
},
{
id: '3',
name: '南区蔬菜大棚',
type: FieldType.GREENHOUSE,
area: 3.5,
location: '湖北省武汉市蔡甸区',
soilType: '壤土',
irrigationSystem: IrrigationSystem.DRIP,
status: '种植中',
createdAt: Date.now(),
lastUpdatedAt: Date.now(),
currentCrop: {
id: 'c2',
name: '番茄',
type: CropType.VEGETABLE,
variety: '大红番茄',
plantingDate: Date.now() - 45 * 24 * 60 * 60 * 1000,
expectedHarvestDate: Date.now() + 15 * 24 * 60 * 60 * 1000,
growthStage: CropGrowthStage.FRUITING,
healthStatus: CropHealthStatus.HEALTHY,
area: 3.5,
expectedYield: 8000
}
},
{
id: '4',
name: '北区准备田',
type: FieldType.FARMLAND,
area: 8.0,
location: '湖北省武汉市黄陂区',
soilType: '黏土',
irrigationSystem: IrrigationSystem.NONE,
status: '准备中',
createdAt: Date.now(),
lastUpdatedAt: Date.now()
}
];
// 逐个添加地块到存储
for (const field of testFields) {
await this.fieldService.addField(field);
}
console.info('[FieldManagementPage] Test data added:', testFields.length);
}
}
然后修改 aboutToAppear 方法,添加测试数据调用:
async aboutToAppear(): Promise<void> {
await this.addTestData(); // 先添加测试数据
await this.loadData(); // 再加载数据
}
六、运行与测试
6.1 测试步骤
-
启动应用
- 点击 DevEco Studio 工具栏的运行按钮
- 或按快捷键
Shift + F10
-
进入地块管理页面
- 完成引导流程后,在首页找到地块管理入口
- 或直接在地址栏输入页面路径
-
查看统计信息
- 页面顶部应显示统计卡片
- 检查地块总数、总面积等数据是否正确
-
测试搜索功能
- 在搜索框输入"水稻"
- 列表应只显示含有"水稻"的地块
- 清空搜索框,列表应恢复显示全部
-
测试状态筛选
- 点击"种植中"按钮
- 列表应只显示状态为"种植中"的地块
- 点击"全部"按钮,列表应显示所有地块
-
测试地块点击
- 点击任意地块卡片
- 应跳转到地块详情页面(后续实现)
6.2 预期效果
| 功能 | 预期效果 |
|---|---|
| 统计卡片 | 显示 4 个地块,37.0 亩总面积,2 个种植中,1 个闲置 |
| 搜索功能 | 输入关键词后实时筛选结果 |
| 筛选功能 | 点击状态按钮后切换显示对应地块 |
| 空状态 | 删除所有数据后显示空状态提示 |
| 卡片点击 | 点击后有响应(跳转详情页) |
七、常见问题与解决方案
7.1 页面无法加载
问题:运行后页面显示白屏或报错
解决方案:
- 检查
main_pages.json是否正确配置页面路由 - 确认文件路径与路由配置一致
- 查看控制台日志,确认具体错误信息
7.2 组件找不到
问题:编译报错 “Cannot find module”
解决方案:
- 确认
CommonComponents.ets已在第3篇中创建 - 检查 import 路径是否正确
- 尝试重新构建项目:
Build → Rebuild Project
7.3 数据不显示
问题:页面正常但列表为空
解决方案:
- 确认
addTestData()方法被调用 - 检查
StorageUtil是否正确初始化 - 使用开发者工具查看本地存储
7.4 筛选功能不生效
问题:点击筛选按钮后列表没有变化
解决方案:
- 检查
filterFields()方法中的筛选逻辑 - 确认状态字符串匹配(注意中文字符)
- 在方法中添加 console.log 调试
八、总结
本篇教程完成了:
- ✅ 地块数据模型设计(枚举、接口)
- ✅ 地块服务实现(单例模式、CRUD操作)
- ✅ 地块列表页面(统计、搜索、筛选)
- ✅ 测试数据添加
- ✅ 页面路由配置
关键技术点:
| 技术点 | 说明 |
|---|---|
| 数据模型 | 使用 enum 和 interface 定义清晰的数据结构 |
| 单例模式 | 服务层采用单例确保全局唯一实例 |
| 组件复用 | 使用 @Builder 构建可复用的UI组件 |
| 状态管理 | 使用 @Local 管理页面状态 |
| 数据持久化 | 通过 StorageUtil 实现本地数据存储 |
九、下一步
在下一篇教程中,我们将学习:
- 地块添加页面实现
- 地块编辑页面实现
- 地块详情页面实现
- 表单验证与错误处理
- 地图选择位置功能
准备工作:
- 熟悉 ArkTS 表单组件(TextInput、Select 等)
- 了解表单验证的基本方法
- 准备测试地块数据
参考资料
教程版本:v1.0
更新日期:2026-01
适用版本:DevEco Studio 5.0+, HarmonyOS API 17+
更多推荐
所有评论(0)