React Native鸿蒙跨平台使用多个useState+ 计算函数 升级为鸿蒙的@State + @DerivedState组合状态管理
本文介绍了一个基于React Native的家具管理应用实现方案。该应用采用组件化架构设计,通过主应用组件(FurnitureRoomApp)统一管理状态,包含家具列表渲染、房间筛选、搜索排序等功能模块。使用TypeScript定义家具和房间数据类型,确保代码安全性。状态管理采用useState钩子处理多个关键状态,包括房间选择、排序方式等。应用实现了灵活的多条件筛选和排序功能,支持按房间、关键词
在移动应用开发中,资产管理类应用是一种常见的应用类型,需要考虑数据展示、分类筛选、搜索排序等多个方面。本文将深入分析一个功能完备的 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 定义了两个核心数据类型:
- FurnitureItem - 家具类型,包含家具的完整信息,如 ID、名称、房间、类别、价格、购买日期、品牌、描述和图片
- 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管理复杂状态。整个方案结构清晰,易于维护扩展,为资产管理类应用开发提供了实用参考。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐




所有评论(0)