forked from Gitlink/forgeplus-react
Merge commit 'f469c5480612039ef24f65313f2995e0749fccc8' into pre_gitlink_ssr
This commit is contained in:
commit
03e7ab5b3e
18
Routes.js
18
Routes.js
|
|
@ -1,6 +1,6 @@
|
|||
// import app from './src/App'
|
||||
import app from './src/App'
|
||||
// import users from './src/forge/users/Index';
|
||||
// import Detail from "./src/forge/Main/Detail";
|
||||
import Detail from "./src/forge/Main/Detail";
|
||||
// import Information from "./src/forge/Information/index"
|
||||
// import NewsDetail from './src/forge/Information/Pages/newsDetail';
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ export default [
|
|||
{
|
||||
key:"detail",
|
||||
path: "/:owner/:projectsId",
|
||||
// component: app,
|
||||
component: app,
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -26,21 +26,31 @@ export const deepRoutes = [
|
|||
{
|
||||
key: "zoneNews",
|
||||
path: "/zone/:deptId/newdetail/:id",
|
||||
onlyHead: true,
|
||||
// component: NewsDetail
|
||||
},
|
||||
{
|
||||
key: "zone",
|
||||
path: "/zone/:deptId",
|
||||
onlyHead: true,
|
||||
// component: Information
|
||||
},
|
||||
{
|
||||
key: "detailBranch",
|
||||
path: "/:owner/:projectsId/tree/:branchName",
|
||||
onlyHead: false,
|
||||
component: Detail,
|
||||
},
|
||||
{
|
||||
key: "detail",
|
||||
path: "/:owner/:projectsId",
|
||||
// component: Detail,
|
||||
onlyHead: false,
|
||||
component: Detail,
|
||||
},
|
||||
{
|
||||
key: "owner",
|
||||
path: "/:owner",
|
||||
onlyHead: true,
|
||||
// component: Detail,
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -159,12 +159,13 @@ const serverConfig = {
|
|||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
publicPath,
|
||||
},
|
||||
},
|
||||
'isomorphic-style-loader',
|
||||
// {
|
||||
// loader: MiniCssExtractPlugin.loader,
|
||||
// options: {
|
||||
// publicPath,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
|
|
|
|||
|
|
@ -11456,7 +11456,7 @@
|
|||
},
|
||||
"isomorphic-style-loader": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/isomorphic-style-loader/-/isomorphic-style-loader-5.3.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-style-loader/-/isomorphic-style-loader-5.3.2.tgz",
|
||||
"integrity": "sha512-5mwHrN2xK5zsKBxSUYF7iDhoU9Kpcpfgn0lFOP0SKk3aKwkl26zi6kh+KDrekjlLzNbYsFnn8o1yWaB3OflVXQ==",
|
||||
"requires": {
|
||||
"hoist-non-react-statics": "^3.0.0",
|
||||
|
|
@ -14459,23 +14459,11 @@
|
|||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pinyin": {
|
||||
"version": "4.0.0-alpha.0",
|
||||
"resolved": "https://registry.npmmirror.com/pinyin/-/pinyin-4.0.0-alpha.0.tgz",
|
||||
"integrity": "sha512-hdxO/2RuLWarhLaIcGKdXjkFZ0hFr3W/Yrvrd0YPxuIafixMZSAa4jvxskYLgYHE8OkOXkGsxMsEScwGzqKe3g==",
|
||||
"requires": {
|
||||
"commander": "~1.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-1.1.1.tgz",
|
||||
"integrity": "sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==",
|
||||
"requires": {
|
||||
"keypress": "0.1.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
"pirates": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz",
|
||||
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
||||
"dev": true
|
||||
},
|
||||
"pkg-dir": {
|
||||
"version": "3.0.0",
|
||||
|
|
@ -21600,17 +21588,26 @@
|
|||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@
|
|||
"analyz": "NODE_ENV=production npm_config_report=true npm run build",
|
||||
"dev": "npm-run-all --parallel dev:**",
|
||||
"dev:server": "cross-env NODE_ENV=dev nodemon --exec babel-node \"./buildserver/bundle.js\" --watch config --watch server",
|
||||
"test:build:server": "cross-env NODE_ENV=localtest webpack --config config/webpack.server.js",
|
||||
"dev:build:server": "cross-env NODE_ENV=dev webpack --config config/webpack.server.js",
|
||||
"pm2": "pm2 start pm2.json"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -15,7 +15,7 @@ app.use('/build', express.static('build'));
|
|||
const options = (target) => {
|
||||
return {
|
||||
target: target,
|
||||
changeOrigin: false, // 允许在请求头中更改主机
|
||||
changeOrigin: true, // 允许在请求头中更改主机
|
||||
timeout: 30000,
|
||||
onProxyRes: (proxyRes, req, res) => {
|
||||
// 代理成功后输出日志
|
||||
|
|
@ -29,25 +29,27 @@ const options = (target) => {
|
|||
};
|
||||
}
|
||||
|
||||
const proxy = createProxyMiddleware(options(`http://localhost:${ rubyPort }`));
|
||||
// const proxy = createProxyMiddleware(options(url));
|
||||
let proxy
|
||||
|
||||
if (process.env.NODE_ENV === 'dev') {
|
||||
// 本地调试用设置代理
|
||||
proxy = createProxyMiddleware(options(url));
|
||||
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', '/favicon.ico', '/admins', '/assets', '/attachments', '/oauth'], (req, res) => {
|
||||
proxy(req, res);
|
||||
});
|
||||
} else {
|
||||
proxy = createProxyMiddleware(options(`http://localhost:${ rubyPort }`));
|
||||
// 设置代理
|
||||
app.use(['/api', '/admins', '/attachments', '/oauth', '/competitions', '/projects', '/auth'], (req, res) => {
|
||||
proxy(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 本地调试用设置代理
|
||||
// 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', '/favicon.ico', '/admins', '/assets', '/attachments', '/oauth'], (req, res) => {
|
||||
// proxy(req, res);
|
||||
// });
|
||||
|
||||
// 设置代理
|
||||
app.use(['/api', '/admins', '/attachments', '/oauth', '/competitions'], (req, res) => {
|
||||
proxy(req, res);
|
||||
});
|
||||
|
||||
// 匹配路径,匹配到的返回处理后的html,没匹配到转发到后端
|
||||
app.get('*',async function (req,res) {
|
||||
|
|
|
|||
139
server/render.js
139
server/render.js
|
|
@ -1,28 +1,30 @@
|
|||
// import React from "react";
|
||||
// import {renderToString} from "react-dom/server";
|
||||
import React from "react";
|
||||
import {renderToString} from "react-dom/server";
|
||||
import {StaticRouter, matchPath} from "react-router-dom";
|
||||
// import {renderRoutes} from "react-router-config";
|
||||
|
||||
import StyleContext from 'isomorphic-style-loader/StyleContext'
|
||||
import {renderRoutes} from "react-router-config";
|
||||
import Routes, { deepRoutes, specialRoute } from "../Routes";
|
||||
// import { Provider } from "react-redux";
|
||||
// import { serverStore } from "../src/redux/stores/configureStore";
|
||||
// import App from "../src/App";
|
||||
// import { Route } from 'react-router-dom';
|
||||
// import { setDefaultMeta } from '../src/common/UrlTool'
|
||||
import { Provider } from "react-redux";
|
||||
import { serverStore } from "../src/redux/stores/configureStore";
|
||||
import { setDefaultMeta } from '../src/common/UrlTool'
|
||||
import Logger from "./log";
|
||||
import { changeHead } from './setHead'
|
||||
|
||||
// 目前只用项目详情页的,不需要动态添加
|
||||
const css = new Set() // 这个必须在路由函数里面,在外面的话,就会累加出现之前的css
|
||||
const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss()));
|
||||
|
||||
export const render = async (req,res)=>{
|
||||
let _route = null,
|
||||
_match = null;
|
||||
// Logger.info(`${req.url} UA:${req.headers['user-agent']}`)
|
||||
Logger.info(`${req.url} UA:${req.headers['user-agent']}`)
|
||||
|
||||
// let store = serverStore();
|
||||
let store = serverStore();
|
||||
|
||||
function matchSubRoutes(routes, url) {
|
||||
for (const route of routes) {
|
||||
// 检查当前路由是否匹配
|
||||
let match = matchPath(url, route.path);
|
||||
let match = matchPath(url, { path: route.path, exact: true });
|
||||
if (match) {
|
||||
// 如果当前路由匹配,并且它有子路由,递归检查子路由
|
||||
if (route.routes) {
|
||||
|
|
@ -50,59 +52,84 @@ export const render = async (req,res)=>{
|
|||
code: 200,
|
||||
};
|
||||
|
||||
|
||||
if (_route && _route.component && _route.component.preFetch) {
|
||||
|
||||
// 设置服务端请求cookie
|
||||
let cookies = req.headers.cookie.split(';')
|
||||
for (let index = 0; index < cookies.length; index++) {
|
||||
const element = cookies[index];
|
||||
domObj.window.document.cookie = element
|
||||
}
|
||||
|
||||
|
||||
context = await _route.component.preFetch({
|
||||
store,
|
||||
match: _match,
|
||||
query: req.path,
|
||||
});
|
||||
|
||||
// 当前只做了项目详情页,没获取到数据使用原来的
|
||||
if(!store.getState().projectReducer.projectBase.id) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let content = ''
|
||||
|
||||
// 匹配路由、并且不是特定一级路由、且不是只动态修改head的,在服务端渲染页面
|
||||
if (_route && !specialRoute.includes(_match.url && _match.url.replace('/', '')) && !_route.onlyHead) {
|
||||
content = renderToString((
|
||||
<Provider store={store}>
|
||||
<StaticRouter location={req.path} context={context}>
|
||||
<StyleContext.Provider value={{insertCss}}>
|
||||
{renderRoutes(Routes)}
|
||||
{/* <Route path='/:owner/:projectsId' exact component={App}></Route> */}
|
||||
</StyleContext.Provider>
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
))
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
// 未匹配到详情页恢复默认header
|
||||
setDefaultMeta()
|
||||
} else {
|
||||
// 匹配到服务端渲染页面注入state
|
||||
let script = domObj.window.document.getElementById('initState')
|
||||
if (!script) {
|
||||
script = domObj.window.document.createElement('script')
|
||||
script.setAttribute('id', 'initState')
|
||||
}
|
||||
script.textContent = `window.__initState__ = ${ JSON.stringify(store.getState()) }`
|
||||
|
||||
// 目前直接把详情页涉及到的css抽出来塞到返回的html里,没有使用insertCss
|
||||
const styleNode = domObj.window.document.createElement('style')
|
||||
styleNode.type = 'text/css'
|
||||
// styleNode.innerHTML = `${ [...css].join('') }`
|
||||
styleNode.innerHTML = detailCss
|
||||
domObj.window.document.head.appendChild(script);
|
||||
domObj.window.document.head.appendChild(styleNode);
|
||||
}
|
||||
|
||||
if (_route && !specialRoute.includes(_match.url && _match.url.replace('/', ''))) {
|
||||
Logger.info(`matchRoot: ${ _match.url }`)
|
||||
await changeHead(_route, _match.params)
|
||||
// 匹配上的,并且只修改head的页面
|
||||
if (_route.onlyHead) {
|
||||
Logger.info(`matchRoot: ${ _match.url }`)
|
||||
await changeHead(_route, _match.params)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// if (_route && _route.component && _route.component.preFetch) {
|
||||
// context = await _route.component.preFetch({
|
||||
// store,
|
||||
// match: _match,
|
||||
// query: req.path,
|
||||
// });
|
||||
// }
|
||||
|
||||
// 暂时不开放ssr,只修改head
|
||||
// const content = renderToString((
|
||||
// <Provider store={store}>
|
||||
// <StaticRouter location={req.path} context={context}>
|
||||
// {renderRoutes(Routes)}
|
||||
// {/* <Route path='/:owner/:projectsId' exact component={App}></Route> */}
|
||||
// </StaticRouter>
|
||||
// </Provider>
|
||||
// ))
|
||||
|
||||
// 暂时不开放ssr,只修改head
|
||||
// if (!content) {
|
||||
// // 未匹配到详情页恢复默认header
|
||||
// setDefaultMeta()
|
||||
// } else {
|
||||
// // 匹配到服务端渲染页面注入state
|
||||
// let script = domObj.window.document.getElementById('initState')
|
||||
// if (!script) {
|
||||
// script = domObj.window.document.createElement('script')
|
||||
// script.setAttribute('id', 'initState')
|
||||
// }
|
||||
// script.textContent = `window.__initState__ = ${ JSON.stringify(store.getState()) }`
|
||||
|
||||
// domObj.window.document.head.appendChild(script);
|
||||
// }
|
||||
|
||||
|
||||
let html = domObj.serialize()
|
||||
|
||||
const prepHTML=(data,rootString)=>{
|
||||
// 暂时不开放ssr,只修改head
|
||||
// data=data.replace('<div id="root" class="page -layout-v -fit widthunit"></div>',`<div id="root" class="page -layout-v -fit widthunit">${rootString}</div>`);
|
||||
data=data.replace('<div id="root" class="page -layout-v -fit widthunit"></div>',`<div id="root" class="page -layout-v -fit widthunit">${rootString}</div>`);
|
||||
return data;
|
||||
}
|
||||
// 暂时不开放ssr,只修改head
|
||||
// res.send(prepHTML(html, content))
|
||||
res.send(prepHTML(html, ''))
|
||||
|
||||
res.send(prepHTML(html, content))
|
||||
|
||||
// store = null
|
||||
store = null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,16 @@ export const changeHead = async ({ key }, params) => {
|
|||
let res
|
||||
let data
|
||||
switch (key) {
|
||||
case 'detail':
|
||||
res = await getProjectDetailFunc(params.owner, params.projectsId);
|
||||
data = res.data
|
||||
if (data && data.status === 1) {
|
||||
let keyWords=`${params.owner},${params.projectsId}${ data.topics.length > 0 && ','}${ data.topics.length > 0 && data.topics.map(item => item.name).join(',') }`;
|
||||
let title= `${params.owner}/${params.projectsId}${data.description?':'+data.description:''}`;
|
||||
setSeoMeta(keyWords,title,data.description || '',`/${params.owner}/${params.projectsId}`,params.owner,params.projectsId);
|
||||
}
|
||||
break;
|
||||
// 详情页不再修改head,直接ssr渲染页面
|
||||
// case 'detail':
|
||||
// res = await getProjectDetailFunc(params.owner, params.projectsId);
|
||||
// data = res.data
|
||||
// if (data && data.status === 1) {
|
||||
// let keyWords=`${params.owner},${params.projectsId}${ data.topics.length > 0 && ','}${ data.topics.length > 0 && data.topics.map(item => item.name).join(',') }`;
|
||||
// let title= `${params.owner}/${params.projectsId}${data.description?':'+data.description:''}`;
|
||||
// setSeoMeta(keyWords,title,data.description || '',`/${params.owner}/${params.projectsId}`,params.owner,params.projectsId);
|
||||
// }
|
||||
// break;
|
||||
case 'zoneNews':
|
||||
res = await getNewsDetail(params.id)
|
||||
data = res.data.data
|
||||
|
|
@ -33,7 +34,7 @@ export const changeHead = async ({ key }, params) => {
|
|||
res = await getOwnerInfo(params.owner);
|
||||
data = res.data
|
||||
const name = data.nickname || data.username
|
||||
if (data && data.status === 1) {
|
||||
if (data && data.name) {
|
||||
setSeoMeta(`${name}`, name, data.description || '', `/${params.owner}`)
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
const jsdom = require("jsdom");
|
||||
import fs from 'fs'
|
||||
import path from 'path';
|
||||
import axios from "axios";
|
||||
|
||||
import { getSetting } from '../src/services/project'
|
||||
const { JSDOM } = jsdom;
|
||||
|
||||
const html = fs.readFileSync(path.join(path.resolve('./build'),'index.html'),'utf-8');
|
||||
// 压缩的项目详情页需要的css
|
||||
const css = fs.readFileSync(path.join(path.resolve('./build/css'),'detail.css'),'utf-8');
|
||||
const { window } = new JSDOM(html);
|
||||
|
||||
const $ = require( "jquery" )( window )
|
||||
|
|
@ -13,20 +14,20 @@ const $ = require( "jquery" )( window )
|
|||
global.window = window;
|
||||
global.document = window.document;
|
||||
global.navigator = window.navigator;
|
||||
global.localStorage = {}
|
||||
global.localStorage = {};
|
||||
global.defaultSetting = {}
|
||||
// 全局缓存css
|
||||
global.detailCss = css
|
||||
|
||||
const setGlobalSetting = async () => {
|
||||
const setting = await getSetting()
|
||||
if (setting.data) {
|
||||
global.defaultSetting = setting.data.setting
|
||||
}
|
||||
}
|
||||
|
||||
setGlobalSetting()
|
||||
|
||||
export const getDomObj = () => {
|
||||
return new JSDOM(html)
|
||||
}
|
||||
|
||||
// axios.get('https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.19/bundle/alex.all.global.min.js')
|
||||
// .then(response => {
|
||||
// // 处理响应
|
||||
// if (response) {
|
||||
// // 这里你可以直接使用response.data,而不是eval
|
||||
// eval(response);
|
||||
// }
|
||||
// })
|
||||
// .catch(error => {
|
||||
// console.error('Error fetching data:', error);
|
||||
// });
|
||||
|
|
|
|||
37
src/App.js
37
src/App.js
|
|
@ -23,7 +23,9 @@ import SiderBarHelp from './glcc/siderBarHelp';
|
|||
import { SnackbarHOC, configShareForCustom } from 'educoder';
|
||||
import { initAxiosInterceptors } from './AppConfig'
|
||||
import cookie from 'react-cookies';
|
||||
|
||||
import InfosIndex from './forge/users/Index'
|
||||
import OrganizeIndex from './forge/Team/Index'
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
window.marked = marked;
|
||||
const theme = createMuiTheme({
|
||||
|
|
@ -75,15 +77,15 @@ const http500 = Loadable({
|
|||
loader: () => import('./modules/500/http500'),
|
||||
loading: Loading,
|
||||
})
|
||||
const InfosIndex = Loadable({
|
||||
loader: () => import('./forge/users/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
// const InfosIndex = Loadable({
|
||||
// loader: () => import('./forge/users/Index'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
// 组织
|
||||
const OrganizeIndex = Loadable({
|
||||
loader: () => import('./forge/Team/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
// const OrganizeIndex = Loadable({
|
||||
// loader: () => import('./forge/Team/Index'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
const Search = Loadable({
|
||||
loader: () => import('./modules/search/'),
|
||||
|
|
@ -169,7 +171,7 @@ class App extends Component {
|
|||
mydisplay: false,
|
||||
occupation: 0,
|
||||
mygetHelmetapi: null,
|
||||
pathType: null,
|
||||
pathType: props.projectBase && props.projectBase.author && props.projectBase.author.type,
|
||||
pathName: null,
|
||||
}
|
||||
}
|
||||
|
|
@ -651,5 +653,18 @@ moment.defineLocale('zh-cn', {
|
|||
doy: 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
export default SnackbarHOC()(App);
|
||||
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
projectBase,
|
||||
} = state.projectReducer;
|
||||
return {
|
||||
projectBase,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
)(SnackbarHOC()(App));
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import {Input} from "antd";
|
|||
import { url as ssrOrigin, host as ssrHost } from '../ssrUrl'
|
||||
const { Search } = Input;
|
||||
|
||||
|
||||
if (!__SERVER__) {
|
||||
const $ = window.$;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import './index.scss';
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Button, Input } from 'antd';
|
||||
import QuillForEditor from '../../quillForEditor';
|
||||
// import QuillForEditor from '../../quillForEditor';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
function CommentForm(props) {
|
||||
|
|
@ -116,7 +116,7 @@ function CommentForm(props) {
|
|||
)
|
||||
}
|
||||
|
||||
<QuillForEditor
|
||||
{/* <QuillForEditor
|
||||
imgAttrs={{ width: '60px', height: '30px' }}
|
||||
wrapStyle={{
|
||||
height: showQuill ? 'auto' : '0px',
|
||||
|
|
@ -131,7 +131,7 @@ function CommentForm(props) {
|
|||
value={ctx}
|
||||
showUploadImage={handleShowImage}
|
||||
onContentChange={handleContentChange}
|
||||
/>
|
||||
/> */}
|
||||
</FormItem>
|
||||
<FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}>
|
||||
<Button onClick={handleCancle}>取消</Button>
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export { default as ActionBtn } from './course/ActionBtn'
|
|||
|
||||
export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
|
||||
|
||||
export { default as QuillForEditor } from './quillForEditor'
|
||||
// export { default as QuillForEditor } from './quillForEditor'
|
||||
|
||||
export { default as Clappr } from './components/media/Clappr'
|
||||
export { default as AliyunUploader } from './components/media/AliyunUploader'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import './index.scss'
|
||||
import 'quill/dist/quill.core.css' // 核心样式
|
||||
import 'quill/dist/quill.snow.css' // 有工具栏
|
||||
import 'quill/dist/quill.bubble.css' // 无工具栏
|
||||
// import 'quill/dist/quill.core.css' // 核心样式
|
||||
// import 'quill/dist/quill.snow.css' // 有工具栏
|
||||
// import 'quill/dist/quill.bubble.css' // 无工具栏
|
||||
import './font.css'
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import Quill from 'quill'
|
||||
|
|
|
|||
|
|
@ -123,24 +123,26 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.urlMenu{
|
||||
line-height: 30px;
|
||||
margin-bottom: 10px;
|
||||
padding:15px 20px 0px 20px;
|
||||
border-bottom: none;
|
||||
li.ant-menu-item{
|
||||
height: 30px;
|
||||
.downMenu {
|
||||
.urlMenu{
|
||||
line-height: 30px;
|
||||
padding:0px 5px;
|
||||
margin-right: 20px!important;
|
||||
&.ant-menu-item-selected,&.ant-menu-item-active{
|
||||
color: #333;
|
||||
}
|
||||
&.ant-menu-item-selected{
|
||||
border-color:#466aff!important;
|
||||
}
|
||||
&.ant-menu-item-active{
|
||||
border-color:transparent ;
|
||||
margin-bottom: 10px;
|
||||
padding:15px 20px 0px 20px;
|
||||
border-bottom: none;
|
||||
li.ant-menu-item{
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding:0px 5px;
|
||||
margin-right: 20px!important;
|
||||
&.ant-menu-item-selected,&.ant-menu-item-active{
|
||||
color: #333;
|
||||
}
|
||||
&.ant-menu-item-selected{
|
||||
border-color:#466aff!important;
|
||||
}
|
||||
&.ant-menu-item-active{
|
||||
border-color:transparent ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@ export default ({ url , name , column , id , login })=>{
|
|||
return(
|
||||
id?
|
||||
<Link to={`/${login}`}>
|
||||
<Img>
|
||||
<span className="User">
|
||||
{ url && <img src={url} alt=""/> }
|
||||
<span>{name}</span>
|
||||
</Img>
|
||||
</span>
|
||||
</Link>
|
||||
:
|
||||
<Img>
|
||||
<span className="User">
|
||||
{ url && <img src={url} alt=""/> }
|
||||
<span>{name}</span>
|
||||
</Img>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@ import React, { useEffect , useState } from 'react';
|
|||
import './header.scss';
|
||||
|
||||
function Footer(){
|
||||
const [ value , setValue ] = useState(undefined);
|
||||
const [ value , setValue ] = useState(__CLIENT__ ? undefined : defaultSetting && defaultSetting.footer);
|
||||
|
||||
useEffect(()=>{
|
||||
try {
|
||||
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
||||
let chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
||||
setValue(chromesettingArray.footer);
|
||||
} catch (e) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import axios from 'axios';
|
|||
import Path from './CoderDepotPath';
|
||||
import Catalogue from './CoderDepotCatalogue';
|
||||
import ReadMe from './CoderDepotReadme';
|
||||
import CoderRootFileDetail from './CoderRootFileDetail';
|
||||
// import CoderRootFileDetail from './CoderRootFileDetail';
|
||||
import './Index.scss';
|
||||
import Releases from '../Component/Releases';
|
||||
import Contributors from '../Component/Contributors';
|
||||
|
|
@ -24,6 +24,14 @@ import Nodata from '../Nodata';
|
|||
import Invite from './sub/Invite';
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
import RenderHtml from '../../components/render-html';
|
||||
import Loadable from 'react-loadable';
|
||||
import Loading from '../../Loading';
|
||||
|
||||
const CoderRootFileDetail = Loadable({
|
||||
loader: () => import("./CoderRootFileDetail"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
/**
|
||||
* projectDetail.type:0是托管项目,1是镜像项目,2是同步镜像项目(为2时不支持在线创建、在线上传、在线修改、在线删除、创建合并请求等功能)
|
||||
*/
|
||||
|
|
@ -40,7 +48,7 @@ function CoderDepot(props){
|
|||
const [ lastCommitAuthor,setLastCommitAuthor ] = useState((projectEntries.last_commit && projectEntries.last_commit.author) || undefined);
|
||||
const [ type ,setType ] = useState('dir');
|
||||
const [ hide , setHide ] = useState(true);
|
||||
const [ hideBtn , setHideBtn ] = useState(false);
|
||||
const [ hideBtn , setHideBtn ] = useState(true);
|
||||
const [ commitCount ,setCommitCount ] = useState(projectEntries.commits_count || 0);
|
||||
const [ dirInfo ,setDirInfo ] = useState(projectEntries.entries || undefined);//文件夹目录列表
|
||||
const [ fileInfo ,setFileInfo ] = useState(undefined);//文件内容信息
|
||||
|
|
@ -222,7 +230,7 @@ function CoderDepot(props){
|
|||
let ele = document.getElementById("ptxt");
|
||||
if(ele){
|
||||
let h = ele.offsetHeight;
|
||||
if( h > 36 ) setHideBtn(true);
|
||||
if( h <= 36 ) setHideBtn(false);
|
||||
}
|
||||
}
|
||||
},[projectDetail,lastCommit])
|
||||
|
|
@ -402,7 +410,7 @@ function CoderDepot(props){
|
|||
const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter" && projectDetail.type !==2 && pullsFlag && props.platform;
|
||||
const fileOperate = type === "dir" && projectDetail && projectDetail.type !==2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
|
||||
return(
|
||||
<WhiteBack>
|
||||
<div className='WhiteBack'>
|
||||
{mirror_status ===0 && <UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} topicsInfo={topics} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/> }
|
||||
<Spin spinning={isSpin}>
|
||||
{
|
||||
|
|
@ -427,11 +435,11 @@ function CoderDepot(props){
|
|||
<div style={{minHeight:"500px"}}>
|
||||
{
|
||||
projectDetail &&
|
||||
<Box className="Panels">
|
||||
<LongWidth>
|
||||
<div className="Panels Box">
|
||||
<div className="LongWidth">
|
||||
<div className="panelmenu">
|
||||
<FlexAJ>
|
||||
<AlignCenter>
|
||||
<div className="FlexAJ">
|
||||
<div className="AlignCenter">
|
||||
<div className="mr30">
|
||||
{
|
||||
props && (props.platform && mirror_status ===0) ?
|
||||
|
|
@ -459,7 +467,7 @@ function CoderDepot(props){
|
|||
/>
|
||||
:
|
||||
<React.Fragment>
|
||||
<AlignCenter className="mr20">
|
||||
<div className="mr20 AlignCenter">
|
||||
{
|
||||
props.platform ?
|
||||
<Link to={`/${owner}/${projectsId}/branches`} className="iconBtn">
|
||||
|
|
@ -474,8 +482,8 @@ function CoderDepot(props){
|
|||
<span className="ml3">{projectDetail && projectDetail.branches_count}</span>
|
||||
</span>
|
||||
}
|
||||
</AlignCenter>
|
||||
<AlignCenter className="mr20">
|
||||
</div>
|
||||
<div className="mr20 AlignCenter">
|
||||
{
|
||||
props.platform ?
|
||||
<Link to={`/${owner}/${projectsId}/tags`} className="iconBtn">
|
||||
|
|
@ -489,11 +497,11 @@ function CoderDepot(props){
|
|||
<span className="ml3">{projectDetail && projectDetail.tags_count}</span>
|
||||
</span>
|
||||
}
|
||||
</AlignCenter>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
</AlignCenter>
|
||||
<AlignCenter className="depotBtn">
|
||||
</div>
|
||||
<div className="depotBtn AlignCenter">
|
||||
<div className="addOptionBtn">
|
||||
{
|
||||
baseOperate &&
|
||||
|
|
@ -525,8 +533,8 @@ function CoderDepot(props){
|
|||
<Dropdown overlay={downloadMenu} placement="bottomRight" trigger={['click']}>
|
||||
<Button type={'primary'}>下载 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-white mr-3"></i></Button>
|
||||
</Dropdown>
|
||||
</AlignCenter>
|
||||
</FlexAJ>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
(dirInfo && dirInfo.length>0) || fileInfo ?
|
||||
<div className="listtable">
|
||||
|
|
@ -543,7 +551,7 @@ function CoderDepot(props){
|
|||
<pre id="ptxt"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`}><RenderHtml value={lastCommit.message}/></Link></pre>
|
||||
</div>
|
||||
}
|
||||
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
|
||||
{ hideBtn && __CLIENT__ && <div className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></div> }
|
||||
|
||||
<span className="ml20 color-grey-6 font-12 mt3">{lastCommit.time_from_now}</span>
|
||||
{
|
||||
|
|
@ -571,7 +579,7 @@ function CoderDepot(props){
|
|||
})
|
||||
}
|
||||
{
|
||||
fileInfo &&
|
||||
fileInfo && __CLIENT__ &&
|
||||
<CoderRootFileDetail
|
||||
{...props}
|
||||
detail={fileInfo}
|
||||
|
|
@ -594,18 +602,18 @@ function CoderDepot(props){
|
|||
{/* readme文件显示(显示文件详情时不显示readme文件) */}
|
||||
{ (readme && readme.replace_content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
|
||||
</div>
|
||||
</LongWidth>
|
||||
</div>
|
||||
{
|
||||
(!(treeValuePath && treeValuePath.length > 0) && !fileInfo ) &&
|
||||
<ShortWidth>
|
||||
<Gap style={{paddingLeft:"30px"}}>
|
||||
<div className="ShortWidth">
|
||||
<div className="Gap" style={{paddingLeft:"30px"}}>
|
||||
<div className="panelmenu">
|
||||
<FlexAJ className="font-18 color-ooo mb20" style={{lineHeight:"28px"}}>关于
|
||||
<div className="font-18 color-ooo mb20 FlexAJ" style={{lineHeight:"28px"}}>关于
|
||||
{
|
||||
projectDetail.permission && (projectDetail.permission==="Admin" || projectDetail.permission==="Owner" || projectDetail.permission==="Manager") &&
|
||||
<i onClick={()=>setOpenModal(true)} className="iconfont icon-a-shezhi color-grey-9 font-15 pointer"></i>
|
||||
}
|
||||
</FlexAJ>
|
||||
</div>
|
||||
{desc && <p className="font-14 color-grey-3 mb15 task-hide-2" style={{lineHeight:"24px",WebkitLineClamp:"4",textAlign:"justify",wordBreak:"break-all"}} title={desc}>{desc}</p>}
|
||||
{/* 项目标签 */}
|
||||
{topics && <div className='projectHomeTopics'>
|
||||
|
|
@ -667,14 +675,14 @@ function CoderDepot(props){
|
|||
{mirror_status ===0 && <Contributors owner={owner} projectsId={projectsId} /> }
|
||||
{/* 语言 */}
|
||||
{ mirror_status ===0 && <LanguagePower owner={owner} projectsId={projectsId}/> }
|
||||
</Gap>
|
||||
</ShortWidth>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Box>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</WhiteBack>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
<div className="commonBox readBox" id="readme">
|
||||
<Anchor offsetTop={58}>
|
||||
<div className="commonBox-title boxTitle">
|
||||
<AlignCenter>
|
||||
<div className="AlignCenter">
|
||||
<Dropdown overlay={menu()} trigger={['hover']} overlayClassName="menuslist">
|
||||
<span className="catelogue">
|
||||
<i className="iconfont icon-muluicon font-12 mr5"></i>
|
||||
|
|
@ -53,7 +53,7 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
|
||||
<span className="commonBox-title-read"><a href="#readme ">{readme.name}</a></span>
|
||||
|
||||
</AlignCenter>
|
||||
</div>
|
||||
{
|
||||
operate ?
|
||||
<a className="ml20 pull-right" onClick={() =>ChangeFile(readme && readme.path, false)}>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import ReadmeCatelogue from "./sub/ReadmeCatelogue";
|
|||
import { Base64 } from "js-base64";
|
||||
import ReplaceModal from "./Modal/ReplaceModal";
|
||||
import {returnbar , turnbar} from 'educoder';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
|
||||
const $ = window.$;
|
||||
function bytesToSize(bytes) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Route , Switch } from 'react-router-dom';
|
|||
import Loadable from 'react-loadable';
|
||||
import Loading from '../../Loading';
|
||||
import './Index.scss';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
|
||||
const FileNew = Loadable({
|
||||
loader: () => import('../Newfile/Index'),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import types from '../../redux/actions/actionTypes'
|
|||
import { getProjectFunc, getProjectDetailFunc } from '../../services/project'
|
||||
import '../css/index.scss'
|
||||
import './list.scss';
|
||||
import '../../modules/courses/css/Courses.css'
|
||||
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
|
||||
|
||||
|
||||
|
|
@ -617,9 +618,9 @@ class Detail extends Component {
|
|||
<div>
|
||||
<div className="detailHeader-wrapper">
|
||||
<div className="normal">
|
||||
<AlignTop style={{ padding: "18px 0px 10px", justifyContent: "space-between" }}>
|
||||
<div className="AlignTop" style={{ padding: "18px 0px 10px", justifyContent: "space-between" }}>
|
||||
<div>
|
||||
<AlignTop>
|
||||
<div className="AlignTop">
|
||||
<div className="projectallName">
|
||||
{project && project.author &&
|
||||
<Link to={`/${project.author.login}`}>{project.author.name}</Link>
|
||||
|
|
@ -629,7 +630,7 @@ class Detail extends Component {
|
|||
</div>
|
||||
{projectDetail && projectDetail.private && <span className="privateTag mt6">私有</span>}
|
||||
{!platform && <span className="privateTag red mt6">只读</span>}
|
||||
</AlignTop>
|
||||
</div>
|
||||
<div className="mt8">
|
||||
{
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
|
|
@ -697,7 +698,7 @@ class Detail extends Component {
|
|||
</span>
|
||||
}
|
||||
</div>
|
||||
</AlignTop>
|
||||
</div>
|
||||
{/* <div className="mt6" style={{minHeight:"20px"}}>
|
||||
{
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
|
|
@ -931,7 +932,7 @@ class Detail extends Component {
|
|||
|
||||
<Route path="/:owner/:projectsId/:subIndex"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
(props) => ( __CLIENT__ && <CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/:owner/:projectsId"
|
||||
|
|
@ -970,9 +971,9 @@ Detail.preFetch = ({ store, match, query }) => {
|
|||
const promises = [
|
||||
dispatch(setProjectDetail(match.params)),
|
||||
dispatch(setProject(match.params)),
|
||||
dispatch(setProjectEntries({...match.params, branch: ''})),
|
||||
dispatch(setProjectEntries({...match.params, branch: match.params.branchName ? match.params.branchName : ''})),
|
||||
dispatch(setProjectMenuList(match.params)),
|
||||
dispatch(setProjectReadMe({...match.params, branch: ''}))
|
||||
dispatch(setProjectReadMe({...match.params, branch: match.params.branchName ? match.params.branchName : ''}))
|
||||
];
|
||||
const data = await Promise.all(promises);
|
||||
resolve({
|
||||
|
|
@ -981,7 +982,13 @@ Detail.preFetch = ({ store, match, query }) => {
|
|||
});
|
||||
};
|
||||
|
||||
export default withRouter(connect(
|
||||
export default __CLIENT__ ? withRouter(connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ImageLayerOfCommentHOC({
|
||||
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
|
||||
parentSelector: ".newContainer",
|
||||
})(Detail))) : withRouter(connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ImageLayerOfCommentHOC({
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import datasetImg from '../img/dataset.jpg';
|
|||
import axios from 'axios';
|
||||
|
||||
function DetailBanner({ history,list , owner , projectsId ,showNotification , url , pathname , state , urlFlag , projectDetail , platform ,open_devops ,current_user, showNpsModal }){
|
||||
const [ menuName , setMenuName ] = useState(undefined);
|
||||
const [ menuName , setMenuName ] = useState(list);
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
|
|||
|
|
@ -232,7 +232,9 @@ class Index extends Component {
|
|||
}).then((result) => {
|
||||
if (result && result.data.id) {
|
||||
projectsType && projectsType !== "mirror" && this.props.showNotification(`项目创建成功!`);
|
||||
this.props.history.push({pathname: `/${result.data.login}/${result.data.identifier}`, query:{showNps: true}});
|
||||
setTimeout(() => {
|
||||
this.props.history.push({pathname: `/${result.data.login}/${result.data.identifier}`, query:{showNps: true}});
|
||||
}, 500);
|
||||
}
|
||||
this.setState({
|
||||
isSpin: false
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ function RecordList(props){
|
|||
|
||||
// 同步状态errorBox
|
||||
let columns = [
|
||||
{ title: '序号', dataIndex: 'index', className:"recordColumns", render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
|
||||
{ title: '序号', dataIndex: 'index', className:"recordColumns", align: 'center', render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
|
||||
{ title: '代码变更方', dataIndex: 'change_from', className:"recordColumns"},
|
||||
{ title: '同步时间', dataIndex: 'sync_time', className:"recordColumns"},
|
||||
{ title: '同步状态', dataIndex: 'sync_status', className:"recordColumns", render: (text)=>sync_status(text)},
|
||||
{ title: 'commit id', dataIndex: 'commit_id', className:"primaryColor recordColumns"},
|
||||
{ title: 'commit id', dataIndex: 'commit_id', className:"recordColumns", render: (text) =>{ return <Link to={`/${owner}/${projectsId}/commits/${text}`} className="primaryColor">{text}</Link>}},
|
||||
{ title: '同步日志', dataIndex: 'action', align: 'center', className:"primaryColor recordColumns"},
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ function RecordList(props){
|
|||
const {sync_repositories_unique=[]} = storeDetail || {};
|
||||
const [token, setToken] = useState({});
|
||||
|
||||
function inviteClick() {
|
||||
const copyEle = document.querySelector('#webhook'); // 获取要复制的节点
|
||||
function inviteClick(idName) {
|
||||
console.log('idName', idName);
|
||||
const copyEle = document.querySelector(idName); // 获取要复制的节点
|
||||
if (!copyEle) {
|
||||
console.error("您的CopyTool未设置正确的inputId");
|
||||
return;
|
||||
|
|
@ -35,7 +36,7 @@ function RecordList(props){
|
|||
<Breadcrumb.Item className="font-15"><Link to={`/${owner}/${projectsId}/service/reposyncer`} className="primaryColor">同步分支列表</Link></Breadcrumb.Item>
|
||||
<Breadcrumb.Item className="font-15">同步配置</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
{sync_repositories_unique.map(item => {
|
||||
{sync_repositories_unique.map((item, index) => {
|
||||
const {type, external_repo_address, sync_repository_ids} = item
|
||||
return <Fragment>
|
||||
<div className="font-15 mt20 mb7">同步仓库平台</div>
|
||||
|
|
@ -44,11 +45,11 @@ function RecordList(props){
|
|||
<div className="infoBox"><a href={external_repo_address} target="_blank">{external_repo_address}</a></div>
|
||||
<div className="font-15 mt20 mb7">Webhook接受请求地址</div>
|
||||
<Input
|
||||
id="webhook"
|
||||
id={`webhook${index}`}
|
||||
className="webhookCopyBox copyWebhookInput"
|
||||
value={`${window.location.origin}/api/v1/${owner}/${projectsId}/sync_repositories/sync?sync_direction=2&repo_type=${type}`}
|
||||
readOnly
|
||||
addonAfter={<a className="primaryColor" onClick={inviteClick}>复制链接</a>}
|
||||
addonAfter={<a className="primaryColor" onClick={()=>{inviteClick(`#webhook${index}`)}}>复制链接</a>}
|
||||
/>
|
||||
<div className="font-15 mt20 mb7">同步仓库授权</div>
|
||||
<Input.Password addonBefore="token" placeholder={`请输入${type.split("::")[1]}的授权token`} style={{width: '60%'}} defaultValue={item.external_token} maxLength={300} onChange={(e)=>{
|
||||
|
|
|
|||
|
|
@ -318,13 +318,9 @@
|
|||
}
|
||||
.storeSetting{
|
||||
.infoBox{
|
||||
width: 70%;
|
||||
padding: 3px 15px;
|
||||
background-color: rgba(70,106,255,0.03);
|
||||
}
|
||||
.webhookCopyBox{
|
||||
width: 70%;
|
||||
}
|
||||
.branchTable{
|
||||
width: 30%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { withRouter } from "react-router";
|
|||
import { SnackbarHOC } from "educoder";
|
||||
import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC";
|
||||
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
|
||||
import ProjectDetail from '../Main/Detail'
|
||||
import '../css/index.scss';
|
||||
import './Index.scss';
|
||||
|
||||
|
|
@ -35,10 +36,10 @@ const SubDetailIndex = Loadable({
|
|||
loading: Loading,
|
||||
});
|
||||
// forge项目详情
|
||||
const ProjectDetail = Loadable({
|
||||
loader: () => import("../Main/Detail"),
|
||||
loading: Loading,
|
||||
});
|
||||
// const ProjectDetail = Loadable({
|
||||
// loader: () => import("../Main/Detail"),
|
||||
// loading: Loading,
|
||||
// });
|
||||
const team = CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(
|
||||
((props)=>{
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -6,15 +6,17 @@ import { withRouter } from "react-router";
|
|||
import { SnackbarHOC } from "educoder";
|
||||
import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC";
|
||||
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
|
||||
import ProjectDetail from '../Main/Detail'
|
||||
|
||||
const Infos = Loadable({
|
||||
loader: () => import("./Infos"),
|
||||
loading: Loading,
|
||||
});
|
||||
// forge项目详情
|
||||
const ProjectDetail = Loadable({
|
||||
loader: () => import("../Main/Detail"),
|
||||
loading: Loading,
|
||||
});
|
||||
// const ProjectDetail = Loadable({
|
||||
// loader: () => import("../Main/Detail"),
|
||||
// loading: Loading,
|
||||
// });
|
||||
|
||||
class DetailTop extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function Index(props) {
|
|||
|
||||
const [ bannerTab , setBannerTab ] = useState(undefined);
|
||||
const register = props && props.mygetHelmetapi && props.mygetHelmetapi.common && props.mygetHelmetapi.common.register;
|
||||
const {mygetHelmetapi:{name}} = props
|
||||
const name = props && props.mygetHelmetapi && props.mygetHelmetapi.name ;
|
||||
const { current_user } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
|
|||
|
|
@ -186,4 +186,57 @@ i.color-orange05:hover {
|
|||
|
||||
.color-light-green {
|
||||
color: #29bd8b !important;
|
||||
}
|
||||
|
||||
.AlignTop {
|
||||
display:flex;
|
||||
align-items: flex-start
|
||||
}
|
||||
.WhiteBack {
|
||||
background-color:#fff;
|
||||
border-radius:5px
|
||||
}
|
||||
.Box {
|
||||
display:flex;
|
||||
align-items:flex-start
|
||||
}
|
||||
.LongWidth {
|
||||
flex:1;
|
||||
width:0;
|
||||
border-radius:5px;
|
||||
margin-bottom:30px
|
||||
}
|
||||
.ShortWidth {
|
||||
width:300px;
|
||||
border-radius:5px;
|
||||
margin-bottom:30px
|
||||
}
|
||||
.Gap {
|
||||
padding-left:20px;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
.AlignCenter {
|
||||
display:flex;
|
||||
align-items: center
|
||||
}
|
||||
.FlexAJ {
|
||||
display:flex;
|
||||
align-items: center;
|
||||
justify-content: space-between
|
||||
}
|
||||
.mt25 {
|
||||
margin-top:25px
|
||||
}
|
||||
.User {
|
||||
display:flex;
|
||||
font-weight: bold;
|
||||
align-items: center
|
||||
}
|
||||
.User img {
|
||||
width:30px;
|
||||
height:30px;
|
||||
border-radius:50%
|
||||
}
|
||||
.User span {
|
||||
margin-left:8px
|
||||
}
|
||||
10
src/index.js
10
src/index.js
|
|
@ -9,13 +9,17 @@ import { configureUrlQuery } from 'react-url-query';
|
|||
import history from './history';
|
||||
import configureStore from './redux/stores/configureStore';
|
||||
import { Provider } from 'react-redux';
|
||||
import StyleContext from 'isomorphic-style-loader/StyleContext'
|
||||
|
||||
configureUrlQuery({ history });
|
||||
const store = configureStore();
|
||||
|
||||
window.__useKindEditor = false;
|
||||
|
||||
|
||||
const insertCss = (...styles) => {
|
||||
const removeCss = styles.map(style => style._insertCss && style._insertCss());
|
||||
return () => removeCss.forEach(dispose => dispose && dispose())
|
||||
}
|
||||
|
||||
const render = (Component) => {
|
||||
ReactDOM.hydrate(
|
||||
|
|
@ -24,7 +28,9 @@ const render = (Component) => {
|
|||
<Provider store={store}>
|
||||
|
||||
<BrowserRouter basename='/'>
|
||||
<Route path='/' component={App}></Route>
|
||||
<StyleContext.Provider value={{insertCss}}>
|
||||
<Route path='/' component={App}></Route>
|
||||
</StyleContext.Provider>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ class ImageLayer extends Component {
|
|||
|
||||
render() {
|
||||
let { showImage, imageSrc, } = this.props;
|
||||
if (__SERVER__) {
|
||||
return null
|
||||
}
|
||||
return ReactDOM.createPortal(
|
||||
<div>
|
||||
{showImage ?
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ export function ImageLayerOfCommentHOC(options = {}) {
|
|||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$(options.parentSelector || ".commentsDelegateParent", 'click', this.onDelegateClick)
|
||||
if (__CLIENT__) {
|
||||
$(options.parentSelector || ".commentsDelegateParent", 'click', this.onDelegateClick)
|
||||
}
|
||||
}
|
||||
|
||||
onImageLayerClose = () => {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
globalLoading: false,
|
||||
dataquerys: {},
|
||||
isloginCancel: undefined,
|
||||
mygetHelmetapi: null,
|
||||
mygetHelmetapi: __CLIENT__ ? null : defaultSetting,
|
||||
giteaVisible:false,
|
||||
email:undefined,
|
||||
completeProfile:false,
|
||||
|
|
@ -61,12 +61,13 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
window.removeEventListener('keyup', this.keyupListener)
|
||||
}
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
current_user: {}
|
||||
})
|
||||
if (!__SERVER__) {
|
||||
this.fetchUsers();
|
||||
} else {
|
||||
this.setState({
|
||||
current_user: {}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,7 +317,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
{...this.state}
|
||||
/> : ""}
|
||||
{npsModalVisible && <NpsModal closeNpsModal={()=>{this.closeNpsModal()}} npsActionType={npsActionType} npsActionId={npsActionId}/>}
|
||||
{ !pathCheck && <Header {...this.state} {...this.props} {...common} publicNav={publicNav}></Header> }
|
||||
{ !pathCheck && <Header {...this.state} {...this.props} {...common} publicNav={publicNav} mygetHelmetapi={ mygetHelmetapi }></Header> }
|
||||
<Spin spinning={this.state.globalLoading} delay={0} className="globalSpin"
|
||||
size="large" tip={this._gLoadingTip || "加载中..."}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -14,45 +14,83 @@ const axiosInstance = axios.create({
|
|||
withCredentials: true
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => {
|
||||
return response;
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
//
|
||||
export async function getProjectFunc (owner, projectsId) {
|
||||
const url = `${baseUrl}/${owner}/${projectsId}/simple.json`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
||||
export async function getProjectDetailFunc (owner, projectsId) {
|
||||
const url = `${baseUrl}/${owner}/${projectsId}/detail.json`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
||||
export async function getProjectEntriesFunc (owner, projectsId, branch) {
|
||||
const url = `${baseUrl}/${owner}/${projectsId}/entries.json`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url, {params: { ref: branch }});
|
||||
}
|
||||
|
||||
export async function getBannerFunc (owner, projectsId) {
|
||||
const url = `${baseUrl}/${owner}/${projectsId}/menu_list.json`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
||||
export async function getProjectReadMe (owner, projectsId, branch) {
|
||||
const url = `${baseUrl}/${owner}/${projectsId}/readme.json`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url, {params: { ref: branch }});
|
||||
}
|
||||
|
||||
export async function getOwnerInfo (owner) {
|
||||
const url = `${baseUrl}/owners/${owner}.json`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
||||
|
||||
export async function getMainInfos (deptId) {
|
||||
const url = `${baseZoneUrl}/zone/open/zoneKey/${deptId}`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
||||
export async function getNewsDetail (id) {
|
||||
const url = `${baseZoneUrl}/cms/doc/open/${id}`;
|
||||
if (__SERVER__) {
|
||||
axiosInstance.defaults.headers.cookie = domObj.window.document.cookie
|
||||
}
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
||||
export async function getSetting () {
|
||||
const url = `${baseUrl}/setting.json`;
|
||||
return axiosInstance.get(url);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue