diff --git a/client/src/app.config.ts b/client/src/app.config.ts index 0d2dc78..c8f333d 100644 --- a/client/src/app.config.ts +++ b/client/src/app.config.ts @@ -2,7 +2,12 @@ export default defineAppConfig({ pages: [ 'pages/index/index', 'pages/zone/index', - 'pages/vip/index' + 'pages/vip/index', + 'pages/favorites/index', + 'pages/history/index', + 'pages/notifications/index', + 'pages/orders/index', + 'pages/activity/index' ], window: { backgroundTextStyle: 'light', @@ -10,5 +15,17 @@ export default defineAppConfig({ navigationBarTitleText: 'LIMO来刻', navigationBarTextStyle: 'white', navigationStyle: 'custom' + }, + // 声明需要的隐私接口 + requiredPrivateInfos: [ + 'getLocation', + 'chooseLocation', + 'chooseAddress' + ], + // 位置权限说明 + permission: { + 'scope.userLocation': { + desc: '您的位置信息将用于推荐附近的运动场馆' + } } }) diff --git a/client/src/components/AuthModal/index.scss b/client/src/components/AuthModal/index.scss new file mode 100644 index 0000000..2d15a37 --- /dev/null +++ b/client/src/components/AuthModal/index.scss @@ -0,0 +1,580 @@ +.auth-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + padding: 32rpx; + + .modal-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + animation: fadeIn 0.3s ease-out; + } + + .modal-content { + position: relative; + z-index: 10; + background: #ffffff; + border-radius: 32rpx; + padding: 48rpx 32rpx; + width: 100%; + max-width: 640rpx; + animation: slideUp 0.3s ease-out; + box-shadow: 0 32rpx 96rpx rgba(0, 0, 0, 0.2); + + .modal-header { + text-align: center; + margin-bottom: 32rpx; + + .logo-container { + margin-bottom: 24rpx; + + .app-logo { + width: 120rpx; + height: 120rpx; + } + } + + .modal-title { + font-size: 36rpx; + font-weight: bold; + color: #111827; + display: block; + margin-bottom: 12rpx; + } + + .modal-subtitle { + font-size: 26rpx; + color: #6B7280; + display: block; + } + } + + // 步骤指示器 + .step-indicator { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 48rpx; + padding: 0 32rpx; + + .step-item { + display: flex; + flex-direction: column; + align-items: center; + position: relative; + + .step-number { + width: 64rpx; + height: 64rpx; + border-radius: 50%; + background: #F3F4F6; + color: #9CA3AF; + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; + font-weight: 600; + margin-bottom: 12rpx; + border: 3rpx solid #E5E7EB; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + + .step-text { + font-size: 20rpx; + color: #9CA3AF; + font-weight: 500; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + + &.active { + .step-number { + background: linear-gradient(135deg, #05C7C7 0%, #04B5B5 100%); + color: #ffffff; + border-color: #05C7C7; + box-shadow: 0 8rpx 24rpx rgba(5, 199, 199, 0.3); + } + + .step-text { + color: #05C7C7; + font-weight: 600; + } + } + + &.completed { + .step-number { + background: #10B981; + color: #ffffff; + border-color: #10B981; + } + + .step-text { + color: #10B981; + } + } + } + + .step-line { + width: 80rpx; + height: 4rpx; + background: #E5E7EB; + margin: 0 24rpx; + margin-bottom: 32rpx; + border-radius: 2rpx; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &.active { + background: linear-gradient(135deg, #05C7C7 0%, #04B5B5 100%); + box-shadow: 0 2rpx 8rpx rgba(5, 199, 199, 0.3); + } + } + } + + // 授权步骤容器 + .auth-step { + animation: fadeInUp 0.3s ease-out; + } + + .auth-info { + margin-bottom: 48rpx; + + .info-item { + display: flex; + align-items: center; + padding: 24rpx; + background: #F9FAFB; + border-radius: 24rpx; + margin-bottom: 24rpx; + border: 2rpx solid #F3F4F6; + + &:last-child { + margin-bottom: 0; + } + + .info-icon { + width: 80rpx; + height: 80rpx; + background: #ffffff; + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 24rpx; + box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); + + .icon-text { + font-size: 36rpx; + } + } + + .info-content { + flex: 1; + + .info-title { + font-size: 28rpx; + font-weight: 600; + color: #111827; + display: block; + margin-bottom: 0; + } + + .info-desc { + font-size: 24rpx; + color: #6B7280; + line-height: 1; + } + } + } + } + + .auth-buttons { + margin-bottom: 32rpx; + + .auth-btn { + width: 100%; + height: 96rpx; + border-radius: 24rpx; + border: none; + font-size: 28rpx; + font-weight: 600; + margin-bottom: 24rpx; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + + &:last-child { + margin-bottom: 0; + } + + &:active { + transform: scale(0.98); + } + + &.primary { + background: linear-gradient(135deg, #05C7C7 0%, #04B5B5 100%); + color: #ffffff; + + &:disabled { + background: #10B981; + opacity: 0.8; + } + } + + &.secondary { + background: #F3F4F6; + color: #374151; + border: 2rpx solid #E5E7EB; + + &:disabled { + background: #10B981; + color: #ffffff; + border-color: #10B981; + } + } + + .btn-text { + position: relative; + z-index: 2; + line-height: 1; + } + + // 按钮光泽效果 + &::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.5s; + } + + &:active::before { + left: 100%; + } + } + } + + // 昵称输入区域 + .nickname-input-section { + margin-bottom: 32rpx; + + .input-label { + margin-bottom: 16rpx; + + .label-text { + font-size: 28rpx; + font-weight: 600; + color: #111827; + } + } + + .nickname-input { + width: 100%; + height: 96rpx; + background: #F9FAFB; + border: 2rpx solid #E5E7EB; + border-radius: 24rpx; + padding: 0 24rpx; + font-size: 28rpx; + color: #111827; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:focus { + border-color: #05C7C7; + background: #ffffff; + box-shadow: 0 0 0 6rpx rgba(5, 199, 199, 0.1); + } + + &::placeholder { + color: #9CA3AF; + font-size: 26rpx; + } + } + } + + .auth-actions { + margin-bottom: 32rpx; + + .complete-btn { + width: 100%; + height: 88rpx; + background: linear-gradient(135deg, #FBBF24 0%, #F59E0B 100%); + border-radius: 24rpx; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 16rpx 48rpx rgba(251, 191, 36, 0.3); + + &:active { + transform: scale(0.98); + } + + .complete-text { + font-size: 32rpx; + font-weight: bold; + color: #ffffff; + text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2); + } + } + + .next-btn { + width: 100%; + height: 88rpx; + background: linear-gradient(135deg, #05C7C7 0%, #04B5B5 100%); + border-radius: 24rpx; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 16rpx 48rpx rgba(5, 199, 199, 0.3); + + &:active { + transform: scale(0.98); + } + + .next-text { + font-size: 32rpx; + font-weight: bold; + color: #ffffff; + text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2); + } + } + + .action-row { + display: flex; + gap: 24rpx; + align-items: center; + + .back-btn { + flex: 1; + height: 80rpx; + background: #F3F4F6; + border-radius: 24rpx; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + border: 2rpx solid #E5E7EB; + + &:active { + transform: scale(0.98); + background: #E5E7EB; + } + + .back-text { + font-size: 28rpx; + font-weight: 600; + color: #6B7280; + } + } + + .skip-btn { + flex: 1; + text-align: center; + padding: 24rpx; + + .skip-text { + font-size: 28rpx; + color: #9CA3AF; + text-decoration: underline; + } + } + } + + .skip-btn { + text-align: center; + padding: 24rpx; + + .skip-text { + font-size: 28rpx; + color: #9CA3AF; + text-decoration: underline; + } + } + } + + .privacy-notice { + text-align: center; + padding: 0 16rpx; + + .notice-text { + font-size: 24rpx; + color: #9CA3AF; + line-height: 1.5; + + .link-text { + color: #05C7C7; + text-decoration: underline; + } + } + } + } +} + +// 动画 +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(100rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +// 响应式设计 +@media (max-width: 480px) { + .auth-modal { + padding: 24rpx; + + .modal-content { + padding: 40rpx 24rpx; + + .modal-header { + margin-bottom: 24rpx; + + .logo-container .app-logo { + width: 100rpx; + height: 100rpx; + } + + .modal-title { + font-size: 32rpx; + } + + .modal-subtitle { + font-size: 24rpx; + } + } + + .step-indicator { + margin-bottom: 40rpx; + padding: 0 16rpx; + + .step-item { + .step-number { + width: 56rpx; + height: 56rpx; + font-size: 22rpx; + margin-bottom: 8rpx; + } + + .step-text { + font-size: 18rpx; + } + } + + .step-line { + width: 60rpx; + margin: 0 16rpx; + margin-bottom: 24rpx; + } + } + + .auth-info { + margin-bottom: 40rpx; + + .info-item { + padding: 20rpx; + + .info-icon { + width: 72rpx; + height: 72rpx; + margin-right: 20rpx; + + .icon-text { + font-size: 32rpx; + } + } + + .info-content { + .info-title { + font-size: 26rpx; + } + + .info-desc { + font-size: 22rpx; + } + } + } + } + + .auth-buttons .auth-btn { + height: 88rpx; + font-size: 26rpx; + } + + .nickname-input-section { + margin-bottom: 24rpx; + + .input-label .label-text { + font-size: 26rpx; + } + + .nickname-input { + height: 80rpx; + font-size: 26rpx; + padding: 0 20rpx; + + &::placeholder { + font-size: 24rpx; + } + } + } + + .auth-actions { + .complete-btn, .next-btn { + height: 80rpx; + + .complete-text, .next-text { + font-size: 28rpx; + } + } + + .action-row .back-btn { + height: 72rpx; + + .back-text { + font-size: 26rpx; + } + } + } + } + } +} \ No newline at end of file diff --git a/client/src/components/AuthModal/index.vue b/client/src/components/AuthModal/index.vue new file mode 100644 index 0000000..038db53 --- /dev/null +++ b/client/src/components/AuthModal/index.vue @@ -0,0 +1,299 @@ + + + \ No newline at end of file diff --git a/client/src/components/index.ts b/client/src/components/index.ts index 2053896..4e404ea 100644 --- a/client/src/components/index.ts +++ b/client/src/components/index.ts @@ -1,4 +1,5 @@ /** * 组件导出 */ -export { default as CommonButton } from './CommonButton/index.vue' \ No newline at end of file +export { default as CommonButton } from './CommonButton/index.vue' +export { default as AuthModal } from './AuthModal/index.vue' \ No newline at end of file diff --git a/client/src/pages/activity/index.config.ts b/client/src/pages/activity/index.config.ts new file mode 100644 index 0000000..33f34e4 --- /dev/null +++ b/client/src/pages/activity/index.config.ts @@ -0,0 +1,6 @@ +export default definePageConfig({ + navigationBarTitleText: '历史活动', + navigationBarBackgroundColor: '#ffffff', + navigationBarTextStyle: 'black', + navigationStyle: 'default' +}) \ No newline at end of file diff --git a/client/src/pages/activity/index.scss b/client/src/pages/activity/index.scss new file mode 100644 index 0000000..37040db --- /dev/null +++ b/client/src/pages/activity/index.scss @@ -0,0 +1,137 @@ +.activity-page { + min-height: 100vh; + background: #f9fafb; + + // Empty State + .empty-state { + padding: 120rpx 48rpx; + display: flex; + justify-content: center; + + .empty-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + + .empty-icon { + font-size: 120rpx; + margin-bottom: 32rpx; + opacity: 0.6; + } + + .empty-title { + font-size: 32rpx; + font-weight: 600; + color: #374151; + margin-bottom: 16rpx; + } + + .empty-subtitle { + font-size: 26rpx; + color: #9ca3af; + line-height: 1.5; + } + } + } + + // Activity List + .activity-list { + padding: 24rpx; + + .date-group { + margin-bottom: 48rpx; + + &:last-child { + margin-bottom: 24rpx; + } + + .date-header { + margin-bottom: 24rpx; + padding: 0 8rpx; + + .date-text { + font-size: 28rpx; + font-weight: 600; + color: #374151; + position: relative; + + &::after { + content: ''; + position: absolute; + bottom: -8rpx; + left: 0; + width: 48rpx; + height: 4rpx; + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + border-radius: 2rpx; + } + } + } + + .activities { + .activity-item { + background: #ffffff; + border-radius: 20rpx; + padding: 24rpx; + margin-bottom: 16rpx; + border: 2rpx solid #f3f4f6; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.995); + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); + } + + &:last-child { + margin-bottom: 0; + } + + .activity-content { + width: 100%; + + .activity-title { + font-size: 28rpx; + font-weight: 600; + color: #111827; + margin-bottom: 8rpx; + line-height: 1.4; + display: block; + } + + .activity-description { + font-size: 24rpx; + color: #6b7280; + margin-bottom: 12rpx; + line-height: 1.4; + display: block; + } + + .activity-time { + font-size: 22rpx; + color: #9ca3af; + display: block; + } + } + } + } + } + } + + // Load More + .load-more { + padding: 32rpx 24rpx; + text-align: center; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.98); + } + + .load-more-text { + font-size: 26rpx; + color: #6b7280; + font-weight: 500; + } + } +} \ No newline at end of file diff --git a/client/src/pages/activity/index.vue b/client/src/pages/activity/index.vue new file mode 100644 index 0000000..a61dfd3 --- /dev/null +++ b/client/src/pages/activity/index.vue @@ -0,0 +1,202 @@ + + + \ No newline at end of file diff --git a/client/src/pages/favorites/index.config.ts b/client/src/pages/favorites/index.config.ts new file mode 100644 index 0000000..96fe731 --- /dev/null +++ b/client/src/pages/favorites/index.config.ts @@ -0,0 +1,6 @@ +export default definePageConfig({ + navigationBarTitleText: '我的收藏', + navigationBarBackgroundColor: '#ffffff', + navigationBarTextStyle: 'black', + navigationStyle: 'default' +}) \ No newline at end of file diff --git a/client/src/pages/favorites/index.scss b/client/src/pages/favorites/index.scss new file mode 100644 index 0000000..1b04170 --- /dev/null +++ b/client/src/pages/favorites/index.scss @@ -0,0 +1,166 @@ +.favorites-page { + min-height: 100vh; + background: #f8fafc; + padding-bottom: 32rpx; +} + +.page-header { + background: #ffffff; + padding: 48rpx 32rpx 32rpx 32rpx; + border-bottom: 2rpx solid #f1f5f9; +} + +.header-title { + font-size: 36rpx; + font-weight: bold; + color: #1e293b; + display: block; + margin-bottom: 8rpx; +} + +.header-subtitle { + font-size: 26rpx; + color: #64748b; + display: block; +} + +.empty-state { + padding: 120rpx 32rpx; + min-height: 600rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.empty-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 24rpx; +} + +.empty-icon { + font-size: 96rpx; + margin-bottom: 16rpx; +} + +.empty-title { + font-size: 32rpx; + font-weight: 600; + color: #374151; +} + +.empty-subtitle { + font-size: 26rpx; + color: #6b7280; + line-height: 1.5; + max-width: 400rpx; +} + +.empty-button { + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + color: #ffffff; + padding: 24rpx 48rpx; + border-radius: 32rpx; + margin-top: 16rpx; + box-shadow: 0 8rpx 24rpx rgba(5, 199, 199, 0.2); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.empty-button:active { + transform: scale(0.95); +} + +.favorites-list { + padding: 16rpx; +} + +.venue-card { + background: #ffffff; + border-radius: 16rpx; + margin-bottom: 16rpx; + padding: 24rpx; + border: 2rpx solid #f1f5f9; +} + +.venue-info { + width: 100%; +} + +.venue-name { + font-size: 30rpx; + font-weight: bold; + color: #1e293b; + margin-bottom: 8rpx; + display: block; +} + +.venue-address { + font-size: 24rpx; + color: #64748b; + line-height: 1.4; + margin-bottom: 16rpx; + display: block; +} + +.venue-footer { + display: flex; + align-items: center; + justify-content: space-between; +} + +.venue-distance { + font-size: 22rpx; + color: #05c7c7; + font-weight: 500; +} + +.venue-actions { + display: flex; + gap: 12rpx; +} + +.action-btn { + padding: 12rpx 20rpx; + border-radius: 20rpx; + font-size: 22rpx; + font-weight: 500; + transition: all 0.3s ease; +} + +.action-btn:active { + transform: scale(0.95); +} + +.remove-btn { + background: #f8f9fa; + color: #6c757d; + border: 2rpx solid #e9ecef; +} + +.remove-btn:active { + background: #e9ecef; +} + +.zone-btn { + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + color: #ffffff; +} + +.zone-btn:active { + background: linear-gradient(135deg, #04b5b5 0%, #039a9a 100%); +} + +.btn-text { + font-size: 22rpx; +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} \ No newline at end of file diff --git a/client/src/pages/favorites/index.vue b/client/src/pages/favorites/index.vue new file mode 100644 index 0000000..7b6ed61 --- /dev/null +++ b/client/src/pages/favorites/index.vue @@ -0,0 +1,113 @@ + + + \ No newline at end of file diff --git a/client/src/pages/history/index.config.ts b/client/src/pages/history/index.config.ts new file mode 100644 index 0000000..f37d4ef --- /dev/null +++ b/client/src/pages/history/index.config.ts @@ -0,0 +1,6 @@ +export default definePageConfig({ + navigationBarTitleText: '观看历史', + navigationBarBackgroundColor: '#ffffff', + navigationBarTextStyle: 'black', + navigationStyle: 'default' +}) \ No newline at end of file diff --git a/client/src/pages/history/index.scss b/client/src/pages/history/index.scss new file mode 100644 index 0000000..d6a7bc9 --- /dev/null +++ b/client/src/pages/history/index.scss @@ -0,0 +1,233 @@ +.history-page { + min-height: 100vh; + background: #f8fafc; + padding-bottom: 32rpx; +} + +.page-header { + background: #ffffff; + padding: 48rpx 32rpx 32rpx 32rpx; + border-bottom: 2rpx solid #f1f5f9; +} + +.header-title { + font-size: 36rpx; + font-weight: bold; + color: #1e293b; + display: block; + margin-bottom: 8rpx; +} + +.header-subtitle { + font-size: 26rpx; + color: #64748b; + display: block; +} + +.empty-state { + padding: 120rpx 32rpx; + min-height: 600rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.empty-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 24rpx; +} + +.empty-icon { + font-size: 96rpx; + margin-bottom: 16rpx; +} + +.empty-title { + font-size: 32rpx; + font-weight: 600; + color: #374151; +} + +.empty-subtitle { + font-size: 26rpx; + color: #6b7280; + line-height: 1.5; + max-width: 400rpx; +} + +.empty-button { + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + color: #ffffff; + padding: 24rpx 48rpx; + border-radius: 32rpx; + margin-top: 16rpx; + box-shadow: 0 8rpx 24rpx rgba(5, 199, 199, 0.2); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.empty-button:active { + transform: scale(0.95); +} + +.history-list { + padding: 16rpx; +} + +.video-card { + background: #ffffff; + border-radius: 16rpx; + margin-bottom: 16rpx; + padding: 16rpx; + border: 2rpx solid #f1f5f9; + display: flex; + gap: 16rpx; + transition: all 0.3s ease; +} + +.video-card:active { + background: #f8fafc; + transform: translateY(-1rpx); +} + +.video-thumbnail { + position: relative; + width: 200rpx; + height: 120rpx; + flex-shrink: 0; +} + +.thumbnail-placeholder { + width: 100%; + height: 100%; + border-radius: 12rpx; + overflow: hidden; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.ronin-bg { + background: linear-gradient(135deg, #3b82f6 0%, #dc2626 100%); +} + +.panda-bg { + background: linear-gradient(135deg, #8b5cf6 0%, #06b6d4 100%); +} + +.tennis-bg { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); +} + +.play-overlay { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 48rpx; + height: 48rpx; + background: rgba(0, 0, 0, 0.6); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.play-icon { + color: #ffffff; + font-size: 20rpx; + margin-left: 4rpx; +} + +.video-duration { + position: absolute; + bottom: 8rpx; + right: 8rpx; + background: rgba(0, 0, 0, 0.8); + color: #ffffff; + padding: 4rpx 8rpx; + border-radius: 6rpx; + font-size: 18rpx; +} + + + +.video-info { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 8rpx; +} + +.venue-name { + font-size: 28rpx; + font-weight: 600; + color: #1e293b; + line-height: 1.3; +} + +.video-meta { + display: flex; + align-items: center; + gap: 16rpx; +} + +.watch-time { + font-size: 22rpx; + color: #64748b; +} + +.video-duration-text { + font-size: 22rpx; + color: #9ca3af; +} + +.video-actions { + width: 48rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: all 0.3s ease; +} + +.video-actions:active { + background: #f3f4f6; +} + +.more-icon { + font-size: 32rpx; + color: #9ca3af; + transform: rotate(90deg); +} + +.clear-section { + padding: 48rpx 32rpx; + display: flex; + justify-content: center; +} + +.clear-button { + background: #f8f9fa; + color: #dc3545; + padding: 20rpx 40rpx; + border-radius: 24rpx; + border: 2rpx solid #dc3545; + transition: all 0.3s ease; +} + +.clear-button:active { + background: #dc3545; + color: #ffffff; + transform: scale(0.95); +} + +.clear-text { + font-size: 26rpx; + font-weight: 500; +} \ No newline at end of file diff --git a/client/src/pages/history/index.vue b/client/src/pages/history/index.vue new file mode 100644 index 0000000..9265cb5 --- /dev/null +++ b/client/src/pages/history/index.vue @@ -0,0 +1,165 @@ + + + \ No newline at end of file diff --git a/client/src/pages/index/index.scss b/client/src/pages/index/index.scss index 803eeb0..a04f97f 100644 --- a/client/src/pages/index/index.scss +++ b/client/src/pages/index/index.scss @@ -1,3 +1,22 @@ +// 全局动画 +@keyframes pulse { + 0%, 100% { + opacity: 0.8; + } + 50% { + opacity: 0.4; + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + .limo-mobile { min-height: 100vh; background: #ffffff; @@ -237,6 +256,12 @@ flex: 1; color: #6B7280; font-size: 24rpx; + + &.location-loading { + color: #9CA3AF; + opacity: 0.8; + animation: pulse 1.5s infinite; + } } .location-switch { @@ -247,6 +272,69 @@ } } + // 位置状态提示页面 + .location-status-section { + padding: 120rpx 32rpx; + min-height: 600rpx; + display: flex; + align-items: center; + justify-content: center; + + .status-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 32rpx; + + .loading-spinner { + width: 80rpx; + height: 80rpx; + border: 6rpx solid #f3f3f3; + border-top: 6rpx solid #05C7C7; + border-radius: 50%; + animation: spin 1s linear infinite; + } + + .status-icon { + font-size: 80rpx; + opacity: 0.5; + } + + .status-title { + font-size: 32rpx; + font-weight: 600; + color: #374151; + } + + .status-subtitle { + font-size: 24rpx; + color: #6B7280; + line-height: 1.5; + max-width: 400rpx; + } + + .retry-button { + background: linear-gradient(135deg, #05C7C7 0%, #04B5B5 100%); + color: #ffffff; + padding: 24rpx 48rpx; + border-radius: 32rpx; + margin-top: 24rpx; + box-shadow: 0 8rpx 24rpx rgba(5, 199, 199, 0.2); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.95); + } + + .retry-text { + font-size: 28rpx; + font-weight: 600; + } + } + } + } + // Venue Cards with premium styling .venue-section { padding: 0 32rpx; @@ -255,7 +343,7 @@ // Timeline .timeline-container { position: absolute; - left: 80rpx; + left: 60rpx; top: 0; bottom: 0; width: 2rpx; @@ -265,7 +353,7 @@ top: 0; bottom: 0; width: 2rpx; - background: linear-gradient(180deg, rgba(5, 199, 199, 0.3) 0%, #E5E7EB 30%, #E5E7EB 100%); + background: linear-gradient(180deg, rgba(5, 199, 199, 0.3) 0%, #E5E7EB 30%, #E5E7EB 80%, rgba(229, 231, 235, 0) 100%); } } @@ -275,7 +363,7 @@ .timeline-dot { position: absolute; - left: 38rpx; + left: 18rpx; top: 10rpx; width: 24rpx; height: 24rpx; @@ -292,7 +380,7 @@ } .venue-meta { - margin-left: 96rpx; + margin-left: 76rpx; margin-bottom: 24rpx; display: flex; align-items: center; @@ -328,7 +416,7 @@ } .venue-content { - margin-left: 96rpx; + margin-left: 76rpx; background: linear-gradient(135deg, #ffffff 0%, rgba(248, 250, 252, 0.5) 100%); border-radius: 48rpx; overflow: hidden; @@ -962,7 +1050,7 @@ .stats-grid { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(2, 1fr); gap: 24rpx; .stat-card { @@ -971,6 +1059,14 @@ padding: 24rpx; text-align: center; border: 2rpx solid #F3F4F6; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + cursor: pointer; + + &:active { + background: #F9FAFB; + transform: scale(0.98); + border-color: #05C7C7; + } .stat-number { font-size: 48rpx; @@ -1150,17 +1246,24 @@ } } - .activity-points { - font-size: 20rpx; - color: #05C7C7; - font-weight: 600; - background: #F0FDFD; - padding: 6rpx 12rpx; - border-radius: 12rpx; - border: 1rpx solid #CCFBF1; - white-space: nowrap; - margin-top: 2rpx; - } + + } + } + + .activity-more-link { + margin-top: 24rpx; + text-align: center; + padding: 16rpx; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.98); + } + + .more-link-text { + font-size: 26rpx; + color: #05C7C7; + font-weight: 500; } } } diff --git a/client/src/pages/index/index.vue b/client/src/pages/index/index.vue index bc36d37..28199d1 100644 --- a/client/src/pages/index/index.vue +++ b/client/src/pages/index/index.vue @@ -57,13 +57,35 @@ 📍 - 杭州市文一西路未来科技城万利大厦0701 - 切换 + {{ currentLocation.address }} + {{ locationLoading ? '定位中' : (currentLocation.address.includes('请选择') ? '选择' : '切换') }} - - + + + + + + 正在定位 + 获取您的位置信息,为您推荐附近场馆 + + + + + + + 📍 + 选择您的位置 + 在地图上选择位置,为您推荐附近的运动场馆 + + 在地图上选择 + + + + + + @@ -215,18 +237,14 @@ - + 12 - 观看场次 + 观看数量 - + 5 收藏场馆 - - 28 - 观看时长(h) - @@ -235,7 +253,7 @@ - 我的下载 + 我的收藏 @@ -256,6 +274,13 @@ + + + + 我的订单 + + + @@ -272,16 +297,17 @@ 观看了 RONIN黄金篮球馆 2小时前 · 观看时长 45分钟 - +10 收藏了 Panda惊怒熊猫运动俱乐部 1天前 - +5 + + 查看历史活动 + @@ -304,12 +330,23 @@ 我的 + + + diff --git a/client/src/pages/notifications/index.config.ts b/client/src/pages/notifications/index.config.ts new file mode 100644 index 0000000..e782c15 --- /dev/null +++ b/client/src/pages/notifications/index.config.ts @@ -0,0 +1,6 @@ +export default definePageConfig({ + navigationBarTitleText: '消息通知', + navigationBarBackgroundColor: '#ffffff', + navigationBarTextStyle: 'black', + navigationStyle: 'default' +}) \ No newline at end of file diff --git a/client/src/pages/notifications/index.scss b/client/src/pages/notifications/index.scss new file mode 100644 index 0000000..d76ad92 --- /dev/null +++ b/client/src/pages/notifications/index.scss @@ -0,0 +1,158 @@ +.notifications-page { + min-height: 100vh; + background: #f8fafc; + padding-bottom: 32rpx; +} + +.page-header { + background: #ffffff; + padding: 48rpx 32rpx 32rpx 32rpx; + border-bottom: 2rpx solid #f1f5f9; +} + +.header-title { + font-size: 36rpx; + font-weight: bold; + color: #1e293b; + display: block; + margin-bottom: 8rpx; +} + +.header-subtitle { + font-size: 26rpx; + color: #64748b; + display: block; +} + +.actions-bar { + background: #ffffff; + padding: 16rpx 32rpx; + border-bottom: 2rpx solid #f1f5f9; + display: flex; + justify-content: flex-end; + gap: 24rpx; +} + +.action-btn { + background: #f8fafc; + color: #64748b; + padding: 12rpx 24rpx; + border-radius: 16rpx; + border: 2rpx solid #e2e8f0; + transition: all 0.3s ease; +} + +.action-btn:active { + background: #e2e8f0; + transform: scale(0.95); +} + +.action-text { + font-size: 24rpx; + font-weight: 500; +} + +.empty-state { + padding: 120rpx 32rpx; + min-height: 600rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.empty-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 24rpx; +} + +.empty-icon { + font-size: 96rpx; + margin-bottom: 16rpx; + opacity: 0.6; +} + +.empty-title { + font-size: 32rpx; + font-weight: 600; + color: #374151; +} + +.empty-subtitle { + font-size: 26rpx; + color: #6b7280; + line-height: 1.5; + max-width: 400rpx; +} + +.notifications-list { + padding: 16rpx; +} + +.notification-card { + background: #ffffff; + border-radius: 16rpx; + margin-bottom: 16rpx; + padding: 24rpx; + border: 2rpx solid #f1f5f9; + position: relative; + transition: all 0.3s ease; +} + +.notification-card:active { + background: #f8fafc; + transform: translateY(-1rpx); +} + +.notification-card.unread { + border-color: #e0f2fe; + background: #fefeff; +} + +.notification-content { + width: 100%; +} + +.notification-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 8rpx; + gap: 16rpx; +} + +.notification-title { + font-size: 28rpx; + font-weight: 600; + color: #1e293b; + line-height: 1.3; +} + +.notification-time { + font-size: 20rpx; + color: #9ca3af; + white-space: nowrap; + margin-top: 2rpx; +} + +.notification-message { + font-size: 24rpx; + color: #64748b; + line-height: 1.5; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.unread-dot { + position: absolute; + top: 24rpx; + right: 24rpx; + width: 16rpx; + height: 16rpx; + background: #ef4444; + border-radius: 50%; +} \ No newline at end of file diff --git a/client/src/pages/notifications/index.vue b/client/src/pages/notifications/index.vue new file mode 100644 index 0000000..aeea841 --- /dev/null +++ b/client/src/pages/notifications/index.vue @@ -0,0 +1,180 @@ + + + \ No newline at end of file diff --git a/client/src/pages/orders/index.config.ts b/client/src/pages/orders/index.config.ts new file mode 100644 index 0000000..39e845d --- /dev/null +++ b/client/src/pages/orders/index.config.ts @@ -0,0 +1,6 @@ +export default definePageConfig({ + navigationBarTitleText: '我的订单', + navigationBarBackgroundColor: '#ffffff', + navigationBarTextStyle: 'black', + navigationStyle: 'default' +}) \ No newline at end of file diff --git a/client/src/pages/orders/index.scss b/client/src/pages/orders/index.scss new file mode 100644 index 0000000..83e9580 --- /dev/null +++ b/client/src/pages/orders/index.scss @@ -0,0 +1,323 @@ +.orders-page { + min-height: 100vh; + background: #f9fafb; + + // Status Tabs + .status-tabs { + background: #ffffff; + padding: 24rpx 24rpx 0 24rpx; + display: flex; + border-bottom: 2rpx solid #f3f4f6; + position: sticky; + top: 0; + z-index: 10; + + .tab-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + padding: 24rpx 16rpx; + position: relative; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.98); + } + + .tab-text { + font-size: 28rpx; + color: #6b7280; + font-weight: 500; + transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + + .tab-badge { + position: absolute; + top: 12rpx; + right: 12rpx; + background: #ef4444; + border-radius: 20rpx; + min-width: 32rpx; + height: 32rpx; + display: flex; + align-items: center; + justify-content: center; + padding: 0 8rpx; + + .badge-text { + color: #ffffff; + font-size: 20rpx; + font-weight: 600; + } + } + + &.active { + .tab-text { + color: #05c7c7; + font-weight: 600; + } + + &::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 64rpx; + height: 6rpx; + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + border-radius: 6rpx; + } + } + } + } + + // Empty State + .empty-state { + padding: 120rpx 48rpx; + display: flex; + justify-content: center; + + .empty-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + + .empty-icon { + font-size: 120rpx; + margin-bottom: 32rpx; + opacity: 0.6; + } + + .empty-title { + font-size: 32rpx; + font-weight: 600; + color: #374151; + margin-bottom: 16rpx; + } + + .empty-subtitle { + font-size: 26rpx; + color: #9ca3af; + margin-bottom: 48rpx; + line-height: 1.5; + } + + .empty-button { + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + color: #ffffff; + padding: 20rpx 40rpx; + border-radius: 28rpx; + box-shadow: 0 8rpx 24rpx rgba(5, 199, 199, 0.3); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.98); + } + + .button-text { + font-size: 26rpx; + font-weight: 600; + } + } + } + } + + // Orders List + .orders-list { + padding: 24rpx; + + .order-card { + background: #ffffff; + border-radius: 24rpx; + border: 2rpx solid #f3f4f6; + margin-bottom: 24rpx; + overflow: hidden; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.995); + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08); + } + + &:last-child { + margin-bottom: 0; + } + + .order-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + padding: 32rpx 32rpx 24rpx 32rpx; + border-bottom: 2rpx solid #f9fafb; + + .order-info { + display: flex; + flex-direction: column; + + .order-number { + font-size: 26rpx; + color: #6b7280; + margin-bottom: 8rpx; + } + + .order-date { + font-size: 24rpx; + color: #9ca3af; + } + } + + .order-status { + .status-text { + font-size: 24rpx; + font-weight: 600; + } + + &.pending { + .status-text { + color: #d97706; + } + } + + &.completed { + .status-text { + color: #047857; + } + } + + &.cancelled { + .status-text { + color: #dc2626; + } + } + } + } + + .order-content { + padding: 24rpx 32rpx 32rpx 32rpx; + + .member-info { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 32rpx; + + .member-card { + display: flex; + align-items: center; + flex: 1; + + .card-icon { + width: 80rpx; + height: 80rpx; + background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%); + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 24rpx; + + .icon-text { + font-size: 36rpx; + } + } + + .member-details { + display: flex; + flex-direction: column; + + .member-type { + font-size: 28rpx; + font-weight: 600; + color: #111827; + margin-bottom: 6rpx; + } + + .member-duration { + font-size: 24rpx; + color: #6b7280; + } + } + } + + .order-amount { + display: flex; + flex-direction: column; + align-items: flex-end; + + .amount-label { + font-size: 22rpx; + color: #9ca3af; + margin-bottom: 6rpx; + } + + .amount-value { + font-size: 32rpx; + font-weight: 700; + color: #ef4444; + } + } + } + + .order-actions { + display: flex; + gap: 16rpx; + justify-content: flex-end; + + .action-btn { + padding: 16rpx 24rpx; + border-radius: 20rpx; + border: 2rpx solid; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:active { + transform: scale(0.95); + } + + .btn-text { + font-size: 24rpx; + font-weight: 600; + } + + &.cancel-btn { + background: #ffffff; + border-color: #d1d5db; + + .btn-text { + color: #6b7280; + } + + &:active { + background: #f9fafb; + } + } + + &.pay-btn { + background: linear-gradient(135deg, #05c7c7 0%, #04b5b5 100%); + border-color: #05c7c7; + box-shadow: 0 4rpx 16rpx rgba(5, 199, 199, 0.3); + + .btn-text { + color: #ffffff; + } + } + + + + &.reorder-btn { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + border-color: #10b981; + box-shadow: 0 4rpx 16rpx rgba(16, 185, 129, 0.3); + + .btn-text { + color: #ffffff; + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/client/src/pages/orders/index.vue b/client/src/pages/orders/index.vue new file mode 100644 index 0000000..6464916 --- /dev/null +++ b/client/src/pages/orders/index.vue @@ -0,0 +1,310 @@ + + + \ No newline at end of file diff --git a/client/src/pages/vip/index.vue b/client/src/pages/vip/index.vue index f33b3a1..03b7332 100644 --- a/client/src/pages/vip/index.vue +++ b/client/src/pages/vip/index.vue @@ -13,42 +13,12 @@ - - - - - @@ -128,46 +98,7 @@ - - - - 用户好评 - - - - - - - 张先生 - - - - - - - - - "VIP服务真的很棒,预约场馆再也不用排队了!" - - - - - - - 李女士 - - - - - - - - - "专属客服响应很快,解决问题很及时,值得推荐!" - - - - + @@ -185,20 +116,6 @@ 立即开通VIP会员 - - - 🛡️ - 安全支付 - - - - - 随时取消 - - - 7天无理由退款 - - 开通即表示同意《VIP会员服务协议》 @@ -217,20 +134,10 @@ import './index.scss' const backIcon = require('@/assets/images/back.svg') // 响应式状态 -const selectedPlan = ref('monthly') +const selectedPlan = ref('quarterly') // 套餐数据 const plans = ref([ - { - id: 'daily', - name: '体验版', - duration: '1天', - price: 9.9, - originalPrice: 19.9, - discount: '限时5折', - popular: false, - colorClass: 'blue-gradient', - }, { id: 'monthly', name: '月度会员', @@ -238,7 +145,7 @@ const plans = ref([ price: 68, originalPrice: 98, discount: '7折优惠', - popular: true, + popular: false, colorClass: 'purple-gradient', }, { @@ -247,9 +154,9 @@ const plans = ref([ duration: '3个月', price: 168, originalPrice: 294, - discount: '超值4.3折', - popular: false, - colorClass: 'pink-gradient', + discount: '超值优惠', + popular: true, + colorClass: 'orange-gradient', }, { id: 'yearly', @@ -257,50 +164,32 @@ const plans = ref([ duration: '1年', price: 588, originalPrice: 1176, - discount: '超值5折', + discount: '最划算', popular: false, - colorClass: 'orange-gradient', + colorClass: 'pink-gradient', }, ]) // 特权数据 const privileges = ref([ - { - icon: '👑', - title: '专属身份标识', - desc: 'VIP专属标识,彰显尊贵身份', - colorClass: 'yellow-gradient', - }, { icon: '⚡', title: '优先预约权', desc: '热门场馆优先预约,不再排队等待', - colorClass: 'blue-gradient', + colorClass: 'orange-gradient', }, { icon: '🎁', title: '专属折扣', desc: '场馆预订享受VIP专属折扣优惠', - colorClass: 'green-gradient', + colorClass: 'purple-gradient', }, { icon: '🛡️', title: '免费取消', desc: '预约后可免费取消,无违约金', - colorClass: 'purple-gradient', - }, - { - icon: '⭐', - title: '专属客服', - desc: '7x24小时VIP专属客服服务', colorClass: 'pink-gradient', }, - { - icon: '✓', - title: '高清回放', - desc: '无限制观看高清比赛回放视频', - colorClass: 'indigo-gradient', - }, ]) // 计算属性:选中的套餐信息 diff --git a/client/src/pages/zone/index.scss b/client/src/pages/zone/index.scss index ffa1704..4cdb5c1 100644 --- a/client/src/pages/zone/index.scss +++ b/client/src/pages/zone/index.scss @@ -15,7 +15,11 @@ left: 0; right: 0; bottom: 0; - background: linear-gradient(135deg, #1f2937 0%, #3b82f6 50%, #ef4444 100%); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + filter: blur(2rpx); + transform: scale(1.1); z-index: 1; } @@ -25,7 +29,7 @@ left: 0; right: 0; bottom: 0; - background: rgba(0, 0, 0, 0.4); + background: rgba(0, 0, 0, 0.6); z-index: 2; } @@ -35,7 +39,7 @@ left: 0; right: 0; bottom: 0; - background: linear-gradient(180deg, rgba(0, 0, 0, 0.2) 0%, transparent 40%, rgba(0, 0, 0, 0.6) 100%); + background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, transparent 30%, rgba(0, 0, 0, 0.8) 100%); z-index: 3; } @@ -51,7 +55,7 @@ .control-btn { width: 64rpx; height: 64rpx; - background: rgba(0, 0, 0, 0.3); + background: rgba(255, 255, 255, 0.15); border-radius: 24rpx; display: flex; align-items: center; @@ -174,6 +178,9 @@ padding: 12rpx 24rpx; box-shadow: 0 8rpx 24rpx rgba(5, 199, 199, 0.3); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + justify-content: center; &:active { transform: scale(0.95); @@ -183,6 +190,7 @@ font-size: 24rpx; color: #ffffff; font-weight: 600; + line-height: 1; } } diff --git a/client/src/pages/zone/index.vue b/client/src/pages/zone/index.vue index 1bac619..4e3fce2 100644 --- a/client/src/pages/zone/index.vue +++ b/client/src/pages/zone/index.vue @@ -2,7 +2,7 @@ - + @@ -155,11 +155,20 @@ + + +