diff --git a/apps/user/app/(main)/(content)/(user)/Header.tsx b/apps/user/app/(main)/(content)/(user)/Header.tsx index 16edee3..30de089 100644 --- a/apps/user/app/(main)/(content)/(user)/Header.tsx +++ b/apps/user/app/(main)/(content)/(user)/Header.tsx @@ -18,9 +18,8 @@ export function Header() { const pathname = usePathname(); const items = useMemo(() => findNavByUrl(pathname), [pathname]); return ( -
+
- {items.map((item, index) => { diff --git a/apps/user/app/(main)/(content)/(user)/dashboard/components/Announcement/Popup.tsx b/apps/user/app/(main)/(content)/(user)/dashboard/components/Announcement/Popup.tsx index 7ecf853..042cc0c 100644 --- a/apps/user/app/(main)/(content)/(user)/dashboard/components/Announcement/Popup.tsx +++ b/apps/user/app/(main)/(content)/(user)/dashboard/components/Announcement/Popup.tsx @@ -39,11 +39,11 @@ export const Popup = ({ ref }: PopupProps) => { return ( - + {data.title} -
+
{data.title}
{data.content}
diff --git a/apps/user/app/(main)/(content)/(user)/dashboard/content.tsx b/apps/user/app/(main)/(content)/(user)/dashboard/content.tsx index 0d1500f..95f453d 100644 --- a/apps/user/app/(main)/(content)/(user)/dashboard/content.tsx +++ b/apps/user/app/(main)/(content)/(user)/dashboard/content.tsx @@ -30,6 +30,7 @@ import { PopupRef, } from '@/app/(main)/(content)/(user)/dashboard/components/Announcement/Popup'; import { Empty } from '@/components/empty'; +import SvgIcon from '@/components/SvgIcon'; import { queryAnnouncement } from '@/services/user/announcement'; import { Popover, PopoverContent, PopoverTrigger } from '@workspace/airo-ui/components/popover'; import { @@ -46,6 +47,7 @@ import { import { Tabs, TabsList, TabsTrigger } from '@workspace/ui/components/tabs'; import { differenceInDays, formatDate } from '@workspace/ui/utils'; import { QRCodeCanvas } from 'qrcode.react'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; const platforms: (keyof API.ApplicationPlatform)[] = [ 'windows', @@ -351,12 +353,12 @@ export default function Content() { onValueChange={setProtocol} className='w-full max-w-full md:w-auto' > - + {['all', ...(data?.protocol || [])].map((item) => ( {item} @@ -399,20 +401,32 @@ export default function Content() { -
-
- {userSubscribeProtocolCurrent} +
+
+
+ {userSubscribeProtocolCurrent} +
+ { + if (result) { + toast.success(t('copySuccess')); + } + }} + > + + + +
-
+
+ +
diff --git a/apps/user/app/(main)/(content)/(user)/sidebar-left.tsx b/apps/user/app/(main)/(content)/(user)/sidebar-left.tsx index db6aa80..33d01a0 100644 --- a/apps/user/app/(main)/(content)/(user)/sidebar-left.tsx +++ b/apps/user/app/(main)/(content)/(user)/sidebar-left.tsx @@ -1,4 +1,5 @@ 'use client'; +import SvgIcon from '@/components/SvgIcon'; import { UserNav } from '@/components/user-nav'; import { navs } from '@/config/navs'; import { @@ -10,9 +11,8 @@ import { SidebarMenuItem, useSidebar, } from '@workspace/airo-ui/components/sidebar'; -import { Icon } from '@workspace/ui/custom-components/icon'; import { useTranslations } from 'next-intl'; -import Image from 'next/legacy/image'; +import Image from 'next/image'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; @@ -21,18 +21,21 @@ export function SidebarLeft({ ...props }: React.ComponentProps) const pathname = usePathname(); const { toggleSidebar } = useSidebar(); return ( - +
+
+ close +
-
+
)
- + {navs .filter((v) => !v.hidden) .map((nav, navIndex) => ( - - {nav.icon && } + +
+ +
{t(nav.title)}
@@ -73,7 +78,7 @@ export function SidebarLeft({ ...props }: React.ComponentProps)
- +
diff --git a/apps/user/app/(main)/(content)/(user)/wallet/page.tsx b/apps/user/app/(main)/(content)/(user)/wallet/page.tsx index ac18767..2fe6809 100644 --- a/apps/user/app/(main)/(content)/(user)/wallet/page.tsx +++ b/apps/user/app/(main)/(content)/(user)/wallet/page.tsx @@ -10,15 +10,16 @@ import { useRef } from 'react'; import { Empty } from '@/components/empty'; import Recharge from '@/components/subscribe/recharge'; +import SvgIcon from '@/components/SvgIcon'; import { Button } from '@workspace/ui/components/button'; import { formatDate } from '@workspace/ui/utils'; -import { Copy } from 'lucide-react'; import Link from 'next/link'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import { toast } from 'sonner'; export default function Page() { const t = useTranslations('wallet'); + const dashboardT = useTranslations('dashboard'); const { user } = useGlobalStore(); const ref = useRef(null); const totalAssets = (user?.balance || 0) + (user?.commission || 0) + (user?.gift_amount || 0); @@ -44,7 +45,7 @@ export default function Page() {
-
+

账户余额

@@ -80,12 +81,12 @@ export default function Page() { text={`${location?.origin}/?invite=${user?.refer_code}`} onCopy={(text, result) => { if (result) { - toast.success(t('copySuccess')); + toast.success(dashboardT('copySuccess')); } }} > -

diff --git a/apps/user/components/SvgIcon.tsx b/apps/user/components/SvgIcon.tsx new file mode 100644 index 0000000..bd1db95 --- /dev/null +++ b/apps/user/components/SvgIcon.tsx @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; + +const SvgIcon = ({ name, ...props }) => { + const [Icon, setIcon] = useState(null); + + useEffect(() => { + let isMounted = true; + + import(`public/svg-icon/${name}.svg`) + .then((module) => { + if (isMounted) { + setIcon(() => module.default); + } + }) + .catch(() => { + if (isMounted) { + setIcon(null); + } + }); + + return () => { + isMounted = false; + }; + }, [name]); + + if (!Icon) { + return null; + } + + return ; +}; + +export default SvgIcon; diff --git a/apps/user/components/affiliate/index.tsx b/apps/user/components/affiliate/index.tsx index 98141be..571f145 100644 --- a/apps/user/components/affiliate/index.tsx +++ b/apps/user/components/affiliate/index.tsx @@ -6,13 +6,13 @@ import { } from '@/components/affiliate/components/AffiliateDialog'; import { Display } from '@/components/display'; import { Empty } from '@/components/empty'; +import SvgIcon from '@/components/SvgIcon'; import useGlobalStore from '@/config/use-global'; import { queryUserAffiliate, queryUserAffiliateList } from '@/services/user/user'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent } from '@workspace/ui/components/card'; import { formatDate } from '@workspace/ui/utils'; -import { Copy } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { useRef, useState } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; @@ -58,7 +58,7 @@ export default function Affiliate() {
历史推荐用户:7
-
+

佣金总额

@@ -67,21 +67,23 @@ export default function Affiliate() {

- - 返佣邀请码 - { - if (result) { - toast.success(t('copySuccess')); - } - }} + 返佣邀请码 + { + if (result) { + toast.success(t('copySuccess')); + } + }} + > + - - + + +

{user?.refer_code} @@ -93,8 +95,12 @@ export default function Affiliate() { } }} > -

@@ -105,7 +111,10 @@ export default function Affiliate() {

邀请记录

- dialogRef.current.open()}> + dialogRef.current.open()} + > 更多
diff --git a/apps/user/components/user-nav.tsx b/apps/user/components/user-nav.tsx index d8de3cc..0ecc6e8 100644 --- a/apps/user/components/user-nav.tsx +++ b/apps/user/components/user-nav.tsx @@ -1,8 +1,10 @@ 'use client'; +import SvgIcon from '@/components/SvgIcon.tsx'; import useGlobalStore from '@/config/use-global'; import { Logout } from '@/utils/common'; import { useSidebar } from '@workspace/airo-ui/components/sidebar'; +import { useIsMobile } from '@workspace/airo-ui/hooks/use-mobile'; import { Avatar, AvatarFallback, AvatarImage } from '@workspace/ui/components/avatar'; import { DropdownMenu, @@ -20,29 +22,30 @@ export function UserNav({ from = '' }: { from?: string }) { const router = useRouter(); const pathname = usePathname(); const { toggleSidebar } = useSidebar(); + const isMobile = useIsMobile(); if (user) { return ( {from === 'profile' ? (
- + - + {user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)} -
+
{user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}
) : ( - + -
- +
+ - + {user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)}
-

+

{user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}

-

+

{user?.auth_methods?.[0]?.auth_identifier}

@@ -84,7 +90,7 @@ export function UserNav({ from = '' }: { from?: string }) { { title: 'profile', url: '/profile', - icon: 'uil:dashboard', + icon: 'profile', }, ].map((item) => ( - + {t(item.title)} ))} @@ -106,9 +112,14 @@ export function UserNav({ from = '' }: { from?: string }) { Logout(); setUser(); }} - className='flex cursor-pointer items-center gap-3 rounded-full px-5 py-2 text-base font-medium text-[#0F2C53] focus:bg-[#E22C2E] focus:text-white sm:text-xl' + className='flex cursor-pointer items-center gap-3 rounded-full bg-[#E22C2E] px-5 py-2 text-base font-medium text-white focus:bg-[#E22C2E] focus:text-white md:bg-white md:text-xl md:text-[#0F2C53]' > - + {t('logout')} diff --git a/apps/user/config/navs.ts b/apps/user/config/navs.ts index 249fe98..a787b77 100644 --- a/apps/user/config/navs.ts +++ b/apps/user/config/navs.ts @@ -3,42 +3,50 @@ export const navs = [ title: 'dashboard', url: '/dashboard', icon: 'uil:dashboard', + image: 'dashboard', }, { url: '/subscribe', icon: 'uil:shop', title: 'subscribe', + image: 'shop', }, { url: '/order', icon: 'uil:notes', title: 'order', + image: 'notes', }, { url: '/wallet', icon: 'uil:wallet', title: 'wallet', + image: 'wallet', }, { url: '/affiliate', icon: 'uil:users-alt', title: 'affiliate', + image: 'affiliate', }, { url: '/document', icon: 'uil:book-alt', title: 'document', + image: 'document', }, { url: '/profile', icon: 'uil:megaphone', title: 'profile', hidden: true, + image: 'profile', }, { url: '/ticket', icon: 'uil:message', title: 'ticket', + image: 'ticket', }, ]; diff --git a/apps/user/next.config.ts b/apps/user/next.config.ts index 9ecfd4c..5029ba2 100644 --- a/apps/user/next.config.ts +++ b/apps/user/next.config.ts @@ -4,7 +4,7 @@ import createNextIntlPlugin from 'next-intl/plugin'; const withNextIntl = createNextIntlPlugin('./locales/request.ts'); const nextConfig: NextConfig = { - transpilePackages: ['@workspace/ui'], + transpilePackages: ['@workspace/ui', '@workspace/airo-ui'], output: 'standalone', images: { remotePatterns: [ @@ -18,6 +18,14 @@ const nextConfig: NextConfig = { }, ], }, + turbopack: { + rules: { + './public/svg-icon/*.svg': { + loaders: ['@svgr/webpack'], + as: '*.js', + }, + }, + }, }; export default withNextIntl(nextConfig); diff --git a/apps/user/package.json b/apps/user/package.json index 42c2f03..3fc615f 100644 --- a/apps/user/package.json +++ b/apps/user/package.json @@ -42,6 +42,7 @@ "zustand": "^5.0.3" }, "devDependencies": { + "@svgr/webpack": "^8.1.0", "@types/node": "^22.10.5", "@types/react": "^19.0.4", "@types/react-copy-to-clipboard": "^5.0.7", diff --git a/apps/user/public/list.png b/apps/user/public/list.png new file mode 100644 index 0000000..55e3bf8 Binary files /dev/null and b/apps/user/public/list.png differ diff --git a/apps/user/public/shrink.png b/apps/user/public/shrink.png new file mode 100644 index 0000000..a20115f Binary files /dev/null and b/apps/user/public/shrink.png differ diff --git a/apps/user/public/svg-icon/affiliate.svg b/apps/user/public/svg-icon/affiliate.svg new file mode 100644 index 0000000..e9a2d00 --- /dev/null +++ b/apps/user/public/svg-icon/affiliate.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/user/public/svg-icon/copy.svg b/apps/user/public/svg-icon/copy.svg new file mode 100644 index 0000000..2deeaa6 --- /dev/null +++ b/apps/user/public/svg-icon/copy.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/user/public/svg-icon/dashboard.svg b/apps/user/public/svg-icon/dashboard.svg new file mode 100644 index 0000000..62325d0 --- /dev/null +++ b/apps/user/public/svg-icon/dashboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/user/public/svg-icon/document.svg b/apps/user/public/svg-icon/document.svg new file mode 100644 index 0000000..b8f1dec --- /dev/null +++ b/apps/user/public/svg-icon/document.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/user/public/svg-icon/exit.svg b/apps/user/public/svg-icon/exit.svg new file mode 100644 index 0000000..ca7deeb --- /dev/null +++ b/apps/user/public/svg-icon/exit.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/user/public/svg-icon/notes.svg b/apps/user/public/svg-icon/notes.svg new file mode 100644 index 0000000..8a4556d --- /dev/null +++ b/apps/user/public/svg-icon/notes.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/user/public/svg-icon/profile.svg b/apps/user/public/svg-icon/profile.svg new file mode 100644 index 0000000..630443d --- /dev/null +++ b/apps/user/public/svg-icon/profile.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/user/public/svg-icon/qrcode.svg b/apps/user/public/svg-icon/qrcode.svg new file mode 100644 index 0000000..b2dcf31 --- /dev/null +++ b/apps/user/public/svg-icon/qrcode.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/user/public/svg-icon/shop.svg b/apps/user/public/svg-icon/shop.svg new file mode 100644 index 0000000..c70a78e --- /dev/null +++ b/apps/user/public/svg-icon/shop.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/user/public/svg-icon/ticket.svg b/apps/user/public/svg-icon/ticket.svg new file mode 100644 index 0000000..4cb78be --- /dev/null +++ b/apps/user/public/svg-icon/ticket.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/user/public/svg-icon/wallet.svg b/apps/user/public/svg-icon/wallet.svg new file mode 100644 index 0000000..7bc089d --- /dev/null +++ b/apps/user/public/svg-icon/wallet.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/airo-ui/src/components/sidebar.tsx b/packages/airo-ui/src/components/sidebar.tsx index f744ce7..4d3e35d 100644 --- a/packages/airo-ui/src/components/sidebar.tsx +++ b/packages/airo-ui/src/components/sidebar.tsx @@ -2,7 +2,7 @@ import { Slot } from '@radix-ui/react-slot'; import { VariantProps, cva } from 'class-variance-authority'; -import { PanelLeft } from 'lucide-react'; +import Image from 'next/image'; import * as React from 'react'; import { Button } from '@workspace/airo-ui/components/button'; @@ -270,14 +270,14 @@ const SidebarTrigger = React.forwardRef< data-sidebar='trigger' variant='ghost' size='icon' - className={cn('h-7 w-7', className)} + className={cn('size-14', className)} onClick={(event) => { onClick?.(event); toggleSidebar(); }} {...props} > - + Toggle Sidebar );