Umi+React+Xrender+Hsf项目开发总结

一、菜单路由配置

1.umirc.ts 中的路由配置

.umirc.ts 文件是 UmiJS 框架中的一个配置文件,用于配置应用的全局设置,包括但不限于路由、插件、样式等。

import { defineConfig } from 'umi';
import config from './def/config';export default defineConfig({plugins: ['@umijs/plugins/dist/model', '@umijs/plugins/dist/request'],model: {},request: {},title: '客服管理后台',publicPath: config.publicPath,favicons: [// 'https://domain.com/favicon.ico',//设置icon],esbuildMinifyIIFE: true,history: {type: 'hash',},routes: [{path: '/',component: '@/pages/business/index',},{path: '/public',component: '@/pages/publicInstance/index',layout: false,ignoreAuth: true,},{path: '/common',name: '通用',icon: 'menu',routes: [{path: '/common/business',name: '业务线管理',component: '@/pages/business/index',},{path: '/common/peopleManage',name: '人员管理配置',component: '@/pages/index',},{path: '/common/skills',name: '技能组管理配置',component: '@/pages/skills/index',},{path: '/common/dialogChange',name: '转交配置',component: '@/pages/dialogChange/index',},],},// 其他路由配置...],
});
主要配置项解释:
  1. defineConfig:

    • 这是一个从 umi 导入的函数,用来创建 Umi 配置对象。它帮助开发者更方便地编写和组织配置。
  2. plugins:

    • 定义使用的插件列表。这里使用了两个插件:@umijs/plugins/dist/model 和 @umijs/plugins/dist/request,它们分别用于状态管理和请求处理。
  3. routes:

    • 定义应用的路由表,每个路由对象可以有以下属性:
      • path: 路由路径。
      • component: 对应路径下加载的组件。
      • routes: 子路由数组,允许嵌套路由。
      • layout: 是否使用布局,默认是 true。当设置为 false 时,表示该路由下的页面不使用全局布局。
      • ignoreAuth: 忽略权限验证,对于无需登录即可访问的公共页面非常有用。
示例分析:
  • 根路径 /:

    • 当用户访问根路径时,会加载 @/pages/business/index 组件。
  • 免登录页面 /public:

    • 访问此路径时,加载 @/pages/publicInstance/index 组件,且设置了 layout: false 和 ignoreAuth: true,意味着这个页面不会使用全局布局,并且不需要进行权限验证。
  • 通用模块 /common:

    • 包含四个子路由,每个子路由都有自己的 pathname 和对应的 component。这些子路由都归属于“通用”分类下,通过 icon 属性可以在侧边栏或导航中显示相应的图标。

2.menuConfig.ts 中的菜单项配置

menuConfig.ts 文件则通常用于定义侧边栏或顶部导航栏的菜单结构。

import React from 'react';
import {BuildOutlined,
} from '@ant-design/icons';
import { MenuProps } from 'antd';// 定义菜单项类型
export type MenuItem = NonNullable<MenuProps['items']>[number];
export const menuData: MenuItem[] = [{key: 'common',icon: React.createElement(BuildOutlined),label: '通用',children: [{key: '/common/business',label: '业务线管理',},// ...更多菜单项],},// ...更多菜单组
];

1. MenuProps['items']

这是从 Ant Design 的 MenuProps 类型中取出 items 属性的类型。

1. NonNullable<...>

  • NonNullable<T> 是 TypeScript 内置的一个工具类型,用于从类型 T 中排除 null 和 undefined。换句话说,它将类型 T 中可能存在的 null 或 undefined 移除掉。

3. [number] 

  • 从 (MenuItemType)[] 中提取出单个元素的类型,即 MenuItemType

二、hsf接口的调用

1、配置服务信息

import business from '@/pages/business';
import AccessProcess from '@/pages/entrance/accessProcess';
import { access } from 'fs';
import java from 'js-to-java';
import { request } from 'umi';export const isDaily = window.location.host.match(/localhost|.alibaba.net|-test.uc.alibaba-inc.com|30.211.81.4/,
);
export const version1 = isDaily ? '1.0.0.DAILY' : '1.0.0';
export const version2 = isDaily ? '2.0.1.DAILY' : '2.0.1';
export const version3 = isDaily ? '2.0.0.KF.DAILY' : '2.0.0.KF';// 固定的hsf服务信息
const servicer: any = {process: {appName: 'cs-xadmin',pathname: 'xxxx',version: isDaily ? '2.0.1.DAILY' : '2.0.1',},oldProcess: {appName: 'cs-xxflow',pathname: 'xxxx',version: isDaily ? '2.0.1.DAILY' : '2.0.1',},
};
/*** hsf通用调取方法* @param {string} key 对应服务key值(获取pathname:version)* @param {string} className 类名* @param {string} action 方法名* @param {string} appName 应用名* @param {any} data 参数**/
export const hsfApi = async (key: string, { className, action, data }: any) => {// 如果是日常环境 & 路径上带有connectToLocalHsf参数,则调用本地hsf,把version中的DAILY 替换为 LOCALlet version = servicer[key]?.version;if (isDaily && window.location.hash.includes('connectHsf=local')) {version = servicer[key].version.replace('DAILY', 'LOCAL');}return request(`/api/hsf?action=${action}`, {method: 'POST',data: {pathname: `${servicer[key]?.pathname}:${version}`,action,appName: servicer[key].appName,data:data && className? [java(className, {...data,}),]: [],},});
};
pathname:
 appName:

2、封装请求函数

// @ts-ignore
/* eslint-disable */
import { hsfApi } from '@/utils/api';/*** 分页查询技能组溢出规则* @param params 查询参数*/
export async function pageList(params: {pageNo?: number;pageSize?: number;name?: string;fromSkillgroupId?: number;toSkillgroupId?: number;
}) {return hsfApi('onlineDispatch', {className: 'com.uc.cs.xadmin.client.param.dispatchschedule.PageOnlineDispatchScheduleParam',action: 'pageList',data: {...params,}});
className: 

action: 

 

参数解释:
  • 'onlineDispatch'
    • 表示要调用的服务名,对应你在 api.ts 中配置的 servicer 对象里的键值。
    • 会去查找该服务的应用名、路径、版本等信息。
  • className
    • 表示你这次请求的参数在后端对应的 Java 类型。
  • action: 'pageList'
    • 表示你要调用的方法名,即后端服务提供的某个接口方法。
  • data: { ...params }
    • 把传入的 params 参数展开并传入请求体中。
    • 最终会被包装成一个 Java 对象发送到后端

三、页面开发

1、父组件

(1)懒加载

import React from 'react';
const AddEditModal = React.lazy(() => import('./components/AddEditModal'));

 React.lazy 是 React 提供的一个用于实现代码分割的功能。通过 React.lazy 和动态 import() 语法,你可以按需加载组件,而不是在应用初始化时就加载所有组件。

(2)useMemo

  const schema = useMemo(() => getOnlineDispatchSchema(groupValues), [groupValues]);

useMemo 是一个用于记忆化的 Hook,它接收两个参数:

  1. 一个创建函数:该函数返回需要被记忆化的值。
  2. 一个依赖项数组:只有当这些依赖项发生变化时,才会重新计算记忆化的值;否则将返回之前记忆化的结果。
  const columns: any = useMemo(() => {   {title: '最后修改信息',dataIndex: 'modifyInfo', // 可以不绑定真实字段,仅占位width: 200,render: (_: any, record: any) => {const modifyTime = dayjs(record.modifyTime).format('YYYY-MM-DD HH:mm:ss');const modifier = record.modifier || '未知';return (<div style={{ lineHeight: 1.5 }}><div>{modifyTime}</div><div>{modifier}</div></div>);},},
}

(3)render

render: (_: any, record: any) => {} 的用法
  1. _:当前列的值。有时你可能不需要使用这个值,所以通常用下划线 _ 来表示忽略这个参数。

  2. record:当前行的所有数据。这是一个对象,包含了该行所有字段的信息。

(4)请求缓存避免发起多个相同请求 

// 在组件外部创建缓存
const skillGroupsCache = {data: null as SkillGroup[] | null,promise: null as Promise<SkillGroup[] | null> | null // 允许Promise返回null
};const fetchSkillGroups = async () => {if (skillGroupsCache.data) return skillGroupsCache.data;if (skillGroupsCache.promise) return skillGroupsCache.promise;skillGroupsCache.promise = (async () => {try {const result = await index({/* 参数 */});skillGroupsCache.data = result.code === 200 ? result.data.rows?.map((v: SkillGroupResponse)  => ({ label: v.name, value: v.id })) || []: [];return skillGroupsCache.data;} catch (error) {console.error('获取技能组失败:', error);return [];} finally {skillGroupsCache.promise = null;}})();return skillGroupsCache.promise;
};
缓存对象 skillGroupsCache

  • data:用于存储从服务器获取到的技能组数据。如果已经成功获取了数据,则直接使用缓存的数据,无需再次发起网络请求。

  • promise:用于存储正在进行中的异步请求,这样可以确保在同一个数据获取过程中,如果有多个地方同时请求相同的数据,它们将共享同一个请求结果,而不是各自发起新的请求。

  1. 检查缓存数据

    • 首先检查 skillGroupsCache.data 是否已经有值。如果有,说明之前已经成功获取过数据,直接返回缓存的数据,避免重复请求。
  2. 检查进行中的请求

    • 如果没有缓存的数据,但 skillGroupsCache.promise 不为空,说明当前有一个正在进行中的请求。在这种情况下,直接返回这个正在进行中的请求(Promise),所有调用者将等待同一个请求的结果,而不是各自发起新的请求。
  3. 发起新请求

    • 如果既没有缓存的数据也没有正在进行中的请求,则创建一个新的异步请求来获取数据,并将其存储在 skillGroupsCache.promise 中。
    • 在请求成功后,将获取到的数据存储在 skillGroupsCache.data 中,并清空 skillGroupsCache.promise
    • 如果请求失败,记录错误信息并返回一个空数组。
  4. 清理

    • 无论请求成功还是失败,在 finally 块中都会将 skillGroupsCache.promise 设置为 null,以便后续的请求可以正常发起。

(5)初始化状态锁

 // 添加初始化状态锁const initializedRef = useRef(false);// 初始化数据useEffect(() => {console.log('initializedRef.current',initializedRef.current);if (initializedRef.current) return;initializedRef.current = true;const initData = async () => {setSpinning(true);try {setTableHeight(useInitTableHeight(-10));const [groups, pageData] = await Promise.all([fetchSkillGroups(),fetchPageList({ pageNo: 1, pageSize: 20 }) // 合并初始化请求]);setGroupValues(groups || []);setRawTableData(pageData.data.data.list || []);// setInitialized(true);} catch (error) {console.error('初始化失败:', error);} finally {setSpinning(false);}};initData();
}, []);

通过 if (initializedRef.current) return; 来判断是否已经执行过初始化操作。如果已经初始化则直接返回,不重复执行初始化逻辑。

使用 useRef 而不是 useState 的原因
  • useRefuseRef 返回一个可变的引用对象,其 .current 属性在组件的整个生命周期内保持不变。修改 .current 属性不会触发组件的重新渲染

  • useState:每次调用 setState 函数都会导致组件重新渲染。如果我们使用 useState 来管理初始化状态,那么每当更新该状态时,都会导致组件重新渲染,这可能会引起性能问题或意外的行为。

(6)??和...运算符

  // 构造请求参数const payload: Record<string, any> = {pageNo: current ?? 1,pageSize: pageSize ?? 20,...(onlineDispatchName && { name: onlineDispatchName }),...(overflowSkillGroup && { fromSkillgroupId: overflowSkillGroup }),...(inflowSkillGroup && { toSkillgroupId: inflowSkillGroup }),...(modifier && { modifier }),};
??对比逻辑或运算符 (||)

在 ES2020 之前,开发者通常使用逻辑或运算符 (||) 来提供默认值。但是,|| 运算符会在左侧操作数是任何假值(如 0, false, '' 等)时也返回右侧的默认值

let current = 0;
console.log(current || 1); // 输出: 1 (可能不符合预期)
console.log(current ?? 1); // 输出: 0 (符合预期)

(7)枚举列表转换成对象

/*** 格式化enums* @param list - 所需要格式化的列表* list格式: [{value: "offline", desc: "下线"}, {value: "online", desc: "上线"}]* @returns 返回格式化后的enums {offline: "下线", online: "上线"}*/
const formatEnums = (list: any, label: string = 'desc', code: string = 'code') => {if (!list || list.length === 0) return null;return (list?.reduce((acc: any, cur: any) => {acc[cur[code]] = cur[label];return acc;}, {}) || null);
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82671.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【运维】基于Python打造分布式系统日志聚合与分析利器

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在分布式系统中,日志数据分散在多个节点,管理和分析变得复杂。本文详细介绍如何基于Python开发一个日志聚合与分析工具,结合Logstash和F…

Python实战:海量获取京东商品信息

在数据驱动的商业时代&#xff0c;数据就是最宝贵的资源。对于电商从业者、市场分析师而言&#xff0c;从京东这类大型电商平台获取商品信息&#xff0c;能够为市场调研、竞品分析、销售策略制定提供重要依据。今天&#xff0c;就来分享如何用Python实现京东商品信息的海量获取…

聊一聊常见的超时问题:timeout

大家好&#xff0c;我是G探险者&#xff01; 在日常开发中&#xff0c;“超时&#xff08;Timeout&#xff09;”类错误是开发者们经常遇到的问题。无论是调用第三方服务、访问数据库&#xff0c;还是并发任务处理&#xff0c;都可能因超时而导致请求失败或系统异常。 本文将系…

创建型模式:工厂方法(Factory Method)模式

一、简介 工厂方法(Factory Method)模式是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。在 C# 中,工厂方法模式提供了一种更灵活的对象创建方式,将对象的创建和使用分离,提高了代码的可维护性和…

外网访问内网海康威视监控视频的方案:WebRTC + Coturn 搭建

外网访问内网海康威视监控视频的方案&#xff1a;WebRTC Coturn 需求背景 在仓库中有海康威视的监控摄像头&#xff0c;内网中是可以直接访问到监控摄像的画面&#xff0c;由于项目的需求&#xff0c;需要在外网中也能看到监控画面。 实现这个功能的意义在于远程操控设备的…

Redis 8.0正式发布,再次开源为哪般?

Redis 8.0 已经于 2025 年 5 月 1 日正式发布&#xff0c;除了一些新功能和性能改进之外&#xff0c;一个非常重要的改变就是新增了开源的 AGPLv3 协议支持&#xff0c;再次回归开源社区。 为什么说再次呢&#xff1f;这个需要从 2024 年 3 月份 Redis 7.4 说起&#xff0c;因为…

382_C++_在用户会话结束时,检查是否有其他会话仍然来自同一个客户端 IP 地址,没有连接状态设置为断开,否则为连接

之前出现的问题:重启管理机,工作机上面热备连接状态显示未连接 (此时是有一个工作机连接管理机的),所以正常应该是连接状态解决:根因分析: 重启管理机后,管理机给过来的cookie是空的,导致工作机同时存在两个管理机的session,在其中一个超时后,调用回调函数通知会话断开…

大模型系列(五)--- GPT3: Language Models are Few-Shot Learners

论文链接&#xff1a; Language Models are Few-Shot Learners 点评&#xff1a; GPT3把参数规模扩大到1750亿&#xff0c;且在少样本场景下性能优异。对于所有任务&#xff0c;GPT-3均未进行任何梯度更新或微调&#xff0c;仅通过纯文本交互形式接收任务描述和少量示例。然而&…

【网络分析工具】网络工具wireshark、TCPdump、iperf使用详解

这里写目录标题 1. wireshark1.1. 过滤包1.2. 常见分析 2. tcpdump3. iperf 1. wireshark **ip.dst eq 10.0.0.21** 是用于网络流量分析工具&#xff08;例如 Wireshark 或 tcpdump&#xff09;的过滤器表达式。 它的作用是筛选出所有目标IP地址为 10.0.0.21 的数据包 IP.add…

Django rest_framework 信号机制生成并使用token

1、在setting.py 中增加设置 DEFAULT_AUTHENTICATION_CLASSES:[rest_framework.authentication.BasicAuthentication,#基本的用户名密码验证rest_framework.authentication.SessionAuthentication,rest_framework.authentication.TokenAuthentication,# token 认证], INSTALLE…

SQL Server To Paimon Demo by Flink standalone cluster mode

需求&#xff1a;使用 Flink CDC 测试 SQL Server 连接 Paimon 操作&#xff1a;启动 Flink standalone cluster 后&#xff0c;接着启动 Flink SQL Client&#xff0c;则通过 Flink SQL Client 提交 insert & select job 到该 8081 cluster Flink SQL Client 执行案例 -…

MySQL 从入门到精通(四):备份与恢复实战——从逻辑到物理,增量备份全解析

数据是企业的核心资产&#xff0c;而数据库作为数据存储的 “心脏”&#xff0c;其备份与恢复策略直接关系到业务的连续性。本文将结合 MySQL 的日志体系与备份工具&#xff0c;深入讲解逻辑备份、物理备份、增量备份的实战操作&#xff0c;帮助你构建可靠的数据库保护方案。 目…

鸿蒙编译boost整合linux跨平台应用

openharmony deveco 4.1支持armeabi-v7a deveco 5.0后不支持arm32位系统 boost编译 使用deveco的写cmake集成boost boost使用1.88的最新版本&#xff0c;带cmake工具链 https://github.com/boostorg/boost.git boost的源码都在sub_module中 deveco 4.1的版本sdk最高到9&am…

机器视觉的平板电脑屏幕组件覆膜应用

在现代智能制造业中&#xff0c;平板电脑屏幕组件覆膜工序是确保产品外观和功能完整性的重要环节。随着技术的进步&#xff0c;传统的覆膜方式已经无法满足高速度、高精度的生产需求。而MasterAlign视觉系统的出现&#xff0c;将传统覆膜工艺转变为智能化、自动化的生产流程。在…

android-ndk开发(10): use of undeclared identifier ‘pthread_getname_np‘

1. 报错描述 使用 pthread 获取线程名字&#xff0c; 用到 pthread_getname_np 函数。 交叉编译到 Android NDK 时链接报错 test_pthread.cpp:19:5: error: use of undeclared identifier pthread_getname_np19 | pthread_getname_np(thread_id, thread_name, sizeof(thr…

【前端基础】6、CSS的文本属性(text相关)

目录内容 text-decoration&#xff1a;设置文本装饰线text-transform&#xff1a;文本中文字的大小写转换text-indent&#xff1a;首行缩进text-align&#xff1a;设置文本对齐方式 一、text-decoration&#xff1a;设置文本装饰线 常见值&#xff1a; None&#xff1a;没有…

【Ansible】模块详解

一、ansible概述 1.1 ansible介绍 Ansible 是一个基于 Python 开发的配置管理和应用部署工具&#xff0c;近年来在自动化管理领域表现突出。它集成了许多传统运维工具的优点&#xff0c;几乎可以实现 Pubbet 和 Saltstack 所具备的功能。 1.2 ansible能做什么 批量处理。An…

Git实战经验分享:深入掌握git commit --amend的进阶技巧

一、工具简介 git commit --amend是Git版本控制系统的核心补救命令&#xff0c;主要用于修正最近一次提交的元数据。该命令不会产生新的提交记录&#xff0c;而是通过覆盖原提交实现版本历史的整洁性&#xff0c;特别适合在本地仓库进行提交优化。 二、核心应用场景 提交信息…

软考 系统架构设计师系列知识点之杂项集萃(56)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;55&#xff09; 第91题 商业智能关注如何从业务数据中提取有用的信息&#xff0c;然后采用这些信息指导企业的业务开展。商业智能系统主要包括数据预处理、建立&#xff08;&#xff09;、数据分…

Spark任务调度流程详解

1. 核心调度组件 DAGScheduler&#xff1a;负责将Job拆分为Stage&#xff0c;处理Stage间的依赖关系。 TaskScheduler&#xff1a;将Task分配到Executor&#xff0c;监控任务执行。 SchedulerBackend&#xff1a;与集群管理器&#xff08;如YARN、K8s&#xff09;通信&#x…