feat:整理博客目录结构
This commit is contained in:
@@ -5,8 +5,9 @@ export const routers = [
|
|||||||
{
|
{
|
||||||
text: '🌿 Vue',
|
text: '🌿 Vue',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Vue3-Common', link: '/Web/Vue/Vue3-Common' },
|
|
||||||
{ text: 'Vue3架构原理', link: '/Web/Vue/Vue3-Architecture' },
|
{ 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: 'JavaScript知识点整理', link: '/Web/Vue/JavaScript-Guide' },
|
||||||
{ text: '前端包管理器', link: '/Web/Vue/Package' },
|
{ text: '前端包管理器', link: '/Web/Vue/Package' },
|
||||||
{ text: 'Vite核心原理', link: '/Web/Vue/Vite-Principle' },
|
{ text: 'Vite核心原理', link: '/Web/Vue/Vite-Principle' },
|
||||||
@@ -27,7 +28,6 @@ export const routers = [
|
|||||||
{ text: 'SpringBoot注解', link: '/Web/SpringBoot/SpringBoot-Annotation' },
|
{ text: 'SpringBoot注解', link: '/Web/SpringBoot/SpringBoot-Annotation' },
|
||||||
{ text: 'SpringBoot3原生镜像', link: '/Web/SpringBoot/SpringBoot3-GraalVM' },
|
{ text: 'SpringBoot3原生镜像', link: '/Web/SpringBoot/SpringBoot3-GraalVM' },
|
||||||
{ text: 'SpringBoot技巧', link: '/Web/SpringBoot/SpringBoot-Skills' },
|
{ 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 RestClient简介', link: '/Web/SpringBoot/SpringBoot-RestClient' },
|
||||||
{ text: 'SpringBoot Redis简介和使用', link: '/Web/SpringBoot/SpringBoot-Redis' },
|
{ text: 'SpringBoot Redis简介和使用', link: '/Web/SpringBoot/SpringBoot-Redis' },
|
||||||
{ text: 'SpringBoot 线程与事件', link: '/Web/SpringBoot/SpringBoot-Thread' }
|
{ text: 'SpringBoot 线程与事件', link: '/Web/SpringBoot/SpringBoot-Thread' }
|
||||||
@@ -88,7 +88,7 @@ export const routers = [
|
|||||||
{
|
{
|
||||||
text: '🚀 运维部署',
|
text: '🚀 运维部署',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'OpenObserve部署和使用', link: '/DevOps/OpenObserve' },
|
{ text: '⭐ OpenObserve部署和使用', link: '/DevOps/OpenObserve' },
|
||||||
{ text: 'Jenkins部署和使用', link: '/DevOps/Jenkins' },
|
{ text: 'Jenkins部署和使用', link: '/DevOps/Jenkins' },
|
||||||
{ text: 'ElasticSearch部署和使用', link: '/DevOps/ElasticSearch' },
|
{ text: 'ElasticSearch部署和使用', link: '/DevOps/ElasticSearch' },
|
||||||
{ text: 'Docker简介和安装', link: '/DevOps/Docker' },
|
{ text: 'Docker简介和安装', link: '/DevOps/Docker' },
|
||||||
@@ -98,6 +98,13 @@ export const routers = [
|
|||||||
{
|
{
|
||||||
text: '🏗️ 项目实战',
|
text: '🏗️ 项目实战',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
text: '🏢 通用模块',
|
||||||
|
items: [
|
||||||
|
{ text: 'Vue3 Common', link: '/Practice/Common/Vue3-Common' },
|
||||||
|
{ text: 'SpringBoot Common', link: '/Practice/Common/SpringBoot-Common' },
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: '🏢 公司项目',
|
text: '🏢 公司项目',
|
||||||
items: [
|
items: [
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Docker简介和安装
|
title: Docker简介和安装
|
||||||
date: 2025-12-15
|
date: 2025-12-15
|
||||||
isGreat: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 一、Docker简介
|
# 一、Docker简介
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: OpenObserve部署和使用
|
title: OpenObserve部署和使用
|
||||||
date: 2025-11-26
|
date: 2025-11-26
|
||||||
|
isGreat: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 一、基础概念
|
# 一、基础概念
|
||||||
@@ -551,6 +552,40 @@ public class FluentBitLogger : MonoBehaviour
|
|||||||
docker-compose文件中也要增加9880端口映射。
|
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 配置数据流
|
## 3.1 配置数据流
|
||||||
  打开控制台,选择数据流->添加数据流,输入fluent bit配置文件中的流名称,选择数据流类型为Logs。
|
  打开控制台,选择数据流->添加数据流,输入fluent bit配置文件中的流名称,选择数据流类型为Logs。
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ public abstract class AbstractException extends RuntimeException {
|
|||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### 3.4 starters模块
|
## 3.4 starters模块
|
||||||
  该模块为整个项目的核心模块,在实际项目中,通过引入相应的starter模块,并配合autoconfigure模块中的功能,即可实现快速自动装配。
|
  该模块为整个项目的核心模块,在实际项目中,通过引入相应的starter模块,并配合autoconfigure模块中的功能,即可实现快速自动装配。
|
||||||
  该模块的pom.xml声明的依赖会注入到实际的项目中。
|
  该模块的pom.xml声明的依赖会注入到实际的项目中。
|
||||||
::: danger
|
::: danger
|
||||||
@@ -373,7 +373,7 @@ public abstract class AbstractException extends RuntimeException {
|
|||||||
之前的SringBoot2,是在**main->resources->META-INF**目录下新建spring.factories文件。
|
之前的SringBoot2,是在**main->resources->META-INF**目录下新建spring.factories文件。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
#### 3.4.1 stater-jdbc
|
### 3.4.1 stater-jdbc
|
||||||
  该模块主要提供数据库相关的配置功能。
|
  该模块主要提供数据库相关的配置功能。
|
||||||
  目前实现的功能有:
|
  目前实现的功能有:
|
||||||
1. 配置MybatisPlus拦截器,添加乐观锁和分页插件。
|
1. 配置MybatisPlus拦截器,添加乐观锁和分页插件。
|
||||||
@@ -450,7 +450,7 @@ public class MybatisMetaObjectHandler implements MetaObjectHandler {
|
|||||||
  `@ConditionalOnClass(SqlSessionFactory.class)`表示只有存在SqlSessionFactory类时,这个配置类才会生效。即如果没有 MyBatis 相关依赖,这个配置类会被 Spring 完全忽略。
|
  `@ConditionalOnClass(SqlSessionFactory.class)`表示只有存在SqlSessionFactory类时,这个配置类才会生效。即如果没有 MyBatis 相关依赖,这个配置类会被 Spring 完全忽略。
|
||||||
  `@Import`用于导入其他配置类或组件到当前配置类中。
|
  `@Import`用于导入其他配置类或组件到当前配置类中。
|
||||||
|
|
||||||
#### 3.4.2 starter-web
|
### 3.4.2 starter-web
|
||||||
  该模块主要提供web相关的配置功能。
|
  该模块主要提供web相关的配置功能。
|
||||||
  目前实现的功能有:
|
  目前实现的功能有:
|
||||||
1. 注册审计拦截器
|
1. 注册审计拦截器
|
||||||
@@ -748,6 +748,162 @@ HandlerInterceptor 拦截器执行顺序:
|
|||||||
4. afterCompletion():视图渲染后 按注册顺序逆序执行。
|
4. afterCompletion():视图渲染后 按注册顺序逆序执行。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### 3.4.3 starter-logging
|
||||||
|
  在`src->main->resources`中添加`logback-spring.xml`文件:
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration scan="true" scanPeriod="10 seconds">
|
||||||
|
|
||||||
|
<!-- 自定义属性 -->
|
||||||
|
<springProperty scope="context" name="fluentdHost" source="logging.fluentd.host" defaultValue="localhost"/>
|
||||||
|
<springProperty scope="context" name="fluentdPort" source="logging.fluentd.port" defaultValue="24224"/>
|
||||||
|
<springProperty scope="context" name="topic" source="spring.application.name" defaultValue="default"/>
|
||||||
|
|
||||||
|
<!-- log路径 -->
|
||||||
|
<property name="log.path" value="logs/${topic}"/>
|
||||||
|
|
||||||
|
<!--控制台日志格式:彩色日志-->
|
||||||
|
<!-- magenta:洋红 -->
|
||||||
|
<!-- boldMagenta:粗红-->
|
||||||
|
<!-- cyan:青色 -->
|
||||||
|
<!-- white:白色 -->
|
||||||
|
<!-- magenta:洋红 -->
|
||||||
|
<property name="CONSOLE_LOG_PATTERN"
|
||||||
|
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
|
||||||
|
|
||||||
|
<!--文件日志格式-->
|
||||||
|
<property name="FILE_LOG_PATTERN"
|
||||||
|
value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n"/>
|
||||||
|
|
||||||
|
<!--编码-->
|
||||||
|
<property name="ENCODING"
|
||||||
|
value="UTF-8"/>
|
||||||
|
|
||||||
|
<!--输出到控制台-->
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<!--日志级别-->
|
||||||
|
<level>DEBUG</level>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<!--日志格式-->
|
||||||
|
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
|
||||||
|
<!--日志字符集-->
|
||||||
|
<charset>${ENCODING}</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--输出到文件-->
|
||||||
|
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<!--日志过滤器:此日志文件只记录INFO级别的-->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>INFO</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>${log.path}/log_info.log</file>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||||
|
<charset>${ENCODING}</charset>
|
||||||
|
</encoder>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<!-- 每天日志归档路径以及格式 -->
|
||||||
|
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<!-- 日志文件的最大大小 -->
|
||||||
|
<maxFileSize>100MB</maxFileSize>
|
||||||
|
<!--日志文件保留天数-->
|
||||||
|
<maxHistory>15</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<!-- 日志过滤器:此日志文件只记录WARN级别的 -->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>WARN</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>${log.path}/log_warn.log</file>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||||
|
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
|
||||||
|
</encoder>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<!-- 日志文件的最大大小 -->
|
||||||
|
<maxFileSize>100MB</maxFileSize>
|
||||||
|
<!--日志文件保留天数-->
|
||||||
|
<maxHistory>15</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<!-- 日志过滤器:此日志文件只记录ERROR级别的 -->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>ERROR</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<!-- 正在记录的日志文件的路径及文件名 -->
|
||||||
|
<file>${log.path}/log_error.log</file>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||||
|
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
|
||||||
|
</encoder>
|
||||||
|
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<!-- 日志文件的最大大小 -->
|
||||||
|
<maxFileSize>100MB</maxFileSize>
|
||||||
|
<!--日志文件保留天数-->
|
||||||
|
<maxHistory>15</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--输出到FLUENCY-->
|
||||||
|
<appender name="FLUENCY" class="ch.qos.logback.more.appenders.FluencyLogbackAppender">
|
||||||
|
<remoteHost>${fluentdHost}</remoteHost>
|
||||||
|
<port>${fluentdPort}</port>
|
||||||
|
<additionalField>
|
||||||
|
<key>topic</key>
|
||||||
|
<value>${topic}</value>
|
||||||
|
</additionalField>
|
||||||
|
<ignoredField>caller</ignoredField>
|
||||||
|
<useEventTime>true</useEventTime>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 开发环境 -->
|
||||||
|
<springProfile name="dev">
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
</root>
|
||||||
|
</springProfile>
|
||||||
|
|
||||||
|
<!-- 生产环境 -->
|
||||||
|
<springProfile name="prod">
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="INFO_FILE"/>
|
||||||
|
<appender-ref ref="WARN_FILE"/>
|
||||||
|
<appender-ref ref="ERROR_FILE"/>
|
||||||
|
</root>
|
||||||
|
</springProfile>
|
||||||
|
|
||||||
|
<!-- 容器环境 -->
|
||||||
|
<springProfile name="docker">
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FLUENCY"/>
|
||||||
|
</root>
|
||||||
|
</springProfile>
|
||||||
|
</configuration>
|
||||||
|
```
|
||||||
|
|
||||||
|
  该文件会根据当前环境自动添加日志输出节点。
|
||||||
|
|
||||||
# 四、项目发布
|
# 四、项目发布
|
||||||
|
|
||||||
## 4.1 发布到Git中
|
## 4.1 发布到Git中
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Vue3-Common
|
title: Vue3 Common
|
||||||
date: 2025-12-15 23:25
|
date: 2025-12-15 23:25
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
# 二、vite.config.ts配置
|
# 二、vite.config.ts配置
|
||||||
## 2.1 生成Typescript类型文件
|
## 2.1 生成ts类型文件
|
||||||
  安装vite-plugin-dts插件
|
  安装vite-plugin-dts插件
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
@@ -377,6 +377,167 @@ const app = createApp(App)
|
|||||||
app.use(router)
|
app.use(router)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
8. 在layout中使用:
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<aside class="layout-sidebar">
|
||||||
|
<menu-list
|
||||||
|
:route="useRoute()"
|
||||||
|
:routers="menuRoutes"
|
||||||
|
:collapse="isCollapse"
|
||||||
|
class="layout-menu"/>
|
||||||
|
</aside>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { MenuList } from 'vue3-common'
|
||||||
|
import menuRoutes from '@/router/menu'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
  如果需要对路由菜单进行权限处理,可以使用计算属性过滤一遍:
|
||||||
|
```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 图标组件
|
## 5.2 图标组件
|
||||||
1. 安装vite-plugin-svg-icons依赖
|
1. 安装vite-plugin-svg-icons依赖
|
||||||
```cmd
|
```cmd
|
||||||
93
docs/Web/Vue/Vue3-DefineModel.md
Normal file
93
docs/Web/Vue/Vue3-DefineModel.md
Normal file
@@ -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
|
||||||
|
<script setup>
|
||||||
|
import Stepper from './Stepper.vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const quantity = ref(1)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>购买数量:{{ quantity }}</p>
|
||||||
|
<Stepper v-model="quantity" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
  子组件:
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
const model = defineModel({
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button @click="quantity--">−</button>
|
||||||
|
<span>{{ quantity }}</span>
|
||||||
|
<button @click="quantity++">+</button>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
  和`defineProps`定义类似,可以指定类型和默认值。
|
||||||
|
|
||||||
|
# 三、多个v-model
|
||||||
|
  父组件:
|
||||||
|
```vue
|
||||||
|
<UserForm
|
||||||
|
v-model:name="form.name"
|
||||||
|
v-model:age="form.age"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
  子组件:
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
const name = defineModel<string>('name', {
|
||||||
|
default: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const age = defineModel<number>('age', {
|
||||||
|
default: 0
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input v-model="name" placeholder="姓名" />
|
||||||
|
<input v-model="age" type="number" placeholder="年龄" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
defineProps: 只读,不能改
|
||||||
|
defineModel: 可写
|
||||||
|
:::
|
||||||
79
docs/Web/Vue/Vue3-Directive.md
Normal file
79
docs/Web/Vue/Vue3-Directive.md
Normal file
@@ -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
|
||||||
|
<div v-demo="'hello'"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
  使用该指令,就会触发打印效果。
|
||||||
|
|
||||||
|
# 三、生命周期
|
||||||
|
| 钩子 | 说明 |
|
||||||
|
| ----- | ----- |
|
||||||
|
| 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,禁止点击、修改样式等等。
|
||||||
|
:::
|
||||||
Reference in New Issue
Block a user