Jelajahi Sumber

我的对账单

panhui 5 tahun lalu
induk
melakukan
655b33289f

+ 9 - 3
app.json

@@ -1,6 +1,6 @@
 {
   "expo": {
-    "name": "DingdongRider",
+    "name": "叮咚外卖骑手端",
     "slug": "DingdongRider",
     "version": "1.0.0",
     "orientation": "portrait",
@@ -15,13 +15,19 @@
     "updates": {
       "fallbackToCacheTimeout": 0
     },
-    "assetBundlePatterns": ["**/*"],
+    "assetBundlePatterns": [
+      "**/*"
+    ],
     "ios": {
       "supportsTablet": true,
       "bundleIdentifier": "com.izouma.dingdongRider",
       "buildNumber": "1.0.0",
       "infoPlist": {
-        "UIBackgroundModes": ["audio", "location", "fetch"]
+        "UIBackgroundModes": [
+          "audio",
+          "location",
+          "fetch"
+        ]
       }
     },
     "android": {

TEMPAT SAMPAH
assets/images/logo2.png


TEMPAT SAMPAH
assets/images/next.png


+ 21 - 1
locales/zh-CN.json

@@ -89,5 +89,25 @@
   "jin-ri-zhi-chu": "今日支出",
   "wo-de-dui-zhang-dan": "我的对账单",
   "xuan-ze-yin-hang-ka": "选择银行卡",
-  "bang-ding-yin-hang-ka": "绑定银行卡"
+  "bang-ding-yin-hang-ka": "绑定银行卡",
+  "shen-qing-shi-jian": "申请时间",
+  "yin-hang-ka": "银行卡",
+  "ti-xian-jin-e": "提现金额",
+  "ti-xian-shi-bai": "提现失败",
+  "ti-xian-cheng-gong": "提现成功",
+  "ti-xian-chu-li-zhong": "提现处理中",
+  "ke-yong-yu-e": "可用余额",
+  "yuan": "元",
+  "yu-ji-liang-xiao-shi-nei-dao-zhang-que-ren-ti-xian": "预计两小时内到账,确认提现",
+  "nothing1": "当前还没有银行卡信息",
+  "qu-tian-jia": "去添加",
+  "yin-hang-ming-cheng": "银行名称",
+  "shu-ru-yin-hang-ming-cheng": "输入银行名称",
+  "ka-hao": "卡号",
+  "shu-ru-yin-hang-ka-hao": "输入银行卡号",
+  "xing-ming": "姓名",
+  "shu-ru-shen-fen-zheng-hao": "输入身份证号",
+  "ding-dong-wai-mai-kuai-jie-zhi-fu-fu-wu-xie-yi": "《叮咚外卖快捷支付服务协议》",
+  "ti-jiao-cheng-gong": "提交成功",
+  "quan-bu": "全部"
 }

+ 6 - 3
login/CertificationScreen.tsx

@@ -76,13 +76,16 @@ export default function CertificationScreen({ navigation }: StackScreenProps) {
                 flex={1}
                 bg="gray100"
                 rounded="sm"
-                loaderColor="gray400"
                 px={12}
                 ml={5}
                 h={30}
                 value={realName}
                 borderWidth={0}
                 fontSize="sm"
+                opacity={1}
+                loaderColor="gray400"
+                color="gray900"
+                opacity={1}
                 placeholder={t('shu-ru-xing-ming')}
                 onChangeText={(text) => {
                   setRealName(text);
@@ -109,14 +112,14 @@ export default function CertificationScreen({ navigation }: StackScreenProps) {
                 type={'custom'}
                 value={idNo}
                 keyboardType="numeric"
-                placeholderTextColor="#a6a6a6"
                 options={{
                   mask: '999999999999999999',
                 }}
                 onChangeText={(text) => {
                   setIdNo(text);
                 }}
-                style={{ flex: 1, fontSize: 12 }}
+                placeholderTextColor="#a6a6a6"
+                style={{ flex: 1, fontSize: 12, height: 31 }}
                 placeholder={t('shu-ru-zheng-jian-hao-ma')}
               />
             </Div>

+ 6 - 1
login/LoginScreen.tsx

@@ -117,6 +117,7 @@ function LoginByPsd() {
               setphone(text);
             }}
             style={{ flex: 1, fontSize: 14 }}
+            placeholderTextColor="#a6a6a6"
             placeholder={t('shu-ru-shang-jia-ming-cheng')}
           />
         </Div>
@@ -136,8 +137,10 @@ function LoginByPsd() {
           value={password}
           borderWidth={0}
           fontSize="md"
+          opacity={1}
+          loaderColor="gray400"
+          color="gray900"
           secureTextEntry
-          color="gray700"
           onChangeText={(text) => {
             console.log(text);
             setpassword(text);
@@ -213,6 +216,7 @@ function LoginByCode() {
               console.log(text);
               setphone(text);
             }}
+            placeholderTextColor="#a6a6a6"
             style={{ flex: 1, fontSize: 14 }}
             placeholder={t('shu-ru-shou-ji-hao')}
           />
@@ -242,6 +246,7 @@ function LoginByCode() {
               console.log(text);
               setCode(text);
             }}
+            placeholderTextColor="#a6a6a6"
             style={{ flex: 1, fontSize: 14 }}
             placeholder={t('shu-ru-yan-zheng-ma')}
           />

+ 7 - 4
login/RegisterScreen.tsx

@@ -99,7 +99,7 @@ export default function RegisterScreen({
             >
               <TextInputMask
                 type={'custom'}
-                loaderColor="gray400"
+                placeholderTextColor="#a6a6a6"
                 px={12}
                 value={phone}
                 keyboardType="numeric"
@@ -160,7 +160,9 @@ export default function RegisterScreen({
               borderWidth={0}
               fontSize="md"
               secureTextEntry
-              color="gray700"
+              opacity={1}
+              loaderColor="gray400"
+              color="gray900"
               onChangeText={(text) => {
                 setpassword(text);
               }}
@@ -176,7 +178,6 @@ export default function RegisterScreen({
               flex={1}
               bg="gray100"
               rounded="sm"
-              loaderColor="gray400"
               px={12}
               ml={5}
               h={30}
@@ -185,7 +186,9 @@ export default function RegisterScreen({
               borderWidth={0}
               fontSize="md"
               secureTextEntry
-              color="gray700"
+              opacity={1}
+              loaderColor="gray400"
+              color="gray900"
               onChangeText={(text) => {
                 setpassword2(text);
               }}

+ 4 - 0
login/TransportationScreen.tsx

@@ -137,6 +137,10 @@ export default function TransportationScreen({ navigation }: StackScreenProps) {
                       <Input
                         w={96}
                         h={25}
+                        opacity={1}
+                        loaderColor="gray400"
+                        color="gray900"
+                        fontSize="sm"
                         value={extra}
                         onChangeText={setExtra}
                         onFocus={() => {

+ 9 - 6
mine/ChangePasswordScreen.tsx

@@ -41,7 +41,6 @@ export default function ChangePasswordScreen({ navigation }: StackScreenProps) {
             <Input
               flex={1}
               rounded="sm"
-              loaderColor="gray400"
               px={12}
               ml={5}
               h={30}
@@ -50,7 +49,9 @@ export default function ChangePasswordScreen({ navigation }: StackScreenProps) {
               borderWidth={0}
               fontSize="md"
               secureTextEntry
-              color="gray700"
+              opacity={1}
+              loaderColor="gray400"
+              color="gray900"
               onChangeText={(text) => {
                 setPassword(text);
               }}
@@ -71,7 +72,6 @@ export default function ChangePasswordScreen({ navigation }: StackScreenProps) {
             <Input
               flex={1}
               rounded="sm"
-              loaderColor="gray400"
               px={12}
               ml={5}
               h={30}
@@ -80,7 +80,9 @@ export default function ChangePasswordScreen({ navigation }: StackScreenProps) {
               borderWidth={0}
               fontSize="md"
               secureTextEntry
-              color="gray700"
+              opacity={1}
+              loaderColor="gray400"
+              color="gray900"
               onChangeText={(text) => {
                 setPasswordNew1(text);
               }}
@@ -101,7 +103,6 @@ export default function ChangePasswordScreen({ navigation }: StackScreenProps) {
             <Input
               flex={1}
               rounded="sm"
-              loaderColor="gray400"
               px={12}
               ml={5}
               h={30}
@@ -110,7 +111,9 @@ export default function ChangePasswordScreen({ navigation }: StackScreenProps) {
               borderWidth={0}
               fontSize="md"
               secureTextEntry
-              color="gray700"
+              opacity={1}
+              loaderColor="gray400"
+              color="gray900"
               onChangeText={(text) => {
                 setPasswordNew2(text);
               }}

+ 3 - 0
modals/InputModalScreen.tsx

@@ -30,6 +30,9 @@ export default function InputModalScreen({
             autoFocus={true}
             defaultValue={defaultVal}
             mt={10}
+            opacity={1}
+            loaderColor="gray400"
+            color="gray900"
             onChangeText={(text) => setVal(text)}
           />
 

+ 17 - 3
navigation/MineStackNavigator.tsx

@@ -22,6 +22,8 @@ import MineWalletScreen from '../wallet/MineWalletScreen';
 import WithdrawApplyScreen from '../wallet/WithdrawApplyScreen';
 import BankCardScreen from '../wallet/BankCardScreen';
 import AddBankCardScreen from '../wallet/AddBankCardScreen';
+import WithdrawResultScreen from '../wallet/WithdrawResultScreen';
+import MineRecordScreen from '../wallet/MineRecordScreen';
 
 const MineStack = createStackNavigator<MineStackParamList>();
 export default function Navigation({
@@ -32,7 +34,7 @@ export default function Navigation({
   const { t } = useTranslation();
   return (
     <MineStack.Navigator
-      initialRouteName="AddBankCard"
+      initialRouteName="MineRecord"
       screenOptions={{
         cardOverlayEnabled: true,
         cardStyle: { backgroundColor: '#f2f2f2', flex: 1 },
@@ -71,18 +73,30 @@ export default function Navigation({
         component={WithdrawApplyScreen}
         options={{ title: t('ti-xian') }}
       />
-
       <MineStack.Screen
         name="BankCard"
         component={BankCardScreen}
         options={{ title: t('xuan-ze-yin-hang-ka') }}
       />
-
       <MineStack.Screen
         name="AddBankCard"
         component={AddBankCardScreen}
         options={{ title: t('bang-ding-yin-hang-ka') }}
       />
+      <MineStack.Screen
+        name="WithdrawResult"
+        component={WithdrawResultScreen}
+        options={{
+          title: '提现结果',
+        }}
+      />
+      <MineStack.Screen
+        name="MineRecord"
+        component={MineRecordScreen}
+        options={{
+					title: '我的对账单',
+        }}
+      />
     </MineStack.Navigator>
   );
 }

+ 33 - 0
package-lock.json

@@ -3511,6 +3511,15 @@
         "prop-types": "^15.6.1"
       }
     },
+    "@wwdrew/react-native-numeric-textinput": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@wwdrew/react-native-numeric-textinput/-/react-native-numeric-textinput-1.1.0.tgz",
+      "integrity": "sha512-snbDuKeO6wSFWhGUxxByayv04VSb2E9o+U0wFHBslCucvIhCqLA23wKzSqeaP4ljlShemVRfZHmoUaJOnEs2AA==",
+      "requires": {
+        "currency-codes": "^1.5.0",
+        "intl": "^1.2.5"
+      }
+    },
     "abab": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
@@ -4805,6 +4814,15 @@
       "integrity": "sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw==",
       "dev": true
     },
+    "currency-codes": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/currency-codes/-/currency-codes-1.5.1.tgz",
+      "integrity": "sha512-hqy8vtlIYKzO6pe2TE0V4/riZALIc7nhtE9cvxk5FDRCvfGplgzUvpTmZlMsyO+NeK5U41j+sQXJOo8l8v9kdg==",
+      "requires": {
+        "first-match": "~0.0.1",
+        "nub": "~0.0.0"
+      }
+    },
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -6320,6 +6338,11 @@
         "locate-path": "^2.0.0"
       }
     },
+    "first-match": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/first-match/-/first-match-0.0.1.tgz",
+      "integrity": "sha1-pg7GQnAPD0NyNOu37D84JHblQv0="
+    },
     "flat-cache": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@@ -6881,6 +6904,11 @@
       "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz",
       "integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg=="
     },
+    "intl": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz",
+      "integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94="
+    },
     "invariant": {
       "version": "2.2.4",
       "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -11824,6 +11852,11 @@
         "path-key": "^2.0.0"
       }
     },
+    "nub": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/nub/-/nub-0.0.0.tgz",
+      "integrity": "sha1-s2m9Mr3eZq9ZYFw7BSC8IZ3MwE8="
+    },
     "nullthrows": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",

+ 1 - 0
package.json

@@ -18,6 +18,7 @@
     "@react-navigation/material-top-tabs": "^5.2.13",
     "@react-navigation/native": "^5.6.1",
     "@react-navigation/stack": "^5.6.2",
+    "@wwdrew/react-native-numeric-textinput": "^1.1.0",
     "ahooks": "^2.2.0",
     "color": "^3.1.2",
     "core-js": "^3.6.5",

+ 25 - 1
utils/MoneyUtils.ts

@@ -1,9 +1,17 @@
 const FinancialType = new Map([
+  [
+    'all',
+    {
+      name: 'quan-bu',
+      icon: 'clipboard-list',
+    },
+  ],
   [
     'INCOME',
     {
       name: 'shou-ru',
       backMoney: (money = 0) => '+' + money.toFixed(2),
+      icon: 'donate',
     },
   ],
   [
@@ -11,8 +19,24 @@ const FinancialType = new Map([
     {
       name: 'zhi-chu',
       backMoney: (money = 0) => '-' + money.toFixed(2),
+      icon: 'donate',
     },
   ],
 ]);
 
-export { FinancialType };
+const applyMap = new Map([
+  [
+    'DENY',
+    { name: 'ti-xian-shi-bai', color: 'red500', icon: 'exclamationcircle' },
+  ],
+  [
+    'PASS',
+    { name: 'ti-xian-cheng-gong', color: 'yellow500', icon: 'checkcircle' },
+  ],
+  [
+    'PENDING',
+    { name: 'ti-xian-chu-li-zhong', color: 'gray100', icon: 'checkcircle' },
+  ],
+]);
+
+export { FinancialType, applyMap };

+ 2 - 0
utils/SystemUtils.ts

@@ -42,10 +42,12 @@ function toastHide() {
 }
 
 function toastSuccess(title) {
+  toastHide();
   if (Platform.OS === 'web') return;
   Toast.success(title);
 }
 function toastInfo(title) {
+  toastHide();
   if (Platform.OS === 'web') return;
   Toast.message(title);
 }

+ 27 - 1
utils/TimeUtils.ts

@@ -9,4 +9,30 @@ function today() {
         ' 00:00:00';
 }
 
-export { today };
+function getSearchDate(showDate) {
+  return (
+    showDate +
+    ' 00:00:00,' +
+    moment(showDate, 'yyyy-MM')
+      .add(1, 'months')
+      .add(-1, 'seconds')
+      .format('yyyy-MM-DD') +
+    ' 23:59:59'
+  );
+}
+
+class MonthDate {
+  constructor() {
+    var list = [];
+    for (let i = 0; i < 6; i++) {
+      list.push(
+        moment()
+          .add(0 - i, 'months')
+          .format('yyyy-MM')
+      );
+    }
+    this.showList = list;
+  }
+}
+
+export { today, MonthDate, getSearchDate };

+ 233 - 3
wallet/AddBankCardScreen.tsx

@@ -1,9 +1,57 @@
 import { StackScreenProps } from '@react-navigation/stack';
 import * as React from 'react';
-import { Div, Button, Image, Text, Avatar } from 'react-native-magnus';
+import {
+  Div,
+  Button,
+  Image,
+  Text,
+  Avatar,
+  Input,
+  Icon,
+} from 'react-native-magnus';
 import { ScrollView } from 'react-native-gesture-handler';
+import { TextInputMask } from 'react-native-masked-text';
+
+import { useTranslation } from 'react-i18next';
+import { useCreation, useRequest } from 'ahooks';
+import useModel from 'flooks';
+
+import User from '../stores/User';
+import request from '../utils/RequestUtils';
+import { toastShow, toastSuccess, toastInfo } from '../utils/SystemUtils';
+
+function saveBank(userId, realName, phone, idNo, bankName, cardNo) {
+  return request.post('/bankCard/save', {
+    data: {
+      userId,
+      realName,
+      phone,
+      idNo,
+      bankName,
+      cardNo,
+    },
+  });
+}
 
 export default function AddBankCardScreen({ navigation }: StackScreenProps) {
+  const { t } = useTranslation();
+  const { userInfo } = useModel(User, ['userInfo']);
+  const [bankName, setbankName] = React.useState<string>('');
+  const [cardNo, setcardNo] = React.useState<string>('');
+  const [idNo, setidNo] = React.useState<string>('');
+  const [phone, setphone] = React.useState<string>('');
+  const [realName, setrealName] = React.useState<string>('');
+  const [code, setCode] = React.useState<string>('');
+  const [sure, setsure] = React.useState<boolean>(false);
+
+  const canSub = useCreation(() => {
+    if (bankName && cardNo && idNo && phone && realName && code && sure) {
+      return true;
+    } else {
+      return false;
+    }
+  }, [bankName, cardNo, idNo, phone, realName, code, sure]);
+
   return (
     <Div bg="gray100">
       <ScrollView
@@ -12,9 +60,191 @@ export default function AddBankCardScreen({ navigation }: StackScreenProps) {
           backgroundColor: '#f2f2f2',
         }}
       >
-        <Div>
-          <Text></Text>
+        <Div bg="white" px={12} mt={7}>
+          <Div
+            row
+            alignItems="center"
+            borderBottomColor="gray100"
+            borderBottomWidth={1}
+          >
+            <Text fontSize="sm" w={75}>
+              {t('yin-hang-ming-cheng')}
+            </Text>
+            <Input
+              flex={1}
+              value={bankName}
+              borderWidth={0}
+              fontSize="sm"
+              px={0}
+              loaderColor="gray400"
+              color="gray900"
+              opacity={1}
+              h={31}
+              placeholder={t('shu-ru-yin-hang-ming-cheng')}
+              onChangeText={(val) => setbankName(val)}
+            />
+          </Div>
+          <Div
+            row
+            alignItems="center"
+            borderBottomColor="gray100"
+            borderBottomWidth={1}
+          >
+            <Text fontSize="sm" w={75}>
+              {t('ka-hao')}
+            </Text>
+            <TextInputMask
+              type={'custom'}
+              value={cardNo}
+              keyboardType="numeric"
+              options={{
+                mask: '9999 9999 9999 9999 999',
+              }}
+              onChangeText={(text) => {
+                setcardNo(text);
+              }}
+              placeholderTextColor="#a6a6a6"
+              style={{ flex: 1, fontSize: 12, height: 31 }}
+              placeholder={t('shu-ru-yin-hang-ka-hao')}
+            />
+          </Div>
+          <Div
+            row
+            alignItems="center"
+            borderBottomColor="gray100"
+            borderBottomWidth={1}
+          >
+            <Text fontSize="sm" w={75}>
+              {t('xing-ming')}
+            </Text>
+            <Input
+              flex={1}
+              value={realName}
+              borderWidth={0}
+              fontSize="sm"
+              px={0}
+              h={31}
+              loaderColor="gray400"
+              color="gray900"
+              opacity={1}
+              placeholder="输入真实姓名"
+              onChangeText={(val) => setrealName(val)}
+            />
+          </Div>
+          <Div
+            row
+            alignItems="center"
+            borderBottomColor="gray100"
+            borderBottomWidth={1}
+          >
+            <Text fontSize="sm" w={75}>
+              {t('shen-fen-zheng-hao')}
+            </Text>
+            <TextInputMask
+              type={'custom'}
+              value={idNo}
+              keyboardType="email-address"
+              options={{
+                mask: '99999999999999999S',
+              }}
+              onChangeText={(text) => {
+                setidNo(text);
+              }}
+              placeholderTextColor="#a6a6a6"
+              style={{ flex: 1, fontSize: 12, height: 31 }}
+              placeholder={t('shu-ru-shen-fen-zheng-hao')}
+            />
+          </Div>
+          <Div
+            row
+            alignItems="center"
+            borderBottomColor="gray100"
+            borderBottomWidth={1}
+          >
+            <Text fontSize="sm" w={75}>
+              {t('shou-ji-hao')}
+            </Text>
+            <TextInputMask
+              type={'custom'}
+              value={phone}
+              keyboardType="numeric"
+              options={{
+                mask: '999 9999 9999',
+              }}
+              onChangeText={(text) => {
+                setphone(text);
+              }}
+              placeholderTextColor="#a6a6a6"
+              style={{ flex: 1, fontSize: 12, height: 31 }}
+              placeholder={t('shu-ru-shou-ji-hao')}
+            />
+          </Div>
+          <Div row alignItems="center">
+            <Text fontSize="sm" w={75}>
+              {t('yan-zheng-ma')}
+            </Text>
+            <TextInputMask
+              type={'custom'}
+              value={code}
+              keyboardType="numeric"
+              options={{
+                mask: '999999',
+              }}
+              onChangeText={(text) => {
+                setCode(text);
+              }}
+              placeholderTextColor="#a6a6a6"
+              style={{ flex: 1, fontSize: 12, height: 31 }}
+              placeholder={t('shu-ru-yan-zheng-ma')}
+            />
+          </Div>
         </Div>
+
+        <Button
+          bg="transparent"
+          p={0}
+          block
+          mt={10}
+          onPress={() => setsure(!sure)}
+        >
+          <Div row alignItems="center" flex={1} justifyContent="center">
+            <Icon
+              fontFamily="Ionicons"
+              ml={20}
+              color={sure ? 'yellow500' : 'gray400'}
+              name={sure ? 'ios-radio-button-on' : 'ios-radio-button-off'}
+            />
+            <Div row flexWrap="wrap" ml={15} flex={1}>
+              <Text color="gray500" fontSize="xs">
+                {t('yi-jing-yue-du-bing-tong-yi')}
+              </Text>
+              <Button bg="none" color="yellow500" fontSize="sm" p={0}>
+                {t('ding-dong-wai-mai-kuai-jie-zhi-fu-fu-wu-xie-yi')}
+              </Button>
+            </Div>
+          </Div>
+        </Button>
+
+        <Button
+          block
+          bg="yellow500"
+          m={15}
+          fontSize="sm"
+          disabled={!canSub}
+          onPress={() => {
+            toastShow();
+            saveBank(userInfo.id, realName, phone, idNo, bankName, cardNo)
+              .then((res) => {
+                toastSuccess(t('ti-jiao-cheng-gong'));
+                navigation.goBack();
+              })
+              .catch((e) => {
+                toastInfo(e.error);
+              });
+          }}
+        >
+          下一步
+        </Button>
       </ScrollView>
     </Div>
   );

+ 24 - 7
wallet/BankCardScreen.tsx

@@ -3,8 +3,14 @@ import * as React from 'react';
 import { Div, Button, Image, Text, Avatar, Icon } from 'react-native-magnus';
 import { ScrollView } from 'react-native-gesture-handler';
 import { useRequest } from 'ahooks';
+import useModel from 'flooks';
+
+import Wallet from './model';
+import BankCom from './BankCom';
+import { useTranslation } from 'react-i18next';
 
 export default function BankCardScreen({ navigation }: StackScreenProps) {
+  const { t } = useTranslation();
   const { data, loading } = useRequest('/bankCard/my', {
     formatResult: (response) => response.content,
     initialData: [],
@@ -23,6 +29,10 @@ export default function BankCardScreen({ navigation }: StackScreenProps) {
     ),
   });
 
+  const { chooseCardId, changeChooseId, setChooseInfo } = useModel(Wallet, [
+    'chooseCardId',
+  ]);
+
   return (
     <Div bg="gray100">
       <ScrollView
@@ -32,13 +42,24 @@ export default function BankCardScreen({ navigation }: StackScreenProps) {
         }}
       >
         {data.map((item) => {
-          return <bankCom key={item.id} info={item} />;
+          return (
+            <BankCom
+              key={item.id}
+              info={item}
+              checked={chooseCardId === item.id}
+              onPress={() => {
+                changeChooseId(item.id);
+                setChooseInfo(item);
+                navigation.navigate('WithdrawApply');
+              }}
+            />
+          );
         })}
 
         {data.length === 0 && !loading && (
           <Div>
             <Text py={30} textAlign="center">
-              当前还没有银行信息
+              {t('nothing1')}
             </Text>
 
             <Button
@@ -46,7 +67,7 @@ export default function BankCardScreen({ navigation }: StackScreenProps) {
               bg="yellow500"
               onPress={() => navigation.navigate('AddBankCard')}
             >
-              去添加
+              {t('qu-tian-jia')}
             </Button>
           </Div>
         )}
@@ -54,7 +75,3 @@ export default function BankCardScreen({ navigation }: StackScreenProps) {
     </Div>
   );
 }
-
-function bankCom(params: type) {
-  return <Div p={15} bg="white" />;
-}

+ 31 - 0
wallet/BankCom.tsx

@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { Div, Button, Image, Text, Avatar, Icon } from 'react-native-magnus';
+
+export default function BankCom({
+  info,
+  checked = false,
+  onPress,
+  next = false,
+}) {
+  const { bankName, cardNo } = info;
+  return (
+    <Button p={12} bg="white" mt={5} block rounded="none" onPress={onPress}>
+      <Div flex={1} row>
+        <Icon
+          w={33}
+          h={33}
+          name="creditcard"
+          bg="yellow500"
+          color="white"
+          rounded="circle"
+        />
+        <Div flex={1} ml={5}>
+          <Text fontSize="sm">{bankName}</Text>
+          <Text fontSize="xs">{cardNo}</Text>
+        </Div>
+        {checked && <Icon name="checkcircle" color="yellow500" />}
+        {next && <Icon name="right" color="gray500" />}
+      </Div>
+    </Button>
+  );
+}

+ 219 - 0
wallet/MineRecordScreen.tsx

@@ -0,0 +1,219 @@
+import { StackScreenProps } from '@react-navigation/stack';
+import * as React from 'react';
+import * as Animatable from 'react-native-animatable';
+import { Div, Button, Image, Text, Avatar, Icon } from 'react-native-magnus';
+import { FlatList } from 'react-native-gesture-handler';
+import { RefreshControl } from 'react-native';
+
+import { useTranslation } from 'react-i18next';
+import { useRequest } from 'ahooks';
+
+import { FinancialType } from '../utils/MoneyUtils';
+
+import { MonthDate, getSearchDate } from '../utils/TimeUtils';
+import request from '../utils/RequestUtils';
+import ReacordCom from './ReacordCom';
+
+import { PopoverPicker } from 'teaset';
+
+interface Result {
+  list: Item[];
+  last: boolean;
+  current: number;
+}
+
+export default function MineRecordScreen({ navigation }: StackScreenProps) {
+  const { t } = useTranslation();
+  const [type, settype] = React.useState<string>('all');
+
+  const [show, setshow] = React.useState<boolean>(false);
+
+  const monthDate = new MonthDate();
+  const [showDate, setshowDate] = React.useState<string>(monthDate.showList[0]);
+  const [layout, setLayout] = React.useState<any>();
+  const { data, loading, loadMore, loadingMore, reload } = useRequest(
+    (d: Result | undefined) => {
+      let current = 0;
+      if (d && d.current) {
+        current = Number(d.current) + 1;
+      }
+      return request.get(__DEV__ ? '/moneyRecord/all' : '/moneyRecord/my', {
+        params: {
+          query: {
+            time: getSearchDate(showDate),
+            type: type != 'all' ? type : '',
+          },
+          size: 10,
+          page: current,
+        },
+      });
+    },
+    {
+      refreshDeps: [showDate, type],
+      formatResult: (response) => {
+        return {
+          list: response.content,
+          current: response.number.toString(),
+          last: response.last,
+          empty: response.empty,
+        };
+      },
+      isNoMore: (r: Result) => {
+        if (r.last) {
+          return true;
+        } else {
+          return false;
+        }
+      },
+      defaultLoading: true,
+      debounceInterval: 1000,
+      loadMore: true,
+    }
+  );
+
+  navigation.setOptions({
+    headerTitle: () => {
+      let name = '我的对账单';
+      if (type !== 'all') {
+        name += '(' + t(FinancialType.get(type).name) + ')';
+      }
+      return (
+        <Button bg="yellow500" onPress={() => setshow(!show)}>
+          {name}
+        </Button>
+      );
+    },
+  });
+
+  return (
+    <Div bg="gray100" flex={1}>
+      {show && (
+        <Div
+          position="absolute"
+          bg="black600"
+          top={0}
+          left={0}
+          right={0}
+          bottom={0}
+          zIndex={2}
+        >
+          <Animatable.View animation="slideInDown" duration={300}>
+            <Div bg="white">
+              {[...FinancialType.keys()].map((item, index) => {
+                const info = FinancialType.get(item);
+                return (
+                  <Button
+                    bg="white"
+                    block
+                    key={index}
+                    onPress={() => {
+                      settype(item);
+                      setshow(false);
+                    }}
+                  >
+                    <Div row flex={1} alignItems="center">
+                      <Icon
+                        name={info.icon}
+                        fontSize="6xl"
+                        fontFamily="FontAwesome5"
+                        color={type == item ? 'yellow500' : 'gray200'}
+                      />
+                      <Text fontSize="lg" ml={10} flex={1}>
+                        {t(info.name)}
+                      </Text>
+                      {type == item && (
+                        <Div p={2} rounded="circle" bg="yellow500">
+                          <Icon
+                            name="check"
+                            fontSize="md"
+                            color="white"
+                            fontFamily="FontAwesome5"
+                          />
+                        </Div>
+                      )}
+                    </Div>
+                  </Button>
+                );
+              })}
+            </Div>
+          </Animatable.View>
+        </Div>
+      )}
+
+      <FlatList
+        refreshControl={
+          <RefreshControl refreshing={loading} onRefresh={reload} />
+        }
+        onEndReached={() => {
+          if (!data.last && !loading && !loadingMore) {
+            loadMore();
+          }
+        }}
+        onEndReachedThreshold={0.5}
+        contentContainerStyle={{
+          flexGrow: 1,
+          backgroundColor: '#f2f2f2',
+        }}
+        data={data.list}
+        renderItem={({ item }) => {
+          return (
+            <Div px={15} bg="white">
+              <ReacordCom info={item} />
+            </Div>
+          );
+        }}
+        keyExtractor={(item) => item.id.toString()}
+        onLayout={(e) => {
+          console.log(e);
+        }}
+        ListFooterComponent={() => {
+          return (
+            <Text p={15} textAlign="center">
+              {data.last ? '到底了' : '加载中...'}
+            </Text>
+          );
+        }}
+        stickyHeaderIndices={[0]}
+        ListEmptyComponent={() => {
+          return (
+            <>
+              {data.empty && (
+                <Text textAlign="center" p={15}>
+                  无数据
+                </Text>
+              )}
+            </>
+          );
+        }}
+        ListHeaderComponent={() => {
+          return (
+            <Div px={15} py={10} bg="gray100">
+              <Button
+                bg="gray100"
+                borderWidth={1}
+                borderColor="yellow500"
+                color="yellow500"
+                fontSize="sm"
+                onPress={(e) => {
+                  const { nativeEvent } = e;
+                  PopoverPicker.show(
+                    {
+                      x: nativeEvent.pageX,
+                      y: nativeEvent.pageY,
+                      width: 120,
+                    },
+                    monthDate.showList,
+                    showDate,
+                    (item, index) => setshowDate(item)
+                  );
+                }}
+              >
+                {showDate}
+              </Button>
+            </Div>
+          );
+        }}
+      />
+    </Div>
+  );
+}

+ 3 - 2
wallet/ReacordCom.tsx

@@ -7,7 +7,6 @@ import { FinancialType } from '../utils/MoneyUtils';
 export default function ReacordCom({ info }) {
   const { t } = useTranslation();
   const { name, amount, avatar, num, type, time } = info;
-
   const typeInfo = FinancialType.get(type);
 
   return (
@@ -35,7 +34,9 @@ export default function ReacordCom({ info }) {
       <Div flex={1} ml={5}>
         <Div row>
           <Text flex={1}>{name}</Text>
-          <Text>{typeInfo.backMoney(amount)}</Text>
+          <Text>
+            {typeInfo.backMoney ? typeInfo.backMoney(amount) : amount}
+          </Text>
         </Div>
         <Text>{time}</Text>
         <Text>{t(typeInfo.name)}</Text>

+ 112 - 5
wallet/WithdrawApplyScreen.tsx

@@ -1,22 +1,129 @@
 import { StackScreenProps } from '@react-navigation/stack';
 import * as React from 'react';
-import { Div, Button, Image, Text, Avatar } from 'react-native-magnus';
+import { Div, Button, Image, Text, Avatar, Input } from 'react-native-magnus';
 import { ScrollView } from 'react-native-gesture-handler';
 
-import { RootStackParamList } from '../types';
+import useModel from 'flooks';
+import { useCreation } from 'ahooks';
+
+import Wallet from './model';
+import User from '../stores/User';
+import BankCom from './BankCom';
+
+import { getMoney } from '../utils/SystemUtils';
+import request from '../utils/RequestUtils';
+import { toastShow, toastSuccess, toastInfo } from '../utils/SystemUtils';
+import { useTranslation } from 'react-i18next';
+
+function saveApply(amount, bankCardId) {
+  return request.post('/withdrawApply/apply', {
+    data: {
+      amount,
+      bankCardId,
+    },
+    requestType: 'form',
+  });
+}
 
 export default function WithdrawApplyScreen({ navigation }: StackScreenProps) {
+  const { t } = useTranslation();
+  const { chooseCardId, chooseInfo } = useModel(Wallet, [
+    'chooseInfo',
+    'chooseCardId',
+  ]);
+
+  const [money, setmoney] = React.useState<string>('');
+  const { userInfo } = useModel(User, ['userInfo']);
+
+  const error = useCreation(() => {
+    if (Number(money) > Number(userInfo.money)) {
+      return true;
+    } else {
+      return false;
+    }
+  }, [money, userInfo]);
+
+  const canNext = useCreation(() => {
+    if (chooseCardId && Number(money) && !error) {
+      return true;
+    } else {
+      return false;
+    }
+  }, [money, error, chooseCardId]);
+
   return (
-    <Div bg="gray100">
+    <Div bg="gray100" flex={1}>
       <ScrollView
         contentContainerStyle={{
           flexGrow: 1,
           backgroundColor: '#f2f2f2',
         }}
       >
-        <Div>
-          <Text></Text>
+        <BankCom
+          info={chooseInfo}
+          next
+          onPress={() => navigation.navigate('BankCard')}
+        />
+
+        <Div bg="white" p={14} mt={10}>
+          <Text color="gray500" fontSize="sm">
+            {t('ti-xian-jin-e')}
+          </Text>
+          <Div
+            row
+            alignItems="center"
+            borderBottomColor={error ? 'red300' : 'gray100'}
+            borderBottomWidth={1}
+          >
+            <Text fontSize="6xl" color={error ? 'red300' : 'gray500'}>
+              ¥
+            </Text>
+            <Input
+              flex={1}
+              autoFocus={true}
+              keyboardType="numeric"
+              value={money}
+              ml={5}
+              borderWidth={0}
+              fontSize="6xl"
+              px={0}
+              loaderColor="gray400"
+              color={error ? 'red500' : 'gray900'}
+              opacity={1}
+              onChangeText={(val) => {
+                setmoney(val);
+              }}
+              onBlur={() => {
+                setmoney(Number(money).toFixed(2));
+              }}
+            />
+          </Div>
+          <Text fontSize="xs" color={error ? 'red500' : 'gary500'} mt={6}>
+            {t('ke-yong-yu-e')}
+            {getMoney(userInfo.money)}
+            {t('yuan')}
+          </Text>
         </Div>
+
+        <Button
+          bg="yellow500"
+          block
+          m={15}
+          disabled={!canNext}
+          onPress={() => {
+            toastShow();
+            saveApply(Number(money), chooseCardId)
+              .then((res) => {
+                toastSuccess(t('ti-jiao-cheng-gong'));
+                navigation.navigate('WithdrawResult', { id: res.id });
+              })
+              .catch((e) => {
+                toastInfo(e.error);
+              });
+          }}
+        >
+          {t('yu-ji-liang-xiao-shi-nei-dao-zhang-que-ren-ti-xian')}
+        </Button>
       </ScrollView>
     </Div>
   );

+ 90 - 0
wallet/WithdrawResultScreen.tsx

@@ -0,0 +1,90 @@
+import { StackScreenProps } from '@react-navigation/stack';
+import * as React from 'react';
+import { RefreshControl } from 'react-native';
+import { Div, Button, Image, Text, Avatar, Icon } from 'react-native-magnus';
+import { ScrollView } from 'react-native-gesture-handler';
+import { useRequest, useCreation } from 'ahooks';
+
+import { RootStackParamList } from '../types';
+
+import { applyMap } from '../utils/MoneyUtils';
+import { getMoney } from '../utils/SystemUtils';
+import { useTranslation } from 'react-i18next';
+
+const logo = require('../assets/images/logo2.png');
+const next = require('../assets/images/next.png');
+export default function WithdrawResultScreen({
+  navigation,
+  route,
+}: StackScreenProps) {
+  const { t } = useTranslation();
+  const { params } = route;
+  const { id } = params || {};
+  const { data, loading, refresh } = useRequest('/withdrawApply/get/' + id, {
+    defaultLoading: true,
+  });
+
+  const statusInfo = useCreation(() => {
+    if (data) {
+      return applyMap.get(data.status);
+    } else {
+      return {};
+    }
+  }, [data]);
+
+  const bankCard = useCreation(() => {
+    if (data) {
+      return data.bankCard || {};
+    } else {
+      return {};
+    }
+  }, [data]);
+
+  return (
+    <Div bg="gray100" flex={1}>
+      <ScrollView
+        contentContainerStyle={{
+          flexGrow: 1,
+          backgroundColor: '#f2f2f2',
+        }}
+        refreshControl={
+          <RefreshControl refreshing={loading} onRefresh={refresh} />
+        }
+      >
+        <Div row bg="white" alignItems="center" pl={31} pr={52} pt={10}>
+          <Image source={logo} w={66} h={66} />
+          <Div flex={1} h={2} bg="yellow500" />
+          <Image source={next} w={33} h={33} />
+          <Div flex={1} h={2} bg={statusInfo.color} />
+          <Icon
+            name={statusInfo.icon}
+            color={statusInfo.color}
+            fontSize="6xl"
+          />
+        </Div>
+        <Text bg="white" textAlign="center" py={10}>
+          {t(statusInfo.name)}
+        </Text>
+
+        {!!data && (
+          <Div p={15} bg="white" mt={10}>
+            <Div row py={2} justifyContent="space-between">
+              <Text>{t('shen-qing-shi-jian')}</Text>
+              <Text>{data.withdrawTime}</Text>
+            </Div>
+            <Div row py={2} justifyContent="space-between">
+              <Text>{t('yin-hang-ka')}</Text>
+              <Text>
+                {bankCard.bankName} ({bankCard.cardNo})
+              </Text>
+            </Div>
+            <Div row py={2} justifyContent="space-between">
+              <Text>{t('ti-xian-jin-e')}</Text>
+              <Text>¥{getMoney(data.amount)}</Text>
+            </Div>
+          </Div>
+        )}
+      </ScrollView>
+    </Div>
+  );
+}

+ 13 - 0
wallet/model.ts

@@ -0,0 +1,13 @@
+const wallet = (now) => ({
+  chooseCardId: 0,
+  chooseInfo: {},
+  changeChooseId(id) {
+    now({ chooseCardId: id });
+  },
+  setChooseInfo(info) {
+    console.log(info);
+    now({ chooseInfo: info });
+  },
+});
+
+export default wallet;