在移动应用开发中,资产管理类应用是一种常见的应用类型,需要考虑数据展示、分类筛选、搜索排序等多个方面。本文将深入分析一个功能完备的 React Native 家具管理应用实现,探讨其架构设计、状态管理、数据处理以及跨端兼容性策略。

组件化

该实现采用了清晰的组件化架构,主要包含以下部分:

  • 主应用组件 (FurnitureRoomApp) - 负责整体布局和状态管理
  • 家具列表渲染 - 负责渲染家具卡片列表
  • 房间筛选 - 提供按房间筛选家具的功能
  • 搜索功能 - 提供搜索家具的功能
  • 排序功能 - 提供按价格、日期或名称排序的功能
  • 筛选功能 - 提供按类别筛选家具的功能

这种架构设计使得代码结构清晰,易于维护和扩展。主应用组件负责管理全局状态和业务逻辑,而各个功能部分负责具体的 UI 渲染,实现了关注点分离。

状态管理

FurnitureRoomApp 组件使用 useState 钩子管理多个关键状态:

const [selectedRoom, setSelectedRoom] = useState<string>('1');
const [items, setItems] = useState<FurnitureItem[]>(furnitureItems);
const [sortBy, setSortBy] = useState<'price' | 'date' | 'name'>('date');
const [filterBy, setFilterBy] = useState<string>('all');
const [searchQuery, setSearchQuery] = useState<string>('');

这种状态管理方式简洁高效,通过状态更新触发组件重新渲染,实现了家具的筛选、搜索、排序等功能。使用 TypeScript 联合类型确保了 sortBy 的取值只能是预定义的几个选项之一,提高了代码的类型安全性。


家具筛选与排序

应用实现了灵活的家具筛选与排序功能:

// 获取当前房间的家具
const getCurrentRoomItems = () => {
  const roomName = rooms.find(room => room.id === selectedRoom)?.name;
  return items.filter(item =>
    item.room === roomName &&
    (searchQuery === '' || item.name.toLowerCase().includes(searchQuery.toLowerCase()))
  ).sort((a, b) => {
    switch (sortBy) {
      case 'price':
        return b.price - a.price;
      case 'date':
        return new Date(b.purchaseDate).getTime() - new Date(a.purchaseDate).getTime();
      case 'name':
        return a.name.localeCompare(b.name);
      default:
        return 0;
    }
  });
};

这种实现方式支持多重筛选和排序条件:

  • 房间筛选 - 只显示当前选中房间的家具
  • 搜索筛选 - 根据搜索关键字筛选家具
  • 排序 - 按价格、日期或名称排序

这种灵活的筛选和排序功能为用户提供了便捷的家具管理体验。

家具卡片

应用实现了家具卡片的渲染:

// 渲染家具项
const renderItem = ({ item }: { item: FurnitureItem }) => (
  <View style={styles.itemCard}>
    <View style={styles.imageContainer}>
      <View style={styles.imagePlaceholder}>
        <Text style={styles.imageText}>🪑</Text>
      </View>
    </View>
    
    <View style={styles.infoContainer}>
      <View style={styles.headerContainer}>
        <Text style={styles.itemName}>{item.name}</Text>
        <Text style={styles.itemPrice}>¥{item.price.toLocaleString()}</Text>
      </View>
      
      <Text style={styles.itemCategory}>{item.category}</Text>
      <Text style={styles.itemBrand}>品牌: {item.brand}</Text>
      <Text style={styles.itemDate}>购买日期: {item.purchaseDate}</Text>
      <Text style={styles.itemDescription} numberOfLines={2}>{item.description}</Text>
      
      <View style={styles.actionContainer}>
        <TouchableOpacity
          style={styles.editButton}
          onPress={() => Alert.alert('编辑', `编辑 ${item.name}`)}
        >
          <Text style={styles.editButtonText}>编辑</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.deleteButton}
          onPress={() => Alert.alert('删除', `删除 ${item.name}`)}
        >
          <Text style={styles.deleteButtonText}>删除</Text>
        </TouchableOpacity>
      </View>
    </View>
  </View>
);

这种实现方式清晰展示了家具的详细信息,包括名称、价格、类别、品牌、购买日期和描述,并提供了编辑和删除按钮。

#—

类型定义

该实现使用 TypeScript 定义了两个核心数据类型:

  1. FurnitureItem - 家具类型,包含家具的完整信息,如 ID、名称、房间、类别、价格、购买日期、品牌、描述和图片
  2. Room - 房间类型,包含房间的 ID、名称、图标和家具数量

这些类型定义使得数据结构更加清晰,提高了代码的可读性和可维护性,同时也提供了类型安全保障。

数据组织

应用数据按照功能模块进行组织:

  • rooms - 房间列表
  • furnitureItems - 家具列表
  • items - 当前管理的家具集合
  • selectedRoom - 当前选中的房间
  • sortBy - 排序方式
  • filterBy - 筛选条件
  • searchQuery - 搜索关键字

这种数据组织方式使得数据管理更加清晰,易于扩展和维护。



当前实现使用 FlatList 渲染家具列表,这是一个好的做法,但可以进一步优化:

// 优化前
<FlatList
  data={getCurrentRoomItems()}
  renderItem={renderItem}
  keyExtractor={item => item.id}
/>

// 优化后
<FlatList
  data={getCurrentRoomItems()}
  renderItem={renderItem}
  keyExtractor={item => item.id}
  initialNumToRender={5} // 初始渲染的项目数
  maxToRenderPerBatch={10} // 每批渲染的最大项目数
  windowSize={10} // 可见区域外渲染的项目数
  removeClippedSubviews={true} // 移除不可见的子视图
  updateCellsBatchingPeriod={100} // 单元格更新的批处理周期
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT, // 预计算的项目高度
    offset: ITEM_HEIGHT * index,
    index
  })}
/>

2. 状态管理

当前实现使用多个 useState 钩子管理状态,可以考虑使用 useReducer 或状态管理库来管理复杂状态:

// 优化前
const [selectedRoom, setSelectedRoom] = useState<string>('1');
const [items, setItems] = useState<FurnitureItem[]>(furnitureItems);
const [sortBy, setSortBy] = useState<'price' | 'date' | 'name'>('date');
const [filterBy, setFilterBy] = useState<string>('all');
const [searchQuery, setSearchQuery] = useState<string>('');

// 优化后
type AppState = {
  selectedRoom: string;
  items: FurnitureItem[];
  sortBy: 'price' | 'date' | 'name';
  filterBy: string;
  searchQuery: string;
};

type AppAction =
  | { type: 'SET_SELECTED_ROOM'; payload: string }
  | { type: 'SET_ITEMS'; payload: FurnitureItem[] }
  | { type: 'SET_SORT_BY'; payload: 'price' | 'date' | 'name' }
  | { type: 'SET_FILTER_BY'; payload: string }
  | { type: 'SET_SEARCH_QUERY'; payload: string }
  | { type: 'ADD_ITEM'; payload: FurnitureItem }
  | { type: 'UPDATE_ITEM'; payload: FurnitureItem }
  | { type: 'DELETE_ITEM'; payload: string };

