Skip to content

Enhance serverlist#145

Merged
ZhuYuxuan9302 merged 8 commits into
mainfrom
enhance-serverlist
Jun 14, 2026
Merged

Enhance serverlist#145
ZhuYuxuan9302 merged 8 commits into
mainfrom
enhance-serverlist

Conversation

@ZhuYuxuan9302

Copy link
Copy Markdown
Member

No description provided.

Revamp ServerList.vue: add skeleton loading cards, richer error/empty states, result meta, improved search UI (icon, clear button, aria labels) and filter controls. Improve server card rendering with icon fallback, disabled card handling, safe description splitting, status dot/players/delay display, and localized defaults for unnamed servers. Add robust data handling and network logic: props for statusApiUrl/pollInterval/requestTimeout, fetchWithTimeout, statusOf/normalizeServers, batched status checks with concurrency, request timeout handling and polling cleanup. Refactor computed/reactive logic (filteredServers, onlineCount, hasActiveFilter), minor UX/CSS tweaks and responsive styles.
Lower the batch concurrency constant from 6 to 5 in ServerList.vue to reduce the number of simultaneous status-check requests, easing load and lowering the chance of hitting API or rate limits during bulk checks.
Refactor ServerList UI and behavior: reorganize filters into two rows, add search clear button and version dropdown, replace type select with clickable type chips (with clear filter), and improve styling. Implement pagination with page-size selector, jump input, and smart visible page buttons; add props for defaultPageSize and pageSizeOptions. Add computed paginatedServers, page navigation helpers, and watchers to reset/adjust current page on filter or size changes. Minor utility/comment tweaks, icon/description handling preserved, and CSS updates for responsiveness and improved visuals.
@ZhuYuxuan9302 ZhuYuxuan9302 requested a review from a team June 14, 2026 09:30
@MCJPG-Agent

Copy link
Copy Markdown
Contributor

🎉 感谢 @ZhuYuxuan9302 提交 Pull Request!
🎉 Thanks @ZhuYuxuan9302 for submitting the Pull Request!

我们会尽快审查您的代码。在此期间,请确保:
We will review your code as soon as possible. In the meantime, please ensure:

  • ✅ 所有测试通过 / All tests pass
  • ✅ 代码符合项目规范 / Code follows project conventions
  • ✅ 已添加必要的文档 / Necessary documentation is added

🤖 我们的 MCJPG Agent 将自动对您的代码进行初步审查。
🤖 Our MCJPG Agent will automatically conduct an initial review of your code.

如有代码辅助需求,请在评论区 @claude 进行请求。
If you need code assistance, please @claude in the comments.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 2026

Copy link
Copy Markdown

Deploying mcjpg with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6ff1ded
Status: ✅  Deploy successful!
Preview URL: https://1fe83ee8.mcjpgdev.pages.dev
Branch Preview URL: https://enhance-serverlist.mcjpgdev.pages.dev

View logs

@MCJPG-Agent

MCJPG-Agent commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ZhuYuxuan9302's task in 3m 27s —— View job


PR #145 代码审查

  • 获取上下文
  • 读取变更文件
  • 分析代码质量
  • 输出审查结果

总体评价

本次 PR 对 ServerList.vue 进行了大幅度的功能增强,代码整体质量较高,引入了骨架屏加载、分页、搜索筛选、服务器在线状态实时查询等功能,UX 提升明显。以下是审查发现的问题,按优先级排列。


问题(需要关注)

🔴 中等:processedIcon(server) 在模板中被调用两次

位置ServerList.vue 第 130–134 行

<VPImage
  v-if="processedIcon(server)"
  :image="processedIcon(server)"

processedIcon(server) 每次渲染被调用两次,每次调用都返回一个全新的对象。这会导致 VPImage 组件因为对象引用变化而不必要地重新渲染。

建议修复:使用 <template> 中间变量,或在 paginatedServers 的 computed 中预处理图标数据。

Fix this →


🔴 中等:statusOf(server.ip) 在模板中被调用 4–5 次

