HarmonyOS ArkTS 实战:从零实现校园二手交易平台应用

优化策略:完整可运行项目 + 分层服务 + 数据持久化 + 多页面路由 + 图片选择(模拟) + 收藏/搜索/筛选 + 统计图表 + 消息提醒


目录


一、项目背景与效果预览

1.1 痛点场景

毕业季书籍、电子产品、二手家具……校园内闲置物品交易需求旺盛,但缺乏一个安全、便捷、信息集中的平台。传统群聊交易信息混乱、无信用评价、无交易记录。本项目打造一个校园二手交易平台,涵盖商品发布、分类浏览、关键词搜索、收藏、私信(模拟)、交易记录、统计等功能,让校园资源循环起来。

1.2 运行效果(模拟器预览)

  • 主界面:顶部为应用标题 + 搜索框(点击跳转搜索页);中部为分类标签(全部/数码/书籍/衣物/其他),下方为商品网格(2列),每个商品卡片显示缩略图(颜色块模拟)、标题、价格、发布时间、收藏按钮。
  • 发布界面:点击底部浮动“+”按钮弹出发布表单(商品标题、分类、描述、价格、图片(模拟)、联系方式)。
  • 详情界面:点击商品进入详情,显示完整信息,可收藏、联系卖家(模拟弹出对话框)。
  • 我的页面(底部Tab切换):显示发布记录、交易记录、收藏列表、统计图表(发布量、成交量)。
  • 交互反馈:发布成功Toast、收藏状态切换动画、搜索实时过滤。

由于篇幅限制,本文聚焦主页面及核心功能,完整多页面路由代码见项目附件。


二、技术栈与开发环境

技术项 说明
开发语言 ArkTS
UI 框架 ArkUI 声明式开发
状态管理 @State / @Provide / @Consume
布局方式 Grid + List + Tabs(底部导航)
数据持久化 @ohos.data.preferences
路由管理 @ohos.router
图表组件 @ohos.arkui.advanced.Chart
弹窗/提示 @ohos.prompt / @ohos.dialog
开发工具 DevEco Studio 5.0+
SDK 版本 API 24 及以上

三、需求分析与功能架构

3.1 核心功能清单(用户故事)

  1. 商品浏览:默认显示全部商品,可按分类过滤,支持关键词搜索(标题/描述)。
  2. 发布商品:填写标题、分类、价格、描述、联系方式,并上传图片(模拟本地资源),发布后显示在列表中。
  3. 商品详情:点击商品卡片进入详情页,查看完整信息,可收藏/取消收藏。
  4. 收藏管理:在“我的”页面查看收藏的商品列表。
  5. 交易记录:记录用户发布和成交的商品,统计总发布量、成交量、成交额。
  6. 消息提醒:模拟买家咨询(点击“联系卖家”弹出对话框)。
  7. 数据统计:以图表展示近7天发布量和成交量趋势。

3.2 数据流向

发布表单 → 商品列表(持久化) → 详情(收藏状态) → 我的页面(统计刷新)

四、数据结构与服务层设计

4.1 数据模型

// model/Goods.ets
export interface Goods {
  id: number;
  title: string;
  category: string;      // 数码/书籍/衣物/其他
  description: string;
  price: number;
  images: string[];      // 图片资源名,模拟
  contact: string;       // 微信/手机
  publishTime: string;   // ISO字符串
  sellerId: string;      // 学号
  status: '在售' | '已下架' | '已成交';
  isFavorited: boolean;  // 当前用户是否收藏
}

// model/TradeRecord.ets
export interface TradeRecord {
  id: number;
  goodsId: number;
  buyerId: string;
  sellerId: string;
  dealPrice: number;
  dealTime: string;
}

4.2 服务层(Service)

沿用 BaseService 模式,实现 GoodsServiceTradeService。以 GoodsService 为例:

// service/GoodsService.ets
import { BaseService } from './BaseService';
import { Goods } from '../model/Goods';

class GoodsService extends BaseService<Goods> {
  constructor() {
    super('TradePrefs', 'goods');
  }

  async fetch(): Promise<Goods[]> {
    await this.simulateDelay(200);
    const data = await this.loadData();
    if (data.length === 0) {
      const mock = this.getMockData();
      await this.saveData(mock);
      return mock;
    }
    return data;
  }

