黎海 2 vuotta sitten
commit
eeb327b923
72 muutettua tiedostoa jossa 52730 lisäystä ja 0 poistoa
  1. 6 0
      .gitignore
  2. 32 0
      app_list.json
  3. 94 0
      auto_pack.py
  4. 10 0
      babel.config.js
  5. 9 0
      config/dev.js
  6. 99 0
      config/index.js
  7. 37 0
      config/prod.js
  8. 46259 0
      package-lock.json
  9. 87 0
      package.json
  10. 27 0
      private/private.wxcf756553cfb65cb8.key
  11. 29 0
      project.config.json
  12. 12 0
      project.tt.json
  13. 8 0
      src/api/config.js
  14. 52 0
      src/api/request.js
  15. 52 0
      src/app.config.js
  16. 55 0
      src/app.js
  17. 0 0
      src/app.less
  18. 133 0
      src/common/tool.js
  19. 16 0
      src/component/taro-plugin-canvas/index.css
  20. 322 0
      src/component/taro-plugin-canvas/index.tsx
  21. 84 0
      src/component/taro-plugin-canvas/types.ts
  22. 394 0
      src/component/taro-plugin-canvas/utils/draw.ts
  23. 204 0
      src/component/taro-plugin-canvas/utils/tools.ts
  24. BIN
      src/images/app-icon.png
  25. BIN
      src/images/nav/like.png
  26. BIN
      src/images/nav/like_pin.png
  27. BIN
      src/images/nav/my.png
  28. BIN
      src/images/nav/my_pin.png
  29. BIN
      src/images/nav/play.png
  30. BIN
      src/images/nav/play_pin.png
  31. 22 0
      src/index.html
  32. 5 0
      src/pages/collection/index.config.js
  33. 196 0
      src/pages/collection/index.jsx
  34. 173 0
      src/pages/collection/index.less
  35. 5 0
      src/pages/index/index.config.js
  36. 149 0
      src/pages/index/index.jsx
  37. 155 0
      src/pages/index/index.less
  38. 5 0
      src/pages/index/subpages/directory/index.config.js
  39. 84 0
      src/pages/index/subpages/directory/index.jsx
  40. 61 0
      src/pages/index/subpages/directory/index.less
  41. 5 0
      src/pages/index/subpages/novel/index.config.js
  42. 182 0
      src/pages/index/subpages/novel/index.jsx
  43. 241 0
      src/pages/index/subpages/novel/index.less
  44. 5 0
      src/pages/index/subpages/novelText/index.config.js
  45. 434 0
      src/pages/index/subpages/novelText/index.jsx
  46. 377 0
      src/pages/index/subpages/novelText/index.less
  47. 5 0
      src/pages/index/subpages/search/index.config.js
  48. 84 0
      src/pages/index/subpages/search/index.jsx
  49. 97 0
      src/pages/index/subpages/search/index.less
  50. 5 0
      src/pages/mine/index.config.js
  51. 200 0
      src/pages/mine/index.jsx
  52. 149 0
      src/pages/mine/index.less
  53. 5 0
      src/pages/mine/subpages/history/index.config.js
  54. 213 0
      src/pages/mine/subpages/history/index.jsx
  55. 162 0
      src/pages/mine/subpages/history/index.less
  56. 5 0
      src/pages/mine/subpages/makeMoney/index.config.js
  57. 112 0
      src/pages/mine/subpages/makeMoney/index.jsx
  58. 105 0
      src/pages/mine/subpages/makeMoney/index.less
  59. 5 0
      src/pages/mine/subpages/moneyPlay/index.config.js
  60. 36 0
      src/pages/mine/subpages/moneyPlay/index.jsx
  61. 4 0
      src/pages/mine/subpages/moneyPlay/index.less
  62. 5 0
      src/pages/mine/subpages/team/index.config.js
  63. 97 0
      src/pages/mine/subpages/team/index.jsx
  64. 73 0
      src/pages/mine/subpages/team/index.less
  65. 5 0
      src/pages/mine/subpages/vipDetail/index.config.js
  66. 276 0
      src/pages/mine/subpages/vipDetail/index.jsx
  67. 367 0
      src/pages/mine/subpages/vipDetail/index.less
  68. 5 0
      src/pages/mine/subpages/withdrawal/index.config.js
  69. 206 0
      src/pages/mine/subpages/withdrawal/index.jsx
  70. 169 0
      src/pages/mine/subpages/withdrawal/index.less
  71. 201 0
      src/service/index.js
  72. 19 0
      types/global.d.ts

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
1
+dist/
2
+deploy_versions/
3
+.temp/
4
+.rn_temp/
5
+node_modules/
6
+.DS_Store

+ 32 - 0
app_list.json

@@ -0,0 +1,32 @@
1
+{
2
+  "taroConfig": {
3
+    "version": "1.4.5",
4
+    "desc": "更改人物动漫化"
5
+  },
6
+  "app_list": [
7
+    {
8
+      "projectname": "AI绘画练习生",
9
+      "description": "AI绘画练习生",
10
+      "appid": "wxcf756553cfb65cb8",
11
+      "path_name": "project.config.json"
12
+    },
13
+    {
14
+      "projectname": "AI绘画实用工具",
15
+      "description": "AI绘画实用工具",
16
+      "appid": "tt93fde5400cc2d78401",
17
+      "path_name": "project.tt.json"
18
+    },
19
+    {
20
+      "projectname": "AI绘画练习生",
21
+      "description": "AI绘画练习生",
22
+      "appid": "tt8534f3891a0bf12e01",
23
+      "path_name": "project.tt.json"
24
+    },
25
+    {
26
+      "projectname": "AI绘画未来派",
27
+      "description": "AI绘画未来派",
28
+      "appid": "tta3fed5433cc1e78401",
29
+      "path_name": "project.tt.json"
30
+    }
31
+  ]
32
+}

+ 94 - 0
auto_pack.py

@@ -0,0 +1,94 @@
1
+#!/usr/bin/python3
2
+# -*- coding:utf-8 -*-
3
+# __author__ = '__spring__'
4
+
5
+'''
6
+归因上报抖音数据的脚本
7
+
8
+'''
9
+
10
+import json
11
+import os
12
+
13
+
14
+class Handle:
15
+
16
+    def __init__(self):
17
+        pass
18
+    
19
+    """
20
+    将替换的字符串写到一个新的文件中,然后将原文件删除,新文件改为原来文件的名字
21
+    :param file: 文件路径
22
+    :param old_str: 需要替换的字符串
23
+    :param new_str: 替换的字符串
24
+    :return: None
25
+    """
26
+    def updateFile(self,file,old_str,new_str):
27
+        with open(file, "r", encoding="utf-8") as f1,open("%s.bak" % file, "w", encoding="utf-8") as f2:
28
+            for line in f1:
29
+                if old_str in line:
30
+                    line = line.replace(old_str, new_str)
31
+                f2.write(line)
32
+        os.remove(file)
33
+        os.rename("%s.bak" % file, file)
34
+    
35
+    def main(self):
36
+        # 读取需要打包的app 列表
37
+        with open('./app_list.json','r') as f :
38
+            data =  f.read()
39
+        data = json.loads(data)
40
+        app_list = data["app_list"]
41
+        tar_ver = data["taroConfig"]
42
+        
43
+        #读取版本号
44
+        with open('./package.json','r') as f :
45
+            package = f.read()
46
+            package =  json.loads(package)
47
+        
48
+        #更新版本号
49
+        with open('./package.json','w') as f :
50
+            package["taroConfig"] = tar_ver
51
+            json.dump(package,f,indent=4,ensure_ascii=False)
52
+            
53
+        
54
+        #遍历需要打包的应用
55
+        for app_info in app_list:
56
+            path_name = app_info["path_name"]
57
+            #读取配置文件
58
+            with open(path_name,'r') as f:
59
+                pro_data =  f.read()
60
+                pro_data = json.loads(pro_data)
61
+            #修改配置文件
62
+            with open(path_name,'w') as f:
63
+                pro_data["appid"] = app_info["appid"]
64
+                pro_data["projectname"] = app_info["projectname"]
65
+                pro_data["description"] = app_info["description"]
66
+                json.dump(pro_data,f,indent=4,ensure_ascii=False)
67
+            
68
+            #修改抖音自定义导航栏的样式
69
+            # self.updateFile("./src/pages/albums_play/index.config.js","custom","default")
70
+            if app_info["appid"].find("wx") > -1:
71
+                #修改打包插件的appid
72
+                self.updateFile("./config/index.js","__APPID__",app_info["appid"])
73
+                #修改抖音自定义导航栏的样式
74
+                # self.updateFile("./src/pages/albums_play/index.config.js","default","custom")
75
+                
76
+                #自己添加打包插件
77
+            self.updateFile("./config/index.js","plugins:[]",'plugins:[["@tarojs/plugin-mini-ci", CIPluginOpt]]')
78
+            
79
+            #开始运行打包命令
80
+            cmd = "npm run build:weapp_upload"
81
+            if path_name.find("tt") > 0:
82
+                cmd = "npm run build:tt_upload"
83
+            os.system(cmd) #执行命令
84
+            
85
+            if app_info["appid"].find("wx") > -1:
86
+                #修改打包插件的appid
87
+                self.updateFile("./config/index.js",app_info["appid"],"__APPID__")
88
+                
89
+            self.updateFile("./config/index.js",'plugins:[["@tarojs/plugin-mini-ci", CIPluginOpt]]',"plugins:[]")
90
+            
91
+        pass
92
+
93
+if __name__=='__main__':
94
+    Handle().main()

+ 10 - 0
babel.config.js

@@ -0,0 +1,10 @@
1
+// babel-preset-taro 更多选项和默认值:
2
+// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
3
+module.exports = {
4
+  presets: [
5
+    ['taro', {
6
+      framework: 'react',
7
+      ts: true
8
+    }]
9
+  ]
10
+}

+ 9 - 0
config/dev.js

@@ -0,0 +1,9 @@
1
+module.exports = {
2
+  env: {
3
+    NODE_ENV: '"development"'
4
+  },
5
+  defineConstants: {
6
+  },
7
+  mini: {},
8
+  h5: {}
9
+}

+ 99 - 0
config/index.js

@@ -0,0 +1,99 @@
1
+
2
+const appid_weapp = '__APPID__'
3
+const CIPluginOpt = {
4
+  // 微信小程序
5
+  weapp: {
6
+    appid: appid_weapp,
7
+    privateKeyPath: `./private/private.${appid_weapp}.key`
8
+  },
9
+  // 字节跳动小程序
10
+  tt: {
11
+    email: "func@fys1001.onexmail.com",
12
+    password: "lihai1243434798"
13
+  },
14
+}
15
+
16
+
17
+const config = {
18
+  projectName: 'myApp',
19
+  date: '2023-3-2',
20
+  designWidth: 750,
21
+  deviceRatio: {
22
+    640: 2.34 / 2,
23
+    750: 1,
24
+    828: 1.81 / 2
25
+  },
26
+  sourceRoot: 'src',
27
+  outputRoot: `dist/${process.env.TARO_ENV}`,
28
+  plugins:[],
29
+  defineConstants: {
30
+  },
31
+  copy: {
32
+    patterns: [
33
+    ],
34
+    options: {
35
+    }
36
+  },
37
+  framework: 'react',
38
+  compiler: 'webpack5',
39
+  cache: {
40
+    enable: false // Webpack 持久化缓存配置,建议开启。默认配置请参考:https://docs.taro.zone/docs/config-detail#cache
41
+  },
42
+  mini: {
43
+    postcss: {
44
+      pxtransform: {
45
+        enable: true,
46
+        config: {
47
+          selectorBlackList: [/van-/],
48
+        }
49
+      },
50
+      url: {
51
+        enable: true,
52
+        config: {
53
+          limit: 1024 // 设定转换尺寸上限
54
+        }
55
+      },
56
+      cssModules: {
57
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
58
+        config: {
59
+          namingPattern: 'module', // 转换模式,取值为 global/module
60
+          generateScopedName: '[name]__[local]___[hash:base64:5]'
61
+        }
62
+      }
63
+    }
64
+  },
65
+  h5: {
66
+    publicPath: '/',
67
+    staticDirectory: 'static',
68
+    esnextModules: ['taro-ui'],
69
+    postcss: {
70
+      autoprefixer: {
71
+        enable: true,
72
+        config: {
73
+        }
74
+      },
75
+      cssModules: {
76
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
77
+        config: {
78
+          namingPattern: 'module', // 转换模式,取值为 global/module
79
+          generateScopedName: '[name]__[local]___[hash:base64:5]'
80
+        }
81
+      }
82
+    }
83
+  },
84
+  rn: {
85
+    appName: 'taroDemo',
86
+    postcss: {
87
+      cssModules: {
88
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
89
+      }
90
+    }
91
+  }
92
+}
93
+
94
+module.exports = function (merge) {
95
+  if (process.env.NODE_ENV === 'development') {
96
+    return merge({}, config, require('./dev'))
97
+  }
98
+  return merge({}, config, require('./prod'))
99
+}

+ 37 - 0
config/prod.js

