panhui 5 жил өмнө
parent
commit
df783eeb23

+ 34 - 0
.vscode/flooks.code-snippets

@@ -0,0 +1,34 @@
+{
+	// Place your DingdongClient 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and 
+	// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope 
+	// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is 
+	// used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 
+	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. 
+	// Placeholders with the same ids are connected.
+	// Example:
+	"flooks": {
+		"scope": "javascript,typescript",
+		"prefix": "flooks",
+		"body": [
+			"const counter = (now) => ({",
+			"  count: 0,",
+			"  add() {",
+			"    const { count } = now();",
+			"    now({ count: count + 1 });",
+			"  },",
+			"  sub() {",
+			"    const { count } = now();",
+			"    now({ count: count - 1 });",
+			"  },",
+			"  async addLater() {",
+			"    const { add } = now();",
+			"    await new Promise((resolve) => setTimeout(resolve, 1000));",
+			"    add();",
+			"  },",
+			"});",
+			"",
+			"export default counter;"
+		],
+		"description": "Log output to console"
+	}
+}

+ 66 - 14
App.js

@@ -1,38 +1,90 @@
 import * as React from 'react'
-
-import { NavigationContainer } from '@react-navigation/native'
+import { StatusBar, Platform, StyleSheet, View } from 'react-native'
+import { NavigationContainer, CommonActions } from '@react-navigation/native'
 import {
   createStackNavigator,
   CardStyleInterpolators,
 } from '@react-navigation/stack'
+import { UseAPIProvider } from '@umijs/use-request'
 
-import { StatusBar, StyleSheet, View } from 'react-native'
+import { ThemeProvider } from 'react-native-paper'
+import { Provider } from '@ant-design/react-native'
+import useModel from 'flooks'
+import { useUpdateEffect } from '@umijs/hooks'
 
 import { navigationRef } from './navigation/RootNavigation'
 import useCachedResources from './hooks/useCachedResources'
 import BottomTabNavigator from './navigation/BottomTabNavigator'
+import BasicScreens from './navigation/BaseNavigator'
+import words from './flooks/Words'
+import user from './flooks/User'
+import request from './Utils/RequestUtils'
+import theme from './constants/Theme'
 
 const Stack = createStackNavigator()
 
 export default function App() {
   const isLoadingComplete = useCachedResources()
+  const { wordsInit } = useModel(words, ['local'])
+  // Load words
+  wordsInit()
+  const { id } = useModel(user, ['id'])
+  useUpdateEffect(() => {
+    let initName = ''
+    if (id === 0) {
+      // 未登录
+      initName = 'Login'
+    } else if (id !== null) {
+      // 已登录
+      initName = 'Home'
+    }
+    if (initName) {
+      navigationRef.current.dispatch(
+        CommonActions.reset({
+          index: 0,
+          routes: [
+            {
+              name: initName,
+            },
+          ],
+        })
+      )
+    }
+  }, [id])
 
   if (!isLoadingComplete) {
     return null
   } else {
     return (
       <View style={styles.container}>
-        <StatusBar backgroundColor="#FFC21C" />
-        <NavigationContainer ref={navigationRef}>
-          <Stack.Navigator
-            screenOptions={{
-              gestureEnabled: true,
-              cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
-            }}
-          >
-            <Stack.Screen name="Root" component={BottomTabNavigator} />
-          </Stack.Navigator>
-        </NavigationContainer>
+        <UseAPIProvider
+          value={{
+            requestMethod: request,
+          }}
+        >
+          <ThemeProvider theme={theme}>
+            <Provider>
+              {Platform.OS !== 'ios' && (
+                <StatusBar translucent={false} backgroundColor="#FFC21C" />
+              )}
+              <NavigationContainer ref={navigationRef}>
+                <Stack.Navigator
+                  initRouteName="InitApp"
+                  screenOptions={{
+                    gestureEnabled: true,
+                    cardStyleInterpolator:
+                      CardStyleInterpolators.forHorizontalIOS,
+                  }}
+                  headerMode="none"
+                >
+                  {/* 基础功能页面 */}
+                  {BasicScreens(Stack.Screen)}
+                  <Stack.Screen name="Home" component={BottomTabNavigator} />
+                </Stack.Navigator>
+              </NavigationContainer>
+            </Provider>
+          </ThemeProvider>
+        </UseAPIProvider>
       </View>
     )
   }

+ 16 - 0
Utils/AsyncStorageUtils.js

@@ -0,0 +1,16 @@
+import { AsyncStorage } from 'react-native'
+
+const addAsyncStorage = async (key, value) => {
+  await AsyncStorage.setItem(key, value)
+  return true
+}
+const removeAsyncStorage = async (key) => {
+  await AsyncStorage.removeItem(key)
+  return true
+}
+const getAsyncStorage = async (key) => {
+  const val = await AsyncStorage.getItem(key)
+  return val
+}
+
+export { addAsyncStorage, removeAsyncStorage, getAsyncStorage }

+ 6 - 0
Utils/FormUtils.js

@@ -0,0 +1,6 @@
+function submitPhone(inputPhone) {
+  const value = inputPhone.replace(/ /g, '')
+  return value
+}
+
+export default submitPhone

+ 49 - 0
Utils/RequestUtils.js

@@ -0,0 +1,49 @@
+import { extend } from 'umi-request'
+import qs from 'qs'
+import { getAsyncStorage } from './AsyncStorageUtils'
+
+const baseUrl = 'http://dingdong.izouma.com'
+const request = extend({
+  prefix: baseUrl,
+  credentials: 'include',
+  errorHandler: (error) => {
+    let errorInfo = ''
+    if (error.response) {
+      errorInfo = error.data
+    } else {
+      errorInfo = error.message
+    }
+    throw errorInfo
+  },
+})
+
+request.interceptors.request.use(
+  (url, options) => {
+    if (options.requestType === 'form') {
+      options.headers = {
+        Accept: 'application/json',
+        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
+        ...options.headers,
+      }
+      options.data = qs.stringify(options.data)
+    }
+    return getAsyncStorage('token').then((token) => {
+      const headers = {
+        'Access-Control-Allow-Origin': '*',
+        ...options.headers,
+      }
+      if (token) {
+        headers.Authorization = `Bearer ${token}`
+      }
+      return Promise.resolve({
+        url,
+        options: {
+          ...options,
+          headers,
+        },
+      })
+    })
+  },
+  { global: true }
+)
+export default request

+ 2 - 1
app.json

@@ -29,6 +29,7 @@
     },
     "web": {
       "favicon": "./assets/images/favicon.png"
-    }
+    },
+    "description": "叮咚外卖用户端平台"
   }
 }

BIN
assets/images/loginImg.png


BIN
assets/images/loginLogo.png


BIN
assets/images/logo_2.png


+ 1 - 0
babel.config.js

@@ -9,6 +9,7 @@ module.exports = (api) => {
           libraryName: '@ant-design/react-native',
         },
       ],
+      'react-native-paper/babel',
     ],
   }
 }

+ 25 - 0
components/Header.js

@@ -0,0 +1,25 @@
+import * as React from 'react'
+import { StatusBar, Platform } from 'react-native'
+import Constants from 'expo-constants'
+import { Appbar } from 'react-native-paper'
+import { goBack } from '../navigation/RootNavigation'
+
+export default function Header() {
+  return (
+    <>
+      {Platform.OS !== 'ios' && <StatusBar backgroundColor="#FFC21C" />}
+
+      <Appbar.Header
+        dark
+        statusBarHeight={Platform.OS === 'ios' ? Constants.statusBarHeight : 0}
+      >
+        <Appbar.BackAction onPress={goBack} />
+        <Appbar.Content
+          title="忘记密码"
+          titleStyle={{ textAlign: 'center', fontSize: 16 }}
+        />
+        <Appbar.Action icon="dots-vertical" />
+      </Appbar.Header>
+    </>
+  )
+}

