Archived
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:
@@ -54,4 +54,3 @@ describe('useManualEndpointQuery(集成冒烟)', () => {
|
||||
app.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ export function buildAuthorizationHeader(skKey: string): Record<string, string>
|
||||
/**
|
||||
* 请求网关接口(相对路径,经 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();
|
||||
|
||||
try {
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 应用外壳:双栏布局(左 sider 接口列表 + 右 tabs 详情区)
|
||||
* 暗色默认(设计文档 §2 定案);组件拆分见实施计划阶段四
|
||||
*/
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import RequestTabs from '@/components/request-tabs.vue';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 接口信息行:method 徽标 + 路径 + 鉴权徽标(宽松/严格)
|
||||
* 阶段五会在此扩展可折叠的注意事项
|
||||
*/
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { EndpointDefinition } from '@/api/endpoints';
|
||||
|
||||
const props = defineProps<{ endpoint: EndpointDefinition }>();
|
||||
|
||||
@@ -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">
|
||||
/**
|
||||
* 单接口完整面板:信息行 → 注意事项折叠 → sk-key 输入 + 发送按钮 → 响应区
|
||||
*
|
||||
* sk-key 存在 bench-store(每 tab 独立、无持久化、随刷新重置):
|
||||
* 2026-09 起从组件局部 ref 提升——EndpointPanel 随 tab 切换卸载重建,
|
||||
* 局部 ref 会在切回时丢输入;store 持有则切 tab 保留(数据仍由 Query 缓存兜底)
|
||||
* 响应区:元信息行(状态/耗时/大小)+ 操作(复制/下载)+ 虚拟滚动 JSON 视图
|
||||
*/
|
||||
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { computed } from 'vue';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 虚拟滚动 JSON 文本视图(核心性能组件,对齐 DevTools Network Response 体验)
|
||||
*
|
||||
@@ -10,7 +11,6 @@
|
||||
* - 滚动条:撑高度用总行数 × 行高的占位 div,窗口偏移用 translateY
|
||||
*/
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useElementSize, useThrottleFn } from '@vueuse/core';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
@@ -51,11 +51,7 @@ const handleScroll = useThrottleFn(() => {
|
||||
/* ---------- 可视窗口(纯函数,阶段二已单测锁死) ---------- */
|
||||
|
||||
const visibleWindow = computed(() =>
|
||||
computeVisibleRowWindow(
|
||||
lineList.value.length,
|
||||
scrollTopPx.value,
|
||||
containerHeightPx.value,
|
||||
),
|
||||
computeVisibleRowWindow(lineList.value.length, scrollTopPx.value, containerHeightPx.value),
|
||||
);
|
||||
|
||||
/** 窗口内行的渲染模型(行号 + 完整片段序列:token 与 plain 交替,括号不丢) */
|
||||
@@ -66,7 +62,11 @@ interface RenderableLine {
|
||||
|
||||
const visibleLineList = computed<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];
|
||||
if (lineText === undefined) continue;
|
||||
lines.push({ lineNumber: index + 1, segments: buildJsonLineSegments(lineText) });
|
||||
@@ -81,7 +81,10 @@ const gutterWidth = computed(() => computeGutterWidth(lineList.value.length));
|
||||
<template>
|
||||
<div ref="scrollContainerRef" class="json-text-view" @scroll.passive="handleScroll">
|
||||
<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">
|
||||
<span class="gutter" :style="{ width: `${gutterWidth}ch` }">{{ line.lineNumber }}</span>
|
||||
<span class="line-content">
|
||||
|
||||
@@ -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">
|
||||
/**
|
||||
* 右侧 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 { storeToRefs } from 'pinia';
|
||||
import { computed } from 'vue';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 左侧接口导航:5 个路径平铺(不分组不编号,按 endpoints.ts 定案顺序)
|
||||
* 点击 → openOrActivateTab(已开则激活,未开则新开)
|
||||
*/
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import { endpointList } from '@/api/endpoints';
|
||||
|
||||
@@ -70,7 +70,11 @@ describe('computeVisibleRowWindow', () => {
|
||||
});
|
||||
|
||||
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('非正视口高度安全返回空窗口', () => {
|
||||
|
||||
@@ -140,35 +140,55 @@ describe('reorderTab', () => {
|
||||
const store = useBenchStore();
|
||||
openThreeTabs(store);
|
||||
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 位', () => {
|
||||
const store = useBenchStore();
|
||||
openThreeTabs(store);
|
||||
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', () => {
|
||||
const store = useBenchStore();
|
||||
openThreeTabs(store);
|
||||
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', () => {
|
||||
const store = useBenchStore();
|
||||
openThreeTabs(store);
|
||||
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 事件异常)', () => {
|
||||
const store = useBenchStore();
|
||||
openThreeTabs(store);
|
||||
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('越界索引:无操作', () => {
|
||||
@@ -176,7 +196,11 @@ describe('reorderTab', () => {
|
||||
openThreeTabs(store);
|
||||
store.reorderTab('/v1/models', 0, 99);
|
||||
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('重排不影响激活态', () => {
|
||||
|
||||
Reference in New Issue
Block a user