【react小项目】bmi-calculator

bmi-calculator

目录

  • bmi-calculator
    • 初始化项目
    • 01大致布局
      • 01代码
    • 02完善样式
      • 02代码
    • 03输入信息模块
      • 03代码
    • 04 使用图表
      • 04代码
    • 05详细记录信息渲染
      • 05代码
    • 06 让数据变成响应式的
      • 06-1输入框的数据处理
      • 06-2图表,和记录信息的区域数据处理
    • 07 删除功能,撤销功能
      • 删除功能完成
      • 撤销功能
    • 08 数据持久化、组件化、模块化
      • 08-1数据持久化
        • 存数据
        • 取数据
        • Undo 使用本地化数据,不使用useRef()缓存了
      • 08-2组件化、模块化
        • 08-2-1输入添加模块
        • 08-2-2图表模块
        • 08-2-3七天数据模块
    • 09 修一些bug

学习地址:https://gitee.com/cheng_yong_xu/bmi-calculator-my

源码地址:https://github.com/GermaVinsmoke/bmi-calculator

不错的学习项目,循序渐进, 很多注释

可以学到什么
函数组件
useState, useEffect,useRef
prop-types
materialize-css
react-chartjs-2(折线图)
数据本地存储
模块化,组件化

成品效果
在这里插入图片描述

初始化项目

第一次提交

在这里插入图片描述

App组件

在这里插入图片描述

启动

在这里插入图片描述

01大致布局

【分支01】

使用了Materialize CSS框架的网格系统(Grid System)来布局页面内容。

在这里插入图片描述

01代码

