1
This commit is contained in:
+3
-1
@@ -17,7 +17,9 @@
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-icons": "^5.6.0"
|
||||
"react-icons": "^5.6.0",
|
||||
"react-lorem-ipsum": "^1.4.10",
|
||||
"zustand": "^5.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
|
||||
Generated
+48
-4
@@ -19,7 +19,7 @@ importers:
|
||||
version: 11.14.0(@types/react@19.2.14)(react@19.2.4)
|
||||
antd:
|
||||
specifier: ^6.3.5
|
||||
version: 6.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
version: 6.3.5(date-fns@2.30.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
next-themes:
|
||||
specifier: ^0.4.6
|
||||
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -32,6 +32,12 @@ importers:
|
||||
react-icons:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.0(react@19.2.4)
|
||||
react-lorem-ipsum:
|
||||
specifier: ^1.4.10
|
||||
version: 1.4.10
|
||||
zustand:
|
||||
specifier: ^5.0.12
|
||||
version: 5.0.12(@types/react@19.2.14)(react@19.2.4)
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^9.39.4
|
||||
@@ -1151,6 +1157,10 @@ packages:
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
date-fns@2.30.0:
|
||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||
engines: {node: '>=0.11'}
|
||||
|
||||
dayjs@1.11.20:
|
||||
resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
|
||||
|
||||
@@ -1583,6 +1593,9 @@ packages:
|
||||
react-is@18.3.1:
|
||||
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
||||
|
||||
react-lorem-ipsum@1.4.10:
|
||||
resolution: {integrity: sha512-PCEuauLZTGJkm++oxJeUycEgQOBg3qmmD7pn5sUyq9e0OvvNbW32RKQsSgalAYGUIbS91UkDgxvt6iHVAsqYJw==}
|
||||
|
||||
react@19.2.4:
|
||||
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1773,6 +1786,24 @@ packages:
|
||||
zod@4.3.6:
|
||||
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
|
||||
|
||||
zustand@5.0.12:
|
||||
resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=18.0.0'
|
||||
immer: '>=9.0.6'
|
||||
react: '>=18.0.0'
|
||||
use-sync-external-store: '>=1.2.0'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
use-sync-external-store:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ant-design/colors@8.0.1':
|
||||
@@ -2364,7 +2395,7 @@ snapshots:
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
|
||||
'@rc-component/picker@1.9.1(dayjs@1.11.20)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
'@rc-component/picker@1.9.1(date-fns@2.30.0)(dayjs@1.11.20)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@rc-component/overflow': 1.0.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/resize-observer': 1.1.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -2374,6 +2405,7 @@ snapshots:
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
optionalDependencies:
|
||||
date-fns: 2.30.0
|
||||
dayjs: 1.11.20
|
||||
|
||||
'@rc-component/portal@2.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
@@ -3297,7 +3329,7 @@ snapshots:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
antd@6.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||
antd@6.3.5(date-fns@2.30.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||
dependencies:
|
||||
'@ant-design/colors': 8.0.1
|
||||
'@ant-design/cssinjs': 2.1.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -3323,7 +3355,7 @@ snapshots:
|
||||
'@rc-component/mutate-observer': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/notification': 1.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/pagination': 1.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/picker': 1.9.1(dayjs@1.11.20)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/picker': 1.9.1(date-fns@2.30.0)(dayjs@1.11.20)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/progress': 1.0.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/qrcode': 1.1.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@rc-component/rate': 1.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -3426,6 +3458,11 @@ snapshots:
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
date-fns@2.30.0:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
optional: true
|
||||
|
||||
dayjs@1.11.20: {}
|
||||
|
||||
debug@4.4.3:
|
||||
@@ -3799,6 +3836,8 @@ snapshots:
|
||||
|
||||
react-is@18.3.1: {}
|
||||
|
||||
react-lorem-ipsum@1.4.10: {}
|
||||
|
||||
react@19.2.4: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
@@ -3942,3 +3981,8 @@ snapshots:
|
||||
zod: 4.3.6
|
||||
|
||||
zod@4.3.6: {}
|
||||
|
||||
zustand@5.0.12(@types/react@19.2.14)(react@19.2.4):
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
react: 19.2.4
|
||||
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
.height_40 {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.height_40_center {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.display_inline_block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.text_center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.width_300 {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.width_400 {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.margin_3_0 {
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.margin_10_20 {
|
||||
margin: 10px 20px;
|
||||
}
|
||||
|
||||
.margin_10_0 {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.margin_0_20 {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.margin_0_10 {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.margin_10_10 {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.margin_20_10 {
|
||||
margin: 20px 10px;
|
||||
}
|
||||
|
||||
.margin_0_5 {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.width_600 {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.margin_left_600 {
|
||||
margin-left: 600px;
|
||||
}
|
||||
|
||||
.right-align-container {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
+5
-138
@@ -1,147 +1,14 @@
|
||||
// import { useState } from 'react'
|
||||
import './App.css'
|
||||
import { Provider } from "@/components/ui/provider"
|
||||
|
||||
import { HomeOutlined, SettingOutlined, ApiOutlined, FolderOutlined, ClearOutlined, UserOutlined, CommentOutlined, AlertOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Menu } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { Input, Avatar } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
const items: MenuProps['items'] = [
|
||||
|
||||
{
|
||||
label: '主页',
|
||||
key: 'home',
|
||||
icon: <HomeOutlined />,
|
||||
},
|
||||
{
|
||||
label: '工具大全',
|
||||
key: 'tools',
|
||||
icon: <ApiOutlined />,
|
||||
},
|
||||
{
|
||||
label: '文章资源',
|
||||
key: 'articles',
|
||||
icon: <FolderOutlined />,
|
||||
},
|
||||
{
|
||||
label: '消息',
|
||||
key: 'xx',
|
||||
icon: <CommentOutlined />,
|
||||
children:[
|
||||
{
|
||||
label: '回复我的',
|
||||
key: 'xx1',
|
||||
},
|
||||
{
|
||||
label: '@我的',
|
||||
key: 'xx2',
|
||||
},
|
||||
{
|
||||
label: '收到的赞',
|
||||
key: 'xx3',
|
||||
},
|
||||
{
|
||||
label: '系统消息',
|
||||
key: 'xx4',
|
||||
},
|
||||
{
|
||||
label: '我的消息',
|
||||
key: 'xx5',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'bug反馈',
|
||||
key: 'bug',
|
||||
icon: <AlertOutlined />,
|
||||
},
|
||||
{
|
||||
label: (<Input
|
||||
placeholder="请输入搜索内容"
|
||||
className='width_600 height_40 margin_3_0 margin_left_600'
|
||||
prefix={<SearchOutlined />}
|
||||
/>),
|
||||
key: 'search',
|
||||
disabled: true,
|
||||
className: "right-align-container",
|
||||
},
|
||||
{
|
||||
label: (<span className='margin_0_20' >
|
||||
|
||||
<Avatar size={40} icon={<UserOutlined />} className='margin_3_0'/>
|
||||
<span className='height_40_center display_inline_block margin_0_10' >
|
||||
用户: 10086
|
||||
</span>
|
||||
</span>),
|
||||
key: 'home',
|
||||
children: [
|
||||
{
|
||||
className: "width_300",
|
||||
type: 'group',
|
||||
label: (
|
||||
<div className='height_40_center text_center'>用户10086</div>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
label: '个人中心',
|
||||
key: 'setting:1',
|
||||
icon: <UserOutlined />
|
||||
},
|
||||
{
|
||||
label: '个人设置',
|
||||
key: 'setting:2',
|
||||
icon: <SettingOutlined />,
|
||||
},
|
||||
|
||||
{
|
||||
label: '浏览历史',
|
||||
key: 'setting:2',
|
||||
icon: <ClearOutlined />
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<div style={{
|
||||
borderBottom: "1px #eee solid",
|
||||
height:"20px"
|
||||
}}></div>
|
||||
),
|
||||
disabled: true,
|
||||
key: 'divider',
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
key: 'setting:3',
|
||||
icon: <UploadOutlined />
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
className: "right-align-container",
|
||||
},
|
||||
];
|
||||
|
||||
import Navbar from '@/components/Navbar/Navbar';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [current, setCurrent] = useState('mail');
|
||||
|
||||
const onClick: MenuProps['onClick'] = e => {
|
||||
console.log('click ', e);
|
||||
setCurrent(e.key);
|
||||
};
|
||||
return (
|
||||
<div style={{
|
||||
borderBottom: "1px solid #eee",
|
||||
margin: "10px 10px"
|
||||
}}>
|
||||
|
||||
<Menu onClick={onClick} selectedKeys={[current]} mode="horizontal" items={items} style = {{
|
||||
border: "0"
|
||||
}} />
|
||||
|
||||
|
||||
</div>
|
||||
<Provider>
|
||||
<Navbar />
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.right-align-container {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.not-font-select {
|
||||
-webkit-user-select: none;
|
||||
/* Safari */
|
||||
-moz-user-select: none;
|
||||
/* Firefox */
|
||||
-ms-user-select: none;
|
||||
/* Internet Explorer */
|
||||
user-select: none;
|
||||
/* 标准语法 */
|
||||
}
|
||||
@@ -0,0 +1,568 @@
|
||||
import {
|
||||
HomeOutlined,
|
||||
SettingOutlined,
|
||||
ApiOutlined,
|
||||
FolderOutlined,
|
||||
ClearOutlined,
|
||||
UserOutlined,
|
||||
CommentOutlined,
|
||||
AlertOutlined,
|
||||
UploadOutlined,
|
||||
ClockCircleTwoTone,
|
||||
PlusCircleTwoTone,
|
||||
ControlTwoTone,
|
||||
MessageTwoTone,
|
||||
} from "@ant-design/icons";
|
||||
import type { MenuProps } from "antd";
|
||||
import { Menu } 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";
|
||||
|
||||
import { QRCode, Button } from "antd";
|
||||
// import { PasswordInput } from "@/components/ui/password-input"
|
||||
|
||||
import { Tabs } from "antd";
|
||||
|
||||
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 [isLogin, setIsLogin] = useState(false);
|
||||
|
||||
const items: MenuProps["items"] = [
|
||||
{
|
||||
label: "主页",
|
||||
key: "home",
|
||||
icon: <HomeOutlined />,
|
||||
},
|
||||
{
|
||||
label: "工具大全",
|
||||
key: "tools",
|
||||
icon: <ApiOutlined />,
|
||||
},
|
||||
{
|
||||
label: "文章资源",
|
||||
key: "articles",
|
||||
icon: <FolderOutlined />,
|
||||
},
|
||||
{
|
||||
label: "消息",
|
||||
key: "xx",
|
||||
icon: <CommentOutlined />,
|
||||
children: [
|
||||
{
|
||||
label: "回复我的",
|
||||
key: "xx1",
|
||||
},
|
||||
{
|
||||
label: "@我的",
|
||||
key: "xx2",
|
||||
},
|
||||
{
|
||||
label: "收到的赞",
|
||||
key: "xx3",
|
||||
},
|
||||
{
|
||||
label: "系统消息",
|
||||
key: "xx4",
|
||||
},
|
||||
{
|
||||
label: "我的消息",
|
||||
key: "xx5",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "bug反馈",
|
||||
key: "bug",
|
||||
icon: <AlertOutlined />,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Input
|
||||
placeholder="请输入搜索内容"
|
||||
className="width_600 height_40 margin_3_0 margin_left_600"
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
),
|
||||
key: "search",
|
||||
disabled: true,
|
||||
className: "right-align-container",
|
||||
},
|
||||
{
|
||||
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>
|
||||
),
|
||||
key: "my",
|
||||
children: isLogin
|
||||
? [
|
||||
{
|
||||
className: "width_300",
|
||||
type: "group",
|
||||
label: (
|
||||
<div className="height_40_center text_center">用户10086</div>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
label: "个人中心",
|
||||
key: "setting:1",
|
||||
icon: <UserOutlined />,
|
||||
},
|
||||
{
|
||||
label: "个人设置",
|
||||
key: "setting:2",
|
||||
icon: <SettingOutlined />,
|
||||
},
|
||||
|
||||
{
|
||||
label: "浏览历史",
|
||||
key: "setting:3",
|
||||
icon: <ClearOutlined />,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<div
|
||||
style={{
|
||||
borderBottom: "1px #eee solid",
|
||||
height: "20px",
|
||||
}}
|
||||
></div>
|
||||
),
|
||||
disabled: true,
|
||||
key: "divider",
|
||||
},
|
||||
{
|
||||
label: "退出登录",
|
||||
key: "setting:4",
|
||||
icon: <UploadOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
label: (
|
||||
<div
|
||||
className="width_300"
|
||||
style={{
|
||||
color: "#000",
|
||||
}}
|
||||
>
|
||||
登录后你可以:
|
||||
<div>111</div>
|
||||
</div>
|
||||
),
|
||||
key: "login",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<MessageTwoTone
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
}}
|
||||
/>{" "}
|
||||
你发布评论后,也可以收到别人的回复
|
||||
</>
|
||||
),
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<ClockCircleTwoTone
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
}}
|
||||
/>{" "}
|
||||
查看自己的历史浏览记录
|
||||
</>
|
||||
),
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<ControlTwoTone
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
}}
|
||||
/>{" "}
|
||||
收藏需要的工具和文章
|
||||
</>
|
||||
),
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<>
|
||||
<PlusCircleTwoTone
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
}}
|
||||
/>{" "}
|
||||
部分登录才可以使用的功能
|
||||
</>
|
||||
),
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Button type="primary" block>
|
||||
立即登录
|
||||
</Button>
|
||||
),
|
||||
key: "divider",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
首次登录?{" "}
|
||||
<Button color="primary" variant="link">
|
||||
立即注册
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
className: "right-align-container",
|
||||
},
|
||||
];
|
||||
|
||||
const onClick: MenuProps["onClick"] = (e) => {
|
||||
console.log("click ", e);
|
||||
setCurrent(e.key);
|
||||
|
||||
if (e.key == "home") {
|
||||
login_dialog.open({});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
borderBottom: "1px solid #eee",
|
||||
margin: "10px 10px",
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
onClick={onClick}
|
||||
selectedKeys={[current]}
|
||||
mode="horizontal"
|
||||
items={items}
|
||||
/>
|
||||
<login_dialog.Viewport />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
@@ -0,0 +1,159 @@
|
||||
"use client"
|
||||
|
||||
import type {
|
||||
ButtonProps,
|
||||
GroupProps,
|
||||
InputProps,
|
||||
StackProps,
|
||||
} from "@chakra-ui/react"
|
||||
import {
|
||||
Box,
|
||||
HStack,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
Stack,
|
||||
mergeRefs,
|
||||
useControllableState,
|
||||
} from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
import { LuEye, LuEyeOff } from "react-icons/lu"
|
||||
|
||||
export interface PasswordVisibilityProps {
|
||||
/**
|
||||
* The default visibility state of the password input.
|
||||
*/
|
||||
defaultVisible?: boolean
|
||||
/**
|
||||
* The controlled visibility state of the password input.
|
||||
*/
|
||||
visible?: boolean
|
||||
/**
|
||||
* Callback invoked when the visibility state changes.
|
||||
*/
|
||||
onVisibleChange?: (visible: boolean) => void
|
||||
/**
|
||||
* Custom icons for the visibility toggle button.
|
||||
*/
|
||||
visibilityIcon?: { on: React.ReactNode; off: React.ReactNode }
|
||||
}
|
||||
|
||||
export interface PasswordInputProps
|
||||
extends InputProps,
|
||||
PasswordVisibilityProps {
|
||||
rootProps?: GroupProps
|
||||
}
|
||||
|
||||
export const PasswordInput = React.forwardRef<
|
||||
HTMLInputElement,
|
||||
PasswordInputProps
|
||||
>(function PasswordInput(props, ref) {
|
||||
const {
|
||||
rootProps,
|
||||
defaultVisible,
|
||||
visible: visibleProp,
|
||||
onVisibleChange,
|
||||
visibilityIcon = { on: <LuEye />, off: <LuEyeOff /> },
|
||||
...rest
|
||||
} = props
|
||||
|
||||
const [visible, setVisible] = useControllableState({
|
||||
value: visibleProp,
|
||||
defaultValue: defaultVisible || false,
|
||||
onChange: onVisibleChange,
|
||||
})
|
||||
|
||||
const inputRef = React.useRef<HTMLInputElement>(null)
|
||||
|
||||
return (
|
||||
<InputGroup
|
||||
endElement={
|
||||
<VisibilityTrigger
|
||||
disabled={rest.disabled}
|
||||
onPointerDown={(e) => {
|
||||
if (rest.disabled) return
|
||||
if (e.button !== 0) return
|
||||
e.preventDefault()
|
||||
setVisible(!visible)
|
||||
}}
|
||||
>
|
||||
{visible ? visibilityIcon.off : visibilityIcon.on}
|
||||
</VisibilityTrigger>
|
||||
}
|
||||
{...rootProps}
|
||||
>
|
||||
<Input
|
||||
{...rest}
|
||||
ref={mergeRefs(ref, inputRef)}
|
||||
type={visible ? "text" : "password"}
|
||||
/>
|
||||
</InputGroup>
|
||||
)
|
||||
})
|
||||
|
||||
const VisibilityTrigger = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
function VisibilityTrigger(props, ref) {
|
||||
return (
|
||||
<IconButton
|
||||
tabIndex={-1}
|
||||
ref={ref}
|
||||
me="-2"
|
||||
aspectRatio="square"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
height="calc(100% - {spacing.2})"
|
||||
aria-label="Toggle password visibility"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
interface PasswordStrengthMeterProps extends StackProps {
|
||||
max?: number
|
||||
value: number
|
||||
}
|
||||
|
||||
export const PasswordStrengthMeter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
PasswordStrengthMeterProps
|
||||
>(function PasswordStrengthMeter(props, ref) {
|
||||
const { max = 4, value, ...rest } = props
|
||||
|
||||
const percent = (value / max) * 100
|
||||
const { label, colorPalette } = getColorPalette(percent)
|
||||
|
||||
return (
|
||||
<Stack align="flex-end" gap="1" ref={ref} {...rest}>
|
||||
<HStack width="full" {...rest}>
|
||||
{Array.from({ length: max }).map((_, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
height="1"
|
||||
flex="1"
|
||||
rounded="sm"
|
||||
data-selected={index < value ? "" : undefined}
|
||||
layerStyle="fill.subtle"
|
||||
colorPalette="gray"
|
||||
_selected={{
|
||||
colorPalette,
|
||||
layerStyle: "fill.solid",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</HStack>
|
||||
{label && <HStack textStyle="xs">{label}</HStack>}
|
||||
</Stack>
|
||||
)
|
||||
})
|
||||
|
||||
function getColorPalette(percent: number) {
|
||||
switch (true) {
|
||||
case percent < 33:
|
||||
return { label: "Low", colorPalette: "red" }
|
||||
case percent < 66:
|
||||
return { label: "Medium", colorPalette: "orange" }
|
||||
default:
|
||||
return { label: "High", colorPalette: "green" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module '*.css' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
.height_40 {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.height_40_center {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.display_inline_block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.text_center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.width_300 {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.width_400 {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.margin_3_0 {
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.margin_10_20 {
|
||||
margin: 10px 20px;
|
||||
}
|
||||
|
||||
.margin_10_0 {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.margin_0_20 {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.margin_0_10 {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.margin_10_10 {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.margin_20_10 {
|
||||
margin: 20px 10px;
|
||||
}
|
||||
|
||||
.margin_0_5 {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.width_600 {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.margin_left_600 {
|
||||
margin-left: 600px;
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import './index.css'
|
||||
import 'antd/dist/antd.css';
|
||||
import App from './App.tsx'
|
||||
|
||||
import './global.css'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
|
||||
+25
-5
@@ -1,7 +1,27 @@
|
||||
// {
|
||||
// "files": [],
|
||||
// "references": [
|
||||
// { "path": "./tsconfig.app.json" },
|
||||
// { "path": "./tsconfig.node.json" }
|
||||
// ]
|
||||
// }
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -10,4 +11,9 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'), // 实际上使用了,但可能没被检测到
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user