diff --git a/docs/.vitepress/theme/router/index.ts b/docs/.vitepress/theme/router/index.ts
index 437f6b4..60e54d5 100644
--- a/docs/.vitepress/theme/router/index.ts
+++ b/docs/.vitepress/theme/router/index.ts
@@ -5,8 +5,9 @@ export const routers = [
{
text: '🌿 Vue',
items: [
- { text: 'Vue3-Common', link: '/Web/Vue/Vue3-Common' },
{ text: 'Vue3架构原理', link: '/Web/Vue/Vue3-Architecture' },
+ { text: 'Vue3自定义指令', link: '/Web/Vue/Vue3-Directive' },
+ { text: 'Vue3 defineModel', link: '/Web/Vue/Vue3-DefineModel' },
{ text: 'JavaScript知识点整理', link: '/Web/Vue/JavaScript-Guide' },
{ text: '前端包管理器', link: '/Web/Vue/Package' },
{ text: 'Vite核心原理', link: '/Web/Vue/Vite-Principle' },
@@ -27,7 +28,6 @@ export const routers = [
{ text: 'SpringBoot注解', link: '/Web/SpringBoot/SpringBoot-Annotation' },
{ text: 'SpringBoot3原生镜像', link: '/Web/SpringBoot/SpringBoot3-GraalVM' },
{ text: 'SpringBoot技巧', link: '/Web/SpringBoot/SpringBoot-Skills' },
- { text: 'SpringBoot Common', link: '/Web/SpringBoot/SpringBoot-Common' },
{ text: 'SpringBoot RestClient简介', link: '/Web/SpringBoot/SpringBoot-RestClient' },
{ text: 'SpringBoot Redis简介和使用', link: '/Web/SpringBoot/SpringBoot-Redis' },
{ text: 'SpringBoot 线程与事件', link: '/Web/SpringBoot/SpringBoot-Thread' }
@@ -88,7 +88,7 @@ export const routers = [
{
text: '🚀 运维部署',
items: [
- { text: 'OpenObserve部署和使用', link: '/DevOps/OpenObserve' },
+ { text: '⭐ OpenObserve部署和使用', link: '/DevOps/OpenObserve' },
{ text: 'Jenkins部署和使用', link: '/DevOps/Jenkins' },
{ text: 'ElasticSearch部署和使用', link: '/DevOps/ElasticSearch' },
{ text: 'Docker简介和安装', link: '/DevOps/Docker' },
@@ -98,6 +98,13 @@ export const routers = [
{
text: '🏗️ 项目实战',
items: [
+ {
+ text: '🏢 通用模块',
+ items: [
+ { text: 'Vue3 Common', link: '/Practice/Common/Vue3-Common' },
+ { text: 'SpringBoot Common', link: '/Practice/Common/SpringBoot-Common' },
+ ]
+ },
{
text: '🏢 公司项目',
items: [
diff --git a/docs/DevOps/Docker.md b/docs/DevOps/Docker.md
index 0c9de8e..62a22b6 100644
--- a/docs/DevOps/Docker.md
+++ b/docs/DevOps/Docker.md
@@ -1,7 +1,6 @@
---
title: Docker简介和安装
date: 2025-12-15
- isGreat: true
---
# 一、Docker简介
diff --git a/docs/DevOps/OpenObserve.md b/docs/DevOps/OpenObserve.md
index 61b16b7..ab0ce67 100644
--- a/docs/DevOps/OpenObserve.md
+++ b/docs/DevOps/OpenObserve.md
@@ -1,6 +1,7 @@
---
title: OpenObserve部署和使用
date: 2025-11-26
+ isGreat: true
---
# 一、基础概念
@@ -551,6 +552,40 @@ public class FluentBitLogger : MonoBehaviour
docker-compose文件中也要增加9880端口映射。
:::
+## 3.5 Vue
+ 发送HTTP数据到FluentBit中:
+```ts
+export const sendError = (err, instance, info) => {
+ try {
+ const log = {
+ top: 'vue-ui',
+ level: 'ERROR',
+ info,
+ component: instance?.$options?.name || 'Anonymous',
+ message: err.message,
+ url: location.href
+ }
+
+ fetch('http://ip:9880', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(log)
+ })
+ } catch {
+
+ }
+}
+```
+
+ 利用app.config.errorHandler监听异常:
+```ts
+app.config.errorHandler = (err, instance, info) => {
+ sendError(err, instance, info)
+}
+```
+
# 四、简单使用
## 3.1 配置数据流
打开控制台,选择数据流->添加数据流,输入fluent bit配置文件中的流名称,选择数据流类型为Logs。
diff --git a/docs/Web/SpringBoot/SpringBoot-Common.md b/docs/Practice/Common/SpringBoot-Common.md
similarity index 82%
rename from docs/Web/SpringBoot/SpringBoot-Common.md
rename to docs/Practice/Common/SpringBoot-Common.md
index 66d75ab..6d29356 100644
--- a/docs/Web/SpringBoot/SpringBoot-Common.md
+++ b/docs/Practice/Common/SpringBoot-Common.md
@@ -365,7 +365,7 @@ public abstract class AbstractException extends RuntimeException {
```
:::
-### 3.4 starters模块
+## 3.4 starters模块
该模块为整个项目的核心模块,在实际项目中,通过引入相应的starter模块,并配合autoconfigure模块中的功能,即可实现快速自动装配。
该模块的pom.xml声明的依赖会注入到实际的项目中。
::: danger
@@ -373,7 +373,7 @@ public abstract class AbstractException extends RuntimeException {
之前的SringBoot2,是在**main->resources->META-INF**目录下新建spring.factories文件。
:::
-#### 3.4.1 stater-jdbc
+### 3.4.1 stater-jdbc
该模块主要提供数据库相关的配置功能。
目前实现的功能有:
1. 配置MybatisPlus拦截器,添加乐观锁和分页插件。
@@ -450,7 +450,7 @@ public class MybatisMetaObjectHandler implements MetaObjectHandler {
`@ConditionalOnClass(SqlSessionFactory.class)`表示只有存在SqlSessionFactory类时,这个配置类才会生效。即如果没有 MyBatis 相关依赖,这个配置类会被 Spring 完全忽略。
`@Import`用于导入其他配置类或组件到当前配置类中。
-#### 3.4.2 starter-web
+### 3.4.2 starter-web
该模块主要提供web相关的配置功能。
目前实现的功能有:
1. 注册审计拦截器
@@ -748,6 +748,162 @@ HandlerInterceptor 拦截器执行顺序:
4. afterCompletion():视图渲染后 按注册顺序逆序执行。
:::
+### 3.4.3 starter-logging
+ 在`src->main->resources`中添加`logback-spring.xml`文件:
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DEBUG
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+
+ ${ENCODING}
+
+
+
+
+
+
+
+ INFO
+ ACCEPT
+ DENY
+
+
+ ${log.path}/log_info.log
+
+ ${FILE_LOG_PATTERN}
+ ${ENCODING}
+
+
+
+
+ ${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
+
+ 100MB
+
+ 15
+
+
+
+
+
+
+ WARN
+ ACCEPT
+ DENY
+
+
+ ${log.path}/log_warn.log
+
+ ${FILE_LOG_PATTERN}
+ ${ENCODING}
+
+
+
+ ${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
+
+ 100MB
+
+ 15
+
+
+
+
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+ ${log.path}/log_error.log
+
+ ${FILE_LOG_PATTERN}
+ ${ENCODING}
+
+
+
+ ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
+
+ 100MB
+
+ 15
+
+
+
+
+
+ ${fluentdHost}
+ ${fluentdPort}
+
+ topic
+ ${topic}
+
+ caller
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+ 该文件会根据当前环境自动添加日志输出节点。
+
# 四、项目发布
## 4.1 发布到Git中
diff --git a/docs/Web/Vue/Vue3-Common.md b/docs/Practice/Common/Vue3-Common.md
similarity index 71%
rename from docs/Web/Vue/Vue3-Common.md
rename to docs/Practice/Common/Vue3-Common.md
index aef6633..c469700 100644
--- a/docs/Web/Vue/Vue3-Common.md
+++ b/docs/Practice/Common/Vue3-Common.md
@@ -1,5 +1,5 @@
---
- title: Vue3-Common
+ title: Vue3 Common
date: 2025-12-15 23:25
---
@@ -32,7 +32,7 @@
:::
# 二、vite.config.ts配置
-## 2.1 生成Typescript类型文件
+## 2.1 生成ts类型文件
安装vite-plugin-dts插件
```cmd
@@ -377,6 +377,167 @@ const app = createApp(App)
app.use(router)
```
+8. 在layout中使用:
+```vue
+
+
+
+
+
+```
+
+ 如果需要对路由菜单进行权限处理,可以使用计算属性过滤一遍:
+```ts
+const routers = computed(() => {
+ const menuPathList = menuRoutes.map((item) => item.path)
+ const removePathList = menuPathList.filter((item) => !authStore.currentUser.menu.includes(item))
+
+ removePathList.forEach((item) => {
+ router.removeRoute(capitalizeStr(item.replace('/', '')))
+ })
+
+ return menuRoutes.filter((route) => !removePathList.includes(route.path))
+})
+```
+
+::: tip
+实现原理:
+```ts
+import type { RouteComponent, RouteRecordRaw, RouteMeta } from 'vue-router'
+
+/**
+ * 任意对象类型
+ */
+interface ICommonObj {
+ [key: string]: any;
+}
+
+/**
+ * 路由meta配置
+ */
+interface IRouteMetaConfig {
+ [key: string]: RouteMeta;
+}
+
+/**
+ * 路由路径转为名称 /admin/dashboard -> AdminDashboard
+ * @param path
+ */
+export const convertPath2Name = (path: string): string => {
+ // 移除路径开头和结尾的斜杠(如果存在)
+ const newPath = path.replace(/^\/|\/$/g, '')
+
+ // 将路径按斜杠分割成多个部分
+ const parts = newPath.split('/')
+
+ // 处理每个部分:将连字符后的字母大写,并移除连字符;处理动态参数(如 :id)
+ return parts.map((part) =>
+ part.split('-').map((subPart) => {
+ // 处理动态参数(如 :id)
+ if (subPart.startsWith(':')) {
+ return subPart.slice(1).charAt(0).toUpperCase() + subPart.slice(2)
+ }
+ // 普通部分
+ return subPart.charAt(0).toUpperCase() + subPart.slice(1)
+ }).join('')).join('')
+}
+
+/**
+ * 根据meta的order排序
+ * @param routes 路由列表
+ */
+export const sortRoutesByOrder = (routes: RouteRecordRaw[]): RouteRecordRaw[] => {
+ // 排序当前路由的子路由
+ routes.forEach((route) => {
+ if (route.children && route.children.length > 0) {
+ // 如果有子路由,递归排序子路由
+ route.children = sortRoutesByOrder(route.children)
+ }
+ })
+
+ // 对当前路由按 meta.order 排序
+ return routes.sort((a, b) => (a.meta?.order as number || 0) - (b.meta?.order as number || 0))
+}
+
+/**
+ * 获取路由
+ * @param modules 通过import.meta.glob导入的模块
+ * @param layout 布局组件
+ * @param metaConfig 路由meta配置
+ */
+export const getRoutersByModules = (modules: ICommonObj,
+ layout: RouteComponent, metaConfig?: IRouteMetaConfig): RouteRecordRaw[] => {
+ // const modules = import.meta.glob('@/views/admin/**/*.vue')
+ const routes: RouteRecordRaw[] = []
+
+ Object.keys(modules).forEach((filePath) => {
+ // 获取文件的模块路径和文件名
+ const parentPath = filePath.substring(0, filePath.lastIndexOf('/')).replace('/src/views', '')
+ const path = filePath.replace('.vue', '')
+ .replace('/src/views', '')
+ .replace(/\[([^\]]+)]/g, '/:$1')
+
+ // 为每个文件生成对应的路由
+ const route: RouteRecordRaw = {
+ path,
+ name: convertPath2Name(path),
+ component: modules[filePath]
+ }
+
+ // 如果存在meta配置
+ if (metaConfig) {
+ route.meta = metaConfig[path]
+
+ // 设置重定向属性
+ if (route.meta?.redirect) {
+ route.redirect = route.meta?.redirect as any
+ }
+ }
+
+ // 如果该路径已经存在(嵌套路由),则将其作为子路由
+ let parentRoute = routes.find((item) => item.path === parentPath)
+ if (!parentRoute) {
+ parentRoute = {
+ path: parentPath,
+ name: convertPath2Name(parentPath),
+ component: layout,
+ children: []
+ }
+
+ // 如果存在meta配置
+ if (metaConfig) {
+ parentRoute.meta = metaConfig[parentPath]
+
+ // 设置重定向属性
+ if (parentRoute.meta?.redirect) {
+ parentRoute.redirect = parentRoute.meta?.redirect
+ }
+ }
+
+ routes.push(parentRoute)
+ }
+
+ // 如果是子路由,加入到父路由的 `children` 数组
+ parentRoute.children?.push(route)
+ })
+
+ return routes
+}
+```
+
+ 核心原理就是利用`import.meta.glob`创建路径->组件的映射,然后结合路由配置表,转为路由对象并添加到`vue router`中。
+:::
+
## 5.2 图标组件
1. 安装vite-plugin-svg-icons依赖
```cmd
diff --git a/docs/Web/Vue/Vue3-DefineModel.md b/docs/Web/Vue/Vue3-DefineModel.md
new file mode 100644
index 0000000..8632d8e
--- /dev/null
+++ b/docs/Web/Vue/Vue3-DefineModel.md
@@ -0,0 +1,93 @@
+---
+ title: Vue3 defineModel
+ date: 2026-06-05
+---
+
+# 一、概念
+ defineModel是对 props + emit的语法糖,返回一个可写的 ref。Vu3.4开始支持。
+ 之前的做法:
+```ts
+// 子组件
+const props = defineProps({
+ modelValue: Number
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+function increment() {
+ emit('update:modelValue', props.modelValue + 1)
+}
+```
+
+ 现在做法:
+```ts
+const model = defineModel()
+```
+
+# 二、简单示例
+ 父组件:
+```vue
+
+
+
+
+
购买数量:{{ quantity }}
+
+
+
+```
+
+ 子组件:
+```vue
+
+
+
+
+ {{ quantity }}
+
+
+```
+
+ 和`defineProps`定义类似,可以指定类型和默认值。
+
+# 三、多个v-model
+ 父组件:
+```vue
+
+```
+
+ 子组件:
+```vue
+
+
+
+
+
+
+```
+
+::: tip
+defineProps: 只读,不能改
+defineModel: 可写
+:::
\ No newline at end of file
diff --git a/docs/Web/Vue/Vue3-Directive.md b/docs/Web/Vue/Vue3-Directive.md
new file mode 100644
index 0000000..b8901b6
--- /dev/null
+++ b/docs/Web/Vue/Vue3-Directive.md
@@ -0,0 +1,79 @@
+---
+ title: Vue3自定义指令
+ date: 2026-06-05
+---
+
+# 一、概念
+ 自定义指令(Directive) 是 Vue 提供的一种机制,用于直接操作 DOM,封装可复用的底层 DOM 行为。它是对模板能力的补充,当现有模板语法(v-if / v-bind / v-on)不够用时,就可以用指令。
+
+# 二、简单示例
+ 注册:
+```ts
+app.directive('demo', {
+ mounted(el, binding) {
+ console.log(el, binding)
+ }
+})
+```
+
+ 使用:
+```vue
+
+```
+
+ 使用该指令,就会触发打印效果。
+
+# 三、生命周期
+| 钩子 | 说明 |
+| ----- | ----- |
+| created | 绑定前 |
+| beforeMount | 元素挂载前 |
+| mounted | 元素已挂载 |
+| beforeUpdate | 更新前 |
+| updated | 更新后 |
+| beforeUnmount | 卸载前 |
+| unmounted | 卸载后 |
+
+# 四、参数
+ mounted(el, binding, vnode, prevVnode):
+| 参数 | 说明 |
+| ----- | ----- |
+| el | 当前 DOM 元素 |
+| binding.value | 指令绑定的值 |
+| binding.arg | 参数 |
+| binding.modifiers | 修饰符 |
+| vnode | 虚拟节点 |
+
+# 五、封装
+ 在`src`目录新建`directives/**.ts`,定义指令:
+```ts
+import { useAuthStore } from '@/stores/auth'
+
+export default {
+ mounted(el, binding) {
+ const authStore = useAuthStore()
+ const permission = binding.value
+
+ const hasPermission = authStore.currentUser?.permission?.includes(permission)
+
+ if (!hasPermission) {
+ el.parentNode?.removeChild(el)
+ }
+ }
+}
+```
+
+ 该指令根据用户权限控制DOM是否显示。
+
+ 然后在`main.ts`中注册指令:
+```ts
+import permissionDirective from '@/directives/permission'
+
+app.directive('permission', permissionDirective)
+```
+
+ 最后在需要的元素中加上`v-permission=""`即可。
+
+::: tip
+自定义指令只做DOM相关的事情,比如隐藏DOM,禁止点击、修改样式等等。
+:::