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 React from 'react';
|
||||||
import ReactTestRenderer from 'react-test-renderer';
|
import ReactTestRenderer from 'react-test-renderer';
|
||||||
import App from '../App';
|
import App from '@/App';
|
||||||
|
|
||||||
test('renders correctly', async () => {
|
test('renders correctly', async () => {
|
||||||
await ReactTestRenderer.act(() => {
|
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 = {
|
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 { AppRegistry } from 'react-native';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { name as appName } from './app.json';
|
import { name as appName } from './app.json';
|
||||||
|
import 'react-native-gesture-handler';
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
AppRegistry.registerComponent(appName, () => App);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference 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>"; };
|
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>"; };
|
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; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -74,6 +78,7 @@
|
|||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||||
|
B056C340A7E240C19B8D27EB /* Resources */,
|
||||||
);
|
);
|
||||||
indentWidth = 2;
|
indentWidth = 2;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -97,6 +102,16 @@
|
|||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
B056C340A7E240C19B8D27EB /* Resources */ = {
|
||||||
|
isa = "PBXGroup";
|
||||||
|
children = (
|
||||||
|
A47F5CC487484D8E9ACED7FB /* antfill.ttf */,
|
||||||
|
FFCC4D9CCC40417BAB83BA33 /* antoutline.ttf */,
|
||||||
|
);
|
||||||
|
name = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
path = "";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -159,6 +174,8 @@
|
|||||||
files = (
|
files = (
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||||
|
9B06ED4A4A7343B390CE52D1 /* antfill.ttf in Resources */,
|
||||||
|
1F775F769E4C47D2BE44D107 /* antoutline.ttf in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,14 +26,13 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string></string>
|
<string/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
@@ -48,5 +47,10 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>antfill.ttf</string>
|
||||||
|
<string>antoutline.ttf</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</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"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "19.1.0",
|
"@ant-design/icons-react-native": "^2.3.2",
|
||||||
"react-native": "0.81.0",
|
"@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/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": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
@@ -38,4 +54,4 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"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