Przeglądaj źródła

initial scaffold

master
hinjin 3 lat temu
rodzic
commit
1c3dee8107
22 zmienionych plików z 1087 dodań i 42 usunięć
  1. +10
    -0
      .babelrc
  2. +12
    -41
      App.js
  3. +6
    -0
      ios/Podfile.lock
  4. +200
    -0
      package-lock.json
  5. +8
    -1
      package.json
  6. +10
    -0
      src/apis/home.js
  7. +120
    -0
      src/common/CustomBasePage.js
  8. +117
    -0
      src/common/CustomPage.js
  9. +76
    -0
      src/common/Utils.js
  10. BIN
      src/icons/home.png
  11. BIN
      src/icons/home_active.png
  12. +20
    -0
      src/store/index.js
  13. +12
    -0
      src/store/reducers/DeviceReducer.js
  14. +12
    -0
      src/store/reducers/LoginReducer.js
  15. +10
    -0
      src/store/reducers/index.js
  16. +49
    -0
      src/utils/CommonUtil.js
  17. +30
    -0
      src/utils/Constants.js
  18. +38
    -0
      src/utils/StorageUtil.js
  19. +106
    -0
      src/utils/index.js
  20. +67
    -0
      src/utils/request.js
  21. +54
    -0
      src/views/HomePage.js
  22. +130
    -0
      src/views/MainPage.js

+ 10
- 0
.babelrc Wyświetl plik

@@ -0,0 +1,10 @@
{
"presets": ["module:metro-react-native-babel-preset"],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/transform-runtime", {
"helpers": true,
"regenerator": false
}]
]
}

+ 12
- 41
App.js Wyświetl plik

@@ -23,51 +23,22 @@ import {
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import {TeaNavigator, TopView} from 'teaset';
import {Provider} from 'react-redux';
import MainPage from './src/views/MainPage';
import configureStore from './src/store/index';
const store = configureStore();

const App: () => React$Node = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<Header />
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step One</Text>
<Text style={styles.sectionDescription}>
Edit <Text style={styles.highlight}>App.js</Text> to change this
screen and then come back to see your edits.
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>See Your Changes</Text>
<Text style={styles.sectionDescription}>
<ReloadInstructions />
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Debug</Text>
<Text style={styles.sectionDescription}>
<DebugInstructions />
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Learn More</Text>
<Text style={styles.sectionDescription}>
Read the docs to discover what to do next:
</Text>
</View>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
<Provider store={store}>
<TopView>
<TeaNavigator rootView={
<MainPage />
}/>
</TopView>
</Provider>
</>
);
};


+ 6
- 0
ios/Podfile.lock Wyświetl plik

@@ -296,6 +296,8 @@ PODS:
- React-Core (= 0.63.3)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- RNCAsyncStorage (1.13.2):
- React
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
@@ -347,6 +349,7 @@ DEPENDENCIES:
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

SPEC REPOS:
@@ -415,6 +418,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/Vibration"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"

@@ -455,6 +460,7 @@ SPEC CHECKSUMS:
React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746
React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
RNCAsyncStorage: bc2f81cc1df90c267ce9ed30bb2dbc93b945a8ee
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a



+ 200
- 0
package-lock.json Wyświetl plik

