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
- });
}
},