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 @@ diff --git a/gym-manage-uniapp/components/memberInfo/BodyTestTrendChart.vue b/gym-manage-uniapp/components/memberInfo/BodyTestTrendChart.vue new file mode 100644 index 0000000..16e9144 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/BodyTestTrendChart.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoBodyReport.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoBodyReport.vue new file mode 100644 index 0000000..553c33b --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoBodyReport.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoBookingList.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoBookingList.vue new file mode 100644 index 0000000..62c1067 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoBookingList.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoCheckInList.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoCheckInList.vue new file mode 100644 index 0000000..508cb87 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoCheckInList.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoCouponPoints.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoCouponPoints.vue new file mode 100644 index 0000000..f1fb48f --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoCouponPoints.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoHeader.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoHeader.vue new file mode 100644 index 0000000..a9fd5b0 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoHeader.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoLogout.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoLogout.vue new file mode 100644 index 0000000..94604f5 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoLogout.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoMemberCard.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoMemberCard.vue new file mode 100644 index 0000000..9190ae2 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoMemberCard.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoQuickActions.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoQuickActions.vue new file mode 100644 index 0000000..b2b935b --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoQuickActions.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoReferral.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoReferral.vue new file mode 100644 index 0000000..86461e4 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoReferral.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoSettings.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoSettings.vue new file mode 100644 index 0000000..50fa947 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoSettings.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoStatusBar.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoStatusBar.vue new file mode 100644 index 0000000..d14ef34 --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoStatusBar.vue @@ -0,0 +1,20 @@ + + + diff --git a/gym-manage-uniapp/components/memberInfo/MemberInfoSubNav.vue b/gym-manage-uniapp/components/memberInfo/MemberInfoSubNav.vue new file mode 100644 index 0000000..eb7cdac --- /dev/null +++ b/gym-manage-uniapp/components/memberInfo/MemberInfoSubNav.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/gym-manage-uniapp/pages.json b/gym-manage-uniapp/pages.json index 2f03bac..52768ee 100644 --- a/gym-manage-uniapp/pages.json +++ b/gym-manage-uniapp/pages.json @@ -1,17 +1,267 @@ { - "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages + "pages": [ { "path": "pages/index/index", "style": { + "navigationStyle": "custom", "navigationBarTitleText": "健身房" } + }, + { + "path": "pages/course/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "课程" + } + }, + { + "path": "pages/train/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "训练" + } + }, + { + "path": "pages/discover/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "发现" + } + }, + { + "path": "pages/memberInfo/memberInfo", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的" + } + }, + { + "path": "pages/memberInfo/memberCard", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的会员卡" + } + }, + { + "path": "pages/memberInfo/userInfo", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "个人信息" + } + }, + { + "path": "pages/memberInfo/booking", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的预约" + } + }, + { + "path": "pages/memberInfo/bodyTestHome", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "智能体测" + } + }, + { + "path": "pages/memberInfo/bodyTestConnect", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "连接设备" + } + }, + { + "path": "pages/memberInfo/bodyTestMeasuring", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "测量中" + } + }, + { + "path": "pages/memberInfo/bodyTestReport", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "体测报告" + } + }, + { + "path": "pages/memberInfo/bodyTestHistory", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "体测记录" + } + }, + { + "path": "pages/memberInfo/bodyTestCompare", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "历史对比" + } + }, + { + "path": "pages/memberInfo/bodyTestSettings", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "体测设置" + } + }, + { + "path": "pages/memberInfo/bodyTestTrend", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "趋势分析" + } + }, + { + "path": "pages/memberInfo/trainReport", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "训练报告" + } + }, + { + "path": "pages/memberInfo/trainSessionDetail", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "训练详情" + } + }, + { + "path": "pages/memberInfo/coupons", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的优惠券" + } + }, + { + "path": "pages/memberInfo/couponDetail", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "优惠券详情" + } + }, + { + "path": "pages/memberInfo/couponCenter", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "领券中心" + } + }, + { + "path": "pages/memberInfo/points", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的积分" + } + }, + { + "path": "pages/memberInfo/pointsMall", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "积分商城" + } + }, + { + "path": "pages/memberInfo/pointsHistory", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "积分明细" + } + }, + { + "path": "pages/memberInfo/referral", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "邀请好友" + } + }, + { + "path": "pages/memberInfo/myCourses", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的课程" + } + }, + { + "path": "pages/memberInfo/checkInHistory", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "签到记录" + } + }, + { + "path": "pages/memberInfo/courseList", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "预约课程" + } + }, + { + "path": "pages/memberInfo/courseDetail", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "课程详情" + } + }, + { + "path": "pages/memberInfo/onlineCourseDetail", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "线上课程" + } + }, + { + "path": "pages/memberInfo/courseEvaluate", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "课程评价" + } } ], "globalStyle": { "navigationBarTextStyle": "black", - "navigationBarTitleText": "uni-app", + "navigationBarTitleText": "健身房", "navigationBarBackgroundColor": "#F8F8F8", "backgroundColor": "#F8F8F8" }, + "tabBar": { + "custom": true, + "color": "#94a3b8", + "selectedColor": "#f97316", + "backgroundColor": "#1A4A6F", + "borderStyle": "white", + "list": [ + { + "pagePath": "pages/index/index", + "text": "首页", + "iconPath": "static/images/home.png", + "selectedIconPath": "static/images/home.png" + }, + { + "pagePath": "pages/course/index", + "text": "课程", + "iconPath": "static/images/bookmark.png", + "selectedIconPath": "static/images/bookmark.png" + }, + { + "pagePath": "pages/train/index", + "text": "训练", + "iconPath": "static/images/dumbbell.png", + "selectedIconPath": "static/images/dumbbell.png" + }, + { + "pagePath": "pages/discover/index", + "text": "发现", + "iconPath": "static/images/activity.png", + "selectedIconPath": "static/images/activity.png" + }, + { + "pagePath": "pages/memberInfo/memberInfo", + "text": "我的", + "iconPath": "static/images/user.png", + "selectedIconPath": "static/images/user.png" + } + ] + }, "uniIdRouter": {} } diff --git a/gym-manage-uniapp/pages/course/index.vue b/gym-manage-uniapp/pages/course/index.vue new file mode 100644 index 0000000..dc11910 --- /dev/null +++ b/gym-manage-uniapp/pages/course/index.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/gym-manage-uniapp/pages/discover/index.vue b/gym-manage-uniapp/pages/discover/index.vue new file mode 100644 index 0000000..971d7c2 --- /dev/null +++ b/gym-manage-uniapp/pages/discover/index.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/gym-manage-uniapp/pages/index/index.vue b/gym-manage-uniapp/pages/index/index.vue index 8257a92..0850155 100644 --- a/gym-manage-uniapp/pages/index/index.vue +++ b/gym-manage-uniapp/pages/index/index.vue @@ -16,7 +16,7 @@ - + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestCompare.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestCompare.vue new file mode 100644 index 0000000..dc437ce --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestCompare.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestConnect.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestConnect.vue new file mode 100644 index 0000000..4977f50 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestConnect.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestHistory.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestHistory.vue new file mode 100644 index 0000000..9bf6e1c --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestHistory.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestHome.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestHome.vue new file mode 100644 index 0000000..d47f81e --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestHome.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestMeasuring.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestMeasuring.vue new file mode 100644 index 0000000..55f3e34 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestMeasuring.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestReport.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestReport.vue new file mode 100644 index 0000000..75fcd4c --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestReport.vue @@ -0,0 +1,315 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestSettings.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestSettings.vue new file mode 100644 index 0000000..e36bad0 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestSettings.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/bodyTestTrend.vue b/gym-manage-uniapp/pages/memberInfo/bodyTestTrend.vue new file mode 100644 index 0000000..97f9ebc --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/bodyTestTrend.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/booking.vue b/gym-manage-uniapp/pages/memberInfo/booking.vue new file mode 100644 index 0000000..9a3db82 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/booking.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/checkInHistory.vue b/gym-manage-uniapp/pages/memberInfo/checkInHistory.vue new file mode 100644 index 0000000..a50968d --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/checkInHistory.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/couponCenter.vue b/gym-manage-uniapp/pages/memberInfo/couponCenter.vue new file mode 100644 index 0000000..4246b8e --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/couponCenter.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/couponDetail.vue b/gym-manage-uniapp/pages/memberInfo/couponDetail.vue new file mode 100644 index 0000000..2b32f94 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/couponDetail.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/coupons.vue b/gym-manage-uniapp/pages/memberInfo/coupons.vue new file mode 100644 index 0000000..fa6ed34 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/coupons.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/courseDetail.vue b/gym-manage-uniapp/pages/memberInfo/courseDetail.vue new file mode 100644 index 0000000..4a464a5 --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/courseDetail.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/gym-manage-uniapp/pages/memberInfo/courseEvaluate.vue b/gym-manage-uniapp/pages/memberInfo/courseEvaluate.vue new file mode 100644 index 0000000..c38b3fc --- /dev/null +++ b/gym-manage-uniapp/pages/memberInfo/courseEvaluate.vue @@ -0,0 +1,88 @@ +