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'), loader: () => import('./modules/login/EducoderLogin'),
loading: Loading, loading: Loading,
}) })
const Search = Loadable({
loader: () => import('./modules/search/'),
loading: Loading,
})
class App extends Component { class App extends Component {
constructor(props) { constructor(props) {
@ -247,6 +251,10 @@ class App extends Component {
</Route> </Route>
{/*404*/} {/*404*/}
<Route path="/nopage" component={Shixunnopage} /> <Route path="/nopage" component={Shixunnopage} />
{/* 查询 */}
<Route path="/search" component={Search} />
{/* 个人主页 */} {/* 个人主页 */}
<Route path="/users/:username" <Route path="/users/:username"
render={ 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)=>{ list && list.map((i,k)=>{
return( 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 { Input , notification , Dropdown , Menu } from 'antd';
import LoginDialog from '../../modules/login/LoginDialog'; import LoginDialog from '../../modules/login/LoginDialog';
import GotoQQgroup from '../../modal/GotoQQgroup';
import HeadSearch from '../Component/HeadSearch';
import AddProjectModal from './AddProjectModal'; import AddProjectModal from './AddProjectModal';
import '../../modules/tpm/TPMIndex.css'; import '../../modules/tpm/TPMIndex.css';
@ -76,36 +79,35 @@ class NewHeader extends Component {
} catch (e) {} } catch (e) {}
} }
SearchInput = (open,item)=>{ // SearchInput = (open,item)=>{
if(open){ // if(open){
return( // return(
<div // <div
onBlur={() => { // onBlur={() => {
setTimeout(() => { // setTimeout(() => {
this.setState({ // this.setState({
openSearch:false // openSearch:false
}) // })
}, 300) // }, 300)
}} // }}
> // >
<Search placeholder="请输入搜索关键字" // <Search placeholder="实践课程/教学课堂/实践项目/交流问答"
className={`search-input mr20`} // className={`search-input mr20`}
onSearch={(value)=>this.onGlobalSearch(value,item)} // onSearch={(value)=>this.onGlobalSearch(value,item)}
autoFocus={true} // autoFocus={true}
style={{width:"260px"}} // />
/> // </div>
</div> // )
) // }else{
}else{ // return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => { // this.setState({openSearch:true})
this.setState({openSearch:true}) // }} />
}} /> // }
} // }
}
onGlobalSearch=(value,item)=>{ // onGlobalSearch=(value,item)=>{
window.location.href=`${item}?value=` + value; // window.location.href=`${item}?value=` + value;
} // }
openNotification = (messge) => { openNotification = (messge) => {
notification.open({ notification.open({
@ -264,7 +266,7 @@ class NewHeader extends Component {
{ {
list.map((item,key)=>{ list.map((item,key)=>{
return( 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; let notice_url = settings && settings.common && settings.common.notice;
return ( return (
<div className="newHeaders" id="nHeader"> <div className="newHeaders" id="nHeader">
@ -447,7 +449,8 @@ class NewHeader extends Component {
} }
</div> </div>
<div className="head-right"> <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)? 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"> <Dropdown overlay={this.addMenu(settings && settings.add)} placement="bottomRight">

View File

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

View File

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

View File

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