chenlei 5 часов назад
Родитель
Сommit
c60da93ccd
88 измененных файлов с 17291 добавлено и 17317 удалено
  1. 12 12
      .editorconfig
  2. 8 8
      .eslintrc.js
  3. 6 6
      .gitignore
  4. 5 5
      .vscode/settings.json
  5. 6 6
      README.md
  6. 13 13
      babel.config.js
  7. 8 8
      config/dev.js
  8. 68 68
      config/index.js
  9. 37 37
      config/prod.js
  10. 24 24
      global.d.ts
  11. 76 76
      package.json
  12. 13 13
      project.config.json
  13. 13 13
      project.tt.json
  14. 126 126
      src/api/index.ts
  15. 29 29
      src/app.config.ts
  16. 65 65
      src/app.scss
  17. 121 121
      src/app.tsx
  18. 23 23
      src/components/FooterProtocol/index.scss
  19. 50 50
      src/components/FooterProtocol/index.tsx
  20. 32 32
      src/components/LoadingPage/index.scss
  21. 54 54
      src/components/LoadingPage/index.tsx
  22. 27 27
      src/components/Video/index.scss
  23. 36 36
      src/components/Video/index.tsx
  24. 20 20
      src/hooks/useInterval.ts
  25. 121 121
      src/hooks/usePreloader.ts
  26. 20 20
      src/index.html
  27. 5 5
      src/pages/banner/index.config.ts
  28. 120 120
      src/pages/banner/index.scss
  29. 270 270
      src/pages/banner/index.tsx
  30. 77 77
      src/pages/home/components/Menu/index.scss
  31. 187 187
      src/pages/home/components/Menu/index.tsx
  32. 56 56
      src/pages/home/components/RoundItem/index.scss
  33. 114 114
      src/pages/home/components/RoundItem/index.tsx
  34. 141 141
      src/pages/home/components/SearchLayout/index.scss
  35. 132 132
      src/pages/home/components/SearchLayout/index.tsx
  36. 121 121
      src/pages/home/components/SightDetailLayout/index.scss
  37. 111 111
      src/pages/home/components/SightDetailLayout/index.tsx
  38. 17 17
      src/pages/home/components/Swiper/index.scss
  39. 179 179
      src/pages/home/components/Swiper/index.tsx
  40. 90 90
      src/pages/home/components/VisitCard/index.scss
  41. 173 173
      src/pages/home/components/VisitCard/index.tsx
  42. 459 485
      src/pages/home/constants.ts
  43. 4 4
      src/pages/home/index.config.ts
  44. 254 254
      src/pages/home/index.scss
  45. 361 361
      src/pages/home/index.tsx
  46. 5 5
      src/pages/iframe/index.config.ts
  47. 12 12
      src/pages/iframe/index.tsx
  48. 3 3
      src/pages/login/index.config.ts
  49. 82 82
      src/pages/login/index.scss
  50. 191 191
      src/pages/login/index.tsx
  51. 3 3
      src/pages/tempp/index.config.ts
  52. 14 14
      src/pages/tempp/index.tsx
  53. 13 13
      src/store/base.ts
  54. 10 10
      src/store/index.ts
  55. 61 61
      src/subModule/components/CertLayout/index.scss
  56. 223 223
      src/subModule/components/CertLayout/index.tsx
  57. 3 3
      src/subModule/pages/document/index.config.ts
  58. 63 63
      src/subModule/pages/document/index.tsx
  59. 3 3
      src/subModule/pages/feedback/index.config.ts
  60. 159 159
      src/subModule/pages/feedback/index.scss
  61. 177 177
      src/subModule/pages/feedback/index.tsx
  62. 4 4
      src/subModule/pages/iframe/index.config.ts
  63. 21 21
      src/subModule/pages/iframe/index.tsx
  64. 3 3
      src/subModule/pages/museum/index.config.ts
  65. 106 106
      src/subModule/pages/museum/index.scss
  66. 6 6
      src/subModule/pages/museum/index.tsx
  67. 37 37
      src/subModule/pages/order/index.scss
  68. 3 3
      src/subModule/pages/portrait-iframe/index.config.ts
  69. 12 12
      src/subModule/pages/portrait-iframe/index.tsx
  70. 3 3
      src/subModule/pages/protocol/index.config.ts
  71. 31 31
      src/subModule/pages/protocol/index.tsx
  72. 3 3
      src/subModule/pages/shanjuan/index.config.ts
  73. 10 10
      src/subModule/pages/shanjuan/index.tsx
  74. 69 69
      src/subModule/pages/shopmall/components/Products/index.scss
  75. 136 136
      src/subModule/pages/shopmall/components/Products/index.tsx
  76. 70 70
      src/subModule/pages/shopmall/components/Ranking/index.scss
  77. 38 38
      src/subModule/pages/shopmall/components/Ranking/index.tsx
  78. 50 50
      src/subModule/pages/shopmall/components/Records/index.scss
  79. 88 88
      src/subModule/pages/shopmall/components/Records/index.tsx
  80. 3 3
      src/subModule/pages/shopmall/index.config.ts
  81. 105 105
      src/subModule/pages/shopmall/index.scss
  82. 129 129
      src/subModule/pages/shopmall/index.tsx
  83. 5 5
      src/taro-ui.scss
  84. 57 57
      src/utils/Header.ts
  85. 45 45
      src/utils/fetch.ts
  86. 110 110
      src/utils/index.ts
  87. 25 25
      tsconfig.json
  88. 11216 11216
      yarn.lock

+ 12 - 12
.editorconfig

@@ -1,12 +1,12 @@
-# http://editorconfig.org
-root = true
-
-[*]
-indent_style = space
-indent_size = 2
-charset = utf-8
-trim_trailing_whitespace = true
-insert_final_newline = true
-
-[*.md]
-trim_trailing_whitespace = false
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false

+ 8 - 8
.eslintrc.js

@@ -1,8 +1,8 @@
-module.exports = {
-  extends: ["taro/react"],
-  rules: {
-    "react/jsx-uses-react": "off",
-    "react/react-in-jsx-scope": "off",
-    "jsx-quotes": false,
-  },
-};
+module.exports = {
+  extends: ["taro/react"],
+  rules: {
+    "react/jsx-uses-react": "off",
+    "react/react-in-jsx-scope": "off",
+    "jsx-quotes": false,
+  },
+};

+ 6 - 6
.gitignore

@@ -1,6 +1,6 @@
-dist/
-deploy_versions/
-.temp/
-.rn_temp/
-node_modules/
-.DS_Store
+dist/
+deploy_versions/
+.temp/
+.rn_temp/
+node_modules/
+.DS_Store

+ 5 - 5
.vscode/settings.json

@@ -1,5 +1,5 @@
-{
-  "editor.defaultFormatter": "esbenp.prettier-vscode",
-  "typescript.tsdk": "node_modules\\typescript\\lib",
-  "editor.formatOnSave": true
-}
+{
+  "editor.defaultFormatter": "esbenp.prettier-vscode",
+  "typescript.tsdk": "node_modules\\typescript\\lib",
+  "editor.formatOnSave": true
+}

+ 6 - 6
README.md

@@ -1,6 +1,6 @@
-## 依赖安装
-
-```bash
-# @dage模块依赖于本地npm
-yarn --registry http://192.168.20.245:4873/
-```
+## 依赖安装
+
+```bash
+# @dage模块依赖于本地npm
+yarn --registry http://192.168.20.245:4873/
+```

+ 13 - 13
babel.config.js

@@ -1,13 +1,13 @@
-// babel-preset-taro 更多选项和默认值:
-// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
-module.exports = {
-  presets: [
-    [
-      "taro",
-      {
-        framework: "react",
-        ts: true,
-      },
-    ],
-  ],
-};
+// babel-preset-taro 更多选项和默认值:
+// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
+module.exports = {
+  presets: [
+    [
+      "taro",
+      {
+        framework: "react",
+        ts: true,
+      },
+    ],
+  ],
+};

+ 8 - 8
config/dev.js

@@ -1,8 +1,8 @@
-module.exports = {
-  env: {
-    NODE_ENV: '"development"',
-  },
-  defineConstants: {},
-  mini: {},
-  h5: {},
-};
+module.exports = {
+  env: {
+    NODE_ENV: '"development"',
+  },
+  defineConstants: {},
+  mini: {},
+  h5: {},
+};

+ 68 - 68
config/index.js

@@ -1,68 +1,68 @@
-const config = {
-  projectName: "wx-csbwg-minapp",
-  date: "2024-3-28",
-  designWidth: 750,
-  deviceRatio: {
-    640: 2.34 / 2,
-    750: 1,
-    828: 1.81 / 2,
-  },
-  sourceRoot: "src",
-  outputRoot: "dist",
-  plugins: [],
-  defineConstants: {
-    "process.env.TARO_APP_API": JSON.stringify(process.env.TARO_APP_API),
-  },
-  copy: {
-    patterns: [],
-    options: {},
-  },
-  framework: "react",
-  mini: {
-    postcss: {
-      pxtransform: {
-        enable: true,
-        config: {
-          mediaQuery: true,
-        },
-      },
-      url: {
-        enable: true,
-        config: {
-          limit: 1024, // 设定转换尺寸上限
-        },
-      },
-      cssModules: {
-        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
-        config: {
-          namingPattern: "module", // 转换模式,取值为 global/module
-          generateScopedName: "[name]__[local]___[hash:base64:5]",
-        },
-      },
-    },
-  },
-  h5: {
-    publicPath: "/",
-    staticDirectory: "static",
-    postcss: {
-      autoprefixer: {
-        enable: true,
-        config: {},
-      },
-      cssModules: {
-        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
-        config: {
-          namingPattern: "module", // 转换模式,取值为 global/module
-          generateScopedName: "[name]__[local]___[hash:base64:5]",
-        },
-      },
-    },
-  },
-};
-
-module.exports = function (merge) {
-  if (process.env.NODE_ENV === "development") {
-    return merge({}, config, require("./dev"));
-  }
-  return merge({}, config, require("./prod"));
-};
+const config = {
+  projectName: "wx-csbwg-minapp",
+  date: "2024-3-28",
+  designWidth: 750,
+  deviceRatio: {
+    640: 2.34 / 2,
+    750: 1,
+    828: 1.81 / 2,
+  },
+  sourceRoot: "src",
+  outputRoot: "dist",
+  plugins: [],
+  defineConstants: {
+    "process.env.TARO_APP_API": JSON.stringify(process.env.TARO_APP_API),
+  },
+  copy: {
+    patterns: [],
+    options: {},
+  },
+  framework: "react",
+  mini: {
+    postcss: {
+      pxtransform: {
+        enable: true,
+        config: {
+          mediaQuery: true,
+        },
+      },
+      url: {
+        enable: true,
+        config: {
+          limit: 1024, // 设定转换尺寸上限
+        },
+      },
+      cssModules: {
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
+        config: {
+          namingPattern: "module", // 转换模式,取值为 global/module
+          generateScopedName: "[name]__[local]___[hash:base64:5]",
+        },
+      },
+    },
+  },
+  h5: {
+    publicPath: "/",
+    staticDirectory: "static",
+    postcss: {
+      autoprefixer: {
+        enable: true,
+        config: {},
+      },
+      cssModules: {
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
+        config: {
+          namingPattern: "module", // 转换模式,取值为 global/module
+          generateScopedName: "[name]__[local]___[hash:base64:5]",
+        },
+      },
+    },
+  },
+};
+
+module.exports = function (merge) {
+  if (process.env.NODE_ENV === "development") {
+    return merge({}, config, require("./dev"));
+  }
+  return merge({}, config, require("./prod"));
+};

+ 37 - 37
config/prod.js

@@ -1,37 +1,37 @@
-module.exports = {
-  env: {
-    NODE_ENV: '"production"'
-  },
-  defineConstants: {
-  },
-  mini: {},
-  h5: {
-    /**
-     * WebpackChain 插件配置
-     * @docs https://github.com/neutrinojs/webpack-chain
-     */
-    // webpackChain (chain) {
-    //   /**
-    //    * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
-    //    * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
-    //    */
-    //   chain.plugin('analyzer')
-    //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
-
-    //   /**
-    //    * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
-    //    * @docs https://github.com/chrisvfritz/prerender-spa-plugin
-    //    */
-    //   const path = require('path')
-    //   const Prerender = require('prerender-spa-plugin')
-    //   const staticDir = path.join(__dirname, '..', 'dist')
-    //   chain
-    //     .plugin('prerender')
-    //     .use(new Prerender({
-    //       staticDir,
-    //       routes: [ '/pages/index/index' ],
-    //       postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
-    //     }))
-    // }
-  }
-}
+module.exports = {
+  env: {
+    NODE_ENV: '"production"'
+  },
+  defineConstants: {
+  },
+  mini: {},
+  h5: {
+    /**
+     * WebpackChain 插件配置
+     * @docs https://github.com/neutrinojs/webpack-chain
+     */
+    // webpackChain (chain) {
+    //   /**
+    //    * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
+    //    * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
+    //    */
+    //   chain.plugin('analyzer')
+    //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
+
+    //   /**
+    //    * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
+    //    * @docs https://github.com/chrisvfritz/prerender-spa-plugin
+    //    */
+    //   const path = require('path')
+    //   const Prerender = require('prerender-spa-plugin')
+    //   const staticDir = path.join(__dirname, '..', 'dist')
+    //   chain
+    //     .plugin('prerender')
+    //     .use(new Prerender({
+    //       staticDir,
+    //       routes: [ '/pages/index/index' ],
+    //       postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
+    //     }))
+    // }
+  }
+}

+ 24 - 24
global.d.ts

@@ -1,24 +1,24 @@
-/// <reference types="@tarojs/taro" />
-
-declare module '*.png';
-declare module '*.gif';
-declare module '*.jpg';
-declare module '*.jpeg';
-declare module '*.svg';
-declare module '*.css';
-declare module '*.less';
-declare module '*.scss';
-declare module '*.sass';
-declare module '*.styl';
-
-declare namespace JSX {
-    interface IntrinsicElements {
-        'import': React.DetailedHTMLProps<React.EmbedHTMLAttributes<HTMLEmbedElement>, HTMLEmbedElement>
-    }
-}
-
-declare namespace NodeJS {
-  interface ProcessEnv {
-    TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd'
-  }
-}
+/// <reference types="@tarojs/taro" />
+
+declare module '*.png';
+declare module '*.gif';
+declare module '*.jpg';
+declare module '*.jpeg';
+declare module '*.svg';
+declare module '*.css';
+declare module '*.less';
+declare module '*.scss';
+declare module '*.sass';
+declare module '*.styl';
+
+declare namespace JSX {
+    interface IntrinsicElements {
+        'import': React.DetailedHTMLProps<React.EmbedHTMLAttributes<HTMLEmbedElement>, HTMLEmbedElement>
+    }
+}
+
+declare namespace NodeJS {
+  interface ProcessEnv {
+    TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd'
+  }
+}

+ 76 - 76
package.json

@@ -1,76 +1,76 @@
-{
-  "name": "wx-csbwg-minapp",
-  "version": "1.0.0",
-  "private": true,
-  "description": "无锡慈善博物馆-小程序",
-  "templateInfo": {
-    "name": "mobx",
-    "typescript": true,
-    "css": "sass"
-  },
-  "scripts": {
-    "build:weapp": "cross-env TARO_APP_API=https://sit-wuxicishan.4dage.com taro build --type weapp",
-    "build:swan": "taro build --type swan",
-    "build:alipay": "taro build --type alipay",
-    "build:tt": "taro build --type tt",
-    "build:h5": "taro build --type h5",
-    "build:rn": "taro build --type rn",
-    "build:qq": "taro build --type qq",
-    "build:quickapp": "taro build --type quickapp",
-    "dev:weapp": "cross-env TARO_APP_API=https://sit-wuxicishan.4dage.com npm run build:weapp -- --watch",
-    "dev:swan": "npm run build:swan -- --watch",
-    "dev:alipay": "npm run build:alipay -- --watch",
-    "dev:tt": "npm run build:tt -- --watch",
-    "dev:h5": "cross-env TARO_APP_API=https://sit-wuxicishan.4dage.com npm run build:h5 -- --watch",
-    "dev:rn": "npm run build:rn -- --watch",
-    "dev:qq": "npm run build:qq -- --watch",
-    "dev:quickapp": "npm run build:quickapp -- --watch"
-  },
-  "browserslist": [
-    "last 3 versions",
-    "Android >= 4.1",
-    "ios >= 8"
-  ],
-  "author": "",
-  "license": "MIT",
-  "dependencies": {
-    "@babel/runtime": "^7.7.7",
-    "@dage/hooks": "^1.0.1",
-    "@dage/service": "^1.0.3",
-    "@dage/utils": "^1.0.2",
-    "@tarojs/components": "3.4.9",
-    "@tarojs/plugin-framework-react": "3.4.9",
-    "@tarojs/react": "3.4.9",
-    "@tarojs/runtime": "3.4.9",
-    "@tarojs/taro": "3.4.9",
-    "async-validator": "^4.2.5",
-    "classnames": "^2.5.1",
-    "mobx": "^4.8.0",
-    "mobx-react": "^6.1.4",
-    "react": "^17.0.0",
-    "react-dom": "^17.0.0",
-    "taro-ui": "^3.3.0"
-  },
-  "devDependencies": {
-    "@babel/core": "^7.8.0",
-    "@tarojs/mini-runner": "3.4.9",
-    "@tarojs/webpack-runner": "3.4.9",
-    "@types/react": "^17.0.2",
-    "@types/sass": "1.43.1",
-    "@types/webpack-env": "^1.13.6",
-    "@typescript-eslint/eslint-plugin": "^5.20.0",
-    "@typescript-eslint/parser": "^5.20.0",
-    "babel-preset-taro": "3.4.9",
-    "cross-env": "^7.0.3",
-    "eslint": "^8.12.0",
-    "eslint-config-taro": "3.4.9",
-    "eslint-plugin-import": "^2.12.0",
-    "eslint-plugin-react": "^7.8.2",
-    "eslint-plugin-react-hooks": "^4.2.0",
-    "stylelint": "9.3.0",
-    "typescript": "^4.1.0"
-  },
-  "resolutions": {
-    "query-string": "7.1.3"
-  }
-}
+{
+  "name": "wx-csbwg-minapp",
+  "version": "1.0.0",
+  "private": true,
+  "description": "无锡慈善博物馆-小程序",
+  "templateInfo": {
+    "name": "mobx",
+    "typescript": true,
+    "css": "sass"
+  },
+  "scripts": {
+    "build:weapp": "cross-env TARO_APP_API=https://wuxicharitymuseum.cn taro build --type weapp",
+    "build:swan": "taro build --type swan",
+    "build:alipay": "taro build --type alipay",
+    "build:tt": "taro build --type tt",
+    "build:h5": "taro build --type h5",
+    "build:rn": "taro build --type rn",
+    "build:qq": "taro build --type qq",
+    "build:quickapp": "taro build --type quickapp",
+    "dev:weapp": "cross-env TARO_APP_API=https://wuxicharitymuseum.cn npm run build:weapp -- --watch",
+    "dev:swan": "npm run build:swan -- --watch",
+    "dev:alipay": "npm run build:alipay -- --watch",
+    "dev:tt": "npm run build:tt -- --watch",
+    "dev:h5": "cross-env TARO_APP_API=https://wuxicharitymuseum.cn npm run build:h5 -- --watch",
+    "dev:rn": "npm run build:rn -- --watch",
+    "dev:qq": "npm run build:qq -- --watch",
+    "dev:quickapp": "npm run build:quickapp -- --watch"
+  },
+  "browserslist": [
+    "last 3 versions",
+    "Android >= 4.1",
+    "ios >= 8"
+  ],
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "@babel/runtime": "^7.7.7",
+    "@dage/hooks": "^1.0.1",
+    "@dage/service": "^1.0.3",
+    "@dage/utils": "^1.0.2",
+    "@tarojs/components": "3.4.9",
+    "@tarojs/plugin-framework-react": "3.4.9",
+    "@tarojs/react": "3.4.9",
+    "@tarojs/runtime": "3.4.9",
+    "@tarojs/taro": "3.4.9",
+    "async-validator": "^4.2.5",
+    "classnames": "^2.5.1",
+    "mobx": "^4.8.0",
+    "mobx-react": "^6.1.4",
+    "react": "^17.0.0",
+    "react-dom": "^17.0.0",
+    "taro-ui": "^3.3.0"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.8.0",
+    "@tarojs/mini-runner": "3.4.9",
+    "@tarojs/webpack-runner": "3.4.9",
+    "@types/react": "^17.0.2",
+    "@types/sass": "1.43.1",
+    "@types/webpack-env": "^1.13.6",
+    "@typescript-eslint/eslint-plugin": "^5.20.0",
+    "@typescript-eslint/parser": "^5.20.0",
+    "babel-preset-taro": "3.4.9",
+    "cross-env": "^7.0.3",
+    "eslint": "^8.12.0",
+    "eslint-config-taro": "3.4.9",
+    "eslint-plugin-import": "^2.12.0",
+    "eslint-plugin-react": "^7.8.2",
+    "eslint-plugin-react-hooks": "^4.2.0",
+    "stylelint": "9.3.0",
+    "typescript": "^4.1.0"
+  },
+  "resolutions": {
+    "query-string": "7.1.3"
+  }
+}

+ 13 - 13
project.config.json

@@ -1,13 +1,13 @@
-{
-  "miniprogramRoot": "./dist",
-  "projectname": "wx-csbwg-minapp",
-  "description": "无锡慈善博物馆-小程序",
-  "appid": "wx0bc995dace29b2ba",
-  "setting": {
-    "urlCheck": true,
-    "es6": false,
-    "postcss": false,
-    "minified": false
-  },
-  "compileType": "miniprogram"
-}
+{
+  "miniprogramRoot": "./dist",
+  "projectname": "wx-csbwg-minapp",
+  "description": "无锡慈善博物馆-小程序",
+  "appid": "wx4d5edf96098e897f",
+  "setting": {
+    "urlCheck": true,
+    "es6": false,
+    "postcss": false,
+    "minified": false
+  },
+  "compileType": "miniprogram"
+}

+ 13 - 13
project.tt.json

@@ -1,13 +1,13 @@
-{
-  "miniprogramRoot": "./",
-  "projectname": "wx-csbwg-minapp",
-  "description": "无锡慈善博物馆-小程序",
-  "appid": "touristappid",
-  "setting": {
-    "urlCheck": true,
-    "es6": false,
-    "postcss": false,
-    "minified": false
-  },
-  "compileType": "miniprogram"
-}
+{
+  "miniprogramRoot": "./",
+  "projectname": "wx-csbwg-minapp",
+  "description": "无锡慈善博物馆-小程序",
+  "appid": "touristappid",
+  "setting": {
+    "urlCheck": true,
+    "es6": false,
+    "postcss": false,
+    "minified": false
+  },
+  "compileType": "miniprogram"
+}

+ 126 - 126
src/api/index.ts