const initialState: AppState = {
  selectedRoom: '1',
  items: furnitureItems,
  sortBy: 'date',
  filterBy: 'all',
  searchQuery: '',
};

const appReducer = (state: AppState, action: AppAction): AppState => {
  switch (action.type) {
    case 'SET_SELECTED_ROOM':
      return { ...state, selectedRoom: action.payload };
    case 'SET_ITEMS':
      return { ...state, items: action.payload };
    case 'SET_SORT_BY':
      return { ...state, sortBy: action.payload };
    case 'SET_FILTER_BY':
      return { ...state, filterBy: action.payload };
    case 'SET_SEARCH_QUERY':
      return { ...state, searchQuery: action.payload };
    case 'ADD_ITEM':
      return { ...state, items: [...state.items, action.payload] };
    case 'UPDATE_ITEM':
      return {
        ...state,
        items: state.items.map(item =>
          item.id === action.payload.id ? action.payload : item
        )
      };
    case 'DELETE_ITEM':
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.payload)
      };
    default:
      return state;
  }
};

const [state, dispatch] = useReducer(appReducer, initialState);

3. 数据持久化

当前实现使用内存状态存储数据,可以考虑集成本地存储实现数据持久化:

import AsyncStorage from '@react-native-async-storage/async-storage';

const STORAGE_KEYS = {
  FURNITURE_ITEMS: '@furniture_items',
  ROOMS: '@rooms',
  SETTINGS: '@app_settings',
};

const FurnitureRoomApp = () => {
  const [items, setItems] = useState<FurnitureItem[]>(furnitureItems);
  const [rooms, setRooms] = useState<Room[]>(rooms);
  const [settings, setSettings] = useState({
    selectedRoom: '1',
    sortBy: 'date' as 'price' | 'date' | 'name',
    filterBy: 'all',
  });

  // 加载数据
  useEffect(() => {
    loadData();
  }, []);

  const loadData = async () => {
    try {
      const storedItems = await AsyncStorage.getItem(STORAGE_KEYS.FURNITURE_ITEMS);
      const storedRooms = await AsyncStorage.getItem(STORAGE_KEYS.ROOMS);
      const storedSettings = await AsyncStorage.getItem(STORAGE_KEYS.SETTINGS);

      if (storedItems) {
        setItems(JSON.parse(storedItems));
      }

      if (storedRooms) {
        setRooms(JSON.parse(storedRooms));
      }

      if (storedSettings) {
        setSettings(JSON.parse(storedSettings));
      }
    } catch (error) {
      console.error('加载数据失败:', error);
    }
  };

  // 保存数据
  const saveData = async () => {
    try {
      await AsyncStorage.setItem(STORAGE_KEYS.FURNITURE_ITEMS, JSON.stringify(items));
      await AsyncStorage.setItem(STORAGE_KEYS.ROOMS, JSON.stringify(rooms));
      await AsyncStorage.setItem(STORAGE_KEYS.SETTINGS, JSON.stringify(settings));
    } catch (error) {
      console.error('保存数据失败:', error);
    }
  };

  // 当数据变化时保存
  useEffect(() => {
    saveData();
  }, [items, rooms, settings]);

  // 其他代码...
};

4. 导航系统

可以集成 React Navigation 实现家具详情页面的导航:

import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen 
          name="Home" 
          component={FurnitureRoomApp} 
          options={{ title: '家具管理' }} 
        />
        <Stack.Screen 
          name="FurnitureDetail" 
          component={FurnitureDetailScreen} 
          options={({ route }) => ({ title: route.params?.furnitureName || '家具详情' })} 
        />
        <Stack.Screen 
          name="AddFurniture" 
          component={AddFurnitureScreen} 
          options={{ title: '添加家具' }} 
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

const FurnitureDetailScreen = ({ route }: { route: any }) => {
  const { furnitureId } = route.params;
  // 获取家具详情并渲染
  return (
    <View style={styles.detailContainer}>
      {/* 家具详情内容 */}
    </View>
  );
};

本文深入分析了一个功能完备的 React Native 家具管理应用实现,从架构设计、状态管理、数据处理到跨端兼容性都进行了详细探讨。该实现不仅功能完整,而且代码结构清晰,具有良好的可扩展性和可维护性。。


理解这个家具购买记录管理应用在 React Native 中的完整实现逻辑,并掌握其向鸿蒙(HarmonyOS)平台跨端适配的核心方案。该应用是典型的移动端列表类应用,涵盖了数据状态管理、列表渲染、筛选排序、交互操作、样式设计等移动端开发的核心场景,是跨端开发中极具代表性的实战案例。

1. 数据模型

该家具管理应用采用 TypeScript 强类型设计,构建了清晰的数据模型和完整的状态管理体系:

// 家具类型定义 - 强类型约束
type FurnitureItem = {
  id: string;
  name: string;
  room: string;
  category: string;
  price: number;
  purchaseDate: string;
  brand: string;
  description: string;
  image: string;
};

// 房间类型定义
type Room = {
  id: string;
  name: string;
  icon: string;
  itemCount: number;
};

// 模拟数据初始化
const rooms: Room[] = [/* 房间数据 */];
const furnitureItems: FurnitureItem[] = [/* 家具数据 */];

数据设计亮点

  • 强类型约束:使用 TypeScript 接口定义数据结构,避免运行时类型错误;
  • 语义化字段:字段命名清晰,涵盖家具管理的核心属性;
  • 模拟数据完整:提供足够的测试数据,覆盖不同房间、品类、价格区间;
  • 数据关联性:通过 room 字段建立家具与房间的关联关系。

2. 状态管理

应用采用多个 useState 钩子构建完整的状态管理体系,覆盖所有交互场景:

const [selectedRoom, setSelectedRoom] = useState<string>('1'); // 当前选中房间
const [items, setItems] = useState<FurnitureItem[]>(furnitureItems); // 家具列表
const [sortBy, setSortBy] = useState<'price' | 'date' | 'name'>('date'); // 排序方式
const [filterBy, setFilterBy] = useState<string>('all'); // 筛选条件
const [searchQuery, setSearchQuery] = useState<string>(''); // 搜索关键词

状态设计原则

  • 单一职责:每个状态仅管理一个维度的数据,职责清晰;
  • 不可变更新:状态更新遵循 React 不可变原则,通过新数组替换实现;
  • 类型安全:排序状态使用联合类型,限制可选值范围;
  • 初始值合理:所有状态都有合理的初始值,避免 undefined 问题。

(1)数据筛选与排序

这是应用的核心业务逻辑,实现了多维度的数据过滤和排序:

const getCurrentRoomItems = () => {
  const roomName = rooms.find(room => room.id === selectedRoom)?.name;
  return items.filter(item => 
    item.room === roomName &&
    (searchQuery === '' || item.name.toLowerCase().includes(searchQuery.toLowerCase()))
  ).sort((a, b) => {
    switch (sortBy) {
      case 'price':
        return b.price - a.price; // 价格降序
      case 'date':
        return new Date(b.purchaseDate).getTime() - new Date(a.purchaseDate).getTime(); // 日期降序
      case 'name':
        return a.name.localeCompare(b.name); // 名称升序
      default:
        return 0;
    }
  });
};