@@ -364,6 +364,17 @@
"@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-proposal-decorators": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz",
"integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==",
"dev": true,
"requires": {
"@babel/helper-create-class-features-plugin": "^7.12.1",
"@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-decorators": "^7.12.1"
}
},
"@babel/plugin-proposal-export-default-from": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.12.1.tgz",
@@ -437,6 +448,15 @@
"@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-syntax-decorators": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz",
"integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
@@ -2160,6 +2180,14 @@
"chalk": "^3.0.0"
}
},
"@react-native-async-storage/async-storage": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.13.2.tgz",
"integrity": "sha512-isTDvUApRJPVWFxV15yrQSOGqarX7cIedq/y4N5yWSnotf68D9qvDEv1I7rCXhkBDi0u4OJt6GA9dksUT0D3wg==",
"requires": {
"deep-assign": "^3.0.0"
}
},
"@react-native-community/cli-debugger-ui": {
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-4.13.1.tgz",
@@ -2781,6 +2809,14 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"axios": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
"integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
"requires": {
"follow-redirects": "^1.10.0"
}
},
"babel-eslint": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -3407,6 +3443,45 @@
"parse-json": "^4.0.0"
}
},
"create-react-class": {
"version": "15.6.0",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz",
"integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=",
"requires": {
"fbjs": "^0.8.9",
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
},
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
}
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -3499,6 +3574,14 @@
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"deep-assign": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-3.0.0.tgz",
"integrity": "sha512-YX2i9XjJ7h5q/aQ/IM9PEwEnDqETAIYbggmdDB3HLTlSgo1CxPsj6pvhPG68rq6SVE0+p+6Ywsm5fTYNrYtBWw==",
"requires": {
"is-obj": "^1.0.0"
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -4743,6 +4826,11 @@
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
"dev": true
},
"follow-redirects": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -4983,6 +5071,14 @@
"source-map": "^0.7.3"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
@@ -5052,6 +5148,11 @@
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
"integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA=="
},
"immutable": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
"integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
},
"import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
@@ -5378,6 +5479,11 @@
}
}
},
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
},
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -9955,6 +10061,60 @@
}
}
},
"react-native-legacy-components": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/react-native-legacy-components/-/react-native-legacy-components-0.1.3.tgz",
"integrity": "sha512-qiYtJl1xhLeWcHnh19L1MTUCKEOR7RPztuz/20XmI9/1Z4j1DeWHhUo4qZFzYpBPqBa6opFgV+u2Dp61bbRXtg==",
"requires": {
"create-react-class": "15.6.0",
"fbjs": "~0.8.9",
"immutable": "~3.7.6",
"prop-types": "^15.5.10",
"react-timer-mixin": "^0.13.2",
"rebound": "^0.0.13"
},
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
}
}
},
"react-redux": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz",
"integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==",
"requires": {
"@babel/runtime": "^7.12.1",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^16.13.1"
}
},
"react-refresh": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz",
@@ -9972,6 +10132,11 @@
"scheduler": "^0.19.1"
}
},
"react-timer-mixin": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz",
"integrity": "sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q=="
},
"read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -10043,6 +10208,32 @@
"integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==",
"dev": true
},
"rebound": {
"version": "0.0.13",
"resolved": "https://registry.npmjs.org/rebound/-/rebound-0.0.13.tgz",
"integrity": "sha1-SiJSVMr32nVnl7GcWBe/enlB+sE="
},
"redux": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
"integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
},
"dependencies": {
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
}
}
},
"redux-thunk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
"integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
},
"regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -11079,6 +11270,15 @@
}
}
},
"teaset": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/teaset/-/teaset-0.7.5.tgz",
"integrity": "sha512-wWCEvJtNmFm1Pcpcjs7+jP7C9sAGx1va1mBT2dUmjkG8IjL/zA/k//Gvd4HyNwNpg5F3Jexwe8K9S1dK42KIgA==",
"requires": {
"prop-types": "^15.7.2",
"react-native-legacy-components": "^0.1.3"
}
},
"temp": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",


+ 8
- 1
package.json Wyświetl plik

