liyongli %!s(int64=4) %!d(string=hai) anos
achega
79e76bfdff
Modificáronse 100 ficheiros con 6592 adicións e 0 borrados
  1. 2 0
      .browserslistrc
  2. 2 0
      .env.dev
  3. 2 0
      .env.devtest
  4. 2 0
      .env.test
  5. 14 0
      .eslintrc.js
  6. 21 0
      .gitignore
  7. 75 0
      README.md
  8. 3 0
      babel.config.js
  9. 40 0
      package.json
  10. 39 0
      page.config.js
  11. 25 0
      public/index.html
  12. 26 0
      src/api/account/account.js
  13. 25 0
      src/api/activist/activist.js
  14. 144 0
      src/api/article/article.js
  15. 72 0
      src/api/party/party.js
  16. 26 0
      src/api/robot/index.js
  17. 39 0
      src/api/user/user.js
  18. 27 0
      src/api/wechat/api.js
  19. 49 0
      src/assets/css/1px.scss
  20. 25 0
      src/assets/css/reset.scss
  21. 45 0
      src/assets/css/style.scss
  22. BIN=BIN
      src/assets/image/0.jpg
  23. BIN=BIN
      src/assets/image/down.jpg
  24. BIN=BIN
      src/assets/image/partySchool.jpg
  25. 217 0
      src/components/Comment/Comment.vue
  26. 148 0
      src/components/Detail/audio.vue
  27. 127 0
      src/components/Detail/news.vue
  28. 177 0
      src/components/Detail/video.vue
  29. 107 0
      src/components/ListItem/ListItem.vue
  30. 116 0
      src/components/ListItem/ListItemBig.vue
  31. 136 0
      src/components/NewsList/NewsList.vue
  32. 19 0
      src/config/apiUrl.js
  33. 19 0
      src/config/index.js
  34. 154 0
      src/config/page.js
  35. 346 0
      src/pages/JoinParty/Index.vue
  36. 107 0
      src/pages/JoinParty/index.js
  37. 32 0
      src/pages/about/Index.vue
  38. 16 0
      src/pages/about/index.js
  39. 98 0
      src/pages/activistPage/activist/Index.vue
  40. 16 0
      src/pages/activistPage/activist/index.js
  41. 95 0
      src/pages/activistPage/activistAdd/Index.vue
  42. 16 0
      src/pages/activistPage/activistAdd/index.js
  43. 101 0
      src/pages/collection/Index.vue
  44. 16 0
      src/pages/collection/index.js
  45. 13 0
      src/pages/common.js
  46. 102 0
      src/pages/demo/Index.vue
  47. 16 0
      src/pages/demo/index.js
  48. 49 0
      src/pages/detail/AppDetail/Index.vue
  49. 16 0
      src/pages/detail/AppDetail/index.js
  50. 214 0
      src/pages/detail/audioDetail/Index.vue
  51. 16 0
      src/pages/detail/audioDetail/index.js
  52. 210 0
      src/pages/detail/newsDetail/Index.vue
  53. 16 0
      src/pages/detail/newsDetail/index.js
  54. 65 0
      src/pages/detail/pdfDetail/Index.vue
  55. 16 0
      src/pages/detail/pdfDetail/index.js
  56. 236 0
      src/pages/detail/videoDetail/Index.vue
  57. 16 0
      src/pages/detail/videoDetail/index.js
  58. 56 0
      src/pages/download/Index.vue
  59. 16 0
      src/pages/download/index.js
  60. 65 0
      src/pages/index/Index.vue
  61. 16 0
      src/pages/index/index.js
  62. 99 0
      src/pages/integral/Index.vue
  63. 16 0
      src/pages/integral/index.js
  64. 122 0
      src/pages/leaving/Index.vue
  65. 16 0
      src/pages/leaving/index.js
  66. 111 0
      src/pages/login/Index.vue
  67. 16 0
      src/pages/login/index.js
  68. 135 0
      src/pages/myHome/Index.vue
  69. 16 0
      src/pages/myHome/index.js
  70. 133 0
      src/pages/myPartybranch/Index.vue
  71. 16 0
      src/pages/myPartybranch/index.js
  72. 41 0
      src/pages/news/Index.vue
  73. 16 0
      src/pages/news/index.js
  74. 153 0
      src/pages/partyAffairs/Index.vue
  75. 16 0
      src/pages/partyAffairs/index.js
  76. 86 0
      src/pages/partyHome/Index.vue
  77. 16 0
      src/pages/partyHome/index.js
  78. 248 0
      src/pages/partyStudy/Index.vue
  79. 16 0
      src/pages/partyStudy/index.js
  80. 135 0
      src/pages/peeList/Index.vue
  81. 16 0
      src/pages/peeList/index.js
  82. 118 0
      src/pages/relationshipTransfer/Index.vue
  83. 16 0
      src/pages/relationshipTransfer/index.js
  84. 222 0
      src/pages/relationshipTransferFrom/Index.vue
  85. 16 0
      src/pages/relationshipTransferFrom/index.js
  86. 218 0
      src/pages/robot/Index.vue
  87. 16 0
      src/pages/robot/index.js
  88. 69 0
      src/pages/service/Index.vue
  89. 16 0
      src/pages/service/index.js
  90. 48 0
      src/pages/special/Index.vue
  91. 16 0
      src/pages/special/index.js
  92. 87 0
      src/pages/study/Index.vue
  93. 16 0
      src/pages/study/index.js
  94. 19 0
      src/utils/adaptation.js
  95. 122 0
      src/utils/buildApiCode.js
  96. 152 0
      src/utils/common.js
  97. 10 0
      src/utils/isTerminal.js
  98. 43 0
      src/utils/nodeTool/analysisArgv.js
  99. 44 0
      src/utils/nodeTool/index.js
  100. 17 0
      src/utils/nodeTool/jsTemplate.js

+ 2 - 0
.browserslistrc

@@ -0,0 +1,2 @@
+> 1%
+last 2 versions

+ 2 - 0
.env.dev

@@ -0,0 +1,2 @@
+NODE_ENV = 'production'
+VUE_APP_MODE = 'buildDev'

+ 2 - 0
.env.devtest

@@ -0,0 +1,2 @@
+NODE_ENV = 'production'
+VUE_APP_MODE = 'buildDevTest'

+ 2 - 0
.env.test

@@ -0,0 +1,2 @@
+NODE_ENV = 'production'
+VUE_APP_MODE = 'buildTest'

+ 14 - 0
.eslintrc.js

@@ -0,0 +1,14 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  extends: ["plugin:vue/essential", "@vue/prettier"],
+  rules: {
+    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
+    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
+  },
+  parserOptions: {
+    parser: "babel-eslint"
+  }
+};

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+.DS_Store
+node_modules
+/dist
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 75 - 0
README.md

@@ -0,0 +1,75 @@
+# vueMultiplePages
+
+## 一个 Vue 多页面应用,适用于移动端不需要单页应用(SPA)的场景
+
+### Project setup
+
+```
+yarn install
+```
+
+### Compiles and hot-reloads for development
+
+```
+yarn dev
+```
+
+### Compiles and minifies
+
+```
+yarn build:dev  //打包开发环境
+yarn build:devtest //打包开发测试环境
+yarn build:test //打包测试环境
+yarn build // 打包正式环境
+```
+
+### Lints and fixes files
+
+```
+yarn lint
+```
+
+### Customize configuration
+
+See [Configuration Reference](https://cli.vuejs.org/config/).
+
+这是一个做了大量移动端的 vue 多页面最佳实践(自我认为)。使用了全新依赖和 ESLint+Prettier 第二次重构(2020.01)。
+
+### 说明
+
+- 使用 normalize.css 重置样式。(如果使用 vant 可以去掉)
+- 添加了 axios 请求库,并做了简单的拦截。
+- 添加了漂亮的移动端调试工具 eruda,方便在手机上调试(使用 npm run build 命令不会出现此工具)。
+- 添加了 Url 参数验证和初始化,`Vue.prototype.$pageParams` 保存了 Url 携带的参数对象。`let { id } = this.$pageParams;`
+- `window.PAGE_PATH(Vue.prototype.$pageName)` 表示当前页面的名字,如 index 目录生成 index.html,window.PAGE_PATH 就是 index
+- 想要添加自己 UI 库,安装好在 common.js 引用即可。(推荐 Vant)
+- 封装了微信 jsSdk 的常用签名分享等一堆堆常用的东西,食用前请先安装 weixin-js-sdk,并在 utils/wechat.js 引入。
+- 添加 postcss-px2rem 自动将 px 转换为 rem 适配移动端,目前为了和大部分 ui 库兼容,设置的设计稿宽度为 375,可自行修改。
+- 添加了 node 工具,在 src/utils/nodeTool 下,运行 node index.js -h 即可查看使用方法。
+- 添加页面请在 pages 文件夹下新建目录,在里面放置 index.js 和 Index.vue(建议使用提供的 node 工具生成页面,他会更新你的配置)。编译后,目录的名字即为网页的名字。至于为什么?请查看 page.config.js。
+- 如果页面太多,可以通过在 pages 下添加分组目录来分类页面,支持无限层级目录,只要目录里存在 index.js 和 Index.vue 即被认为是一个页面。
+- 没有路由(vue-route),页面跳转请使用
+
+```javascript
+window.location.href = "./demo.html" + obj2StrParams(params);
+```
+
+### 其他
+
+我们还需要 fastclick js 去解决移动端点击 300ms 延迟吗?
+从 Chrome 32(早在 2014 年)开始,这种针对移动设备优化的网站的延迟就消失了,
+而无需消除缩放问题!Firefox 和 IE / Edge 之后不久也做了同样的事情,并在 2016 年 3 月在 iOS 9.3 中进行了类似的修复。
+只要您 head 包括:
+
+```html
+<meta name="viewport" content="width=device-width" />
+```
+
+浏览器就会以这种方式假定您已使文本在移动设备上可读,因此无需双击。
+
+---
+
+还有各种移动端奇形怪状的问题解决方案
+https://juejin.im/post/5d6e1899e51d453b1e478b29
+
+> 番外: [MareWood](https://github.com/xusenlin/MareWood) 是一个 Go 开发的轻量级前端部署工具,可以很灵活的配置各种打包部署环境并提供访问,特别是远程的时候,方便后端和测试使用,草鸡好用。

+ 3 - 0
babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  presets: ["@vue/cli-plugin-babel/preset"]
+};

+ 40 - 0
package.json

@@ -0,0 +1,40 @@
+{
+  "name": "vuemultiplepages",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "build:dev": "vue-cli-service build  --mode dev",
+    "build:devtest": "vue-cli-service build  --mode devtest",
+    "build:test": "vue-cli-service build --mode test",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@liripeng/vue-audio-player": "^1.2.8",
+    "axios": "^0.19.0",
+    "core-js": "^3.4.4",
+    "good-storage": "^1.1.0",
+    "node-sass": "4.14.1",
+    "normalize.css": "^8.0.1",
+    "vant": "^2.12.10",
+    "vue": "^2.6.10",
+    "vue-pdf": "^4.2.0",
+    "vue-video-player": "^5.0.2"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^4.1.0",
+    "@vue/cli-plugin-eslint": "^4.1.0",
+    "@vue/cli-service": "^4.1.0",
+    "@vue/eslint-config-prettier": "^5.0.0",
+    "babel-eslint": "^10.0.3",
+    "eslint": "^5.16.0",
+    "eslint-plugin-prettier": "^3.1.1",
+    "eslint-plugin-vue": "^5.0.0",
+    "file-loader": "^6.2.0",
+    "postcss-px2rem": "^0.3.0",
+    "prettier": "^1.19.1",
+    "sass-loader": "^8.0.0",
+    "vue-template-compiler": "^2.6.10"
+  }
+}

+ 39 - 0
page.config.js

@@ -0,0 +1,39 @@
+const fs = require("fs");
+const Config = require("./src/config/page.js");
+
+let pageConfig = {};
+
+function addPageConfig(path, dir) {
+  if (isPage(path + "/" + dir)) {
+    if (pageConfig.hasOwnProperty(dir)) {
+      throw new Error("有名字重复的页面:" + dir);
+    }
+    let template = "public/index.html";
+
+    if (fs.existsSync(path + "/" + dir + "/index.html")) {
+      template = path + "/" + dir + "/index.html";
+    }
+    pageConfig[dir] = {
+      entry: path + "/" + dir + "/index.js",
+      filename: dir + ".html",
+      path: dir,
+      title: Config.hasOwnProperty(dir) ? Config[dir].title : "",
+      template: template
+    };
+  }
+  let fileOrDir = fs.readdirSync(path + "/" + dir);
+  fileOrDir.forEach(function(file) {
+    let newDir = path + "/" + dir;
+    if (fs.statSync(newDir + "/" + file).isDirectory()) {
+      addPageConfig(newDir, file);
+    }
+  });
+}
+
+function isPage(dir) {
+  return fs.existsSync(dir + "/index.js") && fs.existsSync(dir + "/Index.vue");
+}
+
+addPageConfig("src", "pages");
+
+module.exports = pageConfig;

+ 25 - 0
public/index.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport" />
+    <meta name="format-detection" content="telephone=yes" />
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= htmlWebpackPlugin.options.title %></title>
+    <% if(!("production" == process.env.NODE_ENV && undefined == process.env.VUE_APP_MODE)){ %>
+    <script src="//cdn.bootcdn.net/ajax/libs/eruda/2.4.1/eruda.min.js"></script>
+    <script>eruda.init();</script>
+    <% } %>
+    <script>
+      window.PAGE_PATH = '<%= htmlWebpackPlugin.options.path %>';
+    </script>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but vueMultiplePages doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 26 - 0
src/api/account/account.js

@@ -0,0 +1,26 @@
+import request from "@/utils/request.js";
+
+/**
+ * 列表
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function activistList() {
+  return request({
+    url: "/account",
+    method: "get"
+  });
+}
+
+/**
+ * 列表
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function Pay(params) {
+  return request({
+    url: "/account/pay",
+    method: "post",
+    data: params
+  });
+}

+ 25 - 0
src/api/activist/activist.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request.js";
+
+/**
+ * 列表
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function activistList() {
+  return request({
+    url: "/dang-info",
+    method: "get"
+  });
+}
+/**
+ * 添加
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function activistAdd(data) {
+  return request({
+    url: "/dang-info/add",
+    method: "post",
+    data
+  });
+}

+ 144 - 0
src/api/article/article.js

@@ -0,0 +1,144 @@
+import request from "@/utils/request.js";
+
+/**
+ * 列表
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function articleList(params, id) {
+  return request({
+    url: "/article/list/" + id,
+    method: "post",
+    params
+  });
+}
+
+/**
+ * 新闻详情
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function articleDetail(id, err403) {
+  return request({
+    url: "/article/" + id,
+    err403,
+    method: "post"
+  });
+}
+
+/**
+ * 收藏列表
+ * @param id
+ * @returns {AxiosPromise}
+ */
+export function articleCollect(id) {
+  let url = "/article/collect-list";
+  if (id) url += "?lastCollectId=" + id;
+  return request({
+    url: url,
+    method: "get"
+  });
+}
+
+/**
+ * 收藏
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function collect(id) {
+  return request({
+    url: "/article/collect/" + id,
+    method: "get"
+  });
+}
+/**
+ * 取消收藏
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function collectCancel(id) {
+  return request({
+    url: "/article/collect-cancel/" + id,
+    method: "get"
+  });
+}
+
+/**
+ * 点赞
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function digg(id) {
+  return request({
+    url: "/article/digg/" + id,
+    method: "post"
+  });
+}
+/**
+ * 取消点赞
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function diggCancel(id) {
+  return request({
+    url: "/article/cancel-digg/" + id,
+    method: "post"
+  });
+}
+
+/**
+ * 获得banner
+ * @returns promise
+ */
+export function banner() {
+  return request({
+    url: "/article/banner"
+  });
+}
+/**
+ * 进度
+ * @returns promise
+ */
+export function progress(data) {
+  return request({
+    url: "/article/progress",
+    method: "post",
+    data
+  });
+}
+
+/**
+ * 进度
+ * @returns promise
+ */
+export function getReadHistory(params) {
+  return request({
+    url: "/article/history",
+    method: "get",
+    params
+  });
+}
+
+/**
+ * 添加评论
+ * @returns promise
+ */
+export function getComment(data) {
+  return request({
+    url: "/article/add-comment",
+    method: "post",
+    data
+  });
+}
+
+/**
+ * 评论列表
+ * @returns promise
+ */
+export function getCommentList(params, id) {
+  return request({
+    url: "/article/comment-list/" + id,
+    method: "get",
+    params
+  });
+}

+ 72 - 0
src/api/party/party.js

@@ -0,0 +1,72 @@
+import request from "@/utils/request.js";
+
+/**
+ * 列表
+ * @returns {AxiosPromise}
+ */
+export function list() {
+  return request({
+    url: "/organ/list",
+    method: "get"
+  });
+}
+/**
+ * 修改党支部
+ * @param data
+ * @returns {AxiosPromise}
+ */
+export function upParty(data) {
+  return request({
+    url: "/organ/change?organId=" + data.organId,
+    method: "post",
+    data
+  });
+}
+/**
+ * 党组领导
+ * @param data
+ * @returns {AxiosPromise}
+ */
+export function memberParty(data) {
+  return request({
+    url: "/organ/zb-bz",
+    method: "GET",
+    data
+  });
+}
+/**
+ * 党组成员
+ * @param data
+ * @returns {AxiosPromise}
+ */
+export function userParty(data) {
+  return request({
+    url: "/organ/zb",
+    method: "GET",
+    data
+  });
+}
+
+/**
+ * 添加留言
+ * @param data
+ * @returns {AxiosPromise}
+ */
+export function addComment(data) {
+  return request({
+    url: "/organ/comment",
+    method: "post",
+    data
+  });
+}
+/**
+ * 留言
+ * @param data
+ * @returns {AxiosPromise}
+ */
+export function lookComment(id) {
+  return request({
+    url: "/organ/comment-list" + (id || ""),
+    method: "GET"
+  });
+}

+ 26 - 0
src/api/robot/index.js

@@ -0,0 +1,26 @@
+import request from "@/utils/request.js";
+import Config from "@/config/index.js";
+
+/**
+ * 获得机器人token
+ * @returns Promise
+ */
+export function getRobotToken() {
+  return request({
+    url: Config.robotBaseUrl + "/abc-robot/caas/oauth/token/v2",
+    robot: true,
+    method: "post"
+  });
+}
+
+/**
+ * 获得机器人设备
+ * @returns Promise
+ */
+export function getRobotClient() {
+  return request({
+    url: Config.robotBaseUrl + " /push/v1/browser",
+    robot: true,
+    method: "get"
+  });
+}

+ 39 - 0
src/api/user/user.js

