feat(ui): UI 骨架与请求集成(sider、tabs、接口面板 sk-key 走 store、手动查询)

t-tabs 用官方 list prop 数据驱动(受控 value + 动态增删语义干净);请求纯手动触发,enabled 门控 + refetch,staleTime Infinity 切 tab 不重发,queryKey ['endpoint', path, skKey] 换 key 即新查询;endpoint-panel 的 sk-key computed 读写 store,切 tab 输入保留。
This commit is contained in:
2026-09-08 00:49:37 +08:00
parent 3f95335651
commit 60ba5b6306
10 changed files with 746 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
/**
* TanStack Query 接入:手动触发的 endpoint 请求
*
* 语义(设计文档 §3.3 定案):
* - queryKey = [endpointPath, skKey]——同 tab 换 key 是新查询,不同 tab(不同 key)缓存天然隔离
* - staleTime: Infinity——调试工具的响应不该自己变,切走再切回不自动重发
* - 手动触发:sendRequest() 置位 enabled,首次发送后才拉数据;再次点击强制 refetch
*/
import { useQuery, type UseQueryReturnType } from '@tanstack/vue-query';
import { readonly, ref, type Ref } from 'vue';
import { requestGatewayEndpoint, type GatewayResponse } from './gateway-client';
/** 手动请求的返回契约 */
export interface ManualEndpointQuery {
/** 响应数据(GatewayResponse,含 status/ms/bytes/text/json/networkError) */
data: Readonly<Ref<GatewayResponse | undefined>>;
/** 请求进行中 */
isFetching: Readonly<Ref<boolean>>;
/** 是否已发送过至少一次(区分「未发送」与「发送中」的空态) */
hasSent: Readonly<Ref<boolean>>;
/** 触发请求(key 为空时由调用方负责拦截,本层不校验) */
sendRequest: () => void;
}
/**
* 手动触发的 endpoint 查询。
* endpointPath 与 skKey 均为响应式 Ref:换接口 / 换 key 时 queryKey 随之变化(新缓存槽)。
*/
export function useManualEndpointQuery(
endpointPath: Ref<string>,
skKey: Ref<string>,
): ManualEndpointQuery {
const requestEnabled = ref(false);
const hasSent = ref(false);
const query = useQuery<GatewayResponse>({
queryKey: ['endpoint', endpointPath, skKey],
queryFn: () => requestGatewayEndpoint(endpointPath.value, skKey.value),
enabled: requestEnabled,
staleTime: Infinity,
gcTime: Infinity,
retry: false, // 调试工具要看到真实的失败,不重试
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
});
function sendRequest(): void {
hasSent.value = true;
requestEnabled.value = true;
// 已有缓存(如换回旧 key)时强制重新请求——「发送」按钮的语义就是真的发一次
void query.refetch();
}
return {
data: query.data,
isFetching: query.isFetching,
hasSent: readonly(hasSent),
sendRequest,
};
}
/** 具名导出便于类型引用(避免调用方直接依赖 tanstack 内部类型) */
export type { UseQueryReturnType };