为了账号安全,请及时绑定邮箱和手机立即绑定

【九月打卡】第二十天

标签:
React

课程:React18 系统精讲

章节:Redux-toolkit

讲师:阿莱克斯刘
课程内容

【详情页面搭建 1】页面框架、详情与日期选择

本节课,我们来使用最简单的mvc架构搭建产品的详情页面。先简单介绍一下产品详情页面的ui,整个页面分为三个大块,分别是网站的header顶部导航栏,中间部分的页面内容,以及最底部的网站footer。

顶部导航和底部导航可以复用已有的组件,所以我们的关注点是页面的内容。从内容结构的角度来说,我们可以把内容分为产品简介、产品特色、产品费用、预订须知、以及用户评价这5个模块。

对应这5个模块,除了用户评价以外,我们的后端都会返回相应的数据。所以我们可以先试用postman来检验一下数据。

请同学们打开“获得旅游路线”这条api, url我已经准备好了,url的最后一个部分是旅游路线的id。更改一下自己的icode,然后点击发送。如果请求成功,我们将会获得了响应数据,这些数据就是url中id所对应的旅游路线。数据非常详细,有id、有价格、有折扣、有创建时间,甚至还有使用html字符串所对应的产品特色feature、产品费用fees、以及预订须知notice这三个部分。从数据的角度来说,我们后端唯一欠缺的就是用户评价的数据,那么这部分我们将会使用假数据来代替。

回到网页,通过与分析数据,我们可以知道虽然页面被划分为5个模块,但实际上我们需要的却是3个组件,因为产品特色、产品费用、以及预订须知在直接渲染html字符串以后,这三个模块是可以复用组件的。

那么,接下来请打开项目,我们开始代码。产品详情的页面的初始化在之前的课程中已经完成了,我们接下来需要做的事就是拼凑页面组件。不过,在进入页面组件拼装之前,我们可以先通过调用api,取得页面将会使用到的数据,这样我们的开发就有有数据可以观察了。

如何在组件中调取api就不用多说了吧,我们之前讲过很多次。我们将会使用axios来进行api调用,所以引入axios


import  axios  from  "axios";

- 接下来,从react-router-dom引入useParams,使用useParams来取得url中的路由参数,

const { touristRouteId } = useParams<MatchParams>();

- 然后,使用useState  hook来配置三个state, loading, product, 以及error。Loading是boolean,初始化为true;product是any类型,初始化为null;error类型stirng或null,同样初始化为nullconst [loading, setLoading] = useState<boolean>(true);

const [product, setProduct] = useState<any>(null);

const [error, setError] = useState<string | null>(null);


