feat:替换组件库为react-native-elements
This commit is contained in:
parent
a4372a12fc
commit
9481c802f1
@ -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": []
|
||||
}
|
||||
}
|
||||
}
|
6
App.tsx
6
App.tsx
@ -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
513
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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'
|
||||
}
|
||||
});
|
||||
|
@ -38,6 +38,5 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
},
|
||||
list: {
|
||||
padding: 8,
|
||||
}
|
||||
});
|
||||
paddingHorizontal: 8,
|
||||
}});
|
||||
|
Loading…
x
Reference in New Issue
Block a user