@@ -0,0 +1,39 @@
+import request from "@/utils/request.js";
+
+/**
+ * 登录
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function login(data) {
+  return request({
+    url: "/user/login",
+    method: "post",
+    data: data
+  });
+}
+
+/**
+ * 获取验证码
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function getCode(params) {
+  return request({
+    url: "/user/send-code",
+    method: "post",
+    data: params
+  });
+}
+
+/**
+ * 获取用户信息
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function getUser() {
+  return request({
+    url: "/user/info",
+    method: "get"
+  });
+}

+ 27 - 0
src/api/wechat/api.js

@@ -0,0 +1,27 @@
+import request from "@/utils/request.js";
+
+/**
+ * 微信签名
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function wechatSignatureApi(params) {
+  return request({
+    url: "/open/signature",
+    method: "get",
+    params: params
+  });
+}
+
+/**
+ * xxx
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function postRequest(params) {
+  return request({
+    url: "/user",
+    method: "post",
+    data: params
+  });
+}

+ 49 - 0
src/assets/css/1px.scss

@@ -0,0 +1,49 @@
+
+.one-px, .one-px-t, .one-px-b, .one-px-l, .one-px-r {
+  position: relative;
+}
+
+.one-px:before, .one-px-t:before,
+.one-px-b:after, .one-px-l:before,
+.one-px-r:after {
+  content: " ";
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 200%;
+  border-color: #eee;
+  color: #eee;
+  height: 200%;
+  transform-origin: left top;
+  transform: scale(0.5);
+}
+
+.one-px {
+  &:before {
+    border: 1px solid;
+  }
+}
+
+.one-px-t {
+  &:before {
+    border-top: 1px solid;
+  }
+}
+
+.one-px-b {
+  &:after {
+    border-bottom: 1px solid;
+  }
+}
+
+.one-px-l {
+  &:before {
+    border-left: 1px solid;
+  }
+}
+
+.one-px-r {
+  &:after {
+    border-right: 1px solid;
+  }
+}

+ 25 - 0
src/assets/css/reset.scss

@@ -0,0 +1,25 @@
+.van-tabs__line,
+.van-radio__icon--checked .van-icon {
+    background-color: #e42417;
+    border-color: #e42417;
+}
+
+.van-nav-bar .van-icon,
+.van-nav-bar__text {
+    color: #e42417;
+}
+
+
+.visited {
+    color: #abb2b8;
+}
+
+.video-js .vjs-big-play-button {
+    left: 50%;
+    top:50%;
+    transform: translate(-50%,-50%);
+}
+
+.van-icon{
+    color:#e42417
+}

+ 45 - 0
src/assets/css/style.scss

@@ -0,0 +1,45 @@
+@import "1px";
+@import "reset";
+*{
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+}
+html,body{
+  height: 100%;
+  width: 100%;
+  overflow: hidden;
+}
+body{
+  overflow-y: scroll;
+}
+
+body{
+  font-size: 16px;
+  font-weight: 400;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  font-family: -apple-system,
+    BlinkMacSystemFont,
+    "Segoe UI",
+    "Roboto",
+    "Oxygen",
+    "Ubuntu",
+    "Cantarell",
+    "Fira Sans",
+    "Droid Sans",
+    "Helvetica Neue",
+    "Microsoft Yahei",
+    sans-serif;
+  #app{
+    height: 100%;
+  }
+  img{
+    width: 100%;
+  }
+}
+

BIN=BIN
src/assets/image/0.jpg


BIN=BIN
src/assets/image/down.jpg


BIN=BIN
src/assets/image/partySchool.jpg


+ 217 - 0
src/components/Comment/Comment.vue

@@ -0,0 +1,217 @@
+<template>
+  <div class="comment">
+    <span v-if="readCount >= 0" style="color:#969799;font-size:0.14rem">
+      阅读量:{{ readCount || 0 }}
+    </span>
+    <van-grid :column-num="3" class="btnTool" :border="false">
+      <van-grid-item
+        :icon="isFablous ? 'good-job' : 'good-job-o'"
+        @click="fabulous"
+        :badge="diggCount"
+      />
+      <van-grid-item
+        :icon="isCollect ? 'star' : 'star-o'"
+        @click="$emit('changeColl')"
+      />
+      <van-grid-item
+        icon="comment-o"
+        :badge="commentCount"
+        @click="showDialog = !showDialog"
+      />
+    </van-grid>
+    <div v-if="commentList && commentList.length" class="ment">
+      <van-cell-group>
+        <van-cell
+          v-for="item in commentList"
+          :key="item.id"
+          :title="item.nickName"
+          :value="item.createTime"
+          :label="item.content"
+        />
+      </van-cell-group>
+
+      <van-button
+        style="margin: 0 auto;display: block"
+        :loading="more"
+        v-if="commentList.length < commentCount"
+        loading-text="加载中..."
+        @click="$emit('upset')"
+      >
+        加载更多
+      </van-button>
+    </div>
+    <van-empty v-else description="暂无评论" />
+    <van-overlay :show="showDialog" @click="showDialog = false">
+      <div class="diaContent" @click.stop>
+        <div class="body">
+          <div class="title">评论</div>
+          <van-cell-group>
+            <van-cell title="">
+              <template #label>
+                <van-field
+                  v-model="value"
+                  rows="1"
+                  autosize
+                  type="textarea"
+                  placeholder="请输入评论"
+                />
+              </template>
+            </van-cell>
+          </van-cell-group>
+        </div>
+        <van-button block @click="sendComment" type="default">
+          发送
+        </van-button>
+      </div>
+    </van-overlay>
+  </div>
+</template>
+
+<script>
+import {
+  CellGroup as vanCellGroup,
+  Grid as vanGrid,
+  GridItem as vanGridItem,
+  Button as vanButton,
+  Empty as vanEmpty,
+  Field as vanField,
+  Overlay as vanOverlay,
+  Cell as vanCell,
+  Toast
+} from "vant";
+import "vant/lib/cell-group/style";
+import "vant/lib/grid-item/style";
+import "vant/lib/button/style";
+import "vant/lib/field/style";
+import "vant/lib/overlay/style";
+import "vant/lib/empty/style";
+import "vant/lib/cell/style";
+import "vant/lib/toast/style";
+import "vant/lib/grid/style";
+import { getComment } from "@/api/article/article.js";
+export default {
+  name: "app",
+  props: {
+    diggCount: {
+      type: Number,
+      value: 0
+    },
+    commentCount: {
+      type: Number,
+      value: 0
+    },
+    readCount: {
+      type: Number,
+      value: 0
+    },
+    commentList: {
+      type: Array,
+      value: []
+    },
+    more: {
+      type: Boolean,
+      value: false
+    },
+    isFablous: {
+      type: Boolean,
+      value: false
+    },
+    isCollect: {
+      type: Boolean,
+      value: false
+    },
+    id: {
+      type: String,
+      value: ""
+    }
+  },
+  data: function() {
+    return {
+      value: "",
+      showDialog: false
+    };
+  },
+  methods: {
+    fabulous() {
+      // 点赞
+      this.$emit("changeFab", !this.isFablous);
+    },
+    sendComment() {
+      // 写入评论
+      if (!this.value) return;
+      getComment({
+        articleId: this.id,
+        content: this.value
+      }).then(() => {
+        this.showDialog = false;
+        this.value = "";
+        this.$emit("upset");
+        Toast("添加成功!");
+      });
+    }
+  },
+  computed: {},
+  mounted() {},
+  beforeDestroy: function() {},
+  components: {
+    vanCellGroup,
+    vanGridItem,
+    vanButton,
+    vanEmpty,
+    vanField,
+    vanCell,
+    vanOverlay,
+    vanGrid
+  }
+};
+</script>
+<style lang="scss" scoped>
+.ment {
+  padding-bottom: 30px;
+  .commentTitle {
+    color: #969799;
+    font-size: 14px;
+    padding: 0 16px;
+    .van-col {
+      height: 2em;
+      line-height: 2em;
+    }
+  }
+  .commentBody {
+    padding: 0 16px;
+    .van-hairline--top {
+      padding: 3px 0;
+    }
+  }
+}
+.diaContent {
+  transform: translate(-50%, -50%);
+  width: 80%;
+  border-radius: 1em;
+  overflow: hidden;
+  position: absolute;
+  background-color: #fff;
+  left: 50%;
+  top: 50%;
+  .van-button {
+    border-top: 0.01rem solid #ebedf0;
+  }
+
+  .body {
+    .title {
+      text-align: center;
+      height: 2.5em;
+      line-height: 2.5em;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.comment {
+  .btnTool {
+    .van-grid-item__icon {
+      font-size: 0.2rem;
+    }
+  }
+}
+</style>

+ 148 - 0
src/components/Detail/audio.vue

@@ -0,0 +1,148 @@
+<template>
+  <div id="audioDetail" class="app" ref="elementAsconten">
+    <van-sticky>
+      <div class="head">
+        <audio-player
+          ref="audioEle"
+          color="#e42417"
+          @playing="playing"
+          :show-prev-button="false"
+          :show-next-button="false"
+          :audio-list="[item.videoUrl]"
+          :before-play="onBeforePlay"
+        ></audio-player>
+      </div>
+    </van-sticky>
+    <div v-if="item.id">
+      <van-cell-group>
+        <van-cell title="" :value="item.publishTime" />
+        <div
+          @click="showIntrodu = !showIntrodu"
+          :class="{ introduction: true, 'van-ellipsis': !showIntrodu }"
+          v-text="item.normalizedContent"
+          v-if="item.normalizedContent"
+        ></div>
+      </van-cell-group>
+    </div>
+    <van-empty v-else description="内容没找到" />
+  </div>
+</template>
+
+<script>
+// import { articleDetail } from "@/api/article/article.js";
+import { currentUrlToParams } from "@/utils/common.js";
+import {
+  Cell as vanCell,
+  CellGroup as vanCellGroup,
+  Sticky as vanSticky,
+  Empty as vanEmpty
+} from "vant";
+import "video.js/dist/video-js.css";
+import "vant/lib/cell-group/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+import "vant/lib/empty/style";
+import "vant/lib/cell/style";
+import "vant/lib/toast/style";
+import "@liripeng/vue-audio-player/lib/vue-audio-player.css";
+import AudioPlayer from "@liripeng/vue-audio-player";
+import Config from "@/config/index.js";
+export default {
+  name: "app",
+  maxPro: 0,
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    id: {
+      type: String,
+      default: ""
+    }
+  },
+  data: function() {
+    return {
+      chosenAddressId: "1",
+      showIntrodu: true,
+      value: "",
+      more: false,
+      audioList: []
+    };
+  },
+  timeout: null,
+  methods: {
+    goBack() {
+      history.go(-1);
+    },
+    // 播放前做的事
+    onBeforePlay(next) {
+      this.playing();
+      next(); // 开始播放
+    },
+    playing() {
+      const duration = (this.$refs.audioEle.duration * 1000).toFixed(0) - 0;
+      const currentTime =
+        (this.$refs.audioEle.currentTime * 1000).toFixed(0) - 0;
+      let timeout = new Date() - 0;
+      let pro = (currentTime / duration).toFixed(2);
+      if (timeout - this.timeout < Config.proTimeout || pro <= this.maxPro)
+        return;
+      this.timeout = timeout;
+      this.maxPro = pro;
+      // progress({
+      //   articleId: this.$pageParams.id,
+      //   progress: pro
+      // });
+      window.Progress.progress = pro;
+    }
+  },
+  mounted() {
+    window.webkit &&
+      window.webkit.messageHandlers.setWebviewHeight.postMessage({
+        height: this.$refs.elementAsconten.offsetHeight
+      });
+    window.H5Listener &&
+      window.H5Listener.setWebviewHeight(
+        this.$refs.elementAsconten.offsetHeight
+      );
+    this.$pageParams = currentUrlToParams();
+    this.timeout = new Date();
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanCellGroup,
+    AudioPlayer,
+    vanSticky,
+    vanEmpty,
+    vanCell
+  }
+};
+</script>
+<style lang="scss" scoped>
+#audioDetail {
+  .van-button {
+    border: none;
+  }
+  .video-js {
+    .vjs-big-play-button {
+      top: 50%;
+      left: 50%;
+    }
+  }
+  .introduction {
+    padding: 0.5em;
+    font-size: 0.14rem;
+    color: #969799;
+    text-indent: 2em;
+  }
+  .head {
+    padding: 10em 2em 2em 2em;
+  }
+
+  .selectText {
+    color: #ee0a24;
+  }
+}
+</style>

+ 127 - 0
src/components/Detail/news.vue

@@ -0,0 +1,127 @@
+<template>
+  <div class="bg" ref="scroll">
+    <div id="news" ref="croll">
+      <div class="content" ref="content">
+        <div class="grid-cell">
+          <div class="render-detail-article">
+            <div class="render-detail-title" v-text="item.title"></div>
+            <span
+              style="font-size: 14px; color: #abb2b8"
+              v-text="item.origSource + ' ' + item.publishTime"
+            ></span>
+            <div class="contentDetail" v-html="item.content"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Config from "@/config/index.js";
+// import { progress } from "@/api/article/article.js";
+export default {
+  name: "app",
+  timeout: null,
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    id: {
+      type: String,
+      default: ""
+    }
+  },
+  watch: {},
+  data: function() {
+    return {};
+  },
+  filters: {},
+  methods: {
+    setProgress() {
+      if (!this.$refs.croll) return;
+      let timeout = new Date() - 0;
+      let scrollTop = this.$refs.croll.scrollTop;
+      let height =
+        this.$refs.content.offsetHeight + 70 - this.$refs.croll.clientHeight;
+      let pro = (scrollTop / height).toFixed(2);
+      pro = pro > 1 ? "1" : pro;
+      pro = height <= 0 ? "1" : pro;
+      if (
+        (height > 0 && timeout - this.timeout < Config.proTimeout) ||
+        pro <= this.maxPro
+      )
+        return;
+      this.maxPro = pro;
+      this.timeout = timeout;
+      // progress({
+      //   articleId: this.id,
+      //   progress: pro
+      // });
+
+      window.Progress.progress = pro;
+    }
+  },
+  mounted() {
+    this.timeout = new Date() - 1001;
+    window.webkit &&
+      window.webkit.messageHandlers.setWebviewHeight.postMessage({
+        height: this.$refs.scroll.offsetHeight
+      });
+    window.H5Listener &&
+      window.H5Listener.setWebviewHeight(this.$refs.scroll.offsetHeight);
+    this.$refs.scroll.addEventListener("scroll", this.setProgress, true);
+    this.$nextTick(() => {
+      this.setProgress();
+    });
+  },
+  beforeDestroy: function() {
+    this.setProgress();
+    clearTimeout(this.timeout);
+  },
+  components: {}
+};
+</script>
+<style lang="scss">
+.bg {
+  height: 100%;
+}
+#news {
+  padding: 0.1rem;
+  color: #262626;
+  font-family: 微软雅黑, Microsoft YaHei, PingFangSC;
+  overflow-y: scroll;
+  font-size: 16px;
+  font-weight: 450;
+  height: 100%;
+  .render-detail-article .render-detail-title {
+    text-align: center;
+    font-size: 0.2rem;
+    letter-spacing: 0;
+    line-height: 1.2em;
+    font-weight: 500;
+    margin: 0.05rem auto;
+  }
+  p {
+    min-height: 0.3rem;
+    line-height: 1.5em;
+    text-indent: 2em;
+    margin: 0.2rem 0;
+    padding-left: 0.05rem;
+    strong {
+      font-weight: 450;
+    }
+    .render-detail-footer {
+      text-align: right;
+      padding-right: 5px;
+    }
+  }
+
+  img {
+    width: 100%;
+  }
+}
+</style>

+ 177 - 0
src/components/Detail/video.vue

@@ -0,0 +1,177 @@
+<template>
+  <div id="video" class="app" ref="elementAsconten">
+    <van-sticky :offset-top="0">
+      <video-player
+        class="video-player-box"
+        ref="videoPlayer"
+        :options="playerOptions"
+        :playsinline="true"
+        customEventName="customstatechangedeventname"
+        @play="onPlayerPlay($event)"
+        @pause="onPlayerPause($event)"
+        @ended="onPlayerEnded($event)"
+        @waiting="onPlayerWaiting($event)"
+        @playing="onPlayerPlaying($event)"
+        @loadeddata="onPlayerLoadeddata($event)"
+        @timeupdate="onPlayerTimeupdate($event)"
+        @canplay="onPlayerCanplay($event)"
+        @canplaythrough="onPlayerCanplaythrough($event)"
+        @statechanged="playerStateChanged($event)"
+        @ready="playerReadied"
+      >
+      </video-player>
+    </van-sticky>
+    <div v-if="item.id">
+      <van-cell-group>
+        <van-cell :title="item.title" :value="item.publishTime" />
+        <div
+          @click="showIntrodu = !showIntrodu"
+          :class="{ introduction: true, 'van-ellipsis': !showIntrodu }"
+          v-text="item.normalizedContent"
+          v-if="item.normalizedContent"
+        ></div>
+      </van-cell-group>
+    </div>
+    <van-empty v-else description="内容没找到" />
+  </div>
+</template>
+
+<script>
+// import { progress } from "@/api/article/article.js";
+import {
+  Cell as vanCell,
+  CellGroup as vanCellGroup,
+  Sticky as vanSticky,
+  Empty as vanEmpty
+} from "vant";
+import "vant/lib/cell-group/style";
+import "vant/lib/sticky/style";
+import "vant/lib/empty/style";
+import "vant/lib/toast/style";
+import "vant/lib/cell/style";
+import "video.js/dist/video-js.css";
+import { videoPlayer } from "vue-video-player";
+
+import Config from "@/config/index.js";
+
+export default {
+  name: "app",
+  maxPro: 0,
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    id: {
+      type: String,
+      default: ""
+    }
+  },
+  data: function() {
+    return {
+      showIntrodu: true,
+      video: {},
+      playerOptions: {},
+      more: false
+    };
+  },
+  timeout: null,
+  methods: {
+    onPlayerPlay() {
+      console.log("开始播放");
+    },
+    onPlayerPause() {
+      console.log("暂停播放");
+    },
+    onPlayerEnded() {
+      console.log("结束播放");
+    },
+    onPlayerWaiting() {
+      console.log("等待播放");
+    },
+    onPlayerPlaying() {
+      console.log("播放中");
+    },
+    onPlayerLoadeddata() {
+      console.log("加载结束");
+    },
+    onPlayerTimeupdate(e) {
+      let timeout = new Date() - 0;
+      let pro = (e.cache_.currentTime / e.cache_.duration).toFixed(2);
+      if (timeout - this.timeout < Config.proTimeout || pro <= this.maxPro)
+        return;
+      this.timeout = timeout;
+      this.maxPro = pro;
+      // progress({
+      //   articleId: this.id,
+      //   progress: pro
+      // });
+      window.Progress.progress = pro;
+    },
+    onPlayerCanplay() {
+      console.log("允许播放");
+    },
+    onPlayerCanplaythrough() {
+      console.log("允许播放通过");
+    },
+    playerStateChanged() {
+      console.log("播放状态改变");
+    },
+    playerReadied() {
+      console.log("准备好");
+      window.webkit &&
+        window.webkit.messageHandlers.setWebviewHeight.postMessage({
+          height: this.$refs.elementAsconten.offsetHeight
+        });
+      window.H5Listener &&
+        window.H5Listener.setWebviewHeight(
+          this.$refs.elementAsconten.offsetHeight
+        );
+    }
+  },
+  mounted() {
+    this.playerOptions = {
+      // videojs options
+      muted: true,
+      width: "100%",
+      aspectRatio: "16:9",
+      language: "zh-CN",
+      playbackRates: [0.5, 1.0, 1.5, 2.0],
+      sources: [
+        {
+          type: "video/mp4",
+          src: this.item.videoUrl
+        }
+      ],
+      poster: "/static/images/author.jpg"
+    };
+    this.timeout = new Date() - 0;
+  },
+  beforeDestroy: function() {},
+  components: {
+    videoPlayer,
+    vanCell,
+    vanCellGroup,
+    vanSticky,
+    vanEmpty
+  }
+};
+</script>
+<style lang="scss" scoped>
+#video {
+  .video-js {
+    .vjs-big-play-button {
+      top: 50%;
+      left: 50%;
+    }
+  }
+  .introduction {
+    padding: 0.5em;
+    font-size: 0.14rem;
+    color: #969799;
+    text-indent: 2em;
+  }
+}
+</style>

+ 107 - 0
src/components/ListItem/ListItem.vue

@@ -0,0 +1,107 @@
+<template>
+  <div :class="{ 'one-px-b': true, visited: item.read }">
+    <van-row>
+      <van-col :span="item.url ? 16 : 24" v-if="item.title">
+        <div class="listItem">
+          <p
+            class="van-multi-ellipsis--l3 listItemTitle"
+            v-text="item.title"
+          ></p>
+        </div>
+      </van-col>
+      <van-col v-if="item.url" span="8">
+        <div class="listItemImage">
+          <van-image
+            width="100%"
+            height="100%"
+            lazy-load
+            fit="contain"
+            :src="item.url"
+          />
+          <br />
+        </div>
+      </van-col>
+    </van-row>
+    <div class="relation">
+      <div class="relationSon">
+        <span v-text="item.orisource"></span>
+        <span> {{ item.releaseTime | dateChange }} </span>
+        <span class="progress" v-if="item.progress >= 0">
+          {{ item.progress | percentage }}
+        </span>
+      </div>
+      <div class="relationSon"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Col as vanCol, Row as vanRow, Image as VanImage } from "vant";
+import "vant/lib/row/style";
+import "vant/lib/image/style";
+import "vant/lib/col/style";
+export default {
+  name: "app",
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    }
+  },
+  data: function() {
+    return {};
+  },
+  filters: {
+    dateChange(time) {
+      let t = time.replace(/-/g, "/");
+      const D = new Date(t);
+      const year = D.getFullYear();
+      const month =
+        D.getMonth() + 1 > 9 ? D.getMonth() + 1 : "0" + (D.getMonth() + 1);
+      const day = D.getDate() > 9 ? D.getDate() : "0" + D.getDate();
+      const hour = D.getHours() > 9 ? D.getHours() : "0" + D.getHours();
+      const min = D.getMinutes() > 9 ? D.getMinutes() : "0" + D.getMinutes();
+      const sec = D.getSeconds() > 9 ? D.getSeconds() : "0" + D.getSeconds();
+      return (
+        year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec
+      );
+    },
+    percentage(number) {
+      return number * 100 + "%";
+    }
+  },
+  methods: {},
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanCol, vanRow, VanImage }
+};
+</script>
+<style lang="scss" scoped>
+.listItem {
+  padding: 0.1rem;
+  .listItemTitle {
+    font-weight: 600;
+    font-size: 0.15rem;
+  }
+}
+.listItemImage {
+  padding: 0.1rem 0.1rem 0.1rem;
+}
+.relation {
+  line-height: 2.5em;
+  font-size: 0.1rem;
+  color: #abb2b8;
+  .relationSon {
+    padding: 0 0.1rem;
+    position: relative;
+    .progress {
+      position: absolute;
+      right: 0.15rem;
+      color: #e42417;
+      font-size: 0.15rem;
+    }
+  }
+}
+</style>

+ 116 - 0
src/components/ListItem/ListItemBig.vue

@@ -0,0 +1,116 @@
+<template>
+  <div :class="{ 'one-px-b': true, visited: item.read }">
+    <van-row>
+      <van-col :span="item.url ? 16 : 24">
+        <div class="listItem">
+          <p
+            class="van-multi-ellipsis--l3 listItemTitle"
+            v-text="item.title"
+          ></p>
+        </div>
+      </van-col>
+      <van-col span="8"> </van-col>
+    </van-row>
+    <div class="subTitle">
+      <p v-if="item.subTitle" v-text="item.subTitle"></p>
+      <van-image
+        width="100%"
+        height="100%"
+        lazy-load
+        v-if="item.url"
+        fit="contain"
+        :src="item.url"
+      />
+      <br v-if="item.url" />
+    </div>
+    <div class="relation">
+      <div class="relationSon">
+        <span v-text="item.orisource"></span>
+        <span v-if="item.releaseTime">
+          {{ item.releaseTime | dateChange }}
+        </span>
+        <span class="progress" v-if="item.progress >= 0">
+          {{ item.progress | percentage }}
+        </span>
+      </div>
+      <div class="relationSon"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Col as vanCol, Row as vanRow, Image as VanImage } from "vant";
+import "vant/lib/row/style";
+import "vant/lib/image/style";
+import "vant/lib/col/style";
+export default {
+  name: "app",
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    }
+  },
+  data: function() {
+    return {};
+  },
+  filters: {
+    dateChange(time) {
+      let t = time.replace(/-/g, "/");
+      const D = new Date(t);
+      const year = D.getFullYear();
+      const month =
+        D.getMonth() + 1 > 9 ? D.getMonth() + 1 : "0" + (D.getMonth() + 1);
+      const day = D.getDate() > 9 ? D.getDate() : "0" + D.getDate();
+      const hour = D.getHours() > 9 ? D.getHours() : "0" + D.getHours();
+      const min = D.getMinutes() > 9 ? D.getMinutes() : "0" + D.getMinutes();
+      const sec = D.getSeconds() > 9 ? D.getSeconds() : "0" + D.getSeconds();
+      return (
+        year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec
+      );
+    },
+    percentage(number) {
+      return number * 100 + "%";
+    }
+  },
+  methods: {},
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanCol, vanRow, VanImage }
+};
+</script>
+<style lang="scss" scoped>
+.listItem {
+  padding: 0.1rem;
+  .listItemTitle {
+    font-weight: 600;
+    font-size: 0.15rem;
+  }
+}
+.subTitle {
+  padding: 0 0.1rem;
+  font-size: 0.12rem;
+  color: #abb2b8;
+  line-height: 0.2rem;
+  p {
+    margin-bottom: 0.1rem;
+  }
+}
+.relation {
+  line-height: 2.5em;
+  font-size: 0.12rem;
+  color: #abb2b8;
+  .relationSon {
+    padding: 0 0.1rem;
+    position: relative;
+    .progress {
+      position: absolute;
+      right: 0.15rem;
+      font-size: 0.15rem;
+      color: #e42417;
+    }
+  }
+}
+</style>

+ 136 - 0
src/components/NewsList/NewsList.vue

@@ -0,0 +1,136 @@
+<template>
+  <van-pull-refresh v-model="isLoading" @refresh="onRefresh">
+    <van-list
+      v-if="newsList.length"
+      v-model="loading"
+      :finished="finished"
+      offset="10"
+      loading-text="加载中"
+      finished-text="没有更多了"
+      @load="onload"
+    >
+      <van-swipe-cell v-for="item in newsList" :key="item.id" :title="item">
+        <div @click="() => toUrl(item)">
+          <keep-alive>
+            <component :is="item.assemblyName" :item="item"></component>
+          </keep-alive>
+        </div>
+      </van-swipe-cell>
+    </van-list>
+    <van-empty v-else description="内容没找到" />
+  </van-pull-refresh>
+</template>
+
+<script>
+import { articleList } from "@/api/article/article.js";
+import listItemBig from "../ListItem/ListItemBig";
+import listItem from "../ListItem/ListItem";
+import {
+  PullRefresh as vanPullRefresh,
+  SwipeCell as vanSwipeCell,
+  Empty as vanEmpty,
+  List as vanList
+} from "vant";
+import "vant/lib/pull-refresh/style";
+import "vant/lib/swipe-cell/style";
+import "vant/lib/empty/style";
+import "vant/lib/list/style";
+let date = new Date(new Date() - 2000);
+export default {
+  name: "app",
+  props: {
+    comId: {
+      type: String,
+      default: ""
+    }
+  },
+  data: function() {
+    return {
+      active: "partyAffairs",
+      newsList: [],
+      loading: true,
+      finished: false,
+      isLoading: false,
+      isGeting: false
+    };
+  },
+  methods: {
+    onRefresh() {
+      if (this.isGeting) return;
+      this.newsList = [];
+      this.loading = true;
+      this.onload();
+    },
+    onload() {
+      if (this.isGeting) return;
+      let lastId = this.newsList[this.newsList.length - 1];
+      let params = {};
+      lastId && (params.lastId = lastId.id);
+      this.createData(params);
+    },
+    toUrl(item) {
+      let url = item.videoFlag === "V" ? "videoDetail" : "newsDetail";
+      url += ".html?id=" + item.id;
+      window.parent
+        ? (window.parent.location.href = url)
+        : (window.location.href = url);
+    },
+    formData(objData) {
+      let video = Math.random() > 0.5 ? "listItemBig" : "listItem";
+      if (objData.videoFlag === "V") {
+        video = "listItemBig";
+      }
+      return {
+        id: objData.id,
+        url: objData.imgUrl,
+        title: objData.title,
+        orisource: objData.origSource,
+        releaseTime: objData.publishTime,
+        read: objData.read,
+        assemblyName: video,
+        videoFlag: objData.videoFlag,
+        subTitle: objData.normalizedContent
+      };
+    },
+    newList(list) {
+      const viewList = [];
+      list.map(v => {
+        viewList.push(this.formData(v));
+      });
+      return viewList;
+    },
+    createData(options) {
+      this.isGeting = true;
+      let newDate = new Date();
+      if (newDate - date <= 200) {
+        this.isLoading = false;
+        this.loading = false;
+        this.isGeting = false;
+        return;
+      }
+      date = newDate;
+      articleList(options, this.comId).then(res => {
+        const list = this.newList(res || []);
+        this.newsList = [...this.newsList, ...list];
+        this.isLoading = false;
+        this.loading = false;
+        this.isGeting = false;
+      });
+    }
+  },
+  computed: {},
+  mounted() {
+    this.createData({});
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanList,
+    vanSwipeCell,
+    listItem,
+    listItemBig,
+    vanEmpty,
+    vanPullRefresh
+  }
+};
+</script>
+<style lang="scss"></style>

+ 19 - 0
src/config/apiUrl.js

@@ -0,0 +1,19 @@
+let useApiUrl = "http://47.108.160.227:802",
+  isRelease = false; //是否是线上发布版本
+
+if (process.env.NODE_ENV !== "development") {
+  switch (process.env.VUE_APP_MODE) {
+    case "buildDev":
+      useApiUrl = "http://47.108.160.227:8000";
+      break;
+    case "buildTest":
+      useApiUrl = "http://47.108.160.227:802";
+      break;
+    default:
+      useApiUrl = "http://47.108.160.227:802";
+      isRelease = true;
+      break;
+  }
+}
+
+export { useApiUrl, isRelease }; //可以导出更多需要不同环境区分的url

+ 19 - 0
src/config/index.js

@@ -0,0 +1,19 @@
+import { useApiUrl } from "@/config/apiUrl.js";
+
+export default {
+  version: 0.1,
+  timeout: 6000,
+  proTimeout: 1000,
+  apiPrefix: "",
+  requestRetry: 4,
+  requestRetryDelay: 800,
+  tokenKey: "ACCESS_TOKEN",
+  userInfoKey: "USER_INFO",
+  robotBaseUrl: "https://robot.baidu.com",
+  robotBaseWss: "https://robot.baidu.com",
+  clientId: "",
+  apiUrl: useApiUrl,
+  corporation: "广电融媒体",
+  siteName: "",
+  designSize: 375 //设计稿宽度 375,建议使用375,可以和一些主流的ui库兼容。如vant
+};

+ 154 - 0
src/config/page.js

@@ -0,0 +1,154 @@
+//这里做页面参数约定和说明,如果url没有携带requiredParams的参数则无法初始化页面
+module.exports = {
+  robot: {
+    title: "机器人",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  download: {
+    title: "app下载页",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  AppDetail: {
+    title: "app详情",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  leaving: {
+    title: "页面标题leaving",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  integral: {
+    title: "积分列表",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  audioDetail: {
+    title: "音频详情",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  partyStudy: {
+    title: "党校学习",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  pdfDetail: {
+    title: "pdf详情",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  news: {
+    title: "消息",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  about: {
+    title: "关于",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  collection: {
+    title: "收藏",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  myPartybranch: {
+    title: "我的党支部",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  relationshipTransferFrom: {
+    title: "组织关系转移表格",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  relationshipTransfer: {
+    title: "组织关系转移",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  JoinParty: {
+    title: "入党",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  activistAdd: {
+    title: "添加积极分子",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  activist: {
+    title: "积极分子",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  peeList: {
+    title: "党费",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  special: {
+    title: "专题页",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  login: {
+    title: "登录",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  myHome: {
+    title: "我的",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  service: {
+    title: "服务",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  newsDetail: {
+    title: "文章详情",
+    requiredParams: {},
+    optionalParams: {
+      id: "必传项"
+    }
+  },
+  videoDetail: {
+    title: "视频详情",
+    requiredParams: {},
+    optionalParams: {
+      id: "必传项"
+    }
+  },
+  partyHome: {
+    title: "党员之家",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  study: {
+    title: "学习",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  partyAffairs: {
+    title: "党务党训",
+    requiredParams: {},
+    optionalParams: {}
+  },
+  demo: {
+    title: "演示",
+    requiredParams: {},
+    optionalParams: {
+      //必填参数
+      userId: "url必须携带用户Id"
+    }
+  },
+  index: {
+    title: "首页",
+    requiredParams: {}
+  }
+};

+ 346 - 0
src/pages/JoinParty/Index.vue

@@ -0,0 +1,346 @@
+<template>
+  <div id="joinParty" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="入党" left-arrow @click-left="onClickLeft" />
+    </van-sticky>
+    <van-form @submit="onSubmit">
+      <van-field
+        v-model="username"
+        name="用户名"
+        label="用户名"
+        placeholder="用户名"
+        :rules="[{ required: true, message: '请填写用户名' }]"
+      />
+      <van-field name="radio" label="性别">
+        <template #input>
+          <van-radio-group v-model="sex" direction="horizontal">
+            <van-radio name="1">男</van-radio>
+            <van-radio name="0">女</van-radio>
+          </van-radio-group>
+        </template>
+      </van-field>
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="nation"
+        label="民族"
+        placeholder="点击选择民族"
+        @click="showPicker = true"
+      />
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="birth"
+        label="生日"
+        placeholder="点击选择生日"
+        @click="showBirth = true"
+      />
+      <van-field
+        v-model="ID"
+        name="身份证号"
+        label="身份证号"
+        type="digit"
+        placeholder="身份证号"
+        :rules="[{ required: true, message: '请填写身份证号' }]"
+      />
+      <van-field
+        v-model="phone"
+        type="tel"
+        label="联系方式"
+        placeholder="联系方式"
+        :rules="[{ required: true, message: '请填联系方式' }]"
+      />
+      <van-field
+        v-model="add"
+        label="户籍地址"
+        :rules="[{ required: true, message: '请填户籍地址' }]"
+        placeholder="户籍地址"
+      />
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="education"
+        label="文化程度"
+        placeholder="点击选择学历"
+        @click="showEducation = true"
+      />
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="occupation"
+        label="职业"
+        placeholder="点击选择职业"
+        @click="showOccupation = true"
+      />
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="timeWork"
+        label="参加工作时间"
+        placeholder="点击选择时间"
+        @click="showTimeWork = true"
+      />
+      <van-field
+        v-model="post"
+        name="职务"
+        label="职务"
+        placeholder="职务"
+        :rules="[{ required: true, message: '请填写职务' }]"
+      />
+      <van-field
+        v-model="nowAdd"
+        name="现住地"
+        label="现住地"
+        placeholder="现住地"
+        :rules="[{ required: true, message: '请填写现住地' }]"
+      />
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="branch"
+        label="选择支部"
+        placeholder="点击选择支部"
+        @click="showBranch = true"
+      />
+      <van-field
+        v-model="basic"
+        rows="2"
+        autosize
+        label="备注"
+        type="textarea"
+        placeholder="备注"
+        :rules="[{ required: true, message: '请填备注' }]"
+        show-word-limit
+      />
+      <div style="margin: 16px;">
+        <van-button round block type="danger" native-type="submit">
+          提交
+        </van-button>
+      </div>
+    </van-form>
+    <!-- 选择民族 -->
+    <van-popup v-model="showPicker" position="bottom">
+      <van-picker
+        show-toolbar
+        :columns="$nation"
+        @confirm="onConfirm"
+        @cancel="showPicker = false"
+      />
+    </van-popup>
+    <!-- 选择生日 -->
+    <van-popup v-model="showBirth" position="bottom">
+      <van-datetime-picker
+        v-model="oribirth"
+        @cancel="birthCancel"
+        @confirm="birthFunc"
+        :min-date="minDate"
+        type="date"
+        title="选择生日"
+      />
+    </van-popup>
+    <!-- 选择学历 -->
+    <van-popup v-model="showEducation" position="bottom">
+      <van-picker
+        show-toolbar
+        :columns="$education"
+        @confirm="onEducation"
+        @cancel="showEducation = false"
+      />
+    </van-popup>
+    <!-- 选择职业 -->
+    <van-popup v-model="showOccupation" position="bottom">
+      <van-picker
+        show-toolbar
+        :columns="$occupation"
+        @confirm="onOccupation"
+        @cancel="showOccupation = false"
+      />
+    </van-popup>
+    <!-- 选择开始工作日期 -->
+    <van-popup v-model="showTimeWork" position="bottom">
+      <van-datetime-picker
+        v-model="oriTimeWork"
+        @cancel="TimeWorkCancel"
+        @confirm="TimeWorkFunc"
+        :min-date="minDate"
+        type="date"
+        title="选择日期"
+      />
+    </van-popup>
+    <!-- 选择支部 -->
+    <van-popup v-model="showBranch" position="bottom">
+      <van-cascader
+        v-model="branch"
+        title="请选择所在地区"
+        :options="partyList"
+        :field-names="{
+          text: 'name',
+          value: 'id',
+          children: 'children'
+        }"
+        @close="showBranch = false"
+        @finish="branchFunc"
+      />
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import {
+  NavBar as vanNavBar,
+  Button as vanButton,
+  Sticky as vanSticky,
+  Field as vanField,
+  Radio as vanRadio,
+  Popup as vanPopup,
+  Picker as vanPicker,
+  DatetimePicker as vanDatetimePicker,
+  RadioGroup as vanRadioGroup,
+  Form as vanForm,
+  Cascader as vanCascader
+} from "vant";
+import "vant/lib/nav-bar/style";
+import "vant/lib/button/style";
+import "vant/lib/popup/style";
+import "vant/lib/picker/style";
+import "vant/lib/radio/style";
+import "vant/lib/radio-group/style";
+import "vant/lib/form/style";
+import "vant/lib/sticky/style";
+import "vant/lib/field/style";
+import "vant/lib/cascader/style";
+import { list } from "@/api/party/party.js";
+import { toTree } from "@/utils/common.js";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      minDate: new Date("1970-01-01 00:00:00"),
+      showTimeWork: false,
+      showOccupation: false,
+      showEducation: false,
+      showBirth: false,
+      showPicker: false,
+      showBranch: false,
+      branch: "",
+      selectBrach: [],
+      oriTimeWork: "",
+      education: "",
+      post: "",
+      occupation: "",
+      timeWork: "",
+      username: "",
+      nation: "",
+      birth: "",
+      ID: "",
+      oribirth: "",
+      phone: "",
+      add: "",
+      nowAdd: "",
+      sex: "1",
+      basic: "",
+      partyList: []
+    };
+  },
+  methods: {
+    onSubmit() {
+      list().then(res => {
+        console.log(res);
+        this.onClickLeft();
+      });
+    },
+    onClickLeft() {
+      history.go(-1);
+    },
+    onConfirm(value) {
+      this.nation = value;
+      this.showPicker = false;
+    },
+    onEducation(val) {
+      this.showEducation = false;
+      this.education = val;
+    },
+    onOccupation(val) {
+      this.showOccupation = false;
+      this.occupation = val;
+    },
+    birthFunc(val) {
+      this.showBirth = false;
+      let time = new Date(val);
+      this.birth = time.getFullYear() + "-";
+      let month = time.getMonth() + 1;
+      let day = time.getDate();
+      this.birth += month > 9 ? month : "0" + month;
+      this.birth += "-";
+      this.birth += day > 9 ? day : "0" + day;
+    },
+    branchFunc(val) {
+      if (!val.selectedOptions.length) {
+        this.branch = "";
+        this.showBranch = false;
+        return;
+      }
+      this.selectBrach = val.selectedOptions;
+      this.branch = val.selectedOptions
+        .map(v => {
+          return v.name;
+        })
+        .join("-");
+      this.showBranch = false;
+    },
+    birthCancel() {
+      this.showBirth = false;
+      this.birth = "";
+    },
+    TimeWorkFunc(val) {
+      this.showTimeWork = false;
+      let time = new Date(val);
+      this.timeWork = time.getFullYear() + "-";
+      let month = time.getMonth() + 1;
+      let day = time.getDate();
+      this.timeWork += month > 9 ? month : "0" + month;
+      this.timeWork += "-";
+      this.timeWork += day > 9 ? day : "0" + day;
+    },
+    TimeWorkCancel() {
+      this.showTimeWork = false;
+      this.timeWork = "";
+    }
+  },
+  mounted() {
+    list().then(res => {
+      let list = res || [];
+      this.partyList = toTree(list, {
+        pidKey: "parentId",
+        idKey: "id",
+        rootId: 1
+      });
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanSticky,
+    vanNavBar,
+    vanButton,
+    vanForm,
+    vanField,
+    vanPopup,
+    vanRadio,
+    vanPicker,
+    vanDatetimePicker,
+    vanRadioGroup,
+    vanCascader
+  }
+};
+</script>
+<style lang="scss">
+#joinParty {
+  padding-bottom: 2em;
+}
+</style>

+ 107 - 0
src/pages/JoinParty/index.js

@@ -0,0 +1,107 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    // 自定义56个民族
+    Vue.prototype.$nation = [
+      "汉族",
+      "蒙古族",
+      "回族",
+      "藏族",
+      "维吾尔族",
+      "苗族",
+      "彝族",
+      "壮族",
+      "布依族",
+      "朝鲜族",
+      "满族",
+      "侗族",
+      "瑶族",
+      "白族",
+      "土家族",
+      "哈尼族",
+      "哈萨克族",
+      "傣族",
+      "黎族",
+      "傈僳族",
+      "佤族",
+      "畲族",
+      "高山族",
+      "拉祜族",
+      "水族",
+      "东乡族",
+      "纳西族",
+      "景颇族",
+      "柯尔克孜族",
+      "土族",
+      "达斡尔族",
+      "仫佬族",
+      "羌族",
+      "布朗族",
+      "撒拉族",
+      "毛难族",
+      "仡佬族",
+      "锡伯族",
+      "阿昌族",
+      "普米族",
+      "塔吉克族",
+      "怒族",
+      "乌孜别克族",
+      "俄罗斯族",
+      "鄂温克族",
+      "崩龙族",
+      "保安族",
+      "裕固族",
+      "京族",
+      "塔塔尔族",
+      "独龙族",
+      "鄂伦春族",
+      "赫哲族",
+      "门巴族",
+      "珞巴族",
+      "基诺族"
+    ];
+    // 自定义学历
+    Vue.prototype.$education = [
+      "研究生",
+      "大学本科",
+      "大学专科",
+      "中专",
+      "高中",
+      "初中",
+      "小学",
+      "文盲"
+    ];
+    // 自定义学历
+    Vue.prototype.$occupation = [
+      "国家机关/组织/事业单位人员",
+      "农林牧副渔、水利生产人员",
+      "服务业",
+      "现役军人",
+      "销售/采购/客服",
+      "房地产/建筑/物业",
+      "计算机/通信/电子",
+      "财务/金融",
+      "机械制造",
+      "医药/化工/能源",
+      "咨询/法律/教育",
+      "消费品生产",
+      "物流/管理/人力/行政",
+      "市场/媒介/设计",
+      "在校学生",
+      "企业人员",
+      "其它行业"
+    ];
+
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 32 - 0
src/pages/about/Index.vue

@@ -0,0 +1,32 @@
+<template>
+  <div id="about" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar left-arrow @click-left="onClickLeft" title="关于我们" />
+    </van-sticky>
+  </div>
+</template>
+
+<script>
+import { NavBar as vanNavBar, Sticky as vanSticky } from "vant";
+import "vant/lib/sticky/style";
+import "vant/lib/nav-bar/style";
+
+export default {
+  name: "app",
+  data: function() {
+    return {};
+  },
+  methods: {
+    onClickLeft() {
+      history.go(-1);
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanSticky, vanNavBar }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/about/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 98 - 0
src/pages/activistPage/activist/Index.vue

@@ -0,0 +1,98 @@
+<template>
+  <div id="activist" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar left-arrow @click-left="onClickLeft" title="积极分子" />
+    </van-sticky>
+    <van-empty v-if="!activistL.length" description="暂无积极分子" />
+    <van-pull-refresh v-else v-model="isLoading" @refresh="onRefresh">
+      <van-contact-card
+        v-for="item in activistL"
+        :key="item.userId"
+        :name="item.name"
+        :tel="item.phone"
+        type="edit"
+        :editable="false"
+      />
+    </van-pull-refresh>
+    <div class="funcBtn">
+      <van-button type="danger" @click="addActivist" round block>
+        添加积极分子
+      </van-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import {
+  ContactCard as vanContactCard,
+  NavBar as vanNavBar,
+  // Search as vanSearch,
+  Button as vanButton,
+  Sticky as vanSticky,
+  Empty as vanEmpty,
+  PullRefresh as vanPullRefresh
+} from "vant";
+import "vant/lib/contact-card/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/button/style";
+import "vant/lib/sticky/style";
+import "vant/lib/search/style";
+import "vant/lib/empty/style";
+
+import { activistList } from "@/api/activist/activist.js";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      value: "",
+      isLoading: false,
+      activistL: []
+    };
+  },
+  methods: {
+    onRefresh() {
+      this.isLoading = true;
+      this.getApi();
+    },
+    addActivist() {
+      location.href = "./activistAdd.html";
+    },
+    onClickLeft() {
+      history.go(-1);
+    },
+    getApi() {
+      activistList().then(res => {
+        console.log(res);
+        this.isLoading = false;
+        this.activistL = res;
+      });
+    }
+  },
+  mounted() {
+    this.getApi();
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanContactCard,
+    vanNavBar,
+    vanSticky,
+    vanButton,
+    vanEmpty,
+    vanPullRefresh
+  }
+};
+</script>
+<style lang="scss">
+#activist {
+  .funcBtn {
+    position: fixed;
+    width: 90%;
+    left: 5%;
+    bottom: 0.5em;
+  }
+  .custom-image .van-empty__image {
+    width: 90px;
+    height: 90px;
+  }
+}
+</style>

+ 16 - 0
src/pages/activistPage/activist/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 95 - 0
src/pages/activistPage/activistAdd/Index.vue

@@ -0,0 +1,95 @@
+<template>
+  <div id="activistAdd" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="添加积极分子" left-arrow @click-left="onClickLeft" />
+    </van-sticky>
+    <van-form @submit="onSubmit">
+      <van-field
+        v-model="username"
+        name="用户名"
+        label="用户名"
+        placeholder="用户名"
+        :rules="[{ required: true, message: '请填写用户名' }]"
+      />
+      <van-field
+        v-model="phone"
+        type="tel"
+        label="联系方式"
+        placeholder="联系方式"
+        :rules="[{ required: true, message: '请填联系方式' }]"
+      />
+      <van-field
+        v-model="add"
+        label="家庭住址"
+        :rules="[{ required: true, message: '请填家庭住址' }]"
+        placeholder="家庭住址"
+      />
+      <van-field
+        v-model="basic"
+        rows="2"
+        autosize
+        label="基本情况"
+        type="textarea"
+        placeholder="基本情况"
+        :rules="[{ required: true, message: '请填基本情况' }]"
+        show-word-limit
+      />
+      <div style="margin: 16px;">
+        <van-button round block type="danger" native-type="submit">
+          提交
+        </van-button>
+      </div>
+    </van-form>
+  </div>
+</template>
+
+<script>
+import {
+  NavBar as vanNavBar,
+  Button as vanButton,
+  Sticky as vanSticky,
+  Field as vanField,
+  Form as vanForm
+} from "vant";
+import "vant/lib/nav-bar/style";
+import "vant/lib/button/style";
+import "vant/lib/form/style";
+import "vant/lib/sticky/style";
+import "vant/lib/field/style";
+import { activistAdd } from "@/api/activist/activist.js";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      username: "",
+      phone: "",
+      add: "",
+      basic: ""
+    };
+  },
+  methods: {
+    onSubmit() {
+      activistAdd({
+        name: this.username,
+        address: this.add,
+        phone: this.phone,
+        basicInfo: this.basic
+      }).then(res => {
+        console.log(res);
+        this.onClickLeft();
+      });
+    },
+    onClickLeft() {
+      history.go(-1);
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanSticky, vanNavBar, vanButton, vanForm, vanField }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/activistPage/activistAdd/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 101 - 0
src/pages/collection/Index.vue

@@ -0,0 +1,101 @@
+<template>
+  <div id="collection" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="我的收藏" />
+    </van-sticky>
+    <van-pull-refresh v-model="isLoading" @refresh="onRefresh">
+      <van-swipe-cell v-for="item in list" :key="item.id">
+        <van-card
+          @click="() => toUrl(item.id)"
+          :desc="item.normalizedContent"
+          :title="item.title"
+          class="goods-card"
+          :thumb="item.imgUrl"
+        />
+        <template #right>
+          <van-button
+            square
+            @click="() => collection(item.id)"
+            text="取消收藏"
+            type="danger"
+            class="delete-button"
+          />
+        </template>
+      </van-swipe-cell>
+    </van-pull-refresh>
+  </div>
+</template>
+
+<script>
+import {
+  Sticky as vanSticky,
+  NavBar as vanNavBar,
+  Button as vanButton,
+  SwipeCell as vanSwipeCell,
+  Card as vanCard,
+  PullRefresh as vanPullRefresh
+} from "vant";
+import "vant/lib/sticky/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/button/style";
+import "vant/lib/card/style";
+import "vant/lib/pull-refresh/style";
+import "vant/lib/swipe-cell/style";
+import { articleCollect, collectCancel } from "@/api/article/article.js";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      list: [],
+      isLoading: false
+    };
+  },
+  methods: {
+    onClickLeft() {
+      console.log("--");
+      location.href = "/";
+    },
+    toUrl(id) {
+      console.log(id);
+      location.href = "./newsDetail.html?id=" + id;
+    },
+    onRefresh() {
+      articleCollect("").then(res => {
+        this.isLoading = false;
+        this.list = res;
+      });
+    },
+    collection(id) {
+      collectCancel(id).then(() => {
+        articleCollect("").then(res => {
+          console.log(res);
+          this.list = res;
+        });
+      });
+    }
+  },
+  mounted() {
+    articleCollect("").then(res => {
+      console.log(res);
+      this.list = res;
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanSticky,
+    vanNavBar,
+    vanButton,
+    vanSwipeCell,
+    vanCard,
+    vanPullRefresh
+  }
+};
+</script>
+<style lang="scss">
+#collection {
+  .delete-button {
+    height: 100%;
+  }
+}
+</style>

+ 16 - 0
src/pages/collection/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 13 - 0
src/pages/common.js

@@ -0,0 +1,13 @@
+import "normalize.css";
+// import 'vant/lib/index.css';
+import "@/assets/css/style.scss";
+import { setHtmlFontSize } from "@/utils/adaptation.js";
+import { Lazyload } from "vant";
+
+import Vue from "vue";
+
+setHtmlFontSize();
+
+Vue.use(Lazyload, {
+  lazyComponent: true
+});

+ 102 - 0
src/pages/demo/Index.vue

@@ -0,0 +1,102 @@
+<template>
+  <div id="app" class="index">
+    <div>
+      <h2 style="padding: 10px;margin-bottom: 0">
+        全部页面如下:<span style="font-size: 14px">(ctrl+f)查找</span>
+      </h2>
+      <div class="tip">
+        <ul class="tip-content">
+          <li>requiredParams:Url必须携带的参数</li>
+          <li>optionalParams:Url可选携带的参数</li>
+        </ul>
+      </div>
+      <ul class="page-list">
+        <li v-for="(v, name, i) in pageConfig" :key="name">
+          {{ i + 1 }}、<a :href="'./' + name + '.html'"
+            >{{ name }}.html-{{ v.title }}</a
+          >
+          <pre @click="tip(v.requiredParams)">
+requiredParams:{{ v.requiredParams || {} }}</pre
+          >
+          <pre @click="tip(v.optionalParams)">
+optionalParams:{{ v.optionalParams || {} }}</pre
+          >
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script>
+import PageConfig from "@/config/page.js";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      pageConfig: PageConfig
+    };
+  },
+  methods: {
+    tip(o) {
+      alert(JSON.stringify(o));
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: {}
+};
+</script>
+<style lang="scss" scoped>
+* {
+  padding: 0;
+  margin: 0;
+}
+pre {
+  border: 0.02rem dashed #eee;
+  font-size: 14px !important;
+  line-height: 28px;
+  padding: 10px 20px;
+  margin: 20px;
+  overflow-x: auto;
+}
+.page-list {
+  margin-top: 20px;
+  li {
+    border-bottom: 1px solid #ff4242;
+    padding: 6px;
+    margin-bottom: 10px;
+  }
+}
+.myPopup {
+  width: 80%;
+  border-radius: 20px;
+  .myCell {
+    align-items: center;
+  }
+}
+.tip {
+  padding: 10px;
+
+  .tip-content {
+    border-radius: 2px;
+    background: #fafafa;
+    list-style-type: disc;
+    font-size: 12px;
+    color: #969fa9;
+    position: relative;
+    line-height: 26px;
+    padding: 18px 10px 18px 40px;
+    &:after {
+      display: block;
+      content: "";
+      background: #fafafa;
+      width: 12px;
+      height: 12px;
+      position: absolute;
+      top: -6px;
+      transform: rotate(45deg);
+      left: 26px;
+    }
+  }
+}
+</style>

+ 16 - 0
src/pages/demo/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 49 - 0
src/pages/detail/AppDetail/Index.vue

@@ -0,0 +1,49 @@
+<template>
+  <div id="AppDetail" class="app">
+    <component
+      v-if="id"
+      :is="detailData.videoFlag"
+      :item="detailData"
+      :id="id"
+    ></component>
+  </div>
+</template>
+
+<script>
+import A from "@/components/Detail/news.vue";
+import V from "@/components/Detail/video.vue";
+import U from "@/components/Detail/audio.vue";
+import { currentUrlToParams, setToken } from "@/utils/common.js";
+import { articleDetail } from "@/api/article/article.js";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      detailData: {},
+      id: undefined
+    };
+  },
+  methods: {},
+  mounted() {
+    this.$pageParams = currentUrlToParams();
+    this.$pageParams.token && setToken(this.$pageParams.token);
+    window.Progress = function() {
+      return this.progress + "";
+    };
+    articleDetail(this.$pageParams.id, true).then(res => {
+      this.detailData = res || {};
+      this.id = this.$pageParams.id;
+    });
+  },
+  beforeDestroy: function() {},
+  components: { A, V, U }
+};
+</script>
+<style lang="scss">
+html,
+body {
+  height: auto !important;
+  overflow: auto !important;
+}
+</style>

+ 16 - 0
src/pages/detail/AppDetail/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 214 - 0
src/pages/detail/audioDetail/Index.vue

@@ -0,0 +1,214 @@
+<template>
+  <div id="audioDetail" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar :title="audioDetail.title" left-arrow @click-left="goBack">
+      </van-nav-bar>
+    </van-sticky>
+    <van-sticky :offset-top="50">
+      <div class="head">
+        <audio-player
+          ref="audioEle"
+          color="#e42417"
+          @playing="playing"
+          :show-prev-button="false"
+          :show-next-button="false"
+          :audio-list="[audioDetail.videoUrl]"
+          :before-play="onBeforePlay"
+        ></audio-player>
+      </div>
+    </van-sticky>
+    <div v-if="audioDetail.id">
+      <van-cell-group>
+        <van-cell
+          :title="'阅读量: ' + (audioDetail.readCount || 0)"
+          :value="audioDetail.publishTime"
+        />
+        <div
+          @click="showIntrodu = !showIntrodu"
+          :class="{ introduction: true, 'van-ellipsis': !showIntrodu }"
+          v-text="audioDetail.normalizedContent"
+          v-if="audioDetail.normalizedContent"
+        ></div>
+      </van-cell-group>
+    </div>
+    <van-empty v-else description="内容没找到" />
+    <comment-list
+      :commentList="audioDetail.commentList"
+      :isFablous="!!audioDetail.fabulous"
+      :isCollect="!!audioDetail.collection"
+      :diggCount="audioDetail.diggCount"
+      :commentCount="audioDetail.commentCount"
+      v-if="$pageParams"
+      :id="$pageParams.id"
+      :more="more"
+      @changeFab="changeFab"
+      @changeColl="collection"
+      @upset="upset"
+    ></comment-list>
+  </div>
+</template>
+
+<script>
+import {
+  digg,
+  collect,
+  progress,
+  diggCancel,
+  articleDetail,
+  collectCancel,
+  getCommentList
+} from "@/api/article/article.js";
+import CommentList from "../../../components/Comment/Comment";
+import { currentUrlToParams } from "@/utils/common.js";
+import {
+  Cell as vanCell,
+  CellGroup as vanCellGroup,
+  NavBar as vanNavBar,
+  Sticky as vanSticky,
+  Empty as vanEmpty,
+  Toast
+} from "vant";
+import "video.js/dist/video-js.css";
+import "vant/lib/cell-group/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+import "vant/lib/empty/style";
+import "vant/lib/cell/style";
+import "vant/lib/toast/style";
+import "@liripeng/vue-audio-player/lib/vue-audio-player.css";
+import AudioPlayer from "@liripeng/vue-audio-player";
+import Config from "@/config/index.js";
+export default {
+  name: "app",
+  maxPro: 0,
+  data: function() {
+    return {
+      chosenAddressId: "1",
+      showIntrodu: true,
+      audioDetail: {},
+      value: "",
+      more: false,
+      audioList: []
+    };
+  },
+  timeout: null,
+  methods: {
+    goBack() {
+      history.go(-1);
+    },
+    // 播放前做的事
+    onBeforePlay(next) {
+      this.playing();
+      next(); // 开始播放
+    },
+    playing() {
+      const duration = (this.$refs.audioEle.duration * 1000).toFixed(0) - 0;
+      const currentTime =
+        (this.$refs.audioEle.currentTime * 1000).toFixed(0) - 0;
+      let timeout = new Date() - 0;
+      let pro = (currentTime / duration).toFixed(2);
+      if (timeout - this.timeout < Config.proTimeout || pro <= this.maxPro)
+        return;
+      this.timeout = timeout;
+      this.maxPro = pro;
+      progress({
+        articleId: this.$pageParams.id,
+        progress: pro
+      });
+    },
+    collection() {
+      this.$set(this.audioDetail, "collection", !this.audioDetail.collection);
+      this.audioDetail.collection
+        ? collect(this.$pageParams.id).then(() => {
+            Toast("收藏成功!");
+          })
+        : collectCancel(this.$pageParams.id).then(() => {
+            Toast("取消收藏!");
+          });
+    },
+    changeFab(val) {
+      // 点赞
+      this.$set(this.audioDetail, "fabulous", val);
+      val
+        ? digg(this.$pageParams.id).then(() => {
+            Toast("点赞成功!");
+
+            this.$set(
+              this.audioDetail,
+              "diggCount",
+              this.audioDetail.diggCount + 1
+            );
+          })
+        : diggCancel(this.$pageParams.id).then(() => {
+            Toast("取消点赞!");
+            this.$set(
+              this.audioDetail,
+              "diggCount",
+              this.audioDetail.diggCount - 1
+            );
+          });
+    },
+    upset() {
+      let list = this.audioDetail.commentList
+        ? this.audioDetail.commentList
+        : [];
+      this.more = true;
+      let p = {};
+      list.length ? (p.lastId = list[list.length - 1].id) : "";
+      getCommentList(p, this.$pageParams.id).then(res => {
+        this.more = false;
+        list.push(...(res || []));
+        this.$set(this.audioDetail, "commentList", list);
+        this.$set(
+          this.audioDetail,
+          "commentCount",
+          this.audioDetail.commentCount + 1
+        );
+      });
+    }
+  },
+  mounted() {
+    this.$pageParams = currentUrlToParams();
+    this.timeout = new Date();
+    articleDetail(this.$pageParams.id).then(res => {
+      this.audioDetail = res || {};
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanCellGroup,
+    CommentList,
+    AudioPlayer,
+    vanNavBar,
+    vanSticky,
+    vanEmpty,
+    vanCell
+  }
+};
+</script>
+<style lang="scss" scoped>
+#audioDetail {
+  .van-button {
+    border: none;
+  }
+  .video-js {
+    .vjs-big-play-button {
+      top: 50%;
+      left: 50%;
+    }
+  }
+  .introduction {
+    padding: 0.5em;
+    font-size: 0.14rem;
+    color: #969799;
+    text-indent: 2em;
+  }
+  .head {
+    padding: 10em 2em 2em 2em;
+  }
+
+  .selectText {
+    color: #ee0a24;
+  }
+}
+</style>

+ 16 - 0
src/pages/detail/audioDetail/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 210 - 0
src/pages/detail/newsDetail/Index.vue

@@ -0,0 +1,210 @@
+<template>
+  <div id="newsDetail" class="app" ref="croll">
+    <van-sticky :offset-top="0">
+      <van-nav-bar
+        :title="detailData.publishTime"
+        left-text="返回"
+        left-arrow
+        @click-left="goBack"
+      >
+      </van-nav-bar>
+    </van-sticky>
+    <div class="content" ref="content">
+      <div class="grid-cell">
+        <div class="render-detail-article">
+          <div class="render-detail-title" v-text="detailData.title"></div>
+          <span v-text="detailData.origSource"></span>
+          <div class="contentDetail" v-html="detailData.content"></div>
+        </div>
+      </div>
+    </div>
+    <comment-list
+      :commentList="detailData.commentList"
+      :isFablous="!!detailData.fabulous"
+      :isCollect="!!detailData.collect"
+      v-if="$pageParams"
+      :id="$pageParams.id"
+      :more="more"
+      :diggCount="detailData.diggCount"
+      :readCount="detailData.readCount"
+      :commentCount="detailData.commentCount"
+      @changeFab="changeFab"
+      @changeColl="collection"
+      @upset="upset"
+    ></comment-list>
+  </div>
+</template>
+
+<script>
+import {
+  digg,
+  collect,
+  progress,
+  diggCancel,
+  articleDetail,
+  collectCancel,
+  getCommentList
+} from "@/api/article/article.js";
+import { currentUrlToParams } from "@/utils/common.js";
+
+import CommentList from "../../../components/Comment/Comment";
+
+import { NavBar as vanNavBar, Sticky as vanSticky, Toast } from "vant";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+import "vant/lib/toast/style";
+import "vant/lib/field/style";
+import Config from "@/config/index.js";
+
+export default {
+  name: "app",
+  maxPro: 0,
+  timeout: null,
+  data: function() {
+    return {
+      detailData: {},
+      more: false
+    };
+  },
+  methods: {
+    goBack() {
+      history.go(-1);
+    },
+    collection() {
+      this.$set(this.detailData, "collect", !this.detailData.collect);
+      this.detailData.collect
+        ? collect(this.$pageParams.id).then(() => {
+            Toast("收藏成功!");
+          })
+        : collectCancel(this.$pageParams.id).then(() => {
+            Toast("取消收藏!");
+          });
+    },
+    setProgress() {
+      if (!this.$refs.croll) return;
+      let timeout = new Date() - 0;
+      let scrollTop = this.$refs.croll.scrollTop;
+      let height =
+        this.$refs.content.offsetHeight + 70 - this.$refs.croll.clientHeight;
+      let pro = (scrollTop / height).toFixed(2);
+      pro = pro > 1 ? "1" : pro;
+      pro = height <= 0 ? "1" : pro;
+      if (
+        (height > 0 && timeout - this.timeout < Config.proTimeout) ||
+        pro <= this.maxPro
+      )
+        return;
+      this.maxPro = pro;
+      this.timeout = timeout;
+      progress({
+        articleId: this.$pageParams.id,
+        progress: pro
+      });
+    },
+    changeFab(val) {
+      // 点赞
+      this.$set(this.detailData, "fabulous", val);
+      val
+        ? digg(this.$pageParams.id).then(() => {
+            Toast("点赞成功!");
+            this.$set(
+              this.detailData,
+              "diggCount",
+              this.detailData.diggCount + 1
+            );
+          })
+        : diggCancel(this.$pageParams.id).then(() => {
+            Toast("取消点赞!");
+            this.$set(
+              this.detailData,
+              "diggCount",
+              this.detailData.diggCount - 1
+            );
+          });
+    },
+    upset() {
+      let list = this.detailData.commentList ? this.detailData.commentList : [];
+      this.more = true;
+      let p = {};
+      list.length ? (p.lastId = list[list.length - 1].id) : "";
+      getCommentList(p, this.$pageParams.id).then(res => {
+        this.more = false;
+        list.push(...(res || []));
+        this.$set(this.detailData, "commentList", list);
+        this.$set(
+          this.detailData,
+          "commentCount",
+          this.detailData.commentCount + 1
+        );
+      });
+    }
+  },
+  mounted() {
+    this.$pageParams = currentUrlToParams();
+    articleDetail(this.$pageParams.id).then(res => {
+      this.detailData = res || {};
+      this.timeout = new Date() - 1001;
+      window.addEventListener("scroll", this.setProgress, true);
+      this.$nextTick(() => {
+        this.setProgress();
+      });
+    });
+  },
+  beforeDestroy: function() {
+    // 销毁之前
+    this.setProgress();
+    clearTimeout(this.timeout);
+  },
+  components: {
+    vanNavBar,
+    vanSticky,
+    CommentList
+  }
+};
+</script>
+<style lang="scss">
+#newsDetail {
+  padding: 0.1rem;
+  color: #262626;
+  font-family: 微软雅黑, Microsoft YaHei, PingFangSC;
+  overflow-y: scroll;
+  font-size: 16px;
+  font-weight: 450;
+  height: 100%;
+  .van-button {
+    border: none;
+  }
+  .render-detail-article .render-detail-title {
+    text-align: center;
+    font-size: 0.2rem;
+    letter-spacing: 0;
+    line-height: 1.2em;
+    font-weight: 500;
+    margin: 0.05rem auto;
+  }
+  .contentDetail {
+    p {
+      min-height: 0.3rem;
+      line-height: 1.5em;
+      text-indent: 2em;
+      margin: 0.2rem 0;
+      padding-left: 0.05rem;
+      strong {
+        font-weight: 450;
+      }
+      .render-detail-footer {
+        text-align: right;
+        padding-right: 5px;
+      }
+    }
+
+    img {
+      width: 100%;
+    }
+  }
+
+  .selectText {
+    color: #ee0a24;
+  }
+}
+</style>

+ 16 - 0
src/pages/detail/newsDetail/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 65 - 0
src/pages/detail/pdfDetail/Index.vue

@@ -0,0 +1,65 @@
+<template>
+  <div id="pdfDetail" class="app">
+    <van-sticky :offset-top="0" v-if="$pageParams">
+      <van-nav-bar
+        left-arrow
+        @click-left="onClickLeft"
+        :title="$pageParams.name"
+      />
+    </van-sticky>
+    <pdf
+      v-for="i in numPages"
+      :key="i"
+      :src="pdfLink"
+      :page="i"
+      style="width: 100%"
+    ></pdf>
+  </div>
+</template>
+
+<script>
+import pdf from "vue-pdf";
+import { currentUrlToParams } from "@/utils/common.js";
+
+import { NavBar as vanNavBar, Sticky as vanSticky } from "vant";
+import "vant/lib/sticky/style";
+import "vant/lib/nav-bar/style";
+const vuePdf = pdf.createLoadingTask(
+  "https://zhihui-dangjian.oss-cn-chengdu.aliyuncs.com/pdf/%E4%B8%AD%E5%8D%8E%E9%AD%82%E6%99%BA%E6%85%A7%E5%85%9A%E5%BB%BA%E5%B9%B3%E5%8F%B0.pdf"
+);
+export default {
+  name: "app",
+  data: function() {
+    return {
+      loadingPdf: true,
+      pdfLink: vuePdf,
+      numPages: undefined,
+      user: {}
+    };
+  },
+  methods: {
+    onClickLeft() {
+      history.go(-1);
+    }
+  },
+  mounted() {
+    this.$pageParams = currentUrlToParams();
+    console.log(this.$pageParams.name);
+    this.pdfLink.promise.then(p => {
+      console.log(p);
+      this.loadingPdf = false;
+      this.numPages = p.numPages;
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    pdf,
+    vanNavBar,
+    vanSticky
+  }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/detail/pdfDetail/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 236 - 0
src/pages/detail/videoDetail/Index.vue

@@ -0,0 +1,236 @@
+<template>
+  <div id="videoDetail" class="app">
+    <van-sticky :offset-top="0" v-if="videoDetail.videoFlag === 'V'">
+      <video-player
+        class="video-player-box"
+        ref="videoPlayer"
+        :options="playerOptions"
+        :playsinline="true"
+        customEventName="customstatechangedeventname"
+        @play="onPlayerPlay($event)"
+        @pause="onPlayerPause($event)"
+        @ended="onPlayerEnded($event)"
+        @waiting="onPlayerWaiting($event)"
+        @playing="onPlayerPlaying($event)"
+        @loadeddata="onPlayerLoadeddata($event)"
+        @timeupdate="onPlayerTimeupdate($event)"
+        @canplay="onPlayerCanplay($event)"
+        @canplaythrough="onPlayerCanplaythrough($event)"
+        @statechanged="playerStateChanged($event)"
+        @ready="playerReadied"
+      >
+      </video-player>
+    </van-sticky>
+    <div v-if="videoDetail.id">
+      <van-cell-group>
+        <van-cell :title="videoDetail.title" :value="videoDetail.publishTime" />
+        <div
+          @click="showIntrodu = !showIntrodu"
+          :class="{ introduction: true, 'van-ellipsis': !showIntrodu }"
+          v-text="videoDetail.normalizedContent"
+          v-if="videoDetail.normalizedContent"
+        ></div>
+      </van-cell-group>
+    </div>
+    <van-empty v-else description="内容没找到" />
+    <comment-list
+      :commentList="videoDetail.commentList"
+      :isFablous="!!videoDetail.fabulous"
+      :isCollect="!!videoDetail.collection"
+      :diggCount="videoDetail.diggCount"
+      :readCount="videoDetail.readCount"
+      :commentCount="videoDetail.commentCount"
+      v-if="$pageParams"
+      :id="$pageParams.id"
+      :more="more"
+      @changeFab="changeFab"
+      @changeColl="collection"
+      @upset="upset"
+    ></comment-list>
+  </div>
+</template>
+
+<script>
+import {
+  articleDetail,
+  collectCancel,
+  getCommentList,
+  diggCancel,
+  progress,
+  collect,
+  digg
+} from "@/api/article/article.js";
+import { currentUrlToParams } from "@/utils/common.js";
+import {
+  Cell as vanCell,
+  CellGroup as vanCellGroup,
+  Sticky as vanSticky,
+  Empty as vanEmpty,
+  Toast
+} from "vant";
+import "vant/lib/cell-group/style";
+import "vant/lib/sticky/style";
+import "vant/lib/empty/style";
+import "vant/lib/toast/style";
+import "vant/lib/cell/style";
+import "video.js/dist/video-js.css";
+import { videoPlayer } from "vue-video-player";
+import CommentList from "../../../components/Comment/Comment";
+
+import Config from "@/config/index.js";
+
+export default {
+  name: "app",
+  maxPro: 0,
+  data: function() {
+    return {
+      showIntrodu: true,
+      videoDetail: {},
+      playerOptions: {},
+      more: false
+    };
+  },
+  timeout: null,
+  methods: {
+    onPlayerPlay() {
+      console.log("开始播放");
+    },
+    onPlayerPause() {
+      console.log("暂停播放");
+    },
+    onPlayerEnded() {
+      console.log("结束播放");
+    },
+    onPlayerWaiting() {
+      console.log("等待播放");
+    },
+    onPlayerPlaying() {
+      console.log("播放中");
+    },
+    onPlayerLoadeddata() {
+      console.log("加载结束");
+    },
+    onPlayerTimeupdate(e) {
+      let timeout = new Date() - 0;
+      let pro = (e.cache_.currentTime / e.cache_.duration).toFixed(2);
+      if (timeout - this.timeout < Config.proTimeout || pro <= this.maxPro)
+        return;
+      this.timeout = timeout;
+      this.maxPro = pro;
+      progress({
+        articleId: this.$pageParams.id,
+        progress: pro
+      });
+    },
+    onPlayerCanplay() {
+      console.log("允许播放");
+    },
+    onPlayerCanplaythrough() {
+      console.log("允许播放通过");
+    },
+    playerStateChanged() {
+      console.log("播放状态改变");
+    },
+    playerReadied() {
+      console.log("准备好");
+    },
+    collection() {
+      let collectData = this.videoDetail.collection;
+      this.$set(this.videoDetail, "collection", !collectData);
+      !collectData
+        ? collect(this.$pageParams.id).then(() => {
+            Toast("收藏成功!");
+          })
+        : collectCancel(this.$pageParams.id).then(() => {
+            Toast("取消收藏!");
+          });
+    },
+    changeFab(val) {
+      // 点赞
+      this.$set(this.videoDetail, "fabulous", val);
+      val
+        ? digg(this.$pageParams.id).then(() => {
+            Toast("点赞成功!");
+            this.$set(
+              this.videoDetail,
+              "diggCount",
+              this.videoDetail.diggCount + 1
+            );
+          })
+        : diggCancel(this.$pageParams.id).then(() => {
+            Toast("取消点赞!");
+            this.$set(
+              this.videoDetail,
+              "diggCount",
+              this.videoDetail.diggCount - 1
+            );
+          });
+    },
+    upset() {
+      let list = this.videoDetail.commentList
+        ? this.videoDetail.commentList
+        : [];
+      this.more = true;
+      let p = {};
+      list.length ? (p.lastId = list[list.length - 1].id) : "";
+      getCommentList(p, this.$pageParams.id).then(res => {
+        this.more = false;
+        list.push(...(res || []));
+        this.$set(this.videoDetail, "commentList", list);
+        this.$set(
+          this.videoDetail,
+          "commentCount",
+          this.videoDetail.commentCount + 1
+        );
+      });
+    }
+  },
+  mounted() {
+    this.$pageParams = currentUrlToParams();
+    articleDetail(this.$pageParams.id).then(res => {
+      this.videoDetail = res || {};
+      this.playerOptions = {
+        // videojs options
+        muted: true,
+        width: "100%",
+        aspectRatio: "16:9",
+        language: "zh-CN",
+        playbackRates: [0.5, 1.0, 1.5, 2.0],
+        sources: [
+          {
+            type: "video/mp4",
+            src: this.videoDetail.videoUrl
+          }
+        ],
+        poster: "/static/images/author.jpg"
+      };
+      this.timeout = new Date() - 0;
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    videoPlayer,
+    vanCell,
+    vanCellGroup,
+    CommentList,
+    vanSticky,
+    vanEmpty
+  }
+};
+</script>
+<style lang="scss" scoped>
+#videoDetail {
+  .video-js {
+    .vjs-big-play-button {
+      top: 50%;
+      left: 50%;
+    }
+  }
+  .introduction {
+    padding: 0.5em;
+    font-size: 0.14rem;
+    color: #969799;
+    text-indent: 2em;
+  }
+}
+</style>

+ 16 - 0
src/pages/detail/videoDetail/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage()//这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 56 - 0
src/pages/download/Index.vue

@@ -0,0 +1,56 @@
+<template>
+  <div id="download" class="app">
+    <img src="../../assets/image/down.jpg" alt="" />
+    <div class="button">
+      <van-button
+        type="danger"
+        style="border-color: #f51919; background-color: #f51919"
+        round
+        hairline
+        block
+        >下载</van-button
+      >
+    </div>
+  </div>
+</template>
+
+<script>
+import { Button as vanButton } from "vant";
+import "vant/lib/button/style";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      isAndroid: true,
+      isWindows: false,
+      isIphone: false
+    };
+  },
+  methods: {},
+  mounted() {
+    const sys = window.navigator.userAgent;
+    this.isAndroid = /Android/g.test(sys);
+    this.isWindows = /Windows/g.test(sys);
+    if (/iPhone/g.test(sys)) {
+      window.location.href =
+        "itms-apps://itunes.apple.com/app/id1563693329?action=write-review";
+    }
+  },
+  beforeDestroy: function() {},
+  components: { vanButton }
+};
+</script>
+<style lang="scss">
+#download {
+  max-width: 750px;
+  margin: 0 auto;
+  .button {
+    position: fixed;
+    bottom: 0;
+    padding: 1em 2em;
+    width: 100%;
+    background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
+  }
+}
+</style>

+ 16 - 0
src/pages/download/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 65 - 0
src/pages/index/Index.vue

@@ -0,0 +1,65 @@
+<template>
+  <div id="app" class="app">
+    <div class="body">
+      <iframe :src="`./${active}.html`" frameborder="0"></iframe>
+    </div>
+    <van-tabbar
+      v-model="active"
+      active-color="#e42417"
+      inactive-color="#aab3ba"
+      @change="change"
+    >
+      <van-tabbar-item name="partyAffairs" icon="newspaper-o">
+        首页
+      </van-tabbar-item>
+      <van-tabbar-item name="study" icon="notes-o">推荐</van-tabbar-item>
+      <van-tabbar-item name="partyHome" icon="records">
+        移动党校
+      </van-tabbar-item>
+      <van-tabbar-item name="service" icon="orders-o">
+        服务
+      </van-tabbar-item>
+      <van-tabbar-item name="myHome" icon="home-o">
+        我的
+      </van-tabbar-item>
+    </van-tabbar>
+  </div>
+</template>
+
+<script>
+import { Tabbar as vanTabbar, TabbarItem as vanTabbarItem } from "vant";
+import "vant/lib/tabbar-item/style";
+import "vant/lib/tabbar/style";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      active: "study"
+    };
+  },
+  created() {
+    if (sessionStorage.active) this.active = sessionStorage.active;
+  },
+  methods: {
+    change() {
+      sessionStorage.active = this.active;
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanTabbar, vanTabbarItem }
+};
+</script>
+<style lang="scss">
+.app {
+  .body {
+    width: 100%;
+    height: 100%;
+    padding-bottom: 50px;
+    iframe {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+</style>

+ 16 - 0
src/pages/index/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.error(e);
+  });

+ 99 - 0
src/pages/integral/Index.vue

@@ -0,0 +1,99 @@
+<template>
+  <div id="integral" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar
+        left-text="返回"
+        left-arrow
+        @click-left="goBack"
+        title="历史记录"
+      />
+    </van-sticky>
+    <div
+      v-for="item in readList"
+      :key="'read' + item.id"
+      @click="() => toUrl(item)"
+    >
+      <keep-alive>
+        <component :is="item.assemblyName" :item="item"></component>
+      </keep-alive>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getReadHistory } from "@/api/article/article.js";
+
+import listItemBig from "../../components/ListItem/ListItemBig";
+import listItem from "../../components/ListItem/ListItem";
+import { NavBar as vanNavBar, Sticky as vanSticky } from "vant";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      readList: []
+    };
+  },
+  methods: {
+    toUrl(item) {
+      let url =
+        item.videoFlag === "V"
+          ? "videoDetail"
+          : item.videoFlag === "A"
+          ? "newsDetail"
+          : "audioDetail";
+      url += ".html?id=" + item.id;
+      window.parent
+        ? (window.parent.location.href = url)
+        : (window.location.href = url);
+    },
+    goBack() {
+      history.go(-1);
+    },
+    formData(objData) {
+      let video = Math.random() > 0.5 ? "listItemBig" : "listItem";
+      if (objData.videoFlag === "V") {
+        video = "listItemBig";
+      }
+      return {
+        id: objData.id,
+        url: objData.imgUrl,
+        title: objData.title,
+        orisource: objData.origSource,
+        releaseTime: objData.publishTime,
+        progress: objData.progress,
+        read: objData.read,
+        assemblyName: video,
+        videoFlag: objData.videoFlag,
+        subTitle: objData.normalizedContent
+      };
+    },
+    newList(list) {
+      const viewList = [];
+      list.map(v => {
+        viewList.push(this.formData(v));
+      });
+      return viewList;
+    },
+    getRead() {
+      getReadHistory().then(res => {
+        const list = this.newList(res || []);
+        this.readList = [...this.readList, ...list];
+        this.isLoadingReadList = false;
+        this.loadingReadList = false;
+        if ((res && res.length === 0) || !res) this.finishedReadList = true;
+      });
+    }
+  },
+  mounted() {
+    this.getRead();
+  },
+  beforeDestroy: function() {},
+  components: { listItemBig, listItem, vanNavBar, vanSticky }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/integral/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 122 - 0
src/pages/leaving/Index.vue

@@ -0,0 +1,122 @@
+<template>
+  <div id="leaving" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar left-arrow @click-left="onClickLeft" title="留言板" />
+    </van-sticky>
+    <van-tabs v-model="active">
+      <van-tab title="发布留言">
+        <van-field
+          v-model="message"
+          rows="15"
+          autosize
+          type="textarea"
+          placeholder="请输入留言"
+        />
+        <div class="btn">
+          <van-button type="danger" round block @click="addComment">
+            提交
+          </van-button>
+        </div>
+      </van-tab>
+      <van-tab title="留言簿">
+        <van-pull-refresh v-model="isLoading" @refresh="onRefresh">
+          <van-cell-group>
+            <van-cell
+              v-for="item in list"
+              :key="item.id"
+              :title="item.nickName"
+              :value="item.createTime"
+              :label="item.content"
+            />
+          </van-cell-group>
+        </van-pull-refresh>
+      </van-tab>
+    </van-tabs>
+  </div>
+</template>
+
+<script>
+import {
+  PullRefresh as vanPullRefresh,
+  CellGroup as vanCellGroup,
+  NavBar as vanNavBar,
+  Button as vanButton,
+  Sticky as vanSticky,
+  Field as vanField,
+  Tabs as vanTabs,
+  Cell as vanCell,
+  Tab as vanTab,
+  Toast
+} from "vant";
+import "vant/lib/pull-refresh/style";
+import "vant/lib/cell-group/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/button/style";
+import "vant/lib/sticky/style";
+import "vant/lib/field/style";
+import "vant/lib/tabs/style";
+import "vant/lib/toast/style";
+import "vant/lib/cell/style";
+import "vant/lib/tab/style";
+import { addComment, lookComment } from "@/api/party/party.js";
+export default {
+  name: "leaving",
+  data: function() {
+    return {
+      active: 1,
+      message: "",
+      isLoading: false,
+      list: []
+    };
+  },
+  methods: {
+    onClickLeft() {
+      history.go(-1);
+    },
+    onRefresh() {},
+    onLoad() {},
+    addComment() {
+      addComment({ content: this.message }).then(() => {
+        this.list = [];
+        this.message = "";
+        Toast("提交成功!");
+        this.getList();
+      });
+    },
+    getList(id) {
+      lookComment(id).then(res => {
+        let r = res || { childCommentList: [], commentList: [] };
+        let list = [...r.childCommentList, ...r.commentList];
+        this.list.push(...list);
+        this.isLoading = false;
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanPullRefresh,
+    vanCellGroup,
+    vanSticky,
+    vanNavBar,
+    vanButton,
+    vanField,
+    vanTabs,
+    vanCell,
+    vanTab
+  }
+};
+</script>
+<style lang="scss">
+#leaving {
+  height: 100%;
+  overflow: hidden;
+  overflow-y: scroll;
+  .btn {
+    width: 100%;
+    padding: 2em;
+  }
+}
+</style>

+ 16 - 0
src/pages/leaving/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 111 - 0
src/pages/login/Index.vue

@@ -0,0 +1,111 @@
+<template>
+  <div id="login" class="app">
+    <van-image :src="require('../../assets/image/0.jpg')" />
+    <van-form @submit="onSubmit">
+      <van-field
+        v-model="username"
+        name="手机号"
+        label="手机号"
+        :rules="[{ required: true, message: '请填写手机号' }]"
+      >
+        <template #button>
+          <van-button
+            size="small"
+            v-if="!code"
+            :disabled="!username"
+            @click="getCodes"
+          >
+            发送验证码
+          </van-button>
+          <van-count-down v-else format="ss" @finish="endTime" :time="60000" />
+        </template>
+      </van-field>
+      <van-field
+        v-model="password"
+        center
+        clearable
+        label="短信验证码"
+        :rules="[{ required: true, message: '请填写验证码' }]"
+      >
+      </van-field>
+      <div style="margin: 16px;">
+        <van-button round block type="danger" native-type="submit">
+          提交
+        </van-button>
+      </div>
+    </van-form>
+  </div>
+</template>
+
+<script>
+import {
+  Form as vanForm,
+  Button as vanButton,
+  Field as vanField,
+  CountDown as vanCountDown,
+  Image as vanImage
+} from "vant";
+import { login, getCode } from "@/api/user/user.js";
+import "vant/lib/button/style";
+import "vant/lib/field/style";
+import "vant/lib/form/style";
+import { getToken, setToken, setUserInfo } from "@/utils/common.js";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      username: "",
+      password: "",
+      code: false
+    };
+  },
+  created() {
+    if (getToken()) location.replace("./index.html");
+  },
+  methods: {
+    endTime() {
+      this.code = false;
+    },
+    getCodes() {
+      getCode({
+        phone: this.username
+      })
+        .then(() => {
+          this.code = true;
+        })
+        .catch(() => {
+          // console.log(err);
+        });
+    },
+    onSubmit() {
+      login({
+        phone: this.username,
+        code: this.password
+      }).then(res => {
+        setToken(res.accessToken);
+        setUserInfo({
+          userName: res.nickName
+        });
+        location.href = "./index.html";
+      });
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanForm, vanButton, vanField, vanCountDown, vanImage }
+};
+</script>
+<style lang="scss">
+#login {
+  height: 100%;
+  .van-image {
+    margin: 5em auto;
+    width: 5em;
+    display: block;
+  }
+  .van-form {
+    height: 100%;
+  }
+}
+</style>

+ 16 - 0
src/pages/login/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 135 - 0
src/pages/myHome/Index.vue

@@ -0,0 +1,135 @@
+<template>
+  <div id="myHome" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="我的" />
+    </van-sticky>
+    <div class="userData">
+      <van-image
+        class="bannerImage"
+        round
+        fit="cover"
+        :src="require('../../assets/image/partySchool.jpg')"
+      />
+      <div class="user">
+        <p class="userName" v-text="userInfo.userName || '游客'"></p>
+        <p class="van-ellipsis userSubName">积分:{{ userInfo.score }}</p>
+      </div>
+    </div>
+    <van-cell-group>
+      <van-cell
+        title="我的支部"
+        @click="() => toUrl('myPartybranch')"
+        clickable
+        is-link
+      />
+      <!-- <van-cell title="消息" @click="() => toUrl('news')" clickable is-link /> -->
+      <van-cell
+        title="收藏"
+        @click="() => toUrl('collection')"
+        clickable
+        is-link
+      />
+      <van-cell
+        title="历史记录"
+        @click="() => toUrl('integral')"
+        clickable
+        is-link
+      />
+      <van-cell title="分享" @click="showShare = true" clickable is-link />
+      <van-cell title="关于" @click="() => toUrl('about')" clickable is-link />
+      <!-- <van-cell title="设置" @click="() => toUrl('5')" clickable is-link /> -->
+    </van-cell-group>
+    <van-share-sheet
+      v-model="showShare"
+      title="立即分享给好友"
+      :options="options"
+      @select="onSelect"
+    />
+  </div>
+</template>
+
+<script>
+import { getUserInfo, setUserInfo } from "@/utils/common.js";
+import {
+  NavBar as vanNavBar,
+  Sticky as vanSticky,
+  Image as VanImage,
+  Cell as vanCell,
+  CellGroup as vanCellGroup,
+  ShareSheet as vanShareSheet
+} from "vant";
+import "vant/lib/cell-group/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/share-sheet/style";
+import "vant/lib/sticky/style";
+import "vant/lib/image/style";
+import "vant/lib/cell/style";
+import { getUser } from "@/api/user/user.js";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      userInfo: {},
+      showShare: false,
+      options: [
+        { name: "微信", icon: "wechat" },
+        { name: "微博", icon: "weibo" },
+        { name: "复制链接", icon: "link" }
+      ]
+    };
+  },
+  methods: {
+    toUrl(id) {
+      if (!isNaN(Number(id))) return;
+      window.parent
+        ? (window.parent.location = id + ".html")
+        : (location = id + ".html");
+    },
+    onSelect() {
+      this.showShare = false;
+    }
+  },
+  mounted() {
+    this.userInfo = getUserInfo();
+    getUser().then(res => {
+      this.userInfo.score = (res || {}).score;
+      setUserInfo({
+        userName: this.userInfo.userName,
+        score: this.userInfo.score
+      });
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanNavBar,
+    vanSticky,
+    vanCell,
+    vanCellGroup,
+    VanImage,
+    vanShareSheet
+  }
+};
+</script>
+<style lang="scss" scoped>
+#myHome {
+  .userData {
+    padding: 1em;
+    .bannerImage {
+      width: 3em;
+      height: 3em;
+      margin-right: 1em;
+      vertical-align: middle;
+    }
+    .user {
+      display: inline-block;
+      vertical-align: middle;
+      font-size: 0.2rem;
+      .userSubName {
+        margin-top: 0.5em;
+        font-size: 0.12rem;
+        color: #aab3ba;
+      }
+    }
+  }
+}
+</style>

+ 16 - 0
src/pages/myHome/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 133 - 0
src/pages/myPartybranch/Index.vue

@@ -0,0 +1,133 @@
+<template>
+  <div id="myPartybranch" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="我的党支部" left-arrow @click-left="goBack" />
+    </van-sticky>
+    <van-tabs v-model="active">
+      <van-tab title="支部介绍" class="text">
+        <p>技术党总支工作目标: &zwj;</p>
+        <p>
+          不断提升技术研发能力、服务生产经营,围绕企业战略,充分发挥战斗堡垒及先锋模范作用,建立先进的创新型党组织。
+        </p>
+        <p>支部建设目标:</p>
+        <p>
+          1、打造一支队伍。以党的建设总要求为指引,加强支委会引领作用,丰富党支部日常组织生活,打造一支特别能吃苦、特别能战斗的党员队伍。
+        </p>
+        <p>
+          2、建设一个品牌。围绕公司转型发展目标,发挥党员引领作用,带头落实研发部业务工作计划,做好做精传统产品研发及质量改进、积极推进新能源业务开拓和智能化发动机研究,树立一个创新型的支部品牌。
+        </p>
+        <p>
+          3、架设一座桥梁。通过支部开展形势任务宣传教育和支部日常活动,发挥好党支部在上级党组织与党员、党员与群众之间的桥梁与纽带作用,每年开展党支部凝聚力活动,提升党支部活力及影响力。有序开展党员发展工作,吸收优秀青年员工加入党组织。
+        </p>
+      </van-tab>
+      <van-tab title="班子成员">
+        <van-row>
+          <van-col class="head" span="12">姓名</van-col>
+          <van-col class="head" span="12">党内职务名称</van-col>
+        </van-row>
+        <van-row v-for="item in memberParty" :key="item.id">
+          <van-col span="12" v-text="item.name"></van-col>
+          <van-col span="12" v-text="item.position"></van-col>
+        </van-row>
+      </van-tab>
+      <van-tab title="支部成员">
+        <van-row v-for="item in listParty" :key="item.id">
+          <van-col
+            span="12"
+            v-for="it in item"
+            :key="it.id"
+            v-text="it.name"
+          ></van-col>
+        </van-row>
+      </van-tab>
+      <!-- <van-tab title="积分排行">
+        <van-row>
+          <van-col class="head" span="8">名次</van-col>
+          <van-col class="head" span="8">名称</van-col>
+          <van-col class="head" span="8">积分</van-col>
+        </van-row>
+        <van-row>
+          <van-col span="8">1</van-col>
+          <van-col span="8">黄华</van-col>
+          <van-col span="8">100</van-col>
+        </van-row>
+      </van-tab> -->
+    </van-tabs>
+  </div>
+</template>
+
+<script>
+import {
+  NavBar as vanNavBar,
+  Sticky as vanSticky,
+  Tab as vanTab,
+  Tabs as vanTabs,
+  Col as vanCol,
+  Row as vanRow
+} from "vant";
+import "vant/lib/sticky/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/tab/style";
+import "vant/lib/tabs/style";
+import "vant/lib/row/style";
+import "vant/lib/col/style";
+
+import { memberParty, userParty } from "@/api/party/party.js";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      active: "",
+      memberParty: [],
+      listParty: []
+    };
+  },
+  methods: {
+    goBack() {
+      history.go(-1);
+    },
+    forMath(list) {
+      let li = [];
+      list.map((v, i) => {
+        i % 2 === 0 && (li[li.length] = [v]);
+        i % 2 !== 0 && li[li.length - 1].push(v);
+      });
+      return li;
+    }
+  },
+  mounted() {
+    memberParty().then(res => {
+      this.memberParty = res || [];
+    });
+    userParty().then(res => {
+      this.listParty = this.forMath(res);
+    });
+  },
+  beforeDestroy: function() {},
+  components: { vanSticky, vanNavBar, vanTabs, vanTab, vanCol, vanRow }
+};
+</script>
+<style lang="scss">
+#myPartybranch {
+  color: #969fa9;
+  .head {
+    background-color: #eee;
+  }
+  .van-col {
+    text-align: center;
+    height: 2.5em;
+    line-height: 2.5em;
+  }
+  .text {
+    padding: 1em;
+    p {
+      text-indent: 2em;
+      min-height: 1.5em;
+      line-height: 1.5em;
+    }
+  }
+  .van-tab__pane {
+    padding-top: 1em;
+  }
+}
+</style>

+ 16 - 0
src/pages/myPartybranch/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 41 - 0
src/pages/news/Index.vue

@@ -0,0 +1,41 @@
+<template>
+  <div id="news" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar left-arrow @click-left="onClickLeft" title="消息" />
+    </van-sticky>
+    <van-card
+      desc="你在干嘛"
+      title="张三的家"
+      thumb="https://img01.yzcdn.cn/vant/ipad.jpeg"
+    />
+  </div>
+</template>
+
+<script>
+import {
+  NavBar as vanNavBar,
+  Sticky as vanSticky,
+  Card as vanCard
+} from "vant";
+import "vant/lib/sticky/style";
+import "vant/lib/card/style";
+import "vant/lib/nav-bar/style";
+export default {
+  name: "app",
+  data: function() {
+    return {};
+  },
+  methods: {
+    onClickLeft() {
+      history.go(-1);
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanNavBar, vanSticky, vanCard }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/news/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 153 - 0
src/pages/partyAffairs/Index.vue

@@ -0,0 +1,153 @@
+<template>
+  <div id="partyAffairs" class="app">
+    <van-tabs v-model="active" sticky animated swipeable>
+      <van-tab v-for="(item, i) in column" :key="i" :title="item.title">
+        <div class="partyBody">
+          <news-list v-if="!item.type" :comId="item.id" />
+          <div v-if="item.type === '1'">
+            <div
+              v-for="item in special"
+              :key="item.id"
+              @click="() => toUrlSpecial(item.id, item.title)"
+            >
+              <list-item-big :item="item" />
+            </div>
+          </div>
+          <!-- 文件通知 -->
+          <!-- <div v-if="item.type === '2'">
+            <div
+              v-for="item in file"
+              :key="item.id"
+              @click="() => toFile(item)"
+            >
+              <list-item-big :item="item" />
+            </div>
+          </div> -->
+        </div>
+      </van-tab>
+    </van-tabs>
+  </div>
+</template>
+
+<script>
+import ListItemBig from "../../components/ListItem/ListItemBig";
+import NewsList from "../../components/NewsList/NewsList";
+import { Tab as vanTab, Tabs as vanTabs } from "vant";
+import "vant/lib/tab/style";
+import "vant/lib/tabs/style";
+
+const column = [
+  {
+    title: "党建要闻",
+    id: "-1"
+  },
+  {
+    title: "主席专栏",
+    id: "-1"
+  },
+  {
+    title: "专题",
+    id: "-1",
+    type: "1"
+  },
+  {
+    title: "党群党支部",
+    id: "14"
+  },
+  {
+    title: "党建风采",
+    id: "-1"
+  },
+  {
+    title: "文件传达",
+    id: "12"
+  },
+  {
+    title: "组织工作",
+    id: "-1"
+  },
+  {
+    title: "党务公开",
+    id: "-1"
+  },
+  {
+    title: "先进典型",
+    id: "-1"
+  },
+  {
+    title: "党建视听",
+    id: "-1"
+  }
+];
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      active: "partyAffairs",
+      column: column,
+      file: [
+        {
+          title: "中华魂智慧党建平台",
+          publishTime: "2020-12-04 09:25:40"
+        }
+      ],
+      special: [
+        {
+          id: 9,
+          url:
+            "https://boot-img.xuexi.cn/image/1020/process/00907fb16fcc48cc8a091d78d7ea9b26.jpg",
+          title: "公安心向党"
+        },
+        {
+          id: 10,
+          url:
+            "https://boot-img.xuexi.cn/op_card/20200525/special_default_banner.png",
+          title: "小故事 暖新闻"
+        },
+        {
+          id: 11,
+          url:
+            "https://boot-img.xuexi.cn/op_card/20190906/30125553741681839_750x280.jpg",
+          title: "第七届全国道德模范评选表彰活动"
+        }
+      ]
+    };
+  },
+  methods: {
+    toUrlSpecial(id, title) {
+      console.log("跳转专题", id);
+      window.parent
+        ? (window.parent.location.href =
+            "./special.html?id=" + id + "&name=" + title)
+        : (location.href = "./special.html?id=" + id + "&name=" + title);
+    },
+    toFile(item) {
+      console.log("跳转专题", item.id);
+      window.parent
+        ? (window.parent.location.href =
+            "./pdfDetail.html?id=" + item.id + "&name=" + item.title)
+        : (location.href =
+            "./pdfDetail.html?id=" + item.id + "&name=" + item.title);
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: {
+    NewsList,
+    ListItemBig,
+    vanTab,
+    vanTabs
+  }
+};
+</script>
+<style lang="scss">
+#partyAffairs {
+  height: 100%;
+  overflow: hidden;
+  overflow-y: scroll;
+  .partyBody {
+    width: 100%;
+  }
+}
+</style>

+ 16 - 0
src/pages/partyAffairs/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 86 - 0
src/pages/partyHome/Index.vue

@@ -0,0 +1,86 @@
+<template>
+  <div id="partyHome" class="app">
+    <van-image
+      class="bannerImage"
+      :src="require('../../assets/image/partySchool.jpg')"
+    />
+    <van-cell-group>
+      <van-cell
+        title="两学一做"
+        @click="() => toUrl('0', '两学一做')"
+        clickable
+        is-link
+      />
+      <van-cell
+        title="党建视频"
+        @click="() => toUrl('1', '党建视频')"
+        clickable
+        is-link
+      />
+      <van-cell
+        title="党规党章"
+        @click="() => toUrl('2', '党规党章')"
+        clickable
+        is-link
+      />
+      <van-cell
+        title="党史精辟"
+        @click="() => toUrl('3', '党史精辟')"
+        clickable
+        is-link
+      />
+      <van-cell
+        title="党课学习"
+        @click="() => toStudy('8', '党课学习')"
+        clickable
+        is-link
+      />
+    </van-cell-group>
+  </div>
+</template>
+
+<script>
+import {
+  Image as VanImage,
+  Cell as vanCell,
+  CellGroup as vanCellGroup
+} from "vant";
+import "vant/lib/image/style";
+import "vant/lib/cell-group/style";
+import "vant/lib/cell/style";
+
+export default {
+  name: "app",
+  data: function() {
+    return {};
+  },
+  methods: {
+    toUrl(id, title) {
+      console.log("跳转专题", id);
+      window.parent
+        ? (window.parent.location.href =
+            "./special.html?id=" + id + "&name=" + title)
+        : (location.href = "./special.html?id=" + id + "&name=" + title);
+    },
+    toStudy(id, title) {
+      console.log("跳转专题");
+      window.parent
+        ? (window.parent.location.href =
+            "./partyStudy.html?id=" + id + "&name=" + title)
+        : (location.href = "./partyStudy.html?id=" + id + "&name=" + title);
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { VanImage, vanCellGroup, vanCell }
+};
+</script>
+<style lang="scss">
+#partyHome {
+  width: 100%;
+  height: 100%;
+  img {
+    width: 100%;
+  }
+}
+</style>

+ 16 - 0
src/pages/partyHome/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage()//这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 248 - 0
src/pages/partyStudy/Index.vue

@@ -0,0 +1,248 @@
+<template>
+  <div id="partyStudy" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar :title="dataInfo.name" left-arrow @click-left="goBack" />
+    </van-sticky>
+    <van-tabs v-model="active">
+      <van-tab title="理论创新">
+        <van-pull-refresh
+          v-model="isLoadingTextList"
+          @refresh="onRefreshTextList"
+        >
+          <van-list
+            v-if="textList.length"
+            v-model="loadingTextList"
+            :finished="finishedTextList"
+            offset="10"
+            loading-text="加载中"
+            finished-text="没有更多了"
+            @load="
+              () => getTextList({ lastId: textList[textList.length - 1].id })
+            "
+          >
+            <div
+              v-for="item in textList"
+              :key="'text' + item.id"
+              @click="() => toUrl(item)"
+            >
+              <keep-alive>
+                <component :is="item.assemblyName" :item="item"></component>
+              </keep-alive>
+            </div>
+          </van-list>
+        </van-pull-refresh>
+      </van-tab>
+      <van-tab title="视频学习">
+        <van-pull-refresh
+          v-model="isLoadingVideoList"
+          @refresh="onRefreshVideoList"
+        >
+          <van-list
+            v-if="videoList.length"
+            v-model="loadingVideoList"
+            :finished="finishedVideoList"
+            offset="10"
+            loading-text="加载中"
+            finished-text="没有更多了"
+            @load="
+              () => getVideoList({ lastId: videoList[videoList.length - 1].id })
+            "
+          >
+            <div
+              v-for="item in videoList"
+              :key="'video' + item.id"
+              @click="() => toUrl(item)"
+            >
+              <keep-alive>
+                <component :is="item.assemblyName" :item="item"></component>
+              </keep-alive>
+            </div>
+          </van-list>
+        </van-pull-refresh>
+      </van-tab>
+      <van-tab title="有声读物">
+        <van-pull-refresh
+          v-model="isLoadingAudioList"
+          @refresh="onRefreshAudiolList"
+        >
+          <van-list
+            v-if="audioList.length"
+            v-model="loadingAudioList"
+            :finished="finishedAudioList"
+            offset="10"
+            loading-text="加载中"
+            finished-text="没有更多了"
+            @load="
+              () => getAudioList({ lastId: audioList[audioList.length - 1].id })
+            "
+          >
+            <div
+              v-for="item in audioList"
+              :key="'audio' + item.id"
+              @click="() => toUrl(item)"
+            >
+              <keep-alive>
+                <component :is="item.assemblyName" :item="item"></component>
+              </keep-alive>
+            </div>
+          </van-list>
+        </van-pull-refresh>
+      </van-tab>
+    </van-tabs>
+  </div>
+</template>
+
+<script>
+import {
+  PullRefresh as vanPullRefresh,
+  NavBar as vanNavBar,
+  Sticky as vanSticky,
+  Tabs as vanTabs,
+  List as vanList,
+  Tab as vanTab
+} from "vant";
+import "vant/lib/pull-refresh/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+import "vant/lib/tabs/style";
+import "vant/lib/list/style";
+import "vant/lib/tab/style";
+import listItemBig from "../../components/ListItem/ListItemBig";
+import listItem from "../../components/ListItem/ListItem";
+import { currentUrlToParams } from "@/utils/common.js";
+import { articleList } from "@/api/article/article.js";
+const baseData = {
+  video: 8,
+  audio: 13,
+  text: 14
+};
+export default {
+  name: "app",
+  data: function() {
+    return {
+      isLoadingTextList: false,
+      loadingTextList: false,
+      finishedTextList: false,
+      isLoadingVideoList: false,
+      loadingVideoList: false,
+      isLoadingAudioList: false,
+      loadingAudioList: false,
+      finishedAudioList: false,
+      finishedVideoList: false,
+      active: "理论创新",
+      dataInfo: {},
+      textList: [],
+      videoList: [],
+      audioList: []
+    };
+  },
+  methods: {
+    goBack() {
+      history.go(-1);
+    },
+    toUrl(item) {
+      let url =
+        item.videoFlag === "V"
+          ? "videoDetail"
+          : item.videoFlag === "A"
+          ? "newsDetail"
+          : "audioDetail";
+      url += ".html?id=" + item.id;
+      window.parent
+        ? (window.parent.location.href = url)
+        : (window.location.href = url);
+    },
+    onRefreshTextList() {
+      this.textList = [];
+      this.isLoadingTextList = true;
+      this.finishedTextList = false;
+      this.getTextList();
+    },
+    onRefreshVideoList() {
+      this.videoList = [];
+      this.isLoadingVideoList = true;
+      this.finishedVideoList = false;
+      this.getVideoList();
+    },
+    onRefreshAudiolList() {
+      this.audioList = [];
+      this.isLoadingAudioList = true;
+      this.finishedAudioList = false;
+      this.getAudioList();
+    },
+    formData(objData) {
+      let video = Math.random() > 0.5 ? "listItemBig" : "listItem";
+      if (objData.videoFlag === "V") {
+        video = "listItemBig";
+      }
+      return {
+        id: objData.id,
+        url: objData.imgUrl,
+        title: objData.title,
+        orisource: objData.origSource,
+        releaseTime: objData.publishTime,
+        read: objData.read,
+        assemblyName: video,
+        videoFlag: objData.videoFlag,
+        subTitle: objData.normalizedContent
+      };
+    },
+    newList(list) {
+      const viewList = [];
+      list.map(v => {
+        viewList.push(this.formData(v));
+      });
+      return viewList;
+    },
+    getVideoList(params) {
+      articleList(params || {}, baseData.video).then(res => {
+        const list = this.newList(res || []);
+        this.videoList = [...this.videoList, ...list];
+        this.isLoadingVideoList = false;
+        this.loadingVideoList = false;
+        if ((res && res.length === 0) || !res) this.finishedVideoList = true;
+      });
+    },
+    getAudioList(params) {
+      articleList(params || {}, baseData.audio).then(res => {
+        const list = this.newList(res || []);
+        this.audioList = [...this.audioList, ...list];
+        this.isLoadingAudioList = false;
+        this.loadingAudioList = false;
+        if ((res && res.length === 0) || !res) this.finishedAudioList = true;
+      });
+    },
+    getTextList(params) {
+      articleList(params || {}, baseData.text).then(res => {
+        console.log(res);
+        const list = this.newList(res || []);
+        this.textList = [...this.textList, ...list];
+        this.isLoadingTextList = false;
+        this.loadingTextList = false;
+        if ((res && res.length === 0) || !res) this.finishedTextList = true;
+      });
+    }
+  },
+  mounted() {
+    this.dataInfo = currentUrlToParams();
+    this.getAudioList();
+    this.getTextList();
+    this.getVideoList();
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanPullRefresh,
+    listItemBig,
+    vanNavBar,
+    vanSticky,
+    listItem,
+    vanList,
+    vanTabs,
+    vanTab
+  }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/partyStudy/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 135 - 0
src/pages/peeList/Index.vue

@@ -0,0 +1,135 @@
+<template>
+  <div id="peeList" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="我的党支部" left-arrow @click-left="goBack" />
+    </van-sticky>
+    <van-sticky :offset-top="0">
+      <van-row class="van-hairline--bottom head">
+        <van-col span="8">时间</van-col>
+        <van-col span="8">金额</van-col>
+        <van-col span="8">操作</van-col>
+      </van-row>
+    </van-sticky>
+    <van-row
+      class="van-hairline--bottom"
+      v-for="item in monthList"
+      :key="item.id"
+    >
+      <van-col span="8" v-text="item.month"></van-col>
+      <van-col span="8">{{ item.fee | money }}</van-col>
+      <van-col span="8">
+        <van-button
+          type="danger"
+          size="small"
+          @click="() => toUrl(item)"
+          v-if="!item.pay"
+        >
+          待缴纳
+        </van-button>
+        <van-button size="small" type="primary" disabled v-else>
+          已缴纳
+        </van-button>
+      </van-col>
+    </van-row>
+    <van-popup round v-model="show" position="bottom">
+      <div class="bot">
+        <div class="title">确认支付 {{ select.fee | money }}元</div>
+        <van-button type="primary" @click="toPay" round block>
+          支 付
+        </van-button>
+      </div>
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import { activistList, Pay } from "@/api/account/account.js";
+import {
+  NavBar as vanNavBar,
+  Col as vanCol,
+  Row as vanRow,
+  Button as vanButton,
+  Popup as vanPopup,
+  Sticky as vanSticky
+} from "vant";
+import "vant/lib/button/style";
+import "vant/lib/popup/style";
+import "vant/lib/col/style";
+import "vant/lib/row/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      monthList: [],
+      show: false,
+      select: {}
+    };
+  },
+  methods: {
+    toUrl(item) {
+      this.select = item;
+      this.show = true;
+    },
+    goBack() {
+      history.go(-1);
+    },
+    toPay() {
+      Pay({
+        month: this.select.month,
+        fee: this.select.fee
+      }).then(r => {
+        console.log(r);
+        activistList().then(res => {
+          this.monthList = (res || []).sort((a, b) => {
+            let aDate = new Date(a.month);
+            let bDate = new Date(b.month);
+            return bDate - aDate;
+          });
+          this.show = false;
+        });
+      });
+    }
+  },
+  filters: {
+    money(v) {
+      if (isNaN(v)) return "0.00";
+      return Number(v).toFixed(2);
+    }
+  },
+  mounted() {
+    activistList().then(res => {
+      this.monthList = (res || []).sort((a, b) => {
+        let aDate = new Date(a.month);
+        let bDate = new Date(b.month);
+        return bDate - aDate;
+      });
+    });
+  },
+  beforeDestroy: function() {},
+  components: { vanRow, vanCol, vanButton, vanPopup, vanSticky, vanNavBar }
+};
+</script>
+<style lang="scss">
+#peeList {
+  .head {
+    .van-col {
+      background-color: #eee;
+    }
+  }
+  .van-col {
+    text-align: center;
+    padding: 0.1rem 0;
+  }
+  .bot {
+    padding: 0 1em 2em;
+    .title {
+      width: 100%;
+      text-align: center;
+      padding: 0.2rem;
+    }
+  }
+}
+</style>

+ 16 - 0
src/pages/peeList/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 118 - 0
src/pages/relationshipTransfer/Index.vue

@@ -0,0 +1,118 @@
+<template>
+  <div id="relationshipTransfer">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="党支部" left-arrow @click-left="onClickLeft" />
+    </van-sticky>
+    <!-- <van-pull-refresh v-model="isLoading" @refresh="onRefresh"> -->
+    <van-tree-select
+      height="90vh"
+      :items="partyList"
+      :active-id.sync="activeId"
+      :main-active-index.sync="activeIndex"
+    />
+    <!-- </van-pull-refresh> -->
+    <!-- <div class="funcBtn">
+      <van-button type="danger" @click="apply" round block>
+        申请转移
+      </van-button>
+    </div> -->
+  </div>
+</template>
+
+<script>
+import {
+  // PullRefresh as vanPullRefresh,
+  NavBar as vanNavBar,
+  // Button as vanButton,
+  Sticky as vanSticky,
+  TreeSelect as vanTreeSelect
+} from "vant";
+// import "vant/lib/pull-refresh/style";
+import "vant/lib/button/style";
+import "vant/lib/sticky/style";
+import "vant/lib/nav-bar/style";
+import "vant/lib/tree-select/style";
+import { list } from "@/api/party/party.js";
+import { toTree } from "@/utils/common.js";
+
+export default {
+  name: "app",
+  data: function() {
+    return {
+      isLoading: false,
+      activeIndex: "",
+      activeId: "",
+      partyList: []
+    };
+  },
+  methods: {
+    onRefresh() {
+      list().then(res => {
+        let list = (res || []).map(v => {
+          return {
+            id: v.id,
+            text: v.name,
+            parentId: v.parentId,
+            disabled: true
+          };
+        });
+        this.partyList = toTree(list, {
+          pidKey: "parentId",
+          idKey: "id",
+          disabled: true,
+          rootId: "1"
+        });
+        this.isLoading = false;
+      });
+    },
+    apply() {
+      location.href = "./relationshipTransferFrom.html";
+    },
+    onClickLeft() {
+      history.go(-1);
+    }
+  },
+  mounted() {
+    list().then(res => {
+      let list = (res || []).map(v => {
+        return {
+          id: v.id,
+          text: v.name,
+          parentId: v.parentId
+        };
+      });
+      this.partyList = toTree(list, {
+        pidKey: "parentId",
+        idKey: "id",
+        disabled: true,
+        rootId: "1"
+      });
+      console.log(this.partyList);
+    });
+  },
+  beforeDestroy: function() {},
+  components: { vanSticky, vanNavBar, vanTreeSelect }
+};
+</script>
+<style lang="scss">
+#relationshipTransfer {
+  height: 100%;
+  .funcBtn {
+    position: fixed;
+    width: 90%;
+    left: 5%;
+    bottom: 0.5em;
+  }
+  .van-tree-select {
+    .van-sidebar {
+      .van-sidebar-item--disabled {
+        color: #323233;
+      }
+    }
+  }
+
+  .van-tree-select__item--disabled {
+    color: #000;
+  }
+}
+</style>

+ 16 - 0
src/pages/relationshipTransfer/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 222 - 0
src/pages/relationshipTransferFrom/Index.vue

@@ -0,0 +1,222 @@
+<template>
+  <div id="joinParty" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar title="入党" left-arrow @click-left="onClickLeft" />
+    </van-sticky>
+    <van-form @submit="onSubmit">
+      <van-field
+        v-model="username"
+        name="姓名"
+        label="姓名"
+        placeholder="姓名"
+        :rules="[{ required: true, message: '请填写姓名' }]"
+      />
+      <van-field name="radio" label="流动范围">
+        <template #input>
+          <van-radio-group v-model="range" direction="horizontal">
+            <van-radio name="1">系统内</van-radio>
+            <van-radio name="0">系统外</van-radio>
+          </van-radio-group>
+        </template>
+      </van-field>
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        :value="timeWork"
+        label="转出时间"
+        placeholder="点击选择时间"
+        @click="showTimeWork = true"
+      />
+      <van-field
+        v-model="branch"
+        name="转入支部"
+        label="转入支部"
+        v-if="range == '0'"
+        placeholder="点击输入支部"
+        :rules="[{ required: true, message: '请输入支部名称' }]"
+      />
+      <van-field
+        readonly
+        clickable
+        name="picker"
+        v-else
+        :value="branch"
+        label="转入支部"
+        placeholder="点击选择支部"
+        @click="showBranch = true"
+      />
+      <van-field
+        v-model="reason"
+        rows="2"
+        autosize
+        label="转出原因"
+        type="textarea"
+        placeholder="转出原因"
+        :rules="[{ required: true, message: '请填转出原因' }]"
+        show-word-limit
+      />
+      <van-field
+        v-model="basic"
+        rows="2"
+        autosize
+        label="备注"
+        type="textarea"
+        placeholder="备注"
+        :rules="[{ required: true, message: '请填备注' }]"
+        show-word-limit
+      />
+      <div style="margin: 16px;">
+        <van-button round block type="danger" native-type="submit">
+          提交
+        </van-button>
+      </div>
+    </van-form>
+    <!-- 选择开始工作日期 -->
+    <van-popup v-model="showTimeWork" position="bottom">
+      <van-datetime-picker
+        v-model="oriTimeWork"
+        @cancel="TimeWorkCancel"
+        @confirm="TimeWorkFunc"
+        :min-date="minDate"
+        type="date"
+        title="选择日期"
+      />
+    </van-popup>
+    <!-- 选择支部 -->
+    <van-popup v-model="showBranch" position="bottom">
+      <van-cascader
+        v-model="branch"
+        title="请选择所在地区"
+        :options="partyList"
+        :field-names="{
+          text: 'name',
+          value: 'id',
+          children: 'children'
+        }"
+        @close="showBranch = false"
+        @finish="branchFunc"
+      />
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import {
+  NavBar as vanNavBar,
+  Button as vanButton,
+  Sticky as vanSticky,
+  Field as vanField,
+  Radio as vanRadio,
+  Popup as vanPopup,
+  DatetimePicker as vanDatetimePicker,
+  RadioGroup as vanRadioGroup,
+  Form as vanForm,
+  Cascader as vanCascader
+} from "vant";
+import "vant/lib/nav-bar/style";
+import "vant/lib/button/style";
+import "vant/lib/popup/style";
+import "vant/lib/radio/style";
+import "vant/lib/radio-group/style";
+import "vant/lib/form/style";
+import "vant/lib/datetime-picker/style";
+import "vant/lib/sticky/style";
+import "vant/lib/field/style";
+import "vant/lib/cascader/style";
+import { list, upParty } from "@/api/party/party.js";
+import { toTree, getUserInfo } from "@/utils/common.js";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      minDate: new Date("1970-01-01 00:00:00"),
+      showTimeWork: false,
+      showBranch: false,
+      branch: "",
+      select: [],
+      oriTimeWork: "",
+      timeWork: "",
+      username: getUserInfo().userName || "",
+      range: "1",
+      basic: "",
+      reason: "",
+      partyList: []
+    };
+  },
+  methods: {
+    onSubmit() {
+      let select = this.select[this.select.length - 1] || {};
+      upParty({
+        organId: select.id,
+        username: this.username,
+        range: this.range,
+        timeWork: this.timeWork,
+        basic: this.basic,
+        reason: this.reason
+      }).then(() => {
+        this.onClickLeft();
+      });
+    },
+    onClickLeft() {
+      history.go(-1);
+    },
+    branchFunc(val) {
+      if (!val.selectedOptions.length) {
+        this.branch = "";
+        this.showBranch = false;
+        return;
+      }
+      this.select = val.selectedOptions;
+      this.branch = val.selectedOptions
+        .map(v => {
+          return v.name;
+        })
+        .join("-");
+      this.showBranch = false;
+    },
+    TimeWorkFunc(val) {
+      this.showTimeWork = false;
+      let time = new Date(val);
+      this.timeWork = time.getFullYear() + "-";
+      let month = time.getMonth() + 1;
+      let day = time.getDate();
+      this.timeWork += month > 9 ? month : "0" + month;
+      this.timeWork += "-";
+      this.timeWork += day > 9 ? day : "0" + day;
+    },
+    TimeWorkCancel() {
+      this.showTimeWork = false;
+      this.timeWork = "";
+    }
+  },
+  mounted() {
+    list().then(res => {
+      let list = res || [];
+      this.partyList = toTree(list, {
+        pidKey: "parentId",
+        idKey: "id",
+        rootId: "1"
+      });
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanSticky,
+    vanNavBar,
+    vanButton,
+    vanForm,
+    vanField,
+    vanPopup,
+    vanRadio,
+    vanDatetimePicker,
+    vanRadioGroup,
+    vanCascader
+  }
+};
+</script>
+<style lang="scss">
+#joinParty {
+  padding-bottom: 2em;
+}
+</style>

+ 16 - 0
src/pages/relationshipTransferFrom/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 218 - 0
src/pages/robot/Index.vue

@@ -0,0 +1,218 @@
+<template>
+  <div>
+    <van-sticky :offset-top="0">
+      <van-dropdown-menu>
+        <van-dropdown-item
+          @change="change"
+          v-model="robot"
+          :options="robotList"
+        />
+      </van-dropdown-menu>
+    </van-sticky>
+    <div id="robot" class="app">
+      <div class="van-hairline--surround msg"></div>
+      <div class="van-hairline--left van-hairline--right van-hairline--bottom">
+        <van-field v-model="message" rows="1" autosize type="textarea" />
+        <div style="text-align: right; padding: 0.1rem">
+          <van-button size="small" type="primary" @click="tomsg"
+            >发送</van-button
+          >
+        </div>
+      </div>
+      <van-cell title="控制机器人" value="" />
+      <van-row>
+        <van-col span="12">
+          <div class="control">
+            <van-button type="default" @click="() => toOperation('head', 'up')">
+              上
+            </van-button>
+            <div class="conBtnGroup">
+              <van-button
+                type="default"
+                @click="() => toOperation('head', 'left')"
+              >
+                左
+              </van-button>
+              <van-button
+                type="default"
+                @click="() => toOperation('head', 'down')"
+              >
+                下
+              </van-button>
+              <van-button
+                type="default"
+                @click="() => toOperation('head', 'right')"
+              >
+                右
+              </van-button>
+            </div>
+            <div class="text">头部</div>
+          </div>
+        </van-col>
+        <van-col span="12">
+          <div class="control">
+            <van-button
+              type="default"
+              @click="() => toOperation('wheel', 'forward')"
+            >
+              前
+            </van-button>
+            <div class="conBtnGroup">
+              <van-button
+                type="default"
+                @click="() => toOperation('wheel', 'left')"
+              >
+                左
+              </van-button>
+              <van-button
+                type="default"
+                @click="() => toOperation('wheel', 'backward')"
+              >
+                后
+              </van-button>
+              <van-button
+                type="default"
+                @click="() => toOperation('wheel', 'right')"
+              >
+                右
+              </van-button>
+            </div>
+            <div class="text">轮子</div>
+          </div>
+        </van-col>
+      </van-row>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getRobotToken, getRobotClient } from "@/api/robot/index.js";
+import Config from "@/config/index.js";
+import {
+  Button as vanButton,
+  Cell as vanCell,
+  Col as vanCol,
+  Row as vanRow,
+  Field as vanField,
+  DropdownMenu as vanDropdownMenu,
+  DropdownItem as vanDropdownItem,
+  Sticky as vanSticky,
+  Toast
+} from "vant";
+import "vant/lib/button/style";
+import "vant/lib/cell/style";
+import "vant/lib/col/style";
+import "vant/lib/row/style";
+import "vant/lib/field/style";
+import "vant/lib/sticky/style";
+import "vant/lib/dropdown-menu/style";
+import "vant/lib/dropdown-item/style";
+export default {
+  name: "app",
+  robotToken: "",
+  scoket: undefined,
+  deviceId: "",
+  data: function() {
+    return {
+      robotList: [
+        { text: "机器人1", value: 0 },
+        { text: "机器人2", value: 1 },
+        { text: "机器人3", value: 2 }
+      ],
+      robot: 0,
+      message: ""
+    };
+  },
+  methods: {
+    tomsg() {
+      this.scoket &&
+        this.scoket.send({
+          id: "",
+          dialog: {
+            reply: ""
+          }
+        });
+    },
+    toOperation(type, opera) {
+      let p = {};
+      p[type] = opera;
+      this.scoket &&
+        this.scoket.send({
+          id: "",
+          dialog: {
+            reply: ""
+          },
+          controller: p
+        });
+    },
+    createRobotLink() {
+      if (!window.WebSocket) return Toast("该版本暂时不支持");
+      this.scoket && this.scoket.close();
+      this.scoket = new WebSocket(
+        Config.robotBaseWss +
+          "/ws/app/event?clientId=" +
+          Config.clientId +
+          "&deviceId=" +
+          this.deviceId +
+          "&token=" +
+          this.robotToken
+      );
+      this.scoket.onmessage = function(event) {
+        var data = event.data;
+        console.log(data);
+        // 处理数据
+      };
+    },
+    change(e) {
+      this.deviceId = e;
+      this.createRobotLink();
+    }
+  },
+  mounted() {
+    getRobotToken().then(res => {
+      if (res.error_code !== 0) return Toast("机器人走丢了");
+      this.robotToken = res.token || "";
+      getRobotClient({
+        clientId: Config.clientId,
+        token: this.robotToken
+      }).then(r => {
+        if (res.error_code !== 0) return Toast("暂时没有上线设备");
+        this.robotList = r.result || [];
+      });
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    vanButton,
+    vanCell,
+    vanCol,
+    vanRow,
+    vanField,
+    vanSticky,
+    vanDropdownMenu,
+    vanDropdownItem
+  }
+};
+</script>
+<style lang="scss">
+#robot {
+  padding: 0.1rem 0.16rem;
+  .msg {
+    padding: 0.1rem;
+    height: 3rem;
+  }
+  .control {
+    display: inline-block;
+    margin-top: 0.1rem;
+    width: 1.45rem;
+    text-align: center;
+    .conBtnGroup {
+      text-align: center;
+    }
+    .text {
+      padding: 0.1rem;
+      font-size: 0.14rem;
+    }
+  }
+}
+</style>

+ 16 - 0
src/pages/robot/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 69 - 0
src/pages/service/Index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div id="service" class="app">
+    <van-image :src="require('../../assets/image/partySchool.jpg')" />
+    <van-grid :column-num="2">
+      <van-grid-item
+        @click="() => toUrl('peeList')"
+        icon="balance-o"
+        text="缴纳党费"
+      />
+      <van-grid-item
+        @click="() => toUrl('JoinParty')"
+        icon="chart-trending-o"
+        text="党员发展"
+      />
+      <van-grid-item
+        @click="() => toUrl('relationshipTransferFrom')"
+        icon="flag-o"
+        text="组织关系转移"
+      />
+      <van-grid-item
+        @click="() => toUrl('activist')"
+        icon="manager-o"
+        text="培养积极分子"
+      />
+      <van-grid-item
+        @click="() => toUrl('relationshipTransfer')"
+        icon="cluster-o"
+        text="党支部"
+      />
+      <van-grid-item
+        @click="() => toUrl('leaving')"
+        icon="comment-o"
+        text="留言"
+      />
+    </van-grid>
+  </div>
+</template>
+
+<script>
+import {
+  Grid as vanGrid,
+  GridItem as vanGridItem,
+  Image as VanImage
+} from "vant";
+import "vant/lib/grid/style";
+import "vant/lib/grid-item/style";
+export default {
+  name: "app",
+  data: function() {
+    return {};
+  },
+  methods: {
+    toUrl(id) {
+      if (!id) return;
+      console.log("服务-->", id);
+      window.parent
+        ? (window.parent.location.href = "./" + id + ".html")
+        : (location.href = "./" + id + ".html");
+    }
+  },
+  mounted() {},
+  beforeDestroy: function() {},
+  components: { vanGrid, vanGridItem, VanImage }
+};
+</script>
+<style lang="scss" scoped>
+#service {
+}
+</style>

+ 16 - 0
src/pages/service/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 48 - 0
src/pages/special/Index.vue

@@ -0,0 +1,48 @@
+<template>
+  <div id="special" class="app">
+    <van-sticky :offset-top="0">
+      <van-nav-bar
+        left-text="返回"
+        left-arrow
+        @click-left="goBack"
+        :title="dataInfo.name"
+      />
+    </van-sticky>
+    <news-list v-if="this.dataInfo.id > -100" :comId="this.dataInfo.id" />
+  </div>
+</template>
+
+<script>
+import NewsList from "../../components/NewsList/NewsList";
+import { currentUrlToParams } from "@/utils/common.js";
+import { NavBar as vanNavBar, Sticky as vanSticky } from "vant";
+import "vant/lib/nav-bar/style";
+import "vant/lib/sticky/style";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      dataInfo: {},
+      comId: "-2"
+    };
+  },
+  methods: {
+    goBack() {
+      history.go(-1);
+    }
+  },
+  mounted() {
+    this.dataInfo = currentUrlToParams();
+  },
+  beforeDestroy: function() {},
+  components: {
+    NewsList,
+    vanNavBar,
+    vanSticky
+  }
+};
+</script>
+<style lang="scss">
+.app {
+}
+</style>

+ 16 - 0
src/pages/special/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 87 - 0
src/pages/study/Index.vue

@@ -0,0 +1,87 @@
+<template>
+  <div id="study" class="app">
+    <!-- <van-sticky :offset-top="0">
+      <van-search v-model="value" placeholder="请输入搜索关键词" />
+    </van-sticky> -->
+    <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
+      <van-swipe-item
+        @click="() => toUrlDetail(item.id)"
+        v-for="item in list"
+        :key="item.id"
+      >
+        <div class="bannerItem">
+          <van-image :src="item.imgUrl" />
+          <div class="van-ellipsis title" v-text="item.title"></div>
+        </div>
+      </van-swipe-item>
+
+      <template #indicator><div></div> </template>
+    </van-swipe>
+    <news-list comId="-1" />
+  </div>
+</template>
+
+<script>
+import NewsList from "../../components/NewsList/NewsList";
+import { banner } from "@/api/article/article.js";
+// import { Search as vanSearch, Sticky as vanSticky } from "vant";
+import {
+  Swipe as vanSwipe,
+  SwipeItem as vanSwipeItem,
+  Image as vanImage
+} from "vant";
+import "vant/lib/swipe/style";
+import "vant/lib/image/style";
+import "vant/lib/swipe-item/style";
+export default {
+  name: "app",
+  data: function() {
+    return {
+      value: "",
+      list: []
+    };
+  },
+  methods: {
+    toUrlDetail(id) {
+      if (!id) return;
+      window.parent
+        ? (window.parent.location.href = "/newsDetail.html?id=" + id)
+        : (location.href = "/newsDetail.html?id=" + id);
+    }
+  },
+  mounted() {
+    banner().then(res => {
+      this.list = res || [];
+    });
+  },
+  beforeDestroy: function() {},
+  components: {
+    NewsList,
+    vanSwipe,
+    vanSwipeItem,
+    vanImage
+    // vanSearch,
+    // vanSticky
+  }
+};
+</script>
+<style lang="scss" scoped>
+.my-swipe .van-swipe-item {
+  .bannerItem {
+    position: relative;
+    .van-image {
+      width: 100%;
+      height: 100%;
+    }
+    .title {
+      position: absolute;
+      bottom: 0;
+      width: 100%;
+      background-color: rgba(0, 0, 0, 0.6);
+      line-height: 30px;
+      color: #fff;
+      font-size: 14px;
+    }
+  }
+}
+</style>

+ 16 - 0
src/pages/study/index.js

@@ -0,0 +1,16 @@
+import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage() //这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });

+ 19 - 0
src/utils/adaptation.js

@@ -0,0 +1,19 @@
+import Config from "@/config/index.js";
+
+// 基准大小
+const baseSize = 100;
+
+function setFontSize() {
+  const scale = document.documentElement.clientWidth / Config.designSize;
+  document.documentElement.style.fontSize =
+    baseSize * Math.min(scale, 2) + "px";
+}
+
+export function setHtmlFontSize() {
+  // 初始化
+  setFontSize();
+  // 改变窗口大小时重新设置 fontSize
+  window.onresize = function() {
+    setFontSize();
+  };
+}

+ 122 - 0
src/utils/buildApiCode.js

@@ -0,0 +1,122 @@
+/*
+ * 这个是针对swagger-ui api文档代码生成器。
+ * 在使用前,先将swagger-ui其中一个大的模块展开,
+ * 将下面的代码往swagger-ui页面的控制台粘贴执行,就会打印出当前展开文档api代码.复制放到
+ * api文件夹的js里面即可使用
+ */
+
+let ApiList = document.querySelectorAll(".opblock-summary");
+
+let ApiStr = `
+import request from '@/utils/request.js'
+`;
+let MethodNameS = [];
+let JsKeyword = [
+  "break",
+  "else",
+  "new",
+  "var",
+  "case",
+  "finally",
+  "return",
+  "void",
+  "catch",
+  "for",
+  "switch",
+  "while",
+  "continue",
+  "function",
+  "this",
+  "with",
+  "default",
+  "if ",
+  "throw",
+  "delete",
+  "in",
+  "try",
+  "do",
+  "instranceof",
+  "typeof",
+  "abstract",
+  "enum",
+  "int",
+  "short",
+  "boolean",
+  "export",
+  "interface",
+  "static",
+  "byte",
+  "extends",
+  "long",
+  "super",
+  "char",
+  "final",
+  "native",
+  "synchronized",
+  "class",
+  "float",
+  "package",
+  "throws",
+  "const",
+  "goto",
+  "private",
+  "transient",
+  "debugger",
+  "implements",
+  "protected",
+  "volatile",
+  "double",
+  "import",
+  "public"
+];
+let Num = 1;
+
+ApiList.forEach(r => {
+  let path = r.querySelector(".opblock-summary-path a span").innerText;
+  let pathArr = path.split("/");
+  let isPostMethod =
+    r.querySelector(".opblock-summary-method").innerText === "POST";
+  Num = 1;
+
+  let methodName = MethodNameUnique(pathArr[pathArr.length - 1]);
+  MethodNameS.push(methodName);
+
+  ApiStr += `
+
+
+/**
+ * ${r.querySelector(".opblock-summary-description").innerText}
+ * @param params
+ * @returns {AxiosPromise}
+ */
+export function ${methodName}(params) {
+    return request({
+        url: "${path.replace("/api", "")}",
+        method: '${isPostMethod ? "post" : "get"}',
+        ${isPostMethod ? "data" : "params"}: params
+    })
+}
+
+`;
+});
+ApiStr += `
+
+//全部方法
+//${MethodNameS.join("、")}
+`;
+
+function MethodNameUnique(name) {
+  if (MethodNameS.indexOf(name) !== -1 || JsKeyword.indexOf(name) !== -1) {
+    Num++;
+    return MethodNameUnique(name + Num);
+  }
+  return name;
+}
+
+console.warn(
+  "==========================================================================="
+);
+console.log(ApiStr);
+console.warn(
+  "==========================================================================="
+);