@@ -0,0 +1,37 @@
1
+module.exports = {
2
+  env: {
3
+    NODE_ENV: '"production"'
4
+  },
5
+  defineConstants: {
6
+  },
7
+  mini: {},
8
+  h5: {
9
+    /**
10
+     * WebpackChain 插件配置
11
+     * @docs https://github.com/neutrinojs/webpack-chain
12
+     */
13
+    // webpackChain (chain) {
14
+    //   /**
15
+    //    * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
16
+    //    * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
17
+    //    */
18
+    //   chain.plugin('analyzer')
19
+    //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
20
+
21
+    //   /**
22
+    //    * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
23
+    //    * @docs https://github.com/chrisvfritz/prerender-spa-plugin
24
+    //    */
25
+    //   const path = require('path')
26
+    //   const Prerender = require('prerender-spa-plugin')
27
+    //   const staticDir = path.join(__dirname, '..', 'dist')
28
+    //   chain
29
+    //     .plugin('prerender')
30
+    //     .use(new Prerender({
31
+    //       staticDir,
32
+    //       routes: [ '/pages/index/index' ],
33
+    //       postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
34
+    //     }))
35
+    // }
36
+  }
37
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 46259 - 0
package-lock.json


+ 87 - 0
package.json

@@ -0,0 +1,87 @@
1
+{
2
+    "name": "myApp",
3
+    "version": "1.0.0",
4
+    "private": true,
5
+    "description": "liaotian ",
6
+    "templateInfo": {
7
+        "name": "redux",
8
+        "typescript": false,
9
+        "css": "less"
10
+    },
11
+    "scripts": {
12
+        "build:weapp": "taro build --type weapp",
13
+        "build:weapp_upload": "taro build --type weapp  --upload",
14
+        "build:swan": "taro build --type swan",
15
+        "build:alipay": "taro build --type alipay",
16
+        "build:tt": "taro build --type tt",
17
+        "build:tt_upload": "taro build --type tt  --upload",
18
+        "build:h5": "taro build --type h5",
19
+        "build:rn": "taro build --type rn",
20
+        "build:qq": "taro build --type qq",
21
+        "build:jd": "taro build --type jd",
22
+        "build:quickapp": "taro build --type quickapp",
23
+        "dev:weapp": "npm run build:weapp -- --watch",
24
+        "dev:swan": "npm run build:swan -- --watch",
25
+        "dev:alipay": "npm run build:alipay -- --watch",
26
+        "dev:tt": "npm run build:tt -- --watch",
27
+        "dev:h5": "npm run build:h5 -- --watch",
28
+        "dev:rn": "npm run build:rn -- --watch",
29
+        "dev:qq": "npm run build:qq -- --watch",
30
+        "dev:jd": "npm run build:jd -- --watch",
31
+        "dev:quickapp": "npm run build:quickapp -- --watch",
32
+        "deploy": "gh-pages -d dist"
33
+    },
34
+    "browserslist": [
35
+        "last 3 versions",
36
+        "Android >= 4.1",
37
+        "ios >= 8"
38
+    ],
39
+    "author": "",
40
+    "dependencies": {
41
+        "@babel/runtime": "^7.7.7",
42
+        "@tarojs/components": "3.5.7",
43
+        "@tarojs/helper": "3.5.7",
44
+        "@tarojs/plugin-framework-react": "3.5.7",
45
+        "@tarojs/plugin-platform-alipay": "3.5.7",
46
+        "@tarojs/plugin-platform-jd": "3.5.7",
47
+        "@tarojs/plugin-platform-qq": "3.5.7",
48
+        "@tarojs/plugin-platform-swan": "3.5.7",
49
+        "@tarojs/plugin-platform-tt": "3.5.7",
50
+        "@tarojs/plugin-platform-weapp": "3.5.7",
51
+        "@tarojs/react": "3.5.7",
52
+        "@tarojs/router": "3.5.7",
53
+        "@tarojs/runtime": "3.5.7",
54
+        "@tarojs/shared": "3.5.7",
55
+        "@tarojs/taro": "3.5.7",
56
+        "@tarojs/taro-h5": "^3.5.7",
57
+        "@vant/weapp": "^1.10.14",
58
+        "postcss": "^8.2.1",
59
+        "react": "^18.0.0",
60
+        "react-dom": "^18.0.0",
61
+        "taro-cropper": "^1.2.3",
62
+        "tt-ide-cli": "^0.1.15"
63
+    },
64
+    "devDependencies": {
65
+        "@babel/core": "^7.8.0",
66
+        "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
67
+        "@tarojs/cli": "3.5.7",
68
+        "@tarojs/plugin-mini-ci": "3.5.7",
69
+        "@tarojs/webpack5-runner": "3.5.7",
70
+        "@types/react": "^18.0.0",
71
+        "@types/webpack-env": "^1.13.6",
72
+        "babel-preset-taro": "3.5.7",
73
+        "eslint": "^8.12.0",
74
+        "eslint-config-taro": "3.5.7",
75
+        "eslint-plugin-import": "^2.12.0",
76
+        "eslint-plugin-react": "^7.8.2",
77
+        "eslint-plugin-react-hooks": "^4.2.0",
78
+        "react-refresh": "^0.11.0",
79
+        "stylelint": "^14.4.0",
80
+        "taro-ui": "^3.0.0-alpha.9",
81
+        "webpack": "5.69.0"
82
+    },
83
+    "taroConfig": {
84
+        "version": "1.4.5",
85
+        "desc": "更改人物动漫化"
86
+    }
87
+}

+ 27 - 0
private/private.wxcf756553cfb65cb8.key

@@ -0,0 +1,27 @@
1
+-----BEGIN RSA PRIVATE KEY-----
2
+MIIEpAIBAAKCAQEAtiXHmMyLeuv8l313XejSM01sTFJVU6clTXVA1qrCXjIway1z
3
+nnLW5P4get8lb2Uixojsot6Xy+b9Q+A95eCiuTDbTRtwonnP1pgxlzblkYjouGxg
4
+FNJ9ZkSTBBb4hF4Faskr3GglVt+4fLaxN2ey40op2okZ7FNRy8RT0hwpK1Aunf3n
5
+C4U+ChmSwbBT90CybPVjvcfeYbf4p25/BWa8yVogHnbzk4FhOSRYkuBhceZcjUlX
6
+6yBV3OzejtxGqy8aCD7JLOrmUeBF7wvKeapv9hvvGlZ5p4/BY9HAYf+iHGPh781/
7
+Vvww3sz6CGCz7tRjJ9YLWfzgsMydE6xJUxz0KQIDAQABAoIBAAEiSxPQci3ZukDX
8
+Lx8OjAB1wu7WtGFI8RqCWFz9wygC9/oy1j1uSZgzXNBnAzymTIkf4RlCeX9zeLAr
9
+fTcpJtowS603NoRMbdbQK77dQKCfUZOUWSTICq/QTXoBCpsIWtBHYFaG73SS//sG
10
+yCOoKZzwoUITCG5INoImPr9YHYl/e5bpKDZQ/E03md1+jyNCKOsS7TxK/5m/lXqM
11
+2hy6afFCaZgbFMf1i/8A5baXDSmLysTZNBw8zjeRw97h7vcsnbtpyHk1beVE3Zc6
12
+mbaytpcdT0BX6wLHDrpIvDvpiTwHnR19ay71oKGZweLxtFBgwjLpOe70jbe4sRVq
13
+5ImL1AECgYEA7ytsnPhoNkMQgbAH/suB1mZQHnGC1Kotx+BzQZoJsQYKXK2ujhIT
14
+1NCl25IFFWSiUWRvbRnctz9iRf4cgQr6/tKeKd3O5SxH53bb14VhYEjmP2lcQHEP
15
+eMSJJa+L9CYJG5MyCU7zt7LW4EG+5Q0EoEv1AewNmQeQ0XmqPM7fvFECgYEAwvce
16
+Y4DLEvHDXi+CSf3BQidezVEkejCFp+ioADFO0j4RvNdC0gd/JSkwPBHyCmwKHtYL
17
+gwLitPfnz1/uTHXMorhxAdLvY9iRjhix3g+gGJGqKaJwluTjuzs6eanP0qB9MZdA
18
+r4UDteQwJeJ/IuirAc+w0hmM+a9AT0w4Cbs5vFkCgYEAqOtDj+4GNGTTcLUyQGvd
19
+bN7gMVA8dhUntma5IJk5xRSeFuz4PxFXflRLADQ2cPCdj9zesfC1KZuTvhmGakGm
20
+GnoJCUrGiKfotPsGtEZZCxcRv7GbXUw9AC/XKY29zBddEWO50Ec57QFbsBHfUIkM
21
+PmbHSa89v1pPlxNWABKghLECgYBEhs1UlgFKsFVF9pbp4NoboXDt45ZZqFJiGSFr
22
+p+kXdp+Pa23eGqpy6k483uXfCehovD0hmFMM3pHIzuQYVdNOYlu+E2D3DDjk5AMo
23
+lP9lg1V2+tRuBReSSJgeCTS1yJkYZBxlVPBf70uJFVowb+0jXte8kSR+4j1NYfHU
24
+Yc1CSQKBgQCtJidMaU0+iN56nuGtOIgV/UwhhZ/v1d1ylJUcwYhB3BCCgq8oi6Rs
25
+ucFkuPlAcOodDaSj+jysSgh7mMYoBTfYPhWvehalw2yWKe9HuwcscVlw2nrx3uaR
26
+VSCO4/R1/rJ1oHcXvMdusqPH66iwkT2PPvg+NNlDpXnOu1LSyHwEIw==
27
+-----END RSA PRIVATE KEY-----

+ 29 - 0
project.config.json

@@ -0,0 +1,29 @@
1
+{
2
+    "miniprogramRoot": "dist/weapp/",
3
+    "projectname": "AI绘画练习生",
4
+    "description": "AI绘画练习生",
5
+    "appid": "wxcf756553cfb65cb8",
6
+    "setting": {
7
+        "urlCheck": true,
8
+        "es6": false,
9
+        "postcss": false,
10
+        "minified": false,
11
+        "babelSetting": {
12
+            "ignore": [],
13
+            "disablePlugins": [],
14
+            "outputPath": ""
15
+        }
16
+    },
17
+    "compileType": "miniprogram",
18
+    "libVersion": "2.26.0",
19
+    "srcMiniprogramRoot": "dist/",
20
+    "packOptions": {
21
+        "ignore": [],
22
+        "include": []
23
+    },
24
+    "condition": {},
25
+    "editorSetting": {
26
+        "tabIndent": "insertSpaces",
27
+        "tabSize": 2
28
+    }
29
+}

+ 12 - 0
project.tt.json

@@ -0,0 +1,12 @@
1
+{
2
+    "miniprogramRoot": "./",
3
+    "projectname": "指尖文学",
4
+    "appid": "ttfea9881461917a8a01",
5
+    "setting": {
6
+        "urlCheck": true,
7
+        "es6": false,
8
+        "postcss": false,
9
+        "minified": false
10
+    },
11
+    "description": "指尖文学"
12
+}

+ 8 - 0
src/api/config.js

@@ -0,0 +1,8 @@
1
+// 配置请求对象
2
+// 本地调试 dev 开发阶段
3
+// export const baseUrl = "https://test-api-book.fyshark.com";//测试
4
+export const baseUrl = "https://api-book.fyshark.com";//正式
5
+// product 阶段
6
+// https://www.fastmock.site/mock/3f112f6cb2f621fc9c2dd6a14be19f38/beers/
7
+// 设计模式
8
+

+ 52 - 0
src/api/request.js

@@ -0,0 +1,52 @@
1
+import Taro from '@tarojs/taro';
2
+import ttappid from '../../project.tt.json'
3
+import wxappid from '../../project.config.json'
4
+import {
5
+  baseUrl
6
+} from './config';
7
+
8
+
9
+export default (options = {
10
+  method: 'GET',
11
+  data: {}
12
+}) => {
13
+  const request_data = {
14
+    session_key: Taro.getStorageSync('session_key') || '',
15
+    channel_name: Taro.getStorageSync('channel_name') || 'channel_name',
16
+    source_name: Taro.getStorageSync('source_name') || 'source_name',
17
+  };
18
+  if (process.env.TARO_ENV === 'weapp') {
19
+    request_data['appid'] = wxappid.appid
20
+  } else if (process.env.TARO_ENV === 'tt') {
21
+    request_data['appid'] = ttappid.appid
22
+  }
23
+  return Taro.request({
24
+    url: baseUrl + options.url,
25
+    data: {
26
+      ...request_data,
27
+      ...options.data,
28
+    },
29
+    header: {
30
+      'Content-Type': 'application/json',
31
+      'accept': 'application/json'
32
+    },
33
+    method: options.method.toUpperCase(),
34
+  }).then(res => {
35
+    const {
36
+      statusCode,
37
+      data
38
+    } = res;
39
+    const _res = Taro.getStorageInfoSync()
40
+    let notApiStr = '/api/get_app_config;/api/login;/api/report_login;/api/video/video_detail;/api/pay/get_my_wallet;/api/get_template_list'
41
+    
42
+    if (statusCode == 200) {
43
+      return data;
44
+    } else {
45
+      Taro.showToast({
46
+        title: `${res.msg}~`,
47
+        icon: 'none',
48
+        mask: true,
49
+      });
50
+    }
51
+  });
52
+};

+ 52 - 0
src/app.config.js

@@ -0,0 +1,52 @@
1
+
2
+import Taro from '@tarojs/taro'
3
+export default defineAppConfig({
4
+  pages: [
5
+    'pages/index/index',
6
+    'pages/index/subpages/search/index',
7
+    'pages/index/subpages/novel/index',
8
+    'pages/index/subpages/novelText/index',
9
+    'pages/index/subpages/directory/index',
10
+    "pages/mine/index",
11
+    "pages/mine/subpages/history/index",
12
+    "pages/mine/subpages/vipDetail/index",
13
+    "pages/mine/subpages/makeMoney/index",
14
+    "pages/mine/subpages/withdrawal/index",
15
+    "pages/mine/subpages/team/index",
16
+    "pages/mine/subpages/moneyPlay/index",
17
+    'pages/collection/index'
18
+  ],
19
+  window: {
20
+    backgroundTextStyle: 'light',
21
+    navigationBarBackgroundColor: '#FFFFFF',
22
+    navigationBarTitleText: 'AI绘画',
23
+    navigationBarTextStyle: 'black',
24
+    backgroundColor: "#FFFFFF"
25
+  },
26
+  tabBar: {
27
+    color: "#888888",
28
+    selectedColor: "#E04A4A",
29
+    borderStyle: "white",
30
+    backgroundColor: "#FFFFFF",
31
+    list: [
32
+      {
33
+        pagePath: "pages/collection/index",
34
+        iconPath: "images/nav/like.png",
35
+        selectedIconPath: "images/nav/like_pin.png",
36
+        text: "书架"
37
+      },
38
+      {
39
+        pagePath: "pages/index/index",
40
+        iconPath: "images/nav/play.png",
41
+        selectedIconPath: "images/nav/play_pin.png",
42
+        text: "书城"
43
+      },
44
+      {
45
+        pagePath: "pages/mine/index",
46
+        iconPath: "images/nav/my.png",
47
+        selectedIconPath: "images/nav/my_pin.png",
48
+        text: "我的"
49
+      }
50
+    ]
51
+  },
52
+})

+ 55 - 0
src/app.js

@@ -0,0 +1,55 @@
1
+import { Component } from 'react'
2
+import tool from './common/tool'
3
+import Taro, { getCurrentInstance } from '@tarojs/taro'
4
+// import 'taro-ui/dist/style/index.scss'
5
+import './app.less'
6
+
7
+class App extends Component {
8
+  $instance = getCurrentInstance()
9
+  componentDidMount () { }
10
+
11
+  componentDidShow () {
12
+
13
+  }
14
+  onLaunch () {
15
+    console.log('app启动');
16
+    if (!Taro.getStorageSync('session_key')) {
17
+      tool.toLogin();
18
+    }
19
+    
20
+    // 保持屏幕常亮
21
+    Taro.setKeepScreenOn({
22
+      keepScreenOn: true
23
+    })
24
+    // 获取路由参数
25
+    let params = this.$instance.router.params
26
+    if (params.scene) {
27
+      let _scene = decodeURIComponent(params.scene)
28
+      const arr = _scene.split("=");
29
+      const result1 = arr[1].split("&")[0]; // 去掉第二个等号后面的"&"符号
30
+      const result2 = arr[2]; // 第三个等号后面的内容
31
+      Taro.setStorageSync('channel_name', result1)
32
+      Taro.setStorageSync('source_name', result2)
33
+    }
34
+    let item = params
35
+    if (params.c) {
36
+      Taro.setStorageSync('channel_name', item.c)
37
+    }
38
+    if (params.s) {
39
+      Taro.setStorageSync('source_name', item.s)
40
+    }
41
+    tool.reportLogin()
42
+    Taro.setKeepScreenOn({
43
+      keepScreenOn: true
44
+    })
45
+  }
46
+
47
+  componentDidHide () { }
48
+
49
+  render () {
50
+    // this.props.children 是将要会渲染的页面
51
+    return this.props.children
52
+  }
53
+}
54
+
55
+export default App

+ 0 - 0
src/app.less


+ 133 - 0
src/common/tool.js

@@ -0,0 +1,133 @@
1
+import Taro from '@tarojs/taro'
2
+import * as api from '../service/index'
3
+const tool = {
4
+  shareTitle: '一款属于自己的AI绘画小程序,快来创作属于自己的独家作品吧。',
5
+  shareImg: 'http://video-img.fyshark.com/1680599995457%E5%88%86%E4%BA%AB%E5%9B%BE%20200-200.png',
6
+  //去登录
7
+  toLogin: function (rescallback = () => { }) {
8
+    Taro.login({
9
+      success: function (res) {
10
+        let params = {
11
+          code: res.code,
12
+        }
13
+        console.log(res.code, 'res.code');
14
+        if (res.code) {
15
+          api.login(params).then(item => {
16
+            Taro.setStorage({
17
+              key: "session_key",
18
+              data: item.data.session_key
19
+            })
20
+            Taro.setStorage({
21
+              key: "user_id",
22
+              data: item.data.id
23
+            })
24
+          })
25
+        } else {
26
+        }
27
+      }
28
+    })
29
+  },
30
+  //上报渠道信息
31
+  reportLogin:function(){
32
+    api.reportLogin()
33
+  },
34
+  // 生成四位随机数的函数
35
+  generateRandomNumber () {
36
+    // 生成一个 0 到 9999 之间的随机整数
37
+    const randomNumber = Math.floor(Math.random() * 10000);
38
+    // 如果随机数不足四位,则在前面补零
39
+    return randomNumber.toString().padStart(4, '0');
40
+  },
41
+  //判断不为安卓
42
+  notAndroid: function () {
43
+    let is_ios = false;
44
+    Taro.getSystemInfo({
45
+      success: function (res) {
46
+        if (res.platform != "android") {
47
+          is_ios = true
48
+        }
49
+      }
50
+    })
51
+    console.log(is_ios, 'is_ios');
52
+    return is_ios;
53
+  },
54
+  //判断是否为安卓
55
+  isAndroid: function () {
56
+    let is_android = false;
57
+    Taro.getSystemInfo({
58
+      success: function (res) {
59
+        if (res.platform == "android") {
60
+          is_android = true
61
+        }
62
+      }
63
+    })
64
+    return is_android;
65
+  },
66
+  // 生成用户ID的函数
67
+  generateUserID () {
68
+    // 获取当前时间戳
69
+    const timestamp = Date.now().toString();
70
+    // 生成四位随机数
71
+    const randomNumber = this.generateRandomNumber();
72
+    // 将时间戳和随机数拼接起来,得到用户ID
73
+    const userID = timestamp + randomNumber;
74
+    console.log(userID, 'userID');
75
+    return userID;
76
+  },
77
+  /**
78
+* 限制字符串长度
79
+* value:字符串对象
80
+* lmit:展示的长度
81
+*/
82
+  ellipsis: function (value, lmit) {
83
+    if (!value) return ''
84
+    if (value.length > lmit) {
85
+      return value.slice(0, lmit) + '...'
86
+    }
87
+    return value
88
+  },
89
+
90
+  //简化版js节流,默认2s内只能点击一次
91
+  throttle: function (fn, wait = 2000) {
92
+    let timer = null;
93
+    let lastTime = 0;
94
+    console.log('节流');
95
+    return function (...args) {
96
+      const now = +new Date();
97
+
98
+      if (now - lastTime > wait) {
99
+        lastTime = now;
100
+        fn.apply(this, args);
101
+      } else {
102
+        clearTimeout(timer);
103
+        timer = setTimeout(() => {
104
+          lastTime = now;
105
+          fn.apply(this, args);
106
+        }, wait - (now - lastTime));
107
+      }
108
+    };
109
+  },
110
+  //标准时间转年月日
111
+  formattedDate: function (time) {
112
+    // 创建一个表示中国标准时间的日期对象
113
+    var date = new Date(time);
114
+
115
+    // 提取年、月、日信息
116
+    var year = date.getFullYear();
117
+    var month = date.getMonth() + 1; // 月份从0开始,因此需要加1
118
+    var day = date.getDate();
119
+
120
+    // 将月份和日期转换为两位数格式
121
+    if (month < 10) {
122
+      month = "0" + month;
123
+    }
124
+    if (day < 10) {
125
+      day = "0" + day;
126
+    }
127
+
128
+    // 构建年月日字符串
129
+    var formattedDate = year + "-" + month + "-" + day + "";
130
+    return formattedDate
131
+  }
132
+}
133
+export default tool

+ 16 - 0
src/component/taro-plugin-canvas/index.css

@@ -0,0 +1,16 @@
1
+.canvas {
2
+  width: 750rpx;
3
+  height: 750rpx;
4
+}
5
+.canvas.pro {
6
+  position: absolute;
7
+  bottom: 0;
8
+  left: 0;
9
+  transform: translate3d(-9999rpx, 0, 0);
10
+}
11
+.canvas.debug {
12
+  position: absolute;
13
+  bottom: 0;
14
+  left: 0;
15
+  border: 1rpx solid #ccc;
16
+}

+ 322 - 0
src/component/taro-plugin-canvas/index.tsx

@@ -0,0 +1,322 @@
1
+import Taro, { CanvasContext } from '@tarojs/taro';
2
+import { Component } from 'react'
3
+import PropTypes from 'prop-types';
4
+import { Canvas } from '@tarojs/components';
5
+import { randomString, getHeight, downloadImageAndInfo } from './utils/tools';
6
+import {
7
+  drawImage,
8
+  drawText,
9
+  drawBlock,
10
+  drawLine,
11
+} from './utils/draw';
12
+import { IConfig, IIMage } from './types';
13
+import './index.css';
14
+
15
+interface ICanvasDrawerProps {
16
+  config: IConfig;
17
+  onCreateSuccess: (res: any) => void;
18
+  onCreateFail: (err: Error) => void;
19
+}
20
+
21
+interface ICanvasDrawerState {
22
+  pxWidth: number;
23
+  pxHeight: number;
24
+  debug: boolean;
25
+  factor: number;
26
+  pixelRatio: number;
27
+}
28
+
29
+
30
+let count = 1;
31
+export default class CanvasDrawer extends Component<ICanvasDrawerProps, ICanvasDrawerState> {
32
+  cache: any;
33
+  drawArr: any[];
34
+  canvasId: string;
35
+  ctx: CanvasContext | null;
36
+
37
+  static propTypes = {
38
+    config: PropTypes.object.isRequired,
39
+    onCreateSuccess: PropTypes.func.isRequired,
40
+    onCreateFail: PropTypes.func.isRequired,
41
+  };
42
+
43
+  static defaultProps = {};
44
+
45
+  constructor(props) {
46
+    super(props);
47
+    this.state = {
48
+      pxWidth: 0,
49
+      pxHeight: 0,
50
+      debug: false,
51
+      factor: 0,
52
+      pixelRatio: 1,
53
+    }
54
+    this.canvasId = randomString(10);
55
+    this.ctx = null;
56
+    this.cache = {};
57
+    this.drawArr = [];
58
+  }
59
+
60
+  componentWillMount() {
61
+    const { config } = this.props;
62
+    const height = getHeight(config);
63
+    this.initCanvas(config.width, height, config.debug);
64
+  }
65
+
66
+  componentDidMount() {
67
+    const sysInfo = Taro.getSystemInfoSync();
68
+    const screenWidth = sysInfo.screenWidth;
69
+    this.setState({
70
+      factor: screenWidth / 750
71
+    })
72
+    this.onCreate();
73
+  }
74
+
75
+  componentWillUnmount() { }
76
+
77
+  /**
78
+   * @description rpx => px 基础方法
79
+   * @param { number } rpx - 需要转换的数值
80
+   * @param { boolean} int - 是否为 int
81
+   * @param { number } [factor = this.state.factor] - 转化因子
82
+   * @returns { number }
83
+   */
84
+  toPx = (rpx: number, int: boolean = false, factor: number = this.state.factor) => {
85
+    if (int) {
86
+      return Math.ceil(rpx * factor * this.state.pixelRatio);
87
+    }
88
+    return rpx * factor * this.state.pixelRatio;
89
+  }
90
+  /**
91
+   * @description px => rpx
92
+   * @param { number } px - 需要转换的数值
93
+   * @param { boolean} int - 是否为 int
94
+   * @param { number } [factor = this.state.factor] - 转化因子
95
+   * @returns { number }
96
+   */
97
+  toRpx = (px: number, int: boolean = false, factor: number = this.state.factor) => {
98
+    if (int) {
99
+      return Math.ceil(px / factor);
100
+    }
101
+    return px / factor;
102
+  }
103
+
104
+  /**
105
+   * @description 下载图片并获取图片信息
106
+   * @param  {} image
107
+   * @param  {} index
108
+   */
109
+  _downloadImageAndInfo = (image: IIMage, index: number, pixelRatio: number) => {
110
+    return new Promise<any>((resolve, reject) => {
111
+      downloadImageAndInfo(image, index, this.toRpx, pixelRatio)
112
+        .then(
113
+          (result) => {
114
+            this.drawArr.push(result);
115
+            resolve(result);
116
+          }
117
+        )
118
+        .catch(err => {
119
+          console.log(err);
120
+          reject(err)
121
+        });
122
+    })
123
+  }
124
+  /**
125
+   * @param  {} images=[]
126
+   */
127
+  downloadResource = ({ images = [], pixelRatio = 1 }: { images: IIMage[], pixelRatio: number }) => {
128
+    const drawList: any[] = [];
129
+    let imagesTemp = images;
130
+
131
+    imagesTemp.forEach((image, index) => drawList.push(this._downloadImageAndInfo(image, index, pixelRatio)));
132
+
133
+    return Promise.all(drawList);
134
+  }
135
+
136
+  /**
137
+   * @param
138
+   */
139
+  downloadResourceTransit = () => {
140
+    const { config } = this.props;
141
+    return new Promise<any>((resolve, reject) => {
142
+      if (config.images && config.images.length > 0) {
143
+        this.downloadResource({
144
+          images: config.images,
145
+          pixelRatio: config.pixelRatio || 1,
146
+        })
147
+          .then(() => {
148
+            resolve();
149
+          })
150
+          .catch((e) => {
151
+            // console.log(e);
152
+            reject(e)
153
+          });
154
+      } else {
155
+        setTimeout(() => {
156
+          resolve(1);
157
+        }, 500)
158
+      }
159
+    })
160
+  }
161
+
162
+  initCanvas = (w, h, debug) => {
163
+    return new Promise<void>((resolve) => {
164
+      this.setState({
165
+        pxWidth: this.toPx(w),
166
+        pxHeight: this.toPx(h),
167
+        debug,
168
+      }, resolve);
169
+    });
170
+  }
171
+
172
+  onCreate = () => {
173
+    const { onCreateFail, config } = this.props;
174
+    if (config['hide-loading'] === false) {
175
+      Taro.showLoading({ mask: true, title: '生成中...' });
176
+    }
177
+    return this.downloadResourceTransit()
178
+      .then(() => {
179
+        this.create(config);
180
+      })
181
+      .catch((err) => {
182
+        config['hide-loading'] && Taro.hideLoading();
183
+        Taro.showToast({ icon: 'none', title: err.errMsg || '下载图片失败' });
184
+        console.error(err);
185
+        if (!onCreateFail) {
186
+          console.warn('您必须实现 taro-plugin-canvas 组件的 onCreateFail 方法,详见文档 https://github.com/chuyun/taro-plugin-canvas#fail');
187
+        }
188
+        onCreateFail && onCreateFail(err);
189
+      })
190
+  }
191
+
192
+  create = (config) => {
193
+    this.ctx = Taro.createCanvasContext(this.canvasId, this.$scope);
194
+    const height = getHeight(config);
195
+    // 设置 pixelRatio
196
+    this.setState({
197
+      pixelRatio: config.pixelRatio || 1,
198
+    }, () => {
199
+      this.initCanvas(config.width, height, config.debug)
200
+        .then(() => {
201
+          // 设置画布底色
202
+          if (config.backgroundColor) {
203
+            this.ctx!.save();
204
+            this.ctx!.setFillStyle(config.backgroundColor);
205
+            this.ctx!.fillRect(0, 0, this.toPx(config.width), this.toPx(height));
206
+            this.ctx!.restore();
207
+          }
208
+          const {
209
+            texts = [],
210
+            // images = [],
211
+            blocks = [],
212
+            lines = [],
213
+          } = config;
214
+          const queue = this.drawArr
215
+            .concat(texts.map((item) => {
216
+              item.type = 'text';
217
+              item.zIndex = item.zIndex || 0;
218
+              return item;
219
+            }))
220
+            .concat(blocks.map((item) => {
221
+              item.type = 'block';
222
+              item.zIndex = item.zIndex || 0;
223
+              return item;
224
+            }))
225
+            .concat(lines.map((item) => {
226
+              item.type = 'line';
227
+              item.zIndex = item.zIndex || 0;
228
+              return item;
229
+            }));
230
+          // 按照顺序排序
231
+          queue.sort((a, b) => a.zIndex - b.zIndex);
232
+
233
+          queue.forEach((item) => {
234
+            let drawOptions = {
235
+              ctx: (this.ctx as CanvasContext),
236
+              toPx: this.toPx,
237
+              toRpx: this.toRpx,
238
+            }
239
+            if (item.type === 'image') {
240
+              if (drawOptions.ctx !== null) {
241
+                drawImage(item, drawOptions);
242
+              }
243
+            } else if (item.type === 'text') {
244
+              if (drawOptions.ctx !== null) {
245
+                drawText(item, drawOptions)
246
+              }
247
+            } else if (item.type === 'block') {
248
+              if (drawOptions.ctx !== null) {
249
+                drawBlock(item, drawOptions)
250
+              }
251
+
252
+            } else if (item.type === 'line') {
253
+              if (drawOptions.ctx !== null) {
254
+                drawLine(item, drawOptions)
255
+              }
256
+            }
257
+          });
258
+
259
+          const res = Taro.getSystemInfoSync();
260
+          const platform = res.platform;
261
+          let time = 0;
262
+          if (platform === 'android') {
263
+            // 在安卓平台,经测试发现如果海报过于复杂在转换时需要做延时,要不然样式会错乱
264
+            time = 300;
265
+          }
266
+          this.ctx!.draw(false, () => {
267
+            setTimeout(() => {
268
+              this.getTempFile(null);
269
+            }, time);
270
+          });
271
+        })
272
+        .catch((err) => {
273
+          Taro.showToast({ icon: 'none', title: err.errMsg || '生成失败' });
274
+          console.error(err);
275
+        });
276
+    });
277
+
278
+  }
279
+
280
+  getTempFile = (otherOptions) => {
281
+    const { onCreateSuccess, onCreateFail } = this.props;
282
+    Taro.canvasToTempFilePath({
283
+      canvasId: this.canvasId,
284
+      success: (result) => {
285
+        if (!onCreateSuccess) {
286
+          console.warn('您必须实现 taro-plugin-canvas 组件的 onCreateSuccess 方法,详见文档 https://github.com/chuyun/taro-plugin-canvas#success');
287
+        }
288
+        onCreateSuccess && onCreateSuccess(result);
289
+      },
290
+      fail: (error) => {
291
+        const { errMsg } = error;
292
+        console.log(errMsg)
293
+        if (errMsg === 'canvasToTempFilePath:fail:create bitmap failed') {
294
+          count += 1;
295
+          if (count <= 3) {
296
+            this.getTempFile(otherOptions);
297
+          } else {
298
+            if (!onCreateFail) {
299
+              console.warn('您必须实现 taro-plugin-canvas 组件的 onCreateFail 方法,详见文档 https://github.com/chuyun/taro-plugin-canvas#fail');
300
+            }
301
+            onCreateFail && onCreateFail(error);
302
+          }
303
+        }
304
+      },
305
+    }, this.$scope);
306
+  }
307
+
308
+  render() {
309
+    const { pxWidth, pxHeight, debug } = this.state;
310
+    if (pxWidth && pxHeight) {
311
+      return (
312
+        <Canvas
313
+          canvasId={this.canvasId}
314
+          style={`width:${pxWidth}px; height:${pxHeight}px;`}
315
+          className={`${debug ? 'debug' : 'pro'} canvas`}
316
+        />
317
+      );
318
+    }
319
+    return null;
320
+  }
321
+}
322
+

+ 84 - 0
src/component/taro-plugin-canvas/types.ts

@@ -0,0 +1,84 @@
1
+
2
+export interface IConfig {
3
+  width: number;
4
+  height: number;
5
+  backgroundColor?: string;
6
+  debug?: boolean;
7
+  pixelRatio?: number;
8
+  preload?: boolean;
9
+  'hide-loading'?: boolean;
10
+  blocks?: IBlock[];
11
+  texts?: IText[];
12
+  images?: IIMage[];
13
+  lines?: ILine[];
14
+}
15
+
16
+
17
+export interface IBlock {
18
+  x: number;
19
+  y: number;
20
+  width?: number;
21
+  height: number;
22
+  paddingLeft?: number;
23
+  paddingRight?: number;
24
+  borderWidth?: number;
25
+  borderColor?: string;
26
+  backgroundColor?: string;
27
+  borderRadius?: number;
28
+  text?: IText;
29
+  opacity?: number;
30
+  zIndex?: number;
31
+}
32
+
33
+export interface IText {
34
+  x: number;
35
+  y: number;
36
+  text: string | {
37
+    text: string;
38
+    marginLeft: number;
39
+    marginRight: number;
40
+  };
41
+  fontSize: number;
42
+  color?: string;
43
+  opacity?: 1 | 0;
44
+  lineHeight?: number;
45
+  lineNum?: number;
46
+  width?: number;
47
+  marginLeft?: number;
48
+  marginRight?: number;
49
+  textDecoration?: 'line-through' | 'none';
50
+  baseLine?: 'top' | 'middle' | 'bottom';
51
+  textAlign?: 'left' | 'center' | 'right';
52
+  fontFamily?: string;
53
+  fontWeight?: string;
54
+  fontStyle?: string;
55
+  zIndex?: number;
56
+}
57
+
58
+export interface IIMage {
59
+  x: number;
60
+  y: number;
61
+  url: string;
62
+  width: number;
63
+  height: number;
64
+  borderRadius?: number;
65
+  borderWidth?: number;
66
+  borderColor?: string;
67
+  zIndex?: number;
68
+}
69
+
70
+export interface ILine {
71
+  startX: number;
72
+  startY: number;
73
+  endX: number;
74
+  endY: number;
75
+  width: number;
76
+  color?: string;
77
+  zIndex?: number;
78
+}
79
+
80
+export type IDrawType = 'text' | 'image' | 'block' | 'line';
81
+
82
+export type IDrawArrayItem = {
83
+  type?: IDrawType;
84
+} & (ILine | IIMage | IBlock | IText);

+ 394 - 0
src/component/taro-plugin-canvas/utils/draw.ts

@@ -0,0 +1,394 @@
1
+
2
+import { CanvasContext } from "@tarojs/taro";
3
+import { IText, IIMage, ILine, IBlock } from '../types';
4
+
5
+export interface IDrawRadiusRectData {
6
+  x: number;
7
+  y: number;
8
+  w: number;
9
+  h: number;
10
+  r: number;
11
+}
12
+
13
+export interface IDrawOptions {
14
+  ctx: CanvasContext;
15
+  toPx: (rpx: number, int?: boolean, factor?: number) => number;
16
+  toRpx: (px: number, int?: boolean, factor?: number) => number;
17
+}
18
+
19
+/**
20
+  * @description 绘制圆角矩形
21
+  * @param { object } drawData - 绘制数据
22
+  * @param { number } drawData.x - 左上角x坐标
23
+  * @param { number } drawData.y - 左上角y坐标
24
+  * @param { number } drawData.w - 矩形的宽
25
+  * @param { number } drawData.h - 矩形的高
26
+  * @param { number } drawData.r - 圆角半径
27
+  */
28
+export function _drawRadiusRect(drawData: IDrawRadiusRectData, drawOptions: IDrawOptions) {
29
+  const { x, y, w, h, r } = drawData;
30
+  const {
31
+    ctx,
32
+    toPx,
33
+    // toRpx,
34
+  } = drawOptions;
35
+  const br = r / 2;
36
+  ctx.beginPath();
37
+  ctx.moveTo(toPx(x + br), toPx(y));    // 移动到左上角的点
38
+  ctx.lineTo(toPx(x + w - br), toPx(y));
39
+  ctx.arc(toPx(x + w - br), toPx(y + br), toPx(br), 2 * Math.PI * (3 / 4), 2 * Math.PI * (4 / 4))
40
+  ctx.lineTo(toPx(x + w), toPx(y + h - br));
41
+  ctx.arc(toPx(x + w - br), toPx(y + h - br), toPx(br), 0, 2 * Math.PI * (1 / 4))
42
+  ctx.lineTo(toPx(x + br), toPx(y + h));
43
+  ctx.arc(toPx(x + br), toPx(y + h - br), toPx(br), 2 * Math.PI * (1 / 4), 2 * Math.PI * (2 / 4))
44
+  ctx.lineTo(toPx(x), toPx(y + br));
45
+  ctx.arc(toPx(x + br), toPx(y + br), toPx(br), 2 * Math.PI * (2 / 4), 2 * Math.PI * (3 / 4))
46
+}
47
+
48
+
49
+/**
50
+ * @description 计算文本长度
51
+ * @param { Array | Object } text 数组 或者 对象
52
+ */
53
+export function _getTextWidth(_text: IText | IText[], drawOptions: IDrawOptions): number {
54
+  const { ctx, toPx, toRpx } = drawOptions;
55
+  let texts: IText[] = [];
56
+  if (Array.isArray(_text)) {
57
+    texts = _text;
58
+  } else {
59
+    texts.push(_text);
60
+  }
61
+  let width = 0;
62
+  texts.forEach(({
63
+    fontSize,
64
+    text,
65
+    marginLeft = 0,
66
+    marginRight = 0
67
+  }) => {
68
+    ctx.setFontSize(toPx(fontSize));
69
+    let _textWidth = 0;
70
+    if (typeof text === 'object') {
71
+      _textWidth = ctx.measureText(text.text).width + text.marginLeft + text.marginRight;
72
+    } else {
73
+      _textWidth = ctx.measureText(text).width;
74
+    }
75
+    width += _textWidth + marginLeft + marginRight;
76
+  })
77
+  return toRpx(width);
78
+}
79
+
80
+
81
+/**
82
+  * @description 渲染一段文字
83
+  * @param { object } drawData - 绘制数据
84
+  * @param { number } drawData.x - x坐标 rpx
85
+  * @param { number } drawData.y - y坐标 rpx
86
+  * @param { number } drawData.fontSize - 文字大小 rpx
87
+  * @param { number } [drawData.color] - 颜色
88
+  * @param { string } [drawData.baseLine] - 基线对齐方式 top| middle|bottom
89
+  * @param { string } [drawData.textAlign='left'] - 对齐方式 left|center|right
90
+  * @param { string } drawData.text - 当Object类型时,参数为 text 字段的参数,marginLeft、marginRight这两个字段可用
91
+  * @param { number } [drawData.opacity=1] - 1为不透明,0为透明
92
+  * @param { string } [drawData.textDecoration='none']
93
+  * @param { number } [drawData.width] - 文字宽度 没有指定为画布宽度
94
+  * @param { number } [drawData.lineNum=1] - 根据宽度换行,最多的行数
95
+  * @param { number } [drawData.lineHeight=0] - 行高
96
+  * @param { string } [drawData.fontWeight='normal'] - 'bold' 加粗字体,目前小程序不支持 100 - 900 加粗
97
+  * @param { string } [drawData.fontStyle='normal'] - 'italic' 倾斜字体
98
+  * @param { string } [drawData.fontFamily="sans-serif"] - 小程序默认字体为 'sans-serif', 请输入小程序支持的字体
99
+  */
100
+
101
+interface IDrawSingleTextData extends IText {
102
+}
103
+export function _drawSingleText(drawData: IDrawSingleTextData, drawOptions: IDrawOptions) {
104
+  let { x, y, fontSize, color, baseLine, textAlign = 'left', text, opacity = 1, textDecoration = 'none',
105
+    width = 0, lineNum = 1, lineHeight = 0, fontWeight = 'normal', fontStyle = 'normal', fontFamily = "sans-serif" } = drawData;
106
+  const { ctx, toPx } = drawOptions;
107
+  ctx.save();
108
+  ctx.beginPath();
109
+  ctx.font = fontStyle + " " + fontWeight + " " + toPx(fontSize, true) + "px " + fontFamily
110
+  ctx.setGlobalAlpha(opacity);
111
+  // ctx.setFontSize(toPx(fontSize));
112
+  if (typeof text === 'object') {
113
+    text = text.text
114
+  }
115
+
116
+  color && ctx.setFillStyle(color);
117
+  baseLine && ctx.setTextBaseline(baseLine);
118
+  ctx.setTextAlign(textAlign);
119
+  let textWidth = (ctx.measureText(text as string).width);
120
+  const textArr: string[] = [];
121
+  let drawWidth = toPx(width);
122
+  if (width && textWidth > drawWidth) {
123
+    // 文本宽度 大于 渲染宽度
124
+    let fillText = '';
125
+    let line = 1;
126
+    for (let i = 0; i <= (text as string).length - 1; i++) {  // 将文字转为数组,一行文字一个元素
127
+      fillText = fillText + text[i];
128
+      if ((ctx.measureText(fillText).width) >= drawWidth) {
129
+        if (line === lineNum) {
130
+          if (i !== (text as string).length - 1) {
131
+            fillText = fillText.substring(0, fillText.length - 1) + '...';
132
+          }
133
+        }
134
+        if (line <= lineNum) {
135
+          textArr.push(fillText);
136
+        }
137
+        fillText = '';
138
+        line++;
139
+      } else {
140
+        if (line <= lineNum) {
141
+          if (i === (text as string).length - 1) {
142
+            textArr.push(fillText);
143
+          }
144
+        }
145
+      }
146
+    }
147
+    textWidth = width;
148
+  } else {
149
+    textArr.push(text as string);
150
+  }
151
+
152
+  textArr.forEach((item, index) => {
153
+    ctx.fillText(item, toPx(x), toPx(y + (lineHeight || fontSize) * index));
154
+  })
155
+  ctx.restore();
156
+  // textDecoration
157
+  if (textDecoration !== 'none') {
158
+    let lineY = y;
159
+    if (textDecoration === 'line-through') {
160
+      // 目前只支持贯穿线
161
+      lineY = y;
162
+      // 小程序画布baseLine偏移阈值
163
+      let threshold = 5;
164
+
165
+      // 根据baseLine的不同对贯穿线的Y坐标做相应调整
166
+      switch (baseLine) {
167
+        case 'top':
168
+          lineY += fontSize / 2 + threshold;
169
+          break;
170
+        case 'middle':
171
+          break;
172
+        case 'bottom':
173
+          lineY -= fontSize / 2 + threshold;
174
+          break;
175
+        default:
176
+          lineY -= fontSize / 2 - threshold;
177
+          break;
178
+      }
179
+    }
180
+    ctx.save();
181
+    ctx.moveTo(toPx(x), toPx(lineY));
182
+    ctx.lineTo(toPx(x) + toPx(textWidth), toPx(lineY));
183
+    color && ctx.setStrokeStyle(color);
184
+    ctx.stroke();
185
+    ctx.restore();
186
+  }
187
+  return textWidth;
188
+}
189
+
190
+/**
191
+ * 渲染文字
192
+ * @param { object } params - 绘制数据
193
+ * @param { number } params.x - x坐标 rpx
194
+ * @param { number } params.y - y坐标 rpx
195
+ * @param { number } params.fontSize - 文字大小 rpx
196
+ * @param { number } [params.color] - 颜色
197
+ * @param { string } [params.baseLine] - 基线对齐方式 top| middle|bottom
198
+ * @param { string } [params.textAlign='left'] - 对齐方式 left|center|right
199
+ * @param { string } params.text - 当Object类型时,参数为 text 字段的参数,marginLeft、marginRight这两个字段可用
200
+ * @param { number } [params.opacity=1] - 1为不透明,0为透明
201
+ * @param { string } [params.textDecoration='none']
202
+ * @param { number } [params.width] - 文字宽度 没有指定为画布宽度
203
+ * @param { number } [params.lineNum=1] - 根据宽度换行,最多的行数
204
+ * @param { number } [params.lineHeight=0] - 行高
205
+ * @param { string } [params.fontWeight='normal'] - 'bold' 加粗字体,目前小程序不支持 100 - 900 加粗
206
+ * @param { string } [params.fontStyle='normal'] - 'italic' 倾斜字体
207
+ * @param { string } [params.fontFamily="sans-serif"] - 小程序默认字体为 'sans-serif', 请输入小程序支持的字体
208
+ */
209
+export function drawText(params: IText, drawOptions: IDrawOptions) {
210
+  // const { ctx, toPx, toRpx } = drawOptions;
211
+  const {
212
+    x,
213
+    y, text, baseLine,
214
+    // fontSize,
215
+    // color,
216
+    // textAlign,
217
+    // opacity = 1,
218
+    // width,
219
+    // lineNum,
220
+    // lineHeight
221
+  } = params;
222
+  if (Array.isArray(text)) {
223
+    let preText = { x, y, baseLine };
224
+    text.forEach(item => {
225
+      preText.x += item.marginLeft || 0;
226
+      const textWidth = _drawSingleText(Object.assign(item, {
227
+        ...preText,
228
+      }), drawOptions);
229
+      preText.x += textWidth + (item.marginRight || 0); // 下一段字的 x 轴为上一段字 x + 上一段字宽度
230
+    })
231
+  } else {
232
+    _drawSingleText(params, drawOptions);
233
+  }
234
+}
235
+
236
+export interface IDrawImageData extends IIMage {
237
+  imgPath: string;
238
+  w: number;
239
+  h: number;
240
+  sx: number;
241
+  sy: number;
242
+  sw: number;
243
+  sh: number;
244
+}
245
+
246
+/**
247
+ * @description 渲染图片
248
+ * @param { object } data
249
+ * @param { number } x - 图像的左上角在目标 canvas 上 x 轴的位置
250
+ * @param { number } y - 图像的左上角在目标 canvas 上 y 轴的位置
251
+ * @param { number } w - 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
252
+ * @param { number } h - 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
253
+ * @param { number } sx - 源图像的矩形选择框的左上角 x 坐标
254
+ * @param { number } sy - 源图像的矩形选择框的左上角 y 坐标
255
+ * @param { number } sw - 源图像的矩形选择框的宽度
256
+ * @param { number } sh - 源图像的矩形选择框的高度
257
+ * @param { number } [borderRadius=0] - 圆角
258
+ * @param { number } [borderWidth=0] - 边框
259
+ */
260
+export function drawImage(data: IDrawImageData, drawOptions: IDrawOptions) {
261
+  const { ctx, toPx } = drawOptions;
262
+  const { imgPath, x, y, w, h, sx, sy, sw, sh, borderRadius = 0, borderWidth = 0, borderColor } = data;
263
+  ctx.save();
264
+  if (borderRadius > 0) {
265
+    let drawData = {
266
+      x, y, w, h,
267
+      r: borderRadius
268
+    };
269
+    _drawRadiusRect(drawData, drawOptions);
270
+    ctx.strokeStyle = 'rgba(255,255,255,0)';
271
+    ctx.stroke();
272
+    ctx.clip();
273
+    ctx.drawImage(imgPath, toPx(sx), toPx(sy), toPx(sw), toPx(sh), toPx(x), toPx(y), toPx(w), toPx(h));
274
+    if (borderWidth > 0) {
275
+      borderColor && ctx.setStrokeStyle(borderColor);
276
+      ctx.setLineWidth(toPx(borderWidth));
277
+      ctx.stroke();
278
+    }
279
+  } else {
280
+    ctx.drawImage(imgPath, toPx(sx), toPx(sy), toPx(sw), toPx(sh), toPx(x), toPx(y), toPx(w), toPx(h));
281
+  }
282
+  ctx.restore();
283
+}
284
+
285
+/**
286
+ * @description 渲染线
287
+ * @param  { number } startX - 起始坐标
288
+ * @param  { number } startY - 起始坐标
289
+ * @param  { number } endX - 终结坐标
290
+ * @param  { number } endY - 终结坐标
291
+ * @param  { number } width - 线的宽度
292
+ * @param  { string } [color] - 线的颜色
293
+ */
294
+export function drawLine(drawData: ILine, drawOptions: IDrawOptions) {
295
+  const { startX, startY, endX, endY, color, width } = drawData;
296
+  const { ctx, toPx } = drawOptions;
297
+  ctx.save();
298
+  ctx.beginPath();
299
+  color && ctx.setStrokeStyle(color);
300
+  ctx.setLineWidth(toPx(width));
301
+  ctx.moveTo(toPx(startX), toPx(startY));
302
+  ctx.lineTo(toPx(endX), toPx(endY));
303
+  ctx.stroke();
304
+  ctx.closePath();
305
+  ctx.restore();
306
+}
307
+
308
+/**
309
+* @description 渲染块
310
+* @param  { number } x - x坐标
311
+* @param  { number } y - y坐标
312
+* @param  { number } height -高
313
+* @param  { string|object } [text] - 块里面可以填充文字,参考texts字段
314
+* @param  { number } [width=0] - 宽 如果内部有文字,由文字宽度和内边距决定
315
+* @param  { number } [paddingLeft=0] - 内左边距
316
+* @param  { number } [paddingRight=0] - 内右边距
317
+* @param  { number } [borderWidth] - 边框宽度
318
+* @param  { string } [backgroundColor] - 背景颜色
319
+* @param  { string } [borderColor] - 边框颜色
320
+* @param  { number } [borderRadius=0] - 圆角
321
+* @param  { number } [opacity=1] - 透明度
322
+*
323
+*/
324
+export function drawBlock(blockData: IBlock, drawOptions: IDrawOptions) {
325
+  const { ctx, toPx, } = drawOptions;
326
+  const { text, width = 0, height, x, y, paddingLeft = 0, paddingRight = 0, borderWidth, backgroundColor, borderColor, borderRadius = 0, opacity = 1 } = blockData;
327
+  // 判断是否块内有文字
328
+  let blockWidth = 0; // 块的宽度
329
+  let textX = 0;
330
+  let textY = 0;
331
+  if (typeof text !== 'undefined') {
332
+    // 如果有文字并且块的宽度小于文字宽度,块的宽度为 文字的宽度 + 内边距
333
+    // const textWidth = _getTextWidth(typeof text.text === 'string' ? text : text.text, drawOptions);
334
+    const textWidth: number = _getTextWidth(text, drawOptions);
335
+    blockWidth = textWidth > width ? textWidth : width;
336
+    blockWidth += paddingLeft + paddingLeft;
337
+
338
+    const {
339
+      textAlign = 'left',
340
+      // text: textCon,
341
+    } = text;
342
+    textY = height / 2 + y; // 文字的y轴坐标在块中线
343
+    if (textAlign === 'left') {
344
+      // 如果是右对齐,那x轴在块的最左边
345
+      textX = x + paddingLeft;
346
+    } else if (textAlign === 'center') {
347
+      textX = blockWidth / 2 + x;
348
+    } else {
349
+      textX = x + blockWidth - paddingRight;
350
+    }
351
+  } else {
352
+    blockWidth = width;
353
+  }
354
+
355
+  if (backgroundColor) {
356
+    // 画面
357
+    ctx.save();
358
+    ctx.setGlobalAlpha(opacity);
359
+    ctx.setFillStyle(backgroundColor);
360
+    if (borderRadius > 0) {
361
+      // 画圆角矩形
362
+      let drawData = {
363
+        x, y, w: blockWidth, h: height, r: borderRadius
364
+      };
365
+      _drawRadiusRect(drawData, drawOptions);
366
+      ctx.fill();
367
+    } else {
368
+      ctx.fillRect(toPx(x), toPx(y), toPx(blockWidth), toPx(height));
369
+    }
370
+    ctx.restore();
371
+  }
372
+  if (borderWidth) {
373
+    // 画线
374
+    ctx.save();
375
+    ctx.setGlobalAlpha(opacity);
376
+    borderColor && ctx.setStrokeStyle(borderColor);
377
+    ctx.setLineWidth(toPx(borderWidth));
378
+    if (borderRadius > 0) {
379
+      // 画圆角矩形边框
380
+      let drawData = {
381
+        x, y, w: blockWidth, h: height, r: borderRadius,
382
+      }
383
+      _drawRadiusRect(drawData, drawOptions);
384
+      ctx.stroke();
385
+    } else {
386
+      ctx.strokeRect(toPx(x), toPx(y), toPx(blockWidth), toPx(height));
387
+    }
388
+    ctx.restore();
389
+  }
390
+
391
+  if (text) {
392
+    drawText(Object.assign(text, { x: textX, y: textY }), drawOptions)
393
+  }
394
+}

+ 204 - 0
src/component/taro-plugin-canvas/utils/tools.ts

@@ -0,0 +1,204 @@
1
+import Taro from '@tarojs/taro';
2
+import { IConfig } from '../types';
3
+/**
4
+ * @description 生成随机字符串
5
+ * @param  { number } length - 字符串长度
6
+ * @returns { string }
7
+ */
8
+export const randomString = (length: number) => {
9
+  let str = Math.random().toString(36).substr(2);
10
+  if (str.length >= length) {
11
+    return str.substr(0, length);
12
+  }
13
+  str += randomString(length - str.length);
14
+  return str;
15
+}
16
+
17
+/**
18
+ * @description 获取最大高度
19
+ * @param  {} config
20
+ * @returns { number }
21
+ */
22
+export const getHeight = (config: IConfig) => {
23
+  const getTextHeight = (text) => {
24
+    let fontHeight = text.lineHeight || text.fontSize;
25
+    let height = 0;
26
+    if (text.baseLine === 'top') {
27
+      height = fontHeight;
28
+    } else if (text.baseLine === 'middle') {
29
+      height = fontHeight / 2;
30
+    } else {
31
+      height = 0;
32
+    }
33
+    return height;
34
+  }
35
+  const heightArr: number[] = [];
36
+  (config.blocks || []).forEach((item) => {
37
+    heightArr.push(item.y + item.height);
38
+  });
39
+  (config.texts || []).forEach((item) => {
40
+    let height;
41
+    height = getTextHeight(item);
42
+    heightArr.push(item.y + height);
43
+  });
44
+  (config.images || []).forEach((item) => {
45
+    heightArr.push(item.y + item.height);
46
+  });
47
+  (config.lines || []).forEach((item) => {
48
+    heightArr.push(item.startY);
49
+    heightArr.push(item.endY);
50
+  });
51
+  const sortRes = heightArr.sort((a, b) => b - a);
52
+  let canvasHeight = 0;
53
+  if (sortRes.length > 0) {
54
+    canvasHeight = sortRes[0];
55
+  }
56
+  if (config.height < canvasHeight || !config.height) {
57
+    return canvasHeight;
58
+  } else {
59
+    return config.height;
60
+  }
61
+}
62
+
63
+
64
+/**
65
+ * 将http转为https
66
+ * @param {String}} rawUrl 图片资源url
67
+ * @returns { string }
68
+ */
69
+export function mapHttpToHttps(rawUrl: string) {
70
+  if (rawUrl.indexOf(':') < 0) {
71
+    return rawUrl;
72
+  }
73
+  const urlComponent = rawUrl.split(':');
74
+  if (urlComponent.length === 2) {
75
+    if (urlComponent[0] === 'http') {
76
+      urlComponent[0] = 'https';
77
+      return `${urlComponent[0]}:${urlComponent[1]}`;
78
+    }
79
+  }
80
+  return rawUrl;
81
+}
82
+
83
+/**
84
+ * 下载图片资源
85
+ * @param { string } imageUrl
86
+ * @returns  { Promise }
87
+ */
88
+export function downImage(imageUrl: string) {
89
+  return new Promise<string>((resolve, reject) => {
90
+    // if (/^http/.test(imageUrl) && !new RegExp(wx.env.USER_DATA_PATH).test(imageUrl))
91
+    if (
92
+      /^http/.test(imageUrl) &&
93
+      // @ts-ignore
94
+      !new RegExp((wx as any).env.USER_DATA_PATH).test(imageUrl) &&
95
+      !/^http:\/\/tmp/.test(imageUrl)
96
+    ) {
97
+      Taro.downloadFile({
98
+        url: (imageUrl),
99
+        // TODO
100
+        // url: mapHttpToHttps(imageUrl),
101
+        success: (res) => {
102
+          if (res.statusCode === 200) {
103
+            resolve(res.tempFilePath);
104
+          } else {
105
+            reject(res.errMsg);
106
+          }
107
+        },
108
+        fail(err) {
109
+          reject(err);
110
+        },
111
+      });
112
+    } else {
113
+      // 支持本地地址
114
+      resolve(imageUrl);
115
+    }
116
+  });
117
+}
118
+
119
+export interface IMageInfo {
120
+  imgPath: string;
121
+  imgInfo: any;
122
+  index: number | string;
123
+}
124
+
125
+
126
+/**
127
+ * 获取图片信息
128
+ * @param {*} imgPath
129
+ * @param {*} index
130
+ * @returns  { Promise }
131
+ */
132
+export function getImageInfo(imgPath: string, index: string) {
133
+  return new Promise<IMageInfo>((resolve, reject) => {
134
+    Taro.getImageInfo({
135
+      src: imgPath
136
+    })
137
+      .then(res => {
138
+        resolve({
139
+          imgPath,
140
+          imgInfo: res,
141
+          index
142
+        });
143
+      })
144
+      .catch(err => {
145
+        reject(err);
146
+      })
147
+  });
148
+}
149
+
150
+/**
151
+* @description 下载图片并获取图片信息
152
+* @param  {} image
153
+* @param  {} index
154
+* @returns  { Promise }
155
+*/
156
+export function downloadImageAndInfo(image, index, toRpxFunc, pixelRatio) {
157
+  return new Promise<any>((resolve, reject) => {
158
+    const { x, y, url, zIndex } = image;
159
+    const imageUrl = url;
160
+    // 下载图片
161
+    downImage(imageUrl)
162
+      // 获取图片信息
163
+      .then(imgPath => getImageInfo(imgPath, index))
164
+      .then(({ imgPath, imgInfo }) => {
165
+        // 根据画布的宽高计算出图片绘制的大小,这里会保证图片绘制不变形
166
+        let sx;
167
+        let sy;
168
+        const borderRadius = image.borderRadius || 0;
169
+        const setWidth = image.width;
170
+        const setHeight = image.height;
171
+        const width = toRpxFunc(imgInfo.width / pixelRatio);
172
+        const height = toRpxFunc(imgInfo.height / pixelRatio);
173
+
174
+        if (width / height <= setWidth / setHeight) {
175
+          sx = 0;
176
+          sy = (height - ((width / setWidth) * setHeight)) / 2;
177
+        } else {
178
+          sy = 0;
179
+          sx = (width - ((height / setHeight) * setWidth)) / 2;
180
+        }
181
+        let result = {
182
+          type: 'image',
183
+          borderRadius,
184
+          borderWidth: image.borderWidth,
185
+          borderColor: image.borderColor,
186
+          zIndex: typeof zIndex !== 'undefined' ? zIndex : index,
187
+          imgPath,
188
+          sx,
189
+          sy,
190
+          sw: (width - (sx * 2)),
191
+          sh: (height - (sy * 2)),
192
+          x,
193
+          y,
194
+          w: setWidth,
195
+          h: setHeight,
196
+        }
197
+        resolve(result);
198
+      })
199
+      .catch(err => {
200
+        console.log(err);
201
+        reject(err)
202
+      });
203
+  });
204
+}

BIN
src/images/app-icon.png


BIN
src/images/nav/like.png


BIN
src/images/nav/like_pin.png


BIN
src/images/nav/my.png


BIN
src/images/nav/my_pin.png


BIN
src/images/nav/play.png


BIN
src/images/nav/play_pin.png


+ 22 - 0
src/index.html

@@ -0,0 +1,22 @@
1
+<!DOCTYPE html>
2
+<html>
3
+<head>
4
+  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
5
+  <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
6
+  <meta name="apple-mobile-web-app-capable" content="yes">
7
+  <meta name="apple-touch-fullscreen" content="yes">
8
+  <meta name="format-detection" content="telephone=no,address=no">
9
+  <meta name="apple-mobile-web-app-status-bar-style" content="white">
10
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
11
+  <title>myApp</title>
12
+  <style>
13
+    page{
14
+      background-color: #041129;
15
+    }
16
+  </style>
17
+  <script><%= htmlWebpackPlugin.options.script %></script>
18
+</head>
19
+<body>
20
+  <div id="app"></div>
21
+</body>
22
+</html>

+ 5 - 0
src/pages/collection/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '书架',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 196 - 0
src/pages/collection/index.jsx

@@ -0,0 +1,196 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../service/index'
4
+import tool from '../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    bookList: [],
11
+    isEdit: false,
12
+    isOverSelect: false
13
+  }
14
+
15
+  componentWillMount () {
16
+  }
17
+  componentDidShow () {
18
+    Taro.showLoading({
19
+      title: '加载中',
20
+    })
21
+    if (!Taro.getStorageSync('session_key')) {
22
+      setTimeout(() => {
23
+        this.myBookFollow()
24
+      }, 1000)
25
+    } else {
26
+      this.myBookFollow()
27
+    }
28
+  }
29
+  myBookFollow () {
30
+    api.myBookFollow().then(res => {
31
+      Taro.hideLoading()
32
+      if (res.code == 200) {
33
+        this.setState({
34
+          bookList: res.data
35
+        })
36
+      }
37
+    })
38
+  }
39
+  changeEdit () {
40
+    this.setState({
41
+      isEdit: true
42
+    })
43
+  }
44
+  cancelEdit () {
45
+    let _bookList = this.state.bookList
46
+    _bookList.forEach((item, index) => {
47
+      item.isSelect = false
48
+    })
49
+    this.setState({
50
+      bookList: _bookList,
51
+      isOverSelect: false,
52
+      isEdit: false
53
+    })
54
+  }
55
+  deleteEdit () {
56
+    let that = this
57
+    Taro.showModal({
58
+      title: '提示',
59
+      content: '确定删除吗?',
60
+      success: function (res) {
61
+        if (res.confirm) {
62
+          let book_ids = []
63
+          that.state.bookList.forEach(item => {
64
+            if (item.isSelect) {
65
+              book_ids.push(item.book_id)
66
+            }
67
+          })
68
+          api.deleteBookCollect({ book_ids: book_ids.toString() }).then(res => {
69
+            if (res.code == 200) {
70
+              that.myBookFollow()
71
+              that.setState({
72
+                isEdit: false,
73
+                isOverSelect: false
74
+              })
75
+            }
76
+          })
77
+        } else if (res.cancel) {
78
+          console.log('用户点击取消')
79
+        }
80
+
81
+      }
82
+    })
83
+
84
+
85
+
86
+  }
87
+  overSelect () {
88
+    let _bookList = this.state.bookList
89
+    _bookList.forEach((item, index) => {
90
+      item.isSelect = !this.state.isOverSelect
91
+    })
92
+    this.setState({
93
+      bookList: _bookList,
94
+      isOverSelect: !this.state.isOverSelect,
95
+    })
96
+  }
97
+  changeSelect (item, index) {
98
+    console.log(item);
99
+    if (!this.state.isEdit) {
100
+      Taro.navigateTo({
101
+        url: `/pages/index/subpages/novelText/index?book_id=${item.book_id}`
102
+      })
103
+    } else {
104
+      let _bookList = this.state.bookList
105
+      _bookList.forEach((item, i) => {
106
+        if (index == i) {
107
+          item.isSelect = !this.state.bookList[index].isSelect
108
+        }
109
+      })
110
+
111
+      this.setState({
112
+        bookList: _bookList
113
+      }, () => {
114
+        let list = this.state.bookList.filter(res => {
115
+          return !res.isSelect
116
+        })
117
+        if (list.length == 0) {
118
+          this.setState({
119
+            isOverSelect: true
120
+          })
121
+        } else {
122
+          this.setState({
123
+            isOverSelect: false
124
+          })
125
+        }
126
+      })
127
+    }
128
+  }
129
+  addBooks () {
130
+    Taro.navigateTo({
131
+      url: '/pages/index/subpages/search/index'
132
+    })
133
+  }
134
+
135
+  render () {
136
+    return (
137
+      <View className='mine'>
138
+        {
139
+          this.state.bookList.length == 0 ?
140
+            <View className='empty'>
141
+              <Image className='empty-img' src='https://video-img.fyshark.com/1683801289275slsll.png'></Image>
142
+              <View className='empty-tips'>书架暂时没有书哦!</View>
143
+              <View className='add' onClick={e => (this.addBooks())}>去添加</View>
144
+            </View>
145
+            :
146
+            <View>
147
+
148
+              {
149
+                !this.state.isEdit ?
150
+                  <View className='edit'>
151
+                    <View className='edit-content' onClick={e => (this.changeEdit())}>
152
+
153
+                      <Image className='edit-img' src='https://video-img.fyshark.com/1683687193883dhksjn.png'></Image>
154
+                      编辑
155
+                    </View>
156
+                  </View>
157
+                  :
158
+                  <View className='select'>
159
+                    <View className='select-content' onClick={e => (this.overSelect())}>
160
+                      <Image className='select-img' src={this.state.isOverSelect ? 'https://video-img.fyshark.com/1683698925516hnakjl.png' : 'https://video-img.fyshark.com/1683698074122dhkahj.png'}></Image>
161
+                      全选
162
+                    </View>
163
+                  </View>
164
+              }
165
+              <View className='content'>
166
+                {
167
+                  this.state.bookList.map((item, index) => (
168
+                    <View className='content-info' key={index} onClick={e => (this.changeSelect(item, index))}>
169
+                      <View className='info-image'>
170
+                        <Image className='info-img' mode='widthFix' src={item.img}></Image>
171
+                        {
172
+                          this.state.isEdit &&
173
+                          <Image className='select-icon' src={item.isSelect ? 'https://video-img.fyshark.com/1683699004116sihel.png' : 'https://video-img.fyshark.com/1683698064582dhka.png'}></Image>
174
+                        }
175
+                      </View>
176
+                      <View className='info-title'>{tool.ellipsis(item.name, 6)}</View>
177
+                      <View className='info-tips'>已读{item.read_cnt}章·更新{item.album_cnt}章</View>
178
+                    </View>
179
+                  ))
180
+                }
181
+              </View>
182
+              {
183
+                this.state.isEdit &&
184
+                <View className='edit-console'>
185
+                  <View className='console'>
186
+                    <View className='cancellation' onClick={e => (this.cancelEdit())}>取消</View>
187
+                    <View className='delete' onClick={e => (this.deleteEdit())}>删除</View>
188
+                  </View>
189
+                </View>
190
+              }
191
+            </View>
192
+        }
193
+      </View>
194
+    )
195
+  }
196
+}

