Merge branch 'develop'

This commit is contained in:
caishi 2021-06-25 09:14:50 +08:00
commit b44b6530e5
11 changed files with 480 additions and 48 deletions

BIN
public/favicon.ico Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -73,6 +73,10 @@ const EducoderLogin = Loadable({
loader: () => import('./modules/login/EducoderLogin'),
loading: Loading,
})
const Search = Loadable({
loader: () => import('./modules/search/'),
loading: Loading,
})
class App extends Component {
constructor(props) {
@ -247,6 +251,10 @@ class App extends Component {
</Route>
{/*404*/}
<Route path="/nopage" component={Shixunnopage} />
{/* 查询 */}
<Route path="/search" component={Search} />
{/* 个人主页 */}
<Route path="/users/:username"
render={

View File

@ -0,0 +1,41 @@
import React, { useState } from "react";
import { Input ,notification} from "antd";
const { Search } = Input;
export default ({history}) => {
const [openSearch, setOpenSearch] = useState(false);
function onGlobalSearch(value) {
history.push('/search?value=' + value);
// window.location.href = `search?value=` + value;
// history.push({
// pathname:'/search',
// state:value
// })
}
return (
<React.Fragment>
{
openSearch ?
<div
onBlur={() => {
setTimeout(() => {
setOpenSearch(false)
}, 500)
}}
>
<Search placeholder="请输入搜索关键字"
className={`search-input mr20`}
onSearch={onGlobalSearch}
autoFocus={true}
style={{width:'260px'}}
/>
</div>
:
<i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
setOpenSearch(true)
}} />
}
</React.Fragment>
)
};

View File

@ -43,7 +43,7 @@ function SiderBar() {
{
list && list.map((i,k)=>{
return(
<li><a href={`${i.url}`} title={i.question} target="_blank">{i.question}</a></li>
<li key={i.question+k}><a href={`${i.url}`} title={i.question} target="_blank">{i.question}</a></li>
)
})
}

View File

@ -5,6 +5,9 @@ import axios from 'axios';
import { Input , notification , Dropdown , Menu } from 'antd';
import LoginDialog from '../../modules/login/LoginDialog';
import GotoQQgroup from '../../modal/GotoQQgroup';
import HeadSearch from '../Component/HeadSearch';
import AddProjectModal from './AddProjectModal';
import '../../modules/tpm/TPMIndex.css';
@ -76,36 +79,35 @@ class NewHeader extends Component {
} catch (e) {}
}
SearchInput = (open,item)=>{
if(open){
return(
<div
onBlur={() => {
setTimeout(() => {
this.setState({
openSearch:false
})
}, 300)
}}
>
<Search placeholder="请输入搜索关键字"
className={`search-input mr20`}
onSearch={(value)=>this.onGlobalSearch(value,item)}
autoFocus={true}
style={{width:"260px"}}
/>
</div>
)
}else{
return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
this.setState({openSearch:true})
}} />
}
}
// SearchInput = (open,item)=>{
// if(open){
// return(
// <div
// onBlur={() => {
// setTimeout(() => {
// this.setState({
// openSearch:false
// })
// }, 300)
// }}
// >
// <Search placeholder="实践课程/教学课堂/实践项目/交流问答"
// className={`search-input mr20`}
// onSearch={(value)=>this.onGlobalSearch(value,item)}
// autoFocus={true}
// />
// </div>
// )
// }else{
// return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
// this.setState({openSearch:true})
// }} />
// }
// }
onGlobalSearch=(value,item)=>{
window.location.href=`${item}?value=` + value;
}
// onGlobalSearch=(value,item)=>{
// window.location.href=`${item}?value=` + value;
// }
openNotification = (messge) => {
notification.open({
@ -264,7 +266,7 @@ class NewHeader extends Component {
{
list.map((item,key)=>{
return(
(item.name !=="加入课堂" && item.name !=="加入开发项目") && <Menu.Item><a href={item.url}>{item.name}</a></Menu.Item>
(item.name !=="加入课堂" && item.name !=="加入开发项目") && <Menu.Item key={item.name+key}><a href={item.url}>{item.name}</a></Menu.Item>
)
})
}
@ -383,7 +385,7 @@ class NewHeader extends Component {
})
}
let search_url = settings && settings.common && settings.common.search;
// let search_url = settings && settings.common && settings.common.search;
let notice_url = settings && settings.common && settings.common.notice;
return (
<div className="newHeaders" id="nHeader">
@ -447,7 +449,8 @@ class NewHeader extends Component {
}
</div>
<div className="head-right">
{search_url ? this.SearchInput(openSearch,search_url):""}
{/* {search_url ? this.SearchInput(openSearch,search_url):""} */}
<HeadSearch {...this.props}/>
{
current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)?
<Dropdown overlay={this.addMenu(settings && settings.add)} placement="bottomRight">

View File

@ -107,6 +107,7 @@ function CoderDepot(props){
setMainFlag(true);
setReadOnly(true);
setReadme(result.data.readme);
setHide(true);
}
setTimeout(function(){setIsSpin(false);},500);
}).catch(error=>{setIsSpin(false);})
@ -118,7 +119,7 @@ function CoderDepot(props){
let ele = document.getElementById("ptxt");
if(ele){
let h = ele.offsetHeight;
if( h > 18 ) setHideBtn(true)
if( h > 18 ) setHideBtn(true);
}
}
},[projectDetail,lastCommit])
@ -149,6 +150,7 @@ function CoderDepot(props){
setLastCommitAuthor(c && c.committer);
setMainFlag(false);
setReadOnly(true);
setHide(true);
}
setTimeout(function(){setIsSpin(false);},500)
}).catch(error=>{setIsSpin(false);})
@ -332,7 +334,9 @@ function CoderDepot(props){
lastCommit &&
<div className="listtablehead">
<User url={getImageUrl(`/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} id={lastCommitAuthor && lastCommitAuthor.id} login={lastCommitAuthor && lastCommitAuthor.login}/>
<div className={hideBtn && hide ? "ellipsistxt hide" :"ellipsistxt"}><pre id="ptxt">{lastCommit && lastCommit.message}</pre></div>
<div className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}>
<pre id="ptxt">{lastCommit && lastCommit.message}</pre>
</div>
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
<span className="ml12 color-grey-9 mt3">{lastCommit && lastCommit.time_from_now}</span>

View File

@ -200,35 +200,39 @@
.listtablehead{
display: flex;
justify-content: space-between;
align-items: center;
align-items: flex-start;
border-bottom: 1px solid #d9d9d9;
padding:7px 20px;
border-radius: 4px 4px 0px 0px;
background-color: #FAFBFC;
.ellipsistxt{
margin-top: 6px;
#ptxt{
margin-bottom: 0px;
word-break: break-all;
overflow: unset;
white-space:pre-wrap; /* css3.0 */
white-space:-moz-pre-wrap; /* Firefox */
white-space:-pre-wrap; /* Opera 4-6 */
white-space:-o-pre-wrap; /* Opera 7 */
word-wrap:break-word;
}
margin-left: 13px;
line-height:18px;
flex:1;
width: 0;
color: #666;
&>p{
word-break:break-all;
}
&.hide{
&.hidetxt{
height: 18px;
overflow: hidden;
position: relative;
padding-right:8px;
}
&.hide::after{
position: absolute;
right: 0px;
bottom: 0px;
content:"...";
&::after{
position: absolute;
right: 0px;
bottom: 0px;
content:"...";
}
}
}
.ellipsis{

View File

@ -437,6 +437,8 @@ class order extends Component {
status_id: select_params.update_status_id
}).then(result => {
if (result) {
const { getDetail } = this.props;
(select_params && select_params.update_status_id) && getDetail && getDetail();
this.props.showNotification("修改成功!");
this.successFunc();
}
@ -450,7 +452,6 @@ class order extends Component {
const { status_type } = this.state;
this.getIssueList(status_type);
}
resetSelectParams = () => {
let select_params = this.state.select_params;
select_params.update_author_id = undefined;
@ -481,6 +482,8 @@ class order extends Component {
ids: checkedValue
}).then(result => {
if (result) {
const { getDetail } = this.props;
getDetail && getDetail();
this.props.showNotification("删除成功!");
this.successFunc();
}

View File

@ -0,0 +1,31 @@
import React from 'react';
import './index.scss';
export default (props) => {
const { list } = props;
function itemClick(item) {
item.url && window.open(item.url);
}
return (
list.map((item, i) => {
return (
<div className="search-item" key={item.id}>
<div className="search-item-tit">
<h3 className="search-item-title" dangerouslySetInnerHTML={{ __html: item.title }} onClick={() => { itemClick(item) }}></h3>
{item.type == 1 && <p>
<span className="search-icon"><i className="iconfont icon-dianjiliang mr3 font-12" />{item.watchersCount}</span>
<span className="search-icon"><i className="iconfont icon-kongxing mr3 font-16" />{item.praisesCount}</span>
<span className="search-icon"><i className="iconfont icon-fork mr3 font-16" />{item.forkedCount}</span>
</p>}
</div >
<p className="search-item-content" dangerouslySetInnerHTML={{ __html: item.content }}></p>
<div>{item.updateTime}</div>
</div>
)
})
)
}

View File

@ -0,0 +1,249 @@
import React, { useEffect, useState } from 'react';
import { Input, Row, Col, Tabs, Pagination } from 'antd';
import axios from 'axios';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { SnackbarHOC } from 'educoder';
import ItemList from './ItemList';
import Nodata from '../../forge/Nodata';
import './index.scss';
const { Search } = Input;
const { TabPane } = Tabs;
// const https = 'http://192.168.0.77:8081'; //
// const https = 'http://192.168.31.104:8081'; //
// const https='http://106.75.31.211:58081';
const https = 'https://test-statistics.trustie.net';
const GlobalSearch = ({ location, showNotification, history }) => {
const size = 10;
let defaultValue = decodeURI(location.search.split("=")[1] || "");
const [term, setTerm] = useState(defaultValue);
const [searchValue, setSearchValue] = useState(defaultValue);
const [type, setType] = useState(1);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [dataList, setDataList] = useState([]);
const [forcesearch, setForcesearch] = useState(false);
const [totalType1, setTotalType1] = useState(0);
const [totalType2, setTotalType2] = useState(0);
const [totalType3, setTotalType3] = useState(0);
const [totalType4, setTotalType4] = useState(0);
const [totalType5, setTotalType5] = useState(0);
const [ref, setRef] = useState(undefined);
const [focus, setFocus] = useState(0);
useEffect(() => {
searchDataList();
}, [type, page, term, forcesearch]);
useEffect(() => {
if (ref) {
ref && ref.input.input.focus();
}
}, [focus])
function searchFun(val) {
setTerm(val);
setPage(1);
setForcesearch(!forcesearch);
}
function searchDataList() {
const url = https + '/search';
if (!term) {
// showNotification('');
setFocus(focus + 1);
return;
}
axios.defaults.withCredentials = true;
axios.get(url, {
params: {
page,
size,
term,
type,
}
}).then(res => {
if (res && res.status === 200 && res.data && res.data.code === '1') {
const data = res.data.data;
setDataList(data.rows);
setTotal(data.total);
for (const item of data.searchItemTypes) {
if (item.type == 1) {
setTotalType1(item.count);
} else if (item.type == 2) {
setTotalType2(item.count);
} else if (item.type == 3) {
setTotalType3(item.count);
} else if (item.type == 4) {
setTotalType4(item.count);
} else if (item.type == 5) {
setTotalType5(item.count);
}
}
} else if (res && res.data) {
showNotification(res.data.data.message);
setDataList([]);
setTotal(0);
} else {
showNotification('查询失败!');
setDataList([]);
setTotal(0);
}
}).catch(err => {
showNotification('查询失败!返回错误');
setDataList([]);
setTotal(0);
})
}
function changeTab(type) {
setType(type);
setPage(1);
}
useEffect(() => {
history.listen(historyLocation => {
setSearchValue(historyLocation.search.split("=")[1] || "");
setTerm(historyLocation.search.split("=")[1] || "");
})
}, [history]);
return (
<div className="suit-main clearfix">
<div className="search-head">
<Row className="search-box">
<Col xs={20} sm={16} lg={13} >
<Search
placeholder="请输入搜索关键字"
enterButton="搜索"
size="large"
onSearch={searchFun}
className={{ 'global-search': true, "required-search": !searchValue }}
value={searchValue}
onChange={(e) => { setSearchValue(e.target.value) }}
ref={(el) => setRef(el)}
/>
{!searchValue && <span className="ant-form-explain">请输入搜索关键字</span>}
</Col>
</Row>
</div>
<Tabs defaultActiveKey="1" onChange={changeTab}>
<TabPane tab={`项目(${totalType1}`} key={1}>
<div className="search-content">
<p>{`找到${totalType1}条结果`}</p>
<ItemList
list={dataList}
/>
</div>
{
dataList.length ?
<Pagination
showQuickJumper={dataList.length > size}
onChange={(page) => { setPage(page) }}
current={page}
total={total}
showTotal={total => `${total}`}
/>
: <Nodata _html="暂无数据" className="no-data-box" />
}
</TabPane>
<TabPane tab={`帖子(${totalType2}`} key="2">
<div className="search-content">
<p>{`找到${totalType2}条结果`}</p>
<ItemList
list={dataList}
/>
</div>
{
dataList.length ?
<Pagination
showQuickJumper={dataList.length > size}
onChange={(page) => { setPage(page) }}
current={page}
total={total}
showTotal={total => `${total}`}
/> : <Nodata _html="暂无数据" />
}
</TabPane>
{/* <TabPane tab={`${totalType3}`} key={3}>
<div className="search-content">
<p>{`找到${totalType3}条结果`}</p>
<ItemList
list={dataList}
/>
</div>
{
dataList.length ?
<Pagination
showQuickJumper={dataList.length > size}
onChange={(page) => { setPage(page) }}
current={page}
total={total}
showTotal={total => `${total}`}
/> : <Nodata _html="暂无数据" />
}
</TabPane>
<TabPane tab={`资源(${totalType4}`} key="4">
<div className="search-content">
<p>{`找到${totalType4}条结果`}</p>
<ItemList
list={dataList}
/>
</div>
{
dataList.length ?
<Pagination
showQuickJumper={dataList.length > size}
onChange={(page) => { setPage(page) }}
current={page}
total={total}
showTotal={total => `${total}`}
/> : <Nodata _html="暂无数据" />
}
</TabPane> */}
<TabPane tab={`易修(${totalType5}`} key="5">
<div className="search-content">
<p>{`找到${totalType5}条结果`}</p>
<ItemList
list={dataList}
/>
</div>
{
dataList.length ?
<Pagination
showQuickJumper={dataList.length > size}
onChange={(page) => { setPage(page) }}
current={page}
total={total}
showTotal={total => `${total}`}
/> : <Nodata _html="暂无数据" />
}
</TabPane>
</Tabs>
</div>
);
}
export default SnackbarHOC()(TPMIndexHOC(GlobalSearch));

View File

@ -0,0 +1,89 @@
.suit-main {
.search-head {
background: #eef2f5;
}
.search-box {
width: 1200px;
height: 110px;
margin: 0 auto;
.ant-form-explain{
color: #f5222d;
}
}
.global-search {
margin-top: 40px;
}
.required-search{
.ant-input{
border-color: #f5222d !important;
}
}
.ant-tabs-top {
background: #eef2f5;
}
.ant-tabs-tabpane {
background: #fff;
}
.ant-tabs-bar {
width: 1200px;
margin: 0 auto;
}
.ant-tabs-tab-active {
color: #000;
font-weight: 600;
}
.ant-tabs-nav .ant-tabs-tab:hover {
color: #000;
font-weight: 600;
}
/* 列表 */
.search-content {
width: 1200px;
margin: 1.5vw auto;
p{
margin-bottom: .75em !important;
}
}
.search-item {
padding: .75em 0;
border-top:1px solid #e1e4e8;
.search-item-tit{
display: flex;
justify-content: space-between;
}
.search-item-title{
cursor: pointer;
&:hover{
color: #1890ff;
}
span{
color: #1890ff;
}
}
.search-item-content{
span{
color: #1890ff;
}
}
.search-icon{
margin-right: 2em;
color: #aaa;
}
}
.ant-pagination{
text-align: center;
margin-bottom: 3vw;
}
.none_panels{
display: flex;
justify-content: center;
align-items: center;
flex-flow: column nowrap;
height: 40vh;
}
}