oahcoay 4 years ago
parent
commit
5f512fcf5b
96 changed files with 2053 additions and 4632 deletions
  1. 2 1
      config/dev.env.js
  2. 2 1
      config/prod.env.js
  3. 1 1
      index.html
  4. 0 5
      package-lock.json
  5. 1 2
      package.json
  6. 2 3
      project.config.json
  7. 0 16
      server/.eslintrc.json
  8. 0 130
      server/README.md
  9. 0 19
      server/app.js
  10. 0 56
      server/config.js
  11. 0 67
      server/controllers/addbook.js
  12. 0 19
      server/controllers/addcomment.js
  13. 0 26
      server/controllers/bookdetail.js
  14. 0 28
      server/controllers/booklist.js
  15. 0 26
      server/controllers/commentlist.js
  16. 0 5
      server/controllers/demo.js
  17. 0 30
      server/controllers/index.js
  18. 0 10
      server/controllers/login.js
  19. 0 29
      server/controllers/message.js
  20. 0 11
      server/controllers/top.js
  21. 0 9
      server/controllers/upload.js
  22. 0 11
      server/controllers/user.js
  23. 0 31
      server/middlewares/response.js
  24. 0 16
      server/nodemon.json
  25. 0 2770
      server/package-lock.json
  26. 0 35
      server/package.json
  27. 0 14
      server/process.prod.json
  28. 0 36
      server/qcloud.js
  29. 0 47
      server/routes/index.js
  30. 0 28
      server/tools.md
  31. 0 34
      server/tools/cAuth.sql
  32. 0 42
      server/tools/initdb.js
  33. 0 32
      server/tools/snall.sql
  34. 40 42
      src/App.vue
  35. 8 37
      src/app.json
  36. 0 122
      src/components/BookInfo.vue
  37. 11 92
      src/components/Card.vue
  38. 0 70
      src/components/CommentList.vue
  39. 0 34
      src/components/Rate.vue
  40. 0 57
      src/components/TopSwiper.vue
  41. 0 54
      src/components/YearProgress.vue
  42. 104 0
      src/components/list-item.vue
  43. 191 0
      src/components/list-wrap.vue
  44. 0 10
      src/config.js
  45. 4 36
      src/main.js
  46. 939 0
      src/pages/banner/index.vue
  47. 12 0
      src/pages/banner/main.js
  48. 3 0
      src/pages/banner/main.json
  49. 0 89
      src/pages/books/Book.vue
  50. 0 12
      src/pages/books/main.js
  51. 0 69
      src/pages/comments/Comment.vue
  52. 0 12
      src/pages/comments/main.js
  53. 0 194
      src/pages/detail/Detail.vue
  54. 251 0
      src/pages/detail/index.vue
  55. 8 1
      src/pages/detail/main.js
  56. 3 0
      src/pages/detail/main.json
  57. 265 0
      src/pages/index/index.vue
  58. 12 0
      src/pages/index/main.js
  59. 3 0
      src/pages/index/main.json
  60. 61 0
      src/pages/logs/index.vue
  61. 5 0
      src/pages/logs/main.js
  62. 3 0
      src/pages/logs/main.json
  63. 0 105
      src/pages/me/Me.vue
  64. 0 5
      src/pages/me/main.js
  65. 18 0
      src/store/index.js
  66. 21 0
      src/store/modules/nav.js
  67. 11 0
      src/utils/config.js
  68. 24 0
      src/utils/index.js
  69. 1 1
      src/util.js
  70. 47 0
      src/utils/storage.js
  71. BIN
      static/banner/close_store.png
  72. BIN
      static/banner/color_pillar.png
  73. BIN
      static/banner/coupon.png
  74. BIN
      static/banner/draw_btn.png
  75. BIN
      static/banner/gold.png
  76. BIN
      static/banner/gold01.png
  77. BIN
      static/banner/iphone.png
  78. BIN
      static/banner/luck_bg.png
  79. BIN
      static/banner/lucky_title.png
  80. BIN
      static/banner/renminbi.png
  81. BIN
      static/banner/sorry.png
  82. BIN
      static/banner/wow.png
  83. BIN
      static/crop_mango_sound.mp3
  84. BIN
      static/images/grey.gif
  85. BIN
      static/images/user.png
  86. BIN
      static/img/book-active.png
  87. BIN
      static/img/book.png
  88. BIN
      static/img/me-active.png
  89. BIN
      static/img/me.png
  90. BIN
      static/img/other-active.png
  91. BIN
      static/img/other.png
  92. BIN
      static/img/room-active.png
  93. BIN
      static/img/room.png
  94. BIN
      static/img/todo-active.png
  95. BIN
      static/img/todo.png
  96. BIN
      static/img/unlogin.png

+ 2 - 1
config/dev.env.js

@@ -2,5 +2,6 @@ var merge = require('webpack-merge')
 var prodEnv = require('./prod.env')
 
 module.exports = merge(prodEnv, {
-  NODE_ENV: '"development"'
+  NODE_ENV: '"development"',
+  API_ROOT: "'https://api.mgtoutiao.com'" // 开发环境 接口路径 必须是双层引号
 })

+ 2 - 1
config/prod.env.js

@@ -1,3 +1,4 @@
 module.exports = {
-  NODE_ENV: '"production"'
+  NODE_ENV: '"production"',
+  API_ROOT: "'https://api.mgtoutiao.com'" // 生产环境 接口路径 必须是双层引号
 }

+ 1 - 1
index.html

@@ -2,7 +2,7 @@
 <html>
   <head>
     <meta charset="utf-8">
-    <title>my-mpvue</title>
+    <title>村讯精选</title>
   </head>
   <body>
     <div id="app"></div>

+ 0 - 5
package-lock.json

@@ -9982,11 +9982,6 @@
       "resolved": "http://registry.npm.taobao.org/vuex/download/vuex-3.1.0.tgz",
       "integrity": "sha1-Y0uBUVzwz+l2vR/+lgF1XlH4Q7k="
     },
-    "wafer2-client-sdk": {
-      "version": "2.1.0",
-      "resolved": "http://registry.npm.taobao.org/wafer2-client-sdk/download/wafer2-client-sdk-2.1.0.tgz",
-      "integrity": "sha1-szh9jw/jaO0P7ZzOLr/doo+Fs4w="
-    },
     "watchpack": {
       "version": "1.6.0",
       "resolved": "http://registry.npm.taobao.org/watchpack/download/watchpack-1.6.0.tgz",

+ 1 - 2
package.json

@@ -25,8 +25,7 @@
   },
   "dependencies": {
     "mpvue": "^2.0.0",
-    "vuex": "^3.0.1",
-    "wafer2-client-sdk": "^2.1.0"
+    "vuex": "^3.0.1"
   },
   "devDependencies": {
     "babel-core": "^6.22.1",

+ 2 - 3
project.config.json

@@ -9,10 +9,9 @@
 		"autoAudits": false
 	},
 	"miniprogramRoot": "dist/wx/",
-	"qcloudRoot": "server/",
 	"compileType": "miniprogram",
