774 lines
25 KiB
TypeScript
774 lines
25 KiB
TypeScript
'use client'
|
||
|
||
import React from 'react'
|
||
|
||
import { Suspense } from 'react'
|
||
|
||
import { auth } from '@/auth'
|
||
import { clearChats } from '@/app/actions'
|
||
import { Sidebar } from '@/components/sidebar'
|
||
import { SidebarList } from '@/components/sidebar-list'
|
||
import { IconSeparator } from '@/components/ui/icons'
|
||
import { SidebarFooter } from '@/components/sidebar-footer'
|
||
import { ClearHistory } from '@/components/clear-history'
|
||
import { UserMenu, UserData } from '@/components/user-menu'
|
||
import { LoginButton } from '@/components/login-button'
|
||
import {
|
||
Tooltip,
|
||
TooltipContent,
|
||
TooltipTrigger
|
||
} from '@/components/ui/tooltip'
|
||
import { Badge } from '@/components/ui/badge'
|
||
import { ConnectButton } from '@/components/connect-button'
|
||
import { SettingsDropDown } from './settings-drop-down'
|
||
|
||
|
||
import { useLocalStorage } from '@/lib/hooks/use-local-storage'
|
||
import { Button } from './ui/button'
|
||
|
||
import Image from 'next/image';
|
||
import logoImage from '@/components/images/logo.png';
|
||
import { useRouter } from 'next/navigation'
|
||
|
||
// import { Button } from './ui/button'
|
||
import { Flex, Text } from '@radix-ui/themes';
|
||
import Link from 'next/link'
|
||
import { useTranslation } from 'react-i18next'
|
||
import { message } from 'antd'
|
||
import { LogoAI } from '@/components/chat'
|
||
|
||
import { useState, useRef, } from "react";
|
||
|
||
import { Trash2 } from "lucide-react";
|
||
|
||
import { useEffect } from "react";
|
||
import {
|
||
BadgeInfo,
|
||
Tags,
|
||
CalendarClock,
|
||
Building2
|
||
} from "lucide-react";
|
||
|
||
|
||
export function Header() {
|
||
|
||
const router = useRouter();
|
||
const { t } = useTranslation();
|
||
|
||
const [userData, setUserData] = useLocalStorage(
|
||
'UserData',
|
||
{
|
||
auth_token: "",
|
||
id: 1,
|
||
login_ip: "",
|
||
login_time: 0,
|
||
role: "",
|
||
user_name: "",
|
||
version: ""
|
||
} as UserData
|
||
)
|
||
|
||
const soonFunc = () => {
|
||
message.info(t("soon"))
|
||
}
|
||
//w-11/12 sm:w-5/6 md:w-3/4 lg:w-2/3 xl:w-3/5 2xl:w-1/2
|
||
return (
|
||
<header className="sticky top-0 z-50 flex shrink-0 items-center justify-between bg-background px-4 py-[1.5rem] w-[80%] mx-auto">
|
||
|
||
<a href="/">
|
||
<LogoAI className="flex text-center ml-0" />
|
||
</a>
|
||
|
||
<div className="flex items-center justify-between ">
|
||
<div className="flex items-center ">
|
||
{(!!userData && !!userData.user_name) ? (
|
||
<div className='flex text-[#1A1A1A]'>
|
||
<UserMenu user={userData} />
|
||
</div>
|
||
|
||
) : (
|
||
<div className='flex gap-4 grid-cols-2 text-[#1A1A1A]'>
|
||
<Button variant="ghost" className='text-base bg-[#f4f4f5] hover:bg-[#e5e7eb]'>
|
||
<Link href="/#subscribe-target"
|
||
>
|
||
{t('subscribe.subscribe')}
|
||
</Link>
|
||
</Button>
|
||
|
||
|
||
<LoginButton
|
||
variant="ghost"
|
||
// variant="link"
|
||
showGithubIcon={true}
|
||
text={t('login')}
|
||
className="-ml-2 text-base bg-[#f4f4f5] hover:bg-[#e5e7eb]"
|
||
/>
|
||
</div>
|
||
|
||
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
</header>
|
||
)
|
||
}
|
||
|
||
|
||
// ✅ 图标显示逻辑现在是这样的:
|
||
// data.icon = "🚀" 👉 显示 emoji;
|
||
// data.icon = "/images/icon.png" 👉 显示图片;
|
||
// data.icon = undefined/null 👉 显示默认 "Deploy" 字样。
|
||
|
||
// export function DetailPageHeader({ data }: { data: any }) {
|
||
// const [loading, setLoading] = useState(false);
|
||
// const [statusText, setStatusText] = useState(data?.statusText || "加载中...");
|
||
// const [progress, setProgress] = useState(data?.progress || "0%");
|
||
// const [showDelete, setShowDelete] = useState(true); // 默认可以删除
|
||
|
||
// // const handleClick = async (source: "icon" | "info") => {
|
||
// // setLoading(true);
|
||
// // setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作...");
|
||
|
||
// // try {
|
||
// // // 从 localStorage 获取用户信息
|
||
// // const userData = JSON.parse(localStorage.getItem("UserData") || "null");
|
||
|
||
// // if (!userData || !userData.user_name) {
|
||
// // setStatusText("未登录,正在跳转登录页面...");
|
||
// // window.location.href = "/auth/sign-in/";
|
||
// // return;
|
||
// // }
|
||
|
||
// // const userName = userData.user_name;
|
||
|
||
// // // 从组件 props 里的 data 中取 id
|
||
// // const id = data?.id;
|
||
// // if (!id) {
|
||
// // setStatusText("数据缺失:找不到组件 ID");
|
||
// // return;
|
||
// // }
|
||
|
||
// // // 发起部署请求
|
||
// // const res = await fetch("/api/v1/deploy/deploy", {
|
||
// // method: "POST",
|
||
// // headers: { "Content-Type": "application/json" },
|
||
// // body: JSON.stringify({
|
||
// // id,
|
||
// // user_name: userName,
|
||
// // }),
|
||
// // });
|
||
|
||
// // if (!res.ok) {
|
||
// // throw new Error(`HTTP 请求失败:${res.status}`);
|
||
// // }
|
||
|
||
// // const json = await res.json();
|
||
|
||
// // if (json?.header?.code === 0) {
|
||
// // setStatusText(`${json.header.message || "操作成功"}`);
|
||
// // } else {
|
||
// // setStatusText(`${json.header.message || "操作失败(后端返回错误)"}`);
|
||
// // }
|
||
// // } catch (err) {
|
||
// // console.error("请求出错:", err);
|
||
// // setStatusText("操作失败,请检查网络或服务状态");
|
||
// // } finally {
|
||
// // setLoading(false);
|
||
// // }
|
||
// // };
|
||
|
||
// const handleClick = async (source: "icon" | "info") => {
|
||
// setLoading(true);
|
||
// setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作...");
|
||
|
||
// try {
|
||
// const userData = JSON.parse(localStorage.getItem("UserData") || "null");
|
||
|
||
// if (!userData || !userData.user_name) {
|
||
// setStatusText("未登录,正在跳转登录页面...");
|
||
// window.location.href = "/auth/sign-in/";
|
||
// return;
|
||
// }
|
||
|
||
// const userName = userData.user_name;
|
||
// const id = data?.id;
|
||
|
||
// if (!id) {
|
||
// setStatusText("数据缺失:找不到组件 ID");
|
||
// return;
|
||
// }
|
||
|
||
// // 发起部署请求
|
||
// const res = await fetch("/api/v1/deploy/deploy", {
|
||
// method: "POST",
|
||
// headers: { "Content-Type": "application/json" },
|
||
// body: JSON.stringify({
|
||
// id,
|
||
// user_name: userName,
|
||
// }),
|
||
// });
|
||
|
||
// if (!res.ok) throw new Error(`HTTP 请求失败:${res.status}`);
|
||
|
||
// const json = await res.json();
|
||
|
||
// if (json?.header?.code === 0) {
|
||
// setStatusText("部署已启动,正在监听进度...");
|
||
|
||
// // ✅ 检查 env 变量是否读取成功
|
||
// console.log("🧪 WS BASE =", process.env.NEXT_PUBLIC_CLIENT_BASE_WS);
|
||
|
||
// // 发起 WebSocket 连接监听部署进度
|
||
// const wsBase = process.env.NEXT_PUBLIC_CLIENT_BASE_WS;
|
||
// const socket = new WebSocket(`${wsBase}/status/${userName}/${id}`);
|
||
|
||
// socket.onopen = () => {
|
||
// console.log("WebSocket 已连接");
|
||
// };
|
||
|
||
// socket.onmessage = (event) => {
|
||
// console.log("收到进度信息:", event.data);
|
||
// // setStatusText(event.data); // 你也可以用 setProgress() 更新进度条
|
||
// const msg = event.data;
|
||
// setStatusText(msg);
|
||
|
||
// // ✅ 自动解析形如“进度: 65%,阶段: 正在部署”的格式
|
||
// const match = msg.match(/进度[::]?\s*(\d+)%/);
|
||
// if (match && match[1]) {
|
||
// const percent = match[1] + "%";
|
||
// setProgress(percent);
|
||
// }
|
||
// };
|
||
|
||
// socket.onerror = (error) => {
|
||
// console.error("WebSocket 出错:", error);
|
||
// setStatusText("WebSocket 出错");
|
||
// };
|
||
|
||
// socket.onclose = () => {
|
||
// console.log("🔌 WebSocket 连接已关闭");
|
||
// };
|
||
// } else {
|
||
// setStatusText(`${json.header.message || "操作失败(后端返回错误)"}`);
|
||
// }
|
||
// } catch (err) {
|
||
// console.error("请求出错:", err);
|
||
// setStatusText("操作失败,请检查网络或服务状态");
|
||
// } finally {
|
||
// setLoading(false);
|
||
// }
|
||
// };
|
||
|
||
// const handleDelete = () => {
|
||
// if (loading) return;
|
||
|
||
// const confirmed = window.confirm("确定要删除模型吗?");
|
||
// if (confirmed) {
|
||
// setLoading(true);
|
||
// // 模拟删除流程(实际调用 API 或其他逻辑)
|
||
// // await deleteComponent(data.id);
|
||
// console.log("模型已删除");
|
||
|
||
// setLoading(false);
|
||
// }
|
||
// };
|
||
|
||
// useEffect(() => {
|
||
// const fetchDeployStatus = async () => {
|
||
// try {
|
||
// const result = await fetch("/api/v1/deploy/status", {
|
||
// method: "POST",
|
||
// headers: { "Content-Type": "application/json" },
|
||
// body: JSON.stringify({ id: data?.id }),
|
||
// }).then((res) => res.json());
|
||
|
||
// console.log("==================>result:", result);
|
||
|
||
// if (!result || result.header?.code !== 1006) {
|
||
// setStatusText(result?.header?.message || "操作失败(后端返回错误)");
|
||
// setShowDelete(false); // 非 1006 隐藏删除按钮
|
||
// return;
|
||
// }
|
||
|
||
// // 如果成功返回
|
||
// setStatusText(result.header.message || "部署成功");
|
||
// if (result.header?.code === 1006) {
|
||
// setShowDelete(false); // code === 1006 不可删除,隐藏按钮
|
||
// setStatusText("");
|
||
// setProgress("0%");
|
||
// } else {
|
||
// setShowDelete(true); // 其他状态可删除
|
||
// setStatusText(result.header.message || "部署中");
|
||
// setProgress(result.data?.progress || "0%");
|
||
// }
|
||
// // 如果还想设置进度的话,这里也可以 setProgress(result.data.progress || '0%')
|
||
// } catch (err) {
|
||
// console.error("请求部署状态失败:", err);
|
||
// setStatusText("请求失败");
|
||
// setShowDelete(false);
|
||
// }
|
||
// };
|
||
|
||
// fetchDeployStatus();
|
||
// }, [data?.id]); // id 变化时重新获取
|
||
|
||
// // 处理图标路径
|
||
// const isImagePath =
|
||
// typeof data?.icon === "string" &&
|
||
// (data.icon.startsWith("http") || data.icon.startsWith("/"));
|
||
|
||
// const resolvedIconSrc =
|
||
// isImagePath && !data.icon.startsWith("http")
|
||
// ? process.env.NEXT_PUBLIC_CLIENT_IMAGE_URL + data.icon
|
||
// : data.icon;
|
||
|
||
// return (
|
||
// <div className="sticky top-0 z-30 bg-white">
|
||
// <div className="mt-4 mb-1 px-6 lg:px-8 w-11/12 lg:w-2/3 xl:w-3/5 mx-auto">
|
||
// <div className="flex items-start space-x-6">
|
||
// {/* 左图标(可点) */}
|
||
// <button
|
||
// className="group flex items-center justify-center w-24 h-24 md:w-32 md:h-32 border transition"
|
||
// onClick={() => handleClick("icon")}
|
||
// disabled={loading || data?.status === "running"} // 👈 加上 status 判断
|
||
// >
|
||
// {isImagePath ? (
|
||
// <img
|
||
// src={resolvedIconSrc}
|
||
// alt="icon"
|
||
// className="
|
||
// w-full h-full object-contain
|
||
// transition-all duration-200
|
||
// group-hover:border-2 group-hover:border-blue-500
|
||
// group-active:border-2 group-active:border-green-500
|
||
// group-hover:scale-105 group-active:scale-95
|
||
// "
|
||
// />
|
||
// ) : (
|
||
// data?.icon || "Deploy"
|
||
// )}
|
||
// </button>
|
||
|
||
// {/* 信息 + 删除按钮:左右分布 */}
|
||
// <div className="flex justify-between flex-1 items-end">
|
||
// {/* 信息区域 */}
|
||
// <div className="text-sm leading-7 space-y-1.5">
|
||
// <div className="flex items-center gap-2">
|
||
// <BadgeInfo size={16} /> {data?.name || "未命名组件"}
|
||
// </div>
|
||
// <div className="flex items-center gap-2">
|
||
// <Tags size={16} /> {data?.category || "未知"}
|
||
// </div>
|
||
// <div className="flex items-center gap-2">
|
||
// <CalendarClock size={16} /> {data?.updated_at || "未提供"}
|
||
// </div>
|
||
// <div className="flex items-center gap-2">
|
||
// <Building2 size={16} /> {data?.company || "未知公司"}
|
||
// </div>
|
||
// </div>
|
||
|
||
// {/* 条件显示删除按钮 */}
|
||
// {showDelete && (
|
||
// <button
|
||
// onClick={handleDelete}
|
||
// className="hover:text-gray-700 transition self-end"
|
||
// disabled={loading}
|
||
// title="删除"
|
||
// >
|
||
// <Trash2 size={20} />
|
||
// </button>
|
||
// )}
|
||
|
||
// </div>
|
||
|
||
// </div>
|
||
|
||
// {/* 状态条 */}
|
||
// {(data?.progress !== "0%" || statusText) && (
|
||
// <div className="w-full mt-4 bg-gray-200 h-6">
|
||
// <div
|
||
// className="bg-blue-500 h-full text-white text-center text-sm flex items-center justify-center transition-all duration-300 px-2 overflow-hidden whitespace-nowrap text-ellipsis"
|
||
// style={{ width: progress || "0%" }}
|
||
// >
|
||
// {loading ? "操作中..." : statusText}
|
||
// </div>
|
||
// </div>
|
||
// )}
|
||
|
||
// </div>
|
||
// </div>
|
||
// );
|
||
// }
|
||
|
||
|
||
|
||
export function DetailPageHeader({ data }: { data: any }) {
|
||
const [loading, setLoading] = useState(false);
|
||
const [statusText, setStatusText] = useState(data?.statusText || "加载中...");
|
||
const [progress, setProgress] = useState(data?.progress || "0%");
|
||
const [showDelete, setShowDelete] = useState(false); // 默认隐藏避免闪烁
|
||
const [hasWSConnected, setHasWSConnected] = useState(false);
|
||
const [statusLoaded, setStatusLoaded] = useState(false);
|
||
const socketRef = useRef<WebSocket | null>(null);
|
||
|
||
// ✅ 初始化 WebSocket
|
||
const initWebSocket = (userName: string, id: number) => {
|
||
if (socketRef.current) socketRef.current.close();
|
||
|
||
const wsBase = process.env.NEXT_PUBLIC_CLIENT_BASE_WS;
|
||
const socket = new WebSocket(`${wsBase}/status/${userName}/${id}`);
|
||
socketRef.current = socket;
|
||
|
||
socket.onopen = () => {
|
||
console.log("WebSocket 已连接");
|
||
};
|
||
|
||
socket.onmessage = (event) => {
|
||
console.log("收到进度信息:", event.data);
|
||
const msg = event.data;
|
||
setStatusText(msg);
|
||
|
||
const match = msg.match(/进度[::]?\s*(\d+)%/);
|
||
if (match && match[1]) {
|
||
const percent = match[1] + "%";
|
||
setProgress(percent);
|
||
}
|
||
};
|
||
|
||
socket.onerror = (error) => {
|
||
console.error("WebSocket 出错:", error);
|
||
setStatusText("WebSocket 出错");
|
||
};
|
||
|
||
socket.onclose = () => {
|
||
console.log("🔌 WebSocket 连接已关闭");
|
||
setHasWSConnected(false);
|
||
socketRef.current = null;
|
||
};
|
||
|
||
setHasWSConnected(true);
|
||
};
|
||
|
||
const handleClick = async (source: "icon" | "info") => {
|
||
setLoading(true);
|
||
setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作...");
|
||
|
||
try {
|
||
const userData = JSON.parse(localStorage.getItem("UserData") || "null");
|
||
if (!userData?.user_name) {
|
||
setStatusText("未登录,正在跳转登录页面...");
|
||
window.location.href = "/auth/sign-in/";
|
||
return;
|
||
}
|
||
|
||
const userName = userData.user_name;
|
||
const id = data?.id;
|
||
if (!id) {
|
||
setStatusText("数据缺失:找不到组件 ID");
|
||
return;
|
||
}
|
||
|
||
const res = await fetch("/api/v1/deploy/deploy", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ id, user_name: userName }),
|
||
});
|
||
|
||
if (!res.ok) throw new Error(`HTTP 请求失败:${res.status}`);
|
||
|
||
const json = await res.json();
|
||
if (json?.header?.code === 0) {
|
||
setStatusText("部署已启动,正在监听进度...");
|
||
initWebSocket(userName, id);
|
||
} else {
|
||
setStatusText(json.header.message || "操作失败(后端返回错误)");
|
||
}
|
||
} catch (err) {
|
||
console.error("请求出错:", err);
|
||
setStatusText("操作失败,请检查网络或服务状态");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleDelete = () => {
|
||
if (loading) return;
|
||
const confirmed = window.confirm("确定要删除模型吗?");
|
||
if (confirmed) {
|
||
setLoading(true);
|
||
console.log("模型已删除");
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
// ✅ 拉取部署状态
|
||
useEffect(() => {
|
||
const fetchDeployStatus = async () => {
|
||
try {
|
||
const result = await fetch("/api/v1/deploy/status", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ id: data?.id }),
|
||
}).then((res) => res.json());
|
||
|
||
console.log("====> deploy status result:", result);
|
||
|
||
const status = result?.data?.data?.status;
|
||
console.log("🟡 [fetch] 拉取状态 status =", status);
|
||
|
||
const userData = JSON.parse(localStorage.getItem("UserData") || "null");
|
||
const userName = userData?.user_name;
|
||
const id = data?.id;
|
||
|
||
setStatusLoaded(true);
|
||
|
||
if (status === "deploying" && userName && id && !hasWSConnected) {
|
||
console.log("🔄 自动连接 WebSocket: 正在部署中...");
|
||
setStatusText("检测到正在部署,连接中...");
|
||
initWebSocket(userName, id);
|
||
}
|
||
|
||
if (!result || result.header?.code !== 1006) {
|
||
console.warn("🚫 状态接口返回失败或 code 错误", result.header);
|
||
setStatusText(result?.header?.message || "操作失败(后端返回错误)");
|
||
setShowDelete(false);
|
||
return;
|
||
}
|
||
|
||
setStatusText(result.header.message || "部署成功");
|
||
|
||
// ✅ 只在 running / stopped 显示删除按钮
|
||
if (status === "running" || status === "stopped") {
|
||
console.log("✅ 状态允许删除,显示删除按钮");
|
||
setShowDelete(true);
|
||
} else {
|
||
console.log("❌ 状态不允许删除,隐藏按钮");
|
||
setShowDelete(false);
|
||
}
|
||
} catch (err) {
|
||
console.error("请求部署状态失败:", err);
|
||
setStatusText("请求失败");
|
||
setShowDelete(false);
|
||
}
|
||
};
|
||
|
||
fetchDeployStatus();
|
||
}, [data?.id]);
|
||
|
||
// ✅ 卸载时关闭 WebSocket
|
||
useEffect(() => {
|
||
return () => {
|
||
if (socketRef.current) {
|
||
socketRef.current.close();
|
||
}
|
||
};
|
||
}, []);
|
||
|
||
const isImagePath =
|
||
typeof data?.icon === "string" &&
|
||
(data.icon.startsWith("http") || data.icon.startsWith("/"));
|
||
|
||
const resolvedIconSrc =
|
||
isImagePath && !data.icon.startsWith("http")
|
||
? process.env.NEXT_PUBLIC_CLIENT_IMAGE_URL + data.icon
|
||
: data.icon;
|
||
|
||
return (
|
||
<div className="sticky top-0 z-30 bg-white">
|
||
<div className="mt-4 mb-1 px-6 lg:px-8 w-11/12 lg:w-2/3 xl:w-3/5 mx-auto">
|
||
<div className="flex items-start space-x-6">
|
||
<button
|
||
className="group flex items-center justify-center w-24 h-24 md:w-32 md:h-32 border transition"
|
||
onClick={() => handleClick("icon")}
|
||
disabled={loading || data?.status === "running"}
|
||
>
|
||
{isImagePath ? (
|
||
<img
|
||
src={resolvedIconSrc}
|
||
alt="icon"
|
||
className="w-full h-full object-contain transition-all duration-200 group-hover:border-2 group-hover:border-blue-500 group-active:border-2 group-active:border-green-500 group-hover:scale-105 group-active:scale-95"
|
||
/>
|
||
) : (
|
||
data?.icon || "Deploy"
|
||
)}
|
||
</button>
|
||
|
||
<div className="flex justify-between flex-1 items-end">
|
||
<div className="text-sm leading-7 space-y-1.5">
|
||
<div className="flex items-center gap-2">
|
||
<BadgeInfo size={16} /> {data?.name || "未命名组件"}
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<Tags size={16} /> {data?.category || "未知"}
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<CalendarClock size={16} /> {data?.updated_at || "未提供"}
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<Building2 size={16} /> {data?.company || "未知公司"}
|
||
</div>
|
||
</div>
|
||
|
||
{statusLoaded && showDelete && (
|
||
<button
|
||
onClick={handleDelete}
|
||
className="hover:text-gray-700 transition self-end"
|
||
disabled={loading}
|
||
title="删除"
|
||
>
|
||
<Trash2 size={20} />
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{(progress !== "0%" || statusText) && (
|
||
<div className="w-full mt-4 bg-gray-200 h-6">
|
||
<div
|
||
className="bg-blue-500 h-full text-white text-center text-sm flex items-center justify-center transition-all duration-300 px-2 overflow-hidden whitespace-nowrap text-ellipsis"
|
||
style={{ width: progress || "0%" }}
|
||
>
|
||
{loading ? "操作中..." : statusText}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
|
||
|
||
|
||
// export function Header() {
|
||
|
||
// const router = useRouter();
|
||
// const { t } = useTranslation();
|
||
|
||
// const [userData, setUserData] = useLocalStorage(
|
||
// 'UserData',
|
||
// {
|
||
// auth_token: "",
|
||
// id: 1,
|
||
// login_ip: "",
|
||
// login_time: 0,
|
||
// role: "",
|
||
// user_name: "",
|
||
// version: ""
|
||
// } as UserData
|
||
// )
|
||
|
||
// const soonFunc = () => {
|
||
// message.info(t("soon"))
|
||
// }
|
||
|
||
// return (
|
||
// <header className="sticky top-0 z-50 flex w-full shrink-0 items-center justify-between bg-background px-4 py-[1.5rem]">
|
||
|
||
// {/* <MobileLinks className="lg:hidden" />
|
||
// <DesktopLinks className="hidden lg:flex" /> */}
|
||
// <div className="lg:hidden" ></div>
|
||
|
||
// <div className="hidden lg:flex" >
|
||
// {/* <Button variant="ghost" className='text-base font-bold hover:bg-[#e5e7eb]' onClick={soonFunc}>
|
||
// {t("header.course")}
|
||
// </Button>
|
||
// <Button variant="ghost" className='text-base font-bold hover:bg-[#e5e7eb]' onClick={soonFunc}>
|
||
// {t("header.models")}
|
||
// </Button>
|
||
// <Button variant="ghost" className='text-base font-bold hover:bg-[#e5e7eb]' onClick={soonFunc}>
|
||
// {t("header.resources")}
|
||
// </Button> */}
|
||
// </div>
|
||
|
||
// {/* <Image src={logoImage} height={40} alt="show" className="cursor-pointer" onClick={() => {
|
||
// router.push("/")
|
||
// // toast.success('coming soon')
|
||
// }} /> */}
|
||
|
||
// <LogoAI
|
||
// className='flex text-center m-auto'
|
||
// />
|
||
|
||
// <div className="flex items-center justify-between ">
|
||
// <div className="flex items-center ">
|
||
// {/* <IconSeparator className="size-6 text-muted-foreground/50" /> */}
|
||
|
||
// {(!!userData && !!userData.user_name) ? (
|
||
// <div className='flex text-[#1A1A1A]'>
|
||
// {/* <Button variant="ghost" className='text-base bg-[#f4f4f5] hover:bg-[#e5e7eb]'
|
||
// onClick={() => {
|
||
// router.push("/subscribe")
|
||
// }}
|
||
// > {t("subscribe.subscribe")}</Button> */}
|
||
// <UserMenu user={userData} />
|
||
// </div>
|
||
|
||
// ) : (
|
||
// <div className='flex gap-4 grid-cols-2 text-[#1A1A1A]'>
|
||
// <Button variant="ghost" className='text-base bg-[#f4f4f5] hover:bg-[#e5e7eb]'>
|
||
// <Link href="/#subscribe-target"
|
||
// >
|
||
// {t('subscribe.subscribe')}
|
||
// </Link>
|
||
// </Button>
|
||
|
||
|
||
// <LoginButton
|
||
// variant="ghost"
|
||
// // variant="link"
|
||
// showGithubIcon={true}
|
||
// text={t('login')}
|
||
// className="-ml-2 text-base bg-[#f4f4f5] hover:bg-[#e5e7eb]"
|
||
// />
|
||
// </div>
|
||
|
||
|
||
// )}
|
||
// </div>
|
||
// </div>
|
||
|
||
// </header>
|
||
// )
|
||
// }
|
||
|
||
|
||
export function NavBack() {
|
||
|
||
|
||
const router = useRouter();
|
||
const { t } = useTranslation();
|
||
|
||
// const query = router.query; // 假设页面 URL 是 /mypage?id=123
|
||
|
||
console.log("query", router)
|
||
return (
|
||
<nav className="fixed top-[1rem] container flex items-center justify-between " >
|
||
<Link href="/">
|
||
{t("home")}
|
||
</Link>
|
||
</nav>
|
||
)
|
||
}
|
||
|
||
export function TimeP({
|
||
date,
|
||
}: {
|
||
date: string
|
||
}) {
|
||
|
||
|
||
const router = useRouter();
|
||
const { t } = useTranslation();
|
||
|
||
|
||
return (
|
||
<p className=" text-left text-[1rem] text-[#808080]">{date} {t("JellyAI")}</p>
|
||
)
|
||
}
|
||
|
||
|
||
|
||
|