+ 173 - 0
src/pages/collection/index.less

@@ -0,0 +1,173 @@
1
+.mine {
2
+  padding-bottom: 70px;
3
+  padding-top: 50px;
4
+  min-height: 100vh;
5
+
6
+  .edit {
7
+    margin-right: 40px;
8
+    display: flex;
9
+    justify-content: flex-end;
10
+    align-items: center;
11
+    font-size: 28px;
12
+    font-family: PingFang SC;
13
+    font-weight: bold;
14
+    color: #888888;
15
+
16
+    .edit-content {
17
+      display: flex;
18
+      align-items: center;
19
+    }
20
+
21
+    .edit-img {
22
+      width: 30px;
23
+      height: 30px;
24
+      display: block;
25
+      margin-right: 12px;
26
+    }
27
+
28
+  }
29
+
30
+  .select {
31
+    margin-left: 40px;
32
+    display: flex;
33
+    align-items: center;
34
+    font-size: 28px;
35
+    font-family: PingFang SC;
36
+    font-weight: bold;
37
+    color: #888888;
38
+
39
+    .select-content {
40
+      display: flex;
41
+      align-items: center;
42
+    }
43
+
44
+    .select-img {
45
+      width: 30px;
46
+      height: 30px;
47
+      display: block;
48
+      margin-right: 12px;
49
+    }
50
+  }
51
+
52
+  .content {
53
+    margin-top: 20px;
54
+    padding: 0 40px 0 28px;
55
+
56
+    .content-info {
57
+      margin-top: 20px;
58
+      margin-left: 12px;
59
+      display: inline-block;
60
+
61
+      .info-image {
62
+        position: relative;
63
+
64
+      }
65
+
66
+      .info-img {
67
+        width: 216px;
68
+        // height: 300px;
69
+        border-radius: 10px;
70
+      }
71
+
72
+      .info-title {
73
+        font-size: 26px;
74
+        font-family: PingFang SC;
75
+        font-weight: bold;
76
+        color: #363636;
77
+      }
78
+
79
+      .info-tips {
80
+        font-size: 20px;
81
+        font-family: PingFang SC;
82
+        font-weight: bold;
83
+        color: rgba(0, 0, 0, 0.6);
84
+      }
85
+
86
+      .select-icon {
87
+        position: absolute;
88
+        bottom: 14px;
89
+        right: 8px;
90
+        width: 48px;
91
+        height: 48px;
92
+        display: block;
93
+      }
94
+    }
95
+  }
96
+
97
+  .edit-console {
98
+    width: 100%;
99
+    position: fixed;
100
+    bottom: 0;
101
+    left: 0;
102
+
103
+    .console {
104
+      padding: 0 40px;
105
+      display: flex;
106
+      justify-content: space-between;
107
+      align-items: center;
108
+
109
+      .cancellation {
110
+        width: 280px;
111
+        height: 66px;
112
+        line-height: 66px;
113
+        text-align: center;
114
+        background: #E9EAEA;
115
+        border-radius: 33px;
116
+        font-size: 26px;
117
+        font-family: PingFang SC;
118
+        font-weight: bold;
119
+        color: #363636;
120
+      }
121
+
122
+      .delete {
123
+        width: 280px;
124
+        height: 66px;
125
+        line-height: 66px;
126
+        text-align: center;
127
+        background: linear-gradient(90deg, #E04A4A, #EC705D);
128
+        border-radius: 33px;
129
+        font-size: 26px;
130
+        font-family: PingFang SC;
131
+        font-weight: bold;
132
+        color: #FFFFFF;
133
+      }
134
+    }
135
+  }
136
+
137
+  .empty {
138
+    position: absolute;
139
+    left: 50%;
140
+    top: 30%;
141
+    transform: translateX(-50%);
142
+
143
+    .empty-img {
144
+      width: 360px;
145
+      height: 360px;
146
+    }
147
+
148
+    .empty-tips {
149
+      font-size: 26rpx;
150
+      font-family: PingFang SC;
151
+      font-weight: bold;
152
+      text-align: center;
153
+      color: #888888;
154
+    }
155
+
156
+    .add {
157
+      position: relative;
158
+      left: 50%;
159
+      transform: translateX(-50%);
160
+      margin-top: 58px;
161
+      width: 322px;
162
+      height: 68px;
163
+      line-height: 68px;
164
+      background: #E04A4A;
165
+      border-radius: 32px;
166
+      font-size: 28px;
167
+      text-align: center;
168
+      font-family: PingFang SC;
169
+      font-weight: 800;
170
+      color: #FFFFFF;
171
+    }
172
+  }
173
+}

+ 5 - 0
src/pages/index/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '书城',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 149 - 0
src/pages/index/index.jsx

@@ -0,0 +1,149 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, ScrollView, Input } from '@tarojs/components'
3
+import * as api from '../../service/index'
4
+import tool from '../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    tags: [],//标签列表
11
+    tagsIndex: 0,//当前的标签
12
+    booksList: [],//分类书籍
13
+    page: 1,
14
+    page_size: 20,
15
+    isNext: true,//是否还有下一页
16
+  }
17
+
18
+  componentWillMount () {
19
+    this.getBookCatIndex()
20
+  }
21
+  componentDidShow () {
22
+  }
23
+  getBookCatIndex () {
24
+    api.getBookCatIndex().then(res => {
25
+      if (res.code == 200) {
26
+
27
+        this.setState({
28
+          tags: res.data.book_cat,
29
+
30
+        }, () => {
31
+          this.getBookCatInfo()
32
+        })
33
+      }
34
+    })
35
+  }
36
+  getBookCatInfo () {
37
+    let params = {
38
+      cat_id: this.state.tags[this.state.tagsIndex].id,
39
+      page: this.state.page,
40
+      page_size: this.state.page_size,
41
+    }
42
+    api.getBookCatInfo(params).then(res => {
43
+      if (res.code == 200) {
44
+        let isNext = true
45
+        console.log(res.data.book_cat.length, 'res.data.book_cat.length');
46
+        if (res.data.book_cat.length < 20) {
47
+          isNext = false
48
+        }
49
+        this.setState({
50
+          booksList: [...this.state.booksList, ...res.data.book_cat],
51
+          isNext: isNext,
52
+          page: isNext ? this.state.page + 1 : this.state.page
53
+        })
54
+      }
55
+    })
56
+  }
57
+  toSearch () {
58
+    Taro.navigateTo({
59
+      url: '/pages/index/subpages/search/index'
60
+    })
61
+  }
62
+  selectedLabel (item, index) {
63
+    this.setState({
64
+      tagsIndex: index,
65
+      booksList: [],
66
+      page: 1
67
+    }, () => {
68
+      this.getBookCatInfo()
69
+    })
70
+  }
71
+  onReachBottom () {
72
+    console.log(this.state.isNext, 'this.state.isNext');
73
+    if (this.state.isNext) {
74
+      this.getBookCatInfo()
75
+    }
76
+  }
77
+  toDetail (item, index) {
78
+    Taro.navigateTo({
79
+      url: `/pages/index/subpages/novel/index?book_id=${item.id}`
80
+    })
81
+  }
82
+  toMoneyPlay () {
83
+    Taro.navigateTo({
84
+      url: `/pages/mine/subpages/moneyPlay/index`
85
+    })
86
+  }
87
+
88
+
89
+  render () {
90
+    return (
91
+      <View className='mine'>
92
+        <View className='mine-content'>
93
+          <View className='search' onClick={e => (this.toSearch())}>
94
+            <Image className='search-img' src='https://video-img.fyshark.com/1683600195067ahjdklj.png'></Image>
95
+            <Input className='search-input' disabled type='text' placeholder='请输入书名/口令码' focus />
96
+          </View>
97
+          <ScrollView
98
+            className='recharge'
99
+            scrollX
100
+          >
101
+            {
102
+              this.state.tags.map((item, index) => (
103
+                <View className='recharge-list' onClick={e => (this.selectedLabel(item, index))} key={index}>
104
+                  <View className={this.state.tagsIndex == index ? 'recharge-info-active' : 'recharge-info'}
105
+                    key={index}
106
+                  >
107
+                    {item.name}
108
+                  </View>
109
+                  {
110
+                    this.state.tagsIndex == index &&
111
+                    <View className='recharge-tips'></View>
112
+                  }
113
+                </View>
114
+              ))
115
+            }
116
+          </ScrollView>
117
+          <View className='book-content'>
118
+            <View className='book-list'>
119
+              {
120
+                this.state.booksList.map((item, index) => (
121
+                  <View className='book-info' key={index} onClick={e => (this.toDetail(item, index))}>
122
+                    <View className='book-info-left'>
123
+                      <Image className='book-img' src={item.img}></Image>
124
+                    </View>
125
+                    <View className='book-right'>
126
+                      <View className='book-title'>{item.name}</View>
127
+                      <View className='book-tips'>{tool.ellipsis(item.intro, 56)}</View>
128
+                      <View className='book-label-list'>
129
+                        {
130
+                          item.name_tag.length > 0 &&
131
+                          <View className='book-label-info'>
132
+                            {item.name_tag}
133
+                          </View>
134
+                        }
135
+                      </View>
136
+                    </View>
137
+                  </View>
138
+                ))
139
+              }
140
+            </View>
141
+          </View>
142
+        </View>
143
+        <View className='share update-active' onClick={e => (this.toMoneyPlay())}>
144
+          <Image className='share-img' mode='widthFix' src='https://video-img.fyshark.com/1684494291916shjkgnla.png'></Image>
145
+        </View>
146
+      </View>
147
+    )
148
+  }
149
+}

