Procházet zdrojové kódy

添加小程序基础结构,包括首页、书架、分类、福利中心等页面及其样式,新增性别切换组件,配置项目文件,添加图片资源,完善本地存储功能。

yangwenlan před 1 rokem
revize
90cb8be451
69 změnil soubory, kde provedl 1664 přidání a 0 odebrání
  1. 2 0
      .gitignore
  2. 20 0
      app.js
  3. 52 0
      app.json
  4. 12 0
      app.wxss
  5. 0 0
      assets/images/avatar-placeholder.png
  6. binární
      assets/images/bg-book.png
  7. 0 0
      assets/images/covers/book1.png
  8. 0 0
      assets/images/covers/book2.png
  9. 0 0
      assets/images/covers/book3.png
  10. 0 0
      assets/images/covers/book4.png
  11. 0 0
      assets/images/covers/hot1.png
  12. 0 0
      assets/images/covers/hot2.png
  13. 0 0
      assets/images/covers/main-banner.png
  14. 0 0
      assets/images/covers/recommend-1.png
  15. 0 0
      assets/images/covers/strong1.png
  16. 0 0
      assets/images/covers/strong2.png
  17. 0 0
      assets/images/covers/strong3.png
  18. 0 0
      assets/images/covers/strong4.png
  19. binární
      assets/images/icon-coin.png
  20. binární
      assets/images/icons/icon-category.png
  21. 0 0
      assets/images/icons/icon-more.png
  22. binární
      assets/images/icons/icon-recent.png
  23. 0 0
      assets/images/icons/icon-search.png
  24. binární
      assets/images/icons/icon-user.png
  25. 0 0
      assets/images/tabs/tab-benefit.png
  26. binární
      assets/images/tabs/tab-bookshelf.png
  27. binární
      assets/images/tabs/tab-bookshelf_active.png
  28. binární
      assets/images/tabs/tab-category.png
  29. binární
      assets/images/tabs/tab-category_active.png
  30. binární
      assets/images/tabs/tab-mine.png
  31. binární
      assets/images/tabs/tab-mine_active.png
  32. binární
      assets/images/tabs/tab-store.png
  33. binární
      assets/images/tabs/tab-store_active.png
  34. 27 0
      components/gender-tabs/index.js
  35. 4 0
      components/gender-tabs/index.json
  36. 4 0
      components/gender-tabs/index.wxml
  37. 29 0
      components/gender-tabs/index.wxss
  38. 8 0
      pages/benefit/benefit.js
  39. 3 0
      pages/benefit/benefit.json
  40. 9 0
      pages/benefit/benefit.wxml
  41. 21 0
      pages/benefit/benefit.wxss
  42. 96 0
      pages/book/list.js
  43. 3 0
      pages/book/list.json
  44. 39 0
      pages/book/list.wxml
  45. 127 0
      pages/book/list.wxss
  46. 8 0
      pages/bookshelf/bookshelf.js
  47. 3 0
      pages/bookshelf/bookshelf.json
  48. 9 0
      pages/bookshelf/bookshelf.wxml
  49. 21 0
      pages/bookshelf/bookshelf.wxss
  50. 84 0
      pages/category/category.js
  51. 5 0
      pages/category/category.json
  52. 52 0
      pages/category/category.wxml
  53. 131 0
      pages/category/category.wxss
  54. 236 0
      pages/index/index.js
  55. 5 0
      pages/index/index.json
  56. 103 0
      pages/index/index.wxml
  57. 404 0
      pages/index/index.wxss
  58. 18 0
      pages/logs/logs.js
  59. 4 0
      pages/logs/logs.json
  60. 6 0
      pages/logs/logs.wxml
  61. 16 0
      pages/logs/logs.wxss
  62. 9 0
      pages/mine/mine.js
  63. 3 0
      pages/mine/mine.json
  64. 9 0
      pages/mine/mine.wxml
  65. 21 0
      pages/mine/mine.wxss
  66. 28 0
      project.config.json
  67. 7 0
      project.private.config.json
  68. 7 0
      sitemap.json
  69. 19 0
      utils/util.js

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+docs
+.DS_Store

+ 20 - 0
app.js

@@ -0,0 +1,20 @@
+// app.js
+App({
+  onLaunch() {
+    // 展示本地存储能力
+    const logs = wx.getStorageSync('logs') || []
+    logs.unshift(Date.now())
+    wx.setStorageSync('logs', logs)
+
+    // 登录
+    wx.login({
+      success: res => {
+        // 发送 res.code 到后台换取 openId, sessionKey, unionId
+        console.log(res);
+      }
+    })
+  },
+  globalData: {
+    userInfo: null
+  }
+})

+ 52 - 0
app.json

