zdc 2 years ago
parent
commit
d574e02e4d
55 changed files with 1229 additions and 116 deletions
  1. 1 1
      cmd/api_generate/base/interface.ts
  2. 93 8
      cmd/api_generate/dc/userApi.ts
  3. 0 1
      cmd/api_generate/utils/docUtillTs.ts
  4. 12 0
      cmd/img.ts
  5. 2 0
      config/dev.js
  6. 3 0
      config/prod.js
  7. 5 2
      package.json
  8. 194 20
      src/apis/userApi.ts
  9. 1 1
      src/app.config.ts
  10. 5 0
      src/app.scss
  11. 11 2
      src/app.tsx
  12. BIN
      src/assets/bg_aboutus@3x.png
  13. BIN
      src/assets/bg_aboutus_footer@3x.png
  14. BIN
      src/assets/icon_bottom_bar_apply.png
  15. BIN
      src/assets/icon_bottom_bar_apply_active.png
  16. BIN
      src/assets/icon_bottom_bar_mine.png
  17. BIN
      src/assets/icon_bottom_bar_mine_active.png
  18. 1 1
      src/component/button/index.scss
  19. 0 0
      src/component/image/index.scss
  20. 35 0
      src/component/image/index.tsx
  21. 9 0
      src/component/layout/index.scss
  22. 82 8
      src/component/layout/index.tsx
  23. 2 1
      src/config/index.d.ts
  24. 1 0
      src/config/index.js
  25. 10 0
      src/context/bottomBarContext.tsx
  26. 82 0
      src/helper/userHelper.ts
  27. 8 0
      src/hooks/useRedux.ts
  28. 4 0
      src/pages/bottomNavBar/index.config.ts
  29. 36 0
      src/pages/bottomNavBar/index.tsx
  30. 2 2
      src/pages/home/index.scss
  31. 59 18
      src/pages/home/index.tsx
  32. 3 0
      src/pages/user/about/index.config.ts
  33. 4 0
      src/pages/user/about/index.scss
  34. 39 0
      src/pages/user/about/index.tsx
  35. 3 0
      src/pages/user/aggrement/index.config.ts
  36. 5 0
      src/pages/user/aggrement/index.scss
  37. 41 0
      src/pages/user/aggrement/index.tsx
  38. 69 8
      src/pages/user/login/index.tsx
  39. 9 0
      src/pages/user/mine/index.scss
  40. 38 10
      src/pages/user/mine/index.tsx
  41. 3 0
      src/pages/user/off/index.config.ts
  42. 4 0
      src/pages/user/off/index.scss
  43. 41 0
      src/pages/user/off/index.tsx
  44. 24 8
      src/pages/user/setting/index.tsx
  45. 3 0
      src/pages/webView/index.config.ts
  46. 4 0
      src/pages/webView/index.scss
  47. 37 0
      src/pages/webView/index.tsx
  48. 58 0
      src/redux/index.ts
  49. 12 4
      src/routes/route.h5.ts
  50. 14 0
      src/utils/reduxUtil.ts
  51. 9 19
      src/utils/request.ts
  52. 19 0
      src/utils/routeUtil.ts
  53. 31 0
      src/utils/storageUtil.ts
  54. 18 0
      test.json
  55. 83 2
      yarn.lock

+ 1 - 1
cmd/api_generate/base/interface.ts

@@ -7,7 +7,7 @@ export enum IMethod {
     "delete"
 }
 