+ 155 - 0
src/pages/index/index.less

@@ -0,0 +1,155 @@
1
+.mine {
2
+  width: 100vw;
3
+  min-height: 100vh;
4
+
5
+  .mine-content {
6
+    padding: 36px 30px;
7
+
8
+    .search {
9
+      // width: 100%;
10
+      display: flex;
11
+      padding: 0 20px;
12
+      height: 70px;
13
+      align-items: center;
14
+      background: #EAEAEF;
15
+      border-radius: 35px;
16
+
17
+      .search-img {
18
+        width: 36px;
19
+        height: 36px;
20
+        display: flex;
21
+      }
22
+
23
+      .search-input {
24
+        margin-left: 6px;
25
+        flex: 1;
26
+        font-size: 24px;
27
+        font-family: PingFang SC;
28
+        font-weight: bold;
29
+        color: rgba(0, 0, 0, 0.4);
30
+        line-height: 82px;
31
+      }
32
+    }
33
+
34
+    .recharge {
35
+      margin-top: 30px;
36
+      width: 100%;
37
+      white-space: nowrap;
38
+
39
+      .recharge-list {
40
+        display: inline-block;
41
+        position: relative;
42
+        padding-right: 52px;
43
+
44
+        .recharge-info {
45
+          font-size: 28px;
46
+          font-family: PingFang SC;
47
+          font-weight: 800;
48
+          color: #888888;
49
+          line-height: 60px;
50
+        }
51
+
52
+        .recharge-info-active {
53
+          font-size: 32px;
54
+          font-family: PingFang SC;
55
+          font-weight: 800;
56
+          color: #363636;
57
+          line-height: 60px;
58
+        }
59
+
60
+        .recharge-tips {
61
+          position: relative;
62
+          left: 50%;
63
+          transform: translateX(-50%);
64
+          width: 30rpx;
65
+          border: 2px solid #E04A4A;
66
+        }
67
+      }
68
+
69
+    }
70
+
71
+    .book-content {
72
+      padding: 20px 66px 50px 20px;
73
+
74
+      .book-list {
75
+        .book-info {
76
+          display: flex;
77
+          margin-bottom: 30px;
78
+
79
+          .book-info-left {
80
+            .book-img {
81
+              width: 158px;
82
+              height: 220px;
83
+              display: block;
84
+              border-radius: 10px;
85
+            }
86
+          }
87
+
88
+          .book-right {
89
+            margin-left: 20px;
90
+
91
+            .book-title {
92
+              font-size: 26px;
93
+              font-family: PingFang SC;
94
+              font-weight: 800;
95
+              color: #363636;
96
+            }
97
+
98
+            .book-tips {
99
+              font-size: 22px;
100
+              margin-top: 22px;
101
+              font-family: PingFang SC;
102
+              font-weight: bold;
103
+              color: #888888;
104
+            }
105
+
106
+            .book-label-list {
107
+              display: flex;
108
+              margin-top: 28px;
109
+
110
+              .book-label-info {
111
+                margin-left: 10px;
112
+                padding: 6px 12px;
113
+                border-radius: 10px;
114
+                font-size: 18px;
115
+                font-family: PingFang SC;
116
+                font-weight: 800;
117
+                letter-spacing: 1px;
118
+                background:rgb(212 242 245 / 54%);
119
+                color: #4ec7da;
120
+              }
121
+            }
122
+          }
123
+        }
124
+      }
125
+    }
126
+  }
127
+
128
+  .share {
129
+    position: fixed;
130
+    right: 20px;
131
+    bottom: 68px;
132
+
133
+    .share-img {
134
+      width: 150px;
135
+    }
136
+  }
137
+
138
+  /* 定义动画 */
139
+  @keyframes shrink {
140
+
141
+    0%,
142
+    100% {
143
+      transform: translateY(0) scale(1);
144
+    }
145
+
146
+    50% {
147
+      transform: translateY(0px) scale(0.95);
148
+    }
149
+  }
150
+
151
+  /* 应用动画 */
152
+  .update-active {
153
+    animation: shrink 1.2s ease-in-out infinite;
154
+  }
155
+}

+ 5 - 0
src/pages/index/subpages/directory/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 84 - 0
src/pages/index/subpages/directory/index.jsx

@@ -0,0 +1,84 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, RichText, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { getCurrentInstance } from '@tarojs/taro'
6
+import './index.less'
7
+import { AtDrawer } from 'taro-ui'
8
+import "taro-ui/dist/style/components/drawer.scss";
9
+
10
+export default class collection extends Component {
11
+  $instance = getCurrentInstance()
12
+  state = {
13
+    directoryList: [
14
+    ],
15
+    show: true,
16
+    order_by_sta: 0,//0:正序,1:倒序
17
+
18
+  }
19
+
20
+  componentWillMount () {
21
+    this.getBookCatalogue()
22
+  }
23
+  componentDidShow () {
24
+  }
25
+  getBookCatalogue () {
26
+    let routers = this.$instance.router.params
27
+    api.getBookCatalogue({ book_id: routers.book_id, order_by_sta: this.state.order_by_sta }).then(res => {
28
+      if (res.code == 200) {
29
+        this.setState({
30
+          directoryList: res.data.book_catalogue,
31
+        })
32
+      }
33
+    })
34
+  }
35
+  changeOrderBySta () {
36
+    this.setState({
37
+      order_by_sta: this.state.order_by_sta == 0 ? 1 : 0
38
+    }, () => {
39
+      this.getBookCatalogue()
40
+    })
41
+  }
42
+  toNovelText (item, index) {
43
+    api.bookReadReort({ content_id: item.id }).then(res => {
44
+      if (res.code == 200) {
45
+        let routers = this.$instance.router.params
46
+        Taro.navigateTo({
47
+          url: `/pages/index/subpages/novelText/index?book_id=${routers.book_id}`
48
+        })
49
+      }
50
+    })
51
+  }
52
+
53
+
54
+  render () {
55
+    return (
56
+      <View className='mine'>
57
+        <View className='mine-content'>
58
+          <View className='drawer-content'>
59
+            <View className='drawer-over'>
60
+              <View className='drawer-over-num'>共{this.state.directoryList.length}章</View>
61
+              <View className='drawer-over-right' onClick={e => (this.changeOrderBySta())}>
62
+                <View className='drawer-over-right-title'>倒序</View>
63
+                <Image className='drawer-over-right-img' src='https://video-img.fyshark.com/1683631467893djaj.png'></Image>
64
+              </View>
65
+            </View>
66
+            <View className='drawer-list'>
67
+              {
68
+                this.state.directoryList.map((item, index) => (
69
+                  <View className='drawer-info' key={index} onClick={e => (this.toNovelText(item, index))}>
70
+                    <View className='drawer-info-left'>{item.name}</View>
71
+                    {
72
+                      item.is_vip == 1 &&
73
+                      <Image className='drawer-info-right' src='https://video-img.fyshark.com/1683633924768dsjkmalnjdl.png'></Image>
74
+                    }
75
+                  </View>
76
+                ))
77
+              }
78
+            </View>
79
+          </View>
80
+        </View>
81
+      </View>
82
+    )
83
+  }
84
+}

+ 61 - 0
src/pages/index/subpages/directory/index.less

@@ -0,0 +1,61 @@
1
+.mine {
2
+  width: 100vw;
3
+  min-height: 100vh;
4
+  background: #F3F3F3;
5
+
6
+  .mine-content {
7
+    padding: 60px 40px;
8
+    padding-bottom: 180px;
9
+
10
+    .drawer-content {
11
+      position: relative;
12
+
13
+      .drawer-over {
14
+        display: flex;
15
+        justify-content: space-between;
16
+        align-items: center;
17
+
18
+        .drawer-over-right {
19
+          display: flex;
20
+
21
+          .drawer-over-right-title {
22
+            font-size: 26px;
23
+            font-family: PingFang SC;
24
+            font-weight: bold;
25
+            color: #565656;
26
+          }
27
+
28
+          .drawer-over-right-img {
29
+            margin-left: 10px;
30
+            width: 36px;
31
+            height: 36px;
32
+            display: block;
33
+          }
34
+        }
35
+      }
36
+
37
+      .drawer-list {
38
+        font-size: 32px;
39
+        font-family: PingFang SC;
40
+        font-weight: bold;
41
+        color: #6b6969;
42
+
43
+        .drawer-info {
44
+          padding: 35px 0;
45
+          display: flex;
46
+          justify-content: space-between;
47
+          align-items: center;
48
+          border-bottom: 2px solid rgba(53, 53, 53, .1);
49
+        }
50
+
51
+        .drawer-info-right {
52
+          width: 36px;
53
+          height: 36px;
54
+          display: block;
55
+        }
56
+      }
57
+    }
58
+  }
59
+
60
+
61
+}

+ 5 - 0
src/pages/index/subpages/novel/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '详情',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 182 - 0
src/pages/index/subpages/novel/index.jsx

@@ -0,0 +1,182 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { getCurrentInstance } from '@tarojs/taro'
6
+
7
+import './index.less'
8
+
9
+export default class collection extends Component {
10
+  $instance = getCurrentInstance()
11
+  state = {
12
+    booksinfo: {},
13
+    is_collect: 0,//是否加入书架,0|1
14
+    likeBookList: [],
15
+    lastTime: 0
16
+  }
17
+
18
+  componentWillMount () {
19
+    this.getBookinfo()
20
+    this.getBookLike()
21
+  }
22
+  componentDidShow () {
23
+  }
24
+  getBookLike () {
25
+    api.getBookLike().then(res => {
26
+      if (res.code == 200) {
27
+        this.setState({
28
+          likeBookList: res.data.book_like
29
+        })
30
+      }
31
+    })
32
+  }
33
+  getBookinfo () {
34
+    let routers = this.$instance.router.params
35
+    api.getBookinfo({ book_id: routers.book_id }).then(res => {
36
+      if (res.code == 200) {
37
+        this.setState({
38
+          booksinfo: res.data.book_info[0],
39
+          is_collect: res.data.is_collect
40
+        })
41
+      }
42
+    })
43
+  }
44
+  bookCollect () {
45
+    if (this.state.is_collect == 1) return
46
+    let routers = this.$instance.router.params
47
+    api.bookCollect({ book_id: routers.book_id }).then(res => {
48
+      if (res.code == 200) {
49
+        this.getBookinfo()
50
+      }
51
+    })
52
+  }
53
+  onReachBottom () {
54
+    // if (!this.state.total) {
55
+    //   this.setState({
56
+    //     page: this.state.page + 1
57
+    //   })
58
+    // }
59
+  }
60
+  toDetail (item, index) {
61
+    Taro.navigateTo({
62
+      url: '/pages/index/subpages/novel/index'
63
+    })
64
+  }
65
+  toNovelText () {
66
+    Taro.navigateTo({
67
+      url: `/pages/index/subpages/novelText/index?book_id=${this.state.booksinfo.id}`
68
+    })
69
+  }
70
+  toDirectory () {
71
+    Taro.navigateTo({
72
+      url: `/pages/index/subpages/directory/index?book_id=${this.state.booksinfo.id}`
73
+    })
74
+  }
75
+  toLikeBooks (item, index) {
76
+    Taro.navigateTo({
77
+      url: `/pages/index/subpages/novel/index?book_id=${item.id}`
78
+    })
79
+  }
80
+  copyCode () {
81
+    let lastTime = this.state.lastTime;
82
+    let now = new Date().getTime();
83
+    if (now - lastTime < 2000) return
84
+    this.setState({
85
+      lastTime: now
86
+    }, () => {
87
+      api.getCommand({ book_id: this.state.booksinfo.id }).then(res => {
88
+        if (res.code == 200) {
89
+          let text = res.data
90
+          // if (process.env.TARO_ENV == 'tt') {
91
+          //   text = res.data
92
+          // }
93
+          Taro.setClipboardData({
94
+            data: text,
95
+            success: function (res) {
96
+              Taro.showModal({
97
+                title: '口令复制成功',
98
+                content: "是否前往了解赚钱计划?",
99
+                success: function (res) {
100
+                  if (res.confirm) {
101
+                    Taro.navigateTo({
102
+                      url: `/pages/mine/subpages/moneyPlay/index`
103
+                    })
104
+                  } else if (res.cancel) {
105
+                    console.log('用户点击取消')
106
+                  }
107
+                }
108
+              })
109
+            }
110
+          })
111
+
112
+        }
113
+      })
114
+    })
115
+  }
116
+
117
+  render () {
118
+    return (
119
+      <View className='mine'>
120
+        <View className='mine-content'>
121
+          <View className='book-top'>
122
+            <Image className='book-img' src={this.state.booksinfo.img}></Image>
123
+            <View className='top-right'>
124
+              <View className='book-title'>{tool.ellipsis(this.state.booksinfo.name, 15)}</View>
125
+              <View className='book-visit-num'>{this.state.booksinfo.paly_cnt}游览</View>
126
+            </View>
127
+            <View className='share update-active' onClick={e => (this.copyCode())}>
128
+              <Image className='share-img' mode='widthFix' src='https://video-img.fyshark.com/1684206675357shykahfgl.png'></Image>
129
+            </View>
130
+          </View>
131
+          <View className='book-tips'>
132
+            <View className='book-tips-title'>书籍简介</View>
133
+            <View className='book-tips-content'>{this.state.booksinfo.intro} </View>
134
+          </View>
135
+          <View className='directory' onClick={e => (this.toDirectory())}>
136
+            <View className='directory-left'>
137
+              <Image className='directory-icon' src='https://video-img.fyshark.com/1683618704652dadda%20.png'></Image>
138
+              <View className='derectory-title'>查看目录</View>已更新到{this.state.booksinfo.album_cnt}章
139
+            </View>
140
+            <Image className='directory-right-icon' src='https://video-img.fyshark.com/1683618693594ddada%20.png'></Image>
141
+          </View>
142
+          <View className='like'>
143
+            <View className='like-tips'>
144
+              <View className='lick-title'>猜你喜欢</View>
145
+              <View className='lick-change' onClick={e => (this.getBookLike())}>
146
+                换一换
147
+                <Image className='like-img' src='https://video-img.fyshark.com/1683619412847dada%20.png'></Image>
148
+              </View>
149
+            </View>
150
+            <View className='lick-list'>
151
+              {
152
+                this.state.likeBookList.map((item, index) => (
153
+                  <View className='lick-info' key={index} onClick={e => (this.toLikeBooks(item, index))}>
154
+                    <Image className='like-img' src={item.img}></Image>
155
+                    <View className='like-title'>{tool.ellipsis(item.name, 4)}</View>
156
+                  </View>
157
+                ))
158
+              }
159
+            </View>
160
+          </View>
161
+          <View className='selected'>
162
+            <View className='selected-title'>精选内容</View>
163
+            <View className='selected-text'>{this.state.booksinfo.short_name}</View>
164
+          </View>
165
+          <View className='console'>
166
+            <View className='console-content'>
167
+              <View className='console-left'
168
+                style={this.state.is_collect == 1 ? 'opacity:0.3' : ''}
169
+                onClick={e => (this.bookCollect())}>
170
+                <Image className='console-left-img' src='https://video-img.fyshark.com/1683620721580adda.png'></Image>
171
+                <View className='console-left-title'>{this.state.is_collect == 0 ? '加入书架' : '已在书架'}</View>
172
+              </View>
173
+              <View className='console-right' onClick={e => (this.toNovelText())}>
174
+                继续阅读
175
+              </View>
176
+            </View>
177
+          </View>
178
+        </View>
179
+      </View>
180
+    )
181
+  }
182
+}

+ 241 - 0
src/pages/index/subpages/novel/index.less

