旅游教育出版社-数字教材移动端
litian
6 天以前 dde9556d66f10da626c979da6edd3e852a5ccc29
项目代码
2个文件已修改
132个文件已添加
27750 ■■■■■ 已修改文件
.browserslistrc 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.editorconfig 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintrc.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
babel.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 15524 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/android-chrome-192x192.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/android-chrome-512x512.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/android-chrome-maskable-192x192.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/android-chrome-maskable-512x512.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/apple-touch-icon-120x120.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/apple-touch-icon-152x152.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/apple-touch-icon-180x180.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/apple-touch-icon-60x60.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/apple-touch-icon-76x76.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/apple-touch-icon.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/favicon-16x16.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/favicon-32x32.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/msapplication-icon-144x144.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/mstile-150x150.png 补丁 | 查看 | 原始文档 | blame | 历史
public/img/icons/safari-pinned-tab.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/robots.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/base.css 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/common.css 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/font/FZYANSJW_ZHONG.TTF 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/font/兰亭黑 GBK.TTF 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/bookCity/place_img.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/default_avatar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/ce_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/guanwang.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/home-bg.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/jiaoshirenzheng.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/logo_1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/logo_2.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/search.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/shouce.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/shucheng.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/home/shuzijiaocai.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/nav/home-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/nav/home.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/nav/my-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/nav/my.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/nav/shujia-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/nav/shujia.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/banji.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/bg_my.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/default_avatar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/jiaocai.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/jiaoshikefu.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/jiaoshirenzheng.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/kecheng.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/order.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/renzheng_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/setting_2.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/shenqing.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/shoppingCart.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/tiaokuan.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/tuichu.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/wodeshoucang.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/xieyi.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/personalCenter/zhuxiao.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/tab_collection.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/tab_collection_pre.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/3D.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/Audio.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/PPT.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/Select_1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/VR.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/chongzhi.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/contact.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/goushuma.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/img.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/shidu.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/shijuan.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/shixun.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/shiyong.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/shoucang.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/tuozhan.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/video.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/textBook/ziliao.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/wx-icon.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/config.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/WebMiddleGroundApi.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/app.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/edu.js 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/file.js 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/identity.js 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/job.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/resource.js 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/store.js 600 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/api/ugc.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/middleGround/tool.js 384 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/toolClass.js 397 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/vConsole.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/weChat/share.js 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/weChat/weChat.js 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/webApi.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/footer/footer.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/backHeader.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/mainLayout.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/plugin/axios/index.js 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/registerServiceWorker.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes.js 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bookList/bookApply.vue 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bookList/bookDetail.vue 950 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bookList/bookList.vue 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/entrance/bindWeChatAuto.vue 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/entrance/entrance.vue 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/entrance/login.vue 432 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/entrance/weChatLogin.vue 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index/index.vue 687 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index/manual.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pay/pay.vue 1204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/about.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/collection.vue 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/index.vue 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/myApplyBook.vue 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/myOrder.vue 699 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/myShoppingCart.vue 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/myTextBook.vue 318 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/personInfor.vue 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/protocol.vue 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personalCenter/teacherCertificate.vue 979 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/testLogin.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.browserslistrc
New file
@@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead
.editorconfig
New file
@@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
.eslintrc.js
New file
@@ -0,0 +1,48 @@
module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: ["plugin:vue/essential", "@vue/standard"],
  parserOptions: {
    parser: "babel-eslint"
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    indent: ["off", 2],
    semi: ["off", "always"],
    "semi-style": ["off", "last"],
    "no-multiple-empty-lines": ["off", {
      max: 2
    }],
    "no-alert": "error",
    "no-empty-function": "off",
    "no-trailing-spaces": "off",
    "space-unary-ops": [
      "off",
      {
        nonwords: false,
        overrides: {
          "!": false,
          new: false
        }
      }
    ],
    "space-infix-ops": "off",
    "jsx-quotes": ["off", "prefer-double"],
    quotes: ["off", "double"],
    "space-before-function-paren": "off",
    eqeqeq: "off",
    "spaced-comment": "off",
    "prefer-const": "off",
    "no-undef": 'off',
    "operator-linebreak": 'off',
    "comma-dangle": 'off',
    'space-before-blocks': 0,
    properties: 0,
    "vue/no-parsing-error": [2, {
      "x-invalid-end-tag": false
    }]
  }
};
.gitignore
@@ -1,23 +1,22 @@
# Object files
*.o
*.ko
*.obj
*.elf
.DS_Store
node_modules
/dist
# Libraries
*.lib
*.a
# local env files
.env.local
.env.*.local
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
README.md
@@ -1,4 +1,34 @@
## testbook-lyj
旅游教育出版社-数字教材移动端
# mobile-demo
# ç§»åŠ¨ç«¯é¡¹ç›®åˆ›å»ºæ¨¡æ¿ï¼Œä¾èµ–æ³¨å…¥vue-Router、Axios、Vuex、PWA依赖项;
# å·²æ·»åŠ eslint代码验证;
# å·²é›†æˆvantUI框架;
# å·²å¯¼å…¥è·¯ç”±åµŒå¥—模板、路由拦截解决方案、请求拦截解决方案、token验证解决方案;
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
babel.config.js
New file
@@ -0,0 +1,5 @@
module.exports = {
  presets: [
    "@vue/cli-plugin-babel/preset"
  ]
};
package-lock.json
New file
Diff too large
package.json
New file
@@ -0,0 +1,45 @@
{
  "name": "mobile-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "echarts": "^5.6.0",
    "element-china-area-data": "^6.1.0",
    "moment": "^2.29.4",
    "register-service-worker": "^1.7.1",
    "spark-md5": "^3.0.2",
    "vconsole": "^3.9.1",
    "vue": "^2.6.11",
    "vue-clipboard2": "^0.3.1",
    "vue-router": "^3.2.0",
    "vue-video-player": "^5.0.2",
    "vuex": "^3.5.1",
    "weixin-js-sdk": "^1.6.0",
    "xlsx": "^0.18.5"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^4.4.0",
    "@vue/cli-plugin-eslint": "^4.4.0",
    "@vue/cli-plugin-pwa": "^4.4.0",
    "@vue/cli-service": "^4.4.0",
    "@vue/eslint-config-standard": "^5.1.2",
    "axios": "^0.19.2",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^6.2.2",
    "less": "^4.1.3",
    "less-loader": "^5.0.0",
    "vant": "^2.9.3",
    "vue-template-compiler": "^2.6.11"
  }
}
public/favicon.ico
public/img/icons/android-chrome-192x192.png
public/img/icons/android-chrome-512x512.png
public/img/icons/android-chrome-maskable-192x192.png
public/img/icons/android-chrome-maskable-512x512.png
public/img/icons/apple-touch-icon-120x120.png
public/img/icons/apple-touch-icon-152x152.png
public/img/icons/apple-touch-icon-180x180.png
public/img/icons/apple-touch-icon-60x60.png
public/img/icons/apple-touch-icon-76x76.png
public/img/icons/apple-touch-icon.png
public/img/icons/favicon-16x16.png
public/img/icons/favicon-32x32.png
public/img/icons/msapplication-icon-144x144.png
public/img/icons/mstile-150x150.png
public/img/icons/safari-pinned-tab.svg
New file
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
</svg>
public/index.html
New file
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0,user-scalable=no"
    />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title>数字教材</title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> 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>
<!-- <script
  type="text/javascript"
  src="https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"
></script>
<script>
  // åˆå§‹åŒ–
  var vConsole = new VConsole();
  console.log("Hello world");
</script> -->
public/robots.txt
New file
@@ -0,0 +1,2 @@
User-agent: *
Disallow:
src/App.vue
New file
@@ -0,0 +1,25 @@
<template>
  <div id="app">
    <router-view />
  </div>
</template>
<script>
export default {
  name: "app",
  methods: {
    onClick() {
      console.log(1111);
    }
  }
};
</script>
<style>
#app {
  width: 100%;
  height: 100%;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}
</style>
src/assets/css/base.css
New file
@@ -0,0 +1,157 @@
/*重置css*/
body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
form,
fieldset,
lengend,
input,
button,
textarea,
select,
p,
blockquote,
th,
td {
  margin: 0;
  padding: 0;
}
html,
body,
form,
fieldset,
p,
div,
h1,
h2,
h3,
h4,
h5,
h6 {
  -webkit-text-size-adjust: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}
