@tianyio/quality-helper
Version:
A comprehensive quality helper tool for project scaffolding and management
288 lines (247 loc) • 7.84 kB
text/typescript
import { ref, computed } from 'vue'
import { defineStore, acceptHMRUpdate } from 'pinia'
// 用户信息接口
interface UserInfo {
id: string
username: string
email: string
avatar?: string
role: 'admin' | 'user' | 'guest'
preferences: {
theme: 'light' | 'dark' | 'auto'
language: string
notifications: boolean
autoSave: boolean
}
lastLoginTime?: string
}
// 用户设置接口
interface UserSettings {
sidebarCollapsed: boolean
pageSize: number
defaultView: 'grid' | 'list'
shortcuts: Record<string, string>
}
/**
* 用户 Store - 数据持久化案例
* 演示 Pinia 3.0.3 的持久化功能和最佳实践
*/
export const useUserStore = defineStore(
'user',
() => {
// 状态定义
const userInfo = ref<UserInfo | null>(null)
const isLoggedIn = ref(false)
const token = ref<string | null>(null)
const settings = ref<UserSettings>({
sidebarCollapsed: false,
pageSize: 20,
defaultView: 'grid',
shortcuts: {},
})
const loginHistory = ref<string[]>([])
const sessionStartTime = ref<string | null>(null)
// 计算属性
const isAdmin = computed(() => userInfo.value?.role === 'admin')
const displayName = computed(() => {
if (!userInfo.value) return 'Guest'
return userInfo.value.username || userInfo.value.email.split('@')[0]
})
const avatarUrl = computed(() => {
if (userInfo.value?.avatar) return userInfo.value.avatar
// 生成默认头像 URL
const email = userInfo.value?.email ?? 'guest@example.com'
return `https://www.gravatar.com/avatar/${btoa(email)}?d=identicon&s=64`
})
const sessionDuration = computed(() => {
if (!sessionStartTime.value) return 0
return Date.now() - new Date(sessionStartTime.value).getTime()
})
// 登录方法
async function login(credentials: { username: string; password: string }): Promise<boolean> {
try {
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000))
// 模拟登录成功
const mockUser: UserInfo = {
id: '1',
username: credentials.username,
email: `${credentials.username}@example.com`,
role: credentials.username === 'admin' ? 'admin' : 'user',
preferences: {
theme: 'light',
language: 'zh-CN',
notifications: true,
autoSave: true,
},
lastLoginTime: new Date().toISOString(),
}
const mockToken = `token_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
// 更新状态
userInfo.value = mockUser
token.value = mockToken
isLoggedIn.value = true
sessionStartTime.value = new Date().toISOString()
// 记录登录历史
loginHistory.value.unshift(mockUser.lastLoginTime!)
if (loginHistory.value.length > 10) {
loginHistory.value = loginHistory.value.slice(0, 10)
}
if (import.meta.env.DEV) {
console.log('👤 User logged in:', mockUser)
}
return true
} catch (error) {
console.error('Login failed:', error)
return false
}
}
// 登出方法
function logout() {
userInfo.value = null
token.value = null
isLoggedIn.value = false
sessionStartTime.value = null
if (import.meta.env.DEV) {
console.log('👋 User logged out')
}
}
// 更新用户信息
function updateUserInfo(updates: Partial<UserInfo>) {
if (!userInfo.value) {
throw new Error('No user logged in')
}
userInfo.value = { ...userInfo.value, ...updates }
if (import.meta.env.DEV) {
console.log('📝 User info updated:', updates)
}
}
// 更新用户偏好设置
function updatePreferences(preferences: Partial<UserInfo['preferences']>) {
if (!userInfo.value) {
throw new Error('No user logged in')
}
userInfo.value.preferences = { ...userInfo.value.preferences, ...preferences }
if (import.meta.env.DEV) {
console.log('⚙️ User preferences updated:', preferences)
}
}
// 更新用户设置
function updateSettings(newSettings: Partial<UserSettings>) {
settings.value = { ...settings.value, ...newSettings }
if (import.meta.env.DEV) {
console.log('🔧 User settings updated:', newSettings)
}
}
// 切换侧边栏状态
function toggleSidebar() {
settings.value.sidebarCollapsed = !settings.value.sidebarCollapsed
}
// 添加快捷键
function addShortcut(key: string, action: string) {
settings.value.shortcuts[key] = action
}
// 移除快捷键
function removeShortcut(key: string) {
delete settings.value.shortcuts[key]
}
// 切换主题
function toggleTheme() {
if (!userInfo.value) return
const themes: Array<UserInfo['preferences']['theme']> = ['light', 'dark', 'auto']
const currentIndex = themes.indexOf(userInfo.value.preferences.theme)
const nextIndex = (currentIndex + 1) % themes.length
updatePreferences({ theme: themes[nextIndex] })
}
// 检查权限
function hasPermission(permission: string): boolean {
if (!userInfo.value) return false
// 简单的权限检查逻辑
if (userInfo.value.role === 'admin') return true
if (userInfo.value.role === 'guest') return false
// 用户权限检查逻辑
const userPermissions = ['read', 'write']
return userPermissions.includes(permission)
}
// 刷新 token
async function refreshToken(): Promise<boolean> {
if (!token.value) return false
try {
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 500))
const newToken = `token_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
token.value = newToken
if (import.meta.env.DEV) {
console.log('🔄 Token refreshed')
}
return true
} catch (error) {
console.error('Token refresh failed:', error)
logout() // 刷新失败时登出
return false
}
}
// 清除登录历史
function clearLoginHistory() {
loginHistory.value = []
}
// 导出状态
function exportUserData() {
return {
userInfo: userInfo.value,
settings: settings.value,
loginHistory: loginHistory.value,
sessionStartTime: sessionStartTime.value,
}
}
// 导入状态
function importUserData(data: ReturnType<typeof exportUserData>) {
if (data.userInfo) userInfo.value = data.userInfo
if (data.settings) settings.value = data.settings
if (data.loginHistory) loginHistory.value = data.loginHistory
if (data.sessionStartTime) sessionStartTime.value = data.sessionStartTime
}
return {
// 状态
userInfo,
isLoggedIn,
token,
settings,
loginHistory,
sessionStartTime,
// 计算属性
isAdmin,
displayName,
avatarUrl,
sessionDuration,
// 方法
login,
logout,
updateUserInfo,
updatePreferences,
updateSettings,
toggleSidebar,
addShortcut,
removeShortcut,
toggleTheme,
hasPermission,
refreshToken,
clearLoginHistory,
exportUserData,
importUserData,
}
},
{
// 启用数据持久化
persist: {
key: 'user-store',
storage: localStorage,
// 只持久化部分字段,敏感信息如 token 不持久化
paths: ['userInfo', 'settings', 'loginHistory'],
},
}
)
// HMR 支持
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
}