再然后,从react中引入副作用hook,useEffect。我们将会在useEffect使用异步方式来进行api的调用。异步函数中我们先设置loading为ture,然后发送api请求,await axios.get(。如果成功获得数据,那么设置produc数据,并设置loading为false。如果请求失败,我们需要使用try catch来捕获错误信息,设置error为错误信息,并设置loading为false。


useEffect(() => {

const  fetchData = async () => {

setLoading(true);

try {

const { data } = await  axios.get(

`http://123.56.149.216:8080/api/touristRoutes/${touristRouteId}`

);

setProduct(data);

setLoading(false);

} catch (e) {

setError(e.messagae);

setLoading(false);

}

};

fetchData();

}, []);


既然数据是异步取得的,那么我们ui也需要做一些保护措施,来避免error。这段代码同学可以直接从home页面中复制粘贴。if (loading) 和if (error)这两个部分


import { Spin } from  "antd";

  

if (loading) {

return (

<Spin

size="large"

style={{

marginTop:  200,

marginBottom:  200,

marginLeft:  "auto",

marginRight:  "auto",

width:  "100%",

}}

/>

);

}

if (error) {

return  <div>网站出错:{error}</div>;

}


数据取得完成以后,我们就可以来搭建页面的基本骨架了。请同学们先把css样式赋值粘贴到DetailPage.module.css文件里去,然后在detailpage文件中引入样式,因为要复用header和footer,所以引入component文件夹


import  styles  from  "./DetailPage.module.css";

import { Header,Footer } from  "../../components";


先整理一下代码,然后我们把网站的复用组件header和footer先放进来。然后,先使用div元素定义一下页面的主要内容,组件样式classname使用page-content

接着补充页面的骨架,所有的骨架元素,我都会使用div来定义。

  • 第一个组件,产品简介与日期选择,div, className={styles["product-intro-container"]}

  • 第二个组件,带锚点功能的菜单组件,div,className={styles["product-detail-anchor"]}

  • 第三个,产品特色,因为等一下这个组件要与我们的锚点菜单连接,所以,给这个组件加上id,feature,className={styles["product-detail-container"]}

  • 第四个,产品费用,同样的道理,组件加上id,费用,fees。因为产品特色、产品费用,以及接下来的预定须知和商品评价,都会使用相同的组件,所以,style也是一样的,styles["product-detail-container"]

  • 第五个,预定须知,id, notes, className={styles["product-detail-container"]}

  • 最后一个,商品评价,id,comments,className={styles["product-detail-container"]}


export  const  DetailPage: React.FC<RouteComponentProps<MatchParams>> = () => {

return (

<>

<Header  />

<div  className={styles["page-content"]}>

{/* 产品简介 与 日期选择 */}

<div  className={styles["product-intro-container"]}></div>

{/* 锚点菜单 */}

<div  className={styles["product-detail-anchor"]}></div>

{/* 产品特色 */}

<div  id="feature"  className={styles["product-detail-container"]}></div>

{/* 费用 */}

<div  id="fees"  className={styles["product-detail-container"]}></div>

{/* 预订须知 */}

<div  id="notes"  className={styles["product-detail-container"]}></div>

{/* 商品评价*/}

<div  id="comments"  className={styles["product-detail-container"]}></div>

</div>

<Footer  />

</>

);

};


好了,页面的骨架出来了,我们可以运行一下,看看效果。ok,效果大概就是这样,可以看到现在我们有若干个这样的白色的方块,接下来,我们的组件就会被填充在这些方块中。

第一个组件是产品的详情与日期选择,实际上这是左右两个组件组成,左边的产品详情组件需要我们自己搭建,而右边我们可以直接使用ant design的日期选择控件。左右布局我们使用ant desing的row和col组件。一个row,里面套两个col,第一个产品的详情,第二个日期选择,大小稍微设置一下,13 : 11


import { Row, Col } from  "antd";

  

{/* 产品简介 与 日期选择 */}

<div  className={styles["product-intro-container"]}>

<Row>

<Col  span={13}></Col>

<Col  span={11}></Col>

</Row>

</div>


先做简单的,右边的日期选择,可以使用ant design。我们先来完成预览一下ant design的相关文档,https://ant.design/components/date-picker-cn/。

ant design大厂出品,控件的质量都相当高,随便选一个都很好看吧,这个看起来不错,就这个吧

sXNLVS.png

请同学直接复制代码就行了,RangePicker加个状态open,margin top 设置 20


import { DatePicker, Space } from  'antd';

const { RangePicker } = DatePicker;

  

<Col  span={11}>

<RangePicker

open

style={{

marginTop:  20,

}}

/>

</Col>


接下来,我们来完成左边的产品简介组件。既然是组件,那么让我们在component文件夹中创建这个组件的相关文件。文件名称, productIntro,然后就是三大金刚,index.ts,ProductIntro.tsx,ProductIntro.module.css。

先打开样式文件,css文件我提前准备好了,样式特别简单,没啥技术含量,我就直接复制粘贴了。

接着,打开ProductIntro.tsx,先引入样式文件。


import  styles  from  "./ProductIntro.module.css";


然后,来定义一下组件接口,对于产品细节来说,我们将会显示产品的标题,title;简介,shortDescription,字符串类型;价格,string或number;还有诸如优惠券coupons,积分points,折扣discount,评价rating,图片pictures,等等等等。


interface  PropsType {

title: string;

shortDescription: string;

price: string | number;

coupons: string;

points: string;

discount: string;

rating: string | number;

pictures: string[];

}


接下来,是函数组件ProductIntro, 类型React.FC。参数使用花括号展开接口定义的数据。JSX retun 一个div元素把整个组件包裹起来,style等于intro-container。


export  const  ProductIntro: React.FC<PropsType> = ({

title,

shortDescription,

price,

coupons,

discount,

rating,

pictures,

}) => {

return  <div  className={styles["intro-container"]}></div>;

};

  

接下来,我们来补充div元素的内容。

  

第一个内容,当然是产品的名称,也就是参数title,我们使用ant  design中的title组件。引入antd,并且引用Typography组件。使用Typography的子组件title, level  4。

第二行是产品的简洁,继续使用Typography组件,使用Typography子组件text。

<Typography.Title  level={4}>{title}</Typography.Title>

<Typography.Text>{shortDescription}</Typography.Text>

  

接下来,是产品的评价与价格细节表。用div包裹一下,内部有两个元素,第一个是价格,使用ant  design的text组件,marginleft左移 20个单位,内套一个span包裹一下文字,className等于styles["intro-detail-strong-text"],花括号引用参数price。加上修饰字符串,人民币符号 ¥ 和价格的单位 /人起。使用同样的道理完成评分评价元素,marginleft左移 50个单位

<div  className={styles["intro-detail-content"]}>

<Text  style={{ marginLeft:  20 }}>

¥<span  className={styles["intro-detail-strong-text"]}>{price}</span>{" "}

/人起

</Text>

<Text  style={{ marginLeft:  50 }}>

<span  className={styles["intro-detail-strong-text"]}>{rating}</span>{" "}</Text>

</div>

  

好了,再然后就是产品图片的轮博组件,继续使用ant  design,使用他的走马灯组件Carousel和Image组件。走马灯,设置为autoplay,一次总共同时显示3张图片,slidesToShow等于3。

然后,来一个for  loop来循环参数picture,每个图片都使用Image组件来熏染,高度设置为150个单位

<Carousel  autoplay  slidesToShow={3}>

{pictures.map((pic) => (

<Image  src={pic}  height={150}  />

))}

</Carousel>

  

接下来,是课程的难点,我们将会是使用ant  design  ui中的表单组件  table,来显示路线名称、价格、限时抢购折扣、优惠券、以及线路评价。

  

那么我们先看看文档。https://ant.design/components/table-cn/ 。 根据文档的案例, 我们可以看到一个最简单的表单组件有两个属性需要提前定义好,

- 第一个是columns,用来配置表格的行列设置;

- 第二个是dataSource,也就是数据。

  

好了,对表单组件有这个基础认识以后,咱们就可以开始了。首先我们来定义一下,colomus。对于ant  design表格的列配置,我们可以使用typesctip  来定义一下,引入ColumnsType。ColumnsType比较特殊,需要从antd/es/table中导出

import { ColumnsType } from  "antd/es/table";

  

其实,我们只有两个列,第一个是title,标题,title、dataIndex、key都一样就好了,字体左对齐,宽度120;第二个是discription,描述,字体左对齐,宽度无所谓。

  

const  columns: ColumnsType = [

{

title:  "title",

dataIndex:  "title",

key:  "title",

align:  "left",

width:  120,

},

{

title:  "discription",

dataIndex:  "discription",

key:  "discription",

align:  "left",

},

];


然后,我们来配置一下表格的行数据,实际上我们每行数据实际上只需要三个字段的数据,前两个字段与columns对应,我们需要标题title,和描述discription。除此以外,因为我们现在处理的是每行的数据,所以,还需要给每行都定义key,这个key最后会映射给react对象。


interface  RowType {

key: number;

title: string;

discription: string | number | JSX.Element;

}


ok,现在表格的行列数据的类型都配置完成了,我们接下来就要组织表格数据了。

常量,tableDataSource,因为我们处理的将会是具体的数据,所以类型应该是列表形态RowType。 那么具体的数据,我就不一一讲解了,同学们可以自己看代码,基本上就是大白话,理解起来应该是没有问题的。


const  tableDataSource: RowType[] = [

{

key:  0,

title:  "路线名称",

discription:  title,

},

{

key:  1,

title:  "价格",

discription: (

<>

¥{" "}

<Typography.Text  type="danger"  strong>

{price}

</Typography.Text>

</>

),

},

{

key:  2,

title:  "限时抢购折扣",

discription:  discount ? (

<>

¥ <Typography.Text  delete>{price}</Typography.Text>{" "}

<Typography.Text  type="danger"  strong>

¥ {discount}

</Typography.Text>

</>

) : (

"暂无折扣"

),

},

{

key:  2,

title:  "领取优惠",

discription:  coupons ? discount : "无优惠券可领",

},

{

key:  2,

title:  "线路评价",

discription: (

<>

<Rate  allowHalf  defaultValue={+rating}  />

<Typography.Text  style={{ marginLeft:  10 }}>

{rating}</Typography.Text>

</>

),

},

];


接下来,我们就可以在jsx中使用table组件了。先从ant design中导入Table组件,向组件传入columns和tableDataSource。


<Table  columns={columns}  dataSource={tableDataSource}  />


不过报错了,错误就是因为我们还需要把ColumnsType和RowType连接起来。


const  columns: ColumnsType<RowType> = [


更准确一点,我们还可以把RowType的类型定义给table使用,通过这样的定义,我们的table就形成了typescript的强类型定义了。虽然有点繁琐,但是在实操中过逞中,有强类型确实是可以避免很多潜在的错误。

接着,我们还需要给table再加几个属性。


<Table<RowType>

columns={columns}

dataSource={tableDataSource}

showHeader={false}

size="small"

bordered={false}

pagination={false}

/>


好了,组件完成,同学们记得在index中导出一下,我们就可以使用了,回到detail page。引入ProductIntro,然后直接在中使用这个组件。


<ProductIntro

title={product.title}

shortDescription={product.description}

price={product.originalPrice}

coupons={product.coupons}

points={product.points}

discount={product.price}

rating={product.rating}

pictures={product.touristRoutePictures.map((p) =>  p.url)}

/>


ok,我们来跑一下网站测试一下。效果不错,我们从后端api取得了数据,完成了页面的骨架、也顺便完成了页面的第一个组件。下节课,我们继续完成剩下的部分。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消