img {
  border: 0 none;
  vertical-align: middle;
}
li {
  list-style-type: none;
}
caption,
th {
  text-align: left;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: 100%;
  font-weight: 400;
  font-style: normal;
}
body,
input,
button,
textarea,
select {
  font-family: "Microsoft Yahei", å¾®è½¯é›…黑, Helvetica, Arial, sans-serif;
}
button {
  cursor: pointer;
}
em,
cite,
i,
b {
  font-style: normal;
  font-weight: normal;
}
a {
  color: #333;
}
a:link {
  text-decoration: none;
}
:focus {
  outline: none;
}
* {
  -webkit-text-size-adjust: none;
  -moz-text-size-adjust: none;
  -ms-text-size-adjust: none;
  -o-text-size-adjust: none;
  text-size-adjust: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
}
::-ms-clear {
  display: none;
}
::-ms-reveal {
  display: none;
}
input,
button {
  border: none;
  outline: medium;
}
html {
  -webkit-tap-highlight-color: transparent;
} /*去除点击页面中元素时出现的阴影*/
html,
body {
  background-color: #fff;
  width: 100%;
  height: 100%;
  font-size: 14px;
  overflow: hidden;
}
body {
  -webkit-text-size-adjust: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
}
table {
  -webkit-user-select: text;
}
@font-face {
  font-family: "FZYANSJW_ZHONG";
  src: url(../font/FZYANSJW_ZHONG.TTF);
}
.textBookDescription .title {
  font-family: "FZYANSJW_ZHONG" !important;
  font-size: 20px;
  line-height: 40px;
  font-weight: bold;
}
.descriptionCon .con p {
  line-height: 18px !important;
  font-size: 14px !important;
  margin-bottom: 10px !important;
}
.van-tabbar--fixed {
  z-index: 1 !important;
}
src/assets/css/common.css
New file
@@ -0,0 +1,112 @@
/* ç”¨flex方式布局上中下页面结构 */
.article {
  /* display: flex;
  width: 100%;
  height: 100vh;
  flex-direction: column;
  overflow: auto; */
}
.article .main {
  flex-grow: 1;
  padding-top: 40px;
  /* background-color: #eeeeee; */
}
.van-nav-bar {
  height: 40px !important;
  z-index: 999 !important;
}
.common {
  /* background-color: #fff; */
  padding: 0 15px;
}
/* æœç´¢æ¡† */
.van-search__content {
  border-radius: 5px !important;
}
.flex {
  display: flex;
}
.flex1 {
  display: flex;
  justify-content: space-between;
}
.flex2 {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
}
.flex3 {
  display: flex;
  /* justify-content: space-between; */
  flex-wrap: wrap;
}
.flex4 {
  display: flex;
  align-items: center;
}
.flex5 {
  display: flex;
  flex-direction: column;
  text-align: left;
  margin-bottom: 15px;
}
/* å›¾ç‰‡ */
.imageBox {
  position: relative;
}
.imageBox img {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  max-width: 100%;
  max-height: 100%;
}
/* å›ºå®šå®šä½ä¸èƒ½è®¾ç½®é«˜åº¦ */
.footer {
  padding-top: 40px;
}
/* css首行缩进两个字符 */
.text_indent {
  text-indent: 35px;
}
.blank_gap {
  margin-top: 6px;
  margin-bottom: 6px;
}
.text-flow {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  word-break: break-all;
}
.autoImg {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}
src/assets/font/FZYANSJW_ZHONG.TTF
Binary files differ
src/assets/font/À¼Í¤ºÚ GBK.TTF
Binary files differ
src/assets/images/bookCity/place_img.png
src/assets/images/default_avatar.png
src/assets/images/home/ce_icon.png
src/assets/images/home/guanwang.png
src/assets/images/home/home-bg.png
src/assets/images/home/jiaoshirenzheng.png
src/assets/images/home/logo_1.png
src/assets/images/home/logo_2.png
src/assets/images/home/search.png
src/assets/images/home/shouce.png
src/assets/images/home/shucheng.png
src/assets/images/home/shuzijiaocai.png
src/assets/images/nav/home-fill.png
src/assets/images/nav/home.png
src/assets/images/nav/my-fill.png
src/assets/images/nav/my.png
src/assets/images/nav/shujia-fill.png
src/assets/images/nav/shujia.png
src/assets/images/personalCenter/banji.png
src/assets/images/personalCenter/bg_my.png
src/assets/images/personalCenter/default_avatar.png
src/assets/images/personalCenter/jiaocai.png
src/assets/images/personalCenter/jiaoshikefu.png
src/assets/images/personalCenter/jiaoshirenzheng.png
src/assets/images/personalCenter/kecheng.png
src/assets/images/personalCenter/order.png
src/assets/images/personalCenter/renzheng_icon.png
src/assets/images/personalCenter/setting_2.png
src/assets/images/personalCenter/shenqing.png
src/assets/images/personalCenter/shoppingCart.png
src/assets/images/personalCenter/tiaokuan.png
src/assets/images/personalCenter/tuichu.png
src/assets/images/personalCenter/wodeshoucang.png
src/assets/images/personalCenter/xieyi.png
src/assets/images/personalCenter/zhuxiao.png
src/assets/images/tab_collection.png
src/assets/images/tab_collection_pre.png
src/assets/images/textBook/3D.png
src/assets/images/textBook/Audio.png
src/assets/images/textBook/PPT.png
src/assets/images/textBook/Select_1.png
src/assets/images/textBook/VR.png
src/assets/images/textBook/chongzhi.png
src/assets/images/textBook/contact.png
src/assets/images/textBook/goushuma.png
src/assets/images/textBook/img.png
src/assets/images/textBook/shidu.png
src/assets/images/textBook/shijuan.png
src/assets/images/textBook/shixun.png
src/assets/images/textBook/shiyong.png
src/assets/images/textBook/shoucang.png
src/assets/images/textBook/tuozhan.png
src/assets/images/textBook/video.png
src/assets/images/textBook/ziliao.png
src/assets/images/wx-icon.png
src/assets/js/config.js
New file
@@ -0,0 +1,45 @@
// export const requestCtx = "http://182.92.203.7:3001"; // è¯·æ±‚地址
export const requestCtx = "https://www.tepcb.com"; // è¯·æ±‚地址
export const requestTimeOut = 300000; // è¯·æ±‚è¶…æ—¶æ—¶é—´
export const tokenKey = "website-front-token";
export const userInfoKey = "website-front-userInfo"; // ç”¨æˆ·ä¿¡æ¯key
// export const appId = 12;
export const appId = 2;
export const appRefCode = "tourismWebsite";
export const goodsStore = `defaultGoodsStore${appId}`; // é»˜è®¤å•†å“åº“(书城)
export const digitalTextBooksGoodsStore = `tourism_digitalTextbooks`;
export const publicStore = `defaultPublicStore${appId}`; // é»˜è®¤èµ„源开放仓储
export const publicRepository = `defaultPublicRepository${appId}`; // é»˜è®¤èµ„源开放库
export const regTel = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/; // ç”µè¯å·æ­£åˆ™
// export const textBookResourceUrl = "https://www.tepcb.com/books/resource/";
export const textBookResourceUrl = "http://182.92.203.7:3007/books/resource/";
export const textReaderUrl = "https://www.tepcb.com/tourismTextBookReader/#/home";
// é¢‘道refCode
export const refCodes = {
   textBooksStore: {
    textBookCategory: "tourism_allTextbooks", //全部教材
    textBookRecommendation: "tourism_recommendTextbooks", //推荐教材
  },
  //自定义收藏linkType
  LinkType: {
    FavoriteTextBook: "FavoriteTextBook",
  }
};
const config = {
  requestCtx,
  requestTimeOut,
  tokenKey,
  userInfoKey,
  appRefCode,
  goodsStore,
  publicStore,
  publicRepository,
  refCodes,
  regTel,
  digitalTextBooksGoodsStore,
  appId,
  textBookResourceUrl,
  textReaderUrl
};
export default config;
src/assets/js/middleGround/WebMiddleGroundApi.js
New file
@@ -0,0 +1,24 @@
// å–消接口调用能力;
// é˜Ÿåˆ—的必要性??
import resource from "./api/resource";
import store from "./api/store";
import identity from "./api/identity";
import ugc from "./api/ugc";
import app from "./api/app";
import file from "./api/file";
import job from "./api/job";
import edu from "./api/edu";
const WebMiddleGroundApi = {
  resource,
  store,
  identity,
  ugc,
  app,
  file,
  job,
  edu
};
export default WebMiddleGroundApi;
src/assets/js/middleGround/api/app.js
New file
@@ -0,0 +1,48 @@
import request from "@/plugin/axios";
const appApi = {
  // èŽ·å–ç”¨æˆ·æ¶ˆæ¯åˆ—è¡¨
  getAppMessageList(data) {
    return request({
      url: "/app/api/ApiGetAppMessageList",
      method: "post",
      data
    });
  },
  // èŽ·å–ç”¨æˆ·æ¶ˆæ¯è¯¦æƒ…
  getMessage(data) {
    return request({
      url: "/app/api/ApiGetMessage",
      method: "post",
      data
    });
  },
  //获取凭证
  getTicketResult(data) {
    return request({
      url: "/app/api/ApiGetTicketResult",
      method: "post",
      data
    });
  },
  //使用凭证
  useTicket(data) {
    return request({
      url: "/app/api/ApiUseTicket",
      method: "post",
      data
    });
  },
  // èŽ·å–CmsItem按照Event统计
  getEventRankList(data) {
    return request({
      url: "/app/api/ApiGetEventRankList",
      method: "post",
      data
    });
  },
};
export default appApi;
src/assets/js/middleGround/api/edu.js
New file
@@ -0,0 +1,239 @@
import request from "@/plugin/axios";
import { handleQueryResourceListData } from "../tool";
const eduApi = {
  /*
    **获取商品列表**
    path: æ•°æ®è·¯å¾„
    storeInfo: ä»“储
    channelInfo: é¢‘道
    subAccess:
    queryType: æ£€ç´¢ç±»åž‹
    paging: åˆ†é¡µ
    sort: æŽ’序
    fields: è‡ªå®šä¹‰å­—段
  */
  getCourseProductList: ({
    courseInfo = null,
    queryType,
    LinkProductId = null,
    paging = {},
    sort,
    fields,
    coverSize
  }) => {
    const query = {
      Type: queryType || "\\",
      CourseId: courseInfo + "",
      LinkProductId: LinkProductId || "",
      PageQuery: {
        Start: paging.start || "0",
        Size: paging.size || "10"
      },
      SortQuery: sort
        ? [sort]
        : [
            {
              LinkOrder: "Desc"
            }
          ],
      CreateDate: [],
      Description: [],
      Name: [],
      Icon: [],
      RefCode: [],
      TypeId: [],
      SysType: [],
      State: [],
      Tag: [],
      BeginDate: [],
      EndDate: [],
      ProductLinkInfo: [],
      AllowDonate: [],
      // DonatePriceList: [],
      SubProductCount: [],
      ...fields
    };
    const body = { query: JSON.stringify({ Query: [{ Q1: query }] }) };
    let url = "/edu/api/ApiAppUserQueryCourseProduct";
    return request({
      url: url,
      method: "post",
      data: body
    }).then((resp) => {
      if (resp.length > 0) {
        const data = resp[0];
        const datas = handleQueryResourceListData({
          datas: data.datas,
          fields,
          courseInfo,
          coverSize
        });
        return {
          datas,
          total: data.totalCount
        };
      }
      return { datas: [], total: 0 };
    });
  },
  //申请课程
  applyNewCourse(data) {
    return request({
      url: "/edu/api/ApiApplyNewCourse",
      method: "post",
      data
    });
  },
  //更新课程申请信息
  updateCourseApply(data) {
    return request({
      url: "/edu/api/ApiUpdateCourseApply",
      method: "post",
      data
    });
  },
  //获取我的课程
  getAppCourseList(data) {
    return request({
      url: "/edu/api/ApiGetAppCourseList",
      method: "post",
      data
    });
  },
  //获取已申请的课程详情
  getCourseById(data) {
    return request({
      url: "/edu/api/ApiGetCourseById",
      method: "post",
      data
    });
  },
  //获取创建课程中的班级列表
  getCourseClassList(data) {
    return request({
      url: "/edu/api/ApiGetCourseClassList",
      method: "post",
      data
    });
  },
  //创建班级信息
  newCourseClass(data) {
    return request({
      url: "/edu/api/ApiNewCourseClass",
      method: "post",
      data
    });
  },
  //删除班级信息
  delCourseClass(data) {
    return request({
      url: "/edu/api/ApiDelCourseClass",
      method: "post",
      data
    });
  },
  // æ›´æ–°ç­çº§ä¿¡æ¯
  updateCourseClass(data) {
    return request({
      url: "/edu/api/ApiUpdateCourseClass",
      method: "post",
      data
    });
  },
  //创建课程订单
  createCourseOrder(data) {
    return request({
      url: "/store/api/CreateCourseOrder",
      method: "post",
      data
    });
  },
  // å…è´¹é¢†å–课程
  MakeFreeOrderPay(data) {
    return request({
      url: "/store/api/MakeFreeOrderPay",
      method: "post",
      data
    });
  },
  // èŽ·å–æŽ’åç»“æžœ
  getRankingList(data) {
    return request({
      url: "/edu/api/ApiGetRankingList",
      method: "post",
      data
    });
  },
  // èŽ·å–ç»„å·ç»“æžœ
  getEduQuizConfigResult(data) {
    return request({
      url: "/edu/api/ApiGetEduQuizConfigResult",
      method: "post",
      data
    });
  },
  // æ–°å»ºæŽ’名记录
  NewRanking(data) {
    return request({
      url: "/edu/api/ApiNewRanking",
      method: "post",
      data
    });
  },
  // èŽ·å–å•†å“ä¸‹ç»„å·é…ç½®åˆ—è¡¨
  getQuizConfigListByProduct(data) {
    return request({
      url: "/edu/api/ApiGetQuizConfigListByProduct",
      method: "post",
      data
    });
  },
  // åˆ é™¤ç»„卷规则
  DelQuizConfig(data) {
    return request({
      url: "/edu/admin/DelQuizConfig",
      method: "post",
      data
    });
  },
  // æ›´æ–°ç»„卷规则
  UpdateQuizConfig(data) {
    return request({
      url: "/edu/admin/UpdateQuizConfig",
      method: "post",
      data
    });
  },
  // æ ¹æ®ID获取组卷配置
  GetQuizConfig(data) {
    return request({
      url: "/edu/admin/GetQuizConfig",
      method: "post",
      data
    });
  },
};
export default eduApi;
src/assets/js/middleGround/api/file.js
New file
@@ -0,0 +1,40 @@
import request from "@/plugin/axios";
const fileApi = {
  // èŽ·å–pdf总页数
  getPdfTotalPage(data) {
    return request({
      url: "/file/GetPdfTotalPage",
      method: "post",
      data
    });
  },
  // èŽ·å–pdf、word、ppt等转图片预览
  getPdfInfo(data) {
    return request({
      url: "/file/GetPdfInfo",
      method: "post",
      data
    });
  },
  //上传文件
  upload(data) {
    return request({
      url: "/file/api/ApiUpload",
      method: "post",
      data
    });
  },
  // èŽ·å–é˜¿é‡Œäº‘åŠ é€Ÿåœ°å€
  getAliVod(data) {
    return request({
      url: "/file/GetAliVod",
      method: "post",
      data,
    });
  },
};
export default fileApi;
src/assets/js/middleGround/api/identity.js
New file
@@ -0,0 +1,127 @@
import request from "@/plugin/axios";
const identityApi = {
  // èŽ·å–å›¾å½¢éªŒè¯ç 
  getImgCode() {
    return request({
      url: "/identity/NewCaptcha",
      method: "post",
    });
  },
  // èŽ·å–çŸ­ä¿¡éªŒè¯ç 
  getPhoneCode(data) {
    return request({
      url: "/identity/NewSms",
      method: "post",
      data,
    });
  },
  // é€šè¿‡æ‰‹æœºå·æ³¨å†Œç”¨æˆ·
  registerAppUserWithPhone(data) {
    return request({
      url: "/identity/api/RegisterAppUserWithPhone",
      method: "post",
      data,
    });
  },
  // è´¦å·å¯†ç ç™»å½•
  loginByPassword(data) {
    return request({
      url: "/identity/api/LoginByPassword",
      method: "post",
      data,
    });
  },
  // çŸ­ä¿¡éªŒè¯ç ç™»å½•
  loginByMobilePhone(data) {
    return request({
      url: "/identity/api/LoginByMobilePhone",
      method: "post",
      data,
    });
  },
  // è®¾ç½®ç”¨æˆ·key
  setUserKey(data) {
    return request({
      url: "/identity/api/ApiAppUserSetKey",
      method: "post",
      data,
    });
  },
  // èŽ·å–ç”¨æˆ·key
  getUserKey(data) {
    return request({
      url: "/identity/api/ApiGetAppUserKey",
      method: "post",
      data,
    });
  },
  // åˆ é™¤ç”¨æˆ·key
  delUserKey(data) {
    return request({
      url: "/identity/api/ApiDelAppUserKey",
      method: "post",
      data,
    });
  },
  // èŽ·å–åŽ»å½“å‰ç”¨æˆ·ä¿¡æ¯
  getCurrentAppUser() {
    return request({
      url: "/identity/api/GetCurrentAppUser",
      method: "post",
    });
  },
  // æ·»åŠ ç”¨æˆ·ä¿¡æ¯
  setAppUserInfo(data) {
    return request({
      url: "/identity/api/SetAppUserInfoRequest",
      method: "post",
      data,
    });
  },
  // æ£€æŸ¥ç”¨æˆ·å¾®ä¿¡æ˜¯å¦æ³¨å†Œ
  chechWechatAccount(data) {
    return request({
      url: "/identity/api/CheckWeChatAccount",
      method: "post",
      data,
    });
  },
  // ç»‘定微信用户
  bindWeChat(data) {
    return request({
      url: "/identity/api/ApiBindingWeChat",
      method: "post",
      data,
    });
  },
  // ç»‘定手机号
  bindingMobilePhone(data) {
    return request({
      url: "/identity/api/ApiBindingMobilePhone",
      method: "post",
      data,
    });
  },
  // æ³¨é”€è´¦å·,
  unsubscribeAppuser(data) {
    return request({
      url: "/identity/api/ApiWithdraw",
      method: "post",
      data,
    });
  }
};
export default identityApi;
src/assets/js/middleGround/api/job.js
New file
@@ -0,0 +1,33 @@
import request from "@/plugin/axios";
import { tokenKey } from "@/assets/js/config";
import toolClass from "@/assets/js/toolClass";
let token = toolClass.getCookie(tokenKey);
const jobApi = {
  // ç»Ÿè®¡
  newJobWithNewView(data) {
    return request({
      url: "/job/api/NewJobWithNewView",
      method: "post",
      data
    });
  },
  newSession(data) {
    return request({
      url: token ? "/job/api/AppUserNewSession" : "/job/api/NewSession",
      method: "post",
      data
    });
  },
  newJobWithApiNewEvent(data){
    return request({
      url: "/job/api/NewJobWithApiNewEvent",
      method: "post",
      data
    });
  }
};
export default jobApi;
src/assets/js/middleGround/api/resource.js
New file
@@ -0,0 +1,215 @@
import request from "@/plugin/axios";
import { publicStore, publicRepository, tokenKey, } from "@/assets/js/config";
import { handleQueryResourceListData } from "../tool";
import toolClass from "@/assets/js/toolClass";
const resourceApi = {
  /*
    **获取资源列表&详情**
    path: æ•°æ®è·¯å¾„
    storeInfo: ä»“储
    repositoryInfo: ä»“库
    queryType: æ£€ç´¢ç±»åž‹
    paging: åˆ†é¡µ
    sort: æŽ’序
    fields: è‡ªå®šä¹‰å­—段
    itemId: èŽ·å–è¯¦æƒ…æ—¶çš„èµ„æºID
  */
  getItem: ({
    path,
    storeInfo = publicStore,
    repositoryInfo = publicRepository,
    queryType,
    linkType,
    paging,
    sort,
    fields,
    itemId,
    itemType,
    coverSize,
    itemIdArr,
    SysType,
    tourismIsHighQualityResources,
  }) => {
    if (!path) return Promise.reject(new Error("接口请求必要参数不能为空!"));
    const query = {
      AccessControl: {
        Path: path,
        StoreRefCode: storeInfo + "",
        RepositoryRefCode: repositoryInfo + "",
        Type: queryType || "\\",
        LinkType: linkType || "",
      },
      PageQuery: {
        Start: paging?.start || "0",
        Size: paging?.size || "10",
      },
      SortQuery: sort ? [sort] : [],
      CreateDate: [],
      Description: [],
      Name: [],
      Icon: [],
      RefCode: [],
      Type: [],
      TypeId: [],
      State: [],
      Tag: [],
      LinkInfo: [],
      LinkFile: [],
      CmsItemType: [],
      ChildrenFolderCount: [],
      ChildrenCount: [],
      ...fields,
    };
    if (itemIdArr) query.Id = itemIdArr;
    if (SysType) query["SysType="] = [`${SysType}`];
    if (tourismIsHighQualityResources){
      query["tourismIsHighQualityResources="] = [
        `${tourismIsHighQualityResources}`,
      ];
    }
    if (itemId) query["Id="] = [`${itemId}`];
    if (itemType) query["Type="] = [`${itemType}`];
    const body = { query: JSON.stringify({ Query: [{ Q1: query }] }) };
    let token = toolClass.getCookie(tokenKey);
    return request({
      url: token ? "/resource/api/ApiAppUserQuery" : "/resource/api/ApiQuery",
      method: "post",
      data: body,
    }).then((resp) => {
      if (resp.length > 0) {
        const data = resp[0];
        const datas = handleQueryResourceListData({
          datas: data.datas,
          fields,
          path,
          storeInfo,
          repositoryInfo,
          coverSize,
        });
        return { datas, total: data.totalCount };
      }
      return { datas: [], total: 0 };
    });
  },
  // æ¨¡ç³Šæœç´¢
  EsQuery(query) {
    let { params, path, paging } = query;
    let queryBody = {
      Type: "*",
      Store: [],
      ItemType: [],
      CmsType: [],
      SysType: ["CmsItem"],
      LinkInfo: [],
      PageQuery: {
        Start: paging?.start || "0",
        Size: paging?.size || "10",
      },
    };
    // è®¾ç½®ä¸åŒçš„Path
    // å¦‚果没有搜索关键字,则只用获取名称
    if (params.length > 0) {
      let data = {
        "||Name": [...params],
        "||tourism_content*": [...params],
        "||tourism_workflow*": [...params],
        "||tourism_notes*": [...params],
        "||tourism_caseIndex*": [...params],
        "||tourism_basicCase*": [...params],
        "||tourism_judgmentAndReasons*": [...params],
        "||tourism_legalIssuesInvolvedInThisCase*": [...params],
        "||tourism_referenceAnswerAndLegalAnalysis*": [...params],
        "||tourism_case*": [...params],
        "||tourism_clause*": [...params],
        "||tourism_unscramble*": [...params],
        "||tourism_specialRemind*": [...params],
        "||tourism_fiction*": [...params],
        "||tourism_authorityNature*": [...params],
        "||tourism_authorityGist*": [...params],
        "||tourism_penaltyGist*": [...params],
        "||tourism_penaltyTerms*": [...params],
        "||tourism_penaltyType*": [...params],
        "||tourism_remarksNote*": [...params],
        "||tourism_keyword*": [...params],
      };
      Object.assign(queryBody, data);
    } else {
      let data = {
        Name: [],
      };
      Object.assign(queryBody, data);
    }
    // å…¨éƒ¨æœç´¢  - å•独的某个库
    if (path != "All") {
      let data = {
        Path: [
          {
            Repository: "tourism_tourismLawsAndRegulationsDatabase",
            Path: path,
          },
        ],
      };
      Object.assign(queryBody, data);
    }
    const body = { query: JSON.stringify({ Query: [{ Q1: queryBody }] }) };
    return request({
      url: "/resource/api/ApiEsQuery",
      method: "post",
      data: body,
    }).then((res) => {
      if (res.length > 0) {
        const data = res[0];
        const datas = handleQueryResourceListData({
          datas: data.datas,
          path,
        });
        return { datas, total: data.totalCount };
      }
      return { data: [], total: 0 };
    });
  },
  //获取资源类型
  getCmsTypeByRefCode(data) {
    return request({
      url: "resource/api/ApiGetCmsTypeByRefCode",
      method: "post",
      data,
    });
  },
  //取消cms收藏
  delCmsItemLink(data) {
    return request({
      url: "/resource/api/ApiDelCmsItemLink",
      method: "post",
      data,
    });
  },
  //cms收藏
  collectCmsItem(data) {
    return request({
      url: "/resource/api/ApiAddCmsItemLink",
      method: "post",
      data,
    });
  },
  // èŽ·å–cms收藏列表
  getCmsCollectList(data) {
    const body = { query: JSON.stringify({ Query: [{ Q1: data }] }) };
    return request({
      url: "/resource/api/ApiAppUserQuery",
      method: "post",
      data: body,
    });
  },
};
export default resourceApi;
src/assets/js/middleGround/api/store.js
New file
@@ -0,0 +1,600 @@
import request from "@/plugin/axios";
import { tokenKey, goodsStore } from "@/assets/js/config";
import toolClass from "@/assets/js/toolClass";
import { handleQueryResourceListData, handleDetailQueryRequestData } from "../tool";
const storeApi = {
  /*
    **获取商品列表**
    path: æ•°æ®è·¯å¾„
    storeInfo: ä»“储
    channelInfo: é¢‘道
    subAccess:
    queryType: æ£€ç´¢ç±»åž‹
    paging: åˆ†é¡µ
    sort: æŽ’序
    fields: è‡ªå®šä¹‰å­—段
  */
  getProductList: ({
    path = "",
    storeInfo = goodsStore,
    storeEventIdOrRefCode = "",
    queryType,
    linkType,
    subAccess = [],
    paging = {},
    sort,
    fields,
    filterList,
    coverSize,
    mainProductId,
    handelEBooK
  }) => {
    const query = {
      AccessControl: {
        Path: path,
        StoreRefCode: storeInfo + "",
        Type: queryType || "\\",
        LinkType: linkType || ""
      },
      SubAccess: subAccess.length > 0 ? subAccess : [],
      PageQuery: {
        Start: paging.start || "0",
        Size: paging.size || "10"
      },
      SortQuery:
        sort?.length == 0
          ? []
          : sort
          ? [sort]
          : [
              {
                LinkOrder: "Desc"
              }
            ],
      CreateDate: [],
      Description: [],
      Name: [],
      Icon: [],
      RefCode: [],
      TypeId: [],
      SysType: [],
      State: [],
      Tag: [],
      BeginDate: [],
      EndDate: [],
      ProductLinkInfo: [],
      AllowDonate: [],
      // DonatePriceList: [],
      StoreEvent: [],
      SubProductCount: [],
      SaleMethod: [],
      SaleMethodValid: [],
      StoreEventIdOrRefCode: storeEventIdOrRefCode,
      ...fields,
      ...filterList
    };
    if (mainProductId) {
      query.AccessControl.MainProductId = mainProductId;
    }
    if (handelEBooK) {
      query.ProductCmsQuery = [
        {
          QueryCms: {
            Path: "*",
            Type: "\\",
            Name: [],
            Icon: [],
            TypeId: [],
            RefCode: [],
            ChildrenCount: [],
            ChildrenFolderCount: [],
            CreateDate: [],
            SysType: [],
            SaleMethod: [],
            PageQuery: {
              Start: 0,
              Size: 9999,
            },
            ProductLinkInfo: [],
          },
        },
      ];
    }
    const body = {
      query: JSON.stringify({
        Query: [
          {
            Q1: query
          }
        ]
      })
    };
    let token = toolClass.getCookie(tokenKey);
    let url = token ? "/store/api/ApiQueryProductByAppUser" : "/store/api/ApiQueryProduct";
    return request({
      url: url,
      method: "post",
      data: body
    }).then(resp => {
      if (resp.length > 0) {
        const data = resp[0];
        const datas = handleQueryResourceListData({
          datas: data.datas,
          fields,
          path,
          storeInfo,
          coverSize,
          handelEBooK,
        });
        return {
          datas,
          total: data.totalCount,
          extraInfos: data.extraInfos?.StoreEvent
        };
      }
      return {
        datas: [],
        total: 0
      };
    });
  },
  /*
    **获取商品详情**
    path: æ•°æ®è·¯å¾„
    storeInfo: ä»“储
    channelInfo: é¢‘道
    subAccess:
    fields: è‡ªå®šä¹‰å­—段
    productId: å•†å“ID
    cmsPath:cmsPath
  */
  getProductDetail: ({
    path = "",
    storeInfo = goodsStore,
    channelInfo = "",
    queryType,
    subAccess = [],
    fields,
    productId,
    cmsPath,
    coverSize,
    itemId,
    itemFields,
    linkTypes,
    filterList,
    handelEBooK,
  }) => {
    const subQuery = {};
    if (cmsPath) {
      subQuery.QueryCms = {
        Path: cmsPath + "",
        Type: "\\",
        Name: [],
        Icon: [],
        TypeId: [],
        RefCode: [],
        ChildrenCount: [],
        ChildrenFolderCount: [],
        CreateDate: [],
        SysType: [],
        SaleMethod: [],
        PageQuery: {
          Start: 0,
          Size: 100
        },
        ProductLinkInfo: [],
        ...itemFields
      };
      if (itemId) subQuery.QueryCms["Id="] = [`${itemId}`];
    } else {
      subQuery.QueryCms= {
        Path: "*",
        Type: "\\",
        Name: [],
        Icon: [],
        TypeId: [],
        RefCode: [],
        ChildrenCount: [],
        ChildrenFolderCount: [],
        CreateDate: [],
        SysType: [],
        SaleMethod: [],
        PageQuery: {
          Start: 0,
          Size: 9999,
        },
        ProductLinkInfo: [],
      };
    }
    // èŽ·å–å…³è”èµ„æº
    let linkFields = {};
    if (linkTypes && linkTypes.length) {
      for (let i = 0; i < linkTypes.length; i++) {
        const linkType = linkTypes[i];
        subQuery["QueryLink_" + linkType.linkType] = {
          Path: cmsPath + "",
          Type: "\\",
          Name: [],
          Icon: [],
          TypeId: [],
          RefCode: [],
          LinkTypes: [linkType.linkType],
          PageQuery: {
            Start: 0,
            Size: 100
          },
          ProductLinkInfo: [],
          ...linkType.fields
        };
        linkFields = {
          ...linkFields,
          ...linkType.fields
        };
      }
    }
    const query = {
      AccessControl: {
        Path: path,
        StoreRefCode: storeInfo + "",
        ChannelRefCode: channelInfo + "",
        Type: queryType || "\\"
      },
      SubAccess: subAccess.length > 0 ? subAccess : [],
      PageQuery: {
        Start: "0",
        Size: "1"
      },
      "Id=": [`${productId}`],
      SortQuery: [],
      CreateDate: [],
      Description: [],
      Name: [],
      Icon: [],
      RefCode: [],
      TypeId: [],
      SysType: [],
      State: [],
      Tag: [],
      BeginDate: [],
      EndDate: [],
      ProductLinkInfo: [],
      AllowDonate: [],
      DonatePriceList: [],
      StoreEvent: [],
      SaleMethod: [],
      SaleMethodValid: [],
      ProductCmsQuery: [subQuery],
      ...fields,
      ...filterList
    };
    if (productId) query["Id="] = [`${productId}`];
    const body = {
      query: JSON.stringify({
        Query: [
          {
            Q1: query
          }
        ]
      })
    };
    let token = toolClass.getCookie(tokenKey);
    let url = token ? "/store/api/ApiQueryProductByAppUser" : "/store/api/ApiQueryProduct";
    return request({
      url: url,
      method: "post",
      data: body
    }).then(resp => {
      if (resp.length > 0) {
        const data = resp[0];
        if (data.datas.length) {
          data.datas[0].subDatas = data.datas[0].cmsDatas;
          const datas = handleDetailQueryRequestData({
            item: data.datas[0],
            fields,
            itemFields: {
              ...itemFields,
              ...linkFields
            },
            path,
            coverSize,
            handelEBooK
          });
          return {
            datas,
            total: data.totalCount
          };
        } else {
          return {
            datas: [],
            total: 0
          };
        }
      }
      return {
        datas: [],
        total: 0
      };
    });
  },
  // èŽ·å–ä¼˜æƒ åˆ¸åˆ—è¡¨
  getChannelPromoteCodeList(data) {
    return request({
      url: "/store/api/ApiGetChannelPromoteCodeList",
      method: "post",
      data
    });
  },
  // èŽ·å–ç”¨æˆ·å·²é¢†å–çš„ä¼˜æƒ åˆ¸åˆ—è¡¨
  getPromoteCodeList(data) {
    return request({
      url: "/store/api/ApiGetPromoteCodeList",
      method: "post",
      data
    });
  },
  // èŽ·å–ç”¨æˆ·æµè§ˆæŽ’è¡Œ
  getProductViewRank(data) {
    return request({
      url: "/store/api/ApiGetProductViewRank",
      method: "post",
      data
    });
  },
  // èŽ·å–ç”¨æˆ·é”€å”®æŽ’è¡Œ
  getProductSaleRank(data) {
    return request({
      url: "/store/api/ApiGetProductSaleRank",
      method: "post",
      data
    });
  },
  // èŽ·å–å•†å“å¯ç”¨ä¼˜æƒ åˆ¸
  getProductPromoteCodeList(data) {
    return request({
      url: "/store/api/ApiGetProductPromoteCodeList",
      method: "post",
      data
    });
  },
  // èŽ·å–è®¢å•å¯ç”¨ä¼˜æƒ åˆ¸
  getOrderPromoteCodeList(data) {
    return request({
      url: "/store/api/GetOrderPromoteCodeList",
      method: "post",
      data
    });
  },
  // èŽ·å–é”€å”®æ–¹å¼å¯ç”¨ä¼˜æƒ åˆ¸
  getSaleMethodPromoteCodeList(data) {
    return request({
      url: "/store/api/GetSaleMethodPromoteCodeList",
      method: "post",
      data
    });
  },
  // é¢†å–优惠卷
  getPromoteCode(data) {
    return request({
      url: "/store/api/ApiGetPromoteCode",
      method: "post",
      data
    });
  },
  // ä¸ºè®¢å•使用优惠券
  updateOrderPromoteCode(data) {
    return request({
      url: "/store/api/UpdateOrderPromoteCode",
      method: "post",
      data
    });
  },
  // ä¸ºé”€å”®æ–¹å¼ä½¿ç”¨ä¼˜æƒ åˆ¸
  updateSaleMethodPromoteCode(data) {
    return request({
      url: "/store/api/UpdateSaleMethodPromoteCode",
      method: "post",
      data
    });
  },
  // é€šè¿‡è®¢å•号获取订单
  getOrderByOrderNum(data) {
    return request({
      url: "/store/api/GetOrderByOrderNum",
      method: "post",
      data
    });
  },
  // åˆ›å»ºè®¢å•
  initOrder(data) {
    return request({
      url: "/store/api/InitOrder",
      method: "post",
      data
    });
  },
  // ç¡®è®¤è®¢å•
  confirmOrder(data) {
    return request({
      url: "/store/api/ConfirmOrder",
      method: "post",
      data
    });
  },
  // å–消订单
  cancelOrder(data) {
    return request({
      url: "/store/api/CancelOrder",
      method: "post",
      data
    });
  },
  // èµžèµ
  CreateDonateOrder(data) {
    return request({
      url: "/store/api/CreateDonateOrder",
      method: "post",
      data
    });
  },
  //获取用户订单列表
  getUserOrderList(data) {
    return request({
      url: "/store/api/GetUserOrderList",
      method: "post",
      data
    });
  },
  //获取频道下的列表
  getStoreChannelList(data) {
    return request({
      url: "/store/api/ApiGetStoreChannelList",
      method: "post",
      data
    });
  },
  //获取购物车
  getShoppingCartProductList(data) {
    return request({
      url: "/store/api/ApiGetShoppingCartProductList",
      method: "post",
      data
    });
  },
  //添加到购物车
  addShoppingCart(data) {
    return request({
      url: "/store/api/ApiAddShoppingCart",
      method: "post",
      data
    });
  },
  //购物车删除商品
  delShoppingCart(data) {
    return request({
      url: "/store/api/ApiDelShoppingCart",
      method: "post",
      data
    });
  },
  //从购物车创建订单
  shoppingCartCreateOrder(data) {
    return request({
      url: "/store/api/ApiShoppingCartCreateOrder",
      method: "post",
      data
    });
  },
  //获取已购买的商品列表
  getPurchasedProductList(data) {
    return request({
      url: "/store/api/ApiGetPurchasedProductList",
      method: "post",
      data
    });
  },
  //调取微信支付
  makeWeChatPay(data) {
    return request({
      url: "/store/api/MakeWeChatPay",
      method: "post",
      data
    });
  },
  //调取微信二维码支付
  makeWeChatQrPay(data) {
    return request({
      url: "/store/api/MakeWeChatQrPay",
      method: "post",
      data
    });
  },
  // èŽ·å–æ¿€æ´»ç è¯¦æƒ…
  getActiveCode(data) {
    return request({
      url: "/store/api/ApiGetActiveCode",
      method: "post",
      data
    });
  },
  // ä½¿ç”¨æ¿€æ´»ç 
  userActiveCode(data) {
    return request({
      url: "/store/api/ApiUseActiveCode",
      method: "post",
      data
    });
  },
  // å•†å“æŸ¥è¯¢ç±»åž‹å­—段接口
  getProductTypeField(data) {
    return request({
      url: "/store/api/ApiGetProductTypeField",
      method: "post",
      data
    });
  },
  // æ”¶è—æˆ–加入书架
  addProductLink(data) {
    return request({
      url: "/store/api/ApiAddProductLink",
      method: "post",
      data
    });
  },
  // å–消收藏或移除书架
  delProductLink(data) {
    return request({
      url: "/store/api/ApiDelProductLink",
      method: "post",
      data
    });
  },
  //购买免费商品
  MakeFreeOrderPay(data) {
    return request({
      url: "/store/api/MakeFreeOrderPay",
      method: "post",
      data
    });
  },
  //查询商品所在store
  getProductStore(data) {
    return request({
      url: "/store/api/ApiGetProductStore",
      method: "post",
      data
    });
  }
};
export default storeApi;
src/assets/js/middleGround/api/ugc.js
New file
@@ -0,0 +1,75 @@
import request from "@/plugin/axios";
import { tokenKey } from "@/assets/js/config";
import toolClass from "@/assets/js/toolClass";
let token = toolClass.getCookie(tokenKey);
const ugcApi = {
  // èŽ·å–å•†å“ç‚¹èµžTopic
  getProductLikesTopic(data) {
    return request({
      url: token
        ? "/ugc/api/ApiAppUserGetProductLikesTopic"
        : "/ugc/api/ApiGetProductLikesTopic",
      method: "post",
      data
    });
  },
  // èŽ·å–å•†å“è¯„è®ºTopic
  getProductCommentTopic(data) {
    return request({
      url: token
        ? "/ugc/api/ApiAppUserGetProductCommentTopic"
        : "/ugc/api/ApiGetProductCommentTopic",
      method: "post",
      data
    });
  },
  // èŽ·å–MessageList
  getTopicMessageList(data) {
    return request({
      url: toolClass.getCookie(tokenKey)
        ? "/ugc/api/ApiAppUserGetTopicMessageList"
        : "/ugc/api/ApiGetTopicMessageList",
      method: "post",
      data
    });
  },
  // æ–°å»ºMessage
  newTopicMessage(data) {
    return request({
      url: "/ugc/api/ApiNewTopicMessage",
      method: "post",
      data
    });
  },
  // åˆ é™¤Message
  delTopicMessage(data) {
    return request({
      url: "/ugc/api/ApiDelTopicMessage",
      method: "post",
      data
    });
  },
  // èŽ·å–cms评论
  getCmsItemCommentTopic(data) {
    return request({
      url: "/ugc/api/ApiGetCmsItemCommentTopic",
      method: "post",
      data
    });
  },
  //更新TOPICMESSAGE
  updateTopicMessage(data) {
    return request({
      url: "/ugc/api/ApiUpdateTopicMessage",
      method: "post",
      data
    });
  }
};
export default ugcApi;
src/assets/js/middleGround/tool.js
New file
@@ -0,0 +1,384 @@
import { requestCtx } from "@/assets/js/config";
import moment from "moment";
// å¤„理列表查询结果
export function handleQueryResourceListData({
  datas,
  fields,
  path,
  storeInfo,
  repositoryInfo,
  coverSize,
  handelEBooK,
}) {
  const dataList = [];
  for (let i = 0; i < datas.length; i++) {
    const item = datas[i];
    // å¤„理字段
    const _fields = {};
    if (fields != null) {
      for (let fieldKey in fields) {
        if (item.datas[fieldKey]) {
          const values = JSON.parse(item.datas[fieldKey]);
          if (values.length > 0) {
            // ç”¨å­—段名处理返回的字段值
            if (values[0].Value) {
              _fields[fieldKey] = values[0].Value;
            } else if (values[0].Data) {
              _fields[fieldKey] = values[0].Data.Value;
            }
            item.datas[fieldKey] = values[0];
          }
        }
      }
    }
    const subDatas = {};
    if (item.subDatas) {
      for (let subData of item.subDatas) {
        const tag = subData.queryTag.replace("Query", "");
        subDatas[tag] = subData.datas;
      }
    }
    let obj = {
      ...item,
      id: item.id,
      name: item.datas.Name,
      icon: getPublicImage(
        item.datas.Icon,
        coverSize?.width,
        coverSize?.height
      ),
      repositoryInfo: repositoryInfo,
      refCode: item.datas.RefCode === "[]" ? null : item.datas.RefCode,
      state: item.datas.State,
      type: item.datas.Type,
      tag: item.datas.Tag,
      creator: item.datas.Creator ? JSON.parse(item.datas.Creator) : undefined,
      storeInfo: storeInfo,
      linkType: item.datas.LinkType,
      childrenCount: parseInt(item.datas.ChildrenCount ?? "0"),
      childrenFolderCount: parseInt(item.datas.ChildrenFolderCount ?? "0"),
      childrenChannelCount: parseInt(item.datas.ChildrenChannelCount ?? "0"),
      createDate: moment(item.datas.CreateDate).format("YYYY-MM-DD"),
      beginDate: moment(item.datas.BeginDate).format("YYYY-MM-DD"),
      endDate: moment(item.datas.EndDate).format("YYYY-MM-DD"),
      description: item.datas.Description,
      sysType: item.datas.SysType,
      idPath: path + "\\" + item.id,
      typeId: parseInt(item.datas.TypeId),
      linkFile: JSON.parse(item.datas.LinkFile ?? "[]"),
      cmsItemType: item.datas.CmsItemType,
      allowDonate: item.datas.AllowDonate == "True",
      // donatePriceList: JSON.parse(item.datas.DonatePriceList ?? "[]"),
      productLinkInfo: item.datas.ProductLinkInfo ?? "[]",
      storeEvent: JSON.parse(item.datas.StoreEvent ?? "[]"),
      linkInfo: JSON.parse(item.datas.LinkInfo ?? "[]"),
      saleMethod: JSON.parse(item.datas.SaleMethod ?? "[]"),
      subProductCount: parseInt(item.datas.SubProductCount),
      ..._fields,
      datas: item.datas,
      subDatas,
    };
    // ç»Ÿä¸€å¤„理价格
    if (obj.defaultSaleMethod) {
      if (handelEBooK) {
        // èŽ·å–éšä¹¦èµ„æºçš„é”€å”®æ–¹å¼
        let saleMethod = [];
        try {
          saleMethod = obj.cmsDatas[0].datas.find(
            (item) => item.datas.RefCode == "tourism_accompanyingResources"
          ).datas.SaleMethod;
          saleMethod = JSON.parse(saleMethod);
        } catch (error) {
          saleMethod = [];
        }
        if (saleMethod.length) {
          Object.keys(saleMethod[0]).map((key) => {
            let newKey = key.replace(key[0], key[0].toLowerCase());
            saleMethod[0][newKey] = saleMethod[0][key];
            delete saleMethod[0][key];
          });
          obj.defaultSaleMethod = saleMethod[0];
          obj.defaultSaleMethodId = saleMethod[0].id;
          obj.alreadyBuy =
            obj.purchasedSaleMethodIdList.indexOf(obj.defaultSaleMethodId) > -1;
        }
      }
      if (obj.defaultSaleMethod.allowEvent && obj.storeEvent.length) {
        // å·²å‚加活动,获取有效活动并计算价格
        let time = new Date().getTime();
        // è¿‡æ»¤è¿‡æœŸæ´»åЍ
        let event = obj.storeEvent.filter((item) => {
          let endTime = new Date(item.EndDate).getTime();
          return endTime > time;
        });
        // åªå¤„理一条有效活动
        if (event.length) {
          obj.price = (obj.defaultSaleMethod.price * event[0].Value).toFixed(2);
          obj.oldPrice = obj.defaultSaleMethod.price;
          obj.storeEventId = event[0].Id;
          if (
            moment().format("YYYY-MM-DD") <
            moment(obj.defaultSaleMethod.endDate).format("YYYY-MM-DD")
          ) {
            obj.defaultSaleMethodState = "Normal";
          } else {
            obj.defaultSaleMethodState = "Beyond";
          }
        }
      } else {
        if (
          moment().format("YYYY-MM-DD") <
          moment(obj.defaultSaleMethod.endDate).format("YYYY-MM-DD")
        ) {
          obj.defaultSaleMethodState = "Normal";
        } else {
          obj.defaultSaleMethodState = "Beyond";
        }
        obj.price = obj.defaultSaleMethod.price;
        obj.oldPrice = obj.defaultSaleMethod.virtualPrice;
      }
    }
    dataList.push(obj);
  }
  return dataList;
}
// å¤„理详情查询结果
export function handleDetailQueryRequestData({
  item,
  fields,
  path,
  coverSize,
  itemFields,
  handelEBooK,
}) {
  let itemFieldsData = [];
  for (const key in itemFields) {
    itemFieldsData.push(key);
  }
  let fieldsData = [];
  for (const key in fields) {
    fieldsData.push(key);
  }
  for (let i = 0; i < fieldsData.length; i++) {
    const field = fieldsData[i];
    item.datas[field] = JSON.parse(item.datas[field]);
    const datas = item.datas[field];
    if (datas.length > 0) {
      if (datas[0].Value) {
        item[field] = datas[0].Value;
      } else if (datas[0].Data) {
        item[field] = datas[0].Data.Value;
      }
    }
  }
  // å¤„理cms资源
  const subDatas = item.subDatas;
  const linkItemsMap = {};
  if (subDatas) {
    for (const sdata of subDatas) {
      const tag = sdata.queryTag;
      for (const subItem of sdata.datas) {
        convertCmsItemBase(subItem, coverSize, handelEBooK);
        subItem.fileMap = {};
        for (let i = 0; i < itemFieldsData.length; i++) {
          const itemField = itemFieldsData[i];
          try {
            subItem.datas[itemField] = JSON.parse(subItem.datas[itemField]);
          } catch (error) {
            subItem.datas[itemField] = [];
          }
          const itemDatas = subItem.datas[itemField];
          if (itemDatas.length > 0) {
            if (itemDatas[0].Value) {
              subItem[itemField] = itemDatas[0].Value;
              if (itemDatas[0].FileList && itemDatas[0].FileList.length) {
                subItem.fileMap = {
                  ...subItem.fileMap,
                  ...handleLinkFileInfo(itemDatas[0].FileList),
                };
              }
            } else if (itemDatas[0].Data) {
              subItem[itemField] = itemDatas[0].Data.Value;
              if (
                itemDatas[0].Data.FileList &&
                itemDatas[0].Data.FileList.length
              ) {
                subItem.fileMap = {
                  ...subItem.fileMap,
                  ...handleLinkFileInfo(itemDatas[0].Data.FileList),
                };
              }
            } else if (itemDatas[0].CmsItemData) {
              subItem[itemField] = itemDatas[0].CmsItemData.Value;
              if (
                itemDatas[0].CmsItemData.FileList &&
                itemDatas[0].CmsItemData.FileList.length
              ) {
                subItem.fileMap = {
                  ...subItem.fileMap,
                  ...handleLinkFileInfo(itemDatas[0].CmsItemData.FileList),
                };
              }
            }
          }
        }
        if (subItem.productLinkInfo && subItem.productLinkInfo.length) {
          if (subItem.productLinkInfo && subItem.productLinkInfo.length) {
            subItem.productLinkPath =
              subItem.productLinkInfo[0].LinkPath +
              "\\" +
              subItem.productLinkInfo[0].CmsItemId;
          }
          // if (subItem.linkInfo && subItem.linkInfo.length){
          //     subItem.productLinkInfo[0].LinkPath +
          //     "\\" +
          //     subItem.productLinkInfo[0].CmsItemId;
          // }
          if (subItem.linkInfo && subItem.linkInfo.length) {
            subItem.linkPath =
              subItem.linkInfo[0].LinkPath +
              "\\" +
              subItem.linkInfo[0].CmsItemId;
          }
        }
      }
      linkItemsMap[tag] = sdata.datas;
    }
  }
  convertCmsItemBase(item, coverSize, handelEBooK);
  item.idPath = path + "\\" + item.id;
  item.subItems = linkItemsMap;
  return item;
}
const handleLinkFileInfo = (linkList) => {
  let linkFileMap = {};
  for (let z = 0; z < linkList.length; z++) {
    const linkItem = linkList[z];
    linkFileMap[linkItem.Md5] = {
      linkType: linkItem.LinkType,
      extension: linkItem.Extension,
      fileName: linkItem.FileName,
      fileType: linkItem.Type,
      md5: linkItem.Md5,
      icon: linkItem.Icon,
      size: linkItem.Size,
      metaData: JSON.parse(linkItem.MetaData ?? "{}"),
      order: linkItem.Order,
      protectType: linkItem.ProtectType,
    };
  }
  return linkFileMap;
};
const convertCmsItemBase = (item, coverSize, handelEBooK) => {
  item.name = item.datas.Name;
  item.description = item.datas.Description;
  item.refCode = item.datas.RefCode;
  item.state = item.datas.State;
  item.type = item.datas.Type;
  item.tag = item.datas.Tag;
  item.typeId = parseInt(item.datas.TypeId);
  item.icon = getPublicImage(
    item.datas.Icon,
    coverSize?.width,
    coverSize?.height
  );
  item.sysType = item.datas.SysType;
  item.linkFile = JSON.parse(item.datas.LinkFile ?? "[]");
  item.linkType = item.datas.LinkType;
  item.linkAppId = parseInt(item.datas.LinkAppId);
  item.linkStoreId = parseInt(item.datas.LinkStore);
  item.linkRepoId = item.datas.LinkRepository;
  item.childrenCount = parseInt(item.datas.ChildrenCount ?? "0");
  item.childrenFolderCount = parseInt(item.datas.ChildrenFolderCount ?? "0");
  item.childrenChannelCount = parseInt(item.datas.ChildrenChannelCount ?? "0");
  item.linkId = parseInt(item.datas.LinkId);
  item.linkOrg = JSON.parse(item.datas.LinkOrg ?? "[]")[0];
  item.linkDepartment = JSON.parse(item.datas.LinkDepartment ?? "[]")[0];
  item.linkInfo = JSON.parse(item.datas.LinkInfo ?? "[]");
  item.productLinkInfo = JSON.parse(item.datas.ProductLinkInfo ?? "[]");
  item.saleMethod = JSON.parse(item.datas.SaleMethod ?? "[]");
  item.allowDonate = item.datas.AllowDonate == "True";
  // item.donatePriceList = JSON.parse(item.datas.DonatePriceList ?? "[]");
  item.createDate = moment(item.datas.CreateDate).format("YYYY-MM-DD");
  item.beginDate = moment(item.datas.BeginDate).format("YYYY-MM-DD");
  item.endDate = moment(item.datas.EndDate).format("YYYY-MM-DD");
  item.storeEvent = JSON.parse(item.datas.StoreEvent ?? "[]");
  // ç»Ÿä¸€å¤„理价格
  if (item.defaultSaleMethod) {
    if (handelEBooK) {
      // èŽ·å–éšä¹¦èµ„æºçš„é”€å”®æ–¹å¼
      let accompanyingResources = item.cmsDatas[0].datas.find(
        (item) => item.refCode == "tourism_accompanyingResources"
      );
      let saleMethod = "";
      if (accompanyingResources) {
        saleMethod = accompanyingResources.saleMethod;
      }
      if (saleMethod && saleMethod.length > 0) {
        Object.keys(saleMethod[0]).map((key) => {
          let newKey = key.replace(key[0], key[0].toLowerCase());
          saleMethod[0][newKey] = saleMethod[0][key];
          delete saleMethod[0][key];
        });
        item.defaultSaleMethod = saleMethod[0];
        item.defaultSaleMethodId = saleMethod[0].id;
        item.alreadyBuy =
          item.purchasedSaleMethodIdList.indexOf(item.defaultSaleMethodId) > -1;
      }
    }
    if (item.defaultSaleMethod.allowEvent && item.storeEvent.length) {
      // å·²å‚加活动,获取有效活动并计算价格
      let time = new Date().getTime();
      // è¿‡æ»¤è¿‡æœŸæ´»åЍ
      let event = item.storeEvent.filter((item) => {
        let endTime = new Date(item.EndDate).getTime();
        return endTime > time;
      });
      // åªå¤„理一条有效活动
      if (event.length) {
        item.price = (item.defaultSaleMethod.price * event[0].Value).toFixed(2);
        item.oldPrice = item.defaultSaleMethod.price;
        item.storeEventId = event[0].Id;
        if (
          moment().format("YYYY-MM-DD") <
          moment(item.defaultSaleMethod.endDate).format("YYYY-MM-DD")
        ) {
          item.defaultSaleMethodState = "Normal";
        } else {
          item.defaultSaleMethodState = "Beyond";
        }
      }
    } else {
      if (
        moment().format("YYYY-MM-DD") <
        moment(item.defaultSaleMethod.endDate).format("YYYY-MM-DD")
      ) {
        item.defaultSaleMethodState = "Normal";
      } else {
        item.defaultSaleMethodState = "Beyond";
      }
      item.price = item.defaultSaleMethod.price;
      item.oldPrice = item.defaultSaleMethod.virtualPrice;
    }
  }
};
// èŽ·å–ä¸å—ä¿æŠ¤çš„å›¾ç‰‡
export function getPublicImage(md5, width, height) {
  let src = null;
  if (md5) {
    src = requestCtx + `/file/GetPreViewImage?md5=${md5}`;
  } else {
    return require("@/assets/images/bookCity/place_img.png");
  }
  if (width) src += `&width=${width}`;
  if (height) src += `&height=${height}`;
  return src;
}
src/assets/js/toolClass.js
New file
@@ -0,0 +1,397 @@
import SparkMD5 from "spark-md5";
import { getPublicImage } from "@/assets/js/middleGround/tool";
import { requestCtx } from "@/assets/js/config";
import moment from "moment";
var tool = {
  setCookie: function(Cname, value, expiredays, path) {
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + expiredays);
    document.cookie =
      Cname +
      "=" +
      escape(value) +
      (expiredays == null ? "" : ";expires=" + exdate.toGMTString()) +
      (path ? ";path=" + path : "");
  },
  getCookie: function(Cname) {
    if (document.cookie.length > 0) {
      var cStart = document.cookie.indexOf(Cname + "=");
      if (cStart != -1) {
        cStart = cStart + Cname.length + 1;
        var cEnd = document.cookie.indexOf(";", cStart);
        if (cEnd == -1) cEnd = document.cookie.length;
        return unescape(document.cookie.substring(cStart, cEnd));
      }
    }
    return "";
  },
  delCookie: function(name) {
    var exp = new Date();
    exp.setTime(exp.getTime() - 1);
    var cval = tool.getCookie(name);
    if (cval != null) {
      document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
    }
  },
  // å¼ºåˆ¶ä¿ç•™2位小数,如:2,会在2后面补上00.即2.00
  toDecimal2(x) {
    var f;
    f = parseFloat(x);
    if (isNaN(f)) {
      return false;
    }
    f = Math.round(x * 100) / 100;
    var s = f.toString();
    var rs = s.indexOf(".");
    if (rs < 0) {
      rs = s.length;
      s += ".";
    }
    while (s.length <= rs + 2) {
      s += "0";
    }
    return s;
  },
  formateTime(date) {
    var newDate = new Date(+new Date(date) + 8 * 3600 * 1000)
      .toISOString()
      .replace(/T/g, " ")
      .replace(/\.[\d]{3}Z/, "");
    var time = new Date(newDate);
    return time.getTime();
  }
};
// å¤„理订单记录
export function setOrderList(res) {
   let currentTimestamp = moment().startOf("year");
  let arr = [];
  for (let i = 0; i < res.length; i++) {
    const item = res[i];
    // åˆ¤æ–­æ˜¯å¦è¶…出申请发票的日期
    item.exceedingTheSpecifiedTime = moment(item.createDate).isBefore(
      currentTimestamp
    );
    if (item.saleMethodLinks.length > 0) {
      let itemName = null;
      let itemIcon = null;
      // let cmsItemList = item.saleMethodLinks[0].orderSaleMethod.cmsItemList[0];
      // if (cmsItemList.icon) {
      //   itemName = cmsItemList.name;
      //   itemIcon = cmsItemList.icon;
      // } else {
        itemName = item.saleMethodLinks[0].orderSaleMethod.product.name;
        itemIcon = item.saleMethodLinks[0].orderSaleMethod.product.icon;
      // }
      item.saleMethodLinks[0].title = itemName;
      item.saleMethodLinks[0].icon = getPublicImage(itemIcon);
    } else {
      const itemIcon = require("@/assets/images/bookCity/place_img.png");
      const saleMethodLink = [];
      const obj = {
        icon: itemIcon,
        orderSaleMethod: {
          price: item.payPrice,
        },
      };
      saleMethodLink.push(obj);
      item.saleMethodLinks = saleMethodLink;
    }
    if (item.state == "Success") {
      item.CustomState = "支付成功";
    }
    if (item.state == "Cancel") {
      item.CustomState = "取消支付";
    }
    if (item.state == "WaitPay") {
      item.CustomState = "等待支付";
    }
    if (item.state == "WaitDeliver") {
      item.CustomState = "正在支付";
    }
    arr.push(item);
  }
  return arr;
}
//处理表单提交数据
export function worksData(res) {
  let arr = [];
  for (let i = 0; i < res.length; i++) {
    const item = res[i];
    if (item.typeField) {
      if (item.typeField.config) {
        item.typeField.options = JSON.parse(item.typeField.config).option;
      }
      arr.push(item.typeField);
    }
  }
  return arr;
}
export function worksDataBytool(res, value, linkList) {
  let arr = [];
  let nrr = [];
  if (linkList && linkList.length > 0) {
    linkList.forEach(e =>
      nrr.push({
        linkProtectType: e.linkProtectType,
        linkType: e.linkType,
        md5: e.md5
      })
    );
  }
  res.forEach(item => {
    const obj = {
      baseType: item.typeField.baseType,
      order: 0,
      typeFieldId: item.typeField.id,
      sequenceNum: item.config ? JSON.parse(item.config).uuid : "",
      newDataAndFileLinkListRequest: []
    };
    for (let k in value) {
      if (item.typeField.refCode === k) {
        if (typeof value[k] == "object") {
          if (obj.refCode == "region") {
            obj.strValue = value[k]?.join("/");
          } else {
            obj.strValue = JSON.stringify(value[k]);
          }
        } else {
          if (obj.baseType === "String") {
            obj.strValue = value[k] + "";
          } else if (obj.baseType === "Text") {
            obj.textValue = value[k] + "";
          } else {
            obj.strValue = value[k] + "";
          }
        }
        if (item.typeField.type == "File") {
          obj.newDataAndFileLinkListRequest = nrr;
        }
      }
    }
    if (obj.strValue) {
      arr.push(obj);
    }
  });
  return arr;
}
export function resultsBytool(res, value) {
  let obj = {};
  res.forEach(item => {
    item.sequenceNum = item.config ? JSON.parse(item.config).uuid : "";
    value.forEach(e => {
      if (item.sequenceNum == e.sequenceNum) {
        try {
          if (
            e.fileLinkList.length > 0 &&
            obj[item.typeField.refCode] != "region"
          ) {
            for (let i = 0; i < e.fileLinkList.length; i++) {
              const ele = e.fileLinkList[i];
              ele.url = getPublicImage(ele.file.md5);
              ele.name = ele.file.fileName;
              ele.md5 = ele.file.md5;
              ele.isImage = true;
            }
            obj[item.typeField.refCode] = e.fileLinkList;
          } else {
            const val = JSON.parse(e.value);
            if (val) {
              obj[item.typeField.refCode] = val;
            } else {
              obj[item.typeField.refCode] = "-";
            }
          }
        } catch (error) {
          obj[item.typeField.refCode] = e.value ? e.value : "-";
        }
      }
    });
  });
  return obj;
}
export function UpdateworksDataBytool(initData, res, value, linkList) {
  let arr = [];
  let newArr = [];
  for (let i = 0; i < initData.length; i++) {
    const ele = initData[i];
    for (let j = 0; j < res.length; j++) {
      const item = res[j];
      if (item.sequenceNum == ele.sequenceNum) {
        item.refCode = ele.typeField.refCode;
      }
    }
  }
  initData.forEach(citem => {
    const updateOldData = res.find(f => f.sequenceNum == citem.sequenceNum);
    if (updateOldData) {
      const obj = {
        baseType: citem.typeField.baseType,
        order: 0,
        id: updateOldData.id,
        typeFieldId: citem.typeField.id,
        sequenceNum: citem.sequenceNum,
        setDataAndFileLinkListRequest: []
      };
      for (let k in value) {
        if (citem.typeField.refCode === k) {
          if (typeof value[k] == "object" && k != "region") {
            obj.strValue = JSON.stringify(linkList);
            obj.setDataAndFileLinkListRequest = linkList;
          } else if (typeof value[k] == "object" && k == "region") {
            obj.strValue = value[k]?.join("/");
            obj.setDataAndFileLinkListRequest = [{ area: value[k] }];
          } else {
            obj.strValue = value[k].toString();
          }
        }
      }
      // if (obj.strValue) {
      arr.push(obj);
      // }
    } else {
      const newObj = {
        baseType: citem.typeField.baseType,
        order: 0,
        typeFieldId: citem.typeField.id,
        sequenceNum: citem.sequenceNum,
        setDataAndFileLinkListRequest: []
      };
      for (let k in value) {
        if (citem.typeField.refCode === k) {
          if (typeof value[k] == "object") {
            newObj.strValue = JSON.stringify(linkList);
            newObj.setDataAndFileLinkListRequest = linkList;
          } else {
            newObj.strValue = value[k].toString();
          }
        }
      }
      if (newObj.strValue) {
        newArr.push(newObj);
      }
    }
  });
  return {
    updateData: arr,
    newData: newArr
  };
}
/**
 * èŽ·å–ä¸€ä¸ªUUID
 * @param len
 * @param radix
 * @returns {string}
 */