实现技巧

  • 链式操作:先过滤后排序,逻辑清晰;
  • 本地化搜索:转换为小写实现不区分大小写的搜索;
  • 多维度排序:支持价格、日期、名称三种排序方式;
  • 空值保护:使用可选链操作符 ?. 避免空指针错误;
  • 时间比较:将日期字符串转换为时间戳进行数值比较。
(2)列表

使用 FlatList 实现高性能的列表渲染,自定义列表项组件:

const renderItem = ({ item }: { item: FurnitureItem }) => (
  <View style={styles.itemCard}>
    {/* 图片占位区 */}
    <View style={styles.imageContainer}>
      <View style={styles.imagePlaceholder}>
        <Text style={styles.imageText}>🪑</Text>
      </View>
    </View>
    
    {/* 信息展示区 */}
    <View style={styles.infoContainer}>
      {/* 头部(名称+价格) */}
      <View style={styles.headerContainer}>
        <Text style={styles.itemName}>{item.name}</Text>
        <Text style={styles.itemPrice}>¥{item.price.toLocaleString()}</Text>
      </View>
      
      {/* 详情信息 */}
      <Text style={styles.itemCategory}>{item.category}</Text>
      <Text style={styles.itemBrand}>品牌: {item.brand}</Text>
      <Text style={styles.itemDate}>购买日期: {item.purchaseDate}</Text>
      <Text style={styles.itemDescription} numberOfLines={2}>{item.description}</Text>
      
      {/* 操作按钮 */}
      <View style={styles.actionContainer}>
        <TouchableOpacity 
          style={styles.editButton}
          onPress={() => Alert.alert('编辑', `编辑 ${item.name}`)}
        >
          <Text style={styles.editButtonText}>编辑</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          style={styles.deleteButton}
          onPress={() => Alert.alert('删除', `确定要删除 ${item.name} 吗?`, [
            { text: '取消' },
            { text: '确定', onPress: () => deleteItem(item.id) }
          ])}
        >
          <Text style={styles.deleteButtonText}>删除</Text>
        </TouchableOpacity>
      </View>
    </View>
  </View>
);

UI设计亮点

  • 卡片式布局:使用圆角、阴影实现现代卡片设计;
  • 图文排版:左侧图片占位 + 右侧信息区的经典布局;
  • 文本优化:价格格式化、描述文本行数限制;
  • 操作区设计:编辑/删除按钮区分颜色,视觉引导清晰;
  • 交互反馈:删除操作二次确认,防止误操作。
(3)数据操作

实现了完整的 CRUD 中的创建和删除功能:

// 删除家具项
const deleteItem = (id: string) => {
  setItems(items.filter(item => item.id !== id));
};

// 添加新家具
const addNewItem = () => {
  Alert.prompt(
    '添加新家具',
    '请输入家具名称:',
    (value) => {
      if (value) {
        const newRoom = rooms.find(room => room.id === selectedRoom)?.name || '未知房间';
        const newItem: FurnitureItem = {
          id: `${items.length + 1}`,
          name: value,
          room: newRoom,
          category: '其他',
          price: 0,
          purchaseDate: new Date().toISOString().split('T')[0],
          brand: '未知',
          description: '新添加的家具',
          image: ''
        };
        setItems([newItem, ...items]); // 新项添加到列表顶部
      }
    }
  );
};

实现特点

  • 不可变更新:使用 filter 和扩展运算符实现状态更新;
  • 用户输入:使用 Alert.prompt 获取用户输入;
  • 日期处理:自动生成当前日期的购买记录;
  • 关联房间:新添加的家具自动关联当前选中房间;
  • ID生成:简单的自增ID生成策略(实际项目中建议使用 uuid)。

(1)房间选择横向滚动列表
<ScrollView 
  horizontal 
  showsHorizontalScrollIndicator={false} 
  style={styles.roomsContainer}
>
  <View style={styles.roomsList}>
    {rooms.map(room => (
      <TouchableOpacity
        key={room.id}
        style={[
          styles.roomItem,
          selectedRoom === room.id && styles.selectedRoom
        ]}
        onPress={() => setSelectedRoom(room.id)}
      >
        <Text style={styles.roomIcon}>{room.icon}</Text>
        <Text style={styles.roomName}>{room.name}</Text>
        <Text style={styles.roomCount}>{room.itemCount}</Text>
      </TouchableOpacity>
    ))}
  </View>
</ScrollView>

实现要点

  • 横向滚动:horizontal 属性实现横向滚动;
  • 隐藏滚动条:showsHorizontalScrollIndicator={false} 优化视觉效果;
  • 选中状态:通过样式数组实现选中状态的视觉变化;
  • 统一尺寸:固定宽度的房间卡片,保证布局一致性。
(2)排序筛选
<View style={styles.optionsContainer}>
  <View style={styles.sortContainer}>
    <Text style={styles.optionLabel}>排序:</Text>
    <TouchableOpacity 
      style={[styles.optionButton, sortBy === 'name' && styles.activeOption]}
      onPress={() => setSortBy('name')}
    >
      <Text style={styles.optionText}>名称</Text>
    </TouchableOpacity>
    {/* 其他排序选项 */}
  </View>
  
  <View style={styles.filterContainer}>
    <Text style={styles.optionLabel}>筛选:</Text>
    {/* 筛选选项 */}
  </View>
</View>

交互设计

  • 标签+选项:清晰的标签引导,选项按钮化设计;
  • 状态反馈:选中的选项通过背景色变化反馈;
  • 紧凑布局:排序和筛选垂直排列,节省空间。

将该 React Native 家具管理应用适配到鸿蒙平台,核心是将 React 的状态管理、FlatList 列表、横向滚动、样式系统等核心能力映射到鸿蒙 ArkTS + ArkUI 生态,以下是完整的适配方案。

1. 核心技术栈映射

React Native 核心能力 鸿蒙 ArkTS 对应实现 适配关键说明
useState 状态管理 @State/@DerivedState 装饰器 基础状态+派生状态组合使用
FlatList 列表渲染 List + ForEach 组件 高性能列表渲染
横向 ScrollView Scroll + scrollDirection 滚动方向配置
TouchableOpacity Button + stateEffect(false) 可点击组件替换
StyleSheet.create @Styles/@Extend + 链式样式 样式体系重构
Alert.alert/prompt AlertDialog/TextInputDialog 弹窗交互替换
Array.filter/sort/reduce 数组方法完全复用 业务逻辑零修改
Dimensions 尺寸获取 @ohos.window API 屏幕适配(本案例未使用)
文本行数限制 numberOfLines maxLines 属性 文本展示控制

2. 鸿蒙端

// index.ets - 鸿蒙端家具管理应用完整实现
import router from '@ohos.router';
import { BusinessError } from '@ohos.base';

