import React, { useEffect, useRef, useMemo, useState, Fragment } from "react";
import Image from 'next/image';
import styles from "./home.module.scss";
import newStyles from './sidebar.module.scss';
import DragIcon from "../icons/drag.svg";
import logoSrc from "../icons/logo.png";
import deepSeekSrc from "../icons/deepSeek.png";
import { AppstoreOutlined, EditOutlined, MenuOutlined, HomeOutlined, PlusOutlined, StarOutlined, CommentOutlined } from '@ant-design/icons';
import * as AllIcons from '@ant-design/icons';
import { useAppConfig, useChatStore, useGlobalStore } from "../store";
import {
DEFAULT_SIDEBAR_WIDTH,
MAX_SIDEBAR_WIDTH,
MIN_SIDEBAR_WIDTH,
NARROW_SIDEBAR_WIDTH,
} from "../constant";
import { useLocation, useNavigate } from "react-router-dom";
import { isIOS, useMobileScreen, getContrastColor } from "../utils";
import api from "@/app/api/api";
import { Button, Drawer, Dropdown, Empty, Form, Input, Menu, message, Modal, Rate, Tag, Select } from "antd";
import { downloadFile } from "../utils/index";
import dayjs from "dayjs";
import type { DrawerProps, RadioChangeEvent } from 'antd';
import '@/app/styles/common.scss'
const FormItem = Form.Item;
import { processSliceData } from "@/app/utils/index";
export function useHotKey() {
const chatStore = useChatStore();
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.altKey || e.ctrlKey) {
if (e.key === "ArrowUp") {
chatStore.nextSession(-1);
} else if (e.key === "ArrowDown") {
chatStore.nextSession(1);
}
}
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
});
}
export function useDragSideBar() {
const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
const config = useAppConfig();
const startX = useRef(0);
const startDragWidth = useRef(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH);
const lastUpdateTime = useRef(Date.now());
const toggleSideBar = () => {
config.update((config) => {
if (config.sidebarWidth < MIN_SIDEBAR_WIDTH) {
config.sidebarWidth = DEFAULT_SIDEBAR_WIDTH;
} else {
config.sidebarWidth = NARROW_SIDEBAR_WIDTH;
}
});
};
const onDragStart = (e: MouseEvent) => {
// Remembers the initial width each time the mouse is pressed
startX.current = e.clientX;
startDragWidth.current = config.sidebarWidth;
const dragStartTime = Date.now();
const handleDragMove = (e: MouseEvent) => {
if (Date.now() < lastUpdateTime.current + 20) {
return;
}
lastUpdateTime.current = Date.now();
const d = e.clientX - startX.current;
const nextWidth = limit(startDragWidth.current + d);
config.update((config) => {
if (nextWidth < MIN_SIDEBAR_WIDTH) {
config.sidebarWidth = NARROW_SIDEBAR_WIDTH;
} else {
config.sidebarWidth = nextWidth;
}
});
};
const handleDragEnd = () => {
// In useRef the data is non-responsive, so `config.sidebarWidth` can't get the dynamic sidebarWidth
window.removeEventListener("pointermove", handleDragMove);
window.removeEventListener("pointerup", handleDragEnd);
// if user click the drag icon, should toggle the sidebar
const shouldFireClick = Date.now() - dragStartTime < 300;
if (shouldFireClick) {
toggleSideBar();
}
};
window.addEventListener("pointermove", handleDragMove);
window.addEventListener("pointerup", handleDragEnd);
};
const isMobileScreen = useMobileScreen();
const shouldNarrow =
!isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH;
useEffect(() => {
const barWidth = shouldNarrow
? NARROW_SIDEBAR_WIDTH
: limit(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH);
const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
}, [config.sidebarWidth, isMobileScreen, shouldNarrow]);
return {
onDragStart,
shouldNarrow,
};
}
export function SideBarContainer(props: {
children: React.ReactNode;
onDragStart: (e: MouseEvent) => void;
shouldNarrow: boolean;
className?: string;
}) {
const isMobileScreen = useMobileScreen();
const isIOSMobile = useMemo(
() => isIOS() && isMobileScreen,
[isMobileScreen],
);
const { children, className, onDragStart, shouldNarrow } = props;
// shadow-sidebar
return (
{children}
onDragStart(e as any)}
>
);
}
// Sidebar 头部
export function SideBarHeader(props: {
title?: string | React.ReactNode;
subTitle?: string | React.ReactNode;
logo?: React.ReactNode;
children?: React.ReactNode;
}) {
const { title, subTitle, logo, children } = props;
const navigate = useNavigate();
return (
{
window.open('http://10.1.14.17:3200/appCenter')
}} >
{logo}
{children}
);
}
export function SideBarBody(props: {
children: React.ReactNode;
onClick?: (e: React.MouseEvent) => void;
}) {
const { onClick, children } = props;
return (
{children}
);
}
export function SideBarTail(props: {
primaryAction?: React.ReactNode;
secondaryAction?: React.ReactNode;
}) {
const { primaryAction, secondaryAction } = props;
return (
{primaryAction}
{secondaryAction}
);
}
interface AppDrawerProps {
isMobileScreen: boolean,
selectedAppId: string,
type: 'all' | 'collect',
open: boolean,
onClose: () => void,
}
export const SideBar = (props: { className?: string }) => {
// useHotKey();
const { onDragStart, shouldNarrow } = useDragSideBar();
const [showPluginSelector, setShowPluginSelector] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const chatStore = useChatStore();
const globalStore = useGlobalStore();
const [menuList, setMenuList] = useState([])
const [modalOpen, setModalOpen] = useState(false)
const [form] = Form.useForm();
const getType = (): 'bigModel' | 'deepSeek' => {
if (['/knowledgeChat', '/newChat'].includes(location.pathname)) {
return 'bigModel';
} else if (['/deepseekChat', '/newDeepseekChat','/welcome'].includes(location.pathname)) {
return 'deepSeek';
} else {
return 'bigModel';
}
}
// 获取应用类型 app_type
const fetchAppType = async () => {
try {
const res = await api.get(`/deepseek/api/app_type`);
// 解析返回并设置状态
if (res && res.data) {
setAppTypes([{ dictLabel: '收藏', dictValue: '收藏' }, ...res.data]);
}
} catch (error) {
console.error('Failed to fetch app types:', error);
}
}
// 获取应用列表
const fetchGetApplicationList = async (typeId?: string | null, name?: string) => {
setAppListLoading(true);
try {
const data = {
pageSize: 1000,
pageNum: 1,
userId: 1,
isCollect: typeId === '收藏' ? '1' : null,
typeId: typeId === '收藏' ? null : typeId,
name: name,
}
const res: any = await api.post('/deepseek/api/getApplicationList', data);
// 解析返回并设置状态
if (res && res.rows) {
setAppListState(res.rows);
if (name) {
setSearchOptions(res.rows.map((item: any) => ({ label: item.name, value: item.appId })));
}
}
} catch (error) {
console.error('Failed to fetch app list:', error);
} finally {
setAppListLoading(false);
setSearchFetching(false);
}
}
// 应用类型与列表状态
const [appTypes, setAppTypes] = useState([]);
const [appListState, setAppListState] = useState([]);
const [appListLoading, setAppListLoading] = useState(false);
const [openKeys, setOpenKeys] = useState([]); // 当前打开的菜单项
// 渲染应用列表为 Antd Menu(一级:类型,带图标;二级:该类型下的应用)
const previewAppList = () => {
const iconFor = (index: number) => {
const icons = [, , ];
return icons[index % icons.length];
};
const items = appTypes && appTypes.length
? appTypes.map((t: any, idx: number) => {
// 当该一级是当前打开项时,使用 appListState 作为 children(fetchGetApplicationList 填充)
const typeKey = `${t.dictValue}`;
let children = [] as any[];
// if (openKeys.includes(typeKey)) {
if (appListLoading) {
children = [{ key: `${typeKey}-loading`, label: 加载中... }];
} else {
children = (appListState || []).map((a: any, i: number) => ({
key: a.appId || `app-${idx}-${i}`,
// label: a.dictLabel || a.name || a.appName || `应用 ${i}`,
label: a.iconColor ? (() => {
const C = (AllIcons as any)[a.iconType];
const iconColor = getContrastColor(a.iconColor);
return C ?
{a.name || a.appName || `应用 ${i}`}
: {a.iconType}
})() :
{a.name || a.appName || `应用 ${i}`}
,
onClick: () => {
chatStore.updateCurrentSession((value) => {
value.appId = a.appId;
});
// 点击二级应用的处理:打印或导航(保留为 UI 先)
chatStore.clearSessions();
if (getType() === 'bigModel') {
globalStore.setSelectedAppId(a.appId);
} else {
const search = `?showMenu=false&chatMode=LOCAL&appId=${a.appId}`;
navigate({
pathname: '/knowledgeChat',
search: search,
})
globalStore.setSelectedAppId(a.appId);
// location.reload();
}
}
}));
}
// }
if (openKeys.includes(typeKey)) {
return {
key: t.dictValue,
icon: iconFor(idx),
label: t.dictLabel || `类型 ${idx}`,
children: children.length ? children : [{ key: `empty-${idx}`, label: 暂无应用, }],
};
} else {
return {
key: t.dictValue,
icon: iconFor(idx),
label: t.dictLabel || `类型 ${idx}`,
children: [],
};
}
})
: [];
return (