根目录
- jsbundles
- node_modules
- outputBundle
- patches
- scripts
- typings
- .eslintignore
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- README.md
- yarn.lock jsbundles目录:
- MyApp
- MyApp.js
- MyApp.version
- MyApp.web.js
package.json:
"scripts": {
"clean": "rm -rf node_modules/@xxx/* && rm -rf node_modules/react-native && rm -rf scripts",
"update": "npm run clean && npm install",
"postinstall": "cp -rf node_modules/@xxreact/xxreact-core-scripts/scripts . && cp -rf node_modules/@xxreact/xxreact-core-scripts/patch/* node_modules/ && patch-package",
"start": "node node_modules/react-native/local-cli/cli.js start",
"start-win": "node node_modules/react-native/local-cli/cli.js start",
"web-init": "node ./node_modules/@xxreact/xxreact-core-web/local-cli/cli.js init",
"web-start": "cross-env NODE_ENV=development webpack-dev-server --config ./web/webpack.config.dev.js",
"web-bundle": "cross-env NODE_ENV=production webpack --config ./web/webpack.config.prod.js",
"switch-env-test": "sh ./scripts/switch-env.sh test",
"switch-env-pre": "sh ./scripts/switch-env.sh pre",
"switch-env-prd": "sh ./scripts/switch-env.sh prd",
"ios-bundle": "yarn switch-env-prd && ./scripts/make-fullpack-jsbundles.sh -p ios -m MyApp",
"android-bundle": "yarn switch-env-prd && ./scripts/make-fullpack-jsbundles.sh -p android -m MyApp",
"ios-bundle-pre": "yarn switch-env-pre && ./scripts/make-fullpack-jsbundles.sh -p ios -m MyApp",
"android-bundle-pre": "yarn switch-env-pre && ./scripts/make-fullpack-jsbundles.sh -p android -m MyApp",
"ios-bundle-test": "yarn switch-env-test && ./scripts/make-fullpack-jsbundles.sh -p ios -m MyApp",
"android-bundle-test": "yarn switch-env-test && ./scripts/make-fullpack-jsbundles.sh -p android -m MyApp",
"check-package-config": "sh ./scripts/check-package-config.sh",
"eslint:fixAll": "node_modules/.bin/eslint . --fix"
},
"repository": {
"type": "git",
"url": ""
},
"dependencies": {
"@ares/jlogx": "1.0.0",
"@xxreact/xxreact-core-lib": "2.1.270",
"@xxreact/xxreact-core-linear-gradient": "1.0.29",
"@xxreact/xxreact-core-scripts": "^2.1.224",
"@xxreact/with-color-theme": "1.0.20",
"axios": "0.21.4",
"crypto-js": "4.2.0",
"dva-core": "1.1.0",
"lodash": "4.17.21",
"moment": "2.29.1",
"qs": "6.10.1",
"react": "16.8.3",
"react-native": "0.59.9",
"react-native-barcode-builder": "2.0.0",
"react-native-camera": "4.2.0",
"react-native-swipeout": "2.3.6",
"react-native-webview": "11.14.0",
"react-redux": "5.0.6"
},
"devDependencies": {
"@xxreact/xxreact-core-web": "3.0.0",
"@react-native-community/eslint-config": "3.0.3",
"eslint": "8.27.0",
"eslint-plugin-react-native": "4.1.0",
"metro-react-native-babel-preset": "0.59.0",
"patch-package": "6.4.7"
}
}
MyApp.web.js:
import React, { Component } from 'react';
import { AppRegistry, View, StyleSheet } from 'react-native';
import {
XXText,
} from '@XXreact/XXreact-core-lib'
class MyApp extends Component {
render() {
return (
<View style={styles.container}>
<XXText>Hello, MyApp</XXText>
</View>
);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
var app = document.getElementById('m_common_content');
if (!app) {
app = document.createElement('div');
document.body.appendChild(app);
}
AppRegistry.runApplication('MyApp', {
rootTag: app
});
MyApp.js:
/* eslint no-undef: 0 */
import React, { Component } from 'react';
import { dva } from 'dva';
import Navigation from './MyApp/Navigation';
import { ThemeProvider } from '@XXreact/with-color-theme';
import Global, { Dispatch, setDispatch } from './MyApp/utils/Global'
import {
AppRegistry,
} from 'react-native';
import * as models from './MyApp/models'
import { Modules as depositModules } from 'deposit-modules';
import { Modules as deliveryDepositModules } from 'delivery-deposit-modules';
import { Modules as prestoreModules } from 'prestore-modules';
import { Modules as operatorModules } from 'operator-modules';
import { Modules as appleEduDiscountsModules } from 'appleEduDiscounts-modules';
import { initAppInstance } from './MyApp/system/AppInstance';
//初始化dva实例
const Models = []
Object.keys(models).forEach(key => Models.push(models[key]));
//加入业务模块小土豆的model
Object.keys(xiaotudouModules.models).forEach(key => { Models.push(xiaotudouModules.models[key]) });
//加入业务模块活塞管理的model
Object.keys(huosaiModules.models).forEach(key => { Models.push(huosaiModules.models[key]) });
// 指定 `Object.prototype.toString.call(new Blob())` 返回的结果是 `[object Blob]`
Blob.prototype[Symbol.toStringTag] = 'Blob'
const app = dva({
initialState: {},
models: Models,
onError(e) {
log('onError', e);
}
});
//如果不是开发环境就禁用掉所有打印
if (!__DEV__) {
global.console = {
info: () => { },
log: () => { },
warn: () => { },
debug: () => { },
error: () => { }
};
}
//注册一个全局log方法
global.log = (...params) => {
if (__DEV__) console.log(...params);
};
// 方便在任意地方使用dva
setDispatch(app._store.dispatch)
console.disableYellowBox = true;
initAppInstance(app)
// GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
const App = app.start(
<ThemeProvider>
<Navigation />
</ThemeProvider>
);
AppRegistry.registerComponent('MyApp', () => App);
MyApp.version:
{
"moduleName":"$$MODULE_NAME",
"moduleCode":"5.0",
"targetAppVersion":"2.2.45",
"frameworkVersion":"$$SDK_VERSION",
"platform":"$$PLATFORM",
"chineseModuleName":"$$CHINESE_MODULE_NAME",
"poErp":"$$PO_ERP"
}
MyAp目录:
- components
- config
- images
- mock
- models
- pages
- services
- system
- themes
- utils
- config.js
- index.js
- Navigation.js
index.js:
import React, { PureComponent } from 'react'
import {
View,
StyleSheet,
} from 'react-native'
import {
XXText,
} from '@xxreact/XXreact-core-lib'
export default class XXReactIdago extends PureComponent{
render() {
return (
<View style={styles.container}>
<XXText>Hello, XXReactIdago</XXText>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
module.exports = XXReactIdago;
config.js:
import { env } from './config/config'
module.exports = {
isXXAPP: false, //是否是XXapp环境
appIdDefault: 'idoga', //appid
loginTypeDefault: '10',
sourceDefault: '',
loginAppId: '1597', // 京东登录appId
env, // 环境
colorAppIds: {
test: 'idogaTest',
pre: 'idoga',
prd: 'idoga'
},
idogaHost: {
test: '',
pre: 'https://idoga-p.XX.com',
prd: 'https://idoga.XX.com'
},
XXLBSAppid: '0ea66e64bd8b7d2f408a2dec92050424' //XXLBS位置服务appid
}
Navigation.js:
import React from 'react';
import {
View,
StatusBar,
DeviceEventEmitter,
} from 'react-native';
import { connect } from 'dva';
import {
XXRouter,
XXTouchable,
XXImage,
XXNativeSystem,
} from '@XXreact/XXreact-core-lib';
import {
mergeStyle,
} from '@XXreact/with-color-theme';
import { setActions } from './utils/Actions';
import { FsWatermarkView, FsToast } from 'fs-mobile-components';
import { DeviceInfo } from 'fs-device-info';
import { DebugManager } from 'XXfs-debug-tool';
import { WebView } from 'react-native-webview';
const { env } = require('./config/env.json')
import { childrenScene as commonChildrenScene } from './pages';
////这边加载模块的数据流
import { Modules as xiaotudouModules } from 'xiaotudou-modules';
import { Modules as huosaiModules } from 'huosai-modules';
import cStyles, { BORDER_COLOR_WHITE, px2dp } from './themes/commonStyles';
import { Text } from 'react-native';
import { MaxUnReadMessageCount } from './pages/MessageCenter/Config';
import { AccountTypeEnum } from './pages/HomeView/enum';
import { Global } from './utils/Global';
import { homeMenuItemClick } from './pages/HomeView/Util';
const childrenScene = commonChildrenScene
.concat(depositModules.childrenScene)
.concat(deliveryDepositModules.childrenScene)
.concat(prestoreModules.childrenScene)
.concat(operatorModules.childrenScene)
.concat(appleEduDiscountsModules.childrenScene)
const iphoneXMarginBottom = XXNativeSystem.iphoneXMarginBottom;
const homeIcon0 = require('./images/homeIcon0.png')
const homeIcon1 = require('./images/homeIcon1.png')
const msgIcon0 = require('./images/msgIcon0.png')
const msgIcon1 = require('./images/msgIcon1.png')
const myIcon0 = require('./images/myIcon0.png')
const myIcon1 = require('./images/myIcon1.png')
const approveIcon = require('./images/tabbar/icon_approve.png')
const goTopIcon = require('./images/tabbar/icon_go_top.png')
const tabbarBg1 = require('./images/tabbar/tabBarBg1.png')
const {
Router,
Route
} = XXRouter;
class Navigation extends React.Component {
componentDidMount = () => {
// 只有非生产环境就打开调试工具浮点,只有打开工具浮点,系统才会正常记录日志数据
if (env && env !== 'prd') {
// DebugManager初始化
DebugManager.inti()
// 初始化设备信息 可以不执行
DebugManager.initDeviceInfo(DeviceInfo)
// 重写console方法
Object.keys(console).forEach((key) => {
global.console[key] = (function (oriLogFunc) {
return function () {
try {
oriLogFunc(...arguments)
const args = Array.prototype.slice.call(arguments)
DebugManager.appendLogs(key, JSON.stringify(args))
} catch (error) {
}
}
})(console[key])
})
// 重写WebView组件,增加onNavigationStateChange属性传入DebugManager记录webview方法
WebView.defaultProps = Object.assign({}, WebView.defaultProps, {
onNavigationStateChange: params => DebugManager.appendWebViewLogs(params.url)
});
}
}
refreshUnreadMessageCount = () => {
this.props.dispatch({ type: 'messageCenter/queryMessageCountAndGroupList' })
}
render() {
const { userInfo } = this.props
return (
<FsWatermarkView style={styles.screen}
watermarkType={3}
watermark={userInfo.pin}
columnCount={3}
rowCount={5}
rotateZ={-30}
watermarkTextStyle={[ cStyles.XXZhengHeiTiFontWeightBold, { fontSize: px2dp(18) }]}
>
<Router
initRoute={{ key: 'Loading' }}
allowSlidePopFirstPage={true}
gesturesEnabled={true}
navigationBar={(
<NavigationBar
colorTheme={this.props.colorTheme}
unreadMessageCountAll={this.props.unreadMessageCountAll}
refreshUnreadMessageCount={this.refreshUnreadMessageCount}
userInfo={userInfo}
parentProps={this.props}
/>
)}>
// 统一管理路由。在XXRouter中提供了三个组件和一个api模块
//* Router: 配置路由的根组件
//* Route: 配置每一条路由的组件,应该配置为Router的child
//* NavigationBar: 默认导航栏,你也可以不用这个导航栏而自定制导航栏
// * nativeHistory: 所有关于路由跳转的API
{childrenScene.map(item => {
return (
<Route
key={item.key}
component={item.component}
sceneConfig={Router.SceneConfigs.None}
slidePopEnabled={item.slidePopEnabled}
onSlidePop={item.onSlidePop}
/>
)
})}
</Router>
</FsWatermarkView>
)
}
}
class NavigationBar extends React.Component {
state = {
selectedTab: 'HomeView'
};
componentDidMount = () => {
StatusBar.setBarStyle('dark-content');
const { router, navState } = this.props;
setActions(router, navState)
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps
&& nextProps.navState
&& nextProps.navState.routes
&& nextProps.navState.routes[nextProps.navState.index].routeName !== this.state.selectedTab
&& (nextProps.navState.routes[nextProps.navState.index].routeName === 'HomeView'
|| nextProps.navState.routes[nextProps.navState.index].routeName === 'MessageCenter'
|| nextProps.navState.routes[nextProps.navState.index].routeName === 'PersonalCenter')
) {
this.setState({
selectedTab: nextProps.navState.routes[nextProps.navState.index].routeName
})
}
const lightRoute = [ 'PersonalCenter', 'ScanPage' ]
const { userInfo } = this.props;
if (userInfo.accountType === AccountTypeEnum.manager || userInfo.accountType === AccountTypeEnum.guide) {
lightRoute.push('HomeView')
}
if (lightRoute.includes(nextProps.navState.routes[nextProps.navState.index].routeName)) {
StatusBar.setBarStyle('light-content');
} else {
StatusBar.setBarStyle('dark-content');
}
}
onKaiDanPress = () => {
Global.dispatch({
type: 'HomeView/queryMenuWithPagePath',
payload: {pagePath: 'ProductListSearch/normal'}
}).then(menu => {
if (menu) {
homeMenuItemClick(menu, this.props.parentProps)
} else {
FsToast.showString('账号未开通销售权限哦~', 2000, 'center')
}
})
}
onChangeToHome = () => {
const { router } = this.props;
const selectedTab = this.state.selectedTab;
StatusBar.setBarStyle('dark-content');
selectedTab !== 'HomeView' && this.setState({ selectedTab: 'HomeView' })
// 刷新未读消息数量
this.props.refreshUnreadMessageCount()
router.resetTo({ routeName: 'HomeView' })
}
render() {
const { navState, router, userInfo } = this.props;
if (!navState || !router) {
return null;
}
// if (router && router.navigator.state.nav.routes &&
// router.navigator.state.nav.routes[router.navigator.state.nav.routes.length-1].routeName) {
// let routeName = router.navigator.state.nav.routes[router.navigator.state.nav.routes.length-1].routeName;
// if (this.routeName !== routeName) {
// this.routeName = routeName;
// log("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", routeName);
// }
// }
const selectedTab = this.state.selectedTab;
const { routes, index } = navState;
const route = routes[index];
if (route && route.routeName !== 'HomeView' && route.routeName !== 'MessageCenter' && route.routeName !== 'PersonalCenter') {
return null;
}
// 默认总部
const userType = userInfo ? userInfo.accountType : AccountTypeEnum.headquarters;
let homeIcon = null
if (selectedTab !== 'HomeView') {
homeIcon = homeIcon0
} else {
homeIcon = homeIcon1
}
return (
<View style={styles.bottomCon}>
<View style={mergeStyle(styles, userType === AccountTypeEnum.guide ? 'guideFooter' : 'footer', this.props.colorTheme)} >
{userType === AccountTypeEnum.guide && (
<XXImage
style={[ styles.guideBgImg ]}
source={tabbarBg1}
/>
)}
<View style={[ styles.footerItem ]}>
<XXTouchable onPress={this.onChangeToHome}>
<XXImage style={styles.footerImg} source={homeIcon} />
</XXTouchable>
</View>
{userType !== AccountTypeEnum.guide && (
<View style={[ styles.footerItem ]}>
<XXTouchable onPress={() => {
StatusBar.setBarStyle('dark-content');
selectedTab !== 'MessageCenter' && this.setState({ selectedTab: 'MessageCenter' })
router.jumpTo({ routeName: 'MessageCenter' })
// FsToast.showString('待配置', 2000, 'center')
}}>
{selectedTab === 'MessageCenter' ?
<XXImage style={styles.footerImg} source={msgIcon1} /> :
<XXImage style={styles.footerImg} source={msgIcon0} />}
{this._renderUnReadCount()}
</XXTouchable>
</View>
)}
{/* 开单 */}
{userType === AccountTypeEnum.guide && (
<View style={[ styles.footerItemKaiDan ]}>
<XXTouchable style={[ styles.kaiDanButton ]} onPress={this.onKaiDanPress} />
</View>
)}
<View style={[ styles.footerItem ]}>
<XXTouchable onPress={() => {
StatusBar.setBarStyle('light-content');
selectedTab !== 'PersonalCenter' && this.setState({ selectedTab: 'PersonalCenter' })
router.jumpTo({ routeName: 'PersonalCenter' })
}}>
{selectedTab === 'PersonalCenter' ?
<XXImage style={styles.footerImg} source={myIcon1} /> :
<XXImage style={styles.footerImg} source={myIcon0} />}
</XXTouchable>
</View>
</View>
</View >
)
}
/**
* 渲染消息菜单右上角未读消息角标
* @returns
*/
_renderUnReadCount = () => {
const { unreadMessageCountAll = 0 } = this.props;
if (unreadMessageCountAll > 0) {
return (
<View style={[ styles.bgColorRed, styles.borderRadius9, styles.rowJustifyCenter, styles.unreadMsgCount ]}>
<Text style={[ styles.mh5, styles.fontWhite12 ]}>
{unreadMessageCountAll > MaxUnReadMessageCount ? `${MaxUnReadMessageCount}+` : unreadMessageCountAll}
</Text>
</View>
)
}
}
}
const styles = cStyles({
screen: {
flex: 1
},
bottomCon: {
paddingBottom: 0
},
footer: {
flexDirection: 'row',
backgroundColor: '#fff',
elevation: 5,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.5,
shadowRadius: 5,
shadowColor: '#ccc',
paddingBottom: iphoneXMarginBottom
},
guideFooter: {
flexDirection: 'row',
backgroundColor: 'transparent',
paddingBottom: iphoneXMarginBottom
},
footerItem: {
alignItems: 'center',
height: px2dp(50),
flex: 1,
marginHorizontal: px2dp(1)
},
footerItemKaiDan: {
alignItems: 'center',
height: px2dp(50),
flex: 1,
marginHorizontal: px2dp(1)
},
footerImg: {
width: px2dp(60),
height: px2dp(50)
},
unreadMsgCount: {
position: 'absolute',
left: 35,
borderWidth: 1,
borderColor: BORDER_COLOR_WHITE
},
guideBgImg: {
position: 'absolute',
left: 0,
right: 0,
height: px2dp(94),
width: '100%',
bottom: iphoneXMarginBottom
},
kaiDanButton: {
height: px2dp(62),
width: px2dp(62),
marginTop: -px2dp(26)
}
})
const mapStateToProps = state => ({
unreadMessageCountAll: state.messageCenter.unreadMessageCountAll,
userInfo: state.Common.userInfo,
HomeView: state.HomeView,
Common: state.Common
});
export default connect(mapStateToProps)(Navigation);
pages目录
- HomeView
- huosai
- index.js pages/index.js:
import HomeView from './HomeView'; //首页
import HuoSai from './HuoSai/HuoSai'; // 活塞管理
const childrenItem = ({ key, component, slidePopEnabled = true, onSlidePop = () => { } }) => {
return {
key, //路由key
component, //路由组件
slidePopEnabled, //是否可以侧滑返回,默认可以,一般tab页面不可以
onSlidePop //侧滑返回调用的方法
};
};
export const childrenScene = [
childrenItem({
key: 'HomeView',
component: HomeView,
slidePopEnabled: false
}),
childrenItem({
key: 'HuoSai',
component: HuoSai,
slidePopEnabled: true
})]
StaffPerformance目录:
- index.js
- index.style.js StaffPerformance/index.js:
import React from 'react';
import {
View,
Text,
StatusBar,
NativeModules,
TouchableOpacity,
ImageBackground,
TouchableWithoutFeedback,
} from 'react-native';
import {
XXDevice,
XXThemeText,
XXTouchableWithoutFeedback,
XXScrollView,
XXImage,
XXPopupWindow,
} from '@XXreact/XXreact-core-lib';
import { connect } from 'dva';
import PageContainer from '../../components/PageContainer';
import FsTouchableOpacity from '../../components/common/FsTouchableOpacity';
import { Actions } from '../../utils/Actions';
import Wheel from '../../components/Wheel/NewWheel';
import StaffPerformancePanal from './StaffPerformancePanal';
import styles from './index.style';
import { px2dp } from '../../themes/commonStyles';
const trangleDownIcon = require('../../images/trangleDown.png');
const bgIcon = require('../../images/bg-1.png');
const rpx = XXDevice.getRpx;
const { StatusBarManager } = NativeModules;
/**
* @description yuangongyeji首页
* @author L
* @date 2023/10/07
* @class StaffPerformancePage
* @extends {React.Component}
*/
class StaffPerformancePage extends React.Component {
constructor(props) {
super(props);
this.state = {
gmvChartsInitFinished: false, //echarts初始化渲染是否完成-gmv
posChartsInitFinished: false, //echarts初始化渲染是否完成-pos成交
notLoadGmv: true, //echarts初始化后未加载当前数据-gmv
notLoadPos: true, //echarts初始化后未加载当前数据-pos成交
scrollLength: 0, //页面滚动距离
immerseHeight: 0, //状态栏+导航栏高度
scrollIndicator: 1, //当前横向滑动到哪个区域 1:gmv 2:pos成交
showMonthSelect: false, //页面顶部是否显示月份弹窗
selectMonth: { //当前选择查看的月份
value: '', //月份数字
text: '', //月份文案
year: ''//月份所在年
},
gmvDayMonth: 'day', // GMV day:日销售额 month:月完成率
posDayMonth: 'day', // POS成交 day:日销售额 month:月完成率
selectDayModal: false, //是否展示日统计日期选择弹窗
selectDayOperate: 'gmv', //要查看的日统计类型 gmv还是pos
monthDays: [], //日期下拉列表数据
dayIndex: 0,
gmvDayTemp: {//暂存选择的日期 gmv
value: '',
text: ''
},
posDayTemp: {//暂存选择的日期 pos成交
value: '',
text: ''
},
gmvChartType: 'bar', //gmv图表展示的类型 销售额-柱状图bar 完成率-折线图line
posChartType: 'bar', //pos图表展示的类型 销售额-柱状图bar 完成率-折线图line
chartOptionGmv: { //gmv统计图表配置项
xAxis: {
type: 'category',
show: false,
axisLabel: false,
data: []
},
tooltip: {
alwaysShowContent: false
},
grid: { top: '5%', bottom: '3%', right: '0%', left: '0%', containLabel: true },
yAxis: {
type: 'value',
nameTextStyle: {
color: '#aaaaaa'
},
axisLabel: {
color: '#CCCCCC',
overflow: 'truncate',
width: 35
},
splitNumber: 5,
min: 0,
max: 500,
splitLine: {
lineStyle: { type: 'dashed' },
show: true
}
},
series: [{
type: 'bar',
barMaxWidth: '15%',
data: []
}]
},
chartOptionPos: {//pos统计图表配置项
xAxis: {
type: 'category',
show: false,
axisLabel: false,
data: []
},
tooltip: {
alwaysShowContent: false
},
grid: { top: '5%', bottom: '3%', right: '0%', left: '0%', containLabel: true },
yAxis: {
type: 'value',
nameTextStyle: {
color: '#aaaaaa'
},
axisLabel: {
color: '#CCCCCC',
overflow: 'truncate',
width: 35
},
splitNumber: 5,
min: 0,
max: 500,
splitLine: {
lineStyle: { type: 'dashed' },
show: true
}
},
series: [{
type: 'bar',
barMaxWidth: '15%',
data: []
}]
}
};
}
componentWillMount() {
// 计算状态栏+导航栏高度
// 安卓/ios取状态栏高度的方式不同
if (StatusBar.currentHeight) {
this.setState({
immerseHeight: StatusBar.currentHeight + rpx(85)
});
} else {
StatusBarManager.getHeight(statusBarHeight => {
this.setState({
immerseHeight: statusBarHeight.height + rpx(85)
});
});
}
// 初始化月汇总与日汇总的日期
this.initDate()
}
componentDidMount() {
//沉浸状态栏 适配安卓
StatusBar.setBackgroundColor('rgba(0,0,0,0)');
StatusBar.setTranslucent(true);
}
componentWillReceiveProps(nextProps) {
const { gmvSales, gmvSalesDate, gmvCompletionRates, posSales, posSalesDate, posCompletionRates } = nextProps
const { gmvSales: gs, gmvSalesDate: gsd, gmvCompletionRates: gcr, posSales: ps, posSalesDate: psd, posCompletionRates: pcr } = this.props
const { gmvDayMonth, posDayMonth, gmvChartsInitFinished, posChartsInitFinished } = this.state
if (gmvSales !== gs || gmvSalesDate !== gsd || gmvCompletionRates !== gcr) {
//如果已完成echats初始化渲染,则加载数据。否则等渲染完成后再加载。防止echart缩成一团。
if (gmvChartsInitFinished) {
this.setChartsData('gmv', gmvDayMonth === 'day' ? 'bar' : 'line', nextProps)
} else {
this.setState({
notLoadGmv: true
})
}
}
if (posSales !== ps || posSalesDate !== psd || posCompletionRates !== pcr) {
//如果已完成echats初始化渲染,则加载数据。否则等渲染完成后再加载。防止echart缩成一团。
if (posChartsInitFinished) {
this.setChartsData('pos', posDayMonth === 'day' ? 'bar' : 'line', nextProps)
} else {
this.setState({
notLoadPos: true
})
}
}
}
componentWillUnmount() {
}
// 初始化月汇总与日汇总的日期
initDate = () => {
let d = new Date()
//日期数字不足2位补0
let monthFormat = ('0' + (d.getMonth() + 1)).slice(-2)
let dayFormat = ('0' + (d.getDate() === 1 ? 1 : d.getDate() - 1)).slice(-2)
//当前日汇总的日期
this.props.dispatch({
type: 'StaffPerformance/setGmvDay',
gmvDay: {
value: monthFormat + '-' + dayFormat,
text: monthFormat + '-' + dayFormat
}
});
this.props.dispatch({
type: 'StaffPerformance/setPosDay',
posDay: {
value: monthFormat + '-' + dayFormat,
text: monthFormat + '-' + dayFormat
}
});
// 月汇总需要的日期格式
let month = d.getFullYear() + '-' + monthFormat
// 获取pos/gmv月汇总数据 0:gmv 1:pos成交
this.searchPerformanceMonthData(0, month)
this.searchPerformanceMonthData(1, month)
// 初次加载默认选中本月查看
this.setState({
selectMonth: {
value: monthFormat,
text: '本月业绩',
year: d.getFullYear()
}
}, () => {
//生成最近三个月的选项列表
this.setThreeMonths()
//生成当月的日期列表
this.setMonthDayList()
})
}
// 查询月汇总数据(0:gmv 1:pos)
searchPerformanceMonthData = (type, month) => {
this.props.dispatch({
type: 'StaffPerformance/getPerformanceMonth',
payload: {
type: type,
month: month
}
});
}
// 查询日汇总数据(0:gmv 1:pos)
searchPerformanceDayData = (type, day) => {
this.props.dispatch({
type: 'StaffPerformance/getPerformanceDay',
payload: {
type: type,
day: day
}
});
}
// 切换要展示的charts图表
setChartsData = (type, chartsType, nextProps) => {
const { gmvSales = [], gmvSalesDate, gmvCompletionRates = [], posSales = [], posSalesDate, posCompletionRates = [] } = nextProps ? nextProps : this.props
if (type === 'gmv') {
//gmv图表切换成柱状图
if (chartsType === 'bar') {
this.setState({
chartOptionGmv: {
xAxis: {
splitNumber: 5,
min: 0,
max: gmvSalesDate?.length > 0 ? null : 500,
data: gmvSalesDate
},
yAxis: {
splitNumber: 5,
min: 0,
max: gmvSales?.length > 0 ? null : 500
},
tooltip: {
trigger: 'axis',
hideDelay: 2000,
formatter: '{b0}:<br />{c0}(元)'
},
series: [{
type: 'bar',
barMaxWidth: '15%',
itemStyle: {
color: '#9EB5FF',
borderRadius: [ 4, 4, 0, 0 ]
},
data: gmvSales
}]
}
})
} else {
//gmv图表切换成折线图
this.setState({
chartOptionGmv: {
xAxis: {
splitNumber: 5,
min: 0,
max: gmvSalesDate?.length > 0 ? null : 500,
data: gmvSalesDate
},
yAxis: {
splitNumber: 5,
min: 0,
max: gmvCompletionRates?.length > 0 ? null : 500
},
tooltip: {
trigger: 'axis',
hideDelay: 2000,
formatter: '{b0}:<br />{c0}%'
},
series: [{
type: 'line',
itemStyle: {
color: '#9EB5FF'
},
showSymbol: false,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(158,181,255,1)' // 0% 处的颜色
}, {
offset: 1, color: 'rgba(255,255,255,1)' // 100% 处的颜色
}]
}
},
data: gmvCompletionRates
}]
}
})
}
} else {
//pos图表切换成柱状图
if (chartsType === 'bar') {
this.setState({
chartOptionPos: {
xAxis: {
splitNumber: 5,
min: 0,
max: posSales?.length > 0 ? null : 500,
data: posSalesDate
},
yAxis: {
splitNumber: 5,
min: 0,
max: posSales?.length > 0 ? null : 500
},
tooltip: {
trigger: 'axis',
hideDelay: 2000,
formatter: '{b0}:<br />{c0}(元)'
},
series: [{
type: 'bar',
barMaxWidth: '15%',
itemStyle: {
color: '#D4A92A',
borderRadius: [ 4, 4, 0, 0 ]
},
data: posSales
}]
}
})
} else {
//pos图表切换成折线图
this.setState({
chartOptionPos: {
xAxis: {
splitNumber: 5,
min: 0,
max: posSalesDate?.length > 0 ? null : 500,
data: posSalesDate
},
yAxis: {
splitNumber: 5,
min: 0,
max: posCompletionRates?.length > 0 ? null : 500
},
tooltip: {
trigger: 'axis',
hideDelay: 2000,
formatter: '{b0}:<br />{c0}%'
},
series: [{
type: 'line',
itemStyle: {
color: '#D4A92A'
},
showSymbol: false,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(212,169,42,1)' // 0% 处的颜色
}, {
offset: 1, color: 'rgba(255,255,255,1)' // 100% 处的颜色
}]
}
},
data: posCompletionRates
}]
}
})
}
}
}
// 滑动到gmv、pos区域时下方指示器做标记
endScrollToindicate = (e) => {
//安卓ios事件对象不同
const { xIOS } = e.nativeEvent.targetContentOffset || {};
const { x } = e.nativeEvent.contentOffset || {};
if (x > 0 || xIOS > 0) {
this.setState({
scrollIndicator: 2
})
} else {
this.setState({
scrollIndicator: 1
})
}
}
// 展示或关闭顶部月份选择弹窗
showMonthSelection = () => {
const { showMonthSelect } = this.state
this.setState({
showMonthSelect: !showMonthSelect
})
}
// 根据所选月份查询业绩数据
confirmSelectMonth = (selectMonth) => {
this.setState({
selectMonth: selectMonth,
showMonthSelect: false
}, () => {
//分别查询业绩月汇总数据(0:gmv 1:pos)
this.searchPerformanceMonthData(0, selectMonth.year + '-' + selectMonth.value)
this.searchPerformanceMonthData(1, selectMonth.year + '-' + selectMonth.value)
this.setMonthDayList(true)
})
}
// 生成最近三个月业绩选项
setThreeMonths = () => {
const { selectMonth } = this.state
let month = new Date().getMonth()
let year = new Date().getFullYear()
let monthsElm = []
for (let i = 0; i < 3; i++) {
let monthTxt = i === 0 ? '本月业绩' : month + 1 + '月业绩'
let monthFormat = ('0' + (month + 1)).slice(-2)
let yVal = year
if (i === 2) {
this.props.dispatch({
type: 'StaffPerformance/setQueryMinDate',
queryMinDate: year + '-' + (month + 1) + '-' + '1'
});
}
monthsElm.push(<FsTouchableOpacity key={i} onPress={() => this.confirmSelectMonth({ value: monthFormat, text: monthTxt, year: yVal })}>
<View style={[ styles.pv16, styles.ph12, styles.rowJustifyLeftCenter, monthFormat === selectMonth.value ? styles.bgColorGray : null, styles.monthSize ]}>
<Text style={[ monthFormat === selectMonth.value ? styles.fontTitle18 : styles.fontSubtitle16 ]}>{monthTxt}</Text>
</View>
</FsTouchableOpacity>)
if (month === 0) {
month = 12
year--
}
month--
}
return monthsElm
}
//导航栏标题展示月份
getLeftView = () => {
const { selectMonth } = this.state
return <View style={[ styles.rowJustifyLeftCenter, { flex: 1 }]} >
<FsTouchableOpacity onPress={() => { this.showMonthSelection() }}>
<View style={[ styles.rowJustifyLeftCenter, styles.ml8 ]}>
<Text style={[ styles.fontBold, styles.mr4, styles.fontTitle22 ]}>{selectMonth.text}</Text>
<XXImage style={[ styles.img16 ]} source={trangleDownIcon} />
</View>
</FsTouchableOpacity>
</View>
}
//生成日汇总日期下拉列表项,查看日汇总数据。
setMonthDayList = (refreshSelectDay) => {
const { selectMonth } = this.state
// 1.获取前一个月最后一天
let dayLast = new Date(selectMonth.year, selectMonth.value, 0).getDate()
// 2.获取当前天
let nowDay = new Date().getDate()
// 3.判断可查询的最近一天是哪天(如果不是查当前月,则默认查询每月最后一天的数据,否则查前一天)
let dayLoopsNum = 0
if (selectMonth.text === '本月业绩') {
dayLoopsNum = nowDay === 1 ? 1 : (nowDay - 1)
} else {
dayLoopsNum = dayLast
}
// 4.日汇总入参需要的完整日期格式
let selectFullDay = selectMonth.year + '-' + selectMonth.value + '-' + ('0' + dayLoopsNum).slice(-2)
// 5.查询日汇总数据 0:gmv 1:pos成交
this.searchPerformanceDayData(0, selectFullDay)
this.searchPerformanceDayData(1, selectFullDay)
// 生成当月可支持查看的所有日期,供下拉列表选择 <x月-x日>
let dayList = []
for (let i = 1; i <= dayLoopsNum; i++) {
//日期数值不足2位补0
let monthFormat = ('0' + selectMonth.value).slice(-2)
let dayFormat = ('0' + i).slice(-2)
dayList.push({
value: monthFormat + '-' + dayFormat,
text: selectMonth.value + '月' + i + '日'
})
}
this.setState({
monthDays: dayList
}, () => {
if (refreshSelectDay) {
//默认选中最后一天
this.props.dispatch({
type: 'StaffPerformance/setGmvDay',
gmvDay: dayList.length > 0 ? dayList[dayList.length - 1] : {}
});
this.props.dispatch({
type: 'StaffPerformance/setPosDay',
posDay: dayList.length > 0 ? dayList[dayList.length - 1] : {}
});
}
})
}
//切换要展示图表 chartsType的值:day日完成率 month月销售额
setDayMonthGmvPos = (type, chartsType) => {
if (type === 'gmv') {
this.setState({
gmvDayMonth: chartsType
})
if (chartsType === 'day') {
this.setChartsData('gmv', 'bar')
} else {
this.setChartsData('gmv', 'line')
}
} else {
this.setState({
posDayMonth: chartsType
})
if (chartsType === 'day') {
this.setChartsData('pos', 'bar')
} else {
this.setChartsData('pos', 'line')
}
}
}
//关闭日期下拉列表弹窗
_onHide = () => {
this.setState({
selectDayModal: false
})
}
//展示或关闭日期下拉列表弹窗
//type:gmv还是pos show:打开/关闭 dayValue:当前页面上展示的日期
showHideSelectDayModal = (type, show, dayValue) => {
const { monthDays, dayIndex } = this.state
let index = dayIndex
//打开日期列表弹窗时,存储选中的日期
if (show && Array.isArray(monthDays) && monthDays.length > 0) {
//先默认选中当前日期
monthDays.map((item, itemIndex) => {
if (item.value === dayValue) {
index = itemIndex
}
})
this.setState({
dayIndex: index
})
//暂存日期
if (type === 'gmv') {
this.setState({
gmvDayTemp: monthDays[index]
})
} else {
this.setState({
posDayTemp: monthDays[index]
})
}
}
this.setState({
selectDayModal: show,
selectDayOperate: type
})
}
// 选择日汇总日期并查询数据
setSelectDay = () => {
const { gmvDayTemp, posDayTemp, selectDayOperate, selectMonth } = this.state
if (selectDayOperate === 'gmv') {
this.props.dispatch({
type: 'StaffPerformance/setGmvDay',
gmvDay: gmvDayTemp
});
} else {
this.props.dispatch({
type: 'StaffPerformance/setPosDay',
posDay: posDayTemp
});
}
// 查询所选日期的日汇总数据
this.searchPerformanceDayData(selectDayOperate === 'gmv' ? 0 : 1, selectMonth.year + '-' + (selectDayOperate === 'gmv' ? gmvDayTemp.value : posDayTemp.value))
// 关闭选择弹窗
this.showHideSelectDayModal(selectDayOperate, false)
}
//暂存选择的日汇总日期
setSelectedDayTemp = (index, day) => {
const { selectDayOperate } = this.state
this.setState({
dayIndex: index
})
if (selectDayOperate === 'gmv') {
this.setState({
gmvDayTemp: day
})
} else {
this.setState({
posDayTemp: day
})
}
}
//跳转yeji排行榜页面
toRankingPage = (type) => {
const { selectMonth } = this.state
Actions.push({
routeName: 'StaffRankingPage',
props: {
performanceType: type,
month: selectMonth.year + '-' + selectMonth.value
}
})
}
//跳转xiaoshou订单列表
toSalesOrderPage = (type) => {
const { selectMonth } = this.state
const { gmvDay, posDay } = this.props
let monthDay = type === 'gmv' ? gmvDay : posDay
Actions.push({
routeName: 'StaffSalesOrderList',
props: {
salesType: type,
salesDate: selectMonth.year + '-' + monthDay.value
}
})
}
//关闭图表数字提示tooltips
clickPage = () => {
this.refs.gmvPanal.hideToolTip();
this.refs.posPanal.hideToolTip();
}
//业绩数据处理后展示 无数据则展示‘--’ 百分数加%
convertData = (data, noFixed) => {
if (data !== null && data !== undefined && data !== '') {
if (typeof data === 'number' && !noFixed) {
return data.toFixed(2)
}
return data
} else {
return '--'
}
}
//滚动事件
onScroll = e => {
const { immerseHeight } = this.state
const { y } = e.nativeEvent.contentOffset
if (y < immerseHeight) {
this.setState({
scrollLength: y > 0 ? y : 0
})
} else {
this.setState({
scrollLength: immerseHeight
})
}
}
//echarts初始化渲染完成
finishedEchats = (type) => {
const { notLoadGmv, notLoadPos, gmvDayMonth, posDayMonth } = this.state
if (type === 'gmv') {
this.setState({
gmvChartsInitFinished: true
}, () => {
if (notLoadGmv) {
this.setChartsData('gmv', gmvDayMonth === 'day' ? 'bar' : 'line')
}
})
} else {
this.setState({
posChartsInitFinished: true
}, () => {
if (notLoadPos) {
this.setChartsData('pos', posDayMonth === 'day' ? 'bar' : 'line')
}
})
}
}
render() {
const { scrollIndicator, showMonthSelect, immerseHeight, gmvDayMonth, posDayMonth, chartOptionGmv, chartOptionPos,
selectDayModal, monthDays, selectDayOperate, dayIndex, scrollLength } = this.state
const { gmvDay, posDay, gmvMonthData, gmvDayData, posMonthData, posDayData } = this.props
const immerseBackgroundColor = `rgba(255,255,255,${scrollLength / immerseHeight})`
return (
<>
<PageContainer titleView={this.getLeftView()} immerse={true} titleStyle={{ color: '#FFFFFF' }} immerseBackgroundColor={immerseBackgroundColor}>
<View >
<TouchableOpacity onPress={() => this.clickPage()} activeOpacity={1}>
<ImageBackground source={bgIcon} style={[]} >
<XXScrollView showsVerticalScrollIndicator={false} onScroll={(e) => { this.onScroll(e) }}>
<TouchableOpacity onPress={() => this.clickPage()} activeOpacity={1}>
<View style={[ styles.ph12, { paddingTop: immerseHeight }]}>
<XXScrollView horizontal showsHorizontalScrollIndicator={false} decelerationRate="fast" snapToAlignment="start" snapToInterval={302} onMomentumScrollEnd={(e) => this.endScrollToindicate(e)} scrollEventThrottle={50}>
<TouchableOpacity onPress={() => this.clickPage()} activeOpacity={1} style={styles.rowJustifyLeftCenter}>
{/* GGG统计面板 */}
<StaffPerformancePanal
ref="gmvPanal"
panalType="gmv"
gmvMonthData={gmvMonthData}
gmvDayData={gmvDayData}
gmvDayMonth={gmvDayMonth}
gmvDay={gmvDay}
chartOption={chartOptionGmv}
setDayMonthGmvPos={(type, chartsType) => this.setDayMonthGmvPos(type, chartsType)}
showHideSelectDayModal={(type, show, dayValue) => this.showHideSelectDayModal(type, show, dayValue)}
toRankingPage={() => this.toRankingPage('gmv')}
toSalesOrderPage={() => this.toSalesOrderPage('gmv')}
finishedEchats={(type) => this.finishedEchats(type)}
/>
{/* PPP统计面板 */}
<StaffPerformancePanal
ref="posPanal"
panalType="pos"
posMonthData={posMonthData}
posDayData={posDayData}
posDayMonth={posDayMonth}
posDay={posDay}
chartOption={chartOptionPos}
setDayMonthGmvPos={(type, chartsType) => this.setDayMonthGmvPos(type, chartsType)}
showHideSelectDayModal={(type, show, dayValue) => this.showHideSelectDayModal(type, show, dayValue)}
toRankingPage={() => this.toRankingPage('pos')}
toSalesOrderPage={() => this.toSalesOrderPage('pos')}
finishedEchats={(type) => this.finishedEchats(type)}
/>
</TouchableOpacity>
</XXScrollView>
<View style={[ styles.mt20, styles.rowJustifyCenter ]}>
<View style={[ scrollIndicator === 1 ? styles.curIndicator : styles.nonCurIndicator, styles.mr4 ]} />
<View style={scrollIndicator === 2 ? styles.curIndicator : styles.nonCurIndicator} />
</View>
<View style={[ styles.mv24, styles.ph16, styles.pv20, styles.borderRadius8, styles.bgColorWhite, {}]}>
<View>
<Text style={[ styles.fontBold, styles.fontTitle18 ]}>顾客数据</Text>
</View>
<View style={[ styles.rowJustifyAroundStart, styles.mt24 ]}>
<View style={[ styles.columnJustifyCenter, styles.customSizeOne ]}>
<Text style={[ styles.fontSubtitle12 ]}>成交用户</Text>
<Text style={[ styles.fontBold, styles.XXZhengHeiTi, styles.fontTitle24, styles.mt16, { textAlign: 'right' }]} ellipsizeMode="tail" numberOfLines={2}>{this.convertData(posMonthData.dealUserCount, true)}</Text>
</View>
<View style={[ styles.columnJustifyCenter, styles.ml10, styles.customSize ]}>
<Text style={[ styles.fontSubtitle12 ]}>成交后退货(元)</Text>
<Text style={[ styles.fontBold, styles.XXZhengHeiTi, styles.fontTitle24, styles.mt16, { textAlign: 'right' }]} ellipsizeMode="tail" numberOfLines={2}>{this.convertData(posMonthData.refundAmountAfterDeal)}</Text>
</View>
<View style={[ styles.columnJustifyCenter, styles.ml10, styles.customSize ]}>
<Text style={[ styles.fontSubtitle12 ]}>成交后取消(元)</Text>
<Text style={[ styles.fontBold, styles.XXZhengHeiTi, styles.fontTitle24, styles.mt16, { textAlign: 'right' }]} ellipsizeMode="tail" numberOfLines={2}>{this.convertData(posMonthData.cancelAmountAfterDeal)}</Text>
</View>
</View>
</View>
</View>
</TouchableOpacity>
</XXScrollView>
</ImageBackground>
</TouchableOpacity>
</View>
</PageContainer>
{
showMonthSelect ? (
<View style={[{ backgroundColor: 'rgba(0,0,0,0.5)', position: 'absolute', top: immerseHeight, width: '100%', height: '100%', flexDirection: 'column-reverse' }, styles.flex1 ]}>
<TouchableWithoutFeedback onPress={() => this.setState({ showMonthSelect: false })}>
<View style={[ styles.flex1 ]} />
</TouchableWithoutFeedback>
<View style={[{ borderBottomRightRadius: 8, borderBottomLeftRadius: 8 }, styles.bgColorWhite ]}>
<View style={[ styles.columnJustifyLeftCenter, styles.p12 ]}>
{this.setThreeMonths()}
</View>
</View>
</View>
) : null
}
<XXPopupWindow show={selectDayModal} onHide={this._onHide} height={px2dp(300)} style={{ backgroundColor: 'red' }}>
<View style={styles.bgColorWhite}>
<View style={[ styles.pv24, styles.ph16, styles.borderB, { flexDirection: 'row', justifyContent: 'space-between' }]}>
<XXTouchableWithoutFeedback onPress={() => this.showHideSelectDayModal(selectDayOperate, false)}>
<View style={{ justifyContent: 'center' }}>
<XXThemeText style={styles.fontTitle18}>取消</XXThemeText>
</View>
</XXTouchableWithoutFeedback>
<View style={{ justifyContent: 'center' }}><XXThemeText style={[ styles.fontTitle22, styles.fontBold ]}>选择日期</XXThemeText></View>
<XXTouchableWithoutFeedback onPress={() => this.setSelectDay()}>
<View style={{ justifyContent: 'center', color: 'blue' }}>
<XXThemeText style={styles.fontRed18}>确认</XXThemeText>
</View>
</XXTouchableWithoutFeedback>
</View>
<View style={[ styles.ph16, { flexDirection: 'row', justifyContent: 'space-around' }]}>
{Array.isArray(monthDays) && monthDays.length > 0 ? <Wheel
index={dayIndex}
holeLine={null}
holeStyle={styles.holeStyle}
style={styles.selectStyle}
itemStyle={[ styles.rowJustifyCenter, {}]}
itemTextStyle={[ styles.fontTitle18 ]}
items={monthDays.map(item => item.text)}
onChange={(index) => {
this.setSelectedDayTemp(index, monthDays[index]);
}}
/> : null}
</View>
</View>
</XXPopupWindow>
</>
);
}
}
const mapStateToProps = state => ({
...state.StaffPerformance,
...state.Common
});
export default connect(mapStateToProps)(StaffPerformancePage);
index.style.js:
import { default as cStyles } from '../../themes/commonStyles';
import { XXDevice } from '@xxreact/xxreact-core-lib';
import { px2dp } from '../../themes/commonStyles';
export default cStyles({
RrrrPage: {
flex: 1
}
})
model目录 index.js aaa
index.js:
export{default as HomeView} from './HomeView'
import { FsToast, FsLoading } from 'fs-mobile-components';
import {
queryAllEnterpriseInfo
} from '../../services/Manage';
export default {
namespace: '',
state: {
approveList: []//列表
},
effects: {
// 初始化app
* initApp({ }, { call, put }) {
yield call(initENV);
yield call(clearCookies)
const isPosDevice = yield call(FsPosUtil.isPosDevice);
Global.isPosDevice = isPosDevice;
// 初始化XX登录SDK
yield call(FSXXLogin.initSDK, loginAppId);
const xxLoginInfo = yield FSXXLogin.getxxLoginInfo();
// console.log('登录信息:', xxLoginInfo);
let loginInfo = {};
if (xxLoginInfo.code === 0) {
loginInfo = xxLoginInfo.data;
}
yield put({ type: 'updateState', payload: { loginInfo } });
// 判断是否超过7天未登录
const nowTime = currentTimestamp();
const lastLoginTime = yield StorageUtil.get('newLastLoginTime');
const newLastLoginTime = JSON.parse(lastLoginTime);
if (newLastLoginTime) {
if (nowTime - newLastLoginTime > 60 * 60 * 24 * 7 * 1000) {
// 超过七天退出登录
LogX.i('退出登录', `超过七天退出登录`);
yield put({ type: 'logout' });
return;
}
}
if (loginInfo && Object.keys(loginInfo).length > 0 && loginInfo.a2.length > 0) { // 登录过
// console.log('登录信息:', loginInfo);
yield put({ type: 'handleLoginXXSuccess', payload: { loginInfo, autoLogin: true } });
} else { // 未登录跳转到登录
// // console.log('未登录,跳转到登录页面:', Actions);
Actions.replace({ routeName: 'Login' });
yield put({ type: 'checkUpdate', payload: { limit: false } });
}
},
//查询信息
* getEnterpriseInfo({ payload, callback }, { call, put, select }) {
try {
const userInfo = yield select(state => state.Common.userInfo);
payload.shopId = userInfo.shopId || '';
yield put({
type: 'setEnterpriseLoadingState',
enterpriseLoadingState: 1
});
let res = yield call(queryAllEnterpriseInfo, payload);
}catch{
}
}
},
reducers: {
setSelectedIndustryTemp(state, { selectedIndustryTemp }) {
return { ...state, selectedIndustryTemp: selectedIndustryTemp };
}
}
};
components demo: 最后是export default PageContainer; config目录: -config.js
const { pVer } = configObj
/**
* 默认的请求失败 message
*/
const defaultRequestFailMessage = '请求失败,请稍后重试!!'
export { versionDefault, version, env, pVer, defaultRequestFailMessage };
-config.json "prd": { "versionDefault": "2.2.51", "pVer": "20240326" } -env.json { "env": "pre" }
service
import { request } from '../utils/request';
export async function queryOrders(params) {
return request({
sys: 'colorGW',
url: 'app.ep.aaaaa.xxxx',
method: 'GET',
data: params
});
}
utils/request/requestColor
/**
* http请求工具类
*/
const fetch = async (options) => {
let { url, method, data, functionId, extraParams, bigNumKeys, riskData } = options;
axios.defaults.baseURL = host(env);
axios.defaults.method = 'post';
axios.defaults.timeout = 20000;
method = method ? method.toLowerCase() : 'post'
const bodyParams = {
version: versionDefault,
source: sourceDefault,
requestId: new Date().getTime(),
appId: colorAppIds[env],
loginType: loginTypeDefault,
...data
};
let cookies = ''
let token = Global.token
const XXLoginInfo = Global.XXLoginInfo
const clientType = Global.isPosDevice ? 'PDA' : 'APP';
if (XXLoginInfo && XXLoginInfo.a2 && XXLoginInfo.a2.length > 0) {
// console.log('已登录:', XXLoginInfo);
cookies = `pin=${encodeURI(XXLoginInfo.pin)};wskey=${XXLoginInfo.a2};`
await setFromResponse(host(env), `pin=${encodeURI(XXLoginInfo.pin)};`)
await setFromResponse(host(env), `wskey=${XXLoginInfo.a2};`)
}
if (token && token.length > 0) {
cookies = `${cookies}token=${token};`
await setFromResponse(host(env), `token=${token};`)
}
// 添加设备类型clientType
if (clientType) {
cookies = `${cookies}clientType=${clientType};`
await setFromResponse(host(env), `clientType=${clientType};`)
}
// 处置sdk相关
if (riskData && riskData.evToken) {
cookies = `${cookies}x-rp-evtoken=${riskData.evToken};`
await setFromResponse(host(env), `x-rp-evtoken=${riskData.evToken};`)
} else {
const evToken = await FsRiskHandle.getXXRiskHandleToken()
if (evToken && evToken.length > 0) {
cookies = `${cookies}x-rp-evtoken=${evToken};`
await setFromResponse(host(env), `x-rp-evtoken=${(evToken)};`)
}
}
const unionwsws = await FsRiskHandle.getUnionFingerprint()
if (unionwsws && unionwsws.length > 0) {
cookies = `${cookies}unionwsws=${unionwsws};`
// await setFromResponse(host(env), (`unionwsws=${(unionwsws)};`))
await setFromResponse(host(env), (`unionwsws=${(unionwsws).replace(',', '%2C')};`))
}
const headers = {
Cookie: cookies,
'Content-Type': 'application/x-www-form-urlencoded',
ver: versionDefault, // 前端版本号
pver: pVer // 上线日期(打包日期)
};
// 处置sdk相关
if (riskData && riskData.xRpExt) {
headers['X-Rp-Ext'] = JSON.stringify(riskData.xRpExt)
}
const config = { headers, withCredentials: false };
if (bigNumKeys && bigNumKeys.length > 0) {
config.transformResponse = rspData => {
try {
for (const bigNumKey of bigNumKeys) {
rspData = rspData.replace(new RegExp(`"${bigNumKey}":(\\d+)`, 'g'), '"' + bigNumKey + '":"$1"')
}
// console.log('处理大数字:', bigNumKeys)
return JSON.parse(rspData)
} catch (error) {
// console.log('处理大数字异常:', error)
return JSON.parse(rspData)
}
}
}
let splitParams = {
appid: colorAppIds[env],
functionId,
t: currentTimestamp() + '',
partner: '',
loginType: loginTypeDefault,
body: JSON.stringify(bodyParams),
// 神盾小号需要此字段,openudid是iOS端需要的设备号
uuid: Global.deviceId,
openudid: Global.deviceId,
// 神盾参数
...(await getShenDunParams()),
...(await colorDefaultParams()),
...extraParams
}
const sign = await hmacSha256(splitParams)
splitParams = { ...splitParams, sign }
let paramsUrl = qs.stringify(splitParams)
switch (method) {
case 'get':
return axios.get(url, { params: splitParams, ...config });
case 'delete':
return axios.delete(url, { data: paramsUrl, ...config });
case 'head':
return axios.head(url, paramsUrl);
case 'post':
return axios.post(url, paramsUrl, {
...config
});
case 'put':
return axios.put(url, paramsUrl, config);
case 'patch':
return axios.patch(url, paramsUrl, config);
case 'download':
return axios.post(url, paramsUrl, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
responseType: 'blob',
...config
});
default:
return axios(options);
}
};
function requestColor({ params, functionId, ...args }) {
if (!params) {
params = {};
}
const requestOptions = { url: '/', data: params, functionId, ...args };
const riskHandleQueueObj = riskHandleData[riskHandleFunId]
// console.log('处置队列:', riskHandleData);
if (riskHandleQueueObj && riskHandleQueueObj.status === 'handling') {
// 正在处置
// console.log('处置队列接口:', functionId);
LogX.i('处置相关', `处置队列接口:${functionId}`);
return queue({ params, functionId, ...args })
}
return fetch(requestOptions).then(async (response) => {
const rspHeader = response.headers
const wlCode = rspHeader['x-api-wl-message']
if (wlCode && wlCode !== '0') {
console.log('网关异常', { functionId, response });
LogX.w('网关异常', `${JSON.stringify({ functionId, response })}`)
showLoginExpiredDialog()
throw getCommonFailResult('当前登录状态失效,请重新登录!!', response)
}
if (response.status === 200) {
DebugManager.appendHttpLogs([
{ title: 'functionId:', content: functionId },
{ title: 'baseURL:', content: response.config.baseURL },
{ title: '请求Method:', content: response.config.method },
{ title: '请求Headers:', content: JSON.stringify(response.config.headers) },
{ title: 'x-api-request-id:', content: response.headers['x-api-request-id'] },
{ title: 'x-api-wl-message:', content: response.headers['x-api-wl-message'] },
{ title: '请求参数:', content: JSON.stringify(params) },
{ title: '请求结果:', content: JSON.stringify(response.data) }
], 'success')
// console.log('请求成功:', response);
if (response.data && response.data.code === '605') {
const riskObj = {};
riskObj.status = 'handling'
riskHandleFunId = functionId;
riskHandleData[functionId] = riskObj;
const riskResult = await FsRiskHandle.verifyRiskRequest(response.data)
// console.log('处置完成:', riskResult);
if (`${riskResult.code}` === '0') {
// 处置成功
riskObj.status = 'noHandle'
const riskToken = riskResult.data;
const xRpExt = { evToken: riskToken, rpId: response.data.disposal.rpId };
const riskData = { evToken: riskToken, xRpExt }
return requestColor({ params, functionId, riskData, ...args });
} else {
riskObj.status = 'handleFail';
let msg = `处置出错了`;
if (riskResult && `${riskResult.code}` === '-3001') {
msg = riskResult.data || '用户取消验证';
}
msg = `处置出错了${riskResult ? riskResult.code : -1}`;
return getCommonFailResult(msg, response)
}
} else if (response.data.echo) {
return getCommonFailResult(`服务器开小差(WG${response.data.code})`, response)
} else if (response?.data?.code === '401') {
// 后端接口返回401(token失效,或后端取不到pin)
console.log('code401', { config: { params, functionId, ...args }, response });
LogX.w('code401', `${JSON.stringify({ config: { params, functionId, ...args }, response })}`);
const msg = '登录状态已失效,请重新登录'
showLoginExpiredDialog()
throw { success: false, message: msg }
}
return response.data
} else {
DebugManager.appendHttpLogs([
{ title: 'functionId:', content: functionId },
{ title: 'baseURL:', content: response.config.baseURL },
{ title: '请求Method:', content: response.config.method },
{ title: '请求Headers:', content: JSON.stringify(response.config.headers) },
{ title: 'x-api-request-id:', content: response.headers['x-api-request-id'] },
{ title: 'x-api-wl-message:', content: response.headers['x-api-wl-message'] },
{ title: '请求参数:', content: JSON.stringify(params) },
{ title: '请求结果:', content: JSON.stringify(response.data) }
], 'error')
// console.log('请求失败:', response);
return response.data
}
}).catch((error) => {
// 隐藏FsLoading组件,如果接口调用前有显示FsLoading,则此处在请求异常后进行隐藏
FsLoading.hide()
DebugManager.appendHttpLogs([
{ title: 'functionId:', content: functionId },
{ title: 'baseURL:', content: host(env) },
{ title: '请求参数:', content: JSON.stringify(params) },
{ title: '请求结果:', content: '请求出错了.' },
{ title: '异常信息:', content: JSON.stringify(error) }
], 'error')
console.log('请求出错:', error);
LogX.e('请求出错', `错误:${JSON.stringify(error)}`)
// 2024-01-30
// 添加throw是为了做熔断,跳到登录页,这里的请求异常错误(未到服务器端、网络框架报错),
// 不做熔断处理,所以依然需要返回一个错误信息,而不是使用throw抛出异常
// throw error
return { success: false, message: '请求出错了.', error }
});
themes/commonStyles.js
import React from 'react';
import { StyleSheet, StatusBar } from 'react-native';
import { XXDevice } from '@XXreact/XXreact-core-lib';
import * as _ from 'lodash';
const rpx = XXDevice.getRpx;
const basePx = 390;
/** 布局参数 */
export const ROW = 'row';
export const NAVBAR_HEIGHT = rpx(85);
export const COLUMN = 'column';
export const CENTER = 'center';
export const FLEX_START = 'flex-start';
export const FLEX_END = 'flex-end';
export const SPACE_BETWEEN = 'space-between';
export const SPACE_AROUND = 'space-around';
/** 文字色值 */
export const TITLE_COLOR1 = '#1A1A1A'; // 标题
export const TITLE_COLOR2 = '#808080'; // 副标题
export const DES_COLOR1 = '#CCCCCC'; // 描述
export const commonStyles = {
// 常规
XXZhengHeiTi: {
fontFamily: 'XXZhengHT'
// fontWeight: '100'
},
// 项目公共样式
/************ 文字组合 ************/
fontTitle10: { color: TITLE_COLOR1, fontSize: 10 }, // 标题
fontTitle12: { color: TITLE_COLOR1, fontSize: 12 }
}
// 过滤样式中包含px值的数据
export const objPx2dp = (commonStyles) => {
// 将相关
if (_.isObject(commonStyles)) {
for (let key in commonStyles) {
// 判断是否包含width,height,
// fontSize,
// marginLeft, marginRight, marginTop,marginBottom,marginVertical,marginHorizontal
// paddingLeft, paddingRight, paddingTop,paddingBottom,paddingVertical,paddingHorizontal,
// borderRadius, borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadius
const baseArr = [
'width',
'height',
'fontSize',
'marginLeft',
'marginRight',
'marginTop',
'marginBottom',
'marginVertical',
'marginHorizontal',
'paddingLeft',
'paddingRight',
'paddingTop',
'paddingBottom',
'paddingVertical',
'paddingHorizontal',
'borderRadius',
'borderTopLeftRadius',
'borderTopRightRadius',
'borderBottomLeftRadius',
'borderBottomRightRadius'
];
if (_.isObject(commonStyles[key])) {
for (let styleKey in commonStyles[key]) {
if (baseArr.indexOf(styleKey) > -1) {
// 如果宽度等于屏幕宽,高度等于屏高,则不做转换
if ((styleKey === 'width' && commonStyles[key][styleKey] == XXDevice.width) || (styleKey === 'height' && commonStyles[key][styleKey] == XXDevice.height) || !isNumber(commonStyles[key][styleKey])) {
// 此处不做处理
} else {
commonStyles[key][styleKey] = px2dp(commonStyles[key][styleKey]);
}
}
}
}
}
}
return commonStyles;
};
export default function cStyles(oStyle) {
if (!_.isObject(oStyle)) {
const newCommonStyles = _.cloneDeep(commonStyles);
const commonStyle = objPx2dp(newCommonStyles);
return StyleSheet.create(Object.assign({}, commonStyle));
}
try {
const newCommonStyles = _.cloneDeep(commonStyles);
const commonStyle = objPx2dp(newCommonStyles);
const oldStyle = objPx2dp(oStyle);
return StyleSheet.create(Object.assign({}, commonStyle, oldStyle));
} catch (e) {
const newCommonStyles = _.cloneDeep(commonStyles);
const commonStyle = objPx2dp(newCommonStyles);
return StyleSheet.create(Object.assign({}, commonStyle));
}
}