app.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * @Author: code4eat awesomedema@gmail.com
  3. * @Date: 2022-12-14 14:14:32
  4. * @LastEditors: code4eat awesomedema@gmail.com
  5. * @LastEditTime: 2023-03-29 16:03:35
  6. * @FilePath: /BudgetManaSystem/src/app.ts
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. */
  9. // 运行时配置
  10. // 全局初始化数据配置,用于 Layout 用户信息和权限初始化
  11. // 更多信息见文档:https://next.umijs.org/docs/api/runtime-config#getinitialstate
  12. import { AxiosResponse } from '@umijs/max';
  13. import { message, notification } from 'antd';
  14. import type { RequestConfig } from 'umi';
  15. import iconEnum from './menuIcons.js';
  16. import DevicePixelRatio from './utils/devicePixelRatio.js';
  17. import Icon, { createFromIconfontCN } from '@ant-design/icons';
  18. const IconFont = createFromIconfontCN({
  19. scriptUrl: '//at.alicdn.com/t/c/font_3878861_tw9yqguouab.js',
  20. });
  21. // 错误处理方案: 错误类型
  22. enum ErrorShowType {
  23. SILENT = 0,
  24. WARN_MESSAGE = 1,
  25. ERROR_MESSAGE = 2,
  26. NOTIFICATION = 3,
  27. REDIRECT = 9,
  28. }
  29. // 与后端约定的响应数据格式
  30. interface ResponseStructure {
  31. success?: boolean;
  32. code?: string;
  33. data: any;
  34. errorCode?: number;
  35. errorMessage?: string;
  36. showType?: ErrorShowType;
  37. }
  38. export async function getInitialState(): Promise<{ name?: string, isCollapsed: boolean }> {
  39. new DevicePixelRatio().init();
  40. return { isCollapsed: false };
  41. }
  42. export const request: RequestConfig = {
  43. // 统一的请求设定
  44. timeout: 100000000,
  45. headers: { 'X-Requested-With': 'XMLHttpRequest' },
  46. // 错误处理: umi@3 的错误处理方案。
  47. errorConfig: {
  48. // 错误抛出
  49. errorThrower: (res: ResponseStructure) => {
  50. const { success, data, errorCode, errorMessage, showType } = res;
  51. //console.log({success, data, errorCode, errorMessage, showType});
  52. if (!success) {
  53. const error: any = new Error(errorMessage);
  54. error.name = 'BizError';
  55. error.info = { errorCode, errorMessage, showType, data };
  56. throw error; // 抛出自制的错误
  57. }
  58. },
  59. // 错误接收及处理
  60. errorHandler: (error: any, opts: any) => {
  61. if (opts?.skipErrorHandler) throw error;
  62. // 我们的 errorThrower 抛出的错误。
  63. if (error.name === 'BizError') {
  64. const errorInfo: ResponseStructure | undefined = error.info;
  65. if (errorInfo) {
  66. const { errorMessage, errorCode } = errorInfo;
  67. switch (errorInfo.showType) {
  68. case ErrorShowType.SILENT:
  69. // do nothing
  70. break;
  71. case ErrorShowType.WARN_MESSAGE:
  72. message.warning(errorMessage);
  73. break;
  74. case ErrorShowType.ERROR_MESSAGE:
  75. message.error(errorMessage);
  76. break;
  77. case ErrorShowType.NOTIFICATION:
  78. notification.open({
  79. description: errorMessage,
  80. message: errorCode,
  81. });
  82. break;
  83. case ErrorShowType.REDIRECT:
  84. // TODO: redirect
  85. break;
  86. default:
  87. message.error(errorMessage);
  88. }
  89. }
  90. } else if (error.response) {
  91. // Axios 的错误
  92. // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
  93. message.error(`Response status:${error.response.status}`);
  94. } else if (error.request) {
  95. // 请求已经成功发起,但没有收到响应
  96. // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
  97. // 而在node.js中是 http.ClientRequest 的实例
  98. message.error('None response! Please retry.');
  99. } else {
  100. // 发送请求时出了点问题
  101. message.error('Request error, please retry.');
  102. }
  103. },
  104. },
  105. // 请求拦截器
  106. requestInterceptors: [
  107. (config: RequestConfig) => {
  108. // 拦截请求配置,进行个性化处理。
  109. const userData = localStorage.getItem('userData');
  110. const { token = '' } = JSON.parse(userData as any);
  111. return { ...config, url: `/gateway${config.url}`, headers: { token } }
  112. // return { ...config }
  113. }
  114. ],
  115. // 响应拦截器
  116. responseInterceptors: [
  117. (response: AxiosResponse) => {
  118. // 拦截响应数据,进行个性化处理
  119. const { status, data: { success, status: code, errorMessage, data: respData }, config: { method } } = response;
  120. try {
  121. if (status == 200) {
  122. // 网络请求成功
  123. if (method == 'post') {
  124. if (code == 200) {
  125. return response.data.data == null ? {
  126. success: true,
  127. data: true
  128. } : response.data
  129. } else {
  130. notification.error({
  131. message: '',
  132. description: errorMessage,
  133. placement: 'topRight',
  134. icon: <></>
  135. })
  136. return false
  137. }
  138. } else {
  139. if (status != 200) {
  140. message.error('请求失败!');
  141. return false
  142. } else {
  143. if (code == 200) {
  144. return response.data
  145. } else {
  146. notification.error({
  147. message: '',
  148. description: errorMessage,
  149. placement: 'topRight',
  150. icon: <></>
  151. })
  152. return false;
  153. }
  154. }
  155. }
  156. } else {
  157. return false
  158. }
  159. } catch (error) {
  160. console.log({ error });
  161. }
  162. }
  163. ]
  164. };
  165. export type menuDataItemType = {
  166. path: string,
  167. name: string,
  168. icon: any,
  169. component?: string,
  170. softUrl?: string, // 帆软url
  171. children?: menuDataItemType[]
  172. }
  173. // 将服务端获取的菜单 icon 字符串映射为对应的 icon Dom
  174. const mappingIcon = (menuData: menuDataItemType[]) => {
  175. if (menuData.length == 0) {
  176. return [
  177. {
  178. path: '',
  179. name: '',
  180. icon: '',
  181. component: './404',
  182. }
  183. ]
  184. }
  185. const mappingMenu: menuDataItemType[] = menuData.map(item => ({
  186. ...item,
  187. icon: item.icon && iconEnum[item.icon],
  188. children: item.children ? mappingIcon(item.children) : [],
  189. }));
  190. return mappingMenu;
  191. };
  192. const imgNode = (props: any) => {
  193. return <IconFont type='icon-shouye' {...props} />
  194. };
  195. const fileIcon = (params: any) => {
  196. return <IconFont type='icon-jixiaoguanli' />
  197. };
  198. const setting = (params: any) => {
  199. return <IconFont type='icon-xitongshezhi' />
  200. };
  201. //布局配置
  202. export const layout = ({ initialState, setInitialState }: { initialState: any, setInitialState: any }) => {
  203. const { isCollapsed } = initialState;
  204. const onCollapse = (isCollapsed: boolean): void => {
  205. setInitialState({ ...initialState, isCollapsed }).then();
  206. };
  207. return {
  208. menuHeaderRender: false,
  209. token: {
  210. sider: {
  211. colorMenuBackground: '#fff',
  212. colorTextMenuActive: '#3376FE',
  213. colorTextMenuSelected: '#3376FE',
  214. colorTextMenuTitle: '#17181A',
  215. colorTextMenu: '#17181A',
  216. // // colorBgMenuItemHover:'##f0f2f5',
  217. colorBgMenuItemSelected: '#F2F6FF',
  218. // colorBgMenuItemCollapsedHover:'#f0f2f5',
  219. // //colorBgMenuItemCollapsedSelected:'blue'
  220. }
  221. },
  222. siderWidth: 200,
  223. menu: {
  224. locale: false,
  225. request: async () => {
  226. const data: menuDataItemType[] = [
  227. {
  228. path: '/home',
  229. name: '首页',
  230. icon: <Icon component={imgNode} />,
  231. },
  232. {
  233. path: '/budgetMana',
  234. name: '绩效管理',
  235. icon: <Icon component={fileIcon} />,
  236. children: [
  237. {
  238. path: '/budgetMana/monthlySet',
  239. name: '月度结转',
  240. icon: '',
  241. },
  242. {
  243. path: '/budgetMana/monthlyInfoCheck',
  244. name: '月度设置信息核对',
  245. icon: '',
  246. },
  247. {
  248. path: '/budgetMana/monthlyDataCheck',
  249. name: '月度绩效数据核对',
  250. icon: '',
  251. },
  252. {
  253. path: '/budgetMana/personnelSalaryBudget',
  254. name: '人事薪酬预算',
  255. icon: '',
  256. },
  257. {
  258. path: '/budgetMana/oneBatch',
  259. name: '一次分配',
  260. icon: '',
  261. },
  262. ],
  263. },
  264. {
  265. path: '/setting',
  266. name: '系统设置',
  267. icon: <Icon component={setting} />,
  268. children: [
  269. {
  270. path: '/setting/baseSetting',
  271. name: '基础设置',
  272. icon:'',
  273. children: [
  274. {
  275. path: '/setting/baseSetting/dicClassfication',
  276. name: '业务字典分类管理',
  277. icon: '',
  278. },
  279. {
  280. path: '/setting/baseSetting/businessDicMana',
  281. name: '业务字典管理',
  282. icon: '',
  283. },
  284. {
  285. path: '/setting/baseSetting/paramsMana',
  286. name: '参数管理',
  287. icon: '',
  288. }
  289. ]
  290. },
  291. {
  292. path: '/setting/manaPerformanceSet',
  293. name: '管理绩效设置',
  294. icon:'',
  295. children: [
  296. {
  297. path: '/setting/manaPerformanceSet/manaIndicItemSet',
  298. name: '管理指标项目设定',
  299. icon: '',
  300. },
  301. {
  302. path: '/setting/manaPerformanceSet/classAssessAndGradeSet',
  303. name: '职类考核分级设定',
  304. icon: '',
  305. },
  306. {
  307. path: '/setting/manaPerformanceSet/indicGroupWeightSet',
  308. name: '指标分组权重设定',
  309. icon: '',
  310. },
  311. ]
  312. },
  313. ],
  314. }
  315. ];
  316. // console.log('mappingIcon(data)',mappingIcon(data));
  317. // return mappingIcon(data);
  318. return data
  319. },
  320. },
  321. onPageChange: (location: Location) => {
  322. // console.log({location});
  323. // const {pathname} = location;
  324. // localStorage.setItem('currentPath',pathname);
  325. },
  326. collapsedButtonRender: () => {
  327. return (
  328. <div style={{
  329. position: 'absolute', zIndex: 10, right: 17, bottom: 12,
  330. display: 'flex', justifyContent: 'center', alignItems: 'center',
  331. cursor: 'pointer', width: 24, height: 24
  332. }} onClick={
  333. () => {
  334. onCollapse(isCollapsed ? false : true);
  335. }
  336. }><IconFont className='menuCollapseIcon' type={isCollapsed ? 'icon-celanzhankai' : 'icon-celanshouqi'} /></div>
  337. )
  338. },
  339. collapsed: isCollapsed,
  340. contentStyle: {
  341. border: '16px solid #F7F9FC',
  342. //height: '94.5vh', //以去除顶部导航高度
  343. borderRadius: '22px',
  344. background: '#F7F9FC',
  345. // overflow:'scroll',
  346. // margin:'20px 20px'
  347. },
  348. };
  349. };