diff --git a/miniprogram/package.json b/miniprogram/package.json
index 5126597..ac23f76 100644
--- a/miniprogram/package.json
+++ b/miniprogram/package.json
@@ -7,7 +7,9 @@
"dev:mp-weixin": "uni -p mp-weixin",
"build:mp-weixin": "uni build -p mp-weixin",
"dev:h5": "uni",
- "build:h5": "uni build"
+ "build:h5": "uni build",
+ "verify:tabbar": "node scripts/verify-venue-tabbar.mjs",
+ "verify:startup": "node scripts/verify-venue-startup.mjs"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-5000120260211001",
diff --git a/miniprogram/scripts/verify-venue-startup.mjs b/miniprogram/scripts/verify-venue-startup.mjs
new file mode 100644
index 0000000..13cce0d
--- /dev/null
+++ b/miniprogram/scripts/verify-venue-startup.mjs
@@ -0,0 +1,35 @@
+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 read = (relativePath) => readFileSync(resolve(srcRoot, relativePath), 'utf8')
+const expectIncludes = (content, expected, label) => {
+ if (!content.includes(expected)) failures.push(`${label} is missing: ${expected}`)
+}
+
+const venueSelect = read('pages/venue-select/index.vue')
+expectIncludes(venueSelect, '', 'venue-select template')
+expectIncludes(venueSelect, "import { onLoad } from '@dcloudio/uni-app'", 'venue-select imports')
+expectIncludes(venueSelect, "import { VENUE_OPTIONS, navigateToVenueHome } from '@/utils/venue'", 'venue-select imports')
+expectIncludes(venueSelect, "const isSwitchMode = options?.mode === 'switch'", 'venue-select onLoad guard')
+expectIncludes(venueSelect, 'if (storedVenue && !isSwitchMode) {', 'venue-select stored venue guard')
+expectIncludes(venueSelect, 'navigateToVenueHome(storedVenue)', 'venue-select stored venue redirect')
+expectIncludes(venueSelect, 'isReady.value = true', 'venue-select ready flag')
+
+const iceProfile = read('pages/ice/profile/index.vue')
+expectIncludes(iceProfile, "navigateTo('/pages/venue-select/index?mode=switch')", 'ice profile switch entry')
+
+const comprehensiveProfile = read('pages/comprehensive/profile/index.vue')
+expectIncludes(comprehensiveProfile, "url: '/pages/venue-select/index?mode=switch'", 'comprehensive profile switch entry')
+
+if (failures.length) {
+ console.error(`Venue startup verification failed with ${failures.length} issue(s):`)
+ for (const failure of failures) console.error(`- ${failure}`)
+ process.exit(1)
+}
+
+console.log('Venue startup verification passed.')
diff --git a/miniprogram/scripts/verify-venue-tabbar.mjs b/miniprogram/scripts/verify-venue-tabbar.mjs
new file mode 100644
index 0000000..4acfdb2
--- /dev/null
+++ b/miniprogram/scripts/verify-venue-tabbar.mjs
@@ -0,0 +1,62 @@
+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 tabPages = [
+ { file: 'pages/ice/home/index.vue', venue: 'ice', active: 'home' },
+ { file: 'pages/ice/courses/index.vue', venue: 'ice', active: 'courses' },
+ { file: 'pages/ice/bookings/index.vue', venue: 'ice', active: 'bookings' },
+ { file: 'pages/ice/profile/index.vue', venue: 'ice', active: 'profile' },
+ { file: 'pages/comprehensive/home/index.vue', venue: 'comprehensive', active: 'home' },
+ { file: 'pages/comprehensive/courses/index.vue', venue: 'comprehensive', active: 'courses' },
+ { file: 'pages/comprehensive/bookings/index.vue', venue: 'comprehensive', active: 'bookings' },
+ { file: 'pages/comprehensive/profile/index.vue', venue: 'comprehensive', active: 'profile' }
+]
+
+const icons = ['home', 'course', 'booking', 'mine']
+const venues = ['ice', 'comprehensive']
+const failures = []
+
+const assertFile = (path) => {
+ if (!existsSync(path)) failures.push(`Missing file: ${path}`)
+}
+
+assertFile(resolve(srcRoot, 'components/VenueTabBar.vue'))
+
+for (const { file, venue, active } of tabPages) {
+ const path = resolve(srcRoot, file)
+ assertFile(path)
+ if (!existsSync(path)) continue
+
+ const content = readFileSync(path, 'utf8')
+ if (!content.includes('`)
+ }
+ if (!content.includes(`venue="${venue}"`)) {
+ failures.push(`${file} does not pass venue="${venue}"`)
+ }
+ if (!content.includes(`active="${active}"`)) {
+ failures.push(`${file} does not pass active="${active}"`)
+ }
+ if (!content.includes("import VenueTabBar from '@/components/VenueTabBar.vue'")) {
+ failures.push(`${file} does not import VenueTabBar`)
+ }
+}
+
+for (const venue of venues) {
+ for (const icon of icons) {
+ assertFile(resolve(srcRoot, `static/${venue}/tab/${icon}.png`))
+ assertFile(resolve(srcRoot, `static/${venue}/tab/${icon}-active.png`))
+ }
+}
+
+if (failures.length) {
+ console.error(`Venue tabbar verification failed with ${failures.length} issue(s):`)
+ for (const failure of failures) console.error(`- ${failure}`)
+ process.exit(1)
+}
+
+console.log('Venue tabbar verification passed.')
diff --git a/miniprogram/src/components/VenueTabBar.vue b/miniprogram/src/components/VenueTabBar.vue
new file mode 100644
index 0000000..5261cbd
--- /dev/null
+++ b/miniprogram/src/components/VenueTabBar.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
diff --git a/miniprogram/src/pages/comprehensive/bookings/index.vue b/miniprogram/src/pages/comprehensive/bookings/index.vue
index 2975396..93ffb2e 100644
--- a/miniprogram/src/pages/comprehensive/bookings/index.vue
+++ b/miniprogram/src/pages/comprehensive/bookings/index.vue
@@ -31,6 +31,7 @@
加载中...
+
@@ -40,6 +41,7 @@ import { bookingApi } from '@/modules/comprehensive/api/booking'
import { ticketApi } from '@/modules/comprehensive/api/ticket'
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
import { formatDateTime, formatTimeRange } from '@/modules/comprehensive/utils/date'
+import VenueTabBar from '@/components/VenueTabBar.vue'
const session = useComprehensiveSessionStore()
const businessType = ref('course')
diff --git a/miniprogram/src/pages/comprehensive/courses/index.vue b/miniprogram/src/pages/comprehensive/courses/index.vue
index f9e8311..0a21645 100644
--- a/miniprogram/src/pages/comprehensive/courses/index.vue
+++ b/miniprogram/src/pages/comprehensive/courses/index.vue
@@ -41,6 +41,7 @@
当前筛选条件下暂无课程
已加载全部
+
@@ -50,6 +51,7 @@ import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { courseApi } from '@/modules/comprehensive/api/course'
import { venueApi } from '@/modules/comprehensive/api/venue'
import { formatTimeRange, todayString } from '@/modules/comprehensive/utils/date'
+import VenueTabBar from '@/components/VenueTabBar.vue'
const selectedDate = ref(todayString())
const selectedCoachIndex = ref(0)
diff --git a/miniprogram/src/pages/comprehensive/home/index.vue b/miniprogram/src/pages/comprehensive/home/index.vue
index 3ddfe91..cee01a6 100644
--- a/miniprogram/src/pages/comprehensive/home/index.vue
+++ b/miniprogram/src/pages/comprehensive/home/index.vue
@@ -62,6 +62,7 @@
暂无公告活动
+
@@ -70,6 +71,7 @@ import { ref, onMounted } from 'vue'
import { onPullDownRefresh } from '@dcloudio/uni-app'
import { contentApi } from '@/modules/comprehensive/api/content'
import { formatDate, formatTimeRange } from '@/modules/comprehensive/utils/date'
+import VenueTabBar from '@/components/VenueTabBar.vue'
const banners = ref([])
const upcomingCourses = ref([])
diff --git a/miniprogram/src/pages/comprehensive/profile/index.vue b/miniprogram/src/pages/comprehensive/profile/index.vue
index 530b941..273d949 100644
--- a/miniprogram/src/pages/comprehensive/profile/index.vue
+++ b/miniprogram/src/pages/comprehensive/profile/index.vue
@@ -66,12 +66,14 @@
+