基于uni-app的超级大乐透号码生成器案例,从设计到实现的完整解析

1、引言

在移动应用开发领域,彩票类应用因其娱乐性和实用性而备受用户青睐。本文将深入分析一个基于uni-app框架开发的超级大乐透号码生成器,从产品设计、技术架构到具体实现,全面解析这个项目的亮点与价值。

## 更多学习资料:https://www.processon.com/mindmap/60504b5ff346fb348a93b4fa

先看效果图
在这里插入图片描述
在这里插入图片描述

2、项目概述

这是一个功能完整的超级大乐透号码模拟生成应用,具备号码生成、历史记录、数据分析、一键复制等核心功能。项目采用Vue.js + uni-app技术栈,支持多端部署,体现了现代前端开发的最佳实践。

3、技术架构分析

1. 技术选型的智慧

uni-app框架的选择

  • 一套代码,多端运行(H5、小程序、APP)
  • 基于Vue.js,开发体验优秀
  • 丰富的API支持,满足跨平台需求

Vue.js组件化设计

export default {
  data() {
    return {
      currentNumbers: {
        front: [], // 前区5个号码
        back: []   // 后区2个号码
      },
      historyNumbers: [],
      periodCounter: 1
    }
  }
}

2. 数据结构设计的巧思

项目采用了清晰的数据结构设计:

  • currentNumbers: 当前生成的号码对象
  • historyNumbers: 历史记录数组
  • periodCounter: 期数计数器

这种设计既保证了数据的完整性,又便于后续的扩展和维护。

4、核心功能实现解析

1. 智能号码生成算法

generateNumbers() {
  // 生成前区号码(1-35选5个)
  const frontNumbers = []
  while (frontNumbers.length < 5) {
    const num = Math.floor(Math.random() * 35) + 1
    if (!frontNumbers.includes(num)) {
      frontNumbers.push(num)
    }
  }
  frontNumbers.sort((a, b) => a - b)
  
  // 生成后区号码(1-12选2个)
  const backNumbers = []
  while (backNumbers.length < 2) {
    const num = Math.floor(Math.random() * 12) + 1
    if (!backNumbers.includes(num)) {
      backNumbers.push(num)
    }
  }
  backNumbers.sort((a, b) => a - b)
}

算法亮点:

  • 使用while循环确保不重复
  • 自动排序,提升用户体验
  • 严格按照大乐透规则实现

2. 跨平台复制功能

copyNumbers() {
  const frontStr = this.currentNumbers.front.map(num => num.toString().padStart(2, '0')).join(' ')
  const backStr = this.currentNumbers.back.map(num => num.toString().padStart(2, '0')).join(' ')
  const copyText = `前区:${frontStr} 后区:${backStr}`
  
  // #ifdef H5
  if (navigator.clipboard) {
    navigator.clipboard.writeText(copyText)
  }
  // #endif
  
  // #ifdef APP-PLUS || MP-WEIXIN || MP-ALIPAY
  uni.setClipboardData({
    data: copyText,
    success: () => {
      uni.showToast({ title: '号码已复制', icon: 'success' })
    }
  })
  // #endif
}

技术特色:

  • 条件编译实现跨平台兼容
  • 格式化输出,用户友好
  • 完善的错误处理机制

3. 本地存储与数据持久化

saveHistory() {
  try {
    uni.setStorageSync('lottery_history', this.historyNumbers)
    uni.setStorageSync('lottery_counter', this.periodCounter)
  } catch (e) {
    console.log('保存历史记录失败:', e)
  }
}

loadHistory() {
  try {
    const history = uni.getStorageSync('lottery_history')
    const counter = uni.getStorageSync('lottery_counter')
    if (history) this.historyNumbers = history
    if (counter) this.periodCounter = counter
  } catch (e) {
    console.log('加载历史记录失败:', e)
  }
}

设计优势:

  • 异常处理保证应用稳定性
  • 数据分离存储,便于管理
  • 自动加载,无缝用户体验

5、UI/UX设计亮点

1. 现代化的视觉设计

.lottery-page {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  flex-direction: column;
}

.number-ball {
  background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
  border-radius: 32rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

设计特色:

  • 渐变色彩搭配,视觉层次丰富
  • 圆角设计,符合现代审美
  • 阴影效果,增强立体感

2. 响应式布局设计

@media (max-width: 750rpx) {
  .content-scroll {
    padding: 0 16rpx 16rpx;
  }
  .analysis-grid {
    grid-template-columns: 1fr;
    gap: 16rpx;
  }
}

适配策略:

  • 媒体查询实现响应式
  • 网格布局自适应调整
  • 间距动态优化

6、完整代码

<template>
  <view class="lottery-page">
    <!-- 头部标题 -->
    <view class="header">
      <text class="title">🎱 超级大乐透</text>
      <text class="subtitle">号码模拟与历史记录</text>
    </view>

    <scroll-view class="content-scroll" scroll-y="true">
      <!-- 当前号码生成卡片 -->
      <view class="card-container">
        <view class="card-header">
          <view class="icon-wrapper generate-icon">
            <text class="icon">🎲</text>
          </view>
          <view class="header-content">
            <text class="card-title">号码生成</text>
            <text class="card-subtitle">点击生成随机中奖号码</text>
          </view>
        </view>
        <view class="card-body">
          <!-- 当前生成的号码显示 -->
          <view class="current-numbers" v-if="currentNumbers.front.length > 0">
            <view class="numbers-header">
              <text class="numbers-title">当前号码</text>
              <button class="copy-btn" @click="copyNumbers">
                <text class="copy-icon">📋</text>
                <text class="copy-text">复制</text>
              </button>
            </view>
            <view class="numbers-section">
              <text class="section-label">前区号码</text>
              <view class="numbers-row">
                <view 
                  v-for="(num, index) in currentNumbers.front" 
                  :key="index"
                  class="number-ball front-ball"
                >
                  <text class="number-text">{{ num.toString().padStart(2, '0') }}</text>
                </view>
              </view>
            </view>
            <view class="numbers-section">
              <text class="section-label">后区号码</text>
              <view class="numbers-row">
                <view 
                  v-for="(num, index) in currentNumbers.back" 
                  :key="index"
                  class="number-ball back-ball"
                >
                  <text class="number-text">{{ num.toString().padStart(2, '0') }}</text>
                </view>
              </view>
            </view>
          </view>
          
          <!-- 生成按钮 -->
          <view class="generate-actions">
            <button class="generate-btn primary" @click="generateNumbers">
              <text class="btn-icon">🎯</text>
              <text class="btn-text">生成号码</text>
            </button>
            <button class="generate-btn secondary" @click="saveCurrentNumbers" :disabled="currentNumbers.front.length === 0">
              <text class="btn-icon">💾</text>
              <text class="btn-text">保存号码</text>
            </button>
          </view>
        </view>
      </view>

      <!-- 号码分析卡片 -->
      <view class="card-container" v-if="historyNumbers.length > 0">
        <view class="card-header">
          <view class="icon-wrapper analysis-icon">
            <text class="icon">📊</text>
          </view>
          <view class="header-content">
            <text class="card-title">号码分析</text>
            <text class="card-subtitle">基于历史记录的统计分析</text>
          </view>
        </view>
        <view class="card-body">
          <view class="analysis-grid">
            <view class="analysis-item">
              <text class="analysis-label">总期数</text>
              <text class="analysis-value">{{ historyNumbers.length }}</text>
            </view>
            <view class="analysis-item">
              <text class="analysis-label">最热号码</text>
              <text class="analysis-value">{{ getHotNumbers() }}</text>
            </view>
            <view class="analysis-item">
              <text class="analysis-label">最冷号码</text>
              <text class="analysis-value">{{ getColdNumbers() }}</text>
            </view>
            <view class="analysis-item">
              <text class="analysis-label">平均和值</text>
              <text class="analysis-value">{{ getAverageSum() }}</text>
            </view>
          </view>
        </view>
      </view>

      <!-- 历史记录卡片 -->
      <view class="card-container">
        <view class="card-header">
          <view class="icon-wrapper history-icon">
            <text class="icon">📚</text>
          </view>
          <view class="header-content">
            <text class="card-title">历史记录</text>
            <text class="card-subtitle">往期开奖号码记录</text>
          </view>
          <button class="header-btn" @click="clearHistory" v-if="historyNumbers.length > 0">
            <text class="btn-text">清空</text>
          </button>
        </view>
        <view class="card-body">
          <view class="history-list" v-if="historyNumbers.length > 0">
            <view 
              v-for="(record, index) in historyNumbers" 
              :key="index"
              class="history-item"
            >
              <view class="history-header">
                <text class="period-text">{{ record.period }}</text>
                <text class="time-text">{{ formatTime(record.time) }}</text>
              </view>
              <view class="history-numbers">
                <view class="numbers-section">
                  <view class="numbers-row">
                    <view 
                      v-for="(num, numIndex) in record.front" 
                      :key="numIndex"
                      class="number-ball front-ball small"
                    >
                      <text class="number-text">{{ num.toString().padStart(2, '0') }}</text>
                    </view>
                    <text class="separator">+</text>
                    <view 
                      v-for="(num, numIndex) in record.back" 
                      :key="numIndex"
                      class="number-ball back-ball small"
                    >
                      <text class="number-text">{{ num.toString().padStart(2, '0') }}</text>
                    </view>
                  </view>
                </view>
              </view>
            </view>
          </view>
          <view class="empty-state" v-else>
            <text class="empty-icon">🎱</text>
            <text class="empty-text">暂无历史记录</text>
            <text class="empty-desc">生成号码后会自动保存到历史记录</text>
          </view>
        </view>
      </view>

      <!-- 玩法说明卡片 -->
      <view class="card-container">
        <view class="card-header">
          <view class="icon-wrapper info-icon">
            <text class="icon">ℹ️</text>
          </view>
          <view class="header-content">
            <text class="card-title">玩法说明</text>
            <text class="card-subtitle">超级大乐透规则介绍</text>
          </view>
        </view>
        <view class="card-body">
          <view class="rule-list">
            <view class="rule-item">
              <text class="rule-icon">🔴</text>
              <text class="rule-text">前区:从01-35中选择5个号码</text>
            </view>
            <view class="rule-item">
              <text class="rule-icon">🔵</text>
              <text class="rule-text">后区:从01-12中选择2个号码</text>
            </view>
            <view class="rule-item">
              <text class="rule-icon">💰</text>
              <text class="rule-text">每注2元,追加1</text>
            </view>
            <view class="rule-item">
              <text class="rule-icon">🏆</text>
              <text class="rule-text">一等奖:前区5+后区2</text>
            </view>
          </view>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      // 当前生成的号码
      currentNumbers: {
        front: [], // 前区5个号码
        back: []   // 后区2个号码
      },
      
      // 历史记录
      historyNumbers: [],
      
      // 期数计数器
      periodCounter: 1
    }
  },
  
  onLoad() {
    this.loadHistory()
  },
  
  onUnload() {
    this.saveHistory()
  },
  
  methods: {
    // 生成随机号码
    generateNumbers() {
      // 生成前区号码(1-35选5个)
      const frontNumbers = []
      while (frontNumbers.length < 5) {
        const num = Math.floor(Math.random() * 35) + 1
        if (!frontNumbers.includes(num)) {
          frontNumbers.push(num)
        }
      }
      frontNumbers.sort((a, b) => a - b)
      
      // 生成后区号码(1-12选2个)
      const backNumbers = []
      while (backNumbers.length < 2) {
        const num = Math.floor(Math.random() * 12) + 1
        if (!backNumbers.includes(num)) {
          backNumbers.push(num)
        }
      }
      backNumbers.sort((a, b) => a - b)
      
      this.currentNumbers = {
        front: frontNumbers,
        back: backNumbers
      }
      
      // 生成动画效果
      uni.vibrateShort()
      
      uni.showToast({
        title: '号码生成成功!',
        icon: 'success',
        duration: 1500
      })
    },
    
    // 保存当前号码到历史记录
    saveCurrentNumbers() {
      if (this.currentNumbers.front.length === 0) {
        uni.showToast({
          title: '请先生成号码',
          icon: 'none'
        })
        return
      }
      
      const record = {
        period: this.periodCounter++,
        front: [...this.currentNumbers.front],
        back: [...this.currentNumbers.back],
        time: new Date().getTime()
      }
      
      this.historyNumbers.unshift(record)
      
      // 最多保留50期记录
      if (this.historyNumbers.length > 50) {
        this.historyNumbers = this.historyNumbers.slice(0, 50)
      }
      
      this.saveHistory()
      
      uni.showToast({
        title: '号码已保存',
        icon: 'success'
      })
    },
    
    // 清空历史记录
    clearHistory() {
      uni.showModal({
        title: '确认清空',
        content: '确定要清空所有历史记录吗?',
        success: (res) => {
          if (res.confirm) {
            this.historyNumbers = []
            this.periodCounter = 1
            this.saveHistory()
            uni.showToast({
              title: '已清空历史记录',
              icon: 'success'
            })
          }
        }
      })
    },
    
    // 加载历史记录
    loadHistory() {
      try {
        const history = uni.getStorageSync('lottery_history')
        const counter = uni.getStorageSync('lottery_counter')
        if (history) {
          this.historyNumbers = history
        }
        if (counter) {
          this.periodCounter = counter
        }
      } catch (e) {
        console.log('加载历史记录失败:', e)
      }
    },
    
    // 保存历史记录
    saveHistory() {
      try {
        uni.setStorageSync('lottery_history', this.historyNumbers)
        uni.setStorageSync('lottery_counter', this.periodCounter)
      } catch (e) {
        console.log('保存历史记录失败:', e)
      }
    },
    
    // 格式化时间
    formatTime(timestamp) {
      const date = new Date(timestamp)
      const now = new Date()
      const diff = now.getTime() - timestamp
      
      if (diff < 60000) return '刚刚'
      if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前'
      if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前'
      
      return date.toLocaleDateString() + ' ' + date.toLocaleTimeString().slice(0, 5)
    },
    
    // 获取最热号码
    getHotNumbers() {
      if (this.historyNumbers.length === 0) return '-'
      
      const frequency = {}
      this.historyNumbers.forEach(record => {
        [...record.front, ...record.back].forEach(num => {
          frequency[num] = (frequency[num] || 0) + 1
        })
      })
      
      const sorted = Object.entries(frequency).sort((a, b) => b[1] - a[1])
      return sorted.slice(0, 3).map(item => item[0]).join(', ')
    },
    
    // 获取最冷号码
    getColdNumbers() {
      if (this.historyNumbers.length === 0) return '-'
      
      const frequency = {}
      // 初始化所有号码频次为0
      for (let i = 1; i <= 35; i++) {
        frequency[i] = 0
      }
      
      this.historyNumbers.forEach(record => {
        [...record.front, ...record.back].forEach(num => {
          frequency[num] = (frequency[num] || 0) + 1
        })
      })
      
      const sorted = Object.entries(frequency).sort((a, b) => a[1] - b[1])
      return sorted.slice(0, 3).map(item => item[0]).join(', ')
    },
    
    // 获取平均和值
    getAverageSum() {
      if (this.historyNumbers.length === 0) return '-'
      
      const totalSum = this.historyNumbers.reduce((sum, record) => {
        const frontSum = record.front.reduce((a, b) => a + b, 0)
        const backSum = record.back.reduce((a, b) => a + b, 0)
        return sum + frontSum + backSum
      }, 0)
      
      return Math.round(totalSum / this.historyNumbers.length)
    },
        // 复制号码到剪贴板
    copyNumbers() {
      if (this.currentNumbers.front.length === 0) {
        uni.showToast({
          title: '请先生成号码',
          icon: 'none'
        })
        return
      }
      
      const frontStr = this.currentNumbers.front.map(num => num.toString().padStart(2, '0')).join(' ')
      const backStr = this.currentNumbers.back.map(num => num.toString().padStart(2, '0')).join(' ')
      const copyText = `前区:${frontStr} 后区:${backStr}`
      
      // #ifdef H5
      if (navigator.clipboard) {
        navigator.clipboard.writeText(copyText).then(() => {
          uni.showToast({
            title: '号码已复制',
            icon: 'success'
          })
        }).catch(() => {
          this.fallbackCopy(copyText)
        })
      } else {
        this.fallbackCopy(copyText)
      }
      // #endif
      
      // #ifdef APP-PLUS || MP-WEIXIN || MP-ALIPAY
      uni.setClipboardData({
        data: copyText,
        success: () => {
          uni.showToast({
            title: '号码已复制',
            icon: 'success'
          })
        },
        fail: () => {
          uni.showToast({
            title: '复制失败',
            icon: 'none'
          })
        }
      })
      // #endif
    },
    
    // H5环境下的备用复制方法
    fallbackCopy(text) {
      const textArea = document.createElement('textarea')
      textArea.value = text
      document.body.appendChild(textArea)
      textArea.select()
      try {
        document.execCommand('copy')
        uni.showToast({
          title: '号码已复制',
          icon: 'success'
        })
      } catch (err) {
        uni.showToast({
          title: '复制失败',
          icon: 'none'
        })
      }
      document.body.removeChild(textArea)
    },
  }
}
</script>