@@ -0,0 +1,52 @@
+{
+  "pages": [
+    "pages/index/index",
+    "pages/logs/logs",
+    "pages/bookshelf/bookshelf",
+    "pages/benefit/benefit",
+    "pages/category/category",
+    "pages/mine/mine",
+    "pages/book/list"
+  ],
+  "window": {
+    "navigationStyle": "custom",
+    "backgroundColor": "#f6f6f6",
+    "backgroundTextStyle": "light"
+  },
+  "tabBar": {
+    "color": "#999999",
+    "selectedColor": "#000",
+    "backgroundColor": "#ffffff",
+    "borderStyle": "black",
+    "list": [
+      {
+        "pagePath": "pages/index/index",
+        "text": "书城",
+        "iconPath": "assets/images/tabs/tab-store.png",
+        "selectedIconPath": "assets/images/tabs/tab-store_active.png"
+      },
+      {
+        "pagePath": "pages/bookshelf/bookshelf",
+        "text": "书架",
+        "iconPath": "assets/images/tabs/tab-bookshelf.png",
+        "selectedIconPath": "assets/images/tabs/tab-bookshelf_active.png"
+      },
+      {
+        "pagePath": "pages/category/category",
+        "text": "分类",
+        "iconPath": "assets/images/tabs/tab-category.png",
+        "selectedIconPath": "assets/images/tabs/tab-category_active.png"
+      },
+      {
+        "pagePath": "pages/mine/mine",
+        "text": "我的",
+        "iconPath": "assets/images/tabs/tab-mine.png",
+        "selectedIconPath": "assets/images/tabs/tab-mine_active.png"
+      }
+    ]
+  },
+  "style": "v2",
+  "componentFramework": "glass-easel",
+  "sitemapLocation": "sitemap.json",
+  "lazyCodeLoading": "requiredComponents"
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 12 - 0
app.wxss


+ 0 - 0
assets/images/avatar-placeholder.png


binární
assets/images/bg-book.png


+ 0 - 0
assets/images/covers/book1.png


+ 0 - 0
assets/images/covers/book2.png


+ 0 - 0
assets/images/covers/book3.png


+ 0 - 0
assets/images/covers/book4.png


+ 0 - 0
assets/images/covers/hot1.png


+ 0 - 0
assets/images/covers/hot2.png


+ 0 - 0
assets/images/covers/main-banner.png


+ 0 - 0
assets/images/covers/recommend-1.png


+ 0 - 0
assets/images/covers/strong1.png


+ 0 - 0
assets/images/covers/strong2.png


+ 0 - 0
assets/images/covers/strong3.png


+ 0 - 0
assets/images/covers/strong4.png


binární
assets/images/icon-coin.png


binární
assets/images/icons/icon-category.png


+ 0 - 0
assets/images/icons/icon-more.png


binární
assets/images/icons/icon-recent.png


+ 0 - 0
assets/images/icons/icon-search.png


binární
assets/images/icons/icon-user.png


+ 0 - 0
assets/images/tabs/tab-benefit.png


binární
assets/images/tabs/tab-bookshelf.png


binární
assets/images/tabs/tab-bookshelf_active.png


binární
assets/images/tabs/tab-category.png


binární
assets/images/tabs/tab-category_active.png


binární
assets/images/tabs/tab-mine.png


binární
assets/images/tabs/tab-mine_active.png


binární
assets/images/tabs/tab-store.png


binární
assets/images/tabs/tab-store_active.png


+ 27 - 0
components/gender-tabs/index.js

@@ -0,0 +1,27 @@
+Component({
+  properties: {
+    gender: {
+      type: String,
+      value: 'male'
+    }
+  },
+
+  lifetimes: {
+    attached() {
+      // 组件初始化时,从本地存储读取性别设置
+      const gender = wx.getStorageSync('gender') || 'male';
+      this.setData({ gender });
+      // 通知页面更新性别
+      this.triggerEvent('switch', { gender });
+    }
+  },
+
+  methods: {
+    switchGender(e) {
+      const gender = e.currentTarget.dataset.gender;
+      // 保存到本地存储
+      wx.setStorageSync('gender', gender);
+      this.triggerEvent('switch', { gender });
+    }
+  }
+}) 

+ 4 - 0
components/gender-tabs/index.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+} 

+ 4 - 0
components/gender-tabs/index.wxml

@@ -0,0 +1,4 @@
+<view class="gender-tabs">
+  <view class="tab {{gender === 'male' ? 'active' : ''}}" bindtap="switchGender" data-gender="male">男生</view>
+  <view class="tab {{gender === 'female' ? 'active' : ''}}" bindtap="switchGender" data-gender="female">女生</view>
+</view> 

+ 29 - 0
components/gender-tabs/index.wxss

@@ -0,0 +1,29 @@
+.gender-tabs {
+  display: flex;
+  margin-bottom: 20rpx;
+}
+
+.tab {
+  font-size: 30rpx;
+  margin-right: 40rpx;
+  padding: 10rpx 0;
+  position: relative;
+  color: #999;
+}
+
+.tab.active {
+  color: #333;
+  font-weight: bold;
+}
+
+.tab.active:after {
+  content: "";
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  transform: translateX(-50%);
+  width: 40rpx;
+  height: 6rpx;
+  background: linear-gradient(to right, #007aff, #af97ed);
+  border-radius: 3rpx;
+} 

+ 8 - 0
pages/benefit/benefit.js

@@ -0,0 +1,8 @@
+Page({
+  data: {
+    
+  },
+  onLoad: function(options) {
+    
+  }
+})

+ 3 - 0
pages/benefit/benefit.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 9 - 0
pages/benefit/benefit.wxml

@@ -0,0 +1,9 @@
+<!--pages/benefit/benefit.wxml-->
+<view class="container">
+  <view class="header">
+    <text class="title">福利中心</text>
+  </view>
+  <view class="content">
+    <text>福利页面内容</text>
+  </view>
+</view>

+ 21 - 0
pages/benefit/benefit.wxss

@@ -0,0 +1,21 @@
+/* pages/benefit/benefit.wxss */
+.container {
+  padding: 30rpx;
+}
+
+.header {
+  text-align: center;
+  margin-bottom: 30rpx;
+}
+
+.title {
+  font-size: 36rpx;
+  font-weight: bold;
+}
+
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 800rpx;
+}

+ 96 - 0
pages/book/list.js

