feat: 功能
This commit is contained in:
176
frontend/hooks/web/README.md
Normal file
176
frontend/hooks/web/README.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# 公共 Hooks 模块
|
||||
|
||||
此目录包含可在 monorepo 各个应用中复用的 Vue Composition API Hooks。
|
||||
|
||||
## 📁 目录结构
|
||||
|
||||
```
|
||||
hooks/web/
|
||||
├── useCache.js # 缓存管理 Hook
|
||||
├── useUserInfo.js # 用户信息获取 Hook
|
||||
└── useVoiceText.ts # 语音文本处理 Hook(已抽离为公共模块)
|
||||
```
|
||||
|
||||
## 🚀 使用方式
|
||||
|
||||
### useUserInfo Hook
|
||||
|
||||
获取用户信息的公共 Hook,可在各个应用中复用。
|
||||
|
||||
#### 基础用法
|
||||
|
||||
```javascript
|
||||
import { useUserInfo } from '@gold/hooks/web/useUserInfo'
|
||||
import { getToken } from '@/utils/token-manager'
|
||||
|
||||
// 在组件中使用
|
||||
const { fetchUserInfo, loading, error, userInfo } = useUserInfo({
|
||||
getToken, // 传入获取 token 的函数
|
||||
})
|
||||
|
||||
// 获取用户信息
|
||||
await fetchUserInfo()
|
||||
```
|
||||
|
||||
#### 便捷函数
|
||||
|
||||
如果只需要获取一次用户信息,可以使用便捷函数:
|
||||
|
||||
```javascript
|
||||
import { getUserInfo } from '@gold/hooks/web/useUserInfo'
|
||||
import { getToken } from '@/utils/token-manager'
|
||||
|
||||
// 直接获取用户信息
|
||||
const userInfo = await getUserInfo({
|
||||
getToken,
|
||||
})
|
||||
```
|
||||
|
||||
#### 自定义配置
|
||||
|
||||
```javascript
|
||||
const { fetchUserInfo } = useUserInfo({
|
||||
baseUrl: '/custom-api/member', // 自定义 API 基础 URL
|
||||
getToken: () => {
|
||||
// 自定义获取 token 的逻辑
|
||||
return localStorage.getItem('token')
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## 📝 API 说明
|
||||
|
||||
### useUserInfo(options)
|
||||
|
||||
**参数:**
|
||||
- `options.baseUrl` (string, 可选): API 基础 URL,默认使用 `API_BASE.APP_MEMBER`
|
||||
- `options.getToken` (Function, 可选): 获取 token 的函数
|
||||
|
||||
**返回值:**
|
||||
- `fetchUserInfo()` (Function): 获取用户信息的异步函数
|
||||
- `loading` (Ref<boolean>): 加载状态
|
||||
- `error` (Ref<Error | null>): 错误信息
|
||||
- `userInfo` (Ref<Object | null>): 用户信息数据
|
||||
|
||||
### getUserInfo(options)
|
||||
|
||||
便捷函数,直接返回用户信息对象。
|
||||
|
||||
**参数:** 同 `useUserInfo`
|
||||
|
||||
**返回值:** `Promise<Object>` 用户信息对象
|
||||
|
||||
## 🔄 在 Store 中使用
|
||||
|
||||
```javascript
|
||||
import { defineStore } from 'pinia'
|
||||
import { getUserInfo } from '@gold/hooks/web/useUserInfo'
|
||||
import { getToken } from '@/utils/token-manager'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const userInfo = ref(null)
|
||||
|
||||
async function fetchUserInfo() {
|
||||
try {
|
||||
const data = await getUserInfo({ getToken })
|
||||
userInfo.value = data
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return { userInfo, fetchUserInfo }
|
||||
})
|
||||
```
|
||||
|
||||
### useVoiceText Hook
|
||||
|
||||
语音文本转换 Hook,将音频文件转换为文本转录。
|
||||
|
||||
#### 初始化(在应用启动时)
|
||||
|
||||
```javascript
|
||||
// 在应用的 API 服务文件中(如 common.js)
|
||||
import { createApiService } from '@gold/config/api/services'
|
||||
import { setApiService } from '@gold/hooks/web/useVoiceText'
|
||||
import http from '@/api/http'
|
||||
import { getAuthHeader } from '@/utils/token-manager'
|
||||
import { API_BASE } from '@gold/config/api'
|
||||
|
||||
// 创建 API 服务实例
|
||||
const apiService = createApiService({
|
||||
http,
|
||||
getAuthHeader,
|
||||
baseUrl: API_BASE.TIKHUB_APP,
|
||||
})
|
||||
|
||||
// 设置全局 API 服务(供 useVoiceText hook 使用)
|
||||
setApiService(apiService)
|
||||
```
|
||||
|
||||
#### 使用方式
|
||||
|
||||
```javascript
|
||||
import useVoiceText from '@gold/hooks/web/useVoiceText'
|
||||
import type { AudioItem } from '@gold/config/types'
|
||||
|
||||
// 在组件中使用
|
||||
const { getVoiceText } = useVoiceText()
|
||||
|
||||
const audioList: AudioItem[] = [
|
||||
{ audio_url: 'https://example.com/audio.mp3' }
|
||||
]
|
||||
|
||||
const transcriptions = await getVoiceText(audioList)
|
||||
// transcriptions: [{ key: 'url', value: 'transcribed text', audio_url: '...' }]
|
||||
```
|
||||
|
||||
#### 类型定义
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
AudioItem,
|
||||
TranscriptionResult
|
||||
} from '@gold/config/types'
|
||||
```
|
||||
|
||||
## 📦 依赖
|
||||
|
||||
- `vue`: Vue 3 Composition API
|
||||
- `axios`: HTTP 请求库(用于 useUserInfo)
|
||||
- `@gold/config/api`: 公共 API 配置
|
||||
- `@gold/config/api/services`: 公共 API 服务创建器
|
||||
- `@gold/config/types`: 公共类型定义
|
||||
|
||||
## 🔧 配置要求
|
||||
|
||||
确保在应用的 `vite.config.js` 中配置了 `@gold` 别名:
|
||||
|
||||
```javascript
|
||||
resolve: {
|
||||
alias: {
|
||||
'@gold': fileURLToPath(new URL('../../', import.meta.url))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -9,6 +9,9 @@ export const CACHE_KEY = {
|
||||
ROLE_ROUTERS: 'roleRouters',
|
||||
USER: 'user',
|
||||
VisitTenantId: 'visitTenantId',
|
||||
// Token 相关
|
||||
ACCESS_TOKEN: 'ACCESS_TOKEN',
|
||||
REFRESH_TOKEN: 'REFRESH_TOKEN',
|
||||
// 系统设置
|
||||
IS_DARK: 'isDark',
|
||||
LANG: 'lang',
|
||||
@@ -37,3 +40,29 @@ export const deleteUserCache = () => {
|
||||
wsCache.delete(CACHE_KEY.VisitTenantId)
|
||||
// 注意,不要清理 LoginForm 登录表单
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有 token 缓存
|
||||
* 清空 wsCache 中所有可能的 token 键名变体
|
||||
*/
|
||||
export const deleteTokenCache = () => {
|
||||
try {
|
||||
const { wsCache } = useCache()
|
||||
// 删除所有可能的 token 键名变体(大小写不同)
|
||||
const tokenKeys = [
|
||||
CACHE_KEY.ACCESS_TOKEN,
|
||||
'access_token',
|
||||
CACHE_KEY.REFRESH_TOKEN,
|
||||
'refresh_token'
|
||||
]
|
||||
tokenKeys.forEach(key => {
|
||||
try {
|
||||
wsCache.delete(key)
|
||||
} catch (e) {
|
||||
// 忽略单个键删除失败
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn('删除 token 缓存失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
150
frontend/hooks/web/useUserInfo.js
Normal file
150
frontend/hooks/web/useUserInfo.js
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* 用户信息 Hook
|
||||
* 封装获取用户信息的逻辑,可在各个应用中复用
|
||||
*
|
||||
* 使用方式:
|
||||
* import { useUserInfo } from '@gold/hooks/web/useUserInfo'
|
||||
*
|
||||
* const { fetchUserInfo, loading, error } = useUserInfo()
|
||||
* await fetchUserInfo()
|
||||
*/
|
||||
|
||||
import { ref } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { API_BASE } from '@gold/config/api'
|
||||
|
||||
// 获取 token 的工具函数(需要从应用层传入或使用全局配置)
|
||||
let getTokenFn = null
|
||||
|
||||
/**
|
||||
* 设置获取 token 的函数
|
||||
* @param {Function} fn - 获取 token 的函数
|
||||
*/
|
||||
export function setTokenGetter(fn) {
|
||||
getTokenFn = fn
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Authorization Header
|
||||
* @returns {string}
|
||||
*/
|
||||
function getAuthHeader() {
|
||||
if (getTokenFn) {
|
||||
const token = getTokenFn()
|
||||
return token ? `Bearer ${token}` : ''
|
||||
}
|
||||
// 如果没有设置 token getter,尝试从常见位置获取
|
||||
try {
|
||||
// 尝试从 sessionStorage 获取
|
||||
const manualToken = sessionStorage.getItem('DEV_MANUAL_TOKEN')
|
||||
if (manualToken) {
|
||||
return `Bearer ${manualToken}`
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息 Hook
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {string} options.baseUrl - API 基础 URL(可选,默认使用 APP_MEMBER)
|
||||
* @param {Function} options.getToken - 获取 token 的函数(可选)
|
||||
* @returns {Object} { fetchUserInfo, loading, error, userInfo }
|
||||
*/
|
||||
export function useUserInfo(options = {}) {
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
const userInfo = ref(null)
|
||||
|
||||
// 如果传入了 getToken 函数,设置它
|
||||
if (options.getToken) {
|
||||
setTokenGetter(options.getToken)
|
||||
}
|
||||
|
||||
// 确定 API 基础路径
|
||||
const baseUrl = options.baseUrl || API_BASE.APP_MEMBER
|
||||
const apiUrl = `${baseUrl}/user/get`
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @returns {Promise<Object>} 用户信息对象
|
||||
*/
|
||||
async function fetchUserInfo() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const authHeader = getAuthHeader()
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (authHeader) {
|
||||
headers.Authorization = authHeader
|
||||
}
|
||||
|
||||
// 获取 tenant-id(从环境变量或默认值)
|
||||
const tenantId =
|
||||
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_TENANT_ID) ||
|
||||
(typeof process !== 'undefined' && process.env?.VITE_TENANT_ID) ||
|
||||
'1'
|
||||
|
||||
if (tenantId) {
|
||||
headers['tenant-id'] = tenantId
|
||||
}
|
||||
|
||||
const response = await axios.get(apiUrl, { headers })
|
||||
|
||||
// 处理响应数据(根据后端返回格式调整)
|
||||
// 后端通常返回 { code: 0, data: {...}, msg: '...' } 格式
|
||||
let data = null
|
||||
if (response.data) {
|
||||
// 如果响应有 code 字段,说明是标准格式
|
||||
if (typeof response.data.code === 'number') {
|
||||
// code 为 0 或 200 表示成功
|
||||
if (response.data.code === 0 || response.data.code === 200) {
|
||||
data = response.data.data || response.data
|
||||
} else {
|
||||
throw new Error(response.data.msg || response.data.message || '获取用户信息失败')
|
||||
}
|
||||
} else {
|
||||
// 没有 code 字段,直接使用 data
|
||||
data = response.data.data || response.data
|
||||
}
|
||||
}
|
||||
|
||||
if (data) {
|
||||
userInfo.value = data
|
||||
return data
|
||||
} else {
|
||||
throw new Error('获取用户信息失败:响应数据为空')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err
|
||||
console.error('获取用户信息失败:', err)
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fetchUserInfo,
|
||||
loading,
|
||||
error,
|
||||
userInfo,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷函数:直接获取用户信息(不返回响应式状态)
|
||||
* @param {Object} options - 配置选项
|
||||
* @returns {Promise<Object>} 用户信息对象
|
||||
*/
|
||||
export async function getUserInfo(options = {}) {
|
||||
const { fetchUserInfo } = useUserInfo(options)
|
||||
return await fetchUserInfo()
|
||||
}
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import CommonService from '@/api/common'
|
||||
import type { AudioItem, TranscriptionResult } from '@/src/types/global'
|
||||
// 使用公共类型定义
|
||||
import type {
|
||||
AudioItem,
|
||||
TranscriptionResult,
|
||||
TranscriptionResponse,
|
||||
TranscriptionData
|
||||
} from '@gold/config/types'
|
||||
|
||||
/**
|
||||
* 转录数据接口
|
||||
* API 服务接口(需要从应用层注入)
|
||||
*/
|
||||
interface TranscriptionData {
|
||||
file_url?: string
|
||||
transcripts?: Array<{ text: string }>
|
||||
interface ApiService {
|
||||
videoToCharacters: (data: { fileLinkList: string[] }) => Promise<{ data: string }>
|
||||
}
|
||||
|
||||
// 全局 API 服务实例(由应用层设置)
|
||||
let apiServiceInstance: ApiService | null = null
|
||||
|
||||
/**
|
||||
* 响应结果接口
|
||||
* 设置 API 服务实例
|
||||
* @param service - API 服务对象
|
||||
*/
|
||||
interface TranscriptionResponse {
|
||||
results: Array<{
|
||||
transcription_url: string
|
||||
}>
|
||||
export function setApiService(service: ApiService) {
|
||||
apiServiceInstance = service
|
||||
}
|
||||
|
||||
/**
|
||||
* 将音频列表转换为文本转录
|
||||
* @param list - 音频项列表
|
||||
* @param apiService - API 服务实例(可选,如果已通过 setApiService 设置则不需要)
|
||||
* @returns 转录结果数组
|
||||
* @throws 当转录过程出错时抛出错误
|
||||
*
|
||||
@@ -29,9 +36,19 @@ interface TranscriptionResponse {
|
||||
* const transcriptions = await getVoiceText(audioList)
|
||||
* console.log(transcriptions) // [{ key: 'url', value: 'transcribed text' }]
|
||||
*/
|
||||
export async function getVoiceText(list: AudioItem[]): Promise<TranscriptionResult[]> {
|
||||
export async function getVoiceText(
|
||||
list: AudioItem[],
|
||||
apiService?: ApiService
|
||||
): Promise<TranscriptionResult[]> {
|
||||
// 使用传入的 apiService 或全局实例
|
||||
const service = apiService || apiServiceInstance
|
||||
|
||||
if (!service) {
|
||||
throw new Error('getVoiceText: 需要提供 API 服务实例。请使用 setApiService() 设置或传入 apiService 参数')
|
||||
}
|
||||
|
||||
// 调用API将视频转换为文本
|
||||
const ret = await CommonService.videoToCharacters({
|
||||
const ret = await service.videoToCharacters({
|
||||
fileLinkList: list.map(item => item.audio_url),
|
||||
})
|
||||
|
||||
@@ -68,19 +85,29 @@ export async function getVoiceText(list: AudioItem[]): Promise<TranscriptionResu
|
||||
* Hook 返回值接口
|
||||
*/
|
||||
interface UseVoiceTextReturn {
|
||||
getVoiceText: (list: AudioItem[]) => Promise<TranscriptionResult[]>
|
||||
getVoiceText: (list: AudioItem[], apiService?: ApiService) => Promise<TranscriptionResult[]>
|
||||
}
|
||||
|
||||
/**
|
||||
* 语音文本转换 Hook
|
||||
* @param apiService - API 服务实例(可选,如果已通过 setApiService 设置则不需要)
|
||||
* @returns 包含 getVoiceText 方法的对象
|
||||
*
|
||||
* @example
|
||||
* // 方式一:使用全局设置的 API 服务
|
||||
* setApiService(myApiService)
|
||||
* const { getVoiceText } = useVoiceText()
|
||||
* const result = await getVoiceText(audioList)
|
||||
*
|
||||
* @example
|
||||
* // 方式二:传入 API 服务实例
|
||||
* const { getVoiceText } = useVoiceText()
|
||||
* const result = await getVoiceText(audioList, myApiService)
|
||||
*/
|
||||
export default function useVoiceText(): UseVoiceTextReturn {
|
||||
return { getVoiceText }
|
||||
export default function useVoiceText(apiService?: ApiService): UseVoiceTextReturn {
|
||||
return {
|
||||
getVoiceText: (list: AudioItem[]) => getVoiceText(list, apiService)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user