create-soybean 1.7.0 → 1.7.1

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 (148) hide show
  1. package/README.en_US.md +7 -5
  2. package/README.md +7 -5
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +27 -15
  5. package/package.json +12 -24
  6. package/template-ts-lib/_vite-hooks/pre-commit +1 -0
  7. package/template-ts-lib/package.json +40 -0
  8. package/template-ts-lib/pnpm-workspace.yaml +11 -0
  9. package/{template-tsdown → template-ts-lib}/tsconfig.json +7 -6
  10. package/template-ts-lib/vite.config.ts +22 -0
  11. package/template-ubean/README.md +20 -0
  12. package/template-ubean/_editorconfig +11 -0
  13. package/template-ubean/_gitattributes +2 -0
  14. package/template-ubean/_gitignore +32 -0
  15. package/template-ubean/_vite-hooks/pre-commit +1 -0
  16. package/template-ubean/eslint.config.mjs +3 -0
  17. package/template-ubean/package.json +38 -0
  18. package/template-ubean/pnpm-workspace.yaml +11 -0
  19. package/template-ubean/public/data.json +5 -0
  20. package/template-ubean/public/favicon.svg +64 -0
  21. package/template-ubean/src/app.ts +39 -0
  22. package/template-ubean/src/components/islands/IslandClock.vue +57 -0
  23. package/template-ubean/src/components/islands/IslandCounter.vue +66 -0
  24. package/template-ubean/src/components/islands/IslandMedia.vue +69 -0
  25. package/template-ubean/src/components/islands/IslandOnly.vue +54 -0
  26. package/template-ubean/src/components/islands/IslandVisibility.vue +60 -0
  27. package/template-ubean/src/layouts/admin.vue +13 -0
  28. package/template-ubean/src/layouts/default.vue +97 -0
  29. package/template-ubean/src/locales/en.json +31 -0
  30. package/template-ubean/src/locales/zh.json +31 -0
  31. package/template-ubean/src/middleware/01.global.ts +9 -0
  32. package/template-ubean/src/pages/about.vue +135 -0
  33. package/template-ubean/src/pages/blog/[...slug].vue +30 -0
  34. package/template-ubean/src/pages/dashboard/index.vue +19 -0
  35. package/template-ubean/src/pages/data-fetch.vue +95 -0
  36. package/template-ubean/src/pages/index.vue +117 -0
  37. package/template-ubean/src/pages/islands-test.vue +155 -0
  38. package/template-ubean/src/routes/api/hello.ts +60 -0
  39. package/template-ubean/src/routes/api/users/[id].ts +94 -0
  40. package/template-ubean/src/routes/api/users/index.ts +79 -0
  41. package/template-ubean/src/server.ts +9 -0
  42. package/template-ubean/src/typings/env.d.ts +37 -0
  43. package/template-ubean/tsconfig.json +26 -0
  44. package/template-ubean/ubean.config.ts +24 -0
  45. package/template-ubean/vite.config.ts +9 -0
  46. package/template-ubean-client/README.md +18 -0
  47. package/template-ubean-client/_gitignore +32 -0
  48. package/template-ubean-client/_vite-hooks/pre-commit +1 -0
  49. package/template-ubean-client/eslint.config.mjs +3 -0
  50. package/template-ubean-client/index.html +14 -0
  51. package/template-ubean-client/package.json +37 -0
  52. package/template-ubean-client/pnpm-workspace.yaml +11 -0
  53. package/template-ubean-client/public/favicon.svg +64 -0
  54. package/template-ubean-client/src/App.vue +135 -0
  55. package/template-ubean-client/src/main.ts +63 -0
  56. package/template-ubean-client/src/pages/404.vue +36 -0
  57. package/template-ubean-client/src/pages/about.vue +151 -0
  58. package/template-ubean-client/src/pages/about2.reuse.ts +8 -0
  59. package/template-ubean-client/src/pages/blog/[...slug].vue +72 -0
  60. package/template-ubean-client/src/pages/guide.md +30 -0
  61. package/template-ubean-client/src/pages/index.vue +101 -0
  62. package/template-ubean-client/src/pages/users/[id=numeric].vue +80 -0
  63. package/template-ubean-client/src/pages/users/index.vue +56 -0
  64. package/template-ubean-client/src/typings/globals.d.ts +17 -0
  65. package/template-ubean-client/tsconfig.json +25 -0
  66. package/template-ubean-client/vite.config.ts +20 -0
  67. package/template-vue/.gitignore +2 -0
  68. package/template-vue/_vite-hooks/pre-commit +1 -0
  69. package/template-vue/eslint.config.mjs +3 -0
  70. package/template-vue/package.json +17 -26
  71. package/template-vue/pnpm-workspace.yaml +11 -0
  72. package/template-vue/src/App.vue +3 -3
  73. package/template-vue/src/main.ts +2 -7
  74. package/template-vue/uno.config.ts +3 -3
  75. package/template-vue/vite.config.ts +10 -6
  76. package/template-vue-lib/_editorconfig +11 -0
  77. package/template-vue-lib/_gitattributes +2 -0
  78. package/template-vue-lib/_vite-hooks/pre-commit +1 -0
  79. package/template-vue-lib/eslint.config.mjs +3 -0
  80. package/template-vue-lib/package.json +53 -0
  81. package/template-vue-lib/pnpm-workspace.yaml +11 -0
  82. package/template-vue-lib/vite.config.ts +33 -0
  83. package/template-tsdown/_npmrc +0 -1
  84. package/template-tsdown/_oxfmtrc.json +0 -7
  85. package/template-tsdown/_oxlintrc.json +0 -16
  86. package/template-tsdown/package.json +0 -54
  87. package/template-tsdown/tsdown.config.ts +0 -10
  88. package/template-vue/_npmrc +0 -1
  89. package/template-vue/_oxfmtrc.json +0 -7
  90. package/template-vue/_oxlintrc.json +0 -17
  91. package/template-vue/er.config.ts +0 -9
  92. package/template-vue/src/components/HelloWorld.vue +0 -5
  93. package/template-vue/src/constants/index.ts +0 -0
  94. package/template-vue/src/layouts/base/index.vue +0 -9
  95. package/template-vue/src/layouts/blank/index.vue +0 -9
  96. package/template-vue/src/plugins/assets.ts +0 -4
  97. package/template-vue/src/router/index.ts +0 -25
  98. package/template-vue/src/shared/index.ts +0 -0
  99. package/template-vue/src/store/_plugin.ts +0 -17
  100. package/template-vue/src/store/counter/index.ts +0 -14
  101. package/template-vue/src/store/index.ts +0 -12
  102. package/template-vue/src/styles/app.css +0 -49
  103. package/template-vue/src/typings/components.d.ts +0 -17
  104. package/template-vue/src/views/(builtin)/403.vue +0 -5
  105. package/template-vue/src/views/(builtin)/404.vue +0 -5
  106. package/template-vue/src/views/(builtin)/login/index.vue +0 -5
  107. package/template-vue/src/views/(builtin)/wip.vue +0 -5
  108. package/template-vue/src/views/demo.tsx +0 -7
  109. package/template-vue/src/views/home/child.vue +0 -5
  110. package/template-vue/src/views/home/child2.vue +0 -5
  111. package/template-vue/src/views/home/index.vue +0 -9
  112. package/template-vue/src/views/list/[id].vue +0 -9
  113. package/template-vue/src/views/list/add.vue +0 -5
  114. package/template-vue/src/views/list/components/demo.vue +0 -0
  115. package/template-vue/src/views/list/components/search-list.vue +0 -5
  116. package/template-vue/src/views/list/detail2-[[id]]-[[userId]].vue +0 -5
  117. package/template-vue/src/views/list/detail_[id]_[userId].vue +0 -5
  118. package/template-vue/src/views/list/edit_[id].vue +0 -5
  119. package/template-vue/src/views/list/index.vue +0 -5
  120. package/template-vue/src/views/list/modules/list-table.vue +0 -5
  121. package/template-vue-tsdown/_npmrc +0 -1
  122. package/template-vue-tsdown/_oxfmtrc.json +0 -7
  123. package/template-vue-tsdown/_oxlintrc.json +0 -17
  124. package/template-vue-tsdown/package.json +0 -64
  125. package/template-vue-tsdown/tsdown.config.ts +0 -16
  126. package/template-vue-tsdown/vite.config.ts +0 -7
  127. /package/{template-tsdown → template-ts-lib}/.github/workflows/release.yml +0 -0
  128. /package/{template-tsdown → template-ts-lib}/.vscode/extensions.json +0 -0
  129. /package/{template-tsdown → template-ts-lib}/.vscode/launch.json +0 -0
  130. /package/{template-tsdown → template-ts-lib}/.vscode/settings.json +0 -0
  131. /package/{template-tsdown → template-ts-lib}/_editorconfig +0 -0
  132. /package/{template-tsdown → template-ts-lib}/_gitattributes +0 -0
  133. /package/{template-tsdown → template-ts-lib}/_gitignore +0 -0
  134. /package/{template-tsdown → template-ts-lib}/src/index.ts +0 -0
  135. /package/{template-vue-tsdown → template-ubean-client}/_editorconfig +0 -0
  136. /package/{template-vue-tsdown → template-ubean-client}/_gitattributes +0 -0
  137. /package/{template-vue-tsdown → template-vue-lib}/.github/workflows/release.yml +0 -0
  138. /package/{template-vue-tsdown → template-vue-lib}/.vscode/extensions.json +0 -0
  139. /package/{template-vue-tsdown → template-vue-lib}/.vscode/launch.json +0 -0
  140. /package/{template-vue-tsdown → template-vue-lib}/.vscode/settings.json +0 -0
  141. /package/{template-vue-tsdown → template-vue-lib}/_gitignore +0 -0
  142. /package/{template-vue-tsdown → template-vue-lib}/playground/index.html +0 -0
  143. /package/{template-vue-tsdown → template-vue-lib}/playground/src/App.vue +0 -0
  144. /package/{template-vue-tsdown → template-vue-lib}/playground/src/index.ts +0 -0
  145. /package/{template-vue-tsdown → template-vue-lib}/playground/src/style.css +0 -0
  146. /package/{template-vue-tsdown → template-vue-lib}/src/demo-pkg.vue +0 -0
  147. /package/{template-vue-tsdown → template-vue-lib}/src/index.ts +0 -0
  148. /package/{template-vue-tsdown → template-vue-lib}/tsconfig.json +0 -0
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted } from 'vue';
3
+
4
+ const count = ref(0);
5
+ const mountedAt = ref<string>('');
6
+
7
+ function increment() {
8
+ count.value++;
9
+ }
10
+
11
+ onMounted(() => {
12
+ mountedAt.value = new Date().toISOString();
13
+ });
14
+ </script>
15
+
16
+ <template>
17
+ <div class="island-counter">
18
+ <p class="island-label">client:load Island</p>
19
+ <p class="counter-value">{{ count }}</p>
20
+ <button @click="increment">+1</button>
21
+ <p v-if="mountedAt" class="mounted-info">Mounted at: {{ mountedAt }}</p>
22
+ </div>
23
+ </template>
24
+
25
+ <style scoped>
26
+ .island-counter {
27
+ padding: 1rem;
28
+ border: 2px solid #42b883;
29
+ border-radius: 8px;
30
+ background: #f0fdf4;
31
+ }
32
+
33
+ .island-label {
34
+ font-size: 0.8rem;
35
+ color: #15803d;
36
+ font-weight: 600;
37
+ margin-bottom: 0.5rem;
38
+ }
39
+
40
+ .counter-value {
41
+ font-size: 2rem;
42
+ font-weight: 700;
43
+ color: #166534;
44
+ }
45
+
46
+ button {
47
+ padding: 0.4rem 1rem;
48
+ background: #42b883;
49
+ color: white;
50
+ border: none;
51
+ border-radius: 4px;
52
+ cursor: pointer;
53
+ font-size: 0.9rem;
54
+ }
55
+
56
+ button:hover {
57
+ background: #35495e;
58
+ }
59
+
60
+ .mounted-info {
61
+ margin-top: 0.5rem;
62
+ font-size: 0.75rem;
63
+ color: #6b7280;
64
+ font-family: monospace;
65
+ }
66
+ </style>
@@ -0,0 +1,69 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, onUnmounted } from 'vue';
3
+
4
+ const isWide = ref(false);
5
+ const checks = ref(0);
6
+ let mql: MediaQueryList | null = null;
7
+
8
+ function onChange(e: MediaQueryListEvent) {
9
+ isWide.value = e.matches;
10
+ checks.value++;
11
+ }
12
+
13
+ onMounted(() => {
14
+ mql = window.matchMedia('(min-width: 768px)');
15
+ isWide.value = mql.matches;
16
+ mql.addEventListener('change', onChange);
17
+ });
18
+
19
+ onUnmounted(() => {
20
+ if (mql) mql.removeEventListener('change', onChange);
21
+ });
22
+ </script>
23
+
24
+ <template>
25
+ <div class="island-media">
26
+ <p class="island-label">client:media Island</p>
27
+ <p class="media-status" :class="{ wide: isWide, narrow: !isWide }">
28
+ {{ isWide ? '🖥 Wide screen (≥768px)' : '📱 Narrow screen (<768px)' }}
29
+ </p>
30
+ <p class="media-info">Media query changes: {{ checks }}</p>
31
+ <p class="media-hint">Resize browser to trigger media query changes</p>
32
+ </div>
33
+ </template>
34
+
35
+ <style scoped>
36
+ .island-media {
37
+ padding: 1rem;
38
+ border: 2px solid #8b5cf6;
39
+ border-radius: 8px;
40
+ background: #f5f3ff;
41
+ }
42
+
43
+ .island-label {
44
+ font-size: 0.8rem;
45
+ color: #6d28d9;
46
+ font-weight: 600;
47
+ margin-bottom: 0.5rem;
48
+ }
49
+
50
+ .media-status {
51
+ font-size: 1.1rem;
52
+ font-weight: 600;
53
+ }
54
+
55
+ .media-status.wide {
56
+ color: #166534;
57
+ }
58
+
59
+ .media-status.narrow {
60
+ color: #92400e;
61
+ }
62
+
63
+ .media-info,
64
+ .media-hint {
65
+ margin-top: 0.3rem;
66
+ font-size: 0.75rem;
67
+ color: #6b7280;
68
+ }
69
+ </style>
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted } from 'vue';
3
+
4
+ const clientTime = ref<string>('');
5
+
6
+ onMounted(() => {
7
+ clientTime.value = new Date().toISOString();
8
+ });
9
+ </script>
10
+
11
+ <template>
12
+ <div class="island-only">
13
+ <p class="island-label">client:only Island</p>
14
+ <p class="only-status">✓ Rendered on client only</p>
15
+ <p v-if="clientTime" class="only-time">Client time: {{ clientTime }}</p>
16
+ <p class="only-hint">This component was NOT server-side rendered</p>
17
+ </div>
18
+ </template>
19
+
20
+ <style scoped>
21
+ .island-only {
22
+ padding: 1rem;
23
+ border: 2px dashed #ec4899;
24
+ border-radius: 8px;
25
+ background: #fdf2f8;
26
+ }
27
+
28
+ .island-label {
29
+ font-size: 0.8rem;
30
+ color: #be185d;
31
+ font-weight: 600;
32
+ margin-bottom: 0.5rem;
33
+ }
34
+
35
+ .only-status {
36
+ font-size: 1.1rem;
37
+ font-weight: 600;
38
+ color: #166534;
39
+ }
40
+
41
+ .only-time {
42
+ margin-top: 0.3rem;
43
+ font-size: 0.75rem;
44
+ color: #6b7280;
45
+ font-family: monospace;
46
+ }
47
+
48
+ .only-hint {
49
+ margin-top: 0.3rem;
50
+ font-size: 0.75rem;
51
+ color: #9ca3af;
52
+ font-style: italic;
53
+ }
54
+ </style>
@@ -0,0 +1,60 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted } from 'vue';
3
+
4
+ const visible = ref(false);
5
+ const visibleCount = ref(0);
6
+ const firstVisibleAt = ref<string>('');
7
+
8
+ onMounted(() => {
9
+ // The IntersectionObserver is set up by the bootstrap script;
10
+ // once the island is hydrated, we mark it as visible.
11
+ visible.value = true;
12
+ visibleCount.value = 1;
13
+ firstVisibleAt.value = new Date().toISOString();
14
+ });
15
+ </script>
16
+
17
+ <template>
18
+ <div class="island-visibility">
19
+ <p class="island-label">client:visible Island</p>
20
+ <p class="vis-status" :class="{ active: visible }">
21
+ {{ visible ? '✓ Visible & Hydrated' : '○ Waiting for visibility...' }}
22
+ </p>
23
+ <p v-if="firstVisibleAt" class="vis-time">First visible: {{ firstVisibleAt }}</p>
24
+ <p class="vis-count">Visibility events: {{ visibleCount }}</p>
25
+ </div>
26
+ </template>
27
+
28
+ <style scoped>
29
+ .island-visibility {
30
+ padding: 1rem;
31
+ border: 2px solid #f59e0b;
32
+ border-radius: 8px;
33
+ background: #fffbeb;
34
+ }
35
+
36
+ .island-label {
37
+ font-size: 0.8rem;
38
+ color: #b45309;
39
+ font-weight: 600;
40
+ margin-bottom: 0.5rem;
41
+ }
42
+
43
+ .vis-status {
44
+ font-size: 1.1rem;
45
+ font-weight: 600;
46
+ color: #92400e;
47
+ }
48
+
49
+ .vis-status.active {
50
+ color: #166534;
51
+ }
52
+
53
+ .vis-time,
54
+ .vis-count {
55
+ margin-top: 0.3rem;
56
+ font-size: 0.75rem;
57
+ color: #6b7280;
58
+ font-family: monospace;
59
+ }
60
+ </style>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <div class="src-layouts-admin-layout">
5
+ <PageView />
6
+ </div>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .src-layouts-admin-layout {
11
+ min-height: 100vh;
12
+ }
13
+ </style>
@@ -0,0 +1,97 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <div class="layout">
5
+ <header class="header">
6
+ <nav class="nav">
7
+ <Link to="/" class="logo">ubean-app</Link>
8
+ <div class="nav-links">
9
+ <Link to="/">首页</Link>
10
+ <Link to="/about">关于</Link>
11
+ <Link to="/data-fetch">数据请求</Link>
12
+ <Link to="/islands-test">Islands</Link>
13
+ <Link to="/blog/hello">博客</Link>
14
+ </div>
15
+ </nav>
16
+ </header>
17
+ <main class="main">
18
+ <PageView />
19
+ </main>
20
+ <footer class="footer">
21
+ <p>ubean 应用模板</p>
22
+ </footer>
23
+ </div>
24
+ </template>
25
+
26
+ <style>
27
+ * {
28
+ margin: 0;
29
+ padding: 0;
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ .layout {
34
+ min-height: 100vh;
35
+ display: flex;
36
+ flex-direction: column;
37
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
38
+ }
39
+
40
+ .header {
41
+ background: linear-gradient(135deg, #42b883, #35495e);
42
+ padding: 0 20px;
43
+ }
44
+
45
+ .nav {
46
+ max-width: 960px;
47
+ margin: 0 auto;
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: space-between;
51
+ height: 60px;
52
+ }
53
+
54
+ .logo {
55
+ color: white;
56
+ font-size: 1.3rem;
57
+ font-weight: 700;
58
+ text-decoration: none;
59
+ }
60
+
61
+ .nav-links {
62
+ display: flex;
63
+ gap: 1.5rem;
64
+ }
65
+
66
+ .nav-links a {
67
+ color: rgba(255, 255, 255, 0.9);
68
+ text-decoration: none;
69
+ font-weight: 500;
70
+ font-size: 0.95rem;
71
+ transition: color 0.2s;
72
+ }
73
+
74
+ .nav-links a:hover {
75
+ color: white;
76
+ }
77
+
78
+ .main {
79
+ flex: 1;
80
+ max-width: 960px;
81
+ margin: 0 auto;
82
+ padding: 40px 20px;
83
+ width: 100%;
84
+ }
85
+
86
+ .footer {
87
+ background: #2c3e50;
88
+ color: rgba(255, 255, 255, 0.7);
89
+ text-align: center;
90
+ padding: 1.2rem;
91
+ }
92
+
93
+ .footer p {
94
+ margin: 0;
95
+ font-size: 0.9rem;
96
+ }
97
+ </style>
@@ -0,0 +1,31 @@
1
+ {
2
+ "app": {
3
+ "title": "Ubean Test",
4
+ "description": "A test project for ubean framework"
5
+ },
6
+ "common": {
7
+ "hello": "Hello, {name}!",
8
+ "welcome": "Welcome to ubean",
9
+ "goodbye": "Goodbye"
10
+ },
11
+ "items": {
12
+ "count": "You have {count} item | You have {count} items",
13
+ "explicit": "No items | One item | Many items",
14
+ "categorized": "zero: No items | one: One item | other: {count} items"
15
+ },
16
+ "navigation": {
17
+ "home": "Home",
18
+ "about": "About",
19
+ "features": "Features",
20
+ "dashboard": "Dashboard"
21
+ },
22
+ "linked": {
23
+ "greeting": "Hello, @:common.hello",
24
+ "nested": "@:navigation.home page"
25
+ },
26
+ "messages": {
27
+ "loading": "Loading...",
28
+ "error": "An error occurred",
29
+ "success": "Operation completed successfully"
30
+ }
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "app": {
3
+ "title": "Ubean 测试",
4
+ "description": "ubean 框架测试项目"
5
+ },
6
+ "common": {
7
+ "hello": "你好,{name}!",
8
+ "welcome": "欢迎使用 ubean",
9
+ "goodbye": "再见"
10
+ },
11
+ "items": {
12
+ "count": "你有 {count} 个项目",
13
+ "explicit": "没有项目 | 一个项目 | 很多项目",
14
+ "categorized": "zero: 没有项目 | one: 一个项目 | other: {count} 个项目"
15
+ },
16
+ "navigation": {
17
+ "home": "首页",
18
+ "about": "关于",
19
+ "features": "功能",
20
+ "dashboard": "仪表盘"
21
+ },
22
+ "linked": {
23
+ "greeting": "你好,@:common.hello",
24
+ "nested": "@:navigation.home 页面"
25
+ },
26
+ "messages": {
27
+ "loading": "加载中...",
28
+ "error": "发生错误",
29
+ "success": "操作成功完成"
30
+ }
31
+ }
@@ -0,0 +1,9 @@
1
+ import { defineMiddleware } from 'ubean/server';
2
+
3
+ export default defineMiddleware(async (c, next) => {
4
+ const start = Date.now();
5
+ await next();
6
+ const duration = Date.now() - start;
7
+ c.header('X-Test-Middleware', 'ubean-test-global');
8
+ c.header('X-Response-Time', `${duration}ms`);
9
+ });
@@ -0,0 +1,135 @@
1
+ <script setup lang="ts">
2
+ definePage({
3
+ head: {
4
+ title: '关于 - ubean-test',
5
+ meta: [{ name: 'description', content: 'ubean 框架功能测试项目介绍页' }]
6
+ }
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <div class="about">
12
+ <h1>关于 ubean-test</h1>
13
+ <p>这是一个用于验证 ubean 框架各项功能的测试项目。</p>
14
+
15
+ <section class="info-section">
16
+ <h2>测试的功能模块</h2>
17
+ <div class="feature-list">
18
+ <div class="feature-item">
19
+ <span class="check">✅</span>
20
+ <span>文件系统路由 (API + Pages)</span>
21
+ </div>
22
+ <div class="feature-item">
23
+ <span class="check">✅</span>
24
+ <span>布局系统 (Layouts)</span>
25
+ </div>
26
+ <div class="feature-item">
27
+ <span class="check">✅</span>
28
+ <span>SSR 服务端渲染</span>
29
+ </div>
30
+ <div class="feature-item">
31
+ <span class="check">✅</span>
32
+ <span>客户端导航</span>
33
+ </div>
34
+ <div class="feature-item">
35
+ <span class="check">✅</span>
36
+ <span>API 路由与验证器</span>
37
+ </div>
38
+ <div class="feature-item">
39
+ <span class="check">✅</span>
40
+ <span>中间件系统</span>
41
+ </div>
42
+ <div class="feature-item">
43
+ <span class="check">✅</span>
44
+ <span>环境变量管理 (defineEnv)</span>
45
+ </div>
46
+ <div class="feature-item">
47
+ <span class="check">✅</span>
48
+ <span>定时任务 (Cron)</span>
49
+ </div>
50
+ <div class="feature-item">
51
+ <span class="check">✅</span>
52
+ <span>国际化 (i18n)</span>
53
+ </div>
54
+ <div class="feature-item">
55
+ <span class="check">✅</span>
56
+ <span>错误处理</span>
57
+ </div>
58
+ <div class="feature-item">
59
+ <span class="check">✅</span>
60
+ <span>响应工具 (json/text/html/redirect)</span>
61
+ </div>
62
+ <div class="feature-item">
63
+ <span class="check">✅</span>
64
+ <span>SEO (robots/sitemap)</span>
65
+ </div>
66
+ <div class="feature-item">
67
+ <span class="check">✅</span>
68
+ <span>DevTools</span>
69
+ </div>
70
+ </div>
71
+ </section>
72
+
73
+ <Link to="/" class="back-link">← 返回首页</Link>
74
+ </div>
75
+ </template>
76
+
77
+ <style scoped>
78
+ .about h1 {
79
+ color: #42b883;
80
+ font-size: 2rem;
81
+ margin-bottom: 1rem;
82
+ }
83
+
84
+ .about p {
85
+ color: #555;
86
+ font-size: 1.1rem;
87
+ line-height: 1.7;
88
+ margin-bottom: 2rem;
89
+ }
90
+
91
+ .info-section {
92
+ margin: 2rem 0;
93
+ }
94
+
95
+ .info-section h2 {
96
+ color: #35495e;
97
+ font-size: 1.4rem;
98
+ margin-bottom: 1.2rem;
99
+ border-bottom: 2px solid #42b883;
100
+ padding-bottom: 0.5rem;
101
+ }
102
+
103
+ .feature-list {
104
+ display: grid;
105
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
106
+ gap: 0.6rem;
107
+ }
108
+
109
+ .feature-item {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 0.6rem;
113
+ padding: 0.6rem 0.8rem;
114
+ background: #f0f9f4;
115
+ border-radius: 6px;
116
+ font-size: 0.95rem;
117
+ color: #444;
118
+ }
119
+
120
+ .check {
121
+ font-size: 1rem;
122
+ }
123
+
124
+ .back-link {
125
+ display: inline-block;
126
+ margin-top: 2rem;
127
+ color: #42b883;
128
+ text-decoration: none;
129
+ font-weight: 500;
130
+ }
131
+
132
+ .back-link:hover {
133
+ text-decoration: underline;
134
+ }
135
+ </style>
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+ import { useRoute } from 'vue-router';
4
+
5
+ definePage({
6
+ path: '/blog/:slug(.*)*'
7
+ });
8
+
9
+ const route = useRoute('BlogAllSlug');
10
+ const slug = computed(() => {
11
+ const value = route.params.slug;
12
+ if (Array.isArray(value)) return value.filter(Boolean).join('/');
13
+ return typeof value === 'string' ? value : '';
14
+ });
15
+ </script>
16
+
17
+ <template>
18
+ <article>
19
+ <h1>Blog {{ slug }}</h1>
20
+ <p>
21
+ Catch-all file route for
22
+ <code>@ubean/content</code>
23
+ . Prerender URLs come from
24
+ <code>discoverContentPageRoutes</code>
25
+ /
26
+ <code>extractContentPageRoutes</code>
27
+ .
28
+ </p>
29
+ </article>
30
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ useHead({
3
+ title: '控制台'
4
+ });
5
+ </script>
6
+
7
+ <template>
8
+ <div class="dashboard-page">
9
+ <h1>控制台首页</h1>
10
+ <p>
11
+ 使用 admin 布局的页面示例 -
12
+ <code>/dashboard</code>
13
+ (见
14
+ <code>src/layouts/admin.vue</code>
15
+ )
16
+ </p>
17
+ <Link to="/">← 返回首页</Link>
18
+ </div>
19
+ </template>