Bladeren bron

分页修改

hjr 2 maanden geleden
bovenliggende
commit
7e3f721353
3 gewijzigde bestanden met toevoegingen van 505 en 187 verwijderingen
  1. 264 175
      src/pages/book-content/index.ux
  2. 7 12
      src/pages/book-detail/index.ux
  3. 234 0
      src/pages/reader-test/index.ux

+ 264 - 175
src/pages/book-content/index.ux

@@ -15,10 +15,14 @@
         <!-- <list-item type="content" onclick="showOrHidePop" show="{{!!content}}">
           <text class="book-text" style="{{contentStyle}}">{{ content }}</text>
         </list-item> -->
-        <list-item type="content" show="{{!!content}}"  onclick="showOrHidePop">
+        <!-- show="{{!!content}}"  onclick="showOrHidePop" -->
+        <list-item type="content" >
           <div class="doc-page">
-            <richtext scene="book" if="showRich" style="{{contentStyle}}" @splitpage="pageEndFunction" id="book" @pagechanged="pageChangeFunction">
-              {{ content }}</richtext>
+            <swiper class="swiper" style="{{swiperStyle}}" index="{{currentPage - 1}}" @change="onSwiperChange" indicator="false" loop="false">
+              <div for="{{pages}}" class="swiper-page">
+                <text class="book-text" style="{{contentStyle}}">{{ $item }}</text>
+              </div>
+            </swiper>
             <text if="{{totalPage > 0}}" class="index-class">{{ currentPage }} / {{ totalPage }} </text>
           </div>
         </list-item>
@@ -95,7 +99,7 @@
             <text
               class="btn"
               onclick="sizeChange('minus')"
-              disabled="{{userSetting.fontsize <= 30}}"
+              disabled="{{userSetting.fontsize <= 36}}"
               >A-</text
             >
             <text class="size">{{ userSetting.fontsize }}</text>
@@ -126,7 +130,7 @@
         </div>
       </div>
     </stack>
-    <add-desktop bottom="50"></add-desktop>
+    <!-- <add-desktop bottom="50"></add-desktop> -->
   </div>
 </template>
 
@@ -136,7 +140,7 @@ import { getBookContent } from '../../assets/data/book-content.js'
 import { contentsData } from '../../assets/data/contents.js'
 import fetch from '@system.fetch'
 import device from '@system.device'
