react native积累一:搭建

186 阅读12分钟

根目录

  • 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));
  }
}