@@ -0,0 +1,96 @@
+Page({
+  data: {
+    pageTitle: '',
+    bookList: [],
+    loading: false,
+    hasMore: true,
+    pageNum: 1,
+    pageSize: 10,
+    type: ''  // 列表类型:editor(主编推荐)、hot(全网热搜)、recommend(强力推荐)
+  },
+
+  onLoad(options) {
+    // 设置页面标题
+    const titles = {
+      'editor': '主编推荐',
+      'hot': '全网热搜',
+      'recommend': '强力推荐'
+    }
+    const type = options.type || 'editor'
+    const pageTitle = titles[type] || '书籍列表'
+    
+    this.setData({
+      pageTitle,
+      type
+    })
+
+    // 加载初始数据
+    this.loadBookList()
+  },
+
+  // 加载书籍列表
+  async loadBookList() {
+    if (this.data.loading || !this.data.hasMore) return
+
+    this.setData({ loading: true })
+
+    try {
+      // 模拟API请求
+      const mockData = await this.getMockData()
+      
+      const { bookList } = this.data
+      const newList = bookList.concat(mockData)
+      
+      this.setData({
+        bookList: newList,
+        loading: false,
+        hasMore: mockData.length === this.data.pageSize,
+        pageNum: this.data.pageNum + 1
+      })
+    } catch (error) {
+      console.error('加载书籍列表失败:', error)
+      this.setData({ loading: false })
+      wx.showToast({
+        title: '加载失败,请重试',
+        icon: 'none'
+      })
+    }
+  },
+
+  // 模拟获取数据
+  getMockData() {
+    return new Promise((resolve) => {
+      setTimeout(() => {
+        const mockBooks = Array(this.data.pageSize).fill(0).map((_, index) => ({
+          id: this.data.bookList.length + index + 1,
+          title: '书籍标题 ' + (this.data.bookList.length + index + 1),
+          cover: '/images/book-cover.png',
+          description: '这是一段书籍描述,介绍书籍的主要内容和特点。这是一段书籍描述,介绍书籍的主要内容和特点。',
+          readCount: Math.floor(Math.random() * 10000),
+          tag: Math.random() > 0.5 ? '推荐' : '热门'
+        }))
+        resolve(mockBooks)
+      }, 500)
+    })
+  },
+
+  // 触底加载更多
+  onReachBottom() {
+    this.loadBookList()
+  },
+
+  // 点击书籍项
+  onBookTap(e) {
+    const { id } = e.currentTarget.dataset
+    wx.navigateTo({
+      url: `/pages/book/detail?id=${id}`
+    })
+  },
+
+  // 返回上一页
+  goBack() {
+    wx.navigateBack({
+      delta: 1
+    });
+  }
+}) 

+ 3 - 0
pages/book/list.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 39 - 0
pages/book/list.wxml

@@ -0,0 +1,39 @@
+<!--pages/book/list.wxml-->
+<view class="container">
+  <!-- 顶部标题栏 -->
+  <view class="header">
+    <view class="back-btn" bindtap="goBack">
+      <view class="icon-back">
+        <view class="icon-back-arrow"></view>
+      </view>
+    </view>
+    <text class="title">{{pageTitle}}</text>
+  </view>
+
+  <!-- 书籍列表 -->
+  <scroll-view class="book-list" scroll-y bindscrolltolower="onReachBottom">
+    <block wx:for="{{bookList}}" wx:key="id">
+      <view class="book-item" bindtap="onBookTap" data-id="{{item.id}}">
+        <image class="book-cover" src="{{item.cover}}" mode="aspectFill" />
+        <view class="book-info">
+          <view>
+            <text class="book-title">{{item.title}}</text>
+            <text class="book-desc">{{item.description}}</text>
+          </view>
+          <view class="book-stats">
+            <text>{{item.readCount}}人在读</text>
+          </view>
+        </view>
+        <text class="book-tag">{{item.tag}}</text>
+      </view>
+    </block>
+
+    <!-- 加载状态 -->
+    <view class="loading" wx:if="{{loading}}">
+      <text>加载中...</text>
+    </view>
+    <view class="no-more" wx:if="{{!loading && !hasMore}}">
+      <text>没有更多了</text>
+    </view>
+  </scroll-view>
+</view> 

+ 127 - 0
pages/book/list.wxss

