This commit is contained in:
qiuyuan 2025-12-08 14:22:32 +08:00
commit 11040b100b
19 changed files with 1082 additions and 66 deletions

View File

@ -2,5 +2,5 @@
NODE_ENV=development NODE_ENV=development
# api # api
VITE_API_BASIC="http://10.10.1.29:8888" VITE_API_BASIC="http://10.10.1.40:8888"

25
components.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs']
Header: typeof import('./src/components/Header.vue')['default']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
Layout: typeof import('./src/components/layout.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./src/components/sidebar.vue')['default']
SmsCodeInput: typeof import('./src/components/SmsCodeInput.vue')['default']
}
}

482
package-lock.json generated
View File

@ -19,6 +19,8 @@
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"sass": "^1.94.2", "sass": "^1.94.2",
"typescript": "^4.6.4", "typescript": "^4.6.4",
"unplugin-auto-import": "^20.3.0",
"unplugin-vue-components": "^30.0.0",
"vite": "^3.2.3", "vite": "^3.2.3",
"vue-tsc": "^1.0.9" "vue-tsc": "^1.0.9"
} }
@ -148,11 +150,54 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/remapping": {
"version": "2.3.5",
"resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
}, },
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@parcel/watcher": { "node_modules/@parcel/watcher": {
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz",
@ -472,6 +517,13 @@
"nanopop": "^2.1.0" "nanopop": "^2.1.0"
} }
}, },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.10.1", "version": "24.10.1",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.10.1.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.10.1.tgz",
@ -644,6 +696,19 @@
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.24.tgz", "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.24.tgz",
"integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==" "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A=="
}, },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ant-design-vue": { "node_modules/ant-design-vue": {
"version": "4.2.6", "version": "4.2.6",
"resolved": "https://registry.npmmirror.com/ant-design-vue/-/ant-design-vue-4.2.6.tgz", "resolved": "https://registry.npmmirror.com/ant-design-vue/-/ant-design-vue-4.2.6.tgz",
@ -791,6 +856,13 @@
"integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==",
"dev": true "dev": true
}, },
"node_modules/confbox": {
"version": "0.2.2",
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"dev": true,
"license": "MIT"
},
"node_modules/core-js": { "node_modules/core-js": {
"version": "3.47.0", "version": "3.47.0",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.47.0.tgz", "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.47.0.tgz",
@ -818,6 +890,24 @@
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
"dev": true "dev": true
}, },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -1278,11 +1368,31 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/estree-walker": { "node_modules/estree-walker": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
}, },
"node_modules/exsolve": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz",
"integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
"dev": true,
"license": "MIT"
},
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
@ -1522,6 +1632,24 @@
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
}, },
"node_modules/local-pkg": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
"integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
"dev": true,
"license": "MIT",
"dependencies": {
"mlly": "^1.7.4",
"pkg-types": "^2.3.0",
"quansync": "^0.2.11"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@ -1611,6 +1739,45 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/mlly": {
"version": "1.8.0",
"resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz",
"integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.15.0",
"pathe": "^2.0.3",
"pkg-types": "^1.3.1",
"ufo": "^1.6.1"
}
},
"node_modules/mlly/node_modules/confbox": {
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"dev": true,
"license": "MIT"
},
"node_modules/mlly/node_modules/pkg-types": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"confbox": "^0.1.8",
"mlly": "^1.7.4",
"pathe": "^2.0.1"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/muggle-string": { "node_modules/muggle-string": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.3.1.tgz", "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.3.1.tgz",
@ -1659,6 +1826,13 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true "dev": true
}, },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
@ -1678,6 +1852,18 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pkg-types": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
"integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
"dev": true,
"license": "MIT",
"dependencies": {
"confbox": "^0.2.2",
"exsolve": "^1.0.7",
"pathe": "^2.0.3"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.6", "version": "8.5.6",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
@ -1711,6 +1897,23 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/quansync": {
"version": "0.2.11",
"resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz",
"integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/antfu"
},
{
"type": "individual",
"url": "https://github.com/sponsors/sxzz"
}
],
"license": "MIT"
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
@ -1794,6 +1997,13 @@
"compute-scroll-into-view": "^1.0.20" "compute-scroll-into-view": "^1.0.20"
} }
}, },
"node_modules/scule": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
"dev": true,
"license": "MIT"
},
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.3", "version": "7.7.3",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
@ -1819,6 +2029,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/strip-literal": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.1.0.tgz",
"integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^9.0.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/strip-literal/node_modules/js-tokens": {
"version": "9.0.1",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"dev": true,
"license": "MIT"
},
"node_modules/stylis": { "node_modules/stylis": {
"version": "4.3.6", "version": "4.3.6",
"resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz", "resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz",
@ -1844,6 +2074,54 @@
"node": ">=12.22" "node": ">=12.22"
} }
}, },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -1871,6 +2149,13 @@
"node": ">=4.2.0" "node": ">=4.2.0"
} }
}, },
"node_modules/ufo": {
"version": "1.6.1",
"resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz",
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
"dev": true,
"license": "MIT"
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.16.0", "version": "7.16.0",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
@ -1878,6 +2163,196 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unimport": {
"version": "5.5.0",
"resolved": "https://registry.npmmirror.com/unimport/-/unimport-5.5.0.tgz",
"integrity": "sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.15.0",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"local-pkg": "^1.1.2",
"magic-string": "^0.30.19",
"mlly": "^1.8.0",
"pathe": "^2.0.3",
"picomatch": "^4.0.3",
"pkg-types": "^2.3.0",
"scule": "^1.3.0",
"strip-literal": "^3.1.0",
"tinyglobby": "^0.2.15",
"unplugin": "^2.3.10",
"unplugin-utils": "^0.3.0"
},
"engines": {
"node": ">=18.12.0"
}
},
"node_modules/unimport/node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
},
"node_modules/unimport/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/unplugin": {
"version": "2.3.11",
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz",
"integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.5",
"acorn": "^8.15.0",
"picomatch": "^4.0.3",
"webpack-virtual-modules": "^0.6.2"
},
"engines": {
"node": ">=18.12.0"
}
},
"node_modules/unplugin-auto-import": {
"version": "20.3.0",
"resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-20.3.0.tgz",
"integrity": "sha512-RcSEQiVv7g0mLMMXibYVKk8mpteKxvyffGuDKqZZiFr7Oq3PB1HwgHdK5O7H4AzbhzHoVKG0NnMnsk/1HIVYzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"local-pkg": "^1.1.2",
"magic-string": "^0.30.21",
"picomatch": "^4.0.3",
"unimport": "^5.5.0",
"unplugin": "^2.3.11",
"unplugin-utils": "^0.3.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@nuxt/kit": "^4.0.0",
"@vueuse/core": "*"
},
"peerDependenciesMeta": {
"@nuxt/kit": {
"optional": true
},
"@vueuse/core": {
"optional": true
}
}
},
"node_modules/unplugin-auto-import/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/unplugin-utils": {
"version": "0.3.1",
"resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
"integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
"dev": true,
"license": "MIT",
"dependencies": {
"pathe": "^2.0.3",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/sponsors/sxzz"
}
},
"node_modules/unplugin-utils/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/unplugin-vue-components": {
"version": "30.0.0",
"resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz",
"integrity": "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.3",
"debug": "^4.4.3",
"local-pkg": "^1.1.2",
"magic-string": "^0.30.19",
"mlly": "^1.8.0",
"tinyglobby": "^0.2.15",
"unplugin": "^2.3.10",
"unplugin-utils": "^0.3.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@babel/parser": "^7.15.8",
"@nuxt/kit": "^3.2.2 || ^4.0.0",
"vue": "2 || 3"
},
"peerDependenciesMeta": {
"@babel/parser": {
"optional": true
},
"@nuxt/kit": {
"optional": true
}
}
},
"node_modules/unplugin/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "3.2.11", "version": "3.2.11",
"resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.11.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.11.tgz",
@ -2009,6 +2484,13 @@
"dependencies": { "dependencies": {
"loose-envify": "^1.0.0" "loose-envify": "^1.0.0"
} }
},
"node_modules/webpack-virtual-modules": {
"version": "0.6.2",
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
"dev": true,
"license": "MIT"
} }
} }
} }

