boxdz 1 ano atrás
pai
commit
8c7908c270

+ 3 - 0
.env.example

@@ -0,0 +1,3 @@
+# dev
+NODE_ENV=
+BASE_URL=

+ 4 - 0
.gitignore

@@ -33,3 +33,7 @@ yarn-error.*
 
 # typescript
 *.tsbuildinfo
+
+
+# env
+.env

+ 23 - 11
App.tsx

@@ -1,20 +1,32 @@
-import { StatusBar } from 'expo-status-bar';
-import { StyleSheet, Text, View } from 'react-native';
+import { StatusBar } from "expo-status-bar";
+import { StyleSheet, Text, View } from "react-native";
+import { Provider } from "react-redux";
+import Counter from "./component/counter";
+import store from "./store";
 
-export default function App() {
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: "#fff",
+    alignItems: "center",
+    justifyContent: "center",
+  },
+});
+
+function App() {
   return (
     <View style={styles.container}>
       <Text>Open up App.tsx to start working on your app!</Text>
       <StatusBar style="auto" />
+      <Counter />
     </View>
   );
 }
 
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    backgroundColor: '#fff',
-    alignItems: 'center',
-    justifyContent: 'center',
-  },
-});
+export default function Root() {
+  return (
+    <Provider store={store}>
+      <App />
+    </Provider>
+  );
+}

+ 5 - 0
Readme.md

@@ -0,0 +1,5 @@
+# page and file introduce
+
+    - store.js: redux status tree for all reducer register
+    - reducers: redux update function folder
+    - actions : type for update status

+ 5 - 1
app.json

@@ -6,6 +6,7 @@
     "orientation": "portrait",
     "icon": "./assets/icon.png",
     "userInterfaceStyle": "light",
+    "scheme": "template",
     "splash": {
       "image": "./assets/splash.png",
       "resizeMode": "contain",
@@ -25,6 +26,9 @@
     },
     "web": {
       "favicon": "./assets/favicon.png"
-    }
+    },
+    "plugins": [
+      "expo-router"
+    ]
   }
 }

+ 37 - 0
app/(tabs)/_layout.tsx

@@ -0,0 +1,37 @@
+import React from "react";
+import FontAwesome from "@expo/vector-icons/FontAwesome";
+import { Tabs } from "expo-router";
+
+export default function TabLayout() {
+  return (
+    <Tabs screenOptions={{ tabBarActiveTintColor: "blue" }}>
+      <Tabs.Screen
+        name="index"
+        options={{
+          title: "Home",
+          tabBarIcon: ({ color }) => (
+            <FontAwesome size={28} name="home" color={color} />
+          ),
+        }}
+      />
+      <Tabs.Screen
+        name="settings/index"
+        options={{
+          title: "Settings",
+          tabBarIcon: ({ color }) => (
+            <FontAwesome size={28} name="cog" color={color} />
+          ),
+        }}
+      />
+      <Tabs.Screen
+        name="home/index"
+        options={{
+          title: "Homes",
+          tabBarIcon: ({ color }) => (
+            <FontAwesome size={28} name="search" color={color} />
+          ),
+        }}
+      />
+    </Tabs>
+  );
+}

+ 11 - 0
app/(tabs)/home/index.tsx

@@ -0,0 +1,11 @@
+import { Text } from "react-native";
+import React from "react";
+export default function Index() {
+  /**********************************狀態管理**********************************/
+  /**********************************狀態管理**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************異步函數**********************************/
+  /**********************************異步函數**********************************/
+  return <Text>index</Text>;
+}

+ 66 - 0
app/(tabs)/index.tsx

