diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json
new file mode 100644
index 0000000..15db785
--- /dev/null
+++ b/.hbuilderx/launch.json
@@ -0,0 +1,9 @@
+{
+ "version" : "1.0",
+ "configurations" : [
+ {
+ "playground" : "standard",
+ "type" : "uni-app:app-ios"
+ }
+ ]
+}
diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 0000000..8ecac9d
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,23 @@
+// @ts-check
+
+module.exports = {
+ printWidth: 74,
+ tabWidth: 2,
+ semi: false,
+ arrowParens: 'avoid',
+ singleQuote: true,
+ trailingComma: 'none',
+ bracketSpacing: true,
+ htmlWhitespaceSensitivity: 'ignore',
+ endOfLine: 'auto',
+ insertPragma: false,
+ proseWrap: 'preserve',
+ 'objectCurly-newline': [
+ 'error',
+ {
+ multiline: true
+ }
+ ],
+ 'array-bracket-newline': ['error', 'consistent'],
+ vueIndentScriptAndStyle: true
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..7b99e81
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "uni-helper.uni-app-snippets-vscode"
+ ]
+}
\ No newline at end of file
diff --git a/App.vue b/App.vue
index a98e355..6c8f7ab 100644
--- a/App.vue
+++ b/App.vue
@@ -1,34 +1,35 @@
-
diff --git a/components/agreement-checkbox/agreement-checkbox.vue b/components/agreement-checkbox/agreement-checkbox.vue
new file mode 100644
index 0000000..dc4c8e2
--- /dev/null
+++ b/components/agreement-checkbox/agreement-checkbox.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+ 我已阅读并同意
+ 《用户权益》
+ 与
+ 《隐私政策》
+
+
+
+
diff --git a/components/cb-button/cb-button.vue b/components/cb-button/cb-button.vue
new file mode 100644
index 0000000..10b6c00
--- /dev/null
+++ b/components/cb-button/cb-button.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/cb-input/cb-input.vue b/components/cb-input/cb-input.vue
new file mode 100644
index 0000000..24e8592
--- /dev/null
+++ b/components/cb-input/cb-input.vue
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+ {{ isCode ? `${countdown}秒后重新获取` : '获取验证码' }}
+
+
+
+
+
+
diff --git a/components/nav-bar/nav-bar.vue b/components/nav-bar/nav-bar.vue
new file mode 100644
index 0000000..f663269
--- /dev/null
+++ b/components/nav-bar/nav-bar.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/register-app/register-app.vue b/components/register-app/register-app.vue
new file mode 100644
index 0000000..6344a8a
--- /dev/null
+++ b/components/register-app/register-app.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+ {{ isPhone ? '手机' : '邮箱' }}注册
+
+ {{ isPhone ? '邮箱' : '手机号' }}注册
+
+
+
+
+
+
+
diff --git a/constants/storageKeys.js b/constants/storage-keys.js
similarity index 100%
rename from constants/storageKeys.js
rename to constants/storage-keys.js
diff --git a/main.js b/main.js
index 4b747d5..f88e66c 100644
--- a/main.js
+++ b/main.js
@@ -6,7 +6,7 @@ import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
- ...App
+ ...App
})
app.$mount()
// #endif
@@ -15,11 +15,11 @@ app.$mount()
import { createSSRApp } from 'vue'
import * as Pinia from 'pinia'
export function createApp() {
- const app = createSSRApp(App)
- app.use(Pinia.createPinia())
- return {
- app,
- Pinia, // 此处必须将 Pinia 返回
- }
+ const app = createSSRApp(App)
+ app.use(Pinia.createPinia())
+ return {
+ app,
+ Pinia // 此处必须将 Pinia 返回
+ }
}
-// #endif
\ No newline at end of file
+// #endif
diff --git a/pages.json b/pages.json
index a25082e..2bc8699 100644
--- a/pages.json
+++ b/pages.json
@@ -1,5 +1,6 @@
{
- "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+ "pages": [
+ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
@@ -11,6 +12,27 @@
"style": {
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/login/phone-register/phone-register",
+ "style": {
+ "navigationBarTitleText": "手机注册",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/login/email-register/email-register",
+ "style": {
+ "navigationBarTitleText": "邮箱注册",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/login/forgot-password/forgot-password",
+ "style": {
+ "navigationBarTitleText": "忘记密码",
+ "navigationStyle": "custom"
+ }
}
],
"globalStyle": {
@@ -20,4 +42,4 @@
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
-}
+}
\ No newline at end of file
diff --git a/pages/login/email-register/email-register.vue b/pages/login/email-register/email-register.vue
new file mode 100644
index 0000000..1b33423
--- /dev/null
+++ b/pages/login/email-register/email-register.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/pages/login/forgot-password/forgot-password.vue b/pages/login/forgot-password/forgot-password.vue
new file mode 100644
index 0000000..ea9358d
--- /dev/null
+++ b/pages/login/forgot-password/forgot-password.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+ 忘记密码
+
+
+
+
+
+
+
+ 确定
+
+
+
+
+
+
diff --git a/pages/login/login.vue b/pages/login/login.vue
index d91fd32..d01fea7 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -1,15 +1,75 @@
-
- {{ name }}
-
-
-
+
+
+
+ 登录
+ 忘记密码
+
+
+
+
+
+
diff --git a/pages/login/phone-register/phone-register.vue b/pages/login/phone-register/phone-register.vue
new file mode 100644
index 0000000..93c7926
--- /dev/null
+++ b/pages/login/phone-register/phone-register.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/static/images/login/account.png b/static/images/login/account.png
new file mode 100644
index 0000000..cf9a6df
Binary files /dev/null and b/static/images/login/account.png differ
diff --git a/static/images/login/back.png b/static/images/login/back.png
new file mode 100644
index 0000000..ec15f60
Binary files /dev/null and b/static/images/login/back.png differ
diff --git a/static/images/login/code.png b/static/images/login/code.png
new file mode 100644
index 0000000..fe40dd1
Binary files /dev/null and b/static/images/login/code.png differ
diff --git a/static/images/login/conceal.png b/static/images/login/conceal.png
new file mode 100644
index 0000000..182df50
Binary files /dev/null and b/static/images/login/conceal.png differ
diff --git a/static/images/login/email.png b/static/images/login/email.png
new file mode 100644
index 0000000..725644d
Binary files /dev/null and b/static/images/login/email.png differ
diff --git a/static/images/login/invitation.png b/static/images/login/invitation.png
new file mode 100644
index 0000000..13a509b
Binary files /dev/null and b/static/images/login/invitation.png differ
diff --git a/static/images/login/password.png b/static/images/login/password.png
new file mode 100644
index 0000000..fe4dc13
Binary files /dev/null and b/static/images/login/password.png differ
diff --git a/static/images/login/phone.png b/static/images/login/phone.png
new file mode 100644
index 0000000..9f85224
Binary files /dev/null and b/static/images/login/phone.png differ
diff --git a/static/images/login/top.png b/static/images/login/top.png
new file mode 100644
index 0000000..b024ec0
Binary files /dev/null and b/static/images/login/top.png differ
diff --git a/static/images/login/view.png b/static/images/login/view.png
new file mode 100644
index 0000000..868bca2
Binary files /dev/null and b/static/images/login/view.png differ
diff --git a/static/images/public/check-to-confirm.png b/static/images/public/check-to-confirm.png
new file mode 100644
index 0000000..95c9d26
Binary files /dev/null and b/static/images/public/check-to-confirm.png differ
diff --git a/stores/token.js b/stores/token.js
index 73e6125..fa4d009 100644
--- a/stores/token.js
+++ b/stores/token.js
@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
-import { STORAGE_KEYS } from '@/constants/storageKeys';
+import { STORAGE_KEYS } from '@/constants/storage-keys';
import { getToken, removeToken } from '@/utils/storage';
/** 登录状态 */
diff --git a/styles/global.scss b/styles/global.scss
new file mode 100644
index 0000000..2fa036b
--- /dev/null
+++ b/styles/global.scss
@@ -0,0 +1,4 @@
+/* 设置全局背景色 */
+page {
+ background-color: #fff;
+}
diff --git a/styles/login.scss b/styles/login.scss
new file mode 100644
index 0000000..3f85741
--- /dev/null
+++ b/styles/login.scss
@@ -0,0 +1,75 @@
+// 登录,忘记密码顶部样式
+.top-nav {
+ padding: 0 32rpx;
+ height: 446rpx;
+ background-image: url('/static/images/login/top.png');
+ background-size: cover;
+ background-position: 10rpx 0;
+ background-repeat: no-repeat;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ .title-left,
+ .title-right {
+ font-family: PingFang SC, PingFang SC;
+ font-weight: 500;
+ font-style: normal;
+ text-transform: none;
+ margin-bottom: 86rpx;
+ }
+
+ .title-left {
+ font-size: 40rpx;
+ color: #333333;
+ }
+ .title-right {
+ font-size: 28rpx;
+ color: #666666;
+ }
+}
+
+// 注册顶部样式
+.top-register-nav {
+ padding: 120rpx 32rpx 66rpx;
+ display: flex;
+ justify-content: space-between;
+ .title-left,
+ .title-right {
+ font-family: PingFang SC, PingFang SC;
+ font-weight: 500;
+ font-style: normal;
+ text-transform: none;
+ }
+
+ .title-left {
+ font-size: 40rpx;
+ color: #333333;
+ }
+ .title-right {
+ font-size: 28rpx;
+ color: #666666;
+ }
+}
+
+// 输入框排版样式
+.input-wrapper {
+ margin-top: 10rpx;
+ padding: 0 32rpx;
+}
+
+// 底部文字
+.bottom-text {
+ display: flex;
+ justify-content: center;
+ .text {
+ font-family: PingFang SC, PingFang SC;
+ font-weight: 500;
+ font-size: 24rpx;
+ color: #00d9c5;
+ font-style: normal;
+ text-transform: none;
+ &:first-child {
+ color: #333333;
+ }
+ }
+}
diff --git a/uni.scss b/uni.scss
index b9249e9..6c5bb60 100644
--- a/uni.scss
+++ b/uni.scss
@@ -21,32 +21,32 @@ $uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
-$uni-text-color:#333;//基本色
-$uni-text-color-inverse:#fff;//反色
-$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color: #333; //基本色
+$uni-text-color-inverse: #fff; //反色
+$uni-text-color-grey: #999; //辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
-$uni-text-color-disable:#c0c0c0;
+$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
-$uni-bg-color:#ffffff;
-$uni-bg-color-grey:#f8f8f8;
-$uni-bg-color-hover:#f1f1f1;//点击状态颜色
-$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+$uni-bg-color: #ffffff;
+$uni-bg-color-grey: #f8f8f8;
+$uni-bg-color-hover: #f1f1f1; //点击状态颜色
+$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色
/* 边框颜色 */
-$uni-border-color:#c8c7cc;
+$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
-$uni-font-size-sm:12px;
-$uni-font-size-base:14px;
-$uni-font-size-lg:16px;
+$uni-font-size-sm: 12px;
+$uni-font-size-base: 14px;
+$uni-font-size-lg: 16px;
/* 图片尺寸 */
-$uni-img-size-sm:20px;
-$uni-img-size-base:26px;
-$uni-img-size-lg:40px;
+$uni-img-size-sm: 20px;
+$uni-img-size-base: 26px;
+$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
@@ -68,9 +68,9 @@ $uni-spacing-col-lg: 12px;
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
-$uni-color-title: #2C405A; // 文章标题颜色
-$uni-font-size-title:20px;
+$uni-color-title: #2c405a; // 文章标题颜色
+$uni-font-size-title: 20px;
$uni-color-subtitle: #555555; // 二级标题颜色
-$uni-font-size-subtitle:26px;
-$uni-color-paragraph: #3F536E; // 文章段落颜色
-$uni-font-size-paragraph:15px;
+$uni-font-size-subtitle: 26px;
+$uni-color-paragraph: #3f536e; // 文章段落颜色
+$uni-font-size-paragraph: 15px;
diff --git a/utils/request.js b/utils/request.js
index f67862c..1cd03e7 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -32,7 +32,7 @@ const request = (options) => {
title: '加载中...',
mask: true
})
- }
+ };
return new Promise((resolve, reject) => {
uni.request({
diff --git a/utils/router.js b/utils/router.js
index 38f1ced..d5e2ac9 100644
--- a/utils/router.js
+++ b/utils/router.js
@@ -7,60 +7,66 @@
* @returns {string} 拼接后的完整 url
*/
const appendParams = (url, params) => {
- if (!params || Object.keys(params).length === 0) {
- return url
- }
+ if (!params || Object.keys(params).length === 0) {
+ return url
+ }
- const query = Object.entries(params).map(([key, value]) => {
- // 处理复杂类型(如对象、数组)需序列化
- if (typeof value === 'object') {
- value = encodeURIComponent(JSON.stringify(value))
- } else {
- value = encodeURIComponent(String(value))
- }
- return `${key}=${value}`
- }).join('&')
+ const query = Object.entries(params)
+ .map(([key, value]) => {
+ // 处理复杂类型(如对象、数组)需序列化
+ if (typeof value === 'object') {
+ value = encodeURIComponent(JSON.stringify(value))
+ } else {
+ value = encodeURIComponent(String(value))
+ }
+ return `${key}=${value}`
+ })
+ .join('&')
- return url.includes('?') ? `${url}&${query}` : `${url}?${query}`
+ return url.includes('?') ? `${url}&${query}` : `${url}?${query}`
}
/**
* 普通跳转(保留返回)
*/
export const navigateTo = (url, params = {}) => {
- const finalUrl = appendParams(url, params)
- return uni.navigateTo({ url: finalUrl })
+ const finalUrl = appendParams(url, params)
+ return uni.navigateTo({
+ url: finalUrl
+ })
}
/**
* 关闭当前页,跳转到应用内某个页面(不可返回)
*/
export const redirectTo = (url, params = {}) => {
- const finalUrl = appendParams(url, params)
- return uni.redirectTo({ url: finalUrl })
+ const finalUrl = appendParams(url, params)
+ return uni.redirectTo({ url: finalUrl })
}
/**
* 关闭所有页面,打开到应用内某个页面
*/
export const reLaunch = (url, params = {}) => {
- const finalUrl = appendParams(url, params)
- return uni.reLaunch({ url: finalUrl })
+ const finalUrl = appendParams(url, params)
+ return uni.reLaunch({ url: finalUrl })
}
/**
* 返回上一页(可指定 delta)
*/
export const navigateBack = (delta = 1) => {
- return uni.navigateBack({ delta })
+ return uni.navigateBack({ delta })
}
/**
* 跳转到 tabBar 页面(只能用 switchTab)
*/
export const switchTab = (url, params = {}) => {
- if (Object.keys(params).length > 0) {
- console.warn('switchTab 不支持携带参数,请使用全局状态或 storage 传递')
- }
- return uni.switchTab({ url })
-}
\ No newline at end of file
+ if (Object.keys(params).length > 0) {
+ console.warn(
+ 'switchTab 不支持携带参数,请使用全局状态或 storage 传递'
+ )
+ }
+ return uni.switchTab({ url })
+}
diff --git a/utils/storage.js b/utils/storage.js
index baeffa0..e7155af 100644
--- a/utils/storage.js
+++ b/utils/storage.js
@@ -1,4 +1,4 @@
-import { STORAGE_KEYS } from '@/constants/storageKeys'
+import { STORAGE_KEYS } from '@/constants/storage-keys'
/** 保存 token */
export const setToken = (v) => {
diff --git a/utils/use-ui.js b/utils/use-ui.js
new file mode 100644
index 0000000..738464c
--- /dev/null
+++ b/utils/use-ui.js
@@ -0,0 +1,56 @@
+// 加载状态,提示语
+import { ref } from 'vue'
+
+// 全局 loading 状态(可用于页面绑定 v-if)
+const isLoading = ref(false)
+
+/**
+ * 显示 loading
+ * @param {string} title - 提示文字(H5 支持,App 小程序部分支持)
+ */
+const showLoading = (title = '加载中...') => {
+ isLoading.value = true
+ // uni.showLoading 在 H5 和 App 中行为略有不同,但基本可用
+ uni.showLoading({
+ title,
+ mask: true // 防止穿透点击
+ })
+}
+
+/**
+ * 隐藏 loading
+ */
+const hideLoading = () => {
+ isLoading.value = false
+ uni.hideLoading()
+}
+
+/**
+ * 统一 Toast 提示
+ * @param {string} message - 提示内容
+ * @param {string} type - 'success' | 'error' | 'warning' | 'none'
+ * @param {number} duration - 持续时间(毫秒)
+ */
+const showToast = (message, type = 'none', duration = 2000) => {
+ let icon = 'none'
+ if (type === 'success') icon = 'success'
+ if (type === 'error') icon = 'error'
+ if (type === 'warning') icon = 'none'
+
+ uni.showToast({
+ title: message,
+ icon,
+ duration,
+ mask: true
+ })
+}
+
+// 导出响应式状态和方法
+export const useUI = () => {
+ return {
+ isLoading: isLoading, // 可用于模板中 v-if="isLoading"
+ showLoading,
+ hideLoading,
+ showToast
+ }
+}