You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
6.5 KiB
193 lines
6.5 KiB
import { defineStore } from 'pinia'
|
|
import { authApi, profileApi } from '@/api/auth'
|
|
import { courseApi } from '@/api/courses'
|
|
import { activityApi } from '@/api/activities'
|
|
import type { ApiUser } from '@/types/api'
|
|
import type { UserProfile } from '@/types'
|
|
import { clearToken, setToken } from '@/utils/request'
|
|
import { DEFAULT_AVATAR_SRC } from '@/utils/svg'
|
|
|
|
import { CAN_SEE_RADAR_STORAGE_KEY } from '@/stores/tabbar'
|
|
|
|
const STORAGE_KEYS = {
|
|
profile: 'slakeProfile',
|
|
}
|
|
|
|
function syncRadarTabPermission(user: ApiUser | null) {
|
|
uni.setStorageSync(CAN_SEE_RADAR_STORAGE_KEY, Boolean(user?.can_see_radar))
|
|
}
|
|
|
|
function readJson<T>(key: string, fallback: T): T {
|
|
try {
|
|
const raw = uni.getStorageSync(key)
|
|
if (!raw) return fallback
|
|
return typeof raw === 'string' ? JSON.parse(raw) : raw
|
|
} catch {
|
|
return fallback
|
|
}
|
|
}
|
|
|
|
export const useUserStore = defineStore('user', {
|
|
state: () => ({
|
|
user: null as ApiUser | null,
|
|
profile: readJson<UserProfile>(STORAGE_KEYS.profile, {}),
|
|
signedCourseIds: [] as number[],
|
|
signedActivityIds: [] as number[],
|
|
authReady: false,
|
|
_authInitPromise: null as Promise<void> | null,
|
|
}),
|
|
getters: {
|
|
isLoggedIn: (state) => Boolean(state.user),
|
|
isPartner: (state) => Boolean(state.user?.is_partner),
|
|
displayName: (state) => {
|
|
if (!state.user) return '未登录'
|
|
return state.user.name || state.user.nickname || state.profile.name || '微信用户'
|
|
},
|
|
avatarSrc: (state) => {
|
|
if (!state.user) return DEFAULT_AVATAR_SRC
|
|
return state.user.avatar_url || state.profile.avatar || DEFAULT_AVATAR_SRC
|
|
},
|
|
canSeeRadar: (state) => Boolean(state.user?.can_see_radar),
|
|
canSeePapers: (state) => Boolean(state.user?.can_see_papers),
|
|
isStaff: (state) => Boolean(state.user?.is_admin || state.user?.is_grid_member),
|
|
},
|
|
actions: {
|
|
async loginWithWechat(code: string, profile?: { nickname?: string; avatar_url?: string }) {
|
|
const data = await authApi.wechatLogin({ code, ...profile })
|
|
this.applyAuth(data.token, data.user)
|
|
},
|
|
async loginWithDev(profile?: { nickname?: string; avatar_url?: string }) {
|
|
const data = await authApi.devLogin(profile)
|
|
this.applyAuth(data.token, data.user)
|
|
},
|
|
applyAuth(token: string, user: ApiUser) {
|
|
setToken(token)
|
|
this.user = user
|
|
this.profile = {
|
|
...this.profile,
|
|
name: user.name || user.nickname || this.profile.name,
|
|
avatar: user.avatar_url || this.profile.avatar,
|
|
company: user.company || this.profile.company,
|
|
mobile: user.mobile || this.profile.mobile,
|
|
title: user.job_title || this.profile.title,
|
|
research_direction: user.research_direction || this.profile.research_direction,
|
|
research_direction_ids: user.research_direction_ids || this.profile.research_direction_ids,
|
|
custom_research_directions: [],
|
|
}
|
|
uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile))
|
|
syncRadarTabPermission(user)
|
|
},
|
|
async fetchMe() {
|
|
const user = await authApi.me()
|
|
this.user = user
|
|
this.profile = {
|
|
...this.profile,
|
|
name: user.name || user.nickname || this.profile.name,
|
|
avatar: user.avatar_url || this.profile.avatar,
|
|
company: user.company || this.profile.company,
|
|
mobile: user.mobile || this.profile.mobile,
|
|
title: user.job_title || this.profile.title,
|
|
research_direction: user.research_direction || this.profile.research_direction,
|
|
research_direction_ids: user.research_direction_ids || this.profile.research_direction_ids,
|
|
custom_research_directions: [],
|
|
}
|
|
uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile))
|
|
syncRadarTabPermission(user)
|
|
await this.syncSignups()
|
|
},
|
|
async syncSignups() {
|
|
if (!this.isLoggedIn) return
|
|
const [courses, activities] = await Promise.all([
|
|
courseApi.mySignups(),
|
|
activityApi.mySignups(),
|
|
])
|
|
this.signedCourseIds = courses.items.map((item) => item.id)
|
|
this.signedActivityIds = activities.items.map((item) => item.id)
|
|
},
|
|
async logout() {
|
|
try {
|
|
await authApi.logout()
|
|
} catch {
|
|
// ignore
|
|
}
|
|
this.user = null
|
|
this.signedCourseIds = []
|
|
this.signedActivityIds = []
|
|
syncRadarTabPermission(null)
|
|
clearToken()
|
|
},
|
|
async saveProfile(profile: UserProfile) {
|
|
let user = this.user
|
|
if (this.isLoggedIn) {
|
|
user = await profileApi.update({
|
|
name: profile.name,
|
|
mobile: profile.mobile,
|
|
company: profile.company,
|
|
avatar_url: profile.avatar,
|
|
nickname: profile.name,
|
|
job_title: profile.title,
|
|
research_direction_ids: profile.research_direction_ids,
|
|
custom_research_directions: profile.custom_research_directions,
|
|
})
|
|
this.user = user
|
|
}
|
|
this.profile = {
|
|
...this.profile,
|
|
...profile,
|
|
research_direction: user?.research_direction || this.profile.research_direction,
|
|
research_direction_ids: user?.research_direction_ids || profile.research_direction_ids,
|
|
custom_research_directions: [],
|
|
}
|
|
uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile))
|
|
},
|
|
markCourseSigned(id: number) {
|
|
if (!this.signedCourseIds.includes(id)) {
|
|
this.signedCourseIds.push(id)
|
|
}
|
|
},
|
|
markActivitySigned(id: number) {
|
|
if (!this.signedActivityIds.includes(id)) {
|
|
this.signedActivityIds.push(id)
|
|
}
|
|
},
|
|
hasSignedCourse(id: number) {
|
|
return this.signedCourseIds.includes(id)
|
|
},
|
|
hasSignedActivity(id: number) {
|
|
return this.signedActivityIds.includes(id)
|
|
},
|
|
async ensureAuth() {
|
|
const token = uni.getStorageSync('slake_miniapp_token')
|
|
if (!token) {
|
|
this.user = null
|
|
this.authReady = true
|
|
return
|
|
}
|
|
if (this.user) {
|
|
this.authReady = true
|
|
return
|
|
}
|
|
if (!this._authInitPromise) {
|
|
this._authInitPromise = this.fetchMe()
|
|
.then(() => {
|
|
this.authReady = true
|
|
})
|
|
.catch(() => {
|
|
clearToken()
|
|
this.user = null
|
|
this.profile = {}
|
|
uni.removeStorageSync(STORAGE_KEYS.profile)
|
|
this.authReady = true
|
|
})
|
|
.finally(() => {
|
|
this._authInitPromise = null
|
|
})
|
|
}
|
|
await this._authInitPromise
|
|
},
|
|
async bootstrap() {
|
|
await this.ensureAuth()
|
|
},
|
|
},
|
|
})
|