hookehuyr

feat: 新增消息和我的页面并重构首页布局

新增消息页 (message) 和我的页 (mine),包含基础布局和授权状态展示。
新增 AppTabbar 组件实现底部导航栏,支持首页、消息、我的三栏切换。
重构首页布局,将原有测试功能移入测试中心并更新页面标题。
更新 app.config.js 和组件类型声明以支持新页面和组件。
...@@ -7,7 +7,10 @@ export {} ...@@ -7,7 +7,10 @@ export {}
7 7
8 declare module 'vue' { 8 declare module 'vue' {
9 export interface GlobalComponents { 9 export interface GlobalComponents {
10 + AppTabbar: typeof import('./src/components/AppTabbar.vue')['default']
10 IndexNav: typeof import('./src/components/indexNav.vue')['default'] 11 IndexNav: typeof import('./src/components/indexNav.vue')['default']
12 + NutTabbar: typeof import('@nutui/nutui-taro')['Tabbar']
13 + NutTabbarItem: typeof import('@nutui/nutui-taro')['TabbarItem']
11 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] 14 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
12 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] 15 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
13 QrCode: typeof import('./src/components/qrCode.vue')['default'] 16 QrCode: typeof import('./src/components/qrCode.vue')['default']
......
1 export default { 1 export default {
2 pages: [ 2 pages: [
3 'pages/index/index', 3 'pages/index/index',
4 + 'pages/message/index',
5 + 'pages/mine/index',
4 'pages/pay-test/index', 6 'pages/pay-test/index',
5 'pages/pay-bridge/index', 7 'pages/pay-bridge/index',
6 'pages/webview-preview/index', 8 'pages/webview-preview/index',
......
1 +<template>
2 + <view class="app-tabbar">
3 + <nut-tabbar
4 + :model-value="activeTab"
5 + bottom
6 + placeholder
7 + safe-area-inset-bottom
8 + :active-color="activeColor"
9 + :unactive-color="inactiveColor"
10 + @tab-switch="handleTabSwitch"
11 + >
12 + <nut-tabbar-item
13 + v-for="item in tabItems"
14 + :key="item.name"
15 + :name="item.name"
16 + :tab-title="item.title"
17 + >
18 + <template #icon="{ active }">
19 + <component
20 + :is="item.icon"
21 + size="18"
22 + :color="active ? activeColor : inactiveColor"
23 + />
24 + </template>
25 + </nut-tabbar-item>
26 + </nut-tabbar>
27 + </view>
28 +</template>
29 +
30 +<script setup>
31 +import { computed } from 'vue'
32 +import Taro from '@tarojs/taro'
33 +import { Home, Message, My } from '@nutui/icons-vue-taro'
34 +
35 +const props = defineProps({
36 + current: {
37 + type: String,
38 + required: true,
39 + },
40 +})
41 +
42 +const activeColor = '#a67939'
43 +const inactiveColor = '#8b95a7'
44 +
45 +const tabItems = [
46 + {
47 + name: 'home',
48 + title: '首页',
49 + icon: Home,
50 + url: '/pages/index/index',
51 + },
52 + {
53 + name: 'message',
54 + title: '消息',
55 + icon: Message,
56 + url: '/pages/message/index',
57 + },
58 + {
59 + name: 'mine',
60 + title: '我的',
61 + icon: My,
62 + url: '/pages/mine/index',
63 + },
64 +]
65 +
66 +const activeTab = computed(() => props.current)
67 +
68 +const handleTabSwitch = (...args) => {
69 + const nextTab = args[1]
70 + const target = tabItems.find((item) => item.name === nextTab)
71 +
72 + if (!target || target.name === props.current) {
73 + return
74 + }
75 +
76 + Taro.redirectTo({
77 + url: target.url,
78 + })
79 +}
80 +</script>
81 +
82 +<style lang="less">
83 +.app-tabbar {
84 + :deep(.nut-tabbar) {
85 + left: 24rpx;
86 + right: 24rpx;
87 + bottom: 24rpx;
88 + width: auto;
89 + border: 2rpx solid rgba(166, 121, 57, 0.12);
90 + border-radius: 999rpx;
91 + background: rgba(255, 255, 255, 0.96);
92 + box-shadow: 0 24rpx 60rpx rgba(15, 23, 42, 0.12);
93 + overflow: hidden;
94 + }
95 +
96 + :deep(.nut-tabbar-item_icon-box) {
97 + padding-top: 8rpx;
98 + }
99 +
100 + :deep(.nut-tabbar-item_icon-box_nav-word) {
101 + margin-top: 8rpx;
102 + font-size: 22rpx;
103 + font-weight: 600;
104 + }
105 +}
106 +</style>
...@@ -6,5 +6,5 @@ ...@@ -6,5 +6,5 @@
6 * @Description: 首页配置 6 * @Description: 首页配置
7 */ 7 */
8 export default { 8 export default {
9 - navigationBarTitleText: '觉林寺测试' 9 + navigationBarTitleText: '首页'
10 } 10 }
......
1 <template> 1 <template>
2 <view class="index-page"> 2 <view class="index-page">
3 - <view class="index-header"> 3 + <view class="page-content">
4 - <text class="title">觉林寺</text> 4 + <view class="hero-card">
5 - </view> 5 + <text class="hero-eyebrow">觉林寺小程序</text>
6 - <view class="index-body"> 6 + <text class="hero-title">首页</text>
7 - <text class="tip">授权和支付最小测试入口</text> 7 + <text class="hero-desc">
8 + 当前先完成首页、消息、我的三栏结构,测试能力统一收口到测试中心,避免首页继续堆放调试按钮。
9 + </text>
10 + </view>
11 +
8 <view class="status-card"> 12 <view class="status-card">
9 - <text class="status-label">当前授权状态</text> 13 + <view>
10 - <text class="status-value" :class="{ authed: is_authed }"> 14 + <text class="section-label">当前授权状态</text>
11 - {{ is_authed ? '已授权' : '未授权' }} 15 + <text class="status-text">应用启动时会优先尝试静默授权</text>
16 + </view>
17 + <text class="status-tag" :class="{ authed: isAuthed }">
18 + {{ isAuthed ? '已授权' : '未授权' }}
12 </text> 19 </text>
13 </view> 20 </view>
14 - <button class="primary-btn" @tap="goToPayTest">进入支付测试页</button> 21 +
15 - <button class="secondary-btn" @tap="goToWebviewPreview">打开 WebView 预览页</button> 22 + <view class="overview-grid">
16 - <button class="secondary-btn" @tap="refreshAuthStatus">刷新授权状态</button> 23 + <view class="overview-card">
24 + <text class="card-title">首页</text>
25 + <text class="card-desc">展示当前项目概览与测试入口。</text>
26 + </view>
27 + <view class="overview-card">
28 + <text class="card-title">消息</text>
29 + <text class="card-desc">后续承接通知、订单提醒与系统消息。</text>
30 + </view>
31 + <view class="overview-card">
32 + <text class="card-title">我的</text>
33 + <text class="card-desc">后续承接个人信息、授权状态与常用功能。</text>
34 + </view>
35 + </view>
36 +
37 + <view class="test-entry-card">
38 + <text class="section-label">测试入口</text>
39 + <text class="test-entry-desc">
40 + 支付测试与 WebView 预览已移入测试中心,首页只保留统一入口。
41 + </text>
42 + <button class="primary-btn" @tap="goToTestCenter">进入测试中心</button>
43 + </view>
17 </view> 44 </view>
45 +
46 + <AppTabbar current="home" />
18 </view> 47 </view>
19 </template> 48 </template>
20 49
21 <script setup> 50 <script setup>
22 import { ref } from 'vue' 51 import { ref } from 'vue'
23 import Taro, { useDidShow } from '@tarojs/taro' 52 import Taro, { useDidShow } from '@tarojs/taro'
53 +import AppTabbar from '@/components/AppTabbar.vue'
24 import { hasAuth } from '@/utils/authRedirect' 54 import { hasAuth } from '@/utils/authRedirect'
25 55
26 -Taro.setNavigationBarTitle({ title: '首页' }) 56 +const isAuthed = ref(false)
27 -
28 -const is_authed = ref(false)
29 57
30 const refreshAuthStatus = () => { 58 const refreshAuthStatus = () => {
31 - is_authed.value = hasAuth() 59 + isAuthed.value = hasAuth()
32 } 60 }
33 61
34 -const goToPayTest = () => { 62 +const goToTestCenter = () => {
35 Taro.navigateTo({ 63 Taro.navigateTo({
36 url: '/pages/pay-test/index', 64 url: '/pages/pay-test/index',
37 }) 65 })
38 } 66 }
39 67
40 -const goToWebviewPreview = () => {
41 - Taro.navigateTo({
42 - url: '/pages/webview-preview/index',
43 - })
44 -}
45 -
46 useDidShow(() => { 68 useDidShow(() => {
47 refreshAuthStatus() 69 refreshAuthStatus()
48 }) 70 })
...@@ -51,85 +73,128 @@ useDidShow(() => { ...@@ -51,85 +73,128 @@ useDidShow(() => {
51 <style lang="less"> 73 <style lang="less">
52 .index-page { 74 .index-page {
53 min-height: 100vh; 75 min-height: 100vh;
54 - display: flex; 76 + background:
55 - flex-direction: column; 77 + radial-gradient(circle at top right, rgba(166, 121, 57, 0.18), transparent 32%),
56 - align-items: center; 78 + linear-gradient(180deg, #fffaf3 0%, #f6f7fb 100%);
57 - background-color: #f5f5f5;
58 -
59 - .index-header {
60 - width: 100%;
61 - padding: 80rpx 0 40rpx;
62 - display: flex;
63 - justify-content: center;
64 79
65 - .title { 80 + .page-content {
66 - font-size: 48rpx; 81 + padding: 32rpx 24rpx 0;
67 - font-weight: bold; 82 + box-sizing: border-box;
68 - color: #333;
69 - }
70 } 83 }
71 84
72 - .index-body { 85 + .hero-card,
73 - flex: 1; 86 + .status-card,
74 - display: flex; 87 + .overview-card,
75 - width: 100%; 88 + .test-entry-card {
76 - padding: 0 48rpx 80rpx; 89 + background: rgba(255, 255, 255, 0.94);
90 + border: 2rpx solid rgba(166, 121, 57, 0.08);
91 + border-radius: 28rpx;
92 + box-shadow: 0 20rpx 60rpx rgba(15, 23, 42, 0.06);
77 box-sizing: border-box; 93 box-sizing: border-box;
94 + }
95 +
96 + .hero-card {
97 + display: flex;
78 flex-direction: column; 98 flex-direction: column;
99 + padding: 36rpx 32rpx;
100 + }
101 +
102 + .hero-eyebrow {
103 + font-size: 24rpx;
104 + font-weight: 600;
105 + letter-spacing: 4rpx;
106 + color: #a67939;
107 + }
108 +
109 + .hero-title {
110 + margin-top: 12rpx;
111 + font-size: 52rpx;
112 + font-weight: 700;
113 + color: #1f2937;
114 + }
115 +
116 + .hero-desc {
117 + margin-top: 18rpx;
118 + font-size: 26rpx;
119 + line-height: 1.8;
120 + color: #6b7280;
121 + }
122 +
123 + .status-card {
124 + margin-top: 24rpx;
125 + padding: 28rpx 32rpx;
126 + display: flex;
79 align-items: center; 127 align-items: center;
80 - justify-content: center; 128 + justify-content: space-between;
81 gap: 24rpx; 129 gap: 24rpx;
130 + }
131 +
132 + .section-label {
133 + display: block;
134 + font-size: 30rpx;
135 + font-weight: 600;
136 + color: #111827;
137 + }
138 +
139 + .status-text,
140 + .card-desc,
141 + .test-entry-desc {
142 + display: block;
143 + margin-top: 12rpx;
144 + font-size: 24rpx;
145 + line-height: 1.7;
146 + color: #6b7280;
147 + }
148 +
149 + .status-tag {
150 + flex-shrink: 0;
151 + padding: 12rpx 22rpx;
152 + border-radius: 999rpx;
153 + font-size: 24rpx;
154 + font-weight: 600;
155 + color: #b45309;
156 + background: #fef3c7;
157 + }
158 +
159 + .status-tag.authed {
160 + color: #166534;
161 + background: #dcfce7;
162 + }
163 +
164 + .overview-grid {
165 + margin-top: 24rpx;
166 + display: grid;
167 + grid-template-columns: repeat(2, minmax(0, 1fr));
168 + gap: 20rpx;
169 + }
170 +
171 + .overview-card {
172 + padding: 28rpx;
173 + }
174 +
175 + .overview-card:last-child {
176 + grid-column: 1 / span 2;
177 + }
178 +
179 + .card-title {
180 + display: block;
181 + font-size: 30rpx;
182 + font-weight: 600;
183 + color: #111827;
184 + }
185 +
186 + .test-entry-card {
187 + margin-top: 24rpx;
188 + padding: 32rpx;
189 + }
82 190
83 - .tip { 191 + .primary-btn {
84 - font-size: 28rpx; 192 + margin-top: 24rpx;
85 - color: #999; 193 + border-radius: 999rpx;
86 - } 194 + font-size: 30rpx;
87 - 195 + line-height: 88rpx;
88 - .status-card { 196 + color: #fff;
89 - width: 100%; 197 + background: linear-gradient(135deg, #a67939, #8f5e20);
90 - padding: 32rpx;
91 - border-radius: 24rpx;
92 - background: #fff;
93 - display: flex;
94 - align-items: center;
95 - justify-content: space-between;
96 - box-sizing: border-box;
97 - box-shadow: 0 16rpx 40rpx rgba(0, 0, 0, 0.04);
98 -
99 - .status-label {
100 - font-size: 28rpx;
101 - color: #666;
102 - }
103 -
104 - .status-value {
105 - font-size: 30rpx;
106 - font-weight: 600;
107 - color: #d9485f;
108 - }
109 -
110 - .authed {
111 - color: #2d8c4d;
112 - }
113 - }
114 -
115 - .primary-btn,
116 - .secondary-btn {
117 - width: 100%;
118 - border-radius: 999rpx;
119 - font-size: 30rpx;
120 - line-height: 88rpx;
121 - }
122 -
123 - .primary-btn {
124 - color: #fff;
125 - background: linear-gradient(135deg, #111827, #374151);
126 - }
127 -
128 - .secondary-btn {
129 - color: #374151;
130 - background: #fff;
131 - border: 2rpx solid #d1d5db;
132 - }
133 } 198 }
134 } 199 }
135 </style> 200 </style>
......
1 +export default {
2 + navigationBarTitleText: '消息',
3 +}
1 +<template>
2 + <view class="message-page">
3 + <view class="page-content">
4 + <view class="hero-card">
5 + <text class="hero-title">消息</text>
6 + <text class="hero-desc">
7 + 这里预留给系统通知、预约提醒和支付结果消息。当前先完成 Tab 栏结构,后续再接真实消息数据。
8 + </text>
9 + </view>
10 +
11 + <view class="placeholder-card">
12 + <text class="section-title">当前状态</text>
13 + <text class="section-desc">
14 + 暂无消息内容,后续可在这里接接口列表、未读计数和消息详情跳转。
15 + </text>
16 + </view>
17 + </view>
18 +
19 + <AppTabbar current="message" />
20 + </view>
21 +</template>
22 +
23 +<script setup>
24 +import AppTabbar from '@/components/AppTabbar.vue'
25 +</script>
26 +
27 +<style lang="less">
28 +.message-page {
29 + min-height: 100vh;
30 + background:
31 + radial-gradient(circle at top left, rgba(166, 121, 57, 0.12), transparent 28%),
32 + linear-gradient(180deg, #fffaf4 0%, #f4f6fb 100%);
33 +
34 + .page-content {
35 + padding: 32rpx 24rpx 0;
36 + box-sizing: border-box;
37 + }
38 +
39 + .hero-card,
40 + .placeholder-card {
41 + padding: 32rpx;
42 + border-radius: 28rpx;
43 + background: rgba(255, 255, 255, 0.94);
44 + border: 2rpx solid rgba(166, 121, 57, 0.08);
45 + box-shadow: 0 20rpx 60rpx rgba(15, 23, 42, 0.06);
46 + box-sizing: border-box;
47 + }
48 +
49 + .hero-title,
50 + .section-title {
51 + display: block;
52 + font-size: 40rpx;
53 + font-weight: 700;
54 + color: #111827;
55 + }
56 +
57 + .hero-desc,
58 + .section-desc {
59 + display: block;
60 + margin-top: 16rpx;
61 + font-size: 26rpx;
62 + line-height: 1.8;
63 + color: #6b7280;
64 + }
65 +
66 + .placeholder-card {
67 + margin-top: 24rpx;
68 + }
69 +
70 + .section-title {
71 + font-size: 30rpx;
72 + }
73 +}
74 +</style>
1 +export default {
2 + navigationBarTitleText: '我的',
3 +}
1 +<template>
2 + <view class="mine-page">
3 + <view class="page-content">
4 + <view class="hero-card">
5 + <text class="hero-title">我的</text>
6 + <text class="hero-desc">
7 + 这里预留给个人资料、授权信息和常用入口。当前阶段先提供基础占位和授权状态展示。
8 + </text>
9 + </view>
10 +
11 + <view class="status-card">
12 + <text class="section-title">授权状态</text>
13 + <view class="status-row">
14 + <text class="section-desc">当前小程序登录态</text>
15 + <text class="status-tag" :class="{ authed: isAuthed }">
16 + {{ isAuthed ? '已授权' : '未授权' }}
17 + </text>
18 + </view>
19 + </view>
20 + </view>
21 +
22 + <AppTabbar current="mine" />
23 + </view>
24 +</template>
25 +
26 +<script setup>
27 +import { ref } from 'vue'
28 +import { useDidShow } from '@tarojs/taro'
29 +import AppTabbar from '@/components/AppTabbar.vue'
30 +import { hasAuth } from '@/utils/authRedirect'
31 +
32 +const isAuthed = ref(false)
33 +
34 +useDidShow(() => {
35 + isAuthed.value = hasAuth()
36 +})
37 +</script>
38 +
39 +<style lang="less">
40 +.mine-page {
41 + min-height: 100vh;
42 + background:
43 + radial-gradient(circle at top right, rgba(166, 121, 57, 0.14), transparent 30%),
44 + linear-gradient(180deg, #fffaf4 0%, #f3f5f9 100%);
45 +
46 + .page-content {
47 + padding: 32rpx 24rpx 0;
48 + box-sizing: border-box;
49 + }
50 +
51 + .hero-card,
52 + .status-card {
53 + padding: 32rpx;
54 + border-radius: 28rpx;
55 + background: rgba(255, 255, 255, 0.94);
56 + border: 2rpx solid rgba(166, 121, 57, 0.08);
57 + box-shadow: 0 20rpx 60rpx rgba(15, 23, 42, 0.06);
58 + box-sizing: border-box;
59 + }
60 +
61 + .hero-title,
62 + .section-title {
63 + display: block;
64 + font-size: 40rpx;
65 + font-weight: 700;
66 + color: #111827;
67 + }
68 +
69 + .hero-desc,
70 + .section-desc {
71 + display: block;
72 + margin-top: 16rpx;
73 + font-size: 26rpx;
74 + line-height: 1.8;
75 + color: #6b7280;
76 + }
77 +
78 + .status-card {
79 + margin-top: 24rpx;
80 + }
81 +
82 + .section-title {
83 + font-size: 30rpx;
84 + }
85 +
86 + .status-row {
87 + margin-top: 20rpx;
88 + display: flex;
89 + align-items: center;
90 + justify-content: space-between;
91 + gap: 24rpx;
92 + }
93 +
94 + .status-tag {
95 + flex-shrink: 0;
96 + padding: 12rpx 22rpx;
97 + border-radius: 999rpx;
98 + font-size: 24rpx;
99 + font-weight: 600;
100 + color: #b45309;
101 + background: #fef3c7;
102 + }
103 +
104 + .status-tag.authed {
105 + color: #166534;
106 + background: #dcfce7;
107 + }
108 +}
109 +</style>
1 export default { 1 export default {
2 - navigationBarTitleText: '支付测试', 2 + navigationBarTitleText: '测试中心',
3 } 3 }
......
1 <template> 1 <template>
2 <view class="pay-test-page"> 2 <view class="pay-test-page">
3 <view class="hero-card"> 3 <view class="hero-card">
4 - <text class="hero-title">微信支付最小测试页</text> 4 + <text class="hero-title">测试中心</text>
5 <text class="hero-desc"> 5 <text class="hero-desc">
6 - 这里仅验证授权是否可用,以及点击按钮后能否成功拉起微信支付弹框 6 + 这里统一承接当前首页里的测试能力,包括 WebView 预览和微信支付拉起测试
7 </text> 7 </text>
8 </view> 8 </view>
9 9
10 <view class="panel"> 10 <view class="panel">
11 <view class="panel-head"> 11 <view class="panel-head">
12 + <text class="panel-title">调试入口</text>
13 + </view>
14 + <text class="panel-tip">
15 + 如需验证 H5 通过小程序桥页发起支付,可先从这里进入 WebView 预览页。
16 + </text>
17 + <button class="outline-btn" @tap="goToWebviewPreview">
18 + 打开 WebView 预览页
19 + </button>
20 + </view>
21 +
22 + <view class="panel">
23 + <view class="panel-head">
12 <text class="panel-title">授权状态</text> 24 <text class="panel-title">授权状态</text>
13 <text class="auth-tag" :class="{ authed: is_authed }"> 25 <text class="auth-tag" :class="{ authed: is_authed }">
14 {{ is_authed ? '已授权' : '未授权' }} 26 {{ is_authed ? '已授权' : '未授权' }}
...@@ -53,7 +65,7 @@ ...@@ -53,7 +65,7 @@
53 65
54 <script setup> 66 <script setup>
55 import { ref, watch } from 'vue' 67 import { ref, watch } from 'vue'
56 -import { useDidShow, useLoad } from '@tarojs/taro' 68 +import Taro, { useDidShow, useLoad } from '@tarojs/taro'
57 import { useWechatMiniPay } from '@/composables/useWechatMiniPay' 69 import { useWechatMiniPay } from '@/composables/useWechatMiniPay'
58 70
59 const order_id = ref('') 71 const order_id = ref('')
...@@ -91,6 +103,12 @@ const handleRefreshAuth = async () => { ...@@ -91,6 +103,12 @@ const handleRefreshAuth = async () => {
91 }) 103 })
92 } 104 }
93 105
106 +const goToWebviewPreview = () => {
107 + Taro.navigateTo({
108 + url: '/pages/webview-preview/index',
109 + })
110 +}
111 +
94 const handlePay = async () => { 112 const handlePay = async () => {
95 await pay_by_order_id(order_id.value, { 113 await pay_by_order_id(order_id.value, {
96 auto_auth: false, 114 auto_auth: false,
......