feat(home): 角色增加性别筛选

This commit is contained in:
liuyonghe0111 2025-12-24 19:29:59 +08:00
parent f477cc95f8
commit 8b933d26d6
23 changed files with 567 additions and 238 deletions

View File

@ -3,5 +3,7 @@
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
"printWidth": 100,
"arrowParens": "always",
"endOfLine": "lf"
}

View File

@ -27,6 +27,7 @@
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.7",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-separator": "^1.1.7",
@ -48,6 +49,7 @@
"embla-carousel-react": "^8.6.0",
"js-cookie": "^3.0.5",
"lamejs": "^1.2.1",
"lodash": "^4.17.21",
"lucide-react": "^0.525.0",
"next": "16.0.8",
"next-intl": "^4.6.1",
@ -71,6 +73,7 @@
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.17.21",
"@types/node": "^20",
"@types/numeral": "^2.0.5",
"@types/qs": "^6.14.0",

View File

@ -50,6 +50,9 @@ importers:
'@radix-ui/react-label':
specifier: ^2.1.7
version: 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-popover':
specifier: ^1.1.15
version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-radio-group':
specifier: ^1.3.7
version: 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
@ -80,6 +83,9 @@ importers:
'@types/crypto-js':
specifier: ^4.2.2
version: 4.2.2
'@types/lodash':
specifier: ^4.17.21
version: 4.17.21
'@types/react-stickynode':
specifier: ^4.0.3
version: 4.0.3
@ -113,6 +119,9 @@ importers:
lamejs:
specifier: ^1.2.1
version: 1.2.1
lodash:
specifier: ^4.17.21
version: 4.17.21
lucide-react:
specifier: ^0.525.0
version: 0.525.0(react@19.2.1)
@ -1209,6 +1218,9 @@ packages:
'@radix-ui/primitive@1.1.2':
resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
'@radix-ui/primitive@1.1.3':
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
'@radix-ui/react-alert-dialog@1.1.14':
resolution: {integrity: sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==}
peerDependencies:
@ -1340,6 +1352,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-dismissable-layer@1.1.11':
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-dropdown-menu@2.1.15':
resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==}
peerDependencies:
@ -1362,6 +1387,15 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-focus-guards@1.1.3':
resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-focus-scope@1.1.7':
resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
peerDependencies:
@ -1410,6 +1444,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-popover@1.1.15':
resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-popper@1.2.7':
resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==}
peerDependencies:
@ -1423,6 +1470,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-popper@1.2.8':
resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-portal@1.1.9':
resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
peerDependencies:
@ -1449,6 +1509,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-presence@1.1.5':
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-primitive@2.1.3':
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
peerDependencies:
@ -2103,6 +2176,9 @@ packages:
'@types/jsonwebtoken@9.0.10':
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
'@types/lodash@4.17.21':
resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==}
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
@ -5757,6 +5833,8 @@ snapshots:
'@radix-ui/primitive@1.1.2': {}
'@radix-ui/primitive@1.1.3': {}
'@radix-ui/react-alert-dialog@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/primitive': 1.1.2
@ -5883,6 +5961,19 @@ snapshots:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.7)(react@19.2.1)
react: 19.2.1
react-dom: 19.2.1(react@19.2.1)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/primitive': 1.1.2
@ -5904,6 +5995,12 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.7
'@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.7)(react@19.2.1)':
dependencies:
react: 19.2.1
optionalDependencies:
'@types/react': 19.2.7
'@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
@ -5957,6 +6054,29 @@ snapshots:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.1)
aria-hidden: 1.2.6
react: 19.2.1
react-dom: 19.2.1(react@19.2.1)
react-remove-scroll: 2.7.1(@types/react@19.2.7)(react@19.2.1)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-popper@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@floating-ui/react-dom': 2.1.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
@ -5975,6 +6095,24 @@ snapshots:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@floating-ui/react-dom': 2.1.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/rect': 1.1.1
react: 19.2.1
react-dom: 19.2.1(react@19.2.1)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
@ -5995,6 +6133,16 @@ snapshots:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
react: 19.2.1
react-dom: 19.2.1(react@19.2.1)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
dependencies:
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.1)
@ -6725,6 +6873,8 @@ snapshots:
'@types/ms': 2.1.0
'@types/node': 20.19.8
'@types/lodash@4.17.21': {}
'@types/ms@2.1.0': {}
'@types/node@20.19.8':

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,20 +1,20 @@
'use client'
'use client';
import { useParams } from 'next/navigation'
import { useGetAIUserBaseInfo } from '@/hooks/aiUser'
import Image from 'next/image'
import { useParams } from 'next/navigation';
import { useGetAIUserBaseInfo } from '@/hooks/aiUser';
import Image from 'next/image';
// 按钮组件
interface MobileButtonProps {
showIcon?: boolean
icon?: React.ReactNode | null
btnTxt?: string
showTxt?: boolean
size?: 'Large' | 'Medium' | 'Small'
variant?: 'Contrast' | 'Basic' | 'Ghost'
type?: 'Primary' | 'Secondary' | 'Tertiary' | 'Destructive' | 'VIP'
state?: 'Default' | 'Disabled' | 'Pressed'
onClick?: () => void
showIcon?: boolean;
icon?: React.ReactNode | null;
btnTxt?: string;
showTxt?: boolean;
size?: 'Large' | 'Medium' | 'Small';
variant?: 'Contrast' | 'Basic' | 'Ghost';
type?: 'Primary' | 'Secondary' | 'Tertiary' | 'Destructive' | 'VIP';
state?: 'Default' | 'Disabled' | 'Pressed';
onClick?: () => void;
}
function MobileButton({
@ -53,7 +53,7 @@ function MobileButton({
</div>
)}
</button>
)
);
}
return (
@ -64,22 +64,22 @@ function MobileButton({
{showIcon && icon}
{showTxt && <span className="text-[12px] font-medium text-white">{btnTxt}</span>}
</button>
)
);
}
const SharePage = () => {
const { userId } = useParams()
const { userId } = useParams();
const { data: userInfo } = useGetAIUserBaseInfo({
aiId: userId ? Number(userId) : 0,
})
});
const { homeImageUrl } = userInfo || {}
const { homeImageUrl } = userInfo || {};
const handleChatClick = () => {
// 跳转到应用或下载页面
window.open('https://crushlevel.com/download', '_blank')
}
window.open('https://crushlevel.com/download', '_blank');
};
return (
<div className="relative mx-auto h-screen max-w-[750px] overflow-hidden">
@ -140,7 +140,7 @@ const SharePage = () => {
<div className="relative inline-grid shrink-0 grid-cols-[max-content] grid-rows-[max-content] place-items-start px-6 leading-[0]">
{/* AI信息卡片 */}
<div className="relative box-border flex w-full shrink-0 flex-col content-stretch items-start justify-start gap-1 px-0 py-2">
<div className="relative box-border flex w-full shrink-0 flex-col content-stretch items-start justify-start gap-2 rounded-[16px] border border-[rgba(251,222,255,0.2)] bg-[rgba(251,222,255,0.08)] p-[16px] backdrop-blur-[32px] backdrop-filter">
<div className="relative box-border flex w-full shrink-0 flex-col content-stretch items-start justify-start gap-2 rounded-2xl border border-[rgba(251,222,255,0.2)] bg-[rgba(251,222,255,0.08)] p-[16px] backdrop-blur-[32px] backdrop-filter">
{/* 介绍文本 */}
<div className="font-regular relative line-clamp-3 w-full shrink-0 overflow-hidden text-[14px] leading-[20px] text-white">
<p>
@ -197,7 +197,7 @@ const SharePage = () => {
{/* 示例对话消息 */}
<div className="relative box-border flex w-full shrink-0 content-stretch items-start justify-start gap-4 pt-4 pr-20 pb-2 pl-0">
<div className="relative box-border flex min-h-px min-w-px shrink-0 grow basis-0 content-stretch items-start justify-start gap-2.5 rounded-[16px] bg-[rgba(0,0,0,0.65)] px-4 pt-5 pb-4 backdrop-blur-[32px] backdrop-filter">
<div className="relative box-border flex min-h-px min-w-px shrink-0 grow basis-0 content-stretch items-start justify-start gap-2.5 rounded-2xl bg-[rgba(0,0,0,0.65)] px-4 pt-5 pb-4 backdrop-blur-[32px] backdrop-filter">
{/* 语音标签 */}
<div className="absolute top-[-12px] left-0 box-border flex content-stretch items-center justify-center gap-2 overflow-clip rounded-tl-[8px] rounded-tr-[8px] rounded-br-[8px] bg-[#484151] px-3 py-1">
<div className="relative size-3 shrink-0">
@ -224,7 +224,7 @@ const SharePage = () => {
{/* 底部品牌区域 */}
<div className="relative box-border flex w-full shrink-0 content-stretch items-start justify-start gap-1 overflow-clip px-2 py-4">
<div className="relative box-border flex min-h-px min-w-px shrink-0 grow basis-0 content-stretch items-center justify-start gap-3 rounded-[16px] bg-gradient-to-r from-[#f264a4] to-[#c241e6] px-4 py-2">
<div className="relative box-border flex min-h-px min-w-px shrink-0 grow basis-0 content-stretch items-center justify-start gap-3 rounded-2xl bg-gradient-to-r from-[#f264a4] to-[#c241e6] px-4 py-2">
{/* App图标 */}
<div className="relative size-[24px] shrink-0 overflow-clip rounded-[12px]">
<Image
@ -265,7 +265,7 @@ const SharePage = () => {
</div>
</div>
</div>
)
}
);
};
export default SharePage
export default SharePage;

View File

@ -20,7 +20,7 @@ export function CheckInGrid() {
{Array.from({ length: 6 }).map((_, index) => (
<div
key={index}
className="animate-pulse rounded-[16px] border border-[rgba(251,222,255,0.2)] bg-[#282233]"
className="animate-pulse rounded-2xl border border-[rgba(251,222,255,0.2)] bg-[#282233]"
/>
))}
</div>

View File

@ -7,20 +7,20 @@ import { useHomeStore } from '../../store';
import { useEffect } from 'react';
const Character = () => {
const selectedTags = useHomeStore((state) => state.selectedTags);
const characterParams = useHomeStore((state) => state.characterParams);
const { dataSource, isFirstLoading, isLoadingMore, noMoreData, onLoadMore, onSearch } =
useSmartInfiniteQuery<any, any>(fetchCharacters, {
queryKey: 'characters',
defaultQuery: { tagIds: selectedTags },
defaultQuery: characterParams,
});
useEffect(() => {
onSearch({ tagIds: selectedTags });
}, [selectedTags]);
onSearch(characterParams);
}, [characterParams]);
return (
<div className="mt-8">
<div className="mt-4 sm:mt-8">
<InfiniteScrollList<any>
items={dataSource}
enableLazyRender

View File

@ -1,116 +0,0 @@
'use client';
import { cn } from '@/lib/utils';
import Image from 'next/image';
import { Chip } from '@/components/ui/chip';
import { useHomeStore } from '../store';
import { useQuery } from '@tanstack/react-query';
import { fetchCharacterTags } from '@/services/editor';
import { useRef } from 'react';
import { useTranslations } from 'next-intl';
const Filter = () => {
const tab = useHomeStore((state) => state.tab);
const setTab = useHomeStore((state) => state.setTab);
const ref = useRef<HTMLDivElement>(null);
const selectedTags = useHomeStore((state) => state.selectedTags);
const setSelectedTags = useHomeStore((state) => state.setSelectedTags);
const t = useTranslations('home');
// useEffect(() => {
// const mainContent = document.getElementById('main-content');
// if (!mainContent) {
// return;
// }
// const handleScroll = () => {
// const scrollTop = mainContent.scrollTop;
// console.log('scrollTop', scrollTop, ref.current);
// const className = 'absolute bg-bg-primary-normal';
// if (scrollTop > 248) {
// ref.current?.classList.add('absolute bg-bg-primary-normal');
// } else {
// }
// };
// mainContent?.addEventListener('scroll', handleScroll);
// return () => {
// mainContent?.removeEventListener('scroll', handleScroll);
// };
// }, []);
const { data: tags = [] } = useQuery({
queryKey: ['tags', tab],
queryFn: async () => {
if (tab === 'character') {
const { data } = await fetchCharacterTags({ limit: 10 });
return data.rows;
}
return [];
},
});
const tabs = [
{
label: t('story'),
value: 'story',
icon: 'icon-story',
activeIcon: 'icon-story-active',
},
{
label: t('character'),
value: 'character',
icon: 'icon-character',
activeIcon: 'icon-character-active',
},
] as const;
const handleSelect = (tagId: string) => {
if (selectedTags.includes(tagId)) {
setSelectedTags(selectedTags.filter((id) => id !== tagId));
} else {
setSelectedTags([...selectedTags, tagId]);
}
};
return (
<div ref={ref}>
<div className="flex mb-6 gap-12">
{tabs.map((item) => {
const active = tab === item.value;
return (
<div
key={item.value}
onClick={() => setTab(item.value)}
className={cn(
'flex items-center cursor-pointer gap-2',
active ? 'text-txt-primary-normal' : 'text-txt-secondary-normal'
)}
>
<Image
src={`/images/home/${active ? item.activeIcon : item.icon}.png`}
alt={item.label}
width={28}
height={28}
/>
<span className="txt-headline-s">{item.label}</span>
</div>
);
})}
</div>
<div className="flex flex-wrap gap-2">
{tags?.map((tag: any) => (
<Chip
key={tag.id}
size="small"
className="px-4"
state={selectedTags.includes(tag.id) ? 'active' : 'inactive'}
onClick={() => handleSelect(tag.id)}
>
# {tag.name}
</Chip>
))}
</div>
</div>
);
};
export default Filter;

View File

@ -0,0 +1,68 @@
'use client';
import { cn } from '@/lib/utils';
import { useHomeStore } from '../../store';
import React, { useMemo } from 'react';
import { useTranslations } from 'next-intl';
import IconFont from '@/components/ui/iconFont';
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popver';
import omit from 'lodash/omit';
import { TagListSelect } from './TagListSelect';
export const MoreFilter = React.memo(() => {
const characterParams = useHomeStore((state) => state.characterParams);
const setCharacterParams = useHomeStore((state) => state.setCharacterParams);
const t = useTranslations('common');
// 计算筛选条件数量
const queryKeyNum = useMemo(() => {
const currentParams = omit(characterParams, ['tagIds']);
return Object.values(currentParams).reduce((acc, curr) => {
const count = Array.isArray(curr) ? curr.length : 1;
return acc + count;
}, 0);
}, [characterParams]);
const genderOptions = [
{
label: t('gender_0'),
value: 0,
},
{
label: t('gender_1'),
value: 1,
},
{
label: t('gender_2'),
value: 2,
},
];
return (
<Popover>
<PopoverTrigger asChild>
<button
className={cn(
'flex cursor-pointer items-center gap-1 hover:text-white/80 transition-colors',
queryKeyNum > 0 ? 'text-white' : 'text-white/60'
)}
>
<IconFont type="icon-filter-fill" />
<span className="txt-bodySemibold-m">
{t('filter')} {queryKeyNum > 0 ? `(${queryKeyNum})` : ''}
</span>
</button>
</PopoverTrigger>
<PopoverContent className="w-70 p-4" align="end" sideOffset={8}>
<div className="text-white txt-title-s mb-2">{t('gender')}</div>
<TagListSelect
options={genderOptions}
value={characterParams.genders || []}
onChange={(values) => setCharacterParams({ genders: values as number[] })}
/>
</PopoverContent>
</Popover>
);
});
MoreFilter.displayName = 'MoreFilter';

View File

@ -0,0 +1,41 @@
'use client';
import { cn } from '@/lib/utils';
import { Chip } from '@/components/ui/chip';
import React from 'react';
type TagListSelectProps = {
options: { label: string; value: string | number }[];
value: (string | number)[];
onChange: (values: (string | number)[]) => void;
chipProps?: React.ComponentProps<typeof Chip>;
} & Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>;
export const TagListSelect = (props: TagListSelectProps) => {
const { options, value = [], onChange, chipProps, ...rest } = props;
const handleSelect = (id: string | number) => {
if (value.includes(id)) {
onChange(value.filter((v: string | number) => v !== id));
} else {
onChange([...value, id]);
}
};
return (
<div {...rest} className={cn('flex flex-wrap gap-2', rest.className)}>
{options.map((option) => (
<Chip
key={option.value}
size="small"
{...chipProps}
className={cn('px-4', chipProps?.className)}
state={value.includes(option.value) ? 'active' : 'inactive'}
onClick={() => handleSelect(option.value)}
>
{option.label}
</Chip>
))}
</div>
);
};

View File

@ -0,0 +1,85 @@
'use client';
import { cn } from '@/lib/utils';
import Image from 'next/image';
import { useHomeStore } from '../../store';
import { useQuery } from '@tanstack/react-query';
import { fetchCharacterTags } from '@/services/editor';
import { useRef } from 'react';
import { useTranslations } from 'next-intl';
import { TagListSelect } from './TagListSelect';
import { MoreFilter } from './MoreFilter';
const Filter = () => {
const tab = useHomeStore((state) => state.tab);
const setTab = useHomeStore((state) => state.setTab);
const ref = useRef<HTMLDivElement>(null);
const characterParams = useHomeStore((state) => state.characterParams);
const setCharacterParams = useHomeStore((state) => state.setCharacterParams);
const t = useTranslations('home');
const { data: tags = [] } = useQuery({
queryKey: ['tags', tab],
queryFn: async () => {
if (tab === 'character') {
const { data } = await fetchCharacterTags({ limit: 10 });
return data.rows;
}
return [];
},
});
const tabs = [
{
label: t('story'),
value: 'story',
icon: 'icon-story',
activeIcon: 'icon-story-active',
},
{
label: t('character'),
value: 'character',
icon: 'icon-character',
activeIcon: 'icon-character-active',
},
] as const;
return (
<div ref={ref}>
<div className="flex justify-between items-center mb-3">
<div className="flex items-center sm:mb-6 gap-5 sm:gap-12">
{tabs.map((item) => {
const active = tab === item.value;
return (
<div
key={item.value}
onClick={() => setTab(item.value)}
className={cn(
'flex items-center cursor-pointer gap-1 sm:gap-2',
active ? 'text-txt-primary-normal' : 'text-txt-secondary-normal'
)}
>
<Image
src={`/images/home/${active ? item.activeIcon : item.icon}.png`}
alt={item.label}
width={28}
height={28}
className="w-5 h-5 sm:w-7 sm:h-7"
/>
<span className="txt-title-m sm:txt-title-l">{item.label}</span>
</div>
);
})}
</div>
<MoreFilter />
</div>
<TagListSelect
options={tags.map((tag: any) => ({ label: tag.name, value: tag.id }))}
value={characterParams.tagIds || []}
onChange={(values) => setCharacterParams({ tagIds: values as string[] })}
/>
</div>
);
};
export default Filter;

View File

@ -6,10 +6,14 @@ import Link from 'next/link';
import React from 'react';
import { useLayoutStore } from '@/stores';
import { useTranslations } from 'next-intl';
import { useSignIn } from '@/hooks/services/signin';
const Header = React.memo(() => {
const response = useLayoutStore((s) => s.response);
const t = useTranslations('home');
const { isTodaySigned, signInListData } = useSignIn();
if (!signInListData?.list?.length || isTodaySigned) return <div className="h-2"></div>;
return (
<Link href="/crushcoin">

View File

@ -1,13 +1,12 @@
'use client';
import { useState } from 'react';
import React, { useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { cn } from '@/lib/utils';
import { useTranslations } from 'next-intl';
import IconFont from '@/components/ui/iconFont';
const HomePageFooter = () => {
const HomePageFooter = React.memo(() => {
const t = useTranslations('footer');
const [isExpanded, setIsExpanded] = useState(false);
@ -33,7 +32,13 @@ const HomePageFooter = () => {
{/* Logo 和 Slogan */}
<div className="col-span-1">
<div className="mb-4 flex items-center gap-2">
<IconFont type="icon-Logo" size={160} />
<Image
src="/logo.svg"
alt="logo"
width={160}
height={160}
className="cursor-pointer"
/>
</div>
<p className="txt-body-m text-txt-secondary-normal">{t('slogan')}</p>
</div>
@ -123,6 +128,6 @@ const HomePageFooter = () => {
</div>
</footer>
);
};
});
export default HomePageFooter;

View File

@ -1,15 +1,26 @@
import { create } from 'zustand';
type CharacterParams = {
genders?: number[];
tagIds?: string[];
};
interface HomeStore {
tab: 'story' | 'character';
selectedTags: string[];
characterParams: CharacterParams;
setTab: (tab: 'story' | 'character') => void;
setSelectedTags: (selectedTags: string[]) => void;
setCharacterParams: (params: Partial<CharacterParams>) => void;
}
export const useHomeStore = create<HomeStore>((set) => ({
export const useHomeStore = create<HomeStore>((set, get) => ({
tab: 'character',
characterParams: {
genders: [],
tagIds: [],
},
setTab: (tab: 'story' | 'character') => set({ tab }),
selectedTags: [],
setSelectedTags: (selectedTags: string[]) => set({ selectedTags }),
setCharacterParams: (params) => {
const { characterParams } = get();
set({ characterParams: { ...characterParams, ...params } });
},
}));

View File

@ -1,22 +1,22 @@
'use client'
'use client';
import { useState, useEffect, useRef, useCallback } from 'react'
import { IconButton } from '@/components/ui/button'
import { useGetMemberDetail } from '@/hooks/useWallet'
import { VipType } from '@/services/wallet'
import { useState, useEffect, useRef, useCallback } from 'react';
import { IconButton } from '@/components/ui/button';
import { useGetMemberDetail } from '@/hooks/useWallet';
import { VipType } from '@/services/wallet';
const AUTO_PLAY_INTERVAL = 4000 // 自动轮播间隔 4 秒
const AUTO_PLAY_INTERVAL = 4000; // 自动轮播间隔 4 秒
const FADE_DURATION = 400 // 渐变动画时长(毫秒)
const FADE_DURATION = 400; // 渐变动画时长(毫秒)
const CarouselBanner = ({ vipType, visible }: { vipType?: VipType; visible: boolean }) => {
const [currentIndex, setCurrentIndex] = useState(0)
const [isPaused, setIsPaused] = useState(false) // 鼠标悬停时暂停轮播
const [isFading, setIsFading] = useState(false) // 控制淡入淡出动画
const { data: memberDetail, isLoading } = useGetMemberDetail()
const { memberPrivList } = memberDetail || {}
const autoPlayTimerRef = useRef<NodeJS.Timeout | null>(null)
const pendingIndexRef = useRef<number | null>(null)
const [currentIndex, setCurrentIndex] = useState(0);
const [isPaused, setIsPaused] = useState(false); // 鼠标悬停时暂停轮播
const [isFading, setIsFading] = useState(false); // 控制淡入淡出动画
const { data: memberDetail, isLoading } = useGetMemberDetail();
const { memberPrivList } = memberDetail || {};
const autoPlayTimerRef = useRef<NodeJS.Timeout | null>(null);
const pendingIndexRef = useRef<number | null>(null);
// 将 memberPrivList 转换为轮播数据格式
const carouselData =
@ -25,109 +25,109 @@ const CarouselBanner = ({ vipType, visible }: { vipType?: VipType; visible: bool
title: item.title || 'VIP Feature',
description: item.desc || 'Enjoy exclusive VIP benefits',
backgroundImage: item.img || '/images/vip/drawer-bg.png',
})) || []
})) || [];
// 清除定时器
const clearAutoPlayTimer = useCallback(() => {
if (autoPlayTimerRef.current) {
clearInterval(autoPlayTimerRef.current)
autoPlayTimerRef.current = null
clearInterval(autoPlayTimerRef.current);
autoPlayTimerRef.current = null;
}
}, [])
}, []);
// 启动自动轮播定时器
const startAutoPlayTimer = useCallback(() => {
clearAutoPlayTimer()
clearAutoPlayTimer();
if (visible && carouselData.length > 1 && !isPaused) {
autoPlayTimerRef.current = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % carouselData.length)
}, AUTO_PLAY_INTERVAL)
setCurrentIndex((prev) => (prev + 1) % carouselData.length);
}, AUTO_PLAY_INTERVAL);
}
}, [visible, carouselData.length, isPaused, clearAutoPlayTimer])
}, [visible, carouselData.length, isPaused, clearAutoPlayTimer]);
// 鼠标悬停时暂停轮播
const handleMouseEnter = () => {
setIsPaused(true)
clearAutoPlayTimer()
}
setIsPaused(true);
clearAutoPlayTimer();
};
// 鼠标离开时恢复轮播
const handleMouseLeave = () => {
setIsPaused(false)
}
setIsPaused(false);
};
// 带渐变动画的切换
const changeSlideWithFade = useCallback(
(newIndex: number) => {
if (isFading) return // 防止动画过程中重复触发
setIsFading(true)
pendingIndexRef.current = newIndex
if (isFading) return; // 防止动画过程中重复触发
setIsFading(true);
pendingIndexRef.current = newIndex;
setTimeout(() => {
setCurrentIndex(newIndex)
setIsFading(false)
}, FADE_DURATION)
setCurrentIndex(newIndex);
setIsFading(false);
}, FADE_DURATION);
},
[isFading]
)
);
const nextSlide = () => {
if (carouselData.length > 0 && !isFading) {
const newIndex = (currentIndex + 1) % carouselData.length
changeSlideWithFade(newIndex)
startAutoPlayTimer() // 手动切换后重置定时器
const newIndex = (currentIndex + 1) % carouselData.length;
changeSlideWithFade(newIndex);
startAutoPlayTimer(); // 手动切换后重置定时器
}
}
};
const prevSlide = () => {
if (carouselData.length > 0 && !isFading) {
const newIndex = (currentIndex - 1 + carouselData.length) % carouselData.length
changeSlideWithFade(newIndex)
startAutoPlayTimer() // 手动切换后重置定时器
const newIndex = (currentIndex - 1 + carouselData.length) % carouselData.length;
changeSlideWithFade(newIndex);
startAutoPlayTimer(); // 手动切换后重置定时器
}
}
};
// 自动轮播(带渐变动画)
useEffect(() => {
if (visible && carouselData.length > 1 && !isPaused) {
autoPlayTimerRef.current = setInterval(() => {
setIsFading(true)
setIsFading(true);
setTimeout(() => {
setCurrentIndex((prev) => (prev + 1) % carouselData.length)
setIsFading(false)
}, FADE_DURATION)
}, AUTO_PLAY_INTERVAL)
setCurrentIndex((prev) => (prev + 1) % carouselData.length);
setIsFading(false);
}, FADE_DURATION);
}, AUTO_PLAY_INTERVAL);
}
return () => {
clearAutoPlayTimer()
}
}, [visible, carouselData.length, isPaused, clearAutoPlayTimer])
clearAutoPlayTimer();
};
}, [visible, carouselData.length, isPaused, clearAutoPlayTimer]);
useEffect(() => {
if (!memberPrivList) {
return
return;
}
if (visible) {
if (vipType) {
const targetIndex = memberPrivList?.findIndex((item) => item.code === vipType) ?? -1
const targetIndex = memberPrivList?.findIndex((item) => item.code === vipType) ?? -1;
if (targetIndex >= 0) {
setCurrentIndex(targetIndex)
setCurrentIndex(targetIndex);
} else {
setCurrentIndex(0)
setCurrentIndex(0);
}
}
}
}, [visible, memberPrivList])
}, [visible, memberPrivList]);
const currentSlide = carouselData[currentIndex]
const currentSlide = carouselData[currentIndex];
// 加载状态
if (isLoading) {
return (
<div className="relative flex w-full flex-col content-stretch items-start justify-start gap-4 px-6">
{/* 轮播图片容器 - 骨架屏 */}
<div className="relative flex h-[234px] w-full shrink-0 flex-col content-stretch items-start justify-start gap-1 rounded-[16px] backdrop-blur-md backdrop-filter">
<div className="relative h-[235px] w-full shrink-0 animate-pulse rounded-[16px] bg-[#2a2a2a]" />
<div className="relative flex h-[234px] w-full shrink-0 flex-col content-stretch items-start justify-start gap-1 rounded-2xl backdrop-blur-md backdrop-filter">
<div className="relative h-[235px] w-full shrink-0 animate-pulse rounded-2xl bg-[#2a2a2a]" />
</div>
{/* 轮播内容信息 - 骨架屏 */}
@ -136,7 +136,7 @@ const CarouselBanner = ({ vipType, visible }: { vipType?: VipType; visible: bool
<div className="h-4 w-3/4 animate-pulse rounded bg-[#2a2a2a]" />
</div>
</div>
)
);
}
return (
@ -146,7 +146,7 @@ const CarouselBanner = ({ vipType, visible }: { vipType?: VipType; visible: bool
onMouseLeave={handleMouseLeave}
>
{/* 轮播图片容器 */}
<div className="relative flex h-[234px] w-full shrink-0 flex-col content-stretch items-start justify-start gap-1 rounded-[16px]">
<div className="relative flex h-[234px] w-full shrink-0 flex-col content-stretch items-start justify-start gap-1 rounded-2xl">
{/* 背景图片 */}
<div className="relative h-[235px] w-full shrink-0 overflow-hidden rounded-lg bg-cover bg-center bg-no-repeat backdrop-blur-[12px] transition-all duration-500 ease-in-out">
<div className="bg-background-district absolute inset-0 transition-all duration-500 ease-in-out" />
@ -194,7 +194,7 @@ const CarouselBanner = ({ vipType, visible }: { vipType?: VipType; visible: bool
</div>
</div>
</div>
)
}
);
};
export default CarouselBanner
export default CarouselBanner;

View File

@ -50,7 +50,7 @@ export default async function RootLayout({
className={`${poppins.variable} ${oleoScriptSwashCaps.variable} ${NumDisplay.variable} antialiased`}
>
<Script
src="//at.alicdn.com/t/c/font_5076160_m6catzpb7dc.js"
src="//at.alicdn.com/t/c/font_5076160_y1o1u6aqyb.js"
strategy="afterInteractive"
async
/>

View File

@ -49,7 +49,7 @@ const AIStandardCard: React.FC<AIStandardCardProps> = React.memo(
className={`relative flex shrink-0 grow basis-0 cursor-pointer flex-col content-stretch items-start justify-start gap-3 ${disableHover ? '' : 'group'}`}
>
<div
className="relative aspect-[3/4] w-full shrink-0 overflow-hidden rounded-[16px] bg-cover bg-top bg-no-repeat transition-transform"
className="relative aspect-[3/4] w-full shrink-0 overflow-hidden rounded-2xl bg-cover bg-top bg-no-repeat transition-transform"
style={{ backgroundImage: displayImage ? `url('${displayImage}')` : undefined }}
>
<div className="relative box-border flex aspect-[3/4] size-full flex-col content-stretch items-end justify-end overflow-clip p-[12px]">
@ -143,7 +143,7 @@ const AIStandardCard: React.FC<AIStandardCardProps> = React.memo(
{/* 边框 */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 rounded-[16px] border border-solid border-[rgba(251,222,255,0.2)]"
className="pointer-events-none absolute inset-0 rounded-2xl border border-solid border-[rgba(251,222,255,0.2)]"
/>
</div>
</div>

View File

@ -0,0 +1,56 @@
'use client';
import * as React from 'react';
import * as PopoverPrimitive from '@radix-ui/react-popover';
import { cn } from '@/lib/utils';
function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root {...props} />;
}
function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger {...props} />;
}
function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor {...props} />;
}
function PopoverContent({
className,
align = 'center',
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
align={align}
sideOffset={sideOffset}
className={cn(
'bg-surface-float-normal z-50 w-72 rounded-lg p-4 outline-none animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
);
}
function PopoverArrow({
className,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Arrow>) {
return (
<PopoverPrimitive.Arrow
className={cn('bg-surface-float-normal fill-surface-float-normal', className)}
{...props}
/>
);
}
function PopoverClose({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Close>) {
return <PopoverPrimitive.Close {...props} />;
}
export { Popover, PopoverTrigger, PopoverAnchor, PopoverContent, PopoverArrow, PopoverClose };

View File

@ -3,6 +3,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCurrentUser } from '../auth';
import { toast } from 'sonner';
import { useTranslations } from 'next-intl';
import { useMemo } from 'react';
export function useSignIn() {
const { data: user } = useCurrentUser();
@ -24,8 +25,16 @@ export function useSignIn() {
},
});
const isTodaySigned = useMemo(() => {
if (!signInListData?.list?.length) return false;
const today = new Date();
const todayStr = today.toISOString().split('T')[0];
return signInListData.list.find((item) => item.dayStr === todayStr)?.signIn;
}, [signInListData]);
return {
signInListData,
isTodaySigned,
fetchSignInListLoading,
signInLoading,
handleSignIn,

View File

@ -5,9 +5,11 @@ export default {
edit: 'Edit',
select: 'Select',
default: 'Default',
gender_0: 'Male',
gender_1: 'Female',
gender_2: 'Other',
gender: 'Gender',
gender_1: 'Male',
gender_2: 'Female',
gender_0: 'Other',
filter: 'Filter',
},
bottomBar: {
explore: 'Explore',

View File

@ -5,9 +5,11 @@ export default {
edit: '编辑',
select: '选择',
default: '默认',
gender: '性别',
gender_0: '男性',
gender_1: '女性',
gender_2: '其他',
filter: '筛选',
},
bottomBar: {
explore: '首页',

View File

@ -10,8 +10,8 @@ import Notice from './components/Notice';
import LocaleSwitch from './components/LocaleSwitch';
import { items } from './BottomBar';
import { getTopbarConfig } from './config';
import IconFont from '@/components/ui/iconFont';
import { useLayoutStore } from '@/stores';
import Image from 'next/image';
function Topbar() {
const [isScrollBlur, setIsScrollBlur] = useState(false);
@ -56,10 +56,12 @@ function Topbar() {
if (response.sm || items.some((item) => item.path === pathname)) {
return (
<Link href="/">
<IconFont
size={response.sm ? 150 : 100}
className="text-white cursor-pointer"
type="icon-Logo"
<Image
src="/logo.svg"
alt="logo"
width={response.sm ? 150 : 100}
height={response.sm ? 150 : 100}
className="cursor-pointer"
/>
</Link>
);

View File

@ -14,4 +14,4 @@ export const GenderTextMap = {
[Gender.MALE]: GenderText.MALE,
[Gender.FEMALE]: GenderText.FEMALE,
[Gender.OTHER]: GenderText.OTHER,
}
};