位置ServerList.vue 第 146、155、157、159、161、162 行

:class="statusOf(server.ip).online ? 'online' : 'offline'"
...
v-if="statusOf(server.ip).online && statusOf(server.ip).delay != null"
...
{{ statusOf(server.ip).players.online }}/{{ statusOf(server.ip).players.max }}

每次渲染,每个服务器卡片会触发 4–5 次 statusOf 调用,服务器数量多时(例如 9 个/页)累计约 40 次函数调用。statusOf 返回的是 serverStatus.value[ip] 的引用,是安全的,但多次函数调用仍有一定开销。

建议:用 v-for 模板的本地变量(VitePress/Vue 3 可用 v-bind trick 或拆分子组件),或在 paginatedServers computed 中预计算 statusInfo


🟡 次要:descriptionLines(server) 调用两次

位置:第 170 行

<div v-if="descriptionLines(server).length" class="server-description">
  <span v-for="(line, index) in descriptionLines(server)"

同一服务器的 descriptionLines 被计算两次。建议使用单次调用的结果:

<template v-if="descriptionLines(server) as lines && lines.length">
  <div class="server-description">
    <span v-for="(line, index) in lines" ...>

🟡 次要:组件内硬编码中文文本,不兼容 i18n

位置:整个 <template> 部分,如第 47、64、82、103、110、186、224 行等

根据 CLAUDE.md,站点已配置 i18n 国际化。当前组件内所有文案("搜索服务器名称或简介..."、"所有版本"、"加载失败"等)均为硬编码中文,将导致英文等其他语言的用户看到中文界面。

建议:通过 defineProps 接收文本 prop,或使用 VitePress 的 useData() 获取当前语言环境后动态返回文案。


🟡 次要:脉冲动画使用硬编码颜色,不跟随主题

位置:第 941–951 行

@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.5); }
  70% { box-shadow: 0 0 0 5px rgba(34, 197, 94, 0); }
}

颜色值 rgba(34, 197, 94, ...) 是 Tailwind 绿色的硬编码值,不会随用户主题(深色/浅色)变化。在深色模式下可能对比度不足。建议改用 var(--vp-c-green) 实现的方式(可通过 CSS 变量传递透明度)。


🟡 次要:组件卸载后状态更新仍在运行

位置checkServerStatus 函数(第 344–370 行),onUnmounted(第 526–528 行)

onUnmounted 只清除了 pollTimer,但如果卸载时 checkAllStatus 正在执行中(多个并发 fetch 请求),这些异步操作完成后仍会写入 serverStatus.value。Vue 3 中这不会导致崩溃,但属于无效工作。

建议:添加一个 isMounted 标志,或维护一个全局 AbortController 供所有状态请求使用,卸载时统一 abort。


🟢 建议:无链接卡片语义问题

位置:第 118–179 行

<a
  v-for="server in paginatedServers"
  :href="server.link || undefined"
  ...
>

server.link 为 falsy 时,<a> 标签没有 href,在语义上不是有效的链接,屏幕阅读器行为可能不一致。建议根据是否有 link 渲染为 <a><div>(可使用动态组件 :is)。


亮点

  • 骨架屏体验优秀,使用 CSS background-position 动画,无额外依赖
  • 并发控制CONCURRENCY = 5)避免同时发起过多 API 请求
  • fetchWithTimeout 使用 AbortController 正确处理超时,finally 块确保定时器清除
  • 智能分页算法(省略号逻辑)处理边界情况正确
  • encodeURIComponent(ip) 防止 IP 中特殊字符导致 URL 注入问题
  • v-html 使用,无 XSS 风险
  • 响应式设计覆盖移动端,断点处理合理

总结

本次改动功能完整、代码结构清晰,无严重安全问题。主要需要关注的是模板中多次调用非纯函数导致的性能问题(processedIcon、statusOf),以及 i18n 兼容性。其余为小问题,可根据实际需求选择是否修复。
| Branch Preview

