From 8446c004e7199a776376bbef073c5ff5dac6709e Mon Sep 17 00:00:00 2001 From: sjk <2513533895@qq.com> Date: Thu, 8 Jan 2026 13:10:15 +0800 Subject: [PATCH] commit --- go_backend/controller/auth_controller.go | 37 ++++++ go_backend/router/router.go | 1 + .../miniprogram/pages/articles/articles.ts | 14 ++- miniprogram/miniprogram/pages/home/home.ts | 33 +++-- miniprogram/miniprogram/pages/home/home.wxml | 11 ++ miniprogram/miniprogram/pages/home/home.wxss | 80 ++++++++++++ miniprogram/miniprogram/pages/login/login.ts | 53 ++++++-- .../miniprogram/pages/login/login.wxml | 22 ++++ .../miniprogram/pages/login/login.wxss | 117 ++++++++++++++++-- .../miniprogram/pages/login/phone-login.ts | 69 ++++++----- 10 files changed, 373 insertions(+), 64 deletions(-) diff --git a/go_backend/controller/auth_controller.go b/go_backend/controller/auth_controller.go index 1119b78..41c7db3 100644 --- a/go_backend/controller/auth_controller.go +++ b/go_backend/controller/auth_controller.go @@ -6,6 +6,7 @@ import ( "ai_xhs/service" "ai_xhs/utils" "context" + "time" "github.com/gin-gonic/gin" ) @@ -184,6 +185,42 @@ func (ctrl *AuthController) XHSPhoneCodeLogin(c *gin.Context) { }) } +// SendSmsCode 发送阿里云短信验证码 +func (ctrl *AuthController) SendSmsCode(c *gin.Context) { + var req struct { + Phone string `json:"phone" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + common.Error(c, common.CodeInvalidParams, "参数错误: "+err.Error()) + return + } + + // 预检查:验证手机号是否存在于user表中 + if err := ctrl.authService.CheckPhoneExists(req.Phone); err != nil { + common.Error(c, common.CodeServerError, err.Error()) + return + } + + // 调用短信服务发送验证码 + smsService := service.GetSmsService() + code, err := smsService.SendVerificationCode(req.Phone) + if err != nil { + common.Error(c, common.CodeServerError, err.Error()) + return + } + + // 开发环境返回验证码,生产环境不返回 + responseData := gin.H{ + "sent_at": time.Now().Format("2006-01-02 15:04:05"), + } + if config.AppConfig.Server.Mode == "dev" || config.AppConfig.Server.Mode == "debug" { + responseData["code"] = code + } + + common.SuccessWithMessage(c, "验证码发送成功", responseData) +} + // SendXHSVerificationCode 发送小红书手机号验证码 func (ctrl *AuthController) SendXHSVerificationCode(c *gin.Context) { var req struct { diff --git a/go_backend/router/router.go b/go_backend/router/router.go index 07b4661..7b78a55 100644 --- a/go_backend/router/router.go +++ b/go_backend/router/router.go @@ -31,6 +31,7 @@ func SetupRouter(r *gin.Engine) { api.POST("/login/phone", authCtrl.PhoneLogin) // 手机号登录(测试用) api.POST("/login/phone-password", authCtrl.PhonePasswordLogin) // 手机号密码登录 api.POST("/login/xhs-phone-code", authCtrl.XHSPhoneCodeLogin) // 小红书手机号验证码登录 + api.POST("/login/send-sms-code", authCtrl.SendSmsCode) // 发送阿里云短信验证码 api.POST("/xhs/send-verification-code", authCtrl.SendXHSVerificationCode) // 发送小红书验证码 api.POST("/logout", middleware.AuthMiddleware(), authCtrl.Logout) // 退出登录(需要认证) diff --git a/miniprogram/miniprogram/pages/articles/articles.ts b/miniprogram/miniprogram/pages/articles/articles.ts index 728e20a..e12c492 100644 --- a/miniprogram/miniprogram/pages/articles/articles.ts +++ b/miniprogram/miniprogram/pages/articles/articles.ts @@ -138,13 +138,23 @@ Page({ // 处理图片URL:使用统一工具函数处理 const processedCopies = copies.map((copy: any) => { if (copy.images && Array.isArray(copy.images)) { + console.log('[图片处理] 原始图片数据:', copy.images); copy.images = copy.images.map((img: any) => { + const originalUrl = img.image_url; + const processedUrl = getImageUrl(img.image_url); + const originalThumb = img.image_thumb_url || img.image_url; + const processedThumb = getImageUrl(img.image_thumb_url || img.image_url); + + console.log('[图片处理] 原始URL:', originalUrl, '-> 处理后:', processedUrl); + console.log('[图片处理] 原始缩略图:', originalThumb, '-> 处理后:', processedThumb); + return { ...img, - image_url: getImageUrl(img.image_url), - image_thumb_url: getImageUrl(img.image_thumb_url || img.image_url) + image_url: processedUrl, + image_thumb_url: processedThumb }; }); + console.log('[图片处理] 处理后图片数据:', copy.images); } return copy; }); diff --git a/miniprogram/miniprogram/pages/home/home.ts b/miniprogram/miniprogram/pages/home/home.ts index 52dc66b..8e58c65 100644 --- a/miniprogram/miniprogram/pages/home/home.ts +++ b/miniprogram/miniprogram/pages/home/home.ts @@ -30,11 +30,12 @@ Page({ page: 1, pageSize: 6, userAvatar: '/images/default-avatar.svg', // 默认头像 - avatarX: 9999, // 头像 X 坐标(设置到屏幕外,等待初始化) - avatarY: 9999, // 头像 Y 坐标 + avatarX: 9999, // 头像X坐标(设置到屏幕外,等待初始化) + avatarY: 9999, // 头像Y坐标 windowWidth: 375, // 窗口宽度 windowHeight: 667, // 窗口高度 - avatarAnimation: false // 是否启用头像动画 + avatarAnimation: false, // 是否启用头像动画 + showTipModal: false // 是否显示提示模态框 }, onLoad() { @@ -133,7 +134,16 @@ Page({ const response = await EmployeeService.getProducts(page, pageSize); if (response.code === 200 && response.data) { + console.log('[首页图片] 后端返回的产品数据:', response.data.list); + const apiProducts = response.data.list.map((product: ApiProduct, index: number) => { + const originalImage = product.image; + const processedImage = getImageUrl(product.image) || `https://picsum.photos/id/${237 + products.length + index}/300/400`; + + console.log(`[首页图片] 产品 ${product.name}:`); + console.log(' - 原始图片URL:', originalImage); + console.log(' - 处理后URL:', processedImage); + // 商品名称最多8个字,多了直接截断 const truncatedName = product.name.length > 8 ? product.name.substring(0, 8) : product.name; @@ -142,7 +152,7 @@ Page({ name: truncatedName, price: 0, // 后端暂无价格字段 sales: product.available_copies || 0, - image: getImageUrl(product.image) || `https://picsum.photos/id/${237 + products.length + index}/300/400`, + image: processedImage, category: 'beauty', // 后端暂无分类字段 tags: ['种草', '推荐'], hotLevel: product.available_copies > 5 ? 5 : 3, @@ -196,10 +206,12 @@ Page({ // 去生成内容 async goToGenerate() { if (!this.data.selectedProduct) { - wx.showToast({ - title: '请先选择商品', - icon: 'none' - }); + // 显示居中提示框 + this.setData({ showTipModal: true }); + // 1.5秒后自动关闭 + setTimeout(() => { + this.setData({ showTipModal: false }); + }, 1500); return; } // 1. 检查登录状态 @@ -335,6 +347,11 @@ Page({ wx.setStorageSync('avatarPosition', { x: targetX, y: targetY }); }, + // 关闭提示模态框 + closeTipModal() { + this.setData({ showTipModal: false }); + }, + // 分享功能 onShareAppMessage() { return { diff --git a/miniprogram/miniprogram/pages/home/home.wxml b/miniprogram/miniprogram/pages/home/home.wxml index 998e276..2611b9a 100644 --- a/miniprogram/miniprogram/pages/home/home.wxml +++ b/miniprogram/miniprogram/pages/home/home.wxml @@ -58,4 +58,15 @@ + + + + + + + + + 请先选择商品 + + diff --git a/miniprogram/miniprogram/pages/home/home.wxss b/miniprogram/miniprogram/pages/home/home.wxss index 43c1e9b..d8a29b3 100644 --- a/miniprogram/miniprogram/pages/home/home.wxss +++ b/miniprogram/miniprogram/pages/home/home.wxss @@ -235,3 +235,83 @@ page { height: 100%; border-radius: 50%; } + +/* 居中提示模态框 */ +.tip-modal { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s; +} + +.tip-modal.show { + opacity: 1; + pointer-events: auto; +} + +.tip-content { + background: rgba(60, 60, 60, 0.95); + border-radius: 16rpx; + padding: 40rpx; + display: flex; + flex-direction: column; + align-items: center; + gap: 20rpx; + width: 280rpx; + height: 280rpx; + box-sizing: border-box; + justify-content: center; +} + +.tip-icon { + width: 80rpx; + height: 80rpx; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.icon-circle { + position: absolute; + width: 80rpx; + height: 80rpx; + border: 4rpx solid white; + border-radius: 50%; + box-sizing: border-box; +} + +.icon-dot { + position: absolute; + width: 8rpx; + height: 8rpx; + background: white; + border-radius: 50%; + top: 20rpx; +} + +.icon-dot::after { + content: ''; + position: absolute; + width: 6rpx; + height: 32rpx; + background: white; + border-radius: 3rpx; + left: 1rpx; + top: 14rpx; +} + +.tip-text { + font-size: 28rpx; + color: white; + text-align: center; +} diff --git a/miniprogram/miniprogram/pages/login/login.ts b/miniprogram/miniprogram/pages/login/login.ts index 5ed348d..05e9f3a 100644 --- a/miniprogram/miniprogram/pages/login/login.ts +++ b/miniprogram/miniprogram/pages/login/login.ts @@ -5,7 +5,8 @@ import { API } from '../../config/api'; Page({ data: { loginLoading: false, - agreed: false + agreed: false, + showAgreementModal: false // 是否显示协议弹窗 }, onLoad() { @@ -105,19 +106,45 @@ Page({ handleAgreeFirst() { if (this.data.loginLoading) return - // 弹窗提示用户同意协议 - wx.showModal({ - title: '用户协议', - content: '请阅读并同意《用户协议》和《隐私政策》后再登录', - confirmText: '同意', - cancelText: '不同意', - success: (res) => { - if (res.confirm) { - // 用户点击了同意,勾选协议 - this.setData({ agreed: true }); - } - } + // 显示协议弹窗 + this.setData({ showAgreementModal: true }); + }, + + // 同意并登录 + agreeAndLogin(e: any) { + if (this.data.loginLoading) return + + // 检查用户是否授权了手机号 + if (e.detail.errMsg && e.detail.errMsg !== 'getPhoneNumber:ok') { + // 用户拒绝授权手机号 + console.log('用户拒绝授权手机号:', e.detail.errMsg); + this.setData({ showAgreementModal: false }); + wx.showToast({ + title: '需要授权手机号才能登录', + icon: 'none', + duration: 2000 + }); + return; + } + + // 勾选协议、关闭弹窗、发起登录 + this.setData({ + agreed: true, + showAgreementModal: false }); + + // 直接发起登录 + this.performLogin(e.detail); + }, + + // 不同意 + disagree() { + this.setData({ showAgreementModal: false }); + }, + + // 关闭弹窗 + closeAgreementModal() { + this.setData({ showAgreementModal: false }); }, // 微信登录(已同意协议后触发) diff --git a/miniprogram/miniprogram/pages/login/login.wxml b/miniprogram/miniprogram/pages/login/login.wxml index 1867b26..e17360f 100644 --- a/miniprogram/miniprogram/pages/login/login.wxml +++ b/miniprogram/miniprogram/pages/login/login.wxml @@ -63,4 +63,26 @@ + + + + + + 请阅读并同意以下条款 + + 《万花筒AI用户协议》 + 《隐私协议》 + + + + + + + + diff --git a/miniprogram/miniprogram/pages/login/login.wxss b/miniprogram/miniprogram/pages/login/login.wxss index d8a787b..a66171a 100644 --- a/miniprogram/miniprogram/pages/login/login.wxss +++ b/miniprogram/miniprogram/pages/login/login.wxss @@ -148,6 +148,7 @@ checkbox { margin: 0; padding: 0; flex-shrink: 0; + margin-top: 4rpx; /* 微调对齐 */ } /* 将 checkbox 改为圆形 */ @@ -155,6 +156,9 @@ checkbox .wx-checkbox-input { border-radius: 50% !important; width: 36rpx !important; height: 36rpx !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; } checkbox .wx-checkbox-input-checked { @@ -163,15 +167,17 @@ checkbox .wx-checkbox-input-checked { } checkbox .wx-checkbox-input-checked::before { - border-radius: 50%; - width: 36rpx; - height: 36rpx; - line-height: 36rpx; - text-align: center; - font-size: 24rpx; - color: #fff; - background: transparent; - transform: translate(-50%, -50%) scale(1); + width: 100% !important; + height: 100% !important; + line-height: 36rpx !important; + text-align: center !important; + font-size: 24rpx !important; + color: #fff !important; + background: transparent !important; + transform: translate(-50%, -50%) scale(1) !important; + left: 50% !important; + top: 50% !important; + position: absolute !important; } .agreement-text { @@ -191,3 +197,96 @@ checkbox .wx-checkbox-input-checked::before { color: #07c160; font-weight: 500; } + +/* 协议确认弹窗 */ +.agreement-modal { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: flex-end; + z-index: 9999; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s; +} + +.agreement-modal.show { + opacity: 1; + pointer-events: auto; +} + +.modal-content { + width: 100%; + background: white; + border-radius: 32rpx 32rpx 0 0; + padding: 0; + transform: translateY(100%); + transition: transform 0.3s; +} + +.agreement-modal.show .modal-content { + transform: translateY(0); +} + +.modal-body { + padding: 48rpx 48rpx 32rpx; + text-align: center; +} + +.modal-tip { + display: block; + font-size: 30rpx; + color: #333; + font-weight: 500; + margin-bottom: 32rpx; +} + +.modal-links { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 8rpx; +} + +.modal-links .link { + font-size: 26rpx; + color: #07c160; + font-weight: 400; +} + +.modal-footer { + padding: 0 48rpx 48rpx; + padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); + display: flex; + flex-direction: column; + gap: 24rpx; +} + +.modal-btn { + width: 100% !important; + padding: 26rpx; + font-size: 30rpx; + font-weight: 500; + border-radius: 12rpx; + border: none; +} + +.modal-btn::after { + border: none; +} + +.agree-btn { + background: #07c160; + color: white; +} + +.disagree-btn { + background: transparent; + color: #333; + border: none; +} diff --git a/miniprogram/miniprogram/pages/login/phone-login.ts b/miniprogram/miniprogram/pages/login/phone-login.ts index 6330184..97dd70c 100644 --- a/miniprogram/miniprogram/pages/login/phone-login.ts +++ b/miniprogram/miniprogram/pages/login/phone-login.ts @@ -143,7 +143,7 @@ Page({ } }, - // 获取验证码 + // 获取验证码(阿里云短信) async getVerifyCode() { if (!this.data.agreed) { wx.showToast({ @@ -169,40 +169,45 @@ Page({ } try { - // 调用封装的Service方法发送验证码(禁用loading,验证码发送应立即响应) - const res = await EmployeeService.sendXHSCode(phone, false); - - // 兼容 code=0 和 code=200 - if (res.code === 200 || res.code === 0) { - // 发送成功 - wx.showToast({ - title: '验证码已发送', - icon: 'success', - duration: 2000 - }); - - // 开发环境打印验证码 - if (res.data && res.data.code) { - console.log('验证码:', res.data.code); + wx.request({ + url: `${API.baseURL}/api/login/send-sms-code`, + method: 'POST', + data: { + phone: phone + }, + success: (res: any) => { + if (res.statusCode === 200 && (res.data.code === 200 || res.data.code === 0)) { + wx.showToast({ + title: '验证码已发送', + icon: 'success', + duration: 2000 + }); + + // 开发环境打印验证码 + if (res.data.data && res.data.data.code) { + console.log('验证码:', res.data.data.code); + } + + this.startCountdown(); + } else { + wx.showToast({ + title: res.data.message || '发送失败', + icon: 'none', + duration: 2000 + }); + } + }, + fail: (err) => { + console.error('发送验证码失败:', err); + wx.showToast({ + title: '网络错误', + icon: 'none', + duration: 2000 + }); } - - // 开始倒计时 - this.startCountdown(); - } else { - // 发送失败 - wx.showToast({ - title: res.message || '发送失败,请稍后重试', - icon: 'none', - duration: 2000 - }); - } + }); } catch (err) { console.error('发送验证码失败:', err); - wx.showToast({ - title: '网络错误,请稍后重试', - icon: 'none', - duration: 2000 - }); } },