export function uuid(len = 32, radix = 16) {
  const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(
    ""
  );
  let uuid = [];
  let i = null;
  radix = radix || chars.length;
  if (len) {
    // Compact form
    for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
  } else {
    // rfc4122, version 4 form
    let r;
    // rfc4122 requires these characters
    uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
    uuid[14] = "4";
    // Fill in random data.  At i==19 set the high bits of clock sequence as
    // per rfc4122, sec. 4.1.5
    for (i = 0; i < 36; i++) {
      if (!uuid[i]) {
        r = 0 | (Math.random() * 16);
        uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r];
      }
    }
  }
  return uuid.join("");
}
export function getFileMd5(file, chunkSize) {
  return new Promise((resolve, reject) => {
    let blobSlice =
      File.prototype.slice ||
      File.prototype.mozSlice ||
      File.prototype.webkitSlice;
    let chunks = Math.ceil(file.size / chunkSize);
    let currentChunk = 0;
    let spark = new SparkMD5.ArrayBuffer();
    let fileReader = new FileReader();
    fileReader.onload = function(e) {
      spark.append(e.target.result);
      currentChunk++;
      if (currentChunk < chunks) {
        loadNext();
      } else {
        const md5 = spark.end();
        resolve(md5);
      }
    };
    fileReader.onerror = function(e) {
      reject(e);
    };
    function loadNext() {
      let start = currentChunk * chunkSize;
      let end = start + chunkSize;
      if (end > file.size) {
        end = file.size;
      }
      fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
    }
    loadNext();
  });
}
// å¤„理时间,用于显示音视频当前时间
export function realFormatSecond(time) {
  let duration = parseInt(time);
  let minute = parseInt(duration / 60);
  let sec = (duration % 60) + "";
  let isM0 = ":";
  if (minute == 0) {
    minute = "00";
  } else if (minute < 10) {
    minute = "0" + minute;
  }
  if (sec.length == 1) {
    sec = "0" + sec;
  }
  return minute + isM0 + sec;
}
export function getPublicFile(md5) {
  let src = null;
  if (md5) {
    src = requestCtx + `/file/api/ApiDownload?md5=${md5}`;
  } else {
    return "";
  }
  return src;
}
export default {
  ...tool,
  uuid,
  getFileMd5,
  worksDataBytool,
  resultsBytool,
  UpdateworksDataBytool,
  getPublicImage,
  worksData,
  getPublicFile
};
src/assets/js/vConsole.js
New file
@@ -0,0 +1,3 @@
import Vconsole from "vconsole";
const vConsole = new Vconsole();
export default vConsole;
src/assets/js/weChat/share.js
New file
@@ -0,0 +1,124 @@
// æ³¨ï¼š
// 1、微信不支持分享回调,分享统计使用分享链接传参,被分享用户点击后进行分享统计;
// 2、入口为链接的用户,分享只能分享链接
import wx from 'weixin-js-sdk';
import MG from '../middleGround/WebMiddleGroundApi';
import config from '@/assets/js/config';
export function shareJs(jssdk, options) {
  wx.config({
    debug: false, //是否开启调试功能,这里关闭!
    appId: jssdk.appId, //appid
    timestamp: parseInt(jssdk.timestamp), //时间戳
    nonceStr: jssdk.nonceStr, //生成签名的随机字符串
    signature: jssdk.signature, //签名
    jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'],
  });
  var defaults = {
    title: '分享的标题',
    desc: '分享的描述',
    link: '', //分享页面地址,不能为空,这里可以传递参数!!!!!!!
    imgUrl: 'https://tup.iheima.com/sport.png', //分享是封面图片,不能为空
    success: function () { }, //分享成功触发
    cancel: function () { }, //分享取消触发,需要时可以调用
  };
  // åˆå¹¶å¯¹è±¡ï¼ŒåŽé¢çš„æ›¿ä»£å‰é¢çš„!
  options = Object.assign({}, defaults, options);
  wx.ready(function () {
    var thatopts = options;
    // åˆ†äº«åˆ°æœ‹å‹åœˆ
    wx.updateTimelineShareData({
      title: thatopts.title, // åˆ†äº«æ ‡é¢˜
      desc: thatopts.desc, // åˆ†äº«æè¿°
      link: thatopts.link, // åˆ†äº«é“¾æŽ¥
      imgUrl: thatopts.imgUrl, // åˆ†äº«å›¾æ ‡
      success: function () {
        console.log('成功');
        // shareNewJobWithApiNewEvent();
      },
      cancel: function () {
        console.log('分享失败');
      },
    });
    // åˆ†äº«ç»™æœ‹å‹
    wx.updateAppMessageShareData({
      title: thatopts.title, // åˆ†äº«æ ‡é¢˜
      desc: thatopts.desc, // åˆ†äº«æè¿°
      link: thatopts.link, // åˆ†äº«é“¾æŽ¥
      imgUrl: thatopts.imgUrl, // åˆ†äº«å›¾æ ‡
      success: function () {
        console.log('成功');
        // shareNewJobWithApiNewEvent();
      },
      cancel: function () {
        console.log('分享失败');
      },
    });
  });
  wx.error((err) => {
    console.log(err);
  });
}
export function getJsdkAndProduct(val, shareUrl) {
  const shareObj = {};
  shareObj.title = val.name;
  shareObj.desc = val.description;
  shareObj.imgUrl = val.icon;
  shareObj.link = shareUrl;
  const data = {
    appRefCode: config.appRefCode,
    url: window.location.href.split('#')[0],
  };
  MG.app.getWeChatApiSign(data).then((res) => {
    shareJs(res, shareObj);
  });
}
export function shareNewJobWithApiNewEvent(appRefCode, id) {
  const data = {
    appRefCode: appRefCode,
    type: 'View',
    sysType: 'App',
    data: '',
    event: 'shareCount',
    productId: id,
  };
  MG.job.newJobWithApiNewEvent(data).then((res) => {
    if (res) {
      window.location.href = urlDelP(location.href, 'key');
    }
  });
}
function urlDelP(url, name) {
  var urlArr = url.split('?');
  if (urlArr.length > 1 && urlArr[1].indexOf(name) > -1) {
    var query = urlArr[1];
    var obj = {};
    var arr = query.split('&');
    for (var i = 0; i < arr.length; i++) {
      arr[i] = arr[i].split('=');
      obj[arr[i][0]] = arr[i][1];
    }
    delete obj[name];
    // eslint-disable-next-line
    var urlte = urlArr[0] + '?' + JSON.stringify(obj).replace(/[\"\{\}]/g, '').replace(/\:/g, '=').replace(/\,/g, '&'); return urlte;
  } else {
    return url;
  }
}
export function getSharekey(url) {
  let obj = {};
  let urlStr = url.split('?')[1];
  if (url && urlStr) {
    let paramsArr = urlStr.split('&');
    for (let i = 0, len = paramsArr.length; i < len; i++) {
      let arr = paramsArr[i].split('=');
      obj[arr[0]] = arr[1];
    }
  }
  return obj;
}
src/assets/js/weChat/weChat.js
New file
@@ -0,0 +1,129 @@
import {
  appRefCode
} from "@/assets/js/config";
import service from "@/plugin/axios";
let wxAppId = "wxd2941f9abd7ba978"; // å¾®ä¿¡AppId
let WeChat = {
  getCode: (state) => {
    // è°ƒèµ·æŽˆæƒï¼ŒèŽ·å–code。注意:每次调用时先清除已储存的token,避免后续跳转监听到旧的token,一直401
    const url = window.location.href;
    const routeCode = url.split("#")[1];
    if (routeCode == '/weChatLogin' || routeCode == '/selectLoginMethod') {
      window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${wxAppId}&redirect_uri=${encodeURIComponent(
        'https://www.tepcb.com/mobile/teachingService/#/mine'
      )}&response_type=code&scope=snsapi_userinfo&state=${state}`;
    } else {
      window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${wxAppId}&redirect_uri=${encodeURIComponent(
        window.location.href
      )}&response_type=code&scope=snsapi_userinfo&state=${state}`;
    }
  },
  getUrlInfo: state => {
    var url = window.location.search;
    var theRequest = {};
    if (url.indexOf(state) != -1) {
      var infoStr = url.substr(1);
      var infoList = infoStr.split("&");
      if (infoList.length) {
        for (var i = 0; i < infoList.length; i++) {
          let infoItem = infoList[i];
          theRequest[infoItem.split("=")[0]] = infoItem.split("=")[1];
        }
      } else {
        theRequest = null;
      }
    }
    return theRequest;
  },
  login: (code, callback) => {
    // å…¬ä¼—号登录
    service({
      url: "/identity/api/LoginByWeChatSocialCode",
      method: "post",
      data: {
        code,
        appRefCode,
        platform: appRefCode + "_WeChatLogin"
      }
    }).then(res => {
      if (res.status == "Ok") {
        if (callback) callback(res.token);
      } else {
        // eslint-disable-next-line no-unused-vars
        let msg = "";
        try {
          msg = "登录失败!" + "errmsg:" + JSON.parse(res.message).errmsg;
        } catch (error) {
          msg = "登录失败!";
        }
      }
    });
  },
  // å¾®ä¿¡æ”¯ä»˜
  pay: (res, callback) => {
    if (res) {
      if (typeof WeixinJSBridge === "undefined") {
        // å¾®ä¿¡æµè§ˆå™¨å†…置对象。参考微信官方文档
        if (document.addEventListener) {
          document.addEventListener(
            "WeixinJSBridgeReady",
            WeChat.onBridgeReady(res, callback),
            false
          );
        } else if (document.attachEvent) {
          document.attachEvent(
            "WeixinJSBridgeReady",
            WeChat.onBridgeReady(res, callback)
          );
          document.attachEvent(
            "onWeixinJSBridgeReady",
            WeChat.onBridgeReady(res, callback)
          );
        }
      } else {
        // å‡†å¤‡è°ƒç”¨å¾®ä¿¡æ”¯ä»˜
        WeChat.onBridgeReady(res, callback);
      }
    }
  },
  onBridgeReady: (data, callback) => {
    const resData = JSON.parse(data);
    // eslint-disable-next-line
    WeixinJSBridge.invoke(
      "getBrandWCPayRequest", {
        appId: resData.appId, // å…¬ä¼—号名称,由商户传入
        timeStamp: resData.timeStamp, // æ—¶é—´æˆ³
        nonceStr: resData.nonceStr, // éšæœºä¸²
        package: resData.package, // é¢„支付id
        signType: resData.signType, // å¾®ä¿¡ç­¾åæ–¹å¼
        paySign: resData.paySign // å¾®ä¿¡ç­¾å
      },
      function (res) {
        // ä½¿ç”¨ä»¥ä¸Šæ–¹å¼åˆ¤æ–­å‰ç«¯è¿”回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
        if (res.err_msg == "get_brand_wcpay_request:ok") {
          // æ”¯ä»˜æˆåŠŸ
          if (callback) {
            const flag = true;
            callback(flag);
          }
        } else if (res.err_msg == "get_brand_wcpay_request:cancel") {
          // æ”¯ä»˜å–消
          if (callback) {
            const flag = false;
            callback(flag, "支付取消");
          }
        } else {
          // æ”¯ä»˜å¤±è´¥
          if (callback) {
            const flag = false;
            callback(flag, res.errMsg || res.err_msg);
          }
        }
      }
    );
  }
};
export default WeChat;
src/assets/js/webApi.js
New file
@@ -0,0 +1,23 @@
import config from "@/assets/js/config";
var webApi = {
  getdownloadUrl(md5) {
    return (
      config.requestCtx +
      "/api/admin/fileDownload/public?md5=" +
      md5.split(".")[0] +
      "&type=" +
      md5.split(".")[1]
    );
  },
  getdownloadProtectedUrl(md5) {
    return (
      config.requestCtx +
      "/api/admin/fileDownload/protected?md5=" +
      md5.split(".")[0] +
      "&type=" +
      md5.split(".")[1]
    );
  },
};
export default webApi;
src/assets/logo.png
src/components/footer/footer.vue
New file
@@ -0,0 +1,92 @@
<!--
 * @Author: your name
 * @Date: 2020-03-30 09:49:50
 * @LastEditTime: 2020-07-07 12:13:29
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \2020330\src\components\footer\footer.vue
-->
<template>
  <van-tabbar v-model="active" class="active_tab">
    <van-tabbar-item
      v-for="(item, index) in tabbars"
      :key="index"
      @click="tab(index, item.name)"
    >
      <span :class="currIndex == index ? active : ''">{{ item.title }}</span>
      <template slot="icon" slot-scope="props">
        <img :src="props.active ? item.active : item.normal" />
      </template>
    </van-tabbar-item>
  </van-tabbar>
</template>
<script>
export default {
  name: "tabbar",
  data() {
    return {
      currIndex: 0,
      active: 0,
      tabbars: [
        {
          name: "/",
          title: "首页",
          normal: require("@/assets/images/nav/home.png"),
          active: require("@/assets/images/nav/home-fill.png")
        },
        {
          name: "myTextBook",
          title: "书架",
          normal: require("@/assets/images/nav/shujia.png"),
          active: require("@/assets/images/nav/shujia-fill.png")
        },
        {
          index: 3,
          name: "personalCenter",
          title: "我的",
          normal: require("@/assets/images/nav/my.png"),
          active: require("@/assets/images/nav/my-fill.png")
        }
      ]
    };
  },
  methods: {
    tab(index, val) {
      this.currIndex = index;
      this.$router.push(val);
    }
  },
  created() {
    // é€šè¿‡è·¯ç”±è·³è½¬åˆ¤æ–­é€‰ä¸­çš„æ ·å¼
    if (this.$route.name == "/") {
      this.active = 0;
    } else if (this.$route.name == "myTextBook") {
      this.active = 1;
    } else if (this.$route.name == "personalCenter") {
      this.active = 2;
    }
  }
};
</script>
<style scoped>
.active_tab {
  z-index: 10;
  background: #fff;
}
.active_tab img {
  width: 20px;
  height: 20px;
}
.van-tabbar-item--active {
  color: rgb(53, 110, 255);
}
.van-tabbar--fixed {
  border-top: 1px solid rgba(215, 215, 215, 0.56);
  z-index: 3000;
}
</style>
src/components/header/backHeader.vue
New file
@@ -0,0 +1,46 @@
<template>
  <div class="backheader-page">
    <van-nav-bar
      :title="headTitle"
      :fixed="isFixed"
      :border="isBorder"
      :left-arrow="arrow"
      @click-left="goBack"
    />
  </div>
</template>
<script>
export default {
  props: {
    /**
     * headTitle传入头部的标题,默认是旅游教育出版社
     * isFixed æ˜¯å¦å›ºå®šåœ¨é¡¶éƒ¨
     * isBorder æ˜¯å¦æ˜¾ç¤ºä¸‹è¾¹æ¡†
     */
    headTitle: {
      type: String,
      default: "教学服务"
    },
    isFixed: {
      type: Boolean,
      default: true
    },
    isBorder: {
      type: Boolean,
      default: true
    },
    arrow: {
      type: Boolean,
      default: true
    }
  },
  methods: {
    goBack() {
      var that = this;
      // $router.go(num)这个方法的参数是一个整数,意思是在 history è®°å½•中向前或者后退多少步
      that.$router.go(-1);
    }
  }
};
</script>
<style lang="stylus"></style>
src/components/mainLayout.vue
New file
@@ -0,0 +1,31 @@
<template>
  <div>
    <router-view />
  </div>
</template>
<script>
export default {
  name: "mainLayout",
  data() {
    return {};
  },
  created() {},
  methods: {}
};
</script>
<style>
.testPop {
  width: 50px;
  height: 50px;
  position: fixed;
  bottom: 20%;
  right: 20px;
  background-color: #1989fa;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>
src/main.js
New file
@@ -0,0 +1,157 @@
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import service from "@/plugin/axios";
import webApi from "./assets/js/webApi";
import "./registerServiceWorker";
import Vant from "vant";
import "vant/lib/index.css";
// åŸºç¡€css
import "@/assets/css/base.css";
// å…¬å…±css
import "@/assets/css/common.css";
// é…ç½®é¡¹
import config from "@/assets/js/config";
// å·¥å…·ç±»
import toolClass from "@/assets/js/toolClass";
import wx from "weixin-js-sdk";
import MG from "@/assets/js/middleGround/WebMiddleGroundApi";
// æ—¶é—´å¤„理
import moment from "moment";
import WeChat from "@/assets/js/weChat/weChat";
// import vConsole from "@/assets/js/vConsole"
Vue.prototype.webApi = webApi;
Vue.config.productionTip = false;
Vue.prototype.config = config;
Vue.prototype.tool = toolClass;
Vue.prototype.request = service;
Vue.use(Vant);
// Vue.use(vConsole);
// å¾®ä¿¡
Vue.use(wx);
Vue.prototype.MG = MG;
Vue.prototype.moment = moment;
Vue.prototype.WeChat = WeChat;
// è·¯ç”±æ‹¦æˆªå¤„理
router.beforeEach((to, from, next) => {
  console.log(to.meta, "tometa");
  console.log(to.meta.requiresAuth, "requiresAuth");
  if (to.meta && to.meta && to.meta.requiresAuth) {
    // const shareVal = getSharekey(location.href);
    // if (shareVal && shareVal.key) {
    //   shareNewJobWithApiNewEvent(config.appRefCode, shareVal.productId);
    // }
    // åˆ¤æ–­å½“前的token是否存在
    if (toolClass.getCookie(config.tokenKey)) {
      // æ‹¦æˆªå¦‚果是绑定code,静默绑定微信
      let bindingCode = WeChat.getUrlInfo("bindingWeChat");
      if (bindingCode.code) {
        let query = {
          code: bindingCode.code,
          isSocial: true // å…¬ä¼—号和开放平台是不同的主体,公众号绑定微信使用isSocial=true,开放平台(扫码绑定)使用isSocial=false;
        };
        MG.identity
          .bindWeChat(query)
          .then(res => {
            const url = window.location.href;
            const redirectUrl = url.split("?")[0];
            window.location.href = redirectUrl;
          })
          .catch(res => {
            console.log(res);
          });
      } else {
        next();
      }
    } else {
      // æ‹¦æˆªå¦‚果是登录code
      let loginCode = WeChat.getUrlInfo("weChatLogin");
      console.log(loginCode, "loginCode");
      if (loginCode.code) {
        WeChat.login(loginCode.code, data => {
          toolClass.setCookie(config.tokenKey, data);
          const url = window.location.href;
          const domian = url.split("?")[0];
          console.log(domian, 'domian');
          const routeCode = url.split("#")[1];
          const redirectUrl = domian + "#" + routeCode;
          window.location.href = redirectUrl;
        });
        return false;
      }
      // æ£€æŸ¥æ˜¯å¦æ‹¥æœ‰å¾®ä¿¡è´¦æˆ·
      let checkCode = WeChat.getUrlInfo("checkWeChat");
      if (checkCode.code) {
        /**
         * identity/checkWeChatAccount
         * é€šè¿‡code åˆ¤æ–­æ˜¯å¦æ˜¯æ–°ç”¨æˆ·
         *   æ–°ç”¨æˆ·
         *      é€‰æ‹©ç™»å½•方式
         *        å¾®ä¿¡ç™»å½•
         *          ä¸ªäººä¸­å¿ƒ æç¤ºç»‘定手机号
         *        æ‰‹æœºå·ç™»å½•
         *          ç™»å½•成功后 è°ƒç”¨wechat.login é™é»˜ç»‘定用户
         *   å·²æœ‰è´¦æˆ·
         *     å¾®ä¿¡ç™»å½•即可, æ­¤æ—¶ä¸¤ä¸ªè´¦æˆ·å·²ç»æ‰“通
         *
         */
        let query = {
          code: checkCode.code,
          appCode: config.appRefCode
        };
        MG.identity
          .chechWechatAccount(query)
          .then(res => {
            if (res == true) {
              const url = window.location.href;
              const domian = url.split("?")[0];
              const redirectUrl = domian + "#" + "/weChatLogin";
              window.location.href = redirectUrl;
            } else {
              if (to.meta.title != "选择登录方式") {
                const url = window.location.href;
                const domian = url.split("?")[0];
                const redirectUrl = domian + "#" + "/index";
                window.location.href = redirectUrl;
              } else {
                const url = window.location.href;
                const domian = url.split("?")[0];
                const redirectUrl = domian + "#" + "/selectLoginMethod";
                window.location.href = redirectUrl;
              }
            }
          })
          .catch(res => {
            console.log(res);
          });
      } else {
        //判断是否是移动端
        // if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
        //如在是微信中打开
        var ua = navigator.userAgent.toLowerCase(); //获取判断用的对象
        if (ua.match(/MicroMessenger/i) == "micromessenger") {
          toolClass.delCookie(config.tokenKey);
          localStorage.fullPath = to.fullPath;
          window.location.href = config.requestCtx + "/mobile/textbooks/#" + to.fullPath;
          WeChat.getCode("checkWeChat");
        } else {
          WeChat.getCode("weChatLogin");
        }
      }
    }
  } else {
    console.log("不拦截");
    next();
  }
});
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");
src/plugin/axios/index.js
New file
@@ -0,0 +1,86 @@
/* eslint-disable no-constant-condition */
import axios from "axios";
// import router from "@/router";
import myConfig from "@/assets/js/config";
import toolClass from "@/assets/js/toolClass";
import WeChat from "@/assets/js/weChat/weChat";
// import store from "../../store";
// åˆ›å»º axios å®žä¾‹
const service = axios.create({
  baseURL: myConfig.requestCtx,
  timeout: myConfig.requestTimeOut // è¯·æ±‚è¶…æ—¶æ—¶é—´
});
// è¯·æ±‚拦截器
service.interceptors.request.use(
  config => {
    // åœ¨è¯·æ±‚发送之前做一些处理
    // if (config.url !== "/api/account/loginByPassword") {
    let token = toolClass.getCookie(myConfig.tokenKey);
    if (token) config.headers.Authorization = `bearer ${token}`;
    // if (!/^https:\/\/|http:\/\//.test(config.url)) {
    //   const token = toolClass.getCookie("token-"+myConfig.appId);
    //   if (token && token !== "undefined") {
    //     // è®©æ¯ä¸ªè¯·æ±‚携带token-- ['X-Token']为自定义key è¯·æ ¹æ®å®žé™…情况自行修改
    //     config.headers.Authorization = token;
    //   }
    //   // else {
    //   //   router.push({ name: "login" });
    //   // }
    // }
    // }
    return config;
  },
  error => {
    // å‘送失败
    Promise.reject(error);
  }
);
// å“åº”拦截器
service.interceptors.response.use(
  response => {
    // dataAxios æ˜¯ axios è¿”回数据中的 data
    const dataAxios = response.data;
    // è¿™ä¸ªçŠ¶æ€ç æ˜¯å’ŒåŽç«¯çº¦å®šçš„
    const {
      success
    } = dataAxios;
    if (dataAxios.currentDate) {
      sessionStorage.currentDate = new Date(dataAxios.currentDate).getTime();
    }
    // æ ¹æ® code è¿›è¡Œåˆ¤æ–­
    if (success) {
      return dataAxios.data;
    } else {
      // æç¤ºé”™è¯¯
      console.error(dataAxios.msg);
    }
  },
  error => {
    if ((error.message = "Network Error")) {
      let loginCode = WeChat.getUrlInfo("weChatLogin");
      if (loginCode.code) {
        WeChat.login(loginCode.code, data => {
          toolClass.setCookie(myConfig.tokenKey, data);
          location.reload();
        });
      } else {
        // toolClass.delCookie(myConfig.tokenKey);
        // WeChat.getCode("weChatLogin");
      }
    } else {
      if (error.response && error.response.data && error.response.data.error) {
        console.error(error.response.data.error.msg);
      } else {
        console.error("请求发生错误");
      }
    }
    return Promise.reject(error);
  }
);
export default service;
src/registerServiceWorker.js
New file
@@ -0,0 +1,34 @@
/* eslint-disable no-console */
import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready() {
      console.log(
        "App is being served from cache by a service worker.\n" +
        "For more details, visit https://goo.gl/AFskqB"
      );
    },
    registered() {
      console.log("Service worker has been registered.");
    },
    cached() {
      console.log("Content has been cached for offline use.");
    },
    updatefound() {
      console.log("New content is downloading.");
    },
    updated() {
      console.log("New content is available; please refresh.");
    },
    offline() {
      console.log(
        "No internet connection found. App is running in offline mode."
      );
    },
    error(error) {
      console.error("Error during service worker registration:", error);
    }
  });
}
src/router/index.js
New file
@@ -0,0 +1,43 @@
// æ•´åˆè·¯ç”±é…ç½®ã€è·¯ç”±æ‹¦æˆªï¼›ï¼ˆå¯æ‰©å±•页面进度条)
import Vue from "vue";
import VueRouter from "vue-router";
// import myConfig from "@/assets/js/config";
// è·¯ç”±æ•°æ®
import routes from "./routes";
Vue.use(VueRouter);
const router = new VueRouter({
  routes
});
/**
 * è·¯ç”±æ‹¦æˆª
 * æƒé™éªŒè¯
 */
// router.beforeEach((to, from, next) => {
//   // éªŒè¯è·¯ç”±æ˜¯å¦éœ€è¦æœ‰ç™»å½•验证
//   if (to.matched.some(r => r.meta.requiresAuth)) {
//     // è¿™é‡Œæš‚æ—¶å°†localStorage里是否存有token作为验证是否登录的条件
//     // è¯·æ ¹æ®è‡ªèº«ä¸šåŠ¡éœ€è¦ä¿®æ”¹
//     const token = localStorage.getItem(myConfig.tokenName);
//     if (token && token !== "undefined") {
//       next();
//     } else {
//       // éªŒè¯ä¸é€šè¿‡è·³è½¬åˆ°ç™»å½•界面,将当前预计打开的页面完整地址传递至登录页面,登录后继续跳转
//       next({
//         name: "login",
//         query: {
//           redirect: to.fullPath
//         }
//       });
//     }
//   } else {
//     // ä¸éœ€è¦èº«ä»½æ ¡éªŒ ç›´æŽ¥é€šè¿‡
//     next();
//   }
// });
export default router;
src/router/routes.js
New file
@@ -0,0 +1,207 @@
import mainLayout from "@/components/mainLayout";
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
// ä¸»æ¡†æž¶
const mainData = [
  {
    path: "/",
    redirect: {
      name: "index"
    },
    component: mainLayout, // ä¸»æ¡†æž¶è§†å›¾
    children: [
      {
        path: "manual",
        name: "manual",
        meta: {
          requiresAuth: false,
          title: "操作手册"
        },
        component: () => import("@/views/index/manual")
      },
      {
        path: "index",
        name: "index",
        meta: {
          requiresAuth: false,
          title: "首页"
        },
        component: () => import("@/views/index/index")
      },
      {
        path: "testLogin",
        name: "testLogin",
        meta: {
          requiresAuth: false,
          title: "测试登录"
        },
        component: () => import("@/views/testLogin")
      },
      {
        path: "/login",
        name: "login",
        meta: {
          title: "登录"
        },
        component: () => import("@/views/entrance/login")
      },
      {
        path: "/selectLoginMethod",
        name: "selectLoginMethod",
        meta: {
          title: "选择登录方式"
        },
        component: () => import("@/views/entrance/entrance")
      },
      {
        path: "/bindWeChatAuto",
        name: "bindWeChatAuto",
        meta: {
          title: "绑定微信"
        },
        component: () => import("@/views/entrance/bindWeChatAuto")
      },
      {
        path: "/weChatLogin",
        name: "weChatLogin",
        meta: {
          title: "微信登录"
        },
        component: () => import("@/views/entrance/weChatLogin")
      },
      {
        path: "bookList",
        name: "bookList",
        meta: {
          requiresAuth: false,
          title: "列表"
        },
        component: () => import("@/views/bookList/bookList")
      },
      {
        path: "bookDetail",
        name: "bookDetail",
        meta: {
          requiresAuth: false,
          title: "书籍详情"
        },
        component: () => import("@/views/bookList/bookDetail")
      },
      {
        path: "bookApply",
        name: "bookApply",
        meta: {
          requiresAuth: false,
          title: "申请试用"
        },
        component: () => import("@/views/bookList/bookApply")
      },
      {
        path: "pay",
        name: "pay",
        meta: {
          requiresAuth: false,
          title: "确认订单"
        },
        component: () => import("@/views/pay/pay")
      },
      {
        path: "personalCenter",
        name: "personalCenter",
        meta: {
          requiresAuth: true,
          title: "我的"
        },
        component: () => import("@/views/personalCenter/index")
      },
      {
        path: "teacherCertificate",
        name: "teacherCertificate",
        meta: {
          requiresAuth: true,
          title: "教师认证"
        },
        component: () => import("@/views/personalCenter/teacherCertificate")
      },
      {
        path: "collection",
        name: "collection",
        meta: {
          requiresAuth: true,
          title: "我的收藏"
        },
        component: () => import("@/views/personalCenter/collection")
      },
      {
        path: "myShoppingCart",
        name: "myShoppingCart",
        meta: {
          requiresAuth: true,
          title: "购物车"
        },
        component: () => import("@/views/personalCenter/myShoppingCart")
      },
      {
        path: "myOrder",
        name: "myOrder",
        meta: {
          requiresAuth: true,
          title: "我的订单"
        },
        component: () => import("@/views/personalCenter/myOrder")
      },
      {
        path: "myApplyBook",
        name: "myApplyBook",
        meta: {
          requiresAuth: true,
          title: "我的申请"
        },
        component: () => import("@/views/personalCenter/myApplyBook")
      },
      {
        path: "myTextBook",
        name: "myTextBook",
        meta: {
          requiresAuth: true,
          title: "我的教材"
        },
        component: () => import("@/views/personalCenter/myTextBook")
      },
      {
        path: "aboutUs",
        name: "aboutUs",
        meta: {
          requiresAuth: false,
          title: "关于我们"
        },
        component: () => import("@/views/personalCenter/about")
      },
      {
        path: "protocol",
        name: "protocol",
        meta: {
          requiresAuth: false,
          title: "服务协议"
        },
        component: () => import("@/views/personalCenter/protocol")
      },
      {
        path: "personInfor",
        name: "personInfor",
        meta: {
          requiresAuth: false,
          title: "个人信息"
        },
        component: () => import("@/views/personalCenter/personInfor")
      }
    ]
  }
];
// å¯¼å‡º
export default [...mainData];
src/store/index.js
New file
@@ -0,0 +1,117 @@
import Vue from "vue";
import Vuex from "vuex";
import tool from "@/assets/js/toolClass";
import {
  tokenKey,
  userInfoKey
} from "@/assets/js/config";
Vue.use(Vuex);
// localStorage.setItem("electronicBookList", [])
// localStorage.setItem("paperBookList", [])
export default new Vuex.Store({
  state: {
    token: tool.getCookie(tokenKey),
    userInfo: localStorage.getItem(userInfoKey) ?
      JSON.parse(localStorage.getItem(userInfoKey)) : {},
    // electronicBookList: [],
    // paperBookList: []
    //已选电子书列表
    electronicBookList: localStorage.getItem("electronicBookList") ?
      JSON.parse(localStorage.getItem("electronicBookList")) : [],
    //已选纸质书列表
    paperBookList: localStorage.getItem("paperBookList") ?
      JSON.parse(localStorage.getItem("paperBookList")) : [],
  },
  mutations: {
    //添加电子书
    appplyElectronicBook(state, item) {
      state.electronicBookList.unshift(item)
      localStorage.setItem(
        "electronicBookList",
        JSON.stringify(state.electronicBookList)
      )
    },
    //添加纸质书
    appplyPaperBook(state, item) {
      state.paperBookList.unshift(item);
      localStorage.setItem(
        "paperBookList",
        JSON.stringify(state.paperBookList)
      );
    },
    //移除电子书
    removeElectronicBook(state, index) {
      state.electronicBookList.splice(index, 1);
      localStorage.removeItem("electronicBookList");
      localStorage.setItem(
        "electronicBookList",
        JSON.stringify(state.electronicBookList)
      );
    },
    //移除纸质书
    removePaperBook(state, index) {
      state.paperBookList.splice(index, 1);
      localStorage.removeItem("paperBookList");
      localStorage.setItem(
        "paperBookList",
        JSON.stringify(state.paperBookList)
      );
    },
    //清空电子书
    clearElectronicBook(state) {
      state.electronicBookList.length = 0;
      localStorage.removeItem("electronicBookList");
      localStorage.setItem(
        "electronicBookList",
        JSON.stringify([])
      );
    },
    //清空纸质书
    clearPaperBook(state) {
      state.paperBookList.length = 0;
      localStorage.removeItem("paperBookList");
      localStorage.setItem(
        "paperBookList",
        JSON.stringify([])
      );
    },
    //用户信息
    changeUserInfo(state, val) {
      if (val) {
        // é»˜è®¤å¤´åƒ
        state.userInfo = JSON.parse(JSON.stringify(val));
        if (!val.icon) {
          val.icon = require("@/assets/images/default_avatar.png");
        }
        tool.setCookie(userInfoKey, JSON.stringify(val));
        localStorage.setItem(userInfoKey, JSON.stringify(val));
      } else {
        console.log(val, 2);
        tool.setCookie(userInfoKey, JSON.stringify(val));
        localStorage.setItem(userInfoKey, JSON.stringify(val));
      }
    },
    changeLogin(state, val) {
      if (val) {
        state.token = val;
        tool.setCookie(tokenKey, val);
      } else {
        state.token = null;
        tool.delCookie(tokenKey);
      }
    },
    //用户信息
  },
  actions: {
    setUserInfo(context, val) {
      context.commit("changeUserInfo", val);
    },
    setToken(context, val) {
      context.commit("changeLogin", val);
    },
  },
  modules: {}
});
src/views/bookList/bookApply.vue
New file
@@ -0,0 +1,213 @@
<template>
  <div class="applyPage">
    <van-nav-bar
      title="试用申请"
      left-text=""
      right-text=""
      left-arrow
      @click-left="onClickLeft"
    />
    <div class="pageTitle">旅游教育出版社教师教材试用申请表</div>
    <div class="eleContent">
      <div class="bookInfo">
        <div class="title">教材申请</div>
        <div class="book-example">
          <div class="imgBox">
            <img :src="textBookInfo.icon" class="autoImg" alt />
          </div>
          <div class="bookname">{{ textBookInfo.name }}</div>
        </div>
      </div>
      <div class="formBox">
        <div class="title">授课情况</div>
        <van-form ref="formData">
          <van-cell-group>
            <van-field
              v-model="formData.courseName"
              label="课程名称:"
              required
              :rules="[{ required: true, message: '请输入课程名称' }]"
            />
            <van-field
              v-model="formData.studentLevel"
              label="学生层次:"
              required
              :rules="[{ required: true, message: '请输入学生层次' }]"
            />
            <van-field
              v-model="formData.studentsNumber"
              required
              label="学生人数/年:"
              :rules="[{ required: true, message: '请输入学生人数/å¹´' }]"
            />
            <van-field
              v-model="formData.teachingMaterialPress"
              required
              label="现用教材出版社:"
              :rules="[{ required: true, message: '请输入现用教材出版社' }]"
            />
            <van-field
              v-model="formData.teachingMaterials"
              required
              label="所用教材:"
              :rules="[{ required: true, message: '请输入所用教材' }]"
            />
          </van-cell-group>
          <div class="vanBtn">
            <van-button
              round
              block
              type="info"
              @click="onSubmit"
              v-if="!btnLoading"
            >
              æäº¤
            </van-button>
          </div>
        </van-form>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from "vue";
