Compare commits

...

8 Commits

Author SHA1 Message Date
liuhengshu 87bc88600e fix:开源仓嵌入方式
Context testing / dump_contexts_to_log (push) Successful in 1m34s Details
Context testing / run-02 (push) Successful in 4m7s Details
Context testing / run-01 (push) Successful in 4m9s Details
Context testing / run-04 (push) Successful in 5m2s Details
Context testing / run-03 (push) Successful in 5m6s Details
2026-07-31 09:26:39 +08:00
谢思 6409ad6940 智能体登录直接调用agent服务,不走微服务
Context testing / run-01 (push) Failing after 55s Details
Context testing / run-02 (push) Failing after 56s Details
Context testing / run-03 (push) Failing after 55s Details
Context testing / run-04 (push) Failing after 56s Details
Context testing / dump_contexts_to_log (push) Failing after 56s Details
2026-07-24 17:53:56 +08:00
谢思 5aae70e4c7 头像地址处理
Context testing / run-01 (push) Failing after 54s Details
Context testing / run-02 (push) Failing after 55s Details
Context testing / run-03 (push) Failing after 55s Details
Context testing / run-04 (push) Failing after 55s Details
Context testing / dump_contexts_to_log (push) Failing after 55s Details
2026-07-23 17:30:20 +08:00
谢思 811bdc3d10 论坛首页标题增加动画
Context testing / run-01 (push) Failing after 54s Details
Context testing / run-02 (push) Failing after 55s Details
Context testing / run-03 (push) Failing after 55s Details
Context testing / run-04 (push) Failing after 55s Details
Context testing / dump_contexts_to_log (push) Failing after 55s Details
2026-07-23 11:46:00 +08:00
谢思 406f0dc51e ai资源广场增加智能体列表,点击跳转详情iframe嵌入智能体对话页面 2026-07-23 11:25:50 +08:00
谢思 fe17dac74f 论坛首页改版、增加分论坛列表页 2026-07-23 11:24:58 +08:00
谢思 5b1d0016c1 开源软件仓增加传参,不返回fork的子仓库
Context testing / run-01 (push) Successful in 2m22s Details
Context testing / dump_contexts_to_log (push) Successful in 1m31s Details
Context testing / run-02 (push) Successful in 2m52s Details
Context testing / run-04 (push) Successful in 3m53s Details
Context testing / run-03 (push) Successful in 3m56s Details
2026-07-21 10:04:17 +08:00
谢思 3ebc2f6ee4 解决仓库详情页接口往内网ip发请求导致jw不可访问
Context testing / dump_contexts_to_log (push) Successful in 56s Details
Context testing / run-01 (push) Successful in 3m35s Details
Context testing / run-02 (push) Successful in 3m36s Details
Context testing / run-04 (push) Successful in 4m56s Details
Context testing / run-03 (push) Successful in 5m4s Details
2026-07-20 19:13:09 +08:00
58 changed files with 1456 additions and 524 deletions

View File

@ -56,4 +56,4 @@ export const deepRoutes = [
] ]
// 不进行组织或个人判断 // 不进行组织或个人判断
export const specialRoute = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "undefined", "admins", "oauth", "competitions", "hiagent","sf", "development", "governance"] export const specialRoute = ["ossRepo", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "undefined", "admins", "oauth", "competitions", "hiagent","sf", "development", "governance","explore"]

BIN
build.zip

