Skip to content

Commit

Permalink
add(page): search
Browse files Browse the repository at this point in the history
  • Loading branch information
Yangfan2016 committed Apr 29, 2019
1 parent af52891 commit ab79b1d
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 34 deletions.
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": "MIT",
"dependencies": {
"@babel/core": "7.2.2",
"@babel/plugin-proposal-decorators": "^7.4.4",
"@svgr/webpack": "4.1.0",
"@types/jest": "24.0.11",
"@types/node": "11.13.0",
Expand Down Expand Up @@ -130,7 +131,15 @@
},
"babel": {
"presets": [
"react-app"
"babel-preset-react-app"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
}
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>豆瓣电影</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
2 changes: 1 addition & 1 deletion src/App.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 53 additions & 12 deletions src/components/TopNav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { withRouter } from 'react-router-dom';
import {
Icon,
Affix,
Expand All @@ -10,20 +11,24 @@ import {
import { Link } from 'react-router-dom';
import '../css/Home.css';
import * as _ from "lodash";
import { serialize } from '../utils';
import { func } from "prop-types";
import { timingSafeEqual } from "crypto";



@withRouter
class TopNav extends React.Component<any> {
getSuggestionBySearch: (value: string) => any
state: any
constructor(props: any) {
super(props);
this.state = {
hostShowTitle: "",
hotShowList: [], // 热映
suggestList: [], // 搜索建议
searchHistory: this.getSearchHistory().slice(0),
searchStr: "",
isShowSuggestBox: props.showSuggest,
isShowSuggestBox: false,
isShowTipsPanel: true,
isTopNavFixed: false,
};
Expand All @@ -36,7 +41,10 @@ class TopNav extends React.Component<any> {
.then(({ data }: any) => {
let { subjects } = data;

let hostShowTitle = subjects.length > 0 ? subjects[0].title : "";

this.setState({
hostShowTitle,
hotShowList: subjects,
isLoadingHotShow: false,
});
Expand All @@ -49,6 +57,26 @@ class TopNav extends React.Component<any> {
isShowSuggestBox: isShow,
});
}
navToSearch = () => {
let { searchStr, hostShowTitle } = this.state;

searchStr = searchStr.trim();

if (searchStr === "") {
searchStr = hostShowTitle;
}

let query = {
q: searchStr,
};

let search = serialize(query);

this.props.history.push({
pathname: '/search',
search,
});
}
getSearchHistory() {
const KEY = "SEARCH_H";
let cache = JSON.parse(localStorage.getItem(KEY) || "[]");
Expand Down Expand Up @@ -99,15 +127,20 @@ class TopNav extends React.Component<any> {
isValid && this.getSuggestionBySearch(str);

}
componentWillReceiveProps(nextProps: any) {
if (nextProps.showSuggest !== this.state.showSuggest) {
this.setState({
isShowSuggestBox: nextProps.showSuggest,
});
}
closeSuggest = () => {
this.setState({
isShowSuggestBox: false,
});
}
componentDidMount() {
document.addEventListener("click", this.closeSuggest);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeSuggest);
}
render() {
let {
hostShowTitle,
hotShowList,
suggestList,
searchHistory,
Expand All @@ -128,24 +161,32 @@ class TopNav extends React.Component<any> {
<div className="logo"></div>
<div className="search">
<div className="search-box">
<div className="search-btn">
<div className="search-btn" onClick={this.navToSearch}>
<Icon type="search" />
<span>全网搜</span>
</div>
<input className="search-input" placeholder="王牌对王牌 第4季"
<input className="search-input"
placeholder={hostShowTitle}
value={searchStr}
onChange={this.getSearch}
onClick={ev => {
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();
this.toggleSuggestList(true);
}}
onKeyDown={ev => {
this.toggleSuggestList(true);
if (ev.keyCode === 13) {
this.toggleSuggestList(false);
this.navToSearch();
}
}} />
</div>
<div className="search-list" style={
{
"display": isShowSuggestBox ? "block" : "none",
}
}
onClick={ev => ev.stopPropagation()}>
onClick={ev => { ev.nativeEvent.stopImmediatePropagation() }}>
{
isShowTipsPanel ?
<div>
Expand Down
5 changes: 1 addition & 4 deletions src/css/Home.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
}

.header .header-bar {
position: absolute;
top:0;
left:0;
width: 100%;
position: relative;
padding: 10px 0;
background-color: rgba(255,255,255,.24);
z-index: 2;
Expand Down
17 changes: 17 additions & 0 deletions src/css/Search.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.page-search{
width: 80%;
margin: 0 auto;
}

.page-search .search-result-list{
width: 70%;
}

.page-search .search-result-list .list-item{

}

.page-search .search-result-list .item-img{
width:160px;
height:220px;
}
11 changes: 2 additions & 9 deletions src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class Home extends React.Component {
isLoadingGoodBox: true,
isLoadingWeeklyBox: true,
isLoadingTop250: true,
isShowSuggestBox:false,
};

getHotShowing({
Expand Down Expand Up @@ -112,11 +111,6 @@ class Home extends React.Component {
});

}
toggleSuggestList = (isShow: boolean) => {
this.setState({
isShowSuggestBox: isShow,
});
}
renderTop250() {
let { top250List } = this.state as any;
let len = top250List.length;
Expand Down Expand Up @@ -177,7 +171,6 @@ class Home extends React.Component {
isLoadingGoodBox,
isLoadingWeeklyBox,
isLoadingTop250,
isShowSuggestBox,
}: any = this.state;

// temp
Expand All @@ -190,9 +183,9 @@ class Home extends React.Component {
];

return (
<div onClick={ev => { this.toggleSuggestList(false) }}>
<div>
<div className="header">
<TopNav showSuggest={isShowSuggestBox} />
<TopNav />
<div className="header-banner">
<Carousel autoplay>
{bannerList.map((item: any, index: number) => {
Expand Down
77 changes: 75 additions & 2 deletions src/pages/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,87 @@ import React from 'react';
import TopNav from '../components/TopNav';
import Footer from '../components/Footer';

import { getContentBySearch } from '../api';
import { reSerialize } from '../utils';

import { List } from 'antd';

import '../css/Search.css';

class Search extends React.Component {
constructor(props: any) {
super(props);

this.state = {
searchList: [],
isLoadingSearchList: true,
};

let { location } = props;
let { search } = location;
search = search.slice(1); // ?q=123 -> q=123
let query: any = reSerialize(search);
let searchStr = query.q;

getContentBySearch(searchStr, {
count: 10,
})
.then(({ data }: any) => {
let { subjects } = data;
this.setState({
searchList: subjects,
});
});

}
componentWillReceiveProps(newProps: any) {
let search = newProps.location.search;
let oldSearch = (this.props as any).location.search;
if (search !== oldSearch) {
search = search.slice(1); // ?q=123 -> q=123
let query: any = reSerialize(search);
let searchStr = query.q;

getContentBySearch(searchStr, {
count: 10,
})
.then(({ data }: any) => {
let { subjects } = data;
this.setState({
searchList: subjects,
});
});
}
}
render() {
let {
searchList,
}: any = this.state;
return (
<div>
<div className="header">
<div className="header clearfix">
<TopNav />
</div>
<div>123456</div>
<div className="page page-search">
<List
className="search-result-list"
itemLayout="vertical"
size="small"
dataSource={searchList}
renderItem={(item: any, index: number) => {
let { images, title, id } = item;
return (
<List.Item
className="list-item"
key={id}>
<List.Item.Meta
avatar={<img alt="logo" className="item-img" src={images.medium} />}
title={title}
/>
</List.Item>
)
}} />
</div>
<Footer />
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/router/RouterView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export default function () {
<Route exact path="/home" component={Home} />
<Route exact path="/detail/:id" component={Detail} />
<Route exact path="/box" component={Box} />
<Route exact path="/search/:query" component={Search} />
<Route exact path="/search" component={Search} />
<Route component={NotFound} />
</Switch>Î
</Switch>
</BrowserRouter>
);
}
Expand Down
47 changes: 46 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
export { };
// serialize data e.g. {a:"abc",b:"123"} -> "a=abc&b=123"
export function serialize(data: any, isTraditional: boolean = false): string {
let arr = [];
if (typeof data == "object") {
for (let key in data) {
if (data[key] != null) {
let item = data[key];
if (isTraditional && item instanceof Array) {
arr.push(item.map(function (field) {
return encodeURIComponent(key) + "=" + encodeURIComponent(field);
}).join("&"));
} else {
arr.push(encodeURIComponent(key) + "=" + encodeURIComponent(item));
}
}
}
return arr.join("&");
}
return '';
}
/**
* reverse serialize data e.g. "a=abc&b=123" -> {"a":"abc","b":"123"}
* @param {string} str
* @param {boolean} isTraditional if true e.g. "a=abc&b=123&b=456" -> {"a":"abc","b":["123","456"]}
*/
export function reSerialize(str: string, isTraditional = true): object {
let s = decodeURIComponent(str);

return s.split("&").reduce(function (prev:any, cur) {
let flag = cur.indexOf("=");
let key = cur.slice(0, flag);
let val = cur.slice(flag + 1);

if (isTraditional) {
if (prev[key]) {
prev[key] instanceof Array ? prev[key].push(val) : prev[key] = [prev[key], val];
} else {
prev[key] = val;
}
} else {
prev[key] = val;
}

return prev;
}, {});
}
Loading

0 comments on commit ab79b1d

Please sign in to comment.