@@ -0,0 +1,241 @@
1
+.mine {
2
+  width: 100vw;
3
+  min-height: 100vh;
4
+
5
+  .mine-content {
6
+    padding: 42px 30px;
7
+    padding-bottom: 180px;
8
+
9
+    .book-top {
10
+      display: flex;
11
+      position: relative;
12
+
13
+      .book-img {
14
+        width: 200px;
15
+        height: 280px;
16
+        border-radius: 10px;
17
+      }
18
+
19
+      .top-right {
20
+        margin-left: 28px;
21
+
22
+        .book-title {
23
+          margin-top: 28px;
24
+          font-size: 28px;
25
+          font-family: PingFang SC;
26
+          font-weight: 800;
27
+          color: #363636;
28
+        }
29
+
30
+        .book-visit-num {
31
+          margin-top: 38px;
32
+          font-size: 22px;
33
+          font-family: PingFang SC;
34
+          font-weight: 800;
35
+          color: #888888;
36
+        }
37
+      }
38
+      .share{
39
+        position: absolute;
40
+        right: 0;
41
+        top: 68px;
42
+        .share-img{
43
+          width: 150px;
44
+        }
45
+      }
46
+    }
47
+
48
+    .book-tips {
49
+      margin-top: 40px;
50
+
51
+      .book-tips-title {
52
+        font-size: 32px;
53
+        font-family: PingFang SC;
54
+        font-weight: bold;
55
+        color: #363636;
56
+      }
57
+
58
+      .book-tips-content {
59
+        font-size: 28px;
60
+        font-family: PingFang SC;
61
+        font-weight: bold;
62
+        color: #888888;
63
+        line-height: 40px;
64
+        margin-top: 36px;
65
+      }
66
+    }
67
+
68
+    .directory {
69
+      margin-top: 36px;
70
+      display: flex;
71
+      justify-content: space-between;
72
+      align-items: center;
73
+
74
+      .directory-left {
75
+        display: flex;
76
+        align-items: center;
77
+        font-size: 22px;
78
+        font-family: PingFang SC;
79
+        font-weight: 800;
80
+        color: #888888;
81
+
82
+        .directory-icon {
83
+          width: 36px;
84
+          height: 36px;
85
+          display: block;
86
+        }
87
+
88
+        .derectory-title {
89
+          margin: 0 14px 0 8px;
90
+          font-size: 28px;
91
+          color: #363636;
92
+        }
93
+      }
94
+
95
+      .directory-right-icon {
96
+        width: 32px;
97
+        height: 32px;
98
+        display: block;
99
+      }
100
+    }
101
+
102
+    .like {
103
+      margin-top: 80px;
104
+
105
+      .like-tips {
106
+        display: flex;
107
+        justify-content: space-between;
108
+        align-items: center;
109
+
110
+        .lick-title {
111
+          font-size: 28px;
112
+          font-family: PingFang SC;
113
+          font-weight: 800;
114
+          color: #363636;
115
+        }
116
+
117
+        .lick-change {
118
+          display: flex;
119
+          font-size: 24px;
120
+          font-family: PingFang SC;
121
+          font-weight: 800;
122
+          color: #888888;
123
+
124
+          .like-img {
125
+            margin-left: 10px;
126
+            width: 36px;
127
+            height: 36px;
128
+            display: block;
129
+          }
130
+        }
131
+      }
132
+
133
+      .lick-list {
134
+        margin-top: 50px;
135
+        display: flex;
136
+        justify-content: space-between;
137
+
138
+        .lick-info {
139
+          .like-img {
140
+            width: 156px;
141
+            height: 220px;
142
+            border-radius: 10px;
143
+          }
144
+
145
+          .like-title {
146
+            text-align: center;
147
+            font-size: 26px;
148
+            font-family: PingFang SC;
149
+            font-weight: 800;
150
+            color: #363636;
151
+          }
152
+        }
153
+      }
154
+    }
155
+
156
+    .selected {
157
+      margin-top: 40px;
158
+
159
+      .selected-title {
160
+        font-size: 28px;
161
+        font-family: PingFang SC;
162
+        font-weight: 800;
163
+        color: #363636;
164
+      }
165
+
166
+      .selected-text {
167
+        text-indent: 2em;
168
+        margin-top: 30px;
169
+        font-size: 28px;
170
+        font-family: PingFang SC;
171
+        font-weight: bold;
172
+        color: #888888;
173
+        line-height: 40px;
174
+      }
175
+    }
176
+
177
+    .console {
178
+      position: fixed;
179
+      width: 100%;
180
+      bottom: 0;
181
+      left: 0;
182
+
183
+      .console-content {
184
+        padding: 16px 30px 42px 30px;
185
+        display: flex;
186
+        background: #FFFFFF;
187
+        justify-content: space-between;
188
+        align-items: center;
189
+
190
+      }
191
+
192
+      .console-left {
193
+        padding: 0 40px;
194
+        position: relative;
195
+
196
+        .console-left-img {
197
+          position: relative;
198
+          left: 50%;
199
+          transform: translateX(-50%);
200
+          width: 40px;
201
+          height: 40px;
202
+          display: block;
203
+        }
204
+
205
+        .console-left-title {
206
+          font-size: 20px;
207
+          font-family: PingFang SC;
208
+          font-weight: bold;
209
+          color: #888888;
210
+        }
211
+      }
212
+
213
+      .console-right {
214
+        width: 408px;
215
+        height: 62px;
216
+        line-height: 62px;
217
+        text-align: center;
218
+        background: linear-gradient(90deg, #E04A4A, #EC705D);
219
+        border-radius: 31px;
220
+        font-size: 26px;
221
+        font-family: PingFang SC;
222
+        font-weight: bold;
223
+        color: #FFFFFF;
224
+      }
225
+    }
226
+  }
227
+   /* 定义动画 */
228
+ @keyframes shrink {
229
+  0%, 100% {
230
+    transform: translateY(0) scale(1);
231
+  }
232
+  50% {
233
+    transform: translateY(0px) scale(0.95);
234
+  }
235
+}
236
+
237
+/* 应用动画 */
238
+.update-active {
239
+  animation: shrink 1.2s ease-in-out infinite;
240
+}
241
+}

+ 5 - 0
src/pages/index/subpages/novelText/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 434 - 0
src/pages/index/subpages/novelText/index.jsx

@@ -0,0 +1,434 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, RichText, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { getCurrentInstance } from '@tarojs/taro'
6
+import './index.less'
7
+import { AtDrawer } from 'taro-ui'
8
+import "taro-ui/dist/style/components/drawer.scss";
9
+
10
+export default class collection extends Component {
11
+  $instance = getCurrentInstance()
12
+  state = {
13
+    booksinfo: {},
14
+    directoryList: [],//目录
15
+    show: false,
16
+    order_by_sta: 0,//0:正序,1:倒序
17
+    content_id: 0,//上次观看的章节
18
+    isConsole: true,
19
+    purchaseList: [
20
+      {
21
+        title: '7天包周',
22
+        money: 990,
23
+        oldMoney: '¥19.8元/周',
24
+        tips: '热门',
25
+        text: '立享5折',
26
+      },
27
+      {
28
+        title: '7天包周',
29
+        money: 990,
30
+        oldMoney: '¥19.8元/周',
31
+        tips: '',
32
+        text: '立享5折',
33
+      },
34
+      {
35
+        title: '7天包周',
36
+        money: 990,
37
+        oldMoney: '¥19.8元/周',
38
+        tips: '优惠',
39
+        text: '立享5折',
40
+      },
41
+    ],
42
+    selectVip: 0,//当前选择的充值
43
+    isIos: false,
44
+    pay_status: 1,//是否绑定口令
45
+    code:''
46
+  }
47
+
48
+  componentWillMount () {
49
+    if (!Taro.getStorageSync('session_key')) {
50
+      setTimeout(() => {
51
+        this.getBookCatalogue()
52
+        this.getShopList()
53
+      }, 1500)
54
+    } else {
55
+      this.getBookCatalogue()
56
+      this.getShopList()
57
+    }
58
+    let that = this
59
+    Taro.getSystemInfo({
60
+      success: function (res) {
61
+        if (res.platform != "android") {
62
+          that.setState({
63
+            isIos: true
64
+          })
65
+        }
66
+      }
67
+    })
68
+  }
69
+  componentDidShow () {
70
+  }
71
+  getBookCatalogue () {
72
+    Taro.showLoading({
73
+      title: '加载中',
74
+    })
75
+    let routers = this.$instance.router.params
76
+
77
+    api.getBookCatalogue({ book_id: routers.book_id, order_by_sta: this.state.order_by_sta }).then(res => {
78
+      Taro.hideLoading()
79
+      if (res.code == 200) {
80
+        this.setState({
81
+          directoryList: res.data.book_catalogue,
82
+          content_id: res.data.content_id
83
+        }, () => {
84
+          this.getBookContent()
85
+        })
86
+      }
87
+    })
88
+  }
89
+
90
+  getBookContent () {
91
+    let that = this
92
+    let routers = this.$instance.router.params
93
+    api.getBookContent({ book_id: routers.book_id, content_id: this.state.content_id }).then(res => {
94
+      if (res.code == 200) {
95
+        Taro.setNavigationBarTitle({ title: res.data.title })
96
+        this.setState({
97
+          booksinfo: res.data
98
+        }, () => {
99
+          that.changeChapter(res.data.content_id)
100
+          Taro.pageScrollTo({
101
+            scrollTop: 0,
102
+            duration: 10
103
+          })
104
+        })
105
+      }
106
+    })
107
+  }
108
+  changeOrderBySta () {
109
+    this.setState({
110
+      order_by_sta: this.state.order_by_sta == 0 ? 1 : 0
111
+    }, () => {
112
+      this.getBookCatalogue()
113
+    })
114
+  }
115
+  getShopList () {
116
+    api.getShopList().then(res => {
117
+      if (res.code == 200) {
118
+        this.setState({
119
+          purchaseList: res.data.vip_list,
120
+          pay_status: res.data.pay_status
121
+        })
122
+      }
123
+    })
124
+  }
125
+  //充值
126
+  toOrder () {
127
+    if (this.state.purchaseList[this.state.selectVip].ios_status == 0) {
128
+      if (this.state.isIos) {
129
+        Taro.showToast({
130
+          title: 'ios暂不支持充值',
131
+          icon: 'none',
132
+          duration: 2000
133
+        })
134
+        return
135
+      }
136
+    }
137
+    let that = this
138
+    let item = this.state.purchaseList[this.state.selectVip]
139
+    if (process.env.TARO_ENV === 'tt') {
140
+      Taro.showLoading({
141
+        title: '加载中...',
142
+      })
143
+      let routers = this.$instance.router.params
144
+      api.ttCreateOrder({ pay_info_id: item.id, book_id: routers.book_id }).then(res => {
145
+        if (res.code == 200) {
146
+          Taro.hideLoading()
147
+          tt.pay({
148
+            orderInfo: {
149
+              order_id: res.data.order_id,
150
+              order_token: res.data.order_token
151
+            },
152
+            service: 5,
153
+            success (item) {
154
+              if (item.code == 0) {
155
+                that.getBookCatalogue()
156
+                that.getShopList()
157
+                setTimeout(() => {
158
+                  that.getBookCatalogue()
159
+                  that.getShopList()
160
+                }, 2000);
161
+                // 支付成功处理逻辑,只有res.code=0时,才表示支付成功
162
+                // 但是最终状态要以商户后端结果为准
163
+              }
164
+            },
165
+            fail (item) {
166
+              // 调起收银台失败处理逻辑
167
+            },
168
+          });
169
+        }
170
+      })
171
+    } else if (process.env.TARO_ENV === 'weapp') {
172
+      Taro.getSystemInfo({
173
+        success: function (res) {
174
+          Taro.showLoading({
175
+            title: '加载中...',
176
+          })
177
+          api.createOrder({ shop_id: item.id }).then(res => {
178
+            if (res.code == 200) {
179
+              Taro.hideLoading()
180
+              Taro.requestPayment({
181
+                timeStamp: res.data.timeStamp,
182
+                nonceStr: res.data.nonceStr,
183
+                package: res.data.package,
184
+                signType: res.data.signType,
185
+                paySign: res.data.paySign,
186
+                success: function (res) {
187
+                  that.getBookCatalogue()
188
+                  that.getShopList()
189
+                  setTimeout(() => {
190
+                    that.getBookCatalogue()
191
+                    that.getShopList()
192
+                  }, 2000);
193
+                },
194
+                fail: function (err) {
195
+                  try {
196
+                    const res = Taro.getSystemInfoSync()
197
+                    console.log(res, 'iphone=====');
198
+                  } catch (e) {
199
+                  }
200
+                }
201
+              })
202
+            }
203
+          })
204
+        }
205
+      })
206
+    }
207
+
208
+  }
209
+  selectVip (item, index) {
210
+    this.setState({
211
+      selectVip: index
212
+    })
213
+  }
214
+  //上一章
215
+  previous () {
216
+    if (this.state.booksinfo.content_last_id == 0) return
217
+    this.setState({
218
+      content_id: this.state.booksinfo.content_last_id
219
+    }, () => {
220
+
221
+      // this.changeChapter(this.state.booksinfo.content_last_id)
222
+      this.getBookContent()
223
+    })
224
+  }
225
+  //打开目录
226
+  directory () {
227
+    this.setState({
228
+      show: true
229
+    })
230
+  }
231
+  unDirectory () {
232
+    this.setState({
233
+      show: false
234
+    })
235
+  }
236
+  //下一章
237
+  next () {
238
+    if (this.state.booksinfo.content_next_id == 0) return
239
+    this.setState({
240
+      content_id: this.state.booksinfo.content_next_id
241
+    }, () => {
242
+      // this.changeChapter(this.state.booksinfo.content_next_id)
243
+      this.getBookContent()
244
+    })
245
+  }
246
+  //上报阅读记录
247
+  changeChapter (content_id) {
248
+    api.bookReadReort({ content_id: content_id }).then(res => {
249
+      if (res.code == 200) {
250
+
251
+      }
252
+    })
253
+  }
254
+  jumpChapter (content_id) {
255
+    this.setState({
256
+      show: false,
257
+      content_id: content_id
258
+    }, () => {
259
+      this.getBookContent()
260
+    })
261
+  }
262
+  changeIsConsole () {
263
+    this.setState({
264
+      isConsole: !this.state.isConsole
265
+    })
266
+  }
267
+  openCode () {
268
+    this.setState({
269
+      isOpen: true
270
+    })
271
+  }
272
+  searchCode (e) {
273
+    this.setState(
274
+      {
275
+        code: e.detail.value
276
+      }
277
+    )
278
+  }
279
+  cancel () {
280
+    this.setState({
281
+      isOpen: false
282
+    })
283
+  }
284
+  determine () {
285
+    api.getCheckoutBookCode({ input_str: this.state.code }).then(res => {
286
+      if (res.code == 200) {
287
+        this.getShopList()
288
+        this.setState({
289
+          isOpen: false
290
+        })
291
+        Taro.showToast({
292
+          title: `已获取折扣`,
293
+          icon: 'none',
294
+          duration: 2000
295
+        })
296
+      } else {
297
+        Taro.showToast({
298
+          title: res.msg,
299
+          icon: 'none',
300
+          duration: 2000
301
+        })
302
+      }
303
+    })
304
+  }
305
+
306
+  render () {
307
+    return (
308
+      <View className='mine'>
309
+
310
+        {
311
+          this.state.booksinfo.book_is_vip == 0 &&
312
+          <View className='mine-content'>
313
+            <View className='console-modil' onClick={e => (this.changeIsConsole())}>
314
+            </View>
315
+            <View className='novel-title'>{this.state.booksinfo.book_name}</View>
316
+            <RichText
317
+              className='member-title'
318
+              nodes={this.state.booksinfo.book_content}></RichText>
319
+            {
320
+              this.state.isConsole &&
321
+              <View className='console'>
322
+                <View className='console-deteal'>
323
+                  <View className='console-left' style={this.state.booksinfo.content_last_id == 0 ? 'opacity: 0.3;' : ''} onClick={e => (this.previous())}>
324
+                    <Image className='console-img' src='https://video-img.fyshark.com/1683623105394hsikah.png'></Image>
325
+                    上一章
326
+                  </View>
327
+                  <View className='console-content' onClick={e => (this.directory())}>
328
+                    <Image className='console-img' src='https://video-img.fyshark.com/1683623271853dhjkdhjl.png'></Image>
329
+                    目录
330
+                  </View>
331
+                  <View className='console-right' style={this.state.booksinfo.content_next_id == 0 ? 'opacity: 0.3;' : ''} onClick={e => (this.next())}>
332
+                    下一章
333
+                    <Image className='console-img' src='https://video-img.fyshark.com/1683623282017shiojh.png'></Image>
334
+                  </View>
335
+                </View>
336
+              </View>
337
+            }
338
+            <AtDrawer
339
+              show={this.state.show}
340
+              className='drawer'
341
+              onClose={e => (this.unDirectory())}
342
+              left
343
+              width='300px'
344
+              mask
345
+            >
346
+              <View className='drawer-content'>
347
+                <View className='drawer-over'>
348
+                  <View className='drawer-over-num'>共{this.state.directoryList.length}章</View>
349
+                  <View className='drawer-over-right' onClick={e => (this.changeOrderBySta())}>
350
+                    <View className='drawer-over-right-title'>倒序</View>
351
+                    <Image className='drawer-over-right-img' src='https://video-img.fyshark.com/1683631467893djaj.png'></Image>
352
+                  </View>
353
+                </View>
354
+                <View className='drawer-list'>
355
+                  {
356
+                    this.state.directoryList.map((item, index) => (
357
+                      <View className='drawer-info' key={index} onClick={e => (this.jumpChapter(item.id))}>
358
+                        <View className='drawer-info-left'>{item.name}</View>
359
+                        {
360
+                          item.is_vip == 1 &&
361
+                          <Image className='drawer-info-right' src='https://video-img.fyshark.com/1683633924768dsjkmalnjdl.png'></Image>
362
+                        }
363
+                      </View>
364
+                    ))
365
+                  }
366
+                </View>
367
+              </View>
368
+            </AtDrawer>
369
+          </View>
370
+        }
371
+        {this.state.booksinfo.book_is_vip == 1 &&
372
+          <View className='mine-content'>
373
+            <View className='novel-title'>{this.state.booksinfo.book_name}</View>
374
+            <RichText
375
+              className='member-title'
376
+              nodes={this.state.booksinfo.book_intro}></RichText>
377
+            <View className='payInfo'>
378
+              <View className='payInfo-content'>
379
+                <View className='payInfo-title'>开通会员,继续阅读</View>
380
+                {
381
+                  this.state.pay_status == 0 &&
382
+                  <View className='active' onClick={e => (this.openCode())}>
383
+                    <Image className='active-img' src='https://video-img.fyshark.com/1685349084726icon.png' mode='widthFix'></Image>
384
+                  </View>
385
+                }
386
+                <View className='purchase-list'>
387
+                  {
388
+                    this.state.purchaseList.map((item, index) => (
389
+                      <View className={this.state.selectVip == index ? 'purchase-info-active' : 'purchase-info'}
390
+                        key={index}
391
+                        onClick={e => (this.selectVip(item, index))}
392
+                      >
393
+                        {
394
+                          item.tag && item.tag.length > 0 &&
395
+                          < View className='purchase-tips'>{item.tag}</View>
396
+                        }
397
+                        <View className='purchase-title'>{item.title}</View>
398
+                        <View className='purchase-money'><View className='purchase-money-tip'>¥</View>{item.money / 100}</View>
399
+                        <View className='purchase-oldMoney'>¥{item.coin / 100}元</View>
400
+                        <View className='purchase-text'>
401
+                          <View className='purchase-button'>{item.sub_title}</View>
402
+                        </View>
403
+                      </View>
404
+                    ))
405
+                  }
406
+                </View>
407
+                <View className='payInfo-button' onClick={e => (this.toOrder())}>
408
+                  立即支付
409
+                </View>
410
+              </View>
411
+            </View>
412
+          </View>
413
+        }
414
+        {
415
+          this.state.isOpen &&
416
+          <View className='information'>
417
+            <View className='information-content'>
418
+              <View className='information-title'>
419
+                请输入口令码
420
+              </View>
421
+              <View className='search'>
422
+                <Input className='search-input' onInput={e => (this.searchCode(e))} type='text' placeholder='请输入口令码' focus />
423
+              </View>
424
+              <View className='information-console'>
425
+                <View className='cancel' onClick={e => (this.cancel())}>取消</View>
426
+                <View className='determine' onClick={e => (this.determine())}>确定</View>
427
+              </View>
428
+            </View>
429
+          </View>
430
+        }
431
+      </View>
432
+    )
433
+  }
434
+}

+ 377 - 0
src/pages/index/subpages/novelText/index.less

@@ -0,0 +1,377 @@
1
+.mine {
2
+  width: 100vw;
3
+  min-height: 100vh;
4
+  background: #F3F3F3;
5
+
6
+  .mine-content {
7
+    padding: 60px 40px;
8
+    padding-bottom: 180px;
9
+
10
+    .console-modil {
11
+      position: fixed;
12
+      left: 50%;
13
+      top: 20%;
14
+      transform: translateX(-50%);
15
+      width: 500px;
16
+      height: 900px;
17
+    }
18
+
19
+    .novel-title {
20
+      font-size: 40px;
21
+      font-family: PingFang SC;
22
+      font-weight: bold;
23
+      color: #353535;
24
+    }
25
+
26
+    .member-title {
27
+      margin-top: 30px;
28
+      padding-bottom: 280px;
29
+      font-size: 36px;
30
+      line-height: 54px;
31
+      font-family: PingFang SC;
32
+      font-weight: 500;
33
+      color: #5c5a5a;
34
+      letter-spacing: 4px;
35
+
36
+      line-height: 52px;
37
+    }
38
+
39
+    .console {
40
+      position: fixed;
41
+      width: 100%;
42
+      bottom: 78px;
43
+      left: 0;
44
+
45
+      .console-deteal {
46
+        width: 90%;
47
+        position: relative;
48
+        left: 50%;
49
+        transform: translateX(-50%);
50
+        padding: 32px 0;
51
+        display: flex;
52
+        justify-content: space-between;
53
+        font-size: 28px;
54
+        font-family: PingFang SC;
55
+        font-weight: 500;
56
+        color: #363636;
57
+        background: #FFFFFF;
58
+        border-radius: 46rpx;
59
+
60
+      }
61
+
62
+      .console-img {
63
+        width: 28px;
64
+        height: 28px;
65
+        margin: 0 10px;
66
+      }
67
+
68
+      .console-left {
69
+        flex: 1;
70
+        display: flex;
71
+        align-items: center;
72
+        justify-content: center;
73
+      }
74
+
75
+      .console-content {
76
+        flex: 1;
77
+        display: flex;
78
+        justify-content: center;
79
+        align-items: center;
80
+        border-left: 2px solid #A7A7A7;
81
+        border-right: 2px solid #A7A7A7;
82
+      }
83
+
84
+      .console-right {
85
+        flex: 1;
86
+        display: flex;
87
+        justify-content: center;
88
+        align-items: center;
89
+      }
90
+
91
+
92
+    }
93
+  }
94
+
95
+  .drawer {
96
+    overflow: hidden;
97
+  }
98
+
99
+  .at-drawer__content {
100
+    overflow-y: hidden;
101
+  }
102
+
103
+  .drawer-content {
104
+    padding: 20px 30px;
105
+    position: relative;
106
+
107
+    .drawer-over {
108
+      display: flex;
109
+      justify-content: space-between;
110
+      align-items: center;
111
+
112
+      .drawer-over-right {
113
+        display: flex;
114
+
115
+        .drawer-over-right-title {
116
+          font-size: 26px;
117
+          font-family: PingFang SC;
118
+          font-weight: bold;
119
+          color: #565656;
120
+        }
121
+
122
+        .drawer-over-right-img {
123
+          margin-left: 10px;
124
+          width: 36px;
125
+          height: 36px;
126
+          display: block;
127
+        }
128
+      }
129
+    }
130
+
131
+    .drawer-list {
132
+      height: 1320px;
133
+      font-size: 32px;
134
+      font-family: PingFang SC;
135
+      overflow-y: auto;
136
+      font-weight: bold;
137
+      color: #6b6969;
138
+
139
+      .drawer-info {
140
+        padding: 35px 0;
141
+        display: flex;
142
+        justify-content: space-between;
143
+        align-items: center;
144
+        border-bottom: 2px solid rgba(53, 53, 53, .1);
145
+      }
146
+
147
+      .drawer-info-right {
148
+        width: 36px;
149
+        height: 36px;
150
+        display: block;
151
+      }
152
+    }
153
+  }
154
+
155
+  .payInfo {
156
+    position: fixed;
157
+    left: 0;
158
+    top: 0;
159
+    width: 100vw;
160
+    height: 100vh;
161
+    background: rgba(49, 48, 48, .1);
162
+    display: flex;
163
+    align-items: flex-end;
164
+
165
+    .payInfo-content {
166
+
167
+      width: 100%;
168
+      padding: 44px 30px 66px 30px;
169
+      background: #FFFFFF;
170
+
171
+      .active {
172
+        margin-top: 20px;
173
+
174
+        .active-img {
175
+          width: 100%;
176
+        }
177
+      }
178
+
179
+      .payInfo-title {
180
+        text-align: center;
181
+        font-size: 28px;
182
+        font-family: PingFang SC;
183
+        font-weight: 800;
184
+        color: #353535;
185
+      }
186
+
187
+      .purchase-list {
188
+        margin-top: 68px;
189
+        display: flex;
190
+        justify-content: space-between;
191
+
192
+        .purchase-info {
193
+          position: relative;
194
+          width: 212px;
195
+          padding: 54px 0 26px 0;
196
+          background: #FFFFFF;
197
+          border: 2px solid rgba(136, 136, 136, 0.5);
198
+          border-radius: 20rpx;
199
+          text-align: center;
200
+          color: #353535;
201
+
202
+        }
203
+
204
+        .purchase-info-active {
205
+          position: relative;
206
+          width: 212px;
207
+          padding: 54px 0 26px 0;
208
+          background: #FCF5E9;
209
+          border: 2px solid #E7C06F;
210
+          border-radius: 20rpx;
211
+          text-align: center;
212
+          color: #AC5A27;
213
+
214
+        }
215
+
216
+        .purchase-tips {
217
+          position: absolute;
218
+          left: 0;
219
+          top: -22px;
220
+          background: linear-gradient(90deg, #F1A53E, #ED7535);
221
+          border-radius: 20rpx 0rpx 20rpx 0rpx;
222
+          padding: 8px 22px 6px 20px;
223
+          font-size: 24px;
224
+          font-family: PingFang SC;
225
+          font-weight: bold;
226
+          color: #FFFFFF;
227
+        }
228
+
229
+        .purchase-title {
230
+          font-size: 24px;
231
+          font-family: PingFang SC;
232
+          font-weight: 800;
233
+
234
+        }
235
+
236
+        .purchase-money {
237
+          margin-top: 20px;
238
+          font-size: 40px;
239
+          font-family: PingFang SC;
240
+          font-weight: 800;
241
+        }
242
+
243
+        .purchase-money-tip {
244
+          display: inline-block;
245
+          font-size: 20rpx;
246
+          font-family: PingFang SC;
247
+          font-weight: 800;
248
+
249
+        }
250
+
251
+        .purchase-oldMoney {
252
+          margin-top: 10px;
253
+          font-size: 24px;
254
+          font-family: PingFang SC;
255
+          text-decoration: line-through;
256
+          font-weight: bold;
257
+        }
258
+
259
+        .purchase-text {
260
+          margin-top: 24px;
261
+          display: flex;
262
+          justify-content: center;
263
+
264
+          .purchase-button {
265
+            width: 144px;
266
+            height: 38px;
267
+            line-height: 38px;
268
+            background: #F2D9A9;
269
+            border-radius: 19px;
270
+            font-size: 24px;
271
+            font-family: PingFang SC;
272
+            font-weight: 800;
273
+            color: #AC5A27;
274
+          }
275
+        }
276
+      }
277
+
278
+      .payInfo-button {
279
+        margin-top: 50px;
280
+        height: 76px;
281
+        line-height: 76px;
282
+        background: linear-gradient(180deg, #FBC578, #FC9D18);
283
+        border-radius: 38px;
284
+        font-size: 30px;
285
+        font-family: PingFang SC;
286
+        text-align: center;
287
+        font-weight: 800;
288
+        color: #FFFFFF;
289
+      }
290
+    }
291
+  }
292
+  .information {
293
+    position: fixed;
294
+    background: rgba(0, 0, 0, 0.4);
295
+    width: 100vw;
296
+    height: 100vh;
297
+    left: 0;
298
+    top: 0;
299
+    display: flex;
300
+    justify-content: center;
301
+    align-items: center;
302
+
303
+    .information-content {
304
+      border-radius: 20px;
305
+      width: 80%;
306
+      padding: 40px;
307
+      background: #fff;
308
+      .information-title{
309
+        text-align: center;
310
+        font-size: 36px;
311
+      }
312
+      .information-tips{
313
+        margin-top: 20px;
314
+        font-size: 24px;
315
+      }
316
+
317
+      .search-title {
318
+        margin-top: 40px;
319
+        font-size: 32px;
320
+      }
321
+
322
+      .search {
323
+        margin-top: 20px;
324
+        // width: 100%;
325
+        display: flex;
326
+        padding: 0 20px;
327
+        height: 70px;
328
+        align-items: center;
329
+        background: #EAEAEF;
330
+        border-radius: 10px;
331
+  
332
+        .search-img {
333
+          width: 36px;
334
+          height: 36px;
335
+          display: flex;
336
+        }
337
+  
338
+        .search-input {
339
+          margin-left: 6px;
340
+          flex: 1;
341
+          font-size: 24px;
342
+          font-family: PingFang SC;
343
+          font-weight: bold;
344
+          color: rgba(0, 0, 0, 0.4);
345
+          line-height: 82px;
346
+        }
347
+      }
348
+      .information-console{
349
+        margin-top: 40px;
350
+        display: flex;
351
+        justify-content: space-between;
352
+        align-items: center;
353
+        font-size: 28px;
354
+        text-align: center;
355
+        .cancel{
356
+          width: 240px;
357
+          height: 64px;
358
+          border-radius: 10px;
359
+          line-height: 64px;
360
+          border: 1px solid #777373;
361
+
362
+        }
363
+        .determine{
364
+          width: 240px;
365
+          height: 64px;
366
+          border-radius: 10px;
367
+          color: #fff;
368
+          line-height: 64px;
369
+          background:linear-gradient(90deg, #E04A4A, #EC705D);
370
+        }
371
+      }
372
+
373
+    }
374
+
375
+
376
+  }
377
+}

+ 5 - 0
src/pages/index/subpages/search/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '搜索',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 84 - 0
src/pages/index/subpages/search/index.jsx

@@ -0,0 +1,84 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    booksList: [],
11
+  }
12
+
13
+  componentWillMount () {
14
+    this.getBookSearch('')
15
+  }
16
+  componentDidShow () {
17
+  }
18
+  search (e) {
19
+    this.getBookSearch(e.detail.value)
20
+  }
21
+  getBookSearch (input_str) {
22
+    api.getBookSearch({ input_str: input_str }).then(res => {
23
+      if (res.code == 200) {
24
+        this.setState({
25
+          booksList: res.data.book_search
26
+        })
27
+      }
28
+    })
29
+  }
30
+  onReachBottom () {
31
+    // if (!this.state.total) {
32
+    //   this.setState({
33
+    //     page: this.state.page + 1
34
+    //   })
35
+    // }
36
+  }
37
+  toDetail (item, index) {
38
+    Taro.navigateTo({
39
+      url: `/pages/index/subpages/novel/index?book_id=${item.id}`
40
+    })
41
+  }
42
+
43
+  render () {
44
+    return (
45
+      <View className='mine'>
46
+        <View className='mine-content'>
47
+          <View className='search'>
48
+            <Image className='search-img' src='https://video-img.fyshark.com/1683600195067ahjdklj.png'></Image>
49
+            <Input className='search-input' onInput={e => (this.search(e))} type='text' placeholder='请输入书名/口令码' focus />
50
+          </View>
51
+          <View className='search-title'>热门推荐</View>
52
+          <View className='book-content'>
53
+            {
54
+              this.state.booksList.length > 0 &&
55
+              <View className='book-list'>
56
+                {
57
+                  this.state.booksList.map((item, index) => (
58
+                    <View className='book-info' key={index} onClick={e => (this.toDetail(item, index))}>
59
+                      <View className='book-info-left'>
60
+                        <Image className='book-img' src={item.img}></Image>
61
+                      </View>
62
+                      <View className='book-right'>
63
+                        <View className='book-title'>{item.name}</View>
64
+                        <View className='book-tips'>{tool.ellipsis(item.intro, 56)}</View>
65
+                        <View className='book-label-list'>
66
+                          {
67
+                            item.name_tag.length > 0 &&
68
+                            <View className='book-label-info'>
69
+                              {item.name_tag}
70
+                            </View>
71
+                          }
72
+                        </View>
73
+                      </View>
74
+                    </View>
75
+                  ))
76
+                }
77
+              </View>
78
+            }
79
+          </View>
80
+        </View>
81
+      </View>
82
+    )
83
+  }
84
+}

+ 97 - 0
src/pages/index/subpages/search/index.less

@@ -0,0 +1,97 @@
1
+.mine {
2
+  width: 100vw;
3
+  min-height: 100vh;
4
+
5
+  .mine-content {
6
+    padding: 36px 30px;
7
+
8
+    .search {
9
+      // width: 100%;
10
+      display: flex;
11
+      padding: 0 20px;
12
+      height: 70px;
13
+      align-items: center;
14
+      background: #EAEAEF;
15
+      border-radius: 35px;
16
+
17
+      .search-img {
18
+        width: 36px;
19
+        height: 36px;
20
+        display: flex;
21
+      }
22
+
23
+      .search-input {
24
+        margin-left: 6px;
25
+        flex: 1;
26
+        font-size: 24px;
27
+        font-family: PingFang SC;
28
+        font-weight: bold;
29
+        color: rgba(0, 0, 0, 0.4);
30
+        line-height: 82px;
31
+      }
32
+    }
33
+
34
+    .search-title {
35
+      margin-top: 60px;
36
+      font-size: 30px;
37
+      font-family: PingFang SC;
38
+      font-weight: 800;
39
+      color: #363636;
40
+    }
41
+
42
+    .book-content {
43
+      padding: 36px 30px 50px 0;
44
+
45
+      .book-list {
46
+        .book-info {
47
+          display: flex;
48
+          margin-bottom: 30px;
49
+
50
+          .book-info-left {
51
+            .book-img {
52
+              width: 158px;
53
+              height: 220px;
54
+              display: block;
55
+              border-radius: 10px;
56
+            }
57
+          }
58
+
59
+          .book-right {
60
+            margin-left: 20px;
61
+
62
+            .book-title {
63
+              font-size: 26px;
64
+              font-family: PingFang SC;
65
+              font-weight: 800;
66
+              color: #363636;
67
+            }
68
+
69
+            .book-tips {
70
+              font-size: 22px;
71
+              margin-top: 22px;
72
+              font-family: PingFang SC;
73
+              font-weight: bold;
74
+              color: #888888;
75
+            }
76
+            .book-label-list {
77
+              display: flex;
78
+              margin-top: 28px;
79
+
80
+              .book-label-info {
81
+                margin-left: 10px;
82
+                padding: 6px 12px;
83
+                border-radius: 10px;
84
+                font-size: 18px;
85
+                font-family: PingFang SC;
86
+                font-weight: 800;
87
+                letter-spacing: 1px;
88
+                background:rgb(212 242 245 / 54%);
89
+                color: #4ec7da;
90
+              }
91
+            }
92
+          }
93
+        }
94
+      }
95
+    }
96
+  }
97
+}

+ 5 - 0
src/pages/mine/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '我的',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 200 - 0
src/pages/mine/index.jsx

@@ -0,0 +1,200 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../service/index'
4
+import tool from '../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    userInfo: {},
11
+    pageList: [
12
+      {
13
+        title: '阅读记录',
14
+        icon: 'https://video-img.fyshark.com/1683703300058shsho.png',
15
+        url: '/pages/mine/subpages/history/index',
16
+        isOpen: true
17
+      },
18
+      {
19
+        title: '我的收益',
20
+        icon: 'https://video-img.fyshark.com/1683703315275akljs.png',
21
+        url: '/pages/mine/subpages/makeMoney/index',
22
+        isOpen: true
23
+      },
24
+      {
25
+        title: '赚钱计划',
26
+        icon: 'https://video-img.fyshark.com/1683703340452dnkhfgo.png',
27
+        url: '/pages/mine/subpages/moneyPlay/index',
28
+        isOpen: true
29
+      },
30
+      {
31
+        title: '我的团队',
32
+        icon: 'https://video-img.fyshark.com/1685065866448dslajd.png',
33
+        url: '/pages/mine/subpages/team/index',
34
+        isOpen: true
35
+      },
36
+      {
37
+        title: '通知公告',
38
+        icon: 'https://video-img.fyshark.com/1683703331018dhjldj.png',
39
+        url: '',
40
+        isOpen: false
41
+      }
42
+
43
+    ],
44
+    adUnitId: '',
45
+    ad_status: ''
46
+  }
47
+
48
+  componentWillMount () {
49
+    this.getAppConfig()
50
+  }
51
+  componentDidShow () {
52
+    Taro.showLoading({
53
+      title: '加载中',
54
+    })
55
+    if (!Taro.getStorageSync('session_key')) {
56
+      setTimeout(() => {
57
+        this.getUserInfo()
58
+      }, 1000)
59
+    } else {
60
+      this.getUserInfo()
61
+    }
62
+  }
63
+  getAppConfig () {
64
+    api.getAppConfig().then(res => {
65
+      if (res.code == 200) {
66
+        this.setState({
67
+          // adUnitId: res.data.adUnitId,
68
+          ad_status: res.data.ad_status
69
+        })
70
+      }
71
+    })
72
+  }
73
+  getUserInfo () {
74
+    api.getUserInfo().then(res => {
75
+      Taro.hideLoading()
76
+      if (res.code == 200) {
77
+        this.setState({
78
+          userInfo: res.data
79
+        })
80
+      }
81
+    })
82
+  }
83
+  toVipDetial () {
84
+    Taro.navigateTo({
85
+      url: '/pages/mine/subpages/vipDetail/index'
86
+    })
87
+  }
88
+  toDetial (item, index) {
89
+    if (!item.isOpen) {
90
+      Taro.showToast({ title: '敬请期待', icon: 'none' })
91
+      return
92
+    }
93
+    Taro.navigateTo({
94
+      url: item.url
95
+    })
96
+  }
97
+  onShareAppMessage(res) {
98
+    if (res.from === 'button') {
99
+      // 来自页面内转发按钮
100
+      console.log(res.target)
101
+    }
102
+    return {
103
+      channel: "video",
104
+      title: "测试分享视频",
105
+      desc: "测试描述",
106
+      extra: {
107
+        videoTopics: ["hello", "hi"],
108
+        withVideoId: true,
109
+      },
110
+      success(res) {
111
+        /* res结构:{errMsg: string, videoId: string } */
112
+        console.log(res.videoId);
113
+        tt.navigateToVideoView({
114
+          videoId: res.videoId,
115
+          success: (res) => {
116
+            /* res结构: {errMsg: string } */
117
+            console.log("done");
118
+          },
119
+          fail: (err) => {
120
+            if (err.errCode === 1006) {
121
+              tt.showToast({
122
+                title: "something wrong with your network",
123
+              });
124
+            }
125
+          },
126
+        });
127
+      },
128
+    }
129
+  }
130
+
131
+  render () {
132
+    return (
133
+      <View className='mine'>
134
+        <View className='user-info'>
135
+          <View className='info-left'>
136
+            <Image className='user-img' src={this.state.userInfo.head_img}></Image>
137
+          </View>
138
+          <View className='info-right'>
139
+            <View className='user-name'>{this.state.userInfo.user_name}
140
+              {
141
+                this.state.userInfo.is_vip == 1 &&
142
+                <Image className='vip-img' src='https://video-img.fyshark.com/1683860337987f-vip3.png'></Image>
143
+              }
144
+            </View>
145
+            <View className='user-id'>用户ID:{this.state.userInfo.id}</View>
146
+          </View>
147
+        </View>
148
+        {
149
+          this.state.userInfo.is_vip == 0 ?
150
+            <View className='user-vip-banner' onClick={e => (this.toVipDetial())}>
151
+              <Image className='banner-img' mode='widthFix' src='https://video-img.fyshark.com/1683705427196VIP.png'></Image>
152
+            </View>
153
+            :
154
+            <View className='user-vip-banner-have'>
155
+              <View className='vip-banner-have-left'>
156
+                <Image className='vip-banner-img' src='https://video-img.fyshark.com/1683860337987f-vip3.png'></Image>
157
+                <View className='vip-banner-tips'>已开通会员,有效期至:{tool.formattedDate(this.state.userInfo.time_is_vip)}</View>
158
+              </View>
159
+              <View className='vip-banner-have-right' onClick={e => (this.toVipDetial())}>
160
+                续费
161
+              </View>
162
+            </View>
163
+        }
164
+        <View className='user-page'>
165
+          {
166
+            this.state.pageList.map((item, index) => (
167
+              <View className='page-info' key={index} onClick={e => (this.toDetial(item, index))}>
168
+                <View className='page-left'>
169
+                  <Image className='page-icon' src={item.icon}></Image>
170
+                  <View className='page-title'>{item.title}</View>
171
+                </View>
172
+                <Image className='page-next' src='https://video-img.fyshark.com/1683703470907shkls.png'></Image>
173
+              </View>
174
+            ))
175
+          }
176
+          <Button className=' button'
177
+            plain type='default' open-type='contact'>
178
+            <View className='page-info'>
179
+              <View className='page-left'>
180
+                <Image className='page-icon' src='https://video-img.fyshark.com/1683703284336shkdj.png'></Image>
181
+                <View className='page-title'>联系客服</View>
182
+              </View>
183
+              <Image className='page-next' src='https://video-img.fyshark.com/1683703470907shkls.png'></Image>
184
+            </View>
185
+          </Button>
186
+          {/* <Button className=' button'
187
+            plain type='default' open-type='share'>
188
+            <View className='page-info'>
189
+              <View className='page-left'>
190
+                <Image className='page-icon' src='https://video-img.fyshark.com/1683703284336shkdj.png'></Image>
191
+                <View className='page-title'>转发</View>
192
+              </View>
193
+              <Image className='page-next' src='https://video-img.fyshark.com/1683703470907shkls.png'></Image>
194
+            </View>
195
+          </Button> */}
196
+        </View>
197
+      </View>
198
+    )
199
+  }
200
+}