+ 33 - 0
components/HomeHeader.js

@@ -0,0 +1,33 @@
+import * as React from 'react'
+import { StatusBar, Platform } from 'react-native'
+import Constants from 'expo-constants'
+import { Appbar, Menu, Divider } from 'react-native-paper'
+import { useBoolean } from '@umijs/hooks'
+
+export default function Header() {
+  const { state, toggle } = useBoolean(false)
+
+  return (
+    <>
+      {Platform.OS !== 'ios' && <StatusBar backgroundColor="#fff" />}
+
+      <Appbar.Header
+        statusBarHeight={Platform.OS === 'ios' ? Constants.statusBarHeight : 0}
+        color="#fff"
+      >
+        <Appbar.Content title="同进大厦" titleStyle={{ fontSize: 16 }} />
+
+        <Menu
+          visible={state}
+          onDismiss={toggle}
+          anchor={<Appbar.Action icon="dots-vertical" />}
+        >
+          <Menu.Item onPress={() => {}} title="Item 1" />
+          <Menu.Item onPress={() => {}} title="Item 2" />
+          <Divider />
+          <Menu.Item onPress={() => {}} title="Item 3" />
+        </Menu>
+      </Appbar.Header>
+    </>
+  )
+}

+ 14 - 0
constants/Theme.js

@@ -0,0 +1,14 @@
+import { DefaultTheme } from 'react-native-paper'
+
+const theme = {
+  ...DefaultTheme,
+  colors: {
+    ...DefaultTheme.colors,
+    primary: '#FFC21C',
+    accent: '#03dac4',
+    error: '#B00020',
+    text: '#000',
+  },
+}
+
+export default theme

+ 35 - 0
flooks/Toast.js

@@ -0,0 +1,35 @@
+import { Toast, Portal } from '@ant-design/react-native'
+
+const myToast = (now) => ({
+  tKey: '',
+  loading() {
+    const { tKey } = now()
+    if (!tKey) {
+      const newKey = Toast.loading('Loading...', 0)
+      now({
+        tKey: newKey,
+      })
+    }
+  },
+  success(title) {
+    const { clearLoading } = now()
+    clearLoading()
+    Toast.success(title, 2)
+  },
+  warnning(title) {
+    const { clearLoading } = now()
+    clearLoading()
+    Toast.info(title, 2)
+  },
+  clearLoading() {
+    const { tKey } = now()
+    if (tKey) {
+      Portal.remove(tKey)
+      now({
+        tKey: '',
+      })
+    }
+  },
+})
+
+export default myToast

+ 56 - 0
flooks/User.js

@@ -0,0 +1,56 @@
+import request from '../Utils/RequestUtils'
+import { addAsyncStorage } from '../Utils/AsyncStorageUtils'
+import submitPhone from '../Utils/FormUtils'
+import Toast from './Toast'
+
+const app = (now) => ({
+  id: null,
+  userInfo: {},
+  getUser() {
+    return request
+      .get('/user/my')
+      .then((res) => {
+        console.log(res)
+        now({
+          id: res.id,
+          userInfo: res,
+        })
+        return Promise.resolve()
+      })
+      .catch((e) => {
+        now({
+          id: 0,
+        })
+        return Promise.reject(e)
+      })
+  },
+  loginByPsd(phone, password) {
+    const { loading, warnning, success } = now(Toast)
+    loading()
+    return request
+      .post('/auth/login', {
+        data: {
+          username: submitPhone(phone),
+          password,
+        },
+        requestType: 'form',
+      })
+      .then((res) => {
+        return addAsyncStorage('token', res)
+      })
+      .then(() => {
+        const { getUser } = now()
+        return getUser()
+      })
+      .then(() => {
+        const { id } = now()
+        console.log(id)
+        success('登录成功')
+      })
+      .catch((e) => {
+        warnning(e.error)
+      })
+  },
+})
+
+export default app

+ 21 - 0
flooks/Words.js

@@ -0,0 +1,21 @@
+import { i18n, keys } from '../language'
+
+const wordsModel = (now) => ({
+  local: '',
+  wordsInit() {
+    const { local } = now()
+    if (local) {
+      i18n.local = local
+    }
+    const words = {}
+    keys.forEach((item) => {
+      words[`T${item}`] = i18n.t(item)
+    })
+    now({ ...words })
+  },
+  setlocal(local) {
+    now({ local })
+  },
+})
+
+export default wordsModel

+ 2 - 0
hooks/useCachedResources.js

@@ -1,5 +1,6 @@
 /* eslint-disable global-require */
 import { Ionicons } from '@expo/vector-icons'
+import useModel from 'flooks'
 import * as Font from 'expo-font'
 import * as SplashScreen from 'expo-splash-screen'
 import * as React from 'react'