// 类型定义(与RN端完全一致)
type FurnitureItem = {
  id: string;
  name: string;
  room: string;
  category: string;
  price: number;
  purchaseDate: string;
  brand: string;
  description: string;
  image: string;
};

type Room = {
  id: string;
  name: string;
  icon: string;
  itemCount: number;
};

// 模拟数据(与RN端完全一致)
const rooms: Room[] = [
  { id: '1', name: '客厅', icon: '🛋️', itemCount: 5 },
  { id: '2', name: '卧室', icon: '🛏️', itemCount: 3 },
  { id: '3', name: '厨房', icon: '🍳', itemCount: 4 },
  { id: '4', name: '书房', icon: '📚', itemCount: 2 },
  { id: '5', name: '餐厅', icon: '🍽️', itemCount: 2 },
  { id: '6', name: '浴室', icon: '🚿', itemCount: 1 },
];

const initialFurnitureItems: FurnitureItem[] = [
  {
    id: '1',
    name: '沙发',
    room: '客厅',
    category: '座椅',
    price: 3500,
    purchaseDate: '2023-05-15',
    brand: '宜家',
    description: '三人座布艺沙发,灰色,舒适耐用',
    image: ''
  },
  {
    id: '2',
    name: '茶几',
    room: '客厅',
    category: '桌子',
    price: 800,
    purchaseDate: '2023-05-16',
    brand: '宜家',
    description: '简约风格圆形茶几,白色',
    image: ''
  },
  {
    id: '3',
    name: '餐桌',
    room: '餐厅',
    category: '桌子',
    price: 1200,
    purchaseDate: '2023-04-20',
    brand: '曲美',
    description: '实木餐桌,可伸缩设计',
    image: ''
  },
  {
    id: '4',
    name: '书桌',
    room: '书房',
    category: '桌子',
    price: 600,
    purchaseDate: '2023-03-10',
    brand: '宜家',
    description: '简约办公桌,带抽屉',
    image: ''
  },
  {
    id: '5',
    name: '床',
    room: '卧室',
    category: '床具',
    price: 2500,
    purchaseDate: '2023-02-28',
    brand: '顾家',
    description: '双人床,带床垫',
    image: ''
  },
  {
    id: '6',
    name: '衣柜',
    room: '卧室',
    category: '储物',
    price: 1800,
    purchaseDate: '2023-02-28',
    brand: '索菲亚',
    description: '定制衣柜,推拉门设计',
    image: ''
  },
  {
    id: '7',
    name: '冰箱',
    room: '厨房',
    category: '电器',
    price: 2800,
    purchaseDate: '2023-01-15',
    brand: '海尔',
    description: '双开门冰箱,节能静音',
    image: ''
  },
  {
    id: '8',
    name: '洗衣机',
    room: '厨房',
    category: '电器',
    price: 1500,
    purchaseDate: '2023-01-15',
    brand: '小天鹅',
    description: '滚筒洗衣机,8公斤',
    image: ''
  },
];

@Entry
@Component
struct FurnitureRoomApp {
  // 基础状态(对应RN的useState)
  @State selectedRoom: string = '1';
  @State items: FurnitureItem[] = initialFurnitureItems;
  @State sortBy: 'price' | 'date' | 'name' = 'date';
  @State filterBy: string = 'all';
  @State searchQuery: string = '';

  // 派生状态 - 计算当前房间的家具列表
  @DerivedState
  get currentRoomItems(): FurnitureItem[] {
    const roomName = rooms.find(room => room.id === this.selectedRoom)?.name;
    return this.items.filter(item => 
      item.room === roomName &&
      (this.searchQuery === '' || item.name.toLowerCase().includes(this.searchQuery.toLowerCase()))
    ).sort((a, b) => {
      switch (this.sortBy) {
        case 'price':
          return b.price - a.price;
        case 'date':
          return new Date(b.purchaseDate).getTime() - new Date(a.purchaseDate).getTime();
        case 'name':
          return a.name.localeCompare(b.name);
        default:
          return 0;
      }
    });
  }

  // 派生状态 - 计算当前房间总价
  @DerivedState
  get totalCost(): number {
    return this.currentRoomItems.reduce((sum, item) => sum + item.price, 0);
  }

  // 通用样式封装 - 卡片容器样式
  @Styles
  cardStyle() {
    .backgroundColor('#ffffff')
    .borderRadius(12)
    .shadow({ radius: 2, color: '#000', opacity: 0.1, offsetX: 0, offsetY: 1 });
  }

  // 通用样式封装 - 选项按钮样式
  @Styles
  optionButtonStyle(isActive: boolean) {
    .backgroundColor(isActive ? '#3b82f6' : '#f1f5f9')
    .paddingHorizontal(12)
    .paddingVertical(6)
    .borderRadius(20)
    .marginRight(8);
  }

  // 删除家具项(业务逻辑与RN端完全一致)
  private deleteItem(id: string) {
    this.items = this.items.filter(item => item.id !== id);
  }

  // 添加新家具
  private addNewItem() {
    // 鸿蒙的文本输入弹窗
    TextInputDialog.show({
      title: '添加新家具',
      message: '请输入家具名称:',
      confirm: {
        value: '确定'
      },
      cancel: {
        value: '取消'
      },
      placeholder: '例如:椅子'
    }).then((result) => {
      if (result.result && result.value) {
        const newRoom = rooms.find(room => room.id === this.selectedRoom)?.name || '未知房间';
        const newItem: FurnitureItem = {
          id: `${this.items.length + 1}`,
          name: result.value,
          room: newRoom,
          category: '其他',
          price: 0,
          purchaseDate: new Date().toISOString().split('T')[0],
          brand: '未知',
          description: '新添加的家具',
          image: ''
        };
        this.items = [newItem, ...this.items];
      }
    }).catch((err: BusinessError) => {
      console.error('添加家具失败:', err);
    });
  }