+ 152 - 0
src/utils/common.js

@@ -0,0 +1,152 @@
+import Storage from "good-storage";
+import Config from "@/config/index.js";
+
+export function currentUrlToParams(key = null) {
+  let paramsUrl = window.location.href.split("?");
+  if (paramsUrl.length < 2) return key ? null : {};
+  let paramsArr = paramsUrl[1].split("&");
+  let paramsData = {};
+  paramsArr.forEach(r => {
+    let data = r.split("=");
+    paramsData[data[0]] = decodeURIComponent(data[1]);
+  });
+  if (key) return paramsData.hasOwnProperty(key) ? paramsData[key] : null;
+  return paramsData;
+}
+
+/**
+ * 将对象转换为?a=5&b=7形式
+ * @param obj
+ * @param firstStr
+ * @returns {string|string}
+ */
+export function obj2StrParams(obj, firstStr = "?") {
+  let params = firstStr;
+
+  for (let p in obj) {
+    params += p + "=" + obj[p] + "&";
+  }
+  return params.substring(0, params.length - 1);
+}
+/*
+获取当前url
+http://192.168.49.71:8081/ => http://192.168.49.71:8081/
+http://192.168.49.71:8081/mm/ => http://192.168.49.71:8081/mm/
+http://192.168.49.71:8081/mm/index.html => http://192.168.49.71:8081/mm/
+http://192.168.49.71:8081/mm/ff/login.html?id=55 => http://192.168.49.71:8081/mm/ff/
+
+页面名字不能能匹配除\w 的地址,也就是说你的页面名字名字必须由a-z、A-Z、0-9,以及下划线组成才可以。
+*/
+export function getCurrentUrl() {
+  let allUrl = window.location.href;
+  let match = allUrl.match(/(\S+\/)\w+.html/i);
+  return match && Array.isArray(match) && match.length > 1 ? match[1] : allUrl;
+}
+
+/**
+ * 重置对象(会修改原始对象)
+ * @param object
+ * @param defaultVal
+ */
+export function resetObject(object, defaultVal = {}) {
+  for (let k in object) {
+    if (defaultVal.hasOwnProperty(k)) {
+      object[k] = defaultVal[k];
+    } else {
+      if (Array.isArray(object[k])) object[k] = [];
+      if ("string" == typeof object[k]) object[k] = "";
+      if ("number" == typeof object[k]) object[k] = null;
+      if ("boolean" == typeof object[k]) object[k] = false;
+    }
+  }
+}
+
+/**
+ * 对象赋值(会修改原始对象)
+ * @param object
+ * @param valObject
+ */
+export function fillerLeft(object, valObject = {}) {
+  for (let k in object) {
+    if (valObject.hasOwnProperty(k)) {
+      object[k] = valObject[k];
+    }
+  }
+}
+
+/**
+ * 获取用户信息
+ * @param key
+ * @returns {null|*|undefined|{}}
+ */
+export function getUserInfo(key = null) {
+  let userInfo = Storage.get(Config.userInfoKey) || {};
+  if (key) return userInfo.hasOwnProperty(key) ? userInfo[key] : null;
+  return userInfo || {};
+}
+
+/**
+ * 设置用户信息
+ * @param user
+ * @returns {*}
+ */
+export function setUserInfo(user) {
+  Storage.set(Config.userInfoKey, user);
+  return user;
+}
+
+/**
+ * 获取Token
+ * @returns {*|undefined}
+ */
+export function getToken() {
+  return Storage.get(Config.tokenKey);
+}
+
+/**
+ * 设置Token
+ * @param token
+ * @returns {*|undefined}
+ */
+export function setToken(token) {
+  return Storage.set(Config.tokenKey, token);
+}
+
+/**
+ * 移除Token
+ * @returns {*}
+ */
+export function removeToken() {
+  return Storage.remove(Config.tokenKey);
+}
+
+/**
+ * 数组->树
+ */
+export function toTree(list, propsConfig = {}) {
+  const treeList = list || [];
+  const out = [];
+  const config = {
+    rootId: propsConfig.rootId || null,
+    idKey: propsConfig.idKey || "id",
+    pidKey: propsConfig.pidKey || "pid",
+    selectID: propsConfig.selectID || undefined,
+    disabled: propsConfig.disabled || null
+  };
+  for (let index = 0, len = treeList.length; index < len; index++) {
+    const element = treeList[index];
+    element.spread =
+      element.id === config.selectID || config.rootId === element.id;
+    !element.children && (element.children = []);
+    for (let i = 0, lens = treeList.length; i < lens; i++) {
+      const sunItem = treeList[i];
+      config.disabled !== null && (sunItem.disabled = config.disabled);
+      if (element[config.idKey] !== sunItem[config.pidKey]) continue;
+      element.children.push(sunItem);
+    }
+    !element.children.length && delete element.children;
+    if (element[config.idKey] !== config.rootId) continue;
+    out.push(element);
+  }
+  return out;
+}

