Repository: MengZhaoFly/wechatApp-netease_cloudmusic Branch: master Commit: 881767e92f5a Files: 35 Total size: 77.3 KB Directory structure: gitextract_ejhowmuo/ ├── README.md ├── app.js ├── app.json ├── app.wxss ├── common.js ├── pages/ │ ├── account/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ ├── index.wxss │ │ ├── logs.js │ │ ├── logs.json │ │ ├── logs.wxml │ │ └── logs.wxss │ ├── find/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── index/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── logs/ │ │ ├── logs.js │ │ ├── logs.json │ │ ├── logs.wxml │ │ └── logs.wxss │ ├── my/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── now/ │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── style/ │ └── weui.wxss └── utils/ └── util.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ ![](https://img.shields.io/badge/language-js-orange.svg) ![](https://img.shields.io/badge/platform-wechat-lightgrey.svg) ## 初窥 ![](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/all.gif)
todo: - [ ] 添加音乐到收藏(最近)列表 - [ ] 歌词滚动 ## 从一个hello world开始 微信开发者工具生成 目录如下: ``` . |-- app.js |-- app.json |-- app.wxss |-- pages | |-- index # 主页 | | |-- index.js | | |-- index.json | | |-- index.wxml | | `-- index.wxss | `-- log # 日志页面 | | |-- log.js | | |-- log.json | | |-- log.wxml | | `-- log.wxss `-- utils # 工具 `-- util.js ``` 大体为: 每一个page即是一个页面文件 ,每个页面有一个js/wxml/wxss/json文件 规定:**描述页面的这四个文件必须具有相同的路径与文件名。**
全局下同路,为公共的逻辑,样式,配置
与html不同:用`view text navigator` 代替 `div span a` ## 开发者文档走马观花 **app.json:** 注册pages window tabBar networkTimeout
[组件说明](https://mp.weixin.qq.com/debug/wxadoc/dev/component/)
***.js:** 作为逻辑层 与wxml交互 有着丰富的 网络, 媒体, 文件, 数据缓存, 位置, 设备, 界面...的api
[官方文档](https://mp.weixin.qq.com/debug/wxadoc/dev/api/)
***.wxml:** 数据驱动的视图层 + 微信提供了大量的组件 表单 导航 媒体 ... ## 官方组件不够,weui来凑 weui为小程序提供了 weui.wxcss 但大多是造官方组件的轮子
这里精选,也算是补充两个常用组件
**对于小程序没有DOM操作 不熟悉mvvm思想的同学 是个很好的入门** 1. navbar
![navbar](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/tabbar.gif) ```html {{item}} ``` block渲染data里面的四个tabs,slider为激活tab选项时候的表现,panel为内容面板 ```js //js var sliderWidth = 96; // 需要设置slider的宽度,用于计算中间位置 Page({ data: { tabs: ["选项一", "选项二", "选项三"], activeIndex: 1, sliderOffset: 0, sliderLeft: 0 }, onLoad: function () { var that = this; wx.getSystemInfo({ success: function(res) { that.setData({ sliderLeft: (res.windowWidth / that.data.tabs.length - sliderWidth) / 2, sliderOffset: res.windowWidth / that.data.tabs.length * that.data.activeIndex }); } }); }, tabClick: function (e) { this.setData({ sliderOffset: e.currentTarget.offsetLeft, activeIndex: e.currentTarget.id }); } }); ``` 了解mvvm思想的同学不难看出 通过tabs数组渲染出来选项后每次点击获取id 然后通过设置hidden显示或隐藏 2. searchbar
![searchbar](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/searchbar.gif) ```html 实时搜索文本 ``` 一个input输入框+一个搜索label+一个清楚内容的icon + 取消按钮 ```js Page({ data: { inputShowed: false, inputVal: "" }, showInput: function () { this.setData({ inputShowed: true }); }, hideInput: function () { this.setData({ inputVal: "", inputShowed: false }); }, clearInput: function () { this.setData({ inputVal: "" }); }, inputTyping: function (e) { this.setData({ inputVal: e.detail.value }); } }); ``` input上面有一层label 通过Page里面状态的改变而操作其wxml状态的改变
不难体会到:**小程序和Vue**的思想还是挺接近的 ## 站在巨人的肩膀上为大佬们提供云音乐api ---获取云音乐api >[巨人的源github项目](https://github.com/Binaryify/NeteaseCloudMusicApi)
在此我将他部署到leancloud上
即可在线访问,免去烦人的本地localhost启动,在线url
http://neteasemusic.leanapp.cn
调用例子:
http://neteasemusic.leanapp.cn/search?keywords=海阔天空
http://neteasemusic.leanapp.cn/lyric?id=347230
![搜索结果](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/searchresult.jpg) 具体参考api
>[详细文档](https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=neteasecloudmusicapi)
## 一切具备 只欠东风 生成目录 ``` . |-- app.js |-- app.json |-- app.wxss |-- common.js #公用js |-- images #存放项目图片 |-- style |   |-- weui.wxss   # 引入weui样式  万一你自己不想写css样式呢 |-- pages |   |-- find   # 发现音乐 |   |   |-- index.js | | |-- index.json | | |-- index.wxml | | `-- index.wxss |   |--my   # 我的音乐 |   |   |-- index.js | | |-- index.json | | |-- index.wxml | | `-- index.wxss |   |--now  # 正在播放 |   |   |-- index.js | | |-- index.json | | |-- index.wxml | | `-- index.wxss |   |--account   # 账号 |   |   |-- index.js | | |-- index.json | | |-- index.wxml | | `-- index.wxss | |-- index # 主页 | | |-- index.js | | |-- index.json | | |-- index.wxml | | `-- index.wxss | `-- log # 日志页面 `-- utils # 工具 `-- util.js ``` 请先在在app.json中注册页面,设置navigation,配置tabbar
```js { "pages":[ "pages/find/index", "pages/my/index", "pages/now/index", "pages/account/index", "pages/index/index" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#D43C33", "navigationBarTitleText": "网易云音乐", "navigationBarTextStyle":"white", "backgroundColor": "#FBFCFD" }, "tabBar": { "backgroundColor":"#2A2C2E", "color": "#a7a7a7", "selectedColor": "#ffffff", "list": [{ "iconPath":"./images/find.png", "selectedIconPath":"./images/find1.png", "pagePath":"pages/find/index", "text": "发现音乐" }, { "iconPath":"./images/my.png", "selectedIconPath":"./images/my1.png", "pagePath": "pages/my/index", "text": "我的音乐" }, { "iconPath":"./images/now.png", "selectedIconPath":"./images/now1.png", "pagePath": "pages/now/index", "text": "正在播放" }, { "iconPath":"./images/account.png", "selectedIconPath":"./images/account1.png", "pagePath": "pages/account/index", "text": "账号" }] } } ``` - 发现音乐
![](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/mysong.jpg)
布局分为搜索框,navbar,swiper滑动,三列,以及两行三列构成
tips:小程序中`flex`布局基本无兼容性问题 ,可大胆使用
前三个可用上文提到的组件和小程序swiper组件快速完成,
对于搜索功能
我们在搜索input上绑定一个`inputTyping`事件,这样每次键入完毕都可以得到结果,然后我们直接请求api
```js //index.js //获取应用实例 // 个人网易云音乐 ID 66919655 var app = getApp() Page({ data: { searchReault: [] },    //绑定事件    inputTyping: function (e) { let that = this console.log(e.detail) this.setData({ inputVal: e.detail.value }); wx.request({ url: 'http://neteasemusic.leanapp.cn/search', data: { keywords: e.detail.value }, method: 'GET', success: function (res) { let temp = [] if(!res.data.result.songs){ return ; }                //遍历数据                res.data.result.songs.forEach((song, index) => { temp.push({ id: song.id, name: song.name, mp3Url: song.mp3Url, picUrl: song.album.picUrl, singer: song.artists[0].name })                    //设置数据                   that.setData({ searchReault: temp }) }) // 存入搜索的结果进缓存 wx.setStorage({ key:"searchReault", data:temp }) } }) } }); ``` data里面的searchReault数组存入搜索结果,发起一个wx.request,用GET方式传入参数,组织好json后设置data,然后将搜索结果存入本地缓存
**wxml渲染searchReault:**
![](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/search.jpg)
并且自定义data属性,navigator的打开方式为tab切换`open-type="switchTab"` ,绑定一个tonow事件bindtap="tonow" ```html {{item.name}} {{item.singer}} ``` 在tonow事件中,获取当前的歌曲 ``` tonow: function (event) { let songData = { id: event.currentTarget.dataset.id, name: event.currentTarget.dataset.name, mp3Url: event.currentTarget.dataset.songurl, picUrl: event.currentTarget.dataset.picurl, singer: event.currentTarget.dataset.singer } // 将当前点击的歌曲保存在缓存中 wx.setStorageSync('clickdata', songData) wx.switchTab({ url: '../now/index' }) } ``` - 正在播放
![](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/nowplay.jpg)
**布局:**歌曲封面,滑动条上下为操作按钮, 封面在采用圆角,rotate,transition既可以 **滑动快进:**在滑动条上绑定事件 slider3change ```js //滑动 歌曲快进 function sliderToseek(e, cb) { wx.getBackgroundAudioPlayerState({ success: function (res) { var dataUrl = res.dataUrl var duration = res.duration let val = e.detail.value let cal = val * duration / 100 cb && cb(dataUrl, cal); } }) } //分隔 在page中调用 slider3change: function (e) { sliderToseek(e, function (dataUrl, cal) { wx.playBackgroundAudio({ dataUrl: dataUrl }) wx.seekBackgroundAudio({ position: cal }) }) }, ``` 一个自定义的sliderToseek函数:
参数e 可以获取滑动的值,获取正在播放的音乐信息成功后执行`回调函数1->播放 回调函数2->跳到指定位置`; **拆分歌词:** 在api中得到的歌词:"[00:00.00] 作曲 : 黄家驹 [00:01.00] 作词 : 黄家驹 [00:18.580]今天我 寒夜里看雪飘过 [00:25.050]怀着冷却了的心窝漂远方 [00:30.990]风雨里追赶 " 在page外定义函数:
以`]`划分数组 第二部分就是歌词内容:`item.split(']')[1]` 第一部分即为对应的时间:`item.split(']')[0]` ```js // 获取歌词 function getlyric(id,cb) { console.log('id:',id) let url = `http://neteasemusic.leanapp.cn/lyric` wx.request({ url: url, data: { id: id }, method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT // header: {}, // 设置请求的 header success: function (res) { // success if (!res.data.lrc.lyric) return false; let lyric = res.data.lrc.lyric let timearr = lyric.split('[') let obj = {} let lyricArr=[] // seek 为键 歌词为value timearr.forEach((item) => { let key = parseInt(item.split(']')[0].split(':')[0]) * 60 + parseInt(item.split(']')[0].split(':')[1]) let val = item.split(']')[1] obj[key] = val }) for(let key in obj){ // obj[key] = obj[key].split('\n')[0] lyricArr.push(obj[key]) } cb&&cb(obj,lyricArr) }, fail: function (res) { // fail }, complete: function (res) { // complete } }) } ``` 在page中调用:传入歌曲ID(上文我们已经存入缓存,在缓存中取出即可),和将其设置在data的回调 ```js getlyric(id,function(data, lyricArr){ that.setData({ lyricobj:data, lyricArr:lyricArr }) })  ``` wxml进行渲染: ```html ``` **添加歌曲:**
![](https://github.com/MengZhaoFly/wechatApp-netease_cloudmusic/blob/master/results/add.jpg) 我的可以在本地缓存中添加两个key入对应的信息
like:我的喜欢
recent:最近
选择事件 ```js radioChange: function(e) { console.log('radio发生change事件,携带value值为:', e.detail.value) this.setData({ percent:'100%' }) }, //radio发生change事件,携带value值为: like //radio发生change事件,携带value值为: recent ``` 点击添加按钮,向上呼出选项,将当前播放的歌曲设置到对应的数组即可
**进行当前歌曲的播放:** 页面onshow的时候,获取本地缓存的信息,在success的回调中,设置到data,以供页面解析,而后在获取歌词的函数中也进行一次回调,设置歌词, 播放本地音乐,播放成功之后,在success的回调中,获取正在播放的音乐信息,包括该歌曲的总时长,再进行设置。 ``` onShow: function () { var that = this; console.log('正在播放 is on show') // 获取缓存 wx.getStorage({ key: 'clickdata', success: function (res) { var value = res.data var id = value.id if (value) { // 设置到data that.setData({ id:id, name: value.name, src: value.mp3Url, poster: value.picUrl, author: value.singer }) getlyric(id,function(data, lyricArr){ that.setData({ lyricobj:data, lyricArr:lyricArr }) }) } let url = that.data.src || value.mp3Url; // 播放 wx.playBackgroundAudio({ dataUrl: value.mp3Url, title: value.name, coverImgUrl: value.picUrl, success: function () { wx.hideLoading() console.log('url',url) setTimeout(function(){ wx.getBackgroundAudioPlayerState({ success: function (res) { var tempduration = res.duration console.log('get bg success', tempduration, res) // 设置时长 that.setData({ sumduration: tempduration }) }, complete: function (res) { console.log(' get bg complete:', res) } }) },1000) }, complete:function(){ // 获取正在播放的信息 console.log('play',url) } }) } }) }, ``` 这样我们不知不觉进入多个回调嵌套的问题 ## 代码优化,使用Promise,较为优雅地解决回调 **小程序暂时不支持async await**
在 common.js 中为小程序提供的api上裹上一层Promise,并且通过`module.exports = operation`暴露出去
```js const operation = { getMusicData: function () { return new Promise((resolve, reject) => { wx.getBackgroundAudioPlayerState({ success: function (res) { resolve(res); }, fail: function (err) { reject(err); } }) }) }, // 播放音乐 参数:url title 图片url playMusic: function (url, title, pic) { return new Promise((resolve, reject) => { wx.playBackgroundAudio({ dataUrl: url, title: title, coverImgUrl: pic, success: function () { resolve(true) }, fail: function () { reject(new Error('播放错误')); } }) }) }, asyncGetStorage: function (key) { return new Promise((resolve, reject) => { wx.getStorage({ key: key, success: function (res) { resolve(res.data) }, fail: function (err) { reject(err) } }) }) }, getlyric: function (id) { return new Promise((resolve, reject) => { console.log('id:', id) let url = `http://neteasemusic.leanapp.cn/lyric` wx.request({ url: url, data: { id: id }, method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT // header: {}, // 设置请求的 header success: function (res) { // success if (!res.data.lrc.lyric) return false; let lyric = res.data.lrc.lyric let timearr = lyric.split('[') let obj = {} let lyricArr = [] // seek 为键 歌词为value timearr.forEach((item) => { let key = parseInt(item.split(']')[0].split(':')[0]) * 60 + parseInt(item.split(']')[0].split(':')[1]) let val = item.split(']')[1] obj[key] = val }) for (let key in obj) { // obj[key] = obj[key].split('\n')[0] lyricArr.push(obj[key]) } // cb && cb(obj, lyricArr) resolve(lyricArr) }, fail: function (err) { reject(err) }, complete: function (res) { // complete } }) }) } } module.exports = operation ``` 重写一下**当前歌曲播放事件** ```js onShow: function () { let that = this; Common.asyncGetStorage('clickdata')//本地缓存 .then(data => { // console.log(data) if (!data) return; that.setData({ id: data.id, name: data.name, src: data.mp3Url, poster: data.picUrl, author: data.singer }) return Common.playMusic(data.mp3Url, data.name, data.picUrl); }) .then(status => { if(!status) return; wx.hideLoading(); console.log('id,',that.data.id) return Common.getlyric(that.data.id) }) .then((lyricArr) => { console.log('lyricArr',lyricArr) that.setData({ lyricArr: lyricArr }) return Common.getMusicData() }) .then(data => { let tempduration = data.duration console.log('get bg success', tempduration, data) // 设置时长 that.setData({ sumduration: tempduration }) }) }, ```  这样即可缩减部分代码. *** ## 有帮助可以Star 18届小前端求职中`['html/html5', 'css/css3', 'js/es5/es6', 'node']` 1424254461@qq.com ================================================ FILE: app.js ================================================ //app.js App({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) wx.showLoading({ title: '加载中', mask: true }) }, getUserInfo:function(cb){ var that = this if(this.globalData.userInfo){ typeof cb == "function" && cb(this.globalData.userInfo) }else{ //调用登录接口 wx.login({ success: function () { wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo typeof cb == "function" && cb(that.globalData.userInfo) } }) } }) } }, globalData:{ userInfo:null } }) ================================================ FILE: app.json ================================================ { "pages":[ "pages/find/index", "pages/my/index", "pages/now/index", "pages/account/index", "pages/index/index" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#D43C33", "navigationBarTitleText": "网易云音乐", "navigationBarTextStyle":"white", "backgroundColor": "#FBFCFD" }, "tabBar": { "backgroundColor":"#2A2C2E", "color": "#a7a7a7", "selectedColor": "#ffffff", "list": [{ "iconPath":"./images/find.png", "selectedIconPath":"./images/find1.png", "pagePath":"pages/find/index", "text": "发现音乐" }, { "iconPath":"./images/my.png", "selectedIconPath":"./images/my1.png", "pagePath": "pages/my/index", "text": "我的音乐" }, { "iconPath":"./images/now.png", "selectedIconPath":"./images/now1.png", "pagePath": "pages/now/index", "text": "正在播放" }, { "iconPath":"./images/account.png", "selectedIconPath":"./images/account1.png", "pagePath": "pages/account/index", "text": "账号" }] } } ================================================ FILE: app.wxss ================================================ /**app.wxss**/ @import 'style/weui.wxss'; .container { height: 100%; width: 100%; box-sizing: border-box; font-size: 16px; font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; } .searchbar-result{ position: fixed; left: 0; top: 26px; width: 100%; } .weui-navbar__item.weui-bar__item_on { color:#D43C33; } .weui-navbar__slider { background-color:#D43C33; } swiper image{ width: 100%; } .three-circle .weui-grids navigator{ background-color: #fff; border-right:none; } .weui-grid__label { font-size:12px; } .weui-grid__icon { width:40px; height:40px; margin:0 auto; } .header{ position: relative; background-color: #fff; } .header text{ font-size:20px; font-weight:400; padding: 20px 0; } .header text:first-child{ padding-left: 15px; } .header text:first-child::before{ content: ''; display: inline-block; width: 2px; height: 20px; vertical-align: middle; background-color: #D43C33; position: absolute; left: 0; top: 6px; } .header text:last-child{ padding-left: 10px; } .lists-content{ display: flex; justify-content: space-between; flex-wrap: wrap; } .lists-content navigator{ width: calc((100% - 8px) / 3); margin-top: 12px; } .lists-content .image{ width: 100%; height: 130px; margin: 0 auto; } .image{ background-size: cover; } .lists-content navigator view:last-child{ font-size: 14px; width: 100%; height: 44px; margin-top: 5px; display:block;white-space: normal; overflow: hidden; text-overflow:ellipsis; } .weui-cells::after { border-bottom:none; } .weui-cell::before { left:60px; } .weui-cell__bd{ padding-left: 15px; } .listen-content .weui-cell::before { left:80px; } .searchbar-result .weui-cell::before { left: 0; } .searchbar-result .weui-cells{ overflow-y: scroll; } .weui-search-bar__cancel-btn{ color: #D43C33; } ================================================ FILE: common.js ================================================ const operation = { getMusicData: function () { return new Promise((resolve, reject) => { wx.getBackgroundAudioPlayerState({ success: function (res) { resolve(res); }, fail: function (err) { reject(err); } }) }) }, // 播放音乐 参数:url title 图片url playMusic: function (url, title, pic) { return new Promise((resolve, reject) => { wx.playBackgroundAudio({ dataUrl: url, title: title, coverImgUrl: pic, success: function () { resolve(true) }, fail: function () { reject(new Error('播放错误')); } }) }) }, asyncGetStorage: function (key) { return new Promise((resolve, reject) => { wx.getStorage({ key: key, success: function (res) { resolve(res.data) }, fail: function (err) { reject(err) } }) }) }, getlyric: function (id) { return new Promise((resolve, reject) => { console.log('id:', id) let url = `http://neteasemusic.leanapp.cn/lyric` wx.request({ url: url, data: { id: id }, method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT // header: {}, // 设置请求的 header success: function (res) { // success if (!res.data.lrc.lyric) return false; let lyric = res.data.lrc.lyric let timearr = lyric.split('[') let obj = {} let lyricArr = [] // seek 为键 歌词为value timearr.forEach((item) => { let key = parseInt(item.split(']')[0].split(':')[0]) * 60 + parseInt(item.split(']')[0].split(':')[1]) let val = item.split(']')[1] obj[key] = val }) for (let key in obj) { // obj[key] = obj[key].split('\n')[0] lyricArr.push(obj[key]) } // cb && cb(obj, lyricArr) resolve(lyricArr) }, fail: function (err) { reject(err) }, complete: function (res) { // complete } }) }) } } module.exports = operation ================================================ FILE: pages/account/index.js ================================================ //index.js //获取应用实例 var app = getApp() Page({ data: { motto: 'Hello World', userInfo: {} }, //事件处理函数 bindViewTap: function() { wx.navigateTo({ url: '../logs/logs' }) }, onLoad: function () { console.log('onLoad') var that = this //调用应用实例的方法获取全局数据 app.getUserInfo(function(userInfo){ //更新数据 that.setData({ userInfo:userInfo }) }), wx.setNavigationBarTitle({ title: '个人中心' }) } }) ================================================ FILE: pages/account/index.json ================================================ {} ================================================ FILE: pages/account/index.wxml ================================================ {{userInfo.nickName}} ================================================ FILE: pages/account/index.wxss ================================================ /**index.wxss**/ .userinfo { display: flex; flex-direction: column; align-items: center; } .userinfo-avatar { width: 128rpx; height: 128rpx; margin: 20rpx; border-radius: 50%; } .userinfo-nickname { color: #aaa; } .usermotto { margin-top: 200px; } .flex-item{ width: 100rpx; height: 100rpx; background-color: red; } ================================================ FILE: pages/account/logs.js ================================================ // pages/account/logs.js Page({ data:{}, onLoad:function(options){ // 页面初始化 options为页面跳转所带来的参数 }, onReady:function(){ // 页面渲染完成 }, onShow:function(){ // 页面显示 }, onHide:function(){ // 页面隐藏 }, onUnload:function(){ // 页面关闭 } }) ================================================ FILE: pages/account/logs.json ================================================ {} ================================================ FILE: pages/account/logs.wxml ================================================ pages/account/logs.wxml ================================================ FILE: pages/account/logs.wxss ================================================ /* pages/account/logs.wxss */ ================================================ FILE: pages/find/index.js ================================================ //index.js //获取应用实例 // 个人网易云音乐 ID 66919655 var app = getApp() Page({ data: { inputShowed: false, inputVal: "", tabs: ["个性推荐", "歌单", "主播电台", "排行榜"], activeIndex: 0, sliderOffset: 0, sliderLeft: 0, imgUrls: [ 'http://img02.tooopen.com/images/20150928/tooopen_sy_143912755726.jpg', 'http://img06.tooopen.com/images/20160818/tooopen_sy_175866434296.jpg', 'http://img06.tooopen.com/images/20160818/tooopen_sy_175833047715.jpg' ], indicatorDots: true, autoplay: true, interval: 2000, duration: 1000, circular: true, // 歌曲搜索的结果 searchReault: [] }, showInput: function () { this.setData({ inputShowed: true }); }, hideInput: function () { this.setData({ inputVal: "", inputShowed: false }); }, clearInput: function () { this.setData({ inputVal: "" }); }, inputTyping: function (e) { let that = this console.log(e.detail) this.setData({ inputVal: e.detail.value }); // let url = `http://localhost:3000/search?keywords=${e.detail.value}` wx.request({ url: 'http://neteasemusic.leanapp.cn/search', data: { keywords: e.detail.value }, method: 'GET', success: function (res) { let temp = [] if(!res.data.result.songs){ return ; } res.data.result.songs.forEach((song, index) => { temp.push({ id: song.id, name: song.name, mp3Url: song.mp3Url, picUrl: song.album.picUrl, singer: song.artists[0].name }) that.setData({ searchReault: temp }) }) // 存入搜索的结果进缓存 wx.setStorage({ key:"searchReault", data:temp }) }, fail: function (res) { // fail }, complete: function (res) { // complete } }) }, onShow: function(){ wx.hideLoading() }, onLoad: function () { var that = this; wx.getSystemInfo({ success: function (res) { that.setData({ sliderOffset: res.windowWidth / that.data.tabs.length * that.data.activeIndex }); } }); }, tabClick: function (e) { this.setData({ sliderOffset: e.currentTarget.offsetLeft, activeIndex: e.currentTarget.id }); }, tonow: function (event) { let songData = { id: event.currentTarget.dataset.id, name: event.currentTarget.dataset.name, mp3Url: event.currentTarget.dataset.songurl, picUrl: event.currentTarget.dataset.picurl, singer: event.currentTarget.dataset.singer } // 将当前点击的歌曲保存在缓存中 wx.setStorageSync('clickdata', songData) wx.switchTab({ url: '../now/index' }) } }); ================================================ FILE: pages/find/index.json ================================================ {} ================================================ FILE: pages/find/index.wxml ================================================ {{item.name}} {{item.singer}} ================================================ FILE: pages/find/index.wxss ================================================ /**index.wxss**/ .userinfo { display: flex; flex-direction: column; align-items: center; } .userinfo-avatar { width: 128rpx; height: 128rpx; margin: 20rpx; border-radius: 50%; } .userinfo-nickname { color: #aaa; } .usermotto { margin-top: 200px; } .song-name{ color: #507daf; font-size: 18px; } .song-singer{ color: #888888; font-size: 12px; } ================================================ FILE: pages/index/index.js ================================================ //index.js //获取应用实例 var app = getApp() Page({ data: { motto: 'Hello World', userInfo: {} }, //事件处理函数 bindViewTap: function() { wx.navigateTo({ url: '../logs/logs' }) }, onLoad: function () { console.log('onLoad') var that = this //调用应用实例的方法获取全局数据 app.getUserInfo(function(userInfo){ //更新数据 that.setData({ userInfo:userInfo }) }) wx.playBackgroundAudio({ dataUrl: 'http://m2.music.126.net/Yev_vdcos-1_SX2qC9WUfw==/7856010580878729.mp3' }) } }) ================================================ FILE: pages/index/index.json ================================================ {} ================================================ FILE: pages/index/index.wxml ================================================ ================================================ FILE: pages/index/index.wxss ================================================ /**app.wxss**/ .container { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 200rpx 0; box-sizing: border-box; } /**index.wxss**/ .userinfo { display: flex; flex-direction: column; align-items: center; } .userinfo-avatar { width: 128rpx; height: 128rpx; margin: 20rpx; border-radius: 50%; } .userinfo-nickname { color: #aaa; } .usermotto { margin-top: 200px; } .flex-item{ width: 100rpx; height: 100rpx; background-color: red; } ================================================ FILE: pages/logs/logs.js ================================================ //logs.js var util = require('../../utils/util.js') Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync('logs') || []).map(function (log) { return util.formatTime(new Date(log)) }) }) } }) ================================================ FILE: pages/logs/logs.json ================================================ { "navigationBarTitleText": "查看启动日志" } ================================================ FILE: pages/logs/logs.wxml ================================================ {{index + 1}}. {{log}} ================================================ FILE: pages/logs/logs.wxss ================================================ .log-list { display: flex; flex-direction: column; padding: 40rpx; } .log-item { margin: 10rpx; } ================================================ FILE: pages/my/index.js ================================================ //index.js //获取应用实例 var app = getApp() Page({ data: { motto: 'Hello World', userInfo: {} }, //事件处理函数 bindViewTap: function() { wx.navigateTo({ url: '../logs/logs' }) }, redirect:function(){ console.log('点击'); wx.navigateTo({ url: '../index/index', success: function(res){ console.log(res) }, fail: function(res) { // fail console.log(res) }, complete: function(res) { // complete console.log(res) } }) }, onLoad: function () { console.log('onLoad') var that = this //调用应用实例的方法获取全局数据 app.getUserInfo(function(userInfo){ //更新数据 that.setData({ userInfo:userInfo }) }), wx.setNavigationBarTitle({ title: '我的音乐' }) } }) ================================================ FILE: pages/my/index.json ================================================ { "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#D43C33", "navigationBarTitleText": "我的音乐", "navigationBarTextStyle":"white", "backgroundColor": "#FBFCFD" } } ================================================ FILE: pages/my/index.wxml ================================================ 本地播放 0 最近播放 100 我的电台 0 我的收藏 1 我的歌单 本地播放 41首 最近 13首 ================================================ FILE: pages/my/index.wxss ================================================ /**index.wxss**/ .header{ color: #888; } .listen-content .item{ display: flex; padding: 10px; align-items: center; } .bg-img{ width: 30px; height:30px; background-size: cover; background-repeat: no-repeat; } .count{ color: #888888; } .listen-content .bg-img{ width: 50px; height: 50px; } .header{ padding-left: 15px; background-color: #eeeff0; } .listen-content .weui-cell:first-child{ } .weui-cells::before { border-top:none; } ================================================ FILE: pages/now/index.js ================================================ //index.js //获取应用实例 var Common = require('../../common') var app = getApp() Page({ data: { id: 436514312, name: "成都", src: "http://m2.music.126.net/7o5D4dA6271VktgawcbZFA==/18665309393829604.mp3", poster: "http://p1.music.126.net/34YW1QtKxJ_3YnX9ZzKhzw==/2946691234868155.jpg", author: "赵雷", isplaying: true, islyric: false, sumduration: 0, lyricobj:{}, lyricArr:[], isadd:false, items: [ {name: 'recent', value: '最近'}, {name: 'like', value: '我的收藏'} ], percent:'100%' }, addsong:function(){ this.setData({ percent:'0' }) }, radioChange: function(e) { console.log('radio发生change事件,携带value值为:', e.detail.value) this.setData({ percent:'100%' }) }, //事件处理函数 bindViewTap: function () { wx.navigateTo({ url: '../logs/logs' }) }, showCircle:function(){ this.setData({ islyric: true, percent:'100%' }) }, showlyric:function(){ this.setData({ islyric: false, percent:'100%' }) }, onLoad: function () { wx.showLoading({ title: '加载中', mask: true }) console.log('正在播放 onLoad') var that = this //调用应用实例的方法获取全局数据 app.getUserInfo(function (userInfo) { //更新数据 that.setData({ userInfo: userInfo }) }), wx.setNavigationBarTitle({ title: '正在播放' }) }, onShow: function () { let that = this; Common.asyncGetStorage('clickdata')//本地缓存 .then(data => { // console.log(data) if (!data) return; that.setData({ id: data.id, name: data.name, src: data.mp3Url, poster: data.picUrl, author: data.singer }) return Common.playMusic(data.mp3Url, data.name, data.picUrl); }) .then(status => { if(!status) return; wx.hideLoading(); console.log('id,',that.data.id) return Common.getlyric(that.data.id) }) .then((lyricArr) => { console.log('lyricArr',lyricArr) that.setData({ lyricArr: lyricArr }) return Common.getMusicData() }) .then(data => { let tempduration = data.duration console.log('get bg success', tempduration, data) // 设置时长 that.setData({ sumduration: tempduration }) }) }, audioPlay: function () { //背景音乐信息 wx.getBackgroundAudioPlayerState({ success: function (res) { var status = res.status var dataUrl = res.dataUrl var currentPosition = res.currentPosition var duration = res.duration var downloadPercent = res.downloadPercent wx.playBackgroundAudio({ dataUrl: dataUrl }) wx.seekBackgroundAudio({ position: currentPosition }) } }) this.setData({ isplaying: true }) }, audioPause: function () { wx.pauseBackgroundAudio() this.setData({ isplaying: false }) }, audio14: function () { }, audioStart: function () { }, slider3change: function (e) { sliderToseek(e, function (dataUrl, cal) { wx.playBackgroundAudio({ dataUrl: dataUrl }) wx.seekBackgroundAudio({ position: cal }) }) }, prev:function(){ prevSong(this) } }) // 上一曲 function prevSong(that){ let id = that.data.id console.log('id',id) wx.getStorage({ key: 'searchReault', success: function(res) { console.log(res.data) let currentSongIndex = res.data.findIndex((item)=>{ return item.id == id; }) console.log(currentSongIndex) currentSongIndex -- ; console.log(res.data[currentSongIndex]) wx.playBackgroundAudio({ dataUrl: res.data[currentSongIndex].mp3Url }) wx.switchTab({ url: '../now/index' }) } }) } //滑动 歌曲快进 function sliderToseek(e, cb) { wx.getBackgroundAudioPlayerState({ success: function (res) { var dataUrl = res.dataUrl var duration = res.duration let val = e.detail.value let cal = val * duration / 100 cb && cb(dataUrl, cal); } }) } // 获取歌词 function getlyric(id,cb) { console.log('id:',id) let url = `http://neteasemusic.leanapp.cn/lyric` wx.request({ url: url, data: { id: id }, method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT // header: {}, // 设置请求的 header success: function (res) { // success if (!res.data.lrc.lyric) return false; let lyric = res.data.lrc.lyric let timearr = lyric.split('[') let obj = {} let lyricArr=[] // seek 为键 歌词为value timearr.forEach((item) => { let key = parseInt(item.split(']')[0].split(':')[0]) * 60 + parseInt(item.split(']')[0].split(':')[1]) let val = item.split(']')[1] obj[key] = val }) for(let key in obj){ // obj[key] = obj[key].split('\n')[0] lyricArr.push(obj[key]) } cb&&cb(obj,lyricArr) }, fail: function (res) { // fail }, complete: function (res) { // complete } }) } // ---------------------------------------------------- ================================================ FILE: pages/now/index.json ================================================ { } ================================================ FILE: pages/now/index.wxml ================================================ ================================================ FILE: pages/now/index.wxss ================================================ /**index.wxss**/ page{ width: 100%; height: 100%; } .container { position: relative; overflow: hidden; } .container-content{ position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 1; height: 100%; display: flex; flex-direction: column; justify-content: center; } .userinfo { display: flex; flex-direction: column; align-items: center; } .userinfo-avatar { width: 128rpx; height: 128rpx; margin: 20rpx; border-radius: 50%; } .userinfo-nickname { color: #aaa; } .usermotto { margin-top: 200px; } .cicle-infor{ width: 230px; height: 230px; background-size: cover; background-repeat: no-repeat; background-position: center; border-radius: 50%; animation: rotate 10s linear infinite forwards; margin: 0 auto; } @keyframes rotate{ 0%{ transform: rotate(0deg) }100%{ transform: rotate(360deg) } } .fengmian{ flex: 1; display: flex; align-items: center; } .other-content{ height: 170px; } .container{ text-align: center; } .icon-choose{ } .icon-choose, .operations{ display: flex; justify-content: center; align-items: center; } .icon{ width: 30px; height: 30px; background-size: cover; margin: 10px; } .like{ background-image:url(http://op4j4esv2.bkt.clouddn.com/like.png) ; } .download{ background-image:url(http://op4j4esv2.bkt.clouddn.com/download.png) ; } .add{ background-image:url(http://op4j4esv2.bkt.clouddn.com/add.png) ; } .btn{ width: 40px; height: 40px; background-size: cover; margin: 10px; } .prev{ background-image:url(http://op4j4esv2.bkt.clouddn.com/pre.png) ; } .play, .pause{ background-image:url(http://op4j4esv2.bkt.clouddn.com/play.png) ; width: 50px; height: 50px; } .pause{ background-image:url(http://op4j4esv2.bkt.clouddn.com/pause.png) ; } .next{ background-image:url(http://op4j4esv2.bkt.clouddn.com/next.png) ; } .active{ color: #fff; } .lists{ position: absolute; bottom: 0; opacity: .8; /*transform: translateY(170px);*/ z-index: 3; background: linear-gradient(to top, transparent 0%, #D43C33 100%); width: 100%; transition: all .5s; } .lists radio-group{ display: flex; flex-direction: column; align-items: flex-start; height: 170px; overflow-y: scroll; } .lists radio-group label{ padding: 5px 0 5px 20px; } ================================================ FILE: style/weui.wxss ================================================ page { line-height: 1.6; font-family: -apple-system-font, "Helvetica Neue", sans-serif; } icon { vertical-align: middle; } .weui-cells { position: relative; margin-top: 1.17647059em; background-color: #FFFFFF; line-height: 1.41176471; font-size: 17px; } .weui-cells:before { content: " "; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1rpx solid #D9D9D9; color: #D9D9D9; } .weui-cells:after { content: " "; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; border-bottom: 1rpx solid #D9D9D9; color: #D9D9D9; } .weui-cells__title { margin-top: .77em; margin-bottom: .3em; padding-left: 15px; padding-right: 15px; color: #999999; font-size: 14px; } .weui-cells_after-title { margin-top: 0; } .weui-cells__tips { margin-top: .3em; color: #999999; padding-left: 15px; padding-right: 15px; font-size: 14px; } .weui-cell { padding: 10px 15px; position: relative; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; } .weui-cell:before { content: " "; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1rpx solid #D9D9D9; color: #D9D9D9; left: 15px; } .weui-cell:first-child:before { display: none; } .weui-cell_active { background-color: #ECECEC; } .weui-cell_primary { -webkit-box-align: start; -webkit-align-items: flex-start; align-items: flex-start; } .weui-cell__bd { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; } .weui-cell__ft { text-align: right; color: #999999; } .weui-cell_access { color: inherit; } .weui-cell__ft_in-access { padding-right: 13px; position: relative; } .weui-cell__ft_in-access:after { content: " "; display: inline-block; height: 6px; width: 6px; border-width: 2px 2px 0 0; border-color: #C8C8CD; border-style: solid; -webkit-transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0); transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0); position: relative; top: -2px; position: absolute; top: 50%; margin-top: -4px; right: 2px; } .weui-cell_link { color: #586C94; font-size: 14px; } .weui-cell_link:active { background-color: #ECECEC; } .weui-cell_link:first-child:before { display: block; } .weui-icon-radio { margin-left: 3.2px; margin-right: 3.2px; } .weui-icon-checkbox_circle, .weui-icon-checkbox_success { margin-left: 4.6px; margin-right: 4.6px; } .weui-check__label:active { background-color: #ECECEC; } .weui-check { position: absolute; left: -9999px; } .weui-check__hd_in-checkbox { padding-right: 0.35em; } .weui-cell__ft_in-radio { padding-left: 0.35em; } .weui-cell_input { padding-top: 0; padding-bottom: 0; } .weui-label { width: 105px; word-wrap: break-word; word-break: break-all; } .weui-input { height: 2.58823529em; min-height: 2.58823529em; line-height: 2.58823529em; } .weui-toptips { position: fixed; -webkit-transform: translateZ(0); transform: translateZ(0); top: 0; left: 0; right: 0; padding: 5px; font-size: 14px; text-align: center; color: #FFFFFF; z-index: 5000; word-wrap: break-word; word-break: break-all; } .weui-toptips_warn { background-color: #E64340; } .weui-textarea { display: block; width: 100%; } .weui-textarea-counter { color: #B2B2B2; text-align: right; } .weui-textarea-counter_warn { color: #E64340; } .weui-cell_warn { color: #E64340; } .weui-form-preview { position: relative; background-color: #FFFFFF; } .weui-form-preview:before { content: " "; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1rpx solid #D9D9D9; color: #D9D9D9; } .weui-form-preview:after { content: " "; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; border-bottom: 1rpx solid #D9D9D9; color: #D9D9D9; } .weui-form-preview__value { font-size: 14px; } .weui-form-preview__value_in-hd { font-size: 26px; } .weui-form-preview__hd { position: relative; padding: 10px 15px; text-align: right; line-height: 2.5em; } .weui-form-preview__hd:after { content: " "; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; border-bottom: 1rpx solid #D9D9D9; color: #D9D9D9; left: 15px; } .weui-form-preview__bd { padding: 10px 15px; font-size: .9em; text-align: right; color: #999999; line-height: 2; } .weui-form-preview__ft { position: relative; line-height: 50px; display: -webkit-box; display: -webkit-flex; display: flex; } .weui-form-preview__ft:after { content: " "; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1rpx solid #D5D5D6; color: #D5D5D6; } .weui-form-preview__item { overflow: hidden; } .weui-form-preview__label { float: left; margin-right: 1em; min-width: 4em; color: #999999; text-align: justify; text-align-last: justify; } .weui-form-preview__value { display: block; overflow: hidden; word-break: normal; word-wrap: break-word; } .weui-form-preview__btn { position: relative; display: block; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; color: #3CC51F; text-align: center; } .weui-form-preview__btn:after { content: " "; position: absolute; left: 0; top: 0; width: 1px; bottom: 0; border-left: 1rpx solid #D5D5D6; color: #D5D5D6; } .weui-form-preview__btn:first-child:after { display: none; } .weui-form-preview__btn_active { background-color: #EEEEEE; } .weui-form-preview__btn_default { color: #999999; } .weui-form-preview__btn_primary { color: #0BB20C; } .weui-cell_select { padding: 0; } .weui-select { position: relative; padding-left: 15px; padding-right: 30px; height: 2.58823529em; min-height: 2.58823529em; line-height: 2.58823529em; border-right: 1rpx solid #D9D9D9; } .weui-select:before { content: " "; display: inline-block; height: 6px; width: 6px; border-width: 2px 2px 0 0; border-color: #C8C8CD; border-style: solid; -webkit-transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0); transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0); position: relative; top: -2px; position: absolute; top: 50%; right: 15px; margin-top: -4px; } .weui-select_in-select-after { padding-left: 0; } .weui-cell__hd_in-select-after, .weui-cell__bd_in-select-before { padding-left: 15px; } .weui-cell_vcode { padding-right: 0; } .weui-vcode-img { margin-left: 5px; height: 2.58823529em; vertical-align: middle; } .weui-vcode-btn { display: inline-block; height: 2.58823529em; margin-left: 5px; padding: 0 0.6em 0 0.7em; border-left: 1px solid #E5E5E5; line-height: 2.58823529em; vertical-align: middle; font-size: 17px; color: #3CC51F; white-space: nowrap; } .weui-vcode-btn:active { color: #52a341; } .weui-cell_switch { padding-top: 6px; padding-bottom: 6px; } .weui-uploader__hd { display: -webkit-box; display: -webkit-flex; display: flex; padding-bottom: 10px; -webkit-box-align: center; -webkit-align-items: center; align-items: center; } .weui-uploader__title { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; } .weui-uploader__info { color: #B2B2B2; } .weui-uploader__bd { margin-bottom: -4px; margin-right: -9px; overflow: hidden; } .weui-uploader__file { float: left; margin-right: 9px; margin-bottom: 9px; } .weui-uploader__img { display: block; width: 79px; height: 79px; } .weui-uploader__file_status { position: relative; } .weui-uploader__file_status:before { content: " "; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0, 0, 0, 0.5); } .weui-uploader__file-content { position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); color: #FFFFFF; } .weui-uploader__input-box { float: left; position: relative; margin-right: 9px; margin-bottom: 9px; width: 77px; height: 77px; border: 1px solid #D9D9D9; } .weui-uploader__input-box:before, .weui-uploader__input-box:after { content: " "; position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); background-color: #D9D9D9; } .weui-uploader__input-box:before { width: 2px; height: 39.5px; } .weui-uploader__input-box:after { width: 39.5px; height: 2px; } .weui-uploader__input-box:active { border-color: #999999; } .weui-uploader__input-box:active:before, .weui-uploader__input-box:active:after { background-color: #999999; } .weui-uploader__input { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; } .weui-article { padding: 20px 15px; font-size: 15px; } .weui-article__section { margin-bottom: 1.5em; } .weui-article__h1 { font-size: 18px; font-weight: 400; margin-bottom: .9em; } .weui-article__h2 { font-size: 16px; font-weight: 400; margin-bottom: .34em; } .weui-article__h3 { font-weight: 400; font-size: 15px; margin-bottom: .34em; } .weui-article__p { margin: 0 0 .8em; } .weui-msg { padding-top: 36px; text-align: center; } .weui-msg__link { display: inline; color: #586C94; } .weui-msg__icon-area { margin-bottom: 30px; } .weui-msg__text-area { margin-bottom: 25px; padding: 0 20px; } .weui-msg__title { margin-bottom: 5px; font-weight: 400; font-size: 20px; } .weui-msg__desc { font-size: 14px; color: #999999; } .weui-msg__opr-area { margin-bottom: 25px; } .weui-msg__extra-area { margin-bottom: 15px; font-size: 14px; color: #999999; } @media screen and (min-height: 438px) { .weui-msg__extra-area { position: fixed; left: 0; bottom: 0; width: 100%; text-align: center; } } .weui-flex { display: -webkit-box; display: -webkit-flex; display: flex; } .weui-flex__item { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; } .weui-btn { margin-top: 15px; } .weui-btn:first-child { margin-top: 0; } .weui-btn-area { margin: 1.17647059em 15px 0.3em; } .weui-agree { display: block; padding: .5em 15px; font-size: 13px; } .weui-agree__text { color: #999999; } .weui-agree__link { display: inline; color: #586C94; } .weui-agree__checkbox { position: absolute; left: -9999px; } .weui-agree__checkbox-icon { position: relative; top: 2px; display: inline-block; border: 1px solid #D1D1D1; background-color: #FFFFFF; border-radius: 3px; width: 11px; height: 11px; } .weui-agree__checkbox-icon-check { position: absolute; top: 1px; left: 1px; } .weui-footer { color: #999999; font-size: 14px; text-align: center; } .weui-footer_fixed-bottom { position: fixed; bottom: .52em; left: 0; right: 0; } .weui-footer__links { font-size: 0; } .weui-footer__link { display: inline-block; vertical-align: top; margin: 0 .62em; position: relative; font-size: 14px; color: #586C94; } .weui-footer__link:before { content: " "; position: absolute; left: 0; top: 0; width: 1px; bottom: 0; border-left: 1rpx solid #C7C7C7; color: #C7C7C7; left: -0.65em; top: .36em; bottom: .36em; } .weui-footer__link:first-child:before { display: none; } .weui-footer__text { padding: 0 .34em; font-size: 12px; } .weui-grids { border-top: 1rpx solid #D9D9D9; border-left: 1rpx solid #D9D9D9; overflow: hidden; } .weui-grid { position: relative; float: left; padding: 20px 10px; width: 33.33333333%; box-sizing: border-box; border-right: 1rpx solid #D9D9D9; border-bottom: 1rpx solid #D9D9D9; } .weui-grid_active { background-color: #ECECEC; } .weui-grid__icon { display: block; width: 28px; height: 28px; margin: 0 auto; } .weui-grid__label { margin-top: 5px; display: block; text-align: center; color: #000000; font-size: 14px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } .weui-loading { margin: 0 5px; width: 20px; height: 20px; display: inline-block; vertical-align: middle; -webkit-animation: weuiLoading 1s steps(12, end) infinite; animation: weuiLoading 1s steps(12, end) infinite; background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat; background-size: 100%; } .weui-loading.weui-loading_transparent { background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 100 100'%3E%3Cpath fill='none' d='M0 0h100v100H0z'/%3E%3Crect xmlns='http://www.w3.org/2000/svg' width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.56)' rx='5' ry='5' transform='translate(0 -30)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.5)' rx='5' ry='5' transform='rotate(30 105.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.43)' rx='5' ry='5' transform='rotate(60 75.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.38)' rx='5' ry='5' transform='rotate(90 65 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.32)' rx='5' ry='5' transform='rotate(120 58.66 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.28)' rx='5' ry='5' transform='rotate(150 54.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.25)' rx='5' ry='5' transform='rotate(180 50 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.2)' rx='5' ry='5' transform='rotate(-150 45.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.17)' rx='5' ry='5' transform='rotate(-120 41.34 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.14)' rx='5' ry='5' transform='rotate(-90 35 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.1)' rx='5' ry='5' transform='rotate(-60 24.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.03)' rx='5' ry='5' transform='rotate(-30 -5.98 65)'/%3E%3C/svg%3E"); } @-webkit-keyframes weuiLoading { 0% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 360deg); transform: rotate3d(0, 0, 1, 360deg); } } @keyframes weuiLoading { 0% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 360deg); transform: rotate3d(0, 0, 1, 360deg); } } .weui-badge { display: inline-block; padding: .15em .4em; min-width: 8px; border-radius: 18px; background-color: #E64340; color: #FFFFFF; line-height: 1.2; text-align: center; font-size: 12px; vertical-align: middle; } .weui-badge_dot { padding: .4em; min-width: 0; } .weui-loadmore { width: 65%; margin: 1.5em auto; line-height: 1.6em; font-size: 14px; text-align: center; } .weui-loadmore__tips { display: inline-block; vertical-align: middle; } .weui-loadmore_line { border-top: 1px solid #E5E5E5; margin-top: 2.4em; } .weui-loadmore__tips_in-line { position: relative; top: -0.9em; padding: 0 .55em; background-color: #FFFFFF; color: #999999; } .weui-loadmore__tips_in-dot { position: relative; padding: 0 .16em; width: 4px; height: 1.6em; } .weui-loadmore__tips_in-dot:before { content: " "; position: absolute; top: 50%; left: 50%; margin-top: -1px; margin-left: -2px; width: 4px; height: 4px; border-radius: 50%; background-color: #E5E5E5; } .weui-panel { background-color: #FFFFFF; margin-top: 10px; position: relative; overflow: hidden; } .weui-panel:first-child { margin-top: 0; } .weui-panel:before { content: " "; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1rpx solid #E5E5E5; color: #E5E5E5; } .weui-panel:after { content: " "; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; border-bottom: 1rpx solid #E5E5E5; color: #E5E5E5; } .weui-panel__hd { padding: 14px 15px 10px; color: #999999; font-size: 13px; position: relative; } .weui-panel__hd:after { content: " "; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; border-bottom: 1rpx solid #E5E5E5; color: #E5E5E5; left: 15px; } .weui-media-box { padding: 15px; position: relative; } .weui-media-box:before { content: " "; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1rpx solid #E5E5E5; color: #E5E5E5; left: 15px; } .weui-media-box:first-child:before { display: none; } .weui-media-box__title { font-weight: 400; font-size: 17px; width: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; word-wrap: normal; word-wrap: break-word; word-break: break-all; } .weui-media-box__desc { color: #999999; font-size: 13px; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .weui-media-box__info { margin-top: 15px; padding-bottom: 5px; font-size: 13px; color: #CECECE; line-height: 1em; list-style: none; overflow: hidden; } .weui-media-box__info__meta { float: left; padding-right: 1em; } .weui-media-box__info__meta_extra { padding-left: 1em; border-left: 1px solid #CECECE; } .weui-media-box__title_in-text { margin-bottom: 8px; } .weui-media-box_appmsg { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; } .weui-media-box__thumb { width: 100%; height: 100%; vertical-align: top; } .weui-media-box__hd_in-appmsg { margin-right: .8em; width: 60px; height: 60px; line-height: 60px; text-align: center; } .weui-media-box__bd_in-appmsg { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; min-width: 0; } .weui-media-box_small-appmsg { padding: 0; } .weui-cells_in-small-appmsg { margin-top: 0; } .weui-cells_in-small-appmsg:before { display: none; } .weui-progress { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; } .weui-progress__bar { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; } .weui-progress__opr { margin-left: 15px; font-size: 0; } .weui-navbar { display: -webkit-box; display: -webkit-flex; display: flex; position: absolute; z-index: 500; top: 0; width: 100%; border-bottom: 1rpx solid #CCCCCC; } .weui-navbar__item { position: relative; display: block; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; padding: 13px 0; text-align: center; font-size: 0; } .weui-navbar__item.weui-bar__item_on { color: #1AAD19; } .weui-navbar__slider { position: absolute; content: " "; left: 0; bottom: 0; width: 6em; height: 3px; background-color: #1AAD19; -webkit-transition: -webkit-transform .3s; transition: -webkit-transform .3s; transition: transform .3s; transition: transform .3s, -webkit-transform .3s; } .weui-navbar__title { display: inline-block; font-size: 15px; max-width: 8em; width: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; word-wrap: normal; } .weui-tab { position: relative; height: 100%; } .weui-tab__panel { box-sizing: border-box; height: 100%; padding-top: 50px; overflow: auto; -webkit-overflow-scrolling: touch; } .weui-search-bar { position: relative; padding: 8px 10px; display: -webkit-box; display: -webkit-flex; display: flex; box-sizing: border-box; background-color: #EFEFF4; border-top: 1rpx solid #D7D6DC; border-bottom: 1rpx solid #D7D6DC; } .weui-icon-search { margin-right: 8px; font-size: inherit; } .weui-icon-search_in-box { position: absolute; left: 10px; top: 7px; } .weui-search-bar__text { display: inline-block; font-size: 14px; vertical-align: middle; } .weui-search-bar__form { position: relative; -webkit-box-flex: 1; -webkit-flex: auto; flex: auto; border-radius: 5px; background: #FFFFFF; border: 1rpx solid #E6E6EA; } .weui-search-bar__box { position: relative; padding-left: 30px; padding-right: 30px; width: 100%; box-sizing: border-box; z-index: 1; } .weui-search-bar__input { height: 28px; line-height: 28px; font-size: 14px; } .weui-icon-clear { position: absolute; top: 0; right: 0; padding: 7px 8px; font-size: 0; } .weui-search-bar__label { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 2; border-radius: 3px; text-align: center; color: #9B9B9B; background: #FFFFFF; line-height: 28px; } .weui-search-bar__cancel-btn { margin-left: 10px; line-height: 28px; color: #09BB07; white-space: nowrap; } ================================================ FILE: utils/util.js ================================================ function formatTime(date) { var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var hour = date.getHours() var minute = date.getMinutes() var second = date.getSeconds() return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') } function formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n } module.exports = { formatTime: formatTime }