feat: refine merged venue home and auth states
This commit is contained in:
@@ -9,7 +9,9 @@
|
|||||||
"dev:h5": "uni",
|
"dev:h5": "uni",
|
||||||
"build:h5": "uni build",
|
"build:h5": "uni build",
|
||||||
"verify:tabbar": "node scripts/verify-venue-tabbar.mjs",
|
"verify:tabbar": "node scripts/verify-venue-tabbar.mjs",
|
||||||
"verify:startup": "node scripts/verify-venue-startup.mjs"
|
"verify:startup": "node scripts/verify-venue-startup.mjs",
|
||||||
|
"verify:home-switch": "node scripts/verify-home-venue-switch.mjs",
|
||||||
|
"verify:comprehensive-auth": "node scripts/verify-comprehensive-auth-state.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dcloudio/uni-app": "3.0.0-alpha-5000120260211001",
|
"@dcloudio/uni-app": "3.0.0-alpha-5000120260211001",
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { dirname, resolve } from 'node:path'
|
||||||
|
|
||||||
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
const srcRoot = resolve(root, 'src')
|
||||||
|
const failures = []
|
||||||
|
|
||||||
|
const pages = [
|
||||||
|
'pages/comprehensive/bookings/index.vue',
|
||||||
|
'pages/comprehensive/profile/index.vue'
|
||||||
|
]
|
||||||
|
|
||||||
|
const expectIncludes = (content, expected, label) => {
|
||||||
|
if (!content.includes(expected)) failures.push(`${label} is missing: ${expected}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectExcludes = (content, unexpected, label) => {
|
||||||
|
if (content.includes(unexpected)) failures.push(`${label} should not contain: ${unexpected}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const page of pages) {
|
||||||
|
const content = readFileSync(resolve(srcRoot, page), 'utf8')
|
||||||
|
expectIncludes(content, 'v-if="!session.isLoggedIn"', `${page} unauth condition`)
|
||||||
|
expectIncludes(content, '<StatePanel', `${page} unauth component`)
|
||||||
|
expectIncludes(content, 'type="login"', `${page} login state type`)
|
||||||
|
expectIncludes(content, ':show-mark="false"', `${page} login state mark`)
|
||||||
|
expectIncludes(content, "import StatePanel from '@/components/ice/StatePanel.vue'", `${page} StatePanel import`)
|
||||||
|
expectExcludes(content, '星河综合运动中心', `${page} unauth copy`)
|
||||||
|
expectExcludes(content, '登录后查看', `${page} unauth copy`)
|
||||||
|
expectExcludes(content, 'class="brand"', `${page} brand style`)
|
||||||
|
expectExcludes(content, 'class="desc"', `${page} desc style`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures.length) {
|
||||||
|
console.error(`Comprehensive auth state verification failed with ${failures.length} issue(s):`)
|
||||||
|
for (const failure of failures) console.error(`- ${failure}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Comprehensive auth state verification passed.')
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { existsSync, readFileSync } from 'node:fs'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { dirname, resolve } from 'node:path'
|
||||||
|
|
||||||
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
const srcRoot = resolve(root, 'src')
|
||||||
|
const failures = []
|
||||||
|
|
||||||
|
const read = (relativePath) => readFileSync(resolve(srcRoot, relativePath), 'utf8')
|
||||||
|
const expectFile = (relativePath) => {
|
||||||
|
const fullPath = resolve(srcRoot, relativePath)
|
||||||
|
if (!existsSync(fullPath)) failures.push(`Missing file: ${relativePath}`)
|
||||||
|
}
|
||||||
|
const expectIncludes = (content, expected, label) => {
|
||||||
|
if (!content.includes(expected)) failures.push(`${label} is missing: ${expected}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectFile('components/VenueSwitchHeader.vue')
|
||||||
|
|
||||||
|
if (existsSync(resolve(srcRoot, 'components/VenueSwitchHeader.vue'))) {
|
||||||
|
const component = read('components/VenueSwitchHeader.vue')
|
||||||
|
expectIncludes(component, "uni.navigateTo({ url: '/pages/venue-select/index?mode=switch' })", 'VenueSwitchHeader switch navigation')
|
||||||
|
expectIncludes(component, 'class="venue-switch-header"', 'VenueSwitchHeader layout root')
|
||||||
|
expectIncludes(component, 'class="venue-switch-action"', 'VenueSwitchHeader action')
|
||||||
|
expectIncludes(component, '<text class="venue-switch-action__text">切换场馆</text>', 'VenueSwitchHeader action text')
|
||||||
|
expectIncludes(component, '<view class="venue-switch-action__arrow" />', 'VenueSwitchHeader css arrow')
|
||||||
|
}
|
||||||
|
|
||||||
|
const homePages = [
|
||||||
|
{ file: 'pages/ice/home/index.vue', venue: '冰场馆' },
|
||||||
|
{ file: 'pages/comprehensive/home/index.vue', venue: '综合场馆' }
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const { file, venue } of homePages) {
|
||||||
|
const content = read(file)
|
||||||
|
expectIncludes(content, '<VenueSwitchHeader', `${file} template`)
|
||||||
|
expectIncludes(content, `venue-name="${venue}"`, `${file} venue name`)
|
||||||
|
expectIncludes(content, "import VenueSwitchHeader from '@/components/VenueSwitchHeader.vue'", `${file} imports`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures.length) {
|
||||||
|
console.error(`Home venue switch verification failed with ${failures.length} issue(s):`)
|
||||||
|
for (const failure of failures) console.error(`- ${failure}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Home venue switch verification passed.')
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<view class="venue-switch-header" @tap="switchVenue">
|
||||||
|
<view class="venue-switch-main">
|
||||||
|
<text class="venue-switch-kicker">当前场馆</text>
|
||||||
|
<text class="venue-switch-name">{{ venueName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="venue-switch-action">
|
||||||
|
<text class="venue-switch-action__text">切换场馆</text>
|
||||||
|
<view class="venue-switch-action__arrow" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
venueName: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const switchVenue = () => {
|
||||||
|
uni.navigateTo({ url: '/pages/venue-select/index?mode=switch' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.venue-switch-header {
|
||||||
|
min-height: 96rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: $spacing-md;
|
||||||
|
margin-bottom: $spacing-md;
|
||||||
|
padding: 18rpx 20rpx;
|
||||||
|
border: 1rpx solid rgba(226, 232, 240, 0.96);
|
||||||
|
border-radius: $border-radius-base;
|
||||||
|
background: rgba(255, 255, 255, 0.98);
|
||||||
|
box-shadow: 0 10rpx 28rpx rgba(15, 23, 42, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-header:active {
|
||||||
|
background: $bg-color-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-main {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-kicker {
|
||||||
|
color: $text-color-light;
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-name {
|
||||||
|
overflow: hidden;
|
||||||
|
color: $text-color-main;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.25;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-action {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
border: 1rpx solid $border-color;
|
||||||
|
border-radius: $border-radius-sm;
|
||||||
|
background: $bg-color-soft;
|
||||||
|
color: $text-color-regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-action__text,
|
||||||
|
.venue-switch-action__arrow {
|
||||||
|
color: $text-color-regular;
|
||||||
|
font-size: 23rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-switch-action__arrow {
|
||||||
|
width: 12rpx;
|
||||||
|
height: 12rpx;
|
||||||
|
border-top: 2rpx solid $text-color-muted;
|
||||||
|
border-right: 2rpx solid $text-color-muted;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="bookings-page">
|
<view class="bookings-page">
|
||||||
<view v-if="!session.isLoggedIn" class="unlogged">
|
<StatePanel
|
||||||
<text class="brand">星河综合运动中心</text>
|
v-if="!session.isLoggedIn"
|
||||||
<text class="desc">登录后查看课程预约、包场预约与篮球门票权益。</text>
|
primary-text="微信一键登录"
|
||||||
<button class="primary-button login-btn" @tap="login">微信一键登录</button>
|
type="login"
|
||||||
</view>
|
:show-mark="false"
|
||||||
|
@primary="login"
|
||||||
|
/>
|
||||||
|
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="sticky-tabs">
|
<view class="sticky-tabs">
|
||||||
@@ -42,6 +44,7 @@ import { ticketApi } from '@/modules/comprehensive/api/ticket'
|
|||||||
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
|
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
|
||||||
import { formatDateTime, formatTimeRange } from '@/modules/comprehensive/utils/date'
|
import { formatDateTime, formatTimeRange } from '@/modules/comprehensive/utils/date'
|
||||||
import VenueTabBar from '@/components/VenueTabBar.vue'
|
import VenueTabBar from '@/components/VenueTabBar.vue'
|
||||||
|
import StatePanel from '@/components/ice/StatePanel.vue'
|
||||||
|
|
||||||
const session = useComprehensiveSessionStore()
|
const session = useComprehensiveSessionStore()
|
||||||
const businessType = ref('course')
|
const businessType = ref('course')
|
||||||
@@ -133,33 +136,6 @@ const goDetail = (item) => {
|
|||||||
background: $bg-color-soft;
|
background: $bg-color-soft;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unlogged {
|
|
||||||
min-height: 80vh;
|
|
||||||
padding: 180rpx $spacing-lg 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
color: $text-color-main;
|
|
||||||
font-size: 42rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: 2rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
margin-top: $spacing-md;
|
|
||||||
color: $text-color-muted;
|
|
||||||
font-size: 26rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn {
|
|
||||||
width: 520rpx;
|
|
||||||
margin-top: 100rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sticky-tabs {
|
.sticky-tabs {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="home-container">
|
<view class="home-container">
|
||||||
|
<VenueSwitchHeader venue-name="综合场馆" />
|
||||||
|
|
||||||
<view class="banner-wrapper">
|
<view class="banner-wrapper">
|
||||||
<swiper
|
<swiper
|
||||||
v-if="banners.length"
|
v-if="banners.length"
|
||||||
@@ -72,6 +74,7 @@ import { onPullDownRefresh } from '@dcloudio/uni-app'
|
|||||||
import { contentApi } from '@/modules/comprehensive/api/content'
|
import { contentApi } from '@/modules/comprehensive/api/content'
|
||||||
import { formatDate, formatTimeRange } from '@/modules/comprehensive/utils/date'
|
import { formatDate, formatTimeRange } from '@/modules/comprehensive/utils/date'
|
||||||
import VenueTabBar from '@/components/VenueTabBar.vue'
|
import VenueTabBar from '@/components/VenueTabBar.vue'
|
||||||
|
import VenueSwitchHeader from '@/components/VenueSwitchHeader.vue'
|
||||||
|
|
||||||
const banners = ref([])
|
const banners = ref([])
|
||||||
const upcomingCourses = ref([])
|
const upcomingCourses = ref([])
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="profile-page container">
|
<view class="profile-page container">
|
||||||
<view v-if="!session.isLoggedIn" class="unlogged">
|
<StatePanel
|
||||||
<text class="brand">星河综合运动中心</text>
|
v-if="!session.isLoggedIn"
|
||||||
<text class="desc">登录后查看余额、订单、门票权益与个人资料。</text>
|
primary-text="微信一键登录"
|
||||||
<button class="primary-button login-btn" @tap="login">微信一键登录</button>
|
type="login"
|
||||||
</view>
|
:show-mark="false"
|
||||||
|
@primary="login"
|
||||||
|
/>
|
||||||
|
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="user-profile-hero" @tap="go('/pages/comprehensive/profile/detail')">
|
<view class="user-profile-hero" @tap="go('/pages/comprehensive/profile/detail')">
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
|
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
|
||||||
import VenueTabBar from '@/components/VenueTabBar.vue'
|
import VenueTabBar from '@/components/VenueTabBar.vue'
|
||||||
|
import StatePanel from '@/components/ice/StatePanel.vue'
|
||||||
|
|
||||||
const session = useComprehensiveSessionStore()
|
const session = useComprehensiveSessionStore()
|
||||||
const avatarInitial = computed(() => (session.member?.nickname || '用户').slice(0, 1))
|
const avatarInitial = computed(() => (session.member?.nickname || '用户').slice(0, 1))
|
||||||
@@ -105,33 +108,6 @@ const go = (url) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.unlogged {
|
|
||||||
min-height: 80vh;
|
|
||||||
padding-top: 180rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
color: $text-color-main;
|
|
||||||
font-size: 42rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
letter-spacing: 2rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
margin-top: $spacing-md;
|
|
||||||
color: $text-color-muted;
|
|
||||||
font-size: 26rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn {
|
|
||||||
width: 520rpx;
|
|
||||||
margin-top: 100rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-hero {
|
.user-profile-hero {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<block v-else>
|
<block v-else>
|
||||||
|
<VenueSwitchHeader venue-name="冰场馆" />
|
||||||
|
|
||||||
<!-- 极简无感大图轮播 -->
|
<!-- 极简无感大图轮播 -->
|
||||||
<view class="banner-wrapper">
|
<view class="banner-wrapper">
|
||||||
<swiper
|
<swiper
|
||||||
@@ -94,6 +96,7 @@ import { onPullDownRefresh } from '@dcloudio/uni-app'
|
|||||||
import { homeApi } from '@/modules/ice/api/home'
|
import { homeApi } from '@/modules/ice/api/home'
|
||||||
import { errorMessage } from '@/modules/ice/api/session'
|
import { errorMessage } from '@/modules/ice/api/session'
|
||||||
import VenueTabBar from '@/components/VenueTabBar.vue'
|
import VenueTabBar from '@/components/VenueTabBar.vue'
|
||||||
|
import VenueSwitchHeader from '@/components/VenueSwitchHeader.vue'
|
||||||
import StatePanel from '@/components/ice/StatePanel.vue'
|
import StatePanel from '@/components/ice/StatePanel.vue'
|
||||||
|
|
||||||
const todayStr = ref('')
|
const todayStr = ref('')
|
||||||
|
|||||||
Reference in New Issue
Block a user