feat: 实现前端核心功能模块与路由配置
新增多个功能页面包括AI投资、股票市场、收益预测等,并完成路由配置 添加登录注册页面及功能,实现全局错误处理 引入Element Plus UI库,优化移动端导航体验 创建仪表盘和主页布局,完善侧边栏和底部导航组件
This commit is contained in:
parent
f13a25c40e
commit
56ebfda34e
235
services/frontend/package-lock.json
generated
235
services/frontend/package-lock.json
generated
@ -9,8 +9,9 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"element-plus": "^2.10.4",
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-service": "~5.0.8",
|
||||
@ -148,6 +149,15 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
|
||||
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@ -158,6 +168,15 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
||||
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||
@ -221,6 +240,31 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz",
|
||||
"integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz",
|
||||
"integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.2",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
@ -504,6 +548,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"name": "@sxzz/popperjs-es",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
|
||||
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
||||
@ -705,6 +760,21 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
@ -817,6 +887,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
@ -1283,6 +1359,94 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||
@ -1739,6 +1903,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-validator": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
|
||||
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@ -2890,6 +3060,12 @@
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
@ -3280,6 +3456,32 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.10.4",
|
||||
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.10.4.tgz",
|
||||
"integrity": "sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.4.1",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/lodash-es": "^4.17.6",
|
||||
"@vueuse/core": "^9.1.0",
|
||||
"async-validator": "^4.2.5",
|
||||
"dayjs": "^1.11.13",
|
||||
"escape-html": "^1.0.3",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lodash-unified": "^1.0.2",
|
||||
"memoize-one": "^6.0.0",
|
||||
"normalize-wheel-es": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@ -3429,7 +3631,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
@ -5354,9 +5555,25 @@
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
|
||||
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/lodash-es": "*",
|
||||
"lodash": "*",
|
||||
"lodash-es": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
|
||||
@ -5620,6 +5837,12 @@
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
@ -6057,6 +6280,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-wheel-es": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
|
||||
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||
|
||||
@ -3,14 +3,15 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"element-plus": "^2.10.4",
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-service": "~5.0.8",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div id="app" :class="[theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-gray-50 text-gray-900', 'min-h-screen flex flex-col']">
|
||||
<!-- 导航栏 -->
|
||||
<header class="sticky top-0 z-50" :class="theme === 'dark' ? 'bg-gray-900/80 backdrop-blur-md border-b border-gray-800' : 'bg-white/80 backdrop-blur-md border-b border-gray-200'">
|
||||
<header v-if="['Landing', 'Login', 'SignUp'].includes($route.name)" class="sticky top-0 z-50" :class="theme === 'dark' ? 'bg-gray-900/80 backdrop-blur-md border-b border-gray-800' : 'bg-white/80 backdrop-blur-md border-b border-gray-200'">
|
||||
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-green-500 to-blue-600 flex items-center justify-center">
|
||||
@ -22,148 +22,13 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="hidden md:block px-4 py-2 rounded-lg" :class="theme === 'dark' ? 'border border-gray-700 hover:bg-gray-800' : 'border border-gray-300 hover:bg-gray-100'">登录</button>
|
||||
<button class="px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-blue-600 hover:opacity-90 transition-opacity">开始使用</button>
|
||||
<button @click="$router.push('/sign-up')" class="px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-blue-600 hover:opacity-90 transition-opacity">开始使用</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主页区域 -->
|
||||
<section class="relative py-20 overflow-hidden">
|
||||
<div class="absolute inset-0" :class="theme === 'dark' ? 'bg-[radial-gradient(circle_at_top_right,_rgba(79,70,229,0.15),_transparent_70%)]' : 'bg-[radial-gradient(circle_at_top_right,_rgba(79,70,229,0.05),_transparent_70%)]'"></div>
|
||||
<div class="container mx-auto px-4 relative z-10">
|
||||
<div class="max-w-3xl mx-auto text-center">
|
||||
<div class="inline-block px-3 py-1 rounded-full" :class="theme === 'dark' ? 'bg-gray-800 text-green-400' : 'bg-gray-200 text-green-600'">
|
||||
由AI驱动的智能投资平台
|
||||
</div>
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold mb-6 leading-tight">
|
||||
The Most Powerful AI Platform for <span class="text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500">Smarter Investment</span>
|
||||
</h1>
|
||||
<p class="text-xl mb-10 max-w-2xl mx-auto" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
利用人工智能技术分析市场趋势,提供精准选股建议,帮助投资者做出更明智的决策
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<button class="px-8 py-4 rounded-xl bg-gradient-to-r from-green-500 to-blue-600 hover:opacity-90 transition-opacity text-lg font-medium">
|
||||
免费试用 14 天
|
||||
</button>
|
||||
<button class="px-8 py-4 rounded-xl" :class="theme === 'dark' ? 'bg-gray-800 hover:bg-gray-700' : 'bg-gray-200 hover:bg-gray-300'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 inline mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
观看演示
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 核心功能模块 -->
|
||||
<section id="features" class="py-20" :class="theme === 'dark' ? 'bg-gray-900 border-t border-gray-800' : 'bg-white border-t border-gray-200'">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="text-center mb-16">
|
||||
<div class="inline-block px-3 py-1 rounded-full" :class="theme === 'dark' ? 'bg-gray-800 text-blue-400' : 'bg-gray-200 text-blue-600'">
|
||||
核心功能
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold mb-6">AI驱动的智能股票分析与投资决策平台</h2>
|
||||
<p class="text-xl max-w-3xl mx-auto" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
集成五大核心功能模块,提供全方位的股票数据分析与投资决策支持
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<!-- 每日股票推荐 -->
|
||||
<div :class="theme === 'dark' ? 'bg-gray-800/50 border border-gray-800' : 'bg-white border border-gray-200'" class="rounded-2xl p-6 hover:border-blue-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-blue-500/20 flex items-center justify-center mb-6 group-hover:bg-blue-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">每日股票推荐</h3>
|
||||
<p :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'" class="mb-4">
|
||||
基于机器学习算法,每日推送潜力股票推荐列表,结合市场趋势与历史数据分析,助您把握投资机会。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多因子模型评估</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 风险收益比分析</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 行业分布均衡推荐</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 技术分析 -->
|
||||
<div :class="theme === 'dark' ? 'bg-gray-800/50 border border-gray-800' : 'bg-white border border-gray-200'" class="rounded-2xl p-6 hover:border-green-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-green-500/20 flex items-center justify-center mb-6 group-hover:bg-green-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2v-6a2 2 0 00-2-2h-2a2 2 0 00-2 2v6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">技术分析</h3>
|
||||
<p :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'" class="mb-4">
|
||||
提供专业级股票技术分析图表,支持多种技术指标与周期切换,帮助您深入分析股票走势与市场情绪。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 实时K线图与指标</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多周期技术分析</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 技术形态识别</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 用户设置股票 -->
|
||||
<div :class="theme === 'dark' ? 'bg-gray-800/50 border border-gray-800' : 'bg-white border border-gray-200'" class="rounded-2xl p-6 hover:border-purple-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-purple-500/20 flex items-center justify-center mb-6 group-hover:bg-purple-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">用户设置股票</h3>
|
||||
<p :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'" class="mb-4">
|
||||
个性化股票关注列表管理,支持添加、删除和分组管理关注股票,实时追踪您关心的市场动态。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义关注列表</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 股票分组管理</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 快速访问与排序</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 添加指标 -->
|
||||
<div :class="theme === 'dark' ? 'bg-gray-800/50 border border-gray-800' : 'bg-white border border-gray-200'" class="rounded-2xl p-6 hover:border-yellow-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-yellow-500/20 flex items-center justify-center mb-6 group-hover:bg-yellow-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">添加指标</h3>
|
||||
<p :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'" class="mb-4">
|
||||
灵活的技术指标自定义功能,支持添加MACD、RSI、KDJ等多种技术指标,自定义参数与显示样式。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 30+技术指标支持</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义指标参数</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多指标组合分析</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 订阅股票的技术信号提醒 -->
|
||||
<div :class="theme === 'dark' ? 'bg-gray-800/50 border border-gray-800' : 'bg-white border border-gray-200'" class="rounded-2xl p-6 hover:border-red-500/50 transition-all group md:col-span-2 lg:col-span-1">
|
||||
<div class="w-14 h-14 rounded-xl bg-red-500/20 flex items-center justify-center mb-6 group-hover:bg-red-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">技术信号提醒</h3>
|
||||
<p :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'" class="mb-4">
|
||||
智能技术信号监测系统,支持自定义股票信号条件,实时推送买卖点提醒,不错过任何投资机会。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm" :class="theme === 'dark' ? 'text-gray-400' : 'text-gray-600'">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义信号条件</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 实时推送提醒</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 历史信号回溯</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 路由视图 -->
|
||||
<router-view />
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="mt-auto" :class="theme === 'dark' ? 'bg-gray-900 border-t border-gray-800' : 'bg-white border-t border-gray-200'">
|
||||
@ -173,31 +38,31 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
theme: localStorage.getItem('theme') || 'dark'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 应用保存的主题
|
||||
document.documentElement.classList.toggle('dark', this.theme === 'dark');
|
||||
},
|
||||
methods: {
|
||||
toggleTheme() {
|
||||
this.theme = this.theme === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('theme', this.theme);
|
||||
document.documentElement.classList.toggle('dark', this.theme === 'dark');
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
theme: localStorage.getItem('theme') || 'dark'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 移除默认样式,使用Tailwind工具类 */
|
||||
</style>
|
||||
},
|
||||
mounted() {
|
||||
// 应用保存的主题
|
||||
document.documentElement.classList.toggle('dark', this.theme === 'dark');
|
||||
},
|
||||
methods: {
|
||||
toggleTheme() {
|
||||
this.theme = this.theme === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('theme', this.theme);
|
||||
document.documentElement.classList.toggle('dark', this.theme === 'dark');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 移除默认样式,使用Tailwind工具类 */
|
||||
</style>
|
||||
52
services/frontend/src/components/MobileBottomNav.vue
Normal file
52
services/frontend/src/components/MobileBottomNav.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-800 z-40 px-4 py-2 md:hidden">
|
||||
<div class="flex justify-around items-center">
|
||||
<router-link to="/home" class="flex flex-col items-center py-1 px-3 rounded-md" active-class="text-green-500">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h12a1 1 0 001-1v-4a1 1 0 00-1-1h-2a1 1 0 00-1 1v4a1 1 0 01-1 1m-6 0a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1m-6 0h12a1 1 0 001-1v-2a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 01-1 1"/>
|
||||
</svg>
|
||||
<span class="text-xs mt-1">我的主页</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/home/ai-investment" class="flex flex-col items-center py-1 px-3 rounded-md" active-class="text-green-500">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
<span class="text-xs mt-1">AI投资</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/home/stock-market" class="flex flex-col items-center py-1 px-3 rounded-md" active-class="text-green-500">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"/>
|
||||
</svg>
|
||||
<span class="text-xs mt-1">股票市场</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/home/earnings-prediction" class="flex flex-col items-center py-1 px-3 rounded-md" active-class="text-green-500">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
|
||||
</svg>
|
||||
<span class="text-xs mt-1">收益预测</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/home/trading-news" class="flex flex-col items-center py-1 px-3 rounded-md" active-class="text-green-500">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10l4 4h-6a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span class="text-xs mt-1">交易新闻</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MobileBottomNav'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.router-link-active {
|
||||
color: #10b981;
|
||||
}
|
||||
</style>
|
||||
98
services/frontend/src/components/Sidebar.vue
Normal file
98
services/frontend/src/components/Sidebar.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="bg-gray-900 text-gray-100 h-screen w-64 fixed left-0 top-0 p-4 shadow-lg z-10">
|
||||
<div class="flex items-center mb-8">
|
||||
<div class="w-8 h-8 bg-gradient-to-r from-green-500 to-blue-600 rounded-md flex items-center justify-center mr-2">
|
||||
<span class="font-bold text-white">A</span>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold">AriStockAI</h1>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<ul class="space-y-1">
|
||||
<li>
|
||||
<router-link to="/home" class="flex items-center px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors" active-class="bg-gray-800 text-white">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h12a1 1 0 001-1v-4a1 1 0 00-1-1h-2a1 1 0 00-1 1v4a1 1 0 01-1 1m-6 0a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1m-6 0h12a1 1 0 001-1v-2a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 01-1 1"/>
|
||||
</svg>
|
||||
<span>首页</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/home/ai-investment" class="flex items-center px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors" active-class="bg-gray-800 text-white">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
<span>AI投资</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/home/stock-market" class="flex items-center px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors" active-class="bg-gray-800 text-white">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"/>
|
||||
</svg>
|
||||
<span>股票市场</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/home/earnings-prediction" class="flex items-center px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors" active-class="bg-gray-800 text-white">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
|
||||
</svg>
|
||||
<span>收益预测</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/home/trading-news" class="flex items-center px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors" active-class="bg-gray-800 text-white">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10l4 4h-6a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span>交易新闻</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/home/history" class="flex items-center px-4 py-3 rounded-lg hover:bg-gray-800 transition-colors" active-class="bg-gray-800 text-white">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>历史记录</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="absolute bottom-8 left-0 right-0 px-4">
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="flex items-center mb-2">
|
||||
<div class="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center mr-3">
|
||||
<span class="text-sm font-medium">F</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium">fanfpy@qq.com</p>
|
||||
<p class="text-xs text-gray-400">高级会员</p>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="handleLogout" class="w-full py-2 text-sm text-gray-300 hover:text-white bg-gray-700 rounded-lg transition-colors">
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ElMessage } from 'element-plus';
|
||||
export default {
|
||||
name: 'Sidebar',
|
||||
methods: {
|
||||
handleLogout() {
|
||||
localStorage.removeItem('isLoggedIn');
|
||||
this.$router.push('/');
|
||||
ElMessage.success('已成功退出登录');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 侧边栏样式已通过Tailwind实现 */
|
||||
</style>
|
||||
@ -1,5 +1,31 @@
|
||||
import './styles.css'
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
const app = createApp(App).use(router).use(ElementPlus)
|
||||
// 添加全局错误处理
|
||||
app.config.errorHandler = (err, vm, info) => {
|
||||
console.error('Vue全局错误捕获:', err, vm, info);
|
||||
const errorDetails = err.message || err.toString();
|
||||
alert(`Vue错误: ${errorDetails}\n\n详细信息请查看控制台`);
|
||||
}
|
||||
|
||||
// 捕获window全局错误
|
||||
window.addEventListener('error', (event) => {
|
||||
console.error('Window全局错误:', event.error);
|
||||
const errorDetails = event.error?.message || event.error?.toString() || '未知错误';
|
||||
alert(`Window错误: ${errorDetails}\n\n详细信息请查看控制台`);
|
||||
});
|
||||
|
||||
// 捕获未处理的Promise拒绝
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
console.error('未处理的Promise拒绝:', event.reason);
|
||||
const errorDetails = event.reason?.message || event.reason?.toString() || '未知Promise错误';
|
||||
alert(`Promise错误: ${errorDetails}\n\n详细信息请查看控制台`);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.mount('#app')
|
||||
84
services/frontend/src/router/index.js
Normal file
84
services/frontend/src/router/index.js
Normal file
@ -0,0 +1,84 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import Home from '../views/private/Home.vue';
|
||||
import Dashboard from '../views/private/Dashboard.vue';
|
||||
import AiInvestment from '../views/private/AiInvestment.vue';
|
||||
import StockMarket from '../views/private/StockMarket.vue';
|
||||
import EarningsPrediction from '../views/private/EarningsPrediction.vue';
|
||||
import TradingNews from '../views/private/TradingNews.vue';
|
||||
import History from '../views/private/History.vue';
|
||||
import Login from '../views/public/Login.vue';
|
||||
import SignUp from '../views/public/SignUp.vue';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Landing',
|
||||
component: () => import('@/views/public/Index.vue')
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
component: Home,
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'Dashboard',
|
||||
component: Dashboard
|
||||
},
|
||||
{
|
||||
path: 'ai-investment',
|
||||
name: 'AiInvestment',
|
||||
component: AiInvestment
|
||||
},
|
||||
{
|
||||
path: 'stock-market',
|
||||
name: 'StockMarket',
|
||||
component: StockMarket
|
||||
},
|
||||
{
|
||||
path: 'earnings-prediction',
|
||||
name: 'EarningsPrediction',
|
||||
component: EarningsPrediction
|
||||
},
|
||||
{
|
||||
path: 'trading-news',
|
||||
name: 'TradingNews',
|
||||
component: TradingNews
|
||||
},
|
||||
{
|
||||
path: 'history',
|
||||
name: 'History',
|
||||
component: History
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: '/sign-up',
|
||||
name: 'SignUp',
|
||||
component: SignUp
|
||||
}
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
// 路由守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
||||
// 需要登录的路由
|
||||
if (to.meta.requiresAuth && !isLoggedIn) {
|
||||
next('/login');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
|
||||
export default router;
|
||||
157
services/frontend/src/views/Index.vue
Normal file
157
services/frontend/src/views/Index.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-900 text-white">
|
||||
<!-- 英雄区域 -->
|
||||
<section class="relative py-20 overflow-hidden">
|
||||
<div class="absolute inset-0 bg-[radial-gradient(circle_at_top_right,_rgba(79,70,229,0.15),_transparent_70%)]"></div>
|
||||
<div class="container mx-auto px-4 relative z-10">
|
||||
<div class="max-w-3xl mx-auto text-center">
|
||||
<div class="inline-block px-3 py-1 rounded-full bg-gray-800 text-green-400 mb-6">
|
||||
由AI驱动的智能投资平台
|
||||
</div>
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold mb-6 leading-tight">
|
||||
The Most Powerful AI Platform for <span class="text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500">Smarter Investment</span>
|
||||
</h1>
|
||||
<p class="text-xl mb-10 max-w-2xl mx-auto text-gray-400">
|
||||
利用人工智能技术分析市场趋势,提供精准选股建议,帮助投资者做出更明智的决策
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<button class="px-8 py-4 rounded-xl bg-gradient-to-r from-green-500 to-blue-600 hover:opacity-90 transition-opacity text-lg font-medium" @click="handleStartUsing">
|
||||
开始使用
|
||||
</button>
|
||||
<button class="px-8 py-4 rounded-xl bg-gray-800 hover:bg-gray-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 inline mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
观看演示
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 核心功能模块 -->
|
||||
<section id="features" class="py-20 bg-gray-900 border-t border-gray-800">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="text-center mb-16">
|
||||
<div class="inline-block px-3 py-1 rounded-full bg-gray-800 text-blue-400 mb-4">
|
||||
核心功能
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold mb-6">AI驱动的智能股票分析与投资决策平台</h2>
|
||||
<p class="text-xl max-w-3xl mx-auto text-gray-400">
|
||||
集成五大核心功能模块,提供全方位的股票数据分析与投资决策支持
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<!-- 每日股票推荐 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-blue-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-blue-500/20 flex items-center justify-center mb-6 group-hover:bg-blue-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">每日股票推荐</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
基于机器学习算法,每日推送潜力股票推荐列表,结合市场趋势与历史数据分析,助您把握投资机会。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多因子模型评估</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 风险收益比分析</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 行业分布均衡推荐</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 技术分析 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-green-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-green-500/20 flex items-center justify-center mb-6 group-hover:bg-green-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2v-6a2 2 0 00-2-2h-2a2 2 0 00-2 2v6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">技术分析</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
提供专业级股票技术分析图表,支持多种技术指标与周期切换,帮助您深入分析股票走势与市场情绪。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 实时K线图与指标</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多周期技术分析</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 技术形态识别</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 用户设置股票 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-purple-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-purple-500/20 flex items-center justify-center mb-6 group-hover:bg-purple-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">用户设置股票</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
个性化股票关注列表管理,支持添加、删除和分组管理关注股票,实时追踪您关心的市场动态。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义关注列表</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 股票分组管理</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 快速访问与排序</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 添加指标 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-yellow-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-yellow-500/20 flex items-center justify-center mb-6 group-hover:bg-yellow-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">添加指标</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
灵活的技术指标自定义功能,支持添加MACD、RSI、KDJ等多种技术指标,自定义参数与显示样式。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 30+技术指标支持</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义指标参数</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多指标组合分析</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 订阅股票的技术信号提醒 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-red-500/50 transition-all group md:col-span-2 lg:col-span-1">
|
||||
<div class="w-14 h-14 rounded-xl bg-red-500/20 flex items-center justify-center mb-6 group-hover:bg-red-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">技术信号提醒</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
智能技术信号监测系统,支持自定义股票信号条件,实时推送买卖点提醒,不错过任何投资机会。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义信号条件</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 实时推送提醒</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 历史信号回溯</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LandingPage',
|
||||
methods: {
|
||||
handleStartUsing() {
|
||||
// 检查用户登录状态
|
||||
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
||||
if (isLoggedIn) {
|
||||
this.$router.push('/home');
|
||||
} else {
|
||||
this.$router.push('/sign-up');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
12
services/frontend/src/views/private/AiInvestment.vue
Normal file
12
services/frontend/src/views/private/AiInvestment.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-white mb-6">AI投资</h1>
|
||||
<!-- AI投资内容将在后续开发中添加 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiInvestment'
|
||||
}
|
||||
</script>
|
||||
88
services/frontend/src/views/private/Dashboard.vue
Normal file
88
services/frontend/src/views/private/Dashboard.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="p-4 md:p-6">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="relative mb-6">
|
||||
<input type="text" placeholder="Search symbols or ask a question" class="w-full bg-gray-800 rounded-full py-3 pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="absolute left-3 top-3 h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<button class="absolute right-3 top-2.5 bg-green-500 p-1.5 rounded-full">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 市场指标 -->
|
||||
<div class="grid grid-cols-3 gap-3 mb-6">
|
||||
<div class="bg-gray-800 rounded-lg p-3 text-center">
|
||||
<div class="text-xs text-gray-400">IWM</div>
|
||||
<div class="text-white font-medium">+1.01%</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-lg p-3 text-center">
|
||||
<div class="text-xs text-gray-400">DIA</div>
|
||||
<div class="text-white font-medium">+0.5%</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-lg p-3 text-center">
|
||||
<div class="text-xs text-gray-400">BTC</div>
|
||||
<div class="text-white font-medium">-0.2%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 交易策略 -->
|
||||
<h2 class="text-lg font-semibold text-white mb-4">Trading Strategies</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||
<div class="bg-gray-800 rounded-xl p-4 border border-gray-700">
|
||||
<div class="text-green-400 text-sm font-medium mb-2">AI Investment</div>
|
||||
<h3 class="text-white font-bold mb-3">AI Stock Picker</h3>
|
||||
<div class="flex justify-between items-end">
|
||||
<div class="text-xs text-gray-400">Top 3 stocks daily</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-xl p-4 border border-gray-700">
|
||||
<div class="text-green-400 text-sm font-medium mb-2">Stock & Crypto</div>
|
||||
<h3 class="text-white font-bold mb-3">Swing Trades</h3>
|
||||
<div class="flex justify-between items-end">
|
||||
<div class="text-xs text-gray-400">Trade signals daily</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI分析 -->
|
||||
<h2 class="text-lg font-semibold text-white mb-4">AI Analysis</h2>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<button class="bg-gray-800 hover:bg-gray-700 transition-colors rounded-lg p-3 border border-gray-700 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
|
||||
</svg>
|
||||
<span class="text-white text-sm">Quick Insights</span>
|
||||
</button>
|
||||
<button class="bg-gray-800 hover:bg-gray-700 transition-colors rounded-lg p-3 border border-gray-700 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
|
||||
</svg>
|
||||
<span class="text-white text-sm">Technical Expert</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
data() {
|
||||
return {
|
||||
// 可以在这里添加数据属性
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 可以在这里添加方法
|
||||
}
|
||||
}
|
||||
</script>
|
||||
12
services/frontend/src/views/private/EarningsPrediction.vue
Normal file
12
services/frontend/src/views/private/EarningsPrediction.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-white mb-6">收益预测</h1>
|
||||
<!-- 收益预测内容将在后续开发中添加 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'EarningsPrediction'
|
||||
}
|
||||
</script>
|
||||
12
services/frontend/src/views/private/History.vue
Normal file
12
services/frontend/src/views/private/History.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-white mb-6">历史记录</h1>
|
||||
<!-- 历史记录内容将在后续开发中添加 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'History'
|
||||
}
|
||||
</script>
|
||||
90
services/frontend/src/views/private/Home.vue
Normal file
90
services/frontend/src/views/private/Home.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="flex min-h-screen relative">
|
||||
<!-- 移动端侧边栏遮罩层 -->
|
||||
<div v-if="isMobile && isSidebarOpen" @click="isSidebarOpen = false" class="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden"></div>
|
||||
<div :class="['fixed inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out', isSidebarOpen ? 'translate-x-0' : '-translate-x-full', 'w-64', isMobile ? 'w-4/5' : 'w-64']">
|
||||
<Sidebar />
|
||||
</div>
|
||||
<!-- 移动端遮罩层已移除,侧边栏在移动端不再显示 -->
|
||||
<div :class="['flex-1 flex flex-col h-screen overflow-hidden transition-all duration-300', (!isMobile || isSidebarOpen) ? 'ml-64' : 'ml-0', isMobile && isSidebarOpen ? 'ml-4/5' : '']">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="bg-gray-800 border-b border-gray-700 py-3 px-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<button @click="isSidebarOpen = !isSidebarOpen" class="lg:hidden">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="relative w-1/3">
|
||||
<input type="text" placeholder="搜索资产、策略或指标..." class="w-full bg-gray-700 rounded-lg py-2 pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-green-500"
|
||||
:class="theme === 'dark' ? 'text-gray-200 placeholder-gray-400' : 'text-gray-800 placeholder-gray-500'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="absolute left-3 top-2.5 h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button class="p-2 rounded-lg hover:bg-gray-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="h-8 w-8 rounded-full bg-gradient-to-r from-green-500 to-blue-600 flex items-center justify-center text-white font-medium">JD</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<main class="flex-1 overflow-y-auto p-6 bg-gray-900 pb-20"> <!-- 添加底部内边距避免被导航栏遮挡 -->
|
||||
<router-view />
|
||||
</main>
|
||||
<MobileBottomNav v-if="isMobile" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sidebar from '@/components/Sidebar.vue'
|
||||
import MobileBottomNav from '@/components/MobileBottomNav.vue'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
Sidebar,
|
||||
MobileBottomNav
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
theme: localStorage.getItem('theme') || 'dark',
|
||||
isMobile: false,
|
||||
isSidebarOpen: true // 默认显示侧边栏
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkScreenSize() {
|
||||
this.isMobile = window.innerWidth < 768;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.checkScreenSize();
|
||||
// 移动端默认关闭侧边栏
|
||||
if (this.isMobile) {
|
||||
this.isSidebarOpen = false;
|
||||
}
|
||||
window.addEventListener('resize', this.checkScreenSize);
|
||||
document.documentElement.classList.toggle('dark', this.theme === 'dark');
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.checkScreenSize);
|
||||
},
|
||||
computed: {
|
||||
backgroundClass() {
|
||||
return this.theme === 'dark'
|
||||
? 'bg-[radial-gradient(circle_at_top_right,_rgba(79,70,229,0.15),_transparent_70%)]'
|
||||
: 'bg-[radial-gradient(circle_at_top_right,_rgba(79,70,229,0.05),_transparent_70%)]';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
12
services/frontend/src/views/private/StockMarket.vue
Normal file
12
services/frontend/src/views/private/StockMarket.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-white mb-6">股票市场</h1>
|
||||
<!-- 股票市场内容将在后续开发中添加 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'StockMarket'
|
||||
}
|
||||
</script>
|
||||
12
services/frontend/src/views/private/TradingNews.vue
Normal file
12
services/frontend/src/views/private/TradingNews.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-white mb-6">交易新闻</h1>
|
||||
<!-- 交易新闻内容将在后续开发中添加 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TradingNews'
|
||||
}
|
||||
</script>
|
||||
157
services/frontend/src/views/public/Index.vue
Normal file
157
services/frontend/src/views/public/Index.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-900 text-white">
|
||||
<!-- 英雄区域 -->
|
||||
<section class="relative py-20 overflow-hidden">
|
||||
<div class="absolute inset-0 bg-[radial-gradient(circle_at_top_right,_rgba(79,70,229,0.15),_transparent_70%)]"></div>
|
||||
<div class="container mx-auto px-4 relative z-10">
|
||||
<div class="max-w-3xl mx-auto text-center">
|
||||
<div class="inline-block px-3 py-1 rounded-full bg-gray-800 text-green-400 mb-6">
|
||||
由AI驱动的智能投资平台
|
||||
</div>
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold mb-6 leading-tight">
|
||||
The Most Powerful AI Platform for <span class="text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500">Smarter Investment</span>
|
||||
</h1>
|
||||
<p class="text-xl mb-10 max-w-2xl mx-auto text-gray-400">
|
||||
利用人工智能技术分析市场趋势,提供精准选股建议,帮助投资者做出更明智的决策
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<button class="px-8 py-4 rounded-xl bg-gradient-to-r from-green-500 to-blue-600 hover:opacity-90 transition-opacity text-lg font-medium" @click="handleStartUsing">
|
||||
开始使用
|
||||
</button>
|
||||
<button class="px-8 py-4 rounded-xl bg-gray-800 hover:bg-gray-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 inline mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
观看演示
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 核心功能模块 -->
|
||||
<section id="features" class="py-20 bg-gray-900 border-t border-gray-800">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="text-center mb-16">
|
||||
<div class="inline-block px-3 py-1 rounded-full bg-gray-800 text-blue-400 mb-4">
|
||||
核心功能
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold mb-6">AI驱动的智能股票分析与投资决策平台</h2>
|
||||
<p class="text-xl max-w-3xl mx-auto text-gray-400">
|
||||
集成五大核心功能模块,提供全方位的股票数据分析与投资决策支持
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<!-- 每日股票推荐 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-blue-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-blue-500/20 flex items-center justify-center mb-6 group-hover:bg-blue-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">每日股票推荐</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
基于机器学习算法,每日推送潜力股票推荐列表,结合市场趋势与历史数据分析,助您把握投资机会。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多因子模型评估</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 风险收益比分析</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 行业分布均衡推荐</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 技术分析 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-green-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-green-500/20 flex items-center justify-center mb-6 group-hover:bg-green-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2v-6a2 2 0 00-2-2h-2a2 2 0 00-2 2v6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">技术分析</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
提供专业级股票技术分析图表,支持多种技术指标与周期切换,帮助您深入分析股票走势与市场情绪。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 实时K线图与指标</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多周期技术分析</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 技术形态识别</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 用户设置股票 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-purple-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-purple-500/20 flex items-center justify-center mb-6 group-hover:bg-purple-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">用户设置股票</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
个性化股票关注列表管理,支持添加、删除和分组管理关注股票,实时追踪您关心的市场动态。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义关注列表</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 股票分组管理</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 快速访问与排序</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 添加指标 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-yellow-500/50 transition-all group">
|
||||
<div class="w-14 h-14 rounded-xl bg-yellow-500/20 flex items-center justify-center mb-6 group-hover:bg-yellow-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">添加指标</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
灵活的技术指标自定义功能,支持添加MACD、RSI、KDJ等多种技术指标,自定义参数与显示样式。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 30+技术指标支持</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义指标参数</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 多指标组合分析</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 订阅股票的技术信号提醒 -->
|
||||
<div class="bg-gray-800/50 border border-gray-800 rounded-2xl p-6 hover:border-red-500/50 transition-all group md:col-span-2 lg:col-span-1">
|
||||
<div class="w-14 h-14 rounded-xl bg-red-500/20 flex items-center justify-center mb-6 group-hover:bg-red-500/30 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold mb-3">技术信号提醒</h3>
|
||||
<p class="text-gray-400 mb-4">
|
||||
智能技术信号监测系统,支持自定义股票信号条件,实时推送买卖点提醒,不错过任何投资机会。
|
||||
</p>
|
||||
<ul class="space-y-2 text-sm text-gray-400">
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 自定义信号条件</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 实时推送提醒</li>
|
||||
<li class="flex items-center gap-2"><span class="text-green-400">✓</span> 历史信号回溯</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LandingPage',
|
||||
methods: {
|
||||
handleStartUsing() {
|
||||
// 检查用户登录状态
|
||||
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
||||
if (isLoggedIn) {
|
||||
this.$router.push('/home');
|
||||
} else {
|
||||
this.$router.push('/sign-up');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
200
services/frontend/src/views/public/Login.vue
Normal file
200
services/frontend/src/views/public/Login.vue
Normal file
@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-900 text-white p-4">
|
||||
<div class="w-full max-w-md">
|
||||
<div class="text-center mb-8">
|
||||
<div class="flex items-center justify-center space-x-2 mb-2">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-green-500 to-blue-600 flex items-center justify-center">
|
||||
<span class="font-bold text-xl">A</span>
|
||||
</div>
|
||||
<span class="font-bold text-xl">AriStockAI</span>
|
||||
</div>
|
||||
<p class="text-gray-400">
|
||||
没有账号?
|
||||
<a href="#" class="text-green-400 hover:underline" @click.prevent="$router.push('/sign-up')">注册</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 rounded-2xl p-8 shadow-lg">
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<button @click="handleGoogleLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 22q-2.05 0-3.875-.788t-3.188-2.15-2.15-3.187T2 12q0-2.05.788-3.875t2.15-3.188 3.187-2.15T12 2q2.05 0 3.875.788t3.188 2.15 2.15 3.187T22 12q0 2.05-.788 3.875t-2.15 3.188-3.187 2.15T12 22zm0-2q3.333 0 5.667-2.333T20 12q0-3.333-2.333-5.667T12 4Q8.667 4 6.333 6.333T4 12q0 3.333 2.333 5.667T12 20zm0-8q1.657 0 2.828-.828T16 8q0-1.657-.828-2.828T12 4Q10.343 4 9.172 5.172T8 8q0 1.657.828 2.828T12 12zm-6 4h12q-.667-1.333-2.333-2T12 12t-3.667.667T6 14z"/>
|
||||
</svg>
|
||||
Google
|
||||
</button>
|
||||
<button @click="handleTwitterLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 4.11 7.38 1.67 4.79c-.4.69-.64 1.52-.64 2.46 0 1.71.87 3.21 2.18 4.09-.81-.02-1.56-.25-2.22-.64v.06c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.88.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C1.88 20.29 4.49 21 7.34 21c8.45 0 13.17-6.98 13.17-13.17 0-.2-.01-.4-.02-.6.9-.63 1.65-1.43 2.25-2.34z"/>
|
||||
</svg>
|
||||
Twitter
|
||||
</button>
|
||||
<button @click="handleFacebookLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"/>
|
||||
</svg>
|
||||
Facebook
|
||||
</button>
|
||||
<button @click="handleMicrosoftLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2.25c-5.38 0-9.75 4.37-9.75 9.75s4.37 9.75 9.75 9.75 9.75-4.37 9.75-9.75S17.38 2.25 12 2.25zm3.75 11.62c0 2.9-2.35 5.25-5.25 5.25s-5.25-2.35-5.25-5.25 2.35-5.25 5.25-5.25h1.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5H12c-3.86 0-7 3.14-7 7s3.14 7 7 7 7-3.14 7-7v-1.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"/>
|
||||
</svg>
|
||||
Microsoft
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between my-6">
|
||||
<div class="h-px bg-gray-700 flex-grow"></div>
|
||||
<span class="px-4 text-gray-400">OR</span>
|
||||
<div class="h-px bg-gray-700 flex-grow"></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1">邮箱</label>
|
||||
<div class="relative">
|
||||
<input type="email" v-model="email" placeholder="输入您的邮箱" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-500"/>
|
||||
<svg class="absolute right-3 top-3.5 w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p v-if="emailError" class="text-red-400 text-xs mt-1">{{ emailError }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1">密码</label>
|
||||
<div class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" v-model="password" placeholder="输入您的密码" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-500"/>
|
||||
<button type="button" class="absolute right-3 top-3.5 text-gray-500" @click="showPassword = !showPassword">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="passwordError" class="text-red-400 text-xs mt-1">{{ passwordError }}</p>
|
||||
</div>
|
||||
<div class="text-right mb-6">
|
||||
<a href="#" class="text-sm text-gray-400 hover:underline">Forgot Password</a>
|
||||
</div>
|
||||
|
||||
<button class="w-full py-3 px-4 bg-gradient-to-r from-green-500 to-blue-600 rounded-lg font-medium hover:opacity-90 transition-opacity" @click="handleLogin()">
|
||||
登录
|
||||
</button>
|
||||
|
||||
<p class="text-xs text-gray-500 text-center mt-6">
|
||||
By using AriStockAI, you agree to the <a href="#" class="text-gray-400 hover:underline">使用条款</a> and <a href="#" class="text-gray-400 hover:underline">隐私政策</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="showSuccessModal"
|
||||
title="登录成功"
|
||||
:closable="false"
|
||||
:show-close="false"
|
||||
:modal-append-to-body="false"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<div class="text-center py-4">
|
||||
<div class="w-16 h-16 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-gray-400 mb-6">您已成功登录,即将跳转到主页</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleModalClose"
|
||||
class="px-6 py-2 bg-gradient-to-r from-green-500 to-blue-600 rounded-lg"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
|
||||
name: 'Login',
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
password: '',
|
||||
showPassword: false,
|
||||
emailError: '',
|
||||
passwordError: '',
|
||||
showSuccessModal: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateForm() {
|
||||
let isValid = true;
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
// 邮箱验证
|
||||
if (!this.email) {
|
||||
this.emailError = '请输入邮箱';
|
||||
isValid = false;
|
||||
} else if (!emailRegex.test(this.email)) {
|
||||
this.emailError = '请输入有效的邮箱地址';
|
||||
isValid = false;
|
||||
} else {
|
||||
this.emailError = '';
|
||||
}
|
||||
|
||||
// 密码验证
|
||||
if (!this.password) {
|
||||
this.passwordError = '请输入密码';
|
||||
isValid = false;
|
||||
} else {
|
||||
this.passwordError = '';
|
||||
}
|
||||
|
||||
return isValid;
|
||||
},
|
||||
|
||||
handleLogin() {
|
||||
if (!this.validateForm()) return;
|
||||
|
||||
// 模拟登录逻辑
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleGoogleLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleTwitterLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleFacebookLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleMicrosoftLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
handleModalClose() {
|
||||
this.showSuccessModal = false;
|
||||
this.$router.push('/home');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
285
services/frontend/src/views/public/SignUp.vue
Normal file
285
services/frontend/src/views/public/SignUp.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-900 text-white p-4">
|
||||
<div class="w-full max-w-md">
|
||||
<div class="text-center mb-8">
|
||||
<div class="flex items-center justify-center space-x-2 mb-2">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-green-500 to-blue-600 flex items-center justify-center">
|
||||
<span class="font-bold text-xl">A</span>
|
||||
</div>
|
||||
<span class="font-bold text-xl">AriStockAI</span>
|
||||
</div>
|
||||
<p class="text-gray-400">
|
||||
已有账号?
|
||||
<a href="#" class="text-green-400 hover:underline" @click.prevent="$router.push('/login')">登录</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 rounded-2xl p-8 shadow-lg">
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<button @click="handleGoogleLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 22q-2.05 0-3.875-.788t-3.188-2.15-2.15-3.187T2 12q0-2.05.788-3.875t2.15-3.188 3.187-2.15T12 2q2.05 0 3.875.788t3.188 2.15 2.15 3.187T22 12q0 2.05-.788 3.875t-2.15 3.188-3.187 2.15T12 22zm0-2q3.333 0 5.667-2.333T20 12q0-3.333-2.333-5.667T12 4Q8.667 4 6.333 6.333T4 12q0 3.333 2.333 5.667T12 20zm0-8q1.657 0 2.828-.828T16 8q0-1.657-.828-2.828T12 4Q10.343 4 9.172 5.172T8 8q0 1.657.828 2.828T12 12zm-6 4h12q-.667-1.333-2.333-2T12 12t-3.667.667T6 14z"/>
|
||||
</svg>
|
||||
Google
|
||||
</button>
|
||||
<button @click="handleTwitterLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 4.11 7.38 1.67 4.79c-.4.69-.64 1.52-.64 2.46 0 1.71.87 3.21 2.18 4.09-.81-.02-1.56-.25-2.22-.64v.06c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.88.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C1.88 20.29 4.49 21 7.34 21c8.45 0 13.17-6.98 13.17-13.17 0-.2-.01-.4-.02-.6.9-.63 1.65-1.43 2.25-2.34z"/>
|
||||
</svg>
|
||||
Twitter
|
||||
</button>
|
||||
<button @click="handleFacebookLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"/>
|
||||
</svg>
|
||||
Facebook
|
||||
</button>
|
||||
<button @click="handleMicrosoftLogin" class="py-3 px-4 bg-white text-gray-900 rounded-lg font-medium flex items-center justify-center hover:bg-gray-100 transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2.25c-5.38 0-9.75 4.37-9.75 9.75s4.37 9.75 9.75 9.75 9.75-4.37 9.75-9.75S17.38 2.25 12 2.25zm3.75 11.62c0 2.9-2.35 5.25-5.25 5.25s-5.25-2.35-5.25-5.25 2.35-5.25 5.25-5.25h1.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5H12c-3.86 0-7 3.14-7 7s3.14 7 7 7 7-3.14 7-7v-1.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"/>
|
||||
</svg>
|
||||
Microsoft
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between my-6">
|
||||
<div class="h-px bg-gray-700 flex-grow"></div>
|
||||
<span class="px-4 text-gray-400">OR</span>
|
||||
<div class="h-px bg-gray-700 flex-grow"></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1">邮箱</label>
|
||||
<div class="relative">
|
||||
<input type="email" v-model="email" placeholder="输入您的邮箱" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-500"/>
|
||||
<svg class="absolute right-3 top-3.5 w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p v-if="emailError" class="text-red-400 text-xs mt-1">{{ emailError }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1">验证码</label>
|
||||
<div class="relative">
|
||||
<input type="text" v-model="verificationCode" placeholder="输入验证码" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-500"/>
|
||||
</div>
|
||||
<p v-if="verificationCodeError" class="text-red-400 text-xs mt-1">{{ verificationCodeError }}</p>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1"> </label>
|
||||
<button :disabled="countdown > 0" @click="handleSendCode" class="w-full bg-gray-700 hover:bg-gray-600 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white transition-colors">
|
||||
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1">密码</label>
|
||||
<div class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" v-model="password" placeholder="设置密码" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-500"/>
|
||||
<button type="button" class="absolute right-3 top-3.5 text-gray-500" @click="showPassword = !showPassword">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="passwordError" class="text-red-400 text-xs mt-1">{{ passwordError }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-1">确认密码</label>
|
||||
<div class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" v-model="confirmPassword" placeholder="再次输入密码" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-green-500 text-white placeholder-gray-500"/>
|
||||
</div>
|
||||
<p v-if="confirmPasswordError" class="text-red-400 text-xs mt-1">{{ confirmPasswordError }}</p>
|
||||
</div>
|
||||
|
||||
<button class="w-full py-3 px-4 bg-gradient-to-r from-green-500 to-blue-600 rounded-lg font-medium hover:opacity-90 transition-opacity" @click="handleRegister()">
|
||||
注册
|
||||
</button>
|
||||
|
||||
<p class="text-xs text-gray-500 text-center mt-6">
|
||||
By using AriStockAI, you agree to the <a href="#" class="text-gray-400 hover:underline">使用条款</a> and <a href="#" class="text-gray-400 hover:underline">隐私政策</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="showSuccessModal"
|
||||
:title="modalTitle"
|
||||
:closable="false"
|
||||
:show-close="false"
|
||||
:modal-append-to-body="false"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<div class="text-center py-4">
|
||||
<div class="w-16 h-16 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-gray-400 mb-6">{{ modalMessage }}</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleModalClose"
|
||||
class="px-6 py-2 bg-gradient-to-r from-green-500 to-blue-600 rounded-lg"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SignUp',
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
verificationCode: '',
|
||||
showPassword: false,
|
||||
countdown: 0,
|
||||
emailError: '',
|
||||
passwordError: '',
|
||||
confirmPasswordError: '',
|
||||
verificationCodeError: '',
|
||||
showSuccessModal: false,
|
||||
modalTitle: '',
|
||||
modalMessage: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateForm() {
|
||||
let isValid = true;
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
|
||||
|
||||
// 邮箱验证
|
||||
if (!this.email) {
|
||||
this.emailError = '请输入邮箱';
|
||||
isValid = false;
|
||||
} else if (!emailRegex.test(this.email)) {
|
||||
this.emailError = '请输入有效的邮箱地址';
|
||||
isValid = false;
|
||||
} else {
|
||||
this.emailError = '';
|
||||
}
|
||||
|
||||
// 验证码验证
|
||||
if (!this.verificationCode) {
|
||||
this.verificationCodeError = '请输入验证码';
|
||||
isValid = false;
|
||||
} else {
|
||||
this.verificationCodeError = '';
|
||||
}
|
||||
|
||||
// 密码验证
|
||||
if (!this.password) {
|
||||
this.passwordError = '请设置密码';
|
||||
isValid = false;
|
||||
} else if (!passwordRegex.test(this.password)) {
|
||||
this.passwordError = '密码至少8位,包含字母和数字';
|
||||
isValid = false;
|
||||
} else {
|
||||
this.passwordError = '';
|
||||
}
|
||||
|
||||
// 确认密码验证
|
||||
if (!this.confirmPassword) {
|
||||
this.confirmPasswordError = '请确认密码';
|
||||
isValid = false;
|
||||
} else if (this.password !== this.confirmPassword) {
|
||||
this.confirmPasswordError = '两次密码输入不一致';
|
||||
isValid = false;
|
||||
} else {
|
||||
this.confirmPasswordError = '';
|
||||
}
|
||||
|
||||
return isValid;
|
||||
},
|
||||
|
||||
handleRegister() {
|
||||
if (!this.validateForm()) return;
|
||||
|
||||
// 模拟注册逻辑
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.modalTitle = '注册成功';
|
||||
this.modalMessage = '您已成功注册,即将跳转到主页';
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleSendCode() {
|
||||
// 简单邮箱验证
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!this.email || !emailRegex.test(this.email)) {
|
||||
this.emailError = '请输入有效的邮箱地址';
|
||||
return;
|
||||
}
|
||||
this.emailError = '';
|
||||
|
||||
// 模拟发送验证码
|
||||
this.countdown = 60;
|
||||
const timer = setInterval(() => {
|
||||
this.countdown--;
|
||||
if (this.countdown <= 0) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.modalTitle = '发送成功';
|
||||
this.modalMessage = '验证码已发送至您的邮箱';
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleGoogleLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.modalTitle = '注册成功';
|
||||
this.modalMessage = '通过Google账号注册成功,即将跳转到主页';
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleTwitterLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.modalTitle = '注册成功';
|
||||
this.modalMessage = '通过Twitter账号注册成功,即将跳转到主页';
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleFacebookLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.modalTitle = '注册成功';
|
||||
this.modalMessage = '通过Facebook账号注册成功,即将跳转到主页';
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
|
||||
handleMicrosoftLogin() {
|
||||
// 模拟第三方登录
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
this.modalTitle = '注册成功';
|
||||
this.modalMessage = '通过Microsoft账号注册成功,即将跳转到主页';
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
handleModalClose() {
|
||||
this.showSuccessModal = false;
|
||||
// 如果是注册成功,跳转到主页
|
||||
if (this.modalTitle === '注册成功') {
|
||||
this.$router.push('/home');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user