新增专区首页、专区文章

This commit is contained in:
黄心宇 2024-01-31 11:14:52 +08:00
parent 1b4c7e8b60
commit 51d1a143b8
19 changed files with 5484 additions and 7529 deletions

View File

@ -1,8 +1,20 @@
import app from './src/App'
import users from './src/forge/users/Index';
import Detail from "./src/forge/Main/Detail";
import Information from "./src/forge/Information/index"
import NewsDetail from './src/forge/Information/Pages/newsDetail';
export default [
{
key:"zone",
path: "/zone/:deptId",
component: app,
},
{
key: "zoneNews",
path: "/zone/:deptId/newdetail/:id",
component: app
},
{
key:"detail",
path: "/:owner/:projectsId",
@ -12,7 +24,17 @@ export default [
export const deepRoutes = [
{
key:"detail",
key: "zoneNews",
path: "/zone/:deptId/newdetail/:id",
component: NewsDetail
},
{
key: "zone",
path: "/zone/:deptId",
component: Information
},
{
key: "detail",
path: "/:owner/:projectsId",
component: Detail,
}

12615
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@
"@babel/preset-env": "^7.23.6",
"@babel/preset-react": "^7.23.3",
"@babel/preset-stage-2": "^7.8.3",
"@babel/runtime": "^7.0.0-beta.46",
"@babel/runtime-corejs3": "^7.23.6",
"@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
@ -77,6 +76,7 @@
"moment": "^2.23.0",
"monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.9.0",
"nodemon": "^3.0.3",
"numeral": "^2.0.6",
"nvm": "0.0.4",
"object-assign": "4.1.1",
@ -126,6 +126,7 @@
"redux": "^4.0.5",
"redux-thunk": "2.3.0",
"rsuite": "^4.3.4",
"sass": "^1.70.0",
"scroll-into-view": "^1.14.2",
"scrollama": "^3.2.0",
"sha1": "^1.1.1",
@ -205,6 +206,7 @@
"@babel/plugin-proposal-decorators": "^7.0.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.0.0-beta.46",
"babel-loader": "^8.3.0",
"babel-plugin-import": "^1.13.0",
"babel-preset-react-app": "^3.1.1",
@ -226,7 +228,6 @@
"html-webpack-plugin": "^4.0.4",
"less-loader": "^4.1.0",
"mockjs": "^1.1.0",
"node-sass": "^4.14.1",
"npm-run-all": "^4.1.5",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "2.0.8",

View File

@ -1,25 +1,33 @@
import express from "express";
import { getDomObj } from "./window"
import { render } from "./render";
import { url } from "../src/ssrUrl";
import { url, zoneUrl } from "../src/ssrUrl";
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use('/build', express.static('build'));
const targetServer = url; // Java 服务器的地址
const options = {
target: targetServer,
changeOrigin: true, // 允许在请求头中更改主机
timeout: 30000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0'
}
};
const options = (target) => {
return {
target: target,
changeOrigin: true, // 允许在请求头中更改主机
timeout: 30000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0'
}
};
}
// 设置代理
app.use(['/api/zone', '/api/cms'], (req, res) => {
const proxy = createProxyMiddleware(options(zoneUrl));
proxy(req, res);
});
// 设置代理
app.use(['/api', '/images', '/system', '/favicon', '/robots.txt', '/sitemap*.xml', '/sitemap*.txt'], (req, res) => {
const proxy = createProxyMiddleware(options);
const proxy = createProxyMiddleware(options(url));
proxy(req, res);
});

View File

@ -1,6 +1,7 @@
import React from "react";
import {renderToString} from "react-dom/server";
import {StaticRouter, matchPath} from "react-router-dom";
import {renderRoutes} from "react-router-config";
import Routes, { deepRoutes } from "../Routes";
import { Provider } from "react-redux";
@ -60,7 +61,8 @@ export const render = async (req,res)=>{
const content = renderToString((
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<Route path='/:owner/:projectsId' exact component={App}></Route>
{renderRoutes(Routes)}
{/* <Route path='/:owner/:projectsId' exact component={App}></Route> */}
</StaticRouter>
</Provider>
))

View File

@ -326,6 +326,9 @@ export function returnbar(str){
// 手动添加/修改mate标签
export function addMeta(name, content){
if (__SERVER__) {
document = domObj.window.document
}
if(document.querySelector(`meta[name='${name}']`)){
document.querySelector(`meta[name='${name}']`).content=content;
}else{

View File

@ -1,5 +1,5 @@
import React from 'react';
import Slider from 'react-slick';
// import Slider from 'react-slick';
import { getImageUrl } from 'educoder';
import Left from '../../../home/Img/left.png';
import Right from '../../../home/Img/right.png';
@ -26,21 +26,22 @@ function TopEdition(props) {
return(
<Slider {...setting}>
{
bannerList && bannerList.length > 0 ?
bannerList.map((i,k)=>{
return(
<div className={`regform`} key={ k }>
<div style={{backgroundImage:`url(${i})`}}></div>
</div>
)
})
:
<div className={`regform`} style={{backgroundImage:`url(${banner})`}}>
</div>
}
</Slider>
<div></div>
// <Slider {...setting}>
// {
// bannerList && bannerList.length > 0 ?
// bannerList.map((i,k)=>{
// return(
// <div className={`regform`} key={ k }>
// <div style={{backgroundImage:`url(${i})`}}></div>
// </div>
// )
// })
// :
// <div className={`regform`} style={{backgroundImage:`url(${banner})`}}>
// </div>
// }
// </Slider>
)
}
export default TopEdition;

View File

@ -1,5 +1,5 @@
import React , { useEffect, useState } from 'react';
import Slider from 'react-slick';
// import Slider from 'react-slick';
import { Link } from 'react-router-dom';
const settings = {
@ -30,7 +30,7 @@ function Contributor(props) {
return(
<div className="boxmain zone_c_lists pt40">
{
{/* {
showList && showList.length > 0 ?
<Slider {...settings}>
{
@ -50,7 +50,7 @@ function Contributor(props) {
}
</Slider>
:""
}
} */}
</div>
)

View File

@ -1,5 +1,5 @@
import React , { useEffect } from 'react';
import Slider from 'react-slick';
// import Slider from 'react-slick';
import { httpUrl } from '../fetch';
import axios from 'axios';
import { useState } from 'react';

View File

@ -1,5 +1,5 @@
import React , { useEffect, useRef, useState } from 'react';
import Slider from 'react-slick';
// import Slider from 'react-slick';
import projectRight from '../img/projectRight.png';
import projectLeft from '../img/projectLeft.png';
@ -58,22 +58,22 @@ function Projects(props) {
</div>
)
})
:
<Slider ref={sliderRef} {...settings}>
{
projectList.map((i,k)=>{
return(
<div className="container" key={ k }>
<div className="zone_p_item">
{i.projectProperties && i.projectProperties.authorImageUrl && <img className="info_img" src={i.projectProperties.authorImageUrl} alt="" /> }
<p className="z_p_title task-hide" onClick={()=>window.open(i.projectURL)}>{i.projectProperties && i.projectProperties.name}</p>
<p className="z_p_desc task-hide-2">{i.projectProperties && i.projectProperties.description ? i.projectProperties.description : "暂无~"}</p>
</div>
</div>
)
})
}
</Slider>
: ''
// <Slider ref={sliderRef} {...settings}>
// {
// projectList.map((i,k)=>{
// return(
// <div className="container" key={ k }>
// <div className="zone_p_item">
// {i.projectProperties && i.projectProperties.authorImageUrl && <img className="info_img" src={i.projectProperties.authorImageUrl} alt="" /> }
// <p className="z_p_title task-hide" onClick={()=>window.open(i.projectURL)}>{i.projectProperties && i.projectProperties.name}</p>
// <p className="z_p_desc task-hide-2">{i.projectProperties && i.projectProperties.description ? i.projectProperties.description : "~"}</p>
// </div>
// </div>
// )
// })
// }
// </Slider>
)
:""
}

View File

@ -10,9 +10,9 @@ import Partner from '../Component/partner';
import MemberList from '../Component/memberList';
import '../indexZonebyCCF.scss';
import '../indexZone1.scss';
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import Slider from 'react-slick';
// import "slick-carousel/slick/slick.css";
// import "slick-carousel/slick/slick-theme.css";
// import Slider from 'react-slick';
import Left from '../../../home/Img/left.png';
import Right from '../../../home/Img/right.png';
@ -120,7 +120,7 @@ function HeaderPageCCF(props) {
<p className="in_title mb50">{data.homepageCmsTitle}</p>
<div className="boxmain mb100" style={{ display: "flex" }}>
<div className="zone_new_first">
<Slider {...setting}>
{/* <Slider {...setting}>
{
newsList && newsList.length > 0 ?
newsList.map((i, k) => {
@ -135,7 +135,7 @@ function HeaderPageCCF(props) {
:
""
}
</Slider>
</Slider> */}
</div>
<ul className={`zone_new_three_ccf ${newsList.length < 4 ? "flexStart" : "spaceeTween"}`}>
{

View File

@ -8,18 +8,24 @@ import { IsPC } from 'educoder';
import { tempConfig } from '../tempInfo';
import Acce from '../Component/mobile/accessory';
import { addMeta, setSeoMeta } from 'educoder';
import CommentList from '../Component/comments/list'
// import CommentList from '../Component/comments/list'
import { connect } from 'react-redux';
import { withRouter } from "react-router";
import { setZoneDetail, setNewsDetail } from '../../../redux/actions/server';
function NewsDetail(props){
const { deptId , id } = props.match.params;
const [ detail , setDetail ] = useState(undefined);
const [ cmsDir , setCmsDir ] = useState(undefined);
const [ content , setContent]=useState(undefined);
const [ detail , setDetail ] = useState(props.newsDetail || undefined);
const [ cmsDir , setCmsDir ] = useState(props.newsDetail ? props.newsDetail.cmsDir : undefined);
const [ content , setContent]=useState( props.newsDetail ? Base64.decode(props.newsDetail.content) : undefined);
const [ isSpin , setIsSpin]=useState(false);
const [ commentReload, setCommentReload] = useState(undefined);
const temp = props.temp;
const zonedetail = props.data;
const zonedetail = props.zoneDetail || props.data;
if (__SERVER__ && detail) {
setMeta()
}
useEffect(()=>{
let initialContent = undefined;
@ -49,17 +55,27 @@ function NewsDetail(props){
useEffect(()=>{
//
if(detail){
const { name, cmsDir, summary} = detail;
document.title = `${ name }/${ cmsDir.name }`;
addMeta('Keywords', `${ name },${ cmsDir.name },${ summary }`);
setMeta()
}
return componentWillUnmount
}, [detail])
function componentWillUnmount() {
if (zonedetail) {
setMeta(true)
}
}
function setMeta(unmount = false) {
if (unmount) {
document.title= zonedetail.name;
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
} else {
const { name, cmsDir, summary} = detail;
const title = `${ name }/${ cmsDir.name }`;
const keywords = `${ name },${ cmsDir.name },${ zonedetail.name }`;
document.title = title;
setSeoMeta(keywords, title, summary, `/zone/${deptId}`)
}
}
@ -126,7 +142,7 @@ function NewsDetail(props){
{
//
detail.auditStatus === '1' && <div className="info_text mt30">
<CommentList {...props} docId={ id } reload={commentReload} author={ detail.createBy } reloadComment={()=>{setCommentReload(Math.random())}}/>
{/* <CommentList {...props} docId={ id } reload={commentReload} author={ detail.createBy } reloadComment={()=>{setCommentReload(Math.random())}}/> */}
</div>
}
@ -140,4 +156,38 @@ function NewsDetail(props){
</React.Fragment>
)
}
export default NewsDetail;
const mapStateToProps = (state) => {
const {
zoneDetail,
newsDetail
} = state.zoneReducer;
return {
zoneDetail,
newsDetail
};
}
const mapDispatchToProps = (dispatch) => ({
clearProject: (type) => dispatch(actions.clearProject(type)),
});
NewsDetail.preFetch = ({ store, match, query }) => {
return new Promise(async function(resolve, reject) {
let { dispatch } = store;
// const { owner,projectsId } = match.params
const promises = [
dispatch(setZoneDetail(match.params)),
dispatch(setNewsDetail(match.params)),
];
const data = await Promise.all(promises);
resolve({
code: 200,
});
});
};
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(NewsDetail));

View File

@ -1,5 +1,5 @@
import React ,{ useEffect , useState } from "react";
import { connect } from 'react-redux';
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
@ -12,12 +12,16 @@ import { getMainInfos , getCheckZoneRole, getCurrentRole } from './api';
import PublicBanner from "./Component/publicBanner";
import './index.scss';
import { IsPC } from 'educoder';
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
// import "slick-carousel/slick/slick.css";
// import "slick-carousel/slick/slick-theme.css";
import { tempEnum } from "./tempInfo";
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
import { setSeoMeta } from 'educoder';
import NewsDetail from './Pages/newsDetail'
import HeaderPage from './Pages/headerPage'
import HeaderPageZone1 from './Pages/headerPageZone1'
import HeaderPageCCF from './Pages/headerPagebyCCF'
import { setZoneDetail, setNewsDetail } from '../../redux/actions/server';
const SourceDetail = Loadable({
loader: () => import("./Pages/sourceDetail"),
@ -43,29 +47,29 @@ const SourceSelfList = Loadable({
loader: () => import("./Pages/selfList"),
loading: Loading,
});
const HeaderPage = Loadable({
loader: () => import("./Pages/headerPage"),
loading: Loading,
});
// const HeaderPage = Loadable({
// loader: () => import("./Pages/headerPage"),
// loading: Loading,
// });
const HeaderPageZone1 = Loadable({
loader: () => import("./Pages/headerPageZone1"),
loading: Loading,
});
// const HeaderPageZone1 = Loadable({
// loader: () => import("./Pages/headerPageZone1"),
// loading: Loading,
// });
const HeaderPageCCF = Loadable({
loader : () => import("./Pages/headerPagebyCCF"),
loading : Loading,
});
// const HeaderPageCCF = Loadable({
// loader : () => import("./Pages/headerPagebyCCF"),
// loading : Loading,
// });
const ProjectSource = Loadable({
loader: () => import("./Pages/projectSource"),
loading: Loading,
});
const NewsDetail = Loadable({
loader: () => import("./Pages/newsDetail"),
loading: Loading,
});
// const NewsDetail = Loadable({
// loader: () => import("./Pages/newsDetail"),
// loading: Loading,
// });
const NewsList = Loadable({
loader: () => import("./Pages/newsList"),
loading: Loading,
@ -86,16 +90,20 @@ const Help = Loadable({
});
function Index(props){
const [ data , setData ] = useState(undefined);
const [ data , setData ] = useState( props.zoneDetail || undefined);
const [ adminUrl , setAdminUrl ] = useState(undefined);
const { deptId } = props.match.params;
const [ id , setId ] = useState(undefined);
const [ temp , setTemp ] = useState(tempEnum.zone);
const [ temp , setTemp ] = useState( props.zoneDetail ? props.zoneDetail.template : tempEnum.zone);
const [ role , setRole ] = useState(undefined);
const { pathname } = props.history.location;
const sourcedetail = pathname.indexOf(`/zone/${deptId}/newdetail/`)>-1 && IsPC();
const {current_user} = props;
if (__SERVER__ && data) {
setSeoMeta(`${data.name},`, data.name, data.subTitle, `/zone/${deptId}`)
}
useEffect(()=>{
if(deptId && deptId!=="apply"){
getDetail();
@ -257,8 +265,36 @@ function Index(props){
</div>
)
}
export default withRouter((CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(ImageLayerOfCommentHOC({
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
parentSelector: ".newContainer",
})(Index)))))
);
const mapStateToProps = (state) => {
const {
zoneDetail,
newsDetail
} = state.zoneReducer;
return {
zoneDetail,
newsDetail
};
}
const mapDispatchToProps = (dispatch) => ({
clearProject: (type) => dispatch(actions.clearProject(type)),
});
const newIndex = (CNotificationHOC()(SnackbarHOC()(TPMIndexHOC((Index)))))
newIndex.preFetch = ({ store, match, query }) => {
return new Promise(async function(resolve, reject) {
let { dispatch } = store;
// const { owner,projectsId } = match.params
const promises = [
dispatch(setZoneDetail(match.params)),
dispatch(setNewsDetail(match.params)),
];
const data = await Promise.all(promises);
resolve({
code: 200,
});
});
};
export default withRouter((connect(mapStateToProps, mapDispatchToProps)(newIndex)));

View File

@ -113,6 +113,8 @@ const types = {
GET_PROJECT_ENTRIES: 'GET_PROJECT_ENTRIES',
GET_PROJECT_MENU: 'GET_PROJECT_MENU',
GET_PROJECT_README: 'GET_PROJECT_README',
GET_ZONE_DETAIL: 'GET_ZONE_DETAIL',
GET_NEWS_DETAIL: 'GET_NEWS_DETAIL',
}
export default types;

View File

@ -1,6 +1,5 @@
import types from './actionTypes';
import { getProjectFunc, getProjectDetailFunc, getProjectEntriesFunc, getBannerFunc, getProjectReadMe } from '../../services/project'
import { getProjectFunc, getProjectDetailFunc, getProjectEntriesFunc, getBannerFunc, getProjectReadMe, getMainInfos, getNewsDetail } from '../../services/project'
// 获取项目信息
export const setProject = ({ owner,projectsId }) => {
@ -76,4 +75,35 @@ export const setProjectReadMe = ({ owner,projectsId, branch }) => {
}
}
// 获取专区详情
export const setZoneDetail = ({ deptId }) => {
return (dispatch) => {
return getMainInfos(deptId).then(res => {
if (res && res.data && res.data.data) {
dispatch({
type: types.GET_ZONE_DETAIL,
payload: res.data.data
});
return res.data.data
}
})
}
}
// 获取文章详情
export const setNewsDetail = ({ id }) => {
return (dispatch) => {
return getNewsDetail( id ).then(res => {
if (res && res.data && res.data.data) {
dispatch({
type: types.GET_NEWS_DETAIL,
payload: res.data.data
});
return res.data.data
}
})
}
}

View File

@ -19,6 +19,7 @@ import tpiReducer from './tpiReducer';
import staticReducer from './staticReducer';
import wxcodeReducer from './wxcodeReducer';
import projectReducer from './projectReducer'
import zoneReducer from './zoneReducer'
export default combineReducers({
testReducer,
@ -32,5 +33,6 @@ export default combineReducers({
tpiReducer,
staticReducer,
wxcodeReducer,
projectReducer
projectReducer,
zoneReducer
});

View File

@ -0,0 +1,31 @@
import types from "../actions/actionTypes";
const initialState = {
zoneDetail: '', // 当前专区基本信息
newsDetail: '', // 当前文章详情
};
const zoneReducer = (state = initialState, action) => {
switch (action.type) {
case types.GET_ZONE_DETAIL:
return {
...state,
zoneDetail: action.payload
}
case types.GET_NEWS_DETAIL:
return {
...state,
newsDetail: action.payload
}
default:
return {
...state
}
}
}
export default zoneReducer;
export {
zoneReducer
};

View File

@ -1,7 +1,8 @@
import axios from 'axios';
import { url } from '../ssrUrl';
import { url, zoneUrl } from '../ssrUrl';
const baseUrl = `${url}/api`
const baseZoneUrl = `${zoneUrl}/api`
//
export async function getProjectFunc (owner, projectsId) {
@ -27,4 +28,14 @@ export async function getBannerFunc (owner, projectsId) {
export async function getProjectReadMe (owner, projectsId, branch) {
const url = `${baseUrl}/${owner}/${projectsId}/readme.json`;
return axios.get(url, {params: { ref: branch }});
}
}
export async function getMainInfos (deptId) {
const url = `${baseZoneUrl}/zone/open/zoneKey/${deptId}`;
return axios.get(url);
}
export async function getNewsDetail (id) {
const url = `${baseZoneUrl}/cms/doc/open/${id}`;
return axios.get(url);
}

View File

@ -1,2 +1,3 @@
export const url = process.env.NODE_ENV === 'production' ? 'https://www.gitlink.org.cn' : 'http://172.20.32.202:4000'
export const zoneUrl = process.env.NODE_ENV === 'production' ? 'https://gateway.gitlink.org.cn' : 'http://172.20.32.202:4000'
export const host = process.env.NODE_ENV === 'production' ? 'www.gitlink.org.cn' : '172.20.32.202:4000'