  // 渲染家具列表项
  @Builder
  renderItem(item: FurnitureItem) {
    Row({ space: 12 }) {
      // 图片占位区
      Column() {
        Stack() {
          Column() {
            Text('🪑')
              .fontSize(24);
          }
          .width(80)
          .height(80)
          .backgroundColor('#e2e8f0')
          .borderRadius(8)
          .alignItems(ItemAlign.Center)
          .justifyContent(FlexAlign.Center);
        }
      }

      // 信息展示区
      Column({ space: 2, flexShrink: 1 }) {
        // 头部(名称+价格)
        Row({ space: 0 }) {
          Text(item.name)
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .fontColor('#1e293b')
            .flex(1);
          
          Text(`¥${item.price.toLocaleString()}`)
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .fontColor('#3b82f6');
        }
        .marginBottom(4);

        // 详情信息
        Text(item.category)
          .fontSize(12)
          .fontColor('#64748b');
        
        Text(`品牌: ${item.brand}`)
          .fontSize(12)
          .fontColor('#64748b');
        
        Text(`购买日期: ${item.purchaseDate}`)
          .fontSize(12)
          .fontColor('#94a3b8');
        
        Text(item.description)
          .fontSize(14)
          .fontColor('#64748b')
          .maxLines(2) // 对应RN的numberOfLines
          .marginBottom(8);

        // 操作按钮
        Row({ space: 8 }) {
          Button('编辑')
            .backgroundColor('#f1f5f9')
            .paddingHorizontal(12)
            .paddingVertical(6)
            .borderRadius(6)
            .fontSize(12)
            .fontColor('#3b82f6')
            .fontWeight(FontWeight.Medium)
            .stateEffect(true)
            .onClick(() => {
              AlertDialog.show({
                title: '编辑',
                message: `编辑 ${item.name}`,
                confirm: { value: '确定' }
              });
            });
          
          Button('删除')
            .backgroundColor('#fee2e2')
            .paddingHorizontal(12)
            .paddingVertical(6)
            .borderRadius(6)
            .fontSize(12)
            .fontColor('#ef4444')
            .fontWeight(FontWeight.Medium)
            .stateEffect(true)
            .onClick(() => {
              AlertDialog.show({
                title: '删除',
                message: `确定要删除 ${item.name} 吗?`,
                confirm: {
                  value: '确定',
                  action: () => this.deleteItem(item.id)
                },
                cancel: {
                  value: '取消'
                }
              });
            });
        }
        .justifyContent(FlexAlign.End)
        .width('100%');
      }
    }
    .cardStyle()
    .padding(12)
    .marginBottom(12)
    .width('100%');
  }