import { Toast, SwipeCell, Dialog } from "vant";
import { mapState } from "vuex";
Vue.use(Dialog);
Vue.use(SwipeCell);
export default {
  data() {
    return {
      textBookInfo: {},
      formData: {
        courseName: "",
        studentLevel: "",
        studentsNumber: "",
        teachingMaterialPress: "",
        teachingMaterials: ""
      },
      btnLoading: false
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    this.textBookInfo = JSON.parse(this.$route.query.bookInfo);
    this.getType();
  },
  methods: {
    getType() {
      const data = {
        refCodes: ["tourism_digitalTextbooksApplication"]
      };
      this.MG.resource.getCmsTypeByRefCode(data).then(res => {
        this.description = res[0].description;
        this.formData.workInfo = res[0].cmsTypeLinks[0].children;
      });
    },
    onSubmit() {
      var that = this;
      for (const key in that.formData) {
        if (that.formData[key] == "") {
          Toast.fail("请填写所需资料");
          return false;
        }
      }
      this.btnLoading = true;
       const data = {
        topicIdOrRefCode: "applyDigitalBook",
        name: this.userInfo.name || "",
        content: JSON.stringify(that.textBookInfo),
        state: "WaitAudit",
        type: "applyDigitalBook",
        cmsTypeRefCode: "tourism_digitalTextbooksApplication ",
        newDataListRequest: this.tool.worksDataBytool(
          this.formData.workInfo,
          this.formData
        ),
      };
      this.MG.ugc.newTopicMessage(data).then((res) => {
        this.loading = false;
        if (res) {
           Dialog.confirm({
              title: "提示",
              message: "您的试用申请已提交,管理员审核中!",
              confirmButtonText: "确定",
              cancelButtonText: "取消"
            })
            .then(() => {
              this.$router.push({
                name: "myApplyBook"
              });
            })
            .catch(() => {
              this.$router.go(-1);
            });
        }
      });
    },
    onClickLeft() {
      var that = this;
      that.$router.go(-1);
    }
  }
};
</script>
<style scoped>
.pageTitle {
  font-size: 16px;
  text-align: center;
  padding: 20px 0;
  font-weight: bold;
  border-bottom: 2px dashed #dcdcdc;
}
.bookInfo {
  padding: 0 15px;
}
.book-example {
  width: 200px;
  margin: 0 auto;
}
.imgBox {
  box-shadow: 0 3px 6px 1px #00000029;
  position: relative;
  width: 134px;
  height: 180px;
  margin: 0 auto;
  border-radius: 5px;
  overflow: hidden;
}
.bookname {
  margin: 10px;
  font-size:16px;
}
.formBox {
  padding: 0 15px;
}
.title {
  font-size: 14px;
  font-weight: bold;
  padding: 10px 0;
  margin: 10px 0;
}
.vanBtn {
  width: 100%;
  padding: 16px;
  background: #fff;
  position: relative;
  bottom: 0px;
  z-index: 100;
}
.box >>> .van-tabs__line {
  background-color: #3586ff !important;
}
.formBox >>> .van-field__label {
  width: auto !important;
}
</style>
src/views/bookList/bookDetail.vue
New file
@@ -0,0 +1,950 @@
<template>
  <div class="detail">
    <div class="hearder">
      <van-nav-bar
        title="数字教材详情"
        left-text=""
        right-text=""
        left-arrow
        @click-left="onClickLeft"
      />
    </div>
    <div class="bookInfo" v-if="!loading">
      <div class="list">
        <div class="leftBox">
          <div class="imgBox">
            <img :src="detailsInfo.icon" alt class="bookImg" />
          </div>
          <div class="section">
            <div
              class="starSection"
              v-if="!detailsInfo.isFavourite"
              @click="collectBook()"
            >
              <div style="text-align: center">
                <img :src="collectIcon.star" alt="" class="stars" />
              </div>
              <p>收藏</p>
            </div>
            <div
              class="starSection"
              v-if="detailsInfo.isFavourite"
              @click="collectBook()"
            >
              <div style="text-align: center">
                <img :src="collectIcon.selected_star" alt="" class="stars" />
              </div>
              <p>已收藏</p>
            </div>
            <div @click="contactUs()">
              <div style="text-align: center">
                <img
                  src="@/assets/images/textBook/contact.png"
                  alt=""
                  class="stars"
                />
              </div>
              <p>联系我们</p>
            </div>
          </div>
        </div>
        <div class="centerContent">
          <p class="bookName">{{ detailsInfo.name }}</p>
          <p class="sameTitle">作者:{{ detailsInfo.tourism_author || "-" }}</p>
          <p class="sameTitle">ISBN:{{ detailsInfo.tourism_ISBN || "-" }}</p>
          <p class="sameTitle">
            å‡ºç‰ˆæ—¶é—´:{{
              detailsInfo.tourism_publicationDate
                ? moment(
                    detailsInfo.tourism_publicationDate.split(" ")[0]
                  ).format("YYYY-MM")
                : "-"
            }}
          </p>
          <p class="sameTitle">教学层次:{{ teachingLevel || "-" }}</p>
          <p class="sameTitle">专业类型:{{ professionalCategory || "-" }}</p>
          <p class="sameTitle">专区:{{ zone || "-" }}</p>
        </div>
      </div>
      <div class="priceBox">
        <span>数字教材: </span>
        <span v-if="detailsInfo.price > 0" class="price">{{
          "Â¥" + tool.toDecimal2(detailsInfo.price)
        }}</span>
        <span class="freePrice" v-if="detailsInfo.price == 0">免费</span>
        <span class="oldPrice" v-if="detailsInfo.oldPrice">{{
          "Â¥" + tool.toDecimal2(detailsInfo.oldPrice)
        }}</span>
      </div>
    </div>
    <!-- tab标签页 -->
    <div class="tabsBox">
      <van-tabs v-model="tabActive" class="tabs" @change="tabChange">
        <van-tab title="目录">
          <div
            class="detailBox"
            v-if="
              detailsInfo.tourism_catalog &&
                detailsInfo.tourism_catalog !== '<p></p>'
            "
            v-html="detailsInfo.tourism_catalog"
          ></div>
          <div v-else class="noData">
            <van-empty description="暂无数据" image-size="100" />
          </div>
        </van-tab>
        <van-tab title="内容简介">
          <div
            class="detailBox"
            v-if="
              detailsInfo.tourism_content &&
                detailsInfo.tourism_content !== '<p></p>'
            "
            v-html="detailsInfo.tourism_content"
          ></div>
          <div v-else class="noData">
            <van-empty description="暂无数据" image-size="100" />
          </div>
        </van-tab>
        <van-tab title="作者简介">
          <div
            class="detailBox"
            v-if="
              detailsInfo.tourism_authorBrief &&
                detailsInfo.tourism_authorBrief !== '<p></p>'
            "
            v-html="detailsInfo.tourism_authorBrief"
          ></div>
          <div v-else class="noData">
            <van-empty description="暂无数据" image-size="100" />
          </div>
        </van-tab>
        <van-tab title="资源统计">
          <div v-if="!resourceHave" class="noData">
            <van-empty description="暂无数据" image-size="100" />
          </div>
          <div class="detailBox resourceBox">
            <div class="title">资源分布</div>
            <div class="echartsBox">
              <div
                ref="resourcesChart"
                style="width: 100%; height: 300px"
              ></div>
            </div>
            <div class="title" v-if="resourceData.length > 0">
              èµ„源明细
            </div>
            <div class="resourceList" v-if="resourceData.length > 0">
              <div
                v-for="(item, index) in resourceData"
                :key="index"
                class="listItem"
              >
                <div class="listImg">
                  <img class="autoImg" :src="item.list[0].icon" />
                </div>
                <div class="titleCount">
                  <div class="title">
                    {{ item.resourceTypeShow }}
                  </div>
                  <div class="title">{{ item.list.length }} ä¸ª</div>
                </div>
              </div>
            </div>
          </div>
        </van-tab>
      </van-tabs>
    </div>
    <div class="buttom">
      <ul style="display: flex">
        <li class="btmBottom">
          <img
            src="@/assets/images/textBook/goushuma.png"
            alt=""
            class="btnImg"
          />
          <p @click="useBookCode()" v-if="!isBuy">使用购书码</p>
          <p v-else class="disable">使用购书码</p>
        </li>
        <li class="btmBottom">
          <img
            src="@/assets/images/textBook/shiyong.png"
            alt=""
            class="btnImg"
          />
          <p @click="applyBook()" v-if="!isBuy">申请试用</p>
          <p v-else class="disable">申请试用</p>
        </li>
        <li class="btmBottom1">
          <img src="@/assets/images/textBook/shidu.png" alt="" class="btnImg" />
          <p v-if="isBuy" @click="readBook()">阅读</p>
          <p v-if="!isBuy" @click="readBook()">试读</p>
        </li>
        <li
          class="btmBottom2"
          :style="isBuy ? 'background:#999' : 'background:#FF5B77'"
        >
          <p @click="payNow()" v-if="!isBuy">立即购买</p>
          <p v-else>立即购买</p>
        </li>
        <li
          class="btmBottom2"
          :style="isBuy ? 'background:#999' : 'background:#2E70FF'"
        >
          <p @click="addCart()" v-if="!isBuy">加入购物车</p>
          <p v-else>加入购物车</p>
        </li>
      </ul>
    </div>
    <van-dialog
      v-model="dialogCode"
      title="使用购书码"
      show-cancel-button
      @confirm="onConfirm"
    >
      <van-field
        v-model="code"
        placeholder="请输入购书码"
        class="message"
      />
    </van-dialog>
  </div>
</template>
<script>
import Vue from "vue";
import { Toast, SwipeCell, Dialog } from "vant";
import { mapState } from "vuex";
import toolClass from "@/assets/js/toolClass";
import myConfig from "@/assets/js/config";
import axios from "axios";
var echarts = require("echarts");
// å…¨å±€æ³¨å†Œ
Vue.use(Toast);
Vue.use(Dialog);
Vue.use(SwipeCell);
export default {
  data() {
    return {
      token: "",
      id: "",
      //加载状态
      loading: false,
      // è¯¦æƒ…顶部列表
      detailsInfo: {},
      teachingLevel: "",
      professionalCategory: "",
      zone: "",
      // æ”¶è—å›¾ç‰‡
      collectIcon: {
        star: require("@/assets/images/tab_collection.png"),
        selected_star: require("@/assets/images/tab_collection_pre.png")
      },
      isBuy: false,
      tabActive: 0,
      resourceHave: false,
      resourceData: [],
      AudioIcon: require("@/assets/images/textBook/Audio.png"),
      DIcon: require("@/assets/images/textBook/3D.png"),
      imgIcon: require("@/assets/images/textBook/img.png"),
      PPTIcon: require("@/assets/images/textBook/PPT.png"),
      shijuanIcon: require("@/assets/images/textBook/shijuan.png"),
      shixunIcon: require("@/assets/images/textBook/shixun.png"),
      tuozhanIcon: require("@/assets/images/textBook/tuozhan.png"),
      videoIcon: require("@/assets/images/textBook/video.png"),
      VRIcon: require("@/assets/images/textBook/VR.png"),
      ziliaoIcon: require("@/assets/images/textBook/ziliao.png"),
      teacherState: "",
      shoppingCartGetId: [],
      dialogCode: false,
      code: ""
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    var that = this;
    that.token = toolClass.getCookie(myConfig.tokenKey);
    that.id = that.$route.query.id;
    that.getDetail(that.$route.query.id);
  },
  methods: {
    //获取商品详情
    getDetail(id) {
      var that = this;
      that.loading = true;
      that.MG.store
        .getProductDetail({
          storeInfo: that.config.digitalTextBooksGoodsStore,
          path: "*",
          queryType: "*",
          productId: id,
          favoriteTypes: that.config.refCodes.LinkType.FavoriteTextBook,
          fields: {
            tourism_author: [],
            tourism_publicationDate: [],
            tourism_format: [],
            tourism_ISBN: [],
            tourism_editionImpression: [],
            tourism_content: [],
            tourism_catalog: [],
            tourism_authorBrief: [],
            tourism_teachingLevel: [],
            tourism_professionalCategory: [],
            tourism_zone: [],
            viewCount: []
          },
          coverSize: {
            width: 150
          }
        })
        .then(res => {
          console.log(res.datas);
          this.isBuy = res.datas.alreadyBuy;
          that.detailsInfo = res.datas;
          that.loading = false;
          if (this.detailsInfo.price == 0 && !this.isBuy && this.userInfo) {
            if (this.detailsInfo.defaultSaleMethodId) {
              this.MG.store
                .initOrder({
                  requests: [
                    {
                      saleMethodId: this.detailsInfo.defaultSaleMethodId,
                      count: 1
                    }
                  ]
                })
                .then(initRes => {
                  if (initRes.orderNumber) {
                    this.MG.store
                      .confirmOrder({ orderNum: initRes.orderNumber })
                      .then(aRes => {
                        if (aRes) {
                          Toast.success("免费领取成功!");
                          this.getDetail(id);
                        }
                      });
                  }
                });
            }
          }
          that.getProductType();
          this.getBookResource();
        });
    },
    getProductType() {
      var that = this;
      this.MG.store
        .getProductTypeField({
          refCodes: [
            "tourism_teachingLevel",
            "tourism_professionalCategory",
            "tourism_zone"
          ]
        })
        .then(res => {
          if (res.length > 0) {
            res.forEach(element => {
              let optionList = JSON.parse(element.config).option;
              if (element.refCode == "tourism_teachingLevel") {
                optionList.forEach(item => {
                  if (that.detailsInfo.tourism_teachingLevel == item.value) {
                    that.teachingLevel = item.name;
                  }
                });
              }
              if (element.refCode == "tourism_professionalCategory") {
                optionList.forEach(item => {
                  if (
                    that.detailsInfo.tourism_professionalCategory == item.value
                  ) {
                    that.professionalCategory = item.name;
                  }
                });
              }
              if (element.refCode == "tourism_zone") {
                optionList.forEach(item => {
                  if (that.detailsInfo.tourism_zone == item.value) {
                    that.zone = item.name;
                  }
                });
              }
            });
          }
        });
    },
    //收藏
    collectBook() {
      if (this.detailsInfo.isFavourite) {
        this.MG.store
          .delProductLink({
            productIds: [this.detailsInfo.id],
            linkType: this.config.refCodes.LinkType.FavoriteTextBook
          })
          .then(() => {
            this.detailsInfo.isFavourite = false;
          });
      } else {
        let params = {
          productIds: [this.detailsInfo.id],
          linkType: this.config.refCodes.LinkType.FavoriteTextBook
        };
        this.MG.store.addProductLink(params).then(res => {
          this.detailsInfo.isFavourite = true;
        });
      }
    },
    //获取教材资源信息
    getBookResource() {
      console.log(
        this.config.textBookResourceUrl +
          this.detailsInfo.refCode +
          "/resource.json"
      );
      try {
        axios
          .get(
            this.config.textBookResourceUrl +
              this.detailsInfo.refCode +
              "/resource.json"
          )
          .then(async res => {
            if (res.data.length > 0) {
              res.data.forEach(item => {
                if (item.resourceTypeShow == "图片") {
                  item.icon = this.imgIcon;
                } else if (item.resourceTypeShow == "视频") {
                  item.icon = this.videoIcon;
                } else if (item.resourceTypeShow == "音频") {
                  item.icon = this.AudioIcon;
                } else if (item.resourceTypeShow == "PPT") {
                  item.icon = this.PPTIcon;
                } else if (item.resourceTypeShow == "拓展") {
                  item.icon = this.tuozhanIcon;
                } else if (item.resourceTypeShow == "资料") {
                  item.icon = this.ziliaoIcon;
                } else if (item.resourceTypeShow == "试卷") {
                  item.icon = this.shijuanIcon;
                } else if (item.resourceTypeShow == "3D") {
                  item.icon = this.DIcon;
                } else if (item.resourceTypeShow == "实训") {
                  item.icon = this.shixunIcon;
                } else if (item.resourceTypeShow == "VR") {
                  item.icon = this.VRIcon;
                } else {
                  item.icon = this.ziliaoIcon;
                }
              });
              this.resourceData = await this.groupByResourceTypeShow(res.data);
              this.resourceHave = true;
            } else {
              this.resourceHave = false;
            }
            if (this.resourceData.length > 0) {
              this.resourceHave = true;
              this.initChart(this.resourceData);
            } else {
              this.resourceHave = false;
            }
          })
          .catch(error => {
            console.log(error);
            this.resourceData = [];
            this.resourceHave = false;
          });
      } catch (error) {
        this.resourceData = [];
        this.resourceHave = false;
      }
    },
    groupByResourceTypeShow(resources) {
      const grouped = resources.reduce((acc, item) => {
        const key = item.resourceTypeShow;
        if (!acc[key]) {
          acc[key] = {
            resourceTypeShow: key,
            list: []
          };
        }
        acc[key].list.push(item);
        return acc;
      }, {});
      return Object.values(grouped);
    },
    initChart(data) {
      let that = this;
      // åŸºäºŽå‡†å¤‡å¥½çš„dom,初始化echarts实例
      let dataList = [];
      let num = [];
      data.forEach(item => {
        dataList.push(item.resourceTypeShow);
        num.push(item.list.length);
      });
      var myChart = echarts.init(that.$refs.resourcesChart);
      // ç»˜åˆ¶å›¾è¡¨
      myChart.setOption({
        tooltip: {},
        xAxis: {
          data: dataList
        },
        yAxis: {},
        series: [
          {
            name: "数量",
            type: "bar",
            data: num,
            itemStyle: {
              color: function(params) {
                // params.dataIndex是数据项的索引,你可以根据这个索引来设置不同的颜色
                const colors = [
                  "#5EA1FF",
                  "#FF5A85",
                  "#7E7AFF",
                  "#3CB768",
                  "#FF8F54",
                  "#FF574B",
                  "#3DB0BF",
                  "#FBBB3B",
                  "#3B5EFB",
                  "#B1FB3B"
                ];
                return colors[params.dataIndex % colors.length];
              }
            }
          }
        ]
      });
    },
    tabChange(val) {
      console.log(val, 1);
      if (val == 3) {
        this.resourceHave = true;
        this.getBookResource();
      }
    },
    //使用购书码
    useBookCode() {
      this.code = "";
      this.dialogCode = true;
    },
    onConfirm() {
      var that = this;
      let lock = true;
      if (that.code == "") {
        Toast.fail("请输入购书码!");
      } else {
        if (lock) {
          lock = false;
          that.MG.store
            .getActiveCode({
              code: that.code
            })
            .then(res => {
              console.log(res);
              if (res && res.saleMethodList) {
                if (
                  res.saleMethodList[0].id ==
                  this.detailsInfo.defaultSaleMethodId
                ) {
                  that.MG.store
                    .userActiveCode({
                      code: that.code
                    })
                    .then(res => {
                      Toast.success(res == "激活成功" ? "够买成功" : res);
                      lock = true;
                      that.dialogCode = false;
                      that.getDetail(that.id);
                    });
                } else {
                  Toast.fail("您输入的购书码有误!");
                }
              } else {
                Toast.fail("您输入的购书码有误!");
              }
            });
        }
      }
    },
    //到购买页面
    payNow() {
      this.MG.store
        .initOrder({
          requests: [
            {
              saleMethodId: this.detailsInfo.defaultSaleMethodId,
              count: 1
            }
          ]
        })
        .then(res => {
          this.$router.push({
            path: "/pay",
            query: {
              orderNum: res.orderNumber,
              shopId: this.id
            }
          });
        });
    },
    readBook() {
      if (this.detailsInfo.refCode) {
        window.open(
          this.config.textReaderUrl +
            "?bookId=" +
            this.detailsInfo.refCode +
            "&token=" +
            this.token
        );
      } else {
        Toast.fail("暂无资源");
      }
    },
    async addCart() {
      let that = this;
      let query = {
        start: 0,
        size: 999,
        filterList: [],
        searchList: []
      };
      const res = await this.MG.store.getShoppingCartProductList(query);
      res.datas.forEach(item => {
        that.shoppingCartGetId.push(item.saleMethod.id);
      });
      if (
        that.shoppingCartGetId.includes(that.detailsInfo.defaultSaleMethodId)
      ) {
        Toast.fail("该书已在购物车,请勿重复添加");
      } else {
        const data = {
          requests: [
            {
              saleMethodId: this.detailsInfo.defaultSaleMethodId,
              storeEventId: null,
              agentCode: ""
            }
          ]
        };
        this.MG.store.addShoppingCart(data).then(res => {
          if (res) {
            Toast.success("加入购物车成功");
          }
        });
      }
    },
    //申请试用
    applyBook() {
      const role = this.userInfo.role;
      if (role == "Teacher") {
        this.getAlreadyBookList(this.detailsInfo);
      } else {
        Toast.fail("您还未通过教师审核");
      }
    },
    //获取已申请试用列表
    getAlreadyBookList(bookData) {
      const data = {
        start: 0,
        size: 9999,
        topicIdOrRefCode: "applyDigitalBook",
        appRefCode: this.config.appRefCode,
        sort: {
          type: "Desc",
          field: "CreateDate"
        }
      };
      this.MG.ugc.getTopicMessageList(data).then(res => {
        if (res.datas.length > 0) {
          res.datas.map(item => {
            if (item.feedBack) {
              item.feedBack = JSON.parse(item.feedBack);
              if (item.feedBack.endDate) {
                let times = new Date(
                  item.feedBack.endDate + " 23:59:59"
                ).getTime();
                if (times < sessionStorage.currentDate) {
                  item.isExpiry = true;
                }
              }
            }
            if (item.content) {
              item.content = JSON.parse(item.content);
              item.productId = item.content.id;
            }
          });
          let objVal = res.datas.find(
            i =>
              i.productId == bookData.id &&
              i.state != "Reject" &&
              i.state == "WaitAudit"
          );
          if (objVal && !objVal.isExpiry) {
            Dialog.confirm({
              title: "提示",
              message: "您已申请试用该书,是否前往查看",
              confirmButtonText: "确定",
              cancelButtonText: "取消"
            }).then(() => {
              this.$router.push({
                name: "myApplyBook"
              });
            });
          } else {
            this.$router.push({
              name: "bookApply",
              query: {
                bookInfo: JSON.stringify(this.detailsInfo)
              }
            });
          }
        } else {
          this.$router.push({
            name: "bookApply",
            query: {
              bookInfo: JSON.stringify(this.detailsInfo)
            }
          });
        }
      });
    },
    contactUs() {
      this.$router.push({
        name: "aboutUs"
      });
    },
    onClickLeft() {
      this.$router.go(-1);
    }
  },
  mounted() {
    //挂载成功后给pop事件绑定一个方法
    // å¦‚果支持 popstate ï¼ˆä¸€èˆ¬ç§»åŠ¨ç«¯éƒ½æ”¯æŒï¼‰
    if (window.history && window.history.pushState) {
      // å¾€åŽ†å²è®°å½•é‡Œé¢æ·»åŠ ä¸€æ¡æ–°çš„å½“å‰é¡µé¢çš„url
      history.pushState(null, null, document.URL);
      // ç»™ popstate ç»‘定一个方法用来监听页面返回
      window.addEventListener("popstate", this.onClickLeft, false); //false阻止默认事件
    }
  },
  //组件销毁后清除事件
  destroyed() {
    window.removeEventListener("popstate", this.onClickLeft, false); //false阻止默认事件
  }
};
</script>
<style scoped>
.detail {
  padding-bottom: 52px;
  background: #f4f7fb;
}
.hearder {
  width: 100%;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9;
}
/* è¯¦æƒ…内容 */
/* åˆ—表 */
.bookInfo {
  position: relative;
  height: 305px;
  margin-top: 40px;
}
.list {
  width: 100%;
  min-height: 240px;
  display: flex;
  top: 0;
  z-index: 2;
  position: absolute;
  padding: 15px;
  background: #eaf5ff;
  border-radius: 0px 0px 10px 10px;
}
.priceBox {
  width: 100%;
  height: 120px;
  line-height: 180px;
  text-align: right;
  position: absolute;
  font-size: 16px;
  color: #fff;
  bottom: 0;
  background: linear-gradient(180deg, #6b99fd 0%, #2e70ff 100%);
  border-radius: 0px 0px 10px 10px;
  padding: 0 15px;
}
.priceBox .price,
.freePrice {
  font-weight: bold;
  font-size: 20px;
  margin-left: 10px;
}
.priceBox .oldPrice {
  color: #dfdfdf;
  text-decoration: line-through;
  margin-left: 5px;
}
.imgBox {
  width: 110px;
  height: 150px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 5px;
  box-shadow: 0 3px 6px 1px #00000029;
}
.bookImg {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  display: inline-block;
}
.centerContent {
  margin-left: 20px;
}
.section {
  display: flex;
  justify-content: space-between;
  margin-top: 15px;
  color: #979797;
}
.stars {
  width: 20px;
  height: 20px;
  display: inline-block;
  margin-bottom: 5px;
}
.bookName {
  color: #333333;
  font-size: 18px;
  font-weight: bold;
}
.sameTitle {
  margin: 8px 0 5px 0;
  color: #333;
}
.tabsBox {
  margin-top: 20px;
  border-radius: 10px 10px 0 0;
  background: #fff;
  overflow: hidden;
  border: 1px solid #e3ecff;
  /* height: calc(100vh - 360px); */
}
.detailBox {
  padding: 15px;
  height: 400px;
  overflow: auto;
}
.noData {
  font-size: 16px;
  font-weight: bold;
  color: #999;
  min-height: 147px;
  line-height: 147px;
  text-align: center;
}
.detailBox .title {
  margin: 10px 0;
  font-weight: bold;
}
.resourceList {
  display: flex;
}
.listItem {
  width: calc(100% / 5);
  display: block;
  box-sizing: border-box;
}
.listImg {
  margin: 0 auto;
  position: relative;
  width: 60px;
  height: 60px;
}
.titleCount {
  margin: 0;
  overflow: hidden;
  text-align: center;
}
/* åº•部按钮  */
.buttom {
  width: 100%;
  height: 50px;
  position: fixed;
  left: 0;
  bottom: 0;
  z-index: 999;
  border-top: 1px solid #dddddd;
  padding-bottom: env(safe-area-inset-bottom);
}
.btmBottom {
  width: 20%;
  text-align: center;
  line-height: 22px;
  background-color: #fff;
  border-right: 1px solid #ddd;
}
.btmBottom1 {
  width: 15%;
  text-align: center;
  line-height: 22px;
  background-color: #fff;
  border-right: 1px solid #ddd;
}
.btmBottom2 {
  width: 25%;
  text-align: center;
  color: #fff;
  line-height: 48px;
}
.btnImg {
  margin-top: 5px;
}
.disable {
  color: #999;
}
</style>
<style>
.van-icon-arrow-left::before {
  color: #2b68cd;
  font-size: 15px;
}
.van-tab--active {
  color: #3586ff !important;
}
.van-tabs__line {
  background-color: #3586ff !important;
}
</style>
src/views/bookList/bookList.vue
New file
@@ -0,0 +1,539 @@
<template>
  <div class="bookListBox">
    <div class="searchForm">
      <van-search
        v-model="searchAllWords"
        placeholder="请输入搜索关键词"
        class="search"
        @search="onSearch"
      />
      <div class="selectBox">
        <van-dropdown-menu class="classifySortBox">
          <van-dropdown-item
            :title="teachingLevelTitle"
            v-model="teachingLevelType"
            :options="teachingLevel"
            @change="searchSort()"
          />
          <van-dropdown-item
            :title="professionalCategoryTitle"
            v-model="professionalCategoryType"
            :options="professionalCategory"
            @change="searchSort()"
          />
          <van-dropdown-item
            :title="zoneTitle"
            v-model="zoneType"
            :options="zone"
            @change="searchSort()"
          />
        </van-dropdown-menu>
        <div class="reset" @click="resetClick()">
          <span>重置</span>
          <img src="@/assets/images/textBook/chongzhi.png" alt />
        </div>
      </div>
    </div>
    <div class="listBox">
      <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
        <van-list
          v-model="loading"
          :finished="finished"
          :finished-text="finishedtext"
          @load="onLoad"
        >
          <template slot="finished" name="finished">
            <div v-if="listData.length == 0">
              <van-empty description="暂无数据" />
            </div>
            <div v-else>
              æ²¡æœ‰æ›´å¤šäº†
            </div>
          </template>
          <div class="flexWrap" v-if="listData.length > 0">
            <div
              class="displayColumn"
              v-for="(item, index) in listData"
              :key="index"
              @click="goBookDetail(item)"
            >
              <div class="imgBox">
                <img :src="item.icon" alt class="autoImg" />
              </div>
              <div class="bookInfo">
                <div class="title" :title="item.name">
                  {{ item.name }}
                </div>
                <div class="author" :title="item.tourism_author">
                  {{ item.tourism_author }}
                </div>
                <div class="priceBox">
                  <span v-if="item.price > 0" class="price">{{
                    "Â¥" + tool.toDecimal2(item.price)
                  }}</span>
                  <span class="freePrice" v-if="item.price == 0">免费</span>
                  <span class="oldPrice" v-if="item.oldPrice">{{
                    "Â¥" + tool.toDecimal2(item.oldPrice)
                  }}</span>
                </div>
              </div>
            </div>
          </div>
        </van-list>
      </van-pull-refresh>
    </div>
    <Footer />
  </div>
</template>
<script>
import Vue from "vue";
import Footer from "@/components/footer/footer";
import { Toast, Lazyload } from "vant";
Vue.use(Lazyload);
Vue.use(Toast);
export default {
  props: {
    keyWords: {
      type: String,
      default: ""
    }
  },
  components: {
    Footer
    // backHeader
  },
  data() {
    return {
      listData: [],
      btnFlag: false,
      circleList: "circleList",
      changeClass: "changeClass",
      searchBarFixed: false,
      searchAllWords: "",
      teachingLevelTitle: "",
      professionalCategoryTitle: "",
      zoneTitle: "",
      teachingLevel: [
        {
          text: "全部",
          value: "all"
        }
      ],
      professionalCategory: [
        {
          text: "全部",
          value: "all"
        }
      ],
      zone: [
        {
          text: "全部",
          value: "all"
        }
      ],
      teachingLevelType: "",
      professionalCategoryType: "",
      zoneType: "",
      finishedtext: "",
      loading: false,
      finished: false,
      refreshing: false,
      sum: "",
      //分页
      paginationData: {
        page: 1,
        limit: 10,
        totalCount: 0,
        totalPage: 0
      }
    };
  },
  created() {
    this.searchAllWords = this.$route.query.searchAllWords || "";
    this.getProductType();
  },
  mounted() {
    window.addEventListener("scroll", this.scrollToTop);
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.scrollToTop);
  },
  methods: {
    getProductType() {
      this.MG.store
        .getProductTypeField({
          refCodes: [
            "tourism_teachingLevel",
            "tourism_professionalCategory",
            "tourism_zone"
          ]
        })
        .then(res => {
          if (res.length > 0) {
            res.forEach(element => {
              let optionList = JSON.parse(element.config).option;
              if (element.refCode == "tourism_teachingLevel") {
                this.teachingLevelTitle = element.name;
                let list = [];
                optionList.forEach(item => {
                  list.push({
                    text: item.name,
                    value: item.value,
                    checked: false
                  });
                });
                this.teachingLevel = [...this.teachingLevel, ...list];
                console.log(this.teachingLevel, 222);
              }
              if (element.refCode == "tourism_professionalCategory") {
                this.professionalCategoryTitle = element.name;
                let list = [];
                optionList.forEach(item => {
                  list.push({
                    text: item.name,
                    value: item.value,
                    checked: false
                  });
                });
                this.professionalCategory = [
                  ...this.professionalCategory,
                  ...list
                ];
              }
              if (element.refCode == "tourism_zone") {
                this.zoneTitle = element.name;
                let list = [];
                optionList.forEach(item => {
                  list.push({
                    text: item.name,
                    value: item.value,
                    checked: false
                  });
                });
                this.zone = [...this.zone, ...list];
              }
            });
          }
        });
    },
    onLoad() {
      var that = this;
      that.getList();
    },
    //获取列表
    getList() {
      var that = this;
      this.finished = false;
      this.loading = true;
      this.finishedtext = "加载中";
      let searchData = {};
      if (that.searchAllWords) {
        searchData = {
          "Name*": that.searchAllWords,
          "||tourism_author*": that.searchAllWords
        };
      }
      const obj = {
        path:
          this.config.digitalTextBooksGoodsStore +
          "\\" +
          this.config.refCodes.textBooksStore.textBookCategory,
        storeInfo: this.config.digitalTextBooksGoodsStore,
        queryType: "*",
        coverSize: {
          width: 180
        },
        sort: {
          type: "Desc",
          field: "CreateDate"
        },
        paging: {
          start: (this.paginationData.page - 1) * this.paginationData.limit,
          size: this.paginationData.limit
        },
        fields: {
          tourism_author: [],
          tourism_publicationDate: [],
          "tourism_teachingLevel*":
            this.teachingLevelType == "all" ? "" : this.teachingLevelType,
          "tourism_professionalCategory*":
            this.professionalCategoryType == "all"
              ? ""
              : this.professionalCategoryType,
          "tourism_zone*": this.zoneType == "all" ? "" : this.zoneType,
          ...searchData,
          viewCount: []
        }
      };
      this.MG.store.getProductList(obj).then(res => {
        that.listData = [...that.listData, ...res.datas];
        //分页
        that.paginationData.totalCount = res.total;
        if (res.total > that.paginationData.limit) {
          that.paginationData.totalPage = parseInt(
            res.total / that.paginationData.limit
          );
          if (res.total % that.paginationData.limit > 0) {
            that.paginationData.totalPage++;
          }
        } else {
          that.paginationData.totalPage = 1;
        }
        that.sum = Number(Math.ceil(res.total / this.paginationData.limit));
        // åŠ è½½çŠ¶æ€ç»“æŸ
        that.loading = false;
        that.refreshing = false;
        if (that.paginationData.page == this.sum) {
          that.finished = true;
          that.finishedtext = "没有更多了";
          return false;
        }
        if (that.listData.length < 1) {
          that.loading = false;
          that.finished = true;
          that.finishedtext = "暂无数据";
        }
        console.log(that.listData);
        that.paginationData.page++;
      });
    },
    //选中分类
    searchSort() {
      this.listData = [];
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.getList();
    },
    resetClick() {
      this.listData = [];
      this.teachingLevelType = "all";
      this.professionalCategoryType = "all";
      this.zoneType = "all";
      this.searchAllWords = "";
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.getList();
    },
    onSearch() {
      this.listData = [];
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.getList();
    },
    onRefresh() {
      this.listData = [];
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.onLoad();
    },
    goBookDetail(data) {
      if (data.refCode) {
        this.$router.push({
          path: "/bookDetail",
          query: {
            id: data.id
          }
        });
      } else {
        Toast.fail("正在建设中,敬请期待");
      }
    },
    // ç‚¹å‡»å›¾ç‰‡å›žåˆ°é¡¶éƒ¨æ–¹æ³•,加计时器是为了过渡顺滑
    backTop() {
      const that = this;
      const timer = setInterval(() => {
        const ispeed = Math.floor(-that.scrollTop / 5);
        document.documentElement.scrollTop = document.body.scrollTop =
          that.scrollTop + ispeed;
        if (that.scrollTop === 0) {
          clearInterval(timer);
        }
      }, 16);
    },
    // ä¸ºäº†è®¡ç®—距离顶部的高度,当高度大于60显示回顶部图标,小于60则隐藏
    scrollToTop() {
      const that = this;
      const scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
      that.scrollTop = scrollTop;
      if (that.scrollTop > 60) {
        that.btnFlag = true;
      } else {
        that.btnFlag = false;
      }
    },
    handleScroll() {
      var scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
      if (scrollTop > 100) {
        this.searchBarFixed = true;
      } else {
        this.searchBarFixed = false;
      }
    }
  }
};
</script>
<style scoped>
.searchForm {
  width: 100%;
  height: 105px;
  position: fixed;
  top: 0px;
  z-index: 999;
  background-color: #e2f1fe;
  border-bottom: 3px solid #fff;
}
.search {
  background: none !important;
  width: 100%;
  padding: 15px !important;
}
.selectBox {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 36px;
  padding: 0 15px;
}
.classifySortBox {
  height: 36px;
  display: flex;
  align-items: center;
  flex: 1 1;
}
.reset {
  height: 36px;
  width: 45px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: #2b68cd;
}
.listBox {
  margin-top: 105px;
  width: 100%;
  padding: 15px;
  padding-bottom: 50px;
  background: linear-gradient(180deg, #e2f1fe 0%, #ffffff 100%);
}
.listBox >>> .van-list {
  min-height: calc(100vh - 220px);
}
.flexWrap {
  display: flex;
  flex-wrap: wrap;
  min-height: 147px;
}
.displayColumn {
  width: calc(100% / 3 - 14px);
  margin: 7px;
}
.displayColumn .imgBox {
  width: 100%;
  height: 135px;
  margin: 0 auto;
  border-radius: 5px;
  transition: transform 0.3s ease;
  transform: scale(1);
  overflow: hidden;
  box-shadow: 0 3px 6px 1px #00000029;
}
.displayColumn .bookInfo {
  margin: 0;
  overflow: hidden;
}
.displayColumn .bookInfo .title {
  color: #333;
  font-weight: 600;
  margin-top: 10px;
  margin-bottom: 8px;
  font-size: 14px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
.displayColumn .bookInfo .author {
  font-size: 14px;
  color: #999;
  margin: 5px 0;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
.price {
  color: #ef1b3a;
  font-size: 15px;
  font-weight: bold;
}
.freePrice {
  color: #0bc266;
  font-size: 15px;
  font-weight: bold;
}
.oldPrice {
  text-decoration: line-through;
  color: #999 !important;
  margin-left: 5px;
  font-size: 13px;
}
</style>
<style>
.van-search__content {
  background-color: #fff;
  border-radius: 4px;
}
.van-dropdown-menu__bar {
  background: none !important;
  height: 36px !important;
  flex: 1;
  box-shadow: none !important;
}
.van-dropdown-menu__item {
  justify-content: space-between !important;
}
.van-dropdown-menu__title {
  font-size: 14px;
}
.van-dropdown-menu__title::after {
  border-color: transparent transparent #999 #999;
}
.van-dropdown-menu__title--active {
  color: #2b68cd;
}
.van-dropdown-item__option--active {
  color: #2b68cd;
}
.van-dropdown-item__option--active .van-dropdown-item__icon {
  color: #2b68cd;
}
.van-popup {
  border-radius: 0 0 10px 10px;
}
.van-pull-refresh {
  border-radius: 10px;
  background: #fff;
  padding: 5px;
}
</style>
src/views/entrance/bindWeChatAuto.vue
New file
@@ -0,0 +1,55 @@
<template>
  <div class="box">
    <div class="title">提示</div>
    <div class="content">
      æ³¨å†ŒæˆåŠŸä¸ºæ‚¨è´¦æˆ·è‡ªåŠ¨ç»‘å®šå½“å‰å¾®ä¿¡ï¼Œä½¿ç”¨æ‰‹æœºæˆ–å¾®ä¿¡å‡å¯è®¿é—®æœ¬ç½‘ç«™.
    </div>
    <div class="btn" @click="bindingWeChat">点击继续</div>
  </div>
</template>
<script>
export default {
  methods: {
    bindingWeChat() {
      this.WeChat.getCode("bindingWeChat");
    }
  }
};
</script>
<style>
.box {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: 20px;
  color: #333;
  text-align: center;
  padding: 0 20px;
}
.title {
  font-size: 30px;
  font-weight: bold;
  margin-bottom: 20px;
  margin-top: 30px;
}
.content {
  margin-top: 130px;
  font-size: 16px;
}
.btn {
  width: 200px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  color: #409eff;
  border-radius: 20px;
  margin-top: 80px;
  cursor: pointer;
}
</style>
src/views/entrance/entrance.vue
New file
@@ -0,0 +1,211 @@
<template>
  <div class="wrap">
    <div class="choice">
      <van-button
        class="btnSize"
        size="large"
        type="primary"
        @click="toPhoneLogin"
        >手机号登录/注册</van-button
      >
      <van-button
        class="btnSize"
        size="large"
        type="info"
        @click="weChatLogin()"
        >微信登录/注册</van-button
      >
    </div>
    <div class="checkbox">
      <van-checkbox v-model="checked" shape="square"
        >我已阅读并同意
        <span
          style="color: #3586ff; z-index: 1"
          @click.stop="dialogVisible = true"
          >用户条款</span
        >与<span
          style="color: #3586ff; z-index: 1"
          @click.stop="privacyDialogVisible = true"
          >隐私协议</span
        >
      </van-checkbox>
    </div>
    <div class="tips">
      <span class="reminder">温馨提示</span>
      <div class="block"></div>
    </div>
    <div class="annotation">
      <p>如果您已注册过旅游教育出版社PC端账户:</p>
      <p>1. è¯·ä½¿ç”¨æ‰‹æœºå·ç™»å½•,登录后将自动绑定当前微信。</p>
      <p>2. ç»§ç»­ä½¿ç”¨å¾®ä¿¡ç™»å½•将创建独立微信账号。</p>
    </div>
    <van-dialog
      v-model="dialogVisible"
      title="《用户条款》"
      width="90%"
      confirmButtonColor="#3586ff"
      messageAlign="left"
    >
      <div>
        <div class="protocolBox" v-html="protocolTxt"></div>
      </div>
    </van-dialog>
    <van-dialog
      v-model="privacyDialogVisible"
      title="《隐私协议》"
      width="90%"
      confirmButtonColor="#3586ff"
      messageAlign="left"
    >
      <div>
        <div class="protocolBox" v-html="privacyTxt"></div>
      </div>
    </van-dialog>
  </div>
</template>
<script>
import Vue from "vue";
import { Toast } from "vant";
Vue.use(Toast);
export default {
  /**
   * .农大社移动端增加登录方式选择
   * å¢žåŠ æ‰‹æœºå·çš„ç™»å½•ï¼Œå¾®ä¿¡ç™»å½• ç§»åŠ¨ç«¯ ç™»é™†æ–¹å¼é€‰æ‹©ï¼Œ
   * æ‰‹æœºå·ç™»å½• -> èŽ·å–å¾®ä¿¡code é™é»˜ç»‘定微信
   * å¾®ä¿¡ç™»å½• -> è®©ç»‘定手机号。
   */
  data() {
    return {
      checked: false,
      dialogVisible: false,
      protocolTxt: "",
      privacyTxt: "",
      privacyDialogVisible: false
    };
  },
  created() {
    this.getProtocol();
  },
  methods: {
    toPhoneLogin() {
      if (!this.checked) {
        Toast("请同意用户协议与隐私协议后再进行登录");
        return false;
      }
      this.$router.push({
        name: "login"
      });
    },
    weChatLogin() {
      if (!this.checked) {
        Toast("请同意用户协议与隐私协议后再进行登录");
        return false;
      }
      // é‡æ–°èŽ·å–code,类型为微信登录
      // é‡æ–°èŽ·å–code,类型为微信登录
      this.WeChat.getCode("weChatLogin");
    },
    getProtocol() {
      var that = this;
      that.MG.resource
        .getItem({
          path: "tourism_protocol",
          fields: {
            tourism_content: []
          }
        })
        .then(res => {
          try {
            const data = res.datas.find(
              e => e.refCode == "tourism_userRegistrationAgreement"
            );
            const privacyTxtData = res.datas.find(
              e => e.refCode == "tourism_privacyPolicy"
            );
            this.protocolTxt = data ? data.tourism_content : "暂无协议";
            this.privacyTxt = privacyTxtData
              ? privacyTxtData.tourism_content
              : "暂无协议";
          } catch (error) {
            this.protocolTxt = "暂无协议";
            this.privacyTxt = "暂无协议";
          }
        });
    }
  }
};
</script>
<style lang="less" scoped>
.wrap {
  width: 100%;
  height: 100vh;
  overflow: auto;
  box-sizing: border-box;
  padding: 20px;
  background-color: #fff;
  font-size: 12px;
  display: flex;
  flex-direction: column;
  align-self: center;
  .backgroundImg {
    margin-top: 50px;
    margin-bottom: 30px;
    font-size: 30px;
    text-align: center;
  }
  .tips {
    margin: 0 auto;
    font-size: 14px;
    color: #666;
    position: relative;
    margin-top: 50px;
    .reminder {
      color: #7e6ba2;
      font-size: 18px;
      text-align: center;
    }
    .block {
      width: 130px;
      height: 10px;
      background-color: yellow;
      position: absolute;
      top: 22px;
      left: 50%;
      transform: translateX(-50%);
    }
  }
  .annotation {
    // width: 550px;
    margin-top: 230px;
    margin: 0 auto;
    font-size: 16px;
    color: #666;
    margin-top: 20px;
    text-align: left;
    font-size: 14px;
  }
  .choice {
    // width: 500px;
    // margin: 0 auto;
    margin-top: 300px;
    .btnSize {
      height: 50px;
      border-radius: 10px;
      margin-bottom: 20px;
    }
  }
  .checkbox {
    display: flex;
    justify-content: center;
  }
  .protocolBox {
    height: 60vh;
    padding: 10px 20px;
    box-sizing: border-box;
    overflow: auto;
  }
}
</style>
src/views/entrance/login.vue
New file
@@ -0,0 +1,432 @@
<template>
  <div class="warpIndex content">
    <div class="loginContentBox">
      <div class="backgroundImg">
        æ—…游教育出版社
      </div>
      <div class="loginContent">
        <!-- validate-first  -->
        <van-form
          :disabled="loginLoading"
          v-show="loginFormType == 'code'"
          ref="login"
          @submit="loginFun"
        >
          <div class="codeLoginItemBox flex">
            <van-field
              placeholder="请输入手机号"
              v-model="loginForm.mobilePhone"
              name="mobilePhone"
              @keyup.enter.native="loginFun"
              :rules="[
                { required: true, message: '' },
                {
                  pattern: rulesPhonePattern,
                  message: '请输入正确格式的手机号'
                }
              ]"
            ></van-field>
          </div>
          <div class="codeLoginItemBox flex">
            <van-field
              placeholder="请输入图形验证码"
              name="imgCode"
              v-model="loginForm.imgCode"
              @keyup.enter.native="loginFun"
              :rules="[
                { required: true, message: '' },
                { pattern: rulesImgCode, message: '请输入4位的图形验证码' }
              ]"
            ></van-field>
            <div class="centerVertically">
              <img
                :src="imgCode"
                class="imgCode"
                alt=""
                @click="getImgCapcha"
              />
            </div>
          </div>
          <div class="codeLoginItemBox flex">
            <van-field
              placeholder="请输入验证码"
              name="verificationCode"
              :rules="[
                { required: true, message: '' },
                { pattern: rulesImgCode, message: '请输入4位的验证码' }
              ]"
              v-model="loginForm.verificationCode"
              @keyup.enter.native="loginFun"
            ></van-field>
            <div class="centerVertically">
              <van-button
                size="large"
                style="width: 125px; height: 40px; color: #828282"
                @click.prevent="getVerifyCode"
                :disabled="countDown != 0"
                >{{
                  countDown == 0
                    ? "获取短信验证码"
                    : "验证码(" + countDown + "s)"
                }}</van-button
              >
            </div>
          </div>
          <div class="changFormBtn">
            <span v-show="loginFormType == 'password'" @click="resetInput"
              >短信验证码登录</span
            >
            <span v-show="loginFormType == 'code'" @click="resetInput"
              >手机号密码登录</span
            >
          </div>
          <div class="loginBtnBox">
            <van-button
              type="primary"
              class="loginBtn"
              @click="loginFun"
              :loading="loginLoading"
              >登 å½•</van-button
            >
          </div>
        </van-form>
        <van-form
          :disabled="loginLoading"
          v-show="loginFormType == 'password'"
          ref="passwordLogin"
          @submit="phonePwdLogin"
        >
          <div class="loginItemBox">
            <van-field
              v-model="loginForm.mobilePhone"
              placeholder="请输入手机号"
              clearable
              :label-width="50"
              label="手机号"
              name="mobilePhone"
              :rules="[
                { required: true, message: '' },
                {
                  pattern: rulesPhonePattern,
                  message: '请输入正确格式的手机号'
                }
              ]"
              @keyup.enter.native="phonePwdLogin"
            ></van-field>
          </div>
          <div class="loginItemBox">
            <van-field
              :label-width="50"
              label="密码"
              v-model="loginForm.password"
              type="password"
              show-password
              name="password"
              :rules="[{ required: true, message: '' }]"
              placeholder="请输入密码"
            ></van-field>
          </div>
          <div class="changFormBtn">
            <span v-show="loginFormType == 'password'" @click="resetInput"
              >短信验证码登录</span
            >
            <span v-show="loginFormType == 'code'" @click="resetInput"
              >手机号密码登录</span
            >
          </div>
          <div class="loginBtnBox">
            <van-button
              type="primary"
              class="loginBtn"
              @click="phonePwdLogin"
              :loading="loginLoading"
              >登 å½•</van-button
            >
          </div>
        </van-form>
      </div>
    </div>
  </div>
</template>
<script>
import { Toast } from "vant";
export default {
  /**
   * .农大社移动端增加登录方式选择
   * å¢žåŠ æ‰‹æœºå·çš„ç™»å½•ï¼Œå¾®ä¿¡ç™»å½• ç§»åŠ¨ç«¯ ç™»é™†æ–¹å¼é€‰æ‹©ï¼Œ
   * æ‰‹æœºå·ç™»å½• -> èŽ·å–å¾®ä¿¡code é™é»˜ç»‘定微信
   * å¾®ä¿¡ç™»å½• -> è®©ç»‘定手机号。
   */
  data() {
    return {
      countDown: 0,
      loginFormType: "code",
      // ç™»å½•表单
      loginForm: {
        mobilePhone: "",
        password: "",
        imgCode: "",
        verificationCode: ""
      },
      imgCode: "",
      rulesPhonePattern:
        /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
      rulesImgCode: /^\d{4}$/,
      loginLoading: false
    };
  },
  created() {
    this.getImgCapcha();
  },
  methods: {
    getImgCapcha() {
      this.MG.identity.getImgCode().then((res) => {
        this.imgCode = "data:image/png;base64," + res;
      });
    },
    // æ‰‹æœºå· - éªŒè¯ç ç™»å½•
    loginFun() {
      this.$refs.login
        .validate(["mobilePhone", "imgCode", "verificationCode"])
        .then((res) => {
          this.loginLoading = true;
          this.MG.identity
            .loginByMobilePhone({
              phoneNumber: this.loginForm.mobilePhone,
              phoneCaptcha: this.loginForm.verificationCode,
              appRefCode: this.config.appRefCode,
              platform: "PCWeb"
            })
            .then((res) => {
              this.loginLoading = false;
              if (res && res.status == "Ok") {
                Toast.success("登录成功");
                let token = res.token;
                this.tool.setCookie(this.config.tokenKey, token);
                sessionStorage.token = token;
                // é™é»˜ç»‘定微信
                // this.$router.push("/bindWeChatAuto");
              } else {
                let errorTipsTxt = res.data.errorDescription || "手机号或验证码错误!";
                Toast.fail(errorTipsTxt);
              }
            });
        })
        .catch((res) => {});
    },
    // æ‰‹æœºå· - å¯†ç ç™»å½•
    phonePwdLogin() {
      this.$refs.passwordLogin
        .validate(["mobilePhone", "passwordLogin"])
        .then((res) => {
          this.loginLoading = true;
          this.MG.identity
            .loginByPassword({
              loginName: this.loginForm.mobilePhone,
              password: this.loginForm.password,
              appRefCode: this.config.appRefCode,
              platform: "PCWeb"
            })
            .then((res) => {
              this.loginLoading = false;
              if (res && res.status == "Ok") {
                Toast.success("登录成功");
                let token = res.token;
                this.tool.setCookie(this.config.tokenKey, token);
                sessionStorage.token = token;
                // é™é»˜ç»‘定微信
                // this.bindingWeChat();
              } else {
                let errorTipsTxt = res.message || "手机号或验证码错误!";
                Toast.fail(errorTipsTxt);
              }
            });
        })
        .catch((res) => {
          console.log(res);
        });
    },
    // èŽ·å–éªŒè¯ç 
    getVerifyCode() {
      this.$refs.login
        .validate(["mobilePhone", "imgCode"])
        .then((res) => {
          this.MG.identity
            .getPhoneCode({
              phoneNumber: this.loginForm.mobilePhone,
              imageCaptcha: this.loginForm.imgCode,
              appRefCode: this.config.appRefCode
            })
            .then((res) => {
              if (res == "验证码发送成功") {
                this.getSecond(60);
                Toast.success(res);
              } else {
                Toast.fail(res);
                this.getImgCapcha();
              }
            });
        })
        .catch((res) => {
          console.log(res);
        });
    },
    //验证码倒计时
    getSecond(time) {
      if (!this.timer) {
        this.countDown = time;
        this.timer = setInterval(() => {
          this.countDown--;
          if (this.countDown == 0) {
            clearInterval(this.timer);
            this.timer = null;
          }
        }, 1000);
      }
    },
    // é™é»˜ç»‘定微信
    bindingWeChat() {
      // é‡æ–°èŽ·å–code,类型为微信登录
      this.WeChat.getCode("bindingWeChat");
    },
    // åˆ‡æ¢è¾“入框 å¹¶é‡ç½® è¾“入信息
    resetInput() {
      if (this.loginFormType == "password") {
        this.loginFormType = "code";
        this.$refs.login.resetValidation([
          "mobilePhone",
          "imgCode",
          "verificationCode"
        ]);
      } else {
        this.loginFormType = "password";
        this.$refs.passwordLogin.resetValidation([
          "mobilePhone",
          "passwordLogin"
        ]);
      }
      for (const key in this.loginForm) {
        this.loginForm[key] = "";
      }
    }
  }
};
</script>
<style lang="less" scoped>
.warpIndex {
  // width: calc(100% - 40px);
  // height: calc(100vh - 40px);
  // overflow: auto;
  padding:  20px;
  background-color: #fff;
  font-size: 12px;
  box-sizing: border-box;
  .backgroundImg {
    margin-top: 50px;
    font-size: 36px;
    text-align: center;
    font-weight: 700;
    color: #000;
  }
  .loginContentBox {
    position: relative;
    .welcome {
      font-size: 40px;
    }
    .loginContent {
      margin-top: 200px;
      .errorTips {
        position: absolute;
        top: 8px;
        left: 10px;
        font-size: 12px;
        color: #e50021;
        .findPassword {
          cursor: pointer;
          color: #2b68cd;
          font-size: 12px;
        }
      }
      .codeLoginItemBox {
        margin-bottom: 15px;
        .imgCode {
          width: 130px;
          height: 45px;
          img {
            width: 100%;
            height: 100%;
            background: #efefef;
          }
        }
        // åž‚直居中
        .centerVertically {
          display: flex;
          flex-direction: column;
          justify-content: center;
        }
        /deep/ .van-field__error-message {
          font-size: 24px;
        }
        /deep/ .van-cell--borderless::after,
        .van-cell:last-child::after {
          display: block;
        }
      }
      .loginItemBox {
        display: flex;
        justify-content: space-between;
        align-items: center;
        border-radius: 4px;
        margin-bottom: 15px;
        box-sizing: border-box;
        span {
          display: inline-block;
          width: 100px;
          text-align: center;
        }
        /deep/ .van-field__error-message {
          font-size: 24px;
        }
        /deep/ .van-cell--borderless::after,
        .van-cell:last-child::after {
          display: block;
        }
      }
      .changFormBtn {
        margin-top: 30px;
        margin-bottom: 15px;
        overflow: hidden;
        text-align: right;
        span {
          cursor: pointer;
        }
      }
      .loginBtnBox {
        width: 80%;
        margin-bottom: 15px;
        margin: 0 auto;
        margin-top: 100px;
        .loginBtn {
          border-radius: 10px;
          width: 100%;
          height: 50px;
        }
      }
      .registerBox {
        margin-top: 20px;
        font-size: 14px;
        text-align: center;
        color: #666666;
        span {
          cursor: pointer;
        }
        .division {
          margin: 0 20px;
          cursor: initial;
          color: #d0d0d0;
        }
      }
    }
  }
}
</style>
src/views/entrance/weChatLogin.vue
New file
@@ -0,0 +1,150 @@
<template>
  <div class="box">
    <div class="title">登录</div>
    <div class="btn" @click="bindingWeChat">使用微信登录</div>
    <div class="checkbox">
      <van-checkbox v-model="checked" shape="square"
        >我已阅读并同意
        <span
          style="color: #3586ff; z-index: 1"
          @click.stop="dialogVisible = true"
          >用户条款</span
        >与<span
          style="color: #3586ff; z-index: 1"
          @click.stop="privacyDialogVisible = true"
          >隐私协议</span
        >
      </van-checkbox>
    </div>
    <van-dialog
      v-model="dialogVisible"
      title="《用户条款》"
      width="90%"
      confirmButtonColor="#3586ff"
      messageAlign="left"
    >
      <div>
        <div class="protocolBox" v-html="protocolTxt"></div>
      </div>
    </van-dialog>
    <van-dialog
      v-model="privacyDialogVisible"
      title="《隐私协议》"
      width="90%"
      confirmButtonColor="#3586ff"
      messageAlign="left"
    >
      <div>
        <div class="protocolBox" v-html="privacyTxt"></div>
      </div>
    </van-dialog>
  </div>
</template>
<script>
import Vue from "vue";
import { Toast } from "vant";
Vue.use(Toast);
export default {
  data() {
    return {
      checked: false,
      dialogVisible: false,
      protocolTxt: "",
      privacyTxt: "",
      privacyDialogVisible: false
    };
  },
  created() {
    this.getProtocol();
  },
  methods: {
    bindingWeChat() {
      if (this.checked) {
        this.WeChat.getCode("weChatLogin");
      } else {
        Toast("请同意用户协议与隐私协议后再进行登录");
        return false;
      }
    },
    getProtocol() {
      var that = this;
      that.MG.resource
        .getItem({
          path: "tourism_protocol",
          fields: {
            tourism_content: []
          }
        })
        .then(res => {
          try {
            const data = res.datas.find(
              e => e.refCode == "tourism_userRegistrationAgreement"
            );
            const privacyTxtData = res.datas.find(
              e => e.refCode == "tourism_privacyPolicy"
            );
            this.protocolTxt = data ? data.tourism_content : "暂无协议";
            this.privacyTxt = privacyTxtData
              ? privacyTxtData.tourism_content
              : "暂无协议";
          } catch (error) {
            this.protocolTxt = "暂无协议";
            this.privacyTxt = "暂无协议";
          }
        });
    }
  }
};
</script>
<style scoped>
.box {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: 20px;
  color: #333;
  text-align: center;
  padding: 0 20px;
}
.title {
  font-size: 30px;
  font-weight: bold;
  margin-bottom: 20px;
  margin-top: 230px;
}
.content {
  margin-top: 130px;
  font-size: 16px;
}
.btn {
  width: 300px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  background-color: #409eff;
  color: #fff;
  border-radius: 20px;
  margin-top: 80px;
  cursor: pointer;
  font-size: 18px;
}
.checkbox {
  display: flex;
  justify-content: center;
  margin-top: 30px;
  font-size: 14px;
}
.protocolBox {
  height: 60vh;
  padding: 10px 20px;
  box-sizing: border-box;
  overflow: auto;
}
</style>
src/views/index/index.vue
New file
@@ -0,0 +1,687 @@
<template>
  <!-- é¦–页 -->
  <div class="home">
    <!-- <back-header :arrow="false"></back-header> -->
    <div class="home-header">
      <!-- æœç´¢å’Œè½®æ’­ -->
      <div>
        <div class="selcet">
          <img src="@/assets/images/home/logo_2.png" alt class="selectImg" />
          <van-search
            placeholder="请输入要搜索的图书"
            shape="round"
            v-model="searchAllWords"
            @search="select"
          />
        </div>
        <!-- lunbo -->
        <div
          class="common"
          v-if="educate.swipePic && educate.swipePic.length > 0"
        >
          <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
            <!--   -->
            <van-swipe-item
              v-for="(item, index) in educate.swipePic"
              :key="index"
            >
              <div
                :style="{
                  background: 'url(' + item.icon + ') no-repeat 100%,center'
                }"
                alt
                class="banner"
              />
            </van-swipe-item>
          </van-swipe>
        </div>
        <div class="common" v-else>
          <van-loading type="spinner" color="#1989fa" />
        </div>
      </div>
      <!-- è½®æ’­å›¾ä¸‹æ–¹ -->
      <div class="circle">
        <div
          v-for="(item, index) in educate.circles"
          :key="index"
          @click="goApply(item.url, item.circleName)"
          class="circles_one"
          :class="item.itemN"
        >
          <img :src="item.img" alt />
          <div class="circleName">{{ item.circleName }}</div>
        </div>
      </div>
    </div>
    <div class="line"></div>
    <!--推荐教材 -->
    <div class="popularRecommend padding">
      <div class="rowSpan">
        <div class="title">推荐教材</div>
        <div class="more" @click="showMore()">更多 +</div>
      </div>
      <div
        class="flexWrap"
        v-if="!bookRecommendLoading && bookRecommendList.length > 0"
      >
        <div
          class="displayColumn"
          v-for="(item, index) in bookRecommendList"
          :key="index"
          @click="goBookDetail(item)"
        >
          <div class="imgBox">
            <img :src="item.icon" alt class="autoImg" />
          </div>
          <div class="bookInfo">
            <div class="title" :title="item.name">
              {{ item.name }}
            </div>
            <div class="author" :title="item.tourism_author">
              {{ item.tourism_author }}
            </div>
            <div class="priceBox">
              <span v-if="item.price > 0" class="price">{{
                "Â¥" + tool.toDecimal2(item.price)
              }}</span>
              <span class="freePrice" v-if="item.price == 0">免费</span>
              <span class="oldPrice" v-if="item.oldPrice">{{
                "Â¥" + tool.toDecimal2(item.oldPrice)
              }}</span>
            </div>
          </div>
        </div>
      </div>
      <div v-else-if="bookRecommendLoading" class="loadingBox">
        <van-loading type="spinner" color="#1989fa" />
      </div>
      <div v-else class="noData">暂无数据</div>
    </div>
    <div class="line"></div>
    <div class="textBookDescription" v-if="videoInfo">
      <div class="titleBox">
        <div class="title">{{ videoInfo.name }}</div>
        <div class="operate" @click="getManual()">
          <img src="@/assets/images/home/ce_icon.png" alt class="" />
          <span>操作手册</span>
        </div>
      </div>
      <div class="flex descriptionBox">
        <div class="videoBox">
          <video
            :src="showDataVod"
            class="videoPlayer"
            controls
            controlslist="nodownload"
            :poster="videoInfo.icon"
            @ended="handleVideoEnded"
            ref="videoPlayer"
          ></video>
        </div>
        <div class="descriptionCon">
          <div class="con" v-html="videoInfo.tourism_content"></div>
        </div>
      </div>
    </div>
    <div class="line"></div>
    <div class="listBox padding">
      <div class="rowSpan">
        <div class="title">合作院校</div>
        <div class="more" @click="openBtn('false')" v-if="isOpen">更多 +</div>
        <div class="more" @click="openBtn('true')" v-else>收起 -</div>
      </div>
      <div class="partnerList">
        <div
          :class="isOpen ? 'list' : 'list1'"
          v-if="!partnersLoading && partnersList.length > 0"
        >
          <div
            class="partnerItem"
            v-for="(item, index) in partnersList"
            :key="index"
          >
            <div class="imgBox">
              <img class="autoImg" :src="item.icon" />
            </div>
            <div class="bookInfo">
              <div class="title" :title="item.name">
                {{ item.name }}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <Footer />
  </div>
