feat: complete huihui square avatar workflows
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { getAvatarList, createAvatar as apiCreate, deleteAvatar as apiDelete, getTokenBalance, getUserProfile } from '@/api'
|
||||
import { unwrapListData } from '@/utils/avatar-page-data'
|
||||
|
||||
export const useAvatarStore = defineStore('avatar', () => {
|
||||
// 已创建的分身列表(来自后端)
|
||||
const avatars = ref<any[]>([])
|
||||
// 全局 Token 余额(来自后端)
|
||||
const tokenBalance = ref<number>(0)
|
||||
// 当前选中分身 id
|
||||
const currentAvatarId = ref<string | null>(null)
|
||||
// 会会用户资料(头像/昵称,来自会会接口)
|
||||
const userProfile = ref<any>(null)
|
||||
|
||||
// 拉取分身列表
|
||||
const loadAvatars = async () => {
|
||||
try {
|
||||
const res = await getAvatarList()
|
||||
avatars.value = unwrapListData(res)
|
||||
if (avatars.value.length) currentAvatarId.value = avatars.value[0].id
|
||||
} catch (e) {
|
||||
console.error('加载分身失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 拉取 Token 余额
|
||||
const loadTokenBalance = async () => {
|
||||
try {
|
||||
const res = await getTokenBalance()
|
||||
tokenBalance.value = (res as any)?.balance ?? 0
|
||||
} catch (e) {
|
||||
console.error('加载余额失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 拉取会会用户资料(头像/昵称)
|
||||
const loadUserProfile = async () => {
|
||||
// 若已通过 uniapp 壳注入(混合架构),优先保留,不回退到后端 mock
|
||||
if (userProfile.value && (userProfile.value as any).__native) return
|
||||
try {
|
||||
const res = await getUserProfile()
|
||||
userProfile.value = res as any
|
||||
} catch (e) {
|
||||
console.error('加载用户资料失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 由 uniapp 壳注入的会会资料(混合架构),标记为原生来源
|
||||
const setNativeProfile = (profile: { userId?: string; nickname?: string; avatarUrl?: string }) => {
|
||||
userProfile.value = {
|
||||
userId: profile.userId || '',
|
||||
nickname: profile.nickname || '',
|
||||
avatarUrl: profile.avatarUrl || '',
|
||||
__native: true
|
||||
}
|
||||
}
|
||||
|
||||
// 创建分身(写入后端)
|
||||
const addAvatar = async (avatar: any) => {
|
||||
const res: any = await apiCreate(avatar)
|
||||
const created = res?.data ? res.data : res
|
||||
avatars.value.unshift(created)
|
||||
currentAvatarId.value = created.id
|
||||
return created
|
||||
}
|
||||
|
||||
// 删除分身(写入后端,级联清理关联数据)
|
||||
const removeAvatar = async (id: string) => {
|
||||
await apiDelete(id)
|
||||
avatars.value = avatars.value.filter((a) => a.id !== id)
|
||||
if (currentAvatarId.value === id) {
|
||||
currentAvatarId.value = avatars.value[0]?.id || null
|
||||
}
|
||||
}
|
||||
|
||||
// 同步余额(充值后)
|
||||
const setTokenBalance = (balance: number) => {
|
||||
tokenBalance.value = balance
|
||||
}
|
||||
|
||||
return {
|
||||
avatars,
|
||||
tokenBalance,
|
||||
currentAvatarId,
|
||||
userProfile,
|
||||
loadAvatars,
|
||||
loadTokenBalance,
|
||||
loadUserProfile,
|
||||
setNativeProfile,
|
||||
addAvatar,
|
||||
removeAvatar,
|
||||
setTokenBalance
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
export default pinia
|
||||
@@ -0,0 +1,82 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { setAuthToken, sendSmsCode, loginBySms, loginByPassword, logoutUser, type UserProfile } from '@/api'
|
||||
|
||||
const TOKEN_KEY = 'hh_app_token'
|
||||
const USER_KEY = 'hh_app_user'
|
||||
|
||||
// 会会短信登录的本地会话(打通 会会 userId ↔ 本系统用户)
|
||||
export const useUserStore = defineStore('smsuser', () => {
|
||||
const token = ref<string>('')
|
||||
const user = ref<(UserProfile & { huihuiUserId?: string; phone?: string }) | null>(null)
|
||||
const isLogin = ref(false)
|
||||
|
||||
// 启动时从本地恢复会话
|
||||
const loadFromStorage = () => {
|
||||
const t = localStorage.getItem(TOKEN_KEY)
|
||||
const u = localStorage.getItem(USER_KEY)
|
||||
if (t && u) {
|
||||
try {
|
||||
token.value = t
|
||||
user.value = JSON.parse(u)
|
||||
isLogin.value = true
|
||||
setAuthToken(t)
|
||||
} catch {
|
||||
clearLocal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const persist = () => {
|
||||
localStorage.setItem(TOKEN_KEY, token.value)
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(user.value))
|
||||
}
|
||||
|
||||
const clearLocal = () => {
|
||||
localStorage.removeItem(TOKEN_KEY)
|
||||
localStorage.removeItem(USER_KEY)
|
||||
}
|
||||
|
||||
// 发送验证码(返回结果,演示模式含 devCode)
|
||||
const sendCode = async (phone: string) => {
|
||||
return await sendSmsCode(phone)
|
||||
}
|
||||
|
||||
// 短信登录
|
||||
const login = async (phone: string, code: string) => {
|
||||
const res: any = await loginBySms(phone, code)
|
||||
token.value = res.token
|
||||
user.value = { ...(res.user || {}), ...(res.huihui || {}) }
|
||||
isLogin.value = true
|
||||
setAuthToken(res.token)
|
||||
persist()
|
||||
return res
|
||||
}
|
||||
|
||||
// 账号密码登录
|
||||
const loginByPwd = async (account: string, password: string) => {
|
||||
const res: any = await loginByPassword(account, password)
|
||||
token.value = res.token
|
||||
user.value = { ...(res.user || {}), ...(res.huihui || {}) }
|
||||
isLogin.value = true
|
||||
setAuthToken(res.token)
|
||||
persist()
|
||||
return res
|
||||
}
|
||||
|
||||
// 退出
|
||||
const logout = async () => {
|
||||
try {
|
||||
await logoutUser()
|
||||
} catch {
|
||||
/* 忽略网络错误,本地清除即可 */
|
||||
}
|
||||
token.value = ''
|
||||
user.value = null
|
||||
isLogin.value = false
|
||||
setAuthToken(null)
|
||||
clearLocal()
|
||||
}
|
||||
|
||||
return { token, user, isLogin, loadFromStorage, sendCode, login, loginByPwd, logout }
|
||||
})
|
||||
Reference in New Issue
Block a user