  build() {
    Column({ space: 0 }) {
      // 头部导航栏
      this.Header();
      
      // 内容区域(滚动容器)
      Scroll() {
        Column({ space: 16 }) {
          // 搜索栏
          this.SearchBar();

          // 房间选择
          this.RoomSelector();

          // 排序和筛选选项
          this.SortFilterOptions();

          // 房间详情
          this.RoomDetails();

          // 总价统计
          this.TotalCostSection();

          // 家具列表标题
          Text(`${rooms.find(r => r.id === this.selectedRoom)?.name || '房间'}家具 (${this.currentRoomItems.length})`)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#1e293b')
            .marginVertical(12)
            .width('100%');

          // 家具列表(对应RN的FlatList)
          List({ space: 0 }) {
            ForEach(this.currentRoomItems, (item) => {
              ListItem() {
                this.renderItem(item);
              }
            }, (item) => item.id);
          }
          .width('100%')
          .shrink(0)
          .edgeEffect(EdgeEffect.None)
          .scrollBar(BarState.Off);

          // 使用说明
          this.InfoSection();
        }
        .padding(16)
        .width('100%');
      }
      .flex(1)
      .width('100%');

      // 底部导航
      this.BottomNav();
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8fafc')
    .safeArea(true);
  }

  // 头部导航栏 - Builder函数封装
  @Builder
  Header() {
    Row({ space: 0 }) {
      Text('家具购买记录')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b');
      
      // 添加按钮
      Button('+')
        .width(36)
        .height(36)
        .borderRadius(18)
        .backgroundColor('#3b82f6')
        .fontSize(20)
        .fontColor('#ffffff')
        .fontWeight(FontWeight.Bold)
        .stateEffect(true)
        .onClick(() => this.addNewItem())
        .marginLeft('auto');
    }
    .padding(20)
    .backgroundColor('#ffffff')
    .borderBottom({ width: 1, color: '#e2e8f0' })
    .width('100%');
  }

  // 搜索栏 - Builder函数封装
  @Builder
  SearchBar() {
    Row({ space: 12 }) {
      Text('🔍')
        .fontSize(18)
        .fontColor('#64748b');
      
      Text('搜索家具名称')
        .fontSize(14)
        .fontColor('#94a3b8')
        .flex(1);
    }
    .cardStyle()
    .paddingVertical(12)
    .paddingHorizontal(16)
    .borderRadius(20)
    .width('100%');
  }

  // 房间选择器 - Builder函数封装
  @Builder
  RoomSelector() {
    Column({ space: 0 }) {
      Text('选择房间')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .marginVertical(12)
        .width('100%');
      
      // 横向滚动的房间列表
      Scroll({ scrollDirection: ScrollDirection.Horizontal }) {
        Row({ space: 12 }) {
          ForEach(rooms, (room) => {
            Button()
              .width(80)
              .height('auto')
              .backgroundColor(this.selectedRoom === room.id ? '#3b82f6' : '#ffffff')
              .borderRadius(12)
              .padding(12)
              .stateEffect(true)
              .onClick(() => this.selectedRoom = room.id)
              .shadow({ radius: 2, color: '#000', opacity: 0.1, offsetX: 0, offsetY: 1 }) {
                Column({ space: 4 }) {
                  Text(room.icon)
                    .fontSize(24);
                  
                  Text(room.name)
                    .fontSize(12)
                    .fontColor(this.selectedRoom === room.id ? '#ffffff' : '#64748b')
                    .fontWeight(FontWeight.Medium);
                  
                  Text(`${room.itemCount}`)
                    .fontSize(10)
                    .fontColor(this.selectedRoom === room.id ? '#ffffff' : '#94a3b8');
                }
                .alignItems(ItemAlign.Center);
              };
          }, (room) => room.id);
        }
        .width('auto');
      }
      .scrollBar(BarState.Off)
      .width('100%');
    }
  }

  // 排序筛选选项 - Builder函数封装
  @Builder
  SortFilterOptions() {
    Column({ space: 8 }) {
      // 排序选项
      Row({ space: 8 }) {
        Text('排序:')
          .fontSize(14)
          .fontColor('#64748b');
        
        Button('名称')
          .optionButtonStyle(this.sortBy === 'name')
          .fontSize(12)
          .fontColor(this.sortBy === 'name' ? '#ffffff' : '#3b82f6')
          .fontWeight(FontWeight.Medium)
          .stateEffect(true)
          .onClick(() => this.sortBy = 'name');
        
        Button('日期')
          .optionButtonStyle(this.sortBy === 'date')
          .fontSize(12)
          .fontColor(this.sortBy === 'date' ? '#ffffff' : '#3b82f6')
          .fontWeight(FontWeight.Medium)
          .stateEffect(true)
          .onClick(() => this.sortBy = 'date');
        
        Button('价格')
          .optionButtonStyle(this.sortBy === 'price')
          .fontSize(12)
          .fontColor(this.sortBy === 'price' ? '#ffffff' : '#3b82f6')
          .fontWeight(FontWeight.Medium)
          .stateEffect(true)
          .onClick(() => this.sortBy = 'price');
      }
      
      // 筛选选项
      Row({ space: 8 }) {
        Text('筛选:')
          .fontSize(14)
          .fontColor('#64748b');
        
        Button('全部')
          .optionButtonStyle(this.filterBy === 'all')
          .fontSize(12)
          .fontColor(this.filterBy === 'all' ? '#ffffff' : '#3b82f6')
          .fontWeight(FontWeight.Medium)
          .stateEffect(true)
          .onClick(() => this.filterBy = 'all');
        
        Button('高价')
          .optionButtonStyle(this.filterBy === 'expensive')
          .fontSize(12)
          .fontColor(this.filterBy === 'expensive' ? '#ffffff' : '#3b82f6')
          .fontWeight(FontWeight.Medium)
          .stateEffect(true)
          .onClick(() => this.filterBy = 'expensive');
      }
    }
    .width('100%');
  }

  // 房间详情 - Builder函数封装
  @Builder
  RoomDetails() {
    Row({ space: 0 }) {
      Text(`${rooms.find(r => r.id === this.selectedRoom)?.name || '房间'}详情`)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b');
      
      Text(`共 ${this.currentRoomItems.length} 件家具`)
        .fontSize(14)
        .fontColor('#64748b')
        .marginLeft('auto');
    }
    .cardStyle()
    .padding(16)
    .width('100%');
  }

  // 总价统计 - Builder函数封装
  @Builder
  TotalCostSection() {
    Column({ space: 4 }) {
      Text('当前房间总价:')
        .fontSize(14)
        .fontColor('#e0f2fe');
      
      Text(`¥${this.totalCost.toLocaleString()}`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#ffffff');
    }
    .backgroundColor('#3b82f6')
    .borderRadius(12)
    .padding(16)
    .alignItems(ItemAlign.Center)
    .width('100%');
  }

  // 使用说明 - Builder函数封装
  @Builder
  InfoSection() {
    Column({ space: 8 }) {
      Text('使用说明')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .marginBottom(4);
      
      Text('• 点击房间选择不同区域')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
      
      Text('• 点击+号添加新家具')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
      
      Text('• 支持按名称、日期、价格排序')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
      
      Text('• 可编辑或删除已有记录')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
    }
    .cardStyle()
    .padding(16)
    .marginTop(16)
    .width('100%');
  }

  // 底部导航 - Builder函数封装
  @Builder
  BottomNav() {
    Row({ space: 0 }) {
      // 首页
      Button()
        .flex(1)
        .backgroundColor(Color.Transparent)
        .stateEffect(true)
        .onClick(() => {
          AlertDialog.show({ title: '首页', message: '首页功能' });
        }) {
          Column({ space: 4 }) {
            Text('🏠')
              .fontSize(20)
              .fontColor('#94a3b8');
            
            Text('首页')
              .fontSize(12)
              .fontColor('#94a3b8');
          }
        };
      
      // 房间
      Button()
        .flex(1)
        .backgroundColor(Color.Transparent)
        .stateEffect(true)
        .onClick(() => {
          AlertDialog.show({ title: '房间', message: '房间管理' });
        }) {
          Column({ space: 4 }) {
            Text('🚪')
              .fontSize(20)
              .fontColor('#94a3b8');
            
            Text('房间')
              .fontSize(12)
              .fontColor('#94a3b8');
          }
        };
      
      // 统计
      Button()
        .flex(1)
        .backgroundColor(Color.Transparent)
        .stateEffect(true)
        .onClick(() => {
          AlertDialog.show({ title: '统计', message: '数据统计' });
        }) {
          Column({ space: 4 }) {
            Text('📊')
              .fontSize(20)
              .fontColor('#94a3b8');
            
            Text('统计')
              .fontSize(12)
              .fontColor('#94a3b8');
          }
        };
      
      // 我的
      Button()
        .flex(1)
        .backgroundColor(Color.Transparent)
        .stateEffect(true)
        .onClick(() => {
          AlertDialog.show({ title: '我的', message: '个人中心' });
        }) {
          Column({ space: 4 }) {
            Text('👤')
              .fontSize(20)
              .fontColor('#94a3b8');
            
            Text('我的')
              .fontSize(12)
              .fontColor('#94a3b8');
          }
        };
    }
    .backgroundColor('#ffffff')
    .borderTop({ width: 1, color: '#e2e8f0' })
    .paddingVertical(12)
    .width('100%');
  }
}

该家具管理应用的跨端适配实践验证了列表类应用从 React Native 向鸿蒙迁移的高效性,核心的业务逻辑和数据模型可实现完全复用,仅需适配UI组件层和交互层,这种适配模式特别适合数据驱动的列表类应用开发,能够显著提升跨端开发效率,同时利用鸿蒙的原生能力提升应用性能和用户体验。


真实演示案例代码:






// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList } from 'react-native';

// Base64 图标库
const ICONS_BASE64 = {
  home: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  furniture: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  room: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  add: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  search: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  filter: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  sort: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  more: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};

const { width, height } = Dimensions.get('window');

// 家具类型
type FurnitureItem = {
  id: string;
  name: string;
  room: string;
  category: string;
  price: number;
  purchaseDate: string;
  brand: string;
  description: string;
  image: string;
};

// 房间类型
type Room = {
  id: string;
  name: string;
  icon: string;
  itemCount: number;
};

// 模拟数据
const rooms: Room[] = [
  { id: '1', name: '客厅', icon: '🛋️', itemCount: 5 },
  { id: '2', name: '卧室', icon: '🛏️', itemCount: 3 },
  { id: '3', name: '厨房', icon: '🍳', itemCount: 4 },
  { id: '4', name: '书房', icon: '📚', itemCount: 2 },
  { id: '5', name: '餐厅', icon: '🍽️', itemCount: 2 },
  { id: '6', name: '浴室', icon: '🚿', itemCount: 1 },
];

const furnitureItems: FurnitureItem[] = [
  {
    id: '1',
    name: '沙发',
    room: '客厅',
    category: '座椅',
    price: 3500,
    purchaseDate: '2023-05-15',
    brand: '宜家',
    description: '三人座布艺沙发,灰色,舒适耐用',
    image: ''
  },
  {
    id: '2',
    name: '茶几',
    room: '客厅',
    category: '桌子',
    price: 800,
    purchaseDate: '2023-05-16',
    brand: '宜家',
    description: '简约风格圆形茶几,白色',
    image: ''
  },
  {
    id: '3',
    name: '餐桌',
    room: '餐厅',
    category: '桌子',
    price: 1200,
    purchaseDate: '2023-04-20',
    brand: '曲美',
    description: '实木餐桌,可伸缩设计',
    image: ''
  },
  {
    id: '4',
    name: '书桌',
    room: '书房',
    category: '桌子',
    price: 600,
    purchaseDate: '2023-03-10',
    brand: '宜家',
    description: '简约办公桌,带抽屉',
    image: ''
  },
  {
    id: '5',
    name: '床',
    room: '卧室',
    category: '床具',
    price: 2500,
    purchaseDate: '2023-02-28',
    brand: '顾家',
    description: '双人床,带床垫',
    image: ''
  },
  {
    id: '6',
    name: '衣柜',
    room: '卧室',
    category: '储物',
    price: 1800,
    purchaseDate: '2023-02-28',
    brand: '索菲亚',
    description: '定制衣柜,推拉门设计',
    image: ''
  },
  {
    id: '7',
    name: '冰箱',
    room: '厨房',
    category: '电器',
    price: 2800,
    purchaseDate: '2023-01-15',
    brand: '海尔',
    description: '双开门冰箱,节能静音',
    image: ''
  },
  {
    id: '8',
    name: '洗衣机',
    room: '厨房',
    category: '电器',
    price: 1500,
    purchaseDate: '2023-01-15',
    brand: '小天鹅',
    description: '滚筒洗衣机,8公斤',
    image: ''
  },
];

const FurnitureRoomApp: React.FC = () => {
  const [selectedRoom, setSelectedRoom] = useState<string>('1');
  const [items, setItems] = useState<FurnitureItem[]>(furnitureItems);
  const [sortBy, setSortBy] = useState<'price' | 'date' | 'name'>('date');
  const [filterBy, setFilterBy] = useState<string>('all');
  const [searchQuery, setSearchQuery] = useState<string>('');

  // 获取当前房间的家具
  const getCurrentRoomItems = () => {
    const roomName = rooms.find(room => room.id === selectedRoom)?.name;
    return items.filter(item => 
      item.room === roomName &&
      (searchQuery === '' || item.name.toLowerCase().includes(searchQuery.toLowerCase()))
    ).sort((a, b) => {
      switch (sortBy) {
        case 'price':
          return b.price - a.price;
        case 'date':
          return new Date(b.purchaseDate).getTime() - new Date(a.purchaseDate).getTime();
        case 'name':
          return a.name.localeCompare(b.name);
        default:
          return 0;
      }
    });
  };

  // 渲染家具项
  const renderItem = ({ item }: { item: FurnitureItem }) => (
    <View style={styles.itemCard}>
      <View style={styles.imageContainer}>
        <View style={styles.imagePlaceholder}>
          <Text style={styles.imageText}>🪑</Text>
        </View>
      </View>
      
      <View style={styles.infoContainer}>
        <View style={styles.headerContainer}>
          <Text style={styles.itemName}>{item.name}</Text>
          <Text style={styles.itemPrice}>¥{item.price.toLocaleString()}</Text>
        </View>
        
        <Text style={styles.itemCategory}>{item.category}</Text>
        <Text style={styles.itemBrand}>品牌: {item.brand}</Text>
        <Text style={styles.itemDate}>购买日期: {item.purchaseDate}</Text>
        <Text style={styles.itemDescription} numberOfLines={2}>{item.description}</Text>
        
        <View style={styles.actionContainer}>
          <TouchableOpacity 
            style={styles.editButton}
            onPress={() => Alert.alert('编辑', `编辑 ${item.name}`)}
          >
            <Text style={styles.editButtonText}>编辑</Text>
          </TouchableOpacity>
          <TouchableOpacity 
            style={styles.deleteButton}
            onPress={() => Alert.alert('删除', `确定要删除 ${item.name} 吗?`, [
              { text: '取消' },
              { text: '确定', onPress: () => deleteItem(item.id) }
            ])}
          >
            <Text style={styles.deleteButtonText}>删除</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );

  // 删除家具项
  const deleteItem = (id: string) => {
    setItems(items.filter(item => item.id !== id));
  };

  // 添加新家具
  const addNewItem = () => {
    Alert.prompt(
      '添加新家具',
      '请输入家具名称:',
      (value) => {
        if (value) {
          const newRoom = rooms.find(room => room.id === selectedRoom)?.name || '未知房间';
          const newItem: FurnitureItem = {
            id: `${items.length + 1}`,
            name: value,
            room: newRoom,
            category: '其他',
            price: 0,
            purchaseDate: new Date().toISOString().split('T')[0],
            brand: '未知',
            description: '新添加的家具',
            image: ''
          };
          setItems([newItem, ...items]);
        }
      }
    );
  };

  const currentRoomItems = getCurrentRoomItems();

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>家具购买记录</Text>
        <TouchableOpacity style={styles.addButton} onPress={addNewItem}>
          <Text style={styles.addButtonText}>+</Text>
        </TouchableOpacity>
      </View>

      <ScrollView style={styles.content}>
        {/* 搜索栏 */}
        <View style={styles.searchContainer}>
          <Text style={styles.searchIcon}>🔍</Text>
          <Text style={styles.searchPlaceholder}>搜索家具名称</Text>
        </View>

        {/* 房间选择 */}
        <Text style={styles.sectionTitle}>选择房间</Text>
        <ScrollView 
          horizontal 
          showsHorizontalScrollIndicator={false} 
          style={styles.roomsContainer}
        >
          <View style={styles.roomsList}>
            {rooms.map(room => (
              <TouchableOpacity
                key={room.id}
                style={[
                  styles.roomItem,
                  selectedRoom === room.id && styles.selectedRoom
                ]}
                onPress={() => setSelectedRoom(room.id)}
              >
                <Text style={styles.roomIcon}>{room.icon}</Text>
                <Text style={styles.roomName}>{room.name}</Text>
                <Text style={styles.roomCount}>{room.itemCount}</Text>
              </TouchableOpacity>
            ))}
          </View>
        </ScrollView>

        {/* 排序和筛选选项 */}
        <View style={styles.optionsContainer}>
          <View style={styles.sortContainer}>
            <Text style={styles.optionLabel}>排序:</Text>
            <TouchableOpacity 
              style={[styles.optionButton, sortBy === 'name' && styles.activeOption]}
              onPress={() => setSortBy('name')}
            >
              <Text style={styles.optionText}>名称</Text>
            </TouchableOpacity>
            <TouchableOpacity 
              style={[styles.optionButton, sortBy === 'date' && styles.activeOption]}
              onPress={() => setSortBy('date')}
            >
              <Text style={styles.optionText}>日期</Text>
            </TouchableOpacity>
            <TouchableOpacity 
              style={[styles.optionButton, sortBy === 'price' && styles.activeOption]}
              onPress={() => setSortBy('price')}
            >
              <Text style={styles.optionText}>价格</Text>
            </TouchableOpacity>
          </View>
          
          <View style={styles.filterContainer}>
            <Text style={styles.optionLabel}>筛选:</Text>
            <TouchableOpacity 
              style={[styles.optionButton, filterBy === 'all' && styles.activeOption]}
              onPress={() => setFilterBy('all')}
            >
              <Text style={styles.optionText}>全部</Text>
            </TouchableOpacity>
            <TouchableOpacity 
              style={[styles.optionButton, filterBy === 'expensive' && styles.activeOption]}
              onPress={() => setFilterBy('expensive')}
            >
              <Text style={styles.optionText}>高价</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 房间详情 */}
        <View style={styles.roomDetails}>
          <Text style={styles.roomDetailTitle}>
            {rooms.find(r => r.id === selectedRoom)?.name || '房间'}详情
          </Text>
          <Text style={styles.roomItemCount}>{currentRoomItems.length} 件家具
          </Text>
        </View>

        {/* 总价统计 */}
        <View style={styles.totalCostContainer}>
          <Text style={styles.totalCostLabel}>当前房间总价:</Text>
          <Text style={styles.totalCostValue}>
            ¥{currentRoomItems.reduce((sum, item) => sum + item.price, 0).toLocaleString()}
          </Text>
        </View>

        {/* 家具列表 */}
        <Text style={styles.sectionTitle}>
          {rooms.find(r => r.id === selectedRoom)?.name || '房间'}家具 ({currentRoomItems.length})
        </Text>
        
        <FlatList
          data={currentRoomItems}
          renderItem={renderItem}
          keyExtractor={item => item.id}
          showsVerticalScrollIndicator={false}
        />

        {/* 使用说明 */}
        <View style={styles.infoCard}>
          <Text style={styles.infoTitle}>使用说明</Text>
          <Text style={styles.infoText}>• 点击房间选择不同区域</Text>
          <Text style={styles.infoText}>• 点击+号添加新家具</Text>
          <Text style={styles.infoText}>• 支持按名称、日期、价格排序</Text>
          <Text style={styles.infoText}>• 可编辑或删除已有记录</Text>
        </View>
      </ScrollView>

      {/* 底部导航 */}
      <View style={styles.bottomNav}>
        <TouchableOpacity 
          style={styles.navItem} 
          onPress={() => Alert.alert('首页')}
        >
          <Text style={styles.navIcon}>🏠</Text>
          <Text style={styles.navText}>首页</Text>
        </TouchableOpacity>
        
        <TouchableOpacity 
          style={styles.navItem} 
          onPress={() => Alert.alert('房间')}
        >
          <Text style={styles.navIcon}>🚪</Text>
          <Text style={styles.navText}>房间</Text>
        </TouchableOpacity>
        
        <TouchableOpacity 
          style={styles.navItem} 
          onPress={() => Alert.alert('统计')}
        >
          <Text style={styles.navIcon}>📊</Text>
          <Text style={styles.navText}>统计</Text>
        </TouchableOpacity>
        
        <TouchableOpacity 
          style={styles.navItem} 
          onPress={() => Alert.alert('我的')}
        >
          <Text style={styles.navIcon}>👤</Text>
          <Text style={styles.navText}>我的</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8fafc',
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  addButton: {
    width: 36,
    height: 36,
    borderRadius: 18,
    backgroundColor: '#3b82f6',
    alignItems: 'center',
    justifyContent: 'center',
  },
  addButtonText: {
    fontSize: 20,
    color: '#ffffff',
    fontWeight: 'bold',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  searchContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#ffffff',
    borderRadius: 20,
    paddingVertical: 12,
    paddingHorizontal: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  searchIcon: {
    fontSize: 18,
    color: '#64748b',
  },
  searchPlaceholder: {
    fontSize: 14,
    color: '#94a3b8',
    marginLeft: 12,
    flex: 1,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginVertical: 12,
  },
  roomsContainer: {
    marginBottom: 16,
  },
  roomsList: {
    flexDirection: 'row',
  },
  roomItem: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 12,
    marginRight: 12,
    alignItems: 'center',
    width: 80,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  selectedRoom: {
    backgroundColor: '#3b82f6',
  },
  roomIcon: {
    fontSize: 24,
    marginBottom: 4,
  },
  roomName: {
    fontSize: 12,
    color: '#64748b',
    fontWeight: '500',
    marginBottom: 2,
  },
  roomCount: {
    fontSize: 10,
    color: '#94a3b8',
  },
  optionsContainer: {
    marginBottom: 16,
  },
  sortContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 8,
  },
  filterContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  optionLabel: {
    fontSize: 14,
    color: '#64748b',
    marginRight: 8,
  },
  optionButton: {
    backgroundColor: '#f1f5f9',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 20,
    marginRight: 8,
  },
  activeOption: {
    backgroundColor: '#3b82f6',
  },
  optionText: {
    fontSize: 12,
    color: '#3b82f6',
    fontWeight: '500',
  },
  activeOptionText: {
    color: '#ffffff',
  },
  roomDetails: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  roomDetailTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  roomItemCount: {
    fontSize: 14,
    color: '#64748b',
  },
  totalCostContainer: {
    backgroundColor: '#3b82f6',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    alignItems: 'center',
  },
  totalCostLabel: {
    fontSize: 14,
    color: '#e0f2fe',
    marginBottom: 4,
  },
  totalCostValue: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#ffffff',
  },
  itemCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    flexDirection: 'row',
    padding: 12,
    marginBottom: 12,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  imageContainer: {
    marginRight: 12,
  },
  imagePlaceholder: {
    width: 80,
    height: 80,
    backgroundColor: '#e2e8f0',
    borderRadius: 8,
    alignItems: 'center',
    justifyContent: 'center',
  },
  imageText: {
    fontSize: 24,
  },
  infoContainer: {
    flex: 1,
  },
  headerContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 4,
  },
  itemName: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    flex: 1,
  },
  itemPrice: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#3b82f6',
  },
  itemCategory: {
    fontSize: 12,
    color: '#64748b',
    marginBottom: 2,
  },
  itemBrand: {
    fontSize: 12,
    color: '#64748b',
    marginBottom: 2,
  },
  itemDate: {
    fontSize: 12,
    color: '#94a3b8',
    marginBottom: 4,
  },
  itemDescription: {
    fontSize: 14,
    color: '#64748b',
    marginBottom: 8,
  },
  actionContainer: {
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  editButton: {
    backgroundColor: '#f1f5f9',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 6,
    marginRight: 8,
  },
  editButtonText: {
    fontSize: 12,
    color: '#3b82f6',
    fontWeight: '500',
  },
  deleteButton: {
    backgroundColor: '#fee2e2',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 6,
  },
  deleteButtonText: {
    fontSize: 12,
    color: '#ef4444',
    fontWeight: '500',
  },
  infoCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginTop: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  infoTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 12,
  },
  infoText: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 22,
    marginBottom: 8,
  },
  bottomNav: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingVertical: 12,
  },
  navItem: {
    alignItems: 'center',
    flex: 1,
  },
  navIcon: {
    fontSize: 20,
    color: '#94a3b8',
    marginBottom: 4,
  },
  navText: {
    fontSize: 12,
    color: '#94a3b8',
  },
});

export default FurnitureRoomApp;

请添加图片描述


打包

接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

在这里插入图片描述

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

在这里插入图片描述

最后运行效果图如下显示:

请添加图片描述

本文介绍了一个基于React Native的家具管理应用实现方案。该应用采用组件化架构设计,通过主应用组件(FurnitureRoomApp)统一管理状态,包含家具列表渲染、房间筛选、搜索排序等功能模块。使用TypeScript定义家具和房间数据类型,确保代码安全性。状态管理采用useState钩子处理多个关键状态,包括房间选择、排序方式等。应用实现了灵活的多条件筛选和排序功能,支持按房间、关键词搜索以及价格/日期/名称排序。家具卡片组件详细展示各类信息并提供编辑删除操作。文章还提出了性能优化建议,如改进FlatList配置和使用useReducer管理复杂状态。整个方案结构清晰,易于维护扩展,为资产管理类应用开发提供了实用参考。

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