</template>
<script>
import Vue from "vue";
import Footer from "@/components/footer/footer";
import { mapState } from "vuex";
import { Loading, Dialog } from "vant";
import toolClass from "@/assets/js/toolClass";
import myConfig from "@/assets/js/config";
Vue.use(Loading);
Vue.use(Dialog);
export default {
  name: "home",
  data() {
    return {
      searchAllWords: "",
      educate: {
        title: "数字教材",
        // è½®æ’­å›¾ç‰‡
        swipePic: [],
        circles: [
          {
            img: require("@/assets/images/home/guanwang.png"),
            circleName: "官网主页",
            url: "https://www.tepcb.com/mobile/website/",
            itemN: "circle-one"
          },
          {
            img: require("@/assets/images/home/shuzijiaocai.png"),
            circleName: "数字教材",
            url: "/bookList",
            itemN: "circle-two"
          },
          {
            img: require("@/assets/images/home/jiaoshirenzheng.png"),
            circleName: "教师认证",
            url: "/teacherCertificate",
            itemN: "circle-three"
          }
        ]
      },
      bookRecommendList: [],
      bookRecommendLoading: true,
      videoInfo: {},
      showDataVod: "",
      partnersList: [],
      partnersLoading: false,
      isOpen: true,
      token: ""
    };
  },
  created() {
    var that = this;
    var accToken = toolClass.getCookie(myConfig.tokenKey);
    that.token = accToken;
    if (that.token) {
      this.getUserRoleMine();
    }
    that.getBanner();
    that.getBookRecommendList();
    that.getVideoInfo();
    that.getPartnersList();
  },
  computed: {
    ...mapState(["userInfo"])
  },
  methods: {
    getUserRoleMine() {
      let that = this;
      that.MG.identity.getCurrentAppUser().then(res => {
        if (res) {
          that.userId = res.userId;
          let teacherRole = res.roleLinks.find(
            item => item.role?.refCode == "teacher"
          );
          let teacherInfo = res.infoList.find(
            item => item.type == "teacherInfo"
          );
          let wechatInfo = res.infoList.find(item => item.type == "WeChat");
          let studentInfo = res.infoList.find(
            item => item.type == "basicInfo" || item.type == "Default"
          );
          let phoneInfo = res.secretList.find(
            item => item.type == "MobilePhone"
          );
          if (teacherRole && teacherInfo) {
            this.$store.dispatch("setUserInfo", {
              ...teacherInfo,
              phoneNumber: phoneInfo?.credential,
              icon: wechatInfo?.icon,
              role: "Teacher",
              roleId: teacherRole.role.id
            });
          } else if (wechatInfo) {
            this.$store.dispatch("setUserInfo", {
              ...wechatInfo,
              phoneNumber: phoneInfo?.credential,
              role: "Student"
            });
          } else if (studentInfo) {
            this.$store.dispatch("setUserInfo", {
              ...studentInfo,
              icon: wechatInfo?.icon,
              phoneNumber: phoneInfo?.credential,
              role: "Student"
            });
          }
        }
      });
    },
    // èŽ·å–è½®æ’­å›¾
    getBanner() {
      this.MG.resource
        .getItem({
          path: "tourism_digitalTextbook\\tourism_digitalBannerMobile"
        })
        .then(res => {
          this.educate.swipePic = res.datas;
          console.log(res.datas);
        });
    },
    // è·³è½¬æœç´¢
    select() {
      this.$router.push({
        path: "/bookList",
        query: {
          searchAllWords: this.searchAllWords
        }
      });
    },
    //推荐教材
    getBookRecommendList() {
      this.bookRecommendList = [];
      this.bookRecommendLoading = true;
      // å¤„理搜索
      this.MG.store
        .getProductList({
          storeInfo: this.config.digitalTextBooksGoodsStore,
          path:
            this.config.digitalTextBooksGoodsStore +
            "\\" +
            this.config.refCodes.textBooksStore.textBookRecommendation,
          queryType: "*",
          paging: {
            start: 0,
            size: 5
          },
          fields: {
            tourism_author: [],
            tourism_publicationDate: [],
            viewCount: []
          },
          sort: {
            type: "Desc",
            field: "CreateDate"
          },
          coverSize: {
            height: 180
          }
        })
        .then(res => {
          this.bookRecommendList = res.datas;
          this.bookRecommendLoading = false;
        });
    },
    // åŽ»è¯¦æƒ…
    goBookDetail(data) {
      this.$router.push({
        path: "/bookDetail",
        query: {
          id: data.id
        }
      });
    },
    // è½®æ’­å›¾ä¸‹æ–¹ åˆ—表跳转页面
    goApply(url, name) {
      console.log(name);
      if (name === "官网主页") {
        window.open(url);
      } else {
        this.$router.push({
          path: url
        });
      }
    },
    showMore() {
      this.$router.push({
        path: "/bookList"
      });
    },
    //视频说明
    getVideoInfo() {
      this.MG.resource
        .getItem({
          path: "tourism_digitalTextbook\\tourism_digitalRecommendedTextbooks",
          fields: {
            tourism_file: [],
            tourism_content: []
          }
        })
        .then(res => {
          if (res.datas.length > 0) {
            res.datas.forEach(element => {
              if (
                element.refCode ==
                "tourism_digitalRecommendedTextbooksMobile"
              ) {
                this.videoInfo = element;
                this.showDataVod =
                  this.config.requestCtx +
                  "/file/api/ApiDownload?md5=" +
                  element.tourism_file;
              }
            });
          }
        });
    },
    handleVideoEnded() {
      const videoPlayer = this.$refs.videoPlayer;
      if (videoPlayer) {
        videoPlayer.currentTime = 0;
        videoPlayer.load(); // è¿™ä¼šé‡æ–°åŠ è½½è§†é¢‘å¹¶æ˜¾ç¤ºæµ·æŠ¥å›¾
      }
    },
    getManual() {
      this.$router.push({
        path: "/manual"
      });
    },
    //合作院校
    getPartnersList() {
      this.partnersLoading = true;
      this.MG.resource
        .getItem({
          path: "tourism_digitalTextbook\\tourism_digitalPartners",
          fields: {}
        })
        .then(res => {
          this.partnersList = res.datas;
          this.partnersLoading = false;
        });
    },
    openBtn(state) {
      if (state === "true") {
        this.isOpen = true;
      } else {
        this.isOpen = false;
      }
    }
  },
  components: {
    Footer
  }
};
</script>
<style scoped>
.home {
  padding-bottom: 52px;
}
.home-header {
  background: url("../../assets/images/home/home-bg.png");
  background-size: 100% 100%;
}
.selcet {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 15px;
}
.selectImg {
  display: inline-block;
  right: 14px;
  width: 35%;
}
.banner {
  box-sizing: border-box;
  width: 100%;
  height: 150px;
  background-size: cover !important;
  border-radius: 4px;
}
/* è½®æ’­å›¾ä¸‹æ–¹ */
.circle {
  display: flex;
  margin-top: 20px;
  justify-content: space-between;
  padding: 0 15px;
  background: none;
}
.circleName {
  /* font-size: 12px; */
  font-weight: bold;
  margin-left: 5px;
}
.circle img {
  width: 32px;
  height: 32px;
  display: inline-block;
}
.circles_one {
  margin-bottom: 20px;
  display: flex;
  /* flex-flow: column; */
  align-items: center;
  justify-content: center;
  border-radius: 5px;
  padding: 8px 0;
  width: calc(100% / 3 - 10px);
}
.circle-one {
  background: #ffefe6;
  border: 1px solid #ffefe6;
}
.circle-two {
  background: #e0f5ff;
  border: 1px solid #e0f5ff;
}
.circle-three {
  background: #eee3ff;
  border: 1px solid #eee3ff;
}
.line {
  height: 5px;
  background-color: rgb(240, 240, 240);
}
/* å…¬å…± */
.padding {
  padding: 0 15px;
  font-size: 14px;
  color: #333;
}
.more {
  color: #999;
}
.more:active {
  color: #3586ff;
}
.rowSpan {
  display: flex;
  justify-content: space-between;
}
.rowSpan .title {
  font-size: 18px;
  font-weight: bold;
  color: #000;
  margin: 10px 0;
  font-family: "黑体";
}
.rowSpan .more {
  display: flex;
  align-items: center;
}
.flexWrap {
  display: flex;
  flex-wrap: wrap;
  min-height: 147px;
  margin-top: 23px;
}
.displayColumn {
  width: calc(100% / 3 - 15px);
  margin: 7px;
}
.displayColumn .imgBox {
  width: 100%;
  height: 140px;
  margin: 0 auto;
  border-radius: 5px;
  transition: transform 0.3s ease;
  transform: scale(1);
  overflow: hidden;
  box-shadow: 0 3px 6px 1px #00000029;
}
.displayColumn .bookInfo {
  margin: 0;
  overflow: hidden;
}
.displayColumn .bookInfo .title {
  color: #333;
  font-weight: 600;
  margin-top: 10px;
  margin-bottom: 8px;
  font-size: 14px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
.displayColumn .bookInfo .author {
  font-size: 14px;
  color: #999;
  margin: 5px 0;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
.price {
  color: #ef1b3a;
  font-size: 16px;
  font-weight: bold;
}
.freePrice {
  color: #0bc266;
  font-size: 16px;
  font-weight: bold;
}
.oldPrice {
  text-decoration: line-through;
  color: #999 !important;
  margin-left: 5px;
  font-size: 13px;
}
.noData {
  font-size: 16px;
  font-weight: bold;
  color: #999;
  min-height: 147px;
  line-height: 147px;
  /* margin: 0 auto; */
  text-align: center;
}
.textBookDescription {
  padding: 15px;
}
.textBookDescription .titleBox {
  display: flex;
  justify-content: space-between;
}
.titleBox .operate {
  height: 34px;
  width: 105px;
  background: url("../../assets/images/home/shouce.png");
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}
.titleBox .operate img {
  margin-right: 6px;
}
.descriptionBox {
  justify-content: space-between;
  margin-top: 20px;
}
.videoBox {
  width: 45%;
}
.videoPlayer {
  width: 100%;
  border-radius: 10px;
}
.descriptionCon {
  width: 55%;
  padding-left: 10px;
}
.partnerList {
  margin-top: 10px;
  overflow: hidden;
  min-height: 180px;
  flex-wrap: wrap;
}
.list {
  height: 160px;
}
.list1 {
  min-height: 160px;
}
.partnerItem {
  width: calc(100% / 4 - 10px);
  display: block;
  box-sizing: border-box;
}
.partnerItem .imgBox {
  position: relative;
  width: 100%;
  height: 100px;
}
.partnerItem .bookInfo {
  margin: 0;
  overflow: hidden;
  text-align: center;
}
.partnerItem .bookInfo .title {
  color: #333;
  margin: 15px 0;
  /* white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden; */
}
</style>
<style>
/* æœç´¢ */
.van-search {
  background: none !important;
  padding: 10px 0;
  width: 60%;
}
.selcet .van-search__content {
  background-color: #fff;
  border-radius: 4px;
}
/* è½®æ’­ */
.my-swipe .van-swipe-item {
  color: #fff;
  font-size: 20px;
  text-align: center;
}
</style>
src/views/index/manual.vue
New file
@@ -0,0 +1,58 @@
<template>
 <div class="manualPage">
    <van-nav-bar
      title="操作手册"
      left-text
      left-arrow
      @click-left="onClickLeft"
    />
    <div style="padding-top: 20px;" v-html="manualCon"></div>
  </div>
</template>
<script>
export default {
  name: "login",
  data() {
    return {
      manualCon: "",
    };
  },
  created() {
    this.getOperating()
  },
  methods: {
     onClickLeft() {
      this.$router.go(-1);
    },
    //操作手册
    getOperating() {
      this.MG.resource
        .getItem({
          path: "tourism_digitalTextbook\\tourism_digitalOperatingManual",
          fields: {
            tourism_content: [],
          },
        })
        .then((res) => {
          if (res.datas.length > 0) {
            this.manualCon = res.datas[0].tourism_content;
          }
        });
    },
  }
};
</script>
<style scoped>
.manualPage{
    width:100%;
    height:100%;
    padding: 15px;
}
</style>
src/views/pay/pay.vue
New file
@@ -0,0 +1,1204 @@
<template>
  <div class="payOrderBox size12">
    <div class="bookDetailBox">
      <div class="payTitle shop">商品信息</div>
      <div class="detailInfoBox" v-if="bookInfos.length > 0 && !loading">
        <div
          class="classList"
          v-for="(bookInfo, index) in bookInfos"
          :key="index"
        >
          <div class="imgBox">
            <img :src="bookInfo.productInfo.icon" alt="" />
          </div>
          <div class="txtBox">
            <p class="title">
              {{ bookInfo.productInfo.name }}
            </p>
            <span class="price" v-if="bookInfo.orderSaleMethod.price > 0"
              >商品价格:¥{{
                tool.toDecimal2(bookInfo.orderSaleMethod.price)
              }}</span
            >
            <span class="price free" v-else>免费</span>
            <span class="price2" v-if="bookInfo.orderSaleMethod.price > 0"
              >实付价格:¥{{ tool.toDecimal2(bookInfo.payPrice) }}</span
            >
            <span
              v-if="bookInfo.orderSaleMethod.price > 0"
              class="priceSale"
              @click="getProductPromtecode(bookInfo)"
            >
              {{
                bookInfo.productPromoteCode
                  ? "已优惠" + bookInfo.productPromoteCode.value + "元"
                  : "选券"
              }}
              <van-icon name="arrow" />
            </span>
          </div>
        </div>
      </div>
      <!-- å•†å“ä¼˜æƒ åˆ¸ -->
      <van-popup
        v-model="showCouponValue"
        position="bottom"
        :style="{ height: '73%' }"
      >
        <div class="couponListWrap">
          <div class="couponListTitle">
            <div>
              <p class="txt">优惠券</p>
              <p class="tips">
                è®¢å•优惠券和商品优惠券可叠加使用,且每种类型最多选择一张
              </p>
            </div>
            <van-icon @click="showCouponValue = false" name="cross" size="16" />
          </div>
          <div v-if="couponListbyproduct.length > 0">
          <div
            class="couponList"
            v-for="(item, index) in couponListbyproduct"
            :key="index"
          >
            <div
              :class="
                item.use ? 'couponInfo yellowBg' : 'couponInfo yellowBg grayBg'
              "
              @click="selectCoupon(item, bookInfos, index)"
            >
              <div class="priceBox yellowBor">
                <div class="">
                  <span v-if="item.promoteCode.useType != 'Discount'">Â¥</span>
                  <span
                    class="priceFont"
                    v-if="item.promoteCode && item.promoteCode.value"
                    >{{
                      item.promoteCode.useType == "Discount"
                        ? tool.toDecimal1(item.promoteCode.value)
                        : item.promoteCode.value
                    }}
                    <span
                      v-if="item.promoteCode.useType == 'Discount'"
                      style="font-size: 14px;"
                      >折</span
                    ></span
                  >
                </div>
                <div class="couponType">
                  <span
                    v-if="
                      item.promoteCode && item.promoteCode.type == 'Product'
                    "
                    >商品券</span
                  >
                  <span v-else>订单券</span>
                </div>
              </div>
              <div class="conditionBox yellow">
                <div
                  class="condition"
                  v-if="item.promoteCode && item.promoteCode.conditionValue"
                >
                  æ»¡{{ item.promoteCode.conditionValue }}减{{
                    item.promoteCode.value
                  }}
                </div>
                <div
                  v-if="
                    item.promoteCode &&
                      item.promoteCode.value &&
                      item.promoteCode.useType == 'Discount'
                  "
                  class="condition"
                >
                  {{ tool.toDecimal1(item.promoteCode.value) }}折优惠券
                </div>
                <div
                  v-if="
                    item.promoteCode &&
                      item.promoteCode.value &&
                      item.promoteCode.useType == 'Subtraction'
                  "
                  class="condition"
                >
                  ç«‹å‡{{ item.promoteCode.value }}元
                </div>
                <div class="time">
                  {{ item.createDate ? item.createDate.split("T")[0] : "" }}
                  -
                  {{ item.endDate ? item.endDate.split("T")[0] : "" }}
                </div>
              </div>
              <div v-if="item.use" class="btnBox">
                <span v-if="item.select">
                  <van-icon name="checked" color="#f24B3B" size="24" />
                </span>
                <span v-else>
                  <van-icon name="checked" color="#999999" size="24" />
                </span>
              </div>
              <div v-else><span style="width: 24px"></span></div>
            </div>
          </div>
          </div>
          <div v-else><van-empty description="暂无数据" image-size="100" /></div>
        </div>
      </van-popup>
      <div class="noData" v-if="loading">
        <van-loading></van-loading>
      </div>
      <div class="noData" v-if="bookInfos.length == 0 && !loading">
        <van-empty
          class="custom-image"
          image="https://img.yzcdn.cn/vant/custom-empty-image.png"
          description="暂无商品"
        />
      </div>
    </div>
    <div class="spline"></div>
    <div class="payWayBox">
      <div class="payTitle" style="padding-top: 0">支付方式</div>
      <div class="wxBox">
        <img src="@/assets/images/wx-icon.png" alt="" />
        <span>微信支付</span>
      </div>
    </div>
    <div class="spline"></div>
    <div class="payWayBox" v-if="payPrice > 0">
      <div class="payTitle couponBoxTitle">
        <span>订单优惠券</span>
        <span class="selectNum">已选{{ selectNum }}å¼ </span>
      </div>
      <div class="wxBox couponBox" @click="openCoupon">
        <span>已优惠 {{ selectPrice }} å…ƒ</span>
        <span>
          <van-icon name="arrow" />
        </span>
      </div>
    </div>
    <div class="spline"></div>
    <div class="orderDetailBox">
      <div class="payTitle" style="padding-top: 0">订单金额明细</div>
      <div class="detailItemBox">
        <span class="name">商品总价</span>
        <span class="value">Â¥ {{ tool.toDecimal2(orderInfo.totalPrice) }}</span>
      </div>
      <div
        class="detailItemBox"
        style="color: #dd0000;"
        v-if="bookInfos.findIndex(item => item.productPromoteCode) > -1"
      >
        <span class="name" style="font-weight: initial;">商品优惠</span>
      </div>
      <div
        v-for="(bookInfo, index) in bookInfos"
        :key="index"
        style="color: #dd0000;"
      >
        <div class="detailItemBox" v-if="bookInfo.productPromoteCode">
          <span class="name" style="margin-left: 20px;font-weight: initial;">{{
            bookInfo.productPromoteCode.name
          }}</span>
          <span class="value" style="font-weight: initial;"
            >Â¥ {{ tool.toDecimal2(bookInfo.productPromoteCode.value) }}</span
          >
        </div>
      </div>
      <div
        class="detailItemBox"
        style="color: #dd0000;"
        v-if="orderInfo.orderPromoteCode"
      >
        <span class="name" style="font-weight: initial;">订单优惠</span>
      </div>
      <div
        class="detailItemBox"
        style="color: #dd0000;"
        v-if="orderInfo.orderPromoteCode"
      >
        <span class="name" style="margin-left: 20px;font-weight: normal;">{{
          orderInfo.orderPromoteCode.name
        }}</span>
        <span class="value" style="font-weight: initial;"
          >Â¥ {{ selectPrice }}</span
        >
      </div>
      <div class="detailItemBox" style="color: #dd0000;">
        <span class="name">共减</span>
        <span class="value"
          >Â¥
          {{ tool.toDecimal2(orderInfo.totalPrice - orderInfo.payPrice) }}</span
        >
      </div>
      <div class="detailItemBox">
        <span class="name">合计</span>
        <span class="value">Â¥ {{ tool.toDecimal2(payPrice) }}</span>
      </div>
    </div>
    <div class="payBox" v-if="showBtnBox">
      <div class="countBtn">
        <p>
          <span>Â¥ {{ tool.toDecimal2(payPrice) }} å…ƒ</span>
        </p>
        <!-- <p style="font-size: 12px">
          å…±å‡
          <span style="font-size: 12px"
            >Â¥
            {{ tool.toDecimal2(orderInfo.totalPrice - orderInfo.payPrice) }}
            å…ƒ</span
          >
        </p> -->
      </div>
      <div class="btn buyBtn" @click="getOrderCreate">
        {{ payPrice > 0 ? "立即支付" : "立即领取" }}
      </div>
    </div>
    <!-- ä¼˜æƒ åˆ¸å¼¹å±‚ -->
    <van-popup
      v-model="showCoupon"
      position="bottom"
      :style="{ height: '73%' }"
    >
      <div class="couponListWrap">
        <div class="couponListTitle">
          <div>
            <p class="txt">优惠券</p>
            <p class="tips">
              è®¢å•优惠券和商品优惠券可叠加使用,且每种类型最多选择一张
            </p>
          </div>
          <van-icon @click="showCoupon = false" name="cross" size="16" />
        </div>
        <div v-if="couponList.length > 0">
        <div
          class="couponList"
          v-for="(item, index) in couponList"
          :key="index"
        >
          <div
            :class="
              item.use ? 'couponInfo yellowBg' : 'couponInfo yellowBg grayBg'
            "
            @click="selectCoupon(item)"
          >
            <div class="priceBox yellowBor">
              <div class="price">
                <span>Â¥</span>
                <span
                  class="priceFont"
                  v-if="item.promoteCode && item.promoteCode.value"
                  >{{
                    item.promoteCode.useType == "Discount"
                      ? tool.toDecimal1(item.promoteCode.value)
                      : item.promoteCode.value
                  }}</span
                >
              </div>
              <div class="couponType">
                <span
                  v-if="item.promoteCode && item.promoteCode.type == 'Product'"
                  >商品券</span
                >
                <span v-else>订单券</span>
              </div>
            </div>
            <div class="conditionBox yellow">
              <div
                class="condition"
                v-if="item.promoteCode && item.promoteCode.conditionValue"
              >
                æ»¡{{ item.promoteCode.conditionValue }}减{{
                  item.promoteCode.value
                }}
              </div>
              <div
                v-if="
                  item.promoteCode &&
                    item.promoteCode.value &&
                    item.promoteCode.useType == 'Discount'
                "
                class="condition"
              >
                {{ tool.toDecimal1(item.promoteCode.value) }}折优惠券
              </div>
              <div
                v-if="
                  item.promoteCode &&
                    item.promoteCode.value &&
                    item.promoteCode.useType == 'Subtraction'
                "
                class="condition"
              >
                ç«‹å‡{{ item.promoteCode.value }}元
              </div>
              <div class="time">
                {{ item.createDate ? item.createDate.split("T")[0] : "" }}
                -
                {{ item.endDate ? item.endDate.split("T")[0] : "" }}
              </div>
            </div>
            <div v-if="item.use" class="btnBox">
              <span v-if="item.select">
                <van-icon name="checked" color="#f24B3B" size="24" />
              </span>
              <span v-else>
                <van-icon name="checked" color="#999999" size="24" />
              </span>
            </div>
            <div v-else><span style="width: 24px"></span></div>
          </div>
        </div>
        </div>
        <div v-else><van-empty description="暂无数据" image-size="100" /></div>
      </div>
    </van-popup>
  </div>
</template>
<script>
import Vue from "vue";
import { Icon, Toast, Dialog, Field, Popup, Loading, Empty } from "vant";
import { getPublicImage } from "@/assets/js/middleGround/tool";
Vue.use(Icon);
Vue.use(Toast);
Vue.use(Field);
Vue.use(Popup);
Vue.use(Loading);
Vue.use(Empty);
Vue.use(Dialog);
export default {
  name: "pay",
  data() {
    return {
      loading: false,
      //商品id
      productId: this.$route.query.productId ? this.$route.query.productId : 0,
      productIds: [],
      productPrice: 0,
      //优惠券弹层
      showCoupon: false,
      // ä¼˜æƒ åˆ¸åˆ—表
      couponList: [],
      couponListbyproduct: [],
      // å®žé™…付款金额
      payPrice: 0,
      //已选择优惠券张数
      selectNum: 0,
      //已优惠金额
      selectPrice: 0,
      orderSaleMethodsId: 0,
      bookInfos: [],
      showBtnBox: false,
      showCouponValue: false,
      oldRoute: this.$route.query.fromPath ? this.$route.query.fromPath : -1,
      orderInfo: "",
      goBack: false,
      paymentSuccess: false
    };
  },
  created() {
    this.getDetail();
  },
  beforeRouteLeave(to, from, next) {
    if (this.paymentSuccess) {
      next();
    } else {
      next(false);
      if (this.goBack) {
        next();
      } else {
        // å¦‚果是退出就确定是否要退出
        setTimeout(() => {
          Dialog.confirm({
            title: "提示",
            message: "是否放弃本次支付?",
            confirmButtonText: "继续支付",
            cancelButtonText: "放弃支付"
          })
            .then(res => {
              this.getDetail();
            })
            .catch(() => {
              next();
            });
        }, 200);
      }
    }
  },
  methods: {
    //获取详情
    getDetail() {
      let that = this;
      // that.loading = true;
      if (that.$route.query.orderNum) {
        const data = { orderNum: that.$route.query.orderNum };
        that.MG.store.getOrderByOrderNum(data).then(res => {
          that.orderInfo = res;
          that.payPrice = res.payPrice;
          that.loading = false;
          that.handleBookInfo(res.saleMethodLinks);
          that.handleOrderPromoteCodeInfo();
        });
      } else {
        that.MG.store
          .getProductList({
            queryType: "*",
            handelEBooK: true,
            fields: {
              bookshelf_picture: [],
              bookshelf_author: [],
              bookshelf_publisher: [],
              bookshelf_isbn: [],
              bookshelf_paperLink: [],
              bookshelf_brief: [],
              bookshelf_catalog: [],
              bookshelf_pdf: [],
              bookshelf_probationPage: [],
              "Id=": that.$route.query.productIds
            }
          })
          .then(res => {
            let list = res.datas;
            that.handleBookInfo(list);
            // that.loading = false;
          });
      }
    },
    // å¤„理商品信息
    handleBookInfo(datas) {
      var that = this;
      for (let i = 0; i < datas.length; i++) {
        const item = datas[i];
        if (that.$route.query.orderNum) {
          if (item.orderSaleMethod.type == "defaultSaleMethod") {
            item.productInfo = item.orderSaleMethod.product;
            item.productInfo.icon = getPublicImage(
              item.orderSaleMethod.product.icon,
              120
            );
          } else {
            item.productInfo = item.orderSaleMethod.product;
            item.productInfo.saleMethodName = item.orderSaleMethod.name;
            item.productInfo.icon = getPublicImage(
              item.orderSaleMethod.product.icon,
              120
            );
          }
          that.linkId = item.id;
          //图书信息
          // that.bookInfos.push(item);
        } else {
          item.productInfo = item.datas;
          item.productInfo.name = item.datas.Name;
          item.productInfo.icon = item.datas.Icon;
          item.payPrice = item.price;
          that.productPrice = item.price;
          // that.bookInfos.push(item);
        }
        that.showBtnBox = true;
      }
      that.bookInfos = datas;
      //初始化微信sdk
      // that.init_WeiXinSdk(that);
    },
    // å¤„理订单优惠券
    handleOrderPromoteCodeInfo() {
      if (this.orderInfo.orderPromoteCode) {
        this.selectNum = 1;
        if (this.orderInfo.orderPromoteCode.useType == "Discount") {
          let productPayPrice = 0;
          for (let i = 0; i < this.orderInfo.saleMethodLinks.length; i++) {
            const item = this.orderInfo.saleMethodLinks[i];
            productPayPrice += item.payPrice;
          }
          this.selectPrice = (
            productPayPrice *
            (1 - this.orderInfo.orderPromoteCode.value)
          ).toFixed(2);
        } else {
          this.selectPrice = this.orderInfo.orderPromoteCode.value;
        }
      }
    },
    //获取我的可用订单优惠券列表
    getCouponList() {
      let that = this;
      const data = {
        orderNum: that.$route.query.orderNum
      };
      that.MG.store.getOrderPromoteCodeList(data).then(res => {
        //循环判断根据productId判断此商品是否可用
        for (let item of res) {
          if (that.orderInfo.orderPromoteCode) {
            // å·²ç»ä½¿ç”¨äº†ä¼˜æƒ åˆ¸
            if (item.id == that.orderInfo.orderPromoteCode.linkId) {
              item.select = true;
              that.$set(item, "use", true);
            } else {
              that.$set(item, "use", false);
            }
          } else {
            // æœªä½¿ç”¨ä¼˜æƒ åˆ¸
            if (item.promoteCode.conditionValue - 0 <= that.payPrice - 0) {
              that.$set(item, "use", true);
            } else {
              that.$set(item, "use", false);
            }
          }
        }
        that.couponList = res;
      });
    },
    // é€‰æ‹©ä¼˜æƒ åˆ¸ç‚¹å‡»
    getProductPromtecode(item) {
      this.couponListbyproduct = [];
      this.linkId = item.id;
      this.getCouponListbyProduct(item);
      this.showCouponValue = true;
    },
    //获取我的可用商品优惠券列表
    getCouponListbyProduct(ele) {
      let that = this;
      const data = {
        orderNum: that.$route.query.orderNum,
        orderSaleMethodLinkId: ele.id
      };
      that.MG.store.getSaleMethodPromoteCodeList(data).then(res => {
        for (let item of res) {
          if (ele.productPromoteCode) {
            // å·²ç»ä½¿ç”¨äº†ä¼˜æƒ åˆ¸
            if (item.id == ele.productPromoteCode.linkId) {
              item.select = true;
              that.$set(item, "use", true);
            } else {
              that.$set(item, "use", false);
            }
          } else {
            // æœªä½¿ç”¨ä¼˜æƒ åˆ¸
            if (item.promoteCode.conditionValue - 0 <= that.payPrice - 0) {
              that.$set(item, "use", true);
            } else {
              that.$set(item, "use", false);
            }
          }
        }
        that.couponListbyproduct = res;
      });
    },
    openCoupon() {
      if (this.createOrder) {
        Toast("订单已生成,请完成支付!");
      } else {
        this.couponList = [];
        this.getCouponList();
        this.showCoupon = true;
      }
    },
    // ç‚¹å‡»ç«‹å³æ”¯ä»˜ï¼Œåˆ›å»ºè®¢å•
    getOrderCreate() {
      var that = this;
      Toast.loading({ mask: false, duration: 0, message: "加载中..." });
      const data = {
        orderNum: that.$route.query.orderNum
      };
      that.MG.store.getOrderByOrderNum(data).then(res => {
        if (res && res.state != "WaitPay") {
          that.MG.store.confirmOrder(data).then(res => {
            if (res.orderNumber) {
              const resOrderNum = {
                orderNum: res.orderNumber
              };
              if (res.payPrice != 0) {
                that.MG.store.makeWeChatPay(resOrderNum).then(payRes => {
                  that.WeChat.pay(payRes, (success, msg) => {
                    Toast.clear();
                    if (success) {
                      Toast.success("支付成功");
                      this.paymentSuccess = true;
                      setTimeout(() => {
                        Dialog.confirm({
                          title: "提示",
                          message: "是否前往学习?",
                          confirmButtonText: "确认",
                          cancelButtonText: "取消"
                        })
                          .then(() => {
                            this.$router.push({
                              path: "/bookDetail",
                              query: {
                                id: this.$route.query.shopId
                              }
                            });
                          })
                          .catch(() => {
                            this.goBack = true;
                            this.$router.go(-1);
                          });
                      }, 1000);
                    } else {
                      if (msg) {
                        Toast.fail(msg);
                      } else {
                        Toast.fail("支付失败");
                      }
                    }
                  });
                });
              } else {
                Toast("领取成功");
                that.$router.go(-1);
              }
            }
          });
        } else {
          that.MG.store
            .makeWeChatPay({
              orderNum: that.$route.query.orderNum
            })
            .then(payRes => {
              that.WeChat.pay(payRes, (success, msg) => {
                Toast.clear();
                if (success) {
                  Toast.success("支付成功");
                  setTimeout(() => {
                    Dialog.confirm({
                      title: "提示",
                      message: "是否前往学习?",
                      confirmButtonText: "确认",
                      cancelButtonText: "取消"
                    })
                      .then(() => {
                        this.$router.push({ path: "/myTextBook" });
                      })
                      .catch(() => {
                        this.goBack = true;
                        this.$router.go(-1);
                      });
                  }, 1000);
                } else {
                  if (msg) {
                    Toast.fail(msg);
                  } else {
                    Toast.fail("支付失败");
                  }
                }
              });
            });
        }
      });
    },
    //点击优惠券
    selectCoupon(item, datas, i) {
      let that = this;
      //如果优惠券可以点击
      if (item.use) {
        //如果取消
        if (item.select) {
          that.$set(item, "select", false);
          //更新取消折扣后价格
          if (item.promoteCode.type == "Product") {
            that.updatePrice("");
            //将其他优惠券变回
            for (let citem of that.couponListbyproduct) {
              if (citem.promoteCode.useType == item.promoteCode.useType) {
                that.$set(citem, "use", true);
              }
            }
          } else {
            that.initNum(item);
            that.updateOrderPrice("");
            //将其他优惠券变回
            for (let citem of that.couponList) {
              if (citem.id != item.id) {
                that.$set(citem, "use", true);
              }
            }
          }
        } else {
          //如果选中
          // if (datas) {
          //   that.$set(datas[i], 'promoteCodeName', item.promoteCode.name);
          // }
          that.$set(item, "select", true);
          //更新折扣后价格
          if (item.promoteCode.type == "Product") {
            that.updatePrice(item);
            //将其他优惠券变灰
            for (let citem of that.couponListbyproduct) {
              if (citem.id != item.id) {
                that.$set(citem, "use", false);
              }
            }
          } else {
            that.initNum(item);
            that.updateOrderPrice(item);
            //将其他优惠券变灰
            for (let citem of that.couponList) {
              if (citem.id != item.id) {
                that.$set(citem, "use", false);
              }
            }
          }
        }
      } else {
        return false;
      }
    },
    //初始化优惠券选中张数,优惠价格及优惠后金额
    initNum(item) {
      let that = this;
      if (item.select) {
        that.selectNum = 1;
        if (item.promoteCode.useType == "Discount") {
          let productPayPrice = 0;
          for (let i = 0; i < that.bookInfos.length; i++) {
            const item = that.bookInfos[i];
            productPayPrice += item.payPrice;
          }
          that.selectPrice = (
            productPayPrice *
            (1 - item.promoteCode.value)
          ).toFixed(2);
        } else {
          that.selectPrice = item.promoteCode.value;
        }
      } else {
        that.selectNum = 0;
        that.selectPrice = 0;
      }
    },
    // æ›´æ–°å•†å“ä»·æ ¼ä¿¡æ¯
    updatePrice(item) {
      let that = this;
      let data = {};
      if (item == "") {
        data = {
          orderNum: that.$route.query.orderNum,
          saleMethodLinkId: that.linkId,
          userPromoteCodeId: 0
        };
      } else {
        data = {
          orderNum: that.$route.query.orderNum,
          saleMethodLinkId: that.linkId,
          userPromoteCodeId: item.id
        };
      }
      that.MG.store.updateSaleMethodPromoteCode(data).then(res => {
        if (res) {
          that.orderInfo = res;
          if (res.state == "Init") {
            for (let i = 0; i < this.bookInfos.length; i++) {
              const item = this.bookInfos[i];
              for (let j = 0; j < res.saleMethodLinks.length; j++) {
                const ele = res.saleMethodLinks[j];
                if (item.productInfo.id == ele.orderSaleMethod.product.id) {
                  item.payPrice = ele.payPrice;
                }
              }
            }
            that.getDetail();
            that.payPrice = res.payPrice;
          }
        } else {
          Toast("优惠券使用失败:" + res.errormsg);
        }
      });
    },
    // æ›´æ–°è®¢å•价格信息
    updateOrderPrice(item) {
      let that = this;
      let data = {};
      if (item == "") {
        data = {
          orderNum: that.$route.query.orderNum,
          userPromoteCodeId: 0
        };
      } else {
        data = {
          orderNum: that.$route.query.orderNum,
          userPromoteCodeId: item.id
        };
      }
      that.MG.store.updateOrderPromoteCode(data).then(res => {
        if (res) {
          that.orderInfo = res;
          if (res.state == "Init") {
            // that.getDetail();
            that.payPrice = res.payPrice;
          }
        } else {
          Toast("优惠券使用失败:" + res.errormsg);
        }
      });
    }
  }
};
</script>
<style scoped>
.payOrderBox {
  height: 100vh;
  background: #f1f1f1;
  display: flex;
  flex-direction: column;
}
.payWayBox {
  padding: 20px 20px;
  background: #fff;
}
.orderDetailBox {
  padding: 20px 20px 118px;
  background: #fff;
  flex: 1;
}
.payTitle {
  font-size: 16px;
  padding: 10px 0;
  border-bottom: 1px solid #ddd;
  color: #999;
}
.wxBox {
  display: flex;
  align-items: center;
  color: #333;
  margin-top: 15px;
}
.wxBox img {
  width: 20px;
  margin-right: 20px;
}
.couponBoxTitle {
  display: flex;
  align-items: center;
}
.selectNum {
  font-size: 14px;
  color: #fff;
  background: #f24b3b;
  padding: 3px 10px;
  border-radius: 10px;
  margin-left: 30px;
}
/* ä¹¦ç±ä¿¡æ¯ */
.detailInfoBox {
  padding: 15px 20px;
  background: #fff;
  max-height: 58vh;
  overflow-y: auto;
}
.shop {
  padding: 10px 20px;
}
.classList {
  display: flex;
  justify-content: flex-start;
  margin-top: 20px;
  /* min-height: 500px; */
}
.classList .imgBox {
  position: relative;
  margin-right: 40px;
  width: 105px;
  height: 140px;
  border-radius: 8px;
  box-shadow: #aba8a8 2px 4px 6px 0px;
}
.classList .imgBox img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
}
.classList .txtBox {
  position: relative;
  flex: 1;
}
.classList .title {
  margin-top: 20px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.classList .author {
  color: #999;
  margin-top: 20px;
}
.classList .count {
  color: #999;
  margin-top: 20px;
}
.classList .txtBox .price {
  position: absolute;
  left: 0;
  bottom: 40px;
  color: #f24b3b;
  font-weight: 600;
  /* font-size: 28px; */
}
.classList .txtBox .price2 {
  position: absolute;
  left: 0;
  bottom: 10px;
  color: #f24b3b;
  font-weight: 600;
  /* font-size: 28px; */
}
.classList .txtBox .priceSale {
  position: absolute;
  right: 0;
  bottom: 10px;
  color: #f24b3b;
  /* font-size: 28px; */
}
.classList .txtBox .free {
  color: #65e49e;
}
/* ä¼˜æƒ åˆ¸ */
.couponBox {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 15px;
  /* padding: 10px 0; */
  flex-wrap: wrap;
}
.couponListBox {
  display: flex;
  align-items: center;
  flex: 1;
  flex-wrap: wrap;
}
.couponListBox span {
  display: inline-block;
  padding: 8px 12px;
  color: #f24b3b;
  background: #ffe7e5;
  border-radius: 8px;
  margin-right: 12px;
  margin-bottom: 12px;
}
.getCouponBtn {
  display: flex;
  width: 78px;
  align-items: center;
  color: #f24b3b;
  margin-bottom: 12px;
}
/* ä¼˜æƒ åˆ¸åˆ—表 */
.couponListWrap {
  padding-bottom: 30px;
}
.couponList {
  margin-top: 18px;
  padding: 0 30px;
}
.couponInfo {
  display: flex;
  align-content: center;
  justify-content: space-between;
  background: #ffc8c8;
  border-radius: 12px 12px 0 0;
  padding: 30px;
}
.couponListWrap .priceBox {
  display: flex;
  width: 110px;
  align-items: center;
  flex-direction: column;
  color: #cd1e21;
  border-right: 1px dashed #cd1e21;
  padding: 5px 20px 0 0;
}
.priceFont {
  font-size: 40px;
  margin-left: 6px;
}
.conditionBox {
  display: flex;
  flex-direction: column;
  padding: 10px 30px 10px 0;
  color: #d78003;
}
.couponType {
  margin-top: 3px;
}
.condition {
  font-size: 28px;
  margin-bottom: 10px;
}
.btnBox {
  display: flex;
  align-items: center;
}
.getBtn {
  color: #d78003;
  background: #fff;
  padding: 10px 20px;
  border-radius: 6px;
}
.getBtn.selected {
  color: #fff;
  background: #d78003;
}
.couponInfo.yellowBg {
  background: #f9ebd3;
}
.couponListWrap .priceBox.yellowBor {
  color: #d78003;
  border-right: 1px dashed #d78003;
}
.couponListTitle {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  /* margin-top: 18px; */
  padding: 20px;
  /* padding-bottom: 18px; */
  border-bottom: 1px solid #ddd;
}
.couponListTitle .txt {
  font-size: 16px;
  color: #666;
}
.couponListTitle .tips {
  /* font-size: 24px; */
  /* width: 200px; */
  margin-top: 10px;
  color: #ff0000;
}
.grayBg {
  color: #666666 !important;
  background: #eeeeee;
}
.couponInfo.grayBg {
  background: #eeeeee;
}
.grayBg .priceBox.yellowBor {
  color: #666666;
  border-right: 1px dashed #666666;
}
.grayBg .conditionBox {
  color: #666666;
}
/* è´­ä¹°ç›¸å…³æŒ‰é’® */
.payBox {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 60px;
  padding: 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.payBox .btn {
  height: 40px;
  text-align: center;
  line-height: 40px;
  color: #ffffff;
  font-size: 16px;
  box-sizing: border-box;
}
.countBtn {
  background: #fff;
}
.payBox .countBtn {
  color: #666;
  flex: 1;
  text-align: left;
  /* text-indent: 35px; */
}
.payBox .countBtn span {
  font-size: 18px;
  font-weight: 800;
  color: #ff0000;
}
.payBox .buyBtn {
  width: 140px;
  background: #f24b3b;
}
.emptypayBox {
  position: relative;
  width: 100%;
  height: 98px;
  z-index: -1;
}
.detailItemBox {
  margin-top: 10px;
  /* padding: 0 0 0 20px; */
}
.detailItemBox .name {
  /* font-weight: bold; */
}
.detailItemBox .value {
  float: right;
  font-weight: bold;
}
</style>
src/views/personalCenter/about.vue
New file
@@ -0,0 +1,50 @@
<template>
  <div class="personInfor">
    <van-nav-bar
      title="教师客服"
      left-text
      left-arrow
      @click-left="onClickLeft"
    />
    <div style="padding-top: 20px;" v-html="data.tourism_content"></div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      data: ""
    };
  },
  mounted() {
    this.getList();
  },
  methods: {
    onClickLeft() {
      this.$router.go(-1);
    },
    getList() {
      const obj = {
        path: "tourism_about",
        fields: {
          refCode: [],
          tourism_content: []
        }
      };
      this.MG.resource.getItem(obj).then(res => {
        this.data = res.datas.find(item => item.refCode == "teacherService");
      });
    }
  }
};
</script>
<style scoped>
.personInfor {
  width: 100%;
  height: 100%;
  padding: 10px;
}
</style>
src/views/personalCenter/collection.vue
New file
@@ -0,0 +1,328 @@
<template>
  <div class="collect article">
    <van-nav-bar
      title="我的收藏"
      left-text=""
      right-text=""
      left-arrow
      @click-left="onClickLeft"
    />
    <!-- åˆ—表 -->
    <div class="listBox">
      <p class="line"></p>
      <van-pull-refresh
        class="refreshBox"
        v-model="refreshing"
        @refresh="onRefresh"
      >
        <van-list
          v-model="loading"
          :finished="finished"
          :finished-text="finishedtext"
          @load="onLoad"
        >
         <template slot="finished" name="finished">
            <div v-if="collectList.length == 0">
              <van-empty description="暂无数据" />
            </div>
            <div v-else>
              æ²¡æœ‰æ›´å¤šäº†
            </div>
          </template>
          <div class="collectBox">
            <div v-if="collectList && collectList.length > 0">
              <div
                v-for="(item, index) in collectList"
                :key="index"
                class="list"
                @click="gotoDetails(item, index)"
              >
                <img :src="item.icon" alt="" class="bookImg" />
                <div class="centerContent">
                  <p class="bookName">{{ item.name }}</p>
                  <p class="sameTitle">
                    ä½œè€… : {{ item.tourism_author || "-" }}
                  </p>
                  <p class="sameTitle">
                    å‡ºç‰ˆæ—¶é—´ :
                    {{
                      item.tourism_publicationDate
                        ? moment(
                            item.tourism_publicationDate.split(" ")[0]
                          ).format("YYYY-MM")
                        : "-"
                    }}
                  </p>
                  <p class="sameTitle">ISBN : {{ item.tourism_ISBN || "-" }}</p>
                  <p :class="item.price && item.price > 0 ? 'red' : 'free'">
                    <span>{{
                      item.price && item.price > 0
                        ? "ï¿¥" + tool.toDecimal2(item.price)
                        : "免费"
                    }}</span>
                  </p>
                </div>
                <div
                  class="starSection"
                  v-if="isCollect"
                  @click.stop="addCollect(item, index)"
                >
                  <div style="text-align: center">
                    <img
                      :src="collectIcon.selected_star"
                      alt=""
                      class="stars"
                    />
                  </div>
                  <p>已收藏</p>
                </div>
              </div>
            </div>
          </div>
        </van-list>
      </van-pull-refresh>
    </div>
    <Footer />
  </div>