Comment thread docs/.vitepress/theme/components/ServerList.vue Outdated
Comment thread docs/.vitepress/theme/components/ServerList.vue Outdated
ZhuYuxuan9302 and others added 2 commits June 14, 2026 17:36
Co-authored-by: MCJPG Agent <agent@mcjpg.org>
Co-authored-by: MCJPG Agent <agent@mcjpg.org>
@MCJPG-Agent

MCJPG-Agent commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ZhuYuxuan9302's task in 3m 16s —— View job


PR #145 代码审查

  • 获取上下文
  • 读取变更文件
  • 分析代码质量
  • 输出审查结果

总体评价

本次 PR 功能增强方向正确,引入了骨架屏、分页、搜索筛选和服务器在线状态查询等功能。但当前代码存在导致构建失败的严重模板语法错误,需要先修复后才能合并。


🔴 严重:模板语法破损(导致 Cloudflare 构建失败)

1. 双重 <VPImage> 标签叠加

位置ServerList.vue 第 129–136 行

<VPImage
  v-if="processedIcon(server)"
<VPImage
  v-if="processedIcon(server) as icon"
  :image="icon"
  class="server-icon"
  loading="lazy"
/>

此处出现了两个 <VPImage 标签叠加,疑似自动修复建议未正确应用导致的合并冲突产物,直接导致 Vue 模板编译失败。

正确写法应为其中一种:

<VPImage
  v-if="processedIcon(server)"
  :image="processedIcon(server)"
  class="server-icon"
  loading="lazy"
/>

注意v-if="expr as icon" 不是有效的 Vue 模板语法,Vue 模板中不支持 TypeScript 的 as 类型断言。

修复此处 →


2. <template> 标签缺少闭合标签

位置ServerList.vue 第 172–183 行

<template v-if="descriptionLines(server).length > 0">
  <div class="server-description">
    <span v-for="(line, index) in descriptionLines(server)" ...>
      {{ line }}
    </span>
  </div>
</a>       ← 此处关闭的是外层 <a><template> 未关闭!

<template v-if="..."></a> 之前没有对应的 </template> 闭合标签,导致 Vue 模板编译器报错。

正确结构应为:

<template v-if="descriptionLines(server).length > 0">
  <div class="server-description">
    <span v-for="(line, index) in descriptionLines(server)" :key="index" class="desc-line">
      {{ line }}
    </span>
  </div>
</template>   ← 必须加这一行

修复此处 →


🟠 中等:模板中函数重复调用

3. statusOf(server.ip) 在单个卡片中被调用 8–10 次

位置:第 148、152、154、157(×2)、159、161、163、164(×2)行

:class="statusOf(server.ip).online ? 'online' : 'offline'"
:class="{ 'status-dot--pulse': statusOf(server.ip).online }"
{{ statusOf(server.ip).online ? '在线' : '离线' }}
v-if="statusOf(server.ip).online && statusOf(server.ip).delay != null"
...

每次渲染每张卡片触发 ~10 次函数调用,9 个服务器/页时约 90 次。建议在 paginatedServers computed 中预计算状态:

const paginatedServersWithStatus = computed(() =>
  paginatedServers.value.map(s => ({
    ...s,
    _status: s.ip ? statusOf(s.ip) : OFFLINE_STATUS
  }))
)

4. descriptionLines(server) 调用两次

位置:第 172、175 行

<template v-if="descriptionLines(server).length > 0">
  <div ...>
    <span v-for="(line, index) in descriptionLines(server)" ...>

同一服务器的描述被解析两次。可提取为局部变量或在 computed 中预处理。


🟡 次要

5. 所有文案硬编码为中文,不兼容 i18n

位置:第 47、64、82、103、110 行等多处

根据 CLAUDE.md,网站已配置 i18n 国际化,但组件内所有 UI 文案(搜索占位符、版本选项、在线/离线状态、错误提示等)均为硬编码中文。英文用户访问时将看到中文界面。

建议通过 defineProps 传入文本,或使用 useData() 获取当前语言环境后提供多语言文案。


