├── .babelrc
├── .gitignore
├── README.md
├── index.html
├── package.json
├── src
├── App.jsx
├── assets
│ ├── css
│ │ └── style.css
│ └── images
│ │ ├── 360_logo.png
│ │ ├── background.png
│ │ ├── baidu_logo.png
│ │ └── sougou_logo.png
├── components
│ ├── logo-select.jsx
│ └── search-input.jsx
└── main.jsx
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": [ "es2015","react"] }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | node_modules、
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 一个简易的搜索大全页面
2 | ----
3 |
4 | [Vue版demo](http://github.com/lavyun/vue-demo-search)
5 |
6 | * [演示地址](http://lavyun.github.io/vue-demo-search)
7 |
8 | #### 所用知识
9 | - react组件与组件通信
10 | - 简单的webpack配置
11 |
12 | #### 如何运行
13 | 将本项目下载的本地.
14 | > 1. 需要node环境
15 | > 2. 输入命令npm install(最好使用cnpm淘宝镜像)
16 | > 3. 输入命令npm run dev
17 | > 4. 打开index.html
18 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | react-demo-search
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn-react",
3 | "version": "1.0.0",
4 | "description": "React is a JavaScript library for building user interfaces.",
5 | "main": "webpack.config.js",
6 | "directories": {
7 | "example": "examples"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "dev": "webpack --progress --watch"
12 | },
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "jquery": "^3.1.1",
17 | "react": "^15.4.2",
18 | "react-dom": "^15.4.2",
19 | "webpack": "^2.2.0"
20 | },
21 | "devDependencies": {
22 | "babel-core": "^6.23.1",
23 | "babel-loader": "^6.4.0",
24 | "babel-preset-es2015": "^6.22.0",
25 | "babel-preset-react": "^6.23.0",
26 | "css-loader": "^0.27.1",
27 | "file-loader": "^0.9.0",
28 | "style-loader": "^0.13.2",
29 | "url-loader": "^0.5.8"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import SearchInput from './components/search-input'
4 |
5 | const App = React.createClass({
6 | render(){
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 | });
14 |
15 | export default App;
--------------------------------------------------------------------------------
/src/assets/css/style.css:
--------------------------------------------------------------------------------
1 | *{box-sizing: border-box;padding: 0;margin: 0;}
2 | ul,li{list-style: none}
3 | html {
4 | height: 100%;
5 | }
6 |
7 | body {
8 |
9 | height: 100%;
10 | background: url('../images/background.png') no-repeat center;
11 | background-size: 100% 100%
12 | }
13 |
14 | #app {
15 | color: #2c3e50;
16 | width: 700px;
17 | font-family: Source Sans Pro, Helvetica, sans-serif;
18 | text-align: center;
19 | position: absolute;
20 | top: 35%;
21 | left: 50%;
22 | margin-left: -350px;
23 | margin-top: -100px;
24 | }
25 |
26 | .search{width: 100%;}
27 | .search-panel{width: 100%;height: 50px;position: relative;}
28 | .search-input{width: 600px;height: 100%;font-size: 16px;padding: 0 30px 0 10px;display: block;float: left;}
29 | .search-btn{width: 100px;height: 100%;border: none;background-color: rgb(65, 185, 147);color: #fff;font-size: 18px;display: block;float: right}
30 | .search-clearinput{position: absolute;display: block;width: 20px;height: 20px;left: 575px;top: 15px;cursor: pointer;font-size: 18px;}
31 | .search-list{width: 600px;}
32 | .search-list li{text-align: left;background-color: #fff;padding: 5px 10px;cursor: pointer}
33 | .search-list .is-select{background-color: #e4e4e4;}
34 |
35 | .logo-panel{width: 100%;height: 140px;position: relative;margin-bottom: 20px;}
36 | .logo-display{width: 100%;height: 100%;text-align: center}
37 | .logo-select-arrow{
38 | display: block; width: 0;
39 | height: 0;
40 | border: 10px solid #000;
41 | border-color: #000 transparent transparent transparent;
42 | position: absolute;;right: 130px;top: 65px;
43 | cursor: pointer;
44 | }
45 | .logo-list{position: absolute;top: 130px;width: 200px;left: 50%;z-index: 9;border: 1px solid #e4e4e4}
46 | .logo-list-item{background-color: #fff;border-bottom: 1px solid #e4e4e4}
47 | .logo-list-item:hover{background-color: #e4e4e4}
48 | .logo-list-item:last-of-type{border-bottom: 0}
49 | .logo-list-item img{width: 100%;}
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/assets/images/360_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavyun/react-demo-search/e30c13023cca420e74cd454611cfa5c6e47277d1/src/assets/images/360_logo.png
--------------------------------------------------------------------------------
/src/assets/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavyun/react-demo-search/e30c13023cca420e74cd454611cfa5c6e47277d1/src/assets/images/background.png
--------------------------------------------------------------------------------
/src/assets/images/baidu_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavyun/react-demo-search/e30c13023cca420e74cd454611cfa5c6e47277d1/src/assets/images/baidu_logo.png
--------------------------------------------------------------------------------
/src/assets/images/sougou_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavyun/react-demo-search/e30c13023cca420e74cd454611cfa5c6e47277d1/src/assets/images/sougou_logo.png
--------------------------------------------------------------------------------
/src/components/logo-select.jsx:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react'
2 | import {render} from 'react-dom'
3 |
4 | class LogoSelect extends Component{
5 | constructor(props){
6 | super(props);
7 | this.state = {
8 | logos: [require('../assets/images/360_logo.png'),require('../assets/images/baidu_logo.png'),require('../assets/images/sougou_logo.png')],
9 | selectIndex: 0,
10 | showLogo: false
11 | }
12 | }
13 |
14 | //处理logo选择
15 | handleLogoSelect(ev){
16 | var index = parseInt(ev.target.getAttribute('data-index'));
17 | this.setState({
18 | selectIndex: index,
19 | showLogo: false
20 | },function(){
21 | this.props.onLogoChange(index)
22 | })
23 | }
24 |
25 | //显示logo列表
26 | showLogoList(){
27 | this.setState({
28 | showLogo: true
29 | })
30 | }
31 |
32 | render() {
33 | var _state = this.state,
34 | logos = _state.logos;
35 |
36 | const Li= this.state.logos.map((logo, index)=>{
37 | return (
38 |
40 |
41 |

42 |
43 | )
44 | });
45 |
46 | return (
47 |
48 |
49 |

50 |
51 |
52 |
53 | {Li}
54 |
55 |
56 | )
57 | }
58 | }
59 |
60 | export default LogoSelect
61 |
--------------------------------------------------------------------------------
/src/components/search-input.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import {render} from 'react-dom'
3 | import $ from 'jquery'
4 |
5 | import LogoSelect from './logo-select'
6 |
7 | class SearchInput extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | keyword: '',
12 | showList: [],
13 | listIndex: 0,
14 | searchSrcs: ['https://www.so.com/s?ie=utf-8&shb=1&src=360sou_newhome&q=', 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=', 'https://www.sogou.com/web?query='],
15 | searchSrc: 'https://www.so.com/s?ie=utf-8&shb=1&src=360sou_newhome&q='
16 | }
17 | }
18 |
19 | //处理输入
20 | handleInput(ev) {
21 | var _this = this;
22 | _this.setState({
23 | keyword: ev.target.value
24 | }, function () {
25 | //jsonp获取数据
26 | $.ajax({
27 | url: 'https://sug.so.360.cn/suggest?word=' + this.state.keyword + '&encodein=utf-8&encodeout=utf-8',
28 | type: 'get',
29 | dataType: "jsonp"
30 | }).done(function (data) {
31 | var list = data.s;
32 | _this.setState({
33 | showList: list
34 | })
35 | })
36 | })
37 | }
38 |
39 | //处理鼠标hover
40 | handleMouseSelect(ev) {
41 | var index = ev.target.getAttribute('data-index');
42 | this.setState({
43 | listIndex: parseInt(index)
44 | })
45 | }
46 |
47 | //处理点击列表
48 | handleSelectClick(ev) {
49 | this.setState({
50 | keyword: ev.target.innerText
51 | }, function () {
52 | this.refs.input.value = this.state.keyword;
53 | setTimeout(()=> {
54 | this.handleSearch();
55 | }, 50);
56 | })
57 | }
58 |
59 | //处理点击清除按钮
60 | handleClearClick() {
61 | this.setState({
62 | keyword: '',
63 | showList: []
64 | });
65 | this.refs.input.value = '';
66 | }
67 |
68 | //处理搜索
69 | handleSearch() {
70 | var _state = this.state;
71 | window.location.href = _state.searchSrc + _state.keyword;
72 | }
73 |
74 | //处理Enter键
75 | handleKeyEnter(ev) {
76 | var keyCode = ev.keyCode;
77 | switch (keyCode) {
78 | case 13:
79 | this.handleSearch();
80 | break;
81 | case 38:
82 | this.selectUpAndDown(ev, keyCode);
83 | break;
84 | case 40:
85 | this.selectUpAndDown(ev, keyCode);
86 | break;
87 |
88 | }
89 | }
90 |
91 | //上下键选择列表项
92 | selectUpAndDown(ev, keycode) {
93 | ev.preventDefault();
94 | var _state = this.state;
95 |
96 | var stateCb = function () {
97 | this.setState({
98 | keyword: this.state.showList[this.state.listIndex]
99 | }, function () {
100 | this.refs.input.value = this.state.keyword;
101 | });
102 | };
103 |
104 |
105 | if (keycode === 38) {
106 | this.setState({
107 | listIndex: _state.listIndex == 0 ? 9 : --_state.listIndex
108 | }, stateCb);
109 | } else if (keycode === 40) {
110 | this.setState({
111 | listIndex: _state.listIndex == 9 ? 0 : ++_state.listIndex
112 | }, stateCb);
113 | }
114 | }
115 |
116 | //logo选择与父组件通信
117 | onLogoChange(index) {
118 | this.setState({
119 | searchSrc: this.state.searchSrcs[index]
120 | })
121 | }
122 |
123 | render() {
124 | var _this = this;
125 | var _state = this.state;
126 |
127 | const Li = _state.showList.map((value, index)=>
128 |
133 | { value }
134 |
135 | );
136 |
137 | return (
138 |
159 | )
160 | }
161 | }
162 |
163 | export default SearchInput;
164 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import { render } from 'react-dom'
3 |
4 | import './assets/css/style.css'
5 |
6 | import App from './App'
7 |
8 | var app = document.getElementById('app');
9 |
10 | render(, app);
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | entry: './src/main.jsx',
5 | output: {
6 | path: path.join(__dirname, 'dist'),
7 | publicPath: './dist/',
8 | filename: 'main.bundle.js'
9 | },
10 | resolve: {
11 | extensions: ['.js', '.jsx']
12 | },
13 | module: {
14 | rules: [{
15 | test: /\.(js|jsx)$/,
16 | exclude: '/node_modules/',
17 | use: [{
18 | loader: 'babel-loader',
19 | options: {
20 | presets: ['es2015']
21 | }
22 | }]
23 | }, {
24 | test: /\.css/,
25 | use: [{
26 | loader: 'style-loader'
27 | }, {
28 | loader: 'css-loader'
29 | }]
30 | }, {
31 | test: /\.(png|jpg)$/,
32 | use: [{
33 | loader: 'url-loader',
34 | options: {
35 | limit: 10000,
36 | name: 'images/[name].[hash:7].[ext]'
37 | }
38 | }]
39 | }]
40 | }
41 | };
--------------------------------------------------------------------------------