diff --git a/gemini.md b/gemini.md new file mode 100644 index 0000000..7194fd9 --- /dev/null +++ b/gemini.md @@ -0,0 +1,16 @@ +# Gemini Project: MyReactNativeApp + +This is a React Native application built with TypeScript. + +## Project Structure + +- `src/`: Contains the main source code for the application. + - `components/`: Reusable React components. + - `context/`: React context for state management. + - `hooks/`: Custom React hooks. + - `navigation/`: Navigation logic using React Navigation. + - `screens/`: Application screens. + - `services/`: Services for API calls and other business logic. + - `types/`: TypeScript type definitions. +- `android/`: Android specific code. +- `ios/`: iOS specific code. diff --git a/index.js b/index.js index 6011123..0bbc29d 100644 --- a/index.js +++ b/index.js @@ -8,4 +8,6 @@ import { AppRegistry } from 'react-native'; import App from './App'; import { name as appName } from './app.json'; + + AppRegistry.registerComponent(appName, () => App); diff --git a/package-lock.json b/package-lock.json index 6e7396c..a0d6ff7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.11.0", "react": "19.1.0", "react-native": "0.80.1", + "react-native-elements": "^3.4.3", "react-native-gesture-handler": "^2.27.1", "react-native-get-random-values": "^1.11.0", "react-native-safe-area-context": "^5.5.2", @@ -9203,6 +9204,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -10297,6 +10305,15 @@ "node": ">=8" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", @@ -10938,6 +10955,74 @@ } } }, + "node_modules/react-native-elements": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/react-native-elements/-/react-native-elements-3.4.3.tgz", + "integrity": "sha512-VtZc25EecPZyUBER85zFK9ZbY6kkUdcm1ZwJ9hdoGSCr1R/GFgxor4jngOcSYeMvQ+qimd5No44OVJW3rSJECA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@types/react-native-vector-icons": "^6.4.6", + "color": "^3.1.2", + "deepmerge": "^4.2.2", + "hoist-non-react-statics": "^3.3.2", + "lodash.isequal": "^4.5.0", + "opencollective-postinstall": "^2.0.3", + "react-native-ratings": "8.0.4", + "react-native-size-matters": "^0.3.1" + }, + "peerDependencies": { + "react-native-safe-area-context": ">= 3.0.0", + "react-native-vector-icons": ">7.0.0" + } + }, + "node_modules/react-native-elements/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/react-native-elements/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/react-native-elements/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/react-native-elements/node_modules/react-native-ratings": { + "version": "8.0.4", + "resolved": "https://registry.npmmirror.com/react-native-ratings/-/react-native-ratings-8.0.4.tgz", + "integrity": "sha512-Xczu5lskIIRD6BEdz9A0jDRpEck/SFxRqiglkXi0u67yAtI1/pcJC76P4MukCbT8K4BPVl+42w83YqXBoBRl7A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-elements/node_modules/react-native-size-matters": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz", + "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==", + "license": "MIT", + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.27.1", "resolved": "https://registry.npmmirror.com/react-native-gesture-handler/-/react-native-gesture-handler-2.27.1.tgz", diff --git a/package.json b/package.json index 3633f51..66c7fe1 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "axios": "^1.11.0", "react": "19.1.0", "react-native": "0.80.1", + "react-native-elements": "^3.4.3", "react-native-gesture-handler": "^2.27.1", "react-native-get-random-values": "^1.11.0", "react-native-safe-area-context": "^5.5.2", diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 4dab0af..b75943c 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -7,6 +7,7 @@ import TaskEditScreen from '../screens/TaskEditScreen'; import RunScreen from '../screens/RunScreen'; import EditScreen from '../screens/EditScreen'; import SettingsScreen from '../screens/SettingsScreen'; +import LocationScreen from '../screens/LocationScreen'; const HomeStack = createStackNavigator(); const Tab = createBottomTabNavigator(); @@ -74,7 +75,7 @@ export default function AppNavigator() { options={{ headerShown: false }} /> - + ); diff --git a/src/screens/EditScreen.tsx b/src/screens/EditScreen.tsx index 7699e71..e69de29 100644 --- a/src/screens/EditScreen.tsx +++ b/src/screens/EditScreen.tsx @@ -1,18 +0,0 @@ -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; - -export default function EditScreen() { - return ( - - 编辑! - - ); -} - -const styles = StyleSheet.create({ - screenContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, -}); diff --git a/src/screens/LocationScreen.tsx b/src/screens/LocationScreen.tsx new file mode 100644 index 0000000..2e72ec9 --- /dev/null +++ b/src/screens/LocationScreen.tsx @@ -0,0 +1,219 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { StyleSheet, View, FlatList, Text, ActivityIndicator, Alert } from 'react-native'; +import { SearchBar, CheckBox, Button, useTheme, Overlay, ListItem } from '@rneui/themed'; +import api from '../services/api'; + +interface Location { + id: string; + layer_name: string; + is_occupied: boolean; + is_locked: boolean; + is_empty_tray: boolean; + is_disabled: boolean; +} + +const LocationScreen = () => { + const { theme } = useTheme(); + const [locations, setLocations] = useState([]); + const [search, setSearch] = useState(''); + const [loading, setLoading] = useState(true); + const [selectedLocations, setSelectedLocations] = useState([]); + const [isOverlayVisible, setOverlayVisible] = useState(false); + + const fetchData = useCallback(async (query = '') => { + setLoading(true); + try { + const response = await api.get('/api/vwed-operate-point/list', { + params: { layer_name: query }, + }); + setLocations(response.storage_locations); + } catch (error) { + console.error(error); + Alert.alert('错误', '加载库位列表失败。'); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + const handleSearch = () => { + fetchData(search); + }; + + const toggleSelection = (id: string) => { + setSelectedLocations(prev => + prev.includes(id) ? prev.filter(item => item !== id) : [...prev, id] + ); + }; + + const handleBatchUpdate = async (action: string) => { + if (selectedLocations.length === 0) { + Alert.alert('未选择', '请选择要操作的库位。'); + return; + } + setOverlayVisible(false); + try { + const response = await api.put('/api/vwed-operate-point/batch-status', { + layer_names: selectedLocations, + action: action, + }); + + const { success_count, failed_count, results } = response; + let message = `批量操作完成:\n成功 ${success_count} 个, 失败 ${failed_count} 个。\n\n`; + if (failed_count > 0) { + message += '失败详情:\n'; + results.forEach((res: any) => { + if (!res.success) { + message += `${res.layer_name}: ${res.message}\n`; + } + }); + } + Alert.alert('操作结果', message); + + fetchData(search); + setSelectedLocations([]); + } catch (error) { + console.error(error); + Alert.alert('错误', `批量操作失败: ${error.message}`); + } + }; + + const actions = [ + { title: '禁用', action: 'disable' }, + { title: '启用', action: 'enable' }, + { title: '占用', action: 'occupy' }, + { title: '释放', action: 'release' }, + { title: '锁定', action: 'lock' }, + { title: '解锁', action: 'unlock' }, + ]; + + const renderItem = ({ item }: { item: Location }) => ( + + toggleSelection(item.id)} + containerStyle={{ backgroundColor: 'transparent' }} + /> + {item.layer_name} + {item.is_occupied ? '是' : '否'} + {item.is_locked ? '是' : '否'} + {item.is_empty_tray ? '是' : '否'} + {item.is_disabled ? '是' : '否'} + + ); + + if (loading) { + return ( + + + + ); + } + + return ( + + + +