feat:替换组件库为react-native-elements

This commit is contained in:
xudan 2025-07-21 16:45:09 +08:00
parent a4372a12fc
commit 9481c802f1
9 changed files with 433 additions and 377 deletions

View File

@ -4,8 +4,11 @@
"Bash(npm install:*)",
"Bash(mkdir -p:*)",
"Bash(npm install:*)",
"Bash(*:*)"
"Bash(*:*)",
"Bash(npm uninstall react-native-paper react-native-paper-dropdown)",
"Bash(npm uninstall react-native-paper react-native-paper-dropdown --legacy-peer-deps)",
"Bash(npx react-native start --reset-cache)"
],
"deny": []
}
}
}

View File

@ -3,16 +3,16 @@ import { NavigationContainer } from '@react-navigation/native';
import AppNavigator from './src/navigation/AppNavigator';
import { TasksProvider } from './src/context/TasksContext';
import { PaperProvider } from 'react-native-paper';
import { ThemeProvider } from '@rneui/themed';
export default function App() {
return (
<PaperProvider>
<ThemeProvider>
<TasksProvider>
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
</TasksProvider>
</PaperProvider>
</ThemeProvider>
);
}

513
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,12 +14,12 @@
"@react-navigation/bottom-tabs": "^7.4.2",
"@react-navigation/native": "^7.1.14",
"@react-navigation/stack": "^7.4.2",
"@rneui/base": "^4.0.0-rc.8",
"@rneui/themed": "^4.0.0-rc.8",
"react": "19.1.0",
"react-native": "0.80.1",
"react-native-gesture-handler": "^2.27.1",
"react-native-get-random-values": "^1.11.0",
"react-native-paper": "^5.14.5",
"react-native-paper-dropdown": "^2.3.1",
"react-native-safe-area-context": "^5.5.2",
"react-native-screens": "^4.13.1",
"react-native-vector-icons": "^10.2.0",

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Appbar, useTheme } from 'react-native-paper';
import { StyleSheet } from 'react-native';
import { View, StyleSheet } from 'react-native';
import { Button, useTheme } from '@rneui/themed';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
interface BottomActionBarProps {
onRun: () => void;
@ -19,16 +20,16 @@ const BottomActionBar: React.FC<BottomActionBarProps> = ({
onBack,
isSaveDisabled = true,
}) => {
const theme = useTheme();
const { theme } = useTheme();
return (
<Appbar style={[styles.bottom, { backgroundColor: theme.colors.elevation.level2 }]}>
<Appbar.Action icon="arrow-left" onPress={onBack} />
<Appbar.Action icon="play" onPress={onRun} />
<Appbar.Action icon="content-save" onPress={onSave} disabled={isSaveDisabled} />
<Appbar.Action icon="undo" onPress={onUndo} />
<Appbar.Action icon="restore" onPress={onRestore} />
</Appbar>
<View style={[styles.bottom, { backgroundColor: theme.colors.background }]}>
<Button type="clear" onPress={onBack} icon={<Icon name="arrow-left" size={24} color={theme.colors.primary} />} />
<Button type="clear" onPress={onRun} icon={<Icon name="play" size={24} color={theme.colors.primary} />} />
<Button type="clear" onPress={onSave} disabled={isSaveDisabled} icon={<Icon name="content-save" size={24} color={isSaveDisabled ? theme.colors.disabled : theme.colors.primary} />} />
<Button type="clear" onPress={onUndo} icon={<Icon name="undo" size={24} color={theme.colors.primary} />} />
<Button type="clear" onPress={onRestore} icon={<Icon name="restore" size={24} color={theme.colors.primary} />} />
</View>
);
};
@ -38,7 +39,11 @@ const styles = StyleSheet.create({
left: 0,
right: 0,
bottom: 0,
flexDirection: 'row',
justifyContent: 'space-around',
paddingVertical: 8,
borderTopWidth: 1,
borderTopColor: '#e0e0e0',
},
});

View File

