写在前面

实习碎碎念~
本来想记录ant Design组件库的学习,现在变成了实习记录小册子
内容全部来自Alan老师,我的前端师傅~(是的是的,我有师傅啦)
想到什么写什么

零零碎碎

  • 命名一定要语义清晰,不要缩写,长一些也没关系
  • type类型声明可以写在return上面,其他的方法、类型声明等等都写在return里面
  • 时间选择器组件要求的数据类型dayjs和后端给予/需要的数据类型string不一致,要进行数据转换
  • 给表单表格内容添加阴影卡片效果,不要用card组件,用div直接加shadow即可,card的官方文档是“承载文字、列表、图片、段落”,与目的不符
  • 还有很简单的内容不要用Row组件单独占一行,自己写div+flex放就好了,它自带的样式后续可能会冲突
  • 能不用组件库轻易完成的东西,不要用组件库,因为组件本身有自己的样式属性和方法,多层嵌套后期可能会出问题,样式混乱
  • []代表空数组,boolean判断不为false;’ ‘空字符串,boolean判断为false
  • 添加样式注意作用域,优先级,增加优先级不要!important
  • 要让自己的样式优先级盖过组件库组件的样式,用:global
  • return的最外层盒子不要用<></>,用div加类名,给样式增加优先级加这个类名
  • F12控制台写代码可以自定义视频倍速!
  • 行内样式:style={{fontSize: '16px'}}放到less文件记得变回.className{font-size: 16px;},注意小驼峰变短横线,单引号去掉
  • 元素显示与隐藏,如果是值变化用过渡看着会更高级
  • 多多使用F12找元素找属性S
  • 表单一定要设置key列(主键),很重要
  • 不要在标签上挂一堆样式,尽量都写道该模块对应的样式文件中,在标签上直接用类名获取样式,常用的封装
  • 不要随意用遍历,每一个遍历循环复杂度很高
  • 快捷键:
  • ctrl+F在文档中也能用!
  • ctrl+shift+f切换输入法繁体简体
  • ATL+TAB能够快速切屏!
  • 原来默认的缩放比是150%,调成100%后的字体界面原来这么小…看习惯感觉这样小小的好好看哦
  • alloeClear属性的功能是前端需求,不能随便改,要根据FS文档决定
    • 遇到一个Form 有三个选择器都是 allowClear 的,点右边清空后,某一项为空的情况下 search 会出现后端报错,我想关掉 allowClear,这样 Search 不会再携带空值调接口师傅说这样不可以,有没有 allowClear 功能是前端需求,不能随便删改,传值能不能为空要看 FS 文档,如果不能为空,那么要在表单校验里添加非空验证;如果 FS 文档说可以为空值,那就是后端接口的问题,要去找后端
      修改内容要 Jira,和 BA 确认,不确定不要随便改
  • 非bug情况,比如后端要改接口,修改内容要问清楚修改原因,之前为什么可以,是局部还是所有
  • 想写新的页面,但是页面UI好长好长,我想先把架子搭起来,Alan老师说拿到UI不能直接写,要先分析好,我寻思写逻辑不也得在有架子的基础上吗,Alan 老师说因为要先考虑好多个 Form 之间的数据关系,同组件通信比兄弟组件通信方便得多,所以要分析好,把数据间有联系的 From 写在一起,没有联系的拆到其他组件里

重要函数

  1. render函数,常用在在table中自定义列内容
  • render: (text, record, index):
    • text:当前列的数据(这里是 age 的值)。
    • record:当前行的完整数据(包含 name、age、address、fileList 等)。
    • index:当前行的索引(可选参数)
  1. if (fileList && fileList.length > 0)判断数组是否有内容
  • fileList:检查变量是否存在(不为 null 或 undefined)
  • fileList.length > 0:如果变量存在,检查数组是否有元素
  1. useEffect
  • []为空 -> 初次加载执行
  • []为具体属性 -> 改变时执行
  • []为整个store->在缓存的应用发生变化时执行
    • 如果store对象中的对象的应用变化->对象引用也会变化->触发
    • 如果是store对象中的对象的某个属性的属性值变化,那么引用没有变->不会触发
  1. forEach,map前者没有返回值用,后者有返回值用
  2. react父子组件通信
  3. git常用命令:
    git fetch -> git stash ->git merge origin/fileName/childName -> git stash pop ->git add . -> git commit -m ‘message’ -> git push
    切换分支 git swq fileName/childName
    新建分支 git checkout -b fileName/childName
    拉其他分支代码 git merge origin/fileName/childName