6. 脉冲动画颜色硬编码,不跟随主题

位置:第 944–954 行

@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.5); }
  70% { box-shadow: 0 0 0 5px rgba(34, 197, 94, 0); }
}

颜色 rgba(34, 197, 94, ...) 是硬编码值,不会随用户深色/浅色主题变化。建议改用 CSS 变量:

.status-dot--pulse {
  --pulse-color: var(--vp-c-green);
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pulse-color) 50%, transparent); }
  70% { box-shadow: 0 0 0 5px transparent; }
}

7. 组件卸载后异步请求仍会写入状态

位置onUnmounted(第 529–531 行)

pollTimer 被清除,但如果卸载时 checkAllStatus 的批量 fetch 请求正在执行,完成后仍会写入 serverStatus.value。建议添加 AbortController

let globalController = new AbortController()

onUnmounted(() => {
  if (pollTimer) clearInterval(pollTimer)
  globalController.abort()
})

8. 无链接卡片的语义问题

位置:第 118–126 行

server.link 为假值时,<a> 标签没有 href,语义上不是合法的超链接。建议使用动态组件:

<component
  :is="server.link ? 'a' : 'div'"
  :href="server.link || undefined"
  ...
>

亮点

  • ✅ 骨架屏体验优秀,纯 CSS 动画无额外依赖
  • ✅ 并发控制(CONCURRENCY = 5)避免过多同时请求
  • fetchWithTimeout 使用 AbortController 正确处理超时
  • encodeURIComponent(ip) 防止 URL 注入
  • OFFLINE_STATUS 使用 Object.freeze 防止意外修改
  • ✅ 智能省略号分页算法边界处理正确
  • ✅ 响应式设计覆盖移动端

结论

当前代码存在 2 处严重模板语法错误(双重 <VPImage> 标签 + 缺失 </template> 闭合),正是导致 Cloudflare 构建失败的根本原因。 建议作者修复这两处错误后重新推送,再进行后续优化。
| Branch Preview

Refactor ServerList.vue to precompute paginated server items for the template (map servers to objects with id, name, link, icon, initial, descLines and status) and rename iteration variable to `item`. Add gridRef and smooth scroll-to-grid behavior on page changes. Implement abortable fetchWithTimeout and track active AbortControllers; introduce an isUnmounted guard to avoid state updates after unmount and abort all pending requests on unmount. Adjust pageSize options and replace page-size select with a custom styled select (markup + CSS). Misc: concurrency-safe status checks, CSS improvements (pulse animation uses CSS var and color-mix), minor layout and accessibility tweaks.
@MCJPG-Agent

MCJPG-Agent commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ZhuYuxuan9302's task in 2m 31s —— View job