@@ -10,11 +10,18 @@
"lint": "eslint ."
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.13.2",
"axios": "^0.21.0",
"react": "16.13.1",
"react-native": "0.63.3"
"react-native": "0.63.3",
"react-redux": "^7.2.2",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"teaset": "^0.7.5"
},
"devDependencies": {
"@babel/core": "7.12.7",
"@babel/plugin-proposal-decorators": "^7.12.1",
"@babel/runtime": "7.12.5",
"@react-native-community/eslint-config": "1.1.0",
"babel-jest": "25.5.1",


+ 10
- 0
src/apis/home.js Wyświetl plik

@@ -0,0 +1,10 @@
import request from '../utils/request';

// 获取首页列表数据
export function getHomeList(params = {}){
return request({
url: '/v1/homepage',
method: 'GET',
params: {...params}
});
}

+ 120
- 0
src/common/CustomBasePage.js Wyświetl plik

@@ -0,0 +1,120 @@
// BasePage.js

'use strict';

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactNative, {Platform, View, ViewPropTypes} from 'react-native';

import {Theme, KeyboardSpace, TeaNavigator, Toast} from "teaset";

export default class CustomBasePage extends Component {

static propTypes = {
...ViewPropTypes,
scene: PropTypes.object, //转场效果
autoKeyboardInsets: PropTypes.bool, //自动插入键盘占用空间
keyboardTopInsets: PropTypes.number, //插入键盘占用空间顶部偏移,用于底部有固定占用空间(如TabNavigator)的页面
};

static defaultProps = {
...View.defaultProps,
scene: TeaNavigator.SceneConfigs.Replace,
autoKeyboardInsets: Platform.OS === 'ios',
keyboardTopInsets: 0,
};

static contextTypes = {
navigator: PropTypes.func,
};

constructor(props) {
super(props);
this.didMount = false; //代替被废弃的isMounted
this.isFocused = false; //this.state.isFocused move to this.isFocused
this.state = {
};
}

UNSAFE_componentWillMount() {
if (!this.backListener && Platform.OS === 'android') {
let BackHandler = ReactNative.BackHandler ? ReactNative.BackHandler : ReactNative.BackAndroid;
this.backListener = BackHandler.addEventListener('hardwareBackPress', () => this.onHardwareBackPress());
}
}

componentDidMount() {
this.didMount = true;
}

componentWillUnmount() {
if (this.backListener) {
this.backListener.remove();
this.backListener = null;
}
this.didMount = false;
}

get navigator() {
if (!this.context.navigator) {
console.error('The root component is NOT TeaNavigator, then you can not use BasePage.navigator.');
return null;
}
return this.context.navigator();
}

//Call after the scene transition by Navigator.onDidFocus
onDidFocus() {
this.isFocused = true;
}

//Call before the scene transition by Navigator.onWillFocus
onWillFocus() {
}

//Android hardware back key handler, default is pop to prev page
onHardwareBackPress() {
if (!this.context.navigator) return false;
let navigator = this.context.navigator();
if (!navigator) return false;
if (navigator.getCurrentRoutes().length > 1) {
navigator.pop();
return true;
}
if (navigator.getCurrentRoutes().length === 1) {
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
//最近2秒内按过back键,可以退出应用。
ReactNative.BackHandler.exitApp();
return false;
}

this.lastBackPressed = Date.now();
Toast.message('再按一次退出应用');
return true;
}
return false;
}

buildStyle() {
let {style} = this.props;
style = [{
flex: 1,
backgroundColor: Theme.pageColor,
}].concat(style);
return style;
}

renderPage() {
return null;
}

render() {
let {style, children, scene, autoKeyboardInsets, keyboardTopInsets, ...others} = this.props;
return (
<View style={this.buildStyle()} {...others}>
{this.renderPage()}
{autoKeyboardInsets ? <KeyboardSpace topInsets={keyboardTopInsets} /> : null}
</View>
);
}
}

+ 117
- 0
src/common/CustomPage.js Wyświetl plik

@@ -0,0 +1,117 @@
'use strict';

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Platform, View, Dimensions, Text} from 'react-native';
import {BasePage, KeyboardSpace, NavigationBar, Theme, TeaNavigator} from "teaset";
import CustomBasePage from "./CustomBasePage";
import { hairWidth } from '../utils'
import { Color } from 'chalk';