代码 fix

  1. <div className={classNames(style.content, 'flex', 'justify-between', isFold && style.fold)}>
    isFold && style.fold:
    isFold 是一个布尔值(或可以转换为布尔值的表达式),表示一个条件,只有当 isFold 为 true 时,才会执行后面的表达式 style.fold 应用这个样式类

  2. dashboardSearchParamsStore.filterCriteria[key] && (filterCriteria[filterColumnMapping[key]] = dashboardSearchParamsStore.filterCriteria[key])

  • 前者为true,给后者赋值
  1. sortOrder: sortedInfo?.order ? sortedInfo.order.slice(0, sortedInfo.order.indexOf('end')) : 'desc',
    打印一下sortedInfo:
    1
    2
    3
    4
    5
    {column: {…}, order: 'ascend', field: 'warningLabel', columnKey: 'warningLabel'}
    column: {title: 'warning', key: 'warningLabel', dataIndex: 'warningLabel', sorter: true, filters: Array(3), …}
    columnKey: "warningLabel"
    field: "warningLabel"
    order: "ascend"
  • slice 方法用于提取数组的一部分
  • 0 表示起始索引,sortedInfo.order.indexOf (‘end’) 表示结束索引。
  • indexOf ('end') 返回 ‘end’ 在 sortedInfo.order 中的索引位置。
  • sortedInfo.order 是 ‘descend’,则 indexOf (‘end’) 返回 ‘desc’
  • sortedInfo.order 是 ‘ascend’,则 indexOf (‘end’) 返回 ‘ascend’