@@ -13,6 +14,7 @@ export default function useCachedResources() {
       try {
         SplashScreen.preventAutoHideAsync()
 
+
         // Load fonts
 
         await Font.loadAsync(

+ 17 - 0
language/index.js

@@ -0,0 +1,17 @@
+// I18n.js
+import * as Localization from 'expo-localization'
+import i18n from 'i18n-js'
+import th from './th'
+import zh from './zh'
+
+// 支持转换的语言
+i18n.translations = {
+  th,
+  zh,
+}
+i18n.locale = Localization.locale
+i18n.fallbacks = true
+
+const keys = Object.keys(zh)
+
+export { i18n, keys }

+ 38 - 0
language/th.js

@@ -0,0 +1,38 @@
+export default {
+  // 页面的文字
+
+  welcom: '欢迎使用叮咚外卖平台[泰文]',
+  login_form_1: '手机号',
+  login_pla_1: '输入商家手机号',
+  login_form_2: '密码',
+  login_pla_2: '输入密码',
+  login_btn_sub: '登录',
+  login_btn_rej: '注册',
+  register_form_1: '换成泰文换成泰文',
+  register_pla_1: '换成泰文换成泰文',
+  register_form_2: '换成泰文换成泰文',
+  register_pla_2: '换成泰文换成泰文',
+  register_form_3: '换成泰文换成泰文',
+  register_pla_3: '再次输入确认密码',
+
+  guideHome_title1: '很好!恭喜您已经完成了叮咚合作商家的第一步!',
+  guideHome_title2: '为了用户更好地认识您,请认真填写!',
+  guideHome_form_1: '所属品类',
+  guideHome_form_2: '商家地址',
+  guideHome_form_3: '商家联系电话',
+  guideHome_form_4: '营业时间',
+  guideHome_form_5: '营业资质',
+  guideHome_form_6: '商家性质',
+  guideHome_form_7: '上传logo',
+  guideHome_pla_1: '输入您的电话号码',
+  guideHome_pla_2: '(可后面再填)',
+  guideHome_pla_3: '(一旦选择不可更改)',
+
+  // 公共的文字
+
+  changeToEnglish: '切换到泰文',
+  changeToChinese: '切换到中文',
+  appName: '叮咚外卖',
+  connect: '联系客服',
+  next: '下一步',
+}

+ 271 - 0
language/zh.js

@@ -0,0 +1,271 @@
+export default {
+  // 页面的文字
+
+  welcom: '欢迎使用叮咚外卖平台',
+  login_form_1: '手机号',
+  login_pla_1: '输入商家手机号',
+  login_form_2: '密码',
+  login_pla_2: '输入密码',
+  login_form_3: '验证码',
+  login_pla_3: '输入验证码',
+  login_tab_1: '密码登录',
+  login_tab_2: '验证码登陆',
+  login_btn_code_1: '发送验证码',
+  login_btn_code_2: '已发送',
+  login_btn_sub: '登录',
+  login_btn_forget: '忘记密码',
+  login_btn_rej: '商户注册',
+  register_form_1: '商家名称',
+  register_pla_1: '输入商家名称',
+  register_form_2: '显示名称',
+  register_pla_2: '输入显示名称',
+  register_form_3: '确认密码',
+  register_pla_3: '再次输入确认密码',
+  register_form_4: '手机号',
+  register_pla_4: '输入手机号',
+
+  guideHome_title1: '很好!恭喜您已经完成了叮咚合作商家的第一步!',
+  guideHome_title2: '为了用户更好地认识您,请认真填写!',
+  guideHome_form_1: '商家品类',
+  guideHome_form_2: '商家地址',
+  guideHome_form_3: '商家电话',
+  guideHome_form_4: '营业时间',
+  guideHome_form_5: '营业资质',
+  guideHome_form_6: '商家性质',
+  guideHome_form_7: '上传logo',
+  guideHome_pla_1: '输入您的电话号码',
+  guideHome_pla_2: '(可后面再填)',
+  guideHome_pla_3: '(一旦选择不可更改)',
+  guide1_title1: '太好了!您已经在叮咚的外卖版图上站有一席之地了!',
+  guide1_form_1: '商品名称',
+  guide1_pla_1: '输入商品名称',
+  guide1_form_2: '商品价格',
+  guide1_pla_2: '输入商品价格',
+  guide1_form_3: '优惠价格',
+  guide1_pla_3: '输入优惠价格',
+  guide1_form_4: '每日供应数量',
+  guide1_pla_4: '输入每日供应数量',
+  guide1_form_5: '供应时间',
+  guide1_pla_5: '添加商品简介(不超过50字)',
+  guide2_title1: 'woo!您的商品看上去就会很畅销!您可以再软件内添加更多的商品。',
+  guide2_title2:
+    'woo!为了用户更好地认识您,请为这些商品建立分类。分类将在商品的左侧栏!',
+  guide2_form_1: '类别名称',
+  guide2_pla_1: '输入类别名称',
+  guide2_pla_2: '比如单人套餐、前菜、主食等等',
+  guide2_form_2: '显示排序',
+  guide2_form_3: '包含商品',
+  guide3_title1: 'woo!太好了!您已经在叮咚的外卖版图上站有一席之地了!',
+  guide3_title2: '为了用户更好地认识您,请认真填写!',
+  guide3_form_1: '支付宝账号',
+  guide3_pla_1: '输入支付宝账号',
+  guide3_form_2: '支付宝账户名',
+  guide3_pla_2: '输入支付宝账账户名',
+  guide4_title1: '完美!您已经完成了本向导!',
+  guide4_title2: '完整的信息您可以在后续陆续添加。祝您轻松赚钱,开心生活',
+
+  goodsClassificationTitle1: '分类下的商品',
+  goodsClassificationTitle2: '已选择的商品',
+  goodsClassificationTitle3: '未选择的商品',
+  systemClassification1:
+    '该分类为系统预制分类,主要用于提醒用户相关的信息,您可以放餐具以及点单必读等信息。',
+  systemClassification2:
+    '如果您没有放置点单必读商品,系统将不显示该分类。所以,如果暂时没有符合标准的产品,也建议您打开。',
+  systemClassification3:
+    '该分类为系统预制分类,自动罗列了商户中的畅销产品和好评度高的产品,系统会根据销售和评价动态更新。',
+  systemClassification4: '所以,如果暂时没有符合标准的产品,也建议您打开。',
+  systemClassification5:
+    '该分类为系统预制分类,自动罗列了已设置的折扣商品,如果没有折扣商品,系统将不显示该分类。',
+  systemClassification6: '您可以在“我的”->“我的优惠活动”中配置折扣商品。',
+  systemClassTips1: '确定要关闭该系统分类吗?',
+
+  storeAudio: '审核店铺',
+  storeAudioText1: '您的店铺正在审核中',
+  storeAudioText2: '请耐心等待...',
+  storeAudioText3: '恭喜!!!',
+  storeAudioText4: '您的店铺审核成功!',
+  addGoods: '编辑(新增)商品',
+  addGoods2: '新增商品',
+  takeOff: '下架',
+  takeUp: '上架',
+
+  homeBar: '我的店面',
+  homeTip1: '添加banner',
+  homeTitle1: '店主推荐',
+  homeTitle2: '编辑分类',
+  homeTip2: '月售',
+  homeTip3: '公告',
+  homeTitle3: '公告编辑',
+  homeTab1: '点餐',
+  homeTab2: '评价',
+  homeTab3: '商家',
+
+  home3Title1: '商家故事',
+  home3Title2: '文字介绍/品牌故事等等,最多500字',
+
+  bannerTitle: 'banner',
+  logoTitle: '商家头像',
+  qualificationTitle: '营业资质',
+  userTitle1: '基本信息',
+  userTitle2: '我的优惠',
+  userTitle3: '我的商品',
+  userTitle4: '订单',
+  userTitle5: '评价',
+  userTitle6: '我的叮咚币',
+  userTitle21: '满减',
+  userTitle22: '折扣商品',
+  userTitle23: '首单',
+  userTitle24: '优惠券管理',
+  userTitle31: '编辑商品',
+  userTitle32: '商品分类',
+  userTitle33: '招牌商品',
+  userTitle34: '我的推荐位',
+  userTitle41: '自动接单、订单语音设置',
+  userTitle42: '我的对账单',
+  userTitle61: '账单查询',
+  userTitle62: '我的银行卡',
+
+  orderTitleBtn1: '手动接单',
+  orderTitleBtn2: '编辑语音',
+
+  orderInfo1: '流水号',
+  orderInfo2: '订单时间',
+  orderInfo3: '联系客户',
+  orderInfo4: '详细地址',
+  orderInfo5: '订单商品',
+  orderInfo6: '备注',
+  orderButton1: '拒单',
+  orderButton2: '接单',
+  overTips: '确定要拒单吗?',
+  orderSuceess: '操作成功',
+
+  // 银行卡
+  bankMainTitle: '选择银行卡',
+  bindBankText1: '绑定银行卡',
+  bankTitle1: '银行',
+  bankTitle2: '卡号',
+  bankTitle3: '姓名',
+  bankTitle4: '身份证号',
+  bankPla1: '输入银行名称',
+  bankPla2: '输入银行卡号',
+  bankPla3: '输入银行名称',
+  bankPla4: '输入银行名称',
+  bankTitle5: '同意《叮咚外卖快捷支付服务协议》',
+  rechargeBtnText: '预计两小时内到账,确认提现',
+  // 设置
+
+  automaticText1: '自动评价回复',
+  automaticText2: '好评自动评价回复',
+  automaticText3: '差评自动评价回复',
+  automaticText4: '添加回复(不超过100字)',
+
+  distributionTitle: '配送设置',
+  distributionText1: '起送金额',
+  distributionText2: '每餐平均准备时间',
+  distributionText3: '输入金额',
+  distributionText4: '输入时间',
+  distributionText5: '温馨提示',
+  distributionText6:
+    '起送金额为店铺外送起送金额,每餐平均准备时间用于计算商品配送时间,请根据店铺实际餐品准备情况设置。',
+
+  // 我的分类
+  ClassificationManage: '我的分类',
+  ClassificationManageText1: '合理的设置分类具有如下好处',
+  ClassificationManageText2: '(1)可以让用户快速找到适合的商品',
+  ClassificationManageText3:
+    '(2)可以让用户知道商家对于某一样商品的看法和态度',
+  ClassificationManageText4: '同一种商品可以置于不同的分类下。',
+  ClassificationManageText5: '当前有的分类',
+  ClassificationManageText6: '该分类包含了以下商品',
+  // 公共的文字
+
+  saveSuccess: '保存成功',
+  editSuccess: '修改成功',
+  yuan: '元',
+  changeToEnglish: '切换到泰文',
+  changeToChinese: '切换到中文',
+  appName: '叮咚外卖',
+  connect: '联系客服',
+  next: '下一步',
+  pass: '跳过',
+  cancel: '取消',
+  save: '保存',
+  confirm: '确认',
+  MONDAY: '周一',
+  TUESDAY: '周二',
+  WEDNESDAY: '周三',
+  THURSDAY: '周四',
+  FRIDAY: '周五',
+  SATURDAY: '周六',
+  SUNDAY: '周日',
+  every: '每天',
+  start: '开始时间',
+  end: '结束时间',
+  hour: '时',
+  min: '分',
+  minutes: '分钟',
+  weekName: '周期',
+  tip: '提示',
+  passTips: '确认要跳过吗?',
+  add: '添加',
+  remove: '移除',
+  complete: '完成',
+  tab1: '店面',
+  tab2: '订单',
+  tab3: '我的',
+  editText: '编辑',
+  uplaodText: '上传',
+  uplaodImg: '上传图片',
+  removeTips: '确定要移除吗?移除后不可恢复',
+  takeOffTips: '确定要下架该商品吗?',
+  autoBackText: '自动回复',
+  editAutoBack: '编辑自动回复',
+  storeBackInfo: '商家回复',
+  delText: '删除',
+  successText: '成功',
+  addClassTips: '当前已经有两个推荐商品,请先移除后添加。',
+  nothingTips: '暂无数据',
+  fullReduction1: '满',
+  fullReduction2: '减',
+  loading: '加载中',
+  sendCode: '发送验证码',
+  applySuccess: '申请成功',
+  rechargeTime: '预计到账时间',
+  rechargeTitle2: '银行卡',
+  rechargeTitle3: '提现金额',
+
+  // 订单状态
+  NOT_RECEIVED: '未接单',
+  RECEIVED: '已接单',
+  REJECTED: '退单',
+  COMPLETED: '已完成',
+  UNPAID: '未支付',
+  PAID: '已支付',
+  RATED: '待评价',
+  CANCELLED: '已取消',
+  REFUNDED_PENDING: '申请退款中',
+  REFUNDED: '已退款',
+  ALI_PAY: '支付宝',
+  CASH_DELIVERY: '货到付款',
+  CREDIT_CARD: '信用卡',
+  Re_RECEIVED: '待取餐',
+  TAKE_MEAL: '已到店',
+  MEAL_DELIVERY: '正在配送中',
+  CARRY_OUT: '送达',
+
+  // 账单状态
+  BUY: '购买',
+  INCOME: '收入',
+  REFUND: '退款',
+  WITHDRAW: '提现',
+
+  // 提现状态
+  Apply_SUCCESS: '提现成功',
+  Apply_PENDING: '提现处理中',
+  Apply_FAIL: '提现失败',
+
+  // myRecord
+  selectTiltle1: '全部账单',
+  selectTiltle2: '收入账单',
+  selectTiltle3: '提现账单',
+}

+ 13 - 0
navigation/BaseNavigator.jsx

@@ -0,0 +1,13 @@
+import * as React from 'react'
+import Login from '../screens/Login'
+import InitAppScreen from '../screens/InitAppScreen'
+
+export default function BasicScreens(Screen) {
+  return (
+    <>
+      <Screen name="InitApp" component={InitAppScreen} />
+      {/* 登录路由 */}
+      {Login(Screen)}
+    </>
+  )
+}

+ 3 - 20
navigation/BottomTabNavigator.jsx

@@ -1,31 +1,14 @@
 import * as React from 'react'
 import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
 import TabBarIcon from '../components/TabBarIcon'
-import HomeScreen from '../screens/HomeScreen'
-import LinksScreen from '../screens/LinksScreen'
+import HomeScreen from '../screens/Home/HomeScreen'
+import LinksScreen from '../screens/Home/LinksScreen'
 
 const BottomTab = createBottomTabNavigator()
-const INITIAL_ROUTE_NAME = 'Home'
-function getHeaderTitle(route) {
-  const routeName =
-    route.state?.routes[route.state.index]?.name ?? INITIAL_ROUTE_NAME
-
-  switch (routeName) {
-    case 'Home':
-      return 'How to get started'
-    case 'Links':
-      return 'Links to learn more'
-  }
-}
 
 export default function BottomTabNavigator({ navigation, route }) {
-  // Set the header title on the parent stack navigator depending on the
-  // currently active tab. Learn more in the documentation:
-  // https://reactnavigation.org/docs/en/screen-options-resolution.html
-  navigation.setOptions({ headerTitle: getHeaderTitle(route) })
-
   return (
-    <BottomTab.Navigator initialRouteName={INITIAL_ROUTE_NAME}>
+    <BottomTab.Navigator initialRouteName="Home">
       <BottomTab.Screen
         name="Home"
         component={HomeScreen}

+ 3 - 1
navigation/RootNavigation.js

@@ -14,7 +14,9 @@ export function replace(name, params) {
 }
 
 export function goBack() {
-  navigationRef.current?.goBack()
+  if (navigationRef.current.getRootState().index > 0) {
+    navigationRef.current?.goBack()
+  }
 }
 
 export function reset(name, params) {

+ 168 - 0
package-lock.json

@@ -1153,6 +1153,25 @@
       "resolved": "https://registry.npmjs.org/@bang88/react-native-ultimate-listview/-/react-native-ultimate-listview-3.3.0.tgz",
       "integrity": "sha512-qRH8YV1Hi0dJpSQ9VCoWMVFuvxwZ4F5N73fqYMjSh0tGlG/p95WlaM/WVg9PFMphP5Sv5FO0p+vbBKWrbwdE0g=="
     },
+    "@callstack/react-theme-provider": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.5.tgz",
+      "integrity": "sha512-Iec+ybWN0FvNj87sD3oWo/49edGUP0UOSdMnzCJEFJIDYr992ECIuOV89burAAh2/ibPCxgLiK6dmgv2mO/8Tg==",
+      "requires": {
+        "deepmerge": "^3.2.0",
+        "hoist-non-react-statics": "^3.3.0"
+      },
+      "dependencies": {
+        "hoist-non-react-statics": {
+          "version": "3.3.2",
+          "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+          "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+          "requires": {
+            "react-is": "^16.7.0"
+          }
+        }
+      }
+    },
     "@cnakazawa/watch": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
@@ -1986,6 +2005,14 @@
         "use-subscription": "^1.4.0"
       }
     },
+    "@react-navigation/material-top-tabs": {
+      "version": "5.2.9",
+      "resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.2.9.tgz",
+      "integrity": "sha512-HtfXc3sVy7L4Z590whnro5eONXRW6ssqIsT+1WrixSbT7torS5nr9l/z1hIBhMF2VorDSkgLOch5LdrnpBLDkw==",
+      "requires": {
+        "color": "^3.1.2"
+      }
+    },
     "@react-navigation/native": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.5.0.tgz",
@@ -2220,6 +2247,28 @@
         }
       }
     },