+ 10 - 0
src/utils/isTerminal.js

@@ -0,0 +1,10 @@
+const ua = window.navigator.userAgent;
+
+const isAndroid = /(Android);?[\s/]+([\d.]+)?/i.test(ua);
+const isIpad = /(iPad).*OS\s([\d_]+)/i.test(ua);
+const isIpod = /(iPod)(.*OS\s([\d_]+))?/i.test(ua);
+const isIphone = !isIpad && /(iPhone\sOS)\s([\d_]+)/i.test(ua);
+const isWechat = /micromessenger/i.test(ua);
+const isAlipay = /alipayclient/i.test(ua);
+
+export { isIphone, isWechat, isAlipay, isAndroid, isIpad, isIpod };

+ 43 - 0
src/utils/nodeTool/analysisArgv.js

@@ -0,0 +1,43 @@
+let Argv = {};
+const option = [
+  { name: "-n", description: "新建的页面(文件夹)名字" },
+  { name: "-h", description: "查看帮助" },
+  {
+    name: "-p",
+    description: "父目录的名字(文件夹),(有此参数-n会被新建在此目录下)"
+  }
+];
+
+let argvLen = process.argv.length;
+if (argvLen <= 2) {
+  console.log('请使用"-h"参数查看帮助');
+  process.exit();
+}
+
+let argvVal = process.argv.slice(2, argvLen);
+
+argvVal.forEach(r => {
+  let data = r.split("=");
+  let reg = /[a-zA-Z0-9_]*/;
+  let key = data[0].replace("-", "");
+  let val = reg.exec(data[1])[0];
+  if ("h" === key) {
+    console.table(option);
+    process.exit();
+  }
+  if (key && val) {
+    Argv[key] = val;
+  }
+});
+
+if (Object.keys(Argv).length === 0) {
+  console.log('参数有误,请使用"-h"参数查看帮助');
+  process.exit();
+}
+
+if (!Argv.n) {
+  console.log('"-n"是必填参数');
+  process.exit();
+}
+
+module.exports = Argv;

