refactor: 组件顶部注释迁入 script setup 块内 + 全量 prettier 格式化

SFC 顶层(块标签外)不是 prettier 认识的注释位置,块注释会被重排拍平;迁入 <script setup> 顶部后 prettier 正确保留 JSDoc 格式。迁移 6 个 .vue 文件;顺带 prettier 格式化 4 个存量未格式化文件(gateway-client.ts、endpoint-queries.spec.ts、json-text-processor.spec.ts、bench-store.spec.ts)。
This commit is contained in:
2026-09-08 11:06:47 +08:00
parent d4fe766373
commit c690e0507e
10 changed files with 76 additions and 35 deletions
-1
View File
@@ -54,4 +54,3 @@ describe('useManualEndpointQuery(集成冒烟)', () => {
app.unmount(); app.unmount();
}); });
}); });
+4 -1
View File
@@ -28,7 +28,10 @@ export function buildAuthorizationHeader(skKey: string): Record<string, string>
/** /**
* 请求网关接口(相对路径,经 Vite dev server 代理转发到网关,见 vite.config.ts) * 请求网关接口(相对路径,经 Vite dev server 代理转发到网关,见 vite.config.ts)
*/ */
export async function requestGatewayEndpoint(path: string, skKey: string): Promise<GatewayResponse> { export async function requestGatewayEndpoint(
path: string,
skKey: string,
): Promise<GatewayResponse> {
const requestStartedAt = performance.now(); const requestStartedAt = performance.now();
try { try {
+1 -1
View File
@@ -1,9 +1,9 @@
<script setup lang="ts">
/** /**
* 应用外壳:双栏布局(左 sider 接口列表 + 右 tabs 详情区) * 应用外壳:双栏布局(左 sider 接口列表 + 右 tabs 详情区)
* 暗色默认(设计文档 §2 定案);组件拆分见实施计划阶段四 * 暗色默认(设计文档 §2 定案);组件拆分见实施计划阶段四
*/ */
<script setup lang="ts">
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import RequestTabs from '@/components/request-tabs.vue'; import RequestTabs from '@/components/request-tabs.vue';
+1 -2
View File
@@ -1,9 +1,8 @@
<script setup lang="ts">
/** /**
* 接口信息行:method 徽标 + 路径 + 鉴权徽标(宽松/严格) * 接口信息行:method 徽标 + 路径 + 鉴权徽标(宽松/严格)
* 阶段五会在此扩展可折叠的注意事项
*/ */
<script setup lang="ts">
import type { EndpointDefinition } from '@/api/endpoints'; import type { EndpointDefinition } from '@/api/endpoints';
const props = defineProps<{ endpoint: EndpointDefinition }>(); const props = defineProps<{ endpoint: EndpointDefinition }>();
+9 -5
View File
@@ -1,9 +1,13 @@
/** * 单接口完整面板:信息行 → 注意事项折叠 → sk-key 输入 + 发送按钮 → 响应区 * * sk-key 存在
bench-store(每 tab 独立、无持久化、随刷新重置): * 2026-09 起从组件局部 ref 提升——EndpointPanel 随
tab 切换卸载重建, * 局部 ref 会在切回时丢输入;store 持有则切 tab 保留(数据仍由 Query 缓存兜底) *
响应区:元信息行(状态/耗时/大小)+ 操作(复制/下载)+ 虚拟滚动 JSON 视图 */
<script setup lang="ts"> <script setup lang="ts">
/**
* 单接口完整面板:信息行 → 注意事项折叠 → sk-key 输入 + 发送按钮 → 响应区
*
* sk-key 存在 bench-store(每 tab 独立、无持久化、随刷新重置):
* 2026-09 起从组件局部 ref 提升——EndpointPanel 随 tab 切换卸载重建,
* 局部 ref 会在切回时丢输入;store 持有则切 tab 保留(数据仍由 Query 缓存兜底)
* 响应区:元信息行(状态/耗时/大小)+ 操作(复制/下载)+ 虚拟滚动 JSON 视图
*/
import { useClipboard } from '@vueuse/core'; import { useClipboard } from '@vueuse/core';
import { computed } from 'vue'; import { computed } from 'vue';
+11 -8
View File
@@ -1,3 +1,4 @@
<script setup lang="ts">
/** /**
* 虚拟滚动 JSON 文本视图(核心性能组件,对齐 DevTools Network Response 体验) * 虚拟滚动 JSON 文本视图(核心性能组件,对齐 DevTools Network Response 体验)
* *
@@ -10,7 +11,6 @@
* - 滚动条:撑高度用总行数 × 行高的占位 div,窗口偏移用 translateY * - 滚动条:撑高度用总行数 × 行高的占位 div,窗口偏移用 translateY
*/ */
<script setup lang="ts">
import { useElementSize, useThrottleFn } from '@vueuse/core'; import { useElementSize, useThrottleFn } from '@vueuse/core';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@@ -51,11 +51,7 @@ const handleScroll = useThrottleFn(() => {
/* ---------- 可视窗口(纯函数,阶段二已单测锁死) ---------- */ /* ---------- 可视窗口(纯函数,阶段二已单测锁死) ---------- */
const visibleWindow = computed(() => const visibleWindow = computed(() =>
computeVisibleRowWindow( computeVisibleRowWindow(lineList.value.length, scrollTopPx.value, containerHeightPx.value),
lineList.value.length,
scrollTopPx.value,
containerHeightPx.value,
),
); );
/** 窗口内行的渲染模型(行号 + 完整片段序列:token 与 plain 交替,括号不丢) */ /** 窗口内行的渲染模型(行号 + 完整片段序列:token 与 plain 交替,括号不丢) */
@@ -66,7 +62,11 @@ interface RenderableLine {
const visibleLineList = computed<RenderableLine[]>(() => { const visibleLineList = computed<RenderableLine[]>(() => {
const lines: RenderableLine[] = []; const lines: RenderableLine[] = [];
for (let index = visibleWindow.value.startIndex; index <= visibleWindow.value.endIndex; index += 1) { for (
let index = visibleWindow.value.startIndex;
index <= visibleWindow.value.endIndex;
index += 1
) {
const lineText = lineList.value[index]; const lineText = lineList.value[index];
if (lineText === undefined) continue; if (lineText === undefined) continue;
lines.push({ lineNumber: index + 1, segments: buildJsonLineSegments(lineText) }); lines.push({ lineNumber: index + 1, segments: buildJsonLineSegments(lineText) });
@@ -81,7 +81,10 @@ const gutterWidth = computed(() => computeGutterWidth(lineList.value.length));
<template> <template>
<div ref="scrollContainerRef" class="json-text-view" @scroll.passive="handleScroll"> <div ref="scrollContainerRef" class="json-text-view" @scroll.passive="handleScroll">
<div class="scroll-spacer" :style="{ height: `${totalHeightPx}px` }"> <div class="scroll-spacer" :style="{ height: `${totalHeightPx}px` }">
<div class="visible-window" :style="{ transform: `translateY(${visibleWindow.offsetYPx}px)` }"> <div
class="visible-window"
:style="{ transform: `translateY(${visibleWindow.offsetYPx}px)` }"
>
<div v-for="line in visibleLineList" :key="line.lineNumber" class="line"> <div v-for="line in visibleLineList" :key="line.lineNumber" class="line">
<span class="gutter" :style="{ width: `${gutterWidth}ch` }">{{ line.lineNumber }}</span> <span class="gutter" :style="{ width: `${gutterWidth}ch` }">{{ line.lineNumber }}</span>
<span class="line-content"> <span class="line-content">
+14 -9
View File
@@ -1,13 +1,18 @@
/** * 右侧 tabs 容器:仿 Naive UI「纯导航模式」(TDesign 无官方等价 API,参照 * t-design-starter
的实现——t-tabs 只当导航条,面板内容渲染在组件外侧自有容器) * * 为什么不用 t-tabs 的 panel
渲染面板: * - 面板显隐由 .t-tab-panel.t-is-hidden(库内部)管理,做「固定分区 + JSON 区独立滚动」 *
必须穿透 .t-tabs__content / .t-tab-panel 的内部布局,耦合库实现 * - 外置后布局全在自己的
DOM:display-directive 语义(show:v-show 常驻保留状态) *
由本组件手工实现,面板切走不销毁,sk-key/滚动位置/Query 缓存三重保留 * * list
仍数据驱动(label/removable/value),change/remove/drag-sort 事件语义不变; * t-tabs 空 panel
残壳用一条收敛规则隐藏(见样式区注释)。 */
<script setup lang="ts"> <script setup lang="ts">
/**
* 右侧 tabs 容器:仿 Naive UI「纯导航模式」(TDesign 无官方等价 API,参照
* t-design-starter 的实现——t-tabs 只当导航条,面板内容渲染在组件外侧自有容器)
*
* 为什么不用 t-tabs 的 panel 渲染面板:
* - 面板显隐由 .t-tab-panel.t-is-hidden(库内部)管理,做「固定分区 + JSON 区独立滚动」
* 必须穿透 .t-tabs__content / .t-tab-panel 的内部布局,耦合库实现
* - 外置后布局全在自己的 DOM:display-directive 语义(show:v-show 常驻保留状态)
* 由本组件手工实现,面板切走不销毁,sk-key/滚动位置/Query 缓存三重保留
*
* list 仍数据驱动(label/removable/value),change/remove/drag-sort 事件语义不变;
* t-tabs 空 panel 残壳用一条收敛规则隐藏(见样式区注释)。
*/
import { Tabs, type TabPanelProps, type TabValue } from 'tdesign-vue-next'; import { Tabs, type TabPanelProps, type TabValue } from 'tdesign-vue-next';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { computed } from 'vue'; import { computed } from 'vue';
+1 -1
View File
@@ -1,9 +1,9 @@
<script setup lang="ts">
/** /**
* 左侧接口导航:5 个路径平铺(不分组不编号,按 endpoints.ts 定案顺序) * 左侧接口导航:5 个路径平铺(不分组不编号,按 endpoints.ts 定案顺序)
* 点击 → openOrActivateTab(已开则激活,未开则新开) * 点击 → openOrActivateTab(已开则激活,未开则新开)
*/ */
<script setup lang="ts">
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { endpointList } from '@/api/endpoints'; import { endpointList } from '@/api/endpoints';
+5 -1
View File
@@ -70,7 +70,11 @@ describe('computeVisibleRowWindow', () => {
}); });
it('零行(空响应)返回空窗口', () => { it('零行(空响应)返回空窗口', () => {
expect(computeVisibleRowWindow(0, 0, 400)).toEqual({ startIndex: 0, endIndex: -1, offsetYPx: 0 }); expect(computeVisibleRowWindow(0, 0, 400)).toEqual({
startIndex: 0,
endIndex: -1,
offsetYPx: 0,
});
}); });
it('非正视口高度安全返回空窗口', () => { it('非正视口高度安全返回空窗口', () => {
+30 -6
View File
@@ -140,35 +140,55 @@ describe('reorderTab', () => {
const store = useBenchStore(); const store = useBenchStore();
openThreeTabs(store); openThreeTabs(store);
store.reorderTab('/v1/models', 0, 2); store.reorderTab('/v1/models', 0, 2);
expect(store.openTabs).toEqual(['/v1/dashboard/billing/usage', '/api/usage/token', '/v1/models']); expect(store.openTabs).toEqual([
'/v1/dashboard/billing/usage',
'/api/usage/token',
'/v1/models',
]);
}); });
it('后移:2 → 0 位', () => { it('后移:2 → 0 位', () => {
const store = useBenchStore(); const store = useBenchStore();
openThreeTabs(store); openThreeTabs(store);
store.reorderTab('/api/usage/token', 2, 0); store.reorderTab('/api/usage/token', 2, 0);
expect(store.openTabs).toEqual(['/api/usage/token', '/v1/models', '/v1/dashboard/billing/usage']); expect(store.openTabs).toEqual([
'/api/usage/token',
'/v1/models',
'/v1/dashboard/billing/usage',
]);
}); });
it('相邻换位:0 ↔ 1', () => { it('相邻换位:0 ↔ 1', () => {
const store = useBenchStore(); const store = useBenchStore();
openThreeTabs(store); openThreeTabs(store);
store.reorderTab('/v1/models', 0, 1); store.reorderTab('/v1/models', 0, 1);
expect(store.openTabs).toEqual(['/v1/dashboard/billing/usage', '/v1/models', '/api/usage/token']); expect(store.openTabs).toEqual([
'/v1/dashboard/billing/usage',
'/v1/models',
'/api/usage/token',
]);
}); });
it('原位不动:from === to', () => { it('原位不动:from === to', () => {
const store = useBenchStore(); const store = useBenchStore();
openThreeTabs(store); openThreeTabs(store);
store.reorderTab('/v1/dashboard/billing/usage', 1, 1); store.reorderTab('/v1/dashboard/billing/usage', 1, 1);
expect(store.openTabs).toEqual(['/v1/models', '/v1/dashboard/billing/usage', '/api/usage/token']); expect(store.openTabs).toEqual([
'/v1/models',
'/v1/dashboard/billing/usage',
'/api/usage/token',
]);
}); });
it('路径与索引不匹配:无操作(防御 t-tabs 事件异常)', () => { it('路径与索引不匹配:无操作(防御 t-tabs 事件异常)', () => {
const store = useBenchStore(); const store = useBenchStore();
openThreeTabs(store); openThreeTabs(store);
store.reorderTab('/v1/models', 2, 0); // 索引 2 上是 /api/usage/token store.reorderTab('/v1/models', 2, 0); // 索引 2 上是 /api/usage/token
expect(store.openTabs).toEqual(['/v1/models', '/v1/dashboard/billing/usage', '/api/usage/token']); expect(store.openTabs).toEqual([
'/v1/models',
'/v1/dashboard/billing/usage',
'/api/usage/token',
]);
}); });
it('越界索引:无操作', () => { it('越界索引:无操作', () => {
@@ -176,7 +196,11 @@ describe('reorderTab', () => {
openThreeTabs(store); openThreeTabs(store);
store.reorderTab('/v1/models', 0, 99); store.reorderTab('/v1/models', 0, 99);
store.reorderTab('/v1/models', -1, 0); store.reorderTab('/v1/models', -1, 0);
expect(store.openTabs).toEqual(['/v1/models', '/v1/dashboard/billing/usage', '/api/usage/token']); expect(store.openTabs).toEqual([
'/v1/models',
'/v1/dashboard/billing/usage',
'/api/usage/token',
]);
}); });
it('重排不影响激活态', () => { it('重排不影响激活态', () => {