first commit

This commit is contained in:
2026-10-26 18:54:09 +08:00
commit 20a594d834
180 changed files with 18496 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
---
name: v3-use-composables
description: 项目内置组合式函数使用教程,涵盖设备检测、异步下拉、全屏加载、分页、路由监听、主题切换、动态标题、水印等组合式函数。当用户提到以下任何场景时都应触发:使用组合式函数、调用 Composables、判断设备类型、异步加载下拉选项、全屏 Loading、分页逻辑、监听路由变化、切换主题、设置页面标题、添加水印。即使用户没有明确说 Composables,只要意图是使用项目内置的组合式函数就应该使用此 Skill。
metadata:
author: pany
version: "2026.06.13"
---
# 内置组合式函数使用教程
项目在 `src/common/composables` 目录下提供了一组通用组合式函数,通过路径别名 `@@/composables/` 导入。
本 Skill 定义的是项目内置的组合式函数,当这些函数不存在时,以用户需求为准,新增对应函数。
## 设备检测 `useDevice`
判断当前设备类型(移动端 / 桌面端),基于 `appStore.device` 的响应式计算属性。
```ts
import { useDevice } from "@@/composables/useDevice"
const { isMobile, isDesktop } = useDevice()
// 在模板或逻辑中使用
if (isMobile.value) {
// 移动端逻辑
}
```
## 异步下拉选择器 `useFetchSelect`
封装下拉选择器的异步数据加载逻辑,组件挂载时自动调用接口获取选项。
```ts
import { useFetchSelect } from "@@/composables/useFetchSelect"
import { getSelectDataApi } from "./apis/xxx"
const { loading, options, value } = useFetchSelect({
api: getSelectDataApi // 直接传函数引用,返回 ApiResponseData<SelectOption[]>
})
```
在模板中配合 Element Plus 使用:
```vue
<el-card v-loading="loading">
<el-select v-model="value" filterable>
<el-option v-for="item in options" v-bind="item" :key="item.value" placeholder="请选择" />
</el-select>
</el-card>
```
接口返回的数据格式:
```ts
interface SelectOption {
value: string | number
label: string
disabled?: boolean
}
```
## 全屏加载 `useFullscreenLoading`
包装一个函数,在其执行期间自动显示全屏 Loading,函数执行完毕后自动关闭。
```ts
import { useFullscreenLoading } from "@@/composables/useFullscreenLoading"
// 方式一:行内调用(推荐简单场景)
const res = await useFullscreenLoading(getSuccessApi)([1, 2, 3])
// 方式二:自定义 Loading 配置
try {
await useFullscreenLoading(getErrorApi, { text: "删除中...", background: "#F56C6C20" })()
} catch (error) {
ElMessage.error((error as Error).message)
}
// 方式三:先赋值再调用(适合多次复用)
const submitWithLoading = useFullscreenLoading(submitApi)
await submitWithLoading(formData)
```
## 分页 `usePagination`
封装分页状态与操作,适配 Element Plus 的 `el-pagination` 组件。
```ts
import { usePagination } from "@@/composables/usePagination"
function getTableData() {
// 请求表格数据,并更新 paginationData.total
}
const { paginationData, resetCurrentPage, watchPagination } = usePagination({
callback: getTableData,
pageSize: 20,
pageSizes: [10, 20, 50, 100]
})
// 初始化加载,并在 currentPage / pageSize 变化时重新请求
watchPagination()
function handleSearch() {
resetCurrentPage()
}
```
在模板中使用:
```html
<el-pagination
v-model:current-page="paginationData.currentPage"
v-model:page-size="paginationData.pageSize"
:page-sizes="paginationData.pageSizes"
:total="paginationData.total"
:layout="paginationData.layout"
background
/>
```
`callback` 会在 `watchPagination` 监听到页码或每页条数变化时执行;`resetCurrentPage` 用于搜索场景,当前已在第 1 页时直接执行回调,否则重置到第 1 页并交给监听触发,避免重复请求。
默认分页参数:
| 参数 | 默认值 |
|------|--------|
| `total` | `0` |
| `currentPage` | `1` |
| `pageSizes` | `[10, 20, 50]` |
| `pageSize` | `10` |
| `layout` | `"total, sizes, prev, pager, next, jumper"` |
## 路由监听 `useRouteListener`
基于发布订阅模式监听路由变化,比直接 `watch` 路由性能更好,组件卸载时自动移除监听。
```ts
import { useRouteListener } from "@@/composables/useRouteListener"
const { listenerRouteChange, removeRouteListener } = useRouteListener()
// 监听路由变化
listenerRouteChange((route) => {
console.log("路由变化了:", route.path)
})
// 立即执行一次(获取当前路由信息)
listenerRouteChange((route) => {
activeMenu.value = route.path
}, true) // 第二个参数 immediate = true
```
## 主题切换 `useTheme`
管理主题切换,支持 View Transition 动画效果。
```ts
import { useTheme } from "@@/composables/useTheme"
const { themeList, activeThemeName, initTheme, setTheme } = useTheme()
// 初始化主题(应用启动时调用一次)
initTheme()
// 切换主题(需要传入鼠标事件以实现过渡动画)
function handleThemeChange(event: MouseEvent, themeName: ThemeName) {
setTheme(event, themeName)
}
```
可用主题:
| name | title |
|------|-------|
| `"normal"` | 默认 |
| `"dark"` | 黑暗 |
| `"dark-blue"` | 深蓝 |
## 动态标题 `useTitle`
动态设置浏览器标签页标题,格式为 `项目名 | 页面名`。
```ts
import { useTitle } from "@@/composables/useTitle"
const { setTitle } = useTitle()
// 设置标题为 "V3 Admin Vite | 用户管理"
setTitle("用户管理")
// 重置为项目默认标题
setTitle()
```
## 水印 `useWatermark`
为页面或指定容器添加水印,支持防御(防止用户通过控制台删除或隐藏水印),组件卸载时自动清除。
```ts
import { useWatermark } from "@@/composables/useWatermark"
// 默认挂载到 body
const { setWatermark, clearWatermark } = useWatermark()
// 设置水印
setWatermark("机密文件")
// 自定义配置
setWatermark("内部使用", {
defense: true, // 开启防御(默认 true)
color: "#c0c4cc", // 文本颜色
opacity: 0.5, // 透明度
size: 16, // 字体大小
angle: -20, // 倾斜角度
width: 300, // 单个水印宽度(越大密度越低)
height: 200 // 单个水印高度(越大密度越低)
})
// 清除水印
clearWatermark()
```
挂载到指定容器:
```vue
<script setup lang="ts">
import { useWatermark } from "@@/composables/useWatermark"
const localRef = useTemplateRef("localRef")
const { setWatermark, clearWatermark } = useWatermark(localRef)
onMounted(() => {
setWatermark("仅限内部")
})
</script>
<template>
<div ref="localRef">
<!-- 内容 -->
</div>
</template>
```
## 灰色模式与色弱模式 `useGreyAndColorWeakness`
初始化灰色模式和色弱模式,基于 `settingsStore` 的配置自动切换 HTML 根元素的 CSS 类名。
```ts
import { useGreyAndColorWeakness } from "@@/composables/useGreyAndColorWeakness"
const { initGreyAndColorWeakness } = useGreyAndColorWeakness()
// 应用启动时调用一次即可,后续会自动响应 Store 变化
initGreyAndColorWeakness()
```
## 布局模式 `useLayoutMode`
管理系统布局模式(左侧菜单 / 顶部菜单 / 左侧 + 顶部混合菜单)。
```ts
import { useLayoutMode } from "@@/composables/useLayoutMode"
import { LayoutModeEnum } from "@@/constants/app-key"
const { isLeft, isTop, isLeftTop, setLayoutMode } = useLayoutMode()
// 判断当前布局
if (isLeft.value) {
// 左侧菜单布局
}
// 切换布局
setLayoutMode(LayoutModeEnum.Top)
```
## 使用原则
1. 优先使用这些内置组合式函数,不要重复造轮子
2. 组合式函数内部已处理生命周期(如 `onBeforeUnmount` 自动清理),无需手动管理
3. 需要新增通用组合式函数时,在 `src/common/composables` 目录下创建,命名以 `use` 开头
4. 页面私有的组合式函数应放在对应页面目录的 `composables` 子目录下,而非 `src/common/composables`