chore: 增加代码规范和提交校验

Made-with: Cursor
This commit is contained in:
2026-04-03 16:23:35 +08:00
parent e48462cf4a
commit 7eed1ad5c6
10 changed files with 2382 additions and 36 deletions

View File

@@ -9,6 +9,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
);

View File

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from "react";
import { Button, Drawer, Input, Layout, List, Space, Typography } from "antd";
import { Button, Drawer, Input, Layout, Space, Typography } from "antd";
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
@@ -9,6 +9,7 @@ import {
} from "@ant-design/icons";
const { Header, Sider, Content } = Layout;
const MOBILE_WIDTH = 750;
const mockMessages = [
{ role: "assistant", content: "你好,我是 ChatOne 助手,有什么可以帮你?" },
@@ -17,9 +18,14 @@ const mockMessages = [
];
export default function HomePage() {
// 桌面端侧边栏折叠状态(移动端不使用该状态)
const [collapsed, setCollapsed] = useState(false);
// 移动端抽屉侧边栏显示状态
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const [isMobile, setIsMobile] = useState(() => window.innerWidth < 750);
// 当前视口宽度(用于调试响应式是否生效)
const [viewportWidth, setViewportWidth] = useState(0);
// 是否为移动端(小于 750px
const [isMobile, setIsMobile] = useState(false);
const [inputValue, setInputValue] = useState("");
const menuItems = useMemo(
@@ -32,33 +38,46 @@ export default function HomePage() {
);
useEffect(() => {
const onResize = () => {
const mobile = window.innerWidth < 750;
const updateIsMobile = () => {
// 兼容不同浏览器/缩放场景下的视口宽度读取
const viewportWidth = Math.min(
window.innerWidth,
document.documentElement.clientWidth || window.innerWidth,
);
setViewportWidth(viewportWidth);
const mobile = viewportWidth < MOBILE_WIDTH;
setIsMobile(mobile);
if (!mobile) {
// 切回桌面端时关闭移动抽屉,避免状态残留
setMobileSidebarOpen(false);
}
};
window.addEventListener("resize", onResize);
updateIsMobile();
window.addEventListener("resize", updateIsMobile);
window.addEventListener("orientationchange", updateIsMobile);
return () => {
window.removeEventListener("resize", onResize);
window.removeEventListener("resize", updateIsMobile);
window.removeEventListener("orientationchange", updateIsMobile);
};
}, []);
const sidebarContent = (
<>
{/* 侧边栏头部:桌面折叠时显示缩写,其他情况显示完整标题 */}
<div className="h-14 border-b border-gray-100 px-4 flex items-center">
<Typography.Title level={5} style={{ margin: 0 }}>
{collapsed && !isMobile ? "CO" : "ChatOne"}
</Typography.Title>
</div>
<div className="px-2 py-3">
<Space direction="vertical" className="w-full">
<Space orientation="vertical" className="w-full">
{menuItems.map((item) => (
<Button key={item.label} type="text" className="w-full text-left!">
<Space>
{item.icon}
{/* 桌面折叠时隐藏文字;移动端抽屉内始终显示文字 */}
{(!collapsed || isMobile) && <span>{item.label}</span>}
</Space>
</Button>
@@ -70,6 +89,7 @@ export default function HomePage() {
return (
<Layout style={{ minHeight: "100vh" }}>
{/* 桌面端固定侧边栏;移动端改为 Drawer 抽屉 */}
{!isMobile && (
<Sider trigger={null} collapsible collapsed={collapsed} theme="light">
{sidebarContent}
@@ -91,16 +111,24 @@ export default function HomePage() {
}
onClick={() => {
if (isMobile) {
// 移动端点击按钮:打开左侧抽屉
setMobileSidebarOpen(true);
} else {
// 桌面端点击按钮:切换侧边栏折叠
setCollapsed((v) => !v);
}
}}
/>
<Typography.Text type="secondary">ChatOne Web</Typography.Text>
<Space size={12}>
<Typography.Text type="secondary">ChatOne Web</Typography.Text>
<Typography.Text type="secondary">
{`W:${viewportWidth}px / ${isMobile ? "mobile" : "desktop"}`}
</Typography.Text>
</Space>
</Header>
<Content className="bg-gray-50 p-6">
{/* 聊天窗口:消息列表 + 底部输入区 */}
<div className="h-[calc(100vh-112px)] rounded-lg bg-white border border-gray-100 flex flex-col">
<div className="border-b border-gray-100 px-5 py-3">
<Typography.Title level={5} style={{ margin: 0 }}>
@@ -108,24 +136,23 @@ export default function HomePage() {
</Typography.Title>
</div>
<div className="flex-1 overflow-auto px-5 py-4">
<List
split={false}
dataSource={mockMessages}
renderItem={(item) => (
<List.Item>
<div
className={`max-w-[80%] rounded-lg px-3 py-2 ${
item.role === "user"
? "ml-auto bg-blue-50 border border-blue-100"
: "bg-gray-50 border border-gray-100"
}`}
>
<Typography.Text>{item.content}</Typography.Text>
</div>
</List.Item>
)}
/>
<div className="flex-1 overflow-auto px-5 py-4 flex flex-col gap-2">
{mockMessages.map((item, index) => (
<div
key={index}
className={`flex ${item.role === "user" ? "justify-end" : "justify-start"}`}
>
<div
className={`max-w-[80%] rounded-lg px-3 py-2 ${
item.role === "user"
? "bg-blue-50 border border-blue-100"
: "bg-gray-50 border border-gray-100"
}`}
>
<Typography.Text>{item.content}</Typography.Text>
</div>
</div>
))}
</div>
<div className="border-t border-gray-100 p-4">
@@ -148,7 +175,7 @@ export default function HomePage() {
closable
onClose={() => setMobileSidebarOpen(false)}
open={isMobile && mobileSidebarOpen}
width={260}
size={260}
>
{sidebarContent}
</Drawer>