+ 149 - 0
src/pages/mine/index.less

@@ -0,0 +1,149 @@
1
+.mine {
2
+  min-height: 100vh;
3
+  padding: 70px 30px;
4
+
5
+  // padding-bottom: 120px;
6
+  .user-info {
7
+    padding: 0 12px;
8
+    display: flex;
9
+    align-items: center;
10
+
11
+    .info-left {
12
+      .user-img {
13
+        width: 130px;
14
+        height: 130px;
15
+        display: block;
16
+        border-radius: 50%;
17
+      }
18
+    }
19
+
20
+    .info-right {
21
+      margin-left: 28px;
22
+
23
+      .user-name {
24
+        font-size: 34px;
25
+        font-family: PingFang SC;
26
+        font-weight: 800;
27
+        color: #353535;
28
+        display: flex;
29
+        align-items: center;
30
+      }
31
+
32
+      .vip-img {
33
+        margin-left: 16px;
34
+        width: 50px;
35
+        height: 40px;
36
+        display: block;
37
+      }
38
+
39
+      .user-id {
40
+        margin-top: 22px;
41
+        font-size: 24px;
42
+        font-family: PingFang SC;
43
+        font-weight: 800;
44
+        color: #888888;
45
+      }
46
+
47
+    }
48
+  }
49
+
50
+  .user-vip-banner {
51
+    margin-top: 60px;
52
+    width: 100%;
53
+    height: 200px;
54
+
55
+    .banner-img {
56
+      width: 100%;
57
+      display: block;
58
+    }
59
+  }
60
+
61
+  .user-vip-banner-have {
62
+    margin-top: 60px;
63
+    padding: 30px;
64
+    border-radius: 20px;
65
+    background: linear-gradient(90deg, #d48510, #2d291f, #E7AF51);
66
+    display: flex;
67
+    justify-content: space-between;
68
+    align-items: center;
69
+
70
+    .vip-banner-have-left {
71
+      .vip-banner-img {
72
+        width: 60px;
73
+        height: 46px;
74
+        display: block;
75
+      }
76
+
77
+      .vip-banner-tips {
78
+        margin-top: 11px;
79
+        font-size: 26px;
80
+        font-family: PingFang SC;
81
+        font-weight: 800;
82
+        color: #fbf8f8;
83
+      }
84
+    }
85
+    .vip-banner-have-right{
86
+      padding: 15px 40px;
87
+      font-size: 28px;
88
+      border-radius: 30px;
89
+      font-family: PingFang SC;
90
+      font-weight: 800;
91
+      background: #fbf8f8;
92
+      color: #353535;
93
+    }
94
+  }
95
+
96
+  .user-page {
97
+    margin-top: 28px;
98
+
99
+    .page-info {
100
+      width: 100%;
101
+      padding: 28px 0;
102
+      display: flex;
103
+      justify-content: space-between;
104
+      align-items: center;
105
+      border-bottom: 1px solid rgba(94, 94, 93, .1);
106
+
107
+      .page-left {
108
+        display: flex;
109
+        align-items: center;
110
+
111
+        .page-icon {
112
+          width: 36px;
113
+          height: 36px;
114
+          display: block;
115
+        }
116
+
117
+        .page-title {
118
+          margin-left: 16px;
119
+          font-size: 28px;
120
+          font-family: PingFang SC;
121
+          font-weight: 800;
122
+          color: #353535;
123
+        }
124
+      }
125
+
126
+      .page-next {
127
+        width: 30px;
128
+        height: 30px;
129
+      }
130
+    }
131
+  }
132
+  .button {
133
+    padding: 0 0 ;
134
+    border: none !important;
135
+    border-bottom: 1px solid rgba(94, 94, 93, .1) !important;
136
+    line-height: normal !important;
137
+    outline: none !important;
138
+    background-color: rgba(0,0,0,0) !important;
139
+  }
140
+  
141
+  button::after {
142
+    border: none;
143
+  }
144
+  .ad-video{
145
+    margin-top: 10px;
146
+    width: 100%;
147
+    height: 200px;
148
+  }
149
+}

+ 5 - 0
src/pages/mine/subpages/history/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '阅读记录',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 213 - 0
src/pages/mine/subpages/history/index.jsx

@@ -0,0 +1,213 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    bookList: [],
11
+    isEdit: false,
12
+    isOverSelect: false
13
+  }
14
+
15
+  componentWillMount () {
16
+  }
17
+  componentDidShow () {
18
+    this.myBookRead()
19
+  }
20
+  onReachBottom () {
21
+    // if (!this.state.total) {
22
+    //   this.setState({
23
+    //     page: this.state.page + 1
24
+    //   })
25
+    // }
26
+  }
27
+  myBookRead () {
28
+    api.myBookRead().then(res => {
29
+      if (res.code == 200) {
30
+        this.setState({
31
+          bookList: res.data
32
+        })
33
+      }
34
+    })
35
+  }
36
+  changeEdit () {
37
+    this.setState({
38
+      isEdit: true
39
+    })
40
+  }
41
+  cancelEdit () {
42
+    let _bookList = this.state.bookList
43
+    _bookList.forEach((item, index) => {
44
+      item.isSelect = false
45
+    })
46
+    this.setState({
47
+      bookList: _bookList,
48
+      isOverSelect: false,
49
+      isEdit: false
50
+    })
51
+  }
52
+  deleteEdit () {
53
+    let that = this
54
+    Taro.showModal({
55
+      title: '提示',
56
+      content: '确定删除吗?',
57
+      success: function (res) {
58
+        if (res.confirm) {
59
+          let book_ids = []
60
+          that.state.bookList.forEach(item => {
61
+            if (item.isSelect) {
62
+              book_ids.push(item.book_id)
63
+            }
64
+          })
65
+
66
+          api.deleteBookRead({ book_ids: book_ids.toString() }).then(res => {
67
+            if (res.code == 200) {
68
+              that.myBookRead()
69
+              that.setState({
70
+                isEdit: false,
71
+                isOverSelect: false
72
+              })
73
+            }
74
+          })
75
+        } else if (res.cancel) {
76
+          console.log('用户点击取消')
77
+        }
78
+
79
+      }
80
+    })
81
+
82
+  }
83
+  overSelect () {
84
+    let _bookList = this.state.bookList
85
+    _bookList.forEach((item, index) => {
86
+      item.isSelect = !this.state.isOverSelect
87
+    })
88
+    this.setState({
89
+      bookList: _bookList,
90
+      isOverSelect: !this.state.isOverSelect,
91
+    })
92
+  }
93
+  changeSelect (item, index) {
94
+    if (!this.state.isEdit) return
95
+    let _bookList = this.state.bookList
96
+    _bookList.forEach((item, i) => {
97
+      if (index == i) {
98
+        item.isSelect = !this.state.bookList[index].isSelect
99
+      }
100
+    })
101
+
102
+    this.setState({
103
+      bookList: _bookList
104
+    }, () => {
105
+      let list = this.state.bookList.filter(res => {
106
+        return !res.isSelect
107
+      })
108
+      if (list.length == 0) {
109
+        this.setState({
110
+          isOverSelect: true
111
+        })
112
+      } else {
113
+        this.setState({
114
+          isOverSelect: false
115
+        })
116
+      }
117
+    })
118
+  }
119
+  toDetail (item, index) {
120
+    if (this.state.isEdit) {
121
+      let _bookList = this.state.bookList
122
+      _bookList.forEach((item, i) => {
123
+        if (index == i) {
124
+          item.isSelect = !this.state.bookList[index].isSelect
125
+        }
126
+      })
127
+
128
+      this.setState({
129
+        bookList: _bookList
130
+      }, () => {
131
+        let list = this.state.bookList.filter(res => {
132
+          return !res.isSelect
133
+        })
134
+        if (list.length == 0) {
135
+          this.setState({
136
+            isOverSelect: true
137
+          })
138
+        } else {
139
+          this.setState({
140
+            isOverSelect: false
141
+          })
142
+        }
143
+      })
144
+    } else {
145
+      Taro.navigateTo({
146
+        url: `/pages/index/subpages/novelText/index?book_id=${item.book_id}`
147
+      })
148
+    }
149
+  }
150
+
151
+  render () {
152
+    return (
153
+      <View className='mine'>
154
+        {
155
+          this.state.bookList.length == 0 ?
156
+            <View className='empty'>
157
+              <Image className='empty-img' src='https://video-img.fyshark.com/1683801289275slsll.png'></Image>
158
+              <View className='empty-tips'>暂时还没有阅读记录哦!</View>
159
+            </View>
160
+            :
161
+            <View className='mine-content'>
162
+              {
163
+                !this.state.isEdit ?
164
+                  <View className='edit'>
165
+                    <View className='edit-content' onClick={e => (this.changeEdit())}>
166
+                      <Image className='edit-img' src='https://video-img.fyshark.com/1683687193883dhksjn.png'></Image>
167
+                      编辑
168
+                    </View>
169
+                  </View>
170
+                  :
171
+                  <View className='select'>
172
+                    <View className='select-content' onClick={e => (this.overSelect())}>
173
+                      <Image className='select-img' src={this.state.isOverSelect ? 'https://video-img.fyshark.com/1683698925516hnakjl.png' : 'https://video-img.fyshark.com/1683698074122dhkahj.png'}></Image>
174
+                      全选
175
+                    </View>
176
+                  </View>
177
+              }
178
+              <View className='book-content'>
179
+                <View className='book-list'>
180
+                  {
181
+                    this.state.bookList.map((item, index) => (
182
+                      <View className='book-info' key={index} onClick={e => (this.toDetail(item, index))}>
183
+                        <View className='book-info-left'>
184
+                          <Image className='book-img' src={item.img}></Image>
185
+                          {
186
+                            this.state.isEdit &&
187
+                            <Image className='select-icon' src={item.isSelect ? 'https://video-img.fyshark.com/1683699004116sihel.png' : 'https://video-img.fyshark.com/1683698064582dhka.png'}></Image>
188
+                          }
189
+                        </View>
190
+                        <View className='book-right'>
191
+                          <View className='book-title'>{item.name}</View>
192
+                          <View className='book-tips'>{tool.ellipsis(item.album_name, 56)}</View>
193
+                        </View>
194
+                      </View>
195
+                    ))
196
+                  }
197
+                </View>
198
+              </View>
199
+              {
200
+                this.state.isEdit &&
201
+                <View className='edit-console'>
202
+                  <View className='console'>
203
+                    <View className='cancellation' onClick={e => (this.cancelEdit())}>取消</View>
204
+                    <View className='delete' onClick={e => (this.deleteEdit())}>删除</View>
205
+                  </View>
206
+                </View>
207
+              }
208
+            </View>
209
+        }
210
+      </View>
211
+    )
212
+  }
213
+}

+ 162 - 0
src/pages/mine/subpages/history/index.less

