├── .gitignore ├── app.js ├── app.json ├── app.wxss ├── components ├── behaviors │ └── pagination.js ├── book │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── classic │ ├── classic-beh.js │ ├── classic.wxss │ ├── essay │ │ ├── images │ │ │ ├── .DS_Store │ │ │ └── essay@tag.png │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── movie │ │ ├── images │ │ │ └── movie@tag.png │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── music │ │ ├── images │ │ ├── .DS_Store │ │ ├── music@tag.png │ │ ├── player@pause.png │ │ └── player@play.png │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss ├── episode │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── image-button │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── like │ ├── images │ │ ├── like.png │ │ └── like@dis.png │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── loading │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── mask │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── navi │ ├── images │ │ ├── .DS_Store │ │ ├── triangle.dis@left.png │ │ ├── triangle.dis@right.png │ │ ├── triangle@left.png │ │ └── triangle@right.png │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── preview │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── search │ ├── images │ │ ├── cancel.png │ │ └── search.png │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss └── tag │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── config.js ├── images ├── book │ ├── quality.png │ └── tip.png ├── icon │ ├── search.png │ └── share.png ├── my │ ├── about.png │ ├── course.png │ ├── like.png │ ├── my.png │ ├── my@bg.png │ ├── study.png │ └── vendor.png └── tab │ ├── book.png │ ├── book@highlight.png │ ├── classic.png │ ├── classic@highlight.png │ ├── my.png │ └── my@highlight.png ├── models ├── book.js ├── classic.js ├── keyword.js └── like.js ├── pages ├── about │ ├── about.js │ ├── about.json │ ├── about.wxml │ └── about.wxss ├── book │ ├── book.js │ ├── book.json │ ├── book.wxml │ └── book.wxss ├── classic-detail │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── classic │ ├── classic.js │ ├── classic.json │ ├── classic.wxml │ └── classic.wxss ├── course │ ├── course.js │ ├── course.json │ ├── course.wxml │ └── course.wxss ├── detail │ ├── detail.js │ ├── detail.json │ ├── detail.wxml │ └── detail.wxss └── my │ ├── my.js │ ├── my.json │ ├── my.wxml │ └── my.wxss ├── reademeimages ├── aixin.jpg └── navi.jpg ├── readme.md ├── sitemap.json └── utils ├── common.js ├── filter.wxs ├── http-promise.js ├── http.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .vscode 3 | .idea/ 4 | Pipfile.lock 5 | project.config.json 6 | node_modules 7 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | // 展示本地存储能力 5 | var logs = wx.getStorageSync('logs') || [] 6 | logs.unshift(Date.now()) 7 | wx.setStorageSync('logs', logs) 8 | 9 | // 登录 10 | wx.login({ 11 | success: res => { 12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId 13 | } 14 | }) 15 | // 获取用户信息 16 | wx.getSetting({ 17 | success: res => { 18 | if (res.authSetting['scope.userInfo']) { 19 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 20 | wx.getUserInfo({ 21 | success: res => { 22 | // 可以将 res 发送给后台解码出 unionId 23 | this.globalData.userInfo = res.userInfo 24 | 25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 26 | // 所以此处加入 callback 以防止这种情况 27 | if (this.userInfoReadyCallback) { 28 | this.userInfoReadyCallback(res) 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | }) 35 | }, 36 | globalData: { 37 | userInfo: null 38 | } 39 | }) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/classic/classic", 4 | "pages/book/book", 5 | "pages/my/my", 6 | "pages/detail/detail", 7 | "pages/classic-detail/index", 8 | "pages/about/about", 9 | "pages/course/course" 10 | 11 | ], 12 | "requiredBackgroundModes": [ 13 | "audio" 14 | ], 15 | "window": { 16 | "backgroundTextStyle": "light", 17 | "navigationBarBackgroundColor": "#fff", 18 | "navigationBarTitleText": "海洋", 19 | "navigationBarTextStyle": "black", 20 | "navigationStyle": "default", 21 | "enablePullDownRefresh": false 22 | }, 23 | "tabBar": { 24 | "selectedColor": "#000000", 25 | "backgroundColor": "#ffffff", 26 | "color": "#c7c7c7", 27 | "list": [ 28 | { 29 | "pagePath": "pages/classic/classic", 30 | "text": "流行", 31 | "selectedIconPath": "/images/tab/classic@highlight.png", 32 | "iconPath": "/images/tab/classic.png" 33 | }, 34 | { 35 | "pagePath": "pages/book/book", 36 | "selectedIconPath": "/images/tab/book@highlight.png", 37 | "text": "书单", 38 | "iconPath": "/images/tab/book.png" 39 | }, 40 | { 41 | "pagePath": "pages/my/my", 42 | "selectedIconPath": "/images/tab/my@highlight.png", 43 | "text": "喜欢", 44 | "iconPath": "/images/tab/my.png" 45 | } 46 | ] 47 | }, 48 | "sitemapLocation": "sitemap.json" 49 | } -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | 3 | page { 4 | font-family: "PingFangSC-Thin"; 5 | font-size: 32rpx; 6 | } 7 | -------------------------------------------------------------------------------- /components/behaviors/pagination.js: -------------------------------------------------------------------------------- 1 | const paginationBev = Behavior({ 2 | data: { 3 | dataArray: [], //请求返回的数组 4 | total: null, //数据的总数 5 | noneResult: false, //没有得到想要的搜索结果 6 | loading: false, //表示是否正在发送请求,默认是没有发送请求 7 | }, 8 | 9 | methods: { 10 | // 加载更多拼接更多数据到数组中 11 | setMoreData(dataArray) { 12 | const tempArray = this.data.dataArray.concat(dataArray) 13 | this.setData({ 14 | dataArray: tempArray 15 | }) 16 | }, 17 | 18 | // 获取得到的数据的数组的长度 19 | getCurrentStart() { 20 | return this.data.dataArray.length 21 | }, 22 | 23 | // 获取设置数据的 总长度 24 | // 如果返回的结果为0,就说明没有得到搜索结果 25 | setTotal(total) { 26 | this.data.total = total 27 | if (total === 0) { 28 | this.setData({ 29 | noneResult: true 30 | }) 31 | } 32 | }, 33 | 34 | // 如果得到数据的长度大于服务器返回的总长度,代表没有更多数据了,就停止发请求 35 | hasMore() { 36 | if (this.data.dataArray.length >= this.data.total) { 37 | return false 38 | } else { 39 | return true 40 | } 41 | }, 42 | 43 | // 清空数据,设置初始值 44 | initialize() { 45 | this.setData({ 46 | dataArray: [], 47 | noneResult: false, 48 | loading: false, 49 | }) 50 | this.data.total = null 51 | }, 52 | 53 | // 事件节流机制,判断是否加锁 54 | isLocked() { 55 | return this.data.loading ? true : false 56 | }, 57 | 58 | // 加锁 59 | addLocked() { 60 | this.setData({ 61 | loading: true 62 | }) 63 | 64 | }, 65 | 66 | // 解锁 67 | unLocked() { 68 | this.setData({ 69 | loading: false 70 | }) 71 | }, 72 | } 73 | 74 | }) 75 | 76 | export { 77 | paginationBev 78 | } 79 | -------------------------------------------------------------------------------- /components/book/index.js: -------------------------------------------------------------------------------- 1 | // components/book/index.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | book: Object, 8 | showLike: { //每本书下面有个喜欢字样 9 | type: Boolean, 10 | value: true 11 | } 12 | }, 13 | 14 | /** 15 | * 组件的初始数据 16 | */ 17 | data: { 18 | 19 | }, 20 | 21 | /** 22 | * 组件的方法列表 23 | */ 24 | methods: { 25 | onTap(event) { 26 | const bid = this.properties.book.id 27 | // 尽量不要在组件的内部写业务逻辑,会降低组件的通用性,他只服务于当前的项目,属于项目组件 28 | wx.navigateTo({ 29 | url: `/pages/detail/detail?bid=${bid}` 30 | }) 31 | } 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /components/book/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/book/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{book.title}} 5 | {{book.author}} 6 | 7 | {{book.fav_nums}} 喜欢 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /components/book/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-top: 30rpx; 3 | display: flex; 4 | position: relative; 5 | box-shadow: 2px 2px 3px #e3e3e3; 6 | flex-direction: column; 7 | width: 240rpx; 8 | height: 360rpx; 9 | } 10 | 11 | .container image { 12 | width: 100%; 13 | height: 100%; 14 | border-radius: 2px; 15 | } 16 | 17 | .description { 18 | width: 100%; 19 | box-sizing: border-box; 20 | position: absolute; 21 | bottom: 0; 22 | background-color: #fff; 23 | padding: 5rpx 10rpx 8rpx 15rpx; 24 | font-size: 24rpx; 25 | flex-direction: column; 26 | display: flex; 27 | border-bottom-right-radius: 2px; 28 | border-bottom-left-radius: 2px; 29 | } 30 | 31 | .title { 32 | margin-top: 10rpx; 33 | text-overflow: ellipsis; 34 | white-space: nowrap; 35 | overflow: hidden; 36 | } 37 | 38 | .auther { 39 | font-size: 20rpx; 40 | color: #999; 41 | margin-bottom: 10rpx; 42 | text-overflow: ellipsis; 43 | white-space: nowrap; 44 | overflow: hidden; 45 | } 46 | 47 | .foot { 48 | font-size: 20rpx; 49 | display: flex; 50 | flex-direction: row; 51 | justify-content: flex-end; 52 | } 53 | 54 | .footer { 55 | color: #666; 56 | } 57 | -------------------------------------------------------------------------------- /components/classic/classic-beh.js: -------------------------------------------------------------------------------- 1 | let classicBeh = Behavior({ //创建公用行为 2 | properties: { 3 | img: String, 4 | content: String, 5 | hidden: Boolean 6 | }, 7 | data: { 8 | 9 | }, 10 | methods: { 11 | 12 | }, 13 | attached: function() { 14 | 15 | } 16 | }) 17 | 18 | export { 19 | classicBeh 20 | } 21 | -------------------------------------------------------------------------------- /components/classic/classic.wxss: -------------------------------------------------------------------------------- 1 | /* components/classic/movie/index.wxss */ 2 | 3 | .classic-container { 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | } 8 | 9 | .classic-img { 10 | width: 750rpx; 11 | height: 500rpx; 12 | } 13 | 14 | .tag { 15 | width: 46rpx; 16 | height: 142rpx; 17 | position: relative; 18 | right: 310rpx; 19 | bottom: 58rpx; 20 | } 21 | 22 | .content { 23 | font-size: 36rpx; 24 | max-width: 550rpx; 25 | /* 文字多少,都能让其居中 */ 26 | } 27 | -------------------------------------------------------------------------------- /components/classic/essay/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/essay/images/.DS_Store -------------------------------------------------------------------------------- /components/classic/essay/images/essay@tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/essay/images/essay@tag.png -------------------------------------------------------------------------------- /components/classic/essay/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | classicBeh 3 | } from '../classic-beh' 4 | 5 | Component({ 6 | 7 | behaviors: [classicBeh], 8 | /** 9 | * 组件的属性列表 10 | */ 11 | properties: { 12 | 13 | }, 14 | 15 | /** 16 | * 组件的初始数据 17 | */ 18 | data: { 19 | 20 | }, 21 | 22 | /** 23 | * 组件的方法列表 24 | */ 25 | methods: { 26 | 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /components/classic/essay/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/classic/essay/index.wxml: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /components/classic/essay/index.wxss: -------------------------------------------------------------------------------- 1 | @import "../classic.wxss" 2 | -------------------------------------------------------------------------------- /components/classic/movie/images/movie@tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/movie/images/movie@tag.png -------------------------------------------------------------------------------- /components/classic/movie/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | classicBeh 3 | } from '../classic-beh' 4 | 5 | Component({ 6 | 7 | behaviors: [classicBeh], 8 | /** 9 | * 组件的属性列表 10 | */ 11 | properties: { 12 | // img: String, 13 | // content: String 14 | // hidden: Boolean 15 | }, 16 | 17 | /** 18 | * 组件的初始数据 19 | */ 20 | data: { 21 | 22 | }, 23 | 24 | /** 25 | * 组件的方法列表 26 | */ 27 | methods: { 28 | 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /components/classic/movie/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/classic/movie/index.wxml: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /components/classic/movie/index.wxss: -------------------------------------------------------------------------------- 1 | @import "../classic.wxss" 2 | -------------------------------------------------------------------------------- /components/classic/music/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/music/images/.DS_Store -------------------------------------------------------------------------------- /components/classic/music/images/music@tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/music/images/music@tag.png -------------------------------------------------------------------------------- /components/classic/music/images/player@pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/music/images/player@pause.png -------------------------------------------------------------------------------- /components/classic/music/images/player@play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/classic/music/images/player@play.png -------------------------------------------------------------------------------- /components/classic/music/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | classicBeh 3 | } from '../classic-beh' 4 | 5 | // 获取小程序音乐管理对象 6 | const mMgr = wx.getBackgroundAudioManager() 7 | 8 | Component({ 9 | behaviors: [classicBeh], // 组件的属性列表 10 | /** 11 | * 组件的属性列表,动画API CSS3 12 | */ 13 | properties: { 14 | // img: String, 15 | // content: String 16 | src: String, 17 | title: String 18 | 19 | }, 20 | 21 | /** 22 | * 组件的初始数据 23 | */ 24 | data: { 25 | pauseSrc: 'images/player@pause.png', 26 | playSrc: 'images/player@play.png', 27 | playing: false, //当前的音乐默认是不播放的 28 | }, 29 | 30 | // 在组件实例进入页面节点树时执行 31 | // hidden,ready,created都触发不了生命周期函数 32 | attached: function(event) { 33 | console.log('attach实例进入页面', '触发1') 34 | this._monitorSwitch() 35 | this._recoverStatus() 36 | 37 | 38 | }, 39 | 40 | // hidden 不会触发完整生命周期, 适用于频繁切换; wx:if 会触发完整生命周期, 不大可能改变 41 | // 组件卸载的生命周期函数,微信生命周期, 组件退出界面节点树时执行 42 | // 组件卸载音乐停止播放,但这时不生效是因为,在classic.wxml中用的是hidden,应改为if 43 | detached: function(event) { 44 | // mMgr.stop() //为了保证背景音乐的持续播放就不能加stop 45 | }, 46 | 47 | /** 48 | * 组件的方法列表 49 | */ 50 | methods: { 51 | // 为图片绑定触摸播放事件 52 | onPlay: function(evevt) { 53 | console.log('onPlay为图片绑定触摸', '触发4') 54 | //如果没有播放就改为播放,如果播放就改为暂停 55 | if (!this.data.playing) { 56 | console.log('onPlay为图片绑定触摸!this.data.playing', '触发5', this.data.playing) 57 | //图片切换 58 | this.setData({ 59 | playing: true 60 | }) 61 | // 如果当前播放的地址和当前的组件的地址一样,就播放 62 | // 如果当前播放的地址和当前的组件的地址不一样,就把当前组件地址赋值给要播放的地址 63 | 64 | //播放音乐 65 | // mMgr.play() 66 | mMgr.src = this.properties.src 67 | mMgr.title = this.properties.title 68 | } else { 69 | console.log('onPlay为图片绑定触摸', '触发4', this.data.playing) 70 | this.setData({ 71 | playing: false 72 | }) 73 | mMgr.pause() //音乐暂停 74 | } 75 | 76 | }, 77 | 78 | // 监听音乐的播放状态,如果当前组件没有播放的音乐,就设置playing为false。如果播放的地址和当前正在播放的音乐的地址一样,就让播放状态为true 79 | _recoverStatus: function() { 80 | console.log('recoverStatus播放状态', '触发2', this.data.playing) 81 | if (mMgr.paused) { 82 | this.setData({ 83 | playing: false 84 | }) 85 | console.log('recoverStatus播放状态mMgr.paused', '触发6', this.data.playing) 86 | return 87 | 88 | } 89 | if (this.properties.src === mMgr.src) { 90 | 91 | this.setData({ 92 | playing: true 93 | }) 94 | 95 | console.log('recoverStatus播放状态mMgr.src === this.properties.src', '触发5', this.data.playing) 96 | } 97 | }, 98 | 99 | // 监听播放状态,总控开关就可以控制播放状态,结局总控开关和页面不同步问题 100 | _monitorSwitch: function() { 101 | console.log('monitorSwitch背景音频', '触发3') 102 | // 监听背景音频播放事件 103 | mMgr.onPlay(() => { 104 | this._recoverStatus() 105 | console.log('onPlay ' + this.data.playing) 106 | }) 107 | // 监听背景音频暂停事件 108 | mMgr.onPause(() => { 109 | this._recoverStatus() 110 | console.log('onPause ' + this.data.playing) 111 | }) 112 | // 关闭音乐控制台,监听背景音频停止事件 113 | mMgr.onStop(() => { 114 | this._recoverStatus() 115 | console.log('onStop ' + this.data.playing) 116 | }) 117 | // 监听背景音频自然播放结束事件 118 | mMgr.onEnded(() => { 119 | this._recoverStatus() 120 | console.log('onEnded ' + this.data.playing) 121 | }) 122 | } 123 | 124 | 125 | 126 | } 127 | }) 128 | -------------------------------------------------------------------------------- /components/classic/music/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/classic/music/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /components/classic/music/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .classic-img { 8 | width: 422rpx; 9 | height: 422rpx; 10 | margin-top: 60rpx; 11 | border-radius: 50%; 12 | } 13 | 14 | .player-img { 15 | width: 120rpx; 16 | height: 120rpx; 17 | position: relative; 18 | bottom: 270rpx; 19 | } 20 | 21 | .tag { 22 | width: 44rpx; 23 | height: 128rpx; 24 | position: relative; 25 | bottom: 160rpx; 26 | right: 310rpx; 27 | } 28 | 29 | .rotation { 30 | -webkit-transform: rotate(360deg); 31 | animation: rotation 12s linear infinite; 32 | -moz-animation: rotation 12s linear infinite; 33 | -webkit-animation: rotation 12s linear infinite; 34 | -o-animation: rotation 12s linear infinite; 35 | } 36 | 37 | @-webkit-keyframes rotation { 38 | from { 39 | -webkit-transform: rotate(0deg); 40 | } 41 | to { 42 | -webkit-transform: rotate(360deg); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /components/episode/index.js: -------------------------------------------------------------------------------- 1 | // components/epsoide/index.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | index: { //默认显示为0 8 | type: String, 9 | observer: function(newVal, oldVal, changedPath) { 10 | let val = newVal < 10 ? '0' + newVal : newVal 11 | this.setData({ 12 | _index: val 13 | }) 14 | } 15 | } 16 | }, 17 | 18 | /** 19 | * 组件的初始数据 20 | */ 21 | data: { 22 | months: [ 23 | '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', 24 | '十二月' 25 | ], 26 | year: 0, 27 | month: '', 28 | _index: '' 29 | }, 30 | 31 | // 生命周期函数 32 | attached: function() { 33 | let date = new Date() 34 | let year = date.getFullYear() 35 | let month = date.getMonth() 36 | 37 | this.setData({ 38 | year: year, 39 | month: this.data.months[month] 40 | }) 41 | }, 42 | 43 | /** 44 | * 组件的方法列表 45 | */ 46 | methods: { 47 | 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /components/episode/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/episode/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | No. 4 | {{_index}} 5 | 6 | 7 | 8 | {{month}} 9 | {{year}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /components/episode/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 60rpx; 3 | /* 让其变为行内元素,宽度自适应 */ 4 | display: inline-flex; 5 | flex-direction: row; 6 | } 7 | 8 | .plain { 9 | font-size: 32rpx; 10 | } 11 | 12 | .index { 13 | font-size: 60rpx; 14 | line-height: 60rpx; 15 | font-weight: 800; 16 | margin-right: 14rpx; 17 | } 18 | 19 | .line { 20 | height: 44rpx; 21 | margin-right: 14rpx; 22 | border-left: 1px solid black; 23 | } 24 | 25 | .index-container { 26 | display: flex; 27 | flex-direction: row; 28 | align-items: baseline; 29 | } 30 | 31 | .data-container { 32 | height: 60rpx; 33 | display: flex; 34 | flex-direction: column; 35 | margin-top: 5rpx; 36 | } 37 | 38 | .month { 39 | font-size: 24rpx; 40 | line-height: 24rpx; 41 | margin-right: 2rpx; 42 | } 43 | 44 | .year { 45 | font-size: 20rpx; 46 | } 47 | -------------------------------------------------------------------------------- /components/image-button/index.js: -------------------------------------------------------------------------------- 1 | // components/image-button/index.html.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | options: { 7 | multipleSlots: true // 在组件定义时的选项中启用多slot支持 8 | }, 9 | // externalClasses: ['ex-btn-class'], 10 | properties: { 11 | openType: { 12 | type: String 13 | }, 14 | imageSrc: { 15 | type: String 16 | }, 17 | bindgetuserinfo: { 18 | type: String 19 | } 20 | }, 21 | 22 | /** 23 | * 组件的初始数据 24 | */ 25 | data: { 26 | 27 | }, 28 | 29 | 30 | 31 | /** 32 | * 组件的方法列表 33 | */ 34 | methods: { 35 | onGetUserInfo(event) { 36 | this.triggerEvent('getuserinfo', event.detail, {}) 37 | }, 38 | } 39 | }) -------------------------------------------------------------------------------- /components/image-button/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/image-button/index.wxml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/image-button/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | /* display: flex; 3 | flex-direction: row; 4 | justify-content: center; */ 5 | /* display: flex; */ 6 | padding: 0 !important; 7 | border: none !important; 8 | /* display: inline !important; */ 9 | } 10 | 11 | 12 | /* .img{ 13 | width:100%; 14 | height:100%; 15 | } */ 16 | -------------------------------------------------------------------------------- /components/like/images/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/like/images/like.png -------------------------------------------------------------------------------- /components/like/images/like@dis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/like/images/like@dis.png -------------------------------------------------------------------------------- /components/like/index.js: -------------------------------------------------------------------------------- 1 | // components/like/like-cmp.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | like: Boolean, 8 | count: Number, 9 | readOnly: Boolean 10 | }, 11 | 12 | data: { 13 | yesSrc: 'images/like.png', 14 | noSrc: 'images/like@dis.png' 15 | }, 16 | 17 | methods: { 18 | onLike(event) { //自定义事件 19 | if (this.properties.readOnly) { //如果是只读的就不会执行后面的操作 20 | return 21 | } 22 | let count = this.properties.count 23 | let like = this.properties.like 24 | count = like ? count - 1 : count + 1 25 | this.setData({ //是异步的,先执行count+1,在执行!like 26 | count: count, 27 | like: !like 28 | }) 29 | let behavior = like ? 'like' : 'cancel' 30 | //自定义事件 31 | this.triggerEvent('like', { 32 | behavior: behavior 33 | }, {}) 34 | } 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /components/like/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /components/like/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{count}} 4 | -------------------------------------------------------------------------------- /components/like/index.wxss: -------------------------------------------------------------------------------- 1 | /* components/like/like-cmp.wxss */ 2 | 3 | .container { 4 | /* 变成行内元素,自适应内容宽度 */ 5 | display: inline-flex; 6 | flex-direction: row; 7 | /* 必须指定宽度,否则会出现移动 */ 8 | width: 90rpx; 9 | padding: 10rpx; 10 | } 11 | 12 | .container text { 13 | font-size: 24rpx; 14 | color: #bbb; 15 | /* 消除数字上下空白2px */ 16 | line-height: 24rpx; 17 | position: relative; 18 | bottom: 10rpx; 19 | left: 6rpx; 20 | } 21 | 22 | .container image { 23 | width: 32rpx; 24 | height: 28rpx; 25 | } 26 | -------------------------------------------------------------------------------- /components/loading/index.js: -------------------------------------------------------------------------------- 1 | // components/loading/index.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | 8 | }, 9 | 10 | /** 11 | * 组件的初始数据 12 | */ 13 | data: { 14 | 15 | }, 16 | 17 | /** 18 | * 组件的方法列表 19 | */ 20 | methods: { 21 | 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /components/loading/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/loading/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /components/loading/index.wxss: -------------------------------------------------------------------------------- 1 | .spinner { 2 | width: 40rpx; 3 | height: 40rpx; 4 | position: relative; 5 | /* margin: 100px auto; */ 6 | } 7 | 8 | .double-bounce1, 9 | .double-bounce2 { 10 | width: 100%; 11 | height: 100%; 12 | border-radius: 50%; 13 | background-color: #3063b2; 14 | opacity: 0.6; 15 | position: absolute; 16 | top: 0; 17 | left: 0; 18 | -webkit-animation: bounce 2.0s infinite ease-in-out; 19 | animation: bounce 2.0s infinite ease-in-out; 20 | } 21 | 22 | .double-bounce2 { 23 | -webkit-animation-delay: -1.0s; 24 | animation-delay: -1.0s; 25 | } 26 | 27 | @-webkit-keyframes bounce { 28 | 0%, 29 | 100% { 30 | -webkit-transform: scale(0.0) 31 | } 32 | 50% { 33 | -webkit-transform: scale(1.0) 34 | } 35 | } 36 | 37 | @keyframes bounce { 38 | 0%, 39 | 100% { 40 | transform: scale(0.0); 41 | -webkit-transform: scale(0.0); 42 | } 43 | 50% { 44 | transform: scale(1.0); 45 | -webkit-transform: scale(1.0); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /components/mask/index.js: -------------------------------------------------------------------------------- 1 | // components/mask/index.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | 8 | }, 9 | 10 | /** 11 | * 组件的初始数据 12 | */ 13 | data: { 14 | 15 | }, 16 | 17 | /** 18 | * 组件的方法列表 19 | */ 20 | methods: { 21 | 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /components/mask/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/mask/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /components/mask/index.wxss: -------------------------------------------------------------------------------- 1 | /* components/mask/index.wxss */ 2 | 3 | 4 | /* 弹出评论时添加背景蒙层 */ 5 | 6 | .container { 7 | background-color: #000000; 8 | position: fixed; 9 | top: 0; 10 | opacity: 0.6; 11 | width: 100%; 12 | height: 100%; 13 | z-index: 99; 14 | } 15 | -------------------------------------------------------------------------------- /components/navi/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/navi/images/.DS_Store -------------------------------------------------------------------------------- /components/navi/images/triangle.dis@left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/navi/images/triangle.dis@left.png -------------------------------------------------------------------------------- /components/navi/images/triangle.dis@right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/navi/images/triangle.dis@right.png -------------------------------------------------------------------------------- /components/navi/images/triangle@left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/navi/images/triangle@left.png -------------------------------------------------------------------------------- /components/navi/images/triangle@right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/navi/images/triangle@right.png -------------------------------------------------------------------------------- /components/navi/index.js: -------------------------------------------------------------------------------- 1 | // components/navi/navi.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | title: String, 8 | first: Boolean, //如果是第一期向右的箭头就禁用 9 | latest: Boolean //如果是最新一期向左的箭头就禁用 10 | }, 11 | 12 | /** 13 | * 组件的初始数据 14 | */ 15 | data: { 16 | disLeftSrc: './images/triangle.dis@left.png', 17 | leftSrc: './images/triangle@left.png', 18 | disRightSrc: './images/triangle.dis@right.png', 19 | rightSrc: './images/triangle@right.png' 20 | }, 21 | 22 | /** 23 | * 组件的方法列表 24 | */ 25 | methods: { 26 | onLeft: function(event) { //不是最新一期 27 | if (!this.properties.latest) { 28 | this.triggerEvent('left', {}, {}) 29 | } 30 | 31 | }, 32 | onRight: function(event) { //不是第一期 33 | if (!this.properties.first) { 34 | this.triggerEvent('right', {}, {}) 35 | } 36 | 37 | } 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /components/navi/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/navi/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{title}} 4 | 5 | 6 | -------------------------------------------------------------------------------- /components/navi/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 600rpx; 3 | height: 80rpx; 4 | background-color: #f7f7f7; 5 | border-radius: 2px; 6 | display: inline-flex; 7 | flex-direction: row; 8 | justify-content: space-between; 9 | align-items: center; 10 | } 11 | 12 | .title { 13 | font-size: 28rpx; 14 | } 15 | 16 | .icon { 17 | height: 80rpx; 18 | width: 80rpx; 19 | } 20 | -------------------------------------------------------------------------------- /components/preview/index.js: -------------------------------------------------------------------------------- 1 | // components/preview/index.js 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | classic: { 8 | type: Object, 9 | observer: function(newVal) { 10 | if (newVal) { 11 | var typeText = { 12 | 100: "电影", 13 | 200: "音乐", 14 | 300: "句子" 15 | }[newVal.type] 16 | } 17 | this.setData({ 18 | typeText: typeText 19 | }) 20 | } 21 | } 22 | }, 23 | 24 | /** 25 | * 组件的初始数据 26 | */ 27 | data: { 28 | typeText:String 29 | }, 30 | 31 | /** 32 | * 组件的方法列表 33 | */ 34 | methods: { 35 | onTap:function(event){ 36 | // 注意catchtap与bindtap的区别 37 | this.triggerEvent('tap',{ 38 | cid:this.properties.classic.id, 39 | type:this.properties.classic.type 40 | },{}) 41 | } 42 | } 43 | }) -------------------------------------------------------------------------------- /components/preview/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "v-tag": "/components/tag/index", 5 | "v-like": "/components/like/index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /components/preview/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | {{classic.content}} 8 | 9 | -------------------------------------------------------------------------------- /components/preview/index.wxss: -------------------------------------------------------------------------------- 1 | .container{ 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | width:330rpx; 6 | background-color: #ffffff; 7 | } 8 | 9 | .head{ 10 | display: flex; 11 | width:100%; 12 | flex-direction: row; 13 | align-items: center; 14 | justify-content: space-between; 15 | height:80rpx; 16 | } 17 | 18 | .tag{ 19 | margin-left:20rpx; 20 | margin-top:-2rpx; 21 | height:40rpx; 22 | width:72rpx ; 23 | font-size:24rpx; 24 | background-color:#f7f7f7 !important; 25 | } 26 | 27 | .like{ 28 | margin-top:4rpx; 29 | margin-right:4rpx; 30 | } 31 | 32 | .other-img{ 33 | width:100%; 34 | height:240rpx; 35 | } 36 | 37 | .music-img{ 38 | border-radius: 50%; 39 | width:240rpx; 40 | height:240rpx; 41 | } 42 | 43 | .text{ 44 | padding:30rpx; 45 | font-size:28rpx; 46 | height:130rpx; 47 | color:#666666; 48 | overflow: hidden; 49 | } -------------------------------------------------------------------------------- /components/search/images/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/search/images/cancel.png -------------------------------------------------------------------------------- /components/search/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/components/search/images/search.png -------------------------------------------------------------------------------- /components/search/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | KeywordModel 3 | } from '../../models/keyword' 4 | import { 5 | BookModel 6 | } from '../../models/book' 7 | import { 8 | paginationBev 9 | } from '../behaviors/pagination' 10 | 11 | const Keywordmodel = new KeywordModel() 12 | const bookmodel = new BookModel() 13 | 14 | // components/search/index.js 15 | Component({ 16 | // 组件使用行为需加 17 | behaviors: [paginationBev], 18 | 19 | 20 | /** 21 | * 组件的属性列表 22 | */ 23 | properties: { 24 | more: { 25 | type: String, 26 | observer: 'loadMore' 27 | } //从pages/book/book.js 传来的属性,监听滑到底步操作.只要外界传来的属性改变就会触发observer函数执行 28 | }, 29 | 30 | /** 31 | * 组件的初始数据 32 | */ 33 | data: { 34 | historyWords: [], //历史搜索关键字 35 | hotWords: [], //热门搜索关键字 36 | // dataArray: [], //搜索图书当summary=1,返回概要数据 37 | searching: false, //控制搜索到的图书数据的显隐,默认不显示 38 | q: '', //输入框中要显示的内容 39 | // loading: false, //表示是否正在发送请求,默认是没有发送请求 40 | loadingCenter: false //控制loading加载效果是否显示在中间 41 | }, 42 | 43 | // 组件初始化时,小程序默认调用的生命周期函数 44 | attached() { 45 | // const historywords = Keywordmodel.getHistory() 46 | // const hotword = Keywordmodel.getHot() 47 | this.setData({ 48 | historyWords: Keywordmodel.getHistory() 49 | }) 50 | 51 | Keywordmodel.getHot().then(res => { 52 | this.setData({ 53 | hotWords: res.hot 54 | }) 55 | }) 56 | }, 57 | 58 | /** 59 | * 组件的方法列表 60 | */ 61 | methods: { 62 | // 只要外界传来的属性改变就会触发observer函数执行 63 | loadMore() { 64 | console.log('监听函数触发到底了') 65 | // 和onConfirm一样都需要调取search方法返回当summary=1,返回概要数据:并更新数据到dataArray中 66 | // 先判断已得到的搜索数据的长度,在调用search方法将最新获取的数据和原来的数据拼接到一起更新数据然后呈现到页面中 67 | if (!this.data.q) { 68 | return 69 | } 70 | // 如果是正在发送请求就什么也不做 71 | if (this.isLocked()) { 72 | return 73 | } 74 | // const length = this.data.dataArray.length 75 | 76 | if (this.hasMore()) { 77 | this.addLocked() 78 | bookmodel.search(this.getCurrentStart(), this.data.q).then(res => { 79 | this.setMoreData(res.books) 80 | this.unLocked() 81 | }, () => { 82 | this.unLocked() 83 | }) 84 | } 85 | 86 | 87 | }, 88 | 89 | 90 | 91 | // 点击取消将搜索组件关掉,有两种方法:一是,在自己的内部创建一个变量控制显隐,不推荐,因为万一还有其他操作扩展性不好。二是,创建一个自定义事件,将自定义事件传给父级,让父级触发 92 | onCancel(event) { 93 | this.initialize() 94 | this.triggerEvent('cancel', {}, {}) 95 | }, 96 | 97 | // 触摸搜索图片里的x回到原来输入搜索的页面 98 | onDelete(event) { 99 | this.initialize() 100 | this._hideResult() 101 | }, 102 | 103 | // 在input输入框输入完成将输入的内容加到缓存中 104 | onConfirm(event) { 105 | // 为了用户体验好,应该点击完立即显示搜索页面 106 | this._showResult() 107 | // 显示loading效果 108 | this._showLoadingCenter() 109 | // 先清空上一次搜索的数据在加载 110 | // this.initialize() 111 | // 获取搜索的关键词q,调取search方法返回当summary=1,返回概要数据:并更新数据到dataArray中 112 | const q = event.detail.value || event.detail.text 113 | // 不用等数据加载完,输入完成后就把输入的内容显示在输入框中。 114 | this.setData({ 115 | q: q 116 | }) 117 | 118 | bookmodel.search(0, q).then(res => { 119 | this.setMoreData(res.books) 120 | this.setTotal(res.total) 121 | 122 | 123 | // 不能用户输入什么都保存在缓存中,只有用户搜索到有效的关键字时才保存到缓存中 124 | Keywordmodel.addToHistory(q) 125 | 126 | // 数据加载完成,取消显示loading效果 127 | this._hideLoadingCenter() 128 | 129 | }) 130 | 131 | }, 132 | 133 | // 更新变量的状态,显示搜索框 134 | _showResult() { 135 | this.setData({ 136 | searching: true 137 | }) 138 | }, 139 | 140 | // 更新变量的状态,隐藏搜索框 141 | _hideResult() { 142 | this.setData({ 143 | searching: false, 144 | q: '' 145 | }) 146 | }, 147 | 148 | 149 | 150 | // 改变loadingCenter的值 151 | _showLoadingCenter() { 152 | this.setData({ 153 | loadingCenter: true 154 | }) 155 | }, 156 | 157 | // 改变loadingCenter的值 158 | _hideLoadingCenter() { 159 | this.setData({ 160 | loadingCenter: false 161 | }) 162 | } 163 | 164 | 165 | 166 | } 167 | }) 168 | -------------------------------------------------------------------------------- /components/search/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "v-tag": "/components/tag/index", 5 | "v-book": "/components/book/index", 6 | "v-loading": "/components/loading/index" 7 | 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /components/search/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 取消 9 | 10 | 11 | 12 | 13 | 14 | 15 | 历史搜索 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 热门搜索 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 没有搜索到书籍 48 | 49 | 50 | -------------------------------------------------------------------------------- /components/search/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | width: 100%; 6 | /* padding: 0 15px; */ 7 | } 8 | 9 | .header { 10 | background-color: #fff; 11 | position: fixed; 12 | z-index: 99; 13 | height: 100rpx; 14 | border-top: 1px solid #f5f5ff; 15 | border-bottom: 1px solid #f5f5ff; 16 | display: flex; 17 | flex-direction: row; 18 | width: 750rpx; 19 | /* justify-content: space-between; */ 20 | align-items: center; 21 | /* padding: 0 15px; */ 22 | } 23 | 24 | .search-container { 25 | display: inline-flex; 26 | flex-direction: row; 27 | /* justify-content: space-between; */ 28 | align-items: center; 29 | background-color: #f5f5f5; 30 | border-radius: 50px; 31 | margin-left: 20rpx; 32 | } 33 | 34 | .icon { 35 | width: 14px; 36 | height: 14px; 37 | margin: 0 8px 0 12px 38 | } 39 | 40 | .bar { 41 | border-top-right-radius: 15px; 42 | border-bottom-right-radius: 15px; 43 | display: inline-block; 44 | height: 34px; 45 | width: 500rpx; 46 | font-size: 14px; 47 | } 48 | 49 | .cancel-img { 50 | width: 14px; 51 | height: 14px; 52 | margin-right: 10px; 53 | } 54 | 55 | .cancel { 56 | line-height: 34px; 57 | width: 60px; 58 | text-align: center; 59 | display: inline-block; 60 | border: none; 61 | } 62 | 63 | .history { 64 | width: 690rpx; 65 | margin: 40rpx 0 20rpx 0; 66 | display: flex; 67 | font-size: 14px; 68 | margin-top: 160rpx; 69 | flex-direction: column; 70 | } 71 | 72 | .title { 73 | line-height: 15px; 74 | display: flex; 75 | flex-direction: row; 76 | align-items: center; 77 | /* margin-left:100px; */ 78 | } 79 | 80 | .chunk { 81 | height: 15px; 82 | width: 5px; 83 | background-color: #000; 84 | display: inline-block; 85 | margin-right: 10px; 86 | } 87 | 88 | .hot-search { 89 | margin-top: 70rpx; 90 | } 91 | 92 | .tags { 93 | /* padding-left:15px; */ 94 | display: flex; 95 | flex-direction: row; 96 | flex-wrap: wrap; 97 | /* justify-content: flex-start; */ 98 | margin-top: 24rpx; 99 | padding-left: 15px; 100 | width: 630rpx; 101 | } 102 | 103 | .tags v-tag { 104 | margin-right: 10px; 105 | margin-bottom: 10px; 106 | /* padding-bottom: 10px; */ 107 | /* margin-right:6px; */ 108 | } 109 | 110 | .books-container { 111 | width: 570rpx; 112 | margin-top: 100rpx; 113 | display: flex; 114 | flex-direction: row; 115 | flex-wrap: wrap; 116 | padding: 0 90rpx 0 90rpx; 117 | justify-content: space-between; 118 | } 119 | 120 | .books-container v-book { 121 | margin-bottom: 25rpx; 122 | } 123 | 124 | .loading-center { 125 | position: absolute; 126 | top: 50%; 127 | left: 50%; 128 | } 129 | 130 | .loading { 131 | margin: 50rpx 0 50rpx 0; 132 | } 133 | 134 | .empty-tip { 135 | display: inline-block; 136 | width: 100%; 137 | text-align: center; 138 | position: absolute; 139 | top: 50%; 140 | /* left: 275rpx; */ 141 | } 142 | -------------------------------------------------------------------------------- /components/tag/index.js: -------------------------------------------------------------------------------- 1 | // components/tag/index.js 2 | Component({ 3 | 4 | // 启用slot 5 | options: { 6 | multipleSlots: true 7 | }, 8 | 9 | // 外部传进来的css,样式 10 | externalClasses: ['tag-class'], 11 | 12 | 13 | /** 14 | * 组件的属性列表 15 | */ 16 | properties: { 17 | text: String 18 | }, 19 | 20 | /** 21 | * 组件的初始数据 22 | */ 23 | data: { 24 | 25 | }, 26 | 27 | /** 28 | * 组件的方法列表 29 | */ 30 | methods: { 31 | // 触摸短评小标签时,触发的事件,触发一个自定义事件 32 | onTap(event) { 33 | this.triggerEvent('tapping', { 34 | text: this.properties.text 35 | }) 36 | } 37 | } 38 | }) 39 | -------------------------------------------------------------------------------- /components/tag/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /components/tag/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{text}} 4 | 5 | 6 | -------------------------------------------------------------------------------- /components/tag/index.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 4rpx 12rpx; 3 | background-color: #f5f5f5; 4 | color: #666; 5 | border-radius: 2px; 6 | display: inline-flex; 7 | flex-direction: row; 8 | align-items: center; 9 | justify-content: center; 10 | font-size: 28rpx; 11 | } 12 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | api_base_url: 'http://bl.7yue.pro/v1/', 3 | appkey: 'RdshydjBvcYZhMZC' 4 | // appkey: 'GgRhTjUNUYn1fHke' 5 | } 6 | 7 | export { 8 | config 9 | } 10 | -------------------------------------------------------------------------------- /images/book/quality.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/book/quality.png -------------------------------------------------------------------------------- /images/book/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/book/tip.png -------------------------------------------------------------------------------- /images/icon/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/icon/search.png -------------------------------------------------------------------------------- /images/icon/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/icon/share.png -------------------------------------------------------------------------------- /images/my/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/about.png -------------------------------------------------------------------------------- /images/my/course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/course.png -------------------------------------------------------------------------------- /images/my/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/like.png -------------------------------------------------------------------------------- /images/my/my.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/my.png -------------------------------------------------------------------------------- /images/my/my@bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/my@bg.png -------------------------------------------------------------------------------- /images/my/study.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/study.png -------------------------------------------------------------------------------- /images/my/vendor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/my/vendor.png -------------------------------------------------------------------------------- /images/tab/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/tab/book.png -------------------------------------------------------------------------------- /images/tab/book@highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/tab/book@highlight.png -------------------------------------------------------------------------------- /images/tab/classic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/tab/classic.png -------------------------------------------------------------------------------- /images/tab/classic@highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/tab/classic@highlight.png -------------------------------------------------------------------------------- /images/tab/my.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/tab/my.png -------------------------------------------------------------------------------- /images/tab/my@highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/images/tab/my@highlight.png -------------------------------------------------------------------------------- /models/book.js: -------------------------------------------------------------------------------- 1 | import { 2 | HTTP 3 | } from '../utils/http-promise' 4 | 5 | class BookModel extends HTTP { 6 | // 获取热门书籍 7 | getHotList() { 8 | return this.request({ 9 | url: '/book/hot_list' 10 | }) 11 | } 12 | 13 | // 获取喜欢书籍数量 14 | getMyBookCount() { 15 | return this.request({ 16 | url: '/book/favor/count' 17 | }) 18 | } 19 | 20 | // 获取书籍详细信息 21 | getDetail(bid) { 22 | return this.request({ 23 | url: `/book/${bid}/detail` 24 | }) 25 | } 26 | 27 | // 获取书籍点赞情况 28 | getLikeStatus(bid) { 29 | return this.request({ 30 | url: `/book/${bid}/favor` 31 | }) 32 | } 33 | 34 | // 获取书籍短评 35 | getComments(bid) { 36 | return this.request({ 37 | url: `/book/${bid}/short_comment` 38 | }) 39 | } 40 | 41 | // 新增短评 42 | postComment(bid, comment) { 43 | return this.request({ 44 | url: '/book/add/short_comment', 45 | method: 'POST', 46 | data: { 47 | book_id: bid, 48 | content: comment 49 | } 50 | }) 51 | } 52 | 53 | // 书籍搜索 54 | search(start, q) { 55 | return this.request({ 56 | url: '/book/search?summary=1', 57 | data: { 58 | q: q, 59 | start: start 60 | } 61 | }) 62 | } 63 | 64 | 65 | 66 | } 67 | 68 | export { 69 | BookModel 70 | } 71 | -------------------------------------------------------------------------------- /models/classic.js: -------------------------------------------------------------------------------- 1 | import { 2 | HTTP 3 | } from '../utils/http.js' 4 | 5 | class ClassicModel extends HTTP { 6 | constructor() { 7 | super() 8 | } 9 | 10 | //获取最新的期刊 11 | getLatest(cb) { 12 | this.request({ 13 | url: 'classic/latest', 14 | success: (res) => { 15 | 16 | this._setLatestIndex(res.index) 17 | cb(res) 18 | 19 | // 将最新的期刊设置到缓存中 20 | let key = this._getKey(res.index) 21 | wx.setStorageSync(key, res) 22 | } 23 | }) 24 | } 25 | 26 | // // 获取当前一期的上一期 27 | // getPrevious(index, cb) { 28 | // this.request({ 29 | // url: `classic/${index}/previous`, 30 | // success: (res) => { 31 | // cb(res) 32 | // } 33 | // }) 34 | // } 35 | 36 | // // 获取当前一期的下一期 37 | // getNext() { 38 | // this.request({ 39 | // url: `classic/${index}/next`, 40 | // success: (res) => { 41 | // cb(res) 42 | // } 43 | // }) 44 | // } 45 | 46 | // 因为getPrevious,getNext实现代码相似,所以为了简化代码可以合并为一个函数 47 | // 缓存思路:在缓存中寻找key,找不到就发送请求 API,将key写入到缓存中。解决每次都调用Api向服务器发请求,耗费性能 48 | // 在缓存中,确定key 49 | getClassic(index, nextOrPrevious, sCallback) { 50 | // 是next,获取下一期,否则获取上一期 51 | let key = nextOrPrevious === 'next' ? this._getKey(index + 1) : this._getKey(index - 1) 52 | let classic = wx.getStorageSync(key) 53 | if (!classic) { 54 | this.request({ 55 | url: `classic/${index}/${nextOrPrevious}`, 56 | success: (res) => { 57 | wx.setStorageSync(this._getKey(res.index), res) 58 | sCallback(res) 59 | } 60 | }) 61 | } else { //如果在缓存中有找到key,将返回的内容res保存到缓存中 62 | sCallback(classic) 63 | } 64 | 65 | } 66 | 67 | 68 | 69 | // 当前的期刊是否为第一期 70 | isFirst(index) { 71 | return index === 1 ? true : false 72 | } 73 | 74 | // 当前的期刊是否为最新的一期 75 | isLatest(index) { 76 | // lastestIndex get from storage 77 | let latestIndex = this._getLatestIndex() 78 | return latestIndex === index ? true : false 79 | } 80 | 81 | // 将最新的期刊index存入缓存 82 | _setLatestIndex(index) { 83 | wx.setStorageSync('latest', index) 84 | } 85 | 86 | // 在缓存中获取最新期刊的index 87 | _getLatestIndex() { 88 | let index = wx.getStorageSync('latest') 89 | return index 90 | } 91 | 92 | // 设置缓存中的key 93 | _getKey(index) { 94 | let key = `classic-${index}` 95 | return key 96 | } 97 | 98 | // 获取我喜欢期刊的所有信息 99 | getMyFavor(success) { 100 | const params = { 101 | url: 'classic/favor', 102 | success: success 103 | } 104 | this.request(params) 105 | } 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | } 129 | 130 | export { 131 | ClassicModel 132 | } 133 | -------------------------------------------------------------------------------- /models/keyword.js: -------------------------------------------------------------------------------- 1 | import { 2 | HTTP 3 | } from '../utils/http-promise' 4 | 5 | class KeywordModel extends HTTP { 6 | constructor() { 7 | super() 8 | // 把key属性挂载到当前实例上,供实例调取使用 9 | this.key = 'q', 10 | this.maxLength = 10 //搜索关键字的数组最大长度为10 11 | } 12 | 13 | //从缓存中,获取历史搜索关键字,如果缓存中没有直接返回什么也不做 14 | getHistory() { 15 | const words = wx.getStorageSync(this.key) 16 | if (!words) { 17 | return [] 18 | } 19 | return words 20 | } 21 | 22 | // 将历史搜索关键字写入缓存中。先从缓存中获取历史关键字的数组,判断是否已经有此关键字。如果没有,获取数组的长度大于最大长度,就将数组最后一项删除。获取数组的长度小于最大长度就将此次输入的关键字加到数组第一位,并且设置到缓存中; 23 | addToHistory(keyword) { 24 | let words = this.getHistory() 25 | const has = words.includes(keyword) 26 | if (!has && keyword.trim() !== '') { 27 | const length = words.length 28 | if (length >= this.maxLength) { 29 | words.pop() 30 | } 31 | words.unshift(keyword) 32 | wx.setStorageSync(this.key, words) 33 | } 34 | 35 | } 36 | 37 | // 获取热门搜素搜关键字 38 | getHot() { 39 | return this.request({ 40 | url: '/book/hot_keyword' 41 | }) 42 | } 43 | } 44 | 45 | export { 46 | KeywordModel 47 | } 48 | -------------------------------------------------------------------------------- /models/like.js: -------------------------------------------------------------------------------- 1 | import { 2 | HTTP 3 | } from '../utils/http' 4 | 5 | // 点赞组件 6 | class LikeModel extends HTTP { 7 | // 发送取消点赞,点赞API 8 | like(behavior, artID, category) { 9 | let url = behavior === 'like' ? 'like/cancel' : 'like' 10 | this.request({ 11 | url: url, 12 | method: 'POST', 13 | data: { 14 | art_id: artID, 15 | type: category 16 | } 17 | }) 18 | } 19 | 20 | // 获取点赞信息 21 | getClassicLikeStatus(artID, category, cb) { 22 | this.request({ 23 | url: `classic/${category}/${artID}/favor`, 24 | success: cb 25 | }) 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | } 46 | export { 47 | LikeModel 48 | } 49 | -------------------------------------------------------------------------------- /pages/about/about.js: -------------------------------------------------------------------------------- 1 | // pages/about/about.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad: function (options) { 15 | 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面初次渲染完成 20 | */ 21 | onReady: function () { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面显示 27 | */ 28 | onShow: function () { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面隐藏 34 | */ 35 | onHide: function () { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面卸载 41 | */ 42 | onUnload: function () { 43 | 44 | }, 45 | 46 | /** 47 | * 页面相关事件处理函数--监听用户下拉动作 48 | */ 49 | onPullDownRefresh: function () { 50 | 51 | }, 52 | 53 | /** 54 | * 页面上拉触底事件的处理函数 55 | */ 56 | onReachBottom: function () { 57 | 58 | }, 59 | 60 | /** 61 | * 用户点击右上角分享 62 | */ 63 | onShareAppMessage: function () { 64 | 65 | } 66 | }) -------------------------------------------------------------------------------- /pages/about/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "disableScroll":true 3 | } -------------------------------------------------------------------------------- /pages/about/about.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /pages/book/book.js: -------------------------------------------------------------------------------- 1 | import { 2 | BookModel 3 | } from '../../models/book' 4 | import { 5 | random 6 | } from '../../utils/common' 7 | 8 | const bookModel = new BookModel() 9 | 10 | 11 | // pages/book/book.js 12 | Page({ 13 | 14 | /** 15 | * 页面的初始数据 16 | */ 17 | data: { 18 | books: [], 19 | searching: false, //控制搜索框组件search显隐,默认不显示 20 | more: '' //是否需要加载更多数据,默认是不加载 21 | }, 22 | 23 | /** 24 | * 生命周期函数--监听页面加载 25 | */ 26 | onLoad: function(options) { 27 | // 获取热门书籍 28 | const hotList = bookModel.getHotList() 29 | hotList.then(res => { 30 | console.log(res) 31 | this.setData({ 32 | books: res 33 | }) 34 | }) 35 | }, 36 | 37 | // 触摸搜索触发onSearching事件,改变搜索组件searing的显隐 38 | onSearching(event) { 39 | this.setData({ 40 | searching: true 41 | }) 42 | }, 43 | 44 | // 调用子组件传递进来的自定义事件cancel,为其绑定事件onCancel,控制搜索组件searing的显隐 45 | onCancel(event) { 46 | this.setData({ 47 | searching: false 48 | }) 49 | }, 50 | 51 | /** 52 | * 生命周期函数--监听页面初次渲染完成 53 | */ 54 | onReady: function() { 55 | 56 | }, 57 | 58 | /** 59 | * 生命周期函数--监听页面显示 60 | */ 61 | onShow: function() { 62 | 63 | }, 64 | 65 | /** 66 | * 生命周期函数--监听页面隐藏 67 | */ 68 | onHide: function() { 69 | 70 | }, 71 | 72 | /** 73 | * 生命周期函数--监听页面卸载 74 | */ 75 | onUnload: function() { 76 | 77 | }, 78 | 79 | /** 80 | * 页面相关事件处理函数--监听用户下拉动作 81 | */ 82 | onPullDownRefresh: function() { 83 | 84 | }, 85 | 86 | /** 87 | * 页面上拉触底事件的处理函数 88 | */ 89 | onReachBottom: function() { 90 | console.log('到底了') 91 | this.setData({ 92 | more: random(16) 93 | }) 94 | }, 95 | 96 | /** 97 | * 用户点击右上角分享 98 | */ 99 | onShareAppMessage: function() { 100 | 101 | } 102 | }) 103 | -------------------------------------------------------------------------------- /pages/book/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "v-book": "/components/book/index", 4 | "v-search": "/components/search/index" 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /pages/book/book.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 搜索书籍 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /pages/book/book.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | width: 100%; 6 | } 7 | 8 | .sub-container { 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | background-color: #f5f5f5; 13 | margin-top: 100rpx; 14 | } 15 | 16 | .box { 17 | display: flex; 18 | flex-direction: row; 19 | justify-content: center; 20 | align-items: center; 21 | border-radius: 50px; 22 | background-color: #f5f5f5; 23 | height: 68rpx; 24 | width: 700rpx; 25 | color: #999; 26 | } 27 | 28 | .header { 29 | position: fixed; 30 | background-color: #fff; 31 | height: 100rpx; 32 | width: 100%; 33 | border-top: 1px solid #f5f5f5; 34 | display: flex; 35 | flex-direction: row; 36 | align-items: center; 37 | justify-content: center; 38 | box-shadow: 0 0 3px 0 #e3e3e3; 39 | z-index: 99; 40 | } 41 | 42 | .head-img { 43 | width: 106rpx; 44 | height: 34rpx; 45 | margin-top: 40rpx; 46 | } 47 | 48 | .box image { 49 | margin-right: 10px; 50 | width: 14px; 51 | height: 14px; 52 | margin-bottom: -2px; 53 | } 54 | 55 | .books-container { 56 | display: flex; 57 | flex-direction: row; 58 | margin-top: 10rpx; 59 | flex-wrap: wrap; 60 | padding: 0 90rpx 0 90rpx; 61 | justify-content: space-between; 62 | } 63 | 64 | .books-container v-book { 65 | margin-bottom: 10rpx; 66 | } 67 | -------------------------------------------------------------------------------- /pages/classic-detail/index.js: -------------------------------------------------------------------------------- 1 | // pages/classic-detail/index.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad: function (options) { 15 | 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面初次渲染完成 20 | */ 21 | onReady: function () { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面显示 27 | */ 28 | onShow: function () { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面隐藏 34 | */ 35 | onHide: function () { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面卸载 41 | */ 42 | onUnload: function () { 43 | 44 | }, 45 | 46 | /** 47 | * 页面相关事件处理函数--监听用户下拉动作 48 | */ 49 | onPullDownRefresh: function () { 50 | 51 | }, 52 | 53 | /** 54 | * 页面上拉触底事件的处理函数 55 | */ 56 | onReachBottom: function () { 57 | 58 | }, 59 | 60 | /** 61 | * 用户点击右上角分享 62 | */ 63 | onShareAppMessage: function () { 64 | 65 | } 66 | }) -------------------------------------------------------------------------------- /pages/classic-detail/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /pages/classic-detail/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | pages/classic-detail/index.wxml 3 | -------------------------------------------------------------------------------- /pages/classic-detail/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/classic-detail/index.wxss */ -------------------------------------------------------------------------------- /pages/classic/classic.js: -------------------------------------------------------------------------------- 1 | import { 2 | ClassicModel 3 | } from '../../models/classic.js' 4 | import { 5 | LikeModel 6 | } from '../../models/like.js' 7 | 8 | let classicModel = new ClassicModel() 9 | let likeModel = new LikeModel() 10 | Page({ 11 | 12 | /** 13 | * 页面的初始数据 14 | */ 15 | data: { 16 | classic: null, 17 | latest: true, 18 | first: false, 19 | likeCount: 0, 20 | likeStatus: false 21 | 22 | }, 23 | // 监听是否点赞 24 | onLike: function(event) { 25 | console.log(event) 26 | // 获取点赞还是取消的行为 27 | let behavior = event.detail.behavior 28 | likeModel.like(behavior, this.data.classic.id, this.data.classic.type) 29 | }, 30 | 31 | // 自定义事件 32 | // 获取当前一期的下一期 33 | onNext: function(evevt) { 34 | // let index = this.data.classic.index 35 | // classicModel.getNext(index, (res) => { 36 | // // console.log(res) 37 | // this.setData({ 38 | // classic: res, 39 | // latest: classicModel.isLatest(res.index), 40 | // first: classicModel.isFirst(res.index) 41 | // }) 42 | // }) 43 | 44 | this._updateClassic('next') 45 | }, 46 | // 获取当前一期的上一期 47 | onPrevious: function(evevt) { 48 | // let index = this.data.classic.index 49 | // classicModel.getPrevious(index, (res) => { 50 | // // console.log(res) 51 | // this.setData({ 52 | // classic: res, 53 | // latest: classicModel.isLatest(res.index), 54 | // first: classicModel.isFirst(res.index) 55 | // }) 56 | // }) 57 | 58 | this._updateClassic('previous') 59 | 60 | }, 61 | 62 | // 重复代码过多,利用函数封装的思想,新建一个函数抽取公共代码 63 | _updateClassic: function(nextOrPrevious) { 64 | let index = this.data.classic.index 65 | classicModel.getClassic(index, nextOrPrevious, (res) => { 66 | // console.log(res) 67 | this._getLikeStatus(res.id, res.type) 68 | this.setData({ 69 | classic: res, 70 | latest: classicModel.isLatest(res.index), 71 | first: classicModel.isFirst(res.index) 72 | }) 73 | }) 74 | }, 75 | 76 | // 获取点赞信息 77 | _getLikeStatus: function(artID, category) { 78 | likeModel.getClassicLikeStatus(artID, category, (res) => { 79 | this.setData({ 80 | likeCount: res.fav_nums, 81 | likeStatus: res.like_status 82 | }) 83 | }) 84 | }, 85 | 86 | 87 | /** 88 | * 生命周期函数--监听页面加载 89 | */ 90 | onLoad: function(options) { 91 | classicModel.getLatest((res) => { 92 | console.log(res) 93 | // this._getLikeStatus(res.id, res.type) //不能这样写,会多发一次favor请求,消耗性能 94 | this.setData({ 95 | classic: res, 96 | likeCount: res.fav_nums, 97 | likeStatus: res.like_status 98 | }) 99 | }) 100 | 101 | 102 | 103 | // http.request({ 104 | // url: 'classic/latest', 105 | // success: (res) => { 106 | // console.log(res) 107 | // } 108 | // }) 109 | // wx.request({//比较麻烦的做法,不用 110 | // url: 'http://bl.7yue.pro/v1/classic/latest', 111 | // header: { 112 | // appkey: "RdshydjBvcYZhMZC" 113 | // } 114 | // }) 115 | }, 116 | 117 | /** 118 | * 生命周期函数--监听页面初次渲染完成 119 | */ 120 | onReady: function() { 121 | 122 | }, 123 | 124 | /** 125 | * 生命周期函数--监听页面显示 126 | */ 127 | onShow: function() { 128 | 129 | }, 130 | 131 | /** 132 | * 生命周期函数--监听页面隐藏 133 | */ 134 | onHide: function() { 135 | 136 | }, 137 | 138 | /** 139 | * 生命周期函数--监听页面卸载 140 | */ 141 | onUnload: function() { 142 | 143 | }, 144 | 145 | /** 146 | * 页面相关事件处理函数--监听用户下拉动作 147 | */ 148 | onPullDownRefresh: function() { 149 | 150 | }, 151 | 152 | /** 153 | * 页面上拉触底事件的处理函数 154 | */ 155 | onReachBottom: function() { 156 | 157 | }, 158 | 159 | /** 160 | * 用户点击右上角分享 161 | */ 162 | onShareAppMessage: function() { 163 | 164 | } 165 | }) 166 | -------------------------------------------------------------------------------- /pages/classic/classic.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "v-like": "/components/like/index", 5 | "v-movie": "/components/classic/movie/index", 6 | "v-music": "/components/classic/music/index", 7 | "v-essay": "/components/classic/essay/index", 8 | "v-episode": "/components/episode/index", 9 | "v-navi": "/components/navi/index", 10 | "v-button": "/components/image-button/index" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pages/classic/classic.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pages/classic/classic.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | /* 尽量都设宽度为100%,防止出错 */ 3 | width: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | } 8 | 9 | .header { 10 | width: 100%; 11 | display: flex; 12 | flex-direction: row; 13 | height: 100rpx; 14 | align-items: center; 15 | justify-content: space-between; 16 | border-top: 1px solid #f5f5f5; 17 | border-bottom: 1px solid #f5f5f5; 18 | } 19 | 20 | .epsoide { 21 | margin-left: 20rpx; 22 | margin-top: 4rpx; 23 | } 24 | 25 | .like { 26 | margin-top: 6rpx; 27 | } 28 | 29 | .nav { 30 | position: absolute; 31 | bottom: 40rpx; 32 | } 33 | 34 | .like-container { 35 | display: flex; 36 | flex-direction: row; 37 | justify-content: space-between; 38 | align-items: center; 39 | margin-right: 30rpx; 40 | } 41 | 42 | .share-btn { 43 | margin-top: 28rpx; 44 | margin-right: 10rpx; 45 | } 46 | 47 | .share { 48 | width: 40rpx; 49 | height: 40rpx; 50 | } 51 | -------------------------------------------------------------------------------- /pages/course/course.js: -------------------------------------------------------------------------------- 1 | // pages/course/course.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | 9 | }, 10 | onShareAppMessage: function () { 11 | 12 | } 13 | }) -------------------------------------------------------------------------------- /pages/course/course.json: -------------------------------------------------------------------------------- 1 | { 2 | "backgroundColor": "#f5f5f5" 3 | } -------------------------------------------------------------------------------- /pages/course/course.wxml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/course/course.wxss: -------------------------------------------------------------------------------- 1 | .img{ 2 | width:100%; 3 | height:1984rpx; 4 | } -------------------------------------------------------------------------------- /pages/detail/detail.js: -------------------------------------------------------------------------------- 1 | import { 2 | BookModel 3 | } from '../../models/book' 4 | 5 | import { 6 | LikeModel 7 | } from '../../models/like' 8 | 9 | const bookModel = new BookModel() 10 | const likeModel = new LikeModel() 11 | 12 | 13 | 14 | 15 | // pages/detail/detail.js 16 | Page({ 17 | 18 | /** 19 | * 页面的初始数据 20 | */ 21 | data: { 22 | comments: [], 23 | book: null, 24 | likeStatus: false, 25 | likeCount: 0, 26 | posting: false //默认不显示评论输入框 27 | }, 28 | 29 | 30 | /** 31 | * 生命周期函数--监听页面加载 32 | */ 33 | onLoad: function(options) { 34 | // 数据加载时显示loading效果 35 | wx.showLoading() 36 | const bid = options.bid 37 | console.log(bid) 38 | // 获取书籍详细信息 39 | const detail = bookModel.getDetail(bid) 40 | // 获取书籍点赞情况 41 | const likeStatus = bookModel.getLikeStatus(bid) 42 | // 获取书籍短评 43 | const comments = bookModel.getComments(bid) 44 | 45 | // 数据加载完成时取消显示loading效果 46 | Promise.all([detail, comments, likeStatus]).then(res => { 47 | console.log(res) 48 | this.setData({ 49 | book: res[0], 50 | comments: res[1].comments, 51 | likeStatus: res[2].like_status, 52 | likeCount: res[2].fav_nums 53 | }) 54 | wx.hideLoading() 55 | }) 56 | 57 | // detail.then(res => { 58 | // console.log(res) 59 | // this.setData({ 60 | // book: res 61 | // }) 62 | // }) 63 | 64 | // likeStatus.then(res => { 65 | // console.log(res) 66 | // this.setData({ 67 | // likeStatus: res.like_status, 68 | // likeCount: res.fav_nums 69 | // }) 70 | // }) 71 | 72 | // comments.then(res => { 73 | // console.log(res) 74 | // this.setData({ 75 | // comments: res.comments 76 | // }) 77 | // }) 78 | }, 79 | 80 | // 触摸点赞组件事件 81 | onLike(event) { 82 | const like_or_cancel = event.detail.behavior 83 | likeModel.like(like_or_cancel, this.data.book.id, 400) 84 | }, 85 | 86 | // 触摸评论输入框弹出评论窗口 87 | onFakePost(enent) { 88 | this.setData({ 89 | posting: true 90 | }) 91 | }, 92 | 93 | // 在弹出的评论窗口中点取消,让评论窗口消失 94 | onCancl(event) { 95 | this.setData({ 96 | posting: false 97 | }) 98 | }, 99 | 100 | // 触摸tag组件会触发,input输入框也会触发事件onPost 101 | onPost(event) { 102 | // 获取触发tag里的内容或获取用户input输入的内容 103 | const comment = event.detail.text || event.detail.value 104 | // 获取用户input输入的内容 105 | // const commentInput = event.detail.value 106 | 107 | console.log('comment' + comment) 108 | console.log('commentInput' + comment) 109 | 110 | 111 | 112 | // 对tag里的内容或对用户输入的评论做校验 113 | if (comment.length > 12 || !comment) { 114 | wx.showToast({ 115 | title: '短评最多12个字', 116 | icon: 'none' 117 | }) 118 | return 119 | } 120 | 121 | // 调用新增短评接口并将最新的评论插到comments数组的第一位,并且把蒙层mask关闭 122 | bookModel.postComment(this.data.book.id, comment).then(res => { 123 | wx.showToast({ 124 | title: '+1', 125 | icon: 'none' 126 | }) 127 | this.data.comments.unshift({ 128 | content: comment, 129 | nums: 1 //这是后面的数量显示 130 | }) 131 | this.setData({ 132 | comments: this.data.comments, 133 | posting: false 134 | }) 135 | }) 136 | }, 137 | 138 | /** 139 | * 生命周期函数--监听页面初次渲染完成 140 | */ 141 | onReady: function() { 142 | 143 | }, 144 | 145 | /** 146 | * 生命周期函数--监听页面显示 147 | */ 148 | onShow: function() { 149 | 150 | }, 151 | 152 | /** 153 | * 生命周期函数--监听页面隐藏 154 | */ 155 | onHide: function() { 156 | 157 | }, 158 | 159 | /** 160 | * 生命周期函数--监听页面卸载 161 | */ 162 | onUnload: function() { 163 | 164 | }, 165 | 166 | /** 167 | * 页面相关事件处理函数--监听用户下拉动作 168 | */ 169 | onPullDownRefresh: function() { 170 | 171 | }, 172 | 173 | /** 174 | * 页面上拉触底事件的处理函数 175 | */ 176 | onReachBottom: function() { 177 | 178 | }, 179 | 180 | /** 181 | * 用户点击右上角分享 182 | */ 183 | onShareAppMessage: function() { 184 | 185 | } 186 | }) 187 | -------------------------------------------------------------------------------- /pages/detail/detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "v-tag": "/components/tag/index", 4 | "v-like": "/components/like/index", 5 | "v-mask": "/components/mask/index", 6 | "v-button": "/components/image-button/index" 7 | 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /pages/detail/detail.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{book.title}} 10 | {{book.author}} 11 | 12 | 13 | 14 | 短评 15 | 还没有短评 16 | 17 | 18 | 19 | 20 | {{'+'+item.nums}} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 内容简介 28 | {{util.format(book.summary)}} 29 | 30 | 31 | 32 | 书本信息 33 | 34 | 35 | 出版社 36 | 出版年 37 | 页数 38 | 定价 39 | 装帧 40 | 41 | 42 | {{book.publisher}} 43 | {{book.pubdate}} 44 | {{book.pages}} 45 | {{book.price}} 46 | {{book.binding}} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 输入短评 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 尽可点击标签+1 68 | 暂无短评 69 | 70 | 取消 71 | 72 | 73 | 74 | 75 | {{'+'+item.nums}} 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | var highlight = function(index){ 88 | if(index===0){ 89 | return 'ex-tag1' 90 | } 91 | else if(index===1){ 92 | return 'ex-tag2' 93 | } 94 | return '' 95 | } 96 | module.exports={ 97 | highlight:highlight 98 | } 99 | -------------------------------------------------------------------------------- /pages/detail/detail.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: #f5f5f5; 3 | width: 100%; 4 | } 5 | 6 | .head { 7 | background-color: #fff; 8 | padding-top: 40rpx; 9 | padding-bottom: 40rpx; 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | } 14 | 15 | .title { 16 | color: #2f2f2f; 17 | margin-top: 20rpx; 18 | font-size: 38rpx; 19 | font-weight: 600; 20 | } 21 | 22 | .author { 23 | font-size: 28rpx; 24 | color: #999; 25 | } 26 | 27 | .head image { 28 | width: 200rpx; 29 | height: 300rpx; 30 | box-shadow: 2px 2px 3px #e3e3e3; 31 | } 32 | 33 | .sub-container { 34 | width: 690rpx; 35 | display: flex; 36 | flex-direction: column; 37 | align-items: center; 38 | margin-top: 30rpx; 39 | background-color: #fff; 40 | padding: 30rpx; 41 | } 42 | 43 | .headline { 44 | font-size: 30rpx; 45 | font-weight: 600; 46 | color: #2f2f2f; 47 | margin-bottom: 20rpx; 48 | } 49 | 50 | .comment-container { 51 | display: flex; 52 | flex-direction: row; 53 | flex-wrap: wrap; 54 | } 55 | 56 | .comment-container v-tag { 57 | margin-right: 15rpx; 58 | margin-bottom: 10rpx; 59 | } 60 | 61 | .num { 62 | margin-left: 10px; 63 | font-size: 22rpx; 64 | color: #aaa; 65 | } 66 | 67 | .content { 68 | text-indent: 58rpx; 69 | font-weight: 500; 70 | } 71 | 72 | 73 | /* v-tag是自定义组件,不能使用css3,在微信小程序中,只有内置组件才可以用css3 */ 74 | 75 | 76 | /* .comment-container>v-tag:nth-child(1)>view { 77 | background-color: #fffbdd; 78 | } 79 | 80 | .comment-container>v-tag:nth-child(2)>view { 81 | background-color: #eefbff; 82 | } */ 83 | 84 | 85 | /* 定义外部样式 */ 86 | 87 | .ex-tag1 { 88 | background-color: #fffbdd !important; 89 | } 90 | 91 | .ex-tag2 { 92 | background-color: #eefbff !important; 93 | } 94 | 95 | .detail-container { 96 | width: 100%; 97 | display: flex; 98 | flex-direction: row; 99 | justify-content: flex-start; 100 | margin-bottom: 100rpx; 101 | font-size: 28rpx; 102 | color: #666; 103 | } 104 | 105 | .vertical { 106 | display: flex; 107 | flex-direction: column; 108 | } 109 | 110 | .description { 111 | color: #999; 112 | margin-right: 30rpx; 113 | } 114 | 115 | .post-container { 116 | height: 100rpx; 117 | box-shadow: 1px -1px 1px #e3e3e3; 118 | position: fixed; 119 | width: 690rpx; 120 | background-color: #fff; 121 | bottom: 0; 122 | display: flex; 123 | flex-direction: row; 124 | align-items: center; 125 | padding: 0 30rpx; 126 | justify-content: space-between; 127 | } 128 | 129 | .post-fake { 130 | display: flex; 131 | flex-direction: row; 132 | align-items: center; 133 | height: 60rpx; 134 | width: 460rpx; 135 | border: 1px solid #999; 136 | border-radius: 15px; 137 | font-size: 22rpx; 138 | padding-left: 20rpx; 139 | } 140 | 141 | .like { 142 | margin-right: 30rpx; 143 | margin-top: 10rpx; 144 | } 145 | 146 | .like-container { 147 | display: flex; 148 | flex-direction: row; 149 | justify-content: space-between; 150 | align-items: center; 151 | } 152 | 153 | .posting-container { 154 | bottom: 0; 155 | position: fixed; 156 | display: flex; 157 | flex-direction: column; 158 | align-items: center; 159 | background-color: #fff; 160 | width: 100%; 161 | z-index: 999; 162 | } 163 | 164 | .post-header { 165 | width: 100%; 166 | height: 100rpx; 167 | border-bottom: 1px solid #f5f5f5; 168 | border-top: 1px solid #f5f5f5; 169 | display: flex; 170 | flex-direction: row; 171 | align-items: center; 172 | justify-content: space-between; 173 | } 174 | 175 | .cancel { 176 | color: #666; 177 | } 178 | 179 | .post-header text { 180 | padding: 25rpx; 181 | } 182 | 183 | .post-header>text:first-child { 184 | font-size: 28rpx; 185 | color: #bbb; 186 | } 187 | 188 | .posting-container .comment-container { 189 | width: 690rpx; 190 | padding: 40rpx 30rpx 0 30rpx; 191 | } 192 | 193 | .post { 194 | width: 690rpx; 195 | height: 56rpx; 196 | margin: 30rpx auto; 197 | background-color: #f5f5f5; 198 | border-radius: 15px; 199 | padding-left: 25rpx; 200 | } 201 | 202 | .shadow { 203 | color: #999; 204 | } 205 | 206 | .like-container { 207 | display: flex; 208 | flex-direction: row; 209 | justify-content: space-between; 210 | align-items: center; 211 | margin-right: 30rpx; 212 | } 213 | 214 | .share-btn { 215 | margin-top: 28rpx; 216 | margin-right: 10rpx; 217 | } 218 | 219 | .share { 220 | width: 40rpx; 221 | height: 40rpx; 222 | } 223 | -------------------------------------------------------------------------------- /pages/my/my.js: -------------------------------------------------------------------------------- 1 | import { 2 | ClassicModel 3 | } from '../../models/classic' 4 | 5 | import { 6 | BookModel 7 | } from '../../models/book' 8 | 9 | const classicModel = new ClassicModel() 10 | const bookModel = new BookModel() 11 | 12 | 13 | 14 | 15 | Page({ 16 | data: { 17 | authorized: false, //切换用户头像显示 18 | userInfo: null, //用户信息 19 | bookCount: 0, //喜欢的书 20 | classics: null //获取我喜欢期刊的所有信息 21 | }, 22 | onLoad() { 23 | // 查看是否授权 24 | this.userAuthorized() 25 | // 获取喜欢的书的数量 26 | this.getMyBookCount() 27 | // 获取我喜欢期刊的所有信息 28 | this.getMyFavor() 29 | }, 30 | 31 | // 从服务器获取喜欢的书的数量 32 | getMyBookCount() { 33 | bookModel.getMyBookCount().then(res => { 34 | this.setData({ 35 | bookCount: res.count 36 | }) 37 | }) 38 | }, 39 | 40 | // 获取我喜欢期刊的所有信息 41 | getMyFavor() { 42 | classicModel.getMyFavor(res => { 43 | this.setData({ 44 | classics: res 45 | }) 46 | }) 47 | }, 48 | 49 | // 查看是否授权, 已经授权,可以直接调用 getUserInfo 获取头像昵称 50 | userAuthorized() { 51 | wx.getSetting({ 52 | success: data => { 53 | if (data.authSetting['scope.userInfo']) { 54 | wx.getUserInfo({ 55 | success: data => { 56 | console.log(data) 57 | this.setData({ 58 | authorized: true, 59 | userInfo: data.userInfo 60 | }) 61 | } 62 | }) 63 | } else { 64 | console.log('err') 65 | } 66 | } 67 | }) 68 | }, 69 | 70 | 71 | onGetUserInfo(e) { 72 | const userInfo = e.detail.userInfo 73 | console.log(userInfo) 74 | if (userInfo) { 75 | this.setData({ 76 | userInfo, 77 | authorized: true 78 | }) 79 | } 80 | 81 | }, 82 | 83 | // 触摸跳转到另一个页面 84 | onJumpToAbout(event) { 85 | wx.navigateTo({ 86 | url: '/pages/about/about' 87 | }) 88 | }, 89 | 90 | onStudy(event) { 91 | wx.navigateTo({ 92 | url: '/pages/course/course' 93 | }) 94 | } 95 | 96 | 97 | 98 | 99 | 100 | }) 101 | -------------------------------------------------------------------------------- /pages/my/my.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "v-button": "/components/image-button/index", 4 | "v-preview": "/components/preview/index" 5 | 6 | }, 7 | "backgroundColor": "#f5f5f5" 8 | } 9 | -------------------------------------------------------------------------------- /pages/my/my.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{userInfo.nickName}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 关于我们 20 | 21 | 22 | {{bookCount}} 23 | 喜欢的书 24 | 25 | 26 | 27 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /pages/my/my.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .bg { 8 | width: 750rpx; 9 | height: 574rpx; 10 | } 11 | 12 | .about-container { 13 | padding: 0 100rpx; 14 | width: 550rpx; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: space-between; 18 | position: absolute; 19 | top: 440rpx; 20 | } 21 | 22 | .about-us { 23 | display: flex; 24 | flex-direction: column; 25 | align-items: center; 26 | justify-content: space-between; 27 | } 28 | 29 | .about-us image { 30 | width: 34rpx; 31 | height: 34rpx; 32 | } 33 | 34 | .description { 35 | font-size: 24rpx; 36 | color: #999999; 37 | } 38 | 39 | .about-container>view:nth-child(2) { 40 | margin-top: -5rpx; 41 | } 42 | 43 | .avatar { 44 | width: 120rpx; 45 | height: 120rpx; 46 | border-radius: 50%; 47 | /* 加上overflow:hidden才会显示圆 */ 48 | overflow: hidden; 49 | } 50 | 51 | .avatar-position { 52 | position: absolute; 53 | top: 255rpx; 54 | } 55 | 56 | .avatar-container { 57 | display: flex; 58 | flex-direction: column; 59 | align-items: center; 60 | } 61 | 62 | .study { 63 | width: 88rpx; 64 | height: 88rpx; 65 | position: absolute; 66 | top: 40rpx; 67 | right: 45rpx; 68 | } 69 | 70 | .book-num { 71 | font-size: 36rpx; 72 | color: #000000; 73 | } 74 | 75 | .like-container { 76 | /* margin-top:30rpx; */ 77 | width: 100%; 78 | margin-top: -13rpx; 79 | display: flex; 80 | flex-direction: column; 81 | align-items: center; 82 | background-color: #f5f5f5; 83 | } 84 | 85 | .headline { 86 | margin-top: 30rpx; 87 | width: 97rpx; 88 | height: 42rpx; 89 | } 90 | 91 | .preview-container { 92 | margin-top: 30rpx; 93 | display: flex; 94 | flex-direction: row; 95 | padding: 0 30rpx; 96 | flex-wrap: wrap; 97 | justify-content: space-between; 98 | } 99 | 100 | .preview { 101 | margin-bottom: 30rpx; 102 | } 103 | -------------------------------------------------------------------------------- /reademeimages/aixin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/reademeimages/aixin.jpg -------------------------------------------------------------------------------- /reademeimages/navi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wudiufo/WeChat-Mini-Program/1e715c852b7ae555f45e613f8412fd43d013ec3f/reademeimages/navi.jpg -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### 小爱心是否点赞组件 `components/like` 2 | 3 | ![](./reademeimages/aixin.jpg) 4 | 5 | 思路: 6 | 7 | like 默认为 false,显示空心小爱心 8 | 9 | 触摸执行`tap:onLike` 方法,因为 `this.setData({count:count,like:!like})`是异步的,先执行`count = like ? count - 1 : count + 1`,这时like还是false,执行count+1。然后在执行this.setData()方法,将like变为true,显示实心小爱心。 10 | 11 | ```js 12 | let behavior = like ? 'like' : 'cancel' 13 | //自定义事件 14 | this.triggerEvent('like', { 15 | behavior: behavior 16 | }, {}) 17 | ``` 18 | 19 | 自定义事件like,当like为真时,behavior为like,在`models/like.js`中,`let url = behavior === 'like' ? 'like/cancel' : 'like'`,因为`behavior === 'like'`为真,就调用服务器接口'like/cancel',相反就调用like接口。 20 | 21 | 刚开始实心就调用'like/cancel'接口,空心就调用'like'接口 22 | 23 | --------------------- 24 | 25 | ### 底部左右切换组件 ` components/navi` 26 | 27 | ![](./reademeimages/navi.jpg) 28 | 29 | 思路: 30 | 31 | ##### 在`navi/index.js`中: 32 | 33 | ```js 34 | 先定义哪些数据是外部传来的数据,哪些数据是私有数据 35 | properties: {//外部传来的数据 36 | title: String, 37 | first: Boolean, //如果是第一期向右的箭头就禁用,默认是false 38 | latest: Boolean //如果是最新的一期向左的箭头就禁用,默认是false 39 | }, 40 | data: {//私有数据 41 | disLeftSrc: './images/triangle.dis@left.png', 42 | leftSrc: './images/triangle@left.png', 43 | disRightSrc: './images/triangle.dis@right.png', 44 | rightSrc: './images/triangle@right.png' 45 | }, 46 | 47 | ``` 48 | 49 | 50 | 51 | #### 左箭头: 52 | 53 | >在navi/index.wxml中`` 54 | > 55 | >src显示图片规则:如果是最新的期刊,就显示向左禁用状态disLeftSrc箭头;如果不是最新一期的期刊,就显示向左可用状态leftSrc箭头 56 | > 57 | >为图片绑定触摸事件onLeft,在`navi/index.js`中: 58 | > 59 | >```js 60 | >在 methods 中:如果不是最新的期刊,就继续绑定自定义事件left 61 | >onLeft: function(event) { //不是最新一期 62 | > if (!this.properties.latest) { 63 | > this.triggerEvent('left', {}, {}) 64 | > } 65 | > 66 | > }, 67 | >``` 68 | > 69 | > 70 | 71 | #### 右箭头: 72 | 73 | >在navi/index.wxml中`` 74 | > 75 | >src显示图片规则:如果是第一期的期刊,就显示向右禁用状态disRightSrc箭头;如果不是第一期的期刊,就显示向右可用状态rightSrc箭头 76 | > 77 | >为图片绑定触摸事件onRight,在`navi/index.js`中: 78 | > 79 | >```js 80 | >在 methods 中:如果不是第一期的期刊,就继续绑定自定义事件right 81 | >onRight: function(event) { //不是第一期 82 | > if (!this.properties.first) { 83 | > this.triggerEvent('right', {}, {}) 84 | > } 85 | > 86 | > } 87 | >``` 88 | 89 | ##### 在`pages/classic`中: 90 | 91 | > 92 | > 93 | >```js 94 | >1:在 classic.json 中,注册使用navi自定义组件 95 | >{ 96 | > "usingComponents": { 97 | > "v-like": "/components/like/index", 98 | > "v-movie": "/components/classic/movie/index", 99 | > "v-episode": "/components/episode/index", 100 | > "v-navi": "/components/navi/index" 101 | > } 102 | >} 103 | >2:在 classic.wxml 中:绑定自定义事件left, 获取当前一期的下一期;绑定自定义事件right,获取当前一期的上一期 104 | > 105 | > 106 | >3:在 classic.js 中: 107 | >// 获取当前一期的下一期,左箭头 108 | >onNext: function(evevt) {this._updateClassic('next') 109 | > }, 110 | >// 获取当前一期的上一期,右箭头 111 | > onPrevious: function(evevt) { this._updateClassic('previous') 112 | > }, 113 | >// 重复代码过多,利用函数封装的思想,新建一个函数抽取公共代码 114 | >// 发送请求,获取当前页的索引,更新数据 115 | > _updateClassic: function(nextOrPrevious) { 116 | > let index = this.data.classic.index 117 | > classicModel.getClassic(index, nextOrPrevious, (res) => { 118 | > // console.log(res) 119 | > this.setData({ 120 | > classic: res, 121 | > latest: classicModel.isLatest(res.index), 122 | > first: classicModel.isFirst(res.index) 123 | > }) 124 | > }) 125 | > }, 126 | > 127 | > 4:在 models/classic.js 中: 128 | > // 当前的期刊是否为第一期,first就变为true,右箭头就显示禁用 129 | > isFirst(index) { 130 | > return index === 1 ? true : false 131 | > } 132 | >// 当前的期刊是否为最新的一期,latest就变为TRUE,左箭头就显示禁用 133 | >// 由于服务器数据还会更新,确定不了最新期刊的索引,所以就要利用缓存机制,将最新期刊的索引存入到缓存中,如果外界传进来的索引和缓存的最新期刊的索一样,latest就变为TRUE,左箭头就显示禁用 134 | > isLatest(index) { 135 | > let latestIndex = this._getLatestIndex() 136 | > return latestIndex === index ? true : false 137 | > } 138 | >// 将最新的期刊index存入缓存 139 | > _setLatestIndex(index) { 140 | > wx.setStorageSync('latest', index) 141 | > } 142 | > 143 | > // 在缓存中获取最新期刊的index 144 | > _getLatestIndex() { 145 | > let index = wx.getStorageSync('latest') 146 | > return index 147 | > } 148 | >``` 149 | 150 | 151 | 152 | ### 优化缓存。解决每次触摸左右箭头都会频繁向服务器发送请求,这样非常耗性能,用户体验极差。解决方法,就是把第一次发送请求的数据都缓存到本地,再次触摸箭头时,会先查找本地缓存是否有数据,有就直接从缓存中读取数据,没有就在向服务器发送请求,这样利用缓存机制大大的提高了用户的体验。(但也有一部分是需要实时更新的,比如是否点赞的小爱心组件,需要每次都向服务器发送请求获取最新数据) 153 | 154 | ##### 在 models/classic.js 中: 155 | 156 | ```js 157 | 1: 158 | // 设置缓存中的key 的样式,classic-1这种样式 159 | _getKey(index) { 160 | let key = `classic-${index}` 161 | return key 162 | } 163 | 2: 164 | // 因为getPrevious,getNext实现代码相似,所以为了简化代码可以合并为一个函数 165 | // 缓存思路:在缓存中寻找key,找不到就发送请求 API,将key写入到缓存中。解决每次都调用Api向服务器发请求,耗费性能 166 | // 在缓存中,确定key 167 | getClassic(index, nextOrPrevious, sCallback) { 168 | //0: 是next,触摸向左箭头获取下一期,触摸向右箭头否则获取上一期 169 | let key = nextOrPrevious === 'next' ? this._getKey(index + 1) : this._getKey(index - 1) 170 | //1:在缓存中寻找key 171 | let classic = wx.getStorageSync(key) 172 | //2:如果缓存中找不到key,就调用服务器API发送请求获取数据 173 | if (!classic) { 174 | this.request({ 175 | url: `classic/${index}/${nextOrPrevious}`, 176 | success: (res) => { 177 | //将获取到的数据设置到缓存中 178 | wx.setStorageSync(this._getKey(res.index), res) 179 | //再把获取到的数据返回,供用户调取使用 180 | sCallback(res) 181 | } 182 | }) 183 | } else { //3:如果在缓存中有找到key,将缓存中key对应的value值,返回给用户,供用户调取使用 184 | sCallback(classic) 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- 189 | 190 | // 获取最新的期刊利用缓存机制进一步优化 191 | //获取最新的期刊 192 | getLatest(cb) { 193 | this.request({ 194 | url: 'classic/latest', 195 | success: (res) => { 196 | //将最新的期刊index存入缓存,防止触摸向左箭头时,没有设置latest的值,左箭头会一直触发发送请求找不到最新的期刊报错 197 | this._setLatestIndex(res.index) 198 | //再把获取到的数据返回,供用户调取使用 199 | cb(res) 200 | // 将最新的期刊设置到缓存中,先调取 this._getKey() 方法,为最新获取的期刊设置key值,调用微信设置缓存方法将key,和对应的value值res存进去 201 | let key = this._getKey(res.index) 202 | wx.setStorageSync(key, res) 203 | 204 | } 205 | }) 206 | } 207 | ``` 208 | 209 | 处理是否点赞小爱心组件的缓存问题:他不需要缓存,需要实时获取最新数据 210 | 211 | ##### 在 models/like.js 中: 212 | 213 | ```js 214 | //编写一个获取点赞信息的方法,从服务器获取最新点赞信息的数据 215 | // 获取点赞信息 216 | getClassicLikeStatus(artID, category, cb) { 217 | this.request({ 218 | url: `classic/${category}/${artID}/favor`, 219 | success: cb 220 | }) 221 | } 222 | 223 | ``` 224 | 225 | ##### 在 pages/classic/classic.js 中: 226 | 227 | ```js 228 | //设置私有数据初始值 229 | data: { 230 | classic: null, 231 | latest: true, 232 | first: false, 233 | likeCount: 0,//点赞的数量 234 | likeStatus: false //点赞的状态 235 | 236 | }, 237 | 238 | // 在classic.wxml中: