diff --git a/public/locales/en/rank.json b/public/locales/en/rank.json
index fe973ab..a103d8b 100644
--- a/public/locales/en/rank.json
+++ b/public/locales/en/rank.json
@@ -44,5 +44,18 @@
"nginx": "A free, open-source, lightweight, high-performance web server developed by Igor Sysoev for Rambler.ru, the second most visited site in Russia.",
"openresty": "A web platform based on Nginx that can run Lua scripts using its LuaJIT engine, created by Yichun Zhang."
}
+ },
+ "contribution": {
+ "title": "Contribution Ranking",
+ "nav": "{{month}} {{year}} Contribution Ranking",
+ "p_text": "HelloGitHub Contribution Ranking is a ranking based on the contribution value of users in the HelloGitHub open source community, updated with the current month's data on the 15th of each month. Users can earn contribution points in the following ways: sharing an open source project earns 5 points, posting a comment earns 2 points, and if a comment is selected as a hot comment, an additional 10 contribution points will be awarded.",
+ "thead": {
+ "position": "Rank",
+ "name": "User",
+ "rating": "Level",
+ "change": "Change",
+ "total": "Total",
+ "md_change": "📊"
+ }
}
}
diff --git a/public/locales/zh/rank.json b/public/locales/zh/rank.json
index 67ae8e7..b8c9c8f 100644
--- a/public/locales/zh/rank.json
+++ b/public/locales/zh/rank.json
@@ -44,5 +44,18 @@
"nginx": "免费开源、轻量级、高性能 Web 服务器,由伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.ru 站点开发。",
"openresty": "一个基于 Nginx 的 Web 平台,可以使用其 LuaJIT 引擎运行 Lua 脚本,由章亦春创建。"
}
+ },
+ "contribution": {
+ "title": "用户贡献排名",
+ "nav": "{{year}} 年 {{month}} 月用户贡献排行榜",
+ "p_text": "「HelloGitHub 贡献排名」是根据用户在 HelloGitHub 开源社区中的贡献值进行排名,每月 15 日更新当月的数据。用户可通过以下方式获得贡献值:分享开源项目可获得 5 点,发表评论获得 2 点,若评论被选为热评,则可额外获得 10 点贡献值。",
+ "thead": {
+ "position": "排名",
+ "name": "用户",
+ "rating": "等级",
+ "change": "对比上月",
+ "total": "总贡献",
+ "md_change": "本月"
+ }
}
}
diff --git a/src/components/rankTable/RankTable.tsx b/src/components/rankTable/RankTable.tsx
index 5c1c655..3953735 100644
--- a/src/components/rankTable/RankTable.tsx
+++ b/src/components/rankTable/RankTable.tsx
@@ -66,7 +66,7 @@ export const RankTable = ({ columns, list, i18n_lang }: TableProps) => {
return (
{content}
|
@@ -114,11 +114,13 @@ export const RankSearchBar = ({
return i18n_lang == 'en'
? [
{ key: '/report/tiobe', value: 'Language' },
+ { key: '/report/contribution', value: 'Contribution' },
{ key: '/report/netcraft', value: 'Server' },
{ key: '/report/db-engines', value: 'Database' },
]
: [
{ key: '/report/tiobe', value: '编程语言' },
+ { key: '/report/contribution', value: '用户贡献' },
{ key: '/report/netcraft', value: '服务器' },
{ key: '/report/db-engines', value: '数据库' },
];
diff --git a/src/components/report/Report.tsx b/src/components/report/Report.tsx
index 5cd2573..5932803 100644
--- a/src/components/report/Report.tsx
+++ b/src/components/report/Report.tsx
@@ -1,6 +1,8 @@
import { AiFillCaretDown, AiFillCaretUp } from 'react-icons/ai';
import { IoMdRemove, IoMdTrendingDown, IoMdTrendingUp } from 'react-icons/io';
+import { NoPrefetchLink } from '../links/CustomLink';
+
import { RankDataItem } from '@/types/rank';
export const ChangeColumnRender = (
@@ -54,3 +56,55 @@ export const TrendColumnRender = (
}
return {icon};
};
+
+export const ContributionColumnRender = (
+ row: RankDataItem,
+ i18n_lang: string
+) => {
+ let text = '-';
+ if (row.change !== null) {
+ text = `+${row.change}`;
+ } else {
+ return {text};
+ }
+
+ return (
+
+ {i18n_lang === 'en' ? (
+ {text}
+ ) : (
+ {text}
+ )}
+
+ );
+};
+
+export const UserColumnRender = (row: RankDataItem) => {
+ return (
+
+
+
+
{row.name}
+
+
+ );
+};
+
+export const PositionColumnRender = (row: RankDataItem) => {
+ const positionList = ['🥇', '🥈', '🥉'];
+ return (
+
+ {row.position > 3 ? (
+ {row.position}
+ ) : (
+ {positionList[row.position - 1]}
+ )}
+
+ );
+};
diff --git a/src/pages/report/contribution.tsx b/src/pages/report/contribution.tsx
new file mode 100644
index 0000000..cdb477c
--- /dev/null
+++ b/src/pages/report/contribution.tsx
@@ -0,0 +1,179 @@
+import { GetServerSideProps, NextPage } from 'next';
+import { useRouter } from 'next/router';
+import { Trans, useTranslation } from 'next-i18next';
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
+import { useMemo } from 'react';
+
+import Loading from '@/components/loading/Loading';
+import Navbar from '@/components/navbar/Navbar';
+import {
+ getMonthName,
+ RankSearchBar,
+ RankTable,
+} from '@/components/rankTable/RankTable';
+import {
+ ContributionColumnRender,
+ PositionColumnRender,
+ UserColumnRender,
+} from '@/components/report/Report';
+import Seo from '@/components/Seo';
+
+import { getContributionRank } from '@/services/rank';
+import { getClientIP } from '@/utils/util';
+
+import { RankPageProps } from '@/types/rank';
+
+const ContributionPage: NextPage = ({
+ year,
+ month,
+ monthList,
+ list,
+}) => {
+ const { t, i18n } = useTranslation('rank');
+ const router = useRouter();
+
+ const onSearch = (key: string, value: string) => {
+ if (key === 'month') {
+ router.push(`/report/contribution/?month=${value}`);
+ }
+ if (key === 'target') {
+ router.push(`${value}`);
+ }
+ };
+
+ // 排名 用户 等级 贡献值 对比上月
+ const columns: any[] = useMemo(
+ () => [
+ {
+ key: 'position',
+ title: t('contribution.thead.position'),
+ render: PositionColumnRender,
+ width: 80,
+ },
+ {
+ key: 'name',
+ title: t('contribution.thead.name'),
+ render: UserColumnRender,
+ width: 180,
+ },
+ { key: 'rating', title: t('contribution.thead.rating') },
+ {
+ key: 'change',
+ title: t('contribution.thead.change'),
+ render: ContributionColumnRender,
+ },
+ {
+ key: 'total',
+ title: t('contribution.thead.total'),
+ },
+ ],
+ [i18n.language]
+ );
+
+ // 排名 用户 本月 贡献值
+ const md_columns: any[] = useMemo(
+ () =>
+ columns
+ .map((col) => {
+ if (col.key === 'position') {
+ return { ...col, width: 60 };
+ }
+ if (col.key === 'name') {
+ return { ...col, width: 140 };
+ }
+ if (col.key === 'change') {
+ return {
+ ...col,
+ title: t('contribution.thead.md_change'),
+ render: ContributionColumnRender,
+ width: 80,
+ };
+ }
+ if (col.key === 'rating') {
+ return null;
+ }
+ return col;
+ })
+ .filter(Boolean),
+ [i18n.language]
+ );
+
+ return (
+ <>
+
+ {list ? (
+
+ ) : (
+
+ )}
+ >
+ );
+};
+
+export const getServerSideProps: GetServerSideProps = async ({
+ query,
+ req,
+ locale,
+}) => {
+ const ip = getClientIP(req);
+ const data = await getContributionRank(
+ ip,
+ query['month'] as unknown as number
+ );
+ if (!data.success) {
+ return {
+ notFound: true,
+ };
+ } else {
+ return {
+ props: {
+ year: data.year,
+ month: data.month,
+ list: data.data,
+ monthList: data.month_list,
+ ...(await serverSideTranslations(locale as string, ['common', 'rank'])),
+ },
+ };
+ }
+};
+
+export default ContributionPage;
diff --git a/src/pages/report/db-engines.tsx b/src/pages/report/db-engines.tsx
index e512a60..d26affa 100644
--- a/src/pages/report/db-engines.tsx
+++ b/src/pages/report/db-engines.tsx
@@ -65,6 +65,9 @@ const DBEnginesPage: NextPage = ({
if (col.key === 'position') {
return { ...col, width: 60 };
}
+ if (col.key === 'name') {
+ return { ...col, width: 160 };
+ }
if (col.key === 'rating') {
return { ...col, width: 80 };
}
diff --git a/src/services/rank.ts b/src/services/rank.ts
index 9fa9f97..1fd7fb8 100644
--- a/src/services/rank.ts
+++ b/src/services/rank.ts
@@ -63,3 +63,23 @@ export const getDBRank = async (
return {} as RankData;
}
};
+
+// 贡献值排名
+export const getContributionRank = async (
+ ip: string,
+ month?: number
+): Promise => {
+ const req: RequestInit = {};
+ req.headers = { 'x-real-ip': ip, 'x-forwarded-for': ip };
+
+ let url = '/report/contribution/';
+ if (month) {
+ url = `${url}?month=${month}`;
+ }
+ try {
+ const data = await fetcher(makeUrl(url), req);
+ return data;
+ } catch (error) {
+ return {} as RankData;
+ }
+};
diff --git a/src/types/rank.ts b/src/types/rank.ts
index ced16b9..9599bc6 100644
--- a/src/types/rank.ts
+++ b/src/types/rank.ts
@@ -7,6 +7,8 @@ export interface RankDataItem {
star?: string;
percent?: boolean;
total?: number;
+ avatar?: string;
+ uid?: string;
}
export interface RankData {