@@ -0,0 +1,127 @@
+/* pages/book/list.wxss */
+.container {
+  min-height: 100vh;
+  padding: 80rpx 0;
+}
+
+/* 顶部标题栏 */
+.header {
+  padding: 30rpx;
+  display: flex;
+  position: relative;
+  align-item: flex-start;
+  justify-content: flex-start;
+  width: 90vw;
+}
+
+.back-btn {
+  width: 50rpx;
+  height: 50rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 30rpx;
+  position: relative;
+}
+
+/* 返回箭头图标 */
+.icon-back {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.icon-back-arrow {
+  width: 24rpx;
+  height: 24rpx;
+  border-top: 4rpx solid #333;
+  border-left: 4rpx solid #333;
+  transform: rotate(-45deg);
+  margin-left: 8rpx;
+}
+
+.title {
+  font-size: 36rpx;
+  font-weight: bold;
+  color: #333;
+  flex: 1;
+  text-align: center;
+  margin-right: 80rpx; /* 为了保持标题居中,右侧留出与返回按钮相同的空间 */
+}
+
+/* 书籍列表 */
+.book-list {
+  height: calc(100vh - 90rpx);
+  padding: 20rpx;
+}
+
+.book-item {
+  display: flex;
+  padding: 30rpx;
+  margin-bottom: 20rpx;
+  background: #fff;
+  border-radius: 12rpx;
+  position: relative;
+}
+
+.book-cover {
+  width: 160rpx;
+  height: 200rpx;
+  border-radius: 12rpx;
+  margin-right: 20rpx;
+}
+
+.book-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.book-title {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 16rpx;
+}
+
+.book-desc {
+  font-size: 26rpx;
+  color: #666;
+  line-height: 1.6;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.book-stats {
+  font-size: 24rpx;
+  color: #999;
+  margin-top: 16rpx;
+}
+
+.book-tag {
+  position: absolute;
+  top: 30rpx;
+  right: 30rpx;
+  padding: 4rpx 12rpx;
+  background: #ff6b6b;
+  color: #fff;
+  font-size: 22rpx;
+  border-radius: 6rpx;
+}
+
+/* 加载更多 */
+.loading, .no-more {
+  text-align: center;
+  padding: 30rpx 0;
+}
+
+.loading text, .no-more text {
+  font-size: 24rpx;
+  color: #999;
+} 

+ 8 - 0
pages/bookshelf/bookshelf.js

@@ -0,0 +1,8 @@
+Page({
+  data: {
+    
+  },
+  onLoad: function(options) {
+    
+  }
+})

+ 3 - 0
pages/bookshelf/bookshelf.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 9 - 0
pages/bookshelf/bookshelf.wxml

@@ -0,0 +1,9 @@
+<!--pages/bookshelf/bookshelf.wxml-->
+<view class="container">
+  <view class="header">
+    <text class="title">我的书架</text>
+  </view>
+  <view class="content">
+    <text>书架页面内容</text>
+  </view>
+</view>

+ 21 - 0
pages/bookshelf/bookshelf.wxss

@@ -0,0 +1,21 @@
+/* pages/bookshelf/bookshelf.wxss */
+.container {
+  padding: 30rpx;
+}
+
+.header {
+  text-align: center;
+  margin-bottom: 30rpx;
+}
+
+.title {
+  font-size: 36rpx;
+  font-weight: bold;
+}
+
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 800rpx;
+}

+ 84 - 0
pages/category/category.js

@@ -0,0 +1,84 @@
+Page({
+  data: {
+    gender: 'male', // 当前性别选择
+    currentCategory: 'qingchun', // 当前选中的分类
+    bookList: [
+      {
+        id: 1,
+        title: '狂龙圣医',
+        description: '大夏国谪仙少年苏浩,意外封印三魂六魄,一朝觉醒,如若龙升天。一手保命天医术,...',
+        coverUrl: '/images/book1.jpg'
+      },
+      {
+        id: 2,
+        title: '被逼后,她成了豪门宠',
+        description: '某诡异豪门家族的葬礼中,她从漆黑带回一个女子,听说那女子手段不凡,穿...',
+        coverUrl: '/images/book2.jpg'
+      },
+      {
+        id: 3,
+        title: '恢复记忆后,她回归豪门找前夫',
+        description: '江浅从人一朝落难,天之骄女跌落泥泞,传言预知生死,她以倾城之姿...',
+        coverUrl: '/images/book3.jpg'
+      }
+    ]
+  },
+
+  onLoad: function(options) {
+    // 页面首次加载时的处理
+    this.syncGenderState();
+  },
+
+  onShow: function() {
+    // 每次页面显示时同步性别状态
+    this.syncGenderState();
+  },
+
+  onTabItemTap: function() {
+    // tabBar 点击时同步性别状态
+    this.syncGenderState();
+  },
+
+  // 同步性别状态
+  syncGenderState: function() {
+    const gender = wx.getStorageSync('gender') || 'male';
+    if (gender !== this.data.gender) {
+      this.setData({ gender });
+    }
+  },
+
+  // 切换性别
+  switchGender: function(e) {
+    const { gender } = e.detail;
+    this.setData({
+      gender: gender,
+      currentCategory: 'qingchun' // 切换性别时重置分类
+    });
+    // 重新加载分类和书籍列表
+    this.loadBookList('qingchun');
+  },
+
+  // 切换分类
+  switchCategory: function(e) {
+    const category = e.currentTarget.dataset.category;
+    this.setData({
+      currentCategory: category
+    });
+    // 加载对应分类的书籍列表
+    this.loadBookList(category);
+  },
+
+  // 加载书籍列表
+  loadBookList: function(category) {
+    wx.showLoading({
+      title: '加载中...'
+    });
+    
+    // 模拟接口调用
+    setTimeout(() => {
+      wx.hideLoading();
+      // 实际开发中这里应该调用真实的接口,需要传入 gender 和 category 参数
+      // this.requestBookList(this.data.gender, category);
+    }, 500);
+  }
+})

+ 5 - 0
pages/category/category.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {
+    "gender-tabs": "/components/gender-tabs/index"
+  }
+}

+ 52 - 0
pages/category/category.wxml

@@ -0,0 +1,52 @@
+<!--pages/category/category.wxml-->
+<view class="container">
+  <!-- 顶部性别切换和搜索 -->
+  <view class="header">
+    <gender-tabs gender="{{gender}}" bind:switch="switchGender"/>
+    <view class="search-box">
+      <icon type="search" size="14" color="#999"/>
+      <input type="text" placeholder="请输入书名/口令搜索" placeholder-class="placeholder"/>
+    </view>
+  </view>
+
+  <view class="content">
+    <!-- 左侧分类列表 -->
+    <scroll-view scroll-y class="category-list">
+      <view class="category-item {{currentCategory === 'qingchun' ? 'active' : ''}}" data-category="qingchun" bindtap="switchCategory">
+        <text>青春校园</text>
+      </view>
+      <view class="category-item {{currentCategory === 'zongcai' ? 'active' : ''}}" data-category="zongcai" bindtap="switchCategory">
+        <text>总裁豪门</text>
+      </view>
+      <view class="category-item {{currentCategory === 'dushi' ? 'active' : ''}}" data-category="dushi" bindtap="switchCategory">
+        <text>都市言情</text>
+      </view>
+      <view class="category-item {{currentCategory === 'chongsheng' ? 'active' : ''}}" data-category="chongsheng" bindtap="switchCategory">
+        <text>重生年代</text>
+      </view>
+      <view class="category-item {{currentCategory === 'xuanhuan' ? 'active' : ''}}" data-category="xuanhuan" bindtap="switchCategory">
+        <text>玄幻言情</text>
+      </view>
+      <view class="category-item {{currentCategory === 'xiandai' ? 'active' : ''}}" data-category="xiandai" bindtap="switchCategory">
+        <text>现代言情</text>
+      </view>
+      <view class="category-item {{currentCategory === 'gudai' ? 'active' : ''}}" data-category="gudai" bindtap="switchCategory">
+        <text>古代言情</text>
+      </view>
+      <view class="category-item {{currentCategory === 'wuxia' ? 'active' : ''}}" data-category="wuxia" bindtap="switchCategory">
+        <text>武侠架空</text>
+      </view>
+    </scroll-view>
+
+    <!-- 右侧书籍列表 -->
+    <scroll-view scroll-y class="book-list">
+      <view class="book-item" wx:for="{{bookList}}" wx:key="id">
+        <image class="book-cover" src="{{item.coverUrl}}" mode="aspectFill"/>
+        <view class="book-info">
+          <text class="book-title">{{item.title}}</text>
+          <text class="book-desc">{{item.description}}</text>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</view>

