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:
Yuzhiran Dev
2026-09-04 11:47:29 +08:00
parent de8ca9c420
commit 66de1dc396
37 changed files with 2138 additions and 1830 deletions
+40 -14
View File
@@ -1,5 +1,5 @@
<template>
<view class="page-trend page-layout">
<view class="page-trend">
<view class="status-bar-placeholder"></view>
<!-- 顶部 -->
@@ -17,7 +17,8 @@
:key="idx"
@click="activeFilter = idx"
>
<text>{{ tag }}</text>
<text class="filter-icon">{{ tag.icon }}</text>
<text class="filter-label">{{ tag.text }}</text>
</view>
</view>
@@ -64,10 +65,18 @@
</template>
<script setup>
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import { ref, computed, onMounted } from 'vue'
import { trendApi } from '@/utils/api'
const filters = ref(['全部', '🚀 产品', '📄 论文', '🔧 工具', '🏢 公司'])
const filters = ref([
{ icon: '📰', text: '全部' },
{ icon: '🚀', text: '产品' },
{ icon: '📄', text: '论文' },
{ icon: '🔧', text: '工具' },
{ icon: '🏢', text: '公司' }
])
const activeFilter = ref(0)
const items = ref([])
const loading = ref(false)
@@ -78,7 +87,7 @@ onMounted(() => {
const displayItems = computed(() => {
if (activeFilter.value === 0) return items.value
const categoryMap = { 1: 'product', 2: 'paper', 3: 'tool', 4: 'company' }
const categoryMap = { 1: '产品', 2: '论文', 3: '工具', 4: '公司' }
const category = categoryMap[activeFilter.value]
if (!category) return items.value
return items.value.filter(item => item.category === category)
@@ -101,19 +110,19 @@ async function fetchTrends() {
function getTagText(item) {
if (item.hot) return '🔥 热门'
if (item.picked) return '📌 精选'
if (item.category === 'product') return '🚀 产品'
if (item.category === 'paper') return '📄 论文'
if (item.category === 'tool') return '🔧 工具'
if (item.category === 'company') return '🏢 公司'
if (item.category === '产品') return '🚀 产品'
if (item.category === '论文') return '📄 论文'
if (item.category === '工具') return '🔧 工具'
if (item.category === '公司') return '🏢 公司'
return '📰 资讯'
}
function getTagClass(item) {
if (item.hot) return 'hot'
if (item.picked) return 'pick'
if (item.category === 'product') return 'product'
if (item.category === 'paper') return 'paper'
if (item.category === 'tool') return 'tool'
if (item.category === '产品') return 'product'
if (item.category === '论文') return 'paper'
if (item.category === '工具') return 'tool'
return ''
}
@@ -181,10 +190,12 @@ function getSampleData() {
}
// 微信小程序分享
// #ifdef MP-WEIXIN
onShareAppMessage(() => ({
title: 'AI 维度 - 你的AI知识助手',
path: '/pages/index/index'
}))
// #endif
</script>
<style lang="scss" scoped>
@@ -225,12 +236,27 @@ onShareAppMessage(() => ({
}
.filter-item {
padding: 6px 14px;
padding: 8px 14px 6px;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: 16px;
border-radius: 12px;
font-size: 13px;
color: var(--text-secondary);
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
min-width: 64px;
}
.filter-icon {
font-size: 16px;
line-height: 1.2;
}
.filter-label {
font-size: 11px;
line-height: 1.2;
}
.filter-item.active {
@@ -245,7 +271,7 @@ onShareAppMessage(() => ({
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
padding: 0 0 16px;
padding: 0 20px 16px;
}
.trend-card {