<style>
/* 号码头部 */
.numbers-header {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 24rpx;
}

.numbers-title {
  font-size: 28rpx;
  font-weight: 600;
  color: #1d1d1f;
}

.copy-btn {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8rpx;
  padding: 12rpx 20rpx;
  border-radius: 20rpx;
  background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
  border: none;
  font-size: 24rpx;
  color: #ffffff;
  transition: all 0.2s ease;
}

.copy-btn:active {
  transform: scale(0.95);
  opacity: 0.8;
}

.copy-icon {
  font-size: 24rpx;
}

.copy-text {
  font-size: 24rpx;
  font-weight: 500;
}

.lottery-page {
  width: 100%;
  height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  flex-direction: column;
}

/* 头部 */
.header {
  padding: 60rpx 32rpx 40rpx;
  text-align: center;
  color: #ffffff;
}

.title {
  font-size: 48rpx;
  font-weight: 700;
  display: block;
  margin-bottom: 8rpx;
}

.subtitle {
  font-size: 28rpx;
  opacity: 0.9;
  display: block;
}

/* 内容滚动区域 */
.content-scroll {
  flex: 1;
  padding: 0 24rpx 24rpx;
}

/* 卡片容器 */
.card-container {
  margin-bottom: 24rpx;
  border-radius: 24rpx;
  background: #ffffff;
  overflow: hidden;
  box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
}

