release: align H5 demo UI and login

This commit is contained in:
WorkBuddy
2026-10-21 17:34:03 +08:00
parent d51c4c6a76
commit afee993626
35 changed files with 265 additions and 32 deletions
+3
View File
@@ -6,6 +6,9 @@ export const authService = {
wechatLogin(payload: { code: string }) {
return post('/auth/wechat/login', payload, { idempotencyKey: payload.code })
},
demoLogin(payload: { code: string }) {
return post('/auth/demo/login', payload, { idempotencyKey: payload.code })
},
bindPhone(payload: Record<string, any>) {
return post('/auth/phone/bind', payload, { idempotencyKey: payload.phoneCode || payload.encryptedData })
},
+1
View File
@@ -1,6 +1,7 @@
<template>
<div class="page-layout">
<van-nav-bar
class="page-layout__nav"
:title="title"
left-arrow
fixed
+5 -2
View File
@@ -10,6 +10,7 @@
<van-tabbar route fixed placeholder class="tab-layout__bar">
<van-tabbar-item to="/home" icon="wap-home-o">首页</van-tabbar-item>
<van-tabbar-item to="/discover" icon="friends-o">找对象</van-tabbar-item>
<van-tabbar-item to="/messages" icon="chat-o">消息</van-tabbar-item>
<van-tabbar-item to="/activity" icon="flag-o">活动</van-tabbar-item>
<van-tabbar-item to="/mine" icon="user-o">我的</van-tabbar-item>
</van-tabbar>
@@ -17,8 +18,8 @@
</template>
<script setup lang="ts">
// 固定 4 项 TabBar,顺序与 miniapp/app.json 一致。
const cached = ['HomePage', 'DiscoverPage', 'ActivityPage', 'MinePage']
// 固定 5 项 TabBar,顺序与 miniapp/app.json 一致。
const cached = ['HomePage', 'DiscoverPage', 'MessagesPage', 'ActivityPage', 'MinePage']
</script>
<style scoped>
@@ -31,5 +32,7 @@ const cached = ['HomePage', 'DiscoverPage', 'ActivityPage', 'MinePage']
}
.tab-layout__bar {
border-top: 1px solid var(--mini-border);
background: var(--mini-surface);
box-shadow: 0 -8px 24px rgba(28, 30, 88, 0.08);
}
</style>
+46 -22
View File
@@ -10,7 +10,7 @@
<div class="home__brand-slogan">公益婚恋服务平台</div>
</div>
</div>
<div class="home__notice" @click="router.push('/notifications')">
<div class="home__notice" @click="router.push('/messages')">
<van-icon name="bell" size="18" />
<span>通知</span>
</div>
@@ -44,7 +44,7 @@
<van-swipe v-if="banners.length" :autoplay="4500" indicator-color="#FF4F91" class="home__swipe">
<van-swipe-item v-for="b in banners" :key="b.id">
<div class="home__banner-card" @click="onBannerTap(b)">
<div class="home__banner-cover">{{ (b.title || '善').slice(0, 1) }}</div>
<img class="home__banner-image" :src="b.imageFileId || '/assets/brand/home-hero-v2.jpg'" alt="" />
<div class="home__banner-copy">
<div class="home__banner-kicker">公益 · 本地 · 真实 · 安全</div>
<div class="home__banner-title">{{ b.title }}</div>
@@ -54,7 +54,7 @@
</van-swipe-item>
</van-swipe>
<div v-else class="home__banner-card">
<div class="home__banner-cover">缘</div>
<img class="home__banner-image" src="/assets/brand/home-hero-v2.jpg" alt="" />
<div class="home__banner-copy">
<div class="home__banner-kicker">公益 · 本地 · 真实 · 安全</div>
<div class="home__banner-title">在丹阳,遇见更好的你</div>
@@ -73,7 +73,7 @@
:class="{ 'home__entry--disabled': !e.enabled }"
@click="onEntryTap(e)"
>
<div class="home__entry-icon" :class="`tone-${toneOf(e.code)}`">{{ (e.label || '善').slice(0, 1) }}</div>
<div class="home__entry-icon" :class="`tone-${toneOf(e.code)}`"><img :src="iconOf(e.code)" :alt="e.label" /></div>
<div class="home__entry-label">{{ e.label }}</div>
<div class="home__entry-sub">{{ subOf(e.code) }}</div>
</div>
@@ -121,7 +121,7 @@
class="home__mini"
@click="onServiceTap(s.type)"
>
<div class="home__mini-cover" :class="`tone-${toneOf(s.type)}`">{{ (s.title || '善').slice(0, 1) }}</div>
<img class="home__mini-cover" :src="coverOf(s.type)" alt="" />
<div class="home__mini-title">{{ s.title }}</div>
<div class="home__mini-sub">{{ s.ctaText }}</div>
</div>
@@ -137,7 +137,7 @@
class="home__act"
@click="router.push({ path: '/activity/detail', query: { id: a.id } })"
>
<div class="home__act-cover">{{ (a.title || '活').slice(0, 1) }}</div>
<img class="home__act-cover" :src="a.coverFileId || '/assets/covers/activity-default.jpg'" alt="" />
<div class="home__act-info">
<div class="home__act-tag">公益活动</div>
<div class="home__act-title">{{ a.title }}</div>
@@ -150,7 +150,7 @@
<!-- 善缘服务站 -->
<UiSectionHeader title="善缘服务站" />
<div v-if="serviceLocation" class="home__station" @click="router.push('/service-station')">
<div class="home__station-icon">站</div>
<img class="home__station-icon" src="/assets/covers/station-default.jpg" alt="" />
<div class="home__station-info">
<div class="home__station-name">{{ serviceLocation.name }}</div>
<div class="home__station-desc">
@@ -175,7 +175,7 @@
class="home__article"
@click="router.push({ path: '/content/detail', query: { id: art.id } })"
>
<div class="home__article-cover">{{ (art.title || '课').slice(0, 1) }}</div>
<img class="home__article-cover" :src="art.coverFileId || '/assets/covers/classroom-default.jpg'" alt="" />
<div class="home__article-main">
<div class="home__article-title">{{ art.title }}</div>
<div class="home__article-meta">善缘课堂 · 公益内容</div>
@@ -188,7 +188,7 @@
<!-- 善缘安全提醒 -->
<UiSectionHeader title="善缘安全提醒" />
<div class="home__safety" @click="router.push('/anti-fraud')">
<div class="home__safety-icon">盾</div>
<img class="home__safety-icon" src="/assets/covers/anti-fraud-default.jpg" alt="" />
<div class="home__safety-text">{{ safetyNotice.text }}</div>
</div>
</template>
@@ -247,6 +247,16 @@ const ENTRY_SUBS: Record<string, string> = {
anti_fraud: '守护真爱',
service_station: '就在身边',
}
const ENTRY_ICONS: Record<string, string> = {
find_object: '/assets/icons/home/find-object.png',
matchmaker: '/assets/icons/home/matchmaker.png',
activities: '/assets/icons/home/activity.png',
counseling: '/assets/icons/home/counseling.png',
classroom: '/assets/icons/home/classroom.png',
premarital: '/assets/icons/home/premarital.png',
anti_fraud: '/assets/icons/home/anti-fraud.png',
service_station: '/assets/icons/home/service-station.png',
}
/** 固定 8 入口默认值(服务端未返回时兜底;顺序不可变更) */
const DEFAULT_ENTRIES = [
{ code: 'find_object', label: '找对象', enabled: true },
@@ -327,6 +337,15 @@ function toneOf(code: string): string {
function subOf(code: string): string {
return ENTRY_SUBS[code] || ''
}
function iconOf(code: string): string {
return ENTRY_ICONS[code] || ENTRY_ICONS.find_object
}
function coverOf(type: string): string {
if (type === 'counseling') return '/assets/covers/counseling-default.jpg'
if (type === 'premarital') return '/assets/covers/classroom-default.jpg'
if (type === 'service_station') return '/assets/covers/station-default.jpg'
return '/assets/covers/counseling-default.jpg'
}
async function load() {
loading.value = true
@@ -454,7 +473,7 @@ onMounted(load)
/* 品牌区 */
.home__hero {
background: var(--mini-brand-gradient);
background: var(--mini-gradient-night);
padding: 16px 16px 0;
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
@@ -550,19 +569,17 @@ onMounted(load)
height: 150px;
border-radius: var(--mini-radius);
overflow: hidden;
background: linear-gradient(135deg, #2a3a86 0%, #4b2a6b 100%);
background: var(--mini-gradient-night);
display: flex;
align-items: flex-end;
}
.home__banner-cover {
.home__banner-image {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 72px;
font-weight: 700;
color: rgba(255, 255, 255, 0.14);
width: 100%;
height: 100%;
object-fit: cover;
opacity: .9;
}
.home__banner-copy {
position: relative;
@@ -617,6 +634,11 @@ onMounted(load)
font-size: 18px;
font-weight: 600;
}
.home__entry-icon img {
width: 28px;
height: 28px;
object-fit: contain;
}
.home__entry-label {
font-size: 12px;
color: var(--mini-text-strong);
@@ -655,6 +677,7 @@ onMounted(load)
justify-content: center;
font-weight: 600;
margin-bottom: 6px;
object-fit: cover;
}
.home__mini-title {
font-size: 13px;
@@ -714,7 +737,8 @@ onMounted(load)
}
.home__act-cover {
height: 84px;
background: linear-gradient(135deg, #6a5acd 0%, #8367c7 100%);
width: 100%;
object-fit: cover;
color: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
@@ -754,7 +778,7 @@ onMounted(load)
width: 46px;
height: 46px;
border-radius: 12px;
background: linear-gradient(135deg, #2fb36d 0%, #1f8a52 100%);
object-fit: cover;
color: #fff;
display: flex;
align-items: center;
@@ -796,7 +820,7 @@ onMounted(load)
width: 44px;
height: 44px;
border-radius: 10px;
background: linear-gradient(135deg, #f5a623 0%, #e08b00 100%);
object-fit: cover;
color: #fff;
display: flex;
align-items: center;
@@ -830,7 +854,7 @@ onMounted(load)
width: 30px;
height: 30px;
border-radius: 8px;
background: var(--mini-accent-pink);
object-fit: cover;
color: #fff;
display: flex;
align-items: center;
@@ -1,5 +1,5 @@
<template>
<PageLayout title="通知">
<PageLayout :title="pageTitle">
<div class="notif">
<div class="notif__bar">
<span class="notif__count">{{ unreadCount > 0 ? `${unreadCount} 条未读` : '暂无未读' }}</span>
@@ -44,7 +44,8 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { notificationService } from '@/api/services'
import { useAppStore } from '@/stores/app'
import { formatDateTime } from '@/utils/datetime'
@@ -55,6 +56,8 @@ import UiErrorState from '@/components/UiErrorState.vue'
import UiTag from '@/components/UiTag.vue'
const appStore = useAppStore()
const route = useRoute()
const pageTitle = computed(() => route.path === '/messages' ? '消息' : '通知')
const TYPE_TEXT: Record<string, string> = {
system: '系统通知',
+1
View File
@@ -30,6 +30,7 @@ export const routes: RouteRecordRaw[] = [
{ path: '', redirect: '/home' },
{ path: 'home', name: 'HomePage', component: () => import('@/pages/home/HomePage.vue') },
{ path: 'discover', name: 'DiscoverPage', component: () => import('@/pages/discover/DiscoverPage.vue') },
{ path: 'messages', name: 'MessagesPage', component: () => import('@/pages/settings/NotificationsPage.vue') },
{ path: 'activity', name: 'ActivityPage', component: () => import('@/pages/activity/ActivityPage.vue') },
{ path: 'mine', name: 'MinePage', component: () => import('@/pages/mine/MinePage.vue') },
],
+1 -1
View File
@@ -28,7 +28,7 @@ export const useAuthStore = defineStore('auth', () => {
* 无需微信环境即可切换到预置演示用户(见实施计划 D2 / 登录策略)。
*/
async function loginByDemoCode(code: string) {
const data = await authService.wechatLogin({ code })
const data = await authService.demoLogin({ code })
applyTokens(data)
return data
}
+30
View File
@@ -20,6 +20,36 @@ body {
-webkit-font-smoothing: antialiased;
}
button,
.van-button,
.van-tabbar-item,
.van-cell,
.van-nav-bar {
font-family: inherit;
}
.van-nav-bar {
background: var(--mini-night-start);
color: var(--mini-text-on-night);
}
.van-nav-bar .van-nav-bar__title,
.van-nav-bar .van-icon,
.van-nav-bar .van-nav-bar__text {
color: var(--mini-text-on-night);
}
.van-button--primary {
background: var(--mini-gradient-brand-solid);
border-color: transparent;
}
.van-button--plain.van-button--primary {
color: var(--mini-tabbar-active);
background: transparent;
border-color: var(--mini-tabbar-active);
}
#app {
min-height: 100%;
}
+8
View File
@@ -4,6 +4,8 @@
--mini-night-start: #172a72;
--mini-night-end: #25104f;
--mini-brand-gradient: linear-gradient(135deg, #172a72 0%, #25104f 100%);
--mini-gradient-night: linear-gradient(160deg, #172a72 0%, #25104f 100%);
--mini-gradient-brand-solid: linear-gradient(135deg, #c24c50 0%, #b8434a 100%);
/* 强调粉 / 主色 */
--mini-accent-pink: #ff4f91;
@@ -37,4 +39,10 @@
--mini-radius-lg: 16px;
--mini-shadow: 0 4px 16px rgba(23, 42, 114, 0.08);
--mini-shadow-strong: 0 8px 28px rgba(23, 42, 114, 0.14);
--mini-glass: rgba(255, 255, 255, 0.1);
--mini-glass-strong: rgba(255, 255, 255, 0.16);
--mini-hairline: rgba(255, 255, 255, 0.16);
--mini-text-on-night: #fff;
--mini-text-on-night-muted: rgba(255, 255, 255, 0.72);
--mini-banner-scrim: linear-gradient(90deg, rgba(23, 42, 114, 0.84) 0%, rgba(37, 16, 79, 0.4) 56%, rgba(255, 79, 145, 0.12) 100%);
}