Compare commits
5 Commits
d8d7355707
...
484b9c332b
| Author | SHA1 | Date | |
|---|---|---|---|
| 484b9c332b | |||
| b80d368d5e | |||
| b6c8db84a8 | |||
| e937988771 | |||
| 1705ace101 |
40
App.tsx
40
App.tsx
|
|
@ -1,25 +1,3 @@
|
||||||
// /**
|
|
||||||
// * Sample React Native App
|
|
||||||
// * https://github.com/facebook/react-native
|
|
||||||
// *
|
|
||||||
// * @format
|
|
||||||
// */
|
|
||||||
//
|
|
||||||
// import React from 'react';
|
|
||||||
// import Navigation from './src/navigation';
|
|
||||||
// import {ThemeProvider} from './src/contexts/ThemeContext';
|
|
||||||
//
|
|
||||||
// function App(): JSX.Element {
|
|
||||||
// return (
|
|
||||||
// <ThemeProvider>
|
|
||||||
// <Navigation />
|
|
||||||
// </ThemeProvider>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// export default App;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sample React Native App
|
* Sample React Native App
|
||||||
* https://github.com/facebook/react-native
|
* https://github.com/facebook/react-native
|
||||||
|
|
@ -27,15 +5,17 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useState, useEffect, useRef} from 'react';
|
||||||
import Navigation from './src/navigation';
|
import Navigation from './src/navigation';
|
||||||
import {ThemeProvider} from './src/contexts/ThemeContext';
|
import {ThemeProvider} from './src/contexts/ThemeContext';
|
||||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||||
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
|
import {View, Text, ActivityIndicator, StyleSheet} from 'react-native';
|
||||||
|
import {BeautifulDialog, DialogUtils} from './src/utils/DialogUtils';
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
const [fontLoaded, setFontLoaded] = useState(false);
|
const [fontLoaded, setFontLoaded] = useState(false);
|
||||||
const [fontError, setFontError] = useState<string | null>(null);
|
const [fontError, setFontError] = useState<string | null>(null);
|
||||||
|
const dialogRef = useRef<any>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadFont = async () => {
|
const loadFont = async () => {
|
||||||
|
|
@ -66,14 +46,19 @@ function App(): JSX.Element {
|
||||||
return () => clearTimeout(timeout);
|
return () => clearTimeout(timeout);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 设置弹窗引用
|
||||||
|
useEffect(() => {
|
||||||
|
if (dialogRef.current) {
|
||||||
|
DialogUtils.setDialogRef(dialogRef.current);
|
||||||
|
}
|
||||||
|
}, [fontLoaded]);
|
||||||
|
|
||||||
// 显示加载状态或错误信息
|
// 显示加载状态或错误信息
|
||||||
if (!fontLoaded || fontError) {
|
if (!fontLoaded || fontError) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ActivityIndicator size="large" />
|
<ActivityIndicator size="large" />
|
||||||
<Text style={styles.statusText}>
|
<Text style={styles.statusText}>{fontError || 'Loading icons...'}</Text>
|
||||||
{fontError || 'Loading icons...'}
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.debugText}>
|
<Text style={styles.debugText}>
|
||||||
Using MaterialIcons font family: {MaterialIcons.getFontFamily()}
|
Using MaterialIcons font family: {MaterialIcons.getFontFamily()}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -84,6 +69,7 @@ function App(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<Navigation />
|
<Navigation />
|
||||||
|
<BeautifulDialog ref={dialogRef} />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "pda_react_native_template",
|
"name": "pda_react_native_template",
|
||||||
"version": "0.0.1",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"react-native": "0.75.5",
|
"react-native": "0.75.5",
|
||||||
"react-native-chart-kit": "^6.12.0",
|
"react-native-chart-kit": "^6.12.0",
|
||||||
"react-native-linear-gradient": "^2.8.3",
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
|
"react-native-modal": "^14.0.0-rc.1",
|
||||||
"react-native-safe-area-context": "^5.5.1",
|
"react-native-safe-area-context": "^5.5.1",
|
||||||
"react-native-screens": "^4.5.0",
|
"react-native-screens": "^4.5.0",
|
||||||
"react-native-svg": "^15.12.0",
|
"react-native-svg": "^15.12.0",
|
||||||
|
|
|
||||||
26
src/App.tsx
26
src/App.tsx
|
|
@ -1,26 +0,0 @@
|
||||||
/**
|
|
||||||
* Sample React Native App
|
|
||||||
* https://github.com/facebook/react-native
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {StatusBar} from 'react-native';
|
|
||||||
import Navigation from './navigation';
|
|
||||||
import {ThemeProvider} from './contexts/ThemeContext';
|
|
||||||
import {theme} from './constants/theme';
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
return (
|
|
||||||
<ThemeProvider theme={theme}>
|
|
||||||
<StatusBar
|
|
||||||
backgroundColor={theme.colors.primary}
|
|
||||||
barStyle="light-content"
|
|
||||||
/>
|
|
||||||
<Navigation />
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
@ -1,42 +1,28 @@
|
||||||
// 将Flutter的颜色值转换为React Native可用的格式
|
|
||||||
// Flutter: 0xff05dcef -> React Native: #05dcef
|
|
||||||
|
|
||||||
export const theme = {
|
export const theme = {
|
||||||
colors: {
|
colors: {
|
||||||
// 基础颜色
|
// 基础颜色
|
||||||
primary: '#4158D0', // 主色调
|
aqua: '#05DCEF', // 青色主色调 (0xff05dcef)
|
||||||
secondary: '#C850C0', // 次要色调
|
background: '#ffffff', // 背景色
|
||||||
tertiary: '#FFCC70', // 第三色调
|
backgroundGray: '#F5F5F5', // 灰色背景
|
||||||
background: '#ffffff', // 背景色
|
text: '#333333', // 文本颜色
|
||||||
text: '#333333', // 文本颜色
|
textLight: '#666666', // 次要文本颜色
|
||||||
textLight: '#666666', // 次要文本颜色
|
border: '#dddddd', // 边框颜色
|
||||||
border: '#dddddd', // 边框颜色
|
error: '#ff3b30', // 错误颜色
|
||||||
error: '#ff3b30', // 错误颜色
|
cyan: '#00FFFF', // 青色背景
|
||||||
success: '#4cd964', // 成功颜色
|
lightGreen: '#7FFFAA', // 浅绿色
|
||||||
warning: '#ff9500', // 警告颜色
|
|
||||||
cyan: '#00FFFF', // 青色背景
|
|
||||||
|
|
||||||
// 渐变色配置
|
// 渐变色配置
|
||||||
gradients: {
|
gradients: {
|
||||||
primary: ['#4158D0', '#C850C0', '#FFCC70'], // 主要渐变(蓝紫金)
|
primary: ['#05DCEF', '#7DE2F5', '#B8F2FF'], // 主要渐变(青蓝白,小清新)
|
||||||
contrast: ['#00F5A0', '#00D9F5'], // 对比渐变(绿青)
|
contrast: ['#00F5A0', '#00D9F5'], // 对比渐变(绿青)
|
||||||
card: ['#9795F0', '#E3C3F1'], // 卡片渐变(柔和紫色)
|
header: ['#05DCEF', '#7DE2F5'], // 头部渐变
|
||||||
login: ['#4158D0', '#C850C0', '#FFCC70'], // 登录页面专用渐变
|
button: ['#05DCEF', '#7DE2F5'], // 按钮渐变
|
||||||
loginWave: ['rgba(255, 255, 255, 0.3)', 'rgba(255, 255, 255, 0.2)'], // 登录页面波浪渐变
|
|
||||||
header: ['#4158D0', '#C850C0'], // 头部渐变
|
|
||||||
button: ['#4158D0', '#C850C0'], // 按钮渐变
|
|
||||||
// 波浪背景渐变
|
|
||||||
wave: {
|
|
||||||
start: 'rgba(255, 255, 255, 0.3)',
|
|
||||||
middle: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
end: 'rgba(255, 255, 255, 0.1)'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 图表颜色
|
// 图表颜色
|
||||||
chart: {
|
chart: {
|
||||||
blue: '#0077B6', // 深蓝色
|
cyan: '#00FFFF', // 青色
|
||||||
orange: '#FB8500', // 明亮的橙色
|
lightGreen: '#7FFFAA', // 浅绿色
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -87,7 +73,7 @@ export const theme = {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 4,
|
height: 4,
|
||||||
},
|
},
|
||||||
shadowOpacity: 0.30,
|
shadowOpacity: 0.3,
|
||||||
shadowRadius: 4.65,
|
shadowRadius: 4.65,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
},
|
},
|
||||||
|
|
@ -102,12 +88,6 @@ export const theme = {
|
||||||
elevation: 6,
|
elevation: 6,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// 波浪效果配置
|
|
||||||
wave: {
|
|
||||||
height: 60,
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 类型定义
|
// 类型定义
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
class ScannerService {
|
|
||||||
async init(): Promise<void> {
|
|
||||||
// 初始化扫描器
|
|
||||||
console.log('Scanner initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
async startScan(): Promise<string> {
|
|
||||||
// 开始扫描
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
// 模拟扫描结果
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve('Scanned barcode result');
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async stopScan(): Promise<void> {
|
|
||||||
// 停止扫描
|
|
||||||
console.log('Scanner stopped');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const scannerService = new ScannerService();
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import {useState, useCallback} from 'react';
|
|
||||||
import {scannerService} from '../device/scanner';
|
|
||||||
|
|
||||||
export const useScanner = () => {
|
|
||||||
const [isScanning, setIsScanning] = useState(false);
|
|
||||||
const [result, setResult] = useState<string | null>(null);
|
|
||||||
const [error, setError] = useState<Error | null>(null);
|
|
||||||
|
|
||||||
const startScan = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
setIsScanning(true);
|
|
||||||
setError(null);
|
|
||||||
const scanResult = await scannerService.startScan();
|
|
||||||
setResult(scanResult);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err as Error);
|
|
||||||
} finally {
|
|
||||||
setIsScanning(false);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const stopScan = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
await scannerService.stopScan();
|
|
||||||
setIsScanning(false);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err as Error);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isScanning,
|
|
||||||
result,
|
|
||||||
error,
|
|
||||||
startScan,
|
|
||||||
stopScan,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
||||||
import {LoginScreen} from '../screens/auth/LoginScreen';
|
import {LoginScreen} from '../screens/auth/LoginScreen';
|
||||||
import {HomeScreen} from '../screens/home/HomeScreen';
|
import {HomeScreen} from '../screens/home/HomeScreen';
|
||||||
import {StockInEmpty} from '../screens/stockIn/StockInEmpty';
|
import {StockInEmpty} from '../screens/stockIn/StockInEmpty';
|
||||||
import StockInWheelManual from '../screens/stockIn/StockInWheelManual';
|
import {StockInManual} from '../screens/stockIn/StockInManual';
|
||||||
import {RootStackParamList} from './types';
|
import {RootStackParamList} from './types';
|
||||||
import {Platform} from 'react-native';
|
import {Platform} from 'react-native';
|
||||||
import {screensEnabled, enableScreens} from 'react-native-screens';
|
import {screensEnabled, enableScreens} from 'react-native-screens';
|
||||||
|
|
@ -50,8 +50,8 @@ const Navigation = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="StockInWheelManual"
|
name="StockInManual"
|
||||||
component={StockInWheelManual}
|
component={StockInManual}
|
||||||
options={{
|
options={{
|
||||||
animation: 'none',
|
animation: 'none',
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ export type RootStackParamList = {
|
||||||
Login: undefined;
|
Login: undefined;
|
||||||
Home: undefined;
|
Home: undefined;
|
||||||
StockInEmpty: undefined;
|
StockInEmpty: undefined;
|
||||||
StockInWheelEBS: undefined;
|
StockInEBS: undefined;
|
||||||
StockInWheelManual: undefined;
|
StockInManual: undefined;
|
||||||
Pick: undefined;
|
Pick: undefined;
|
||||||
StockCheck: undefined;
|
StockCheck: undefined;
|
||||||
// 在这里添加其他页面的路由参数定义
|
// 在这里添加其他页面的路由参数定义
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export const LoginScreen: React.FC = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const handleLogin = useCallback(() => {
|
const handleLogin = useCallback(() => {
|
||||||
if (!isFocused) return;
|
if (!isFocused) {return;}
|
||||||
|
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
|
|
@ -51,7 +51,7 @@ export const LoginScreen: React.FC = () => {
|
||||||
<View style={styles.contentContainer}>
|
<View style={styles.contentContainer}>
|
||||||
{/* 标题区域 */}
|
{/* 标题区域 */}
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<Text style={styles.title}>景旺冷冻仓</Text>
|
<Text style={styles.title}>仓库模板</Text>
|
||||||
<Text style={styles.subtitle}>智能仓储管理系统</Text>
|
<Text style={styles.subtitle}>智能仓储管理系统</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
@ -60,8 +60,8 @@ export const LoginScreen: React.FC = () => {
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<View style={styles.logoWrapper}>
|
<View style={styles.logoWrapper}>
|
||||||
<View style={[styles.logoCircle, theme.shadow.medium]}>
|
<View style={[styles.logoCircle, theme.shadow.medium]}>
|
||||||
<Text style={[styles.logoText, {color: theme.colors.primary}]}>
|
<Text style={[styles.logoText, {color: theme.colors.aqua}]}>
|
||||||
JW
|
BK
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -83,7 +83,7 @@ export const LoginScreen: React.FC = () => {
|
||||||
{/* 底部版权信息 */}
|
{/* 底部版权信息 */}
|
||||||
<View style={styles.footer}>
|
<View style={styles.footer}>
|
||||||
<Text style={[styles.footerText, {color: theme.colors.textLight}]}>
|
<Text style={[styles.footerText, {color: theme.colors.textLight}]}>
|
||||||
© 2024 景旺电子
|
© 2025 菲达宝开
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -8,16 +8,16 @@ import {
|
||||||
ScrollView,
|
ScrollView,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import {useNavigation} from '@react-navigation/native';
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
|
||||||
import { RootStackParamList } from '../../navigation/types';
|
import {RootStackParamList} from '../../navigation/types';
|
||||||
import { PieChart } from 'react-native-chart-kit';
|
import {PieChart} from 'react-native-chart-kit';
|
||||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
import { useTheme } from '../../contexts/ThemeContext';
|
import {useTheme} from '../../contexts/ThemeContext';
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import Svg, { Path } from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
|
|
||||||
const { width } = Dimensions.get('window');
|
const {width} = Dimensions.get('window');
|
||||||
|
|
||||||
type HomeScreenNavigationProp = NativeStackNavigationProp<
|
type HomeScreenNavigationProp = NativeStackNavigationProp<
|
||||||
RootStackParamList,
|
RootStackParamList,
|
||||||
|
|
@ -48,22 +48,22 @@ export const HomeScreen: React.FC = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const menuItems: MenuItem[] = [
|
const menuItems: MenuItem[] = [
|
||||||
{ title: '空载具入库', icon: 'local-shipping', route: 'StockInEmpty' },
|
{title: '空载具入库', icon: 'local-shipping', route: 'StockInEmpty'},
|
||||||
{ title: '手动码盘入库', icon: 'inventory', route: 'StockInWheelManual' },
|
{title: '手动码盘入库', icon: 'inventory', route: 'StockInManual'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const stockData = [
|
const stockData = [
|
||||||
{
|
{
|
||||||
name: '空闲',
|
name: '空闲',
|
||||||
population: 40,
|
population: 40,
|
||||||
color: theme.colors.chart.blue,
|
color: theme.colors.chart.cyan,
|
||||||
legendFontColor: theme.colors.textLight,
|
legendFontColor: theme.colors.textLight,
|
||||||
legendFontSize: theme.fontSize.regular,
|
legendFontSize: theme.fontSize.regular,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '占用',
|
name: '占用',
|
||||||
population: 60,
|
population: 60,
|
||||||
color: theme.colors.chart.orange,
|
color: theme.colors.chart.lightGreen,
|
||||||
legendFontColor: theme.colors.textLight,
|
legendFontColor: theme.colors.textLight,
|
||||||
legendFontSize: theme.fontSize.regular,
|
legendFontSize: theme.fontSize.regular,
|
||||||
},
|
},
|
||||||
|
|
@ -77,23 +77,30 @@ export const HomeScreen: React.FC = () => {
|
||||||
|
|
||||||
if (!fontLoaded) {
|
if (!fontLoaded) {
|
||||||
return (
|
return (
|
||||||
<View style={[styles.loadingContainer, { backgroundColor: theme.colors.background }]}>
|
<View
|
||||||
<Text style={{ color: theme.colors.text }}>加载图标中...</Text>
|
style={[
|
||||||
|
styles.loadingContainer,
|
||||||
|
{backgroundColor: theme.colors.backgroundGray},
|
||||||
|
]}>
|
||||||
|
<Text style={{color: theme.colors.text}}>加载图标中...</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.container, { backgroundColor: theme.colors.background }]}>
|
<SafeAreaView
|
||||||
|
style={[
|
||||||
|
styles.container,
|
||||||
|
{backgroundColor: theme.colors.backgroundGray},
|
||||||
|
]}>
|
||||||
{/* 头部区域 */}
|
{/* 头部区域 */}
|
||||||
<View style={styles.headerSection}>
|
<View style={styles.headerSection}>
|
||||||
{/* 渐变背景 */}
|
{/* 渐变背景 */}
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={theme.colors.gradients.primary}
|
colors={theme.colors.gradients.primary}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
start={{ x: 0, y: 0 }}
|
start={{x: 0, y: 0}}
|
||||||
end={{ x: 1, y: 1 }}
|
end={{x: 1, y: 1}}>
|
||||||
>
|
|
||||||
{/* 半透明波浪效果 */}
|
{/* 半透明波浪效果 */}
|
||||||
<View style={styles.waveOverlay}>
|
<View style={styles.waveOverlay}>
|
||||||
<Svg height="100%" width={width} style={styles.waveSvg}>
|
<Svg height="100%" width={width} style={styles.waveSvg}>
|
||||||
|
|
@ -131,17 +138,25 @@ export const HomeScreen: React.FC = () => {
|
||||||
style={styles.menuButton}>
|
style={styles.menuButton}>
|
||||||
<Icon name="menu" size={24} color={theme.colors.background} />
|
<Icon name="menu" size={24} color={theme.colors.background} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={[styles.headerTitle, { color: theme.colors.background }]}>
|
<Text style={[styles.headerTitle, {color: theme.colors.background}]}>
|
||||||
景旺WMS移动终端(冷冻)
|
WMS移动终端(模板)
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity style={styles.iconButton}>
|
<TouchableOpacity style={styles.iconButton}>
|
||||||
<Icon name="notifications" size={24} color={theme.colors.background} />
|
<Icon
|
||||||
|
name="notifications"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.background}
|
||||||
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 主体内容 */}
|
{/* 主体内容 */}
|
||||||
<View style={[styles.mainContent, { backgroundColor: '#FFFFFF' }]}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.mainContent,
|
||||||
|
{backgroundColor: theme.colors.backgroundGray},
|
||||||
|
]}>
|
||||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||||
{/* 快捷操作区 */}
|
{/* 快捷操作区 */}
|
||||||
<View style={styles.quickActions}>
|
<View style={styles.quickActions}>
|
||||||
|
|
@ -157,9 +172,17 @@ export const HomeScreen: React.FC = () => {
|
||||||
end={{x: 1, y: 0}}
|
end={{x: 1, y: 0}}
|
||||||
/>
|
/>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<Icon name={item.icon} size={32} color={theme.colors.background} />
|
<Icon
|
||||||
|
name={item.icon}
|
||||||
|
size={32}
|
||||||
|
color={theme.colors.background}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={[styles.actionTitle, {color: theme.colors.background}]}>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.actionTitle,
|
||||||
|
{color: theme.colors.background},
|
||||||
|
]}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
@ -190,29 +213,39 @@ export const HomeScreen: React.FC = () => {
|
||||||
{/* 侧边菜单 */}
|
{/* 侧边菜单 */}
|
||||||
{isDrawerOpen && (
|
{isDrawerOpen && (
|
||||||
<>
|
<>
|
||||||
<View style={[styles.drawer, { backgroundColor: theme.colors.background }]}>
|
<View
|
||||||
|
style={[styles.drawer, {backgroundColor: theme.colors.background}]}>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={theme.colors.gradients.primary}
|
colors={theme.colors.gradients.primary}
|
||||||
style={styles.drawerHeader}>
|
style={styles.drawerHeader}>
|
||||||
<Text style={[styles.drawerTitle, { color: theme.colors.background }]}>
|
<Text
|
||||||
景旺电子
|
style={[styles.drawerTitle, {color: theme.colors.background}]}>
|
||||||
|
仓库模板
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.drawerSubtitle, { color: theme.colors.background }]}>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.drawerSubtitle,
|
||||||
|
{color: theme.colors.background},
|
||||||
|
]}>
|
||||||
欢迎使用WMS移动终端
|
欢迎使用WMS移动终端
|
||||||
</Text>
|
</Text>
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
{menuItems.map((item, index) => (
|
{menuItems.map((item, index) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={index}
|
key={index}
|
||||||
style={[styles.drawerItem, { borderBottomColor: theme.colors.border }]}
|
style={[
|
||||||
|
styles.drawerItem,
|
||||||
|
{borderBottomColor: theme.colors.border},
|
||||||
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setIsDrawerOpen(false);
|
setIsDrawerOpen(false);
|
||||||
navigation.navigate(item.route);
|
navigation.navigate(item.route);
|
||||||
}}>
|
}}>
|
||||||
<View style={styles.drawerIconContainer}>
|
<View style={styles.drawerIconContainer}>
|
||||||
<Icon name={item.icon} size={28} color={theme.colors.primary} />
|
<Icon name={item.icon} size={28} color={theme.colors.aqua} />
|
||||||
</View>
|
</View>
|
||||||
<Text style={[styles.drawerItemText, { color: theme.colors.text }]}>
|
<Text
|
||||||
|
style={[styles.drawerItemText, {color: theme.colors.text}]}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,9 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Alert,
|
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {httpService} from '../../services/http';
|
|
||||||
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
|
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import {useNavigation} from '@react-navigation/native';
|
||||||
import {RootStackParamList} from '../../navigation/types';
|
import {RootStackParamList} from '../../navigation/types';
|
||||||
|
|
@ -17,11 +15,16 @@ import {useTheme} from '../../contexts/ThemeContext';
|
||||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
|
import axios from 'axios';
|
||||||
|
import {DialogUtils} from '../../utils';
|
||||||
|
|
||||||
interface StockInEmptyResponse {
|
interface StockInEmptyResponse {
|
||||||
code: number;
|
code: number;
|
||||||
message: string;
|
message: string;
|
||||||
data: string;
|
data: {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type StockInEmptyScreenNavigationProp = NativeStackNavigationProp<
|
type StockInEmptyScreenNavigationProp = NativeStackNavigationProp<
|
||||||
|
|
@ -38,55 +41,53 @@ export const StockInEmpty: React.FC = () => {
|
||||||
// 空托入库方法
|
// 空托入库方法
|
||||||
const handleEmptyIn = async () => {
|
const handleEmptyIn = async () => {
|
||||||
if (!vehicleNo.trim()) {
|
if (!vehicleNo.trim()) {
|
||||||
Alert.alert('警告', '请先填写载具号', [
|
DialogUtils.showWarningMessage('警告', '请先填写载具号', '返回填写');
|
||||||
{text: '返回填写', style: 'cancel'},
|
|
||||||
]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const response = await httpService.post<StockInEmptyResponse>(
|
const response = await axios.post<StockInEmptyResponse>(
|
||||||
'/api/vehicle/empty-in',
|
'/api/vehicle/empty-in',
|
||||||
{vehicleNo: vehicleNo.trim()}
|
{vehicleNo: vehicleNo.trim()},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.code !== 200) {
|
if (response.status !== 200) {
|
||||||
Alert.alert('警告', '服务器请求失败', [
|
DialogUtils.showWarningMessage('警告', '服务器请求失败', '我知道了');
|
||||||
{text: '我知道了', style: 'cancel'},
|
|
||||||
]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保response.data是字符串类型
|
const responseData = response.data;
|
||||||
const responseData = typeof response.data === 'string'
|
|
||||||
? JSON.parse(response.data)
|
|
||||||
: response.data;
|
|
||||||
|
|
||||||
if (responseData.code === 200) {
|
if (responseData.code === 200) {
|
||||||
Alert.alert('成功', '', [
|
DialogUtils.showSuccessMessage('成功', '空载具入库成功', '我知道了');
|
||||||
{text: '我知道了', style: 'default'},
|
|
||||||
]);
|
|
||||||
setVehicleNo('');
|
setVehicleNo('');
|
||||||
} else {
|
} else {
|
||||||
Alert.alert('警告', `服务器返回失败:${responseData.message}`, [
|
DialogUtils.showWarningMessage(
|
||||||
{text: '我知道了', style: 'cancel'},
|
'警告',
|
||||||
]);
|
`服务器返回失败:${responseData.message}`,
|
||||||
|
'我知道了',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Alert.alert('请求发生错误', `请求服务器发生错误:${error}`, [
|
DialogUtils.showErrorMessage(
|
||||||
{text: '我知道了', style: 'cancel'},
|
'请求发生错误',
|
||||||
]);
|
`请求服务器发生错误:${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`,
|
||||||
|
'我知道了',
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.container, {backgroundColor: theme.colors.background}]}>
|
<SafeAreaView
|
||||||
|
style={[styles.container, {backgroundColor: theme.colors.background}]}>
|
||||||
{/* 头部导航栏 */}
|
{/* 头部导航栏 */}
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[theme.colors.primary, theme.colors.secondary, theme.colors.tertiary]}
|
colors={theme.colors.gradients.primary}
|
||||||
start={{x: 0, y: 0}}
|
start={{x: 0, y: 0}}
|
||||||
end={{x: 1, y: 0}}
|
end={{x: 1, y: 0}}
|
||||||
style={styles.header}>
|
style={styles.header}>
|
||||||
|
|
@ -95,7 +96,9 @@ export const StockInEmpty: React.FC = () => {
|
||||||
style={styles.backButton}>
|
style={styles.backButton}>
|
||||||
<Icon name="arrow-back" size={24} color={theme.colors.background} />
|
<Icon name="arrow-back" size={24} color={theme.colors.background} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={[styles.headerTitle, {color: theme.colors.background}]}>空载具入库</Text>
|
<Text style={[styles.headerTitle, {color: theme.colors.background}]}>
|
||||||
|
空载具入库
|
||||||
|
</Text>
|
||||||
<View style={styles.headerRight} />
|
<View style={styles.headerRight} />
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
|
|
||||||
|
|
@ -108,23 +111,25 @@ export const StockInEmpty: React.FC = () => {
|
||||||
style={styles.waveSvg}
|
style={styles.waveSvg}
|
||||||
preserveAspectRatio="none">
|
preserveAspectRatio="none">
|
||||||
<Path
|
<Path
|
||||||
fill={`${theme.colors.tertiary}40`}
|
fill={`${theme.colors.aqua}30`}
|
||||||
d="M0,96L48,112C96,128,192,160,288,186.7C384,213,480,235,576,213.3C672,192,768,128,864,128C960,128,1056,192,1152,213.3C1248,235,1344,213,1392,202.7L1440,192L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z"
|
d="M0,96L48,112C96,128,192,160,288,186.7C384,213,480,235,576,213.3C672,192,768,128,864,128C960,128,1056,192,1152,213.3C1248,235,1344,213,1392,202.7L1440,192L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z"
|
||||||
/>
|
/>
|
||||||
</Svg>
|
</Svg>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 主体内容 */}
|
{/* 主体内容 */}
|
||||||
<View style={styles.content}>
|
<View
|
||||||
<LinearGradient
|
style={[
|
||||||
colors={theme.colors.gradients.contrast}
|
styles.content,
|
||||||
style={StyleSheet.absoluteFill}
|
{backgroundColor: theme.colors.backgroundGray},
|
||||||
start={{x: 0, y: 0}}
|
]}>
|
||||||
end={{x: 1, y: 0}}
|
|
||||||
/>
|
|
||||||
{/* 说明文字 */}
|
{/* 说明文字 */}
|
||||||
<View style={[styles.infoSection, {backgroundColor: `${theme.colors.primary}15`}]}>
|
<View
|
||||||
<Icon name="info-outline" size={24} color={theme.colors.primary} />
|
style={[
|
||||||
|
styles.infoSection,
|
||||||
|
{backgroundColor: `${theme.colors.aqua}15`},
|
||||||
|
]}>
|
||||||
|
<Icon name="info-outline" size={24} color={theme.colors.aqua} />
|
||||||
<Text style={[styles.infoText, {color: theme.colors.text}]}>
|
<Text style={[styles.infoText, {color: theme.colors.text}]}>
|
||||||
请扫描或输入载具号进行空载具入库操作
|
请扫描或输入载具号进行空载具入库操作
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -135,11 +140,20 @@ export const StockInEmpty: React.FC = () => {
|
||||||
<Text style={[styles.inputLabel, {color: theme.colors.text}]}>
|
<Text style={[styles.inputLabel, {color: theme.colors.text}]}>
|
||||||
载具号 <Text style={styles.required}>*</Text>
|
载具号 <Text style={styles.required}>*</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<View style={[styles.inputWrapper, {
|
<View
|
||||||
borderColor: theme.colors.border,
|
style={[
|
||||||
backgroundColor: `${theme.colors.background}CC`,
|
styles.inputWrapper,
|
||||||
}]}>
|
{
|
||||||
<Icon name="qr-code-scanner" size={24} color={theme.colors.primary} style={styles.inputIcon} />
|
borderColor: theme.colors.border,
|
||||||
|
backgroundColor: `${theme.colors.background}CC`,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Icon
|
||||||
|
name="qr-code-scanner"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.aqua}
|
||||||
|
style={styles.inputIcon}
|
||||||
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, {color: theme.colors.text}]}
|
style={[styles.input, {color: theme.colors.text}]}
|
||||||
value={vehicleNo}
|
value={vehicleNo}
|
||||||
|
|
@ -160,7 +174,7 @@ export const StockInEmpty: React.FC = () => {
|
||||||
|
|
||||||
{/* 提交按钮 */}
|
{/* 提交按钮 */}
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[theme.colors.primary, theme.colors.secondary]}
|
colors={theme.colors.gradients.button}
|
||||||
start={{x: 0, y: 0}}
|
start={{x: 0, y: 0}}
|
||||||
end={{x: 1, y: 0}}
|
end={{x: 1, y: 0}}
|
||||||
style={[styles.submitButton, loading && styles.submitButtonDisabled]}>
|
style={[styles.submitButton, loading && styles.submitButtonDisabled]}>
|
||||||
|
|
@ -172,8 +186,17 @@ export const StockInEmpty: React.FC = () => {
|
||||||
<ActivityIndicator color={theme.colors.background} />
|
<ActivityIndicator color={theme.colors.background} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Icon name="save" size={24} color={theme.colors.background} style={styles.submitIcon} />
|
<Icon
|
||||||
<Text style={[styles.submitButtonText, {color: theme.colors.background}]}>
|
name="save"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.background}
|
||||||
|
style={styles.submitIcon}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.submitButtonText,
|
||||||
|
{color: theme.colors.background},
|
||||||
|
]}>
|
||||||
确认入库
|
确认入库
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import {
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Alert,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import {useNavigation} from '@react-navigation/native';
|
||||||
|
|
@ -14,6 +13,7 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
import {useTheme} from '../../contexts/ThemeContext';
|
import {useTheme} from '../../contexts/ThemeContext';
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
|
import {DialogUtils, MessageItem} from '../../utils';
|
||||||
|
|
||||||
interface PackageDataItem {
|
interface PackageDataItem {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -34,7 +34,7 @@ interface PackageDataItem {
|
||||||
lineLocationId: string;
|
lineLocationId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StockInWheelManual: React.FC = () => {
|
export const StockInManual: React.FC = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const [vehicleCode, setVehicleCode] = useState('');
|
const [vehicleCode, setVehicleCode] = useState('');
|
||||||
|
|
@ -42,9 +42,6 @@ const StockInWheelManual: React.FC = () => {
|
||||||
const [packageData, setPackageData] = useState<PackageDataItem[]>([]);
|
const [packageData, setPackageData] = useState<PackageDataItem[]>([]);
|
||||||
const [packageDataId, setPackageDataId] = useState(0);
|
const [packageDataId, setPackageDataId] = useState(0);
|
||||||
const [lastProcessedVehicleCode, setLastProcessedVehicleCode] = useState('');
|
const [lastProcessedVehicleCode, setLastProcessedVehicleCode] = useState('');
|
||||||
const [areaID, setAreaID] = useState('有卤');
|
|
||||||
const [status, setStatus] = useState('合格');
|
|
||||||
const [factory, setFactory] = useState('二厂');
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const vehicleInputRef = useRef<TextInput>(null);
|
const vehicleInputRef = useRef<TextInput>(null);
|
||||||
|
|
@ -52,25 +49,30 @@ const StockInWheelManual: React.FC = () => {
|
||||||
|
|
||||||
const resolveVehicle = useCallback(() => {
|
const resolveVehicle = useCallback(() => {
|
||||||
const code = vehicleCode.trim();
|
const code = vehicleCode.trim();
|
||||||
if (code.length === 0 || code === lastProcessedVehicleCode) {return;}
|
if (code.length === 0 || code === lastProcessedVehicleCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (code.length === 15) {
|
if (code.length === 15) {
|
||||||
setLastProcessedVehicleCode(code);
|
setLastProcessedVehicleCode(code);
|
||||||
Alert.alert('扫码成功', `绑定托盘: ${code}`);
|
DialogUtils.showSuccessMessage('扫码成功', `绑定托盘: ${code}`);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert('绑定托盘失败', '无效的载具号长度');
|
DialogUtils.showErrorMessage('绑定托盘失败', '无效的载具号长度');
|
||||||
}
|
}
|
||||||
}, [vehicleCode, lastProcessedVehicleCode]);
|
}, [vehicleCode, lastProcessedVehicleCode]);
|
||||||
|
|
||||||
const resolveCode = useCallback(() => {
|
const resolveCode = useCallback(() => {
|
||||||
if (!goodsCode) {
|
if (!goodsCode) {
|
||||||
Alert.alert('警告', '条码文本框内无数据,请先扫描或者输入数据');
|
DialogUtils.showWarningMessage(
|
||||||
|
'警告',
|
||||||
|
'条码文本框内无数据,请先扫描或者输入数据',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeData = goodsCode.split(',');
|
const codeData = goodsCode.split(',');
|
||||||
if (![6, 7, 8].includes(codeData.length)) {
|
if (![6, 7, 8].includes(codeData.length)) {
|
||||||
Alert.alert('警告', '条码格式错误');
|
DialogUtils.showWarningMessage('警告', '条码格式错误');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +104,7 @@ const StockInWheelManual: React.FC = () => {
|
||||||
setPackageData(prev => [...prev, newItem]);
|
setPackageData(prev => [...prev, newItem]);
|
||||||
setPackageDataId(prev => prev + 1);
|
setPackageDataId(prev => prev + 1);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert('警告', '该物料批次已存在,不能重复添加');
|
DialogUtils.showWarningMessage('警告', '该物料批次已存在,不能重复添加');
|
||||||
}
|
}
|
||||||
|
|
||||||
setGoodsCode('');
|
setGoodsCode('');
|
||||||
|
|
@ -131,71 +133,57 @@ const StockInWheelManual: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyNumber = (_id: string) => {
|
const modifyNumber = (_id: string) => {
|
||||||
Alert.alert(
|
DialogUtils.showConfirmMessage('修改数量', '请输入新的数量', {
|
||||||
'修改数量',
|
cancelLabel: '取消',
|
||||||
'请输入新的数量',
|
confirmLabel: '确定',
|
||||||
[
|
confirm: () => {
|
||||||
{text: '取消', style: 'cancel'},
|
// TODO: 需要实现一个自定义的输入对话框
|
||||||
{
|
DialogUtils.showMessage('提示', '此功能暂不可用,请稍后再试');
|
||||||
text: '确定',
|
},
|
||||||
onPress: () => {
|
});
|
||||||
// TODO: 需要实现一个自定义的输入对话框
|
|
||||||
Alert.alert('提示', '此功能暂不可用,请稍后再试');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const showDetails = (item: PackageDataItem) => {
|
const showDetails = (item: PackageDataItem) => {
|
||||||
const message = [
|
const message: MessageItem[] = [
|
||||||
{label: '序号:', msg: item.id},
|
{label: '序号', msg: item.id},
|
||||||
{label: '采购单号:', msg: item.segment1},
|
{label: '采购单号', msg: item.segment1},
|
||||||
{label: '物料号:', msg: item.itemId},
|
{label: '物料号', msg: item.itemId},
|
||||||
{label: '批次号:', msg: item.batch},
|
{label: '批次号', msg: item.batch},
|
||||||
{label: '数量:', msg: item.quantity},
|
{label: '数量', msg: item.quantity},
|
||||||
{label: '重量:', msg: item.weight},
|
{label: '重量', msg: item.weight},
|
||||||
{label: '生产日期:', msg: item.productData},
|
{label: '生产日期', msg: item.productData},
|
||||||
];
|
];
|
||||||
|
|
||||||
Alert.alert(
|
DialogUtils.showMessageList('数据详情', message, '我知道了');
|
||||||
'数据详情',
|
|
||||||
message.map(m => `${m.label} ${m.msg}`).join('\n'),
|
|
||||||
[{text: '我知道了'}],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const wheelComplete = async () => {
|
const wheelComplete = async () => {
|
||||||
if (packageData.length === 0) {
|
if (packageData.length === 0) {
|
||||||
Alert.alert('警告', '您的码盘数据为空');
|
DialogUtils.showWarningMessage('警告', '您的码盘数据为空');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vehicleCode.trim()) {
|
if (!vehicleCode.trim()) {
|
||||||
Alert.alert('警告', '请先扫描载具号');
|
DialogUtils.showWarningMessage('警告', '请先扫描载具号');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const areaIDIndex = areaID === '有卤' ? 1 : 2;
|
const areaIDIndex = 1; // 默认有卤
|
||||||
const factoryIndex = factory === '二厂' ? 2 : 3;
|
const factoryIndex = 2; // 默认二厂
|
||||||
const statusIndex = {
|
const statusIndex = 1; // 默认合格
|
||||||
合格: 1,
|
|
||||||
不合格: 2,
|
|
||||||
封存: 3,
|
|
||||||
待检: 4,
|
|
||||||
进口物料: 5,
|
|
||||||
}[status] || 1;
|
|
||||||
|
|
||||||
const confirm = await new Promise(resolve =>
|
const confirm = await new Promise<boolean>(resolve => {
|
||||||
Alert.alert(
|
DialogUtils.showConfirmMessage(
|
||||||
'码盘完成',
|
'码盘完成',
|
||||||
`载具:${vehicleCode} 码盘 ${packageData.length} 条数据,是否继续?`,
|
`载具:${vehicleCode} 码盘 ${packageData.length} 条数据,是否继续?`,
|
||||||
[
|
{
|
||||||
{text: '取消', style: 'cancel', onPress: () => resolve(false)},
|
cancelLabel: '取消',
|
||||||
{text: '继续', onPress: () => resolve(true)},
|
confirmLabel: '继续',
|
||||||
],
|
cancel: () => resolve(false),
|
||||||
),
|
confirm: () => resolve(true),
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
if (!confirm) return;
|
if (!confirm) return;
|
||||||
|
|
||||||
|
|
@ -222,24 +210,36 @@ const StockInWheelManual: React.FC = () => {
|
||||||
setPackageDataId(0);
|
setPackageDataId(0);
|
||||||
setVehicleCode('');
|
setVehicleCode('');
|
||||||
setPackageData([]);
|
setPackageData([]);
|
||||||
Alert.alert('码盘成功', '', [{text: '我知道了'}]);
|
DialogUtils.showSuccessMessage(
|
||||||
|
'码盘成功',
|
||||||
|
'码盘操作已完成',
|
||||||
|
'我知道了',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert('警告', `服务器返回失败:${data.message}`);
|
DialogUtils.showWarningMessage(
|
||||||
|
'警告',
|
||||||
|
`服务器返回失败:${data.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Alert.alert('请求发生错误', `请求服务器发生错误:${error instanceof Error ? error.message : String(error)}`, [
|
DialogUtils.showErrorMessage(
|
||||||
{text: '我知道了', style: 'cancel'},
|
'请求发生错误',
|
||||||
]);
|
`请求服务器发生错误:${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`,
|
||||||
|
'我知道了',
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, {backgroundColor: theme.colors.background}]}>
|
<View
|
||||||
|
style={[styles.container, {backgroundColor: theme.colors.background}]}>
|
||||||
{/* 头部导航栏 */}
|
{/* 头部导航栏 */}
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[theme.colors.primary, theme.colors.secondary, theme.colors.tertiary]}
|
colors={theme.colors.gradients.primary}
|
||||||
start={{x: 0, y: 0}}
|
start={{x: 0, y: 0}}
|
||||||
end={{x: 1, y: 0}}
|
end={{x: 1, y: 0}}
|
||||||
style={styles.header}>
|
style={styles.header}>
|
||||||
|
|
@ -263,23 +263,25 @@ const StockInWheelManual: React.FC = () => {
|
||||||
style={styles.waveSvg}
|
style={styles.waveSvg}
|
||||||
preserveAspectRatio="none">
|
preserveAspectRatio="none">
|
||||||
<Path
|
<Path
|
||||||
fill={`${theme.colors.tertiary}40`}
|
fill={`${theme.colors.aqua}30`}
|
||||||
d="M0,96L48,112C96,128,192,160,288,186.7C384,213,480,235,576,213.3C672,192,768,128,864,128C960,128,1056,192,1152,213.3C1248,235,1344,213,1392,202.7L1440,192L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z"
|
d="M0,96L48,112C96,128,192,160,288,186.7C384,213,480,235,576,213.3C672,192,768,128,864,128C960,128,1056,192,1152,213.3C1248,235,1344,213,1392,202.7L1440,192L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z"
|
||||||
/>
|
/>
|
||||||
</Svg>
|
</Svg>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.mainContent}>
|
<View
|
||||||
<LinearGradient
|
style={[
|
||||||
colors={theme.colors.gradients.contrast}
|
styles.mainContent,
|
||||||
style={StyleSheet.absoluteFill}
|
{backgroundColor: theme.colors.backgroundGray},
|
||||||
start={{x: 0, y: 0}}
|
]}>
|
||||||
end={{x: 1, y: 0}}
|
|
||||||
/>
|
|
||||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||||
{/* 信息提示区 */}
|
{/* 信息提示区 */}
|
||||||
<View style={[styles.infoSection, {backgroundColor: `${theme.colors.primary}15`}]}>
|
<View
|
||||||
<Icon name="info-outline" size={24} color={theme.colors.primary} />
|
style={[
|
||||||
|
styles.infoSection,
|
||||||
|
{backgroundColor: `${theme.colors.aqua}15`},
|
||||||
|
]}>
|
||||||
|
<Icon name="info-outline" size={24} color={theme.colors.aqua} />
|
||||||
<Text style={[styles.infoText, {color: theme.colors.text}]}>
|
<Text style={[styles.infoText, {color: theme.colors.text}]}>
|
||||||
请先扫描载具号,然后添加物料信息进行码盘入库
|
请先扫描载具号,然后添加物料信息进行码盘入库
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -287,12 +289,23 @@ const StockInWheelManual: React.FC = () => {
|
||||||
|
|
||||||
{/* 载具号输入区 */}
|
{/* 载具号输入区 */}
|
||||||
<View style={styles.inputContainer}>
|
<View style={styles.inputContainer}>
|
||||||
<Text style={[styles.label, {color: theme.colors.text}]}>载具号</Text>
|
<Text style={[styles.label, {color: theme.colors.text}]}>
|
||||||
<View style={[styles.inputWrapper, {
|
载具号
|
||||||
borderColor: theme.colors.border,
|
</Text>
|
||||||
backgroundColor: `${theme.colors.background}CC`,
|
<View
|
||||||
}]}>
|
style={[
|
||||||
<Icon name="qr-code-scanner" size={24} color={theme.colors.primary} style={styles.inputIcon} />
|
styles.inputWrapper,
|
||||||
|
{
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
backgroundColor: `${theme.colors.background}CC`,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Icon
|
||||||
|
name="qr-code-scanner"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.aqua}
|
||||||
|
style={styles.inputIcon}
|
||||||
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={vehicleInputRef}
|
ref={vehicleInputRef}
|
||||||
style={[styles.input, {color: theme.colors.text}]}
|
style={[styles.input, {color: theme.colors.text}]}
|
||||||
|
|
@ -305,7 +318,11 @@ const StockInWheelManual: React.FC = () => {
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => setVehicleCode('')}
|
onPress={() => setVehicleCode('')}
|
||||||
style={styles.clearButton}>
|
style={styles.clearButton}>
|
||||||
<Icon name="cancel" size={20} color={theme.colors.textLight} />
|
<Icon
|
||||||
|
name="cancel"
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.textLight}
|
||||||
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -313,12 +330,23 @@ const StockInWheelManual: React.FC = () => {
|
||||||
|
|
||||||
{/* 条码输入区 */}
|
{/* 条码输入区 */}
|
||||||
<View style={styles.inputContainer}>
|
<View style={styles.inputContainer}>
|
||||||
<Text style={[styles.label, {color: theme.colors.text}]}>物料条码</Text>
|
<Text style={[styles.label, {color: theme.colors.text}]}>
|
||||||
<View style={[styles.inputWrapper, {
|
物料条码
|
||||||
borderColor: theme.colors.border,
|
</Text>
|
||||||
backgroundColor: `${theme.colors.background}CC`,
|
<View
|
||||||
}]}>
|
style={[
|
||||||
<Icon name="qr-code-2" size={24} color={theme.colors.primary} style={styles.inputIcon} />
|
styles.inputWrapper,
|
||||||
|
{
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
backgroundColor: `${theme.colors.background}CC`,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Icon
|
||||||
|
name="qr-code-2"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.aqua}
|
||||||
|
style={styles.inputIcon}
|
||||||
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={goodsInputRef}
|
ref={goodsInputRef}
|
||||||
style={[styles.input, {color: theme.colors.text}]}
|
style={[styles.input, {color: theme.colors.text}]}
|
||||||
|
|
@ -331,155 +359,55 @@ const StockInWheelManual: React.FC = () => {
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => setGoodsCode('')}
|
onPress={() => setGoodsCode('')}
|
||||||
style={styles.clearButton}>
|
style={styles.clearButton}>
|
||||||
<Icon name="cancel" size={20} color={theme.colors.textLight} />
|
<Icon
|
||||||
|
name="cancel"
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.textLight}
|
||||||
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 选项区域 */}
|
|
||||||
<View style={styles.optionsContainer}>
|
|
||||||
{/* 区域选择 */}
|
|
||||||
<View style={styles.optionGroup}>
|
|
||||||
<Text style={[styles.optionLabel, {color: theme.colors.text}]}>区域</Text>
|
|
||||||
<View style={styles.radioGroup}>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.radioButton,
|
|
||||||
{borderColor: theme.colors.border},
|
|
||||||
areaID === '有卤' && styles.radioButtonActive,
|
|
||||||
areaID === '有卤' && {backgroundColor: theme.colors.primary},
|
|
||||||
]}
|
|
||||||
onPress={() => setAreaID('有卤')}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.radioText,
|
|
||||||
{color: theme.colors.text},
|
|
||||||
areaID === '有卤' && {color: theme.colors.background},
|
|
||||||
]}>
|
|
||||||
有卤
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.radioButton,
|
|
||||||
{borderColor: theme.colors.border},
|
|
||||||
areaID === '无卤' && styles.radioButtonActive,
|
|
||||||
areaID === '无卤' && {backgroundColor: theme.colors.primary},
|
|
||||||
]}
|
|
||||||
onPress={() => setAreaID('无卤')}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.radioText,
|
|
||||||
{color: theme.colors.text},
|
|
||||||
areaID === '无卤' && {color: theme.colors.background},
|
|
||||||
]}>
|
|
||||||
无卤
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 工厂选择 */}
|
|
||||||
<View style={styles.optionGroup}>
|
|
||||||
<Text style={[styles.optionLabel, {color: theme.colors.text}]}>工厂</Text>
|
|
||||||
<View style={styles.radioGroup}>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.radioButton,
|
|
||||||
{borderColor: theme.colors.border},
|
|
||||||
factory === '二厂' && styles.radioButtonActive,
|
|
||||||
factory === '二厂' && {backgroundColor: theme.colors.primary},
|
|
||||||
]}
|
|
||||||
onPress={() => setFactory('二厂')}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.radioText,
|
|
||||||
{color: theme.colors.text},
|
|
||||||
factory === '二厂' && {color: theme.colors.background},
|
|
||||||
]}>
|
|
||||||
二厂
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.radioButton,
|
|
||||||
{borderColor: theme.colors.border},
|
|
||||||
factory === '三厂' && styles.radioButtonActive,
|
|
||||||
factory === '三厂' && {backgroundColor: theme.colors.primary},
|
|
||||||
]}
|
|
||||||
onPress={() => setFactory('三厂')}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.radioText,
|
|
||||||
{color: theme.colors.text},
|
|
||||||
factory === '三厂' && {color: theme.colors.background},
|
|
||||||
]}>
|
|
||||||
三厂
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 状态选择 - 改为与区域、工厂相同的样式 */}
|
|
||||||
<View style={styles.optionGroup}>
|
|
||||||
<Text style={[styles.optionLabel, {color: theme.colors.text}]}>状态</Text>
|
|
||||||
<View style={styles.radioGroup}>
|
|
||||||
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
||||||
<View style={styles.radioGroup}>
|
|
||||||
{['合格', '不合格', '封存', '待检', '进口物料'].map((item) => (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={item}
|
|
||||||
style={[
|
|
||||||
styles.radioButton,
|
|
||||||
{borderColor: theme.colors.border},
|
|
||||||
status === item && styles.radioButtonActive,
|
|
||||||
status === item && {backgroundColor: theme.colors.primary},
|
|
||||||
]}
|
|
||||||
onPress={() => setStatus(item)}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.radioText,
|
|
||||||
{color: theme.colors.text},
|
|
||||||
status === item && {color: theme.colors.background},
|
|
||||||
]}>
|
|
||||||
{item}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 操作按钮区 */}
|
{/* 操作按钮区 */}
|
||||||
<View style={styles.buttonGroup}>
|
<View style={styles.buttonGroup}>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[theme.colors.primary, theme.colors.secondary]}
|
colors={theme.colors.gradients.button}
|
||||||
start={{x: 0, y: 0}}
|
start={{x: 0, y: 0}}
|
||||||
end={{x: 1, y: 0}}
|
end={{x: 1, y: 0}}
|
||||||
style={styles.button}>
|
style={styles.button}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.buttonContent}
|
style={styles.buttonContent}
|
||||||
onPress={resolveCode}>
|
onPress={resolveCode}>
|
||||||
<Icon name="add-circle-outline" size={24} color={theme.colors.background} style={styles.buttonIcon} />
|
<Icon
|
||||||
<Text style={[styles.buttonText, {color: theme.colors.background}]}>
|
name="add-circle-outline"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.background}
|
||||||
|
style={styles.buttonIcon}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={[styles.buttonText, {color: theme.colors.background}]}>
|
||||||
添加物料
|
添加物料
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
|
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[theme.colors.success, theme.colors.secondary]}
|
colors={theme.colors.gradients.button}
|
||||||
start={{x: 0, y: 0}}
|
start={{x: 0, y: 0}}
|
||||||
end={{x: 1, y: 0}}
|
end={{x: 1, y: 0}}
|
||||||
style={styles.button}>
|
style={styles.button}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.buttonContent}
|
style={styles.buttonContent}
|
||||||
onPress={wheelComplete}>
|
onPress={wheelComplete}>
|
||||||
<Icon name="check-circle-outline" size={24} color={theme.colors.background} style={styles.buttonIcon} />
|
<Icon
|
||||||
<Text style={[styles.buttonText, {color: theme.colors.background}]}>
|
name="check-circle-outline"
|
||||||
|
size={24}
|
||||||
|
color={theme.colors.background}
|
||||||
|
style={styles.buttonIcon}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={[styles.buttonText, {color: theme.colors.background}]}>
|
||||||
码盘完成
|
码盘完成
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
@ -489,42 +417,65 @@ const StockInWheelManual: React.FC = () => {
|
||||||
{/* 物料列表 */}
|
{/* 物料列表 */}
|
||||||
<View style={styles.tableContainer}>
|
<View style={styles.tableContainer}>
|
||||||
<View style={styles.tableHeader}>
|
<View style={styles.tableHeader}>
|
||||||
<Icon name="list-alt" size={24} color={theme.colors.primary} />
|
<Icon name="list-alt" size={24} color={theme.colors.aqua} />
|
||||||
<Text style={[styles.tableTitle, {color: theme.colors.text}]}>
|
<Text style={[styles.tableTitle, {color: theme.colors.text}]}>
|
||||||
已添加物料 ({packageData.length})
|
已添加物料 ({packageData.length})
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{packageData.map((item, index) => (
|
{packageData.map(item => (
|
||||||
<View
|
<View key={item.id} style={[styles.card]}>
|
||||||
key={item.id}
|
<View
|
||||||
style={[styles.card]}>
|
style={[
|
||||||
<LinearGradient
|
StyleSheet.absoluteFill,
|
||||||
colors={theme.colors.gradients.contrast}
|
{
|
||||||
style={[StyleSheet.absoluteFill, {borderRadius: 12}]}
|
borderRadius: 12,
|
||||||
start={{x: 0, y: 0}}
|
backgroundColor: theme.colors.background,
|
||||||
end={{x: 1, y: 0}}
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
{/* 卡片头部 */}
|
{/* 卡片头部 */}
|
||||||
<View style={styles.cardHeader}>
|
<View style={styles.cardHeader}>
|
||||||
<View style={styles.cardHeaderLeft}>
|
<View style={styles.cardHeaderLeft}>
|
||||||
<Text style={[styles.cardHeaderText, {color: theme.colors.background}]}>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.cardHeaderText,
|
||||||
|
{color: theme.colors.text},
|
||||||
|
]}>
|
||||||
序号:{item.id}
|
序号:{item.id}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.cardHeaderText, {color: theme.colors.background}]}>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.cardHeaderText,
|
||||||
|
{color: theme.colors.text},
|
||||||
|
]}>
|
||||||
采购单号:{item.segment1}
|
采购单号:{item.segment1}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.cardActions}>
|
<View style={styles.cardActions}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.cardActionButton, {backgroundColor: `${theme.colors.primary}15`}]}
|
style={[
|
||||||
|
styles.cardActionButton,
|
||||||
|
{backgroundColor: `${theme.colors.aqua}15`},
|
||||||
|
]}
|
||||||
onPress={() => showDetails(item)}>
|
onPress={() => showDetails(item)}>
|
||||||
<Icon name="info-outline" size={20} color={theme.colors.primary} />
|
<Icon
|
||||||
|
name="info-outline"
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.aqua}
|
||||||
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.cardActionButton, {backgroundColor: `${theme.colors.error}15`}]}
|
style={[
|
||||||
|
styles.cardActionButton,
|
||||||
|
{backgroundColor: `${theme.colors.error}15`},
|
||||||
|
]}
|
||||||
onPress={() => deleteItem(item.id)}>
|
onPress={() => deleteItem(item.id)}>
|
||||||
<Icon name="delete-outline" size={20} color={theme.colors.error} />
|
<Icon
|
||||||
|
name="delete-outline"
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.error}
|
||||||
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -533,37 +484,83 @@ const StockInWheelManual: React.FC = () => {
|
||||||
<View style={styles.cardContent}>
|
<View style={styles.cardContent}>
|
||||||
<View style={styles.cardRow}>
|
<View style={styles.cardRow}>
|
||||||
<View style={styles.cardField}>
|
<View style={styles.cardField}>
|
||||||
<Text style={[styles.cardLabel, {color: theme.colors.background}]}>物料号</Text>
|
<Text
|
||||||
<Text style={[styles.cardValue, {color: theme.colors.background}]}>{item.itemId}</Text>
|
style={[
|
||||||
|
styles.cardLabel,
|
||||||
|
{color: theme.colors.textLight},
|
||||||
|
]}>
|
||||||
|
物料号
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[styles.cardValue, {color: theme.colors.text}]}>
|
||||||
|
{item.itemId}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.cardField}>
|
<View style={styles.cardField}>
|
||||||
<Text style={[styles.cardLabel, {color: theme.colors.background}]}>批次号</Text>
|
<Text
|
||||||
<Text style={[styles.cardValue, {color: theme.colors.background}]}>{item.batch}</Text>
|
style={[
|
||||||
|
styles.cardLabel,
|
||||||
|
{color: theme.colors.textLight},
|
||||||
|
]}>
|
||||||
|
批次号
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[styles.cardValue, {color: theme.colors.text}]}>
|
||||||
|
{item.batch}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.cardRow}>
|
<View style={styles.cardRow}>
|
||||||
<View style={styles.cardField}>
|
<View style={styles.cardField}>
|
||||||
<Text style={[styles.cardLabel, {color: theme.colors.background}]}>数量</Text>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.cardLabel,
|
||||||
|
{color: theme.colors.textLight},
|
||||||
|
]}>
|
||||||
|
数量
|
||||||
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.editableValue}
|
style={styles.editableValue}
|
||||||
onPress={() => modifyNumber(item.id)}>
|
onPress={() => modifyNumber(item.id)}>
|
||||||
<Text style={[styles.cardValue, {color: theme.colors.background}]}>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.cardValue,
|
||||||
|
{color: theme.colors.text},
|
||||||
|
]}>
|
||||||
{item.quantity}
|
{item.quantity}
|
||||||
</Text>
|
</Text>
|
||||||
<Icon name="edit" size={16} color={theme.colors.background} />
|
<Icon name="edit" size={16} color={theme.colors.text} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.cardField}>
|
<View style={styles.cardField}>
|
||||||
<Text style={[styles.cardLabel, {color: theme.colors.background}]}>重量</Text>
|
<Text
|
||||||
<Text style={[styles.cardValue, {color: theme.colors.background}]}>{item.weight}</Text>
|
style={[
|
||||||
|
styles.cardLabel,
|
||||||
|
{color: theme.colors.textLight},
|
||||||
|
]}>
|
||||||
|
重量
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[styles.cardValue, {color: theme.colors.text}]}>
|
||||||
|
{item.weight}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.cardRow}>
|
<View style={styles.cardRow}>
|
||||||
<View style={styles.cardField}>
|
<View style={styles.cardField}>
|
||||||
<Text style={[styles.cardLabel, {color: theme.colors.background}]}>生产日期</Text>
|
<Text
|
||||||
<Text style={[styles.cardValue, {color: theme.colors.background}]}>{item.productData}</Text>
|
style={[
|
||||||
|
styles.cardLabel,
|
||||||
|
{color: theme.colors.textLight},
|
||||||
|
]}>
|
||||||
|
生产日期
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[styles.cardValue, {color: theme.colors.text}]}>
|
||||||
|
{item.productData}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -574,9 +571,17 @@ const StockInWheelManual: React.FC = () => {
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<View style={[styles.loadingOverlay, {backgroundColor: 'rgba(0, 0, 0, 0.7)'}]}>
|
<View
|
||||||
<View style={[styles.loadingCard, {backgroundColor: theme.colors.background}]}>
|
style={[
|
||||||
<ActivityIndicator size="large" color={theme.colors.primary} />
|
styles.loadingOverlay,
|
||||||
|
{backgroundColor: 'rgba(0, 0, 0, 0.7)'},
|
||||||
|
]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.loadingCard,
|
||||||
|
{backgroundColor: theme.colors.background},
|
||||||
|
]}>
|
||||||
|
<ActivityIndicator size="large" color={theme.colors.aqua} />
|
||||||
<Text style={[styles.loadingText, {color: theme.colors.text}]}>
|
<Text style={[styles.loadingText, {color: theme.colors.text}]}>
|
||||||
正在请求入库...
|
正在请求入库...
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -675,36 +680,7 @@ const styles = StyleSheet.create({
|
||||||
clearButton: {
|
clearButton: {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
},
|
},
|
||||||
optionsContainer: {
|
|
||||||
marginBottom: 20,
|
|
||||||
},
|
|
||||||
optionGroup: {
|
|
||||||
marginBottom: 16,
|
|
||||||
},
|
|
||||||
optionLabel: {
|
|
||||||
fontSize: 16,
|
|
||||||
marginBottom: 8,
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
radioGroup: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 12,
|
|
||||||
},
|
|
||||||
radioButton: {
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 8,
|
|
||||||
paddingVertical: 8,
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
minWidth: 80,
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
radioButtonActive: {
|
|
||||||
borderColor: 'transparent',
|
|
||||||
},
|
|
||||||
radioText: {
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
buttonGroup: {
|
buttonGroup: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
gap: 12,
|
gap: 12,
|
||||||
|
|
@ -821,4 +797,4 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default StockInWheelManual;
|
export default StockInManual;
|
||||||
893
src/utils/DialogUtils.tsx
Normal file
893
src/utils/DialogUtils.tsx
Normal file
|
|
@ -0,0 +1,893 @@
|
||||||
|
import React, {useState, useRef} from 'react';
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
StyleSheet,
|
||||||
|
Dimensions,
|
||||||
|
ScrollView,
|
||||||
|
Animated,
|
||||||
|
StatusBar,
|
||||||
|
} from 'react-native';
|
||||||
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
|
import {theme} from '../constants/theme';
|
||||||
|
|
||||||
|
const {width, height} = Dimensions.get('window');
|
||||||
|
|
||||||
|
export interface MessageItem {
|
||||||
|
label: string;
|
||||||
|
msg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DialogConfig {
|
||||||
|
type: 'success' | 'warning' | 'error' | 'info' | 'confirm';
|
||||||
|
title: string;
|
||||||
|
message?: string;
|
||||||
|
messageList?: MessageItem[];
|
||||||
|
buttons?: DialogButton[];
|
||||||
|
onConfirm?: () => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
confirmText?: string;
|
||||||
|
cancelText?: string;
|
||||||
|
showIcon?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogButton {
|
||||||
|
text: string;
|
||||||
|
onPress?: () => void;
|
||||||
|
style?: 'default' | 'cancel' | 'destructive';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 美观的弹窗组件
|
||||||
|
interface BeautifulDialogProps {
|
||||||
|
ref?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BeautifulDialog: React.FC<BeautifulDialogProps> = React.forwardRef(
|
||||||
|
(props, ref) => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [config, setConfig] = useState<DialogConfig | null>(null);
|
||||||
|
const scaleAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
const opacityAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({
|
||||||
|
show: (dialogConfig: DialogConfig) => {
|
||||||
|
setConfig(dialogConfig);
|
||||||
|
setVisible(true);
|
||||||
|
// 动画效果
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.spring(scaleAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
tension: 100,
|
||||||
|
friction: 8,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(opacityAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 200,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start();
|
||||||
|
},
|
||||||
|
hide: () => {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.spring(scaleAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
tension: 100,
|
||||||
|
friction: 8,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(opacityAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 150,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start(() => {
|
||||||
|
setVisible(false);
|
||||||
|
setConfig(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
const dialogRef = (ref as any)?.current || ref;
|
||||||
|
if (dialogRef?.hide) {
|
||||||
|
dialogRef.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
config?.onConfirm?.();
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
config?.onCancel?.();
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIconConfig = () => {
|
||||||
|
switch (config?.type) {
|
||||||
|
case 'success':
|
||||||
|
return {
|
||||||
|
name: 'check-circle',
|
||||||
|
color: theme.colors.cyan,
|
||||||
|
bgColor: '#e6f7ff',
|
||||||
|
};
|
||||||
|
case 'warning':
|
||||||
|
return {
|
||||||
|
name: 'warning',
|
||||||
|
color: theme.colors.cyan,
|
||||||
|
bgColor: '#e6f7ff',
|
||||||
|
};
|
||||||
|
case 'error':
|
||||||
|
return {name: 'error', color: theme.colors.cyan, bgColor: '#e6f7ff'};
|
||||||
|
case 'confirm':
|
||||||
|
return {name: 'help', color: theme.colors.cyan, bgColor: '#e6f7ff'};
|
||||||
|
default:
|
||||||
|
return {name: 'info', color: theme.colors.cyan, bgColor: '#e6f7ff'};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGradientColors = () => {
|
||||||
|
switch (config?.type) {
|
||||||
|
case 'success':
|
||||||
|
return [theme.colors.cyan, theme.colors.aqua];
|
||||||
|
case 'warning':
|
||||||
|
return [theme.colors.cyan, theme.colors.aqua];
|
||||||
|
case 'error':
|
||||||
|
return [theme.colors.cyan, theme.colors.aqua];
|
||||||
|
case 'confirm':
|
||||||
|
return [theme.colors.cyan, theme.colors.aqua];
|
||||||
|
default:
|
||||||
|
return [theme.colors.cyan, theme.colors.aqua];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!visible || !config) return null;
|
||||||
|
|
||||||
|
const iconConfig = getIconConfig();
|
||||||
|
const gradientColors = getGradientColors();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
transparent
|
||||||
|
animationType="none"
|
||||||
|
statusBarTranslucent
|
||||||
|
onRequestClose={handleClose}>
|
||||||
|
<StatusBar
|
||||||
|
backgroundColor="rgba(0, 0, 0, 0.5)"
|
||||||
|
barStyle="light-content"
|
||||||
|
/>
|
||||||
|
<Animated.View style={[styles.overlay, {opacity: opacityAnim}]}>
|
||||||
|
<Animated.View
|
||||||
|
style={[styles.dialogContainer, {transform: [{scale: scaleAnim}]}]}>
|
||||||
|
{/* 头部图标区域 */}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.iconContainer,
|
||||||
|
{backgroundColor: iconConfig.bgColor},
|
||||||
|
]}>
|
||||||
|
<View style={styles.iconWrapper}>
|
||||||
|
<Icon
|
||||||
|
name={iconConfig.name}
|
||||||
|
size={32}
|
||||||
|
color={iconConfig.color}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 内容区域 */}
|
||||||
|
<View style={styles.contentContainer}>
|
||||||
|
<Text style={styles.title}>{config.title}</Text>
|
||||||
|
|
||||||
|
{config.message && (
|
||||||
|
<Text style={styles.message}>{config.message}</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{config.messageList && (
|
||||||
|
<View style={styles.messageListContainer}>
|
||||||
|
{config.messageList.map((item, index) => (
|
||||||
|
<View key={index} style={styles.messageItem}>
|
||||||
|
<Text style={styles.messageLabel}>{item.label}:</Text>
|
||||||
|
<Text style={styles.messageValue}>{item.msg}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 按钮区域 */}
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
{config.type === 'confirm' ? (
|
||||||
|
<>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, styles.cancelButton]}
|
||||||
|
onPress={handleCancel}>
|
||||||
|
<Text style={styles.cancelButtonText}>
|
||||||
|
{config.cancelText || '取消'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<LinearGradient
|
||||||
|
colors={gradientColors}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 0}}
|
||||||
|
style={[styles.button, styles.confirmButton]}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonTouchable}
|
||||||
|
onPress={handleConfirm}>
|
||||||
|
<Text style={styles.confirmButtonText}>
|
||||||
|
{config.confirmText || '确定'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</LinearGradient>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<LinearGradient
|
||||||
|
colors={gradientColors}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 0}}
|
||||||
|
style={[styles.button, styles.singleButton]}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonTouchable}
|
||||||
|
onPress={handleConfirm}>
|
||||||
|
<Text style={styles.confirmButtonText}>
|
||||||
|
{config.confirmText || '确定'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</LinearGradient>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</Animated.View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 输入框弹窗组件
|
||||||
|
interface InputDialogProps {
|
||||||
|
visible: boolean;
|
||||||
|
title: string;
|
||||||
|
message?: string;
|
||||||
|
hintText?: string;
|
||||||
|
cancelLabel?: string;
|
||||||
|
confirmLabel?: string;
|
||||||
|
onCancel: () => void;
|
||||||
|
onConfirm: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InputDialog: React.FC<InputDialogProps> = ({
|
||||||
|
visible,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
hintText,
|
||||||
|
cancelLabel = '取消',
|
||||||
|
confirmLabel = '确定',
|
||||||
|
onCancel,
|
||||||
|
onConfirm,
|
||||||
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
const scaleAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
const opacityAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.spring(scaleAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
tension: 100,
|
||||||
|
friction: 8,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(opacityAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 200,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start();
|
||||||
|
} else {
|
||||||
|
scaleAnim.setValue(0);
|
||||||
|
opacityAnim.setValue(0);
|
||||||
|
}
|
||||||
|
}, [visible, opacityAnim, scaleAnim]);
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
onConfirm(inputValue);
|
||||||
|
setInputValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
onCancel();
|
||||||
|
setInputValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
transparent
|
||||||
|
animationType="none"
|
||||||
|
statusBarTranslucent
|
||||||
|
onRequestClose={handleCancel}>
|
||||||
|
<StatusBar
|
||||||
|
backgroundColor="rgba(0, 0, 0, 0.5)"
|
||||||
|
barStyle="light-content"
|
||||||
|
/>
|
||||||
|
<Animated.View style={[styles.overlay, {opacity: opacityAnim}]}>
|
||||||
|
<Animated.View
|
||||||
|
style={[styles.dialogContainer, {transform: [{scale: scaleAnim}]}]}>
|
||||||
|
{/* 头部图标 */}
|
||||||
|
<View style={[styles.iconContainer, {backgroundColor: '#e6f7ff'}]}>
|
||||||
|
<View style={styles.iconWrapper}>
|
||||||
|
<Icon name="edit" size={32} color={theme.colors.cyan} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 内容区域 */}
|
||||||
|
<View style={styles.contentContainer}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
{message && <Text style={styles.message}>{message}</Text>}
|
||||||
|
|
||||||
|
<View style={styles.inputContainer}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.textInput}
|
||||||
|
placeholder={hintText}
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
value={inputValue}
|
||||||
|
onChangeText={setInputValue}
|
||||||
|
maxLength={1000}
|
||||||
|
autoFocus
|
||||||
|
multiline={false}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 按钮区域 */}
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, styles.cancelButton]}
|
||||||
|
onPress={handleCancel}>
|
||||||
|
<Text style={styles.cancelButtonText}>{cancelLabel}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[theme.colors.cyan, theme.colors.aqua]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 0}}
|
||||||
|
style={[styles.button, styles.confirmButton]}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonTouchable}
|
||||||
|
onPress={handleConfirm}>
|
||||||
|
<Text style={styles.confirmButtonText}>{confirmLabel}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</LinearGradient>
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</Animated.View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 单选框弹窗组件
|
||||||
|
interface SingleSelectDialogProps {
|
||||||
|
visible: boolean;
|
||||||
|
title: string;
|
||||||
|
message?: string;
|
||||||
|
options: string[];
|
||||||
|
selectedIndex?: number;
|
||||||
|
submitText?: string;
|
||||||
|
onSubmit: (index: number, value: string) => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SingleSelectDialog: React.FC<SingleSelectDialogProps> = ({
|
||||||
|
visible,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
options,
|
||||||
|
selectedIndex = 0,
|
||||||
|
submitText = '确定',
|
||||||
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
|
}) => {
|
||||||
|
const [selected, setSelected] = useState(selectedIndex);
|
||||||
|
const scaleAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
const opacityAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.spring(scaleAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
tension: 100,
|
||||||
|
friction: 8,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(opacityAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 200,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
]).start();
|
||||||
|
} else {
|
||||||
|
scaleAnim.setValue(0);
|
||||||
|
opacityAnim.setValue(0);
|
||||||
|
setSelected(selectedIndex);
|
||||||
|
}
|
||||||
|
}, [visible, selectedIndex, opacityAnim, scaleAnim]);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
onSubmit(selected, options[selected]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
transparent
|
||||||
|
animationType="none"
|
||||||
|
statusBarTranslucent
|
||||||
|
onRequestClose={onCancel}>
|
||||||
|
<StatusBar
|
||||||
|
backgroundColor="rgba(0, 0, 0, 0.5)"
|
||||||
|
barStyle="light-content"
|
||||||
|
/>
|
||||||
|
<Animated.View style={[styles.overlay, {opacity: opacityAnim}]}>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.dialogContainer,
|
||||||
|
styles.selectDialogContainer,
|
||||||
|
{transform: [{scale: scaleAnim}]},
|
||||||
|
]}>
|
||||||
|
{/* 头部 */}
|
||||||
|
<View style={styles.selectHeader}>
|
||||||
|
<View style={[styles.iconContainer, {backgroundColor: '#e6f7ff'}]}>
|
||||||
|
<View style={styles.iconWrapper}>
|
||||||
|
<Icon name="list" size={28} color={theme.colors.cyan} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.headerContent}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<TouchableOpacity onPress={onCancel} style={styles.closeButton}>
|
||||||
|
<Icon name="close" size={24} color="#666" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{message && (
|
||||||
|
<View style={styles.messageContainer}>
|
||||||
|
<Text style={styles.message}>{message}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 选项列表 */}
|
||||||
|
<ScrollView
|
||||||
|
style={styles.optionsContainer}
|
||||||
|
showsVerticalScrollIndicator={false}>
|
||||||
|
{options.map((option, index) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={index}
|
||||||
|
style={[
|
||||||
|
styles.optionItem,
|
||||||
|
selected === index && styles.selectedOptionItem,
|
||||||
|
]}
|
||||||
|
onPress={() => setSelected(index)}>
|
||||||
|
<View style={styles.optionContent}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.radioButton,
|
||||||
|
selected === index && styles.radioButtonSelected,
|
||||||
|
]}>
|
||||||
|
{selected === index && (
|
||||||
|
<View style={styles.radioButtonInner} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.optionText,
|
||||||
|
selected === index && styles.selectedOptionText,
|
||||||
|
]}>
|
||||||
|
{option}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
{/* 提交按钮 */}
|
||||||
|
<View style={styles.selectButtonContainer}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[theme.colors.cyan, theme.colors.aqua]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 0}}
|
||||||
|
style={styles.submitButton}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonTouchable}
|
||||||
|
onPress={handleSubmit}>
|
||||||
|
<Text style={styles.confirmButtonText}>{submitText}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</LinearGradient>
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</Animated.View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全新的 DialogUtils 类
|
||||||
|
export class DialogUtils {
|
||||||
|
private static dialogRef: any = null;
|
||||||
|
|
||||||
|
static setDialogRef(ref: any) {
|
||||||
|
DialogUtils.dialogRef = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出一个信息展示框
|
||||||
|
*/
|
||||||
|
static showMessage(
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
btnLabel: string = '确定',
|
||||||
|
onPress?: () => void,
|
||||||
|
): void {
|
||||||
|
if (DialogUtils.dialogRef) {
|
||||||
|
DialogUtils.dialogRef.show({
|
||||||
|
type: 'info',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
confirmText: btnLabel,
|
||||||
|
onConfirm: onPress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 展示一个信息列表,message 必须是 label 和 msg
|
||||||
|
*/
|
||||||
|
static showMessageList(
|
||||||
|
title: string,
|
||||||
|
message: MessageItem[],
|
||||||
|
btnLabel: string = '确定',
|
||||||
|
onPress?: () => void,
|
||||||
|
): void {
|
||||||
|
if (DialogUtils.dialogRef) {
|
||||||
|
DialogUtils.dialogRef.show({
|
||||||
|
type: 'info',
|
||||||
|
title,
|
||||||
|
messageList: message,
|
||||||
|
confirmText: btnLabel,
|
||||||
|
onConfirm: onPress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出一个成功的提示框
|
||||||
|
*/
|
||||||
|
static showSuccessMessage(
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
btnLabel: string = '确定',
|
||||||
|
onPress?: () => void,
|
||||||
|
): void {
|
||||||
|
if (DialogUtils.dialogRef) {
|
||||||
|
DialogUtils.dialogRef.show({
|
||||||
|
type: 'success',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
confirmText: btnLabel,
|
||||||
|
onConfirm: onPress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出一个警告提示框
|
||||||
|
*/
|
||||||
|
static showWarningMessage(
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
btnLabel: string = '确定',
|
||||||
|
onPress?: () => void,
|
||||||
|
): void {
|
||||||
|
if (DialogUtils.dialogRef) {
|
||||||
|
DialogUtils.dialogRef.show({
|
||||||
|
type: 'warning',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
confirmText: btnLabel,
|
||||||
|
onConfirm: onPress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出一个错误提示框
|
||||||
|
*/
|
||||||
|
static showErrorMessage(
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
btnLabel: string = '确定',
|
||||||
|
onPress?: () => void,
|
||||||
|
): void {
|
||||||
|
if (DialogUtils.dialogRef) {
|
||||||
|
DialogUtils.dialogRef.show({
|
||||||
|
type: 'error',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
confirmText: btnLabel,
|
||||||
|
onConfirm: onPress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出一个确认框
|
||||||
|
*/
|
||||||
|
static showConfirmMessage(
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
options: {
|
||||||
|
cancel?: () => void;
|
||||||
|
confirm?: () => void;
|
||||||
|
cancelLabel?: string;
|
||||||
|
confirmLabel?: string;
|
||||||
|
} = {},
|
||||||
|
): void {
|
||||||
|
const {
|
||||||
|
cancel,
|
||||||
|
confirm,
|
||||||
|
cancelLabel = '取消',
|
||||||
|
confirmLabel = '确定',
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
if (DialogUtils.dialogRef) {
|
||||||
|
DialogUtils.dialogRef.show({
|
||||||
|
type: 'confirm',
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
cancelText: cancelLabel,
|
||||||
|
confirmText: confirmLabel,
|
||||||
|
onCancel: cancel,
|
||||||
|
onConfirm: confirm,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
},
|
||||||
|
dialogContainer: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 16,
|
||||||
|
minWidth: width * 0.8,
|
||||||
|
maxWidth: width * 0.9,
|
||||||
|
maxHeight: height * 0.8,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: {width: 0, height: 8},
|
||||||
|
shadowOpacity: 0.25,
|
||||||
|
shadowRadius: 16,
|
||||||
|
elevation: 8,
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: 24,
|
||||||
|
paddingBottom: 16,
|
||||||
|
borderTopLeftRadius: 16,
|
||||||
|
borderTopRightRadius: 16,
|
||||||
|
},
|
||||||
|
iconWrapper: {
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
borderRadius: 30,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: {width: 0, height: 2},
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 4,
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
contentContainer: {
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingBottom: 24,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#666',
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: 24,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
messageListContainer: {
|
||||||
|
backgroundColor: '#f8f9fa',
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: 16,
|
||||||
|
marginTop: 12,
|
||||||
|
},
|
||||||
|
messageItem: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
messageLabel: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#666',
|
||||||
|
fontWeight: '500',
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
|
messageValue: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#333',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingBottom: 24,
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
flex: 1,
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 12,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
buttonTouchable: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
cancelButton: {
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#d9d9d9',
|
||||||
|
},
|
||||||
|
confirmButton: {
|
||||||
|
shadowColor: theme.colors.cyan,
|
||||||
|
shadowOffset: {width: 0, height: 4},
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
singleButton: {
|
||||||
|
shadowColor: theme.colors.cyan,
|
||||||
|
shadowOffset: {width: 0, height: 4},
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
cancelButtonText: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
confirmButtonText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
inputContainer: {
|
||||||
|
marginTop: 16,
|
||||||
|
},
|
||||||
|
textInput: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#d9d9d9',
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: 12,
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#333',
|
||||||
|
backgroundColor: '#fafafa',
|
||||||
|
},
|
||||||
|
// 单选框相关样式
|
||||||
|
selectDialogContainer: {
|
||||||
|
maxHeight: height * 0.7,
|
||||||
|
},
|
||||||
|
selectHeader: {
|
||||||
|
paddingBottom: 8,
|
||||||
|
},
|
||||||
|
headerContent: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingTop: 8,
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
borderRadius: 16,
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
messageContainer: {
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingBottom: 12,
|
||||||
|
},
|
||||||
|
optionsContainer: {
|
||||||
|
maxHeight: height * 0.4,
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
},
|
||||||
|
optionItem: {
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#f0f0f0',
|
||||||
|
},
|
||||||
|
selectedOptionItem: {
|
||||||
|
backgroundColor: '#e6f7ff',
|
||||||
|
borderRadius: 8,
|
||||||
|
borderBottomColor: 'transparent',
|
||||||
|
marginVertical: 2,
|
||||||
|
},
|
||||||
|
optionContent: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
},
|
||||||
|
radioButton: {
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
borderRadius: 10,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: '#d9d9d9',
|
||||||
|
marginRight: 16,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
radioButtonSelected: {
|
||||||
|
borderColor: theme.colors.cyan,
|
||||||
|
},
|
||||||
|
radioButtonInner: {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
backgroundColor: theme.colors.cyan,
|
||||||
|
},
|
||||||
|
optionText: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#333',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
selectedOptionText: {
|
||||||
|
color: theme.colors.cyan,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
selectButtonContainer: {
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingTop: 16,
|
||||||
|
paddingBottom: 24,
|
||||||
|
},
|
||||||
|
submitButton: {
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 12,
|
||||||
|
shadowColor: theme.colors.cyan,
|
||||||
|
shadowOffset: {width: 0, height: 4},
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
6
src/utils/index.ts
Normal file
6
src/utils/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
// 弹窗工具导出
|
||||||
|
export {DialogUtils, InputDialog, SingleSelectDialog} from './DialogUtils';
|
||||||
|
export type {MessageItem} from './DialogUtils';
|
||||||
|
|
||||||
|
// 存储工具导出
|
||||||
|
export * from './storage';
|
||||||
Loading…
Reference in New Issue
Block a user