chore: 前端代码优化 - ESLint 配置、Prettier 格式化、加载骨架屏、React 组件规范
- 更新 ESLint 配置添加 react-hooks 规则 - 运行 Prettier 格式化所有前端文件 - 为 users、apps、audit、quotas 页面添加 Skeleton 加载骨架屏 - 修复动态组件渲染问题(CategoryIcon/TypeIcon 使用 React.createElement) - 修复 useRef 渲染期间访问问题(改用 useMemo) - 修复 effect 中 setState 同步调用问题 - 新增 global-not-found.tsx 404 页面 - 修复 knowledge 页面引号转义问题
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
export {
|
||||
useSSEStream,
|
||||
updateLastAssistantMessage,
|
||||
setStreamErrorMessage,
|
||||
} from "./use-sse-stream";
|
||||
export { useSSEStream, updateLastAssistantMessage, setStreamErrorMessage } from "./use-sse-stream";
|
||||
export { useCopyToClipboard } from "./use-copy-clipboard";
|
||||
export { useScrollToBottom } from "./use-scroll-bottom";
|
||||
export { useFileExport } from "./use-file-export";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo, useRef } from "react";
|
||||
import { useMemo } from "react";
|
||||
import type { App } from "@/lib/types";
|
||||
|
||||
/**
|
||||
@@ -10,8 +10,7 @@ import type { App } from "@/lib/types";
|
||||
export function useAppConfig(app: App): Record<string, unknown> {
|
||||
return useMemo(() => {
|
||||
try {
|
||||
if (typeof app.app_config === "string")
|
||||
return JSON.parse(app.app_config);
|
||||
if (typeof app.app_config === "string") return JSON.parse(app.app_config);
|
||||
return app.app_config || {};
|
||||
} catch {
|
||||
return {};
|
||||
@@ -24,15 +23,13 @@ export function useAppConfig(app: App): Record<string, unknown> {
|
||||
* chatbot-ui / agent-ui 共用。
|
||||
*/
|
||||
export function useSuggestedPrompts(app: App): string[] {
|
||||
return useRef(
|
||||
(() => {
|
||||
try {
|
||||
if (typeof app.suggested_prompts === "string")
|
||||
return JSON.parse(app.suggested_prompts) as string[];
|
||||
return (app.suggested_prompts as string[]) || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
})(),
|
||||
).current;
|
||||
return useMemo(() => {
|
||||
try {
|
||||
if (typeof app.suggested_prompts === "string")
|
||||
return JSON.parse(app.suggested_prompts) as string[];
|
||||
return (app.suggested_prompts as string[]) || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}, [app.suggested_prompts]);
|
||||
}
|
||||
|
||||
@@ -8,24 +8,21 @@ import { toast } from "sonner";
|
||||
* chatbot-ui / agent-ui / doc-writer-ui / analysis-ui 的导出功能共用。
|
||||
*/
|
||||
export function useFileExport() {
|
||||
const download = useCallback(
|
||||
(content: string, filename: string, successMsg = "已导出") => {
|
||||
const blob = new Blob([content], { type: "application/octet-stream" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.style.display = "none";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 30000);
|
||||
toast.success(successMsg);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const download = useCallback((content: string, filename: string, successMsg = "已导出") => {
|
||||
const blob = new Blob([content], { type: "application/octet-stream" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.style.display = "none";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 30000);
|
||||
toast.success(successMsg);
|
||||
}, []);
|
||||
|
||||
return { download };
|
||||
}
|
||||
|
||||
@@ -21,10 +21,7 @@ export function useSSEStream(options: UseSSEStreamOptions = {}) {
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
|
||||
const processStream = useCallback(
|
||||
async (
|
||||
response: Response,
|
||||
onChunk: (accumulated: string) => void,
|
||||
) => {
|
||||
async (response: Response, onChunk: (accumulated: string) => void) => {
|
||||
if (!response.ok) throw new Error("请求失败");
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
@@ -90,14 +87,9 @@ export function useSSEStream(options: UseSSEStreamOptions = {}) {
|
||||
* 更新消息列表中最后一条 assistant 消息的内容。
|
||||
* chatbot-ui / agent-ui / doc-writer-ui / analysis-ui 共用此逻辑。
|
||||
*/
|
||||
export function updateLastAssistantMessage(
|
||||
prev: Message[],
|
||||
content: string,
|
||||
): Message[] {
|
||||
export function updateLastAssistantMessage(prev: Message[], content: string): Message[] {
|
||||
return prev.map((m, i) =>
|
||||
i === prev.length - 1 && m.role === "assistant"
|
||||
? { ...m, content }
|
||||
: m,
|
||||
i === prev.length - 1 && m.role === "assistant" ? { ...m, content } : m,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user