+    "@umijs/hooks": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/@umijs/hooks/-/hooks-1.9.3.tgz",
+      "integrity": "sha512-h83Zk0x2oO8HeTZlSLsT4KVh+Me1VoXu+DHmT+cpqR7caBhii0T5c8Weko/pFBkEiB4QKzvWi7Zj+78VSKkI5w==",
+      "requires": {
+        "@umijs/use-request": "^1.4.3",
+        "intersection-observer": "^0.7.0",
+        "lodash.isequal": "^4.5.0",
+        "resize-observer-polyfill": "^1.5.1",
+        "screenfull": "^5.0.0"
+      }
+    },
+    "@umijs/use-request": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/@umijs/use-request/-/use-request-1.4.3.tgz",
+      "integrity": "sha512-aH4GCdRnMCaaciygdN0KtCDQdBBh1KyiNUAgYDPX8Y4brmbymEpJViX1FU4isOTbV34WlbkWTiBpR9HIi2ciNQ==",
+      "requires": {
+        "lodash.debounce": "^4.0.8",
+        "lodash.throttle": "^4.1.1",
+        "umi-request": "^1.2.17"
+      }
+    },
     "@unimodules/core": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/@unimodules/core/-/core-5.1.2.tgz",
@@ -2606,6 +2655,14 @@
       "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==",
       "dev": true
     },