-	"appid": "wxa28c90f932b1fb61",
-	"projectname": "my-mpvue",
+	"appid": "wx4305ff37a0879263",
+	"projectname": "mango-xcx-info",
 	"condition": {
 		"search": {
 			"current": -1,

+ 0 - 16
server/.eslintrc.json

@@ -1,16 +0,0 @@
-{
-  "root": true,
-  "parser": "babel-eslint",
-  "parserOptions": {
-    "sourceType": "module"
-  },
-  "extends": "standard",
-  "rules": {
-    "indent": [2, 2, { "SwitchCase": 1 }],
-    "arrow-parens": 0,
-    "generator-star-spacing": 0
-  },
-  "env": {
-    "mocha": true
-  }
-}

+ 0 - 130
server/README.md

@@ -1,130 +0,0 @@
-# 腾讯云小程序解决方案 Demo - Node.js
-
-Node.js 版本 Wafer SDK 的服务端 Demo
-
-## 下载源码
-
-你可以直接通过 git 将代码 clone 到本地,也可以点击[这里](https://github.com/tencentyun/wafer-node-server-demo/releases)下载。
-
-```bash
-git clone https://github.com/tencentyun/wafer-node-server-demo.git
-```
-
-## 开始使用
-
-#### 安装依赖
-
-```bash
-# 安装全局依赖
-npm i pm2 nodemon -g
-
-# 安装项目依赖
-npm i
-```
-
-#### 启动项目
-
-```bash
-# 开发环境,监听文件变化自动重启,并会输出 debug 信息
-tnpm run dev
-
-# 线上部署环境
-tnpm start
-```
-
-按照[小程序创建资源配置指引](https://github.com/tencentyun/weapp-doc)进行操作,可以得到运行本示例所需的资源和服务,其中包括已部署好的示例代码及自动下发的 SDK 配置文件 `/etc/qcloud/sdk.config`。
-
-- 示例代码部署目录:`/data/release/node-weapp-demo`
-- 运行示例的 Node 版本:`v8.1.0`
-- Node 进程管理工具:`pm2`
-
-## 项目结构
-
-```
-koa-weapp-demo
-├── README.md
-├── app.js
-├── controllers
-│   ├── index.js
-│   ├── login.js
-│   ├── message.js
-│   ├── tunnel.js
-│   ├── upload.js
-│   └── user.js
-├── middlewares
-│   └── response.js
-├── config.js
-├── package.json
-├── process.json
-├── nodemon.json
-├── qcloud.js
-└── routes
-    └── index.js
-```
-`app.js` 是 Demo 的主入口文件,Demo 使用 Koa 框架,在 `app.js` 创建一个 Koa 实例并响应请求。
-
-`routes/index.js` 是 Demo 的路由定义文件
-
-`controllers` 存放 Demo 所有业务逻辑的目录,`index.js` 不需要修改,他会动态的将 `controllers` 文件夹下的目录结构映射成 modules 的 Object,例如 Demo 中的目录将会被映射成如下的结构:
-
-```javascript
-// index.js 输出
-{
-  login: require('login'),
-  message: require('message'),
-  tunnel: require('tunnel'),
-  upload: require('upload'),
-  user: require('user')
-}
-```
-
-`qcloud.js` 导出了一个 SDK 的单例,包含了所有的 SDK 接口,之后使用的时候只需要 `require` 这个文件就行,无需重复初始化 SDK。
-
-`config.js` 主要的配置如下:
-
-```javascript
-{
-  port: '5757',                             // 项目启动的端口
-
-  appId: 'wx00dd00dd00dd00dd',              // 微信小程序 App ID
-  appSecret: 'abcdefg',                     // 微信小程序 App Secret
-  wxLoginExpires: 7200,                     // 微信登录态有效期
-  useQcloudLogin: false,                    // 是否使用腾讯云代理登录
-
-  /**
-   * MySQL 配置,用来存储用户登录态和用户信息
-   * 如果不提供 MySQL 配置,模式会使用自动配置好的本地镜像中的 MySQL 储存信息
-   * 具体查看文档-登录态储存和校验
-   **/
-  mysql: {
-    host: 'localhost',
-    port: 3306,
-    user: 'root',
-    db: 'cAuth',
-    pass: '',
-    char: 'utf8'
-  },
-  
-  // COS 配置,用于上传模块使用
-  cos: {
-    /**
-     * 区域
-     * 华北:cn-north
-     * 华东:cn-east
-     * 华南:cn-south
-     * 西南:cn-southwest
-     */
-    region: 'cn-south',
-    fileBucket: 'test',                    // Bucket 名称
-    uploadFolder: ''                       // 文件夹
-  }
-}
-```
-
-除了 `config.js` ,腾讯云还会在你初始化小程序解决方案的时候,向你的机器下发 `sdk.config`,里面包含了你的腾讯云 AppId、SecretId、SecretKey 和服务器等信息,无需修改,`qcloud.js` 会自动引入。如果你想要在自己的机器上部署 SDK 的 Demo,请查看[自行部署 Demo 说明]()。
-
-除此以外,关于 SDK 的详细配置信息,还可以查看 [SDK 的 API 文档]()。
-
-
-### 技术博客参考
-[Mpvue+koa开发微信小程序——上传测试代码到腾讯云以及腾讯云后台本地开发环境的搭建](https://blog.csdn.net/Fabulous1111/article/details/80553806)

+ 0 - 19
server/app.js

@@ -1,19 +0,0 @@
-const Koa = require('koa')
-const app = new Koa()
-const debug = require('debug')('koa-weapp-demo')
-const response = require('./middlewares/response')
-const bodyParser = require('koa-bodyparser')
-const config = require('./config')
-
-// 使用响应处理中间件
-app.use(response)
-
-// 解析请求体
-app.use(bodyParser())
-
-// 引入路由分发
-const router = require('./routes')
-app.use(router.routes())
-
-// 启动程序,监听端口
-app.listen(config.port, () => debug(`listening on port ${config.port}`))

+ 0 - 56
server/config.js

@@ -1,56 +0,0 @@
-// 相关配置上传github已删除;运行请自行添加 ,参考 README.md
-const CONF = {
-    serverHost: 'localhost',
-    tunnelServerUrl: '',
-    tunnelSignatureKey: '',
-    // 腾讯云相关配置可以查看云 API 密钥控制台:https://console.cloud.tencent.com/capi
-    qcloudAppId: '',
-    qcloudSecretId: '',
-    qcloudSecretKey: '',
-    wxMessageToken: '',
-    networkTimeout: 30000,
-
-    port: '5757',
-    rootPathname: '',
-
-    // 微信小程序 App ID
-    appId: '',
-
-    // 微信小程序 App Secret
-    appSecret: '',
-
-    // 是否使用腾讯云代理登录小程序
-    useQcloudLogin: false,
-
-    /**
-     * MySQL 配置,用来存储 session 和用户信息
-     * 若使用了腾讯云微信小程序解决方案
-     * 开发环境下,MySQL 的初始密码为您的微信小程序 appid
-     */
-    mysql: {
-        host: 'localhost',
-        port: 3306,
-        user: 'root',
-        db: 'cAuth',
-        pass: 'root',
-        char: 'utf8mb4'
-    },
-
-    cos: {
-        /**
-         * 地区简称
-         * @查看 https://cloud.tencent.com/document/product/436/6224
-         */
-        region: 'ap-guangzhou',
-        // Bucket 名称
-        fileBucket: 'qcloudtest',
-        // 文件夹
-        uploadFolder: ''
-    },
-
-    // 微信登录态有效期
-    wxLoginExpires: 7200
-    // wxMessageToken: 'abcdefgh'
-}
-
-module.exports = CONF

+ 0 - 67
server/controllers/addbook.js

@@ -1,67 +0,0 @@
-
-const https = require('https')
-const { mysql } = require('../qcloud')
-// 新增图书  1. 获取豆瓣信息  2 入库
-// https://api.douban.com/v2/book/isbn/9787536692930
-module.exports = async (ctx) => {
-  const { isbn, openid } = ctx.request.body
-  console.log('添加图书', isbn, openid)
-  if (isbn && openid) {
-    // 录入异常情况
-    const findRes = await mysql('books').select().where('isbn', isbn)
-    if (findRes.length) {
-      ctx.state = {
-        code: -1,
-        data: {
-          msg: '图书已存在'
-        }
-      }
-      return
-    }
-
-    let url = 'https://api.douban.com/v2/book/isbn/' + isbn
-    console.log('链接', url)
-    const bookInfo = await getJSON(url)
-    const rate = bookInfo.rating.average
-    const { title, image, alt, publisher, summary, price } = bookInfo
-    const tags = bookInfo.tags.map(v => {
-      return `${v.title} ${v.count}`
-    }).join(',')
-    const author = bookInfo.author.join(',')
-    try {
-      // 写入库
-      await mysql('books').insert({
-        isbn, openid, rate, title, image, alt, publisher, summary, price, tags, author
-      })
-      ctx.state.data = {
-        title,
-        msg: 'success'
-      }
-    } catch (e) {
-      ctx.state = {
-        code: -1,
-        data: {
-          msg: '新增失败:' + e.sqlMessage
-        }
-      }
-    }
-  }
-}
-
-function getJSON (url) {
-  return new Promise((resolve, reject) => {
-    https.get(url, res => {
-      let urlData = ''
-      res.on('data', data => {
-        urlData += data
-      })
-      res.on('end', data => {
-        const bookInfo = JSON.parse(urlData)
-        if (bookInfo.title) {
-          resolve(bookInfo)
-        }
-        reject(bookInfo)
-      })
-    })
-  })
-}

+ 0 - 19
server/controllers/addcomment.js

@@ -1,19 +0,0 @@
-const { mysql } = require('../qcloud')
-
-module.exports = async (ctx) => {
-  const {bookid, comment, openid, location, phone} = ctx.request.body
-  console.log(bookid, comment, openid, location, phone)
-  try {
-    await mysql('comments').insert({bookid, comment, openid, location, phone})
-    ctx.state.data = {
-      msg: 'success'
-    }
-  } catch (e) {
-    ctx.state = {
-      code: -1,
-      data: {
-        msg: '评论失败' + e.sqlMessage
-      }
-    }
-  }
-}

+ 0 - 26
server/controllers/bookdetail.js

@@ -1,26 +0,0 @@
-const {mysql} = require('../qcloud')
-
-module.exports = async (ctx) => {
-  const {id} = ctx.request.query
-  const detail = await mysql('books')
-    .select('books.*', 'cSessionInfo.user_info')
-    .join('cSessionInfo', 'books.openid', 'cSessionInfo.open_id')
-    .where('id', id)
-    .first()
-
-  // console.log(detail)
-  const info = JSON.parse(detail.user_info)
-  ctx.state.data = Object.assign({}, detail, {
-    tags: detail.tags.split(','),
-    summary: detail.summary.split('\n'),
-    user_info: {
-      name: info.nickName,
-      image: info.avatarUrl
-    }
-  })
-
-  // 浏览量+1
-  await mysql('books')
-    .where('id', id)
-    .increment('count', 1)
-}

+ 0 - 28
server/controllers/booklist.js

@@ -1,28 +0,0 @@
-const { mysql } = require('../qcloud')
-
-module.exports = async (ctx) => {
-  const {page, openid} = ctx.request.query
-  const size = 10
-  const mysqlSelect = mysql('books')
-    .select('books.*', 'cSessionInfo.user_info')
-    .join('cSessionInfo', 'books.openid', 'cSessionInfo.open_id')
-    .orderBy('books.id', 'desc')
-  let books
-  if (openid) {
-    // 根据opid过滤
-    books = await mysqlSelect.where('books.openid', openid)
-  } else {
-    // 全部图书 分页
-    books = await mysqlSelect.limit(size).offset(Number(page) * size)
-  }
-  ctx.state.data = {
-    list: books.map(v => {
-      const info = JSON.parse(v.user_info)
-      return Object.assign({}, v, {
-        user_info: {
-          nickName: info.nickName
-        }
-      })
-    })
-  }
-}

+ 0 - 26
server/controllers/commentlist.js

@@ -1,26 +0,0 @@
-const { mysql } = require('../qcloud')
-
-module.exports = async (ctx) => {
-  const {bookid, openid} = ctx.request.query
-  const mysqlSelect = mysql('comments')
-    .select('comments.*', 'cSessionInfo.user_info')
-    .join('cSessionInfo', 'comments.openid', 'cSessionInfo.open_id')
-  let comments
-  if (bookid) {
-    // 图书详情的评论列表
-    comments = await mysqlSelect.where('bookid', bookid)
-  } else if (openid) {
-    // 根据个人的openid筛选
-    comments = await mysqlSelect.where('openid', openid)
-  }
-  ctx.state.data = {
-    list: comments.map(v => {
-      const info = JSON.parse(v.user_info)
-
-      return Object.assign({}, v, {
-        title: info.nickName,
-        image: info.avatarUrl
-      })
-    })
-  }
-}

+ 0 - 5
server/controllers/demo.js

@@ -1,5 +0,0 @@
-module.exports = async (ctx) => {
-  ctx.state.data = {
-    msg: 'hello 小程序后台'
-  }
-}

+ 0 - 30
server/controllers/index.js

@@ -1,30 +0,0 @@
-const _ = require('lodash')
-const fs = require('fs')
-const path = require('path')
-
-/**
- * 映射 d 文件夹下的文件为模块
- */
-const mapDir = d => {
-  const tree = {}
-
-  // 获得当前文件夹下的所有的文件夹和文件
-  const [dirs, files] = _(fs.readdirSync(d)).partition(p => fs.statSync(path.join(d, p)).isDirectory())
-
-  // 映射文件夹
-  dirs.forEach(dir => {
-    tree[dir] = mapDir(path.join(d, dir))
-  })
-
-  // 映射文件
-  files.forEach(file => {
-    if (path.extname(file) === '.js') {
-      tree[path.basename(file, '.js')] = require(path.join(d, file))
-    }
-  })
-
-  return tree
-}
-
-// 默认导出当前文件夹下的映射
-module.exports = mapDir(path.join(__dirname))

+ 0 - 10
server/controllers/login.js

@@ -1,10 +0,0 @@
-// 登录授权接口
-module.exports = async (ctx, next) => {
-  // 通过 Koa 中间件进行登录之后
-  // 登录信息会被存储到 ctx.state.$wxInfo
-  // 具体查看:
-  if (ctx.state.$wxInfo.loginState) {
-    ctx.state.data = ctx.state.$wxInfo.userinfo
-    ctx.state.data['time'] = Math.floor(Date.now() / 1000)
-  }
-}

+ 0 - 29
server/controllers/message.js

@@ -1,29 +0,0 @@
-const { message: { checkSignature } } = require('../qcloud')
-
-/**
- * 响应 GET 请求(响应微信配置时的签名检查请求)
- */
-async function get (ctx, next) {
-  const { signature, timestamp, nonce, echostr } = ctx.query
-  if (checkSignature(signature, timestamp, nonce)) ctx.body = echostr
-  else ctx.body = 'ERR_WHEN_CHECK_SIGNATURE'
-}
-
-async function post (ctx, next) {
-  // 检查签名,确认是微信发出的请求
-  const { signature, timestamp, nonce } = ctx.query
-  if (!checkSignature(signature, timestamp, nonce)) ctx.body = 'ERR_WHEN_CHECK_SIGNATURE'
-
-  /**
-     * 解析微信发送过来的请求体
-     * 可查看微信文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/receive.html#接收消息和事件
-     */
-  // const body = ctx.request.body
-
-  ctx.body = 'success'
-}
-
-module.exports = {
-  post,
-  get
-}

+ 0 - 11
server/controllers/top.js

@@ -1,11 +0,0 @@
-const {mysql} = require('../qcloud')
-
-module.exports = async (ctx) => {
-  const top = await mysql('books')
-    .select('id', 'title', 'image', 'count')
-    .orderBy('count', 'desc')
-    .limit(9)
-  ctx.state.data = {
-    list: top
-  }
-}

+ 0 - 9
server/controllers/upload.js

@@ -1,9 +0,0 @@
-const { uploader } = require('../qcloud')
-
-module.exports = async ctx => {
-  // 获取上传之后的结果
-  // 具体可以查看:
-  const data = await uploader(ctx.req)
-
-  ctx.state.data = data
-}

+ 0 - 11
server/controllers/user.js

@@ -1,11 +0,0 @@
-module.exports = async (ctx, next) => {
-  // 通过 Koa 中间件进行登录态校验之后
-  // 登录信息会被存储到 ctx.state.$wxInfo
-  // 具体查看:
-  if (ctx.state.$wxInfo.loginState === 1) {
-    // loginState 为 1,登录态校验成功
-    ctx.state.data = ctx.state.$wxInfo.userinfo
-  } else {
-    ctx.state.code = -1
-  }
-}

+ 0 - 31
server/middlewares/response.js

@@ -1,31 +0,0 @@
-const debug = require('debug')('koa-weapp-demo')
-
-/**
- * 响应处理模块
- */
-module.exports = async function (ctx, next) {
-    try {
-        // 调用下一个 middleware
-        await next()
-
-        // 处理响应结果
-        // 如果直接写入在 body 中,则不作处理
-        // 如果写在 ctx.body 为空,则使用 state 作为响应
-        ctx.body = ctx.body ? ctx.body : {
-            code: ctx.state.code !== undefined ? ctx.state.code : 0,
-            data: ctx.state.data !== undefined ? ctx.state.data : {}
-        }
-    } catch (e) {
-        // catch 住全局的错误信息
-        debug('Catch Error: %o', e)
-
-        // 设置状态码为 200 - 服务端错误
-        ctx.status = 200
-
-        // 输出详细的错误信息
-        ctx.body = {
-            code: -1,
-            error: e && e.message ? e.message : e.toString()
-        }
-    }
-}

+ 0 - 16
server/nodemon.json

@@ -1,16 +0,0 @@
-{
-    "restartable": "rs",
-    "ignore": [
-        ".git",
-        "node_modules/**/node_modules"
-    ],
-    "verbose": true,
-    "execMap": {
-        "js": "node --harmony"
-    },
-    "env": {
-        "NODE_ENV": "development",
-        "DEBUG": "*,-nodemon:*,-nodemon,-knex:pool"
-    },
-    "ext": "js json"
-}

File diff suppressed because it is too large
+ 0 - 2770
server/package-lock.json


+ 0 - 35
server/package.json

@@ -1,35 +0,0 @@
-{
-    "name": "koa-weapp-demo",
-    "version": "1.0.0",
-    "description": "",
-    "main": "app.js",
-    "scripts": {
-        "start": "pm2 start process.prod.json --no-daemon",
-        "dev": "nodemon --config nodemon.json app.js",
-        "initdb": "npm install && node tools/initdb.js",
-        "lint": "eslint --fix --ext .js server"
-    },
-    "author": "Jason",
-    "license": "MIT",
-    "dependencies": {
-        "axios": "^0.15.3",
-        "knex": "^0.13.0",
-        "koa": "^2.0.0",
-        "koa-bodyparser": "^3.2.0",
-        "koa-log4": "^2.1.0",
-        "koa-router": "^7.0.1",
-        "lodash": "^4.17.4",
-        "mkdir-p": "0.0.7",
-        "mysql": "^2.14.1",
-        "pify": "^2.3.0",
-        "wafer-node-sdk": "^1.4.0"
-    },
-    "devDependencies": {
-        "babel-eslint": "^7.1.0",
-        "debug": "^2.6.8",
-        "eslint": "^3.9.1",
-        "eslint-config-standard": "^6.2.1",
-        "eslint-plugin-promise": "^3.3.1",
-        "eslint-plugin-standard": "^2.0.1"
-    }
-}

+ 0 - 14
server/process.prod.json

@@ -1,14 +0,0 @@
-{
-    "name": "session",
-    "script": "app.js",
-    "cwd": "./",
-    "exec_mode": "fork",
-    "watch": true,
-    "ignore_watch": ["tmp"],
-    "env": {
-        "NODE_ENV": "production"
-    },
-    "engines": {
-        "node": ">=7.6"
-    }
-}

+ 0 - 36
server/qcloud.js

@@ -1,36 +0,0 @@
-const fs = require('fs')
-const qcloud = require('wafer-node-sdk')
-
-// 获取基础配置
-const configs = require('./config')
-
-// 获取 sdk.config
-const sdkConfig = (() => {
-    const sdkConfigPath = '/data/release/sdk.config.json'
-
-    // 检查文件是否存在
-    try {
-        const stats = fs.statSync(sdkConfigPath)
-
-        if (!stats.isFile()) {
-            console.log('sdk.config.json 不存在,将使用 config.js 中的配置')
-            return {}
-        }
-    } catch (e) {
-        return {}
-    }
-
-    // 返回配置信息
-    try {
-        const content = fs.readFileSync(sdkConfigPath, 'utf8')
-        return JSON.parse(content)
-    } catch (e) {
-        // 如果配置读取错误或者 JSON 解析错误,则输出空配置项
-        console.log('sdk.config.json 解析错误,不是 JSON 字符串')
-        return {}
-    }
-})()
-
-// 初始化 SDK
-// 将基础配置和 sdk.config 合并传入 SDK 并导出初始化完成的 SDK
-module.exports = qcloud(Object.assign({}, sdkConfig, configs))

+ 0 - 47
server/routes/index.js

@@ -1,47 +0,0 @@
-/**
- * ajax 服务路由集合
- */
-const router = require('koa-router')({
-    prefix: '/weapp'
-})
-const controllers = require('../controllers')
-
-// 从 sdk 中取出中间件
-// 这里展示如何使用 Koa 中间件完成登录态的颁发与验证
-const { auth: { authorizationMiddleware, validationMiddleware } } = require('../qcloud')
-
-// --- 登录与授权 Demo --- //
-// 登录接口
-router.get('/login', authorizationMiddleware, controllers.login)
-// 用户信息接口(可以用来验证登录态)
-router.get('/user', validationMiddleware, controllers.user)
-
-// --- 图片上传 Demo --- //
-// 图片上传接口,小程序端可以直接将 url 填入 wx.uploadFile 中
-router.post('/upload', controllers.upload)
-
-// --- 信道服务接口 Demo --- //
-// // GET  用来响应请求信道地址的
-// router.get('/tunnel', controllers.tunnel.get)
-// // POST 用来处理信道传递过来的消息
-// router.post('/tunnel', controllers.tunnel.post)
-
-// --- 客服消息接口 Demo --- //
-// GET  用来响应小程序后台配置时发送的验证请求
-router.get('/message', controllers.message.get)
-// POST 用来处理微信转发过来的客服消息
-router.post('/message', controllers.message.post)
-
-// 测试 demo api //
-router.get('/demo', controllers.demo)
-// 添加图书
-router.post('/addbook', controllers.addbook)
-// 获取图书列表
-router.get('/booklist', controllers.booklist)
-router.get('/bookdetail', controllers.bookdetail)
-router.get('/top', controllers.top)
-// 评论
-router.post('/addcomment', controllers.addcomment)
-router.get('/commentlist', controllers.commentlist)
-
-module.exports = router

+ 0 - 28
server/tools.md

@@ -1,28 +0,0 @@
-# 腾讯云小程序解决方案 Demo 工具使用文档
-
-本文件夹下的脚本为腾讯云小程序解决方案 Demo 配套的工具,旨在让用户方便快捷的使用并创建小程序的开发环境。
-
-工具包括:
-
-- [数据库初始化工具](#数据库初始化工具)
-
-## 数据库初始化工具
-
-本工具是为了让用户快速的按照腾讯云制定的数据库 schema 创建符合 SDK 标准的数据库结构。
-
-_**注意**:本工具支持的 MySQL 版本为 **5.7**,并且需提前在数据库中创建名为 `cAuth` 的数据库。`charset` 设置为 `utf8mb4`。_
-
-快速使用:
-
-```bash
-npm run initdb
-```
-
-或直接执行 `tools` 目录下的 `initdb.js` 文件:
-
-```bash
-# 请保证已经执行了 npm install 安装了所需要的依赖
-node tools/initdb.js
-```
-
-我们提供了初始化的 SQL 文件,你也可以用其他数据库工具(如 Navicat)直接导入 SQL 文件。

+ 0 - 34
server/tools/cAuth.sql

@@ -1,37 +0,0 @@
-/*
- Navicat Premium Data Transfer
-
- Source Server         : Localhost
- Source Server Type    : MySQL
- Source Server Version : 50717
- Source Host           : localhost
- Source Database       : cAuth
-
- Target Server Type    : MySQL
- Target Server Version : 50717
- File Encoding         : utf-8
-
- Date: 08/10/2017 22:22:52 PM
-*/
-
-SET NAMES utf8;
-SET FOREIGN_KEY_CHECKS = 0;
-
-DROP TABLE IF EXISTS `cSessionInfo`;
-CREATE TABLE `cSessionInfo` (
-  `open_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
-  `uuid` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
-  `skey` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
-  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
-  `last_visit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
-  `session_key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
-  `user_info` varchar(2048) COLLATE utf8mb4_unicode_ci NOT NULL,
-  PRIMARY KEY (`open_id`),
-  KEY `openid` (`open_id`) USING BTREE,
-  KEY `skey` (`skey`) USING BTREE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话管理用户信息';
-
-SET FOREIGN_KEY_CHECKS = 1;

+ 0 - 42
server/tools/initdb.js

@@ -1,42 +0,0 @@
-/**
- * 腾讯云微信小程序解决方案
- * Demo 数据库初始化脚本
- * @author Jason
- */
-const fs = require('fs')
-const path = require('path')
-const { mysql: config } = require('../config')
-
-console.log('\n======================================')
-console.log('开始初始化数据库...')
-
-// 初始化 SQL 文件路径
-const INIT_DB_FILE = path.join(__dirname, './cAuth.sql')
-
-const DB = require('knex')({
-    client: 'mysql',
-    connection: {
-        host: config.host,
-        port: config.port,
-        user: config.user,
-        password: config.pass,
-        database: config.db,
-        charset: config.char,
-        multipleStatements: true
-    }
-})
-
-console.log(`准备读取 SQL 文件:${INIT_DB_FILE}`)
-
-// 读取 .sql 文件内容
-const content = fs.readFileSync(INIT_DB_FILE, 'utf8')
-
-console.log('开始执行 SQL 文件...')
-
-// 执行 .sql 文件内容
-DB.raw(content).then(res => {
-    console.log('数据库初始化成功!')
-    process.exit(0)
-}, err => {
-    throw new Error(err)
-})

+ 0 - 32
server/tools/snall.sql

@@ -1,33 +0,0 @@
-
-
-DROP TABLE IF EXISTS `books`;
-
- CREATE TABLE `books` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `isbn` varchar(20) NOT NULL,
-  `openid` varchar(100) NOT NULL,
-  `title` varchar(100) NOT NULL,
-  `image` varchar(100) NOT NULL,
-  `alt` varchar(100) NOT NULL,
-  `publisher` varchar(100) NOT NULL,
-  `summary` varchar(1000) NOT NULL,
-  `price` varchar(100) DEFAULT NULL,
-  `rate` float DEFAULT NULL,
-  `tags` varchar(100) DEFAULT NULL,
-  `author` varchar(100) DEFAULT NULL,
-  `count` int(11) NOT NULL DEFAULT '0',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
-
-DROP TABLE IF EXISTS `comments`;
-
-CREATE TABLE `comments` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `openid` varchar(100) NOT NULL,
-  `bookid` varchar(100) NOT NULL,
-  `comment` varchar(200) NOT NULL,
-  `phone` varchar(20) DEFAULT NULL,
-  `location` varchar(20) DEFAULT NULL,
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

+ 40 - 42
src/App.vue

@@ -1,52 +1,50 @@
-
 <script>
-// import qcloud from 'wafer2-client-sdk'
 export default {
-  async created () {
-    // const res = await get('/weapp/demo')
-    // console.log(123, res)
-    // wx.request({
-    //   url:config.host+'/weapp/demo',
-    //   success:function(res){
-    //     console.log(res)
-    //   }
-    // })
-    console.log('小程序启动了')
+  created () {
+    // 调用API从本地缓存中获取数据
+    /*
+     * 平台 api 差异的处理方式:  api 方法统一挂载到 mpvue 名称空间, 平台判断通过 mpvuePlatform 特征字符串
+     * 微信:mpvue === wx, mpvuePlatform === 'wx'
+     * 头条:mpvue === tt, mpvuePlatform === 'tt'
+     * 百度:mpvue === swan, mpvuePlatform === 'swan'
+     * 支付宝(蚂蚁):mpvue === my, mpvuePlatform === 'my'
+     */
+
+    let logs
+    if (mpvuePlatform === 'my') {
+      logs = mpvue.getStorageSync({key: 'logs'}).data || []
+      logs.unshift(Date.now())
+      mpvue.setStorageSync({
+        key: 'logs',
+        data: logs
+      })
+    } else {
+      logs = mpvue.getStorageSync('logs') || []
+      logs.unshift(Date.now())
+      mpvue.setStorageSync('logs', logs)
+    }
+  },
+  log () {
+    console.log(`log at:${Date.now()}`)
   }
 }
 </script>
 
 <style>
-.text-footer{
-  text-align: center;
-  font-size: 12px;
-  margin-bottom:5px;
-}
-.text-primary{
-  color:#EA5149;
-}
-.page-title{
-  padding-left:20px;
-  background:#eee;
-  line-height: 40px;
-  font-size: 14px;
-}
-.right{
-  float: right;
-}
-.btn{
-  color:white;
-  background:#EA5A49;
-  margin-bottom: 10px;
-  padding-left: 15px;
-  padding-left: 15px;
-  border-radius: 2px;
-  font-size: 16px;
-  line-height: 40px;
-  height: 40px;
-  width: 100%;
+.container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: space-between;
+  padding: 200rpx 0;
+  box-sizing: border-box;
 }
-.btn:active{
-  background: #FA5A49;
+/* this rule will be remove */
+* {
+  transition: width 2s;
+  -moz-transition: width 2s;
+  -webkit-transition: width 2s;
+  -o-transition: width 2s;
 }
 </style>

+ 8 - 37
src/app.json

@@ -1,44 +1,15 @@
 {
   "pages": [
-    "pages/books/main",
-    "pages/comments/main",
-    "pages/me/main",
+    "pages/index/main",
+    "pages/banner/main",
     "pages/detail/main"
-
   ],
-  "permission": {
-    "scope.userLocation": {
-      "desc": "你的位置信息将用于小程序位置接口的效果展示"
-    }
-  },
   "window": {
     "backgroundTextStyle": "light",
-    "navigationBarBackgroundColor": "#EA5149",
-    "navigationBarTitleText": "蜗牛图书",
-    "navigationBarTextStyle": "black",
-    "enablePullDownRefresh": true
+    "navigationBarBackgroundColor": "#db499c",
+    "navigationBarTitleText": "村讯精选",
+    "navigationBarTextStyle": "white"
   },
-  "tabBar": {
-    "list": [
-      {
-        "pagePath": "pages/books/main",
-        "text": "图书",
-        "iconPath": "static/img/book.png",
-        "selectedIconPath": "static/img/book-active.png"
-      },
-      {
-        "pagePath": "pages/comments/main",
-        "text": "评论",
-        "iconPath": "static/img/todo.png",
-        "selectedIconPath": "static/img/todo-active.png"
-      },
-      {
-        "pagePath": "pages/me/main",
-        "text": "我",
-        "iconPath": "static/img/me.png",
-        "selectedIconPath": "static/img/me-active.png"
-      }
-
-    ]
-  }
-}
+  "navigateToMiniProgramAppIdList": ["wx558e4cb3c26e6076"],
+  "requiredBackgroundModes": ["audio", "location"]
+}

+ 0 - 122
src/components/BookInfo.vue

@@ -1,122 +0,0 @@
-<template>
-  <div class="bookinfo">
-    <div class="thumb">
-      <img class="back" 
-           :src="info.image"  
-           mode="scaleToFill">
-      <img class="img" 
-           :src="info.image" 
-           mode="aspectFit">
-      <div class="info">
-        <div class="title">
-          {{info.title}}
-        </div>
-        <div class="author">
-          {{info.author}}
-        </div>
-      </div>
-    </div>
-    <div class="owner detail">
-        <img class="avatar" 
-            :src="userinfo.image"
-            mode="aspectFit">
-           <!-- 添加人 -->
-           {{userinfo.name}}
-        <div class="right text-primary">
-          {{info.rate}}分
-          <Rate :value="info.rate"></Rate>
-        </div>
-      </div>
-      <div class="detail">
-        {{info.publisher}}
-        <div class="right">
-          {{info.price}}
-        </div>
-      </div>
-      <!-- 标签和详情 -->
-      <div class="tags">
-        <div class="badge" :key="tag" v-for="tag in info.tags" >{{tag}}</div>
-      </div>
-      <div class="summary">
-        <p :key="i" v-for="(sum, i) in info.summary">{{sum}}</p>
-      </div>
-  </div>
-</template>
-<script>
-import Rate from '@/components/Rate'
-export default {
-  components: {
-    Rate
-  },
-  props: ['info'],
-  computed: {
-    userinfo () {
-      return this.info.user_info || {}
-    }
-  }
-}
-</script>
-<style lang="scss">
-.bookinfo {
-  .summary {
-    padding: 0 15px;
-    margin-top: 10px;
-    p {
-      text-indent: 2em;  // 段落开头缩进2格
-      font-size: 14px;
-    }
-  }
-  .badge {
-    display: inline-block;
-    margin: 5px;
-    padding: 5px;
-    border-radius: 10px;
-    border: 1px solid #EA5A49;
-    color: #EA5A49;
-  }
-  font-size: 14px;
-  .right {
-    float: right;
-  }
-  .detail {
-    padding: 5px 10px;
-    .avatar {
-      width: 40rpx;
-      height: 40rpx;
-      border-radius: 50%;
-      vertical-align: middle;
-    }
-  }
-  .thumb {
-    width: 750rpx;
-    height: 450rpx;
-    overflow: hidden;
-    position: relative;
-    .back {
-      filter: blur(15px);
-      width: 100%;
-    }
-    .img {
-      position: absolute;
-      width: 100%;
-      height: 300rpx;
-      left: 0;
-      top: 30rpx;
-    }
-    .info {
-      color: white;
-      position: absolute;
-      width: 100%;
-      left: 0;
-      top: 330rpx;
-      text-align: center;
-      .title {
-        font-size: 16px;
-      }
-      .author {
-        font-size: 14px;
-      }
-    }
-  }
-}
-</style>

+ 11 - 92
src/components/Card.vue

@@ -1,100 +1,19 @@
 <template>
-  <a :href="detailUrl">
-    <div class="book-card">
-      <div class="thumb">
-        <img 
-          :src="book.image" 
-          class="img" 
-          mode="aspectFit"
-          @click.stop="preview"
-          >
-      </div>
-      <div class="detail">
-        <div class="row text-primary">
-          <div class="right">
-            {{book.rate}} <Rate :value="book.rate" />
-          </div>
-          <div class="left">
-            {{book.title}}
-          </div>
-        </div>
-        <div class="row">
-          <div class="right text-primary">
-            浏览量: {{book.count}}
-          </div>
-          <div class="left">
-            {{book.author}}
-          </div>
-        </div>
-        <div class="row">
-          <div class="right">
-            <!-- 捐赠人 -->
-            {{book.user_info.nickName}}
-          </div>
-          <div class="left">
-          {{book.publisher}}
-          </div>
-        </div>
-      </div>
-    </div>
-  </a>
+  <div>
+    <p class="card">
+      {{text}}
+    </p>
+  </div>
 </template>
+
 <script>
-import Rate from '@/components/Rate'
 export default {
-  components: {
-    Rate
-  },
-  props: ['book'],
-  computed: {
-    detailUrl () {
-      return '/pages/detail/main?id=' + this.book.id
-    }
-  },
-  methods: {
-    preview () {
-      wx.previewImage({
-        current: this.book.image,
-        urls: [this.book.image]
-      })
-    }
-  }
-
+  props: ['text']
 }
 </script>
-<style lang='scss' scoped>
-.book-card{
-  padding:5px;
-  overflow: hidden;
-  margin-top:5px;
-  margin-bottom:5px;
-  font-size:14px;
-  .thumb{
-    width:90px;
-    height:90px;
-    float: left;
-    margin:0 auto;
-    overflow:hidden;
-    .img{
-      max-width: 100%;
-      max-height: 100%;
-    }
-  }
-  .detail{
-    margin-left: 100px;
-    .row{
-      line-height:20px;
-      margin-bottom:3px;
-    }
-    .right{
-      float: right;
-      
-    }
-    .left{
-      margin-right:80px;
-    }
-  }
-  
+
+<style>
+.card {
+  padding: 10px;
 }
 </style>
-

+ 0 - 70
src/components/CommentList.vue

@@ -1,70 +0,0 @@
-<template>
-  <div class="comment-list">
-    <div class="page-title"  v-if="comments.length">评论</div>
-    <div class="comment"
-        v-for="comment in comments"
-        :key="comment.id"
-        @click="handleClick(comment)"
-        >
-      <div class="user">
-        <div class="inline">
-          <img :src="comment.image"
-              class="avatar"
-              mode="aspectFit">
-          {{comment.title}}
-        </div>
-        <div class="right">
-          {{comment.location||'未知地点'}}
-          <span class="text-primary">
-            --
-          </span>
-          {{comment.phone||'未知型号'}}
-        </div>
-      </div>
-      <div class="content">
-        {{comment.comment}}
-      </div>
-    </div>
-  </div>
-</template>
-<script>
-export default {
-  props: ['comments', 'type'],
-  methods: {
-    handleClick (comment) {
-      if (this.type === 'user') {
-        wx.navigateTo({
-          url: '/pages/detail/main?id=' + comment.bookid
-        })
-      }
-    }
-  }
-}
-</script>
-<style lang="scss">
-.comment-list{
-  background:#eee;
-  font-size:14px;
-  .comment{
-    background: white;
-    margin-bottom:10px;
-    padding:5px 20px;
-    .content{
-      margin:10px 0;
-    }
-    .user{
-      .inline{
-        display:inline;
-        .avatar{
-          width:20px;
-          height:20px;
-          border-radius: 50%;
-        }
-      }
-
-    }
-
-  }
-}
-</style>
-

+ 0 - 34
src/components/Rate.vue

@@ -1,34 +0,0 @@
-<template>
-  <div class="rate">
-    <span>☆☆☆☆☆</span>
-    <div class="hollow" :style="style">
-      ★★★★★
-    </div>
-  </div>
-</template>
-<script>
-export default {
-  props: {
-    value: {type: [Number, String], default: '0'}
-  },
-  computed: {
-    style () {
-      return `width:${this.value / 2}em`
-    }
-  }
-}
-</script>
-<style lang="scss" scoped>
-.rate {
-  position: relative;
-  display: inline-block;
-  .hollow {
-    position: absolute;
-    display: inline-block;
-    top: 0;
-    left: 0;
-    width: 0;
-    overflow: hidden;
-  }
-}
-</style>

+ 0 - 57
src/components/TopSwiper.vue

@@ -1,57 +0,0 @@
-<template>
-  <div class="swiper">
-    <swiper
-      :indicator-dots="true"
-      indicator-color="#EA5A49"
-      :autoplay="true"
-      :interval="6000"
-      :duration="1000"
-      :circular="true"
-    >
-      <div :key="imgindex" v-for="(top, imgindex) in imgUrls">
-         <swiper-item>
-           <!-- img中mode是小程序提供图片大小格式  -->
-           <img 
-             @click='bookDetail(img)'
-             class="slide-image" 
-             mode="aspectFit" 
-             v-for="img in top"
-             :key="img.id"
-             :src="img.image"
-             >
-         </swiper-item>
-      </div>
-    </swiper>
-  </div>
-</template>
-<script>
-export default {
-  props: ['tops'],
-  computed: {
-    imgUrls () {
-      // 如果通用 请用chunk函数 比如lodash的chunk方法
-      let res = this.tops
-      console.log([res.slice(0, 3), res.slice(3, 6), res.slice(6)])
-      return [res.slice(0, 3), res.slice(3, 6), res.slice(6)]
-    }
-  },
-  methods: {
-    bookDetail (item) {
-      // console.log('top options', item);
-      wx.navigateTo({
-        url: '/pages/detail/main?id=' + item.id
-      })
-    }
-  }
-}
-</script>
-<style lang="scss">
-.swiper{
-  margin-top:5px;
-  .slide-image{
-    width:33%;
-    height:250rpx;
-  }
-}
-</style>
-

+ 0 - 54
src/components/YearProgress.vue

@@ -1,54 +0,0 @@
-<template>
-  <div class="progressbar">
-    
-    <progress :percent="percent" activeColor="#EA5A49" show-info />
-    <p>{{year}}已经过去{{days}}天</p>
-  </div>
-</template>
-<script>
-export default {
-  methods: {
-    isLeapYear () {
-      const year = new Date().getFullYear()
-      if (year % 400 === 0) {
-        return true
-      } else if (year % 4 === 0 && year % 100 !== 0) {
-        return true
-      } else {
-        return false
-      }
-    },
-    getDayOfYear () {
-      return this.isLeapYear() ? 366 : 365
-    }
-  },
-  computed: {
-    year () {
-      return new Date().getFullYear()
-    },
-    days () {
-      let start = new Date()
-      start.setMonth(0)
-      start.setDate(1)
-      // start 就是今年第一天
-      // 今年时间戳 - 今年第一天时间戳
-      let offset = new Date().getTime() - start.getTime()
-      return parseInt(offset / 1000 / 60 / 60 / 24) + 1
-    },
-    percent () {
-      return (this.days * 100 / this.getDayOfYear()).toFixed(1)
-    }
-  }
-}
-</script>
-<style lang='scss'>
-.progressbar{
-  text-align: center;
-  margin-top: 10px;
-  margin-bottom: 40px;
-  width: 100%;
-  progress {
-    margin-bottom: 10px;
-  }
-}
-</style>

+ 104 - 0
src/components/list-item.vue

@@ -0,0 +1,104 @@
+<template>
+  <div v-if="itemData" class="item-container" @click="handleToPage(itemData.info_id)">
+    <div class="item-title">{{itemData.title}}</div>
+    <div class="item-pic-wrap" v-if="itemData.img_list.length > 1 ">
+      <div class="item-pic-item" v-for="(url, idx) in itemData.img_list" :key="idx">
+        <div class="pic-item" v-if="idx < 3">
+          <img mode="widthFix" :src="!!url ? url : defImage " alt="" >
+        </div>
+      </div>
+    </div>
+    <div class="item-media">
+      <span class="media-name">{{itemData.media_info.media_name}}</span>
+      <span class="media-tip" @click="todrawEvent"></span>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  props: ['itemData'],
+  data () {
+    return {
+      defImage: '/static/images/grey.gif'
+    }
+  },
+  methods: {
+    todrawEvent () {
+      wx.navigateTo({
+        url: '/pages/banner/main'
+      })
+    },
+    handleToPage (infoId) {
+      let goldCoins = wx.getStorageSync('userInfo').gold_coins
+      if (goldCoins < 10) {
+        wx.showToast({
+          title: `很遗憾, 阅读至少需要10金币`,
+          icon: 'none',
+          duration: 2000
+        })
+      } else {
+        wx.navigateTo({
+          url: `/pages/detail/main?info_id=${infoId}`
+        })
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+  .item-container {
+    &:last-child {
+      margin-bottom: 60px;
+    }
+    border-bottom: 1px solid #ebebeb;
+    padding: 10px 0;
+    margin: 0 15px;
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    justify-content: space-around;
+    .item-title {
+      font-size: 18px;
+      color: #222;
+    }
+    .item-pic-wrap {
+      width: 100%;
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      justify-content: space-between;
+      .item-pic-item { 
+        display: inline-block;
+        width: 32%;
+        .pic-item {
+          margin-top: 10px;
+          max-height: 80px;
+          overflow: hidden;
+          img {
+            border: none;
+            width: 100%;
+            height: 100%
+          }
+        }
+      }
+    }
+    .item-media {
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      font-size: 14px;
+      margin-bottom: -5px;
+      .media-name {
+        color: #9b9b9b;
+        visibility: hidden;
+      } 
+      .media-tip {
+        color: #fccf85;
+      }
+
+    }
+  }
+</style>
+
+

+ 191 - 0
src/components/list-wrap.vue

@@ -0,0 +1,191 @@
+<template>
+  <div class="list-wrap scroll-touch">
+    <wsh-list-item  
+      v-for="(item, index) in newsLists" 
+      :key = 'index' :itemData = 'item' />
+    <!-- <div class="list-wrap-ad">
+      <ad unit-id="adunit-7496c51cbc82905d"></ad>
+    </div> -->
+    <div class="list-footer">
+      <p v-if="moreStatus"> 没有更多数据</p>
+    </div>
+  </div>
+</template>
+
+<script>
+import {get} from '@/utils/request'
+
+import ListItem from './list-item'
+export default {
+  props: ['channelId'],
+  data () {
+    return {
+      tips: '请稍后',
+      show: true,
+      animated: true,
+      newsLists: [],
+      page: 1,
+      limit: 40,
+      moreStatus: false,
+      interstitialAd: null,
+      rewardedVideoAd: null,
+      onlyPlay: true
+    }
+  },
+  onLoad () {
+    // 加载视频广告
+    if (wx.createRewardedVideoAd) {
+      this.rewardedVideoAd = wx.createRewardedVideoAd({
+        adUnitId: 'adunit-de36057cf600716b'
+      })
+      this.rewardedVideoAd.onLoad(() => {
+        console.log('onLoad event emit')
+      })
+      this.rewardedVideoAd.onError((err) => {
+        console.log('onError event emit', err)
+      })
+      this.rewardedVideoAd.onClose((res) => {
+        console.log('onClose event emit', res)
+      })
+    }
+    // 加载插屏广告
+    if (wx.createInterstitialAd) {
+      this.interstitialAd = wx.createInterstitialAd({ adUnitId: 'adunit-5ecedc2dc28ef414' })
+      this.interstitialAd.onLoad(() => {
+        console.log('onLoad event emit')
+        console.log('插屏 广告加载成功')
+      })
+      this.interstitialAd.onError((err) => {
+        console.log('onError event emit', err)
+      })
+      this.interstitialAd.onClose((res) => {
+        console.log('onClose event emit', res)
+      })
+    }
+  },
+  components: {
+    'wsh-list-item': ListItem
+  },
+  methods: {
+    async getListDate (init) {
+      if (init) {
+        this.page = 1
+        this.moreStatus = false
+      }
+      this.page++
+      // wx.showNavigationBarLoading()
+      const params = {
+        page: this.page,
+        limit: this.limit,
+        channel_id: this.channelId,
+      }
+      // if (this.page !== 1) {
+      //   wx.showToast({
+      //     title: '加载中...',
+      //     mask: true,
+      //     icon: 'loading'
+      //   })
+      // }
+      console.log('params', params)
+      const newsData = await get('/v1/info/web-list', params)
+      console.log('newDate', newsData)
+      if (newsData.list.length < this.limit & this.page > 1) {
+        this.moreStatus = true
+      }
+      if (init) {
+        wx.stopPullDownRefresh() // 手动停止下拉刷新
+        this.newsLists = newsData.list
+      } else {
+        wx.hideNavigationBarLoading() // 关闭下拉刷新加载
+        if (newsData.list) {
+          this.newsLists = this.newsLists.concat(newsData.list)
+          this.onlyPlay = true
+        } else {
+        }
+      }
+    }
+  },
+  onPullDownRefresh () {
+    wx.stopPullDownRefresh();
+    // console.log('观看视频获取最新资讯')
+    var that = this
+    wx.showModal({
+      title: '提示',
+      content: '观看视频获取最新资讯',
+      success (res) {
+        if (res.confirm) {
+          // console.log('用户点击确定')
+          that.rewardedVideoAd.show()
+            .then(() => console.log('激励视频 广告显示'))
+            .catch(() => {
+              that.rewardedVideoAd.load()
+                .then(() => that.rewardedVideoAd.show())
+                .catch(err => {
+                  console.log('激励视频 广告显示失败', err)
+                })
+            })
+          that.rewardedVideoAd.onClose(res => {
+            // 用户点击了【关闭广告】按钮
+            if (res && res.isEnded) {
+              // 正常播放结束,可以下发游戏奖励
+              wx.showToast({
+                title: '更多最新资讯尽请期待',
+                icon: 'none',
+                duration: 2000
+              });
+            } else {
+              // 播放中途退出,不下发游戏奖励
+            }
+          })
+        } else if (res.cancel) {
+          // console.log('用户点击取消')
+        }
+      }
+    })
+    // wx.startPullDownRefresh()
+  },
+  onReachBottom () {
+    // if (!this.moreStatus) {
+    //   // 没有更多了
+    //   return false
+    // }
+    this.interstitialAd.show().catch((err) => {
+      console.error(err)
+    })
+    this.interstitialAd.onClose(res => {
+      console.log('插屏 广告关闭')
+    })
+    if (this.onlyPlay) {
+      this.onlyPlay = false
+      this.getListDate()
+    }
+    // console.log('上拉加载', this.page)
+  },
+  mounted () {
+    this.getListDate(true)
+  },
+  created () {
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.scroll-touch {
+	-webkit-overflow-scrolling: touch;
+}
+.list-wrap {
+  //  border-top: 1px solid #ebebeb;
+  height: 100%;
+  .list-wrap-ad {
+    width: 100%;
+  }
+}
+.list-footer {
+  display: none;
+  width: 100%;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+}
+</style>
+

+ 0 - 10
src/config.js

@@ -1,10 +0,0 @@
-// 配置项
-
-const host = 'http://localhost:5757'
-
-const config = {
-  host,
-  loginUrl: `${host}/weapp/login`,
-  userUrl: `${host}/weapp/user`
-}
-export default config

+ 4 - 36
src/main.js

@@ -1,42 +1,10 @@
 import Vue from 'vue'
 import App from './App'
+import store from '@/store/index'
 
+Vue.prototype.$store = store
 Vue.config.productionTip = false
+App.mpType = 'app'
+
 const app = new Vue(App)
 app.$mount()
-
-export default{
-  config: {
-    pages: ['^pages/books/main'],
-    'window': {
-      'backgroundTextStyle': 'light',
-      'navigationBarBackgroundColor': '#EA5149',
-      'navigationBarTitleText': '蜗牛图书',
-      'navigationBarTextStyle': 'light'
-    },
-    'tabBar': {
-      selectedColor: '#EA5149',
-      list: [
-        {
-          pagePath: 'pages/books/main',
-          text: '图书',
-          iconPath: 'static/img/book.png',
-          selectedIconPath: 'static/img/book-active.png'
-        },
-        {
-          pagePath: 'pages/comments/main',
-          text: '评论',
-          iconPath: 'static/img/todo.png',
-          selectedIconPath: 'static/img/todo-active.png'
-        },
-        {
-          pagePath: 'pages/me/main',
-          text: '我',
-          iconPath: 'static/img/me.png',
-          selectedIconPath: 'static/img/me-active.png'
-        }
-
-      ]
-    }
-  }
-}

File diff suppressed because it is too large
+ 939 - 0
src/pages/banner/index.vue


+ 12 - 0
src/pages/banner/main.js

@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import Banner from './index'
+
+// add this to handle exception
+Vue.config.errorHandler = function (err) {
+  if (console && console.error) {
+    console.error(err)
+  }
+}
+
+const app = new Vue(Banner)
+app.$mount()

+ 3 - 0
src/pages/banner/main.json

@@ -0,0 +1,3 @@
+{
+  "enablePullDownRefresh": false
+}

+ 0 - 89
src/pages/books/Book.vue

@@ -1,89 +0,0 @@
-<template>
-  <div>
-    <TopSwiper :tops='tops'></TopSwiper>
-    <!-- <p>图书列表</p> -->
-    <Card v-for="book in books" :key="book.openid" :book='book'></Card>
-    <p class="text-footer" v-if="!more">
-      没有更多数据
-    </p>
-  </div>
-</template>
-<script>
-// 35条数据
-// 每次加载10条
-// 0页   0-10
-// 1     10-20
-// 2     20-30(5)
-// page 当前第几页
-
-// 没有更多数据
-// 1. page=0 不能显示这条提醒
-// 2. page>0 数据长度<10 停止触底加载
-
-import {get} from '@/util'
-import Card from '@/components/Card'
-import TopSwiper from '@/components/TopSwiper'
-export default {
-  components: {
-    Card,
-    TopSwiper
-  },
-  data () {
-    return {
-      books: [],
-      page: 0,
-      more: true,
-      tops: []
-    }
-  },
-  methods: {
-    async getList (init) {
-      if (init) {
-        this.page = 0
-        this.more = true
-      }
-      wx.showNavigationBarLoading()
-      const books = await get('/weapp/booklist', {page: this.page})
-      if (books.list.length < 10 && this.page > 0) {
-        this.more = false // 这种情况下 没有更多数据了
-        // console.log(this.more);
-      }
-      if (init) {
-        this.books = books.list
-        wx.stopPullDownRefresh() // 手动停止下拉刷新
-      } else {
-        // 下拉刷新,不能直接覆盖books 而是累加
-        this.books = this.books.concat(books.list)
-      }
-      wx.hideNavigationBarLoading() // 关闭下拉刷新加载
-    },
-    async getTop () {
-      const tops = await get('/weapp/top')
-      this.tops = tops.list
-    }
-  },
-  onPullDownRefresh () {
-    this.getList(true)
-    // console.log('下拉');
-    this.getTop()
-  },
-  onReachBottom () {
-    // console.log('没有更多了')
-    if (!this.more) {
-      // 没有更多了
-      return false
-    }
-    this.page++
-    // console.log('上啦加载', this.page)
-    this.getList()
-  },
-  mounted () {
-    this.getList(true)
-    this.getTop()
-  }
-}
-</script>
-<style lang='scss'>
-
-
-</style>

+ 0 - 12
src/pages/books/main.js

@@ -1,12 +0,0 @@
-import Vue from 'vue'
-import Book from './Book'
-
-const app = new Vue(Book)
-app.$mount()
-
-export default{
-  config: {
-    // 是否全局开启下拉刷新。
-    enablePullDownRefresh: true
-  }
-}

+ 0 - 69
src/pages/comments/Comment.vue

@@ -1,69 +0,0 @@
-<template>
-<div class="container">
-  <CommentList v-if="userinfo.openId"
-      type="user"
-      :comments="comments"></CommentList>
-  <div v-if='userinfo.openId'>
-    <div class="page-title">我的图书</div>
-    <Card v-for="book in books"
-          :key="book.id"
-          :book="book"></Card>
-    <div  v-if='!books.length'>暂时还没添加过书,快去添加几本把</div>
-  </div>
-</div>
-</template>
-<script>
-import {get} from '@/util'
-import CommentList from '@/components/CommentList'
-import Card from '@/components/Card'
-export default {
-  data () {
-    return {
-      comments: [],
-      books: [],
-      userinfo: {}
-    }
-  },
-  components: {
-    CommentList,
-    Card
-  },
-  methods: {
-    init () {
-      wx.showNavigationBarLoading()
-      this.getComments()
-      this.getBooks()
-      wx.hideNavigationBarLoading()
-    },
-    async getBooks () {
-      const books = await get('/weapp/booklist', {
-        openid: this.userinfo.openId
-      })
-      this.books = books.list
-    },
-    async getComments () {
-      const comments = await get('/weapp/commentlist', {
-        openid: this.userinfo.openId
-      })
-      this.comments = comments.list
-    }
-  },
-  onPullDownRefresh () {
-    this.init()
-    wx.stopPullDownRefresh()
-  },
-  onShow () {
-    if (!this.userinfo.openId) {
-      let userinfo = wx.getStorageSync('userInfo')
-      if (userinfo) {
-        this.userinfo = userinfo
-        this.init()
-      }
-    }
-  }
-}
-</script>
-<style>
-
-
-</style>

+ 0 - 12
src/pages/comments/main.js

@@ -1,12 +0,0 @@
-import Vue from 'vue'
-import Comment from './Comment'
-
-const app = new Vue(Comment)
-app.$mount()
-
-export default{
-  config: {
-    navigationBarTitleText: '评论列表',
-    enablePullDownRefresh: true
-  }
-}

+ 0 - 194
src/pages/detail/Detail.vue

@@ -1,194 +0,0 @@
-<template>
-  <div>
-    <!-- 图书id{{bookid}} -->
-    <BookInfo :info='info'></BookInfo>
-    <CommentList :comments="comments"></CommentList>
-    <div class="comment" v-if="showAdd">
-      <textarea v-model="comment"
-                class="textarea"
-                :maxlength="100"
-                placeholder="请输入图书短评"></textarea>
-      <div class="location">
-        地理位置:
-         <switch color="#EA5A49" @change="getGeo" :checked="location" />
-         <span class="text-primary">{{location}}</span>
-      </div>
-      <div class="phone">
-        手机型号:
-         <switch color="#EA5A49" @change="getPhone" :checked="phone"/>
-         <span class="text-primary">{{phone}}</span>
-      </div>
-      <button class="btn" @click="addComment">评论</button>
-    </div>
-    <div v-else class="text-footer">
-      未登录或者已经评论过啦
-    </div>
-    <button open-type='share' class="btn">转发给好友</button>
-  </div>
-</template>
-<script>
-import {get, post, showModal} from '@/util'
-import BookInfo from '@/components/BookInfo'
-import CommentList from '@/components/CommentList'
-export default {
-  components: {
-    BookInfo,
-    CommentList
-  },
-  data () {
-    return {
-      comments: [],
-      userinfo: {},
-      bookid: '',
-      info: {},
-      comment: '',
-      location: '',
-      phone: ''
-    }
-  },
-  computed: {
-    showAdd () {
-      // 未登录
-      if (!this.userinfo.openId) {
-        return false
-      }
-      // 评论页面里查到有自己的openid
-      if (this.comments.filter(v => v.openid === this.userinfo.openId).length) {
-        return false
-      }
-      return true
-    }
-  },
-  methods: {
-    async addComment () {
-      if (!this.comment) {
-        return
-      }
-      // 评论内容 手机型号 地理位置 图书id 用户openid
-      const data = {
-        openid: this.userinfo.openId,
-        bookid: this.bookid,
-        comment: this.comment,
-        phone: this.phone,
-        location: this.location
-      }
-      try {
-        console.log(data)
-        await post('/weapp/addcomment', data)
-        this.comment = ''
-        this.getComments()
-      } catch (e) {
-        showModal('失败', e.msg)
-      }
-    },
-    getGeo (e) {
-      // NVYWM4sZYGjaHPpO6vwVk0j1v4ijBBKB
-      const ak = 'NVYWM4sZYGjaHPpO6vwVk0j1v4ijBBKB'
-      let url = 'http://api.map.baidu.com/geocoder/v2/'
-
-      if (e.target.value) {
-        wx.getLocation({
-          type: 'wgs84',
-          success: geo => {
-            console.log('地理数据', geo)
-            wx.request({
-              url,
-              data: {
-                ak,
-                location: `${geo.latitude},${geo.longitude}`,
-                output: 'json'
-              },
-              success: res => {
-                console.log('百度api返回', res)
-                if (res.data.status === 0) {
-                  this.location = res.data.result.addressComponent.city
-                } else {
-                  this.location = '未知地点'
-                }
-              }
-            })
-          }
-        })
-      } else {
-        this.location = ''
-      }
-    },
-    getPhone (e) {
-      if (e.target.value) {
-        const res = wx.getSystemInfoSync()
-        // console.log(res.model);
-        this.phone = res.model
-      } else {
-        this.phone = ''
-      }
-    },
-    async getDetail () {
-      const info = await get('/weapp/bookdetail', {id: this.bookid})
-      wx.setNavigationBarTitle({
-        title: info.title
-      })
-      console.log(info)
-      this.info = info
-    },
-    async getComments () {
-      const comments = await get('/weapp/commentlist', {bookid: this.bookid})
-      console.log('comments', comments)
-      this.comments = comments.list || []
-    }
-  },
-  onShareAppMessage: function (res) {
-    return {
-      title: this.info.title,
-      path: 'pages/detail/main',
-      imageUrl: '' // 不填写默认页面截图
-      // success: function (shareTickets) {
-      //   // console.info(shareTickets + '成功');
-      //   console.log(shareTickets + '成功');
-      //   // 转发成功
-      // },
-      // fail: function (res) {
-      //   console.log(res + '失败');
-      //   // 转发失败
-      // },
-      // complete:function(res){
-      //   console.log(res + '不管成功失败都会执行');
-      //   // 不管成功失败都会执行
-      // }
-    }
-  },
-  mounted () {
-    this.bookid = this.$root.$mp.query.id // 跳转页面使用this.$root.$mp.query获取参数
-    this.getDetail()
-    this.getComments()
-    const userinfo = wx.getStorageSync('userInfo')
-    console.log('userInfo', userinfo)
-    if (userinfo) {
-      this.userinfo = userinfo
-    }
-    // wx.showShareMenu({
-    //   withShareTicket: true
-    // });
-  }
-}
-</script>
-<style lang="scss">
-.comment {
-  margin-top: 10px;
-  .textarea {
-    width: 730rpx;
-    background-color: #eee;
-    padding: 10px;
-  }
-  .location {
-    margin-top: 10px;
-    padding: 5px 10px;
-  }
-  .phone {
-    margin-top: 10px;
-    padding: 5px 10px;
-  }
-  .btn {
-     margin: 10px 0;
-  }
-}
-</style>

+ 251 - 0
src/pages/detail/index.vue

@@ -0,0 +1,251 @@
+<template>
+  <div class="detail-container scroll-touch">
+    <div class="detail-ad-wrap">
+      <ad unit-id="adunit-21265a59493c9c9b"></ad>
+    </div>
+    <!-- {{info_id}} -->
+    <div v-if="detailHtml" :class="[ contentDisplayAll ? 'detail-context-all' : 'detail-context' ]">
+      <h2 class="new-title">{{newsDetail.title}}</h2>
+      <div v-html="detailHtml"></div>
+    </div>
+    <div class="click-display-all" 
+      v-if="detailHtml && !contentDisplayAll"
+      @click="handleClickAll">
+      查看全文
+      <span class="caret"></span>
+    </div>
+     <!-- 相关推荐  -->
+    <div class="rise-container" v-if="newsDetail.relation_list">
+      <h4 class="rise-name">相关推荐</h4>
+      <div class="relation-item" 
+        v-for="(item, index) in newsDetail.relation_list" 
+        @click="handleToPage(item.info_id)"
+        :key= "index">
+        <div class="item-title">{{item.title}}</div>
+        <div class="item-pic-wrap">
+          <div class="item-pic-item" v-for="(url, idx ) in item.img_list" :key = "idx">
+            <img :src="url" alt="">
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import {get} from '@/utils/request'
+import config from '@/utils/config'
+
+export default {
+  onLoad: function (options) {
+    this.info_id = options.info_id
+    // var timer = setTimeout(() => {
+    //   this.handleConsume()
+    //   clearTimeout(timer)
+    // }, 5000)
+    if (wx.createRewardedVideoAd) {
+      this.videoAd = wx.createRewardedVideoAd({
+        adUnitId: 'adunit-adf736062e0b8ffa'
+      })
+      this.videoAd.onLoad(() => {})
+      this.videoAd.onError((err) => console.log(err))
+      // this.videoAd.onClose((res) => {})
+    }
+  },
+  // onUnload: function () {
+  //   wx.reLaunch({
+  //     url: '/pages/index/main'
+  //   })
+  // },
+  data () {
+    return {
+      info_id: '',
+      newsDetail: {},
+      detailHtml: '',
+      contentDisplayAll: false,
+      ptTime: {
+        hr: 0,
+        min: 0
+      },
+      videoAd: null
+    }
+  },
+  methods: {
+    async handleConsume () {
+      await wx.request({
+        method: 'post',
+        url: `${config.host}/mp/common/read-coin`,
+        data: {
+          appid: config.appId,
+          mp_token: wx.getStorageSync('mpToken'),
+          user_id: wx.getStorageSync('userInfo').user_id,
+          info_id: this.info_id
+        },
+        success: res => {
+          if (res.data.code === 0) {
+            wx.showToast({
+              title: `扣除10枚金币`,
+              icon: 'none',
+              duration: 2000
+            })
+          }
+        }
+      })
+    },
+    handleClickAll () {
+      const self = this
+      wx.showModal({
+        title: '友情提示',
+        content: '观看完视频查看',
+        success (res) {
+          if (res.confirm) {
+            // console.log('用户点击确定')
+            self.videoAd.show().catch(() => {
+              self.videoAd.load()
+                .then(() => self.videoAd.show())
+                .catch(err => {
+                  console.log('激励视频 广告显示失败', err)
+                })
+            })
+          } else if (res.cancel) {
+            // console.log('用户点击取消')
+          }
+        }
+      })
+      this.videoAd.onClose(res => {
+        // 用户点击了【关闭广告】按钮
+        if (res && res.isEnded) {
+          // 正常播放结束,可以下发游戏奖励
+          this.contentDisplayAll = true
+        } else {
+          // 播放中途退出,不下发游戏奖励
+        }
+      })
+    },
+    handleToPage (infoId) {
+      wx.navigateTo({
+        url: `/pages/detail/main?info_id=${infoId}`
+      })
+    },
+    async getNewsDetail () {
+      const params = {
+        sid: 'aad1ad2da28cadec36d3c9bec53ca609',
+        info_id: this.info_id,
+        // callback:jQuery331017495393845717588_1564626981414,
+        _: 1564626981418
+      }
+      let detailData = await get('/v1/info/web-detail', params)
+      this.newsDetail = detailData
+      this.detailHtml = detailData.content.replace(/<img/gi, '<img style="max-width:100%;height:auto" ')
+    }
+  },
+  mounted () {
+    this.contentDisplayAll = false
+    this.detailHtml = ''
+    this.newsDetail = {}
+    this.getNewsDetail()
+  },
+  onPullDownRefresh () {
+    wx.stopPullDownRefresh();
+  }
+}
+</script>
+<style lang="scss" scoped>
+  .scroll-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+  .detail-container {
+    margin: 0 auto 14px;
+    display: flex;
+    width: 100%;
+    flex-direction: column;
+    align-items: center;
+    justify-content: flex-start;
+    .detail-context {
+      max-height: 1000px;
+      overflow-y: hidden;
+      padding: 0 10px;
+    }
+    .detail-context-all {
+      padding: 0 10px;
+    }
+    .new-title {
+      font-size: 22px;
+      font-weight: 560;
+      margin-bottom: 10px;
+    }
+  }
+  .rise-container {
+    padding: 0 10px;
+    .rise-name {
+      color: #999;
+      font-size: 18px;
+      margin-top: 10px;
+    }
+    .relation-item {
+      border-bottom: 1px solid #eae8e8;
+      padding: 10px 0;
+      .item-title {
+        font-size: 16px;
+      }
+      .item-pic-wrap {
+        margin-top: 10px;
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .item-pic-item {
+          display: inline-block;
+          width: 32%;
+          height: 66px;
+          img {
+            width: 100%;
+            height: 100%
+          }
+        }
+      }
+    }
+  }
+
+</style>
+<style lang="scss">
+.detail-context {
+  .album-item {
+    width: 100%;
+    padding: 10px;
+    .a-image {
+      display: inline-block;
+      width: 100%;
+      img {
+        width: 100%;
+      }
+    }
+  }
+}
+.click-display-all {
+  height: 32px;
+  line-height: 32px;
+  text-align: center;
+  width: 100%;
+  font-size: 14px;
+  color: #ff6700;
+  background-color: #fff;
+  font-weight: 800;
+  .caret {
+    display: inline-block;
+    width: 0;
+    height: 0;
+    margin-left: 2px;
+    vertical-align: middle;
+    border-top: 4px dashed;
+    border-top: 4px solid\9;
+    border-right: 4px solid transparent;
+    border-left: 4px solid transparent;
+  }
+}
+.detail-ad-wrap {
+  width: 100%;
+}
+</style>
+
+
+

+ 8 - 1
src/pages/detail/main.js

@@ -1,5 +1,12 @@
 import Vue from 'vue'
-import Detail from './Detail'
+import Detail from './index'
+
+// add this to handle exception
+Vue.config.errorHandler = function (err) {
+  if (console && console.error) {
+    console.error(err)
+  }
+}
 
 const app = new Vue(Detail)
 app.$mount()

+ 3 - 0
src/pages/detail/main.json

@@ -0,0 +1,3 @@
+{
+  "enablePullDownRefresh": false
+}

+ 265 - 0
src/pages/index/index.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="scroll-touch">
+    <scroll-view
+      :scroll-left="scrollleft"
+      id="fixd"
+      :class="['section-area','fix']"
+      scroll-x="true"
+      style="width: 100%;white-space:nowrap;background-color:#fff; border-bottom: 1px solid #ebebeb; box-sizing: border-box;"
+    >
+      <div class="nav">
+        <div
+          :id="'navitem' +index"
+          @click="tabClick(index)"
+          v-for="(item,index) in nav"
+          :key="index"
+          class="nav_item"
+          :class="activeIndex == index? 'active':''"
+        >{{item.name}}</div>
+      </div>
+    </scroll-view>
+    <swiper
+      :style="['height:100vh']"
+      class="swiper"
+      duration="200"
+      :current="activeIndex"
+      @change="swiperChange"
+    >
+      <div v-for="(swi,s) in nav" :key="s">
+        <swiper-item>
+          <div class="content scroll-touch" v-if="activeIndex == s">
+            <!-- {{swi.name}} -->
+            <div class="content-ad">
+              <!-- banner广告 -->
+              <ad unit-id="adunit-7496c51cbc82905d" ad-intervals="30"></ad>
+            </div>
+            <wsh-list-wrap :channelId="swi.channel_id" />
+          </div>
+        </swiper-item>
+      </div>
+    </swiper>
+    <!-- <div class="clude-fixed" @click="cludeHandle" v-if="cludeShow">赚金币</div> -->
+  </div>
+</template>
+<script>
+import {get} from '@/utils/request'
+import ListWrap from '@/components/list-wrap'
+import config from '@/utils/config'
+export default {
+  components: {
+    'wsh-list-wrap': ListWrap
+  },
+  data () {
+    return {
+      nav: [],
+      top: 0, // 导航栏初始到屏幕顶部高度
+      activeIndex: 0, // 选项卡及swiper位置
+      scrollleft: 0, // scroll-view 横向滚动条位置
+      windowWidth: 0, // 窗口宽度
+      swiperHeight: 2000, // 此处为swiper高度,
+      cludeShow: true,
+      scrollTimer: '',
+      homeAd: null
+    }
+  },
+  created () {
+    this.login()
+  },
+  methods: {
+    async getUserInfo () {
+      await wx.request({
+        method: 'post',
+        url: config.userUrl,
+        data: {
+          appid: config.appId,
+          mp_token: wx.getStorageSync('mpToken'),
+          user_id: wx.getStorageSync('userInfo').user_id
+        },
+        success: res => {
+          console.log(res)
+          if (res.data.code === 0) {
+            wx.setStorageSync('userInfo', res.data.data.user_info)
+            this.userInfo = wx.getStorageSync('userInfo')
+          } else {
+            wx.clearStorage()
+          }
+        }
+      })
+    },
+    login () {
+      let user = wx.getStorageSync('userInfo')
+      if (!user) {
+        var that = this
+        wx.login({
+          success (res) {
+            if (res.code) {
+              console.log({
+                'res': res,
+                'config': config.appId
+              })
+              // 这里可以把code传给后台,后台用此获取openid及session_key
+              that.loginHandle(res.code)
+            }
+          }
+        })
+      } else {
+        this.getUserInfo()
+      }
+    },
+    loginHandle (code) {
+      // console.log(config.loginUrl)
+      wx.request({
+        method: 'post',
+        url: config.loginUrl,
+        data: {
+          code: code,
+          appid: config.appId
+        },
+        success: res => {
+          // console.log(res)
+          if (res.data.code === 0) {
+            wx.setStorageSync('mpToken', res.data.data.mp_token)
+            wx.setStorageSync('sessionKey', res.data.data.session_key)
+            wx.setStorageSync('userInfo', res.data.data.user_info)
+            this.userInfo = wx.getStorageSync('userInfo')
+          } else {
+            wx.clearStorage()
+          }
+        }
+      })
+    },
+    cludeHandle () {
+      wx.navigateTo({
+        url: '/pages/banner/main'
+      })
+    },
+    async getCategoryNav () {
+      const params = {
+        key_name: 'channels-huanlian'
+      }
+      let navData = await get('/v1/common/web-config', params)
+      this.nav = navData.list
+    },
+    tabClick (e) {
+      this.activeIndex = e
+    },
+    swiperChange (e) {
+      this.activeIndex = e.mp.detail.current
+      let that = this
+      const query = wx.createSelectorQuery()
+      query.select('#navitem' + this.activeIndex).boundingClientRect()
+      query.exec(function (res) {
+        if (res[0].right > that.windowWidth) {
+          that.scrollleft = res[0].right
+        } else if (res[0].left < 0) {
+          that.scrollleft = res[0].left
+        }
+      })
+    }
+  },
+  mounted () {
+    this.getCategoryNav()
+  },
+  onLoad () {
+    let that = this
+    let res = wx.getSystemInfoSync()
+    that.windowWidth = res.windowWidth
+    const query = wx.createSelectorQuery()
+    query.select('#fixd', '.nav').boundingClientRect()
+    query.exec(function (res) {
+      that.top = res[0].top
+    })
+    if (wx.createInterstitialAd) {
+      this.homeAd = wx.createInterstitialAd({
+        adUnitId: 'adunit-5ecedc2dc28ef414'
+      })
+      this.homeAd.onLoad(() => {
+        console.log('插屏 广告加载成功')
+      })
+      this.homeAd.onError((err) => {
+        console.log('onError event emit', err)
+      })
+      // this.homeAd.onClose(() => {})
+    }
+    if (this.homeAd) {
+      const self = this
+      setTimeout(() => {
+        self.homeAd.show().catch((err) => {
+          console.error('errer', err)
+        })
+      }, 10000)
+    }
+  },
+  onPageScroll (e) {
+  }
+}
+</script>
+<style lang="scss" scoped>
+// .section-area ::-webkit-scrollbar {  
+//     width: 0;  
+//     height: 0;  
+//     color: transparent;
+// } 
+.scroll-touch {
+	-webkit-overflow-scrolling: touch;
+}
+.nav {
+  width: 100%;
+  display: flex;
+  height: 50px;
+  background-color: #fff;
+  box-sizing: border-box;
+  .nav_item {
+    flex: auto;
+    text-align: center;
+    line-height: 50px;
+    padding: 0 10px;
+  }
+  .active {
+    color: #f34b50;
+  }
+}
+.content {
+  background-color: #fff;
+  overflow: hidden;
+  height: 100%;
+  overflow-y: scroll;
+  .content-ad {
+    width: 100%;
+  }
+}
+.fix {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  z-index: 10;
+  // animation: move 0.1s linear;
+}
+.swiper {
+  position: relative;
+  top: 50px;
+  left: 0;
+}
+@keyframes move {
+  from {
+    opacity: 0.7;
+  }
+  to {
+    opacity: 1;
+  }
+}
+.clude-fixed {
+  position: fixed;
+  z-index: 999;
+  bottom: 17%;
+  right: 2%;
+  width: 50px;
+  height: 50px;
+  border-radius: 50%;
+  background: yellow;
+  font-size: 13px;
+  text-align: center;
+  line-height: 50px;
+}
+</style>

+ 12 - 0
src/pages/index/main.js

@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import App from './index'
+
+// add this to handle exception
+Vue.config.errorHandler = function (err) {
+  if (console && console.error) {
+    console.error(err)
+  }
+}
+
+const app = new Vue(App)
+app.$mount()

+ 3 - 0
src/pages/index/main.json

@@ -0,0 +1,3 @@
+{
+  "enablePullDownRefresh": true
+}

+ 61 - 0
src/pages/logs/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div>
+      <swiper v-if="imgUrls.length > 0" indidator-dots="imgUrls.length > 1" >
+      <block v-for="(item, index) in imgUrls" :key="index" >
+        <swiper-item>
+          <image :src="item" mode="scaleToFill"></image>
+        </swiper-item>
+      </block>
+    </swiper>
+
+    <ul class="container log-list">
+      <li v-for="(log, index) in logs" :class="{ red: aa }" :key="index" class="log-item">
+        <card :text="(index + 1) + ' . ' + log"></card>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+import { formatTime } from '@/utils/index'
+import card from '@/components/card'
+
+export default {
+  components: {
+    card
+  },
+
+  data () {
+    return {
+      logs: [],
+      imgUrls: [
+        'http://mss.sankuai.com/v1/mss_51a7233366a4427fa6132a6ce72dbe54/newsPicture/05558951-de60-49fb-b674-dd906c8897a6',
+        'http://mss.sankuai.com/v1/mss_51a7233366a4427fa6132a6ce72dbe54/coursePicture/0fbcfdf7-0040-4692-8f84-78bb21f3395d',
+        'http://mss.sankuai.com/v1/mss_51a7233366a4427fa6132a6ce72dbe54/management-school-picture/7683b32e-4e44-4b2f-9c03-c21f34320870'
+      ]
+    }
+  },
+
+  created () {
+    let logs
+    if (mpvuePlatform === 'my') {
+      logs = mpvue.getStorageSync({key: 'logs'}).data || []
+    } else {
+      logs = mpvue.getStorageSync('logs') || []
+    }
+    this.logs = logs.map(log => formatTime(new Date(log)))
+  }
+}
+</script>
+
+<style>
+.log-list {
+  display: flex;
+  flex-direction: column;
+  padding: 40rpx;
+}
+
+.log-item {
+  margin: 10rpx;
+}
+</style>

+ 5 - 0
src/pages/logs/main.js

@@ -0,0 +1,5 @@
+import Vue from 'vue'
+import App from './index'
+
+const app = new Vue(App)
+app.$mount()

+ 3 - 0
src/pages/logs/main.json

@@ -0,0 +1,3 @@
+{
+  "navigationBarTitleText": "查看启动日志"
+}

+ 0 - 105
src/pages/me/Me.vue

@@ -1,105 +0,0 @@
-<template>
-  <div class="container">
-    <button  v-if='!userInfo.openId'  open-type="getUserInfo" @click="login">登录</button>
-    <div class="userinfo" >
-      <img :src="userInfo.avatarUrl" alt="">
-      <p>{{userInfo.nickName}}</p>
-    </div>
-    <YearProgress></YearProgress>
-    <button v-if='userInfo.openId' @click="scanBook" class='btn'>添加图书</button>
-  </div>
-</template>
-
-<script>
-import {showSuccess, post, showModal} from '@/util'
-import config from '@/config'
-import qcloud from 'wafer2-client-sdk'
-import YearProgress from '@/components/YearProgress'
-export default {
-  components: {
-    YearProgress
-  },
-  data () {
-    return {
-      userInfo: {
-        avatarUrl: require('../../../static/img/unlogin.png'),
-        nickName: '未登录'
-      }
-    }
-  },
-  created () {
-    this.onShow()
-  },
-  methods: {
-    async addBook (isbn) {
-      const res = await post('/weapp/addbook', {
-        isbn,
-        openid: this.userInfo.openId
-      })
-      console.log(res)
-      showModal('添加成功', `${res.title}添加成功`)
-    },
-    scanBook () {
-      wx.scanCode({
-        success: (res) => {
-          if (res.result) {
-            console.log(res.result)
-            this.addBook(res.result)
-          }
-        }
-      })
-    },
-    login () {
-      let user = wx.getStorageSync('userInfo')
-      if (!user) {
-        qcloud.setLoginUrl(config.loginUrl)
-        qcloud.login({
-          success: (userInfo) => {
-            qcloud.request({
-              url: config.userUrl,
-              login: true,
-              success: (userRes) => {
-                showSuccess('登录成功')
-                wx.setStorageSync('userInfo', userRes.data.data)
-                this.userInfo = userRes.data.data
-              }
-            })
-          },
-          fail: function (err) {
-            console.log('登录失败', err)
-          }
-        })
-      }
-    },
-    onShow () {
-      let userInfo = wx.getStorageSync('userInfo')
-      if (userInfo) {
-        this.userInfo = userInfo
-      }
-    }
-  }
-}
-</script>
-
-
-
-
-
-<style lang='scss'>
-.container{
-  padding:0 30rpx;
-}  
-.userinfo{
-  margin-top:100rpx;
-  text-align:center;
-  img{
-    width: 150rpx;
-    height:150rpx;
-    margin: 20rpx;
-    border-radius: 50%;
-  }
-}
-
-
-</style>
-

+ 0 - 5
src/pages/me/main.js

@@ -1,5 +0,0 @@
-import Vue from 'vue'
-import Me from './Me'
-
-const app = new Vue(Me)
-app.$mount()

+ 18 - 0
src/store/index.js

@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+// 第三方插件
+import createLogger from 'vuex/dist/logger'
+// modules
+import nav from './modules/nav'
+
+Vue.use(Vuex)
+
+const debug = process.env.NODE_ENV !== 'production' // 是生产环境还是开发环境
+// 导出 store 对象
+export default new Vuex.Store({
+  modules: {
+    nav
+  },
+  strict: debug,
+  plugins: debug ? [createLogger()] : [] // 在开发环境下,配置日志插件。
+})

+ 21 - 0
src/store/modules/nav.js

@@ -0,0 +1,21 @@
+
+export default {
+  state: {
+    channelId: 'rec'
+  },
+  getters: {
+    channelId (state) {
+      return state.channelId
+    }
+  },
+  mutations: {
+    CHANGE_CHANNEL_ID (state, channelId) {
+      state.channelId = channelId
+    }
+  },
+  actions: {
+    changeChannelId ({ commit }, channelId) {
+      commit('CHANGE_CHANNEL_ID', channelId)
+    }
+  }
+}

+ 11 - 0
src/utils/config.js

@@ -0,0 +1,11 @@
+// 配置项
+const host = process.env.API_ROOT
+const appId = 'wx4305ff37a0879263'
+console.log(host)
+const config = {
+  host,
+  appId,
+  loginUrl: `${host}/mp/auth/login`,
+  userUrl: `${host}/mp/common/user-info`
+}
+export default config

+ 24 - 0
src/utils/index.js

@@ -0,0 +1,24 @@
+function formatNumber (n) {
+  const str = n.toString()
+  return str[1] ? str : `0${str}`
+}
+
+export function formatTime (date) {
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+
+  const hour = date.getHours()
+  const minute = date.getMinutes()
+  const second = date.getSeconds()
+
+  const t1 = [year, month, day].map(formatNumber).join('/')
+  const t2 = [hour, minute, second].map(formatNumber).join(':')
+
+  return `${t1} ${t2}`
+}
+
+export default {
+  formatNumber,
+  formatTime
+}

+ 1 - 1
src/util.js

@@ -20,7 +20,7 @@ function request (url, method, data, header = {}) {
         if (res.data.code === 0) {
           resolve(res.data.data)
         } else {
-          showModal('失败', res.data.data.msg)
+          showModal('失败', res.data.data)
           reject(res.data)
         }
       }

+ 47 - 0
src/utils/storage.js

@@ -0,0 +1,47 @@
+var dtime = '_deadtime'
+function put (k, v, t) {
+  swan.setStorageSync(k, v)
+  var seconds = parseInt(t)
+  if (seconds > 0) {
+    var timestamp = Date.parse(new Date())
+    timestamp = timestamp / 1000 + seconds
+    swan.setStorageSync(k + dtime, timestamp + '')
+  } else {
+    swan.removeStorageSync(k + dtime)
+  }
+}
+
+function get (k, def) {
+  var deadtime = parseInt(swan.getStorageSync(k + dtime))
+  if (deadtime) {
+    if (parseInt(deadtime) < Date.parse(new Date()) / 1000) {
+      if (def) {
+        return def
+      } else {
+        return
+      }
+    }
+  }
+  var res = swan.getStorageSync(k)
+  if (res) {
+    return res
+  } else {
+    return def
+  }
+}
+
+function remove (k) {
+  swan.removeStorageSync(k)
+  swan.removeStorageSync(k + dtime)
+}
+
+function clear () {
+  swan.clearStorageSync()
+}
+const storage = {
+  put: put,
+  get: get,
+  remove: remove,
+  clear: clear
+}
+module.exports = storage

BIN
static/banner/close_store.png


BIN
static/banner/color_pillar.png


BIN
static/banner/coupon.png


BIN
static/banner/draw_btn.png


BIN
static/banner/gold.png


BIN
static/banner/gold01.png


BIN
static/banner/iphone.png


BIN
static/banner/luck_bg.png


BIN
static/banner/lucky_title.png


BIN
static/banner/renminbi.png


BIN
static/banner/sorry.png


BIN
static/banner/wow.png


BIN
static/crop_mango_sound.mp3


BIN
static/images/grey.gif


BIN
static/images/user.png


BIN
static/img/book-active.png


BIN
static/img/book.png


BIN
static/img/me-active.png


BIN
static/img/me.png


BIN
static/img/other-active.png


BIN
static/img/other.png


BIN
static/img/room-active.png


BIN
static/img/room.png


BIN
static/img/todo-active.png


BIN
static/img/todo.png


BIN
static/img/unlogin.png