Hooks 业务钩子
提供常用业务场景的 Vue 3 Composition API 钩子函数,简化开发流程。
🎮 在线演示
想要查看 Hooks 的使用效果?请访问 在线演示 查看功能演示
特性
🎯 业务导向
专注于常见业务场景的功能封装
📦 开箱即用
无需配置,导入即可使用
⚡ 状态管理
内置加载、进度等状态管理
🔧 灵活扩展
支持自定义配置和扩展
存储相关
useLocalStorage
操作 localStorage 的钩子函数。
js
import { useLocalStorage } from 'imtes-ui'
const { get, set, remove } = useLocalStorage('userSettings', {})
// 获取数据
const data = get()
// 设置数据
set({ theme: 'dark', language: 'zh-CN' })
// 删除数据
remove()
useSessionStorage
操作 sessionStorage 的钩子函数。
js
import { useSessionStorage } from 'imtes-ui'
const { get, set, remove } = useSessionStorage('tempData', null)
// 获取数据
const data = get()
// 设置数据
set({ token: 'abc123' })
// 删除数据
remove()
工具函数
useThrottle
节流函数钩子。
js
import { useThrottle } from 'imtes-ui'
const throttledFn = useThrottle(() => {
console.log('执行函数')
}, 300)
// 在300ms内多次调用,只会执行一次
throttledFn()
useDebounce
防抖函数钩子。
查看代码
vue
<template>
<div>
<el-input
v-model="searchText"
placeholder="输入搜索关键词"
@input="handleSearch"
/>
<div v-if="isSearching" class="mt-2 text-gray-500">
正在搜索...
</div>
<div class="mt-4">
<div v-for="item in searchResults" :key="item.id" class="p-2 border-b">
{{ item.name }}
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useDebounce } from 'imtes-ui'
const searchText = ref('')
const searchResults = ref([])
const isSearching = ref(false)
const { debouncedFunction: debouncedSearch } = useDebounce(
async (query) => {
if (!query) {
searchResults.value = []
return
}
isSearching.value = true
try {
// 模拟API调用
const response = await fetch(`/api/search?q=${query}`)
searchResults.value = await response.json()
} finally {
isSearching.value = false
}
},
500 // 500ms防抖
)
const handleSearch = (value) => {
debouncedSearch(value)
}
</script>
API:
js
const { debouncedFunction, cancel } = useDebounce(fn, delay)
参数:
fn
: 要防抖的函数delay
: 防抖延迟时间(毫秒)
返回值:
debouncedFunction
: 防抖后的函数cancel
: 取消防抖的函数
网络相关
useNetworkStatus
网络状态检测钩子。
js
import { useNetworkStatus } from 'imtes-ui'
const { isOnline } = useNetworkStatus()
// 监听网络状态
watch(isOnline, (online) => {
if (online) {
console.log('网络已连接')
} else {
console.log('网络已断开')
}
})
业务组件 Hook
useTable
表格业务逻辑钩子。
查看代码
vue
<template>
<div>
<im-el-table
:data="tableData"
:loading="loading"
:columns="columns"
:current-page="currentPage"
:page-size="pageSize"
:total="total"
@page-change="handlePageChange"
@sort-change="handleSortChange"
/>
</div>
</template>
<script setup>
import { useTable } from 'imtes-ui'
const {
tableData,
loading,
currentPage,
pageSize,
total,
fetchData,
refresh,
search,
reset,
handlePageChange,
handleSortChange
} = useTable({
api: '/api/users',
pageSize: 10
})
// 搜索
const handleSearch = (params) => {
search(params)
}
// 刷新
const handleRefresh = () => {
refresh()
}
</script>
API:
js
const tableHook = useTable(options)
参数:
options.api
: API 接口地址options.pageSize
: 每页数量,默认 10options.params
: 默认查询参数
返回值:
tableData
: 表格数据loading
: 加载状态currentPage
: 当前页码pageSize
: 每页数量total
: 总数量fetchData
: 获取数据函数refresh
: 刷新数据函数search
: 搜索函数reset
: 重置函数handlePageChange
: 页码变化处理handleSortChange
: 排序变化处理
useForm
表单业务逻辑钩子。
查看代码
vue
<template>
<el-form ref="formRef" :model="formData" :rules="rules">
<el-form-item label="用户名" prop="username">
<el-input v-model="formData.username" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitting" @click="handleSubmit">
提交
</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { useForm } from 'imtes-ui'
const {
formRef,
formData,
submitting,
validate,
resetForm,
submit
} = useForm({
initialData: {
username: '',
email: ''
},
submitApi: '/api/users'
})
const rules = {
username: [{ required: true, message: '请输入用户名' }],
email: [{ required: true, message: '请输入邮箱' }]
}
const handleSubmit = async () => {
const isValid = await validate()
if (isValid) {
await submit()
}
}
const handleReset = () => {
resetForm()
}
</script>
API:
js
const formHook = useForm(options)
参数:
options.initialData
: 初始数据options.submitApi
: 提交接口地址
返回值:
formRef
: 表单引用formData
: 表单数据submitting
: 提交状态validate
: 验证函数resetForm
: 重置函数submit
: 提交函数
useDialog
弹窗业务逻辑钩子。
js
import { useDialog } from 'imtes-ui'
const {
visible,
open,
close,
confirm,
cancel
} = useDialog({
onConfirm: async () => {
// 确认逻辑
console.log('确认操作')
return true // 返回 true 关闭弹窗
}
})
// 打开弹窗
const handleOpen = () => {
open()
}
文件操作
useDownload
文件下载钩子。
查看代码
vue
<template>
<div>
<ImButton
type="info"
:loading="downloading"
@click="handleDownload"
>
下载文件
</ImButton>
<div v-if="progress > 0" class="mt-3">
<div class="text-sm text-gray-500 mb-1">
下载进度: {{ progress }}%
</div>
<el-progress :percentage="progress" />
</div>
</div>
</template>
<script setup>
import { useDownload } from 'imtes-ui'
const {
downloading,
progress,
error,
download
} = useDownload()
const handleDownload = async () => {
try {
await download('https://example.com/file.pdf', 'document.pdf')
} catch (err) {
console.error('下载失败:', err)
}
}
</script>
useUpload
文件上传钩子。
js
import { useUpload } from 'imtes-ui'
const {
uploading,
progress,
result,
error,
upload
} = useUpload()
const handleUpload = async (file) => {
try {
const response = await upload(file, '/api/upload')
console.log('上传成功:', response)
} catch (err) {
console.error('上传失败:', err)
}
}
useExport
数据导出钩子。
js
import { useExport } from 'imtes-ui'
const {
exporting,
progress,
exportData
} = useExport()
const handleExport = async () => {
const data = [
{ name: '张三', age: 25, city: '北京' },
{ name: '李四', age: 30, city: '上海' }
]
await exportData(data, {
filename: '用户数据.xlsx',
headers: ['姓名', '年龄', '城市']
})
}
usePrint
打印功能钩子。
js
import { usePrint } from 'imtes-ui'
const { print } = usePrint()
const handlePrint = () => {
// 打印指定元素
print('#printContent')
// 或打印整个页面
print()
}
usePreview
文件预览钩子。
查看代码
vue
<template>
<div>
<ImButton @click="handlePreview">预览文件</ImButton>
<ImDialog v-model="previewVisible" title="文件预览" width="80%">
<div v-if="loading" class="text-center">
<el-icon class="is-loading"><Loading /></el-icon>
<div class="mt-2">正在加载...</div>
</div>
<div v-else-if="error" class="text-center text-red-500">
{{ error }}
</div>
<div v-else class="preview-content">
<img v-if="isImage" :src="previewUrl" class="max-w-full h-auto" />
<iframe v-else-if="isPdf" :src="previewUrl" class="w-full h-96"></iframe>
<div v-else class="text-center text-gray-500">
不支持预览此文件类型
</div>
</div>
</ImDialog>
</div>
</template>
<script setup>
import { usePreview } from 'imtes-ui'
const {
previewVisible,
loading,
error,
previewUrl,
isImage,
isPdf,
preview,
close
} = usePreview()
const handlePreview = async () => {
await preview('https://example.com/document.pdf')
}
</script>
API 测试
useTestApi
API 接口测试钩子。
查看代码
vue
<template>
<div class="api-test">
<div class="mb-4">
<el-select v-model="method" placeholder="请求方法">
<el-option label="GET" value="GET" />
<el-option label="POST" value="POST" />
<el-option label="PUT" value="PUT" />
<el-option label="DELETE" value="DELETE" />
</el-select>
<el-input
v-model="url"
placeholder="请输入API地址"
class="ml-2"
style="width: 300px"
/>
<ImButton
type="primary"
:loading="testing"
@click="handleTest"
class="ml-2"
>
测试
</ImButton>
</div>
<div class="mb-4">
<h4>请求参数:</h4>
<el-input
v-model="params"
type="textarea"
:rows="4"
placeholder="JSON格式参数"
/>
</div>
<div v-if="result" class="result">
<h4>响应结果:</h4>
<pre class="bg-gray-100 p-4 rounded">{{ JSON.stringify(result, null, 2) }}</pre>
</div>
<div v-if="error" class="error text-red-500">
<h4>错误信息:</h4>
<pre class="bg-red-50 p-4 rounded">{{ error }}</pre>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useTestApi } from 'imtes-ui'
const {
testing,
result,
error,
testApi
} = useTestApi()
const method = ref('GET')
const url = ref('')
const params = ref('')
const handleTest = async () => {
try {
const requestParams = params.value ? JSON.parse(params.value) : {}
await testApi({
method: method.value,
url: url.value,
data: requestParams
})
} catch (err) {
console.error('测试失败:', err)
}
}
</script>
工具类 Hook
usePermission
权限检查钩子。
js
import { usePermission } from 'imtes-ui'
const userPermissions = ['user:read', 'user:write', 'admin:read']
const { hasPermission, hasAllPermissions } = usePermission(
['user:read', 'user:write'],
userPermissions
)
console.log(hasPermission.value) // true (有其中一个权限)
console.log(hasAllPermissions.value) // true (有所有权限)
useFormatter
数据格式化钩子。
js
import { useFormatter } from 'imtes-ui'
const {
formatFileSize,
formatMoney,
formatDate,
formatPhone,
formatIdCard
} = useFormatter()
console.log(formatFileSize(1024)) // "1 KB"
console.log(formatMoney(1234.56)) // "¥1,234.56"
console.log(formatDate(new Date())) // "2024-01-15 10:30:45"
console.log(formatPhone('13800138000')) // "138****8000"
console.log(formatIdCard('123456789012345678')) // "123456********5678"
useClipboard
剪贴板操作钩子。
js
import { useClipboard } from 'imtes-ui'
const { copy, read } = useClipboard()
// 复制文本
const handleCopy = async () => {
const success = await copy('要复制的文本')
if (success) {
console.log('复制成功')
}
}
// 读取剪贴板
const handleRead = async () => {
const text = await read()
console.log('剪贴板内容:', text)
}
useFullscreen
全屏操作钩子。
js
import { useFullscreen } from 'imtes-ui'
const { isFullscreen, enter, exit, toggle } = useFullscreen('#app')
// 进入全屏
const handleEnter = () => {
enter()
}
// 退出全屏
const handleExit = () => {
exit()
}
// 切换全屏
const handleToggle = () => {
toggle()
}
useConfirm
确认对话框钩子。
js
import { useConfirm } from 'imtes-ui'
const { confirm, alert, prompt } = useConfirm()
// 确认对话框
const handleDelete = async () => {
const result = await confirm('确定要删除这条记录吗?', '删除确认')
if (result) {
// 执行删除操作
console.log('已确认删除')
}
}
// 警告对话框
const handleAlert = async () => {
await alert('操作成功!', '提示')
}
// 输入对话框
const handlePrompt = async () => {
const result = await prompt('请输入新的名称', '重命名')
if (result) {
console.log('新名称:', result)
}
}
useTitle
页面标题管理钩子。
js
import { useTitle } from 'imtes-ui'
const { title, setTitle, resetTitle } = useTitle('默认标题')
// 设置标题
setTitle('新的页面标题')
// 重置标题
resetTitle()
使用示例
完整的业务场景
结合多个 Hook 实现完整的用户管理页面:
查看完整示例
vue
<template>
<div class="user-management">
<!-- 搜索表单 -->
<div class="search-form mb-4">
<el-form inline>
<el-form-item label="用户名">
<el-input
v-model="searchForm.username"
placeholder="请输入用户名"
@input="handleSearch"
/>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm.status" placeholder="请选择状态">
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<ImButton type="primary" @click="handleRefresh">刷新</ImButton>
<ImButton @click="handleReset">重置</ImButton>
</el-form-item>
</el-form>
</div>
<!-- 操作按钮 -->
<div class="actions mb-4">
<ImButton type="primary" @click="handleAdd">新增用户</ImButton>
<ImButton
type="success"
:loading="exporting"
@click="handleExport"
>
导出数据
</ImButton>
</div>
<!-- 用户表格 -->
<im-el-table
:data="tableData"
:loading="loading"
:columns="columns"
:current-page="currentPage"
:page-size="pageSize"
:total="total"
@page-change="handlePageChange"
@sort-change="handleSortChange"
>
<template #actions="{ row }">
<ImButton size="small" @click="handleEdit(row)">编辑</ImButton>
<ImButton
size="small"
type="danger"
@click="handleDelete(row)"
>
删除
</ImButton>
</template>
</im-el-table>
<!-- 用户表单弹窗 -->
<ImDialog v-model="formVisible" :title="isEdit ? '编辑用户' : '新增用户'">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="formData.username" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="formData.phone" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<ImButton @click="formVisible = false">取消</ImButton>
<ImButton
type="primary"
:loading="submitting"
@click="handleSubmit"
>
确定
</ImButton>
</template>
</ImDialog>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import {
useTable,
useForm,
useDialog,
useConfirm,
useExport,
useDebounce
} from 'imtes-ui'
// 表格相关
const {
tableData,
loading,
currentPage,
pageSize,
total,
fetchData,
refresh,
search,
reset,
handlePageChange,
handleSortChange
} = useTable({
api: '/api/users',
pageSize: 10
})
// 表单相关
const {
formRef,
formData,
submitting,
validate,
resetForm,
submit
} = useForm({
initialData: {
username: '',
email: '',
phone: '',
status: 1
}
})
// 弹窗相关
const { visible: formVisible, open: openForm, close: closeForm } = useDialog()
// 确认对话框
const { confirm } = useConfirm()
// 导出相关
const { exporting, exportData } = useExport()
// 搜索表单
const searchForm = reactive({
username: '',
status: ''
})
// 防抖搜索
const { debouncedFunction: debouncedSearch } = useDebounce((params) => {
search(params)
}, 300)
const isEdit = ref(false)
// 表格列配置
const columns = [
{ field: 'id', title: 'ID', width: 80 },
{ field: 'username', title: '用户名' },
{ field: 'email', title: '邮箱' },
{ field: 'phone', title: '手机号' },
{ field: 'status', title: '状态', slots: { default: 'status' } },
{ field: 'actions', title: '操作', slots: { default: 'actions' } }
]
// 表单验证规则
const rules = {
username: [{ required: true, message: '请输入用户名' }],
email: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入正确的邮箱格式' }
],
phone: [
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
]
}
// 搜索
const handleSearch = () => {
debouncedSearch(searchForm)
}
// 刷新
const handleRefresh = () => {
refresh()
}
// 重置
const handleReset = () => {
Object.assign(searchForm, { username: '', status: '' })
reset()
}
// 新增
const handleAdd = () => {
isEdit.value = false
resetForm()
openForm()
}
// 编辑
const handleEdit = (row) => {
isEdit.value = true
Object.assign(formData, row)
openForm()
}
// 删除
const handleDelete = async (row) => {
const result = await confirm(`确定要删除用户"${row.username}"吗?`, '删除确认')
if (result) {
// 调用删除API
console.log('删除用户:', row.id)
refresh()
}
}
// 提交表单
const handleSubmit = async () => {
const isValid = await validate()
if (isValid) {
await submit()
closeForm()
refresh()
}
}
// 导出数据
const handleExport = async () => {
await exportData(tableData.value, {
filename: '用户数据.xlsx',
headers: ['ID', '用户名', '邮箱', '手机号', '状态']
})
}
</script>
这个示例展示了如何组合使用多个 Hook 来构建一个完整的用户管理页面,包括:
- 使用
useTable
管理表格状态和分页 - 使用
useForm
管理表单状态和验证 - 使用
useDialog
管理弹窗状态 - 使用
useConfirm
处理确认操作 - 使用
useExport
实现数据导出 - 使用
useDebounce
实现搜索防抖
通过这些 Hook 的组合,可以大大简化业务逻辑的实现,提高开发效率。