  async add(item: Goods): Promise<Goods[]> {
    const list = await this.loadData();
    list.unshift(item); // 最新在上面
    await this.saveData(list);
    return list;
  }

  async update(id: number, newItem: Goods): Promise<Goods[]> {
    const list = await this.loadData();
    const idx = list.findIndex(g => g.id === id);
    if (idx !== -1) {
      list[idx] = newItem;
      await this.saveData(list);
    }
    return list;
  }

  async delete(id: number): Promise<Goods[]> {
    const list = await this.loadData();
    const filtered = list.filter(g => g.id !== id);
    await this.saveData(filtered);
    return filtered;
  }

  private getMockData(): Goods[] {
    const now = Date.now();
    return [
      { id: 1, title: '二手iPhone 13', category: '数码', description: '9成新,256G,无维修', price: 3500, images: ['img_phone'], contact: 'wx123', publishTime: new Date(now - 86400000 * 2).toISOString(), sellerId: '2022001', status: '在售', isFavorited: false },
      { id: 2, title: '高等数学教材', category: '书籍', description: '全新,带习题答案', price: 25, images: ['img_book'], contact: 'wx456', publishTime: new Date(now - 3600000 * 5).toISOString(), sellerId: '2022002', status: '在售', isFavorited: false },
      { id: 3, title: '自行车', category: '其他', description: '山地车,骑了一年', price: 500, images: ['img_bike'], contact: 'wx789', publishTime: new Date(now - 86400000).toISOString(), sellerId: '2022003', status: '在售', isFavorited: false },
    ];
  }

  private simulateDelay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

export const goodsService = new GoodsService();

TradeService 类似,用于记录成交数据。


五、核心功能实现(完整代码)

5.1 页面状态与数据加载(主页面 Index.ets)

采用底部 Tabs 实现“首页”和“我的”两个页面(简化),实际可拆分为独立页面,但为方便展示,用 Tabs 组件实现。

// pages/Index.ets
import { Goods } from '../model/Goods';
import { TradeRecord } from '../model/TradeRecord';
import { goodsService } from '../service/GoodsService';
import { tradeService } from '../service/TradeService';
import prompt from '@ohos.prompt';
import router from '@ohos.router';
import { Chart, ChartType } from '@ohos.arkui.advanced';

@Entry
@Component
struct Index {
  @State goodsList: Goods[] = [];
  @State filteredList: Goods[] = [];
  @State currentCategory: string = '全部';
  @State searchKeyword: string = '';
  @State myFavorites: Goods[] = [];
  @State myPublished: Goods[] = [];
  @State tradeRecords: TradeRecord[] = [];
  @State totalDealCount: number = 0;
  @State totalDealAmount: number = 0;
  @State isLoading: boolean = true;
  @State currentTabIndex: number = 0; // 0首页 1我的

  // 发布弹窗
  @State isPublishDialogVisible: boolean = false;
  @State newTitle: string = '';
  @State newCategory: string = '数码';
  @State newDesc: string = '';
  @State newPrice: string = '';
  @State newContact: string = '';
  @State newImage: string = 'default_img';

  // 搜索跳转
  private searchInput: string = '';

  aboutToAppear() {
    this.loadData();
  }

  async loadData() {
    this.isLoading = true;
    try {
      this.goodsList = await goodsService.fetch();
      this.tradeRecords = await tradeService.fetch();
      this.applyFilter();
      this.calcStats();
      this.loadMyData();
    } catch (e) {
      prompt.showToast({ message: '数据加载失败' });
    } finally {
      this.isLoading = false;
    }
  }

  private applyFilter() {
    let list = this.goodsList;
    if (this.currentCategory !== '全部') {
      list = list.filter(g => g.category === this.currentCategory);
    }
    if (this.searchKeyword.trim()) {
      const kw = this.searchKeyword.trim().toLowerCase();
      list = list.filter(g => g.title.toLowerCase().includes(kw) || g.description.toLowerCase().includes(kw));
    }
    this.filteredList = list;
  }

  private calcStats() {
    const deals = this.tradeRecords;
    this.totalDealCount = deals.length;
    this.totalDealAmount = deals.reduce((sum, r) => sum + r.dealPrice, 0);
  }

  private loadMyData() {
    // 假设当前用户ID为 'me'
    this.myPublished = this.goodsList.filter(g => g.sellerId === 'me' && g.status === '在售');
    this.myFavorites = this.goodsList.filter(g => g.isFavorited);
  }

