文章目录
- 前言
- 一、 效果展示
- 二、相关模块
- 1. Statistic统计模块
- 功能分析
- 代码+详细注释
- 使用方式
- 2. Search搜索模块
- 功能分析
- 代码+详细注释
- 使用方式
- 3. banner模块
- 功能分析
- 代码+详细注释
- 使用方式
- 总结
前言
前面我们已经封装了这个项目基本要用到的全局组件了,现在就开始进入页面构建以及接口对接阶段了。首先,我们先来构建首页响应式布局,接下来会讲到真实接口对接,搭配react-query实现轮询,并使用memo,useMemo,usePrevious等钩子来优化页面,避免不必要的重新渲染。
一、 效果展示
二、相关模块
1. Statistic统计模块
功能分析
该模块主要为了展示基本数据统计,以及Echart图表统计
代码+详细注释
(1) 图表统计模块抽离
// @/pages/Home/StaticBlock/HashRateEchart/index.tsx
import { memo } from "react";
import BigNumber from "bignumber.js";
import "echarts/lib/chart/line";
import "echarts/lib/component/title";
import echarts from "echarts/lib/echarts";
import { useTranslation } from "react-i18next";
import { HomeChartBlock } from "./styled";
import { ReactChartBlock } from "@/components/Echarts/common";
// 使用useOption函数生成Echarts配置对象
const useOption = () => {
const { t } = useTranslation();
return (data: any, useMiniStyle: boolean): echarts.EChartOption => {
return {
color: ["#ffffff"], // 颜色设置
title: {
text: "图表y轴时间", // 标题
textAlign: "left", // 标题对齐方式
textStyle: {
color: "#ffffff", // 字体颜色
fontSize: 14, // 字体大小
fontWeight: "lighter", // 字体粗细
fontFamily: "Lato", // 字体类型
},
},
grid: {
left: useMiniStyle ? "1%" : "2%", // 图表距离容器左边的距离
right: "3%", // 图表距离容器右边的距离
top: useMiniStyle ? "20%" : "15%", // 图表距离容器顶部的距离
bottom: "2%", // 图表距离容器底部的距离
containLabel: true, // 是否包含坐标轴的刻度标签
},
xAxis: [
{
axisLine: {
lineStyle: {
color: "#ffffff", // x轴颜色
width: 1, // x轴宽度
},
},
data: data.map((item: any) => item.xTime), // x轴数据
axisLabel: {
formatter: (value: string) => value, // x轴坐标标签格式化
},
boundaryGap: false, // 是否留空
},
],
yAxis: [
{
position: "left",
type: "value",
scale: true,
axisLine: {
lineStyle: {
color: "#ffffff", // y轴颜色
width: 1, // y轴宽度
},
},
splitLine: {
lineStyle: {
color: "#ffffff", // y轴分割线颜色
width: 0.5, // y轴分割线宽度
opacity: 0.2, // y轴分割线透明度
},
},
axisLabel: {
formatter: (value: string) => new BigNumber(value), // y轴坐标标签格式化
},
boundaryGap: ["5%", "2%"], // y轴两侧留空
},
{
position: "right",
type: "value",
axisLine: {
lineStyle: {
color: "#ffffff", // y轴颜色
width: 1, // y轴宽度
},
},
},
],
series: [
{
name: t("block.hash_rate"), // 系列名称
type: "line", // 系列类型
yAxisIndex: 0, // y轴索引
lineStyle: {
color: "#ffffff", // 系列颜色
width: 1, // 系列线条宽度
},
symbol: "none", // 系列标记的图形类型
data: data.map((item: any) =>
new BigNumber(item.yValue).toNumber()
), // 系列数据
},
],
};
};
};
// HomeChartBlock组件
export default memo(() => {
// 后期改为真实接口请求
const echartData = [
{ xTime: "2020-01-01", yValue: "1500" },
{ xTime: "2020-01-02", yValue: "5220" },
{ xTime: "2020-01-03", yValue: "4000" },
{ xTime: "2020-01-04", yValue: "3500" },
{ xTime: "2020-01-05", yValue: "7800" },
];
// 解析配置对象
const parseOption = useOption();
return (
<HomeChartBlock to="/block-list">
{/* 使用ReactChartBlock组件展示Echarts图表 */}
<ReactChartBlock
option={parseOption(echartData, true)}
notMerge
lazyUpdate
style={{
height: "180px",
}}
></ReactChartBlock>
</HomeChartBlock>
);
});
--------------------------------------------------------------------------------------------------------------
// @/pages/Home/StaticBlock/HashRateEchart/styled.tsx
import styled from "styled-components";
import Link from "@/components/Link";
export const HomeChartBlock = styled(Link)`
canvas {
cursor: pointer;
}
`;
export const ChartLoadingBlock = styled.div`
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.no-data {
font-size: 18px;
}
`;
(2)引入统计图表
// @/pages/Home/StatisticBlock/index.tsx
import { FC } from "react";
import classNames from "classnames";
import { useTranslation } from "react-i18next";
import HashRateEchart from "./HashRateEchart/index";
import { HomeStatisticBlock, HomeStatisticItem } from "./styled";
import { useStatistics } from './hook'
const StatisticBlock: FC = () => {
// 区块链统计数据类型声明
interface EchartsAndData {
name: string;
value: string;
}
// 使用区块链统计数据
const useEchartsAndDataList = (): EchartsAndData[] => {
const { t } = useTranslation();
const statistics = useStatistics()
return [
{
name: t("home.echartsAndData.name1"),
value: t(statistics.value1),
},
{
name: t("home.echartsAndData.name2"),
value: t(statistics.value2),
},
{
name: t("home.echartsAndData.name3"),
value: t(statistics.value3),
},
{
name: t("home.echartsAndData.name4"),
value: t(statistics.value4),
},
];
};
// 使用区块链统计数据
const echartsAndDataList = useEchartsAndDataList();
// 单个统计渲染组件
const StatisticItem = ({ data }: { data: EchartsAndData }) => (
<HomeStatisticItem>
<div className={classNames("statistic-item-left-title")}>{data.name}</div>
<div className={classNames("statistic-item-left-value")}>{data.value}</div>
</HomeStatisticItem>
);
return (
<>
<HomeStatisticBlock>
<div className={classNames("statistic-item")}>
<div className={classNames("statistic-item-left")}>
<StatisticItem data={echartsAndDataList[0]}></StatisticItem>
<StatisticItem data={echartsAndDataList[1]}></StatisticItem>
</div>
<div className={classNames("statistic-item-right")}>
{/* hash图表模拟 */}
<HashRateEchart />
</div>
</div>
<div className={classNames("statistic-item")}>
<div className={classNames("statistic-item-left")}>
<StatisticItem data={echartsAndDataList[2]}></StatisticItem>
<StatisticItem data={echartsAndDataList[3]}></StatisticItem>
</div>
<div className={classNames("statistic-item-right")}>
{/* hash图表模拟 */}
<HashRateEchart />
</div>
</div>
</HomeStatisticBlock>
</>
);
};
export default StatisticBlock;
--------------------------------------------------------------------------------------------------------------
// @/pages/Home/StatisticBlock/styled.tsx
import styled from "styled-components";
import variables from "@/styles/variables.module.scss";
export const HomeStatisticBlock = styled.div`
width: 100%;
height: 207px;
display: flex;
margin-bottom: 20px;
.statistic-item {
display: flex;
align-items: center;
justify-content: space-between;
flex: 1;
background: #232323;
@media (max-width: ${variables.extraLargeBreakPoint}) {
flex-direction: column;
}
@media (max-width: ${variables.mobileBreakPoint}) {
}
.statistic-item-left {
flex: 1;
width: 100%;
height: 100%;
}
.statistic-item-right {
flex: 2;
width: 100%;
height: 100%;
padding: 10px;
// background: linear-gradient(304deg, #6e85e0 2%, #577cdb 48%, #486ecc 99%);
@media (max-width: ${variables.extraLargeBreakPoint}) {
}
@media (max-width: ${variables.mobileBreakPoint}) {
}
}
&:last-child {
background: #484e4e;
}
}
@media (max-width: ${variables.extraLargeBreakPoint}) {
height: 310px;
}
@media (max-width: ${variables.mobileBreakPoint}) {
flex-direction: column;
height: auto;
}
`;
export const HomeStatisticItem = styled.div`
padding: 30px;
display: flex;
justify-content: space-between;
flex-direction: column;
color: #fff;
.statistic-item-left-title {
font-size: 14px;
margin-bottom: 5px;
}
.statistic-item-left-value {
font-size: 18px;
font-weight: bold;
}
@media (max-width: ${variables.extraLargeBreakPoint}) {
padding: 15px 30px;
flex-direction: row;
margin-bottom: 0;
.statistic-item-left-value {
font-size: 16px;
}
}
@media (max-width: ${variables.mobileBreakPoint}) {
padding: 10px 20px;
}
`;
使用方式
// 引入
import StatisticBlock from "./SearchBlock";
// 使用
<StatisticBlock />
2. Search搜索模块
功能分析
(1)引入全局封装的搜索组件,抽离成一个灵巧组件
(2)使用国际化语言
代码+详细注释
// @/pages/Home/SearchBlock/index.tsx
import { FC, memo } from "react";
import { useTranslation } from "react-i18next";
import classNames from "classnames";
import styles from "./index.module.scss";
import Search from "@/components/Search";
// SearchBlock 组件
const SearchBlock: FC = memo(() => {
// 获取 i18n 的翻译函数
const [t] = useTranslation();
return (
<div className={classNames(styles.searchBlock)}>
{/* 标题 */}
<span className={classNames(styles.title)}>{t("common.Explorer")}</span>
{/* 内容 */}
<div className={classNames(styles.content)}>
{/* 搜索组件 */}
<Search hasButton />
</div>
</div>
);
});
// 导出 SearchBlock 组件
export default SearchBlock;
--------------------------------------------------------------------------------------------------------------
// @/pages/Home/SearchBlock/index.module.scss
@import "@/styles/variables.module";
.searchBlock {
display: flex;
align-items: center;
margin: 20px 0;
.title {
display: flex;
align-items: center;
font-weight: 800;
font-size: 20px;
@media (max-width: $extraLargeBreakPoint) {
margin-bottom: 20px;
}
@media (max-width: $mobileBreakPoint) {
font-size: 16px;
margin-bottom: 14px;
}
}
.content {
flex: 1;
margin-left: 16px;
@media (max-width: $extraLargeBreakPoint) {
width: 100%;
margin-left: 0;
}
}
@media (max-width: $extraLargeBreakPoint) {
display: block;
}
}
使用方式
// 引入
import SearchBlock from "./SearchBlock";
// 使用
<SearchBlock />
3. banner模块
功能分析
banner展示图,此处PC端和移动端采用不同的图片
代码+详细注释
// @/pages/Home/Banner/index.tsx
import classNames from "classnames";
import styles from "./index.module.scss";
export default () => <div className={classNames(styles.banner)} />;
--------------------------------------------------------------------------------------------------------------
// @/pages/Home/Banner/index.module.scss
@import "@/styles/variables.module";
$backgroundColor: #232323;
.banner {
width: 100%;
height: 200px;
background: url("./assets/banner.svg") no-repeat center center / auto 100%;
background-color: $backgroundColor;
position: relative;
@media (max-width: $mobileBreakPoint) {
background-image: url("./assets/banner_phone.svg");
}
}
使用方式
// 引入
import Banner from "./Banner";
// 使用
<Banner />
总结
下一篇讲【首页响应式构建之区块、交易列表布局】。关注本栏目,将实时更新。