export default class CustomPage extends CustomBasePage {

static propTypes = {
...BasePage.propTypes,
title: PropTypes.string,
backgroundColor: PropTypes.string,
tintColor: PropTypes.string,
titleStyle: Text.propTypes.style,
showBackButton: PropTypes.bool,
navigationBarInsets: PropTypes.bool,
};

static defaultProps = {
...BasePage.defaultProps,
scene: TeaNavigator.SceneConfigs.PushFromRight,
title: null,
backgroundColor: 'transparent',
tintColor: '#FFFFFF',
titleStyle: null,
showBackButton: false,
navigationBarInsets: true,
};

constructor(props) {
super(props);
this.screenWidth = Dimensions.get('window').width;
}

setTitle(title){
this.setState({title});
}
onLayout(e) {
let {width} = Dimensions.get('window');
if (width != this.screenWidth) {
this.screenWidth = width;
this.forceUpdate();
}
this.props.onLayout && this.props.onLayout(e);
}

renderNavigationTitle() {
return this.state.title || this.props.title;
}

renderNavigationLeftView() {
if (!this.props.showBackButton) return null;
return (
<NavigationBar.BackButton
title={this.props.hasOwnProperty('backButtonTitle') ? this.props.backButtonTitle : Theme.backButtonTitle}
onPress={() => {
this.onBackPress ? this.onBackPress() : this.navigator.pop()
}}
/>
);
}

renderNavigationRightView() {
return null;
}

renderNavigationBar() {
return (
<NavigationBar
tintColor={this.props.tintColor}
backgroundColor={this.props.backgroundColor}
titleStyle={this.props.titleStyle}
title={this.renderNavigationTitle()}
leftView={this.renderNavigationLeftView()}
rightView={this.renderNavigationRightView()}
/>
);
}

renderPage() {
return null;
}

render() {
let {style, children, scene, autoKeyboardInsets, keyboardTopInsets, title, showBackButton, navigationBarInsets, ...others} = this.props;

let {left: paddingLeft, right: paddingRight} = Theme.screenInset;
let pageContainerStyle = [{
flex: 1,
paddingLeft,
paddingRight,
marginTop: navigationBarInsets ? (Theme.navBarContentHeight + Theme.statusBarHeight) : 0,
}];

return (
<View style={this.buildStyle()} onLayout={e => this.onLayout(e)} {...others}>
<View style={{flex: 1}}>
<View style={pageContainerStyle}>
{this.renderPage()}
</View>
{this.renderNavigationBar()}
</View>
{autoKeyboardInsets ? <KeyboardSpace topInsets={keyboardTopInsets}/> : null}
</View>
);
}


}



+ 76
- 0
src/common/Utils.js Wyświetl plik

@@ -0,0 +1,76 @@
import {Dimensions, Platform, StatusBar} from "react-native";

const X_WIDTH = 375;
const X_HEIGHT = 812;
const XSMAX_WIDTH = 414;
const XSMAX_HEIGHT = 896;

const {width: D_WIDTH, height: D_HEIGHT} = Dimensions.get('window');

const isIPhoneX = (() => {
if (Platform.OS === 'web') return false;

return (
Platform.OS === 'ios' &&
((D_HEIGHT === X_HEIGHT && D_WIDTH === X_WIDTH) ||
(D_HEIGHT === X_WIDTH && D_WIDTH === X_HEIGHT)) ||
((D_HEIGHT === XSMAX_HEIGHT && D_WIDTH === XSMAX_WIDTH) ||
(D_HEIGHT === XSMAX_WIDTH && D_WIDTH === XSMAX_HEIGHT))
);
})();

const isLandscape = (() => {
return Dimensions.get('window').width > Dimensions.get('window').height;
})();