+ 131 - 0
pages/category/category.wxss

@@ -0,0 +1,131 @@
+/* pages/category/category.wxss */
+page {
+  height: 100vh;
+}
+
+.container {
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+}
+
+/* 顶部区域 */
+.header {
+  padding: 20rpx 0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 90vw;
+}
+
+/* 搜索框 */
+.search-box {
+  display: flex;
+  align-items: center;
+  height: 70rpx;
+  background-color: #fff;
+  border-radius: 35rpx;
+  padding: 0 30rpx;
+  width: 400rpx;
+}
+
+.search-box icon {
+  margin-right: 10rpx;
+}
+
+.search-box input {
+  flex: 1;
+  height: 100%;
+  font-size: 28rpx;
+}
+
+.placeholder {
+  color: #999;
+}
+
+/* 内容区域 */
+.content {
+  flex: 1;
+  display: flex;
+  overflow: hidden;
+}
+
+/* 左侧分类列表 */
+.category-list {
+  width: 200rpx;
+  height: 100%;
+  background-color: #f5f6fa;
+}
+
+.category-item {
+  height: 100rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 28rpx;
+  color: #666;
+  position: relative;
+}
+
+.category-item.active {
+  color: #007aff;
+  font-weight: bold;
+  background-color: #fff;
+}
+
+.category-item.active::before {
+  content: '';
+  position: absolute;
+  left: 0;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 6rpx;
+  height: 36rpx;
+  background: linear-gradient(to bottom, #007aff, #af97ed);
+  border-radius: 3rpx;
+}
+
+/* 右侧书籍列表 */
+.book-list {
+  flex: 1;
+  height: 100%;
+  background-color: #fff;
+  padding: 20rpx;
+}
+
+.book-item {
+  display: flex;
+  padding: 20rpx;
+  border-bottom: 1rpx solid #f5f5f5;
+}
+
+.book-cover {
+  width: 160rpx;
+  height: 200rpx;
+  border-radius: 12rpx;
+  margin-right: 20rpx;
+}
+
+.book-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.book-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 10rpx;
+}
+
+.book-desc {
+  font-size: 26rpx;
+  color: #666;
+  line-height: 1.5;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}

+ 236 - 0
pages/index/index.js

@@ -0,0 +1,236 @@
+// index.js
+const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
+const testCover = '/assets/images/bg-book.png'
+
+Page({
+  data: {
+    // 轮播配置
+    indicatorDots: true,
+    autoplay: true,
+    interval: 3000,
+    duration: 500,
+    
+    // 性别选项
+    gender: 'male', // male or female
+    
+    // 主编推荐数据
+    recommendBooks: [
+      {
+        id: 1,
+        title: '恢复记忆后,她重归家门虐前夫',
+        desc: '江逸凌从一朝落难,天之骄女跌落神坛,得津南于绝邦之境;赐她姓名,赐以闯荡之名赐她他,...',
+        stats: '近期收藏3万',
+        cover: testCover
+      }
+    ],
+    
+    // 推荐书籍列表
+    bookList: [
+      {
+        id: 1,
+        title: '你好可空',
+        cover: testCover
+      },
+      {
+        id: 2,
+        title: '盖世人屠',
+        cover: testCover
+      },
+      {
+        id: 3,
+        title: '倾听男女心声',
+        cover: testCover
+      },
+      {
+        id: 4,
+        title: '至尊圣医',
+        cover: testCover
+      }
+    ],
+    
+    // 全网热推
+    hotBooks: [
+      {
+        id: 1,
+        title: '进荒陆上:小农女意外救国',
+        desc: '富宁于农母留半年,一起穿到了古代,遇上饥荒,她靠,战乱,开荒要逆命!人人都说这...',
+        stats: '14.3万人看过',
+        cover: testCover
+      },
+      {
+        id: 2,
+        title: '离婚后,绝色女总裁被求原谅',
+        desc: '他劝妻子成立公司,妻子名扬天下却抛弃他提出想离婚,却不知,他才是幕后的那个王者!',
+        stats: '42.3万人看过',
+        cover: testCover
+      }
+    ],
+    
+    // 强力推荐
+    strongBooks: [
+      {
+        id: 1,
+        title: '她重归家门虐前夫',
+        cover: testCover
+      },
+      {
+        id: 2,
+        title: '离婚后,我和总裁复合了',
+        cover: testCover
+      },
+      {
+        id: 3,
+        title: '娃儿,下山啦',
+        cover: testCover
+      },
+      {
+        id: 4,
+        title: '寒门千金',
+        cover: testCover
+      }
+    ],
+    
+    motto: 'Hello World',
+    userInfo: {
+      avatarUrl: defaultAvatarUrl,
+      nickName: '',
+    },
+    hasUserInfo: false,
+    canIUseGetUserProfile: wx.canIUse('getUserProfile'),
+    canIUseNicknameComp: wx.canIUse('input.type.nickname'),
+  },
+  
+  onLoad() {
+    // 从本地存储读取性别设置
+    this.syncGenderState();
+  },
+
+  onShow() {
+    // 每次页面显示时同步性别状态
+    this.syncGenderState();
+  },
+
+  onTabItemTap() {
+    // tabBar 点击时同步性别状态
+    this.syncGenderState();
+  },
+
+  // 同步性别状态
+  syncGenderState() {
+    const gender = wx.getStorageSync('gender') || 'male';
+    if (gender !== this.data.gender) {
+      this.setData({ gender });
+      // 重新加载数据
+      this.loadGenderData(gender);
+    }
+  },
+  
+  // 切换性别分类
+  switchGender: function(e) {
+    const { gender } = e.detail;
+    this.setData({ gender });
+    // 根据性别加载不同的书籍数据
+    this.loadGenderData(gender);
+  },
+  
+  // 加载性别相关数据
+  loadGenderData: function(gender) {
+    wx.showLoading({
+      title: '加载中...'
+    });
+    
+    // 模拟加载数据
+    setTimeout(() => {
+      wx.hideLoading();
+    }, 500);
+  },
+  
+  // 跳转到搜索页面
+  goToSearch: function() {
+    wx.navigateTo({
+      url: '/pages/search/search'
+    });
+  },
+  
+  // 跳转到书籍详情页
+  goToBookDetail: function(e) {
+    const bookId = e.currentTarget.dataset.id;
+    wx.navigateTo({
+      url: '/pages/book/detail?id=' + bookId
+    });
+  },
+  
+  // 跳转到功能页面
+  goToFeature: function(e) {
+    const feature = e.currentTarget.dataset.feature;
+    switch(feature) {
+      case 'recent':
+        wx.navigateTo({
+          url: '/pages/read/recent'
+        });
+        break;
+      case 'user':
+        wx.navigateTo({
+          url: '/pages/user/center'
+        });
+        break;
+      case 'category':
+        wx.navigateTo({
+          url: '/pages/category/hot'
+        });
+        break;
+      case 'editor':
+        wx.navigateTo({
+          url: '/pages/book/list?type=editor'
+        });
+        break;
+      case 'hot':
+        wx.navigateTo({
+          url: '/pages/book/list?type=hot'
+        });
+        break;
+      case 'recommend':
+        wx.navigateTo({
+          url: '/pages/book/list?type=recommend'
+        });
+        break;
+      default:
+        break;
+    }
+  },
+  
+  bindViewTap() {
+    wx.navigateTo({
+      url: '../logs/logs'
+    })
+  },
+  onChooseAvatar(e) {
+    const { avatarUrl } = e.detail
+    const { nickName } = this.data.userInfo
+    this.setData({
+      "userInfo.avatarUrl": avatarUrl,
+      hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
+    })
+  },
+  onInputChange(e) {
+    const nickName = e.detail.value
+    const { avatarUrl } = this.data.userInfo
+    this.setData({
+      "userInfo.nickName": nickName,
+      hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
+    })
+  },
+  getUserProfile(e) {
+    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
+    wx.getUserProfile({
+      desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
+      success: (res) => {
+        console.log(res)
+        this.setData({
+          userInfo: res.userInfo,
+          hasUserInfo: true
+        })
+      }
+    })
+  },
+})