View File

@ -20,6 +20,8 @@
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"sass": "^1.94.2", "sass": "^1.94.2",
"typescript": "^4.6.4", "typescript": "^4.6.4",
"unplugin-auto-import": "^20.3.0",
"unplugin-vue-components": "^30.0.0",
"vite": "^3.2.3", "vite": "^3.2.3",
"vue-tsc": "^1.0.9" "vue-tsc": "^1.0.9"
} }

View File

@ -16,3 +16,7 @@ export const updatePassword = (newPassword:any) => request.put('/v1/auth/update_
export const fetchUserInfo = () => request.get('/v1/auth/info') export const fetchUserInfo = () => request.get('/v1/auth/info')
// 修改手机号 // 修改手机号
export const updatePhoneNumber = (params:any) => request.put('/v1/auth/update_phone', params) export const updatePhoneNumber = (params:any) => request.put('/v1/auth/update_phone', params)
//注册
export const register = (params:any) => request.post('/v1/auth/register', params)
//忘记密码
export const forgetPassword = (params:any) => request.post('/v1/auth/forgot_password', params)

View File

@ -0,0 +1,138 @@
<!-- src/components/SmsCodeInput.vue -->
<template>
<a-input v-model:value="innerValue" placeholder="请输入手机号" :disabled="disabled" @change="handleChange">
<template #addonAfter>
<div class="sms-code-btn" :class="{ disabled: !canSend }" @click="handleGetCode">
{{ countDown > 0 ? `${countDown}秒后重试` : '获取验证码' }}
</div>
</template>
</a-input>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
// Props & Emits
const props = defineProps<{
value?: string
disabled?: boolean
}>()
const emit = defineEmits<{
(e: 'update:value', val: string): void
(e: 'send'): void //
}>()
//
const innerValue = ref(props.value || '')
const countDown = ref(0)
const timer = ref<number | null>(null)
//
const getStorageKey = (phone: string) => `sms_code_expire_${phone}`
//
const canSend = computed(() => {
return (
!props.disabled &&
countDown.value === 0 &&
/^\d{11}$/.test(innerValue.value)
)
})
// value
watch(
() => props.value,
(newVal) => {
if (newVal !== innerValue.value) {
innerValue.value = newVal || ''
}
}
)
const handleChange = () => {
emit('update:value', innerValue.value)
}
//
const initCountDown = () => {
const phone = innerValue.value
if (!/^\d{11}$/.test(phone)) return
const key = getStorageKey(phone)
const expireTimeStr = localStorage.getItem(key)
if (expireTimeStr) {
const expireTime = Number(expireTimeStr)
const remaining = Math.floor((expireTime - Date.now()) / 1000)
if (remaining > 0) {
countDown.value = remaining
startTimer()
} else {
localStorage.removeItem(key)
}
}
}
const startTimer = () => {
if (timer.value) clearInterval(timer.value)
timer.value = setInterval(() => {
if (countDown.value > 0) {
countDown.value--
} else {
stopTimer()
}
}, 1000) as unknown as number
}
const stopTimer = () => {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
const key = getStorageKey(innerValue.value)
localStorage.removeItem(key)
}
const handleGetCode = () => {
if (!canSend.value) return
// localStorage
const expireTime = Date.now() + 60 * 1000
const key = getStorageKey(innerValue.value)
localStorage.setItem(key, String(expireTime))
countDown.value = 60
startTimer()
//
emit('send')
}
//
watch(innerValue, () => {
//
if (timer.value) {
stopTimer()
}
//
initCountDown()
})
//
onMounted(() => {
initCountDown()
})
</script>
<style scoped>
.sms-code-btn {
cursor: pointer;
color: #1890ff;
user-select: none;
}
.sms-code-btn.disabled {
cursor: not-allowed;
color: #ccc;
}
</style>

View File

@ -75,6 +75,11 @@ const routes: RouteRecordRaw[] = [
name: "SSH", name: "SSH",
component: () => import("@/views/document/ssh.vue"), component: () => import("@/views/document/ssh.vue"),
}, },
{
path: "member",
name: "member",
component: () => import("@/views/document/member.vue"),
},
], ],
}, },
{ {
@ -196,6 +201,12 @@ const routes: RouteRecordRaw[] = [
component: () => component: () =>
import("@/views/admin/account/cost/voucher/index.vue"), import("@/views/admin/account/cost/voucher/index.vue"),
}, },
{
path: "growthValue",
name: "growthValue",
component: () =>
import("@/views/admin/growthValue/index.vue"),
},
], ],
}, },
], ],

26
src/utils/validotors.js Normal file
View File

@ -0,0 +1,26 @@
// utils/validators.js
/**
* 密码验证规则8~16必须包含字母和数字
* @param {Object} rule - Ant Design 表单规则对象可选
* @param {string} value - 当前输入的值
* @returns {Promise}
*/
export const validatePassword = (rule, value) => {
if (!value) {
return Promise.reject('请输入密码!');
}
if (value.length < 8 || value.length > 16) {
return Promise.reject('密码长度必须为8~16位');
}
const hasLetter = /[a-zA-Z]/.test(value);
const hasNumber = /\d/.test(value);
if (!hasLetter || !hasNumber) {
return Promise.reject('密码必须同时包含字母和数字!');
}
return Promise.resolve();
};

View File

@ -0,0 +1,111 @@
<template>
<a-row :gutter="[24, 18]">
<a-col :span="24">
<a-card>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div><span style="color:#cca558;font-weight: bold;font-size: 18px;">0</span><span> / 100</span>
</div>
<div>
<a href="/document/member" target="_blank" class="faq-title">了解成长值获取方式></a>
</div>
</div>
<a-progress stroke-linecap="square" :percent="75" strokeColor="#52c41a" />
<div style="color: #333;">
<span>{{ timeRange }}</span>
<span style="margin-left: 10px;color: #999;">(近三个月内获得的成长值)</span>
</div>
</a-card>
</a-col>
<a-col :span="24">
<a-card title="成长值获取记录">
<a-table :dataSource="listData" :columns="columns" bordered :pagination="paginationState"
@change="onTableChange">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'created_at'">
<div>
{{ record.created_at ? dayjs(record.created_at).format('YYYY-MM-DD HH:mm') : '-' }}
</div>
</template>
<template v-if="column.key === 'billing_end'">
<div>
{{ dayjs(record.billing_start).format('YYYY-MM-DD HH:mm') + "~"
+ dayjs(record.billing_end).format('YYYY-MM-DD HH:mm') }}
</div>
</template>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
</template>
<script setup lang="ts">
import { ref, onBeforeMount } from 'vue'
import { usePagination } from '@/hooks'
import { useList } from '@/apis/admin'
import dayjs from 'dayjs'
// const listData=ref([ {title:1}])
// const paginationState=ref({
// total: 0,
// current: 1,
// pageSize: 10,
// showTotal: (total) => ` ${total} `,
// pageSizeOptions: ['10', '20', '30', '40'],
// })
const { listData, paginationState, resetPagination, searchFormData } = usePagination()
const columns = ref([
{ title: '获取方式', dataIndex: 'serial_number', key: 'serial_number' },
{ title: '获取时间', dataIndex: 'created_at', key: 'created_at' },
{ title: '成长值奖励', dataIndex: 'host_id', key: 'host_id' },
])
const timeRange = ref('')
onBeforeMount(() => {
const today = new Date()
const threeMonthsAgo = new Date(today)
threeMonthsAgo.setMonth(today.getMonth() - 3)
const format = (d) => d.toISOString().slice(0, 10)
timeRange.value = `${format(threeMonthsAgo)}${format(today)}`
getPageList()
})
const getPageList = async () => {
try {
const { pageSize, current } = paginationState
const res: any = await useList({ pageSize: pageSize, pageNum: current });
listData.value = res.list;
paginationState.total = res.count;
console.log('订单列表:', res);
// advantageList.value = list;
} catch (error: any) {
console.error('产品优势请求失败:', error);
}
}
/**
* 分页
*/
function onTableChange({ current, pageSize }) {
paginationState.current = current
paginationState.pageSize = pageSize
getPageList()
}
/**
* 搜索
*/
function handleSearch() {
resetPagination()
getPageList()
}
/**
* 重置
*/
function handleResetSearch() {
searchFormData.value = {}
resetPagination()
getPageList()
}
</script>

