This commit is contained in:
hailin 2025-04-02 22:16:48 +08:00
parent 9470ba7985
commit f1bace981c
1 changed files with 349 additions and 142 deletions

View File

@ -120,19 +120,69 @@ export function Header() {
// data.icon = "/images/icon.png" 👉 显示图片; // data.icon = "/images/icon.png" 👉 显示图片;
// data.icon = undefined/null 👉 显示默认 "Deploy" 字样。 // data.icon = undefined/null 👉 显示默认 "Deploy" 字样。
export function DetailPageHeader({ data }: { data: any }) { // export function DetailPageHeader({ data }: { data: any }) {
const [loading, setLoading] = useState(false); // const [loading, setLoading] = useState(false);
const [statusText, setStatusText] = useState(data?.statusText || "加载中..."); // const [statusText, setStatusText] = useState(data?.statusText || "加载中...");
const [progress, setProgress] = useState(data?.progress || "0%"); // const [progress, setProgress] = useState(data?.progress || "0%");
const [showDelete, setShowDelete] = useState(true); // 默认可以删除 // 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") => { // const handleClick = async (source: "icon" | "info") => {
// setLoading(true); // setLoading(true);
// setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作..."); // setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作...");
// try { // try {
// // 从 localStorage 获取用户信息
// const userData = JSON.parse(localStorage.getItem("UserData") || "null"); // const userData = JSON.parse(localStorage.getItem("UserData") || "null");
// if (!userData || !userData.user_name) { // if (!userData || !userData.user_name) {
@ -142,9 +192,8 @@ export function DetailPageHeader({ data }: { data: any }) {
// } // }
// const userName = userData.user_name; // const userName = userData.user_name;
// // 从组件 props 里的 data 中取 id
// const id = data?.id; // const id = data?.id;
// if (!id) { // if (!id) {
// setStatusText("数据缺失:找不到组件 ID"); // setStatusText("数据缺失:找不到组件 ID");
// return; // return;
@ -160,14 +209,46 @@ export function DetailPageHeader({ data }: { data: any }) {
// }), // }),
// }); // });
// if (!res.ok) { // if (!res.ok) throw new Error(`HTTP 请求失败:${res.status}`);
// throw new Error(`HTTP 请求失败:${res.status}`);
// }
// const json = await res.json(); // const json = await res.json();
// if (json?.header?.code === 0) { // if (json?.header?.code === 0) {
// setStatusText(`${json.header.message || "操作成功"}`); // 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 { // } else {
// setStatusText(`${json.header.message || "操作失败(后端返回错误)"}`); // setStatusText(`${json.header.message || "操作失败(后端返回错误)"}`);
// } // }
@ -179,49 +260,158 @@ export function DetailPageHeader({ data }: { data: any }) {
// } // }
// }; // };
// const handleDelete = () => {
// if (loading) return;
const handleClick = async (source: "icon" | "info") => { // const confirmed = window.confirm("确定要删除模型吗?");
setLoading(true); // if (confirmed) {
setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作..."); // setLoading(true);
// // 模拟删除流程(实际调用 API 或其他逻辑)
// // await deleteComponent(data.id);
// console.log("模型已删除");
try { // setLoading(false);
const userData = JSON.parse(localStorage.getItem("UserData") || "null"); // }
// };
if (!userData || !userData.user_name) { // useEffect(() => {
setStatusText("未登录,正在跳转登录页面..."); // const fetchDeployStatus = async () => {
window.location.href = "/auth/sign-in/"; // try {
return; // 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());
const userName = userData.user_name; // console.log("==================>result:", result);
const id = data?.id;
if (!id) { // if (!result || result.header?.code !== 1006) {
setStatusText("数据缺失:找不到组件 ID"); // setStatusText(result?.header?.message || "操作失败(后端返回错误)");
return; // setShowDelete(false); // 非 1006 隐藏删除按钮
} // return;
// }
// 发起部署请求 // // 如果成功返回
const res = await fetch("/api/v1/deploy/deploy", { // setStatusText(result.header.message || "部署成功");
method: "POST", // if (result.header?.code === 1006) {
headers: { "Content-Type": "application/json" }, // setShowDelete(false); // code === 1006 不可删除,隐藏按钮
body: JSON.stringify({ // setStatusText("");
id, // setProgress("0%");
user_name: userName, // } 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);
// }
// };
if (!res.ok) throw new Error(`HTTP 请求失败:${res.status}`); // fetchDeployStatus();
// }, [data?.id]); // id 变化时重新获取
const json = await res.json(); // // 处理图标路径
// const isImagePath =
// typeof data?.icon === "string" &&
// (data.icon.startsWith("http") || data.icon.startsWith("/"));
if (json?.header?.code === 0) { // const resolvedIconSrc =
setStatusText("部署已启动,正在监听进度..."); // isImagePath && !data.icon.startsWith("http")
// ? process.env.NEXT_PUBLIC_CLIENT_IMAGE_URL + data.icon
// : data.icon;
// ✅ 检查 env 变量是否读取成功 // return (
console.log("🧪 WS BASE =", process.env.NEXT_PUBLIC_CLIENT_BASE_WS); // <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>
// 发起 WebSocket 连接监听部署进度 // {/* 信息 + 删除按钮:左右分布 */}
// <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(true);
// ✅ 提取 WebSocket 初始化逻辑
const initWebSocket = (userName: string, id: number) => {
const wsBase = process.env.NEXT_PUBLIC_CLIENT_BASE_WS; const wsBase = process.env.NEXT_PUBLIC_CLIENT_BASE_WS;
const socket = new WebSocket(`${wsBase}/status/${userName}/${id}`); const socket = new WebSocket(`${wsBase}/status/${userName}/${id}`);
@ -231,11 +421,9 @@ export function DetailPageHeader({ data }: { data: any }) {
socket.onmessage = (event) => { socket.onmessage = (event) => {
console.log("收到进度信息:", event.data); console.log("收到进度信息:", event.data);
// setStatusText(event.data); // 你也可以用 setProgress() 更新进度条
const msg = event.data; const msg = event.data;
setStatusText(msg); setStatusText(msg);
// ✅ 自动解析形如“进度: 65%,阶段: 正在部署”的格式
const match = msg.match(/进度[:]?\s*(\d+)%/); const match = msg.match(/进度[:]?\s*(\d+)%/);
if (match && match[1]) { if (match && match[1]) {
const percent = match[1] + "%"; const percent = match[1] + "%";
@ -251,8 +439,41 @@ export function DetailPageHeader({ data }: { data: any }) {
socket.onclose = () => { socket.onclose = () => {
console.log("🔌 WebSocket 连接已关闭"); console.log("🔌 WebSocket 连接已关闭");
}; };
};
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("部署已启动,正在监听进度...");
initWebSocket(userName, id);
} else { } else {
setStatusText(`${json.header.message || "操作失败(后端返回错误)"}`); setStatusText(json.header.message || "操作失败(后端返回错误)");
} }
} catch (err) { } catch (err) {
console.error("请求出错:", err); console.error("请求出错:", err);
@ -262,24 +483,16 @@ export function DetailPageHeader({ data }: { data: any }) {
} }
}; };
const handleDelete = () => { const handleDelete = () => {
if (loading) return; if (loading) return;
const confirmed = window.confirm("确定要删除模型吗?"); const confirmed = window.confirm("确定要删除模型吗?");
if (confirmed) { if (confirmed) {
setLoading(true); setLoading(true);
// 模拟删除流程(实际调用 API 或其他逻辑)
// await deleteComponent(data.id);
console.log("模型已删除"); console.log("模型已删除");
setLoading(false); setLoading(false);
} }
}; };
useEffect(() => { useEffect(() => {
const fetchDeployStatus = async () => { const fetchDeployStatus = async () => {
try { try {
@ -291,24 +504,32 @@ export function DetailPageHeader({ data }: { data: any }) {
console.log("==================>result:", result); console.log("==================>result:", result);
const status = result?.data?.status;
const userData = JSON.parse(localStorage.getItem("UserData") || "null");
const userName = userData?.user_name;
const id = data?.id;
if (status === "deploying" && userName && id) {
setStatusText("检测到正在部署,连接中...");
initWebSocket(userName, id);
}
if (!result || result.header?.code !== 1006) { if (!result || result.header?.code !== 1006) {
setStatusText(result?.header?.message || "操作失败(后端返回错误)"); setStatusText(result?.header?.message || "操作失败(后端返回错误)");
setShowDelete(false); // 非 1006 隐藏删除按钮 setShowDelete(false);
return; return;
} }
// 如果成功返回
setStatusText(result.header.message || "部署成功"); setStatusText(result.header.message || "部署成功");
if (result.header?.code === 1006) { if (result.header?.code === 1006) {
setShowDelete(false); // code === 1006 不可删除,隐藏按钮 setShowDelete(false);
setStatusText(""); setStatusText("");
setProgress("0%"); setProgress("0%");
} else { } else {
setShowDelete(true); // 其他状态可删除 setShowDelete(true);
setStatusText(result.header.message || "部署中"); setStatusText(result.header.message || "部署中");
setProgress(result.data?.progress || "0%"); setProgress(result.data?.progress || "0%");
} }
// 如果还想设置进度的话,这里也可以 setProgress(result.data.progress || '0%')
} catch (err) { } catch (err) {
console.error("请求部署状态失败:", err); console.error("请求部署状态失败:", err);
setStatusText("请求失败"); setStatusText("请求失败");
@ -317,11 +538,8 @@ export function DetailPageHeader({ data }: { data: any }) {
}; };
fetchDeployStatus(); fetchDeployStatus();
}, [data?.id]); // id 变化时重新获取 }, [data?.id]);
// 处理图标路径
const isImagePath = const isImagePath =
typeof data?.icon === "string" && typeof data?.icon === "string" &&
(data.icon.startsWith("http") || data.icon.startsWith("/")); (data.icon.startsWith("http") || data.icon.startsWith("/"));
@ -335,32 +553,23 @@ export function DetailPageHeader({ data }: { data: any }) {
<div className="sticky top-0 z-30 bg-white"> <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="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"> <div className="flex items-start space-x-6">
{/* 左图标(可点) */}
<button <button
className="group flex items-center justify-center w-24 h-24 md:w-32 md:h-32 border transition" className="group flex items-center justify-center w-24 h-24 md:w-32 md:h-32 border transition"
onClick={() => handleClick("icon")} onClick={() => handleClick("icon")}
disabled={loading} disabled={loading || data?.status === "running"}
> >
{isImagePath ? ( {isImagePath ? (
<img <img
src={resolvedIconSrc} src={resolvedIconSrc}
alt="icon" alt="icon"
className=" 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"
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" data?.icon || "Deploy"
)} )}
</button> </button>
{/* 信息 + 删除按钮:左右分布 */}
<div className="flex justify-between flex-1 items-end"> <div className="flex justify-between flex-1 items-end">
{/* 信息区域 */}
<div className="text-sm leading-7 space-y-1.5"> <div className="text-sm leading-7 space-y-1.5">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<BadgeInfo size={16} /> {data?.name || "未命名组件"} <BadgeInfo size={16} /> {data?.name || "未命名组件"}
@ -376,7 +585,6 @@ export function DetailPageHeader({ data }: { data: any }) {
</div> </div>
</div> </div>
{/* 条件显示删除按钮 */}
{showDelete && ( {showDelete && (
<button <button
onClick={handleDelete} onClick={handleDelete}
@ -387,12 +595,9 @@ export function DetailPageHeader({ data }: { data: any }) {
<Trash2 size={20} /> <Trash2 size={20} />
</button> </button>
)} )}
</div>
</div> </div>
</div>
{/* 状态条 */}
{(data?.progress !== "0%" || statusText) && ( {(data?.progress !== "0%" || statusText) && (
<div className="w-full mt-4 bg-gray-200 h-6"> <div className="w-full mt-4 bg-gray-200 h-6">
<div <div
@ -403,13 +608,15 @@ export function DetailPageHeader({ data }: { data: any }) {
</div> </div>
</div> </div>
)} )}
</div> </div>
</div> </div>
); );
} }
// export function Header() { // export function Header() {
// const router = useRouter(); // const router = useRouter();