@ -1,7 +1,8 @@
import React from 'react';
import { StyleSheet } from 'react-native';
import { Card, Text, Chip, useTheme } from 'react-native-paper';
import { StyleSheet, TouchableOpacity } from 'react-native';
import { Card, Text, Chip, useTheme } from '@rneui/themed';
import { Task, TaskStatus } from '../types/task';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
interface TaskCardProps {
task: Task;
@ -16,36 +17,40 @@ const statusColors: Record<TaskStatus, string> = {
};
const TaskCard: React.FC<TaskCardProps> = ({ task, onPress }) => {
const theme = useTheme();
const { theme } = useTheme();
return (
<Card style={styles.card} onPress={() => onPress(task.id)}>
<Card.Content>
<Text variant="titleMedium" style={styles.title}>
{task.name}
</Text>
<TouchableOpacity onPress={() => onPress(task.id)} style={{ flex: 1, width: '50%' }}>
<Card containerStyle={styles.card}>
<Card.Title style={styles.title}>{task.name}</Card.Title>
<Card.Divider />
<Chip
icon="information"
mode="outlined"
selectedColor={statusColors[task.status]}
style={[styles.chip, { backgroundColor: theme.colors.surface }]}
textStyle={{ fontSize: 12 }}
>
{task.status}
</Chip>
</Card.Content>
</Card>
title={task.status}
icon={{
name: 'information',
type: 'material-community',
size: 20,
color: statusColors[task.status],
}}
type="outline"
containerStyle={styles.chip}
titleStyle={{ color: statusColors[task.status], fontSize: 12 }}
buttonStyle={{ borderColor: statusColors[task.status] }}
/>
</Card>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
card: {
margin: 8,
width: '45%',
margin: 4,
borderRadius: 8,
},
title: {
marginBottom: 12,
minHeight: 50, // Ensure cards have similar height
textAlign: 'left',
},
chip: {
alignSelf: 'flex-start',

View File

@ -1,8 +1,13 @@
import React, { useState } from 'react';
import { View, StyleSheet, ScrollView } from 'react-native';
import { TextInput, Menu, Button, Divider, Text } from 'react-native-paper';
import { View, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
import { Input, BottomSheet, ListItem, Text } from '@rneui/themed';
import { Task, RobotAction } from '../types/task';
import { LOCATIONS, ROBOT_ACTIONS, PAYLOADS, LOCATIONS_BAYS } from '../data/mockData';
import {
LOCATIONS,
ROBOT_ACTIONS,
PAYLOADS,
LOCATIONS_BAYS,
} from '../data/mockData';
interface TaskFormProps {
task: Task;
@ -10,91 +15,117 @@ interface TaskFormProps {
}
const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
const [menuVisible, setMenuVisible] = useState<{ [key: string]: boolean }>({});
const [isVisible, setIsVisible] = useState(false);
const [currentField, setCurrentField] = useState('');
const [currentItems, setCurrentItems] = useState<
{ label: string; value: string }[]
>([]);
const openMenu = (name: string) => setMenuVisible(prev => ({ ...prev, [name]: true }));
const closeMenu = (name: string) => setMenuVisible(prev => ({ ...prev, [name]: false }));
const handleParamChange = (field: string, value: string | RobotAction) => {
const updatedTask = {
...task,
parameters: {
...task.parameters,
[field]: value,
},
};
onTaskChange(updatedTask);
const handleParamChange = (field: string, value: string | RobotAction) => {
const updatedTask = {
...task,
parameters: {
...task.parameters,
[field]: value,
},
};
onTaskChange(updatedTask);
};
const renderDropdown = (
name: string,
label: string,
value: string,
items: { label: string, value: string }[]
) => (
<View style={styles.input}>
<Text>{label}</Text>
<Menu
visible={menuVisible[name]}
onDismiss={() => closeMenu(name)}
anchor={
<Button onPress={() => openMenu(name)} mode="outlined">
{value || `选择${label}`}
</Button>
}
const openBottomSheet = (
field: string,
items: { label: string; value: string }[],
) => {
setCurrentField(field);
setCurrentItems(items);
setIsVisible(true);
};
const renderDropdown = (
name: string,
label: string,
value: string,
items: { label: string; value: string }[],
) => (
<TouchableOpacity onPress={() => openBottomSheet(name, items)}>
<Input
label={label}
value={value}
editable={false}
rightIcon={{ name: 'arrow-drop-down' }}
/>
</TouchableOpacity>
);
return (
<ScrollView contentContainerStyle={styles.container}>
<Input
label="任务名称"
value={task.name}
onChangeText={text => onTaskChange({ ...task, name: text })}
/>
{renderDropdown(
'startLocation',
'起点',
task.parameters.startLocation,
LOCATIONS,
)}
{renderDropdown(
'endLocation',
'终点',
task.parameters.endLocation,
LOCATIONS,
)}
<Input
label="途经点 (可选)"
value={task.parameters.waypoint || ''}
onChangeText={text => handleParamChange('waypoint', text)}
/>
{renderDropdown(
'robotAction',
'机器人动作',
task.parameters.robotAction,
ROBOT_ACTIONS,
)}
{renderDropdown('payload', '载荷', task.parameters.payload, PAYLOADS)}
{renderDropdown(
'locationBay',
'库位',
task.parameters.locationBay,
LOCATIONS_BAYS,
)}
<BottomSheet
isVisible={isVisible}
onBackdropPress={() => setIsVisible(false)}
>
<ScrollView>
{currentItems.map((item, index) => (
<ListItem
key={index}
onPress={() => {
handleParamChange(currentField, item.value);
setIsVisible(false);
}}
>
<ScrollView style={{ maxHeight: 200 }}>
{items.map(item => (
<Menu.Item
key={item.value}
onPress={() => {
handleParamChange(name, item.value);
closeMenu(name);
}}
title={item.label}
/>
))}
</ScrollView>
</Menu>
</View>
);
return (
<ScrollView contentContainerStyle={styles.container}>
<TextInput
label="任务名称"
value={task.name}
onChangeText={text => onTaskChange({ ...task, name: text })}
style={styles.input}
mode="outlined"
/>
{renderDropdown('startLocation', '起点', task.parameters.startLocation, LOCATIONS)}
{renderDropdown('endLocation', '终点', task.parameters.endLocation, LOCATIONS)}
<TextInput
label="途经点 (可选)"
value={task.parameters.waypoint || ''}
onChangeText={text => handleParamChange('waypoint', text)}
style={styles.input}
mode="outlined"
/>
{renderDropdown('robotAction', '机器人动作', task.parameters.robotAction, ROBOT_ACTIONS)}
{renderDropdown('payload', '载荷', task.parameters.payload, PAYLOADS)}
{renderDropdown('locationBay', '库位', task.parameters.locationBay, LOCATIONS_BAYS)}
<ListItem.Content>
<ListItem.Title>{item.label}</ListItem.Title>
</ListItem.Content>
</ListItem>
))}
</ScrollView>
);
</BottomSheet>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
},
input: {
marginBottom: 16,
},
container: {
padding: 16,
},
});
export default TaskForm;

View File

@ -6,7 +6,7 @@ import { useTasks } from '../context/TasksContext';
import TaskForm from '../components/TaskForm';
import BottomActionBar from '../components/BottomActionBar';
import { Task } from '../types/task';
import { ActivityIndicator } from 'react-native-paper';
import { Dialog } from '@rneui/themed';
type RootStackParamList = {
TaskEdit: { taskId: string };
@ -67,7 +67,11 @@ export default function TaskEditScreen() {
}
if (!task) {
return <ActivityIndicator animating={true} size="large" style={styles.loader}/>;
return (
<Dialog isVisible={true}>
<Dialog.Loading />
</Dialog>
);
}
return (
@ -89,10 +93,6 @@ const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingBottom: 60,
},
loader: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});

View File

@ -38,6 +38,5 @@ const styles = StyleSheet.create({
flex: 1,
},
list: {
padding: 8,
}
});
paddingHorizontal: 8,
}});