Browse Source

fix: 课程链接

lvkun996 2 weeks ago
parent
commit
8362ffcf14

+ 4 - 0
components.d.ts

@@ -11,7 +11,11 @@ declare module '@vue/runtime-core' {
     OpenApp: typeof import('./src/components/OpenApp/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
+    VanButton: typeof import('vant/es')['Button']
+    VanDialog: typeof import('vant/es')['Dialog']
+    VanField: typeof import('vant/es')['Field']
     VanIcon: typeof import('vant/es')['Icon']
+    VanLoading: typeof import('vant/es')['Loading']
     VanOverlay: typeof import('vant/es')['Overlay']
     VanProgress: typeof import('vant/es')['Progress']
   }

BIN
dist.zip


+ 164 - 161
package-lock.json

@@ -1077,6 +1077,110 @@
         "tslint": "^5.20.1",
         "webpack": "^4.0.0",
         "yorkie": "^2.0.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "cosmiconfig": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+          "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "@types/parse-json": "^4.0.0",
+            "import-fresh": "^3.1.0",
+            "parse-json": "^5.0.0",
+            "path-type": "^4.0.0",
+            "yaml": "^1.7.2"
+          }
+        },
+        "deepmerge": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz",
+          "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+          "dev": true,
+          "optional": true
+        },
+        "fork-ts-checker-webpack-plugin-v5": {
+          "version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
+          "resolved": "https://registry.npmmirror.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz",
+          "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "@babel/code-frame": "^7.8.3",
+            "@types/json-schema": "^7.0.5",
+            "chalk": "^4.1.0",
+            "cosmiconfig": "^6.0.0",
+            "deepmerge": "^4.2.2",
+            "fs-extra": "^9.0.0",
+            "memfs": "^3.1.2",
+            "minimatch": "^3.0.4",
+            "schema-utils": "2.7.0",
+            "semver": "^7.3.2",
+            "tapable": "^1.0.0"
+          }
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "jsonfile": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
+          "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "graceful-fs": "^4.1.6",
+            "universalify": "^2.0.0"
+          }
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        },
+        "semver": {
+          "version": "7.7.2",
+          "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
+          "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+          "dev": true,
+          "optional": true
+        },
+        "universalify": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+          "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+          "dev": true,
+          "optional": true
+        }
       }
     },
     "@vue/cli-plugin-vuex": {
@@ -1149,6 +1253,17 @@
         "webpack-merge": "^4.2.2"
       },
       "dependencies": {
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
         "cliui": {
           "version": "6.0.0",
           "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
@@ -1160,6 +1275,37 @@
             "wrap-ansi": "^6.2.0"
           }
         },
+        "json5": {
+          "version": "2.2.3",
+          "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+          "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+          "dev": true,
+          "optional": true
+        },
+        "loader-utils": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
+          "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        },
+        "vue-loader-v16": {
+          "version": "npm:vue-loader@16.8.3",
+          "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz",
+          "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "chalk": "^4.1.0",
+            "hash-sum": "^2.0.0",
+            "loader-utils": "^2.0.0"
+          }
+        },
         "wrap-ansi": {
           "version": "6.2.0",
           "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -2226,6 +2372,16 @@
       "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
       "dev": true
     },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
     "block-stream": {
       "version": "0.0.9",
       "resolved": "https://registry.npmmirror.com/block-stream/-/block-stream-0.0.9.tgz",
@@ -2890,6 +3046,7 @@
           "dev": true,
           "optional": true,
           "requires": {
+            "bindings": "^1.5.0",
             "nan": "^2.12.1"
           }
         }
@@ -5499,6 +5656,13 @@
         }
       }
     },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
     "filesize": {
       "version": "3.6.1",
       "resolved": "https://registry.npmmirror.com/filesize/-/filesize-3.6.1.tgz",
@@ -5768,123 +5932,6 @@
         }
       }
     },
-    "fork-ts-checker-webpack-plugin-v5": {
-      "version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
-      "resolved": "https://registry.npmmirror.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz",
-      "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "@babel/code-frame": "^7.8.3",
-        "@types/json-schema": "^7.0.5",
-        "chalk": "^4.1.0",
-        "cosmiconfig": "^6.0.0",
-        "deepmerge": "^4.2.2",
-        "fs-extra": "^9.0.0",
-        "memfs": "^3.1.2",
-        "minimatch": "^3.0.4",
-        "schema-utils": "2.7.0",
-        "semver": "^7.3.2",
-        "tapable": "^1.0.0"
-      },
-      "dependencies": {
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "cosmiconfig": {
-          "version": "6.0.0",
-          "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
-          "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "@types/parse-json": "^4.0.0",
-            "import-fresh": "^3.1.0",
-            "parse-json": "^5.0.0",
-            "path-type": "^4.0.0",
-            "yaml": "^1.7.2"
-          }
-        },
-        "deepmerge": {
-          "version": "4.3.1",
-          "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz",
-          "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
-          "dev": true,
-          "optional": true
-        },
-        "fs-extra": {
-          "version": "9.1.0",
-          "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
-          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "at-least-node": "^1.0.0",
-            "graceful-fs": "^4.2.0",
-            "jsonfile": "^6.0.1",
-            "universalify": "^2.0.0"
-          }
-        },
-        "jsonfile": {
-          "version": "6.1.0",
-          "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
-          "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "graceful-fs": "^4.1.6",
-            "universalify": "^2.0.0"
-          }
-        },
-        "lru-cache": {
-          "version": "6.0.0",
-          "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
-          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "yallist": "^4.0.0"
-          }
-        },
-        "schema-utils": {
-          "version": "2.7.0",
-          "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.0.tgz",
-          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "@types/json-schema": "^7.0.4",
-            "ajv": "^6.12.2",
-            "ajv-keywords": "^3.4.1"
-          }
-        },
-        "semver": {
-          "version": "7.6.0",
-          "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz",
-          "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "lru-cache": "^6.0.0"
-          }
-        },
-        "universalify": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
-          "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
     "form-data": {
       "version": "2.3.3",
       "resolved": "https://registry.npmmirror.com/form-data/-/form-data-2.3.3.tgz",
@@ -13284,50 +13331,6 @@
         }
       }
     },
