From 76ba02d29bbf74928e6a2650c4ee075a7b4c7787 Mon Sep 17 00:00:00 2001 From: yangyao Date: Wed, 14 Jan 2026 18:36:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B3=A8=E5=86=8C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 2 +- api/user.js | 22 +- components/login-popup.vue | 44 - components/login.vue | 1615 ++++++++++++++++++------------------ manifest.json | 5 +- pages.json | 6 + pages/index/index.vue | 17 +- pages/login/api.js | 39 + pages/login/index.vue | 839 +++++++++++++++++++ pages/mbti/index.vue | 2 +- pages/memory/index.vue | 2 +- pages/mine/index.vue | 166 +++- pages/order/index.vue | 2 +- pages/product/index.vue | 2 +- store/user.js | 225 +++-- utils/request.js | 127 ++- 16 files changed, 2090 insertions(+), 1025 deletions(-) delete mode 100644 components/login-popup.vue create mode 100644 pages/login/api.js create mode 100644 pages/login/index.vue diff --git a/.env.development b/.env.development index b2a11e7..d461ade 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ # .env.development NODE_ENV=development VITE_APP_RUN_ENV=development -VITE_APP_BASE_URL="http://127.0.0.1:8000" \ No newline at end of file +VITE_APP_BASE_URL="https://takway-ai.top/test" \ No newline at end of file diff --git a/api/user.js b/api/user.js index bf8884b..6be2736 100644 --- a/api/user.js +++ b/api/user.js @@ -18,7 +18,13 @@ export const editMobileInit = (params) => { * @desc 发送验证码 */ export const sendCode = (params) => { - return get('/sale/login/send-mb-verify-code', params); + return post('/app-api/member/auth/send-sms-code', params); +}; +/** + * @desc 发送邮箱验证码 + */ +export const sendCodeToEmail = (params) => { + return get('/app-api/member/auth/code', params); }; /** * @desc 发送验证码初始化 @@ -26,11 +32,23 @@ export const sendCode = (params) => { export const loginLRInit = (params) => { return get('/sale/login/send-mb-verify-code-init', params); }; +/** + * @desc 邮箱注册 + */ +export const registerByEmail = (params) => { + return post('/app-api/member/auth/email-register', params); +}; +/** + * @desc 邮箱登录 + */ +export const loginByEmail = (params) => { + return post('/app-api/member/auth/email-login', params); +}; /** * @desc 登录 */ export const login = (params) => { - return get('/sale/login/login', params); + return post('/app-api/member/auth/weixin-mini-app-login', params); }; /** * @desc 注册 diff --git a/components/login-popup.vue b/components/login-popup.vue deleted file mode 100644 index d4545c6..0000000 --- a/components/login-popup.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - diff --git a/components/login.vue b/components/login.vue index 2144852..ad9b2f8 100644 --- a/components/login.vue +++ b/components/login.vue @@ -1,855 +1,834 @@ - - diff --git a/manifest.json b/manifest.json index 903b86c..64693d2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name" : "sweekar-app", - "appid" : "__UNI__AB3EDCE", + "appid" : "__UNI__308E2A1", "description" : "", "versionName" : "1.1.3", "versionCode" : 113, @@ -45,7 +45,8 @@ }, /* ios打包配置 */ "ios" : { - "idfa" : false + "idfa" : false, + "dSYMs" : false }, /* SDK配置 */ "sdkConfigs" : {} diff --git a/pages.json b/pages.json index 4d52608..240c342 100644 --- a/pages.json +++ b/pages.json @@ -47,6 +47,12 @@ "style": { "navigationStyle": "custom" } + }, + { + "path": "pages/login/index", + "style": { + "navigationBarTitleText": "登录" + } } ], diff --git a/pages/index/index.vue b/pages/index/index.vue index 0441e02..5d8ba19 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -3,7 +3,7 @@ import { ref, onMounted, getCurrentInstance, nextTick } from 'vue'; import { onLoad, onShow, onReady, onHide, onPullDownRefresh } from '@dcloudio/uni-app'; import CustomTabBar from '@/components/CustomTabBar.vue' import { getSystemSafeArea, getSystemInfo, isEmpty, showToast, showLoadingToast, closeToast } from '@/common/common.js'; -import LoginPopup from '@/components/login-popup.vue'; +import LoginPopup from '@/components/login.vue'; import { getProducts } from './api.js'; import { useUserStore } from '@/store/user.js'; @@ -134,7 +134,8 @@ const handleBook = (productId) => { - + Master,I missed you! Let's play together! @@ -206,5 +207,17 @@ const handleBook = (productId) => { diff --git a/pages/login/api.js b/pages/login/api.js new file mode 100644 index 0000000..6224f1d --- /dev/null +++ b/pages/login/api.js @@ -0,0 +1,39 @@ +import { get, post, put, dele } from '@/utils/request'; + +/** + * @desc 发送邮箱验证码 + */ +export const sendCodeToEmail = (params) => { + return get('/app-api/member/auth/code', params); +}; +/** + * @desc 发送验证码初始化 + */ +export const loginLRInit = (params) => { + return get('/sale/login/send-mb-verify-code-init', params); +}; +/** + * @desc 邮箱注册 + */ +export const registerByEmail = (params) => { + return post('/app-api/member/auth/email-register', params); +}; +/** + * @desc 邮箱登录 + */ +export const loginByEmail = (params) => { + return post('/app-api/member/auth/email-login', params); +}; + +/** + * @desc 登录 + */ +export const login = (params) => { + return post('/app-api/member/auth/weixin-mini-app-login', params); +}; +/** + * @desc 注册 + */ +export const register = (params) => { + return get('/front/user/login/register', params); +}; \ No newline at end of file diff --git a/pages/login/index.vue b/pages/login/index.vue new file mode 100644 index 0000000..9137a12 --- /dev/null +++ b/pages/login/index.vue @@ -0,0 +1,839 @@ + + + + + diff --git a/pages/mbti/index.vue b/pages/mbti/index.vue index ec9a77d..8b798af 100644 --- a/pages/mbti/index.vue +++ b/pages/mbti/index.vue @@ -3,7 +3,7 @@ import { ref, onMounted, getCurrentInstance, nextTick } from 'vue'; import { onLoad, onShow, onReady, onHide, onPullDownRefresh } from '@dcloudio/uni-app'; import CustomTabBar from '@/components/CustomTabBar.vue' import { getSystemSafeArea, getSystemInfo, isEmpty, showToast, showLoadingToast, closeToast } from '@/common/common.js'; -import LoginPopup from '@/components/login-popup.vue'; +import LoginPopup from '@/components/login.vue'; import { getProducts } from './api.js'; import { useUserStore } from '@/store/user.js'; diff --git a/pages/memory/index.vue b/pages/memory/index.vue index ec9a77d..8b798af 100644 --- a/pages/memory/index.vue +++ b/pages/memory/index.vue @@ -3,7 +3,7 @@ import { ref, onMounted, getCurrentInstance, nextTick } from 'vue'; import { onLoad, onShow, onReady, onHide, onPullDownRefresh } from '@dcloudio/uni-app'; import CustomTabBar from '@/components/CustomTabBar.vue' import { getSystemSafeArea, getSystemInfo, isEmpty, showToast, showLoadingToast, closeToast } from '@/common/common.js'; -import LoginPopup from '@/components/login-popup.vue'; +import LoginPopup from '@/components/login.vue'; import { getProducts } from './api.js'; import { useUserStore } from '@/store/user.js'; diff --git a/pages/mine/index.vue b/pages/mine/index.vue index ec9a77d..301c633 100644 --- a/pages/mine/index.vue +++ b/pages/mine/index.vue @@ -3,7 +3,7 @@ import { ref, onMounted, getCurrentInstance, nextTick } from 'vue'; import { onLoad, onShow, onReady, onHide, onPullDownRefresh } from '@dcloudio/uni-app'; import CustomTabBar from '@/components/CustomTabBar.vue' import { getSystemSafeArea, getSystemInfo, isEmpty, showToast, showLoadingToast, closeToast } from '@/common/common.js'; -import LoginPopup from '@/components/login-popup.vue'; +import LoginPopup from '@/components/login.vue'; import { getProducts } from './api.js'; import { useUserStore } from '@/store/user.js'; @@ -29,6 +29,27 @@ const productData = ref(); const scrollHeight = ref(0); //scroll-view滚动高度 const pageView = ref({}); + +// 控制登录弹窗显示 +const toggleLoginPopup = () => { + showLogin.value = !showLogin.value; +}; + +// 关闭登录弹窗 +const closeLoginPopup = () => { + showLogin.value = false; +}; + +// 登录成功回调 +const onLoginSuccess = () => { + closeLoginPopup(); + // 可以在这里执行登录成功后的操作 + uni.showToast({ + title: '登录成功', + icon: 'success' + }); +}; + /** * @desc 获取可视内容高度 */ @@ -36,11 +57,6 @@ const getContentHeight = (e) => { pageView.value = e; }; - - - - - /** * @desc 跳转至详情页面 */ @@ -49,6 +65,7 @@ const toDetailPage = (productId) => { url: '/pages/product/detail', }); }; + //获取首页数据 const getProductList = async()=>{ const res = await getProducts(); @@ -57,7 +74,6 @@ const getProductList = async()=>{ console.log("商品数据",productData.value); } - onLoad(()=>{ // getProductList(); @@ -65,7 +81,6 @@ onLoad(()=>{ // 模拟数据 - /** * @desc 处理预订事件 */ @@ -76,14 +91,129 @@ const handleBook = (productId) => { }); }; +/** + * @desc 跳转至登录页面 + */ +const toLoginPage = (productId) => { + uni.navigateTo({ + url: '/pages/login/index', + }); +}; + +onMounted(() => { + // 示例:在页面加载完成后自动显示登录弹窗 + // setTimeout(() => { + // showLogin.value = true; + // }, 1000); +}); diff --git a/pages/order/index.vue b/pages/order/index.vue index ec9a77d..8b798af 100644 --- a/pages/order/index.vue +++ b/pages/order/index.vue @@ -3,7 +3,7 @@ import { ref, onMounted, getCurrentInstance, nextTick } from 'vue'; import { onLoad, onShow, onReady, onHide, onPullDownRefresh } from '@dcloudio/uni-app'; import CustomTabBar from '@/components/CustomTabBar.vue' import { getSystemSafeArea, getSystemInfo, isEmpty, showToast, showLoadingToast, closeToast } from '@/common/common.js'; -import LoginPopup from '@/components/login-popup.vue'; +import LoginPopup from '@/components/login.vue'; import { getProducts } from './api.js'; import { useUserStore } from '@/store/user.js'; diff --git a/pages/product/index.vue b/pages/product/index.vue index ec9a77d..8b798af 100644 --- a/pages/product/index.vue +++ b/pages/product/index.vue @@ -3,7 +3,7 @@ import { ref, onMounted, getCurrentInstance, nextTick } from 'vue'; import { onLoad, onShow, onReady, onHide, onPullDownRefresh } from '@dcloudio/uni-app'; import CustomTabBar from '@/components/CustomTabBar.vue' import { getSystemSafeArea, getSystemInfo, isEmpty, showToast, showLoadingToast, closeToast } from '@/common/common.js'; -import LoginPopup from '@/components/login-popup.vue'; +import LoginPopup from '@/components/login.vue'; import { getProducts } from './api.js'; import { useUserStore } from '@/store/user.js'; diff --git a/store/user.js b/store/user.js index ce74548..a390586 100644 --- a/store/user.js +++ b/store/user.js @@ -1,27 +1,77 @@ // 引用pinia商店 -import { ref, reactive } from 'vue'; +import { ref, computed } from 'vue'; import { defineStore } from 'pinia'; import { parse, stringify } from 'zipson'; + export const useUserStore = defineStore( 'user', () => { - const userInfo = ref({}); - const platform = ref(''); - const userToken = ref(''); + // 用户基础信息 + const userInfo = ref({ + areaCode: '', + avatar: '', + birthday: null, + email: null, + gender: null, + mobile: '', + nickName: '', + trueName: null, + userId: null, + }); + + // 认证相关信息 + const token = ref(''); const isLogin = ref(false); + + // 平台配置信息 + const platform = ref(''); const currency = ref('HKD'); + const lang = ref(''); + + // 客服联系方式 const kfTelCN = ref(''); const kfTelHK = ref(''); const wxLink = ref(''); - const lang = ref(''); - const setProfile = (val) => { - userInfo.value = { - ...val, - }; - console.log(userInfo.value); + + // 计算属性 - 是否已登录 + const isLoggedIn = computed(() => isLogin.value && token.value !== ''); + + // 计算属性 - 用户头像 + const userAvatar = computed(() => userInfo.value.avatar || '/static/default-avatar.png'); + + // 计算属性 - 用户昵称 + const userName = computed(() => userInfo.value.nickName || userInfo.value.trueName || '游客'); + + // 设置用户信息 + const setUserInfo = (data) => { + if (data && typeof data === 'object') { + userInfo.value = { + ...userInfo.value, + ...data, + }; + } }; - //清理用户信息,退出时使用 - const clearProfile = () => { + + // 设置认证令牌 + const setToken = (tokenValue) => { + token.value = tokenValue || ''; + isLogin.value = !!tokenValue; + }; + + // 设置登录状态 + const setLoginStatus = (status) => { + isLogin.value = Boolean(status); + }; + + // 用户登录 - 合并设置用户信息和令牌 + const login = (userData, accessToken) => { + setUserInfo(userData); + setToken(accessToken); + setLoginStatus(true); + }; + + // 用户登出 - 清理所有用户相关数据 + const logout = () => { userInfo.value = { areaCode: '', avatar: '', @@ -33,80 +83,131 @@ export const useUserStore = defineStore( trueName: null, userId: null, }; + token.value = ''; + isLogin.value = false; }; + + // 更新用户特定字段 + const updateUserInfo = (field, value) => { + if (userInfo.value.hasOwnProperty(field)) { + userInfo.value[field] = value; + } + }; + + // 批量更新用户信息 + const batchUpdateUserInfo = (updates) => { + if (updates && typeof updates === 'object') { + Object.keys(updates).forEach(key => { + if (userInfo.value.hasOwnProperty(key)) { + userInfo.value[key] = updates[key]; + } + }); + } + }; + + // 获取用户权限列表(如果有的话) + const getUserPermissions = computed(() => { + // 这里可以根据实际业务逻辑返回用户权限 + return userInfo.value.permissions || []; + }); + + // 检查用户是否有特定权限 + const hasPermission = (permission) => { + if (!isLoggedIn.value) return false; + return getUserPermissions.value.includes(permission); + }; + return { - isLogin, + // 状态 userInfo, - userToken, + token, + isLogin, platform, currency, kfTelCN, kfTelHK, wxLink, lang, - setProfile, - clearProfile, + + // 计算属性 + isLoggedIn, + userAvatar, + userName, + getUserPermissions, + + // 动作 + setUserInfo, + setToken, + setLoginStatus, + login, + logout, + updateUserInfo, + batchUpdateUserInfo, + hasPermission, }; }, { persist: { + key: 'user-store', // 自定义存储键名 storage: { getItem(key) { - return uni.getStorageSync(key); + try { + const storedData = uni.getStorageSync(key); + return storedData; + } catch (error) { + console.error('读取用户存储数据失败:', error); + return null; + } }, setItem(key, value) { - uni.setStorageSync(key, value); + try { + uni.setStorageSync(key, value); + } catch (error) { + console.error('保存用户数据失败:', error); + } }, }, serializer: { - deserialize: parse, - serialize: stringify, + deserialize: (data) => { + try { + return parse(data); + } catch (error) { + console.error('反序列化用户数据失败:', error); + return null; + } + }, + serialize: (data) => { + try { + return stringify(data); + } catch (error) { + console.error('序列化用户数据失败:', error); + return '{}'; + } + }, }, + // 指定需要持久化的状态 + paths: ['userInfo', 'token', 'isLogin', 'platform', 'currency', 'kfTelCN', 'kfTelHK', 'wxLink', 'lang'], }, } ); /* - 页面内使用pinia - - 1、引用你定义的store实例 - import { useUserStore } from '@/stores/user' +使用示例: +1. 在组件中导入 + import { useUserStore } from '@/store/user.js' - 2、实例化useUserStore - const user = useUserStore() - - 3、引入 storeToRefs 用于 给实例的属性创建refs,即响应式 - import { storeToRefs } from 'pinia' - 例如:const {count} = storeToRefs(user) - count.value 此时为响应式 - - 4、修改state 方式共两种 如下 - user.count++ - 4-1、 - // 带自动补全 ✨ - user.$patch({ count: counter.count + 1 }) - $patch()可批量修改state的属性值 - 例如: - 批量修改 - const pathState = () => { - store.$patch({ - user: { name: 'ks', age: 20 }, - }); - }; - - 其他操作state的方法如下: - - 替换State - const replaceState = () => { - store.$state.user = { age: 20, name: 'eee', sex: 'women' }; - }; - 重置state的值 - const reset = () => { - store.$reset(); - }; - - 4-2、 - // 或使用 action 代替 - user.increment() - - */ +2. 使用方式 + const userStore = useUserStore(); + + // 获取计算属性 + const { isLoggedIn, userName } = storeToRefs(userStore); + + // 调用动作 + userStore.login(userData, accessToken); + userStore.logout(); + + // 检查权限 + if (userStore.hasPermission('admin')) { + // 执行管理员操作 + } +*/ diff --git a/utils/request.js b/utils/request.js index a3092bd..a424566 100644 --- a/utils/request.js +++ b/utils/request.js @@ -1,84 +1,73 @@ +// utils/request.js(或你当前文件) + import { useUserStore } from '@/store/user'; export const baseUrl = import.meta.env.VITE_APP_BASE_URL; const appVersion = uni.getSystemInfoSync().appVersion; -// 添加拦截器 -// const httpInterceptor = { -// // 拦截前触发 -// invoke(options) { -// // 1. 非 http 开头需拼接地址 -// if (!options.url.startsWith('http')) { -// options.url = baseUrl + options.url; -// } -// // 2. 请求超时, 默认 10s -// options.timeout = 10000; -// options.header = { -// ...options.header, -// 'Content-Type': 'application/x-www-form-urlencoded', -// }; -// const userStore = useUserStore(); -// options.data = { -// ...options.data, -// __token: userStore.userToken, -// __platform: userStore.platform, -// }; -// }, -// }; -// uni.addInterceptor('request', httpInterceptor); -// // #ifndef H5 -// uni.addInterceptor('uploadFile', httpInterceptor); -// // #endif -// 对uni原生的request做的一个封装 +// 对 uni.request 的封装 export const request = (options = {}) => { return new Promise((resolve, reject) => { const userStore = useUserStore(); - // 请求头 - options.header = { - 'Content-Type': 'application/x-www-form-urlencoded', - // version: appVersion, - }; - var params = { + + // 👇 关键修改:默认 Content-Type 为 application/json + const contentType = options.header?.['Content-Type'] || 'application/json'; + + // 公共参数 + const commonParams = { __token: userStore.userToken, __platform: userStore.platform, __language: userStore.lang, - __channelId: 33, }; - var data = options.data; - data = { - ...data, - ...params, + + // 合并 data + let requestData = { + ...options.data, + ...commonParams, }; - // uni原生request + + // 👇 如果是 JSON,data 保持为对象(uni.request 会自动序列化) + // 如果是 x-www-form-urlencoded,需要手动转成字符串(但不推荐) + if (contentType === 'application/x-www-form-urlencoded') { + // 注意:uni.request 在非 H5 平台可能不支持直接传对象 + x-www-form-urlencoded + // 所以这里需要手动拼接 + const params = new URLSearchParams(); + for (const key in requestData) { + if (requestData[key] !== undefined && requestData[key] !== null) { + params.append(key, requestData[key]); + } + } + requestData = params.toString(); // 转为 a=1&b=2 形式 + } + // 如果是 application/json,requestData 保持为对象即可 + uni .request({ - url: baseUrl + options.url || '', + url: baseUrl + (options.url || ''), method: options.method || 'GET', - data: data || {}, - header: options.header || {}, + data: requestData, // 对象 or 字符串 + header: { + 'Content-Type': contentType, + // version: appVersion, // 如需可加 + ...options.header, + }, }) .then((res) => { if (res.statusCode === 401) { - // usetToken无效,移除本地缓存 userStore.userToken = ''; userStore.clearProfile(); userStore.isLogin = false; - resolve({ - ...res.data, - statusCode: res.statusCode, - }); - } else { - resolve({ - ...res.data, - statusCode: res.statusCode, - }); } + resolve({ + ...res.data, + statusCode: res.statusCode, + }); }) .catch((error) => { if (error.statusCode === 401) { - // usetToken无效,移除本地缓存 userStore.userToken = ''; userStore.clearProfile(); + userStore.isLogin = false; reject({ ...error.data, statusCode: error.statusCode, @@ -99,37 +88,19 @@ export const request = (options = {}) => { }); }; -// 定义get请求 +// 导出方法(保持不变) export const get = (url, data = {}, options = {}) => { - options.url = url; - options.method = 'GET'; - options.data = data; - - return request(options); + return request({ ...options, url, method: 'GET', data }); }; -// 定义post请求 + export const post = (url, data = {}, options = {}) => { - options.url = url; - options.method = 'POST'; - options.data = data; - - return request(options); + return request({ ...options, url, method: 'POST', data }); }; -// 定义put请求 export const put = (url, data = {}, options = {}) => { - options.url = url; - options.method = 'PUT'; - options.data = data; - - return request(options); + return request({ ...options, url, method: 'PUT', data }); }; -// 定义delete请求 export const dele = (url, data = {}, options = {}) => { - options.url = url; - options.method = 'DELETE'; - options.data = data; - - return request(options); -}; + return request({ ...options, url, method: 'DELETE', data }); +}; \ No newline at end of file