/* 卡片头部 */
.card-header {
  display: flex;
  flex-direction: row;
  align-items: center;
  padding: 28rpx 32rpx;
  border-bottom: 1rpx solid #f0f0f5;
  background: linear-gradient(135deg, #f8f9fc 0%, #ffffff 100%);
}

.icon-wrapper {
  width: 72rpx;
  height: 72rpx;
  border-radius: 16rpx;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  margin-right: 24rpx;
  flex-shrink: 0;
}

.generate-icon {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.analysis-icon {
  background: linear-gradient(135deg, #ffeaa7 0%, #fab1a0 100%);
}

.history-icon {
  background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
}

.info-icon {
  background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
}

.icon {
  font-size: 36rpx;
  color: #ffffff;
}

.header-content {
  flex: 1;
}

.card-title {
  font-size: 32rpx;
  font-weight: 600;
  color: #1d1d1f;
  margin-bottom: 4rpx;
  display: block;
}

.card-subtitle {
  font-size: 24rpx;
  color: #8e8e93;
  display: block;
}

.header-btn {
  padding: 12rpx 24rpx;
  border-radius: 16rpx;
  background: #f2f2f7;
  border: none;
  font-size: 24rpx;
  color: #007aff;
}

/* 卡片内容 */
.card-body {
  padding: 32rpx;
}

/* 当前号码显示 */
.current-numbers {
  margin-bottom: 32rpx;
}

.numbers-section {
  margin-bottom: 24rpx;
}

.section-label {
  font-size: 24rpx;
  color: #8e8e93;
  margin-bottom: 16rpx;
  display: block;
  font-weight: 500;
}

.numbers-row {
  display: flex;
  align-items: center;
  gap: 12rpx;
  flex-wrap: wrap;
  flex-direction: row;
}

.number-ball {
  width: 64rpx;
  height: 64rpx;
  border-radius: 32rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

.number-ball.small {
  width: 48rpx;
  height: 48rpx;
  border-radius: 24rpx;
}

.front-ball {
  background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
  color: #ffffff;
}

.back-ball {
  background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
  color: #ffffff;
}

.number-text {
  font-size: 24rpx;
  font-weight: 600;
}

.separator {
  font-size: 32rpx;
  color: #8e8e93;
  font-weight: 600;
  margin: 0 8rpx;
}

/* 生成按钮 */
.generate-actions {
  display: flex;
  gap: 16rpx;
  flex-direction: row;
}

.generate-btn {
  flex: 1;
  height: 88rpx;
  border-radius: 16rpx;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12rpx;
  font-size: 28rpx;
  font-weight: 600;
  transition: all 0.2s ease;
}

.generate-btn.primary {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: #ffffff;
}

.generate-btn.secondary {
  background: #f2f2f7;
  color: #007aff;
}

.generate-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.generate-btn:active:not(:disabled) {
  transform: scale(0.95);
}

.btn-icon {
  font-size: 28rpx;
}

.btn-text {
  font-size: 28rpx;
}

/* 分析网格 */
.analysis-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24rpx;
}

.analysis-item {
  background: #f8f9fc;
  border-radius: 16rpx;
  padding: 24rpx;
  text-align: center;
}

.analysis-label {
  font-size: 24rpx;
  color: #8e8e93;
  margin-bottom: 8rpx;
  display: block;
}

.analysis-value {
  font-size: 32rpx;
  font-weight: 600;
  color: #1d1d1f;
  display: block;
}

/* 历史记录 */
.history-list {
  max-height: 600rpx;
  overflow-y: auto;
}

.history-item {
  background: #f8f9fc;
  border-radius: 16rpx;
  padding: 24rpx;
  margin-bottom: 16rpx;
}

.history-header {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16rpx;
}

.period-text {
  font-size: 28rpx;
  font-weight: 600;
  color: #1d1d1f;
}

.time-text {
  font-size: 24rpx;
  color: #8e8e93;
}

/* 空状态 */
.empty-state {
  text-align: center;
  padding: 60rpx 32rpx;
}

.empty-icon {
  font-size: 80rpx;
  display: block;
  margin-bottom: 16rpx;
}

.empty-text {
  font-size: 32rpx;
  color: #8e8e93;
  margin-bottom: 8rpx;
  display: block;
}

.empty-desc {
  font-size: 24rpx;
  color: #c7c7cc;
  display: block;
}

/* 规则说明 */
.rule-list {
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}

.rule-item {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 16rpx;
  padding: 16rpx;
  background: #f8f9fc;
  border-radius: 12rpx;
}

.rule-icon {
  font-size: 32rpx;
  flex-shrink: 0;
}

.rule-text {
  font-size: 28rpx;
  color: #1d1d1f;
  line-height: 1.4;
}

/* 响应式设计 */
@media (max-width: 750rpx) {
  .content-scroll {
    padding: 0 16rpx 16rpx;
  }
  
  .card-header {
    padding: 24rpx 28rpx;
  }
  
  .card-body {
    padding: 28rpx;
  }
  
  .analysis-grid {
    grid-template-columns: 1fr;
    gap: 16rpx;
  }
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值