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();
|
app.unmount();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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,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 }>();
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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,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';
|
||||||
|
|||||||
@@ -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('非正视口高度安全返回空窗口', () => {
|
||||||
|
|||||||
@@ -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('重排不影响激活态', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user