Binary file not shown.

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 2340181 */ font-family: "iconfont"; /* Project id 2340181 */
src: url('iconfont.woff2?t=1778741577467') format('woff2'), src: url('iconfont.woff2?t=1784688510240') format('woff2'),
url('iconfont.woff?t=1778741577467') format('woff'), url('iconfont.woff?t=1784688510240') format('woff'),
url('iconfont.ttf?t=1778741577467') format('truetype'); url('iconfont.ttf?t=1784688510240') format('truetype');
} }
.iconfont { .iconfont {
@ -13,10 +13,94 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-xiala5:before { .icon-zhinengti:before {
content: "\ea0f";
}
.icon-a-fabu1:before {
content: "\ea0d";
}
.icon-a-search-ai6c9d7b621:before {
content: "\ea0e";
}
.icon-remenhuati:before {
content: "\e9df"; content: "\e9df";
} }
.icon-chengguolingyu:before {
content: "\ea09";
}
.icon-chengguoleixing:before {
content: "\ea0a";
}
.icon-chengguolaiyuan:before {
content: "\ea0b";
}
.icon-zhinengzhushou:before {
content: "\ea08";
}
.icon-shine-search:before {
content: "\ea07";
}
.icon-zuojiantou3:before {
content: "\ea05";
}
.icon-youjiantou2:before {
content: "\ea06";
}
.icon-fanhuagongju:before {
content: "\ea02";
}
.icon-jingxuanxiangmu1:before {
content: "\ea03";
}
.icon-gengduo2:before {
content: "\ea04";
}
.icon-shengwuyiyao:before {
content: "\e9f5";
}
.icon-youjiantou1:before {
content: "\e9fa";
}
.icon-zuojiantou2:before {
content: "\e9fd";
}
.icon-daqihaiyanghuanjing:before {
content: "\e9fe";
}
.icon-tianwendiliwuli:before {
content: "\e9ff";
}
.icon-xinnengyuanxincailiao:before {
content: "\ea00";
}
.icon-gongyeshejizhizao:before {
content: "\ea01";
}
.icon-tiezi:before {
content: "\e9f4";
}
.icon-yulan:before { .icon-yulan:before {
content: "\e9f1"; content: "\e9f1";
} }

File diff suppressed because one or more lines are too long

View File

@ -6,12 +6,159 @@
"description": "", "description": "",
"glyphs": [ "glyphs": [
{ {
"icon_id": "47540662", "icon_id": "48129347",
"name": "下拉", "name": "智能体",
"font_class": "xiala5", "font_class": "zhinengti",
"unicode": "ea0f",
"unicode_decimal": 59919
},
{
"icon_id": "48114624",
"name": "发布 (1)",
"font_class": "a-fabu1",
"unicode": "ea0d",
"unicode_decimal": 59917
},
{
"icon_id": "48114623",
"name": "search-ai.6c9d7b62 (1)",
"font_class": "a-search-ai6c9d7b621",
"unicode": "ea0e",
"unicode_decimal": 59918
},
{
"icon_id": "48114629",
"name": "热门话题",
"font_class": "remenhuati",
"unicode": "e9df", "unicode": "e9df",
"unicode_decimal": 59871 "unicode_decimal": 59871
}, },
{
"icon_id": "47969738",
"name": "成果领域",
"font_class": "chengguolingyu",
"unicode": "ea09",
"unicode_decimal": 59913
},
{
"icon_id": "47969739",
"name": "成果类型",
"font_class": "chengguoleixing",
"unicode": "ea0a",
"unicode_decimal": 59914
},
{
"icon_id": "47969740",
"name": "成果来源",
"font_class": "chengguolaiyuan",
"unicode": "ea0b",
"unicode_decimal": 59915
},
{
"icon_id": "45990748",
"name": "智能助手",
"font_class": "zhinengzhushou",
"unicode": "ea08",
"unicode_decimal": 59912
},
{
"icon_id": "47826921",
"name": "shine-search",
"font_class": "shine-search",
"unicode": "ea07",
"unicode_decimal": 59911
},
{
"icon_id": "47798947",
"name": "左箭头",
"font_class": "zuojiantou3",
"unicode": "ea05",
"unicode_decimal": 59909
},
{
"icon_id": "47798946",
"name": "右箭头",
"font_class": "youjiantou2",
"unicode": "ea06",
"unicode_decimal": 59910
},
{
"icon_id": "47778051",
"name": "泛化工具",
"font_class": "fanhuagongju",
"unicode": "ea02",
"unicode_decimal": 59906
},
{
"icon_id": "47778050",
"name": "精选项目",
"font_class": "jingxuanxiangmu1",
"unicode": "ea03",
"unicode_decimal": 59907
},
{
"icon_id": "47778049",
"name": "更多",
"font_class": "gengduo2",
"unicode": "ea04",
"unicode_decimal": 59908
},
{
"icon_id": "47778058",
"name": "生物医药",
"font_class": "shengwuyiyao",
"unicode": "e9f5",
"unicode_decimal": 59893
},
{
"icon_id": "47778057",
"name": "右箭头",
"font_class": "youjiantou1",
"unicode": "e9fa",
"unicode_decimal": 59898
},
{
"icon_id": "47778056",
"name": "左箭头",
"font_class": "zuojiantou2",
"unicode": "e9fd",
"unicode_decimal": 59901
},
{
"icon_id": "47778052",
"name": "大气海洋环境",
"font_class": "daqihaiyanghuanjing",
"unicode": "e9fe",
"unicode_decimal": 59902
},
{
"icon_id": "47778055",
"name": "天文地理物理",
"font_class": "tianwendiliwuli",
"unicode": "e9ff",
"unicode_decimal": 59903
},
{
"icon_id": "47778054",
"name": "新能源新材料",
"font_class": "xinnengyuanxincailiao",
"unicode": "ea00",
"unicode_decimal": 59904
},
{
"icon_id": "47778053",
"name": "工业设计制造",
"font_class": "gongyeshejizhizao",
"unicode": "ea01",
"unicode_decimal": 59905
},
{
"icon_id": "47756092",
"name": "帖子",
"font_class": "tiezi",
"unicode": "e9f4",
"unicode_decimal": 59892
},
{ {
"icon_id": "47540661", "icon_id": "47540661",
"name": "预览", "name": "预览",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -236,6 +236,11 @@ const GovernanceIframe = Loadable({
loading: Loading, loading: Loading,
}); });
const OssRepoIframe = Loadable({
loader: () => import("./forge/ossRepoIframe"),
loading: Loading,
});
const Forums = Loadable({ const Forums = Loadable({
loader: () => import("./forums"), loader: () => import("./forums"),
loading: Loading, loading: Loading,
@ -252,7 +257,7 @@ const AI = Loadable({
}); });
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。 // 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "hiagent","sf", "development", "governance", "forums", "news", "exploreStore", "factory", "zoneAdmin", "ai"]; const keyWord = ["ossRepo", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "hiagent","sf", "development", "governance", "explore","forums", "news", "exploreStore", "factory", "zoneAdmin", "ai"];
class App extends Component { class App extends Component {
constructor(props) { constructor(props) {
@ -636,7 +641,7 @@ class App extends Component {
<Route path="/search" component={Search} mygetHelmetapi={mygetHelmetapi} /> <Route path="/search" component={Search} mygetHelmetapi={mygetHelmetapi} />
{/* 项目标签列表页 */} {/* 项目标签列表页 */}
<Route path="/explore/topic/:id/:name" component={Topic} mygetHelmetapi={mygetHelmetapi} /> <Route path="/ossRepo/topic/:id/:name" component={Topic} mygetHelmetapi={mygetHelmetapi} />
<Route path={"/:OIdentifier/enterprise"} <Route path={"/:OIdentifier/enterprise"}
render={ render={
@ -723,7 +728,7 @@ class App extends Component {
<Projects {...this.props} {...props} /> <Projects {...this.props} {...props} />
)} )}
></Route> ></Route>
<Route exact path="/explore" <Route exact path="/ossRepo"
render={ render={
(props) => ( (props) => (
<Projects {...this.props} {...props} {...this.state}/> <Projects {...this.props} {...props} {...this.state}/>
@ -745,6 +750,15 @@ class App extends Component {
}> }>
</Route> </Route>
<Route
path={"/explore"}
render={
(props) => {
return (<OssRepoIframe {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route <Route
path={"/factory"} path={"/factory"}
render={ render={

29
src/ai/api.js Normal file
View File

@ -0,0 +1,29 @@
import fetch from './fetch';
import fetchLogin from './fetchLogin';
/**智能体分类列表***/
export function getAgentTypeList(params) {
return fetch({
url: `/console-api/home-page/agent-square/get-bot-type-list`,
method: 'get',
params
});
}
/**智能体列表***/
export function getAgentList(params) {
return fetch({
url: `/console-api/home-page/agent-square/get-bot-page-by-type`,
method: 'get',
params
});
}
/**智能体登录***/
export function agentLogin(data) {
return fetchLogin({
url: `/api/login/oauth/access_token`,
method: 'post',
data
});
}

106
src/ai/detail/agent.jsx Normal file
View File

@ -0,0 +1,106 @@
import React, { useEffect, useState } from "react";
import { withRouter } from "react-router";
import { httpUrl } from '../../ai/fetch';
import cookie from 'react-cookies';
import { aiAgentLogin } from "../../forge/Information/api";
import { agentLogin } from "../api";
function Agent(props) {
const { match, current_user, history, showLoginDialog } = props;
const { id } = match.params;
const { login } = current_user || {};
const [iframeUrl, setIframeUrl] = useState('');
useEffect(() => {
document.title = "智能体-AI资源";
checkLoginAndGetIframeUrl();
}, []);
// iframe URL
const checkLoginAndGetIframeUrl = async () => {
try {
//
if (!login) {
history.push(`/login?go_page=/ai/agents/${id}`);
return;
}
// Cookieagent
const agentCookie = cookie.load('agent');
let currentTokenData = null;
if (!agentCookie) {
// Cookieagent
const res = await agentLogin({
grant_type: 'password',
client_id: '4b25ca036879216a020a',
username: 'hnxjy',
password: '123456',
scope: 'profile'
});
if (res && res.data && res.data) {
const { access_token, expires_in } = res.data;
// + expires_in
const expiryDate = new Date(Date.now() + (expires_in) * 1000);
// tokenagent Cookie
currentTokenData = access_token;
cookie.save('agent', currentTokenData, { expires: expiryDate, path: '/' });
}
} else {
// CookieCookietoken
currentTokenData = agentCookie;
}
// iframe URLtoken
const url = getIframeUrl(id, currentTokenData);
setIframeUrl(url);
} catch (err) {
console.error('检查登录状态失败:', err);
}
};
// iframe URLtokenURL
const getIframeUrl = (agentId, accessToken) => {
if (!httpUrl || !agentId) return '';
try {
const urlObj = new URL(httpUrl);
const baseOrigin = `${urlObj.protocol}//${urlObj.host}`;
let url = `${baseOrigin}/chat/${agentId}`;
// tokenURL
if (accessToken) {
const separator = url.includes('?') ? '&' : '?';
url += `${separator}token=${encodeURIComponent(accessToken)}`;
}
return url;
} catch (e) {
return '';
}
};
return (
<div className="agent-container" style={{ width: '100%', height: '100vh', overflow: 'hidden' }}>
{iframeUrl ? (
<iframe
src={iframeUrl}
style={{
width: '100%',
height: '100%',
border: 'none',
}}
allow="clipboard-read; clipboard-write"
/>
) : (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
加载中...
</div>
)}
</div>
);
}
export default withRouter(Agent);

60
src/ai/fetch.js Normal file
View File

@ -0,0 +1,60 @@
import axios from 'axios';
import { message , notification } from 'antd';
import cookie from 'react-cookies';
function beforeFetch(actionUrl){
if (window.location.href.indexOf('localhost') < 0) {
axios.defaults.withCredentials = true;
}
const config = {
// headers:{Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjo0NCwidXNlcl9rZXkiOiI3NWY3MmJmYmE2NGU4N2U3NWVjNWMwZDk2OThkMDFiN2EyODE0YjYwIiwidXNlcm5hbWUiOiJpbm5vdiJ9.IviAyZQjPFc4rT3ejAKfJYx3_QgS78RCKHx1XS-A4uI_mrc7_l6wK76HVIoPCEvP0Qimzyc6dpeVW4cE2fILZw"},
baseURL: actionUrl,
timeout: 1800000, // 请求超时时间
}
const service = axios.create(config);
service.interceptors.request.use(request => {
// let deptId = sessionStorage.getItem('deptId')
// request.headers.Authorization = cookie.load('autologin_trustie')
// if (deptId) {
// // 用于权限区分
// request.headers['Dept-Id'] = deptId
// }
return request
})
service.interceptors.response.use(
response => {
const res = response||{};
if(res && res.data && res.data.code === 404){
message.error("错误链接!");
window.location.href = '/nopage';
return Promise.reject('error');
}else if(res && res.data && res.data.code === 500){
message.error(res.data.msg);
return Promise.reject('error');
}else{
return response;
}
},
error => {
console.log(error);
// notification.open({
// message: "提示",
// description: error.message,
// });
// return Promise.reject(error);
}
)
return service;
}
let settings = localStorage.chromesetting&& localStorage.chromesetting !=="undefined"&& JSON.parse(localStorage.chromesetting);
// 根据当前IP判断使用哪个zone配置
const isSpecificIP = window.location.hostname.startsWith('27.');
// let actionUrl = settings && (isSpecificIP ? settings.common.zone : settings.common.zone_local) + '/zoneAdmin/api';
let actionUrl = settings && settings.common && settings.common.agent
const service = beforeFetch(actionUrl);
export const httpUrl = actionUrl;
export default service;

61
src/ai/fetchLogin.js Normal file
View File

@ -0,0 +1,61 @@
import axios from 'axios';
import { message , notification } from 'antd';
import cookie from 'react-cookies';
function beforeFetch(actionUrl){
if (window.location.href.indexOf('localhost') < 0) {
axios.defaults.withCredentials = true;
}
const config = {
// headers:{Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjo0NCwidXNlcl9rZXkiOiI3NWY3MmJmYmE2NGU4N2U3NWVjNWMwZDk2OThkMDFiN2EyODE0YjYwIiwidXNlcm5hbWUiOiJpbm5vdiJ9.IviAyZQjPFc4rT3ejAKfJYx3_QgS78RCKHx1XS-A4uI_mrc7_l6wK76HVIoPCEvP0Qimzyc6dpeVW4cE2fILZw"},
baseURL: actionUrl,
timeout: 1800000, // 请求超时时间
}
const service = axios.create(config);
service.interceptors.request.use(request => {
// let deptId = sessionStorage.getItem('deptId')
// request.headers.Authorization = cookie.load('autologin_trustie')
// if (deptId) {
// // 用于权限区分
// request.headers['Dept-Id'] = deptId
// }
return request
})
service.interceptors.response.use(
response => {
const res = response||{};
if(res && res.data && res.data.code === 404){
message.error("错误链接!");
window.location.href = '/nopage';
return Promise.reject('error');
}else if(res && res.data && res.data.code === 500){
message.error(res.data.msg);
return Promise.reject('error');
}else{
return response;
}
},
error => {
console.log(error);
// notification.open({
// message: "提示",
// description: error.message,
// });
// return Promise.reject(error);
}
)
return service;
}
let settings = localStorage.chromesetting&& localStorage.chromesetting !=="undefined"&& JSON.parse(localStorage.chromesetting);
// 根据当前IP判断使用哪个zone配置
const isSpecificIP = window.location.hostname.startsWith('27.');
// let actionUrl = settings && (isSpecificIP ? settings.common.zone : settings.common.zone_local) + '/zoneAdmin/api';
let actionUrl = settings && settings.common && settings.common.agent_login;
const service = beforeFetch(actionUrl);
export const httpUrl = actionUrl;
export default service;

BIN
src/ai/image/card_bk3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -25,6 +25,10 @@ const MyResources = Loadable({
loader: () => import("./my-resources"), loader: () => import("./my-resources"),
loading: Loading, loading: Loading,
}) })
const Agents = Loadable({
loader: () => import("./detail/agent"),
loading: Loading,
})
function AI(props) { function AI(props) {
const { current_user: { login } } = props const { current_user: { login } } = props
@ -56,6 +60,12 @@ function AI(props) {
return <div className="ai_resouce_page"> return <div className="ai_resouce_page">
<Switch {...props}> <Switch {...props}>
<Route
path="/ai/agents/:id"
render={(p) => (
<Agents {...props} {...p} aiZoneInfo={aiZoneInfo} roleByZone={roleByZone}/>
)}
></Route>
<Route <Route
path="/ai/:type/my" path="/ai/:type/my"
render={(p) => ( render={(p) => (

View File

@ -1,20 +1,126 @@
import { Fragment, useEffect, useState } from "react"; import { Fragment, useEffect, useState } from "react";
import './index.scss'; import './index.scss';
// Mock 12.md
const agentMockCate = [
{ id: "work", name: "工作" },
{ id: "study", name: "学习" },
{ id: "writing", name: "写作" },
{ id: "programming", name: "编程" },
{ id: "life", name: "生活" },
{ id: "health", name: "健康" }
];
const agentMockList = [
//
{
id: "meeting-minutes",
name: "会议纪要助手",
image: "",
desc: "专注于会议纪要的整理与提炼,帮助用户快速生成结构清晰、重点突出的会议纪要。",
cate: "work"
},
{
id: "project-management",
name: "项目管理助手",
image: "",
desc: "协助项目管理者进行任务拆解、进度跟踪、风险识别与资源协调,提升项目管理效率。",
cate: "work"
},
//
{
id: "knowledge-combination",
name: "知识梳理助手",
image: "",
desc: "帮助用户将零散的知识点系统化整理,构建知识框架,提升学习效率与记忆效果。",
cate: "study"
},
{
id: "training-planning",
name: "培训规划助手",
image: "",
desc: "根据岗位需求与员工能力现状,协助制定科学合理的培训计划与课程体系。",
cate: "study"
},
//
{
id: "official-writing",
name: "公文写作助手",
image: "",
desc: "专注于各类公文的撰写与润色,确保格式规范、用语准确、逻辑严密。",
cate: "writing"
},
{
id: "report-writing",
name: "报告撰写助手",
image: "",
desc: "协助用户撰写各类工作报告、调研报告、总结报告,提升报告的专业性与可读性。",
cate: "writing"
},
//
{
id: "code-review",
name: "代码审查助手",
image: "",
desc: "协助开发人员进行代码审查,发现潜在问题,提升代码质量与可维护性。",
cate: "programming"
},
{
id: "tech-documentation",
name: "技术文档助手",
image: "",
desc: "协助技术人员撰写 API 文档、系统设计文档、操作手册等技术文档,确保文档清晰准确。",
cate: "programming"
},
//
{
id: "schedule-planning",
name: "日程规划助手",
image: "",
desc: "协助用户合理安排日常工作与生活日程,提升时间利用效率,平衡工作与生活。",
cate: "life"
},
{
id: "meeting-arrangement",
name: "会议安排助手",
image: "",
desc: "协助用户高效安排会议时间、协调与会人员、准备会议材料,减少会议组织成本。",
cate: "life"
},
//
{
id: "schedule-management",
name: "作息管理助手",
image: "",
desc: "协助用户建立科学的作息规律,提供睡眠改善建议与作息调整方案,促进身心健康。",
cate: "health"
},
{
id: "exercise-planning",
name: "运动计划助手",
image: "",
desc: "根据用户的身体状况与运动目标,协助制定科学合理的运动计划,促进身体健康。",
cate: "health"
}
];
import { aiResouceType, aiResouceTypeKey, iconByCateIndex, iconByCateName } from "../static"; import { aiResouceType, aiResouceTypeKey, iconByCateIndex, iconByCateName } from "../static";
import icon1 from '../image/icon1.png'; import icon1 from '../image/icon1.png';
import icon2 from '../image/icon2.png'; import icon2 from '../image/icon2.png';
import icon3 from '../image/icon3.png'; import icon3 from '../image/icon3.png';
import icon5 from '../image/icon5.png'; import icon5 from '../image/icon5.png';
import icon6 from '../image/icon6.png'; import icon6 from '../image/icon6.png';
import { Divider, Input, Pagination } from "antd"; import { Divider, Input, Pagination, Spin } from "antd";
import ActionItem from "../../factory/resource/components/ActionItem"; import ActionItem from "../../factory/resource/components/ActionItem";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { getAISourceList, getProjectsLists, getProjectsTypeLists, getSourceZoneList } from "../../forge/Information/api"; import { getAISourceList, getProjectsLists, getProjectsTypeLists, getSourceZoneList } from "../../forge/Information/api";
import moment from "moment"; import moment from "moment";
import Nodata from "../../forge/Nodata"; import Nodata from "../../forge/Nodata";
import { getAgentList, getAgentTypeList } from "../api";
import { httpUrl } from "../fetch";
function List(props) { function List(props) {
const { match: { params }, history, aiZoneInfo, current_user } = props || {};
const { match: { params }, history, aiZoneInfo, current_user, showLoginDialog } = props || {};
const type = params.type || aiResouceType[0].key const type = params.type || aiResouceType[0].key
const [activeCategory, setActiveCategory] = useState(); const [activeCategory, setActiveCategory] = useState();
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
@ -22,25 +128,58 @@ function List(props) {
const [cate, setCate] = useState([]); const [cate, setCate] = useState([]);
const [list, setList] = useState(); const [list, setList] = useState();
const [search, setSearch] = useState(); const [search, setSearch] = useState();
const [loading, setLoading] = useState(false);
const limit = 12;
const { id: aiZoneId } = aiZoneInfo || {} const { id: aiZoneId } = aiZoneInfo || {}
const typeDetail = type && aiResouceType.find(i => i.key === type); const aiTypeInList = [
...aiResouceType,
{ key: "agents", name: "智能体", icon: "icon-zhinengti", intro: "驱动行业数智化", desc: "矩阵覆盖全场景需求,极速响应精准输出,低成本释放 AI 算力,赋能每一项任务。", createIntro: "上传您的智能体文件" }
]
const typeDetail = type && aiTypeInList.find(i => i.key === type);
useEffect(() => { useEffect(() => {
document.title = `AI资源广场`; document.title = `AI资源广场`;
}, []); }, []);
// agents aiZoneId
useEffect(() => { useEffect(() => {
setActiveCategory(undefined); if (type === "agents") {
if(!httpUrl){
setCate(agentMockCate);
return;
}
getAgentTypeList().then(res => {
if (res && res.data && res.data.data) {
const data = res.data.data
setCate(data.map(item=>{
return {
...item,
name: item.typeName,
id: item.typeKey
}
}));
}
})
}
}, [type])
// aiZoneId aiZoneId
useEffect(() => {
if (type === "agents") return; // agents useEffect
if (!aiZoneId) return;
switch (type) { switch (type) {
case "codes": case "codes":
aiZoneId && getProjectsTypeLists(aiZoneId).then(res => { getProjectsTypeLists(aiZoneId).then(res => {
if (res && res.data && res.data.rows) { if (res && res.data && res.data.rows) {
setCate(res.data.rows); setCate(res.data.rows);
} }
}) })
break; break;
default: default:
aiZoneId && getSourceZoneList(aiZoneId, { getSourceZoneList(aiZoneId, {
domainCategory: aiResouceTypeKey[type] domainCategory: aiResouceTypeKey[type]
}).then(res => { }).then(res => {
if (res && res.data && res.data.rows) { if (res && res.data && res.data.rows) {
@ -52,22 +191,70 @@ function List(props) {
}, [type, aiZoneId]) }, [type, aiZoneId])
useEffect(() => { useEffect(() => {
if(!aiZoneId) return if (type === "agents") {
setLoading(true);
if(!httpUrl){
let filteredList = [...agentMockList];
if (activeCategory) {
filteredList = filteredList.filter(item => item.cate === activeCategory);
}
if (search) {
filteredList = filteredList.filter(item =>
item.name.toLowerCase().includes(search.toLowerCase()) ||
item.desc.toLowerCase().includes(search.toLowerCase())
);
}
const start = (page - 1) * limit;
setList(filteredList.slice(start, start + limit));
setTotal(filteredList.length);
setLoading(false);
return;
}
getAgentList({
page: page,
pageSize: limit,
search: search,
type: activeCategory
}).then(res => {
if (res && res.data && res.data.data) {
const data = res.data.data;
setList(data.pageData.map(row => {
const { botId, botCoverUrl, botName, botDesc } = row;
return {
id: botId,
name: botName,
image: botCoverUrl,
desc: botDesc,
}}
));
setTotal(data.totalCount);
}
}).finally(() => {
setLoading(false);
})
}
}, [type, activeCategory, page, search])
useEffect(() => {
if (type === "agents" || !aiZoneId) return
setLoading(true);
switch (type) { switch (type) {
case "codes": case "codes":
getProjectsLists(aiZoneId, { getProjectsLists(aiZoneId, {
pageNum: page, pageNum: page,
pageSize: 10, pageSize: limit,
projectTypeId: activeCategory, projectTypeId: activeCategory,
name: search name: search
}).then(res => { }).then(res => {
if (res && res.data && res.data.rows) { if (res && res.data && res.data.rows) {
setList(res.data.rows.map(row => { setList(res.data.rows.map(row => {
const { projectProperties, id } = row; const { projectProperties, id } = row;
const imageUrl = projectProperties.authorImageUrl.split(':4000/') && projectProperties.authorImageUrl.split(':4000/')[1]
const authorImageUrl = imageUrl ? `${window.location.origin}/${imageUrl}` : projectProperties.authorImageUrl
return { return {
id: id, id: id,
name: projectProperties.fromOwner === "AI-RESOURCE" ? projectProperties.name.split("/").pop() : projectProperties.name, name: projectProperties.fromOwner === "AI-RESOURCE" ? projectProperties.name.split("/").pop() : projectProperties.name,
image: projectProperties.authorImageUrl, image: authorImageUrl,
isNew: false, isNew: false,
url: projectProperties.fullName, url: projectProperties.fullName,
desc: projectProperties.description, desc: projectProperties.description,
@ -79,13 +266,15 @@ function List(props) {
})) }))
setTotal(res.data.total) setTotal(res.data.total)
} }
}).finally(() => {
setLoading(false);
}) })
break; break;
default: default:
getAISourceList(aiZoneId, { getAISourceList(aiZoneId, {
domainId: activeCategory, domainId: activeCategory,
pageNum: page, pageNum: page,
pageSize: 10, pageSize: limit,
name: search, name: search,
resourceCategory: aiResouceTypeKey[type] resourceCategory: aiResouceTypeKey[type]
}).then(res => { }).then(res => {
@ -105,6 +294,8 @@ function List(props) {
})) }))
setTotal(res.data.total) setTotal(res.data.total)
} }
}).finally(() => {
setLoading(false);
}) })
break; break;
} }
@ -147,9 +338,10 @@ function List(props) {
面向开发者提供可信合规高效的 AI 资产分发中心涵盖百亿级模型权重库千万量级专业数据集托管代码仓及<br />智能skill中心实现从资源检索安全审计到业务集成的一体化研发生态 面向开发者提供可信合规高效的 AI 资产分发中心涵盖百亿级模型权重库千万量级专业数据集托管代码仓及<br />智能skill中心实现从资源检索安全审计到业务集成的一体化研发生态
</div> </div>
<div className="ai_home_banner_tabs"> <div className="ai_home_banner_tabs">
{aiResouceType.map((item) => ( {aiTypeInList.map((item) => (
<Link <Link
to={`/ai/${item.key}`} to={`/ai/${item.key}`}
onClick={()=>{setActiveCategory(undefined); setList([])}}
key={item.key} key={item.key}
className={`ai_home_banner_tab font-15 pointer ${type === item.key ? 'active' : ''}`} className={`ai_home_banner_tab font-15 pointer ${type === item.key ? 'active' : ''}`}
> >
@ -174,7 +366,7 @@ function List(props) {
className={`ai_home_sidebar_item mb30 ${!activeCategory ? 'active' : ''}`} className={`ai_home_sidebar_item mb30 ${!activeCategory ? 'active' : ''}`}
onClick={() => setActiveCategory(undefined)} onClick={() => setActiveCategory(undefined)}
> >
<i className={`${!activeCategory ? "iconfont icon-remenmoxing-2" : "iconfont-color icon-colorremenmoxing-1"} font-14 mr10`}></i> {type !== "agents" && <i className={`${!activeCategory ? "iconfont icon-remenmoxing-2" : "iconfont-color icon-colorremenmoxing-1"} font-14 mr10`}></i>}
全部 全部
</li> </li>
{cate.map((category, index) => { {cate.map((category, index) => {
@ -184,7 +376,7 @@ function List(props) {
className={`ai_home_sidebar_item mb30 ${activeCategory === category.id ? 'active' : ''}`} className={`ai_home_sidebar_item mb30 ${activeCategory === category.id ? 'active' : ''}`}
onClick={() => setActiveCategory(category.id)} onClick={() => setActiveCategory(category.id)}
> >
<i className={`${icons[Number(activeCategory === category.id)]} font-14 mr10`}></i> {type !== "agents" && <i className={`${icons[Number(activeCategory === category.id)]} font-14 mr10`}></i>}
{category.name} {category.name}
</li> </li>
})} })}
@ -196,54 +388,65 @@ function List(props) {
{/* 搜索和操作栏 */} {/* 搜索和操作栏 */}
<div className="ai_home_toolbar"> <div className="ai_home_toolbar">
<Input.Search className="ai_home_search flex1" placeholder={`输入关键词搜索您想要的${typeDetail.name}(共${total}个)`} enterButton={<i className="iconfont icon-sousuo5 font-15"></i>} onSearch={(value) => { setPage(1); setSearch(value) }} /> <Input.Search className="ai_home_search flex1" placeholder={`输入关键词搜索您想要的${typeDetail.name}(共${total}个)`} enterButton={<i className="iconfont icon-sousuo5 font-15"></i>} onSearch={(value) => { setPage(1); setSearch(value) }} />
{current_user && current_user.login && <div className=""> {current_user && current_user.login && type !== "agents" && <div className="">
<a className="btn-outline font-bd mr10" href={`/ai/${type}/create`}> 新建 </a> <a className="btn-outline font-bd mr10" href={`/ai/${type}/create`}> 新建 </a>
<a className="btn-outline font-bd ai_home_action_my" href={`/ai/${type}/my`}>我的{typeDetail.name}<i className="iconfont icon-arrright ml5"></i></a> <a className="btn-outline font-bd ai_home_action_my" href={`/ai/${type}/my`}>我的{typeDetail.name}<i className="iconfont icon-arrright ml5"></i></a>
</div>} </div>}
</div> </div>
{/* 模型列表 */} {/* 模型列表 */}
{list && !!list.length ? <div className={`ai_home_list ${type}_pattern`}> <Spin spinning={loading}>
{list.map((model) => ( {list && !!list.length ? <div className={`ai_home_list ${type}_pattern`}>
<ActionItem key={model.id} className="ai_home_card" padding="1px" borderRadius="12px"> {list.map((model) => (
<div> <ActionItem key={`${type}-${model.id}`} className="ai_home_card" padding="1px" borderRadius="12px">
<div className="df-center"> {type === "agents" ? <div>
{model.image && <img src={model.image} width={38} className="mr15" style={{ borderRadius: '8px' }} />} <div className="df">
{model.icon && type !== "skills" && <i className={`iconfont ${model.icon} font-30 mr15`}></i>} <img src={model.image ? model.image : require(`../image/${model.id}.png`).default} width={46} className="mr15" style={{ borderRadius: '8px' }} />
<div className="flex1 task-hide mr10" style={{ minHeight: '40px' }}> <span className="font-18 font-bd task-hide">{model.name}</span>
<div className="font-16 font-bd">{model.name}{model.isNew && <i className="iconfont-color icon-colora-zuixin1 font-20 ml10"></i>}</div>
{model.desc && <div className="color3f4">{model.desc}</div>}
</div> </div>
<a className="btn-black arrowRight" href={type === "codes" ? `/${model.url}` : `/ai/${type}/${model.id}`}>查看<i className="iconfont icon-arrright"></i></a> <div className="color3f4 mt15 task-hide-2" style={{minHeight: 56}}>{model.desc}</div>
</div> <div className="btn-Box">
{type === "skills" ? <div className="df-center skills_bottom color828"> {httpUrl ? <a className="btn-black" href={`/ai/${type}/${model.id}`} onClick={(e) => { if (!current_user || !current_user.login) { e.preventDefault(); e.stopPropagation(); showLoginDialog(); } }}>立即使用</a> : <a className="unClickBtn">暂未开放</a>}
<img src={icon6} width={18} /> </div>
<span className="mrat ml10 color091 font-13">{model.domainName}</span> </div>:<div>
{aiCard(model)} <div className="df-center">
</div> : <Fragment> {model.image && <img src={model.image} width={38} className="mr15" style={{ borderRadius: '8px' }} />}
{type === "codes" ? <div className="mt15"></div> : <Divider dashed />} {model.icon && type !== "skills" && <i className={`iconfont ${model.icon} font-30 mr15`}></i>}
<div className="df-center color828"> <div className="flex1 task-hide mr10" style={{minHeight: '40px'}}>
<div className="font-16 font-bd">{model.name}{model.isNew && <i className="iconfont-color icon-colora-zuixin1 font-20 ml10"></i>}</div>
{model.desc && <div className="color3f4">{model.desc}</div>}
</div>
<a className="btn-black arrowRight" href={type === "codes" ? `/${model.url}` : `/ai/${type}/${model.id}`}>查看<i className="iconfont icon-arrright"></i></a>
</div>
{type === "skills" ? <div className="df-center skills_bottom color828">
<img src={icon6} width={18} />
<span className="mrat ml10 color091 font-13">{model.domainName}</span>
{aiCard(model)} {aiCard(model)}
{type === "codes" ? <div className="df-center mlat"> </div> : <Fragment>
{model.language && !!model.language.length && <Fragment> {type === "codes" ? <div className="mt15"></div> : <Divider dashed />}
<img src={icon5} className="mr8 mt1" width={18} /> <div className="df-center color828">
{model.language.map((topic, index) => { {aiCard(model)}
return <Fragment> {type === "codes" ? <div className="df-center mlat">
<span className="color091">{topic}</span> {model.language && !!model.language.length && <Fragment>
{index < model.language.length - 1 && <Divider type="vertical" style={{ height: '7px' }} className="mt5" />} <img src={icon5} className="mr8 mt1" width={18} />
</Fragment> {model.language.map((topic, index) => {
})} return <Fragment>
</Fragment>} <span className="color091">{topic}</span>
</div> : <span className="ai_home_card_tag mlat">{model.domainName}</span>} {index < model.language.length - 1 && <Divider type="vertical" style={{ height: '7px' }} className="mt5" />}
</div> </Fragment>
</Fragment>} })}
</div> </Fragment>}
</ActionItem> </div> : <span className="ai_home_card_tag mlat">{model.domainName}</span>}
))} </div>
</div> : <div className="pt80"><Nodata _html="暂无数据"/></div>} </Fragment>}
</div>}
</ActionItem>
))}
</div> : <div className="pt80"><Nodata _html="暂无数据"/></div>}
</Spin>
{/* 分页 */} {/* 分页 */}
<Pagination className="edu-txt-right ai_list_Pagination mb70 black_active_Pagination" current={page} onChange={(p) => { setPage(p) }} pageSize={10} total={total} showQuickJumper hideOnSinglePage /> <Pagination className="edu-txt-right ai_list_Pagination mb70 black_active_Pagination" current={page} onChange={(p) => { setPage(p) }} pageSize={limit} total={total} showQuickJumper hideOnSinglePage />
</div> </div>
</div> </div>
</div> </div>

View File

@ -222,6 +222,42 @@
} }
} }
.agents_pattern{
grid-template-columns: repeat(3, 1fr);
.ai_home_card{
position: relative;
.action-item_content{
padding: 27px 36px;
background-image: url('../image/card_bk3.png');
background-size: 100% 100%;
}
&:hover{
.btn-Box{
display: block;
position: absolute;
left: 0;
bottom: 17px;
text-align: center;
width: 100%;
}
}
.btn-Box{
display: none;
}
.btn-black{
padding: 4px 48px 4px 48px;
}
.unClickBtn{
padding: 8px 47px 8px 47px;
background:#9ea2a8;
border-radius:10px;
color:#ffffff;
font-size:15px;
cursor: not-allowed;
}
}
}
// 卡片 // 卡片
.ai_home_card { .ai_home_card {

View File

@ -122,6 +122,7 @@ class NewHeader extends Component {
//退出账号时清除登录页面的下次自动登录(用户再次打开登录页面时下次自动登录框不勾选) //退出账号时清除登录页面的下次自动登录(用户再次打开登录页面时下次自动登录框不勾选)
cookie.remove("autologin"); cookie.remove("autologin");
cookie.remove("login"); cookie.remove("login");
cookie.remove("agent");
//退出账号 //退出账号
var url = `/accounts/logout.json`; var url = `/accounts/logout.json`;
axios.get((url)).then((result) => { axios.get((url)).then((result) => {
@ -349,7 +350,7 @@ class NewHeader extends Component {
}, },
{ {
"name": "开源软件仓库", "name": "开源软件仓库",
"link": "/explore", "link": "/ossRepo",
"index": "2", "index": "2",
"hidden": false "hidden": false
}, },

View File

@ -72,7 +72,7 @@ class Index extends Component {
></Route> ></Route>
<Route <Route
path="/explore" path="/ossRepo"
render={(props) => ( render={(props) => (
<ProjectHome {...this.props} {...props} /> <ProjectHome {...this.props} {...props} />
)} )}

View File

@ -650,4 +650,13 @@ export function getAIProjectStatistics(params){
method: 'get', method: 'get',
params params
}) })
}
/**AI智能体登录***/
export function aiAgentLogin(data) {
return fetch({
url: `/auth/aiAgent/login`,
method: 'POST',
data: data
})
} }

View File

@ -12,7 +12,6 @@ import { Base64 } from 'js-base64';
import { setProject, setProjectDetail, setProjectEntries, setProjectMenuList, setProjectReadMe } from '../../redux/actions/server'; import { setProject, setProjectDetail, setProjectEntries, setProjectMenuList, setProjectReadMe } from '../../redux/actions/server';
import actions from '../../redux/actions'; import actions from '../../redux/actions';
import types from '../../redux/actions/actionTypes' import types from '../../redux/actions/actionTypes'
import { getProjectFunc, getProjectDetailFunc } from '../../services/project'
import '../css/index.scss' import '../css/index.scss'
import './list.scss'; import './list.scss';
import '../../modules/courses/css/Courses.css' import '../../modules/courses/css/Courses.css'
@ -252,7 +251,7 @@ class Detail extends Component {
let propsParam = this.props.match.params; let propsParam = this.props.match.params;
if (prevState && this.props && (prevParam.projectsId !== propsParam.projectsId || prevParam.owner !== propsParam.owner)) { if (prevState && this.props && (prevParam.projectsId !== propsParam.projectsId || prevParam.owner !== propsParam.owner)) {
const { projectsId, owner } = this.props.match.params; const { projectsId, owner } = this.props.match.params;
const data = await getProjectFunc(owner, projectsId); const data = await this.getProjectFunc(owner, projectsId);
if (data.data) { if (data.data) {
this.getProject(data.data) this.getProject(data.data)
} }
@ -276,7 +275,7 @@ class Detail extends Component {
async componentWillMount() { async componentWillMount() {
const { projectsId, owner } = this.props.match.params; const { projectsId, owner } = this.props.match.params;
if (!this.props.projectBase) { if (!this.props.projectBase) {
const data = await getProjectFunc(owner, projectsId); const data = await this.getProjectFunc(owner, projectsId);
if (data && data.data) { if (data && data.data) {
this.getProject(data.data) this.getProject(data.data)
} }
@ -314,17 +313,11 @@ class Detail extends Component {
mirror_status:data.mirror_status || 0 mirror_status:data.mirror_status || 0
}) })
this.getBanner(this.props.projectMenu) this.getBanner(this.props.projectMenu)
let projectdata
if (this.props.defaultDetail) { if (this.props.defaultDetail) {
projectdata = this.props.defaultDetail projectdata = this.props.defaultDetail
} else {
const res = await getProjectDetailFunc(owner, projectsId, !window.location.host);
if (res && res.status === 200) {
projectdata = res.data
}
}
if (projectdata) {
this.getDetail(projectdata) this.getDetail(projectdata)
} else {
this.getDetail();
} }
} }
} }
@ -468,6 +461,11 @@ class Detail extends Component {
} }
} }
getProjectFunc = async (owner, projectsId)=>{
const url = `/${owner}/${projectsId}/simple.json`;
return axios.get(url);
}
// 获取动态导航栏菜单 // 获取动态导航栏菜单
async getBanner(data) { async getBanner(data) {
const { projectsId, owner } = this.props.match.params; const { projectsId, owner } = this.props.match.params;

View File

@ -0,0 +1,31 @@
import React, { useEffect, useState } from "react";
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
import loadingGIf from "./Images/loading.gif";
function OssRepo(props) {
const {current_user, mygetHelmetapi} = props;
const {admin, user_id} = current_user || {}
const {common} = mygetHelmetapi || {};
const {ossRepo="http://27.122.114.160:3001"} = common || {};
const [loading, setLoading] = useState(true);
useEffect(()=>{
document.title = '开源软件仓库';
// document.body.style.background = '#fff';
return () => {
document.body.style.background = '';
}
}, [])
const url = `${ossRepo}/?uid=${user_id === 2 ? undefined : user_id}`
// const url = "http://localhost:3007/"
return <div className="" style={{border: "none"}}>
{loading && <div className="center" style={{minHeight: '60vh', marginTop: '30vh'}}>
<img src={loadingGIf} width={250}/>
<div className="font-16" style={{position: 'relative', top: '-40px', color: '#091221', opacity: '0.6'}}>加载中请耐心等待</div>
</div>}
<iframe title={`ossRepoIframe`} src={url} id={`ossRepoIframe`} frameBorder="0" name={`ossRepoIframe`} width="100%" style={{height: '100vh', border: 'none'}} onLoad={()=>{setLoading(false)}}></iframe>
</div>
}
export default TPMIndexHOC(OssRepo);

View File

@ -124,7 +124,8 @@ function ProjectHomePage(props) {
page: page, page: page,
topic_id: topicId, topic_id: topicId,
sort_by: cateID ? undefined : "praises_count", sort_by: cateID ? undefined : "praises_count",
language_id: undefined language_id: undefined,
exclude_forked: true
}; };
if (cateID === 39 && topicId) { if (cateID === 39 && topicId) {
// topicIdlanguage_idmavenpythonnpm // topicIdlanguage_idmavenpythonnpm

View File

@ -123,4 +123,11 @@ export function deleteUserForums(id,is_apply) {
is_apply is_apply
} }
}); });
} }
export async function getForumHead(id) {
return fetch({
url: `/api/memos/forum_memos_head/${id}.json`,
method: 'get',
});
}

View File

@ -154,8 +154,6 @@ function detail(props) {
} }
}) })
}}>{memo.is_fine ? "取消推荐" : "推荐"}</a> }}>{memo.is_fine ? "取消推荐" : "推荐"}</a>
<a onClick={()=>{setVisible(1)}}>删除</a>
</Fragment>} </Fragment>}
{(memo.is_current_user || banned_permission) && <Fragment> {(memo.is_current_user || banned_permission) && <Fragment>
@ -167,6 +165,8 @@ function detail(props) {
setVisible(apply_destroy ? 3 : 2) setVisible(apply_destroy ? 3 : 2)
}}>{apply_destroy ? "撤销申请" : "申请删帖"}</a> }}>{apply_destroy ? "撤销申请" : "申请删帖"}</a>
</Fragment>} </Fragment>}
{banned_permission && <a onClick={()=>{setVisible(1)}}>删除</a>}
</div> </div>
} }
> >

View File

@ -5,6 +5,7 @@ import { Divider, Form, Input, Cascader, Select, Breadcrumb, Upload, Button, Ico
import RichEditor from '../../forge/Component/RichEditor'; import RichEditor from '../../forge/Component/RichEditor';
import UploadImage from '../../forge/Team/Component/UploadImage'; import UploadImage from '../../forge/Team/Component/UploadImage';
import { httpUrl } from "../fetch"; import { httpUrl } from "../fetch";
import cookie from 'react-cookies';
const { Option } = Select; const { Option } = Select;
function EditForum(props) { function EditForum(props) {
@ -198,7 +199,7 @@ function EditForum(props) {
</Form.Item> </Form.Item>
<Form.Item label="上传附件"> <Form.Item label="上传附件">
{getFieldDecorator('attachments')( {getFieldDecorator('attachments')(
<Upload.Dragger action={`${httpUrl}/api/attachments.json`} beforeUpload={beforeUpload} onChange={changeFileList} fileList={fileList}> <Upload.Dragger action={`${httpUrl}/api/attachments.json`} beforeUpload={beforeUpload} onChange={changeFileList} fileList={fileList} withCredentials={true} headers={{ Authorization: cookie.load("autologin_trustie") }}>
<p className="ant-upload-drag-icon"> <p className="ant-upload-drag-icon">
<Icon type="inbox" /> <Icon type="inbox" />
</p> </p>
@ -210,7 +211,7 @@ function EditForum(props) {
<Button <Button
className="mr20" className="mr20"
onClick={() => { onClick={() => {
history.push("/forums"); history.go(-1);
}} }}
> >
返回 返回

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 204 KiB

BIN
src/forums/image/bk3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
src/forums/image/bk4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 KiB

BIN
src/forums/image/icon17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/forums/image/icon18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

BIN
src/forums/image/icon19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 707 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -15,6 +15,10 @@ const List = Loadable({
loader : () => import("./list"), loader : () => import("./list"),
loading: Loading, loading: Loading,
}) })
const ListById = Loadable({
loader : () => import("./list/listById"),
loading: Loading,
})
const Editor = Loadable({ const Editor = Loadable({
loader : () => import("./edit"), loader : () => import("./edit"),
loading: Loading, loading: Loading,
@ -29,6 +33,12 @@ function SoftBot(props){
<Editor {...props} {...p} /> <Editor {...props} {...p} />
)} )}
></Route> ></Route>
<Route
path="/forums/theme/:id"
render={(p) => (
<ListById {...props} {...p} />
)}
></Route>
<Route <Route
path="/forums/:id/edit" path="/forums/:id/edit"
render={(p) => ( render={(p) => (

View File

@ -0,0 +1,67 @@
import React from "react";
import { Link } from "react-router-dom";
import { Divider, Input, Pagination, Tag } from "antd";
import Nodata from "../../forge/Nodata";
import icon5 from '../image/icon5.png';
import icon6 from '../image/icon6.png';
import icon10 from '../image/icon10.png';
import icon17 from '../image/icon17.png';
import './index.scss';
function ForumList({title="最新帖子", forumList, LIMIT, page, setPage, total, onSearch, onSort, sortBy}){
const sortImg1 = {
0: require('../image/icon7.png'),
1: require('../image/icon8.png'),
2: require('../image/icon9.png'),
}
return <div className="flex1 pb30">
<div className="df forums_header">
<img src={icon17} width={24}/>
<span className="font-22 font-bd ml8 mr30">{title}</span>
<Input.Search placeholder="输入名称关键字" className="forums_search" allowClear onSearch={(value)=>{onSearch(value)}}/>
<a onClick={()=>{onSort("published_at")}} className={`${sortBy === "published_at" ? 'active' : ''}`}>最新</a>
<Divider type="vertical" />
<a onClick={()=>{onSort("replies_count")}} className={`${sortBy === "replies_count" ? 'active' : ''}`}>最热</a>
</div>
<div className="forums-list4-box pt5">
{forumList.map((item, index) => {
const orderIndex = index+LIMIT*(page-1);
const imageUrl = item.image_url.split(':4000/') && item.image_url.split(':4000/')[1]
const authorImageUrl = imageUrl ? `${window.location.origin}/${imageUrl}` : item.image_url
return <div className="forums-list4-item">
<div className="pr80 AlignCenter">
{orderIndex < 3 ? <img src={sortImg1[orderIndex]} width={24} className="mr10"/> : <span className="font-bd font-20 mr10">{orderIndex + 1}</span>}
<Link to={`/forums/${item.id}`} className="forums-list4-item-link task-hide font-22 mr10">
{item.subject}
</Link>
{item.sticky && <Tag color="#d06d3c">置顶</Tag>}
{item.is_original && <Tag color="#7e41f1">原创</Tag>}
{item.is_fine && <Tag color="#2E89FF">精华</Tag>}
<i className="iconfont icon-jiantou21 font-16"></i>
</div>
<div className="opacity8 mt15 forums-list4-item-bottom">
<img src={authorImageUrl} alt="" className="user-img" />
{item.username}
<span className="ml10">{item.published_time}</span>
<Divider type="vertical" />
<img src={icon5} width={14} className="mb3 mr5" />
{item.praises_count}
<img src={icon6} width={14} className="mb3 mr5 ml10" />
{item.replies_count}
</div>
{/* <span className="viewed_count">
<img src={icon10} width={11} className="mb3 mr5 viewed_count-icon" />
{item.viewed_count}
</span> */}
</div>
})}
{forumList.length === 0 && (
<Nodata _html="暂无数据" />
)}
<Pagination pageSize={LIMIT} total={total} hideOnSinglePage={true} showQuickJumper current={page} onChange={(page) => { setPage(page) }} className='edu-txt-right mt30 black_active_Pagination pr25' />
</div>
</div>
}
export default ForumList;

View File

@ -1,253 +1,228 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import './index.scss'; import './index.scss';
import { Button, Carousel, Divider, Pagination, Tag } from "antd"; import { getForumSections, getForumsList } from "../api";
import { getForumsById, getForumSections, getForumsList } from "../api";
import { httpUrl } from "../fetch";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import icon5 from '../image/icon5.png';
import icon6 from '../image/icon6.png';
import icon10 from '../image/icon10.png';
import img1 from '../image/img1.png';
import img2 from '../image/img2.png';
import img3 from '../image/img3.png';
import img4 from '../image/img4.png';
import Nodata from "../../forge/Nodata"; import Nodata from "../../forge/Nodata";
import { forumSectionId } from "../static"; import { forumSectionId } from "../static";
import { rollingButton } from "../../components/button"; import { rollingButton, slidingArrowButton } from "../../components/button";
import { HoverUnderline } from "../../components/title"; import ForumList from "./forumList";
function List(props) { function List(props) {
const {mygetHelmetapi, location, current_user} = props; const {mygetHelmetapi, current_user, showLoginDialog} = props;
const {common} = mygetHelmetapi || {}; const {common} = mygetHelmetapi || {};
const {forums: forumsDoMain} = common || {}; const {forums: forumsDoMain} = common || {};
const tagNameByState = location && location.state && location.state.name;
const [forumsList, setForumsList] = useState([]); const [forumsList, setForumsList] = useState([]);
const [forumsImgList, setForumsImgList] = useState([]);
const [hottestForumsList, setHottestForumsList] = useState([]); const [hottestForumsList, setHottestForumsList] = useState([]);
// //
const [forumTag, setForumTag] = useState([]); const [forumTag, setForumTag] = useState([]);
//
const [forumTagDetail, setForumTagDetail] = useState(undefined);
// //
const LIMIT = 10; const LIMIT = 10;
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [forumListByTag, setForumListByTag] = useState([]); const [recommendList, setRecommendList] = useState([]);
// active // active
const [activeId, setActiveId] = useState(0); const [activeId, setActiveId] = useState(null);
//
const [isInitialized, setIsInitialized] = useState(false);
const [search, setSearch] = useState()
const [sort, setSort] = useState("published_at")
//
const autoPlayTimerRef = React.useRef(null);
//
const isHoveringRef = React.useRef(false);
// id
const hoveringIdRef = React.useRef(null);
// forumTag
const forumTagRef = React.useRef([]);
const sortImg1 = {
0: require('../image/icon7.png'),
1: require('../image/icon8.png'),
2: require('../image/icon9.png'),
}
const sortImg2 = { const sortImg2 = {
0: require('../image/icon2.png'), 0: require('../image/icon2.png'),
1: require('../image/icon3.png'), 1: require('../image/icon3.png'),
2: require('../image/icon4.png'), 2: require('../image/icon4.png'),
} }
const forumsImg = {
0: img1,
1: img2,
2: img3,
3: img4
}
function init(updatedTags){ //
const tagByState = updatedTags.filter(i=>i.title.includes(tagNameByState)) const startAutoPlay = () => {
setForumTagDetail(tagByState && !!tagByState.length ? tagByState[0] : updatedTags[0]); stopAutoPlay();
if (tagByState && !!tagByState.length) { autoPlayTimerRef.current = setInterval(() => {
requestAnimationFrame(() => { //
requestAnimationFrame(() => { if (isHoveringRef.current) return;
const el = document.getElementById('forums-list3-id')
if (el) { // 使forumTagRefforumTag
el.scrollIntoView({ behavior: 'smooth' }) const currentTags = forumTagRef.current;
} if (!currentTags || currentTags.length === 0) return;
})
}) setActiveId(prevId => {
const currentIndex = currentTags.findIndex(t => t.id === prevId);
const nextIndex = (currentIndex + 1) % currentTags.length;
const nextTag = currentTags[nextIndex];
if (nextTag) {
return nextTag.id;
}
return prevId;
});
}, 3000); // 3
};
//
const stopAutoPlay = () => {
if (autoPlayTimerRef.current) {
clearInterval(autoPlayTimerRef.current);
autoPlayTimerRef.current = null;
} }
} };
//
const handleMouseEnter = (tag) => {
isHoveringRef.current = true;
hoveringIdRef.current = tag.id;
stopAutoPlay();
setActiveId(tag.id);
};
//
const handleMouseLeave = () => {
isHoveringRef.current = false;
hoveringIdRef.current = null;
startAutoPlay();
};
useEffect(() => { useEffect(() => {
document.title = '论坛交流'; document.title = '论坛交流';
getForumsList({
page: 1,
limit: 6,
sort: 'published_at',
homepage_show: true
}).then(res => {
if (res) {
setForumsList(res.memos || []);
const imageList = res.memos.filter(item=>!!item.memo_image_info)
setForumsImgList(imageList)
setHottestForumsList(res.hottest_memos || []);
}
})
getForumSections(forumSectionId).then(res => { getForumSections(forumSectionId).then(res => {
if (res && res.recommend_forum_sections) { if (res && res.recommend_forum_sections) {
// //
const childrenTags = res.recommend_forum_sections; const childrenTags = res.recommend_forum_sections;
setForumTag(childrenTags); setForumTag(childrenTags);
init(childrenTags) forumTagRef.current = childrenTags;
const firstTag = childrenTags && !!childrenTags.length ? childrenTags[0] : childrenTags[0];
// if (firstTag) {
// const fetchEssencePosts = async (tag) => { setActiveId(firstTag.id);
// try { }
// const essenceRes = await getForumsById(tag.id, { setIsInitialized(true);
// page: 1,
// limit: 1,
// sort: 'published_at',
// select_type: 'is_fine',
// });
// return {
// ...tag,
// essencePosts: essenceRes?.memos || []
// };
// } catch (err) {
// return {
// ...tag,
// essencePosts: []
// };
// }
// };
// // 使 Promise.all
// Promise.all(childrenTags.map(fetchEssencePosts))
// .then(updatedTags => {
// setForumTag(updatedTags);
// if (updatedTags.length > 0) {
// init(updatedTags)
// }
// })
// .catch(error => {
// //
// setForumTag(childrenTags);
// if (childrenTags.length > 0) {
// init(childrenTags)
// }
// });
} }
}) })
}, []) }, [])
// forumTag
useEffect(() => { useEffect(() => {
if (forumTagDetail && forumTagDetail.id) { if (forumTag.length > 0 && isInitialized) {
// const forums = forumTagDetail.essencePosts[0] || {} forumTagRef.current = forumTag;
getForumsById(forumTagDetail.id, { startAutoPlay();
page: page,
limit: LIMIT,
sort: 'published_at',
select_type: 'all',
// hide_id: forums.id
}).then(res => {
if (res) {
setForumListByTag(res.memos || []);
setTotal(res.memos_count);
}
})
} }
}, [forumTagDetail && forumTagDetail.id, page])
return () => {
stopAutoPlay();
};
}, [forumTag.length, isInitialized])
useEffect(()=>{
getForumsList({
page: page,
limit: LIMIT,
sort: sort,
search: search,
}).then(res => {
if (res) {
setTotal(res.memos_count);
setForumsList(res.memos || []);
setHottestForumsList(res.hottest_memos || []);
setRecommendList(res.recommend_memos || []);
}
})
}, [page, sort, search])
return ( return (
<div className="forums-list-box"> <div className="forums-list-box">
<div className="forums-banner-box"> <div className="forums-banner-box">
<div className="width1600 center"> <div className="width1600 center">
<div className="font-60 font-bd forums-banner-title1 mb10"><span>论坛交流·</span><span className="carousel-text">开始思想的</span><span className="carousel-text">联接与互动</span></div> <div className="font-60 font-bd forums-banner-title1 mb10">
<div className="font-18 mb35 forums-banner-title2">需求反馈 技术交流 创意分享</div> <span>论坛交流·</span>
{forumsDoMain && rollingButton({href: current_user && current_user.login ? `/forums/add` : `/login?go_page=/forums/add`, name: '发布帖子', hoverName: '发布帖子', classname: "add-forums"})} <span className="carousel-text">解锁头脑风暴</span>
</div> <span className="eyes">
<div className="width1600 df pt50 forums-hot-list"> <svg viewBox="0 0 50 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-full h-full overflow-visible">
{forumsImgList && !!forumsImgList.length && <Carousel autoplay className='forums-list-left-Carousel' beforeChange={(_, nextId)=>{setActiveId(nextId)}}> <rect class="animate-eye-left-white" width="18.67" height="23.44" rx="10.72" fill="#ffffff00" stroke="#000" stroke-width="1.5"></rect>
{forumsImgList.map((item, index)=>{ <rect class="animate-eye-right-white" width="18.67" height="23.44" rx="10.72" fill="#ffffff00" stroke="#000" stroke-width="1.5"></rect>
return <img src={httpUrl + item.memo_image_info.url} alt=''key={index}/> <circle class="animate-eye-right-pupil" r="5.395" fill="#000"></circle>
})} <circle class="animate-eye-left-pupil" r="5.49" fill="#000"></circle>
</Carousel>} </svg>
<div className="forums-list1"> </span>
{forumsList.map((item, index) => (
<div className={`font-18 forums-list-item ${activeId === index ? 'forums-list-item-active' : ''}`} key={index}>
<span className="forums-subject AlignCenter">
<Link className="task-hide" to={`/forums/${item.id}`}>{item.subject}</Link>
{index === 0 && <Tag color="#df2659" className="mr0">NEW</Tag>}
{item.is_original && <Tag color="#7e41f1">原创</Tag>}
</span>
</div>
))}
</div>
{/* 热门回答 */}
<div className="hot-forums-list flex1 ml20">
<div className="font-22 hot-forums-list-header">
<img src={require('../image/icon1.png')} alt="" width={24} className="mr8 mb3"/>
热门回答
</div>
<div className="forums-list2">
{hottestForumsList.slice(0,5).map((item, index) => (
<div className="font-18 forums-list-item" key={index}>
<span className="forums-subject AlignCenter">
{index < 3 ? <img src={sortImg2[index]} width={22} className="mr5"/> : <span className="ml5 mr10">{index + 1}</span>}
<Link className="task-hide" to={`/forums/${item.id}`}>{item.subject}</Link>
</span>
</div>
))}
</div>
</div> </div>
<div className="font-18 mb35 forums-banner-title2">开源动态社区前言新闻速递开启开源新视野</div>
{forumsDoMain && rollingButton({href: current_user && current_user.login ? `/forums/add` : `/login?go_page=/forums/add`, name: '发布帖子', hoverName: '发布帖子', classname: "add-forums font-18"})}
</div> </div>
</div> <div className="width1600 df pt30 forums-hot-list">
<div className="forums-list3-box" id="forums-list3-box">
<div className="font-40 font-bd center mb40">精华论坛</div>
<div className="width1600 pb100" id="forums-list3-id">
{/* 四个板块 */} {/* 四个板块 */}
<div className="team highlight-list teamShow"> <div className="team highlight-list teamShow">
{forumTag.map((item, index) => { {forumTag.map((item, index) => {
const isSelected = forumTagDetail && forumTagDetail.id === item.id; const isSelected = activeId === item.id;
const forums = item.essencePosts && item.essencePosts[0] || {};
return ( return (
<li className={`highlight-card ${isSelected ? "hovered":""}`} onClick={()=>{setForumTagDetail(item); setPage(1)}}> <li
<img src={forumsImg[index]} alt="" className="mb30" width={37}/> className={`highlight-card ${isSelected ? "hovered":""}`}
<div className='t-title'><span dangerouslySetInnerHTML={{ __html: item.title }}></span></div> onMouseEnter={() => handleMouseEnter(item)}
<div className="t-desc" dangerouslySetInnerHTML={{ __html: item.description }}></div> onMouseLeave={handleMouseLeave}
style={{ cursor: 'pointer' }}
>
<a href={`/forums/theme/${item.id}`}>
<div className='t-title'><span dangerouslySetInnerHTML={{ __html: item.title }}></span></div>
<div className="highlight-card-bottom">
查看更多
<i className="iconfont icon-arrright font-24 line-normal"></i>
</div>
</a>
</li> </li>
) )
})} })}
</div> </div>
{/* 板块对应的帖子列表 */} </div>
<div className="forums-list4-box mt40"> </div>
{forumListByTag.map((item, index) => { <div className="forums-list3-box" id="forums-list3-box">
const orderIndex = index+LIMIT*(page-1) <div className="width1600 pb100 df forums-list3-content" id="forums-list3-id">
return <div className="forums-list4-item"> {/* 帖子列表 */}
<div className="pr80 AlignCenter"> <ForumList forumList={forumsList} LIMIT={LIMIT} page={page} setPage={setPage} total={total} onSearch={setSearch} onSort={setSort} sortBy={sort}/>
{orderIndex < 3 ? <img src={sortImg1[orderIndex]} width={24} className="mr10"/> : <span className="font-bd font-20 mr10">{orderIndex + 1}</span>} <div className="forums-list3-right">
<Link to={`/forums/${item.id}`} className="forums-list4-item-link task-hide"> <a className="add_forum" href={`/forums/add`} onClick={(e)=>{
<HoverUnderline classname="font-22 task-hide"> if(current_user && !current_user.login){
{item.subject} e.stopPropagation();
</HoverUnderline> e.preventDefault();
</Link> showLoginDialog("/forums/add");
{item.sticky && <Tag color="#d06d3c">置顶</Tag>} }
{item.is_original && <Tag color="#7e41f1">原创</Tag>} }}><i className="iconfont icon-a-fabu1 font-16 mr5"></i>写点什么</a>
{item.is_fine && <Tag color="#2E89FF">精华</Tag>} <div className="hot-forums-list mt20 mb20">
</div> <div className="font-22 hot-forums-list-header">
<div className="opacity8 mt15"> <img src={require('../image/icon1.png')} alt="" width={24} className="mr8 mb3"/>
<img src={item.image_url} alt="" className="user-img" /> 热门话题
{item.username}
<span className="ml10">{item.published_time}</span>
<Divider type="vertical" />
<img src={icon5} width={14} className="mb3 mr5" />
{item.praises_count}
<img src={icon6} width={14} className="mb3 mr5 ml10" />
{item.replies_count}
</div>
{/* <span className="viewed_count">
<img src={icon10} width={11} className="mb3 mr5 viewed_count-icon" />
{item.viewed_count}
</span> */}
</div> </div>
})} <div className="forums-list2">
{forumListByTag.length === 0 && ( {hottestForumsList && hottestForumsList.length > 0 ? hottestForumsList.map((item, index) => (
<Nodata _html="暂无数据" /> <div className="font-18 forums-list-item" key={index}>
)} <span className="forums-subject AlignCenter">
<Pagination pageSize={LIMIT} total={total} hideOnSinglePage={true} current={page} onChange={(page) => { setPage(page) }} className='edu-txt-right mt30' /> {index < 3 ? <img src={sortImg2[index]} width={22} className="mr5"/> : <span className="ml5 mr10">{index + 1}</span>}
<Link className="task-hide" to={`/forums/${item.id}`}>{item.subject}</Link>
</span>
</div>
)) : <Nodata _html="暂无数据" small/>}
</div>
</div>
<div className="hot-forums-list">
<div className="font-22 hot-forums-list-header">
<img src={require('../image/icon18.png')} alt="" width={24} className="mr8 mb3"/>
版主推荐
</div>
<div className="forums-list2">
{recommendList && recommendList.length > 0 ? recommendList.map((item, index) => (
<div className="font-18 forums-list-item" key={index}>
<span className="forums-subject AlignCenter">
<span className="ml5 mr10">{index + 1}</span>
<Link className="task-hide" to={`/forums/${item.id}`}>{item.subject}</Link>
</span>
</div>
)) : <Nodata _html="暂无数据" small/>}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,27 +4,32 @@
color: #091221; color: #091221;
background-color: #f1f6fd; background-color: #f1f6fd;
font-family: "alibabaReg"; font-family: "alibabaReg";
background-image: url('../image/banner.png');
background-size: 100% 100%;
.ant-tag { .ant-tag {
line-height: 16px; line-height: 16px;
padding: 0 3px; padding: 0 3px;
margin-left: 10px;
margin-right: 0px; margin-right: 0px;
&+.ant-tag { &+.ant-tag {
margin-left: 6px; margin-left: 6px;
} }
} }
.font-bd{
font-weight: normal;
font-family: "alibaba";
}
} }
.forums-banner-box { .forums-banner-box {
background-image: url('../image/banner.png'); padding: 70px 0 120px;
background-size: 100% 100%;
padding: 70px 0 145px;
.forums-banner-title1 { .forums-banner-title1 {
font-family: 'DouyinSansBold'; font-family: 'DouyinSansBold';
line-height: normal; line-height: normal;
position: relative;
&>:first-child { &>:first-child {
--duration: 200ms; --duration: 200ms;
@ -39,6 +44,7 @@
&>:nth-child(3) { &>:nth-child(3) {
--duration: 200ms; --duration: 200ms;
--delay: 600ms; --delay: 600ms;
--top: -16px
} }
&>* { &>* {
@ -46,17 +52,27 @@
--offsetTop: 56px; --offsetTop: 56px;
animation: itemUp var(--duration) ease-in-out var(--delay) forwards, bgOpacity var(--duration) linear var(--delay) reverse backwards animation: itemUp var(--duration) ease-in-out var(--delay) forwards, bgOpacity var(--duration) linear var(--delay) reverse backwards
} }
.eyes{
width: 77px;
display: inline-block;
.animate-eye-right-white {
animation: 3s cubic-bezier(.4,0,.2,1) infinite eyeRightWhite
}
.animate-eye-left-white {
animation: 3s cubic-bezier(.4,0,.2,1) infinite eyeLeftWhite
}
.animate-eye-right-pupil {
animation: 3s cubic-bezier(.4,0,.2,1) infinite eyeRightPupil
}
.animate-eye-left-pupil {
animation: 3s cubic-bezier(.4,0,.2,1) infinite eyeLeftPupil
}
}
} }
.carousel-text { .carousel-text {
background-image: linear-gradient(313.07deg, #d62ece 0%, #1664ff 100%); color: #2149d4;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
&:nth-of-type(2) {
background-image: linear-gradient(313.07deg, #1664ff 0%, #06050e 100%);
}
} }
.forums-banner-title2 { .forums-banner-title2 {
@ -69,7 +85,7 @@
width: 168px; width: 168px;
height: 60px; height: 60px;
border-radius: 30px; border-radius: 30px;
border: 4px solid #CCD3E8; border: 4px solid #E2D5DC;
line-height: 52px; line-height: 52px;
position: relative; position: relative;
--offsetTop: 26px; --offsetTop: 26px;
@ -82,30 +98,11 @@
} }
} }
.ant-carousel { }
width: 48.3%;
border-radius: 15px 0px 0px 15px;
overflow: hidden;
}
.forums-list-left-Carousel {
img {
height: 392px;
object-fit: cover;
}
}
.forums-list1 {
width: 23.4%;
background-color: #ffffff;
border-radius: 0px 15px 15px 0px;
padding: 10px 15px;
}
.hot-forums-list { .hot-forums-list {
background-color: #ffffff; background-color: #ffffff;
border-radius: 8px; border-radius: 15px;
box-shadow: 0px 0px 10px rgba(35, 44, 121, 0.07); box-shadow: 0px 0px 10px rgba(35, 44, 121, 0.07);
} }
@ -139,15 +136,14 @@
} }
} }
.forums-list-item:not(:last-of-type):not(.forums-list-item-active) { .forums-list-item:not(:last-of-type) {
.forums-subject { .forums-subject {
border-bottom: 1px dashed rgba(130, 132, 164, 0.4); border-bottom: 1px dashed rgba(130, 132, 164, 0.4);
} }
} }
.forums-list-item:hover, .forums-list-item:hover {
.forums-list-item-active {
background-image: linear-gradient(180deg, #f7f9fd00 0%, #f7f9fd 100%); background-image: linear-gradient(180deg, #f7f9fd00 0%, #f7f9fd 100%);
border-radius: 4px; border-radius: 4px;
font-weight: bold; font-weight: bold;
@ -162,13 +158,13 @@
} }
.hot-forums-list-header { .hot-forums-list-header {
padding: 12px 25px 10px 25px; padding: 12px 16px 10px 16px;
background-image: url('../image/bk2.png'); background-image: url('../image/bk2.png');
background-size: 100% 100%; background-size: 100% 100%;
} }
.forums-list2 { .forums-list2 {
padding: 0 25px 12px 25px; padding: 0 16px 12px 16px;
} }
.forums-hot-list { .forums-hot-list {
@ -176,17 +172,49 @@
--offsetTop: 56px; --offsetTop: 56px;
animation: itemUp .4s ease-in-out 1.36s forwards, bgOpacity .4s linear 1.36s reverse backwards; animation: itemUp .4s ease-in-out 1.36s forwards, bgOpacity .4s linear 1.36s reverse backwards;
} }
}
.forums-list3-box { .forums-list3-box {
padding: 60px 0 60px;
background-image: linear-gradient(179.88deg, #ffffff 0%, rgba(255, 255, 255, 0.62) 16.1%, rgba(255, 255, 255, 0) 100%);
border-radius: 40px;
position: relative; position: relative;
--offsetTop: 116px; --offsetTop: 116px;
--top: -60px; --top: -60px;
animation: itemUp .4s ease-in-out 1.56s forwards, bgOpacity .4s linear 1.56s reverse backwards; animation: itemUp .4s ease-in-out 1.56s forwards, bgOpacity .4s linear 1.56s reverse backwards;
.forums-list3-content {
gap: 20px;
&>div.flex1{
background-color: white;
border-radius: 0px 0px 15px 15px;
}
}
.forums-list3-right{
width: 26.5%;
flex-shrink: 0;
}
}
a.add_forum, a.add_forum:link{
display: block;
height:48px;
line-height: 48px;
background:#091221;
border-radius:15px;
color:#ffffff;
font-size:18px;
text-align: center;
&:hover{
opacity: 0.8;
}
}
.forums-list4-box {
background-color: #ffffff;
border-radius: 0 0 15px 15px;
.user-img { .user-img {
width: 24px; width: 24px;
height: 24px; height: 24px;
@ -195,124 +223,38 @@
margin-right: 10px; margin-right: 10px;
} }
.forums-section-box { .forums-list4-item {
gap: 20px; padding: 25px 30px 0 25px;
}
.forums-section-item-box {
border-radius: 15px;
border: 2px solid transparent;
box-shadow: 0px 3px 6px rgba(22, 100, 255, 0.02);
position: relative; position: relative;
&.active { &:hover{
border-color: #2149d4; background:#f7f9fd;
border-radius:10px;
&::before { .forums-list4-item-link{
width: 106%; font-family: "alibaba";
height: 110%;
content: '';
position: absolute;
background-color: rgb(33 72 212 / 20%);
top: -5%;
left: -3%;
border-radius: 15px;
} }
&::after { .ant-tag {
content: ""; display: none;
position: absolute;
top: 105%;
left: 50%;
transform: translateX(-50%);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid rgb(33 72 212 / 20%);
}
}
&:nth-of-type(3) .forums-section-item {
background-image: url('../image/forums3.png');
background-size: 100% 100%;
}
&:nth-of-type(2) .forums-section-item {
background-image: url('../image/forums2.png');
background-size: 100% 100%;
}
&:nth-of-type(4) .forums-section-item {
background-image: url('../image/forums4.png');
background-size: 100% 100%;
}
}
.forums-section-item {
position: relative;
cursor: pointer;
border-radius: 15px;
background-image: url('../image/forums1.png');
background-size: 100% 100%;
.memo_image_info {
width: 100%;
height: auto;
object-fit: cover;
border-radius: 15px 15px 0 0;
transition: transform .4s cubic-bezier(.25, .46, .45, .94);
}
.memo_image_info_box {
margin: 15px;
overflow: hidden;
border-radius: 15px;
}
.forums-section-item-content {
// background-color: white;
border-radius: 15px;
overflow: hidden;
}
.icon-jiantou3 {
display: none;
}
&:hover {
.icon-jiantou3 {
// color:#2149d4;
// display: inline-block;
} }
a { .icon-jiantou21{
// color:#2149d4;
}
.memo_image_info {
transform: scale(1.05);
}
}
a:hover {
color: #2149d4;
.icon-jiantou3 {
display: inline-block; display: inline-block;
} }
.forums-list4-item-link{
color: #091221;
}
} }
}
}
.forums-list4-box { .forums-list4-item-bottom{
background-color: #ffffff; border-bottom: 1px dashed rgba(130, 132, 164, 0.4);
border-radius: 15px; padding-bottom: 25px;
padding: 25px 30px; }
.forums-list4-item { .icon-jiantou21{
position: relative; display: none;
border-bottom: 1px dashed rgba(130, 132, 164, 0.4); }
padding-bottom: 12px;
margin-bottom: 20px;
} }
.viewed_count { .viewed_count {
@ -332,22 +274,11 @@
animation: fire 1s ease-in-out infinite; animation: fire 1s ease-in-out infinite;
} }
} }
.forums-list4-item-link {
&:hover {
.hover-underline {
padding-right: 30px;
}
&~.ant-tag {
display: none;
}
}
}
} }
.team { .team {
display: flex; display: flex;
gap: 20px;
margin-top: 40px; margin-top: 40px;
width: 100%; width: 100%;
padding-left: 0px; padding-left: 0px;
@ -361,103 +292,70 @@
} }
li { li {
height: 280px; height: 260px;
padding: 30px 30px 34px 30px; padding: 30px 30px 34px 30px;
box-sizing: border-box; box-sizing: border-box;
border-radius: 15px;
flex: 1 1; flex: 1 1;
display: flex; display: flex;
flex-direction: column; align-items: center;
justify-content: center;
width: 0; width: 0;
margin-right: 20px;
transition: 0.2s; transition: 0.2s;
text-align: left;
position: relative; position: relative;
transition-duration: 1s; transition-duration: 1s;
cursor: pointer; cursor: pointer;
background-size: 100% 100%; background-size: 100% 100%;
border-radius: 15px;
&>span {
margin-bottom: auto;
font-size: 18px;
}
&::after {
position: absolute;
width: 100%;
height: 100%;
content: "";
right: 0;
top: 0;
background-position: right bottom;
background-repeat: no-repeat;
background-size: 100% 100%;
border-radius: 15px;
transition-duration: 1s;
}
&:nth-child(1) { &:nth-child(1) {
background-image: url("../image/image13.png"); background-image: url("../image/image5.png");
.t-title{
&::after { background:linear-gradient(94.98deg,#ffffff 3.87%,#ffffff 83.9%,#eca2b9 100%);
background-image: url("../image/image9.png"); -webkit-background-clip: text;
width: 100%; -webkit-text-fill-color: transparent;
background-clip: text;
} }
} }
&:nth-child(2) { &:nth-child(2) {
background-image: url("../image/image6.png"); background-image: url("../image/image6.png");
.t-title{
&::after { background:linear-gradient(92.24deg,#ffffff 2.21%,#ffffff 86.8%,#87e1e8 99.78%);
background-image: url("../image/image10.png"); -webkit-background-clip: text;
width: 100%; -webkit-text-fill-color: transparent;
background-clip: text;
} }
} }
&:nth-child(3) { &:nth-child(3) {
background-image: url("../image/image7.png"); background-image: url("../image/image7.png");
.t-title{
&::after { background:linear-gradient(97.88deg,#ffffff 3.83%,#ffffff 83.89%,#eca2b9 100%);
background-image: url("../image/image11.png"); -webkit-background-clip: text;
width: 100%; -webkit-text-fill-color: transparent;
background-clip: text;
} }
} }
&:nth-child(4) { &:nth-child(4) {
background-image: url("../image/image8.png"); background-image: url("../image/image8.png");
.t-title{
&::after { background:linear-gradient(94.26deg,#ffffff 2.16%,#ffffff 81.2%,#9cd8f8 100%);
background-image: url("../image/image12.png"); -webkit-background-clip: text;
width: 100%; -webkit-text-fill-color: transparent;
background-clip: text;
} }
} }
&.hovered {
color: white;
background-image: url("../image/image5.png");
.t-title span{
font-size: 26px;
line-height: 32px;
}
}
// &:hover{
// .t-title span{
// font-size: 26px;
// }
// }
&:last-child {
margin-right: 0px;
}
&.hovered, &:hover { &.hovered, &:hover {
flex: 2 1; transform: translateY(-24px);
.highlight-card-bottom{
&:nth-child(1)::after { font-family: "alibaba";
width: 50%;
} }
&:not(:nth-child(1))::after { .iconfont {
width: 70%; display: inline-block;
animation: arrow-move 1.2s ease-in-out infinite;
} }
} }
@ -467,30 +365,84 @@
span { span {
display: block; display: block;
font-size: 18px; font-size: 28px;
line-height: 30px; line-height: 36px;
font-family: "alibabaBold"; font-family: "alibabaBold";
} }
} }
.t-desc { .highlight-card-bottom{
font-size: 14px; position: absolute;
line-height: 22px; left: 0px;
text-align: left; bottom: 10px;
word-break: break-all; width: 100%;
overflow: hidden; display: flex;
text-overflow: ellipsis; justify-content: space-between;
display: -webkit-box; align-items: center;
-webkit-box-orient: vertical; padding: 0 25px;
font-family: alibabaPuhuiR; color: white;
-webkit-line-clamp: 2; font-size:20px;
opacity: 0.8;
} }
}
}
.t-time { .forums_header{
font-size: 15px; background-image: url('../image/bk3.png');
text-align: left; background-size: 100% 100%;
align-items: center;
padding: 11px 30px;
.forums_search{
width: 320px;
margin-right: auto;
.ant-input{
border: none;
border-radius:18px;
height: 36px;
padding-left: 16px;
} }
.ant-input-search-icon{
position: relative;
width: 15px;
svg{
display: none;
}
&::before{
content: "\ea0e";
font-family: iconfont;
position: absolute;
display: inline-block;
color: #191919;
left: 0px;
font-size: 16px;
}
}
}
a{
color:#8284a4;
font-size:15px;
&.active{
color: #2149d4;
}
}
}
.forumListById_box{
background-image: url('../image/bk4.png');
background-size: 100% 100%;
padding-bottom: 100px;
min-height: 89vh;
a.add_forum{
padding: 0 18px;
border-radius:24px;
margin-left: auto;
}
.ant-pagination{
padding-bottom: 40px;
} }
} }
@ -519,4 +471,49 @@
100% { 100% {
width: 90%; width: 90%;
} }
}
@keyframes eyeRightWhite {
0%,to {
transform: translate(27.22px,1px)
}
33%,66% {
transform: translate(24.22px,3px)
}
80% {
transform: translate(24.22px,1px)
}
}
@keyframes eyeLeftWhite {
0%,to {
transform: translate(6.59px,4.94px)
}
33%,66% {
transform: translate(3.59px,6.94px)
}
80% {
transform: translate(3.59px,4.94px)
}
}
@keyframes eyeRightPupil {
0%,to {
transform: translate(38.745px,12.895px)
}
33%,66% {
transform: translate(31.745px,16.895px)
}
80% {
transform: translate(31.745px,10.895px)
}
}
@keyframes eyeLeftPupil {
0%,to {
transform: translate(18.16px,17.11px)
}
33%,66% {
transform: translate(11.16px,21.11px)
}
80% {
transform: translate(11.16px,15.11px)
}
} }

View File

@ -0,0 +1,84 @@
import React, { useEffect, useState, useMemo } from "react";
import { Breadcrumb } from "antd";
import { Link } from "react-router-dom";
import icon19 from '../image/icon19.png';
import { getForumHead, getForumsById } from "../api";
import ForumList from "./forumList";
import './index.scss';
function forumListById(props){
const LIMIT = 10;
const { match: { params: { id } }, current_user, showLoginDialog } = props;
const [detail, setDetail] = useState({});
const [forumsList, setForumsList] = useState([]);
const [total, setTotal] = useState(0);
const [page, setPage] = useState(1);
const [search, setSearch] = useState()
const [sort, setSort] = useState("published_at")
const {forum_section={}, bread_crumb} = detail || {};
const {forum_tag} = bread_crumb || {};
// forum_tag children_bread_crumb ID
const getTagIds = (tag) => {
if (!tag) return [];
const ids = [tag.id];
if (tag.children_bread_crumb && tag.children_bread_crumb.id) {
ids.push(...getTagIds(tag.children_bread_crumb));
}
return ids;
};
const tagIdsStr = useMemo(() => getTagIds(forum_tag).join(','), [forum_tag]);
useEffect(()=>{
if(!id) return
getForumHead(id).then(res=>{
setDetail(res || {});
document.title = `${res && res.forum_section && res.forum_section.title}-论坛交流`;
})
}, [id])
useEffect(()=>{
if(!id) return
getForumsById(id, {
page: page,
limit: LIMIT,
sort: sort,
select_type: 'all',
search: search
}).then(res => {
if (res) {
setForumsList(res.memos || []);
setTotal(res.memos_count);
}
})
}, [id, page, sort, search])
return <div className="forumListById_box">
<div className="width1600 pt25 pb20">
<Link to="/forums" className="opacity7">论坛交流<i className="iconfont icon-youjiantou font-12 ml5 mr5"></i></Link>
{forum_section.title}详情
</div>
<div className="width1600 df" style={{alignItems: 'center'}}>
<img src={icon19} width={48} className="mr25"/>
<div>
<div className="font-bd font-24">{forum_section.title}</div>
<div className="opacity8 font-15">{forum_section.description}</div>
</div>
<a className="add_forum" href={`/forums/add?sec=${tagIdsStr}`} onClick={(e)=>{
if(current_user && !current_user.login){
e.stopPropagation();
e.preventDefault();
showLoginDialog(`/forums/add?sec=${tagIdsStr}`);
}
}}><i className="iconfont icon-a-fabu1 font-16 mr5"></i>写点什么</a>
</div>
{/* 板块对应的帖子列表 */}
<div className="width1600 df mt30">
<ForumList title="全部帖子" forumList={forumsList} LIMIT={LIMIT} page={page} setPage={setPage} total={total} onSearch={setSearch} onSort={setSort} sortBy={sort}/>
</div>
</div>
}
export default forumListById;

View File

@ -315,10 +315,11 @@ i.color-orange05:hover {
color:#fff !important; color:#fff !important;
} }
} }
.ant-pagination-item:hover, .ant-pagination-item:focus, .ant-pagination-item:active, .ant-pagination-next:hover .ant-pagination-item-link, .ant-pagination-prev:hover .ant-pagination-item-link{ li, li:hover, li:focus, li:active{
border-color:#091221; border-color:#091221 !important;
a, i{ a, i, a.ant-pagination-item-link i{
color:#091221; color:#091221;
border-color:#091221 !important;
} }
} }
.ant-pagination-options-quick-jumper input:hover{ .ant-pagination-options-quick-jumper input:hover{