+    "axios": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+      "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+      "requires": {
+        "follow-redirects": "1.5.10"
+      }
+    },
     "axobject-query": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz",
@@ -4670,6 +4727,14 @@
         "fontfaceobserver": "^2.1.0"
       }
     },
+    "expo-image-picker": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-8.1.0.tgz",
+      "integrity": "sha512-UMHiDGiflg47A/nfnmJ1gYdB/sEoxB4STVGgMeLNYpUkVR59kUWqsGTyVEtSywNCLPuXPZVB9NaRUd81Rbs7lA==",
+      "requires": {
+        "expo-permissions": "~8.1.0"
+      }
+    },
     "expo-keep-awake": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-8.1.0.tgz",
@@ -4690,6 +4755,14 @@
         "url-parse": "^1.4.4"
       }
     },
+    "expo-localization": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-8.1.0.tgz",
+      "integrity": "sha512-+wbsn9QLNG2GNOsbc+dfTQ39VEwrbn0TAungNqZ0UI9K2ZhrfQpAaAzAdN3TMOEhHluQKe0hiuULHpUgg8o7Kg==",
+      "requires": {
+        "rtl-detect": "^1.0.2"
+      }
+    },
     "expo-location": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-8.1.0.tgz",
@@ -5133,6 +5206,29 @@
         "@babel/runtime": "^7.9.6"
       }
     },
+    "follow-redirects": {
+      "version": "1.5.10",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+      "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+      "requires": {
+        "debug": "=3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
     "fontfaceobserver": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz",
@@ -5429,6 +5525,11 @@
       "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
       "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
     },
+    "i18n-js": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.7.0.tgz",
+      "integrity": "sha512-t5E5iiEqnkb3vy27V73dU3vcjDlmzbusKC5sXcGFOhte3b2BieVLu+qX1kRlyty9QfZADW3iZUSqeeUS9sal5w=="
+    },
     "iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -5604,6 +5705,11 @@
         "side-channel": "^1.0.2"
       }
     },
+    "intersection-observer": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz",
+      "integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg=="
+    },
     "invariant": {
       "version": "2.2.4",
       "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -7164,6 +7270,11 @@
       "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
       "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
     },
+    "lodash.isequal": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+      "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+    },
     "lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@@ -8074,6 +8185,14 @@
         "minimist": "^1.2.5"
       }
     },
+    "mockjs": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mockjs/-/mockjs-1.1.0.tgz",
+      "integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==",
+      "requires": {
+        "commander": "*"
+      }
+    },
     "moment": {
       "version": "2.26.0",
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
@@ -9512,6 +9631,26 @@
         "prop-types": "^15.6.2"
       }
     },
+    "react-native-paper": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-3.10.1.tgz",
+      "integrity": "sha512-rl1B9z/S3Q+D4lzTqQOVjpKTd7eLOUhxp7HRufsw8lq4hznAAckynelxOkHxZLHyWT1w9SX4Hdv654i4Uo1KEg==",
+      "requires": {
+        "@callstack/react-theme-provider": "^3.0.5",
+        "color": "^3.1.2",
+        "react-native-safe-area-view": "^0.14.9"
+      },
+      "dependencies": {
+        "react-native-safe-area-view": {
+          "version": "0.14.9",
+          "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.14.9.tgz",
+          "integrity": "sha512-WII/ulhpVyL/qbYb7vydq7dJAfZRBcEhg4/UWt6F6nAKpLa3gAceMOxBxI914ppwSP/TdUsandFy6lkJQE0z4A==",
+          "requires": {
+            "hoist-non-react-statics": "^2.3.1"
+          }
+        }
+      }
+    },
     "react-native-reanimated": {
       "version": "1.7.1",
       "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.7.1.tgz",
@@ -9570,6 +9709,11 @@
         "react-tween-state": "^0.1.5"
       }
     },
+    "react-native-tab-view": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-2.14.2.tgz",
+      "integrity": "sha512-BkQ9htmUM3iroMTgIdCSP+1On8bRHkgFHiOCAnk9OhoFlQOvwZebSU6k705QBDeuuwEVIIT4+C+nT2MvXI/FEQ=="
+    },
     "react-native-text-size": {
       "version": "4.0.0-rc.1",
       "resolved": "https://registry.npmjs.org/react-native-text-size/-/react-native-text-size-4.0.0-rc.1.tgz",
@@ -9889,6 +10033,11 @@
       "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz",
       "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc="
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
     "resolve": {
       "version": "1.17.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
@@ -9943,6 +10092,11 @@
       "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
       "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA=="
     },
+    "rtl-detect": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.2.tgz",
+      "integrity": "sha512-5X1422hvphzg2a/bo4tIDbjFjbJUOaPZwqE6dnyyxqwFqfR+tBcvfqapJr0o0VygATVCGKiODEewhZtKF+90AA=="
+    },
     "run-async": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@@ -10028,6 +10182,11 @@
         "ajv-keywords": "^3.4.1"
       }
     },
+    "screenfull": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz",
+      "integrity": "sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ=="
+    },
     "semver": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -11078,6 +11237,15 @@
       "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
       "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
     },
+    "umi-request": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/umi-request/-/umi-request-1.3.3.tgz",
+      "integrity": "sha512-TRf5x11OJ/9VBi2ZzCJGg/ZFs6KXxpZg1hV0/9oYa9d4YZtQ62Gk6djbonYoMFkyw7fMcxRh8ayPe4YG/gCNeg==",
+      "requires": {
+        "isomorphic-fetch": "^2.2.1",
+        "qs": "^6.9.1"
+      }
+    },
     "unicode-canonical-property-names-ecmascript": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",

+ 18 - 5
package.json

@@ -16,30 +16,43 @@
     "@babel/polyfill": "^7.10.1",
     "@expo/vector-icons": "^10.0.0",
     "@react-native-community/blur": "^3.6.0",
-    "@react-native-community/datetimepicker": "2.2.2",
+    "@react-native-community/datetimepicker": "^2.2.2",
     "@react-native-community/masked-view": "0.1.6",
-    "@react-native-community/netinfo": "^5.5.1",
+    "@react-native-community/netinfo": "5.5.1",
     "@react-navigation/bottom-tabs": "^5.3.1",
+    "@react-navigation/material-top-tabs": "^5.2.9",
     "@react-navigation/native": "^5.2.1",
     "@react-navigation/stack": "^5.2.16",
+    "@umijs/hooks": "^1.9.3",
+    "@umijs/use-request": "^1.4.3",
     "acorn": "^7.2.0",
+    "axios": "^0.19.2",
     "expo": "^37.0.0",
     "expo-asset": "~8.1.5",
     "expo-constants": "~9.0.0",