-    "vue-loader-v16": {
-      "version": "npm:vue-loader@16.8.3",
-      "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz",
-      "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "chalk": "^4.1.0",
-        "hash-sum": "^2.0.0",
-        "loader-utils": "^2.0.0"
-      },
-      "dependencies": {
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "json5": {
-          "version": "2.2.3",
-          "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
-          "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
-          "dev": true,
-          "optional": true
-        },
-        "loader-utils": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
-          "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^3.0.0",
-            "json5": "^2.1.2"
-          }
-        }
-      }
-    },
     "vue-router": {
       "version": "4.1.6",
       "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz",

+ 41 - 0
src/api/common.ts

@@ -20,3 +20,44 @@ export function getParentWXSignature () {
     params: { url }
   })
 }
+
+
+// 短信登录
+export function smsLogin (phone: string, code: string) {
+  return request({
+    url: `app/sms/checkCode/app/${phone}/${code}`,
+    method: 'POST',
+  })
+}
+
+// 根据手机号获取验证码
+export function getSmsByPhone (phone: string) {
+  return request({
+    url: `app/sms/getCode/${phone}`,
+    method: 'GET',
+  })
+}
+
+// 领取vip
+export function collectCoursePackage (vipType: number) {
+  return request({
+    url: `app/ai/course/vip/add/${vipType}`,
+    method: 'POST',
+  })
+}
+
+// 领取课程包
+export function addAiCourse () {
+  return request({
+    url: `app/ai/course/vip/addAiCourse`,
+    method: 'POST',
+  })
+}
+
+// 获取课程包详情
+export function getAiCourseInfo () {
+  return request({
+    url: `app/ai/course/vip/getAiCourseInfo`,
+    method: 'GET',
+  })
+}

BIN
src/assets/logo.png


+ 30 - 0
src/pages/CollectCourses/App.vue

@@ -0,0 +1,30 @@
+<template>
+  <router-view />
+</template>
+
+<script lang="ts" setup >
+
+</script>
+
+<style lang="scss">
+#app {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+}
+
+#nav {
+  padding: 30px;
+
+  a {
+    font-weight: bold;
+    color: #2c3e50;
+
+    &.router-link-exact-active {
+      color: #42b983;
+    }
+  }
+}
+</style>

+ 11 - 0
src/pages/CollectCourses/main.ts

@@ -0,0 +1,11 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import router from './router'
+import '@/utils/vant'
+
+import 'lib-flexible'
+import 'normalize.css'
+import * as Pinia from 'pinia'
+import { Toast } from 'vant'
+
+createApp(App).use(router).use(Toast).use(Pinia.createPinia()).mount('#CollectCourses')

+ 17 - 0
src/pages/CollectCourses/public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= htmlWebpackPlugin.options.title %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="CollectCourses"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 26 - 0
src/pages/CollectCourses/router/index.ts

@@ -0,0 +1,26 @@
+import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
+
+const routes: Array<RouteRecordRaw> = [
+  {
+    path: '/login',
+    name: 'login',
+    component: () => import('../views/login.vue')
+  },
+  {
+    path: '/collect-courses',
+    name: 'collect-courses',
+    component: () => import('../views/collect.vue')
+  },
+  {
+    path: '/vip',
+    name: 'vip',
+    component: () => import('../views/vip.vue')
+  }
+]
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes
+})
+
+export default router

+ 571 - 0
src/pages/CollectCourses/views/collect.vue

