1、好客租房移动web项目 -- 练习
1.1、components
---home
(1)Chat.jsx
import React from 'react'
class Chat extends React.Component {
render() {
return 这是Chat组件
}
}
export default Chat
(2)Info.jsx
import React from 'react'
class Info extends React.Component {
render() {
return 这是Info组件
}
}
export default Info
(3)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;
}
(4)My.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.jsx
import React from 'react'
class My extends React.Component {
render() {
return 这是My组件
}
}
export default My
1.2、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;
}
1.3、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'
import Info from './home/Info'
import Chat from './home/Chat'
import My from './home/My'
class Home extends React.Component {
// 点标记语法
render() {
return (
{/* 网格布局 */}
主页
资讯
微聊
我的
)
}
}
export default Home
1.4、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;
}
1.5、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. 跳转到home组件
history.push('/home')
} else {
console.log(meta.msg)
}
}
// 组件的渲染方法
render() {
return (
登录
{/* Form:表示整个表单组件 */}
{/* Form.Field:表示表单的一个字段 */}
登录
)
}
}
export default withRouter(Login)
1.6、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
1.7、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%;
}
1.8、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'))
2、好客租房移动web项目 -- 参考
---modules
2.1、chat
(1)chat.js
import React from 'react';
class Chat extends React.Component {
render() {
return (
微聊
);
}
}
export default Chat;
2.2、home
(1)calculator.js
import React from 'react';
import {Icon,Tab,Dropdown,Input,Grid,Button } from 'semantic-ui-react';
import ReactEcharts from 'echarts-for-react';
class MyChart extends React.Component {
getOption = () => {
const {chartData} = this.props;
return {
title : {
text: '贷款数据统计',
// subtext: '纯属虚构',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{c}"
// formatter: "{a}
{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
left: 'left',
data: ['贷款总额','支付利息']
},
series : [{
name: '访问来源',
type: 'pie',
radius : '55%',
center: ['50%', '60%'],
// data:[
// {value:335, name:'贷款总额'},
// {value:310, name:'支付利息'},
// {value:200, name:'利息'}
// ],
data: chartData,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
}
}
render(){
return
}
}
class Calculator extends React.Component {
constructor(props){
super(props);
this.state = {
type: 1,
year: 1,
rate: 1,
chartData: [
{value:335, name:'贷款总额'},
{value:310, name:'支付利息'},
{value:200, name:'利息'}
]
}
}
handleType = (e, { value }) => this.setState({ type:value });
handleYear = (e, { value }) => this.setState({ year:value });
handleRate = (e, { value }) => this.setState({ rate:value });
handleCalc = () => {
// 获取表单中的数据,根据表单的数据计算贷款信息,根据信息渲染图表
let chartData = [
{value:535, name:'贷款总额'},
{value:210, name:'支付利息'}
]
// 跟新数据
this.setState({
chartData: chartData
});
}
render() {
const {hideCalc} = this.props;
const types = [
{ key: 1, text: '按房间总额', value: 1 },
{ key: 2, text: '按贷款总额', value: 2 },
]
const years = (n) => {
let arr = [];
for(let i=1;i<=n;i++) {
arr.push({
key : i,
text: i,
value: i
});
}
return arr;
}
const rates = [
{key: 1,text: '基准利率(3.25%)',value: 1},
{key: 2,text: '基准利率9.5折',value: 2},
{key: 3,text: '基准利率9折',value: 3},
{key: 4,text: '基准利率8.5折',value: 4}
]
const First = () => {
const {type,year,rate} = this.state;
return (
贷款方式
贷款总额
贷款年限
贷款利率
);
}
const panes = [
{ menuItem: '公积金贷款', render: () => },
{ menuItem: '商业贷款', render: () => 商业贷款 },
{ menuItem: '组合贷款', render: () => 组合贷款 },
];
return (
贷款利率计算
);
}
}
export default Calculator;
(2)home.css
.home-container {
position: absolute;
width: 100%;
height: 100%;
background-color: #fff;
overflow: hidden;
}
.home-topbar {
position: absolute;
width: 100%;
height: 38px;
line-height: 38px;
// background-color: #B5EFFF;
z-index: 2;
}
.home-banner {
height: 200px;
}
.home-content {
box-sizing: border-box;
// background-color: yellow;
width: 100%;
height: 100%;
padding-top: 38px;
overflow-y: auto;
}
.home-content::-webkit-scrollbar{
width: 3px;
}
.home-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;
}
.home-content::-webkit-scrollbar-track {
background-color: #fff;
}
.ui.grid>.row>.column {
text-align: center;
margin-top: 10px;
}
.home-msg {
margin: 25px 0 15px 0;
position: relative;
}
.home-msg .home-msg-more {
position: absolute;
right: 5px;
top: 12px;
}
.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-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-ask ul li div:first-child i {
color: green;
}
.slider-slide {
min-height: auto !important;
vertical-align: bottom !important;
}
.home-menu-item {
border-radius: 31px;
height: 62px;
line-height: 62px;
color: #fff;
}
.home-hire-title {
height: 40px;
font-weight: 600;
font-size: 18px;
line-height: 40px;
padding-left: 10px;
border-bottom: 1px solid #CECECE;
}
.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.unstackable.items>.item.home-msg-img>.image, .ui.unstackable.items>.item.home-msg-img>.image>img {
width: 50px !important;
height: 50px !important;
margin-left: 7px;
}
.ui.items>.item>.image {
margin-left: 10px;
margin-top: 6px;
}
.ui.items>.item>.content>.description:last-child {
color: orange;
}
.map-house {
position: fixed;
bottom: 50px;
top: 0px;
z-index: 9999;
height: 100%;
width: 100%;
background-color: lightgreen;
}
.map-house-title {
// position: absolute;
height: 50px;
line-height: 50px;
background-color: #F5F5F5;
text-align: center;
font-size: 22px;
z-index: 9999;
width: 100%;
}
.map-house-title i {
position: absolute;
left: 10px;
top: 8px;
}
.map-house-content{
position: absolute;
width: 100%;
bottom: 0;
top: 50px;
}
.home-calc {
position: fixed;
// bottom: 50px;
top: 0px;
z-index: 9999;
height: 100%;
width: 100%;
background-color: #fff;
}
.home-calc-title {
height: 50px;
line-height: 50px;
background-color: #F5F5F5;
text-align: center;
font-size: 22px;
z-index: 9999;
width: 100%;
}
.home-calc-title i {
position: absolute;
left: 10px;
top: 8px;
}
.home-calc-content{
position: absolute;
width: 100%;
bottom: 0;
top: 50px;
}
.ui.bottom.attached.segment.active.tab .six.wide.column {
line-height: 40px;
color: #000;
}
.calc-first-total input {
width: 196px;
}
.search-bar {
position: fixed;
bottom: 50px;
top: 38px;
z-index: 9999;
height: 100%;
width: 100%;
background-color: #fff;
}
.house-list {
position: fixed;
bottom: 0px;
top: 0px;
z-index: 9999;
height: 100%;
width: 100%;
background-color: #fff;
}
.house-list-title {
height: 50px;
line-height: 50px;
background-color: #F5F5F5;
text-align: center;
font-size: 22px;
z-index: 9999;
width: 100%;
}
.house-list-title i {
position: absolute;
left: 10px;
top: 8px;
}
.calc-chart {
width: 100%;
margin-top: 10px;
}
(3)home.js
import React from 'react';
import {Input,Grid,Icon,Item,Button,Dimmer,Loader} from 'semantic-ui-react';
import ImageGallery from 'react-image-gallery';
import 'semantic-ui-css/semantic.min.css';
import "react-image-gallery/styles/css/image-gallery.css";
import './home.css';
import axios from 'axios';
import Calculator from './calculator.js';
import Map from './map.js';
import {withRouter} from "react-router-dom";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
images: [],
menus: [],
infos: [],
faqs: [],
houses: [],
loading: true
}
}
doRequest(path,dataName){
return axios.post(path).then(result=>{
if(result.meta.status === 200) {
// 在then中直接返回数据,那么该数据会提供给下一个then
return result.data.list;
}
}).then();
}
componentDidMount() {
let images = this.doRequest('homes/swipe','images');
let menus = this.doRequest('homes/menu','menus');
let infos = this.doRequest('homes/info','infos');
let faqs = this.doRequest('homes/faq','faqs');
let houses = this.doRequest('homes/house','houses');
Promise.all([images,menus,infos,faqs,houses]).then((result)=>{
this.setState({
images: result[0],
menus: result[1],
infos: result[2],
faqs: result[3],
houses: result[4],
loading: false,
isShowCalc: false,
isShowMap: false
});
});
// axios.post('homes/swipe').then(result=>{
// if(result.meta.status === 200) {
// this.setState({
// images: result.data.list
// });
// }
// })
// axios.post('homes/menu').then(result=>{
// if(result.meta.status === 200) {
// this.setState({
// menus: result.data.list
// });
// }
// })
// axios.post('homes/info').then(result=>{
// if(result.meta.status === 200) {
// this.setState({
// infos: result.data.list
// });
// }
// })
// axios.post('homes/faq').then(result=>{
// if(result.meta.status === 200) {
// this.setState({
// faqs: result.data.list
// });
// }
// })
// axios.post('homes/house').then(result=>{
// if(result.meta.status === 200) {
// this.setState({
// houses: result.data.list
// });
// }
// })
}
handleMenu = (name) => {
switch(name){
case '二手房':
this.props.history.push('/home/list',{query:{menuName:name,homeType:1}});
break;
case '新房':
this.props.history.push('/home/list',{query:{menuName:name,homeType:2}});
break;
case '租房':
this.props.history.push('/home/list',{query:{menuName:name,homeType:3}});
break;
case '海外':
this.props.history.push('/home/list',{query:{menuName:name,homeType:4}});
break;
case '地图找房':
this.setState({
isShowMap: !this.state.isShowMap
});
break;
case '查公交':
break;
case '计算器':
this.setState({
isShowCalc: !this.state.isShowCalc
});
break;
case '问答':
break;
default:
console.log('没有匹配操作')
}
}
toggleCalc = () => {
// 隐藏计算器
this.setState({
isShowCalc: !this.state.isShowCalc
});
}
toggleMap = () => {
// 隐藏地图
this.setState({
isShowMap: !this.state.isShowMap
});
}
render() {
const Menus = (props) => {
const list = props.menuData.map(item=>{
return (
{item.menu_name}
);
})
return (
{list}
);
}
const Info = (props) => {
const infos = props.infoData.map(item=>{
return (
限购 ●
{item.info_title}
);
});
return (
-
{infos}
);
}
const Faq = (props) => {
const faq = props.faqData.map(item=>{
let tags = item.question_tag.split(',').map((tag,index)=>{
return
});
return (
{item.question_name}
{tags}
{item.atime} ● {item.qnum}
);
})
return (
好客问答
{faq}
);
}
const House = (props) => {
let newHouse = [];
let oldHouse = [];
let hireHouse = [];
props.houseData.forEach((item,index)=>{
let tags = item.home_tags.split(',').map((tag,index)=>{
return
})
let houseItem = (
-
{item.home_name}
{item.home_desc}
{tags}
{item.home_price}
);
if(index < 2) {
newHouse.push(houseItem);
}else if(index === 2 || index === 3) {
oldHouse.push(houseItem);
}else{
hireHouse.push(houseItem);
}
})
return (
最新开盘
{newHouse}
二手精选
{oldHouse}
组一个家
{hireHouse}
);
}
return (
{this.state.isShowCalc && }
{this.state.isShowMap &&
);
}
}
export default withRouter(Home);
(4)list.js
import React from 'react';
import {Icon,Item} from 'semantic-ui-react';
import {withRouter} from "react-router-dom";
import axios from 'axios';
class HouseList extends React.Component {
constructor(props){
super(props);
this.state = {
list: []
}
}
componentDidMount(){
const {query} = this.props.location.state;
axios.post('homes/list',{
home_type: query.homeType
}).then(result=>{
if(result.meta.status === 200) {
this.setState({
list: result.data
});
}
})
}
hideList = () => {
// 处理路由的跳转
// this.props.history.push('/home');
this.props.history.goBack();
}
render(){
const {query} = this.props.location.state;
const listInfo = this.state.list.map(item=>{
return (
-
{item.home_name}
{item.home_desc}
{item.home_tags}
{item.home_price}
);
});
return (
{query.menuName}
{listInfo}
);
}
}
export default withRouter(HouseList);
(5)map.js
import React from 'react';
import {Icon} from 'semantic-ui-react';
import axios from 'axios';
class Map extends React.Component {
constructor(props){
super(props);
this.state = {
list: []
}
}
initMap = (callback) => {
// 产生地图效果
// 百度地图API功能
var BMap = window.BMap;
var BMapLib = window.BMapLib;
// 创建Map实例
var map = new BMap.Map("allmap");
// 初始化地图,设置中心点坐标和地图级别
map.centerAndZoom(new BMap.Point(116.43244, 39.929986), 12);
// 添加地图类型控件
map.addControl(new BMap.MapTypeControl());
// 设置地图缩放
map.addControl(new BMap.ScaleControl({
anchor: window.BMAP_NAVIGATION_CONTROL_ZOOM
}));
// 设置地图导航
map.addControl(new BMap.NavigationControl({
enableGeolocation: true
}));
// 设置缩略图控件。
map.addControl(new BMap.OverviewMapControl());
// 设置地图显示的城市 此项是必须设置的
map.setCurrentCity("北京");
// 开启鼠标滚轮缩放
map.enableScrollWheelZoom(true);
// ----------------------------------------------
let xy = this.state.list;
var markers = [];
var pt = null;
for (var i in xy) {
pt = new BMap.Point(xy[i].x, xy[i].y);
markers.push(new BMap.Marker(pt));
}
// 地图上覆盖物的聚合效果
var markerClusterer = new BMapLib.MarkerClusterer(map, {
markers: markers,
girdSize: 100,
styles: [{
background: 'rgba(12,181,106,0.9)',
size: new BMap.Size(92, 92),
textSize: '16',
textColor: '#fff',
borderRadius: 'true'
}],
});
markerClusterer.setMaxZoom(50);
markerClusterer.setGridSize(10);
}
componentDidMount(){
axios.post('homes/map', {
type: 1
}).then(result=>{
if(result.meta.status === 200) {
this.setState({
list: result.data
});
// 初始化地图
this.initMap()
}
});
}
render(){
return (
地图找房
);
}
}
export default Map;
2.3、info
(1)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: 2;
}
.find-content {
box-sizing: border-box;
// background-color: yellow;
width: 100%;
height: 100%;
padding-top: 40px;
// overflow-y: scroll;
}
.ui.bottom.attached.segment.active.tab {
// position: relative;
top: 80px;
bottom: 0px;
// background-color: lightblue;
// height: 100%;
position: absolute;
overflow-y: auto;
}
.ui.items>.item {
background-color: #fff;
}
.ui.items>.item>.content>.header.info-title {
height: auto;
filter: none;
-webkit-filter: blur(0px);
-moz-filter: blur(0px);
-ms-filter: blur(0px);
filter:progid:DXImageTransform.Microsoft.Blur(PixelRadius='0');
}
.info-ask-list {
// background-color: lightgreen;
}
.info-ask-list li {
// height: 130px;
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-list li .num {
// position: absolute;
// right: 5px;
// bottom: 10px;
// color: #000;
}
.info-ask-btn {
padding: 10px 15px;
}
(2)info.js
import React from 'react';
import {Tab} from 'semantic-ui-react';
import './info.css';
import Loader from './loader.js';
class RecoMessage extends React.Component {
render(){
return
}
}
class TopMessage extends React.Component {
render(){
return
}
}
class AskAnswer extends React.Component {
render(){
return
}
}
class Info extends React.Component {
render() {
const panes = [
{ menuItem: '资讯', render: () =>
},
{ menuItem: '头条', render: () =>
},
{ menuItem: '问答', render: () =>
},
]
return (
资讯
);
}
}
export default Info;
(3)loader.js
import React from 'react';
import Tloader from 'react-touch-loader';
import './tab.css';
import axios from 'axios';
import { Item,Icon,Button,Modal,TextArea } from 'semantic-ui-react';
class QuestionModel extends React.Component {
constructor(props) {
super(props);
this.state = {
size: 'small',
commentStyle: {
width: '100%',
border: 0
},
value: ''
};
}
handleContent = (event) => {
this.setState({
value: event.target.value
});
}
handleSubmit = () => {
axios.post('infos/question',{
question: this.state.value
}).then(result=>{
if(result.meta.status === 200) {
// 关闭窗口
this.props.close();
}
})
}
render() {
const {close} = this.props;
return (
发表评论
)
}
}
const RecoMessage = (props) => {
let content = [];
props.listData.forEach(item=>{
content.push(
-
{item.info_title}
$1200
1 Month
);
})
return (
{content}
);
}
const AskAnswer = (props) => {
let list = [];
props.listData.forEach(item=>{
list.push(
思维
{item.question_name}
{item.answer_content&&(
{item.username} 的回答
)}
{item.answer_content}
{item.question_tag&&item.question_tag.split(',').map((tag,index)=>{return {tag}X})}
{item.qnum?item.qnum:0}个回答
);
});
return (
{list}
);
}
class Loader extends React.Component {
constructor(props) {
super(props);
this.state = {
initializing: 0,
hasMore: true,
listData: [],
total: 0,
pagenum: 0, // 当前加载到了第几条
pagesize: 2,
loadSwitch : false,
open: false
}
}
refresh = (resolve, reject) => {
// 为什么要添加定时函数?
if(this.state.loadSwitch) {
// 阻止后续代码运行并终止任务
return reject();
}
setTimeout(()=>{
// 刷新数据,重置状态
this.setState({
pagenum: 0,
loadSwitch: true
});
this.loadData().then(result=>{
console.log('success')
this.setState({
loadSwitch: false
});
resolve();
})
}, 0);
}
loadMore = (resolve) => {
// 加载更多,本质上包含分页逻辑(1、从第几条开始,查询多少条)
// 计算当前要加载的条数
setTimeout(()=>{
this.setState({
pagenum: this.state.pagenum + this.state.pagesize
});
this.loadData().then(result=>{
// 把新加载的数据填充到原有的集合中
// this.state.listData.push(...result);
// 推荐直接使用setState方式进行数据更新,不要使用push直接操作原始数据
let newArr = [...this.state.listData, ...result]
this.setState({
listData: newArr
});
// 处理是否还有更多的数据
this.setState({
hasMore: this.state.pagenum>0 && this.state.pagenum {
const {type} = this.props;
return axios.post('infos/list',{
pagenum: this.state.pagenum,
pagesize: this.state.pagesize,
type: type
}).then(result=>{
if(result.meta.status === 200) {
this.setState({
total: result.data.list.total
});
return result.data.list.data;
}
})
}
componentDidMount(){
this.loadData().then(result=>{
this.setState({
listData: result,
});
})
}
handleClose = () => {
// 打开问题弹窗
this.setState({
open: !this.state.open
});
}
initList = () => {
// 产生列表内容
const {type} = this.props;
if(type === 1 || type === 2) {
// 加载资讯
return
}else if(type === 3){
// 加载问答列表
return
}
}
render(){
const {hasMore,initializing} = this.state;
return (
this.refresh(resolve, reject)}
onLoadMore={(resolve) => this.loadMore(resolve)}
hasMore={hasMore}
initializing={initializing}>
{this.initList()}
);
}
}
export default Loader;
(4)tab.css
:focus {
outline: 0
}
:not(input):not(textarea):not([contenteditable]) {
-webkit-touch-callout: none;
user-select: none
}
.view {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
overflow: hidden;
// background-color: #EFEFF4;
display: flex;
flex-direction: column;
}
.main::-webkit-scrollbar{
width: 3px;
}
.main::-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;
}
.main::-webkit-scrollbar-track {
background-color: #fff;
}
.view h1,
.view h2 {
margin: 0;
background-color: #333;
color: #eee;
font-size: 1em;
line-height: 3
}
.view h1 a,
.view h2 a {
color: inherit;
margin: 0 1em
}
.view .main {
flex: 1;
overflow-x: hidden;
overflow-y: scroll;
-webkit-overflow-scrolling: touch
}
.view .main ul {
list-style: none;
margin: 0;
padding: 0
}
.view .main ul li {
background-color: #fff;
border-bottom: 1px solid #ccc
}
.view .main ul li p {
margin: 0;
line-height: 3em
}
.tloader-msg:after {
content: '下拉刷新'
}
.state-pulling.enough .tloader-msg:after {
content: '松开刷新'
}
.state-refreshed .tloader-msg:after {
content: '刷新成功'
}
.tloader-loading:after {
content: '正在加载...'
}
.tloader-symbol .tloader-loading:after {
content: '正在刷新...'
}
.tloader-btn:after {
content: '点击加载更多'
}
.tloader {
position: relative
}
.tloader.state-pulling {
overflow-y: hidden
}
.tloader-symbol {
position: absolute;
top: 0;
left: 0;
right: 0;
color: #7676a1;
text-align: center;
height: 3rem;
overflow: hidden
}
.state- .tloader-symbol,
.state-reset .tloader-symbol {
height: 0
}
.state-reset .tloader-symbol {
transition: height 0s .2s
}
.state-loading .tloader-symbol {
display: none
}
.tloader-msg {
line-height: 3rem;
font-size: 11px
}
.state-pulling .tloader-msg i,
.state-reset .tloader-msg i {
display: inline-block;
font-size: 2em;
margin-right: .6em;
vertical-align: middle;
height: 1em;
border-left: 1px solid;
position: relative;
transition: transform .3s ease
}
.state-pulling .tloader-msg i:after,
.state-pulling .tloader-msg i:before,
.state-reset .tloader-msg i:after,
.state-reset .tloader-msg i:before {
content: '';
position: absolute;
font-size: .5em;
width: 1em;
bottom: 0;
border-top: 1px solid
}
.state-pulling .tloader-msg i:before,
.state-reset .tloader-msg i:before {
right: 1px;
transform: rotate(50deg);
transform-origin: right
}
.state-pulling .tloader-msg i:after,
.state-reset .tloader-msg i:after {
left: 0;
transform: rotate(-50deg);
transform-origin: left
}
.state-pulling.enough .tloader-msg i {
transform: rotate(180deg)
}
.state-refreshing .tloader-msg {
height: 0;
opacity: 0
}
.state-refreshed .tloader-msg {
opacity: 1;
transition: opacity 1s
}
.state-refreshed .tloader-msg i {
display: inline-block;
box-sizing: content-box;
vertical-align: middle;
margin-right: 10px;
font-size: 20px;
height: 1em;
width: 1em;
border: 1px solid;
border-radius: 100%;
position: relative
}
.state-refreshed .tloader-msg i:before {
content: '';
position: absolute;
top: 3px;
left: 7px;
height: 11px;
width: 5px;
border: solid;
border-width: 0 1px 1px 0;
transform: rotate(40deg)
}
.tloader-body {
margin-top: -1px;
padding-top: 1px
}
.state-refreshing .tloader-body {
transform: translate3d(0, 3rem, 0);
transition: transform .2s
}
.state-refreshed .tloader-body {
animation: refreshed .4s
}
.state-reset .tloader-body {
transition: transform .2s
}
@keyframes refreshed {
0% {
transform: translate3d(0, 3rem, 0)
}
50% {
transform: translate3d(0, 3rem, 0)
}
}
.state-refreshing .tloader-footer {
display: none
}
.tloader-footer .tloader-btn {
color: #484869;
font-size: .9em;
text-align: center;
line-height: 3rem
}
.state-loading .tloader-footer .tloader-btn {
display: none
}
.tloader-loading {
display: none;
text-align: center;
line-height: 3rem;
font-size: 11px;
color: #7676a1
}
.tloader-loading .ui-loading {
font-size: 20px;
margin-right: .6rem
}
.state-loading .tloader-footer .tloader-loading,
.state-refreshing .tloader-symbol .tloader-loading {
display: block
}
@keyframes circle {
100% {
transform: rotate(360deg)
}
}
.ui-loading {
display: inline-block;
vertical-align: middle;
font-size: 1.5rem;
width: 1em;
height: 1em;
border: 2px solid #9494b6;
border-top-color: #fff;
border-radius: 100%;
animation: circle .8s infinite linear
}
#ui-waiting .ui-loading {
border: 2px solid #fff;
border-top-color: #9494b6
}
@keyframes tloader-progressing {
0% {
width: 0
}
10% {
width: 40%
}
20% {
width: 75%
}
30% {
width: 95%
}
}
@keyframes tloader-progressed {
0% {
opacity: 1
}
}
.tloader-progress {
position: relative
}
.tloader-progress:before {
content: "";
z-index: 1000;
position: absolute;
top: 0;
left: 0;
height: 2px;
background-color: #08BF06;
width: 99%;
animation: tloader-progressing 9s ease-out
}
.ed.tloader-progress:before {
opacity: 0;
width: 100%;
animation: tloader-progressed 1s
}
2.4.my
(1)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;
}
(2)my.js
import React from 'react';
import { Grid,Icon,Button,Modal,TextArea } from 'semantic-ui-react'
import './my.css';
import axios from 'axios';
import AvatarEditor from 'react-avatar-editor'
class ImageModel extends React.Component {
constructor(props) {
super(props);
this.state = {
size: 'small',
commentStyle: {
width: '100%',
border: 0
},
value: '',
scale: 1,
allowZoomOut: false
};
this.fileInput = React.createRef();
}
submitComment = (e) => {
this.props.selectImage(this.fileInput.current.files[0])
}
render() {
const { open } = this.props
return (
选择图片
)
}
}
class AvatarModel extends React.Component {
constructor(props) {
super(props);
this.state = {
size: 'small',
commentStyle: {
width: '100%',
border: 0
},
value: '',
scale: 1,
allowZoomOut: false
};
}
handleScale = e => {
const scale = parseFloat(e.target.value)
this.setState({
scale: scale
})
}
setEditorRef = editor => {
if (editor) this.editor = editor
}
submitComment = (e) => {
// 生成头像数据(把图片生成base64格式的数据)
const img = this.editor.getImageScaledToCanvas().toDataURL()
axios.post('/my/avatar',{
avatar: img
}).then(data=>{
// 上传成功之后更新本地头像信息
this.props.updateAvatar(img);
})
}
render() {
const { open,close,avatar } = this.props
return (
上传头像
缩放:
)
}
}
class My extends React.Component {
constructor(props) {
super(props);
this.state = {
avatarPath: '', //生成的头像的base64数据
uname: '',
open: false,
show: false,
avatar: null // 选择的文件
}
}
componentDidMount(){
axios.post('my/info',{
user_id: localStorage.getItem('uid')
}).then(result=>{
if(result.meta.status === 200) {
this.setState({
avatarPath: result.data.avatar,
uname: result.data.username
});
}
})
}
// handleClose = () => {
// this.setState({
// open: !this.state.open
// })
// }
selectImage = (file) => {
// 设置选中的图片
this.setState({
avatar: file
});
// 隐藏选择图片的窗口
this.toggleSelect();
// 显示图片裁切弹窗
this.cropImage();
}
cropImage = () => {
this.setState({
show: !this.state.show
});
}
toggleSelect = () => {
this.setState({
open: !this.state.open
});
}
updateAvatar = (img) => {
// 更新最新的头像图片
this.setState({
avatarPath: img
});
// 关闭裁切窗口
this.cropImage();
}
render() {
return (
{this.state.uname}
编辑个人资料
看房记录
我的订单
我的收藏
个人资料
身份认证
联系我们
);
}
}
export default My;
2.5、main.css
.main-content {
position: absolute;
top: 0;
bottom: 60px;
width: 100%;
background-color: lightblue;
}
.main-menu {
position: fixed;
width: 100%;
height: 60px;
bottom: 0;
}
.placeholder {
color: #bbb;
text-align: center;
height: 60px;
line-height: 60px;
width: 100%;
z-index: 9999;
}
.placeholder i {
display: block;
height: 45px;
width: 100%;
font-size: 24px;
line-height: 45px;
}
.placeholder div {
height: 15px;
line-height: 10px;
}
.iconfont {
font-size: 24px;
}
.active {
color: orange;
}
.ui.grid>.row {
padding-top: 0;
padding-bottom: 0;
}
.main-menu .ui.grid>.row>.column {
margin-top: 0;
}
2.6、main.js
import React from 'react';
import {Grid,Icon} from 'semantic-ui-react';
import {Route, Link} from "react-router-dom";
import 'semantic-ui-css/semantic.min.css';
import './main.css';
import Home from './home/home.js';
import Chat from './chat/chat.js';
import Info from './info/info.js';
import My from './my/my.js';
import HouseList from './home/list.js';
class Menu extends React.Component {
render(){
const {to, current, menuName, iconName} = this.props;
return (
(
{menuName}
)}
/>
);
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {
}
}
render() {
return (
);
}
}
export default Main;
2.7、App.css
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
2.8、App.js
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Route,
Switch,
Redirect
} from "react-router-dom";
import './App.css';
import Login from './login.js';
import Main from './modules/main.js';
import axios from 'axios';
// axios.defaults.baseURL = 'http://47.96.21.88:8086/';
axios.defaults.baseURL = 'http://127.0.0.1:8086/';
// 请求拦截器
axios.interceptors.request.use(function (config) {
if(!config.url.endsWith('/login')){
// 登录的地址不需要添加token请求头
config.headers.Authorization = localStorage.getItem('mytoken');
}
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function (response) {
// Do something with response data
// console.log(response)
return response.data
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
class App extends Component {
render() {
return (
);
}
}
const Show = () => {
return Show
}
export default App;
2.9、index.css
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
2.10、index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render( , document.getElementById('root'));
2.11、login.css
.login-container {
height: 100%;
background-color: lightgreen;
}
.login-container .login-title {
line-height: 50px;
font-size: 24px;
color: #fff;
text-align: center;
height: 50px;
background-color: #21B97A;
}
.login-container .login-form {
margin: 20px 10px;
text-align: center;
}
.login-container .login-form button {
width: 100%;
}
2.12、login.js
import React from 'react';
import axios from 'axios';
import {withRouter} from "react-router-dom";
import {Form} from 'semantic-ui-react';
import 'semantic-ui-css/semantic.min.css';
import './login.css';
class Login extends React.Component {
constructor(props) {
super(props)
this.state = {
uname: '',
pwd: ''
}
}
handleSubmit = () => {
const {history} = this.props;
axios.post('users/login',{
uname: this.state.uname,
pwd: this.state.pwd
}).then(result=>{
// console.log(data.data)
if(result.meta.status === 200) {
// 存储token
localStorage.setItem('mytoken', result.data.token);
localStorage.setItem('uid', result.data.uid);
// 登录成功
history.push('/home');
}
});
}
handleChange = (event) => {
// 从原生DOM中获取属性:name 和 value
const {name,value} = event.target;
this.setState({
[name]: value
});
}
render() {
const {uname,pwd} = this.state;
return (
登录
);
}
}
export default withRouter(Login);