yinweiwen
2 years ago
13 changed files with 274 additions and 31 deletions
@ -0,0 +1,17 @@ |
|||||
|
import React, { useEffect } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Spin, Card, Input } from 'antd'; |
||||
|
import '../style.less'; |
||||
|
|
||||
|
// todo AutoComplete
|
||||
|
const SerachBar= (props) => { |
||||
|
const { dispatch, actions, user, loading } = props |
||||
|
|
||||
|
return ( |
||||
|
<div className="parent"> |
||||
|
<Input className="search" type="text" placeholder=" "></Input> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default SerachBar; |
@ -1,5 +1,6 @@ |
|||||
'use strict'; |
'use strict'; |
||||
|
|
||||
import Search from './search'; |
import Search from './search'; |
||||
|
import SearchRes from './searchRes'; |
||||
|
|
||||
export { Search }; |
export { Search, SearchRes }; |
@ -0,0 +1,142 @@ |
|||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Affix, List, Input, Space, Empty } from 'antd'; |
||||
|
import '../style.less'; |
||||
|
import { |
||||
|
DatabaseOutlined, |
||||
|
} from '@ant-design/icons'; |
||||
|
import { push } from 'react-router-redux' |
||||
|
|
||||
|
const IconText = ({ icon, text }) => ( |
||||
|
<Space> |
||||
|
{React.createElement(icon)} |
||||
|
{text} |
||||
|
</Space> |
||||
|
); |
||||
|
|
||||
|
function getHighlightedText(text, highlight) { |
||||
|
// Split text on highlight term, include term itself into parts, ignore case
|
||||
|
const parts = text.split(new RegExp(`(${highlight})`, 'gi')); |
||||
|
return <span>{parts.map(part => part.toLowerCase() === highlight.toLowerCase() ? <span style={{ color: "red" }}><b>{part}</b></span> : part)}</span>; |
||||
|
} |
||||
|
|
||||
|
const searchRes = (props) => { |
||||
|
const { dispatch, actions, user, loading, searchDatas } = props |
||||
|
|
||||
|
const [controlSearchData, setControlSearchData] = useState([]) |
||||
|
|
||||
|
// params
|
||||
|
const keyword = props.match?.params?.wd |
||||
|
|
||||
|
const [inputValue, setInputValue] = useState(keyword) |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (!!keyword && keyword.length > 0) { |
||||
|
dispatch(actions.search.search(keyword)) |
||||
|
} |
||||
|
}, [keyword]) |
||||
|
|
||||
|
useEffect(() => { |
||||
|
setControlSearchData(searchDatas) |
||||
|
}, [searchDatas]) |
||||
|
|
||||
|
const onSearch = (v) => { |
||||
|
console.log(`search for ${v}`) |
||||
|
dispatch(actions.search.search(v)) |
||||
|
} |
||||
|
|
||||
|
const onSearchEnv = (v) => { |
||||
|
console.log(`search for ${v.target.value}`) |
||||
|
dispatch(actions.search.search(v.target.value)) |
||||
|
} |
||||
|
|
||||
|
const SafeSearch = (v) => { |
||||
|
let kwd = '' |
||||
|
if (typeof v === "string") { |
||||
|
kwd = v; |
||||
|
} else { |
||||
|
kwd = v.target.value; |
||||
|
} |
||||
|
if (kwd) { |
||||
|
dispatch(push(`/s/${kwd}`)) |
||||
|
} else { |
||||
|
setControlSearchData([]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div className="parent-top"> |
||||
|
<Input.Search |
||||
|
className="search-top" |
||||
|
placeholder=" " |
||||
|
onSearch={SafeSearch} |
||||
|
enterButton="Search" |
||||
|
allowClear |
||||
|
size="large" |
||||
|
onPressEnter={SafeSearch} |
||||
|
maxLength={255} |
||||
|
value={inputValue} |
||||
|
onChange={(e) => setInputValue(e.target.value)} |
||||
|
/> |
||||
|
{ |
||||
|
controlSearchData && controlSearchData.length > 0 ? |
||||
|
<List |
||||
|
className='search-list' |
||||
|
itemLayout="vertical" |
||||
|
size="large" |
||||
|
pagination={{ |
||||
|
position: "both", |
||||
|
showQuickJumper: true, |
||||
|
onChange: (page) => { |
||||
|
console.log(page); |
||||
|
}, |
||||
|
pageSize: 10, |
||||
|
}} |
||||
|
dataSource={controlSearchData} |
||||
|
footer={ |
||||
|
<div> |
||||
|
<b>Free-Sun</b> 搜索结果 |
||||
|
</div> |
||||
|
} |
||||
|
renderItem={(item) => ( |
||||
|
<List.Item |
||||
|
key={item.title} |
||||
|
actions={[ |
||||
|
<IconText icon={DatabaseOutlined} text="from iota database" />, |
||||
|
]} |
||||
|
extra={ |
||||
|
item.ext?.img ? |
||||
|
<img |
||||
|
width={200} |
||||
|
alt="img" |
||||
|
src={item.ext.img} |
||||
|
/> : null |
||||
|
} |
||||
|
> |
||||
|
<List.Item.Meta |
||||
|
title={<a href={item.link}>{getHighlightedText(item.title, keyword)}</a>} |
||||
|
description={item.ext?.desc ? item.ext.desc : undefined} |
||||
|
// description={item.content}
|
||||
|
/> |
||||
|
{getHighlightedText(item.content, keyword)} |
||||
|
</List.Item> |
||||
|
)} |
||||
|
/> : <Empty className='search-empty' /> |
||||
|
} |
||||
|
|
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
function mapStateToProps(state) { |
||||
|
const { auth, global, searchRes } = state; |
||||
|
console.log(searchRes) |
||||
|
return { |
||||
|
loading: searchRes.isRequesting, |
||||
|
user: auth.user, |
||||
|
actions: global.actions, |
||||
|
searchDatas: searchRes.data || [], |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(searchRes); |
File diff suppressed because one or more lines are too long
@ -1,12 +1,18 @@ |
|||||
'use strict'; |
'use strict'; |
||||
import { Search, } from './containers'; |
import { Search, SearchRes } from './containers'; |
||||
|
|
||||
export default [{ |
export default [{ |
||||
type: 'inner', |
type: 'inner', |
||||
route: { |
route: { |
||||
path: '/search', |
path: '/', |
||||
key: 'search', |
key: 'search', |
||||
breadcrumb: '搜索引擎', |
breadcrumb: '搜索引擎', |
||||
component: Search |
component: Search, |
||||
|
childRoutes: [{ |
||||
|
path: '/s/:wd', |
||||
|
key: 'searchRes', |
||||
|
breadcrumb: '搜索结果', |
||||
|
component: SearchRes, |
||||
|
}] |
||||
} |
} |
||||
}]; |
}]; |
@ -1,3 +1,48 @@ |
|||||
#example:hover { |
#example:hover { |
||||
font-size: larger; |
font-size: larger; |
||||
} |
} |
||||
|
.search { |
||||
|
width: 500px; |
||||
|
font: 16px arial,sans-serif; |
||||
|
} |
||||
|
// .search:hover, .search:focus { |
||||
|
// outline: none; |
||||
|
// box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08); |
||||
|
// } |
||||
|
|
||||
|
.parent{ |
||||
|
display:flex; |
||||
|
flex-direction: column; |
||||
|
// justify-content: center; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.parent-top{ |
||||
|
margin-left: 20%; |
||||
|
margin-bottom: 10%; |
||||
|
display:flex; |
||||
|
flex-wrap: nowrap; |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
align-items: flex-start; // 垂直居中展示 |
||||
|
} |
||||
|
|
||||
|
.search-top { |
||||
|
width: 75%; |
||||
|
height: 50px; |
||||
|
font: 16px arial,sans-serif; |
||||
|
// margin:auto; |
||||
|
} |
||||
|
|
||||
|
.search-empty{ |
||||
|
align-self: center; |
||||
|
} |
||||
|
|
||||
|
.search-content { |
||||
|
width: 75%; |
||||
|
} |
||||
|
|
||||
|
.search-list{ |
||||
|
width: 75%; |
||||
|
} |
Binary file not shown.
Loading…
Reference in new issue