banner图片宽度自适应

This commit is contained in:
caishi 2024-05-15 11:25:05 +08:00
parent a407064ab1
commit f12ab4c07b
83 changed files with 2248 additions and 2609 deletions

View File

@ -11,6 +11,28 @@ export const Competitions = {
//竞赛首页
path: '/competitions/exports',
component: '@/pages/Competitions/Exports/',
},{
path: '/competitions/2024/information/:id',
component: '@/pages/metting/information/detail',
},
{
path: '/competitions/2024',
component: '@/pages/metting/banner',
routes: [
{
path: '/competitions/2024',
component: '@/pages/metting/home',
},{
path: '/competitions/2024/organization',
component: '@/pages/metting/organization',
},{
path: '/competitions/2024/track/:id',
component: '@/pages/metting/track',
},{
path: '/competitions/2024/information',
component: '@/pages/metting/information',
}
]
},
{
//竞赛首页

View File

@ -1,264 +1,14 @@
import { Paperlibrary } from "./paperlibrary";
import { Paths } from './path';
import { Classrooms } from './classroom';
import { Competitions } from './competitions';
import { Forums } from './forums';
import { Problemset } from './problemset';
import { Shiuns } from './shixuns';
import { Users } from './users';
import { Question } from './question';
import { Engineering } from './engineering';
import { Metting } from './metting';
const routes = [
// { path: '/detail', component: '@/pages/Editor' },
{
path: "/",
component: "@/layouts/index",
routes: [
// Paperlibrary,
// Paths,
// Classrooms,
Competitions,
// Forums,
// Problemset,
// Shiuns,
// Users,
// Question,
// Engineering,
// {
// path: '/educoder-demo',
// exact: true,
// component: '@/pages/Demo/index',
// },
// {
// path: '/otherlogin',
// exact: true,
// component: '@/pages/User/OtherLogin',
// },
// {
// path: '/otherloginqq',
// exact: true,
// component: '@/pages/User/QQLogin',
// },
// {
// path: '/otherloginstart',
// exact: true,
// component: '@/pages/User/WechatLogin',
// },
// {
// path: '/tasks/:taskId',
// exact: true,
// component: '@/pages/tasks/index',
// },
// {
// path: '/tasks/:identifier/jupyter/',
// exact: true,
// component: '@/pages/tasks/Jupyter',
// },
// {
// path: '/myproblems/:id/record-detail/:submitId',
// exact: true,
// component: '@/pages/MyProblem/RecordDetail',
// },
// {
// path: '/myproblems/:id',
// exact: true,
// component: '@/pages/MyProblem/index',
// },
// {
// path: '/login',
// component: '@/layouts/user',
// routes: [
// {
// path: '/login',
// component: '@/pages/User/Login',
// },
// ]
// },
// {
// path: '/user',
// component: '@/layouts/user',
// routes: [
// {
// path: '/user/login',
// component: '@/pages/User/Login',
// },
// {
// path: '/user/register',
// component: '@/pages/User/Register',
// },
// {
// path: '/user/reset-password',
// component: '@/pages/User/ResetPassword',
// },
// ]
// },
// {
// path: '/account',
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: '/account/:username',
// component: '@/pages/Account',
// routes: [
// {
// path: '/account/profile',
// component: '@/pages/Account/Profile/index',
// },
// {
// path: '/account/profile/edit',
// component: '@/pages/Account/Profile/Edit/index',
// },
// {
// path: '/account/certification',
// component: '@/pages/Account/Certification/index',
// },
// {
// path: '/account/secure',
// component: '@/pages/Account/Secure/index',
// },
// {
// path: '/account/binding',
// component: '@/pages/Account/Binding/index',
// },
// {
// path: '/account/Results',
// component: '@/pages/Account/Results/index',
// },
// // {
// // path: '/account/teach-group',
// // component: '@/pages/Account/TeachGroup/index',
// // },
// ],
// },
// ],
// },
// {
// path: "/ch/:id",
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: "/ch/rest/edit/:categoryId/:id",
// // component:"@/pages/RestFul",
// component: "@/pages/RestFul/Edit/index",
// exact: true,
// }, {
// path: "/ch/rest/",
// exact: true,
// component: "@/pages/RestFul"
// }, {
// path: "/ch/rest/:id",
// component: "@/pages/RestFul",
// exact: true,
// }
// ]
// },
// {
// path: '/order',
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: '/order',
// component: '@/pages/User/Detail/Order/index',
// },
// {
// path: '/order/:courseId/information',
// component: '@/pages/User/Detail/Order/pages/orderInformation/index',
// },
// {
// path: '/order/:courseId/pay',
// component: '@/pages/User/Detail/Order/pages/orderPay/index',
// },
// {
// path: '/order/:orderNum/result',
// component: '@/pages/User/Detail/Order/pages/result/index',
// },
// ],
// },
// {
// path: '/colleges',
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: '/colleges/:id/statistics',
// component: '@/pages/Colleges/index',
// },
// ],
// },
// {
// path: '/help',
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: '/help/:id',
// component: '@/pages/Help/Index',
// },
// ],
// },
// {
// path: '/messages',
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: '/messages/:userId/user_tidings',
// component: '@/pages/Messages/Tidings',
// },
// {
// path: '/messages/:userId/private_messages',
// component: '@/pages/Messages/Private',
// },
// {
// path: '/messages/:userId/message_detail',
// component: '@/pages/Messages/Detail',
// }
// ],
// },
// {
// path: '/video',
// component: '@/layouts/SimpleLayouts',
// routes: [
// {
// path: '/video/:videoId',
// component: '@/pages/Video/Detail/[id]',
// }
// ],
// },
// {
// path: "/terminal",
// component: '@/pages/Terminal/'
// },
// { path: '/detail', component: '@/pages/Editor' },
// {
// path: '/',
// component: '@/layouts/SimpleLayouts',
// routes: [
// { path: '/', exact: true, component: '@/pages/index' },
// { path: '/api/*', component: '@/pages/Api', exact: true },
// { path: '/search', exact: true, component: '@/pages/Search/index' },
// { path: '/moop_cases', exact: true, component: '@/pages/MoopCases/index' },
// { path: '/moop_cases/new', exact: true, component: '@/pages/MoopCases/FormPanel' },
// { path: '/moop_cases/:caseId', exact: true, component: '@/pages/MoopCases/InfoPanel' },
// { path: '/moop_cases/:caseId/edit', exact: true, component: '@/pages/MoopCases/FormPanel' },
// { path: '/moop_cases/:caseId/publish-success', exact: true, component: '@/pages/MoopCases/Success' },
// // { path: "/randompaper", exact: true, component: "@/pages/Paperlibrary/Random/Add/index" },
// { path: "/randompaper", exact: true, component: "@/pages/Paperlibrary/Random/AddAndEdit/index" },
// { path: "/randompaper/edit/:id", exact: true, component: "@/pages/Paperlibrary/Random/AddAndEdit/index" },
// // { path: "/randompaper/edit/:id", exact: true, component: "@/pages/Paperlibrary/Random/Edit/index" },
// { path: "/randompaper/detail/:id", exact: true, component: "@/pages/Paperlibrary/Random/Detail/index" },
// { path: '/403', component: '@/pages/HttpStatus/403' },
// { path: '/500', component: '@/pages/HttpStatus/500' },
// { path: '/404', component: '@/pages/HttpStatus/404' },
// { path: '/iwce', component: '@/pages/HttpStatus/HpcCourse' },
// { path: '/iwce/:itemname', component: '@/pages/HttpStatus/HpcCourse' },
// { path: '/hpc-course', component: '@/pages/HttpStatus/HpcCourse' },
// { path: '/user_agents', component: '@/pages/HttpStatus/UserAgents' },
// { path: '/three', component: '@/pages/Three' },
// { component: '@/pages/HttpStatus/404' }
// ],
// }
// Metting
]
},
]

29
config/routes/metting.ts Normal file
View File

@ -0,0 +1,29 @@
export const Metting = {
path: '/2024',
component: '@/layouts/SimpleLayouts',
routes: [
{
path: '/2024/information/:id',
component: '@/pages/metting/information/detail',
},
{
path: '/2024',
component: '@/pages/metting/banner',
routes: [
{
path: '/2024',
component: '@/pages/metting/home',
},{
path: '/2024/organization',
component: '@/pages/metting/organization',
},{
path: '/2024/track/:id',
component: '@/pages/metting/track',
},{
path: '/2024/information',
component: '@/pages/metting/information',
}
]
}
],
}

View File

@ -34,6 +34,7 @@
"animate.css": "4.1.1",
"antd": "4.6.1",
"array-move": "3.0.1",
"axios": "^1.6.8",
"checkapi.js": "^0.1.2",
"code-prettify": "0.1.0",
"codemirror": "5.58.2",

Binary file not shown.

View File

@ -5,7 +5,7 @@ import marked, {
cleanToc,
getMathExpressions,
resetMathExpressions,
lines,
lines
} from '@/utils/marked';
import 'code-prettify';
import Hls from 'hls.js';
@ -37,21 +37,8 @@ export default ({
}: IProps) => {
let str = String(value);
const [data, setData] = useState("")
const html = useMemo(() => {
try {
const reg = /\(\s+\/api\/attachments\/|\(\/api\/attachments\/|\(\/attachments\/download\//g;
const reg2 = /\"\/api\/attachments\/|\"\/attachments\/download\//g;
const reg3 = /\(\s+\/files\/uploads\/|\(\/files\/uploads\//g;
str = str.replace(reg, "(" + ENV.API_SERVER + "/api/attachments/")
.replace(reg2, '"' + ENV.API_SERVER + "/api/attachments/")
.replace(reg3, '"' + ENV.API_SERVER + "/files/uploads/")
.replaceAll("http://video.educoder", "https://video.educoder")
.replaceAll("http://www.educoder.net/api", "https://data.educoder.net/api")
.replaceAll("https://www.educoder.net/api", "https://data.educoder.net/api")
.replace(/\r\n/g,"\n")
str = str.replace(new RegExp("(?<!\\n)\\n(?!\\n)", "g"), " \n")
} catch (e) { };
let rs = marked(str)
const math_expressions = getMathExpressions();

View File

@ -1,3 +1,7 @@
@font-face {
font-family: YouSheBiaoTiHei;
src: url(../../public/font/YouSheBiaoTiHei-2.ttf);
}
.loading {
position: fixed !important;
top: 50% !important;

View File

@ -49,6 +49,7 @@ interface CompetitionsParamsType {
page: number, //第一页开始
per_page: number, //每页条数默认20
category: any, //分类筛选”nearly_published”,”progressing”, “ended”
external:string
}
export interface CompetitionsType {
namespace: string;

View File

@ -0,0 +1,26 @@
import { useState, useEffect } from 'react';
import { getDirList , getBaseInfo } from '../pages/metting/api';
export default function Page() {
const [dirList, setDirList] = useState({});
const [mainInfo, setMainInfo] = useState({});
useEffect(() => {
getDirList().then((res) => {
if (res && res.rows.length > 0) {
let idList = {}
res.rows.map(e => {
idList[e.name] = e.id
})
setDirList(idList);
}
});
getBaseInfo().then(result => {
setMainInfo(result.data);
}).catch(console.error())
}, []);
return {
dirList,mainInfo
};
};

View File

@ -1,215 +0,0 @@
@import '../../global.less';
.bg {
background: rgba(250, 250, 250, 1);
margin-bottom: 80px;
}
.header {
height: 240px;
.flex_box_container();
flex-direction: row;
justify-content: center;
background-image: url('../../assets/images/statistics.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
div[class~='ant-row'] {
width: 100%;
}
}
.headerContent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 1300px;
width: 1300px;
height: 100%;
}
.headTitle {
flex: 1 1;
display: flex;
align-items: center;
color: #4cacff;
font-size: 32px;
}
.headItemTitle {
color: #989898;
font-size: 14px;
margin-bottom: 10px;
}
.headItemValue {
font-size: 24px;
color: #fff;
margin-bottom: 10px;
}
.wrap {
width: 1300px;
background-color: #fff;
margin: 0px auto;
margin-top: 30px;
ul[class~='ant-pagination'] {
width: 100%;
text-align: center;
}
[class~='ant-table-row'] {
[class~='ant-table-cell'] {
color: #05101a;
}
}
}
.basicTitle {
line-height: 1.2;
padding: 2rem 1.25rem;
border-bottom: unset;
background: #fff;
font-size: 1.5rem;
font-weight: 500;
}
.basicHead {
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
p {
width: 217px;
text-align: center;
font-size: 16px;
height: 48px;
line-height: 48px;
color: #686868;
background: #f5f5f5;
border-top: 1px solid #ebebeb;
margin-top: 0;
margin-bottom: 0px;
}
}
.basicContent {
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
}
.basicItem {
width: 217px;
text-align: center;
height: 100px;
line-height: 100px;
background: #ffffff;
border-top: 1px solid #ebebeb;
border-bottom: 1px solid #ebebeb;
color: #000000;
font-size: 24px;
}
.basicItemText {
color: #000000;
margin-left: 5px;
font-size: 16px;
}
.courseLoading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 500px;
}
.maxWidth220 {
max-width: 220px;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxWidth340 {
max-width: 340px;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxWidth175 {
max-width: 175px;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxWidth225 {
max-width: 225px;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxWidth255 {
max-width: 255px;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxWidth105 {
max-width: 105px;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.colorFFC {
color: #ffc107;
}
.colorBlack {
color: rgb(5, 16, 26);
}
.colorGreen {
color: rgb(23, 162, 184);
}
.color4CA {
color: #4cacff;
}
.rankLeft {
width: 63%;
}
.rankRight {
width: 37%;
}
.flexRow {
display: flex;
flex-direction: row;
}

View File

@ -1,549 +0,0 @@
import React, { FC, useEffect, useState } from 'react';
import {
ShixunsDetailModelState,
GlobalSettingModelState,
ConnectProps,
Loading,
connect,
Dispatch,
useParams
} from 'umi';
import { Row, Col, Spin, Table ,Pagination} from "antd";
import NoData from '@/components/NoData';
import {
getCollegesStatistics, getShixunReportCount, getShixunTime, getCourseStatistics,
getTeacherRanking, getStudentRanking, getShixunChart, getStudentHot
} from '@/service/colleges';
import {
teacherRankingColums, studentRankingColums,
getTeacherRankingEchartOption, getStudentRankingEchartOption
} from './utils';
import ReactEcharts from "echarts-for-react";
import styles from "./index.less";
import { setDocumentTitle } from '@/utils/util'
interface PageProps extends ConnectProps {
shixunsDetail: ShixunsDetailModelState;
globalSetting: GlobalSettingModelState;
loading: boolean;
dispatch: Dispatch;
}
const CollegesPage: FC<PageProps> = ({
shixunsDetail,
globalSetting,
loading,
dispatch,
...props
}) => {
const params = useParams<any>();
const [headData, setHeadData] = useState<any>({});
const [shixunReportCount, setShixunReportCount] = useState<number>();
const [shixunTime, setShixunTime] = useState<number>();
const [courseStatistics, setCourseStatistics] = useState<any>();
const [teacherRanking, setTeacherRanking] = useState<any>();
const [studentRanking, setStudentRanking] = useState<any>();
const [shixunChartData, setShixunChartData] = useState<any[]>();
const [shixunChartDataName, setshixunChartDataName] = useState<any[]>();
const [studentHotValue, setStudentHotValue] = useState<any[]>();
const [studentHotName, setStudentHotName] = useState<any[]>();
const [page, setPage] = useState<number>(1);
const [isLoadingCourse, setIsLoadingCourse] = useState<boolean>(false);
const [isLoadingTeacher, setIsLoadingTeacher] = useState<boolean>(false);
const [isLoadingStudent, setIsLoadingStudent] = useState<boolean>(false);
useEffect(() => {
setDocumentTitle('学院统计')
if (!params.id) {
return;
}
getHeadData();
getShixunReportCountData();
getShixunTimeData();
getTeacherRankingData(1, 10);
getStudentRankingData(1, 10);
getShixunChartData();
getStudentHotData();
getCourseStatisticsData(1, 10);
}, [params.id]);
const getHeadData = async () => {
const res = await getCollegesStatistics({ id: params.id });
setDocumentTitle(res?.school || '头歌实践教学平台')
setHeadData(res || {});
}
const getShixunReportCountData = async () => {
const res = await getShixunReportCount({ id: params.id });
setShixunReportCount(res?.shixun_report_count || 0);
}
const getShixunTimeData = async () => {
const res = await getShixunTime({ id: params.id });
setShixunTime(res?.shixun_time || 0);
}
const getCourseStatisticsData = async (page: number, per_page: number) => {
setIsLoadingCourse(true);
const res = await getCourseStatistics({ id: params.id, page, per_page,...params }).catch(e => setIsLoadingCourse(false));
setIsLoadingCourse(false);
setCourseStatistics(res || []);
}
const getTeacherRankingData = async (page: number, per_page: number) => {
setIsLoadingTeacher(true);
const res = await getTeacherRanking({ id: params.id, page, per_page }).catch(e => setIsLoadingTeacher(false));
setIsLoadingTeacher(false);
setTeacherRanking(res || []);
}
const getStudentRankingData = async (page: number, per_page: number) => {
setIsLoadingStudent(true);
const res = await getStudentRanking({ id: params.id, page, per_page }).catch(e => setIsLoadingStudent(false));
setIsLoadingStudent(false);
setStudentRanking(res || []);
}
const getShixunChartData = async () => {
const res = await getShixunChart({ id: params.id });
setShixunChartData(res?.data || []);
setshixunChartDataName(res?.names || []);
}
const getStudentHotData = async () => {
const res = await getStudentHot({ id: params.id });
setStudentHotValue(res?.values || []);
setStudentHotName(res?.names || []);
}
const courseStatisticsColums: any = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
align: 'center',
className: styles.maxWidth220,
render: (text: any, record: any) => (
<a href={`/classrooms/${record.id}/students`} title={record.name} target="_blank">
{
record.name
}
</a>
)
},
{
title: '管理教师',
dataIndex: 'teachers',
key: 'teachers',
align: 'center',
className: styles.maxWidth340,
render: (text: any, record: any) => (
<span className={styles.colorBlack} title={record.teachers}>
{
record.teachers
}
</span>
)
},
{
title: '评测次数',
dataIndex: 'times',
key: 'evaluating_count',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "evaluating_count" ? (params.sort === 2 ? "descend" : "ascend") : null,
className: styles.maxWidth175,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record.evaluating_count
}
</span>
),
},
{
title: '学生',
key: 'student_count',
dataIndex: 'student',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "student_count" ? (params.sort === 2 ? "descend" : "ascend") : null,
className: styles.maxWidth225,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record.student_count
}
</span>
)
},
{
title: '实训作业',
dataIndex: 'training',
key: 'shixun_work_count',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "shixun_work_count" ? (params.sort === 2 ? "descend" : "ascend") : null,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record.shixun_work_count
}
</span>
)
},
{
title: '资源',
dataIndex: 'resources',
key: 'attachments_count',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "attachments_count" ? (params.sort === 2 ? "descend" : "ascend") : null,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record?.attachments_count
}
</span>
),
},
{
title: '帖子',
dataIndex: 'posts',
key: 'message_count',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "message_count" ? (params.sort === 2 ? "descend" : "ascend") : null,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record.message_count
}
</span>
)
},
{
title: '其它任务',
dataIndex: 'othertasks',
key: 'other_work_count',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "other_work_count" ? (params.sort === 2 ? "descend" : "ascend") : null,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record.other_work_count
}
</span>
)
},
{
title: '状态',
dataIndex: 'states',
key: 'states',
align: 'center',
render: (text: any, record: any) => (
<span className={record.is_end ? styles.colorBlack : styles.colorFFC}>
{
record.is_end ?
"已结束" :
"正在进行"
}
</span>
)
},
{
title: '最新活跃时间',
dataIndex: 'timemy',
key: 'activity_time',
align: 'center',
sorter: true,
showSorterTooltip: false,
sortOrder:params.sort_name === "activity_time" ? (params.sort === 2 ? "descend" : "ascend") : null,
render: (text: any, record: any) => (
<span className={styles.colorBlack} >
{
record.activity_time
}
</span>
)
},
];
return <section className={styles.bg}>
<header className={styles.header}>
<div className={styles.headerContent}>
<div className={styles.headTitle}>{headData?.school}</div>
<Row justify="space-around" gutter={[40, 0]}>
<Col span={4} className="tc">
<p className={styles.headItemTitle}></p>
<p className={styles.headItemValue}>{headData?.teachers_count || 0}</p>
</Col>
<Col span={4} className="tc">
<p className={styles.headItemTitle}></p>
<p className={styles.headItemValue}>{headData?.students_count || 0}</p>
</Col>
<Col span={4} className="tc">
<p className={styles.headItemTitle}></p>
<p className={styles.headItemValue}>{headData?.courses_count || 0}</p>
</Col>
<Col span={4} className="tc">
<p className={styles.headItemTitle}></p>
<p className={styles.headItemValue}>{headData?.shixuns_count || 0}</p>
</Col>
</Row>
</div>
</header>
<div className={styles.wrap}>
<div className={styles.basicTitle}>使</div>
<div className={styles.basicHead}>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
</div>
<div className={styles.basicContent}>
<div className={styles.basicItem}>
{
headData?.teachers_count || headData?.teachers_count === 0 ?
<>
{headData?.teachers_count}
<span className={styles.basicItemText}></span>
</> :
<Spin />
}
</div>
<div className={styles.basicItem}>
{
headData?.students_count || headData?.students_count === 0 ?
<>
{headData?.students_count}
<span className={styles.basicItemText}></span>
</> :
<Spin />
}
</div>
<div className={styles.basicItem}>
{
headData?.courses_count || headData?.courses_count === 0 ?
<>
{headData?.courses_count}
<span className={styles.basicItemText}></span>
</> :
<Spin />
}
</div>
<div className={styles.basicItem}>
{
headData?.shixuns_count || headData?.shixuns_count === 0 ?
<>
{headData?.shixuns_count}
<span className={styles.basicItemText}></span>
</> :
<Spin />
}
</div>
<div className={styles.basicItem}>
{
shixunReportCount || shixunReportCount === 0 ?
<>
{shixunReportCount}
<span className={styles.basicItemText}></span>
</> :
<Spin />
}
</div>
<div className={styles.basicItem}>
{
shixunTime || shixunTime === 0 ?
<>
{shixunTime}
<span className={styles.basicItemText}></span>
</> :
<Spin />
}
</div>
</div>
</div>
<div className={styles.wrap}>
<div className={styles.basicTitle}></div>
{
!courseStatistics &&
<div className={styles.courseLoading}>
<Spin tip="正在加载..." />
</div>
}
{
courseStatistics &&
(
courseStatistics?.courses?.length ?
<Table
columns={courseStatisticsColums}
dataSource={courseStatistics?.courses}
loading={isLoadingCourse}
onChange={(a, b, c: any)=>{
params.sort_name=c?.columnKey;
if (c.order === 'ascend') params['sort'] = 1;
if (c.order === 'descend') params['sort'] = 2;
if (!c.order) {
params['sort'] = 1
};
setPage(1);
getCourseStatisticsData(1,10);
}}
pagination={false}
// pagination={{
// current: page,
// pageSize: 10,
// showQuickJumper: true,
// showSizeChanger: false,
// total: courseStatistics?.course_count,
// onChange: (page, pageSize) => {
// setPage(page);
// getCourseStatisticsData(page, 10);
// }
// }}
/> :
<NoData />
)
}
<Pagination current={page}
style={{marginTop:'20px'}}
pageSize={10} showQuickJumper
showSizeChanger={false}
total={courseStatistics?.course_count}
onChange={(page,pagesize)=>{
setPage(page);
getCourseStatisticsData(page, 10);
}}
/>
</div>
<div className={`${styles.wrap} ${styles.flexRow}`}>
<div className={styles.rankLeft}>
<div className={styles.basicTitle}></div>
{
!teacherRanking &&
<div className={styles.courseLoading}>
<Spin tip="正在加载..." />
</div>
}
{
teacherRanking &&
(
teacherRanking?.teachers?.length ?
<Table
columns={teacherRankingColums}
dataSource={teacherRanking?.teachers}
loading={isLoadingTeacher}
pagination={false}
/> :
<NoData />
)
}
</div>
<div className={styles.rankRight}>
<div className={styles.basicTitle}>线</div>
{
!shixunChartData &&
<div className={styles.courseLoading}>
<Spin tip="正在加载..." />
</div>
}
{
shixunChartData &&
(
shixunChartData?.length ?
<ReactEcharts
option={getTeacherRankingEchartOption(shixunChartData, shixunChartDataName)}
style={{ height: 600 }}
opts={{ renderer: 'svg' }}
/> :
<NoData />
)
}
</div>
</div>
<div className={`${styles.wrap} ${styles.flexRow}`}>
<div className={styles.rankLeft}>
<div className={styles.basicTitle}></div>
{
!studentRanking &&
<div className={styles.courseLoading}>
<Spin tip="正在加载..." />
</div>
}
{
studentRanking &&
(
studentRanking?.teachers?.length ?
<Table
columns={studentRankingColums}
dataSource={studentRanking?.teachers}
loading={isLoadingStudent}
pagination={false}
/> :
<NoData />
)
}
</div>
<div className={styles.rankRight}>
<div className={styles.basicTitle}></div>
{
!studentHotName &&
<div className={styles.courseLoading}>
<Spin tip="正在加载..." />
</div>
}
{
studentHotName &&
(
studentHotName?.length ?
<ReactEcharts
option={getStudentRankingEchartOption(studentHotName, studentHotValue)}
style={{ height: 600 }}
opts={{ renderer: 'svg' }}
/> :
<NoData />
)
}
</div>
</div>
</section>
};
export default connect(({
shixunsDetail,
loading,
globalSetting
}: {
shixunsDetail: ShixunsDetailModelState;
loading: Loading;
globalSetting: GlobalSettingModelState;
}) => ({
shixunsDetail,
globalSetting,
loading: loading.models.index,
}))(CollegesPage);

View File

@ -1,351 +0,0 @@
import React from 'react';
import ENV from '@/utils/env';
import styles from './index.less';
export const teacherRankingColums: any = [
{
title: '排名',
dataIndex: 'ranking',
key: 'ranking',
align: 'center',
render: (text: any, record: any, index: number) => (
index + 1
)
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
align: 'center',
className: styles.maxWidth105,
render: (text: any, record: any) => (
<a href={`/users/${record.login}`} title={record.name} target="_blank">
{
record.name
}
</a>
)
},
{
title: '管理课堂',
dataIndex: 'classroom',
key: 'classroom',
align: 'center',
className: styles.maxWidth175,
render: (text: any, record: any) => (
<span>
{
record.course_count
}
</span>
),
},
{
title: '已发布实训作业',
key: 'assignment',
dataIndex: 'assignment',
align: 'center',
className: styles.maxWidth255,
render: (text: any, record: any) => (
<span>
{
record.shixun_work_count
}
</span>
)
},
{
title: '未发布实训作业',
dataIndex: 'released',
key: 'released',
align: 'center',
render: (text: any, record: any) => (
<span>
{
record.un_shixun_work_count
}
</span>
)
},
{
title: '学生数',
dataIndex: 'studentnumber',
key: 'studentnumber',
align: 'center',
render: (text: any, record: any) => (
<span>
{
record.student_count
}
</span>
),
},
{
title: '完成率',
dataIndex: 'completionrate',
key: 'completionrate',
align: 'center',
render: (text: any, record: any) => (
<span>
{
record.complete_rate + "%"
}
</span>
)
},
{
title: '发布实训',
dataIndex: 'releasetraining',
key: 'releasetraining',
align: 'center',
render: (text: any, record: any) => (
<span className={styles.color4CA}>
{
record.publish_shixun_count
}
</span>
)
}
];
const renderStudentRanking = (key: number) => {
const image = `${ENV.IMG_SERVER}/images/educoder/competition/${key}.png`;
const mapping: any = {
1: <img width="18px" height="22px" className="mt5" src={image} />,
2: <img width="18px" height="22px" className="mt5" src={image} />,
3: <img width="18px" height="22px" className="mt5" src={image} />,
}
return mapping[key] || key;
}
export const studentRankingColums: any = [
{
title: '排名',
dataIndex: 'ranking',
key: 'ranking',
align: 'center',
className: "edu-txt-center font-14",
width: '100px',
render: (text: any, record: any, index: number) => (
<span>
{
renderStudentRanking(index + 1)
}
</span>
)
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
align: 'center',
className: styles.maxWidth105,
render: (text: any, record: any) => (
<a href={`/users/${record.login}`} title={record.name} target="_blank">
{
record.name
}
</a>
)
},
{
title: '学号',
dataIndex: 'studentid',
key: 'studentid',
align: 'center',
className: styles.maxWidth175,
render: (text: any, record: any) => (
<span>
{
record.student_id
}
</span>
),
},
{
title: '完成实训',
key: 'training',
dataIndex: 'training',
align: 'center',
className: styles.maxWidth225,
render: (text: any, record: any) => (
<span>
{
record.shixun_count
}
</span>
)
},
{
title: '在学实训',
dataIndex: 'learning',
key: 'learning',
align: 'center',
render: (text: any, record: any) => (
<span>
{
record.study_shixun_count
}
</span>
)
},
{
title: '金币',
dataIndex: 'goldcoin',
key: 'goldcoin',
align: 'center',
render: (text: any, record: any) => (
<span>
{
record.grade
}
</span>
),
},
{
title: '经验值',
dataIndex: 'empirical',
key: 'empirical',
align: 'center',
render: (text: any, record: any) => (
<span className={styles.colorGreen}>
{
record.experience
}
</span>
)
},
];
export const getTeacherRankingEchartOption = (data: any[] = [], dataName: any[] = []) => {
return {
tooltip: {
trigger: 'item',
formatter: "{d}% <br/>"
},
legend: {
bottom: 50,
left: 'center',
data: dataName
},
series: [
{
type: 'pie',
radius: '65%',
center: ['50%', '35%'],
selectedMode: 'single',
data: data,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
}
export const getStudentRankingEchartOption = (names: any[] = [], values: any[] = []) => {
const colorList = ['#962e66', '#623363', '#CCCCCC', '#9A9A9A', '#FF8080', '#FF80C2', '#B980FF', '#80B9FF', '#6FE9FF', '#4DE8B4', '#F8EF63', '#FFB967'];
return {
backgroundColor: '#fff',
grid: {
left: '3%',
right: '8%',
bottom: '15%',
containLabel: true
},
tooltip: {
show: "true",
trigger: 'item',
formatter: '{c0}',
backgroundColor: 'rgba(0,0,0,0.7)', // 背景
padding: [8, 10], //内边距
extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
xAxis: {
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#CCCCCC'
}
},
splitLine: {
show: false,
lineStyle: {
color: '#CCCCCC'
}
},
axisLabel: {
textStyle: {
color: '#656565',
fontWeight: 'normal',
fontSize: '12'
},
formatter: '{value}'
}
},
yAxis: {
type: 'category',
axisLine: {
lineStyle: {
color: '#cccccc'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
inside: false,
textStyle: {
color: '#656565',
fontWeight: 'normal',
fontSize: '12'
}
},
data: names
},
series: [{
name: '',
type: 'bar',
itemStyle: {
normal: {
show: true,
color: function (params: any) {
return colorList[params.dataIndex]
},
barBorderRadius: 50,
borderWidth: 0,
borderColor: '#333'
}
},
barGap: '0%',
barCategoryGap: '50%',
data: values
}
]
}
}

View File

@ -89,7 +89,7 @@ const competitionsIndexPage: FC<PageProps> = ({
// getHomeNotice();
dispatch({
type: 'competitions/getList',
payload: { ...competitions.listParams},
payload: { ...competitions.listParams,external:"gitlink"},
});
}, [competitions.name]);
useEffect(() => {
@ -412,6 +412,7 @@ const competitionsIndexPage: FC<PageProps> = ({
competitions.listParams.page = 1;
competitions.listParams.category = v;
competitions.listParams.page = 1;
competitions.listParams.external = "gitlink";
dispatch({
type: 'competitions/getList',
payload: { ...competitions.listParams },

View File

@ -1,186 +0,0 @@
import React, { useEffect, useState } from 'react'
import { Input, Form, Button, Checkbox, Upload, message, Modal } from 'antd'
import { InboxOutlined } from '@ant-design/icons'
import ENV from '@/utils/env'
import { Tag } from '../index'
import MarkdownEditor from '@/components/markdown-editor'
const CheckboxGroup = Checkbox.Group
const { Dragger } = Upload
function getBase64(img: any, callback: any) {
const reader = new FileReader()
reader.addEventListener('load', () => callback(reader.result))
reader.readAsDataURL(img)
}
interface IMoopCase {
callback: any
tags: Array<Tag>
initialValues: any
removeAttachment: (id: any) => Promise<boolean>
}
export default ({ callback, tags = [], initialValues = {}, removeAttachment }: IMoopCase) => {
const [uploadUrl, setUploadUrl] = useState<string>('')
const [form] = Form.useForm()
function onSubmit(values: any) {
const { cover_id, attachment_ids, ...rest } = values
callback({
...rest,
cover_id: cover_id?.map((item: any) => item.response ? item.response.id : item.uid)[cover_id?.length -1],
attachment_ids: attachment_ids ? attachment_ids?.map((item: any) => item.response ? item.response.id : item.uid) : []
})
}
function normFile(e) {
if (Array.isArray(e)) {
return e
}
return e && e.fileList
}
const uploadProps = {
multiple: true,
beforeUpload: (file: any) => {
const isLt150M = file.size / 1024 / 1024 < 100
if (!isLt150M) {
message.error('该文件无法上传。超过文件大小限制(100MB),建议上传到百度云等其它共享工具里然后再txt文档里给出链接以及共享密码并上传')
return isLt150M
}
},
action: `${ENV.API_SERVER}/api/attachments.json`,
onRemove: (file: any) => {
Modal.confirm({
centered: true,
okText: '确定',
cancelText: '取消',
content: '是否确认删除?',
onOk: () => {
return removeAttachment(file.response ? file.response.id : file.uid)
},
onCancel: () => {
return false
}
})
}
}
function onChangeCoverUrl(info: any) {
if (info.file.status === 'done') {
getBase64(info.file.originFileObj, (imageUrl: string) =>
setUploadUrl(imageUrl)
)
}
}
const uploadCoverProps = {
showUploadList: false,
withCredentials:true,
action: `${ENV.API_SERVER}/api/attachments.json`,
onChange: onChangeCoverUrl
}
useEffect(() => {
if (initialValues.title) {
form.setFieldsValue(initialValues)
if(initialValues.cover_id.length>0){
setUploadUrl( ENV.IMG_SERVER + initialValues.cover_id[0].url)
}
}
}, [initialValues])
return (
<Form
form={form}
initialValues={initialValues}
className="moop-panel"
onFinish={onSubmit}
>
<Form.Item
label="标题"
name="title"
extra={<p className='tip'>/</p>}
rules={[{ required: true, message: '请输入标题' }]}
>
<Input placeholder='例如:软件工程教学案例' />
</Form.Item>
<div className="flex-container">
<Form.Item
label="作者"
name="author_name"
rules={[{ required: true, message: '请输入作者名称' }]}
>
<Input placeholder='请输入姓名' style={{ marginRight: 20, width: 200 }}
/>
</Form.Item>
<Form.Item
name="author_school_name"
rules={[{ required: true, message: '请输入作者单位名称' }]}
>
<Input placeholder='请输入作者单位名称' style={{ width: 200 }} />
</Form.Item>
</div>
<Form.Item name="tag_ids" label='标签' rules={[{ required: true, message: '请选择一个标签' }]} >
<CheckboxGroup>
{tags.map(t => <Checkbox key={t.id} value={t.id} > {t.name} </Checkbox>)}
</CheckboxGroup>
</Form.Item>
<Form.Item name='publish' valuePropName="checked">
<Checkbox></Checkbox>
</Form.Item>
<Form.Item
name="content"
label='描述'
valuePropName='defaultValue'
rules={[{ required: true, message: '请输入描述内容' }]}
>
<MarkdownEditor id='moop_cases_content' height={400} />
</Form.Item>
<Form.Item name="attachment_ids" valuePropName="fileList" getValueFromEvent={normFile} >
<Dragger {...uploadProps} >
<p className='c-blue' style={{ marginBottom: 10, fontSize: 18 }} ></p>
<p className='c-grey-c'>CTRL可以上传多份文档100MB</p>
</Dragger>
</Form.Item>
<Form.Item name='cover_id' label='封面图' extra='上传尺寸120*90 px' getValueFromEvent={normFile} valuePropName="fileList"
rules={[{ required: true, message: "请上传封面图" }]}
>
<Upload {...uploadCoverProps}>
{uploadUrl ? <img src={uploadUrl} width={120} /> : <a title='上传封面图' className='upload-cover'> <InboxOutlined /> </a>}
</Upload>
</Form.Item>
<div className="desc">
<h3></h3>
<p> </p>
</div>
<div className="desc">
<h3></h3>
<ul style={{ padding: 0 }}>
<li>1.</li>
<li>2.
<a href="https://www.educoder.net/courses/1309/boards/5909/messages/34799" target='_blank'> </a> <a href='https://www.educoder.net/courses/1309/boards/5909/messages/34798' target='_blank'>使</a>100MB</li>
<li>3.</li>
<li>4.使ChromeFirefoxSafariIE11</li>
</ul>
</div>
<Form.Item style={{ marginTop: 10 }}>
<Button type="primary" htmlType="submit" >
</Button>
</Form.Item>
</Form>
)
}

View File

@ -1,22 +0,0 @@
.moop-panel {
width: 1200px;
margin: 0 auto;
background: #fff;
padding: 30px;
.tip {
margin-top: 10px;
color: #ccc;
}
.upload-cover {
width: 120px;
height: 90px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
border: 1px dashed #ccc;
font-size: 24px;
margin-bottom: 10px;
}
}

View File

@ -1,147 +0,0 @@
import React, { useEffect, useReducer } from 'react'
import { Spin } from 'antd'
import '../InfoPanel/index.less'
import './index.less'
import { Tag } from '../index'
import { Link, useParams, useHistory } from 'umi'
import MoopForm from './MoopForm'
import { getMoopCase, getLibraryTags, removeAttachment, addMoopCase, updateMoopCase } from './service'
interface IState {
loading: boolean
data: any
tags: Array<Tag>
}
const initialState: IState = {
loading: false,
data: {},
tags: []
}
enum Types {
GET_DATA,
SET_LOADING,
GET_TAGS
}
function reducer(state: IState, action: any) {
switch (action.type) {
case Types.GET_DATA:
return { ...state, ...action.payload }
case Types.GET_TAGS:
return { ...state, tags: action.payload }
case Types.SET_LOADING:
return { ...state, loading: action.payload }
default:
throw new Error()
}
}
export default () => {
const { caseId } = useParams()
const history = useHistory()
const [state, dispatch] = useReducer(reducer, initialState)
const { loading, tags, data } = state
useEffect(() => {
async function init() {
const response = await getLibraryTags()
dispatch({
type: Types.GET_TAGS,
payload: response.library_tags
})
}
init()
}, [])
useEffect(() => {
let didCancel = false
async function init() {
dispatch({ type: Types.SET_LOADING, payload: false })
let response = await getMoopCase(caseId)
const { author_name, author_school_name, content, cover, attachments, status, title, tags } = response
if (!didCancel) {
if (response.status === 404) {
history.push('/moop_cases')
} else {
dispatch({
type: Types.GET_DATA,
payload:
{
data: {
title,
author_name,
author_school_name,
content,
tag_ids: tags.map((item: any) => item.id),
publish: status === 'published',
attachment_ids: attachments.map((item: any) => {
return {
uid: item.id,
id: item.id,
name: item.title,
url: item.url,
filesize: item.filesize,
status: 'done'
}
}),
cover_id:cover?.id?[{
uid: cover?.id,
id: cover?.id,
name: cover?.title,
url: cover?.url,
filesize: cover?.filesize,
status: 'done'
}]:[],
},
loading: false
}
})
}
}
}
if (!didCancel && caseId) {
init()
}
return () => {
didCancel = true
}
}, [caseId])
async function onUpdateCase(values: any) {
if (caseId) {
await updateMoopCase(caseId, values)
history.push(`/moop_cases/${caseId}/publish-success`)
} else {
const { id } = await addMoopCase(values)
if (id) {
history.push(`/moop_cases/${id}/publish-success`)
}
}
}
const moopFormProps = {
initialValues: data,
tags,
removeAttachment,
callback: onUpdateCase
}
return (
<Spin spinning={loading} delay={500}>
<div className="moop-info">
<div className="crumb">
<Link to='/moop_cases'> &gt; </Link>
<span></span>
</div>
<div className="title">
<h2></h2>
<Link to='/moop_cases'></Link>
</div>
<MoopForm {...moopFormProps} />
</div>
</Spin>
)
}

View File

@ -1,22 +0,0 @@
import { get, post, del, put } from '@/utils/fetch'
export function getMoopCase(id: number) {
return get(`libraries/${id}.json`)
}
export function getLibraryTags() {
return get('library_tags.json')
}
export async function removeAttachment(id: number) {
const response = await del(`attachments/${id}.json`)
return response.status === 0
}
export function addMoopCase(params: any) {
return post(`libraries.json`, params)
}
export function updateMoopCase(id: number, params: any) {
return put(`libraries/${id}.json`, params)
}

View File

@ -1,119 +0,0 @@
.moop-info {
width: 1200px;
margin: 0 auto;
.crumb {
margin: 10px 0 20px 0;
line-height: 20px;
}
h2 {
font-size: 22px;
line-height: 40px;
display: flex;
flex-flow: row nowrap;
align-items: center;
}
.title {
overflow: hidden;
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: space-between;
a {
font-size: 14px;
text-align: right;
}
}
}
.moop-info-body {
padding: 30px;
background: #fff;
.attachment {
list-style: none;
margin: 10px 0 0 0;
padding: 0;
.iconfont {
color: #29bd8b;
margin-right: 10px;
}
a {
display: flex;
flex-flow: row nowrap;
align-items: center;
color: rgba(0, 0, 0, 0.65);
span {
margin-right: 10px;
}
}
}
.sum {
width: 100%;
color: #999;
display: flex;
flex-flow: row nowrap;
align-items: center;
font-size: 14px;
span:first-child {
flex: 1 0 auto;
}
span {
margin-right: 15px;
}
}
.desc {
font-size: 16px;
flex: 1 0 auto;
.flex-container > span {
flex: 1 0 auto;
}
}
}
.moop-sub-title {
display: flex;
flex-flow: row nowrap;
align-items: center;
img {
border-radius: 25px;
margin-right: 15px;
}
margin-bottom: 10px;
}
.praise {
width: 70px;
height: 70px;
background-color: #4cacff;
border-radius: 50%;
color: #fff;
text-align: center;
margin: 20px auto;
cursor: pointer;
line-height: 22px;
border: 0px solid transparent;
outline: none;
display: block;
span {
display: block;
}
&:disabled {
background: #bcd1e3;
}
}

View File

@ -1,161 +0,0 @@
import React, { useReducer, useEffect } from 'react'
import { Spin, Button, Popconfirm } from 'antd'
import { useParams, Link, useHistory } from 'umi'
import { get, post, del } from '@/utils/fetch'
import { Tag } from '../index'
import Env from '@/utils/env'
import RenderHtml from '@/components/RenderHtml'
import './index.less'
import '../label.css'
interface State {
data: any,
}
const initialState: State = {
data: undefined,
}
export enum Types {
GET_DATA = 'get-data',
PRAISE = 'on-praise'
}
function reducer(state: State, action: any) {
switch (action.type) {
case Types.GET_DATA:
return { ...state, data: action.payload }
case Types.PRAISE:
return { ...state, data: action.payload }
default:
throw new Error()
}
}
export default () => {
const [state, dispatch] = useReducer(reducer, initialState)
const { data } = state
const { caseId } = useParams()
const history = useHistory()
useEffect(() => {
let didCancel = false
async function init() {
const response = await get(`libraries/${caseId}.json`)
if (!didCancel) {
if (response.status === 404) {
history.push('/moop_cases')
} else {
dispatch({
type: Types.GET_DATA,
payload: response,
})
}
}
}
if (!didCancel) {
init()
}
return () => {
didCancel = true
}
}, [caseId])
async function onPraise() {
await post(`praise_tread/like.json`, {
object_id: caseId,
object_type: "library"
})
let rs = { ...data }
rs.operation.user_praised = true
rs.praise_count += 1
dispatch({
type: Types.PRAISE,
payload: rs
})
}
async function onDeleteCase() {
await del(`/libraries/${caseId}.json`)
history.push('/moop_cases')
}
return (
<Spin spinning={!data} delay={500}>
{data ?
<section className='moop-info'>
<div className="crumb">
<Link to='/moop_cases'> &gt; </Link>
<span>{data.title}</span>
</div>
<div className="title">
<h2>
{data.title}
{data.tags?.map((tag: Tag, index: number) =>
<span className={'label-' + index} key={tag.id}>{tag.name}</span>
)}
</h2>
<Link to='/moop_cases'></Link>
</div>
<div className="moop-info-body">
<div className="moop-sub-title">
<img src={`${Env.IMG_SERVER}/images/${data.creator?.image_url}`} alt="cover" width={50} />
<div className="desc">
<div className="flex-container">
<span>{data.creator?.name}</span>
{data.operation?.can_editable ?
<Link to={`/moop_cases/${data.id}/edit`} style={{ marginRight: 10 }}>
<Button type='ghost'></Button>
</Link>
: null}
{data.operation?.can_deletable ?
<Popconfirm
placement="bottomRight"
title="是否确认删除?"
onConfirm={onDeleteCase}
>
<Button type='dashed'></Button>
</Popconfirm>
: null}
</div>
<div className="sum">
<span>{data.creator?.school_name}</span>
<span>{data.uuid}</span>
<span>{data.published_at}</span>
</div>
</div>
</div>
<p>{`${data.author_name} / ${data.author_school_name}`}</p>
<RenderHtml value={data.content} />
<ul className="attachment">
{
data.attachments?.map(
(item: any) =>
<li key={item.id}>
<a href={item.url}>
<i className="iconfont icon-fujian"></i>
<span>{item.title}</span>
<span>{item.filesize}</span>
</a>
</li>
)
}
</ul>
<button className="praise" disabled={data.operation?.user_praised} onClick={onPraise}>
{
data.operation?.user_praised ?
<span></span> :
<span>
<i className="iconfont icon-dianzan"></i>
</span>
}
<span>{data.praise_count}</span>
</button>
</div>
</section> : null}
</Spin>
)
}

View File

@ -1,28 +0,0 @@
.moop-case-success {
width: 1200px;
height: 570px;
margin: 20px auto 40px auto;
background: #fff;
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
text-align: center;
.notice {
margin: 20px auto;
color: #999;
font-size: 16px;
}
p {
font-size: 24px;
margin: 0;
font-weight: bold;
color: #333;
}
img {
margin-bottom: 20px;
}
}

View File

@ -1,23 +0,0 @@
import React from 'react'
import OkUrl from './ok.png'
import { Button } from 'antd'
import { Link, useParams } from 'umi'
import './index.less'
export default () => {
const { caseId } = useParams()
return (
<div className="moop-case-success">
<img src={OkUrl} alt="ok" width={100} />
<p></p>
<p></p>
<div className="notice">
</div>
<div>
<Button type='ghost' size='large' style={{ marginRight: 20 }} ><Link to={`/moop_cases/${caseId}`}></Link></Button>
<Button type='primary' size='large' ><Link to='/moop_cases/new'></Link></Button>
</div>
</div>
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,134 +0,0 @@
@import '../../global.less';
.moop-case {
// img {
// display: block;
// max-width: 100%;
// }
.banner {
height: 300px;
overflow: hidden;
.flex_box_container();
.flex_box_center();
img {
height: 300px;
}
}
.moop-title {
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: space-between;
background-color: #fff;
border-bottom: 1px solid #eeeeee;
padding: 20px 30px;
span {
color: #333;
font-size: 18px;
}
}
.moop-main {
width: 1200px;
margin: 20px auto;
}
.btn-ghost {
padding: 0px 10px;
color: #4cacff;
border: 1px solid #4cacff;
border-radius: 4px;
}
.moop-search {
width: 300px;
height: 40px;
}
.search-input-container {
padding: 20px 30px;
background-color: #fff;
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: space-between;
a {
color: #05101a;
margin-right: 30px;
line-height: 20px;
font-size: 16px;
&.active {
color: #4cacff;
}
}
}
}
.search-footer {
padding-bottom: 30px;
width: 1200px;
margin: 0 auto;
display: flex;
flex-flow: column nowrap;
justify-content: center;
text-align: center;
}
.case-list {
width: 1200px;
margin: 20px auto 0 auto;
list-style: none;
padding: 0;
display: flex;
flex-flow: column nowrap;
a {
display: flex;
flex-flow: row nowrap;
background: #fff;
padding: 20px 30px;
}
h2 {
font-size: 22px;
line-height: 40px;
display: flex;
flex-flow: row nowrap;
align-items: center;
}
img {
border-radius: 4px;
margin-right: 15px;
}
.info {
flex: 1 0 auto;
}
li {
margin-bottom: 20px;
}
.detail {
display: flex;
color: #333;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
line-height: 20px;
font-size: 14px;
}
.sum {
color: #ccc;
span {
margin-left: 20px;
}
}
}

View File

@ -1,236 +0,0 @@
import React, { useEffect, useReducer,FC } from 'react'
import { get } from '@/utils/fetch'
import './index.less'
import './label.css'
import { Spin, Input, Pagination } from 'antd'
import {
GlobalSettingModelState,
ConnectProps,
connect,
Link,
UserModelState,
} from 'umi';
import Env from '@/utils/env'
import { isLogin } from '@/utils/authority'
import bgUrl from './teach-case-bg.jpg';
const Search = Input.Search
interface PageProps extends Partial<ConnectProps> {
user: UserModelState;
globalSetting: GlobalSettingModelState;
}
type State = {
loading: boolean,
page: number,
count: number,
list: any,
type: string,
keyword: string,
}
export type Tag = { id: number, name: string }
const initialState: State = {
loading: false,
type: 'all',
page: 1,
count: 0,
list: [],
keyword: ''
}
export enum Types {
GET_LIST,
CHANGE_KEYWORD,
CHANGE_PAGE,
CHANGE_TYPE,
SET_LOADING
}
function reducer(state: State, action: any) {
switch (action.type) {
case Types.GET_LIST:
return { ...state, ...action.payload }
case Types.CHANGE_PAGE:
return { ...state, page: action.payload }
case Types.CHANGE_KEYWORD:
return { ...state, ...action.payload }
case Types.CHANGE_TYPE:
return { ...state, ...action.payload }
case Types.SET_LOADING:
return { ...state, loading: action.payload }
default:
throw new Error()
}
}
const MoopCases: FC<PageProps> = ({
user,
globalSetting,
dispatch
}) => {
// const MoopCases = () => {
const [state, setState] = useReducer(reducer, initialState)
const { loading, page, list, keyword, type, count } = state
//出现页面右侧悬浮按钮
useEffect(() => {
dispatch({
type: 'globalSetting/onlyShowBackTopToggle',
payload: false
});
return () => {
dispatch({
type: 'globalSetting/onlyShowBackTopToggle',
payload: true
});
}
}, [])
useEffect(() => {
let didCancel = false
async function init() {
setState({
type: Types.SET_LOADING,
payload: true,
})
const response = await get(`libraries.json`, {
keyword,
page,
type,
})
const { libraries, count } = response
if (!didCancel) {
setState({
type: Types.GET_LIST,
payload: {
count,
list: libraries,
loading: false,
},
})
}
}
if (!didCancel) {
init()
}
return () => {
didCancel = true
}
}, [page, keyword, type])
function onSetKeyword(value: string) {
setState({
type: Types.CHANGE_KEYWORD, payload: {
keyword: value,
page: 1,
}
})
}
function onChangeType(e: React.MouseEvent<HTMLElement>): void {
setState({ type: Types.CHANGE_TYPE, payload: { type: (e.target as any).id, page: 1 } })
}
function onPageChange(value: number) {
document.body.scrollIntoView()
setState({
type: Types.CHANGE_PAGE,
payload: value,
})
}
return (
<div className="moop-case">
<section className={"banner"}>
<img src={Env.IMG_SERVER + '/' + globalSetting?.setting?.moop_cases_banner_url} alt='bg' />
</section>
<section className='moop-main'>
<div className="moop-title">
<span></span>
<Link to='/moop_cases/new' className="btn-ghost"></Link>
</div>
<div className="search-input-container">
<div className="flex-container">
<a id='all' onClick={onChangeType} className={type === 'all' ? 'active' : ''}></a>
{isLogin() ?
<a id="mine" onClick={onChangeType} className={type === 'mine' ? 'active' : ''}></a> : null
}
</div>
<Search
className='moop-search'
defaultValue={keyword}
placeholder="输入教学案例标题、作者、单位进行检索"
onSearch={onSetKeyword}
></Search>
</div>
</section>
<Spin spinning={loading} delay={500}>
<CaseList data={list} />
</Spin>
<div className="search-footer">
{count > 20 ?
<Pagination
showQuickJumper
current={page}
onChange={onPageChange}
pageSize={20}
showSizeChanger={false}
total={count}
></Pagination> : null
}
</div>
</div>
)
}
function CaseList({ data }: { data: any }) {
return (
<ul className="case-list">
{
data.map((item: any) =>
<li key={item.id}>
<a href={`/moop_cases/${item.id}`} target='_blank'>
<img src={`${Env.IMG_SERVER}${item.cover_url}`} width={120} alt={item.title} />
<div className="info">
<h2>
{item.title}
{item.tags?.map((tag: Tag, index: number) =>
<span className={'label-' + index} key={tag.id}>{tag.name}</span>
)}
</h2>
<div className="detail">
<div className="flex-container">
<span>{`${item.author_name} ${item.author_school_name}`}</span>
</div>
<div className="sum">
<span> <i className="fa fa-eye"></i> {item.visited_count} </span>
<span> <i className="fa fa-thumbs-o-up"></i> {item.praise_count} </span>
<span> <i className="fa fa-download"></i> {item.download_count} </span>
</div>
</div>
</div>
</a>
</li>
)}
</ul>
)
}
export default connect(
({
user,
globalSetting,
}: {
user: UserModelState;
globalSetting: GlobalSettingModelState;
}) => ({
user,
globalSetting,
}),
)(MoopCases);

View File

@ -1,28 +0,0 @@
[class^='label-'] {
cursor: pointer;
line-height: 20px;
height: 20px;
padding: 0 9px;
width: 68px;
text-align: center;
border-radius: 9px;
font-size: 12px;
margin-left: 10px;
color: #fff;
}
.label-0 {
background: #fc2b6a;
}
.label-1 {
background: #ff781b;
}
.label-2 {
background: #4cacff;
}
.label-3 {
background: #36c53c;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

File diff suppressed because one or more lines are too long

View File

@ -52,9 +52,6 @@
</head>
<body>
<div style="position: absolute;top: -2000px;">
<img src="https://ali-cdn.educoder.net/images/avatars/LaboratorySetting/1nav?t=1609921874" title="头歌" />
</div>
<div id="root">
</div>
<div style="display: none;">

113
src/pages/metting/api.js Normal file
View File

@ -0,0 +1,113 @@
import http from './index'
import { config } from './info';
/**
* 获取基础信息
* @param {*} params
* @returns
*/
export function getBaseInfo(params) {
return http({
url: `/api/zone/open/zoneKey/${config.zoneKey}`,
method: 'get',
params
});
}
/**
* 获取会议议程主会场指定新闻列表
* @param {*} cateId 指定专栏id
* @param {*} params
* @returns
*/
export function getDocList(cateId, params) {
return http({
url: `/api/cms/doc/open/dir/${cateId}/docList`,
method: 'get',
params
});
}
/**
* 获取会议议程分会场指定新闻列表内容
* @param {*} cateId 指定专栏id
* @param {*} params
* @returns
*/
export function getSubAgenda(cateId, params) {
return http({
url: `/api/cms/doc/open/dir/${cateId}/docContentList`,
method: 'get',
params
});
}
/**
* 获取会议指南专区首页文章列表
* @returns
*/
export function gethomePageDocList() {
return http({
url: `/api/cms/doc/open/zone/${config.zoneId}/homePageDocList`,
method: 'get',
});
}
/**
* 获取与会嘉宾
* @returns
*/
export function getMemberList() {
return http({
url: `/api/zone/open/${config.zoneId}/member/homePageList`,
method: 'get',
});
}
/**
* 合作伙伴
* @returns
*/
export function getPartnerList() {
return http({
url: `/api/zone/open/${config.zoneId}/partners/list`,
method: 'get',
});
}
/**
* 获取组织机构
* @returns
*/
export function getOrganization(){
return http({
url:`/api/zone/open/${config.zoneId}/member/overviewList`,
method: 'get',
})
}
/**
* 获取文章详情
* @param {*} id 文章id
* @returns
*/
export function getDocDetail(id){
return http({
url:`/api/cms/doc/open/${id}`,
method: 'get',
})
}
/**
* 获取所有栏目列表
* @returns
*/
export function getDirList(){
return http({
url:`/api/cms/doc/open/zone/${config.zoneId}/dirList`,
method: 'get',
})
}

View File

@ -0,0 +1,21 @@
import './index.less';
import MenuBanner from './menu';
import SubBanner from '../img/subBanner.png';
import { useModel } from 'umi';
function Banner(props) {
const { mainInfo } = useModel('defaultConfig');
return(
<div className='mainroot'>
<div className='mainBanner'>
<div className="header">
<img src={mainInfo?.bannerList} />
<div className='bannerContent'><MenuBanner /></div>
</div>
</div>
{props.children}
</div>
)
}
export default Banner;

View File

@ -0,0 +1,50 @@
.bannerContent{
position: absolute;
bottom: -41px;
width: 100%;
height: 76px;
.banners{
width: 1200px;
margin:0px auto;
height: 100%;
background-color: #fff;
padding:0px 40px;
border-radius: 12px;
display: flex;
align-items: center;
box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.09);
a{
img{
margin-right: 18px;
}
font-weight: bold;
font-size: 16px;
color: #252B3A;
padding-right: 100px;
&.active{
color: #6233DE;
}
}
}
}
.mainroot{
background: url('../img/back.png') no-repeat;
background-size: 100% 100%;
.mainBanner{
padding-bottom: 41px;
width: 100%;
.header {
width: 100%;
text-align: center;
color: #ffffff;
position: relative;
font-size: 15px;
width: 100%;
&>img{
width: 100%;
max-height: 300px;
object-fit: cover;
}
}
}
}

View File

@ -0,0 +1,30 @@
import './index.less';
import { useState , useEffect } from 'react';
import {useLocation , Link} from 'umi';
import { menu } from '../info';
function Menu(props) {
const location = useLocation();
const matchItem = (item) => {
if(location.pathname.indexOf("/track/")>-1){
return item.indexOf("/track/") > -1
}else{
return item === location.pathname
}
}
return(
<ul className='banners'>
{
menu && menu?.length > 0 && menu.map((i,k)=>{
return(
<Link to={i.link} key={k} className={`${matchItem(i.link) ? `active` : ``}`}><img src={require(`../img/logo${k+1}${matchItem(i.link) ? "-color":""}.png`)} />{i.name}</Link>
)
})
}
</ul>
)
}
export default Menu;

View File

@ -0,0 +1,212 @@
import { useRef, useState, useEffect } from 'react';
import {
Link , useModel
}from 'umi';
import './index.less';
import { getBaseInfo, getPartnerList , gethomePageDocList , getDocDetail } from '../api.js';
import Left from '../img/left.png';
import Right from '../img/right.png';
import Arrow from '../img/arrow.png';
import ArrowPurple from '../img/arrowPurple.png';
import Diamond from '../img/liu.png';
import Number1 from '../img/number1.png';
import Number2 from '../img/number2.png';
import Number3 from '../img/number3.png';
import Team from '../img/team.png';
import trackBack from '../img/track.png';
import trackBack2 from '../img/track2.png';
import Dynamics from '../img/dynamics.png';
import Dynamicsicon from '../img/dynamicsicon.png';
function Home(){
const [ partner, setPartner ] = useState([]);
const [newsList, setNewsList] = useState([]);
const [ visits , setVisits ] = useState(0);
const { mainInfo } = useModel('defaultConfig');
useEffect(()=>{
getPartner();
getNewsList();
getVisit();
},[])
//
const getVisit=()=>{
getDocDetail(656).then(res=>{
res && setVisits(res?.data?.visits);
}).catch(console.error())
}
//
const getPartner = () => {
getPartnerList().then(res => {
setPartner(res.rows);
}).catch(console.error())
}
//
const getNewsList = () => {
gethomePageDocList().then(res => {
setNewsList(res.rows)
})
}
return(
<div className="page headpage">
{/* <div className="banner">
<img src={ Banner } alt=""/>
</div> */}
<div className='part'>
<p className='homeMaintitle'><img src={Left} /><span>赛事简介</span><img src={Right} /></p>
<p className="homeSubtitle"><span>EVENT INTRODUCTION</span></p>
<div className='homeDesc'>{mainInfo.introductionContent}</div>
{/* <a className='applicationBtn hoverBtn'>立即报名<img src={Arrow} width={19}/></a> */}
<ul className='numbersData'>
<li>
<span className='number'><em>{visits}</em></span>
<div className='line'><img src={Diamond} alt="" width={23} /></div>
<span className='btn'>浏览次数</span>
</li>
<li>
<span className='number'><em>0</em></span>
<div className='line'><img src={Diamond} alt="" width={23} /></div>
<span className='btn'>报名人数</span>
</li>
<li>
<span className='number'><em>1000,000</em></span>
<div className='line'><img src={Diamond} alt="" width={23} /></div>
<span className='btn'>奖金总数</span>
</li>
</ul>
</div>
<div className='part'>
<p className='homeMaintitle'><img src={Left} /><span>大赛流程</span><img src={Right} /></p>
<p className="homeSubtitle"><span>COMPETITION PROCESS</span></p>
<ul className='team'>
<li>
<p className='t-title'><img src={Team} width={33}/><span>热身赛</span><img src={Number1} width={80}/></p>
<p className='t-desc'>正式比赛前的预备赛事帮助选手熟悉流程和参赛项目</p>
<p className='t-time'>2024 5月16日-6月30日</p>
</li>
<li>
<p className='t-title'><img src={Team} width={33}/><span> </span><img src={Number2} width={97}/></p>
<p className='t-desc'>完成整体赛事后的线上选拔出三等奖及入围一二等奖的优秀团队</p>
<p className='t-time'>2024 7月1日-8月31日</p>
</li>
<li>
<p className='t-title'><img src={Team} width={33}/><span> </span><img src={Number3} width={95}/></p>
<p className='t-desc'>通过线下的现场演示/答辩最终决出一二等奖的胜者</p>
<p className='t-time'>2024 9月1日-9月20日</p>
</li>
</ul>
</div>
<div className='part'>
<p className='homeMaintitle'><img src={Left} /><span>大赛赛道</span><img src={Right} /></p>
<p className="homeSubtitle"><span>COMPETITION TRACK</span></p>
<div className='track'>
<li>
<img src={trackBack} alt="" className='backimg'/>
<div className='trackContent'>
<div className='flex1'>
<p className='tracktitle'><span>贡献</span>开源项目贡献赛道</p>
<p style={{opacity:"0.71"}}>参赛队伍解决开源项目疑修提交代码创新应用并移植工具凭贡献获奖</p>
</div>
<a className='applicationBtn' href='/competitions/2024/track/633'>查看详情<img src={ArrowPurple} width={19}/></a>
<span className='money'>¥ 69,000</span>
</div>
</li>
<li>
<img src={trackBack2} alt="" className='backimg'/>
<div className='trackContent'>
<div className='flex1'>
<p className='tracktitle'><span>挑战</span>开源任务挑战赛道</p>
<p style={{opacity:"0.71"}}>根据指定任务汇聚优秀算法和解决方案可持续优化更新结合性能和效果评奖</p>
</div>
<a className='applicationBtn' href='/competitions/2024/track/634'>查看详情<img src={ArrowPurple} width={19}/></a>
<span className='money'>¥ 69,000</span>
</div>
</li>
<li>
<img src={trackBack2} alt="" className='backimg'/>
<div className='trackContent'>
<div className='flex1'>
<p className='tracktitle'><span>教学</span>开源案例教学赛道</p>
<p style={{opacity:"0.71"}}>在线汇聚高校实践教学案例助力实践教学创新依据案例质量与应用成效评奖</p>
</div>
<a className='applicationBtn' href='/competitions/2024/track/635'>查看详情<img src={ArrowPurple} width={19}/></a>
<span className='money'>¥ 69,000</span>
</div>
</li>
<li>
<img src={trackBack} alt="" className='backimg'/>
<div className='trackContent'>
<div className='flex1'>
<p className='tracktitle'><span>评注</span>开源代码评注赛道</p>
<p style={{opacity:"0.71"}}>研读开源项目代码撰写技术评注代码注释及缺陷报告按数量质量及贡献评奖</p>
</div>
<a className='applicationBtn' href='/competitions/2024/track/636'>查看详情<img src={ArrowPurple} width={19}/></a>
<span className='money'>¥ 69,000</span>
</div>
</li>
</div>
</div>
<div className='part'>
<p className='homeMaintitle'><img src={Left} /><span>赛事动态</span><img src={Right} /></p>
<p className="homeSubtitle"><span>EVENT DYNAMICS</span></p>
{
newsList && newsList.length > 0 &&
<div className='dynamics'>
<ul className='dynamicsNews'>
{ newsList.map((i, k) => {
return k < 3 ?
<li>
<p className='uper'><img src={Dynamicsicon} width={26}/>{ i.name }</p>
<p className='under'>
<span>{i.publishTime}</span>
<Link to={`/competitions/2024/information/${ i.id }`} className='applicationBtn'>查看详情<img src={Arrow} width={19}/></Link>
</p>
</li>
: ''
})}
</ul>
<div className='dynamicsImg'>
<img src={Dynamics} alt="" />
</div>
</div>
}
<a className='applicationBtn hoverBtn' href="/competitions/2024/information" style={{marginTop:32}}>更多资讯<img src={Arrow} width={19}/></a>
</div>
<div className='part'>
<p className='homeMaintitle'><img src={Left} /><span>合作伙伴</span><img src={Right} /></p>
<p className="homeSubtitle"><span>PARTNERS</span></p>
<div className="partner-content">
{
partner.map((e, k) => {
return (
e.zonePartnersList.length > 0 && e.typeName.indexOf("高校") === -1 ?
<div className="partner-type" key={ k }>
<div className="type-title">
<span>{ e.typeName }</span>
</div>
<div className="type-content">
{
e.zonePartnersList.map((i, subK) => {
return <a key={ subK } href={ i.link } target="_blank">
<div>
<img src={ i.logo } alt=""/>
</div>
</a>
})
}
</div>
</div>
:''
)
})
}
</div>
</div>
</div>
)
}
export default Home;

View File

@ -0,0 +1,427 @@
.page.headpage {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.banner {
height: 100vh;
width: 100%;
position: relative;
img {
width: 100%;
height: 100%;
// object-fit: cover;
}
}
.part{
text-align: center;
width: 1200px;
}
.homeMaintitle{
font-size: 48px;
position: relative;
margin:54px auto 0px;
width: 224px;
img{
width: 12px;
position: absolute;
&:first-child{
left: -12px;
}
&:last-child{
bottom: 0px;
}
}
span{
margin:0px 22px;
font-family: YouSheBiaoTiHei;
}
text-align: center;
}
.homeSubtitle{
margin-top: 12px;
text-align: center;
span{
font-size: 16px;
font-family: YouSheBiaoTiHei;
background: -webkit-linear-gradient(76deg, rgba(24,163,168,0.77) 0%, #2E89FF 51%, rgba(158,3,127,0.77) 100%);
background: linear-gradient(76deg, rgba(24,163,168,0.77) 0%, #2E89FF 51%, rgba(158,3,127,0.77) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 针对WebKit浏览器 */
color: transparent; /* 兼容其他浏览器 */
letter-spacing: 2px;
font-weight: 400;
}
}
.homeDesc{
width: 1200px;
font-weight: 400;
font-size: 16px;
color: #040E29;
line-height: 40px;
letter-spacing: 1px;
margin-top: 34px;
text-align: left;
text-indent: 2em;
}
.applicationBtn{
margin-top:20px;
height: 44px;
line-height: 44px;
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
border-radius: 5px 5px 5px 5px;
padding:0px 24px;
color: #fff;
font-weight: 400;
font-size: 20px;
display: inline-block;
img{
margin:0px 0px 4px 15px;
}
}
.numbersData{
display: flex;
padding:50px 0px;
position: relative;
&::before{
content: "";
height: 3px;
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
left: calc(1200px / 6 + 10px);
right: calc(1200px / 6 - 10px);
position: absolute;
top:50%;
margin-top: 3.5px;
}
li{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
span.number{
font-family: YouSheBiaoTiHei;
font-weight: 400;
font-size: 28px;
color: #040E29;
line-height: 40px;
em{
font-size: 50px;
background: -webkit-linear-gradient(103deg, #5950F3 0%, #6B16CA 100%);
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 针对WebKit浏览器 */
color: transparent; /* 兼容其他浏览器 */
margin-right:12px;
padding:0px 10px;
font-style: normal;
}
}
span.btn{
width: 140px;
height: 50px;
line-height: 46px;
background: #F6F7FA;
box-shadow: 0px 0px 10px 1px rgba(7,70,165,0.11);
border-radius: 9px 9px 9px 9px;
border: 2px solid #FFFFFF;
font-weight: bold;
font-size: 20px;
color: #040E29;
text-align: center;
}
&>.line{
margin:40px 0px 27px;
width: 100%;
position: relative;
text-align: center;
height: 20px;
img{
position: absolute;
margin-left: -12px;
}
}
}
}
.team{
display: flex;
margin-top: 67px;
width: 100%;
margin-bottom: 10px;
li{
height: 198px;
padding:20px 30px;
box-sizing: border-box;
background: #F6F7FA;
box-shadow: 0px 0px 10px 1px rgba(7,70,165,0.11);
border-radius: 22px 22px 22px 22px;
border: 2px solid #FFFFFF;
flex: 1;
width: 0;
margin-right: 80px;
transition: 0.2s;
&:last-child{
margin-right: 0px;
}
&:hover{
transform: translateY(-8px);
}
.t-title{
display: flex;
align-items: center;
margin-bottom: 10px;
span{
display: block;
margin-right: auto;
font-weight: bold;
font-size: 20px;
color: #040E29;
line-height: 40px;
}
}
.t-desc{
font-weight: 400;
font-size: 15px;
color: #040E29;
line-height: 26px;
text-align: left;
margin-bottom: 18px;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.t-time{
font-weight: 400;
font-size: 15px;
color: #4C2795;
text-align: left;
}
}
}
.track{
margin-top: 18px;
display: flex;
flex-wrap: wrap;
width: 1200px;
margin-bottom: 42px;
li{
list-style-type: none;
height: 175px;
margin: 39px 40px 0px 0px;
background: linear-gradient( 92deg, #9356E4 0%, #5D3E9A 100%);
border-radius: 12px;
box-sizing: border-box;
color: #fff;
text-align: left;
width:calc( 50% - 20px);
font-weight: 400;
font-size: 15px;
color: #FFFFFF;
line-height: 28px;
position: relative;
.backimg{
position: absolute;
right: 0;
height: 100%;
z-index: 0;
width: 80%;
}
&:nth-child(2n){
margin-right: 0px;
}
&:nth-child(2){
background: linear-gradient( 92deg, #4779EC 0%, #11317A 100%);
}
&:nth-child(3){
background: linear-gradient( 92deg, #088592 0%, #0D569C 100%);
}
&:nth-child(4){
background: linear-gradient( 92deg, #736CF0 0%, #3C1E85 100%);
}
.trackContent{
display: flex;
align-items: center;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
padding:28px 27px 28px 39px ;
.money{
font-weight: 400;
font-size: 36px;
color: #FFFFFF;
line-height: 40px;
height: 40px;
letter-spacing: 2px;
font-family:'Alimama ShuHeiTi';
width: 194px;
text-align: right;
}
.tracktitle{
font-weight: 400;
font-size: 22px;
color: #FFFFFF;
line-height: 38px;
height: 38px;
margin-bottom: 22px;
span{
font-weight: bold;
font-size: 26px;
}
}
.applicationBtn{
background: #fff;
color: #6233DE;
margin-left: 32px;
display: none;
margin-top: 0px;
}
&:hover{
.applicationBtn{display: block;}
.money{display: none;}
}
}
}
}
.flex1{
flex: 1;
}
.dynamics{
display: flex;
max-width: 1200px;
margin-top:60px;
justify-content: space-between;
.dynamicsNews{
width: 40%;
display: flex;
flex-direction: column;
justify-content: space-between;
li{
background: #F6F7FA;
box-shadow: 0px 0px 10px 1px rgba(7,70,165,0.11);
border-radius: 13px;
border: 2px solid #FFFFFF;
text-align: left;
padding:22px 12px 18px 38px;
.uper{
font-weight: 400;
font-size: 16px;
color: #040E29;
line-height: 26px;
height: 48px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
img{
margin-right: 10px;
float: left;
margin-top:3px ;
}
}
.under{
display: flex;
align-items: center;
font-weight: 400;
font-size: 14px;
color: #040E29;
height: 20px;
margin-bottom: 0px;
.applicationBtn{
height: 36px;
line-height: 36px;
font-weight: 400;
font-size: 15px;
padding:0px 11px;
margin-left: auto;
margin-top: 0px;
opacity: 0;
transition: 0.8s;
img{
margin-bottom: 2px;
}
}
}
&:hover .applicationBtn{
opacity: 1;
}
}
}
.dynamicsImg{
width: calc( 60% - 32px);
img{
width: 100%;
object-fit: cover;
}
border-radius: 2px;
}
}
.partner-content {
margin-top: 10px;
margin-bottom: 50px;
.partner-type {
width:fit-content;
.type-title {
line-height: 28px;
font-weight: bold;
font-size: 20px;
color: #151313;
letter-spacing: 5px;
margin-bottom: 30px;
text-align: left;
span{
position: relative;
&::before{
position: absolute;
left: 0px;
height: 2px;
width: 32px;
content: "";
bottom:-7px;
background: linear-gradient( 90deg, #5374B4 0%, #853CB8 100%);
}
}
}
.type-content {
display: flex;
flex-wrap: wrap;
div {
margin-right: 42px;
padding: 6px;
background-color:#ffffff;
border-radius:4px;
box-shadow:0px 0px 6px rgba(28, 48, 175, 0.03);
height: 83px;
width: 163px;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 35px;
border:1px solid #fff;
&:hover{
border-color: #6233DE;
}
}
a{
&:nth-child(6n) div{
margin-right: 0px;
}
}
img {
max-height: 55px;
max-width: 130px;
}
}
}
}
.hoverBtn:hover{
opacity: 0.77;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,41 @@
import axios from 'axios';
import { message , notification } from 'antd';
function beforeFetch(){
if (window.location.href.indexOf('localhost') < 0) {
axios.defaults.withCredentials = true;
}
const service = axios.create({
baseURL: 'https://gateway.gitlink.org.cn',
timeout: 1800000, // 请求超时时间
});
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.data;
}
},
error => {
console.log(error);
// notification.open({
// message: "提示",
// description: error.message,
// });
// return Promise.reject(error);
}
)
return service;
}
const service = beforeFetch();
export default service;

54
src/pages/metting/info.js Normal file
View File

@ -0,0 +1,54 @@
export const menu = [
{
name: '首页',
link: '/competitions/2024',
},
{
name: '组织架构',
link: '/competitions/2024/organization',
},
{
name: '赛事安排',
link: '/competitions/2024/track/633',
},
{
name: '赛事动态',
link: '/competitions/2024/information',
}
]
export const defaultPage = {
'/register': 300,
'/contact': 299
}
export const information = {
title: '大会资讯',
desc: '方便您第一时间收集各会议论坛信息,解读大会通知资讯',
category: [
{
title: '赛事安排',
eng: 'Event arrangement',
anchor: 'forum',
},
{
title: '赛事动态',
eng: 'Event dynamics',
anchor: 'guide',
},
{
title: '赛题发布',
eng: 'Competition question release',
anchor: 'notice',
}
]
}
export const config = {
// zoneKey: 'kydhgw',
// zoneId: 6,
zoneKey:"KYDS7th",
zoneId:11
}
export const hidenMemberType = ['与会嘉宾', '院士嘉宾']

View File

@ -0,0 +1,68 @@
import './index.less'
import RenderHtml from '@/components/RenderHtml'
import { useLocation, useParams } from 'umi';
import { Base64 } from 'js-base64';
import { Breadcrumb , Divider } from 'antd';
import views from '../img/views.png'
import { useEffect } from 'react';
import { getDocDetail } from '../api'
import { useState } from 'react';
import { defaultPage } from '../info'
function Detail() {
const location = useLocation();
const { id } = location.pathname.includes('information') ? useParams() : { id: defaultPage[location.pathname] };
const [ detail, setDetail ] = useState({});
const [ cmsDir , setCmsDir ] = useState(undefined);
const [ content , setContent]=useState(undefined);
useEffect(()=>{
getDetail();
},[id])
const getDetail = () => {
getDocDetail(id).then(res => {
setDetail(res.data);
setCmsDir(res.data.cmsDir);
setContent(Base64.decode(res.data.content));
})
}
return <div className="detail">
{
detail &&
<div className="detail-content">
{
location.pathname.includes('information') && <Breadcrumb separator=">">
{ cmsDir && <Breadcrumb.Item href="/competitions/2024/information">{ cmsDir.name }</Breadcrumb.Item> }
<Breadcrumb.Item>正文</Breadcrumb.Item>
</Breadcrumb>
}
<div className="detail-info">
{
location.pathname.includes('information') && <>
<div className="info-text-main">
<p className="info-name">{ detail.name }</p>
<ul className="info-ul-value">
{ cmsDir && <li>发布于{ cmsDir.createdAt }</li> }
{ detail.visits && <li><img src={ views } alt="" className="mr5" />{ detail.visits }</li> }
</ul>
</div>
<Divider dashed={true} />
</>
}
{
content ?
<RenderHtml className="informations_detail imageLayerParent" value={ content } url={ location } />
:
<span>暂无详情~</span>
}
</div>
</div>
}
</div>
}
export default Detail

View File

@ -0,0 +1,192 @@
import './index.less'
import { Spin, Divider , Input } from 'antd';
import { useState , useEffect } from 'react';
import { information } from '../info';
import { getDocList } from '../api'
import empty from '../img/empty.png';
import { useModel , Link } from 'umi';
import Left from '../img/left.png';
import Right from '../img/right.png';
import SubBanner from '../img/subBanner.png';
import Search from '../img/search.png';
import { throttle } from '@/utils/mettingUtil';
function Information() {
const [ isSpin , setIsSpin ] = useState(false);
const [ informationList , setInformationList ] = useState([]);
const { dirList } = useModel('defaultConfig');
const [ tabKey , setTabKey ] = useState(0);
const [ positionNum ,setPositionNum ] = useState([]);
const [ showAnchor , setShowAnchor ] = useState(false);
const[ title ,setTitle] = useState(undefined);
const[ searchTitle ,setSearchTitle] = useState(undefined);
useEffect(()=>{
if (location.pathname === '/competitions/2024/information') {
window.addEventListener('scroll', throttleHandleScroll);
return ComponentWillUnmount;
} else {
ComponentWillUnmount()
}
},[location.pathname,positionNum])
function ComponentWillUnmount() {
window.removeEventListener("scroll", throttleHandleScroll);
}
function handleScroll() {
let scrollTop = document.documentElement.scrollTop; //
setShowAnchor(scrollTop > 362);
if(scrollTop < 362) setTabKey(0);
if(positionNum && positionNum.length>0){
for(let i=0;i<positionNum.length;i++){
if(i>0 && scrollTop <= positionNum[i] && scrollTop > positionNum[i-1] ){
setTabKey(i);
return;
}
}
}
}
const throttleHandleScroll = throttle(handleScroll, 100)
useEffect(()=>{
if ( JSON.stringify(dirList) !== '{}' ) {
getAllLists();
}
let anchor = document.getElementsByClassName("anchor");
if(anchor && anchor.length>1){
let arr = [];
for(let i=0;i<anchor.length;i++){
arr.push(anchor[i].getBoundingClientRect().top)
}
setPositionNum(arr);
}
},[dirList,searchTitle])
const getAllLists = () => {
setIsSpin(true);
const requestList = [];
information.category.map(e => {
dirList[e.title] && requestList.push(getList(dirList[e.title], e.anchor,searchTitle))
})
Promise.all(requestList).then(res => {
let totalList = {}
res.map(e => {
totalList[e.name] = e.list
})
setInformationList(totalList)
setIsSpin(false)
})
}
const getList = (id, name,title) => {
return new Promise((resolve, reject) => {
getDocList(id).then(res => {
let list = res.rows;
list = title ? list.filter(i=>i.name.indexOf(title)>-1 || i.summary.indexOf(title)>-1) : list;
list.forEach(e => {
if (e.publishTime) {
let time = e.publishTime.split('/');
e.date1 = `${time[0]}${time[1]}`;
e.date2 = `${time[2]}`;
} else {
e.date1 = e.date2 = '-'
}
})
resolve({ name, list: list });
})
})
}
const scrollToAnchor = (anchorName) => {
if (anchorName) {
let anchorElement = document.getElementById(anchorName);
if(anchorElement) { anchorElement.scrollIntoView({block: 'start', behavior: 'smooth'}); }
}
}
return <div className="information">
{/* {
information.category &&
<div className="tab">
<div className='tabPanel'>
{
information.category.map((e, k) => {
return <div key={ k } onClick={() => { scrollToAnchor(e.anchor);setTabKey(k) }} className={`tab-item ${k===tabKey?"active":""}`}>{ e.title }</div>
})
}
<Input
className="searchInput"
value={ title }
// onBlur={ (e) => { setTitle(e.target.value) } }
onChange={ (e) => { setTitle(e.target.value) } }
onPressEnter={()=>{setSearchTitle(title)}}
placeholder="请搜索文章标题关键词"
suffix={<img src={Search} width={16}/>}
style={{width:"213px"}}
/>
</div>
</div>
} */}
<div className="tabPanel">
<Input
className="searchInput"
value={ title }
// onBlur={ (e) => { setTitle(e.target.value) } }
onChange={ (e) => { setTitle(e.target.value) } }
onPressEnter={()=>{setSearchTitle(title)}}
placeholder="请搜索文章标题关键词"
suffix={<img src={Search} width={16}/>}
style={{width:"213px"}}
/>
</div>
{
information.category && showAnchor &&
<div className='anchorPart' style={{top:362,left:(document.documentElement.clientWidth-1200)/2-125}}>
{
information.category.map((e,k)=>{
return <li key={ k } className={`${k===tabKey?"active":""}`} onClick={() => { scrollToAnchor(e.anchor);}}>{ e.title }</li>
})
}
</div>
}
<Spin spinning={isSpin}>
{
information.category.map((e) => {
return <div className="content" key={ e.anchor }>
<div className="anchor" id={ e.anchor }></div>
{/* <div className="content-title">
<p>{ e.title }</p>
<p>{ e.eng }</p>
</div> */}
<div style={{textAlign:"center",paddingTop:18,paddingBottom:50}}>
<p className='homeMaintitle'><img src={Left} /><span>{ e.title }</span><img src={Right} /></p>
<p className="homeSubtitle"><span>{ e.eng.toUpperCase() }</span></p>
</div>
<div className="content-wrapper">
{
informationList[e.anchor] && informationList[e.anchor].length > 0 ? informationList[e.anchor].map(( e, k ) => {
return <div className="content-item" key={ k }>
<div className="item-left">{ e.date2 }</div>
<div className="item-right">
<p className="time">{ e.date1 }</p>
<Divider style={{ color: 'rgba(9, 37, 77, 0.1)', margin: '0px' }}/>
<Link to={ `/competitions/2024/information/${ e.id }` } className="item-title task-hide">{ e.name }</Link>
<p className="item-desc task-hide-2">{ e.summary }</p>
<a href={ `/competitions/2024/information/${ e.id }` } className="read-more">阅读全文 <span></span></a>
</div>
</div>
}):
<img className="empty" src={ empty } alt="" />
}
</div>
</div>
})
}
</Spin>
</div>
}
export default Information

View File

@ -0,0 +1,280 @@
.information {
.anchorPart{
width: 115px;
background-color: #fff;
position: fixed;
box-shadow: 0px 0px 12px 1px rgba(107,136,204,0.06);
border-radius: 4px 4px 4px 4px;
z-index: 11;
padding:12px 0px;
li{
position: relative;
list-style-type: none;
padding:0px 15px;
height: 45px;
line-height: 45px;
font-weight: 400;
font-size: 15px;
color: #1F2329;
cursor: pointer;
&.active{
color: #6233DE;
}
&.active::after{
width: 2px;
height: 29px;
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
border-radius: 0px 0px 0px 0px;
left: 0px;
content: "";
top: 8px;
position: absolute;
}
}
}
.homeMaintitle{
font-size: 48px;
position: relative;
margin: 0px auto;
width: 224px;
img{
width: 12px;
position: absolute;
&:first-child{
left: -12px;
}
&:last-child{
bottom: 0px;
}
}
span{
margin:0px 22px;
font-family: YouSheBiaoTiHei;
}
text-align: center;
}
.ant-spin-container {
background: url('../img/back.png') no-repeat;
background-size: 100% 100%;
}
.header {
width: 100%;
height: 300px;
text-align: center;
color: #ffffff;
position: relative;
font-size: 15px;
width: 100%;
&>img{
width: 100%;
height: 300px;
object-fit: cover;
}
// .title {
// font-size: 30px;
// }
// .desc {
// margin-top: 19px;
// opacity: 80%;
// }
}
.tab {
display: flex;
width: 100%;
height:62px;
background-color:#ffffff;
}
.tabPanel{
width: 1200px;
display: flex;
justify-content: center;
align-items: center;
margin:35px auto 0px;
position: relative;
height: 32px;
.tab-item {
margin: 0 69px;
cursor: pointer;
height:62px;
line-height:62px;
position: relative;
font-size: 16px;
&.active::after{
position: absolute;
width: 67px;
height: 2px;
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
border-radius: 0px 0px 0px 0px;
content: "";
left: 50%;
margin-left: -33px;
bottom: 0px;
}
&.active,&:hover{
color: #6233DE;
}
}
.searchInput{
position: absolute;
right: 0px;
background-color: #fff;
&:hover,&.ant-input-affix-wrapper:focus{
border-color: #6233DE!important;
box-shadow: 0 0 0 2px rgba(98, 51, 222, 0.1)!important;
}
}
}
.anchor {
position: absolute;
top: -50px;
}
.content {
width: 1200px;
margin: auto;
position: relative;
padding:50px 0px;
.content-title {
width: 531px;
height: 157px;
background: url('../img/info-title-bg.png') no-repeat;
background-size: 100% 100%;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
p:first-child {
font-family: Alimama ShuHeiTi;
color: #0e1015;
font-size: 40px;
margin-top: 60px;
}
p:last-child {
font-family:PingFang SC;
color: #0d41c5;
font-size: 15px;
margin-top: 14px;
}
}
.content-wrapper {
.content-item {
display: flex;
margin-bottom: 10px;
.item-left {
font-family: PingFang SC;
font-weight: 700;
color: #6233DE;
font-size: 24px;
margin-right: 30px;
flex-shrink: 0;
}
.item-right {
flex: 1;
.time {
color: #5F6577;
font-size: 14px;
margin-bottom: 10px;
}
.item-title {
font-weight: 700;
color: #252b3a;
font-size: 18px;
margin-top: 20px;
display: block;
&:hover{
color: #6233DE;
}
}
.item-desc {
color: #49454F;
font-size: 16px;
line-height: 32px;
margin-top: 20px;
white-space: pre-wrap;
}
.read-more {
float: right;
color: #6233DE;
font-size: 15px;
margin-top: 10px;
display: flex;
span{
transition: 0.2s;
display: block;
}
&:hover>span {
transform: translateX(3px);
}
}
}
}
}
}
}
.detail{
background: url('../img/information-detail-bg.png') no-repeat;
background-size: 100% 100%;
width: 100%;
flex-grow: 1;
padding-bottom: 60px;
.detail-content {
width: 1200px;
margin: 0px auto;
padding: 30px 0px;
}
.ant-breadcrumb {
a {
color: #0d41c5;
}
}
.detail-info{
background-color:#fefefe;
border-radius:4px;
box-shadow:0px 0px 15px rgba(30, 47, 162, 0.06);
padding:30px 35px 50px;
margin-top: 28px;
.info-text-main{
.info-name{
color:#1f2329;
font-size:22px;
line-height: 30px;
margin-bottom: 14px!important;
word-break: break-all;
}
.info-ul-value{
display: flex;
align-items: center;
li{
color:#4c5876;
height: 20px;
line-height: 20px;
margin-right: 25px;
display: flex;
align-items: center;
img {
margin-right: 5px;
}
}
}
}
}
}
.homeSubtitle{
margin-top: 12px;
text-align: center;
span{
font-size: 16px;
font-family: YouSheBiaoTiHei;
background: -webkit-linear-gradient(76deg, rgba(24,163,168,0.77) 0%, #2E89FF 51%, rgba(158,3,127,0.77) 100%);
background: linear-gradient(76deg, rgba(24,163,168,0.77) 0%, #2E89FF 51%, rgba(158,3,127,0.77) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 针对WebKit浏览器 */
color: transparent; /* 兼容其他浏览器 */
letter-spacing: 2px;
font-weight: 400;
}
}

View File

@ -0,0 +1,21 @@
import { Tooltip } from 'antd'
import './index.less'
function MemberItem(props) {
const { className, data } = props
return <div className={ `${ className } member-item` }>
{
data.name && <>
<img src={ data.imageUrl } alt="" className="head-img"/>
<span className="task-hide">{ data.name }</span>
<Tooltip title={ data.introduction }>
<p className="task-hide-2">{ data.introduction }</p>
</Tooltip>
<div className="line"></div>
</>
}
</div>
}
export default MemberItem

View File

@ -0,0 +1,45 @@
.member-item {
width: 200px;
height: 315px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
border-bottom: 1.5px solid linear-gradient(90deg,#08428e 0%,#019765 100%);
margin-bottom: 35px;
.head-img {
width: 200px;
height: 250px;
border: 2px solid #ffffff;
object-fit: cover;
background-color: #B6B6BB;
}
span {
position: absolute;
font-weight: bold;
color: #ffffff;
font-size: 18px;
bottom: 78px;
left: -40px;
width:84px;
line-height: 29px;
background-image:url('../img/orz1.png');
background-size: 100% 100%;
text-align: center;
padding: 0 10px;
}
p {
margin-top: 15px;
color: #252b3a;
font-size: 15px;
line-height: 1.5;
text-align: center;
}
.line {
position: absolute;
bottom: -5px;
width: 100%;
height: 1.5px;
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
}
}

View File

@ -0,0 +1,149 @@
import './index.less'
import { Spin } from 'antd';
import React,{ useState , useEffect } from 'react';
import MemberItem from '../memberitem'
import { getOrganization , getPartnerList , getDocList , getDocDetail } from '../api.js';
import { hidenMemberType } from '../info';
import { Base64 } from 'js-base64';
import RenderHtml from '@/components/RenderHtml'
import orz2 from '../img/orz2.png';
import Logo from '../img/logo.png';
import Team from '../img/team.png';
import Left from '../img/left.png';
import Right from '../img/right.png';
function Organization() {
const [ isSpin , setIsSpin ] = useState(false);
const [ organization, setOrganization ] = useState([])
const [ partner, setPartner ] = useState([]);
const [ orz , setOrz ] = useState([]);
useEffect(()=>{
getList();
getPartner();
Init();
},[])
const getPartner = () => {
getPartnerList().then(res => {
let rows = res?.rows.length > 0 ? res?.rows.filter(i=>i.typeName.includes("高校")):[];
setPartner(rows);
}).catch(console.error())
}
const getList = () => {
setIsSpin(true);
getOrganization().then(res => {
let rows = res?.rows;
rows = rows.map(i=>{
let members= i.zoneMemberList && i.zoneMemberList.length>0 ? i.zoneMemberList.filter(e=>e.memberLevel !== "主席"):i.zoneMemberList;
return {...i,unPresident:members.length>0?members.map(e=>{return e.name}):[]}
})
setOrganization(rows);
setIsSpin(false);
})
}
const Init=()=>{
getDocList(658).then(res => {
if( res?.rows?.length > 0){
let arr = [];
for(let i=0;i<res?.rows.length;i++){
let e = res?.rows[i];
arr.push(getDetailForId(e.id));
}
Promise.all(arr).then(res=>{
setOrz(res)
})
}
})
}
const getDetailForId = (id) => {
return new Promise((resolve, reject) => {
getDocDetail(id).then(res => {
let detail = res.data;
resolve({detail:detail});
})
})
}
return <div className="organization">
<div className='orzLogo'>
<img src={Logo} alt="" height={84}/>
<div className='orzDesc'>
<img src={orz2} alt="" width={138}/>
<div className='descCon'>
<img src={Team} width={33}/>CCF开源发展委员会是一个致力于推动开源文化技术教育和产业发展的专业组织重点聚焦共同打造开源开放中立的产学研协同开源创新服务平台探索建立CCF开源项目孵化机制培育原始开源创新项目依托CCF联接科教资源产业资源和社会资源等形成产研联动的开源创新模式推动CCF开源品牌建设和开源社区运营为CCF会员乃至全球开源创新实践者提供高水平的开源创新服务助力我国开源生态建设的发展
</div>
</div>
</div>
<p className='homeMaintitle' style={{margin:"50px auto 0px"}}><img src={Left} /><span>组织架构</span><img src={Right} /></p>
<p className="homeSubtitle"><span>ORGANIZARIONAL STRUCTURE</span></p>
<Spin spinning={isSpin}>
{/* {
organization && organization.length > 0 && organization.map((i, k) => {
if (!hidenMemberType.includes(i.typeName)) {
return <div className="module" key={ k }>
<p className='homeMaintitle'><img src={Left} /><span>{ i.typeName }</span><img src={Right} /></p>
<p className="homeSubtitle"><span>{subtitle[k]}</span></p>
<p className="module-desc">{ i.typeIntroduction }</p>
{
i.zoneMemberList && i.zoneMemberList.length > 0 && <div className="module-content">
{
i.zoneMemberList.map(( e, subK ) => {
return e.memberLevel === "主席" && <MemberItem key={ subK } className={`module-item`} data={ e } ></MemberItem>
})
}
</div>
}
{
i.unPresident && i.unPresident.length>0 && <p className='membersSub'><img src={Team} width={33} />{i.typeName}委员{ i.unPresident.join("、")}</p>
}
</div>
}
})
} */}
{
orz && orz.length > 0 && orz.map((i,k)=>{
let detail = i.detail;
return(
<>
<p className='orzTitle'><img src={Team} width={33} style={{marginRight:13}}/>{detail.name}</p>
<div className="module" key={ k }>
{
detail.content ?
<RenderHtml className="informations_detail imageLayerParent" value={ Base64.decode(detail.content) } url={ location } />
:
""
}
</div>
</>
)
})
}
</Spin>
{
partner?.length > 0 ?
<div style={{textAlign:"center",paddingTop:68,paddingBottom:50}}>
<p className='homeMaintitle' style={{width:224,margin:"0px auto"}}><img src={Left} /><span>合作高校</span><img src={Right} /></p>
<p className="homeSubtitle"><span>PARTNERS</span></p>
<div className='partnersLogo'>
{
partner.map((i,k)=>{
return i.zonePartnersList && i.zonePartnersList.length >0 ? i.zonePartnersList.map((e,key)=>{
return(
<a key={k}><img src={e.logo} alt=""/></a>
)
})
:""
})
}
</div>
</div>
:""
}
</div>
}
export default Organization

View File

@ -0,0 +1,203 @@
.organization {
width: 100%;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
position: relative;
padding-bottom: 60px;
.ant-spin-nested-loading {
width: 1200px;
}
.homeMaintitle{
font-size: 48px;
position: relative;
margin-bottom: 0px;
img{
width: 12px;
position: absolute;
&:first-child{
left: -12px;
}
&:last-child{
bottom: 0px;
}
}
span{
margin:0px 22px;
font-family: YouSheBiaoTiHei;
}
text-align: center;
}
.homeSubtitle{
margin-top: 12px;
text-align: center;
span{
font-size: 16px;
font-family: YouSheBiaoTiHei;
background: -webkit-linear-gradient(76deg, rgba(24,163,168,0.77) 0%, #2E89FF 51%, rgba(158,3,127,0.77) 100%);
background: linear-gradient(76deg, rgba(24,163,168,0.77) 0%, #2E89FF 51%, rgba(158,3,127,0.77) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 针对WebKit浏览器 */
color: transparent; /* 兼容其他浏览器 */
letter-spacing: 2px;
font-weight: 400;
}
}
.back-img {
width: 100%;
img{
width: 100%;
height: 300px;
object-fit: cover;
}
}
.orzLogo{
padding-top:48px;
text-align: center;
.orzDesc{
position: relative;
width: 1200px;
min-height: 170px;
margin-top: 30px;
&>img{
position: absolute;
left: 0px;
top:-18px;
z-index: 1;
}
.descCon{
position: absolute;
background: #F6F7FA;
box-shadow: 0px 0px 10px 1px rgba(7,70,165,0.11);
border-radius: 10px 10px 10px 10px;
border: 2px solid #FFFFFF;
padding:20px 50px 20px 110px;
line-height: 34px;
text-align: left;
left: 50px;
text-align: justify;
font-size: 15px;
color: #040E29;
img{
float: left;
margin-right: 8px;
margin-top: 3px;
}
}
}
}
.orzTitle{
font-family: YouSheBiaoTiHei;
font-weight: 400;
font-size: 38px;
color: #0A0A0A;
letter-spacing: 3px;
margin-bottom: 0px;
}
.module {
display: flex;
flex-direction: column;
margin-top: 24px;
background: #F6F7FA;
box-shadow: 0px 0px 10px 1px rgba(7,70,165,0.11);
border-radius: 22px;
padding:20px 40px;
margin-bottom: 35px;
border: 2px solid #FFFFFF;
.module-title {
display: flex;
align-items: center;
font-family:Alimama ShuHeiTi;
color:#0e1015;
font-size:34px;
margin-bottom: 30px;
span {
margin: 0 30px;
position: relative;
&::after {
content: "";
position: absolute;
bottom: -3px;
left: -10px;
opacity: 9%;
width: 100%;
height: 11px;
background-color: #0d41c5;
}
}
img {
width: 48px;
height: 21px;
&:last-child {
transform: rotate(180deg);
}
}
}
.module-desc {
color: #252b3a;
font-size: 15px;
margin-bottom: 37px;
line-height: 1.8;
}
.module-content {
display: flex;
flex-wrap: wrap;
.module-item {
margin-right: 55px;
margin-left: 55px;
margin-bottom: 32px;
&:nth-child(4n) {
margin-right: 0px;
}
}
}
}
.ant-carousel{
width: 1200px;
}
.membersSub{
width: 100%;
font-weight: bold;
font-size: 20px;
color: #040E29;
line-height: 40px;
img{
float: left;
margin: 5px 10px 0px 0px;
}
}
.partnersLogo{
display: flex!important;
flex-wrap: wrap;
margin-top: 54px;
width: 1200px;
a{
margin-right: 42px;
padding: 6px;
background-color: #ffffff;
border-radius: 4px;
box-shadow: 0px 0px 6px rgba(28, 48, 175, 0.03);
height: 83px;
width: 163px;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 35px;
border:1px solid #fff;
&:hover{
border-color: #6233DE;
}
&:nth-child(6n){
margin-right: 0px;
}
img{
max-height: 55px;
max-width: 130px;
}
}
}
}

View File

@ -0,0 +1,112 @@
import './index.less'
import { useState , useEffect } from 'react';
import RenderHtml from '@/components/RenderHtml';
import { useParams , Link } from 'umi';
import { getDocDetail } from '../api';
import { Base64 } from 'js-base64';
import { throttle } from '@/utils/mettingUtil';
function Track(props) {
const [ tabKey , setTabKey ] = useState(0);
const [ content , setContent]=useState(undefined);
const [ dealContent , setDealContent]=useState(undefined);
const [ showAnchor , setShowAnchor ] = useState(false);
const [ anchor , setAnchor ] = useState(false);
const [ menu , setMenu]=useState([]);
const { id } = useParams();
useEffect(()=>{
if (location.pathname.indexOf('/competitions/2024/track')>-1) {
window.addEventListener('scroll', throttleHandleScroll);
return ComponentWillUnmount;
} else {
ComponentWillUnmount()
}
},[location.pathname])
function ComponentWillUnmount() {
window.removeEventListener("scroll", throttleHandleScroll);
}
function handleScroll() {
let scrollTop = document.documentElement.scrollTop; //
setShowAnchor(scrollTop > 320);
if(scrollTop < 320){setAnchor("")}
}
const throttleHandleScroll = throttle(handleScroll, 100)
useEffect(()=>{
if(id){
setTabKey(id);
getDetail();
}
},[id])
const getDetail = () => {
getDocDetail(id).then(res => {
let content = Base64.decode(res.data.content);
setContent(content);
})
}
const scrollToAnchor = (anchorName) => {
if (anchorName) {
let anchorElement = document.getElementById(anchorName);
if(anchorElement) { setAnchor(anchorName);anchorElement.scrollIntoView({block: 'start', behavior: 'smooth'}); }
}
}
useEffect(() => {
if(content){
let c = content;
const hTagsRegex = /<h([1-6])(.*?)>(.*?)<\/h\1>/g;
let match;
let list = [];
while ((match = hTagsRegex.exec(content))) {
list.push({level:+match[1],id:match[3]})
c = c.replace(`${match[0]}`, `<div id="${match[3]}"></div>${match[0]}`);
}
setMenu(list);
setDealContent(c);
}
}, [content]);
return(
<div className='track'>
<div className='tabPanel'>
<div className="tab">
<Link key={ 633 } to='/competitions/2024/track/633' className={`tab-item ${tabKey === "633" ?"active":""}`}>开源项目贡献赛道</Link>
<Link key={ 634 } to='/competitions/2024/track/634' className={`tab-item ${tabKey === "634" ?"active":""}`}>开源任务挑战赛道</Link>
<Link key={ 635 } to='/competitions/2024/track/635' className={`tab-item ${tabKey === "635" ?"active":""}`}>开源案例教学赛道</Link>
<Link key={ 636 } to='/competitions/2024/track/636' className={`tab-item ${tabKey === "636" ?"active":""}`}>开源代码评注赛道</Link>
</div>
</div>
<div className="detail-info">
{/* <div className="info-text-main">
<p className="info-name">{ detail.name }</p>
<ul className="info-ul-value">
{ cmsDir && <li>发布于{ cmsDir.createdAt }</li> }
{ detail && detail.visits && <li><img src={ views } alt="" className="mr5" />{ detail.visits }</li> }
</ul>
</div>
<Divider dashed={true} /> */}
{
menu?.length > 0 && showAnchor ?
<ul className='menuPanel' style={{top:320,left:(document.documentElement.clientWidth-1200)/2-225}}>
{
menu.map((i,k)=>{
return <li key={k} style={{paddingLeft:`${i.level*10}px`}} className={`${anchor ===i.id?"active":""}`}><a onClick={()=>scrollToAnchor(i.id)}>{i?.id}</a></li>
})
}
</ul>
:""
}
{
dealContent ?
<RenderHtml className="informations_detail imageLayerParent" value={ dealContent } url={ location } />
:
<span>暂无详情~</span>
}
</div>
</div>
)
}
export default Track;

View File

@ -0,0 +1,103 @@
.track{
padding-bottom: 40px;
position: relative;
.tabPanel{
text-align: center;
padding:35px 0px 0px;
}
.tab {
width: 1200px;
margin:0px auto;
display: flex;
align-items: center;
padding-left:40px;
.tab-item {
margin-right: 78px;
font-weight: 400;
font-size: 17px;
cursor: pointer;
position: relative;
color: #040E29;
border-radius: 4px;
padding:0px 10px;
height: 32px;
line-height: 32px;
&.active{
color: #fff;
background: linear-gradient( 103deg, #5950F3 0%, #6B16CA 100%);
}
}
}
.detail-info{
width: 1200px;
border-radius:4px;
box-shadow:0px 0px 15px rgba(30, 47, 162, 0.06);
padding:30px 35px 50px;
margin:28px auto 0px;
background-color: #fff;
.menuPanel{
position: fixed;
background-color: #fff;
border-radius: 4px;
width: 200px;
max-height: 320px;
overflow-y: auto;
padding:12px 0px;
box-shadow: 0px 0px 12px 1px rgba(107, 136, 204, 0.06);
li{
position: relative;
&.active::before{
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 2px;
content: "";
background: linear-gradient(103deg, #5950F3 0%, #6B16CA 100%);
}
&.active,&:hover{
a{
color: #6B16CA;
}
}
a{
font-size: 14px;
height: 28px;
line-height: 28px;
font-weight: 400;
font-size: 15px;
color: #1F2329;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 145px;
display: block;
}
}
}
.info-text-main{
.info-name{
color:#1f2329;
font-size:22px;
line-height: 30px;
margin-bottom: 14px!important;
word-break: break-all;
}
.info-ul-value{
display: flex;
align-items: center;
li{
color:#4c5876;
height: 20px;
line-height: 20px;
margin-right: 25px;
display: flex;
align-items: center;
img {
margin-right: 5px;
}
}
}
}
}
}

View File

@ -172,8 +172,6 @@ export default function request(url: string, option: any, flag?: boolean) {
Authorization:token
}
}
console.log("1:",url);
console.log("2:",newOptions.headers);
const expirys = options.expirys && 60
/**

View File

@ -72,7 +72,7 @@ export function getTocContent() {
const tokenizer = {
heading(src: string) {
const cap = headingRegex.exec(src)
const cap = headingRegex.exec(src);
if (cap) {
return {
type: 'heading',

13
src/utils/mettingUtil.js Normal file
View File

@ -0,0 +1,13 @@
export function throttle(fn, delay){
let timer = null; // 使用定时器实现
return function (){
let _this = this; // 当前 this 保存,以免后续处理中 this 丢失
if(!timer){
fn.apply(_this, arguments);
timer = setTimeout(function (){
timer = null;
}, delay);
}
}
}

View File

@ -4682,6 +4682,15 @@ aws4@^1.8.0:
resolved "https://registry.npmmirror.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3"
integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==
axios@^1.6.8:
version "1.6.8"
resolved "https://registry.npmmirror.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-core@7.0.0-bridge.0:
version "7.0.0-bridge.0"
resolved "https://registry.npmmirror.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
@ -8001,7 +8010,7 @@ flv.js@1.5.0:
es6-promise "^4.2.5"
webworkify "^1.5.0"
follow-redirects@^1.0.0:
follow-redirects@^1.0.0, follow-redirects@^1.15.6:
version "1.15.6"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
@ -8070,6 +8079,15 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.npmmirror.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@ -13395,6 +13413,11 @@ proxy-addr@~2.0.5, proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"