feat: 小程序统一走 Go 后台,咨询与登录切到 /api/v1
去掉协会 WebView 和魔方账密页,微信登录与咨询接口共用同一 token 和拦截器。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<yxg-page title="我的魔方">
|
||||
<view class="hero">
|
||||
<view class="av" @click="pickAvatar">
|
||||
<image class="av-img" :src="avatarSrc" mode="aspectFill" />
|
||||
</view>
|
||||
<text class="name">{{ accountLabel }}</text>
|
||||
<text class="meta">{{ loggedIn ? `${profileCount} 份档案` : '登录后同步你的探索' }}</text>
|
||||
<view v-if="loggedIn" class="logout" @click="logout"><text>退出登录</text></view>
|
||||
<view v-else class="login" @click="openWechatLogin"><text>登录</text></view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="hint"><text>加载中…</text></view>
|
||||
<view v-else-if="error" class="err" @click="load"><text>{{ error }} · 点此重试</text></view>
|
||||
<template v-else>
|
||||
<view v-if="membership && membership.active" class="vip">
|
||||
<text class="vip-t">成长会员 · {{ planLabel }}</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-t">档案与内容</text>
|
||||
<view class="list">
|
||||
<view v-for="it in archive" :key="it.to" class="row" @click="yxgGo(it.to)">
|
||||
<yxg-tool-icon :name="it.icon" :size="28" />
|
||||
<text class="row-t">{{ it.label }}</text>
|
||||
<text class="chev">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-t">成长与会员</text>
|
||||
<view class="list">
|
||||
<view v-for="it in growth" :key="it.to" class="row" @click="yxgGo(it.to)">
|
||||
<yxg-tool-icon :name="it.icon" :size="28" />
|
||||
<text class="row-t">{{ it.label }}</text>
|
||||
<text class="chev">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</yxg-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import YxgPage from '@/components/yxg/yxg-page.vue'
|
||||
import YxgToolIcon from '@/components/yxg/yxg-tool-icon.vue'
|
||||
import { yxgApi, assetURL, setYxgToken } from '@/util/yxgApi.js'
|
||||
import { mineGroupArchive, mineGroupGrowth } from '@/util/yxgCatalog.js'
|
||||
import { YXG_DEFAULT_AVATAR } from '@/util/yxgConfig.js'
|
||||
import { openWechatLogin } from '@/util/yxgAuth.js'
|
||||
import { yxgGo } from '@/util/yxgNav.js'
|
||||
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
const me = ref(null)
|
||||
const membership = ref(null)
|
||||
const profileCount = ref(0)
|
||||
const archive = mineGroupArchive
|
||||
const growth = mineGroupGrowth
|
||||
|
||||
const loggedIn = computed(() => !!me.value)
|
||||
const accountLabel = computed(() => me.value?.nickname || me.value?.phone || '未登录')
|
||||
const avatarSrc = computed(() => assetURL(me.value?.avatar_url) || YXG_DEFAULT_AVATAR)
|
||||
const planLabel = computed(() => {
|
||||
const p = membership.value?.plan
|
||||
if (p === 'quarter') return '季卡'
|
||||
if (p === 'year') return '年卡'
|
||||
if (p === 'month') return '月卡'
|
||||
return '会员中'
|
||||
})
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
me.value = await yxgApi.authMe()
|
||||
} catch {
|
||||
me.value = null
|
||||
membership.value = null
|
||||
profileCount.value = 0
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
const [m, profiles] = await Promise.all([yxgApi.getMembership(), yxgApi.listProfiles()])
|
||||
membership.value = m
|
||||
profileCount.value = (profiles.items || []).length
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : '加载失败'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
try { await yxgApi.authLogout() } catch { /* ignore */ }
|
||||
setYxgToken('')
|
||||
uni.removeStorageSync('userinfo')
|
||||
me.value = null
|
||||
membership.value = null
|
||||
profileCount.value = 0
|
||||
}
|
||||
|
||||
function pickAvatar() {
|
||||
if (!loggedIn.value) {
|
||||
openWechatLogin()
|
||||
return
|
||||
}
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
success: async (res) => {
|
||||
const path = res.tempFilePaths[0]
|
||||
try {
|
||||
me.value = await yxgApi.authUploadAvatar(path)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: e.message || '上传失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onShow(load)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page { background-color: #ffd1c7; }
|
||||
</style>
|
||||
<style scoped>
|
||||
.hero {
|
||||
margin: 8px 16px 0; padding: 20px 16px; background: #fff; border-radius: 20px;
|
||||
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||||
box-shadow: 0 8px 28px rgba(229, 77, 66, 0.1);
|
||||
}
|
||||
.av { width: 72px; height: 72px; border-radius: 50%; overflow: hidden; background: #ffe8e0; }
|
||||
.av-img { width: 72px; height: 72px; }
|
||||
.name { font-size: 18px; font-weight: 700; color: #333; }
|
||||
.meta { font-size: 12px; color: #999; }
|
||||
.logout, .login {
|
||||
margin-top: 8px; padding: 6px 16px; border-radius: 999px; font-size: 13px;
|
||||
}
|
||||
.logout { color: #999; background: #f7f2ef; }
|
||||
.login { color: #fff; background: #e54d42; }
|
||||
.hint, .err { padding: 16px; font-size: 13px; color: #999; }
|
||||
.err { color: #e54d42; }
|
||||
.vip {
|
||||
margin: 14px 16px 0; padding: 12px 14px; border-radius: 14px;
|
||||
background: linear-gradient(145deg, #fff4e0, #ffd98a);
|
||||
}
|
||||
.vip-t { font-size: 14px; font-weight: 700; color: #8a5a18; }
|
||||
.sec { margin: 18px 16px 0; }
|
||||
.sec-t { display: block; font-size: 15px; font-weight: 700; color: #333; margin-bottom: 10px; }
|
||||
.list { background: #fff; border-radius: 16px; overflow: hidden; }
|
||||
.row {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 12px 14px; border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
.row:last-child { border-bottom: none; }
|
||||
.row-t { flex: 1; font-size: 15px; color: #333; }
|
||||
.chev { color: #ccc; font-size: 16px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user