+    "expo-file-system": "~8.1.0",
     "expo-font": "~8.1.0",
+    "expo-image-picker": "~8.1.0",
     "expo-linking": "~1.0.1",
+    "expo-localization": "~8.1.0",
     "expo-splash-screen": "^0.2.3",
     "expo-web-browser": "~8.2.1",
     "flooks": "^3.0.0",
+    "i18n-js": "^3.7.0",
+    "mockjs": "^1.1.0",
+    "qs": "^6.9.4",
     "react": "16.9.0",
     "react-dom": "16.9.0",
     "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
-    "react-native-gesture-handler": "~1.6.0",
-    "react-native-reanimated": "~1.7.0",
+    "react-native-gesture-handler": "^1.6.1",
+    "react-native-paper": "^3.10.1",
+    "react-native-reanimated": "^1.7.1",
     "react-native-safe-area-context": "0.7.3",
     "react-native-screens": "~2.2.0",
+    "react-native-tab-view": "^2.14.2",
     "react-native-ui-lib": "^5.8.1",
-    "react-native-web": "^0.11.7"
+    "react-native-web": "^0.11.7",
+    "umi-request": "^1.3.3"
   },
   "devDependencies": {
     "@babel/cli": "^7.10.1",

+ 109 - 0
screens/Home/HomeScreen.js

@@ -0,0 +1,109 @@
+import * as WebBrowser from 'expo-web-browser'
+import * as React from 'react'
+import { Platform, StyleSheet } from 'react-native'
+import { ScrollView } from 'react-native-gesture-handler'
+import Header from '../../components/HomeHeader'
+
+export default function HomeScreen() {
+  return (
+    <ScrollView
+      style={styles.container}
+      contentContainerStyle={styles.contentContainer}
+    >
+      <Header />
+    </ScrollView>
+  )
+}
+
+HomeScreen.navigationOptions = {
+  header: null,
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: '#fff',
+  },
+  developmentModeText: {
+    marginBottom: 20,
+    color: 'rgba(0,0,0,0.4)',
+    fontSize: 14,
+    lineHeight: 19,
+    textAlign: 'center',
+  },
+  contentContainer: {
+    paddingTop: 30,
+  },
+  welcomeContainer: {
+    alignItems: 'center',
+    marginTop: 10,
+    marginBottom: 20,
+  },
+  welcomeImage: {
+    width: 100,
+    height: 80,
+    resizeMode: 'contain',
+    marginTop: 3,
+    marginLeft: -10,
+  },
+  getStartedContainer: {
+    alignItems: 'center',
+    marginHorizontal: 50,
+  },
+  homeScreenFilename: {
+    marginVertical: 7,
+  },
+  codeHighlightText: {
+    color: 'rgba(96,100,109, 0.8)',
+  },
+  codeHighlightContainer: {
+    backgroundColor: 'rgba(0,0,0,0.05)',
+    borderRadius: 3,
+    paddingHorizontal: 4,
+  },
+  getStartedText: {
+    fontSize: 17,
+    color: 'rgba(96,100,109, 1)',
+    lineHeight: 24,
+    textAlign: 'center',
+  },
+  tabBarInfoContainer: {
+    position: 'absolute',
+    bottom: 0,
+    left: 0,
+    right: 0,
+    ...Platform.select({
+      ios: {
+        shadowColor: 'black',
+        shadowOffset: { width: 0, height: -3 },
+        shadowOpacity: 0.1,
+        shadowRadius: 3,
+      },
+      android: {
+        elevation: 20,
+      },
+    }),
+    alignItems: 'center',
+    backgroundColor: '#fbfbfb',
+    paddingVertical: 20,
+  },
+  tabBarInfoText: {
+    fontSize: 17,
+    color: 'rgba(96,100,109, 1)',
+    textAlign: 'center',
+  },
+  navigationFilename: {
+    marginTop: 5,
+  },
+  helpContainer: {
+    marginTop: 15,
+    alignItems: 'center',
+  },
+  helpLink: {
+    paddingVertical: 15,
+  },
+  helpLinkText: {
+    fontSize: 14,
+    color: '#2e78b7',
+  },
+})

+ 9 - 0
screens/Home/LinksScreen.js

@@ -0,0 +1,9 @@
+import * as WebBrowser from 'expo-web-browser'
+import * as React from 'react'
+import { StyleSheet, View } from 'react-native'
+
+export default function LinksScreen() {
+  return <View />
+}
+
+const styles = StyleSheet.create({})

+ 0 - 204
screens/HomeScreen.js

@@ -1,204 +0,0 @@
-import * as WebBrowser from 'expo-web-browser'
-import * as React from 'react'
-import {
-  Image,
-  Platform,
-  StyleSheet,
-  Text,
-  TouchableOpacity,
-  View,
-} from 'react-native'
-import { ScrollView } from 'react-native-gesture-handler'
-
-import { MonoText } from '../components/StyledText'
-
-export default function HomeScreen() {
-  return (
-    <View style={styles.container}>
-      <ScrollView
-        style={styles.container}
-        contentContainerStyle={styles.contentContainer}
-      >
-        <View style={styles.welcomeContainer}>
-          <Image
-            source={
-              __DEV__
-                ? require('../assets/images/robot-dev.png')
-                : require('../assets/images/robot-prod.png')
-            }
-            style={styles.welcomeImage}
-          />
-        </View>
-
-        <View style={styles.getStartedContainer}>
-          <DevelopmentModeNotice />
-
-          <Text style={styles.getStartedText}>
-            Open up the code for this screen:
-          </Text>
-
-          <View
-            style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
-          >
-            <MonoText>screens/HomeScreen.js</MonoText>
-          </View>
-
-          <Text style={styles.getStartedText}>
-            Change any of the text, save the file, and your app will
-            automatically reload.
-          </Text>
-        </View>
-
-        <View style={styles.helpContainer}>
-          <TouchableOpacity onPress={handleHelpPress} style={styles.helpLink}>
-            <Text style={styles.helpLinkText}>
-              Help, it didn’t automatically reload!
-            </Text>
-          </TouchableOpacity>
-        </View>
-      </ScrollView>
-
-      <View style={styles.tabBarInfoContainer}>
-        <Text style={styles.tabBarInfoText}>
-          This is a tab bar. You can edit it in:
-        </Text>
-
-        <View
-          style={[styles.codeHighlightContainer, styles.navigationFilename]}
-        >
-          <MonoText style={styles.codeHighlightText}>
-            navigation/BottomTabNavigator.js
-          </MonoText>
-        </View>
-      </View>
-    </View>
-  )
-}
-
-HomeScreen.navigationOptions = {
-  header: null,
-}
-
-function DevelopmentModeNotice() {
-  if (__DEV__) {
-    const learnMoreButton = (
-      <Text onPress={handleLearnMorePress} style={styles.helpLinkText}>
-        Learn more
-      </Text>
-    )
-
-    return (
-      <Text style={styles.developmentModeText}>
-        Development mode is enabled: your app will be slower but you can use
-        useful development tools. {learnMoreButton}
-      </Text>
-    )
-  } else {
-    return (
-      <Text style={styles.developmentModeText}>
-        You are not in development mode: your app will run at full speed.
-      </Text>
-    )
-  }
-}
-
-function handleLearnMorePress() {
-  WebBrowser.openBrowserAsync(
-    'https://docs.expo.io/versions/latest/workflow/development-mode/'
-  )
-}
-
-function handleHelpPress() {
-  WebBrowser.openBrowserAsync(
-    'https://docs.expo.io/versions/latest/get-started/create-a-new-app/#making-your-first-change'
-  )
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    backgroundColor: '#fff',
-  },
-  developmentModeText: {
-    marginBottom: 20,
-    color: 'rgba(0,0,0,0.4)',
-    fontSize: 14,
-    lineHeight: 19,
-    textAlign: 'center',
-  },
-  contentContainer: {
-    paddingTop: 30,
-  },
-  welcomeContainer: {
-    alignItems: 'center',
-    marginTop: 10,
-    marginBottom: 20,
-  },
-  welcomeImage: {
-    width: 100,
-    height: 80,
-    resizeMode: 'contain',
-    marginTop: 3,
-    marginLeft: -10,
-  },
-  getStartedContainer: {
-    alignItems: 'center',
-    marginHorizontal: 50,
-  },
-  homeScreenFilename: {
-    marginVertical: 7,
-  },
-  codeHighlightText: {
-    color: 'rgba(96,100,109, 0.8)',
-  },
-  codeHighlightContainer: {
-    backgroundColor: 'rgba(0,0,0,0.05)',
-    borderRadius: 3,
-    paddingHorizontal: 4,
-  },
-  getStartedText: {
-    fontSize: 17,
-    color: 'rgba(96,100,109, 1)',
-    lineHeight: 24,
-    textAlign: 'center',
-  },
-  tabBarInfoContainer: {
-    position: 'absolute',
-    bottom: 0,
-    left: 0,
-    right: 0,
-    ...Platform.select({
-      ios: {
-        shadowColor: 'black',
-        shadowOffset: { width: 0, height: -3 },
-        shadowOpacity: 0.1,
-        shadowRadius: 3,
-      },
-      android: {
-        elevation: 20,
-      },
-    }),
-    alignItems: 'center',
-    backgroundColor: '#fbfbfb',
-    paddingVertical: 20,
-  },
-  tabBarInfoText: {
-    fontSize: 17,
-    color: 'rgba(96,100,109, 1)',
-    textAlign: 'center',
-  },
-  navigationFilename: {
-    marginTop: 5,
-  },
-  helpContainer: {
-    marginTop: 15,
-    alignItems: 'center',
-  },
-  helpLink: {
-    paddingVertical: 15,
-  },
-  helpLinkText: {
-    fontSize: 14,
-    color: '#2e78b7',
-  },
-})