  // 搜索事件
  private onSearch(keyword: string) {
    this.searchKeyword = keyword;
    this.applyFilter();
  }

  // 分类切换
  private onCategoryChange(cat: string) {
    this.currentCategory = cat;
    this.applyFilter();
  }

  // ... 其他方法
}

5.2 发布商品(含分类、价格、图片模拟)

点击底部浮动按钮弹出发布表单。

private openPublishDialog() {
  this.newTitle = '';
  this.newCategory = '数码';
  this.newDesc = '';
  this.newPrice = '';
  this.newContact = '';
  this.isPublishDialogVisible = true;
}

private async confirmPublish() {
  if (!this.newTitle.trim() || !this.newPrice.trim()) {
    prompt.showToast({ message: '标题和价格必填' });
    return;
  }
  const price = parseFloat(this.newPrice);
  if (isNaN(price) || price <= 0) {
    prompt.showToast({ message: '请输入有效价格' });
    return;
  }
  const goods: Goods = {
    id: Date.now(),
    title: this.newTitle.trim(),
    category: this.newCategory,
    description: this.newDesc.trim() || '无描述',
    price: price,
    images: [this.newImage], // 可多张,简化
    contact: this.newContact.trim() || '未提供',
    publishTime: new Date().toISOString(),
    sellerId: 'me',
    status: '在售',
    isFavorited: false
  };
  try {
    this.goodsList = await goodsService.add(goods);
    this.applyFilter();
    prompt.showToast({ message: '发布成功!' });
    this.isPublishDialogVisible = false;
    this.loadMyData();
  } catch (e) {
    prompt.showToast({ message: '发布失败' });
  }
}

发布弹窗 UI(使用 dialog):

@Builder PublishDialogContent() {
  Column() {
    Text('发布商品').fontSize(18).fontWeight(FontWeight.Bold).margin(12);
    TextInput({ placeholder: '标题', text: this.newTitle }).onChange(v => this.newTitle = v).margin(6);
    Row() {
      Text('分类').width(60);
      Select([
        { value: '数码' }, { value: '书籍' }, { value: '衣物' }, { value: '其他' }
      ]).selected(0).onSelect((index) => {
        this.newCategory = ['数码','书籍','衣物','其他'][index];
      }).width(150);
    }.margin(6);
    TextArea({ placeholder: '描述', text: this.newDesc }).onChange(v => this.newDesc = v).height(80).margin(6);
    TextInput({ placeholder: '价格(元)', text: this.newPrice }).type(InputType.Number).onChange(v => this.newPrice = v).margin(6);
    TextInput({ placeholder: '联系方式', text: this.newContact }).onChange(v => this.newContact = v).margin(6);
    // 图片选择模拟:直接使用默认
    Row() {
      Text('图片:').width(60);
      Button('选择图片(模拟)').onClick(() => {
        prompt.showToast({ message: '已选择默认图片' });
        this.newImage = 'default_img';
      }).height(30).fontSize(12);
    }.margin(6);
    Row() {
      Button('取消').onClick(() => this.isPublishDialogVisible = false).backgroundColor('#999');
      Button('发布').onClick(() => this.confirmPublish()).backgroundColor('#166534').margin({ left: 20 });
    }.margin(16);
  }
  .padding(20)
  .width('90%')
  .backgroundColor('#FFF')
  .borderRadius(16);
}

5.3 商品列表与搜索筛选

商品网格展示,并支持分类标签。

@Builder CategoryTabs() {
  Row({ space: 10 }) {
    ForEach(['全部', '数码', '书籍', '衣物', '其他'], (cat: string) => {
      Text(cat)
        .fontSize(14)
        .fontColor(this.currentCategory === cat ? '#FFF' : '#1C1917')
        .padding({ left: 14, right: 14, top: 6, bottom: 6 })
        .backgroundColor(this.currentCategory === cat ? '#166534' : '#F5F5F4')
        .borderRadius(16)
        .onClick(() => this.onCategoryChange(cat))
    })
  }
  .width('100%')
  .padding({ left: 16, right: 16, top: 8 })
}

@Builder GoodsGrid() {
  Grid() {
    ForEach(this.filteredList, (goods: Goods) => {
      GridItem() {
        Column() {
          // 图片占位
          Row()
            .width('100%')
            .height(120)
            .backgroundColor('#E5E7EB')
            .borderRadius({ topLeft: 8, topRight: 8 })
            .overlay(Text('📷').fontSize(30).fontColor('#999'))
          Column() {
            Text(goods.title)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
              .maxLines(1)
              .textOverflow({ overflow: TextOverflow.Ellipsis })
              .width('100%')
            Row() {
              Text(`¥${goods.price}`)
                .fontSize(16)
                .fontColor('#166534')
                .fontWeight(FontWeight.Bold)
              Blank()
              // 收藏按钮
              Image(goods.isFavorited ? $r('app.media.ic_favorite_filled') : $r('app.media.ic_favorite_outline'))
                .width(20).height(20)
                .onClick(() => this.toggleFavorite(goods.id))
            }
            .width('100%')
            .margin({ top: 4 })
            Text(goods.publishTime.slice(0,10))
              .fontSize(10)
              .fontColor('#999')
              .width('100%')
              .margin({ top: 2 })
          }
          .padding(10)
          .alignItems(HorizontalAlign.Start)
        }
        .width('100%')
        .backgroundColor('#FFF')
        .borderRadius(8)
        .onClick(() => {
          // 跳转详情页,传递商品ID
          router.pushUrl({ url: 'pages/Detail', params: { goodsId: goods.id } });
        })
      }
    })
  }
  .columnsTemplate('1fr 1fr')
  .columnsGap(10)
  .rowsGap(10)
  .width('100%')
  .padding(10)
  .layoutWeight(1)
}

收藏切换逻辑:

private async toggleFavorite(goodsId: number) {
  const goods = this.goodsList.find(g => g.id === goodsId);
  if (!goods) return;
  const updated = { ...goods, isFavorited: !goods.isFavorited };
  try {
    this.goodsList = await goodsService.update(goodsId, updated);
    this.applyFilter();
    this.loadMyData();
    prompt.showToast({ message: updated.isFavorited ? '已收藏' : '已取消收藏' });
  } catch (e) {
    prompt.showToast({ message: '操作失败' });
  }
}

5.4 商品详情与联系卖家(模拟)

详情页 Detail.ets(简化)接收参数,展示完整信息,并提供“联系卖家”按钮弹出对话框。

// pages/Detail.ets
import router from '@ohos.router';
import { Goods } from '../model/Goods';
import { goodsService } from '../service/GoodsService';
import prompt from '@ohos.prompt';

@Entry
@Component
struct Detail {
  @State goods: Goods | null = null;
  private goodsId: number = -1;

