Browse Source

searchbar

master
yinweiwen 2 years ago
parent
commit
d309ad30c7
  1. 18
      code/web/client/assets/color.less
  2. 6
      code/web/client/src/layout/components/header/index.js
  3. 6
      code/web/client/src/layout/containers/layout/index.js
  4. 12
      code/web/client/src/sections/search/actions/search.js
  5. 17
      code/web/client/src/sections/search/components/SearchBar.js
  6. 3
      code/web/client/src/sections/search/containers/index.js
  7. 34
      code/web/client/src/sections/search/containers/search.js
  8. 142
      code/web/client/src/sections/search/containers/searchRes.js
  9. 6
      code/web/client/src/sections/search/containers/toplogo.js
  10. 12
      code/web/client/src/sections/search/routes.js
  11. 47
      code/web/client/src/sections/search/style.less
  12. 2
      code/web/client/src/utils/webapi.js
  13. BIN
      code/web/node_modules.rar

18
code/web/client/assets/color.less

@ -1147,10 +1147,10 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
.ant-mentions-dropdown-menu-item-active {background-color: @item-hover-bg;} .ant-mentions-dropdown-menu-item-active {background-color: @item-hover-bg;}
.ant-menu-item-danger.ant-menu-item {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item:hover, .ant-menu-item-danger.ant-menu-item-active {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-item-danger.ant-menu-item-active {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item:active {background: color(~`colorPalette("@{table-header-sort-bg}", 4)`);} .ant-menu-item-danger.ant-menu-item:active {background: color(~`colorPalette("@{calendar-input-bg}", 1)`);}
.ant-menu-item-danger.ant-menu-item-selected {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item-selected {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item-selected > a, .ant-menu-item-danger.ant-menu-item-selected > a:hover {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item-selected > a, .ant-menu-item-danger.ant-menu-item-selected > a:hover {color: #ff4d4f;}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: color(~`colorPalette("@{table-header-sort-bg}", 4)`);} .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: color(~`colorPalette("@{calendar-input-bg}", 1)`);}
.ant-menu-inline .ant-menu-item-danger.ant-menu-item::after {border-right-color: #ff4d4f;} .ant-menu-inline .ant-menu-item-danger.ant-menu-item::after {border-right-color: #ff4d4f;}
.ant-menu-dark .ant-menu-item-danger.ant-menu-item, .ant-menu-dark .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-dark .ant-menu-item-danger.ant-menu-item > a {color: #ff4d4f;} .ant-menu-dark .ant-menu-item-danger.ant-menu-item, .ant-menu-dark .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-dark .ant-menu-item-danger.ant-menu-item > a {color: #ff4d4f;}
.ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;} .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;}
@ -1727,11 +1727,11 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-checkable:active, .ant-tag-checkable-checked {color: #fff;} .ant-tag-checkable:active, .ant-tag-checkable-checked {color: #fff;}
.ant-tag-checkable-checked {background-color: @primary-color;} .ant-tag-checkable-checked {background-color: @primary-color;}
.ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);} .ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);}
.ant-tag-pink {color: #c41d7f;background: color(~`colorPalette("@{picker-basic-cell-active-with-range-color}", 1)`);border-color: #ffadd2;} .ant-tag-pink {color: #c41d7f;background: color(~`colorPalette("@{table-header-cell-split-color}", 4)`);border-color: #ffadd2;}
.ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;} .ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-magenta {color: #c41d7f;background: color(~`colorPalette("@{picker-basic-cell-active-with-range-color}", 1)`);border-color: #ffadd2;} .ant-tag-magenta {color: #c41d7f;background: color(~`colorPalette("@{table-header-cell-split-color}", 4)`);border-color: #ffadd2;}
.ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;} .ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-red {color: #cf1322;background: color(~`colorPalette("@{table-header-sort-bg}", 4)`);border-color: #ffa39e;} .ant-tag-red {color: #cf1322;background: color(~`colorPalette("@{calendar-input-bg}", 1)`);border-color: #ffa39e;}
.ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;} .ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;}
.ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;} .ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;}
.ant-tag-volcano-inverse {color: #fff;background: #fa541c;border-color: #fa541c;} .ant-tag-volcano-inverse {color: #fff;background: #fa541c;border-color: #fa541c;}
@ -1739,7 +1739,7 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;} .ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;}
.ant-tag-yellow {color: #d4b106;background: #feffe6;border-color: #fffb8f;} .ant-tag-yellow {color: #d4b106;background: #feffe6;border-color: #fffb8f;}
.ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;} .ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;}
.ant-tag-gold {color: #d48806;background: #fffbe6;border-color: #ffe58f;} .ant-tag-gold {color: #d48806;background: color(~`colorPalette("@{btn-shadow}", 1)`);border-color: #ffe58f;}
.ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;} .ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;}
.ant-tag-cyan {color: #08979c;background: #e6fffb;border-color: #87e8de;} .ant-tag-cyan {color: #08979c;background: #e6fffb;border-color: #87e8de;}
.ant-tag-cyan-inverse {color: #fff;background: #13c2c2;border-color: #13c2c2;} .ant-tag-cyan-inverse {color: #fff;background: #13c2c2;border-color: #13c2c2;}
@ -1747,11 +1747,11 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-lime-inverse {color: #fff;background: #a0d911;border-color: #a0d911;} .ant-tag-lime-inverse {color: #fff;background: #a0d911;border-color: #a0d911;}
.ant-tag-green {color: #389e0d;background: #f6ffed;border-color: #b7eb8f;} .ant-tag-green {color: #389e0d;background: #f6ffed;border-color: #b7eb8f;}
.ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;} .ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;}
.ant-tag-blue {color: #096dd9;background: #e6f7ff;border-color: #91d5ff;} .ant-tag-blue {color: #096dd9;background: color(~`colorPalette("@{transfer-item-hover-bg}", 1)`);border-color: #91d5ff;}
.ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;} .ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;}
.ant-tag-geekblue {color: #1d39c4;background: #f0f5ff;border-color: #adc6ff;} .ant-tag-geekblue {color: #1d39c4;background: #f0f5ff;border-color: #adc6ff;}
.ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;} .ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;}
.ant-tag-purple {color: #531dab;background: color(~`colorPalette("@{layout-trigger-background}", 2)`);border-color: #d3adf7;} .ant-tag-purple {color: #531dab;background: #f9f0ff;border-color: #d3adf7;}
.ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;} .ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;}
.ant-tag-success {color: #52c41a;background: @success-color-deprecated-bg;border-color: @success-color-deprecated-border;} .ant-tag-success {color: #52c41a;background: @success-color-deprecated-bg;border-color: @success-color-deprecated-border;}
.ant-tag-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;} .ant-tag-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;}
@ -1988,7 +1988,7 @@ a.ant-typography.ant-typography-disabled:hover, .ant-typography a.ant-typography
.ant-upload-list-picture .ant-upload-list-item-error, .ant-upload-list-picture-card .ant-upload-list-item-error {border-color: #ff4d4f;} .ant-upload-list-picture .ant-upload-list-item-error, .ant-upload-list-picture-card .ant-upload-list-item-error {border-color: #ff4d4f;}
.ant-upload-list-picture .ant-upload-list-item:hover .ant-upload-list-item-info, .ant-upload-list-picture-card .ant-upload-list-item:hover .ant-upload-list-item-info {background: transparent;} .ant-upload-list-picture .ant-upload-list-item:hover .ant-upload-list-item-info, .ant-upload-list-picture-card .ant-upload-list-item:hover .ant-upload-list-item-info {background: transparent;}
.ant-upload-list-picture .ant-upload-list-item-uploading, .ant-upload-list-picture-card .ant-upload-list-item-uploading {border-style: dashed;} .ant-upload-list-picture .ant-upload-list-item-uploading, .ant-upload-list-picture-card .ant-upload-list-item-uploading {border-style: dashed;}
.ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#e6f7ff'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#e6f7ff'] {fill: @error-color-deprecated-bg;} .ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='color(~`colorPalette("@{transfer-item-hover-bg}", 1)`)'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='color(~`colorPalette("@{transfer-item-hover-bg}", 1)`)'] {fill: @error-color-deprecated-bg;}
.ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'] {fill: #ff4d4f;} .ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'] {fill: #ff4d4f;}
.ant-upload-list-picture-card .ant-upload-list-item-info::before {background-color: rgba(0, 0, 0, 0.5);} .ant-upload-list-picture-card .ant-upload-list-item-info::before {background-color: rgba(0, 0, 0, 0.5);}
.ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-eye, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-download, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-delete {color: rgba(255, 255, 255, 0.85);} .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-eye, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-download, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-delete {color: rgba(255, 255, 255, 0.85);}

6
code/web/client/src/layout/components/header/index.js

@ -8,7 +8,7 @@ import lightVars from '$themes/light.json';
import exampleVars from '$themes/example.json' import exampleVars from '$themes/example.json'
import styles from './style.css'; import styles from './style.css';
import { import {
MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined,SmileOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
const themeMap = { const themeMap = {
@ -60,7 +60,9 @@ const Header = props => {
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />} {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</span> </span>
<div className={styles['header-title']} style={{}}> <div className={styles['header-title']} style={{}}>
安心云4.0 <Link to="/">
安心云4.0
</Link>
{/* <span>{user.orgName}</span> */} {/* <span>{user.orgName}</span> */}
</div> </div>
</div> </div>

6
code/web/client/src/layout/containers/layout/index.js

@ -56,9 +56,9 @@ const LayoutContainer = props => {
useEffect(() => { useEffect(() => {
NProgress.done(); NProgress.done();
if (!user || !user.authorized) { // if (!user || !user.authorized) {
history.push('/signin'); // history.push('/search');
} // }
if (msg) { if (msg) {
message.destroy(); message.destroy();
if (msg.done) { if (msg.done) {

12
code/web/client/src/sections/search/actions/search.js

@ -3,13 +3,13 @@
import { basicAction } from '@peace/utils' import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils' import { ApiTable } from '$utils'
export function getMembersW(orgId) { export function search(keyword){
return dispatch => basicAction({ return dispatch => basicAction({
type: 'get', type: 'get',
dispatch: dispatch, dispatch: dispatch,
actionType: 'GET_MEMBERS', actionType: 'GET_SEARCH',
url: `${ApiTable.getEnterprisesMembers.replace('{enterpriseId}', orgId)}`, url: `${ApiTable.search.replace('{wd}', keyword)}`,
msg: { error: '获取用户列表失败' }, msg: { error: '搜索失败' },
reducer: { name: 'members' } reducer: { name: 'searchRes' }
}); });
} }

17
code/web/client/src/sections/search/components/SearchBar.js

@ -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;

3
code/web/client/src/sections/search/containers/index.js

@ -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 };

34
code/web/client/src/sections/search/containers/search.js

@ -2,7 +2,8 @@ import React, { useEffect } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Spin, Card, Input } from 'antd'; import { Spin, Card, Input } from 'antd';
import '../style.less'; import '../style.less';
import './style.less'; import toplogo from './toplogo'
import { push } from 'react-router-redux'
const Search = (props) => { const Search = (props) => {
const { dispatch, actions, user, loading } = props const { dispatch, actions, user, loading } = props
@ -11,20 +12,41 @@ const Search = (props) => {
// dispatch(actions.search.getMembersW(user.orgId)) // dispatch(actions.search.getMembersW(user.orgId))
}, []) }, [])
const SafeSearch = (v) => {
let kwd = ''
if (typeof v === "string") {
kwd = v;
} else {
kwd = v.target.value;
}
if (kwd) {
dispatch(push(`/s/${kwd}`))
}
}
return ( return (
<div className="parent"> <div className="parent">
<Input className="search" type="text" placeholder=" "></Input> {toplogo}
<Input.Search
className="search"
placeholder=" "
onSearch={SafeSearch}
enterButton="Search"
allowClear
size="large"
onPressEnter={SafeSearch}
maxLength={255}
/>
{/* <Input className="search" type="text" placeholder=" "></Input> */}
</div> </div>
) )
} }
function mapStateToProps(state) { function mapStateToProps(state) {
const { auth, global, members } = state; const { auth, global } = state;
return { return {
loading: members.isRequesting,
user: auth.user, user: auth.user,
actions: global.actions, actions: global.actions
members: members.data
}; };
} }

142
code/web/client/src/sections/search/containers/searchRes.js

@ -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);

6
code/web/client/src/sections/search/containers/toplogo.js

File diff suppressed because one or more lines are too long

12
code/web/client/src/sections/search/routes.js

@ -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,
}]
} }
}]; }];

47
code/web/client/src/sections/search/style.less

@ -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%;
}

2
code/web/client/src/utils/webapi.js

@ -5,6 +5,8 @@ export const ApiTable = {
login: 'login', login: 'login',
logout: 'logout', logout: 'logout',
search: 'v1/search/{wd}',
getEnterprisesMembers: 'enterprises/{enterpriseId}/members', getEnterprisesMembers: 'enterprises/{enterpriseId}/members',
}; };

BIN
code/web/node_modules.rar

Binary file not shown.
Loading…
Cancel
Save