export function getStatusBarHeight() {
if (Platform.OS === 'ios') {
if (isIPhoneX) return isLandscape ? 0 : 44;
} else if (Platform.OS === 'android') {
if (Platform.Version > 20) return StatusBar.currentHeight;
return 0;
}
return isLandscape ? 0 : 20;
}

export function getBottomOffset() {
if (Platform.OS === 'ios') {
if (isIPhoneX) return isLandscape ? 24 : 34;
} else if (Platform.OS === 'android') {
if (Platform.Version > 20) return 0;
return 0;
}
return isLandscape ? 0 : 20;
}

export function getScreenInset() {
let statusBarHeight = getStatusBarHeight()
return ({
left: isLandscape && isIPhoneX ? 44 : 0,
right: isLandscape && isIPhoneX ? 44 : 0,
top: statusBarHeight,
bottom: isIPhoneX ? (isLandscape ? 24 : 34) : 0,
});
}

export function formatNumber(data) {
if (data != null) {
return data.toFixed(2);
} else {
return null;
}
}

export function desensitizeMobile(mobile) {
if (mobile && mobile.length >= 11) {
return mobile.substr(0, 3) + " **** " + mobile.substr(mobile.length - 4, 4);
}
return mobile;
}

export function desensitizeBankcardNo(bankcardNo) {
if (bankcardNo && bankcardNo.length >= 10) {
return bankcardNo.substr(0, 4) + " **** **** " + bankcardNo.substr(bankcardNo.length - 4, 4);
}
return bankcardNo;
}

BIN
src/icons/home.png Wyświetl plik

Przed Po
Szerokość: 22  |  Wysokość: 22  |  Rozmiar: 450B

BIN
src/icons/home_active.png Wyświetl plik

Przed Po
Szerokość: 22  |  Wysokość: 22  |  Rozmiar: 508B

+ 20
- 0
src/store/index.js Wyświetl plik

@@ -0,0 +1,20 @@
import {applyMiddleware, createStore} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers/index'

export function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
} else {
return state;
}
}
}

const createStoreWithMW = applyMiddleware(thunkMiddleware)(createStore);

export default function configureStore(initialState) {
const store = createStoreWithMW(rootReducer, initialState);
return store;
}

+ 12
- 0
src/store/reducers/DeviceReducer.js Wyświetl plik

@@ -0,0 +1,12 @@
const initialState = {
deviceType: 1,
deviceToken: ''
};

export default function login(state = initialState, action) {
return {
...state,
deviceType: action.deviceType,
deviceToken: action.deviceToken
}
}

+ 12
- 0
src/store/reducers/LoginReducer.js Wyświetl plik

@@ -0,0 +1,12 @@
const initialState = {
token: '',
member: {}
};

export default function login(state = initialState, action) {
return {
...state,
token: action.token,
member: action.member
}
}

+ 10
- 0
src/store/reducers/index.js Wyświetl plik

@@ -0,0 +1,10 @@
import {combineReducers} from "redux";
import login from "./LoginReducer";
import device from "./DeviceReducer";

const rootReducer = combineReducers({
login: login,
device: device
});

export default rootReducer;

+ 49
- 0
src/utils/CommonUtil.js Wyświetl plik

@@ -0,0 +1,49 @@
import React from 'react';
import {ActivityIndicator, DeviceEventEmitter} from "react-native";
import {Theme, Toast} from "teaset";

export default class CommonUtil {

static toast = null

static formatAmount(amount) {
return (amount / 100).toFixed(2) + '';
}

static isMobile(str) {
let reg = /^[1][0-9]{10}$/;
return reg.test(str);
}

static isCardNo(card) {
let reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return reg.test(card);
}

static trim(str) {
if (!str) {
return '';
}
return str.replace(/^(\s*)|(\s*)$/g, '');
}

static showLoading(str) {
CommonUtil.toast = Toast.show({
text: str,
icon: <ActivityIndicator size='large' color={Theme.toastIconTintColor}/>,
position: 'center',
duration: 60000
});
return CommonUtil.toast
}

static hideLoading() {
if (CommonUtil.toast) {
Toast.hide(CommonUtil.toast);
}
}

static logOut(){
DeviceEventEmitter.emit('LOG_OUT');
}
}

