diff --git a/gym-manage-uniapp/common/constants/routes.js b/gym-manage-uniapp/common/constants/routes.js
new file mode 100644
index 0000000..7320f12
--- /dev/null
+++ b/gym-manage-uniapp/common/constants/routes.js
@@ -0,0 +1,120 @@
+/** 与 pages.json 保持一致 */
+export const PAGE = {
+ INDEX: '/pages/index/index',
+ COURSE: '/pages/course/index',
+ TRAIN: '/pages/train/index',
+ DISCOVER: '/pages/discover/index',
+ MEMBER: '/pages/memberInfo/memberInfo',
+ BOOKING: '/pages/memberInfo/booking',
+ MEMBER_CARD: '/pages/memberInfo/memberCard',
+ USER_INFO: '/pages/memberInfo/userInfo',
+ BODY_TEST_HOME: '/pages/memberInfo/bodyTestHome',
+ BODY_TEST_CONNECT: '/pages/memberInfo/bodyTestConnect',
+ BODY_TEST_MEASURING: '/pages/memberInfo/bodyTestMeasuring',
+ BODY_TEST_REPORT: '/pages/memberInfo/bodyTestReport',
+ BODY_TEST_HISTORY: '/pages/memberInfo/bodyTestHistory',
+ BODY_TEST_COMPARE: '/pages/memberInfo/bodyTestCompare',
+ BODY_TEST_SETTINGS: '/pages/memberInfo/bodyTestSettings',
+ BODY_TEST_TREND: '/pages/memberInfo/bodyTestTrend',
+ COURSE_LIST: '/pages/memberInfo/courseList',
+ COURSE_DETAIL: '/pages/memberInfo/courseDetail',
+ COUPON_DETAIL: '/pages/memberInfo/couponDetail',
+ COUPON_CENTER: '/pages/memberInfo/couponCenter',
+ POINTS_MALL: '/pages/memberInfo/pointsMall',
+ POINTS_HISTORY: '/pages/memberInfo/pointsHistory',
+ ONLINE_COURSE: '/pages/memberInfo/onlineCourseDetail',
+ COURSE_EVALUATE: '/pages/memberInfo/courseEvaluate',
+ TRAIN_SESSION: '/pages/memberInfo/trainSessionDetail',
+ TRAIN_REPORT: '/pages/memberInfo/trainReport',
+ COUPONS: '/pages/memberInfo/coupons',
+ POINTS: '/pages/memberInfo/points',
+ REFERRAL: '/pages/memberInfo/referral',
+ MY_COURSES: '/pages/memberInfo/myCourses',
+ CHECK_IN_HISTORY: '/pages/memberInfo/checkInHistory'
+}
+
+/** 底部 TabBar 页面路径,顺序与 TabBar.vue 一致 */
+export const TAB_ROUTES = [
+ PAGE.INDEX,
+ PAGE.COURSE,
+ PAGE.TRAIN,
+ PAGE.DISCOVER,
+ PAGE.MEMBER
+]
+
+const TAB_PAGES = new Set(TAB_ROUTES)
+
+/** 防止 Tab 连点触发并发路由 */
+let tabNavigating = false
+
+function normalizePath(url) {
+ if (!url) return ''
+ const path = url.split('?')[0]
+ return path.startsWith('/') ? path : `/${path}`
+}
+
+export function getTabIndexByRoute(route) {
+ const path = normalizePath(route)
+ const idx = TAB_ROUTES.indexOf(path)
+ return idx >= 0 ? idx : 0
+}
+
+export function getCurrentRoutePath() {
+ const pages = getCurrentPages()
+ if (!pages.length) return PAGE.INDEX
+ const page = pages[pages.length - 1]
+ const route = page.route || page.$page?.fullPath || ''
+ return normalizePath(route ? `/${route}` : PAGE.INDEX)
+}
+
+export function navigateToPage(url) {
+ const path = normalizePath(url)
+ if (TAB_PAGES.has(path)) {
+ switchToTab(path)
+ return
+ }
+ uni.navigateTo({
+ url,
+ fail: (err) => {
+ console.error('[navigateTo]', url, err)
+ uni.showToast({ title: '页面跳转失败', icon: 'none' })
+ }
+ })
+}
+
+export function switchToTab(url) {
+ const path = normalizePath(url)
+ if (getCurrentRoutePath() === path || tabNavigating) return
+
+ tabNavigating = true
+ const done = () => {
+ setTimeout(() => {
+ tabNavigating = false
+ }, 320)
+ }
+
+ uni.switchTab({
+ url: path,
+ complete: done,
+ fail: (err) => {
+ console.warn('[switchTab]', path, err)
+ uni.showToast({ title: '页面跳转失败', icon: 'none' })
+ }
+ })
+}
+
+export function goToMemberCenter() {
+ switchToTab(PAGE.MEMBER)
+}
+
+export function goBackOrTab(fallbackUrl = PAGE.MEMBER) {
+ uni.navigateBack({
+ delta: 1,
+ fail: () => switchToTab(fallbackUrl)
+ })
+}
+
+/** 子页面返回:统一回到 tab 页「个人中心」 */
+export function backToMemberCenter() {
+ switchToTab(PAGE.MEMBER)
+}
diff --git a/gym-manage-uniapp/common/memberInfo/bodyTestChart.js b/gym-manage-uniapp/common/memberInfo/bodyTestChart.js
new file mode 100644
index 0000000..84c7e10
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/bodyTestChart.js
@@ -0,0 +1,163 @@
+const COLORS = {
+ primary: '#0B2B4B',
+ accent: '#FF6B35',
+ accentLight: 'rgba(255, 107, 53, 0.25)',
+ grid: '#E9EDF2',
+ text: '#5E6F8D',
+ fill: 'rgba(26, 74, 111, 0.35)',
+ line: '#1A4A6F'
+}
+
+function setupCanvas(canvas, width, height, dpr) {
+ canvas.width = width * dpr
+ canvas.height = height * dpr
+ const ctx = canvas.getContext('2d')
+ ctx.scale(dpr, dpr)
+ return ctx
+}
+
+/** 绘制雷达图 */
+export function drawRadarChart(canvas, opts = {}) {
+ if (!canvas) return
+ const {
+ width = 280,
+ height = 240,
+ labels = [],
+ values = [],
+ dpr = 1
+ } = opts
+ const ctx = setupCanvas(canvas, width, height, dpr)
+ ctx.clearRect(0, 0, width, height)
+
+ const cx = width / 2
+ const cy = height / 2 + 8
+ const radius = Math.min(width, height) * 0.32
+ const count = labels.length || 6
+ const angleStep = (Math.PI * 2) / count
+
+ for (let level = 1; level <= 4; level += 1) {
+ ctx.beginPath()
+ const r = (radius * level) / 4
+ for (let i = 0; i <= count; i += 1) {
+ const angle = -Math.PI / 2 + i * angleStep
+ const x = cx + r * Math.cos(angle)
+ const y = cy + r * Math.sin(angle)
+ if (i === 0) ctx.moveTo(x, y)
+ else ctx.lineTo(x, y)
+ }
+ ctx.strokeStyle = COLORS.grid
+ ctx.lineWidth = 1
+ ctx.stroke()
+ }
+
+ for (let i = 0; i < count; i += 1) {
+ const angle = -Math.PI / 2 + i * angleStep
+ ctx.beginPath()
+ ctx.moveTo(cx, cy)
+ ctx.lineTo(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle))
+ ctx.strokeStyle = COLORS.grid
+ ctx.stroke()
+ }
+
+ ctx.beginPath()
+ values.forEach((val, i) => {
+ const ratio = Math.min(1, Math.max(0, val / 100))
+ const angle = -Math.PI / 2 + i * angleStep
+ const x = cx + radius * ratio * Math.cos(angle)
+ const y = cy + radius * ratio * Math.sin(angle)
+ if (i === 0) ctx.moveTo(x, y)
+ else ctx.lineTo(x, y)
+ })
+ ctx.closePath()
+ ctx.fillStyle = COLORS.fill
+ ctx.fill()
+ ctx.strokeStyle = COLORS.accent
+ ctx.lineWidth = 2
+ ctx.stroke()
+
+ ctx.font = '11px sans-serif'
+ ctx.fillStyle = COLORS.text
+ ctx.textAlign = 'center'
+ labels.forEach((label, i) => {
+ const angle = -Math.PI / 2 + i * angleStep
+ const x = cx + (radius + 18) * Math.cos(angle)
+ const y = cy + (radius + 18) * Math.sin(angle) + 4
+ ctx.fillText(label, x, y)
+ })
+}
+
+/** 绘制折线趋势图 */
+export function drawTrendChart(canvas, opts = {}) {
+ if (!canvas) return
+ const {
+ width = 300,
+ height = 160,
+ points = [],
+ dpr = 1,
+ unit = ''
+ } = opts
+ const ctx = setupCanvas(canvas, width, height, dpr)
+ ctx.clearRect(0, 0, width, height)
+
+ if (!points.length) {
+ ctx.fillStyle = COLORS.text
+ ctx.font = '13px sans-serif'
+ ctx.textAlign = 'center'
+ ctx.fillText('暂无趋势数据', width / 2, height / 2)
+ return
+ }
+
+ const pad = { top: 16, right: 12, bottom: 28, left: 12 }
+ const chartW = width - pad.left - pad.right
+ const chartH = height - pad.top - pad.bottom
+ const values = points.map((p) => p.value)
+ const min = Math.min(...values) * 0.95
+ const max = Math.max(...values) * 1.05
+ const range = max - min || 1
+
+ ctx.strokeStyle = COLORS.grid
+ ctx.lineWidth = 1
+ for (let i = 0; i <= 3; i += 1) {
+ const y = pad.top + (chartH * i) / 3
+ ctx.beginPath()
+ ctx.moveTo(pad.left, y)
+ ctx.lineTo(width - pad.right, y)
+ ctx.stroke()
+ }
+
+ const coords = points.map((p, i) => ({
+ x: pad.left + (chartW * i) / Math.max(1, points.length - 1),
+ y: pad.top + chartH - ((p.value - min) / range) * chartH
+ }))
+
+ ctx.beginPath()
+ coords.forEach((pt, i) => {
+ if (i === 0) ctx.moveTo(pt.x, pt.y)
+ else ctx.lineTo(pt.x, pt.y)
+ })
+ ctx.strokeStyle = COLORS.line
+ ctx.lineWidth = 2
+ ctx.stroke()
+
+ coords.forEach((pt, i) => {
+ ctx.beginPath()
+ ctx.arc(pt.x, pt.y, 4, 0, Math.PI * 2)
+ ctx.fillStyle = COLORS.accent
+ ctx.fill()
+ ctx.strokeStyle = '#fff'
+ ctx.lineWidth = 1.5
+ ctx.stroke()
+
+ ctx.fillStyle = COLORS.text
+ ctx.font = '10px sans-serif'
+ ctx.textAlign = 'center'
+ ctx.fillText(points[i].label, pt.x, height - 8)
+ })
+
+ if (unit) {
+ ctx.fillStyle = COLORS.text
+ ctx.font = '10px sans-serif'
+ ctx.textAlign = 'left'
+ ctx.fillText(unit, pad.left, pad.top - 2)
+ }
+}
diff --git a/gym-manage-uniapp/common/memberInfo/bodyTestStore.js b/gym-manage-uniapp/common/memberInfo/bodyTestStore.js
new file mode 100644
index 0000000..4347af5
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/bodyTestStore.js
@@ -0,0 +1,283 @@
+import { bodyTestMock } from './mockData.js'
+
+function clone(value) {
+ return JSON.parse(JSON.stringify(value))
+}
+
+function formatRecordTime(date) {
+ const y = date.getFullYear()
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ const h = String(date.getHours()).padStart(2, '0')
+ const min = String(date.getMinutes()).padStart(2, '0')
+ return `${y}-${m}-${d} ${h}:${min}`
+}
+
+function formatIsoDate(date) {
+ const y = date.getFullYear()
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ return `${y}-${m}-${d}`
+}
+
+function formatTime(date) {
+ const h = String(date.getHours()).padStart(2, '0')
+ const min = String(date.getMinutes()).padStart(2, '0')
+ return `${h}:${min}`
+}
+
+export function getDefaultBodyTestState() {
+ return {
+ settings: { ...bodyTestMock.settings },
+ device: { ...bodyTestMock.device },
+ records: clone(bodyTestMock.records)
+ }
+}
+
+export function mergeBodyTestState(saved) {
+ const defaults = getDefaultBodyTestState()
+ if (!saved) return defaults
+ return {
+ settings: { ...defaults.settings, ...(saved.settings || {}) },
+ device: { ...defaults.device, ...(saved.device || {}) },
+ records: saved.records?.length ? saved.records : defaults.records
+ }
+}
+
+export function getLatestBodyTestRecord(store) {
+ const records = store.bodyTest?.records || []
+ return records.length ? { ...records[0] } : null
+}
+
+export function getBodyTestRecordById(store, id) {
+ const numId = Number(id)
+ const record = (store.bodyTest?.records || []).find((item) => item.id === numId)
+ return record ? { ...record } : null
+}
+
+export function getBodyTestHistory(store, year) {
+ let list = (store.bodyTest?.records || []).map((item) => ({ ...item }))
+ if (year && year !== 'all') {
+ list = list.filter((r) => r.date.startsWith(String(year)))
+ }
+ return list
+}
+
+export function getBodyTestChangeBadge(record, previous) {
+ if (!previous?.metrics || !record?.metrics) return null
+ const diff = Math.round((record.metrics.bodyFat - previous.metrics.bodyFat) * 10) / 10
+ if (diff === 0) return null
+ const sign = diff > 0 ? '+' : ''
+ return { key: 'bodyFat', text: `体脂率${sign}${diff}%`, good: diff < 0 }
+}
+
+export function getBodyTestYears(store) {
+ const years = new Set((store.bodyTest?.records || []).map((r) => r.date.slice(0, 4)))
+ return ['all', ...Array.from(years).sort().reverse()]
+}
+
+export function computeGrade(score) {
+ if (score >= 90) return { grade: 'A', gradeLabel: '优秀' }
+ if (score >= 80) return { grade: 'B+', gradeLabel: '良好' }
+ if (score >= 70) return { grade: 'B', gradeLabel: '中等' }
+ if (score >= 60) return { grade: 'C', gradeLabel: '一般' }
+ return { grade: 'D', gradeLabel: '需改善' }
+}
+
+export function computeScore(metrics) {
+ const bmi = metrics.bmi || 22
+ const bodyFat = metrics.bodyFat || 25
+ const muscle = metrics.muscleMass || 22
+ const bmiScore = bmi >= 18.5 && bmi <= 24 ? 90 : bmi >= 17 && bmi <= 27 ? 75 : 60
+ const fatScore = bodyFat <= 22 ? 92 : bodyFat <= 26 ? 80 : bodyFat <= 30 ? 68 : 55
+ const muscleScore = muscle >= 22 ? 88 : muscle >= 20 ? 76 : 62
+ return Math.round((bmiScore + fatScore + muscleScore) / 3)
+}
+
+export function computeChanges(current, previous) {
+ if (!previous?.metrics) return {}
+ const keys = ['weight', 'bmi', 'bodyFat', 'muscleMass', 'visceralFat', 'bmr', 'bodyWater', 'boneMass']
+ const changes = {}
+ keys.forEach((key) => {
+ const cur = Number(current.metrics[key])
+ const prev = Number(previous.metrics[key])
+ if (Number.isFinite(cur) && Number.isFinite(prev)) {
+ const diff = Math.round((cur - prev) * 10) / 10
+ changes[key] = diff
+ }
+ })
+ return changes
+}
+
+export function formatChangeValue(key, diff, unitSystem = 'metric') {
+ if (diff === undefined || diff === null) return '--'
+ const sign = diff > 0 ? '+' : ''
+ const units = {
+ weight: unitSystem === 'metric' ? 'kg' : 'lb',
+ bodyFat: '%',
+ muscleMass: 'kg',
+ bmi: '',
+ visceralFat: '级',
+ bmr: 'kcal',
+ bodyWater: '%',
+ boneMass: 'kg'
+ }
+ const unit = units[key] || ''
+ return `${sign}${diff}${unit}`
+}
+
+export function buildBodyReportSummary(record, previous) {
+ if (!record) {
+ return {
+ date: '--',
+ weight: '--',
+ bmi: '--',
+ bodyFat: '--',
+ bmr: '--',
+ status: '暂无数据',
+ change: '--'
+ }
+ }
+ const changes = computeChanges(record, previous)
+ const weightChange = changes.weight
+ let changeText = '--'
+ if (weightChange !== undefined) {
+ const sign = weightChange > 0 ? '+' : ''
+ changeText = `${sign}${weightChange}kg`
+ }
+ return {
+ date: record.date,
+ weight: String(record.metrics.weight),
+ bmi: String(record.metrics.bmi),
+ bodyFat: `${record.metrics.bodyFat}%`,
+ bmr: String(record.metrics.bmr),
+ status: record.status,
+ change: changeText,
+ recordId: record.id
+ }
+}
+
+export function getBodyTestTrendData(store, metricKey, limit = 6) {
+ const records = [...(store.bodyTest?.records || [])].reverse().slice(-limit)
+ return records.map((item) => ({
+ id: item.id,
+ date: item.date,
+ label: item.date.slice(5),
+ value: Number(item.metrics[metricKey]) || 0
+ }))
+}
+
+export function getCompareData(store, idA, idB) {
+ const a = getBodyTestRecordById(store, idA)
+ const b = getBodyTestRecordById(store, idB)
+ if (!a || !b) return null
+ const keys = ['weight', 'bmi', 'bodyFat', 'muscleMass', 'visceralFat', 'bmr']
+ const metrics = keys.map((key) => ({
+ key,
+ label: bodyTestMock.metricDefs.find((m) => m.key === key)?.label || key,
+ valueA: a.metrics[key],
+ valueB: b.metrics[key],
+ diff: Math.round((a.metrics[key] - b.metrics[key]) * 10) / 10
+ }))
+ return { recordA: a, recordB: b, metrics }
+}
+
+export function getRecommendedCourses(record) {
+ const ids = record?.recommendedCourseIds || []
+ return bodyTestMock.recommendedCourses.filter((c) => ids.includes(c.id))
+}
+
+export function updateBodyTestSettings(store, patch) {
+ store.bodyTest.settings = { ...store.bodyTest.settings, ...patch }
+ return store
+}
+
+export function connectBodyTestDevice(store) {
+ store.bodyTest.device = {
+ ...store.bodyTest.device,
+ connected: true,
+ battery: Math.min(100, (store.bodyTest.device.battery || 80) + Math.floor(Math.random() * 5)),
+ lastConnected: formatRecordTime(new Date())
+ }
+ return store
+}
+
+export function disconnectBodyTestDevice(store) {
+ store.bodyTest.device = { ...store.bodyTest.device, connected: false }
+ return store
+}
+
+function nextRecordId(records) {
+ return (records || []).reduce((max, item) => Math.max(max, item.id || 0), 0) + 1
+}
+
+/** 模拟一次完整体测并写入记录 */
+export function saveSimulatedBodyTestRecord(store, finalMetrics) {
+ const now = new Date()
+ const previous = getLatestBodyTestRecord(store)
+ const metrics = { ...finalMetrics }
+ const heightCm = Number(store.profile?.height) || 165
+ const heightM = heightCm / 100
+ metrics.bmi = Math.round((metrics.weight / (heightM * heightM)) * 10) / 10
+
+ const score = computeScore(metrics)
+ const { grade, gradeLabel } = computeGrade(score)
+ const status = score >= 80 ? '比较健康' : score >= 70 ? '需关注' : '建议改善'
+
+ const radar = {
+ weight: Math.min(95, Math.round(score * 0.9 + Math.random() * 5)),
+ bodyFat: Math.min(95, Math.round(100 - metrics.bodyFat * 2.5)),
+ muscle: Math.min(95, Math.round(metrics.muscleMass * 3.2)),
+ bone: Math.min(95, Math.round(metrics.boneMass * 32)),
+ water: Math.min(95, Math.round(metrics.bodyWater * 1.4)),
+ bmr: Math.min(95, Math.round(metrics.bmr / 16))
+ }
+
+ const record = {
+ id: nextRecordId(store.bodyTest.records),
+ date: formatIsoDate(now),
+ time: formatTime(now),
+ score,
+ grade,
+ gradeLabel,
+ status,
+ metrics,
+ radar,
+ bodySegments: clone(bodyTestMock.records[0].bodySegments),
+ advice: clone(bodyTestMock.records[0].advice),
+ recommendedCourseIds: [1, 2]
+ }
+
+ if (previous) {
+ record.changes = computeChanges(record, previous)
+ }
+
+ store.bodyTest.records.unshift(record)
+ store.bodyReport = buildBodyReportSummary(record, previous)
+ return record
+}
+
+/** 测量过程实时数据插值 */
+export function interpolateMeasuringMetrics(progress, profile) {
+ const baseWeight = Number(profile?.weight) || 64
+ const target = {
+ weight: baseWeight - 0.3 + Math.random() * 0.2,
+ bodyFat: 24.5 + Math.random() * 0.8,
+ muscleMass: 22.4 + Math.random() * 0.3,
+ visceralFat: 6,
+ bmr: 1380 + Math.floor(Math.random() * 20),
+ bodyWater: 52.5 + Math.random(),
+ boneMass: 2.4,
+ protein: 16.2
+ }
+ const ratio = Math.min(1, progress / 100)
+ return {
+ weight: Math.round((baseWeight + (target.weight - baseWeight) * ratio) * 10) / 10,
+ bodyFat: Math.round((26 + (target.bodyFat - 26) * ratio) * 10) / 10,
+ muscleMass: Math.round((21.5 + (target.muscleMass - 21.5) * ratio) * 10) / 10,
+ bmr: Math.round(1340 + (target.bmr - 1340) * ratio),
+ bodyWater: Math.round((51 + (target.bodyWater - 51) * ratio) * 10) / 10
+ }
+}
+
+export { bodyTestMock }
diff --git a/gym-manage-uniapp/common/memberInfo/bookingStore.js b/gym-manage-uniapp/common/memberInfo/bookingStore.js
new file mode 100644
index 0000000..9b9b41e
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/bookingStore.js
@@ -0,0 +1,127 @@
+import { courseCatalogMock } from './mockData.js'
+
+function clone(value) {
+ return JSON.parse(JSON.stringify(value))
+}
+
+export function getDefaultCourseCatalog() {
+ return clone(courseCatalogMock.courses)
+}
+
+export function mergeCourseCatalog(saved) {
+ const defaults = getDefaultCourseCatalog()
+ if (!saved?.length) return defaults
+ return saved.map((item, i) => ({ ...defaults[i], ...item }))
+}
+
+function parseCourseStart(course) {
+ const str = `${course.date} ${course.startTime}`.replace(/-/g, '/')
+ return new Date(str)
+}
+
+function getPeriod(hour) {
+ if (hour < 12) return 'morning'
+ if (hour < 18) return 'afternoon'
+ return 'evening'
+}
+
+export function filterCourses(courses, filters = {}) {
+ const {
+ date = '',
+ weekDates = [],
+ type = 'all',
+ coach = '全部',
+ period = 'all'
+ } = filters
+
+ return courses.filter((c) => {
+ if (type !== 'all' && c.type !== type) return false
+ if (coach !== '全部' && c.coach !== coach) return false
+ if (period !== 'all' && c.period !== period) return false
+ if (date && c.date !== date) {
+ if (!weekDates.length || !weekDates.includes(c.date)) return false
+ }
+ return true
+ })
+}
+
+export function getCourseById(store, id) {
+ const course = (store.courseCatalog || []).find((c) => c.id === Number(id))
+ return course ? { ...course } : null
+}
+
+export function canCancelBooking(item) {
+ if (!item?.courseDate || !item?.startTime) return !!item?.canCancel
+ const start = new Date(`${item.courseDate} ${item.startTime}`.replace(/-/g, '/'))
+ const diff = start - Date.now()
+ return diff >= 2 * 3600000
+}
+
+export function bookCourse(store, courseId) {
+ const course = store.courseCatalog.find((c) => c.id === Number(courseId))
+ if (!course) return { ok: false, message: '课程不存在' }
+ if (course.enrolled >= course.capacity) return { ok: false, message: '课程已约满' }
+ const exists = store.ongoingBookings.some((b) => b.courseId === course.id)
+ if (exists) return { ok: false, message: '您已预约该课程' }
+
+ course.enrolled += 1
+ const nextId = store.ongoingBookings.reduce((m, b) => Math.max(m, b.id || 0), 0) + 1
+ const parts = course.date.split('-')
+ const booking = {
+ id: nextId,
+ courseId: course.id,
+ title: course.title,
+ banner: course.banner,
+ status: 'booked',
+ statusLabel: '已预约',
+ schedule: `${parts[1]}月${parts[2]}日 ${course.startTime}-${course.endTime}`,
+ dateDay: parts[2],
+ dateMonth: `月${parts[2]}日`,
+ timeRange: `${course.startTime}-${course.endTime}`,
+ courseDate: course.date,
+ startTime: course.startTime,
+ coach: course.coach,
+ coachShort: course.coach.replace('教练', ''),
+ location: course.location,
+ footerText: `可取消(需提前2小时,截止 ${parts[1]}/${parts[2]} ${course.startTime} 前2小时)`,
+ canCancel: true,
+ type: course.type
+ }
+ store.ongoingBookings.unshift(booking)
+ return { ok: true, message: '预约成功', booking }
+}
+
+export function getWeekDates(baseDateStr) {
+ const base = baseDateStr ? new Date(baseDateStr.replace(/-/g, '/')) : new Date()
+ const day = base.getDay() || 7
+ const monday = new Date(base)
+ monday.setDate(base.getDate() - day + 1)
+ const dates = []
+ for (let i = 0; i < 7; i += 1) {
+ const d = new Date(monday)
+ d.setDate(monday.getDate() + i)
+ dates.push(formatIso(d))
+ }
+ return dates
+}
+
+function formatIso(d) {
+ const y = d.getFullYear()
+ const m = String(d.getMonth() + 1).padStart(2, '0')
+ const day = String(d.getDate()).padStart(2, '0')
+ return `${y}-${m}-${day}`
+}
+
+export function enrichCourseForDisplay(course) {
+ const remaining = course.capacity - course.enrolled
+ const percent = Math.round((course.enrolled / course.capacity) * 100)
+ return {
+ ...course,
+ remaining,
+ percent,
+ full: remaining <= 0,
+ scarcityLabel: remaining > 0 && remaining <= 5 ? `仅剩${remaining}席` : ''
+ }
+}
+
+export { courseCatalogMock }
diff --git a/gym-manage-uniapp/common/memberInfo/format.js b/gym-manage-uniapp/common/memberInfo/format.js
new file mode 100644
index 0000000..67d0369
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/format.js
@@ -0,0 +1,37 @@
+/** 手机号展示脱敏(中间四位 ****) */
+
+export function maskPhone(phone) {
+ if (phone == null || phone === '') return ''
+
+ const str = String(phone).trim()
+ if (str.includes('****')) return str
+
+ const digits = str.replace(/\D/g, '')
+ if (digits.length === 11) {
+ return `${digits.slice(0, 3)}****${digits.slice(7)}`
+ }
+ if (digits.length > 4) {
+ const hideLen = Math.min(4, digits.length - 3)
+ const start = Math.floor((digits.length - hideLen) / 2)
+ return `${digits.slice(0, start)}${'*'.repeat(hideLen)}${digits.slice(start + hideLen)}`
+ }
+
+ return str
+}
+
+/** 个人中心头部:138****6789 已绑定微信 */
+export function formatMemberCenterPhone(phone) {
+ const masked = maskPhone(phone)
+ return masked ? `${masked} 已绑定微信` : ''
+}
+
+/** 保存前规范化:尽量存 11 位数字;已是脱敏串则原样保留 */
+export function normalizePhoneForStore(phone) {
+ const str = String(phone || '').trim()
+ if (!str) return ''
+ if (str.includes('****')) return str
+
+ const digits = str.replace(/\D/g, '')
+ if (digits.length >= 11) return digits.slice(0, 11)
+ return digits || str
+}
diff --git a/gym-manage-uniapp/common/memberInfo/index.js b/gym-manage-uniapp/common/memberInfo/index.js
new file mode 100644
index 0000000..580a06a
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/index.js
@@ -0,0 +1,79 @@
+export { memberCenterMock, userInfoMock, fitnessGoalOptions, bookingMock, memberCardMock, bodyTestMock, moduleMock, courseCatalogMock } from './mockData.js'
+export { statusBarTimeMixin, subPageMixin } from './mixins.js'
+export {
+ loadMemberStore,
+ saveMemberStore,
+ persistMemberStore,
+ syncStats,
+ computeRemainingDays,
+ buildCardTip,
+ formatUpcomingAlert,
+ getBookingPreview,
+ getCenterPageData,
+ cancelOngoingBooking,
+ renewMemberCard,
+ parseLocalDate,
+ saveUserProfile
+} from './store.js'
+export {
+ getLatestBodyTestRecord,
+ getBodyTestRecordById,
+ getBodyTestHistory,
+ computeChanges,
+ formatChangeValue,
+ buildBodyReportSummary,
+ getBodyTestTrendData,
+ getCompareData,
+ getRecommendedCourses,
+ getBodyTestChangeBadge,
+ getBodyTestYears,
+ updateBodyTestSettings,
+ connectBodyTestDevice,
+ disconnectBodyTestDevice,
+ saveSimulatedBodyTestRecord,
+ interpolateMeasuringMetrics,
+ bodyTestMock
+} from './bodyTestStore.js'
+export {
+ getTrainingReportData,
+ getTrainingSessionById,
+ filterTrainingSessions,
+ getCouponsByStatus,
+ getCouponById,
+ useCoupon,
+ deleteExpiredCoupon,
+ getCouponCenterList,
+ claimCouponFromCenter,
+ getPointsPageData,
+ redeemPointsReward,
+ filterPointsHistory,
+ getReferralPageData,
+ getMyCoursesData,
+ getMyCoursesByTab,
+ getOnlineCourseById,
+ updateOnlineProgress,
+ getCheckInHistory,
+ moduleMock
+} from './moduleStore.js'
+export {
+ filterCourses,
+ getCourseById,
+ bookCourse,
+ canCancelBooking,
+ enrichCourseForDisplay,
+ getWeekDates,
+ courseCatalogMock
+} from './bookingStore.js'
+export { previewImage, persistChosenImage, isLocalFilePath } from './media.js'
+export { maskPhone, formatMemberCenterPhone, normalizePhoneForStore } from './format.js'
+export {
+ validateName,
+ validatePhone,
+ validatePhoneForRebind,
+ validateHeight,
+ validateWeight,
+ validateBirthday,
+ validateFitnessGoals,
+ validateUserProfile,
+ showValidationError
+} from './validate.js'
diff --git a/gym-manage-uniapp/common/memberInfo/media.js b/gym-manage-uniapp/common/memberInfo/media.js
new file mode 100644
index 0000000..fca1f1c
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/media.js
@@ -0,0 +1,159 @@
+/** 头像等媒体:真机选图后须 saveFile,/static/ 须 getImageInfo */
+
+function buildStaticPathCandidates(url) {
+ const list = [url]
+ if (url.startsWith('/')) {
+ list.push(url.slice(1))
+ } else {
+ list.push(`/${url}`)
+ }
+ return [...new Set(list.filter(Boolean))]
+}
+
+function isPackageStaticPath(url) {
+ return /^(\/)?static\//i.test(url)
+}
+
+/** chooseImage / saveFile 产生的本地路径(含真机 temp、usr、store) */
+export function isLocalFilePath(url) {
+ if (!url) return false
+ if (/^(wxfile:|file:|blob:|data:)/i.test(url)) return true
+ if (/^https?:\/\/(tmp|usr|store)\//i.test(url)) return true
+ if (/^https?:\/\//i.test(url) && !isPackageStaticPath(url)) return true
+ return false
+}
+
+function showPreviewFail() {
+ uni.showToast({ title: '无法预览头像', icon: 'none' })
+}
+
+function openPreview(path, onFail) {
+ if (!path) {
+ ;(onFail || showPreviewFail)()
+ return
+ }
+ uni.previewImage({
+ urls: [path],
+ current: path,
+ fail: () => (onFail ? onFail() : showPreviewFail())
+ })
+}
+
+function previewLocalFile(url) {
+ openPreview(url, () => {
+ uni.getImageInfo({
+ src: url,
+ success: (res) => {
+ openPreview(res.path || url, showPreviewFail)
+ },
+ fail: showPreviewFail
+ })
+ })
+}
+
+function tryGetImageInfo(candidates, index, onSuccess, onFail) {
+ if (index >= candidates.length) {
+ onFail()
+ return
+ }
+ uni.getImageInfo({
+ src: candidates[index],
+ success: (res) => onSuccess(res.path || candidates[index]),
+ fail: () => tryGetImageInfo(candidates, index + 1, onSuccess, onFail)
+ })
+}
+
+function getMpUserDataPath() {
+ // #ifdef MP-WEIXIN
+ return wx.env.USER_DATA_PATH
+ // #endif
+ return ''
+}
+
+function tryCopyFile(candidates, index, onSuccess, onFail) {
+ // #ifdef MP-WEIXIN
+ const userPath = getMpUserDataPath()
+ if (!userPath) {
+ onFail()
+ return
+ }
+ const fs = uni.getFileSystemManager()
+ const extMatch = candidates[0]?.match(/\.(\w+)(?:\?|$)/)
+ const ext = extMatch ? extMatch[1] : 'png'
+ const dest = `${userPath}/avatar_preview_${Date.now()}.${ext}`
+
+ if (index >= candidates.length) {
+ onFail()
+ return
+ }
+
+ fs.copyFile({
+ srcPath: candidates[index],
+ destPath: dest,
+ success: () => onSuccess(dest),
+ fail: () => tryCopyFile(candidates, index + 1, onSuccess, onFail)
+ })
+ // #endif
+ // #ifndef MP-WEIXIN
+ onFail()
+ // #endif
+}
+
+function previewPackageStatic(url) {
+ const candidates = buildStaticPathCandidates(url)
+ tryGetImageInfo(
+ candidates,
+ 0,
+ (path) => openPreview(path, showPreviewFail),
+ () => {
+ tryCopyFile(
+ candidates,
+ 0,
+ (path) => openPreview(path, showPreviewFail),
+ showPreviewFail
+ )
+ }
+ )
+}
+
+/** 选图后将临时文件转为真机可预览、可持久化的本地路径 */
+export function persistChosenImage(tempPath) {
+ return new Promise((resolve) => {
+ const path = String(tempPath || '').trim()
+ if (!path) {
+ resolve('')
+ return
+ }
+
+ // #ifdef MP-WEIXIN
+ uni.saveFile({
+ tempFilePath: path,
+ success: (res) => resolve(res.savedFilePath || path),
+ fail: () => resolve(path)
+ })
+ // #endif
+ // #ifndef MP-WEIXIN
+ resolve(path)
+ // #endif
+ })
+}
+
+export function previewImage(src, fallback = '') {
+ const url = String(src || fallback || '').trim()
+ if (!url) {
+ uni.showToast({ title: '暂无头像', icon: 'none' })
+ return
+ }
+
+ if (isLocalFilePath(url)) {
+ previewLocalFile(url)
+ return
+ }
+
+ if (isPackageStaticPath(url)) {
+ previewPackageStatic(url)
+ return
+ }
+
+ previewLocalFile(url)
+}
diff --git a/gym-manage-uniapp/common/memberInfo/mixins.js b/gym-manage-uniapp/common/memberInfo/mixins.js
new file mode 100644
index 0000000..baf110d
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/mixins.js
@@ -0,0 +1,28 @@
+import { backToMemberCenter } from '../constants/routes.js'
+
+/** 状态栏时间(Pixso 顶栏占位) */
+export const statusBarTimeMixin = {
+ data() {
+ return {
+ statusBarTime: '9:41'
+ }
+ },
+ onLoad() {
+ this.updateStatusBarTime()
+ },
+ methods: {
+ updateStatusBarTime() {
+ const now = new Date()
+ this.statusBarTime = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
+ }
+ }
+}
+
+/** 子页面返回个人中心 tab */
+export const subPageMixin = {
+ methods: {
+ goBack() {
+ backToMemberCenter()
+ }
+ }
+}
diff --git a/gym-manage-uniapp/common/memberInfo/mockData.js b/gym-manage-uniapp/common/memberInfo/mockData.js
new file mode 100644
index 0000000..573deaa
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/mockData.js
@@ -0,0 +1,884 @@
+/** 个人中心模块 mock 数据(后续可替换为 API) */
+
+export const memberCenterMock = {
+ userInfo: {
+ name: '张小芳',
+ phone: '13812345678 已绑定微信',
+ memberLevel: '黄金会员',
+ avatar: '/static/images/AvatarEditWrap.png'
+ },
+ stats: {
+ checkInCount: 128,
+ trainingHours: 23,
+ pointsBalance: 1250
+ },
+ cardInfo: {
+ name: '健身时长卡',
+ detailTag: '详情',
+ expireDate: '有效期至 2025年12月31日',
+ remainingDays: 187,
+ tip: '距离下次到期还有187天,请及时续费'
+ },
+ checkIns: [
+ {
+ id: 1,
+ title: '今日签到 · 瑜伽初级班',
+ time: '2024-07-12 09:05',
+ tag: '团课',
+ tagTheme: 'group'
+ },
+ {
+ id: 2,
+ title: '自由训练 · 进馆记录',
+ time: '2024-07-11 18:30',
+ tag: '自由',
+ tagTheme: 'free'
+ },
+ {
+ id: 3,
+ title: '私教课 · 力量训练',
+ time: '2024-07-10 14:00',
+ tag: '私教',
+ tagTheme: 'private'
+ }
+ ],
+ bodyReport: {
+ date: '2024-07-01',
+ weight: '63.5',
+ bmi: '22.1',
+ bodyFat: '24.8%',
+ bmr: '165',
+ status: '比较健康',
+ change: '-1.2kg'
+ },
+ couponPoints: {
+ amount: '¥50',
+ couponDesc: '满500可用 · 1张',
+ couponAction: '去使用',
+ points: 1250,
+ pointsLabel: '我的积分',
+ pointsAction: '去兑换'
+ },
+ referral: {
+ code: 'FIT-ZXF-2024',
+ invited: 5,
+ registered: 3,
+ purchased: 2
+ }
+}
+
+export const userInfoMock = {
+ name: '张小芳',
+ phone: '13812345678',
+ gender: 'female',
+ birthday: '1995年06月15日',
+ height: '165',
+ weight: '63.5',
+ fitnessGoals: ['减脂', '塑形'],
+ avatar: '/static/images/AvatarEditWrap.png'
+}
+
+export const fitnessGoalOptions = ['减脂', '塑形', '增肌', '提升耐力', '改善体态']
+
+export const memberCardMock = {
+ card: {
+ name: '黄金健身时长卡',
+ status: '生效中',
+ validityStart: '2024年01月01日',
+ validity: '2024年01月01日 - 2025年12月31日',
+ validityEnd: '2025-12-31',
+ remainingDays: 187
+ },
+ recordTabs: [
+ { key: 'all', label: '全部' },
+ { key: 'consume', label: '消费' },
+ { key: 'checkin', label: '签到' }
+ ],
+ records: [
+ {
+ id: 1,
+ type: 'checkin',
+ title: '瑜伽初级班 · 团课签到',
+ time: '2024-07-12 09:05',
+ value: '-1次',
+ valueType: 'negative',
+ icon: '/static/images/dumbbell.png',
+ iconTheme: 'orange'
+ },
+ {
+ id: 2,
+ type: 'checkin',
+ title: '自由进馆',
+ time: '2024-07-11 18:30',
+ value: '-1天',
+ valueType: 'negative',
+ icon: '/static/images/mappin.png',
+ iconTheme: 'green'
+ },
+ {
+ id: 3,
+ type: 'consume',
+ title: '会员卡充值',
+ time: '2024-07-01 10:00',
+ value: '+90天',
+ valueType: 'positive',
+ icon: '/static/images/pluscircle.png',
+ iconTheme: 'orange'
+ }
+ ],
+ rules: [
+ '时长卡有效期内不限入场次数,但需提前预约团课',
+ '卡到期后不退余额,请合理安排使用',
+ '一卡仅限本人使用,不可转让'
+ ]
+}
+
+/** 智能体测模块 mock 数据 */
+export const bodyTestMock = {
+ settings: {
+ autoSync: true,
+ bluetoothEnabled: true,
+ notifyOnComplete: true,
+ shareAnonymous: false,
+ unitSystem: 'metric'
+ },
+ device: {
+ connected: false,
+ name: 'InBody 270',
+ model: 'IB-270',
+ battery: 86,
+ signal: 'strong',
+ lastConnected: '2024-07-10 18:20'
+ },
+ connectSteps: [
+ { step: 1, title: '开启体测仪', desc: '长按电源键 3 秒,等待蓝牙指示灯闪烁' },
+ { step: 2, title: '靠近设备', desc: '将手机靠近体测仪 1 米范围内' },
+ { step: 3, title: '确认连接', desc: '点击下方按钮搜索并配对设备' }
+ ],
+ metricDefs: [
+ { key: 'weight', label: '体重', unit: 'kg', icon: '/static/images/target.png' },
+ { key: 'bmi', label: 'BMI', unit: '', icon: '/static/images/activity.png' },
+ { key: 'bodyFat', label: '体脂率', unit: '%', icon: '/static/images/trendingdown.png' },
+ { key: 'muscleMass', label: '肌肉量', unit: 'kg', icon: '/static/images/dumbbell.png' },
+ { key: 'visceralFat', label: '内脏脂肪', unit: '级', icon: '/static/images/alertcircle.png' },
+ { key: 'bmr', label: '基础代谢', unit: 'kcal', icon: '/static/images/clock.png' },
+ { key: 'bodyWater', label: '体水分', unit: '%', icon: '/static/images/shield.png' },
+ { key: 'boneMass', label: '骨量', unit: 'kg', icon: '/static/images/user.png' }
+ ],
+ radarLabels: [
+ { key: 'weight', label: '体重控制' },
+ { key: 'bodyFat', label: '体脂肪' },
+ { key: 'muscle', label: '肌肉量' },
+ { key: 'bone', label: '骨量' },
+ { key: 'water', label: '体水分' },
+ { key: 'bmr', label: '基础代谢' }
+ ],
+ trendMetrics: [
+ { key: 'weight', label: '体重' },
+ { key: 'bodyFat', label: '体脂率' },
+ { key: 'muscleMass', label: '肌肉量' },
+ { key: 'bmi', label: 'BMI' }
+ ],
+ recommendedCourses: [
+ {
+ id: 1,
+ title: '燃脂 HIIT 团课',
+ coach: '李明教练',
+ schedule: '每周二、四 19:00',
+ banner: '/static/images/AC1Banner.png',
+ tag: '减脂推荐'
+ },
+ {
+ id: 2,
+ title: '核心力量塑形',
+ coach: '王强教练',
+ schedule: '每周一、三 18:30',
+ banner: '/static/images/AC2Banner.png',
+ tag: '塑形推荐'
+ }
+ ],
+ records: [
+ {
+ id: 4,
+ date: '2024-07-12',
+ time: '09:05',
+ score: 85,
+ grade: 'B+',
+ gradeLabel: '良好',
+ status: '比较健康',
+ bodyAge: 27,
+ realAge: 29,
+ metrics: {
+ weight: 63.5,
+ bmi: 22.1,
+ bodyFat: 24.8,
+ muscleMass: 22.6,
+ visceralFat: 6,
+ bmr: 1385,
+ bodyWater: 52.8,
+ boneMass: 2.42,
+ protein: 16.4
+ },
+ radar: { weight: 78, bodyFat: 72, muscle: 74, bone: 81, water: 79, bmr: 73 },
+ bodySegments: [
+ { part: '左臂', level: 'normal', value: '2.1kg' },
+ { part: '右臂', level: 'normal', value: '2.2kg' },
+ { part: '躯干', level: 'high', value: '28.5kg' },
+ { part: '左腿', level: 'normal', value: '8.6kg' },
+ { part: '右腿', level: 'normal', value: '8.7kg' }
+ ],
+ advice: [
+ '体脂率略高,建议增加有氧训练频率至每周 3-4 次',
+ '核心肌群表现良好,可尝试进阶力量课程',
+ '保持当前蛋白质摄入,有助于维持肌肉量'
+ ],
+ recommendedCourseIds: [1, 2]
+ },
+ {
+ id: 3,
+ date: '2024-06-28',
+ time: '18:40',
+ score: 82,
+ grade: 'B+',
+ gradeLabel: '良好',
+ status: '比较健康',
+ bodyAge: 28,
+ realAge: 29,
+ metrics: {
+ weight: 64.7,
+ bmi: 22.5,
+ bodyFat: 25.3,
+ muscleMass: 22.2,
+ visceralFat: 7,
+ bmr: 1370,
+ bodyWater: 52.1,
+ boneMass: 2.4,
+ protein: 16.1
+ },
+ radar: { weight: 74, bodyFat: 68, muscle: 70, bone: 80, water: 76, bmr: 70 },
+ bodySegments: [
+ { part: '左臂', level: 'normal', value: '2.0kg' },
+ { part: '右臂', level: 'normal', value: '2.1kg' },
+ { part: '躯干', level: 'high', value: '28.2kg' },
+ { part: '左腿', level: 'normal', value: '8.5kg' },
+ { part: '右腿', level: 'normal', value: '8.6kg' }
+ ],
+ advice: [
+ '体重较上次下降 0.8kg,减脂方向正确',
+ '建议配合拉伸课程改善体态'
+ ],
+ recommendedCourseIds: [1]
+ },
+ {
+ id: 2,
+ date: '2024-06-10',
+ time: '10:15',
+ score: 79,
+ grade: 'B',
+ gradeLabel: '中等',
+ status: '需关注',
+ bodyAge: 30,
+ realAge: 29,
+ metrics: {
+ weight: 65.5,
+ bmi: 22.8,
+ bodyFat: 26.1,
+ muscleMass: 21.8,
+ visceralFat: 8,
+ bmr: 1355,
+ bodyWater: 51.5,
+ boneMass: 2.38,
+ protein: 15.8
+ },
+ radar: { weight: 70, bodyFat: 62, muscle: 66, bone: 78, water: 72, bmr: 66 },
+ bodySegments: [
+ { part: '左臂', level: 'low', value: '1.9kg' },
+ { part: '右臂', level: 'normal', value: '2.0kg' },
+ { part: '躯干', level: 'high', value: '28.0kg' },
+ { part: '左腿', level: 'normal', value: '8.4kg' },
+ { part: '右腿', level: 'normal', value: '8.5kg' }
+ ],
+ advice: [
+ '内脏脂肪偏高,建议减少高糖饮食',
+ '增加抗阻训练提升肌肉量'
+ ],
+ recommendedCourseIds: [2]
+ },
+ {
+ id: 1,
+ date: '2024-05-20',
+ time: '14:30',
+ score: 76,
+ grade: 'B',
+ gradeLabel: '中等',
+ status: '需关注',
+ bodyAge: 31,
+ realAge: 29,
+ metrics: {
+ weight: 66.2,
+ bmi: 23.1,
+ bodyFat: 26.8,
+ muscleMass: 21.5,
+ visceralFat: 9,
+ bmr: 1340,
+ bodyWater: 51.0,
+ boneMass: 2.35,
+ protein: 15.5
+ },
+ radar: { weight: 66, bodyFat: 58, muscle: 62, bone: 76, water: 68, bmr: 62 },
+ bodySegments: [
+ { part: '左臂', level: 'low', value: '1.8kg' },
+ { part: '右臂', level: 'low', value: '1.9kg' },
+ { part: '躯干', level: 'high', value: '27.8kg' },
+ { part: '左腿', level: 'normal', value: '8.3kg' },
+ { part: '右腿', level: 'normal', value: '8.4kg' }
+ ],
+ advice: [
+ '建议制定 8 周减脂计划并定期复测',
+ '每日饮水量建议达到 2000ml'
+ ],
+ recommendedCourseIds: [1, 2]
+ }
+ ]
+}
+
+export const bookingMock = {
+ upcomingAlert: '明天 09:00 有一堂瑜伽课,请提前 30 分钟到场',
+ tabs: [
+ { key: 'ongoing', label: '进行中' },
+ { key: 'history', label: '历史预约' }
+ ],
+ ongoing: [
+ {
+ id: 1,
+ title: '瑜伽基础班',
+ banner: '/static/images/AC1Banner.png',
+ status: 'booked',
+ statusLabel: '已预约',
+ schedule: '07月15日 09:00-10:00',
+ dateDay: '07',
+ dateMonth: '月15日',
+ timeRange: '09:00-10:00',
+ coach: '李明教练',
+ coachShort: '李明',
+ location: '一楼 大厅',
+ footerText: '可取消(截止 07/15 07:00)',
+ canCancel: true
+ },
+ {
+ id: 2,
+ title: '私教健身课',
+ banner: '/static/images/AC2Banner.png',
+ status: 'pending',
+ statusLabel: '待上课',
+ schedule: '07月18日 14:00-15:00',
+ dateDay: '07',
+ dateMonth: '月18日',
+ timeRange: '14:00-15:00',
+ coach: '王强教练',
+ coachShort: '王强',
+ location: 'B区私教室',
+ footerText: '地点:B区私教室',
+ canCancel: true
+ }
+ ],
+ history: [
+ {
+ id: 3,
+ title: '动感单车',
+ banner: '/static/images/AC1Banner.png',
+ status: 'completed',
+ statusLabel: '已完成',
+ schedule: '07月10日 19:00-20:00',
+ coach: '赵敏教练',
+ footerText: '已签到',
+ canCancel: false
+ },
+ {
+ id: 4,
+ title: '普拉提进阶',
+ banner: '/static/images/AC2Banner.png',
+ status: 'cancelled',
+ statusLabel: '已取消',
+ schedule: '07月05日 10:00-11:00',
+ coach: '李明教练',
+ footerText: '用户主动取消',
+ canCancel: false
+ }
+ ]
+}
+
+/** 可预约课程 catalog */
+export const courseCatalogMock = {
+ coaches: ['全部', '李明教练', '王强教练', '赵敏教练'],
+ periodOptions: [
+ { key: 'all', label: '全部时段' },
+ { key: 'morning', label: '上午' },
+ { key: 'afternoon', label: '下午' },
+ { key: 'evening', label: '晚上' }
+ ],
+ typeOptions: [
+ { key: 'all', label: '全部' },
+ { key: 'group', label: '团课' },
+ { key: 'private', label: '私教' }
+ ],
+ courses: [
+ {
+ id: 101,
+ title: '瑜伽基础班',
+ type: 'group',
+ coach: '李明教练',
+ coachAvatar: '/static/images/user0.png',
+ date: '2024-07-15',
+ startTime: '09:00',
+ endTime: '10:00',
+ location: '一楼大厅',
+ enrolled: 12,
+ capacity: 20,
+ price: '次卡扣 1 次',
+ payType: 'session',
+ period: 'morning',
+ banner: '/static/images/AC1Banner.png',
+ intro: '适合零基础学员,重点提升柔韧性与呼吸控制。',
+ suitable: '久坐办公族、初学者、想改善体态者',
+ coachBio: '国家一级瑜伽指导员,5年教学经验',
+ coachRating: 4.9,
+ reviews: [
+ { user: '会员 A', score: 5, text: '教练讲解很细致,氛围很好' },
+ { user: '会员 B', score: 5, text: '适合新手,推荐' }
+ ],
+ cancelRule: '至少提前 2 小时取消,否则视为爽约'
+ },
+ {
+ id: 102,
+ title: 'HIIT 燃脂团课',
+ type: 'group',
+ coach: '赵敏教练',
+ coachAvatar: '/static/images/user1.png',
+ date: '2024-07-15',
+ startTime: '19:00',
+ endTime: '20:00',
+ location: '有氧区',
+ enrolled: 18,
+ capacity: 20,
+ price: '时长卡',
+ payType: 'duration',
+ period: 'evening',
+ banner: '/static/images/AC1Banner.png',
+ intro: '高强度间歇训练,快速燃脂提升心肺。',
+ suitable: '有一定运动基础、目标减脂者',
+ coachBio: 'ACE 认证教练,擅长 HIIT 与动感单车',
+ coachRating: 4.8,
+ reviews: [{ user: '会员 C', score: 5, text: '强度够,出汗很多' }],
+ cancelRule: '至少提前 2 小时取消'
+ },
+ {
+ id: 103,
+ title: '私教 · 力量训练',
+ type: 'private',
+ coach: '王强教练',
+ coachAvatar: '/static/images/user2.png',
+ date: '2024-07-16',
+ startTime: '14:00',
+ endTime: '15:00',
+ location: 'B区私教室',
+ enrolled: 1,
+ capacity: 1,
+ price: '私教课时卡',
+ payType: 'private',
+ period: 'afternoon',
+ banner: '/static/images/AC2Banner.png',
+ intro: '一对一力量训练,定制训练计划。',
+ suitable: '增肌塑形、康复训练',
+ coachBio: 'NSCA 认证私教,8年从业经验',
+ coachRating: 5.0,
+ reviews: [{ user: '会员 D', score: 5, text: '非常专业' }],
+ cancelRule: '至少提前 2 小时取消'
+ },
+ {
+ id: 104,
+ title: '普拉提进阶',
+ type: 'group',
+ coach: '李明教练',
+ coachAvatar: '/static/images/user0.png',
+ date: '2024-07-17',
+ startTime: '10:30',
+ endTime: '11:30',
+ location: '二楼瑜伽室',
+ enrolled: 8,
+ capacity: 15,
+ price: '次卡扣 1 次',
+ payType: 'session',
+ period: 'morning',
+ banner: '/static/images/AC2Banner.png',
+ intro: '核心稳定与体态矫正进阶课程。',
+ suitable: '有普拉提基础者',
+ coachBio: '国家一级瑜伽指导员',
+ coachRating: 4.9,
+ reviews: [],
+ cancelRule: '至少提前 2 小时取消'
+ },
+ {
+ id: 105,
+ title: '动感单车',
+ type: 'group',
+ coach: '赵敏教练',
+ coachAvatar: '/static/images/user1.png',
+ date: '2024-07-18',
+ startTime: '18:30',
+ endTime: '19:30',
+ location: '单车房',
+ enrolled: 20,
+ capacity: 20,
+ price: '储值卡 ¥39',
+ payType: 'stored',
+ period: 'evening',
+ banner: '/static/images/AC1Banner.png',
+ intro: '音乐骑行,团队氛围燃脂。',
+ suitable: '所有级别,可调节阻力',
+ coachBio: 'ACE 认证教练',
+ coachRating: 4.7,
+ reviews: [{ user: '会员 E', score: 4, text: '音乐很带感' }],
+ cancelRule: '至少提前 2 小时取消'
+ }
+ ]
+}
+
+/** 个人中心其它模块 mock 数据 */
+export const moduleMock = {
+ trainingReport: {
+ periodLabel: '本周训练',
+ summary: {
+ sessions: 4,
+ hours: 6.5,
+ calories: 2180,
+ streak: 3,
+ visits: 5
+ },
+ monthlyHours: [
+ { label: '第1周', value: 4.2 },
+ { label: '第2周', value: 5.8 },
+ { label: '第3周', value: 6.5 },
+ { label: '第4周', value: 5.0 }
+ ],
+ monthlyCalories: [
+ { label: '第1周', value: 1200 },
+ { label: '第2周', value: 1680 },
+ { label: '第3周', value: 2180 },
+ { label: '第4周', value: 1850 }
+ ],
+ weeklyHours: [
+ { label: '一', value: 1.2 },
+ { label: '二', value: 0 },
+ { label: '三', value: 1.5 },
+ { label: '四', value: 0.8 },
+ { label: '五', value: 1.0 },
+ { label: '六', value: 2.0 },
+ { label: '日', value: 0 }
+ ],
+ sessions: [
+ {
+ id: 1,
+ title: '瑜伽基础班',
+ coach: '李明教练',
+ date: '2024-07-12',
+ time: '09:00-10:00',
+ duration: '60分钟',
+ calories: 320,
+ type: 'group',
+ typeLabel: '团课'
+ },
+ {
+ id: 2,
+ title: '自由训练 · 力量',
+ coach: '自主训练',
+ date: '2024-07-11',
+ time: '18:30-19:45',
+ duration: '75分钟',
+ calories: 480,
+ type: 'free',
+ typeLabel: '自由'
+ },
+ {
+ id: 3,
+ title: '私教 · 核心塑形',
+ coach: '王强教练',
+ date: '2024-07-10',
+ time: '14:00-15:00',
+ duration: '60分钟',
+ calories: 410,
+ type: 'private',
+ typeLabel: '私教'
+ },
+ {
+ id: 4,
+ title: '动感单车',
+ coach: '赵敏教练',
+ date: '2024-07-08',
+ time: '19:00-20:00',
+ duration: '60分钟',
+ calories: 520,
+ type: 'group',
+ typeLabel: '团课'
+ }
+ ]
+ },
+ couponTabs: [
+ { key: 'available', label: '可用' },
+ { key: 'used', label: '已使用' },
+ { key: 'expired', label: '已过期' }
+ ],
+ coupons: [
+ {
+ id: 1,
+ status: 'available',
+ amount: 50,
+ title: '满500减50',
+ desc: '全场团课/私教可用',
+ expire: '2024-12-31',
+ minSpend: 500,
+ tag: '通用券',
+ rules: '1. 满500元可用\n2. 适用于团课/私教\n3. 不可与其他优惠叠加\n4. 有效期至2024-12-31',
+ scope: '全门店 · 团课/私教',
+ flow: '选择课程 → 确认订单 → 选择优惠券 → 完成支付'
+ },
+ {
+ id: 2,
+ status: 'available',
+ amount: 30,
+ title: '新人专享',
+ desc: '首次购课立减',
+ expire: '2024-08-31',
+ minSpend: 200,
+ tag: '新人券',
+ rules: '1. 限新注册用户首次购课\n2. 满200可用',
+ scope: '全门店 · 首次购课',
+ flow: '首次预约课程时自动提示使用'
+ },
+ {
+ id: 3,
+ status: 'used',
+ amount: 20,
+ title: '签到奖励券',
+ desc: '连续签到7天获得',
+ expire: '2024-07-01',
+ minSpend: 100,
+ tag: '奖励券',
+ usedAt: '2024-06-28',
+ rules: '满100可用',
+ scope: '团课',
+ flow: '预约时使用'
+ },
+ {
+ id: 4,
+ status: 'expired',
+ amount: 100,
+ title: '周年庆特惠',
+ desc: '满1000可用',
+ expire: '2024-06-01',
+ minSpend: 1000,
+ tag: '活动券',
+ rules: '满1000可用,已过期',
+ scope: '全门店',
+ flow: '—'
+ }
+ ],
+ couponCenter: [
+ {
+ id: 11,
+ amount: 20,
+ title: '周末团课券',
+ desc: '周末团课满200减20',
+ expireDays: 30,
+ minSpend: 200,
+ tag: '可领取',
+ claimed: false
+ },
+ {
+ id: 12,
+ amount: 50,
+ title: '私教体验券',
+ desc: '私教课满500减50',
+ expireDays: 15,
+ minSpend: 500,
+ tag: '限时',
+ claimed: false
+ },
+ {
+ id: 13,
+ amount: 10,
+ title: '签到加油券',
+ desc: '无门槛10元券',
+ expireDays: 7,
+ minSpend: 0,
+ tag: '每日',
+ claimed: true
+ }
+ ],
+ pointsConfig: {
+ rate: '100积分 = 1元',
+ rule: '签到、训练、邀请好友、购课均可获得积分;积分可用于商城兑换。'
+ },
+ pointsRewards: [
+ { id: 1, name: '团课体验券', cost: 500, stock: 12, icon: '/static/images/ticket.png' },
+ { id: 2, name: '运动毛巾', cost: 800, stock: 5, icon: '/static/images/dumbbell.png' },
+ { id: 3, name: '私教体验30分钟', cost: 2000, stock: 3, icon: '/static/images/usercheck.png' },
+ { id: 4, name: '蛋白粉小样', cost: 350, stock: 20, icon: '/static/images/star.png' }
+ ],
+ pointsHistory: [
+ { id: 1, type: 'earn', title: '团课签到', amount: 50, time: '2024-07-12 09:10', balance: 1250 },
+ { id: 2, type: 'earn', title: '邀请好友注册', amount: 200, time: '2024-07-08 15:30', balance: 1200 },
+ { id: 3, type: 'spend', title: '兑换团课体验券', amount: -500, time: '2024-07-01 11:00', balance: 1000 },
+ { id: 4, type: 'earn', title: '会员卡续费奖励', amount: 100, time: '2024-07-01 10:05', balance: 1500 },
+ { id: 5, type: 'earn', title: '体测完成奖励', amount: 30, time: '2024-06-28 18:45', balance: 1400 }
+ ],
+ referralRules: [
+ '好友通过您的邀请码注册,双方各得 100 积分',
+ '好友首次购课成功后,您额外获得 300 积分',
+ '每月邀请奖励上限 10 人,超出不再计奖',
+ '积分可用于兑换课程体验券及周边礼品'
+ ],
+ referralRecords: [
+ { id: 1, name: '李**', avatar: '/static/images/user0.png', status: 'purchased', statusLabel: '已购课', time: '2024-07-05', reward: '+300积分', rewardStatus: '已发放' },
+ { id: 2, name: '王**', avatar: '/static/images/user1.png', status: 'registered', statusLabel: '已注册', time: '2024-06-20', reward: '+100积分', rewardStatus: '已发放' },
+ { id: 3, name: '陈**', avatar: '/static/images/user2.png', status: 'invited', statusLabel: '已邀请', time: '2024-06-15', reward: '待注册', rewardStatus: '待发放' },
+ { id: 4, name: '赵**', avatar: '/static/images/user3.png', status: 'purchased', statusLabel: '已购课', time: '2024-06-01', reward: '+300积分', rewardStatus: '已发放' },
+ { id: 5, name: '刘**', avatar: '/static/images/user0.png', status: 'registered', statusLabel: '已注册', time: '2024-05-28', reward: '+100积分', rewardStatus: '已发放' }
+ ],
+ referralRewardSummary: {
+ totalPoints: 800,
+ totalCoupons: 2,
+ pendingCount: 1
+ },
+ myCourseTabs: [
+ { key: 'group', label: '团课' },
+ { key: 'private', label: '私教' },
+ { key: 'online', label: '线上课' },
+ { key: 'package', label: '训练营' }
+ ],
+ myCourses: {
+ group: {
+ ongoing: [
+ {
+ id: 1,
+ title: '瑜伽基础班',
+ coach: '李明教练',
+ banner: '/static/images/AC1Banner.png',
+ progress: 6,
+ total: 12,
+ schedule: '每周二、四 09:00',
+ location: '一楼大厅',
+ nextClass: '07月16日 09:00',
+ canCancel: true,
+ bookingId: 1
+ }
+ ],
+ completed: [
+ {
+ id: 3,
+ title: '动感单车入门',
+ coach: '赵敏教练',
+ banner: '/static/images/AC1Banner.png',
+ progress: 8,
+ total: 8,
+ schedule: '已结课',
+ location: '单车房',
+ completedAt: '2024-06-30',
+ canEvaluate: true
+ }
+ ]
+ },
+ private: {
+ remaining: 7,
+ coach: '王强教练',
+ coachAvatar: '/static/images/user2.png',
+ nextClass: '07月15日 14:00',
+ bookings: [
+ { id: 2, title: '私教 · 力量训练', time: '07月18日 14:00', status: '已预约', location: 'B区私教室' }
+ ],
+ completed: [
+ { id: 5, title: '私教 · 核心塑形', time: '2024-07-10 14:00', coach: '王强教练' }
+ ]
+ },
+ online: [
+ {
+ id: 201,
+ title: '居家核心训练',
+ cover: '/static/images/AC2Banner.png',
+ duration: '45分钟',
+ progress: 60,
+ chapters: 6,
+ watched: 4,
+ type: 'video'
+ },
+ {
+ id: 202,
+ title: '直播 · 晨间拉伸',
+ cover: '/static/images/AC1Banner.png',
+ duration: '30分钟',
+ progress: 0,
+ liveTime: '07月20日 07:00',
+ type: 'live'
+ }
+ ],
+ package: [
+ {
+ id: 301,
+ title: '28天减脂训练营',
+ banner: '/static/images/AC1Banner.png',
+ progress: 3,
+ total: 10,
+ coach: '李明教练',
+ schedule: '每周5练'
+ }
+ ]
+ },
+ checkInTabs: [
+ { key: 'all', label: '全部' },
+ { key: 'group', label: '团课' },
+ { key: 'private', label: '私教' },
+ { key: 'free', label: '自由' }
+ ],
+ checkInHistory: [
+ {
+ id: 1,
+ title: '今日签到 · 瑜伽初级班',
+ time: '2024-07-12 09:05',
+ tag: '团课',
+ tagTheme: 'group',
+ location: '一楼大厅'
+ },
+ {
+ id: 2,
+ title: '自由训练 · 进馆记录',
+ time: '2024-07-11 18:30',
+ tag: '自由',
+ tagTheme: 'free',
+ location: '器械区'
+ },
+ {
+ id: 3,
+ title: '私教课 · 力量训练',
+ time: '2024-07-10 14:00',
+ tag: '私教',
+ tagTheme: 'private',
+ location: 'B区私教室'
+ },
+ {
+ id: 4,
+ title: '团课签到 · 动感单车',
+ time: '2024-07-08 19:02',
+ tag: '团课',
+ tagTheme: 'group',
+ location: '单车房'
+ },
+ {
+ id: 5,
+ title: '自由训练 · 进馆记录',
+ time: '2024-07-06 17:45',
+ tag: '自由',
+ tagTheme: 'free',
+ location: '有氧区'
+ }
+ ]
+}
diff --git a/gym-manage-uniapp/common/memberInfo/moduleStore.js b/gym-manage-uniapp/common/memberInfo/moduleStore.js
new file mode 100644
index 0000000..116c485
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/moduleStore.js
@@ -0,0 +1,270 @@
+import { moduleMock } from './mockData.js'
+
+function clone(value) {
+ return JSON.parse(JSON.stringify(value))
+}
+
+export function getDefaultModuleState() {
+ return {
+ trainingReport: clone(moduleMock.trainingReport),
+ coupons: clone(moduleMock.coupons),
+ couponCenter: clone(moduleMock.couponCenter),
+ pointsHistory: clone(moduleMock.pointsHistory),
+ pointsRewards: clone(moduleMock.pointsRewards),
+ redeemRecords: [],
+ referralRecords: clone(moduleMock.referralRecords),
+ myCourses: clone(moduleMock.myCourses),
+ checkInHistory: clone(moduleMock.checkInHistory)
+ }
+}
+
+export function mergeModuleState(saved) {
+ const defaults = getDefaultModuleState()
+ if (!saved) return defaults
+ return {
+ trainingReport: { ...defaults.trainingReport, ...(saved.trainingReport || {}) },
+ coupons: saved.coupons?.length ? saved.coupons : defaults.coupons,
+ couponCenter: saved.couponCenter?.length ? saved.couponCenter : defaults.couponCenter,
+ pointsHistory: saved.pointsHistory?.length ? saved.pointsHistory : defaults.pointsHistory,
+ pointsRewards: saved.pointsRewards?.length ? saved.pointsRewards : defaults.pointsRewards,
+ redeemRecords: saved.redeemRecords || defaults.redeemRecords,
+ referralRecords: saved.referralRecords?.length ? saved.referralRecords : defaults.referralRecords,
+ myCourses: saved.myCourses ? mergeMyCourses(defaults.myCourses, saved.myCourses) : defaults.myCourses,
+ checkInHistory: saved.checkInHistory?.length ? saved.checkInHistory : defaults.checkInHistory
+ }
+}
+
+function mergeMyCourses(defaults, saved) {
+ return {
+ group: saved.group || defaults.group,
+ private: saved.private || defaults.private,
+ online: saved.online?.length ? saved.online : defaults.online,
+ package: saved.package?.length ? saved.package : defaults.package
+ }
+}
+
+function syncCouponSummary(store) {
+ const available = store.modules.coupons.filter((c) => c.status === 'available')
+ const top = available[0]
+ store.couponPoints = {
+ ...store.couponPoints,
+ amount: top ? `¥${top.amount}` : '暂无',
+ couponDesc: top
+ ? `满${top.minSpend}可用 · ${available.length}张`
+ : '暂无可用优惠券',
+ couponAction: available.length ? '去使用' : '去领取',
+ points: store.stats.pointsBalance,
+ pointsLabel: '我的积分',
+ pointsAction: '去兑换'
+ }
+ return store
+}
+
+export function finalizeModules(store) {
+ syncCouponSummary(store)
+ store.checkIns = store.modules.checkInHistory.slice(0, 3).map((item) => ({ ...item }))
+ return store
+}
+
+export function getTrainingReportData(store, period = 'week') {
+ const report = store.modules.trainingReport
+ const trend = period === 'month' ? report.monthlyHours : report.weeklyHours
+ const calTrend = period === 'month' ? report.monthlyCalories : report.weeklyHours.map((w, i) => ({
+ label: w.label,
+ value: Math.round((report.summary.calories / 7) * (w.value || 0.5))
+ }))
+ return {
+ ...report,
+ period,
+ summary: {
+ ...report.summary,
+ hours: store.stats.trainingHours ?? report.summary.hours,
+ visits: report.summary.visits ?? store.stats.checkInCount ?? 5
+ },
+ trendHours: trend.map((t) => ({ ...t, id: t.label })),
+ trendCalories: calTrend.map((t) => ({ ...t, id: t.label })),
+ sessions: report.sessions.map((s) => ({ ...s }))
+ }
+}
+
+export function getTrainingSessionById(store, id) {
+ const session = store.modules.trainingReport.sessions.find((s) => s.id === Number(id))
+ if (!session) return null
+ return {
+ ...session,
+ heartRate: '128 bpm',
+ comment: '动作标准,核心发力良好,下次可增加负重。',
+ checkInTime: `${session.date} ${session.time.split('-')[0]}`
+ }
+}
+
+export function filterTrainingSessions(store, filters = {}) {
+ let list = store.modules.trainingReport.sessions.map((s) => ({ ...s }))
+ if (filters.type && filters.type !== 'all') {
+ list = list.filter((s) => s.type === filters.type)
+ }
+ return list
+}
+
+export function getCouponsByStatus(store, status) {
+ return store.modules.coupons.filter((c) => c.status === status).map((c) => ({ ...c }))
+}
+
+export function getCouponById(store, id) {
+ const c = store.modules.coupons.find((item) => item.id === Number(id))
+ return c ? { ...c } : null
+}
+
+export function useCoupon(store, id) {
+ const coupon = store.modules.coupons.find((c) => c.id === id)
+ if (!coupon || coupon.status !== 'available') return null
+ coupon.status = 'used'
+ coupon.usedAt = new Date().toISOString().slice(0, 10)
+ syncCouponSummary(store)
+ return coupon
+}
+
+export function deleteExpiredCoupon(store, id) {
+ const idx = store.modules.coupons.findIndex((c) => c.id === id && c.status === 'expired')
+ if (idx >= 0) store.modules.coupons.splice(idx, 1)
+ syncCouponSummary(store)
+ return store
+}
+
+export function getCouponCenterList(store) {
+ return store.modules.couponCenter.map((c) => ({ ...c }))
+}
+
+export function claimCouponFromCenter(store, centerId) {
+ const item = store.modules.couponCenter.find((c) => c.id === centerId)
+ if (!item || item.claimed) return { ok: false, message: '已领取或不存在' }
+ const nextId = store.modules.coupons.reduce((m, c) => Math.max(m, c.id || 0), 0) + 1
+ const expire = new Date()
+ expire.setDate(expire.getDate() + item.expireDays)
+ store.modules.coupons.unshift({
+ id: nextId,
+ status: 'available',
+ amount: item.amount,
+ title: item.title,
+ desc: item.desc,
+ expire: expire.toISOString().slice(0, 10),
+ minSpend: item.minSpend,
+ tag: item.tag,
+ rules: `领取后${item.expireDays}天内有效,满${item.minSpend}可用`,
+ scope: '全门店',
+ flow: '预约课程时使用'
+ })
+ item.claimed = true
+ syncCouponSummary(store)
+ return { ok: true, message: '领取成功' }
+}
+
+export function getPointsPageData(store) {
+ return {
+ balance: store.stats.pointsBalance,
+ config: moduleMock.pointsConfig,
+ history: store.modules.pointsHistory.map((h) => ({ ...h })),
+ rewards: store.modules.pointsRewards.map((r) => ({ ...r })),
+ redeemRecords: (store.modules.redeemRecords || []).map((r) => ({ ...r }))
+ }
+}
+
+function nextHistoryId(history) {
+ return (history || []).reduce((max, item) => Math.max(max, item.id || 0), 0) + 1
+}
+
+function formatNow() {
+ const d = new Date()
+ const pad = (n) => String(n).padStart(2, '0')
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
+}
+
+export function redeemPointsReward(store, rewardId) {
+ const reward = store.modules.pointsRewards.find((r) => r.id === rewardId)
+ if (!reward || reward.stock <= 0) return { ok: false, message: '库存不足' }
+ if (store.stats.pointsBalance < reward.cost) {
+ return { ok: false, message: '积分不足' }
+ }
+ store.stats.pointsBalance -= reward.cost
+ reward.stock -= 1
+ store.modules.pointsHistory.unshift({
+ id: nextHistoryId(store.modules.pointsHistory),
+ type: 'spend',
+ title: `兑换${reward.name}`,
+ amount: -reward.cost,
+ time: formatNow(),
+ balance: store.stats.pointsBalance
+ })
+ store.modules.redeemRecords = store.modules.redeemRecords || []
+ store.modules.redeemRecords.unshift({
+ id: nextHistoryId(store.modules.redeemRecords),
+ name: reward.name,
+ cost: reward.cost,
+ time: formatNow()
+ })
+ syncCouponSummary(store)
+ return { ok: true, message: '兑换成功', reward }
+}
+
+export function filterPointsHistory(store, filter = 'all') {
+ const list = store.modules.pointsHistory
+ if (filter === 'earn') return list.filter((h) => h.amount > 0).map((h) => ({ ...h }))
+ if (filter === 'spend') return list.filter((h) => h.amount < 0).map((h) => ({ ...h }))
+ return list.map((h) => ({ ...h }))
+}
+
+export function getReferralPageData(store) {
+ return {
+ ...store.referral,
+ records: store.modules.referralRecords.map((r) => ({ ...r })),
+ rules: moduleMock.referralRules,
+ rewardSummary: moduleMock.referralRewardSummary
+ }
+}
+
+export function getMyCoursesData(store, tab) {
+ const data = store.modules.myCourses
+ if (tab === 'group') return { ...data.group }
+ if (tab === 'private') return { ...data.private }
+ if (tab === 'online') return { list: data.online.map((c) => ({ ...c })) }
+ if (tab === 'package') return { list: data.package.map((c) => ({ ...c })) }
+ return {}
+}
+
+export function getOnlineCourseById(store, id) {
+ const course = store.modules.myCourses.online.find((c) => c.id === Number(id))
+ if (!course) return null
+ return {
+ ...course,
+ chapters: [
+ { id: 1, title: '热身激活', duration: '8分钟', done: true },
+ { id: 2, title: '核心训练 A', duration: '12分钟', done: true },
+ { id: 3, title: '核心训练 B', duration: '10分钟', done: true },
+ { id: 4, title: '拉伸放松', duration: '8分钟', done: true },
+ { id: 5, title: '进阶组合', duration: '15分钟', done: false },
+ { id: 6, title: '总结复盘', duration: '5分钟', done: false }
+ ],
+ comments: [{ user: '学员A', text: '跟着练很方便' }]
+ }
+}
+
+export function updateOnlineProgress(store, courseId, progress) {
+ const course = store.modules.myCourses.online.find((c) => c.id === Number(courseId))
+ if (course) course.progress = progress
+ return course
+}
+
+export function getCheckInHistory(store, filter = 'all') {
+ const list = store.modules.checkInHistory
+ if (filter === 'all') return list.map((i) => ({ ...i }))
+ return list.filter((i) => i.tagTheme === filter).map((i) => ({ ...i }))
+}
+
+/** @deprecated use getMyCoursesData */
+export function getMyCoursesByTab(store, tab) {
+ if (tab === 'ongoing') return store.modules.myCourses.group?.ongoing || []
+ if (tab === 'completed') return store.modules.myCourses.group?.completed || []
+ return []
+}
+
+export { moduleMock }
diff --git a/gym-manage-uniapp/common/memberInfo/store.js b/gym-manage-uniapp/common/memberInfo/store.js
new file mode 100644
index 0000000..b161e7d
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/store.js
@@ -0,0 +1,308 @@
+import {
+ memberCenterMock,
+ userInfoMock,
+ memberCardMock,
+ bookingMock
+} from './mockData.js'
+import { formatMemberCenterPhone, normalizePhoneForStore } from './format.js'
+import {
+ getDefaultBodyTestState,
+ mergeBodyTestState,
+ getLatestBodyTestRecord,
+ buildBodyReportSummary
+} from './bodyTestStore.js'
+import {
+ getDefaultModuleState,
+ mergeModuleState,
+ finalizeModules
+} from './moduleStore.js'
+import {
+ getDefaultCourseCatalog,
+ mergeCourseCatalog,
+ canCancelBooking
+} from './bookingStore.js'
+
+const STORAGE_KEY = 'gym_member_info_v1'
+
+function clone(value) {
+ return JSON.parse(JSON.stringify(value))
+}
+
+export function buildCardTip(remainingDays) {
+ return `距离下次到期还有${remainingDays}天,请及时续费`
+}
+
+function applyCardInfo(store) {
+ const days = computeRemainingDays(store.card.validityEnd)
+ store.card.remainingDays = days
+ store.cardInfo.remainingDays = days
+ store.cardInfo.tip = buildCardTip(days)
+ return store
+}
+
+export function syncStats(store) {
+ store.stats = {
+ ...store.stats,
+ pointsBalance: store.stats.pointsBalance ?? 1250
+ }
+ return store
+}
+
+function finalizeStore(store) {
+ syncStats(store)
+ applyCardInfo(store)
+ if (store.profile?.avatar) {
+ store.memberProfile.avatar = store.profile.avatar
+ }
+ if (store.profile?.phone) {
+ store.memberProfile.phone = formatMemberCenterPhone(store.profile.phone)
+ }
+ const latestBodyTest = getLatestBodyTestRecord(store)
+ if (latestBodyTest) {
+ const previous = store.bodyTest.records[1]
+ store.bodyReport = buildBodyReportSummary(latestBodyTest, previous)
+ }
+ if (store.modules) {
+ finalizeModules(store)
+ }
+ return store
+}
+
+function getDefaultStore() {
+ return finalizeStore({
+ profile: { ...userInfoMock, avatar: memberCenterMock.userInfo.avatar },
+ memberProfile: { ...memberCenterMock.userInfo },
+ stats: { ...memberCenterMock.stats },
+ cardInfo: { ...memberCenterMock.cardInfo },
+ card: { ...memberCardMock.card },
+ records: clone(memberCardMock.records),
+ ongoingBookings: clone(bookingMock.ongoing),
+ historyBookings: clone(bookingMock.history),
+ checkIns: clone(memberCenterMock.checkIns),
+ bodyReport: { ...memberCenterMock.bodyReport },
+ bodyTest: getDefaultBodyTestState(),
+ modules: getDefaultModuleState(),
+ courseCatalog: getDefaultCourseCatalog(),
+ couponPoints: { ...memberCenterMock.couponPoints },
+ referral: { ...memberCenterMock.referral }
+ })
+}
+
+function mergeDefaults(saved) {
+ const defaults = getDefaultStore()
+ return finalizeStore({
+ profile: { ...defaults.profile, ...(saved.profile || {}) },
+ memberProfile: { ...defaults.memberProfile, ...(saved.memberProfile || {}) },
+ stats: { ...defaults.stats, ...(saved.stats || {}) },
+ cardInfo: { ...defaults.cardInfo, ...(saved.cardInfo || {}) },
+ card: { ...defaults.card, ...(saved.card || {}) },
+ records: saved.records?.length ? saved.records : defaults.records,
+ ongoingBookings: saved.ongoingBookings ?? defaults.ongoingBookings,
+ historyBookings: saved.historyBookings ?? defaults.historyBookings,
+ checkIns: saved.checkIns?.length ? saved.checkIns : defaults.checkIns,
+ bodyReport: { ...defaults.bodyReport, ...(saved.bodyReport || {}) },
+ bodyTest: mergeBodyTestState(saved.bodyTest),
+ modules: mergeModuleState(saved.modules),
+ courseCatalog: mergeCourseCatalog(saved.courseCatalog),
+ couponPoints: { ...defaults.couponPoints, ...(saved.couponPoints || {}) },
+ referral: { ...defaults.referral, ...(saved.referral || {}) }
+ })
+}
+
+export function loadMemberStore() {
+ try {
+ const saved = uni.getStorageSync(STORAGE_KEY)
+ if (saved && typeof saved === 'object') {
+ return mergeDefaults(saved)
+ }
+ } catch (e) {
+ console.warn('[memberStore] load failed', e)
+ }
+ return getDefaultStore()
+}
+
+export function saveMemberStore(store) {
+ uni.setStorageSync(STORAGE_KEY, store)
+}
+
+/** 解析为本地 0 点,避免 ISO 字符串时区偏差 */
+export function parseLocalDate(dateStr) {
+ if (!dateStr) return null
+ const str = String(dateStr).trim()
+ const iso = str.match(/^(\d{4})-(\d{2})-(\d{2})$/)
+ if (iso) {
+ return new Date(Number(iso[1]), Number(iso[2]) - 1, Number(iso[3]))
+ }
+ const cn = str.match(/(\d{4})年(\d{2})月(\d{2})日/)
+ if (cn) {
+ return new Date(Number(cn[1]), Number(cn[2]) - 1, Number(cn[3]))
+ }
+ const parsed = new Date(str.replace(/-/g, '/'))
+ return Number.isNaN(parsed.getTime()) ? null : parsed
+}
+
+function formatIsoDate(date) {
+ const y = date.getFullYear()
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ return `${y}-${m}-${d}`
+}
+
+function formatChineseDate(date) {
+ const y = date.getFullYear()
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ return `${y}年${m}月${day}日`
+}
+
+function formatRecordTime(date) {
+ const y = date.getFullYear()
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ const h = String(date.getHours()).padStart(2, '0')
+ const min = String(date.getMinutes()).padStart(2, '0')
+ return `${y}-${m}-${d} ${h}:${min}`
+}
+
+function nextRecordId(records) {
+ return (records || []).reduce((max, item) => Math.max(max, item.id || 0), 0) + 1
+}
+
+export function computeRemainingDays(endDateStr) {
+ const end = parseLocalDate(endDateStr)
+ if (!end) return 0
+ const now = new Date()
+ now.setHours(0, 0, 0, 0)
+ end.setHours(0, 0, 0, 0)
+ const diff = Math.ceil((end - now) / 86400000)
+ return Math.max(0, diff)
+}
+
+export function formatUpcomingAlert(booking) {
+ if (!booking) return ''
+ const timePart = booking.timeRange || booking.schedule?.split(' ')[1] || ''
+ return `明天 ${timePart} 有一堂${booking.title},请提前 30 分钟到场`
+}
+
+export function toBookingPreviewItem(item) {
+ return {
+ id: item.id,
+ dateDay: item.dateDay,
+ dateMonth: item.dateMonth,
+ desc: `${item.title} · ${item.timeRange}`,
+ coach: item.coachShort || item.coach.replace('教练', ''),
+ location: item.location ? `地点:${item.location}` : '',
+ status: item.status,
+ statusLabel: item.statusLabel
+ }
+}
+
+export function getBookingPreview(store, limit = 2) {
+ return store.ongoingBookings.slice(0, limit).map(toBookingPreviewItem)
+}
+
+export function getCenterPageData(store) {
+ return {
+ userInfo: { ...store.memberProfile },
+ stats: { ...store.stats },
+ cardInfo: { ...store.cardInfo },
+ bookingPreview: getBookingPreview(store),
+ checkIns: store.checkIns.map((item) => ({ ...item })),
+ bodyReport: {
+ ...store.bodyReport,
+ weight: store.profile.weight || store.bodyReport.weight
+ },
+ couponPoints: {
+ ...store.couponPoints,
+ points: store.stats.pointsBalance
+ },
+ referral: { ...store.referral }
+ }
+}
+
+export function cancelOngoingBooking(store, id) {
+ const index = store.ongoingBookings.findIndex((b) => b.id === id)
+ if (index < 0) return { ok: false, message: '预约不存在' }
+
+ const item = store.ongoingBookings[index]
+ if (!canCancelBooking(item)) {
+ return { ok: false, message: '距开课不足2小时,无法取消' }
+ }
+
+ const [removed] = store.ongoingBookings.splice(index, 1)
+ if (removed.courseId) {
+ const course = store.courseCatalog.find((c) => c.id === removed.courseId)
+ if (course && course.enrolled > 0) course.enrolled -= 1
+ }
+ store.historyBookings.unshift({
+ ...removed,
+ status: 'cancelled',
+ statusLabel: '已取消',
+ footerText: '用户主动取消',
+ canCancel: false
+ })
+ finalizeStore(store)
+ saveMemberStore(store)
+ return { ok: true, message: '已取消' }
+}
+
+export function renewMemberCard(store, addDays = 90) {
+ const now = new Date()
+ now.setHours(0, 0, 0, 0)
+
+ let base = parseLocalDate(store.card.validityEnd) || new Date(now)
+ base.setHours(0, 0, 0, 0)
+ // 已过期:从今天起续费;未过期:从当前到期日起顺延
+ if (base < now) {
+ base = new Date(now)
+ }
+
+ const end = new Date(base)
+ end.setDate(end.getDate() + addDays)
+
+ const validityEnd = formatIsoDate(end)
+ const validityEndCn = formatChineseDate(end)
+ store.card.validityEnd = validityEnd
+ store.card.validity = store.card.validityStart
+ ? `${store.card.validityStart} - ${validityEndCn}`
+ : `2024年01月01日 - ${validityEndCn}`
+ store.cardInfo.expireDate = `有效期至 ${validityEndCn}`
+
+ store.records.unshift({
+ id: nextRecordId(store.records),
+ type: 'consume',
+ title: '会员卡续费',
+ time: formatRecordTime(new Date()),
+ value: `+${addDays}天`,
+ valueType: 'positive',
+ icon: '/static/images/pluscircle.png',
+ iconTheme: 'orange'
+ })
+
+ finalizeStore(store)
+ saveMemberStore(store)
+ return store
+}
+
+export function saveUserProfile(store, profile) {
+ const phone = normalizePhoneForStore(profile.phone ?? store.profile.phone)
+ store.profile = { ...store.profile, ...profile, phone }
+ store.memberProfile = {
+ ...store.memberProfile,
+ name: store.profile.name,
+ phone: formatMemberCenterPhone(store.profile.phone),
+ avatar: store.profile.avatar || store.memberProfile.avatar
+ }
+ if (store.profile.weight) {
+ store.bodyReport.weight = store.profile.weight
+ }
+ finalizeStore(store)
+ saveMemberStore(store)
+ return store
+}
+
+export function persistMemberStore(store) {
+ finalizeStore(store)
+ saveMemberStore(store)
+ return store
+}
diff --git a/gym-manage-uniapp/common/memberInfo/validate.js b/gym-manage-uniapp/common/memberInfo/validate.js
new file mode 100644
index 0000000..080ef30
--- /dev/null
+++ b/gym-manage-uniapp/common/memberInfo/validate.js
@@ -0,0 +1,202 @@
+/** 个人信息页前端校验(与后端手机号规则对齐:^1[3-9]\\d{9}$) */
+
+const PHONE_REG = /^1[3-9]\d{9}$/
+const MIN_NAME_LEN = 2
+const MAX_NAME_LEN = 8
+const NAME_REG = new RegExp(
+ `^[\\u4e00-\\u9fa5a-zA-Z·\\s]{${MIN_NAME_LEN},${MAX_NAME_LEN}}$`
+)
+const MEASURE_REG = /^\d+(\.\d)?$/
+
+const MIN_HEIGHT = 50
+const MAX_HEIGHT = 250
+const MIN_WEIGHT = 20
+const MAX_WEIGHT = 300
+const MIN_BIRTH_YEAR = 1900
+const MIN_AGE = 14
+const MAX_FITNESS_GOALS = 5
+
+export function isMaskedPhone(phone) {
+ return String(phone || '').includes('****')
+}
+
+export function validateName(name) {
+ const value = String(name ?? '').trim()
+ if (!value) {
+ return { ok: false, message: '请输入姓名' }
+ }
+ if (!NAME_REG.test(value)) {
+ return { ok: false, message: `姓名为${MIN_NAME_LEN}-${MAX_NAME_LEN}个汉字或字母` }
+ }
+ return { ok: true, value }
+}
+
+/** 保存时使用:允许保留已脱敏的旧手机号 */
+export function validatePhone(phone, options = {}) {
+ const { allowMasked = true } = options
+ const raw = String(phone ?? '').trim()
+ if (!raw) {
+ return { ok: false, message: '请绑定手机号' }
+ }
+ if (allowMasked && isMaskedPhone(raw)) {
+ const digits = raw.replace(/\D/g, '')
+ if (digits.length >= 7) {
+ return { ok: true, value: raw }
+ }
+ return { ok: false, message: '手机号格式不正确' }
+ }
+
+ const digits = raw.replace(/\D/g, '')
+ if (!PHONE_REG.test(digits)) {
+ return { ok: false, message: '请输入11位有效手机号' }
+ }
+ return { ok: true, value: digits }
+}
+
+/** 换绑时必须输入完整新号 */
+export function validatePhoneForRebind(phone) {
+ return validatePhone(phone, { allowMasked: false })
+}
+
+function parseMeasure(value) {
+ const str = String(value ?? '').trim()
+ if (!str || !MEASURE_REG.test(str)) {
+ return null
+ }
+ const num = Number(str)
+ return Number.isFinite(num) ? num : null
+}
+
+function formatMeasure(num) {
+ return Number.isInteger(num) ? String(num) : String(Number(num.toFixed(1)))
+}
+
+export function validateHeight(height) {
+ const num = parseMeasure(height)
+ if (num == null) {
+ return { ok: false, message: '请输入有效身高(单位 cm)' }
+ }
+ if (num < MIN_HEIGHT || num > MAX_HEIGHT) {
+ return { ok: false, message: `身高请在 ${MIN_HEIGHT}-${MAX_HEIGHT} cm 之间` }
+ }
+ return { ok: true, value: formatMeasure(num) }
+}
+
+export function validateWeight(weight) {
+ const num = parseMeasure(weight)
+ if (num == null) {
+ return { ok: false, message: '请输入有效体重(单位 kg)' }
+ }
+ if (num < MIN_WEIGHT || num > MAX_WEIGHT) {
+ return { ok: false, message: `体重请在 ${MIN_WEIGHT}-${MAX_WEIGHT} kg 之间` }
+ }
+ return { ok: true, value: formatMeasure(num) }
+}
+
+export function parseBirthdayChinese(birthday) {
+ const match = String(birthday ?? '').match(/(\d{4})年(\d{2})月(\d{2})日/)
+ if (!match) return null
+ return {
+ year: Number(match[1]),
+ month: Number(match[2]),
+ day: Number(match[3])
+ }
+}
+
+export function validateBirthday(birthday) {
+ const parts = parseBirthdayChinese(birthday)
+ if (!parts) {
+ return { ok: false, message: '请选择生日' }
+ }
+ const { year, month, day } = parts
+ if (year < MIN_BIRTH_YEAR) {
+ return { ok: false, message: '生日年份不合理' }
+ }
+
+ const date = new Date(year, month - 1, day)
+ if (
+ date.getFullYear() !== year ||
+ date.getMonth() !== month - 1 ||
+ date.getDate() !== day
+ ) {
+ return { ok: false, message: '生日日期无效' }
+ }
+
+ const today = new Date()
+ today.setHours(0, 0, 0, 0)
+ if (date > today) {
+ return { ok: false, message: '生日不能晚于今天' }
+ }
+
+ const minBirth = new Date(
+ today.getFullYear() - MIN_AGE,
+ today.getMonth(),
+ today.getDate()
+ )
+ if (date > minBirth) {
+ return { ok: false, message: `需年满 ${MIN_AGE} 周岁` }
+ }
+
+ return { ok: true, value: `${year}年${String(month).padStart(2, '0')}月${String(day).padStart(2, '0')}日` }
+}
+
+export function validateGender(gender) {
+ if (gender === 'male' || gender === 'female') {
+ return { ok: true, value: gender }
+ }
+ return { ok: false, message: '请选择性别' }
+}
+
+export function validateFitnessGoals(goals, options = []) {
+ const list = Array.isArray(goals) ? goals : []
+ const allowed = new Set(options)
+ const invalid = list.filter((g) => !allowed.has(g))
+ if (invalid.length) {
+ return { ok: false, message: '健身目标选项无效' }
+ }
+ if (list.length > MAX_FITNESS_GOALS) {
+ return { ok: false, message: `最多选择 ${MAX_FITNESS_GOALS} 个健身目标` }
+ }
+ return { ok: true, value: [...list] }
+}
+
+export function validateUserProfile(profile, goalOptions = []) {
+ const nameResult = validateName(profile.name)
+ if (!nameResult.ok) return nameResult
+
+ const phoneResult = validatePhone(profile.phone)
+ if (!phoneResult.ok) return phoneResult
+
+ const genderResult = validateGender(profile.gender)
+ if (!genderResult.ok) return genderResult
+
+ const birthdayResult = validateBirthday(profile.birthday)
+ if (!birthdayResult.ok) return birthdayResult
+
+ const heightResult = validateHeight(profile.height)
+ if (!heightResult.ok) return heightResult
+
+ const weightResult = validateWeight(profile.weight)
+ if (!weightResult.ok) return weightResult
+
+ const goalsResult = validateFitnessGoals(profile.fitnessGoals, goalOptions)
+ if (!goalsResult.ok) return goalsResult
+
+ return {
+ ok: true,
+ value: {
+ ...profile,
+ name: nameResult.value,
+ phone: phoneResult.value,
+ gender: genderResult.value,
+ birthday: birthdayResult.value,
+ height: heightResult.value,
+ weight: weightResult.value,
+ fitnessGoals: goalsResult.value
+ }
+ }
+}
+
+export function showValidationError(message) {
+ uni.showToast({ title: message, icon: 'none' })
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-all.css b/gym-manage-uniapp/common/style/memberInfo/member-info-all.css
new file mode 100644
index 0000000..8824a7b
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-all.css
@@ -0,0 +1,12 @@
+@import './member-info-page.css';
+@import './member-info-status-bar.css';
+@import './member-info-header.css';
+@import './member-info-member-card.css';
+@import './member-info-quick-actions.css';
+@import './member-info-booking-list.css';
+@import './member-info-check-in-list.css';
+@import './member-info-body-report.css';
+@import './member-info-coupon-points.css';
+@import './member-info-referral.css';
+@import './member-info-settings.css';
+@import './member-info-logout.css';
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-body-report.css b/gym-manage-uniapp/common/style/memberInfo/member-info-body-report.css
new file mode 100644
index 0000000..8a52946
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-body-report.css
@@ -0,0 +1,245 @@
+.body-report-section {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+}
+
+.body-report-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ width: 100%;
+}
+
+.body-report-section__header {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.body-report-section__header-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.body-report-section__title {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+}
+
+.body-report-section__card {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ border-radius: 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white, #ffffff);
+}
+
+.body-report-section__card-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: var(--spacing-md, 16px);
+ width: 100%;
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+.body-report-section__card-head {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.body-report-section__card-head-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.body-report-section__desc {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+}
+
+.body-report-section__view-btn {
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+}
+
+.body-report-section__view-icon {width: 14px;
+ height: 14px;
+ display: block;
+}
+
+.body-report-section__view-report {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--accent-orange);
+}
+
+.body-report-section__metrics {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+
+.body-report-section__metrics-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ width: 100%;
+}
+
+.body-report-section__metric {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-width: 0;
+}
+
+.body-report-section__metric-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ align-items: center;
+ width: 100%;
+}
+
+.body-report-section__text {
+ font-size: var(--font-size-xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--accent-orange);
+}
+
+.body-report-section__text-2 {
+ font-size: var(--font-size-xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--success-green);
+}
+
+.body-report-section__text-4 {
+ font-size: var(--font-size-xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: rgba(243, 156, 18, 1);
+}
+
+.body-report-section__num {
+ font-size: var(--font-size-xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--primary-deep);
+}
+
+.body-report-section__metric-value,
+.body-report-section__text-3,
+.body-report-section__metric-label,
+.body-report-section__text-5 {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+}
+
+.body-report-section__metric-divider {
+ width: 1px;
+ height: 30px;
+ flex-shrink: 0;
+ background-color: var(--border-light, #e9edf2);
+}
+
+.body-report-section__summary {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 10px 12px;
+ border-radius: 10px;
+ background-color: var(--bg-light, #f9fafe);
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+.body-report-section__summary-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ width: 100%;
+ min-width: 0;
+}
+
+.body-report-section__goal {
+ flex: 1;
+ min-width: 0;
+ max-width: 58%;
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacing-xs);
+ align-items: center;
+ padding: 5px 10px;
+ border-radius: 100px;
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.body-report-section__goal-icon {width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.body-report-section__goal-text {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--success-green);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.body-report-section__change {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacing-xs);
+ align-items: center;
+}
+
+.body-report-section__change-icon {width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.body-report-section__metric-value-2 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--success-green);
+ white-space: nowrap;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-booking-list.css b/gym-manage-uniapp/common/style/memberInfo/member-info-booking-list.css
new file mode 100644
index 0000000..d9f7e40
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-booking-list.css
@@ -0,0 +1,215 @@
+.booking-section {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.booking-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+
+.booking-section__header {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.booking-section__header-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+
+.booking-section__title {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+ flex-shrink: 0;
+ white-space: nowrap;
+}
+
+.booking-section__link {
+ flex-shrink: 0;
+}
+
+.booking-section__item {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ border-radius: 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white, #ffffff);
+}
+
+.booking-section__item-inner {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ align-items: center;
+ padding: 14px;
+ width: 100%;
+ position: relative;
+}
+
+.booking-section__date {
+ width: 48px;
+ height: 56px;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ border-radius: 10px;
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.booking-section__date-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+}
+
+.booking-section__num {
+ font-size: var(--font-size-2xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--accent-orange);
+ line-height: 1;
+}
+
+.booking-section__date-sub {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-regular);
+ color: var(--accent-orange-light);
+}
+
+.booking-section__content {
+ width: 100%;
+ height: auto;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+
+.booking-section__content-inner {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xs);
+ align-items: flex-start;
+ width: 100%;
+}
+
+.booking-section__desc {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-medium);
+ color: var(--text-dark);
+}
+
+.booking-section__meta {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.booking-section__meta-inner {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ width: 100%;
+}
+
+.booking-section__icon-coach {width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.booking-section__coach {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-regular);
+ color: var(--text-muted);
+}
+
+.booking-section__icon-location {width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.booking-section__text {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-regular);
+ color: var(--text-muted);
+}
+
+.booking-section__status-wrap {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.booking-section__status-badge {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ padding: 3px 8px;
+ border-radius: 6px;
+}
+
+.booking-section__status-badge--booked {
+ background-color: var(--success-green);
+}
+
+.booking-section__status-badge--pending {
+ background-color: rgba(255, 243, 238, 1);
+ border: 1px solid rgba(212, 166, 74, 1);
+}
+
+.booking-section__status-text {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-medium);
+ color: var(--text-inverse);
+ line-height: 1.2;
+}
+
+.booking-section__status-text--pending {
+ color: var(--accent-orange);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-check-in-list.css b/gym-manage-uniapp/common/style/memberInfo/member-info-check-in-list.css
new file mode 100644
index 0000000..c2afe4f
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-check-in-list.css
@@ -0,0 +1,186 @@
+.checkin-section {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.checkin-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+
+.checkin-section__header {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.checkin-section__header-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.checkin-section__title {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-bold);
+ color: var(--text-dark);
+}
+
+.checkin-section__list {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px;
+ box-shadow: var(--shadow-sm);
+ background-color: var(--bg-white);
+ border: 1px solid var(--border-light);
+ overflow: hidden;
+}
+
+.checkin-section__list-inner {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+}
+
+.checkin-section__row {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.checkin-section__item {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.checkin-section__item-inner {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ align-items: center;
+ padding: 14px var(--spacing-md);
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.checkin-section__dot {
+ width: 8px;
+ height: 8px;
+ flex-shrink: 0;
+ border-radius: var(--radius-full);
+ background-color: var(--success-green);
+}
+
+.checkin-section__dot--group {
+ background-color: var(--success-green);
+}
+
+.checkin-section__dot--free {
+ background-color: var(--accent-orange);
+}
+
+.checkin-section__dot--private {
+ background-color: var(--primary-deep);
+}
+
+.checkin-section__content {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.checkin-section__content-inner {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xs);
+ align-items: flex-start;
+ width: 100%;
+}
+
+.checkin-section__desc {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-medium);
+ color: var(--text-dark);
+ line-height: 1.3;
+}
+
+.checkin-section__text {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-regular);
+ color: var(--text-light);
+ line-height: 1.3;
+}
+
+.checkin-section__tag-badge {
+ flex-shrink: 0;
+ margin-left: var(--spacing-sm);
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ padding: 3px var(--spacing-sm);
+ border-radius: 6px;
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.checkin-section__tag-badge--group {
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.checkin-section__tag-badge--free {
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.checkin-section__tag-badge--private {
+ background-color: rgba(235, 243, 250, 1);
+}
+
+.checkin-section__tag-text {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: var(--font-weight-medium);
+ color: var(--success-green);
+ line-height: 1.2;
+}
+
+.checkin-section__tag-text--group {
+ color: var(--success-green);
+}
+
+.checkin-section__tag-text--free {
+ color: var(--accent-orange);
+}
+
+.checkin-section__tag-text--private {
+ color: var(--primary-deep);
+}
+
+.checkin-section__divider {
+ height: 1px;
+ background-color: var(--border-light);
+ margin-left: calc(var(--spacing-md) + 8px + 12px);
+ margin-right: var(--spacing-md);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-component-reset.css b/gym-manage-uniapp/common/style/memberInfo/member-info-component-reset.css
new file mode 100644
index 0000000..ce4ecd1
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-component-reset.css
@@ -0,0 +1,99 @@
+/* 组件根节点:锁定浅色变量 + box-sizing(小程序组件内继承 page 的 theme-light 不稳定) */
+.status-bar,
+.profile-header,
+.member-card-section,
+.quick-actions,
+.booking-section,
+.checkin-section,
+.body-report-section,
+.coupon-section,
+.referral-section,
+.settings-section,
+.logout-btn__border-wrap,
+.logout-section {
+ box-sizing: border-box;
+ --primary-dark: #0B2B4B;
+ --primary-deep: #1A4A6F;
+ --primary-light: #2C6288;
+ --accent-orange: #FF6B35;
+ --accent-orange-light: #FF8C5A;
+ --accent-orange-dark: #E55A2B;
+ --bg-light: #F9FAFE;
+ --bg-white: #FFFFFF;
+ --bg-gray: #F2F5F9;
+ --text-dark: #1E2A3A;
+ --text-muted: #5E6F8D;
+ --text-light: #8A99B4;
+ --text-inverse: #FFFFFF;
+ --border-light: #E9EDF2;
+ --border-focus: #FF6B35;
+ --success-green: #2ECC71;
+ --warning-amber: #F39C12;
+ --error-red: #E74C3C;
+ --info-blue: #3498DB;
+ --spacing-xs: 4px;
+ --spacing-sm: 8px;
+ --spacing-md: 16px;
+ --spacing-lg: 24px;
+ --spacing-xl: 32px;
+ --radius-sm: 12px;
+ --radius-md: 20px;
+ --radius-lg: 28px;
+ --radius-full: 999px;
+ --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ --font-size-xs: 11px;
+ --font-size-sm: 12px;
+ --font-size-base: 14px;
+ --font-size-md: 16px;
+ --font-size-lg: 18px;
+ --font-size-xl: 20px;
+ --font-size-2xl: 22px;
+ --font-size-3xl: 24px;
+ --font-size-4xl: 28px;
+ --font-size-5xl: 32px;
+ --font-weight-regular: 400;
+ --font-weight-medium: 500;
+ --font-weight-bold: 700;
+ --shadow-sm: 0 8px 20px rgba(0, 0, 0, 0.03), 0 2px 6px rgba(0, 0, 0, 0.05);
+}
+
+/* 区块标题右侧操作链接:查看全部 / 全部记录 / 历史数据 / 推荐记录 */
+.member-card-section__link-text,
+.booking-section__view-all,
+.checkin-section__view-all,
+.coupon-section__view-all,
+.body-report-section__history-link,
+.referral-section__records-link {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--accent-orange);
+ white-space: nowrap;
+ flex-shrink: 0;
+ line-height: 1.4;
+}
+
+.member-card-section__link,
+.booking-section__link,
+.checkin-section__link,
+.coupon-section__link,
+.body-report-section__link,
+.referral-section__link {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 2px;
+ flex-shrink: 0;
+}
+
+.member-card-section__link-arrow,
+.booking-section__link-arrow,
+.checkin-section__link-arrow,
+.coupon-section__link-arrow,
+.body-report-section__link-arrow,
+.referral-section__link-arrow {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ display: block;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-coupon-points.css b/gym-manage-uniapp/common/style/memberInfo/member-info-coupon-points.css
new file mode 100644
index 0000000..6223081
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-coupon-points.css
@@ -0,0 +1,243 @@
+@import '@/common/style/memberInfo/member-info-gradient-cards.css';
+
+.coupon-section {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.coupon-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+
+.coupon-section__header {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.coupon-section__header-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+
+.coupon-section__title {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.coupon-section__link {
+ width: auto;
+ height: auto;
+ position: relative;
+}
+
+.coupon-section__view-all {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-grow: 0;
+}
+
+.coupon-section__link-arrow {
+ position: relative;
+}
+
+.coupon-section__cards {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+
+.coupon-section__cards-inner {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+
+.coupon-section__coupon {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+ border-radius: var(--radius-sm);
+}
+
+.coupon-section__coupon-inner {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xs);
+ align-items: flex-start;
+ justify-content: center;
+ padding: 14px 14px 14px 14px;
+ width: 100%;
+ position: relative;
+}
+
+.coupon-section__amount {
+ font-size: var(--font-size-3xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.coupon-section__desc {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.coupon-section__coupon-status {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 2px 6px 2px 6px;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(255, 255, 255, 0.1882352977991104);
+}
+
+.coupon-section__status {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.coupon-section__points {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+ border-radius: var(--radius-sm);
+}
+
+.coupon-section__points-inner {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xs);
+ align-items: flex-start;
+ justify-content: center;
+ padding: 14px 14px 14px 14px;
+ width: 100%;
+ position: relative;
+}
+
+.coupon-section__num {
+ font-size: var(--font-size-3xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.coupon-section__points-label {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.coupon-section__points-action {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 2px 6px 2px 6px;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(255, 255, 255, 0.1882352977991104);
+}
+
+.coupon-section__text {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-gradient-cards.css b/gym-manage-uniapp/common/style/memberInfo/member-info-gradient-cards.css
new file mode 100644
index 0000000..6db6e36
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-gradient-cards.css
@@ -0,0 +1,31 @@
+/**
+ * 与个人中心首页 coupon-section 一致的渐变卡片背景
+ * 使用 SVG 背景,兼容微信小程序(CSS 变量渐变在部分端不生效)
+ */
+
+.mi-gradient-blue,
+.coupon-section__points,
+.mi-mod-points-hero,
+.bt-hero,
+.bt-score-card,
+.mi-mod-referral-hero {
+ background-color: #0B2B4B;
+ background-position: center;
+ background-size: cover;
+ background-image: url("data:image/svg+xml;utf8,%3Csvg%20viewBox%3D'0%200%20174%20106'%20preserveAspectRatio%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%0A%20%20%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3ClinearGradient%20id%3D'grad'%20gradientUnits%3D'objectBoundingBox'%20x1%3D'0'%20y1%3D'0.5'%20x2%3D'1'%20y2%3D'0.5'%20gradientTransform%3D'matrix(-0.7071%2C%200.7071%2C%20-0.7071%2C%20-0.7071%2C%201.2071%2C%200.5000)'%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cstop%20stop-color%3D'rgba(11%2C43%2C75%2C1)'%20offset%3D'0'%2F%3E%3Cstop%20stop-color%3D'rgba(26%2C74%2C111%2C1)'%20offset%3D'1'%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2FlinearGradient%3E%0A%20%20%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%20%20%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'url(%23grad)'%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E");
+ box-shadow: 0px 4px 8px 0px rgba(11, 43, 75, 0.25);
+}
+
+.mi-gradient-orange,
+.coupon-section__coupon,
+.mi-mod-coupon__left,
+.mi-mod-coupon__use,
+.mi-center-coupon__btn:not(.mi-center-coupon__btn--done),
+.bt-page__action-link--primary,
+.bt-btn--primary {
+ background-color: #FF6B35;
+ background-position: center;
+ background-size: cover;
+ background-image: url("data:image/svg+xml;utf8,%3Csvg%20viewBox%3D'0%200%20174%20106'%20preserveAspectRatio%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%0A%20%20%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3ClinearGradient%20id%3D'grad'%20gradientUnits%3D'objectBoundingBox'%20x1%3D'0'%20y1%3D'0.5'%20x2%3D'1'%20y2%3D'0.5'%20gradientTransform%3D'matrix(-0.7071%2C%200.7071%2C%20-0.7071%2C%20-0.7071%2C%201.2071%2C%200.5000)'%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cstop%20stop-color%3D'rgba(255%2C107%2C53%2C1)'%20offset%3D'0'%2F%3E%3Cstop%20stop-color%3D'rgba(255%2C140%2C90%2C1)'%20offset%3D'1'%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2FlinearGradient%3E%0A%20%20%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%20%20%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'url(%23grad)'%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E");
+ box-shadow: 0px 4px 8px 0px rgba(255, 107, 53, 0.25);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-header.css b/gym-manage-uniapp/common/style/memberInfo/member-info-header.css
new file mode 100644
index 0000000..22b25ed
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-header.css
@@ -0,0 +1,271 @@
+.profile-header {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+}
+
+/* 顶栏固定:仅白底导航栏吸顶,下方用户信息可滚动 */
+.profile-header__toolbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ width: 100%;
+ background-color: var(--bg-white, #ffffff);
+ box-sizing: border-box;
+}
+
+.profile-header__toolbar-spacer {
+ width: 100%;
+ flex-shrink: 0;
+}
+
+.profile-header__nav {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 44px;
+ padding-left: 16px;
+ box-sizing: border-box;
+}
+
+.profile-header__nav-left {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+ min-width: 56px;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 2;
+}
+
+.profile-header__nav-right {
+ min-width: 72px;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 2;
+ margin-left: auto;
+}
+
+.profile-header__title {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+ max-width: 42%;
+ text-align: center;
+ font-size: var(--font-size-md, 16px);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ pointer-events: none;
+ z-index: 1;
+}
+
+.profile-header__icon-bell,
+.profile-header__icon-settings {
+ width: 22px;
+ height: 22px;
+ flex-shrink: 0;
+ display: block;
+}
+
+/* settings.png 为白色线稿,白底顶栏需着色后才可见 */
+.profile-header__icon-settings {
+ filter: brightness(0) saturate(100%) invert(52%) sepia(98%) saturate(1800%) hue-rotate(346deg) brightness(102%) contrast(101%);
+}
+
+/* 用户信息渐变区 */
+.profile-header__hero {
+ width: 100%;
+ background-position: center;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+ background-image: url("data:image/svg+xml;utf8,%3Csvg%20viewBox%3D'0%200%20390%20239'%20preserveAspectRatio%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%0A%20%20%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3ClinearGradient%20id%3D'grad'%20gradientUnits%3D'objectBoundingBox'%20x1%3D'0'%20y1%3D'0.5'%20x2%3D'1'%20y2%3D'0.5'%20gradientTransform%3D'matrix(-0.0000%2C%201.0000%2C%20-1.0000%2C%20-0.0000%2C%201.0000%2C%200.0000)'%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cstop%20stop-color%3D'rgba(11%2C43%2C75%2C1)'%20offset%3D'0'%2F%3E%3Cstop%20stop-color%3D'rgba(26%2C74%2C111%2C1)'%20offset%3D'1'%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2FlinearGradient%3E%0A%20%20%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%20%20%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'url(%23grad)'%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E");
+}
+
+.profile-header__inner {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ padding: 16px 20px 28px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.profile-header__user {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.profile-header__user-inner {
+ display: flex;
+ flex-direction: row;
+ gap: 14px;
+ align-items: center;
+ width: 100%;
+}
+
+.profile-header__avatar-wrap {
+ position: relative;
+ width: 72px;
+ height: 72px;
+ flex-shrink: 0;
+ overflow: visible;
+}
+
+.profile-header__avatar-ring {
+ width: 72px;
+ height: 72px;
+ border-radius: 50%;
+ border: 3px solid #ffffff;
+ overflow: hidden;
+ box-sizing: border-box;
+ background-color: rgba(255, 255, 255, 0.12);
+}
+
+.profile-header__avatar {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.profile-header__avatar-badge {
+ position: absolute;
+ right: -1px;
+ bottom: -1px;
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ border: 2px solid #ffffff;
+ background-color: #2ecc71;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 2;
+ box-sizing: border-box;
+}
+
+.profile-header__avatar-badge-icon {
+ width: 12px;
+ height: 12px;
+ display: block;
+ flex-shrink: 0;
+ filter: brightness(0) invert(1);
+}
+
+.profile-header__user-meta {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.profile-header__user-meta-inner {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xs, 4px);
+ align-items: flex-start;
+ width: 100%;
+}
+
+.profile-header__name {
+ font-size: var(--font-size-xl, 20px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ white-space: nowrap;
+}
+
+.profile-header__phone {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ white-space: nowrap;
+}
+
+.profile-header__badge {
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacing-xs, 4px);
+ align-items: center;
+ padding: 3px 10px;
+ border-radius: 10px;
+ background-color: var(--accent-orange);
+}
+
+.profile-header__badge-icon {
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.profile-header__level {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-inverse);
+ white-space: nowrap;
+}
+
+.profile-header__stats {
+ width: 100%;
+}
+
+.profile-header__stats-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding-top: 16px;
+ width: 100%;
+}
+
+.profile-header__stat {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-width: 0;
+}
+
+.profile-header__stat-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ align-items: center;
+ width: 100%;
+}
+
+.profile-header__stat-value {
+ font-size: var(--font-size-2xl, 22px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ white-space: nowrap;
+}
+
+.profile-header__stat-label {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ white-space: nowrap;
+}
+
+.profile-header__stat-divider {
+ width: 1px;
+ height: 32px;
+ flex-shrink: 0;
+ background-color: rgba(255, 255, 255, 0.31);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-logout.css b/gym-manage-uniapp/common/style/memberInfo/member-info-logout.css
new file mode 100644
index 0000000..94bbf42
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-logout.css
@@ -0,0 +1,40 @@
+.logout-section {
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.logout-section__btn {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ width: 100%;
+ min-height: 50px;
+ padding: 14px 16px;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: #ffffff;
+ box-sizing: border-box;
+}
+
+.logout-section__icon {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.logout-section__text {
+ font-size: 14px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ font-weight: 500;
+ color: #e74c3c;
+ line-height: 1;
+ white-space: nowrap;
+}
+
+.mi-tap-btn--hover {
+ opacity: 0.85;
+ transform: scale(0.98);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-member-card.css b/gym-manage-uniapp/common/style/memberInfo/member-info-member-card.css
new file mode 100644
index 0000000..d9a3d97
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-member-card.css
@@ -0,0 +1,362 @@
+.member-card-section {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.member-card-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+
+.member-card-section__head {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.member-card-section__head-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+
+.member-card-section__title {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.member-card-section__link {
+ width: auto;
+ height: auto;
+ position: relative;
+}
+
+.member-card-section__link-text {
+ white-space: nowrap;
+}
+
+.member-card-section__link-arrow {
+ position: relative;
+}
+
+.member-card-preview {
+ width: 100%;
+ min-height: 140px;
+ height: auto;
+ overflow: visible;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ border-radius: 16px;
+ box-shadow: 0px 8px 16px 0px rgba(255, 107, 53, 0.25);
+ background-position: center;
+ background-size: 100% 100%;
+ box-sizing: border-box;
+ background-image: url("data:image/svg+xml;utf8,%3Csvg%20viewBox%3D'0%200%20358%20140'%20preserveAspectRatio%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%0A%20%20%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3ClinearGradient%20id%3D'grad'%20gradientUnits%3D'objectBoundingBox'%20x1%3D'0'%20y1%3D'0.5'%20x2%3D'1'%20y2%3D'0.5'%20gradientTransform%3D'matrix(-0.7071%2C%200.7071%2C%20-0.7071%2C%20-0.7071%2C%201.2071%2C%200.5000)'%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cstop%20stop-color%3D'rgba(255%2C107%2C53%2C1)'%20offset%3D'0'%2F%3E%3Cstop%20stop-color%3D'rgba(255%2C140%2C90%2C1)'%20offset%3D'0.6000000238418579'%2F%3E%3Cstop%20stop-color%3D'rgba(229%2C90%2C43%2C1)'%20offset%3D'1'%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2FlinearGradient%3E%0A%20%20%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%20%20%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'url(%23grad)'%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E");
+}
+
+.member-card-preview__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ align-items: stretch;
+ padding: 20px 16px 16px;
+ width: 100%;
+ box-sizing: border-box;
+ position: relative;
+}
+
+.member-card-preview__head {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.member-card-preview__head-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ width: 100%;
+ min-width: 0;
+ position: relative;
+}
+
+.member-card-preview__type-row {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 6px;
+ align-items: center;
+}
+
+.member-card-preview__icon-wrap {
+ width: 18px;
+ height: 18px;
+ position: relative;
+ flex-shrink: 0;
+}
+
+.member-card-preview__icon-bg {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ border-radius: 1.5px 1.5px 1.5px 1.5px;
+}
+
+.member-card-preview__icon-border {
+ position: absolute;
+ left: 8.33%;
+ top: 20.83%;
+ right: 8.33%;
+ bottom: 20.83%;
+ width: 83.34%;
+ height: 58.34%;
+}
+
+.member-card-preview__icon-stroke {
+ position: absolute;
+ inset: -0.75px -0.75px -0.75px -0.75px;
+ border-radius: 1.5px 1.5px 1.5px 1.5px;
+ pointer-events: none;
+ border-width: 1.5px 1.5px 1.5px 1.5px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--text-inverse);
+}
+
+.member-card-preview__icon-line {width: 83.34%;
+ height: 8.33%;
+ position: absolute;
+ left: 8.33%;
+ right: 8.33%;
+ top: 41.67%;
+ bottom: 50%;
+ display: block;
+}
+
+.member-card-preview__name {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.member-card-preview__tag {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 3px 10px;
+ border-radius: 8px;
+ background-color: rgba(255, 255, 255, 0.19);
+}
+
+.member-card-preview__tag-text {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.member-card-preview__expire {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.member-card-preview__footer {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ background-color: transparent;
+}
+
+.member-card-preview__footer-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ width: 100%;
+ min-width: 0;
+ position: relative;
+}
+
+.member-card-preview__days {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacing-xs);
+ align-items: flex-start;
+}
+
+.member-card-preview__days-num {
+ font-size: var(--font-size-4xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.member-card-preview__days-unit {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.member-card-preview__renew {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 6px 14px;
+ border-radius: 14px;
+ background-color: var(--bg-white);
+}
+
+.member-card-preview__renew-text {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.member-card-tip__inner {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ border-radius: 10px 10px 10px 10px;
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.member-card-tip__content {
+ display: flex;
+ flex-direction: row;
+ gap: 6px;
+ align-items: center;
+ padding: 8px 12px 8px 12px;
+ width: 100%;
+ position: relative;
+}
+
+.member-card-tip {
+ position: relative;
+ width: 100%;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+
+.member-card-tip__border {
+ position: absolute;
+ inset: 0px;
+ border-radius: 10px 10px 10px 10px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(255, 204, 170, 1);
+}
+
+.member-card-tip__icon {width: 14px;
+ height: 14px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+
+.member-card-tip__text {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--accent-orange);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-page.css b/gym-manage-uniapp/common/style/memberInfo/member-info-page.css
new file mode 100644
index 0000000..3a0ba60
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-page.css
@@ -0,0 +1,87 @@
+.scroll-container {
+ height: 100%;
+ width: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ box-sizing: border-box;
+ --spacing-xs: 4px;
+ --spacing-sm: 8px;
+ --spacing-md: 16px;
+ --spacing-lg: 24px;
+ --spacing-xl: 32px;
+ --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ --font-size-xs: 11px;
+ --font-size-sm: 12px;
+ --font-size-base: 14px;
+ --font-size-md: 16px;
+ --font-size-lg: 18px;
+ --font-size-xl: 20px;
+ --font-size-2xl: 22px;
+ --font-size-3xl: 24px;
+ --font-size-4xl: 28px;
+ --font-size-5xl: 32px;
+}
+
+.scroll-container > view {
+ width: 100%;
+}
+
+.member-page {
+ width: 100%;
+ min-height: 100%;
+ height: auto;
+ overflow: visible;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ background-color: var(--bg-light);
+ box-sizing: border-box;
+ padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
+}
+
+.member-page__body {
+ width: 100%;
+ height: auto;
+ flex: 1;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+}
+
+.member-page__sections {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+ padding: var(--spacing-md, 16px);
+ padding-bottom: calc(var(--spacing-md, 16px) + env(safe-area-inset-bottom));
+ width: 100%;
+ box-sizing: border-box;
+ position: relative;
+ font-family: var(--font-family);
+}
+
+.member-page__sections text {
+ font-family: var(--font-family);
+}
+
+/* ????????? */
+.status-bar,
+.profile-header,
+.member-card-section,
+.quick-actions,
+.booking-section,
+.checkin-section,
+.body-report-section,
+.coupon-section,
+.referral-section,
+.settings-section,
+.logout-btn__border-wrap,
+.logout-section {
+ width: 100%;
+ box-sizing: border-box;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-quick-actions.css b/gym-manage-uniapp/common/style/memberInfo/member-info-quick-actions.css
new file mode 100644
index 0000000..015e8e4
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-quick-actions.css
@@ -0,0 +1,226 @@
+.quick-actions {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ border-radius: 16px;
+ box-shadow: 0px 2px 12px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white, #ffffff);
+ overflow: hidden;
+}
+
+.quick-actions__inner {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+}
+
+.quick-actions__grid {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+}
+
+.quick-actions__grid-inner {
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+}
+
+.quick-actions__item {
+ flex: 1;
+ height: 80px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ min-width: 0;
+}
+
+.quick-actions__item-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+}
+
+.quick-actions__icon-wrap {
+ width: 40px;
+ height: 40px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: var(--radius-sm);
+}
+
+.quick-actions__icon-wrap-inner {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+}
+
+.quick-actions__icon {
+ width: 20px;
+ height: 20px;
+ position: relative;
+}
+
+.quick-actions__icon-part {
+ position: absolute;
+ display: block;
+}
+
+.quick-actions__icon-part:nth-child(1) {
+ width: 7.69%;
+ height: 16.67%;
+ left: 30.77%;
+ top: 8.33%;
+
+}
+
+.quick-actions__icon-part:nth-child(2) {
+ width: 7.69%;
+ height: 16.67%;
+ left: 61.54%;
+ top: 8.33%;
+
+}
+
+.quick-actions__icon-part:nth-child(4) {
+ width: 7.69%;
+ height: 16.67%;
+ left: 30.77%;
+ top: 58.33%;
+
+}
+
+.quick-actions__icon-part:nth-child(5) {
+ width: 7.69%;
+ height: 16.67%;
+ left: 61.54%;
+ top: 58.33%;
+
+}
+
+.quick-actions__border-wrap {
+ position: absolute;
+ left: 12.5%;
+ top: 25%;
+ width: 75%;
+ height: 50%;
+}
+
+.quick-actions__rect {
+ width: 100%;
+ height: 100%;
+ border-radius: 2px;
+}
+
+.quick-actions__border {
+ position: absolute;
+ inset: -1px;
+ border-radius: 2px;
+ border: 1px solid var(--accent-orange);
+ pointer-events: none;
+}
+
+.quick-actions__icon-img {width: 20px;
+ height: 20px;
+ display: block;
+}
+
+.quick-actions__title,
+.quick-actions__title-2,
+.quick-actions__title-3,
+.quick-actions__title-4,
+.quick-actions__coach,
+.quick-actions__text,
+.quick-actions__text-2,
+.quick-actions__points-desc {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-dark);
+ text-align: center;
+}
+
+.quick-actions__divider {
+ width: 100%;
+ height: 1px;
+ background-color: var(--border-light, #e9edf2);
+}
+
+/* 第�?�?�??�?*/
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(1),
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(1) .quick-actions__icon-wrap {
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(2),
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(2) .quick-actions__icon-wrap {
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(2) .quick-actions__icon-img {
+
+}
+
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(3),
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(3) .quick-actions__icon-wrap {
+ background-color: rgba(235, 243, 250, 1);
+}
+
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(3) .quick-actions__icon-img {
+
+}
+
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(4),
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(4) .quick-actions__icon-wrap {
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.quick-actions__grid:nth-of-type(1) .quick-actions__item:nth-child(4) .quick-actions__icon-img {
+
+}
+
+/* 第�?�?�??�?*/
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(1),
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(1) .quick-actions__icon-wrap {
+ background-color: rgba(255, 236, 236, 1);
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(1) .quick-actions__icon-img {
+
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(2),
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(2) .quick-actions__icon-wrap {
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(2) .quick-actions__icon-img {
+
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(3),
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(3) .quick-actions__icon-wrap {
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(3) .quick-actions__icon-img {
+
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(4),
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(4) .quick-actions__icon-wrap {
+ background-color: rgba(235, 243, 250, 1);
+}
+
+.quick-actions__grid:nth-of-type(3) .quick-actions__item:nth-child(4) .quick-actions__icon-img {
+
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-referral.css b/gym-manage-uniapp/common/style/memberInfo/member-info-referral.css
new file mode 100644
index 0000000..292cc62
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-referral.css
@@ -0,0 +1,211 @@
+.referral-section {
+ width: 100%;
+ max-width: 100%;
+ box-sizing: border-box;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: var(--bg-white, #ffffff);
+ overflow: hidden;
+}
+
+.referral-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 16px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.referral-section__header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.referral-section__title {
+ font-size: var(--font-size-md, 16px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+ flex-shrink: 0;
+}
+
+.referral-section__link {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 2px;
+ flex-shrink: 0;
+}
+
+.referral-section__records-link {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--accent-orange);
+ line-height: 1.4;
+ white-space: nowrap;
+}
+
+.referral-section__link-arrow {
+ width: 14px;
+ height: 14px;
+ display: block;
+ flex-shrink: 0;
+}
+
+/* 推荐码行:grid 避免小程序 flex 宽度计算异常 */
+.referral-section__code-row {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ column-gap: 10px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.referral-section__code-box {
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 2px;
+ padding: 10px 12px;
+ border-radius: 8px;
+ background-color: #f2f5f9;
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+.referral-section__code-label {
+ display: block;
+ font-size: var(--font-size-xs, 11px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ line-height: 1.4;
+}
+
+.referral-section__code-value {
+ display: block;
+ width: 100%;
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--primary-dark);
+ line-height: 1.3;
+ letter-spacing: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.referral-section__copy-btn {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ height: 100%;
+ min-height: 52px;
+ padding: 0 14px;
+ border-radius: 10px;
+ background-color: var(--accent-orange);
+ box-sizing: border-box;
+}
+
+/* 例图:双矩形复制图标 */
+.referral-section__copy-icon {
+ position: relative;
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+}
+
+.referral-section__copy-sheet {
+ position: absolute;
+ width: 9px;
+ height: 9px;
+ border: 1.5px solid #ffffff;
+ border-radius: 2px;
+ box-sizing: border-box;
+}
+
+.referral-section__copy-sheet--back {
+ right: 0;
+ bottom: 0;
+ opacity: 0.85;
+}
+
+.referral-section__copy-sheet--front {
+ left: 0;
+ top: 0;
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+.referral-section__copy-text {
+ display: block;
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-inverse);
+ line-height: 1.4;
+ white-space: nowrap;
+}
+
+.referral-section__stats {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.referral-section__stat {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 2px;
+}
+
+.referral-section__stat-num {
+ display: block;
+ font-size: var(--font-size-lg, 18px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ line-height: 1.2;
+}
+
+.referral-section__stat-num--orange {
+ color: var(--accent-orange);
+}
+
+.referral-section__stat-num--green {
+ color: var(--success-green);
+}
+
+.referral-section__stat-num--amber {
+ color: #f39c12;
+}
+
+.referral-section__stat-label {
+ display: block;
+ font-size: var(--font-size-xs, 11px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ line-height: 1.4;
+ white-space: nowrap;
+}
+
+.referral-section__stat-divider {
+ width: 1px;
+ height: 28px;
+ flex-shrink: 0;
+ background-color: var(--border-light);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-settings.css b/gym-manage-uniapp/common/style/memberInfo/member-info-settings.css
new file mode 100644
index 0000000..f09a2e3
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-settings.css
@@ -0,0 +1,166 @@
+.settings-section {
+ width: 100%;
+ box-sizing: border-box;
+ --bg-white: #ffffff;
+ --text-dark: #1e2a3a;
+ --text-light: #8a99b4;
+ --error-red: #e74c3c;
+ --success-green: #2ecc71;
+ --border-light: #e9edf2;
+ --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ --font-size-xs: 11px;
+ --font-size-base: 14px;
+ --font-size-md: 16px;
+ --font-weight-medium: 500;
+ --font-weight-bold: 700;
+}
+
+.settings-section__inner {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ width: 100%;
+}
+
+.settings-section__title {
+ display: block;
+ font-size: 16px;
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: #1e2a3a;
+ line-height: 1.4;
+}
+
+.settings-section__list {
+ width: 100%;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: #ffffff;
+ overflow: hidden;
+}
+
+.settings-section__list-inner {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+}
+
+.settings-section__item {
+ width: 100%;
+ min-height: 52px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ background-color: #ffffff;
+}
+
+.settings-section__item--tall {
+ min-height: 60px;
+}
+
+.settings-section__item-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 16px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.settings-section__item-icon-wrap {
+ width: 32px;
+ height: 32px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 8px;
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.settings-section__item-icon-wrap--blue {
+ background-color: rgba(235, 243, 250, 1);
+}
+
+.settings-section__item-icon-wrap--green {
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.settings-section__item-icon-wrap--red {
+ background-color: rgba(255, 236, 236, 1);
+}
+
+.settings-section__item-icon-wrap-inner {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+}
+
+.settings-section__item-icon {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.settings-section__item-label {
+ flex: 1;
+ min-width: 0;
+ display: block;
+ font-size: 14px;
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: #1e2a3a;
+ line-height: 1.4;
+}
+
+.settings-section__item-label--danger {
+ color: #e74c3c;
+}
+
+.settings-section__item-texts {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 2px;
+}
+
+.settings-section__item-title {
+ display: block;
+ font-size: 14px;
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: #1e2a3a;
+ line-height: 1.4;
+}
+
+.settings-section__item-desc {
+ display: block;
+ font-size: 11px;
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: #2ecc71;
+ line-height: 1.3;
+}
+
+.settings-section__item-arrow {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.settings-section__item-divider {
+ width: 100%;
+ height: 1px;
+ background-color: #e9edf2;
+}
+
+.mi-tap-row--hover {
+ opacity: 0.72;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-status-bar.css b/gym-manage-uniapp/common/style/memberInfo/member-info-status-bar.css
new file mode 100644
index 0000000..f66707c
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-status-bar.css
@@ -0,0 +1,48 @@
+.status-bar {
+ width: 100%;
+ height: 62px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ background-color: var(--primary-dark);
+}
+
+.status-bar__inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0px 20px 0px 20px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.status-bar__time {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+
+.status-bar__icons {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-sub-nav.css b/gym-manage-uniapp/common/style/memberInfo/member-info-sub-nav.css
new file mode 100644
index 0000000..df33a3d
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-sub-nav.css
@@ -0,0 +1,123 @@
+.sub-nav {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ box-sizing: border-box;
+}
+
+.sub-nav__toolbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ width: 100%;
+ background-color: #ffffff;
+ box-sizing: border-box;
+ border-bottom: 1px solid #e9edf2;
+}
+
+.sub-nav__spacer {
+ width: 100%;
+ flex-shrink: 0;
+}
+
+.sub-nav__nav {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 44px;
+ padding-left: 12px;
+ padding-right: 12px;
+ box-sizing: border-box;
+ width: 100%;
+}
+
+.sub-nav__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ border-radius: 8px;
+ background-color: #f9fafe;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 2;
+}
+
+.sub-nav__back-icon {
+ width: 20px;
+ height: 20px;
+ max-width: 20px;
+ max-height: 20px;
+ display: block;
+ flex-shrink: 0;
+}
+
+.sub-nav__title {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ max-width: 50%;
+ text-align: center;
+ font-size: 16px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ font-weight: 600;
+ color: #1e2a3a;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ pointer-events: none;
+ z-index: 1;
+ line-height: 1.2;
+}
+
+.sub-nav__right {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ margin-left: auto;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 2;
+}
+
+.sub-nav__action {
+ flex-shrink: 0;
+ margin-right: 4px;
+}
+
+.sub-nav__action-text {
+ font-size: 14px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ font-weight: 500;
+ color: #ff6b35;
+ white-space: nowrap;
+}
+
+.sub-nav__action--button {
+ padding: 6px 14px;
+ border-radius: 8px;
+ background-color: #ff6b35;
+}
+
+.sub-nav__action--button .sub-nav__action-text {
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.sub-nav__capsule {
+ flex-shrink: 0;
+ min-width: 0;
+}
+
+.sub-nav__capsule--h5 {
+ width: 0 !important;
+ min-width: 0 !important;
+ overflow: hidden;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/member-info-tap.css b/gym-manage-uniapp/common/style/memberInfo/member-info-tap.css
new file mode 100644
index 0000000..7c8f613
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/member-info-tap.css
@@ -0,0 +1,36 @@
+/* 小程序点击反馈(配合 hover-class 使用) */
+.mi-tap--hover,
+.mi-tap--scale,
+.mi-tap-card--hover,
+.mi-tap-btn--hover,
+.mi-tap-tab--hover,
+.mi-tap-save--hover {
+ transition: opacity 0.15s ease, transform 0.15s ease;
+}
+
+.mi-tap--scale {
+ transform: scale(0.97);
+}
+
+.mi-tap-card--hover {
+ opacity: 0.92;
+ transform: scale(0.995);
+}
+
+.mi-tap-btn--hover {
+ opacity: 0.85;
+ transform: scale(0.98);
+}
+
+.mi-tap-tab--hover {
+ opacity: 0.75;
+}
+
+.mi-tap-row--hover {
+ background-color: rgba(249, 250, 254, 0.95);
+}
+
+.mi-tap-save--hover {
+ opacity: 0.88;
+ transform: scale(0.99);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/body-test-common.css b/gym-manage-uniapp/common/style/memberInfo/pages/body-test-common.css
new file mode 100644
index 0000000..4394c53
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/body-test-common.css
@@ -0,0 +1,860 @@
+/* 智能体测模块 - 公共样式(基于 base.css 变量) */
+@import '@/common/style/memberInfo/member-info-gradient-cards.css';
+
+.bt-page {
+ width: 100%;
+ min-height: 100%;
+ box-sizing: border-box;
+ overflow-x: hidden;
+ background-color: var(--bg-light, #F9FAFE);
+}
+
+.bt-page__body {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-sm, 12px);
+ padding: var(--spacing-md, 16px) var(--spacing-md, 16px) 40px;
+ box-sizing: border-box;
+}
+
+.bt-card {
+ width: 100%;
+ padding: 16px;
+ border-radius: var(--radius-md, 20px);
+ background-color: var(--bg-white, #fff);
+ box-shadow: var(--shadow-sm, 0 8px 20px rgba(0, 0, 0, 0.03));
+ box-sizing: border-box;
+}
+
+.bt-card__title {
+ font-size: var(--font-size-md, 16px);
+ font-weight: 700;
+ color: var(--text-dark, #1E2A3A);
+ margin-bottom: 12px;
+ display: block;
+}
+
+.bt-card__desc {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ line-height: 1.5;
+ display: block;
+}
+
+.bt-hero {
+ padding: 20px;
+ border-radius: var(--radius-sm, 12px);
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.bt-hero__top {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ justify-content: space-between;
+}
+
+.bt-hero__label {
+ font-size: var(--font-size-sm, 12px);
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.bt-hero__badge {
+ padding: 4px 10px;
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.18);
+}
+
+.bt-hero__badge-text {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-inverse, #fff);
+}
+
+.bt-hero__score-row {
+ display: flex;
+ flex-direction: row;
+ align-items: baseline;
+ gap: 8px;
+}
+
+.bt-hero__score {
+ font-size: 48px;
+ font-weight: 800;
+ color: var(--text-inverse, #fff);
+ line-height: 1;
+}
+
+.bt-hero__grade {
+ font-size: var(--font-size-xl, 20px);
+ font-weight: 700;
+ color: var(--accent-orange-light, #FF8C5A);
+}
+
+.bt-hero__meta {
+ font-size: var(--font-size-sm, 12px);
+ color: rgba(255, 212, 184, 1);
+}
+
+.bt-hero__actions {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ margin-top: 4px;
+}
+
+.bt-btn {
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ padding: 12px 16px;
+ border-radius: var(--radius-full, 999px);
+ box-sizing: border-box;
+}
+
+.bt-btn--primary {
+ box-shadow: var(--shadow-orange-glow, 0 4px 12px rgba(255, 107, 53, 0.25));
+}
+
+.bt-btn--ghost {
+ background: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+}
+
+.bt-btn--outline {
+ background: var(--bg-white, #fff);
+ border: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-btn__text {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+}
+
+.bt-btn--primary .bt-btn__text,
+.bt-btn--ghost .bt-btn__text {
+ color: var(--text-inverse, #fff);
+}
+
+.bt-btn--outline .bt-btn__text {
+ color: var(--primary-dark, #0B2B4B);
+}
+
+.bt-btn__icon {
+ width: 16px;
+ height: 16px;
+}
+
+.bt-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 8px;
+}
+
+.bt-grid__item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 6px;
+ padding: 12px 4px;
+ border-radius: var(--radius-sm, 12px);
+ background: var(--bg-gray, #F2F5F9);
+}
+
+.bt-grid__icon {
+ width: 22px;
+ height: 22px;
+}
+
+.bt-grid__label {
+ font-size: 11px;
+ color: var(--text-muted, #5E6F8D);
+ text-align: center;
+}
+
+.bt-device {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+}
+
+.bt-device__icon-wrap {
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
+ background: var(--bg-gray, #F2F5F9);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.bt-device__icon {
+ width: 22px;
+ height: 22px;
+}
+
+.bt-device__info {
+ flex: 1;
+ min-width: 0;
+}
+
+.bt-device__name {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.bt-device__status {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ display: block;
+ margin-top: 2px;
+}
+
+.bt-device__status--on {
+ color: var(--success-green, #2ECC71);
+}
+
+.bt-device__dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--text-light, #8A99B4);
+ flex-shrink: 0;
+}
+
+.bt-device__dot--on {
+ background: var(--success-green, #2ECC71);
+}
+
+.bt-metrics {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+}
+
+.bt-metric {
+ padding: 14px;
+ border-radius: var(--radius-sm, 12px);
+ background: var(--bg-gray, #F2F5F9);
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.bt-metric__value {
+ font-size: var(--font-size-xl, 20px);
+ font-weight: 700;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.bt-metric__label {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-metric__change {
+ font-size: 11px;
+ font-weight: 600;
+}
+
+.bt-metric__change--down {
+ color: var(--success-green, #2ECC71);
+}
+
+.bt-metric__change--up {
+ color: var(--warning-amber, #F39C12);
+}
+
+.bt-steps {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.bt-step {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ padding: 12px 0;
+}
+
+.bt-step__num {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: var(--primary-dark, #0B2B4B);
+ color: var(--text-inverse, #fff);
+ font-size: var(--font-size-sm, 12px);
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.bt-step__content {
+ flex: 1;
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-step:last-child .bt-step__content {
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+.bt-step__title {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.bt-step__desc {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ margin-top: 4px;
+ display: block;
+ line-height: 1.5;
+}
+
+.bt-measure {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 24px 16px;
+ gap: 16px;
+}
+
+.bt-measure__ring-wrap {
+ position: relative;
+ width: 180px;
+ height: 180px;
+}
+
+.bt-measure__ring-bg {
+ width: 180px;
+ height: 180px;
+ border-radius: 50%;
+ border: 10px solid var(--border-light, #E9EDF2);
+ box-sizing: border-box;
+}
+
+.bt-measure__ring-fill {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 180px;
+ height: 180px;
+ border-radius: 50%;
+ border: 10px solid transparent;
+ border-top-color: var(--accent-orange, #FF6B35);
+ border-right-color: var(--accent-orange, #FF6B35);
+ box-sizing: border-box;
+ transform: rotate(-90deg);
+ transition: transform 0.3s ease;
+}
+
+.bt-measure__center {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+}
+
+.bt-measure__percent {
+ font-size: 36px;
+ font-weight: 800;
+ color: var(--primary-dark, #0B2B4B);
+ display: block;
+}
+
+.bt-measure__hint {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-measure__live {
+ width: 100%;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+}
+
+.bt-measure__live-item {
+ padding: 12px;
+ border-radius: var(--radius-sm, 12px);
+ background: var(--bg-gray, #F2F5F9);
+ text-align: center;
+}
+
+.bt-measure__live-value {
+ font-size: var(--font-size-lg, 18px);
+ font-weight: 700;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.bt-measure__live-label {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ margin-top: 2px;
+ display: block;
+}
+
+.bt-score-card {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 16px;
+ padding: 20px;
+ border-radius: var(--radius-sm, 12px);
+}
+
+.bt-score-card__circle {
+ width: 72px;
+ height: 72px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.15);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.bt-score-card__num {
+ font-size: 28px;
+ font-weight: 800;
+ color: var(--text-inverse, #fff);
+ line-height: 1;
+}
+
+.bt-score-card__grade {
+ font-size: 11px;
+ color: var(--accent-orange-light, #FF8C5A);
+ font-weight: 600;
+}
+
+.bt-score-card__info {
+ flex: 1;
+}
+
+.bt-score-card__title {
+ font-size: var(--font-size-md, 16px);
+ font-weight: 700;
+ color: var(--text-inverse, #fff);
+ display: block;
+}
+
+.bt-score-card__date {
+ font-size: var(--font-size-sm, 12px);
+ color: rgba(255, 255, 255, 0.7);
+ margin-top: 4px;
+ display: block;
+}
+
+.bt-body-map {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 0;
+}
+
+.bt-body-map__figure {
+ width: 120px;
+ height: 200px;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.bt-body-map__head {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: var(--primary-light, #2C6288);
+ opacity: 0.6;
+}
+
+.bt-body-map__torso {
+ width: 56px;
+ height: 70px;
+ border-radius: 12px;
+ background: var(--primary-deep, #1A4A6F);
+ margin-top: 4px;
+}
+
+.bt-body-map__limbs {
+ display: flex;
+ flex-direction: row;
+ gap: 48px;
+ margin-top: -60px;
+}
+
+.bt-body-map__arm {
+ width: 16px;
+ height: 50px;
+ border-radius: 8px;
+ background: var(--primary-light, #2C6288);
+ opacity: 0.7;
+}
+
+.bt-body-map__legs {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ margin-top: 4px;
+}
+
+.bt-body-map__leg {
+ width: 22px;
+ height: 60px;
+ border-radius: 8px;
+ background: var(--primary-light, #2C6288);
+ opacity: 0.7;
+}
+
+.bt-body-map__segments {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.bt-body-map__seg {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 12px;
+ border-radius: 8px;
+ background: var(--bg-gray, #F2F5F9);
+}
+
+.bt-body-map__seg--high {
+ border-left: 3px solid var(--accent-orange, #FF6B35);
+}
+
+.bt-body-map__seg--low {
+ border-left: 3px solid var(--info-blue, #3498DB);
+}
+
+.bt-body-map__seg-name {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-dark, #1E2A3A);
+}
+
+.bt-body-map__seg-val {
+ font-size: var(--font-size-sm, 12px);
+ font-weight: 600;
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-advice-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.bt-advice-item {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: flex-start;
+}
+
+.bt-advice-item__dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--accent-orange, #FF6B35);
+ margin-top: 6px;
+ flex-shrink: 0;
+}
+
+.bt-advice-item__text {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ line-height: 1.5;
+ flex: 1;
+}
+
+.bt-course {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-course:last-child {
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+.bt-course__banner {
+ width: 72px;
+ height: 72px;
+ border-radius: 12px;
+ flex-shrink: 0;
+}
+
+.bt-course__info {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.bt-course__tag {
+ font-size: 10px;
+ color: var(--accent-orange, #FF6B35);
+ font-weight: 600;
+}
+
+.bt-course__title {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.bt-course__meta {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-history-item {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+ padding: 14px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-history-item:last-child {
+ border-bottom: none;
+}
+
+.bt-history-item__date {
+ width: 52px;
+ text-align: center;
+ flex-shrink: 0;
+}
+
+.bt-history-item__day {
+ font-size: var(--font-size-xl, 20px);
+ font-weight: 700;
+ color: var(--primary-dark, #0B2B4B);
+ display: block;
+}
+
+.bt-history-item__month {
+ font-size: 10px;
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-history-item__info {
+ flex: 1;
+ min-width: 0;
+}
+
+.bt-history-item__score-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+}
+
+.bt-history-item__grade {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.bt-history-item__status {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-history-item__metrics {
+ font-size: 11px;
+ color: var(--text-light, #8A99B4);
+ margin-top: 2px;
+}
+
+.bt-history-item__arrow {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+}
+
+.bt-compare-header {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+}
+
+.bt-compare-picker {
+ padding: 12px;
+ border-radius: var(--radius-sm, 12px);
+ background: var(--bg-gray, #F2F5F9);
+ text-align: center;
+}
+
+.bt-compare-picker__label {
+ font-size: 10px;
+ color: var(--text-light, #8A99B4);
+ display: block;
+}
+
+.bt-compare-picker__date {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ margin-top: 4px;
+ display: block;
+}
+
+.bt-compare-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-compare-row:last-child {
+ border-bottom: none;
+}
+
+.bt-compare-row__label {
+ flex: 1;
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bt-compare-row__val {
+ width: 60px;
+ text-align: center;
+ font-size: var(--font-size-sm, 12px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.bt-compare-row__diff {
+ width: 56px;
+ text-align: right;
+ font-size: 11px;
+ font-weight: 600;
+}
+
+.bt-setting {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 14px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-setting:last-child {
+ border-bottom: none;
+}
+
+.bt-setting__label {
+ font-size: var(--font-size-base, 14px);
+ color: var(--text-dark, #1E2A3A);
+}
+
+.bt-setting__desc {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-light, #8A99B4);
+ margin-top: 2px;
+}
+
+.bt-footer-actions {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ padding: 16px;
+}
+
+.bt-empty {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 16px;
+}
+
+.bt-empty__text {
+ font-size: var(--font-size-base, 14px);
+ color: var(--text-light, #8A99B4);
+}
+
+.bt-tabs {
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacing-sm, 8px);
+ padding: 0 var(--spacing-md, 16px) var(--spacing-sm, 8px);
+ overflow-x: auto;
+ box-sizing: border-box;
+}
+
+.bt-tab {
+ padding: 8px 14px;
+ border-radius: var(--radius-full, 999px);
+ background: var(--bg-white, #fff);
+ border: 1px solid var(--border-light, #E9EDF2);
+ flex-shrink: 0;
+}
+
+.bt-tab--active {
+ background: var(--primary-dark, #0B2B4B);
+ border-color: var(--primary-dark, #0B2B4B);
+}
+
+.bt-tab__text {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ white-space: nowrap;
+}
+
+.bt-tab--active .bt-tab__text {
+ color: var(--text-inverse, #fff);
+ font-weight: 600;
+}
+
+.bt-trend-link {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 12px 0 0;
+ margin-top: 8px;
+ border-top: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-trend-link__text {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--primary-deep, #1A4A6F);
+ font-weight: 600;
+}
+
+.bt-trend-link__arrow {
+ width: 14px;
+ height: 14px;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/booking-page.css b/gym-manage-uniapp/common/style/memberInfo/pages/booking-page.css
new file mode 100644
index 0000000..774d479
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/booking-page.css
@@ -0,0 +1,272 @@
+.booking-page {
+ width: 100%;
+ min-height: 100%;
+ box-sizing: border-box;
+ overflow-x: hidden;
+ background-color: var(--bg-light, #F9FAFE);
+}
+
+/* Tab 栏 */
+.booking-page__tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: stretch;
+ width: 100%;
+ height: 44px;
+ background-color: var(--bg-white, #ffffff);
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+ box-sizing: border-box;
+}
+
+.booking-page__tab {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 44px;
+ padding: 0 16px;
+ box-sizing: border-box;
+}
+
+.booking-page__tab-text {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light, #8A99B4);
+ white-space: nowrap;
+}
+
+.booking-page__tab-text--active {
+ font-weight: 700;
+ color: var(--accent-orange, #FF6B35);
+}
+
+.booking-page__tab-indicator {
+ position: absolute;
+ left: 16px;
+ right: 16px;
+ bottom: 0;
+ height: 2px;
+ border-radius: 2px;
+ background-color: var(--accent-orange, #FF6B35);
+}
+
+/* 内容区 */
+.booking-page__body {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 16px 16px 40px;
+ box-sizing: border-box;
+}
+
+/* 提醒横幅 */
+.booking-page__alert {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+ width: 100%;
+ padding: 10px 12px;
+ border-radius: 10px;
+ border: 1px solid rgba(255, 204, 170, 1);
+ background-color: rgba(255, 243, 238, 1);
+ box-sizing: border-box;
+}
+
+.booking-page__alert-icon {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.booking-page__alert-text {
+ flex: 1;
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--accent-orange, #FF6B35);
+ line-height: 1.4;
+}
+
+/* 预约卡片 */
+.bk-card {
+ width: 100%;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: var(--bg-white, #ffffff);
+ overflow: hidden;
+ box-sizing: border-box;
+ transition: opacity 0.15s ease, transform 0.15s ease;
+}
+
+.bk-card__banner {
+ width: 100%;
+ height: 80px;
+ display: block;
+ border-radius: 14px 14px 0 0;
+}
+
+.bk-card__content {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ padding: 12px 14px;
+ box-sizing: border-box;
+}
+
+.bk-card__header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ gap: 8px;
+}
+
+.bk-card__title {
+ flex: 1;
+ min-width: 0;
+ font-size: var(--font-size-md, 16px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark, #1E2A3A);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.bk-card__status {
+ flex-shrink: 0;
+ padding: 4px 10px;
+ border-radius: 6px;
+ box-sizing: border-box;
+}
+
+.bk-card__status--booked {
+ background-color: var(--success-green, #2ECC71);
+}
+
+.bk-card__status--pending {
+ background-color: rgba(255, 243, 238, 1);
+ border: 1px solid rgba(212, 166, 74, 1);
+}
+
+.bk-card__status--completed {
+ background-color: var(--bg-light, #F9FAFE);
+ border: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bk-card__status--cancelled {
+ background-color: var(--bg-light, #F9FAFE);
+ border: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bk-card__status-text {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 500;
+ white-space: nowrap;
+}
+
+.bk-card__status-text--booked {
+ color: var(--text-inverse, #ffffff);
+}
+
+.bk-card__status-text--pending {
+ color: var(--accent-orange, #FF6B35);
+}
+
+.bk-card__status-text--completed {
+ color: var(--text-muted, #5E6F8D);
+}
+
+.bk-card__status-text--cancelled {
+ color: var(--text-light, #8A99B4);
+}
+
+/* 时间与教练信息(分两行) */
+.bk-card__meta {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ width: 100%;
+}
+
+.bk-card__meta-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+}
+
+.bk-card__meta-icon {
+ width: 13px;
+ height: 13px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.bk-card__meta-text {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted, #5E6F8D);
+ white-space: nowrap;
+}
+
+/* 底部操作行 */
+.bk-card__footer {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ gap: 8px;
+}
+
+.bk-card__footer-info {
+ flex: 1;
+ min-width: 0;
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light, #8A99B4);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.bk-card__cancel {
+ flex-shrink: 0;
+ padding: 6px 14px;
+ border-radius: 8px;
+ border: 1px solid var(--border-light, #E9EDF2);
+ background-color: var(--bg-white, #ffffff);
+ box-sizing: border-box;
+}
+
+.bk-card__cancel-text {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--error-red, #E74C3C);
+ white-space: nowrap;
+}
+
+/* 空状态 */
+.booking-page__empty {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 48px 16px;
+}
+
+.booking-page__empty-text {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light, #8A99B4);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/booking-pixso.css b/gym-manage-uniapp/common/style/memberInfo/pages/booking-pixso.css
new file mode 100644
index 0000000..485f762
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/booking-pixso.css
@@ -0,0 +1,774 @@
+.Pixso-frame-2_965 {
+ width: 100%;
+ height: 62px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_965 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0px 20px 0px 20px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_966 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_967 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_968 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_968 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.stroke-wrapper-2_968 {
+ position: relative;
+ width: 100%;
+ height: 52px;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_968 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 0px 0px 0px 0px;
+ pointer-events: none;
+ border-width: 0px 0px 1px 0px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--border-light);
+}
+.Pixso-frame-2_969 {
+ width: 32px;
+ height: 32px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.frame-content-2_969 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_970 {width: 20px;
+ height: 20px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_972 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ text-align: center;
+ color: var(--primary-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_973 {
+ width: 32px;
+ height: 32px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: rgba(249, 250, 254, 0);
+}
+.Pixso-frame-2_974 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_974 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.stroke-wrapper-2_974 {
+ position: relative;
+ width: 100%;
+ height: 44px;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_974 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 0px 0px 0px 0px;
+ pointer-events: none;
+ border-width: 0px 0px 1px 0px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--border-light);
+}
+.Pixso-frame-2_975 {
+ width: 74px;
+ height: 44px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+}
+.frame-content-2_975 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_976 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_977 {
+ width: 74px;
+ height: 2px;
+ position: absolute;
+ left: 0px;
+ top: 42px;
+ border-radius: 2px 2px 2px 2px;
+ background-color: var(--accent-orange);
+}
+.Pixso-frame-2_978 {
+ width: auto;
+ height: 44px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+}
+.frame-content-2_978 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_979 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_980 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.frame-content-2_980 {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 16px 16px 40px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_981 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ border-radius: 10px 10px 10px 10px;
+ background-color: rgba(255, 243, 238, 1);
+}
+.frame-content-2_981 {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: center;
+ padding: 10px 12px 10px 12px;
+ width: 100%;
+ position: relative;
+}
+.stroke-wrapper-2_981 {
+ position: relative;
+ width: 100%;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_981 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 10px 10px 10px 10px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(255, 204, 170, 1);
+}
+.Pixso-vector-2_982 {width: 14px;
+ height: 14px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_985 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--accent-orange);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_986 {
+ width: 100%;
+ height: 195px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_986 {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-vector-2_987 {width: 100%;
+ height: 80px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_988 {
+ width: 358px;
+ height: 80px;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ border-radius: 14px 14px 0px 0px;
+ background-color: rgba(0, 0, 0, 0.1882352977991104);
+}
+.Pixso-frame-2_989 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.frame-content-2_989 {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ align-items: flex-start;
+ padding: 12px 14px 12px 14px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_990 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+.frame-content-2_990 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_991 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_992 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 4px 10px 4px 10px;
+ border-radius: 6px 6px 6px 6px;
+ background-color: var(--success-green);
+}
+.Pixso-paragraph-2_993 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_994 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_994 {
+ display: flex;
+ flex-direction: row;
+ gap: 14px;
+ align-items: center;
+ width: 100%;
+ position: relative;
+}
+.Pixso-vector-2_995 {width: 13px;
+ height: 13px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_998 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-vector-2_999 {width: 13px;
+ height: 13px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_1002 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1003 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+.frame-content-2_1003 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_1004 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1005 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 6px 14px 6px 14px;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-white);
+}
+.stroke-wrapper-2_1005 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_1005 {
+ position: absolute;
+ inset: -1px -1px -1px -1px;
+ border-radius: 9px 9px 9px 9px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--border-light);
+}
+.Pixso-paragraph-2_1006 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--error-red);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1007 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_1007 {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-vector-2_1008 {width: 100%;
+ height: 80px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_1009 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.frame-content-2_1009 {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ align-items: flex-start;
+ padding: 12px 14px 12px 14px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_1010 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+.frame-content-2_1010 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_1011 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1012 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 4px 10px 4px 10px;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(255, 243, 238, 1);
+}
+.stroke-wrapper-2_1012 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_1012 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 6px 6px 6px 6px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(212, 166, 74, 1);
+}
+.Pixso-paragraph-2_1013 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1014 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_1014 {
+ display: flex;
+ flex-direction: row;
+ gap: 14px;
+ align-items: center;
+ width: 100%;
+ position: relative;
+}
+.Pixso-vector-2_1015 {width: 13px;
+ height: 13px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_1018 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-vector-2_1019 {width: 13px;
+ height: 13px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_1022 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1023 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+.frame-content-2_1023 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_1024 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_1025 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 6px 14px 6px 14px;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-white);
+}
+.stroke-wrapper-2_1025 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_1025 {
+ position: absolute;
+ inset: -1px -1px -1px -1px;
+ border-radius: 9px 9px 9px 9px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--border-light);
+}
+.Pixso-paragraph-2_1026 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--error-red);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/course-list-page.css b/gym-manage-uniapp/common/style/memberInfo/pages/course-list-page.css
new file mode 100644
index 0000000..c077dd9
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/course-list-page.css
@@ -0,0 +1,264 @@
+.mi-course-list__filters {
+ padding: 0 var(--spacing-md, 16px) var(--spacing-sm, 8px);
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-sm, 10px);
+ box-sizing: border-box;
+}
+
+.mi-course-list__date-bar {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+}
+
+.mi-course-list__mode {
+ padding: 8px 12px;
+ border-radius: 999px;
+ background: var(--primary-dark, #0B2B4B);
+ flex-shrink: 0;
+}
+
+.mi-course-list__mode-text {
+ font-size: 12px;
+ color: #fff;
+ font-weight: 600;
+}
+
+.mi-course-list__dates {
+ flex: 1;
+ white-space: nowrap;
+}
+
+.mi-course-list__date {
+ display: inline-flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 8px 12px;
+ margin-right: 6px;
+ border-radius: 12px;
+ background: var(--bg-white, #fff);
+ border: 1px solid var(--border-light, #E9EDF2);
+}
+
+.mi-course-list__date--active {
+ background: rgba(255, 107, 53, 0.12);
+ border-color: var(--accent-orange, #FF6B35);
+}
+
+.mi-course-list__date-week {
+ font-size: 10px;
+ color: var(--text-muted, #5E6F8D);
+}
+
+.mi-course-list__date-day {
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.mi-course-list__chips {
+ white-space: nowrap;
+}
+
+.mi-course-list__row {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+}
+
+.mi-course-list__picker {
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 12px;
+ border-radius: 12px;
+ background: var(--bg-white, #fff);
+ border: 1px solid var(--border-light, #E9EDF2);
+ font-size: 13px;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.mi-course-list__arrow {
+ width: 14px;
+ height: 14px;
+ transform: rotate(90deg);
+}
+
+.mi-course-card {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ padding: 12px;
+ border-radius: 18px;
+ background: var(--bg-white, #fff);
+ box-shadow: var(--shadow-sm);
+ margin-bottom: 12px;
+}
+
+.mi-course-card__banner {
+ width: 96px;
+ height: 120px;
+ border-radius: 12px;
+ flex-shrink: 0;
+}
+
+.mi-course-card__body {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.mi-course-card__head {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 6px;
+}
+
+.mi-course-card__title {
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--text-dark, #1E2A3A);
+ flex: 1;
+}
+
+.mi-course-card__type {
+ padding: 2px 8px;
+ border-radius: 6px;
+ flex-shrink: 0;
+}
+
+.mi-course-card__type--group {
+ background: rgba(255, 107, 53, 0.12);
+}
+
+.mi-course-card__type--private {
+ background: rgba(11, 43, 75, 0.1);
+}
+
+.mi-course-card__type text {
+ font-size: 10px;
+ font-weight: 600;
+ color: var(--text-muted, #5E6F8D);
+}
+
+.mi-course-card__coach {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+ font-size: 12px;
+ color: var(--text-muted, #5E6F8D);
+}
+
+.mi-course-card__avatar {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+}
+
+.mi-course-card__meta {
+ font-size: 11px;
+ color: var(--text-light, #8A99B4);
+}
+
+.mi-course-card__capacity {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+}
+
+.mi-course-card__bar {
+ flex: 1;
+ height: 6px;
+ border-radius: 3px;
+ background: var(--bg-gray, #F2F5F9);
+ overflow: hidden;
+}
+
+.mi-course-card__bar-fill {
+ height: 100%;
+ background: var(--gradient-orange);
+ border-radius: 3px;
+}
+
+.mi-course-card__cap-text {
+ font-size: 10px;
+ color: var(--text-muted, #5E6F8D);
+ flex-shrink: 0;
+}
+
+.mi-course-card__scarcity {
+ font-size: 10px;
+ color: var(--accent-orange, #FF6B35);
+ font-weight: 600;
+ flex-shrink: 0;
+}
+
+.mi-course-card__footer {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 4px;
+}
+
+.mi-course-card__price {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--primary-deep, #1A4A6F);
+}
+
+.mi-course-card__btn {
+ padding: 6px 16px;
+ border-radius: 999px;
+ background: var(--gradient-orange);
+}
+
+.mi-course-card__btn text {
+ font-size: 12px;
+ font-weight: 600;
+ color: #fff;
+}
+
+.mi-course-card__btn--disabled {
+ background: var(--bg-gray, #F2F5F9);
+}
+
+.mi-course-card__btn--disabled text {
+ color: var(--text-light, #8A99B4);
+}
+
+.mi-course-list__fab {
+ position: fixed;
+ right: 16px;
+ bottom: 32px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+ padding: 12px 16px;
+ border-radius: 999px;
+ background: var(--primary-dark, #0B2B4B);
+ box-shadow: var(--shadow-md);
+ z-index: 10;
+}
+
+.mi-course-list__fab-icon {
+ width: 18px;
+ height: 18px;
+}
+
+.mi-course-list__fab-text {
+ font-size: 13px;
+ font-weight: 600;
+ color: #fff;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/member-card-page.css b/gym-manage-uniapp/common/style/memberInfo/pages/member-card-page.css
new file mode 100644
index 0000000..7f8275f
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/member-card-page.css
@@ -0,0 +1,340 @@
+.member-card-page {
+ width: 100%;
+ min-height: 100%;
+ box-sizing: border-box;
+ overflow-x: hidden;
+ background-color: var(--bg-light, #F9FAFE);
+}
+
+.member-card-page__body {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 16px 16px 40px;
+ box-sizing: border-box;
+}
+
+/* 会员卡 */
+.mc-hero {
+ width: 100%;
+ min-height: 160px;
+ padding: 20px 20px 16px;
+ border-radius: 18px;
+ box-shadow: 0 10px 20px rgba(11, 43, 75, 0.31);
+ background: linear-gradient(135deg, #0B2B4B 0%, #1A4A6F 100%);
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.mc-hero__top {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ justify-content: space-between;
+ width: 100%;
+ gap: 8px;
+}
+
+.mc-hero__title-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+ flex: 1;
+ min-width: 0;
+}
+
+.mc-hero__crown {
+ width: 18px;
+ height: 18px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.mc-hero__name {
+ font-size: var(--font-size-md, 16px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse, #ffffff);
+ white-space: nowrap;
+}
+
+.mc-hero__badge {
+ flex-shrink: 0;
+ padding: 4px 10px;
+ border-radius: 10px;
+ background-color: rgba(255, 255, 255, 0.19);
+}
+
+.mc-hero__badge-text {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse, #ffffff);
+ white-space: nowrap;
+}
+
+.mc-hero__validity {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ display: block;
+}
+
+.mc-hero__bottom {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ justify-content: space-between;
+ width: 100%;
+ margin-top: 4px;
+}
+
+.mc-hero__days {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ gap: 4px;
+}
+
+.mc-hero__days-num {
+ font-size: var(--font-size-5xl, 32px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse, #ffffff);
+ line-height: 1;
+}
+
+.mc-hero__days-unit {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ padding-top: 8px;
+}
+
+.mc-hero__renew {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 4px;
+ padding: 8px 18px;
+ border-radius: 16px;
+ background-color: var(--bg-white, #ffffff);
+ flex-shrink: 0;
+}
+
+.mc-hero__renew-icon {
+ width: 13px;
+ height: 13px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.mc-hero__renew-text {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--accent-orange, #FF6B35);
+ white-space: nowrap;
+}
+
+/* 使用记录 */
+.mc-records {
+ width: 100%;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: var(--bg-white, #ffffff);
+ overflow: hidden;
+ box-sizing: border-box;
+}
+
+.mc-records__header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 16px;
+ height: 48px;
+ box-sizing: border-box;
+}
+
+.mc-records__title {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.mc-records__tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 2px;
+ padding: 2px;
+ border-radius: 8px;
+ background-color: var(--bg-light, #F9FAFE);
+ flex-shrink: 0;
+}
+
+.mc-records__tab {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 24px;
+ padding: 0 10px;
+ border-radius: 6px;
+ box-sizing: border-box;
+}
+
+.mc-records__tab--active {
+ background-color: var(--bg-white, #ffffff);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
+}
+
+.mc-records__tab-text {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light, #8A99B4);
+ white-space: nowrap;
+}
+
+.mc-records__tab-text--active {
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.mc-records__divider {
+ width: 100%;
+ height: 1px;
+ background-color: var(--bg-light, #F9FAFE);
+}
+
+.mc-records__item-inner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 16px;
+ box-sizing: border-box;
+}
+
+.mc-records__icon-wrap {
+ width: 36px;
+ height: 36px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.mc-records__icon-wrap--orange {
+ background-color: rgba(255, 243, 238, 1);
+}
+
+.mc-records__icon-wrap--green {
+ background-color: rgba(240, 250, 245, 1);
+}
+
+.mc-records__icon {
+ width: 18px;
+ height: 18px;
+ display: block;
+ flex-shrink: 0;
+}
+
+.mc-records__info {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.mc-records__item-title {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.mc-records__item-time {
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light, #8A99B4);
+ display: block;
+}
+
+.mc-records__value {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 700;
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.mc-records__value--negative {
+ color: var(--error-red, #E74C3C);
+}
+
+.mc-records__value--positive {
+ color: var(--success-green, #2ECC71);
+}
+
+/* 使用规则 */
+.mc-rules {
+ width: 100%;
+ padding: 16px;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: var(--bg-white, #ffffff);
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.mc-rules__title {
+ font-size: var(--font-size-base, 14px);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.mc-rules__item {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ gap: 8px;
+}
+
+.mc-rules__bullet {
+ width: 6px;
+ height: 6px;
+ margin-top: 6px;
+ border-radius: 1px;
+ background-color: var(--accent-orange, #FF6B35);
+ flex-shrink: 0;
+}
+
+.mc-rules__text {
+ flex: 1;
+ font-size: var(--font-size-sm, 12px);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted, #5E6F8D);
+ line-height: 1.5;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/member-card-pixso.css b/gym-manage-uniapp/common/style/memberInfo/pages/member-card-pixso.css
new file mode 100644
index 0000000..8bd0ac8
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/member-card-pixso.css
@@ -0,0 +1,982 @@
+.Pixso-frame-2_878 {
+ width: 100%;
+ height: 62px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_878 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0px 20px 0px 20px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_879 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_880 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_881 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_881 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.stroke-wrapper-2_881 {
+ position: relative;
+ width: 100%;
+ height: 52px;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_881 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 0px 0px 0px 0px;
+ pointer-events: none;
+ border-width: 0px 0px 1px 0px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--border-light);
+}
+.Pixso-frame-2_882 {
+ width: 32px;
+ height: 32px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.frame-content-2_882 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_883 {width: 20px;
+ height: 20px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_885 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ text-align: center;
+ color: var(--primary-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_886 {
+ width: 32px;
+ height: 32px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: rgba(249, 250, 254, 0);
+}
+.Pixso-frame-2_887 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.frame-content-2_887 {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 16px 16px 40px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_888 {
+ width: 100%;
+ height: 160px;
+ overflow: hidden;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 18px 18px 18px 18px;
+ box-shadow: 0px 10px 20px 0px rgba(11, 43, 75, 0.3137254901960784);
+ background-position: center;
+ background-image: url("data:image/svg+xml;utf8,%3Csvg%20viewBox%3D'0%200%20358%20160'%20preserveAspectRatio%3D'none'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%0A%20%20%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3ClinearGradient%20id%3D'grad'%20gradientUnits%3D'objectBoundingBox'%20x1%3D'0'%20y1%3D'0.5'%20x2%3D'1'%20y2%3D'0.5'%20gradientTransform%3D'matrix(-0.7071%2C%200.7071%2C%20-0.7071%2C%20-0.7071%2C%201.2071%2C%200.5000)'%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cstop%20stop-color%3D'rgba(11%2C43%2C75%2C1)'%20offset%3D'0'%2F%3E%3Cstop%20stop-color%3D'rgba(26%2C74%2C111%2C1)'%20offset%3D'1'%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2FlinearGradient%3E%0A%20%20%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%20%20%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'url(%23grad)'%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E");
+}
+.frame-content-2_888 {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ padding: 20px 20px 16px 20px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-frame-2_889 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+.frame-content-2_889 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_890 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 6px;
+ align-items: center;
+}
+.Pixso-vector-2_891 {width: 18px;
+ height: 18px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_894 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_895 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 4px 10px 4px 10px;
+ border-radius: 10px 10px 10px 10px;
+ background-color: rgba(255, 255, 255, 0.1882352977991104);
+}
+.Pixso-paragraph-2_896 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_897 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_898 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+.frame-content-2_898 {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ justify-content: space-between;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_899 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: flex-start;
+}
+.Pixso-paragraph-2_900 {
+ font-size: var(--font-size-5xl);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_901 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: rgba(255, 212, 184, 1);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_902 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ padding: 8px 18px 8px 18px;
+ border-radius: 16px 16px 16px 16px;
+ background-color: var(--bg-white);
+}
+.Pixso-vector-2_903 {width: 13px;
+ height: 13px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_908 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_909 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_909 {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_910 {
+ width: 100%;
+ height: 48px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+.frame-content-2_910 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_911 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_912 {
+ width: auto;
+ height: 28px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.frame-content-2_912 {
+ display: flex;
+ flex-direction: row;
+ gap: 2px;
+ align-items: center;
+ padding: 2px 2px 2px 2px;
+ height: 100%;
+ position: relative;
+}
+.Pixso-frame-2_913 {
+ width: auto;
+ height: 24px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+ border-radius: 6px 6px 6px 6px;
+ box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.06274509803921569);
+ background-color: var(--bg-white);
+}
+.frame-content-2_913 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 10px 0px 10px;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_914 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_915 {
+ width: auto;
+ height: 24px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(249, 250, 254, 0);
+}
+.frame-content-2_915 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 10px 0px 10px;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_916 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_917 {
+ width: auto;
+ height: 24px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(249, 250, 254, 0);
+}
+.frame-content-2_917 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 10px 0px 10px;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_918 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_919 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_920 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_920 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 14px 16px 14px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_921 {
+ width: 36px;
+ height: 36px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 10px 10px 10px 10px;
+ background-color: rgba(255, 243, 238, 1);
+}
+.frame-content-2_921 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_922 {width: 18px;
+ height: 18px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_928 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.frame-content-2_928 {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_929 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_930 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ white-space: pre-wrap;
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_931 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--error-red);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_932 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_933 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_933 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 14px 16px 14px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_934 {
+ width: 36px;
+ height: 36px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 10px 10px 10px 10px;
+ background-color: rgba(240, 250, 245, 1);
+}
+.frame-content-2_934 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_935 {width: 18px;
+ height: 18px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_938 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.frame-content-2_938 {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_939 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_940 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ white-space: pre-wrap;
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_941 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--error-red);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_942 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_943 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_943 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 14px 16px 14px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_944 {
+ width: 36px;
+ height: 36px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 10px 10px 10px 10px;
+ background-color: rgba(255, 243, 238, 1);
+}
+.frame-content-2_944 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_945 {width: 18px;
+ height: 18px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_949 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.frame-content-2_949 {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_950 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_951 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ white-space: pre-wrap;
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_952 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 700;
+ color: var(--success-green);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_953 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_953 {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ align-items: flex-start;
+ padding: 16px 16px 16px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_954 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_955 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+.frame-content-2_955 {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_956 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_957 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_958 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+.frame-content-2_958 {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_959 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_960 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_961 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+.frame-content-2_961 {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_962 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--accent-orange);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_963 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-muted);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/module-pages-common.css b/gym-manage-uniapp/common/style/memberInfo/pages/module-pages-common.css
new file mode 100644
index 0000000..8e56469
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/module-pages-common.css
@@ -0,0 +1,584 @@
+/* 个人中心其它模块页面样式 */
+@import '@/common/style/memberInfo/member-info-gradient-cards.css';
+
+.mi-mod-tabs {
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacing-sm, 8px);
+ padding: 0 var(--spacing-md, 16px) var(--spacing-sm, 8px);
+ overflow-x: auto;
+ box-sizing: border-box;
+}
+
+/* 训练明细 */
+.mi-mod-session {
+ padding: 14px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.mi-mod-session:last-child {
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+.mi-mod-session__head {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+}
+
+.mi-mod-session__title {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ flex: 1;
+}
+
+.mi-mod-session__tag {
+ padding: 2px 8px;
+ border-radius: 6px;
+ flex-shrink: 0;
+}
+
+.mi-mod-session__tag--group {
+ background: rgba(255, 107, 53, 0.12);
+}
+
+.mi-mod-session__tag--private {
+ background: rgba(11, 43, 75, 0.1);
+}
+
+.mi-mod-session__tag--free {
+ background: rgba(46, 204, 113, 0.12);
+}
+
+.mi-mod-session__tag-text {
+ font-size: 10px;
+ font-weight: 600;
+ color: var(--text-muted, #5E6F8D);
+}
+
+.mi-mod-session__meta {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ margin-top: 4px;
+ display: block;
+}
+
+.mi-mod-session__footer {
+ display: flex;
+ flex-direction: row;
+ gap: 16px;
+ margin-top: 8px;
+}
+
+.mi-mod-session__stat {
+ font-size: var(--font-size-sm, 12px);
+ font-weight: 600;
+ color: var(--primary-deep, #1A4A6F);
+}
+
+/* 优惠券 */
+.mi-mod-coupon {
+ display: flex;
+ flex-direction: row;
+ border-radius: var(--radius-md, 20px);
+ overflow: hidden;
+ margin-bottom: 12px;
+ box-shadow: var(--shadow-sm);
+}
+
+.mi-mod-coupon--expired,
+.mi-mod-coupon--used {
+ opacity: 0.65;
+}
+
+.mi-mod-coupon__left {
+ width: 100px;
+ padding: 16px 12px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.mi-mod-coupon--used .mi-mod-coupon__left,
+.mi-mod-coupon--expired .mi-mod-coupon__left {
+ background: var(--bg-gray, #F2F5F9);
+}
+
+.mi-mod-coupon__amount {
+ font-size: 28px;
+ font-weight: 800;
+ color: var(--text-inverse, #fff);
+ line-height: 1;
+}
+
+.mi-mod-coupon--used .mi-mod-coupon__amount,
+.mi-mod-coupon--expired .mi-mod-coupon__amount {
+ color: var(--text-muted, #5E6F8D);
+}
+
+.mi-mod-coupon__min {
+ font-size: 10px;
+ color: rgba(255, 255, 255, 0.85);
+ margin-top: 4px;
+}
+
+.mi-mod-coupon--used .mi-mod-coupon__min,
+.mi-mod-coupon--expired .mi-mod-coupon__min {
+ color: var(--text-light, #8A99B4);
+}
+
+.mi-mod-coupon__right {
+ flex: 1;
+ padding: 14px 16px;
+ background: var(--bg-white, #fff);
+ position: relative;
+}
+
+.mi-mod-coupon__title {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.mi-mod-coupon__desc {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ margin-top: 4px;
+ display: block;
+}
+
+.mi-mod-coupon__expire {
+ font-size: 11px;
+ color: var(--text-light, #8A99B4);
+ margin-top: 8px;
+ display: block;
+}
+
+.mi-mod-coupon__tag {
+ position: absolute;
+ top: 14px;
+ right: 14px;
+ padding: 2px 8px;
+ border-radius: 6px;
+ background: var(--bg-gray, #F2F5F9);
+}
+
+.mi-mod-coupon__tag-text {
+ font-size: 10px;
+ color: var(--accent-orange, #FF6B35);
+ font-weight: 600;
+}
+
+/* 积分 */
+.mi-mod-points-hero {
+ padding: 24px 20px;
+ border-radius: var(--radius-sm, 12px);
+ text-align: center;
+}
+
+.mi-mod-points-hero__label {
+ font-size: var(--font-size-xs, 12px);
+ color: rgba(255, 212, 184, 1);
+ display: block;
+}
+
+.mi-mod-points-hero__value {
+ font-size: var(--font-size-3xl, 2rem);
+ font-weight: 700;
+ color: var(--text-inverse, #fff);
+ line-height: 1.2;
+ display: block;
+ margin: 8px 0;
+}
+
+.mi-mod-points-hero__tip {
+ font-size: var(--font-size-xs, 12px);
+ color: rgba(255, 212, 184, 1);
+ line-height: 1.5;
+}
+
+.mi-mod-rewards {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+}
+
+.mi-mod-reward {
+ padding: 14px;
+ border-radius: var(--radius-sm, 12px);
+ background: var(--bg-gray, #F2F5F9);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ text-align: center;
+}
+
+.mi-mod-reward__icon {
+ width: 28px;
+ height: 28px;
+}
+
+.mi-mod-reward__name {
+ font-size: var(--font-size-sm, 12px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.mi-mod-reward__cost {
+ font-size: 11px;
+ color: var(--accent-orange, #FF6B35);
+ font-weight: 600;
+}
+
+.mi-mod-reward__stock {
+ font-size: 10px;
+ color: var(--text-light, #8A99B4);
+}
+
+.mi-mod-points-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.mi-mod-points-row:last-child {
+ border-bottom: none;
+}
+
+.mi-mod-points-row__info {
+ flex: 1;
+}
+
+.mi-mod-points-row__title {
+ font-size: var(--font-size-base, 14px);
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.mi-mod-points-row__time {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-light, #8A99B4);
+ margin-top: 2px;
+ display: block;
+}
+
+.mi-mod-points-row__right {
+ text-align: right;
+}
+
+.mi-mod-points-row__amount {
+ font-size: var(--font-size-md, 16px);
+ font-weight: 700;
+ display: block;
+}
+
+.mi-mod-points-row__amount--earn {
+ color: var(--success-green, #2ECC71);
+}
+
+.mi-mod-points-row__amount--spend {
+ color: var(--warning-amber, #F39C12);
+}
+
+.mi-mod-points-row__balance {
+ font-size: 10px;
+ color: var(--text-light, #8A99B4);
+}
+
+/* 邀请 */
+.mi-mod-referral-hero {
+ padding: 20px;
+ border-radius: var(--radius-sm, 12px);
+}
+
+.mi-mod-referral-hero__title {
+ font-size: var(--font-size-lg, 18px);
+ font-weight: 700;
+ color: var(--text-inverse, #fff);
+ display: block;
+}
+
+.mi-mod-referral-hero__desc {
+ font-size: var(--font-size-sm, 12px);
+ color: rgba(255, 212, 184, 1);
+ margin-top: 6px;
+ display: block;
+}
+
+.mi-mod-referral-code {
+ margin-top: 16px;
+ padding: 16px;
+ border-radius: 12px;
+ background: rgba(255, 255, 255, 0.15);
+ text-align: center;
+}
+
+.mi-mod-referral-code__label {
+ font-size: var(--font-size-sm, 12px);
+ color: rgba(255, 255, 255, 0.75);
+ display: block;
+}
+
+.mi-mod-referral-code__value {
+ font-size: 24px;
+ font-weight: 800;
+ color: var(--text-inverse, #fff);
+ letter-spacing: 2px;
+ margin-top: 6px;
+ display: block;
+}
+
+.mi-mod-referral-stats {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-around;
+}
+
+.mi-mod-referral-stat {
+ text-align: center;
+}
+
+.mi-mod-referral-stat__num {
+ font-size: var(--font-size-xl, 20px);
+ font-weight: 800;
+ display: block;
+}
+
+.mi-mod-referral-stat__num--orange {
+ color: var(--accent-orange, #FF6B35);
+}
+
+.mi-mod-referral-stat__num--green {
+ color: var(--success-green, #2ECC71);
+}
+
+.mi-mod-referral-stat__num--amber {
+ color: var(--warning-amber, #F39C12);
+}
+
+.mi-mod-referral-stat__label {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ margin-top: 4px;
+ display: block;
+}
+
+.mi-mod-referral-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.mi-mod-referral-row:last-child {
+ border-bottom: none;
+}
+
+.mi-mod-referral-row__info {
+ flex: 1;
+}
+
+.mi-mod-referral-row__name {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.mi-mod-referral-row__time {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-light, #8A99B4);
+ margin-top: 2px;
+ display: block;
+}
+
+.mi-mod-referral-row__right {
+ text-align: right;
+}
+
+.mi-mod-referral-row__status {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ display: block;
+}
+
+.mi-mod-referral-row__reward {
+ font-size: 11px;
+ font-weight: 600;
+ color: var(--accent-orange, #FF6B35);
+ margin-top: 2px;
+ display: block;
+}
+
+/* 我的课程 */
+.mi-mod-course-card {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ padding: 14px;
+ border-radius: var(--radius-md, 20px);
+ background: var(--bg-white, #fff);
+ box-shadow: var(--shadow-sm);
+ margin-bottom: 12px;
+}
+
+.mi-mod-course-card__banner {
+ width: 88px;
+ height: 88px;
+ border-radius: 12px;
+ flex-shrink: 0;
+}
+
+.mi-mod-course-card__content {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.mi-mod-course-card__title {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+}
+
+.mi-mod-course-card__coach {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+}
+
+.mi-mod-course-card__progress {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+ margin-top: 4px;
+}
+
+.mi-mod-course-card__progress-bar {
+ flex: 1;
+ height: 6px;
+ border-radius: 3px;
+ background: var(--bg-gray, #F2F5F9);
+ overflow: hidden;
+}
+
+.mi-mod-course-card__progress-fill {
+ height: 100%;
+ border-radius: 3px;
+ background: var(--gradient-orange);
+}
+
+.mi-mod-course-card__progress-text {
+ font-size: 10px;
+ color: var(--text-muted, #5E6F8D);
+ flex-shrink: 0;
+}
+
+.mi-mod-course-card__meta {
+ font-size: 11px;
+ color: var(--text-light, #8A99B4);
+}
+
+.mi-mod-course-card__next {
+ font-size: 11px;
+ font-weight: 600;
+ color: var(--primary-deep, #1A4A6F);
+}
+
+/* 签到记录 */
+.mi-mod-checkin-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--border-light, #E9EDF2);
+}
+
+.mi-mod-checkin-row:last-child {
+ border-bottom: none;
+}
+
+.mi-mod-checkin-row__icon {
+ width: 40px;
+ height: 40px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.mi-mod-checkin-row__icon--group {
+ background: rgba(255, 107, 53, 0.12);
+}
+
+.mi-mod-checkin-row__icon--private {
+ background: rgba(11, 43, 75, 0.1);
+}
+
+.mi-mod-checkin-row__icon--free {
+ background: rgba(46, 204, 113, 0.12);
+}
+
+.mi-mod-checkin-row__icon-img {
+ width: 20px;
+ height: 20px;
+}
+
+.mi-mod-checkin-row__info {
+ flex: 1;
+ min-width: 0;
+}
+
+.mi-mod-checkin-row__title {
+ font-size: var(--font-size-base, 14px);
+ font-weight: 600;
+ color: var(--text-dark, #1E2A3A);
+ display: block;
+}
+
+.mi-mod-checkin-row__time {
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ margin-top: 2px;
+ display: block;
+}
+
+.mi-mod-checkin-row__tag {
+ padding: 4px 10px;
+ border-radius: 8px;
+ flex-shrink: 0;
+}
+
+.mi-mod-checkin-row__tag--group {
+ background: rgba(255, 107, 53, 0.12);
+}
+
+.mi-mod-checkin-row__tag--private {
+ background: rgba(11, 43, 75, 0.1);
+}
+
+.mi-mod-checkin-row__tag--free {
+ background: rgba(46, 204, 113, 0.12);
+}
+
+.mi-mod-checkin-row__tag-text {
+ font-size: 10px;
+ font-weight: 600;
+ color: var(--text-muted, #5E6F8D);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/page-reset.css b/gym-manage-uniapp/common/style/memberInfo/pages/page-reset.css
new file mode 100644
index 0000000..abdcea9
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/page-reset.css
@@ -0,0 +1,32 @@
+/* 子页面根容器:锁定变量,与 H5 theme-light 一致 */
+.scroll-container {
+ box-sizing: border-box;
+ --primary-dark: #0B2B4B;
+ --primary-deep: #1A4A6F;
+ --accent-orange: #FF6B35;
+ --accent-orange-light: #FF8C5A;
+ --bg-light: #F9FAFE;
+ --bg-white: #FFFFFF;
+ --text-dark: #1E2A3A;
+ --text-muted: #5E6F8D;
+ --text-light: #8A99B4;
+ --text-inverse: #FFFFFF;
+ --border-light: #E9EDF2;
+ --success-green: #2ECC71;
+ --spacing-xs: 4px;
+ --spacing-sm: 8px;
+ --spacing-md: 16px;
+ --spacing-lg: 24px;
+ --spacing-xl: 32px;
+ --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ --font-size-xs: 11px;
+ --font-size-sm: 12px;
+ --font-size-base: 14px;
+ --font-size-md: 16px;
+ --font-size-lg: 18px;
+ --font-size-xl: 20px;
+ --font-size-2xl: 22px;
+ --font-size-3xl: 24px;
+ --font-size-4xl: 28px;
+ --font-size-5xl: 32px;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/sub-page-base.css b/gym-manage-uniapp/common/style/memberInfo/pages/sub-page-base.css
new file mode 100644
index 0000000..a1df48b
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/sub-page-base.css
@@ -0,0 +1,83 @@
+.scroll-container {
+ height: 100%;
+ width: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ box-sizing: border-box;
+}
+
+.scroll-container > view {
+ width: 100%;
+}
+
+/* ========== 子页面统一布局(参考 base.css 间距变量) ========== */
+
+.bt-page,
+.booking-page {
+ width: 100%;
+ min-height: 100%;
+ box-sizing: border-box;
+ overflow-x: hidden;
+ background-color: var(--bg-light, #F9FAFE);
+}
+
+/* 导航栏下方首个区块:与固定顶栏留出间距 */
+.bt-page > .sub-nav + .mi-mod-tabs,
+.bt-page > .sub-nav + .mi-course-list__filters,
+.bt-page > .sub-nav + .bt-page__action-bar,
+.bt-page > .sub-nav + .bt-page__body,
+.booking-page > .sub-nav + .booking-page__tabs {
+ margin-top: var(--spacing-md, 16px);
+}
+
+/* tabs / 筛选栏下方内容区:避免重复过大顶边距 */
+.bt-page > .sub-nav + .mi-mod-tabs + .bt-page__action-bar + .bt-page__body,
+.bt-page > .sub-nav + .mi-mod-tabs + .bt-page__body,
+.bt-page > .sub-nav + .mi-course-list__filters + .bt-page__body,
+.booking-page > .sub-nav + .booking-page__tabs + .bt-page__action-bar + .booking-page__body,
+.booking-page > .sub-nav + .booking-page__tabs + .booking-page__body {
+ padding-top: var(--spacing-sm, 8px);
+}
+
+/* 导航栏下直接跟操作栏(无 tabs) */
+.bt-page > .sub-nav + .bt-page__action-bar + .bt-page__body {
+ padding-top: var(--spacing-sm, 8px);
+}
+
+/* 页面内次级操作栏(原导航栏右侧按钮下移至此) */
+.bt-page__action-bar {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--spacing-sm, 8px);
+ padding: 0 var(--spacing-md, 16px) var(--spacing-sm, 8px);
+ box-sizing: border-box;
+}
+
+.bt-page__action-bar--end {
+ justify-content: flex-end;
+}
+
+.bt-page__action-bar-text {
+ flex: 1;
+ font-size: var(--font-size-sm, 12px);
+ color: var(--text-muted, #5E6F8D);
+ line-height: 1.4;
+}
+
+.bt-page__action-link {
+ flex-shrink: 0;
+ font-size: var(--font-size-sm, 12px);
+ font-weight: 600;
+ color: var(--primary-deep, #1A4A6F);
+ padding: 6px 12px;
+ border-radius: var(--radius-full, 999px);
+ background-color: var(--bg-white, #FFFFFF);
+ border: 1px solid var(--border-light, #E9EDF2);
+}
+
+.bt-page__action-link--primary {
+ color: var(--text-inverse, #FFFFFF);
+ border-color: transparent;
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/user-info-page.css b/gym-manage-uniapp/common/style/memberInfo/pages/user-info-page.css
new file mode 100644
index 0000000..24af1ab
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/user-info-page.css
@@ -0,0 +1,192 @@
+.Pixso-frame-2_791 {
+ width: 100%;
+ box-sizing: border-box;
+ overflow-x: hidden;
+ overflow-y: visible;
+ background-color: var(--bg-light, #F9FAFE);
+}
+
+.frame-content-2_802 {
+ box-sizing: border-box;
+ padding-bottom: calc(88px + env(safe-area-inset-bottom)) !important;
+}
+
+.user-info-save-bar {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 100;
+ padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
+ background-color: var(--bg-white, #ffffff);
+ box-shadow: 0 -2px 12px rgba(26, 25, 24, 0.06);
+ box-sizing: border-box;
+}
+
+.user-info-save-bar__btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 48px;
+ border-radius: 12px;
+ background-color: var(--accent-orange, #FF6B35);
+}
+
+.user-info-save-bar__text {
+ font-size: var(--font-size-md, 16px);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-inverse, #ffffff);
+ line-height: 1;
+}
+
+.avatar-block {
+ width: 100%;
+ height: 120px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 14px;
+ box-shadow: 0 2px 10px rgba(26, 25, 24, 0.03);
+ background-color: var(--bg-white);
+ flex-shrink: 0;
+ overflow: hidden;
+ box-sizing: border-box;
+}
+
+.avatar-block__inner {
+ position: relative;
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ overflow: hidden;
+ flex-shrink: 0;
+ background-color: var(--bg-light, #f9fafe);
+}
+
+.avatar-block__photo {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.avatar-block__change {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ width: 100%;
+ height: 28px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ background-color: rgba(0, 0, 0, 0.55);
+ z-index: 2;
+ box-sizing: border-box;
+}
+
+.avatar-block__icon {
+ width: 12px;
+ height: 12px;
+ flex-shrink: 0;
+ display: block;
+ filter: brightness(0) invert(1);
+}
+
+.avatar-block__text {
+ font-size: var(--font-size-xs, 11px);
+ line-height: 1;
+ color: #ffffff;
+ white-space: nowrap;
+}
+
+.frame-content-2_811,
+.frame-content-2_817,
+.frame-content-2_826,
+.frame-content-2_842,
+.frame-content-2_848,
+.frame-content-2_859 {
+ min-width: 0;
+}
+
+.Pixso-paragraph-2_813,
+.Pixso-paragraph-2_844,
+.Pixso-paragraph-2_861 {
+ display: block;
+ min-width: 0;
+}
+
+.gender-btn {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 4px;
+ padding: 6px 14px;
+ border-radius: 8px;
+ background-color: var(--bg-light, #F9FAFE);
+ flex-shrink: 0;
+}
+
+.gender-btn__icon {
+ width: 14px;
+ height: 14px;
+ display: block;
+ flex-shrink: 0;
+}
+
+.gender-btn__text {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-light, #8A99B4);
+ white-space: nowrap;
+}
+
+.gender-btn--active {
+ background-color: var(--accent-orange, #FF6B35);
+}
+
+.gender-btn--active .gender-btn__text {
+ color: var(--text-inverse, #ffffff);
+}
+
+.goal-tags {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 8px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.goal-tag {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 7px 16px;
+ border-radius: 100px;
+ border: 1px solid rgba(209, 208, 205, 1);
+ background-color: var(--bg-white, #ffffff);
+ flex-shrink: 0;
+ box-sizing: border-box;
+}
+
+.goal-tag__text {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted, #5E6F8D);
+ white-space: nowrap;
+}
+
+.goal-tag--selected {
+ border-color: var(--accent-orange, #FF6B35);
+ background-color: var(--accent-orange, #FF6B35);
+}
+
+.goal-tag--selected .goal-tag__text {
+ color: var(--text-inverse, #ffffff);
+}
diff --git a/gym-manage-uniapp/common/style/memberInfo/pages/user-info-pixso.css b/gym-manage-uniapp/common/style/memberInfo/pages/user-info-pixso.css
new file mode 100644
index 0000000..93384ce
--- /dev/null
+++ b/gym-manage-uniapp/common/style/memberInfo/pages/user-info-pixso.css
@@ -0,0 +1,1035 @@
+.Pixso-frame-2_791 {
+ width: 100%;
+ height: auto;
+ min-height: 100%;
+ overflow-x: hidden;
+ overflow-y: visible;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_792 {
+ width: 100%;
+ height: 62px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_792 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0px 20px 0px 20px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_793 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-paragraph-2_794 {
+ font-size: var(--font-size-sm);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_795 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ background-color: var(--bg-white);
+}
+.frame-content-2_795 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.stroke-wrapper-2_795 {
+ position: relative;
+ width: 100%;
+ height: 52px;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_795 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 0px 0px 0px 0px;
+ pointer-events: none;
+ border-width: 0px 0px 1px 0px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: var(--border-light);
+}
+.Pixso-frame-2_796 {
+ width: 32px;
+ height: 32px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.frame-content-2_796 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_797 {width: 20px;
+ height: 20px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_799 {
+ font-size: var(--font-size-md);
+ font-family: var(--font-family);
+ font-weight: 700;
+ text-align: center;
+ color: var(--primary-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_800 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 6px 14px 6px 14px;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--accent-orange);
+}
+.Pixso-paragraph-2_801 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_802 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.frame-content-2_802 {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 16px 16px 40px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_803 {
+ width: 100%;
+ height: 120px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_803 {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px 0px 20px 0px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-vector-2_804 {
+ width: 80px;
+ height: 80px;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-frame-2_805 {
+ width: 80px;
+ height: 26px;
+ position: absolute;
+ left: 139px;
+ top: 74px;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ border-radius: 0px 0px 40px 53px;
+ background-color: rgba(0, 0, 0, 0.501960813999176);
+}
+.frame-content-2_805 {
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-vector-2_806 {
+ width: 12px;
+ height: 12px;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-paragraph-2_809 {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_810 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_810 {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_811 {
+ width: 100%;
+ height: 56px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_811 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_812 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-paragraph-2_813 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-vector-2_814 {width: 16px;
+ height: 16px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_816 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_817 {
+ width: 100%;
+ height: 56px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_817 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_818 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-frame-2_819 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.frame-content-2_819 {
+ display: flex;
+ flex-direction: row;
+ gap: 6px;
+ align-items: center;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_820 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_821 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 2px 8px 2px 8px;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(240, 250, 245, 1);
+}
+.Pixso-paragraph-2_822 {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--success-green);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-vector-2_823 {width: 16px;
+ height: 16px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_825 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_826 {
+ width: 100%;
+ height: 56px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_826 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_827 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-frame-2_828 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.frame-content-2_828 {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_829 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ padding: 6px 14px 6px 14px;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--accent-orange);
+}
+.Pixso-vector-2_830 {width: 14px;
+ height: 14px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_834 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_835 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ padding: 6px 14px 6px 14px;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.Pixso-vector-2_836 {width: 14px;
+ height: 14px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-paragraph-2_840 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_841 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_842 {
+ width: 100%;
+ height: 56px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_842 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_843 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-paragraph-2_844 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-vector-2_845 {width: 16px;
+ height: 16px;
+ position: relative;
+ flex-shrink: 0;
+ display: block;
+}
+.Pixso-frame-2_847 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_848 {
+ width: 100%;
+ height: 56px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_848 {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_849 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-frame-2_850 {
+ width: 100%;
+ height: 36px;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ flex-grow: 1;
+ flex-basis: 0;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.frame-content-2_850 {
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ padding: 0px 12px 0px 12px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_851 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-paragraph-2_852 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_853 {
+ width: 12px;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: rgba(249, 250, 254, 0);
+}
+.Pixso-paragraph-2_854 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-frame-2_855 {
+ width: 100%;
+ height: 36px;
+ position: relative;
+ flex-shrink: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ flex-grow: 1;
+ flex-basis: 0;
+ border-radius: 8px 8px 8px 8px;
+ background-color: var(--bg-light);
+}
+.frame-content-2_855 {
+ display: flex;
+ flex-direction: row;
+ gap: 4px;
+ align-items: center;
+ padding: 0px 12px 0px 12px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_856 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-dark);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-paragraph-2_857 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--text-light);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_858 {
+ width: 100%;
+ height: 1px;
+ position: relative;
+ flex-shrink: 0;
+ background-color: var(--bg-light);
+}
+.Pixso-frame-2_859 {
+ width: 100%;
+ height: 56px;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+.frame-content-2_859 {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ padding: 0px 16px 0px 16px;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_860 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: 60px;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+}
+.Pixso-paragraph-2_861 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 400;
+ color: var(--success-green);
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 1;
+ flex-grow: 1;
+ flex-basis: 0;
+}
+.Pixso-frame-2_862 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 2px 8px 2px 8px;
+ border-radius: 6px 6px 6px 6px;
+ background-color: rgba(240, 250, 245, 1);
+}
+.stroke-wrapper-2_862 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_862 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 6px 6px 6px 6px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(200, 240, 216, 1);
+}
+.Pixso-paragraph-2_863 {
+ font-size: var(--font-size-xs);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--success-green);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_864 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 14px 14px 14px 14px;
+ box-shadow: 0px 2px 10px 0px rgba(26, 25, 24, 0.03137254901960784);
+ background-color: var(--bg-white);
+}
+.frame-content-2_864 {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 16px 16px 16px 16px;
+ width: 100%;
+ position: relative;
+}
+.Pixso-paragraph-2_865 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 600;
+ color: var(--text-dark);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_866 {
+ width: 100%;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+.frame-content-2_866 {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 8px 8px;
+ align-items: flex-start;
+ align-content: flex-start;
+ width: 100%;
+ position: relative;
+}
+.Pixso-frame-2_867 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 7px 16px 7px 16px;
+ border-radius: 100px 100px 100px 100px;
+ background-color: var(--accent-orange);
+}
+.Pixso-paragraph-2_868 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_869 {
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 7px 16px 7px 16px;
+ border-radius: 100px 100px 100px 100px;
+ background-color: var(--accent-orange);
+}
+.Pixso-paragraph-2_870 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-inverse);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_871 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 7px 16px 7px 16px;
+ border-radius: 100px 100px 100px 100px;
+ background-color: var(--bg-light);
+}
+.stroke-wrapper-2_871 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_871 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 100px 100px 100px 100px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(209, 208, 205, 1);
+}
+.Pixso-paragraph-2_872 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_873 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 7px 16px 7px 16px;
+ border-radius: 100px 100px 100px 100px;
+ background-color: var(--bg-light);
+}
+.stroke-wrapper-2_873 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_873 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 100px 100px 100px 100px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(209, 208, 205, 1);
+}
+.Pixso-paragraph-2_874 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
+.Pixso-frame-2_875 {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 7px 16px 7px 16px;
+ border-radius: 100px 100px 100px 100px;
+ background-color: var(--bg-light);
+}
+.stroke-wrapper-2_875 {
+ position: relative;
+ width: auto;
+ height: auto;
+ display: flex;
+ flex-shrink: 0;
+}
+.stroke-2_875 {
+ position: absolute;
+ inset: 0px;
+ border-radius: 100px 100px 100px 100px;
+ pointer-events: none;
+ border-width: 1px 1px 1px 1px;
+ border-style: solid;
+ box-sizing: border-box;
+ border-color: rgba(209, 208, 205, 1);
+}
+.Pixso-paragraph-2_876 {
+ font-size: var(--font-size-base);
+ font-family: var(--font-family);
+ font-weight: 500;
+ color: var(--text-muted);
+ width: auto;
+ height: auto;
+ position: relative;
+ flex-shrink: 0;
+ white-space: nowrap;
+ flex-grow: 0;
+}
diff --git a/gym-manage-uniapp/components/TabBar.vue b/gym-manage-uniapp/components/TabBar.vue
index d341948..aa51392 100644
--- a/gym-manage-uniapp/components/TabBar.vue
+++ b/gym-manage-uniapp/components/TabBar.vue
@@ -1,59 +1,91 @@
-
/g, '');
+ result = result.replace(/\s+id="[^"]*"/g, '');
+ result = result.replace(/\{\{\s*"([^"]*?)"\s*\}\}/gs, '$1');
+ result = result.replace(/\{\{\s*'([^']*?)'\s*\}\}/gs, '$1');
+ result = result.replace(
+ '