基于React搭建个人空间(更新版)

开始

注意

项目需要手动安装axios和配置antd按需引入(可参考官方文档:https://3x.ant.design/docs/react/use-with-create-react-app-cn)。其实,不用配置也行,但每次需要import 'antd/dist/antd.css

原理

原理:

  • 项目基于React实现,通过axios发送请求获取数据,并通过bootstrap和antd进行页面渲染

涉及知识点

  • 掌握html,css,js三剑客基础知识以及弹性布局
  • 具备一定node.js基础,知道npm启动服务器就行
  • 具备一定的axios获取数据原理
  • 具备React基础知识,路由以及使用antd组件库

项目

项目结构

描述

Login(登录页)
Homer(主页)
Header(主页表头卡片)
Asider(侧边栏导航区)
Messa(个人信息展示)
Photo(接口加载的照片)
image(静态文件)
在这里插入图片描述

源码

注意:

  • 组件下每一个文件都包含在一个文件夹里,如Login/Login.jsx,Login/Login.css,下面不再声明!!!

Login

import React, { Component } from 'react'

import './Login.css'
export default class Login extends Component {
  Login = () => {
    const { user, passwd } = this
    if (user.value !== 'Admin' && passwd.value !== '123') return alert('用户或密码错误')
    localStorage.setItem('token', 'Bearer xxxx')//设置token验证 
    this.props.history.replace('/home')
  }
  render() {
    return (
      <div className="main">
        <div className="login-box">
          <div className="owl" id="owl">
            <div className="hand"></div>
            <div className="hand hand-r"></div>
            <div className="arms">
              <div className="arm"></div>
              <div className="arm arm-r"></div>
            </div>
          </div>
          <div className="input-box">
            <input ref={(c) => (this.user = c)} type="text" placeholder="账号" />
            <input ref={(c) => (this.passwd = c)} type="password" placeholder="密码" id="password" />
            <button onClick={() => this.Login()}>登录</button>
          </div>
        </div>
      </div>
    )
  }
}

* {
  /* 初始化 */
  margin: 0;
  padding: 0;
}
.main {
  /* 100%窗口高度 */
  height: 100vh;
  /* 弹性布局 居中 */
  display: flex;
  justify-content: center;
  align-items: center;
  /* 渐变背景 */
  background: linear-gradient(200deg, #72afd3, #96fbc4);
}
.login-box {
  /* 相对定位 */
  position: relative;
  width: 320px;
}
.input-box {
  /* 弹性布局 垂直排列 */
  display: flex;
  flex-direction: column;
}
.input-box input {
  height: 40px;
  border-radius: 3px;
  /* 缩进15像素 */
  text-indent: 15px;
  outline: none;
  border: none;
  margin-bottom: 15px;
}
.input-box input:focus {
  outline: 1px solid lightseagreen;
}
.input-box button {
  border: none;
  height: 45px;
  background-color: lightseagreen;
  color: #fff;
  border-radius: 3px;
  cursor: pointer;
}
/* 接下来是猫头鹰的样式 */
.owl {
  width: 211px;
  height: 108px;
  /* 背景图片 */
  background: url('/src/image/owl-login.png') no-repeat;
  /* 绝对定位 */
  position: absolute;
  top: -100px;
  /* 水平居中 */
  left: 50%;
  transform: translateX(-50%);
}
.owl .hand {
  width: 34px;
  height: 34px;
  border-radius: 40px;
  background-color: #472d20;
  /* 绝对定位 */
  position: absolute;
  left: 12px;
  bottom: -8px;
  /* 沿Y轴缩放0.6倍(压扁) */
  transform: scaleY(0.6);
  /* 动画过渡 */
  transition: 0.3s ease-out;
}
.owl .hand.hand-r {
  left: 170px;
}
.owl.password .hand {
  transform: translateX(42px) translateY(-15px) scale(0.7);
}
.owl.password .hand.hand-r {
  transform: translateX(-42px) translateY(-15px) scale(0.7);
}
.owl .arms {
  position: absolute;
  top: 58px;
  width: 100%;
  height: 41px;
  overflow: hidden;
}
.owl .arms .arm {
  width: 40px;
  height: 65px;
  position: absolute;
  left: 20px;
  top: 40px;
  background: url('/src/image/owl-login-arm.png') no-repeat;
  transform: rotate(-20deg);
  transition: 0.3s ease-out;
}
.owl .arms .arm.arm-r {
  transform: rotate(20deg) scaleX(-1);
  left: 158px;
}
.owl.password .arms .arm {
  transform: translateY(-40px) translateX(40px);
}
.owl.password .arms .arm.arm-r {
  transform: translateY(-40px) translateX(-40px) scaleX(-1);
}

Home

import Header from '../Header/Header'
import React, { Component } from 'react'
import Asider from '../Asider/Asider'
import './Home.css'
export default class Home extends Component {
  render() {
    return (
      <div className="container">
        <Header />
        <Asider />
      </div>
    )
  }
}

body {
  background-color: #96fbc4;
}

Header

import React, { Component } from 'react'
import { PageHeader, Descriptions } from 'antd'
import './Header.css'
export default class Header extends Component {
  render() {
    return (
      <div className="site-page-header-ghost-wrapper">
        <PageHeader ghost={false} onBack={() => window.history.back()} title="React" subTitle="I love React!">
          <Descriptions size="small" column={3}>
            <Descriptions.Item label="Created">李现</Descriptions.Item>
            <Descriptions.Item label="Identity">
              <a href="https://github.com">student</a>
            </Descriptions.Item>
            <Descriptions.Item label="Creation Time">2022/3/23</Descriptions.Item>
            <Descriptions.Item label="Effective Time">2022/3/23</Descriptions.Item>
            <Descriptions.Item label="Remarks">Gonghu Road, Xihu District, Hangzhou, Zhejiang, China</Descriptions.Item>
          </Descriptions>
        </PageHeader>
      </div>
    )
  }
}

.site-page-header-ghost-wrapper {
  padding: 24px;
  background-color: #2496d8;
}

Asider

import React, { Component } from 'react'
import { Menu } from 'antd'
import { AppstoreOutlined, PieChartOutlined, DesktopOutlined, ContainerOutlined, MailOutlined } from '@ant-design/icons'
import { Link, Route, Redirect } from 'react-router-dom'
import Message from '../Message/Message'
import Photo from '../Photo/Photo'
import './Asider.css'
// import 'antd/dist/antd.css'
const { SubMenu } = Menu
export default class Asider extends Component {
  state = {
    collapsed: false
  }
  //退出登陆
  close = () => {
    localStorage.removeItem('token')//清除token
  }
  toggleCollapsed = () => {
    this.setState({
      collapsed: !this.state.collapsed
    })
  }
  render() {
    return (
      <div className="mytbody">
        {/* 导航区 */}
        <div style={{ width: 256 }}>
          <Menu defaultSelectedKeys={['1']} defaultOpenKeys={['sub1']} mode="inline" theme="dark" inlineCollapsed={this.state.collapsed}>
            <Menu.Item key="1" icon={<PieChartOutlined />}>
              <Link to="/home/message">个人资料介绍</Link>
            </Menu.Item>
            <Menu.Item key="2" icon={<DesktopOutlined />}>
              <Link to="/home/photo">相册</Link>
            </Menu.Item>
            <Menu.Item key="3" icon={<ContainerOutlined />}>
              <Link to="/" onClick={this.close}>
                退出
              </Link>
            </Menu.Item>
            <SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
              <Menu.Item key="5">Option 5</Menu.Item>
              <Menu.Item key="6">Option 6</Menu.Item>
              <Menu.Item key="7">Option 7</Menu.Item>
              <Menu.Item key="8">Option 8</Menu.Item>
            </SubMenu>
            <SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
              <Menu.Item key="9">Option 9</Menu.Item>
              <Menu.Item key="10">Option 10</Menu.Item>
              <SubMenu key="sub3" title="Submenu">
                <Menu.Item key="11">Option 11</Menu.Item>
                <Menu.Item key="12">Option 12</Menu.Item>
              </SubMenu>
            </SubMenu>
          </Menu>
        </div>
        {/* 展示区 */}

        <Route path="/home/message" component={Message} />
        <Route path="/home/photo" component={Photo} />
        <Redirect to="/home/message" />
      </div>
    )
  }
}

.mytbody {
  display: flex;
}

Message

import React, { Component } from 'react'
import { Layout } from 'antd'
import './Message.css'
// import img1 from '../../image/lx.jpg'
import img2 from '../../image/lx1.jpg'
import img3 from '../../image/lx2.jpg'
import img4 from '../../image/lx3.jpg'
const { Header, Footer, Sider, Content } = Layout
export default class Message extends Component {
  render() {
    return (
      <div>
        <Layout>
          <Sider>
            <div className="profile">
              {/* eslint-disable-next-line jsx-a11y/alt-text */}
              <img src="http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcd996d7a.jpg" />
              <h3>李现</h3>
              <p>actor</p>
              <div className="share">
                {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
                <a href="https://www.baidu.com" className="fab fa-facebook-f"></a>
                {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
                <a href="https://www.baidu.com" className="fab fa-twitter"></a>
                {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
                <a href="https://www.baidu.com" className="fab fa-instagram"></a>
                {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
                <a href="https://www.baidu.com" className="fab fa-linkedin"></a>
              </div>
              <div className="buttons">
                <a href="https://www.baidu.com" className="btn">
                  download cv
                </a>
                <a href="https://www.baidu.com" className="btn">
                  hire me
                </a>
              </div>
            </div>
          </Sider>
          <Layout>
            <Header className="header-title">
              <h3 className="title">你不知道的李现</h3>
            </Header>
            <Content>
              <div className="about">
                <p>李现,曾用名李晛,1991年10月19日出生于湖北省咸宁市,成长于湖北省荆州市 ,中国内地影视男演员,毕业于北京电影学院表演系2010级</p>
                <p>
                  李现出生于湖北省咸宁市,成长于湖北省荆州市,其父母在荆州市工作 [1] ,他在上初二的时候体重达到了160斤,到了高三时他的体重开始下降。李现高中就读于荆州中学,由于第一年高考不理想,第二年他便报考了表演和播音主持方向,之后顺利考上了北京电影学院表演系2010级 [23]
                  ,而他也是当时湖北省荆州市第一位考取北京电影学院的大学生;此外,他还接受了电视台的采访,展示了吹萨克斯、弹吉他等才艺 [24] 。
                </p>
              </div>
              <div className="display">
                <ul>
                  <li>中文名:&nbsp;&nbsp;&nbsp;李现</li>
                  <li>&nbsp;名:&nbsp;&nbsp;&nbsp;李晛、现哥、现现</li>
                  <li>&nbsp;籍:&nbsp;&nbsp;&nbsp;中国</li>
                  <li>出生地:&nbsp;&nbsp;&nbsp;湖北省咸宁市</li>
                </ul>
                <ul>
                  <li>&nbsp;&nbsp;座:&nbsp;&nbsp;&nbsp;天秤座</li>
                  <li>&nbsp;&nbsp;高:&nbsp;&nbsp;&nbsp;185cm</li>
                  <li>&nbsp;&nbsp;业:&nbsp;&nbsp;&nbsp;演员</li>
                  <li>毕业院校:&nbsp;&nbsp;&nbsp;北京电影学院</li>
                </ul>
              </div>
            </Content>
            <Footer>
              <div className="image">
                <h3 className="title">心之所"现"</h3>
                <div className="box-container">
                  <div className="box">
                    <img src={img2} alt="" />
                  </div>
                  <div className="box">
                    <img src={img3} alt="" />
                  </div>
                  <div className="box">
                    <img src={img4} alt="" />
                  </div>
                </div>
              </div>
            </Footer>
          </Layout>
        </Layout>
      </div>
    )
  }
}

:root {
  --blue: #2980b9;
  --black: #333;
  --white: #fff;
  --light-color: #777;
  --light-bg: #eee;
  --box-shadow: 0 5px 10px rgba(0, 0, 00, 0.1);
}

* {
  font-family: serif;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  outline: none;
  border: none;
  text-decoration: none;
  text-transform: capitalize;
  transition: all 0.2s linear;
  list-style: none;
}

.btn {
  margin-top: 10px;
  display: inline-block;
  padding: 7px 20px;
  border-radius: 5px;
  background: var(--blue);
  color: var(--white);
  font-size: 17px;
}

.profile {
  flex: 1 1 400px;
  height: 600px;
  background: var(--white);
  padding: 30px 20px;
  text-align: center;
  position: sticky;
  border-right: 2px solid var(--black);
  top: 20px;
  left: 0px;
}

.profile img {
  height: 150px;
  width: 150px;
  border-radius: 50%;
  border: 2px solid var(--black);
  object-fit: cover;
  margin-bottom: 10px;
}

.profile h3 {
  color: var(--black);
  font-size: 20px;
}

.profile p {
  color: var(--blue);
  padding: 5px 0;
  margin-bottom: 10px;
  font-size: 15px;
}

.profile .share {
  margin: 10px 0;
}

.profile .share a {
  height: 50px;
  width: 50px;
  line-height: 50px;
  font-size: 20px;
  border-radius: 50%;
  color: var(--black);
  background: var(--light-bg);
}

.profile .share a:hover {
  background: var(--blue);
  color: var(--white);
  box-shadow: var(--box-shadow);
}
.title {
  margin-top: 10px;
  font-weight: 400;
  font-style: italic;
  letter-spacing: 1em;
  margin-bottom: 10px;
  padding-bottom: 5px;
  border-bottom: 1px solid #000;
  color: var(#fff);
}
.about p {
  color: var(--light-color);
  text-indent: 2em;
  line-height: 30px;
}
.display {
  display: flex;
  justify-content: space-around;
}

.display ul {
  flex: 1 1 200px;
  padding: 10px 20px;
  font-size: 18px;
  font-weight: bold;
}

.image .title {
  font-weight: 400;
  font-style: italic;
  letter-spacing: 1em;
  margin-bottom: 10px;
  padding-bottom: 5px;
  border-bottom: 1px solid #000;
  color: var(--blue);
}

.image .box-container {
  display: flex;
  justify-content: space-around;
}
.image .box-container img {
  margin-right: 5px;
  width: 200px;
  height: 300px;
}
.header-title {
  background-color: #2496d8;
}

Photo

import React, { Component } from 'react'
import axios from 'axios'
// import { Button } from 'antd'
import './Photo.css'

export default class Photo extends Component {
  state = {
    imgList: [],
    flag: false
  }
  componentDidMount() {
    axios.get('http://localhost:8080/api/photo').then(
      (req) => {
        this.setState({
          imgList: req.data.data
        })
      },
      (err) => {
        console.log(err.message)
      }
    )
  }
  componentWillUnmount() {
    this.setState = () => {
      return
    }
  }

  // getList = () => {
  //   axios.get('http://localhost:8080/api/photo').then(
  //     (req) => {
  //       this.setState({
  //         imgList: req.data.data
  //       })
  //     },
  //     (err) => {
  //       console.log(err.message)
  //     }
  //   )
  // }
  // hideList = () => {
  //   this.setState({
  //     imgList: []
  //   })
  // }
  render() {
    const { imgList } = this.state
    return (
      <div>
        {/* <Button type="primary" danger onClick={this.getList}>
          display
        </Button>
        &nbsp;&nbsp;
        <Button type="primary" danger onClick={this.hideList}>
          hide
        </Button> */}
        <div className="row">
          {imgList.map((item) => {
            return (
              <div className="col-sm-6 col-md-4" key={item.id}>
                <div className="thumbnail">
                  <img src={item.avator} alt="..." />
                  <div className="caption">
                    <h3>Thumbnail label</h3>
                    <p>...</p>
                    <p>
                      {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
                      <a href="#" className="btn btn-primary" role="button">
                        Button
                      </a>{' '}
                      {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
                      <a href="#" className="btn btn-default" role="button">
                        Button
                      </a>
                    </p>
                  </div>
                </div>
              </div>
            )
          })}
        </div>
      </div>
    )
  }
}

.test {
  width: 400px;
  grid-template: 400px;
}

其它文件

index.html
引入样式

<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet" />
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.css" rel="stylesheet" />

App.jsx

import React, { Component } from 'react'
import { Route, Redirect } from 'react-router-dom'

import './App.css'
import Home from './components/Home/Home'
import Login from './components/Login/Login'

export default class App extends Component {
  render() {
    return (
      <div>
        {/* exact精准匹配 */}
        <Route exact path="/" component={Login} />

        <Route path="/home" component={Home} />
        <Redirect to="/" />
      </div>
    )
  }
}


index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
)

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()

接口文件
react_demo.js

const express = require('express')
const cors = require('cors')
const app = express()

app.use(cors())

app.use((req, res, next) => {
  console.log('请求中...')
  next()
})

app.get('/api/photo', (req, res) => {
  const users = {
    msg: 'ok',
    status: 200,
    data: [
      { id: 1, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcd996d7a.jpg' },
      { id: 2, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcda5da91.jpg' },
      { id: 3, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcdb1f5d2.jpg' },
      { id: 4, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcdbd5403.jpg' },
      { id: 5, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcdc9afb0.jpg' },
      { id: 6, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcdd55abc.jpg' },
      { id: 7, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcde159b8.jpg' },
      { id: 8, avator: 'http://pic1.win4000.com/wallpaper/2019-07-30/5d3ffcded4110.jpg' },
      { id: 9, avator: 'http://pic1.win4000.com/wallpaper/2019-08-02/5d44009e69276.jpg' }
    ]
  }
  res.send(users)
})

app.listen(8080, (err) => {
  // console.log(err)
  if (!err) {
    console.log('The server is running at http://localhost:8080/api/photo')
  }
})

运行项目

  1. nodemon .\react_demo.js 启动接口
  2. npm start 启动项目

效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

react+node.js+antd+bootstrap实现个人空间案例

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值