+ 30
- 0
src/utils/Constants.js Wyświetl plik

@@ -0,0 +1,30 @@
/*
* @Author: Hinjin
* @Date: 2020-03-14 00:10:21
* @LastEditors: Hinjin
* @LastEditTime: 2020-08-31 18:40:48
* @FilePath: src/utils/Constants.js
*/
export default class Constants {
static VERSION = '1.0.1';

static HOST = '101.133.164.241:8081';

static BASE_URL = 'http://101.133.164.241:8081/';

static KEY_TOEKN = 'Authorization';

static KEY_USER = 'userInfo';

static KEY_USER_NAME = 'userName';
static KEY_PASSWORD = 'password';

static KEY_REMEBER_ME = 'rememberMe';

static THEME_COLOR = '#12AA9C';

static GET_BASE_URL = (path = '') => {
return `http://${window.host || Constants.HOST}/tms-service/api${path}`;
}
}

+ 38
- 0
src/utils/StorageUtil.js Wyświetl plik

@@ -0,0 +1,38 @@
import AsyncStorage from '@react-native-async-storage/async-storage';

export default class StorageUtil {
static saveJson = async (key, value) => {
await this.saveString(key, JSON.stringify(value));
}

/**
* 获取一个Json对象
* @param key
*/
static getJson = async (key, callback) => {
const res = await this.getString(key);
const result = res ? JSON.parse(res) : res;
callback && callback(result);
return result;
}

static saveString = async (key, value) => {
if (key != null && value != null) {
await AsyncStorage.setItem(key, value)
}
return false
}


static getString = async (key, callback) => {
const result = await AsyncStorage.getItem(key)
if (callback) {
callback && callback(result)
}
return result
}

static remove = async(key) => {
return await AsyncStorage.removeItem(key).then(() => {});
}
}

+ 106
- 0
src/utils/index.js Wyświetl plik

@@ -0,0 +1,106 @@
import {Dimensions, Platform, StatusBar, PixelRatio} from "react-native";
import { Toast } from "teaset";


const X_WIDTH = 375;
const X_HEIGHT = 812;
const XSMAX_WIDTH = 414;
const XSMAX_HEIGHT = 896;

const {width: D_WIDTH, height: D_HEIGHT} = Dimensions.get('window');

const isIPhoneX = (() => {
if (Platform.OS === 'web') return false;

return (
Platform.OS === 'ios' &&
((D_HEIGHT === X_HEIGHT && D_WIDTH === X_WIDTH) ||
(D_HEIGHT === X_WIDTH && D_WIDTH === X_HEIGHT)) ||
((D_HEIGHT === XSMAX_HEIGHT && D_WIDTH === XSMAX_WIDTH) ||
(D_HEIGHT === XSMAX_WIDTH && D_WIDTH === XSMAX_HEIGHT))
);
})();

const isLandscape = (() => {
return Dimensions.get('window').width > Dimensions.get('window').height;
})();

export const hairWidth = 1 / PixelRatio.get();

export function getStatusBarHeight() {
if (Platform.OS === 'ios') {
if (isIPhoneX) return isLandscape ? 0 : 44;
} else if (Platform.OS === 'android') {
if (Platform.Version > 20) return StatusBar.currentHeight;
return 0;
}
return isLandscape ? 0 : 20;
}

export function getBottomOffset() {
if (Platform.OS === 'ios') {
if (isIPhoneX) return isLandscape ? 24 : 34;
} else if (Platform.OS === 'android') {
if (Platform.Version > 20) return 0;
return 0;
}
return isLandscape ? 0 : 20;
}