1
2
3
4
5
6
7
8
9
10
11
filterOption={(input, option) => {
if (input.length >= 3) {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
return true
}}
//这个条件检查输入框中的内容长度是否至少为 3 个字符。如果输入内容长度小于 3,则直接返回 true,表示不进行过滤。
//return (option?.label ?? '').toLowerCase ().includes (input.toLowerCase ())
//option?.label ?? '':这是一个可选链操作符,用于安全地访问 option 对象的 label 属性。如果 option 为 null 或 undefined,则使用空字符串 '' 代替。
//.toLowerCase ():将 option.label 和 input 都转换为小写,以便进行不区分大小写的匹配。
//.includes (input.toLowerCase ()):检查 option.label 是否包含 input(已转换为小写)。如果包含,则返回 true,表示该选项应该显示在过滤后的列表中。
  1. _.cloneDeep 是 lodash 库中的一个方法,用于深拷贝对象。比如,它用于深拷贝 dashboardSearchParamsStore.searchCriteria,以避免直接修改原始对象
  • item.allowToShowColumnsByStatusAndRole.some((column) => column.roleName === roleName && column.taskStatus === taskStatus)
    some 方法用于检查数组中是否至少有一个元素满足给定的条件
    这行代码的作用是检查 item.allowToShowColumnsByStatusAndRole 数组中是否至少有一个元素满足 column.roleName === roleName && column.taskStatus === taskStatus 这个条件,有返回 true,否则返回 false
  1. <Form.Item shouldUpdate={(prevValues, curValues) => prevValues.updatedYear === curValues.updatedYear}>
    updatedYear 的值发生变化时,表单项会更新
    当 shouldUpdate 为 true 时,Form 的任意变化都会使该 Form.Item 重新渲染。这对于自定义渲染一些区域十分有帮助,要注意 Form.Item 里包裹的子组件必须由函数返回,否则 shouldUpdate 不会起作用当 shouldUpdate 为方法时,表单的每次数值更新都会调用该方法,提供原先的值与当前的值以供你比较是否需要更新
    updatedYear: dayjs().year().toString(): 获取当前日期和时间的年份转字符串onFinish 属性:提交表单且数据验证成功后回调事件onFieldsChange 属性:字段更新时触发回调事件

  2. const { benefitsInformation: benefitsInformationOptions } = useSelector((state: any) => state.dropdown)
    把名 dropdown 缓存中的 benefitsInformation 拿出来重命名为 benefitsInformationOptions

  3. A判断 && B判断 && C(), AB都为ture->走C

    1
    2
    3
    currentCardStatus === CardStatus.TODO
    && (currentRole.roleName !== Melon && currentRole.roleName !== Nie)
    && (<>前面两个都为true噢</>)
  4. 多个异步函数顺序执行(存疑)
    解决方法: 使用 async/await 保证顺序执行
    为了确保两个异步函数按照先后顺序执行,可以使用 async/await,将按钮点击事件的处理函数也定义为异步函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
const handleAddBtnClick = async () => {
console.log('Start process.');

// 先执行第一个异步函数,并等待它完成
await fun1();
console.log('First function completed.');

// 再执行第二个异步函数,并等待它完成
await fun2();
console.log('Second function completed.');

console.log('All functions completed.');
};
  1. return Promise.reject(new Error()) 这行代码很重要

  2. 统一配置后端报错弹窗信息,个别信息单独处理

  3. 用户自动登出问题:埋点监控用户键盘操作
    用户在使用系统填表时跳转到了首页,原因分析:
    首先检查前端的自动登出功能,设置的用户30分钟没有任何操作跳转到超时页面再由用户主动重新登录,和用户反馈现象不一致,同时此功能代码没问题测试也检查过,排除这个可能。
    前端只有响应拦截到401会跳转到login界面,怀疑是后端token过期,这个猜测测试测了看到token在继续操作后是有刷新的,不是token过期导致的。
    其次怀疑是用户操作导致的,可能用户习惯用某个键盘快捷键导致跳转回去,前前端考虑在页面加入埋点,来证实用户操作,判断是否触发了什么快捷键,来锁定具体的深层次原因

埋点监控用户键盘操作:用uselocation,在每一个navigate(‘首页’)的地方传一个不同值的NO,比如navigate(‘首页’,(state:{NO:1})),在首页接收
import { useLocation } from ‘react-router-dom’,
const dashboardNo = useLocation.state?.error

useEffect监听

再在case页面监听用户的键盘操作

  1. 长表单save&submit
    很长的表单,save不做完整的校验,只有在submit校验
    save不做必填校验,要做类型校验,因为如果一个input后端数据库用的是varchar不是nvarchar,用户填一个中文进去save,那么存到数据库的是会报错或者是乱码,用户下次再回来继续填表会出问题

  2. 字段间的联动关系
    某一个checklist选某个值才会显示某些input,某个checklist的选址值控制某个input的默认值是否允许手动输入

  3. table + 模糊查询
    table第一列用户输入一个code,后面的多个列:先根据用户最终的选择值自动填入(把之前的信息带过来),自动输入的内容允许用户编辑,这部分内容save后存到新表,不会改变通过code带出原数据的表

  4. table + 模糊查询
    table第一列用户输入一个code,后面的多个列:先根据用户最终的选择值自动填入(把之前的信息带过来),自动输入的内容允许用户编辑,这部分内容save后存到新表,不会改变通过code带出原数据的表。

刚实习的练习

1. 表单

  • 要求:表单包含report date to时间选择器;report date form 时间选择器;下拉选项选择器;search按钮;reset重置按钮
  • 详细:时间要月-日-年的格式展示,时间存储要用string类型,设置某一个为必填
  • 布局需求:每一块文字说明和选择器上下组合排列,这几块横向布局
  • 内容实现:用form组件搭架子,DatePicker组件完成两个时间选择器,用dayjs.format('MM-DD-YYYY')方法格式化时间,给form.item添加required属性设置必填
  • 整体布局实现:用Row和Col组件实现,设置gutter={[32,16]}表示左右间距16px,上下间距8px
    useState位置变化效果变化
  • 小块布局实现:默认的label和选择器是水平排列,想要垂直排列,在fromAPI文档中设置layout属性改变布局,vertical值为上下布局
  • 颜色实现:在项目utils里面封装了要求的颜色,有的是常量,不是常量的也有定义,引入文件使用颜色

2.表格

  • 要求:一个表单表格,第一列前是多选小方框,第一列为Form Name,第二列是Form Date(展示日期,点击后可以选择日期进行修改),第三列File(如果该行没有文件,展示上传按钮,可以上传文件;如果有文件,展示文件名和删除按钮,隐藏上传按钮),第四列Action(只有当这列前面的多选框被勾选时,删除按钮可以点,否则不能点),表格上边有添加新列的按钮
  • 整体实现:用带有多选框功能的Table组件

修改日期功能

  1. 首先要让这一列的内容可以修改,再考虑修改什么样子
    那么怎么知道可不可修改呢,定义一个boolean类型edit变量判断,给formDate列设为true,
    那么怎么能让展示的表格的该列变得可以修改呢通过表单组件,用form.item包括时间选择器组件
    如果edit变量为true=>该列是可修改的=>判断列类型是否为日期列=>
    是=>渲染事件选择器组件=>设置焦点在该单元格上
    不是=>正常渲染表格内容
  2. 再看要修改的时间选择器DatePicker组件
    开始DatePicker没有展示数据,因为这一列开始的数据类型是后端需要的string,但是事件选择器组件需要dayjs格式的数据,string类型数据无法显示
    所以要在点击这个单元格的时候,把数据string=>dayjs再放进DatePicker组件中展示
    当用户选择了新的日期,触发save方法,把修改后的日期dayjs=>string,再进行保存,保存后再渲染

上传文件功能

在这列用record()方法添加upload组件,upload组件的onChange接收参数info包含{file,fileList}参数(要用大括号包裹,整个为info)
if判断是否有文件名,有=>渲染文件名+删除按钮;没有=>渲染上传文件按钮
上传文件后触发uploadFile方法,接收render的第二个参数record,用它拿到当前行数据,给record.file赋值为当前文件名,再把赋值后的数据覆盖更新到原来的整个数据里,再用更新后的数据重新渲染s
有文件,点击删除按钮,**把当列的文件名赋值为空record.file = ‘’不可以=[]**,再把赋值后的数据覆盖更新到原来的整个数据里,再用更新后的数据重新渲染

选中列删除按钮功能

把所有选中的行的key放进一个数组里,在渲染第四列时,判断当前行key是否在数组里,如果在=>删除按钮生效,不在=>不生效
按钮的生效与否通过button组件的disabled属性设置

3.三个小卡片

三个小卡片,左上角图标、右上角数字、右下角比较小的文字

4.可折叠的搜索表单

需求:左侧是搜索表单,表单的右上角外部有小图标,点击后搜索表单折叠(消失),右侧的Table占用它原来的空间
我的思路:给搜索表单设置position:relative,折叠图标设置position:absolute;top:0;right:-30;,点击折叠图标=>isFossld变量取反,再根据isFold的状态=>表单的最大宽度变=>折叠图标的right变=>Table的cro组件的span变
问题:搜索表单没有实现隐藏
Alan老师思路:不再让折叠小图标附属在可能会小时的搜索表单上,给table设置position:relative,让小图标挂靠在会一直存在的table上
搜索表单的折叠(消失)用display:none(visiblity:hhidden属性元素占位但不可见;visibility:visible属性元素占位且可见)
Alan老师说,也可以修改搜索表单的位置,让他偏移到页面之外来实现消失的效果,如果是使用值,可以添加过度动画,看起来会更高级,直接用属性display:none这种无法过渡
transform: translate(0,0)显示,加渐变:transition: transform 0.5s,isfold=true=> transform:translate(-100%,0)向左移出视口,
其他:opacity属性(透明度渐变隐藏):opacity:0;为透明不可见,opacity:1;为不透明可见,但是仍占用空间

canvas–”输入文字”问题解决

用canvas写一个建议的画图工具,支持用户在图片上绘画矩形、椭圆、输入文字
点击按钮输入文字功能,一直没成功,终于破案了!!!
Alan老师说问题在创建了DOM元素但是没有添加到页面中,带着我开始找问题:
只留下最关键的创建元素和添加元素到页面两行代码,其他属性方法全部注释掉,再让注释掉的部分一个一个生效,找到了罪魁祸首!

问题出现在:在输入文字后的保存方法中,有进行内容检测,如果内容为空那么不保存,同时销毁DOM元素。逻辑看似没问题,但是代码写的有问题,刚创建的DOM元素还没开始写内容呢就进行了内容非空判断,然后就被销毁掉了,等于是:创建=>插入页面=>没内容=>马上销毁=>所以显示不出输入框
其他知识:文档里用的小写2d一定不许写2D,大小写要注意!让图片根据画布大小等比例缩放、事件监听、阻止冒泡、随机颜色

小tasks

select选择器互斥

两个选择器共用同一组数据,A选中的数据不会出现在B的选项中,B选中的也不会出现在A的选项中,用filter过滤另一方选中的数据

tabs

children里面可以放多个组件,实现切换不同标签渲染不同内容

react中的css

:global 覆盖组件中的样式
优先级不够的话,一点一点网上找父亲的类给它添加,如果多个类名并列,中间没有空格

高频css

复刻页面印象深css的有:
font-size: 16px;``font-weight: bold,box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);,padding-right: 10px;``margin: 3px;

用redux实现数据持久化

table表单选中的列,刷新后依然处于选中状态

layout组件(布局)

Layout:最外层容器,用于包裹其他布局组件
Layout.Header:头部容器
Layout.Footer:底部容器
Layout.Content:内容区域容器
Layout.Sider:侧边栏容器
Row/Col:栅格系统组件,用于精细化布局(属于 Layout 体系的补充)

1