  aboutToAppear() {
    const params = router.getParams() as { goodsId: number };
    if (params) {
      this.goodsId = params.goodsId;
      this.loadGoods();
    }
  }

  async loadGoods() {
    const list = await goodsService.fetch();
    this.goods = list.find(g => g.id === this.goodsId) || null;
  }

  build() {
    Column() {
      if (this.goods) {
        // 图片
        Row().width('100%').height(200).backgroundColor('#E5E7EB')
          .overlay(Text('📷').fontSize(50).fontColor('#999'));
        Column({ space: 8 }) {
          Text(this.goods.title).fontSize(20).fontWeight(FontWeight.Bold).width('100%');
          Text(`分类:${this.goods.category}`).fontSize(14).width('100%');
          Text(`价格:¥${this.goods.price}`).fontSize(18).fontColor('#166534').width('100%');
          Text(`描述:${this.goods.description}`).fontSize(14).width('100%');
          Text(`联系方式:${this.goods.contact}`).fontSize(14).width('100%');
          Text(`发布时间:${this.goods.publishTime.slice(0,16)}`).fontSize(12).fontColor('#999').width('100%');
          Row() {
            Button('收藏').onClick(() => {
              // 调用父页面的收藏方法,可通过全局事件或返回刷新
              prompt.showToast({ message: '收藏功能已在列表实现' });
            }).backgroundColor('#166534');
            Button('联系卖家').onClick(() => {
              prompt.showDialog({
                title: '联系卖家',
                message: `卖家联系方式:${this.goods?.contact || '未提供'}`,
                buttons: [{ text: '复制', color: '#166534' }, { text: '关闭' }]
              });
            }).backgroundColor('#CA8A04');
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceAround)
          .margin({ top: 16 });
        }
        .padding(16)
        .alignItems(HorizontalAlign.Start)
      } else {
        Text('商品不存在').fontSize(16).margin(20);
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFF')
  }
}

5.5 交易记录与消息通知

在“我的”页面展示发布列表和交易记录。由于Tabs切换,我们在主页面 Index.etsbuild 中增加 Tabs

// 在build中替换之前的内容,使用Tabs
build() {
  Column() {
    // 搜索栏(首页显示)
    if (this.currentTabIndex === 0) {
      Row() {
        TextInput({ placeholder: '搜索商品...', text: this.searchKeyword })
          .onChange(v => this.onSearch(v))
          .layoutWeight(1)
          .height(40)
          .backgroundColor('#F5F5F4')
          .borderRadius(20)
          .padding({ left: 12 })
        Image($r('app.media.ic_search')).width(24).height(24).margin({ left: 8 });
      }
      .width('90%')
      .margin({ top: 8 })
      .padding({ left: 16, right: 16 });
    }

    Tabs({ barPosition: BarPosition.End }) {
      TabContent() {
        Column() {
          if (this.isLoading) LoadingProgress().color('#166534');
          else {
            this.CategoryTabs();
            this.GoodsGrid();
          }
        }
        .width('100%')
        .height('100%')
        .backgroundColor('#F9FAFB')
      }
      .tabBar('🏠 首页')

      TabContent() {
        Column() {
          // 统计卡片
          this.StatsCard();
          // 我的发布
          Text('我的发布').fontSize(16).fontWeight(FontWeight.Medium).margin(12).alignSelf(ItemAlign.Start);
          List() {
            ForEach(this.myPublished, (g: Goods) => {
              ListItem() {
                Row() {
                  Text(g.title).layoutWeight(1);
                  Text(`¥${g.price}`).margin({ right: 10 });
                  Button('下架').onClick(() => this.takeOff(g.id)).height(28).fontSize(12).backgroundColor('#DC2626');
                }
                .padding(10)
                .width('100%')
                .border({ width: { bottom: 1 }, color: '#F0F0F0' })
              }
            })
          }
          .width('100%')
          .height(200)

          // 交易记录
          Text('交易记录').fontSize(16).fontWeight(FontWeight.Medium).margin(12).alignSelf(ItemAlign.Start);
          List() {
            ForEach(this.tradeRecords, (r: TradeRecord) => {
              ListItem() {
                Row() {
                  Text(`成交价¥${r.dealPrice}`).layoutWeight(1);
                  Text(r.dealTime.slice(0,10));
                }
                .padding(10)
                .width('100%')
              }
            })
          }
          .width('100%')
          .height(150)
        }
        .width('100%')
        .height('100%')
        .padding(10)
      }
      .tabBar('👤 我的')
    }
    .width('100%')
    .layoutWeight(1)

    // 发布浮动按钮
    if (this.currentTabIndex === 0) {
      Button('+')
        .width(56).height(56)
        .borderRadius(28)
        .backgroundColor('#166534')
        .fontSize(32)
        .fontColor('#FFF')
        .position({ x: '90%', y: '85%' })
        .onClick(() => this.openPublishDialog())
    }
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#FFF')
  .dialog($$this.isPublishDialogVisible, this.PublishDialogContent())
}

下架方法:

private async takeOff(goodsId: number) {
  const goods = this.goodsList.find(g => g.id === goodsId);
  if (!goods) return;
  const updated = { ...goods, status: '已下架' };
  try {
    this.goodsList = await goodsService.update(goodsId, updated);
    this.loadMyData();
    prompt.showToast({ message: '已下架' });
  } catch (e) {}
}

5.6 数据统计图表

在“我的”页面顶部添加统计卡片和趋势图。

@Builder StatsCard() {
  Column() {
    Row() {
      Column() {
        Text('发布数').fontSize(12).fontColor('#78716C');
        Text(`${this.myPublished.length}`).fontSize(20).fontWeight(FontWeight.Bold);
      }.layoutWeight(1).alignItems(HorizontalAlign.Center);
      Column() {
        Text('成交数').fontSize(12).fontColor('#78716C');
        Text(`${this.totalDealCount}`).fontSize(20).fontWeight(FontWeight.Bold);
      }.layoutWeight(1).alignItems(HorizontalAlign.Center);
      Column() {
        Text('成交额').fontSize(12).fontColor('#78716C');
        Text(`¥${this.totalDealAmount.toFixed(2)}`).fontSize(20).fontWeight(FontWeight.Bold);
      }.layoutWeight(1).alignItems(HorizontalAlign.Center);
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#F5F5F4')
    .borderRadius(12);

    // 近7天成交量趋势
    if (this.tradeRecords.length > 0) {
      const dates = this.getWeeklyTradeData();
      Chart({
        type: ChartType.Line,
        datasets: [{
          data: dates.counts,
          color: '#166534',
          strokeWidth: 2,
          pointStyle: { shape: 'circle', size: 4 }
        }],
        options: {
          xAxis: { labels: dates.dates.map(d => d.slice(5)), color: '#999' },
          yAxis: { min: 0, step: 1, color: '#999' }
        }
      }).width('100%').height(80).margin({ top: 8 });
    }
  }
  .width('100%')
  .margin({ bottom: 12 })
}

private getWeeklyTradeData(): { dates: string[], counts: number[] } {
  const result: { [date: string]: number } = {};
  const now = new Date();
  for (let i = 6; i >= 0; i--) {
    const d = new Date(now);
    d.setDate(d.getDate() - i);
    const key = d.toISOString().slice(0, 10);
    result[key] = 0;
  }
  this.tradeRecords.forEach(r => {
    const date = r.dealTime.slice(0, 10);
    if (result[date] !== undefined) result[date]++;
  });
  const dates = Object.keys(result).sort();
  const counts = dates.map(d => result[d]);
  return { dates, counts };
}

5.7 数据持久化(Preferences)

已在 BaseService 中实现,所有数据改动自动保存。


六、UI 界面设计与实现(完整组件)

6.1 顶部标题与搜索入口

见上述 build 中的搜索栏,在首页显示。

6.2 分类筛选标签

CategoryTabs 构建器。

6.3 商品网格列表

GoodsGrid 构建器。

6.4 发布商品浮动按钮

见底部 Button('+')

6.5 我的收藏与交易记录页面(路由)

由于篇幅,收藏列表可在“我的”页面增加一个区域,或独立页面。此处我们在“我的”中展示我的发布和交易记录,并可通过点击“我的收藏”跳转。

// 在“我的”Tab中添加收藏入口
Row() {
  Text('我的收藏').fontSize(16).fontWeight(FontWeight.Medium);
  Blank();
  Text('查看全部 >').fontSize(12).fontColor('#166534').onClick(() => {
    router.pushUrl({ url: 'pages/Favorites' });
  });
}
.width('100%')
.margin({ top: 8 })

收藏页面 Favorites.ets 类似商品列表,只显示 isFavorited=true 的商品,可取消收藏。


七、完整主页面代码(Index.ets)

由于篇幅,此处提供完整结构(已在上述各部分覆盖),实际项目需补全所有引用文件和资源。以下是 Index.ets 最终合并代码框架(仅示意):

// Index.ets 完整代码(简略,合并以上所有片段)
// ... 导入
@Entry
@Component
struct Index {
  // 所有状态
  // 所有方法
  // 所有Builder
  build() {
    // 使用 Tabs + 搜索栏 + 浮动按钮 + dialogs
  }
}

完整可运行项目包含 modelservicepages(Index, Detail, Favorites)等,已打包为附件。


八、运行与调试

在这里插入图片描述

8.1 环境

  • DevEco Studio 5.0+,API 24。
  • 无需特殊权限。

8.2 运行

  1. 导入项目,同步。
  2. 创建模拟器运行。
  3. 测试发布、浏览、收藏、搜索、查看我的页面。

8.3 调试技巧

  • 使用 HiLog 打印数据变更。
  • Preferences 文件可通过 Device File Explorer 查看。

九、项目总结与扩展思路

9.1 项目总结

  • 完整功能:发布、浏览、搜索、收藏、交易记录、统计图表。
  • 工程化:服务层抽象,数据持久化,路由分离。
  • 交互丰富:弹窗、动画、Toast、搜索实时过滤。
  • UI设计:网格布局、分类标签、底部Tabs,风格统一。

9.2 扩展方向

  1. 用户登录与权限:接入账号系统,区分买卖家。
  2. 真实图片上传:使用 @ohos.file.picker 选择图片并存储。
  3. 即时通讯:集成 WebSocket 实现实时聊天。
  4. 评价系统:交易完成后互评,建立信用体系。
  5. 地理位置:基于位置推荐附近商品。
  6. 后台推送:新品上架通知、交易提醒。
Logo

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

更多推荐