PR #145 代码审查(最新同步)

  • 获取上下文
  • 读取变更文件(最新提交 80bafea
  • 分析代码质量,核查历史问题修复情况
  • 输出审查结果

总体评价

最新提交 80bafea("ServerList: precompute items and cancel requests")对上轮审查提出的严重问题进行了系统性修复,代码质量显著提升。以下为本轮审查结论。


✅ 已修复问题(与上轮审查对比)

问题 状态
双重 <VPImage> 标签导致模板编译失败 ✅ 已修复
<template> 缺少闭合标签 ✅ 已修复
processedIcon() 在模板中调用两次 ✅ 已修复(预计算进 paginatedServers computed)
statusOf() 在单卡片中调用 8–10 次 ✅ 已修复(item.status 预计算)
descriptionLines() 调用两次 ✅ 已修复(item.descLines 预计算)
组件卸载后异步请求仍写入状态 ✅ 已修复(isUnmounted 标志 + activeControllers.forEach(c => c.abort()) 统一中止)
脉冲动画颜色硬编码不跟随主题 ✅ 已修复(--pulse-color: var(--vp-c-green) + color-mix() 实现主题感知)

剩余问题

🟡 次要:UI 文案仍为硬编码中文,不兼容 i18n

位置:模板各处(第 47、64、82、103、110、137、155、161、186、224 行等)

根据 CLAUDE.md,网站已配置 i18n 国际化。但搜索占位符、版本下拉选项、状态标签("在线"/"离线")、错误提示、分页按钮文字等均为硬编码中文。英文等其他语言用户将看到中文界面。

建议通过 defineProps 接收多语言文案 prop,或使用 useData() 获取当前语言环境后动态提供文案。

修复此处 →


🟡 次要:<a> 无 href 时的无障碍语义问题

位置:模板第 118 行(现为 paginatedServersv-for

item.link 为假值时,<a :href="undefined"> 渲染为无 href 属性的锚元素。当前虽有 .server-card--disabledcursor: default)处理,但 <a> 无 href 在无障碍语义上不符合规范,屏幕阅读器行为可能不一致。

建议改为动态组件:

<component
  :is="item.link ? 'a' : 'div'"
  :href="item.link || undefined"
  :target="item.link ? '_blank' : undefined"
  rel="noopener noreferrer"
  class="server-card"
  :class="{ 'server-card--disabled': !item.link }"
>

修复此处 →


🟢 建议:文件末尾缺少换行符

位置:文件末尾

-</style>
\ No newline at end of file
+</style>

建议在文件末尾添加换行符,符合 POSIX 标准和大多数编辑器约定。


🟢 建议:color-mix() 兼容性提示

位置:第 977–990 行(pulse 动画)

color-mix(in srgb, ...) 需要 Chrome 111+、Firefox 113+、Safari 16.2+ 以上版本支持。若需兼容旧版浏览器,可添加 fallback(如直接使用 rgba 值作为回退)。VitePress 项目通常面向现代浏览器,一般无需特殊处理,仅作提示。


亮点(本轮新增)

  • 预计算 paginatedServers:图标、状态、描述行统一在 computed 中预处理,模板完全无重复函数调用,性能大幅提升
  • 统一中止请求activeControllers Set + onUnmounted 统一 abort,无内存泄漏风险
  • OFFLINE_STATUS 使用 Object.freeze:防止意外修改共享对象
  • 主题感知脉冲动画:通过 CSS 变量 + color-mix() 实现,深浅模式均适配
  • isUnmounted 标志:异步请求完成后检查卸载状态,不会在 Vue 3 严格模式下产生警告
  • normalizeServers 防御性处理:过滤非对象、补全 id 字段,鲁棒性高

总结

之前导致 Cloudflare 构建失败的两处严重模板错误均已修复,性能问题(重复函数调用)也通过 paginatedServers computed 预计算一并解决。当前代码可正常构建。

主要未解决项为 i18n 硬编码文案(英文用户看到中文界面),这是功能完整性问题,建议在合并前或后续 PR 中处理。无安全性问题,逻辑整体正确。
| Branch Preview

@ZhuYuxuan9302 ZhuYuxuan9302 enabled auto-merge June 14, 2026 10:00
Adjusts the scroll-margin-top in docs/.vitepress/theme/components/ServerList.vue from 16px to 64px. This increases the offset when navigating to anchors so content isn't obscured by the site header or toolbar.

@linyuyuan2010 linyuyuan2010 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@FireGuo1145 FireGuo1145 self-requested a review June 14, 2026 10:20
@ZhuYuxuan9302 ZhuYuxuan9302 merged commit 33e1df6 into main Jun 14, 2026
5 of 6 checks passed
@ZhuYuxuan9302 ZhuYuxuan9302 deleted the enhance-serverlist branch June 14, 2026 10:21
@MCJPG-Agent

Copy link
Copy Markdown
Contributor

❤️ 感谢来自 @ZhuYuxuan9302 的 PR ❤️
❤️ Great PR from @ZhuYuxuan9302 ❤️

如果您也对 MCJPG 组织的开发工作感兴趣,欢迎联系并加入我们!
If you're also interested in the development work of the MCJPG organization, feel free to reach out and join us!

@pingguomc pingguomc added the documentation Improvements or additions to documentation label Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants