app.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. * @Author: your name
  3. * @Date: 2021-09-03 14:28:27
  4. * @LastEditors: Please set LastEditors
  5. * @Description: In User Settings Edit
  6. * @FilePath: /MedicalWisdomCheckSys/src/app.tsx
  7. */
  8. import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
  9. import { PageLoading } from '@ant-design/pro-layout';
  10. import type { RunTimeLayoutConfig} from 'umi';
  11. import {history, Link } from 'umi';
  12. import { notification, Modal } from 'antd';
  13. // import 'yet-another-abortcontroller-polyfill'
  14. import RightContent from '@/components/RightContent';
  15. import { BookOutlined, LinkOutlined } from '@ant-design/icons';
  16. import iconEnum from './menuIcons';
  17. import {loginOut} from '@/utils';
  18. import logoIcon from '../public/logo.png';
  19. import { getMenus } from './services/user';
  20. import {updateUserInfo} from '@/pages/user/Login/service';
  21. import type { RequestOptionsInit,Context } from 'umi-request';
  22. const isDev = process.env.NODE_ENV === 'development';
  23. const loginPath = '/user/login';
  24. let hospSign: any='';
  25. let ifStopNextRequet: boolean = false; // 控制当出现token过期阻止后面弹窗提示
  26. if(history){
  27. hospSign = history.location.query?.hospSign;
  28. if(!hospSign){
  29. hospSign = localStorage.getItem('hospSign');
  30. }
  31. }
  32. /** 获取用户信息比较慢的时候会展示一个 loading */
  33. // export const initialStateConfig = {
  34. // loading: <PageLoading />,
  35. // };
  36. export type menuDataItemType = {
  37. path: string,
  38. name: string,
  39. icon: any,
  40. component: string,
  41. softUrl?: string, // 帆软url
  42. children?: menuDataItemType[]
  43. }
  44. /**
  45. * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
  46. * */
  47. export async function getInitialState(): Promise<{
  48. settings?: Partial<LayoutSettings>;
  49. currentUser?: API.CurrentUserData;
  50. logo?: string;
  51. isDev?: boolean,
  52. spacicalPageParamsType?:any[];
  53. goSetting?: boolean,// 设置栏触发
  54. resetPasswordHandle?: (formData: any) => Promise<boolean>,
  55. fetchUserMenu?: () => Promise<menuDataItemType[]>,
  56. menu?: menuDataItemType[],
  57. clearUserData?: () => Promise<boolean>; // 清空用户数据
  58. fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
  59. postDataToTop?: (data: any) => Promise<boolean>,
  60. }> {
  61. const fetchUserInfo = async () => {
  62. try {
  63. const userData = localStorage.getItem('userData');
  64. console.log({userData});
  65. if(userData){
  66. return JSON.parse(userData);
  67. }
  68. throw Error;
  69. } catch (error) {
  70. // history.push(`${loginPath}?hospSign=${hospSign}`);
  71. }
  72. return undefined;
  73. };
  74. const fetchUserMenu = async ()=>{
  75. const menu = await getMenus();
  76. if(menu){
  77. return menu;
  78. }
  79. }
  80. const resetPasswordHandle = async (data: API.EditUserInfoType)=>{
  81. const resp = await updateUserInfo(data);
  82. if(resp){
  83. return Promise.resolve(true);
  84. }
  85. return Promise.resolve(false);
  86. }
  87. const clearUserData = ()=>{
  88. localStorage.removeItem('userData');
  89. return Promise.resolve(true);
  90. };
  91. // 如果是登录页面,不执行
  92. if (history.location.pathname !== loginPath) {
  93. const currentUser = await fetchUserInfo();
  94. return {
  95. fetchUserInfo,
  96. currentUser,
  97. logo:logoIcon,
  98. clearUserData,
  99. menu:[],
  100. fetchUserMenu,
  101. resetPasswordHandle,
  102. isDev:process.env.NODE_ENV == 'development',
  103. settings: {
  104. navTheme:'dark',
  105. layout:'side'
  106. },
  107. };
  108. }
  109. return {
  110. fetchUserInfo,
  111. settings: {},
  112. };
  113. }
  114. const authHeaderInterceptor = (url: string, options: RequestOptionsInit) => {
  115. const userData = localStorage.getItem('userData');
  116. const authHeader = {token:''};
  117. if(userData){
  118. const {token}: API.CurrentUserData = JSON.parse(userData);
  119. authHeader.token = token;
  120. }
  121. return {
  122. url: `${url}`,
  123. options: { ...options, interceptors: true, headers: authHeader },
  124. };
  125. };
  126. interface responseInterceptorsOptions extends RequestOptionsInit {
  127. isCloseNotify?: boolean, // 是否关闭POST请求后反馈
  128. responseSpecifyFeedback?: {
  129. successMessage: string,
  130. errorMessage: string,
  131. isShow: boolean, // 是否启用
  132. }
  133. }
  134. const responseInterceptors = async (response: Response, options: responseInterceptorsOptions) => {
  135. // console.log({response,options});
  136. const requestMethod = options.method;
  137. const {url,responseSpecifyFeedback,isCloseNotify=false} = options;
  138. try {
  139. const {status} = response;
  140. if(status == 200){
  141. // 网络请求成功
  142. const _response = await response.clone().json();
  143. const { status: dataStatus,errorCode,errorMessage,data} = _response;
  144. if(dataStatus == 200){
  145. // 接口请求成功
  146. if(requestMethod=='POST'&&url != "/api/pfm/login"&&!isCloseNotify){
  147. if(responseSpecifyFeedback&&responseSpecifyFeedback.isShow){
  148. // 当指定了请求成功反馈时
  149. console.log({responseSpecifyFeedback});
  150. console.log(responseSpecifyFeedback.successMessage);
  151. notification.success({
  152. message:responseSpecifyFeedback.successMessage
  153. });
  154. }else{
  155. // 否则用默认提示
  156. notification.success({
  157. message:'操作成功!'
  158. });
  159. }
  160. }
  161. if(data){
  162. return data;
  163. }
  164. return true;
  165. }if(errorCode == 499){
  166. if(!ifStopNextRequet){
  167. Modal.confirm({
  168. title: '抱歉,登录已过期请重新登录!',
  169. onOk: () => {
  170. ifStopNextRequet = false;
  171. loginOut();
  172. }
  173. });
  174. }
  175. ifStopNextRequet = true;
  176. }else {
  177. // 接口请求信息错误
  178. notification.error({
  179. message:errorMessage
  180. });
  181. }
  182. }else {
  183. // 网络请求失败
  184. notification.error({
  185. message:'服务器错误!'
  186. });
  187. throw Error;
  188. }
  189. }catch(error){
  190. console.log({error});
  191. }
  192. };
  193. const demo1Middleware = async (ctx: Context, next: () => void) => {
  194. await next();
  195. };
  196. type RequestConfig = Record<string, any>
  197. export const request: RequestConfig = {
  198. // 新增自动添加AccessToken的请求前拦截器
  199. errorHandler: (error: any) => {
  200. console.log({ error });
  201. throw error;
  202. },
  203. middlewares: [demo1Middleware],
  204. requestInterceptors: [authHeaderInterceptor],
  205. responseInterceptors: [responseInterceptors],
  206. };
  207. // 将服务端获取的菜单 icon 字符串映射为对应的 icon Dom
  208. const mappingIcon = (menuData: menuDataItemType[]) => {
  209. if(menuData.length==0){
  210. console.log('空菜单');
  211. return [
  212. {
  213. path:'',
  214. name: '',
  215. icon: '',
  216. component: './404',
  217. }
  218. ]
  219. }
  220. const mappingMenu: menuDataItemType[] = menuData.map(item => ({
  221. ...item,
  222. icon: item.icon&&iconEnum[item.icon],
  223. children: item.children ? mappingIcon(item.children) : [],
  224. }));
  225. return mappingMenu;
  226. };
  227. // export const qiankun = {
  228. // // 应用加载之前
  229. // async bootstrap(props:any) {
  230. // console.log('MedicalWisdomCheckSys bootstrap', props);
  231. // },
  232. // // 应用 render 之前触发
  233. // async mount(props:any) {
  234. // console.log('MedicalWisdomCheckSys mount', props);
  235. // },
  236. // // 应用卸载之后触发
  237. // async unmount(props:any) {
  238. // console.log('MedicalWisdomCheckSys unmount', props);
  239. // },
  240. // };
  241. // ProLayout 支持的api https://procomponents.ant.design/components/layout
  242. export const layout: RunTimeLayoutConfig = ({ initialState,setInitialState }) => {
  243. return {
  244. logo:initialState?.logo,
  245. rightContentRender: () => <RightContent />,
  246. disableContentMargin: false,
  247. // footerRender: () => <Footer />,
  248. onPageChange:() => {
  249. // const { location } = history;
  250. // // 如果没有登录,重定向到 login
  251. // // if (!initialState?.currentUser && location.pathname !== loginPath) {
  252. // // history.push(`${loginPath}?hospSign=${hospSign}`);
  253. // // }
  254. },
  255. menu: {
  256. params:{
  257. currentUser:initialState?.currentUser
  258. },
  259. request: async () => {
  260. // initialState.currentUser 中包含了所有用户信息
  261. if(initialState){
  262. const { currentUser,isDev} = initialState;
  263. if(false){
  264. // 开发环境
  265. return []
  266. }
  267. if (currentUser) {
  268. const data: any[] = await getMenus();
  269. if(data){
  270. await setInitialState(t=>({...t,menu:data}));
  271. /**
  272. *
  273. * 菜单跳转报表临时处理,后期统一换成中台调用
  274. */
  275. const getVFromTree = (data:any[], key: string) => {
  276. let result: any[] = [];
  277. function looper(data: any[], key: string) {
  278. data.forEach((t) => {
  279. if (t[key] && t[key] != 0) {
  280. //非一般页面
  281. result.push({
  282. contentType: t[key],
  283. path: t['path'],
  284. reportId: t['reportId'],
  285. url: t['youshuUrl'],
  286. });
  287. }
  288. if (t.children && t.children.length > 0) {
  289. looper(t.children, key);
  290. }
  291. });
  292. }
  293. looper(data, key);
  294. return result;
  295. };
  296. const _menu = getVFromTree(data, 'contentType');
  297. setInitialState((t) => ({ ...t, spacicalPageParamsType: _menu }));
  298. /////////////////////////////--------临时处理----------///////////////////////////////////////////////
  299. return mappingIcon(data);
  300. }
  301. } else {
  302. return [
  303. {
  304. component: './404',
  305. }
  306. ]
  307. }
  308. }
  309. return []
  310. },
  311. },
  312. links: isDev
  313. ? [
  314. <Link to="/umi/plugin/openapi" target="_blank">
  315. <LinkOutlined />
  316. <span>OpenAPI 文档</span>
  317. </Link>,
  318. <Link to="/~docs">
  319. <BookOutlined />
  320. <span>业务组件文档</span>
  321. </Link>,
  322. ]
  323. : [],
  324. menuHeaderRender: undefined,
  325. // 自定义 403 页面
  326. // unAccessible: <div>unAccessible</div>,
  327. ...initialState?.settings,
  328. };
  329. };