Compare commits
124 Commits
Author | SHA1 | Date |
---|---|---|
|
e85f29aeb4 | 1 month ago |
|
fc02d3107e | 2 months ago |
|
27fac0685a | 2 months ago |
|
34e2a6f6d2 | 2 months ago |
|
949ac8c8eb | 2 months ago |
|
a203ba9c1f | 2 months ago |
|
fdf819757f | 2 months ago |
|
b3e3fabeb5 | 2 months ago |
|
9b66afe68a | 2 months ago |
|
4c117beaab | 2 months ago |
|
856f81df29 | 2 months ago |
|
656ba585f3 | 2 months ago |
|
46205e3ab9 | 2 months ago |
|
3490b7f029 | 2 months ago |
|
c6b6b2a109 | 2 months ago |
|
481100687b | 2 months ago |
|
be14099289 | 2 months ago |
|
a80b13901d | 2 months ago |
|
cb90fd41b0 | 2 months ago |
|
da271a45ae | 2 months ago |
|
2f28719e38 | 2 months ago |
|
a49cdf682e | 2 months ago |
|
b98abe886f | 2 months ago |
|
faf4222020 | 2 months ago |
|
76c106bf53 | 2 months ago |
|
39deb5e072 | 2 months ago |
|
b86dda189d | 2 months ago |
|
2f7e0439c9 | 2 months ago |
|
69f1ebf775 | 2 months ago |
|
14957d1ad4 | 2 months ago |
|
3c6d3e943a | 2 months ago |
|
7690adac20 | 2 months ago |
|
87eeb1a7c0 | 2 months ago |
|
7ed6d428d9 | 2 months ago |
|
ba0a06136e | 2 months ago |
|
2f116c4b4f | 2 months ago |
|
620ca67caa | 2 months ago |
|
dc94d43538 | 2 months ago |
|
a134f85b10 | 2 months ago |
|
6d2cd1c295 | 2 months ago |
|
5121b71bfc | 2 months ago |
|
baa6af0cc3 | 2 months ago |
|
510e1e0b8f | 2 months ago |
|
375e18b206 | 2 months ago |
|
0c69f446f8 | 2 months ago |
|
d4a36b6b19 | 2 months ago |
|
e9ad55b137 | 2 months ago |
|
85023d6df3 | 2 months ago |
|
6b970d4f3b | 2 months ago |
|
8c1feb73f2 | 2 months ago |
|
7f1bf457e5 | 2 months ago |
|
0f6559b8d7 | 2 months ago |
|
775136ffa3 | 2 months ago |
|
d665adbe35 | 2 months ago |
|
c6b1177f72 | 2 months ago |
|
8ea1899218 | 2 months ago |
|
f69f74f594 | 2 months ago |
|
8847aa7198 | 2 months ago |
|
f7181a9235 | 2 months ago |
|
7c79d9ffc0 | 2 months ago |
|
09db624b06 | 2 months ago |
|
69847f82a0 | 2 months ago |
|
edc2fb952f | 2 months ago |
|
3ed0e19f31 | 2 months ago |
|
48dc528398 | 2 months ago |
|
cfb9c7c111 | 2 months ago |
|
b1eeac0acf | 2 months ago |
|
877cde3f82 | 2 months ago |
|
ce3c34556e | 2 months ago |
|
93e59a14b9 | 2 months ago |
|
4759fe4d28 | 2 months ago |
|
5403ecf2fd | 2 months ago |
|
63f9a443dd | 2 months ago |
|
bf4e36fd04 | 2 months ago |
|
8bf72069f5 | 2 months ago |
|
ce40ca84b8 | 2 months ago |
|
7fb917e071 | 2 months ago |
|
13f9e9a8bd | 2 months ago |
|
fdedb16346 | 2 months ago |
|
99fee2d869 | 2 months ago |
|
5784be40c5 | 2 months ago |
|
0d79380794 | 2 months ago |
|
ceb5be3ea5 | 2 months ago |
|
98cf4f6fa5 | 2 months ago |
|
9b948cb095 | 2 months ago |
|
3f1a95ac9a | 2 months ago |
|
6a3a54f33a | 2 months ago |
|
5c93b16865 | 2 months ago |
|
677f6d76a3 | 2 months ago |
|
255555e5cd | 2 months ago |
|
c925b89030 | 2 months ago |
|
f2573a0f14 | 2 months ago |
|
48ab97b6ee | 2 months ago |
|
72dcad6c60 | 2 months ago |
|
184d493fd4 | 2 months ago |
|
c609fb8673 | 2 months ago |
|
374e26eeac | 2 months ago |
|
6067435fcf | 2 months ago |
|
f8d14203c8 | 2 months ago |
|
6b72047c6c | 2 months ago |
|
fbed280614 | 2 months ago |
|
5b6f75850d | 2 months ago |
|
32eb9fc0e4 | 2 months ago |
|
ad342ff88c | 2 months ago |
|
8811c6be21 | 2 months ago |
|
1a0bc92051 | 2 months ago |
|
d052f3ec3e | 2 months ago |
|
5aa7149805 | 2 months ago |
|
792009a3f7 | 2 months ago |
|
41a73aa2cc | 2 months ago |
|
0dc560a630 | 2 months ago |
|
341c4db025 | 2 months ago |
|
fd47561fb5 | 2 months ago |
|
0759b9c22e | 2 months ago |
|
191e58d1ac | 2 months ago |
|
4450695560 | 2 months ago |
|
ae4854bbd4 | 2 months ago |
|
c9457f7f68 | 2 months ago |
|
846783844c | 2 months ago |
|
d239e4e8d0 | 2 months ago |
|
9835107084 | 2 months ago |
|
e7369befaa | 2 months ago |
|
3795352762 | 2 months ago |
|
0575395cb1 | 2 months ago |
@ -0,0 +1,62 @@
|
||||
version: 2
|
||||
name: feng
|
||||
description: ""
|
||||
global:
|
||||
concurrent: 1
|
||||
trigger:
|
||||
webhook: gitlink@1.0.0
|
||||
event:
|
||||
- ref: push
|
||||
ruleset-operator: AND
|
||||
workflow:
|
||||
- ref: start
|
||||
name: 开始
|
||||
task: start
|
||||
- ref: git_clone_0
|
||||
name: git clone
|
||||
task: git_clone@1.2.9
|
||||
input:
|
||||
username: ((user.name))
|
||||
password: ((mima.mima))
|
||||
remote_url: '"https://gitlink.org.cn/fengyujue/gitlink_help_center.git"'
|
||||
ref: '"refs/heads/master"'
|
||||
commit_id: '""'
|
||||
depth: 1
|
||||
needs:
|
||||
- start
|
||||
- ref: end
|
||||
name: 结束
|
||||
task: end
|
||||
needs:
|
||||
- ssh_cmd_0
|
||||
- ref: docker_image_build_0
|
||||
name: docker镜像构建
|
||||
task: docker_image_build@1.6.0
|
||||
input:
|
||||
docker_username: ((docker.name))
|
||||
docker_password: ((docker.mima))
|
||||
image_name: '"crpi-ybi767umyzh9owc0.cn-hangzhou.personal.cr.aliyuncs.com/help-center-gitlink/gitlink_help_gitlink"'
|
||||
image_tag: '"latest"'
|
||||
registry_address: '"crpi-ybi767umyzh9owc0.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: ssh_cmd_0
|
||||
name: ssh执行命令
|
||||
task: ssh_cmd@1.1.1
|
||||
input:
|
||||
ssh_pass: ((ssh.password))
|
||||
ssh_ip: '"120.27.137.186"'
|
||||
ssh_port: '"22"'
|
||||
ssh_user: '"root"'
|
||||
ssh_cmd: '"docker stop group8 || true && docker rm group8 || true && docker pull
|
||||
crpi-ybi767umyzh9owc0.cn-hangzhou.personal.cr.aliyuncs.com/help-center-gitlink/gitlink_help_gitlink:latest
|
||||
&& docker run -d -p 3000:3000 --name group8
|
||||
crpi-ybi767umyzh9owc0.cn-hangzhou.personal.cr.aliyuncs.com/help-center-gitlink/gitlink_help_gitlink:latest"'
|
||||
needs:
|
||||
- docker_image_build_0
|
||||
|
@ -0,0 +1,63 @@
|
||||
version: 2
|
||||
name: 流水线
|
||||
description: ""
|
||||
global:
|
||||
concurrent: 1
|
||||
trigger:
|
||||
webhook: gitlink@1.0.0
|
||||
event:
|
||||
- ref: push
|
||||
ruleset-operator: AND
|
||||
workflow:
|
||||
- ref: start
|
||||
name: 开始
|
||||
task: start
|
||||
- ref: git_clone_0
|
||||
name: git clone
|
||||
task: git_clone@1.2.9
|
||||
input:
|
||||
username: ((user.name))
|
||||
password: ((mima.mima))
|
||||
remote_url: '"https://gitlink.org.cn/fengyujue/gitlink_help_center.git"'
|
||||
ref: '"refs/heads/wangfengzhi"'
|
||||
commit_id: '""'
|
||||
depth: 1
|
||||
needs:
|
||||
- start
|
||||
- ref: end
|
||||
name: 结束
|
||||
task: end
|
||||
needs:
|
||||
- ssh_cmd_0
|
||||
- ref: docker_image_build_0
|
||||
name: docker镜像构建
|
||||
task: docker_image_build@1.6.0
|
||||
input:
|
||||
docker_username: ((docker.name))
|
||||
docker_password: ((docker.mima))
|
||||
image_name: '"crpi-ybi767umyzh9owc0.cn-hangzhou.personal.cr.aliyuncs.com/help-center-gitlink/gitlink_help_gitlink"'
|
||||
image_tag: '"latest"'
|
||||
registry_address: '"crpi-ybi767umyzh9owc0.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: ssh_cmd_0
|
||||
name: ssh执行命令
|
||||
task: ssh_cmd@1.1.1
|
||||
input:
|
||||
ssh_pass: ((ssh.password))
|
||||
ssh_ip: '"120.27.137.186"'
|
||||
ssh_port: '"22"'
|
||||
ssh_user: '"root"'
|
||||
ssh_cmd: docker stop gitlink_help_center || true && docker rm
|
||||
gitlink_help_center || true && docker pull
|
||||
crpi-ybi767umyzh9owc0.cn-hangzhou.personal.cr.aliyuncs.com/help-center-gitlink/gitlink_help_gitlink:latest;
|
||||
docker run -d -p 3000:3000 --name gitlink_help_center
|
||||
crpi-ybi767umyzh9owc0.cn-hangzhou.personal.cr.aliyuncs.com/help-center-gitlink/gitlink_help_gitlink:latest
|
||||
needs:
|
||||
- docker_image_build_0
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "合并请求(PR)",
|
||||
"position": 5
|
||||
"label": "合并请求",
|
||||
"position": 2,
|
||||
"className": "merge-request"
|
||||
}
|
||||
|
@ -1,6 +1,67 @@
|
||||
---
|
||||
id: merge-request-issue
|
||||
title: 合并请求关联疑修
|
||||
sidebar_label: '合并请求关联疑修'
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# 合并请求关联疑修
|
||||
|
||||
## 功能介绍
|
||||
|
||||
合并请求关联疑修功能允许您在创建或编辑合并请求时,将相关的疑修与合并请求建立关联。这样可以帮助团队成员更好地追踪代码变更与疑修之间的关系,提高开发效率。
|
||||
|
||||
## 如何关联疑修
|
||||
|
||||
### 方法一:在合并请求描述中引用
|
||||
|
||||
1. 在创建或编辑合并请求时,在描述中使用 `#` 符号引用疑修编号
|
||||
2. 例如:`修复 #123 中提到的登录问题`
|
||||
3. 系统会自动识别并建立关联
|
||||
|
||||
### 方法二:在疑修详情页关联
|
||||
|
||||
1. 打开疑修详情页面
|
||||
2. 点击"关联合并请求"按钮
|
||||
3. 选择要关联的合并请求
|
||||
4. 确认关联
|
||||
|
||||
## 关联后的效果
|
||||
|
||||
1. 在疑修详情页面可以看到关联的合并请求列表
|
||||
2. 在合并请求详情页面可以看到关联的疑修信息
|
||||
3. 当合并请求被合并时,系统会自动更新疑修状态
|
||||
4. 可以通过关联关系快速追踪代码变更
|
||||
|
||||
## 管理关联关系
|
||||
|
||||
### 查看关联
|
||||
|
||||
1. 在疑修详情页面的"关联合并请求"部分查看
|
||||
2. 在合并请求详情页面的"关联疑修"部分查看
|
||||
|
||||
### 解除关联
|
||||
|
||||
1. 在疑修详情页面:
|
||||
- 找到要解除关联的合并请求
|
||||
- 点击"解除关联"按钮
|
||||
- 确认操作
|
||||
|
||||
2. 在合并请求详情页面:
|
||||
- 找到要解除关联的疑修
|
||||
- 点击"解除关联"按钮
|
||||
- 确认操作
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 在创建合并请求时,及时关联相关的疑修
|
||||
2. 使用清晰的描述说明合并请求与疑修的关系
|
||||
3. 定期检查关联关系,确保其准确性
|
||||
4. 在合并请求被合并后,及时确认疑修状态
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 一个合并请求可以关联多个疑修
|
||||
2. 一个疑修可以关联多个合并请求
|
||||
3. 关联关系建立后可以随时修改
|
||||
4. 解除关联不会影响已合并的代码变更
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "平台公告",
|
||||
"position": 99
|
||||
"position": 1,
|
||||
"className": "platform-announcement"
|
||||
}
|
||||
|
@ -1,4 +1,87 @@
|
||||
---
|
||||
id: platform-announcement
|
||||
title: 平台公告
|
||||
sidebar_label: '平台公告'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# 平台公告
|
||||
|
||||
## 功能介绍
|
||||
|
||||
平台公告功能允许管理员发布和管理平台相关的通知、更新和重要信息。通过平台公告,可以及时向用户传达重要信息,确保用户了解平台的最新动态。
|
||||
|
||||
## 创建平台公告
|
||||
|
||||
1. 进入平台公告管理页面
|
||||
2. 点击"新建公告"按钮
|
||||
3. 填写公告标题和内容
|
||||
4. 设置公告类型和优先级
|
||||
5. 选择发布范围
|
||||
6. 点击"发布"按钮
|
||||
|
||||
## 公告类型
|
||||
|
||||
平台公告支持以下类型:
|
||||
|
||||
- 系统更新:平台功能更新和系统维护通知
|
||||
- 功能发布:新功能发布和功能介绍
|
||||
- 活动通知:平台活动、比赛和培训通知
|
||||
- 重要提醒:安全提醒和重要政策变更
|
||||
- 其他通知:其他类型的平台通知
|
||||
|
||||
## 公告管理
|
||||
|
||||
### 编辑公告
|
||||
|
||||
1. 在公告列表中找到要编辑的公告
|
||||
2. 点击"编辑"按钮
|
||||
3. 修改公告内容
|
||||
4. 点击"保存"按钮
|
||||
|
||||
### 删除公告
|
||||
|
||||
1. 在公告列表中找到要删除的公告
|
||||
2. 点击"删除"按钮
|
||||
3. 确认删除操作
|
||||
|
||||
### 公告状态
|
||||
|
||||
公告可能处于以下状态:
|
||||
|
||||
- 草稿:未发布的公告
|
||||
- 已发布:正在显示的公告
|
||||
- 已过期:超过显示期限的公告
|
||||
- 已归档:已归档的公告
|
||||
|
||||
## 公告显示
|
||||
|
||||
### 显示位置
|
||||
|
||||
1. 平台首页公告栏
|
||||
2. 用户个人中心
|
||||
3. 相关功能页面
|
||||
|
||||
### 显示规则
|
||||
|
||||
1. 按优先级排序显示
|
||||
2. 按发布时间倒序排列
|
||||
3. 支持置顶显示
|
||||
4. 支持定时发布和自动过期
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 公告标题要简洁明了
|
||||
2. 内容要清晰易懂
|
||||
3. 重要信息要突出显示
|
||||
4. 及时更新和删除过期公告
|
||||
5. 合理设置公告优先级
|
||||
6. 注意公告的时效性
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 公告内容要符合平台规范
|
||||
2. 避免发布重复或过时的信息
|
||||
3. 重要公告要及时通知用户
|
||||
4. 定期清理过期公告
|
||||
5. 注意公告的权限管理
|
@ -0,0 +1 @@
|
||||
<center>该功能无法使用</center><br/>
|
@ -0,0 +1,56 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function BackToTop() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
// 监听滚动事件
|
||||
useEffect(() => {
|
||||
const toggleVisibility = () => {
|
||||
if (window.pageYOffset > 300) {
|
||||
setIsVisible(true);
|
||||
} else {
|
||||
setIsVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', toggleVisibility);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', toggleVisibility);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 滚动到顶部
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible && (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className={styles.backToTop}
|
||||
aria-label="返回顶部"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M12 19V5M5 12l7-7 7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
.backToTop {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--ifm-color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.backToTop:hover {
|
||||
background-color: var(--ifm-color-primary-darker);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.backToTop:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.backToTop {
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function Bookmark({ docId }) {
|
||||
const [isBookmarked, setIsBookmarked] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 从localStorage中读取标记状态
|
||||
const bookmarks = JSON.parse(localStorage.getItem('docBookmarks') || '{}');
|
||||
setIsBookmarked(bookmarks[docId] || false);
|
||||
}, [docId]);
|
||||
|
||||
const toggleBookmark = (e) => {
|
||||
e.stopPropagation(); // 阻止事件冒泡
|
||||
const bookmarks = JSON.parse(localStorage.getItem('docBookmarks') || '{}');
|
||||
const newState = !isBookmarked;
|
||||
bookmarks[docId] = newState;
|
||||
localStorage.setItem('docBookmarks', JSON.stringify(bookmarks));
|
||||
setIsBookmarked(newState);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggleBookmark}
|
||||
className={`${styles.bookmarkButton} ${isBookmarked ? styles.bookmarked : ''}`}
|
||||
aria-label={isBookmarked ? '取消标记' : '添加标记'}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill={isBookmarked ? 'currentColor' : 'none'}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
.bookmarkButton {
|
||||
position: absolute;
|
||||
left: -20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
transition: all 0.2s ease;
|
||||
opacity: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.bookmarkButton:hover {
|
||||
color: var(--ifm-color-primary);
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
.bookmarked {
|
||||
color: var(--ifm-color-primary);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 当鼠标悬停在侧边栏项目上时显示标记按钮 */
|
||||
:global(.menu__link-wrapper:hover) .bookmarkButton {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 已标记的项目始终显示标记按钮 */
|
||||
:global(.menu__link-wrapper) .bookmarked {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 确保侧边栏项目有足够的空间显示标记按钮 */
|
||||
:global(.menu__link-wrapper) {
|
||||
padding-left: 25px !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* 调整列表项样式 */
|
||||
:global(.menu__list-item) {
|
||||
position: relative !important;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function DocBookmark({ docId }) {
|
||||
const [isBookmarked, setIsBookmarked] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 从localStorage中读取标记状态
|
||||
const bookmarks = JSON.parse(localStorage.getItem('docBookmarks') || '{}');
|
||||
setIsBookmarked(bookmarks[docId] || false);
|
||||
}, [docId]);
|
||||
|
||||
const toggleBookmark = () => {
|
||||
const bookmarks = JSON.parse(localStorage.getItem('docBookmarks') || '{}');
|
||||
const newState = !isBookmarked;
|
||||
bookmarks[docId] = newState;
|
||||
localStorage.setItem('docBookmarks', JSON.stringify(bookmarks));
|
||||
setIsBookmarked(newState);
|
||||
|
||||
// 触发自定义事件,通知侧边栏更新
|
||||
const event = new CustomEvent('bookmarkChanged', {
|
||||
detail: { docId, isBookmarked: newState }
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.bookmarkContainer}>
|
||||
<button
|
||||
onClick={toggleBookmark}
|
||||
className={`${styles.bookmarkButton} ${isBookmarked ? styles.bookmarked : ''}`}
|
||||
aria-label={isBookmarked ? '取消标记' : '添加标记'}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
height="20"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill={isBookmarked ? 'currentColor' : 'none'}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
||||
</svg>
|
||||
<span className={styles.bookmarkText}>
|
||||
{isBookmarked ? '取消标记' : '添加标记'}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
.bookmarkContainer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--ifm-color-emphasis-200);
|
||||
}
|
||||
|
||||
.bookmarkButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--ifm-color-emphasis-300);
|
||||
border-radius: 4px;
|
||||
background: var(--ifm-background-color);
|
||||
color: var(--ifm-color-emphasis-700);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.bookmarkButton:hover {
|
||||
background: var(--ifm-color-emphasis-100);
|
||||
border-color: var(--ifm-color-primary);
|
||||
color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
.bookmarked {
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
border-color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
.bookmarked:hover {
|
||||
background: var(--ifm-color-primary-darker);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bookmarkText {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
.commentSection {
|
||||
margin-top: 3rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--ifm-color-emphasis-200);
|
||||
}
|
||||
|
||||
.commentTitle {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
}
|
||||
|
||||
.commentList {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.commentItem {
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
background-color: var(--ifm-background-color);
|
||||
}
|
||||
|
||||
.commentHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.commentAuthor {
|
||||
font-weight: 500;
|
||||
color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
.commentTime {
|
||||
font-size: 0.875rem;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
}
|
||||
|
||||
.deleteButton {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
cursor: pointer;
|
||||
padding: 0 4px;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.deleteButton:hover {
|
||||
color: var(--ifm-color-danger);
|
||||
}
|
||||
|
||||
.commentText {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
}
|
||||
|
||||
.commentInput {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.commentInput textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 8px;
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.commentInput textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
align-self: flex-end;
|
||||
padding: 0.5rem 1.5rem;
|
||||
background-color: var(--ifm-color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.submitButton:hover {
|
||||
background-color: var(--ifm-color-primary-darker);
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
import React, { useState } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function DocRating({ docId }) {
|
||||
const [hasRated, setHasRated] = useState(false);
|
||||
const [showMessage, setShowMessage] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
const handleRating = (isPositive) => {
|
||||
// 保存评级到localStorage
|
||||
const ratings = JSON.parse(localStorage.getItem('docRatings') || '{}');
|
||||
ratings[docId] = isPositive;
|
||||
localStorage.setItem('docRatings', JSON.stringify(ratings));
|
||||
|
||||
// 显示相应的消息
|
||||
setMessage(isPositive
|
||||
? '感谢您的评价,祝您使用愉快'
|
||||
: '不好意思,给您带来不好的体验,我们一定会积极改进'
|
||||
);
|
||||
setShowMessage(true);
|
||||
setHasRated(true);
|
||||
|
||||
// 5秒后隐藏消息
|
||||
setTimeout(() => {
|
||||
setShowMessage(false);
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
if (hasRated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.ratingSection}>
|
||||
<p className={styles.ratingText}>请您对本文档做出评价</p>
|
||||
<div className={styles.ratingButtons}>
|
||||
<button
|
||||
onClick={() => handleRating(true)}
|
||||
className={`${styles.ratingButton} ${styles.positiveButton}`}
|
||||
>
|
||||
好
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleRating(false)}
|
||||
className={`${styles.ratingButton} ${styles.negativeButton}`}
|
||||
>
|
||||
不好
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{showMessage && (
|
||||
<>
|
||||
<div className={styles.messageOverlay} />
|
||||
<div className={styles.message}>
|
||||
{message}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
.ratingSection {
|
||||
margin-top: 2rem;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 8px;
|
||||
background-color: var(--ifm-background-color);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ratingText {
|
||||
font-size: 1.1rem;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ratingButtons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.ratingButton {
|
||||
padding: 0.5rem 2rem;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.positiveButton {
|
||||
background-color: var(--ifm-color-success);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.positiveButton:hover {
|
||||
background-color: var(--ifm-color-success-darker);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.negativeButton {
|
||||
background-color: var(--ifm-color-danger);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.negativeButton:hover {
|
||||
background-color: var(--ifm-color-danger-darker);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.messageOverlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.message {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-radius: 8px;
|
||||
background-color: var(--ifm-background-color);
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
font-size: 1.1rem;
|
||||
text-align: center;
|
||||
min-width: 300px;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -40%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import DocItem from '@theme-original/DocItem';
|
||||
import DocComment from '@site/src/components/DocComment';
|
||||
import DocRating from '@site/src/components/DocRating';
|
||||
|
||||
export default function DocItemWrapper(props) {
|
||||
return (
|
||||
<>
|
||||
<DocItem {...props} />
|
||||
<DocRating docId={props.content.metadata.id} />
|
||||
<DocComment docId={props.content.metadata.id} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import DocPage from '@theme-original/DocPage';
|
||||
import DocBookmark from '@site/src/components/DocBookmark';
|
||||
import {useLocation} from '@docusaurus/router';
|
||||
|
||||
export default function DocPageWrapper(props) {
|
||||
const {content} = props;
|
||||
const location = useLocation();
|
||||
const docId = location.pathname;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocBookmark docId={docId} />
|
||||
<DocPage {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import DocSidebarItem from '@theme-original/DocSidebarItem';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function DocSidebarItemWrapper(props) {
|
||||
const { item } = props;
|
||||
const [isBookmarked, setIsBookmarked] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (item.type === 'doc') {
|
||||
const bookmarks = JSON.parse(localStorage.getItem('docBookmarks') || '{}');
|
||||
setIsBookmarked(bookmarks[item.docId] || false);
|
||||
|
||||
// 监听标记变化事件
|
||||
const handleBookmarkChange = (event) => {
|
||||
const { docId, isBookmarked: newState } = event.detail;
|
||||
if (docId === item.docId) {
|
||||
setIsBookmarked(newState);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('bookmarkChanged', handleBookmarkChange);
|
||||
return () => {
|
||||
window.removeEventListener('bookmarkChanged', handleBookmarkChange);
|
||||
};
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
if (item.type === 'doc') {
|
||||
return (
|
||||
<li className={`menu__list-item ${isBookmarked ? styles.bookmarkedItem : ''}`}>
|
||||
<div className={styles.menuLinkWrapper}>
|
||||
<DocSidebarItem {...props} />
|
||||
<div className={styles.actionButtons}>
|
||||
<button
|
||||
onClick={() => {
|
||||
const bookmarks = JSON.parse(localStorage.getItem('docBookmarks') || '{}');
|
||||
const newState = !isBookmarked;
|
||||
bookmarks[item.docId] = newState;
|
||||
localStorage.setItem('docBookmarks', JSON.stringify(bookmarks));
|
||||
setIsBookmarked(newState);
|
||||
window.dispatchEvent(new CustomEvent('bookmarkChanged', {
|
||||
detail: { docId: item.docId, isBookmarked: newState }
|
||||
}));
|
||||
}}
|
||||
className={`${styles.bookmarkButton} ${isBookmarked ? styles.bookmarked : ''}`}
|
||||
aria-label={isBookmarked ? '取消标记' : '添加标记'}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill={isBookmarked ? 'currentColor' : 'none'}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return <DocSidebarItem {...props} />;
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
.menuLinkWrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.actionButtons {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.bookmarkButton {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.bookmarkButton:hover {
|
||||
color: var(--ifm-color-primary);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.bookmarked {
|
||||
color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
.bookmarkedItem {
|
||||
background-color: var(--ifm-color-emphasis-100);
|
||||
}
|
||||
|
||||
.bookmarkedItem :global(.menu__link) {
|
||||
font-weight: 500;
|
||||
color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
/* 确保侧边栏项目有足够的空间显示按钮 */
|
||||
:global(.menu__link-wrapper) {
|
||||
padding-right: 80px !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* 调整列表项样式 */
|
||||
:global(.menu__list-item) {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.bookmarkedItem :global(.menu__link:hover) {
|
||||
background-color: var(--ifm-color-emphasis-200) !important;
|
||||
}
|
||||
|
||||
/* 确保样式在深色模式下也生效 */
|
||||
[data-theme='dark'] .bookmarkedItem :global(.menu__link) {
|
||||
background-color: var(--ifm-color-emphasis-200) !important;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .bookmarkedItem :global(.menu__link:hover) {
|
||||
background-color: var(--ifm-color-emphasis-300) !important;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import Layout from '@theme-original/Layout';
|
||||
import BackToTop from '@site/src/components/BackToTop';
|
||||
|
||||
export default function LayoutWrapper(props) {
|
||||
return (
|
||||
<>
|
||||
<Layout {...props} />
|
||||
<BackToTop />
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +1,67 @@
|
||||
---
|
||||
id: merge-request-issue
|
||||
title: 合并请求关联疑修
|
||||
sidebar_label: '合并请求关联疑修'
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# 合并请求关联疑修
|
||||
|
||||
## 功能介绍
|
||||
|
||||
合并请求关联疑修功能允许您在创建或编辑合并请求时,将相关的疑修与合并请求建立关联。这样可以帮助团队成员更好地追踪代码变更与疑修之间的关系,提高开发效率。
|
||||
|
||||
## 如何关联疑修
|
||||
|
||||
### 方法一:在合并请求描述中引用
|
||||
|
||||
1. 在创建或编辑合并请求时,在描述中使用 `#` 符号引用疑修编号
|
||||
2. 例如:`修复 #123 中提到的登录问题`
|
||||
3. 系统会自动识别并建立关联
|
||||
|
||||
### 方法二:在疑修详情页关联
|
||||
|
||||
1. 打开疑修详情页面
|
||||
2. 点击"关联合并请求"按钮
|
||||
3. 选择要关联的合并请求
|
||||
4. 确认关联
|
||||
|
||||
## 关联后的效果
|
||||
|
||||
1. 在疑修详情页面可以看到关联的合并请求列表
|
||||
2. 在合并请求详情页面可以看到关联的疑修信息
|
||||
3. 当合并请求被合并时,系统会自动更新疑修状态
|
||||
4. 可以通过关联关系快速追踪代码变更
|
||||
|
||||
## 管理关联关系
|
||||
|
||||
### 查看关联
|
||||
|
||||
1. 在疑修详情页面的"关联合并请求"部分查看
|
||||
2. 在合并请求详情页面的"关联疑修"部分查看
|
||||
|
||||
### 解除关联
|
||||
|
||||
1. 在疑修详情页面:
|
||||
- 找到要解除关联的合并请求
|
||||
- 点击"解除关联"按钮
|
||||
- 确认操作
|
||||
|
||||
2. 在合并请求详情页面:
|
||||
- 找到要解除关联的疑修
|
||||
- 点击"解除关联"按钮
|
||||
- 确认操作
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 在创建合并请求时,及时关联相关的疑修
|
||||
2. 使用清晰的描述说明合并请求与疑修的关系
|
||||
3. 定期检查关联关系,确保其准确性
|
||||
4. 在合并请求被合并后,及时确认疑修状态
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 一个合并请求可以关联多个疑修
|
||||
2. 一个疑修可以关联多个合并请求
|
||||
3. 关联关系建立后可以随时修改
|
||||
4. 解除关联不会影响已合并的代码变更
|
@ -1,4 +1,87 @@
|
||||
---
|
||||
id: platform-announcement
|
||||
title: 平台公告
|
||||
sidebar_label: '平台公告'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# 平台公告
|
||||
|
||||
## 功能介绍
|
||||
|
||||
平台公告功能允许管理员发布和管理平台相关的通知、更新和重要信息。通过平台公告,可以及时向用户传达重要信息,确保用户了解平台的最新动态。
|
||||
|
||||
## 创建平台公告
|
||||
|
||||
1. 进入平台公告管理页面
|
||||
2. 点击"新建公告"按钮
|
||||
3. 填写公告标题和内容
|
||||
4. 设置公告类型和优先级
|
||||
5. 选择发布范围
|
||||
6. 点击"发布"按钮
|
||||
|
||||
## 公告类型
|
||||
|
||||
平台公告支持以下类型:
|
||||
|
||||
- 系统更新:平台功能更新和系统维护通知
|
||||
- 功能发布:新功能发布和功能介绍
|
||||
- 活动通知:平台活动、比赛和培训通知
|
||||
- 重要提醒:安全提醒和重要政策变更
|
||||
- 其他通知:其他类型的平台通知
|
||||
|
||||
## 公告管理
|
||||
|
||||
### 编辑公告
|
||||
|
||||
1. 在公告列表中找到要编辑的公告
|
||||
2. 点击"编辑"按钮
|
||||
3. 修改公告内容
|
||||
4. 点击"保存"按钮
|
||||
|
||||
### 删除公告
|
||||
|
||||
1. 在公告列表中找到要删除的公告
|
||||
2. 点击"删除"按钮
|
||||
3. 确认删除操作
|
||||
|
||||
### 公告状态
|
||||
|
||||
公告可能处于以下状态:
|
||||
|
||||
- 草稿:未发布的公告
|
||||
- 已发布:正在显示的公告
|
||||
- 已过期:超过显示期限的公告
|
||||
- 已归档:已归档的公告
|
||||
|
||||
## 公告显示
|
||||
|
||||
### 显示位置
|
||||
|
||||
1. 平台首页公告栏
|
||||
2. 用户个人中心
|
||||
3. 相关功能页面
|
||||
|
||||
### 显示规则
|
||||
|
||||
1. 按优先级排序显示
|
||||
2. 按发布时间倒序排列
|
||||
3. 支持置顶显示
|
||||
4. 支持定时发布和自动过期
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 公告标题要简洁明了
|
||||
2. 内容要清晰易懂
|
||||
3. 重要信息要突出显示
|
||||
4. 及时更新和删除过期公告
|
||||
5. 合理设置公告优先级
|
||||
6. 注意公告的时效性
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 公告内容要符合平台规范
|
||||
2. 避免发布重复或过时的信息
|
||||
3. 重要公告要及时通知用户
|
||||
4. 定期清理过期公告
|
||||
5. 注意公告的权限管理
|
Loading…
Reference in new issue