From 3919b572c876190121bdd1e3212c467310ef84f7 Mon Sep 17 00:00:00 2001 From: wei Date: Tue, 17 Sep 2024 10:34:19 +0000 Subject: [PATCH] laste --- .vscode/settings.json | 4 + apps/web/README.md | 4 + apps/web/docs/prisma.md | 2 + apps/web/package.json | 13 ++- apps/web/src/app/(pages)/layout.module.css | 0 apps/web/src/app/(pages)/layout.tsx | 0 apps/web/src/app/(pages)/page.module.css | 0 apps/web/src/app/(pages)/page.tsx | 0 .../web/src/app/_components/shadcn/button.tsx | 57 +++++++++++ apps/web/{ => src/app}/components.json | 9 +- apps/web/src/app/layout.module.css | 3 - apps/web/src/app/layout.tsx | 12 +-- apps/web/src/app/page.module.css.ts | 20 ++++ apps/web/src/app/page.tsx | 19 ++-- apps/web/src/app/styles/app.css | 4 + .../{assets => app/styles}/images/bg-dark.png | Bin apps/web/src/{ => app}/styles/index.css | 4 +- apps/web/src/app/styles/tailwind/base.css | 42 +++++++++ .../{ => app}/styles/tailwind/components.css | 0 .../web/src/app/styles/tailwind/utilities.css | 38 ++++++++ .../tailwind/base.css => app/styles/vars.css} | 52 ++++++---- apps/web/src/app/tailwind.config.ts | 89 ++++++++++++++++++ apps/web/src/database/schema/post.prisma | 11 +++ apps/web/src/database/seed/forum.ts | 7 +- apps/web/src/database/seed/utils.ts | 29 ------ apps/web/src/database/types.ts | 72 ++++++++++++++ apps/web/src/lib/utils.ts | 6 -- apps/web/src/{lib => libs}/db.ts | 3 +- apps/web/src/libs/utils.ts | 45 +++++++++ apps/web/src/styles/app.css | 5 - apps/web/src/styles/tailwind/utilities.css | 2 - apps/web/src/styles/vars.css | 12 --- apps/web/tailwind.config.ts | 75 ++------------- apps/web/tsconfig.json | 2 +- packages/core/stylelint.js | 2 +- packages/utils/src/index.ts | 1 + packages/utils/src/utils/random.ts | 56 +++++++++++ packages/utils/src/utils/tools.ts | 14 --- pnpm-lock.yaml | 67 ++++++++++++- 39 files changed, 594 insertions(+), 187 deletions(-) create mode 100644 apps/web/src/app/(pages)/layout.module.css create mode 100644 apps/web/src/app/(pages)/layout.tsx create mode 100644 apps/web/src/app/(pages)/page.module.css create mode 100644 apps/web/src/app/(pages)/page.tsx create mode 100644 apps/web/src/app/_components/shadcn/button.tsx rename apps/web/{ => src/app}/components.json (56%) delete mode 100644 apps/web/src/app/layout.module.css create mode 100644 apps/web/src/app/page.module.css.ts create mode 100644 apps/web/src/app/styles/app.css rename apps/web/src/{assets => app/styles}/images/bg-dark.png (100%) rename apps/web/src/{ => app}/styles/index.css (87%) create mode 100644 apps/web/src/app/styles/tailwind/base.css rename apps/web/src/{ => app}/styles/tailwind/components.css (100%) create mode 100644 apps/web/src/app/styles/tailwind/utilities.css rename apps/web/src/{styles/tailwind/base.css => app/styles/vars.css} (59%) create mode 100644 apps/web/src/app/tailwind.config.ts create mode 100644 apps/web/src/database/schema/post.prisma create mode 100644 apps/web/src/database/types.ts delete mode 100644 apps/web/src/lib/utils.ts rename apps/web/src/{lib => libs}/db.ts (85%) create mode 100644 apps/web/src/libs/utils.ts delete mode 100644 apps/web/src/styles/app.css delete mode 100644 apps/web/src/styles/tailwind/utilities.css delete mode 100644 apps/web/src/styles/vars.css create mode 100644 packages/utils/src/utils/random.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f3e75b1..3f57224 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,10 @@ "emmet.includeLanguages": { "postcss": "css" }, + "tailwindCSS.experimental.configFile": { + "apps/web/src/app/tailwind-config.ts": "apps/web/src/app/**" + + }, "stylelint.enable": true, "stylelint.snippet": ["css", "scss", "less", "postcss"], "stylelint.validate": ["css", "scss", "less", "postcss"], diff --git a/apps/web/README.md b/apps/web/README.md index c403366..0811876 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -34,3 +34,7 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. + + +## components.json +Shadcn config files \ No newline at end of file diff --git a/apps/web/docs/prisma.md b/apps/web/docs/prisma.md index 7454358..fe666cb 100644 --- a/apps/web/docs/prisma.md +++ b/apps/web/docs/prisma.md @@ -9,6 +9,8 @@ schema.prisma ```generator client { provider = "prisma-client-js" + // prismaSchemaFolder 设置读取schema文件夹下所有的模型文件 + previewFeatures = ["prismaSchemaFolder"] } datasource db { diff --git a/apps/web/package.json b/apps/web/package.json index 494d29e..82e183b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,7 +12,7 @@ "dbrs": "cross-env NODE_ENV=development prisma migrate reset -f", "dbs": "cross-env NODE_ENV=development prisma db seed", "dbo": "prisma studio", - "addsc": "pnpm dlx shadcn-ui@latest add", + "addsc": "pnpm dlx shadcn-ui@latest add -c ./src/app", "dev": "next dev", "build": "next build", "start": "next start", @@ -21,12 +21,13 @@ "lint:style": "stylelint \"**/*.css\" --fix --cache --cache-location node_modules/.cache/stylelint/" }, "dependencies": { + "@3rapp/utils": "workspace:*", "@3rapp/store": "workspace:*", "@faker-js/faker": "^8.4.1", "@prisma/client": "^5.16.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-select": "^2.1.1", - "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-slot": "^1.1.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "immutable": "^4.3.6", @@ -35,7 +36,8 @@ "prisma-paginate": "^5.2.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "tailwind-merge": "^2.3.0" + "tailwind-merge": "^2.3.0", + "uuid": "^10.0.0" }, "devDependencies": { "@3rapp/core": "workspace:*", @@ -43,6 +45,7 @@ "@types/node": "^20.12.12", "@types/react": "^18.3.2", "@types/react-dom": "^18.3.0", + "@types/uuid": "^10.0.0", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", "eslint": "^8.57.0", @@ -54,6 +57,10 @@ "prisma": "^5.16.1", "prisma-extension-bark": "^0.2.2", "stylelint": "^16.5.0", + "stylelint-config-css-modules": "^4.4.0", + "stylelint-config-recess-order": "^5.0.1", + "stylelint-config-standard": "^36.0.1", + "stylelint-prettier": "^5.0.2", "tailwindcss": "^3.4.3", "tailwindcss-animate": "^1.0.7", "ts-node": "^10.9.2", diff --git a/apps/web/src/app/(pages)/layout.module.css b/apps/web/src/app/(pages)/layout.module.css new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/src/app/(pages)/layout.tsx b/apps/web/src/app/(pages)/layout.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/src/app/(pages)/page.module.css b/apps/web/src/app/(pages)/page.module.css new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/src/app/(pages)/page.tsx b/apps/web/src/app/(pages)/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/src/app/_components/shadcn/button.tsx b/apps/web/src/app/_components/shadcn/button.tsx new file mode 100644 index 0000000..e5a3ea5 --- /dev/null +++ b/apps/web/src/app/_components/shadcn/button.tsx @@ -0,0 +1,57 @@ +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; + +import { cn } from '@/libs/utils'; + +const buttonVariants = cva( + 'tw-inline-flex tw-items-center tw-justify-center tw-whitespace-nowrap tw-rounded-md tw-text-sm tw-font-medium tw-transition-colors focus-visible:tw-outline-none focus-visible:tw-ring-1 focus-visible:tw-ring-ring disabled:tw-pointer-events-none disabled:tw-opacity-50', + { + variants: { + variant: { + default: + 'tw-bg-primary tw-text-primary-foreground tw-shadow hover:tw-bg-primary/90', + destructive: + 'tw-bg-destructive tw-text-destructive-foreground tw-shadow-sm hover:tw-bg-destructive/90', + outline: + 'tw-border tw-border-input tw-bg-background tw-shadow-sm hover:tw-bg-accent hover:tw-text-accent-foreground', + secondary: + 'tw-bg-secondary tw-text-secondary-foreground tw-shadow-sm hover:tw-bg-secondary/80', + ghost: 'hover:tw-bg-accent hover:tw-text-accent-foreground', + link: 'tw-text-primary tw-underline-offset-4 hover:tw-underline', + }, + size: { + default: 'tw-h-9 tw-px-4 tw-py-2', + sm: 'tw-h-8 tw-rounded-md tw-px-3 tw-text-xs', + lg: 'tw-h-10 tw-rounded-md tw-px-8', + icon: 'tw-h-9 tw-w-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : 'button'; + return ( + + ); + }, +); +Button.displayName = 'Button'; + +export { Button, buttonVariants }; diff --git a/apps/web/components.json b/apps/web/src/app/components.json similarity index 56% rename from apps/web/components.json rename to apps/web/src/app/components.json index 40c36b4..1189447 100644 --- a/apps/web/components.json +++ b/apps/web/src/app/components.json @@ -4,14 +4,15 @@ "rsc": true, "tsx": true, "tailwind": { - "config": "tailwind.config.ts", - "css": "src/styles/index.css", + "config": "tailwind-config.ts", + "css": "styles/index.css", "baseColor": "zinc", "cssVariables": true, "prefix": "tw-" }, "aliases": { - "components": "@/components", - "utils": "@/lib/utils" + "components": "@/app/_components", + "ui": "@/app/_components/shadcn", + "utils": "@/libs/utils" } } diff --git a/apps/web/src/app/layout.module.css b/apps/web/src/app/layout.module.css deleted file mode 100644 index fc39a6e..0000000 --- a/apps/web/src/app/layout.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.app { - @apply tw-bg-[color: #ededed] tw-flex tw-w-full tw-h-[100vh] tw-p-0 tw-m-0; -} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index c00675e..a7f364a 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,19 +1,17 @@ import { Metadata } from 'next'; +import { PropsWithChildren } from 'react'; -import '@/styles/index.css'; +import '@/app/styles/index.css'; export const metadata: Metadata = { title: 'nextapp', description: '3r教室Next.js全栈开发课程', }; -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { +const RootLayout: React.FC = ({ children }) => { return ( {children} ); -} +}; +export default RootLayout; diff --git a/apps/web/src/app/page.module.css.ts b/apps/web/src/app/page.module.css.ts new file mode 100644 index 0000000..a1d5245 --- /dev/null +++ b/apps/web/src/app/page.module.css.ts @@ -0,0 +1,20 @@ +import clsx from 'clsx'; + +export const container = clsx( + 'tw-h-[100vh]', + 'tw-w-full', + 'tw-p-0', + 'tw-m-0', + 'tw-flex', + 'tw-flex-auto', + 'tw-items-center', + 'tw-justify-center', +); +export const block = clsx( + 'tw-shadow-md', + 'tw-p-5', + 'tw-bg-black', + 'tw-text-center', + 'tw-text-white', + 'tw-text-lg', +); diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 110b6c3..ad8486f 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,7 +1,14 @@ -export default function Page() { - return ( -
-