@@ -0,0 +1,162 @@
1
+.mine {
2
+  width: 100vw;
3
+  position: relative;
4
+
5
+  .empty {
6
+    position: absolute;
7
+    left: 50%;
8
+    top: 30%;
9
+    transform: translateX(-50%);
10
+
11
+    .empty-img {
12
+      width: 360px;
13
+      height: 360px;
14
+    }
15
+
16
+    .empty-tips {
17
+      font-size: 26rpx;
18
+      font-family: PingFang SC;
19
+      font-weight: bold;
20
+      text-align: center;
21
+      color: #888888;
22
+    }
23
+  }
24
+
25
+  .mine-content {
26
+    padding: 70px 50px;
27
+
28
+    .edit {
29
+      display: flex;
30
+      justify-content: flex-end;
31
+      align-items: center;
32
+      font-size: 28px;
33
+      font-family: PingFang SC;
34
+      font-weight: bold;
35
+      color: #888888;
36
+
37
+      .edit-content {
38
+        display: flex;
39
+        align-items: center;
40
+      }
41
+
42
+      .edit-img {
43
+        width: 30px;
44
+        height: 30px;
45
+        display: block;
46
+        margin-right: 12px;
47
+      }
48
+    }
49
+
50
+    .select {
51
+      display: flex;
52
+      align-items: center;
53
+      font-size: 28px;
54
+      font-family: PingFang SC;
55
+      font-weight: bold;
56
+      color: #888888;
57
+
58
+      .select-content {
59
+        display: flex;
60
+        align-items: center;
61
+      }
62
+
63
+      .select-img {
64
+        width: 30px;
65
+        height: 30px;
66
+        display: block;
67
+        margin-right: 12px;
68
+      }
69
+    }
70
+
71
+    .book-content {
72
+      margin-top: 16px;
73
+
74
+      .book-list {
75
+        .book-info {
76
+          display: flex;
77
+          margin-bottom: 30px;
78
+
79
+          .book-info-left {
80
+            position: relative;
81
+
82
+            .book-img {
83
+              width: 158px;
84
+              height: 220px;
85
+              display: block;
86
+              border-radius: 10px;
87
+            }
88
+
89
+            .select-icon {
90
+              position: absolute;
91
+              bottom: 14px;
92
+              right: 8px;
93
+              width: 48px;
94
+              height: 48px;
95
+              display: block;
96
+            }
97
+          }
98
+
99
+          .book-right {
100
+            margin-left: 20px;
101
+
102
+            .book-title {
103
+              font-size: 26px;
104
+              font-family: PingFang SC;
105
+              font-weight: 800;
106
+              color: #363636;
107
+            }
108
+
109
+            .book-tips {
110
+              font-size: 22px;
111
+              margin-top: 22px;
112
+              font-family: PingFang SC;
113
+              font-weight: bold;
114
+              color: #888888;
115
+            }
116
+          }
117
+        }
118
+      }
119
+    }
120
+
121
+    .edit-console {
122
+      width: 100%;
123
+      position: fixed;
124
+      bottom: 38px;
125
+      left: 0;
126
+
127
+      .console {
128
+        padding: 0 40px;
129
+        display: flex;
130
+        justify-content: space-between;
131
+        align-items: center;
132
+
133
+        .cancellation {
134
+          width: 330px;
135
+          height: 66px;
136
+          line-height: 66px;
137
+          text-align: center;
138
+          background: #E9EAEA;
139
+          border-radius: 10px;
140
+          font-size: 26px;
141
+          font-family: PingFang SC;
142
+          font-weight: bold;
143
+          color: #363636;
144
+        }
145
+
146
+        .delete {
147
+          width: 330px;
148
+          height: 66px;
149
+          line-height: 66px;
150
+          text-align: center;
151
+          background: linear-gradient(90deg, #E04A4A, #EC705D);
152
+          border-radius: 10px;
153
+          font-size: 26px;
154
+          font-family: PingFang SC;
155
+          font-weight: bold;
156
+          color: #FFFFFF;
157
+        }
158
+      }
159
+    }
160
+  }
161
+
162
+}

+ 5 - 0
src/pages/mine/subpages/makeMoney/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '我的收益',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 112 - 0
src/pages/mine/subpages/makeMoney/index.jsx

@@ -0,0 +1,112 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    earningslist: [],//收益详情
11
+    add_sum: 0,//累计收益
12
+    already_sum: 0,//已提取收益
13
+    now_sum: 0,//可提现金额
14
+    extract_status: 0,
15
+    page: 1,
16
+    page_size: 20,
17
+    isNext: true,//是否还有下一页
18
+  }
19
+
20
+  componentWillMount () {
21
+    this.getEarningsInfo()
22
+  }
23
+  componentDidShow () {
24
+  }
25
+  getEarningsInfo () {
26
+    let params = {
27
+      page: this.state.page,
28
+      page_size: this.state.page_size,
29
+    }
30
+    api.getEarningsInfo(params).then(res => {
31
+      if (res.code == 200) {
32
+        let isNext = true
33
+        console.log(res.data.earningslist.length, 'res.data.book_cat.length');
34
+        if (res.data.earningslist.length < 20) {
35
+          isNext = false
36
+        }
37
+        this.setState({
38
+          earningslist: [...res.data.earningslist, ...this.state.earningslist],
39
+          add_sum: res.data.add_sum,
40
+          already_sum: res.data.already_sum,
41
+          now_sum: res.data.now_sum,
42
+          extract_status: res.data.extract_status,
43
+          isNext: isNext,
44
+          page: isNext ? this.state.page + 1 : this.state.page
45
+        })
46
+      }
47
+    })
48
+  }
49
+  withdrawal () {
50
+    if (this.state.extract_status == 2) {
51
+      Taro.showToast({
52
+        title: `目前已有提现金额在审核中...`,
53
+        icon: 'none',
54
+      });
55
+      return
56
+    }
57
+    Taro.navigateTo({
58
+      url: '/pages/mine/subpages/withdrawal/index'
59
+    })
60
+  }
61
+  onReachBottom () {
62
+    if (this.state.isNext) {
63
+      this.getEarningsInfo()
64
+    }
65
+  }
66
+
67
+  render () {
68
+    return (
69
+      <View className='mine'>
70
+        <View className='make-info'>
71
+          <View className='make-info-top'>
72
+            <View className='make-info-top-left'>
73
+              <View className='make-info-title'>目前可提取总金额(元)</View>
74
+              <View className='make-info-money'>{this.state.now_sum / 100}</View>
75
+            </View>
76
+            {
77
+              this.state.extract_status != 4 &&
78
+              <View className='make-info-top-right' onClick={e => (this.withdrawal())}>{this.state.extract_status == 2 ? '审核中' : '提现'}</View>
79
+            }
80
+          </View>
81
+          <View className='make-info-bottom'>
82
+            <View className='make-info-bottom-left'>
83
+              <View className='make-all-money-title'>累计收益(元)</View>
84
+              <View className='make-all-money-num'>{this.state.add_sum / 100}</View>
85
+            </View>
86
+            <View className='make-info-bottom-right'>
87
+              <View className='withdrawal-title'>已提现金额(元)</View>
88
+              <View className='withdrawal-num'>{this.state.already_sum / 100}</View>
89
+            </View>
90
+          </View>
91
+        </View>
92
+        <View className='income'>
93
+          <View className='income-title'>收益详情</View>
94
+          <View className='income-list'>
95
+            {
96
+              this.state.earningslist.map((item, index) => (
97
+                <View className='income-info' key={index}>
98
+                  <View className='income-left'>
99
+                    <View className='income-info-title'>{item.money_name}</View>
100
+                    <View className='income-date'>{item.time}</View>
101
+                  </View>
102
+                  <View className='income-right' style={item.earnings_type == 0 ? 'color: #84f121' : 'color: #f13921;'}>{item.earnings_type == 0 ? '+' : '-'}{item.money / 100}</View>
103
+                </View>
104
+              ))
105
+            }
106
+          </View>
107
+          <View className='income-tips'>已经到底啦</View>
108
+        </View>
109
+      </View >
110
+    )
111
+  }
112
+}

+ 105 - 0
src/pages/mine/subpages/makeMoney/index.less

@@ -0,0 +1,105 @@
1
+.mine {
2
+  min-height: 100vh;
3
+  background: #e4e4e4;
4
+  padding: 40px 40px;
5
+
6
+  .make-info {
7
+    padding: 40px;
8
+    background: linear-gradient(180deg, #efbe66, #ec8f05);
9
+    border-radius: 30px;
10
+    font-size: 24px;
11
+    color: #fff;
12
+
13
+    .make-info-top {
14
+      display: flex;
15
+      justify-content: space-between;
16
+      align-items: center;
17
+      padding-bottom: 40px;
18
+      border-bottom: 1px solid #ffffff44;
19
+
20
+      .make-info-top-left {
21
+        .make-info-title {}
22
+
23
+        .make-info-money {
24
+          margin-top: 20px;
25
+          font-size: 28px;
26
+        }
27
+      }
28
+
29
+      .make-info-top-right {
30
+        padding: 10px 20px;
31
+        background: #fff;
32
+        color: #d48510;
33
+        border-radius: 28px;
34
+        font-size: 28px;
35
+      }
36
+    }
37
+
38
+    .make-info-bottom {
39
+      margin-top: 30px;
40
+      display: flex;
41
+      justify-content: space-between;
42
+      align-items: center;
43
+
44
+      .make-info-bottom-left {
45
+        .make-all-money-title {}
46
+
47
+        .make-all-money-num {
48
+          margin-top: 20px;
49
+          font-size: 26px;
50
+        }
51
+      }
52
+
53
+      .make-info-bottom-right {
54
+        .withdrawal-num {
55
+          margin-top: 20px;
56
+          font-size: 26px;
57
+        }
58
+      }
59
+    }
60
+  }
61
+  .income{
62
+    // height: 900px;
63
+    // overflow-y: auto;
64
+    margin-top: 20px;
65
+    padding: 40px 0;
66
+    border-radius: 10px;
67
+    background: #ffffff;
68
+    .income-title{
69
+      font-size: 28px;
70
+      text-align: center;
71
+      padding: 20px 0;
72
+      border-bottom: 1px solid rgba(68, 68, 68, 0.1);
73
+    }
74
+    .income-list{
75
+      padding:0 40px ;
76
+      .income-info{
77
+        display: flex;
78
+        justify-content: space-between;
79
+        align-items: center;
80
+        border-bottom: 1px solid rgba(68, 68, 68, 0.1);
81
+        padding: 30px 0;
82
+        .income-left{
83
+          .income-info-title{
84
+            font-size: 28px;
85
+  
86
+          }
87
+          .income-date{
88
+            margin-top: 10px;
89
+            font-size: 24px;
90
+            color: #4e4d4d;
91
+          }
92
+        }
93
+        .income-right{
94
+          
95
+        }
96
+      }
97
+    }
98
+    .income-tips{
99
+      margin-top: 40px;
100
+      font-size: 24px;
101
+      text-align: center;
102
+      color: rgba(13, 12, 12, 0.4);
103
+    }
104
+  }
105
+}

+ 5 - 0
src/pages/mine/subpages/moneyPlay/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '赚钱计划',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 36 - 0
src/pages/mine/subpages/moneyPlay/index.jsx

@@ -0,0 +1,36 @@
1
+import { Component } from 'react'
2
+import { View, RichText, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    content: ''
11
+  }
12
+
13
+  componentWillMount () {
14
+    this.getInformationDetails()
15
+  }
16
+
17
+  componentDidShow () {
18
+  }
19
+  getInformationDetails () {
20
+    api.getInformationDetails({ news_id: 24 }).then(res => {
21
+      if (res.code == 200) {
22
+        this.setState({
23
+          content: res.data.content
24
+        })
25
+      }
26
+    })
27
+  }
28
+
29
+  render () {
30
+    return (
31
+      <View className='mine'>
32
+        <RichText className='member-title' nodes={this.state.content}></RichText>
33
+      </View>
34
+    )
35
+  }
36
+}

+ 4 - 0
src/pages/mine/subpages/moneyPlay/index.less

@@ -0,0 +1,4 @@
1
+.mine {
2
+  min-height: 100vh;
3
+  padding: 40px 20px;
4
+}

+ 5 - 0
src/pages/mine/subpages/team/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '我的团队',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 97 - 0
src/pages/mine/subpages/team/index.jsx

@@ -0,0 +1,97 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    my_code: "",//邀请码
11
+    my_role_name: "普通用户",
12
+    my_role_status: 0,
13
+    team_sum: 0
14
+  }
15
+
16
+  componentWillMount () {
17
+  }
18
+  componentDidShow () {
19
+    this.getTeamInfo()
20
+
21
+  }
22
+  getTeamInfo () {
23
+    api.getTeamInfo().then(res => {
24
+      if (res.code == 200) {
25
+        this.setState({
26
+          my_code: res.data.my_code,
27
+          my_role_name: res.data.my_role_name,
28
+          my_role_status: res.data.my_role_status,
29
+          team_sum: res.data.team_sum
30
+        })
31
+      }
32
+    })
33
+  }
34
+  teamEnqueue () {
35
+    if (this.state.my_role_status == 0) {
36
+      api.teamEnqueue({ input_str: this.state.my_code }).then(res => {
37
+        if (res.code == 200) {
38
+          this.getTeamInfo()
39
+        } else {
40
+          Taro.showToast({ icon: 'none', title: res.msg });
41
+        }
42
+      })
43
+    } else {
44
+      Taro.setClipboardData({
45
+        data: this.state.my_code,
46
+        success: function (res) {
47
+          Taro.showToast({ icon: 'none', title: '邀请码复制成功' });
48
+        }
49
+      })
50
+    }
51
+  }
52
+  search (e) {
53
+    console.log(e);
54
+    this.setState({
55
+      my_code: e.detail.value
56
+    })
57
+  }
58
+
59
+
60
+  render () {
61
+    return (
62
+      <View className='mine'>
63
+        <View className='content'>
64
+          {
65
+            this.state.my_role_status == 0 ?
66
+              <View>
67
+                <View className='title'>请输入邀请码</View>
68
+                <View className='search'>
69
+                  <Image className='search-img' src='https://video-img.fyshark.com/1683600195067ahjdklj.png'></Image>
70
+                  <Input className='search-input' onInput={e => (this.search(e))} type='text' placeholder='请输入邀请码' focus />
71
+                </View>
72
+              </View>
73
+              :
74
+              <View>
75
+                <View className='title'>团队邀请码:</View>
76
+                <View className='code'>{this.state.my_code}</View>
77
+              </View>
78
+          }
79
+          <View className='console'>
80
+            <View className='console-button' onClick={e => (this.teamEnqueue())}>{this.state.my_role_status == 0 ? "确定" : '一键复制'}</View>
81
+          </View>
82
+          <View className='user-info'>
83
+            <View className='identity'>我的角色:{this.state.my_role_name}</View>
84
+            {
85
+              this.state.my_role_status != 1 &&
86
+              <View className='status'>我的邀请状态:{this.state.my_role_status == 0 ? '未邀请' : '已加入'}</View>
87
+            }
88
+            {
89
+              this.state.my_role_status == 1 &&
90
+              <View className='status'>我的团队人数:{this.state.team_sum}人</View>
91
+            }
92
+          </View>
93
+        </View>
94
+      </View>
95
+    )
96
+  }
97
+}

+ 73 - 0
src/pages/mine/subpages/team/index.less

@@ -0,0 +1,73 @@
1
+.mine {
2
+  width: 100vw;
3
+  position: relative;
4
+  
5
+  .content {
6
+    padding: 40px;
7
+    margin-top: 60px;
8
+
9
+    .title {
10
+      font-size: 28px;
11
+      margin-bottom: 30px;
12
+    }
13
+    .code{
14
+      text-align: center;
15
+      font-size: 48px;
16
+    }
17
+
18
+    .search {
19
+      // width: 100%;
20
+      display: flex;
21
+      padding: 0 20px;
22
+      height: 70px;
23
+      align-items: center;
24
+      background: #EAEAEF;
25
+      border-radius: 10px;
26
+
27
+      .search-img {
28
+        width: 36px;
29
+        height: 36px;
30
+        display: flex;
31
+      }
32
+
33
+      .search-input {
34
+        margin-left: 6px;
35
+        flex: 1;
36
+        font-size: 24px;
37
+        font-family: PingFang SC;
38
+        font-weight: bold;
39
+        color: rgba(0, 0, 0, 0.4);
40
+        line-height: 82px;
41
+      }
42
+    }
43
+    .console{
44
+      width: 100%;
45
+      display: flex;
46
+      justify-content: center;
47
+      align-items: center;
48
+      .console-button{
49
+        margin-top: 80px;
50
+        width: 70%;
51
+        height: 60px;
52
+        border-radius: 60px;
53
+        line-height: 60px;
54
+        background: firebrick;
55
+        color: #EAEAEF;
56
+        font-size: 28px;
57
+        text-align: center;
58
+      }
59
+    }
60
+    .user-info{
61
+      margin-top: 40px;
62
+      padding: 40px;
63
+      background: #e3e3e979;
64
+      font-size: 28px;
65
+      .identity{
66
+        
67
+      }
68
+      .status{
69
+        margin-top: 10px;
70
+      }
71
+    }
72
+  }
73
+}

+ 5 - 0
src/pages/mine/subpages/vipDetail/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '开通会员',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 276 - 0
src/pages/mine/subpages/vipDetail/index.jsx

@@ -0,0 +1,276 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    userInfo: {},
11
+    purchaseList: [],
12
+    selectVip: 0,//当前选择的充值
13
+    isIos: false,
14
+    isOpen: false,//是否打开口令填写弹窗
15
+    code: '',//口令
16
+    pay_status: 1,//是否绑定口令
17
+  }
18
+
19
+  componentWillMount () {
20
+    let that = this
21
+    this.getShopList()
22
+    this.getUserInfo()
23
+    Taro.getSystemInfo({
24
+      success: function (res) {
25
+        if (res.platform != "android") {
26
+          that.setState({
27
+            isIos: true
28
+          })
29
+        }
30
+      }
31
+    })
32
+  }
33
+  componentDidShow () {
34
+  }
35
+  getUserInfo () {
36
+    api.getUserInfo().then(res => {
37
+      Taro.hideLoading()
38
+      if (res.code == 200) {
39
+        this.setState({
40
+          userInfo: res.data
41
+        })
42
+      }
43
+    })
44
+  }
45
+  getShopList () {
46
+    api.getShopList().then(res => {
47
+      if (res.code == 200) {
48
+        this.setState({
49
+          purchaseList: res.data.vip_list,
50
+          pay_status: res.data.pay_status
51
+        })
52
+      }
53
+    })
54
+  }
55
+  //充值
56
+  toOrder () {
57
+    if (this.state.purchaseList[this.state.selectVip].ios_status == 0) {
58
+      if (this.state.isIos) {
59
+        Taro.showToast({
60
+          title: 'ios暂不支持充值',
61
+          icon: 'none',
62
+          duration: 2000
63
+        })
64
+        return
65
+      }
66
+    }
67
+    let that = this
68
+    let item = this.state.purchaseList[this.state.selectVip]
69
+    if (process.env.TARO_ENV === 'tt') {
70
+      Taro.showLoading({
71
+        title: '加载中...',
72
+      })
73
+      api.ttCreateOrder({ pay_info_id: item.id, book_id: -1 }).then(res => {
74
+        if (res.code == 200) {
75
+          Taro.hideLoading()
76
+          tt.pay({
77
+            orderInfo: {
78
+              order_id: res.data.order_id,
79
+              order_token: res.data.order_token
80
+            },
81
+            service: 5,
82
+            success (item) {
83
+              if (item.code == 0) {
84
+                that.getUserInfo()
85
+                setTimeout(() => {
86
+                  that.getUserInfo()
87
+                }, 2000);
88
+                // 支付成功处理逻辑,只有res.code=0时,才表示支付成功
89
+                // 但是最终状态要以商户后端结果为准
90
+              }
91
+            },
92
+            fail (item) {
93
+              // 调起收银台失败处理逻辑
94
+            },
95
+          });
96
+        }
97
+      })
98
+    } else if (process.env.TARO_ENV === 'weapp') {
99
+      Taro.getSystemInfo({
100
+        success: function (res) {
101
+          Taro.showLoading({
102
+            title: '加载中...',
103
+          })
104
+          api.createOrder({ shop_id: item.id }).then(res => {
105
+            if (res.code == 200) {
106
+              Taro.hideLoading()
107
+              Taro.requestPayment({
108
+                timeStamp: res.data.timeStamp,
109
+                nonceStr: res.data.nonceStr,
110
+                package: res.data.package,
111
+                signType: res.data.signType,
112
+                paySign: res.data.paySign,
113
+                success: function (res) {
114
+                  // console.log(res);
115
+                  //添加2s延迟在刷新数据
116
+                  setTimeout(() => {
117
+                    that.getUserInfo()
118
+                  }, 2000);
119
+                },
120
+                fail: function (err) {
121
+                  try {
122
+                    const res = Taro.getSystemInfoSync()
123
+                    console.log(res, 'iphone=====');
124
+                  } catch (e) {
125
+                  }
126
+                }
127
+              })
128
+            }
129
+          })
130
+        }
131
+      })
132
+    }
133
+
134
+  }
135
+  selectVip (item, index) {
136
+    this.setState({
137
+      selectVip: index
138
+    })
139
+  }
140
+  openCode () {
141
+    this.setState({
142
+      isOpen: true
143
+    })
144
+  }
145
+  searchCode (e) {
146
+    this.setState(
147
+      {
148
+        code: e.detail.value
149
+      }
150
+    )
151
+  }
152
+  cancel () {
153
+    this.setState({
154
+      isOpen: false
155
+    })
156
+  }
157
+  determine () {
158
+    api.getCheckoutBookCode({ input_str: this.state.code }).then(res => {
159
+      if (res.code == 200) {
160
+        this.getShopList()
161
+        this.getUserInfo()
162
+        this.setState({
163
+          isOpen: false
164
+        })
165
+        Taro.showToast({
166
+          title: `已获取折扣`,
167
+          icon: 'none',
168
+          duration: 2000
169
+        })
170
+      } else {
171
+        Taro.showToast({
172
+          title: res.msg,
173
+          icon: 'none',
174
+          duration: 2000
175
+        })
176
+      }
177
+    })
178
+  }
179
+
180
+  render () {
181
+    return (
182
+      <View className='mine'>
183
+        <View className='mine-content'>
184
+          {
185
+            this.state.userInfo.is_vip == 1 &&
186
+            <View className='user-vip-banner-have'>
187
+              <View className='vip-banner-have-left'>
188
+                <Image className='vip-banner-img' src='https://video-img.fyshark.com/1683860337987f-vip3.png'></Image>
189
+                <View className='vip-banner-tips'>已开通会员,有效期至:{tool.formattedDate(this.state.userInfo.time_is_vip)}</View>
190
+              </View>
191
+            </View>
192
+          }
193
+          <View className='vip-tips'>
194
+            <View className='tips-title'>会员权益</View>
195
+            <View className='tips-content'>
196
+              <View className='tips-left'>
197
+                <View className='tips-img'>
198
+                  <Image className='tips-icon' src='https://video-img.fyshark.com/1683709602205akkhs.png'></Image>
199
+                </View>
200
+                <View className='tips-text'>
201
+                  <View className='text-title'>免费阅读</View>
202
+                  <View className='text-introduce'>付费章节享受免费阅读</View>
203
+                </View>
204
+              </View>
205
+              <View className='tips-right'>
206
+                <View className='tips-img'>
207
+                  <Image className='tips-icon' src='https://video-img.fyshark.com/1683709622282slljl.png'></Image>
208
+                </View>
209
+                <View className='tips-text'>
210
+                  <View className='text-title'>新书优先阅读</View>
211
+                  <View className='text-introduce'>新书会员可优先阅读</View>
212
+                </View>
213
+              </View>
214
+            </View>
215
+          </View>
216
+          <View className='purchase'>
217
+            <View className='purchase-title'>购买会员</View>
218
+            {
219
+              this.state.pay_status == 0 &&
220
+              <View className='active' onClick={e => (this.openCode())}>
221
+                <Image className='active-img' src='https://video-img.fyshark.com/1685349084726icon.png' mode='widthFix'></Image>
222
+              </View>
223
+            }
224
+            <View className='purchase-list'>
225
+              {
226
+                this.state.purchaseList.map((item, index) => (
227
+                  <View className={this.state.selectVip == index ? 'purchase-info-active' : 'purchase-info'}
228
+                    key={index}
229
+                    onClick={e => (this.selectVip(item, index))}
230
+                  >
231
+                    {
232
+                      item.tag && item.tag.length > 0 &&
233
+                      < View className='purchase-tips'>{item.tag}</View>
234
+                    }
235
+                    <View className='purchase-title'>{item.title}</View>
236
+                    <View className='purchase-money'><View className='purchase-money-tip'>¥</View>{item.money / 100}</View>
237
+                    <View className='purchase-oldMoney'>¥{item.coin / 100}元/周</View>
238
+                    <View className='purchase-text'>
239
+                      <View className='purchase-button'>{item.sub_title}</View>
240
+                    </View>
241
+                  </View>
242
+                ))
243
+              }
244
+            </View>
245
+          </View>
246
+          <View className='payInfo' onClick={e => (this.toOrder())}>
247
+            立即支付
248
+          </View>
249
+          <View className='prompt'>
250
+            <View className='prompt-title'>温馨提示</View>
251
+            <View className='prompt-text'>1.未满18周岁的用户请谨慎购买;</View>
252
+            <View className='prompt-text'>2.VIP会员属于虚拟商品,一旦购买不做退换;</View>
253
+            <View className='prompt-text'>3.如有问题请在我的页面联系客服。</View>
254
+          </View>
255
+        </View>
256
+        {
257
+          this.state.isOpen &&
258
+          <View className='information'>
259
+            <View className='information-content'>
260
+              <View className='information-title'>
261
+                请输入口令码
262
+              </View>
263
+              <View className='search'>
264
+                <Input className='search-input' onInput={e => (this.searchCode(e))} type='text' placeholder='请输入口令码' focus />
265
+              </View>
266
+              <View className='information-console'>
267
+                <View className='cancel' onClick={e => (this.cancel())}>取消</View>
268
+                <View className='determine' onClick={e => (this.determine())}>确定</View>
269
+              </View>
270
+            </View>
271
+          </View>
272
+        }
273
+      </View >
274
+    )
275
+  }
276
+}

+ 367 - 0
src/pages/mine/subpages/vipDetail/index.less