+ 44 - 0
src/utils/nodeTool/index.js

@@ -0,0 +1,44 @@
+const fs = require("fs");
+
+const Argv = require("./analysisArgv.js");
+let JsTemplate = require("./jsTemplate.js");
+let VueTemplate = require("./vueTemplate.js");
+const Utils = require("./utils.js");
+
+const pageDir = Utils.getPageDir();
+
+if (Utils.existsPage(Argv.n)) {
+  console.log(Argv.n + "页面已经存在,请使用其他名字");
+  process.exit();
+}
+
+let workPageDir;
+
+try {
+  workPageDir = pageDir + "/" + Argv.n;
+  if (Argv.hasOwnProperty("p")) {
+    if (!fs.existsSync(pageDir + "/" + Argv.p)) {
+      console.log("asd");
+      fs.mkdirSync(pageDir + "/" + Argv.p);
+    }
+    workPageDir = pageDir + "/" + Argv.p + "/" + Argv.n;
+  }
+  fs.mkdirSync(workPageDir);
+  console.log("创建目录" + Argv.n + "成功");
+} catch (e) {
+  console.log("创建目录失败,请检查目录是否已经存在!");
+  console.log(e.toString());
+  process.exit();
+}
+Utils.updatePageConfig(Argv.n);
+
+VueTemplate.replace('id="app"', 'id="' + Argv.n + '"');
+VueTemplate.replace("#app", "#" + Argv.n);
+
+fs.writeFileSync(
+  workPageDir + "/Index.vue",
+  VueTemplate.replace('id="app"', 'id="' + Argv.n + '"')
+);
+fs.writeFileSync(workPageDir + "/index.js", JsTemplate);
+
+console.log("生成页面成功,请重新运行npm run dev");

+ 17 - 0
src/utils/nodeTool/jsTemplate.js

@@ -0,0 +1,17 @@
+module.exports = `import Vue from "vue";
+import App from "./Index.vue";
+import "@/pages/common.js";
+import { validateInitPage } from "@/utils/validateInitPage.js";
+
+validateInitPage()//这里还可以链式then做一些权限拦截
+  .then((pageName, pageParams) => {
+    Vue.prototype.$pageName = pageName;
+    Vue.prototype.$pageParams = pageParams;
+    new Vue({
+      render: h => h(App)
+    }).$mount("#app");
+  })
+  .catch(e => {
+    console.log(e);
+  });
+`;

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio