前言
自己因地点地四月疫情原因,导致无法在京的实习由于延期无法抵达而遗憾收场。在学校度过了两个月,终究在暑假在家邻近找了一个公司来前端实习,距今现已有将近四个月了,在这里作业氛围很好,leader也待人和善,但是在这里呆了四个月,我终究决定完毕实习,展望未来,迎接春招。
公司主要技术栈
- 结构运用的是react16.8以后的函数式组件开发。
- 运用了umi结构,进行了一些权限设置、路由设置、和接口恳求封装。
- 组件库运用的是antd,4x的版本。
离开原因
- 公司事务较为重复,不利于未来开展。 我地点的公司,事务一般都是面向后台办理系统 + 主页门户的模式,大多数都是表单的格局,来进行后台数据的办理,没有自己的产品。
- 同学喜迎offer,心中略有不甘,我有一些同学线上加线下实习后,在秋招面试中获得了喜人的成果,而我因实习时刻不太长,还在学习一些事务上的常识,导致没有准备错失秋招。
- 公司排期紧,首要导致加班严重,我没有时刻自学其他方面常识,其次其他同事也由于排期很忙,导致有一些问题会一知半解或许随意找曾经的事务代码来学习,往往在处理完问题后仍然一知半解,由于排期紧而又没有时刻自己弄懂,不利于未来开展。
收获总结
- 熟悉了事务,懂得了前后端联调,和根本的接口恳求等。
- 熟悉了antd方面关于表单后台的一些组件的运用。
- 学会了自学检查特点及其文档,
学习总结:
封装组件
在我本来的文章中要恋爱好歹开个小鹏吧中,便是运用react-router@6 版本来装备路由,用自己的代码没用组件来完成一个手机作用的分页切换路由的作用。而在实习期间,由于运用umi4,Umi4 运用react-router@6作为路由组件,并运用antd组件完成相似的作用。
装备前台门户路由
{
path: '/portal',
name: '门户',
layout: false,
routes: [
{
path: '/portal/home',
name: 'test1111',
icon: '',
component: './LzjcPages/Portal/Home',
// access: 'canNormalUser',
},
{
path: '/portal/organization',
name: 'test2222',
icon: '',
component: './LzjcPages/Portal/Organization',
// access: 'canNormalUser',
},
{
path: '/portal/learnCenter',
name: 'test33333',
icon: '',
component: './LzjcPages/Portal/LearnCenter',
// access: 'canNormalUser',
},
{
path: '/portal/activeCenter',
name: 'test44444',
icon: '',
component: './LzjcPages/Portal/ActiveCenter',
// access: 'canNormalUser',
},
{
path: '/portal',
redirect: './LzjcPages/Portal/Home',
},
],
},
装备根本上同react-router@6,而且由于增加了权限操控access能够依据用户身份,来操控用户是否能够进入改页面。而且终究一个路由会必定匹配,由于与父路由一样,当其他子路由都没有匹配到的时分,直接跳转到主页。
封装共用组件HomeLayout
HomeLayout 是门户路由通用的组件,用来进行门户模块的切换时分的导航栏,在这里能够用来写一些通用的办法,例如登出、查询个人信息等,
const PortalLayout = (props) => {
const { tabKey, children } = props;
const [curTab, setCurTab] = useState('home');
const [tabMenus, setTabMenus] = useState([ //
{ title: 'test1', key: 'home' },
{ title: 'test2', key: 'organization' },
{ title: 'test3', key: 'learnCenter' },
{ title: 'test4', key: 'activeCenter' },
{ title: 'test5', key: 'checkCenter' },
]);
useEffect(() => {
setCurTab(tabKey);
}, [tabKey]);
const switchTab = (key) => {
if (key != 'courseDetailCenter' || checkLogin()) {
setCurTab(key);
history.push(`/portal/${key}`);
}
};
const loginOut = async (currentUser) => {
await outLogin();
Cookies.remove('accessToken'); //删除存储
const { origin, href } = window.location;
let url = `${origin}${ORIGIN_BASE}/#${loginPath}?redirect=${href}&appCode=${APP_CODE}`;
redirectLoginPath(url);
};
return (
<div className={styles.proContainer}>
<img className={styles.topImg} src={appImages.bgbanner} alt="" />
<div className={styles.topToolBar}>
<span className={styles.welcomeLine}>
欢迎来到党建作业{' '}
{formatTime({ type: 'yyyy-mm-dd', isweek: true })}
</span>
<span className={styles.enterBlock}>
<span onClick={() => { history.push('/index') }}>后台进口</span>
{/* <span>{currentUser && currentUser.user.userName}</span> */}
<span onClick={() => { loginOut() }}> 退出</span>
</span>
</div>
<div className={styles.tabMenus}>
<ul className={styles.nvaMenu}>
{tabMenus.map((d) => (
<li
className={`${styles.tabMenu} ${curTab == d.key && styles.tabMenuSelected}`}
key={d.key}
onClick={() => {
switchTab(d.key);
}}
>
<span>{d.title}</span>
</li>
))}
</ul>
</div>
<div className={styles.header}>
{children} // 渲染子组件
</div>
</div>
);
}
在上面代码中,封装了通用组件,来作为门户路由的通用组件,一切门户组件如主页都要引进改组件,作为导航栏,而且运用postion:stick 来使得导航栏一直呈现在门户页面上。
<PortalLayout tabKey="home">
<div style={{display:"flex",justifyContent:"center",}}>
<div style={{fontWeight:"bold",fontSize:"40px"}}>test111111</div>
</div>
</PortalLayout>
StepsForm – 分步表单
实习方针作用如下:这里事务是一个复杂的流程表单,事务方针是在每个小流程中的下一步的时分,都要恳求post 接口,保存当时小流程,而且当保存下一步后,不能退回进行修正了。当进入下一流程需求进行判别:假如以行进提交过大流程,只需求进入下一流程就能够。假如曾经没有提交过大流程,则需求恳求接口将整个大流程进行post接口提交。
界说主要变量:
current:表明当时处在哪个大流程
current1,current2,current3,current4,current5 分布操控处在大流程下的组件中地点小流程情况。
stepsKey 用来表明当时人物最高现已走到哪个大流程,由于咱们能够回来上一流程,此刻咱们并不知道咱们的大流程表单是否需求提交,若stepKey==current 当时流程,则表明当时走的流程是咱们还未提交的,需求调用接口发送数据,假如stepKey > current,表明曾经现已走过了,此刻不需求调用接口,直接将current = current +1 ,经过current + 用户id + current1/2/3/4/5 来恳求接口,拿到对应的数据。
代码完成:
<StepsForm
current={current}
formMapRef={formRef}
submitter={{
render: (props) => {
if (props.step > 0 && props.step <= 3) {
return [
<Button key="pre" onClick={() => {
setCurrent(current - 1)
}}>
上一流程
</Button>,
// {(current==1&¤t1==2)}
((current == 0 && current1 >= 2) || (current == 1 && current2 >= 4) || (current == 2 && current3 >= 5) || (current == 3 && current4 >= 7) || current == 4 && (current5 >= 7)) && <Button type="primary" key="goToTree" onClick={() => {
formStep.validateFields().then((errors, values) => {
props.onSubmit?.()
}).catch((error) => { });
}}>
下一流程
</Button>,
];
}
if (props.step <= 3) {
return [
((current == 0 && current1 == 2) || (current == 1 && current2 == 4) || (current == 2 && current3 == 5) || (current == 3 && current4 >= 7) || current == 4 && (current5 >= 7)) && <Button type="primary" key="goToTree" onClick={() => {
formStep.validateFields().then((errors, values) => {
props.onSubmit?.()
}).catch((error) => { });
}}>
下一流程
</Button>,
]
}
if (props.step == 4) {
return [
<Button key="pre" onClick={() => {
setCurrent(current - 1)
}}>
上一流程
</Button>,
(current == 4 && (current5 >= 7)) && <Button type="primary" key="goToTree" onClick={() => {
formStep.validateFields().then((errors, values) => {
props.onSubmit?.()
}).catch((error) => { });
}}>
完成
</Button>,
];
}
},
}}
>
<StepsForm.StepForm name="step1" title="大流程1" className={styles.stepsleft} onFinish={(value) => {
if (current1 == 2) {
if (stepsKey == 0) {
handOneFinish()
} else {
setCurrent(current + 1)
setStepsKey(current + 1)
}
}
}}>
{current == 0 &&
<PartyApply
dict={dict}
formRef={formStep}
activeKeyForm={activeKeyForm}
stepsKey={current}
initialValues={initialValues}
current={current1}
setCurrent={setCurrent1}
file={file}
setFile={setFile}
fileList={fileList}
setFileList={setFileList}
conversation={conversation}
setConversation={setConversation}
conversationList={conversationList}
setConversationList={setConversationList}
id={id}
setId={setId}
/>
}
</StepsForm.StepForm>
<StepsForm.StepForm name="step2" title='大流程2' className={styles.stepsleft} onFinish={(value) => {
if (current2 == 4) {
if (stepsKey == 1) {
handTwoFinish()
} else {
setCurrent(current + 1)
setStepsKey(current + 1)
}
}
}}>
{current == 1 &&
<PartyMember
dict={dict}
formRef={formStep}
activeKeyForm={activeKeyForm}
stepsKey={current}
initialValues={initialValues}
current={current2}
setCurrent={setCurrent2}
file={file}
setFile={setFile}
fileList={fileList}
setFileList={setFileList}
conversation={conversation}
setConversation={setConversation}
conversationList={conversationList}
setConversationList={setConversationList}
keep={keep}
keepList={keepList}
setKeep={setKeep}
setKeepList={setKeepList}
register={register}
registerList={registerList}
setRegister={setRegister}
setRegisterList={setRegisterList}
id={id}
setId={setId}
/>
}
</StepsForm.StepForm>
<StepsForm.StepForm name="step3" title='大流程3' className={styles.stepsleft} onFinish={(value) => {
if (current3 == 5) {
if (stepsKey == 2) {
handThreeFinish()
} else {
setCurrent(current + 1)
setStepsKey(current + 1)
}
}
}}>
{
current == 2 &&
<GrowTarget
formRef={formStep}
activeKeyForm={activeKeyForm}
dict={dict}
initialValues={initialValues}
current={current3}
setCurrent={setCurrent3}
stepsKey={current}
file={file}
setFile={setFile}
fileList={fileList}
setFileList={setFileList}
conversation={conversation}
setConversation={setConversation}
conversationList={conversationList}
setConversationList={setConversationList}
keep={keep}
keepList={keepList}
setKeep={setKeep}
setKeepList={setKeepList}
register={register}
registerList={registerList}
setRegister={setRegister}
setRegisterList={setRegisterList}
id={id}
setId={setId}
/>
}
</StepsForm.StepForm>
<StepsForm.StepForm name="step4" title='大流程4' className={styles.stepsleft} onFinish={(value) => {
if (current4 == 7) {
if (stepsKey == 3) {
handFourFinish()
} else {
setCurrent(current + 1)
setStepsKey(current + 1)
}
}
}}>
{
current == 3 &&
<PartyReceive
formRef={formStep}
activeKeyForm={activeKeyForm}
dict={dict}
initialValues={initialValues}
current={current4}
setCurrent={setCurrent4}
stepsKey={current}
file={file}
setFile={setFile}
fileList={fileList}
setFileList={setFileList}
conversation={conversation}
setConversation={setConversation}
conversationList={conversationList}
setConversationList={setConversationList}
keep={keep}
keepList={keepList}
setKeep={setKeep}
setKeepList={setKeepList}
register={register}
registerList={registerList}
setRegister={setRegister}
setRegisterList={setRegisterList}
wishBook={wishBook}
setWishBook={setWishBook}
wishBookList={wishBookList}
setWishBooksList={setWishBooksList}
opinion={opinion}
setOpinion={setOpinion}
opinionList={opinionList}
setOpinionList={setOpinionList}
id={id}
setId={setId}
/>
}
</StepsForm.StepForm>
<StepsForm.StepForm name="step5" title='大流程5' className={styles.stepsleft} onFinish={(value) => {
if (current5 == 7) {
if (stepsKey == 4) {
handFiveFinish()
} else {
setVisible(false)
actionRef.current.reload();
}
}
}}>
{
current == 4 &&
<PartyWorker
formRef={formStep}
activeKeyForm={activeKeyForm}
dict={dict}
initialValues={initialValues}
current={current5}
setCurrent={setCurrent5}
stepsKey={current}
file={file}
setFile={setFile}
fileList={fileList}
setFileList={setFileList}
keep={keep}
keepList={keepList}
setKeep={setKeep}
setKeepList={setKeepList}
register={register}
registerList={registerList}
setRegister={setRegister}
setRegisterList={setRegisterList}
wishBook={wishBook}
setWishBook={setWishBook}
wishBookList={wishBookList}
setWishBooksList={setWishBooksList}
opinion={opinion}
setOpinion={setOpinion}
opinionList={opinionList}
setOpinionList={setOpinionList}
id={id}
setId={setId}
/>
}
</StepsForm.StepForm>
</StepsForm>
代码概况:
首要拆分结构疏忽功用, 咱们最根本的代码结构是如下:下面代码就能够将大流程的流程图显现出来,经过current={current}来主动判别当时在哪个流程。
<StepsForm
current={current}
submitter={...}
>
<StepsForm.StepForm name="step5" title='大流程1' onFinish={()=>{}}>
</StepsForm.StepForm>
<StepsForm.StepForm name="step5" title='大流程2' onFinish={()=>{}}>
</StepsForm.StepForm>
<StepsForm.StepForm name="step5" title='大流程3' onFinish={()=>{}}>
</StepsForm.StepForm>
<StepsForm.StepForm name="step5" title='大流程4' onFinish={()=>{}}>
</StepsForm.StepForm>
<StepsForm.StepForm name="step5" title='大流程5' onFinish={()=>{}}>
</StepsForm.StepForm>
</StepsForm>
==============================================
其次经过current 当时流程,来调用该流程对应的组件,该组件操控的是小流程的显现,经过传入currentn, 进行小流程操控
{ current == n && <SomeComponent/>}
// 下面写一个大流程1对应的组件
const StepsProces = (props) => {
const { formRef, stepsKey, dict, activeKeyForm, initialValues, current, setCurrent, file, setFile, fileList, setFileList, conversation, setConversation, conversationList, setConversationList, id, setId, ...propsI } = props;
const actionRef = useRef();
const [validator, setValidator] = useState([]);
const validatorData = validator ? validatorObj(validator) : {};
const accessToken = Cookies.get('accessToken') || ''; //token
//form回显
useEffect(() => {
.... // 表单数据回显
}, [])
const stepsForm1 = (<div>
<Form form={formRef} action={actionRef} layout="vertical" >
<Row gutter={24}>
<Col span={8}>
<Form.Item name="sqrdzz" label="吃饭" rules={formItemValidator(validatorData.sqrdzz)}>
<TreeSelect
treeData={handleData(
dict?.treeData,
{ nodeName: 'name', nodeId: 'id', children: 'children' },
'treeSelect',
)}
showCheckedStrategy={TreeSelect.SHOW_ALL}
placeholder="吃饭"
treeDefaultExpandAll
disabled={current == 2 || activeKeyForm > 1}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="sqrdsj" label="睡觉" rules={formItemValidator(validatorData.sqrdsj)}>
<DatePicker placeholder='睡觉' disabled={current == 2 || activeKeyForm > 1} />
</Form.Item>
</Col>
</Row>
</Form>
</div >)
const stepsForm2 = current > 1 && (<div>
<Form form={formRef} layout="vertical">
<Row gutter={24}>
<Col span={8}>
<Form.Item name="thr" label="多读书" rules={formItemValidator(validatorData.thr)}>
<Select placeholder="多读书" disabled={activeKeyForm > 1} showSearch filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}>
{dict.dyryxx &&
dict.dyryxx.map((item, index) => (
<Option value={item.id} key={index}>
{item.xm}
</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="thsj" label="多看报" rules={formItemValidator(validatorData.thsj)}>
<DatePicker placeholder='多看报' disabled={activeKeyForm > 1} />
</Form.Item>
</Col>
</Row>
</Form>
</div>)
// 恳求下一步的接口,对当时小流程进行保存
const handleNext = () => {
.......
}
return (
stepsKey == 0 &&
<div className={styles.stepsInfo}>
<Steps progressDot direction="vertical" current={current} initial={1} className={styles.stepsInfoHeader}>
<Step key={1} title={"小流程1111"} description={stepsForm1} />
<Step key={2} title={"小流程2222"} description={current > 1 && stepsForm2} />
</Steps>
<div className="steps-action">
{current < 2 && (
<Button type="primary" onClick={() => { handleNext() }}>
下一步
</Button>
)}
</div>
</div>
);
};
代码解说:
小流程需求用到antd的Steps的步骤条,也跟StepsForm 相似,有总Steps 和 分 Step, Step 的每一个description 都表明一个Form表单,表达的便是小流程,有完好的表单事情。然后当点击下一步,会将当时处在的流程进行上传接口,进入下一个小流程。
Echars 图
Echars 图在完成前我也接触过,在实习中也用到了,温习了一遍还是很不错的,其间主要难点都在装备项的一些繁琐装备中,在开发的过程中,经常都是去Examples 比如中,找到相似的在此基础上进行修正,常常会遗漏某些装备项的特点的意义,在总结中也给我了温习的机会,从头全面了解这些装备项。
例如:以玫瑰图为比如,列举其具体特点
// 玫瑰图设置
const Echars2 = (props) => {
const { id } = { ...props } // 经过透传获取id,该id用于恳求接口,获取某个id下的数据项
let option = { // 装备项
legend: { // 图例组件。 图例组件展现了不同系列的标记(symbol),色彩和姓名。能够经过点击图例操控哪些系列不显现
top: '15%', // 图例组件离容器上侧的间隔,可所以百分比,也可所以像素,也可所以单词如:middle,top,bottom
width: "20%", // 图例的宽度,当orient设置为vertical,便是每一行一个图例,设置horizontal只有一行排满才会换行
right: "5%", // 间隔右边的间隔 top,width,right,left 不给值默许都是auto
orient:"vertical", // 设置摆放为竖向摆放,默许设置为"horizontal",横向摆放
// type :'plain':一般图例。缺省便是一般图例。 / 'scroll':可翻滚翻页的图例。当图例数量较多时能够运用。
// tooltip 在legend 中也有tooltip,表明的是鼠标悬浮图例显现提示,也有formatter,回来标签显现作用
// formatter: function (name) {
// return echarts.format.truncateText(name, 40, '14px Microsoft Yahei', '…'); //name:数据, 40宽度, 14px.. 字体
// },
},
tooltip: {
trigger: 'item',
formatter: function (params) {
return `<span>${params.name}</span></br><span>${params.data.value}(${params.percent}%)</span>`
},
borderColor: "#f2f3f5",
},
toolbox: {
show: true,
},
series: [
{
name: '暂无',
type: 'pie',
radius: [20, 80],
center: ['35%', '50%'],
roseType: 'area',
// itemStyle: {
// borderRadius: 5
// },
label: {
show: false
},
data: []
}
]
};
const initChart = () => {
let element = document.getElementById('chart2');
let myChart = echarts.init(element);
myChart.clear()
myChart.setOption(option);
}
const ageSwitch = (num) => {
switch (parseInt(num)) {
case 1:
return '28岁以下'
case 2:
return '29-35岁'
case 3:
return '36-50岁'
case 4:
return '51-60岁'
case 5:
return '61岁以上'
}
}
useEffect(async () => {
const resp = await getNlfbTj({ unitId: id })
if (resp.code == 200) {
let list = []
resp.result.map(v => {
list.push({ value: v.sl, name: ageSwitch(v.zd) })
})
console.log(list)
option.series[0].data = list
initChart()
} else {
message.error(resp.message)
}
}, [id])
return (
<>
<div style={{ display: "flex", justifyContent: "flex-start", height: "15%" }}>
<div className={styles.echars1}>
<div className={styles.image}></div>
<div className={styles.text}>党员年龄分布</div>
</div>
</div>
{
<div id='chart2' style={{ width: '100%', margin: '14px auto 0px', height: '85%', marginTop: 6 }}>
</div>
// <div className={styles.brithEmpty}><img src={appImages.empty} /></div>
}
</>
)
}
export default Echars2;
各种装备项介绍
option:设置装备项
legend:
图例组件。 图例组件展现了不同系列的标记(symbol),色彩和姓名。能够经过点击图例操控哪些系列不显现
legend.top 图例组件离容器上侧的间隔,可所以百分比,也可所以像素,还可所以单词:middle,top,bottom
legend.bottom 图例组件离容器下侧的间隔,可所以百分比,也可所以像素
legend.left/ legend.right 图例组件离容器左/右侧的间隔,可所以百分比,也可所以像素 。
上面四个特点代表图例在容器中的位置,默许值是auto
legend.orient 图例组件的摆放方法,是水平摆放还是竖直摆放,默许值为’horizontal’水平摆放。’vertical’垂直摆放需手动设置
legend.width/ height
图例组件的宽高,可所以百分比,也可所以像素 。 当图例组件设置水平摆放,当width不行放下一个图例的Item的时分,就会在下一行从头进行水平摆放,同理垂直摆放相似,当高度不行下一个图例的Item的时分,改动width,移动继续垂直摆放。
legend.type
‘plain’ : 一般图例,当width和height 都不行的时分,会发生缺省,便是一般实例
‘scroll’: 当宽和高都不行,也便是图例数量较多时,可翻滚翻页的图例,需求具体装备
legend.tooltip 设置悬浮于某个图例的弹窗提示,跟option.tooltip相似,在后边具体介绍
legend.formatter 制定悬浮框的某些款式,提示框浮层内容格局器,支撑字符串模板和回调函数两种方法。
剩下的一些特点比较杂,不常用,多是Item图例的具体设置,就不多介绍了。
tooltip:
提示框组件的通用介绍:
提示框组件能够设置在多种当地:
- 能够设置在全局,即 tooltip
- 能够设置在坐标系中,即 grid.tooltip、polar.tooltip、single.tooltip
- 能够设置在系列中,即 series.tooltip
- 能够设置在系列的每个数据项中,即 series.data.tooltip
- 能够设置在图例中,即legend.tooltip
tooltip.trigger
触发类型,默许为 tooltip.trigger= ‘item’
‘item’ : 数据项图形触发,主要在散点图,饼图等无类目轴的图表中运用
‘axis’ : 坐标轴触发,主要在柱状图,折线图等会运用类目轴的图表中运用。
‘none’: 什么都不触发。
tooltip.formatter
提示框浮层内容格局器,支撑字符串模板和回调函数两种方法:
支撑字符串模板
模板变量有
{a}
,{b}
,{c}
,{d}
,{e}
,别离表明系列名,数据名,数据值等。 在tigger为'axis'
的时分,会有多个系列的数据,此刻能够经过{a0}
,{a1}
,{a2}
这种后边加索引的方法表明系列的索引。 不同图表类型下的{a}
,{b}
,{c}
,{d}
意义不一样。 其间变量{a}
,{b}
,{c}
,{d}
在不同图表类型下代表数据意义为:
- 折线(区域)图、柱状(条形)图、K线图 :
{a}
(系列称号),{b}
(类目值),{c}
(数值),{d}
(无)- 散点图(气泡)图 :
{a}
(系列称号),{b}
(数据称号),{c}
(数值数组),{d}
(无)- 地图 :
{a}
(系列称号),{b}
(区域称号),{c}
(兼并数值),{d}
(无)- 饼图、仪表盘、漏斗图:
{a}
(系列称号),{b}
(数据项称号),{c}
(数值),{d}
(百分比)回调函数
formatter: function (params) { return `<span>${params.name}</span></br><span style="color:#5470C6">${params.data.value} (${params.percent}%)</span>` },
其间 params.name 替代的是饼图的 {a}, params.data.value 代表 {c}, params.percent 代表{d},这些只需求打印出来就能够了。
回调函数比字符串模板相比,有一些优势,比如愈加灵敏,比如呈现了在一个弹窗中的字符串中,得呈现俩个不同色彩的,如上面的比如,这里运用模板字符串,运用一致的 tooltip.textStyle.color 的话会全部修正其色彩,因此最好的办法是经过回调函数,回来Html字符串,经过Html中的style 来设置特定某些文字的色彩。
tooltip.borderColor 提示框浮层的边框色彩。
tooltip.textStyle 提示框浮层的文本款式。具有包含color,fontStyle,fontWeight,fontFamily,fontSize 等等特点。