@@ -1,126 +1,126 @@
-import { requestByGet, requestByPost } from "@dage/service";
-
-export const wechatLoginApi = (code: string) => {
-  return requestByGet("/api/show/wx/login/" + code, undefined, {
-    meta: {
-      showError: true,
-    },
-  });
-};
-
-export const setUserInfoApi = (params: any) => {
-  return requestByPost("/api/cms/game/editUser", params, {
-    meta: {
-      showError: true,
-    },
-  });
-};
-
-export const checkLoginApi = () => {
-  return requestByGet("/api/show/checkLogin");
-};
-
-export const feedbackApi = (params: any) => {
-  return requestByPost("/api/cms/game/message", params, {
-    meta: {
-      showError: true,
-      showLoading: true,
-    },
-  });
-};
-
-export const getUserPointApi = () => {
-  return requestByGet("/api/cms/game/getPoint");
-};
-
-export const getPointRecordListApi = () => {
-  return requestByGet("/api/cms/game/point/getList", undefined, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const getProductListApi = (params: any) => {
-  return requestByPost("/api/cms/game/prize/pageList", params, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const redeemApi = (params: any) => {
-  return requestByPost("/api/cms/game/prize/redeem", params, {
-    meta: {
-      showLoading: true,
-      showError: true,
-    },
-  });
-};
-
-export const getSignListApi = (params: any) => {
-  return requestByGet("/api/show/visit/getList", params, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const addVisitApi = (params: any) => {
-  return requestByPost("/api/show/visit/addVisit", params);
-};
-
-/**
- * 检查昵称是否违规
- */
-export const validateNickName = (name: string) => {
-  return requestByPost(`/api/show/saveNickname/${name}`, undefined, {
-    meta: {
-      showError: true,
-    },
-  });
-};
-
-export const getShopRuleApi = () => {
-  return requestByPost("/api/show/getRuleConfig", undefined, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const handleVisitStateApi = (m: number) => {
-  return requestByGet(`/api/cms/game/user/update/invite/${m}`);
-};
-
-export const getRankingListApi = (params?: any) => {
-  return requestByGet("/api/cms/game/point/user/getSort", params, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const getProtocolDetailApi = (m) => {
-  return requestByGet(`/api/show/getDictConfig/${m}`, undefined, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const checkRedeemApi = () => {
-  return requestByGet(`/api/cms/game/redeem/check`, undefined, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
-
-export const getRedeemInfoApi = () => {
-  return requestByGet(`/api/cms/game/redeem/info`, undefined, {
-    meta: {
-      showLoading: true,
-    },
-  });
-};
+import { requestByGet, requestByPost } from "@dage/service";
+
+export const wechatLoginApi = (code: string) => {
+  return requestByGet("/api/show/wx/login/" + code, undefined, {
+    meta: {
+      showError: true,
+    },
+  });
+};
+
+export const setUserInfoApi = (params: any) => {
+  return requestByPost("/api/cms/game/editUser", params, {
+    meta: {
+      showError: true,
+    },
+  });
+};
+
+export const checkLoginApi = () => {
+  return requestByGet("/api/show/checkLogin");
+};
+
+export const feedbackApi = (params: any) => {
+  return requestByPost("/api/cms/game/message", params, {
+    meta: {
+      showError: true,
+      showLoading: true,
+    },
+  });
+};
+
+export const getUserPointApi = () => {
+  return requestByGet("/api/cms/game/getPoint");
+};
+
+export const getPointRecordListApi = () => {
+  return requestByGet("/api/cms/game/point/getList", undefined, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const getProductListApi = (params: any) => {
+  return requestByPost("/api/cms/game/prize/pageList", params, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const redeemApi = (params: any) => {
+  return requestByPost("/api/cms/game/prize/redeem", params, {
+    meta: {
+      showLoading: true,
+      showError: true,
+    },
+  });
+};
+
+export const getSignListApi = (params: any) => {
+  return requestByGet("/api/show/visit/getList", params, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const addVisitApi = (params: any) => {
+  return requestByPost("/api/show/visit/addVisit", params);
+};
+
+/**
+ * 检查昵称是否违规
+ */
+export const validateNickName = (name: string) => {
+  return requestByPost(`/api/show/saveNickname/${name}`, undefined, {
+    meta: {
+      showError: true,
+    },
+  });
+};
+
+export const getShopRuleApi = () => {
+  return requestByPost("/api/show/getRuleConfig", undefined, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const handleVisitStateApi = (m: number) => {
+  return requestByGet(`/api/cms/game/user/update/invite/${m}`);
+};
+
+export const getRankingListApi = (params?: any) => {
+  return requestByGet("/api/cms/game/point/user/getSort", params, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const getProtocolDetailApi = (m) => {
+  return requestByGet(`/api/show/getDictConfig/${m}`, undefined, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const checkRedeemApi = () => {
+  return requestByGet(`/api/cms/game/redeem/check`, undefined, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};
+
+export const getRedeemInfoApi = () => {
+  return requestByGet(`/api/cms/game/redeem/info`, undefined, {
+    meta: {
+      showLoading: true,
+    },
+  });
+};

+ 29 - 29
src/app.config.ts

@@ -1,29 +1,29 @@
-export default defineAppConfig({
-  pages: [
-    "pages/tempp/index",
-    "pages/banner/index",
-    "pages/home/index",
-    "pages/iframe/index",
-  ],
-  subpackages: [
-    {
-      root: "subModule",
-      pages: [
-        "pages/shopmall/index",
-        "pages/feedback/index",
-        "pages/order/index",
-        "pages/iframe/index",
-        "pages/portrait-iframe/index",
-        "pages/museum/index",
-        "pages/protocol/index",
-        "pages/document/index",
-      ],
-    },
-  ],
-  window: {
-    backgroundTextStyle: "light",
-    navigationBarBackgroundColor: "#fff",
-    navigationBarTitleText: "锡善云城",
-    navigationBarTextStyle: "black",
-  },
-});
+export default defineAppConfig({
+  pages: [
+    "pages/tempp/index",
+    "pages/banner/index",
+    "pages/home/index",
+    "pages/iframe/index",
+  ],
+  subpackages: [
+    {
+      root: "subModule",
+      pages: [
+        "pages/shopmall/index",
+        "pages/feedback/index",
+        "pages/order/index",
+        "pages/iframe/index",
+        "pages/portrait-iframe/index",
+        "pages/museum/index",
+        "pages/protocol/index",
+        "pages/document/index",
+      ],
+    },
+  ],
+  window: {
+    backgroundTextStyle: "light",
+    navigationBarBackgroundColor: "#fff",
+    navigationBarTitleText: "锡善云城",
+    navigationBarTextStyle: "black",
+  },
+});

+ 65 - 65
src/app.scss

@@ -1,65 +1,65 @@
-page,
-.root {
-  font-family: "SourceHanSansSCRegular";
-}
-
-.margin-safe-area-bottom {
-  margin-bottom: constant(safe-area-inset-bottom);
-  margin-bottom: env(safe-area-inset-bottom);
-}
-.padding-safe-area-bottom {
-  padding-bottom: constant(safe-area-inset-bottom) !important;
-  padding-bottom: env(safe-area-inset-bottom) !important;
-}
-
-.limit-line {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  display: -webkit-box;
-  -webkit-line-clamp: 1;
-  align-content: center;
-  word-break: break-all;
-  word-wrap: break-word;
-  /* autoprefixer: off */
-  -webkit-box-orient: vertical;
-}
-
-.line-2 {
-  -webkit-line-clamp: 2;
-}
-
-@font-face {
-  font-family: "Source Han Serif CN Heavy";
-  src: url("https://houseoss.4dkankan.com/project/wx-csbwg-public/fonts/SourceHanSerifCNHeavy.otf");
-}
-@font-face {
-  font-family: "Alibaba PuHuiTi-Bold";
-  src: url("https://houseoss.4dkankan.com/project/wx-csbwg-public/fonts/ALIBABA-PUHUITI-BOLD.OTF");
-}
-
-.ld-page {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: rgba(255, 255, 255);
-  transition: opacity ease-in 1s;
-  z-index: 999;
-
-  &__icon {
-    animation: loading-rotate 2s linear infinite;
-  }
-  &.hide {
-    opacity: 0;
-  }
-}
-
-@keyframes loading-rotate {
-  100% {
-    transform: rotate(360deg);
-  }
-}
+page,
+.root {
+  font-family: "SourceHanSansSCRegular";
+}
+
+.margin-safe-area-bottom {
+  margin-bottom: constant(safe-area-inset-bottom);
+  margin-bottom: env(safe-area-inset-bottom);
+}
+.padding-safe-area-bottom {
+  padding-bottom: constant(safe-area-inset-bottom) !important;
+  padding-bottom: env(safe-area-inset-bottom) !important;
+}
+
+.limit-line {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 1;
+  align-content: center;
+  word-break: break-all;
+  word-wrap: break-word;
+  /* autoprefixer: off */
+  -webkit-box-orient: vertical;
+}
+
+.line-2 {
+  -webkit-line-clamp: 2;
+}
+
+@font-face {
+  font-family: "Source Han Serif CN Heavy";
+  src: url("https://wuxicharitymuseum.cn/wx-csbwg-public/fonts/SourceHanSerifCNHeavy.otf");
+}
+@font-face {
+  font-family: "Alibaba PuHuiTi-Bold";
+  src: url("https://wuxicharitymuseum.cn/wx-csbwg-public/fonts/ALIBABA-PUHUITI-BOLD.OTF");
+}
+
+.ld-page {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: rgba(255, 255, 255);
+  transition: opacity ease-in 1s;
+  z-index: 999;
+
+  &__icon {
+    animation: loading-rotate 2s linear infinite;
+  }
+  &.hide {
+    opacity: 0;
+  }
+}
+
+@keyframes loading-rotate {
+  100% {
+    transform: rotate(360deg);
+  }
+}

+ 121 - 121
src/app.tsx

@@ -1,121 +1,121 @@
-import Taro from "@tarojs/taro";
-import { Component } from "react";
-import { Provider } from "mobx-react";
-import { compose, initial } from "@dage/service";
-
-import store from "./store";
-import { login, TOKEN_KEY } from "./utils";
-import { $fetch } from "./utils/fetch";
-
-import "./app.scss";
-import "./taro-ui.scss";
-
-declare global {
-  interface DageRequestMeta {
-    showLoading?: boolean;
-    showError?: boolean;
-  }
-}
-
-initial({
-  // @ts-ignore
-  fetch: $fetch,
-  baseURL: process.env.TARO_APP_API as string,
-  interceptor: compose(
-    (request, next) => {
-      const token = Taro.getStorageSync(TOKEN_KEY);
-      if (token) {
-        request.headers["token"] = token;
-      }
-      return next();
-    },
-    // @ts-ignore
-    async (request, next) => {
-      const { showLoading, showError } = request.meta;
-      try {
-        if (showLoading) {
-          Taro.showLoading({
-            title: "加载中...",
-          });
-        }
-
-        const response = await next();
-
-        if (showLoading && showError) {
-          Taro.hideLoading();
-        }
-
-        if (response.code !== 0) {
-          // @ts-ignore
-          const message = response.msg ?? "系统出差中";
-          response.errorMessage = message;
-          if (showError) {
-            Taro.showToast({
-              title: message,
-              icon: "none",
-              duration: 2000,
-            });
-          }
-        }
-
-        return response;
-      } catch (err) {
-        Taro.showToast({
-          title: "系统出差中",
-          icon: "none",
-          duration: 2000,
-        });
-      } finally {
-        if (showLoading && !showError) {
-          Taro.hideLoading();
-        }
-      }
-    }
-  ),
-});
-
-const loadFont = (url: string, family: string) => {
-  return new Promise((res, rej) => {
-    Taro.loadFontFace({
-      family,
-      global: true,
-      source: `url('${url}')`,
-      // @ts-ignore
-      scopes: ["native", "webview"],
-      success: res,
-      fail: rej,
-    });
-  });
-};
-
-class App extends Component {
-  componentDidMount() {
-    login();
-
-    loadFont(
-      "https://houseoss.4dkankan.com/project/wx-csbwg-public/fonts/SourceHanSansSCBold.otf",
-      "SourceHanSansCN-Bold"
-    );
-    loadFont(
-      "https://houseoss.4dkankan.com/project/wx-csbwg-public/fonts/SourceHanSerifCN-SemiBold.otf",
-      "SourceHanSerifCN-Bold"
-    );
-    loadFont(
-      "https://houseoss.4dkankan.com/project/wx-csbwg-public/fonts/SourceHanSansSCRegular.otf",
-      "SourceHanSansSCRegular"
-    );
-  }
-
-  componentDidShow() {}
-
-  componentDidHide() {}
-
-  componentDidCatchError() {}
-
-  // this.props.children 就是要渲染的页面
-  render() {
-    return <Provider store={store}>{this.props.children}</Provider>;
-  }
-}
-
-export default App;
+import Taro from "@tarojs/taro";
+import { Component } from "react";
+import { Provider } from "mobx-react";
+import { compose, initial } from "@dage/service";
+
+import store from "./store";
+import { login, TOKEN_KEY } from "./utils";
+import { $fetch } from "./utils/fetch";
+
+import "./app.scss";
+import "./taro-ui.scss";
+
+declare global {
+  interface DageRequestMeta {
+    showLoading?: boolean;
+    showError?: boolean;
+  }
+}
+
+initial({
+  // @ts-ignore
+  fetch: $fetch,
+  baseURL: process.env.TARO_APP_API as string,
+  interceptor: compose(
+    (request, next) => {
+      const token = Taro.getStorageSync(TOKEN_KEY);
+      if (token) {
+        request.headers["token"] = token;
+      }
+      return next();
+    },
+    // @ts-ignore
+    async (request, next) => {
+      const { showLoading, showError } = request.meta;
+      try {
+        if (showLoading) {
+          Taro.showLoading({
+            title: "加载中...",
+          });
+        }
+
+        const response = await next();
+
+        if (showLoading && showError) {
+          Taro.hideLoading();
+        }
+
+        if (response.code !== 0) {
+          // @ts-ignore
+          const message = response.msg ?? "系统出差中";
+          response.errorMessage = message;
+          if (showError) {
+            Taro.showToast({
+              title: message,
+              icon: "none",
+              duration: 2000,
+            });
+          }
+        }
+
+        return response;
+      } catch (err) {
+        Taro.showToast({
+          title: "系统出差中",
+          icon: "none",
+          duration: 2000,
+        });
+      } finally {
+        if (showLoading && !showError) {
+          Taro.hideLoading();
+        }
+      }
+    }
+  ),
+});
+
+const loadFont = (url: string, family: string) => {
+  return new Promise((res, rej) => {
+    Taro.loadFontFace({
+      family,
+      global: true,
+      source: `url('${url}')`,
+      // @ts-ignore
+      scopes: ["native", "webview"],
+      success: res,
+      fail: rej,
+    });
+  });
+};
+
+class App extends Component {
+  componentDidMount() {
+    login();
+
+    loadFont(
+      "https://wuxicharitymuseum.cn/wx-csbwg-public/fonts/SourceHanSansSCBold.otf",
+      "SourceHanSansCN-Bold"
+    );
+    loadFont(
+      "https://wuxicharitymuseum.cn/wx-csbwg-public/fonts/SourceHanSerifCN-SemiBold.otf",
+      "SourceHanSerifCN-Bold"
+    );
+    loadFont(
+      "https://wuxicharitymuseum.cn/wx-csbwg-public/fonts/SourceHanSansSCRegular.otf",
+      "SourceHanSansSCRegular"
+    );
+  }
+
+  componentDidShow() {}
+
+  componentDidHide() {}
+
+  componentDidCatchError() {}
+
+  // this.props.children 就是要渲染的页面
+  render() {
+    return <Provider store={store}>{this.props.children}</Provider>;
+  }
+}
+
+export default App;

+ 23 - 23
src/components/FooterProtocol/index.scss

@@ -1,23 +1,23 @@
-.footer-protocol {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
-  gap: 12px;
-  margin-top: 20px;
-  font-size: 23px;
-  color: #424a4a;
-  font-family: "SourceHanSerifCN-Bold";
-
-  .primary {
-    color: #589498;
-  }
-  &__checkbox {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    width: 23px;
-    height: 23px;
-    border-radius: 4px;
-    border: 2px solid #d5ddd7;
-  }
-}
+.footer-protocol {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 12px;
+  margin-top: 20px;
+  font-size: 23px;
+  color: #424a4a;
+  font-family: "SourceHanSerifCN-Bold";
+
+  .primary {
+    color: #589498;
+  }
+  &__checkbox {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 23px;
+    height: 23px;
+    border-radius: 4px;
+    border: 2px solid #d5ddd7;
+  }
+}

+ 50 - 50
src/components/FooterProtocol/index.tsx

@@ -1,50 +1,50 @@
-import { View, Text, ITouchEvent } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import classNames from "classnames";
-import React from "react";
-import { AtIcon } from "taro-ui";
-import "./index.scss";
-
-export interface FooterProtocolProps {
-  style?: React.CSSProperties;
-  checked: boolean;
-  setChecked: Function;
-}
-
-export const FooterProtocol: FC<FooterProtocolProps> = ({
-  style,
-  checked,
-  setChecked,
-}) => {
-  const handleClick = () => {
-    setChecked(!checked);
-  };
-
-  const goDetail = (e: ITouchEvent, id: number) => {
-    e.stopPropagation();
-
-    Taro.navigateTo({
-      url: `/subModule/pages/protocol/index?id=${id}`,
-    });
-  };
-
-  return (
-    <View
-      className={classNames("footer-protocol", { checked })}
-      style={style}
-      onClick={handleClick}
-    >
-      <View className="footer-protocol__checkbox">
-        {checked && <AtIcon value="check" size={8} color="#589498" />}
-      </View>
-      阅读并同意
-      <Text className="primary" onClick={(e) => goDetail(e, 2)}>
-        《用户服务协议》
-      </Text>
-      及
-      <Text className="primary" onClick={(e) => goDetail(e, 3)}>
-        《个人信息保护政策》
-      </Text>
-    </View>
-  );
-};
+import { View, Text, ITouchEvent } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import classNames from "classnames";
+import React from "react";
+import { AtIcon } from "taro-ui";
+import "./index.scss";
+
+export interface FooterProtocolProps {
+  style?: React.CSSProperties;
+  checked: boolean;
+  setChecked: Function;
+}
+
+export const FooterProtocol: FC<FooterProtocolProps> = ({
+  style,
+  checked,
+  setChecked,
+}) => {
+  const handleClick = () => {
+    setChecked(!checked);
+  };
+
+  const goDetail = (e: ITouchEvent, id: number) => {
+    e.stopPropagation();
+
+    Taro.navigateTo({
+      url: `/subModule/pages/protocol/index?id=${id}`,
+    });
+  };
+
+  return (
+    <View
+      className={classNames("footer-protocol", { checked })}
+      style={style}
+      onClick={handleClick}
+    >
+      <View className="footer-protocol__checkbox">
+        {checked && <AtIcon value="check" size={8} color="#589498" />}
+      </View>
+      阅读并同意
+      <Text className="primary" onClick={(e) => goDetail(e, 2)}>
+        《用户服务协议》
+      </Text>
+      及
+      <Text className="primary" onClick={(e) => goDetail(e, 3)}>
+        《个人信息保护政策》
+      </Text>
+    </View>
+  );
+};

+ 32 - 32
src/components/LoadingPage/index.scss

@@ -1,32 +1,32 @@
-.loading-page {
-  position: relative;
-  height: 100%;
-  overflow: hidden;
-  background: url("https://houseoss.4dkankan.com/project/wx-csbwg-public/images/bg_loading%402x-min.png")
-    no-repeat center center / cover;
-}
-
-.loading-main {
-  position: absolute;
-  left: 130px;
-  right: 130px;
-  bottom: 40px;
-
-  &__txt {
-    color: #ff9494;
-    text-align: center;
-    font-size: 31px;
-  }
-}
-
-.loading-progress {
-  position: relative;
-  margin-bottom: 20px;
-
-  .at-progress {
-    &__outer-inner {
-      background: rgba(0, 0, 0, 0.19);
-      border: 2rpx solid #ffffff;
-    }
-  }
-}
+.loading-page {
+  position: relative;
+  height: 100%;
+  overflow: hidden;
+  background: url("https://wuxicharitymuseum.cn/wx-csbwg-public/images/bg_loading%402x-min.png")
+    no-repeat center center / cover;
+}
+
+.loading-main {
+  position: absolute;
+  left: 130px;
+  right: 130px;
+  bottom: 40px;
+
+  &__txt {
+    color: #ff9494;
+    text-align: center;
+    font-size: 31px;
+  }
+}
+
+.loading-progress {
+  position: relative;
+  margin-bottom: 20px;
+
+  .at-progress {
+    &__outer-inner {
+      background: rgba(0, 0, 0, 0.19);
+      border: 2rpx solid #ffffff;
+    }
+  }
+}

+ 54 - 54
src/components/LoadingPage/index.tsx

@@ -1,54 +1,54 @@
-import { View } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import { AtProgress } from "taro-ui";
-import { useEffect } from "react";
-import { usePreloader } from "../../hooks/usePreloader";
-import "./index.scss";
-
-export interface LoadingPageProps {}
-
-const getList = () => {
-  const list: string[] = [];
-
-  for (let i = 1; i <= 26; i++) {
-    list.push(
-      `https://houseoss.4dkankan.com/project/wx-csbwg-public/line-sights/${i}.png`,
-      `https://houseoss.4dkankan.com/project/wx-csbwg-public/sights/${i}.png`
-    );
-  }
-
-  return list;
-};
-
-export const LoadingPage: FC<LoadingPageProps> = () => {
-  const { start, percent } = usePreloader({
-    list: [...getList()],
-    success() {
-      setInterval(() => {
-        Taro.switchTab({
-          url: "/pages/home/index",
-        });
-      }, 500);
-    },
-  });
-
-  useEffect(() => {
-    start();
-  }, []);
-
-  return (
-    <View className="loading-page">
-      <View className="loading-main margin-safe-area-bottom">
-        <View className="loading-progress">
-          <AtProgress
-            percent={Number(percent)}
-            isHidePercent={true}
-            color="#FF8888"
-          />
-        </View>
-
-        <View className="loading-main__txt">LOADING...</View>
-      </View>
-    </View>
-  );
-};
+import { View } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import { AtProgress } from "taro-ui";
+import { useEffect } from "react";
+import { usePreloader } from "../../hooks/usePreloader";
+import "./index.scss";
+
+export interface LoadingPageProps {}
+
+const getList = () => {
+  const list: string[] = [];
+
+  for (let i = 1; i <= 26; i++) {
+    list.push(
+      `https://wuxicharitymuseum.cn/wx-csbwg-public/line-sights/${i}.png`,
+      `https://wuxicharitymuseum.cn/wx-csbwg-public/sights/${i}.png`
+    );
+  }
+
+  return list;
+};
+
+export const LoadingPage: FC<LoadingPageProps> = () => {
+  const { start, percent } = usePreloader({
+    list: [...getList()],
+    success() {
+      setInterval(() => {
+        Taro.switchTab({
+          url: "/pages/home/index",
+        });
+      }, 500);
+    },
+  });
+
+  useEffect(() => {
+    start();
+  }, []);
+
+  return (
+    <View className="loading-page">
+      <View className="loading-main margin-safe-area-bottom">
+        <View className="loading-progress">
+          <AtProgress
+            percent={Number(percent)}
+            isHidePercent={true}
+            color="#FF8888"
+          />
+        </View>
+
+        <View className="loading-main__txt">LOADING...</View>
+      </View>
+    </View>
+  );
+};

+ 27 - 27
src/components/Video/index.scss

@@ -1,27 +1,27 @@
-.video-wrap {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  z-index: 998;
-
-  &__video {
-    width: 100%;
-    height: 100%;
-  }
-  &__skip {
-    position: absolute;
-    right: 30px;
-    bottom: 30px;
-    display: flex;
-    justify-content: center;
-    line-height: 86px;
-    width: 234px;
-    height: 102px;
-    color: white;
-    font-size: 28px;
-    font-family: "SourceHanSerifCN-Bold";
-    background: url("./btn0@2x-min.png") no-repeat center / contain;
-  }
-}
+.video-wrap {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 998;
+
+  &__video {
+    width: 100%;
+    height: 100%;
+  }
+  &__skip {
+    position: absolute;
+    right: 30px;
+    bottom: 30px;
+    display: flex;
+    justify-content: center;
+    line-height: 86px;
+    width: 234px;
+    height: 102px;
+    color: white;
+    font-size: 28px;
+    font-family: "SourceHanSerifCN-Bold";
+    background: url("./btn0@2x-min.png") no-repeat center / contain;
+  }
+}

+ 36 - 36
src/components/Video/index.tsx

@@ -1,36 +1,36 @@
-import { Video, VideoProps, View } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import classNames from "classnames";
-import "./index.scss";
-
-export interface VideoWrapProps extends Omit<VideoProps, "onEnded"> {
-  onEnded: Function;
-}
-
-export const VideoWrap: FC<VideoWrapProps> = ({
-  className,
-  onEnded,
-  ...props
-}) => {
-  return (
-    <View className={classNames("video-wrap", className)}>
-      <Video
-        className="video-wrap__video"
-        enableProgressGesture={false}
-        objectFit="cover"
-        onWaiting={() => {
-          Taro.showLoading();
-        }}
-        onTimeUpdate={() => {
-          Taro.hideLoading();
-        }}
-        {...props}
-        onEnded={() => onEnded()}
-      />
-
-      <View className="video-wrap__skip" onClick={() => onEnded()}>
-        跳过
-      </View>
-    </View>
-  );
-};
+import { Video, VideoProps, View } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import classNames from "classnames";
+import "./index.scss";
+
+export interface VideoWrapProps extends Omit<VideoProps, "onEnded"> {
+  onEnded: Function;
+}
+
+export const VideoWrap: FC<VideoWrapProps> = ({
+  className,
+  onEnded,
+  ...props
+}) => {
+  return (
+    <View className={classNames("video-wrap", className)}>
+      <Video
+        className="video-wrap__video"
+        enableProgressGesture={false}
+        objectFit="cover"
+        onWaiting={() => {
+          Taro.showLoading();
+        }}
+        onTimeUpdate={() => {
+          Taro.hideLoading();
+        }}
+        {...props}
+        onEnded={() => onEnded()}
+      />
+
+      <View className="video-wrap__skip" onClick={() => onEnded()}>
+        跳过
+      </View>
+    </View>
+  );
+};

+ 20 - 20
src/hooks/useInterval.ts

@@ -1,20 +1,20 @@
-import { useEffect, useRef } from "react";
-
-export const useInterval = (callback: Function, delay: number | null) => {
-  const savedCallback = useRef<Function>();
-
-  useEffect(() => {
-    savedCallback.current = callback;
-  });
-
-  useEffect(() => {
-    function tick() {
-      savedCallback.current?.();
-    }
-
-    if (delay !== null) {
-      let id = setInterval(tick, delay);
-      return () => clearInterval(id);
-    }
-  }, [delay]);
-};
+import { useEffect, useRef } from "react";
+
+export const useInterval = (callback: Function, delay: number | null) => {
+  const savedCallback = useRef<Function>();
+
+  useEffect(() => {
+    savedCallback.current = callback;
+  });
+
+  useEffect(() => {
+    function tick() {
+      savedCallback.current?.();
+    }
+
+    if (delay !== null) {
+      let id = setInterval(tick, delay);
+      return () => clearInterval(id);
+    }
+  }, [delay]);
+};

+ 121 - 121
src/hooks/usePreloader.ts

@@ -1,121 +1,121 @@
-import Taro from "@tarojs/taro";
-import { useCallback, useRef, useState } from "react";
-
-export enum PRELOADER_STATUS {
-  ERROR = "error",
-  LOADING = "loading",
-  WAITING = "waiting",
-  DONE = "done",
-}
-
-const parseUrlName = (url: string) => {
-  const regex = /\/([^/]+)\.\w+$/;
-  const match = url.match(regex);
-  if (match && match.length >= 2) {
-    return match[1].split(".")[0];
-  } else {
-    return "";
-  }
-};
-
-export function usePreloader(params: {
-  list: string[];
-  /**
-   * 百分比保留几位小数
-   */
-  decimals?: number;
-  success?: Function;
-  error?: Function;
-  /**
-   * 尝试重连次数
-   * @default 3
-   */
-  retry?: number;
-}) {
-  const _list = useRef(params.list);
-  const _retry = params.retry ?? 3;
-
-  const fileSizeStack = useRef<Map<string, number>>(new Map());
-
-  /**
-   * 进度
-   */
-  const [percent, setPercent] = useState(0);
-  const [status, setStatus] = useState<PRELOADER_STATUS>(
-    PRELOADER_STATUS.WAITING
-  );
-
-  const handlePreload = (url: string, retry = _retry) => {
-    return new Promise((res, rej) => {
-      Taro.request({
-        url,
-        responseType: "arraybuffer",
-        enableChunked: true,
-      })
-        .then(() => {
-          fileSizeStack.current.set(url, 1);
-
-          if (fileSizeStack.current.size === _list.current.length) {
-            setPercent(100);
-          }
-
-          res(true);
-        })
-        .catch((err) => {
-          console.log(err);
-          if (retry) {
-            console.log("尝试重新预加载");
-            setTimeout(() => {
-              handlePreload(url, retry - 1)
-                .then(res)
-                .catch(rej);
-            }, 2000);
-          } else {
-            rej(err);
-          }
-        });
-    });
-  };
-
-  const resetParams = () => {
-    fileSizeStack.current.clear();
-    setPercent(0);
-  };
-
-  const start = () => {
-    if (status === PRELOADER_STATUS.LOADING) return;
-
-    if (status !== PRELOADER_STATUS.WAITING) {
-      resetParams();
-    }
-
-    setStatus(PRELOADER_STATUS.LOADING);
-
-    const timer = setInterval(() => {
-      if (percent < 99) {
-        setPercent((num) => num + 1);
-      } else {
-        clearInterval(timer);
-      }
-    }, 500);
-
-    Promise.all(_list.current.map((url) => handlePreload(url)))
-      .then(() => {
-        params.success?.();
-        setStatus(PRELOADER_STATUS.DONE);
-      })
-      .catch((err: Error) => {
-        params.error?.(err);
-        setStatus(PRELOADER_STATUS.ERROR);
-      })
-      .finally(() => {
-        clearInterval(timer);
-      });
-  };
-
-  return {
-    status,
-    percent,
-    start,
-  };
-}
+import Taro from "@tarojs/taro";
+import { useCallback, useRef, useState } from "react";
+
+export enum PRELOADER_STATUS {
+  ERROR = "error",
+  LOADING = "loading",
+  WAITING = "waiting",
+  DONE = "done",
+}
+
+const parseUrlName = (url: string) => {
+  const regex = /\/([^/]+)\.\w+$/;
+  const match = url.match(regex);
+  if (match && match.length >= 2) {
+    return match[1].split(".")[0];
+  } else {
+    return "";
+  }
+};
+
+export function usePreloader(params: {
+  list: string[];
+  /**
+   * 百分比保留几位小数
+   */
+  decimals?: number;
+  success?: Function;
+  error?: Function;
+  /**
+   * 尝试重连次数
+   * @default 3
+   */
+  retry?: number;
+}) {
+  const _list = useRef(params.list);
+  const _retry = params.retry ?? 3;
+
+  const fileSizeStack = useRef<Map<string, number>>(new Map());
+
+  /**
+   * 进度
+   */
+  const [percent, setPercent] = useState(0);
+  const [status, setStatus] = useState<PRELOADER_STATUS>(
+    PRELOADER_STATUS.WAITING
+  );
+
+  const handlePreload = (url: string, retry = _retry) => {
+    return new Promise((res, rej) => {
+      Taro.request({
+        url,
+        responseType: "arraybuffer",
+        enableChunked: true,
+      })
+        .then(() => {
+          fileSizeStack.current.set(url, 1);
+
+          if (fileSizeStack.current.size === _list.current.length) {
+            setPercent(100);
+          }
+
+          res(true);
+        })
+        .catch((err) => {
+          console.log(err);
+          if (retry) {
+            console.log("尝试重新预加载");
+            setTimeout(() => {
+              handlePreload(url, retry - 1)
+                .then(res)
+                .catch(rej);
+            }, 2000);
+          } else {
+            rej(err);
+          }
+        });
+    });
+  };
+
+  const resetParams = () => {
+    fileSizeStack.current.clear();
+    setPercent(0);
+  };
+
+  const start = () => {
+    if (status === PRELOADER_STATUS.LOADING) return;
+
+    if (status !== PRELOADER_STATUS.WAITING) {
+      resetParams();
+    }
+
+    setStatus(PRELOADER_STATUS.LOADING);
+
+    const timer = setInterval(() => {
+      if (percent < 99) {
+        setPercent((num) => num + 1);
+      } else {
+        clearInterval(timer);
+      }
+    }, 500);
+
+    Promise.all(_list.current.map((url) => handlePreload(url)))
+      .then(() => {
+        params.success?.();
+        setStatus(PRELOADER_STATUS.DONE);
+      })
+      .catch((err: Error) => {
+        params.error?.(err);
+        setStatus(PRELOADER_STATUS.ERROR);
+      })
+      .finally(() => {
+        clearInterval(timer);
+      });
+  };
+
+  return {
+    status,
+    percent,
+    start,
+  };
+}

+ 20 - 20
src/index.html

@@ -1,20 +1,20 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
-  <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
-  <meta name="apple-mobile-web-app-capable" content="yes">
-  <meta name="apple-touch-fullscreen" content="yes">
-  <meta name="format-detection" content="telephone=no,address=no">
-  <meta name="apple-mobile-web-app-status-bar-style" content="white">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
-  <title></title>
-  <script>
-    /* 以下为 H5 响应式脚本,请不要删除! */
-    !function(x){function w(){var v,u,t,tes,s=x.document,r=s.documentElement,a=r.getBoundingClientRect().width;if(!v&&!u){var n=!!x.navigator.appVersion.match(/AppleWebKit.*Mobile.*/);v=x.devicePixelRatio;tes=x.devicePixelRatio;v=n?v:1,u=1/v}if(a>=640){r.style.fontSize="40px"}else{if(a<=320){r.style.fontSize="20px"}else{r.style.fontSize=a/320*20+"px"}}}x.addEventListener("resize",function(){w()});w()}(window);
-  </script>
-</head>
-<body>
-  <div id="app"></div>
-</body>
-</html>
+<!DOCTYPE html>
+<html>
+<head>
+  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+  <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
+  <meta name="apple-mobile-web-app-capable" content="yes">
+  <meta name="apple-touch-fullscreen" content="yes">
+  <meta name="format-detection" content="telephone=no,address=no">
+  <meta name="apple-mobile-web-app-status-bar-style" content="white">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
+  <title></title>
+  <script>
+    /* 以下为 H5 响应式脚本,请不要删除! */
+    !function(x){function w(){var v,u,t,tes,s=x.document,r=s.documentElement,a=r.getBoundingClientRect().width;if(!v&&!u){var n=!!x.navigator.appVersion.match(/AppleWebKit.*Mobile.*/);v=x.devicePixelRatio;tes=x.devicePixelRatio;v=n?v:1,u=1/v}if(a>=640){r.style.fontSize="40px"}else{if(a<=320){r.style.fontSize="20px"}else{r.style.fontSize=a/320*20+"px"}}}x.addEventListener("resize",function(){w()});w()}(window);
+  </script>
+</head>
+<body>
+  <div id="app"></div>
+</body>
+</html>

+ 5 - 5
src/pages/banner/index.config.ts

@@ -1,5 +1,5 @@
-export default definePageConfig({
-  enableShareAppMessage: true,
-  enableShareTimeline: true,
-  navigationStyle: "custom",
-});
+export default definePageConfig({
+  enableShareAppMessage: true,
+  enableShareTimeline: true,
+  navigationStyle: "custom",
+});

+ 120 - 120
src/pages/banner/index.scss

@@ -1,120 +1,120 @@
-.page-swiper {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  padding-bottom: 30px;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  z-index: 997;
-
-  &.is-error {
-    background: url("./images/bg@2x-min.jpg") no-repeat center / cover;
-  }
-  &__bg {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    z-index: -1;
-  }
-  &__title {
-    position: absolute;
-    top: 8.5vh;
-    left: 50%;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    height: 15vh;
-    transform: translateX(-50%);
-    z-index: 2;
-  }
-  &-footer {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: 70px;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    z-index: 2;
-
-    &__logo {
-      height: 8vh;
-    }
-    &__tips {
-      height: 2.5vh;
-    }
-    &__text {
-      padding: 25px 0 42px;
-      font-size: 23px;
-      color: #405166;
-    }
-  }
-}
-
-.banner {
-  position: relative;
-  width: 100%;
-  height: 52vh;
-  z-index: 2;
-
-  &-swiper-item {
-    perspective: 800px;
-    transform-style: preserve-3d;
-  }
-  &-item {
-    position: relative;
-    margin: 20px auto 0;
-    width: 345px;
-    height: calc(52vh - 40px);
-    border: 4px solid #ffffff;
-    transform-style: preserve-3d;
-    box-sizing: border-box;
-    overflow: hidden;
-
-    &.active {
-      border-radius: 20px;
-      border: 6px solid rgba($color: #ffffff, $alpha: 0.7);
-      box-shadow: 0 0 20px #ffffff;
-    }
-    &::before {
-      content: "";
-      position: absolute;
-      top: -60px;
-      left: -60px;
-      right: -60px;
-      bottom: -60px;
-      background: url("./images/Mask-group@2x-min.png") no-repeat center / cover;
-      // animation: linear fade-in 0.2s forwards;
-      z-index: 1;
-    }
-    &__label {
-      position: absolute;
-      top: 0;
-      right: 0;
-      width: 42px;
-      z-index: 1;
-    }
-    &__img {
-      width: 100%;
-      height: 100%;
-      background-repeat: no-repeat;
-      background-size: cover;
-      will-change: background-position;
-    }
-  }
-}
-
-@keyframes fade-in {
-  0% {
-    opacity: 0;
-  }
-  100% {
-    opacity: 1;
-  }
-}
+.page-swiper {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  padding-bottom: 30px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  z-index: 997;
+
+  &.is-error {
+    background: url("./images/bg@2x-min.jpg") no-repeat center / cover;
+  }
+  &__bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: -1;
+  }
+  &__title {
+    position: absolute;
+    top: 8.5vh;
+    left: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 15vh;
+    transform: translateX(-50%);
+    z-index: 2;
+  }
+  &-footer {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 70px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    z-index: 2;
+
+    &__logo {
+      height: 8vh;
+    }
+    &__tips {
+      height: 2.5vh;
+    }
+    &__text {
+      padding: 25px 0 42px;
+      font-size: 23px;
+      color: #405166;
+    }
+  }
+}
+
+.banner {
+  position: relative;
+  width: 100%;
+  height: 52vh;
+  z-index: 2;
+
+  &-swiper-item {
+    perspective: 800px;
+    transform-style: preserve-3d;
+  }
+  &-item {
+    position: relative;
+    margin: 20px auto 0;
+    width: 345px;
+    height: calc(52vh - 40px);
+    border: 4px solid #ffffff;
+    transform-style: preserve-3d;
+    box-sizing: border-box;
+    overflow: hidden;
+
+    &.active {
+      border-radius: 20px;
+      border: 6px solid rgba($color: #ffffff, $alpha: 0.7);
+      box-shadow: 0 0 20px #ffffff;
+    }
+    &::before {
+      content: "";
+      position: absolute;
+      top: -60px;
+      left: -60px;
+      right: -60px;
+      bottom: -60px;
+      background: url("./images/Mask-group@2x-min.png") no-repeat center / cover;
+      // animation: linear fade-in 0.2s forwards;
+      z-index: 1;
+    }
+    &__label {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 42px;
+      z-index: 1;
+    }
+    &__img {
+      width: 100%;
+      height: 100%;
+      background-repeat: no-repeat;
+      background-size: cover;
+      will-change: background-position;
+    }
+  }
+}
+
+@keyframes fade-in {
+  0% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 1;
+  }
+}

+ 270 - 270
src/pages/banner/index.tsx

@@ -1,270 +1,270 @@
-import { Swiper, SwiperItem, View, Image, Video } from "@tarojs/components";
-import Taro, { FC, pxTransform } from "@tarojs/taro";
-import classNames from "classnames";
-import { useRef, useState } from "react";
-import TitleImg1 from "./images/tab2@2x-min.png";
-import TitleImg2 from "./images/tab1@2x-min.png";
-import TitleImg3 from "./images/tab3@2x-min.png";
-import LabelImg1 from "./images/Group35@2x-min.png";
-import LabelImg2 from "./images/Group37@2x-min.png";
-import LabelImg3 from "./images/Group36@2x-min.png";
-import LogoImg from "./images/LOGO@2x-min.png";
-import FootPageImg from "./images/Group23@2x-min.png";
-import baseStore from "../../store/base";
-import { getSceneUrl, login } from "../../utils";
-import { VisitCard } from "../../pages/home/components/VisitCard";
-import { VideoWrap } from "../../components/Video";
-import { AtIcon } from "taro-ui";
-import "./index.scss";
-
-const LIST = [
-  {
-    title: TitleImg1,
-    img: "https://houseoss.4dkankan.com/project/wx-csbwg-public/images/pic-or-2%402x-min.jpg",
-    label: LabelImg1,
-  },
-  {
-    title: TitleImg2,
-    img: "https://houseoss.4dkankan.com/project/wx-csbwg-public/images/pic-or%402x-min.jpg",
-    label: LabelImg2,
-  },
-  {
-    title: TitleImg3,
-    img: "https://houseoss.4dkankan.com/project/wx-csbwg-public/images/pic-or-3%402x-min.jpg",
-    label: LabelImg3,
-  },
-];
-
-const system = Taro.getSystemInfoSync();
-const getPX = (num: number) => num / (750 / system.windowWidth);
-const swipeOffsetX = getPX(390);
-const damping = 50 / swipeOffsetX;
-const rotateDamping = 20 / swipeOffsetX;
-
-const POS_MAP = {
-  0: [50, 100, 200],
-  1: [0, 50, 100],
-  2: [-100, 0, 50],
-};
-
-const ROTATEY_MAP = {
-  0: [0, -20, -40],
-  1: [20, 0, -20],
-  2: [40, 20, 0],
-};
-
-const BannerPage: FC = () => {
-  const [loaded, setLoaded] = useState(false);
-  const [loading, setLoading] = useState(true);
-  // 背景视频是否报错
-  const [videoError, setVideoError] = useState(false);
-  const [XSZCVideoVisible, setXSZCVideoVisible] = useState(false);
-  const [BWGVideoVisible, setBWGVideoVisible] = useState(false);
-  const [active, setActive] = useState(1);
-  const [moveX, setMoveX] = useState(0);
-  const [bgPos, setBgPos] = useState([...POS_MAP[1]]);
-  const [bgRotateY, setBgRotateY] = useState([...ROTATEY_MAP[1]]);
-  const moving = useRef(false);
-  const isFirstMove = useRef(true);
-  const [visitVisible, setVisitVisible] = useState(false);
-
-  const handleClick = (idx: number) => {
-    if (idx !== active) return;
-
-    switch (idx) {
-      case 0:
-        Taro.navigateTo({
-          url: "/pages/home/index",
-        });
-        break;
-      case 1:
-        setXSZCVideoVisible(true);
-        break;
-      case 2:
-        setBWGVideoVisible(true);
-        break;
-    }
-  };
-
-  const goUnityPage = async () => {
-    const userInfo = baseStore.userInfo;
-
-    if (!userInfo) {
-      await login();
-    }
-
-    if (userInfo.invite === 0) {
-      setVisitVisible(true);
-      setXSZCVideoVisible(false);
-    } else {
-      Taro.navigateTo({
-        url: "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl()),
-      });
-
-      setTimeout(() => {
-        setXSZCVideoVisible(false);
-      }, 1000);
-    }
-  };
-
-  const handleBgLoaded = () => {
-    setLoaded(true);
-    setTimeout(() => {
-      setLoading(false);
-    }, 1000);
-  };
-
-  return (
-    <View className={classNames("page-swiper", { "is-error": videoError })}>
-      <View
-        className={classNames("ld-page", { hide: loaded })}
-        style={{ zIndex: loading ? 999 : -1 }}
-      >
-        <AtIcon
-          className="ld-page__icon"
-          value="loading"
-          color="#589498"
-          size={40}
-        />
-      </View>
-
-      <Image
-        className="page-swiper__title"
-        src={LIST[active].title}
-        mode="heightFix"
-      />
-
-      <Swiper
-        className="banner"
-        current={active}
-        display-multiple-items={1}
-        nextMargin={pxTransform(180)}
-        previousMargin={pxTransform(180)}
-        onTransition={({ detail: { dx } }) => {
-          moving.current = true;
-          setMoveX(dx - (isFirstMove.current ? swipeOffsetX : 0));
-        }}
-        onAnimationFinish={({ detail: { current } }) => {
-          moving.current = false;
-          isFirstMove.current = false;
-          setActive(current);
-          setBgPos([...POS_MAP[current]]);
-          setBgRotateY([...ROTATEY_MAP[current]]);
-        }}
-      >
-        {LIST.map((item, idx) => {
-          const isActive = active === idx;
-          const bgPosition = moving.current
-            ? Math.min(Math.max(bgPos[idx] - moveX * damping, 0), 100)
-            : bgPos[idx];
-          const rotateY = moving.current
-            ? Math.min(
-                Math.max(bgRotateY[idx] + moveX * rotateDamping, -20),
-                20
-              )
-            : bgRotateY[idx];
-
-          return (
-            <SwiperItem key={idx} className="banner-swiper-item">
-              <View
-                className={classNames("banner-item", {
-                  active: isActive,
-                })}
-                style={{
-                  transform: `rotateY(${rotateY}deg)`,
-                }}
-                onClick={handleClick.bind(undefined, idx)}
-              >
-                {!isActive && (
-                  <Image
-                    className="banner-item__label"
-                    src={item.label}
-                    mode="widthFix"
-                    style={{
-                      right: idx <= active ? 0 : "unset",
-                      left: idx > active ? 0 : "unset",
-                    }}
-                  />
-                )}
-                <View
-                  className="banner-item__img"
-                  style={{
-                    backgroundImage: `url(${item.img})`,
-                    backgroundPosition: `${bgPosition}%`,
-                  }}
-                ></View>
-              </View>
-            </SwiperItem>
-          );
-        })}
-      </Swiper>
-
-      <View className="page-swiper-footer">
-        <Image
-          className="page-swiper-footer__logo"
-          src={LogoImg}
-          mode="heightFix"
-        />
-        <View className="page-swiper-footer__text">
-          让慈善之光照进心灵,照亮生活,照耀社会
-        </View>
-        <Image
-          className="page-swiper-footer__tips"
-          src={FootPageImg}
-          mode="heightFix"
-        />
-      </View>
-
-      {!videoError && (
-        <Video
-          className="page-swiper__bg"
-          src="https://houseoss.4dkankan.com/project/wx-csbwg-public/videos/bg%403x.mp4"
-          autoplay
-          muted
-          loop
-          controls={false}
-          enableProgressGesture={false}
-          objectFit="cover"
-          onLoadedMetaData={handleBgLoaded}
-          onError={() => {
-            setVideoError(true);
-            handleBgLoaded();
-          }}
-        />
-      )}
-
-      <VisitCard
-        isOpened={visitVisible}
-        onClose={() => setVisitVisible(false)}
-      />
-
-      {XSZCVideoVisible && (
-        <VideoWrap
-          autoplay
-          controls={false}
-          src="https://houseoss.4dkankan.com/project/wx-csbwg-public/videos/xszc.mp4"
-          onEnded={goUnityPage}
-        />
-      )}
-
-      {BWGVideoVisible && (
-        <VideoWrap
-          autoplay
-          controls={false}
-          objectFit="cover"
-          src="https://houseoss.4dkankan.com/project/wx-csbwg-public/videos/bwg.mp4"
-          onEnded={() => {
-            Taro.navigateTo({
-              url: "/subModule/pages/museum/index",
-            });
-            setTimeout(() => {
-              setBWGVideoVisible(false);
-            }, 1000);
-          }}
-        />
-      )}
-    </View>
-  );
-};
-
-export default BannerPage;
+import { Swiper, SwiperItem, View, Image, Video } from "@tarojs/components";
+import Taro, { FC, pxTransform } from "@tarojs/taro";
+import classNames from "classnames";
+import { useRef, useState } from "react";
+import TitleImg1 from "./images/tab2@2x-min.png";
+import TitleImg2 from "./images/tab1@2x-min.png";
+import TitleImg3 from "./images/tab3@2x-min.png";
+import LabelImg1 from "./images/Group35@2x-min.png";
+import LabelImg2 from "./images/Group37@2x-min.png";
+import LabelImg3 from "./images/Group36@2x-min.png";
+import LogoImg from "./images/LOGO@2x-min.png";
+import FootPageImg from "./images/Group23@2x-min.png";
+import baseStore from "../../store/base";
+import { getSceneUrl, login } from "../../utils";
+import { VisitCard } from "../../pages/home/components/VisitCard";
+import { VideoWrap } from "../../components/Video";
+import { AtIcon } from "taro-ui";
+import "./index.scss";
+
+const LIST = [
+  {
+    title: TitleImg1,
+    img: "https://wuxicharitymuseum.cn/wx-csbwg-public/images/pic-or-2%402x-min.jpg",
+    label: LabelImg1,
+  },
+  {
+    title: TitleImg2,
+    img: "https://wuxicharitymuseum.cn/wx-csbwg-public/images/pic-or%402x-min.jpg",
+    label: LabelImg2,
+  },
+  {
+    title: TitleImg3,
+    img: "https://wuxicharitymuseum.cn/wx-csbwg-public/images/pic-or-3%402x-min.jpg",
+    label: LabelImg3,
+  },
+];
+
+const system = Taro.getSystemInfoSync();
+const getPX = (num: number) => num / (750 / system.windowWidth);
+const swipeOffsetX = getPX(390);
+const damping = 50 / swipeOffsetX;
+const rotateDamping = 20 / swipeOffsetX;
+
+const POS_MAP = {
+  0: [50, 100, 200],
+  1: [0, 50, 100],
+  2: [-100, 0, 50],
+};
+
+const ROTATEY_MAP = {
+  0: [0, -20, -40],
+  1: [20, 0, -20],
+  2: [40, 20, 0],
+};
+
+const BannerPage: FC = () => {
+  const [loaded, setLoaded] = useState(false);
+  const [loading, setLoading] = useState(true);
+  // 背景视频是否报错
+  const [videoError, setVideoError] = useState(false);
+  const [XSZCVideoVisible, setXSZCVideoVisible] = useState(false);
+  const [BWGVideoVisible, setBWGVideoVisible] = useState(false);
+  const [active, setActive] = useState(1);
+  const [moveX, setMoveX] = useState(0);
+  const [bgPos, setBgPos] = useState([...POS_MAP[1]]);
+  const [bgRotateY, setBgRotateY] = useState([...ROTATEY_MAP[1]]);
+  const moving = useRef(false);
+  const isFirstMove = useRef(true);
+  const [visitVisible, setVisitVisible] = useState(false);
+
+  const handleClick = (idx: number) => {
+    if (idx !== active) return;
+
+    switch (idx) {
+      case 0:
+        Taro.navigateTo({
+          url: "/pages/home/index",
+        });
+        break;
+      case 1:
+        setXSZCVideoVisible(true);
+        break;
+      case 2:
+        setBWGVideoVisible(true);
+        break;
+    }
+  };
+
+  const goUnityPage = async () => {
+    const userInfo = baseStore.userInfo;
+
+    if (!userInfo) {
+      await login();
+    }
+
+    if (userInfo.invite === 0) {
+      setVisitVisible(true);
+      setXSZCVideoVisible(false);
+    } else {
+      Taro.navigateTo({
+        url: "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl()),
+      });
+
+      setTimeout(() => {
+        setXSZCVideoVisible(false);
+      }, 1000);
+    }
+  };
+
+  const handleBgLoaded = () => {
+    setLoaded(true);
+    setTimeout(() => {
+      setLoading(false);
+    }, 1000);
+  };
+
+  return (
+    <View className={classNames("page-swiper", { "is-error": videoError })}>
+      <View
+        className={classNames("ld-page", { hide: loaded })}
+        style={{ zIndex: loading ? 999 : -1 }}
+      >
+        <AtIcon
+          className="ld-page__icon"
+          value="loading"
+          color="#589498"
+          size={40}
+        />
+      </View>
+
+      <Image
+        className="page-swiper__title"
+        src={LIST[active].title}
+        mode="heightFix"
+      />
+
+      <Swiper
+        className="banner"
+        current={active}
+        display-multiple-items={1}
+        nextMargin={pxTransform(180)}
+        previousMargin={pxTransform(180)}
+        onTransition={({ detail: { dx } }) => {
+          moving.current = true;
+          setMoveX(dx - (isFirstMove.current ? swipeOffsetX : 0));
+        }}
+        onAnimationFinish={({ detail: { current } }) => {
+          moving.current = false;
+          isFirstMove.current = false;
+          setActive(current);
+          setBgPos([...POS_MAP[current]]);
+          setBgRotateY([...ROTATEY_MAP[current]]);
+        }}
+      >
+        {LIST.map((item, idx) => {
+          const isActive = active === idx;
+          const bgPosition = moving.current
+            ? Math.min(Math.max(bgPos[idx] - moveX * damping, 0), 100)
+            : bgPos[idx];
+          const rotateY = moving.current
+            ? Math.min(
+                Math.max(bgRotateY[idx] + moveX * rotateDamping, -20),
+                20
+              )
+            : bgRotateY[idx];
+
+          return (
+            <SwiperItem key={idx} className="banner-swiper-item">
+              <View
+                className={classNames("banner-item", {
+                  active: isActive,
+                })}
+                style={{
+                  transform: `rotateY(${rotateY}deg)`,
+                }}
+                onClick={handleClick.bind(undefined, idx)}
+              >
+                {!isActive && (
+                  <Image
+                    className="banner-item__label"
+                    src={item.label}
+                    mode="widthFix"
+                    style={{
+                      right: idx <= active ? 0 : "unset",
+                      left: idx > active ? 0 : "unset",
+                    }}
+                  />
+                )}
+                <View
+                  className="banner-item__img"
+                  style={{
+                    backgroundImage: `url(${item.img})`,
+                    backgroundPosition: `${bgPosition}%`,
+                  }}
+                ></View>
+              </View>
+            </SwiperItem>
+          );
+        })}
+      </Swiper>
+
+      <View className="page-swiper-footer">
+        <Image
+          className="page-swiper-footer__logo"
+          src={LogoImg}
+          mode="heightFix"
+        />
+        <View className="page-swiper-footer__text">
+          让慈善之光照进心灵,照亮生活,照耀社会
+        </View>
+        <Image
+          className="page-swiper-footer__tips"
+          src={FootPageImg}
+          mode="heightFix"
+        />
+      </View>
+
+      {!videoError && (
+        <Video
+          className="page-swiper__bg"
+          src="https://wuxicharitymuseum.cn/wx-csbwg-public/videos/bg%403x.mp4"
+          autoplay
+          muted
+          loop
+          controls={false}
+          enableProgressGesture={false}
+          objectFit="cover"
+          onLoadedMetaData={handleBgLoaded}
+          onError={() => {
+            setVideoError(true);
+            handleBgLoaded();
+          }}
+        />
+      )}
+
+      <VisitCard
+        isOpened={visitVisible}
+        onClose={() => setVisitVisible(false)}
+      />
+
+      {XSZCVideoVisible && (
+        <VideoWrap
+          autoplay
+          controls={false}
+          src="https://wuxicharitymuseum.cn/wx-csbwg-public/videos/xszc.mp4"
+          onEnded={goUnityPage}
+        />
+      )}
+
+      {BWGVideoVisible && (
+        <VideoWrap
+          autoplay
+          controls={false}
+          objectFit="cover"
+          src="https://wuxicharitymuseum.cn/wx-csbwg-public/videos/bwg.mp4"
+          onEnded={() => {
+            Taro.navigateTo({
+              url: "/subModule/pages/museum/index",
+            });
+            setTimeout(() => {
+              setBWGVideoVisible(false);
+            }, 1000);
+          }}
+        />
+      )}
+    </View>
+  );
+};
+
+export default BannerPage;

+ 77 - 77
src/pages/home/components/Menu/index.scss

@@ -1,77 +1,77 @@
-.menu {
-  .at-drawer__mask {
-    backdrop-filter: blur(14px);
-    background: rgba(73, 112, 115, 0.4);
-  }
-  .at-drawer__content {
-    padding: 130px 60px;
-    box-sizing: border-box;
-    background: url("../../../../images/pop_menu@2x-min.png") no-repeat center
-      top / cover;
-  }
-  &__close {
-    position: absolute;
-    top: 25px;
-    left: 35px;
-    width: 40px;
-    height: 40px;
-  }
-  &__search {
-    position: absolute;
-    top: 34px;
-    right: 79px;
-    width: 40px;
-    height: 40px;
-  }
-
-  &-item {
-    margin-bottom: 40px;
-    color: #424a4a;
-
-    &.active &__title {
-      color: #589498;
-    }
-    &__title {
-      position: relative;
-      margin-bottom: 20px;
-      display: flex;
-      align-items: center;
-      font-family: "Source Han Serif CN Heavy";
-      font-size: 38px;
-
-      &.is-active::after {
-        content: "";
-        position: absolute;
-        top: -6px;
-        left: 0;
-        width: 329px;
-        height: 77px;
-        background: url("./title_active@2x-min.png") no-repeat center / contain;
-        z-index: -1;
-      }
-      &__icon {
-        margin-right: 46px;
-        width: 48px;
-        height: 48px;
-      }
-      &.reflect {
-        position: absolute;
-        bottom: -20px;
-        left: 0;
-        right: 0;
-        transform: rotatex(180deg) translatey(15px);
-        transform-origin: 50% 100%;
-        mask: linear-gradient(
-          transparent 0%,
-          transparent 30%,
-          rgba(0, 0, 0, 0.5) 100%
-        );
-      }
-    }
-    &__sub {
-      margin-left: 130px;
-      padding: 20px 0;
-      font-size: 31px;
-    }
-  }
-}
+.menu {
+  .at-drawer__mask {
+    backdrop-filter: blur(14px);
+    background: rgba(73, 112, 115, 0.4);
+  }
+  .at-drawer__content {
+    padding: 130px 60px;
+    box-sizing: border-box;
+    background: url("../../../../images/pop_menu@2x-min.png") no-repeat center
+      top / cover;
+  }
+  &__close {
+    position: absolute;
+    top: 25px;
+    left: 35px;
+    width: 40px;
+    height: 40px;
+  }
+  &__search {
+    position: absolute;
+    top: 34px;
+    right: 79px;
+    width: 40px;
+    height: 40px;
+  }
+
+  &-item {
+    margin-bottom: 40px;
+    color: #424a4a;
+
+    &.active &__title {
+      color: #589498;
+    }
+    &__title {
+      position: relative;
+      margin-bottom: 20px;
+      display: flex;
+      align-items: center;
+      font-family: "Source Han Serif CN Heavy";
+      font-size: 38px;
+
+      &.is-active::after {
+        content: "";
+        position: absolute;
+        top: -6px;
+        left: 0;
+        width: 329px;
+        height: 77px;
+        background: url("./title_active@2x-min.png") no-repeat center / contain;
+        z-index: -1;
+      }
+      &__icon {
+        margin-right: 46px;
+        width: 48px;
+        height: 48px;
+      }
+      &.reflect {
+        position: absolute;
+        bottom: -20px;
+        left: 0;
+        right: 0;
+        transform: rotatex(180deg) translatey(15px);
+        transform-origin: 50% 100%;
+        mask: linear-gradient(
+          transparent 0%,
+          transparent 30%,
+          rgba(0, 0, 0, 0.5) 100%
+        );
+      }
+    }
+    &__sub {
+      margin-left: 130px;
+      padding: 20px 0;
+      font-size: 31px;
+    }
+  }
+}

+ 187 - 187
src/pages/home/components/Menu/index.tsx

@@ -1,187 +1,187 @@
-import classNames from "classnames";
-import { Image, View, Text, ScrollView } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import { AtDrawer } from "taro-ui";
-import { AtDrawerProps } from "taro-ui/types/drawer";
-import { observer } from "mobx-react";
-import { useMemo } from "react";
-import { getShopRuleApi } from "../../../../api";
-import { getSceneUrl, login } from "../../../../utils";
-import baseStore from "../../../../store/base";
-import CloseIcon from "../../../../images/icon_back_menu@2x.png";
-// import SearchIcon from "../../../../images/icon_search_black@2x-min.png";
-
-import MapIcon from "../../../../images/icon_map_normal@2x-min.png";
-import ActMapIcon from "../../../../images/icon_map_active@2x-min.png";
-import CityIcon from "../../../../images/icon_build_normal@2x-min.png";
-import ActCityIcon from "../../../../images/icon_build_active@2x-min.png";
-import UserIcon from "../../../../images/icon_user_normal@2x-min.png";
-import ActUserIcon from "../../../../images/icon_user_active@2x-min.png";
-
-import "./index.scss";
-
-export interface MenuProps extends AtDrawerProps {
-  openSearch(): void;
-}
-
-export const Menu: FC<MenuProps> = observer((props) => {
-  const LIST = useMemo(
-    () => [
-      {
-        label: "慈善地图",
-        icon: MapIcon,
-        activeIcon: ActMapIcon,
-        children: [
-          {
-            label: "全部遗存",
-            method: props.openSearch,
-          },
-        ],
-      },
-      {
-        label: "锡善云城",
-        icon: CityIcon,
-        activeIcon: ActCityIcon,
-        children: [
-          {
-            label: "慈善广场",
-            link:
-              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(2)),
-          },
-          {
-            label: "慈善博物馆",
-            link: "/subModule/pages/museum/index",
-          },
-          {
-            label: "慈善云学校",
-            link:
-              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(0)),
-          },
-          {
-            label: "慈善堂",
-            link:
-              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(1)),
-          },
-          {
-            label: "爱心林场",
-            link:
-              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(3)),
-          },
-        ],
-      },
-      {
-        label: "用户中心",
-        icon: UserIcon,
-        activeIcon: ActUserIcon,
-        children: [
-          {
-            label: "个人设定",
-            link:
-              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(4)),
-          },
-          {
-            label: "用户反馈",
-            link: "/subModule/pages/feedback/index",
-            needAuth: true,
-          },
-          {
-            label: "爱心站",
-            method: async () => {
-              const { display, rtf } = await getShopRuleApi();
-              if (display === 0) {
-                Taro.showToast({
-                  title: "商城暂未开启,敬请期待",
-                  icon: "none",
-                  duration: 3000,
-                });
-              } else {
-                baseStore.shopTips = rtf;
-
-                Taro.navigateTo({
-                  url: "/subModule/pages/shopmall/index",
-                });
-              }
-            },
-            needAuth: true,
-          },
-        ],
-      },
-    ],
-    []
-  );
-
-  const handleSubTab = async (subItem) => {
-    if (subItem.needAuth && !baseStore.isLogin) {
-      await login();
-    }
-
-    if (subItem.method) {
-      subItem.method();
-    } else {
-      Taro.navigateTo({
-        url: subItem.link,
-      });
-    }
-  };
-
-  return (
-    <AtDrawer {...props} className="menu" width={Taro.pxTransform(550)}>
-      <Image
-        src={CloseIcon}
-        className="menu__close"
-        onClick={() => props.onClose?.()}
-      />
-      {/* <Image
-        src={SearchIcon}
-        className="menu__search"
-        onClick={() => props.openSearch()}
-      /> */}
-
-      {props.show && (
-        <ScrollView scrollY style={{ height: "100%" }}>
-          {LIST.map((item, idx) => {
-            const isActive = idx === 0;
-
-            return (
-              <View
-                key={item.label}
-                className={classNames("menu-item", { active: isActive })}
-              >
-                <View
-                  className={classNames("menu-item__title", {
-                    "is-active": isActive,
-                  })}
-                >
-                  <Image
-                    className="menu-item__title__icon"
-                    src={isActive ? item.activeIcon : item.icon}
-                  />
-                  <Text>{item.label}</Text>
-
-                  {isActive && (
-                    <View className="menu-item__title reflect">
-                      <Image
-                        className="menu-item__title__icon"
-                        src={isActive ? item.activeIcon : item.icon}
-                      />
-                      <Text>{item.label}</Text>
-                    </View>
-                  )}
-                </View>
-
-                {item.children.map((subItem) => (
-                  <View
-                    className="menu-item__sub"
-                    onClick={handleSubTab.bind(undefined, subItem)}
-                  >
-                    {subItem.label}
-                  </View>
-                ))}
-              </View>
-            );
-          })}
-        </ScrollView>
-      )}
-    </AtDrawer>
-  );
-});
+import classNames from "classnames";
+import { Image, View, Text, ScrollView } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import { AtDrawer } from "taro-ui";
+import { AtDrawerProps } from "taro-ui/types/drawer";
+import { observer } from "mobx-react";
+import { useMemo } from "react";
+import { getShopRuleApi } from "../../../../api";
+import { getSceneUrl, login } from "../../../../utils";
+import baseStore from "../../../../store/base";
+import CloseIcon from "../../../../images/icon_back_menu@2x.png";
+// import SearchIcon from "../../../../images/icon_search_black@2x-min.png";
+
+import MapIcon from "../../../../images/icon_map_normal@2x-min.png";
+import ActMapIcon from "../../../../images/icon_map_active@2x-min.png";
+import CityIcon from "../../../../images/icon_build_normal@2x-min.png";
+import ActCityIcon from "../../../../images/icon_build_active@2x-min.png";
+import UserIcon from "../../../../images/icon_user_normal@2x-min.png";
+import ActUserIcon from "../../../../images/icon_user_active@2x-min.png";
+
+import "./index.scss";
+
+export interface MenuProps extends AtDrawerProps {
+  openSearch(): void;
+}
+
+export const Menu: FC<MenuProps> = observer((props) => {
+  const LIST = useMemo(
+    () => [
+      {
+        label: "慈善地图",
+        icon: MapIcon,
+        activeIcon: ActMapIcon,
+        children: [
+          {
+            label: "全部遗存",
+            method: props.openSearch,
+          },
+        ],
+      },
+      {
+        label: "锡善云城",
+        icon: CityIcon,
+        activeIcon: ActCityIcon,
+        children: [
+          {
+            label: "慈善广场",
+            link:
+              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(2)),
+          },
+          {
+            label: "慈善博物馆",
+            link: "/subModule/pages/museum/index",
+          },
+          {
+            label: "慈善云学校",
+            link:
+              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(0)),
+          },
+          {
+            label: "慈善堂",
+            link:
+              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(1)),
+          },
+          {
+            label: "爱心林场",
+            link:
+              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(3)),
+          },
+        ],
+      },
+      {
+        label: "用户中心",
+        icon: UserIcon,
+        activeIcon: ActUserIcon,
+        children: [
+          {
+            label: "个人设定",
+            link:
+              "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl(4)),
+          },
+          {
+            label: "用户反馈",
+            link: "/subModule/pages/feedback/index",
+            needAuth: true,
+          },
+          {
+            label: "爱心站",
+            method: async () => {
+              const { display, rtf } = await getShopRuleApi();
+              if (display === 0) {
+                Taro.showToast({
+                  title: "商城暂未开启,敬请期待",
+                  icon: "none",
+                  duration: 3000,
+                });
+              } else {
+                baseStore.shopTips = rtf;
+
+                Taro.navigateTo({
+                  url: "/subModule/pages/shopmall/index",
+                });
+              }
+            },
+            needAuth: true,
+          },
+        ],
+      },
+    ],
+    []
+  );
+
+  const handleSubTab = async (subItem) => {
+    if (subItem.needAuth && !baseStore.isLogin) {
+      await login();
+    }
+
+    if (subItem.method) {
+      subItem.method();
+    } else {
+      Taro.navigateTo({
+        url: subItem.link,
+      });
+    }
+  };
+
+  return (
+    <AtDrawer {...props} className="menu" width={Taro.pxTransform(550)}>
+      <Image
+        src={CloseIcon}
+        className="menu__close"
+        onClick={() => props.onClose?.()}
+      />
+      {/* <Image
+        src={SearchIcon}
+        className="menu__search"
+        onClick={() => props.openSearch()}
+      /> */}
+
+      {props.show && (
+        <ScrollView scrollY style={{ height: "100%" }}>
+          {LIST.map((item, idx) => {
+            const isActive = idx === 0;
+
+            return (
+              <View
+                key={item.label}
+                className={classNames("menu-item", { active: isActive })}
+              >
+                <View
+                  className={classNames("menu-item__title", {
+                    "is-active": isActive,
+                  })}
+                >
+                  <Image
+                    className="menu-item__title__icon"
+                    src={isActive ? item.activeIcon : item.icon}
+                  />
+                  <Text>{item.label}</Text>
+
+                  {isActive && (
+                    <View className="menu-item__title reflect">
+                      <Image
+                        className="menu-item__title__icon"
+                        src={isActive ? item.activeIcon : item.icon}
+                      />
+                      <Text>{item.label}</Text>
+                    </View>
+                  )}
+                </View>
+
+                {item.children.map((subItem) => (
+                  <View
+                    className="menu-item__sub"
+                    onClick={handleSubTab.bind(undefined, subItem)}
+                  >
+                    {subItem.label}
+                  </View>
+                ))}
+              </View>
+            );
+          })}
+        </ScrollView>
+      )}
+    </AtDrawer>
+  );
+});

+ 56 - 56
src/pages/home/components/RoundItem/index.scss

@@ -1,56 +1,56 @@
-.round-item {
-  position: absolute;
-  will-change: top, right, transform;
-  width: 300px;
-  height: 300px;
-  transition: transform linear 0.1s;
-
-  &__img {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    opacity: 0.5;
-
-    &.active {
-      mask-image: radial-gradient(
-        circle,
-        #fff calc(var(--radius) - 5%),
-        transparent calc(var(--radius) + 5%)
-      );
-      animation: scene-transition 0.5s ease-out forwards;
-      opacity: 1;
-      z-index: 1;
-    }
-    &.hide {
-      opacity: 0;
-    }
-    &.fade-out {
-      animation: fade-out 0.5s ease-out forwards;
-    }
-  }
-}
-
-@property --radius {
-  syntax: "<percentage>";
-  inherits: true;
-  initial-value: -5%;
-}
-
-@keyframes scene-transition {
-  to {
-    --radius: 105%;
-  }
-}
-
-@keyframes fade-out {
-  0% {
-    opacity: 1;
-    transform: scale(1);
-  }
-  100% {
-    opacity: 0;
-    transform: scale(0);
-  }
-}
+.round-item {
+  position: absolute;
+  will-change: top, right, transform;
+  width: 300px;
+  height: 300px;
+  transition: transform linear 0.1s;
+
+  &__img {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    opacity: 0.5;
+
+    &.active {
+      mask-image: radial-gradient(
+        circle,
+        #fff calc(var(--radius) - 5%),
+        transparent calc(var(--radius) + 5%)
+      );
+      animation: scene-transition 0.5s ease-out forwards;
+      opacity: 1;
+      z-index: 1;
+    }
+    &.hide {
+      opacity: 0;
+    }
+    &.fade-out {
+      animation: fade-out 0.5s ease-out forwards;
+    }
+  }
+}
+
+@property --radius {
+  syntax: "<percentage>";
+  inherits: true;
+  initial-value: -5%;
+}
+
+@keyframes scene-transition {
+  to {
+    --radius: 105%;
+  }
+}
+
+@keyframes fade-out {
+  0% {
+    opacity: 1;
+    transform: scale(1);
+  }
+  100% {
+    opacity: 0;
+    transform: scale(0);
+  }
+}

+ 114 - 114
src/pages/home/components/RoundItem/index.tsx

@@ -1,114 +1,114 @@
-import { View, Image } from "@tarojs/components";
-import { FC } from "@tarojs/taro";
-import {
-  CSSProperties,
-  memo,
-  useEffect,
-  useMemo,
-  useRef,
-  useState,
-} from "react";
-import { requestAnimationFrame } from "@tarojs/runtime";
-import classNames from "classnames";
-import "./index.scss";
-
-export interface RoundItemProps {
-  id: number;
-  angle: number;
-  bigR: number;
-  minR: number;
-  isActive: boolean;
-  isNear: boolean;
-  onActLoaded: () => void;
-  onClick: () => void;
-}
-
-const IMG_BASE_URL = "https://houseoss.4dkankan.com/project/wx-csbwg-public";
-
-export const RoundItem: FC<RoundItemProps> = memo(
-  ({ id, angle, bigR, minR, isActive, isNear, onClick, onActLoaded }) => {
-    const [curAngle, setCurAngle] = useState(angle);
-    const [actImgLoaded, setActImgLoaded] = useState(false);
-    const _angle = useRef(angle);
-    const inited = useRef(false);
-    const timer = useRef<number | NodeJS.Timeout>();
-    const moving = useRef(false);
-
-    const style = useMemo<CSSProperties>(() => {
-      const x = bigR * (1 - Math.cos((curAngle * Math.PI) / 180)) - minR;
-      const y = bigR * (1 - Math.sin((curAngle * Math.PI) / 180)) - minR;
-      let scale = 0.5;
-
-      if (isActive) {
-        scale = 1;
-      } else if (isNear) {
-        scale = 0.6;
-      }
-
-      return {
-        top: y + "px",
-        right: x + "px",
-        transform: `scale(${scale})`,
-      };
-    }, [curAngle, bigR, isActive, isNear]);
-
-    useEffect(() => {
-      if (inited.current) {
-        moving.current = true;
-        setActImgLoaded(false);
-        move();
-      }
-
-      inited.current = true;
-    }, [angle]);
-
-    const move = () => {
-      const offset = _angle.current - angle;
-      _angle.current = _angle.current + (offset > 0 ? -1 : 1);
-
-      setCurAngle(_angle.current);
-      timer.current = requestAnimationFrame(move);
-
-      if (offset === 0) {
-        cancelAnimationFrame(timer.current as number);
-        _angle.current = angle;
-        moving.current = false;
-      }
-    };
-
-    return (
-      <View
-        className={classNames("round-item", {
-          "is-active": isActive,
-          "is-near": isNear,
-        })}
-        style={style}
-        onClick={onClick}
-      >
-        <Image
-          className={classNames("round-item__img", {
-            "fade-out": isActive,
-          })}
-          mode="aspectFit"
-          src={`${IMG_BASE_URL}/line-sights/${id}.png`}
-        />
-
-        {isActive && (
-          <Image
-            className={classNames(
-              "round-item__img",
-              actImgLoaded ? "active" : "hide"
-            )}
-            mode="aspectFit"
-            src={`${IMG_BASE_URL}/sights/${id}.png`}
-            onLoad={() => {
-              setActImgLoaded(true);
-              onActLoaded();
-            }}
-            onError={onActLoaded}
-          />
-        )}
-      </View>
-    );
-  }
-);
+import { View, Image } from "@tarojs/components";
+import { FC } from "@tarojs/taro";
+import {
+  CSSProperties,
+  memo,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { requestAnimationFrame } from "@tarojs/runtime";
+import classNames from "classnames";
+import "./index.scss";
+
+export interface RoundItemProps {
+  id: number;
+  angle: number;
+  bigR: number;
+  minR: number;
+  isActive: boolean;
+  isNear: boolean;
+  onActLoaded: () => void;
+  onClick: () => void;
+}
+
+const IMG_BASE_URL = "https://wuxicharitymuseum.cn/wx-csbwg-public";
+
+export const RoundItem: FC<RoundItemProps> = memo(
+  ({ id, angle, bigR, minR, isActive, isNear, onClick, onActLoaded }) => {
+    const [curAngle, setCurAngle] = useState(angle);
+    const [actImgLoaded, setActImgLoaded] = useState(false);
+    const _angle = useRef(angle);
+    const inited = useRef(false);
+    const timer = useRef<number | NodeJS.Timeout>();
+    const moving = useRef(false);
+
+    const style = useMemo<CSSProperties>(() => {
+      const x = bigR * (1 - Math.cos((curAngle * Math.PI) / 180)) - minR;
+      const y = bigR * (1 - Math.sin((curAngle * Math.PI) / 180)) - minR;
+      let scale = 0.5;
+
+      if (isActive) {
+        scale = 1;
+      } else if (isNear) {
+        scale = 0.6;
+      }
+
+      return {
+        top: y + "px",
+        right: x + "px",
+        transform: `scale(${scale})`,
+      };
+    }, [curAngle, bigR, isActive, isNear]);
+
+    useEffect(() => {
+      if (inited.current) {
+        moving.current = true;
+        setActImgLoaded(false);
+        move();
+      }
+
+      inited.current = true;
+    }, [angle]);
+
+    const move = () => {
+      const offset = _angle.current - angle;
+      _angle.current = _angle.current + (offset > 0 ? -1 : 1);
+
+      setCurAngle(_angle.current);
+      timer.current = requestAnimationFrame(move);
+
+      if (offset === 0) {
+        cancelAnimationFrame(timer.current as number);
+        _angle.current = angle;
+        moving.current = false;
+      }
+    };
+
+    return (
+      <View
+        className={classNames("round-item", {
+          "is-active": isActive,
+          "is-near": isNear,
+        })}
+        style={style}
+        onClick={onClick}
+      >
+        <Image
+          className={classNames("round-item__img", {
+            "fade-out": isActive,
+          })}
+          mode="aspectFit"
+          src={`${IMG_BASE_URL}/line-sights/${id}.png`}
+        />
+
+        {isActive && (
+          <Image
+            className={classNames(
+              "round-item__img",
+              actImgLoaded ? "active" : "hide"
+            )}
+            mode="aspectFit"
+            src={`${IMG_BASE_URL}/sights/${id}.png`}
+            onLoad={() => {
+              setActImgLoaded(true);
+              onActLoaded();
+            }}
+            onError={onActLoaded}
+          />
+        )}
+      </View>
+    );
+  }
+);

+ 141 - 141
src/pages/home/components/SearchLayout/index.scss

@@ -1,141 +1,141 @@
-.search-layout {
-  .at-float-layout__overlay {
-    background: rgba(0, 0, 0, 0.65);
-    backdrop-filter: blur(30px);
-  }
-  .at-float-layout__container {
-    max-height: 100vh;
-    height: 100vh;
-    background-color: transparent;
-  }
-  .at-input__icon {
-    margin-right: 20px;
-
-    &-close {
-      font-size: 44px;
-    }
-  }
-  .layout-body {
-    padding: 25px 40px;
-    max-height: unset;
-    min-height: unset;
-    height: 100%;
-    box-sizing: border-box;
-  }
-  .layout-body__content {
-    display: flex;
-    flex-direction: column;
-    max-height: unset;
-    min-height: unset;
-    height: 100%;
-  }
-}
-
-.search-input {
-  display: flex;
-  align-items: center;
-  height: 77px;
-
-  &__input {
-    flex: 1;
-    margin: 0;
-    padding: 6px 0 6px 28px;
-    height: inherit;
-    box-sizing: border-box;
-    background: unset;
-    border: 2px solid white;
-    border-radius: 50px;
-    overflow: hidden;
-
-    .at-input__input,
-    .at-input__container {
-      height: 100%;
-    }
-    .at-input__input {
-      font-size: 24px;
-      color: white;
-    }
-    &::after {
-      display: none;
-    }
-  }
-  &__cancel {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    width: 110px;
-    height: inherit;
-    font-size: 31px;
-    color: white;
-  }
-}
-
-.search-tab {
-  flex: 1;
-  margin-top: 6px;
-  height: unset;
-
-  .at-tabs__header {
-    background-color: unset;
-  }
-  .at-tabs__item {
-    font-size: 24px;
-    color: rgba(255, 255, 255, 0.6);
-
-    &--active {
-      color: white;
-      font-size: 31px;
-      font-family: "SourceHanSansCN-Bold";
-    }
-  }
-  .at-tabs__item-underline {
-    left: 50%;
-    bottom: 26px;
-    margin-left: -35px;
-    width: 70px;
-    height: 8px;
-    border-radius: 50px;
-    background-color: #589498;
-    z-index: -1;
-  }
-  .at-tabs__underline {
-    background-color: rgba(255, 255, 255, 0.2);
-  }
-
-  &__scroll {
-    height: calc(100vh - 50px - 77px - 92px - 6px);
-    padding-bottom: constant(safe-area-inset-bottom);
-    padding-bottom: env(safe-area-inset-bottom);
-    box-sizing: border-box;
-  }
-  &__item {
-    display: flex;
-    align-items: flex-start;
-    padding: 20px 23px;
-
-    &-inner {
-      flex: 1;
-      width: 0;
-      color: rgba(255, 255, 255, 0.6);
-      font-size: 24px;
-
-      &__label {
-        color: white;
-        font-size: 31px;
-        font-family: "SourceHanSansCN-Bold";
-      }
-    }
-    &__more {
-      margin-left: 20px;
-      width: 48px;
-      height: 48px;
-      background: url("../../../../images/icon_enter@2x-min.png") no-repeat
-        center / contain;
-    }
-  }
-  &__nomore {
-    text-align: center;
-    color: white;
-    line-height: 300px;
-  }
-}
+.search-layout {
+  .at-float-layout__overlay {
+    background: rgba(0, 0, 0, 0.65);
+    backdrop-filter: blur(30px);
+  }
+  .at-float-layout__container {
+    max-height: 100vh;
+    height: 100vh;
+    background-color: transparent;
+  }
+  .at-input__icon {
+    margin-right: 20px;
+
+    &-close {
+      font-size: 44px;
+    }
+  }
+  .layout-body {
+    padding: 25px 40px;
+    max-height: unset;
+    min-height: unset;
+    height: 100%;
+    box-sizing: border-box;
+  }
+  .layout-body__content {
+    display: flex;
+    flex-direction: column;
+    max-height: unset;
+    min-height: unset;
+    height: 100%;
+  }
+}
+
+.search-input {
+  display: flex;
+  align-items: center;
+  height: 77px;
+
+  &__input {
+    flex: 1;
+    margin: 0;
+    padding: 6px 0 6px 28px;
+    height: inherit;
+    box-sizing: border-box;
+    background: unset;
+    border: 2px solid white;
+    border-radius: 50px;
+    overflow: hidden;
+
+    .at-input__input,
+    .at-input__container {
+      height: 100%;
+    }
+    .at-input__input {
+      font-size: 24px;
+      color: white;
+    }
+    &::after {
+      display: none;
+    }
+  }
+  &__cancel {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 110px;
+    height: inherit;
+    font-size: 31px;
+    color: white;
+  }
+}
+
+.search-tab {
+  flex: 1;
+  margin-top: 6px;
+  height: unset;
+
+  .at-tabs__header {
+    background-color: unset;
+  }
+  .at-tabs__item {
+    font-size: 24px;
+    color: rgba(255, 255, 255, 0.6);
+
+    &--active {
+      color: white;
+      font-size: 31px;
+      font-family: "SourceHanSansCN-Bold";
+    }
+  }
+  .at-tabs__item-underline {
+    left: 50%;
+    bottom: 26px;
+    margin-left: -35px;
+    width: 70px;
+    height: 8px;
+    border-radius: 50px;
+    background-color: #589498;
+    z-index: -1;
+  }
+  .at-tabs__underline {
+    background-color: rgba(255, 255, 255, 0.2);
+  }
+
+  &__scroll {
+    height: calc(100vh - 50px - 77px - 92px - 6px);
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+    box-sizing: border-box;
+  }
+  &__item {
+    display: flex;
+    align-items: flex-start;
+    padding: 20px 23px;
+
+    &-inner {
+      flex: 1;
+      width: 0;
+      color: rgba(255, 255, 255, 0.6);
+      font-size: 24px;
+
+      &__label {
+        color: white;
+        font-size: 31px;
+        font-family: "SourceHanSansCN-Bold";
+      }
+    }
+    &__more {
+      margin-left: 20px;
+      width: 48px;
+      height: 48px;
+      background: url("../../../../images/icon_enter@2x-min.png") no-repeat
+        center / contain;
+    }
+  }
+  &__nomore {
+    text-align: center;
+    color: white;
+    line-height: 300px;
+  }
+}

+ 132 - 132
src/pages/home/components/SearchLayout/index.tsx

@@ -1,132 +1,132 @@
-import { useCallback, useEffect, useState } from "react";
-import { FC } from "@tarojs/taro";
-import { AtFloatLayout, AtInput, AtTabs, AtTabsPane } from "taro-ui";
-import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
-import { ScrollView, View } from "@tarojs/components";
-import { CITY_LIST, SIGHT_LIST } from "../../constants";
-import { debounce } from "../../../../utils";
-import { getSignListApi } from "../../../../api";
-import "./index.scss";
-
-export interface SearchLayoutProps extends AtFloatLayoutProps {
-  openDetail(id: (typeof SIGHT_LIST)[0]): void;
-}
-
-export const SearchLayout: FC<SearchLayoutProps> = (props) => {
-  const [curTab, setCurTab] = useState(0);
-  const [keyword, setKeyword] = useState("");
-  const [input, setInput] = useState("");
-  const [list, setList] = useState<any>({});
-  const [loading, setLoading] = useState(false);
-
-  useEffect(() => {
-    if (props.isOpened) {
-      getSignList();
-    }
-  }, [props.isOpened]);
-
-  const getSignList = async (idx?: number) => {
-    const _idx = idx ?? curTab;
-    if (list[_idx]) return;
-
-    try {
-      setLoading(true);
-      const data = await getSignListApi({
-        region: _idx === 0 ? "" : CITY_LIST[_idx].title,
-        type: "scene",
-      });
-      setList({
-        ...list,
-        [_idx]: data,
-      });
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  const handleTabClick = (idx: number) => {
-    setCurTab(idx);
-    getSignList(idx);
-  };
-
-  const debounceSearch = useCallback(
-    debounce((v: string) => {
-      setKeyword(v);
-    }, 1000),
-    []
-  );
-
-  const fakeSearch = (v: string) => {
-    return v.match(keyword) !== null;
-  };
-
-  return (
-    <AtFloatLayout className="search-layout" {...props}>
-      {props.isOpened && (
-        <>
-          <View className="search-input">
-            <AtInput
-              clear
-              className="search-input__input"
-              name="keyword"
-              value={input}
-              confirm-type="search"
-              placeholder="请输入要搜索的内容..."
-              onChange={(v) => {
-                setInput(v as string);
-                debounceSearch(v as string);
-              }}
-            />
-
-            <View className="search-input__cancel" onClick={props.onClose}>
-              取消
-            </View>
-          </View>
-
-          <AtTabs
-            current={curTab}
-            scroll
-            className="search-tab"
-            tabList={CITY_LIST}
-            onClick={handleTabClick}
-          >
-            {CITY_LIST.map((item, idx) => {
-              const _list = (list[idx] || []).filter((i) =>
-                keyword ? fakeSearch(i.name) : true
-              );
-
-              return (
-                <AtTabsPane current={curTab} index={item.id}>
-                  <ScrollView scrollY className="search-tab__scroll">
-                    {_list.length
-                      ? _list.map((subItem) => (
-                          <View
-                            key={subItem.id}
-                            className="search-tab__item"
-                            onClick={props.openDetail.bind(undefined, subItem)}
-                          >
-                            <View className="search-tab__item-inner">
-                              <View className="search-tab__item-inner__label">
-                                {subItem.name}
-                              </View>
-                              <View className="limit-line">
-                                {subItem.description.slice(0, 50)}
-                              </View>
-                            </View>
-
-                            <View className="search-tab__item__more" />
-                          </View>
-                        ))
-                      : !loading && (
-                          <View className="search-tab__nomore">暂无内容</View>
-                        )}
-                  </ScrollView>
-                </AtTabsPane>
-              );
-            })}
-          </AtTabs>
-        </>
-      )}
-    </AtFloatLayout>
-  );
-};
+import { useCallback, useEffect, useState } from "react";
+import { FC } from "@tarojs/taro";
+import { AtFloatLayout, AtInput, AtTabs, AtTabsPane } from "taro-ui";
+import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
+import { ScrollView, View } from "@tarojs/components";
+import { CITY_LIST, SIGHT_LIST } from "../../constants";
+import { debounce } from "../../../../utils";
+import { getSignListApi } from "../../../../api";
+import "./index.scss";
+
+export interface SearchLayoutProps extends AtFloatLayoutProps {
+  openDetail(id: (typeof SIGHT_LIST)[0]): void;
+}
+
+export const SearchLayout: FC<SearchLayoutProps> = (props) => {
+  const [curTab, setCurTab] = useState(0);
+  const [keyword, setKeyword] = useState("");
+  const [input, setInput] = useState("");
+  const [list, setList] = useState<any>({});
+  const [loading, setLoading] = useState(false);
+
+  useEffect(() => {
+    if (props.isOpened) {
+      getSignList();
+    }
+  }, [props.isOpened]);
+
+  const getSignList = async (idx?: number) => {
+    const _idx = idx ?? curTab;
+    if (list[_idx]) return;
+
+    try {
+      setLoading(true);
+      const data = await getSignListApi({
+        region: _idx === 0 ? "" : CITY_LIST[_idx].title,
+        type: "scene",
+      });
+      setList({
+        ...list,
+        [_idx]: data,
+      });
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleTabClick = (idx: number) => {
+    setCurTab(idx);
+    getSignList(idx);
+  };
+
+  const debounceSearch = useCallback(
+    debounce((v: string) => {
+      setKeyword(v);
+    }, 1000),
+    []
+  );
+
+  const fakeSearch = (v: string) => {
+    return v.match(keyword) !== null;
+  };
+
+  return (
+    <AtFloatLayout className="search-layout" {...props}>
+      {props.isOpened && (
+        <>
+          <View className="search-input">
+            <AtInput
+              clear
+              className="search-input__input"
+              name="keyword"
+              value={input}
+              confirm-type="search"
+              placeholder="请输入要搜索的内容..."
+              onChange={(v) => {
+                setInput(v as string);
+                debounceSearch(v as string);
+              }}
+            />
+
+            <View className="search-input__cancel" onClick={props.onClose}>
+              取消
+            </View>
+          </View>
+
+          <AtTabs
+            current={curTab}
+            scroll
+            className="search-tab"
+            tabList={CITY_LIST}
+            onClick={handleTabClick}
+          >
+            {CITY_LIST.map((item, idx) => {
+              const _list = (list[idx] || []).filter((i) =>
+                keyword ? fakeSearch(i.name) : true
+              );
+
+              return (
+                <AtTabsPane current={curTab} index={item.id}>
+                  <ScrollView scrollY className="search-tab__scroll">
+                    {_list.length
+                      ? _list.map((subItem) => (
+                          <View
+                            key={subItem.id}
+                            className="search-tab__item"
+                            onClick={props.openDetail.bind(undefined, subItem)}
+                          >
+                            <View className="search-tab__item-inner">
+                              <View className="search-tab__item-inner__label">
+                                {subItem.name}
+                              </View>
+                              <View className="limit-line">
+                                {subItem.description.slice(0, 50)}
+                              </View>
+                            </View>
+
+                            <View className="search-tab__item__more" />
+                          </View>
+                        ))
+                      : !loading && (
+                          <View className="search-tab__nomore">暂无内容</View>
+                        )}
+                  </ScrollView>
+                </AtTabsPane>
+              );
+            })}
+          </AtTabs>
+        </>
+      )}
+    </AtFloatLayout>
+  );
+};

+ 121 - 121
src/pages/home/components/SightDetailLayout/index.scss

@@ -1,121 +1,121 @@
-.sight-layout {
-  .at-float-layout__overlay {
-    background: rgba(0, 0, 0, 0.65);
-    backdrop-filter: blur(30px);
-  }
-  .at-float-layout__container {
-    max-height: 100vh;
-    height: 100vh;
-    background-color: transparent;
-  }
-  .layout-body {
-    padding: 0;
-    // padding-bottom: constant(safe-area-inset-bottom);
-    // padding-bottom: env(safe-area-inset-bottom);
-    max-height: unset;
-    min-height: unset;
-    height: 100%;
-    box-sizing: border-box;
-  }
-  .layout-body__content {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    max-height: unset;
-    min-height: unset;
-    height: 100%;
-
-    &::after {
-      content: "";
-      position: absolute;
-      left: 0;
-      bottom: 0;
-      width: 685px;
-      height: 722px;
-      background: url("../../../../images/logo@2x-min.png") no-repeat center /
-        contain;
-      z-index: -1;
-    }
-  }
-
-  &-wrap {
-    position: relative;
-    padding: 85px 55px 210px;
-    height: 100%;
-    box-sizing: border-box;
-    z-index: 2;
-  }
-  &-head {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    margin-bottom: 35px;
-    padding-bottom: 45px;
-    color: rgba(255, 255, 255, 0.6);
-    font-size: 24px;
-
-    &__label {
-      letter-spacing: 7px;
-      font-size: 46px;
-      color: white;
-      line-height: 54px;
-      font-family: "Source Han Serif CN Heavy";
-    }
-    &__bd {
-      position: absolute;
-      left: 50%;
-      bottom: 0;
-      width: 100%;
-      transform: translateX(-50%);
-    }
-  }
-
-  &__img {
-    width: 100%;
-    height: 420px;
-  }
-
-  &-main {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    padding: 0 25px;
-  }
-
-  &-toolbar {
-    display: flex;
-    justify-content: center;
-    margin-top: 50px;
-    gap: 36px;
-
-    &__btn {
-      width: 220px;
-      height: 72px;
-      line-height: 72px;
-      font-size: 31px;
-      color: white;
-      text-align: center;
-      background: url("../../../../images/btn_m_white@2x-min.png") no-repeat
-        center / contain;
-    }
-  }
-
-  &__text {
-    margin-top: 40px;
-    color: white;
-    font-size: 31px;
-    line-height: 46px;
-    text-indent: 2em;
-  }
-
-  &__close {
-    position: absolute;
-    left: 50%;
-    bottom: 100px;
-    width: 80px;
-    height: 80px;
-    transform: translateX(-50%);
-    z-index: 2;
-  }
-}
+.sight-layout {
+  .at-float-layout__overlay {
+    background: rgba(0, 0, 0, 0.65);
+    backdrop-filter: blur(30px);
+  }
+  .at-float-layout__container {
+    max-height: 100vh;
+    height: 100vh;
+    background-color: transparent;
+  }
+  .layout-body {
+    padding: 0;
+    // padding-bottom: constant(safe-area-inset-bottom);
+    // padding-bottom: env(safe-area-inset-bottom);
+    max-height: unset;
+    min-height: unset;
+    height: 100%;
+    box-sizing: border-box;
+  }
+  .layout-body__content {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    max-height: unset;
+    min-height: unset;
+    height: 100%;
+
+    &::after {
+      content: "";
+      position: absolute;
+      left: 0;
+      bottom: 0;
+      width: 685px;
+      height: 722px;
+      background: url("../../../../images/logo@2x-min.png") no-repeat center /
+        contain;
+      z-index: -1;
+    }
+  }
+
+  &-wrap {
+    position: relative;
+    padding: 85px 55px 210px;
+    height: 100%;
+    box-sizing: border-box;
+    z-index: 2;
+  }
+  &-head {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    margin-bottom: 35px;
+    padding-bottom: 45px;
+    color: rgba(255, 255, 255, 0.6);
+    font-size: 24px;
+
+    &__label {
+      letter-spacing: 7px;
+      font-size: 46px;
+      color: white;
+      line-height: 54px;
+      font-family: "Source Han Serif CN Heavy";
+    }
+    &__bd {
+      position: absolute;
+      left: 50%;
+      bottom: 0;
+      width: 100%;
+      transform: translateX(-50%);
+    }
+  }
+
+  &__img {
+    width: 100%;
+    height: 420px;
+  }
+
+  &-main {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 0 25px;
+  }
+
+  &-toolbar {
+    display: flex;
+    justify-content: center;
+    margin-top: 50px;
+    gap: 36px;
+
+    &__btn {
+      width: 220px;
+      height: 72px;
+      line-height: 72px;
+      font-size: 31px;
+      color: white;
+      text-align: center;
+      background: url("../../../../images/btn_m_white@2x-min.png") no-repeat
+        center / contain;
+    }
+  }
+
+  &__text {
+    margin-top: 40px;
+    color: white;
+    font-size: 31px;
+    line-height: 46px;
+    text-indent: 2em;
+  }
+
+  &__close {
+    position: absolute;
+    left: 50%;
+    bottom: 100px;
+    width: 80px;
+    height: 80px;
+    transform: translateX(-50%);
+    z-index: 2;
+  }
+}

+ 111 - 111
src/pages/home/components/SightDetailLayout/index.tsx

@@ -1,111 +1,111 @@
-import { useEffect, useMemo } from "react";
-import Taro, { FC } from "@tarojs/taro";
-import { AtFloatLayout } from "taro-ui";
-import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
-import { Image, ScrollView, View } from "@tarojs/components";
-import BorderImg from "../../../../images/line01@2x.png";
-import CloseIcon from "../../../../images/icon_back@2x-min.png";
-import { SIGHT_LIST } from "../../constants";
-import { addVisitApi } from "../../../../api";
-import { getBaseURL } from "@dage/service";
-import "./index.scss";
-
-const baseUrl = getBaseURL();
-
-export interface SightDetailLayoutProps extends AtFloatLayoutProps {
-  item: (typeof SIGHT_LIST)[0];
-}
-
-export const SightDetailLayout: FC<SightDetailLayoutProps> = (props) => {
-  const realUrl = useMemo(
-    () =>
-      props.item.imgUrl ||
-      // @ts-ignore
-      `${baseUrl}${props.item.thumb}`,
-    [props.item]
-  );
-
-  useEffect(() => {
-    if (props.isOpened) {
-      addVisitApi({
-        device: "wx",
-        id: props.item.id,
-      });
-    }
-  }, [props.isOpened]);
-
-  const handlePreview = () => {
-    Taro.previewImage({
-      current: realUrl,
-      urls: [realUrl],
-    });
-  };
-
-  return (
-    <AtFloatLayout className="sight-layout" {...props}>
-      <ScrollView scrollY className="sight-layout-wrap">
-        <View className="sight-layout-head">
-          <View className="sight-layout-head__label">
-            {/* @ts-ignore */}
-            {props.item.title || props.item.name}
-          </View>
-          {/* @ts-ignore */}
-          <View>{props.item.region || props.item.address}</View>
-
-          <Image
-            src={BorderImg}
-            mode="widthFix"
-            className="sight-layout-head__bd"
-          />
-        </View>
-
-        <View className="sight-layout-main">
-          <Image
-            src={realUrl}
-            className="sight-layout__img"
-            mode="aspectFill"
-            onClick={handlePreview}
-          />
-
-          {props.item.link && (
-            <View className="sight-layout-toolbar">
-              {/* <View className="sight-layout-toolbar__btn">全景浏览</View> */}
-
-              <View
-                className="sight-layout-toolbar__btn"
-                onClick={() => {
-                  Taro.navigateTo({
-                    url: `/subModule/pages/iframe/index?url=${encodeURIComponent(
-                      props.item.link
-                    )}`,
-                  });
-                }}
-              >
-                场景漫游
-              </View>
-            </View>
-          )}
-
-          {Array.isArray(props.item.content) ? (
-            props.item.content.map((i, idx) => (
-              <View className="sight-layout__text" key={idx}>
-                {i}
-              </View>
-            ))
-          ) : (
-            <View className="sight-layout__text">
-              {/* @ts-ignore */}
-              {props.item.content || props.item.description}
-            </View>
-          )}
-        </View>
-      </ScrollView>
-
-      <Image
-        className="sight-layout__close"
-        src={CloseIcon}
-        onClick={props.onClose}
-      />
-    </AtFloatLayout>
-  );
-};
+import { useEffect, useMemo } from "react";
+import Taro, { FC } from "@tarojs/taro";
+import { AtFloatLayout } from "taro-ui";
+import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
+import { Image, ScrollView, View } from "@tarojs/components";
+import BorderImg from "../../../../images/line01@2x.png";
+import CloseIcon from "../../../../images/icon_back@2x-min.png";
+import { SIGHT_LIST } from "../../constants";
+import { addVisitApi } from "../../../../api";
+import { getBaseURL } from "@dage/service";
+import "./index.scss";
+
+const baseUrl = getBaseURL();
+
+export interface SightDetailLayoutProps extends AtFloatLayoutProps {
+  item: (typeof SIGHT_LIST)[0];
+}
+
+export const SightDetailLayout: FC<SightDetailLayoutProps> = (props) => {
+  const realUrl = useMemo(
+    () =>
+      props.item.imgUrl ||
+      // @ts-ignore
+      `${baseUrl}${props.item.thumb}`,
+    [props.item]
+  );
+
+  useEffect(() => {
+    if (props.isOpened) {
+      addVisitApi({
+        device: "wx",
+        id: props.item.id,
+      });
+    }
+  }, [props.isOpened]);
+
+  const handlePreview = () => {
+    Taro.previewImage({
+      current: realUrl,
+      urls: [realUrl],
+    });
+  };
+
+  return (
+    <AtFloatLayout className="sight-layout" {...props}>
+      <ScrollView scrollY className="sight-layout-wrap">
+        <View className="sight-layout-head">
+          <View className="sight-layout-head__label">
+            {/* @ts-ignore */}
+            {props.item.title || props.item.name}
+          </View>
+          {/* @ts-ignore */}
+          <View>{props.item.region || props.item.address}</View>
+
+          <Image
+            src={BorderImg}
+            mode="widthFix"
+            className="sight-layout-head__bd"
+          />
+        </View>
+
+        <View className="sight-layout-main">
+          <Image
+            src={realUrl}
+            className="sight-layout__img"
+            mode="aspectFill"
+            onClick={handlePreview}
+          />
+
+          {props.item.link && (
+            <View className="sight-layout-toolbar">
+              {/* <View className="sight-layout-toolbar__btn">全景浏览</View> */}
+
+              <View
+                className="sight-layout-toolbar__btn"
+                onClick={() => {
+                  Taro.navigateTo({
+                    url: `/subModule/pages/iframe/index?url=${encodeURIComponent(
+                      props.item.link
+                    )}`,
+                  });
+                }}
+              >
+                场景漫游
+              </View>
+            </View>
+          )}
+
+          {Array.isArray(props.item.content) ? (
+            props.item.content.map((i, idx) => (
+              <View className="sight-layout__text" key={idx}>
+                {i}
+              </View>
+            ))
+          ) : (
+            <View className="sight-layout__text">
+              {/* @ts-ignore */}
+              {props.item.content || props.item.description}
+            </View>
+          )}
+        </View>
+      </ScrollView>
+
+      <Image
+        className="sight-layout__close"
+        src={CloseIcon}
+        onClick={props.onClose}
+      />
+    </AtFloatLayout>
+  );
+};

+ 17 - 17
src/pages/home/components/Swiper/index.scss

@@ -1,17 +1,17 @@
-.sights {
-  position: relative;
-  left: 50%;
-  margin-top: -280px;
-  width: 1000px;
-  height: 1000px;
-  transform: translateX(-50%);
-
-  &__panel {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: -130px;
-    height: 300px;
-    z-index: 1;
-  }
-}
+.sights {
+  position: relative;
+  left: 50%;
+  margin-top: -280px;
+  width: 1000px;
+  height: 1000px;
+  transform: translateX(-50%);
+
+  &__panel {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: -130px;
+    height: 300px;
+    z-index: 1;
+  }
+}

+ 179 - 179
src/pages/home/components/Swiper/index.tsx

@@ -1,179 +1,179 @@
-import { ITouchEvent, View } from "@tarojs/components";
-import { RoundItem } from "../RoundItem";
-import Taro from "@tarojs/taro";
-import {
-  forwardRef,
-  useCallback,
-  useImperativeHandle,
-  useMemo,
-  useRef,
-  useState,
-} from "react";
-import { useInterval } from "../../../../hooks/useInterval";
-import "./index.scss";
-import { SORT_MAP_ID } from "../../constants";
-
-export interface SwiperProps {
-  curSwiperItem: number;
-  handleDetail(id: number): void;
-  setCurSwiperItem(num: number): void;
-}
-
-export interface SwiperMethods {
-  toNext(): void;
-  toPrev(): void;
-  setIsRunning(v: boolean): void;
-}
-
-const SIGHT_COUNT = 26;
-const Big_R = (500 * Taro.getSystemInfoSync().windowWidth) / 750;
-const MIN_R = (150 * Taro.getSystemInfoSync().windowWidth) / 750;
-
-const getItemStack = () => {
-  const stack: number[] = [];
-  for (let i = 0; i <= 13; i++) {
-    stack.push(i);
-  }
-  return stack;
-};
-
-const ANGLE = 360 / 12;
-
-export const Swiper = forwardRef<SwiperMethods, SwiperProps>(
-  ({ curSwiperItem, setCurSwiperItem, handleDetail }, ref) => {
-    const [itemStack, setItemStack] = useState(getItemStack());
-    const touchStartX = useRef(0);
-    const moving = useRef(false);
-    const [isRunning, setIsRunning] = useState(false);
-    const loaded = useRef(false);
-
-    const getNextItem = (curIndex: number, length: number) => {
-      return (curIndex + 1) % length;
-    };
-    const getPreviousItem = (curIndex: number, length: number) => {
-      return (curIndex - 1 + length) % length;
-    };
-    const getIndicesRelative = (curIndex: number, length: number) => {
-      const nextIndex = getNextItem(curIndex, length);
-      const nextThreeIndices = [nextIndex];
-      for (let i = 1; i < 1; i++) {
-        nextThreeIndices.push(getNextItem(nextThreeIndices[i - 1], length));
-      }
-
-      const previousIndex = getPreviousItem(curIndex, length);
-      const previousThreeIndices = [previousIndex];
-      for (let i = 1; i <= 2; i++) {
-        previousThreeIndices.push(
-          getPreviousItem(previousThreeIndices[i - 1], length)
-        );
-      }
-
-      return [nextThreeIndices, previousThreeIndices];
-    };
-
-    const handleTouchStart = (e: ITouchEvent) => {
-      setIsRunning(false);
-      touchStartX.current = e.changedTouches[0].clientX;
-    };
-    const handleTouchEnd = (e: ITouchEvent) => {
-      const touchEndX = e.changedTouches[0].clientX;
-      const deltaX = touchEndX - touchStartX.current;
-
-      if (deltaX > 50) {
-        toPrev();
-      } else if (deltaX < -50) {
-        toNext();
-      }
-    };
-
-    const toggleHandler = (fn: Function) => {
-      if (moving.current) return;
-
-      setIsRunning(false);
-      moving.current = true;
-      fn();
-    };
-
-    const toNext = () => {
-      toggleHandler(() => {
-        const temp = [...itemStack];
-        const swiperItem = getNextItem(curSwiperItem, SIGHT_COUNT);
-        temp.shift();
-        temp.push(getNextItem(curSwiperItem + 3, SIGHT_COUNT));
-        setItemStack(temp);
-        setCurSwiperItem(swiperItem);
-      });
-    };
-
-    const toPrev = () => {
-      toggleHandler(() => {
-        const temp = [...itemStack];
-        const swiperItem = getPreviousItem(curSwiperItem, SIGHT_COUNT);
-        temp.pop();
-        temp.unshift(getPreviousItem(itemStack[0], SIGHT_COUNT));
-        setItemStack(temp);
-        setCurSwiperItem(swiperItem);
-      });
-    };
-
-    const [nexts, prevs] = useMemo(
-      () => getIndicesRelative(curSwiperItem, SIGHT_COUNT),
-      [curSwiperItem]
-    );
-    const activeId = getPreviousItem(curSwiperItem, SIGHT_COUNT);
-
-    const nearIds = [
-      getPreviousItem(activeId, SIGHT_COUNT),
-      getNextItem(activeId, SIGHT_COUNT),
-    ];
-
-    useImperativeHandle(ref, () => {
-      return {
-        toNext,
-        toPrev,
-        setIsRunning,
-      };
-    });
-
-    useInterval(toNext, isRunning ? 5000 : null);
-
-    const handleActLoaded = useCallback(() => {
-      setTimeout(() => {
-        moving.current = false;
-        loaded.current && setIsRunning(true);
-        loaded.current = true;
-      }, 1000);
-    }, []);
-
-    return (
-      <View
-        className="sights"
-        onTouchStart={handleTouchStart}
-        onTouchEnd={handleTouchEnd}
-      >
-        {itemStack.map((id, idx) => {
-          const realId = SORT_MAP_ID[id + 1 > 25 ? 0 : id + 1];
-          const show =
-            [...nexts, ...prevs].includes(id) || id === curSwiperItem;
-          const isActive = activeId === id;
-
-          return (
-            show && (
-              <RoundItem
-                key={id}
-                id={realId}
-                angle={ANGLE * idx}
-                bigR={Big_R}
-                minR={MIN_R}
-                isActive={isActive}
-                isNear={nearIds.includes(id)}
-                onClick={handleDetail.bind(undefined, realId)}
-                onActLoaded={handleActLoaded}
-              />
-            )
-          );
-        })}
-      </View>
-    );
-  }
-);
+import { ITouchEvent, View } from "@tarojs/components";
+import { RoundItem } from "../RoundItem";
+import Taro from "@tarojs/taro";
+import {
+  forwardRef,
+  useCallback,
+  useImperativeHandle,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useInterval } from "../../../../hooks/useInterval";
+import "./index.scss";
+import { SORT_MAP_ID } from "../../constants";
+
+export interface SwiperProps {
+  curSwiperItem: number;
+  handleDetail(id: number): void;
+  setCurSwiperItem(num: number): void;
+}
+
+export interface SwiperMethods {
+  toNext(): void;
+  toPrev(): void;
+  setIsRunning(v: boolean): void;
+}
+
+const SIGHT_COUNT = 26;
+const Big_R = (500 * Taro.getSystemInfoSync().windowWidth) / 750;
+const MIN_R = (150 * Taro.getSystemInfoSync().windowWidth) / 750;
+
+const getItemStack = () => {
+  const stack: number[] = [];
+  for (let i = 0; i <= 13; i++) {
+    stack.push(i);
+  }
+  return stack;
+};
+
+const ANGLE = 360 / 12;
+
+export const Swiper = forwardRef<SwiperMethods, SwiperProps>(
+  ({ curSwiperItem, setCurSwiperItem, handleDetail }, ref) => {
+    const [itemStack, setItemStack] = useState(getItemStack());
+    const touchStartX = useRef(0);
+    const moving = useRef(false);
+    const [isRunning, setIsRunning] = useState(false);
+    const loaded = useRef(false);
+
+    const getNextItem = (curIndex: number, length: number) => {
+      return (curIndex + 1) % length;
+    };
+    const getPreviousItem = (curIndex: number, length: number) => {
+      return (curIndex - 1 + length) % length;
+    };
+    const getIndicesRelative = (curIndex: number, length: number) => {
+      const nextIndex = getNextItem(curIndex, length);
+      const nextThreeIndices = [nextIndex];
+      for (let i = 1; i < 1; i++) {
+        nextThreeIndices.push(getNextItem(nextThreeIndices[i - 1], length));
+      }
+
+      const previousIndex = getPreviousItem(curIndex, length);
+      const previousThreeIndices = [previousIndex];
+      for (let i = 1; i <= 2; i++) {
+        previousThreeIndices.push(
+          getPreviousItem(previousThreeIndices[i - 1], length)
+        );
+      }
+
+      return [nextThreeIndices, previousThreeIndices];
+    };
+
+    const handleTouchStart = (e: ITouchEvent) => {
+      setIsRunning(false);
+      touchStartX.current = e.changedTouches[0].clientX;
+    };
+    const handleTouchEnd = (e: ITouchEvent) => {
+      const touchEndX = e.changedTouches[0].clientX;
+      const deltaX = touchEndX - touchStartX.current;
+
+      if (deltaX > 50) {
+        toPrev();
+      } else if (deltaX < -50) {
+        toNext();
+      }
+    };
+
+    const toggleHandler = (fn: Function) => {
+      if (moving.current) return;
+
+      setIsRunning(false);
+      moving.current = true;
+      fn();
+    };
+
+    const toNext = () => {
+      toggleHandler(() => {
+        const temp = [...itemStack];
+        const swiperItem = getNextItem(curSwiperItem, SIGHT_COUNT);
+        temp.shift();
+        temp.push(getNextItem(curSwiperItem + 3, SIGHT_COUNT));
+        setItemStack(temp);
+        setCurSwiperItem(swiperItem);
+      });
+    };
+
+    const toPrev = () => {
+      toggleHandler(() => {
+        const temp = [...itemStack];
+        const swiperItem = getPreviousItem(curSwiperItem, SIGHT_COUNT);
+        temp.pop();
+        temp.unshift(getPreviousItem(itemStack[0], SIGHT_COUNT));
+        setItemStack(temp);
+        setCurSwiperItem(swiperItem);
+      });
+    };
+
+    const [nexts, prevs] = useMemo(
+      () => getIndicesRelative(curSwiperItem, SIGHT_COUNT),
+      [curSwiperItem]
+    );
+    const activeId = getPreviousItem(curSwiperItem, SIGHT_COUNT);
+
+    const nearIds = [
+      getPreviousItem(activeId, SIGHT_COUNT),
+      getNextItem(activeId, SIGHT_COUNT),
+    ];
+
+    useImperativeHandle(ref, () => {
+      return {
+        toNext,
+        toPrev,
+        setIsRunning,
+      };
+    });
+
+    useInterval(toNext, isRunning ? 5000 : null);
+
+    const handleActLoaded = useCallback(() => {
+      setTimeout(() => {
+        moving.current = false;
+        loaded.current && setIsRunning(true);
+        loaded.current = true;
+      }, 1000);
+    }, []);
+
+    return (
+      <View
+        className="sights"
+        onTouchStart={handleTouchStart}
+        onTouchEnd={handleTouchEnd}
+      >
+        {itemStack.map((id, idx) => {
+          const realId = SORT_MAP_ID[id + 1 > 25 ? 0 : id + 1];
+          const show =
+            [...nexts, ...prevs].includes(id) || id === curSwiperItem;
+          const isActive = activeId === id;
+
+          return (
+            show && (
+              <RoundItem
+                key={id}
+                id={realId}
+                angle={ANGLE * idx}
+                bigR={Big_R}
+                minR={MIN_R}
+                isActive={isActive}
+                isNear={nearIds.includes(id)}
+                onClick={handleDetail.bind(undefined, realId)}
+                onActLoaded={handleActLoaded}
+              />
+            )
+          );
+        })}
+      </View>
+    );
+  }
+);

+ 90 - 90
src/pages/home/components/VisitCard/index.scss

@@ -1,90 +1,90 @@
-.visit-card {
-  .at-curtain__container {
-    width: 646px;
-  }
-  .at-curtain__body {
-    padding: 0 75px 75px;
-    display: flex;
-    flex-direction: column;
-    height: 1174px;
-    box-sizing: border-box;
-    background: url("./pop_bg@2x-min.png") no-repeat center / contain;
-  }
-  .at-curtain__btn-close {
-    display: none;
-  }
-
-  &__title {
-    font-size: 38px;
-    color: #ffe794;
-    font-family: "Source Han Serif CN Heavy";
-    text-align: center;
-    height: 114px;
-    line-height: 134px;
-    text-align: center;
-  }
-  &__inner {
-    margin-top: 60px;
-    color: #a59e88;
-    font-size: 28px;
-    line-height: 46px;
-    flex: 1;
-    height: 0;
-
-    .first {
-      margin-bottom: 10px;
-    }
-    .right {
-      margin-top: 10px;
-      text-align: right;
-    }
-  }
-  &__bd {
-    margin: 48px 0;
-    width: 100%;
-  }
-  &__input {
-    display: flex;
-    gap: 10px;
-    height: 70px;
-
-    &__input {
-      flex: 1;
-      padding: 0 30px;
-      height: inherit;
-      border-radius: 50px;
-      background: white;
-      font-size: 23px;
-      color: #424a4a;
-    }
-    &__visit {
-      min-width: 154px;
-      height: inherit;
-      line-height: 70px;
-      font-size: 23px;
-      color: #5a503c;
-      background: #e4d8a7;
-      border: none;
-    }
-  }
-  &__wechat {
-    margin-top: 23px;
-    width: 100%;
-    height: 80px;
-    color: white;
-    font-size: 23px;
-    border: 2px solid #e4d8a7;
-    background: #589498;
-    border-radius: 50px;
-
-    &__icon {
-      width: 57px;
-      height: 57px;
-    }
-    .at-button__text {
-      display: flex;
-      align-items: center;
-      gap: 24px;
-    }
-  }
-}
+.visit-card {
+  .at-curtain__container {
+    width: 646px;
+  }
+  .at-curtain__body {
+    padding: 0 75px 75px;
+    display: flex;
+    flex-direction: column;
+    height: 1174px;
+    box-sizing: border-box;
+    background: url("./pop_bg@2x-min.png") no-repeat center / contain;
+  }
+  .at-curtain__btn-close {
+    display: none;
+  }
+
+  &__title {
+    font-size: 38px;
+    color: #ffe794;
+    font-family: "Source Han Serif CN Heavy";
+    text-align: center;
+    height: 114px;
+    line-height: 134px;
+    text-align: center;
+  }
+  &__inner {
+    margin-top: 60px;
+    color: #a59e88;
+    font-size: 28px;
+    line-height: 46px;
+    flex: 1;
+    height: 0;
+
+    .first {
+      margin-bottom: 10px;
+    }
+    .right {
+      margin-top: 10px;
+      text-align: right;
+    }
+  }
+  &__bd {
+    margin: 48px 0;
+    width: 100%;
+  }
+  &__input {
+    display: flex;
+    gap: 10px;
+    height: 70px;
+
+    &__input {
+      flex: 1;
+      padding: 0 30px;
+      height: inherit;
+      border-radius: 50px;
+      background: white;
+      font-size: 23px;
+      color: #424a4a;
+    }
+    &__visit {
+      min-width: 154px;
+      height: inherit;
+      line-height: 70px;
+      font-size: 23px;
+      color: #5a503c;
+      background: #e4d8a7;
+      border: none;
+    }
+  }
+  &__wechat {
+    margin-top: 23px;
+    width: 100%;
+    height: 80px;
+    color: white;
+    font-size: 23px;
+    border: 2px solid #e4d8a7;
+    background: #589498;
+    border-radius: 50px;
+
+    &__icon {
+      width: 57px;
+      height: 57px;
+    }
+    .at-button__text {
+      display: flex;
+      align-items: center;
+      gap: 24px;
+    }
+  }
+}

+ 173 - 173
src/pages/home/components/VisitCard/index.tsx

@@ -1,173 +1,173 @@
-import { ScrollView, View, Image, Input } from "@tarojs/components";
-import { FC } from "@tarojs/taro";
-import { AtButton, AtCurtain } from "taro-ui";
-import { AtCurtainProps } from "taro-ui/types/curtain";
-import BorderImg from "./line@2x-min.png";
-import { useEffect, useState } from "react";
-import {
-  createRandomString,
-  getSceneUrl,
-  login,
-  NICKNAME_KEY,
-  onValidateField,
-} from "../../../../utils";
-import WechatIcon from "../../../../images/icon_wechat@2x-min.png";
-import {
-  handleVisitStateApi,
-  setUserInfoApi,
-  validateNickName,
-} from "../../../../api";
-import baseStore from "../../../../store/base";
-import Taro from "@tarojs/taro";
-import "./index.scss";
-
-export interface VisitCardProps extends AtCurtainProps {}
-
-const formRules = {
-  name: [{ required: true, message: "请输入昵称" }],
-};
-
-export const VisitCard: FC<VisitCardProps> = (props) => {
-  // const [loading, setLoading] = useState(false);
-  const [wechatLoading, setWechatLoading] = useState(false);
-  const [name, setName] = useState(`云城居民${createRandomString()}`);
-
-  useEffect(() => {
-    if (baseStore.userInfo) {
-      if (baseStore.userInfo.invite === 1) {
-        setName(baseStore.userInfo.nickName);
-      }
-    }
-  }, [baseStore.userInfo]);
-
-  const handleValidate = async () => {
-    const errors = await onValidateField(
-      {
-        name: name.trim(),
-      },
-      formRules
-    );
-    let valid = !errors || errors.filter((error) => error.message).length <= 0;
-
-    if (!valid) {
-      Taro.showToast({
-        icon: "none",
-        title: errors[0].message,
-      });
-      return false;
-    }
-
-    const reg = /[\u4e00-\u9fa5a-zA-Z]/g;
-    const matches = name.match(reg);
-
-    if (matches) {
-      await validateNickName(matches.join(""));
-    }
-
-    return true;
-  };
-
-  const handleUserInfo = async () => {
-    if (!(await handleValidate())) return;
-
-    try {
-      setWechatLoading(true);
-
-      if (!baseStore.isLogin) {
-        await login();
-      }
-
-      await setUserInfoApi({
-        id: baseStore.userInfo.id,
-        nickName: name,
-      });
-      await handleVisitStateApi(1);
-
-      Taro.setStorageSync(NICKNAME_KEY, name);
-      baseStore.name = name;
-      baseStore.userInfo.invite = 1;
-      goHomePage();
-    } finally {
-      setWechatLoading(false);
-    }
-  };
-
-  // const handleVisitor = async () => {
-  //   try {
-  //     if (!(await handleValidate())) return;
-
-  //     setLoading(true);
-
-  //     await setVistorName(name);
-  //     await setUserInfoApi({
-  //       id: baseStore.userInfo.id,
-  //       nickName: name,
-  //     });
-
-  //     Taro.setStorageSync(NICKNAME_KEY, name);
-  //     baseStore.name = name;
-  //     goHomePage();
-  //   } finally {
-  //     setLoading(false);
-  //   }
-  // };
-
-  const goHomePage = () => {
-    Taro.navigateTo({
-      url: "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl()),
-    });
-    // @ts-ignore
-    props.onClose();
-  };
-
-  return (
-    <AtCurtain {...props} className="visit-card">
-      <View className="visit-card__title">邀请函</View>
-
-      <ScrollView scrollY className="visit-card__inner">
-        <View className="first">致:</View>
-        <View>
-          在这座梦幻的游戏主城中,我们不仅追逐冒险和快乐,更致力于传递爱与温暖。每一次的探索,都是为了点亮他人的心灵,每一步的冒险,都是为了温暖他人的生活。我们欢迎你加入我们的行列,成为这座城市的一员,与我们一起行善,传递爱意。在这里,每一个人都是慈善的使者,每一次游戏都是一次美好的奉献。加入我们,让我们共同创造更美好的世界!
-        </View>
-        <View>期待你的加入!</View>
-        <View className="right">陶乐乐</View>
-      </ScrollView>
-
-      <Image className="visit-card__bd" src={BorderImg} mode="widthFix" />
-
-      <View className="visit-card__input">
-        {props.isOpened && (
-          <Input
-            value={name}
-            className="visit-card__input__input"
-            name="name"
-            maxlength={6}
-            type="nickname"
-            placeholder="请输入6个字以内昵称"
-            onInput={({ detail: { value } }) => {
-              setName(value as string);
-            }}
-          />
-        )}
-
-        {/* <AtButton
-          className="visit-card__input__visit"
-          circle
-          loading={loading}
-          onClick={handleVisitor}
-        >
-          我是游客
-        </AtButton> */}
-      </View>
-
-      <AtButton
-        loading={wechatLoading}
-        className="visit-card__wechat"
-        onClick={handleUserInfo}
-      >
-        <Image className="visit-card__wechat__icon" src={WechatIcon} />
-        成为云城居民
-      </AtButton>
-    </AtCurtain>
-  );
-};
+import { ScrollView, View, Image, Input } from "@tarojs/components";
+import { FC } from "@tarojs/taro";
+import { AtButton, AtCurtain } from "taro-ui";
+import { AtCurtainProps } from "taro-ui/types/curtain";
+import BorderImg from "./line@2x-min.png";
+import { useEffect, useState } from "react";
+import {
+  createRandomString,
+  getSceneUrl,
+  login,
+  NICKNAME_KEY,
+  onValidateField,
+} from "../../../../utils";
+import WechatIcon from "../../../../images/icon_wechat@2x-min.png";
+import {
+  handleVisitStateApi,
+  setUserInfoApi,
+  validateNickName,
+} from "../../../../api";
+import baseStore from "../../../../store/base";
+import Taro from "@tarojs/taro";
+import "./index.scss";
+
+export interface VisitCardProps extends AtCurtainProps {}
+
+const formRules = {
+  name: [{ required: true, message: "请输入昵称" }],
+};
+
+export const VisitCard: FC<VisitCardProps> = (props) => {
+  // const [loading, setLoading] = useState(false);
+  const [wechatLoading, setWechatLoading] = useState(false);
+  const [name, setName] = useState(`云城居民${createRandomString()}`);
+
+  useEffect(() => {
+    if (baseStore.userInfo) {
+      if (baseStore.userInfo.invite === 1) {
+        setName(baseStore.userInfo.nickName);
+      }
+    }
+  }, [baseStore.userInfo]);
+
+  const handleValidate = async () => {
+    const errors = await onValidateField(
+      {
+        name: name.trim(),
+      },
+      formRules
+    );
+    let valid = !errors || errors.filter((error) => error.message).length <= 0;
+
+    if (!valid) {
+      Taro.showToast({
+        icon: "none",
+        title: errors[0].message,
+      });
+      return false;
+    }
+
+    const reg = /[\u4e00-\u9fa5a-zA-Z]/g;
+    const matches = name.match(reg);
+
+    if (matches) {
+      await validateNickName(matches.join(""));
+    }
+
+    return true;
+  };
+
+  const handleUserInfo = async () => {
+    if (!(await handleValidate())) return;
+
+    try {
+      setWechatLoading(true);
+
+      if (!baseStore.isLogin) {
+        await login();
+      }
+
+      await setUserInfoApi({
+        id: baseStore.userInfo.id,
+        nickName: name,
+      });
+      await handleVisitStateApi(1);
+
+      Taro.setStorageSync(NICKNAME_KEY, name);
+      baseStore.name = name;
+      baseStore.userInfo.invite = 1;
+      goHomePage();
+    } finally {
+      setWechatLoading(false);
+    }
+  };
+
+  // const handleVisitor = async () => {
+  //   try {
+  //     if (!(await handleValidate())) return;
+
+  //     setLoading(true);
+
+  //     await setVistorName(name);
+  //     await setUserInfoApi({
+  //       id: baseStore.userInfo.id,
+  //       nickName: name,
+  //     });
+
+  //     Taro.setStorageSync(NICKNAME_KEY, name);
+  //     baseStore.name = name;
+  //     goHomePage();
+  //   } finally {
+  //     setLoading(false);
+  //   }
+  // };
+
+  const goHomePage = () => {
+    Taro.navigateTo({
+      url: "/pages/iframe/index?url=" + encodeURIComponent(getSceneUrl()),
+    });
+    // @ts-ignore
+    props.onClose();
+  };
+
+  return (
+    <AtCurtain {...props} className="visit-card">
+      <View className="visit-card__title">邀请函</View>
+
+      <ScrollView scrollY className="visit-card__inner">
+        <View className="first">致:</View>
+        <View>
+          在这座梦幻的游戏主城中,我们不仅追逐冒险和快乐,更致力于传递爱与温暖。每一次的探索,都是为了点亮他人的心灵,每一步的冒险,都是为了温暖他人的生活。我们欢迎你加入我们的行列,成为这座城市的一员,与我们一起行善,传递爱意。在这里,每一个人都是慈善的使者,每一次游戏都是一次美好的奉献。加入我们,让我们共同创造更美好的世界!
+        </View>
+        <View>期待你的加入!</View>
+        <View className="right">陶乐乐</View>
+      </ScrollView>
+
+      <Image className="visit-card__bd" src={BorderImg} mode="widthFix" />
+
+      <View className="visit-card__input">
+        {props.isOpened && (
+          <Input
+            value={name}
+            className="visit-card__input__input"
+            name="name"
+            maxlength={6}
+            type="nickname"
+            placeholder="请输入6个字以内昵称"
+            onInput={({ detail: { value } }) => {
+              setName(value as string);
+            }}
+          />
+        )}
+
+        {/* <AtButton
+          className="visit-card__input__visit"
+          circle
+          loading={loading}
+          onClick={handleVisitor}
+        >
+          我是游客
+        </AtButton> */}
+      </View>
+
+      <AtButton
+        loading={wechatLoading}
+        className="visit-card__wechat"
+        onClick={handleUserInfo}
+      >
+        <Image className="visit-card__wechat__icon" src={WechatIcon} />
+        成为云城居民
+      </AtButton>
+    </AtCurtain>
+  );
+};

Разница между файлами не показана из-за своего большого размера
+ 459 - 485
src/pages/home/constants.ts


+ 4 - 4
src/pages/home/index.config.ts

@@ -1,4 +1,4 @@
-export default definePageConfig({
-  enableShareAppMessage: true,
-  enableShareTimeline: true,
-});
+export default definePageConfig({
+  enableShareAppMessage: true,
+  enableShareTimeline: true,
+});

+ 254 - 254
src/pages/home/index.scss

@@ -1,254 +1,254 @@
-.home {
-  position: relative;
-  height: inherit;
-
-  &-loading {
-    position: fixed;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 998;
-
-    &.hide {
-      animation: loading-leave 1s ease-in forwards;
-    }
-    &.close {
-      display: none;
-      top: -200%;
-      left: -200%;
-      width: 0;
-      height: 0;
-      opacity: 0;
-      z-index: -1;
-    }
-  }
-  &__cloud {
-    position: absolute;
-    left: -100%;
-    bottom: -260px;
-    width: 100%;
-    animation: cloudMove linear 60s infinite;
-  }
-  &-container {
-    position: relative;
-    height: 100vh;
-    overflow: hidden;
-    background: linear-gradient(180deg, #99b6b4 0%, #fffaea 100%);
-    z-index: 1;
-  }
-  &__menu-btn {
-    position: fixed;
-    top: 30px;
-    left: 27px;
-    width: 80px;
-    height: 76px;
-    background: url("../../images/icon_menu@2x-min.png") no-repeat center /
-      cover;
-    z-index: 9;
-  }
-  &__bg {
-    position: relative;
-    width: 100%;
-    z-index: -1;
-  }
-  &__btm-bg {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    width: 100%;
-  }
-  &-main {
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    overflow: hidden;
-    z-index: 2;
-
-    &__btn {
-      position: absolute;
-      left: 50%;
-      bottom: 180px;
-      width: 460px;
-      text-align: center;
-      font-size: 31px;
-      color: #589498;
-      font-family: "SourceHanSerifCN-Bold";
-      transform: translateX(-50%);
-      z-index: 9;
-
-      .flashing {
-        animation: flashing linear 2s infinite;
-      }
-      .reflect {
-        position: absolute;
-        bottom: -4px;
-        left: 0;
-        right: 0;
-        transform: rotatex(180deg) translatey(15px);
-        transform-origin: 50% 100%;
-        mask: linear-gradient(
-          transparent 0%,
-          transparent 20%,
-          rgba(0, 0, 0, 0.5) 100%
-        );
-      }
-      &::before {
-        content: "";
-        position: absolute;
-        top: -38px;
-        left: 50%;
-        width: 664px;
-        height: 20px;
-        transform: translateX(-50%);
-        background: url("../../images/line@2x-min.png") no-repeat center /
-          contain;
-      }
-      &::after {
-        content: "";
-        position: absolute;
-        left: 50%;
-        bottom: -76px;
-        transform: translateX(-50%);
-        width: 56px;
-        height: 56px;
-        background: url("../../images/img_slide@2x.png") no-repeat center /
-          contain;
-      }
-    }
-  }
-
-  &-toggle {
-    display: flex;
-    align-items: center;
-    gap: 20px;
-    margin-top: 150px;
-    padding: 0 60px;
-
-    &__icon {
-      width: 60px;
-      height: 60px;
-    }
-    &-inner {
-      flex: 1;
-      position: relative;
-    }
-    &-label {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      color: #589498;
-      font-size: 24px;
-      letter-spacing: 7px;
-      background: url("../../images/img_active_title@2x-min.png") no-repeat
-        center bottom / contain;
-
-      &__title {
-        font-size: 46px;
-        font-family: "Source Han Serif CN Heavy";
-      }
-    }
-    &__detail {
-      position: absolute;
-      left: 50%;
-      bottom: -108px;
-      width: 296px;
-      height: 88px;
-      background: url("../../images/btn_more@2x-min.png") no-repeat center /
-        contain;
-      transform: translateX(-50%);
-    }
-  }
-
-  &__point {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 50px;
-    height: 70px;
-    background: url("../../images/icon_address_active@2x-min.png") no-repeat
-      center / contain;
-    will-change: top left;
-    transition: all linear 0.2s;
-    z-index: 2;
-
-    &-icon {
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 50px;
-      height: 70px;
-
-      // &.red::after {
-      //   background: #c59797;
-      // }
-      &::after {
-        content: "";
-        position: absolute;
-        left: 50%;
-        bottom: 0;
-        width: 10px;
-        height: 5px;
-        transform: translateX(-50%);
-        background: #97c59b;
-        border-radius: 25%;
-        overflow: hidden;
-      }
-    }
-  }
-  &__search {
-    position: fixed;
-    top: 32px;
-    right: 23px;
-    padding: 10px;
-    width: 40px;
-    height: 40px;
-    z-index: 9;
-  }
-}
-
-.home2 {
-  position: relative;
-  height: 100vh;
-  overflow: hidden;
-
-  &__btn {
-    position: absolute;
-    left: 50%;
-    bottom: 150px;
-    width: 496px;
-    transform: translateX(-50%);
-    z-index: 2;
-  }
-  &__bg {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-  }
-}
-
-@keyframes flashing {
-  0% {
-    opacity: 1;
-  }
-  50% {
-    opacity: 0.6;
-  }
-  100% {
-    opacity: 1;
-  }
-}
-
-@keyframes cloudMove {
-  50% {
-    left: 0;
-  }
-  100% {
-    left: -100%;
-  }
-}
+.home {
+  position: relative;
+  height: inherit;
+
+  &-loading {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 998;
+
+    &.hide {
+      animation: loading-leave 1s ease-in forwards;
+    }
+    &.close {
+      display: none;
+      top: -200%;
+      left: -200%;
+      width: 0;
+      height: 0;
+      opacity: 0;
+      z-index: -1;
+    }
+  }
+  &__cloud {
+    position: absolute;
+    left: -100%;
+    bottom: -260px;
+    width: 100%;
+    animation: cloudMove linear 60s infinite;
+  }
+  &-container {
+    position: relative;
+    height: 100vh;
+    overflow: hidden;
+    background: linear-gradient(180deg, #99b6b4 0%, #fffaea 100%);
+    z-index: 1;
+  }
+  &__menu-btn {
+    position: fixed;
+    top: 30px;
+    left: 27px;
+    width: 80px;
+    height: 76px;
+    background: url("../../images/icon_menu@2x-min.png") no-repeat center /
+      cover;
+    z-index: 9;
+  }
+  &__bg {
+    position: relative;
+    width: 100%;
+    z-index: -1;
+  }
+  &__btm-bg {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    width: 100%;
+  }
+  &-main {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: hidden;
+    z-index: 2;
+
+    &__btn {
+      position: absolute;
+      left: 50%;
+      bottom: 180px;
+      width: 460px;
+      text-align: center;
+      font-size: 31px;
+      color: #589498;
+      font-family: "SourceHanSerifCN-Bold";
+      transform: translateX(-50%);
+      z-index: 9;
+
+      .flashing {
+        animation: flashing linear 2s infinite;
+      }
+      .reflect {
+        position: absolute;
+        bottom: -4px;
+        left: 0;
+        right: 0;
+        transform: rotatex(180deg) translatey(15px);
+        transform-origin: 50% 100%;
+        mask: linear-gradient(
+          transparent 0%,
+          transparent 20%,
+          rgba(0, 0, 0, 0.5) 100%
+        );
+      }
+      &::before {
+        content: "";
+        position: absolute;
+        top: -38px;
+        left: 50%;
+        width: 664px;
+        height: 20px;
+        transform: translateX(-50%);
+        background: url("../../images/line@2x-min.png") no-repeat center /
+          contain;
+      }
+      &::after {
+        content: "";
+        position: absolute;
+        left: 50%;
+        bottom: -76px;
+        transform: translateX(-50%);
+        width: 56px;
+        height: 56px;
+        background: url("../../images/img_slide@2x.png") no-repeat center /
+          contain;
+      }
+    }
+  }
+
+  &-toggle {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    margin-top: 150px;
+    padding: 0 60px;
+
+    &__icon {
+      width: 60px;
+      height: 60px;
+    }
+    &-inner {
+      flex: 1;
+      position: relative;
+    }
+    &-label {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      color: #589498;
+      font-size: 24px;
+      letter-spacing: 7px;
+      background: url("../../images/img_active_title@2x-min.png") no-repeat
+        center bottom / contain;
+
+      &__title {
+        font-size: 46px;
+        font-family: "Source Han Serif CN Heavy";
+      }
+    }
+    &__detail {
+      position: absolute;
+      left: 50%;
+      bottom: -108px;
+      width: 296px;
+      height: 88px;
+      background: url("../../images/btn_more@2x-min.png") no-repeat center /
+        contain;
+      transform: translateX(-50%);
+    }
+  }
+
+  &__point {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 50px;
+    height: 70px;
+    background: url("../../images/icon_address_active@2x-min.png") no-repeat
+      center / contain;
+    will-change: top left;
+    transition: all linear 0.2s;
+    z-index: 2;
+
+    &-icon {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 50px;
+      height: 70px;
+
+      // &.red::after {
+      //   background: #c59797;
+      // }
+      &::after {
+        content: "";
+        position: absolute;
+        left: 50%;
+        bottom: 0;
+        width: 10px;
+        height: 5px;
+        transform: translateX(-50%);
+        background: #97c59b;
+        border-radius: 25%;
+        overflow: hidden;
+      }
+    }
+  }
+  &__search {
+    position: fixed;
+    top: 32px;
+    right: 23px;
+    padding: 10px;
+    width: 40px;
+    height: 40px;
+    z-index: 9;
+  }
+}
+
+.home2 {
+  position: relative;
+  height: 100vh;
+  overflow: hidden;
+
+  &__btn {
+    position: absolute;
+    left: 50%;
+    bottom: 150px;
+    width: 496px;
+    transform: translateX(-50%);
+    z-index: 2;
+  }
+  &__bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+  }
+}
+
+@keyframes flashing {
+  0% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.6;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+
+@keyframes cloudMove {
+  50% {
+    left: 0;
+  }
+  100% {
+    left: -100%;
+  }
+}

+ 361 - 361
src/pages/home/index.tsx

@@ -1,361 +1,361 @@
-import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
-import {
-  Image,
-  View,
-  Text,
-  Video,
-  Swiper,
-  SwiperItem,
-} from "@tarojs/components";
-import classNames from "classnames";
-import Taro, {
-  FC,
-  nextTick,
-  pxTransform,
-  useDidHide,
-  useDidShow,
-} from "@tarojs/taro";
-import { Menu } from "./components/Menu";
-import BtmBgImg from "../../images/img_city-min.png";
-import CloudImg from "../../images/img_cloud-min.png";
-import LeftIcon from "../../images/icon_blue_left@2x-min.png";
-import RightIcon from "../../images/icon_blue_right@2x-min.png";
-import { Swiper as SightSwiper, SwiperMethods } from "./components/Swiper";
-import { SearchLayout } from "./components/SearchLayout";
-import { SightDetailLayout } from "./components/SightDetailLayout";
-import { SIGHT_LIST, SORT_MAP_ID } from "./constants";
-import { getSignListApi } from "../../api";
-import { VisitCard } from "./components/VisitCard";
-import { getSceneUrl, login } from "../../utils";
-import baseStore from "../../store/base";
-import SearchIcon from "../../images/icon_search_white@2x-min.png";
-import { VideoWrap } from "../../components/Video";
-import "./index.scss";
-import { AtIcon } from "taro-ui";
-
-const PointIcon = memo(() => {
-  return (
-    <>
-      {SIGHT_LIST.map((item) => (
-        <View
-          key={item.id}
-          className={classNames("home__point-icon", [
-            Math.round(Math.random()) ? "" : "red",
-          ])}
-          style={{
-            top: pxTransform(item.pos.y),
-            left: pxTransform(item.pos.x),
-          }}
-        />
-      ))}
-    </>
-  );
-});
-
-const bgScale = Math.round(1558 / 778);
-const system = Taro.getSystemInfoSync();
-const imgHeight = bgScale * system.windowWidth;
-const offsetHeight =
-  imgHeight > system.screenHeight ? imgHeight - system.screenHeight : 0;
-
-const HomePage: FC = () => {
-  const [loading, setLoading] = useState(true);
-  const [videoMetaLoaded, setVideoMetaLoaded] = useState(false);
-  const [curSwiper, setCurSwiper] = useState(0);
-  const [loaded, setLoaded] = useState(false);
-  const [visitVisible, setVisitVisible] = useState(false);
-  const [showMenu, setShowMenu] = useState(false);
-  const [showSearch, setShowSearch] = useState(false);
-  const [showSight, setShowSight] = useState(false);
-  const swiperRef = useRef<SwiperMethods>(null);
-  const [curSwiperItem, setCurSwiperItem] = useState(10);
-  const [item, setItem] = useState(
-    SIGHT_LIST.find((i) => i.id === SORT_MAP_ID[curSwiperItem])
-  );
-  const audioContext = useRef<Taro.InnerAudioContext>();
-
-  const realItem = useMemo(
-    () => SIGHT_LIST.find((i) => i.id === SORT_MAP_ID[curSwiperItem]),
-    [curSwiperItem]
-  );
-
-  const createBgAudio = () => {
-    const audio = Taro.createInnerAudioContext();
-    audio.autoplay = loaded;
-    audio.loop = true;
-    audio.volume = 0.3;
-    audio.src =
-      "https://houseoss.4dkankan.com/project/wx-csbwg-public/audios/ysdt-bg.mp3";
-    audioContext.current = audio;
-  };
-
-  const openSearch = (closeMenu?: boolean) => {
-    closeMenu && setShowMenu(false);
-    setShowSearch(true);
-    if (!closeMenu) {
-      swiperRef.current?.setIsRunning(false);
-    }
-  };
-
-  const handleDetail = useCallback(
-    async (id?: number) => {
-      swiperRef.current?.setIsRunning(false);
-      const target = SIGHT_LIST.find(
-        (i) => i.id === (id || SORT_MAP_ID[curSwiperItem])
-      );
-      const data = await getSignListApi({
-        searchKey: target?.title,
-        type: "scene",
-      });
-      setItem(data[0] || target);
-      setShowSight(true);
-    },
-    [curSwiperItem]
-  );
-
-  const closeDetail = () => {
-    setShowSight(false);
-    curSwiper === 0 && swiperRef.current?.setIsRunning(true);
-  };
-
-  const closeMenu = () => {
-    setShowMenu(false);
-    curSwiper === 0 && swiperRef.current?.setIsRunning(true);
-  };
-
-  const handleLoaded = () => {
-    nextTick(() => {
-      setLoaded(true);
-      swiperRef.current?.setIsRunning(true);
-      audioContext.current?.play();
-    });
-  };
-
-  useDidShow(() => {
-    createBgAudio();
-
-    loaded &&
-      !showMenu &&
-      !showSearch &&
-      !showSight &&
-      swiperRef.current?.setIsRunning(true);
-  });
-  useDidHide(() => {
-    audioContext.current?.destroy();
-    audioContext.current = undefined;
-
-    swiperRef.current?.setIsRunning(false);
-  });
-
-  useEffect(() => {
-    return () => {
-      audioContext.current?.destroy();
-    };
-  }, []);
-
-  return (
-    <>
-      <View
-        className="home__menu-btn"
-        onClick={() => {
-          setShowMenu(true);
-          swiperRef.current?.setIsRunning(false);
-        }}
-      />
-
-      <Menu
-        show={showMenu}
-        onClose={closeMenu}
-        openSearch={openSearch.bind(undefined, true)}
-      />
-
-      <Image
-        src={SearchIcon}
-        className="home__search"
-        onClick={() => openSearch()}
-      />
-
-      <Swiper
-        vertical
-        current={curSwiper}
-        style={{ height: "100vh" }}
-        onChange={(v) => {
-          setCurSwiper(v.detail.current);
-          swiperRef.current?.setIsRunning(v.detail.current === 0);
-        }}
-      >
-        <SwiperItem className="home-container">
-          <View className="home">
-            <View
-              className="home-main"
-              style={{
-                top: `${-offsetHeight}px`,
-              }}
-            >
-              {realItem && (
-                <View
-                  className="home__point"
-                  style={{
-                    top: pxTransform(realItem.pos.y),
-                    left: pxTransform(realItem.pos.x),
-                  }}
-                />
-              )}
-
-              <PointIcon />
-
-              <SightSwiper
-                ref={swiperRef}
-                curSwiperItem={curSwiperItem}
-                setCurSwiperItem={setCurSwiperItem}
-                handleDetail={handleDetail}
-              />
-
-              <View className="home-toggle">
-                <Image
-                  className="home-toggle__icon"
-                  src={LeftIcon}
-                  onClick={() => swiperRef.current?.toPrev()}
-                />
-
-                <View className="home-toggle-inner">
-                  <View className="home-toggle-label">
-                    <Text className="home-toggle-label__title limit-line">
-                      {realItem?.title}
-                    </Text>
-                    <Text>{realItem?.address}</Text>
-                  </View>
-
-                  <View
-                    className="home-toggle__detail"
-                    onClick={() => handleDetail()}
-                  />
-                </View>
-
-                <Image
-                  className="home-toggle__icon"
-                  src={RightIcon}
-                  onClick={() => swiperRef.current?.toNext()}
-                />
-              </View>
-            </View>
-
-            <View className="home-main__btn" onClick={() => setCurSwiper(1)}>
-              <View className="flashing">
-                <View>探索锡善云城</View>
-                <View className="reflect">探索锡善云城</View>
-              </View>
-            </View>
-
-            <Image
-              className="home__bg"
-              src="https://houseoss.4dkankan.com/project/wx-csbwg-public/images/img_map%402x-min.jpg"
-              mode="widthFix"
-              style={{
-                top: `${-offsetHeight}px`,
-              }}
-            />
-            <Image className="home__btm-bg" src={BtmBgImg} mode="widthFix" />
-            <Image className="home__cloud" src={CloudImg} mode="heightFix" />
-          </View>
-        </SwiperItem>
-
-        <SwiperItem className="home2">
-          <Image
-            className="home2__btn"
-            src="https://houseoss.4dkankan.com/project/wx-csbwg-public/images/btn_start%402x-min.png"
-            mode="widthFix"
-            onClick={async () => {
-              const userInfo = baseStore.userInfo;
-
-              if (!userInfo) {
-                await login();
-              }
-
-              if (userInfo.invite === 0) {
-                setVisitVisible(true);
-                return;
-              }
-
-              Taro.navigateTo({
-                url:
-                  "/pages/iframe/index?url=" +
-                  encodeURIComponent(getSceneUrl()),
-              });
-            }}
-          />
-
-          <Video
-            className="home2__bg"
-            src="https://houseoss.4dkankan.com/project/wx-csbwg-public/videos/city.mp4"
-            autoplay
-            muted
-            loop
-            controls={false}
-            objectFit="cover"
-            enableProgressGesture={false}
-          />
-        </SwiperItem>
-      </Swiper>
-
-      {!loaded && (
-        <VideoWrap
-          className={classNames("home-loading")}
-          src="https://houseoss.4dkankan.com/project/wx-csbwg-public/videos/ysdt.mp4"
-          autoplay
-          controls={false}
-          onLoadedMetaData={() => {
-            setVideoMetaLoaded(true);
-            setTimeout(() => {
-              setLoading(false);
-            }, 1000);
-          }}
-          onEnded={handleLoaded}
-          onError={handleLoaded}
-        />
-      )}
-
-      <SearchLayout
-        isOpened={showSearch}
-        onClose={() => {
-          setShowSearch(false);
-          if (curSwiper === 0) {
-            swiperRef.current?.setIsRunning(true);
-          }
-        }}
-        openDetail={(item) => {
-          swiperRef.current?.setIsRunning(false);
-          setItem(item);
-          setShowSight(true);
-        }}
-      />
-
-      {item && (
-        <SightDetailLayout
-          item={item}
-          isOpened={showSight}
-          onClose={closeDetail}
-        />
-      )}
-
-      <VisitCard
-        isOpened={visitVisible}
-        onClose={() => setVisitVisible(false)}
-      />
-
-      <View
-        className={classNames("ld-page", { hide: videoMetaLoaded })}
-        style={{ zIndex: loading ? 999 : -1 }}
-      >
-        <AtIcon
-          className="ld-page__icon"
-          value="loading"
-          color="#589498"
-          size={40}
-        />
-      </View>
-    </>
-  );
-};
-
-export default HomePage;
+import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
+import {
+  Image,
+  View,
+  Text,
+  Video,
+  Swiper,
+  SwiperItem,
+} from "@tarojs/components";
+import classNames from "classnames";
+import Taro, {
+  FC,
+  nextTick,
+  pxTransform,
+  useDidHide,
+  useDidShow,
+} from "@tarojs/taro";
+import { Menu } from "./components/Menu";
+import BtmBgImg from "../../images/img_city-min.png";
+import CloudImg from "../../images/img_cloud-min.png";
+import LeftIcon from "../../images/icon_blue_left@2x-min.png";
+import RightIcon from "../../images/icon_blue_right@2x-min.png";
+import { Swiper as SightSwiper, SwiperMethods } from "./components/Swiper";
+import { SearchLayout } from "./components/SearchLayout";
+import { SightDetailLayout } from "./components/SightDetailLayout";
+import { SIGHT_LIST, SORT_MAP_ID } from "./constants";
+import { getSignListApi } from "../../api";
+import { VisitCard } from "./components/VisitCard";
+import { getSceneUrl, login } from "../../utils";
+import baseStore from "../../store/base";
+import SearchIcon from "../../images/icon_search_white@2x-min.png";
+import { VideoWrap } from "../../components/Video";
+import "./index.scss";
+import { AtIcon } from "taro-ui";
+
+const PointIcon = memo(() => {
+  return (
+    <>
+      {SIGHT_LIST.map((item) => (
+        <View
+          key={item.id}
+          className={classNames("home__point-icon", [
+            Math.round(Math.random()) ? "" : "red",
+          ])}
+          style={{
+            top: pxTransform(item.pos.y),
+            left: pxTransform(item.pos.x),
+          }}
+        />
+      ))}
+    </>
+  );
+});
+
+const bgScale = Math.round(1558 / 778);
+const system = Taro.getSystemInfoSync();
+const imgHeight = bgScale * system.windowWidth;
+const offsetHeight =
+  imgHeight > system.screenHeight ? imgHeight - system.screenHeight : 0;
+
+const HomePage: FC = () => {
+  const [loading, setLoading] = useState(true);
+  const [videoMetaLoaded, setVideoMetaLoaded] = useState(false);
+  const [curSwiper, setCurSwiper] = useState(0);
+  const [loaded, setLoaded] = useState(false);
+  const [visitVisible, setVisitVisible] = useState(false);
+  const [showMenu, setShowMenu] = useState(false);
+  const [showSearch, setShowSearch] = useState(false);
+  const [showSight, setShowSight] = useState(false);
+  const swiperRef = useRef<SwiperMethods>(null);
+  const [curSwiperItem, setCurSwiperItem] = useState(10);
+  const [item, setItem] = useState(
+    SIGHT_LIST.find((i) => i.id === SORT_MAP_ID[curSwiperItem])
+  );
+  const audioContext = useRef<Taro.InnerAudioContext>();
+
+  const realItem = useMemo(
+    () => SIGHT_LIST.find((i) => i.id === SORT_MAP_ID[curSwiperItem]),
+    [curSwiperItem]
+  );
+
+  const createBgAudio = () => {
+    const audio = Taro.createInnerAudioContext();
+    audio.autoplay = loaded;
+    audio.loop = true;
+    audio.volume = 0.3;
+    audio.src =
+      "https://wuxicharitymuseum.cn/wx-csbwg-public/audios/ysdt-bg.mp3";
+    audioContext.current = audio;
+  };
+
+  const openSearch = (closeMenu?: boolean) => {
+    closeMenu && setShowMenu(false);
+    setShowSearch(true);
+    if (!closeMenu) {
+      swiperRef.current?.setIsRunning(false);
+    }
+  };
+
+  const handleDetail = useCallback(
+    async (id?: number) => {
+      swiperRef.current?.setIsRunning(false);
+      const target = SIGHT_LIST.find(
+        (i) => i.id === (id || SORT_MAP_ID[curSwiperItem])
+      );
+      const data = await getSignListApi({
+        searchKey: target?.title,
+        type: "scene",
+      });
+      setItem(data[0] || target);
+      setShowSight(true);
+    },
+    [curSwiperItem]
+  );
+
+  const closeDetail = () => {
+    setShowSight(false);
+    curSwiper === 0 && swiperRef.current?.setIsRunning(true);
+  };
+
+  const closeMenu = () => {
+    setShowMenu(false);
+    curSwiper === 0 && swiperRef.current?.setIsRunning(true);
+  };
+
+  const handleLoaded = () => {
+    nextTick(() => {
+      setLoaded(true);
+      swiperRef.current?.setIsRunning(true);
+      audioContext.current?.play();
+    });
+  };
+
+  useDidShow(() => {
+    createBgAudio();
+
+    loaded &&
+      !showMenu &&
+      !showSearch &&
+      !showSight &&
+      swiperRef.current?.setIsRunning(true);
+  });
+  useDidHide(() => {
+    audioContext.current?.destroy();
+    audioContext.current = undefined;
+
+    swiperRef.current?.setIsRunning(false);
+  });
+
+  useEffect(() => {
+    return () => {
+      audioContext.current?.destroy();
+    };
+  }, []);
+
+  return (
+    <>
+      <View
+        className="home__menu-btn"
+        onClick={() => {
+          setShowMenu(true);
+          swiperRef.current?.setIsRunning(false);
+        }}
+      />
+
+      <Menu
+        show={showMenu}
+        onClose={closeMenu}
+        openSearch={openSearch.bind(undefined, true)}
+      />
+
+      <Image
+        src={SearchIcon}
+        className="home__search"
+        onClick={() => openSearch()}
+      />
+
+      <Swiper
+        vertical
+        current={curSwiper}
+        style={{ height: "100vh" }}
+        onChange={(v) => {
+          setCurSwiper(v.detail.current);
+          swiperRef.current?.setIsRunning(v.detail.current === 0);
+        }}
+      >
+        <SwiperItem className="home-container">
+          <View className="home">
+            <View
+              className="home-main"
+              style={{
+                top: `${-offsetHeight}px`,
+              }}
+            >
+              {realItem && (
+                <View
+                  className="home__point"
+                  style={{
+                    top: pxTransform(realItem.pos.y),
+                    left: pxTransform(realItem.pos.x),
+                  }}
+                />
+              )}
+
+              <PointIcon />
+
+              <SightSwiper
+                ref={swiperRef}
+                curSwiperItem={curSwiperItem}
+                setCurSwiperItem={setCurSwiperItem}
+                handleDetail={handleDetail}
+              />
+
+              <View className="home-toggle">
+                <Image
+                  className="home-toggle__icon"
+                  src={LeftIcon}
+                  onClick={() => swiperRef.current?.toPrev()}
+                />
+
+                <View className="home-toggle-inner">
+                  <View className="home-toggle-label">
+                    <Text className="home-toggle-label__title limit-line">
+                      {realItem?.title}
+                    </Text>
+                    <Text>{realItem?.address}</Text>
+                  </View>
+
+                  <View
+                    className="home-toggle__detail"
+                    onClick={() => handleDetail()}
+                  />
+                </View>
+
+                <Image
+                  className="home-toggle__icon"
+                  src={RightIcon}
+                  onClick={() => swiperRef.current?.toNext()}
+                />
+              </View>
+            </View>
+
+            <View className="home-main__btn" onClick={() => setCurSwiper(1)}>
+              <View className="flashing">
+                <View>探索锡善云城</View>
+                <View className="reflect">探索锡善云城</View>
+              </View>
+            </View>
+
+            <Image
+              className="home__bg"
+              src="https://wuxicharitymuseum.cn/wx-csbwg-public/images/img_map%402x-min.jpg"
+              mode="widthFix"
+              style={{
+                top: `${-offsetHeight}px`,
+              }}
+            />
+            <Image className="home__btm-bg" src={BtmBgImg} mode="widthFix" />
+            <Image className="home__cloud" src={CloudImg} mode="heightFix" />
+          </View>
+        </SwiperItem>
+
+        <SwiperItem className="home2">
+          <Image
+            className="home2__btn"
+            src="https://wuxicharitymuseum.cn/wx-csbwg-public/images/btn_start%402x-min.png"
+            mode="widthFix"
+            onClick={async () => {
+              const userInfo = baseStore.userInfo;
+
+              if (!userInfo) {
+                await login();
+              }
+
+              if (userInfo.invite === 0) {
+                setVisitVisible(true);
+                return;
+              }
+
+              Taro.navigateTo({
+                url:
+                  "/pages/iframe/index?url=" +
+                  encodeURIComponent(getSceneUrl()),
+              });
+            }}
+          />
+
+          <Video
+            className="home2__bg"
+            src="https://wuxicharitymuseum.cn/wx-csbwg-public/videos/city.mp4"
+            autoplay
+            muted
+            loop
+            controls={false}
+            objectFit="cover"
+            enableProgressGesture={false}
+          />
+        </SwiperItem>
+      </Swiper>
+
+      {!loaded && (
+        <VideoWrap
+          className={classNames("home-loading")}
+          src="https://wuxicharitymuseum.cn/wx-csbwg-public/videos/ysdt.mp4"
+          autoplay
+          controls={false}
+          onLoadedMetaData={() => {
+            setVideoMetaLoaded(true);
+            setTimeout(() => {
+              setLoading(false);
+            }, 1000);
+          }}
+          onEnded={handleLoaded}
+          onError={handleLoaded}
+        />
+      )}
+
+      <SearchLayout
+        isOpened={showSearch}
+        onClose={() => {
+          setShowSearch(false);
+          if (curSwiper === 0) {
+            swiperRef.current?.setIsRunning(true);
+          }
+        }}
+        openDetail={(item) => {
+          swiperRef.current?.setIsRunning(false);
+          setItem(item);
+          setShowSight(true);
+        }}
+      />
+
+      {item && (
+        <SightDetailLayout
+          item={item}
+          isOpened={showSight}
+          onClose={closeDetail}
+        />
+      )}
+
+      <VisitCard
+        isOpened={visitVisible}
+        onClose={() => setVisitVisible(false)}
+      />
+
+      <View
+        className={classNames("ld-page", { hide: videoMetaLoaded })}
+        style={{ zIndex: loading ? 999 : -1 }}
+      >
+        <AtIcon
+          className="ld-page__icon"
+          value="loading"
+          color="#589498"
+          size={40}
+        />
+      </View>
+    </>
+  );
+};
+
+export default HomePage;

+ 5 - 5
src/pages/iframe/index.config.ts

@@ -1,5 +1,5 @@
-export default definePageConfig({
-  navigationBarTitleText: "锡善云城",
-  pageOrientation: "landscape",
-  navigationStyle: "custom",
-});
+export default definePageConfig({
+  navigationBarTitleText: "锡善云城",
+  pageOrientation: "landscape",
+  navigationStyle: "custom",
+});

+ 12 - 12
src/pages/iframe/index.tsx

@@ -1,12 +1,12 @@
-import { WebView } from "@tarojs/components";
-import { FC, useRouter } from "@tarojs/taro";
-
-const IframePage: FC = () => {
-  const route = useRouter();
-
-  return route.params.url ? (
-    <WebView src={decodeURIComponent(route.params.url)} />
-  ) : null;
-};
-
-export default IframePage;
+import { WebView } from "@tarojs/components";
+import { FC, useRouter } from "@tarojs/taro";
+
+const IframePage: FC = () => {
+  const route = useRouter();
+
+  return route.params.url ? (
+    <WebView src={decodeURIComponent(route.params.url)} />
+  ) : null;
+};
+
+export default IframePage;

+ 3 - 3
src/pages/login/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationBarTitleText: "登录",
-});
+export default definePageConfig({
+  navigationBarTitleText: "登录",
+});

+ 82 - 82
src/pages/login/index.scss

@@ -1,82 +1,82 @@
-.login {
-  position: relative;
-  height: 100vh;
-  overflow: hidden;
-
-  &__bg {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    z-index: -1;
-  }
-  &.is-loading {
-    background-image: url("https://houseoss.4dkankan.com/project/wx-csbwg-public/images/bg_loading%402x-min.png");
-  }
-  &-main {
-    position: absolute;
-    left: 100px;
-    right: 100px;
-    bottom: 116px;
-    background: transparent;
-
-    &__name {
-      display: flex;
-      gap: 14px;
-      height: 96px;
-    }
-  }
-  &__name-input {
-    flex: 1;
-    margin-left: 0;
-    padding-left: 34px;
-    height: inherit;
-    border-radius: 10px;
-    border: 2px solid white;
-    background: rgba(0, 0, 0, 0.75);
-    opacity: 0.5;
-    box-sizing: border-box;
-
-    .at-input__input {
-      font-size: 31px;
-      color: white;
-    }
-    .at-input__children::after {
-      display: none;
-    }
-    &__num {
-      color: white !important;
-    }
-  }
-  &__visitor {
-    flex-shrink: 0;
-    min-width: 162px;
-    height: inherit;
-    color: #876f52;
-    font-size: 31px;
-    color: white;
-    border: 2px solid rgba(255, 231, 148, 1);
-    border-radius: 10px;
-    background: rgba(184, 163, 87, 0.23);
-  }
-  &__wechat-login {
-    margin-top: 14px;
-    height: 96px;
-    color: white;
-    font-size: 31px;
-    border: 0;
-    background: #03c05e;
-    border-radius: 10px;
-
-    &__icon {
-      width: 57px;
-      height: 57px;
-    }
-    .at-button__text {
-      display: flex;
-      align-items: center;
-      gap: 24px;
-    }
-  }
-}
+.login {
+  position: relative;
+  height: 100vh;
+  overflow: hidden;
+
+  &__bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: -1;
+  }
+  &.is-loading {
+    background-image: url("https://wuxicharitymuseum.cn/wx-csbwg-public/images/bg_loading%402x-min.png");
+  }
+  &-main {
+    position: absolute;
+    left: 100px;
+    right: 100px;
+    bottom: 116px;
+    background: transparent;
+
+    &__name {
+      display: flex;
+      gap: 14px;
+      height: 96px;
+    }
+  }
+  &__name-input {
+    flex: 1;
+    margin-left: 0;
+    padding-left: 34px;
+    height: inherit;
+    border-radius: 10px;
+    border: 2px solid white;
+    background: rgba(0, 0, 0, 0.75);
+    opacity: 0.5;
+    box-sizing: border-box;
+
+    .at-input__input {
+      font-size: 31px;
+      color: white;
+    }
+    .at-input__children::after {
+      display: none;
+    }
+    &__num {
+      color: white !important;
+    }
+  }
+  &__visitor {
+    flex-shrink: 0;
+    min-width: 162px;
+    height: inherit;
+    color: #876f52;
+    font-size: 31px;
+    color: white;
+    border: 2px solid rgba(255, 231, 148, 1);
+    border-radius: 10px;
+    background: rgba(184, 163, 87, 0.23);
+  }
+  &__wechat-login {
+    margin-top: 14px;
+    height: 96px;
+    color: white;
+    font-size: 31px;
+    border: 0;
+    background: #03c05e;
+    border-radius: 10px;
+
+    &__icon {
+      width: 57px;
+      height: 57px;
+    }
+    .at-button__text {
+      display: flex;
+      align-items: center;
+      gap: 24px;
+    }
+  }
+}

+ 191 - 191
src/pages/login/index.tsx

@@ -1,191 +1,191 @@
-import { Text, View, Image, Video } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import { AtButton, AtForm, AtInput } from "taro-ui";
-import classNames from "classnames";
-import { useEffect, useState } from "react";
-import {
-  NICKNAME_KEY,
-  TOKEN_KEY,
-  createRandomString,
-  onValidateField,
-} from "../../utils";
-import WechatIcon from "../../images/icon_wechat@2x-min.png";
-import {
-  checkLoginApi,
-  setUserInfoApi,
-  validateNickName,
-  wechatLoginApi,
-} from "../../api";
-import baseStore from "../../store/base";
-import "./index.scss";
-
-const formRules = {
-  name: [{ required: true, message: "请输入昵称" }],
-};
-
-const LoginPage: FC = () => {
-  const [loading, setLoading] = useState(false);
-  const [wechatLoading, setWechatLoading] = useState(false);
-  const [name, setName] = useState(`云城居民${createRandomString()}`);
-
-  const handleValidate = async () => {
-    const errors = await onValidateField(
-      {
-        name,
-      },
-      formRules
-    );
-    let valid = !errors || errors.filter((error) => error.message).length <= 0;
-
-    if (!valid) {
-      Taro.showToast({
-        icon: "none",
-        title: errors[0].message,
-      });
-      return false;
-    }
-
-    await validateNickName(name);
-
-    return true;
-  };
-
-  const handleUserInfo = async (e) => {
-    // console.log(e);
-    if (!(await handleValidate())) return;
-
-    Taro.login({
-      async success({ code }) {
-        try {
-          setWechatLoading(true);
-          Taro.showLoading({
-            title: "登陆中...",
-          });
-
-          const data = await wechatLoginApi(code);
-          Taro.setStorageSync(TOKEN_KEY, data.token);
-
-          await setUserInfoApi({
-            id: data.user.id,
-            nickName: name,
-          });
-
-          Taro.setStorageSync(NICKNAME_KEY, name);
-          baseStore.name = name;
-          baseStore.isLogin = true;
-          goHomePage();
-        } finally {
-          setWechatLoading(false);
-          Taro.hideLoading();
-        }
-      },
-    });
-  };
-
-  const handleVisitor = async () => {
-    try {
-      if (!(await handleValidate())) return;
-
-      setLoading(true);
-      Taro.showLoading({
-        title: "登陆中...",
-      });
-
-      Taro.setStorageSync(NICKNAME_KEY, name);
-      baseStore.name = name;
-      goHomePage();
-    } finally {
-      setLoading(false);
-      Taro.hideLoading();
-    }
-  };
-
-  const checkLogin = async () => {
-    try {
-      Taro.showLoading({
-        title: "登录中...",
-      });
-      const data = await checkLoginApi();
-
-      if (data) {
-        baseStore.isLogin = true;
-        goHomePage();
-      } else {
-        Taro.removeStorageSync(TOKEN_KEY);
-        Taro.removeStorageSync(NICKNAME_KEY);
-      }
-    } finally {
-      Taro.hideLoading();
-    }
-  };
-
-  const goHomePage = () => {
-    Taro.switchTab({
-      url: "/pages/home/index",
-    });
-  };
-
-  useEffect(() => {
-    const token = Taro.getStorageSync(TOKEN_KEY);
-
-    if (token) {
-      checkLogin();
-    }
-  }, []);
-
-  return (
-    <View className={classNames("login")}>
-      <AtForm
-        className="login-main margin-safe-area-bottom"
-        onSubmit={handleVisitor}
-      >
-        <View className="login-main__name">
-          <AtInput
-            value={name}
-            className="login__name-input"
-            name="name"
-            maxLength={6}
-            // @ts-ignore
-            type="nickname"
-            placeholder="请输入昵称"
-            onChange={(val) => {
-              setName(val as string);
-            }}
-          >
-            <Text className="login__name-input__num">{name.length}/6</Text>
-          </AtInput>
-
-          <AtButton
-            loading={loading}
-            className="login__visitor"
-            formType="submit"
-          >
-            游客模式
-          </AtButton>
-        </View>
-
-        <AtButton
-          loading={wechatLoading}
-          className="login__wechat-login"
-          openType="getUserInfo"
-          onGetUserInfo={handleUserInfo}
-        >
-          <Image className="login__wechat-login__icon" src={WechatIcon} />
-          微信一键登录
-        </AtButton>
-      </AtForm>
-
-      <Video
-        className="login__bg"
-        src="https://houseoss.4dkankan.com/project/wx-csbwg-public/videos/login.mp4"
-        autoplay
-        loop
-        muted
-        controls={false}
-        objectFit="cover"
-      />
-    </View>
-  );
-};
-
-export default LoginPage;
+import { Text, View, Image, Video } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import { AtButton, AtForm, AtInput } from "taro-ui";
+import classNames from "classnames";
+import { useEffect, useState } from "react";
+import {
+  NICKNAME_KEY,
+  TOKEN_KEY,
+  createRandomString,
+  onValidateField,
+} from "../../utils";
+import WechatIcon from "../../images/icon_wechat@2x-min.png";
+import {
+  checkLoginApi,
+  setUserInfoApi,
+  validateNickName,
+  wechatLoginApi,
+} from "../../api";
+import baseStore from "../../store/base";
+import "./index.scss";
+
+const formRules = {
+  name: [{ required: true, message: "请输入昵称" }],
+};
+
+const LoginPage: FC = () => {
+  const [loading, setLoading] = useState(false);
+  const [wechatLoading, setWechatLoading] = useState(false);
+  const [name, setName] = useState(`云城居民${createRandomString()}`);
+
+  const handleValidate = async () => {
+    const errors = await onValidateField(
+      {
+        name,
+      },
+      formRules
+    );
+    let valid = !errors || errors.filter((error) => error.message).length <= 0;
+
+    if (!valid) {
+      Taro.showToast({
+        icon: "none",
+        title: errors[0].message,
+      });
+      return false;
+    }
+
+    await validateNickName(name);
+
+    return true;
+  };
+
+  const handleUserInfo = async (e) => {
+    // console.log(e);
+    if (!(await handleValidate())) return;
+
+    Taro.login({
+      async success({ code }) {
+        try {
+          setWechatLoading(true);
+          Taro.showLoading({
+            title: "登陆中...",
+          });
+
+          const data = await wechatLoginApi(code);
+          Taro.setStorageSync(TOKEN_KEY, data.token);
+
+          await setUserInfoApi({
+            id: data.user.id,
+            nickName: name,
+          });
+
+          Taro.setStorageSync(NICKNAME_KEY, name);
+          baseStore.name = name;
+          baseStore.isLogin = true;
+          goHomePage();
+        } finally {
+          setWechatLoading(false);
+          Taro.hideLoading();
+        }
+      },
+    });
+  };
+
+  const handleVisitor = async () => {
+    try {
+      if (!(await handleValidate())) return;
+
+      setLoading(true);
+      Taro.showLoading({
+        title: "登陆中...",
+      });
+
+      Taro.setStorageSync(NICKNAME_KEY, name);
+      baseStore.name = name;
+      goHomePage();
+    } finally {
+      setLoading(false);
+      Taro.hideLoading();
+    }
+  };
+
+  const checkLogin = async () => {
+    try {
+      Taro.showLoading({
+        title: "登录中...",
+      });
+      const data = await checkLoginApi();
+
+      if (data) {
+        baseStore.isLogin = true;
+        goHomePage();
+      } else {
+        Taro.removeStorageSync(TOKEN_KEY);
+        Taro.removeStorageSync(NICKNAME_KEY);
+      }
+    } finally {
+      Taro.hideLoading();
+    }
+  };
+
+  const goHomePage = () => {
+    Taro.switchTab({
+      url: "/pages/home/index",
+    });
+  };
+
+  useEffect(() => {
+    const token = Taro.getStorageSync(TOKEN_KEY);
+
+    if (token) {
+      checkLogin();
+    }
+  }, []);
+
+  return (
+    <View className={classNames("login")}>
+      <AtForm
+        className="login-main margin-safe-area-bottom"
+        onSubmit={handleVisitor}
+      >
+        <View className="login-main__name">
+          <AtInput
+            value={name}
+            className="login__name-input"
+            name="name"
+            maxLength={6}
+            // @ts-ignore
+            type="nickname"
+            placeholder="请输入昵称"
+            onChange={(val) => {
+              setName(val as string);
+            }}
+          >
+            <Text className="login__name-input__num">{name.length}/6</Text>
+          </AtInput>
+
+          <AtButton
+            loading={loading}
+            className="login__visitor"
+            formType="submit"
+          >
+            游客模式
+          </AtButton>
+        </View>
+
+        <AtButton
+          loading={wechatLoading}
+          className="login__wechat-login"
+          openType="getUserInfo"
+          onGetUserInfo={handleUserInfo}
+        >
+          <Image className="login__wechat-login__icon" src={WechatIcon} />
+          微信一键登录
+        </AtButton>
+      </AtForm>
+
+      <Video
+        className="login__bg"
+        src="https://wuxicharitymuseum.cn/wx-csbwg-public/videos/login.mp4"
+        autoplay
+        loop
+        muted
+        controls={false}
+        objectFit="cover"
+      />
+    </View>
+  );
+};
+
+export default LoginPage;

+ 3 - 3
src/pages/tempp/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationStyle: "custom",
-});
+export default definePageConfig({
+  navigationStyle: "custom",
+});

+ 14 - 14
src/pages/tempp/index.tsx

@@ -1,14 +1,14 @@
-import Taro, { FC } from "@tarojs/taro";
-import { useEffect } from "react";
-
-const TempPage: FC = () => {
-  useEffect(() => {
-    Taro.redirectTo({
-      url: "/pages/banner/index",
-    });
-  }, []);
-
-  return null;
-};
-
-export default TempPage;
+import Taro, { FC } from "@tarojs/taro";
+import { useEffect } from "react";
+
+const TempPage: FC = () => {
+  useEffect(() => {
+    Taro.redirectTo({
+      url: "/pages/banner/index",
+    });
+  }, []);
+
+  return null;
+};
+
+export default TempPage;

+ 13 - 13
src/store/base.ts

@@ -1,13 +1,13 @@
-import Taro from "@tarojs/taro";
-import { observable } from "mobx";
-import { NICKNAME_KEY, USER_INFO_KEY } from "../utils";
-
-const baseStore = observable({
-  name: Taro.getStorageSync(NICKNAME_KEY) ?? "",
-  userInfo: Taro.getStorageSync(USER_INFO_KEY),
-  isLogin: false,
-
-  shopTips: "",
-});
-
-export default baseStore;
+import Taro from "@tarojs/taro";
+import { observable } from "mobx";
+import { NICKNAME_KEY, USER_INFO_KEY } from "../utils";
+
+const baseStore = observable({
+  name: Taro.getStorageSync(NICKNAME_KEY) ?? "",
+  userInfo: Taro.getStorageSync(USER_INFO_KEY),
+  isLogin: false,
+
+  shopTips: "",
+});
+
+export default baseStore;

+ 10 - 10
src/store/index.ts

@@ -1,10 +1,10 @@
-import React from "react";
-import baseStore from "./base";
-
-const stores = React.createContext({
-  base: baseStore,
-});
-
-export const useStores = () => React.useContext(stores);
-
-export default stores;
+import React from "react";
+import baseStore from "./base";
+
+const stores = React.createContext({
+  base: baseStore,
+});
+
+export const useStores = () => React.useContext(stores);
+
+export default stores;

+ 61 - 61
src/subModule/components/CertLayout/index.scss

@@ -1,61 +1,61 @@
-.cert {
-  .at-float-layout__overlay {
-    background: rgba(0, 0, 0, 0.65);
-    backdrop-filter: blur(30px);
-  }
-  .at-float-layout__container {
-    max-height: 100vh;
-    height: 100vh;
-    background-color: transparent;
-  }
-  .layout-body {
-    padding: 0;
-    // padding-bottom: constant(safe-area-inset-bottom);
-    // padding-bottom: env(safe-area-inset-bottom);
-    max-height: unset;
-    min-height: unset;
-    height: 100%;
-    box-sizing: border-box;
-  }
-  .layout-body__content {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    max-height: unset;
-    min-height: unset;
-    height: 100%;
-  }
-
-  &-wrap {
-    position: relative;
-    display: flex;
-    gap: 38px;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    padding: 85px 0 210px;
-    height: 100%;
-    box-sizing: border-box;
-    z-index: 2;
-  }
-
-  &__tips {
-    font-size: 31px;
-    color: white;
-  }
-
-  &__close {
-    position: absolute;
-    left: 50%;
-    bottom: 100px;
-    width: 80px;
-    height: 80px;
-    transform: translateX(-50%);
-    z-index: 2;
-  }
-
-  &__img {
-    width: 750px;
-    height: 516px;
-  }
-}
+.cert {
+  .at-float-layout__overlay {
+    background: rgba(0, 0, 0, 0.65);
+    backdrop-filter: blur(30px);
+  }
+  .at-float-layout__container {
+    max-height: 100vh;
+    height: 100vh;
+    background-color: transparent;
+  }
+  .layout-body {
+    padding: 0;
+    // padding-bottom: constant(safe-area-inset-bottom);
+    // padding-bottom: env(safe-area-inset-bottom);
+    max-height: unset;
+    min-height: unset;
+    height: 100%;
+    box-sizing: border-box;
+  }
+  .layout-body__content {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    max-height: unset;
+    min-height: unset;
+    height: 100%;
+  }
+
+  &-wrap {
+    position: relative;
+    display: flex;
+    gap: 38px;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 85px 0 210px;
+    height: 100%;
+    box-sizing: border-box;
+    z-index: 2;
+  }
+
+  &__tips {
+    font-size: 31px;
+    color: white;
+  }
+
+  &__close {
+    position: absolute;
+    left: 50%;
+    bottom: 100px;
+    width: 80px;
+    height: 80px;
+    transform: translateX(-50%);
+    z-index: 2;
+  }
+
+  &__img {
+    width: 750px;
+    height: 516px;
+  }
+}

+ 223 - 223
src/subModule/components/CertLayout/index.tsx

@@ -1,223 +1,223 @@
-import { Canvas, Image, View } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import { useEffect, useRef, useState } from "react";
-import { AtFloatLayout } from "taro-ui";
-import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
-import CloseIcon from "../../../images/icon_back@2x-min.png";
-import "./index.scss";
-
-export interface CertLayoutProps extends AtFloatLayoutProps {
-  name: string;
-  date: string;
-}
-
-const system = Taro.getSystemInfoSync();
-
-export const CertLayout: FC<CertLayoutProps> = ({ name, date, ...props }) => {
-  const loaded = useRef(false);
-  const [imgPath, setImgPath] = useState("");
-
-  useEffect(() => {
-    if (props.isOpened && !loaded.current) init();
-  }, [props.isOpened]);
-
-  const init = async () => {
-    try {
-      Taro.showLoading({
-        title: "绘制中",
-      });
-
-      await new Promise((resolve, reject) => {
-        Taro.createSelectorQuery()
-          .select("#certCanvas")
-          .fields({ node: true, size: true }, async (res) => {
-            const canvas = res.node;
-            const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
-
-            canvas.width = res.width * system.pixelRatio;
-            canvas.height = res.height * system.pixelRatio;
-
-            try {
-              const bgInfo = await getTempImgPath(
-                "https://houseoss.4dkankan.com/project/wx-csbwg-public/images/cert-min.png"
-              );
-              const ratio = bgInfo.width / canvas.width;
-
-              const bgSource = await loadImg(canvas, bgInfo.path);
-
-              ctx.drawImage(bgSource, 0, 0, canvas.width, canvas.height);
-
-              ctxText(
-                ctx,
-                "特此授予锡善云城·热心市民".split("").join(" "),
-                `${7 * system.pixelRatio}px SourceHanSansSCRegular`,
-                "center",
-                "#424A4A",
-                canvas.width / 2,
-                800 / ratio
-              );
-
-              ctxText(
-                ctx,
-                name.split("").join(" "),
-                `${15 * system.pixelRatio}px SourceHanSansCN-Bold`,
-                "center",
-                "#CFC49E",
-                canvas.width / 2,
-                1020 / ratio
-              );
-
-              ctxTextWrap(
-                ctx,
-                "特此证明,感谢您的热情参与与慷慨支持。您的善举为社区带来了无限的温暖与希望。因您的无私奉献,特授予您热心市民称号,以表彰您对慈善事业的贡献。您的善行将永远激励着我们,为构建更美好的社会贡献一份力量。",
-                `${7 * system.pixelRatio}px SourceHanSansSCRegular`,
-                "center",
-                "#424A4A",
-                canvas.width / 2,
-                1200 / ratio,
-                460 * system.pixelRatio
-              );
-
-              ctxText(
-                ctx,
-                date,
-                `${6 * system.pixelRatio}px SourceHanSerifCN-Bold`,
-                "left",
-                "#424A4A",
-                740 / ratio,
-                1660 / ratio
-              );
-
-              ctxText(
-                ctx,
-                "锡善云城",
-                `${6 * system.pixelRatio}px SourceHanSerifCN-Bold`,
-                "left",
-                "#424A4A",
-                1880 / ratio,
-                1660 / ratio
-              );
-
-              await new Promise((resolve, reject) => {
-                Taro.canvasToTempFilePath({
-                  canvas,
-                  fileType: "jpg",
-                  success(res2) {
-                    setImgPath(res2.tempFilePath);
-
-                    loaded.current = true;
-                    resolve(true);
-                  },
-                  fail(err) {
-                    Taro.showToast({
-                      title: err.errMsg,
-                      icon: "none",
-                      duration: 4000,
-                    });
-                    reject(err);
-                  },
-                });
-              });
-
-              resolve(true);
-            } catch (err) {
-              reject(false);
-            }
-          })
-          .exec();
-      });
-    } finally {
-      Taro.hideLoading();
-    }
-  };
-
-  const loadImg = (canvas, src) => {
-    return new Promise((res, rej) => {
-      const source = canvas.createImage();
-      source.src = src;
-      source.onload = () => {
-        res(source);
-      };
-      source.onerror = rej;
-    }) as Promise<CanvasImageSource>;
-  };
-
-  const ctxTextWrap = (ctx, text, font, align, color, x, y, maxWidth) => {
-    let line = "";
-    const lines: string[] = [];
-    const words = text.split("");
-    for (let i = 0; i < words.length; i++) {
-      const word = words[i];
-      const testLine = line + word;
-      const metrics = ctx.measureText(testLine);
-
-      if (metrics.width > maxWidth && i > 0) {
-        lines.push(line);
-        line = word;
-      } else {
-        line = testLine;
-      }
-    }
-    lines.push(line);
-
-    lines.forEach((t, idx) =>
-      ctxText(ctx, t, font, align, color, x, y + idx * 28)
-    );
-  };
-
-  const ctxText = (ctx, text, font, align, color, x, y) => {
-    ctx.beginPath();
-    ctx.font = font;
-    ctx.textAlign = align;
-    ctx.fillStyle = color;
-    ctx.fillText(text, x, y);
-    ctx.save();
-  };
-
-  const getTempImgPath = async (src: string) => {
-    return new Promise((resolve, reject) => {
-      Taro.getImageInfo({
-        src,
-        success(res) {
-          resolve(res);
-        },
-        fail(err) {
-          reject(err);
-        },
-      });
-    }) as Promise<Taro.getImageInfo.SuccessCallbackResult>;
-  };
-
-  return (
-    <AtFloatLayout className="cert" {...props}>
-      <View className="cert-wrap">
-        {imgPath ? (
-          <>
-            <Image
-              className="cert__img"
-              src={imgPath}
-              showMenuByLongpress
-              onClick={() => {
-                Taro.previewImage({
-                  current: imgPath,
-                  urls: [imgPath],
-                });
-              }}
-            />
-
-            <View className="cert__tips">长按图片,保存证书</View>
-          </>
-        ) : (
-          <Canvas
-            id="certCanvas"
-            type="2d"
-            className="cert__img"
-            style="position:absolute; top: -200%; left: -200%"
-          />
-        )}
-      </View>
-
-      <Image className="cert__close" src={CloseIcon} onClick={props.onClose} />
-    </AtFloatLayout>
-  );
-};
+import { Canvas, Image, View } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import { useEffect, useRef, useState } from "react";
+import { AtFloatLayout } from "taro-ui";
+import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
+import CloseIcon from "../../../images/icon_back@2x-min.png";
+import "./index.scss";
+
+export interface CertLayoutProps extends AtFloatLayoutProps {
+  name: string;
+  date: string;
+}
+
+const system = Taro.getSystemInfoSync();
+
+export const CertLayout: FC<CertLayoutProps> = ({ name, date, ...props }) => {
+  const loaded = useRef(false);
+  const [imgPath, setImgPath] = useState("");
+
+  useEffect(() => {
+    if (props.isOpened && !loaded.current) init();
+  }, [props.isOpened]);
+
+  const init = async () => {
+    try {
+      Taro.showLoading({
+        title: "绘制中",
+      });
+
+      await new Promise((resolve, reject) => {
+        Taro.createSelectorQuery()
+          .select("#certCanvas")
+          .fields({ node: true, size: true }, async (res) => {
+            const canvas = res.node;
+            const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
+
+            canvas.width = res.width * system.pixelRatio;
+            canvas.height = res.height * system.pixelRatio;
+
+            try {
+              const bgInfo = await getTempImgPath(
+                "https://wuxicharitymuseum.cn/wx-csbwg-public/images/cert-min.png"
+              );
+              const ratio = bgInfo.width / canvas.width;
+
+              const bgSource = await loadImg(canvas, bgInfo.path);
+
+              ctx.drawImage(bgSource, 0, 0, canvas.width, canvas.height);
+
+              ctxText(
+                ctx,
+                "特此授予锡善云城·热心市民".split("").join(" "),
+                `${7 * system.pixelRatio}px SourceHanSansSCRegular`,
+                "center",
+                "#424A4A",
+                canvas.width / 2,
+                800 / ratio
+              );
+
+              ctxText(
+                ctx,
+                name.split("").join(" "),
+                `${15 * system.pixelRatio}px SourceHanSansCN-Bold`,
+                "center",
+                "#CFC49E",
+                canvas.width / 2,
+                1020 / ratio
+              );
+
+              ctxTextWrap(
+                ctx,
+                "特此证明,感谢您的热情参与与慷慨支持。您的善举为社区带来了无限的温暖与希望。因您的无私奉献,特授予您热心市民称号,以表彰您对慈善事业的贡献。您的善行将永远激励着我们,为构建更美好的社会贡献一份力量。",
+                `${7 * system.pixelRatio}px SourceHanSansSCRegular`,
+                "center",
+                "#424A4A",
+                canvas.width / 2,
+                1200 / ratio,
+                460 * system.pixelRatio
+              );
+
+              ctxText(
+                ctx,
+                date,
+                `${6 * system.pixelRatio}px SourceHanSerifCN-Bold`,
+                "left",
+                "#424A4A",
+                740 / ratio,
+                1660 / ratio
+              );
+
+              ctxText(
+                ctx,
+                "锡善云城",
+                `${6 * system.pixelRatio}px SourceHanSerifCN-Bold`,
+                "left",
+                "#424A4A",
+                1880 / ratio,
+                1660 / ratio
+              );
+
+              await new Promise((resolve, reject) => {
+                Taro.canvasToTempFilePath({
+                  canvas,
+                  fileType: "jpg",
+                  success(res2) {
+                    setImgPath(res2.tempFilePath);
+
+                    loaded.current = true;
+                    resolve(true);
+                  },
+                  fail(err) {
+                    Taro.showToast({
+                      title: err.errMsg,
+                      icon: "none",
+                      duration: 4000,
+                    });
+                    reject(err);
+                  },
+                });
+              });
+
+              resolve(true);
+            } catch (err) {
+              reject(false);
+            }
+          })
+          .exec();
+      });
+    } finally {
+      Taro.hideLoading();
+    }
+  };
+
+  const loadImg = (canvas, src) => {
+    return new Promise((res, rej) => {
+      const source = canvas.createImage();
+      source.src = src;
+      source.onload = () => {
+        res(source);
+      };
+      source.onerror = rej;
+    }) as Promise<CanvasImageSource>;
+  };
+
+  const ctxTextWrap = (ctx, text, font, align, color, x, y, maxWidth) => {
+    let line = "";
+    const lines: string[] = [];
+    const words = text.split("");
+    for (let i = 0; i < words.length; i++) {
+      const word = words[i];
+      const testLine = line + word;
+      const metrics = ctx.measureText(testLine);
+
+      if (metrics.width > maxWidth && i > 0) {
+        lines.push(line);
+        line = word;
+      } else {
+        line = testLine;
+      }
+    }
+    lines.push(line);
+
+    lines.forEach((t, idx) =>
+      ctxText(ctx, t, font, align, color, x, y + idx * 28)
+    );
+  };
+
+  const ctxText = (ctx, text, font, align, color, x, y) => {
+    ctx.beginPath();
+    ctx.font = font;
+    ctx.textAlign = align;
+    ctx.fillStyle = color;
+    ctx.fillText(text, x, y);
+    ctx.save();
+  };
+
+  const getTempImgPath = async (src: string) => {
+    return new Promise((resolve, reject) => {
+      Taro.getImageInfo({
+        src,
+        success(res) {
+          resolve(res);
+        },
+        fail(err) {
+          reject(err);
+        },
+      });
+    }) as Promise<Taro.getImageInfo.SuccessCallbackResult>;
+  };
+
+  return (
+    <AtFloatLayout className="cert" {...props}>
+      <View className="cert-wrap">
+        {imgPath ? (
+          <>
+            <Image
+              className="cert__img"
+              src={imgPath}
+              showMenuByLongpress
+              onClick={() => {
+                Taro.previewImage({
+                  current: imgPath,
+                  urls: [imgPath],
+                });
+              }}
+            />
+
+            <View className="cert__tips">长按图片,保存证书</View>
+          </>
+        ) : (
+          <Canvas
+            id="certCanvas"
+            type="2d"
+            className="cert__img"
+            style="position:absolute; top: -200%; left: -200%"
+          />
+        )}
+      </View>
+
+      <Image className="cert__close" src={CloseIcon} onClick={props.onClose} />
+    </AtFloatLayout>
+  );
+};

+ 3 - 3
src/subModule/pages/document/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationBarTitleText: "文档",
-});
+export default definePageConfig({
+  navigationBarTitleText: "文档",
+});

+ 63 - 63
src/subModule/pages/document/index.tsx

@@ -1,63 +1,63 @@
-import Taro, { FC, useDidHide, useDidShow, useRouter } from "@tarojs/taro";
-import { useEffect, useRef } from "react";
-
-const DocumentPage: FC = () => {
-  const route = useRouter();
-  const goBack = useRef(false);
-
-  useEffect(() => {
-    if (!route.params.url) {
-      Taro.showToast({
-        title: "请传入url",
-        icon: "error",
-        duration: 3000,
-      });
-      return;
-    }
-
-    Taro.showLoading({
-      title: "加载中",
-    });
-
-    Taro.downloadFile({
-      url: decodeURIComponent(route.params.url),
-      success(res) {
-        Taro.openDocument({
-          filePath: res.tempFilePath,
-          // @ts-ignore
-          fileType: route.params.fileType || "pdf",
-          showMenu: true,
-          success() {
-            Taro.hideLoading();
-          },
-          fail(res) {
-            Taro.showToast({
-              title: res.errMsg,
-              icon: "none",
-              duration: 3000,
-            });
-          },
-        });
-      },
-      fail(res) {
-        Taro.showToast({
-          title: res.errMsg,
-          icon: "none",
-          duration: 3000,
-        });
-      },
-    });
-  }, []);
-
-  useDidShow(() => {
-    goBack.current && Taro.navigateBack();
-  });
-
-  useDidHide(() => {
-    goBack.current = true;
-  });
-
-  return null;
-};
-
-export default DocumentPage;
+import Taro, { FC, useDidHide, useDidShow, useRouter } from "@tarojs/taro";
+import { useEffect, useRef } from "react";
+
+const DocumentPage: FC = () => {
+  const route = useRouter();
+  const goBack = useRef(false);
+
+  useEffect(() => {
+    if (!route.params.url) {
+      Taro.showToast({
+        title: "请传入url",
+        icon: "error",
+        duration: 3000,
+      });
+      return;
+    }
+
+    Taro.showLoading({
+      title: "加载中",
+    });
+
+    Taro.downloadFile({
+      url: decodeURIComponent(route.params.url),
+      success(res) {
+        Taro.openDocument({
+          filePath: res.tempFilePath,
+          // @ts-ignore
+          fileType: route.params.fileType || "pdf",
+          showMenu: true,
+          success() {
+            Taro.hideLoading();
+          },
+          fail(res) {
+            Taro.showToast({
+              title: res.errMsg,
+              icon: "none",
+              duration: 3000,
+            });
+          },
+        });
+      },
+      fail(res) {
+        Taro.showToast({
+          title: res.errMsg,
+          icon: "none",
+          duration: 3000,
+        });
+      },
+    });
+  }, []);
+
+  useDidShow(() => {
+    goBack.current && Taro.navigateBack();
+  });
+
+  useDidHide(() => {
+    goBack.current = true;
+  });
+
+  return null;
+};
+
+export default DocumentPage;

+ 3 - 3
src/subModule/pages/feedback/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationBarTitleText: "用户反馈",
-});
+export default definePageConfig({
+  navigationBarTitleText: "用户反馈",
+});

+ 159 - 159
src/subModule/pages/feedback/index.scss

@@ -1,159 +1,159 @@
-.feedback-page {
-  padding: 6px 0 0 6px;
-  height: 100vh;
-  box-sizing: border-box;
-  background: linear-gradient(#b9d6d3, #fafbeb);
-}
-
-.feedback {
-  position: relative;
-
-  &::before {
-    content: "";
-    position: absolute;
-    top: 0;
-    left: 25px;
-    width: 35px;
-    height: 130px;
-    background: url("../../images/Userfeedback@2x-min.png") no-repeat center /
-      contain;
-  }
-  &__bg {
-    width: 100%;
-  }
-  &-main {
-    position: absolute;
-    top: 53px;
-    left: 69px;
-    right: 52px;
-    bottom: 0;
-    z-index: 1;
-    background: unset;
-
-    &__form {
-      padding: 0 24px;
-
-      &:first-child {
-        margin-bottom: 38px;
-        padding-bottom: 38px;
-        border-bottom: 2px solid #d5ddd7;
-      }
-    }
-  }
-  &__label {
-    font-family: "SourceHanSerifCN-Bold";
-    font-size: 38px;
-    color: #424a4a;
-  }
-  &__input {
-    margin: 20px 0 38px;
-    padding: 2px;
-    width: 100%;
-    height: 100px;
-    box-sizing: border-box;
-    border-radius: 10px;
-    overflow: hidden;
-    background: linear-gradient(
-      180deg,
-      rgba(255, 255, 255, 1),
-      rgba(255, 255, 255, 0.15)
-    );
-
-    .at-input__container {
-      width: 100%;
-      height: 100%;
-      background-color: rgba(213, 221, 215, 0.5);
-      border-radius: 10px;
-    }
-    .at-input__input {
-      padding: 0 38px;
-      font-size: 31px;
-    }
-
-    &.textarea {
-      position: relative;
-      margin-bottom: 0;
-      padding: 2px;
-      height: 385px;
-      border: 0;
-
-      .at-textarea__textarea {
-        position: relative;
-        padding: 30px 38px;
-        height: 100%;
-        box-sizing: border-box;
-        border-radius: 10px;
-        background-color: rgba(213, 221, 215, 0.5);
-        z-index: 1;
-      }
-      &::after {
-        content: "";
-        position: absolute;
-        top: 0;
-        left: 0;
-        right: 0;
-        bottom: 0;
-        background: linear-gradient(
-          180deg,
-          rgba(255, 255, 255, 1),
-          rgba(255, 255, 255, 0.15)
-        );
-      }
-    }
-  }
-
-  .code {
-    display: flex;
-    align-items: center;
-    margin: 20px 0 40px;
-    gap: 20px;
-
-    .feedback__input {
-      flex: 1;
-      margin: 0;
-    }
-    &__img {
-      flex-shrink: 0;
-      width: 256px;
-      height: 100px;
-    }
-  }
-
-  .toolbar {
-    position: relative;
-    display: flex;
-    justify-content: center;
-    gap: 46px;
-
-    &::before {
-      content: "";
-      position: absolute;
-      left: 0;
-      right: 0;
-      bottom: 0;
-      height: 40px;
-      z-index: 900;
-    }
-    &__btn {
-      width: 262px;
-      height: 128px;
-      border: 0;
-      font-size: 31px;
-
-      .at-button__text {
-        position: relative;
-        top: 12px;
-      }
-      &.cancel {
-        color: #525252;
-        background: url("../../images/btn_L_whtie@2x-min.png") no-repeat center /
-          contain;
-      }
-      &.submit {
-        color: #9c6d42;
-        background: url("../../images/btn_L_yellow@2x-min.png") no-repeat center /
-          contain;
-      }
-    }
-  }
-}
+.feedback-page {
+  padding: 6px 0 0 6px;
+  height: 100vh;
+  box-sizing: border-box;
+  background: linear-gradient(#b9d6d3, #fafbeb);
+}
+
+.feedback {
+  position: relative;
+
+  &::before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 25px;
+    width: 35px;
+    height: 130px;
+    background: url("../../images/Userfeedback@2x-min.png") no-repeat center /
+      contain;
+  }
+  &__bg {
+    width: 100%;
+  }
+  &-main {
+    position: absolute;
+    top: 53px;
+    left: 69px;
+    right: 52px;
+    bottom: 0;
+    z-index: 1;
+    background: unset;
+
+    &__form {
+      padding: 0 24px;
+
+      &:first-child {
+        margin-bottom: 38px;
+        padding-bottom: 38px;
+        border-bottom: 2px solid #d5ddd7;
+      }
+    }
+  }
+  &__label {
+    font-family: "SourceHanSerifCN-Bold";
+    font-size: 38px;
+    color: #424a4a;
+  }
+  &__input {
+    margin: 20px 0 38px;
+    padding: 2px;
+    width: 100%;
+    height: 100px;
+    box-sizing: border-box;
+    border-radius: 10px;
+    overflow: hidden;
+    background: linear-gradient(
+      180deg,
+      rgba(255, 255, 255, 1),
+      rgba(255, 255, 255, 0.15)
+    );
+
+    .at-input__container {
+      width: 100%;
+      height: 100%;
+      background-color: rgba(213, 221, 215, 0.5);
+      border-radius: 10px;
+    }
+    .at-input__input {
+      padding: 0 38px;
+      font-size: 31px;
+    }
+
+    &.textarea {
+      position: relative;
+      margin-bottom: 0;
+      padding: 2px;
+      height: 385px;
+      border: 0;
+
+      .at-textarea__textarea {
+        position: relative;
+        padding: 30px 38px;
+        height: 100%;
+        box-sizing: border-box;
+        border-radius: 10px;
+        background-color: rgba(213, 221, 215, 0.5);
+        z-index: 1;
+      }
+      &::after {
+        content: "";
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: linear-gradient(
+          180deg,
+          rgba(255, 255, 255, 1),
+          rgba(255, 255, 255, 0.15)
+        );
+      }
+    }
+  }
+
+  .code {
+    display: flex;
+    align-items: center;
+    margin: 20px 0 40px;
+    gap: 20px;
+
+    .feedback__input {
+      flex: 1;
+      margin: 0;
+    }
+    &__img {
+      flex-shrink: 0;
+      width: 256px;
+      height: 100px;
+    }
+  }
+
+  .toolbar {
+    position: relative;
+    display: flex;
+    justify-content: center;
+    gap: 46px;
+
+    &::before {
+      content: "";
+      position: absolute;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      height: 40px;
+      z-index: 900;
+    }
+    &__btn {
+      width: 262px;
+      height: 128px;
+      border: 0;
+      font-size: 31px;
+
+      .at-button__text {
+        position: relative;
+        top: 12px;
+      }
+      &.cancel {
+        color: #525252;
+        background: url("../../images/btn_L_whtie@2x-min.png") no-repeat center /
+          contain;
+      }
+      &.submit {
+        color: #9c6d42;
+        background: url("../../images/btn_L_yellow@2x-min.png") no-repeat center /
+          contain;
+      }
+    }
+  }
+}

+ 177 - 177
src/subModule/pages/feedback/index.tsx

@@ -1,177 +1,177 @@
-import { useState } from "react";
-import { Image, Label, View } from "@tarojs/components";
-import Taro, { FC } from "@tarojs/taro";
-import { AtButton, AtForm, AtInput, AtTextarea } from "taro-ui";
-import BgImg from "../../images/feedback@2x-min.png";
-import { handleValidate } from "../../../utils";
-import { getBaseURL } from "@dage/service";
-import { Rules } from "async-validator";
-import { feedbackApi } from "../../../api";
-import { FooterProtocol } from "../../../components/FooterProtocol";
-import "./index.scss";
-
-const DEFAULT_PARAMS = {
-  name: "",
-  phone: "",
-  content: "",
-  randCode: "",
-};
-
-const formRules: Rules = {
-  name: [{ required: true, message: "请输入称呼" }],
-  phone: [{ required: true, message: "请输入联系方式" }],
-  content: [{ required: true, message: "请输入反馈内容" }],
-  randCode: [
-    { required: true, message: "请输入验证码" },
-    {
-      validator: (_, value) => {
-        return value.length === 5;
-      },
-      message: "验证码输入有误",
-    },
-  ],
-};
-
-const FeedBackPage: FC = () => {
-  const [checked, setChecked] = useState(false);
-  const [params, setParams] = useState({ ...DEFAULT_PARAMS });
-  const [timestamp, setTimestamp] = useState(new Date().getTime());
-  const [loading, setLoading] = useState(false);
-
-  const handleSubmit = async (event, readProtocol = false) => {
-    if (!(await handleValidate(params, formRules))) return;
-
-    if (!checked && !readProtocol) {
-      Taro.showModal({
-        title: "提示",
-        content: "是否已阅读并同意《用户服务协议》及《个人信息保护政策》",
-        confirmText: "同意",
-        success: (res) => {
-          if (res.confirm) {
-            setChecked(true);
-            handleSubmit(undefined, true);
-          }
-        },
-      });
-      return;
-    }
-
-    try {
-      setLoading(true);
-      await feedbackApi(params);
-
-      setParams({ ...DEFAULT_PARAMS });
-      setTimestamp(new Date().getTime());
-      Taro.showToast({
-        title: "提交成功",
-        icon: "success",
-      });
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  return (
-    <View className="feedback-page">
-      <View className="feedback">
-        <AtForm className="feedback-main" onSubmit={handleSubmit}>
-          <View className="feedback-main__form">
-            <Label className="feedback__label">您的称呼</Label>
-            <AtInput
-              name="name"
-              className="feedback__input"
-              maxLength={20}
-              placeholder="请输入内容,20字以内"
-              value={params.name}
-              onChange={(val) =>
-                setParams({
-                  ...params,
-                  name: val as string,
-                })
-              }
-            />
-
-            <Label className="feedback__label">联系方式</Label>
-            <AtInput
-              name="phone"
-              type="phone"
-              className="feedback__input"
-              maxLength={20}
-              placeholder="请输入内容,20字以内"
-              value={params.phone}
-              onChange={(val) =>
-                setParams({
-                  ...params,
-                  phone: val as string,
-                })
-              }
-            />
-
-            <Label className="feedback__label">反馈内容</Label>
-            <AtTextarea
-              value={params.content}
-              className="feedback__input textarea"
-              maxLength={200}
-              placeholder="请输入内容,200字以内"
-              onChange={(val) =>
-                setParams({
-                  ...params,
-                  content: val,
-                })
-              }
-            />
-          </View>
-
-          <View className="feedback-main__form">
-            <Label className="feedback__label">验证码</Label>
-            <View className="code">
-              <AtInput
-                name="randCode"
-                value={params.randCode}
-                className="feedback__input"
-                maxLength={5}
-                placeholder="请输入内容"
-                onChange={(val) =>
-                  setParams({
-                    ...params,
-                    randCode: val as string,
-                  })
-                }
-              />
-
-              <Image
-                className="code__img"
-                src={`${getBaseURL()}/api/show/getRandCode?t=${timestamp}`}
-                onClick={() => setTimestamp(new Date().getTime())}
-              />
-            </View>
-
-            <View className="toolbar">
-              <AtButton
-                className="toolbar__btn cancel"
-                onClick={() => {
-                  Taro.navigateBack();
-                }}
-              >
-                取消
-              </AtButton>
-              <AtButton
-                className="toolbar__btn submit"
-                formType="submit"
-                loading={loading}
-              >
-                提交
-              </AtButton>
-            </View>
-          </View>
-
-          <FooterProtocol checked={checked} setChecked={setChecked} />
-        </AtForm>
-
-        <Image src={BgImg} className="feedback__bg" mode="widthFix" />
-      </View>
-    </View>
-  );
-};
-
-export default FeedBackPage;
+import { useState } from "react";
+import { Image, Label, View } from "@tarojs/components";
+import Taro, { FC } from "@tarojs/taro";
+import { AtButton, AtForm, AtInput, AtTextarea } from "taro-ui";
+import BgImg from "../../images/feedback@2x-min.png";
+import { handleValidate } from "../../../utils";
+import { getBaseURL } from "@dage/service";
+import { Rules } from "async-validator";
+import { feedbackApi } from "../../../api";
+import { FooterProtocol } from "../../../components/FooterProtocol";
+import "./index.scss";
+
+const DEFAULT_PARAMS = {
+  name: "",
+  phone: "",
+  content: "",
+  randCode: "",
+};
+
+const formRules: Rules = {
+  name: [{ required: true, message: "请输入称呼" }],
+  phone: [{ required: true, message: "请输入联系方式" }],
+  content: [{ required: true, message: "请输入反馈内容" }],
+  randCode: [
+    { required: true, message: "请输入验证码" },
+    {
+      validator: (_, value) => {
+        return value.length === 5;
+      },
+      message: "验证码输入有误",
+    },
+  ],
+};
+
+const FeedBackPage: FC = () => {
+  const [checked, setChecked] = useState(false);
+  const [params, setParams] = useState({ ...DEFAULT_PARAMS });
+  const [timestamp, setTimestamp] = useState(new Date().getTime());
+  const [loading, setLoading] = useState(false);
+
+  const handleSubmit = async (event, readProtocol = false) => {
+    if (!(await handleValidate(params, formRules))) return;
+
+    if (!checked && !readProtocol) {
+      Taro.showModal({
+        title: "提示",
+        content: "是否已阅读并同意《用户服务协议》及《个人信息保护政策》",
+        confirmText: "同意",
+        success: (res) => {
+          if (res.confirm) {
+            setChecked(true);
+            handleSubmit(undefined, true);
+          }
+        },
+      });
+      return;
+    }
+
+    try {
+      setLoading(true);
+      await feedbackApi(params);
+
+      setParams({ ...DEFAULT_PARAMS });
+      setTimestamp(new Date().getTime());
+      Taro.showToast({
+        title: "提交成功",
+        icon: "success",
+      });
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <View className="feedback-page">
+      <View className="feedback">
+        <AtForm className="feedback-main" onSubmit={handleSubmit}>
+          <View className="feedback-main__form">
+            <Label className="feedback__label">您的称呼</Label>
+            <AtInput
+              name="name"
+              className="feedback__input"
+              maxLength={20}
+              placeholder="请输入内容,20字以内"
+              value={params.name}
+              onChange={(val) =>
+                setParams({
+                  ...params,
+                  name: val as string,
+                })
+              }
+            />
+
+            <Label className="feedback__label">联系方式</Label>
+            <AtInput
+              name="phone"
+              type="phone"
+              className="feedback__input"
+              maxLength={20}
+              placeholder="请输入内容,20字以内"
+              value={params.phone}
+              onChange={(val) =>
+                setParams({
+                  ...params,
+                  phone: val as string,
+                })
+              }
+            />
+
+            <Label className="feedback__label">反馈内容</Label>
+            <AtTextarea
+              value={params.content}
+              className="feedback__input textarea"
+              maxLength={200}
+              placeholder="请输入内容,200字以内"
+              onChange={(val) =>
+                setParams({
+                  ...params,
+                  content: val,
+                })
+              }
+            />
+          </View>
+
+          <View className="feedback-main__form">
+            <Label className="feedback__label">验证码</Label>
+            <View className="code">
+              <AtInput
+                name="randCode"
+                value={params.randCode}
+                className="feedback__input"
+                maxLength={5}
+                placeholder="请输入内容"
+                onChange={(val) =>
+                  setParams({
+                    ...params,
+                    randCode: val as string,
+                  })
+                }
+              />
+
+              <Image
+                className="code__img"
+                src={`${getBaseURL()}/api/show/getRandCode?t=${timestamp}`}
+                onClick={() => setTimestamp(new Date().getTime())}
+              />
+            </View>
+
+            <View className="toolbar">
+              <AtButton
+                className="toolbar__btn cancel"
+                onClick={() => {
+                  Taro.navigateBack();
+                }}
+              >
+                取消
+              </AtButton>
+              <AtButton
+                className="toolbar__btn submit"
+                formType="submit"
+                loading={loading}
+              >
+                提交
+              </AtButton>
+            </View>
+          </View>
+
+          <FooterProtocol checked={checked} setChecked={setChecked} />
+        </AtForm>
+
+        <Image src={BgImg} className="feedback__bg" mode="widthFix" />
+      </View>
+    </View>
+  );
+};
+
+export default FeedBackPage;

+ 4 - 4
src/subModule/pages/iframe/index.config.ts

@@ -1,4 +1,4 @@
-export default definePageConfig({
-  navigationBarTitleText: "锡善云城",
-  pageOrientation: "landscape",
-});
+export default definePageConfig({
+  navigationBarTitleText: "锡善云城",
+  pageOrientation: "landscape",
+});

+ 21 - 21
src/subModule/pages/iframe/index.tsx

@@ -1,21 +1,21 @@
-import { WebView } from "@tarojs/components";
-import Taro, { FC, useRouter } from "@tarojs/taro";
-import { useEffect } from "react";
-
-const IframePage: FC = () => {
-  const route = useRouter();
-
-  useEffect(() => {
-    if (route.params.title) {
-      Taro.setNavigationBarTitle({
-        title: route.params.title,
-      });
-    }
-  }, []);
-
-  return route.params.url ? (
-    <WebView src={decodeURIComponent(route.params.url)} />
-  ) : null;
-};
-
-export default IframePage;
+import { WebView } from "@tarojs/components";
+import Taro, { FC, useRouter } from "@tarojs/taro";
+import { useEffect } from "react";
+
+const IframePage: FC = () => {
+  const route = useRouter();
+
+  useEffect(() => {
+    if (route.params.title) {
+      Taro.setNavigationBarTitle({
+        title: route.params.title,
+      });
+    }
+  }, []);
+
+  return route.params.url ? (
+    <WebView src={decodeURIComponent(route.params.url)} />
+  ) : null;
+};
+
+export default IframePage;

+ 3 - 3
src/subModule/pages/museum/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationBarTitleText: "无锡慈善博物馆",
-});
+export default definePageConfig({
+  navigationBarTitleText: "无锡慈善博物馆",
+});

+ 106 - 106
src/subModule/pages/museum/index.scss

@@ -1,106 +1,106 @@
-.museum {
-  width: 100vw;
-  height: 100vh;
-  overflow: hidden;
-  background: url("./images/bg@2x-min.jpg") no-repeat center bottom / cover;
-
-  &__logo {
-    position: absolute;
-    top: 50px;
-    left: 50%;
-    width: 564px;
-    height: 302px;
-    transform: translateX(-50%);
-    transition: all linear 0.2s;
-
-    &.sm {
-      top: 20px;
-      width: 400px;
-      height: 214px;
-    }
-  }
-  &-swiper {
-    width: 100%;
-    height: 100%;
-
-    &-item {
-      overflow: visible;
-    }
-  }
-  &-contain {
-    position: relative;
-    padding-top: 123px;
-    height: calc(100% + 360px);
-    border-top-left-radius: 30px;
-    border-top-right-radius: 30px;
-    background: rgba(22, 18, 14, 0.6);
-    box-sizing: border-box;
-
-    &__close {
-      position: absolute;
-      top: 30px;
-      right: 20px;
-      width: 122px;
-      height: 123px;
-    }
-  }
-  .list {
-    height: 100%;
-
-    &-item {
-      display: flex;
-      flex-direction: column;
-      padding-left: 32px;
-
-      &__scroll {
-        flex: 1;
-        height: 0;
-        padding-bottom: 20px;
-      }
-      &__img {
-        margin: 10px 0 46px;
-        width: 480px;
-      }
-      &__inner {
-        width: calc(100% - 100px);
-        color: white;
-        text-indent: 2em;
-        font-size: 31px;
-        line-height: 36px;
-      }
-    }
-  }
-  &__tips {
-    display: none;
-    position: absolute;
-    left: 50%;
-    bottom: 67px;
-    text-align: center;
-    line-height: 92px;
-    width: 448px;
-    height: 92px;
-    color: white;
-    font-size: 31px;
-    background: rgba(0, 0, 0, 0.85);
-    border-radius: 10px;
-    transform: translate(-50%);
-    opacity: 0;
-
-    &.show {
-      display: block;
-      animation: tipsAni ease-in-out 2s forwards;
-    }
-  }
-}
-
-@keyframes tipsAni {
-  20% {
-    opacity: 1;
-  }
-  80% {
-    opacity: 1;
-  }
-  100% {
-    opacity: 0;
-  }
-}
+.museum {
+  width: 100vw;
+  height: 100vh;
+  overflow: hidden;
+  background: url("./images/bg@2x-min.jpg") no-repeat center bottom / cover;
+
+  &__logo {
+    position: absolute;
+    top: 50px;
+    left: 50%;
+    width: 564px;
+    height: 302px;
+    transform: translateX(-50%);
+    transition: all linear 0.2s;
+
+    &.sm {
+      top: 20px;
+      width: 400px;
+      height: 214px;
+    }
+  }
+  &-swiper {
+    width: 100%;
+    height: 100%;
+
+    &-item {
+      overflow: visible;
+    }
+  }
+  &-contain {
+    position: relative;
+    padding-top: 123px;
+    height: calc(100% + 360px);
+    border-top-left-radius: 30px;
+    border-top-right-radius: 30px;
+    background: rgba(22, 18, 14, 0.6);
+    box-sizing: border-box;
+
+    &__close {
+      position: absolute;
+      top: 30px;
+      right: 20px;
+      width: 122px;
+      height: 123px;
+    }
+  }
+  .list {
+    height: 100%;
+
+    &-item {
+      display: flex;
+      flex-direction: column;
+      padding-left: 32px;
+
+      &__scroll {
+        flex: 1;
+        height: 0;
+        padding-bottom: 20px;
+      }
+      &__img {
+        margin: 10px 0 46px;
+        width: 480px;
+      }
+      &__inner {
+        width: calc(100% - 100px);
+        color: white;
+        text-indent: 2em;
+        font-size: 31px;
+        line-height: 36px;
+      }
+    }
+  }
+  &__tips {
+    display: none;
+    position: absolute;
+    left: 50%;
+    bottom: 67px;
+    text-align: center;
+    line-height: 92px;
+    width: 448px;
+    height: 92px;
+    color: white;
+    font-size: 31px;
+    background: rgba(0, 0, 0, 0.85);
+    border-radius: 10px;
+    transform: translate(-50%);
+    opacity: 0;
+
+    &.show {
+      display: block;
+      animation: tipsAni ease-in-out 2s forwards;
+    }
+  }
+}
+
+@keyframes tipsAni {
+  20% {
+    opacity: 1;
+  }
+  80% {
+    opacity: 1;
+  }
+  100% {
+    opacity: 0;
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 6 - 6
src/subModule/pages/museum/index.tsx


+ 37 - 37
src/subModule/pages/order/index.scss

@@ -1,37 +1,37 @@
-.order {
-  &::before {
-    background-image: url("../../images/redemption@2x-min.png");
-  }
-
-  &__line {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    font-size: 31px;
-    color: #424a4a;
-
-    .size38 {
-      font-size: 38px;
-    }
-    .bold {
-      font-family: "SourceHanSansCN-Bold";
-    }
-    .size38.bold {
-      margin-bottom: 10px;
-    }
-    .green {
-      color: #589498;
-    }
-    .red {
-      color: #ff7272;
-    }
-  }
-
-  .feedback-main__form:first-child {
-    margin-bottom: 20px;
-  }
-
-  .toolbar {
-    margin-top: 40px;
-  }
-}
+.order {
+  &::before {
+    background-image: url("../../images/redemption@2x-min.png");
+  }
+
+  &__line {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: 31px;
+    color: #424a4a;
+
+    .size38 {
+      font-size: 38px;
+    }
+    .bold {
+      font-family: "SourceHanSansCN-Bold";
+    }
+    .size38.bold {
+      margin-bottom: 10px;
+    }
+    .green {
+      color: #589498;
+    }
+    .red {
+      color: #ff7272;
+    }
+  }
+
+  .feedback-main__form:first-child {
+    margin-bottom: 20px;
+  }
+
+  .toolbar {
+    margin-top: 40px;
+  }
+}

+ 3 - 3
src/subModule/pages/portrait-iframe/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  pageOrientation: "portrait",
-});
+export default definePageConfig({
+  pageOrientation: "portrait",
+});

+ 12 - 12
src/subModule/pages/portrait-iframe/index.tsx

@@ -1,12 +1,12 @@
-import { WebView } from "@tarojs/components";
-import { FC, useRouter } from "@tarojs/taro";
-
-const IframePage: FC = () => {
-  const route = useRouter();
-
-  return route.params.url ? (
-    <WebView src={decodeURIComponent(route.params.url)} />
-  ) : null;
-};
-
-export default IframePage;
+import { WebView } from "@tarojs/components";
+import { FC, useRouter } from "@tarojs/taro";
+
+const IframePage: FC = () => {
+  const route = useRouter();
+
+  return route.params.url ? (
+    <WebView src={decodeURIComponent(route.params.url)} />
+  ) : null;
+};
+
+export default IframePage;

+ 3 - 3
src/subModule/pages/protocol/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationBarTitleText: "协议",
-});
+export default definePageConfig({
+  navigationBarTitleText: "协议",
+});

+ 31 - 31
src/subModule/pages/protocol/index.tsx

@@ -1,31 +1,31 @@
-import { RichText, View } from "@tarojs/components";
-import Taro, { FC, useRouter } from "@tarojs/taro";
-import { useEffect, useState } from "react";
-import { getProtocolDetailApi } from "../../../api";
-
-const ProtocolPage: FC = () => {
-  const route = useRouter();
-  const [rtf, setRTF] = useState("");
-
-  useEffect(() => {
-    getDetail();
-  }, []);
-
-  const getDetail = async () => {
-    const data = await getProtocolDetailApi(route.params.id);
-
-    setRTF(data.rtf);
-
-    Taro.setNavigationBarTitle({
-      title: data.name,
-    });
-  };
-
-  return (
-    <View style={{ padding: `0 ${Taro.pxTransform(30)}` }}>
-      <RichText nodes={rtf} />
-    </View>
-  );
-};
-
-export default ProtocolPage;
+import { RichText, View } from "@tarojs/components";
+import Taro, { FC, useRouter } from "@tarojs/taro";
+import { useEffect, useState } from "react";
+import { getProtocolDetailApi } from "../../../api";
+
+const ProtocolPage: FC = () => {
+  const route = useRouter();
+  const [rtf, setRTF] = useState("");
+
+  useEffect(() => {
+    getDetail();
+  }, []);
+
+  const getDetail = async () => {
+    const data = await getProtocolDetailApi(route.params.id);
+
+    setRTF(data.rtf);
+
+    Taro.setNavigationBarTitle({
+      title: data.name,
+    });
+  };
+
+  return (
+    <View style={{ padding: `0 ${Taro.pxTransform(30)}` }}>
+      <RichText nodes={rtf} />
+    </View>
+  );
+};
+
+export default ProtocolPage;

+ 3 - 3
src/subModule/pages/shanjuan/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  pageOrientation: "portrait",
-});
+export default definePageConfig({
+  pageOrientation: "portrait",
+});

+ 10 - 10
src/subModule/pages/shanjuan/index.tsx

@@ -1,10 +1,10 @@
-import { WebView } from "@tarojs/components";
-import { FC } from "@tarojs/taro";
-
-const ShanjuanPage: FC = () => {
-  return (
-    <WebView src="https://houseoss.4dkankan.com/project/wuxicishanbwg/index.html?m=TEST" />
-  );
-};
-
-export default ShanjuanPage;
+import { WebView } from "@tarojs/components";
+import { FC } from "@tarojs/taro";
+
+const ShanjuanPage: FC = () => {
+  return (
+    <WebView src="https://wuxicharitymuseum.cn/wuxicishanbwg/index.html?m=TEST" />
+  );
+};
+
+export default ShanjuanPage;

+ 69 - 69
src/subModule/pages/shopmall/components/Products/index.scss

@@ -1,69 +1,69 @@
-.products {
-  display: flex;
-  flex-wrap: wrap;
-  margin: 40px 0;
-
-  &-wrap {
-    flex: 1;
-    height: 0;
-  }
-  &-item {
-    margin: 8px;
-    padding: 0 8px 8px;
-    width: calc(50% - 16px);
-    color: #424a4a;
-    background: rgba(255, 255, 255, 0.5);
-    border-radius: 10px;
-    box-sizing: border-box;
-
-    &__img {
-      width: 100%;
-      height: 269px;
-      border-radius: 10px;
-      overflow: hidden;
-    }
-    &__main {
-      position: relative;
-      padding: 0 13px;
-    }
-    &__title {
-      margin-top: 13px;
-      font-size: 38px;
-      line-height: 50px;
-    }
-    &__stock {
-      color: #9ba2a0;
-      font-size: 23px;
-      min-height: 34px;
-    }
-    &__ft {
-      font-size: 23px;
-      line-height: 58px;
-
-      &__price {
-        color: #589498;
-        font-size: 38px;
-        font-family: "SourceHanSansCN-Bold";
-      }
-    }
-    &__is-buy {
-      position: absolute;
-      right: 13px;
-      bottom: 16px;
-      width: 100px;
-      height: 100px;
-    }
-    &__buy {
-      position: absolute;
-      right: 13px;
-      bottom: 16px;
-      width: 80px;
-      height: 80px;
-    }
-  }
-  &__more {
-    text-align: center;
-    font-size: 24px;
-    color: #525252;
-  }
-}
+.products {
+  display: flex;
+  flex-wrap: wrap;
+  margin: 40px 0;
+
+  &-wrap {
+    flex: 1;
+    height: 0;
+  }
+  &-item {
+    margin: 8px;
+    padding: 0 8px 8px;
+    width: calc(50% - 16px);
+    color: #424a4a;
+    background: rgba(255, 255, 255, 0.5);
+    border-radius: 10px;
+    box-sizing: border-box;
+
+    &__img {
+      width: 100%;
+      height: 269px;
+      border-radius: 10px;
+      overflow: hidden;
+    }
+    &__main {
+      position: relative;
+      padding: 0 13px;
+    }
+    &__title {
+      margin-top: 13px;
+      font-size: 38px;
+      line-height: 50px;
+    }
+    &__stock {
+      color: #9ba2a0;
+      font-size: 23px;
+      min-height: 34px;
+    }
+    &__ft {
+      font-size: 23px;
+      line-height: 58px;
+
+      &__price {
+        color: #589498;
+        font-size: 38px;
+        font-family: "SourceHanSansCN-Bold";
+      }
+    }
+    &__is-buy {
+      position: absolute;
+      right: 13px;
+      bottom: 16px;
+      width: 100px;
+      height: 100px;
+    }
+    &__buy {
+      position: absolute;
+      right: 13px;
+      bottom: 16px;
+      width: 80px;
+      height: 80px;
+    }
+  }
+  &__more {
+    text-align: center;
+    font-size: 24px;
+    color: #525252;
+  }
+}

+ 136 - 136
src/subModule/pages/shopmall/components/Products/index.tsx

@@ -1,136 +1,136 @@
-import { Image, ScrollView, Text, View } from "@tarojs/components";
-import Taro, { FC, useDidShow } from "@tarojs/taro";
-import { useRef, useState } from "react";
-import BuyIcon from "../../../../images/icon_presents@2x-min.png";
-import IsBuyIcon from "../../../../images/icon_complete@2x-min.png";
-import { checkRedeemApi, getProductListApi } from "../../../../../api";
-import { getBaseURL } from "@dage/service";
-import "./index.scss";
-
-const baseUrl = getBaseURL();
-
-export interface ProductsProps {
-  curTab: number;
-  /**
-   * 用户当前积分
-   */
-  point: number;
-  openCertLayout: Function;
-}
-
-export const Products: FC<ProductsProps> = ({ point, openCertLayout }) => {
-  const params = useRef({
-    pageNum: 1,
-    pageSize: 20,
-  });
-  const hasMore = useRef(true);
-  /** 是否已购买证书 */
-  const [certIsBuy, setCertIsBuy] = useState(false);
-  const [list, setList] = useState<any[]>([]);
-
-  useDidShow(() => {
-    checkRedeem();
-  });
-
-  const checkRedeem = async () => {
-    const data = await checkRedeemApi();
-    setCertIsBuy(data);
-  };
-
-  const getProductList = async () => {
-    if (!hasMore.current) return;
-
-    const data = await getProductListApi(params.current);
-
-    if (params.current.pageNum === 1) {
-      setList(data.records);
-    } else {
-      setList([...list, ...data.records]);
-    }
-
-    if (params.current.pageNum + 1 > data.pages) {
-      hasMore.current = false;
-    }
-
-    params.current.pageNum += 1;
-  };
-
-  useDidShow(() => {
-    params.current.pageNum = 1;
-    hasMore.current = true;
-    getProductList();
-  });
-
-  const handleBuy = (item: any) => {
-    if (point < item.score) {
-      Taro.showToast({
-        title: "积分不足,无法兑换",
-        icon: "none",
-        duration: 3000,
-      });
-      return;
-    }
-
-    if (item.id === 1 && certIsBuy) {
-      openCertLayout();
-      return;
-    }
-
-    Taro.navigateTo({
-      url: `/subModule/pages/order/index?id=${item.id}&score=${item.score}&point=${point}`,
-    });
-  };
-
-  return (
-    <>
-      <ScrollView
-        scrollY
-        className="products-wrap"
-        onScrollToLower={getProductList}
-      >
-        <View className="products">
-          {list.map((item) => {
-            const imgUrl = `${baseUrl}${item.thumb}`;
-            const isCert = item.id === 1;
-
-            return (
-              <View
-                key={item.id}
-                className="products-item"
-                onClick={handleBuy.bind(undefined, item)}
-              >
-                <Image
-                  className="products-item__img"
-                  src={imgUrl}
-                  mode="aspectFill"
-                />
-
-                <View className="products-item__main">
-                  <View className="products-item__title limit-line">
-                    {item.name}
-                  </View>
-                  <View className="products-item__stock">
-                    {!isCert ? <>库存:{item.stock}</> : ""}
-                  </View>
-                  <View className="products-item__ft">
-                    <Text className="products-item__ft__price">
-                      {item.score}
-                    </Text>
-                    <Text>爱心</Text>
-                  </View>
-
-                  {isCert && certIsBuy ? (
-                    <Image className="products-item__is-buy" src={IsBuyIcon} />
-                  ) : (
-                    <Image className="products-item__buy" src={BuyIcon} />
-                  )}
-                </View>
-              </View>
-            );
-          })}
-        </View>
-        <View className="products__more">更多礼品敬请期待</View>
-      </ScrollView>
-    </>
-  );
-};
+import { Image, ScrollView, Text, View } from "@tarojs/components";
+import Taro, { FC, useDidShow } from "@tarojs/taro";
+import { useRef, useState } from "react";
+import BuyIcon from "../../../../images/icon_presents@2x-min.png";
+import IsBuyIcon from "../../../../images/icon_complete@2x-min.png";
+import { checkRedeemApi, getProductListApi } from "../../../../../api";
+import { getBaseURL } from "@dage/service";
+import "./index.scss";
+
+const baseUrl = getBaseURL();
+
+export interface ProductsProps {
+  curTab: number;
+  /**
+   * 用户当前积分
+   */
+  point: number;
+  openCertLayout: Function;
+}
+
+export const Products: FC<ProductsProps> = ({ point, openCertLayout }) => {
+  const params = useRef({
+    pageNum: 1,
+    pageSize: 20,
+  });
+  const hasMore = useRef(true);
+  /** 是否已购买证书 */
+  const [certIsBuy, setCertIsBuy] = useState(false);
+  const [list, setList] = useState<any[]>([]);
+
+  useDidShow(() => {
+    checkRedeem();
+  });
+
+  const checkRedeem = async () => {
+    const data = await checkRedeemApi();
+    setCertIsBuy(data);
+  };
+
+  const getProductList = async () => {
+    if (!hasMore.current) return;
+
+    const data = await getProductListApi(params.current);
+
+    if (params.current.pageNum === 1) {
+      setList(data.records);
+    } else {
+      setList([...list, ...data.records]);
+    }
+
+    if (params.current.pageNum + 1 > data.pages) {
+      hasMore.current = false;
+    }
+
+    params.current.pageNum += 1;
+  };
+
+  useDidShow(() => {
+    params.current.pageNum = 1;
+    hasMore.current = true;
+    getProductList();
+  });
+
+  const handleBuy = (item: any) => {
+    if (point < item.score) {
+      Taro.showToast({
+        title: "积分不足,无法兑换",
+        icon: "none",
+        duration: 3000,
+      });
+      return;
+    }
+
+    if (item.id === 1 && certIsBuy) {
+      openCertLayout();
+      return;
+    }
+
+    Taro.navigateTo({
+      url: `/subModule/pages/order/index?id=${item.id}&score=${item.score}&point=${point}`,
+    });
+  };
+
+  return (
+    <>
+      <ScrollView
+        scrollY
+        className="products-wrap"
+        onScrollToLower={getProductList}
+      >
+        <View className="products">
+          {list.map((item) => {
+            const imgUrl = `${baseUrl}${item.thumb}`;
+            const isCert = item.id === 1;
+
+            return (
+              <View
+                key={item.id}
+                className="products-item"
+                onClick={handleBuy.bind(undefined, item)}
+              >
+                <Image
+                  className="products-item__img"
+                  src={imgUrl}
+                  mode="aspectFill"
+                />
+
+                <View className="products-item__main">
+                  <View className="products-item__title limit-line">
+                    {item.name}
+                  </View>
+                  <View className="products-item__stock">
+                    {!isCert ? <>库存:{item.stock}</> : ""}
+                  </View>
+                  <View className="products-item__ft">
+                    <Text className="products-item__ft__price">
+                      {item.score}
+                    </Text>
+                    <Text>爱心</Text>
+                  </View>
+
+                  {isCert && certIsBuy ? (
+                    <Image className="products-item__is-buy" src={IsBuyIcon} />
+                  ) : (
+                    <Image className="products-item__buy" src={BuyIcon} />
+                  )}
+                </View>
+              </View>
+            );
+          })}
+        </View>
+        <View className="products__more">更多礼品敬请期待</View>
+      </ScrollView>
+    </>
+  );
+};

+ 70 - 70
src/subModule/pages/shopmall/components/Ranking/index.scss

@@ -1,70 +1,70 @@
-.ranking {
-  flex: 1;
-  padding-top: 30px;
-  height: 0;
-  box-sizing: border-box;
-
-  &-item {
-    display: flex;
-    align-items: center;
-    padding: 17px 50px;
-
-    &.is-first {
-      .ranking-item__sort::after {
-        content: "";
-        position: absolute;
-        top: 0;
-        left: 0;
-        right: 0;
-        bottom: 0;
-        background: url("./icon_winner@2x-min.png") no-repeat center / contain;
-      }
-      .ranking-item__val {
-        color: #ffd337;
-      }
-    }
-    &.is-me {
-      background: #589498;
-
-      .ranking-item__sort,
-      .ranking-item__val,
-      .ranking-item__name {
-        color: white;
-      }
-    }
-    &__sort {
-      position: relative;
-      color: #393939;
-      font-size: 31px;
-      width: 56px;
-      height: 56px;
-      line-height: 56px;
-      text-align: center;
-      font-family: "Alibaba PuHuiTi-Bold";
-
-      &__num {
-        position: relative;
-        z-index: 2;
-      }
-    }
-    &__avatar {
-      margin: 0 30px 0 42px;
-      width: 80px;
-      height: 80px;
-      border-radius: 50%;
-      overflow: hidden;
-      border: 4px solid #ffffff;
-    }
-    &__name {
-      flex: 1;
-      width: 0;
-      color: #424a4a;
-      font-size: 31px;
-    }
-    &__val {
-      color: #424a4a;
-      font-size: 38px;
-      font-family: "Alibaba PuHuiTi-Bold";
-    }
-  }
-}
+.ranking {
+  flex: 1;
+  padding-top: 30px;
+  height: 0;
+  box-sizing: border-box;
+
+  &-item {
+    display: flex;
+    align-items: center;
+    padding: 17px 50px;
+
+    &.is-first {
+      .ranking-item__sort::after {
+        content: "";
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: url("./icon_winner@2x-min.png") no-repeat center / contain;
+      }
+      .ranking-item__val {
+        color: #ffd337;
+      }
+    }
+    &.is-me {
+      background: #589498;
+
+      .ranking-item__sort,
+      .ranking-item__val,
+      .ranking-item__name {
+        color: white;
+      }
+    }
+    &__sort {
+      position: relative;
+      color: #393939;
+      font-size: 31px;
+      width: 56px;
+      height: 56px;
+      line-height: 56px;
+      text-align: center;
+      font-family: "Alibaba PuHuiTi-Bold";
+
+      &__num {
+        position: relative;
+        z-index: 2;
+      }
+    }
+    &__avatar {
+      margin: 0 30px 0 42px;
+      width: 80px;
+      height: 80px;
+      border-radius: 50%;
+      overflow: hidden;
+      border: 4px solid #ffffff;
+    }
+    &__name {
+      flex: 1;
+      width: 0;
+      color: #424a4a;
+      font-size: 31px;
+    }
+    &__val {
+      color: #424a4a;
+      font-size: 38px;
+      font-family: "Alibaba PuHuiTi-Bold";
+    }
+  }
+}

+ 38 - 38
src/subModule/pages/shopmall/components/Ranking/index.tsx

@@ -1,38 +1,38 @@
-import { Image, ScrollView, View } from "@tarojs/components";
-import { FC } from "@tarojs/taro";
-import classNames from "classnames";
-import baseStore from "../../../../../store/base";
-import DefaultAvatar from "./default-avatar-min.png";
-import "./index.scss";
-
-export interface RankingProps {
-  list: any[];
-}
-
-export const Ranking: FC<RankingProps> = ({ list }) => {
-  return (
-    <ScrollView scrollY className="ranking">
-      {list.map((item, idx) => (
-        <View
-          key={item.id}
-          className={classNames("ranking-item", {
-            "is-first": idx === 0,
-            "is-me": baseStore.userInfo?.id === item.id,
-          })}
-        >
-          <View className="ranking-item__sort">
-            <View className="ranking-item__sort__num">{idx + 1}</View>
-          </View>
-
-          <Image className="ranking-item__avatar" src={DefaultAvatar} />
-
-          <View className="ranking-item__name limit-line">
-            {item.nickName.slice(0, 1)}***
-          </View>
-
-          <View className="ranking-item__val">{item.pcs}</View>
-        </View>
-      ))}
-    </ScrollView>
-  );
-};
+import { Image, ScrollView, View } from "@tarojs/components";
+import { FC } from "@tarojs/taro";
+import classNames from "classnames";
+import baseStore from "../../../../../store/base";
+import DefaultAvatar from "./default-avatar-min.png";
+import "./index.scss";
+
+export interface RankingProps {
+  list: any[];
+}
+
+export const Ranking: FC<RankingProps> = ({ list }) => {
+  return (
+    <ScrollView scrollY className="ranking">
+      {list.map((item, idx) => (
+        <View
+          key={item.id}
+          className={classNames("ranking-item", {
+            "is-first": idx === 0,
+            "is-me": baseStore.userInfo?.id === item.id,
+          })}
+        >
+          <View className="ranking-item__sort">
+            <View className="ranking-item__sort__num">{idx + 1}</View>
+          </View>
+
+          <Image className="ranking-item__avatar" src={DefaultAvatar} />
+
+          <View className="ranking-item__name limit-line">
+            {item.nickName.slice(0, 1)}***
+          </View>
+
+          <View className="ranking-item__val">{item.pcs}</View>
+        </View>
+      ))}
+    </ScrollView>
+  );
+};

+ 50 - 50
src/subModule/pages/shopmall/components/Records/index.scss

@@ -1,50 +1,50 @@
-.records {
-  flex: 1;
-  margin: 20px 0;
-  height: 0;
-  box-sizing: border-box;
-
-  &-tips {
-    padding: 0 20px;
-    color: #589498;
-    text-align: center;
-    font-size: 23px;
-    height: 50px;
-    line-height: 50px;
-  }
-
-  &-item {
-    display: flex;
-    align-items: center;
-    padding: 20px 35px;
-
-    &__lf {
-      flex: 1;
-
-      &__label {
-        font-size: 31px;
-        color: #424a4a;
-      }
-      &__date {
-        color: #989f9e;
-        font-size: 23px;
-      }
-    }
-    &__rg {
-      padding-left: 20px;
-      font-family: "SourceHanSansCN-Bold";
-      font-size: 38px;
-      color: #ffce22;
-
-      &.red {
-        color: #ff6c63;
-      }
-    }
-  }
-
-  &__nomore {
-    padding-top: 80px;
-    color: #999;
-    text-align: center;
-  }
-}
+.records {
+  flex: 1;
+  margin: 20px 0;
+  height: 0;
+  box-sizing: border-box;
+
+  &-tips {
+    padding: 0 20px;
+    color: #589498;
+    text-align: center;
+    font-size: 23px;
+    height: 50px;
+    line-height: 50px;
+  }
+
+  &-item {
+    display: flex;
+    align-items: center;
+    padding: 20px 35px;
+
+    &__lf {
+      flex: 1;
+
+      &__label {
+        font-size: 31px;
+        color: #424a4a;
+      }
+      &__date {
+        color: #989f9e;
+        font-size: 23px;
+      }
+    }
+    &__rg {
+      padding-left: 20px;
+      font-family: "SourceHanSansCN-Bold";
+      font-size: 38px;
+      color: #ffce22;
+
+      &.red {
+        color: #ff6c63;
+      }
+    }
+  }
+
+  &__nomore {
+    padding-top: 80px;
+    color: #999;
+    text-align: center;
+  }
+}

+ 88 - 88
src/subModule/pages/shopmall/components/Records/index.tsx

@@ -1,88 +1,88 @@
-import { View, Text, ScrollView } from "@tarojs/components";
-import Taro, { FC, pxTransform } from "@tarojs/taro";
-import classNames from "classnames";
-import baseStore from "../../../../../store/base";
-import VirtualList from "@tarojs/components/virtual-list";
-import { useEffect, useState } from "react";
-import "./index.scss";
-
-export interface RecordsProps {
-  list: any[];
-  active: boolean;
-}
-
-export interface RecordsItemProps {
-  data: any[];
-  index: number;
-}
-
-export const RecordsItem: FC<RecordsItemProps> = ({ data, index }) => {
-  const item = data[index];
-
-  return (
-    <View key={item.id} className="records-item">
-      <View className="records-item__lf">
-        <View className="records-item__lf__label">{item.type}</View>
-        <View className="records-item__lf__date">
-          {item.createTime}
-
-          {item.description && (
-            <Text style={{ paddingLeft: pxTransform(20) }}>
-              {item.description}
-            </Text>
-          )}
-        </View>
-      </View>
-
-      <View
-        className={classNames("records-item__rg", {
-          red: item.score < 0,
-        })}
-      >
-        {item.score > 0 ? "+" : ""}
-        {item.score}
-      </View>
-    </View>
-  );
-};
-
-export const Records: FC<RecordsProps> = ({ list, active }) => {
-  const [listHeight, setListHeight] = useState(0);
-
-  useEffect(() => {
-    if (active && !listHeight) {
-      Taro.createSelectorQuery()
-        .select(".records")
-        .boundingClientRect((rect) => {
-          setListHeight(rect.height);
-        })
-        .exec();
-    }
-  }, [active]);
-
-  return (
-    <>
-      <ScrollView scrollY className="records">
-        {!list.length ? (
-          <View className="records__nomore">暂无记录</View>
-        ) : listHeight ? (
-          <VirtualList
-            width="100%"
-            height={listHeight}
-            itemData={list}
-            itemCount={list.length}
-            itemSize={59}
-            children={RecordsItem}
-            overscanCount={20}
-          />
-        ) : null}
-      </ScrollView>
-
-      {baseStore.shopTips && (
-        <View className="records-tips padding-safe-area-bottom">
-          如有疑问,请咨询 {baseStore.shopTips}
-        </View>
-      )}
-    </>
-  );
-};
+import { View, Text, ScrollView } from "@tarojs/components";
+import Taro, { FC, pxTransform } from "@tarojs/taro";
+import classNames from "classnames";
+import baseStore from "../../../../../store/base";
+import VirtualList from "@tarojs/components/virtual-list";
+import { useEffect, useState } from "react";
+import "./index.scss";
+
+export interface RecordsProps {
+  list: any[];
+  active: boolean;
+}
+
+export interface RecordsItemProps {
+  data: any[];
+  index: number;
+}
+
+export const RecordsItem: FC<RecordsItemProps> = ({ data, index }) => {
+  const item = data[index];
+
+  return (
+    <View key={item.id} className="records-item">
+      <View className="records-item__lf">
+        <View className="records-item__lf__label">{item.type}</View>
+        <View className="records-item__lf__date">
+          {item.createTime}
+
+          {item.description && (
+            <Text style={{ paddingLeft: pxTransform(20) }}>
+              {item.description}
+            </Text>
+          )}
+        </View>
+      </View>
+
+      <View
+        className={classNames("records-item__rg", {
+          red: item.score < 0,
+        })}
+      >
+        {item.score > 0 ? "+" : ""}
+        {item.score}
+      </View>
+    </View>
+  );
+};
+
+export const Records: FC<RecordsProps> = ({ list, active }) => {
+  const [listHeight, setListHeight] = useState(0);
+
+  useEffect(() => {
+    if (active && !listHeight) {
+      Taro.createSelectorQuery()
+        .select(".records")
+        .boundingClientRect((rect) => {
+          setListHeight(rect.height);
+        })
+        .exec();
+    }
+  }, [active]);
+
+  return (
+    <>
+      <ScrollView scrollY className="records">
+        {!list.length ? (
+          <View className="records__nomore">暂无记录</View>
+        ) : listHeight ? (
+          <VirtualList
+            width="100%"
+            height={listHeight}
+            itemData={list}
+            itemCount={list.length}
+            itemSize={59}
+            children={RecordsItem}
+            overscanCount={20}
+          />
+        ) : null}
+      </ScrollView>
+
+      {baseStore.shopTips && (
+        <View className="records-tips padding-safe-area-bottom">
+          如有疑问,请咨询 {baseStore.shopTips}
+        </View>
+      )}
+    </>
+  );
+};

+ 3 - 3
src/subModule/pages/shopmall/index.config.ts

@@ -1,3 +1,3 @@
-export default definePageConfig({
-  navigationBarTitleText: "爱心站",
-});
+export default definePageConfig({
+  navigationBarTitleText: "爱心站",
+});

+ 105 - 105
src/subModule/pages/shopmall/index.scss

@@ -1,105 +1,105 @@
-.shopmall {
-  padding-top: 219px;
-  background: linear-gradient(180deg, #b9d6d3 0%, #fafbeb 100%);
-
-  &-banner {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    padding: 56px 0 0 50px;
-    height: 255px;
-    color: white;
-    font-size: 31px;
-    background: linear-gradient(90deg, #aacbc8 0%, rgba(88, 148, 152, 0) 100%);
-    z-index: 1;
-
-    &__count {
-      font-family: "SourceHanSansCN-Bold";
-      font-size: 70px;
-      line-height: 80px;
-    }
-    &::after {
-      content: "";
-      position: absolute;
-      top: -120px;
-      right: 0;
-      width: 494px;
-      height: 552px;
-      background: url("../../images/img_gift@2x-min.png") no-repeat center /
-        contain;
-    }
-  }
-
-  &-main {
-    position: relative;
-    padding: 10px 21px;
-    min-height: calc(100vh - 219px);
-    overflow: hidden;
-    border-radius: 40px;
-    border: 2px solid rgba(255, 255, 255, 0.8);
-    background: linear-gradient(
-      180deg,
-      rgba(255, 255, 255, 0.8) 0%,
-      rgba(255, 255, 255, 0) 100%
-    );
-    box-sizing: border-box;
-    z-index: 2;
-  }
-
-  &-tab {
-    position: absolute;
-    top: 0;
-    left: 0;
-    display: flex;
-    flex-direction: column;
-
-    .at-tabs__header {
-      margin: 0 auto;
-      width: calc(100% - 42px);
-      height: 140px;
-      background: unset;
-      border-bottom: 1px solid #d5ddd7;
-    }
-    .at-tabs__item-underline {
-      bottom: 42px;
-      left: 50%;
-      margin-left: -50px;
-      width: 100px;
-      height: 14px;
-      background: #ffe49d;
-      box-shadow: 0px 4px 8px 0px rgba(210, 173, 40, 0.5);
-      border-radius: 12px;
-      z-index: -1;
-    }
-    .at-tabs__underline {
-      display: none;
-    }
-    .at-tabs__item {
-      padding: 0;
-      font-size: 38px;
-      color: #424a4a;
-      height: 100%;
-      display: inline-flex;
-      align-items: center;
-      justify-content: center;
-
-      &--active {
-        color: #589498;
-        font-family: "SourceHanSansCN-Bold";
-      }
-      &:nth-child(2) {
-        padding: 0 80px;
-      }
-    }
-    .at-tabs__body {
-      flex: 1;
-      height: 0;
-    }
-    .at-tabs-pane {
-      display: inline-flex;
-      flex-direction: column;
-      height: 100% !important;
-    }
-  }
-}
+.shopmall {
+  padding-top: 219px;
+  background: linear-gradient(180deg, #b9d6d3 0%, #fafbeb 100%);
+
+  &-banner {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    padding: 56px 0 0 50px;
+    height: 255px;
+    color: white;
+    font-size: 31px;
+    background: linear-gradient(90deg, #aacbc8 0%, rgba(88, 148, 152, 0) 100%);
+    z-index: 1;
+
+    &__count {
+      font-family: "SourceHanSansCN-Bold";
+      font-size: 70px;
+      line-height: 80px;
+    }
+    &::after {
+      content: "";
+      position: absolute;
+      top: -120px;
+      right: 0;
+      width: 494px;
+      height: 552px;
+      background: url("../../images/img_gift@2x-min.png") no-repeat center /
+        contain;
+    }
+  }
+
+  &-main {
+    position: relative;
+    padding: 10px 21px;
+    min-height: calc(100vh - 219px);
+    overflow: hidden;
+    border-radius: 40px;
+    border: 2px solid rgba(255, 255, 255, 0.8);
+    background: linear-gradient(
+      180deg,
+      rgba(255, 255, 255, 0.8) 0%,
+      rgba(255, 255, 255, 0) 100%
+    );
+    box-sizing: border-box;
+    z-index: 2;
+  }
+
+  &-tab {
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: flex;
+    flex-direction: column;
+
+    .at-tabs__header {
+      margin: 0 auto;
+      width: calc(100% - 42px);
+      height: 140px;
+      background: unset;
+      border-bottom: 1px solid #d5ddd7;
+    }
+    .at-tabs__item-underline {
+      bottom: 42px;
+      left: 50%;
+      margin-left: -50px;
+      width: 100px;
+      height: 14px;
+      background: #ffe49d;
+      box-shadow: 0px 4px 8px 0px rgba(210, 173, 40, 0.5);
+      border-radius: 12px;
+      z-index: -1;
+    }
+    .at-tabs__underline {
+      display: none;
+    }
+    .at-tabs__item {
+      padding: 0;
+      font-size: 38px;
+      color: #424a4a;
+      height: 100%;
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+
+      &--active {
+        color: #589498;
+        font-family: "SourceHanSansCN-Bold";
+      }
+      &:nth-child(2) {
+        padding: 0 80px;
+      }
+    }
+    .at-tabs__body {
+      flex: 1;
+      height: 0;
+    }
+    .at-tabs-pane {
+      display: inline-flex;
+      flex-direction: column;
+      height: 100% !important;
+    }
+  }
+}

+ 129 - 129
src/subModule/pages/shopmall/index.tsx

@@ -1,129 +1,129 @@
-import { View } from "@tarojs/components";
-import { FC, useDidShow } from "@tarojs/taro";
-import { AtTabs, AtTabsPane } from "taro-ui";
-import { useState } from "react";
-import { Products } from "./components/Products";
-import { Records } from "./components/Records";
-import {
-  getPointRecordListApi,
-  getRankingListApi,
-  getRedeemInfoApi,
-  getUserPointApi,
-} from "../../../api";
-import { Ranking } from "./components/Ranking";
-import { CertLayout } from "../../../subModule/components/CertLayout";
-import { formatDate } from "@dage/utils";
-import "./index.scss";
-
-enum TABS {
-  SHOP,
-  RANKING,
-  RECORD,
-}
-
-const ShopmallPage: FC = () => {
-  const [curTab, setCurTab] = useState(TABS.SHOP);
-  const [point, setPoint] = useState<string | number>("--");
-  const [recordList, setRecordList] = useState<any[]>([]);
-  const [rankingList, setRankingList] = useState<any[]>([]);
-  const [showCert, setShowCert] = useState(false);
-  const [certInfo, setCertInfo] = useState({
-    name: "",
-    date: "",
-  });
-
-  const handleTabClick = (idx: number) => {
-    setCurTab(idx);
-
-    if (idx === TABS.RECORD) {
-      getPointRecordList();
-    } else if (idx === TABS.RANKING) {
-      getRankingList();
-    }
-  };
-
-  const getUserPoint = async () => {
-    const data = await getUserPointApi();
-    setPoint(data.total);
-  };
-
-  const getPointRecordList = async () => {
-    const data = await getPointRecordListApi();
-    setRecordList(data);
-  };
-
-  const getRankingList = async () => {
-    const data = await getRankingListApi({
-      limit: 10,
-    });
-    setRankingList(data);
-  };
-
-  const getRedeemInfo = async () => {
-    const data = await getRedeemInfoApi();
-    setCertInfo({
-      name: data.certName,
-      date: formatDate(data.createTime, "YYYY年MM月DD日"),
-    });
-  };
-
-  const openCertLayout = async () => {
-    await getRedeemInfo();
-    setShowCert(true);
-  };
-
-  useDidShow(() => {
-    getUserPoint();
-  });
-
-  return (
-    <View className="shopmall">
-      <View className="shopmall-banner">
-        <View className="shopmall-banner__label">爱心</View>
-        <View className="shopmall-banner__count">{point}</View>
-      </View>
-
-      <View className="shopmall-main">
-        <AtTabs
-          current={curTab}
-          scroll
-          className="shopmall-tab"
-          tabList={[
-            { title: "爱心兑换" },
-            { title: "排行榜" },
-            { title: "爱心记录" },
-          ]}
-          onClick={handleTabClick}
-        >
-          {/* 爱心兑换 */}
-          <AtTabsPane current={curTab} index={TABS.SHOP}>
-            <Products
-              curTab={curTab}
-              point={point as number}
-              openCertLayout={openCertLayout}
-            />
-          </AtTabsPane>
-
-          {/* 排行榜 */}
-          <AtTabsPane current={curTab} index={TABS.RANKING}>
-            <Ranking list={rankingList} />
-          </AtTabsPane>
-
-          {/* 爱心记录 */}
-          <AtTabsPane current={curTab} index={TABS.RECORD}>
-            <Records list={recordList} active={curTab === TABS.RECORD} />
-          </AtTabsPane>
-        </AtTabs>
-      </View>
-
-      <CertLayout
-        name={certInfo.name}
-        date={certInfo.date}
-        isOpened={showCert}
-        onClose={() => setShowCert(false)}
-      />
-    </View>
-  );
-};
-
-export default ShopmallPage;
+import { View } from "@tarojs/components";
+import { FC, useDidShow } from "@tarojs/taro";
+import { AtTabs, AtTabsPane } from "taro-ui";
+import { useState } from "react";
+import { Products } from "./components/Products";
+import { Records } from "./components/Records";
+import {
+  getPointRecordListApi,
+  getRankingListApi,
+  getRedeemInfoApi,
+  getUserPointApi,
+} from "../../../api";
+import { Ranking } from "./components/Ranking";
+import { CertLayout } from "../../../subModule/components/CertLayout";
+import { formatDate } from "@dage/utils";
+import "./index.scss";
+
+enum TABS {
+  SHOP,
+  RANKING,
+  RECORD,
+}
+
+const ShopmallPage: FC = () => {
+  const [curTab, setCurTab] = useState(TABS.SHOP);
+  const [point, setPoint] = useState<string | number>("--");
+  const [recordList, setRecordList] = useState<any[]>([]);
+  const [rankingList, setRankingList] = useState<any[]>([]);
+  const [showCert, setShowCert] = useState(false);
+  const [certInfo, setCertInfo] = useState({
+    name: "",
+    date: "",
+  });
+
+  const handleTabClick = (idx: number) => {
+    setCurTab(idx);
+
+    if (idx === TABS.RECORD) {
+      getPointRecordList();
+    } else if (idx === TABS.RANKING) {
+      getRankingList();
+    }
+  };
+
+  const getUserPoint = async () => {
+    const data = await getUserPointApi();
+    setPoint(data.total);
+  };
+
+  const getPointRecordList = async () => {
+    const data = await getPointRecordListApi();
+    setRecordList(data);
+  };
+
+  const getRankingList = async () => {
+    const data = await getRankingListApi({
+      limit: 10,
+    });
+    setRankingList(data);
+  };
+
+  const getRedeemInfo = async () => {
+    const data = await getRedeemInfoApi();
+    setCertInfo({
+      name: data.certName,
+      date: formatDate(data.createTime, "YYYY年MM月DD日"),
+    });
+  };
+
+  const openCertLayout = async () => {
+    await getRedeemInfo();
+    setShowCert(true);
+  };
+
+  useDidShow(() => {
+    getUserPoint();
+  });
+
+  return (
+    <View className="shopmall">
+      <View className="shopmall-banner">
+        <View className="shopmall-banner__label">爱心</View>
+        <View className="shopmall-banner__count">{point}</View>
+      </View>
+
+      <View className="shopmall-main">
+        <AtTabs
+          current={curTab}
+          scroll
+          className="shopmall-tab"
+          tabList={[
+            { title: "爱心兑换" },
+            { title: "排行榜" },
+            { title: "爱心记录" },
+          ]}
+          onClick={handleTabClick}
+        >
+          {/* 爱心兑换 */}
+          <AtTabsPane current={curTab} index={TABS.SHOP}>
+            <Products
+              curTab={curTab}
+              point={point as number}
+              openCertLayout={openCertLayout}
+            />
+          </AtTabsPane>
+
+          {/* 排行榜 */}
+          <AtTabsPane current={curTab} index={TABS.RANKING}>
+            <Ranking list={rankingList} />
+          </AtTabsPane>
+
+          {/* 爱心记录 */}
+          <AtTabsPane current={curTab} index={TABS.RECORD}>
+            <Records list={recordList} active={curTab === TABS.RECORD} />
+          </AtTabsPane>
+        </AtTabs>
+      </View>
+
+      <CertLayout
+        name={certInfo.name}
+        date={certInfo.date}
+        isOpened={showCert}
+        onClose={() => setShowCert(false)}
+      />
+    </View>
+  );
+};
+
+export default ShopmallPage;

+ 5 - 5
src/taro-ui.scss

@@ -1,5 +1,5 @@
-$color-brand: #7aa7a7;
-$color-brand-light: #c0d8d7;
-$color-brand-dark: #628e8e;
-
-@import "~taro-ui/dist/style/index.scss";
+$color-brand: #7aa7a7;
+$color-brand-light: #c0d8d7;
+$color-brand-dark: #628e8e;
+
+@import "~taro-ui/dist/style/index.scss";

+ 57 - 57
src/utils/Header.ts

@@ -1,57 +1,57 @@
-/**
- * 模拟浏览器端 Headers 类的实现
- */
-export default class CustomHeader implements Headers {
-  private headers: Map<string, string>;
-
-  constructor(headers: Record<string, string>) {
-    this.headers = new Map(
-      Object.keys(headers).map((key) => [key.toLowerCase(), headers[key]])
-    );
-  }
-
-  get(name: string): string | null {
-    return this.headers.get(name.toLowerCase()) ?? null;
-  }
-
-  set(name: string, value: string): void {
-    this.headers.set(name.toLowerCase(), value);
-  }
-
-  has(name: string): boolean {
-    return this.headers.has(name.toLocaleLowerCase());
-  }
-
-  append(name: string, value: string): void {
-    this.set(name, value);
-  }
-
-  delete(name: string): void {
-    this.headers.delete(name.toLowerCase());
-  }
-
-  values() {
-    return this.headers.values();
-  }
-
-  entries() {
-    return this.headers.entries();
-  }
-
-  keys() {
-    return this.headers.keys();
-  }
-
-  forEach(
-    callbackfn: (value: string, key: string, map: Headers) => void,
-    thisArg?: any
-  ) {
-    return this.headers.forEach((v, k) => {
-      callbackfn.call(thisArg ?? this, v, k, this);
-    }, this);
-  }
-
-  [Symbol.iterator]() {
-    return this.headers[Symbol.iterator]();
-  }
-}
+/**
+ * 模拟浏览器端 Headers 类的实现
+ */
+export default class CustomHeader implements Headers {
+  private headers: Map<string, string>;
+
+  constructor(headers: Record<string, string>) {
+    this.headers = new Map(
+      Object.keys(headers).map((key) => [key.toLowerCase(), headers[key]])
+    );
+  }
+
+  get(name: string): string | null {
+    return this.headers.get(name.toLowerCase()) ?? null;
+  }
+
+  set(name: string, value: string): void {
+    this.headers.set(name.toLowerCase(), value);
+  }
+
+  has(name: string): boolean {
+    return this.headers.has(name.toLocaleLowerCase());
+  }
+
+  append(name: string, value: string): void {
+    this.set(name, value);
+  }
+
+  delete(name: string): void {
+    this.headers.delete(name.toLowerCase());
+  }
+
+  values() {
+    return this.headers.values();
+  }
+
+  entries() {
+    return this.headers.entries();
+  }
+
+  keys() {
+    return this.headers.keys();
+  }
+
+  forEach(
+    callbackfn: (value: string, key: string, map: Headers) => void,
+    thisArg?: any
+  ) {
+    return this.headers.forEach((v, k) => {
+      callbackfn.call(thisArg ?? this, v, k, this);
+    }, this);
+  }
+
+  [Symbol.iterator]() {
+    return this.headers[Symbol.iterator]();
+  }
+}

+ 45 - 45
src/utils/fetch.ts

@@ -1,45 +1,45 @@
-import Taro from "@tarojs/taro";
-import Headers from "./Header";
-
-export const $fetch = (input, init) => {
-  const requestInit = (init ??
-    (typeof input === "object" ? input : {})) as RequestInit;
-  const url = typeof input === "string" ? input : (input as Request).url;
-  const method = requestInit.method ?? "GET";
-  const headers = requestInit.headers ?? {};
-  const body = requestInit.body;
-
-  headers["Content-Type"] = "application/json";
-
-  return new Promise((resolve) => {
-    Taro.request({
-      url,
-      method: method as any,
-      header: headers,
-      data: body,
-      dataType: "text",
-      complete: (evt) => {
-        const { statusCode, data, errMsg, header } =
-          evt as Taro.request.SuccessCallbackResult;
-        // @ts-ignore
-        const response = {
-          ok: statusCode != null && statusCode >= 200 && statusCode < 300,
-          status: statusCode,
-          statusText: errMsg,
-          headers: new Headers(header ?? {}),
-          text: () => Promise.resolve(data),
-          json: () =>
-            new Promise((res, rej) => {
-              try {
-                res(JSON.parse(data));
-              } catch (err) {
-                rej(err);
-              }
-            }),
-        } as Response;
-
-        resolve(response);
-      },
-    });
-  });
-};
+import Taro from "@tarojs/taro";
+import Headers from "./Header";
+
+export const $fetch = (input, init) => {
+  const requestInit = (init ??
+    (typeof input === "object" ? input : {})) as RequestInit;
+  const url = typeof input === "string" ? input : (input as Request).url;
+  const method = requestInit.method ?? "GET";
+  const headers = requestInit.headers ?? {};
+  const body = requestInit.body;
+
+  headers["Content-Type"] = "application/json";
+
+  return new Promise((resolve) => {
+    Taro.request({
+      url,
+      method: method as any,
+      header: headers,
+      data: body,
+      dataType: "text",
+      complete: (evt) => {
+        const { statusCode, data, errMsg, header } =
+          evt as Taro.request.SuccessCallbackResult;
+        // @ts-ignore
+        const response = {
+          ok: statusCode != null && statusCode >= 200 && statusCode < 300,
+          status: statusCode,
+          statusText: errMsg,
+          headers: new Headers(header ?? {}),
+          text: () => Promise.resolve(data),
+          json: () =>
+            new Promise((res, rej) => {
+              try {
+                res(JSON.parse(data));
+              } catch (err) {
+                rej(err);
+              }
+            }),
+        } as Response;
+
+        resolve(response);
+      },
+    });
+  });
+};

+ 110 - 110
src/utils/index.ts

@@ -1,110 +1,110 @@
-import Taro, { getStorageSync } from "@tarojs/taro";
-import Schema, { Rules } from "async-validator";
-import baseStore from "../store/base";
-import { wechatLoginApi } from "../api";
-
-export const TOKEN_KEY = "token";
-export const NICKNAME_KEY = "nickname";
-export const USER_INFO_KEY = "userinfo";
-
-export const MUSEUM_URL = "https://www.4dkankan.com/spg.html?m=SG-fwjuL9mtG2A";
-
-export const getSceneUrl = (scene?: number) => {
-  const name = getStorageSync(NICKNAME_KEY);
-  const token = getStorageSync(TOKEN_KEY);
-
-  return `https://app.4dage.com/projects/wxcs/wx/index.html?platform=wx&name=${encodeURIComponent(
-    name
-  )}&token=${token}${typeof scene === "number" ? `&scene=${scene}` : ""}`;
-};
-
-export const createRandomString = () => {
-  const characters =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-  let randomString = "";
-  for (let i = 0; i < 2; i++) {
-    randomString += characters.charAt(
-      Math.floor(Math.random() * characters.length)
-    );
-  }
-  return randomString;
-};
-
-export const onValidateField = (value: any, rules: Rules) => {
-  const validator = new Schema(rules);
-  return new Promise<any>((resolve) => {
-    validator.validate(value, (errors) => {
-      if (errors) {
-        resolve(errors);
-      } else {
-        const errMsg = Object.keys(rules).map((key) => {
-          return {
-            field: key,
-            message: undefined,
-          };
-        });
-        resolve(errMsg);
-      }
-    });
-  });
-};
-
-export const handleValidate = async (params, formRules) => {
-  const errors = await onValidateField(params, formRules);
-  let valid = !errors || errors.filter((error) => error.message).length <= 0;
-
-  if (!valid) {
-    Taro.showToast({
-      icon: "none",
-      title: errors[0].message,
-    });
-    return false;
-  }
-
-  return true;
-};
-
-export function debounce(func: Function, time: number, immediate = false) {
-  let timer: number | null = null;
-  return (...args: any) => {
-    if (timer) clearInterval(timer);
-    if (immediate) {
-      if (!timer) func.apply(this, args);
-      timer = window.setTimeout(() => {
-        timer = null;
-      }, time);
-    } else {
-      timer = window.setTimeout(() => {
-        func.apply(this, args);
-      }, time);
-    }
-  };
-}
-
-export const login = () => {
-  return new Promise((res, rej) => {
-    Taro.login({
-      async success({ code }) {
-        try {
-          Taro.showLoading({
-            title: "登陆中...",
-          });
-
-          const data = await wechatLoginApi(code);
-          Taro.setStorageSync(TOKEN_KEY, data.token);
-          Taro.setStorageSync(USER_INFO_KEY, data.user);
-
-          baseStore.isLogin = true;
-          baseStore.userInfo = data.user;
-          baseStore.name = data.user.nickName;
-          res(data);
-        } finally {
-          Taro.hideLoading();
-        }
-      },
-      fail(err) {
-        rej(err);
-      },
-    });
-  });
-};
+import Taro, { getStorageSync } from "@tarojs/taro";
+import Schema, { Rules } from "async-validator";
+import baseStore from "../store/base";
+import { wechatLoginApi } from "../api";
+
+export const TOKEN_KEY = "token";
+export const NICKNAME_KEY = "nickname";
+export const USER_INFO_KEY = "userinfo";
+
+export const MUSEUM_URL = "https://mp-1.4dkankan.com/spg.html?m=SG-fwjuL9mtG2A";
+
+export const getSceneUrl = (scene?: number) => {
+  const name = getStorageSync(NICKNAME_KEY);
+  const token = getStorageSync(TOKEN_KEY);
+
+  return `https://wuxicharitymuseum.cn/wx/index.html?platform=wx&name=${encodeURIComponent(
+    name
+  )}&token=${token}${typeof scene === "number" ? `&scene=${scene}` : ""}`;
+};
+
+export const createRandomString = () => {
+  const characters =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+  let randomString = "";
+  for (let i = 0; i < 2; i++) {
+    randomString += characters.charAt(
+      Math.floor(Math.random() * characters.length)
+    );
+  }
+  return randomString;
+};
+
+export const onValidateField = (value: any, rules: Rules) => {
+  const validator = new Schema(rules);
+  return new Promise<any>((resolve) => {
+    validator.validate(value, (errors) => {
+      if (errors) {
+        resolve(errors);
+      } else {
+        const errMsg = Object.keys(rules).map((key) => {
+          return {
+            field: key,
+            message: undefined,
+          };
+        });
+        resolve(errMsg);
+      }
+    });
+  });
+};
+
+export const handleValidate = async (params, formRules) => {
+  const errors = await onValidateField(params, formRules);
+  let valid = !errors || errors.filter((error) => error.message).length <= 0;
+
+  if (!valid) {
+    Taro.showToast({
+      icon: "none",
+      title: errors[0].message,
+    });
+    return false;
+  }
+
+  return true;
+};
+
+export function debounce(func: Function, time: number, immediate = false) {
+  let timer: number | null = null;
+  return (...args: any) => {
+    if (timer) clearInterval(timer);
+    if (immediate) {
+      if (!timer) func.apply(this, args);
+      timer = window.setTimeout(() => {
+        timer = null;
+      }, time);
+    } else {
+      timer = window.setTimeout(() => {
+        func.apply(this, args);
+      }, time);
+    }
+  };
+}
+
+export const login = () => {
+  return new Promise((res, rej) => {
+    Taro.login({
+      async success({ code }) {
+        try {
+          Taro.showLoading({
+            title: "登陆中...",
+          });
+
+          const data = await wechatLoginApi(code);
+          Taro.setStorageSync(TOKEN_KEY, data.token);
+          Taro.setStorageSync(USER_INFO_KEY, data.user);
+
+          baseStore.isLogin = true;
+          baseStore.userInfo = data.user;
+          baseStore.name = data.user.nickName;
+          res(data);
+        } finally {
+          Taro.hideLoading();
+        }
+      },
+      fail(err) {
+        rej(err);
+      },
+    });
+  });
+};

+ 25 - 25
tsconfig.json

@@ -1,25 +1,25 @@
-{
-  "compilerOptions": {
-    "target": "es2017",
-    "module": "commonjs",
-    "removeComments": false,
-    "preserveConstEnums": true,
-    "moduleResolution": "node",
-    "experimentalDecorators": true,
-    "noImplicitAny": false,
-    "allowSyntheticDefaultImports": true,
-    "outDir": "lib",
-    "noUnusedLocals": true,
-    "noUnusedParameters": true,
-    "strictNullChecks": true,
-    "sourceMap": true,
-    "baseUrl": ".",
-    "rootDir": ".",
-    "jsx": "react-jsx",
-    "allowJs": true,
-    "resolveJsonModule": true,
-    "typeRoots": ["node_modules/@types"]
-  },
-  "include": ["./src", "global.d.ts"],
-  "compileOnSave": false
-}
+{
+  "compilerOptions": {
+    "target": "es2017",
+    "module": "commonjs",
+    "removeComments": false,
+    "preserveConstEnums": true,
+    "moduleResolution": "node",
+    "experimentalDecorators": true,
+    "noImplicitAny": false,
+    "allowSyntheticDefaultImports": true,
+    "outDir": "lib",
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "strictNullChecks": true,
+    "sourceMap": true,
+    "baseUrl": ".",
+    "rootDir": ".",
+    "jsx": "react-jsx",
+    "allowJs": true,
+    "resolveJsonModule": true,
+    "typeRoots": ["node_modules/@types"]
+  },
+  "include": ["./src", "global.d.ts"],
+  "compileOnSave": false
+}

Разница между файлами не показана из-за своего большого размера
+ 11216 - 11216
yarn.lock