├── .npmrc ├── .eslintrc.json ├── src ├── app │ ├── favicon.ico │ ├── home │ │ ├── components │ │ │ ├── normalPlayer │ │ │ │ └── index.jsx │ │ │ ├── header.jsx │ │ │ ├── recommendList │ │ │ │ ├── style.js │ │ │ │ └── index.jsx │ │ │ ├── miniPlayer │ │ │ │ ├── style.js │ │ │ │ └── index.jsx │ │ │ ├── player │ │ │ │ └── index.jsx │ │ │ └── playList │ │ │ │ ├── style.js │ │ │ │ └── index.jsx │ │ ├── style.js │ │ ├── layout.jsx │ │ ├── rank │ │ │ ├── page.jsx │ │ │ ├── style.js │ │ │ ├── components │ │ │ │ └── main.jsx │ │ │ └── [id] │ │ │ │ ├── page.jsx │ │ │ │ └── style.js │ │ ├── singer │ │ │ ├── page.jsx │ │ │ ├── components │ │ │ │ ├── top-header.jsx │ │ │ │ ├── songList.jsx │ │ │ │ ├── horizen-item.jsx │ │ │ │ └── main.jsx │ │ │ ├── [id] │ │ │ │ ├── style.js │ │ │ │ └── page.jsx │ │ │ └── style.js │ │ ├── page.jsx │ │ └── recommend │ │ │ └── main.jsx │ ├── favorite │ │ ├── layout.jsx │ │ └── page.jsx │ ├── providers.jsx │ ├── mine │ │ └── page.jsx │ ├── ReduxProvider.jsx │ ├── page.jsx │ ├── globals.css │ ├── layout.jsx │ ├── registry.jsx │ └── HomeLayout.style.js ├── assets │ ├── music.png │ └── global-style.js ├── store │ ├── index.js │ └── slices │ │ ├── songs.js │ │ └── singers.js ├── hooks │ ├── useGetAlbums.jsx │ ├── useGetSingerSongs.jsx │ └── useSingerMutation.jsx ├── components │ ├── svg-icon │ │ └── index.jsx │ ├── slider │ │ ├── style.js │ │ └── index.jsx │ ├── loading │ │ └── index.jsx │ ├── header │ │ └── index.jsx │ ├── loading-v2 │ │ └── index.jsx │ ├── progress-circle │ │ └── index.jsx │ ├── toast │ │ └── index.jsx │ ├── confirm │ │ └── index.jsx │ └── scroll │ │ └── index.jsx ├── lib │ └── db.ts ├── utils │ └── index.js └── api │ └── config.js ├── jsconfig.json ├── postcss.config.mjs ├── next.config.mjs ├── docker-compose.yml ├── .env ├── .gitignore ├── public ├── vercel.svg └── next.svg ├── prisma ├── seed.ts └── schema.prisma ├── tailwind.config.js ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | public-hoist-pattern[]=*@nextui-org/* -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrcluo/react-nextjs/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /src/assets/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrcluo/react-nextjs/HEAD/src/assets/music.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/app/home/components/normalPlayer/index.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function NormalPlayer() { 4 | return
213
; 5 | } 6 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /src/app/favorite/layout.jsx: -------------------------------------------------------------------------------- 1 | export default function FavoriteLayout({ children }) { 2 | return ( 3 |
4 |
共享的头部
5 | {children} 6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | compiler: { 4 | styledComponents: true, 5 | }, 6 | reactStrictMode: false, 7 | }; 8 | 9 | export default nextConfig; 10 | -------------------------------------------------------------------------------- /src/app/providers.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | // 集成nextui 3 | 4 | import { NextUIProvider } from "@nextui-org/react"; 5 | 6 | export function Providers({ children }) { 7 | return {children}; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/home/style.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import styled from "styled-components"; 3 | 4 | export const Content = styled.div` 5 | position: fixed; 6 | top: 94px; 7 | left: 0; 8 | bottom: ${(props) => (props.play > 0 ? "60px" : 0)}; 9 | width: 100%; 10 | `; 11 | -------------------------------------------------------------------------------- /src/app/mine/page.jsx: -------------------------------------------------------------------------------- 1 | import prisma from "@/lib/db"; 2 | 3 | export default async function Mine() { 4 | const data = await prisma.user.findMany(); 5 | console.log(222222, data); 6 | return ( 7 |
8 |

Mine Page

9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/app/favorite/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useSelector } from "react-redux"; 3 | export default function Home() { 4 | return ( 5 |
6 |

Favorite Page

7 |
{useSelector((state) => state.singers.contact)}
8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import singers from "./slices/singers"; 3 | import songs from "./slices/songs"; 4 | 5 | const store = configureStore({ 6 | reducer: { 7 | singers, 8 | songs, 9 | }, 10 | }); 11 | export default store; 12 | -------------------------------------------------------------------------------- /src/app/ReduxProvider.jsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | import { Provider } from 'react-redux' 5 | import store from '../store' 6 | 7 | const ReduxProvider = ({ children }) => { 8 | return {children} 9 | } 10 | 11 | export default ReduxProvider -------------------------------------------------------------------------------- /src/app/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useEffect } from "react"; 3 | import { useRouter } from "next/navigation"; 4 | function Home() { 5 | const router = useRouter(); 6 | useEffect(() => { 7 | router.push("/home"); 8 | }, []); 9 | return
开屏动画
; 10 | } 11 | 12 | export default React.memo(Home); 13 | -------------------------------------------------------------------------------- /src/app/home/layout.jsx: -------------------------------------------------------------------------------- 1 | import Header from "./components/header"; 2 | import Player from "./components/player"; 3 | 4 | export const metadata = { 5 | title: "Create Next App", 6 | description: "Generated by create next app", 7 | }; 8 | 9 | export default function HomeLayout({ children }) { 10 | return ( 11 |
12 |
13 | {children} 14 | 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/hooks/useGetAlbums.jsx: -------------------------------------------------------------------------------- 1 | import useSWR from "swr"; 2 | import { API_BASE_URL } from "@/api/config"; 3 | export default function useGetAlbums(id) { 4 | const fetcher = (url) => fetch(url).then((res) => res.json()); 5 | const { data, error, isLoading } = useSWR( 6 | `${API_BASE_URL}/playlist/detail?id=${id}`, 7 | fetcher 8 | ); 9 | return { 10 | data, 11 | isLoading, 12 | isError: error, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/hooks/useGetSingerSongs.jsx: -------------------------------------------------------------------------------- 1 | import useSWR from "swr"; 2 | import { API_BASE_URL } from "@/api/config"; 3 | export default function useGetSingerSongs(id) { 4 | const fetcher = (url) => fetch(url).then((res) => res.json()); 5 | const { data, error, isLoading } = useSWR( 6 | `${API_BASE_URL}/artists?id=${id}`, 7 | fetcher 8 | ); 9 | return { 10 | data, 11 | isLoading, 12 | isError: error, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # docker-compose.yml 2 | version: "3.1" 3 | services: 4 | db: 5 | image: postgres 6 | volumes: 7 | - ./postgres:/var/lib/postgresql/data 8 | restart: always 9 | ports: 10 | - 5432:5432 11 | environment: 12 | - POSTGRES_USER=cluonote 13 | - POSTGRES_PASSWORD=EASYlife.520 14 | 15 | adminer: 16 | image: adminer 17 | restart: always 18 | ports: 19 | - 8080:8080 20 | -------------------------------------------------------------------------------- /src/app/home/rank/page.jsx: -------------------------------------------------------------------------------- 1 | import { API_BASE_URL } from "@/api/config"; 2 | import Main from "./components/main"; 3 | 4 | export default async function Rank() { 5 | const getData = async () => { 6 | const res = await fetch(`${API_BASE_URL}/toplist/detail`); 7 | if (!res.ok) { 8 | throw new Error("Failed to fetch data"); 9 | } 10 | return res.json(); 11 | }; 12 | const data = await getData(); 13 | return
; 14 | } 15 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public" -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body {} 20 | 21 | @layer utilities { 22 | .text-balance { 23 | text-wrap: balance; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | /postgres 9 | /prisma/migrations 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # local env files 31 | .env*.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/home/singer/page.jsx: -------------------------------------------------------------------------------- 1 | import { API_BASE_URL } from "@/api/config"; 2 | import TopHeader from "./components/top-header"; 3 | import Main from "./components/main"; 4 | 5 | export default async function Singer() { 6 | const getData = async (count = 1) => { 7 | const res = await fetch(`${API_BASE_URL}/top/artists?offset=${count}`); 8 | if (!res.ok) { 9 | throw new Error("Failed to fetch data"); 10 | } 11 | return res.json(); 12 | }; 13 | const data = await getData(); 14 | return ( 15 |
16 | 17 |
18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/svg-icon/index.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const SliderContainer = styled.span` 4 | .svg-class { 5 | width: 1em; 6 | height: 1em; 7 | vertical-align: -0.15em; 8 | fill: currentColor; 9 | overflow: hidden; 10 | } 11 | `; 12 | 13 | function SvgIcon(props) { 14 | const { iconClass = "", fill = "", svgClass = "" } = props; 15 | return ( 16 | 17 | 20 | 21 | ); 22 | } 23 | 24 | export default SvgIcon; 25 | -------------------------------------------------------------------------------- /prisma/seed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | // js用 const { PrismaClient } = require("@prisma/client"); 3 | // 初始化 Prisma Client 4 | const prisma = new PrismaClient(); 5 | 6 | async function main() { 7 | //在此编写 Prisma Client 查询 8 | const user = await prisma.user.create({ 9 | data: { 10 | name: "小马", 11 | avatar: 12 | "https://p3-passport.byteimg.com/img/user-avatar/585e1491713363bc8f67d06c485e8260~100x100.awebp", 13 | }, 14 | }); 15 | console.log(user); 16 | } 17 | 18 | main() 19 | .catch((e) => { 20 | console.error(e); 21 | process.exit(1); 22 | }) 23 | .finally(async () => { 24 | // 关闭 Prisma Client 25 | await prisma.$disconnect(); 26 | }); 27 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | import { nextui } from "@nextui-org/react"; 4 | const config = { 5 | content: [ 6 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 8 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 9 | "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}", 10 | ], 11 | theme: { 12 | extend: { 13 | backgroundImage: { 14 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 15 | "gradient-conic": 16 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 17 | }, 18 | }, 19 | }, 20 | darkMode: "class", 21 | plugins: [nextui()], 22 | }; 23 | export default config; 24 | -------------------------------------------------------------------------------- /src/app/layout.jsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | import ReduxProvider from "./ReduxProvider"; 3 | import StyledComponentsRegistry from "./registry"; 4 | import { Providers } from "./providers"; 5 | 6 | export default function RootLayout({ children }) { 7 | return ( 8 | // suppressHydrationWarning用于在客户端与服务器端渲染(SSR)内容不一致时,抑制 hydration 过程中的警告。 9 | 10 | 11 |