Commit 87735636 by GGbong

2.0.0版本

1 parent c0c3eafa
Showing with 1312 additions and 578 deletions
...@@ -6,22 +6,42 @@ ...@@ -6,22 +6,42 @@
* @flow strict-local * @flow strict-local
*/ */
import React from 'react' import React, { useLayoutEffect } from 'react'
import { StatusBar } from 'react-native' import { StatusBar, Platform, Text, TextInput } from 'react-native'
import { SafeAreaProvider } from 'react-native-safe-area-context' import { SafeAreaProvider } from 'react-native-safe-area-context'
import { RouterView } from './src/router' import { RouterView } from './src/router'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { ThemeProvider } from 'rn-ui'
import { defaultTheme } from './src/commo/theme'
import { PrivacyAlert, Update } from './src/components'
import store from './src/store' import store from './src/store'
import SplashScreen from 'react-native-splash-screen'
const App: () => React$Node = () => { //阻止系统缩放
TextInput.defaultProps = Object.assign({}, TextInput.defaultProps, { allowFontScaling: false })
Text.defaultProps = Object.assign({}, Text.defaultProps, { allowFontScaling: false })
const App = () => {
useLayoutEffect(() => {
store.dispatch.site.init()
SplashScreen.hide()
}, [])
return ( return (
<Provider store={store}> <>
<SafeAreaProvider> <PrivacyAlert />
<StatusBar translucent={true} backgroundColor="rgba(0,0,0,0)" barStyle="dark-content" /> {Platform.OS === 'android' && <Update />}
<RouterView /> {Platform.OS === 'ios' && <StatusBar translucent={true} backgroundColor="rgba(255,255,255,0)" barStyle="dark-content" />}
</SafeAreaProvider> <Provider store={store}>
</Provider> <SafeAreaProvider>
); <ThemeProvider theme={defaultTheme}>
}; <RouterView />
</ThemeProvider>
</SafeAreaProvider>
</Provider>
</>
)
}
export default App; export default App
...@@ -1264,6 +1264,14 @@ ...@@ -1264,6 +1264,14 @@
"@types/yargs": "^13.0.0" "@types/yargs": "^13.0.0"
} }
}, },
"@react-native-community/async-storage": {
"version": "1.12.0",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/@react-native-community/async-storage/-/async-storage-1.12.0.tgz",
"integrity": "sha1-0vxlvAiqHD6VFLvp/nCV6rjorKM=",
"requires": {
"deep-assign": "^3.0.0"
}
},
"@react-native-community/cli-debugger-ui": { "@react-native-community/cli-debugger-ui": {
"version": "4.8.0", "version": "4.8.0",
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-4.8.0.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-4.8.0.tgz",
...@@ -2719,6 +2727,14 @@ ...@@ -2719,6 +2727,14 @@
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
}, },
"deep-assign": {
"version": "3.0.0",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/deep-assign/-/deep-assign-3.0.0.tgz",
"integrity": "sha1-yOTE1AHLolVQovD0hqLnW8XyGaI=",
"requires": {
"is-obj": "^1.0.0"
}
},
"deep-is": { "deep-is": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/deep-is/-/deep-is-0.1.3.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/deep-is/-/deep-is-0.1.3.tgz",
...@@ -4469,6 +4485,11 @@ ...@@ -4469,6 +4485,11 @@
} }
} }
}, },
"is-obj": {
"version": "1.0.1",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
},
"is-plain-object": { "is-plain-object": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/is-plain-object/-/is-plain-object-2.0.4.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/is-plain-object/-/is-plain-object-2.0.4.tgz",
...@@ -7881,6 +7902,11 @@ ...@@ -7881,6 +7902,11 @@
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/react-native-screens/-/react-native-screens-2.7.0.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/react-native-screens/-/react-native-screens-2.7.0.tgz",
"integrity": "sha1-LTzzw5pmXpyhx3QmT8zbkOeUQEc=" "integrity": "sha1-LTzzw5pmXpyhx3QmT8zbkOeUQEc="
}, },
"react-native-splash-screen": {
"version": "3.2.0",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz",
"integrity": "sha1-1H7IVXsbqYjuPqmNAUYwgbYP/0U="
},
"react-native-vector-icons": { "react-native-vector-icons": {
"version": "6.6.0", "version": "6.6.0",
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/react-native-vector-icons/-/react-native-vector-icons-6.6.0.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/react-native-vector-icons/-/react-native-vector-icons-6.6.0.tgz",
...@@ -8299,6 +8325,32 @@ ...@@ -8299,6 +8325,32 @@
"glob": "^7.1.3" "glob": "^7.1.3"
} }
}, },
"rn-ui": {
"version": "0.3.17",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/rn-ui/-/rn-ui-0.3.17.tgz",
"integrity": "sha512-vpUeSzUQOsJNAHARxCKSFSShVu818nwZPQjTJ/7vQFAlLzRT5LPkaXKqiRLiGjymYy1+/feQHT8xh+IT77nzsw==",
"requires": {
"deepmerge": "^4.2.2",
"hoist-non-react-statics": "^3.3.2",
"prop-types": "^15.7.2",
"react-native-iphone-x-helper": "^1.2.1"
},
"dependencies": {
"deepmerge": {
"version": "4.2.2",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha1-RNLqNnm49NT/ujPwPYZfwee/SVU="
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "http://47.107.176.206:8089/repository/xbdnpm/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha1-7OCsr3HWLClpwuxZ/v9CpLGoW0U=",
"requires": {
"react-is": "^16.7.0"
}
}
}
},
"rsvp": { "rsvp": {
"version": "4.8.5", "version": "4.8.5",
"resolved": "http://39.108.128.154:8089/repository/xbd-npm/rsvp/-/rsvp-4.8.5.tgz", "resolved": "http://39.108.128.154:8089/repository/xbd-npm/rsvp/-/rsvp-4.8.5.tgz",
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
"mock": "npx nodemon mock/index.js" "mock": "npx nodemon mock/index.js"
}, },
"dependencies": { "dependencies": {
"@react-native-community/async-storage": "^1.12.0",
"@react-native-community/masked-view": "^0.1.10", "@react-native-community/masked-view": "^0.1.10",
"@react-navigation/bottom-tabs": "^5.4.4", "@react-navigation/bottom-tabs": "^5.4.4",
"@react-navigation/native": "^5.3.2", "@react-navigation/native": "^5.3.2",
...@@ -25,8 +26,10 @@ ...@@ -25,8 +26,10 @@
"react-native-reanimated": "^1.8.0", "react-native-reanimated": "^1.8.0",
"react-native-safe-area-context": "^1.0.0", "react-native-safe-area-context": "^1.0.0",
"react-native-screens": "^2.7.0", "react-native-screens": "^2.7.0",
"react-native-splash-screen": "^3.2.0",
"react-native-vector-icons": "^6.6.0", "react-native-vector-icons": "^6.6.0",
"react-redux": "^7.2.0" "react-redux": "^7.2.0",
"rn-ui": "^0.3.17"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.6.2", "@babel/core": "^7.6.2",
......
import { FETCH } from '../libs/fetch'
export const shoppingList = () => {
return FETCH.request({
url: 'mall/goodsInfo/getPageList',
method: 'get'
})
}
\ No newline at end of file
import { FETCH } from '../libs/fetch'
export const findAddr = async () => {
return FETCH.request({
url: '/tms/open/addr/findAddr',
method: 'get'
})
}
export const findAddrVersion = async () => {
return FETCH.request({
url: '/tms/open/addr/findVersion',
method: 'get'
})
}
export const findStreet = async params => {
return FETCH.request({
url: '/tms/addr',
method: 'get',
params
})
}
\ No newline at end of file
import { FETCH } from '../libs/fetch'
import sha256 from 'js-sha256'
/**
* 登陆
* @param {*} loginAccount
* @param {*} loginPassword
* @param {*} operationChannel
*/
export const login = (loginAccount, loginPassword, operationChannel = 0, applyPlatform = [0], applyCarrier = [0]) => {
loginPassword = sha256(loginPassword)
return FETCH.request({
url: '/user/sysLogin/login',
method: 'POST',
data: { loginAccount, loginPassword, operationChannel, applyPlatform, applyCarrier }
})
}
\ No newline at end of file
import { FETCH } from '../libs/fetch'
import { APP } from '../config/app'
export const getTopVersion = (app_code, successCall, errorCall) => {
return FETCH.sourceRequest({
url: `${APP.updateApi}${app_code}`,
}, successCall, errorCall)
}
/**获取版本更新列表 */
export const getVersionList = (successCall, errorCall) => {
return FETCH.sourceRequest({
url: `${APP.appAdminUrl}/api/app_version/${APP.APP_CODE}`
}, successCall, errorCall)
}
\ No newline at end of file
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
No preview for this file type
This diff could not be displayed because it is too large.
import { setUnit } from '../libs/utils'
const Colors = {
primary: '#F77116',
secondary: '#1890FF',
screen: '#f5f6f7',
success: '#38C897',
placeholder: '#C0C5CC',
primaryText: '#333'
}
const TouchStyle = {
/**touchableOpacity 按下时的透明度 */
activeOpacity: 0.8
}
/**@description ui库全局配置 */
const defaultTheme = {
colors: {
primary: Colors.primary
},
XbdButton: {
long: true,
style: {
height: setUnit(99),
marginHorizontal: setUnit(33),
marginVertical: setUnit(42)
},
textStyle: {
fontSize: setUnit(33)
}
},
XbdCell: {
arrowSize: setUnit(33),
style: {
paddingHorizontal: setUnit(33),
paddingVertical: setUnit(33)
},
titleTextStyle: {
color: '#444',
fontSize: setUnit(33)
},
extraTextStyle: {
color: '#676869',
fontSize: setUnit(29)
}
}
}
export { Colors, TouchStyle, defaultTheme }
\ No newline at end of file
import { PrivacyAlert } from './privacy-alert'
import Update from './update'
export {
PrivacyAlert,
Update
}
\ No newline at end of file
import PrivacyAlert from './view'
export { PrivacyAlert }
\ No newline at end of file
import { StyleSheet } from 'react-native'
import { setUnit } from '../../libs/utils'
const styles = StyleSheet.create({
content: {
color: '#333',
fontSize: setUnit(30)
},
privacy: {
color: '#1892FF',
fontSize: setUnit(30)
}
})
export { styles }
\ No newline at end of file
/**
* @author sheng
* @description 隐私弹窗
* @date 2020/09/04
*/
import React, { useCallback, useEffect, useState } from 'react'
import { Text, Linking } from 'react-native'
import { XbdAlertDialog } from 'rn-ui'
import { APP } from '../../config'
import { getStorage, setStorage, setUnit } from '../../libs/utils'
import { styles } from './style'
const Component = () => {
const [visible, setVisible] = useState(false)
useEffect(() => {
showPrivacy()
}, [])
const goPrivacy = useCallback(() => Linking.openURL(APP.privacyLink), [])
const showPrivacy = async () => {
const notFirst = await getStorage(APP.notFirstInstall)
if (notFirst === null)
setVisible(true)
}
const onOk = useCallback(() => {
setVisible(false)
setStorage(APP.notFirstInstall, true)
})
return (
<XbdAlertDialog
visible={visible}
title='温馨提示'
closable={false}
okText={'我知道了'}
maskClosable={false}
onOk={onOk}
style={{ width: setUnit(560) }}
onVisibleChange={visible => setVisible(visible)}
center={
<Text style={styles.content}>{`欢迎来到欢迎来到信巴迪商品交易所, 感谢您对信巴迪的信任和支持!\n`}
为了提供给您更加优质和个性化的服务, 我们会收集或使用您的搜索、浏览与购买等信息。具体内容请您详阅
<Text style={styles.privacy} onPress={goPrivacy}>《商城平台隐私权政策》</Text>
全文, 我们已采用业内先进的信息保护措施, 并会持续优化信息保护技术和安全管理流程, 来保护您的个人信息安全。
</Text>
}
/>
)
}
export default Component
\ No newline at end of file
import View from './view'
export default View
\ No newline at end of file
import { StyleSheet } from 'react-native'
import { setUnit } from '../../libs/utils'
export const styles = StyleSheet.create({
body: {
backgroundColor: 'rgba(0,0,0,0.2)',
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
modal: {
width: setUnit(583),
backgroundColor: '#ffffff',
borderRadius: setUnit(17),
elevation: 1,
shadowOffset: {
width: setUnit(2),
height: setUnit(2)
},
shadowOpacity: 0.3,
shadowColor: '#eeeeee'
},
title: {
paddingTop: setUnit(48),
width: '100%',
textAlign: 'center',
fontSize: setUnit(38),
fontWeight: '500',
color: '#333333'
},
content: {
width: '100%',
paddingLeft: setUnit(25),
paddingRight: setUnit(25),
fontSize: setUnit(31),
color: '#333333',
fontWeight: '300',
paddingTop: setUnit(33)
},
button: {
width: '100%',
height: setUnit(100),
borderTopColor: '#e5e5e5',
borderTopWidth: setUnit(2),
justifyContent: 'center',
alignItems: 'center',
marginTop: setUnit(50)
},
buttonTitle: {
fontSize: setUnit(38),
fontWeight: '500',
color: '#434343'
}
})
import React, { useEffect, useState, useRef, useCallback } from 'react'
import { Alert, Linking, Modal, View, TouchableNativeFeedback, Text } from 'react-native'
import { getVersionCode, setStorage, getStorage } from '../../libs/utils'
import { getTopVersion } from '../../api/version'
import { XbdToast } from 'rn-ui'
import { styles } from './styles'
import { APP } from '../../config'
let http
const Component = () => {
const [show, setShow] = useState(false)
const [version, setVersion] = useState('')
const downUrl = useRef('')
/** 打开下载链接 */
const openDown = () => {
Linking.openURL(downUrl.current)
.catch(() => {
XbdToast.show('打开下载地址失败,请重试')
})
}
/**insterLog */
const insterLog = useCallback(() => setStorage('checkUpdateTime', new Date().getTime() + 86400000), [])
const getUpdate = async () => {
const notFirst = await getStorage(APP.notFirstInstall)
if (new Date().getTime() < (await getStorage('checkUpdateTime') || 0) && notFirst === null) return
const versionCode = await getVersionCode()
http = getTopVersion(APP.APP_CODE, res => {
if (res === null) return
const { version_code, down, version, is_update } = res
downUrl.current = down
if (version_code > versionCode && is_update) {
setShow(true)
setVersion(version)
}
else if (version_code > versionCode)
// 保存查询时间
Alert.alert('重要提示', `发现新版本${version}需要更新,是否更新`, [
{ text: '一天后提醒', onPress: insterLog },
{ text: '取消' },
{ text: '立即更新', onPress: openDown }
])
})
}
useEffect(() => {
setTimeout(() => getUpdate(), 3000)
return () => http && http.cancel()
}, [])
return (
<Modal visible={show} transparent={true} style={styles.body}>
<View style={styles.body}>
<View style={styles.modal}>
<Text style={styles.title}>重要提示</Text>
<Text style={styles.content}>发现新版本{version},需要更新才能继续使用。请更新后重新进入APP</Text>
<TouchableNativeFeedback onPress={openDown}>
<View style={styles.button}>
<Text style={styles.buttonTitle}>立即更新</Text>
</View>
</TouchableNativeFeedback>
</View>
</View>
</Modal>
)
}
export default Component
\ No newline at end of file
const isBuildDebug = process.env.NODE_ENV === 'development' || false
const APP = { const APP = {
/** @description token存储的名称 */ /** @description token存储的名称 */
tokenName: 'jwt', tokenName: 'jwt',
...@@ -6,36 +8,71 @@ const APP = { ...@@ -6,36 +8,71 @@ const APP = {
tokenExpires: 7, tokenExpires: 7,
/**@description 初始的HTTP请求的全局header头*/ /**@description 初始的HTTP请求的全局header头*/
header: { }, header: {},
/**@description API基础请求地址*/ /**@description API基础请求地址*/
baseUrl: { baseUrl: isBuildDebug ? `https://gateway-dev.b2bwings.com/` : 'https://gateway.b2bwings.com' ,
/**@description mock代理的地址*/
dev: {
prot: 9000,
host: '192.168.1.141', // 替换为本地IP
origin: () => `http://${APP.baseUrl.dev.host}:${APP.baseUrl.dev.prot}`
},
/**@description 开放环境请求地址*/
proxy: 'http://gateway-dev.b2bwings.com',
/**@description 生产环境请求地址*/
pro: 'https://gateway.b2bwings.com'
},
/**@description 应用的登陆API的PATH*/ /**@description 应用的登陆API的PATH*/
loginPath: '/user/sysLogin/login', loginPath: '/user/sysLogin/login',
/**@description 401页面地址,用于提醒用户登陆状态失效*/ /**@description 401页面地址,用于提醒用户登陆状态失效*/
error404: '/404', error404: '/404',
/**@description 静态资源目录,必须。用于菜单图标显示*/ /**@description 静态资源目录,必须。用于菜单图标显示*/
STATIS_URL: 'https://b2bwings-system-image.oss-cn-shenzhen.aliyuncs.com/', STATIS_URL: 'https://b2bwings-system-image.oss-cn-shenzhen.aliyuncs.com/',
/**@description 静态资源目录,必须。图片地址*/
IMG_URL: 'https://images.b2bwings.com/',
/**@description tab按钮颜色配置*/ /**@description tab按钮颜色配置*/
tabConfig: { tabConfig: {
activeTintColor: '#6E4DB3', activeTintColor: '#F77116',
inactiveTintColor: 'gray' inactiveTintColor: '#434343'
} },
/** @description 地址库存储名称 */
siteCacheName: '_site',
/** @description 地址库存储版本名称 */
siteVersionName: '_site_version',
/** @description 运营渠道 0=信巴迪 100=众家联 */
operationChannel: 0,
/** @description 适用平台:0=业务平台 1=运管平台 */
applyPlatform: [0],
/** @description 适用前端 0=web 1=app 3=小程序 */
applyCarrier: [1],
/** @description 配置发送邮件接口中的跳转链接(link参数),找回密码用*/
emailLinkFindPassword: isBuildDebug ? 'https://xbd-dev.b2bwings.com/findPassword' : 'https://xbd.b2bwings.com/findPassword',
/** @description 配置发送邮件接口中的跳转链接(link参数),重置邮箱用 */
emailLinkSetEmail: isBuildDebug ? 'https://xbd-dev.b2bwings.com/setMail' : 'https://xbd.b2bwings.com/setMail',
/**@description 用户信息本地存储名称 */
authCacheName: '_auth',
/**@description 查询版本更新的API地址 */
updateApi: isBuildDebug ? 'http://app-admin-dev.b2bwings.com/api/app/version/' : 'http://app-admin.b2bwings.com/api/app/version/',
/**@description app版本管理url */
appAdminUrl: isBuildDebug ? 'http://app-admin-dev.b2bwings.com' : 'http://app-admin.b2bwings.com',
/**@public APP编码 */
APP_CODE: isBuildDebug ? '' : '',
/**@description 隐私政策链接 */
privacyLink: 'https://app-static.b2bwings.com/static/page/privacy-policy/PrivacyPolicy.html',
/**@description 是否首次安装进入app */
notFirstInstall: '_not_first_install'
} }
exports.APP = APP export {
\ No newline at end of file APP,
isBuildDebug
}
export { APP } from './app' export { APP } from './app'
export { FORM } from './form'
export const UI_WIDTH = 750 export const UI_WIDTH = 750
\ No newline at end of file
/**
* @author sheng
* @date 2020/06/08
* @description 表单数据校验
*/
class CheckUtil {
/**校验邮箱格式 */
static isEmail(str) {
let reg = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
return reg.test(str)
}
/**校验手机号 */
static isMobile(str) {
let reg = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
return reg.test(str)
}
/**校验用户账号 */
static checkAccount(str) {
let reg = /^\d{9}$/
return reg.test(str)
}
}
export default CheckUtil
\ No newline at end of file
import http from './http-request' import http from './http-request'
import { APP } from '../config' import { APP } from '../config'
console.log(APP.baseUrl.dev.origin()) const FETCH = new http(APP.baseUrl)
const FETCH = new http(process.env.NODE_ENV === 'development' ?
APP.baseUrl.dev.origin() :
APP.baseUrl.pro
)
export { FETCH } export { FETCH }
\ No newline at end of file
/**
* @description 手机号格式化显示 123****5678
* @param {String|Number} num
* @returns {String}
*/
export function formatPhoneNo(num) {
return num.toString().replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
}
/**
* 格式化Email为 h****@b2bwings.com 的格式
* @param {*} str
*/
export function formatEmail(str) {
if (!str) {
return ''
}
let i = str.indexOf('@')
if (i <= 0) {
return ''
}
let start = str.substring(0, 1)
let end = str.substring(i)
return `${start}****${end}`
}
/**
* @description 数字千分位
* @param {number|string} num
* @author sheng
* @date 2020/04/26
*/
export function thousandBitSeparator(num) {
return num && (num.toString().indexOf('.') != -1
? num.toString().replace(/(\d)(?=(\d{3})+\.)/g, function ($0, $1) {
return $1 + ",";
}) : num.toString().replace(/(\d)(?=(\d{3})+$)/g, function ($0, $1) {
return $1 + ",";
}));
}
/**
* 获取该日开始时间 零时零分零秒
* @param {number} stmp 毫秒时间戳
* @returns {number} 毫秒时间戳
*/
export function getDateStartTime(stmp) {
return new Date(new Date(Number(stmp)).toLocaleDateString()).getTime()
}
/**
* 获取该日结束时间 23时59分59秒
* @param {date} date 毫秒时间戳
* @returns {number} 毫秒时间戳
*/
export function getDateEndTime(stmp) {
return new Date(
new Date(new Date(Number(stmp)).toLocaleDateString()).getTime() +
24 * 60 * 60 * 1000 -
1
)
}
/**
* 日期格式化
* @param {*} fmt
* @param {*} time 毫秒时间戳
*/
export function dateFormat(fmt, time) {
let ret
let newDate = new Date() //实例化一个Date对象
newDate.setTime(time)
const opt = {
"Y+": newDate.getFullYear().toString(), // 年
"M+": (newDate.getMonth() + 1).toString(), // 月
"D+": newDate.getDate().toString(), // 日
"h+": newDate.getHours().toString(), // 时
"m+": newDate.getMinutes().toString(), // 分
"s+": newDate.getSeconds().toString() // 秒
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt)
if (ret) {
fmt = fmt.replace(ret[1], (ret[1].length == 1) ?
(opt[k]) : (opt[k].padStart(ret[1].length, "0")))
}
}
return fmt
}
/**
* 分钱转换为元
* @param {*} num
*/
export function fenToYuan(num) {
return (Number(num) / 100).toFixed(2)
}
/**
* 请求参数格式化为字符串
* @param {Object} query
* @author sheng
* @date 2020/06/23
*/
export function formatQuery(query) {
if (typeof query !== 'object') {
return ''
}
let params = []
if (query) {
for (let item in query) {
let vals = query[item]
if (vals !== undefined) {
params.push(item + '=' + query[item])
}
}
}
return params.length ? '?' + params.join('&') : ''
}
/**minutes */
export function getRemainTime(minutes) {
/**剩余小时 */
let hour = Math.floor(minutes / 60)
/**剩余分钟 */
let min = Math.round(minutes % 60)
return hour <= 0 ? `${min}分钟` : `${hour}小时${min}分钟`
}
\ No newline at end of file
import { Alert } from 'react-native'
import axios, { CancelToken } from 'axios' import axios, { CancelToken } from 'axios'
import { APP } from '../config' import { APP } from '../config'
...@@ -23,7 +22,6 @@ class HttpRequest { ...@@ -23,7 +22,6 @@ class HttpRequest {
instance.interceptors.request.use(config => { instance.interceptors.request.use(config => {
return config return config
}, error => { }, error => {
if (process.env.NODE_ENV === 'development') console.warn(error)
return Promise.reject(error) return Promise.reject(error)
}) })
...@@ -33,10 +31,8 @@ class HttpRequest { ...@@ -33,10 +31,8 @@ class HttpRequest {
return this.handleRespone(res) return this.handleRespone(res)
}, error => { }, error => {
if (process.env.NODE_ENV === 'development') console.warn(error) if (process.env.NODE_ENV === 'development') console.warn(error)
return Promise.reject(error.response ? `发生错误,错误代码:${error.response.status};详情:${error.response.message || ''}` : error.message + "")
return Promise.reject(error.response ? `发生错误,错误代码:${error.response.status};详情:${error.response.message}` : error.message + "")
}) })
} }
...@@ -87,29 +83,16 @@ class HttpRequest { ...@@ -87,29 +83,16 @@ class HttpRequest {
const { code, message, data } = res.data const { code, message, data } = res.data
switch(code) { switch(code) {
case 200: case 200:
if (res.config.url === this.loginUrl) this.saveLoginInfo(data.jwt, data.user.vid, res.config.data) //保存登陆数据
return data return data
case 401: case 401:
Alert.alert('登陆状态失效,请重新登陆') return Promise.reject(`错误: ${code};详情:未登录或登录状态已过期`)
break
case 403: case 403:
return Promise.reject(`发生错误,错误代码: ${code};详情:无权限使用此功能`) return Promise.reject(`错误: ${code};详情:无权限使用此功能`)
default: default:
if (process.env.NODE_ENV === 'development') console.warn(JSON.stringify(res)) if (process.env.NODE_ENV === 'development') console.warn(JSON.stringify(res.config))
return Promise.reject(`发生错误,错误代码: ${code};详情:${message}`) return Promise.reject(`错误: ${code};详情:${message || '未知原因'}`)
} }
} }
/**
* 保存登陆数据
* @param {*} jwt token
* @param {*} data 请求数据体
*/
saveLoginInfo(jwt, vid, data) {
data = JSON.parse(data)
const { applyCarrier, applyPlatform } = data
// store.commit('SYSTEM_LOGIN', { jwt , vid, applyCarrier, applyPlatform})
}
} }
export default HttpRequest export default HttpRequest
import { Dimensions } from 'react-native' import { Dimensions } from 'react-native'
import { UI_WIDTH } from '../config' import { UI_WIDTH } from '../config'
import DeviceInfo from 'react-native-device-info' import DeviceInfo from 'react-native-device-info'
import { CommonActions } from '@react-navigation/native'
import AsyncStorage from '@react-native-community/async-storage'
import CheckUtil from './check'
import { APP } from '../config'
import { FETCH } from './fetch'
/** 窗口属性 */ /** 窗口属性 */
const WINDOWS = Dimensions.get('window') export const WINDOWS = Dimensions.get('window')
/** UI图缩放比例 */ /** UI图缩放比例 */
const UI_SACEL = WINDOWS.width / UI_WIDTH const UI_SACEL = WINDOWS.width / UI_WIDTH
...@@ -13,7 +18,7 @@ const UI_SACEL = WINDOWS.width / UI_WIDTH ...@@ -13,7 +18,7 @@ const UI_SACEL = WINDOWS.width / UI_WIDTH
* @param {*} num * @param {*} num
*/ */
export const setUnit = num => { export const setUnit = num => {
return num * UI_SACEL return num * UI_SACEL
} }
/** 获取版本号 */ /** 获取版本号 */
...@@ -21,3 +26,144 @@ export const getVersionCode = async () => await DeviceInfo.getBuildNumber() ...@@ -21,3 +26,144 @@ export const getVersionCode = async () => await DeviceInfo.getBuildNumber()
/** 获取版本名称 */ /** 获取版本名称 */
export const getVersion = async () => await DeviceInfo.getVersion() export const getVersion = async () => await DeviceInfo.getVersion()
/** 获取屏幕高度 */
export const getHeight = () => WINDOWS.height
/** 存储数据 */
export const setStorage = async (key, value) => {
try {
const data = {
type: typeof value,
data: value
}
if (data.type !== 'string')
switch (data.type) {
case 'number':
data.data += ''
break
case 'undefined':
data.data = ''
break
case 'boolean':
data.data = Number(data.data) + ''
break
default:
data.data = JSON.stringify(data.data)
break
}
await AsyncStorage.setItem(key, JSON.stringify(data))
} catch (error) {
return null
}
}
/** 获取数据 */
export const getStorage = async key => {
try {
const data = JSON.parse(await AsyncStorage.getItem(key))
switch (data.type) {
case 'string':
return data.data
case 'number':
return Number(data.data)
case 'undefined':
return undefined
case 'boolean':
return Boolean(Number(data.data))
default:
return JSON.parse(data.data)
}
} catch (error) {
return null
}
}
/**
* 分单位的金额转换成元单位
* @param {[type]} fen [description]
* @return {[type]} [description]
*/
export function fen2yuan(fen) {
if (typeof fen === 'string') fen = Number(fen)
let y = fen / 100
if (Number.isNaN(y))
return '金额错误'
else
return y.toFixed(2)
}
/**
* 校验手机号和验证码
* @param {string} phone
* @returns {Object}
*/
export const checkPhoneAndCode = (phone, code) => {
if (!phone.trim().length) {
return { status: 0, msg: '手机号不能为空' }
}
if (!CheckUtil.isMobile(phone)) {
return { status: 0, msg: '手机号格式不正确' }
}
if (!code.trim().length) {
return { status: 0, msg: '验证码不能为空' }
}
return { status: 1, msg: '' }
}
/**
* 校验两次输入的邮箱
* @param {*} email
* @param {*} confirmEmail
* @returns {Object}
*/
export const checkEmails = (email, confirmEmail) => {
if (!email.trim().length) {
return { status: 0, msg: '邮箱不能为空' }
}
if (!CheckUtil.isEmail(email)) {
return { status: 0, msg: '邮箱格式不正确' }
}
if (email !== confirmEmail) {
return { status: 0, msg: '两次输入邮箱不一致' }
}
return { status: 1, msg: '' }
}
/**
* 判断token是否过期
* @param {*} saveTime token存储时间
*/
export const isTokenExpired = (saveTime) => {
let now = new Date().getTime()
return now - saveTime > APP.tokenExpires * 24 * 60 * 60 * 1000
}
/**
* 设置网络请求头
* @param {*} jwt
* @param {*} sessionId
*/
export const setHeader = (jwt, sessionId) => {
FETCH.setHeader('Authorization', jwt)
FETCH.setHeader('Sessionid', sessionId)
}
/**
* 重置路由栈
* @param {*} navigation
*/
export const resetRoutes = navigation => {
navigation.dispatch(
CommonActions.reset({
index: 1,
routes: [
{ name: 'Index' },
{ name: 'Login' }
],
})
)
}
import React from 'react' import React from 'react'
import { StyleSheet, Platform } from 'react-native'
import { Colors } from 'react-native/Libraries/NewAppScreen' import { Colors } from 'react-native/Libraries/NewAppScreen'
import { CardStyleInterpolators } from '@react-navigation/stack' import { CardStyleInterpolators } from '@react-navigation/stack'
import { setUnit } from '../libs/utils' import { setUnit } from '../libs/utils'
import { Icons } from '../assets/icons' import { Icons } from '../assets/icons'
const headerStyle = { const headerStyle = {
headerTitleAlign: 'center', // 文字居中 headerTitleAlign: 'center', // 文字居中
headerStyle: { headerStyle: {
backgroundColor: Colors.white borderBottomColor: '#eee',
borderBottomWidth: StyleSheet.hairlineWidth,
backgroundColor: Colors.white ,
elevation: 0
}, // 标题栏样式 }, // 标题栏样式
headerTintColor: '#333333', // 标题栏字体颜色 headerTintColor: '#262626', // 标题栏字体颜色
headerTitleStyle: { headerTitleStyle: {
fontSize: setUnit(38) fontSize: setUnit(38)
}, },
headerBackImage: () => (<Icons name="zuo" size={30} color="#333" />), // 标题栏返回图标 headerShown: false,
headerBackImage: Platform.OS === 'ios' ? undefined : () => <Icons name="fanhui" size={setUnit(33)} color="#262626" />, // 标题栏返回图标
headerBackTitleVisible: false,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, // 设置左右滑动 cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, // 设置左右滑动
gestureDirection: 'horizontal', // 初始化右滑手势配置 gestureDirection: 'horizontal', // 初始化右滑手势配置
gestureEnabled: true // 启用右滑返回 gestureEnabled: true, // 启用右滑返回
gestureResponseDistance: { horizontal: 20 }
} }
export { headerStyle } const headerRightButton = StyleSheet.create({
\ No newline at end of file text: {
fontWeight: '400',
fontSize: setUnit(29),
color: '#6c6c6c',
paddingRight: setUnit(25)
}
})
export { headerStyle, headerRightButton }
\ No newline at end of file
// 路由配置 // 路由配置
import React, { createRef } from 'react' import React from 'react'
import { NavigationContainer } from '@react-navigation/native' import { NavigationContainer } from '@react-navigation/native'
import { Route } from './route' import { Route } from './route'
import { navigateRef } from './ref'
const navigateRef = createRef()
/** 路由视图 */ /** 路由视图 */
const RouterView: () => React$Node = () => { const RouterView = () => {
return ( return (
<NavigationContainer ref={navigateRef}> <NavigationContainer ref={navigateRef}>
<Route /> <Route />
...@@ -14,13 +13,4 @@ const RouterView: () => React$Node = () => { ...@@ -14,13 +13,4 @@ const RouterView: () => React$Node = () => {
) )
} }
/**
* 直接导航方式,用于无法访问导航道具场景下使用
* @param {*} name
* @param {*} params
*/
export const navigate = (name, params) => {
navigateRef.current?.navigate(name, params)
}
export { RouterView } export { RouterView }
import React from 'react'
export const navigateRef = React.createRef()
/**
* 直接导航方式,用于无法访问导航道具场景下使用
* @param {*} name
* @param {*} params
*/
export const navigate = (name, params = {}) => {
navigateRef.current?.navigate(name, params)
}
...@@ -3,21 +3,26 @@ import React from 'react' ...@@ -3,21 +3,26 @@ import React from 'react'
import { createStackNavigator } from '@react-navigation/stack' import { createStackNavigator } from '@react-navigation/stack'
import { headerStyle } from '../config' import { headerStyle } from '../config'
import { TabRouter } from '../tab' import { TabRouter } from '../tab'
import { Error404 } from '../../views/error/404'
import Login from '../../views/login' import About from '../../views/about'
import Shopping from '../../views/shopping' import Privacy from '../../views/about/privacy'
import Play from '../../views/play' import Features from '../../views/about/features'
import FeatureDetail from '../../views/about/feature-detail'
const Stack = createStackNavigator() const Stack = createStackNavigator()
const Route: () => React$Node = () => { const Route = () => {
return ( return (
<Stack.Navigator initialRouteName="Index" screenOptions={{ ...headerStyle }} mode={ 'card' } > <Stack.Navigator initialRouteName="Index" screenOptions={{ ...headerStyle }} mode={'card'} >
<Stack.Screen options={{ headerShown: false }} name="Index" component={ TabRouter } /> <Stack.Screen name="Index" options={{ title: '首页' }} component={ TabRouter } />
<Stack.Screen name="Error_404" component={ Error404 } /> {/* 关于 */}
<Stack.Screen name="login" options={{ title: '登陆' }} component={ Login } /> <Stack.Screen name='About' options={{ title: '', headerShown: true, headerStyle: {elevation: 0, backgroundColor: '#fff'} }} component={About}/>
<Stack.Screen name="shopping" options={{ title: '商品详情' }} component={ Shopping } /> {/* 隐私协议与政策 */}
<Stack.Screen name="play" options={{ title: '支付' }} component={ Play } /> <Stack.Screen name='Privacy' options={{title: '隐私协议', headerShown: true}} component={Privacy}/>
{/* 功能介绍 */}
<Stack.Screen name='Features' options={{ headerShown: true, title: '功能介绍'}} component={Features}/>
{/* 功能介绍 */}
<Stack.Screen name='FeatureDetail' options={{ headerShown: true, title: '', headerStyle: {elevation: 0, backgroundColor: '#fff'}}} component={FeatureDetail}/>
</Stack.Navigator> </Stack.Navigator>
) )
} }
......
...@@ -2,36 +2,28 @@ ...@@ -2,36 +2,28 @@
import React from 'react' import React from 'react'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { APP } from '../../config' import { APP } from '../../config'
import Home from '../../views/home'
import { Icons } from '../../assets/icons'
import { Home } from '../../views/home'
import Me from '../../views/me'
const Tab = createBottomTabNavigator() // 创建tab栈堆 const Tab = createBottomTabNavigator() // 创建tab栈堆
/** 选项配置函数 */ /** 选项配置函数 */
const tabScreen = props => { const tabScreen = ({ route }) => ({
const route = props.route tabBarIcon: ({ focused, size }) => {
return { switch (route.name) {
tabBarIcon: ({color, size}) => { case 'home':
switch (route.name) { return null
case 'home':
return <Icons name="shouye" color={ color } size={ size } />
case 'me':
return <Icons name="qudaoshangguanli" color={ color } size={ size } />
}
}
} }
} }
})
const TabRouter: () => React$Node = () => { const TabRoute = () => {
return ( return (
<Tab.Navigator screenOptions={ tabScreen } tabBarOptions={ APP.tabConfig }> <Tab.Navigator backBehavior={'none'} screenOptions={tabScreen} tabBarOptions={APP.tabConfig}>
<Tab.Screen name="home" component={ Home } /> <Tab.Screen options={{ title: '首页' }} name="home" component={Home} />
<Tab.Screen name="me" component={ Me } /> </Tab.Navigator>
</Tab.Navigator> )
)
} }
export { TabRouter }
\ No newline at end of file export default TabRoute
\ No newline at end of file
var STORE_CONTEXT = null
export const createConetxt = store => STORE_CONTEXT = store
export const getStore = () => STORE_CONTEXT
\ No newline at end of file
import { init } from '@rematch/core' import { init } from '@rematch/core'
import models from './modules' import models from './modules'
import { createConetxt } from './context'
const store = init({ const store = init({
models models
}) })
createConetxt(store)
export const { dispatch, getState } = store export const { dispatch, getState } = store
export default store export default store
\ No newline at end of file
import user from './user' import site from './site'
export default { export default {
user site
} }
\ No newline at end of file
import { connect } from 'react-redux'
const mapState = state => state.site
const mapDispatch = dispatch => ({})
export default connect(mapState, mapDispatch)
import { getStorage, setStorage } from '../../../libs/utils'
import { APP } from '../../../config'
import { findAddr, findAddrVersion } from '../../../api/site'
export default {
state: {
data: []
},
reducers: {
save(_, data) {
return { data }
},
update(_, data) {
return { data }
}
},
effects: {
async init(_, rootState) {
const data = rootState.site.data
if (data.length !== 0) return
// 查询缓存库
const cacheList = await getStorage(APP.siteCacheName) || []
if (cacheList.length !== 0) { // 缓存存在数组
this.save(cacheList)
findAddrVersion()
.then(async resVersion => {
const version = await getStorage(APP.siteVersionName)
if (version != resVersion) this.httpGet(resVersion)
})
.catch(err => {})
}
else // 网络获取
this.httpGet()
},
async httpGet(version) {
findAddr()
.then(async res => {
if (version === undefined) { // 网络获取版本号
findAddrVersion()
.then(res => setStorage(APP.siteVersionName, res))
.catch(err => {})
}
setStorage(APP.siteCacheName, res)
this.update(res)
})
.catch(err => {})
}
}
}
\ No newline at end of file
export default {
// 数据
state: {
userInfo: false,
user: {
name: '',
phone: ''
}
},
// 同步
reducers: {
loginSuccess(state, user) {
return {
userInfo: true,
user: user
}
}
},
// 异步
effects: {
}
}
\ No newline at end of file
import { StyleSheet } from 'react-native'
import { setUnit } from '../../../libs/utils'
const styles = StyleSheet.create({
container: {
flex: 1,
padding: setUnit(32),
backgroundColor: '#fff'
},
desc: {
color: '#434343',
fontSize: setUnit(24)
}
})
export { styles }
\ No newline at end of file
/**
* @author sheng
* @description 功能介绍详情页
* @date 2020/09/03
*/
import React, { useLayoutEffect } from 'react'
import { View, Text } from 'react-native'
import { styles } from './style'
import { Icons } from '../../../assets/icons'
import { setUnit } from '../../../libs/utils'
const Page = ({route, navigation }) => {
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<Icons
name='guanbi'
color='#555'
size={setUnit(32)}
style={{ paddingHorizontal: setUnit(24) }}
onPress={() => navigation.goBack()}
/>
)
})
}, [navigation])
return (
<View style={styles.container}>
<Text style={styles.desc}>{route.params?.content}</Text>
</View>
)
}
export default Page
\ No newline at end of file
import Page from './view'
export default Page
\ No newline at end of file
import { StyleSheet } from 'react-native'
import { setUnit } from '../../../libs/utils'
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff'
},
item: {
paddingHorizontal: setUnit(25),
paddingVertical: setUnit(16),
alignItems: 'center',
flexDirection: 'row'
},
content: {
flex: 1
},
title: {
fontSize: setUnit(28),
color: '#434343'
},
subtitle: {
color: '#8c8c8c',
fontSize: setUnit(24)
},
emptyText: {
fontSize: setUnit(33),
color: '#434343'
},
containerStyle: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
export { styles }
\ No newline at end of file
/**
* @author sheng
* @description 功能介绍列表页
* @date 2020/09/03
*/
import React, { memo, useState, useLayoutEffect, useCallback, useEffect } from 'react'
import { Text, View, FlatList, Pressable, InteractionManager } from 'react-native'
import { Icons } from '../../../assets/icons'
import { styles } from './style'
import { dateFormat, setUnit } from '../../../libs/utils'
import { XbdDivider, XbdLoading } from 'rn-ui'
import { getVersionList } from '../../../api/version'
import { ListEmpty } from '../../../components'
const Item = memo(({ navigation, item }) => {
const { version, create_time, remark } = item
return (
<>
<Pressable
style={styles.item}
android_ripple={{ color: '#eee' }}
onPress={() => { navigation.navigate('FeatureDetail', {content: remark || ''}) }}
>
<View style={styles.content}>
<Text style={styles.title}>商品交易所{version}主要更新</Text>
<Text style={styles.subtitle}>{dateFormat('MM/DD', new Date(create_time).getTime())}</Text>
</View>
<Icons name='you' size={setUnit(32)} color='#C2C3C4' />
</Pressable>
<XbdDivider style={{ marginLeft: setUnit(25) }} />
</>
)
})
let http
const Page = ({ navigation }) => {
const [records, setRecords] = useState([])
const [loading, setLoading] = useState(false)
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<Icons
name='guanbi'
color='#555'
size={setUnit(32)}
style={{ paddingHorizontal: setUnit(24) }}
onPress={() => navigation.goBack()}
/>
)
})
}, [navigation])
useEffect(() => {
fetchData()
return () => {
http && http.cancel()
}
}, [])
const fetchData = useCallback(() => {
InteractionManager.runAfterInteractions(() => {
setLoading(true)
http = getVersionList(res => {
setRecords(res?.reverse() || [])
setLoading(false)
}, err => {
setLoading(false)
alert(err)
})
})
}, [])
return (
<View style={styles.container}>
<FlatList
data={records}
contentContainerStyle={!records.length ? styles.containerStyle : {}}
keyExtractor={(item, index) => item.id?.toString()}
renderItem={({ item }) => <Item navigation={navigation} item={item} />}
ListEmptyComponent={() => <ListEmpty/>}
/>
<XbdLoading visible={loading}/>
</View>
)
}
export default Page
\ No newline at end of file
import Page from './view'
export default Page
\ No newline at end of file
import Page from './view'
export default Page
\ No newline at end of file
import { StyleSheet } from 'react-native'
const styles = StyleSheet.create({
wrapper: {
position: 'absolute',
left: 0,
bottom: 0,
right: 0,
top: 0,
justifyContent: 'center',
alignItems: 'center'
}
})
export { styles }
\ No newline at end of file
/**
* @author sheng
* @description 隐私政策
* @date 2020/09/03
*/
import React from 'react'
import { View, ActivityIndicator } from 'react-native'
import WebView from 'react-native-webview'
import { Colors } from '../../../commo/theme'
import { APP } from '../../../config'
import { styles } from './style'
const Page = () => {
const indicator = () => {
return (
<View style={styles.wrapper}>
<ActivityIndicator size={'large'} color={Colors.primary} />
</View>
)
}
return (
<WebView
startInLoadingState={true}
renderLoading={indicator}
source={{ uri: APP.privacyLink }}
/>
)
}
export default Page
\ No newline at end of file
import { StyleSheet } from 'react-native'
import { setUnit } from '../../libs/utils'
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff'
},
header: {
marginTop: setUnit(112),
alignItems: 'center'
},
logo: {
width: setUnit(116),
height: setUnit(116)
},
name: {
marginTop: setUnit(56),
color: '#262626',
fontSize: setUnit(40)
},
version: {
marginTop: setUnit(12),
color: '#262626',
fontSize: setUnit(32)
},
center: {
marginTop: setUnit(56)
},
footer: {
alignItems: 'center',
paddingBottom: setUnit(48)
},
private: {
fontSize: setUnit(26),
color: '#6292D0'
},
copyright: {
marginTop: setUnit(24),
textAlign: 'center',
color: '#8c8c8c',
}
})
export { styles }
\ No newline at end of file
/**
* @author sheng
* @description 关于信巴迪
* @date 2020/09/03
*/
import React, { useCallback, useEffect, useState, useRef } from 'react'
import { View, Image, Text, Alert, Linking } from 'react-native'
import { XbdCell, XbdDivider, XbdToast } from 'rn-ui'
import { styles } from './style'
import { getVersion, getVersionCode, setUnit } from '../../libs/utils'
import { getTopVersion } from '../../api/version'
import { APP } from '../../config'
let http
const Page = ({ navigation }) => {
const [version, setVersion] = useState('')
const downUrl = useRef('')
useEffect(() => {
getCurVersion()
return () => http && http.cancel()
}, [])
/** 打开下载链接 */
const openDown = () => {
Linking.openURL(downUrl.current)
.then(() => { })
.catch(err => {
XbdToast.show('打开下载地址失败,请重试')
})
}
const getUpdate = async () => {
const versionCode = await getVersionCode()
http = getTopVersion(APP.APP_CODE, res => {
if (res === null) return
const { version_code, down, version } = res
downUrl.current = down
if (version_code > versionCode) {
Alert.alert('提示', `发现新版本${version}需要更新,是否更新`, [
{ text: '取消' },
{ text: '立即更新', onPress: openDown }
])
} else {
XbdToast.show('已是最新版本')
}
})
}
/**获取当前版本号 */
const getCurVersion = useCallback(async () => {
const version = await getVersion()
setVersion(version)
}, [])
const goPage = useCallback(() => {
navigation.navigate('Privacy')
}, [navigation])
return (
<View style={styles.container}>
<View style={{ flex: 1 }}>
<View style={styles.header}>
<Image
source={require('../../assets/images/ic_launcher.png')}
style={styles.logo}
/>
<Text style={styles.name}>商品交易所</Text>
<Text style={styles.version}>Version {version}</Text>
</View>
<View style={styles.center}>
<XbdDivider style={{ marginHorizontal: setUnit(25) }} />
<XbdCell
title={'功能介绍'}
onClick={() => navigation.navigate('Features')}
padLeft={setUnit(25)}
padRight={setUnit(25)}
/>
<XbdCell
title={'版本更新'}
onClick={getUpdate}
padLeft={setUnit(25)}
padRight={setUnit(25)}
/>
</View>
</View>
<View style={styles.footer}>
<Text style={styles.private} onPress={goPage}>《商城平台隐私权政策》</Text>
<Text style={styles.copyright}>
{`广州信巴迪信息科技有限公司 版权所有\nCopyright© 粤ICP备18005051号-1`}
</Text>
</View>
</View>
)
}
export default Page
\ No newline at end of file
import React from 'react'
import { SafeAreaView, View, Text } from 'react-native'
const Error404: () => React$Node = () => {
return (
<SafeAreaView style={{ backgroundColor: '#ffffff', flex: 1 }} >
<View>
<Text>错误,404</Text>
</View>
</SafeAreaView>
)
}
export { Error404 }
\ No newline at end of file
import Page from './view' import View from './view'
import { Platform } from 'react-native'
export const Home = Page
export default View
import { StyleSheet } from 'react-native'
import { Colors } from 'react-native/Libraries/NewAppScreen'
import { setUnit } from '../../libs/utils'
const styles = StyleSheet.create({
scrollView: {
flex: 1,
backgroundColor: "#6E4DB3"
},
body: {
flex: 1
},
h1: {
fontWeight: '400',
textAlign: 'center',
fontSize: 40,
// paddingTop: 20,
paddingBottom: 20,
color: Colors.white
},
button: {
marginBottom: 20,
marginTop: 20
},
shoppItem: {
width: '100%',
padding: setUnit(20)
},
img: {
height: setUnit(500)
},
shoppTitle: {
textAlign: 'center',
fontSize: 16,
color: 'rgba(0,0,0,.7)',
paddingTop: 5,
paddingBottom: 5,
backgroundColor: '#fff'
}
})
export { styles }
\ No newline at end of file
File mode changed
import React, { useState, useEffect, memo } from 'react'; import React from 'react'
import { import { View } from 'react-native'
ScrollView,
View,
Text,
TouchableWithoutFeedback,
Image
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context'
import { styles } from './style'
const ShoppItem = memo(({title, go }) => { const Page = () => {
return ( return (
<TouchableWithoutFeedback onPress={ () => go({ name: title }) } > <View>
<View style={styles.shoppItem} > <Text>我是首页</Text>
<Image source={{uri: "https://images.b2bwings.com/10170109-22af-467b-8b18-008ec4d09724.jpg"}} style={styles.img} /> </View>
<Text style={styles.shoppTitle}>{title}</Text>
</View>
</TouchableWithoutFeedback>
)
})
const Page: () => React$Node = ({ navigation }) => {
const [ list, setList ] = useState([])
useEffect(() => {
setList( [ "荔枝-妃子笑 (演示路由跳转)", "荔枝-白糖婴 (演示路由跳转)" ])
}, []) // effect只执行一次
const go = params => navigation.navigate('shopping', params)
return(
<SafeAreaView style={styles.scrollView}>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={{ flex: 1 }}>
<View>
<Text style={ styles.h1 }>信巴迪-基础环境</Text>
</View>
{list.map(e => (<ShoppItem title={e} go={go} key={e} />))}
</ScrollView>
</SafeAreaView>
) )
} }
export default Page export default Page
\ No newline at end of file
import Page from './view'
import { connect } from 'react-redux'
const mapState = state => ({})
const mapDispatch = dispatch => ({
loginSuccess: dispatch.user.loginSuccess
})
export default connect(mapState, mapDispatch)(Page)
\ No newline at end of file
import { StyleSheet } from 'react-native'
import { setUnit } from '../../libs/utils'
export const styles = StyleSheet.create({
body: {
flex: 1,
backgroundColor: 'rgba(245,246,247,1)',
alignItems: 'center'
},
input: {
width: '100%',
height: setUnit(100),
backgroundColor: 'rgb(255,255,255)',
borderTopColor: '#DDDDDD',
borderBottomColor: '#DDDDDD',
borderTopWidth: 1,
borderBottomWidth: 1,
fontSize: setUnit(29),
padding: setUnit(30),
color: 'rgb(0,0,0)'
},
button: {
width: setUnit(400),
height: setUnit(60),
paddingTop: setUnit(20)
},
loading: {
position: 'absolute',
top: 0,
left: 0,
zIndex: 5,
width: '100%',
height: '100%'
}
})
\ No newline at end of file
import React, { useState } from 'react'
import { View, TextInput, Button, Alert, ActivityIndicator } from 'react-native'
import { styles } from './style'
import { login } from '../../api/user'
const Page: () => React$Node = ({ navigation ,loginSuccess = () => {} }) => {
const [name, setName ] = useState('')
const [passwd, setPasswd] = useState('')
const [show, setShow ] = useState(false)
const go = () => {
setShow(true)
login(name, passwd)
.then(res => {
setShow(false)
loginSuccess(res.user)
navigation.goBack()
})
.catch(err => {
setShow(false)
Alert.alert(err)
})
}
return (
<View style={styles.body}>
{show && <ActivityIndicator size={40} style={styles.loading} /> }
<TextInput style={styles.input} value={ name } onChangeText={val => setName(val)} clearButtonMode="unless-editing" placeholder="输入账户" />
<TextInput style={styles.input} value={ passwd } onChangeText={val => setPasswd(val)} clearButtonMode="unless-editing" secureTextEntry={true} placeholder="输入密码"/>
<View style={styles.button}>
<Button title="登陆" onPress={ go } />
</View>
</View>
)
}
export default Page
\ No newline at end of file
import page from './view'
import { connect } from 'react-redux'
const mapState = state => ({
userInfo: state.user.userInfo,
user: state.user.user
})
const Me = connect(mapState)(page)
export default Me
import { StyleSheet } from 'react-native'
import { Colors } from 'react-native/Libraries/NewAppScreen'
import { setUnit } from '../../libs/utils'
const styles = StyleSheet.create({
body: {
backgroundColor: Colors.white,
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
headerImg: {
width: setUnit(200),
height: setUnit(200),
borderColor: 'rgba(0,0,0,.3)',
borderWidth: 1,
borderRadius: setUnit(100),
},
title: {
fontSize: setUnit(29),
paddingTop: setUnit(20),
paddingBottom: setUnit(20),
color: 'rgba(0,0,0,.9)'
},
tips: {
fontSize: setUnit(20),
color: 'rgba(0,0,0,.3)'
},
appVersion: {
paddingTop: setUnit(100)
},
appVersionTitle: {
fontSize: setUnit(16),
color: 'rgba(0,0,0,.2)'
}
})
export { styles }
\ No newline at end of file
import React, { useState } from 'react'
import { Text, Image, Button, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { styles } from './style'
import { getVersionCode, getVersion } from '../../libs/utils'
const Page: () => React$Node = ({ navigation, userInfo, user }) => {
const [version, setVersion] = useState(null)
const [versionCode, setVersionCode] = useState(null)
const imageConfig = {
uri: userInfo ? "https://images.b2bwings.com/10170109-22af-467b-8b18-008ec4d09724.jpg" : null
}
const name = userInfo ? user.name : '未登录'
versionCode === null && getVersionCode().then(res => setVersionCode(res))
version === null && getVersion().then(res => setVersion(res))
return (
<SafeAreaView style={styles.body}>
<Image source={imageConfig} style={styles.headerImg} />
<Text style={styles.title}>{name}</Text>
{ userInfo && <Text style={styles.tips}>{user.phone}</Text> }
{ userInfo && <Text style={styles.tips}>职位:{user.position}</Text> }
{ userInfo ? <></> :<Button title="点击登陆" onPress={() => navigation.navigate('login')} />}
<View style={styles.appVersion}>
<Text style={styles.appVersionTitle} >APP版本:{version}</Text>
<Text style={styles.appVersionTitle} >APP版本号: {versionCode}</Text>
</View>
</SafeAreaView>
)
}
export default Page
import { StyleSheet } from 'react-native'
import { Colors } from 'react-native/Libraries/NewAppScreen'
import { setUnit } from '../../libs/utils'
export const styles = StyleSheet.create({
body: {
flex: 1,
backgroundColor: 'rgba(0,0,0,.08)',
paddingLeft: setUnit(75),
paddingRight: setUnit(75),
paddingTop: setUnit(20)
},
passwdBox: {
width: setUnit(600),
marginTop: setUnit(20),
height: setUnit(100),
backgroundColor: Colors.white,
position: 'relative',
borderColor: 'rgba(0,0,0,.4)',
borderWidth: 3,
marginBottom: setUnit(100)
},
passwdItem: {
height: setUnit(90),
width: setUnit(97),
backgroundColor: Colors.white,
position: 'absolute',
top: 0,
borderRightWidth: 3,
borderRightColor: 'rgba(0,0,0,.4)',
zIndex: 1,
justifyContent: 'center',
alignItems: 'center'
},
itemIcon: {
width: setUnit(20),
height: setUnit(20),
backgroundColor: '#000000',
borderRadius: 20
},
passwd: {
height: setUnit(100),
width: setUnit(100),
borderLeftColor: 'rgba(0,0,0,0)',
borderLeftWidth: 3,
borderBottomColor: 'rgba(0,0,0,.4)',
borderBottomWidth: 3,
opacity: 0,
color: 'rgba(0,0,0,0)',
zIndex: 2
}
})
\ No newline at end of file
import React, { useState } from 'react'
import { TextInput, View, Text, Button, Alert } from 'react-native'
import { styles } from './styles'
import { setUnit } from '../../libs/utils'
const Page: () => React$Node = () => {
const [passwd, setPasswd] = useState('')
const change = text => {
setPasswd(text)
}
const list = [0,0,0,0,0,0]
const item = list.map((e, k) => {
return (
<View style={{ ...styles.passwdItem, left: setUnit(k * 100) }} key={k} >
{ passwd.length > k && <Text style={styles.itemIcon} />}
</View>
)
})
const submit = () => {
if (passwd.length >= 6) Alert.alert('支付密码不正确')
else Alert.alert('请输入6位数密码')
}
return (
<View style={styles.body}>
<View style={styles.passwdBox}>
{item}
<TextInput value={passwd} secureTextEntry={true} maxLength={6} onChangeText={change} keyboardType="numeric" style={styles.passwd} />
</View>
<Button title="购买" onPress={submit} />
</View>
)
}
export default Page
import Page from './view'
import { connect } from 'react-redux'
const mapState = state => ({
userInfo: state.user.userInfo
})
export default connect(mapState)(Page)
import { StyleSheet } from 'react-native'
import { Colors } from 'react-native/Libraries/NewAppScreen'
import { setUnit } from '../../libs/utils'
export const styles = StyleSheet.create({
body: {
backgroundColor: Colors.white,
flex: 1
},
title: {
fontSize: setUnit(40),
textAlign: 'center',
padding: setUnit(20),
paddingBottom: setUnit(80)
}
})
\ No newline at end of file
import React from 'react'
import { View, Text, Button } from 'react-native'
import { styles } from './styles'
const Page: () => React$Node = ({ route, userInfo = false, navigation }) => {
const goShop = () => userInfo ? navigation.navigate('play') : navigation.navigate('login')
return (
<View style={styles.body}>
<Text style={styles.title}>商品名称:{route.params.name}</Text>
<Button title="购买" onPress={goShop} />
</View>
)
}
export default Page
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!