</template>
<script>
import Footer from "@/components/footer/footer";
import { mapState } from "vuex";
import { Toast } from "vant";
// import List from "../bookList/bookList";
export default {
  data() {
    return {
      form: this.$route.query.formMine,
      refreshing: false,
      loading: false,
      finished: false,
      finishedtext: "",
      collectList: [],
      // æ”¶è—å›¾ç‰‡
      collectIcon: {
        star: require("@/assets/images/tab_collection.png"),
        selected_star: require("@/assets/images/tab_collection_pre.png")
      },
      //收藏状态
      isCollect: true,
      linkType: this.config.refCodes.LinkType.FavoriteTextBook,
      paginationData: {
        page: 1,
        limit: 10,
        totalCount: 0,
        totalPage: 0
      }
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    this.getCollectList();
  },
  methods: {
    onRefresh() {
      // æ¸…空列表数据
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.refreshing = false;
      this.getCollectList(true);
    },
    onLoad() {
      this.getCollectList();
    },
    //获取收藏列表
    getCollectList(isLode) {
      var that = this;
      this.finished = false;
      this.loading = true;
      this.finishedtext = "加载中";
      let { limit, page } = this.paginationData;
      this.MG.store
        .getProductList({
          queryType: "AppUserProductLink",
          linkType: this.linkType,
          handelEBooK: true,
          paging: {
            start: limit * page - limit,
            size: limit
          },
          fields: {
            tourism_author: [],
            tourism_paperPrice: [],
            tourism_ISBN: [],
            tourism_publicationDate: []
          }
        })
        .then(res => {
          if (isLode) {
            that.collectList = res.datas;
          } else {
            that.collectList = [...that.collectList, ...res.datas];
          }
          //分页
          that.paginationData.totalCount = res.total;
          if (res.total > that.paginationData.limit) {
            that.paginationData.totalPage = parseInt(
              res.total / that.paginationData.limit
            );
            if (res.total % that.paginationData.limit > 0) {
              that.paginationData.totalPage++;
            }
          } else {
            that.paginationData.totalPage = 1;
          }
          that.sum = Number(Math.ceil(res.total / this.paginationData.limit));
          // åŠ è½½çŠ¶æ€ç»“æŸ
          that.loading = false;
          that.refreshing = false;
          if (that.paginationData.page == this.sum) {
            that.finished = true;
            that.finishedtext = "没有更多了";
            return false;
          }
          if (that.collectList.length < 1) {
            that.loading = false;
            that.finished = true;
            that.finishedtext = "暂无数据";
          }
          that.paginationData.page++;
        });
    },
    addCollect(item, index) {
      var that = this;
      const data = {
        productIds: [item.id],
        linkType: that.linkType
      };
      that.MG.store.delProductLink(data).then(res => {
        if (res) {
          Toast.success("取消收藏成功");
          that.paginationData.page = 1;
          that.collectList.splice(index, 1);
        }
      });
    },
    gotoDetails(item) {
      this.$router.push({
        path: "/bookDetail",
        query: {
          shopId: item.id
        }
      });
    },
    onClickLeft() {
      var that = this;
      that.$router.go(-1);
    }
  },
  components: {
    Footer
  }
};
</script>
<style scoped>
.refreshBox {
  flex: 1;
  overflow: auto;
}
.collectBox {
  padding: 0 15px;
}
.collect {
  padding-bottom: 50px;
  height: 100%;
  box-sizing: border-box;
}
.line {
  height: 5px;
  background-color: #eee;
}
.listBox {
  padding-top: 40px;
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}
/* åˆ—表 */
.list {
  display: flex;
  position: relative;
  padding: 15px 0 15px 0;
  border-bottom: 1px solid #e8e8e8;
}
.bookImg {
  width: 75px;
  height: 100px;
  display: inline-block;
}
.centerContent {
  margin-left: 14px;
}
.starSection {
  min-width: 36px;
  text-align: center;
  position: absolute;
  right: 10px;
  bottom: 10px;
}
.stars {
  width: 15px;
  height: 14px;
  display: inline-block;
}
.bookName {
  color: #333333;
  font-size: 14px;
}
.sameTitle {
  margin: 4px 0 5px 0;
  color: #999;
}
.red {
  color: #ef1b3a;
  font-size: 12px;
  font-weight: bold;
}
.free {
  color: #0bc266;
  font-size: 12px;
  font-weight: bold;
}
</style>
<style>
.noData {
  font-size: 14px;
  font-weight: bold;
  color: #999;
  text-align: center;
}
.collect .van-nav-bar {
  position: fixed;
  width: 100%;
  z-index: 999;
  height: 40px;
  line-height: 40px;
  font-size: 16px;
  font-weight: 500;
}
.van-icon-arrow-left::before {
  color: #010101;
  font-size: 15px;
}
</style>
src/views/personalCenter/index.vue
New file
@@ -0,0 +1,340 @@
<template>
  <div class="personalCenterPage">
    <div class="person_user">
      <div class="setInfo">
        <img src="@/assets/images/personalCenter/setting_2.png" alt="" />
        <span @click="setUserInfo()">个人信息</span>
      </div>
      <div class="user_info">
        <div class="userIcon">
          <img v-if="userInfo.icon" :src="userInfo.icon" alt="" class="icon" />
          <img
            v-else
            src="@/assets/images/personalCenter/default_avatar.png"
            alt=""
            class="icon"
          />
          <img
            src="@/assets/images/personalCenter/renzheng_icon.png"
            alt=""
            class="auth"
          />
        </div>
        <div class="userName">
          <p class="name">
            {{ userInfo.name }}
          </p>
          <div class="role">
            {{ userInfo.role == "Teacher" ? "已认证教师" : "未认证教师" }}
          </div>
        </div>
      </div>
    </div>
    <!-- åˆ—表 -->
    <div class="listBox">
      <div class="info-list">
        <div class="title">常用功能</div>
        <div class="listCon">
          <div
            v-for="(item, index) in functionData"
            :key="index"
            @click="tab(item.name)"
            class="listItem"
          >
            <img :src="item.icon" alt="" />
            <div class="listName">
              {{ item.title }}
            </div>
          </div>
        </div>
      </div>
      <div class="info-list">
        <div class="title">其他功能</div>
        <div class="listCon">
          <div
            v-for="(item, index) in otherFunctionData"
            :key="index"
            @click="tab(item.name)"
            class="listItem"
          >
            <img :src="item.icon" alt="" />
            <div class="listName">
              {{ item.title }}
            </div>
          </div>
        </div>
      </div>
    </div>
    <Footer />
  </div>
</template>
<script>
import Footer from "@/components/footer/footer";
import { mapState } from "vuex";
import myConfig from "@/assets/js/config";
import { Toast, Dialog } from "vant";
Vue.use(Toast);
Vue.use(Dialog);
export default {
  data() {
    return {
      teacherRole: "",
      functionData: [
        {
          name: "teacherCertificate",
          title: "教师认证",
          icon: require("@/assets/images/personalCenter/jiaoshirenzheng.png")
        },
        {
          name: "collection",
          title: "我的收藏",
          icon: require("@/assets/images/personalCenter/wodeshoucang.png")
        },
        {
          name: "myShoppingCart",
          title: "购物车",
          icon: require("@/assets/images/personalCenter/shoppingCart.png")
        },
        {
          name: "myOrder",
          title: "我的订单",
          icon: require("@/assets/images/personalCenter/order.png")
        },
        {
          name: "myApplyBook",
          title: "我的申请",
          icon: require("@/assets/images/personalCenter/shenqing.png")
        },
        {
          name: "myTextBook",
          title: "我的教材",
          icon: require("@/assets/images/personalCenter/jiaocai.png")
        },
        {
          name: "",
          title: "我的课程",
          icon: require("@/assets/images/personalCenter/kecheng.png")
        },
        {
          name: "",
          title: "我的班级",
          icon: require("@/assets/images/personalCenter/banji.png")
        }
      ],
      otherFunctionData: [
        {
          name: "aboutUs",
          title: "教师客服",
          icon: require("@/assets/images/personalCenter/jiaoshikefu.png")
        },
        {
          name: "/protocol?type=1",
          title: "用户服务协议",
          icon: require("@/assets/images/personalCenter/xieyi.png")
        },
        {
          name: "protocol",
          title: "教师认证服务条款",
          icon: require("@/assets/images/personalCenter/tiaokuan.png")
        },
        {
          name: "testLogin",
          title: "退出登录",
          icon: require("@/assets/images/personalCenter/tuichu.png")
        },
        {
          name: "unsubscribe",
          title: "注销账号",
          icon: require("@/assets/images/personalCenter/zhuxiao.png")
        }
      ]
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  components: {
    Footer
  },
  created() {
    console.log(this.userInfo);
    this.getUserRoleMine();
  },
  methods: {
    getUserRoleMine() {
      let that = this;
      that.MG.identity.getCurrentAppUser().then(res => {
        if (res) {
          let teacherRole = res.roleLinks.find(
            item => item.role.refCode == "teacher"
          );
          let teacherInfo = res.infoList.find(
            item => item.type == "teacherInfo"
          );
          let wechatInfo = res.infoList.find(item => item.type == "WeChat");
          let studentInfo = res.infoList.find(
            item => item.type == "basicInfo" || item.type == "Default"
          );
          let phoneInfo = res.secretList.find(
            item => item.type == "MobilePhone"
          );
          if (teacherRole && teacherInfo) {
            this.$store.dispatch("setUserInfo", {
              ...teacherInfo,
              phoneNumber: phoneInfo?.credential,
              icon: wechatInfo?.icon,
              role: "Teacher",
              roleId: teacherRole.role.id
            });
          } else if (wechatInfo) {
            this.$store.dispatch("setUserInfo", {
              ...wechatInfo,
              phoneNumber: phoneInfo?.credential,
              role: "Student"
            });
          } else if (studentInfo) {
            this.$store.dispatch("setUserInfo", {
              ...studentInfo,
              icon: wechatInfo?.icon,
              phoneNumber: phoneInfo?.credential,
              role: "Student"
            });
          }
        }
      });
    },
    tab(name) {
      if (name == "testLogin") {
        this.tool.delCookie(myConfig.tokenKey);
        this.tool.delCookie(myConfig.userInfoKey);
        localStorage.removeItem(myConfig.userInfoKey);
        this.$store.dispatch("setUserInfo", {});
        this.$router.push({
          path: "index"
        });
      } else if (name == "unsubscribe") {
        Dialog.confirm({
          title: "警告",
          message: "账号注销之后无法重新注册,请谨慎操作!!!",
          confirmButtonText: "仍要注销",
          cancelButtonText: "取消"
        })
          .then(() => {
            this.MG.identity.unsubscribeAppuser().then(res => {
              this.tool.delCookie(myConfig.tokenKey);
              this.tool.delCookie(myConfig.userInfoKey);
              localStorage.removeItem(myConfig.userInfoKey);
              this.$router.push({
                path: "index"
              });
            });
          })
          .catch(() => {
            // on cancel
          });
      } else if (name) {
        this.$router.push({
          path: name,
          query: {
            formMine: 1
          }
        });
      } else {
        Toast("建设中");
      }
    },
    setUserInfo() {
      this.$router.push({
        path: "personInfor"
      });
    }
  }
};
</script>
<style scoped>
.personalCenterPage {
  height: 100vh;
  padding-bottom: 50px;
  background: #f4f7fb;
  position: relative;
}
.person_user {
  height: 250px;
  background: url("../../assets/images/personalCenter/bg_my.png");
  background-size: 100% 100%;
  padding: 15px;
}
.setInfo {
  text-align: right;
  margin-top: 10px;
}
.setInfo img {
  margin-right: 5px;
}
.user_info {
  display: flex;
}
.userIcon {
  position: relative;
  padding: 0 10px;
}
.icon {
  width: 100px;
  height: 100px;
}
.auth {
  position: absolute;
  top: 80px;
  right: 18px;
}
.userName {
  margin-top: 20px;
  margin-left: 10px;
}
.userName .name {
  font-size: 18px;
  font-weight: bold;
}
.userName .role {
  margin-top: 10px;
  width: 85px;
  text-align: center;
  color: #fff;
  padding: 5px;
  background: linear-gradient(90deg, #5ea1ff 0%, #2e70ff 100%);
  border-radius: 3px 3px 3px 3px;
}
.listBox {
  padding: 15px 15px 60px 15px;
  position: absolute;
  top: 50px;
  padding-top: 100px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #f4f7fb 100%);
}
.info-list {
  background: #fff;
  margin-top: 20px;
  border-radius: 10px;
}
.info-list .title {
  font-size: 18px;
  font-weight: bold;
  padding: 20px;
}
.listCon {
  display: flex;
  flex-wrap: wrap;
}
.listItem {
  width: calc(100% / 4);
  text-align: center;
  padding: 0 15px 20px 15px;
}
.listName {
  margin-top: 15px;
}
</style>
src/views/personalCenter/myApplyBook.vue
New file
@@ -0,0 +1,319 @@
<template>
  <div class="applyPage">
    <div class="topBox">
      <van-nav-bar
        title="我的申请"
        left-text=""
        right-text=""
        left-arrow
        @click-left="onClickLeft"
      />
      <div class="tips">
        å¦‚您在教材试用申请过程中遇到问题,请于工作时间联系我们。<span>
          QQ号:3565269931 / å’¨è¯¢ç”µè¯010-65778403(工作时间:9:00~17:00)</span
        >
      </div>
    </div>
    <!-- åˆ—表 -->
    <div class="listBox">
      <p class="line"></p>
      <van-pull-refresh
        class="refreshBox"
        v-model="refreshing"
        @refresh="onRefresh"
      >
        <van-list
          v-model="loading"
          :finished="finished"
          :finished-text="finishedtext"
          @load="onLoad"
        >
          <div class="collectBox">
            <div v-if="dataList && dataList.length > 0">
              <div v-for="(item, index) in dataList" :key="index" class="list">
                <div class="infor">
                  <div class="infoBox">
                    <div>
                      å®¡æ ¸çŠ¶æ€:
                      <span
                        :class="{
                          reviewstatus: true,
                          reviewstatusRed: item.state == 'Reject',
                          reviewstatusWait: item.state == 'WaitAudit'
                        }"
                        >{{
                          item.state == "WaitAudit"
                            ? "审核中"
                            : item.state == "Normal"
                            ? "通过"
                            : "拒绝"
                        }}</span
                      >
                    </div>
                    <div style="color: orangered" v-if="item.isExpiry">
                      é˜…读期限:<span>已过期</span>
                    </div>
                    <div class="time">申请时间:{{ item.updateDate }}</div>
                  </div>
                  <div
                    style="color: orangered"
                    class="reasonForFailure"
                    v-if="
                      item.state == 'Normal' && item.feedBack && !item.isExpiry
                    "
                  >
                    è¯•用期限:<span>{{ item.updateDate }}</span> --
                    <span>{{ item.feedBack.endDate }}</span>
                  </div>
                  <div
                    class="reasonForFailure"
                    style="margin: 10px 0"
                    v-if="item.state == 'Reject'"
                  >
                    <div class="centerVertically">未通过原因:</div>
                    <div
                      style="flex: 1"
                      :title="
                        item.feedBack.reason ? item.feedBack.reason : ' - '
                      "
                    >
                      {{ item.feedBack.reason ? item.feedBack.reason : " - " }}
                    </div>
                  </div>
                </div>
                <div class="bookContent" @click="gotoDetails(item, index)">
                  <img :src="item.content.icon" alt="" class="bookImg" />
                  <div class="centerContent">
                    <p class="bookName">{{ item.content.name }}</p>
                    <p class="sameTitle">
                      ä½œè€… :
                      {{
                        item.content.tourism_author
                          ? item.content.tourism_author
                          : "-"
                      }}
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </van-list>
      </van-pull-refresh>
    </div>
  </div>
</template>
<script>
import { mapState } from "vuex";
import { getPublicImage } from "@/assets/js/middleGround/tool";
export default {
  data() {
    return {
      refreshing: false,
      loading: false,
      finished: false,
      finishedtext: "",
      dataList: [],
      paginationData: {
        page: 1,
        limit: 10,
        totalCount: 0,
        totalPage: 0
      }
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    this.getdataList();
  },
  methods: {
    onRefresh() {
      // æ¸…空列表数据
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.refreshing = false;
      this.getdataList(true);
    },
    onLoad() {
      this.getdataList();
    },
    //获取列表
    getdataList(isLode) {
      var that = this;
      this.finished = false;
      this.loading = true;
      this.finishedtext = "加载中";
      let { page, limit } = this.paginationData;
      const data = {
        start: limit * page - limit,
        size: limit,
        topicIdOrRefCode: "applyDigitalBook",
        appRefCode: "tourismWebsite",
        sort: {
          type: "Desc",
          field: "CreateDate"
        }
      };
      this.MG.ugc.getTopicMessageList(data).then(res => {
        res.datas.forEach(item => {
          item.icon = item.icon ? item.icon : getPublicImage(null);
          item.updateDate = item.updateDate.split("T")[0];
          if (item.feedBack) {
            item.feedBack = JSON.parse(item.feedBack);
            if (item.feedBack.endDate) {
              let times = new Date(
                item.feedBack.endDate + " 23:59:59"
              ).getTime();
              if (times < sessionStorage.currentDate) {
                item.isExpiry = true;
              }
            }
          }
          if (item.content) {
            item.content = JSON.parse(item.content);
          }
        });
        if (isLode) {
          that.dataList = res.datas;
        } else {
          that.dataList = [...that.dataList, ...res.datas];
        }
        console.log(that.dataList);
        //分页
        that.paginationData.totalCount = res.totalSize;
        if (res.totalSize > that.paginationData.limit) {
          that.paginationData.totalPage = parseInt(
            res.totalSize / that.paginationData.limit
          );
          if (res.totalSize % that.paginationData.limit > 0) {
            that.paginationData.totalPage++;
          }
        } else {
          that.paginationData.totalPage = 1;
        }
        that.sum = Number(Math.ceil(res.totalSize / this.paginationData.limit));
        // åŠ è½½çŠ¶æ€ç»“æŸ
        that.loading = false;
        that.refreshing = false;
        if (that.paginationData.page == this.sum) {
          that.finished = true;
          that.finishedtext = "没有更多了";
          return false;
        }
        if (that.dataList.length < 1) {
          that.loading = false;
          that.finished = true;
          that.finishedtext = "暂无数据";
        }
        that.paginationData.page++;
      });
    },
    gotoDetails(item) {
      this.$router.push({
        path: "/bookDetail",
        query: {
          shopId: item.id
        }
      });
    },
    onClickLeft() {
      var that = this;
      that.$router.go(-1);
    }
  },
};
</script>
<style scoped>
.tips {
  padding: 15px;
}
.tips span {
  color: #2b68cd;
}
.refreshBox {
  flex: 1;
  overflow: auto;
}
.collectBox {
  padding: 0 15px;
}
.line {
  height: 5px;
  background-color: #eee;
}
.listBox {
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}
/* åˆ—表 */
.list {
  padding: 15px 0 15px 0;
  border-bottom: 1px solid #e8e8e8;
}
.infoBox {
  line-height: 30px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.time {
  color: #999;
}
.reviewstatus {
  color: #0bc266;
}
.reviewstatusWait {
  color: #ffbe00;
}
.reviewstatusRed {
  color: red;
}
.reasonForFailure {
  display: flex;
  line-height: 30px;
}
.centerVertically {
  width: 80px;
}
.bookContent {
  display: flex;
  margin-top: 10px;
}
.bookImg {
  width: 75px;
  height: 100px;
  display: inline-block;
  box-shadow: 0 3px 6px 1px #00000029;
}
.centerContent {
  margin-left: 14px;
}
.bookName {
  color: #333333;
  font-size: 14px;
}
.sameTitle {
  margin: 4px 0 5px 0;
  color: #999;
}
</style>
src/views/personalCenter/myOrder.vue
New file
@@ -0,0 +1,699 @@
<template>
  <div class="orderPage">
    <div class="header">
      <van-nav-bar
        title="我的订单"
        left-text=""
        right-text=""
        @click-left="onClickLeft"
        left-arrow
      />
    </div>
    <!-- åˆ—表 -->
    <div class="recordBox">
      <van-tabs
        v-model="tabActive"
        color="#1989fa"
        title-active-color="#1989fa"
        @change="changeMenu"
      >
        <van-tab title="全部">
          <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
            <van-list
              v-model="loading"
              :finished="finished"
              finished-text="没有更多了"
              @load="onLoad"
            >
              <template slot="finished" name="finished">
                <div v-if="orderList.length == 0">
                  <van-empty description="暂无数据" />
                </div>
                <div v-else>
                  æ²¡æœ‰æ›´å¤šäº†
                </div>
              </template>
              <div class="orderListBox">
                <div
                  class="orderList"
                  v-for="(item, index) in orderList"
                  :key="index"
                >
                  <div class="spline"></div>
                  <div class="orderTitle">
                    <span class="orderNum">订单号: {{ item.orderNumber }}</span>
                    <span class="orderState wait" v-if="item.state == 'WaitPay'"
                      >待支付</span
                    >
                    <span
                      class="orderState successTxt"
                      v-if="item.state == 'Success'"
                      >已完成</span
                    >
                    <span class="orderState" v-if="item.state == 'Cancel'"
                      >已取消</span
                    >
                  </div>
                  <div class="orderContent">
                    <div
                      class="contentItem"
                      v-for="(citem, cindex) in item.saleMethodLinks"
                      :key="cindex"
                      @click="toDetail(citem)"
                    >
                      <div class="imgBox">
                        <img
                          class="bookImg"
                          :src="
                            tool.getPublicImage(
                              citem.orderSaleMethod.product.icon,
                              '210'
                            )
                          "
                          alt=""
                        />
                      </div>
                      <div class="txtBox">
                        <div class="title">
                          {{ citem.orderSaleMethod.product.name }}
                          <span
                            v-if="citem.orderSaleMethod.name != '默认销售方式'"
                          >
                            {{
                              citem.orderSaleMethod.name.replace(
                                /-销售方式/g,
                                ""
                              )
                            }}
                          </span>
                        </div>
                        <div class="price" v-if="citem.payPrice != 0">
                          Â¥
                          {{ tool.toDecimal2(citem.payPrice) }}
                        </div>
                        <div v-else class="successTxt">
                          å…è´¹
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="orderFunBox">
                    <div class="countInfoBox">
                      <span style="float: left;font-size: 12px;color: #999;"
                        >创建时间:{{
                          item.createDate.replace("T", " ").split(".")[0]
                        }}</span
                      >
                      <span v-if="item.payPrice != 0"
                        >合计:
                        <span class="priceTxt"
                          >Â¥ {{ tool.toDecimal2(item.payPrice) }}</span
                        ></span
                      >
                      <span v-else class="successTxt">
                        å…è´¹
                      </span>
                    </div>
                    <div class="orderBtnBox">
                      <span
                        class="orderBtn"
                        @click="deletOrder(item)"
                        v-if="item.state == 'WaitPay'"
                        >取消订单</span
                      >
                      <span
                        class="orderBtn pay"
                        @click="toPay(item)"
                        v-if="item.state == 'WaitPay'"
                        >去支付</span
                      >
                      <!-- <span
                        class="orderBtn invoice"
                        v-if="
                          item.state == 'Success' &&
                            item.payPrice > 0 &&
                            !item.invoiceInfo &&
                            !item.exceedingTheSpecifiedTime
                        "
                        @click="toInvoice(item)"
                        >申请发票</span
                      > -->
                    </div>
                  </div>
                </div>
              </div>
            </van-list>
          </van-pull-refresh>
        </van-tab>
        <van-tab title="待支付">
          <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
            <van-list
              v-model="loading"
              :finished="finished"
              finished-text="没有更多了"
              @load="onLoad"
            >
              <template slot="finished" name="finished">
                <div v-if="orderList.length == 0">
                  <van-empty description="暂无数据" />
                </div>
                <div v-else>
                  æ²¡æœ‰æ›´å¤šäº†
                </div>
              </template>
              <div class="orderListBox">
                <div
                  class="orderList"
                  v-for="(item, index) in orderList"
                  :key="index"
                >
                  <div class="spline"></div>
                  <div class="orderTitle">
                    <span class="orderNum">订单号: {{ item.orderNumber }}</span>
                    <span class="orderState wait" v-if="item.state == 'WaitPay'"
                      >待支付</span
                    >
                  </div>
                  <div class="orderContent">
                    <div
                      class="contentItem"
                      v-for="(citem, cindex) in item.saleMethodLinks"
                      :key="cindex"
                      @click="toDetail(citem)"
                    >
                      <div class="imgBox">
                        <img
                          class="bookImg"
                          :src="
                            tool.getPublicImage(
                              citem.orderSaleMethod.product.icon,
                              '210'
                            )
                          "
                          alt=""
                        />
                      </div>
                      <div class="txtBox">
                        <div class="title">
                          {{ citem.orderSaleMethod.product.name }}
                        </div>
                        <div class="price" v-if="citem.payPrice != 0">
                          Â¥
                          {{ tool.toDecimal2(citem.payPrice) }}
                        </div>
                        <div v-else class="successTxt">免费</div>
                      </div>
                    </div>
                  </div>
                  <div class="orderFunBox">
                    <div class="countInfoBox">
                      <span style="float: left;font-size: 12px;color: #999;"
                        >创建时间:{{
                          item.createDate.replace("T", " ").split(".")[0]
                        }}</span
                      >
                      <span v-if="item.payPrice != 0"
                        >合计:
                        <span class="priceTxt"
                          >Â¥ {{ tool.toDecimal2(item.payPrice) }}</span
                        ></span
                      >
                      <span v-else class="successTxt">
                        å…è´¹
                      </span>
                    </div>
                    <div class="orderBtnBox">
                      <span
                        class="orderBtn"
                        @click="deletOrder(item)"
                        v-if="item.state == 'WaitPay'"
                        >取消订单</span
                      >
                      <span
                        @click="toPay(item)"
                        class="orderBtn pay"
                        v-if="item.state == 'WaitPay'"
                        >去支付</span
                      >
                    </div>
                  </div>
                </div>
              </div>
            </van-list>
          </van-pull-refresh>
        </van-tab>
        <van-tab title="已完成">
          <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
            <van-list
              v-model="loading"
              :finished="finished"
              finished-text="没有更多了"
              @load="onLoad"
            >
              <template slot="finished" name="finished">
                <div v-if="orderList.length == 0">
                  <van-empty description="暂无数据" />
                </div>
                <div v-else>
                  æ²¡æœ‰æ›´å¤šäº†
                </div>
              </template>
              <div class="orderListBox">
                <div
                  class="orderList"
                  v-for="(item, index) in orderList"
                  :key="index"
                >
                  <div class="spline"></div>
                  <div class="orderTitle">
                    <span class="orderNum">订单号: {{ item.orderNumber }}</span>
                    <span
                      class="orderState successTxt"
                      v-if="item.state == 'Success'"
                      >已完成</span
                    >
                  </div>
                  <div class="orderContent">
                    <div
                      class="contentItem"
                      v-for="(citem, cindex) in item.saleMethodLinks"
                      :key="cindex"
                      @click="toDetail(citem)"
                    >
                      <div class="imgBox">
                        <img
                          class="bookImg"
                          :src="
                            tool.getPublicImage(
                              citem.orderSaleMethod.product.icon,
                              '210'
                            )
                          "
                          alt=""
                        />
                      </div>
                      <div class="txtBox">
                        <div class="title">
                          {{ citem.orderSaleMethod.product.name }}
                        </div>
                        <div class="price" v-if="citem.payPrice != 0">
                          Â¥
                          {{ tool.toDecimal2(citem.payPrice) }}
                        </div>
                        <div v-else class="successTxt">
                          å…è´¹
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="orderFunBox">
                    <div class="countInfoBox">
                      <span style="float: left;font-size: 12px;color: #999;"
                        >创建时间:{{
                          item.createDate.replace("T", " ").split(".")[0]
                        }}</span
                      >
                      <span v-if="item.payPrice != 0"
                        >合计:
                        <span class="priceTxt"
                          >Â¥ {{ tool.toDecimal2(item.payPrice) }}</span
                        ></span
                      >
                      <span v-else class="successTxt">
                        å…è´¹
                      </span>
                    </div>
                    <!-- <div class="orderBtnBox">
                      <span
                        class="orderBtn invoice"
                        v-if="
                          item.state == 'Success' &&
                            item.payPrice > 0 &&
                            !item.invoiceInfo &&
                            !item.exceedingTheSpecifiedTime
                        "
                        @click="toInvoice(item)"
                        >申请发票</span
                      >
                    </div> -->
                  </div>
                </div>
              </div>
            </van-list>
          </van-pull-refresh>
        </van-tab>
        <van-tab title="已取消">
          <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
            <van-list
              v-model="loading"
              :finished="finished"
              finished-text="没有更多了"
              @load="onLoad"
            >
              <template slot="finished" name="finished">
                <div v-if="orderList.length == 0">
                  <van-empty description="暂无数据" />
                </div>
                <div v-else>
                  æ²¡æœ‰æ›´å¤šäº†
                </div>
              </template>
              <div class="orderListBox">
                <div
                  class="orderList"
                  v-for="(item, index) in orderList"
                  :key="index"
                >
                  <div class="spline"></div>
                  <div class="orderTitle">
                    <span class="orderNum">订单号: {{ item.orderNumber }}</span>
                    <span class="orderState" v-if="item.state == 'Cancel'"
                      >已取消</span
                    >
                  </div>
                  <div class="orderContent">
                    <div
                      class="contentItem"
                      v-for="(citem, cindex) in item.saleMethodLinks"
                      :key="cindex"
                      @click="toDetail(citem)"
                    >
                      <div class="imgBox">
                        <img
                          class="bookImg"
                          :src="
                            tool.getPublicImage(
                              citem.orderSaleMethod.product.icon,
                              '210'
                            )
                          "
                          alt=""
                        />
                      </div>
                      <div class="txtBox">
                        <div class="title">
                          {{ citem.orderSaleMethod.product.name }}
                        </div>
                        <div class="price" v-if="citem.payPrice != 0">
                          Â¥
                          {{ tool.toDecimal2(citem.payPrice) }}
                        </div>
                        <div v-else class="successTxt">
                          å…è´¹
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="orderFunBox">
                    <div class="countInfoBox">
                      <span style="float: left;font-size: 12px;color: #999;"
                        >创建时间:{{
                          item.createDate.replace("T", " ").split(".")[0]
                        }}</span
                      >
                      <span
                        >合计:
                        <span class="priceTxt"
                          >Â¥ {{ tool.toDecimal2(item.payPrice) }}</span
                        ></span
                      >
                    </div>
                  </div>
                </div>
              </div>
            </van-list>
          </van-pull-refresh>
        </van-tab>
      </van-tabs>
    </div>
  </div>
</template>
<script>
import { mapState } from "vuex";
import { setOrderList } from "@/assets/js/toolClass";
import Vue from "vue";
import {
  Tab,
  Tabs,
  List,
  PullRefresh,
  NavBar,
  Dialog,
  Toast,
  Empty
} from "vant";
Vue.use(Tab);
Vue.use(Tabs);
Vue.use(List);
Vue.use(PullRefresh);
Vue.use(Dialog);
Vue.use(Toast);
Vue.use(NavBar);
Vue.use(Empty);
export default {
  data() {
    return {
      arrow: true,
      headTitle: "我的订单",
      //分页
      loading: true,
      finished: false,
      refreshing: false,
      // tabActive
      tabActive: 0,
      // è®¢å•列表
      orderList: [],
      //分页
      paginationData: {
        page: 1,
        limit: 6,
        totalCount: 0,
        totalPage: 0
      },
      //筛选条件请求参数
      querySearch: []
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    this.getOrderList();
  },
  beforeRouteLeave(to, from, next) {
    if (to.name != "bookDetail") {
      this.$store.commit("delKeepAlive", from.name);
    }
    next();
  },
  methods: {
    //获取订单列表
    getOrderList() {
      this.loading = true;
      let data = {
        Size: 9999,
        Start: 0,
        sort: {
          type: "Desc",
          field: "CreateDate"
        },
        searchList: this.querySearch,
        filterList: this.queryFilter
      };
      this.MG.store.getUserOrderList(data).then(response => {
        // console.log(response)
        // åˆ¤æ–­æ˜¯å¦å·²åŠ è½½å®Œæ•°æ®'
        let list = [];
        response.datas.forEach(element => {
          if (
            element.saleMethodLinks[0].orderSaleMethod.product.cmsTypeRefCode ==
            "tourism_digitalTextbooks"
          ) {
            list.push(element);
          }
        });
        this.finished = true;
        this.orderList = setOrderList(list);
        this.loading = false;
        this.refreshing = false;
      });
    },
    //切换菜单
    changeMenu() {
      if (this.tabActive == 0) {
        this.queryFilter = [];
      } else if (this.tabActive == 1) {
        this.queryFilter = [{ field: "State", value: "WaitPay" }];
      } else if (this.tabActive == 2) {
        this.queryFilter = [{ field: "State", value: "Success" }];
      } else if (this.tabActive == 3) {
        this.queryFilter = [{ field: "State", value: "Cancel" }];
      }
      this.orderList = [];
      // æ¢å¤æ•°æ®åŠ è½½å®Œæ¯•çš„çŠ¶æ€
      this.finished = false;
      this.getOrderList();
    },
    // ä¸Šæ‹‰åŠ è½½
    onLoad() {
      this.getOrderList();
    },
    // ä¸‹æ‹‰åˆ·æ–°
    onRefresh() {
      this.paginationData.page = 1;
      this.getOrderList();
      this.finished = true;
    },
    // ç”³è¯·å‘票
    toInvoice(item) {
      this.$router.push({
        path: "/applyInvoice",
        query: {
          invoiceOrderNum: item.orderNumber,
          invoiceOrderPrice: item.payPrice,
          invoiceOrderInfo: JSON.stringify(item)
        }
      });
    },
    toPay(item) {
      this.$router.push({
        path: "/pay",
        query: {
          orderNum: item.orderNumber
        }
      });
    },
    deletOrder(item) {
      Dialog.confirm({
        title: "温馨提示",
        message: "确定取消该订单吗?"
      })
        .then(() => {
          this.MG.store
            .cancelOrder({ orderNum: item.orderNumber })
            .then(res => {
              if (res) {
                Toast.success("订单已取消");
                item.state = "Cancel";
              } else {
                Toast.fail("订单取消失败");
              }
            });
        })
        .catch(() => {});
    },
    toDetail(item) {
      let query = {
        path: "*",
        queryType: "*",
        productId: item.orderSaleMethod.product.id,
        storeInfo: this.config.digitalTextBooksGoodsStore
      };
      this.MG.store.getProductDetail(query).then(res => {
        this.$router.push({
          path: "/bookDetail",
          query: {
            id: res.datas.id
          }
        });
      });
    },
    onClickLeft() {
      var that = this;
      that.$router.go(-1);
    }
  }
};
</script>
<style scoped>
.recordBox {
  max-height: calc(100vh - 75px);
  overflow: auto;
}
.orderTitle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: #999;
  padding: 20px 15px;
  border-bottom: 1px solid #dddddd;
}
.orderContent {
  padding: 15px;
}
.contentItem {
  display: flex;
  margin-top: 12px;
}
.txtBox {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: space-between;
  margin-left: 15px;
}
.imgBox {
  width: 105px;
  height: 140px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 5px;
  box-shadow: 0 3px 6px 1px #00000029;
}
.imgBox img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  display: inline-block;
}
.txtBox .title {
  font-size: 16px;
  color: #333333;
  font-weight: bold;
}
.txtBox .price {
  font-size: 18px;
  color: #f24b3b;
}
.orderFunBox {
  padding: 15px;
  border-top: 1px solid #ddd;
}
.orderBtnBox {
  text-align: right;
}
.countInfoBox {
  text-align: right;
}
.orderBtn {
  margin-top: 30px;
  display: inline-block;
  padding: 10px 20px;
  color: #333333;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 10px;
  margin-left: 20px;
}
.orderState.discuss {
  color: #2e70ff;
}
.orderState.wait {
  color: #f04343;
}
.invoice {
  color: #fff;
  background: #2e70ff;
}
.pay {
  color: #fff;
  background: #f04343;
}
.priceTxt {
  color: #f04343;
}
</style>
src/views/personalCenter/myShoppingCart.vue
New file
@@ -0,0 +1,439 @@
<template>
  <div class="mycart">
    <van-nav-bar
      class="navBar"
      title="我的购物车"
      @click-left="onClickLeft"
      left-arrow
    >
      <template #right>
        <span v-if="cartList.length > 0" @click="showDel">{{
          isDel ? "完成" : "管理"
        }}</span>
      </template>
    </van-nav-bar>
    <div class="recordBox">
      <div class="orderListBox">
        <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
          <van-list
            v-model="loading"
            :finished="finished"
            finished-text="没有更多了"
            @load="onLoad"
          >
            <template slot="finished" name="finished">
              <div v-if="pictureList.length == 0">
                <van-empty description="暂无数据" />
              </div>
              <div v-else>
                æ²¡æœ‰æ›´å¤šäº†
              </div>
            </template>
            <div
              class="orderList"
              v-for="(item, index) in pictureList"
              :key="'p' + index"
            >
              <div class="orderContent">
                <div class="contentItem">
                  <van-checkbox
                    class="itemCheck"
                    v-model="item.isChecked"
                    isChecked-color="#ee0a24"
                    @click="initPrice"
                  ></van-checkbox>
                  <div class="imgBox">
                    <img class="bookImg" :src="item.icon" alt="" />
                  </div>
                  <div class="txtBox">
                    <div class="title">{{ item.title }}</div>
                    <div class="priceBox">
                      <div class="price">
                        Â¥
                        {{
                          item.price
                            ? tool.toDecimal2(item.price)
                            : tool.toDecimal2(0)
                        }}
                      </div>
                      <span class="num">x1</span>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </van-list>
        </van-pull-refresh>
      </div>
    </div>
    <div class="payWrap">
      <div class="spline" style="height: 8px"></div>
      <div class="payBtnBox">
        <div class="readBtnBox">
          <div class="checkBox">
            <van-checkbox
              v-model="allChecked"
              isChecked-color="#ee0a24"
              @click="checkAll"
              >全选</van-checkbox
            >
          </div>
          <div class="totalPriecBox" v-if="!isDel">
            <div>已选数量: {{ selectedNum }}</div>
            <div>
              <span>合计:</span>
              <span class="totalPrice"
                >Â¥ {{ tool.toDecimal2(totalPrice) }}</span
              >
            </div>
          </div>
        </div>
        <div class="buyBtn" v-if="!isDel" @click="toPay">去结算</div>
        <div class="buyBtn" v-if="isDel" @click="delCart">
          åˆ é™¤
        </div>
      </div>
    </div>
    <!-- è´­ä¹°æŒ‰é’®å ä½ -->
    <div class="emptypayBox"></div>
  </div>
</template>
<script>
import Vue from "vue";
import { mapState } from "vuex";
import {
  List,
  PullRefresh,
  Checkbox,
  NavBar,
  Toast,
  Dialog,
  Empty
} from "vant";
Vue.use(List);
Vue.use(PullRefresh);
Vue.use(Checkbox);
Vue.use(NavBar);
Vue.use(Toast);
Vue.use(Dialog);
Vue.use(Empty);
export default {
  data() {
    return {
      // è®¢å•列表
      cartList: [],
      // å•†å“
      pictureList: [],
      // å…¨é€‰
      allChecked: false,
      // æ€»ä»·æ ¼
      totalPrice: 0,
      // ç®¡ç†åˆ é™¤æŒ‰é’®
      isDel: false,
      // é€‰ä¸­å•†å“çš„æ•°é‡
      selectedNum: 0,
      paginationData: {
        page: 1,
        limit: 6,
        totalCount: 0,
        totalPage: 0
      },
      //分页
      loading: false,
      finished: false,
      refreshing: false
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    this.getUserCartList();
  },
  methods: {
    //获取用户购物车
    getUserCartList() {
      this.loading = true;
      let data = {
        size: 999,
        start: 0
      };
      this.MG.store.getShoppingCartProductList(data).then(res => {
        // åˆ¤æ–­æ˜¯å¦å·²åŠ è½½å®Œæ•°æ®
        let list = [];
        res.datas.forEach(element => {
          if (
            element.productMonWithLinkDto.links[0].storeRefCode ==
            "tourism_digitalTextbooks"
          ) {
            list.push(element);
          }
        });
        this.finished = true;
        var courseList = list.map(item => {
          return {
            id: item.productMonWithLinkDto.product.id,
            title: item.productMonWithLinkDto.product.name,
            icon: this.tool.getPublicImage(
              item.productMonWithLinkDto.product.icon,
              "100"
            ),
            price: item.saleMethod.price,
            type: item.saleMethod.type,
            isChecked: false,
            linkId: item.id,
            name: item.saleMethod.name,
            description: item.saleMethod.description,
            saleMethodId: item.saleMethod.id
          };
        });
        this.cartList = courseList;
        this.pictureList = courseList;
        this.finished = true;
        this.loading = false;
        this.refreshing = false;
        // é‡æ–°è®¡ç®—总价
        this.initPrice();
      });
    },
    // è®¡ç®—总价
    initPrice() {
      this.selectedNum = 0;
      let price = 0;
      for (let i = 0; i < this.cartList.length; i++) {
        if (this.cartList[i].isChecked) {
          this.selectedNum += 1;
          price = price + (this.cartList[i].price - 0);
        }
      }
      this.totalPrice = price;
      if (this.cartList.length == this.selectedNum && this.selectedNum != 0) {
        this.allChecked = true;
      } else {
        this.allChecked = false;
      }
    },
    // ç‚¹å‡»å…¨é€‰
    checkAll() {
      let price = 0;
      if (!this.allChecked) {
        for (let item of this.cartList) {
          item.isChecked = false;
        }
        this.selectedNum = 0;
        price = 0;
      } else {
        for (let item of this.cartList) {
          item.isChecked = true;
          if (item.price) {
            price += item.price - 0;
          }
          this.selectedNum = this.cartList.length;
        }
      }
      this.totalPrice = price;
    },
    // åˆ é™¤è´­ç‰©è½¦
    delCart() {
      Dialog.confirm({
        title: "温馨提示",
        message: "确定从购物车移除所选商品吗?"
      })
        .then(() => {
          let listdata = [];
          this.cartList.map(item => {
            if (item.isChecked) {
              listdata.push(item.linkId);
            }
          });
          const data = {
            ids: listdata
          };
          this.MG.store
            .delShoppingCart(data)
            .then(res => {
              if (res) {
                Toast.success("移除成功");
                this.showDel();
                this.onRefresh();
              } else {
                Toast.fail("移除失败,请稍后重试");
              }
            })
            .catch(function(error) {
              console.log(error);
            });
        })
        .catch(() => {
          Toast.fail("已取消操作");
        });
    },
    showDel() {
      this.isDel = !this.isDel;
    },
    onClickLeft() {
      this.$router.go(-1);
    },
    //到购买页面
    toPay() {
      let selectId = [];
      if (this.cartList.filter(item => item.isChecked).length > 0) {
        for (let item of this.cartList.filter(item => item.isChecked)) {
          selectId.push(item.linkId);
        }
        let data = {
          linkIds: [...selectId]
        };
        this.MG.store.shoppingCartCreateOrder(data).then(res => {
          this.$router.push({
            path: "/pay",
            query: {
              orderNum: res.orderNumber
            }
          });
        });
      } else {
        Toast.fail("请选择要购买的商品");
      }
    },
    // ä¸Šæ‹‰åŠ è½½
    onLoad() {
      this.getUserCartList();
    },
    // ä¸‹æ‹‰åˆ·æ–°
    onRefresh() {
      this.getUserCartList();
      this.finished = true;
    }
  }
};
</script>
<style scoped>
.sortTitle {
  padding: 20px 35px;
  background: #eee;
  font-size: 26px;
  color: #999;
}
.orderContent {
  padding: 20px;
  border-bottom: 1px solid #ddd;
}
.contentItem {
  display: flex;
}
.txtBox {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: space-between;
  margin-left: 15px;
}
.imgBox {
  width: 105px;
  height: 140px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 5px;
  box-shadow: 0 3px 6px 1px #00000029;
}
.imgBox img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  display: inline-block;
}
.txtBox .title {
  font-size: 16px;
  color: #333333;
  font-weight: bold;
}
.priceBox {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.priceBox .price {
  font-size: 18px;
  font-weight: bold;
  color: #f24b3b;
}
.priceBox .num {
  color: #999;
  font-size: 16px;
}
/* è´­ä¹°ç›¸å…³æŒ‰é’® */
.payWrap {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  background: #fff;
  z-index: 3;
}
.payBtnBox {
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: 3;
  padding-bottom: env(safe-area-inset-bottom);
}
.payBtnBox .readBtnBox {
  display: flex;
  align-items: center;
  width: 60%;
  padding: 0 20px;
  box-sizing: border-box;
}
.checkBox {
  display: flex;
  align-items: center;
  width: 120px;
}
.itemCheck {
  margin-right: 15px;
}
.totalPriecBox > div {
  display: inline-block;
}
.totalPrice {
  color: #f24b3b;
  margin-left: 30px;
}
.payBtnBox .buyBtn {
  width: 40%;
  text-align: center;
  line-height: 50px;
  color: #ffffff;
  font-size: 18px;
  background: #f24b3b;
}
.paypayBtnBoxBox .redBtn {
  background: #3c85ff;
}
.payBtnBox .addBtn {
  color: #333;
  background: #fff;
  border-top: 1px solid #ddd;
}
.emptypayBox {
  position: relative;
  width: 100%;
  height: 98px;
}
</style>
<style>
.mycart .van-nav-bar__right span {
  font-size: 16px;
  color: #2b68cd;
}
</style>
src/views/personalCenter/myTextBook.vue
New file
@@ -0,0 +1,318 @@
<template>
  <div class="collect article">
    <van-nav-bar title="我的教材" left-text="" right-text="" />
    <!-- åˆ—表 -->
    <div class="listBox">
      <p class="line"></p>
      <van-pull-refresh
        class="refreshBox"
        v-model="refreshing"
        @refresh="onRefresh"
      >
        <van-list
          v-model="loading"
          :finished="finished"
          :finished-text="finishedtext"
          @load="onLoad"
        >
          <template slot="finished" name="finished">
            <div v-if="collectList.length == 0">
              <van-empty description="暂无数据" />
            </div>
            <div v-else>
              æ²¡æœ‰æ›´å¤šäº†
            </div>
          </template>
          <div class="collectBox">
            <div v-if="collectList && collectList.length > 0">
              <div
                v-for="(item, index) in collectList"
                :key="index"
                class="list"
                @click="gotoDetails(item.product, index)"
              >
                <img :src="item.product.icon" alt="" class="bookImg" />
                <div class="centerContent">
                  <p class="bookName">{{ item.product.name }}</p>
                  <p class="sameTitle">
                    ä½œè€… :
                    {{
                      item.tourism_author.length != 0
                        ? item.tourism_author
                        : "-"
                    }}
                  </p>
                  <p class="sameTitle">
                    ISBN :
                    {{
                      item.tourism_ISBN.length != 0 ? item.tourism_ISBN : "-"
                    }}
                  </p>
                </div>
              </div>
            </div>
          </div>
        </van-list>
      </van-pull-refresh>
    </div>
    <Footer />
  </div>
</template>
<script>
import Footer from "@/components/footer/footer";
import { mapState } from "vuex";
import { getPublicImage } from "@/assets/js/middleGround/tool";
export default {
  data() {
    return {
      form: this.$route.query.formMine,
      refreshing: false,
      loading: false,
      finished: false,
      finishedtext: "",
      collectList: [],
      paginationData: {
        page: 1,
        limit: 10,
        totalCount: 0,
        totalPage: 0
      },
      keyQueryRequests: [
        {
          key: "tourism_author"
        },
        {
          key: "tourism_ISBN"
        }
      ]
    };
  },
  computed: {
    ...mapState(["userInfo"])
  },
  created() {
    this.getCollectList();
  },
  methods: {
    onRefresh() {
      // æ¸…空列表数据
      this.paginationData.page = 1;
      this.finished = false;
      this.loading = true;
      this.refreshing = false;
      this.getCollectList(true);
    },
    onLoad() {
      this.getCollectList();
    },
    //获取列表
    getCollectList(isLode) {
      var that = this;
      this.finished = false;
      this.loading = true;
      this.finishedtext = "加载中";
      this.MG.store
        .getPurchasedProductList({
          sort: {
            type: "Desc",
            field: "CreateDate"
          },
          Size: that.paginationData.limit,
          Start: (that.paginationData.page - 1) * that.paginationData.limit,
          searchList: [
            {
              keywords: "tourism_digitalTextbooks",
              field: "ProductType"
            }
          ],
          keyQueryRequests: this.keyQueryRequests
        })
        .then(res => {
          let list = that.handResultsChange(res.datas);
          list.forEach(item => {
            item.product.icon = getPublicImage(item.product.icon);
          });
          if (isLode) {
            that.collectList = list;
          } else {
            that.collectList = [...that.collectList, ...list];
          }
          //分页
          that.paginationData.totalCount = res.total;
          if (res.total > that.paginationData.limit) {
            that.paginationData.totalPage = parseInt(
              res.total / that.paginationData.limit
            );
            if (res.total % that.paginationData.limit > 0) {
              that.paginationData.totalPage++;
            }
          } else {
            that.paginationData.totalPage = 1;
          }
          that.sum = Number(
            Math.ceil(res.totalSize / this.paginationData.limit)
          );
          // åŠ è½½çŠ¶æ€ç»“æŸ
          that.loading = false;
          that.refreshing = false;
          if (that.paginationData.page == this.sum) {
            that.finished = true;
            that.finishedtext = "没有更多了";
            return false;
          }
          if (that.collectList.length < 1) {
            that.loading = false;
            that.finished = true;
            that.finishedtext = "暂无数据";
          }
          that.paginationData.page++;
        });
    },
    // å¤„理查询结果
    handResultsChange(data) {
      let fieldsData = [];
      for (let i = 0; i < data.length; i++) {
        const item = data[i];
        for (const val in this.keyQueryRequests) {
          fieldsData.push(this.keyQueryRequests[val].key);
        }
        for (let i = 0; i < fieldsData.length; i++) {
          const field = fieldsData[i];
          item[field] = JSON.parse(item.datas[field]);
          const datas = item[field];
          if (datas.length > 0) {
            if (datas[0].Value) {
              item[field] = datas[0].Value;
            } else if (datas[0].Data) {
              item[field] = datas[0].Data.Value;
            }
          }
        }
      }
      return data;
    },
    gotoDetails(item) {
      this.$router.push({
        path: "/bookDetail",
        query: {
          id: item.id
        }
      });
    },
    onClickLeft() {
      var that = this;
      that.$router.go(-1);
    }
  },
  components: {
    Footer
  }
};
</script>
<style scoped>
.refreshBox {
  flex: 1;
  overflow: auto;
}
.collectBox {
  padding: 0 15px;
}
.collect {
  padding-bottom: 50px;
  height: 100%;
  box-sizing: border-box;
}
.line {
  height: 5px;
  background-color: #eee;
}
.listBox {
  padding-top: 40px;
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}
/* åˆ—表 */
.list {
  display: flex;
  position: relative;
  padding: 15px 0 15px 0;
  border-bottom: 1px solid #e8e8e8;
}
.bookImg {
  width: 75px;
  height: 100px;
  display: inline-block;
  box-shadow: 0 3px 6px 1px #00000029;
}
.centerContent {
  margin-left: 14px;
}
.starSection {
  min-width: 36px;
  text-align: center;
  position: absolute;
  right: 10px;
  bottom: 10px;
}
.stars {
  width: 15px;
  height: 14px;
  display: inline-block;
}
.bookName {
  color: #333333;
  font-size: 14px;
}
.sameTitle {
  margin: 4px 0 5px 0;
  color: #999;
}
.red {
  color: #ef1b3a;
  font-size: 12px;
  font-weight: bold;
}
.free {
  color: #0bc266;
  font-size: 12px;
  font-weight: bold;
}
</style>
<style>
.noData {
  font-size: 14px;
  font-weight: bold;
  color: #999;
  text-align: center;
}
.collect .van-nav-bar {
  position: fixed;
  width: 100%;
  z-index: 999;
  height: 40px;
  line-height: 40px;
  font-size: 16px;
  font-weight: 500;
}
.van-icon-arrow-left::before {
  color: #010101;
  font-size: 15px;
}
</style>
src/views/personalCenter/personInfor.vue
New file
@@ -0,0 +1,175 @@
<template>
  <div class="personInfor">
    <van-nav-bar
      title="个人信息"
      left-text
      left-arrow
      @click-left="onClickLeft"
    />
    <ul class="listInfor">
      <li class="firstLi">
        <span style="font-size:14px;color:#333;">头像</span>
        <div>
          <img v-if="userInfo.icon" :src="userInfo.icon" alt="" />
          <img v-else src="@/assets/images/default_avatar.png" alt="" />
        </div>
      </li>
      <li>
        <div style="display:flex;justify-content: space-between;">
          <span class="title">用户名:</span>
          <div>
            <span class="content">{{ userInfo.name }}</span>
          </div>
        </div>
      </li>
      <li>
        <div style="display:flex;justify-content: space-between;">
          <span class="title">手机号:</span>
          <div>
            <span class="content">{{ phone }}</span>
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>
<script>
import { mapState } from "vuex";
export default {
  data() {
    return {
      avatarIcon: "",
      username: "",
      phone: ""
    };
  },
  created() {
    // this.getUserInfo();
  },
  computed: {
    ...mapState(["userInfo"])
  },
  mounted() {
    this.teacherInfo = JSON.parse(this.userInfo.data)
    this.phone = this.teacherInfo.phone ? this.teacherInfo.phone : "-"
  },
  methods: {
    evil(fn) {
      let Fn = Function; // ä¸€ä¸ªå˜é‡æŒ‡å‘Function,防止有些前端编译工具报错
      return new Fn("return " + fn)();
    },
    //获取用户信息
    getUserInfo() {
      let that = this;
      that.request.get("/api/user/info").then(res => {
        if (res.headImgUrl && res.headImgUrl != "") {
          if (res.headImgUrl.indexOf("https") > -1) {
            that.avatarIcon = res.headImgUrl;
          } else {
            that.avatarIcon =
              that.config.requestCtx +
              "/fileDownload.ashx?md5=" +
              res.headImgUrl +
              "&appId=" +
              that.config.appId;
          }
        } else {
          that.avatarIcon = require("@/assets/images/default_avatar.png");
        }
        that.phone = res.mobile;
        that.username = res.account && res.account != "" ? res.account : "";
        if (res.roles.indexOf("Teacher") != -1) {
          that.request
            .post("/api/EduTeacher/getTeacherInfo", {
              appId: that.config.appId
            })
            .then(res => {
              res.nickName =
                res.nickName && res.nickName != ""
                  ? that.evil("'" + res.nickName + "'")
                  : "";
              that.userInfo = res;
            });
        } else if (res.roles.indexOf("Student") != -1) {
          that.getStudentInfo();
        }
      });
    },
    // èŽ·å–å­¦ç”ŸåŸºæœ¬ä¿¡æ¯
    getStudentInfo() {
      let that = this;
      that.request
        .post("/api/EduStudent/getStudentInfo", { appId: that.config.appId })
        .then(res => {
          if (res) {
            res.nickName =
              res.nickName && res.nickName != ""
                ? that.evil("'" + res.nickName + "'")
                : "";
            that.userInfo = res;
          }
        });
    },
    onClickLeft() {
      var that = this;
      that.$router.push({
        path: "/personalCenter"
      });
    }
  }
};
</script>
<style scoped>
.listInfor {
  padding-top: 38px;
}
.listInfor img {
  width: 65px;
  height: 65px;
  display: inline-block;
  border-radius: 100%;
}
.listInfor li {
  border-bottom: 1px solid #f6f6f6;
  padding: 0 15px;
}
.firstLi {
  height: 85px;
  line-height: 85px;
  display: flex;
  justify-content: space-between;
}
li + li {
  height: 48px;
  line-height: 48px;
}
li + li:last-child {
  margin-bottom: 30px;
}
.title {
  font-size: 14px;
  color: #333333;
}
.content {
  color: #999999;
  font-size: 14px;
}
</style>
<style>
.personInfor .van-nav-bar {
  position: fixed;
  width: 100%;
}
</style>
src/views/personalCenter/protocol.vue
New file
@@ -0,0 +1,91 @@
<template>
  <div class="protocol">
    <van-nav-bar
      :title="title"
      left-text
      left-arrow
      @click-left="onClickLeft"
    />
    <div class="contentBox" v-html="contentBox"></div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      protocol: "",
      title: "",
      contentBox: ""
    };
  },
  created() {
    this.getProtocol();
  },
  methods: {
    onClickLeft() {
      var that = this;
      that.$router.push({
        path: "/personalCenter"
      });
    },
    //获取用户协议
    getProtocol() {
      var that = this;
      that.MG.resource
        .getItem({
          path: "tourism_protocol",
          fields: {
            tourism_content: [],
            tourism_tag: []
          }
        })
        .then(res => {
          try {
            const agreementData = res.datas.find(
              e => e.refCode == "tourism_userRegistrationAgreement"
            );
            const teacherData = res.datas.find(
              e => e.refCode == "tourism_teacherCertificationAgreement"
            );
            if (
              agreementData &&
              agreementData.tourism_content &&
              this.$route.query.type
            ) {
              that.title = agreementData.name;
              that.contentBox = agreementData.tourism_content.replace(
                /<strong>/gi,
                '<strong class="p_class">'
              );
            }
            if (
              teacherData &&
              teacherData.tourism_content &&
              !this.$route.query.type
            ) {
              that.title = teacherData.name;
              that.contentBox = teacherData.tourism_content.replace(
                /<strong>/gi,
                '<strong class="p_class">'
              );
            }
          } catch (error) {
            console.log(error);
          }
        });
    }
  }
};
</script>
<style scoped>
.protocol {
  width: 100vw;
  height: 100vh;
}
.contentBox {
  padding: 30px;
  box-sizing: border-box;
  height: calc(100vh - 46px);
  overflow: auto;
}
</style>
src/views/personalCenter/teacherCertificate.vue
New file
@@ -0,0 +1,979 @@
<template>
  <div class="teacherCertificate">
    <van-nav-bar
      class="navBar"
      title="教师认证"
      left-text
      left-arrow
      @click-left="onClickLeft"
    />
    <div class="teacherCertificateBox" v-if="!loading">
      <div class="topTitle">
        <span>认证信息</span>
        <div class="currentState">
          <span>当前状态:</span>
          <span class="status-w" v-if="approve.state == 'WaitAudit'"
            >等待审核</span
          >
          <span class="status-s" v-else-if="approve.state == 'Normal'"
            >已认证</span
          >
          <span class="status-f" v-else-if="approve.state == 'Reject'"
            >已驳回</span
          >
          <span class="status-b" v-else>待认证</span>
        </div>
      </div>
      <!-- <div class="reasonBox" v-if="reasonTxt && reasonTxt != ''">
        <span class="title">驳回原因</span> : {{ reasonTxt }}
      </div> -->
      <!-- form表单 -->
      <div>
        <van-form @submit="onSubmit">
          <van-field
            v-model="approve.fullName"
            :readonly="stateDisable"
            label="姓名"
            placeholder="请输入您的姓名"
            :rules="[{ required: true, message: '请输入您的姓名' }]"
          />
          <!-- èŒåŠ¡ -->
          <van-field
            clickable
            label="职务"
            readonly
            :rules="[{ required: true, message: '请选择职务' }]"
            :value="approve.post"
            placeholder="请选择"
            @click="showPickerPop"
          />
          <van-popup v-model="showPicker" position="bottom">
            <van-picker
              show-toolbar
              :columns="colunmsJob"
              @confirm="onConfirm"
              @cancel="showPicker = false"
            />
          </van-popup>
          <!-- èŒç§° -->
          <van-field
            clickable
            label="职称"
            readonly
            :value="approve.positionalTitle"
            :rules="[{ required: true, message: '请选择职称' }]"
            placeholder="请选择"
            @click="showPicker1Pop"
          />
          <van-popup v-model="showPicker1" position="bottom">
            <van-picker
              show-toolbar
              :readonly="stateDisable"
              :columns="colunmsTitle"
              @confirm="onConfirm1"
              @cancel="showPicker1 = false"
            ></van-picker>
          </van-popup>
          <!-- å­¦åކ -->
          <van-field
            readonly
            clickable
            label="学历"
            :rules="[{ required: false, message: '请选择学历' }]"
            :value="approve.education"
            placeholder="请选择"
            @click="showPicker2Pop"
          />
          <van-popup v-model="showPicker2" position="bottom">
            <van-picker
              show-toolbar
              :columns="colunmsEdu"
              @confirm="onConfirm2"
              @cancel="showPicker2 = false"
            />
          </van-popup>
          <van-field
            :readonly="stateDisable"
            v-model="approve.schoolName"
            :rules="[{ required: true, message: '请输入您的所在学校名称' }]"
            label="所在学校名称"
            placeholder="请输入您的所在学校名称"
            @change="chageState(true)"
          />
          <van-field
            :readonly="stateDisable"
            v-model="approve.faculty"
            :rules="[{ required: true, message: '请输入您的所在院、系' }]"
            label="所在院、系"
            placeholder="请输入您的所在院、系"
            @change="chageState(true)"
          />
          <!-- <van-field
            :readonly="stateDisable"
            v-model="approve.region"
            :rules="[{ required: true, message: '请输入所在省份' }]"
            label="所在省份:"
            placeholder="请输入所在省份"
          /> -->
          <van-field
            v-model="approve.region"
            is-link
            :readonly="stateDisable"
            label="所在省份:"
            placeholder="请选择所在省份:"
            :rules="[{ required: true, message: '请选择所在省份' }]"
            @click="showAddress"
          />
          <van-popup v-model="showPicker3" round position="bottom">
            <van-cascader
              v-model="approve.region"
              title="请选择所在省份:"
              :options="areaOptions"
              @close="showPicker3 = false"
              @finish="finishAddress"
            />
          </van-popup>
          <van-field
            :readonly="stateDisable"
            v-model="approve.lecturingSpecialty"
            :rules="[{ required: true, message: '请输入您的授课专业' }]"
            label="授课专业"
            placeholder="请输入您的授课专业"
            @change="chageState(true)"
          />
          <van-field
            :readonly="stateDisable"
            v-model="approve.phone"
            name="phone"
            type="tel"
            label="联系电话"
            :rules="[
              { required: true, message: '请填写您的手机号码!' },
              { pattern: /^1[3456789]\d{9}$/, message: '手机号码格式错误!' }
            ]"
            placeholder="请输入您的联系电话"
          />
          <van-field
            :readonly="stateDisable"
            name="qq"
            v-model="approve.qq"
            :rules="[{ required: false, message: '请填写您的QQ' }]"
            label="QQ"
            placeholder="请填写您的QQ"
          />
          <van-field
            :readonly="stateDisable"
            name="email"
            v-model="approve.email"
            label="电子邮件"
            placeholder="请输入您的电子邮件"
          />
          <van-field
            :readonly="stateDisable"
            v-model="approve.detailedAddress"
            label="学校地址"
            :rules="[{ required: true, message: '请输入详细地址' }]"
            placeholder="请输入详细地址"
          />
          <van-field
            readonly
            name="certificate"
            label="相关证件"
            :rules="[{ required: true, message: '请上传相关证件' }]"
          >
            <template #input>
              <van-uploader
                :after-read="afterRead"
                :disabled="stateDisable"
                v-model="approve.relevantCertificates"
                @delete="deleteFile"
                multiple
              >
                <!-- <van-button type="primary">点击上传</van-button> -->
              </van-uploader>
            </template>
          </van-field>
          <div class="file-infor" style="flex-direction: column">
            <div class="attention">
              <p class="notice">
                1.请上传您的教师工作证(不是教师资格证)或能体现您学校、姓名等信息的相关证件(例如教工卡,教务系统登陆后截图等)
              </p>
            </div>
            <div>
              <p class="notice">2.支持上传jpg/png文件,且不超过500kb</p>
              <p class="phone">
                <span class="notice"
                  >3.如您在认证时遇到问题,请于工作时间联系我们。</span
                >
                QQ号:3565269931 / å’¨è¯¢ç”µè¯010-65778403
                ï¼ˆå·¥ä½œæ—¶é—´ï¼š9:00~17:00)
              </p>
            </div>
          </div>
          <div
            v-if="!stateDisable"
            style="background-color: rgb(240, 240, 240); padding-top: 25px; height: 175px"
          >
            <van-checkbox
              v-model="approve.agree"
              shape="square"
              style="padding-left: 20px; width: 200px"
              >同意
              <span
                style="color: #3586ff; z-index: 1"
                @click.stop="dialogVisible = true"
                >教师会员协议</span
              >
            </van-checkbox>
            <div style="margin: 40px">
              <van-button
                round
                block
                type="info"
                native-type="submit"
                :disabled="submitDisabled"
                >提 äº¤</van-button
              >
            </div>
          </div>
          <div v-if="stateDisable && approve.state != 'WaitAudit'">
            <div style="margin:40px;">
              <van-button round block type="info" @click="stateDisable = false"
                >ä¿® æ”¹</van-button
              >
            </div>
          </div>
        </van-form>
      </div>
    </div>
    <div class="loadingBox" v-else>
      <van-loading type="spinner" color="#1989fa" />
    </div>
    <van-dialog
      v-model="dialogVisible"
      title="《教师认证服务条款》"
      width="90%"
      confirmButtonColor="#3586ff"
      messageAlign="left"
    >
      <div>
        <div class="protocolBox" v-html="protocolTxt"></div>
      </div>
    </van-dialog>
  </div>