-import ad from '@service.ad'
+// import ad from '@service.ad'
 
 export default {
   public: {
@@ -149,8 +153,6 @@ export default {
     shelfList:[],//书架列表
     totalPage: 0,
     index: 0,
-    showRich:false,
-    componentName: 'richtext',
     screenHeight:1000,
     screenWidth: 720,
     pages: [], // 分页后的内容数组
@@ -160,7 +162,7 @@ export default {
     showPop: false,
     showCategory:false,
     userSetting: {
-      fontsize: 30,
+      fontsize: 36,
       bgColor: '#EAEAEF',
       nightMode: false,
       fontFamily: 'default'
@@ -203,24 +205,34 @@ export default {
         fontFamily: this.userSetting.fontFamily,
         color: this.userSetting.nightMode ? 'rgb(126,129,134)' : '#000000',
         fontSize: this.userSetting.fontsize + 'px',
-        height:this.screenHeight + 'px',
+        lineHeight: (this.userSetting.fontsize * 1.5) + 'px'
+      }
+    },
+    swiperStyle() {
+      return {
+        height: (this.screenHeight - 400) + 'px'
       }
     }
   },
   async onInit() {
     var that = this
-    this.initAd()
+    const deviceInfo = await device.getInfo()
+      console.log('deviceInfo',deviceInfo)
+    this.screenHeight = (deviceInfo.data.screenHeight * 1) || 1100
+    this.screenWidth = deviceInfo.data.screenWidth * 1
 
-    device.getInfo({
-     success: function(ret) {
-       that.screenHeight = (ret.screenHeight * 1 - 500) || 1100
-       that.screenWidth = ret.screenWidth * 1
-        console.log(`handling success, brand = ${ ret.screenHeight } ${ ret.screenWidth }`)
-      }
-    })
+    this.initAd()
+    setTimeout(()=>{
+        if(this.bannerAd) {        
+        this.bannerAd.show();
+        } else {        
+          this.initAd();
+        }
+      },50)
     $utils.getStorage('bookshelf').then(value => {
-        console.log('value  bookshelf',value)
+        if(value){
           this.shelfList = JSON.parse(value)
+        }
       })
     //搜索优化
     this.$page.setMeta({
@@ -241,7 +253,7 @@ export default {
     let infos = res.data.result.list[0]
     this.chapterTitle = infos.chapterName
     this.getTxtContent(infos.content)
-    console.log('this.content',this.content)
+    // console.log('this.content',this.content)
   },
    initAd() {
     try {
@@ -249,20 +261,12 @@ export default {
       this.bannerAd =  require('@service.ad').createBannerAd({  // 使用require方式避免在不支持广告接口的厂商运行时报错
         adUnitId: '9bcd5671a506459c9e6ef9c642468dc9',
         style:{
-            width:750,
+            width:1080,
             height:100,
+            left: 0,       
+            top: this.screenHeight - 100,
         }
       })
-      this.bannerAd.onLoad(() => {     // 监听广告加载
-        console.log('onLoad event emit')
-        this.bannerAd.show()
-      })
-      this.bannerAd.onError((err) => { // 监听广告出错
-        console.log('onError event emit', err)
-      })
-      this.bannerAd.onClose((res) => { // 监听广告关闭
-        console.log('onClose event emit', res)
-      })
     } catch (e) {
       console.log('initAd',e)
     }
@@ -277,22 +281,9 @@ export default {
         if (res.code === 200) {
           // 处理文本编码
           const text = this.decodeData(res.data);
-          this.content = formatText(text);
-          this.showRich = false
-          setTimeout(()=>{
-            this.showRich = true
-          },50)
-          // this.paginateContent()
-          // 格式化文本:为每行添加p标签
-        function formatText(text) {
-            const lines = text.split('\n');
-            return lines.map(line => {
-                // 跳过空行
-                if (line.trim() === '') return '';
-                return `<p>${line}</p>`;
-            }).join('');
-        }
-        console.log('this.content',this.content)
+          this.content = text;
+          this.paginateContent();
+          // console.log('this.content',this.content)
         } else {
           console.error('请求失败:', res.code);
         }
@@ -316,6 +307,44 @@ export default {
       return ''
     }
   },
+  // 根据页面高度分段内容
+  paginateContent() {
+    if (!this.content) return;
+    const maxHeight = this.screenHeight - 500; // 减去标题、底部按钮和边距的高度
+    const fontSize = this.userSetting.fontsize;
+    const lineHeight = fontSize * 1.5; // 行高
+    const maxLinesPerPage = Math.floor(maxHeight / lineHeight); 
+    // console.log(`maxHeight${maxHeight}  fontSize ${fontSize}lineHeight${lineHeight} maxLinesPerPage${maxLinesPerPage} this.screenWidth ${this.screenWidth}`)
+    const lines = this.content.split('\n').filter(line => line.trim() !== '');
+    const pages = [];
+    let currentPage = '';
+    let currentLineCount = 0;
+    for (let i = 0; i < lines.length; i++) {
+      const line = lines[i];
+      // 更准确地计算每行能容纳的字符数,考虑屏幕宽度和边距
+      const availableWidth = this.screenWidth - 120; // 减去左右边距和padding
+      const charsPerLine = Math.floor(availableWidth / (fontSize * 0.9)); // 0.6是字符宽度比例
+      const estimatedLines = Math.ceil(line.length / charsPerLine);
+      if (currentLineCount + estimatedLines > maxLinesPerPage && currentPage.trim() !== '') {
+        pages.push(currentPage.trim());
+        currentPage = line + '\n';
+        currentLineCount = estimatedLines;
+      } else {
+        currentPage += line + '\n';
+        currentLineCount += estimatedLines;
+      }
+    }
+    
+    if (currentPage.trim() !== '') {
+      pages.push(currentPage.trim());
+    }
+    
+    this.pages = pages;
+    this.totalPage = pages.length;
+    this.currentPage = 1;
+    // console.log('pages',this.pages)
+    // console.log('分页完成,总页数:', this.totalPage, '屏幕高度:', this.screenHeight, '字体大小:', fontSize, '每页最大行数:', maxLinesPerPage, '可用高度:', maxHeight);
+  },
   async onReady() {
     //获取用户自定义的样式,不存在自定义样式时使用默认样式
     const data = await $utils.getStorage('user-setting')
@@ -418,16 +447,12 @@ export default {
     $utils.routerBack()
   },
   sizeChange(type) {
-    if(type == 'minus' && this.userSetting.fontsize <=30  || type == 'plus' && this.userSetting.fontsize >=50){
+    if(type == 'minus' && this.userSetting.fontsize <=36  || type == 'plus' && this.userSetting.fontsize >=50){
         return
     }
-    // if(this.currentPage != 0){ // 
-    //   $utils.showToast('请回到第一页后再调整')
-    //   return
-    // }
-    // this.paginateContent()
     if (type === 'plus') this.userSetting.fontsize = this.userSetting.fontsize+ 2
     else this.userSetting.fontsize = this.userSetting.fontsize - 2
+    this.paginateContent() // 重新分页
     this.saveUserSettings()
   },
   bgcolorChange(color) {
@@ -436,6 +461,7 @@ export default {
   },
   fontChange(item) {
     this.userSetting.fontFamily = item.font
+    this.paginateContent() // 重新分页
     this.saveUserSettings()
   },
   changeNightMode(e) {
@@ -465,173 +491,236 @@ export default {
       })
     }
   },
-  pageChangeFunction(evt) {
-    // 获取当前页数
-    this.currentPage = evt.curpage
-  },
-  pageEndFunction(evt) {
-    this.totalPage = evt.totalpage
+  onSwiperChange(evt) {
+    this.currentPage = evt.index + 1;
   }
 }
 </script>
 
-<style lang="less">
-@import '../../assets/styles/index.less';
-
+<style>
 .doc-page {
   flex-direction: column;
   align-items: center;
-  /* background-color: #ebcda3; */
+  width: 100%;
+  min-height: 800px;
 }
 
 .index-class {
   position: fixed;
-  font-size: 30px;
+  font-size: 36px;
   color: grey;
   left: 0px;
   bottom: 10px;
+  width: 100%;
+  text-align: center;
 }
 
-.rich-text {
-  font-size: 22px;
-  margin: 24px;
+.book-text {
+  margin: 0;
+  max-height: 100%;
+  /* line-height: 1.5;
+  text-align: justify;
+  word-wrap: break-word;
+  word-break: break-all; */
 }
 
 .index-wrapper {
   width: 100px;
   height: 100px;
-  /* background-color: red; */
 }
+
 .swiper {
   width: 100%;
-  height: 1100px;
+  height: 800px;
 }
-.swiper-item {
+
+.swiper-page {
   width: 100%;
   height: 100%;
-  padding: 20px;
+  /* padding: 20px; */
+  justify-content: flex-start;
+  align-items: flex-start;
+  overflow: scroll;
+  flex-direction: column;
 }
+
 .stack-wrapper {
   width: 100%;
   height: 100%;
 }
+
 .book-content {
-  padding: 0 @gap-4;
+  padding: 0 32px;
   width: 100%;
   flex: 1;
-  .flex-box-mixins(column, flex-start, flex-start);
-  .book-title {
-    .title;
-    margin: 30px 0 @gap-3 0;
-  }
-  .g22kjdgy{
-    color: #ffffff;
-  }
-  /* .book-text {
-    margin: @gap-3 0;
-  } */
+  flex-direction: column;
+  justify-content: flex-start;
+  align-items: flex-start;
+}
+
+.book-title {
+  font-size: 48px;
+  font-weight: bold;
+  color: #333;
+  margin: 30px 0 24px 0;
+}
+
+.g22kjdgy {
+  color: #ffffff;
 }
+
 .btn-bottom {
-  .btn-primary;
-  margin: 0 @gap-2;
+  background-color: #007aff;
+  color: #ffffff;
+  padding: 20px;
+  border-radius: 8px;
+  margin: 0 16px;
   flex: 1;
   text-align: center;
 }
+
 .btn-margin {
-  margin: 0 @gap-2;
+  margin: 0 16px;
 }
+
 .btn-bottom-disabled {
-  .btn-disabled;
+  background-color: #cccccc;
+  color: #999999;
 }
+
 .pop-layer {
-  .page-column;
-  .header {
-    .flex-box-mixins(row, flex-start, center);
-    width: 100%;
-    height: 30 * @size-factor;
-    background-color: @layer-bg;
-    padding: 0 @gap-4;
-    image {
-      width: 10 * @size-factor;
-      height: 10 * @size-factor;
-      margin-right: @gap-4;
-    }
-    text {
-      font-size: 8 * @size-factor;
-      color: @white;
-    }
-  }
-  .footer {
-    padding-top: @gap-3;
-    .flex-box-mixins(column, flex-start, center);
-    width: 100%;
-    background-color: @layer-bg;
-    .font-size-change {
-      width: 90%;
-      padding: @gap-1 @gap-4;
-      .flex-box-mixins(row, space-between, center);
-      .btn {
-        font-size: 10 * @size-factor;
-        color: @white;
-      }
-      .btn:disabled {
-        opacity: 0.5;
-      }
-      .size {
-        font-size: 8 * @size-factor;
-        color: @white;
-      }
-    }
-    .bg-color-change {
-      width: 90%;
-      padding: @gap-1 @gap-4;
-      .flex-box-mixins(row, space-between, center);
-      .color-item {
-        width: 20 * @size-factor;
-        height: 10 * @size-factor;
-        border-radius: 8px;
-      }
-      .selected {
-        border: 5px solid @brand;
-      }
-    }
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  flex-direction: column;
+  justify-content: flex-end;
+  align-items: center;
+}
 
-    .font-change {
-      width: 90%;
-      padding: @gap-1 @gap-4;
-      .flex-box-mixins(row, space-between, center);
-      flex-wrap: wrap;
-      .font-item {
-        .btn-primary;
-        color: @grey;
-        border-color: @grey;
-        width: 45%;
-        margin-bottom: 2 * @size-factor;
-      }
-      .selected {
-        color: @brand;
-        border-color: @brand;
-        border-width: 3px;
-      }
-    }
-  }
-  .middle {
-    width: 100%;
-    height: 100%;
-    flex: 1;
-    .float-btn {
-      position: absolute;
-      right: 10 * @size-factor;
-      .flex-box-mixins(row, center, center);
-      width: 20 * @size-factor;
-      height: 20 * @size-factor;
-      background-color: @layer-bg;
-      border-radius: 100%;
-      image {
-        width: 10 * @size-factor;
-        height: 10 * @size-factor;
-      }
-    }
-  }
+.header {
+  flex-direction: row;
+  justify-content: flex-start;
+  align-items: center;
+  width: 100%;
+  height: 120px;
+  background-color: #333333;
+  padding: 0 32px;
+}
+
+.header image {
+  width: 40px;
+  height: 40px;
+  margin-right: 32px;
+}
+
+.header text {
+  font-size: 32px;
+  color: #ffffff;
+}
+
+.footer {
+  padding-top: 24px;
+  flex-direction: column;
+  justify-content: flex-start;
+  align-items: center;
+  width: 100%;
+  background-color: #333333;
+  height: 550px;
+}
+
+.font-size-change {
+  width: 90%;
+  padding: 8px 32px;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.font-size-change .btn {
+  font-size: 40px;
+  color: #ffffff;
+}
+
+.font-size-change .btn:disabled {
+  opacity: 0.5;
+}
+
+.font-size-change .size {
+  font-size: 32px;
+  color: #ffffff;
+}
+
+.bg-color-change {
+  width: 90%;
+  padding: 8px 32px;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.bg-color-change .color-item {
+  width: 80px;
+  height: 40px;
+  border-radius: 8px;
+}
+
+.bg-color-change .selected {
+  border: 5px solid #007aff;
+}
+
+.font-change {
+  width: 90%;
+  padding: 8px 32px;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: wrap;
+}
+
+.font-change .font-item {
+  background-color: #007aff;
+  color: #ffffff;
+  padding: 20px;
+  border-radius: 8px;
+  border: 2px solid #007aff;
+  width: 45%;
+  margin-bottom: 16px;
+  text-align: center;
+}
+
+.font-change .selected {
+  color: #007aff;
+  border-color: #007aff;
+  border-width: 3px;
+  background-color: transparent;
+}
+
+.middle {
+  width: 100%;
+  height: 100%;
+  flex: 1;
+}
+
+.float-btn {
+  position: absolute;
+  right: 40px;
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
+  width: 80px;
+  height: 80px;
+  background-color: #333333;
+  border-radius: 100%;
+}
+
+.float-btn image {
+  width: 40px;
+  height: 40px;
+}
+.bottom-bar{
+  width: 100%;
 }
 </style>

+ 7 - 12
src/pages/book-detail/index.ux

@@ -66,7 +66,7 @@
 import { bookListData } from '../../assets/data/book-list.js'
 import { commentData } from '../../assets/data/comment.js'
 import device from '@system.device'
-
+import prompt from '@system.prompt'
 export default {
   public: {
     info: '',
@@ -116,27 +116,22 @@ export default {
         } else {        
           this.initAd();
         }
-      },500)
-    this.indexNativeAd()  // 原生退出广告弹窗
+      },50)
+    // this.indexNativeAd()  // 原生退出广告弹窗
   },
-  initAd(){
+  async initAd(){
     try {
       var that = this
-      device.getInfo({
-      success: function(ret) {
-        that.screenHeight = ret.screenHeight
-        console.log('ret',ret)
+      const ret = await device.getInfo()
          that.ad = require('@service.ad').createBannerAd({  // 使用require方式避免在不支持的广告接口的厂商运行是报错
           adUnitId: '9bcd5671a506459c9e6ef9c642468dc9',          
           style:{            
             left: 0,       
-            top: that.screenHeight + 50,
-            height:100,        
+            top: ret.data.statusBarHeight > 119 ?  ret.data.screenHeight - 100 :  ret.data.screenHeight + (120 - ret.data.statusBarHeight),
+            height:100,
             width: 1080
           }
         })
-        }
-      })
       } catch (e) {        
         console.log(e)
       }

+ 234 - 0
src/pages/reader-test/index.ux

@@ -0,0 +1,234 @@
+<import name="custom-reader" src="../../components/custom-reader"></import>
+
+<template>
+  <div class="page">
+    <div class="header">
+      <text class="title">自定义阅读器测试</text>
+    </div>
+    
+    <div class="reader-container">
+      <custom-reader 
+        id="testReader"
+        content="{{testContent}}"
+        fontSize="{{fontSize}}"
+        bgColor="{{bgColor}}"
+        textColor="{{textColor}}"
+        pageWidth="{{pageWidth}}"
+        pageHeight="{{pageHeight}}"
+        @paginated="onPaginated"
+        @pageChanged="onPageChanged"
+        @fontSizeChanged="onFontSizeChanged"
+      />
+    </div>
+    
+    <div class="controls">
+      <div class="control-group">
+        <text class="control-label">字体大小: {{fontSize}}px</text>
+        <div class="control-buttons">
+          <text class="btn" onclick="decreaseFont">A-</text>
+          <text class="btn" onclick="increaseFont">A+</text>
+        </div>
+      </div>
+      
+      <div class="control-group">
+        <text class="control-label">背景颜色</text>
+        <div class="color-buttons">
+          <div 
+            class="color-btn {{bgColor === '#EAEAEF' ? 'active' : ''}}" 
+            style="background-color: #EAEAEF;"
+            onclick="setBgColor('#EAEAEF')"
+          ></div>
+          <div 
+            class="color-btn {{bgColor === '#FAF9DE' ? 'active' : ''}}" 
+            style="background-color: #FAF9DE;"
+            onclick="setBgColor('#FAF9DE')"
+          ></div>
+          <div 
+            class="color-btn {{bgColor === '#E3EDCD' ? 'active' : ''}}" 
+            style="background-color: #E3EDCD;"
+            onclick="setBgColor('#E3EDCD')"
+          ></div>
+        </div>
+      </div>
+      
+      <div class="info">
+        <text>当前页: {{currentPage}} / {{totalPages}}</text>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import device from '@system.device'
+
+export default {
+  private: {
+    testContent: `这是一个测试文本,用来验证自定义阅读器的功能。
+
+第一章 测试章节
+
+这是一个很长的段落,用来测试阅读器的分页功能。这个段落包含了足够多的文字,以确保能够触发分页逻辑。我们希望看到阅读器能够智能地将这些文字分成多个页面,每个页面都有合适的内容量。
+
+阅读器的分页算法会考虑字体大小、行高、页面宽度和高度等因素,确保每页的内容既不会太少也不会太多。这样用户就能获得最佳的阅读体验。
+
+第二章 功能测试
+
+现在我们来测试字体大小调节功能。用户可以通过点击A+和A-按钮来调整字体大小,阅读器会自动重新分页以适应新的字体大小。
+
+我们还需要测试背景颜色切换功能。不同的背景颜色可以适应不同的阅读环境,比如在夜间阅读时可以选择深色背景,在白天阅读时可以选择浅色背景。
+
+第三章 滑动翻页
+
+阅读器支持左右滑动来翻页,这是现代移动阅读应用的标准功能。用户可以通过手指左右滑动来浏览不同的页面,操作非常直观和流畅。
+
+我们相信这个自定义阅读器能够为用户提供比原生richtext组件更好的阅读体验。它不仅支持字体大小调节,还提供了智能分页和流畅的翻页体验。
+
+测试文本结束。`,
+    fontSize: 18,
+    bgColor: '#EAEAEF',
+    textColor: '#000000',
+    pageWidth: 700,
+    pageHeight: 1000,
+    currentPage: 1,
+    totalPages: 0
+  },
+  
+  onInit() {
+    this.getScreenInfo()
+  },
+  
+  methods: {
+    getScreenInfo() {
+      const that = this
+      device.getInfo({
+        success: function(ret) {
+          that.pageHeight = (ret.screenHeight * 1 - 200) || 1000
+          that.pageWidth = ret.screenWidth * 1
+        }
+      })
+    },
+    
+    decreaseFont() {
+      if (this.fontSize > 14) {
+        this.fontSize -= 1
+      }
+    },
+    
+    increaseFont() {
+      if (this.fontSize < 24) {
+        this.fontSize += 1
+      }
+    },
+    
+    setBgColor(color) {
+      this.bgColor = color
+    },
+    
+    onPaginated(evt) {
+      this.totalPages = evt.totalPages
+      console.log('分页完成,总页数:', evt.totalPages)
+    },
+    
+    onPageChanged(evt) {
+      this.currentPage = evt.currentPage
+      console.log('页面切换:', evt.currentPage, '/', evt.totalPages)
+    },
+    
+    onFontSizeChanged(newSize) {
+      console.log('字体大小改变:', newSize)
+    }
+  }
+}
+</script>
+
+<style lang="less">
+.page {
+  flex-direction: column;
+  height: 100%;
+  background-color: #f5f5f5;
+}
+
+.header {
+  height: 100px;
+  background-color: #007aff;
+  justify-content: center;
+  align-items: center;
+  padding: 0 20px;
+}
+
+.title {
+  color: white;
+  font-size: 20px;
+  font-weight: bold;
+}
+
+.reader-container {
+  flex: 1;
+  margin: 20px;
+  background-color: white;
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.controls {
+  padding: 20px;
+  background-color: white;
+  margin: 0 20px 20px 20px;
+  border-radius: 12px;
+}
+
+.control-group {
+  margin-bottom: 20px;
+}
+
+.control-label {
+  font-size: 16px;
+  color: #333;
+  margin-bottom: 10px;
+}
+
+.control-buttons {
+  flex-direction: row;
+  gap: 10px;
+}
+
+.btn {
+  padding: 10px 20px;
+  background-color: #007aff;
+  color: white;
+  border-radius: 8px;
+  font-size: 16px;
+}
+
+.btn:active {
+  opacity: 0.8;
+}
+
+.color-buttons {
+  flex-direction: row;
+  gap: 15px;
+}
+
+.color-btn {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  border: 3px solid transparent;
+}
+
+.color-btn.active {
+  border-color: #007aff;
+}
+
+.info {
+  text-align: center;
+  padding: 15px;
+  background-color: #f0f0f0;
+  border-radius: 8px;
+}
+
+.info text {
+  color: #666;
+  font-size: 14px;
+}
+</style>