Initial commit
This commit is contained in:
130
App.jsx
Normal file
130
App.jsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { lazy, useEffect, Suspense, useState } from 'react';
|
||||
import {
|
||||
NavigationContainer,
|
||||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
|
||||
// import {I18nextProvider} from 'react-i18next';
|
||||
import './locales/i18n.js';
|
||||
|
||||
import {
|
||||
Grid,
|
||||
Icon,
|
||||
Provider,
|
||||
SearchBar,
|
||||
Text,
|
||||
Toast,
|
||||
} from '@ant-design/react-native';
|
||||
import { AuthProvider, useContextHook } from './components/AuthContext/index';
|
||||
|
||||
|
||||
import Login from '@/views/Login/index';
|
||||
import Guide from '@/views/Guide/index';
|
||||
import HomeIndex from '@/views/Home/Index/index';
|
||||
import HomeInventory from '@/views/Home/Inventory/index';
|
||||
import HomeProfile from '@/views/Home/Profile/index';
|
||||
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
function HomeTabs() {
|
||||
const { themeColor } = useContextHook();
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
tabBarActiveTintColor: themeColor.primaryColor,
|
||||
tabBarInactiveTintColor: themeColor.colorTextBase,
|
||||
headerShown: false,
|
||||
}}>
|
||||
<Tab.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: 'Home',
|
||||
tabBarIcon: ({ color, focused }) => (
|
||||
<Icon
|
||||
name="home"
|
||||
color={color}
|
||||
size={28}
|
||||
style={[{ marginBottom: -3 }]}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
component={HomeIndex}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="inventory"
|
||||
options={{
|
||||
title: 'Inventory',
|
||||
tabBarIcon: ({ color, focused }) => (
|
||||
<Icon
|
||||
name="appstore"
|
||||
color={color}
|
||||
size={28}
|
||||
style={[{ marginBottom: -3 }]}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
component={HomeInventory}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="profile"
|
||||
options={{
|
||||
title: 'Profile',
|
||||
tabBarIcon: ({ color, focused }) => (
|
||||
<Icon
|
||||
name="setting"
|
||||
color={color}
|
||||
size={28}
|
||||
style={[{ marginBottom: -3 }]}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
component={HomeProfile}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function RootStack() {
|
||||
return (
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen
|
||||
name="Guide"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
component={Guide}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Login"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
component={Login}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Home"
|
||||
component={HomeTabs}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
export default function App() {
|
||||
return (
|
||||
<GestureHandlerRootView>
|
||||
<AuthProvider>
|
||||
<NavigationContainer>
|
||||
<RootStack />
|
||||
</NavigationContainer>
|
||||
</AuthProvider>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
}
|
||||
45
App.tsx
45
App.tsx
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import { NewAppScreen } from '@react-native/new-app-screen';
|
||||
import { StatusBar, StyleSheet, useColorScheme, View } from 'react-native';
|
||||
import {
|
||||
SafeAreaProvider,
|
||||
useSafeAreaInsets,
|
||||
} from 'react-native-safe-area-context';
|
||||
|
||||
function App() {
|
||||
const isDarkMode = useColorScheme() === 'dark';
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||
<AppContent />
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function AppContent() {
|
||||
const safeAreaInsets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<NewAppScreen
|
||||
templateFileName="App.tsx"
|
||||
safeAreaInsets={safeAreaInsets}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default App;
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactTestRenderer from 'react-test-renderer';
|
||||
import App from '../App';
|
||||
import App from '@/App';
|
||||
|
||||
test('renders correctly', async () => {
|
||||
await ReactTestRenderer.act(() => {
|
||||
|
||||
BIN
android/app/src/main/assets/fonts/antfill.ttf
Normal file
BIN
android/app/src/main/assets/fonts/antfill.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/antoutline.ttf
Normal file
BIN
android/app/src/main/assets/fonts/antoutline.ttf
Normal file
Binary file not shown.
13
android/link-assets-manifest.json
Normal file
13
android/link-assets-manifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"migIndex": 1,
|
||||
"data": [
|
||||
{
|
||||
"path": "node_modules/@ant-design/icons-react-native/fonts/antfill.ttf",
|
||||
"sha1": "56960e7721fc92b62e0f7c4d131ffe34ed042c49"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/@ant-design/icons-react-native/fonts/antoutline.ttf",
|
||||
"sha1": "66720607b7496a48f145425386b2082b73662fd1"
|
||||
}
|
||||
]
|
||||
}
|
||||
66
api/request.js
Normal file
66
api/request.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// api.js
|
||||
import axios from 'axios';
|
||||
import AsyncStorage from '@/storage/index';
|
||||
import {filterEmptyValue} from '@/utils/common';
|
||||
import Env from '@/config/env';
|
||||
|
||||
// const BASE_URL = 'http://129.226.148.140:8000'
|
||||
// 测试环境
|
||||
// const BASE_URL = 'https://me.dreamwork.site/eladmin/';
|
||||
// 正式环境
|
||||
// const BASE_URL = 'https://mesystem.online/eladmin/'
|
||||
// 创建 Axios 实例
|
||||
const api = axios.create({
|
||||
baseURL: `${Env.URL}${Env.BASE}`, // 设置你的基地址
|
||||
timeout: 10000, // 设置请求超时时间
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
api.interceptors.request.use(
|
||||
async config => {
|
||||
// config.url = config.url.replace('mesapi', '');
|
||||
// console.log('🚀 ~ config:', config);
|
||||
try {
|
||||
// 假设获取 token 是一个异步操作
|
||||
const token = await AsyncStorage.getItem('appToken');
|
||||
|
||||
// 将 token 添加到请求头
|
||||
// config.headers.Authorization = `Bearer ${token}`;
|
||||
|
||||
if (token && token.appToken) {
|
||||
config.headers.Authorization = token.appToken;
|
||||
config.headers.token = token.appToken; // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取 token 失败:', error);
|
||||
// 处理错误,比如可以抛出错误或者记录日志
|
||||
// 也可以重试以获取 token,视需求而定
|
||||
}
|
||||
// console.log('🚀 ~ config:', config);
|
||||
|
||||
config.params = filterEmptyValue(config.params || {});
|
||||
if (config.headers['Content-Type'] != 'multipart/form-data') {
|
||||
config.data = filterEmptyValue(config.data || {});
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
error => Promise.reject(error),
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
api.interceptors.response.use(
|
||||
response => {
|
||||
return response.data;
|
||||
},
|
||||
error => {
|
||||
// 可以在这里统一处理错误(如错误提示、日志记录等)
|
||||
console.error('API response error:',error , error && error.response);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
export default api;
|
||||
38
api/workflowOrder.js
Normal file
38
api/workflowOrder.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import request from './request'
|
||||
|
||||
export function getWorkflowOrderTableList(params={}) {
|
||||
return request({
|
||||
url: "mesapi/workflow/work_orders",
|
||||
method: "get",
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
export function addWorkflowOrder(data = {}) {
|
||||
return request({
|
||||
url: "mesapi/workflow/work_orders",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function editWorkflowOrder(data = {}) {
|
||||
return request({
|
||||
url: `mesapi/workflow/work_orders/${data.work_order_id}`,
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
}
|
||||
export function delWorkflowOrder(ids = []) {
|
||||
return request({
|
||||
url: `mesapi/workflow/work_orders/${ids[0]}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
export function workflowOrderProcess(data = {}) {
|
||||
return request({
|
||||
url: `mesapi/workflow/work_orders/process/${data.work_order_id}?action=${data.action}`,
|
||||
method: "post",
|
||||
data
|
||||
});
|
||||
}
|
||||
BIN
assets/fonts/SpaceMono-Regular.ttf
Normal file
BIN
assets/fonts/SpaceMono-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/inter-medium.ttf
Normal file
BIN
assets/fonts/inter-medium.ttf
Normal file
Binary file not shown.
BIN
assets/images/logo.png
Normal file
BIN
assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
200
assets/styles/css.js
Normal file
200
assets/styles/css.js
Normal file
@@ -0,0 +1,200 @@
|
||||
import { StyleSheet, StatusBar } from 'react-native';
|
||||
|
||||
// create useStyles hooks
|
||||
export const useStyles = (props = {}) =>
|
||||
StyleSheet.create({ value: props }).value;
|
||||
|
||||
export const paddingTopStatusBarHeight = useStyles({
|
||||
paddingTop: StatusBar.currentHeight || 0,
|
||||
});
|
||||
export const statusBarHeight = useStyles({
|
||||
height: StatusBar.currentHeight || 0,
|
||||
});
|
||||
export const flexRow = useStyles({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
});
|
||||
export const flexColumn = useStyles({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
export const flexSub = useStyles({
|
||||
flex: 1,
|
||||
});
|
||||
export const flexShrink = useStyles({
|
||||
flexShrink: 0,
|
||||
});
|
||||
export const flexWrap = useStyles({
|
||||
flexWrap: 'wrap',
|
||||
});
|
||||
export const flexnoWrap = useStyles({
|
||||
flexWrap: 'nowrap',
|
||||
});
|
||||
export const justifyStart = useStyles({
|
||||
justifyContent: 'flex-start',
|
||||
});
|
||||
export const justifyEnd = useStyles({
|
||||
justifyContent: 'flex-end',
|
||||
});
|
||||
export const justifyCenter = useStyles({
|
||||
justifyContent: 'center',
|
||||
});
|
||||
export const justifyBetween = useStyles({
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
export const justifyAround = useStyles({
|
||||
justifyContent: 'space-around',
|
||||
});
|
||||
export const justifyEvenly = useStyles({
|
||||
justifyContent: 'space-evenly',
|
||||
});
|
||||
|
||||
export const alignItemsStart = useStyles({
|
||||
alignItems: 'flex-start',
|
||||
});
|
||||
export const alignItemsEnd = useStyles({
|
||||
alignItems: 'flex-end',
|
||||
});
|
||||
|
||||
export const alignItemsCenter = useStyles({
|
||||
alignItems: 'center',
|
||||
});
|
||||
export const alignItemsStretch = useStyles({
|
||||
alignItems: 'stretch',
|
||||
});
|
||||
|
||||
export const positionRelative = useStyles({
|
||||
position: 'relative',
|
||||
});
|
||||
export const positionAbsolute = useStyles({
|
||||
position: 'absolute',
|
||||
});
|
||||
|
||||
export const positionSticky = useStyles({
|
||||
position: 'sticky',
|
||||
});
|
||||
|
||||
export const positionStatic = useStyles({
|
||||
position: 'static',
|
||||
});
|
||||
|
||||
export const positionFixed = useStyles({
|
||||
position: 'fixed',
|
||||
});
|
||||
|
||||
export const getColor = (color = {brandPrimary: '#ff6600', brandPrimaryTap: '#ff6600' }) => {
|
||||
const brandPrimary = color.brandPrimary || '#108ee9';
|
||||
const brandPrimaryTap = color.brandPrimaryTap || '#1284d6';
|
||||
return {
|
||||
transparent: 'transparent',
|
||||
brandPrimary,
|
||||
brandPrimaryTap,
|
||||
|
||||
// 文字色
|
||||
color_text_base: color.color_text_base || '#000000', // 基本
|
||||
color_text_base_inverse: color.color_text_base_inverse || '#ffffff', // 基本 _ 反色
|
||||
color_text_placeholder: color.color_text_placeholder || '#bbbbbb', // 文本框提示
|
||||
color_text_disabled: color.color_text_disabled || '#bbbbbb', // 失效
|
||||
color_text_caption: color.color_text_caption || '#888888', // 辅助描述
|
||||
color_text_paragraph: color.color_text_paragraph || '#333333', // 段落
|
||||
color_link: color.color_link || brandPrimary, // 链接
|
||||
color_icon_base: color.color_icon_base || '#cccccc', // 许多小图标的背景,比如一些小圆点,加减号
|
||||
|
||||
// 背景色
|
||||
fill_body: color.fill_body || '#f5f5f9', // 页面背景
|
||||
fill_base: color.fill_base || '#ffffff', // 组件默认背景
|
||||
fill_tap: color.fill_tap || '#dddddd', // 组件默认背景 _ 按下
|
||||
fill_disabled: color.fill_disabled || '#dddddd', // 通用失效背景
|
||||
fill_mask: color.fill_mask || 'rgba(0, 0, 0, .4)', // 遮罩背景
|
||||
fill_grey: color.fill_grey || '#f7f7f7',
|
||||
|
||||
// 全局/品牌色
|
||||
brand_primary: color.brand_primary || brandPrimary,
|
||||
brand_primary_tap: color.brand_primary_tap || brandPrimaryTap,
|
||||
brand_success: color.brand_success || '#6abf47',
|
||||
brand_warning: color.brand_warning || '#faad14',
|
||||
brand_error: color.brand_error || '#f4333c', // 错误(form validate)
|
||||
brand_important: color.brand_important || '#ff5b05', // 用于小红点
|
||||
|
||||
// 边框色
|
||||
border_color_base: color.border_color_base || '#dddddd', // 基础的
|
||||
border_color_thin: color.border_color_thin || '#eeeeee', // 更细的
|
||||
|
||||
primary_button_fill: color.primary_button_fill || brandPrimary,
|
||||
primary_button_fill_tap: color.primary_button_fill_tap || brandPrimaryTap,
|
||||
|
||||
ghost_button_color: color.ghost_button_color || brandPrimary, // 同时应用于背景、文字颜色、边框色
|
||||
ghost_button_fill_tap: color.ghost_button_fill_tap || `${brandPrimary}99`, // alpha 60%
|
||||
|
||||
warning_button_fill: color.warning_button_fill || '#e94f4f',
|
||||
warning_button_fill_tap: color.warning_button_fill_tap || '#d24747',
|
||||
|
||||
// tab_bar
|
||||
tab_bar_fill: color.tab_bar_fill || '#ebeeef',
|
||||
|
||||
// toast
|
||||
toast_fill: color.toast_fill || 'rgba(0, 0, 0, .8)',
|
||||
|
||||
// search_bar
|
||||
search_bar_fill: color.search_bar_fill || '#efeff4',
|
||||
search_color_icon: color.search_color_icon || '#bbbbbb', // input search icon 的背景色
|
||||
|
||||
// notice_bar
|
||||
notice_bar_fill: color.notice_bar_fill || '#fffada',
|
||||
|
||||
// checkbox
|
||||
checkbox_fill_disabled: color.checkbox_fill_disabled || '#f5f5f5',
|
||||
checkbox_border: color.checkbox_border || '#d9d9d9',
|
||||
checkbox_border_disabled: color.checkbox_border_disabled || '#b9b9b9',
|
||||
|
||||
// switch
|
||||
switch_unchecked: color.switch_unchecked || '#cccccc',
|
||||
switch_unchecked_disabled: color.switch_unchecked_disabled || '#cccccc66', // switch_fill的40%透明度
|
||||
|
||||
// tooltip
|
||||
tooltip_dark: color.tooltip_dark || 'rgba(0, 0, 0, 0.9)',
|
||||
};
|
||||
};
|
||||
|
||||
export const getColorStyles = color => {
|
||||
return {
|
||||
default: useStyles({
|
||||
color: color.default,
|
||||
}),
|
||||
defaultBg: useStyles({
|
||||
backgroundColor: color.default,
|
||||
}),
|
||||
primary: useStyles({
|
||||
color: color.primary,
|
||||
}),
|
||||
primaryBg: useStyles({
|
||||
backgroundColor: color.primary,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
statusBarHeight,
|
||||
paddingTopStatusBarHeight,
|
||||
flexRow,
|
||||
flexColumn,
|
||||
flexSub,
|
||||
flexShrink,
|
||||
flexWrap,
|
||||
flexnoWrap,
|
||||
justifyStart,
|
||||
justifyEnd,
|
||||
justifyCenter,
|
||||
justifyBetween,
|
||||
justifyAround,
|
||||
justifyEvenly,
|
||||
alignItemsStart,
|
||||
alignItemsEnd,
|
||||
alignItemsCenter,
|
||||
alignItemsStretch,
|
||||
positionRelative,
|
||||
positionAbsolute,
|
||||
getColor,
|
||||
getColorStyles,
|
||||
useStyles,
|
||||
};
|
||||
@@ -1,3 +1,17 @@
|
||||
module.exports = {
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
presets: [
|
||||
'module:@react-native/babel-preset',
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
root: ['./'],
|
||||
alias: {
|
||||
'@': './'
|
||||
},
|
||||
},
|
||||
],
|
||||
['import', { libraryName: '@ant-design/react-native' }], // 与 Web 平台的区别是不需要设置 style
|
||||
],
|
||||
};
|
||||
|
||||
41
components/AuthContext/index.jsx
Normal file
41
components/AuthContext/index.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { createContext, useState, useContext } from 'react';
|
||||
import { getColor } from '@/assets/styles/css';
|
||||
import { Provider } from '@ant-design/react-native';
|
||||
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { StatusBar } from 'react-native';
|
||||
|
||||
StatusBar.setHidden(true);
|
||||
|
||||
// 创建一个上下文
|
||||
const AuthContext = createContext();
|
||||
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [user, setUser] = useState(null);
|
||||
const hasPerm = (roles = []) => {
|
||||
if (user && user.roles) {
|
||||
return (user.roles || []).some(role => {
|
||||
return (roles || []).includes(role);
|
||||
});
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
const currentLocale = 'zh-CN';
|
||||
const themeColor = getColor();
|
||||
|
||||
return (
|
||||
<Provider locale={currentLocale} theme={themeColor}>
|
||||
<SafeAreaView style={{ flex: 1}}>
|
||||
<AuthContext.Provider value={{ user, setUser, hasPerm, themeColor, currentLocale }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
</SafeAreaView>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// 自定义 Hook 来使用 AuthContext
|
||||
export const useContextHook = () => {
|
||||
return useContext(AuthContext);
|
||||
};
|
||||
32
components/RotatingIcon/index.jsx
Normal file
32
components/RotatingIcon/index.jsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Animated, View } from 'react-native';
|
||||
import { Icon } from '@ant-design/react-native'
|
||||
|
||||
const RotatingIcon = () => {
|
||||
const spinAnim = useRef(new Animated.Value(0)).current; // 创建一个新的 Animated.Value 实例
|
||||
|
||||
useEffect(() => {
|
||||
Animated.loop(
|
||||
Animated.timing(spinAnim, {
|
||||
toValue: 1,
|
||||
duration: 800,
|
||||
useNativeDriver: true, // 这是推荐的做法
|
||||
})
|
||||
).start();
|
||||
}, [spinAnim]);
|
||||
|
||||
const spin = spinAnim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
|
||||
<Animated.View style={{ transform: [{ rotate: spin }] }}>
|
||||
<Icon name="loading-3-quarters" size={24} color="#999999" />
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default RotatingIcon;
|
||||
13
config/env.js
Normal file
13
config/env.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const dev = {
|
||||
URL: 'https://me.dreamwork.site',
|
||||
BASE: '/eladmin/',
|
||||
|
||||
// URL: 'http://127.0.0.1:8000',
|
||||
// BASE: '',
|
||||
};
|
||||
const prod = {
|
||||
URL: 'https://mesystem.online',
|
||||
BASE: '/eladmin/',
|
||||
};
|
||||
|
||||
export default dev;
|
||||
64
hooks/useRouter.js
Normal file
64
hooks/useRouter.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
useIsFocused,
|
||||
useRoute,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native';
|
||||
import AsyncStorage from '@/storage/index';
|
||||
|
||||
export const useRouter = props => {
|
||||
const router = useNavigation();
|
||||
console.log('🚀 ~ useRouter ~ router:', router)
|
||||
const isFocused = useIsFocused();
|
||||
const route = useRoute();
|
||||
|
||||
const linkTo = (path, params) => {
|
||||
router.navigate(path, params);
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
router.goBack();
|
||||
};
|
||||
|
||||
const setParams = params => {
|
||||
router.setParams(params);
|
||||
};
|
||||
|
||||
const getParams = () => {
|
||||
return router.getState().params;
|
||||
};
|
||||
|
||||
// 获取当前路由
|
||||
const getCurrentRoute = () => {
|
||||
return router.getState().routes[router.getState().index].name;
|
||||
};
|
||||
|
||||
// 重置所有路由
|
||||
const resetAllRoutes = (name) => {
|
||||
router.reset({
|
||||
index: 0,
|
||||
routes: [{ name: name || 'Guide' }],
|
||||
});
|
||||
};
|
||||
|
||||
// 判断是否 登录了再跳转 如果 没有登录就去登录页面
|
||||
const isLogin = () => {
|
||||
const user = AsyncStorage.getItemAsync('user');
|
||||
if (!user) {
|
||||
router.navigate('Login');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
router,
|
||||
isFocused,
|
||||
route,
|
||||
linkTo,
|
||||
goBack,
|
||||
setParams,
|
||||
getParams,
|
||||
isLogin,
|
||||
getCurrentRoute,
|
||||
resetAllRoutes,
|
||||
};
|
||||
};
|
||||
1
index.js
1
index.js
@@ -5,5 +5,6 @@
|
||||
import { AppRegistry } from 'react-native';
|
||||
import App from './App';
|
||||
import { name as appName } from './app.json';
|
||||
import 'react-native-gesture-handler';
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
9B06ED4A4A7343B390CE52D1 /* antfill.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A47F5CC487484D8E9ACED7FB /* antfill.ttf */; };
|
||||
1F775F769E4C47D2BE44D107 /* antoutline.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FFCC4D9CCC40417BAB83BA33 /* antoutline.ttf */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -24,6 +26,8 @@
|
||||
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = app/AppDelegate.swift; sourceTree = "<group>"; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = app/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
A47F5CC487484D8E9ACED7FB /* antfill.ttf */ = {isa = PBXFileReference; name = "antfill.ttf"; path = "../node_modules/@ant-design/icons-react-native/fonts/antfill.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
FFCC4D9CCC40417BAB83BA33 /* antoutline.ttf */ = {isa = PBXFileReference; name = "antoutline.ttf"; path = "../node_modules/@ant-design/icons-react-native/fonts/antoutline.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -74,6 +78,7 @@
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||
B056C340A7E240C19B8D27EB /* Resources */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
@@ -97,6 +102,16 @@
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B056C340A7E240C19B8D27EB /* Resources */ = {
|
||||
isa = "PBXGroup";
|
||||
children = (
|
||||
A47F5CC487484D8E9ACED7FB /* antfill.ttf */,
|
||||
FFCC4D9CCC40417BAB83BA33 /* antoutline.ttf */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
path = "";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -159,6 +174,8 @@
|
||||
files = (
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
9B06ED4A4A7343B390CE52D1 /* antfill.ttf in Resources */,
|
||||
1F775F769E4C47D2BE44D107 /* antoutline.ttf in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -26,14 +26,13 @@
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<string/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
@@ -48,5 +47,10 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>antfill.ttf</string>
|
||||
<string>antoutline.ttf</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
13
ios/link-assets-manifest.json
Normal file
13
ios/link-assets-manifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"migIndex": 1,
|
||||
"data": [
|
||||
{
|
||||
"path": "node_modules/@ant-design/icons-react-native/fonts/antfill.ttf",
|
||||
"sha1": "56960e7721fc92b62e0f7c4d131ffe34ed042c49"
|
||||
},
|
||||
{
|
||||
"path": "node_modules/@ant-design/icons-react-native/fonts/antoutline.ttf",
|
||||
"sha1": "66720607b7496a48f145425386b2082b73662fd1"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
jsconfig.json
Normal file
10
jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"include": ["**/*.js", "**/*.jsx"],
|
||||
"exclude": ["**/node_modules", "**/Pods"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
10
locales/en.json
Normal file
10
locales/en.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"welcome": "Welcome",
|
||||
"greeting": "Hello, {{name}}!",
|
||||
"login": "Login123",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"login_success": "Login Success",
|
||||
"login_failed": "Login Failed",
|
||||
"login_error_message": "Login Failed, Please Try Again"
|
||||
}
|
||||
60
locales/i18n.js
Normal file
60
locales/i18n.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import * as RNLocalize from 'react-native-localize';
|
||||
import AsyncStorage from '@/storage/index';
|
||||
// 导入语言包
|
||||
import en from './en.json';
|
||||
import zh from './zh.json';
|
||||
console.log('🚀 ~ RNLocalize:', RNLocalize)
|
||||
|
||||
// 导入语言资源
|
||||
const resources = {
|
||||
en: { translation: en },
|
||||
zh: { translation: zh }
|
||||
};
|
||||
|
||||
const languageDetector = {
|
||||
type: 'languageDetector',
|
||||
async: true,
|
||||
detect: async callback => {
|
||||
try {
|
||||
// 从本地存储读取用户选择的语言
|
||||
const saveLng = await AsyncStorage.getItemAsync('@APP_LANGUAGE');
|
||||
const lng = saveLng ? saveLng.lng : null;
|
||||
|
||||
// 如果没有选择的语言,使用设备首选语言
|
||||
const bestLanguageOption = RNLocalize.getLocales()[0].languageCode || 'en';
|
||||
callback(lng || bestLanguageOption);
|
||||
} catch (error) {
|
||||
console.error("Failed to detect or load the language", error);
|
||||
}
|
||||
},
|
||||
init: () => {},
|
||||
cacheUserLanguage: async lng => {
|
||||
console.log('🚀 ~ lng:', lng)
|
||||
try {
|
||||
// 将用户选择的语言存储到本地
|
||||
await AsyncStorage.setItemAsync('@APP_LANGUAGE', {lng});
|
||||
} catch (error) {
|
||||
console.error("Failed to save the user's language choice", error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
i18n
|
||||
.use(languageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'en',
|
||||
compatibilityJSON: 'v3',
|
||||
interpolation: {
|
||||
escapeValue: false, // react already saves from xss
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
|
||||
|
||||
10
locales/zh.json
Normal file
10
locales/zh.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"welcome": "欢迎",
|
||||
"greeting": "你好, {{name}}!",
|
||||
"login": "登录",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"login_success": "登录成功",
|
||||
"login_failed": "登录失败",
|
||||
"login_error_message": "登录失败,请重试"
|
||||
}
|
||||
2776
package-lock.json
generated
2776
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -10,10 +10,26 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.81.0",
|
||||
"@ant-design/icons-react-native": "^2.3.2",
|
||||
"@ant-design/react-native": "^5.4.3",
|
||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||
"@react-native/new-app-screen": "0.81.0",
|
||||
"react-native-safe-area-context": "^5.5.2"
|
||||
"@react-navigation/bottom-tabs": "^7.4.6",
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"@react-navigation/stack": "^7.4.7",
|
||||
"axios": "^1.11.0",
|
||||
"babel-plugin-import": "^1.13.8",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"i18next": "^25.4.2",
|
||||
"react": "19.1.0",
|
||||
"react-i18next": "^15.7.2",
|
||||
"react-native": "0.81.0",
|
||||
"react-native-gesture-handler": "^2.28.0",
|
||||
"react-native-localize": "^3.5.2",
|
||||
"react-native-reanimated": "^4.0.2",
|
||||
"react-native-safe-area-context": "^5.6.1",
|
||||
"react-native-screens": "^4.15.3",
|
||||
"react-native-worklets": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
@@ -38,4 +54,4 @@
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
react-native.config.js
Normal file
3
react-native.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
assets: ['node_modules/@ant-design/icons-react-native/fonts'],
|
||||
};
|
||||
26
storage/index.js
Normal file
26
storage/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// storage.js
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
const Storage = {
|
||||
setItemAsync(key, value) {
|
||||
return AsyncStorage.setItem(key, JSON.stringify(value || {}));
|
||||
},
|
||||
|
||||
getItemAsync(key) {
|
||||
return new Promise(resolve=>{
|
||||
AsyncStorage.getItem(key).then(value=>{
|
||||
resolve(value ? JSON.parse(value) : null)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
removeItemAsync(key) {
|
||||
return AsyncStorage.removeItem(key)
|
||||
},
|
||||
|
||||
clearAllAsync() {
|
||||
return AsyncStorage.clear();
|
||||
}
|
||||
};
|
||||
|
||||
export default Storage;
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": "@react-native/typescript-config",
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["**/node_modules", "**/Pods"]
|
||||
}
|
||||
87
views/Guide/index.jsx
Normal file
87
views/Guide/index.jsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
StatusBar
|
||||
} from 'react-native';
|
||||
|
||||
import { Toast, Button } from '@ant-design/react-native';
|
||||
import { useContextHook } from '@/components/AuthContext/index';
|
||||
|
||||
import AsyncStorage from '@/storage/index';
|
||||
import { useRouter } from '@/hooks/useRouter';
|
||||
|
||||
import {
|
||||
statusBarHeight,
|
||||
paddingTopStatusBarHeight,
|
||||
flexRow,
|
||||
flexColumn,
|
||||
flexSub,
|
||||
flexShrink,
|
||||
flexWrap,
|
||||
flexnoWrap,
|
||||
justifyStart,
|
||||
justifyEnd,
|
||||
justifyCenter,
|
||||
justifyBetween,
|
||||
justifyAround,
|
||||
justifyEvenly,
|
||||
alignItemsStart,
|
||||
alignItemsEnd,
|
||||
alignItemsCenter,
|
||||
alignItemsStretch,
|
||||
positionRelative,
|
||||
positionAbsolute,
|
||||
useStyles,
|
||||
} from '@/assets/styles/css';
|
||||
|
||||
const Index = () => {
|
||||
const { themeColor } = useContextHook();
|
||||
const { linkTo, resetAllRoutes, isFocused } = useRouter();
|
||||
useEffect(() => {
|
||||
const user = AsyncStorage.getItemAsync('user');
|
||||
// 这里请求配置后,再跳转
|
||||
setTimeout(() => {
|
||||
console.log('user', user);
|
||||
if (user && user.token) {
|
||||
resetAllRoutes('Home');
|
||||
} else {
|
||||
resetAllRoutes('Login');
|
||||
}
|
||||
}, 1000);
|
||||
}, [isFocused]);
|
||||
|
||||
return (
|
||||
<View style={[flexSub, {}]}>
|
||||
<View
|
||||
style={[
|
||||
flexSub,
|
||||
justifyCenter,
|
||||
alignItemsCenter,
|
||||
flexRow
|
||||
]}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
backgroundColor: 'red',
|
||||
}}>
|
||||
login111
|
||||
</Text>
|
||||
<Button type="primary" onPress={() => {
|
||||
Toast.show({
|
||||
content: 'Please enter user name',
|
||||
position: 'center',
|
||||
mask: true,
|
||||
});
|
||||
}}>Click me</Button>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default Index;
|
||||
64
views/Home/Index/index.jsx
Normal file
64
views/Home/Index/index.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
|
||||
import { Provider, Toast } from '@ant-design/react-native';
|
||||
|
||||
import {
|
||||
statusBarHeight,
|
||||
paddingTopStatusBarHeight,
|
||||
flexRow,
|
||||
flexColumn,
|
||||
flexSub,
|
||||
flexShrink,
|
||||
flexWrap,
|
||||
flexnoWrap,
|
||||
justifyStart,
|
||||
justifyEnd,
|
||||
justifyCenter,
|
||||
justifyBetween,
|
||||
justifyAround,
|
||||
justifyEvenly,
|
||||
alignItemsStart,
|
||||
alignItemsEnd,
|
||||
alignItemsCenter,
|
||||
alignItemsStretch,
|
||||
positionRelative,
|
||||
positionAbsolute,
|
||||
useStyles,
|
||||
} from '@/assets/styles/css';
|
||||
|
||||
const Index = () => {
|
||||
useEffect(() => {
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
flexSub,
|
||||
justifyCenter,
|
||||
alignItemsCenter,
|
||||
{
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
]}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
}}>
|
||||
index
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default Index;
|
||||
64
views/Home/Inventory/index.jsx
Normal file
64
views/Home/Inventory/index.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
|
||||
import { Provider, Toast } from '@ant-design/react-native';
|
||||
|
||||
import {
|
||||
statusBarHeight,
|
||||
paddingTopStatusBarHeight,
|
||||
flexRow,
|
||||
flexColumn,
|
||||
flexSub,
|
||||
flexShrink,
|
||||
flexWrap,
|
||||
flexnoWrap,
|
||||
justifyStart,
|
||||
justifyEnd,
|
||||
justifyCenter,
|
||||
justifyBetween,
|
||||
justifyAround,
|
||||
justifyEvenly,
|
||||
alignItemsStart,
|
||||
alignItemsEnd,
|
||||
alignItemsCenter,
|
||||
alignItemsStretch,
|
||||
positionRelative,
|
||||
positionAbsolute,
|
||||
useStyles,
|
||||
} from '@/assets/styles/css';
|
||||
|
||||
const Index = () => {
|
||||
useEffect(() => {
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
flexSub,
|
||||
justifyCenter,
|
||||
alignItemsCenter,
|
||||
{
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
]}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
}}>
|
||||
Inventory
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default Index;
|
||||
64
views/Home/Profile/index.jsx
Normal file
64
views/Home/Profile/index.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
|
||||
import { Provider, Toast } from '@ant-design/react-native';
|
||||
|
||||
import {
|
||||
statusBarHeight,
|
||||
paddingTopStatusBarHeight,
|
||||
flexRow,
|
||||
flexColumn,
|
||||
flexSub,
|
||||
flexShrink,
|
||||
flexWrap,
|
||||
flexnoWrap,
|
||||
justifyStart,
|
||||
justifyEnd,
|
||||
justifyCenter,
|
||||
justifyBetween,
|
||||
justifyAround,
|
||||
justifyEvenly,
|
||||
alignItemsStart,
|
||||
alignItemsEnd,
|
||||
alignItemsCenter,
|
||||
alignItemsStretch,
|
||||
positionRelative,
|
||||
positionAbsolute,
|
||||
useStyles,
|
||||
} from '@/assets/styles/css';
|
||||
|
||||
const Index = () => {
|
||||
useEffect(() => {
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
flexSub,
|
||||
justifyCenter,
|
||||
alignItemsCenter,
|
||||
{
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
]}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
}}>
|
||||
Profile
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default Index;
|
||||
195
views/Login/index.jsx
Normal file
195
views/Login/index.jsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { Provider, Toast } from '@ant-design/react-native';
|
||||
|
||||
|
||||
import AsyncStorage from '@/storage/index';
|
||||
import { useNavigation, useIsFocused } from '@react-navigation/native';
|
||||
import { useContextHook } from '@/components/AuthContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
statusBarHeight,
|
||||
paddingTopStatusBarHeight,
|
||||
flexRow,
|
||||
flexColumn,
|
||||
flexSub,
|
||||
flexShrink,
|
||||
flexWrap,
|
||||
flexnoWrap,
|
||||
justifyStart,
|
||||
justifyEnd,
|
||||
justifyCenter,
|
||||
justifyBetween,
|
||||
justifyAround,
|
||||
justifyEvenly,
|
||||
alignItemsStart,
|
||||
alignItemsEnd,
|
||||
alignItemsCenter,
|
||||
alignItemsStretch,
|
||||
positionRelative,
|
||||
positionAbsolute,
|
||||
useStyles,
|
||||
} from '@/assets/styles/css';
|
||||
|
||||
|
||||
const inputItem = useStyles({
|
||||
width: '80%',
|
||||
height: 40,
|
||||
borderColor: 'gray',
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
paddingHorizontal: 10,
|
||||
marginBottom: 20,
|
||||
});
|
||||
|
||||
const LoginScreen = () => {
|
||||
const { themeColor } = useContextHook();
|
||||
const { t, i18n } = useTranslation();
|
||||
console.log('🚀 ~ LoginScreen ~ i18n:', i18n)
|
||||
|
||||
const router = useNavigation();
|
||||
const [language, setLanguage] = useState('en');
|
||||
|
||||
const isFocused = useIsFocused();
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
useEffect(() => {
|
||||
|
||||
}, [isFocused]);
|
||||
|
||||
const handleLogin = () => {
|
||||
if (!username) {
|
||||
Toast.show({
|
||||
content: t('Please enter user name'),
|
||||
position: 'center',
|
||||
mask: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!password) {
|
||||
Toast.show({
|
||||
content: t('Please enter password'),
|
||||
position: 'center',
|
||||
mask: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const key = Toast.show({
|
||||
icon: 'loading',
|
||||
content: t('loading'),
|
||||
position: 'center',
|
||||
mask: true,
|
||||
duration: 0,
|
||||
});
|
||||
console.log(123,123);
|
||||
// login({ username, password })
|
||||
// .then(res => {
|
||||
// Toast.remove(key);
|
||||
// setTimeout(() => {
|
||||
// Toast.show({
|
||||
// content: t('login success'), //i18n.t('login_success'),
|
||||
// position: 'center',
|
||||
// mask: true,
|
||||
// onClose: () => {
|
||||
// // 设置全局的用户信息
|
||||
// setUser(res.user)
|
||||
// AsyncStorage.setItem('appToken', {
|
||||
// appToken: res.token,
|
||||
// user: res.user,
|
||||
// }).then(() => {
|
||||
// router.replace('Home');
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
// }, 200);
|
||||
// })
|
||||
// .catch(err => {
|
||||
// Toast.remove(key);
|
||||
// setTimeout(() => {
|
||||
// Toast.show({
|
||||
// content: t('login_failed'), // i18n.t('login_failed'),
|
||||
// position: 'center',
|
||||
// mask: true,
|
||||
// });
|
||||
// }, 200);
|
||||
// // i18n.t('login_error_message')
|
||||
// });
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
flexSub,
|
||||
justifyCenter,
|
||||
alignItemsCenter,
|
||||
{
|
||||
backgroundColor: themeColor.colorBgBase,
|
||||
},
|
||||
]}>
|
||||
<View style={[{ padding: 20 }]}>
|
||||
<Image
|
||||
source={require('@/assets/images/logo.png')}
|
||||
style={[
|
||||
{
|
||||
width: 300,
|
||||
height: 150,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
}}>
|
||||
{t('login')}
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[inputItem]}
|
||||
placeholder={t('username')}
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
autoCapitalize="none"
|
||||
keyboardType="default"
|
||||
/>
|
||||
<TextInput
|
||||
style={[inputItem]}
|
||||
placeholder={t('password')}
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
width: '80%',
|
||||
backgroundColor: '#7ac0f6',
|
||||
borderRadius: 5,
|
||||
paddingVertical: 10,
|
||||
}}
|
||||
onPress={handleLogin}>
|
||||
<Text
|
||||
style={{
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
fontSize: 18,
|
||||
}}>
|
||||
{t('login')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default LoginScreen;
|
||||
Reference in New Issue
Block a user