export function getScreenInset() {
let statusBarHeight = getStatusBarHeight()
return ({
left: isLandscape && isIPhoneX ? 44 : 0,
right: isLandscape && isIPhoneX ? 44 : 0,
top: statusBarHeight,
bottom: isIPhoneX ? (isLandscape ? 24 : 34) : 0,
});
}

export function formatNumber(data) {
if (data != null) {
return data.toFixed(2);
} else {
return null;
}
}

export function desensitizeMobile(mobile) {
if (mobile && mobile.length >= 11) {
return mobile.substr(0, 3) + " **** " + mobile.substr(mobile.length - 4, 4);
}
return mobile;
}

export function desensitizeBankcardNo(bankcardNo) {
if (bankcardNo && bankcardNo.length >= 10) {
return bankcardNo.substr(0, 4) + " **** **** " + bankcardNo.substr(bankcardNo.length - 4, 4);
}
return bankcardNo;
}

/**
* 非空校验
* @param {*} checkArr
* @param {*} callback
*/
export function paramsCheck(checkArr, callback) {
let isValid = true;
for (let i = 0; i < checkArr.length; i++) {
// 若字段为空
if (checkArr[i].param) {
isValid = false;
Toast.fail(checkArr[i].tip);
break;
}
}
isValid && callback && callback();
}

/**
* 判断是否是数字
* @param {*} value
*/
export function isNumber(value) {
return typeof value === 'number' && !isNaN(value);
}

+ 67
- 0
src/utils/request.js Wyświetl plik

@@ -0,0 +1,67 @@
/*
* @Author: Hinjin
* @Date: 2020-03-15 19:48:49
* @LastEditors: Hinjin
* @LastEditTime: 2020-09-01 22:56:26
* @FilePath: src/utils/request.js
*/
import Constants from "./Constants";
import axios from 'axios';
import { Platform, AsyncStorage } from "react-native";
import StorageUtil from "./StorageUtil";
import CommonUtil from "./CommonUtil";

axios.defaults.baseURL = Constants.GET_BASE_URL();
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.defaults.headers.common['source'] = Platform.OS === "ios" ? 'app-ios' : 'app-android';

let isGetHost = false;
const request = async ({ url, method = 'GET', params, data, headers }) => {
const token = await StorageUtil.getString(Constants.KEY_TOEKN);
if (!window.host && !isGetHost) {
window.host = await StorageUtil.getString('host');
axios.defaults.baseURL = Constants.GET_BASE_URL();
isGetHost = true;
}
axios.defaults.headers.common['Authorization'] = token;
if (!!headers && headers['Content-Type']) {
axios.defaults.headers.post['Content-Type'] = headers['Content-Type'];
};
const _headers = {
...headers,
Authorization: token
}
return axios({
method,
url,
params,
data,
headers: _headers,
// headers: {
// // 'Content-Type': 'multipart/form-data;',
// Authorization: token
// }
});
}

// 响应拦截器
axios.interceptors.response.use(res => {
const code = res.data.code;
// 登录失效
if (code === 99998) {
CommonUtil.logOut();
return res.data.data;
} else if (code !== 0) {
return Promise.reject(res.data.msg)
} else {
return res.data.data;
}
},
error => {
console.log('err' + error)
return Promise.reject(error)
}
)


export default request;

+ 54
- 0
src/views/HomePage.js Wyświetl plik

@@ -0,0 +1,54 @@
import {BasePage, Carousel, Label, Overlay, Toast, Button} from "teaset";
import React from "react";
import {
ActivityIndicator,
Dimensions,
FlatList,
Image, PermissionsAndroid, Platform,
ScrollView,
AsyncStorage,
StyleSheet,
TouchableOpacity,
TouchableWithoutFeedback,
View,
Text,
RefreshControl,
Animated, Easing, DeviceEventEmitter
} from "react-native";
import {connect} from "react-redux";
import CustomPage from "../common/CustomPage";
import { getHomeList } from '../apis/home';

