import {Component} from 'react';
export default class WeirwoodErrorBoundary extends Component {
static getDerivedStateFromError(error) {
return {error};
}
constructor(props) {
super(props);
this.state = {
error: null
};
}
componentDidCatch(error, errorInfo) {
this.setState({
error
});
if (window.__Weirwood) {
window.__Weirwood.error.captureException(error);
}
}
render() {
if (this.state.error) {
return <div>页面出错了!请稍后重试</div>;
}
return this.props.children;
}
}
Main.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Switch} from 'react-router';
import {Provider} from 'react-redux';
import ConnectedRouter from './common/connected-react-router';
import WeirwoodErrorBoundary from './common/components/WeirwoodErrorBoundary';
import './resource/style/main.less';
import InitRoutes from './router';
ReactDOM.render(
<WeirwoodErrorBoundary>
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<InitRoutes store={store} history={history} />
</Switch>
</ConnectedRouter>
</Provider>
</WeirwoodErrorBoundary>,
document.getElementById('root')
);
src/app/entry/action.js
/**
* @file action
* @author [email protected]
*
**/
import {get, noop, omit, pick, set, size} from 'lodash';
import {get as getCookie} from 'lib/storage/cookie';
import {set as setStorage, get as getStorage, remove as removeStorage} from 'lib/storage/localStorage';
import {updateRoute, getUrlParam} from 'common/util';
import {logoutAccount} from 'common/util/logout';
import {resetShopInfo} from 'app/shopInfo/action';
import {getBizCode} from 'common/util/platform';
import {NO_AUTH} from 'src/config';
import {
SELECTED_APP_KEY,
INIT_API_CACHE_KEY,
STORAGE_LOGIN_TYPE,
INIT_FOR_DUXIAODIAN,
FROM_MERCHANT_KEY,
PARENT_ID_KEY
} from 'common/config/storageKey';
import {COOKIE_NAME} from 'common/config/cookieConfig';
import {appInitIsloadingSelector} from 'common/selectors/platform';
import message from 'common/containers/wrapReduxFlowForm/formItemFactory/message';
import ACTIONS from './actionTypes';
import {HOME_GUIDE_LOCAL_STORAGE_NAME, SHOW_MENU} from './config';
import {loadCommissionInfo} from './service/loadCommissionInfo';
import {INIT_SESSION_ID} from 'common/config/logConfig';
import {isPassSelector} from '../../../src/selectors';
import {checkUserQulification} from '../assetCenter/service';
import {errorInformer} from '../assetCenter/util';
import {whichBizTypeSelector} from '../../common/selectors/bizType';
import baiduMonitor, {setUserProperty} from '../../common/util/baiduMonitor';
import {basicInfoAction} from './reducer/basicInfo';
import {userIdSelector, optIdSelector, isSonAccountSelector} from 'common/selectors/platform.js';
import {getShopRegisterPath} from 'src/common/util/getPath';
import {needGetHagList} from 'src/common/config/hagInfo';
import {initSurvey} from './util';
import * as api from './service';
import * as homeApi from 'app/home/apis';
import {onlineService} from 'src/common/util/quarkRequest';
const INIT_CACHE_TIME = 43200000; // 12小时
/**
* 更新路由
* @param {string} options.nav 一级导航path
* @param {Object} options.match match
* @param {Object} options.isPurifyQuery 路由
* @return {Function} 回调
*/
export const onUpdateRoute = (params = {}) => (dispatch, getState) => {
const {nav, match} = params;
dispatch({
type: ACTIONS.MENU_CLICK_ROUTER_CHANGE,
payload: {
key: nav,
match: match
}
});
// 路由点击统一增加参数,功能:单独针对生活服务有子店路由全部拼接subShopId参数,兼容强制刷新选中子店逻辑
updateRoute(params, dispatch, getState());
};
export const getToken = state => {
const isPass = isPassSelector(state);
const ucToken = getCookie('CPTK__513') || getCookie('__cas__st__513') || '';
const token = isPass
? ''
: ucToken;
return token;
};
export const changeSelectedBizAction = (appInfo = {}) => (dispatch, getState) => {
const {appId, subAppId, appName} = appInfo;
const shopId = get(appInfo, 'shopInfo.shopId');
const fromMerchant = getStorage(FROM_MERCHANT_KEY);
const monitorShopInfo = {
appId,
subAppId,
appName,
sessionId: INIT_SESSION_ID,
fromMerchant: +fromMerchant || 0
};
shopId && (monitorShopInfo.shopId = shopId);
appId && setUserProperty(monitorShopInfo);
const bizCode = getBizCode(appInfo);
// 存储选中的业务线到localStorage
setStorage(SELECTED_APP_KEY, appInfo);
dispatch({
type: ACTIONS.SELECTED_BIZ_CHANGE,
payload: {
...appInfo,
bizCode
}
});
dispatch(resetShopInfo(get(appInfo, 'shoInfo')));
};
export const initBasicInfo = (params = {}) => (dispatch, getState) => {
dispatch({
type: basicInfoAction['ON_BASIC_INFO_RESET']
});
};
export const changeBasicInfo = (params = {}) => (dispatch, getState) => {
dispatch({
type: basicInfoAction['ON_BASIC_INFO_CHANGE'],
payload: params
});
if (window.__Weirwood && window.__Weirwood.error && window.__Weirwood.error.setContext) {
window.__Weirwood.error.setContext(params);
}
if (window.__Weirwood && window.__Weirwood.perf && window.__Weirwood.perf.addVar) {
window.__Weirwood.perf.addVar(params);
}
};
export const passBindUcAction = (params = {}) => (dispatch, getState) => {
return dispatch(api.passBindUcService(params));
};
export const fetchAppListAction = (params = {}) => (dispatch, getState) => {
return dispatch(api.fetchAppInfoListServer(params));
};
export const getMenuListAction = (params = {}) => (dispatch, getState) => {
const bizCode = whichBizTypeSelector(getState());
return dispatch(api.fetchMenuListServer(params, bizCode));
};
export const getUserHagInfo = (params = {}) => (dispatch, getState) => {
return dispatch(api.fetchUserHagInfoServer(params));
};
export const appInitLoadingChangeAction = (payload = false) => (dispatch, getState) => {
dispatch({
type: ACTIONS.APP_INIT_IS_LOADING,
payload
});
};
export const getShopDetailAction = (params = {}) => (dispatch, getState) => {
return dispatch(api.fetchShopDetailServer(params));
};
export const getShopContactAction = (params = {}) => (dispatch, getState) => {
return dispatch(homeApi.getShopContactInfo(params));
};
export const checkHasRoleAction = (params = {}) => (dispatch, getState) => {
return dispatch(api.fetchCheckHasRoleServer(params));
};
export const changeSelectedBiz = (
app = {},
openedJumpPath,
finallyCb = {}
) => (dispatch, getState) => {
const appInitIsloading = appInitIsloadingSelector(getState());
// 如果正在请求,不执行后续动作;
if (appInitIsloading) {
return Promise.resolve();
}
// 如果URL上存在fromMerchant字段,则代表来源为新登录页merchantLogin,存入redux。后续不置为false
const urlFromMerchant = getUrlParam('fromMerchant', window.location.search) || 0;
const urlParentid = getUrlParam('parentid', window.location.search) || 0;
urlFromMerchant && setStorage(FROM_MERCHANT_KEY, parseInt(urlFromMerchant, 10));
urlParentid && setStorage(PARENT_ID_KEY, parseInt(urlParentid, 10));
const {
initFinally,
menuListFinally
} = finallyCb;
dispatch(changeSelectedBizAction(omit(app, ['mainUcId'])));
const appId = get(app, 'appId');
const subAppId = get(app, 'subAppId');
// 当缓存中存在对应mainUcId,这里会传入mainUcId
const mainUcId = get(app, 'mainUcId');
const appParams = {
appId,
subAppId,
shopId: null,
mainUcId
};
dispatch(appInitLoadingChangeAction(true));
const bizCode = whichBizTypeSelector(getState());
const nowDate = new Date();
const today = `${nowDate.getFullYear()}-${nowDate.getMonth() + 1}-${nowDate.getDate()}`;
const cacheParams = {
duxiaodian: {
cacheKey: INIT_FOR_DUXIAODIAN,
cacheType: 'local',
// 获取对应日期对应用户缓存
cacheGet: ({cacheLocalItem, paramsStr}) => {
return get(cacheLocalItem, `${today}.${paramsStr}`, '');
},
// 设定缓存
cacheSet: ({response, paramsStr}) => {
// 仅在入驻完成后缓存
if (get(response, 'data.shopInfo.showMenu', 0) !== SHOW_MENU) {
return;
}
// 取出缓存
const allInit = getStorage(INIT_FOR_DUXIAODIAN) || {};
// 追加本次缓存
set(allInit, `${today}.${paramsStr}`, JSON.stringify(response));
// 取当天缓存进行缓存set
setStorage(INIT_FOR_DUXIAODIAN, pick(allInit, today));
},
delay: 1000 * 20
},
others: {
cacheKey: INIT_API_CACHE_KEY.init,
cacheType: 'local',
cacheTime: INIT_CACHE_TIME,
active: 'manual'
}
};
// init接口获取ucId和passId,营销模块ucId必传,店铺保存接口必须passId,
return dispatch(api.passBindUcService(appParams, cacheParams.others)).then(res => {
if (res.redirect) {
return;
}
// 存储账户类型
setStorage(STORAGE_LOGIN_TYPE, getCookie(COOKIE_NAME.LOGIN_TYPE));
dispatch(changeBasicInfo({
userId: res.ucId,
optId: res.sonUcId,
userName: res.mainUcName,
optName: res.ucName,
hasHitSmallFlow: res.localSmallFlow || 0 // 0为未命中,1为命中
}));
// 多主账号子账号,跳转shopCenter
// TODO 这里可能影响mcc需要重点回归 @wuxinru
if (!mainUcId && res?.isSonAccount && size(res?.mainShopList) > 1) {
dispatch(appInitLoadingChangeAction(false));
dispatch(onUpdateRoute({nav: '/shopCenter', isReplace: true}));
return;
}
// 存储账户类型
const shopId = get(res, 'shopInfo.shopId');
// 咨询状态改为上线
if (shopId) {
onlineService({
serviceId: res.ucId,
optId: res.sonUcId,
shopId
}).catch(noop);
}
const showMenu = get(res, 'shopInfo.showMenu');
baiduMonitor('all_system_entry');
dispatch(changeSelectedBizAction({
...app,
shopInfo: get(res, 'shopInfo'),
isInHag: res.isInHag,
...(res?.shopInfo
? {
appId: get(res, 'shopInfo.appId'),
subAppId: get(res, 'shopInfo.subAppId')
}
: {}
)
}));
// 拉取当前用户的hag名单
if (needGetHagList.length) {
dispatch(api.fetchUserHagInfoServer({
item: {
hagRoles: needGetHagList,
shopId
}
})).catch(noop);
}
dispatch(appInitLoadingChangeAction(false));
const shopRegisterNav = getShopRegisterPath();
const isSonAccount = isSonAccountSelector(getState());
// 店铺ID存在并且showMenu为1
if (shopId && showMenu) {
// 请求菜单需要加菜单loading态
dispatch(api.fetchMenuListServer({
...appParams,
shopId
}, isSonAccount ? {} : { // 子账号不缓存菜单
cacheKey: INIT_API_CACHE_KEY.moduleList,
cacheType: 'local',
cacheTime: INIT_CACHE_TIME,
delay: 60000
})).then(() => {
// 请求完新业务线的菜单,需要跳转首页
if (openedJumpPath) {
if (openedJumpPath === shopRegisterNav) {
updateRoute({nav: '/home'}, dispatch, getState());
}
else {
updateRoute({nav: openedJumpPath}, dispatch, getState());
}
}
}).finally(() => {
menuListFinally && menuListFinally();
}).catch(noop);
dispatch(api.fetchCheckHasRoleServer({roleName: 'ai_create_product'})).catch(noop);
}
// 未入驻成功,进入店铺入驻
else {
dispatch(onUpdateRoute({nav: shopRegisterNav, isReplace: true}));
}
if (!window.surveyApp) {
initSurvey({
userid: res.ucId,
optid: res.sonUcId
});
}
dispatch(authority());
}).catch(err => {
const errCode = +get(err, 'errors.0.code');
let defaultMsg = '';
if (errCode === NO_AUTH) {
defaultMsg = '抱歉,您当前账号暂无开通业务权限。';
}
const errMsg = get(err, 'errors.0.message', defaultMsg);
errMsg && message.error(errMsg);
}).finally(re => {
initFinally && initFinally();
});
};
// 鉴权
export const authority = () => (dispatch, getState) => {
const userId = userIdSelector(getState());
const optId = optIdSelector(getState());
const changeAuth = payload => dispatch({
type: ACTIONS.ON_AUTH_INFO_CHANGE,
payload
});
changeAuth({userId, optId});
};
// 发起登出
export const logoutUcAndPass = () => (dispatch, getState) => {
// 手动点击退出登录
logoutAccount();
};
export const getCommissionInfo = params => dispatch => {
return dispatch(loadCommissionInfo(params));
};
export const checkQulification = () => dispatch => {
dispatch(checkUserQulification({})).catch(e => {
errorInformer(e.error);
});
};
export const setGuideStorage = () => dispatch => {
const {KEY, OPTIONS: {VISIBLE}} = HOME_GUIDE_LOCAL_STORAGE_NAME;
// 用户登录时不存在localStorage:KEY时,添加KEY
!getStorage(KEY) && setStorage(KEY, VISIBLE);
};
router/index.js
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actionCreators from 'src/app/entry/action';
import {
menuMapSelector,
getShopShowMenuRes,
getRouterPathname,
getUrlSearch,
isShopRegisterPage,
querySubShopIdSelector
} from 'common/selectors/platform';
import {
isServiceEcommerceBizSelector
} from 'common/selectors/bizType';
import Main from './Main.js';
const mapStateToProps = state => {
const menuMap = menuMapSelector(state);
const isShopRegister = isShopRegisterPage(state);
const isService = isServiceEcommerceBizSelector(state);
return {
menuMap,
showMenu: getShopShowMenuRes(state),
pathname: getRouterPathname(state),
search: getUrlSearch(state),
isShopRegister,
isService,
querySubShopId: querySubShopIdSelector(state)
};
};
const mapDispatchToProps = dispatch => {
return bindActionCreators(actionCreators, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);