1、WebSocket的Demo
1.1、App.js
// node中是通过net核心模块来开发websocket
// 市面上有很多基于net模块开发的第三方模块,开发websocket特别的简单。
const WebSocket = require('ws')
// 创建webSocket服务
const server = new WebSocket.Server({ port: 8080 })
// 启动websocket服务
// 只要浏览器端连接了websocket,connention事件就会触发
server.on('connection', client => {
console.log('客户端连接上了')
client.send('欢迎光临')
// 每当客户端给我们发送消息的时候,message事件就会触发
client.on('message', data => {
let msg = ''
switch (data) {
case '你好':
msg = '废话,当然好'
break
case '你是男的':
msg = '不,我是小姐姐'
break
case '约吗':
msg = '丑拒'
break
default:
msg = '你说啥呀'
}
client.send(msg)
})
})
1.2、index.html
Document
2、好客租房移动web项目
2.1、components
--- home
(1)chat
--Chat.css
.chat-container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.chat-title {
position: absolute;
top: 0;
width: 100%;
height: 40px;
line-height: 40px;
background-color: #d4e0ee;
text-align: center;
font-size: 20px;
z-index: 2;
}
.chat-list {
box-sizing: border-box;
height: 100%;
width: 100%;
padding-top: 40px;
overflow-y: scroll;
}
.chat-list ul {
margin: 0;
padding: 0;
}
.chat-list li {
position: relative;
list-style: none;
background-color: #f3f3f3;
padding: 0 15px;
margin-top: 2px;
height: 70px;
line-height: 70px;
overflow: hidden;
}
.chat-list li img {
width: 60px;
height: 60px;
margin-left: 5px;
margin-top: 5px;
}
.chat-list li .name {
position: absolute;
left: 90px;
top: -15px;
}
.chat-list li .info {
position: absolute;
left: 90px;
top: 15px;
}
.chat-list li .time {
position: absolute;
right: 20px;
top: -15px;
}
.chat-window {
position: fixed;
top: 0;
width: 100%;
height: 100%;
background-color: #f0f0f0;
z-index: 99999;
}
.chat-list::-webkit-scrollbar {
width: 3px;
}
.chat-list::-webkit-scrollbar-thumb {
background-color: #cbdaea;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
min-height: 2rem;
background-clip: padding-box;
border-radius: 5px;
}
.chat-list::-webkit-scrollbar-track {
background-color: #fff;
}
.chat-list::-webkit-scrollbar-track-piece {
height: 30px;
}
--Chat.jsx
import React from 'react'
import './Chat.css'
import moment from 'moment'
import ChatWindow from './ChatWindow'
class Chat extends React.Component {
constructor(props) {
super(props)
this.state = {
list: [],
isShow: false,
chatInfo: {}
}
}
async componentDidMount() {
let res = await this.axios.post('chats/list')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
list: data.list
})
}
}
toChat = item => {
// console.log(item)
this.setState({
isShow: true,
chatInfo: {
from_user: item.from_user,
to_user: item.to_user,
avatar: item.avatar,
username: item.username
}
})
}
closeWindow = () => {
this.setState({
isShow: false
})
}
render() {
return (
{this.state.isShow && (
)}
聊天
{this.state.list.map(item => (
-
{item.username}
{item.chat_msg}
{moment(item.ctime).format('YYYY-MM-DD HH:mm:ss')}
))}
)
}
}
export default Chat
--ChatWindow.css
.chat-window-title {
height: 40px;
line-height: 40px;
font-size: 20px;
padding-top: 3px;
background-color: #d4e0ee;
position: absolute;
width: 100%;
text-align: center;
box-sizing: border-box;
}
.chat-window-content {
position: absolute;
top: 40px;
bottom: 126px;
width: 100%;
overflow-y: auto;
}
.chat-window-content ul {
margin: 0;
padding: 0;
list-style: none;
}
.chat-window-content ul li {
min-height: 60px;
clear: both;
}
.chat-window-content ul li img {
width: 50px;
height: 50px;
padding-top: 5px;
padding-left: 5px;
vertical-align: top;
float: left;
}
.chat-window-content ul li span {
display: inline-block;
font-size: 20px;
background-color: #fff;
padding: 5px 10px;
margin: 10px 60px 0 10px;
width: 65%;
min-height: 50px;
float: left;
box-sizing: border-box;
word-break: break-all;
word-wrap: break-word;
}
.chat-window-content ul li.chat-info-right img {
float: right;
padding-right: 5px;
}
.chat-window-content ul li.chat-info-right span {
float: right;
margin: 10px 6px 0 60px;
background-color: lightgreen;
}
.chat-window-input {
width: 100%;
position: absolute;
bottom: 0;
background-color: #21b97a;
min-height: 60px;
text-align: right;
padding: 5px;
}
.chat-ret-btn {
position: absolute;
left: 0;
top: 8px;
}
.ui.form textarea {
margin-bottom: 5px;
}
--ChatWindow.jsx
import React from 'react'
import { Icon, Form, Button, TextArea } from 'semantic-ui-react'
import './ChatWindow.css'
import handle from './wsmain'
class ChatWindow extends React.Component {
constructor(props) {
super(props)
this.state = {
list: [],
client: '',
msgContent: '',
fromAvatar: ''
}
}
getChatList = async () => {
// 发送ajax请求,获取聊天数据
let res = await this.axios.post('chats/info', {
from_user: this.props.chatInfo.from_user,
to_user: this.props.chatInfo.to_user
})
let { meta, data } = res
if (meta.status === 200) {
this.setState({
list: data.list
})
}
}
// 聊天窗口组件挂载的时候,获取聊天列表信息
async componentDidMount() {
// 获取当前用户的头像
let res = await this.axios.post('my/info', {
user_id: localStorage.getItem('uid')
})
let { meta, data } = res
if (meta.status === 200) {
// 通过websocket连接服务器,得到client对象
let currentUser = localStorage.getItem('uid') - 0
// 参数1: 连接聊天服务器的id
// 参数2: 回调函数,服务器每次给发送的消息,都在data中
// 返回值:client对象
let client = handle(currentUser, data => {
// 该回调函数用来处理服务器返回的消息(其实就是对方发送消息)
// 其实就是接收对方返回的消息
let newData = JSON.parse(data.content)
let newList = [...this.state.list, newData]
this.setState({
list: newList
})
})
this.setState({
client: client,
avatar: data.avatar
})
}
this.getChatList()
}
// 给服务器发送数据
sendMsg = () => {
// 给服务器发送的数据包含:
// from_user: 从谁发
// to_user: 给谁发
// this.state.msgContent: 发送的内容
let pdata = {
id: new Date().getTime(),
from_user: this.props.chatInfo.from_user,
to_user: this.props.chatInfo.to_user,
chat_msg: this.state.msgContent,
avatar: this.state.avatar
}
// 把消息发送出去
this.state.client.emitEvent('msg_text_send', JSON.stringify(pdata))
// 重新渲染聊天列表
let newList = [...this.state.list, pdata]
this.setState({
list: newList,
msgContent: ''
})
}
handleChange = e => {
this.setState({
msgContent: e.target.value
})
}
render() {
let currentUser = localStorage.getItem('uid') - 0
return (
{this.props.chatInfo.username}
{this.state.list.map(item => (
// 通过currentUser与from_user去比较
-
{/* 这个头像显示是错误 */}
{item.chat_msg}
))}
)
}
}
export default ChatWindow
--DataPacket.js
class DataPacket {
constructor(message) {
if(typeof(message) === "string"){
try {
this._data = JSON.parse(message);
} catch (error) {
this._data = undefined;
}
} else if(typeof(message) === "object"){
this._data = message;
}
}
get data() {
return this._data
}
get content() {
return this._data["content"]
}
get type() {
return this._data["type"];
}
set type(type) {
this._data["type"] = type;
}
get rawMessage() {
return JSON.stringify(this._data);
}
}
export default DataPacket
--DMClient.js
import DataPacket from './DataPacket.js'
import IMEvent from './IMEvent.js'
/**
* 通讯客户端
*/
class IMClient {
constructor(url) {
this._url = url
this._autoConnect = true
this._handlers = {}
this._DataPacketQueue = []
this._isOpened = false
this.addEventListener(IMEvent.CONNECTED, () => {
this.serverOnConnected()
})
// this.addEventListener(IMEvent.CONNECTED, () => {
// this.clearMsgQueue();
// })
this.addEventListener(IMEvent.DISCONNECTED, () => {
this.serverOnDisconnected()
})
}
/**
* 底层通讯函数回调
*/
// 连接
connect() {
if (!this._socket) {
this._socket = new WebSocket(this._url)
this._socket.onmessage = evt => {
this.onMessage(evt.data)
}
this._socket.onopen = ws => {
this.onOpen(ws)
}
this._socket.onclose = ws => {
this.onClose(ws)
}
this._socket.onerror = ws => {
this.onError(ws)
}
}
}
// 消息接收
onMessage(message) {
// console.log(message, 11)
if (typeof message === 'string') {
// message就是服务器返回的消息
this.dispatchMessage(message)
}
}
// 打开回调
onOpen() {
this.emitEvent(IMEvent.CONNECTED)
}
// 关闭回调
onClose() {
this._socket = undefined
this.emitEvent(IMEvent.DISCONNECTED)
if (this.autoReconnect) {
setTimeout(() => {
this.connect()
}, 5000)
}
}
// 出现错误回调
onError() {
this._socket = undefined
if (this.autoReconnect) {
setTimeout(() => {
this.connect()
}, 5000)
}
}
// 向服务器发送数据包
sendDataPacket(dataPacket) {
if (this._isOpened) {
this._socket.send(dataPacket.rawMessage)
} else {
this._DataPacketQueue.push(dataPacket)
}
}
/**
* 事件处理
*/
// 当连接服务器时处理
serverOnConnected() {
this._isOpened = true
}
// 当断开连接服务器时处理
serverOnDisconnected() {
this._isOpened = false
}
// 清空消息队列
clearMsgQueue() {
if (this._DataPacketQueue.length > 0) {
this._DataPacketQueue.forEach(dataPacket => {
this.sendDataPacket(dataPacket)
})
this._socketMsgQueue = []
}
}
/**
* 发射事件
* @param {string} type 事件类型
* @param {参数列表} args 传递事件参数
*/
emitEvent(type, ...args) {
if (this._handlers[type] && this._handlers[type].length > 0) {
let handlers = this._handlers[type]
for (var i = 0; i < handlers.length; i++) {
handlers[i].call(this, ...args)
}
}
}
/**
* 添加事件监听
*
* @param {string} type 事件类型
* @param {Function} callback 事件处理函数
*/
addEventListener(type, handler) {
if (!this._handlers[type]) {
this._handlers[type] = []
}
this._handlers[type].push(handler)
}
/**
* 移除事件
*
* @param {string} type 事件类型
* @param {*} callback
*/
removeEventListener(type, handler) {
if (this._handlers[type] && this._handlers[type].length > 0) {
let handlers = this._handlers[type]
for (var i = handlers.length - 1; i >= 0; i--) {
if (handler === handlers[i]) {
handlers.splice(i, 1)
}
}
}
}
/**
* 消息封装成数据包
*/
dispatchMessage(message) {
let dataPacket = new DataPacket(message)
if (dataPacket.data) {
this.dispatchDataPacket(dataPacket)
}
}
/**
* 处理数据包
*/
dispatchDataPacket(dataPacket) {
this.emitEvent(dataPacket.type, dataPacket)
}
}
export default IMClient
--IMEvent.js
export default {
ERROR: "error",
CONNECTED: "connected",
DISCONNECTED: "disconnected",
MSG_DISCONNECTED: "msg_disconnected",
MSG_TEXT_SEND: "msg_text_send",
MSG_TEXT_REC: "msg_text_rec",
USER_REG: "user_reg",
}
--wsmain.js
import IMEvent from './IMEvent.js'
import IMClient from './IMClient.js'
import DataPacket from './DataPacket.js'
// import config from '../../common.js';
const handle = (currentUser, handleMsg) => {
const client = new IMClient('ws://127.0.0.1:8087');
// 发送消息
client.addEventListener(IMEvent.MSG_TEXT_SEND, data => {
let dataPacket = new DataPacket({
type: IMEvent.MSG_TEXT_SEND,
content: data
})
client.sendDataPacket(dataPacket)
})
// 接收消息
client.addEventListener(IMEvent.MSG_TEXT_REC, data => {
handleMsg(data)
})
// 注册身份
client.addEventListener(IMEvent.CONNECTED, () => {
let dataPacket = new DataPacket({
type: IMEvent.USER_REG,
content: currentUser
})
client.sendDataPacket(dataPacket)
console.log('success')
})
client.connect();
return client;
}
export default handle;
(2)demo
--Demo.jsx
import React from 'react'
// 1. 导入相关的依赖包, 会自动导入依赖的核心样式
import Tloader from 'react-touch-loader'
import './style.less'
class Demo extends React.Component {
constructor() {
super()
this.state = {
hasMore: false,
initializing: 1,
total: 0 // 总条数
}
}
refresh = (resolve, reject) => {
setTimeout(() => {
// 获取第一页的数据
this.setState({
total: 10,
hasMore: true
})
resolve()
}, 1000)
}
loadMore = (resolve, reject) => {
setTimeout(() => {
let newTotal = this.state.total + 10
this.setState({
total: newTotal,
hasMore: newTotal > 0 && newTotal < 50
})
resolve()
}, 1000)
}
componentDidMount() {
// 后台加载数据
setTimeout(() => {
this.setState({
total: 10,
initializing: 2,
hasMore: true
})
}, 1000)
}
render() {
let { hasMore, initializing, total } = this.state
let data = []
for (var i = 0; i < total; i++) {
data.push(
{i}
)
}
return (
{/* onRefresh: 下拉刷新功能 */}
{data}
)
}
}
export default Demo
--Demo-备份.jsx
import React from 'react'
import './style.less'
import Tloader from 'react-touch-loader'
class Demo extends React.Component {
// 构造函数
constructor() {
super()
this.state = {
// 能够下拉刷新(能)
canRefreshResolve: 1,
// 总的数量
listLen: 0,
// 是否有更多的数据
hasMore: 0,
// 0: 不显示进度条 1:显示进度条 2.结束进度条
initializing: 1,
// 刷新的时间
refreshedAt: Date.now()
}
}
// 组件刚挂载执行的代码
componentDidMount() {
// 发送ajax请求,获取到数据
setTimeout(() => {
this.setState({
// 获取到了9条数据
listLen: 9,
// hasMore: true, 有更多,显示加载更多
hasMore: 1,
// 结束进度条
initializing: 2 // initialized
})
}, 2000)
}
// 下拉刷新的代码
refresh = (resolve, reject) => {
setTimeout(() => {
const { canRefreshResolve } = this.state
if (!canRefreshResolve) reject()
else {
// 重置了状态
this.setState({
listLen: 9,
hasMore: 1,
refreshedAt: Date.now()
})
resolve()
}
}, 2000)
}
// 加载更多的代码
loadMore = resolve => {
setTimeout(() => {
const { listLen } = this.state
const l = listLen + 9
this.setState({
listLen: l,
hasMore: l > 0 && l < 50
})
resolve()
}, 2000)
}
render() {
const { listLen, hasMore, initializing, refreshedAt } = this.state
const list = []
if (listLen) {
for (let i = 0; i < listLen; i++) {
list.push(
{i}
)
}
}
return (
{list}
)
}
}
export default Demo
--style.less
html,
body {
margin: 0;
padding: 0;
}
body {
font-family: 'Helvetica Neue', Helvetica, 'STHeitiSC-Light',
'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
text-align: center;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
:focus {
outline: none;
}
:not(input):not(textarea):not([contenteditable]) {
-webkit-touch-callout: none;
user-select: none;
}
html {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.view {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
overflow: hidden;
background-color: #efeff4;
display: flex;
flex-direction: column;
// header, footer
h1,
.footer {
margin: 0;
background-color: #333;
color: #eee;
font-size: 1em;
line-height: 3;
a {
color: inherit;
margin: 0 1em;
}
}
.main {
flex: 1;
overflow-x: hidden;
overflow-y: scroll;
-webkit-overflow-scrolling: touch; // enhance ios scrolling
ul {
list-style: none;
margin: 0;
padding: 0;
li {
background-color: #fff;
border-bottom: 1px solid #ccc;
p {
margin: 0;
line-height: 3em;
}
}
}
}
}
(3)info
--Info.css
.find-container {
position: absolute;
width: 100%;
height: 100%;
background-color: lightgreen;
overflow: hidden;
}
.find-topbar {
position: absolute;
width: 100%;
height: 40px;
line-height: 40px;
background-color: #b5efff;
text-align: center;
font-size: 20px;
z-index: 999;
}
.find-content {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 40px;
overflow-y: auto;
}
.ui.bottom.attached.segment.active.tab {
top: 80px;
bottom: 0px;
position: absolute;
overflow-y: auto;
}
.ui .view {
background-color: #fff;
}
/* 消息的样式 */
.ui.items > .item > .content > .header.info-title {
text-align: left;
height: auto;
filter: none;
-webkit-filter: blur(0px);
-moz-filter: blur(0px);
-ms-filter: blur(0px);
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='0');
}
.ui.items > .item .meta {
text-align: left;
}
/* 问答相关的样式 */
.info-ask-list {
margin: 0;
padding: 0;
list-style: none;
text-align: left;
}
.info-ask-list li {
position: relative;
margin: 5px 10px;
padding: 10px 0;
}
.info-ask-list li .title {
color: #000;
font-weight: bold;
padding: 5px;
font-size: 16px;
}
.info-ask-list li .title .cate {
box-sizing: border-box;
background-color: #c6f3ff;
margin-right: 5px;
padding: 0px 3px;
display: inline-block;
font-size: 14px;
}
.info-ask-list li .user {
font-size: 12px;
color: gray;
padding-left: 5px;
}
.info-ask-list li .info {
font-size: 10px;
color: gray;
padding-left: 5px;
}
.info-ask-list li .tag {
padding-left: 5px;
margin-top: 10px;
overflow: hidden;
}
.info-ask-list li .tag span {
font-size: 12px;
color: #000;
border: 1px solid gray;
margin-right: 5px;
padding: 2px 5px;
}
.info-ask-list li .tag span:last-child {
float: right;
border: 0px;
}
.info-ask-btn {
padding: 10px 15px;
}
--Info.jsx
import React from 'react'
import './Info.css'
import { Tab, Item, Icon } from 'semantic-ui-react'
import Tloader from 'react-touch-loader'
class Info extends React.Component {
render() {
const panes = [
{
menuItem: '资讯',
render: () => (
)
},
{
menuItem: '头条',
render: () => (
)
},
{
menuItem: '问答',
render: () => (
)
}
]
return (
资讯
)
}
}
export default Info
function M1() {
return
}
function M2() {
return
}
function M3() {
return
}
// 定义Message组件
function Message({ data }) {
return (
{data.map(item => (
-
{item.info_title}
$1200
1 Month
))}
)
}
// 定义问答组件
function AskAnswer({ data }) {
return (
{data.map(item => (
-
思维
你好你好你好你好你好你好你好你好你好你好你好你好你好你好
张三的回答
你好你好你好你好你好你好你好你好你好你好你好你好你好你好
你好X
你好X
你好X
123个回答
))}
)
}
// 定义touch-loader组件
class Loader extends React.Component {
constructor() {
super()
this.state = {
hasMore: false,
initializing: 1,
pagenum: 0,
pagesize: 2,
list: [],
total: 0
}
}
// 加载数据
loadData = () => {
// 发送ajax请求,动态加载数据
// console.log(this.props.type)
return this.axios
.post('infos/list', {
type: this.props.type,
pagenum: this.state.pagenum,
pagesize: this.state.pagesize
})
.then(res => {
let { meta, data } = res
if (meta.status === 200) {
return data.list
}
})
}
// 需要在组件渲染的时候,动态的加载数据
async componentDidMount() {
let data = await this.loadData()
let newNum = this.state.pagenum + this.state.pagesize
this.setState({
list: data.data,
initializing: 2,
toal: data.total,
pagenum: newNum,
hasMore: newNum < data.total
})
}
refresh = async (resolve, reject) => {
// 重置初始的条数
// react的setState是异步的,通过setState修改react内部的数据,不是立即更新的
// 如果就想获取立即更新的数据
this.setState({
pagenum: 0
})
setTimeout(async () => {
let data = await this.loadData()
let newNum = this.state.pagenum + this.state.pagesize
this.setState({
list: data.data,
pagenum: newNum,
hasMore: newNum < data.total
})
resolve()
}, 0)
}
loadMore = async (resolve, reject) => {
let data = await this.loadData()
let newNum = this.state.pagenum + this.state.pagesize
let newList = [...this.state.list, ...data.data]
this.setState({
list: newList,
initializing: 2,
toal: data.total,
pagenum: newNum,
hasMore: newNum < data.total
})
resolve()
}
render() {
let { hasMore, initializing, list } = this.state
let { type } = this.props
return (
{type === '3' ? : }
)
}
}
(4)main
--Main.css
.main {
width: 100%;
height: 100%;
position: relative;
}
.search {
height: 40px;
line-height: 40px;
position: absolute;
width: 100%;
top: 0;
z-index: 999;
}
.content {
width: 100%;
height: 100%;
padding-top: 40px;
/* 给content添加了垂直的滚动条 */
overflow-y: auto;
}
/* 滚动条样式 */
.content::-webkit-scrollbar {
width: 3px;
}
.content::-webkit-scrollbar-thumb {
background-color: #cbdaea;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
min-height: 2rem;
background-clip: padding-box;
border-radius: 5px;
}
.content::-webkit-scrollbar-track {
background-color: #fff;
}
/* 菜单的样式 */
.home-menu-item {
border-radius: 31px;
height: 62px;
line-height: 62px;
color: #fff;
}
.ui.grid > .row > .column:nth-child(1) .home-menu-item {
background-color: #0ac250;
}
.ui.grid > .row > .column:nth-child(2) .home-menu-item {
background-color: #ff611a;
}
.ui.grid > .row > .column:nth-child(3) .home-menu-item {
background-color: #f6b906;
}
.ui.grid > .row > .column:nth-child(4) .home-menu-item {
background-color: #4f99e4;
}
.ui.grid > .row > .column:nth-child(5) .home-menu-item {
background-color: #fd8701;
}
.ui.grid > .row > .column:nth-child(6) .home-menu-item {
background-color: #23acf2;
}
.ui.grid > .row > .column:nth-child(7) .home-menu-item {
background-color: #eb1e03;
}
.ui.grid > .row > .column:nth-child(8) .home-menu-item {
background-color: #7ccc39;
}
.ui.menu > .row > .column {
text-align: center;
margin-top: 10px;
}
/* 好客咨询样式 */
.home-msg {
margin: 25px 0 15px 0;
position: relative;
}
.home-msg .ui.unstackable.items > .item.home-msg-img > .image,
.home-msg .ui.unstackable.items > .item.home-msg-img > .image > img {
width: 50px !important;
height: 50px !important;
margin-left: 7px;
margin-top: 2px;
}
.ui.items > .item > .content > .header {
display: block;
height: 25px;
margin-top: 5px;
font-weight: 600;
}
.ui.items > .item > .content > .header span:first-child {
color: orange;
margin-right: 10px;
overflow: hidden;
display: inline-block;
}
.ui.items > .item > .content > .header span:last-child {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: baseline;
width: 65%;
}
.home-msg .home-msg-more {
position: absolute;
right: 5px;
top: 12px;
}
/* 好客问答样式 */
.home-ask-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #cecece;
}
.home-ask ul {
list-style: none;
margin: 0;
padding: 0;
}
.home-ask ul li {
height: 70px;
line-height: 70px;
}
.home-ask ul li div {
height: 35px;
line-height: 35px;
}
.home-ask ul li div:first-child {
margin-left: 10px;
font-size: 18px;
}
.home-ask ul li div:last-child {
margin-left: 30px;
position: relative;
}
.home-ask ul li div:last-child div {
position: absolute;
font-size: 14px;
right: 15px;
top: 0;
}
/* 房屋样式 */
.home-hire-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #cecece;
}
.ui.items > .item > .image {
margin-left: 10px;
margin-top: 6px;
}
.ui.items > .item > .content > .description:last-child {
color: orange;
}
--Main.jsx
import React from 'react'
import 'react-image-gallery/styles/css/image-gallery.css'
import './Main.css'
import {
Input,
Grid,
Icon,
Item,
Button,
Dimmer,
Loader
} from 'semantic-ui-react'
// 导入轮播图组件
import ImageGallery from 'react-image-gallery'
class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
imgList: [],
menuList: [],
infoList: [],
faqList: [],
houseList: [],
loading: true
}
}
/* // 获取轮播图数据
getImgList = async () => {
let res = await this.axios.post('homes/swipe')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
imgList: data.list
})
}
}
// 获取菜单数据
getMenuList = async () => {
let res = await this.axios.post('homes/menu')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
menuList: data.list
})
}
}
// 获取咨询数据
getInfoList = async () => {
let res = await this.axios.post('homes/info')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
infoList: data.list
})
}
}
// 获取咨询数据
getFaqList = async () => {
let res = await this.axios.post('homes/faq')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
faqList: data.list
})
}
}
// 获取房屋数据
getHouseList = async () => {
let res = await this.axios.post('homes/house')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
houseList: data.list
})
}
} */
doRequest = (url, dataName) => {
return this.axios.post(url).then(res => {
let { meta, data } = res
if (meta.status === 200) {
this.setState({
[dataName]: data.list
})
}
})
}
// 页面加载完成,需要发送ajax请求,获取轮播图数据
async componentDidMount() {
// Promise.all()
await Promise.all([
this.doRequest('homes/swipe', 'imgList'),
this.doRequest('homes/menu', 'menuList'),
this.doRequest('homes/info', 'infoList'),
this.doRequest('homes/faq', 'faqList'),
this.doRequest('homes/house', 'houseList')
])
this.setState({
loading: false
})
}
render() {
return (
Loading
{/* 轮播图 */}
{/* 菜单部分 */}
{/* 好客咨询部分 */}
{/* 好客问答部分 */}
{/* 房屋信息部分 */}
{/* */}
)
}
}
export default Main
// 定义菜单组件,渲染菜单数据
// 参数的解构
function MenuList({ data }) {
return (
{data.map(item => (
{item.menu_name}
))}
)
}
// 好客资讯组件
function InfoList({ data }) {
return (
-
{data.map(item => (
限购 ●
{item.info_title}
))}
)
}
// 好客问答组件
function FaqList({ data }) {
return (
好客问答
{data.map(item => (
-
{item.question_name}
{item.question_tag.split(',').map(tag => (
))}
{item.atime} ● {' '}
{item.qnum}
))}
)
}
// 房屋组件
function HouseList({ data }) {
let newHouse = []
let oldHouse = []
let hireHouse = []
data.forEach(item => {
let temp = (
-
{item.home_name}
{item.home_desc}
{item.home_tags.split(',').map(tag => (
))}
{item.home_price}
)
if (item.home_type === 1) {
newHouse.push(temp)
} else if (item.home_type === 2) {
oldHouse.push(temp)
} else {
hireHouse.push(temp)
}
})
// console.log(data)
return (
最新开盘
{newHouse}
二手精选
{oldHouse}
组一个家
{hireHouse}
)
}
(5)my
--My.css
.my-container {
overflow-y: auto;
height: 100%;
}
.my-title {
min-height: 300px;
position: relative;
}
.my-title img {
width: 100%;
}
.my-title .info {
position: absolute;
background: #fff;
width: 60%;
height: 65%;
bottom: 0px;
left: 50%;
transform: translateX(-50%);
box-shadow: 2px 2px 3px #787d82;
margin: 50px auto 0;
padding: 0 20px;
padding-top: 0;
text-align: center;
}
.my-title .myicon {
position: relative;
transform: translateY(-50%);
border-radius: 50%;
max-width: 90px;
max-height: 90px;
border: solid 5px #f5f5f5;
display: inline-block;
box-shadow: 0px 2px 2px #bdbdbd;
}
.my-title .myicon img {
border-radius: 50%;
}
.my-title .name {
margin-top: -30px;
margin-bottom: 10px;
}
.my-title .edit {
color: #787d82;
margin-top: 20px;
}
.my-menu.ui.grid > .row > .column {
margin-top: 20px;
margin-bottom: 10px;
color: #808589;
}
.my-menu.ui.grid > .row > .column div {
margin-top: 5px;
}
.my-ad {
text-align: center;
margin-top: 10px;
}
.my-ad img {
width: 85%;
}
.my-container::-webkit-scrollbar {
width: 3px;
}
.my-container::-webkit-scrollbar-thumb {
background-color: #cbdaea;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
min-height: 2rem;
background-clip: padding-box;
border-radius: 5px;
}
.my-container::-webkit-scrollbar-track {
background-color: #fff;
}
.my-container::-webkit-scrollbar-track-piece {
height: 30px;
}
.ui.modal > .actions {
text-align: center;
}
.ui.modal > .content {
text-align: center;
}
.ui.modal > .content .avatar-zoom {
color: #000;
margin-right: 10px;
display: inline-block;
vertical-align: text-bottom;
padding-bottom: 2px;
font-size: 18px;
}
--My.jsx
import React from 'react'
import './My.css'
import { Button, Grid, Icon, Modal } from 'semantic-ui-react'
import AvatarEditor from 'react-avatar-editor'
class My extends React.Component {
constructor(props) {
super(props)
this.state = {
avatar: '',
username: '',
isShowSelect: false, // 是否显示选择图片的模态框
isShowCrop: false, // 是否显示裁切图片的模态框
avatarFile: ''
}
}
// 显示选择图片的弹窗
showSelect = () => {
this.setState({
isShowSelect: true
})
}
// 显示裁切图片的弹窗
showCrop = file => {
// 1. 隐藏选择图片的弹窗
// 2. 把上传到的图片保存起来
//3. 显示裁切图片的弹窗
this.setState({
isShowSelect: false,
avatarFile: file,
isShowCrop: true
})
}
// 关闭裁切模态框
closeCrop = avatar => {
this.setState({
isShowCrop: false,
avatar: avatar
})
}
// 当组件挂载好了,发送ajax请求,获取到个人信息
// 接口需要参数: user_id
async componentDidMount() {
let res = await this.axios.post('my/info', {
user_id: localStorage.getItem('uid')
})
let { meta, data } = res
if (meta.status === 200) {
this.setState({
avatar: data.avatar,
username: data.username
})
}
}
render() {
return (
{/* 给头像注册点击事件,显示选择头像的弹窗 */}
{this.state.username}
编辑个人资料
看房记录
我的订单
我的收藏
个人资料
身份认证
联系我们
)
}
}
export default My
// 选择头像的弹窗组件
class SelectAvatar extends React.Component {
constructor(props) {
super(props)
this.fileRef = React.createRef()
}
submitFile = () => {
// 获取到上传的文件, 传递给父组件
let file = this.fileRef.current.files[0]
this.props.showCrop(file)
}
render() {
let { open } = this.props
return (
{/* 弹窗组件 */}
选择图片
)
}
}
// 裁切头像的弹窗组件
class CropAvatar extends React.Component {
constructor() {
super()
this.state = {
scale: 1
}
}
handleScale = e => {
this.setState({
scale: e.target.value - 0
})
}
// 设置编辑的ref
setEditorRef = editor => (this.editor = editor)
submitAvatar = async () => {
// 发送ajax请求,上传裁切的图片
//1. 如果获取到裁切后的图片
let avatar = this.editor.getImageScaledToCanvas().toDataURL()
// 2.发送ajax请求,修改图片了
let res = await this.axios.post('my/avatar', {
avatar: avatar
})
let { meta } = res
if (meta.status === 200) {
// 修改关闭模态框, 把裁切后的图片传给父组件,让父组件把图片修改
this.props.closeCrop(avatar)
}
}
render() {
let { open, avatarFile } = this.props
return (
上传头像
缩放:
)
}
}
2.2、Chat.jsx
import React from 'react'
class Chat extends React.Component {
render() {
return 这是Chat组件
}
}
export default Chat
2.3、Info.jsx
import React from 'react'
class Info extends React.Component {
render() {
return 这是Info组件
}
}
export default Info
2.4、Main.css
.main {
width: 100%;
height: 100%;
position: relative;
}
.search {
height: 40px;
line-height: 40px;
position: absolute;
width: 100%;
top: 0;
}
.content {
width: 100%;
height: 100%;
padding-top: 40px;
/* 给content添加了垂直的滚动条 */
overflow-y: auto;
}
/* 滚动条样式 */
.content::-webkit-scrollbar {
width: 3px;
}
.content::-webkit-scrollbar-thumb {
background-color: #cbdaea;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
min-height: 2rem;
background-clip: padding-box;
border-radius: 5px;
}
.content::-webkit-scrollbar-track {
background-color: #fff;
}
/* 菜单的样式 */
.home-menu-item {
border-radius: 31px;
height: 62px;
line-height: 62px;
color: #fff;
}
.ui.grid > .row > .column:nth-child(1) .home-menu-item {
background-color: #0ac250;
}
.ui.grid > .row > .column:nth-child(2) .home-menu-item {
background-color: #ff611a;
}
.ui.grid > .row > .column:nth-child(3) .home-menu-item {
background-color: #f6b906;
}
.ui.grid > .row > .column:nth-child(4) .home-menu-item {
background-color: #4f99e4;
}
.ui.grid > .row > .column:nth-child(5) .home-menu-item {
background-color: #fd8701;
}
.ui.grid > .row > .column:nth-child(6) .home-menu-item {
background-color: #23acf2;
}
.ui.grid > .row > .column:nth-child(7) .home-menu-item {
background-color: #eb1e03;
}
.ui.grid > .row > .column:nth-child(8) .home-menu-item {
background-color: #7ccc39;
}
.ui.menu > .row > .column {
text-align: center;
margin-top: 10px;
}
/* 好客咨询样式 */
.home-msg {
margin: 25px 0 15px 0;
position: relative;
}
.home-msg .ui.unstackable.items > .item.home-msg-img > .image,
.home-msg .ui.unstackable.items > .item.home-msg-img > .image > img {
width: 50px !important;
height: 50px !important;
margin-left: 7px;
margin-top: 2px;
}
.ui.items > .item > .content > .header {
display: block;
height: 25px;
margin-top: 5px;
font-weight: 600;
}
.ui.items > .item > .content > .header span:first-child {
color: orange;
margin-right: 10px;
overflow: hidden;
display: inline-block;
}
.ui.items > .item > .content > .header span:last-child {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: baseline;
width: 65%;
}
.home-msg .home-msg-more {
position: absolute;
right: 5px;
top: 12px;
}
/* 好客问答样式 */
.home-ask-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #cecece;
}
.home-ask ul {
list-style: none;
margin: 0;
padding: 0;
}
.home-ask ul li {
height: 70px;
line-height: 70px;
}
.home-ask ul li div {
height: 35px;
line-height: 35px;
}
.home-ask ul li div:first-child {
margin-left: 10px;
font-size: 18px;
}
.home-ask ul li div:last-child {
margin-left: 30px;
position: relative;
}
.home-ask ul li div:last-child div {
position: absolute;
font-size: 14px;
right: 15px;
top: 0;
}
/* 房屋样式 */
.home-hire-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #cecece;
}
.ui.items > .item > .image {
margin-left: 10px;
margin-top: 6px;
}
.ui.items > .item > .content > .description:last-child {
color: orange;
}
2.5、Main.jsx
import React from 'react'
import 'react-image-gallery/styles/css/image-gallery.css'
import './Main.css'
import {
Input,
Grid,
Icon,
Item,
Button,
Dimmer,
Loader
} from 'semantic-ui-react'
// 导入轮播图组件
import ImageGallery from 'react-image-gallery'
class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
imgList: [],
menuList: [],
infoList: [],
faqList: [],
houseList: [],
loading: true
}
}
/* // 获取轮播图数据
getImgList = async () => {
let res = await this.axios.post('homes/swipe')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
imgList: data.list
})
}
}
// 获取菜单数据
getMenuList = async () => {
let res = await this.axios.post('homes/menu')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
menuList: data.list
})
}
}
// 获取咨询数据
getInfoList = async () => {
let res = await this.axios.post('homes/info')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
infoList: data.list
})
}
}
// 获取咨询数据
getFaqList = async () => {
let res = await this.axios.post('homes/faq')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
faqList: data.list
})
}
}
// 获取房屋数据
getHouseList = async () => {
let res = await this.axios.post('homes/house')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
houseList: data.list
})
}
} */
doRequest = (url, dataName) => {
return this.axios.post(url).then(res => {
let { meta, data } = res
if (meta.status === 200) {
this.setState({
[dataName]: data.list
})
}
})
}
// 页面加载完成,需要发送ajax请求,获取轮播图数据
async componentDidMount() {
// Promise.all()
await Promise.all([
this.doRequest('homes/swipe', 'imgList'),
this.doRequest('homes/menu', 'menuList'),
this.doRequest('homes/info', 'infoList'),
this.doRequest('homes/faq', 'faqList'),
this.doRequest('homes/house', 'houseList')
])
this.setState({
loading: false
})
}
render() {
return (
Loading
{/* 轮播图 */}
{/* 菜单部分 */}
{/* 好客咨询部分 */}
{/* 好客问答部分 */}
{/* 房屋信息部分 */}
{/* */}
)
}
}
export default Main
// 定义菜单组件,渲染菜单数据
// 参数的解构
function MenuList({ data }) {
return (
{data.map(item => (
{item.menu_name}
))}
)
}
// 好客资讯组件
function InfoList({ data }) {
return (
-
{data.map(item => (
限购 ●
{item.info_title}
))}
)
}
// 好客问答组件
function FaqList({ data }) {
return (
好客问答
{data.map(item => (
-
{item.question_name}
{item.question_tag.split(',').map(tag => (
))}
{item.atime} ● {' '}
{item.qnum}
))}
)
}
// 房屋组件
function HouseList({ data }) {
let newHouse = []
let oldHouse = []
let hireHouse = []
data.forEach(item => {
let temp = (
-
{item.home_name}
{item.home_desc}
{item.home_tags.split(',').map(tag => (
))}
{item.home_price}
)
if (item.home_type === 1) {
newHouse.push(temp)
} else if (item.home_type === 2) {
oldHouse.push(temp)
} else {
hireHouse.push(temp)
}
})
// console.log(data)
return (
最新开盘
{newHouse}
二手精选
{oldHouse}
组一个家
{hireHouse}
)
}
2.6、My.jsx
import React from 'react'
class My extends React.Component {
render() {
return 这是My组件
}
}
export default My
2.7、Chat.jsx
import React from 'react'
class Chat extends React.Component {
render() {
return 这是Chat组件
}
}
export default Chat
2.8、Info.jsx
import React from 'react'
class Info extends React.Component {
render() {
return 这是Info组件
}
}
export default Info
2.9、Main.css
.main {
width: 100%;
height: 100%;
position: relative;
}
.search {
height: 40px;
line-height: 40px;
position: absolute;
width: 100%;
top: 0;
}
.content {
width: 100%;
height: 100%;
padding-top: 40px;
/* 给content添加了垂直的滚动条 */
overflow-y: auto;
}
/* 滚动条样式 */
.content::-webkit-scrollbar {
width: 3px;
}
.content::-webkit-scrollbar-thumb {
background-color: #cbdaea;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
min-height: 2rem;
background-clip: padding-box;
border-radius: 5px;
}
.content::-webkit-scrollbar-track {
background-color: #fff;
}
/* 菜单的样式 */
.home-menu-item {
border-radius: 31px;
height: 62px;
line-height: 62px;
color: #fff;
}
.ui.grid > .row > .column:nth-child(1) .home-menu-item {
background-color: #0ac250;
}
.ui.grid > .row > .column:nth-child(2) .home-menu-item {
background-color: #ff611a;
}
.ui.grid > .row > .column:nth-child(3) .home-menu-item {
background-color: #f6b906;
}
.ui.grid > .row > .column:nth-child(4) .home-menu-item {
background-color: #4f99e4;
}
.ui.grid > .row > .column:nth-child(5) .home-menu-item {
background-color: #fd8701;
}
.ui.grid > .row > .column:nth-child(6) .home-menu-item {
background-color: #23acf2;
}
.ui.grid > .row > .column:nth-child(7) .home-menu-item {
background-color: #eb1e03;
}
.ui.grid > .row > .column:nth-child(8) .home-menu-item {
background-color: #7ccc39;
}
.ui.menu > .row > .column {
text-align: center;
margin-top: 10px;
}
/* 好客咨询样式 */
.home-msg {
margin: 25px 0 15px 0;
position: relative;
}
.home-msg .ui.unstackable.items > .item.home-msg-img > .image,
.home-msg .ui.unstackable.items > .item.home-msg-img > .image > img {
width: 50px !important;
height: 50px !important;
margin-left: 7px;
margin-top: 2px;
}
.ui.items > .item > .content > .header {
display: block;
height: 25px;
margin-top: 5px;
font-weight: 600;
}
.ui.items > .item > .content > .header span:first-child {
color: orange;
margin-right: 10px;
overflow: hidden;
display: inline-block;
}
.ui.items > .item > .content > .header span:last-child {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: baseline;
width: 65%;
}
.home-msg .home-msg-more {
position: absolute;
right: 5px;
top: 12px;
}
/* 好客问答样式 */
.home-ask-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #cecece;
}
.home-ask ul {
list-style: none;
margin: 0;
padding: 0;
}
.home-ask ul li {
height: 70px;
line-height: 70px;
}
.home-ask ul li div {
height: 35px;
line-height: 35px;
}
.home-ask ul li div:first-child {
margin-left: 10px;
font-size: 18px;
}
.home-ask ul li div:last-child {
margin-left: 30px;
position: relative;
}
.home-ask ul li div:last-child div {
position: absolute;
font-size: 14px;
right: 15px;
top: 0;
}
/* 房屋样式 */
.home-hire-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #cecece;
}
.ui.items > .item > .image {
margin-left: 10px;
margin-top: 6px;
}
.ui.items > .item > .content > .description:last-child {
color: orange;
}
2.10、Main.jsx
import React from 'react'
import 'react-image-gallery/styles/css/image-gallery.css'
import './Main.css'
import {
Input,
Grid,
Icon,
Item,
Button,
Dimmer,
Loader
} from 'semantic-ui-react'
// 导入轮播图组件
import ImageGallery from 'react-image-gallery'
class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
imgList: [],
menuList: [],
infoList: [],
faqList: [],
houseList: [],
loading: true
}
}
/* // 获取轮播图数据
getImgList = async () => {
let res = await this.axios.post('homes/swipe')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
imgList: data.list
})
}
}
// 获取菜单数据
getMenuList = async () => {
let res = await this.axios.post('homes/menu')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
menuList: data.list
})
}
}
// 获取咨询数据
getInfoList = async () => {
let res = await this.axios.post('homes/info')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
infoList: data.list
})
}
}
// 获取咨询数据
getFaqList = async () => {
let res = await this.axios.post('homes/faq')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
faqList: data.list
})
}
}
// 获取房屋数据
getHouseList = async () => {
let res = await this.axios.post('homes/house')
let { meta, data } = res
if (meta.status === 200) {
this.setState({
houseList: data.list
})
}
} */
doRequest = (url, dataName) => {
return this.axios.post(url).then(res => {
let { meta, data } = res
if (meta.status === 200) {
this.setState({
[dataName]: data.list
})
}
})
}
// 页面加载完成,需要发送ajax请求,获取轮播图数据
async componentDidMount() {
// Promise.all()
await Promise.all([
this.doRequest('homes/swipe', 'imgList'),
this.doRequest('homes/menu', 'menuList'),
this.doRequest('homes/info', 'infoList'),
this.doRequest('homes/faq', 'faqList'),
this.doRequest('homes/house', 'houseList')
])
this.setState({
loading: false
})
}
render() {
return (
Loading
{/* 轮播图 */}
{/* 菜单部分 */}
{/* 好客咨询部分 */}
{/* 好客问答部分 */}
{/* 房屋信息部分 */}
{/* */}
)
}
}
export default Main
// 定义菜单组件,渲染菜单数据
// 参数的解构
function MenuList({ data }) {
return (
{data.map(item => (
{item.menu_name}
))}
)
}
// 好客资讯组件
function InfoList({ data }) {
return (
-
{data.map(item => (
限购 ●
{item.info_title}
))}
)
}
// 好客问答组件
function FaqList({ data }) {
return (
好客问答
{data.map(item => (
-
{item.question_name}
{item.question_tag.split(',').map(tag => (
))}
{item.atime} ● {' '}
{item.qnum}
))}
)
}
// 房屋组件
function HouseList({ data }) {
let newHouse = []
let oldHouse = []
let hireHouse = []
data.forEach(item => {
let temp = (
-
{item.home_name}
{item.home_desc}
{item.home_tags.split(',').map(tag => (
))}
{item.home_price}
)
if (item.home_type === 1) {
newHouse.push(temp)
} else if (item.home_type === 2) {
oldHouse.push(temp)
} else {
hireHouse.push(temp)
}
})
// console.log(data)
return (
最新开盘
{newHouse}
二手精选
{oldHouse}
组一个家
{hireHouse}
)
}
2.11、My.jsx
import React from 'react'
class My extends React.Component {
render() {
return 这是My组件
}
}
export default My
3、Home.css
.home {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.home_content {
position: absolute;
width: 100%;
top: 0;
bottom: 60px;
/* background-color: lightgreen; */
}
.home_menu {
position: absolute;
width: 100%;
height: 60px;
bottom: 0;
/* background-color: lightblue; */
}
.home_menu .ui.grid {
margin: 0;
}
.home_menu .ui.grid > .row {
padding: 0;
}
.home_menu .column {
height: 60px;
border-right: 1px solid #ccc;
background-color: #eee;
box-sizing: border-box;
}
.home_menu .column a {
color: #666;
width: 100%;
height: 100%;
display: block;
text-align: center;
}
.home_menu .column a.active {
color: darkorange;
}
.home_menu .column i {
font-size: 24px;
height: 40px;
line-height: 40px;
}
.home_menu .column p {
height: 20px;
line-height: 20px;
}
4、Home.jsx
import React from 'react'
import './Home.css'
import { Grid, Icon } from 'semantic-ui-react'
import { NavLink, Switch, Route } from 'react-router-dom'
import Main from './home/main/Main'
import Info from './home/info/Info'
import Chat from './home/chat/Chat'
import My from './home/my/My'
// import Demo from './home/demo/Demo.jsx'
class Home extends React.Component {
// 点标记语法
render() {
return (
{/* 网格布局 */}
主页
资讯
微聊
我的
)
}
}
export default Home
5、Login.css
.login_container {
height: 100%;
background-color: #90ee90;
}
.login_title {
height: 50px;
line-height: 50px;
background-color: #21b97a;
text-align: center;
color: #fff;
font-size: 24px;
}
.login_form {
margin: 20px 10px;
}
6、Login.jsx
import React from 'react'
// 引入semanticui的组件
import { Form } from 'semantic-ui-react'
// 引入Login.css样式
import './Login.css'
// 引入withRouter实现编程式导航
import { withRouter } from 'react-router-dom'
class Login extends React.Component {
// 构造函数
constructor(props) {
super(props)
this.state = {
uname: '',
pwd: ''
}
}
// 用于处理受控组件
handleChange = e => {
let { name, value } = e.target
this.setState({
[name]: value
})
}
// 登录功能
login = async e => {
e.preventDefault()
let { history } = this.props
// 发送请求
let { uname, pwd } = this.state
// await必须在async函数中使用,await可以在promise对象前面使用
// await会暂停aysnc函数的执行,等待promise的结果,才会继续async函数的执行
let res = await this.axios.post('users/login', {
uname,
pwd
})
let { meta, data } = res
if (meta.status === 200) {
//1. 把token给保存到浏览器本地
localStorage.setItem('myToken', data.token)
//2. 把userid存储起来
localStorage.setItem('uid', data.uid)
//3. 跳转到home组件
history.push('/home')
} else {
console.log(meta.msg)
}
}
// 组件的渲染方法
render() {
return (
登录
{/* Form:表示整个表单组件 */}
{/* Form.Field:表示表单的一个字段 */}
登录
)
}
}
export default withRouter(Login)
7、App.jsx
import React from 'react'
// 导入路由
import {
BrowserRouter as Router,
Route,
Switch,
Redirect
} from 'react-router-dom'
// 导入组件
import Home from './components/Home'
import Login from './components/Login'
class App extends React.Component {
render() {
return (
)
}
}
export default App
8、index.css
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
#root {
height: 100%;
width: 100%;
}
9、index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import 'semantic-ui-css/semantic.min.css'
import App from './App'
// 导入axios对象
import axios from 'axios'
// 把axios对象绑定到了React组件的原型上,将来所有的react组件都能访问到axios对象
React.Component.prototype.axios = axios
// 给axios对象配置默认全局路径
axios.defaults.baseURL = 'http://localhost:9999/'
// axios.defaults.baseURL = 'http://47.96.21.88:8086/'
// 给axios配置一个响应拦截器 直接把data中的数据返回
axios.interceptors.response.use(
function(response) {
// 拦截到axios所有的请求,直接返回了响应结果中的data数据
return response.data
},
function(error) {
return error
}
)
// 配置请求拦截器,每次请求,除了login,都可以添加token值
axios.interceptors.request.use(
function(config) {
if (!window.location.href.endsWith('/login')) {
config.headers.Authorization = localStorage.getItem('myToken')
}
return config
},
function(error) {
return Promise.reject(error)
}
)
ReactDOM.render( , document.getElementById('root'))