first commit
This commit is contained in:
163
src/stores/app.js
Normal file
163
src/stores/app.js
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
|
||||
export const useAppStore = defineStore('app', () => {
|
||||
// 应用状态
|
||||
const sidebarCollapsed = ref(false)
|
||||
const mobileSidebarOpen = ref(false)
|
||||
const theme = ref('light')
|
||||
const language = ref('zh-CN')
|
||||
const loading = ref(false)
|
||||
const notifications = ref([])
|
||||
const isMobile = ref(false)
|
||||
|
||||
// 计算属性:根据设备类型判断侧边栏状态
|
||||
const sidebarVisible = computed(() => {
|
||||
if (isMobile.value) {
|
||||
return mobileSidebarOpen.value
|
||||
}
|
||||
return !sidebarCollapsed.value
|
||||
})
|
||||
|
||||
// 切换侧边栏
|
||||
const toggleSidebar = () => {
|
||||
if (isMobile.value) {
|
||||
mobileSidebarOpen.value = !mobileSidebarOpen.value
|
||||
} else {
|
||||
sidebarCollapsed.value = !sidebarCollapsed.value
|
||||
localStorage.setItem('sidebarCollapsed', JSON.stringify(sidebarCollapsed.value))
|
||||
}
|
||||
}
|
||||
|
||||
// 设置侧边栏状态
|
||||
const setSidebarCollapsed = (collapsed) => {
|
||||
if (isMobile.value) {
|
||||
mobileSidebarOpen.value = !collapsed
|
||||
} else {
|
||||
sidebarCollapsed.value = collapsed
|
||||
localStorage.setItem('sidebarCollapsed', JSON.stringify(collapsed))
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭移动端侧边栏
|
||||
const closeMobileSidebar = () => {
|
||||
if (isMobile.value) {
|
||||
mobileSidebarOpen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 设置移动端状态
|
||||
const setMobileState = (mobile) => {
|
||||
isMobile.value = mobile
|
||||
if (mobile) {
|
||||
mobileSidebarOpen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换主题
|
||||
const toggleTheme = () => {
|
||||
theme.value = theme.value === 'light' ? 'dark' : 'light'
|
||||
localStorage.setItem('theme', theme.value)
|
||||
}
|
||||
|
||||
// 设置主题
|
||||
const setTheme = (newTheme) => {
|
||||
theme.value = newTheme
|
||||
localStorage.setItem('theme', newTheme)
|
||||
}
|
||||
|
||||
// 设置语言
|
||||
const setLanguage = (lang) => {
|
||||
language.value = lang
|
||||
localStorage.setItem('language', lang)
|
||||
}
|
||||
|
||||
// 设置加载状态
|
||||
const setLoading = (status) => {
|
||||
loading.value = status
|
||||
}
|
||||
|
||||
// 添加通知
|
||||
const addNotification = (notification) => {
|
||||
const id = Date.now().toString()
|
||||
notifications.value.push({
|
||||
id,
|
||||
...notification,
|
||||
timestamp: new Date()
|
||||
})
|
||||
|
||||
// 自动移除通知(5秒后)
|
||||
setTimeout(() => {
|
||||
removeNotification(id)
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
// 移除通知
|
||||
const removeNotification = (id) => {
|
||||
const index = notifications.value.findIndex(n => n.id === id)
|
||||
if (index > -1) {
|
||||
notifications.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 清空所有通知
|
||||
const clearNotifications = () => {
|
||||
notifications.value = []
|
||||
}
|
||||
|
||||
// 初始化应用配置
|
||||
const initAppConfig = () => {
|
||||
// 从localStorage恢复配置
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
const savedLanguage = localStorage.getItem('language')
|
||||
const savedSidebarCollapsed = localStorage.getItem('sidebarCollapsed')
|
||||
|
||||
if (savedTheme) theme.value = savedTheme
|
||||
if (savedLanguage) language.value = savedLanguage
|
||||
if (savedSidebarCollapsed) sidebarCollapsed.value = JSON.parse(savedSidebarCollapsed)
|
||||
|
||||
// 检测移动端
|
||||
const checkMobile = () => {
|
||||
const mobile = window.innerWidth <= 768
|
||||
setMobileState(mobile)
|
||||
}
|
||||
|
||||
// 初始检测
|
||||
checkMobile()
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', checkMobile)
|
||||
|
||||
// 返回清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkMobile)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
sidebarCollapsed,
|
||||
mobileSidebarOpen,
|
||||
theme,
|
||||
language,
|
||||
loading,
|
||||
notifications,
|
||||
isMobile,
|
||||
|
||||
// 计算属性
|
||||
sidebarVisible,
|
||||
|
||||
// 方法
|
||||
toggleSidebar,
|
||||
setSidebarCollapsed,
|
||||
closeMobileSidebar,
|
||||
setMobileState,
|
||||
toggleTheme,
|
||||
setTheme,
|
||||
setLanguage,
|
||||
setLoading,
|
||||
addNotification,
|
||||
removeNotification,
|
||||
clearNotifications,
|
||||
initAppConfig
|
||||
}
|
||||
})
|
||||
11
src/stores/counter.js
Normal file
11
src/stores/counter.js
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
501
src/stores/user.js
Normal file
501
src/stores/user.js
Normal file
@@ -0,0 +1,501 @@
|
||||
/**
|
||||
* 用户状态管理 Store
|
||||
*
|
||||
* 后端响应格式说明:
|
||||
* 所有API响应都被包装在标准格式中:
|
||||
* {
|
||||
* "success": true,
|
||||
* "data": { ... }, // 真正的数据在这里
|
||||
* "message": "操作成功",
|
||||
* "requestId": "",
|
||||
* "timestamp": 1752469413
|
||||
* }
|
||||
*
|
||||
* 使用示例:
|
||||
*
|
||||
* // 在组件中使用
|
||||
* import { useUserStore } from '@/stores/user'
|
||||
*
|
||||
* const userStore = useUserStore()
|
||||
*
|
||||
* // 登录
|
||||
* const loginData = {
|
||||
* method: 'sms', // 或 'password'
|
||||
* phone: '13800138000',
|
||||
* code: '123456', // 短信登录时需要
|
||||
* password: 'password123' // 密码登录时需要
|
||||
* }
|
||||
* const result = await userStore.login(loginData)
|
||||
* if (result.success) {
|
||||
* console.log('登录成功')
|
||||
* // result.data 包含: { user, access_token, token_type, expires_in, login_method }
|
||||
* }
|
||||
*
|
||||
* // 注册
|
||||
* const registerData = {
|
||||
* phone: '13800138000',
|
||||
* password: 'password123',
|
||||
* confirmPassword: 'password123',
|
||||
* code: '123456'
|
||||
* }
|
||||
* const result = await userStore.register(registerData)
|
||||
* if (result.success) {
|
||||
* // result.data 包含注册成功的数据
|
||||
* }
|
||||
*
|
||||
* // 发送验证码
|
||||
* const result = await userStore.sendCode('13800138000', 'register')
|
||||
* if (result.success) {
|
||||
* // result.data 包含验证码发送结果
|
||||
* }
|
||||
*
|
||||
* // 重置密码
|
||||
* const resetData = {
|
||||
* phone: '13800138000',
|
||||
* newPassword: 'newpassword123',
|
||||
* confirmNewPassword: 'newpassword123',
|
||||
* code: '123456'
|
||||
* }
|
||||
* const result = await userStore.resetPassword(resetData)
|
||||
* if (result.success) {
|
||||
* // result.data 包含重置密码结果
|
||||
* }
|
||||
*
|
||||
* // 获取用户信息
|
||||
* const result = await userStore.fetchUserProfile()
|
||||
* if (result.success) {
|
||||
* // result.data 包含用户信息: { id, phone, created_at, updated_at }
|
||||
* }
|
||||
*
|
||||
* // 修改密码
|
||||
* const passwordData = {
|
||||
* oldPassword: 'oldpassword123',
|
||||
* newPassword: 'newpassword123',
|
||||
* confirmNewPassword: 'newpassword123',
|
||||
* code: '123456'
|
||||
* }
|
||||
* const result = await userStore.changePassword(passwordData)
|
||||
* if (result.success) {
|
||||
* // result.data 包含修改密码结果
|
||||
* }
|
||||
*
|
||||
* // 登出
|
||||
* userStore.logout()
|
||||
*
|
||||
* // 检查登录状态
|
||||
* const isAuth = await userStore.checkAuth()
|
||||
*
|
||||
* // 响应式状态
|
||||
* console.log(userStore.isLoggedIn) // 是否已登录
|
||||
* console.log(userStore.userInfo) // 用户信息: { id, phone, created_at, updated_at }
|
||||
* console.log(userStore.loading) // 加载状态
|
||||
*/
|
||||
|
||||
import { userApi } from '@/api'
|
||||
import router from '@/router'
|
||||
import { authEventBus } from '@/utils/request'
|
||||
import { clearLocalVersions, saveLocalVersions, VERSION_CONFIG, versionChecker } from '@/utils/version'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态
|
||||
const user = ref(null)
|
||||
const accessToken = ref(localStorage.getItem('access_token') || '')
|
||||
const tokenType = ref(localStorage.getItem('token_type') || 'Bearer')
|
||||
const isAuthenticated = ref(false)
|
||||
const loading = ref(false)
|
||||
// 新增:初始化标志
|
||||
const initialized = ref(false)
|
||||
|
||||
// 版本管理
|
||||
const tokenVersion = ref(localStorage.getItem('token_version') || VERSION_CONFIG.TOKEN_VERSION)
|
||||
const appVersion = ref(localStorage.getItem('app_version') || VERSION_CONFIG.APP_VERSION)
|
||||
|
||||
// 计算属性
|
||||
const isLoggedIn = computed(() => {
|
||||
return isAuthenticated.value && !!accessToken.value
|
||||
})
|
||||
|
||||
const userInfo = computed(() => user.value)
|
||||
|
||||
// 新增:用户类型相关计算属性
|
||||
const isAdmin = computed(() => {
|
||||
return user.value?.user_type === 'admin'
|
||||
})
|
||||
|
||||
const userType = computed(() => {
|
||||
return user.value?.user_type || 'user'
|
||||
})
|
||||
|
||||
// 新增:用户认证状态计算属性
|
||||
const isCertified = computed(() => {
|
||||
return user.value?.is_certified || false
|
||||
})
|
||||
|
||||
// 检查用户信息是否完整
|
||||
const isUserInfoComplete = computed(() => {
|
||||
return user.value &&
|
||||
user.value.id &&
|
||||
user.value.phone &&
|
||||
user.value.user_type !== undefined
|
||||
})
|
||||
|
||||
// 强制刷新用户信息
|
||||
const refreshUserProfile = async () => {
|
||||
if (!accessToken.value) {
|
||||
return { success: false, error: '未登录' }
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await fetchUserProfile()
|
||||
return result
|
||||
} catch (error) {
|
||||
return { success: false, error }
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const hasRole = (role) => {
|
||||
return userType.value === role
|
||||
}
|
||||
|
||||
// 监听认证错误事件
|
||||
const handleAuthError = (message) => {
|
||||
console.log('用户store收到认证错误事件:', message)
|
||||
logout()
|
||||
}
|
||||
|
||||
// 处理版本退出登录事件
|
||||
const handleVersionLogout = (event) => {
|
||||
console.log('收到版本退出登录事件:', event.detail)
|
||||
logout()
|
||||
ElMessage.error('系统已更新,请重新登录')
|
||||
router.push('/auth/login')
|
||||
}
|
||||
|
||||
// 处理版本刷新事件
|
||||
const handleVersionRefresh = (event) => {
|
||||
console.log('收到版本刷新事件:', event.detail)
|
||||
saveLocalVersions({ appVersion: VERSION_CONFIG.APP_VERSION })
|
||||
ElMessage.success('应用已更新,正在刷新页面...')
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
// 版本检查
|
||||
const checkVersions = () => {
|
||||
const currentTokenVersion = localStorage.getItem('token_version') || VERSION_CONFIG.TOKEN_VERSION
|
||||
const currentAppVersion = localStorage.getItem('app_version') || VERSION_CONFIG.APP_VERSION
|
||||
|
||||
// 检查token版本
|
||||
if (currentTokenVersion !== VERSION_CONFIG.TOKEN_VERSION) {
|
||||
console.warn('Token版本不匹配,需要重新登录', {
|
||||
current: currentTokenVersion,
|
||||
expected: VERSION_CONFIG.TOKEN_VERSION
|
||||
})
|
||||
|
||||
// 清除所有认证相关数据
|
||||
logout()
|
||||
|
||||
// 显示提示信息
|
||||
ElMessage.error('系统已更新,请重新登录')
|
||||
|
||||
// 跳转到登录页面
|
||||
router.push('/auth/login')
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查应用版本
|
||||
if (currentAppVersion !== VERSION_CONFIG.APP_VERSION) {
|
||||
console.log('应用版本已更新,刷新页面', {
|
||||
current: currentAppVersion,
|
||||
expected: VERSION_CONFIG.APP_VERSION
|
||||
})
|
||||
|
||||
// 更新本地存储的应用版本
|
||||
saveLocalVersions({ appVersion: VERSION_CONFIG.APP_VERSION })
|
||||
|
||||
// 显示提示信息
|
||||
ElMessage.success('应用已更新,正在刷新页面...')
|
||||
|
||||
// 延迟刷新页面
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 1500)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 登录
|
||||
const login = async (loginData) => {
|
||||
loading.value = true
|
||||
try {
|
||||
let response
|
||||
if (loginData.method === 'sms') {
|
||||
response = await userApi.loginWithSMS({
|
||||
phone: loginData.phone,
|
||||
code: loginData.code
|
||||
})
|
||||
} else {
|
||||
response = await userApi.loginWithPassword({
|
||||
phone: loginData.phone,
|
||||
password: loginData.password
|
||||
})
|
||||
}
|
||||
|
||||
// 后端返回格式: { success: true, data: { user, access_token, token_type, ... }, message, ... }
|
||||
// 真正的数据在 response.data 中
|
||||
const responseData = response.data
|
||||
|
||||
// 保存token和用户信息
|
||||
accessToken.value = responseData.access_token
|
||||
tokenType.value = responseData.token_type
|
||||
user.value = responseData.user
|
||||
isAuthenticated.value = true
|
||||
|
||||
// 保存到localStorage
|
||||
localStorage.setItem('access_token', responseData.access_token)
|
||||
localStorage.setItem('token_type', responseData.token_type)
|
||||
saveLocalVersions({ tokenVersion: VERSION_CONFIG.TOKEN_VERSION })
|
||||
|
||||
// 登录成功后,主动获取最新的用户信息以确保数据完整
|
||||
try {
|
||||
const profileResult = await fetchUserProfile()
|
||||
if (profileResult.success) {
|
||||
console.log('登录后用户信息更新成功')
|
||||
// 如果获取到更完整的用户信息,更新用户数据
|
||||
if (profileResult.data) {
|
||||
user.value = { ...user.value, ...profileResult.data }
|
||||
}
|
||||
}
|
||||
} catch (profileError) {
|
||||
console.warn('登录后获取用户信息失败:', profileError)
|
||||
// 即使获取用户信息失败,也不影响登录流程
|
||||
}
|
||||
|
||||
return { success: true, data: responseData }
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error)
|
||||
return { success: false, error }
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 注册
|
||||
const register = async (registerData) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await userApi.register({
|
||||
phone: registerData.phone,
|
||||
password: registerData.password,
|
||||
confirm_password: registerData.confirmPassword,
|
||||
code: registerData.code
|
||||
})
|
||||
|
||||
// 后端返回格式: { success: true, data: {...}, message, ... }
|
||||
return { success: true, data: response.data }
|
||||
} catch (error) {
|
||||
return { success: false, error }
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const resetPassword = async (resetData) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await userApi.resetPassword({
|
||||
phone: resetData.phone,
|
||||
new_password: resetData.newPassword,
|
||||
confirm_new_password: resetData.confirmNewPassword,
|
||||
code: resetData.code
|
||||
})
|
||||
|
||||
// 后端返回格式: { success: true, data: {...}, message, ... }
|
||||
return { success: true, data: response.data }
|
||||
} catch (error) {
|
||||
return { success: false, error }
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
const sendCode = async (phone, scene) => {
|
||||
try {
|
||||
const response = await userApi.sendCode({
|
||||
phone,
|
||||
scene
|
||||
})
|
||||
|
||||
// 后端返回格式: { success: true, data: {...}, message, ... }
|
||||
return { success: true, data: response.data }
|
||||
} catch (error) {
|
||||
return { success: false, error }
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
const fetchUserProfile = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await userApi.getProfile()
|
||||
// 后端返回格式: { success: true, data: {...}, message, ... }
|
||||
const userData = response.data
|
||||
user.value = userData
|
||||
isAuthenticated.value = true
|
||||
|
||||
// 记录认证状态变化
|
||||
if (userData.is_certified !== undefined) {
|
||||
console.log('用户认证状态:', userData.is_certified ? '已认证' : '未认证')
|
||||
}
|
||||
|
||||
return { success: true, data: userData }
|
||||
} catch (error) {
|
||||
return { success: false, error }
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
const changePassword = async (passwordData) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await userApi.changePassword({
|
||||
old_password: passwordData.oldPassword,
|
||||
new_password: passwordData.newPassword,
|
||||
confirm_new_password: passwordData.confirmNewPassword,
|
||||
code: passwordData.code
|
||||
})
|
||||
|
||||
// 后端返回格式: { success: true, data: {...}, message, ... }
|
||||
return { success: true, data: response.data }
|
||||
} catch (error) {
|
||||
return { success: false, error }
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 登出
|
||||
const logout = () => {
|
||||
// 清除状态
|
||||
user.value = null
|
||||
accessToken.value = ''
|
||||
tokenType.value = 'Bearer'
|
||||
isAuthenticated.value = false
|
||||
loading.value = false
|
||||
|
||||
// 清除localStorage
|
||||
localStorage.removeItem('access_token')
|
||||
localStorage.removeItem('token_type')
|
||||
clearLocalVersions()
|
||||
}
|
||||
|
||||
// 检查登录状态
|
||||
const checkAuth = async () => {
|
||||
if (!accessToken.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await fetchUserProfile()
|
||||
return result.success
|
||||
} catch (error) {
|
||||
logout()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
const init = async () => {
|
||||
// 监听认证错误事件
|
||||
authEventBus.onAuthError(handleAuthError)
|
||||
|
||||
// 监听版本更新事件
|
||||
window.addEventListener('version:logout', handleVersionLogout)
|
||||
window.addEventListener('version:refresh', handleVersionRefresh)
|
||||
|
||||
// 进行版本检查
|
||||
if (!checkVersions()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (accessToken.value && !user.value) {
|
||||
// 有token但无用户信息,自动拉取
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await fetchUserProfile()
|
||||
isAuthenticated.value = result.success
|
||||
|
||||
// 如果认证成功,启动版本检查器
|
||||
if (result.success) {
|
||||
versionChecker.startAutoCheck()
|
||||
}
|
||||
} catch {
|
||||
isAuthenticated.value = false
|
||||
logout()
|
||||
} finally {
|
||||
loading.value = false
|
||||
initialized.value = true
|
||||
}
|
||||
} else {
|
||||
// 如果已经认证,启动版本检查器
|
||||
if (isAuthenticated.value) {
|
||||
versionChecker.startAutoCheck()
|
||||
}
|
||||
initialized.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 清理
|
||||
const cleanup = () => {
|
||||
// 停止版本检查器
|
||||
versionChecker.stopAutoCheck()
|
||||
|
||||
// 移除事件监听器
|
||||
window.removeEventListener('version:logout', handleVersionLogout)
|
||||
window.removeEventListener('version:refresh', handleVersionRefresh)
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
user,
|
||||
accessToken,
|
||||
tokenType,
|
||||
isAuthenticated,
|
||||
loading,
|
||||
initialized, // 新增
|
||||
|
||||
// 计算属性
|
||||
isLoggedIn,
|
||||
userInfo,
|
||||
isAdmin,
|
||||
userType,
|
||||
isCertified, // 新增
|
||||
isUserInfoComplete, // 新增
|
||||
hasRole,
|
||||
|
||||
// 方法
|
||||
login,
|
||||
register,
|
||||
resetPassword,
|
||||
sendCode,
|
||||
fetchUserProfile,
|
||||
changePassword,
|
||||
logout,
|
||||
checkAuth,
|
||||
init,
|
||||
cleanup,
|
||||
refreshUserProfile // 新增
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user