From 2a369638b33cb31dfbd6668d2ba85bd09b810ba1 Mon Sep 17 00:00:00 2001 From: cs10086 <1291454411@qq.com> Date: Thu, 9 Apr 2026 16:30:42 +0800 Subject: [PATCH] 1 --- .claude/settings.local.json | 7 + .claudeignore | 19 + package.json | 2 + pnpm-lock.yaml | 94 ++++- src/app/store.ts | 18 + src/components/Markdown/Markdown.css | 32 ++ src/components/Markdown/Markdown.tsx | 84 ++--- src/components/Navbar/Navbar.css | 24 +- src/components/Navbar/Navbar.tsx | 287 +++++++------- src/features/login/authSlice.ts | 263 +++++++++++++ src/features/login/index.ts | 3 + src/global.css | 536 +++++++++++++++++++++++++++ src/main.tsx | 19 +- src/pages/Document/Document.tsx | 95 +---- src/pages/History/History.tsx | 10 +- src/pages/Home/Home.tsx | 47 +-- src/pages/Settings/Settings.tsx | 43 +-- src/stores/Store.ts | 0 src/utils/axios.ts | 125 +++++++ src/utils/request.ts | 101 +++++ tsconfig.json | 11 +- 21 files changed, 1425 insertions(+), 395 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 .claudeignore create mode 100644 src/app/store.ts create mode 100644 src/features/login/authSlice.ts create mode 100644 src/features/login/index.ts delete mode 100644 src/stores/Store.ts create mode 100644 src/utils/axios.ts create mode 100644 src/utils/request.ts diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..3f582e9 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(npx tsc:*)" + ] + } +} diff --git a/.claudeignore b/.claudeignore new file mode 100644 index 0000000..7b104f5 --- /dev/null +++ b/.claudeignore @@ -0,0 +1,19 @@ +# 依赖和构建产物 +node_modules/ +dist/ +build/ +.next/ +__pycache__/ + +# 锁文件和日志 +*.lock +*.log + +# 版本控制和IDE +.git/ +.idea/ +.vscode/ + +# 测试覆盖率 +coverage/ +.cache/ \ No newline at end of file diff --git a/package.json b/package.json index 35ce2cb..8a71b72 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@ant-design/icons": "^6.1.1", "@chakra-ui/react": "^3.34.0", "@emotion/react": "^11.14.0", + "@reduxjs/toolkit": "^2.11.2", "antd": "^6.3.5", "axios": "^1.14.0", "dayjs": "^1.11.20", @@ -28,6 +29,7 @@ "react-json-tree": "^0.20.0", "react-lorem-ipsum": "^1.4.10", "react-markdown": "^10.1.0", + "react-redux": "^9.2.0", "react-router-dom": "^7.14.0", "react-syntax-highlighter": "^16.1.1", "rehype-highlight": "^7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c07ca5b..59794e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@emotion/react': specifier: ^11.14.0 version: 11.14.0(@types/react@19.2.14)(react@19.2.4) + '@reduxjs/toolkit': + specifier: ^2.11.2 + version: 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4) antd: specifier: ^6.3.5 version: 6.3.5(date-fns@2.30.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -62,6 +65,9 @@ importers: react-markdown: specifier: ^10.1.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.4) + react-redux: + specifier: ^9.2.0 + version: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) react-router-dom: specifier: ^7.14.0 version: 7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -82,7 +88,7 @@ importers: version: 6.0.0 zustand: specifier: ^5.0.12 - version: 5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + version: 5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: '@eslint/js': specifier: ^9.39.4 @@ -681,6 +687,17 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@rolldown/binding-android-arm64@1.0.0-rc.12': resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -776,6 +793,12 @@ packages: '@rolldown/pluginutils@1.0.0-rc.7': resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@swc/helpers@0.5.20': resolution: {integrity: sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==} @@ -832,6 +855,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@typescript-eslint/eslint-plugin@8.57.2': resolution: {integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1609,6 +1635,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immer@11.1.4: + resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2105,6 +2134,18 @@ packages: '@types/react': '>=18' react: '>=18' + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + react-router-dom@7.14.0: resolution: {integrity: sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==} engines: {node: '>=20.0.0'} @@ -2132,6 +2173,14 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + refractor@5.0.0: resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} @@ -2156,6 +2205,9 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3207,6 +3259,18 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.4 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.2.4 + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + '@rolldown/binding-android-arm64@1.0.0-rc.12': optional: true @@ -3261,6 +3325,10 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.7': {} + '@standard-schema/spec@1.1.0': {} + + '@standard-schema/utils@0.3.0': {} + '@swc/helpers@0.5.20': dependencies: tslib: 2.8.1 @@ -3316,6 +3384,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/use-sync-external-store@0.0.6': {} + '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -4519,6 +4589,8 @@ snapshots: ignore@7.0.5: {} + immer@11.1.4: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -5203,6 +5275,15 @@ snapshots: transitivePeerDependencies: - supports-color + react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + redux: 5.0.1 + react-router-dom@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: react: 19.2.4 @@ -5229,6 +5310,12 @@ snapshots: react@19.2.4: {} + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + refractor@5.0.0: dependencies: '@types/hast': 3.0.4 @@ -5297,6 +5384,8 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve@1.22.11: @@ -5530,9 +5619,10 @@ snapshots: zod@4.3.6: {} - zustand@5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + zustand@5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: '@types/react': 19.2.14 + immer: 11.1.4 react: 19.2.4 use-sync-external-store: 1.6.0(react@19.2.4) diff --git a/src/app/store.ts b/src/app/store.ts new file mode 100644 index 0000000..fd4f558 --- /dev/null +++ b/src/app/store.ts @@ -0,0 +1,18 @@ +// src/app/store.ts +import { configureStore } from '@reduxjs/toolkit'; +import { authReducer } from '@/features/login'; // 使用路径别名 + +import { useSelector, useDispatch } from "react-redux"; + +const store = configureStore({ + reducer: { + auth: authReducer, + }, +}); + +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; + +export default store; + +export const useAppDispatch = () => useDispatch(); \ No newline at end of file diff --git a/src/components/Markdown/Markdown.css b/src/components/Markdown/Markdown.css index e69de29..978eacf 100644 --- a/src/components/Markdown/Markdown.css +++ b/src/components/Markdown/Markdown.css @@ -0,0 +1,32 @@ +/* Markdown renderer specific styles */ +.markdown-renderer { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} + +.markdown-renderer h1, +.markdown-renderer h2, +.markdown-renderer h3, +.markdown-renderer h4, +.markdown-renderer h5, +.markdown-renderer h6 { + margin-top: 0; + margin-bottom: 0.5em; + font-weight: 600; + line-height: 1.25; +} + +.markdown-renderer p { + margin-top: 0; + margin-bottom: 1em; +} + +.markdown-renderer ul, +.markdown-renderer ol { + padding-left: 2em; + margin-top: 0; + margin-bottom: 1em; +} + +.markdown-renderer li { + margin-bottom: 0.25em; +} \ No newline at end of file diff --git a/src/components/Markdown/Markdown.tsx b/src/components/Markdown/Markdown.tsx index ce021bb..08158de 100644 --- a/src/components/Markdown/Markdown.tsx +++ b/src/components/Markdown/Markdown.tsx @@ -15,6 +15,15 @@ interface MarkdownRendererProps { codeTheme?: "vscDarkPlus" | "vs" | "atomDark"; } +// Define proper TypeScript types for react-markdown components +interface CodeProps { + node?: any; + inline?: boolean; + className?: string; + children?: React.ReactNode; + [key: string]: any; +} + function MarkdownRenderer({ content, className = "", @@ -36,8 +45,8 @@ function MarkdownRenderer({ const components: Components = { // 修复代码组件 - code({ node, inline, className, children, ...props }) { - const match = /language-(\w+)/.exec(className || ""); + code({ node, inline, className = "", children, ...props }: CodeProps) { + const match = /language-(\w+)/.exec(className); // 关键:正确转换 children 为字符串 const codeString = React.Children.toArray(children) @@ -56,34 +65,18 @@ function MarkdownRenderer({ if (!inline && match) { return ( -
+
{showCopyButton && (
{match[1]} @@ -113,15 +106,7 @@ function MarkdownRenderer({ return ( {trimmedCode} @@ -130,55 +115,36 @@ function MarkdownRenderer({ }, // 其他组件保持不变... - h1: ({ children }) => ( + h1: ({ children }: { children?: React.ReactNode }) => (

{children}

), - h2: ({ children }) => ( + h2: ({ children }: { children?: React.ReactNode }) => (

{children}

), - a: ({ href, children }) => ( + a: ({ href, children }: { href?: string; children?: React.ReactNode }) => ( {children} ), - blockquote: ({ children }) => ( + blockquote: ({ children }: { children?: React.ReactNode }) => (
{children}
@@ -186,7 +152,7 @@ function MarkdownRenderer({ }; return ( -
+
{ - const [openLogin, setLoginOpen] = useState(false); - const [current, setCurrent] = useState("home"); - const [isLogin, setIsLogin] = useState(false); - const [login_dialog_tab_value, setLogin_dialog_tab_value] = useState("1"); + const auth = useSelector(selectAuth); + // console.log("auth", auth); + const isLogin = useSelector(selectIsLogin); + const user = useSelector(selectUser); + const accessToken = useSelector(selectAccessToken); + const refreshToken = useSelector(selectRefreshToken); + const tokenType = useSelector(selectTokenType); + const error = useSelector(selectError); + const expiresIn = useSelector(selectExpiresIn); + const loading = useSelector(selectLoading); - const [to_email, setTo_email] = useState(""); - const [code, setCode] = useState(""); + const dispatch = useAppDispatch(); - const [login_user, setLogin_user] = useState(""); - const [login_password, setLogin_password] = useState(""); + const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); + const [currentMenuItem, setCurrentMenuItem] = useState("home"); + const [loginTabValue, setLoginTabValue] = useState("1"); - const changeTo_email = (e: React.ChangeEvent) => { - setTo_email(e.target.value); + const [email, setEmail] = useState(""); + const [verificationCode, setVerificationCode] = useState(""); + + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value); }; - const changeCode = (e: React.ChangeEvent) => { - setCode(e.target.value); + const handleVerificationCodeChange = (e: React.ChangeEvent) => { + setVerificationCode(e.target.value); }; - const changeLogin_user = (e: React.ChangeEvent) => { - setLogin_user(e.target.value); + const handleUsernameChange = (e: React.ChangeEvent) => { + setUsername(e.target.value); }; - const changeLogin_password = (e: React.ChangeEvent) => { - setLogin_password(e.target.value); + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value); }; + const [isEmailSent, setIsEmailSent] = useState(false); + const [emailCountdown, setEmailCountdown] = useState(0); - const [isSendEmail, setIsSendEmail] = useState(false); - const [sendEmailTime, setSendEmailTime] = useState(0); - - const tab_to_register = () => { - setLogin_dialog_tab_value("2"); + const switchToRegisterTab = () => { + setLoginTabValue("2"); }; - const tab_to_login = () => { - setLogin_dialog_tab_value("1"); + const switchToLoginTab = () => { + setLoginTabValue("1"); }; const items: MenuProps["items"] = [ @@ -133,7 +159,6 @@ const Navbar: React.FC = () => { ), key: "search", disabled: true, - className: "right-align-container", }, { label: ( @@ -155,10 +180,10 @@ const Navbar: React.FC = () => { <> { - setLoginOpen(true); + setIsLoginModalOpen(true); }} > 登录 @@ -166,7 +191,7 @@ const Navbar: React.FC = () => { { - setLoginOpen(true); + setIsLoginModalOpen(true); }} > 点击登录 @@ -179,7 +204,6 @@ const Navbar: React.FC = () => { children: isLogin ? [ { - className: "width_300", type: "group", label: (
用户10086
@@ -204,10 +228,7 @@ const Navbar: React.FC = () => { { label: (
), disabled: true, @@ -225,10 +246,7 @@ const Navbar: React.FC = () => { { label: (
登录后你可以:
111
@@ -241,9 +259,7 @@ const Navbar: React.FC = () => { label: ( <> 你发布评论后,也可以收到别人的回复 @@ -254,9 +270,7 @@ const Navbar: React.FC = () => { label: ( <> 查看自己的历史浏览记录 @@ -268,9 +282,7 @@ const Navbar: React.FC = () => { label: ( <> 收藏需要的工具和文章 @@ -282,9 +294,7 @@ const Navbar: React.FC = () => { label: ( <> 部分登录才可以使用的功能 @@ -298,7 +308,7 @@ const Navbar: React.FC = () => { type="primary" block onClick={async () => { - setLoginOpen(true); + setIsLoginModalOpen(true); }} > 立即登录 @@ -309,13 +319,13 @@ const Navbar: React.FC = () => { }, { label: ( -
+
首次登录?{" "} ) - : ( - isSendEmail - ? ("重新发送") - : ("发送验证码") - ) - } + {emailCountdown > 0 ? ( + + ) : isEmailSent ? ( + "重新发送" + ) : ( + "发送验证码" + )} { gap="medium" vertical={false} justify="center" - style={{ - marginTop: "30px", - }} + className="margin-top-30" > @@ -205,11 +180,7 @@ const Home: React.FC = () => { {
设置
个性化以及全局设置
@@ -98,11 +85,7 @@ const Home: React.FC = () => { } placeholder="搜索设置项..." - style={{ - margin: "10px", - marginTop: "10px", - marginBottom: "10px", - }} + className="margin_10 margin-top-10 margin-bottom-10" />
{ items={items} /> - +