@@ -0,0 +1,66 @@
+import React, { useEffect, useState } from "react";
+import { Text, StyleSheet, View, PermissionsAndroid } from "react-native";
+import MapView, { Marker } from "react-native-maps";
+import * as Location from "expo-location";
+import { LocationObject } from "expo-location";
+import { mapStyle } from "../../style/map";
+
+export default function Index() {
+  /**********************************狀態管理**********************************/
+  const [location, setLocation] = useState<LocationObject>();
+
+  useEffect(() => {
+    (async () => {
+      let { status } = await Location.requestForegroundPermissionsAsync();
+      if (status !== "granted") {
+        console.log("Permission denied");
+        return;
+      }
+
+      let location = await Location.getCurrentPositionAsync({});
+      setLocation(location);
+    })();
+  }, []);
+  /**********************************狀態管理**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************異步函數**********************************/
+  /**********************************異步函數**********************************/
+  return (
+    <View>
+      {location && (
+        <MapView
+          customMapStyle={mapStyle}
+          followsUserLocation={true}
+          initialRegion={{
+            latitude: 22.3193,
+            longitude: 114.1694,
+            latitudeDelta: 0.1,
+            longitudeDelta: 0.1,
+          }}
+          showsPointsOfInterest={false}
+          showsUserLocation={true}
+          showsBuildings={false}
+          showsTraffic={false}
+          style={styles.map}
+        >
+          <Marker
+            coordinate={{ latitude: 22.3193, longitude: 114.1694 }}
+            title="Marker Title"
+            onPress={() => {}}
+            description="Marker Description"
+          />
+        </MapView>
+      )}
+    </View>
+  );
+}
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+  },
+  map: {
+    width: "100%",
+    height: "100%",
+  },
+});

+ 11 - 0
app/(tabs)/settings/index.tsx

@@ -0,0 +1,11 @@
+import React from "react";
+import { Text } from "react-native";
+export default function Index() {
+  /**********************************狀態管理**********************************/
+  /**********************************狀態管理**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************異步函數**********************************/
+  /**********************************異步函數**********************************/
+  return <Text>setting</Text>;
+}

+ 9 - 0
app/_layout.tsx

@@ -0,0 +1,9 @@
+import { Stack } from "expo-router/stack";
+
+export default function AppLayout() {
+  return (
+    <Stack>
+      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
+    </Stack>
+  );
+}

+ 25 - 0
component/counter.tsx

@@ -0,0 +1,25 @@
+import React from "react";
+import { useSelector, useDispatch } from "react-redux";
+import { counterSlice, decrement, increment } from "../reducers/counterReducer";
+import { Button, Text, View } from "react-native";
+import store from "../store";
+
+export default function Counter() {
+  /**********************************狀態管理**********************************/
+  const value = useSelector((state: any) => state.counter.value);
+  const dispatch = useDispatch();
+  /**********************************狀態管理**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************組件初始化**********************************/
+  /**********************************異步函數**********************************/
+  /**********************************異步函數**********************************/
+  return (
+    <View>
+      <View style={{ flexDirection: "row", alignItems: "center" }}>
+        <Button title="Increment" onPress={() => dispatch(increment())} />
+        <Text>{value}</Text>
+        <Button title="Decrement" onPress={() => dispatch(decrement())} />
+      </View>
+    </View>
+  );
+}

+ 74 - 0
config/server.tsx

@@ -0,0 +1,74 @@
+import axios from "axios";
+
+let baseURL: string;
+if (process.env.NODE_ENV === "production") {
+  baseURL = "";
+} else {
+  baseURL = process.env.NEXT_PUBLIC_DEV_BASE_URL as string;
+}
+
+// 拦截器
+axios.interceptors.response.use(
+  (response) => {
+    return response;
+  },
+  (error) => {
+    return Promise.reject(error);
+  }
+);
+
+axios.interceptors.request.use(
+  (config) => {
+    console.log(config);
+    config.headers["Accept"] = "application/json";
+
+    config.baseURL = baseURL;
+    // config.timeout = 10000;
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  }
+);
+
+export function requests<T>(
+  url: string,
+  option: {
+    method: "post" | "get" | "put" | "delete";
+    params?: { [key: string]: any };
+    data?: { [key: string]: any };
+    headers?: { [key: string]: any };
+    requestType?: "json" | "form";
+    timeout?: number;
+  }
+) {
+  if (!option.requestType) {
+    option.requestType = "json";
+  }
+  return new Promise<T>((resolve, reject) => {
+    const Content_Type =
+      option.requestType == "json"
+        ? "application/json;charset=UTF-8"
+        : "application/x-www-form-urlencoded;charset=UTF-8";
+
+    axios<T>({
+      timeout: option.timeout,
+      url: baseURL + url,
+      method: option.method,
+      params: option.params,
+      data: option.data,
+      headers: {
+        "Content-Type": Content_Type,
+        ...option.headers,
+      },
+    })
+      .then((res) => {
+        resolve(res.data);
+      })
+      .catch((err) => {
+        reject(err);
+      });
+  });
+}
+
+export default requests;

