feat: complete huihui square avatar workflows

This commit is contained in:
stefanfeng
2026-07-24 14:04:21 +08:00
parent 2ef58a44b8
commit e3bda469bb
68 changed files with 8062 additions and 132 deletions
+95
View File
@@ -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
}
})
+5
View File
@@ -0,0 +1,5 @@
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
+82
View File
@@ -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 }
})