This commit is contained in:
2026-04-02 22:54:04 +08:00
parent 624268216d
commit 649b99b6b4
10 changed files with 3943 additions and 3297 deletions
-2970
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -14,9 +14,13 @@
"@chakra-ui/react": "^3.34.0",
"@emotion/react": "^11.14.0",
"antd": "^6.3.5",
"dayjs": "^1.11.20",
"i18next": "^26.0.3",
"i18next-browser-languagedetector": "^8.2.1",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^17.0.2",
"react-icons": "^5.6.0",
"react-lorem-ipsum": "^1.4.10",
"zustand": "^5.0.12"
@@ -35,4 +39,4 @@
"typescript-eslint": "^8.57.0",
"vite": "^8.0.1"
}
}
}
+85 -2
View File
@@ -20,6 +20,15 @@ importers:
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)
dayjs:
specifier: ^1.11.20
version: 1.11.20
i18next:
specifier: ^26.0.3
version: 26.0.3(typescript@5.9.3)
i18next-browser-languagedetector:
specifier: ^8.2.1
version: 8.2.1
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -29,6 +38,9 @@ importers:
react-dom:
specifier: ^19.2.4
version: 19.2.4(react@19.2.4)
react-i18next:
specifier: ^17.0.2
version: 17.0.2(i18next@26.0.3(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
react-icons:
specifier: ^5.6.0
version: 5.6.0(react@19.2.4)
@@ -37,7 +49,7 @@ importers:
version: 1.4.10
zustand:
specifier: ^5.0.12
version: 5.0.12(@types/react@19.2.14)(react@19.2.4)
version: 5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
devDependencies:
'@eslint/js':
specifier: ^9.39.4
@@ -1328,6 +1340,20 @@ packages:
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
html-parse-stringify@3.0.1:
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
i18next-browser-languagedetector@8.2.1:
resolution: {integrity: sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==}
i18next@26.0.3:
resolution: {integrity: sha512-1571kXINxHKY7LksWp8wP+zP0YqHSSpl/OW0Y0owFEf2H3s8gCAffWaZivcz14rMkOvn3R/psiQxVsR9t2Nafg==}
peerDependencies:
typescript: ^5 || ^6
peerDependenciesMeta:
typescript:
optional: true
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -1582,6 +1608,22 @@ packages:
peerDependencies:
react: ^19.2.4
react-i18next@17.0.2:
resolution: {integrity: sha512-shBftH2vaTWK2Bsp7FiL+cevx3xFJlvFxmsDFQSrJc+6twHkP0tv/bGa01VVWzpreUVVwU+3Hev5iFqRg65RwA==}
peerDependencies:
i18next: '>= 26.0.1'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
typescript: ^5 || ^6
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
typescript:
optional: true
react-icons@5.6.0:
resolution: {integrity: sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==}
peerDependencies:
@@ -1714,6 +1756,11 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
vite@8.0.3:
resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1757,6 +1804,10 @@ packages:
yaml:
optional: true
void-elements@3.1.0:
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
engines: {node: '>=0.10.0'}
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -3625,6 +3676,20 @@ snapshots:
dependencies:
react-is: 16.13.1
html-parse-stringify@3.0.1:
dependencies:
void-elements: 3.1.0
i18next-browser-languagedetector@8.2.1:
dependencies:
'@babel/runtime': 7.29.2
i18next@26.0.3(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.29.2
optionalDependencies:
typescript: 5.9.3
ignore@5.3.2: {}
ignore@7.0.5: {}
@@ -3828,6 +3893,17 @@ snapshots:
react: 19.2.4
scheduler: 0.27.0
react-i18next@17.0.2(i18next@26.0.3(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.29.2
html-parse-stringify: 3.0.1
i18next: 26.0.3(typescript@5.9.3)
react: 19.2.4
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
react-dom: 19.2.4(react@19.2.4)
typescript: 5.9.3
react-icons@5.6.0(react@19.2.4):
dependencies:
react: 19.2.4
@@ -3950,6 +4026,10 @@ snapshots:
dependencies:
punycode: 2.3.1
use-sync-external-store@1.6.0(react@19.2.4):
dependencies:
react: 19.2.4
vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0):
dependencies:
lightningcss: 1.32.0
@@ -3964,6 +4044,8 @@ snapshots:
- '@emnapi/core'
- '@emnapi/runtime'
void-elements@3.1.0: {}
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -3982,7 +4064,8 @@ snapshots:
zod@4.3.6: {}
zustand@5.0.12(@types/react@19.2.14)(react@19.2.4):
zustand@5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)):
optionalDependencies:
'@types/react': 19.2.14
react: 19.2.4
use-sync-external-store: 1.6.0(react@19.2.4)
+3
View File
@@ -0,0 +1,3 @@
.taps {
--tabs-indicator-bg: "red", --tabs-indicator-shadow: theme.shadows.xs, --tabs-trigger-radius: theme.radii.full
}
+214 -6
View File
@@ -1,14 +1,222 @@
// import { useState } from 'react'
import './App.css'
import { Provider } from "@/components/ui/provider"
import "./App.css";
import { Provider } from "@/components/ui/provider";
import Navbar from '@/components/Navbar/Navbar';
import Navbar from "@/components/Navbar/Navbar";
import Test from "@/components/test/test";
import { Pagination } from "antd";
import { Tabs } from "antd";
import type { TabsProps } from "antd";
import React, { useState } from "react";
import type { RadioChangeEvent } from "antd";
import { Flex, Radio } from "antd";
import { Card } from "antd";
import { Image, Button } from "antd";
import { EyeOutlined } from '@ant-design/icons';
const { Meta } = Card;
const App: React.FC = () => {
const [value, setValue] = useState(1);
const onChange = (e: RadioChangeEvent) => {
setValue(e.target.value);
};
const items: TabsProps["items"] = [
{
key: "all",
label: "全部分类",
children: (
<Radio.Group
onChange={onChange}
value={value}
optionType="button"
options={[
{
value: 1,
className: "option-1",
label: (
<Flex gap="small" justify="center" align="center" vertical>
综合排序
</Flex>
),
},
{
value: 2,
className: "option-2",
label: (
<Flex gap="small" justify="center" align="center" vertical>
最多使用
</Flex>
),
},
{
value: 3,
className: "option-3",
label: (
<Flex gap="small" justify="center" align="center" vertical>
最新发布
</Flex>
),
},
{
value: 4,
className: "option-4",
label: (
<Flex gap="small" justify="center" align="center" vertical>
最多收藏
</Flex>
),
},
]}
/>
),
},
{
key: "1",
label: "分类1",
children: "Content of Tab Pane 1",
},
{
key: "2",
label: "分类2",
children: "Content of Tab Pane 2",
},
{
key: "3",
label: "分类3",
children: "Content of Tab Pane 3",
},
];
const baseStyle: React.CSSProperties = {
width: "25%",
height: 350,
};
return (
<Provider>
<Navbar />
</Provider>
<>
<Provider>
<Navbar />
{/* <Test /> */}
<div
style={{
width: "100%",
height: "400px",
backgroundColor: "#b3c6ff",
lineHeight: "200px",
textAlign: "center",
}}
>
装饰用背景图片
</div>
</Provider>
<Tabs
defaultActiveKey="all"
items={items}
centered
style={{
margin: "30px",
marginLeft: "100px",
marginRight: "100px",
}}
/>
{Array.from({ length: 5 }).map((_, rowIndex) => (
<Flex
key={`row-${rowIndex}`}
vertical={false}
style={{
marginTop: rowIndex === 0 ? "30px" : "0",
marginBottom: "30px",
marginLeft: "50px",
marginRight: "50px",
}}
justify="center"
wrap="nowrap"
gap="middle"
>
{Array.from({ length: 5 }).map((_, colIndex) => {
const cardIndex = rowIndex * 5 + colIndex;
return (
<Flex
key={`card-${rowIndex}-${colIndex}`}
style={{
...baseStyle,
}}
justify="center"
align="center"
>
<Card
hoverable
style={{ width: 300 }}
cover={
<div
style={{
height: 200,
overflow: "hidden",
position: "relative",
}}
>
<Image
draggable={false}
alt={`card-${cardIndex}`}
src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png"
preview={false}
/>
</div>
}
>
<Meta title={(
<div>
<span>功能描述</span>
<span style={{float: "right"}}><EyeOutlined style={{marginRight: "5px"}}/>1111</span>
</div>
)} description="详细介绍..........." />
<Flex
justify="center"
align="center"
wrap="nowrap"
gap="middle"
style={{
marginTop: "20px",
}}
>
<Button color="primary">立刻使用</Button>
<Button>技术文档</Button>
</Flex>
</Card>
</Flex>
);
})}
</Flex>
))}
<Flex
justify="center"
style={{
margin: "30px",
marginTop: "60px",
marginBottom: "60px",
}}
>
<Pagination
align="center"
total={85}
showSizeChanger
showQuickJumper={true}
showTotal={(total) => `数据一共 ${total} 项`}
/>
</Flex>
</>
);
};
+365 -309
View File
@@ -14,14 +14,13 @@ import {
MessageTwoTone,
} from "@ant-design/icons";
import type { MenuProps } from "antd";
import { Menu } from "antd";
import { Menu, Modal } from "antd";
import React, { useState } from "react";
import { Input, Avatar } from "antd";
import { SearchOutlined } from "@ant-design/icons";
import "./Navbar.css";
import { Dialog, Portal, createOverlay, CloseButton } from "@chakra-ui/react";
import { PasswordInput } from "@/components/ui/password-input";
import { Input as ChakraInput, InputGroup, Box } from "@chakra-ui/react";
@@ -35,296 +34,17 @@ import { Space } from "antd";
import { Flex } from "antd";
interface DialogProps {
title: string;
description?: string;
content?: React.ReactNode;
}
const login_dialog = createOverlay<DialogProps>((props) => {
const { ...rest } = props;
return (
<Dialog.Root {...rest} size={"xl"} placement="center">
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.CloseTrigger top="0" asChild>
<CloseButton bg="bg" size="sm" />
</Dialog.CloseTrigger>
<Dialog.Body
style={{
paddingBottom: "10px",
}}
>
<div
style={{
display: "flex",
height: "100%",
minHeight: "400px",
}}
>
<Dialog.Body
style={{
textAlign: "center",
maxWidth: "300px",
borderRight: "1px solid #eee",
}}
>
<Dialog.Title>扫描二维码登录</Dialog.Title>
<Dialog.Body
style={{
marginTop: "30px",
}}
>
<QRCode
errorLevel="H"
size={200}
value="https://ant.design/"
/>
<div
style={{
textAlign: "center",
color: "#999",
marginTop: "20px",
}}
>
使用APP扫码登录
</div>
<div
style={{
textAlign: "center",
color: "#999",
marginTop: "5px",
}}
>
扫描登录或者扫码下载APP
</div>
</Dialog.Body>
</Dialog.Body>
<div>
<Dialog.Body
style={{
textAlign: "center",
}}
>
<Tabs
defaultActiveKey="1"
centered
items={[
{
label: (
<span className="not-font-select">
账号密码登录
</span>
),
key: "1",
children: (
<Box spaceY="-1px" marginTop="30px">
<InputGroup startElement="账号">
<ChakraInput
ps="3.5em"
pe="0"
style={{
borderRadius: "5px 5px 0 0px",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Space.Compact style={{ width: "100%" }}>
<InputGroup startElement="密码" flex="1">
<PasswordInput
ps="3.5em"
pe="0"
style={{
borderRadius: "0 0 0 5px",
borderRight: "0",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Button
color="default"
variant="link"
style={{
height: "40px",
borderRadius: "0 0 5px 0",
border:
"1px solid var(--chakra-colors-border)",
}}
>
忘记密码?
</Button>
</Space.Compact>
<Flex
gap="medium"
vertical={false}
justify="center"
style={{
marginTop: "30px",
}}
>
<Button block size="large">
登录
</Button>
<Button type="primary" block size="large">
注册
</Button>
</Flex>
</Box>
),
},
{
label: (
<span className="not-font-select">
邮箱验证码登录
</span>
),
key: "2",
children: (
<Box spaceY="-1px" marginTop="30px">
<Space.Compact style={{ width: "100%" }}>
<InputGroup startElement="邮箱" flex="1">
<ChakraInput
placeholder="请输入邮箱号"
ps="3.5em"
pe="0"
style={{
borderRadius: "5px 0 0 0px",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Button
color="default"
variant="link"
style={{
height: "40px",
borderRadius: "0 5px 0 0",
border:
"1px solid var(--chakra-colors-border)",
}}
>
获取验证码
</Button>
</Space.Compact>
<InputGroup startElement="验证码">
<ChakraInput
placeholder="请输入验证码"
ps="5em"
pe="0"
style={{
borderRadius: "0 0 5px 5px",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Flex
gap="medium"
vertical={false}
justify="center"
style={{
marginTop: "30px",
}}
>
<Button
size="large"
type="primary"
style={{
padding: "0 60px",
}}
>
登录 / 注册
</Button>
</Flex>
</Box>
),
},
]}
/>
</Dialog.Body>
<Dialog.Body>
<div
className="not-font-select"
style={{
textAlign: "center",
color: "#ccc",
}}
>
其他登录方式
</div>
<Flex
gap="medium"
vertical={false}
justify="center"
style={{
marginTop: "20px",
marginBottom: "30px",
}}
>
<Button color="default" variant="text">
<Avatar src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAMAAACfWMssAAAAb1BMVEUAAABMouVIn+NEn99MouVMouVMo+ZMouVKouRKouJMn+NMouZLouRLo+VMoeNMouZLoeVLoeVNn+RMouX///+83Pal0PL0+v6x1vRireiay/FXqOfS6Pl5uevH4vePxe+Ev+1utOrp9Pzp8/yEv+7XzqLPAAAAE3RSTlMA3yAQ74C/n2BQQK9wz5CPz7BwJ8NfpgAAAbNJREFUSMe1lutygjAQhTeEOyi2AQIC3t//GcvYqk3OYsh0+v1yMN+czc4mQBwyL5ONUDMiLsqc1iGzOFQGYZGu0KLZQkQSrNSQ6I2aGRqkpktxlXJQSc4LNsqJCBhPOCQwwXOa6PmbErxlNpJeVMqD6uWliqOfprPiyN5tcOp0PaObntnmo9hIAV39pFNA9BOogKb+RQN/h9+RCfzR1gbtQqSArtQmemAjUy4QI7GxBTy92qKGJfEshlgp0DO15srmhuIRFuX0Cc/2KO5hUUkF1xt3dxKKuUS3uCPBjA0w4rySwqYyDLCM+EqxVrd45UTtFm81ywlEuzkHXhyhOfY1DArfntg+jRMY/PB80CeODaLhHigpt7fYNSgeuvpgD7k0j9V4Gfbc6RjN7oRE1rCeejVo27vMD4/WFokynB33qUpnUYb+t5zg7+OhNYrVxx5PIzGRJ9ijniwxIC7y3DV3urZ9/DrDfQxvR793a+YnpvRk6+Nt6YXceRQq//rxgKb/h87/fVrtAmKQW2c/JfGkb0PDjBYJomUtMuJQTcRKDUmL0LLiDDWevCzie7LYJGXOWl/BnLhvbq/sWgAAAABJRU5ErkJggg==" />
QQ 登录
</Button>
<Button color="default" variant="text">
<Avatar src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAMAAACfWMssAAAAgVBMVEUAAABXu0BYt0BQt0BXu0BWu0BXu0BXu0BXu0BWukBWukBXvEBXu0BXvEBXvEBYukBVukBXu0BWt0BXukBXuEBXu0D////1+/Pq9+fV7s/A5rer3aBsw1jg89uBzHBiwEy14auL0Xug2ZOW1YeBzG93yGSW1YjL6sO14azL6cN2yGP3XpzOAAAAFXRSTlMA3yAQ78+/r5+AUI9w74BgYEBAkHDBb56KAAACF0lEQVRIx52W6XKDIBRGwT3GZmsRUXFP0vb9H7AKGS8aiCXnR0TCmU/gOoh0uJck8jEZwUGYXND/cOPggyz4CE//0HZgKeDIsdSA3Qs1Bk2XejLF7ckGe1fnOT7ZBDsaDxNiZ4Jna4Jnb7rgbeKrK7QnFuzBOxIrYrsJAth9iIdl/9CwLE0pv/elqfoegWpfXdAUYINW9GRkRIBWakBemiOVGRbpE1lpijwaPDANCxvCc8qBbcVF47vq5EQ1YjCK3nyXiXE3QqrpSseeu+jptc96XgWmHSGDEGtCmDHygpK5nUuRdr2MvvfNdMvzXCN+KVNk6RO0qOpr37fXJzFCwdzmT9532THZovmqFHxlF3/WcdWQGUsBI2g3K/G3WG3o4oEVsVqOK4RHaTpfaKkXCVsWzPTL65pPN7X4kxnEX6qIXS4mJqfOH5tVKSJWzJsiXqlcklxe5AI0yuL4RDUpiKkGphRrRFRK+lLk88AQSg4KXVC9TvwSRQ4MU5m1xZ2xlmnEm1LkrqeKTVbU5rcaNtJDCAWqCOutq90CpjiKMTFQZuuah/9Oo+h6ZtPkYTSxI0YKReWLtxFBpJ5bzjOasWYsoBp6HQSRW5R5tz4C4HS0PltjO/H05sH6iQDXtz0d3/94ANPes/9Asjd9572PwE8X6Tm+DPViZMQ5mLUDxGnVCFtqwDH0VlYQS22bcxIGIhn7UXLWWn+10s6FZo+4YQAAAABJRU5ErkJggg==" />
微信登录
</Button>
<Button color="default" variant="text">
<Avatar src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAMAAACfWMssAAAAh1BMVEUAAADXQDjTQDzaQzfcRTfbRDfcRDbcQzbbRDbbRDfcQzbaQzbbRDbbRDbaRDfcQzbbRDfaQjjaQjXcQzfYRDfbRDTbRDf////gW1D99PPtoZvyubT76Ob20M3pioLdT0Tvrajkc2n20c3rlo7mfnb0xcD43NrrlY7kcmniZ1343driZ1z0xMEgvW1iAAAAFnRSTlMAIBDf34Dv78+/n1BAj7CvcGBgkHBwDUc+aAAAAmpJREFUSMeVlueCqjAQRgOI0qy7m0YVsN297/98OwEdCUQ05w8WDt/MkIjEhJv8RIFHAS/c/CTkM9w4XFKN5ebwgeaDNWURObYa4s+oMWrG1MOruDV9w9o1eU5A37JwDN6CUksTPWsTPXvTRe89wXBCa2rB+ukd6By3K6ca8WcNVoyxLB26i0ex/mxgzRTiMlx990A6C09PZ6XKwXbpIyP6Fn4C8zKONHVYFrJ4tJWfoUMJ5k2PPBi0igHZrX8j4HVLITPXB7udeK1gHem9ziJjouTwQYmnhCCuxh5UhSJO9qKmWwxrTcZeyh5UpcyFyOH0Bi5SaYNNyG7qYeK95CttWUWlJn6PW5RsSgVtlyqxoEhEQs1rNEPrldf5cI8Qb+hxYRKlaVsSY4NZKqU81aynpQY0kTNF3pS4fsDFvTESx4Gi0b4vMqWKdl4UcHWukmSeZXnRV9Gb5VgcDucIJ3B11JbckSmKyXAC7V7I52hxI2WmyYYk0m7+Dbd8JR7boTYlbshOE9WBsYb+MglBZ6r4p8TxXL+Hi7zov2dCnY1N8v7ViIS4z21V9q3U7Ap9YkzOulnrLAkZLtYTA4f+CpYLHEjFgCMdtwhiTBGedT+D/NwtIHU2V3li4tE9iO5qaILQwvF4LNUtTLsr8InnTX6Pebfq8qopmup/V29W0CkRIaNISDmxJwLqNeEYHwG8repzltVpA/Ua8UmP61ErPHy2xnbiniBflg9WxA0sCnXJAMezadDSRM/aRM/eDMCb4n7Z/glE9rOhq5i8xPFfaz7GmdXIs9aw4O1qZIUxam9IdtuwS/aCaJcYrT+uh9kYccQkXQAAAABJRU5ErkJggg==" />
微博登录
</Button>
</Flex>
</Dialog.Body>
</div>
</div>
</Dialog.Body>
<Dialog.Body
style={{
paddingTop: "0px",
}}
>
<div
style={{
textAlign: "center",
color: "#ccc",
}}
>
未注册过账号的手机号/邮箱,我们将自动帮你注册账号{" "}
</div>
<div
style={{
textAlign: "center",
color: "#ccc",
}}
>
登录或完成注册即代表你同意
<Button color="primary" variant="link">
用户协议
</Button>
和
<Button color="primary" variant="link">
隐私政策
</Button>
</div>
</Dialog.Body>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
);
});
// login_dialog.open({});
const Navbar: React.FC = () => {
const [current, setCurrent] = useState("mail");
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 tab_to_register = () => {
setLogin_dialog_tab_value("2");
};
const items: MenuProps["items"] = [
{
@@ -388,16 +108,38 @@ const Navbar: React.FC = () => {
},
{
label: (
<span
className="margin_0_20"
onClick={() => {
login_dialog.open({});
}}
>
<Avatar size={40} icon={<UserOutlined />} className="margin_3_0" />
<span className="height_40_center display_inline_block margin_0_10">
用户: 10086
</span>
<span className="margin_0_20" onClick={() => {}}>
{isLogin ? (
<>
<span>
<Avatar
size={40}
className="margin_3_0"
icon={<UserOutlined />}
></Avatar>
<span className="height_40_center display_inline_block margin_0_10">
用户10086
</span>
</span>
</>
) : (
<>
<Avatar
size={40}
className="margin_3_0 not-font-select"
style={{ backgroundColor: "#00aeec" }}
onClick={async () => {
console.log(1);
setLoginOpen(true);
}}
>
登录
</Avatar>
<span className="height_40_center display_inline_block margin_0_10 not-font-select">
点击登录
</span>
</>
)}
</span>
),
key: "my",
@@ -459,7 +201,7 @@ const Navbar: React.FC = () => {
<div>111</div>
</div>
),
key: "login",
key: "info1",
disabled: true,
},
{
@@ -469,7 +211,7 @@ const Navbar: React.FC = () => {
style={{
marginRight: "10px",
}}
/>{" "}
/>
你发布评论后,也可以收到别人的回复
</>
),
@@ -482,10 +224,11 @@ const Navbar: React.FC = () => {
style={{
marginRight: "10px",
}}
/>{" "}
/>
查看自己的历史浏览记录
</>
),
key: "info2",
disabled: true,
},
{
@@ -495,10 +238,11 @@ const Navbar: React.FC = () => {
style={{
marginRight: "10px",
}}
/>{" "}
/>
收藏需要的工具和文章
</>
),
key: "info3",
disabled: true,
},
{
@@ -508,19 +252,20 @@ const Navbar: React.FC = () => {
style={{
marginRight: "10px",
}}
/>{" "}
/>
部分登录才可以使用的功能
</>
),
key: "inf4",
disabled: true,
},
{
label: (
<Button type="primary" block>
<Button type="primary" block onClick={() => setLoginOpen}>
立即登录
</Button>
),
key: "divider",
key: "",
disabled: true,
},
{
@@ -543,10 +288,15 @@ const Navbar: React.FC = () => {
console.log("click ", e);
setCurrent(e.key);
if (e.key == "home") {
login_dialog.open({});
if (e.key === "my") {
console.log("点击了主页");
}
if (e.key === "setting:4") {
console.log("点击了退出登录");
setIsLogin(false);
}
};
return (
<div
style={{
@@ -560,7 +310,313 @@ const Navbar: React.FC = () => {
mode="horizontal"
items={items}
/>
<login_dialog.Viewport />
<Modal
centered
open={openLogin}
footer={null}
onOk={() => setLoginOpen(false)}
onCancel={() => setLoginOpen(false)}
width={"900px"}
>
<div
style={{
width: "852px",
}}
role="alertdialog"
>
<div>
<div />
<div>
<div>
<div
style={{
paddingBottom: "10px",
}}
>
<div
style={{
display: "flex",
height: "100%",
minHeight: "400px",
}}
>
<div
style={{
textAlign: "center",
minWidth: "300px",
borderRight: "1px solid #eee",
}}
>
<div>扫描二维码登录</div>
<div
style={{
marginTop: "30px",
}}
>
<Flex justify="center">
<QRCode
errorLevel="H"
size={200}
style={{
textAlign: "center"
}}
value="https://ant.design/"
/>
</Flex>
<div
style={{
textAlign: "center",
color: "#999",
marginTop: "20px",
}}
>
使用APP扫码登录
</div>
<div
style={{
textAlign: "center",
color: "#999",
marginTop: "5px",
}}
>
扫描登录或者扫码下载APP
</div>
</div>
</div>
<div>
<div
style={{
width: "500px",
marginLeft: "40px",
marginRight: "40px",
textAlign: "center",
}}
>
<Tabs
defaultActiveKey={login_dialog_tab_value}
centered
items={[
{
label: (
<span className="not-font-select">
账号密码登录
</span>
),
key: "1",
children: (
<Flex justify="center">
<Box spaceY="-1px" marginTop="30px">
<InputGroup startElement="账号">
<ChakraInput
autoFocus
size="lg"
ps="3.5em"
pe="0"
style={{
borderRadius: "5px 5px 0 0px",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Space.Compact style={{ width: "100%" }}>
<InputGroup startElement="密码" flex="1">
<PasswordInput
size="lg"
ps="3.5em"
pe="0"
style={{
borderRadius: "0 0 0 5px",
borderRight: "0",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Button
color="default"
variant="link"
style={{
height: "44px",
borderRadius: "0 0 5px 0",
border:
"1px solid var(--chakra-colors-border)",
}}
>
忘记密码?
</Button>
</Space.Compact>
<Flex
gap="medium"
vertical={false}
justify="center"
style={{
marginTop: "30px",
}}
>
<Button
block
size="large"
onClick={tab_to_register}
>
注册
</Button>
<Button type="primary" block size="large">
登录
</Button>
</Flex>
</Box>
</Flex>
),
},
{
label: (
<span className="not-font-select">
邮箱验证码登录
</span>
),
key: "2",
children: (
<Box spaceY="-1px" marginTop="30px">
<Space.Compact style={{ width: "100%" }}>
<InputGroup startElement="邮箱" flex="1">
<ChakraInput
size="lg"
placeholder="请输入邮箱号"
ps="3.5em"
pe="0"
style={{
borderRadius: "5px 0 0 0px",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Button
color="default"
variant="link"
style={{
height: "44px",
borderRadius: "0 5px 0 0",
border:
"1px solid var(--chakra-colors-border)",
}}
>
获取验证码
</Button>
</Space.Compact>
<InputGroup startElement="验证码">
<ChakraInput
size="lg"
placeholder="请输入验证码"
ps="5em"
pe="0"
style={{
borderRadius: "0 0 5px 5px",
}}
css={{ "--focus-color": "#bff" }}
/>
</InputGroup>
<Flex
gap="medium"
vertical={false}
justify="center"
style={{
marginTop: "30px",
}}
>
<Button
size="large"
type="primary"
style={{
padding: "0 60px",
}}
>
登录 / 注册
</Button>
</Flex>
</Box>
),
},
]}
/>
</div>
<div>
<div
className="not-font-select"
style={{
textAlign: "center",
color: "#ccc",
marginTop: "30px"
}}
>
其他登录方式
</div>
<Flex
gap="medium"
vertical={false}
justify="center"
style={{
marginTop: "20px",
marginBottom: "30px",
}}
>
<Button color="default" variant="text">
<Avatar src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAMAAACfWMssAAAAb1BMVEUAAABMouVIn+NEn99MouVMouVMo+ZMouVKouRKouJMn+NMouZLouRLo+VMoeNMouZLoeVLoeVNn+RMouX///+83Pal0PL0+v6x1vRireiay/FXqOfS6Pl5uevH4vePxe+Ev+1utOrp9Pzp8/yEv+7XzqLPAAAAE3RSTlMA3yAQ74C/n2BQQK9wz5CPz7BwJ8NfpgAAAbNJREFUSMe1lutygjAQhTeEOyi2AQIC3t//GcvYqk3OYsh0+v1yMN+czc4mQBwyL5ONUDMiLsqc1iGzOFQGYZGu0KLZQkQSrNSQ6I2aGRqkpktxlXJQSc4LNsqJCBhPOCQwwXOa6PmbErxlNpJeVMqD6uWliqOfprPiyN5tcOp0PaObntnmo9hIAV39pFNA9BOogKb+RQN/h9+RCfzR1gbtQqSArtQmemAjUy4QI7GxBTy92qKGJfEshlgp0DO15srmhuIRFuX0Cc/2KO5hUUkF1xt3dxKKuUS3uCPBjA0w4rySwqYyDLCM+EqxVrd45UTtFm81ywlEuzkHXhyhOfY1DArfntg+jRMY/PB80CeODaLhHigpt7fYNSgeuvpgD7k0j9V4Gfbc6RjN7oRE1rCeejVo27vMD4/WFokynB33qUpnUYb+t5zg7+OhNYrVxx5PIzGRJ9ijniwxIC7y3DV3urZ9/DrDfQxvR793a+YnpvRk6+Nt6YXceRQq//rxgKb/h87/fVrtAmKQW2c/JfGkb0PDjBYJomUtMuJQTcRKDUmL0LLiDDWevCzie7LYJGXOWl/BnLhvbq/sWgAAAABJRU5ErkJggg==" />
QQ 登录
</Button>
<Button color="default" variant="text">
<Avatar src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAMAAACfWMssAAAAgVBMVEUAAABXu0BYt0BQt0BXu0BWu0BXu0BXu0BXu0BWukBWukBXvEBXu0BXvEBXvEBYukBVukBXu0BWt0BXukBXuEBXu0D////1+/Pq9+fV7s/A5rer3aBsw1jg89uBzHBiwEy14auL0Xug2ZOW1YeBzG93yGSW1YjL6sO14azL6cN2yGP3XpzOAAAAFXRSTlMA3yAQ78+/r5+AUI9w74BgYEBAkHDBb56KAAACF0lEQVRIx52W6XKDIBRGwT3GZmsRUXFP0vb9H7AKGS8aiCXnR0TCmU/gOoh0uJck8jEZwUGYXND/cOPggyz4CE//0HZgKeDIsdSA3Qs1Bk2XejLF7ckGe1fnOT7ZBDsaDxNiZ4Jna4Jnb7rgbeKrK7QnFuzBOxIrYrsJAth9iIdl/9CwLE0pv/elqfoegWpfXdAUYINW9GRkRIBWakBemiOVGRbpE1lpijwaPDANCxvCc8qBbcVF47vq5EQ1YjCK3nyXiXE3QqrpSseeu+jptc96XgWmHSGDEGtCmDHygpK5nUuRdr2MvvfNdMvzXCN+KVNk6RO0qOpr37fXJzFCwdzmT9532THZovmqFHxlF3/WcdWQGUsBI2g3K/G3WG3o4oEVsVqOK4RHaTpfaKkXCVsWzPTL65pPN7X4kxnEX6qIXS4mJqfOH5tVKSJWzJsiXqlcklxe5AI0yuL4RDUpiKkGphRrRFRK+lLk88AQSg4KXVC9TvwSRQ4MU5m1xZ2xlmnEm1LkrqeKTVbU5rcaNtJDCAWqCOutq90CpjiKMTFQZuuah/9Oo+h6ZtPkYTSxI0YKReWLtxFBpJ5bzjOasWYsoBp6HQSRW5R5tz4C4HS0PltjO/H05sH6iQDXtz0d3/94ANPes/9Asjd9572PwE8X6Tm+DPViZMQ5mLUDxGnVCFtqwDH0VlYQS22bcxIGIhn7UXLWWn+10s6FZo+4YQAAAABJRU5ErkJggg==" />
微信登录
</Button>
<Button color="default" variant="text">
<Avatar src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAMAAACfWMssAAAAh1BMVEUAAADXQDjTQDzaQzfcRTfbRDfcRDbcQzbbRDbbRDfcQzbaQzbbRDbbRDbaRDfcQzbbRDfaQjjaQjXcQzfYRDfbRDTbRDf////gW1D99PPtoZvyubT76Ob20M3pioLdT0Tvrajkc2n20c3rlo7mfnb0xcD43NrrlY7kcmniZ1343driZ1z0xMEgvW1iAAAAFnRSTlMAIBDf34Dv78+/n1BAj7CvcGBgkHBwDUc+aAAAAmpJREFUSMeVlueCqjAQRgOI0qy7m0YVsN297/98OwEdCUQ05w8WDt/MkIjEhJv8RIFHAS/c/CTkM9w4XFKN5ebwgeaDNWURObYa4s+oMWrG1MOruDV9w9o1eU5A37JwDN6CUksTPWsTPXvTRe89wXBCa2rB+ukd6By3K6ca8WcNVoyxLB26i0ex/mxgzRTiMlx990A6C09PZ6XKwXbpIyP6Fn4C8zKONHVYFrJ4tJWfoUMJ5k2PPBi0igHZrX8j4HVLITPXB7udeK1gHem9ziJjouTwQYmnhCCuxh5UhSJO9qKmWwxrTcZeyh5UpcyFyOH0Bi5SaYNNyG7qYeK95CttWUWlJn6PW5RsSgVtlyqxoEhEQs1rNEPrldf5cI8Qb+hxYRKlaVsSY4NZKqU81aynpQY0kTNF3pS4fsDFvTESx4Gi0b4vMqWKdl4UcHWukmSeZXnRV9Gb5VgcDucIJ3B11JbckSmKyXAC7V7I52hxI2WmyYYk0m7+Dbd8JR7boTYlbshOE9WBsYb+MglBZ6r4p8TxXL+Hi7zov2dCnY1N8v7ViIS4z21V9q3U7Ap9YkzOulnrLAkZLtYTA4f+CpYLHEjFgCMdtwhiTBGedT+D/NwtIHU2V3li4tE9iO5qaILQwvF4LNUtTLsr8InnTX6Pebfq8qopmup/V29W0CkRIaNISDmxJwLqNeEYHwG8repzltVpA/Ua8UmP61ErPHy2xnbiniBflg9WxA0sCnXJAMezadDSRM/aRM/eDMCb4n7Z/glE9rOhq5i8xPFfaz7GmdXIs9aw4O1qZIUxam9IdtuwS/aCaJcYrT+uh9kYccQkXQAAAABJRU5ErkJggg==" />
微博登录
</Button>
</Flex>
</div>
</div>
</div>
</div>
<div
style={{
paddingTop: "0px",
}}
>
<div
style={{
textAlign: "center",
color: "#ccc",
}}
>
未注册过账号的手机号/邮箱,我们将自动帮你注册账号{" "}
</div>
<div
style={{
textAlign: "center",
color: "#ccc",
}}
>
登录或完成注册即代表你同意
<Button color="primary" variant="link">
用户协议
</Button>
和
<Button color="primary" variant="link">
隐私政策
</Button>
</div>
</div>
</div>
</div>
</div>
</div>
</Modal>
</div>
);
};
+56
View File
@@ -0,0 +1,56 @@
import React, { useState } from 'react';
import { Button, Flex, Modal } from 'antd';
const Test: React.FC = () => {
const [open, setOpen] = useState(false);
const [openResponsive, setOpenResponsive] = useState(false);
return (
<Flex vertical gap="medium" align="flex-start">
{/* Basic */}
<Button type="primary" onClick={() => setOpen(true)}>
Open Modal of 1000px width
</Button>
<Modal
title="Modal 1000px width"
centered
open={open}
onOk={() => setOpen(false)}
onCancel={() => setOpen(false)}
width={1000}
>
<p>some contents...</p>
<p>some contents...</p>
<p>some contents...</p>
</Modal>
{/* Responsive */}
<Button type="primary" onClick={() => setOpenResponsive(true)}>
Open Modal of responsive width
</Button>
<Modal
title="Modal responsive width"
centered
open={openResponsive}
width={{
xs: '90%',
sm: '80%',
md: '70%',
lg: '60%',
xl: '50%',
xxl: '40%',
}}
>
<p>some contents...</p>
<div>111</div>
<div>111</div>
<div>111</div>
<div>111</div><div>111</div>
<div>111</div>
</Modal>
</Flex>
);
};
export default Test;
+45
View File
@@ -0,0 +1,45 @@
// src/i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
// 导入语言文件
import translationEN from './locales/en/translation.json';
import translationZH from './locales/zh/translation.json';
// 语言资源
const resources = {
en: {
translation: translationEN
},
zh: {
translation: translationZH
}
};
i18n
.use(LanguageDetector) // 检测浏览器语言
.use(initReactI18next) // 绑定 react-i18next
.init({
resources,
fallbackLng: 'zh', // 默认语言
lng: 'zh', // 设置默认语言为中文
// 检测选项
detection: {
order: ['localStorage', 'navigator', 'htmlTag', 'path', 'subdomain'],
caches: ['localStorage'],
},
interpolation: {
escapeValue: false // react 已经做了 XSS 防护
},
// 其他配置
debug: process.env.NODE_ENV === 'development',
saveMissing: true, // 保存缺失的翻译
keySeparator: false, // 使用嵌套结构
nsSeparator: false
});
export default i18n;
+30 -9
View File
@@ -1,13 +1,34 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import 'antd/dist/antd.css';
import App from './App.tsx'
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import "antd/dist/antd.css";
import App from "./App.tsx";
import './global.css'
import "./global.css";
import { ConfigProvider } from "antd";
import zhCN from 'antd/es/locale/zh_CN';
import enUS from 'antd/es/locale/en_US';
import jaJP from 'antd/es/locale/ja_JP';
import koKR from 'antd/es/locale/ko_KR';
import zhTW from 'antd/es/locale/zh_TW';
import frFR from 'antd/es/locale/fr_FR';
import deDE from 'antd/es/locale/de_DE';
import esES from 'antd/es/locale/es_ES';
import ruRU from 'antd/es/locale/ru_RU';
import arEG from 'antd/es/locale/ar_EG';
createRoot(document.getElementById('root')!).render(
// 初始可用语言选择
// 未写分配语言包的代码
import dayjs from 'dayjs';
dayjs.locale('zh-cn');
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
</StrictMode>,
)
);
+3140
View File
File diff suppressed because it is too large Load Diff