feat: 微信分享钩子全页面接入 + 订阅信息结构化映射 + JWT 错误处理加强
- client: 全部页面接入 onShareAppMessage/onShareTimeline 分享钩子 - client: login 页增强、profile 订阅展示优化 - client/stores: 订阅字段扁平结构映射为 subInfo(plan/expiresAt/startDate) - server: auth 路由 JWT 无效/过期统一返回 401 未授权响应 - server: models/tests 同步更新
This commit is contained in:
@@ -4,6 +4,14 @@
|
||||
<view class="bg-orb orb-2"></view>
|
||||
<view class="bg-orb orb-3"></view>
|
||||
|
||||
<!-- 顶部返回 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<text class="nav-back-icon">←</text>
|
||||
<text class="nav-back-text">返回</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="login-container">
|
||||
<view class="logo-wrap">
|
||||
<view class="logo-ring"></view>
|
||||
@@ -28,6 +36,11 @@
|
||||
<input class="form-input" v-model="loginForm.password" type="password" placeholder="请输入密码" />
|
||||
</view>
|
||||
|
||||
<!-- 忘记密码 -->
|
||||
<view class="forgot-row">
|
||||
<text class="forgot-link" @click="goForgotPassword">忘记密码?</text>
|
||||
</view>
|
||||
|
||||
<!-- 登录协议勾选 -->
|
||||
<view class="agreement-row">
|
||||
<view class="agreement-checkbox" :class="{ checked: loginAgreed }" @click="loginAgreed = !loginAgreed">
|
||||
@@ -45,12 +58,14 @@
|
||||
<text>{{ loading ? '登录中...' : '登录' }}</text>
|
||||
</view>
|
||||
<!-- 微信小程序一键登录 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="wx-login-row" v-if="isMiniApp">
|
||||
<view class="wx-login-btn" @click="wechatLogin">
|
||||
<text class="wx-icon">💬</text>
|
||||
<text>微信一键登录</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
<!-- 注册表单 -->
|
||||
@@ -59,6 +74,10 @@
|
||||
<text class="form-label">邮箱 <text style="color:var(--color-vip);font-size:11px;">*</text></text>
|
||||
<input class="form-input" v-model="registerForm.email" type="email" placeholder="请输入邮箱地址" @input="registerForm.email = $event.detail.value.trim(); verifyCodeSent = false" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">用户名 <text style="color:var(--color-vip);font-size:11px;">*</text></text>
|
||||
<input class="form-input" v-model="registerForm.username" placeholder="请输入用户名(用于登录)" @input="registerForm.username = $event.detail.value.trim()" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">昵称 <text style="color:var(--text-muted);font-size:11px;">(选填)</text></text>
|
||||
<input class="form-input" v-model="registerForm.nickName" placeholder="显示名称,留空自动生成" />
|
||||
@@ -100,21 +119,30 @@
|
||||
<view class="footer-info">
|
||||
<text>© 2026 宇之然 · AI 维度</text>
|
||||
</view>
|
||||
|
||||
<!-- 显著有效的「拒绝/暂不登录」入口,避免强制登录 -->
|
||||
<view class="skip-login" @click="goBrowse">
|
||||
<text>暂不登录,先体验</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { useUserStore } from '@/stores/index'
|
||||
|
||||
const isMiniApp = ref(false)
|
||||
// #ifdef MP-WEIXIN
|
||||
try {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
if (sysInfo && sysInfo.version && sysInfo.platform) {
|
||||
isMiniApp.value = true
|
||||
}
|
||||
} catch (e) {}
|
||||
// #endif
|
||||
|
||||
const activeTab = ref('login')
|
||||
const loading = ref(false)
|
||||
@@ -127,7 +155,7 @@ let countdownTimer = null
|
||||
const userStore = useUserStore()
|
||||
|
||||
const loginForm = ref({ email: '', password: '' })
|
||||
const registerForm = ref({ email: '', password: '', nickName: '', verifyCode: '' })
|
||||
const registerForm = ref({ email: '', username: '', password: '', nickName: '', verifyCode: '' })
|
||||
|
||||
const BASE_URL = 'https://wdkj.yuzhiran.com.cn'
|
||||
|
||||
@@ -241,6 +269,10 @@ const handleRegister = async () => {
|
||||
uni.showToast({ title: '请输入密码', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!registerForm.value.username) {
|
||||
uni.showToast({ title: '请输入用户名', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (registerForm.value.password.length < 6) {
|
||||
uni.showToast({ title: '密码至少 6 位', icon: 'none' })
|
||||
return
|
||||
@@ -255,7 +287,8 @@ const handleRegister = async () => {
|
||||
registerForm.value.email,
|
||||
registerForm.value.password,
|
||||
registerForm.value.nickName || '',
|
||||
registerForm.value.verifyCode
|
||||
registerForm.value.verifyCode,
|
||||
registerForm.value.username
|
||||
)
|
||||
uni.showToast({ title: '注册成功', icon: 'success' })
|
||||
setTimeout(() => uni.switchTab({ url: '/pages/index/index' }), 1000)
|
||||
@@ -274,11 +307,26 @@ function openAgreement(type) {
|
||||
uni.navigateTo({ url: pages[type] || pages.terms })
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
uni.navigateBack({ fallback: () => uni.switchTab({ url: '/pages/index/index' }) })
|
||||
}
|
||||
|
||||
/** 拒绝登录:返回首页以游客身份浏览,不强制登录 */
|
||||
function goBrowse() {
|
||||
uni.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
|
||||
function goForgotPassword() {
|
||||
uni.showToast({ title: '请联系管理员重置密码', icon: 'none' })
|
||||
}
|
||||
|
||||
// 微信小程序分享
|
||||
// #ifdef MP-WEIXIN
|
||||
onShareAppMessage(() => ({
|
||||
title: 'AI 维度 - 你的AI知识助手',
|
||||
path: '/pages/index/index'
|
||||
}))
|
||||
// #endif
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -303,6 +351,19 @@ onShareAppMessage(() => ({
|
||||
.orb-2 { width: 160px; height: 160px; top: 30%; right: -40px; background: var(--dim3-color); }
|
||||
.orb-3 { width: 180px; height: 180px; bottom: -60px; left: 30%; background: var(--dim5-color); }
|
||||
|
||||
// 顶部导航栏
|
||||
.nav-bar {
|
||||
position: absolute; top: 0; left: 0; right: 0; z-index: 10;
|
||||
padding: 44px 20px 0;
|
||||
.nav-back {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 8px 12px; cursor: pointer;
|
||||
&:active { opacity: 0.6; }
|
||||
}
|
||||
.nav-back-icon { font-size: 20px; color: var(--text-primary); }
|
||||
.nav-back-text { font-size: 14px; color: var(--text-secondary); }
|
||||
}
|
||||
|
||||
.login-container {
|
||||
position: relative; z-index: 1;
|
||||
width: 100%; max-width: 360px;
|
||||
@@ -354,6 +415,15 @@ onShareAppMessage(() => ({
|
||||
}
|
||||
}
|
||||
|
||||
// 忘记密码
|
||||
.forgot-row {
|
||||
display: flex; justify-content: flex-end; margin: -8px 0 0;
|
||||
.forgot-link {
|
||||
font-size: 12px; color: var(--color-brand); padding: 4px 0;
|
||||
&:active { opacity: 0.6; }
|
||||
}
|
||||
}
|
||||
|
||||
// 验证码行
|
||||
.verify-row {
|
||||
display: flex; gap: 10px;
|
||||
@@ -417,5 +487,20 @@ onShareAppMessage(() => ({
|
||||
}
|
||||
|
||||
.footer-info { margin-top: 40px; font-size: 11px; color: var(--text-muted); }
|
||||
|
||||
// 暂不登录入口(拒绝/返回,显著有效)
|
||||
.skip-login {
|
||||
margin-top: 24px;
|
||||
padding: 14px 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
&:active { opacity: 0.7; }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user