</template>
<script>
import Vue from "vue";
import { Toast } from "vant";
import tool from "@/assets/js/toolClass";
import { regionData } from "element-china-area-data"; //引入
// import { mapState } from "vuex";
// :rules="[
//               { required: false, message: '请输入正确电子邮件' },
//               {
//                 pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/,
//                 message: '邮箱格式错误!'
//               }
//             ]"
Vue.use(Toast);
export default {
  data() {
    return {
      cascaderValue: "",
      fileValue: "",
      areaOptions: [],
      token: "",
      dialogVisible: false,
      protocolTxt: "",
      refCode: "",
      headers: {
        Authorization: ""
      },
      submitDisabled: true, // æäº¤æŒ‰é’®,默认禁选
      showPicker: false,
      showPicker1: false,
      showPicker2: false,
      showPicker3: false,
      //加载状态
      loading: true,
      subLoading: false,
      //表单禁用状态
      stateDisable: false,
      //审核状态
      state: "",
      teacherData: {
        //姓名
        username: "",
        //昵称
        nickName: "",
        // èŒåŠ¡
        job: "",
        //职称
        ranks: "",
        //学历
        education: "",
        // description: { education: "" },
        //所在学校
        schoolName: "",
        //所在院系
        facultyDepartment: "",
        //授课专业
        lectureSpecialty: "",
        //联系电话
        phone: "",
        //电子邮件
        email: "",
        //所在学校地址
        schoolAddress: "",
        //通讯地址
        phoneAddress: "",
        //相关证件
        certificate: [],
        // æ•™å¸ˆåŒæ„å‹¾é€‰æ¡†
        checked: false
      },
      isUpdate: false,
      approve: {
        fullName: "", //姓名
        post: "", //职务
        positionalTitle: "", //职称
        education: "", //学历
        schoolName: "", //学校名称
        region: "", //省份
        faculty: "", //所在院系
        lecturingSpecialty: "", //授课专业
        phone: "", //联系电话
        qq: "", //qq
        email: "", //联系邮箱
        schoolAddress: "", //学校地址
        detailedAddress: "", //通讯地址
        // description: "", //个人简介
        relevantCertificates: [], //相关证件
        state: "", //审核状态默认待审核
        agree: false
      },
      //picker选择列表
      colunmsJob: [
        "院长",
        "副院长",
        "教研主任",
        "教研副主任",
        "系主任",
        "系副主任",
        "专业负责人",
        "主任",
        "其他"
      ],
      colunmsTitle: ["正高级", "高级", "一级", "二级", "三级", "无"],
      colunmsEdu: ["大专", "本科", "硕士", "博士"],
      uploadList: [],
      worksInfo: [],
      fileList: [],
      isWaitAudit: false,
      userId: "",
      isFree: false
    };
  },
  watch: {
    "approve.agree": {
      handler(val) {
        this.submitDisabled = !val;
      }
    },
    $route(to, from) {
      if (
        to.path == "/teacherCertificate" &&
        tool.getCookie("websiteFrontToken")
      ) {
        this.getProtocol();
      }
    }
  },
  created() {
    var that = this;
    var accToken = tool.getCookie("website-front-token");
    that.token = accToken;
    if (accToken) {
      that.getUserRole();
      that.getType();
    }
    that.changeAddress(regionData);
    that.getProtocol();
    if (this.$route.query?.loginType == "free") {
      this.isFree = true;
    }
  },
  methods: {
    // å¤„理省市
    changeAddress(data) {
      for (let i = 0; i < data.length; i++) {
        const item = data[i];
        item.value = item.label;
        item.text = item.label;
        if (item.children?.length > 0) {
          this.changeAddress(item.children);
        }
      }
      this.areaOptions = data;
    },
    chageState(val) {
      this.isWaitAudit = val;
    },
    // é€‰æ‹©åœ°å€
    finishAddress({ selectedOptions }) {
      this.showPicker3 = false;
      this.approve.region = selectedOptions
        .map(option => option.text)
        .join("/");
    },
    deleteFile(file) {
      this.fileList.forEach((item, index) => {
        if (item.md5 == file.md5) {
          this.fileList.splice(index, 1);
        }
      });
    },
    getUserRole() {
      let that = this;
      that.MG.identity.getCurrentAppUser().then(res => {
        that.userId = res.userId;
        let teacherRole = res.roleLinks.find(item => item.role?.refCode == "teacher");
        let teacherInfo = res.infoList.find(item => item.type == "teacherInfo") ?? {};
        let wechatInfo = res.infoList.find(item => item.type == "WeChat") ?? {};
        let studentInfo = res.infoList.find(item => item.type == "Default");
        let phoneInfo = res.secretList.find(item => item.type == "MobilePhone");
        if (teacherRole && teacherInfo) {
          this.$store.dispatch("setUserInfo", {
            ...teacherInfo,
            phoneNumber: phoneInfo?.credential,
            icon: wechatInfo?.icon,
            role: "Teacher",
            roleId: teacherRole.role.id
          });
        } else if (wechatInfo) {
          this.$store.dispatch("setUserInfo", {
            ...wechatInfo,
            phoneNumber: phoneInfo?.credential,
            role: "Student"
          });
        } else if (studentInfo) {
          this.$store.dispatch("setUserInfo", {
            ...studentInfo,
            icon: wechatInfo?.icon,
            phoneNumber: phoneInfo?.credential,
            role: "Student"
          });
        }
      });
    },
    getType() {
      const data = {
        refCodes: ["tourism_teacherCertification"]
      };
      this.MG.resource.getCmsTypeByRefCode(data).then(res => {
        this.newGetTeacherInfo();
        this.worksInfo = res[0].cmsTypeLinks[0].children;
      });
    },
    newGetTeacherInfo() {
      let that = this;
      this.loading = true;
      const data = {
        start: 0,
        size: 10,
        topicIdOrRefCode: "teacherRoleApproval",
        appRefCode: "tourismWebsite",
        sort: {
          type: "Desc",
          field: "CreateDate"
        }
      };
      that.MG.ugc.getTopicMessageList(data).then(res => {
        try {
          if (res.datas.length > 0) {
            that.fileList = [];
            const resData = res.datas.find(
              i => i.appUserCreator.userId == that.userId
            );
            that.approve = that.tool.resultsBytool(
              that.worksInfo,
              resData.cmsItemDataList
            );
            that.approve.state = resData.state;
            if (
              that.approve.state == "WaitAudit" ||
              that.approve.state == "Normal"
            ) {
              that.stateDisable = true;
            }
            that.topicId = resData.id;
            that.topicMessageList = resData.cmsItemDataList;
            if (that.approve.feedBack != null) {
              that.reasonTxt = JSON.parse(that.approve.feedBack).reason;
            }
            if (resData.cmsItemDataList.length > 0) {
              that.isUpdate = true;
            }
            if (that.approve.relevantCertificates.length > 0) {
              if (typeof that.approve.relevantCertificates == "object") {
                that.approve.relevantCertificates.forEach(ele => {
                  let imgObj = {
                    md5: ele.file.md5,
                    url:
                      this.config.requestCtx +
                      `/file/GetPreViewImage?md5=` +
                      ele.file.md5,
                    linkType: "LinkFile",
                    linkProtectType: "Public",
                    isImage: true
                  };
                  that.fileList.push(imgObj);
                });
              } else {
                let imgObj = {
                  url:
                    this.config.requestCtx +
                    `/file/GetPreViewImage?md5=` +
                    that.approve.relevantCertificates,
                  md5: that.approve.relevantCertificates,
                  linkType: "LinkFile",
                  linkProtectType: "Public",
                  isImage: true
                };
                that.fileList.push(imgObj);
              }
            }
          }
          that.loading = false;
        } catch (error) {
          console.log(error);
          that.loading = false;
        }
      });
    },
    getProtocol() {
      var that = this;
      that.MG.resource
        .getItem({
          path: "tourism_protocol",
          fields: {
            tourism_content: []
          }
        })
        .then(res => {
          try {
            const data = res.datas.find(
              e => e.refCode == "tourism_teacherCertificationAgreement"
            );
            this.protocolTxt = data ? data.tourism_content : "暂无协议";
          } catch (error) {
            this.protocolTxt = "暂无协议";
          }
        });
    },
    onSubmit(val) {
      let that = this;
      if (this.subLoading) {
        return false;
      }
      this.subLoading = true;
      if (this.isUpdate) {
        this.updateSubmit();
      } else {
        this.stateDisable = true;
        if (that.approve.agree) {
          // this.approve.region = this.approve.region?.split("/");
          // !this.isFree ? "WaitAudit" : "Normal"
          const data = {
            topicIdOrRefCode: "teacherRoleApproval",
            name: this.approve.fullName,
            content: "",
            state: !this.isFree ? "WaitAudit" : "Normal",
            type: "teacherRegister",
            cmsTypeRefCode: "tourism_teacherCertification",
            newDataListRequest: this.tool.worksDataBytool(
              this.worksInfo,
              this.approve,
              this.fileList
            )
          };
          let basicInfo = JSON.parse(JSON.stringify(this.approve));
          delete basicInfo.agree;
          delete basicInfo.worksInfo;
          delete basicInfo.state;
          const userInfo = {
            requests: [
              {
                data: JSON.stringify(basicInfo),
                name: basicInfo.fullName,
                type: "teacherInfo"
              }
            ]
          };
          this.MG.identity.setAppUserInfo(userInfo).then(res => {
            this.MG.ugc.newTopicMessage(data).then(() => {
              console.log(res);
              this.subLoading = true;
              if (res !== false) {
                that.newGetTeacherInfo();
              }
            });
          });
        }
      }
    },
    updateSubmit() {
      let basicInfo = JSON.parse(JSON.stringify(this.approve));
      this.approve.region = this.approve.region?.split("/");
      const fields = this.tool.UpdateworksDataBytool(
        this.worksInfo,
        this.topicMessageList,
        this.approve,
        this.fileList
      );
      let state = "Normal";
      if (this.approve.state == "WaitAudit" || this.approve.state == "Reject") {
        state = "WaitAudit";
      }
      if (this.approve.state == "Normal") {
        state = this.isWaitAudit ? "WaitAudit" : "Normal";
      }
      const data = {
        description: "",
        icon: "",
        id: this.topicId,
        topicIdOrRefCode: "teacherRoleApproval",
        name: this.approve.fullName,
        content: "",
        type: "teacherRegister",
        state: state,
        newDataRequests: fields.newData,
        updateDataRequests: fields.updateData,
        delDataRequest: {
          ids: []
        }
      };
      delete basicInfo.tourism_relevantCertificates;
      delete basicInfo.agree;
      delete basicInfo.worksInfo;
      delete basicInfo.state;
      const userInfo = {
        requests: [
          {
            data: JSON.stringify(basicInfo),
            name: basicInfo.fullName,
            type: "newTeacherInfo"
          }
        ]
      };
      this.MG.identity.setAppUserInfo(userInfo).then(res => {
        if (res) {
          this.MG.ugc.updateTopicMessage(data).then(res => {
            this.subLoading = false;
            if (res !== false) {
              this.getUserRole();
              this.newGetTeacherInfo();
            }
          });
        }
      });
    },
    async beforeUpload(file) {
      console.log(file);
      const isJPG = file.type === "image/jpeg" || file.type === "image/png";
      const isLt2M = (0.5 * file.size) / 1024 / 1024 < 0.5;
      if (!isJPG) {
        Toast("上传文件只能是 jpg/png æ ¼å¼!");
        return false;
      }
      if (!isLt2M) {
        Toast("上传文件大小不能超过 500KB!");
        return false;
      }
      const md5 = await this.tool.getFileMd5(file, 1024);
      this.currentMD5 = md5;
      let currentFileName = file.name.substring(0, file.name.lastIndexOf("."));
      this.currentFileName = currentFileName;
      let currentExtension = file.name.substring(
        file.name.lastIndexOf(".") + 1,
        file.name.length
      );
      this.currentExtension = currentExtension;
      this.currentFileType = file.type;
      // this.fileList.push({
      //   md5: md5,
      //   url: this.config.requestCtx + `/file/GetPreViewImage?md5=` + md5,
      //   linkType: "LinkFile",
      //   linkProtectType: "Public",
      //   isImage: true
      // });
      // this.approve.relevantCertificates = this.fileList;
      return isJPG && isLt2M;
    },
    afterRead(file) {
      var that = this;
      that.isLoding = true;
      const FileName = file.file.name.split(".")[0];
      const Extension = file.file.name.split(".")[1];
      const FileType = file.file.type;
      let size = 1024;
      that.tool
        .getFileMd5(file.file, size * 1024)
        .then(e => {
          const imgData = new FormData();
          that.fileMd5 = e;
          imgData.append("Md5", e);
          imgData.append("FileName", FileName);
          imgData.append("Extension", Extension);
          imgData.append("FileType", FileType);
          imgData.append("MetaData", null);
          imgData.append("file", file.file);
          that.MG.file.upload(imgData).then(res => {
            if (res) {
              this.fileList.push({
                md5: e,
                url: this.config.requestCtx + `/file/GetPreViewImage?md5=` + e,
                linkType: "LinkFile",
                linkProtectType: "Public",
                isImage: true
              });
              this.approve.relevantCertificates = this.fileList;
              // this.fileList = this.approve.relevantCertificates
              console.log(this.temp_fileList, "this.temp_fileList");
            }
          });
        })
        .catch(e => {
          that.isLoding = false;
          console.error(e);
        });
      // let fd = new FormData();
      // fd.append("appId", this.config.appId);
      // fd.append("file", file.file);
      // this.request({
      //   method: "POST",
      //   url: "/api/fileUpload/public",
      //   data: fd
      // }).then(res => {
      //   // å¤„理相关证件
      //   let imgList = res.uploadSuccessResults;
      //   let showImgList = [];
      //   for (let i = 0; i < imgList.length; i++) {
      //     if (imgList[i].md5) {
      //       showImgList.push({
      //         name: imgList[i].fileName,
      //         md5: imgList[i].md5,
      //         url:
      //           this.config.requestCtx +
      //           "/fileDownload.ashx?md5=" +
      //           imgList[i].md5 +
      //           "&appId=" +
      //           this.config.appId,
      //         isImage: true
      //       });
      //     }
      //   }
      //   this.uploadList = [...this.uploadList, ...showImgList];
      //   this.teacherData.certificate = this.uploadList;
      // });
    },
    //显示职务弹窗
    showPickerPop() {
      if (!this.stateDisable) {
        this.showPicker = true;
      }
      this.MG.store
        .getProductTypeField({
          refCodes: ["post"]
        })
        .then(res => {
          if (res && res[0].config) {
            let list = JSON.parse(res[0].config).option;
            list.forEach(item => {
              item.text = item.name;
            });
            this.colunmsJob = list;
          }
        });
    },
    //显示职称弹窗
    showPicker1Pop() {
      if (!this.stateDisable) {
        this.showPicker1 = true;
      }
      this.MG.store
        .getProductTypeField({
          refCodes: ["positionalTitle"]
        })
        .then(res => {
          if (res && res[0].config) {
            let list = JSON.parse(res[0].config).option;
            list.forEach(item => {
              item.text = item.name;
            });
            this.colunmsTitle = list;
          }
        });
    },
    //学历弹窗
    showPicker2Pop() {
      if (!this.stateDisable) {
        this.showPicker2 = true;
      }
      this.MG.store
        .getProductTypeField({
          refCodes: ["education"]
        })
        .then(res => {
          if (res && res[0].config) {
            let list = JSON.parse(res[0].config).option;
            list.forEach(item => {
              item.text = item.name;
            });
            this.colunmsEdu = list;
          }
        });
    },
    // åœ°å€å¼¹æ¡†
    showAddress() {
      if (this.stateDisable) {
        return false;
      }
      this.showPicker3 = true;
    },
    // è¿”回箭头
    onClickLeft() {
      var that = this;
      that.$router.go(-1);
    },
    onConfirm(value) {
      this.approve.post = value.text;
      this.showPicker = false;
    },
    onConfirm1(value) {
      this.approve.positionalTitle = value.text;
      this.showPicker1 = false;
    },
    onConfirm2(value) {
      this.approve.education = value.text;
      this.showPicker2 = false;
    }
  }
};
</script>
<style scoped>
.phone {
  color: #2b68cd;
  line-height: 22px;
}
.attention {
  line-height: 22px;
}
.file-infor {
  padding: 15px 12px;
  box-sizing: border-box;
}
.file-infor .notice {
  color: #ccc;
}
.status-w {
  color: #e6a23c;
}
.status-s {
  color: #67c23a;
}
.status-f {
  color: #f56c6c;
}
.status-b {
  color: #2b68cd;
}
.loadingBox {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.topTitle {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 40px;
  height: 40px;
  line-height: 40px;
  padding: 0 16px;
  /* margin-top: 40px; */
  background-color: rgba(215, 215, 215, 0.56);
  color: #999999;
}
.certificate {
  font-size: 13px;
  padding: 0 35px 0 15px;
}
.navBar {
  top: 0;
  left: 0;
  height: 40px;
  line-height: 40px;
  position: fixed;
  width: 100%;
}
</style>
<style>
.van-nav-bar__title {
  height: 40px;
  line-height: 40px;
  font-size: 16px;
}
.van-uploader__wrapper--disabled .van-uploader__preview-delete {
  display: none;
}
.van-field--disabled .van-cell__value {
  background: #f6f6f6;
}
/* .van-field__control {
  text-indent: 8px;
} */
/* .van-uploader__preview {
  top: 102px;
} */
.van-field--error .van-field__control,
.van-field--error .van-field__control::placeholder {
  color: #b7b7b7;
}
/* .van-uploader__preview-delete-icon {
  right: -5px;
} */
.van-uploader__preview-delete {
  right: 1px;
}
.teacherCertificate .van-dialog__content {
  height: 60vh;
  font-size: 14px;
  overflow: auto;
  padding: 15px;
}
</style>
src/views/testLogin.vue
New file
@@ -0,0 +1,148 @@
<template>
  <div class="loginBox">
    <h1>需要登录</h1>
    <van-cell-group style="margin-top:100px;">
      <van-field v-model="username" label="账号" placeholder="请输入账号" />
      <van-field v-model="password" type="password" label="密码" />
    </van-cell-group>
    <div style="margin: 60px;">
      <van-button round block type="info" @click="login">登录</van-button>
    </div>
    <!-- <h1>需要登录</h1> -->
    <!-- <div class="username">
      <el-input v-model="username" type="text" />
    </div>
    <div class="password">
      <el-input v-model="password" type="password" />
    </div>-->
    <!-- <button @click="login">登录</button> -->
  </div>
</template>
<script>
export default {
  name: "login",
  data() {
    return {
      username: "15200000002",
      password: "xA123456"
      // username: "guest",
      // password: "guest"
    };
  },
  created() {
    if (this.tool.getCookie("token")) {
      this.tool.delCookie("token");
    }
  },
  methods: {
    login() {
      let that = this;
      const data = {
        loginName: this.username,
        password: this.password,
        appRefCode: this.config.appRefCode,
        platform: "mobile"
      };
      that.MG.identity.loginByPassword(data).then(res => {
        if (res.status == "Ok") {
          let token = res.token;
          this.tool.setCookie(this.config.tokenKey, token);
          sessionStorage.token = token;
          this.getUserRole();
          that.$router.push({
            path: "/"
          });
        } else {
          this.$toast.fail(res.data.errorDescription);
        }
      });
    },
    // èŽ·å–ç™»å½•ç”¨æˆ·èº«ä»½
    getUserRole() {
      let that = this;
      that.MG.identity.getCurrentAppUser().then(res => {
        if (res) {
          that.userId = res.userId;
          let roleData = null;
          let teacherInfo = res.infoList.find(
            item => item.type == "teacherInfo"
          );
          let wechatInfo = res.infoList.find(item => item.type == "WeChat");
          let studentInfo = res.infoList.find(item => item.type == "student");
          let defaultInfo = res.infoList.find(item => item.type == "Default");
          if (res.roleLinks.length > 0) {
            roleData = res.roleLinks.find(item => item.role.type == "appRole");
          }
          if (teacherInfo) {
            this.$store.dispatch("setUserInfo", {
              ...teacherInfo,
              role: roleData ? "Teacher" : "Student",
              roleId: roleData ? roleData.role.id : null
            });
          } else if (wechatInfo) {
            this.$store.dispatch("setUserInfo", {
              ...wechatInfo,
              role: "Student"
            });
          } else if (studentInfo) {
            this.$store.dispatch("setUserInfo", {
              ...studentInfo,
              role: "Student"
            });
          } else {
            this.$store.dispatch("setUserInfo", {
              ...defaultInfo,
              role: "Student"
            });
          }
        } else {
          // this.$router.push({
          //   path: "/login"
          // });
        }
      });
    },
    getNormalUserInfo(icon, nickName, name) {
      let that = this;
      that.request
        .post("/api/account/setDefaultRole", { appId: that.config.appId })
        .then(res => {
          that.getUserRole();
        });
    },
    // èŽ·å–å­¦ç”ŸåŸºæœ¬ä¿¡æ¯
    getStudentInfo(icon, nickName, name) {
      let that = this;
      that.request
        .post("/api/EduStudent/getStudentInfo", { appId: that.config.appId })
        .then(res => {
          if (res) {
            var userInfo = {
              phone: res.phone,
              name: name,
              nickName: nickName,
              role: "Student",
              icon: icon,
              appUserId: res.appUserId
            };
            that.$store.dispatch("setUserInfo", userInfo);
            that.$router.push({
              path: "/"
            });
          }
        });
    }
  }
};
</script>
<style scoped>
.loginBox {
  padding-top: 50px;
}
.loginBox h1 {
  text-align: center;
  font-size: 18px;
  font-weight: bold;
}
</style>
vue.config.js
New file
@@ -0,0 +1,10 @@
module.exports = {
  publicPath: "/mobile/textbooks",
  // devServer Options don't belong into `configureWebpack`
  devServer: {
    host: "0.0.0.0",
    hot: true,
    disableHostCheck: false
  },
  productionSourceMap: false
};