-export type IParamsTypeEnum = "String" | "bool" | "num" | "DateTime" | "object" | "list" | "enum" | "int" | "double" | "dynamic";
+export type IParamsTypeEnum = "String" | "bool" | "num" | "DateTime" | "object" | "list" | "enum" | "int" | "double" | "dynamic" | "number";
 export type IParamsTypeEnum1 = "String" | "bool" | "num" | "DateTime" | "int" | "double";
 
 export type IParamsType = {

+ 93 - 8
cmd/api_generate/dc/userApi.ts

@@ -1,16 +1,101 @@
 import { IApiDocInterface } from "../base/interface";
 
 const userApiDoc: IApiDocInterface = {
-    "/adm/account/verificationCode": {
-        funcName: "getCode",
-        desc: "获取验证码",
+    "/wxMp/login": {
+        funcName: "wxLogin",
+        desc: "微信登录",
+        get: {
+            params: {
+                code: { type: "String", desc: "小程序获取的code", required: true, },
+            },
+            response: {
+                isReal: { type: 'number', desc: '', },
+                showHome: { type: 'String', desc: '' },
+                userMobile: { type: 'String', desc: '' },
+                nickName: { type: 'String', desc: '' },
+                inviteCode: { type: 'String', desc: '' },
+                currentIdStatus: { type: 'number', desc: '' },
+                headUrls: { type: 'String', desc: '' },
+                userId: { type: 'number', desc: '' },
+                token: { type: 'String', desc: '' },
+                isRealNameNetLoan: { type: 'number', desc: '' }
+            }
+        }
+    },
+    "/api/v1/app/agreements": {
+        funcName: "agreements",
+        desc: "获取隐私政策",
+        post: {
+            body: {
+                pageView: { type: "String", desc: "register_privacy", required: true },
+            },
+            response: [
+                {
+                    name: { type: "String", desc: "名称", required: true },
+                    version: { type: "String", desc: "版本", required: true },
+                    selected: { type: "int", desc: "是否选择", required: true },
+                    url: { type: "String", "desc": "webview 地址", required: true },
+                }
+            ]
+        }
+    },
+    "/api/v2/user/logoff": {
+        funcName: "logOff",
+        desc: "注销微信",
+        post: {
+          
+        }
+    },
+    "/getIndexInfo/{appId}": {
+        funcName: "getIndexInfo",
+        desc: "微信登录",
+        post: {
+            params: {
+                appId: { url: true, type: "String", desc: "Appid", required: true, },
+            },
+            response: {
+                appId: { type: "String" },
+                link: { type: "String", desc: "链接地址" },
+                name: { type: "String", desc: "链接文案" },
+            }
+        }
+    },
+    "/api/v2/app/settings": {
+        funcName: "homeTab",
+        desc: "获取首页tabs",
         post: {
-            // params: {
-            //     type: { type: "num", desc: "查询类型 0:查询全部(不传时默认),1:查询充值可用币种,2:查询提现可用币种", required: true, },
-            // },
+            body: {
+                appId: { type: "String", desc: "Appid", required: true, },
+            },
             response: {
-                imgToken: { type: "String", desc: "图片token", required: true, },
-                verification: { type: "String", desc: "验证码", required: true, },
+                tabMenus: {
+                    type: "list",
+                    child: {
+                        id: { type: 'int', desc: '' },
+                        appid: { type: 'int', desc: '' },
+                        code: { type: 'String', desc: '' },
+                        name: { type: 'String', desc: '' },
+                        icon: { type: 'String', desc: '' },
+                        iconActive: { type: 'String', desc: '' },
+                        jumpTypeId: { type: 'int', desc: '' },
+                        jumpTarget: { type: 'String', desc: '' },
+                        status: { type: 'int', desc: '' },
+                        sort: { type: 'int', desc: '' }
+                    }
+                },
+                articleCategories: {
+                    type: "list",
+                    child: {
+                        id: { type: 'int', desc: '' },
+                        name: { type: 'String', desc: '' },
+                        sort: { type: 'int', desc: '' },
+                        status: { type: 'int', desc: '' },
+                        createBy: { type: 'String', desc: '' },
+                        updateBy: { type: 'String', desc: '' },
+                        createTime: { type: 'String', desc: '' },
+                        updateTime: { type: 'String', desc: '' }
+                    }
+                }
             }
         }
     },

+ 0 - 1
cmd/api_generate/utils/docUtillTs.ts

@@ -160,7 +160,6 @@ export default class DocUtilTs {
             for (const key in obj.element.params) {
                 const element = obj.element.params[key];
                 if (element.url === true) {
-                    console.log(333, key)
                     url = url.replace(`{${key}}`, `\${options.params!.${key}}`);
                 }
             }

+ 12 - 0
cmd/img.ts

@@ -0,0 +1,12 @@
+
+import * as fs from 'fs';
+
+
+(async function () {
+    let list = fs.readdirSync("./src/assets");
+    // console.log(123, list);
+    list.forEach(p => {
+        
+    });
+})();
+

+ 2 - 0
config/dev.js

@@ -5,6 +5,8 @@ module.exports = {
   defineConstants: {
     "ENV": '"DEVELOPMENT"',
     "HOST": '"http://loan-web-api2.internal.jiebide.xin:881"',
+    // "HOST": '"https://api.bicredit.xin"',
+    "APPID": '"422"'
   },
   mini: {},
   h5: {}

+ 3 - 0
config/prod.js

@@ -3,6 +3,9 @@ module.exports = {
     NODE_ENV: '"production"'
   },
   defineConstants: {
+    "ENV": '"PRODUCTION"',
+    "HOST": '"https://api.bicredit.xin"',
+    "APPID": '"422"'
   },
   mini: {},
   h5: {

+ 5 - 2
package.json

@@ -10,6 +10,7 @@
   },
   "scripts": {
     "api": "ts-node cmd/api_generate/dc_boss.ts",
+    "apie": "ts-node cmd/api_generate/json2entity.ts",
     "route": "ts-node cmd/start.ts",
     "build:weapp": "taro build --type weapp",
     "build:swan": "taro build --type swan",
@@ -38,6 +39,7 @@
   "author": "",
   "dependencies": {
     "@babel/runtime": "^7.7.7",
+    "@reduxjs/toolkit": "^1.9.1",
     "@taroify/core": "^0.1.0-alpha.1",
     "@taroify/icons": "^0.1.0-alpha.1",
     "@tarojs/components": "3.5.10",
@@ -60,7 +62,8 @@
     "classnames": "^2.3.2",
     "qs": "^6.11.0",
     "react": "^18.0.0",
-    "react-dom": "^18.0.0"
+    "react-dom": "^18.0.0",
+    "react-redux": "^8.0.5"
   },
   "devDependencies": {
     "@babel/core": "^7.8.0",
@@ -83,4 +86,4 @@
     "typescript": "^4.1.0",
     "webpack": "5.69.0"
   }
-}
+}

+ 194 - 20
src/apis/userApi.ts

@@ -1,23 +1,197 @@
 
-import RequestUtil, { AxiosRequestConfigFunc } from "@/utils/request";
-
-
-export class GetCodeUsingPostResponse {
-
-  /// 图片token
-  imgToken!: string;
-
-  /// 验证码
-  verification!: string;
-
+        import RequestUtil, { AxiosRequestConfigFunc } from "@/utils/request";
+        
+        
+export  class WxLoginUsingGetParams {
+        
+                /// 小程序获取的code
+                 code!: string ;
+                
 }
-
-
-export default class UserApi {
-
-  /// 获取验证码
-  static getCodeUsingPost(options: AxiosRequestConfigFunc<any, any>): RequestUtil<GetCodeUsingPostResponse> {
-    return new RequestUtil<GetCodeUsingPostResponse>(`/adm/account/verificationCode`, 'POST', options);
-  }
-
+    
+export  class WxLoginUsingGetResponse {
+        
+                /// 
+                 isReal!: undefined ;
+                
+                /// 
+                 showHome!: string ;
+                
+                /// 
+                 userMobile!: string ;
+                
+                /// 
+                 nickName!: string ;
+                
+                /// 
+                 inviteCode!: string ;
+                
+                /// 
+                 currentIdStatus!: undefined ;
+                
+                /// 
+                 headUrls!: string ;
+                
+                /// 
+                 userId!: undefined ;
+                
+                /// 
+                 token!: string ;
+                
+                /// 
+                 isRealNameNetLoan!: undefined ;
+                
+}
+    
+export  class AgreementsUsingPostBody {
+        
+                /// register_privacy
+                 pageView!: string ;
+                
+}
+    
+export  class AgreementsUsingPostResponse {
+        
+                /// 名称
+                 name!: string ;
+                
+                /// 版本
+                 version!: string ;
+                
+                /// 是否选择
+                 selected!: number ;
+                
+                /// webview 地址
+                 url!: string ;
+                
+}
+    
+export  class GetIndexInfoUsingPostParams {
+        
+                /// Appid
+                 appId!: string ;
+                
+}
+    
+export  class GetIndexInfoUsingPostResponse {
+        
+                /// undefined
+                 appId!: string ;
+                
+                /// 链接地址
+                 link!: string ;
+                
+                /// 链接文案
+                 name!: string ;
+                
+}
+    
+export  class HomeTabUsingPostBody {
+        
+                /// Appid
+                 appId!: string ;
+                
+}
+    
+export  class HomeTabUsingPostResponseTabMenus {
+        
+                /// 
+                 id!: number ;
+                
+                /// 
+                 appid!: number ;
+                
+                /// 
+                 code!: string ;
+                
+                /// 
+                 name!: string ;
+                
+                /// 
+                 icon!: string ;
+                
+                /// 
+                 iconActive!: string ;
+                
+                /// 
+                 jumpTypeId!: number ;
+                
+                /// 
+                 jumpTarget!: string ;
+                
+                /// 
+                 status!: number ;
+                
+                /// 
+                 sort!: number ;
+                
+}
+    
+export  class HomeTabUsingPostResponseArticleCategories {
+        
+                /// 
+                 id!: number ;
+                
+                /// 
+                 name!: string ;
+                
+                /// 
+                 sort!: number ;
+                
+                /// 
+                 status!: number ;
+                
+                /// 
+                 createBy!: string ;
+                
+                /// 
+                 updateBy!: string ;
+                
+                /// 
+                 createTime!: string ;
+                
+                /// 
+                 updateTime!: string ;
+                
+}
+    
+export  class HomeTabUsingPostResponse {
+        
+                /// undefined
+                  tabMenus!: HomeTabUsingPostResponseTabMenus[] ;
+                
+                /// undefined
+                  articleCategories!: HomeTabUsingPostResponseArticleCategories[] ;
+                
 }
+    
+        
+        export default class UserApi {
+               
+        /// 微信登录
+        static  wxLoginUsingGet(options:AxiosRequestConfigFunc<any,WxLoginUsingGetParams> ):RequestUtil<WxLoginUsingGetResponse> {
+            return new RequestUtil<WxLoginUsingGetResponse>(`/wxMp/login`,'GET' ,options);
+        }
+       
+        /// 获取隐私政策
+        static  agreementsUsingPost(options:AxiosRequestConfigFunc<AgreementsUsingPostBody,any> ):RequestUtil<AgreementsUsingPostResponse[]> {
+            return new RequestUtil<AgreementsUsingPostResponse[]>(`/api/v1/app/agreements`,'POST' ,options);
+        }
+       
+        /// 微信登录
+        static  logOffUsingPost(options:AxiosRequestConfigFunc<any,any> ):RequestUtil<any> {
+            return new RequestUtil<any>(`/api/v2/user/logoff`,'POST' ,options);
+        }
+       
+        /// 微信登录
+        static  getIndexInfoUsingPost(options:AxiosRequestConfigFunc<any,GetIndexInfoUsingPostParams> ):RequestUtil<GetIndexInfoUsingPostResponse> {
+            return new RequestUtil<GetIndexInfoUsingPostResponse>(`/getIndexInfo/${options.params!.appId}`,'POST' ,options);
+        }
+       
+        /// 获取首页tabs
+        static  homeTabUsingPost(options:AxiosRequestConfigFunc<HomeTabUsingPostBody,any> ):RequestUtil<HomeTabUsingPostResponse> {
+            return new RequestUtil<HomeTabUsingPostResponse>(`/api/v2/app/settings`,'POST' ,options);
+        }
+    
+        }
+        

+ 1 - 1
src/app.config.ts

@@ -1,4 +1,4 @@
-let pages = ["pages/home/index","pages/user/login/index","pages/user/setting/index","pages/user/mine/index"];
+let pages = ["pages/bottomNavBar/index","pages/user/aggrement/index","pages/user/about/index","pages/user/off/index","pages/webView/index","pages/user/login/index","pages/user/setting/index"];
 
 if (process.env.TARO_ENV === 'h5') {
   pages = [

+ 5 - 0
src/app.scss

@@ -20,4 +20,9 @@ page {
     top: 0;
     left: 0;
     z-index: 100;
+}
+
+.nav-bar-hide {
+    height: 0;
+    overflow: hidden;
 }

+ 11 - 2
src/app.tsx

@@ -21,6 +21,9 @@ import { PropsWithChildren } from 'react';
 import { useRequest } from 'ahooks';
 import Taro from '@tarojs/taro';
 import AppContext from './context/appContext';
+import { Provider as ReduxProvider } from 'react-redux';
+import ReduxUtil from './utils/reduxUtil';
+import UserHelper from './helper/userHelper';
 
 interface IAppProps {
 
@@ -28,7 +31,11 @@ interface IAppProps {
 
 const App = (props: PropsWithChildren<IAppProps>) => {
   const result = useRequest(Taro.getSystemInfo);
+  useRequest(() => {
+    return UserHelper.cacheTabMenu();
+  })
   return (
+    // (appContext.systemInfo?.screenHeight || 0) - (appContext.systemInfo?.safeArea?.bottom || 0);
     <AppContext.Provider value={{
       systemInfo: result.data,
       screenSize: {
@@ -37,10 +44,12 @@ const App = (props: PropsWithChildren<IAppProps>) => {
       },
       padding: {
         top: result.data?.safeArea?.top || 0,
-        bottom: result.data?.safeArea?.bottom || 0,
+        bottom: (result.data?.screenHeight || 0) - (result.data?.safeArea?.bottom || 0),
       }
     }} >
-      {props.children}
+      <ReduxProvider store={ReduxUtil.store} >
+        {props.children}
+      </ReduxProvider>
     </AppContext.Provider>
   )
 }

BIN
src/assets/bg_aboutus@3x.png


BIN
src/assets/bg_aboutus_footer@3x.png


BIN
src/assets/icon_bottom_bar_apply.png


BIN
src/assets/icon_bottom_bar_apply_active.png


BIN
src/assets/icon_bottom_bar_mine.png


BIN
src/assets/icon_bottom_bar_mine_active.png


+ 1 - 1
src/component/button/index.scss

@@ -1,7 +1,7 @@
 .button {
     background-color: $primary-color;
     color: #fff;
-    padding: 12px 0;
+    padding: 24px 0;
     box-shadow: 0px 4px 30px 0px rgba(73, 108, 242, 0.6);
 
     &.button-fill {

+ 0 - 0
src/component/image/index.scss


+ 35 - 0
src/component/image/index.tsx

@@ -0,0 +1,35 @@
+import React, { PropsWithChildren } from 'react';
+import './index.scss'
+import { Image as TaroImage, ImageProps } from '@tarojs/components';
+
+
+
+interface IImageProps extends ImageProps {
+    src: string;
+    size?: number | { width?: number, height?: number };
+    /**计算的宽度 */
+    calculationWidth?: number;
+}
+
+const Image = (props: PropsWithChildren<IImageProps>) => {
+    const { style = {}, src, ...imgProps } = props;
+    let imgStyle: React.CSSProperties = style as React.CSSProperties;
+    if (props.size) {
+        if (typeof props.size === "number") {
+            imgStyle.width = props.size;
+            imgStyle.height = props.size;
+        }
+        else {
+            imgStyle = { ...imgStyle, ...props.size }
+        }
+    }
+    return (
+        <TaroImage
+            style={imgStyle}
+            src={src}
+            {...imgProps}
+        />
+    )
+}
+
+export default Image;

+ 9 - 0
src/component/layout/index.scss

@@ -0,0 +1,9 @@
+.bottom-nav {
+
+    box-shadow: 0px 0px 16px 0px rgba(0, 0, 0, 0.08);
+
+    .bottom-nav-icon {
+        width: 50px;
+        height: 50px;
+    }
+}

+ 82 - 8
src/component/layout/index.tsx

@@ -1,27 +1,101 @@
 import AppContext from '@/context/appContext';
+import BottomBarContext from '@/context/bottomBarContext';
+import UserHelper from '@/helper/userHelper';
+import useStore from '@/hooks/useRedux';
+import RouteUtil from '@/utils/routeUtil';
 import { FixedView } from '@taroify/core';
-import { View, ViewProps } from '@tarojs/components';
+import { Image, View, ViewProps } from '@tarojs/components';
+import { pxTransform } from '@tarojs/taro';
+import classNames from 'classnames';
 import { PropsWithChildren, useContext } from 'react';
+import Flex from '../flex';
+import Label from '../label';
 import './index.scss'
 
 interface ILayoutProps extends ViewProps {
     safe?: boolean;
+    bottomBarIndex?: number;
 }
+const barList = [
+    {
+        icon: require('../../assets/icon_bottom_bar_apply.png'),
+        activeIcon: require('../../assets/icon_bottom_bar_apply_active.png'),
+        label: "申请",
+        index: 0,
+        mustLogin: false,
+    },
+    {
+        icon: require('../../assets/icon_bottom_bar_mine.png'),
+        activeIcon: require('../../assets/icon_bottom_bar_mine_active.png'),
+        label: "我的",
+        mustLogin: true,
+        index: 1
+    }
+]
 
-const InternalLayout = (props: PropsWithChildren<ILayoutProps>) => {
-    const { safe = false, ...viewProps } = props;
+const BottomNavBar = () => {
     const appContext = useContext(AppContext);
-    const safeStyle: React.CSSProperties = {
+    const bottomBarContext = useContext(BottomBarContext);
+    const menu = useStore(p => p.tabMenu);
+    return (
+        <FixedView style={{ height: pxTransform(104), paddingBottom: appContext.padding.bottom, zIndex: 110, background: "#fff" }} className="bottom-nav" placeholder position="bottom">
+            <Flex style={{ height: pxTransform(104) }} direction="row" alignItem="center" justifyContent="center" >
+                {
+                    menu.map(p => {
+                        let _index = p.code === "home" ? 0 : 1;
+                        return (
+                            <Flex key={p.code} onClick={() => {
+                                UserHelper.afterLogin({
+                                    callback: async () => {
+                                        if (['home', 'user_center'].includes(p.code)) {
+                                            bottomBarContext.switchTo(_index);
+                                        }
+                                        else {
+                                            RouteUtil.toWebViewPage({
+                                                url: p.jumpTarget
+                                            });
+                                        }
+                                    }
+                                })
+                                return;
+                            }} flex={1} alignItem="center" justifyContent="center" >
+                                {/* <Image className='bottom-nav-icon' src={p.icon} /> */}
+                                <Image className='bottom-nav-icon' src={_index === bottomBarContext?.index ? p.iconActive : p.icon} />
+                                {/* <Label size="sm" type={"default"} > */}
+                                <Label size="sm" type={_index === bottomBarContext?.index ? "default" : "secondary"} >
+                                    {p.name}
+                                </Label>
+                            </Flex>
+                        )
+                    })
+                }
 
-    }
+            </Flex>
+        </FixedView>
+    )
+}
+
+const InternalLayout = (props: PropsWithChildren<ILayoutProps>) => {
+    const { bottomBarIndex, safe = false, ...viewProps } = props;
+    const appContext = useContext(AppContext);
+    const bottomBarContext = useContext(BottomBarContext);
+    const safeStyle: React.CSSProperties = {}
     if (safe) {
         safeStyle.paddingTop = appContext.padding.top;
         safeStyle.paddingBottom = appContext.padding.bottom; //(appContext.systemInfo?.screenHeight || 0) - (appContext.systemInfo?.safeArea?.bottom || 0);
     }
     return (
-        <View  {...viewProps} style={safeStyle} >
-            {props.children}
-            <FixedView style={{ height: 44, zIndex: 110, background: "#fff" }} placeholder position="bottom">固定在底部</FixedView>
+        <View className={classNames({ "nav-bar-hide": (bottomBarIndex ?? false) === false ? false : bottomBarContext?.index !== bottomBarIndex })} >
+            <View  {...viewProps} style={safeStyle} className={classNames({
+                [viewProps.className || ""]: !!viewProps.className,
+
+            })} >
+                {props.children}
+            </View>
+            {
+                props.bottomBarIndex !== undefined &&
+                <BottomNavBar />
+            }
         </View>
     )
 }

+ 2 - 1
src/config/index.d.ts

@@ -2,7 +2,8 @@ export type EnvType = "DEVELOPMENT" | "TEST" | "PRODUCTION" | "UAT"
 
 export interface Env {
     HOST: string,
-    ENV: EnvType
+    ENV: EnvType,
+    APPID: string
 }
 
 declare const APPConfig: Env;

+ 1 - 0
src/config/index.js

@@ -1,4 +1,5 @@
 export default {
     HOST: HOST,
     ENV: ENV,
+    APPID: APPID
 }

+ 10 - 0
src/context/bottomBarContext.tsx

@@ -0,0 +1,10 @@
+import React from "react";
+
+interface IBottomBarContextInfo {
+    switchTo: (index: number) => void;
+    index: number;
+}
+
+const BottomBarContext = React.createContext<IBottomBarContextInfo>(null as any);
+
+export default BottomBarContext;

+ 82 - 0
src/helper/userHelper.ts

@@ -0,0 +1,82 @@
+import UserApi, { WxLoginUsingGetResponse } from "@/apis/userApi";
+import APPConfig from "@/config";
+import { ILoginParms } from "@/pages/user/login";
+import { reduxActions } from "@/redux";
+import ReduxUtil from "@/utils/reduxUtil";
+import RouteUtil from "@/utils/routeUtil";
+import StorageUtil, { EStorage } from "@/utils/storageUtil";
+import Taro from "@tarojs/taro";
+
+export default class UserHelper {
+
+    /**
+     * 用户微信登录
+     * @param code 小程序获取的Code
+     */
+    public static async userWxLogin(code: string) {
+        let result = await UserApi.wxLoginUsingGet({ params: { code }, errorTips: true, loading: true }).toData();
+        if (result) {
+            this.cacheUserInfo(result);
+            this.cacheTabMenu();
+            return true;
+        }
+        return false;
+    }
+
+    public static async cacheUserInfo(userInfo: WxLoginUsingGetResponse) {
+        StorageUtil.set(EStorage.userInfo, userInfo);
+        this.cacheUserInfoToRedux(userInfo);
+    }
+
+    /**缓存到redux */
+    public static async cacheUserInfoToRedux(userInfo: WxLoginUsingGetResponse) {
+        ReduxUtil.dispatch(reduxActions.changeUserInfo(userInfo));
+    }
+
+    public static isLogin() {
+        return !!ReduxUtil.store.getState().userInfo;
+    }
+
+    /**推出登录 */
+    public static outLogin() {
+        Taro.clearStorageSync();
+        ReduxUtil.dispatch(reduxActions.clearState());
+        RouteUtil.reLaunch("/pages/bottomNavBar/index");
+    }
+
+    static callback?: () => Promise<any>;
+
+
+    public static afterLogin<T = any>(options: {
+        toLoginPage?: boolean,
+        callbackBeforeRoute?: boolean,
+    } & Omit<ILoginParms<T>, "callbackBeforeRoute">) {
+        const { toLoginPage = true, callback, callbackBeforeRoute = false, page, params } = options;
+        this.callback = callback;
+        if (UserHelper.isLogin()) {
+            callback?.();
+        }
+        else {
+            toLoginPage && RouteUtil.push<ILoginParms>("/pages/user/login/index", {
+                params: {
+                    page: page,
+                    params,
+                    callbackBeforeRoute: callbackBeforeRoute ? 1 : 0
+                }
+            });
+        }
+    }
+
+    public static async cacheTabMenu() {
+        await UserApi.homeTabUsingPost({
+            data: { "appId": APPConfig.APPID }
+        })
+            .toData()
+            .then(p => {
+                if (p) {
+                    StorageUtil.set(EStorage.tabMenu, p.tabMenus);
+                    ReduxUtil.dispatch(reduxActions.changeTabMenu(p.tabMenus));
+                }
+            })
+    }
+};

+ 8 - 0
src/hooks/useRedux.ts

@@ -0,0 +1,8 @@
+import { IReduxStore } from '@/redux';
+import { useSelector } from 'react-redux';
+
+const useStore = <T,>(map: (state: IReduxStore) => T) => {
+    return useSelector(map);
+}
+
+export default useStore;

+ 4 - 0
src/pages/bottomNavBar/index.config.ts

@@ -0,0 +1,4 @@
+export default definePageConfig({
+  navigationBarTitleText: '首页',
+  navigationStyle: "custom"
+})

+ 36 - 0
src/pages/bottomNavBar/index.tsx

@@ -0,0 +1,36 @@
+import { PropsWithChildren, useContext, useState } from 'react';
+import Home from '../home';
+import Mine from '../user/mine';
+import BottomBarContext from '@/context/bottomBarContext';
+import useStore from '@/hooks/useRedux';
+
+interface IBottomNavBarProps {
+
+}
+
+const BottomNavBar = (props: PropsWithChildren<IBottomNavBarProps>) => {
+    const [index, setIndex] = useState(0);
+    const bottomBarContext = useContext(BottomBarContext);
+    const menu = useStore(p => p.tabMenu);
+    return (
+        <BottomBarContext.Provider value={{
+            switchTo: setIndex,
+            index
+        }}  >
+            {
+                menu.map(p => {
+                    if (p.code === "home") {
+                        return (
+                            <Home />
+                        )
+                    }
+                    else if (p.code === "user_center") {
+                        return <Mine />
+                    }
+                })
+            }
+        </BottomBarContext.Provider>
+    )
+}
+
+export default BottomNavBar;

+ 2 - 2
src/pages/home/index.scss

@@ -34,8 +34,8 @@
 
     .index-back-btn {
         width: 354px;
-        height: 84px;
-        padding: 0;
+        // height: 84px;
+        padding: 16px 0;
     }
     .index-process-img{
         width: 104px;

+ 59 - 18
src/pages/home/index.tsx

@@ -1,23 +1,63 @@
+import UserApi from '@/apis/userApi';
 import Button from '@/component/button';
 import Flex from '@/component/flex';
 import Label from '@/component/label';
 import Layout from '@/component/layout';
 import Padding, { EdgeInsets } from '@/component/padding';
 import SizeBox from '@/component/sizeBox';
+import APPConfig from '@/config';
 import AppContext from '@/context/appContext';
+import UserHelper from '@/helper/userHelper';
 import ImageUtil from '@/utils/imgUtils';
+import RouteUtil from '@/utils/routeUtil';
 import { Image, ScrollView, View } from '@tarojs/components';
-import Taro, { pxTransform } from '@tarojs/taro';
-import React, { PropsWithChildren, useContext, useMemo } from 'react';
+import { pxTransform } from '@tarojs/taro';
+import { useRequest } from 'ahooks';
+import React, { useContext, useMemo } from 'react';
 import './index.scss'
 
-interface IHomeProps {
+// const func = () => {
+//     return new Promise<string>(p => {
+//         setTimeout(() => {
+//             p("请求成功123123" + (+new Date()));
+//         }, 2000);
+//     })
+// }
+
+
+// const Example = () => {
+//     const result = useRequest(() => {
+//         return func();
+//     }, {
+//         pollingInterval: 3000
+//     })
+
+//     return (
+//         <View>
+//             <Label>
+//                 {
+//                     result.loading ? "请求中" : result.data
+//                 }
+//             </Label>
+//             <Button onClick={() => {
+//                 result.refresh();
+//             }} >更新</Button>
+//         </View>
+//     )
+// }
+
+
 
-}
 
-const Home = (props: PropsWithChildren<IHomeProps>) => {
+const Home = () => {
     const appContext = useContext(AppContext);
     const { screenWidth } = appContext.systemInfo!;
+   
+    const indexInfoResult = useRequest(() => {
+        return UserApi.getIndexInfoUsingPost({
+            params: { appId: APPConfig.APPID }
+        }).toData();
+    })
     const imgBackStyle = useMemo(() => {
         return { ...ImageUtil.calculationHeight(332 / 170, (screenWidth - 64 + 16) / 2), position: "relative" } as React.CSSProperties;
     }, []);
@@ -36,7 +76,7 @@ const Home = (props: PropsWithChildren<IHomeProps>) => {
         )
     }
     return (
-        <Layout safe className='index' >
+        <Layout safe className='index' bottomBarIndex={0} >
             <ScrollView>
                 <Image style={ImageUtil.calculationHeight(750 / 88, appContext.systemInfo!.screenWidth)} src={require('../../assets/home_logo.png')} />
                 <SizeBox height={24} />
@@ -46,20 +86,17 @@ const Home = (props: PropsWithChildren<IHomeProps>) => {
                         <Flex alignItem="center" justifyContent="center" rowGap={24} className='index-back-content' >
                             <Label className='index-back-se' >可担保贷款额度</Label>
                             <Label className='index-back-amount' >200,000.00</Label>
-                            <Button openType="getPhoneNumber" onGetPhoneNumber={e => {
-
-                                console.log(333, e);
-                            }} className='index-back-btn' color="white" fill >立即申请</Button>
                             <Button onClick={() => {
-                                Taro.getUserProfile({
-                                    desc: "12312",
-                                    success: (oo) => {
-                                        console.log(oo);
+                                UserHelper.afterLogin({
+                                    callbackBeforeRoute: true,
+                                    callback: async () => {
+                                        indexInfoResult.data && RouteUtil.toWebViewPage({
+                                            // url: "http://localhost:8080/br/chengyr"
+                                            url: indexInfoResult.data.link,
+                                        })
                                     }
                                 })
-                            }} >
-                                123123
-                            </Button>
+                            }} className='index-back-btn' color="white" fill >{indexInfoResult.data?.name||"立即申请"}</Button>
                         </Flex>
                     </View>
                 </Padding>
@@ -109,4 +146,8 @@ const Home = (props: PropsWithChildren<IHomeProps>) => {
     )
 }
 
-export default Home;
+export default Home;
+
+
+
+

+ 3 - 0
src/pages/user/about/index.config.ts

@@ -0,0 +1,3 @@
+export default definePageConfig({
+  navigationBarTitleText: '关于我们'
+})

+ 4 - 0
src/pages/user/about/index.scss

@@ -0,0 +1,4 @@
+.index {
+   
+
+}

+ 39 - 0
src/pages/user/about/index.tsx

@@ -0,0 +1,39 @@
+import Flex from '@/component/flex';
+import Layout from '@/component/layout';
+import { PropsWithChildren, useContext, useMemo } from 'react';
+import { Image, View } from '@tarojs/components';
+import './index.scss'
+import ImageUtil from '@/utils/imgUtils';
+import AppContext from '@/context/appContext';
+import Label from '@/component/label';
+
+interface IAboutProps {
+
+}
+
+const About = (props: PropsWithChildren<IAboutProps>) => {
+    const appContext = useContext(AppContext);
+    const { screenWidth } = appContext.systemInfo!;
+    const imgBackStyle = useMemo(() => {
+        return { ...ImageUtil.calculationHeight(1125 / 957, (screenWidth)), position: "relative" } as React.CSSProperties;
+    }, []);
+    return (
+        <Layout  >
+            <Flex style={{ height: "100vh" }} >
+                <View style={imgBackStyle}  >
+                    <Image className='background-image' src={require(`../../../assets/bg_aboutus@3x.png`)} />
+                    <Flex rowGap="sm" className='background-content' >
+                        <View style={{ height: 200 }} />
+                        <Flex alignItem="center" className='login-back-logo-wrap' >
+                            <Image style={{ ...ImageUtil.calculationHeight(268 / 352, 120) }} className='login-logo' src={require("@/assets/logo.png")} />
+                        </Flex>
+                    </Flex>
+                </View>
+                <Flex flex={2} />
+                <Label style={{ marginBottom: Math.max(appContext.padding.bottom, 16), textAlign: "center" }} type="secondary" >重庆甜瓜科技有限公司</Label>
+            </Flex>
+        </Layout>
+    )
+}
+
+export default About;

+ 3 - 0
src/pages/user/aggrement/index.config.ts

@@ -0,0 +1,3 @@
+export default definePageConfig({
+  navigationBarTitleText: '首页'
+})

+ 5 - 0
src/pages/user/aggrement/index.scss

@@ -0,0 +1,5 @@
+.mine-item-icon {
+    width: 48px;
+    height: 48px;
+    padding-right: 24px;
+}

+ 41 - 0
src/pages/user/aggrement/index.tsx

@@ -0,0 +1,41 @@
+import UserApi from '@/apis/userApi';
+import Layout from '@/component/layout';
+import { Cell } from '@taroify/core';
+import { useRequest } from 'ahooks';
+import React, { PropsWithChildren } from 'react';
+import { Image } from "@tarojs/components";
+import './index.scss'
+import SizeBox from '@/component/sizeBox';
+import { Arrow } from '@taroify/icons';
+import RouteUtil from '@/utils/routeUtil';
+
+interface IAggrementProps {
+
+}
+
+const Aggrement = (props: PropsWithChildren<IAggrementProps>) => {
+    const aggrement = useRequest(() => {
+        return UserApi.agreementsUsingPost({ data: { "pageView": "user_center_view" } }).toData();
+    })
+    console.log(666, aggrement);
+    return (
+        <Layout>
+            {
+                aggrement.data?.map(p => (
+                    <React.Fragment key={p.name}>
+                        <Cell size="large"
+                            onClick={() => {
+                                RouteUtil.toWebViewPage({
+                                    url: p.url
+                                })
+                            }}
+                            title={p.name} rightIcon={<Arrow />}></Cell>
+                        <SizeBox height={8} />
+                    </React.Fragment>
+                ))
+            }
+        </Layout>
+    )
+}
+
+export default Aggrement;

+ 69 - 8
src/pages/user/login/index.tsx

@@ -1,19 +1,45 @@
+import UserApi from '@/apis/userApi';
 import Button from '@/component/button';
 import Flex from '@/component/flex';
 import Label from '@/component/label';
 import Layout from '@/component/layout';
 import Padding, { EdgeInsets } from '@/component/padding';
+import { useParams } from '@/component/routes/hooks/useParams';
 import SizeBox from '@/component/sizeBox';
+import UserHelper from '@/helper/userHelper';
+import { IRoutesUrls } from '@/routes/route.h5';
 import RouteUtil from '@/utils/routeUtil';
 import { Image } from '@tarojs/components';
-import { PropsWithChildren } from 'react';
+import { useRequest } from 'ahooks';
+import { PropsWithChildren, useEffect } from 'react';
 import './index.scss'
 
 interface ILoginProps {
 
 }
 
+export interface ILoginParms<T = any> {
+    callback?: () => Promise<any>,
+    page?: IRoutesUrls,
+    params?: T;
+    /**回调函数是否先于路由动作之前执行 */
+    callbackBeforeRoute?: number
+}
+
+export class LoginCallback {
+
+}
+
 const Login = (props: PropsWithChildren<ILoginProps>) => {
+    const params = useParams<ILoginParms>();
+    const aggrement = useRequest(() => {
+        return UserApi.agreementsUsingPost({ data: { "pageView": "register_privacy" } }).toData();
+    })
+    useEffect(() => {
+        () => {
+            UserHelper.callback = undefined;
+        }
+    })
     return (
         <Layout className='login' >
             <Flex className='login-back' >
@@ -24,15 +50,50 @@ const Login = (props: PropsWithChildren<ILoginProps>) => {
                 <Flex flex={2} />
                 <Padding padding={EdgeInsets.symmetric({ horizontal: 80 })} >
                     <Flex alignItem='center' rowGap={12} >
-                        <Button onClick={() => {
-                            RouteUtil.push("/pages/home/index");
+                        <Button openType="getPhoneNumber" onGetPhoneNumber={async (e) => {
+                            if (e.detail.code && await UserHelper.userWxLogin(e.detail.code) === true) {
+                                if (params.callbackBeforeRoute?.toString() === "1") {
+                                    if (params.page) {
+                                        RouteUtil.replace(params.page, { params: params.params });
+                                    }
+                                    else {
+                                        RouteUtil.pop();
+                                    }
+                                }
+
+                                if (UserHelper.callback) {
+                                    await UserHelper.callback();
+                                }
+                                if (params.callbackBeforeRoute?.toString() !== "1") {
+                                    if (params.page) {
+                                        RouteUtil.replace(params.page, { params: params.params });
+                                    }
+                                    else {
+                                        RouteUtil.pop();
+                                    }
+                                }
+                            }
                         }} fill >微信登录</Button>
                         <SizeBox height={16} />
-                        <Label size="sm" >点击微信登录则代表阅读并同意</Label>
-                        <Flex direction="row" >
-                            <Label className='login-aggrement' size="sm" >《用户服务协议》</Label>
-                            <Label className='login-aggrement' size="sm" >《隐私协议》</Label>
-                        </Flex>
+                        {
+                            aggrement.data &&
+                            <>
+                                <Label size="sm" >点击微信登录则代表阅读并同意</Label>
+                                <Flex direction="row" >
+                                    {
+                                        aggrement.data?.map((p, index) => {
+                                            return (
+                                                <Label key={index}
+                                                    onClick={() => {
+                                                        RouteUtil.toWebViewPage({ url: p.url })
+                                                    }}
+                                                    className='login-aggrement' size="sm" >《{p.name}》</Label>
+                                            )
+                                        })
+                                    }
+                                </Flex>
+                            </>
+                        }
                     </Flex>
                 </Padding>
                 <Flex flex={1} />

+ 9 - 0
src/pages/user/mine/index.scss

@@ -10,6 +10,15 @@
         .mine-menu-wrap {
             height: 172px;
             background-color: #F9F9FF;
+            width: 100%;
+            border: none;
+            border-radius: 0;
+            box-shadow: none;
+            color: #000000;
+            font-weight: 400;
+            &::after{
+                border: none;
+            }
         }
 
         .mine-menu-icon {

+ 38 - 10
src/pages/user/mine/index.tsx

@@ -10,6 +10,10 @@ import { PropsWithChildren, useContext } from 'react';
 import './index.scss'
 import { Cell } from "@taroify/core"
 import { Arrow } from "@taroify/icons"
+import useStore from '@/hooks/useRedux';
+import RouteUtil from '@/utils/routeUtil';
+import Taro from '@tarojs/taro';
+import Button from '@/component/button';
 
 interface IMineProps {
 
@@ -17,37 +21,61 @@ interface IMineProps {
 
 const Mine = (props: PropsWithChildren<IMineProps>) => {
     const { screenSize } = useContext(AppContext);
+    const userInfo = useStore(p => p.userInfo);
     return (
-        <Layout className='mine' >
+        <Layout className='mine' bottomBarIndex={1} >
             <View style={{ ...ImageUtil.calculationHeight(750 / 328, screenSize.width), position: "relative" }} >
                 <Image src={require('@/assets/icon_mine_back.png')} className="background-image" />
                 <Flex justifyContent="flex-end" className='background-content' >
                     <Padding padding={EdgeInsets.symmetric({ horizontal: 32 })} >
                         <Flex columnGap={32} direction="row" alignItem="center" >
                             <Image src={require('@/assets/icon_mine_avatar.png')} className="mine-avatar" />
-                            <Label bold >点击此处登录</Label>
+                            <Label bold >
+                                {
+                                    userInfo ?
+                                        `HI,${userInfo.userMobile}` :
+                                        "点击此处登录"
+                                }
+
+                            </Label>
                         </Flex>
                     </Padding>
                 </Flex>
             </View>
             <SizeBox height={16} />
             <Flex direction="row" className='mine-menu' >
-                <Flex columnGap="sm" direction="row" alignItem="center" justifyContent="center" flex={1} className='mine-menu-wrap' >
+                <Flex onClick={() => {
+                    // https://huirong.bicredit.xin/
+                    RouteUtil.toWebViewPage({ url: "https://h5.bicredit.xin/view/webapp/business-cooperation.html" })
+                }} columnGap="sm" direction="row" alignItem="center" justifyContent="center" flex={1} className='mine-menu-wrap' >
                     <Label>商务合作</Label>
                     <Image src={require('@/assets/icon_mine_business.png')} className="mine-menu-icon" />
                 </Flex>
                 <SizeBox width={16} />
-                <Flex columnGap="sm" direction="row" alignItem="center" justifyContent="center" flex={1} className='mine-menu-wrap' >
-                    <Label>在线客服</Label>
-                    <Image src={require('@/assets/icon_mine_service.png')} className="mine-menu-icon" />
+                <Flex flex={1} >
+                    <Button className='mine-menu-wrap' open-type="contact" session-from="sessionFrom">
+                        <Flex columnGap="sm" direction="row" alignItem="center" justifyContent="center" style={{ height: "100%" }} flex={1} >
+                            在线客服
+                            <Image src={require('@/assets/icon_mine_service.png')} className="mine-menu-icon" />
+                        </Flex>
+
+                    </Button>
                 </Flex>
+
+
             </Flex>
             <SizeBox height={16} />
-            <Cell size="large" icon={<Image src={require('@/assets/icon_mine_aggrement.png')} className="mine-item-icon" />} title="用户协议" rightIcon={<Arrow />}  ></Cell>
-            <SizeBox height={8} />
-            <Cell size="large" icon={<Image src={require('@/assets/icon_mine_upgrade.png')} className="mine-item-icon" />} title="检查更新" rightIcon={<Arrow />}  ></Cell>
+            <Cell size="large"
+                onClick={() => {
+                    RouteUtil.push("/pages/user/aggrement/index")
+                }}
+                icon={<Image src={require('@/assets/icon_mine_aggrement.png')} className="mine-item-icon" />} title="用户协议" rightIcon={<Arrow />}  ></Cell>
             <SizeBox height={8} />
-            <Cell size="large" icon={<Image src={require('@/assets/icon_mine_setting.png')} className="mine-item-icon" />} title="设置" rightIcon={<Arrow />}  ></Cell>
+            {/* <Cell size="large" icon={<Image src={require('@/assets/icon_mine_upgrade.png')} className="mine-item-icon" />} title="检查更新" rightIcon={<Arrow />}  ></Cell>
+            <SizeBox height={8} /> */}
+            <Cell onClick={() => {
+                RouteUtil.push("/pages/user/setting/index")
+            }} size="large" icon={<Image src={require('@/assets/icon_mine_setting.png')} className="mine-item-icon" />} title="设置" rightIcon={<Arrow />}  ></Cell>
         </Layout>
     )
 }

+ 3 - 0
src/pages/user/off/index.config.ts

@@ -0,0 +1,3 @@
+export default definePageConfig({
+  navigationBarTitleText: '账号注销'
+})

+ 4 - 0
src/pages/user/off/index.scss

@@ -0,0 +1,4 @@
+.index {
+   
+
+}

+ 41 - 0
src/pages/user/off/index.tsx

@@ -0,0 +1,41 @@
+import UserApi from '@/apis/userApi';
+import Button from '@/component/button';
+import Label from '@/component/label';
+import Layout from '@/component/layout';
+import Padding from '@/component/padding';
+import UserHelper from '@/helper/userHelper';
+import Taro from '@tarojs/taro';
+import './index.scss'
+
+
+const LogOff = () => {
+    return (
+        <Layout>
+            <Padding>
+                <Label style={{ marginBottom: 4 }} bold size="lg" >注销须知</Label>
+                <Label style={{ marginBottom: 4 }} type="secondary" size="sm" >1、若账号存在风险或异常行为无法注销;</Label>
+                <Label style={{ marginBottom: 4 }} type="secondary" size="sm" >2、账号内若有未完成状态订单无法注销;</Label>
+                <Label style={{ marginBottom: 4 }} type="secondary" size="sm" >3、本平台仅能注销在本平台注册的账号,若通过本平台申请第三方贷款或其他服务,请联系第三方注销;</Label>
+                <Label style={{ marginBottom: 24 }} type="secondary" size="sm" >4、账号注销后无法恢复,账户内信息将无法查看,请谨慎注销。</Label>
+                <Button onClick={() => {
+                    Taro.showModal({
+                        title: "确认注销?",
+
+                        success: (res) => {
+
+                            res.confirm && UserApi.logOffUsingPost({
+                                loading: true, errorTips: true
+                            })
+                                .checkSuccess()
+                                .then(p => {
+                                    p && UserHelper.outLogin()
+                                })
+                        }
+                    })
+                }} round={false} >熟知并同意</Button>
+            </Padding>
+        </Layout>
+    )
+}
+
+export default LogOff;

+ 24 - 8
src/pages/user/setting/index.tsx

@@ -1,9 +1,12 @@
 import Layout from '@/component/layout';
 import SizeBox from '@/component/sizeBox';
-import { Cell, Switch } from '@taroify/core';
+import { Cell } from '@taroify/core';
 import { PropsWithChildren } from 'react';
 import './index.scss'
 import { Arrow } from "@taroify/icons"
+import Taro from '@tarojs/taro';
+import UserHelper from '@/helper/userHelper';
+import RouteUtil from '@/utils/routeUtil';
 
 interface ISettingProps {
 
@@ -13,15 +16,28 @@ const Setting = (props: PropsWithChildren<ISettingProps>) => {
     return (
         <Layout className='' >
             <SizeBox height={16} />
-            <Cell size="large" title="个性化推荐" bordered={false} rightIcon={<Switch size="24" />}  ></Cell>
+            {/* <Cell size="large" title="个性化推荐" bordered={false} rightIcon={<Switch size="24" />}  ></Cell> */}
+            {/* <SizeBox height={8} /> */}
+            <Cell onClick={()=>{
+                RouteUtil.push("/pages/user/about/index")
+            }} size="large" title="关于我们" bordered={false} rightIcon={<Arrow />}  ></Cell>
             <SizeBox height={8} />
-            <Cell size="large" title="关于我们" bordered={false} rightIcon={<Arrow />}  ></Cell>
+            {/* <Cell size="large" title="清理缓存" bordered={false} rightIcon={<Arrow />}  ></Cell>
+            <SizeBox height={8} /> */}
+            <Cell  onClick={()=>{
+                RouteUtil.push("/pages/user/off/index");
+            }} size="large" title="账户注销" bordered={false} rightIcon={<Arrow />}  ></Cell>
             <SizeBox height={8} />
-            <Cell size="large" title="清理缓存" bordered={false} rightIcon={<Arrow />}  ></Cell>
-            <SizeBox height={8} />
-            <Cell size="large" title="账户注销" bordered={false} rightIcon={<Arrow />}  ></Cell>
-            <SizeBox height={8} />
-            <Cell size="large" title="安全退出" bordered={false} rightIcon={<Arrow />}  ></Cell>
+            <Cell
+                onClick={() => {
+                    Taro.showModal({
+                        title: "是否退出登录",
+                        success: () => {
+                            UserHelper.outLogin();
+                        }
+                    })
+                }}
+                size="large" title="安全退出" bordered={false} rightIcon={<Arrow />}  ></Cell>
         </Layout>
     )
 }

+ 3 - 0
src/pages/webView/index.config.ts

@@ -0,0 +1,3 @@
+export default definePageConfig({
+  navigationBarTitleText: '',
+})

+ 4 - 0
src/pages/webView/index.scss

@@ -0,0 +1,4 @@
+.index {
+   
+
+}

+ 37 - 0
src/pages/webView/index.tsx

@@ -0,0 +1,37 @@
+import Layout from '@/component/layout';
+import './index.scss'
+import { WebView as WxWebView } from '@tarojs/components';
+import { useParams } from '@/component/routes/hooks/useParams';
+import { useMemo } from 'react';
+import ReduxUtil from '@/utils/reduxUtil';
+import qs from 'qs';
+
+
+export interface IWebViewParams {
+    url: string
+}
+
+const WebView = () => {
+    const params = useParams<IWebViewParams>();
+    const url = useMemo(() => {
+        let _url = decodeURIComponent(params.url);
+        const userInfo = ReduxUtil.store.getState().userInfo!;
+        let _qs = qs.stringify({ token: userInfo.token, phone: userInfo.userMobile, userId: userInfo.userId }, { encode: false });
+        if (_url.indexOf("?") > -1) {
+            _url += `&${_qs}`;
+        }
+        else {
+            _url += `?${_qs}`;
+        }
+        return _url;
+    }, [])
+    console.log(555,url);
+    return (
+        <Layout>
+            <WxWebView
+                src={url} />
+        </Layout>
+    )
+}
+
+export default WebView;

+ 58 - 0
src/redux/index.ts

@@ -0,0 +1,58 @@
+import { HomeTabUsingPostResponseTabMenus, WxLoginUsingGetResponse } from '@/apis/userApi';
+import StorageUtil, { EStorage } from '@/utils/storageUtil';
+import { createSlice } from '@reduxjs/toolkit';
+
+
+export interface IReduxStore {
+    userInfo: WxLoginUsingGetResponse;
+    tabMenu: HomeTabUsingPostResponseTabMenus[]
+}
+
+const initialState = (): IReduxStore => ({
+    userInfo: StorageUtil.get(EStorage.userInfo),
+    tabMenu: StorageUtil.get(EStorage.tabMenu) || [
+        {
+            code: "home",
+            icon: require('@/assets/icon_bottom_bar_apply.png'),
+            iconActive: require('@/assets/icon_bottom_bar_apply_active.png'),
+            name: "首页",
+        },
+        {
+            code: "user_center",
+            icon: require('@/assets/icon_bottom_bar_mine.png'),
+            iconActive: require('@/assets/icon_bottom_bar_mine_active.png'),
+            name: "我的",
+        }
+    ]
+})
+
+export const reducersSlice = createSlice({
+    name: 'reducer',
+    initialState: initialState(),
+    reducers: {
+        changeUserInfo: (state, action) => {
+            state.userInfo = action.payload;
+        },
+        clearState: (state) => {
+            let initState = initialState();
+            for (const key in state) {
+                state[key] = initState[key];
+            }
+            // state = initialState();
+        },
+        changeTabMenu: (state, action) => {
+            state.tabMenu = action.payload;
+        }
+        /**更改地址 */
+        // changeAddress: (state, action) => {
+        //     let obj = { ...action.payload };
+        //     state.wallet = obj;
+        // },
+
+        // changeTokenList: (state, action) => {
+        //     state.tokenList = action.payload;
+        // }
+    }
+});
+
+export const reduxActions = reducersSlice.actions;

+ 12 - 4
src/routes/route.h5.ts

@@ -1,14 +1,22 @@
-import Home from "@/pages/home";
+import BottomNavBar from "@/pages/bottomNavBar";
+import About from "@/pages/user/about";
+import Aggrement from "@/pages/user/aggrement";
 import Login from "@/pages/user/login";
-import Mine from "@/pages/user/mine";
+import LogOff from "@/pages/user/off";
 import Setting from "@/pages/user/setting";
+import WebView from "@/pages/webView";
+
+
 
 const routes = {
     /// 开始 请勿修改 ------
-    "/pages/home/index": { component: Home },
+    "/pages/bottomNavBar/index": { component: BottomNavBar },
+    "/pages/user/aggrement/index": { component: Aggrement },
+    "/pages/user/about/index": { component: About },
+    "/pages/user/off/index": { component: LogOff },
+    "/pages/webView/index": { component: WebView },
     "/pages/user/login/index": { component: Login },
     "/pages/user/setting/index": { component: Setting },
-    "/pages/user/mine/index": { component: Mine },
     /// 结束 请勿修改 ------
 }
 

+ 14 - 0
src/utils/reduxUtil.ts

@@ -0,0 +1,14 @@
+import { configureStore } from "@reduxjs/toolkit";
+import { reducersSlice } from "../redux";
+
+
+export default class ReduxUtil {
+
+    public static store = configureStore({
+        reducer: reducersSlice.reducer
+    })
+
+    public static dispatch(actions: any) {
+        this.store.dispatch(actions);
+    }
+}

+ 9 - 19
src/utils/request.ts

@@ -1,6 +1,9 @@
+import { WxLoginUsingGetResponse } from "@/apis/userApi";
+import APPConfig from "@/config";
 import Taro from "@tarojs/taro";
 import qs from "qs";
 import ModalUtil from "./modalUtil";
+import StorageUtil, { EStorage } from "./storageUtil";
 
 
 interface IRequestBaseResult<T> {
@@ -33,45 +36,32 @@ export default class RequestUtil<T> {
     }
 
     public runRequest(url: string, method: "POST" | "GET" | "PUT" | "DELETE", options: IRequestOptions) {
+        options.loading && Taro.showLoading();
         return new Promise<Taro.request.SuccessCallbackResult<IRequestBaseResult<T>>>((resolve, rej) => {
             if (options.params) {
                 url += "?" + qs.stringify(options.params);
             }
+            url = APPConfig.HOST + url;
+            let userInfo = StorageUtil.get<WxLoginUsingGetResponse>(EStorage.userInfo);
             Taro.request({
                 url,
                 method,
+                header: { userId: userInfo?.userId, "appId": APPConfig.APPID, runPlatform: 1, token: userInfo?.token },
                 data: options.data,
                 fail: () => {
                     options.loading && Taro.hideLoading();
                     rej();
                 },
                 success: (res) => {
-                    // if (typeof res.data === "string") {
-                    //     try {
-                    //         res.data = JSON.parse(res.data);
-                    //     } catch (error) {
-
-                    //     }
-                    // }
                     options.loading && Taro.hideLoading();
                     let _success = this.checkSuccessSync(res);
 
-                    if (_success) {
+                    if (_success && options.success) {
                         ModalUtil.toast(typeof (options.success) === "string" ? options.success : res.data.msg);
                     }
                     else {
-                        ModalUtil.toast(res.data.msg);
+                        options.errorTips && ModalUtil.toast(res.data.msg);
                     }
-                    // else if (res.data.code !== 0 && (options.tips || options.errorTips)) {
-
-                    // }
-                    // else if (res.data.code === 10001) {
-                    //     // ModalUtil.toast("登录失效");
-                    //     // MemberHelper.clearMemberInfo();
-                    //     // Taro.reLaunch({
-                    //     //     url: "/pages/user/userLogin/index"
-                    //     // })
-                    // }
                     resolve(res);
                 },
             })

+ 19 - 0
src/utils/routeUtil.ts

@@ -1,3 +1,4 @@
+import { IWebViewParams } from "@/pages/webView";
 import Taro from "@tarojs/taro";
 import qs from "qs";
 import { IRoutesUrls } from "../routes/route.h5";
@@ -14,6 +15,19 @@ export default class RouteUtil {
         });
     }
 
+    public static replace<T>(_url: IRoutesUrls, option?: { params: T }) {
+        if (option?.params) {
+            _url += "?" + qs.stringify(option.params, { "encode": false });
+        }
+        Taro.redirectTo({
+            url: _url,
+        });
+    }
+
+    public static reLaunch(_url: IRoutesUrls) {
+        Taro.reLaunch({ url: _url })
+    }
+
     /**返回 */
     public static pop(stack: number = 1) {
         Taro.navigateBack({ "delta": stack });
@@ -23,4 +37,9 @@ export default class RouteUtil {
     public static removeLast() {
 
     }
+
+    public static toWebViewPage(options: IWebViewParams) {
+        options.url = encodeURIComponent(options.url);
+        RouteUtil.push<IWebViewParams>("/pages/webView/index", { params: options })
+    }
 }

+ 31 - 0
src/utils/storageUtil.ts

@@ -0,0 +1,31 @@
+import Taro from "@tarojs/taro";
+
+export enum EStorage {
+    userInfo = "USER_INFO",
+    tabMenu = "TAB_MENU"
+}
+
+export default class StorageUtil {
+
+    public static set(key: EStorage, value: any) {
+        if (typeof value !== "string") {
+            try {
+                value = JSON.stringify(value);
+            } catch (error) {
+
+            }
+        }
+        Taro.setStorageSync(key, value)
+    }
+
+    public static get<T>(key: EStorage): T {
+        let value = Taro.getStorageSync(key);
+        try {
+            value = value && JSON.parse(value)
+        } catch (error) {
+
+        }
+        return value;
+
+    }
+};

+ 18 - 0
test.json

@@ -0,0 +1,18 @@
+{
+    "msg": "注册成功",
+    "code": 0,
+    "data": {
+        "isReal": 0,
+        "showHome": "1",
+        "userMobile": "17764828301",
+        "nickName": "",
+        "inviteCode": "PvVqGf",
+        "grade": null,
+        "sex": null,
+        "currentIdStatus": 0,
+        "headUrls": "https://loansm.oss-cn-hangzhou.aliyuncs.com/usr_ico_default.png",
+        "userId": 3004520,
+        "token": "21ed4e4e-386a-46b1-bde1-86892c67c19e",
+        "isRealNameNetLoan": 0
+    }
+}

+ 83 - 2
yarn.lock

@@ -1088,6 +1088,13 @@
     core-js-pure "^3.25.1"
     regenerator-runtime "^0.13.11"
 
+"@babel/runtime@^7.12.1", "@babel/runtime@^7.9.2":
+  version "7.20.7"
+  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd"
+  integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==
+  dependencies:
+    regenerator-runtime "^0.13.11"
+
 "@babel/runtime@^7.14.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
   version "7.20.6"
   resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
@@ -1331,6 +1338,16 @@
     schema-utils "^3.0.0"
     source-map "^0.7.3"
 
+"@reduxjs/toolkit@^1.9.1":
+  version "1.9.1"
+  resolved "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-1.9.1.tgz#4c34dc4ddcec161535288c60da5c19c3ef15180e"
+  integrity sha512-HikrdY+IDgRfRYlCTGUQaiCxxDDgM1mQrRbZ6S1HFZX5ZYuJ4o8EstNmhTwHdPl2rTmLxzwSu0b3AyeyTlR+RA==
+  dependencies:
+    immer "^9.0.16"
+    redux "^4.2.0"
+    redux-thunk "^2.4.2"
+    reselect "^4.1.7"
+
 "@sideway/address@^4.1.3":
   version "4.1.4"
   resolved "https://registry.npmmirror.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@@ -1963,6 +1980,14 @@
     "@types/minimatch" "*"
     "@types/node" "*"
 
+"@types/hoist-non-react-statics@^3.3.1":
+  version "3.3.1"
+  resolved "https://registry.npmmirror.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
+  integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
+  dependencies:
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+
 "@types/html-minifier-terser@^6.0.0":
   version "6.1.0"
   resolved "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@@ -2062,7 +2087,7 @@
   resolved "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
   integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
 
-"@types/react@^18.0.0":
+"@types/react@*", "@types/react@^18.0.0":
   version "18.0.26"
   resolved "https://registry.npmmirror.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
   integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==
@@ -2115,6 +2140,11 @@
   dependencies:
     "@types/node" "*"
 
+"@types/use-sync-external-store@^0.0.3":
+  version "0.0.3"
+  resolved "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
+  integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
+
 "@types/vinyl@^2.0.4":
   version "2.0.7"
   resolved "https://registry.npmmirror.com/@types/vinyl/-/vinyl-2.0.7.tgz#9739a9a2afaf9af32761c54a0e82c735279f726c"
@@ -6527,6 +6557,13 @@ hls.js@^1.1.5:
   resolved "https://registry.npmmirror.com/hls.js/-/hls.js-1.2.9.tgz#2f25e42ec4c2ea8c88ab23c0f854f39062d45ac9"
   integrity sha512-SPjm8ix0xe6cYzwDvdVGh2QvQPDkCYrGWpZu6bRaKNNVyEGWM9uF0pooh/Lqj/g8QBQgPFEx1vHzW8SyMY9rqg==
 
+hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+  dependencies:
+    react-is "^16.7.0"
+
 home-or-tmp@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmmirror.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -6749,6 +6786,11 @@ image-size@~0.5.0:
   resolved "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
   integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
 
+immer@^9.0.16:
+  version "9.0.16"
+  resolved "https://registry.npmmirror.com/immer/-/immer-9.0.16.tgz#8e7caab80118c2b54b37ad43e05758cdefad0198"
+  integrity sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==
+
 immutable@^4.0.0:
   version "4.1.0"
   resolved "https://registry.npmmirror.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
@@ -9779,7 +9821,7 @@ react-dom@^18.0.0:
     loose-envify "^1.1.0"
     scheduler "^0.23.0"
 
-react-is@^16.13.1:
+react-is@^16.13.1, react-is@^16.7.0:
   version "16.13.1"
   resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -9789,6 +9831,11 @@ react-is@^17.0.2:
   resolved "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
   integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
 
+react-is@^18.0.0:
+  version "18.2.0"
+  resolved "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+  integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
 react-reconciler@0.27.0:
   version "0.27.0"
   resolved "https://registry.npmmirror.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b"
@@ -9797,6 +9844,18 @@ react-reconciler@0.27.0:
     loose-envify "^1.1.0"
     scheduler "^0.21.0"
 
+react-redux@^8.0.5:
+  version "8.0.5"
+  resolved "https://registry.npmmirror.com/react-redux/-/react-redux-8.0.5.tgz#e5fb8331993a019b8aaf2e167a93d10af469c7bd"
+  integrity sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==
+  dependencies:
+    "@babel/runtime" "^7.12.1"
+    "@types/hoist-non-react-statics" "^3.3.1"
+    "@types/use-sync-external-store" "^0.0.3"
+    hoist-non-react-statics "^3.3.2"
+    react-is "^18.0.0"
+    use-sync-external-store "^1.0.0"
+
 react-refresh@^0.11.0:
   version "0.11.0"
   resolved "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
@@ -9928,6 +9987,18 @@ redent@^3.0.0:
     indent-string "^4.0.0"
     strip-indent "^3.0.0"
 
+redux-thunk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
+  integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
+
+redux@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.npmmirror.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
+  integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
+  dependencies:
+    "@babel/runtime" "^7.9.2"
+
 regenerate-unicode-properties@^10.1.0:
   version "10.1.0"
   resolved "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"
@@ -10152,6 +10223,11 @@ requires-port@^1.0.0:
   resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
   integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
 
+reselect@^4.1.7:
+  version "4.1.7"
+  resolved "https://registry.npmmirror.com/reselect/-/reselect-4.1.7.tgz#56480d9ff3d3188970ee2b76527bd94a95567a42"
+  integrity sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==
+
 resize-observer-polyfill@^1.5.1:
   version "1.5.1"
   resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
@@ -11820,6 +11896,11 @@ url-to-options@^1.0.1:
   resolved "https://registry.npmmirror.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
   integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==
 
+use-sync-external-store@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
+  integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
+
 use@^3.1.0:
   version "3.1.1"
   resolved "https://registry.npmmirror.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"