import React, { useState, useEffect } from 'react';
import 'materialize-css/dist/css/materialize.min.css';
import './App.css'
const App = () => {return (<div className='container'>{/* 标题 */}<div className='row center'><h1 className='white-text'>BMI Tracker</h1></div>{/* 输入框 */}<div className='row'><div className='col m12 s12'><div className='row'><div className='col m6 s12'><label htmlFor="weight">Weight (in kg)</label><inputtype="number"id="weight"name="weight"min="1"max="999"placeholder="50"/></div><div className='col m6 s12'><label htmlFor="height">Height (in cm)</label><inputtype="number"id="height"name="height"min="1"max="999"placeholder="175"/></div></div><div className='center'><buttonid="bmi-btn"className="calculate-btn"type="button">Calculate BMI</button></div>{ }</div>
//</div>{/* 统计图 */}<div className='row center white-text'>统计图</div>{/* 详细记录信息 */}<div><div className='row center'><h2 className='white-text'>7 Day Data</h2></div><div me='data-container row'><div className="col m6 s12"><div className="card"><div className="card-content"><span className="card-title" data-test="bmi">BMI: 20.1</span><div className="card-data"><span data-test="weight">Weight: 70 kg</span><span data-test="height">Height: 180 cm</span><span data-test="date">Date: 2022/12/12</span></div><button className="delete-btn">X</button></div></div></div></div></div><div className='center'><button className='calculate-btn'>Undo</button></div></div>)
}export default App;

02完善样式

【分支02】
在这里插入图片描述

02代码

body{background-color: #172B4D;
}
/* .center h1 {color: #fff;
} */input {background-color: #fff !important;border-radius: 44px !important;width: 90% !important;padding: 0px 15px !important;
}input:focus {border-bottom: none !important;box-shadow: none !important;
}label {display: block;color: #fff !important;font-size: 1rem !important;
}.calculate-btn{background-color: #3f51b5;padding: 15px 50px;color: white;font-size: 16px;border-radius: 44px;cursor: pointer;border: 1px solid #3f51b5;margin-bottom: 40px;transform: translate3d(0, 0, 0);transition: all 0.2s ease;
}.calculate-btn:hover {background-color: #fff;transform: translate(0px, -2px);color: #5364c3;box-shadow: 0px 15px 30px -12px rgba(255, 255, 255, 0.2);
}.calculate-btn:focus {background-color: #32408f;
}.calculate-btn:focus:hover {color: white;
}.calculate-btn:disabled {border: 1px solid #999999;background-color: #cccccc;color: #666666;cursor: default;
}.calculate-btn:disabled:hover {box-shadow: none;transform: translate(0, 0);
}.data-container {background-color: #1f3a67;border-radius: 11px;margin-top: 40px;padding-top: 40px;padding-bottom: 40px;
}.card{background-color: #274881 !important;color: white;
}.card-title {font-weight: 500 !important;text-align: center;
}.card-data {display: flex;justify-content: space-around;
}.delete-btn {background-color: #e74c3c;color: white;border: none;border-radius: 50%;font-weight: 700;padding: 5px 9px;cursor: pointer;position: absolute;top: 0;right: 0;}
.delete-btn:focus {background-color: #e74c3c;
}

03输入信息模块

【分支03】

1.定义数据
2.定义,初始化数据状态
3.input 改变时,更新数据(受控组件)
4.提交数据
在这里插入图片描述

03代码

// src\components\App\App.jsx
import React, { useState, useEffect } from 'react';
import 'materialize-css/dist/css/materialize.min.css';
import './App.css'// 定义数据
const initialValues = {weight: '100',height: '180',data: ''
}const App = () => {// 定义,初始化数据状态const [state, setState] = useState(initialValues)// input 改变时,更新数据const handleChange = e => {let { value, name } = e.target;setState({...state,[name]: value,})}// 提交数据const handleSubmit = e => {setState(initialValues)console.log('已提交', state)}return (<div className='container'>{/* 标题 */}<div className='row center'><h1 className='white-text'>BMI Tracker</h1></div>{/* 输入框 */}<div className='row'><div className='col m12 s12'><div className='row'><div className='col m6 s12'><label htmlFor="weight">Weight (in kg)</label><inputtype="number"id="weight"name="weight"min="1"max="999"placeholder="50"value={state.weight}onChange={handleChange}/></div><div className='col m6 s12'><label htmlFor="height">Height (in cm)</label><inputtype="number"id="height"name="height"min="1"max="999"placeholder="175"value={state.height}onChange={handleChange}/></div></div><div className='center'><buttonid="bmi-btn"className="calculate-btn"type="button"disabled={!state.weight || !state.height}onClick={handleSubmit}>Calculate BMI</button></div>{ }</div></div>{/* 统计图 */}<div className='row center white-text'>统计图</div>{/* 详细记录信息 */}<div><div className='row center'><h2 className='white-text'>7 Day Data</h2></div><div className='data-container row'><div className="col m6 s12"><div className="card"><div className="card-content"><span className="card-title" data-test="bmi">BMI: 20.1</span><div className="card-data"><span data-test="weight">Weight: 70 kg</span><span data-test="height">Height: 180 cm</span><span data-test="date">Date: 2022/12/12</span></div><button className="delete-btn">X</button></div></div></div></div></div><div className='center'><button className='calculate-btn'>Undo</button></div></div>)
}export default App;

04 使用图表

chartjs:https://www.chartjs.org/docs/latest/
react-chartjs-2:https://react-chartjs-2.js.org/
主要知道react-chartjs-2怎么使用
【04分支】
在这里插入图片描述

04代码

// src\components\App\App.jsx
import React, { useState, useEffect } from 'react';
import { Line } from 'react-chartjs-2';
import 'materialize-css/dist/css/materialize.min.css';
import './App.css'// 定义数据
const initialValues = {weight: '100',height: '180',data: ''
}const App = () => {// 定义,初始化数据状态const [state, setState] = useState(initialValues)// input 改变时,更新数据const handleChange = e => {let { value, name } = e.target;setState({...state,[name]: value,})}// 提交数据const handleSubmit = e => {setState(initialValues)console.log('已提交', state)}const labelData = [2021,2022,2023]const bmiData = [100,200,300]// 定义图标数据const data = canvas => {// 从传入的canvas元素中获取2D绘图上下文,这是在canvas上绘制图形的基础。// 这段代码创建了一个线性渐变对象,起始于坐标(63, 81),结束于(181, 700),颜色从#929dd9渐变到#172b4d。这常用于为图表的填充色提供动态效果。const ctx = canvas.getContext("2d");const gradient = ctx.createLinearGradient(63, 81, 181, 700);gradient.addColorStop(0, '#929dd9');gradient.addColorStop(1, '#172b4d');return{labels: labelData,  // 图表的标签数组,通常对应X轴的各个分类datasets: [  // 一个数据集对象{label: 'BMI',  // 数据集的标签,通常用于图例data: bmiData,  // 数据集的实际数值数组,对应Y轴的值。backgroundColor: gradient,  // 使用之前创建的gradient作为填充色。borderColor: '#3F51B5',  // 数据点的边框颜色为#3F51B5。pointRadius: 6,  // 数据点的半径为6。pointHoverRadius: 8,  // 鼠标悬停时数据点的半径增大到8。pointHoverBorderColor: 'white',  // 鼠标悬停时数据点边框颜色变为白色。pointHoverBorderWidth: 2  // 鼠标悬停时数据点边框宽度为2。}]}
}// options 该对象包含了配置信息,主要用来定制基于Chart.js库的图表外观和行为const options = {responsive: true, // 设置图表是否应响应式scales: { //定义图表的坐标轴配置,包括x轴(xAxes)和y轴(yAxes)的样式和行为xAxes: [{scaleLabel: {display: true,labelString: 'Date',fontSize: 18,fontColor: 'white'},gridLines: {display: false,color: 'white'},ticks: {fontColor: 'white',fontSize: 16}}],yAxes: [{scaleLabel: { //  x轴标题的配置。display: true,  // 是否显示x轴标题labelString: 'BMI',  // x轴标题的文本内容fontSize: 18,  // 标题的字体大小和颜色fontColor: 'white'},gridLines: {  // 网格线的配置display: false,  // 不显示x轴的网格线color: 'white'  // 格线的颜色,即使不显示也定义了颜色},ticks: {  // 刻度线的配置fontColor: 'white', // 刻度线标签的字体颜色和大小。fontSize: 16,beginAtZero: true   // 图表的y轴刻度从0开始}}]},tooltips: { // 定义图表提示框(tooltip)的样式。// 分别设置提示框标题和内容的字体大小。titleFontSize: 13,bodyFontSize: 13}}return (<div className='container'>{/* 标题 */}<div className='row center'><h1 className='white-text'>BMI Tracker</h1></div>{/* 输入框 */}<div className='row'><div className='col m12 s12'><div className='row'><div className='col m6 s12'><label htmlFor="weight">Weight (in kg)</label><inputtype="number"id="weight"name="weight"min="1"max="999"placeholder="50"value={state.weight}onChange={handleChange}/></div><div className='col m6 s12'><label htmlFor="height">Height (in cm)</label><inputtype="number"id="height"name="height"min="1"max="999"placeholder="175"value={state.height}onChange={handleChange}/></div></div><div className='center'><buttonid="bmi-btn"className="calculate-btn"type="button"disabled={!state.weight || !state.height}onClick={handleSubmit}>Calculate BMI</button></div>{ }</div></div>{/* 统计图 */}<div className='row center white-text'>{/* 使用折线图 */}<Line data={data} options={options}/></div>{/* 详细记录信息 */}<div><div className='row center'><h2 className='white-text'>7 Day Data</h2></div><div className='data-container row'><div className="col m6 s12"><div className="card"><div className="card-content"><span className="card-title" data-test="bmi">BMI: 20.1</span><div className="card-data"><span data-test="weight">Weight: 70 kg</span><span data-test="height">Height: 180 cm</span><span data-test="date">Date: 2022/12/12</span></div><button className="delete-btn">X</button></div></div></div></div></div><div className='center'><button className='calculate-btn'>Undo</button></div></div>)
};
export default App;

解释

const data = canvas => {const ctx = canvas.getContext('2d');const gradient = ctx.createLinearGradient(63, 81, 181, 700);gradient.addColorStop(0, '#929dd9');gradient.addColorStop(1, '#172b4d');return {labels: labelData,datasets: [{label: 'BMI',data: bmiData,backgroundColor: gradient,borderColor: '#3F51B5',pointRadius: 6,pointHoverRadius: 8,pointHoverBorderColor: 'white',pointHoverBorderWidth: 2}]};};

这段JavaScript代码定义了一个名为data的函数,它接收一个canvas元素作为参数,并返回一个配置对象,该对象常用于初始化或更新基于Chart.js(或其他类似图表库)的图表数据和样式。下面是代码的详细解释:

  1. 获取2D渲染上下文:
javascriptconst ctx = canvas.getContext('2d');

这行代码从传入的canvas元素中获取2D绘图上下文,这是在canvas上绘制图形的基础。

  1. 创建线性渐变:
javascript   const gradient = ctx.createLinearGradient(63, 81, 181, 700);gradient.addColorStop(0, '#929dd9');gradient.addColorStop(1, '#172b4d');

这段代码创建了一个线性渐变对象,起始于坐标(63, 81),结束于(181, 700),颜色从#929dd9渐变到#172b4d。这常用于为图表的填充色提供动态效果。

  1. 返回图表配置对象
    返回的对象结构定义了图表的数据和样式,主要包括:
    • labels: labelData,图表的标签数组,通常对应X轴的各个分类。

    • datasets
      包含一个数据集对象,具体定义为:
      • label: 'BMI',数据集的标签,通常用于图例。
      • data: bmiData,数据集的实际数值数组,对应Y轴的值。
      • backgroundColor: 使用之前创建的gradient作为填充色。
      • borderColor: 数据点的边框颜色为#3F51B5
      • pointRadius: 数据点的半径为6。
      • pointHoverRadius: 鼠标悬停时数据点的半径增大到8。
      • pointHoverBorderColor: 鼠标悬停时数据点边框颜色变为白色。
      • pointHoverBorderWidth: 鼠标悬停时数据点边框宽度为2。

综上所述,这个函数用于生成一个配置对象,配置了一种特定样式的图表,其中数据填充色为线性渐变,适合于展示BMI(身体质量指数)等相关数据的图表展示。

options的对象,该对象包含了配置信息,主要用来定制基于Chart.js库的图表外观和行为。具体配置项解释如下:

  • responsive: true: 设置图表是否应响应式,即图表是否会根据其容器的大小自动调整。

  • scales: 定义图表的坐标轴配置,包括x轴(xAxes)和y轴(yAxes)的样式和行为。

    • xAxes: 配置x轴的设置。

      • scaleLabel
        x轴标题的配置。
        • display: true: 是否显示x轴标题。
        • labelString: ‘Date’: x轴标题的文本内容。
        • fontSize: 18fontColor: ‘white’: 标题的字体大小和颜色。
      • gridLines
        网格线的配置。
        • display: false: 不显示x轴的网格线。
        • color: ‘white’: 网格线的颜色,即使不显示也定义了颜色。
      • ticks
        刻度线的配置。
        • fontColor: ‘white’fontSize: 16: 刻度线标签的字体颜色和大小。
    • yAxes: 配置y轴的设置,结构和配置项含义与x轴相似,但多了beginAtZero: true,表示y轴的刻度应该从0开始。

  • tooltips: 定义图表提示框(tooltip)的样式。

    • titleFontSize: 13bodyFontSize: 13: 分别设置提示框标题和内容的字体大小。

整体而言,这段代码详细地定制了一个图表的外观,包括坐标轴的标题、网格线、刻度线的样式,以及提示框的字体大小,使得图表更加符合特定的视觉需求,比如使用白色字体适应深色背景等。

05详细记录信息渲染

【05分支】

1.完整信息数据列表
硬编码,编写两组数据
2.完整信息数据列表渲染到图表
3.详细记录信息渲染

在这里插入图片描述
现在和设计稿已经一样了,

05代码

// src\components\App\App.jsx
import React, { useState, useEffect } from 'react';
import { Line } from 'react-chartjs-2';
import 'materialize-css/dist/css/materialize.min.css';
import './App.css'// 定义数据
const initialValues = {weight: '100',height: '180',data: ''
}// 完整信息数据列表
const stateS = [{ "weight": "50", "height": "170", "date": "2024/6/11 20:05:16", "bmi": "17.30", "id": "e4d54aef-0e89-4e7e-a887-9d7a289da5de" },{ "weight": "51", "height": "170", "date": "2024/6/11 20:05:32", "bmi": "17.65", "id": "a79a7b3c-c1e6-48b3-a2ff-f331db09fa72" }
]const App = () => {// 定义,初始化数据状态const [state, setState] = useState(initialValues)// input 改变时,更新数据const handleChange = e => {let { value, name } = e.target;setState({...state,[name]: value,})}// 提交数据const handleSubmit = e => {setState(initialValues)console.log('已提交', state)}// 交给图表 显示数据const labelData = stateS.map(item => item.date)const bmiData = stateS.map(item => item.bmi)// 定义图标数据const data = canvas => {// 从传入的canvas元素中获取2D绘图上下文,这是在canvas上绘制图形的基础。// 这段代码创建了一个线性渐变对象,起始于坐标(63, 81),结束于(181, 700),颜色从#929dd9渐变到#172b4d。这常用于为图表的填充色提供动态效果。const ctx = canvas.getContext("2d");const gradient = ctx.createLinearGradient(63, 81, 181, 700);gradient.addColorStop(0, '#929dd9');gradient.addColorStop(1, '#172b4d');return {labels: labelData,  // 图表的标签数组,通常对应X轴的各个分类datasets: [  // 一个数据集对象{label: 'BMI',  // 数据集的标签,通常用于图例data: bmiData,  // 数据集的实际数值数组,对应Y轴的值。backgroundColor: gradient,  // 使用之前创建的gradient作为填充色。borderColor: '#3F51B5',  // 数据点的边框颜色为#3F51B5。pointRadius: 6,  // 数据点的半径为6。pointHoverRadius: 8,  // 鼠标悬停时数据点的半径增大到8。pointHoverBorderColor: 'white',  // 鼠标悬停时数据点边框颜色变为白色。pointHoverBorderWidth: 2  // 鼠标悬停时数据点边框宽度为2。}]}}// options 该对象包含了配置信息,主要用来定制基于Chart.js库的图表外观和行为const options = {responsive: true, // 设置图表是否应响应式scales: { //定义图表的坐标轴配置,包括x轴(xAxes)和y轴(yAxes)的样式和行为xAxes: [{scaleLabel: {display: true,labelString: 'Date',fontSize: 18,fontColor: 'white'},gridLines: {display: false,color: 'white'},ticks: {fontColor: 'white',fontSize: 16}}],yAxes: [{scaleLabel: { //  x轴标题的配置。display: true,  // 是否显示x轴标题labelString: 'BMI',  // x轴标题的文本内容fontSize: 18,  // 标题的字体大小和颜色fontColor: 'white'},gridLines: {  // 网格线的配置display: false,  // 不显示x轴的网格线color: 'white'  // 格线的颜色,即使不显示也定义了颜色},ticks: {  // 刻度线的配置fontColor: 'white', // 刻度线标签的字体颜色和大小。fontSize: 16,beginAtZero: true   // 图表的y轴刻度从0开始}}]},tooltips: { // 定义图表提示框(tooltip)的样式。// 分别设置提示框标题和内容的字体大小。titleFontSize: 13,bodyFontSize: 13}}return (<div className='container'>{/* 标题 */}<div className='row center'><h1 className='white-text'>BMI Tracker</h1></div>{/* 输入框 */}<div className='row'><div className='col m12 s12'><div className='row'><div className='col m6 s12'><label htmlFor="weight">Weight (in kg)</label><inputtype="number"id="weight"name="weight"min="1"max="999"placeholder="50"value={state.weight}onChange={handleChange}/></div><div className='col m6 s12'><label htmlFor="height">Height (in cm)</label><inputtype="number"id="height"name="height"min="1"max="999"placeholder="175"value={state.height}onChange={handleChange}/></div></div><div className='center'><buttonid="bmi-btn"className="calculate-btn"type="button"disabled={!state.weight || !state.height}onClick={handleSubmit}>Calculate BMI</button></div>{ }</div></div>{/* 统计图 */}<div className='row center white-text'>{/* 使用折线图 */}<Line data={data} options={options} /></div>{/* 详细记录信息 */}<div><div className='row center'><h2 className='white-text'>7 Day Data</h2></div><div className='data-container row'>{stateS.length > 0 ? (<>{stateS.map(info => (<div className="col m6 s12"><div className="card"><div className="card-content"><span className="card-title" data-test="bmi">BMI: {info.bmi}</span><div className="card-data"><span data-test="weight">Weight: {info.weight} kg</span><span data-test="height">Height: {info.height} cm</span><span data-test="date">Date: {info.date}</span></div><button className="delete-btn">X</button></div></div></div>))}</>) : (<div className='center white-text'>No log found</div>)}</div></div><div className='center'><button className='calculate-btn'>Undo</button></div></div>)
};
export default App;

06 让数据变成响应式的

06-1输入框的数据处理

【06-1分支】

问题
在这里插入图片描述
问题代码
在这里插入图片描述
解决
在这里插入图片描述
在这里插入图片描述

每次state变化,都会触发更新

我们在体重,身高输入框,输入
53,175
会往state状态里插入,如下的的一条数据

{weight: '53', height: '175', date: '2024/6/15 21:40:24', bmi: '17.31', id: 'f83452b5-a7b5-4a57-beb1-ea552bf432cb'}

接在下来,我们的图表,和记录信息的区域,都显示这些数据
在这里插入图片描述

06-2图表,和记录信息的区域数据处理

state有几条数据就显示几条
在这里插入图片描述

在这里插入图片描述

07 删除功能,撤销功能

点击x删除对应数据
点击Undo撤销上一步操作(如果上一步是点击x删除对应数据,那么Undo就是回复上一步;如果上一步是添加里一条数据,那么Undo就是删除新添加的这条数据 )
在这里插入图片描述

删除功能完成

在这里插入图片描述

撤销功能

思路就是所在点击删除后,第一件事就是先保存一份最新的state

  // let lastState // 注意这个地方,如只是一般的变量,那么每次setState(lastState),渲染的时候handleUndo函数都会从新执行,一直在初始化lastState,所以需要使用useReflet lastState = useRef([])const deleteCard = (id) => {lastState.current = state.slice();let newState = state.filter(item => item.id !== id)setState(newState)// console.log(id,state)// console.log(lastState.current)}const handleUndo = () => {// setState(lastState);setState(lastState.current);// console.log(lastState.current , state)}

到目前位置,我们所有的功能都已完成
在这里插入图片描述

08 数据持久化、组件化、模块化

目前我们我们把这个小应用全部写在了一个文件里,这样文件会显得臃肿,庞大,混乱难以维护。当等功能增多的时候就会,更加庞大,混乱。

所以我们接下来要拆分这个组件,分成一个个小的组件。

现在我们的数据在缓存里,刷新就会丢失。所以我们将数据持久化到本地,关闭浏览器也不会丢失。

08-1数据持久化

【分支08-1】
写一个将数据存储到本地,从本地获取的数据的模块

// src\helpers\localStorage.js
export const getData = (key) => {if (!localStorage) return;try {return JSON.parse(localStorage.getItem(key));} catch (err) {console.error(`Error getting item ${key} from localStorage`, err);}
};export const storeData = (key, item) => {if (!localStorage) return;try {return localStorage.setItem(key, JSON.stringify(item));} catch (err) {console.error(`Error storing item ${key} to localStorage`, err);}
};

在这里插入图片描述

存数据
useEffect(() => {storeData('data', state);  // 初始化组件和每次更新state时,都会触发storeData保存数据console.log('App_state', state)}, [state]);
取数据
const App = () => {// ,initialState 被定义为一个箭头函数,然后作为 useState 的参数使用。这里有一个常见的误解:通常我们不希望将 useState 的初始化函数定义为箭头函数,因为这样会导致每次组件渲染时都会创建一个新的函数实例,可能会引发不必要的组件重新渲染。// initialState是一个箭头函数,这种方式适用于当你想延迟执行 getData('data') 或者在未来的某个时间点决定是否执行这个操作时const initialState = () => getData('data') || [];  const [state, setState] = useState(initialState)
Undo 使用本地化数据,不使用useRef()缓存了
  // let lastState // 注意这个地方,如只是一般的变量,那么每次setState(lastState),渲染的时候handleUndo函数都会从新执行,一直在初始化lastState,所以需要使用useRef// let lastState = useRef([])const deleteCard = (id) => {storeData('lastState', state);  // 不使用useRef([])缓存了,直接本地化保存数据let newState = state.filter(item => item.id !== id)setState(newState)// console.log(id,state)// console.log(lastState.current)}const handleUndo = () => {// setState(lastState);setState(getData('lastState'));// console.log(lastState.current , state)}

08-2组件化、模块化

将输入添加,图表模块,七天数据模块,做成单独的模块

输入添加模块
图表模块
七天数据模块

08-2-1输入添加模块

【分支08-2-1】
直接将我写好的都拿过来
Input_handleChange , 从App模块传递到 BmiForm 模块

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import '../App/App.css'const Input_initialValues = {weight: '',height: '',date: ''
}const BmiForm = ({ Input_handleChange }) => {// 定义,初始化数据状态const [Input_state, setState_Input] = useState(Input_initialValues)// input 改变时,更新数据const handleChange = e => {let { value, name } = e.target;// 输入的数字不能大于999if (value > 999) {value = 999}const date = new Date().toLocaleString().split(',')[0]// console.log(date)// 更新输入框的值setState_Input({...Input_state,[name]: value,date})}// 提交数据const handleSubmit = () => {Input_handleChange(Input_state)setState_Input(Input_initialValues)// console.log('已提交', Input_state)// console.log('已提交', Input_initialValues)}return (<>{/* 输入框 */}< div className='row' ><div className='col m12 s12'><div className='row'><div className='col m6 s12'><label htmlFor="weight">Weight (in kg)</label><inputtype="number"id="weight"name="weight"min="1"max="999"placeholder="50"value={Input_state.weight}onChange={handleChange}/></div><div className='col m6 s12'><label htmlFor="height">Height (in cm)</label><inputtype="number"id="height"name="height"min="1"max="999"placeholder="175"value={Input_state.height}onChange={handleChange}/></div></div><div className='center'><buttonid="bmi-btn"className="calculate-btn"type="button"disabled={!Input_state.weight || !Input_state.height}onClick={handleSubmit}>Calculate BMI</button></div>{ }</div></div ></>)
}BmiForm.propTypes ={change: PropTypes.func.isRequired
}export default BmiForm;
08-2-2图表模块

【分支08-2-2】
直接将我写好的都拿过来

08-2-3七天数据模块

【分支08-2-3】
直接将我写好的都拿过来

09 修一些bug

【分支09】

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

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

相关文章

基于C#开发web网页管理系统模板流程-主界面统计功能完善

点击返回目录-> 基于C#开发web网页管理系统模板流程-总集篇-CSDN博客 前言 紧接上篇->基于C#开发web网页管理系统模板流程-主界面管理员入库和出库功能完善_c#web程序设计-CSDN博客 统计功能是管理系统很常见的功能&#xff0c;例如仓库管理系统要统计某时间段的出入库以…

QT信号与槽/窗口组件优化/使用QT制作QQ登录界面

使用手动连接&#xff0c;将登录框中的取消按钮使用第二中连接方式&#xff0c;右击转到槽&#xff0c;在该槽函数中&#xff0c;调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断u界面上输入的账号是否为"admin"&#xff0c;…

永磁同步直线电机(PMLSM)控制与仿真3-永磁同步直线电机数学三环控制整定

文章目录 1、电流环参数整定2、速度环参数整定3、位置环参数整定 写在前面&#xff1a;原本为一篇文章写完了永磁同步直线电机数学模型介绍&#xff0c;永磁同步直线电机数学模型搭建&#xff0c;以及永磁同步直线电机三环参数整定及三环仿真模型搭建&#xff0c;但因为篇幅较长…

HTML前端

html 超文本标记语言 文本&#xff1a;文字字符 超文本&#xff1a;网页内容 标记&#xff1a;标签 标识 提供许多标签&#xff0c;不同标签功能不同&#xff0c;网页就是通过这些标签描述出来的&#xff0c;最终由浏览器解释运行我们看到的网页 <!-- html注释<!DO…

C++ 50 之 继承中的对象模型

继承中的对象模型 在C编译器的内部可以理解为结构体&#xff0c;子类是由父类成员叠加子类新成员而成&#xff1a; #include <iostream> #include <string> using namespace std;class Base03{ public:int m_a; protected:int m_b; private:int m_c; // 哪怕是…

lua对接GPT4实现对话

演示效果&#xff1a; 准备材料&#xff1a; 1、FastWeb网站开发服务&#xff1a;fwlua.com 2、一台服务器 该示例使用开源项目&#xff1a;fastweb 实现。 代码比较简单&#xff0c;主要是两部分&#xff0c;一个lua代码和一个html页面&#xff0c;用来用户发起请求和后台…

面向事件编程之观察者模式

前言 村里的老人常说&#xff1a;真男人就该懂得遵守“三不原则”——不主动、不拒绝、不负责。 一个复杂的软件系统&#xff0c;其中必然会存在各种各样的“对象”&#xff0c;如果在设计之初没有注意控制好耦合度&#xff0c;导致各个对象甚至是函数之间高度耦合&#xff0…

网络安全知识全景地图V1.0 - 20240616更新

网络安全领域的知识全景涵盖了从基础概念到高级技术的广泛内容。博主基于自身十年多的工作经验结合CISSP认证官方教材按照不同的主题和层次梳理出如下高层次的概览地图&#xff0c;可以帮助个人和组织理解网络安全领域的主题。 1.1. 基础理论 1.1.1. 网络安全概述 网络安全的…

React@16.x(29)useRef

目录 1&#xff0c;介绍2&#xff0c;和 React.createRef() 的区别3&#xff0c;计时器的问题 目前来说&#xff0c;因为函数组件每次触发更新时&#xff0c;都会重新运行。无法像类组件一样让一些内容保持不变。 所以才出现了各种 HOOK 函数&#xff1a;useState&#xff0c;u…

Camtasia Studio 2024软件最新版下载【安装详细图文教程】

​Camtasia是美国TechSmith公司出品的一款集电脑屏幕录制、视频剪辑为一体的软件套装。同时包含Camtasia 录制器、Camtasia Studio&#xff08;编辑器&#xff09;、Camtasia 菜单制作器、Camtasia 剧场、Camtasia 播放器和Screencast的内置功能。 安 装 包 获 取 地 址&#x…

AirPlay技术规范及认证资讯

AirPlay是Apple开发的一种无线技术&#xff0c;允许用户将音频、视频或图片从iOS设备、Mac电脑或其他支持AirPlay的设备无线传输到支持AirPlay的接收器设备上&#xff0c;例如智能电视或音响系统。这项技术基于Wi-Fi网络&#xff0c;提供了一种便捷的方式来共享媒体内容。AirPl…

车票信息的请求与显示

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 1 发送与分析车票信息的查询请求 得到了获取车票信息的网络请求地址&#xff0c;然后又分析出请求地址的必要参数以及车站名称转换的文件&#xff…

《软件测试52讲》——测试基础知识篇

1 你真的懂测试吗&#xff1f;从“用户登录”测试谈起 从“用户登录”测试谈起&#xff0c;“用户登录”功能作为测试对象 作为测试工程师&#xff0c;你的目标是要保证系统在各种应用场景下的功能是符合设计要求的&#xff0c;所以你需要考虑的测试用例就需要更多、更全面。 …

2078.两栋颜色不同且距离最远的房子

街上有 n 栋房子整齐地排成一列&#xff0c;每栋房子都粉刷上了漂亮的颜色。给你一个下标从 0 开始且长度为 n 的整数数组 colors &#xff0c;其中 colors[i] 表示第 i 栋房子的颜色。 返回 两栋 颜色 不同 房子之间的 最大 距离。 第 i 栋房子和第 j 栋房子之间的距离是 a…

Linux应用编程 - i2c-dev操作I2C

嵌入式Linux操作I2C设备&#xff0c;我们一般会在内核态编写I2C驱动程序。另外还能在用户空间编写I2C程序&#xff0c;下面介绍相关代码的实现。 i2c-dev框架在内核中封装了I2C通信所需要的所有通信细节&#xff0c;I2C适配器会在/dev目录下创建字符设备&#xff0c;例如&#…

kettle从入门到精通 第七十课 ETL之kettle kettle数据校验,脏数据清洗轻松拿捏

场景&#xff1a;输入在指定的错误(错误应涵盖数据类型不匹配的情况)行数内&#xff0c;trans不报错&#xff0c;但通过错误处理步骤捕捉&#xff0c;并记入文件&#xff0c;整个数据管线正常完成直至处理完最后一个输入行。 解决方案&#xff1a;使用步骤【数据检验】进行处理…

本周MoonBit新增Wasm1引用计数支持、语法即将添加错误恢复机制

MoonBit更新 【Wasm MVP】Wasm1 后端添加基于 Perceus 算法的引用计数支持 【语法】throw raise try catch 均被保留为关键字 为了即将添加的错误处理机制 【Core】List与sorted_map被移动至core/immut下 List被移动至core/immut/list包中&#xff0c;并被移除内置类型支持 …

哈希表、递归在二叉树中的应用-1372. 二叉树中的最长交错路径

题目链接及描述 1372. 二叉树中的最长交错路径 - 力扣&#xff08;LeetCode&#xff09; 题目分析 题目所述&#xff0c;计算在二叉树中交替遍历的最大深度【左->右->左】【右->左->右】&#xff0c;例如对于从当前根节点root出发&#xff0c;则此时遍历方向有两个…

今日成果2024-6-7 TrustZone TEE安全SDK开发指南

Rockchip Vendor Storage Application Note.pdf OK 开机下&#xff0c;可以实现Vendor Storage的读写。 0ms时同步RTC时间 OK Rockchip_Developer_Guide_TEE_SDK_CN.pdf 什么是TrustZone 此系统方法意味着可以保护安全内存、加密块、键盘和屏幕等外设&#xff0c;从而可确…

视频剪辑可以赚钱吗 快速学会视频剪辑的方法

由于视频剪辑的需求不断增长&#xff0c;学会视频剪辑成为一项自媒体必备的技能&#xff0c;这个技能可以为个人带来收入和职业发展带来机会。无论是作为自由职业者还是在公司工作&#xff0c;掌握视频剪辑技能都可以为你提供更多的工作机会和竞争优势。这篇文章将讲解视频剪辑…