+ 22 - 0
screens/InitAppScreen.jsx

@@ -0,0 +1,22 @@
+import * as WebBrowser from 'expo-web-browser'
+import * as React from 'react'
+import { Avatar } from 'react-native-paper'
+import { Flex } from '@ant-design/react-native'
+import useModel from 'flooks'
+import { useMount } from '@umijs/hooks'
+import user from '../flooks/User'
+
+const splash = require('../assets/images/splash.png')
+
+export default function LinksScreen() {
+  //   const { data, error, loading } = useRequest('/user/my')
+  const { getUser } = useModel(user, [])
+  useMount(() => {
+    getUser()
+  })
+  return (
+    <Flex justify="center" style={{ flex: 1, backgroundColor: '#FFC21C' }}>
+      <Avatar.Image size={80} source={splash} />
+    </Flex>
+  )
+}

+ 0 - 100
screens/LinksScreen.js

@@ -1,100 +0,0 @@
-import { Ionicons } from '@expo/vector-icons'
-
-import * as WebBrowser from 'expo-web-browser'
-
-import * as React from 'react'
-
-import { StyleSheet, Text, View } from 'react-native'
-
-import { RectButton, ScrollView } from 'react-native-gesture-handler'
-
-export default function LinksScreen() {
-  return (
-    <ScrollView
-      style={styles.container}
-      contentContainerStyle={styles.contentContainer}
-    >
-      <OptionButton
-        icon="md-school"
-        label="Read the Expo documentation"
-        onPress={() => WebBrowser.openBrowserAsync('https://docs.expo.io')}
-      />
-
-      <OptionButton
-        icon="md-compass"
-        label="Read the React Navigation documentation"
-        onPress={() =>
-          WebBrowser.openBrowserAsync('https://reactnavigation.org')
-        }
-      />
-
-      <OptionButton
-        icon="ios-chatboxes"
-        label="Ask a question on the forums"
-        onPress={() => WebBrowser.openBrowserAsync('https://forums.expo.io')}
-        isLastOption
-      />
-    </ScrollView>
-  )
-}
-
-function OptionButton({ icon, label, onPress, isLastOption }) {
-  return (
-    <RectButton
-      style={[styles.option, isLastOption && styles.lastOption]}
-      onPress={onPress}
-    >
-      <View style={{ flexDirection: 'row' }}>
-        <View style={styles.optionIconContainer}>
-          <Ionicons name={icon} size={22} color="rgba(0,0,0,0.35)" />
-        </View>
-
-        <View style={styles.optionTextContainer}>
-          <Text style={styles.optionText}>{label}</Text>
-        </View>
-      </View>
-    </RectButton>
-  )
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-
-    backgroundColor: '#fafafa',
-  },
-
-  contentContainer: {
-    paddingTop: 15,
-  },
-
-  optionIconContainer: {
-    marginRight: 12,
-  },
-
-  option: {
-    backgroundColor: '#fdfdfd',
-
-    paddingHorizontal: 15,
-
-    paddingVertical: 15,
-
-    borderWidth: StyleSheet.hairlineWidth,
-
-    borderBottomWidth: 0,
-
-    borderColor: '#ededed',
-  },
-
-  lastOption: {
-    borderBottomWidth: StyleSheet.hairlineWidth,
-  },
-
-  optionText: {
-    fontSize: 15,
-
-    alignSelf: 'flex-start',
-
-    marginTop: 1,
-  },
-})

+ 96 - 0
screens/Login/BackPasswordScreen.jsx

@@ -0,0 +1,96 @@
+import * as WebBrowser from 'expo-web-browser'
+import * as React from 'react'
+import { StyleSheet, View } from 'react-native'
+import { WingBlank, InputItem } from '@ant-design/react-native'
+import { Caption, Button, Paragraph } from 'react-native-paper'
+import Header from '../../components/Header'
+
+export default function BackPasswordScreen() {
+  const [phone, setphone] = React.useState()
+  const [code, setCode] = React.useState()
+  const [password, setPassword] = React.useState()
+  const [password2, setPassword2] = React.useState()
+
+  return (
+    <>
+      <Header />
+
+      <View style={styles.list}>
+        <WingBlank>
+          <View>
+            <InputItem
+              clear
+              type="phone"
+              value={phone}
+              onChange={setphone}
+              placeholder="输入手机号"
+              style={{ fontSize: 14 }}
+            >
+              <Paragraph>手机号</Paragraph>
+            </InputItem>
+            <InputItem
+              type="number"
+              value={code}
+              onChange={setCode}
+              extra={<Caption style={{ color: '#FFC21C' }}>发送验证码</Caption>}
+              placeholder="输入验证码"
+              maxLength={6}
+              style={{ fontSize: 14 }}
+              onExtraClick={() => {
+                console.log('发送验证码')
+              }}
+            >
+              <Paragraph>验证码</Paragraph>
+            </InputItem>
+            <InputItem
+              clear
+              type="password"
+              value={password}
+              onChange={setPassword}
+              placeholder="输入新密码"
+              style={{ fontSize: 14 }}
+            >
+              <Paragraph>新密码</Paragraph>
+            </InputItem>
+            <InputItem
+              clear
+              type="password"
+              value={password2}
+              onChange={setPassword2}
+              placeholder="再次输入密码"
+              style={{ fontSize: 14 }}
+            >
+              <Paragraph>确认密码</Paragraph>
+            </InputItem>
+
+            <View style={[styles.btn]}>
+              <Button
+                dark
+                mode="contained"
+                contentStyle={{ width: 120 }}
+                onPress={() => {}}
+              >
+                确定
+              </Button>
+            </View>
+          </View>
+        </WingBlank>
+      </View>
+    </>
+  )
+}
+
+const styles = StyleSheet.create({
+  list: {
+    backgroundColor: '#fff',
+    borderWidth: 0,
+    flex: 1,
+    paddingTop: 20,
+  },
+  btn: {
+    paddingLeft: 80,
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    marginTop: 40,
+  },
+})

