From db63086c3829ffab5733fe42170dfdf4c4efe386 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Thu, 14 May 2026 09:35:15 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 服务启停说明.md | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 服务启停说明.md diff --git a/服务启停说明.md b/服务启停说明.md new file mode 100644 index 00000000..348fa7e6 --- /dev/null +++ b/服务启停说明.md @@ -0,0 +1,113 @@ +# 后端服务启停说明(Windows) + +你当前是 **Windows + PowerShell** 环境,下面这份是可直接用的后端启停命令。 + +> 说明:仓库里的 `scripts/*.sh` 是 Bash 脚本,Windows 原生 PowerShell 不能直接执行,所以会出现“启动不了”。 + +## 一、后端目录 + +后端目录: + +```text +offline-backend-20260512/backend/ +``` + +下面命令默认都在这个目录执行。 + +--- + +## 二、Windows 下推荐启动方式 + +### 1) 开发模式启动(热重载) + +```powershell +uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 --reload +``` + +### 2) 普通模式启动(无热重载) + +```powershell +uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 +``` + +> 如果你习惯 `make dev / make gateway`,Windows 上通常还需要额外安装 make,建议直接用上面两条 `uv run uvicorn` 更稳。 + +--- + +## 三、停止后端 + +如果后端在当前窗口前台运行: + +- 直接按 `Ctrl + C` + +如果窗口已经关了,或你想强制结束 8001 端口进程: + +```powershell +netstat -ano | findstr :8001 +``` + +记下最后一列 PID 后执行: + +```powershell +taskkill /PID /F +``` + +--- + +## 四、首次安装依赖(只需一次) + +在 `offline-backend-20260512/backend` 目录执行: + +```powershell +uv sync +``` + +如果 `uv` 命令不存在,先安装(任选其一): + +```powershell +pip install uv +``` + +或(已安装 winget 时): + +```powershell +winget install --id Astral-sh.uv -e +``` + +--- + +## 五、快速自检 + +启动后浏览器访问: + +- [http://localhost:8001/health](http://localhost:8001/health) + +返回健康状态即表示后端正常。 + +--- + +## 六、常见问题 + +### 1) 报错:端口 8001 被占用 + +先执行: + +```powershell +netstat -ano | findstr :8001 +``` + +然后 `taskkill` 对应 PID,再重启后端。 + +### 2) 报错:`uv` 不是内部或外部命令 + +先安装 `uv`(见上面的“首次安装依赖”),安装后重开一个 PowerShell 窗口再试。 + +### 3) 报错:模块/依赖缺失 + +重新执行: + +```powershell +uv sync +``` + +再启动。 From 65e89a424867686f8fdd06ca74b30ee9526a62b7 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Thu, 14 May 2026 09:36:45 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-web/src/pages/PageRoutes.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frontend-web/src/pages/PageRoutes.tsx b/frontend-web/src/pages/PageRoutes.tsx index 93eb74f2..102aec7f 100644 --- a/frontend-web/src/pages/PageRoutes.tsx +++ b/frontend-web/src/pages/PageRoutes.tsx @@ -5,18 +5,23 @@ import { PageSidebar } from "@/components/page-sidebar"; import WorkspaceRoutes from "./WorkspaceRoutes"; +// 设为 true 时隐藏侧边栏(开发中,暂未完成) +const SIDEBAR_ENABLED = false; + function PageLayout() { const [collapsed, setCollapsed] = useState(false); return (
- setCollapsed((prev) => !prev)} - /> + {SIDEBAR_ENABLED && ( + setCollapsed((prev) => !prev)} + /> + )}
From e286e59431cc791698ff0d90da085d23c46176cf Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Thu, 14 May 2026 09:42:12 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-web/src/pages/PageRoutes.tsx | 9 +++++-- frontend-web/src/pages/WorkspaceRoutes.tsx | 28 +++++++++++++++------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/frontend-web/src/pages/PageRoutes.tsx b/frontend-web/src/pages/PageRoutes.tsx index 102aec7f..b227d3ad 100644 --- a/frontend-web/src/pages/PageRoutes.tsx +++ b/frontend-web/src/pages/PageRoutes.tsx @@ -21,14 +21,19 @@ function PageLayout() { )}
} /> } + element={ + + } />
diff --git a/frontend-web/src/pages/WorkspaceRoutes.tsx b/frontend-web/src/pages/WorkspaceRoutes.tsx index 15b7eb1e..bf1527a6 100644 --- a/frontend-web/src/pages/WorkspaceRoutes.tsx +++ b/frontend-web/src/pages/WorkspaceRoutes.tsx @@ -20,16 +20,24 @@ import ScheduledTasksPage from "./ScheduledTasksPage"; function WorkspaceLayout({ children, pageSidebarCollapsed, + pageSidebarHidden = false, }: { children: ReactNode; pageSidebarCollapsed: boolean; + pageSidebarHidden?: boolean; }) { + const pageSidebarWidth = pageSidebarHidden + ? "0rem" + : pageSidebarCollapsed + ? "4rem" + : "16rem"; + return ( } /> + } @@ -74,7 +86,7 @@ export default function WorkspaceRoutes({ + @@ -84,7 +96,7 @@ export default function WorkspaceRoutes({ + } @@ -92,7 +104,7 @@ export default function WorkspaceRoutes({ + } @@ -104,7 +116,7 @@ export default function WorkspaceRoutes({ + @@ -118,7 +130,7 @@ export default function WorkspaceRoutes({ + } @@ -126,7 +138,7 @@ export default function WorkspaceRoutes({ + } From c79f5c5a7a6012cd7297116209464721fb233c34 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Thu, 14 May 2026 10:42:37 +0800 Subject: [PATCH 04/15] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/workspace/input-box.tsx | 64 +++++++++++++++---- .../workspace/messages/message-list.tsx | 2 +- .../workspace/messages/skeleton.tsx | 2 +- .../src/components/workspace/welcome.tsx | 8 +-- frontend-web/src/core/i18n/locales/zh-CN.ts | 2 +- frontend-web/src/pages/ChatPage.tsx | 4 +- 6 files changed, 59 insertions(+), 23 deletions(-) diff --git a/frontend-web/src/components/workspace/input-box.tsx b/frontend-web/src/components/workspace/input-box.tsx index 85dc3dd4..7bc633fa 100644 --- a/frontend-web/src/components/workspace/input-box.tsx +++ b/frontend-web/src/components/workspace/input-box.tsx @@ -86,6 +86,30 @@ import { Tooltip } from "./tooltip"; type InputMode = "flash" | "thinking" | "pro" | "ultra"; +// 推荐问题列表 — 可自由配置 title(卡片标题)和 content(发送给对话的内容) +const RECOMMENDED_QUESTIONS: { title: string; content: string }[] = [ + { + title: "市场分析报告", + content: "请帮我写一份关于当前 AI 市场的详细分析报告,包括市场规模、主要玩家和发展趋势。", + }, + { + title: "制定学习计划", + content: "请帮我制定一个为期 3 个月的 Python 编程学习计划,从基础到进阶,每周安排具体任务。", + }, + { + title: "代码审查建议", + content: "请帮我审查代码,找出潜在的问题、性能瓶颈以及改进建议,并给出修改示例。", + }, + { + title: "数据分析方案", + content: "请帮我设计一个用于分析用户行为数据的方案,包括数据收集、处理和可视化策略。", + }, + { + title: "文章润色优化", + content: "请帮我润色以下文章,使其更加流畅、专业,并保持原意不变。", + }, +]; + function getResolvedMode( mode: InputMode | undefined, supportsThinking: boolean, @@ -430,7 +454,32 @@ export function InputBox({ }, [context.model_name, disabled, isMock, status, thread.messages, threadId]); return ( -
+
+ {extraHeader && ( +
+ {extraHeader} +
+ )} + + {isNewThread && searchParams.get("mode") !== "skill" && ( +
+ {RECOMMENDED_QUESTIONS.map((q) => ( + + ))} +
+ )} + {showFollowups && (
@@ -473,13 +522,6 @@ export function InputBox({ onSubmit={handleSubmit} {...props} > - {extraHeader && ( -
-
- {extraHeader} -
-
- )} {(attachment) => } @@ -846,12 +888,6 @@ export function InputBox({ )} - {isNewThread && searchParams.get("mode") !== "skill" && ( -
- -
- )} - diff --git a/frontend-web/src/components/workspace/messages/message-list.tsx b/frontend-web/src/components/workspace/messages/message-list.tsx index 4a37f957..b9c8b90b 100644 --- a/frontend-web/src/components/workspace/messages/message-list.tsx +++ b/frontend-web/src/components/workspace/messages/message-list.tsx @@ -68,7 +68,7 @@ export function MessageList({ - + {groupMessages(messages, (group) => { if (group.type === "human" || group.type === "assistant") { return group.messages.map((msg) => { diff --git a/frontend-web/src/components/workspace/messages/skeleton.tsx b/frontend-web/src/components/workspace/messages/skeleton.tsx index 66601b92..77fce907 100644 --- a/frontend-web/src/components/workspace/messages/skeleton.tsx +++ b/frontend-web/src/components/workspace/messages/skeleton.tsx @@ -24,7 +24,7 @@ function SkeletonBar({ export function MessageListSkeleton() { let index = 0; return ( -
+
{searchParams.get("mode") === "skill" ? (
- {t.welcome.createYourOwnSkillDescription.includes("\n") ? ( + {/* {t.welcome.createYourOwnSkillDescription.includes("\n") ? (
               {t.welcome.createYourOwnSkillDescription}
             
) : (

{t.welcome.createYourOwnSkillDescription}

- )} + )} */}
) : (
- {t.welcome.description.includes("\n") ? ( + {/* {t.welcome.description.includes("\n") ? (
               {t.welcome.description}
             
) : (

{t.welcome.description}

- )} + )} */}
)}
diff --git a/frontend-web/src/core/i18n/locales/zh-CN.ts b/frontend-web/src/core/i18n/locales/zh-CN.ts index 64d52a81..8fec05ae 100644 --- a/frontend-web/src/core/i18n/locales/zh-CN.ts +++ b/frontend-web/src/core/i18n/locales/zh-CN.ts @@ -59,7 +59,7 @@ export const zhCN: Translations = { // Welcome welcome: { - greeting: "你好,欢迎回来!", + greeting: "你好,欢迎使用CM助手", description: "欢迎使用 CM 助手", diff --git a/frontend-web/src/pages/ChatPage.tsx b/frontend-web/src/pages/ChatPage.tsx index 8a2cd504..0ac3427c 100644 --- a/frontend-web/src/pages/ChatPage.tsx +++ b/frontend-web/src/pages/ChatPage.tsx @@ -145,8 +145,8 @@ export default function ChatPage() { "relative w-full", isLandingState && "-translate-y-[calc(50vh-96px)]", isLandingState - ? "max-w-(--container-width-sm)" - : "max-w-(--container-width-md)", + ? "max-w-(--container-width-md)" + : "max-w-(--container-width-lg)", )} >
From a7f1e89ce9e25fcb61d219ef71b7bb883bfd0bba Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Thu, 14 May 2026 10:51:29 +0800 Subject: [PATCH 05/15] =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=8D=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/workspace/input-box.tsx | 33 +-- .../workspace/workspace-nav-menu.tsx | 13 +- .../src/core/recommended-questions/hooks.ts | 58 +++++ .../src/core/recommended-questions/index.ts | 57 ++++ .../src/pages/RecommendedQuestionsPage.tsx | 246 ++++++++++++++++++ frontend-web/src/pages/WorkspaceRoutes.tsx | 9 + 6 files changed, 389 insertions(+), 27 deletions(-) create mode 100644 frontend-web/src/core/recommended-questions/hooks.ts create mode 100644 frontend-web/src/core/recommended-questions/index.ts create mode 100644 frontend-web/src/pages/RecommendedQuestionsPage.tsx diff --git a/frontend-web/src/components/workspace/input-box.tsx b/frontend-web/src/components/workspace/input-box.tsx index 7bc633fa..4bc67415 100644 --- a/frontend-web/src/components/workspace/input-box.tsx +++ b/frontend-web/src/components/workspace/input-box.tsx @@ -80,35 +80,14 @@ import { DropdownMenuTrigger, } from "../ui/dropdown-menu"; +import { useRecommendedQuestions } from "@/core/recommended-questions/hooks"; + import { useThread } from "./messages/context"; import { ModeHoverGuide } from "./mode-hover-guide"; import { Tooltip } from "./tooltip"; type InputMode = "flash" | "thinking" | "pro" | "ultra"; -// 推荐问题列表 — 可自由配置 title(卡片标题)和 content(发送给对话的内容) -const RECOMMENDED_QUESTIONS: { title: string; content: string }[] = [ - { - title: "市场分析报告", - content: "请帮我写一份关于当前 AI 市场的详细分析报告,包括市场规模、主要玩家和发展趋势。", - }, - { - title: "制定学习计划", - content: "请帮我制定一个为期 3 个月的 Python 编程学习计划,从基础到进阶,每周安排具体任务。", - }, - { - title: "代码审查建议", - content: "请帮我审查代码,找出潜在的问题、性能瓶颈以及改进建议,并给出修改示例。", - }, - { - title: "数据分析方案", - content: "请帮我设计一个用于分析用户行为数据的方案,包括数据收集、处理和可视化策略。", - }, - { - title: "文章润色优化", - content: "请帮我润色以下文章,使其更加流畅、专业,并保持原意不变。", - }, -]; function getResolvedMode( mode: InputMode | undefined, @@ -185,6 +164,8 @@ export function InputBox({ null, ); + const [recommendedQuestions] = useRecommendedQuestions(); + useEffect(() => { if (models.length === 0) { return; @@ -461,11 +442,11 @@ export function InputBox({
)} - {isNewThread && searchParams.get("mode") !== "skill" && ( + {isNewThread && searchParams.get("mode") !== "skill" && recommendedQuestions.length > 0 && (
- {RECOMMENDED_QUESTIONS.map((q) => ( + {recommendedQuestions.map((q) => ( +
+ + {questions.length === 0 ? ( +
+

暂无推荐问题

+ +
+ ) : ( +
+ {questions.map((q, index) => ( +
handleDragStart(index)} + onDragOver={(e) => handleDragOver(e, index)} + onDrop={() => handleDrop(index)} + onDragEnd={() => { + setDragIndex(null); + setDragOverIndex(null); + }} + className={`flex items-start gap-3 rounded-xl border bg-white px-4 py-3.5 transition-colors ${ + dragOverIndex === index && dragIndex !== index + ? "border-blue-400 bg-blue-50" + : "border-[#e4e4e7]" + }`} + > + +
+
+ {q.title} +
+
+ {q.content} +
+
+
+ + +
+
+ ))} +
+ )} + + {/* 添加 / 编辑弹窗 */} + !o && closeDialog()}> + + + + {dialog?.mode === "add" ? "添加推荐问题" : "编辑推荐问题"} + + +
+
+

卡片标题

+ setFormTitle(e.target.value)} + /> +
+
+

发送内容

+