+ 5 - 0
pages/index/index.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {
+    "gender-tabs": "/components/gender-tabs/index"
+  }
+}

+ 103 - 0
pages/index/index.wxml

@@ -0,0 +1,103 @@
+<!--index.wxml-->
+<view class="container">
+
+  <!-- 头部区域 -->
+  <!-- <view class="header">
+    <view class="avatar">
+      <image src="/assets/images/avatar-placeholder.png" mode="aspectFill"></image>
+    </view>
+  </view> -->
+
+  <view class="main-wrapper">
+    <!-- 性别切换和搜索区域 -->
+    <view class="tab-search">
+      <gender-tabs gender="{{gender}}" bind:switch="switchGender"/>
+      <view class="search-box" bindtap="goToSearch">
+        <icon type="search" size="14" color="#999"></icon>
+        <input placeholder="请输入书名/口令搜索" disabled="true" />
+      </view>
+    </view>
+
+    <!-- 主Banner区域 -->
+    <view class="main-banner">
+      <image src="/assets/images/bg-book.png" mode="aspectFill"></image>
+    </view>
+
+    <!-- 功能区 -->
+    <view class="feature-section">
+      <view class="feature-item" bindtap="goToFeature" data-feature="recent">
+        <image src="/assets/images/icons/icon-recent.png" mode="aspectFit"></image>
+        <text>最近阅读</text>
+      </view>
+      <view class="feature-item" bindtap="goToFeature" data-feature="user">
+        <image src="/assets/images/icons/icon-user.png" mode="aspectFit"></image>
+        <text>个人中心</text>
+      </view>
+      <view class="feature-item" bindtap="goToFeature" data-feature="category">
+        <image src="/assets/images/icons/icon-category.png" mode="aspectFit"></image>
+        <text>热门分类</text>
+      </view>
+    </view>
+  </view>
+
+
+  <!-- 主编推荐 -->
+  <view class="recommend-section">
+    <view class="section-header">
+      <text class="title">主编推荐</text>
+      <text class="more" bindtap="goToFeature" data-feature="editor">更多 ></text>
+    </view>
+
+    <view class="recommend-main">
+      <view class="recommend-large" wx:for="{{recommendBooks}}" wx:key="id" bindtap="goToBookDetail" data-id="{{item.id}}">
+        <image src="{{item.cover}}" mode="aspectFill"></image>
+        <view class="book-info">
+          <view class="book-title">{{item.title}}</view>
+          <view class="book-desc">{{item.desc}}</view>
+          <view class="book-stats">{{item.stats}}</view>
+        </view>
+      </view>
+    </view>
+
+    <view class="recommend-books">
+      <view class="book-item" wx:for="{{bookList}}" wx:key="id" bindtap="goToBookDetail" data-id="{{item.id}}">
+        <image src="{{item.cover}}" mode="aspectFill"></image>
+        <text>{{item.title}}</text>
+      </view>
+    </view>
+  </view>
+
+  <!-- 全网热推 -->
+  <view class="hot-section">
+    <view class="section-header">
+      <text class="title">全网热推</text>
+      <text class="more" bindtap="goToFeature" data-feature="hot">更多 ></text>
+    </view>
+
+    <view class="hot-books">
+      <view class="hot-book-item" wx:for="{{hotBooks}}" wx:key="id" bindtap="goToBookDetail" data-id="{{item.id}}">
+        <image src="{{item.cover}}" mode="aspectFill"></image>
+        <view class="hot-book-info">
+          <view class="hot-book-title">{{item.title}}</view>
+          <view class="hot-book-desc">{{item.desc}}</view>
+          <view class="hot-book-stats">{{item.stats}}</view>
+        </view>
+      </view>
+    </view>
+  </view>
+
+  <!-- 强力推荐 -->
+  <view class="strong-recommend">
+    <view class="section-header">
+      <text class="title">强力推荐</text>
+      <text class="more" bindtap="goToFeature" data-feature="recommend">更多 ></text>
+    </view>
+
+    <view class="strong-books">
+      <view class="strong-book-item" wx:for="{{strongBooks}}" wx:key="id" bindtap="goToBookDetail" data-id="{{item.id}}">
+        <image src="{{item.cover}}" mode="aspectFill"></image>
+        <text>{{item.title}}</text>
+      </view>
+    </view>
+  </view>
+</view>