@@ -0,0 +1,367 @@
1
+.mine {
2
+  width: 100vw;
3
+  min-height: 100vh;
4
+
5
+  .mine-content {
6
+    padding: 70px 24px;
7
+    padding-bottom: 180px;
8
+
9
+    .vip-tips {
10
+      .tips-title {
11
+        font-size: 28rpx;
12
+        font-family: PingFang SC;
13
+        font-weight: bold;
14
+        color: #353535;
15
+
16
+      }
17
+
18
+      .tips-content {
19
+        margin-top: 60px;
20
+        padding-bottom: 52px;
21
+        border-bottom: 1px solid #B2B2B2;
22
+        display: flex;
23
+
24
+        .tips-left {
25
+          flex: 1;
26
+          display: flex;
27
+          align-items: center;
28
+
29
+          .tips-img {
30
+            width: 78px;
31
+            height: 78px;
32
+            background: linear-gradient(90deg, #F8E6C0, #DCA542);
33
+            border-radius: 50%;
34
+            display: flex;
35
+            justify-content: center;
36
+            align-items: center;
37
+
38
+            .tips-icon {
39
+              width: 50px;
40
+              height: 50px;
41
+            }
42
+          }
43
+
44
+          .tips-text {
45
+            margin-left: 12px;
46
+
47
+            .text-title {
48
+              font-size: 28rpx;
49
+              font-family: PingFang SC;
50
+              font-weight: bold;
51
+              color: #353535;
52
+            }
53
+
54
+            .text-introduce {
55
+              font-size: 22px;
56
+              font-family: PingFang SC;
57
+              font-weight: bold;
58
+              color: #888888;
59
+            }
60
+          }
61
+        }
62
+
63
+        .tips-right {
64
+          flex: 1;
65
+          display: flex;
66
+          align-items: center;
67
+
68
+          .tips-img {
69
+            width: 78px;
70
+            height: 78px;
71
+            background: linear-gradient(90deg, #F8E6C0, #DCA542);
72
+            border-radius: 50%;
73
+            display: flex;
74
+            justify-content: center;
75
+            align-items: center;
76
+
77
+            .tips-icon {
78
+              width: 50px;
79
+              height: 50px;
80
+            }
81
+          }
82
+
83
+          .tips-text {
84
+            margin-left: 12px;
85
+
86
+            .text-title {
87
+              font-size: 28rpx;
88
+              font-family: PingFang SC;
89
+              font-weight: bold;
90
+              color: #353535;
91
+            }
92
+
93
+            .text-introduce {
94
+              font-size: 22px;
95
+              font-family: PingFang SC;
96
+              font-weight: bold;
97
+              color: #888888;
98
+            }
99
+          }
100
+        }
101
+      }
102
+    }
103
+
104
+    .purchase {
105
+      margin-top: 48px;
106
+
107
+      .purchase-title {
108
+        font-size: 28px;
109
+        font-family: PingFang SC;
110
+        font-weight: bold;
111
+        color: #353535;
112
+      }
113
+
114
+      .active {
115
+        margin-top: 20px;
116
+        .active-img {
117
+          width: 100%;
118
+        }
119
+      }
120
+
121
+      .purchase-list {
122
+        margin-top: 38px;
123
+        display: flex;
124
+        justify-content: space-between;
125
+
126
+        .purchase-info {
127
+          position: relative;
128
+          width: 212px;
129
+          padding: 54px 0 26px 0;
130
+          background: #FFFFFF;
131
+          border: 2px solid rgba(136, 136, 136, 0.5);
132
+          border-radius: 20rpx;
133
+          text-align: center;
134
+          color: #353535;
135
+
136
+        }
137
+
138
+        .purchase-info-active {
139
+          position: relative;
140
+          width: 212px;
141
+          padding: 54px 0 26px 0;
142
+          background: #FCF5E9;
143
+          border: 2px solid #E7C06F;
144
+          border-radius: 20rpx;
145
+          text-align: center;
146
+          color: #AC5A27;
147
+
148
+        }
149
+
150
+        .purchase-tips {
151
+          position: absolute;
152
+          left: 0;
153
+          top: -22px;
154
+          background: linear-gradient(90deg, #F1A53E, #ED7535);
155
+          border-radius: 20rpx 0rpx 20rpx 0rpx;
156
+          padding: 8px 22px 6px 20px;
157
+          font-size: 24px;
158
+          font-family: PingFang SC;
159
+          font-weight: bold;
160
+          color: #FFFFFF;
161
+        }
162
+
163
+        .purchase-title {
164
+          font-size: 24px;
165
+          font-family: PingFang SC;
166
+          font-weight: 800;
167
+
168
+        }
169
+
170
+        .purchase-money {
171
+          margin-top: 20px;
172
+          font-size: 40px;
173
+          font-family: PingFang SC;
174
+          font-weight: 800;
175
+        }
176
+
177
+        .purchase-money-tip {
178
+          display: inline-block;
179
+          font-size: 20rpx;
180
+          font-family: PingFang SC;
181
+          font-weight: 800;
182
+
183
+        }
184
+
185
+        .purchase-oldMoney {
186
+          margin-top: 10px;
187
+          font-size: 24px;
188
+          font-family: PingFang SC;
189
+          font-weight: bold;
190
+          text-decoration: line-through
191
+        }
192
+
193
+        .purchase-text {
194
+          margin-top: 24px;
195
+          display: flex;
196
+          justify-content: center;
197
+
198
+          .purchase-button {
199
+            width: 144px;
200
+            height: 38px;
201
+            line-height: 38px;
202
+            background: #F2D9A9;
203
+            border-radius: 19px;
204
+            font-size: 24px;
205
+            font-family: PingFang SC;
206
+            font-weight: 800;
207
+            color: #AC5A27;
208
+          }
209
+        }
210
+      }
211
+    }
212
+
213
+    .payInfo {
214
+      margin-top: 50px;
215
+      height: 76px;
216
+      line-height: 76px;
217
+      background: linear-gradient(180deg, #FBC578, #FC9D18);
218
+      border-radius: 38px;
219
+      font-size: 30px;
220
+      font-family: PingFang SC;
221
+      text-align: center;
222
+      font-weight: 800;
223
+      color: #FFFFFF;
224
+    }
225
+
226
+    .prompt {
227
+      margin-top: 50px;
228
+
229
+      .prompt-title {
230
+        font-size: 28px;
231
+        font-family: PingFang SC;
232
+        font-weight: bold;
233
+        color: #353535;
234
+      }
235
+
236
+      .prompt-text {
237
+        font-size: 22px;
238
+        font-family: PingFang SC;
239
+        font-weight: bold;
240
+        color: #888888;
241
+        line-height: 52px;
242
+      }
243
+    }
244
+
245
+    .user-vip-banner-have {
246
+      padding: 30px;
247
+      border-radius: 20px;
248
+      background: linear-gradient(90deg, #d48510, #2d291f, #E7AF51);
249
+      display: flex;
250
+      justify-content: space-between;
251
+      align-items: center;
252
+      margin-bottom: 24px;
253
+
254
+      .vip-banner-have-left {
255
+        .vip-banner-img {
256
+          width: 60px;
257
+          height: 46px;
258
+          display: block;
259
+        }
260
+
261
+        .vip-banner-tips {
262
+          margin-top: 11px;
263
+          font-size: 26px;
264
+          font-family: PingFang SC;
265
+          font-weight: 800;
266
+          color: #fbf8f8;
267
+        }
268
+      }
269
+
270
+      .vip-banner-have-right {
271
+        padding: 15px 40px;
272
+        font-size: 28px;
273
+        border-radius: 30px;
274
+        font-family: PingFang SC;
275
+        font-weight: 800;
276
+        background: #fbf8f8;
277
+        color: #353535;
278
+      }
279
+
280
+    }
281
+  }
282
+  .information {
283
+    position: fixed;
284
+    background: rgba(0, 0, 0, 0.4);
285
+    width: 100vw;
286
+    height: 100vh;
287
+    left: 0;
288
+    top: 0;
289
+    display: flex;
290
+    justify-content: center;
291
+    align-items: center;
292
+
293
+    .information-content {
294
+      border-radius: 20px;
295
+      width: 80%;
296
+      padding: 40px;
297
+      background: #fff;
298
+      .information-title{
299
+        text-align: center;
300
+        font-size: 36px;
301
+      }
302
+      .information-tips{
303
+        margin-top: 20px;
304
+        font-size: 24px;
305
+      }
306
+
307
+      .search-title {
308
+        margin-top: 40px;
309
+        font-size: 32px;
310
+      }
311
+
312
+      .search {
313
+        margin-top: 20px;
314
+        // width: 100%;
315
+        display: flex;
316
+        padding: 0 20px;
317
+        height: 70px;
318
+        align-items: center;
319
+        background: #EAEAEF;
320
+        border-radius: 10px;
321
+  
322
+        .search-img {
323
+          width: 36px;
324
+          height: 36px;
325
+          display: flex;
326
+        }
327
+  
328
+        .search-input {
329
+          margin-left: 6px;
330
+          flex: 1;
331
+          font-size: 24px;
332
+          font-family: PingFang SC;
333
+          font-weight: bold;
334
+          color: rgba(0, 0, 0, 0.4);
335
+          line-height: 82px;
336
+        }
337
+      }
338
+      .information-console{
339
+        margin-top: 40px;
340
+        display: flex;
341
+        justify-content: space-between;
342
+        align-items: center;
343
+        font-size: 28px;
344
+        text-align: center;
345
+        .cancel{
346
+          width: 240px;
347
+          height: 64px;
348
+          border-radius: 10px;
349
+          line-height: 64px;
350
+          border: 1px solid #777373;
351
+
352
+        }
353
+        .determine{
354
+          width: 240px;
355
+          height: 64px;
356
+          border-radius: 10px;
357
+          color: #fff;
358
+          line-height: 64px;
359
+          background:linear-gradient(90deg, #E04A4A, #EC705D);
360
+        }
361
+      }
362
+
363
+    }
364
+
365
+
366
+  }
367
+}

+ 5 - 0
src/pages/mine/subpages/withdrawal/index.config.js

@@ -0,0 +1,5 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '提现',
3
+  enableShareAppMessage: true,
4
+  enableShareTimeline: true ,
5
+})

+ 206 - 0
src/pages/mine/subpages/withdrawal/index.jsx

@@ -0,0 +1,206 @@
1
+import { Component } from 'react'
2
+import { View, Text, Button, Image, scrollView, Input } from '@tarojs/components'
3
+import * as api from '../../../../service/index'
4
+import tool from '../../../../common/tool'
5
+import Taro, { } from '@tarojs/taro'
6
+import './index.less'
7
+
8
+export default class collection extends Component {
9
+  state = {
10
+    title: '',
11
+    phone: '',
12
+    account: '',
13
+    vip_list: [],
14
+    vipIndex: 0,//当前选择的提现金额
15
+    now_sum: 0,//总金额
16
+    extract_status: 0,//是否已填写收款信息
17
+    isOpen: false,//是否打开收款信息填写弹窗
18
+    extract_text: '',
19
+  }
20
+
21
+  componentWillMount () {
22
+    this.getExtractList()
23
+  }
24
+  componentDidShow () {
25
+  }
26
+  getExtractList () {
27
+    api.getExtractList().then(res => {
28
+      if (res.code == 200) {
29
+        this.setState({
30
+          now_sum: res.data.now_sum,
31
+          vip_list: res.data.vip_list,
32
+          extract_status: res.data.extract_status,
33
+          title: res.data.alipay_name,
34
+          phone: res.data.phone_num,
35
+          account: res.data.alipay_account,
36
+          extract_text: res.data.extract_text
37
+        })
38
+      }
39
+    })
40
+  }
41
+  searchTitle (e) {
42
+    console.log(e);
43
+    this.setState(
44
+      {
45
+        title: e.detail.value
46
+      }
47
+    )
48
+  }
49
+  searchAccount (e) {
50
+    console.log(e);
51
+    this.setState(
52
+      {
53
+        account: e.detail.value
54
+      }
55
+    )
56
+  }
57
+  searchPhone (e) {
58
+    console.log(e);
59
+    this.setState(
60
+      {
61
+        phone: e.detail.value
62
+      }
63
+    )
64
+  }
65
+  cancel () {
66
+    this.setState({
67
+      isOpen: false
68
+    })
69
+  }
70
+  determine () {
71
+    let that = this
72
+    switch (0) {
73
+      case this.state.title.length:
74
+        Taro.showToast({
75
+          title: `请输入支付宝昵称`,
76
+          icon: 'none',
77
+        });
78
+        return
79
+      case this.state.account.length:
80
+        Taro.showToast({
81
+          title: `请输入支付宝账号`,
82
+          icon: 'none',
83
+        });
84
+        return
85
+
86
+      case this.state.phone.length:
87
+        Taro.showToast({
88
+          title: `请输入自己的手机号`,
89
+          icon: 'none',
90
+        });
91
+        return
92
+    }
93
+    let text = `确定提现${this.state.vip_list[this.state.vipIndex].title}到支付宝吗?`
94
+    if (this.state.vip_list[this.state.vipIndex].money == -1) {
95
+      text = `确定将全部金额提现到支付宝吗?`
96
+    }
97
+    Taro.showModal({
98
+      title: '提示',
99
+      content: text,
100
+      success: function (res) {
101
+        if (res.confirm) {
102
+          let params = {
103
+            alipay_name: that.state.title,
104
+            alipay_account: that.state.account,
105
+            phone_num: that.state.phone,
106
+            money: that.state.vip_list[that.state.vipIndex].money == -1 ? that.state.now_sum : that.state.vip_list[that.state.vipIndex].money
107
+          }
108
+          api.alipayExtract(params).then(i => {
109
+            if (i.code == 200) {
110
+              Taro.navigateBack()
111
+            }
112
+          })
113
+        } else if (res.cancel) {
114
+          console.log('用户点击取消')
115
+        }
116
+      }
117
+    })
118
+  }
119
+  openUserInfo (item, index) {
120
+    console.log(item);
121
+    if (this.state.now_sum / 100 < 50) {
122
+      Taro.showToast({
123
+        title: `总金额小于50`,
124
+        icon: 'none',
125
+      });
126
+      return
127
+    }
128
+    if (this.state.now_sum < item.money) {
129
+      Taro.showToast({
130
+        title: `总金额小于${item.money / 100}`,
131
+        icon: 'none',
132
+      });
133
+      return
134
+    }
135
+
136
+    this.setState({
137
+      isOpen: true,
138
+      vipIndex: index,
139
+    })
140
+  }
141
+
142
+  render () {
143
+    return (
144
+      <View className='mine'>
145
+        <View className='withdrawal-top'>
146
+          <View className='withdrawal-num'>{this.state.now_sum / 100}</View>
147
+          <View className='withdrawal-tips'>总金额(元)</View>
148
+        </View>
149
+        <View className='withdrawal-info'>
150
+          {
151
+            this.state.extract_status == 3 &&
152
+            <View className='withdrawal-results'>
153
+              <View className='results-title'>提现失败</View>
154
+              <View className='results-tips'>原因:{this.state.extract_text}</View>
155
+            </View>
156
+          }
157
+          <View className='withdrawal-title'>支付宝提现</View>
158
+          <View className='withdrawal-content'>
159
+            {
160
+              this.state.vip_list.map((item, index) => (
161
+                <View className='withdrawal-info-list' key={index} onClick={e => (this.openUserInfo(item, index))}>
162
+                  {item.money == -1 ? '全部' : `${item.money / 100}`}
163
+                  {
164
+                    item.money != -1 &&
165
+                    <View className='withdrawal-info-list-unit'>元</View>
166
+                  }
167
+                </View>
168
+              ))
169
+            }
170
+          </View>
171
+        </View>
172
+        <View className='tip'>
173
+          <View className='tips-text'>1、个人提现到账时间预计在三个工作日之内。</View>
174
+          <View className='tips-text'>2、大金额提现会有工作人员电话确认,请保持通讯正常。</View>
175
+        </View>
176
+        {
177
+          this.state.isOpen &&
178
+          <View className='information'>
179
+            <View className='information-content'>
180
+              <View className='information-title'>
181
+                请填写收款信息
182
+              </View>
183
+              <View className='information-tips'>请仔细确认以下信息,如有错误导致打款失败平台概不负责!</View>
184
+              <View className='search-title'>支付宝昵称:</View>
185
+              <View className='search'>
186
+                <Input className='search-input' onInput={e => (this.searchTitle(e))} type='text' placeholder={this.state.title.length > 0 ? this.state.title : '请输入支付宝昵称'} focus />
187
+              </View>
188
+              <View className='search-title'>支付宝账号:</View>
189
+              <View className='search'>
190
+                <Input className='search-input' onInput={e => (this.searchAccount(e))} type='text' placeholder={this.state.account.length > 0 ? this.state.account : '请输入支付宝账号'} focus />
191
+              </View>
192
+              <View className='search-title'>手机号:</View>
193
+              <View className='search'>
194
+                <Input className='search-input' onInput={e => (this.searchPhone(e))} type='text' placeholder={this.state.phone.length > 0 ? this.state.phone : '请输入自己的手机号'} focus />
195
+              </View>
196
+              <View className='information-console'>
197
+                <View className='cancel' onClick={e => (this.cancel())}>取消</View>
198
+                <View className='determine' onClick={e => (this.determine())}>确定</View>
199
+              </View>
200
+            </View>
201
+          </View>
202
+        }
203
+      </View >
204
+    )
205
+  }
206
+}

+ 169 - 0
src/pages/mine/subpages/withdrawal/index.less

@@ -0,0 +1,169 @@
1
+.mine {
2
+  min-height: 100vh;
3
+
4
+  .withdrawal-top {
5
+    padding: 60px 0 100px 0;
6
+    background: #ea702d;
7
+
8
+    .withdrawal-num {
9
+      font-size: 46px;
10
+      color: #f2f3d0;
11
+      text-align: center;
12
+    }
13
+
14
+    .withdrawal-tips {
15
+      margin-top: 10px;
16
+      font-size: 28px;
17
+      color: #f2f3d0;
18
+      text-align: center;
19
+    }
20
+  }
21
+
22
+  .withdrawal-info {
23
+    width: 100%;
24
+    margin-top: -20px;
25
+    border-top-left-radius: 20px;
26
+    border-top-right-radius: 20px;
27
+    background: #fff;
28
+    padding: 40px 0;
29
+    .withdrawal-results{
30
+      padding: 0 40px;
31
+      margin-bottom: 20px;
32
+      text-align: center;
33
+      color: red;
34
+      font-size: 24px;
35
+      .results-tips{
36
+        margin-top: 10px
37
+      }
38
+    }
39
+
40
+    .withdrawal-title {
41
+      padding-left: 40px;
42
+      font-size: 32px;
43
+    }
44
+    .withdrawal-results{
45
+
46
+    }
47
+
48
+    .withdrawal-content {
49
+      margin-top: 40px;
50
+
51
+      .withdrawal-info-list {
52
+        border-radius: 10px;
53
+        margin-top: 20px;
54
+        margin-left: 40px;
55
+        display: inline-block;
56
+        width: 198px;
57
+        height: 120px;
58
+        line-height: 120px;
59
+        font-size: 36px;
60
+        font-family: PingFang SC;
61
+        font-weight: 600;
62
+        text-align: center;
63
+        border: 1px solid rgba(68, 68, 68, 0.2);
64
+
65
+        .withdrawal-info-list-unit {
66
+          margin-left: 6px;
67
+          font-size: 24px;
68
+          display: inline-block;
69
+          font-weight: 400;
70
+        }
71
+      }
72
+    }
73
+  }
74
+  .tip{
75
+    margin-top: 60px;
76
+    padding: 0 40px;
77
+    
78
+    .tips-text{
79
+      margin-top: 10px;
80
+      font-size: 24px;
81
+    }
82
+  }
83
+
84
+  .information {
85
+    position: fixed;
86
+    background: rgba(0, 0, 0, 0.4);
87
+    width: 100vw;
88
+    height: 100vh;
89
+    left: 0;
90
+    top: 0;
91
+    display: flex;
92
+    justify-content: center;
93
+    align-items: center;
94
+
95
+    .information-content {
96
+      border-radius: 20px;
97
+      width: 80%;
98
+      padding: 40px;
99
+      background: #fff;
100
+      .information-title{
101
+        text-align: center;
102
+        font-size: 36px;
103
+      }
104
+      .information-tips{
105
+        margin-top: 20px;
106
+        font-size: 24px;
107
+      }
108
+
109
+      .search-title {
110
+        margin-top: 40px;
111
+        font-size: 32px;
112
+      }
113
+
114
+      .search {
115
+        margin-top: 20px;
116
+        // width: 100%;
117
+        display: flex;
118
+        padding: 0 20px;
119
+        height: 70px;
120
+        align-items: center;
121
+        background: #EAEAEF;
122
+        border-radius: 10px;
123
+  
124
+        .search-img {
125
+          width: 36px;
126
+          height: 36px;
127
+          display: flex;
128
+        }
129
+  
130
+        .search-input {
131
+          margin-left: 6px;
132
+          flex: 1;
133
+          font-size: 24px;
134
+          font-family: PingFang SC;
135
+          font-weight: bold;
136
+          color: rgba(0, 0, 0, 0.4);
137
+          line-height: 82px;
138
+        }
139
+      }
140
+      .information-console{
141
+        margin-top: 40px;
142
+        display: flex;
143
+        justify-content: space-between;
144
+        align-items: center;
145
+        font-size: 28px;
146
+        text-align: center;
147
+        .cancel{
148
+          width: 240px;
149
+          height: 64px;
150
+          border-radius: 10px;
151
+          line-height: 64px;
152
+          border: 1px solid #777373;
153
+
154
+        }
155
+        .determine{
156
+          width: 240px;
157
+          height: 64px;
158
+          border-radius: 10px;
159
+          color: #fff;
160
+          line-height: 64px;
161
+          background:linear-gradient(90deg, #E04A4A, #EC705D);
162
+        }
163
+      }
164
+
165
+    }
166
+
167
+
168
+  }
169
+}

+ 201 - 0
src/service/index.js

@@ -0,0 +1,201 @@
1
+//首页js接口
2
+import Request from '../api/request';
3
+
4
+//登录
5
+export const login = data =>
6
+  Request({
7
+    url: '/api/login',
8
+    method: 'POST',
9
+    data,
10
+  });
11
+//首页tab
12
+export const getBookCatIndex = data =>
13
+  Request({
14
+    url: '/api/get_book_cat_index',
15
+    method: 'POST',
16
+    data,
17
+  });
18
+//分类书籍
19
+export const getBookCatInfo = data =>
20
+  Request({
21
+    url: '/api/get_book_cat_info_v2',
22
+    method: 'POST',
23
+    data,
24
+  });
25
+//书籍简介
26
+export const getBookinfo = data =>
27
+  Request({
28
+    url: '/api/get_book_info',
29
+    method: 'POST',
30
+    data,
31
+  });
32
+//加入书架
33
+export const bookCollect = data =>
34
+  Request({
35
+    url: '/api/book_collect',
36
+    method: 'POST',
37
+    data,
38
+  });
39
+//获取目录
40
+export const getBookCatalogue = data =>
41
+  Request({
42
+    url: '/api/get_book_catalogue',
43
+    method: 'POST',
44
+    data,
45
+  });
46
+//获取文本
47
+export const getBookContent = data =>
48
+  Request({
49
+    url: '/api/get_book_content',
50
+    method: 'POST',
51
+    data,
52
+  });
53
+//获取书架
54
+export const myBookFollow = data =>
55
+  Request({
56
+    url: '/api/my_book_follow',
57
+    method: 'POST',
58
+    data,
59
+  });
60
+//获取用户信息
61
+export const getUserInfo = data =>
62
+  Request({
63
+    url: '/api/get_user_info',
64
+    method: 'POST',
65
+    data,
66
+  });
67
+//上报阅读记录
68
+export const bookReadReort = data =>
69
+  Request({
70
+    url: '/api/book_read_reort',
71
+    method: 'POST',
72
+    data,
73
+  });
74
+//获取订单列表
75
+export const getShopList = data =>
76
+  Request({
77
+    url: '/api/get_shop_list',
78
+    method: 'POST',
79
+    data,
80
+  });
81
+//获取阅读记录
82
+export const myBookRead = data =>
83
+  Request({
84
+    url: '/api/my_book_read',
85
+    method: 'POST',
86
+    data,
87
+  });
88
+//删除阅读记录
89
+export const deleteBookRead = data =>
90
+  Request({
91
+    url: '/api/delete_book_read',
92
+    method: 'POST',
93
+    data,
94
+  });
95
+//删除书架记录
96
+export const deleteBookCollect = data =>
97
+  Request({
98
+    url: '/api/delete_book_collect',
99
+    method: 'POST',
100
+    data,
101
+  });
102
+//搜索
103
+export const getBookSearch = data =>
104
+  Request({
105
+    url: '/api/get_book_search',
106
+    method: 'POST',
107
+    data,
108
+  });
109
+//创建微信订单
110
+export const createOrder = data =>
111
+  Request({
112
+    url: '/api/wx_create_order',
113
+    method: 'POST',
114
+    data,
115
+  });
116
+//创建抖音订单
117
+export const ttCreateOrder = data =>
118
+  Request({
119
+    url: '/api/pay/tt_create_order',
120
+    method: 'POST',
121
+    data,
122
+  });
123
+//喜欢列表
124
+export const getBookLike = data =>
125
+  Request({
126
+    url: '/api/get_book_like',
127
+    method: 'POST',
128
+    data,
129
+  });
130
+//获取口令
131
+export const getCommand = data =>
132
+  Request({
133
+    url: '/api/get_command',
134
+    method: 'POST',
135
+    data,
136
+  });
137
+//获取我的收益
138
+export const getEarningsInfo = data =>
139
+  Request({
140
+    url: '/api/get_earnings_info',
141
+    method: 'POST',
142
+    data,
143
+  });
144
+//获取提现列标
145
+export const getExtractList = data =>
146
+  Request({
147
+    url: '/api/get_extract_list',
148
+    method: 'POST',
149
+    data,
150
+  });
151
+//提现
152
+export const alipayExtract = data =>
153
+  Request({
154
+    url: '/api/alipay_extract',
155
+    method: 'POST',
156
+    data,
157
+  });
158
+//赚钱说明
159
+export const getInformationDetails = data =>
160
+  Request({
161
+    url: '/api/get_information_details',
162
+    method: 'POST',
163
+    data,
164
+  });
165
+//是否打开抖音广告
166
+export const getAppConfig = data =>
167
+  Request({
168
+    url: '/api/get_app_config',
169
+    method: 'POST',
170
+    data,
171
+  });
172
+//上报
173
+export const reportLogin = data =>
174
+  Request({
175
+    url: '/api/report_login',
176
+    method: 'POST',
177
+    data,
178
+  });
179
+//获取我的团队信息
180
+export const getTeamInfo = data =>
181
+  Request({
182
+    url: '/api/get_team_info',
183
+    method: 'POST',
184
+    data,
185
+  });
186
+
187
+//输入邀请码
188
+export const teamEnqueue = data =>
189
+  Request({
190
+    url: '/api/team_enqueue',
191
+    method: 'POST',
192
+    data,
193
+  });
194
+//绑定口令
195
+export const getCheckoutBookCode = data =>
196
+  Request({
197
+    url: '/api/get_checkout_book_code',
198
+    method: 'POST',
199
+    data,
200
+  });
201
+

+ 19 - 0
types/global.d.ts

@@ -0,0 +1,19 @@
1
+/// <reference types="@tarojs/taro" />
2
+
3
+declare module '*.png';
4
+declare module '*.gif';
5
+declare module '*.jpg';
6
+declare module '*.jpeg';
7
+declare module '*.svg';
8
+declare module '*.css';
9
+declare module '*.less';
10
+declare module '*.scss';
11
+declare module '*.sass';
12
+declare module '*.styl';
13
+
14
+declare namespace NodeJS {
15
+  interface ProcessEnv {
16
+    TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd'
17
+  }
18
+}
19
+