Compare commits

..

22 Commits

Author SHA1 Message Date
HaroldSu 00af580dce feat: .devops/进阶1.yml 2025-06-19 16:14:51 +08:00
HaroldSu 761a84dcf0 Update index.tsx 2025-06-03 16:32:58 +08:00
HaroldSu ea82106336 Merge pull request '1' (#3) from 赖胜杨 into master 2025-06-03 16:17:28 +08:00
苏宁韧 d8e5763717 1 2025-06-03 16:16:02 +08:00
HaroldSu dd53337ce4 Merge pull request '修改' (#2) from 赖胜杨 into master 2025-06-02 18:30:07 +08:00
苏宁韧 26ea40c099 修改 2025-06-02 18:28:15 +08:00
HaroldSu 63d96c2f73 refactor: .devops/未命名项目.yml 2025-06-01 10:00:22 +08:00
HaroldSu 0f50aa86d4 refactor: .devops/未命名项目.yml 2025-06-01 09:53:08 +08:00
HaroldSu 954a8bd324 refactor: .devops/未命名项目.yml 2025-06-01 09:53:07 +08:00
HaroldSu ee7f883434 refactor: .devops/未命名项目.yml 2025-06-01 09:49:04 +08:00
HaroldSu 7187dae7cc refactor: .devops/未命名项目.yml 2025-06-01 09:49:02 +08:00
HaroldSu 19cf692b0c refactor: .devops/未命名项目.yml 2025-06-01 09:49:00 +08:00
HaroldSu 9005c8b471 refactor: .devops/未命名项目.yml 2025-06-01 09:46:36 +08:00
HaroldSu 5502ca1e1b refactor: .devops/未命名项目.yml 2025-06-01 09:24:50 +08:00
HaroldSu 0702a96a75 refactor: .devops/未命名项目.yml 2025-06-01 09:24:49 +08:00
HaroldSu ab2224345b refactor: .devops/未命名项目.yml 2025-06-01 08:40:11 +08:00
HaroldSu f09bfd7b56 refactor: .devops/未命名项目.yml 2025-06-01 08:39:15 +08:00
HaroldSu 590b76d71b Update 代码评审.md 2025-05-27 15:16:28 +08:00
HaroldSu dd605f7e27 Update 创建合并请求.md 2025-05-27 10:16:01 +08:00
HaroldSu ebd1a2c618 Update 创建合并请求.md 2025-05-27 10:15:43 +08:00
HaroldSu 9e8c47b653 Update 创建合并请求.md 2025-05-27 10:12:02 +08:00
HaroldSu 44e7136883 Update 创建合并请求.md 2025-05-27 10:10:58 +08:00
65 changed files with 27957 additions and 14110 deletions

View File

@ -7,6 +7,10 @@ trigger:
webhook: gitlink@1.0.0
event:
- ref: push
ruleset:
- param-ref: branch
operator: EQ
value: '"master"'
ruleset-operator: AND
workflow:
- ref: start
@ -16,9 +20,9 @@ workflow:
name: git clone
task: git_clone@1.2.9
input:
username: ((ganshihao21.ganshihao21_user))
password: ((ganshihao21.ganshihao21))
remote_url: '"https://gitlink.org.cn/ganshihao21/reposync.git"'
username: ((Harold.Harold))
password: ((Harold.Harold_key))
remote_url: '"https://gitlink.org.cn/HaroldSu/gitlink_help_center.git"'
ref: '"refs/heads/master"'
commit_id: '""'
depth: 1
@ -28,11 +32,11 @@ workflow:
name: docker镜像构建
task: docker_image_build@1.6.0
input:
docker_username: ((nudt_devops.nudt_devops))
docker_password: ((nudt_devops.nudt_devops))
image_name: '"registry.cn-guangzhou.aliyuncs.com/nudt_devops/reposync_group5"'
docker_username: ((Harold.Ali_Harold12))
docker_password: ((Harold.Ali_Harold12_key))
image_name: '"crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com/harold12/gitlink"'
image_tag: '"latest"'
registry_address: '"registry.cn-guangzhou.aliyuncs.com"'
registry_address: '"crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com"'
docker_file: '"Dockerfile"'
docker_build_path: '"."'
workspace: git_clone_0.git_path
@ -44,14 +48,15 @@ workflow:
name: ssh执行命令
task: ssh_cmd@1.1.1
input:
ssh_pass: ((ssh_key.ssh_key))
ssh_ip: '"47.96.164.145"'
ssh_pass: ((Harold.Harold_ssh_key))
ssh_ip: '"47.98.140.99"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_cmd: '"docker stop abc_group5 && docker rm abc_group5 && docker pull
registry.cn-guangzhou.aliyuncs.com/nudt_devops/reposync_group5:latest &&
docker run -d -p 3000:3000 --name abc_group5
registry.cn-guangzhou.aliyuncs.com/nudt_devops/reposync_group5:latest"'
ssh_cmd: '"docker stop gitlink || true && docker rm gitlink || true && docker
pull
crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com/harold12/gitlink:latest;
docker run -d -p 3000:3000 --name gitlink
crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com/harold12/gitlink:latest"'
needs:
- docker_image_build_0
- ref: end

58
.devops/进阶1.yml Normal file
View File

@ -0,0 +1,58 @@
version: 2
name: 进阶1
description: ""
global:
concurrent: 1
trigger:
webhook: gitlink@1.0.0
event:
- ref: push
ruleset-operator: AND
workflow:
- ref: start
name: 开始
task: start
- ref: docker_image_build_0
name: docker镜像构建
task: docker_image_build@1.6.0
input:
docker_username: ((wangxin22.docker_user))
docker_password: ((wangxin22.docker_pswd))
image_name: '"crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com/harold12/reposyncer"'
image_tag: '"latest"'
registry_address: '"crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com"'
docker_file: '"Dockerfile"'
docker_build_path: '"."'
workspace: git_clone_0.git_path
image_push: true
build_args: '""'
needs:
- git_clone_0
- ref: git_clone_0
name: git clone
task: git_clone@1.2.9
input:
username: ((Harold.Harold))
password: ((Harold.Harold_key))
remote_url: '"https://www.gitlink.org.cn/HaroldSu/reposync.git"'
ref: '"refs/heads/master"'
commit_id: '""'
depth: 1
needs:
- start
- ref: ssh_cmd_0
name: ssh执行命令
task: ssh_cmd@1.1.1
input:
ssh_pass: ((Harold.Harold_ssh_key))
ssh_ip: '"47.98.140.99"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_cmd: '"docker stop reposyncer ; docker rm reposyncer ; docker pull crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com/harold12/reposyncer:latest ; docker run -d -p 8000:8000 --name reposyncer crpi-8qmkfh15gc3yl0m3.cn-hangzhou.personal.cr.aliyuncs.com/harold12/reposyncer:latest bash start.sh -s backend"'
needs:
- docker_image_build_0
- ref: end
name: 结束
task: end
needs:
- ssh_cmd_0

View File

@ -84,8 +84,7 @@
![gitlink logo](/img/gitlink_logo.png)
```
初始化项目
node v20.x npm install --force
## 前端build成中文 i18n中可编辑对应中文内容
```
npm run build -- --locale zh-CN

View File

@ -69,25 +69,6 @@ GitLink确实开源是CCF官方指定的开源创新服务平台旨在
<h2 class="text--truncate cardTitle" title="第三方服务">第三方服务</h2>
<p>第三方服务使用及设置[3个文档]</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/通知/通知简介">
<h2 class="text--truncate cardTitle" title="通知">通知</h2>
<p>通知简介及设置[2个文档]</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/个人主页建站/建站流程">
<h2 class="text--truncate cardTitle" title="个人主页建站">个人主页建站</h2>
<p>个人主页建站使用及设置[2个文档]</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/平台公告">
<h2 class="text--truncate cardTitle" title="平台公告">平台公告</h2>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/服务协议/GitLink服务协议">
<h2 class="text--truncate cardTitle" title="服务协议">服务协议</h2>
<p>GitLink服务协议[1个文档]</p>
</a></article>
</section>
</div>
</div>

View File

@ -6,8 +6,8 @@ sidebar_position: 3
# 代码评审
### **1. 进入代码评审**
如下图所示,点击“代码评审”按钮可以进入代码评审
![](../../static/img/PR/进入代码评审.png)<br/>
![](/img/PR/进入代码评审.png)<br/>
### **2. 代码评审界面**
如下图所示用户可以在该界面下查看该PR操作要合并的新的代码并对其进行评审。
![](../../static/img/PR/具体代码评审.png)<br/>
![](/img/PR/具体代码评审.png)<br/>

View File

@ -0,0 +1,4 @@
{
"label": "团队协作",
"position": 8
}

View File

@ -0,0 +1,104 @@
---
sidebar_label: '看板管理'
sidebar_position: 1
---
# 看板管理
看板管理是 GitLink 提供的团队协作功能,帮助团队更好地管理和跟踪任务进度。通过可视化的看板界面,团队成员可以清晰地了解任务状态,提高协作效率。
## 功能特点
1. 多维度任务管理
- 支持按项目、里程碑、标签等维度组织任务
- 自定义任务状态列
- 灵活的任务分类和筛选
2. 任务卡片
- 显示任务标题、描述、负责人、截止日期等关键信息
- 支持任务优先级标记
- 关联代码提交和 Pull Request
- 任务评论和讨论
3. 拖拽操作
- 支持任务卡片在不同状态列之间拖拽
- 支持任务卡片排序
- 批量操作任务
4. 数据统计
- 任务完成情况统计
- 团队成员工作量统计
- 任务周期分析
- 自定义统计报表
## 使用指南
### 1. 创建看板
1. 进入项目页面,点击"看板"标签
2. 点击"新建看板"按钮
3. 设置看板名称和描述
4. 选择看板类型(项目看板/里程碑看板)
5. 配置任务状态列
### 2. 管理任务
1. 添加任务
- 点击"新建任务"按钮
- 填写任务信息(标题、描述、负责人、截止日期等)
- 设置任务优先级和标签
- 关联相关代码提交或 Pull Request
2. 更新任务状态
- 拖拽任务卡片到目标状态列
- 点击任务卡片编辑详细信息
- 添加任务评论和讨论
3. 任务筛选和搜索
- 使用筛选器按状态、负责人、标签等筛选任务
- 使用搜索框快速查找任务
- 保存常用筛选条件
### 3. 查看统计
1. 进入看板统计页面
2. 选择统计时间范围
3. 查看任务完成情况、工作量分布等统计信息
4. 导出统计报表
## 最佳实践
1. 任务管理
- 保持任务描述清晰具体
- 及时更新任务状态
- 合理设置任务优先级
- 定期清理已完成任务
2. 团队协作
- 及时响应任务评论
- 定期同步任务进度
- 合理分配任务工作量
- 保持看板整洁有序
3. 数据统计
- 定期查看统计报表
- 分析团队工作效率
- 优化工作流程
- 调整任务分配策略
## 注意事项
1. 权限控制
- 看板创建者可以设置访问权限
- 任务负责人可以更新任务状态
- 项目管理员可以管理所有任务
2. 数据同步
- 任务状态变更实时同步
- 支持离线操作
- 自动保存编辑内容
3. 性能优化
- 大量任务时使用分页加载
- 支持任务数据导出
- 定期清理历史数据

View File

@ -15,6 +15,8 @@ sidebar_position: 1
![](/img/quickstart/register_phone.png)
> **注意**:由于网络或运营商原因,手机号注册时可能出现不发送验证码的情况。建议优先使用邮箱注册方式。
- 邮箱注册
![](/img/quickstart/register_email.png)

View File

@ -1,8 +1,5 @@
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
//Docusaurus v3写法
// const { themes } = require('prism-react-renderer');
// import { themes } from 'prism-react-renderer';
import("@easyops-cn/docusaurus-search-local").PluginOptions;
/** @type {import('@docusaurus/types').DocusaurusConfig} */
@ -11,14 +8,17 @@ module.exports = {
tagline:'gitlink帮助中心',
url: 'https://help.gitlink.org.cn/',
baseUrl: '/',
onBrokenLinks: 'warn',
onBrokenMarkdownLinks: 'warn',
onBrokenLinks: 'ignore',
onBrokenMarkdownLinks: 'ignore',
favicon: 'img/icon.ico',
organizationName: 'luffyZh', // Usually your GitHub org/user name.
projectName: 'docusaurus-luffyzh-website', // Usually your repo name.
scripts: [],
scripts: [
'/js/theme-toggle.js',
],
// stylesheets: ['styles/dark-mode.css'],
themeConfig: {
docs:{
sidebar:{
hideable:true,
@ -31,7 +31,7 @@ module.exports = {
{property:"og:image:alt",content:"GitLink确实开源是中国计算机学会CCF官方指定的开源创新服务平台 - Gitlink/forgeplusGitLink确实开源是中国计算机学会CCF官方指定的开源创新服务平台 for gitlink Gitlink/forgeplus for git"}
],
prism: {
theme:lightCodeTheme,
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
},
zoom: {
@ -72,31 +72,15 @@ module.exports = {
position: 'left',
dropdownActiveClassDisabled: true
},
// {
// type: 'localeDropdown',
// position: 'right',
// },
// {
// position: 'right',
// component:'./src/components/LanguageSwitcher',
// label:"language",
// to:"#"
// items: [
// {
// label: 'English',
// onClick: () => handleLocaleChange('en')
// },
// {
// label: '简体中文',
// onClick: () => handleLocaleChange('zh-Hans')
// }
// ]
// }
// {
// href: 'https://github.com/boxyhq',
// position: 'right',
// className: 'header-github-link',
// },
{
type: 'localeDropdown',
position: 'right',
},
{
type: 'html',
position: 'right',
value: '<button id="theme-toggle" class="theme-toggle-button" title="护眼模式切换" style="background:#e6f7e6;color:#333;border:1px solid #b7eb8f;border-radius:4px;margin-left:8px;padding:4px 12px;cursor:pointer;"><span style=\'margin-right:4px\'>🌱</span>护眼模式</button>',
},
],
},
footer: {
@ -197,23 +181,23 @@ module.exports = {
explicitSearchResultPath: true,
// For Docs using Chinese, The `language` is recommended to set to:
// ```
// language: ["en", "zh"],
// ```
},
],
],
i18n: {
defaultLocale: 'zh-cn',
locales: ['zh-cn','en'],
locales: ['zh-cn', 'en'],
localeConfigs: {
en: {
label: 'English',
htmlLang: 'en',
},
'zh-cn': {
label: '简体中文',
label: '中文',
direction: 'ltr',
htmlLang: 'zh-CN',
},
'en': {
label: 'English',
direction: 'ltr',
htmlLang: 'en-US',
},
},
},
};
};

1
gitlink_help_center Submodule

@ -0,0 +1 @@
Subproject commit 26ea40c0996ad101d5f4d9c0231838d68fdfad60

308
i18n/en/code.json Normal file
View File

@ -0,0 +1,308 @@
{
"theme.ErrorPageContent.title": {
"message": "This page crashed.",
"description": "The title of the fallback page when the page crashed"
},
"theme.NotFound.title": {
"message": "Page Not Found",
"description": "The title of the 404 page"
},
"theme.NotFound.p1": {
"message": "We could not find what you were looking for.",
"description": "The first paragraph of the 404 page"
},
"theme.NotFound.p2": {
"message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.",
"description": "The 2nd paragraph of the 404 page"
},
"theme.admonition.note": {
"message": "note",
"description": "The default label used for the Note admonition (:::note)"
},
"theme.admonition.tip": {
"message": "tip",
"description": "The default label used for the Tip admonition (:::tip)"
},
"theme.admonition.danger": {
"message": "danger",
"description": "The default label used for the Danger admonition (:::danger)"
},
"theme.admonition.info": {
"message": "info",
"description": "The default label used for the Info admonition (:::info)"
},
"theme.admonition.caution": {
"message": "caution",
"description": "The default label used for the Caution admonition (:::caution)"
},
"theme.blog.archive.title": {
"message": "Archive",
"description": "The page & hero title of the blog archive page"
},
"theme.blog.archive.description": {
"message": "Archive",
"description": "The page & hero description of the blog archive page"
},
"theme.BackToTopButton.buttonAriaLabel": {
"message": "Scroll back to top",
"description": "The ARIA label for the back to top button"
},
"theme.blog.paginator.navAriaLabel": {
"message": "Blog list page navigation",
"description": "The ARIA label for the blog pagination"
},
"theme.blog.paginator.newerEntries": {
"message": "Newer Entries",
"description": "The label used to navigate to the newer blog posts page (previous page)"
},
"theme.blog.paginator.olderEntries": {
"message": "Older Entries",
"description": "The label used to navigate to the older blog posts page (next page)"
},
"theme.blog.post.paginator.navAriaLabel": {
"message": "Blog post page navigation",
"description": "The ARIA label for the blog posts pagination"
},
"theme.blog.post.paginator.newerPost": {
"message": "Newer Post",
"description": "The blog post button label to navigate to the newer/previous post"
},
"theme.blog.post.paginator.olderPost": {
"message": "Older Post",
"description": "The blog post button label to navigate to the older/next post"
},
"theme.blog.post.plurals": {
"message": "One post|{count} posts",
"description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
},
"theme.blog.tagTitle": {
"message": "{nPosts} tagged with \"{tagName}\"",
"description": "The title of the page for a blog tag"
},
"theme.tags.tagsPageLink": {
"message": "View All Tags",
"description": "The label of the link targeting the tag list page"
},
"theme.colorToggle.ariaLabel": {
"message": "Switch between dark and light mode (currently {mode})",
"description": "The ARIA label for the navbar color mode toggle"
},
"theme.colorToggle.ariaLabel.mode.dark": {
"message": "dark mode",
"description": "The name for the dark color mode"
},
"theme.colorToggle.ariaLabel.mode.light": {
"message": "light mode",
"description": "The name for the light color mode"
},
"theme.docs.DocCard.categoryDescription": {
"message": "{count} items",
"description": "The default description for a category card in the generated index about how many items this category includes"
},
"theme.docs.breadcrumbs.navAriaLabel": {
"message": "Breadcrumbs",
"description": "The ARIA label for the breadcrumbs"
},
"theme.docs.paginator.navAriaLabel": {
"message": "Docs pages",
"description": "The ARIA label for the docs pagination"
},
"theme.docs.paginator.previous": {
"message": "Previous",
"description": "The label used to navigate to the previous doc"
},
"theme.docs.paginator.next": {
"message": "Next",
"description": "The label used to navigate to the next doc"
},
"theme.docs.tagDocListPageTitle.nDocsTagged": {
"message": "One doc tagged|{count} docs tagged",
"description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
},
"theme.docs.tagDocListPageTitle": {
"message": "{nDocsTagged} with \"{tagName}\"",
"description": "The title of the page for a docs tag"
},
"theme.docs.versionBadge.label": {
"message": "Version: {versionLabel}"
},
"theme.docs.versions.unreleasedVersionLabel": {
"message": "This is unreleased documentation for {siteTitle} {versionLabel} version.",
"description": "The label used to tell the user that he's browsing an unreleased doc version"
},
"theme.docs.versions.unmaintainedVersionLabel": {
"message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.",
"description": "The label used to tell the user that he's browsing an unmaintained doc version"
},
"theme.docs.versions.latestVersionSuggestionLabel": {
"message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).",
"description": "The label used to tell the user to check the latest version"
},
"theme.docs.versions.latestVersionLinkLabel": {
"message": "latest version",
"description": "The label used for the latest version suggestion link label"
},
"theme.common.editThisPage": {
"message": "Edit this page",
"description": "The link label to edit the current page"
},
"theme.common.headingLinkTitle": {
"message": "Direct link to {heading}",
"description": "Title for link to heading"
},
"theme.lastUpdated.atDate": {
"message": " on {date}",
"description": "The words used to describe on which date a page has been last updated"
},
"theme.lastUpdated.byUser": {
"message": " by {user}",
"description": "The words used to describe by who the page has been last updated"
},
"theme.lastUpdated.lastUpdatedAtBy": {
"message": "Last updated{atDate}{byUser}",
"description": "The sentence used to display when a page has been last updated, and by who"
},
"theme.navbar.mobileVersionsDropdown.label": {
"message": "Versions",
"description": "The label for the navbar versions dropdown on mobile view"
},
"theme.tags.tagsListLabel": {
"message": "Tags:",
"description": "The label alongside a tag list"
},
"theme.AnnouncementBar.closeButtonAriaLabel": {
"message": "Close",
"description": "The ARIA label for close button of announcement bar"
},
"theme.blog.sidebar.navAriaLabel": {
"message": "Blog recent posts navigation",
"description": "The ARIA label for recent posts in the blog sidebar"
},
"theme.CodeBlock.copied": {
"message": "Copied",
"description": "The copied button label on code blocks"
},
"theme.CodeBlock.copyButtonAriaLabel": {
"message": "Copy code to clipboard",
"description": "The ARIA label for copy code blocks button"
},
"theme.CodeBlock.copy": {
"message": "Copy",
"description": "The copy button label on code blocks"
},
"theme.CodeBlock.wordWrapToggle": {
"message": "Toggle word wrap",
"description": "The title attribute for toggle word wrapping button of code block lines"
},
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": {
"message": "Toggle the collapsible sidebar category '{label}'",
"description": "The ARIA label to toggle the collapsible sidebar category"
},
"theme.NavBar.navAriaLabel": {
"message": "Main",
"description": "The ARIA label for the main navigation"
},
"theme.TOCCollapsible.toggleButtonLabel": {
"message": "On this page",
"description": "The label used by the button on the collapsible TOC component"
},
"theme.navbar.mobileLanguageDropdown.label": {
"message": "Languages",
"description": "The label for the mobile language switcher dropdown"
},
"theme.blog.post.readingTime.plurals": {
"message": "One min read|{readingTime} min read",
"description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
},
"theme.blog.post.readMore": {
"message": "Read More",
"description": "The label used in blog post item excerpts to link to full blog posts"
},
"theme.blog.post.readMoreLabel": {
"message": "Read more about {title}",
"description": "The ARIA label for the link to full blog posts from excerpts"
},
"theme.docs.breadcrumbs.home": {
"message": "Home page",
"description": "The ARIA label for the home page in the breadcrumbs"
},
"theme.docs.sidebar.collapseButtonTitle": {
"message": "Collapse sidebar",
"description": "The title attribute for collapse button of doc sidebar"
},
"theme.docs.sidebar.collapseButtonAriaLabel": {
"message": "Collapse sidebar",
"description": "The title attribute for collapse button of doc sidebar"
},
"theme.docs.sidebar.navAriaLabel": {
"message": "Docs sidebar",
"description": "The ARIA label for the sidebar navigation"
},
"theme.docs.sidebar.closeSidebarButtonAriaLabel": {
"message": "Close navigation bar",
"description": "The ARIA label for close button of mobile sidebar"
},
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": {
"message": "Toggle navigation bar",
"description": "The ARIA label for hamburger menu button of mobile navigation"
},
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": {
"message": "← Back to main menu",
"description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"
},
"theme.docs.sidebar.expandButtonTitle": {
"message": "Expand sidebar",
"description": "The ARIA label and title attribute for expand button of doc sidebar"
},
"theme.docs.sidebar.expandButtonAriaLabel": {
"message": "Expand sidebar",
"description": "The ARIA label and title attribute for expand button of doc sidebar"
},
"theme.SearchPage.existingResultsTitle": {
"message": "Search results for \"{query}\"",
"description": "The search page title for non-empty query"
},
"theme.SearchPage.emptyResultsTitle": {
"message": "Search the documentation",
"description": "The search page title for empty query"
},
"theme.SearchPage.searchContext.everywhere": {
"message": "everywhere"
},
"theme.SearchPage.documentsFound.plurals": {
"message": "1 document found|{count} documents found",
"description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
},
"theme.SearchPage.noResultsText": {
"message": "No documents were found",
"description": "The paragraph for empty search result"
},
"theme.SearchBar.noResultsText": {
"message": "No results"
},
"theme.SearchBar.seeAll": {
"message": "See all results"
},
"theme.SearchBar.seeAllOutsideContext": {
"message": "See results outside {context}"
},
"theme.SearchBar.searchInContext": {
"message": "See all results in {context}"
},
"theme.SearchBar.label": {
"message": "Search",
"description": "The ARIA label and placeholder for search button"
},
"theme.ErrorPageContent.tryAgain": {
"message": "Try again",
"description": "The label of the button to try again rendering when the React error boundary captures an error"
},
"theme.common.skipToMainContent": {
"message": "Skip to main content",
"description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"
},
"theme.tags.tagsPageTitle": {
"message": "Tags",
"description": "The title of the tag list page"
}
}