+ 0 - 0
config/serverError.tsx


Diferenças do arquivo suprimidas por serem muito extensas
+ 764 - 10
package-lock.json


+ 15 - 2
package.json

@@ -1,7 +1,7 @@
 {
   "name": "template",
   "version": "1.0.0",
-  "main": "node_modules/expo/AppEntry.js",
+  "main": "expo-router/entry",
   "scripts": {
     "start": "expo start",
     "android": "expo start --android",
@@ -9,10 +9,23 @@
     "web": "expo start --web"
   },
   "dependencies": {
+    "@reduxjs/toolkit": "^2.2.1",
+    "axios": "^1.6.7",
+    "dotenv": "^16.4.5",
     "expo": "~50.0.11",
+    "expo-constants": "~15.4.5",
+    "expo-env": "^1.1.1",
+    "expo-linking": "~6.2.2",
+    "expo-location": "~16.5.5",
+    "expo-router": "~3.4.8",
     "expo-status-bar": "~1.11.1",
     "react": "18.2.0",
-    "react-native": "0.73.4"
+    "react-native": "0.73.4",
+    "react-native-maps": "1.10.0",
+    "react-native-safe-area-context": "4.8.2",
+    "react-native-screens": "~3.29.0",
+    "react-redux": "^9.1.0",
+    "redux": "^5.0.1"
   },
   "devDependencies": {
     "@babel/core": "^7.20.0",

+ 23 - 0
reducers/counterReducer.tsx

@@ -0,0 +1,23 @@
+import { createSlice } from "@reduxjs/toolkit";
+
+export const counterSlice = createSlice({
+  name: "counter",
+  initialState: {
+    value: 0,
+  },
+  reducers: {
+    increment: (state) => {
+      state.value += 1;
+    },
+    decrement: (state) => {
+      state.value -= 1;
+    },
+    incrementByAmount: (state, action) => {
+      state.value += action.payload;
+    },
+  },
+});
+
+export const { increment, decrement, incrementByAmount } = counterSlice.actions;
+
+export default counterSlice.reducer;

+ 11 - 0
store.tsx

@@ -0,0 +1,11 @@
+import { configureStore } from "@reduxjs/toolkit";
+import counterReducer from "./reducers/counterReducer";
+
+const store = configureStore({
+  reducer: {
+    counter: counterReducer,
+    // Define a top-level state field named `todos`, handled by `todosReducer`
+  },
+});
+
+export default store;

+ 36 - 0
style/map.ts

@@ -0,0 +1,36 @@
+export const mapStyle =[
+  {
+    "featureType": "poi",
+    "stylers": [
+      {
+        "visibility": "off"
+      }
+    ]
+  },
+  {
+    "featureType": "road",
+    "elementType": "labels.icon",
+    "stylers": [
+      {
+        "visibility": "off"
+      }
+    ]
+  },
+  {
+    "featureType": "road.highway",
+    "elementType": "labels.icon",
+    "stylers": [
+      {
+        "visibility": "off"
+      }
+    ]
+  },
+  {
+    "featureType": "transit",
+    "stylers": [
+      {
+        "visibility": "off"
+      }
+    ]
+  }
+]

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff