This commit is contained in:
xuhd committed 2026-06-01 14:21:20 +08:00
commit e30b44dae1
68 files changed
+10813 -3976

No files matched your search

@@ -0,0 +1,57 @@
/**
* 外部系统跳转登录时透传的参数:
* theme —— 主题色,"dark-blue" => 深色,其他 => 浅色(在 LoginPage 调 setTheme 直接生效)
* ticket —— 第三方 ticket,持久化到 localStorage 供后续请求使用
* userId —— 外部用户 id,同上
* env —— 环境标识,同上
*
* 这里只暴露 ``ticket / userId / env`` 三个 storage key 和读写工具;
* theme 不存(直接走 ThemeContext,避免双源)。
*/
export const EXTERNAL_LOGIN_STORAGE_KEYS = {
ticket: "login.ticket",
userId: "login.userId",
env: "login.env",
} as const;
export type ExternalLoginParams = {
ticket?: string;
userId?: string;
env?: string;
};
/** 把外部跳转带来的三个参数写到 localStorage;隐私模式 / quota 异常时静默失败 */
export function persistExternalLoginParams(params: ExternalLoginParams): void {
try {
if (params.ticket) {
localStorage.setItem(EXTERNAL_LOGIN_STORAGE_KEYS.ticket, params.ticket);
}
if (params.userId) {
localStorage.setItem(EXTERNAL_LOGIN_STORAGE_KEYS.userId, params.userId);
}
if (params.env) {
localStorage.setItem(EXTERNAL_LOGIN_STORAGE_KEYS.env, params.env);
}
} catch {
// localStorage 不可用时不阻断登录流程
}
}
/** 读取持久化的外部登录参数;不可用时返回空字符串 */
export function readExternalLoginParams(): Required<ExternalLoginParams> {
try {
return {
ticket: localStorage.getItem(EXTERNAL_LOGIN_STORAGE_KEYS.ticket) ?? "",
userId: localStorage.getItem(EXTERNAL_LOGIN_STORAGE_KEYS.userId) ?? "",
env: localStorage.getItem(EXTERNAL_LOGIN_STORAGE_KEYS.env) ?? "",
};
} catch {
return { ticket: "", userId: "", env: "" };
}
}
/** 把 ``theme`` query 值映射成 ThemeContext 的 ``'dark' | 'light'`` */
export function resolveThemeFromQuery(themeParam: string | null | undefined): "dark" | "light" {
return themeParam === "dark-blue" ? "dark" : "light";
}
+45
View File
@@ -0,0 +1,45 @@
import { requestCrudService } from "@/service/request-axios-service";
const CONSUMER_URL = import.meta.env.VITE_AIPI_CONSUMER_URL || "";
export const TOOLS_TOKEN_STORAGE_KEY = "tools_token";
export const EXTERNAL_USER_ID_STORAGE_KEY = "userId";
export const EXTERNAL_USER_INFO_STORAGE_KEY = "userInfo";
export const GENERAL_QA_ROUTE = "/page/strategy/qa/general";
type UserDetailResponse = {
data?: {
id?: string;
[key: string]: unknown;
};
};
/**
* External login via yUserId + optional token (same flow as web-code Login.tsx):
* persist tools_token, resolve user via consumer API, store userId/userInfo.
*/
export async function loginByYUserId(
yUserId: string,
token?: string | null,
): Promise<void> {
if (token) {
localStorage.setItem(TOOLS_TOKEN_STORAGE_KEY, token);
}
const res = (await requestCrudService.get(
`${CONSUMER_URL}/cop/userDetailByYuserId`,
{ yuserId: yUserId },
)) as UserDetailResponse;
const result = res.data;
if (!result?.id) {
throw new Error("无法根据 yUserId 获取用户信息。");
}
localStorage.setItem(EXTERNAL_USER_ID_STORAGE_KEY, result.id);
localStorage.setItem(EXTERNAL_USER_INFO_STORAGE_KEY, JSON.stringify(result));
}
export function persistToolsToken(token: string): void {
localStorage.setItem(TOOLS_TOKEN_STORAGE_KEY, token);
}
@@ -6,7 +6,14 @@ export function isSidebarEnabledFromSearch(search: string): boolean {
const params = new URLSearchParams(
search.startsWith("?") ? search : `?${search}`,
);
return params.get(PAGE_SIDEBAR_QUERY_PARAM) === "1";
if (params.get(PAGE_SIDEBAR_QUERY_PARAM) === "1") {
return true;
}
// External yUserId login enables the page sidebar (see LoginPage + yuser-login).
if (params.get("yUserId")) {
return true;
}
return false;
}
export function persistSidebarEnabled(enabled: boolean): void {
@@ -0,0 +1,316 @@
/**
* 全局侧边栏菜单数据与权限过滤逻辑。
*
* 浅色 ``PageSidebar`` 和深色 ``PageSidebarDark`` 共用这份数据 —— 菜单结构与
* adminOnly 权限策略对两种皮肤完全一致;UI 渲染(icon 显示/折叠样式/hover flyout)
* 在各自组件里实现。
*
* 修改菜单只需改这一处,两个 sidebar 自动同步。
*/
import type { ElementType } from "react";
import {
Bot,
Brain,
CalendarClock,
CircleHelp,
Database,
FileBox,
FileText,
Folder,
Gauge,
Grid3X3,
HelpCircle,
History,
Layout,
Library,
List,
ListChecks,
ListTodo,
MessageSquareCode,
MessageSquarePlus,
Network,
NotebookPen,
Plus,
Settings,
ShieldCheck,
Sparkles,
Target,
Users,
} from "lucide-react";
export type MenuItem = {
id: string;
label: string;
icon: ElementType;
path?: string;
adminOnly?: boolean;
children?: MenuItem[];
};
export const sidebarMenuItems: MenuItem[] = [
{
id: "qa-center",
label: "问答中心",
icon: HelpCircle,
children: [
{ id: "general-qa", label: "通用问答", icon: CircleHelp, path: "/page/workspace/chats/new" },
{ id: "expert-qa", label: "专家问答", icon: Brain, path: "/page/strategy/qa/expert-qa" },
// 「圆桌规划」入口暂时隐藏 —— 功能仍在 /page/roundtable/planning,只是不在侧边栏出现。
// 需要重新开放时取消注释即可。
// {
// id: "qa-roundtable",
// label: "圆桌规划",
// icon: Bot,
// path: "/page/roundtable/planning",
// },
{ id: "qa-zzjc", label: "zzjc支持", icon: Bot },
{ id: "qa-zzxd", label: "zzXD应用", icon: Layout },
{ id: "qa-research", label: "课题研究", icon: FileText, path: "/page/canvas/ai-writing" },
],
},
// {
// id: "light-app-center",
// label: "轻应用",
// icon: Grid3X3,
// children: [
// {
// id: "light-app-note-management",
// label: "笔记管理",
// icon: NotebookPen,
// children: [
// {
// id: "light-app-new-note",
// label: "新建笔记",
// icon: Plus,
// path: "/page/strategy/light-app/new-note",
// },
// {
// id: "light-app-note-list",
// label: "笔记列表",
// icon: List,
// path: "/page/strategy/light-app/notes",
// },
// {
// id: "light-app-note-qa",
// label: "笔记问答",
// icon: MessageSquareCode,
// path: "/page/strategy/light-app/note-qa",
// },
// ],
// },
// { id: "light-app-square", label: "应用广场", icon: Layout },
// { id: "light-app-records", label: "运行记录", icon: History },
// ],
// },
{
id: "task-management",
label: "任务管理",
icon: Target,
children: [
{
id: "xk-center-task",
label: "XK中任务",
icon: Folder,
children: [
{
id: "xk-task-jc",
label: "任务JC",
icon: ListTodo,
path: "/page/strategy/task-management/xk-task-jc",
},
{
id: "xk-task-dispatch",
label: "任务调度",
icon: ListTodo,
path: "/page/strategy/task-management/xk-task-dispatch",
},
],
},
{
id: "coh-center-task",
label: "COH中任务",
icon: Folder,
children: [
{
id: "coh-task-3qfx",
label: "3QFX",
icon: ListTodo,
path: "/page/strategy/task-management/plan-task-sentiment",
},
{
id: "coh-task-fx",
label: "任务FX",
icon: ListTodo,
path: "/page/strategy/task-management/plan-task/plan-task-fx",
},
{
id: "coh-task-xdfx",
label: "XDFX",
icon: ListTodo,
path: "/page/strategy/task-management/plan-task/plan-task-action",
},
],
},
{
id: "pg-center-task",
label: "PG中任务",
icon: Folder,
children: [
{
id: "pg-task-pg",
label: "任务PG",
icon: ListTodo,
path: "/page/strategy/task-management/effect-task-pg",
},
{
id: "pg-task-xdpg",
label: "XDPG",
icon: ListTodo,
path: "/page/strategy/task-management/effect-action-pg",
},
{
id: "pg-task-info-pg",
label: "信息PG",
icon: ListTodo,
path: "/page/strategy/task-management/effect-info-pg",
},
],
},
{
id: "ty-center-task",
label: "TY中任务",
icon: Folder,
children: [
{
id: "ty-task-xdty",
label: "XDTY任务",
icon: ListTodo,
path: "/page/strategy/task-management/mnty-task-mechanism",
},
{
id: "ty-task-plan",
label: "方案TY任务",
icon: ListTodo,
path: "/page/strategy/task-management/mnty-task-plan",
},
{
id: "ty-task-xxty",
label: "XXTY任务",
icon: ListTodo,
path: "/page/strategy/task-management/mnty-task-zsty",
},
],
},
],
},
{
id: "comprehensive-management",
label: "综合管理",
icon: Settings,
children: [
{ id: "mgmt-agents", label: "智能体管理", icon: Bot, path: "/page/strategy/agents" },
{ id: "mgmt-skills", label: "技能管理", icon: Sparkles, path: "/page/strategy/skills" },
{ id: "mgmt-memory", label: "记忆管理", icon: Brain, path: "/page/strategy/memory" },
{
id: "mgmt-tasks",
label: "定时任务",
icon: ListChecks,
path: "/page/strategy/scheduled/tasks",
},
{
id: "mgmt-recommended-questions",
label: "推荐问题配置",
icon: MessageSquarePlus,
path: "/page/strategy/settings/recommended-questions",
adminOnly: true,
},
{
id: "mgmt-prompt-prefix",
label: "问答前缀配置",
icon: MessageSquareCode,
path: "/page/strategy/settings/prompt-prefix",
adminOnly: true,
},
{
id: "mgmt-private-square",
label: "私密广场配置",
icon: ShieldCheck,
path: "/page/strategy/settings/private-square",
adminOnly: true,
},
{
id: "mgmt-llm-metrics",
label: "大模型调用统计",
icon: Gauge,
path: "/page/strategy/settings/llm-metrics",
adminOnly: true,
},
{
id: "mgmt-users",
label: "用户管理",
icon: Users,
path: "/page/strategy/admin/users",
adminOnly: true,
},
{
id: "mgmt-admin-scheduled-tasks",
label: "定时任务管理",
icon: CalendarClock,
path: "/page/strategy/admin/scheduled-tasks",
adminOnly: true,
},
{
id: "mgmt-user-activity",
label: "用户活动审计",
icon: History,
path: "/page/strategy/admin/user-activity",
adminOnly: true,
},
],
},
{
id: "resource-management",
label: "资源管理",
icon: Database,
children: [
{
id: "resource-network-xwt",
label: "网络xwt资源管理",
icon: Network,
path: "/page/strategy/resource-management/resource-network-actor",
},
{
id: "resource-knowledge-base",
label: "知识库资源管理",
icon: Library,
path: "/page/strategy/resource-management/resource-knowledge-base",
},
{
id: "resource-evaluation-index",
label: "作战资源管理",
icon: FileBox,
path: "/page/strategy/resource-management/resource-evaluation-index",
},
],
},
];
/** 默认展开的菜单 id 集合(浅色版可折叠交互的初始状态) */
export const defaultExpandedIds: ReadonlySet<string> = new Set([
"qa-center",
"light-app-center",
"light-app-note-management",
"task-management",
"xk-center-task",
]);
/** 按 admin 角色过滤菜单:非 admin 不展示 ``adminOnly: true`` 的项及其子项 */
export function filterMenuItemsByRole(items: MenuItem[], isAdmin: boolean): MenuItem[] {
return items
.filter((item) => !item.adminOnly || isAdmin)
.map((item) =>
item.children
? { ...item, children: filterMenuItemsByRole(item.children, isAdmin) }
: item,
);
}