Page

+import { Button } from './_components/shadcn/button'; +import { block, container } from './page.module.css'; + +const App: React.FC = () => ( +
+
+ 欢迎来到3R教室,这是Nextjs课程的开始 +
- ); -} +
+); +export default App; diff --git a/apps/web/src/app/styles/app.css b/apps/web/src/app/styles/app.css new file mode 100644 index 0000000..3f3406e --- /dev/null +++ b/apps/web/src/app/styles/app.css @@ -0,0 +1,4 @@ +body { + @apply tw-bg-[url(images/bg-dark.png)] tw-bg-cover tw-bg-fixed tw-bg-center; + font-size: var(--font-size-base); +} diff --git a/apps/web/src/assets/images/bg-dark.png b/apps/web/src/app/styles/images/bg-dark.png similarity index 100% rename from apps/web/src/assets/images/bg-dark.png rename to apps/web/src/app/styles/images/bg-dark.png diff --git a/apps/web/src/styles/index.css b/apps/web/src/app/styles/index.css similarity index 87% rename from apps/web/src/styles/index.css rename to apps/web/src/app/styles/index.css index 645c1da..c3cef20 100644 --- a/apps/web/src/styles/index.css +++ b/apps/web/src/app/styles/index.css @@ -1,8 +1,10 @@ @import './vars.css'; +@import './app.css'; @import 'tailwindcss/base'; @import './tailwind/base.css'; @import 'tailwindcss/components'; @import './tailwind/components.css'; @import 'tailwindcss/utilities'; @import './tailwind/utilities.css'; -@import './app.css'; + +@config "../tailwind.config.ts"; diff --git a/apps/web/src/app/styles/tailwind/base.css b/apps/web/src/app/styles/tailwind/base.css new file mode 100644 index 0000000..de28578 --- /dev/null +++ b/apps/web/src/app/styles/tailwind/base.css @@ -0,0 +1,42 @@ +@layer base { + html, + body { + @apply tw-h-[100vh] tw-w-full tw-p-0 tw-m-0 tw-text-[var(--font-color-base)]; + } + + html { + font-family: var(--font-family-standard); + } + + body { + font-size: var(--font-size-base); + } + + a { + @apply tw-text-[var(--font-color-link)]; + } + + h1 { + @apply tw-text-3xl; + } + + h2 { + @apply tw-text-2xl; + } + + h3 { + @apply tw-text-xl; + } + + h4 { + @apply tw-text-lg; + } + + h5 { + @apply tw-text-sm; + } +} + +* { + @apply tw-border-border; +} diff --git a/apps/web/src/styles/tailwind/components.css b/apps/web/src/app/styles/tailwind/components.css similarity index 100% rename from apps/web/src/styles/tailwind/components.css rename to apps/web/src/app/styles/tailwind/components.css diff --git a/apps/web/src/app/styles/tailwind/utilities.css b/apps/web/src/app/styles/tailwind/utilities.css new file mode 100644 index 0000000..dd4cbe3 --- /dev/null +++ b/apps/web/src/app/styles/tailwind/utilities.css @@ -0,0 +1,38 @@ +@layer utilities { + /* 鼠标移动到文字或链接加深颜色 */ + .tw-hover { + @apply hover:tw-text-[var(--font-color-link-hover)]; + + /* 只显示一行文字, 溢出部分以省略号代替 */ + .tw-ellips { + @apply tw-inline-block tw-overflow-hidden tw-max-w-full tw-whitespace-nowrap tw-text-ellipsis tw-break-all; + } + + /* 鼠标移动到文字或链接出现动画下划线 */ + .tw-animate-decoration { + background: linear-gradient(var(--font-color-link-hover), var(--font-color-link-hover)) + 0% 100% / 0% 1px no-repeat; + transition: background-size ease-out 200ms; + + &:not(:focus):hover { + background-size: 100% 1px; + } + } + + /* 粗下划线 */ + .tw-animate-decoration-lg:not(:focus):hover { + background-size: 100% 2px; + } + + /* 取消下划线 */ + .tw-none-animate-decoration { + background-size: 0 !important; + transition: none !important; + + &:hover, + &:not(:focus):hover { + background-size: 100% 0 !important; + } + } + } +} diff --git a/apps/web/src/styles/tailwind/base.css b/apps/web/src/app/styles/vars.css similarity index 59% rename from apps/web/src/styles/tailwind/base.css rename to apps/web/src/app/styles/vars.css index 1611831..33cac74 100644 --- a/apps/web/src/styles/tailwind/base.css +++ b/apps/web/src/app/styles/vars.css @@ -1,3 +1,27 @@ +@layer base { + :root { + /* 默认文字大小 */ + --font-size-base: 0.875rem; + + /* 默认字体 */ + --font-family-standard: 'Source Sans Pro', 'Hiragino Sans GB', 'Microsoft Yahei', simsun, + helvetica, arial, sans-serif, monospace; + + /* 默认代码字体 */ + --font-family-code: 'Fira Code', 'Source Sans Pro', 'Hiragino Sans GB', 'Microsoft Yahei', + simsun, helvetica, arial, sans-serif, monospace; + + /* 默认文字颜色 */ + --font-color-base: #2d2929; + + /* 链接文字颜色 */ + --font-color-link: #131212; + + /* 鼠标移动到文字或链接上后的文字颜色 */ + --font-color-link-hover: #0b0b0b; + } +} + @layer base { :root { --background: 0 0% 100%; @@ -20,6 +44,11 @@ --input: 240 5.9% 90%; --ring: 240 10% 3.9%; --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; } .dark { @@ -42,23 +71,10 @@ --border: 240 3.7% 15.9%; --input: 240 3.7% 15.9%; --ring: 240 4.9% 83.9%; - } - - html { - font-family: var(--font-family-standard); - } - - body { - font-size: var(--font-size-base); - } -} - -@layer base { - * { - @apply tw-border-border; - } - - body { - @apply tw-bg-background tw-text-foreground; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; } } diff --git a/apps/web/src/app/tailwind.config.ts b/apps/web/src/app/tailwind.config.ts new file mode 100644 index 0000000..667b562 --- /dev/null +++ b/apps/web/src/app/tailwind.config.ts @@ -0,0 +1,89 @@ +/* eslint-disable global-require */ +/* eslint-disable import/no-extraneous-dependencies */ +import type { Config } from 'tailwindcss'; + +const config = { + darkMode: ['class'], + content: ['./src/app/**/*.{ts,tsx}'], + prefix: 'tw-', + theme: { + container: { + center: true, + padding: '2rem', + screens: { + xs: '480px', + sm: '576px', + md: '768px', + lg: '992px', + xl: '1200px', + '2xl': '1400px', + }, + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + boxShadow: { + nysm: '0 0 2px 0 rgb(0 0 0 / 0.05)', + ny: '0 0 3px 0 rgb(0 0 0 / 0.1), 0 0 2px - 1px rgb(0 0 0 / 0.1)', + nymd: '0 0 6px -1px rgb(0 0 0 / 0.1), 0 0 4px -2px rgb(0 0 0 / 0.1)', + nylg: '0 0 15px -3px rgb(0 0 0 / 0.1), 0 0 6px -4px rgb(0 0 0 / 0.1)', + spread: '0 5px 40px rgb(0 0 0 / 0.1)', + }, + }, + }, + plugins: [require('tailwindcss-animate')], +} satisfies Config; + +export default config; diff --git a/apps/web/src/database/schema/post.prisma b/apps/web/src/database/schema/post.prisma new file mode 100644 index 0000000..b7ba123 --- /dev/null +++ b/apps/web/src/database/schema/post.prisma @@ -0,0 +1,11 @@ +model Post { + id String @id @default(uuid()) + thumb String + title String + summary String? + body String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("posts") +} diff --git a/apps/web/src/database/seed/forum.ts b/apps/web/src/database/seed/forum.ts index b91af9b..624156f 100644 --- a/apps/web/src/database/seed/forum.ts +++ b/apps/web/src/database/seed/forum.ts @@ -1,11 +1,10 @@ +import { getRandListData, getRandomMin } from '@3rapp/utils'; import { faker } from '@faker-js/faker'; import { isNil } from 'lodash'; import { prisma } from '../client'; -import { getRandListData, getRandomIndex } from './utils'; - const forumTags = [ { name: 'react', @@ -42,8 +41,8 @@ export const main = async () => { data: { publishedAt, title: faker.lorem.sentence({ min: 3, max: 10 }), - body: faker.lorem.paragraphs(getRandomIndex(8), '
\n'), - views: getRandomIndex(2100), + body: faker.lorem.paragraphs(getRandomMin(8), '
\n'), + views: getRandomMin(2100), tags: { connect: getRandListData(tags), }, diff --git a/apps/web/src/database/seed/utils.ts b/apps/web/src/database/seed/utils.ts index 959f949..da2af0f 100644 --- a/apps/web/src/database/seed/utils.ts +++ b/apps/web/src/database/seed/utils.ts @@ -1,34 +1,5 @@ import { base, zh_CN, en, Faker } from '@faker-js/faker'; -/** - * 获取小于N的随机整数 - * @param count - */ -export const getRandomIndex = (count: number) => Math.floor(Math.random() * count); - -/** - * 从列表中获取一个随机项 - * @param list - */ -export const getRandItemData = >(list: T[]) => { - return list[getRandomIndex(list.length)]; -}; - -/** - * 从列表中获取多个随机项组成一个新列表 - * @param list - */ -export const getRandListData = >(list: T[]) => { - const result: T[] = []; - for (let i = 0; i < getRandomIndex(list.length); i++) { - const random = getRandItemData(list); - if (!result.find((item) => item.id === random.id)) { - result.push(random); - } - } - return result; -}; - /** * faker实例,首选语言为简体中文 */ diff --git a/apps/web/src/database/types.ts b/apps/web/src/database/types.ts new file mode 100644 index 0000000..9466c4e --- /dev/null +++ b/apps/web/src/database/types.ts @@ -0,0 +1,72 @@ +/** + * 分页原数据 + */ +export interface PaginateMeta { + /** + * 当前页项目数量 + */ + itemCount: number; + /** + * 项目总数量 + */ + totalItems?: number; + /** + * 每页显示数量 + */ + perPage: number; + /** + * 总页数 + */ + totalPages?: number; + /** + * 当前页数 + */ + currentPage: number; +} + +/** + * 分页选项 + */ +export interface PaginateOptions { + /** + * 当前页数 + */ + page?: number; + /** + * 每页显示数量 + */ + limit?: number; +} + +/** + * 分页返回数据 + */ +export interface PaginateReturn> { + meta: PaginateMeta; + items: E[]; +} +/** + * 文章类型 + */ +export interface IPost { + /** + * 文章ID + */ + id: string; + /** + * 文章标题 + */ + title: string; + /** + * 文章内容 + */ + body: string; + /** + * 文章封面图 + */ + thumb: string; + /** + * 文章摘要 + */ + summary?: string; +} diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts deleted file mode 100644 index dd53ea8..0000000 --- a/apps/web/src/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from 'clsx'; -import { twMerge } from 'tailwind-merge'; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} diff --git a/apps/web/src/lib/db.ts b/apps/web/src/libs/db.ts similarity index 85% rename from apps/web/src/lib/db.ts rename to apps/web/src/libs/db.ts index 98eae2c..c0cc617 100644 --- a/apps/web/src/lib/db.ts +++ b/apps/web/src/libs/db.ts @@ -1,4 +1,5 @@ /* eslint-disable no-var */ + import { PrismaClient } from '@prisma/client'; import { withBark } from 'prisma-extension-bark'; import paginateExt from 'prisma-paginate'; @@ -9,7 +10,7 @@ const prismaClientSingleton = () => { .$extends(paginateExt); }; declare global { - // eslint-disable-next-line vars-on-top + // eslint-disable-next-line vars-on-top, @typescript-eslint/no-redundant-type-constituents var prismaGlobal: undefined | ReturnType; } const db = globalThis.prismaGlobal ?? prismaClientSingleton(); diff --git a/apps/web/src/libs/utils.ts b/apps/web/src/libs/utils.ts new file mode 100644 index 0000000..0fae436 --- /dev/null +++ b/apps/web/src/libs/utils.ts @@ -0,0 +1,45 @@ +import { type ClassValue, clsx } from 'clsx'; +import { isNil } from 'lodash'; +import { twMerge } from 'tailwind-merge'; + +import { PaginateOptions, PaginateReturn } from '@/database/types'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} +/** + * 数据分页函数 + * @param data + * @param options + */ +export const paginate = async >( + data: T[], + options: PaginateOptions, +): Promise> => { + // 当设置每页数据量小于1时,则设置为每页一条数据 + const limit = isNil(options.limit) || options.limit < 1 ? 1 : options.limit; + // 如果当前页小于1,则设置当前页为第一页 + const page = isNil(options.page) || options.page < 1 ? 1 : options.page; + // 起始数据游标,如果页面是第一页则从第1条数据开始截取,如果大于第一页则从当前页的第一条数据开始截取 + const start = page > 1 ? (page - 1) * limit + 1 : 0; + const items = data.slice(start, start + limit); + // 页面数量 + const totalPages = + data.length % limit === 0 + ? Math.floor(data.length / limit) + : Math.floor(data.length / limit) + 1; + // 计算最后一页的数据量 + const remainder = data.length % limit !== 0 ? data.length % limit : limit; + // 根据最优一页的数据量得出当前页面的数据量 + const itemCount = page < totalPages ? limit : remainder; + return { + items, + meta: { + totalItems: data.length, + itemCount, + perPage: limit, + totalPages, + currentPage: page, + }, + }; +}; diff --git a/apps/web/src/styles/app.css b/apps/web/src/styles/app.css deleted file mode 100644 index 1694a4e..0000000 --- a/apps/web/src/styles/app.css +++ /dev/null @@ -1,5 +0,0 @@ -html, -body, -#root { - @apply tw-h-[100vh] tw-w-full tw-flex tw-p-0 tw-m-0; -} diff --git a/apps/web/src/styles/tailwind/utilities.css b/apps/web/src/styles/tailwind/utilities.css deleted file mode 100644 index 8056596..0000000 --- a/apps/web/src/styles/tailwind/utilities.css +++ /dev/null @@ -1,2 +0,0 @@ -@layer utilities { -} diff --git a/apps/web/src/styles/vars.css b/apps/web/src/styles/vars.css deleted file mode 100644 index 6a9427c..0000000 --- a/apps/web/src/styles/vars.css +++ /dev/null @@ -1,12 +0,0 @@ -:root { - /* 标准字体大小 */ - --font-size-base: 0.875rem; - - /* 标准字体 */ - --font-family-standard: 'Source Sans Pro', 'Hiragino Sans GB', 'Microsoft Yahei', simsun, - helvetica, arial, sans-serif, monospace; - - /* 代码字体 */ - --font-family-firacode: 'Fira Code', 'Source Sans Pro', 'Hiragino Sans GB', 'Microsoft Yahei', - simsun, helvetica, arial, sans-serif, monospace; -} diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts index d3d5aaf..2db053d 100644 --- a/apps/web/tailwind.config.ts +++ b/apps/web/tailwind.config.ts @@ -1,82 +1,19 @@ -/* eslint-disable @typescript-eslint/no-require-imports */ -/* eslint-disable global-require */ import type { Config } from 'tailwindcss'; const config = { - darkMode: ['class'], content: ['./src/**/*.{ts,tsx}'], prefix: 'tw-', theme: { - container: { - center: true, - padding: '2rem', - screens: { - xs: '480px', - sm: '576px', - md: '768px', - lg: '992px', - xl: '1200px', - '2xl': '1400px', - }, - }, extend: { - colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0' }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', + boxShadow: { + nysm: '0 0 2px 0 rgb(0 0 0 / 0.05)', + ny: '0 0 3px 0 rgb(0 0 0 / 0.1), 0 0 2px - 1px rgb(0 0 0 / 0.1)', + nymd: '0 0 6px -1px rgb(0 0 0 / 0.1), 0 0 4px -2px rgb(0 0 0 / 0.1)', + nylg: '0 0 15px -3px rgb(0 0 0 / 0.1), 0 0 6px -4px rgb(0 0 0 / 0.1)', + spread: '0 5px 40px rgb(0 0 0 / 0.1)', }, }, }, - plugins: [require('tailwindcss-animate')], } satisfies Config; export default config; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index bdb34ca..9f76c0a 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -2,7 +2,7 @@ "extends": "@3rapp/core/tsconfig/next.json", "compilerOptions": { "paths": { - "@/*": ["/src/*"] + "@/*": ["./src/*"] } }, "include": ["next-env.d.ts", "typings/**/*.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], diff --git a/packages/core/stylelint.js b/packages/core/stylelint.js index 9c1c3ab..9f5a7d3 100644 --- a/packages/core/stylelint.js +++ b/packages/core/stylelint.js @@ -28,7 +28,7 @@ module.exports = { 'at-rule-no-unknown': [ true, { - ignoreAtRules: ['layer', 'apply', 'screen', 'define-mixin', 'mixin', 'tailwind'], + ignoreAtRules: ['layer', 'apply', 'screen', 'define-mixin', 'mixin', 'config'], }, ], diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 446e770..1c9f44b 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,2 +1,3 @@ export * from './utils/tools'; export * from './utils/nesttools'; +export * from './utils/random'; diff --git a/packages/utils/src/utils/random.ts b/packages/utils/src/utils/random.ts new file mode 100644 index 0000000..990666b --- /dev/null +++ b/packages/utils/src/utils/random.ts @@ -0,0 +1,56 @@ +/** + * 获取小于N的随机整数 + * @param count + */ +export const getRandomMin = (count: number) => Math.floor(Math.random() * count); + +/** + * 获取一定范围内的随机整数 + * @param min + * @param max + */ +export const getRandomInt = (min: number, max: number) => + Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1)) + min; + +/** + * 生成只包含字母的固定长度的字符串 + * @param length + */ +export const getRandomCharString = (length: number) => { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +}; + +/** + * 从列表中获取一个随机项 + * @param list + */ +export const getRandItemData = (list: T[]) => { + return list[getRandomMin(list.length)]; +}; + +/** + * 从列表中获取多个随机项组成一个新列表 + * @param list + */ +export const getRandListData = (list: T[]) => { + const result: T[] = []; + for (let i = 0; i < getRandomMin(list.length); i++) { + const random = getRandItemData(list); + const canPush = !result.find((item) => { + if ('id' in (random as Record)) { + const check = random as Record; + const current = item as Record; + return current.id === check.id; + } + return item === random; + }); + if (canPush) result.push(random); + } + return result; +}; diff --git a/packages/utils/src/utils/tools.ts b/packages/utils/src/utils/tools.ts index 8f19a64..507640c 100644 --- a/packages/utils/src/utils/tools.ts +++ b/packages/utils/src/utils/tools.ts @@ -1,5 +1,3 @@ -import { resolve } from 'path'; - import deepmerge from 'deepmerge'; /** @@ -22,18 +20,6 @@ export const deepMerge = ( return deepmerge(x, y, options) as T2 extends T1 ? T1 : T1 & T2; }; -/** - * 获取dir参数相对于当前调用这个函数的文件的绝对路径 - * 比如调用者是scripts/test.ts,而src与scripts同级别.那么,pathResolve('../src')就是src的绝对路径 - * @param dir - */ -export const pathResolve = (dir: string) => { - const { stack } = new Error(); - const stackLines = stack.split('\n'); - const callerLine = stackLines[2].trim(); - const callerFilePath = callerLine.match(/\(([^:)]+):/)[1]; - return resolve(callerFilePath, dir); -}; /** * 用于请求验证中转义null * @param value diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3203bd8..ce94f7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -346,6 +346,9 @@ importers: '@3rapp/store': specifier: workspace:* version: link:../../packages/store + '@3rapp/utils': + specifier: workspace:* + version: link:../../packages/utils '@faker-js/faker': specifier: ^8.4.1 version: 8.4.1 @@ -359,7 +362,7 @@ importers: specifier: ^2.1.1 version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': - specifier: ^1.0.2 + specifier: ^1.1.0 version: 1.1.0(@types/react@18.3.3)(react@18.3.1) class-variance-authority: specifier: ^0.7.0 @@ -388,6 +391,9 @@ importers: tailwind-merge: specifier: ^2.3.0 version: 2.4.0 + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@3rapp/core': specifier: workspace:* @@ -404,6 +410,9 @@ importers: '@types/react-dom': specifier: ^18.3.0 version: 18.3.0 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.40) @@ -437,6 +446,18 @@ importers: stylelint: specifier: ^16.5.0 version: 16.7.0(typescript@5.5.4) + stylelint-config-css-modules: + specifier: ^4.4.0 + version: 4.4.0(stylelint@16.7.0(typescript@5.5.4)) + stylelint-config-recess-order: + specifier: ^5.0.1 + version: 5.0.1(stylelint@16.7.0(typescript@5.5.4)) + stylelint-config-standard: + specifier: ^36.0.1 + version: 36.0.1(stylelint@16.7.0(typescript@5.5.4)) + stylelint-prettier: + specifier: ^5.0.2 + version: 5.0.2(prettier@3.3.3)(stylelint@16.7.0(typescript@5.5.4)) tailwindcss: specifier: ^3.4.3 version: 3.4.7(ts-node@10.9.2(@swc/core@1.7.3(@swc/helpers@0.5.11))(@types/node@20.14.12)(typescript@5.5.4)) @@ -13952,38 +13973,82 @@ snapshots: client-only: 0.0.1 react: 18.3.1 + stylelint-config-css-modules@4.4.0(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + stylelint: 16.7.0(typescript@5.5.4) + optionalDependencies: + stylelint-scss: 6.5.0(stylelint@16.7.0(typescript@5.5.4)) + stylelint-config-css-modules@4.4.0(stylelint@16.8.1(typescript@5.5.4)): dependencies: stylelint: 16.8.1(typescript@5.5.4) optionalDependencies: stylelint-scss: 6.5.0(stylelint@16.8.1(typescript@5.5.4)) + stylelint-config-recess-order@5.0.1(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + stylelint: 16.7.0(typescript@5.5.4) + stylelint-order: 6.0.4(stylelint@16.7.0(typescript@5.5.4)) + stylelint-config-recess-order@5.0.1(stylelint@16.8.1(typescript@5.5.4)): dependencies: stylelint: 16.8.1(typescript@5.5.4) stylelint-order: 6.0.4(stylelint@16.8.1(typescript@5.5.4)) + stylelint-config-recommended@14.0.1(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + stylelint: 16.7.0(typescript@5.5.4) + stylelint-config-recommended@14.0.1(stylelint@16.8.1(typescript@5.5.4)): dependencies: stylelint: 16.8.1(typescript@5.5.4) + stylelint-config-standard@36.0.1(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + stylelint: 16.7.0(typescript@5.5.4) + stylelint-config-recommended: 14.0.1(stylelint@16.7.0(typescript@5.5.4)) + stylelint-config-standard@36.0.1(stylelint@16.8.1(typescript@5.5.4)): dependencies: stylelint: 16.8.1(typescript@5.5.4) stylelint-config-recommended: 14.0.1(stylelint@16.8.1(typescript@5.5.4)) + stylelint-order@6.0.4(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + postcss: 8.4.41 + postcss-sorting: 8.0.2(postcss@8.4.41) + stylelint: 16.7.0(typescript@5.5.4) + stylelint-order@6.0.4(stylelint@16.8.1(typescript@5.5.4)): dependencies: postcss: 8.4.41 postcss-sorting: 8.0.2(postcss@8.4.41) stylelint: 16.8.1(typescript@5.5.4) + stylelint-prettier@5.0.2(prettier@3.3.3)(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + prettier: 3.3.3 + prettier-linter-helpers: 1.0.0 + stylelint: 16.7.0(typescript@5.5.4) + stylelint-prettier@5.0.2(prettier@3.3.3)(stylelint@16.8.1(typescript@5.5.4)): dependencies: prettier: 3.3.3 prettier-linter-helpers: 1.0.0 stylelint: 16.8.1(typescript@5.5.4) + stylelint-scss@6.5.0(stylelint@16.7.0(typescript@5.5.4)): + dependencies: + css-tree: 2.3.1 + is-plain-object: 5.0.0 + known-css-properties: 0.34.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.4 + postcss-selector-parser: 6.1.1 + postcss-value-parser: 4.2.0 + stylelint: 16.7.0(typescript@5.5.4) + optional: true + stylelint-scss@6.5.0(stylelint@16.8.1(typescript@5.5.4)): dependencies: css-tree: 2.3.1