@wzyjs/cli 0.3.32 → 0.3.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/index.js +350 -35
  2. package/package.json +6 -4
  3. package/template/web/base/.next/cache/tsconfig.tsbuildinfo +1 -0
  4. package/template/web/base/Dockerfile +37 -0
  5. package/template/web/base/_env +2 -0
  6. package/template/web/base/eslint.config.js +94 -0
  7. package/template/web/base/next.config.js +29 -0
  8. package/template/web/base/package.json +53 -0
  9. package/template/web/base/postcss.config.js +5 -0
  10. package/template/web/base/prisma/models/demo/DemoItem.zmodel +8 -0
  11. package/template/web/base/prisma/schema.prisma +29 -0
  12. package/template/web/base/prisma/schema.zmodel +1 -0
  13. package/template/web/base/src/app/api/trpc/[trpc]/route.ts +24 -0
  14. package/template/web/base/src/app/auth/disabled/page.tsx +29 -0
  15. package/template/web/base/src/app/auth/error/page.tsx +47 -0
  16. package/template/web/base/src/app/auth/signin/page.tsx +53 -0
  17. package/template/web/base/src/app/auth/unauthorized/page.tsx +60 -0
  18. package/template/web/base/src/app/demo/page.tsx +59 -0
  19. package/template/web/base/src/app/layout.tsx +32 -0
  20. package/template/web/base/src/app/not-found.tsx +21 -0
  21. package/template/web/base/src/app/page.tsx +92 -0
  22. package/template/web/base/src/components/base/Layout/Header/context.tsx +56 -0
  23. package/template/web/base/src/components/base/Layout/Header/index.tsx +32 -0
  24. package/template/web/base/src/components/base/Layout/index.tsx +27 -0
  25. package/template/web/base/src/components/base/Providers/TRPCReactProvider.tsx +25 -0
  26. package/template/web/base/src/components/base/Providers/index.tsx +31 -0
  27. package/template/web/base/src/components/base/display/DisplayProvider.tsx +44 -0
  28. package/template/web/base/src/components/base/display/consts.ts +10 -0
  29. package/template/web/base/src/components/base/display/context.ts +6 -0
  30. package/template/web/base/src/components/base/display/index.ts +4 -0
  31. package/template/web/base/src/components/base/display/types.ts +12 -0
  32. package/template/web/base/src/components/base/display/useDisplay.ts +9 -0
  33. package/template/web/base/src/components/base/display/utils.ts +18 -0
  34. package/template/web/base/src/components/base/theme/ThemeProvider.tsx +83 -0
  35. package/template/web/base/src/components/base/theme/ThemeToggle.tsx +26 -0
  36. package/template/web/base/src/components/base/theme/consts.tsx +59 -0
  37. package/template/web/base/src/components/base/theme/context.ts +14 -0
  38. package/template/web/base/src/components/base/theme/useAppTheme.ts +15 -0
  39. package/template/web/base/src/components/base/theme/utils.ts +17 -0
  40. package/template/web/base/src/components/index.ts +1 -0
  41. package/template/web/base/src/consts/index.ts +6 -0
  42. package/template/web/base/src/enums/index.ts +1 -0
  43. package/template/web/base/src/env.js +44 -0
  44. package/template/web/base/src/hooks/index.ts +1 -0
  45. package/template/web/base/src/server/db/client.ts +19 -0
  46. package/template/web/base/src/server/routers/index.ts +3 -0
  47. package/template/web/base/src/server/trpc/context.ts +14 -0
  48. package/template/web/base/src/server/trpc/index.ts +1 -0
  49. package/template/web/base/src/server/trpc/init.ts +27 -0
  50. package/template/web/base/src/server/trpc/procedures.ts +5 -0
  51. package/template/web/base/src/server/trpc/router.ts +11 -0
  52. package/template/web/base/src/server/utils/index.ts +1 -0
  53. package/template/web/base/src/styles/globals.css +3 -0
  54. package/template/web/base/src/types/index.ts +1 -0
  55. package/template/web/base/src/utils/index.ts +4 -0
  56. package/template/web/base/src/utils/query-client/index.ts +31 -0
  57. package/template/web/base/src/utils/trpc/index.ts +23 -0
  58. package/template/web/base/src/utils/trpc/utils.ts +61 -0
  59. package/template/web/base/tailwind.config.cjs +7 -0
  60. package/template/web/base/tsconfig.json +46 -0
  61. package/template/web/google/_env +8 -0
  62. package/template/web/google/package.json +55 -0
  63. package/template/web/google/prisma/models/auth/Account.zmodel +26 -0
  64. package/template/web/google/prisma/models/auth/Session.zmodel +17 -0
  65. package/template/web/google/prisma/models/auth/User.zmodel +22 -0
  66. package/template/web/google/prisma/schema.zmodel +2 -0
  67. package/template/web/google/src/app/api/auth/[...nextauth]/route.ts +7 -0
  68. package/template/web/google/src/app/auth/error/page.tsx +56 -0
  69. package/template/web/google/src/app/auth/signin/page.tsx +83 -0
  70. package/template/web/google/src/app/layout.tsx +35 -0
  71. package/template/web/google/src/components/base/AuthGuard/Loading.tsx +19 -0
  72. package/template/web/google/src/components/base/AuthGuard/index.tsx +45 -0
  73. package/template/web/google/src/components/base/Layout/Header/UserAuthStatus.tsx +93 -0
  74. package/template/web/google/src/components/base/Layout/Header/index.tsx +34 -0
  75. package/template/web/google/src/components/base/Providers/index.tsx +34 -0
  76. package/template/web/google/src/env.js +52 -0
  77. package/template/web/google/src/server/auth/next-auth/adapter.ts +77 -0
  78. package/template/web/google/src/server/auth/next-auth/options.ts +53 -0
  79. package/template/web/google/src/server/trpc/context.ts +21 -0
  80. package/template/web/google/src/server/trpc/procedures.ts +16 -0
  81. package/template/web/middle/_env +10 -0
  82. package/template/web/middle/package.json +55 -0
  83. package/template/web/middle/src/app/api/auth/[...nextauth]/route.ts +7 -0
  84. package/template/web/middle/src/app/auth/disabled/page.tsx +71 -0
  85. package/template/web/middle/src/app/auth/error/page.tsx +56 -0
  86. package/template/web/middle/src/app/auth/signin/page.tsx +91 -0
  87. package/template/web/middle/src/app/auth/unauthorized/page.tsx +88 -0
  88. package/template/web/middle/src/app/layout.tsx +35 -0
  89. package/template/web/middle/src/app/middle/page.tsx +10 -0
  90. package/template/web/middle/src/app/page.tsx +20 -0
  91. package/template/web/middle/src/components/base/AuthGuard/Loading.tsx +19 -0
  92. package/template/web/middle/src/components/base/AuthGuard/index.tsx +45 -0
  93. package/template/web/middle/src/components/base/Layout/Header/UserAuthStatus.tsx +93 -0
  94. package/template/web/middle/src/components/base/Layout/Header/index.tsx +34 -0
  95. package/template/web/middle/src/components/base/Layout/Sidebar/index.tsx +103 -0
  96. package/template/web/middle/src/components/base/Layout/index.tsx +35 -0
  97. package/template/web/middle/src/components/base/Providers/MiddleProvider.tsx +33 -0
  98. package/template/web/middle/src/components/base/Providers/index.tsx +37 -0
  99. package/template/web/middle/src/env.js +57 -0
  100. package/template/web/middle/src/server/auth/next-auth/options.ts +26 -0
  101. package/template/web/middle/src/server/trpc/context.ts +22 -0
  102. package/template/web/middle/src/server/trpc/procedures.ts +16 -0
  103. package/template/web/middle/src/server/utils/index.ts +2 -0
  104. package/template/web/middle/src/server/utils/middle.ts +34 -0
  105. package/template/web/middle/src/types/index.ts +3 -0
  106. package/template/web/middle/src/utils/index.ts +5 -0
  107. package/template/web/middle/src/utils/middle.ts +28 -0