View File

@ -0,0 +1,14 @@
{
"title": {
"message": "Blog",
"description": "The title for the blog used in SEO"
},
"description": {
"message": "Blog",
"description": "The description for the blog used in SEO"
},
"sidebar.title": {
"message": "Recent posts",
"description": "The label for the left sidebar"
}
}

View File

@ -0,0 +1,58 @@
{
"version.label": {
"message": "Next",
"description": "The label for version current"
},
"sidebar.defaultSidebar.category.快速开始": {
"message": "快速开始",
"description": "The label for category 快速开始 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.代码库管理": {
"message": "代码库管理",
"description": "The label for category 代码库管理 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.组织管理": {
"message": "组织管理",
"description": "The label for category 组织管理 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.疑修(Issue)": {
"message": "疑修(Issue)",
"description": "The label for category 疑修(Issue) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.合并请求(PR)": {
"message": "合并请求(PR)",
"description": "The label for category 合并请求(PR) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.DevOps引擎(Engine)": {
"message": "DevOps引擎(Engine)",
"description": "The label for category DevOps引擎(Engine) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.维基(Wiki)": {
"message": "维基(Wiki)",
"description": "The label for category 维基(Wiki) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.机器人(Bot)": {
"message": "机器人(Bot)",
"description": "The label for category 机器人(Bot) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.团队协作": {
"message": "团队协作",
"description": "The label for category 团队协作 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.第三方服务": {
"message": "第三方服务",
"description": "The label for category 第三方服务 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.通知": {
"message": "通知",
"description": "The label for category 通知 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.个人主页建站": {
"message": "个人主页建站",
"description": "The label for category 个人主页建站 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.服务协议": {
"message": "服务协议",
"description": "The label for category 服务协议 in sidebar defaultSidebar"
}
}

View File

@ -0,0 +1,93 @@
---
sidebar_label: "Introduction"
label: "Introduction"
sidebar_position: 1
slug: /
---
# About GitLink
GitLink (Indeed Open Source) is the official open source innovation service platform designated by CCF. With the mission of "serving open source innovation" and the vision of "becoming a gathering place for open source innovation", adhering to the values of "innovation, openness, collaboration, and sharing", it is dedicated to empowering large-scale open source collaborative innovation and building an open source innovation ecosystem for innovation incubation and new engineering talent cultivation!
![](/img/ccf_gitlink.png)
# Platform Features
- **Distributed Collaborative Development**: Supports online file editing, branch management, contribution statistics, repository forking, and merge requests;
- **One-stop Process Management**: Supports issues, milestones, notifications, tag archiving, Wiki documentation, and organization management;
- **Efficient Pipeline Operations**: Provides lightweight workflow engine with custom configuration, static scanning, and artifact building;
- **Multi-level Code Analysis**: Supports code traceability analysis, license risk analysis, open source vulnerability detection, and reinforcement suggestions;
- **Multi-dimensional User Profiling**: Supports development activity statistics, contribution calendar, capability modeling, and role and professional positioning analysis.
# Help Documentation
The help documentation will help you fully understand the GitLink platform. Let's contribute to open source innovation together!
<div className="row">
<div className="col col--12">
<section className="row list">
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/快速开始/注册GitLink账号">
<h2 className="text--truncate cardTitle" title="Quick Start">Quick Start</h2>
<p>Help users quickly register and use the platform [5 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/代码库管理/仓库创建">
<h2 className="text--truncate cardTitle" title="Repository Management">Repository Management</h2>
<p>Repository usage and settings [8 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/组织管理/组织简介">
<h2 className="text--truncate cardTitle" title="Organization Management">Organization Management</h2>
<p>Organization usage and settings [5 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/疑修/疑修简介">
<h2 className="text--truncate cardTitle" title="Issues">Issues</h2>
<p>Issue usage and settings [7 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/合并请求/合并请求简介">
<h2 className="text--truncate cardTitle" title="Merge Requests">Merge Requests</h2>
<p>Merge Request usage and settings [5 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/DevOps引擎/引擎简介">
<h2 className="text--truncate cardTitle" title="DevOps Engine">DevOps Engine</h2>
<p>DevOps Engine usage and settings [6 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/维基/模板导入及导出">
<h2 className="text--truncate cardTitle" title="Wiki">Wiki</h2>
<p>Wiki usage and settings [2 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/Bot市场/bot安装">
<h2 className="text--truncate cardTitle" title="Bot Market">Bot Market</h2>
<p>Bot Market usage and settings [4 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/第三方服务/跨平台代码同步">
<h2 className="text--truncate cardTitle" title="Third-party Services">Third-party Services</h2>
<p>Third-party service usage and settings [3 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/通知/通知简介">
<h2 className="text--truncate cardTitle" title="Notifications">Notifications</h2>
<p>Notification introduction and settings [2 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/个人主页建站/建站流程">
<h2 className="text--truncate cardTitle" title="Personal Site">Personal Site</h2>
<p>Personal site creation and settings [2 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/平台公告">
<h2 className="text--truncate cardTitle" title="Platform Announcements">Platform Announcements</h2>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/服务协议/GitLink服务协议">
<h2 className="text--truncate cardTitle" title="Service Agreement">Service Agreement</h2>
<p>GitLink Service Agreement [1 document]</p>
</a></article>
</section>
</div>
</div>

View File

@ -0,0 +1,54 @@
{
"version.label": {
"message": "1.1.0",
"description": "The label for version 1.1.0"
},
"sidebar.defaultSidebar.category.快速开始": {
"message": "快速开始",
"description": "The label for category 快速开始 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.代码库管理": {
"message": "代码库管理",
"description": "The label for category 代码库管理 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.组织管理": {
"message": "组织管理",
"description": "The label for category 组织管理 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.疑修(Issue)": {
"message": "疑修(Issue)",
"description": "The label for category 疑修(Issue) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.合并请求(PR)": {
"message": "合并请求(PR)",
"description": "The label for category 合并请求(PR) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.DevOps引擎(Engine)": {
"message": "DevOps引擎(Engine)",
"description": "The label for category DevOps引擎(Engine) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.维基(Wiki)": {
"message": "维基(Wiki)",
"description": "The label for category 维基(Wiki) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.机器人(Bot)": {
"message": "机器人(Bot)",
"description": "The label for category 机器人(Bot) in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.第三方服务": {
"message": "第三方服务",
"description": "The label for category 第三方服务 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.通知": {
"message": "通知",
"description": "The label for category 通知 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.个人主页建站": {
"message": "个人主页建站",
"description": "The label for category 个人主页建站 in sidebar defaultSidebar"
},
"sidebar.defaultSidebar.category.服务协议": {
"message": "服务协议",
"description": "The label for category 服务协议 in sidebar defaultSidebar"
}
}

View File

@ -0,0 +1,93 @@
---
sidebar_label: "Introduction"
label: "Introduction"
sidebar_position: 1
slug: /
---
# About GitLink
GitLink (Indeed Open Source) is the official open source innovation service platform designated by CCF. With the mission of "serving open source innovation" and the vision of "becoming a gathering place for open source innovation", adhering to the values of "innovation, openness, collaboration, and sharing", it is dedicated to empowering large-scale open source collaborative innovation and building an open source innovation ecosystem for innovation incubation and new engineering talent cultivation!
![](/img/ccf_gitlink.png)
# Platform Features
- **Distributed Collaborative Development**: Supports online file editing, branch management, contribution statistics, repository forking, and merge requests;
- **One-stop Process Management**: Supports issues, milestones, notifications, tag archiving, Wiki documentation, and organization management;
- **Efficient Pipeline Operations**: Provides lightweight workflow engine with custom configuration, static scanning, and artifact building;
- **Multi-level Code Analysis**: Supports code traceability analysis, license risk analysis, open source vulnerability detection, and reinforcement suggestions;
- **Multi-dimensional User Profiling**: Supports development activity statistics, contribution calendar, capability modeling, and role and professional positioning analysis.
# Help Documentation
The help documentation will help you fully understand the GitLink platform. Let's contribute to open source innovation together!
<div className="row">
<div className="col col--12">
<section className="row list">
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/快速开始/注册GitLink账号">
<h2 className="text--truncate cardTitle" title="Quick Start">Quick Start</h2>
<p>Help users quickly register and use the platform [5 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/代码库管理/仓库创建">
<h2 className="text--truncate cardTitle" title="Repository Management">Repository Management</h2>
<p>Repository usage and settings [8 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/组织管理/组织简介">
<h2 className="text--truncate cardTitle" title="Organization Management">Organization Management</h2>
<p>Organization usage and settings [5 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/疑修/疑修简介">
<h2 className="text--truncate cardTitle" title="Issues">Issues</h2>
<p>Issue usage and settings [7 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/合并请求/合并请求简介">
<h2 className="text--truncate cardTitle" title="Merge Requests">Merge Requests</h2>
<p>Merge Request usage and settings [5 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/DevOps引擎/引擎简介">
<h2 className="text--truncate cardTitle" title="DevOps Engine">DevOps Engine</h2>
<p>DevOps Engine usage and settings [6 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/维基/模板导入及导出">
<h2 className="text--truncate cardTitle" title="Wiki">Wiki</h2>
<p>Wiki usage and settings [2 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/Bot市场/bot安装">
<h2 className="text--truncate cardTitle" title="Bot Market">Bot Market</h2>
<p>Bot Market usage and settings [4 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/第三方服务/跨平台代码同步">
<h2 className="text--truncate cardTitle" title="Third-party Services">Third-party Services</h2>
<p>Third-party service usage and settings [3 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/通知/通知简介">
<h2 className="text--truncate cardTitle" title="Notifications">Notifications</h2>
<p>Notification introduction and settings [2 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/个人主页建站/建站流程">
<h2 className="text--truncate cardTitle" title="Personal Site">Personal Site</h2>
<p>Personal site creation and settings [2 documents]</p>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/平台公告">
<h2 className="text--truncate cardTitle" title="Platform Announcements">Platform Announcements</h2>
</a></article>
<article className="col col--6 margin-bottom--lg">
<a className="card padding--lg cardContainer" href="/服务协议/GitLink服务协议">
<h2 className="text--truncate cardTitle" title="Service Agreement">Service Agreement</h2>
<p>GitLink Service Agreement [1 document]</p>
</a></article>
</section>
</div>
</div>

View File

@ -0,0 +1,62 @@
{
"link.title.社区": {
"message": "社区",
"description": "The title of the footer links column with title=社区 in the footer"
},
"link.title.支持与服务": {
"message": "支持与服务",
"description": "The title of the footer links column with title=支持与服务 in the footer"
},
"link.title.加入我们": {
"message": "加入我们",
"description": "The title of the footer links column with title=加入我们 in the footer"
},
"link.item.label.网站首页": {
"message": "网站首页",
"description": "The label of footer link with label=网站首页 linking to https://www.gitlink.org.cn"
},
"link.item.label.关于我们": {
"message": "关于我们",
"description": "The label of footer link with label=关于我们 linking to https://www.gitlink.org.cn/aboutus"
},
"link.item.label.教学实践": {
"message": "教学实践",
"description": "The label of footer link with label=教学实践 linking to https://www.gitlink.org.cn/educoder"
},
"link.item.label.合作伙伴": {
"message": "合作伙伴",
"description": "The label of footer link with label=合作伙伴 linking to https://forum.trustie.net/forums/5030/detail"
},
"link.item.label.API文档": {
"message": "API文档",
"description": "The label of footer link with label=API文档 linking to https://forgeplus.trustie.net/docs/api"
},
"link.item.label.Git常用命令": {
"message": "Git常用命令",
"description": "The label of footer link with label=Git常用命令 linking to https://git-scm.com"
},
"link.item.label.引擎使用手册": {
"message": "引擎使用手册",
"description": "The label of footer link with label=引擎使用手册 linking to https://forum.gitlink.org.cn/forums/7487/detail"
},
"link.item.label.服务协议": {
"message": "服务协议",
"description": "The label of footer link with label=服务协议 linking to https://forum.trustie.net/forums/5029/detail"
},
"link.item.label.官网邮箱gitlink@ccf.org.cn": {
"message": "官网邮箱gitlink@ccf.org.cn",
"description": "The label of footer link with label=官网邮箱gitlink@ccf.org.cn linking to https://www.gitlink.org.cn"
},
"link.item.label.QQ群": {
"message": "QQ群",
"description": "The label of footer link with label=QQ群 linking to https://www.gitlink.org.cn"
},
"link.item.label.公众号": {
"message": "公众号",
"description": "The label of footer link with label=公众号 linking to https://www.gitlink.org.cn"
},
"copyright": {
"message": "<p>©Copyright 2025 CCF 开源发展委员会</p><p>Powered by Trustie& IntelliDE 京ICP备13000930号</p>",
"description": "The footer copyright"
}
}

View File

@ -0,0 +1,10 @@
{
"logo.alt": {
"message": "gitlink",
"description": "The alt text of navbar logo"
},
"item.label.帮助中心": {
"message": "帮助中心",
"description": "Navbar item with label 帮助中心"
}
}

View File

@ -1,47 +0,0 @@
为了构建更好的平台生态环境我们推出了基于OAuth2的API版本。
API 接口使用方式以及Url都参照GitHub为了各位开发者更好的兼容已经存在的第三方应用。
# API 使用条款
未经用户允许,不准爬取或存储用户的资源。
禁止滥用 API请求频率过快将导致请求终止。
OAuth2 认证基本流程
![image](./oauth2.png)
# OAuth2 获取 AccessToken 认证步骤
### 1. 授权码模式
* 应用通过 浏览器 或 Webview 将用户引导到平台三方认证页面上( GET请求
`https://gitlink.org.cn/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code`
* 用户对应用进行授权
注意: 如果之前已经授权过的需要跳过授权页面,需要在上面第一步的 URL 加上 scope 参数,且 scope 的值需要和用户上次授权的勾选的一致。如用户在上次授权了user_info。则步骤A 中 GET 请求应为:
`https://gitlink.org.cn/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&scope=user_info`
* 平台认证服务器通过回调地址`{redirect_uri}`将 用户授权码 传递给 应用服务器 或者直接在 Webview 中跳转到携带 用户授权码的回调地址上Webview 直接获取code即可`{redirect_uri}?code=abc&state=xyz`)
* 应用服务器 或 Webview 使用 access_token API 向平台认证服务器发送post请求传入 用户授权码 以及 回调地址( POST请求
注:请求过程建议将 client_secret 放在 Body 中传值,以保证数据安全。
`https://gitlink.org.cn/oauth/token?grant_type=authorization_code&code={code}&client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}`
* 平台认证服务器返回 access_token
应用通过 access_token 访问 Open API 使用用户数据。
当 access_token 过期后(有效期为一天),你可以通过以下 refresh_token 方式重新获取 access_token POST请求
`https://gitlink.org.cn/oauth/token?grant_type=refresh_token&refresh_token={refresh_token}`
> 注意:如果获取 access_token 返回 403可能是没有设置User-Agent的原因。
详见获取Token时服务端响应状态403是什么情况
### 2. 密码模式
* 用户向客户端提供邮箱地址和密码。客户端将邮箱地址和密码发给平台认证服务器,并向平台认证服务器请求令牌。( POST请求。Content-Type: application/x-www-form-urlencoded
`curl -X POST --data-urlencode "grant_type=password" --data-urlencode "username={email}" --data-urlencode "password={password}" --data-urlencode "client_id={client_id}" --data-urlencode "client_secret={client_secret}" --data-urlencode "scope=user_info" https://gitlink.org.cn/oauth/token`
scope表示权限范围有以下选项请求时使用空格隔开
`user_info` `projects` `pull_requests` `issues` `notes` `keys` `hook groups` `gists` `enterprises`
* 认证服务器返回 access_token
应用通过 access_token 访问 Open API 使用用户数据。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

30358
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "docusaurus",
"version": "3.0.0",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -14,21 +14,24 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@babel/runtime": "^7.27.4",
"@cmfcmf/docusaurus-search-local": "1.1.0",
"@docusaurus/core": "3.9.2",
"@docusaurus/plugin-client-redirects": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@docusaurus/core": "2.4.1",
"@docusaurus/plugin-client-redirects": "2.4.1",
"@docusaurus/preset-classic": "2.4.1",
"@easyops-cn/docusaurus-search-local": "^0.35.0",
"@mdx-js/react": "^3.1.1",
"@mdx-js/react": "1.6.22",
"@node-rs/jieba": "^1.7.0",
"antd": "^5.25.4",
"axios": "^1.9.0",
"clsx": "1.2.1",
"docusaurus-gtm-plugin": "0.0.2",
"docusaurus-plugin-image-zoom": "1.0.1",
"or": "^0.2.0",
"prism-react-renderer": "^1.3.5",
"prismjs": "^1.30.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"html2pdf.js": "^0.10.3",
"react": "17.0.2",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "17.0.2",
"react-modal": "3.16.1"
},
"browserslist": {
@ -44,6 +47,6 @@
]
},
"engines": {
"node": ">=18.x"
"node": ">=14.18.1 <=18.x"
}
}

View File

@ -0,0 +1,304 @@
import React, { useState, useEffect, useCallback } from 'react';
import styles from './styles.module.css';
import BrowserOnly from '@docusaurus/BrowserOnly';
interface DocNoteBoxProps {
docPath: string;
}
// 获取标准化的文档路径
function getNormalizedPath(path: string): string {
// 移除开头的版本号(如 /version-1.1.0/
let normalizedPath = path.replace(/^\/version-\d+\.\d+\.\d+\//, '');
// 确保路径以 / 开头
if (!normalizedPath.startsWith('/')) {
normalizedPath = '/' + normalizedPath;
}
// 移除结尾的斜杠(除非是根路径)
if (normalizedPath !== '/' && normalizedPath.endsWith('/')) {
normalizedPath = normalizedPath.slice(0, -1);
}
console.log('路径标准化:', { 原始路径: path, 标准化后: normalizedPath });
return normalizedPath;
}
function getEnglishContent(docPath: string): string {
const normalizedPath = getNormalizedPath(docPath);
switch (normalizedPath) {
case '/zh-cn/quickstart/register':
return `Register a GitLink Account\n\n1. Click the Register button\nImage 1: [Figure 1]\n\n2. Fill in registration information\nImage 2: [Figure 2]\n\n3. Registration complete`;
case '/zh-cn/quickstart/create-project':
return `Create Your First Open Source Project\n\n1. Click Create Project\nImage 1: [Figure 1]\n\n2. Fill in project details\nImage 2: [Figure 2]`;
case '/zh-cn/quickstart/submit-code':
return `Submit Your First Code\n\n1. Open your project\nImage 1: [Figure 1]\n\n2. Click Submit Code\nImage 2: [Figure 2]`;
case '/zh-cn/quickstart/search-project':
return `Search for Open Source Projects\n\n1. Go to the search page\nImage 1: [Figure 1]\n\n2. Enter keywords and search\nImage 2: [Figure 2]`;
// 你可以继续添加更多文档的英文内容
default:
return 'No English version available for this document.';
}
}
export default function DocNoteBox({ docPath }: DocNoteBoxProps): JSX.Element {
// 中文问题记录
const [note, setNote] = useState('');
const [isSaving, setIsSaving] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
// 英文备注
const [enNote, setEnNote] = useState('');
const [isEnSaving, setIsEnSaving] = useState(false);
const [isEnExpanded, setIsEnExpanded] = useState(false);
// 是否显示恢复按钮
const [showRestoreButton, setShowRestoreButton] = useState(false);
// 保存滚动位置
const saveScrollPosition = useCallback(() => {
const position = window.scrollY;
const normalizedPath = getNormalizedPath(docPath);
const scrollData = {
path: normalizedPath,
position: position,
timestamp: Date.now()
};
console.log('保存位置:', scrollData);
localStorage.setItem('last-scroll-position', JSON.stringify(scrollData));
}, [docPath]);
// 恢复滚动位置
const restoreScrollPosition = useCallback(() => {
const savedData = localStorage.getItem('last-scroll-position');
if (savedData) {
try {
const { path, position } = JSON.parse(savedData);
const normalizedPath = getNormalizedPath(docPath);
console.log('恢复位置:', { savedPath: path, currentPath: normalizedPath, position });
// 只有当路径匹配时才恢复
if (path === normalizedPath) {
window.scrollTo({
top: position,
behavior: 'smooth'
});
// 恢复后清除保存的位置并隐藏按钮
localStorage.removeItem('last-scroll-position');
setShowRestoreButton(false);
}
} catch (e) {
console.error('Failed to restore scroll position:', e);
}
}
}, [docPath]);
// 中文保存
const saveNote = useCallback(() => {
setIsSaving(true);
localStorage.setItem(`doc-note-${docPath}`, note);
setTimeout(() => setIsSaving(false), 1000);
}, [note, docPath]);
// 英文保存
const saveEnNote = useCallback(() => {
setIsEnSaving(true);
localStorage.setItem(`doc-note-en-${docPath}`, enNote);
setTimeout(() => setIsEnSaving(false), 1000);
}, [enNote, docPath]);
// 加载
useEffect(() => {
const savedNote = localStorage.getItem(`doc-note-${docPath}`);
if (savedNote) setNote(savedNote);
const savedEnNote = localStorage.getItem(`doc-note-en-${docPath}`);
if (savedEnNote) setEnNote(savedEnNote);
// 检查是否有保存的位置
const savedData = localStorage.getItem('last-scroll-position');
if (savedData) {
try {
const { path } = JSON.parse(savedData);
const normalizedPath = getNormalizedPath(docPath);
console.log('检查位置:', { savedPath: path, currentPath: normalizedPath });
setShowRestoreButton(path === normalizedPath);
} catch (e) {
console.error('Failed to check saved position:', e);
}
}
}, [docPath]);
// 自动保存
useEffect(() => {
const timer = setTimeout(saveNote, 30000);
return () => clearTimeout(timer);
}, [note, saveNote]);
useEffect(() => {
const timer = setTimeout(saveEnNote, 30000);
return () => clearTimeout(timer);
}, [enNote, saveEnNote]);
// 定期保存位置每30秒
useEffect(() => {
const timer = setInterval(saveScrollPosition, 30000);
return () => clearInterval(timer);
}, [saveScrollPosition]);
// 监听滚动事件
useEffect(() => {
let timeoutId: NodeJS.Timeout;
const handleScroll = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
console.log('滚动停止,保存位置');
saveScrollPosition();
}, 1000); // 滚动停止1秒后保存
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
clearTimeout(timeoutId);
};
}, [saveScrollPosition]);
// 页面可见性变化时保存位置
useEffect(() => {
const handleVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
console.log('页面隐藏,保存位置');
saveScrollPosition();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, [saveScrollPosition]);
// 页面关闭时保存位置
useEffect(() => {
const handleBeforeUnload = () => {
console.log('页面关闭,保存位置');
saveScrollPosition();
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [saveScrollPosition]);
// 获取英文内容
const englishContent = getEnglishContent(docPath);
// 导出 PDF
const exportToPDF = useCallback(() => {
// 动态导入 html2pdf.js
import('html2pdf.js').then((html2pdf) => {
// 获取文档内容
const content = document.querySelector('article');
if (!content) {
console.error('找不到文档内容');
return;
}
// 创建一个临时容器
const container = document.createElement('div');
container.style.padding = '20px';
// 复制文档内容
const contentClone = content.cloneNode(true) as HTMLElement;
// 移除不需要的元素
const elementsToRemove = contentClone.querySelectorAll('.theme-edit-this-page, .theme-last-updated, .theme-prev-next-link');
elementsToRemove.forEach(el => el.remove());
// 添加到容器
container.appendChild(contentClone);
// 配置 PDF 选项
const opt = {
margin: 10,
filename: `${docPath.split('/').pop() || 'document'}.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: {
scale: 2,
useCORS: true,
letterRendering: true
},
jsPDF: {
unit: 'mm',
format: 'a4',
orientation: 'portrait' as const
}
};
// 导出 PDF
html2pdf.default().set(opt).from(container).save();
});
}, [docPath]);
return (
<BrowserOnly>
{() => (
<div className={styles.container}>
{/* 导出 PDF 按钮 */}
<button
className={styles.exportButton}
onClick={exportToPDF}
title="导出为 PDF"
>
PDF
</button>
{/* 恢复位置按钮 */}
{showRestoreButton && (
<button
className={styles.restoreButton}
onClick={restoreScrollPosition}
title="恢复到上次阅读位置"
>
</button>
)}
{/* 中文问题记录 */}
<div className={`${styles.noteBox} ${isExpanded ? styles.expanded : styles.collapsed}`}>
<div className={styles.noteHeader} onClick={() => setIsExpanded(v => !v)}>
<h3></h3>
<div className={styles.headerRight}>
{isSaving && <span className={styles.savingIndicator}>...</span>}
<span className={styles.expandIcon}>{isExpanded ? '▼' : '▲'}</span>
</div>
</div>
{isExpanded && (
<textarea
className={styles.noteTextarea}
value={note}
onChange={e => setNote(e.target.value)}
onBlur={saveNote}
placeholder="在这里记录你的问题..."
/>
)}
</div>
{/* 英文只读文本框 */}
<div className={`${styles.noteBox} ${isEnExpanded ? styles.expanded : styles.collapsed}`}>
<div className={styles.noteHeader} onClick={() => setIsEnExpanded(v => !v)}>
<h3>English Version</h3>
<div className={styles.headerRight}>
{isEnSaving && <span className={styles.savingIndicator}>Saving...</span>}
<span className={styles.expandIcon}>{isEnExpanded ? '▼' : '▲'}</span>
</div>
</div>
{isEnExpanded && (
<textarea
className={styles.noteTextarea}
value={englishContent}
readOnly
style={{ background: '#f5f5f5', color: '#333', fontStyle: 'italic' }}
/>
)}
</div>
</div>
)}
</BrowserOnly>
);
}

View File

@ -0,0 +1,141 @@
.container {
position: fixed;
right: 20px;
bottom: 20px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 1000;
}
.noteBox {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
width: 300px;
transition: all 0.3s ease;
}
.noteHeader {
padding: 12px 16px;
border-bottom: 1px solid #eee;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.noteHeader h3 {
margin: 0;
font-size: 16px;
color: #333;
}
.headerRight {
display: flex;
align-items: center;
gap: 8px;
}
.savingIndicator {
font-size: 12px;
color: #666;
}
.expandIcon {
font-size: 12px;
color: #666;
transition: transform 0.3s ease;
}
.noteTextarea {
width: 100%;
min-height: 100px;
padding: 12px;
border: none;
resize: vertical;
font-size: 14px;
line-height: 1.5;
color: #333;
background: #f9f9f9;
}
.noteTextarea:focus {
outline: none;
background: #fff;
}
.collapsed {
height: 48px;
overflow: hidden;
}
.expanded {
height: auto;
}
.restoreButton,
.exportButton {
background: #1890ff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.restoreButton:hover,
.exportButton:hover {
background: #40a9ff;
}
.restoreButton:active,
.exportButton:active {
background: #096dd9;
}
.englishContent {
display: flex;
flex-direction: column;
gap: 16px;
}
.englishLink {
padding: 8px 0;
}
.englishLink a {
color: var(--ifm-color-primary);
text-decoration: none;
font-size: 14px;
display: flex;
align-items: center;
gap: 4px;
}
.englishLink a:hover {
text-decoration: underline;
}
.englishImage {
width: 100%;
border-radius: 4px;
overflow: hidden;
}
.englishImage img {
width: 100%;
height: auto;
display: block;
}
@media (max-width: 1400px) {
.container {
position: static;
width: 100%;
margin-top: 20px;
}
}

View File

@ -0,0 +1,23 @@
.feedback-float-btn {
position: fixed;
right: 32px;
bottom: 32px;
z-index: 9999;
width: 56px;
height: 56px;
border-radius: 50%;
background: linear-gradient(135deg, #6a82fb 0%, #fc5c7d 100%);
color: #fff;
border: none;
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
font-size: 28px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: box-shadow 0.2s, transform 0.2s;
}
.feedback-float-btn:hover {
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
transform: scale(1.08);
}

View File

@ -0,0 +1,24 @@
import React, { useState } from 'react';
import FeedbackModal from './FeedbackModal';
import './FeedbackButton.css';
console.log('FeedbackButton 渲染');
const FeedbackButton: React.FC = () => {
const [visible, setVisible] = useState(false);
return (
<>
<button
className="feedback-float-btn"
onClick={() => setVisible(true)}
title="问题反馈"
>
<span role="img" aria-label="feedback">💬</span>
</button>
<FeedbackModal visible={visible} onClose={() => setVisible(false)} />
</>
);
};
export default FeedbackButton;

View File

@ -0,0 +1,84 @@
.feedback-modal-mask {
position: fixed;
z-index: 1100;
left: 0; top: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.25);
display: flex;
align-items: center;
justify-content: center;
}
.feedback-modal {
background: #fff;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.18);
padding: 32px 28px 24px 28px;
min-width: 320px;
max-width: 90vw;
position: relative;
animation: feedback-pop 0.25s cubic-bezier(.68,-0.55,.27,1.55);
}
@keyframes feedback-pop {
0% { transform: scale(0.7); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.feedback-modal-close {
position: absolute;
right: 16px;
top: 12px;
background: none;
border: none;
font-size: 22px;
color: #888;
cursor: pointer;
}
.feedback-modal h2 {
margin: 0 0 18px 0;
font-size: 22px;
color: #333;
text-align: center;
}
.feedback-modal label {
display: block;
margin-bottom: 16px;
color: #444;
font-size: 15px;
}
.feedback-modal select,
.feedback-modal textarea {
width: 100%;
margin-top: 6px;
padding: 8px;
border-radius: 6px;
border: 1px solid #ddd;
font-size: 15px;
resize: vertical;
box-sizing: border-box;
}
.feedback-modal textarea {
min-height: 64px;
max-height: 180px;
}
.feedback-submit-btn {
width: 100%;
padding: 10px 0;
background: linear-gradient(135deg, #6a82fb 0%, #fc5c7d 100%);
color: #fff;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
margin-top: 8px;
transition: background 0.2s;
}
.feedback-submit-btn:disabled {
background: #eee;
color: #aaa;
cursor: not-allowed;
}
.feedback-success {
color: #52c41a;
font-size: 18px;
text-align: center;
margin: 32px 0 24px 0;
}

View File

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import { saveFeedback } from '../../services/feedbackService';
import './FeedbackModal.css';
const FEEDBACK_TYPES = [
'功能异常',
'界面问题',
'文档错误',
'其他',
];
interface FeedbackModalProps {
visible: boolean;
onClose: () => void;
}
const FeedbackModal: React.FC<FeedbackModalProps> = ({ visible, onClose }) => {
const [type, setType] = useState(FEEDBACK_TYPES[0]);
const [desc, setDesc] = useState('');
const [submitted, setSubmitted] = useState(false);
if (!visible) return null;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!desc.trim()) return;
saveFeedback({ type, desc, time: Date.now() });
setSubmitted(true);
setTimeout(() => {
setSubmitted(false);
setDesc('');
onClose();
}, 1200);
};
return (
<div className="feedback-modal-mask">
<div className="feedback-modal">
<button className="feedback-modal-close" onClick={onClose}>×</button>
<h2></h2>
{submitted ? (
<div className="feedback-success"></div>
) : (
<form onSubmit={handleSubmit}>
<label>
<select value={type} onChange={e => setType(e.target.value)}>
{FEEDBACK_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
</select>
</label>
<label>
<textarea
value={desc}
onChange={e => setDesc(e.target.value)}
placeholder="请详细描述您遇到的问题..."
required
/>
</label>
<button type="submit" className="feedback-submit-btn" disabled={!desc.trim()}>
</button>
</form>
)}
</div>
</div>
);
};
export default FeedbackModal;

View File

@ -0,0 +1,57 @@
.kanban-board {
padding: 20px;
background: #f5f5f5;
min-height: calc(100vh - 64px);
}
.kanban-header {
margin-bottom: 20px;
display: flex;
align-items: center;
}
.kanban-columns {
display: flex;
gap: 20px;
overflow-x: auto;
padding-bottom: 20px;
}
.kanban-column {
background: #fff;
border-radius: 8px;
padding: 16px;
min-width: 300px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.kanban-column h3 {
margin: 0 0 16px 0;
color: #333;
font-size: 16px;
}
.task-list {
min-height: 100px;
}
.task-card {
margin-bottom: 8px;
cursor: pointer;
transition: all 0.3s;
}
.task-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.task-card h4 {
margin: 0 0 8px 0;
color: #333;
}
.task-card p {
margin: 4px 0;
color: #666;
font-size: 12px;
}

View File

@ -0,0 +1,174 @@
import React, { useState, useEffect } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { Card, Button, Input, Select, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import './KanbanBoard.css';
interface Task {
id: string;
title: string;
description: string;
assignee: string;
dueDate: string;
priority: 'high' | 'medium' | 'low';
status: string;
labels: string[];
}
interface Column {
id: string;
title: string;
tasks: Task[];
}
const KanbanBoard: React.FC = () => {
const [columns, setColumns] = useState<Column[]>([]);
const [newTaskTitle, setNewTaskTitle] = useState('');
const [selectedColumn, setSelectedColumn] = useState('');
// 初始化看板列
useEffect(() => {
const initialColumns: Column[] = [
{ id: 'todo', title: '待办', tasks: [] },
{ id: 'inProgress', title: '进行中', tasks: [] },
{ id: 'done', title: '已完成', tasks: [] },
];
setColumns(initialColumns);
}, []);
// 处理拖拽结束
const onDragEnd = (result: any) => {
if (!result.destination) return;
const { source, destination } = result;
const sourceColumn = columns.find(col => col.id === source.droppableId);
const destColumn = columns.find(col => col.id === destination.droppableId);
if (!sourceColumn || !destColumn) return;
const sourceTasks = [...sourceColumn.tasks];
const destTasks = source.droppableId === destination.droppableId
? sourceTasks
: [...destColumn.tasks];
const [removed] = sourceTasks.splice(source.index, 1);
destTasks.splice(destination.index, 0, removed);
const newColumns = columns.map(col => {
if (col.id === source.droppableId) {
return { ...col, tasks: sourceTasks };
}
if (col.id === destination.droppableId) {
return { ...col, tasks: destTasks };
}
return col;
});
setColumns(newColumns);
};
// 添加新任务
const handleAddTask = () => {
if (!newTaskTitle || !selectedColumn) {
message.error('请填写任务标题并选择状态列');
return;
}
const newTask: Task = {
id: Date.now().toString(),
title: newTaskTitle,
description: '',
assignee: '',
dueDate: '',
priority: 'medium',
status: selectedColumn,
labels: [],
};
const newColumns = columns.map(col => {
if (col.id === selectedColumn) {
return { ...col, tasks: [...col.tasks, newTask] };
}
return col;
});
setColumns(newColumns);
setNewTaskTitle('');
message.success('任务添加成功');
};
return (
<div className="kanban-board">
<div className="kanban-header">
<Input
placeholder="输入任务标题"
value={newTaskTitle}
onChange={e => setNewTaskTitle(e.target.value)}
style={{ width: 200, marginRight: 16 }}
/>
<Select
placeholder="选择状态列"
value={selectedColumn}
onChange={value => setSelectedColumn(value)}
style={{ width: 120, marginRight: 16 }}
>
{columns.map(col => (
<Select.Option key={col.id} value={col.id}>
{col.title}
</Select.Option>
))}
</Select>
<Button type="primary" onClick={handleAddTask}>
</Button>
</div>
<DragDropContext onDragEnd={onDragEnd}>
<div className="kanban-columns">
{columns.map(column => (
<div key={column.id} className="kanban-column">
<h3>{column.title}</h3>
<Droppable droppableId={column.id}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className="task-list"
>
{column.tasks.map((task, index) => (
<Draggable
key={task.id}
draggableId={task.id}
index={index}
>
{(provided) => (
<Card
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className="task-card"
>
<h4>{task.title}</h4>
{task.assignee && (
<p>: {task.assignee}</p>
)}
{task.dueDate && (
<p>: {task.dueDate}</p>
)}
</Card>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</div>
))}
</div>
</DragDropContext>
</div>
);
};
export default KanbanBoard;

View File

@ -0,0 +1,101 @@
import React from 'react';
import { Modal, Form, Input, DatePicker, Select, Button } from 'antd';
import { Task } from './types';
interface TaskDetailProps {
task: Task | null;
visible: boolean;
onClose: () => void;
onSave: (task: Task) => void;
}
const TaskDetail: React.FC<TaskDetailProps> = ({
task,
visible,
onClose,
onSave,
}) => {
const [form] = Form.useForm();
const handleSubmit = () => {
form.validateFields().then(values => {
onSave({
...task!,
...values,
dueDate: values.dueDate?.format('YYYY-MM-DD'),
});
onClose();
});
};
return (
<Modal
title="任务详情"
open={visible}
onCancel={onClose}
footer={[
<Button key="cancel" onClick={onClose}>
</Button>,
<Button key="submit" type="primary" onClick={handleSubmit}>
</Button>,
]}
>
<Form
form={form}
layout="vertical"
initialValues={task || {}}
>
<Form.Item
name="title"
label="标题"
rules={[{ required: true, message: '请输入任务标题' }]}
>
<Input />
</Form.Item>
<Form.Item
name="description"
label="描述"
>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item
name="assignee"
label="负责人"
>
<Input />
</Form.Item>
<Form.Item
name="dueDate"
label="截止日期"
>
<DatePicker style={{ width: '100%' }} />
</Form.Item>
<Form.Item
name="priority"
label="优先级"
>
<Select>
<Select.Option value="high"></Select.Option>
<Select.Option value="medium"></Select.Option>
<Select.Option value="low"></Select.Option>
</Select>
</Form.Item>
<Form.Item
name="labels"
label="标签"
>
<Select mode="tags" />
</Form.Item>
</Form>
</Modal>
);
};
export default TaskDetail;

View File

@ -0,0 +1,16 @@
export interface Task {
id: string;
title: string;
description: string;
assignee: string;
dueDate: string;
priority: 'high' | 'medium' | 'low';
status: string;
labels: string[];
}
export interface Column {
id: string;
title: string;
tasks: Task[];
}

View File

@ -1,38 +0,0 @@
// src/components/LanguageSwitcher.js
import React from 'react';
import { useDocusaurusContext } from '@docusaurus/useDocusaurusContext';
export default function LanguageSwitcher() {
const { i18n } = useDocusaurusContext();
const locales = [
{ code: 'en', label: 'English' },
{ code: 'zh-Hans', label: '简体中文' }
];
return (
<div style={{ marginRight: '1rem' }}>
<select
value={i18n.currentLocale}
onChange={(e) => {
localStorage.setItem('preferredLocale', e.target.value);
window.location.href = `/${e.target.value}/`;
}}
style={{
padding: '0.5rem 1rem',
border: '1px solid #ddd',
borderRadius: '4px',
backgroundColor: 'white',
fontSize: '0.9rem',
cursor: 'pointer',
}}
>
{locales.map((locale) => (
<option key={locale.code} value={locale.code}>
{locale.label}
</option>
))}
</select>
</div>
);
}

View File

@ -0,0 +1,31 @@
.menu-item-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-right: 8px;
}
.menu-item-content span {
flex: 1;
margin-right: 8px;
}
/* 让收藏按钮在导航栏始终可见且对齐 */
.ant-menu-item .star-button,
.ant-menu-item .star-icon {
opacity: 1 !important;
display: inline-flex !important;
visibility: visible !important;
width: auto !important;
height: auto !important;
color: #d9d9d9 !important;
}
.ant-menu-item.starred .star-icon {
color: #fadb14 !important;
}
.ant-menu-item {
padding-right: 24px !important;
}

View File

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react';
import { Layout, Menu } from 'antd';
import { useLocation, useNavigate } from 'react-router-dom';
import StarButton from '../StarButton/StarButton';
import { starService } from '../../services/starService';
import './Navigation.css';
const { Sider } = Layout;
interface NavigationProps {
items: {
key: string;
label: string;
path: string;
}[];
}
const Navigation: React.FC<NavigationProps> = ({ items }) => {
const location = useLocation();
const navigate = useNavigate();
const [starredItems, setStarredItems] = useState<string[]>([]);
useEffect(() => {
// 获取收藏列表
const fetchStarredItems = async () => {
try {
const items = await starService.getStarredItems();
setStarredItems(items);
} catch (error) {
console.error('获取收藏列表失败:', error);
}
};
fetchStarredItems();
// 监听收藏变化事件
const handleStarredChanged = () => {
fetchStarredItems();
};
window.addEventListener('starred-changed', handleStarredChanged);
// 清理事件监听
return () => {
window.removeEventListener('starred-changed', handleStarredChanged);
};
}, []);
const handleStarChange = async (itemPath: string, starred: boolean) => {
try {
if (starred) {
await starService.addStar(itemPath);
setStarredItems([...starredItems, itemPath]);
} else {
await starService.removeStar(itemPath);
setStarredItems(starredItems.filter(id => id !== itemPath));
}
} catch (error) {
console.error('更新收藏状态失败:', error);
}
};
return (
<Sider width={200} className="site-layout-background">
<Menu
mode="inline"
selectedKeys={[location.pathname]}
style={{ height: '100%', borderRight: 0 }}
>
{items.map(item => (
<Menu.Item
key={item.path}
onClick={() => navigate(item.path)}
className={starredItems.includes(item.path) ? 'starred' : ''}
>
<div className="menu-item-content">
<span>{item.label}</span>
console.log('渲染导航栏StarButton', item.path, starredItems.includes(item.path));
<StarButton
starred={starredItems.includes(item.path)}
onStarChange={(starred) => handleStarChange(item.path, starred)}
/>
</div>
</Menu.Item>
))}
</Menu>
</Sider>
);
};
export default Navigation;

View File

@ -0,0 +1,28 @@
.star-button {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4px;
transition: all 0.3s ease;
min-width: 24px;
min-height: 24px;
}
.star-button:hover {
transform: scale(1.1);
}
.star-icon {
font-size: 16px;
color: #d9d9d9;
transition: all 0.3s ease;
}
.star-icon.starred {
color: #fadb14;
}
.star-button:hover .star-icon {
color: #fadb14;
}

View File

@ -0,0 +1,29 @@
import React from 'react';
import { StarOutlined, StarFilled } from '@ant-design/icons';
import './StarButton.css';
interface StarButtonProps {
starred: boolean;
onStarChange?: (starred: boolean) => void;
}
const StarButton: React.FC<StarButtonProps> = ({
starred,
onStarChange,
}) => {
const handleClick = () => {
onStarChange?.(!starred);
};
return (
<div className="star-button" onClick={handleClick}>
{starred ? (
<StarFilled className="star-icon starred" />
) : (
<StarOutlined className="star-icon" />
)}
</div>
);
};
export default StarButton;

View File

@ -0,0 +1,46 @@
import React, { useEffect, useState } from 'react';
import { useColorMode } from '@docusaurus/theme-common';
export default function ThemeToggle() {
const { colorMode, setColorMode } = useColorMode();
const [isEyeCare, setIsEyeCare] = useState(false);
useEffect(() => {
// 检查是否处于护眼模式
const isEyeCareMode = document.documentElement.classList.contains('eye-care-mode');
setIsEyeCare(isEyeCareMode);
}, []);
const toggleTheme = () => {
if (isEyeCare) {
// 如果当前是护眼模式,切换到浅色模式
document.documentElement.classList.remove('eye-care-mode');
setColorMode('light');
setIsEyeCare(false);
} else if (colorMode === 'light') {
// 如果当前是浅色模式,切换到深色模式
setColorMode('dark');
} else {
// 如果当前是深色模式,切换到护眼模式
document.documentElement.classList.add('eye-care-mode');
setIsEyeCare(true);
}
};
const getThemeIcon = () => {
if (isEyeCare) {
return '👁️';
}
return colorMode === 'light' ? '☀️' : '🌙';
};
return (
<button
className="theme-toggle-button"
onClick={toggleTheme}
title="切换主题"
>
<span className="theme-toggle-icon">{getThemeIcon()}</span>
</button>
);
}

View File

@ -0,0 +1,27 @@
import React from 'react';
const ThemeToggle: React.FC = () => {
const toggleTheme = () => {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme');
if (currentTheme === 'eye-care') {
html.setAttribute('data-theme', 'light');
} else {
html.setAttribute('data-theme', 'eye-care');
}
};
return (
<button
className="theme-toggle-button"
style={{ background: '#e6f7e6', color: '#333', border: '1px solid #b7eb8f', borderRadius: 4, marginLeft: 8, padding: '4px 12px', cursor: 'pointer' }}
title="护眼模式切换"
onClick={toggleTheme}
>
<span role="img" aria-label="eye-care" style={{ marginRight: 4 }}>🌱</span>
</button>
);
};
export default ThemeToggle;

View File

@ -16,6 +16,20 @@
--ifm-color-primary-lightest: rgb(146, 224, 208);
--ifm-code-font-size: 95%;
--search-local-modal-background:#1b2440;
/* 护眼模式颜色变量 */
--ifm-background-color: #f5f5f5;
--ifm-background-surface-color: #ffffff;
--ifm-navbar-background-color: #f5f5f5;
--ifm-menu-color: #333333;
--ifm-menu-color-active: #466aff;
--ifm-menu-color-background-active: #e6e6e6;
--ifm-menu-color-background-hover: #f0f0f0;
--ifm-toc-link-color: #333333;
--ifm-toc-border-color: #e6e6e6;
--ifm-breadcrumb-color-active: #466aff;
--ifm-breadcrumb-color: #666666;
--ifm-breadcrumb-separator-filter: invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(95%) contrast(86%);
}
.navbar{
background-color: rgba(27, 36, 64, 1);
@ -57,6 +71,7 @@ html[data-theme='dark'] .docusaurus-highlight-code-line {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.footer__bottom .margin-bottom--sm{
position: absolute;
@ -148,4 +163,45 @@ html[data-theme='dark'] .docusaurus-highlight-code-line {
.widget{
display: none!important;
opacity: 0;
}
}
/* 护眼模式样式 */
html[data-theme='eye-care'] {
--ifm-background-color: #e6f7e6;
--ifm-background-surface-color: #ffffff;
--ifm-navbar-background-color: #e6f7e6;
--ifm-menu-color: #333333;
--ifm-menu-color-active: #466aff;
--ifm-menu-color-background-active: #e6e6e6;
--ifm-menu-color-background-hover: #f0f0f0;
--ifm-toc-link-color: #333333;
--ifm-toc-border-color: #e6e6e6;
--ifm-breadcrumb-color-active: #466aff;
--ifm-breadcrumb-color: #666666;
--ifm-breadcrumb-separator-filter: invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(95%) contrast(86%);
}
/* 主题切换按钮样式 */
.theme-toggle-button {
display: flex;
align-items: center;
padding: 0.5rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.theme-toggle-button:hover {
background-color: var(--ifm-menu-color-background-hover);
}
.theme-toggle-icon {
width: 20px;
height: 20px;
margin-right: 0.5rem;
}
/* 移除全局护眼模式 */
/* body {
background-color: #e6f7e6;
} */

1
src/js/theme-toggle.js Normal file
View File

@ -0,0 +1 @@

View File

@ -10,6 +10,7 @@ import InvestorsSection from '../components/InvestorsSection';
import CustomersSection from '../components/CustomersSection';
import DeveloperCommunitySection from '../components/DeveloperCommunitySection';
import FeatureSection from '../components/FeatureSection';
import FeedbackButton from '../components/FeedbackButton/FeedbackButton';
const Icon3 = require('../../static/img/home-buildingblocks.svg').default;
@ -21,7 +22,7 @@ const IndexPage = () => {
<Layout title={title} description={description}>
<HeroSection
title="Security Building Blocks for Developers"
description="Reduce Time to Market without sacrificing your security posture! BoxyHQs suite of APIs for security and privacy helps engineering teams build and ship compliant cloud applications faster."
description="Reduce Time to Market without sacrificing your security posture! BoxyHQ's suite of APIs for security and privacy helps engineering teams build and ship compliant cloud applications faster."
image="/img/home-hero.svg"
buttons={[
{
@ -142,6 +143,7 @@ const IndexPage = () => {
{/* <NewsSection />
<InvestorsSection />
<DeveloperCommunitySection /> */}
<FeedbackButton />
</Layout>
);
};

View File

@ -0,0 +1,13 @@
const FEEDBACK_KEY = 'feedback_list';
export function saveFeedback(feedback: { type: string; desc: string; time: number }) {
const raw = localStorage.getItem(FEEDBACK_KEY);
const list = raw ? JSON.parse(raw) : [];
list.push(feedback);
localStorage.setItem(FEEDBACK_KEY, JSON.stringify(list));
}
export function getFeedbackList() {
const raw = localStorage.getItem(FEEDBACK_KEY);
return raw ? JSON.parse(raw) : [];
}

View File

@ -0,0 +1,42 @@
import axios from 'axios';
import { Task, Column } from '../components/Kanban/types';
const API_BASE_URL = '/api/kanban';
export const kanbanService = {
// 获取看板数据
getKanbanBoard: async (projectId: string): Promise<Column[]> => {
const response = await axios.get(`${API_BASE_URL}/board/${projectId}`);
return response.data;
},
// 创建新任务
createTask: async (projectId: string, task: Omit<Task, 'id'>): Promise<Task> => {
const response = await axios.post(`${API_BASE_URL}/task/${projectId}`, task);
return response.data;
},
// 更新任务
updateTask: async (projectId: string, taskId: string, task: Partial<Task>): Promise<Task> => {
const response = await axios.put(`${API_BASE_URL}/task/${projectId}/${taskId}`, task);
return response.data;
},
// 删除任务
deleteTask: async (projectId: string, taskId: string): Promise<void> => {
await axios.delete(`${API_BASE_URL}/task/${projectId}/${taskId}`);
},
// 更新任务状态
updateTaskStatus: async (
projectId: string,
taskId: string,
newStatus: string,
newIndex: number
): Promise<void> => {
await axios.put(`${API_BASE_URL}/task/${projectId}/${taskId}/status`, {
status: newStatus,
index: newIndex,
});
},
};

View File

@ -0,0 +1,37 @@
const STORAGE_KEY = 'starred_docs';
function getStarredSet(): Set<string> {
const raw = localStorage.getItem(STORAGE_KEY);
return new Set(raw ? JSON.parse(raw) : []);
}
function saveStarredSet(set: Set<string>) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(Array.from(set)));
}
export const starService = {
// 获取收藏状态
getStarStatus: async (itemId: string): Promise<boolean> => {
const set = getStarredSet();
return set.has(itemId);
},
// 添加收藏
addStar: async (itemId: string): Promise<void> => {
const set = getStarredSet();
set.add(itemId);
saveStarredSet(set);
},
// 取消收藏
removeStar: async (itemId: string): Promise<void> => {
const set = getStarredSet();
set.delete(itemId);
saveStarredSet(set);
},
// 获取收藏列表
getStarredItems: async (): Promise<string[]> => {
return Array.from(getStarredSet());
},
};

View File

@ -0,0 +1,56 @@
import React from 'react';
import {useDoc} from '@docusaurus/theme-common/internal';
import DocItemContent from '@theme-original/DocItem/Content';
// import type {Props} from '@theme/DocItem/Content';
import StarButton from '../../../components/StarButton/StarButton';
import {starService} from '../../../services/starService';
import '../styles.css';
type Props = React.ComponentProps<typeof DocItemContent>;
export default function DocItemContentWrapper(props: Props): JSX.Element {
const {metadata} = useDoc();
const [starred, setStarred] = React.useState(false);
const docPath = metadata.permalink;
React.useEffect(() => {
const checkStarStatus = async () => {
try {
const status = await starService.getStarStatus(docPath);
console.log('获取收藏状态', docPath, status);
setStarred(status);
} catch (error) {
console.error('获取收藏状态失败:', error);
}
};
checkStarStatus();
}, [docPath]);
const handleStarChange = async (newStarred: boolean) => {
try {
if (newStarred) {
await starService.addStar(docPath);
} else {
await starService.removeStar(docPath);
}
const status = await starService.getStarStatus(docPath);
setStarred(status);
// 派发自定义事件,通知导航栏刷新
window.dispatchEvent(new Event('starred-changed'));
} catch (error) {
console.error('更新收藏状态失败:', error);
}
};
return (
<div className="doc-item-wrapper">
<div className="doc-star-button">
<StarButton
starred={starred}
onStarChange={handleStarChange}
/>
</div>
<DocItemContent {...props} />
</div>
);
}

View File

@ -0,0 +1,22 @@
import React from 'react';
import DocItem from '@theme-original/DocItem';
import type DocItemType from '@theme/DocItem';
import type { WrapperProps } from '@docusaurus/types';
import DocNoteBox from '../../components/DocNoteBox';
import { useLocation } from '@docusaurus/router';
type Props = WrapperProps<typeof DocItemType>;
export default function DocItemWrapper(props: Props): JSX.Element {
const location = useLocation();
const docPath = location.pathname;
return (
<div style={{ display: 'flex', gap: '20px' }}>
<div style={{ flex: 1 }}>
<DocItem {...props} />
</div>
<DocNoteBox docPath={docPath} />
</div>
);
}

View File

@ -0,0 +1,18 @@
.doc-item-wrapper {
position: relative;
}
.doc-star-button {
position: absolute;
top: 1rem;
right: 1rem;
z-index: 1;
}
/* 确保收藏按钮在移动设备上也能正常显示 */
@media (max-width: 996px) {
.doc-star-button {
top: 0.5rem;
right: 0.5rem;
}
}

View File

@ -0,0 +1,28 @@
import React from 'react';
import { useThemeConfig } from '@docusaurus/theme-common';
import { useColorMode } from '@docusaurus/theme-common';
import { translate } from '@docusaurus/Translate';
import { IconDarkMode, IconLightMode } from '@docusaurus/theme-common';
import { IconEye } from '@docusaurus/theme-common';
export default function ThemeToggle(): JSX.Element {
const { colorMode, setColorMode } = useColorMode();
const { darkMode, respectPrefersColorScheme } = useThemeConfig();
const toggleTheme = () => {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme');
if (currentTheme === 'eye-care') {
html.setAttribute('data-theme', 'light');
} else {
html.setAttribute('data-theme', 'eye-care');
}
};
return (
<div className="theme-toggle-button" onClick={toggleTheme} style={{ display: 'flex', alignItems: 'center', padding: '0.5rem', cursor: 'pointer' }}>
<IconEye className="theme-toggle-icon" style={{ width: '20px', height: '20px', marginRight: '0.5rem' }} />
<span></span>
</div>
);
}

View File

@ -0,0 +1,6 @@
import React from 'react';
import ThemeToggle from '@site/src/components/ThemeToggle';
export default function ThemeToggleNavbarItem() {
return <ThemeToggle />;
}

View File

@ -0,0 +1,7 @@
import ComponentTypes from '@theme-original/NavbarItems/ComponentTypes';
import ThemeToggleNavbarItem from '@site/src/theme/NavbarItems/ThemeToggleNavbarItem';
export default {
...ComponentTypes,
'theme-toggle': ThemeToggleNavbarItem,
};

29
src/types/html2pdf.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
declare module 'html2pdf.js' {
interface Html2PdfOptions {
margin?: number;
filename?: string;
image?: {
type?: string;
quality?: number;
};
html2canvas?: {
scale?: number;
useCORS?: boolean;
letterRendering?: boolean;
};
jsPDF?: {
unit?: string;
format?: string;
orientation?: 'portrait' | 'landscape';
};
}
interface Html2PdfInstance {
set(options: Html2PdfOptions): Html2PdfInstance;
from(element: HTMLElement): Html2PdfInstance;
save(): Promise<void>;
}
function html2pdf(): Html2PdfInstance;
export = html2pdf;
}

17
static/js/theme-toggle.js Normal file
View File

@ -0,0 +1,17 @@
document.addEventListener('click', function (e) {
var btn = e.target.closest('#theme-toggle');
if (btn) {
var html = document.documentElement;
var current = html.getAttribute('data-theme');
if (current === 'eye-care') {
html.setAttribute('data-theme', 'light');
} else {
html.setAttribute('data-theme', 'eye-care');
}
}
});
// 初次绑定
window.addEventListener('DOMContentLoaded', bindEyeCareBtn);
// 之后每500ms尝试绑定一次兼容按钮动态渲染
setInterval(bindEyeCareBtn, 500);

24
tsconfig.json Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"paths": {
"@site/*": ["src/*"]
}
},
"include": ["src/", "docusaurus.config.js"],
"exclude": ["node_modules"]
}

View File

@ -69,26 +69,6 @@ GitLink确实开源是CCF官方指定的开源创新服务平台旨在
<h2 class="text--truncate cardTitle" title="第三方服务">第三方服务</h2>
<p>第三方服务使用及设置[3个文档]</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/通知/通知简介">
<h2 class="text--truncate cardTitle" title="通知">通知</h2>
<p>通知使用及设置[2个文档]</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/个人主页建站/站点创建流程">
<h2 class="text--truncate cardTitle" title="个人主页建站">个人主页建站</h2>
<p>个人主页建站使用及设置[2个文档]</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/平台公告/">
<h2 class="text--truncate cardTitle" title="平台公告">平台公告</h2>
<p>平台公告使用及设置</p>
</a></article>
<article class="col col--6 margin-bottom--lg">
<a class="card padding--lg cardContainer" href="/服务协议/GitLink服务协议">
<h2 class="text--truncate cardTitle" title="服务协议">服务协议</h2>
<p>服务协议使用及设置[1个文档]</p>
</a></article>
</section>
</div>
</div>

View File

@ -1,4 +1,11 @@
---
sidebar_label: '代码提交'
sidebar_position: 3
---
---
### **1. 代码提交入口**
用户可以通过项目代码库中间的文件下的"上传文件"按键来上传文件。
![](../../../static/img/代码库管理/代码提交/上传文件入口.png)<br/>
### **2. 上传文件**
进入上传文件页面后,如下图所示,拖动文件,选择提交变更的分支的单击"创建项目"按键即可完成创建。
![](../../../static/img/代码库管理/代码提交/上传文件.png)<br/>

View File

@ -1,4 +1,13 @@
---
sidebar_label: '分支管理'
sidebar_position: 5
---
---
### **1. 查看分支**
在代码库栏下,如下图所示,用户可以点击代码库分支从而查看当前项目的所有分支,并且选择对其进行操作。
![](../../../static/img/代码库管理/分支管理/分支管理方式1.jpg)<br/>
### **2. 合并分支**
在"和并请求PR"下点击"新建合并请求"后,
![](../../../static/img/代码库管理/分支管理/分支管理方式3.png)<br/>
如下图所示,用户可以选择源分支与目标分支以及填写分支信息后提交分支合并请求。
![](../../../static/img/代码库管理/分支管理/分支管理方式2.png)<br/>

View File

@ -1,4 +1,19 @@
---
sidebar_label: '文件管理'
sidebar_position: 4
---
---
### **1. 文件管理位置**
在代码库界面,可直接对代码库中该分支下的文件进行操作,如下图所示。
![](../../../static/img/代码库管理/文件管理/文件管理入口.png)<br/>
### **2. 进入目录**
点击进入相关目录,找到要编辑的文件,如下图所示。
![](../../../static/img/代码库管理/文件管理/进入文件所在目录.png)<br/>
### **3. 进入文件编辑页面**
在线编辑文件,单击文件,即可进入编辑文件页面,如下图所示。
![](../../../static/img/代码库管理/文件管理/进入文件编辑界面.png)<br/>
### **4. 编辑文件**
编辑界面右侧有三个按钮,分别是"下载"、"编辑"和"删除",点击下载即可将文件下载到本地,编辑则可在线编辑文档,删除则将文件从代码库中删除,按钮位置如下图所示。
![](../../../static/img/代码库管理/文件管理/文件编辑按钮.png)<br/>

View File

@ -3,4 +3,11 @@ sidebar_label: '代码评审'
sidebar_position: 3
---
# 代码评审
# 代码评审
### **1. 进入代码评审**
如下图所示,点击“代码评审”按钮可以进入代码评审
![](/img/PR/进入代码评审.png)<br/>
### **2. 代码评审界面**
如下图所示用户可以在该界面下查看该PR操作要合并的新的代码并对其进行评审。
![](/img/PR/具体代码评审.png)<br/>

View File

@ -15,6 +15,8 @@ sidebar_position: 1
![](/img/quickstart/register_phone.png)
> **注意**:由于网络或运营商原因,手机号注册时可能出现不发送验证码的情况。建议优先使用邮箱注册方式。
- 邮箱注册
![](/img/quickstart/register_email.png)

View File

@ -18,6 +18,30 @@ GitLink默认疑修共有缺陷、功能、疑问、支持、任务、协助、
- **文档:** 表示文档材料补充;
- **测试:** 表示需要测试的需求;
- **重复:** 表示已存在类似的疑修。
## 如何区分和使用疑修类别
1. **选择合适的标记类型**
- 根据疑修的性质和目的选择对应的标记类型
- 例如如果是报告一个bug应选择"缺陷"标记;如果是提出新功能建议,应选择"功能"标记
2. **标记的使用场景**
- **缺陷标记**用于报告代码中的bug、错误或异常行为
- **功能标记**:用于提出新功能建议或功能改进
- **疑问标记**:用于提出对代码或功能的疑问
- **支持标记**:用于提出对特定功能的支持请求
- **任务标记**:用于分配具体的开发任务
- **协助标记**:用于请求社区帮助解决问题
- **搁置标记**:用于标记暂时不需要处理的问题
- **文档标记**:用于标记需要补充或修改文档的问题
- **测试标记**:用于标记需要测试的功能或问题
- **重复标记**:用于标记重复的问题
3. **标记的管理**
- 在疑修编辑页面可以添加或移除标记
- 一个疑修可以同时拥有多个标记
- 项目成员可以根据项目需求自定义标记的颜色和含义
![](/img/Issue/imageIssue14.png)
另外,**项目成员**可以根据需求或习惯,进行标记含义或颜色标志的修改、新建标记和删除标记操作。

View File

@ -1,4 +1,21 @@
---
sidebar_label: '组织成员管理'
sidebar_position: 4
---
---
# 成员管理Members Management
在 个人所管理的项目当中的**仓库设置**当中的**成员管理**可以进入成员管理界面
![](../../../static/img/Org/imageOrg14.png)
## 成员查询和添加
在成员管理界面当中,可以进行组织成员的查询和添加
![](../../../static/img/Org/imageOrg14.png)
## 成员邀请权限设置
管理员可以对邀请成员的权限进行设置
![](../../../static/img/Org/imageOrg15.png)
此外,也可以复制邀请链接的方式来更方便地邀请成员加入到项目当中来
**注**:此项功能仅对管理员可见

View File

@ -1,4 +1,12 @@
---
sidebar_label: '模板导入及导出'
sidebar_position: 2
---
---
## 模板的导入及导出管理
#### 1.模板导入
通过点击wiki界面的**导入模板**按钮即可导入本地的txtmarkdown等格式文件进行Wiki文档的创建当前平台此项功能尚在开发测试环节
[](/img/wiki/img4.png)
#### 2.模板导出
当前平台此项功能尚在开发测试环节
<br/>

View File

@ -1,4 +1,22 @@
---
sidebar_label: '维基页面管理'
sidebar_position: 1
---
---
# 确实开源帮助中心
## 维基界面管理
#### 1.维基界面
GitLink项目安排了专门的**维基(Wiki)界面**来展示项目的一些情况
[](/img/wiki/img1.png)
#### 2.创建维基内容
在初始化的维基界面当中点击**创建Wiki文档**或者**导入模板**即可进行Wiki文档的编辑
<br/>
#### 3.进行Wiki文档的编辑
点击**创建Wiki文档**后即跳转到编辑界面,初始内容为空白的文档
[](/img/wiki/img3.png)
也可以通过勾选*添加模板*来进行模板的导入,平台提供的模板包括*周报*和*月报*的格式
[](/img/wiki/img2.png)
完成编辑后点击左下角的的保存即可

8868
yarn.lock Normal file

File diff suppressed because it is too large Load Diff