View File

@ -8,7 +8,7 @@
<a-card title="实例" :bordered="false" class="card"> <a-card title="实例" :bordered="false" class="card">
<a-row :gutter="32" align="middle"> <a-row :gutter="32" align="middle">
<!-- 第一栏容器实例和运行中 --> <!-- 第一栏容器实例和运行中 -->
<a-col :span="8"> <a-col :span="12">
<div class="stats-column"> <div class="stats-column">
<div class="stats-row"> <div class="stats-row">
<div class="stat-item"> <div class="stat-item">
@ -24,7 +24,7 @@
</a-col> </a-col>
<!-- 第二栏即将到期和即将释放 --> <!-- 第二栏即将到期和即将释放 -->
<a-col :span="8"> <a-col :span="12">
<div class="stats-column"> <div class="stats-column">
<div class="vertical-divider"></div> <div class="vertical-divider"></div>
<div class="stats-row"> <div class="stats-row">
@ -51,11 +51,10 @@
</a-col> </a-col>
<!-- 第三栏预警设置 --> <!-- 第三栏预警设置 -->
<a-col :span="8"> <!-- <a-col :span="8">
<div class="stats-column"> <div class="stats-column">
<div class="vertical-divider"></div> <div class="vertical-divider"></div>
<div class="warning-row"> <div class="warning-row">
<!-- 到期释放短信预警 -->
<div class="warning-module"> <div class="warning-module">
<div class="warning-label"> <div class="warning-label">
<span>到期释放</span> <span>到期释放</span>
@ -72,8 +71,6 @@
<a-button type="link" size="small" danger>去绑定邮箱</a-button> <a-button type="link" size="small" danger>去绑定邮箱</a-button>
</div> </div>
</div> </div>
<!-- 余额不足短信预警 -->
<div class="warning-module"> <div class="warning-module">
<div class="warning-label"> <div class="warning-label">
<span>余额不足</span> <span>余额不足</span>
@ -85,11 +82,10 @@
<a-switch v-model:checked="switch2" size="small" /> <a-switch v-model:checked="switch2" size="small" />
<div class="warning-status">{{ switch2 ? '已开启' : '未开启' }}</div> <div class="warning-status">{{ switch2 ? '已开启' : '未开启' }}</div>
</div> </div>
<!-- 无操作按钮 -->
</div> </div>
</div> </div>
</div> </div>
</a-col> </a-col> -->
</a-row> </a-row>
</a-card> </a-card>
@ -161,7 +157,7 @@
<div class="growth-title">成长值</div> <div class="growth-title">成长值</div>
<a-progress :percent="10" status="active" strokeColor="#d46b08" /> <a-progress :percent="10" status="active" strokeColor="#d46b08" />
<div class="growth-text">距离升级还需90成长值 <a href="#">升级攻略</a></div> <div class="growth-text">距离升级还需90成长值 <a href="#">升级攻略</a></div>
<a-button type="link" class="growth-btn">进入成长值主页></a-button> <a-button type="link" class="growth-btn" @click="() => router.push('/layout/admin/growthValue')">进入成长值主页></a-button>
</div> </div>
</a-card> </a-card>
@ -169,12 +165,12 @@
<a-card :bordered="false" class="card margin-top"> <a-card :bordered="false" class="card margin-top">
<div class="fee-header"> <div class="fee-header">
<div class="fee-title">我的余额</div> <div class="fee-title">我的余额</div>
<a-button type="primary" size="small" danger>去充值</a-button> <!-- <a-button type="primary" size="small" danger>去充值</a-button> -->
</div> </div>
<div class="fee-info"> <div class="fee-info">
<div class="fee-item"> <div class="fee-item">
<span>可用</span> <span>可用</span>
<span class="fee-value">¥{{ userInfo.noFreezeBalace }}</span> <span class="fee-value">¥{{ userInfo.balace }}</span>
<span class="fee-divider">|</span> <span class="fee-divider">|</span>
<span>冻结</span> <span>冻结</span>
<span class="fee-value">¥{{ userInfo.freezeBalace }}</span> <span class="fee-value">¥{{ userInfo.freezeBalace }}</span>
@ -182,7 +178,7 @@
<div class="fee-item"> <div class="fee-item">
<GiftOutlined class="fee-icon" /> <GiftOutlined class="fee-icon" />
<span>代金券</span> <span>代金券</span>
<span class="fee-value">¥0.00</span> <span class="fee-value">暂无</span>
</div> </div>
<div class="fee-item"> <div class="fee-item">
<TagOutlined class="fee-icon" /> <TagOutlined class="fee-icon" />
@ -218,6 +214,7 @@ import {
QuestionCircleOutlined QuestionCircleOutlined
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { on } from 'events' import { on } from 'events'
import router from '@/router'
// //

View File

@ -134,7 +134,7 @@ const handleRent = () => {
.instance-list { .instance-list {
padding: 24px; padding: 24px;
background: #f5f7fa; background: #f5f7fa;
min-height: 100vh; // min-height: 100vh;
} }
.header-section { .header-section {

View File

@ -47,6 +47,13 @@ const menuItems = [
{ key: '/document/SSH', label: 'SSH' }, { key: '/document/SSH', label: 'SSH' },
] ]
}, },
{
key:'', label: '会员与计费',
children: [
{ key: '/document/member', label: '会员等级' },
]
}
]; ];
// //
const handleMenuClick = (key) => { const handleMenuClick = (key) => {

View File

@ -0,0 +1,61 @@
<template>
<div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="_1">排位会员<a class="headerlink" href="#_1" title="Permanent link"></a></h1>
<p>当前活动默认注册后会送1个月排位会员</p>
<h2 id="_2">👑会员等级👑<a class="headerlink" href="#_2" title="Permanent link"></a></h2>
<p>会员等级取决于近 3 个月内获得的成长值3个月内的成长值累计一旦满足条件则自动触发升级或降级等级规则如下</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>等级</th>
<th>普通用户</th>
<th>排位会员</th>
</tr>
</thead>
<tbody>
<tr>
<td>会员条件非学生</td>
<td>近3月成长值&gt;=0</td>
<td>近3月成长值&gt;=100</td>
</tr>
<tr>
<td>会员条件学生</td>
<td>--</td>
<td>认证学生期间一直为排位会员</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>举例1当前时间为2021-06-04 12:00:00近3个月(30 *
3)也就是2021-03-06~2021-06-04在这段时间内累计获得正好100成长值那么当下会立即触发升级为排位会员</p>
<p>举例2如果当前未进行学生认证那么现在认证学生会立即使用学生会员升级规则触发升级会员</p>
<blockquote>
<p>鉴于学生能直接享受会员价格强烈建议进行<a href="../student/">学生认证</a></p>
</blockquote>
<h2 id="_3">👑会员成长规则👑<a class="headerlink" href="#_3" title="Permanent link"></a></h2>
<ol>
<li>每充值1元获1成长值如退款成长值将相应扣减</li>
<li>其他任务可获成长值具体请参考网站活动</li>
</ol>
<h2 id="_4">👑会员权益👑<a class="headerlink" href="#_4" title="Permanent link"></a></h2>
<h3 id="gpu">GPU会员优享价<a class="headerlink" href="#gpu" title="Permanent link"></a></h3>
<p>具体价格请以网站实时显示价格为准~</p>
</article>
</div>
</div>
</template>

View File

@ -3,10 +3,10 @@
<div :class="isHome ? 'gx_layout_header_home' : 'gx_layout_header_noHome'" class="gx_layout_header"> <div :class="isHome ? 'gx_layout_header_home' : 'gx_layout_header_noHome'" class="gx_layout_header">
<div class="logo">GxDL算力云</div> <div class="logo">GxDL算力云</div>
<div class="menu"> <div class="menu">
<a-menu v-model:selectedKeys="current" :class="isHome?'custom-menu':''" mode="horizontal" :items="leftRoutes" <a-menu v-model:selectedKeys="current" :class="isHome ? 'custom-menu' : ''" mode="horizontal"
@click="({ key }) => handleMenuClick(key)" /> :items="leftRoutes" @click="({ key }) => handleMenuClick(key)" />
<a-menu v-model:selectedKeys="current" mode="horizontal" :class="isHome?'custom-menu':''" :items="rightRoutes" <a-menu v-model:selectedKeys="current" mode="horizontal" :class="isHome ? 'custom-menu' : ''"
@click="({ key }) => handleMenuClick(key)" /> :items="rightRoutes" @click="({ key }) => handleMenuClick(key)" />
</div> </div>
<div class="user-info"> <div class="user-info">
<a-dropdown> <a-dropdown>
@ -23,11 +23,15 @@
<a-card hoverable style="width: 300px"> <a-card hoverable style="width: 300px">
<template #cover> <template #cover>
<div style="background:#f5f7fa;padding:10px;line-height: 30px"> <div style="background:#f5f7fa;padding:10px;line-height: 30px">
<div>{{ userInfo.userName }} <a-tag color="blue" style="margin-left: 10px;">{{ userInfo.accountType==='USER'?'个人认证':'企业认证' }}</a-tag></div> <div>{{ userInfo.userName }} <a-tag color="blue" style="margin-left: 10px;">{{
userInfo.accountType==='USER'?'个人认证':'企业认证' }}</a-tag></div>
<div>ID:{{ userInfo.id }}</div> <div>ID:{{ userInfo.id }}</div>
</div> </div>
<div style="padding: 10px;line-height: 45px;"> <div style="padding: 10px;line-height: 45px;">
<div style="display: flex;justify-content: space-between;align-items: center;"><div>可用余额¥200.00</div> <a-button type="primary" danger ghost size="small">去充值</a-button></div> <div style="display: flex;justify-content: space-between;align-items: center;">
<div>可用余额¥{{ userInfo.balace }}</div>
<!-- <a-button type="primary" danger ghost size="small">去充值</a-button> -->
</div>
<div>实例数量¥{{ userInfo.caseNum }}</div> <div>实例数量¥{{ userInfo.caseNum }}</div>
<div>冻结余额¥{{ userInfo.freezeBalace }}</div> <div>冻结余额¥{{ userInfo.freezeBalace }}</div>
<div>未冻结余额¥{{ userInfo.noFreezeBalace }}</div> <div>未冻结余额¥{{ userInfo.noFreezeBalace }}</div>
@ -165,6 +169,7 @@ const logout = () => {
background-color: rgba(240, 240, 240, 1); background-color: rgba(240, 240, 240, 1);
} }
} }
/* 自定义菜单主题 */ /* 自定义菜单主题 */
.custom-menu { .custom-menu {
/* 背景色 */ /* 背景色 */

View File

@ -2,15 +2,11 @@
<a-form ref="formRef" :model="formState" name="normal_login" class="login-form" @finish="onFinish" <a-form ref="formRef" :model="formState" name="normal_login" class="login-form" @finish="onFinish"
@finish-failed="onFinishFailed"> @finish-failed="onFinishFailed">
<a-form-item label="手机号" name="phone" :rules="[{ required: true, message: '请输入手机号!' }]"> <a-form-item label="手机号" name="phone" :rules="[{ required: true, message: '请输入手机号!' }]">
<a-input v-model:value="formState.phone" placeholder="请输入手机号"> <SmsCodeInput v-model:value="formState.phone" />
<template #addonAfter>
<div>获取验证码</div>
</template>
</a-input>
</a-form-item> </a-form-item>
<!-- 手动控制密码字段的错误状态 --> <!-- 手动控制密码字段的错误状态 -->
<a-form-item label="验证码" name="code" :rules="[{ required: true, message: '请输入验证码!' }]"> <a-form-item label="验证码" name="code" :rules="[{ required: true, message: '请输入验证码!' }]">
<a-input v-model:value="formState.code" placeholder="请输入验证码"/> <a-input v-model:value="formState.code" placeholder="请输入验证码" />
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-button type="primary" html-type="submit" block class="login-form-button"> <a-button type="primary" html-type="submit" block class="login-form-button">
@ -21,11 +17,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, shallowRef } from 'vue'; import { reactive, ref, shallowRef, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { login, fetchUserInfo } from '@/apis/modules/login'; import { login, fetchUserInfo } from '@/apis/modules/login';
import { message, type FormInstance } from 'ant-design-vue'; import { message, type FormInstance } from 'ant-design-vue';
import SmsCodeInput from '@/components/SmsCodeInput.vue';
const router = useRouter(); const router = useRouter();
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
@ -35,13 +31,11 @@ const codeHelp = shallowRef('');
interface FormState { interface FormState {
phone: string; phone: string;
code: string; code: string;
remember: boolean;
} }
const formState = reactive<FormState>({ const formState = reactive<FormState>({
phone: '', phone: '',
code: '', code: '',
remember: true,
}); });
const clearCodeError = () => { const clearCodeError = () => {
@ -49,17 +43,11 @@ const clearCodeError = () => {
codeHelp.value = ''; codeHelp.value = '';
}; };
const setCodeError = (msg: string) => {
codeValidateStatus.value = 'error';
codeHelp.value = msg;
};
const onFinish = async (values: FormState) => { const onFinish = async (values: FormState) => {
clearCodeError(); clearCodeError();
const loginData = { const loginData = {
...values, ...values,
login_method: 'PWD'//PWDSMS login_method: 'SMS'//PWDSMS
}; };
formRef.value?.validateFields().then(async () => { formRef.value?.validateFields().then(async () => {
try { try {
@ -71,7 +59,7 @@ const onFinish = async (values: FormState) => {
throw new Error('登录失败:未返回有效凭证'); throw new Error('登录失败:未返回有效凭证');
} }
localStorage.setItem('token', token); localStorage.setItem('token', token);
const userRes=await fetchUserInfo(); const userRes = await fetchUserInfo();
localStorage.setItem('userInfo', JSON.stringify(userRes)); localStorage.setItem('userInfo', JSON.stringify(userRes));
router.push('/layout/home'); router.push('/layout/home');
} catch (error: any) { } catch (error: any) {

View File

@ -0,0 +1,73 @@
<template>
<a-form ref="formRef" :model="formState" name="normal_login" class="login-form" @finish="onFinish"
@finish-failed="onFinishFailed" style="margin-top: 45px;">
<a-form-item label="手机号" name="phone" :rules="[{ required: true, message: '请输入手机号!' }]">
<SmsCodeInput v-model:value="formState.phone" />
</a-form-item>
<!-- 手动控制密码字段的错误状态 -->
<a-form-item label="验证码" name="code" :rules="[{ required: true, message: '请输入验证码!' }]">
<a-input v-model:value="formState.code" placeholder="请输入验证码" />
</a-form-item>
<a-form-item label="密码" name="password" :rules="[{ required: true, message: '请输入8~16位且包含字母和数字'},{validator: validatePassword }]">
<a-input-password v-model:value="formState.password" placeholder="请输入密码" />
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" block class="login-form-button">
</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive, ref, shallowRef, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import apis from '@/apis';
import { message, type FormInstance } from 'ant-design-vue';
import SmsCodeInput from '@/components/SmsCodeInput.vue';
import { validatePassword } from '@/utils/validotors'; //
const router = useRouter();
const formRef = ref<FormInstance>();
const codeValidateStatus = shallowRef<'success' | 'warning' | 'error' | ''>('');
const codeHelp = shallowRef('');
interface FormState {
phone: string;
code: string;
password: string;
}
const formState = reactive<FormState>({
phone: '',
code: '',
password: '',
});
const clearCodeError = () => {
codeValidateStatus.value = '';
codeHelp.value = '';
};
const onFinish = async (values: FormState) => {
clearCodeError();
const loginData = {
...values,
};
formRef.value?.validateFields().then(async () => {
try {
const res: any = await apis.login.register(loginData);
console.log('注册请求成功:', res);
message.success('注册成功,请登录');
} catch (error: any) {
console.error('注册请求失败:', error);
message.error(error);
}
});
};
const onFinishFailed = (errorInfo: any) => {
console.log('表单验证失败:', errorInfo);
};
</script>

View File

@ -0,0 +1,72 @@
<template>
<a-form ref="formRef" :model="formState" name="normal_login" class="login-form" @finish="onFinish"
@finish-failed="onFinishFailed" style="margin-top: 45px;">
<a-form-item label="手机号" name="phone" :rules="[{ required: true, message: '请输入手机号!' }]">
<SmsCodeInput v-model:value="formState.phone" />
</a-form-item>
<!-- 手动控制密码字段的错误状态 -->
<a-form-item label="验证码" name="code" :rules="[{ required: true, message: '请输入验证码!' }]">
<a-input v-model:value="formState.code" placeholder="请输入验证码" />
</a-form-item>
<a-form-item label="新密码" name="password" :rules="[{ required: true, message: '请输入8~16位且包含字母和数字'},{validator: validatePassword }]">
<a-input-password v-model:value="formState.password" placeholder="请输入新密码" />
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" block class="login-form-button">
</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive, ref, shallowRef, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import apis from '@/apis';
import { message, type FormInstance } from 'ant-design-vue';
import SmsCodeInput from '@/components/SmsCodeInput.vue';
import { validatePassword } from '@/utils/validotors'; //
const router = useRouter();
const formRef = ref<FormInstance>();
const codeValidateStatus = shallowRef<'success' | 'warning' | 'error' | ''>('');
const codeHelp = shallowRef('');
interface FormState {
phone: string;
code: string;
password: string;
}
const formState = reactive<FormState>({
phone: '',
code: '',
password: '',
});
const clearCodeError = () => {
codeValidateStatus.value = '';
codeHelp.value = '';
};
const onFinish = async (values: FormState) => {
clearCodeError();
const loginData = {
...values,
};
formRef.value?.validateFields().then(async () => {
try {
const res: any = await apis.login.forgetPassword(loginData);
message.success('密码修改成功,请登录');
} catch (error: any) {
console.error('密码修改请求失败:', error);
message.error(error);
}
});
};
const onFinishFailed = (errorInfo: any) => {
console.log('表单验证失败:', errorInfo);
};
</script>

View File

@ -6,7 +6,7 @@
<div class="login-content"> <div class="login-content">
<div class="login-content-main"> <div class="login-content-main">
<h4 class="login-content-title">GPU算力管理平台</h4> <h4 class="login-content-title">GPU算力管理平台</h4>
<div> <div v-if="currentTag == 'login'">
<a-tabs v-model:activeKey="state.tabsActiveName" size="large"> <a-tabs v-model:activeKey="state.tabsActiveName" size="large">
<a-tab-pane key="account" tab="账号密码登录"> <a-tab-pane key="account" tab="账号密码登录">
<Account /> <Account />
@ -16,8 +16,22 @@
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
<div style="width: 100%;display: flex;justify-content: space-between;font-size: 14px;"> <div style="width: 100%;display: flex;justify-content: space-between;font-size: 14px;">
<span style="color:#1677ff;cursor: pointer;">注册</span> <span style="color:#1677ff;cursor: pointer;" @click="currentTag = 'register'">注册</span>
<span style="color:#666;cursor: pointer;">忘记密码</span> <span style="color:#666;cursor: pointer;" @click="currentTag = 'updatePwd'">忘记密码</span>
</div>
</div>
<div v-else-if="currentTag == 'register'">
<div>
<Register />
<span style="color: #666;cursor: pointer;font-size: 14px;" @click="currentTag = 'login'">
< 返回</span>
</div>
</div>
<div v-else-if="currentTag == 'updatePwd'">
<div style="color: #666;cursor: pointer;font-size: 14px;">
<UpdatePwd />
<span style="color: #666;cursor: pointer;font-size: 14px;" @click="currentTag = 'login'">
< 返回</span>
</div> </div>
</div> </div>
</div> </div>
@ -28,13 +42,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, computed } from "vue"; import { reactive, computed, ref } from "vue";
import Account from "@/views/login/component/account.vue"; import Account from "@/views/login/component/account.vue";
import Code from "@/views/login/component/code.vue"; import Code from "@/views/login/component/code.vue";
import Register from "./component/register.vue";
import UpdatePwd from "./component/updatePwd.vue";
// import Mobile from "@/views/login/component/mobile.vue"; // import Mobile from "@/views/login/component/mobile.vue";
// import Scan from "@/views/login/component/scan.vue"; // import Scan from "@/views/login/component/scan.vue";
// import { useThemeConfigStateStore } from "@/stores/themeConfig"; // import { useThemeConfigStateStore } from "@/stores/themeConfig";
// const theme = useThemeConfigStateStore(); // const theme = useThemeConfigStateStore();
const currentTag = ref('login');//login:register:updatePwd:
const state = reactive({ const state = reactive({
tabsActiveName: "account", tabsActiveName: "account",
isTabPaneShow: true, isTabPaneShow: true,

View File

@ -5,6 +5,11 @@ import { fileURLToPath, URL } from "node:url";
import path from "path"; import path from "path";
export default defineConfig({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
// resolve: {
// alias: {
// '@': fileURLToPath(new URL('./src', import.meta.url))
// }
// },
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "src"), "@": path.resolve(__dirname, "src"),
@ -14,13 +19,5 @@ export default defineConfig({
port: 8080, // 设置开发服务器端口为 8080 port: 8080, // 设置开发服务器端口为 8080
host: true, host: true,
open: true, // 可选:启动时自动打开浏览器 open: true, // 可选:启动时自动打开浏览器
proxy: {
"/v1": {
target: "http://10.10.1.27:8888",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/v1/, '')
}
}
}, },
}); });