diff --git a/assets/css/transitions.css b/assets/css/transitions.css
index 2a46885..f4328f7 100644
--- a/assets/css/transitions.css
+++ b/assets/css/transitions.css
@@ -1,10 +1,43 @@
-/* 淡入淡出动画 */
-.fade-enter-active,
-.fade-leave-active {
+.indexFade-enter-active,
+.indexFade-leave-active {
transition: opacity 0.3s ease;
}
-.fade-enter-from,
-.fade-leave-to {
+.indexFade-enter-from,
+.indexFade-leave-to {
opacity: 0;
}
+/* 淡入淡出动画 */
+
+.slide-enter-active,
+.slide-leave-active {
+ transition: all 0.5s ease;
+}
+.slide-enter-from,
+.slide-leave-to {
+ opacity: 0;
+ transform: translate(100%, 0);
+}
+
+.slide-left-enter-active,
+.slide-left-leave-active,
+.slide-right-enter-active,
+.slide-right-leave-active {
+ transition: all 0.2s;
+}
+.slide-left-enter-from {
+ opacity: 0;
+ transform: translate(50px, 0);
+}
+.slide-left-leave-to {
+ opacity: 0;
+ transform: translate(-50px, 0);
+}
+.slide-right-enter-from {
+ opacity: 0;
+ transform: translate(-50px, 0);
+}
+.slide-right-leave-to {
+ opacity: 0;
+ transform: translate(50px, 0);
+}
\ No newline at end of file
diff --git a/docs/06-Nuxt生命周期.md b/docs/06-Nuxt生命周期.md
new file mode 100644
index 0000000..790e469
--- /dev/null
+++ b/docs/06-Nuxt生命周期.md
@@ -0,0 +1,111 @@
+# Nuxt HTTP 请求生命周期
+
+当一个 HTTP 请求发送到 Nuxt 应用时,它会经过一系列步骤,下面是完整的请求生命周期:
+
+## 服务器端处理
+
+### 1. Nitro 服务器初始化(仅一次)
+
+- Nitro 服务器启动并初始化 `/server/plugins` 目录下的插件
+- 这些插件只在服务器启动时执行一次
+- 在无服务器环境中,每个请求都会启动服务器,但插件不会被等待执行完成
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-1-setup-nitro-server-and-nitro-plugins-once)
+
+### 2. Nitro 服务器中间件
+
+- 对于每个请求,执行 `server/middleware/` 下的中间件
+- 这些中间件可用于身份验证、日志记录或请求转换
+- 如果中间件返回值,请求将终止并将返回值作为响应
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-2-nitro-server-middleware)
+
+### 3. 请求路由分发
+
+根据请求路径,Nitro 将请求分发到不同的处理程序:
+
+- 如果请求路径匹配 `/api/*`,则路由到 API 处理程序(`server/api/` 目录)
+- 如果请求路径匹配 `server/routes/` 中定义的路由,则路由到相应的处理程序
+- 如果是页面请求,则继续 Nuxt 应用的初始化
+
+[服务器目录结构](https://nuxt.com/docs/guide/directory-structure/server)
+
+### 4. API 处理(如果是 API 请求)
+
+- 如果请求匹配 `server/api/` 目录中的路由,相应的处理程序将被执行
+- 处理程序可以直接返回 JSON 数据、Promise 或使用 `event.node.res.end()` 发送响应
+- API 处理完成后,请求结束,不会继续到 Vue 应用
+
+[服务器 API](https://nuxt.com/docs/guide/directory-structure/server)
+
+### 5. Nuxt 应用初始化(如果是页面请求)
+
+- 创建 Vue 和 Nuxt 实例
+- 执行 Nuxt 服务器插件,包括内置插件和 `plugins/` 目录中的自定义插件
+- 调用 `app:created` 钩子
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-3-initialize-nuxt-and-execute-nuxt-app-plugins)
+
+### 6. 路由验证
+
+- 如果在 `definePageMeta` 中定义了 `validate` 方法,则调用该方法验证动态路由参数
+- 如果验证失败,可能会终止请求或重定向
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-4-route-validation)
+
+### 7. 执行 Nuxt 应用中间件
+
+- 执行全局路由中间件、命名路由中间件和匿名路由中间件
+- 中间件可以执行重定向,这会导致浏览器收到 `Location:` 头并发起新请求
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-5-execute-nuxt-app-middleware)
+
+### 8. 页面和组件设置
+
+- 初始化页面和组件
+- 使用 `useFetch` 和 `useAsyncData` 获取所需数据
+- 服务器端不执行 Vue 生命周期钩子如 `onBeforeMount`、`onMounted` 等
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-6-setup-page-and-components)
+
+### 9. 渲染和生成 HTML 输出
+
+- 将组件与 `unhead` 设置结合生成完整的 HTML 文档
+- 调用 `app:rendered` 钩子
+- 调用 `render:html` 钩子,允许操作生成的 HTML
+- 将 HTML 和相关数据发送回客户端
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-7-render-and-generate-html-output)
+
+## 客户端处理
+
+### 1. 执行 Nuxt 应用中间件
+
+- 中间件在服务器端和客户端都会运行
+- 可以使用 `import.meta.client` 和 `import.meta.server` 区分环境
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-3-execute-nuxt-app-middleware)
+
+### 2. 挂载 Vue 应用和水合
+
+- 调用 `app:beforeMount` 钩子
+- 调用 `app.mount('#__nuxt')` 将 Vue 应用挂载到 DOM
+- Vue 执行水合步骤,使客户端应用程序具有交互性
+- 调用 `app:mounted` 钩子
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-4-mount-vue-application-and-hydration)
+
+### 3. Vue 生命周期
+
+- 浏览器执行完整的 Vue 生命周期
+- 包括 `onBeforeMount`、`onMounted` 等钩子
+
+[Nuxt 生命周期](https://nuxt.com/docs/guide/concepts/nuxt-lifecycle#step-5-vue-lifecycle)
+
+## 重要说明
+
+- 服务器端重定向会导致浏览器收到 `Location:` 头并发起新请求,所有应用状态将重置
+- 中间件在服务器端渲染和客户端水合期间都会执行,可能导致代码执行两次
+- 对于 API 请求,建议使用 `useAsyncData`、`useFetch` 等 SSR 友好的组合式函数,确保服务器端获取的数据在水合期间被重用
+
+[通用渲染](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering)
\ No newline at end of file
diff --git a/drizzle/schema.ts b/drizzle/schema.ts
index 30e7ccd..edfb077 100644
--- a/drizzle/schema.ts
+++ b/drizzle/schema.ts
@@ -1,10 +1,10 @@
import { mysqlTable, index, primaryKey, unique, varchar, int, tinyint, datetime } from "drizzle-orm/mysql-core"
import { sql } from "drizzle-orm"
-import { bigintString } from './customType.js';
+import { bigintString } from '~/drizzle/customType';
const bigint = bigintString;
export const blogMenu = mysqlTable("blog_menu", {
id: bigint({ mode: "number" }).notNull(),
- pid: bigint({ mode: "number" }).notNull(),
+ pid: bigint({ mode: "number" }).default('0').notNull(),
name: varchar({ length: 255 }).notNull(),
desc: varchar({ length: 255 }),
icon: varchar({ length: 255 }).default("").notNull(),
diff --git a/layouts/home.vue b/layouts/home.vue
index 33054a0..e2f090d 100644
--- a/layouts/home.vue
+++ b/layouts/home.vue
@@ -5,11 +5,9 @@
@@ -38,7 +36,7 @@
overflow-y: auto;
}
- & > div.homeMainLeft {
+ & > .homeMainLeft {
flex: 1;
}
@@ -48,7 +46,7 @@
background-color: var(--bg-color-be);
}
- & > div.homeMainRight {
+ & > .homeMainRight {
flex: 1;
}
diff --git a/modules/my-imports.ts b/modules/my-imports.ts
new file mode 100644
index 0000000..88b7bba
--- /dev/null
+++ b/modules/my-imports.ts
@@ -0,0 +1,18 @@
+import { defineNuxtModule, addImportsSources } from '@nuxt/kit'
+
+export default defineNuxtModule({
+ meta: {
+ name: 'my-imports'
+ },
+ setup() {
+ addImportsSources({
+ from: 'consola',
+ imports: [
+ // 列出您需要从consola导入的函数
+ 'createConsola',
+ 'consola'
+ // 其他需要的函数...
+ ],
+ })
+ }
+})
\ No newline at end of file
diff --git a/nuxt.config.ts b/nuxt.config.ts
index c0c6a43..60aa9e8 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -13,6 +13,9 @@ export default defineNuxtConfig({
host: '0.0.0.0',
port: 3000,
},
+ features: {
+ devLogs: 'silent' // 这是默认值,确保它没有被设置为 true,日志区分server和client, false为不在client打印
+ },
css: [
'~/assets/css/style.css',
'~/assets/css/font.css',
@@ -40,12 +43,12 @@ export default defineNuxtConfig({
]
},
layoutTransition:{
- name: 'fade',
+ name: 'indexFade',
mode: 'out-in',
type: 'transition', // 明确指定动画类型
duration: {
- enter: 200,
- leave: 500
+ enter: 300,
+ leave: 300
},
appear: true
},
@@ -92,6 +95,11 @@ export default defineNuxtConfig({
'/docs/json'
],
},
+ defaultToken: {
+ username: process.env.USERNAME || 'expressgy',
+ nickname: process.env.PASSWORD || 'Nie',
+ userId: '1'
+ }
},
})
diff --git a/pages/home/blog/index.vue b/pages/home/blog/index.vue
index 3780e72..c8f8f41 100644
--- a/pages/home/blog/index.vue
+++ b/pages/home/blog/index.vue
@@ -1,13 +1,17 @@
- Blog
+ 内容SS{{data}}
\ No newline at end of file
diff --git a/pages/home/book/index.vue b/pages/home/book/index.vue
index 4974a96..f3e8293 100644
--- a/pages/home/book/index.vue
+++ b/pages/home/book/index.vue
@@ -1,6 +1,10 @@
diff --git a/pages/home/fun/index.vue b/pages/home/fun/index.vue
index d5fc4cb..2801b00 100644
--- a/pages/home/fun/index.vue
+++ b/pages/home/fun/index.vue
@@ -1,6 +1,10 @@
diff --git a/pages/home/hello/index.vue b/pages/home/hello/index.vue
index bcc1ff9..0164eee 100644
--- a/pages/home/hello/index.vue
+++ b/pages/home/hello/index.vue
@@ -1,6 +1,10 @@
diff --git a/pages/home/index.vue b/pages/home/index.vue
index a21b081..172a6e3 100644
--- a/pages/home/index.vue
+++ b/pages/home/index.vue
@@ -1,27 +1,42 @@
-
-
redisData: {{data}}
-
汇文明朝体 ""“”| 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
-
落霞孤鹜 ""“”| 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
-
nice ""“”| 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
-
TechnicLite ""“”| 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
-
星撰
-
6sans-serif ""“”| 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
-
default ""“”| 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
-
+
+
redisData: {{ data }}
+
汇文明朝体 ""“”|
+ 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
+
+
落霞孤鹜 ""“”|
+ 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
+
+
nice ""“”|
+ 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
+
+
TechnicLite ""“”|
+ 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
+
+
星撰
+
6sans-serif ""“”|
+ 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
+
+
default ""“”|
+ 星撰你好啊,你叫什么名字,ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
+
+
\ No newline at end of file
diff --git a/pages/index.vue b/pages/index.vue
index 67b173a..d725c1c 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -8,9 +8,10 @@ const leaveTime = ref(false)
// 离开前路由拦截
onBeforeRouteLeave((to, from, next) => {
leaveTime.value = true
+ consola.error('leave')
setTimeout(() => {
next(true)
- }, 1300)
+ }, 1000)
})
diff --git a/server/api/blog/blogMenu/[blogId].delete.ts b/server/api/blog/blogMenu/[blogId].delete.ts
index 675cda1..7d36572 100644
--- a/server/api/blog/blogMenu/[blogId].delete.ts
+++ b/server/api/blog/blogMenu/[blogId].delete.ts
@@ -1,3 +1,3 @@
export default defineEventHandler(event => {
- return 'Hello blog/blogMenu.[blogId].delete' + event.context.params.blogId;
+ return 'Hello blog/blogMenu.[blogId].delete' + event.context.params?.blogId;
})
diff --git a/server/api/blog/blogMenu/[blogId].patch.ts b/server/api/blog/blogMenu/[blogId].patch.ts
index 0fc841f..de8aeba 100644
--- a/server/api/blog/blogMenu/[blogId].patch.ts
+++ b/server/api/blog/blogMenu/[blogId].patch.ts
@@ -1,3 +1,3 @@
export default defineEventHandler(event => {
- return 'Hello blog/blogMenu.[blogId].patch' + event.context.params.blogId;
+ return 'Hello blog/blogMenu.[blogId].patch' + event.context.params?.blogId;
})
diff --git a/server/api/blog/blogMenu/[blogId].put.ts b/server/api/blog/blogMenu/[blogId].put.ts
index 41ca639..5a7d69d 100644
--- a/server/api/blog/blogMenu/[blogId].put.ts
+++ b/server/api/blog/blogMenu/[blogId].put.ts
@@ -1,3 +1,3 @@
export default defineEventHandler(event => {
- return 'Hello blog/blogMenu.[blogId].update' + event.context.params.blogId;
+ return 'Hello blog/blogMenu.[blogId].update' + event.context.params?.blogId;
})
diff --git a/server/api/blog/blogMenu/index.get.ts b/server/api/blog/blogMenu/index.get.ts
index 62b16e8..d50a39c 100644
--- a/server/api/blog/blogMenu/index.get.ts
+++ b/server/api/blog/blogMenu/index.get.ts
@@ -1,3 +1,12 @@
-export default defineEventHandler(event => {
- return 'Hello blog/blogMenu.get'
+import {BlogMenuDB} from "~/server/services/blog/blogMenu";
+import consola from "consola";
+
+export default defineEventHandler(async event => {
+ // 获取登录信息
+ const headerAuth = event.context.auth
+ // 初始化DB
+ const blogMenuDB = new BlogMenuDB(event)
+ const resd = await blogMenuDB.getBlogMenu(headerAuth)
+ consola.info(resd)
+ return resd
})
diff --git a/server/api/blog/blogMenu/index.post.ts b/server/api/blog/blogMenu/index.post.ts
index 4df4c78..9ce077a 100644
--- a/server/api/blog/blogMenu/index.post.ts
+++ b/server/api/blog/blogMenu/index.post.ts
@@ -1,3 +1,11 @@
-export default defineEventHandler(event => {
+export default defineEventHandler(async event => {
+ const requAuth = event.context.auth
+ const body = await readBody(event)
+ if(!requAuth.isTrue) {
+ // 判断为正常登录
+ throw createError({
+
+ })
+ }
return 'Hello blog/blogMenu.post'
})
diff --git a/server/api/hello.ts b/server/api/hello.ts
index 2736958..935448e 100644
--- a/server/api/hello.ts
+++ b/server/api/hello.ts
@@ -3,7 +3,6 @@ import consola from "consola";
export default defineEventHandler(async event => {
// console.log(await event.context.redis.get('SI HI'))
consola.info('API')
- await new Promise(resolve => setTimeout(resolve, 1000));
consola.info('API TimeOut')
const result = await event.context.redis.get('SI HI');
return {
diff --git a/server/middleware/02.auth.ts b/server/middleware/02.auth.ts
index 8d831b5..2b14461 100644
--- a/server/middleware/02.auth.ts
+++ b/server/middleware/02.auth.ts
@@ -2,10 +2,12 @@ import consola from "consola";
export default defineEventHandler(event => {
// 跳过无需验证的路由
if (event.path?.startsWith('/api/auth')) return
+ const {defaultToken} = useRuntimeConfig()
const token = getHeader(event, 'Authorization')?.split('Bearer ')[1];
const t = generateAccessToken({
- name: event.path,
+ ...defaultToken,
+ isTrue: false
})
consola.info(`Token ${t}`);
diff --git a/server/plugins/mysql.ts b/server/plugins/mysql.ts
index 93f8532..4ea0fbe 100644
--- a/server/plugins/mysql.ts
+++ b/server/plugins/mysql.ts
@@ -44,7 +44,8 @@ export default defineNitroPlugin(async nitroApp => {
const db = drizzle(pool,{
logger: {
logQuery: (query, params) => {
- consola.debug(`SQL: ${query} - Params: ${JSON.stringify(params)}`); // 打印日志,包括查询和参数
+ console.log(`SQL: ${query} - Params: ${JSON.stringify(params, (_, v) =>
+ typeof v === 'bigint' ? v.toString() : v)}`); // 打印日志,包括查询和参数
}
}
});
diff --git a/server/services/blog/blogMenu.ts b/server/services/blog/blogMenu.ts
new file mode 100644
index 0000000..f399b7c
--- /dev/null
+++ b/server/services/blog/blogMenu.ts
@@ -0,0 +1,40 @@
+import type {EventHandlerRequest, H3Event} from "h3";
+import type {MySql2Database, MySqlRawQueryResult} from "drizzle-orm/mysql2";
+import type {RedisClient} from "ioredis/built/connectors/SentinelConnector/types";
+import type {Pool} from "mysql2/promise";
+import type {HeaderAuth} from "~/types/nuxt.config";
+import {blogMenu} from "~/drizzle/schema";
+import {and, asc, desc, eq} from "drizzle-orm";
+
+type InsertBlogMenu = typeof blogMenu.$inferInsert;
+
+export class BlogMenuDB {
+ private readonly db:MySql2Database>& { $client: Pool }
+ private readonly redis: RedisClient
+ constructor(event: H3Event) {
+ this.db = event.context.mysql
+ this.redis = event.context.redis
+ }
+
+ // 插入目录
+ async insertBlogMenu(insertBlogMenu: InsertBlogMenu, headerAuth: HeaderAuth): Promise {
+ const result = await this.db.insert(blogMenu).values({
+ ...insertBlogMenu,
+ createdBy: headerAuth.userId,
+ })
+ return result;
+ }
+
+ // 查询目录
+ async getBlogMenu(headerAuth: HeaderAuth){
+ // 指定用户的
+ // 判断未删除的
+ // 如果是登陆的,可以查看未公开的,如果不是,只能查看公开的
+ return this.db
+ .select()
+ .from(blogMenu)
+ .where(
+ and(eq(blogMenu.createdBy, headerAuth.userId), eq(blogMenu.deleted, 0), headerAuth.isTrue ? undefined : eq(blogMenu.public, 1))
+ ).orderBy(asc(blogMenu.sort), desc(blogMenu.createdAt));
+ }
+}
\ No newline at end of file
diff --git a/server/utils/entity.ts b/server/utils/entity.ts
index 67d56b8..d711548 100644
--- a/server/utils/entity.ts
+++ b/server/utils/entity.ts
@@ -1,4 +1,4 @@
-import * as schema from "../../drizzle/schema";
+import * as schema from "~/drizzle/schema";
export function entity() {
return schema
}
diff --git a/server/utils/jwt.ts b/server/utils/jwt.ts
index 5da12be..fbb6b86 100644
--- a/server/utils/jwt.ts
+++ b/server/utils/jwt.ts
@@ -1,8 +1,6 @@
import jwt from 'jsonwebtoken'
import type {StringValue} from "ms";
const {sign, verify} = jwt
-console.log(jwt)
-// export function jwt() {}
export const generateAccessToken = (payload: object) => {
diff --git a/tsconfig.json b/tsconfig.json
index a746f2a..61c3b06 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,4 +1,9 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
- "extends": "./.nuxt/tsconfig.json"
+ "extends": "./.nuxt/tsconfig.json",
+ "compilerOptions": {
+ "paths": {
+// "~/*": ["./*"]
+ }
+ }
}
diff --git a/types/nuxt.config.d.ts b/types/nuxt.config.d.ts
index 9569a23..2100a85 100644
--- a/types/nuxt.config.d.ts
+++ b/types/nuxt.config.d.ts
@@ -9,3 +9,10 @@
// },
// }
// }
+
+export type HeaderAuth = {
+ username: string;
+ nickname: string;
+ userId: string;
+ isTrue: boolean;
+}
\ No newline at end of file