@connect(
state => ({
}), dispatch => ({
})
)
export default class HomePage extends CustomPage {

static defaultProps = {
...BasePage.defaultProps,
backgroundColor: 'transparent',
titleStyle: {color: '#000', fontWeight: 'bold', fontSize: 18},
title: '会员中心',
};

async componentDidMount() {
this.getData();
}

async getData(){
try{
const res = await getHomeList({page: 1, page_size: 20});
console.log({res});
} catch(e){
console.log({e});
}
}

renderPage(){
return <View style={{flex: 1}}>
</View>
}
}

+ 130
- 0
src/views/MainPage.js Wyświetl plik

@@ -0,0 +1,130 @@
'use strict';

import React from "react";
import {
Alert,
Animated,
DeviceEventEmitter,
Dimensions,
Easing,
Image,
NetInfo,
Platform,
TouchableOpacity,
TouchableWithoutFeedback,
View,
Linking, NativeModules,
} from "react-native";
import {connect} from "react-redux";
import {BasePage, Label, Overlay, TabView, TeaNavigator, Toast} from "teaset";
import CustomBasePage from "../common/CustomBasePage";
import HomePage from "./HomePage";

@connect(
state => ({
loginState: state.login,
}), dispatch => ({
})
)

export default class MainPage extends CustomBasePage {
static defaultProps = {
...BasePage.defaultProps,
scene: TeaNavigator.SceneConfigs.PushFromRight,
};

constructor(props) {
super(props);
}

goLogin() {
// this.navigator.push({
// view: <LoginPage />
// })
}

renderPage() {
let {type, custom, activeIndex, goodsCount} = this.state;
let {loginState} = this.props
let customBarStyle = Platform.OS === 'android' ? null : {
borderTopWidth: 0,
shadowColor: '#ccc',
shadowOffset: {height: -1},
shadowOpacity: 0.4,
shadowRadius: 0.5,
};
return <View style={{flex: 1, position: 'relative'}}>
<TabView style={{flex: 1}} barStyle={custom ? customBarStyle : null} type={type} activeIndex={activeIndex}>
<TabView.Sheet
title='首页'
icon={require('../icons/home.png')}
activeIcon={require('../icons/home_active.png')}
onPress={() => {
this.setState({activeIndex: 0})
}}
>
<HomePage
type={type}
custom={custom}
onChangeType={type => this.setState({type})}
onChangeCustom={custom => this.setState({custom})}
/>
</TabView.Sheet>
<TabView.Sheet
title='分类'category
icon={require('../icons/home.png')}
activeIcon={require('../icons/home_active.png')}
onPress={() => {
this.setState({activeIndex: 1})
}}
>
<HomePage
type={type}
custom={custom}
onChangeType={type => this.setState({type})}
onChangeCustom={custom => this.setState({custom})}
/>
</TabView.Sheet>
<TabView.Sheet
title='会员中心'
icon={require('../icons/home.png')}
activeIcon={require('../icons/home_active.png')}
onPress={() => {
if (!loginState.token) {
this.goLogin()
} else {
this.setState({activeIndex: 2})
}
}}
>
<HomePage
type={type}
custom={custom}
onChangeType={type => this.setState({type})}
onChangeCustom={custom => this.setsState({custom})}
/>
</TabView.Sheet>
<TabView.Sheet
title='我的'
icon={require('../icons/home.png')}
activeIcon={require('../icons/home_active.png')}
onPress={() => {
if (!loginState.token) {
this.goLogin()
} else {
this.setState({activeIndex: 4})
}
}}
// badge={100}
>
<HomePage
type={type}
custom={custom}
onChangeType={type => this.setState({type})}
onChangeCustom={custom => this.setState({custom})}
/>
</TabView.Sheet>
</TabView>
</View>;
}
}

Ładowanie…
Anuluj
Zapisz