@@ -0,0 +1,37 @@
1
+ # ----------------------------
2
+ # 构建阶段
3
+ # ----------------------------
4
+ FROM oven/bun:latest AS builder
5
+
6
+ # 设置工作目录
7
+ ARG ROOT=app
8
+ WORKDIR /${ROOT}
9
+
10
+ COPY . .
11
+
12
+ # 替换 package.json 中的 workspace:* 为 *
13
+ RUN sed -i 's/"workspace:\*"/"*"/g' package.json
14
+
15
+ # 安装依赖
16
+ RUN bun install -f
17
+
18
+ # 构建
19
+ RUN bun -bun run build
20
+
21
+ # ----------------------------
22
+ # 运行阶段
23
+ # ----------------------------
24
+ FROM oven/bun:latest AS runner
25
+
26
+ ARG ROOT=app
27
+ WORKDIR /${ROOT}
28
+
29
+ # 只复制构建产物
30
+ COPY --from=builder /${ROOT}/.next/standalone ./
31
+ COPY --from=builder /${ROOT}/.next/static ./.next/static
32
+
33
+ # 开放端口
34
+ EXPOSE 3000
35
+
36
+ # 启动服务
37
+ CMD ["bun", "server.js"]
@@ -0,0 +1,2 @@
1
+ # 数据库配置
2
+ DATABASE_URL=postgresql://root:password.@localhost:5432/demo
@@ -0,0 +1,94 @@
1
+ import { defineConfig, globalIgnores } from 'eslint/config'
2
+ import nextVitals from 'eslint-config-next/core-web-vitals'
3
+ import nextTs from 'eslint-config-next/typescript'
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // 配置不参与 ESLint 检查的文件和目录
9
+ globalIgnores([
10
+ // 忽略 Next.js 构建产物目录
11
+ '.next/**',
12
+ // 忽略 Prisma 的 schema 文件
13
+ 'prisma/**',
14
+ // 忽略其他构建产物目录
15
+ 'build/**',
16
+ // 忽略 Next.js 自动生成的类型声明文件
17
+ 'next-env.d.ts',
18
+ // 忽略自动生成的 API 代码
19
+ 'src/server/generated/**',
20
+ ]),
21
+ {
22
+ // 指定这组规则生效的文件类型
23
+ files: ['**/*.{js,ts,tsx,mts,cts}'],
24
+ // 配置代码解析相关选项
25
+ languageOptions: {
26
+ // 配置解析器参数
27
+ parserOptions: {
28
+ // 让 TypeScript 按项目服务模式提供类型信息
29
+ projectService: true,
30
+ },
31
+ },
32
+ // 自定义需要关闭的 ESLint 规则
33
+ rules: {
34
+ // 允许匿名默认导出
35
+ 'import/no-anonymous-default-export': 'off',
36
+ // 允许在 effect 里调用 setState
37
+ 'react-hooks/set-state-in-effect': 'off',
38
+ // 不检查 hook 中的纯函数约束
39
+ 'react-hooks/purity': 'off',
40
+ // 不限制 hook 中对 ref 的使用方式
41
+ 'react-hooks/refs': 'off',
42
+ // 不强制 hook 中的数据保持不可变
43
+ 'react-hooks/immutability': 'off',
44
+ // 不强制检查 effect 依赖项是否完整
45
+ 'react-hooks/exhaustive-deps': 'off',
46
+ // 不强制组件设置 displayName
47
+ 'react/display-name': 'off',
48
+ // 允许基础类型直接转成字符串
49
+ '@typescript-eslint/no-base-to-string': 'off',
50
+ // 不强制数组类型写法统一
51
+ '@typescript-eslint/array-type': 'off',
52
+ // 不强制统一使用 interface 或 type
53
+ '@typescript-eslint/consistent-type-definitions': 'off',
54
+ // 不强制统一使用 type import
55
+ '@typescript-eslint/consistent-type-imports': 'off',
56
+ // 不检查未使用的变量
57
+ '@typescript-eslint/no-unused-vars': 'off',
58
+ // 允许没有 await 的 async 函数
59
+ '@typescript-eslint/require-await': 'off',
60
+ // 不严格检查 Promise 的误用场景
61
+ '@typescript-eslint/no-misused-promises': 'off',
62
+ // 允许使用 any 类型
63
+ '@typescript-eslint/no-explicit-any': 'off',
64
+ // 不强制优先使用 ?? 运算符
65
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
66
+ // 不检查不安全的对象属性访问
67
+ '@typescript-eslint/no-unsafe-member-access': 'off',
68
+ // 不检查不安全的函数参数传递
69
+ '@typescript-eslint/no-unsafe-argument': 'off',
70
+ // 允许使用 ts 注释指令
71
+ '@typescript-eslint/ban-ts-comment': 'off',
72
+ // 不检查不安全的函数调用
73
+ '@typescript-eslint/no-unsafe-call': 'off',
74
+ // 不检查不安全的赋值
75
+ '@typescript-eslint/no-unsafe-assignment': 'off',
76
+ // 不强制处理所有悬空 Promise
77
+ '@typescript-eslint/no-floating-promises': 'off',
78
+ // 不检查不安全的返回值
79
+ '@typescript-eslint/no-unsafe-return': 'off',
80
+ // 不检查多余的类型断言
81
+ '@typescript-eslint/no-unnecessary-type-assertion': 'off',
82
+ // 不强制 Promise reject 使用 Error 对象
83
+ '@typescript-eslint/prefer-promise-reject-errors': 'off',
84
+ // 允许使用 namespace
85
+ '@typescript-eslint/no-namespace': 'off',
86
+ // 不强制索引对象写法统一
87
+ '@typescript-eslint/consistent-indexed-object-style': 'off',
88
+ // 允许显式声明可推断的类型
89
+ '@typescript-eslint/no-inferrable-types': 'off',
90
+ },
91
+ },
92
+ ])
93
+
94
+ export default eslintConfig
@@ -0,0 +1,29 @@
1
+ // 只要 Next 读取配置,这个文件就会先执行一次。如果环境变量缺了或格式不对,项目会尽早报错,而不是等运行到某个接口时才炸。
2
+ import './src/env.js'
3
+
4
+ /** @type {import('next').NextConfig} */
5
+ const config = {
6
+ output: 'standalone', // 减小打包体积
7
+ logging: {
8
+ incomingRequests: false, // 控制台不显示每次请求的日志
9
+ },
10
+ poweredByHeader: false, // 响应头里不暴露 x-powered-by: Next.js
11
+ productionBrowserSourceMaps: false, // 生产环境不生成浏览器端的 .map 文件
12
+ // transpilePackages: [], // 即使这个包在 node_modules 里,也要把它当源码重新编译
13
+ experimental: {
14
+ serverSourceMaps: false, // 不生成服务端的 .map 文件
15
+ },
16
+ // 裁掉 Prisma 未使用的 wasm/edge runtime,保留 native query engine,减小 standalone 体积
17
+ outputFileTracingExcludes: {
18
+ '/**': [
19
+ 'node_modules/.bun/@prisma+client@*/node_modules/@prisma/client/runtime/query_*',
20
+ 'node_modules/.bun/@prisma+client@*/node_modules/@prisma/client/runtime/wasm-*',
21
+ 'node_modules/.bun/@prisma+client@*/node_modules/@prisma/client/runtime/edge*',
22
+ 'node_modules/.bun/@prisma+client@*/node_modules/.prisma/client/query_engine_bg.*',
23
+ 'node_modules/.bun/@prisma+client@*/node_modules/.prisma/client/wasm*',
24
+ 'node_modules/.bun/@prisma+client@*/node_modules/.prisma/client/edge.js',
25
+ ],
26
+ },
27
+ }
28
+
29
+ export default config
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "__WZYJS_APP_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "NODE_ENV=production next build --turbopack",
9
+ "start": "next start",
10
+ "generate": "rm -rf src/server/generated && zenstack generate --output ./src/server/generated/.zenstack --no-compile --schema ./prisma/schema.zmodel",
11
+ "db:push": "prisma db push",
12
+ "lint": "eslint .",
13
+ "typecheck": "tsc --noEmit",
14
+ "check": "bun run lint && bun run typecheck",
15
+ "postinstall": "bun run generate"
16
+ },
17
+ "dependencies": {
18
+ "@prisma/client": "^6.14.0",
19
+ "@t3-oss/env-nextjs": "^0.10.1",
20
+ "@tanstack/react-query": "^5.50.0",
21
+ "@trpc/client": "^11.7.0",
22
+ "@trpc/next": "^11.7.0",
23
+ "@trpc/react-query": "^11.7.0",
24
+ "@trpc/server": "^11.7.0",
25
+ "@wzyjs/hooks": "workspace:*",
26
+ "@wzyjs/next": "workspace:*",
27
+ "@wzyjs/uis": "workspace:*",
28
+ "@wzyjs/utils": "workspace:*",
29
+ "@zenstackhq/runtime": "2.22.1",
30
+ "@zenstackhq/trpc": "2.22.1",
31
+ "antd": "^6.3.1",
32
+ "next": "^16.2.9",
33
+ "react": "^19.1.0",
34
+ "react-dom": "^19.1.0",
35
+ "server-only": "^0.0.1",
36
+ "superjson": "^2.2.1",
37
+ "zenstack": "2.22.1",
38
+ "zod": "^3.23.3",
39
+ "zod-validation-error": "^3.4.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^20",
43
+ "@types/react": "19.1.10",
44
+ "@types/react-dom": "^19",
45
+ "eslint": "^9",
46
+ "eslint-config-next": "^16.2.9",
47
+ "postcss": "^8.4.39",
48
+ "prisma": "^6.14.0",
49
+ "prisma-json-types-generator": "^3.3.0",
50
+ "tailwindcss": "^3.4.3",
51
+ "typescript": "^5"
52
+ }
53
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ },
5
+ }
@@ -0,0 +1,8 @@
1
+ import "@wzyjs/next/Base"
2
+
3
+ model DemoItem extends Base {
4
+ title String
5
+ description String
6
+
7
+ @@allow('all', true)
8
+ }
@@ -0,0 +1,29 @@
1
+ //////////////////////////////////////////////////////////////////////////////////////////////
2
+ // DO NOT MODIFY THIS FILE //
3
+ // This file is automatically generated by ZenStack CLI and should not be manually updated. //
4
+ //////////////////////////////////////////////////////////////////////////////////////////////
5
+
6
+ datasource db {
7
+ provider = "postgresql"
8
+ url = env("DATABASE_URL")
9
+ }
10
+
11
+ generator client {
12
+ provider = "prisma-client-js"
13
+ output = "../src/server/generated/prisma-client"
14
+ }
15
+
16
+ generator json {
17
+ provider = "prisma-json-types-generator"
18
+ }
19
+
20
+ model DemoItem {
21
+ id String @id() @default(cuid())
22
+ createdAt DateTime @default(now())
23
+ updatedAt DateTime? @updatedAt()
24
+ sort Int?
25
+ enabled Boolean @default(true)
26
+ isDeleted Boolean @default(false)
27
+ title String
28
+ description String
29
+ }
@@ -0,0 +1 @@
1
+ import "./models/demo/DemoItem"
@@ -0,0 +1,24 @@
1
+ import type { NextRequest } from 'next/server'
2
+ import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
3
+
4
+ import { apiRouter } from '@/server/trpc'
5
+ import { createTRPCContext } from '@/server/trpc/context'
6
+ import { cors } from '@/server/utils'
7
+
8
+ const handler = async (req: NextRequest) => {
9
+ const response = await fetchRequestHandler({
10
+ req,
11
+ router: apiRouter,
12
+ endpoint: '/api/trpc',
13
+ createContext: () => createTRPCContext({ req }),
14
+ onError: ({ path, error }) => {
15
+ console.error(666, `❌ ${req.method} ${req.nextUrl.pathname} failed on ${path ?? '<no-path>'}: ${error.message}`)
16
+ },
17
+ })
18
+
19
+ return cors.applyCorsHeaders(req, response)
20
+ }
21
+
22
+ const OPTIONS = cors.createCorsPreflightResponse
23
+
24
+ export { handler as GET, handler as POST, OPTIONS }
@@ -0,0 +1,29 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+
5
+ import { Button, PanelPage, Result, StopOutlined, Typography } from '@/components'
6
+
7
+ export default function DisabledPage() {
8
+ return (
9
+ <PanelPage eyebrow='Access Disabled' title='账号已禁用'>
10
+ <Result
11
+ status='403'
12
+ icon={<StopOutlined style={{ color: '#f5222d' }} />}
13
+ title='当前账号已被禁用'
14
+ subTitle={(
15
+ <Typography.Paragraph className='!mb-0 !text-sm !leading-6 !text-slate-600 dark:!text-slate-300'>
16
+ 登录模块接入后,这里会展示当前账号在应用里的禁用状态。
17
+ </Typography.Paragraph>
18
+ )}
19
+ extra={(
20
+ <Link href='/auth/signin'>
21
+ <Button type='primary' danger className='!rounded-lg'>
22
+ 返回登录页
23
+ </Button>
24
+ </Link>
25
+ )}
26
+ />
27
+ </PanelPage>
28
+ )
29
+ }
@@ -0,0 +1,47 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { useSearchParams } from 'next/navigation'
5
+
6
+ import { Button, PanelPage, Result, Typography } from '@/components'
7
+
8
+ const errorMessageMap: Record<string, string> = {
9
+ OAuthSignin: '无法发起登录,请稍后再试。',
10
+ OAuthCallback: '登录回调失败,请检查回调地址配置。',
11
+ OAuthCreateAccount: '创建登录账号失败,请稍后再试。',
12
+ EmailCreateAccount: '创建登录账号失败,请稍后再试。',
13
+ Callback: '登录流程中断,请重新发起登录。',
14
+ OAuthAccountNotLinked: '当前邮箱已绑定其他登录方式。',
15
+ EmailSignin: '邮件登录失败。',
16
+ CredentialsSignin: '账号校验失败。',
17
+ SessionRequired: '当前页面需要先登录。',
18
+ default: '登录失败,请稍后重试。',
19
+ }
20
+
21
+ export default function AuthErrorPage() {
22
+ const searchParams = useSearchParams()
23
+ const error = searchParams.get('error') || 'default'
24
+
25
+ return (
26
+ <PanelPage eyebrow='Authentication Exception' title='登录失败'>
27
+ <Result
28
+ status='error'
29
+ title='本次认证没有成功完成'
30
+ subTitle={errorMessageMap[error]}
31
+ extra={[
32
+ <Link href='/auth/signin' key='signin'>
33
+ <Button type='primary' className='!rounded-lg px-6'>
34
+ 返回登录页
35
+ </Button>
36
+ </Link>,
37
+ ]}
38
+ />
39
+
40
+ <div className='mx-4 mb-4 rounded-lg bg-white/70 p-5 dark:bg-white/5 sm:mx-6'>
41
+ <Typography.Paragraph className='!mb-0 text-center !text-sm !leading-6 !text-slate-500 dark:!text-slate-300'>
42
+ 登录模块接入后,请检查对应的客户端配置、回调地址和会话密钥。
43
+ </Typography.Paragraph>
44
+ </div>
45
+ </PanelPage>
46
+ )
47
+ }
@@ -0,0 +1,53 @@
1
+ 'use client'
2
+
3
+ import { Suspense } from 'react'
4
+
5
+ import { useSearchParams } from 'next/navigation'
6
+
7
+ import { Button, PanelPage, Space, Spin, Typography } from '@/components'
8
+
9
+ const { Text } = Typography
10
+
11
+ const SignInPage = () => {
12
+ const searchParams = useSearchParams()
13
+ const error = searchParams.get('error')
14
+
15
+ return (
16
+ <PanelPage
17
+ eyebrow='Secure Sign In'
18
+ title='账号登录'
19
+ description='登录模块接入后,这里会展示对应的登录入口。'
20
+ >
21
+ <Space orientation='vertical' size='large' className='w-full'>
22
+ {error ? (
23
+ <div className='rounded-lg border border-red-200 bg-red-50/90 p-4 text-left dark:border-red-500/30 dark:bg-red-500/10'>
24
+ <div className='text-sm font-semibold text-red-700 dark:text-red-300'>
25
+ 登录流程没有完成
26
+ </div>
27
+ <p className='mt-2 text-sm leading-6 text-red-600 dark:text-red-200'>
28
+ 请重试一次;如果问题持续出现,优先检查登录配置和回调地址。
29
+ </p>
30
+ </div>
31
+ ) : null}
32
+
33
+ <Button type='primary' size='large' className='!h-12 !rounded-lg !text-sm !font-semibold shadow-sm' block disabled>
34
+ 登录模块待接入
35
+ </Button>
36
+
37
+ <div className='rounded-lg bg-white/70 p-5 text-center dark:bg-white/5'>
38
+ <Text className='text-sm leading-6 text-slate-500 dark:text-slate-300'>
39
+ 基础模板只保留页面骨架。使用 `google` 或 `middle` 模板后会替换为真实登录流程。
40
+ </Text>
41
+ </div>
42
+ </Space>
43
+ </PanelPage>
44
+ )
45
+ }
46
+
47
+ export default function SignIn() {
48
+ return (
49
+ <Suspense fallback={<Spin />}>
50
+ <SignInPage />
51
+ </Suspense>
52
+ )
53
+ }
@@ -0,0 +1,60 @@
1
+ 'use client'
2
+
3
+ import { useRouter, useSearchParams } from 'next/navigation'
4
+
5
+ import {
6
+ ArrowRightOutlined,
7
+ Button,
8
+ PanelPage,
9
+ Result,
10
+ RetweetOutlined,
11
+ StopOutlined,
12
+ Typography,
13
+ } from '@/components'
14
+
15
+ import { getSafeCallbackPath } from '@/utils'
16
+
17
+ export default function UnauthorizedPage() {
18
+ const router = useRouter()
19
+ const searchParams = useSearchParams()
20
+
21
+ const retryUrl = getSafeCallbackPath(searchParams.get('callbackUrl'), '/')
22
+
23
+ return (
24
+ <PanelPage eyebrow='Access Control' title='访问受限'>
25
+ <Result
26
+ status='403'
27
+ title='当前账号暂无访问权限'
28
+ icon={<StopOutlined style={{ color: '#f5222d' }} />}
29
+ subTitle={(
30
+ <Typography.Paragraph className='!mb-0 !text-sm !leading-6 !text-slate-600 dark:!text-slate-300'>
31
+ 登录模块接入后,这里会展示当前账号的权限状态。
32
+ </Typography.Paragraph>
33
+ )}
34
+ extra={(
35
+ <div className='flex w-full flex-col gap-3 sm:flex-row sm:justify-center'>
36
+ <Button
37
+ type='primary'
38
+ icon={<RetweetOutlined />}
39
+ className='!rounded-lg'
40
+ onClick={() => {
41
+ router.push(retryUrl)
42
+ }}
43
+ >
44
+ 重试
45
+ </Button>
46
+ <Button
47
+ icon={<ArrowRightOutlined />}
48
+ className='!rounded-lg'
49
+ onClick={() => {
50
+ router.push('/')
51
+ }}
52
+ >
53
+ 回到首页
54
+ </Button>
55
+ </div>
56
+ )}
57
+ />
58
+ </PanelPage>
59
+ )
60
+ }
@@ -0,0 +1,59 @@
1
+ 'use client'
2
+
3
+ import { Button } from '@/components'
4
+ import { api } from '@/utils'
5
+
6
+ export default function DemoPage() {
7
+ const itemsQuery = api.demoItem.findMany.useQuery({
8
+ orderBy: { createdAt: 'desc' },
9
+ select: {
10
+ id: true,
11
+ title: true,
12
+ description: true,
13
+ },
14
+ take: 20,
15
+ })
16
+
17
+ const createItem = api.demoItem.create.useMutation({
18
+ onSuccess: () => {
19
+ void itemsQuery.refetch()
20
+ },
21
+ })
22
+
23
+ const onCreate = () => {
24
+ void createItem.mutateAsync({
25
+ data: {
26
+ title: `示例记录 ${new Date().toLocaleTimeString('zh-CN')}`,
27
+ description: 'Created from middle template',
28
+ },
29
+ })
30
+ }
31
+
32
+ return (
33
+ <div className='mx-auto max-w-3xl px-5 py-8'>
34
+ <div className='mb-6 flex items-center justify-between gap-4'>
35
+ <h1 className='text-2xl font-semibold'>Demo Items</h1>
36
+ <Button type='primary' loading={createItem.isPending} onClick={onCreate}>
37
+ 新增示例
38
+ </Button>
39
+ </div>
40
+
41
+ {itemsQuery.isLoading ? (
42
+ <div className='text-sm text-slate-500 dark:text-[#aab4c2]'>加载中...</div>
43
+ ) : itemsQuery.data?.length ? (
44
+ <div className='space-y-3'>
45
+ {itemsQuery.data.map(item => (
46
+ <div key={item.id} className='rounded-lg border border-slate-200 bg-white p-4 dark:border-[#303846] dark:bg-[#171c23]'>
47
+ <div className='font-medium'>{item.title}</div>
48
+ <div className='mt-2 text-xs text-slate-500 dark:text-[#aab4c2]'>{item.description || '-'}</div>
49
+ </div>
50
+ ))}
51
+ </div>
52
+ ) : (
53
+ <div className='rounded-lg border border-slate-200 bg-white p-6 text-sm text-slate-500 dark:border-[#303846] dark:bg-[#171c23] dark:text-[#aab4c2]'>
54
+ 暂无数据,点“新增示例”创建一条。
55
+ </div>
56
+ )}
57
+ </div>
58
+ )
59
+ }
@@ -0,0 +1,32 @@
1
+ import { type ReactNode } from 'react'
2
+ import { type Metadata } from 'next'
3
+
4
+ import { Providers } from '@/components/base/Providers'
5
+ import { Layout } from '@/components/base/Layout'
6
+
7
+ import '@/styles/globals.css'
8
+
9
+ export const metadata: Metadata = {
10
+ title: '__WZYJS_APP_DISPLAY_NAME__',
11
+ icons: [{ rel: 'icon', url: '/favicon.ico' }],
12
+ }
13
+
14
+ interface RootLayoutProps {
15
+ children: ReactNode
16
+ }
17
+
18
+ export default function RootLayout(props: RootLayoutProps) {
19
+ const { children } = props
20
+
21
+ return (
22
+ <html lang='zh-CN' suppressHydrationWarning>
23
+ <body suppressHydrationWarning>
24
+ <Providers>
25
+ <Layout>
26
+ {children}
27
+ </Layout>
28
+ </Providers>
29
+ </body>
30
+ </html>
31
+ )
32
+ }
@@ -0,0 +1,21 @@
1
+ import Link from 'next/link'
2
+
3
+ export default function NotFound() {
4
+ return (
5
+ <main className='flex min-h-screen items-center justify-center bg-slate-50 px-5 py-8 transition-colors dark:bg-[#14171c]'>
6
+ <section className='w-full max-w-xl rounded-lg border border-slate-200 bg-white p-8 text-center shadow-sm transition-colors dark:border-[#303846] dark:bg-[#1b2028]'>
7
+ <p className='text-sm font-semibold uppercase text-slate-400 dark:text-slate-500'>404</p>
8
+ <h1 className='mt-3 text-3xl font-semibold text-slate-950 dark:text-[#e8edf5]'>页面没有找到</h1>
9
+ <p className='mt-4 text-sm leading-6 text-slate-500 dark:text-[#aab4c2]'>
10
+ 这个页面可能已经移动、删除,或者地址输入有误。
11
+ </p>
12
+ <Link
13
+ href='/'
14
+ className='mt-6 inline-flex h-10 items-center rounded-lg bg-slate-950 px-4 text-sm font-medium text-white transition-colors hover:bg-slate-800 dark:bg-[#e8edf5] dark:text-[#14171c] dark:hover:bg-white'
15
+ >
16
+ 回到首页
17
+ </Link>
18
+ </section>
19
+ </main>
20
+ )
21
+ }