@@ -0,0 +1,571 @@
+<template>
+  <div class="collect-container">
+    <div class="content" v-if="courseInfo">
+      <!-- 课程模块 -->
+      <div class="module-card">
+        <h3 class="module-title">课程包</h3>
+        <div class="course-package">
+          <img class="course-package-img" :src="courseInfo.imgCoverMini || 'https://zaojiao.net/admin/course-package.png'" :alt="courseInfo.name" />
+          <div class="price-tag">
+            <span class="current-price">{{ courseInfo.price }}元</span>
+            <span class="original-price">{{ courseInfo.markingPrice }}元</span>
+          </div>
+          <div class="course-info">
+            <h2 class="course-title">{{ courseInfo.name || '早教启蒙课程包' }}</h2>
+            <div class="course-desc" v-html="courseInfo.description"></div>
+            <div class="notice-section">
+              <div class="notice-title">注意:</div>
+              <ul class="notice-list">
+                <li>未在有效期内使用视为自动放弃,不予补发或延期</li>
+                <li>若同时拥有多张会员卡,有效期可叠加</li>
+              </ul>
+              <div class="disclaimer">
+                活动最终解释权归逻辑狗所有
+              </div>
+            </div>
+            <div class="suit-age" v-if="courseInfo.suitAge">
+              <van-icon name="friends-o" color="#0643A2" />
+              <span>适合年龄: {{ courseInfo.suitAge }}</span>
+            </div>
+            <ul class="feature-list">
+              <li v-for="(feature, index) in features" :key="index">
+                <van-icon name="checked" color="#0643A2" /> {{ feature }}
+              </li>
+            </ul>
+          </div>
+        </div>
+        
+        <van-button
+          type="primary"
+          class="collect-button"
+          @click="showCoursesConfirmDialog = true"
+        >
+          立即领取课程包
+        </van-button>
+      </div>
+      
+      <!-- VIP特权模块 -->
+      <!-- <div class="module-card">
+        <div class="vip-package">
+    
+          <div class="vip-info">
+            <h2 class="vip-title">VIP会员特权</h2>
+            <div class="vip-benefits">
+              <p><van-icon name="star" color="#FFD700" /> 专属客服一对一服务</p>
+              <p><van-icon name="star" color="#FFD700" /> 课程内容不限次数观看</p>
+              <p><van-icon name="star" color="#FFD700" /> 独家学习资料下载</p>
+              <p><van-icon name="star" color="#FFD700" /> 专家在线答疑</p>
+            </div>
+          </div>
+        </div>
+        
+        <van-button
+          type="primary"
+          class="collect-button vip-button"
+          @click="showVIPConfirmDialog = true"
+        >
+          立即领取VIP特权
+        </van-button>
+      </div> -->
+    </div>
+    
+    <div class="loading" v-else>
+      <van-loading type="spinner" color="#0643A2" size="48px" />
+      <p>加载中...</p>
+    </div>
+    
+    <!-- 课程确认弹窗 -->
+    <van-dialog
+      v-model:show="showCoursesConfirmDialog"
+      title="确认领取"
+      confirm-button-text="确认领取"
+      @confirm="handleCollectCourses"
+    >
+      <div class="dialog-content">
+        <van-icon name="gift-o" size="40" color="#0643A2" />
+        <p>您即将领取{{ courseInfo?.name || '课程包' }}</p>
+        <p class="small-text">领取后可在"我的课程"中查看</p>
+      </div>
+    </van-dialog>
+    
+    <!-- VIP确认弹窗 -->
+    <van-dialog
+      v-model:show="showVIPConfirmDialog"
+      title="确认领取"
+      confirm-button-text="确认领取"
+      @confirm="handleCollectVIP"
+    >
+      <div class="dialog-content">
+        <van-icon name="gem-o" size="40" color="#FFD700" />
+        <p>您即将领取VIP会员特权</p>
+        <p class="small-text">领取后即可享受VIP专属服务</p>
+      </div>
+    </van-dialog>
+    
+    <!-- 领取结果弹窗 -->
+    <van-dialog
+      v-model:show="showResultDialog"
+      title="领取结果"
+      :show-cancel-button="false"
+      confirm-button-text="我知道了"
+    >
+      <div class="dialog-content">
+        <van-icon :name="collectResult.icon" size="40" :color="collectResult.color" />
+        <p>{{ collectResult.message }}</p>
+      </div>
+    </van-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { addAiCourse, collectCoursePackage, getAiCourseInfo } from '@/api/common';
+import { showToast } from 'vant';
+import 'vant/lib/toast/style';
+import { computed, onMounted, reactive, ref } from 'vue';
+
+interface CourseInfo {
+  name?: string;
+  imgCoverMini?: string;
+  price?: string | number;
+  markingPrice?: string | number;
+  suitAge?: string;
+  description?: string;
+  simpleDescription?: string;
+}
+
+interface ApiResponse {
+  data?: {
+    status: number;
+  };
+  msg?: string;
+}
+
+const showCoursesConfirmDialog = ref(false)
+const showVIPConfirmDialog = ref(false)
+const showResultDialog = ref(false)
+const courseInfo = ref<CourseInfo | null>(null)
+const isLoading = ref(true)
+
+const features = computed(() => {
+  if (!courseInfo.value || !courseInfo.value.simpleDescription) return []
+  return courseInfo.value.simpleDescription.split('\n').filter((item: string) => item.trim())
+})
+
+const collectResult = reactive({
+  icon: 'success',
+  color: '#0643A2',
+  message: '领取成功!'
+})
+
+const fetchCourseInfo = async () => {
+  try {
+    isLoading.value = true
+    const res = await getAiCourseInfo()
+    if (res && res.data) {
+      courseInfo.value = res.data
+    } else {
+      showToast('获取课程信息失败')
+    }
+  } catch (error) {
+    showToast('获取课程信息失败')
+  } finally {
+    isLoading.value = false
+  }
+}
+
+const handleCollectCourses = async () => {
+  try {
+    const res = await addAiCourse() as ApiResponse
+    const isSuccess = res?.data?.status === 200
+
+    collectResult.icon = isSuccess ? 'success' : 'warning-o'
+    collectResult.color = isSuccess ? '#0643A2' : '#ff976a'
+    collectResult.message = isSuccess
+      ? `恭喜您,${courseInfo.value?.name || '课程包'}领取成功!`
+      : res?.msg || '领取失败,请稍后再试'
+
+    showResultDialog.value = true
+  } catch (error) {
+    showToast('领取失败,请稍后再试')
+  }
+}
+
+const handleCollectVIP = async () => {
+  try {
+    // 假设VIP类型为1,如果需要动态获取,可以从路由或其他地方获取
+    const res = await collectCoursePackage(1) as ApiResponse
+    const isSuccess = res?.data?.status === 200
+
+    collectResult.icon = isSuccess ? 'success' : 'warning-o'
+    collectResult.color = isSuccess ? '#FFD700' : '#ff976a'
+    collectResult.message = isSuccess
+      ? '恭喜您,VIP特权领取成功!'
+      : res?.msg || '领取失败,请稍后再试'
+
+    showResultDialog.value = true
+  } catch (error) {
+    showToast('领取失败,请稍后再试')
+  }
+}
+
+onMounted(() => {
+  fetchCourseInfo()
+})
+</script>
+
+<style lang="scss" scoped>
+.collect-container {
+  width: 100%;
+  min-height: 100vh;
+  background: linear-gradient(135deg, #e1f5fe 0%, #ffffff 100%);
+  background-image: url('https://zaojiao.net/admin/bg-pattern.png');
+  background-repeat: repeat;
+  background-size: 200px;
+  padding: 20px;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 150px;
+    background-image: url('https://zaojiao.net/admin/top-wave.png');
+    background-size: cover;
+    background-position: bottom;
+    z-index: 1;
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 150px;
+    background-image: url('https://zaojiao.net/admin/bottom-wave.png');
+    background-size: cover;
+    background-position: top;
+    z-index: 1;
+  }
+}
+
+.loading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  z-index: 2;
+
+  p {
+    color: #0643A2;
+    margin-top: 16px;
+    font-size: 16px;
+  }
+}
+
+.content {
+  width: 98%;
+  max-width: 500px;
+  display: flex;
+  flex-direction: column;
+  gap: 30px;
+  margin: 40px 0;
+  margin-top: 0px;
+  z-index: 2;
+}
+
+.module-card {
+  background-color: rgba(255, 255, 255, 0.9);
+  border-radius: 16px;
+  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
+  padding: 24px;
+  position: relative;
+  overflow: hidden;
+}
+
+.module-title {
+  margin: 0 0 20px 0;
+  color: #0643A2;
+  text-align: center;
+  font-weight: 600;
+  position: relative;
+  padding-bottom: 10px;
+
+  &::after {
+    content: '';
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    transform: translateX(-50%);
+    width: 50px;
+    height: 3px;
+    background-color: #0643A2;
+    border-radius: 3px;
+  }
+}
+
+.course-package, .vip-package {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 30px;
+  position: relative;
+}
+
+.course-package-img, .vip-img {
+  width: 90%;
+  max-width: 300px;
+  border-radius: 12px;
+  margin-bottom: 20px;
+  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+  object-fit: cover;
+}
+
+.course-package-img {
+  aspect-ratio: 16/9;
+}
+
+.vip-img {
+  aspect-ratio: 1/1;
+  max-width: 150px;
+  border-radius: 50%;
+  border: 3px solid #FFD700;
+  padding: 5px;
+  background-color: #fff;
+}
+
+.price-tag {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  background-color: rgba(255, 72, 72, 0.9);
+  padding: 5px 10px;
+  border-radius: 20px;
+  color: white;
+  font-weight: bold;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+
+  .current-price {
+    font-size: 18px;
+  }
+
+  .original-price {
+    font-size: 12px;
+    text-decoration: line-through;
+    opacity: 0.8;
+  }
+}
+
+.course-info, .vip-info {
+  width: 100%;
+}
+
+.course-title, .vip-title {
+  font-size: 22px;
+  color: #333;
+  margin: 0 0 10px 0;
+  text-align: center;
+}
+
+.vip-title {
+  color: #FFD700;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+}
+
+.course-desc {
+  width: 100%;
+  height: 650px;
+  overflow: hidden;
+  overflow-y: auto;
+  font-size: 14px;
+  color: #666;
+  margin-bottom: 15px;
+  text-align: center;
+  line-height: 1.5;
+
+  :deep(img) {
+    max-width: 100%;
+    height: auto;
+    display: block;
+    margin: 0 auto;
+  }
+
+  :deep(p) {
+    margin-bottom: 12px;
+  }
+}
+
+.vip-benefits {
+  margin-bottom: 15px;
+
+  p {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin: 10px 0;
+    font-size: 14px;
+    color: #333;
+
+    .van-icon {
+      flex-shrink: 0;
+    }
+  }
+}
+
+.suit-age {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+  color: #0643A2;
+  background-color: rgba(6, 67, 162, 0.1);
+  padding: 5px 10px;
+  border-radius: 20px;
+  width: fit-content;
+  margin-left: auto;
+  margin-right: auto;
+
+  span {
+    margin-left: 5px;
+  }
+}
+
+.feature-list {
+  list-style: none;
+  padding: 0;
+  margin: 0 0 20px 0;
+
+  li {
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+    margin-bottom: 8px;
+    color: #444;
+
+    .van-icon {
+      margin-right: 8px;
+    }
+  }
+}
+
+.collect-button {
+  width: 100%;
+  height: 44px;
+  border-radius: 22px;
+  font-size: 16px;
+  font-weight: 500;
+  background-color: #0643A2;
+  border: none;
+  box-shadow: 0 4px 10px rgba(6, 67, 162, 0.3);
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: -50%;
+    left: -50%;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0) 70%);
+    transform: scale(0);
+    opacity: 0;
+    transition: transform 0.5s, opacity 0.5s;
+  }
+
+  &:active::before {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+.vip-button {
+  background-color: #FFD700;
+  color: #333;
+  box-shadow: 0 4px 10px rgba(255, 215, 0, 0.3);
+}
+
+.dialog-content {
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  p {
+    margin: 10px 0 0;
+    font-size: 16px;
+    color: #333;
+  }
+
+  .small-text {
+    font-size: 12px;
+    color: #999;
+  }
+}
+
+.notice-section {
+  width: 100%;
+  padding: 16px;
+  background-color: rgba(6, 67, 162, 0.05);
+  border-radius: 8px;
+  margin: 20px 0;
+
+  .notice-title {
+    color: #0643A2;
+    font-weight: 500;
+    font-size: 14px;
+    margin-bottom: 8px;
+  }
+
+  .notice-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+
+    li {
+      position: relative;
+      padding-left: 12px;
+      color: #666;
+      font-size: 12px;
+      line-height: 1.6;
+      margin-bottom: 6px;
+
+      &::before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 8px;
+        width: 4px;
+        height: 4px;
+        border-radius: 50%;
+        background-color: #0643A2;
+      }
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  .disclaimer {
+    margin-top: 12px;
+    padding-top: 12px;
+    border-top: 1px solid rgba(6, 67, 162, 0.1);
+    font-size: 12px;
+    color: #999;
+    text-align: center;
+  }
+}
+</style>

+ 238 - 0
src/pages/CollectCourses/views/login.vue

@@ -0,0 +1,238 @@
+<template>
+  <div class='login'>
+      <img class='logo' :src="require('@/assets/logo.png')" />
+      <span class='label'>{{title}}</span>
+      <div class="form" >
+        <div class="form-item" >
+          <div class="title" >
+            <van-icon name="desktop-o" />
+            手机号
+          </div>
+          <div class="field-container">
+            <van-field class="filed" v-model="loginState.username" placeholder="请输入手机号" />
+            <div class="send-code-btn" @click="sendCode" :class="{ 'disabled': !canSendCode || countdown > 0 }">
+              {{ countdown > 0 ? `${countdown}s` : '发送验证码' }}
+            </div>
+          </div>
+        </div>
+        <div class="form-item" >
+          <div class="title" >
+            <van-icon name="discount-o" />
+            验证码
+          </div>
+          <van-field class="filed" v-model="loginState.code" placeholder="请输入验证码" />
+        </div>
+      </div>
+      <div class='phone-button' @click="handleLogin" >
+          登录
+      </div>
+    </div>
+</template>
+<script setup lang='ts'>
+import { getSmsByPhone, smsLogin } from '@/api/common'
+import { showToast } from 'vant'
+import { computed, onMounted, ref } from 'vue'
+import { useTokenStore } from '@/store'
+import { useRouter, useRoute } from 'vue-router'
+import 'vant/lib/toast/style'
+
+const loginState = ref({
+  username: '',
+  code: ''
+})
+
+const { set: setToken } = useTokenStore()
+
+const countdown = ref(0)
+const timer = ref<number | null>(null)
+
+const router = useRouter()
+
+const route = useRoute()
+
+const title = computed(() => {
+  return route.query.path === 'course' ? '卢菲菲亲子记忆力课程' : `逻辑狗APP${route.query.type}天特权领取`
+})
+
+const canSendCode = computed(() => {
+  return loginState.value.username.length === 11
+})
+
+
+const sendCode = async () => {
+  if (!canSendCode.value || countdown.value > 0) return
+  
+  try {
+    await getSmsByPhone(loginState.value.username)
+    showToast('验证码发送成功')
+    
+    // Start countdown
+    countdown.value = 60
+    timer.value = window.setInterval(() => {
+      countdown.value--
+      if (countdown.value <= 0) {
+        clearInterval(timer.value as number)
+        timer.value = null
+      }
+    }, 1000)
+  } catch (error) {
+    showToast('验证码发送失败')
+  }
+}
+
+const handleLogin = async () => {
+  if (!loginState.value.username) {
+    return showToast('请输入手机号')
+  }
+  
+  if (loginState.value.username.length !== 11) {
+    return showToast('请输入正确的手机号')
+  }
+  
+  if (!loginState.value.code) {
+    return showToast('请输入验证码')
+  }
+  
+  try {
+    const { data, status, msg } = await smsLogin(loginState.value.username, loginState.value.code)
+    console.log('status',msg);
+    
+    if (status === 200 ) {
+      console.log('???');
+      
+      showToast('登录成功')
+      
+      useTokenStore().set(data.accessToken)
+   
+      if (route.query.path === 'course') {
+        router.push({ path: '/collect-courses' })
+      } else {
+        router.push({ 
+          path: '/vip',
+          query: {
+            type: route.query.type
+          }
+        })
+      }
+
+    } else {
+      showToast(msg)
+    }
+
+  } catch (error) {
+    showToast('登录失败')
+  }
+}
+
+onMounted(() => {
+  // Clear timer when component unmounts
+  return () => {
+    if (timer.value) {
+      clearInterval(timer.value)
+    }
+  }
+})
+</script>
+<style lang='scss' scoped >
+.login {
+    width: 100vw;
+    height: 100vh;
+    font-family: PingFangSC-Medium, PingFang SC;
+    position: relative;
+    background-color: #FFFFFF;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding-top: 120px;
+    box-sizing: border-box;
+    overflow: hidden;
+}
+
+.logo {
+  width: 80px;
+  height: 80px;
+  display: block;
+}
+
+.label {
+    margin-top: 28px;
+    font-size: 20px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    color: #333333;
+}
+
+.form {
+  width: 300px;
+  margin-top: 50px;
+  .form-item {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    margin-bottom: 24px;
+    .title {
+      text-align: left;
+      margin-bottom: 12px;
+    }
+    .filed {
+      width: 300px;
+      height: 43px;
+      font-size: 16px;
+      background-color: rgb(229, 239, 238);
+      border-radius: 12px;
+      text-align: center;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      padding-left: 12px;
+      color: #000;
+    }
+    .field-container {
+      position: relative;
+      width: 100%;
+      
+      .filed {
+        padding-right: 120px;
+      }
+      
+      .send-code-btn {
+        position: absolute;
+        right: 10px;
+        top: 50%;
+        transform: translateY(-50%);
+        font-size: 14px;
+        color: #0643A2;
+        cursor: pointer;
+        
+        &.disabled {
+          color: #999;
+          cursor: not-allowed;
+        }
+      }
+    }
+  }
+}
+
+.phone-button {
+    width: 296px;
+    height: 42px;
+    border-radius: 21px;
+    border: 1px solid #0643A2;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 16px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    background-color: #0643A2;
+    color: #FFFFFF;
+    margin-top: 63px;
+}
+
+.tip {
+    font-size: 12px;
+    font-weight: 400;
+    color: #999999;
+}
+</style>

+ 485 - 0
src/pages/CollectCourses/views/vip.vue

@@ -0,0 +1,485 @@
+<template>
+  <div class="collect-container">
+
+      
+      <!-- VIP特权模块 -->
+      <div class="module-card">
+        <div class="vip-package">
+          <div class="vip-info">
+            <h2 class="vip-title">VIP会员特权</h2>
+            <div class="vip-benefits">
+              <p><van-icon name="star" color="#FFD700" /> 仅限注册用户领取,每位用户(同一手机号/设备/IP)限领1次</p>
+              <p><van-icon name="star" color="#FFD700" /> 在活动页面【绑定手机号】点击「立即领取」,系统自动发放至逻辑狗APP账户</p>
+              <p><van-icon name="star" color="#FFD700" /> 逻辑狗APP站内100+课程限时免费观看</p>
+              <p><van-icon name="star" color="#FFD700" /> 有效期{{route.query.type}}天,自成功领取后即时生效,{{route.query.type}}天后自动失效</p>
+            </div>
+            <div class="notice-section">
+              <div class="notice-title">注意:</div>
+              <ul class="notice-list">
+                <li>未在有效期内使用视为自动放弃,不予补发或延期。</li>
+                <li>若同时拥有多张会员卡,有效期可叠加。</li>
+              </ul>
+              <div class="disclaimer">
+                活动最终解释权归逻辑狗所有
+              </div>
+            </div>
+          </div>
+        </div>
+        
+        <van-button
+          type="primary"
+          class="collect-button vip-button"
+          @click="showVIPConfirmDialog = true"
+        >
+          立即领取{{route.query.type}}天VIP特权
+        </van-button>
+      </div>
+    </div>
+    
+    
+    <!-- VIP确认弹窗 -->
+    <van-dialog
+      v-model:show="showVIPConfirmDialog"
+      title="确认领取"
+      confirm-button-text="确认领取"
+      @confirm="handleCollectVIP"
+    >
+      <div class="dialog-content">
+        <van-icon name="gem-o" size="40" color="#FFD700" />
+        <p>您即将领取VIP会员特权</p>
+        <p class="small-text">领取后即可享受VIP专属服务</p>
+      </div>
+    </van-dialog>
+    
+    <!-- 领取结果弹窗 -->
+    <van-dialog
+      v-model:show="showResultDialog"
+      title="领取结果"
+      :show-cancel-button="false"
+      confirm-button-text="我知道了"
+    >
+      <div class="dialog-content">
+        <van-icon :name="collectResult.icon" size="40" :color="collectResult.color" />
+        <p>{{ collectResult.message }}</p>
+      </div>
+    </van-dialog>
+
+</template>
+
+<script lang="ts" setup>
+import { collectCoursePackage } from '@/api/common';
+import { showToast } from 'vant';
+import 'vant/lib/toast/style';
+import { computed, reactive, ref } from 'vue';
+import { useRoute } from 'vue-router';
+
+interface CourseInfo {
+  name?: string;
+  imgCoverMini?: string;
+  price?: string | number;
+  markingPrice?: string | number;
+  suitAge?: string;
+  description?: string;
+  simpleDescription?: string;
+}
+
+const showCoursesConfirmDialog = ref(false)
+const showVIPConfirmDialog = ref(false)
+const showResultDialog = ref(false)
+const courseInfo = ref<CourseInfo | null>(null)
+const isLoading = ref(true)
+const route = useRoute()
+
+const features = computed(() => {
+  if (!courseInfo.value || !courseInfo.value.simpleDescription) return []
+  return courseInfo.value.simpleDescription.split('\n').filter((item: string) => item.trim())
+})
+
+const collectResult = reactive({
+  icon: 'success',
+  color: '#0643A2',
+  message: '领取成功!'
+})
+
+const handleCollectCourses = async () => {
+
+}
+
+const handleCollectVIP = async () => {
+  try {
+    const res = await collectCoursePackage(route.query.type)
+    
+    const isSuccess = res?.status === 200
+
+    collectResult.icon = isSuccess ? 'success' : 'warning-o'
+    collectResult.color = isSuccess ? '#FFD700' : '#ff976a'
+    collectResult.message = isSuccess
+      ? '恭喜您,VIP特权领取成功!'
+      : res?.msg || '用户已拥有vip特权'
+
+    showResultDialog.value = true
+  } catch (error) {
+    showToast('领取失败,请稍后再试')
+  }
+}
+
+</script>
+
+<style lang="scss" scoped>
+.collect-container {
+  width: 100%;
+  min-height: 100vh;
+  background: linear-gradient(135deg, #e1f5fe 0%, #ffffff 100%);
+  background-image: url('https://zaojiao.net/admin/bg-pattern.png');
+  background-repeat: repeat;
+  background-size: 200px;
+  padding: 20px;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 150px;
+    background-image: url('https://zaojiao.net/admin/top-wave.png');
+    background-size: cover;
+    background-position: bottom;
+    z-index: 1;
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 150px;
+    background-image: url('https://zaojiao.net/admin/bottom-wave.png');
+    background-size: cover;
+    background-position: top;
+    z-index: 1;
+  }
+}
+
+.loading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  z-index: 2;
+
+  p {
+    color: #0643A2;
+    margin-top: 16px;
+    font-size: 16px;
+  }
+}
+
+.content {
+  width: 98%;
+  max-width: 500px;
+  display: flex;
+  flex-direction: column;
+  gap: 30px;
+  margin: 40px 0;
+  margin-top: 0px;
+  z-index: 2;
+}
+
+.module-card {
+  width: 90%;
+  height: 450px;
+  background-color: rgba(255, 255, 255, 0.9);
+  border-radius: 16px;
+  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
+  padding: 24px;
+  position: relative;
+  overflow: hidden;
+}
+
+.module-title {
+  margin: 0 0 20px 0;
+  color: #0643A2;
+  text-align: center;
+  font-weight: 600;
+  position: relative;
+  padding-bottom: 10px;
+
+  &::after {
+    content: '';
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    transform: translateX(-50%);
+    width: 50px;
+    height: 3px;
+    background-color: #0643A2;
+    border-radius: 3px;
+  }
+}
+
+.course-package, .vip-package {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 30px;
+  position: relative;
+}
+
+.course-package-img, .vip-img {
+  width: 90%;
+  max-width: 300px;
+  border-radius: 12px;
+  margin-bottom: 20px;
+  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+  object-fit: cover;
+}
+
+.course-package-img {
+  aspect-ratio: 16/9;
+}
+
+.vip-img {
+  aspect-ratio: 1/1;
+  max-width: 150px;
+  border-radius: 50%;
+  border: 3px solid #FFD700;
+  padding: 5px;
+  background-color: #fff;
+}
+
+.price-tag {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  background-color: rgba(255, 72, 72, 0.9);
+  padding: 5px 10px;
+  border-radius: 20px;
+  color: white;
+  font-weight: bold;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+
+  .current-price {
+    font-size: 18px;
+  }
+
+  .original-price {
+    font-size: 12px;
+    text-decoration: line-through;
+    opacity: 0.8;
+  }
+}
+
+.course-info, .vip-info {
+  width: 100%;
+}
+
+.course-title, .vip-title {
+  font-size: 22px;
+  color: #333;
+  margin: 0 0 10px 0;
+  text-align: center;
+}
+
+.vip-title {
+  color: #FFD700;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+}
+
+.course-desc {
+  width: 100%;
+  max-height: 650px;
+  overflow: hidden;
+  overflow-y: auto;
+  font-size: 14px;
+  color: #666;
+  margin-bottom: 15px;
+  text-align: center;
+  line-height: 1.5;
+
+  :deep(img) {
+    max-width: 100%;
+    height: auto;
+    display: block;
+    margin: 0 auto;
+  }
+
+  :deep(p) {
+    margin-bottom: 12px;
+  }
+}
+
+.vip-benefits {
+  margin-bottom: 15px;
+
+  p {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin: 10px 0;
+    font-size: 14px;
+    color: #333;
+
+    .van-icon {
+      flex-shrink: 0;
+    }
+  }
+}
+
+.suit-age {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+  color: #0643A2;
+  background-color: rgba(6, 67, 162, 0.1);
+  padding: 5px 10px;
+  border-radius: 20px;
+  width: fit-content;
+  margin-left: auto;
+  margin-right: auto;
+
+  span {
+    margin-left: 5px;
+  }
+}
+
+.feature-list {
+  list-style: none;
+  padding: 0;
+  margin: 0 0 20px 0;
+
+  li {
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+    margin-bottom: 8px;
+    color: #444;
+
+    .van-icon {
+      margin-right: 8px;
+    }
+  }
+}
+
+.collect-button {
+  width: 100%;
+  height: 44px;
+  border-radius: 22px;
+  font-size: 16px;
+  font-weight: 500;
+  background-color: #0643A2;
+  border: none;
+  box-shadow: 0 4px 10px rgba(6, 67, 162, 0.3);
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: -50%;
+    left: -50%;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0) 70%);
+    transform: scale(0);
+    opacity: 0;
+    transition: transform 0.5s, opacity 0.5s;
+  }
+
+  &:active::before {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+.vip-button {
+  background-color: #FFD700;
+  color: #333;
+  box-shadow: 0 4px 10px rgba(255, 215, 0, 0.3);
+}
+
+.dialog-content {
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  p {
+    margin: 10px 0 0;
+    font-size: 16px;
+    color: #333;
+  }
+
+  .small-text {
+    font-size: 12px;
+    color: #999;
+  }
+}
+
+.notice-section {
+  width: 100%;
+  padding: 6px;
+  background-color: rgba(6, 67, 162, 0.05);
+  border-radius: 8px;
+  margin: 20px 0;
+
+  .notice-title {
+    color: #0643A2;
+    font-weight: 500;
+    font-size: 14px;
+    margin-bottom: 8px;
+  }
+
+  .notice-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+
+    li {
+      position: relative;
+      padding-left: 12px;
+      color: #666;
+      font-size: 12px;
+      line-height: 1.6;
+      margin-bottom: 6px;
+
+      &::before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 8px;
+        width: 4px;
+        height: 4px;
+        border-radius: 50%;
+        background-color: #0643A2;
+      }
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  .disclaimer {
+    margin-top: 12px;
+    padding-top: 12px;
+    border-top: 1px solid rgba(6, 67, 162, 0.1);
+    font-size: 12px;
+    color: #999;
+    text-align: center;
+  }
+}
+</style>

+ 0 - 8
src/router/index.ts

@@ -7,14 +7,6 @@ const routes: Array<RouteRecordRaw> = [
     name: 'Home',
     component: Home
   },
-  {
-    path: '/about',
-    name: 'About',
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
-  }
 ]
 
 const router = createRouter({

+ 7 - 2
src/service/request.ts

@@ -1,7 +1,7 @@
 import { useTokenStore } from '@/store'
 import axios, { AxiosResponse } from 'axios'
 
-const baseURL = process.env.NODE_ENV === 'development' ? '/api' : '/zd-api'
+const baseURL = process.env.NODE_ENV === 'development' ? '/zd-api' : '/zd-api'
 // const baseURL = process.env.NODE_EN === 'development' ? 'https://open.api.luojigou.vip/' : '/zd-api'
 // const baseURL = process.env.NODE_ENV === 'development' ? '/https://open.api.luojigou.vip/' : '/zd-api'
 // const baseURL = '/zd-api'
@@ -14,8 +14,13 @@ const instance = axios.create({
 })
 
 instance.interceptors.request.use((config) => {
-  config.headers.token = useTokenStore().get()
+  try {
+    config.headers.token = useTokenStore().get()
+  } catch (error) {
+    
+  }
 
+  
   return config
 }, function (error) {
   // 对请求错误做些什么

+ 1 - 1
src/store/token.ts

@@ -8,7 +8,7 @@ export const useTokenStore = defineStore(StoreEnum.TOKEN, () => {
 
   return {
     get: () => state(),
-    set: (token: string) => setState(token)
+    set: (token: string) =>  setState(token)
   }
 
 })

+ 8 - 2
vue.config.js

@@ -50,7 +50,12 @@ module.exports = {
       entry: 'src/pages/Pay/main.ts',
       template: 'src/pages/Pay/public/index.html',
       filename: 'pay.html'
-    }
+    },
+    pay: {
+      entry: 'src/pages/CollectCourses/main.ts',
+      template: 'src/pages/CollectCourses/public/index.html',
+      filename: 'collect-courses.html'
+    },
   },
   chainWebpack: config => {
     config.resolve.alias
@@ -79,7 +84,8 @@ module.exports = {
       },
       '/zd-api/': {
         // target: 'https://open.test.luojigou.vip/', // 开发环境
-        target: 'https://open.api.luojigou.vip/', // 生产环境
+        // target: 'https://open.api.luojigou.vip/', // 生产环境
+        target: 'http://192.168.1.105:8083/', // 生产环境
         ws: false,
         secure: false,
         changeOrigin: true,

File diff suppressed because it is too large
+ 260 - 238
yarn.lock


Some files were not shown because too many files changed in this diff