+ 236 - 0
screens/Login/LoginScreen.jsx

@@ -0,0 +1,236 @@
+import * as WebBrowser from 'expo-web-browser'
+import * as React from 'react'
+import { StyleSheet, View, StatusBar, Platform, Image } from 'react-native'
+import { Flex, WingBlank, InputItem } from '@ant-design/react-native'
+import { Card, Paragraph, Button, Caption } from 'react-native-paper'
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'
+import { useEventEmitter } from '@umijs/hooks'
+import useModel from 'flooks'
+import Toast from '../../flooks/Toast'
+import user from '../../flooks/User'
+
+const Tab = createMaterialTopTabNavigator()
+
+// const img = require('../../assets/images/loginImg.png')
+const img2 = require('../../assets/images/loginLogo.png')
+
+export default function LoginScreen({ navigation }) {
+  const focus$ = useEventEmitter()
+  return (
+    <>
+      {Platform.OS !== 'ios' && <StatusBar backgroundColor="#FFF2C7" />}
+      <View style={styles.container}>
+        <WingBlank style={styles.container}>
+          <Flex
+            direction="column"
+            justify="center"
+            align="stretch"
+            style={styles.container}
+          >
+            <Card style={styles.center}>
+              <Image source={img2} style={styles.img2} resizeMode="contain" />
+              <View style={styles.tab}>
+                <Tab.Navigator
+                  tabBarOptions={{
+                    activeTintColor: '#FFC750',
+                    inactiveTintColor: '#000',
+                    indicatorStyle: {
+                      backgroundColor: '#FFC750',
+                      height: 0,
+                    },
+                    labelStyle: {
+                      fontSize: 16,
+                    },
+                    style: {
+                      backgroundColor: '#fff',
+                      height: 50,
+                      elevation: 0,
+                      shadowOffset: {
+                        width: 0,
+                        height: 0,
+                      },
+                      shadowOpacity: 0,
+                      shadowRadius: 0,
+                      borderBottomWidth: 1,
+                      borderColor: '#eee',
+                    },
+                  }}
+                >
+                  <Tab.Screen
+                    name="密码登陆"
+                    component={LoginPassword}
+                    initialParams={{ focus$ }}
+                  />
+                  <Tab.Screen
+                    name="验证码登陆"
+                    component={LoginCode}
+                    initialParams={{ focus$ }}
+                  />
+                </Tab.Navigator>
+              </View>
+              <View style={styles.btn}>
+                <Button
+                  mode="ghost"
+                  onPress={() => {
+                    navigation.navigate('BackPassword')
+                  }}
+                >
+                  <Caption style={{ color: '#B4B4B4' }}>忘记密码</Caption>
+                </Button>
+                <Button mode="ghost" onPress={() => {}}>
+                  <Caption style={{ color: '#B4B4B4' }}>用户注册</Caption>
+                </Button>
+              </View>
+              <View style={[styles.btn, { marginTop: 20 }]}>
+                <Button
+                  mode="contained"
+                  contentStyle={{ width: 120 }}
+                  onPress={() => {}}
+                >
+                  <Paragraph
+                    style={{ color: '#fff' }}
+                    onPress={() => {
+                      focus$.emit()
+                    }}
+                  >
+                    登录
+                  </Paragraph>
+                </Button>
+              </View>
+            </Card>
+          </Flex>
+        </WingBlank>
+      </View>
+    </>
+  )
+}
+
+const LoginPassword = ({ navigation, route }) => {
+  const [phone, setphone] = React.useState()
+  const [password, setPassword] = React.useState()
+
+  const { warnning } = useModel(Toast, [])
+  const { loginByPsd } = useModel(user, [])
+  const { params } = route
+  const { focus$ } = params || {}
+  focus$.useSubscription(() => {
+    if (navigation.isFocused()) {
+      if (!phone) {
+        warnning('手机号不能为空')
+      } else if (!password) {
+        warnning('密码不能为空')
+      } else {
+        loginByPsd(phone, password)
+      }
+    }
+  })
+
+  return (
+    <View style={styles.list}>
+      <InputItem
+        clear
+        type="phone"
+        value={phone}
+        onChange={setphone}
+        placeholder="输入手机号"
+        style={{ fontSize: 14 }}
+      >
+        <Paragraph>手机号</Paragraph>
+      </InputItem>
+      <InputItem
+        clear
+        type="password"
+        value={password}
+        onChange={setPassword}
+        placeholder="输入密码"
+        style={{ fontSize: 14 }}
+      >
+        <Paragraph>密码</Paragraph>
+      </InputItem>
+    </View>
+  )
+}
+
+const LoginCode = ({ navigation, route }) => {
+  const [phone, setphone] = React.useState()
+  const [code, setCode] = React.useState()
+  const { warnning } = useModel(Toast, [])
+  const { loginByPsd } = useModel(user, [])
+  const { params } = route
+  const { focus$ } = params || {}
+  focus$.useSubscription(() => {
+    if (navigation.isFocused()) {
+      if (!phone) {
+        warnning('手机号不能为空')
+      } else if (!code) {
+        warnning('验证码不能为空')
+      } else {
+        loginByPsd(phone, code)
+      }
+    }
+  })
+
+  return (
+    <View style={styles.list}>
+      <InputItem
+        clear
+        type="phone"
+        value={phone}
+        onChange={setphone}
+        style={{ fontSize: 14 }}
+        placeholder="输入手机号"
+      >
+        <Paragraph>手机号</Paragraph>
+      </InputItem>
+      <InputItem
+        clear
+        type="number"
+        value={code}
+        onChange={setCode}
+        extra={<Caption style={{ color: '#FFC21C' }}>发送验证码</Caption>}
+        placeholder="输入验证码"
+        maxLength={6}
+        style={{ fontSize: 14 }}
+        onExtraClick={() => {
+          console.log('发送验证码')
+        }}
+      >
+        <Paragraph>验证码</Paragraph>
+      </InputItem>
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  container: { flex: 1, backgroundColor: '#FFF2C7' },
+  center: {
+    paddingHorizontal: 10,
+    // paddingVertical: 20,
+    paddingTop: 20,
+    backgroundColor: '#fff',
+    borderRadius: 7,
+    flexDirection: 'column',
+    height: 380,
+  },
+  img2: {
+    height: 90,
+    backgroundColor: '#fff',
+    width: '100%',
+    // marginBottom: 20,
+  },
+  list: {
+    backgroundColor: '#fff',
+    borderWidth: 0,
+    paddingTop: 10,
+  },
+  tab: {
+    height: 150,
+    backgroundColor: '#fff',
+    paddingBottom: 10,
+  },
+  btn: {
+    paddingLeft: 80,
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+  },
+})

+ 13 - 0
screens/Login/index.js

@@ -0,0 +1,13 @@
+import * as WebBrowser from 'expo-web-browser'
+import * as React from 'react'
+import LoginScreen from './LoginScreen'
+import BackPasswordScreen from './BackPasswordScreen'
+
+export default function Bank(Screen) {
+  return (
+    <>
+      <Screen name="Login" component={LoginScreen} />
+      <Screen name="BackPassword" component={BackPasswordScreen} />
+    </>
+  )
+}