Add Help Page
This commit is contained in:
408
src/views/help/help.vue
Normal file
408
src/views/help/help.vue
Normal file
@@ -0,0 +1,408 @@
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const router = useRouter()
|
||||
const activeNames = ref(['4xx', '5xx'])
|
||||
|
||||
const statusCodes = {
|
||||
'1xx': {
|
||||
title: '1xx - 信息响应',
|
||||
items: [
|
||||
{ code: '100', name: 'Continue', desc: '客户端应当继续发送请求。服务器已收到请求头,客户端应继续发送请求体。' },
|
||||
{ code: '101', name: 'Switching Protocols', desc: '服务器已理解并同意切换协议,通常用于 WebSocket 升级。' },
|
||||
]
|
||||
},
|
||||
'2xx': {
|
||||
title: '2xx - 成功响应',
|
||||
items: [
|
||||
{ code: '200', name: 'OK', desc: '请求已成功处理,服务器返回了请求的数据。' },
|
||||
{ code: '201', name: 'Created', desc: '请求已成功,并且创建了新资源,通常在 POST 或 PUT 请求后返回。' },
|
||||
{ code: '204', name: 'No Content', desc: '请求成功处理,但没有返回内容。常用于 DELETE 请求。' },
|
||||
]
|
||||
},
|
||||
'3xx': {
|
||||
title: '3xx - 重定向',
|
||||
items: [
|
||||
{ code: '301', name: 'Moved Permanently', desc: '请求的资源已永久移动到新 URL,客户端应更新书签或链接。' },
|
||||
{ code: '302', name: 'Found', desc: '请求的资源临时移动到新 URL,客户端应继续使用原有 URL。' },
|
||||
{ code: '304', name: 'Not Modified', desc: '资源未修改,可使用缓存版本。常用于条件请求(If-Modified-Since)。' },
|
||||
]
|
||||
},
|
||||
'4xx': {
|
||||
title: '4xx - 客户端错误',
|
||||
items: [
|
||||
{ code: '400', name: 'Bad Request', desc: '服务器无法理解请求格式,请检查请求语法和参数。' },
|
||||
{ code: '401', name: 'Unauthorized', desc: '请求要求身份验证,请登录后重试。' },
|
||||
{ code: '403', name: 'Forbidden', desc: '服务器理解请求,但拒绝授权访问,权限不足。' },
|
||||
{ code: '404', name: 'Not Found', desc: '服务器找不到请求的资源,请检查 URL 是否正确。' },
|
||||
{ code: '405', name: 'Method Not Allowed', desc: '请求方法不被允许,例如对只读接口发送了 POST 请求。' },
|
||||
{ code: '408', name: 'Request Timeout', desc: '请求超时,客户端未能在服务器预设时间内完成请求。' },
|
||||
{ code: '429', name: 'Too Many Requests', desc: '发送请求过多,已触发速率限制,请稍后重试。' },
|
||||
]
|
||||
},
|
||||
'5xx': {
|
||||
title: '5xx - 服务器错误',
|
||||
items: [
|
||||
{ code: '500', name: 'Internal Server Error', desc: '服务器遇到意外情况,无法完成请求,通常是服务端代码异常。' },
|
||||
{ code: '502', name: 'Bad Gateway', desc: '服务器作为网关或代理时,从上游服务器收到无效响应。' },
|
||||
{ code: '503', name: 'Service Unavailable', desc: '服务器暂时无法处理请求,通常因过载或维护停机。' },
|
||||
{ code: '504', name: 'Gateway Timeout', desc: '服务器作为网关或代理时,未及时从上游服务器收到响应。' },
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
const solutions = [
|
||||
{
|
||||
title: '404 页面未找到',
|
||||
icon: '🔍',
|
||||
steps: [
|
||||
'检查 URL 是否拼写正确,注意大小写和路径分隔符。',
|
||||
'确认目标资源是否已移动或删除。',
|
||||
'如果是内部链接,检查路由配置是否正确。',
|
||||
'配置自定义 404 页面,引导用户回到首页或搜索。',
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '401 / 403 认证与权限错误',
|
||||
icon: '🔐',
|
||||
steps: [
|
||||
'确认用户已登录且 Token / Session 未过期。',
|
||||
'检查请求头中 Authorization 字段格式是否正确。',
|
||||
'验证用户角色权限是否满足访问要求。',
|
||||
'如使用 CORS,确认跨域请求携带了凭证(withCredentials)。',
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '500 服务器内部错误',
|
||||
icon: '⚙️',
|
||||
steps: [
|
||||
'查看服务器日志,定位具体异常堆栈。',
|
||||
'检查数据库连接是否正常。',
|
||||
'确认服务端代码是否有未捕获的异常。',
|
||||
'尝试重启服务或回滚最近部署的版本。',
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '502 / 504 网关错误',
|
||||
icon: '🌐',
|
||||
steps: [
|
||||
'检查上游服务是否正常运行。',
|
||||
'确认 Nginx / 反向代理配置是否正确。',
|
||||
'增大代理超时时间(proxy_read_timeout)。',
|
||||
'检查服务器负载和资源使用情况。',
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '网络异常 / 请求超时',
|
||||
icon: '📡',
|
||||
steps: [
|
||||
'检查客户端网络连接是否稳定。',
|
||||
'确认 DNS 解析是否正常。',
|
||||
'设置合理的请求超时时间,并添加重试机制。',
|
||||
'使用 navigator.onLine 检测离线状态并给出提示。',
|
||||
]
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="help-page">
|
||||
<header class="help-header">
|
||||
<h1>帮助与参考</h1>
|
||||
<p class="subtitle">HTTP 状态码速查 & Web 常见错误处理方案</p>
|
||||
<el-button type="primary" size="large" bg @click="router.push('/')">
|
||||
← 返回首页
|
||||
</el-button>
|
||||
</header>
|
||||
|
||||
<!-- HTTP 状态码速查 -->
|
||||
<section class="section">
|
||||
<h2 class="section-title">📋 HTTP 状态码速查</h2>
|
||||
<el-collapse v-model="activeNames" class="status-collapse">
|
||||
<el-collapse-item
|
||||
v-for="(group, key) in statusCodes"
|
||||
:key="key"
|
||||
:title="group.title"
|
||||
:name="key"
|
||||
>
|
||||
<el-table :data="group.items" stripe style="width: 100%">
|
||||
<el-table-column prop="code" label="状态码" width="100" />
|
||||
<el-table-column prop="name" label="英文名称" width="220" />
|
||||
<el-table-column prop="desc" label="说明" />
|
||||
</el-table>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</section>
|
||||
|
||||
<!-- 常见错误处理方案 -->
|
||||
<section class="section">
|
||||
<h2 class="section-title">🛠️ 常见 Web 错误处理方案</h2>
|
||||
<div class="solution-grid">
|
||||
<el-card
|
||||
v-for="(item, index) in solutions"
|
||||
:key="index"
|
||||
class="solution-card"
|
||||
shadow="hover"
|
||||
>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-icon">{{ item.icon }}</span>
|
||||
<span class="card-title">{{ item.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<ol class="step-list">
|
||||
<li v-for="(step, i) in item.steps" :key="i">{{ step }}</li>
|
||||
</ol>
|
||||
</el-card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 全局错误处理建议 -->
|
||||
<section class="section">
|
||||
<h2 class="section-title">💡 全局错误处理最佳实践</h2>
|
||||
<el-card shadow="never" class="practice-card">
|
||||
<ul class="practice-list">
|
||||
<li><strong>统一拦截:</strong>在 Axios 拦截器中统一处理 HTTP 错误,根据状态码弹出对应提示。</li>
|
||||
<li><strong>友好提示:</strong>为用户展示可理解的错误信息,避免直接暴露技术细节。</li>
|
||||
<li><strong>日志记录:</strong>将错误详情(状态码、请求路径、时间戳)上报至日志系统,便于排查。</li>
|
||||
<li><strong>降级策略:</strong>关键接口失败时提供缓存数据或默认值,保证页面基本可用。</li>
|
||||
<li><strong>重试机制:</strong>对网络超时等临时性错误,实现自动重试(建议最多 3 次,指数退避)。</li>
|
||||
<li><strong>错误边界:</strong>在 Vue 中使用 onErrorCaptured 捕获组件树错误,防止白屏。</li>
|
||||
</ul>
|
||||
</el-card>
|
||||
</section>
|
||||
|
||||
<footer class="help-footer">
|
||||
<p>如有疑问,请联系系统管理员。</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.help-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(160deg, #f0f4f8 0%, #dce3ec 50%, #e8ecf1 100%);
|
||||
padding: 40px 20px 24px;
|
||||
}
|
||||
|
||||
/* ── Header ── */
|
||||
.help-header {
|
||||
text-align: center;
|
||||
margin-bottom: 52px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.help-header h1 {
|
||||
font-size: 2.6em;
|
||||
font-weight: 700;
|
||||
color: #1a1a2e;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1em;
|
||||
color: #6b7280;
|
||||
margin-bottom: 28px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* ── Section ── */
|
||||
.section {
|
||||
max-width: 980px;
|
||||
margin: 0 auto 56px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.45em;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
margin-bottom: 22px;
|
||||
padding-left: 14px;
|
||||
border-left: 4px solid #409eff;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* ── Collapse ── */
|
||||
.status-collapse {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
:deep(.el-collapse) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__header) {
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
background: #fff;
|
||||
padding: 0 20px;
|
||||
height: 52px;
|
||||
line-height: 52px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
transition: background-color 0.25s ease;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__header:hover) {
|
||||
background: #f5f8ff;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__wrap) {
|
||||
background: #fafbfd;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__content) {
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-table th.el-table__cell) {
|
||||
background: #f0f5ff;
|
||||
color: #1a1a2e;
|
||||
font-weight: 600;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
:deep(.el-table .el-table__row:hover > td) {
|
||||
background: #ecf5ff !important;
|
||||
}
|
||||
|
||||
/* ── Solution Cards ── */
|
||||
.solution-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 22px;
|
||||
}
|
||||
|
||||
.solution-card {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.solution-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(64, 158, 255, 0.12);
|
||||
}
|
||||
|
||||
:deep(.solution-card .el-card__header) {
|
||||
background: linear-gradient(135deg, #f0f5ff 0%, #e8f0fe 100%);
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid #e4e9f0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 1.6em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.08em;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
:deep(.solution-card .el-card__body) {
|
||||
padding: 16px 20px 20px;
|
||||
}
|
||||
|
||||
.step-list {
|
||||
padding-left: 20px;
|
||||
line-height: 2;
|
||||
color: #374151;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.step-list li {
|
||||
margin-bottom: 4px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.step-list li::marker {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ── Practice Card ── */
|
||||
.practice-card {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
:deep(.practice-card .el-card__body) {
|
||||
padding: 8px 24px;
|
||||
}
|
||||
|
||||
.practice-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.practice-list li {
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
line-height: 1.8;
|
||||
color: #374151;
|
||||
transition: padding-left 0.2s ease;
|
||||
}
|
||||
|
||||
.practice-list li:hover {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.practice-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.practice-list li strong {
|
||||
color: #409eff;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
/* ── Footer ── */
|
||||
.help-footer {
|
||||
text-align: center;
|
||||
padding: 40px 0 16px;
|
||||
color: #9ca3af;
|
||||
font-size: 0.92em;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 640px) {
|
||||
.help-header h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.solution-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup>
|
||||
import { onMounted, onBeforeUnmount, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const canvasRef = ref(null)
|
||||
canvasRef.value = undefined;
|
||||
@@ -90,6 +93,11 @@ onBeforeUnmount(() => {
|
||||
if (animationId) cancelAnimationFrame(animationId)
|
||||
window.removeEventListener('resize', () => {})
|
||||
})
|
||||
|
||||
const button = [
|
||||
{type: 'success', text: '查看帮助'}
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -109,6 +117,19 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<el-button
|
||||
:type="button[0].type"
|
||||
:key="button[0].text"
|
||||
size="large"
|
||||
class="action-btn"
|
||||
bg
|
||||
@click="router.push('/help')"
|
||||
>
|
||||
{{ button[0].text }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -158,4 +179,13 @@ onBeforeUnmount(() => {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.btn-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.action-btn {
|
||||
font-size: 18px;
|
||||
padding: 14px 36px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user