+ 404 - 0
pages/index/index.wxss

@@ -0,0 +1,404 @@
+/**index.wxss**/
+page {
+  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica,
+    "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
+  color: #333;
+  min-height: 100vh;
+}
+
+.container {
+  width: 100%;
+  min-height: 100vh;
+  padding-bottom: 100rpx;
+  box-sizing: border-box;
+  position: relative;
+}
+
+/* 状态栏占位 */
+.status-bar {
+  height: 40rpx;
+  width: 100%;
+}
+
+/* 头部区域 */
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0 30rpx;
+  height: 100rpx;
+}
+
+.avatar {
+  width: 60rpx;
+  height: 60rpx;
+  border-radius: 50%;
+  overflow: hidden;
+}
+
+.avatar image {
+  width: 100%;
+  height: 100%;
+}
+
+.icon-more,
+.icon-search {
+  width: 50rpx;
+  height: 50rpx;
+  margin-left: 30rpx;
+}
+
+.icon-more image,
+.icon-search image {
+  width: 100%;
+  height: 100%;
+}
+
+.main-wrapper {
+  width: 100vw;
+  background: linear-gradient(to right,
+    rgba(247, 244, 241, 1) 0%,
+    rgba(247, 244, 241, 0.8) 20%,
+    rgba(247, 244, 241, 0.6) 40%,
+    rgba(247, 244, 241, 0.4) 60%,
+    rgba(247, 244, 241, 0.2) 80%,
+    rgba(247, 244, 241, 0) 100%
+  );
+  position: relative;
+  overflow: hidden;
+}
+
+
+/* 性别和搜索 */
+.tab-search {
+  padding: 30rpx 30rpx 0 30rpx;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.gender-tabs {
+  display: flex;
+  margin-bottom: 20rpx;
+}
+
+.tab {
+  font-size: 30rpx;
+  margin-right: 40rpx;
+  padding: 10rpx 0;
+  position: relative;
+  color: #999;
+}
+
+.tab.active {
+  color: #333;
+  font-weight: bold;
+}
+
+.tab.active:after {
+  content: "";
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  transform: translateX(-50%);
+  width: 40rpx;
+  height: 6rpx;
+  background: linear-gradient(to right, #007aff, #af97ed);
+  border-radius: 3rpx;
+}
+
+/* 搜索框 */
+.search-box {
+  display: flex;
+  align-items: center;
+  height: 70rpx;
+  background-color: #fff;
+  border-radius: 35rpx;
+  padding: 0 30rpx;
+  width: 400rpx;
+}
+
+.search-box icon {
+  margin-right: 10rpx;
+}
+
+.search-box input {
+  flex: 1;
+  height: 100%;
+  font-size: 28rpx;
+  color: #999;
+}
+
+/* 主Banner */
+.main-banner {
+  width: 100%;
+  height: 260rpx;
+  padding: 20rpx 30rpx;
+  box-sizing: border-box;
+}
+
+.main-banner image {
+  width: 100%;
+  height: 100%;
+  border-radius: 20rpx;
+}
+
+/* 功能区 */
+.feature-section {
+  display: flex;
+  justify-content: space-around;
+  padding: 30rpx;
+  background-color: #fff;
+  border-radius: 20rpx;
+  margin: 20rpx 30rpx 30rpx;
+  box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.05);
+}
+
+.feature-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.feature-item image {
+  width: 80rpx;
+  height: 80rpx;
+  margin-bottom: 10rpx;
+}
+
+.feature-item text {
+  font-size: 24rpx;
+  color: #666;
+}
+
+/* 公共区域标题 */
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 30rpx;
+}
+
+.section-header .title {
+  font-size: 34rpx;
+  font-weight: bold;
+}
+
+.section-header .more {
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* 主编推荐 */
+.recommend-section {
+  background-color: #fff;
+  margin: 20rpx 30rpx 30rpx;
+  border-radius: 20rpx;
+}
+
+.recommend-main {
+  padding: 0 30rpx;
+}
+
+.recommend-large {
+  display: flex;
+  padding-bottom: 30rpx;
+  border-bottom: 1rpx solid #eee;
+}
+
+.recommend-large image {
+  width: 190rpx;
+  height: 250rpx;
+  border-radius: 10rpx;
+}
+
+.book-info {
+  flex: 1;
+  margin-left: 20rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.book-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  margin-bottom: 10rpx;
+}
+
+.book-desc {
+  font-size: 24rpx;
+  color: #666;
+  line-height: 1.5;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.book-stats {
+  font-size: 24rpx;
+  color: #007aff;
+}
+
+.recommend-books {
+  display: flex;
+  padding: 30rpx;
+  justify-content: space-between;
+}
+
+.book-item {
+  width: 22%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.book-item image {
+  width: 100%;
+  height: 220rpx;
+  border-radius: 10rpx;
+  margin-bottom: 10rpx;
+}
+
+.book-item text {
+  font-size: 24rpx;
+  color: #333;
+  text-align: center;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: 100%;
+}
+
+/* 全网热推 */
+.hot-section {
+  background-color: #fff;
+  margin: 20rpx 30rpx 30rpx;
+  border-radius: 20rpx;
+}
+
+.hot-books {
+  padding: 0 30rpx 30rpx;
+}
+
+.hot-book-item {
+  display: flex;
+  margin-bottom: 30rpx;
+}
+
+.hot-book-item:last-child {
+  margin-bottom: 0;
+}
+
+.hot-book-item image {
+  width: 200rpx;
+  height: 270rpx;
+  border-radius: 10rpx;
+}
+
+.hot-book-info {
+  flex: 1;
+  margin-left: 20rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.hot-book-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  margin-bottom: 10rpx;
+}
+
+.hot-book-desc {
+  font-size: 24rpx;
+  color: #666;
+  line-height: 1.5;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.hot-book-stats {
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* 强力推荐 */
+.strong-recommend {
+  background-color: #fff;
+  margin: 20rpx 30rpx 30rpx;
+  border-radius: 20rpx;
+  width: 690rpx;
+  box-sizing: border-box;
+}
+
+.strong-books {
+  display: flex;
+  padding: 0 30rpx 30rpx;
+  justify-content: space-between;
+}
+
+.strong-book-item {
+  width: 22%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.strong-book-item image {
+  width: 100%;
+  height: 220rpx;
+  border-radius: 10rpx;
+  margin-bottom: 10rpx;
+}
+
+.strong-book-item text {
+  font-size: 24rpx;
+  color: #333;
+  text-align: center;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: 100%;
+}
+
+/* 底部Tab导航 */
+.tab-bar {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100rpx;
+  display: flex;
+  background-color: #fff;
+  border-top: 1rpx solid #eee;
+  z-index: 100;
+}
+
+.tab-item {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.tab-item image {
+  width: 50rpx;
+  height: 50rpx;
+  margin-bottom: 6rpx;
+}
+
+.tab-item text {
+  font-size: 22rpx;
+  color: #999;
+}
+
+.tab-item.active text {
+  color: #ff5252;
+}

+ 18 - 0
pages/logs/logs.js

@@ -0,0 +1,18 @@
+// logs.js
+const util = require('../../utils/util.js')
+
+Page({
+  data: {
+    logs: []
+  },
+  onLoad() {
+    this.setData({
+      logs: (wx.getStorageSync('logs') || []).map(log => {
+        return {
+          date: util.formatTime(new Date(log)),
+          timeStamp: log
+        }
+      })
+    })
+  }
+})

+ 4 - 0
pages/logs/logs.json

@@ -0,0 +1,4 @@
+{
+  "usingComponents": {
+  }
+}

+ 6 - 0
pages/logs/logs.wxml

@@ -0,0 +1,6 @@
+<!--logs.wxml-->
+<scroll-view class="scrollarea" scroll-y type="list">
+  <block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log">
+    <view class="log-item">{{index + 1}}. {{log.date}}</view>
+  </block>
+</scroll-view>

+ 16 - 0
pages/logs/logs.wxss

@@ -0,0 +1,16 @@
+page {
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+}
+.scrollarea {
+  flex: 1;
+  overflow-y: hidden;
+}
+.log-item {
+  margin-top: 20rpx;
+  text-align: center;
+}
+.log-item:last-child {
+  padding-bottom: env(safe-area-inset-bottom);
+}

+ 9 - 0
pages/mine/mine.js

@@ -0,0 +1,9 @@
+// pages/mine/mine.js
+Page({
+  data: {
+    
+  },
+  onLoad: function(options) {
+    
+  }
+})

+ 3 - 0
pages/mine/mine.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 9 - 0
pages/mine/mine.wxml

@@ -0,0 +1,9 @@
+<!--pages/mine/mine.wxml-->
+<view class="container">
+  <view class="header">
+    <text class="title">个人中心</text>
+  </view>
+  <view class="content">
+    <text>我的页面内容</text>
+  </view>
+</view>

+ 21 - 0
pages/mine/mine.wxss

@@ -0,0 +1,21 @@
+/* pages/mine/mine.wxss */
+.container {
+  padding: 30rpx;
+}
+
+.header {
+  text-align: center;
+  margin-bottom: 30rpx;
+}
+
+.title {
+  font-size: 36rpx;
+  font-weight: bold;
+}
+
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 800rpx;
+}

+ 28 - 0
project.config.json

@@ -0,0 +1,28 @@
+{
+  "compileType": "miniprogram",
+  "libVersion": "trial",
+  "packOptions": {
+    "ignore": [],
+    "include": []
+  },
+  "setting": {
+    "coverView": true,
+    "es6": true,
+    "postcss": true,
+    "minified": true,
+    "enhance": true,
+    "showShadowRootInWxmlPanel": true,
+    "packNpmRelationList": [],
+    "babelSetting": {
+      "ignore": [],
+      "disablePlugins": [],
+      "outputPath": ""
+    }
+  },
+  "condition": {},
+  "editorSetting": {
+    "tabIndent": "auto",
+    "tabSize": 2
+  },
+  "appid": "wxa7a33088566e1292"
+}

+ 7 - 0
project.private.config.json

@@ -0,0 +1,7 @@
+{
+  "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
+  "projectname": "readSpace",
+  "setting": {
+    "compileHotReLoad": true
+  }
+}

+ 7 - 0
sitemap.json

@@ -0,0 +1,7 @@
+{
+  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
+  "rules": [{
+  "action": "allow",
+  "page": "*"
+  }]
+}

+ 19 - 0
utils/util.js

@@ -0,0 +1,19 @@
+const formatTime = date => {
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  const hour = date.getHours()
+  const minute = date.getMinutes()
+  const second = date.getSeconds()
+
+  return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
+}
+
+const formatNumber = n => {
+  n = n.toString()
+  return n[1] ? n : `0${n}`
+}
+
+module.exports = {
+  formatTime
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů