Zhangbw преди 3 месеца
родител
ревизия
97ff9c4956

+ 2 - 1
dist/dev/mp-weixin/app.js

@@ -7,6 +7,7 @@ if (!Math) {
   "./pages/strong/strong.js";
   "./pages/rank/rank.js";
   "./pages/mine/mine.js";
+  "./pages/login/login.js";
 }
 const _sfc_main = {
   globalData: {
@@ -29,7 +30,7 @@ const _sfc_main = {
     console.log("App Hide");
   }
 };
-const App = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/miniprogram-1/src/App.vue"]]);
+const App = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/App.vue"]]);
 function createApp() {
   const app = common_vendor.createSSRApp(App);
   return {

+ 12 - 11
dist/dev/mp-weixin/app.json

@@ -4,7 +4,8 @@
     "pages/pool/pool",
     "pages/strong/strong",
     "pages/rank/rank",
-    "pages/mine/mine"
+    "pages/mine/mine",
+    "pages/login/login"
   ],
   "window": {
     "navigationBarBackgroundColor": "#ffffff",
@@ -20,32 +21,32 @@
       {
         "pagePath": "pages/index/index",
         "text": "打分查询",
-        "iconPath": "static/images/tab_search.png",
-        "selectedIconPath": "static/images/tab_search_active.png"
+        "iconPath": "/static/images/tab_search.png",
+        "selectedIconPath": "/static/images/tab_search_active.png"
       },
       {
         "pagePath": "pages/pool/pool",
         "text": "超短池",
-        "iconPath": "static/images/tab_short.png",
-        "selectedIconPath": "static/images/tab_short_active.png"
+        "iconPath": "/static/images/tab_short.png",
+        "selectedIconPath": "/static/images/tab_short_active.png"
       },
       {
         "pagePath": "pages/strong/strong",
         "text": "强势池",
-        "iconPath": "static/images/tab_strong.png",
-        "selectedIconPath": "static/images/tab_strong_active.png"
+        "iconPath": "/static/images/tab_strong.png",
+        "selectedIconPath": "/static/images/tab_strong_active.png"
       },
       {
         "pagePath": "pages/rank/rank",
         "text": "模拟排名",
-        "iconPath": "static/images/tab_rank.png",
-        "selectedIconPath": "static/images/tab_rank_active.png"
+        "iconPath": "/static/images/tab_rank.png",
+        "selectedIconPath": "/static/images/tab_rank_active.png"
       },
       {
         "pagePath": "pages/mine/mine",
         "text": "个人中心",
-        "iconPath": "static/images/tab_mine.png",
-        "selectedIconPath": "static/images/tab_mine_active.png"
+        "iconPath": "/static/images/tab_mine.png",
+        "selectedIconPath": "/static/images/tab_mine_active.png"
       }
     ]
   },

+ 50 - 14
dist/dev/mp-weixin/pages/index/index.js

@@ -1,6 +1,7 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
 const utils_api = require("../../utils/api.js");
+const utils_auth = require("../../utils/auth.js");
 const _sfc_main = {
   __name: "index",
   setup(__props) {
@@ -11,7 +12,40 @@ const _sfc_main = {
     const result = common_vendor.ref(null);
     const suggestions = common_vendor.ref([]);
     const showDropdown = common_vendor.ref(false);
+    const isLoggedIn = common_vendor.ref(false);
     let timer = null;
+    common_vendor.onMounted(() => {
+      isLoggedIn.value = utils_auth.isLoggedIn();
+      console.log("=== 页面加载 ===");
+      console.log("登录状态:", isLoggedIn.value);
+      console.log("Token:", common_vendor.index.getStorageSync("user_token"));
+    });
+    common_vendor.onShow(() => {
+      isLoggedIn.value = utils_auth.isLoggedIn();
+      console.log("=== 页面显示 ===");
+      console.log("登录状态:", isLoggedIn.value);
+    });
+    const handleSearchClick = () => {
+      console.log("=== 点击搜索按钮 ===");
+      console.log("当前登录状态:", isLoggedIn.value);
+      if (!isLoggedIn.value) {
+        console.log("未登录,显示登录提示");
+        common_vendor.index.showModal({
+          title: "提示",
+          content: "查询股票信息需要先登录,是否前往登录?",
+          success: (res) => {
+            if (res.confirm) {
+              common_vendor.index.navigateTo({
+                url: "/pages/login/login"
+              });
+            }
+          }
+        });
+        return;
+      }
+      console.log("已登录,执行搜索");
+      onSearch();
+    };
     const onKeywordChange = (e) => {
       const value = e.detail.value;
       keyword.value = value;
@@ -30,8 +64,9 @@ const _sfc_main = {
         return;
       }
       try {
-        const list = await utils_api.getSuggestions(kw.trim());
-        console.log("模糊查询返回数据:", list);
+        const response = await utils_api.getSuggestions(kw.trim());
+        console.log("模糊查询返回数据:", response);
+        const list = response.data || [];
         suggestions.value = Array.isArray(list) ? list : [];
         showDropdown.value = suggestions.value.length > 0;
         console.log("下拉框状态:", { showDropdown: showDropdown.value, suggestionsLength: suggestions.value.length });
@@ -95,7 +130,7 @@ const _sfc_main = {
         b: common_vendor.o(onSearch),
         c: common_vendor.o(onInputBlur),
         d: keyword.value,
-        e: common_vendor.o(onSearch),
+        e: common_vendor.o(handleSearchClick),
         f: showDropdown.value && suggestions.value && suggestions.value.length > 0
       }, showDropdown.value && suggestions.value && suggestions.value.length > 0 ? {
         g: common_vendor.f(suggestions.value, (item, index, i0) => {
@@ -107,16 +142,17 @@ const _sfc_main = {
           };
         })
       } : {}, {
-        h: hasSearched.value
+        h: common_vendor.t(isLoggedIn.value ? "" : "(需登录)"),
+        i: hasSearched.value
       }, hasSearched.value ? common_vendor.e({
-        i: loading.value
+        j: loading.value
       }, loading.value ? {} : errorMsg.value ? {
-        k: common_vendor.t(errorMsg.value)
+        l: common_vendor.t(errorMsg.value)
       } : result.value ? {
-        m: common_vendor.t(result.value.stockName),
-        n: common_vendor.t(result.value.stockCode),
-        o: common_vendor.t(result.value.score),
-        p: common_vendor.f(result.value.history, (item, index, i0) => {
+        n: common_vendor.t(result.value.stockName),
+        o: common_vendor.t(result.value.stockCode),
+        p: common_vendor.t(result.value.score),
+        q: common_vendor.f(result.value.history, (item, index, i0) => {
           return {
             a: common_vendor.t(item.date),
             b: common_vendor.t(item.score),
@@ -124,7 +160,7 @@ const _sfc_main = {
             d: index
           };
         }),
-        q: common_vendor.f(result.value.factors, (item, index, i0) => {
+        r: common_vendor.f(result.value.factors, (item, index, i0) => {
           return {
             a: common_vendor.t(item.name),
             b: common_vendor.t(item.value),
@@ -132,11 +168,11 @@ const _sfc_main = {
           };
         })
       } : {}, {
-        j: errorMsg.value,
-        l: result.value
+        k: errorMsg.value,
+        m: result.value
       }) : {});
     };
   }
 };
-const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/miniprogram-1/src/pages/index/index.vue"]]);
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/pages/index/index.vue"]]);
 wx.createPage(MiniProgramPage);

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/dev/mp-weixin/pages/index/index.wxml


+ 216 - 0
dist/dev/mp-weixin/pages/login/login.js

@@ -0,0 +1,216 @@
+"use strict";
+const common_vendor = require("../../common/vendor.js");
+const utils_api = require("../../utils/api.js");
+const utils_auth = require("../../utils/auth.js");
+const _sfc_main = {
+  data() {
+    return {
+      phone: "",
+      code: "",
+      countdown: 0,
+      agreed: false,
+      canUseWxLogin: true,
+      // 是否支持微信登录
+      timer: null
+    };
+  },
+  onLoad() {
+    this.canUseWxLogin = true;
+  },
+  onUnload() {
+    if (this.timer) {
+      clearInterval(this.timer);
+    }
+  },
+  methods: {
+    /**
+     * 处理微信一键登录
+     * @param {object} e - 事件对象,包含加密的手机号信息
+     */
+    async handleWxLogin(e) {
+      if (!this.agreed) {
+        common_vendor.index.showToast({
+          title: "请先同意用户协议和隐私政策",
+          icon: "none"
+        });
+        return;
+      }
+      if (e.detail.errMsg === "getPhoneNumber:ok") {
+        try {
+          common_vendor.index.showLoading({ title: "登录中..." });
+          const loginRes = await common_vendor.index.login();
+          const result = await utils_api.wxLogin(loginRes.code);
+          utils_auth.setToken(result.data.token);
+          utils_auth.setUserInfo(result.data.userInfo);
+          common_vendor.index.hideLoading();
+          common_vendor.index.showToast({
+            title: "登录成功",
+            icon: "success"
+          });
+          setTimeout(() => {
+            this.navigateBack();
+          }, 1500);
+        } catch (error) {
+          common_vendor.index.hideLoading();
+          common_vendor.index.showToast({
+            title: error.message || "登录失败",
+            icon: "none"
+          });
+        }
+      } else {
+        common_vendor.index.showToast({
+          title: "获取手机号失败",
+          icon: "none"
+        });
+      }
+    },
+    /**
+     * 发送验证码
+     */
+    async sendCode() {
+      if (!this.phone) {
+        common_vendor.index.showToast({
+          title: "请输入手机号",
+          icon: "none"
+        });
+        return;
+      }
+      if (!/^1[3-9]\d{9}$/.test(this.phone)) {
+        common_vendor.index.showToast({
+          title: "请输入正确的手机号",
+          icon: "none"
+        });
+        return;
+      }
+      if (!this.agreed) {
+        common_vendor.index.showToast({
+          title: "请先同意用户协议和隐私政策",
+          icon: "none"
+        });
+        return;
+      }
+      try {
+        common_vendor.index.showLoading({ title: "发送中..." });
+        await utils_api.sendSmsCode(this.phone);
+        common_vendor.index.hideLoading();
+        common_vendor.index.showToast({
+          title: "验证码已发送",
+          icon: "success"
+        });
+        this.countdown = 60;
+        this.timer = setInterval(() => {
+          this.countdown--;
+          if (this.countdown <= 0) {
+            clearInterval(this.timer);
+          }
+        }, 1e3);
+      } catch (error) {
+        common_vendor.index.hideLoading();
+        common_vendor.index.showToast({
+          title: error.message || "发送失败",
+          icon: "none"
+        });
+      }
+    },
+    /**
+     * 处理手机号登录
+     */
+    async handlePhoneLogin() {
+      if (!this.phone) {
+        common_vendor.index.showToast({
+          title: "请输入手机号",
+          icon: "none"
+        });
+        return;
+      }
+      if (!/^1[3-9]\d{9}$/.test(this.phone)) {
+        common_vendor.index.showToast({
+          title: "请输入正确的手机号",
+          icon: "none"
+        });
+        return;
+      }
+      if (!this.code) {
+        common_vendor.index.showToast({
+          title: "请输入验证码",
+          icon: "none"
+        });
+        return;
+      }
+      if (!this.agreed) {
+        common_vendor.index.showToast({
+          title: "请先同意用户协议和隐私政策",
+          icon: "none"
+        });
+        return;
+      }
+      try {
+        common_vendor.index.showLoading({ title: "登录中..." });
+        console.log("准备登录,phone:", this.phone, "code:", this.code);
+        const result = await utils_api.phoneLogin(this.phone, this.code);
+        console.log("登录成功,result:", result);
+        utils_auth.setToken(result.data.token);
+        utils_auth.setUserInfo(result.data.userInfo);
+        utils_auth.setUserInfo(result.data.userInfo);
+        common_vendor.index.hideLoading();
+        common_vendor.index.showToast({
+          title: "登录成功",
+          icon: "success"
+        });
+        setTimeout(() => {
+          this.navigateBack();
+        }, 1500);
+      } catch (error) {
+        common_vendor.index.hideLoading();
+        common_vendor.index.showToast({
+          title: error.message || "登录失败",
+          icon: "none"
+        });
+      }
+    },
+    /**
+     * 处理协议勾选变化
+     */
+    handleAgreeChange(e) {
+      this.agreed = e.detail.value.length > 0;
+    },
+    /**
+     * 返回上一页或首页
+     */
+    navigateBack() {
+      const pages = getCurrentPages();
+      if (pages.length > 1) {
+        common_vendor.index.navigateBack();
+        const app = getApp();
+        if (app.globalData.loginCallback) {
+          app.globalData.loginCallback();
+          app.globalData.loginCallback = null;
+        }
+      } else {
+        common_vendor.index.switchTab({
+          url: "/pages/index/index"
+        });
+      }
+    }
+  }
+};
+function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
+  return common_vendor.e({
+    a: $data.canUseWxLogin
+  }, $data.canUseWxLogin ? {
+    b: common_vendor.o((...args) => $options.handleWxLogin && $options.handleWxLogin(...args))
+  } : {}, {
+    c: $data.phone,
+    d: common_vendor.o(($event) => $data.phone = $event.detail.value),
+    e: $data.code,
+    f: common_vendor.o(($event) => $data.code = $event.detail.value),
+    g: common_vendor.t($data.countdown > 0 ? `${$data.countdown}秒后重试` : "获取验证码"),
+    h: common_vendor.o((...args) => $options.sendCode && $options.sendCode(...args)),
+    i: $data.countdown > 0,
+    j: common_vendor.o((...args) => $options.handlePhoneLogin && $options.handlePhoneLogin(...args)),
+    k: $data.agreed,
+    l: common_vendor.o((...args) => $options.handleAgreeChange && $options.handleAgreeChange(...args))
+  });
+}
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-cdfe2409"], ["__file", "D:/program/gupiao-wx/src/pages/login/login.vue"]]);
+wx.createPage(MiniProgramPage);

+ 6 - 0
dist/dev/mp-weixin/pages/login/login.json

@@ -0,0 +1,6 @@
+{
+  "navigationBarTitleText": "登录",
+  "navigationBarBackgroundColor": "#667eea",
+  "navigationBarTextStyle": "white",
+  "usingComponents": {}
+}

+ 1 - 0
dist/dev/mp-weixin/pages/login/login.wxml

@@ -0,0 +1 @@
+<view class="login-container data-v-cdfe2409"><view class="login-header data-v-cdfe2409"><text class="app-name data-v-cdfe2409">量化选股大师</text><text class="welcome-text data-v-cdfe2409">欢迎登录</text></view><view class="login-form data-v-cdfe2409"><button wx:if="{{a}}" class="wx-login-btn data-v-cdfe2409" open-type="getPhoneNumber" bindgetphonenumber="{{b}}"><image class="wx-icon data-v-cdfe2409" src="/static/images/wechat.png" mode="aspectFit"></image><text class="data-v-cdfe2409">微信一键登录</text></button><view class="divider data-v-cdfe2409"><view class="divider-line data-v-cdfe2409"></view><text class="divider-text data-v-cdfe2409">或</text><view class="divider-line data-v-cdfe2409"></view></view><view class="phone-login data-v-cdfe2409"><view class="input-group data-v-cdfe2409"><input class="input-field data-v-cdfe2409" type="number" placeholder="请输入手机号" maxlength="11" value="{{c}}" bindinput="{{d}}"/></view><view class="input-group code-group data-v-cdfe2409"><input class="input-field code-input data-v-cdfe2409" type="number" placeholder="请输入验证码" maxlength="6" value="{{e}}" bindinput="{{f}}"/><button class="code-btn data-v-cdfe2409" bindtap="{{h}}" disabled="{{i}}">{{g}}</button></view><button class="login-btn data-v-cdfe2409" bindtap="{{j}}">登录</button></view><view class="agreement data-v-cdfe2409"><checkbox-group class="data-v-cdfe2409" bindchange="{{l}}"><label class="data-v-cdfe2409"><checkbox class="data-v-cdfe2409" checked="{{k}}" color="#5d55e8"/><text class="agreement-text data-v-cdfe2409">我已阅读并同意</text><text class="agreement-link data-v-cdfe2409">《用户协议》</text><text class="agreement-text data-v-cdfe2409">和</text><text class="agreement-link data-v-cdfe2409">《隐私政策》</text></label></checkbox-group></view></view></view>

+ 121 - 0
dist/dev/mp-weixin/pages/login/login.wxss

@@ -0,0 +1,121 @@
+
+.login-container.data-v-cdfe2409 {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  padding: 100rpx 60rpx;
+}
+.login-header.data-v-cdfe2409 {
+  text-align: center;
+  margin-bottom: 100rpx;
+}
+.app-name.data-v-cdfe2409 {
+  display: block;
+  font-size: 48rpx;
+  font-weight: bold;
+  color: #ffffff;
+  margin-bottom: 20rpx;
+}
+.welcome-text.data-v-cdfe2409 {
+  display: block;
+  font-size: 28rpx;
+  color: rgba(255, 255, 255, 0.8);
+}
+.login-form.data-v-cdfe2409 {
+  background: #ffffff;
+  border-radius: 20rpx;
+  padding: 60rpx 40rpx;
+  box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.1);
+}
+.wx-login-btn.data-v-cdfe2409 {
+  width: 100%;
+  height: 90rpx;
+  background: #07c160;
+  color: #ffffff;
+  border-radius: 45rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 32rpx;
+  border: none;
+  margin-bottom: 40rpx;
+}
+.wx-icon.data-v-cdfe2409 {
+  width: 40rpx;
+  height: 40rpx;
+  margin-right: 15rpx;
+}
+.divider.data-v-cdfe2409 {
+  display: flex;
+  align-items: center;
+  margin: 40rpx 0;
+}
+.divider-line.data-v-cdfe2409 {
+  flex: 1;
+  height: 1rpx;
+  background: #e5e5e5;
+}
+.divider-text.data-v-cdfe2409 {
+  padding: 0 20rpx;
+  color: #999999;
+  font-size: 24rpx;
+}
+.phone-login.data-v-cdfe2409 {
+  margin-top: 40rpx;
+}
+.input-group.data-v-cdfe2409 {
+  margin-bottom: 30rpx;
+}
+.input-field.data-v-cdfe2409 {
+  width: 100%;
+  height: 90rpx;
+  background: #f5f6fb;
+  border-radius: 45rpx;
+  padding: 0 30rpx;
+  font-size: 28rpx;
+  box-sizing: border-box;
+}
+.code-group.data-v-cdfe2409 {
+  display: flex;
+  align-items: center;
+}
+.code-input.data-v-cdfe2409 {
+  flex: 1;
+  margin-right: 20rpx;
+}
+.code-btn.data-v-cdfe2409 {
+  width: 200rpx;
+  height: 90rpx;
+  background: #5d55e8;
+  color: #ffffff;
+  border-radius: 45rpx;
+  font-size: 24rpx;
+  border: none;
+  padding: 0;
+  line-height: 90rpx;
+}
+.code-btn[disabled].data-v-cdfe2409 {
+  background: #cccccc;
+}
+.login-btn.data-v-cdfe2409 {
+  width: 100%;
+  height: 90rpx;
+  background: #5d55e8;
+  color: #ffffff;
+  border-radius: 45rpx;
+  font-size: 32rpx;
+  border: none;
+  margin-top: 20rpx;
+}
+.agreement.data-v-cdfe2409 {
+  margin-top: 40rpx;
+  text-align: center;
+}
+.agreement-text.data-v-cdfe2409 {
+  font-size: 24rpx;
+  color: #999999;
+  margin: 0 5rpx;
+}
+.agreement-link.data-v-cdfe2409 {
+  font-size: 24rpx;
+  color: #5d55e8;
+}

+ 51 - 26
dist/dev/mp-weixin/pages/mine/mine.js

@@ -1,55 +1,80 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
+const utils_auth = require("../../utils/auth.js");
 const _sfc_main = {
   __name: "mine",
   setup(__props) {
     const isLoggedIn = common_vendor.ref(false);
     const userInfo = common_vendor.ref({
-      nickName: "",
-      avatarUrl: ""
+      nickname: "",
+      avatar: "",
+      phone: ""
     });
     const points = common_vendor.ref(900);
+    common_vendor.onMounted(() => {
+      loadUserInfo();
+    });
+    const loadUserInfo = () => {
+      isLoggedIn.value = utils_auth.isLoggedIn();
+      if (isLoggedIn.value) {
+        const storedInfo = utils_auth.getUserInfo();
+        if (storedInfo) {
+          userInfo.value = storedInfo;
+        }
+      }
+    };
     const toggleLogin = () => {
       if (isLoggedIn.value) {
-        isLoggedIn.value = false;
-        userInfo.value = { nickName: "", avatarUrl: "" };
-        common_vendor.index.showToast({ title: "已退出登录", icon: "none" });
+        common_vendor.index.showModal({
+          title: "提示",
+          content: "确定要退出登录吗?",
+          success: (res) => {
+            if (res.confirm) {
+              utils_auth.logout();
+              isLoggedIn.value = false;
+              userInfo.value = { nickname: "", avatar: "", phone: "" };
+              common_vendor.index.showToast({ title: "已退出登录", icon: "none" });
+            }
+          }
+        });
       } else {
-        isLoggedIn.value = true;
-        userInfo.value = {
-          nickName: "测试用户",
-          // Using a placeholder or one of the existing images as a mock avatar
-          avatarUrl: "/static/images/tab_mine_active.png"
-        };
-        common_vendor.index.showToast({ title: "登录成功", icon: "success" });
+        common_vendor.index.navigateTo({
+          url: "/pages/login/login"
+        });
       }
     };
     const handleRecharge = () => {
-      if (!isLoggedIn.value) {
-        common_vendor.index.showToast({ title: "请先登录", icon: "none" });
+      if (!utils_auth.checkLogin(() => {
+        handleRecharge();
+      })) {
         return;
       }
       common_vendor.index.showToast({ title: "充值功能开发中", icon: "none" });
     };
     const handleHistory = () => {
-      if (!isLoggedIn.value) {
-        common_vendor.index.showToast({ title: "请先登录", icon: "none" });
+      if (!utils_auth.checkLogin(() => {
+        handleHistory();
+      })) {
         return;
       }
       common_vendor.index.showToast({ title: "记录功能开发中", icon: "none" });
     };
     return (_ctx, _cache) => {
-      return {
-        a: isLoggedIn.value && userInfo.value.avatarUrl ? userInfo.value.avatarUrl : "/static/images/tab_mine_active.png",
-        b: common_vendor.t(isLoggedIn.value ? userInfo.value.nickName || "微信用户" : "游客"),
-        c: common_vendor.t(isLoggedIn.value ? points.value : 0),
-        d: common_vendor.o(handleRecharge),
-        e: common_vendor.o(handleHistory),
-        f: common_vendor.t(isLoggedIn.value ? "退出登录" : "登录"),
-        g: common_vendor.o(toggleLogin)
-      };
+      return common_vendor.e({
+        a: isLoggedIn.value && userInfo.value.avatar ? userInfo.value.avatar : "/static/images/tab_mine_active.png",
+        b: common_vendor.t(isLoggedIn.value ? userInfo.value.nickname || "微信用户" : "游客"),
+        c: isLoggedIn.value && userInfo.value.phone
+      }, isLoggedIn.value && userInfo.value.phone ? {
+        d: common_vendor.t(userInfo.value.phone)
+      } : {}, {
+        e: common_vendor.t(isLoggedIn.value ? points.value : 0),
+        f: common_vendor.o(handleRecharge),
+        g: common_vendor.o(handleHistory),
+        h: common_vendor.t(isLoggedIn.value ? "退出登录" : "登录"),
+        i: common_vendor.o(toggleLogin)
+      });
     };
   }
 };
-const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/miniprogram-1/src/pages/mine/mine.vue"]]);
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/pages/mine/mine.vue"]]);
 wx.createPage(MiniProgramPage);

+ 1 - 1
dist/dev/mp-weixin/pages/mine/mine.wxml

@@ -1 +1 @@
-<view class="page-mine"><view class="user-card"><view class="avatar-container"><image class="avatar" src="{{a}}" mode="aspectFill"></image></view><view class="user-info"><text class="username">{{b}}</text></view></view><view class="section-card"><view class="card-header"><view class="header-icon-box"><text class="header-icon-text">🪙</text></view><text class="header-title">积分值</text></view><view class="card-content points-content"><text class="points-value">积分值:{{c}}</text><view class="points-actions"><button class="mini-btn" bindtap="{{d}}">充值</button><button class="mini-btn" bindtap="{{e}}">积分记录</button></view></view></view><view class="section-card"><view class="card-header"><view class="header-icon-box"><text class="header-icon-text">📝</text></view><text class="header-title">交易记录</text></view><view class="card-content transaction-content"><text class="empty-text">暂无交易记录</text></view></view><view class="action-area"><button class="main-btn" bindtap="{{g}}">{{f}}</button></view></view>
+<view class="page-mine"><view class="user-card"><view class="avatar-container"><image class="avatar" src="{{a}}" mode="aspectFill"></image></view><view class="user-info"><text class="username">{{b}}</text><text wx:if="{{c}}" class="phone">{{d}}</text></view></view><view class="section-card"><view class="card-header"><view class="header-icon-box"><text class="header-icon-text">🪙</text></view><text class="header-title">积分值</text></view><view class="card-content points-content"><text class="points-value">积分值:{{e}}</text><view class="points-actions"><button class="mini-btn" bindtap="{{f}}">充值</button><button class="mini-btn" bindtap="{{g}}">积分记录</button></view></view></view><view class="section-card"><view class="card-header"><view class="header-icon-box"><text class="header-icon-text">📝</text></view><text class="header-title">交易记录</text></view><view class="card-content transaction-content"><text class="empty-text">暂无交易记录</text></view></view><view class="action-area"><button class="main-btn" bindtap="{{i}}">{{h}}</button></view></view>

+ 7 - 0
dist/dev/mp-weixin/pages/mine/mine.wxss

@@ -33,11 +33,18 @@
 .user-info {
   margin-left: 30rpx;
   flex: 1;
+  display: flex;
+  flex-direction: column;
 }
 .username {
   font-size: 36rpx;
   font-weight: bold;
   color: #222222;
+  margin-bottom: 10rpx;
+}
+.phone {
+  font-size: 24rpx;
+  color: #999999;
 }
 
 /* Section Cards */

+ 27 - 1
dist/dev/mp-weixin/pages/pool/pool.js

@@ -1,5 +1,6 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
+const utils_auth = require("../../utils/auth.js");
 const _sfc_main = {
   __name: "pool",
   setup(__props) {
@@ -7,6 +8,12 @@ const _sfc_main = {
     const showModal = common_vendor.ref(false);
     const selectedPlan = common_vendor.ref("daily");
     const selectedDate = common_vendor.ref("2025年11月20日");
+    const isLoggedIn = common_vendor.ref(false);
+    const checkLogin = () => {
+      isLoggedIn.value = utils_auth.isLoggedIn();
+      console.log("超短池 - 登录状态:", isLoggedIn.value);
+      return isLoggedIn.value;
+    };
     const checkPurchaseStatus = () => {
       try {
         const purchaseInfo = common_vendor.index.getStorageSync("pool_purchase");
@@ -28,6 +35,23 @@ const _sfc_main = {
       }
     };
     const showPurchaseModal = () => {
+      console.log("点击立即解锁");
+      if (!checkLogin()) {
+        console.log("未登录,跳转登录页");
+        common_vendor.index.showModal({
+          title: "提示",
+          content: "解锁超短池需要先登录,是否前往登录?",
+          success: (res) => {
+            if (res.confirm) {
+              common_vendor.index.navigateTo({
+                url: "/pages/login/login"
+              });
+            }
+          }
+        });
+        return;
+      }
+      console.log("已登录,显示购买弹窗");
       showModal.value = true;
     };
     const closePurchaseModal = () => {
@@ -70,9 +94,11 @@ const _sfc_main = {
       });
     };
     common_vendor.onLoad(() => {
+      checkLogin();
       checkPurchaseStatus();
     });
     common_vendor.onShow(() => {
+      checkLogin();
       checkPurchaseStatus();
     });
     return (_ctx, _cache) => {
@@ -99,5 +125,5 @@ const _sfc_main = {
     };
   }
 };
-const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/miniprogram-1/src/pages/pool/pool.vue"]]);
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/pages/pool/pool.vue"]]);
 wx.createPage(MiniProgramPage);

+ 2 - 1
dist/dev/mp-weixin/pages/rank/rank.js

@@ -1,6 +1,7 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
 const utils_api = require("../../utils/api.js");
+require("../../utils/auth.js");
 const _sfc_main = {
   __name: "rank",
   setup(__props) {
@@ -83,5 +84,5 @@ const _sfc_main = {
     };
   }
 };
-const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/miniprogram-1/src/pages/rank/rank.vue"]]);
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/pages/rank/rank.vue"]]);
 wx.createPage(MiniProgramPage);

+ 44 - 1
dist/dev/mp-weixin/pages/strong/strong.js

@@ -1,5 +1,6 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
+const utils_auth = require("../../utils/auth.js");
 const _sfc_main = {
   __name: "strong",
   setup(__props) {
@@ -23,6 +24,12 @@ const _sfc_main = {
     const currentStock = common_vendor.ref({});
     const buyQuantity = common_vendor.ref("100");
     const sellQuantity = common_vendor.ref("100");
+    const isLoggedIn = common_vendor.ref(false);
+    const checkLogin = () => {
+      isLoggedIn.value = utils_auth.isLoggedIn();
+      console.log("强势池 - 登录状态:", isLoggedIn.value);
+      return isLoggedIn.value;
+    };
     const buyTotalAmount = common_vendor.computed(() => {
       const qty = parseInt(buyQuantity.value) || 0;
       const price = parseFloat(currentStock.value.price) || 0;
@@ -34,6 +41,23 @@ const _sfc_main = {
       return qty * price;
     });
     const showBuyModal = (stock) => {
+      console.log("点击买入按钮");
+      if (!checkLogin()) {
+        console.log("未登录,提示登录");
+        common_vendor.index.showModal({
+          title: "提示",
+          content: "模拟交易需要先登录,是否前往登录?",
+          success: (res) => {
+            if (res.confirm) {
+              common_vendor.index.navigateTo({
+                url: "/pages/login/login"
+              });
+            }
+          }
+        });
+        return;
+      }
+      console.log("已登录,显示买入弹窗");
       currentStock.value = { ...stock };
       buyQuantity.value = "100";
       showBuyModalFlag.value = true;
@@ -42,6 +66,23 @@ const _sfc_main = {
       showBuyModalFlag.value = false;
     };
     const showSellModal = (stock) => {
+      console.log("点击卖出按钮");
+      if (!checkLogin()) {
+        console.log("未登录,提示登录");
+        common_vendor.index.showModal({
+          title: "提示",
+          content: "模拟交易需要先登录,是否前往登录?",
+          success: (res) => {
+            if (res.confirm) {
+              common_vendor.index.navigateTo({
+                url: "/pages/login/login"
+              });
+            }
+          }
+        });
+        return;
+      }
+      console.log("已登录,显示卖出弹窗");
       currentStock.value = { ...stock };
       sellQuantity.value = "100";
       showSellModalFlag.value = true;
@@ -142,8 +183,10 @@ const _sfc_main = {
       });
     };
     common_vendor.onLoad(() => {
+      checkLogin();
     });
     common_vendor.onShow(() => {
+      checkLogin();
     });
     return (_ctx, _cache) => {
       return common_vendor.e({
@@ -193,5 +236,5 @@ const _sfc_main = {
     };
   }
 };
-const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/miniprogram-1/src/pages/strong/strong.vue"]]);
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/pages/strong/strong.vue"]]);
 wx.createPage(MiniProgramPage);

+ 77 - 59
dist/dev/mp-weixin/utils/api.js

@@ -1,19 +1,34 @@
 "use strict";
 const common_vendor = require("../common/vendor.js");
+const utils_auth = require("./auth.js");
 const BASE_URL = "http://localhost:8080";
-const getSuggestions = (keyword) => {
+const request = (options) => {
   return new Promise((resolve, reject) => {
+    const token = utils_auth.getToken();
+    const header = options.header || {};
+    if (token) {
+      header["Authorization"] = `Bearer ${token}`;
+    }
     common_vendor.index.request({
-      url: `${BASE_URL}/v1/stock/suggestion`,
-      method: "GET",
-      data: {
-        keyword
-      },
+      url: `${BASE_URL}${options.url}`,
+      method: options.method || "GET",
+      data: options.data || {},
+      header,
       success: (res) => {
-        if (res.statusCode === 200 && res.data) {
+        if (res.statusCode === 200) {
           resolve(res.data);
+        } else if (res.statusCode === 401) {
+          common_vendor.index.showToast({
+            title: "登录已过期,请重新登录",
+            icon: "none"
+          });
+          common_vendor.index.removeStorageSync("user_token");
+          common_vendor.index.navigateTo({
+            url: "/pages/login/login"
+          });
+          reject(new Error("未授权"));
         } else {
-          reject(new Error("服务暂不可用"));
+          reject(new Error(res.data.message || "服务暂不可用"));
         }
       },
       fail: (err) => {
@@ -22,67 +37,70 @@ const getSuggestions = (keyword) => {
     });
   });
 };
+const wxLogin = (code) => {
+  return request({
+    url: "/v1/auth/wxLogin",
+    method: "POST",
+    header: {
+      "content-type": "application/json"
+    },
+    data: { code }
+  });
+};
+const phoneLogin = (phone, verifyCode) => {
+  console.log("phoneLogin调用参数:", { phone, verifyCode });
+  return request({
+    url: "/v1/auth/phoneLogin",
+    method: "POST",
+    header: {
+      "content-type": "application/json"
+    },
+    data: { phone, verifyCode }
+  });
+};
+const sendSmsCode = (phone) => {
+  return request({
+    url: "/v1/auth/sendCode",
+    method: "POST",
+    header: {
+      "content-type": "application/json"
+    },
+    data: { phone }
+  });
+};
+const getSuggestions = (keyword) => {
+  return request({
+    url: "/v1/stock/suggestion",
+    method: "GET",
+    data: { keyword }
+  });
+};
 const searchStocks = (keyword) => {
-  return new Promise((resolve, reject) => {
-    common_vendor.index.request({
-      url: `${BASE_URL}/v1/stock/search`,
-      method: "POST",
-      header: {
-        "content-type": "application/json"
-      },
-      data: {
-        keyword
-      },
-      success: (res) => {
-        if (res.statusCode === 200 && res.data) {
-          resolve(res.data);
-        } else {
-          reject(new Error("服务暂不可用"));
-        }
-      },
-      fail: (err) => {
-        reject(new Error("网络异常"));
-      }
-    });
+  return request({
+    url: "/v1/stock/search",
+    method: "POST",
+    header: {
+      "content-type": "application/json"
+    },
+    data: { keyword }
   });
 };
 const getUserPortfolio = () => {
-  return new Promise((resolve, reject) => {
-    common_vendor.index.request({
-      url: `${BASE_URL}/v1/user/portfolio`,
-      method: "GET",
-      success: (res) => {
-        if (res.statusCode === 200 && res.data) {
-          resolve(res.data);
-        } else {
-          reject(new Error("服务暂不可用"));
-        }
-      },
-      fail: (err) => {
-        reject(new Error("网络异常"));
-      }
-    });
+  return request({
+    url: "/v1/user/portfolio",
+    method: "GET"
   });
 };
 const getLeaderboard = () => {
-  return new Promise((resolve, reject) => {
-    common_vendor.index.request({
-      url: `${BASE_URL}/v1/rank/leaderboard`,
-      method: "GET",
-      success: (res) => {
-        if (res.statusCode === 200 && res.data) {
-          resolve(res.data);
-        } else {
-          reject(new Error("服务暂不可用"));
-        }
-      },
-      fail: (err) => {
-        reject(new Error("网络异常"));
-      }
-    });
+  return request({
+    url: "/v1/rank/leaderboard",
+    method: "GET"
   });
 };
 exports.getLeaderboard = getLeaderboard;
 exports.getSuggestions = getSuggestions;
 exports.getUserPortfolio = getUserPortfolio;
+exports.phoneLogin = phoneLogin;
 exports.searchStocks = searchStocks;
+exports.sendSmsCode = sendSmsCode;
+exports.wxLogin = wxLogin;

+ 59 - 0
dist/dev/mp-weixin/utils/auth.js

@@ -0,0 +1,59 @@
+"use strict";
+const common_vendor = require("../common/vendor.js");
+const TOKEN_KEY = "user_token";
+const USER_INFO_KEY = "user_info";
+const setToken = (token) => {
+  common_vendor.index.setStorageSync(TOKEN_KEY, token);
+};
+const getToken = () => {
+  return common_vendor.index.getStorageSync(TOKEN_KEY) || null;
+};
+const removeToken = () => {
+  common_vendor.index.removeStorageSync(TOKEN_KEY);
+};
+const setUserInfo = (userInfo) => {
+  common_vendor.index.setStorageSync(USER_INFO_KEY, JSON.stringify(userInfo));
+};
+const getUserInfo = () => {
+  const userInfo = common_vendor.index.getStorageSync(USER_INFO_KEY);
+  return userInfo ? JSON.parse(userInfo) : null;
+};
+const removeUserInfo = () => {
+  common_vendor.index.removeStorageSync(USER_INFO_KEY);
+};
+const isLoggedIn = () => {
+  return !!getToken();
+};
+const logout = () => {
+  removeToken();
+  removeUserInfo();
+};
+const checkLogin = (callback) => {
+  if (isLoggedIn()) {
+    return true;
+  }
+  common_vendor.index.showModal({
+    title: "提示",
+    content: "此操作需要登录,是否前往登录?",
+    success: (res) => {
+      if (res.confirm) {
+        common_vendor.index.navigateTo({
+          url: "/pages/login/login",
+          success: () => {
+            if (callback) {
+              getApp().globalData.loginCallback = callback;
+            }
+          }
+        });
+      }
+    }
+  });
+  return false;
+};
+exports.checkLogin = checkLogin;
+exports.getToken = getToken;
+exports.getUserInfo = getUserInfo;
+exports.isLoggedIn = isLoggedIn;
+exports.logout = logout;
+exports.setToken = setToken;
+exports.setUserInfo = setUserInfo;

+ 1 - 1
project.config.json

@@ -1,7 +1,7 @@
 {
   "miniprogramRoot": "dist/dev/mp-weixin/",
   "compileType": "miniprogram",
-  "libVersion": "trial",
+  "libVersion": "3.11.3",
   "packOptions": {
     "ignore": [],
     "include": []

+ 2 - 1
project.private.config.json

@@ -19,5 +19,6 @@
     "checkInvalidKey": true,
     "ignoreDevUnusedFiles": true
   },
-  "libVersion": "3.11.3"
+  "libVersion": "3.11.3",
+  "condition": {}
 }

+ 18 - 10
src/pages.json

@@ -29,6 +29,14 @@
       "style": {
         "navigationBarTitleText": "个人中心"
       }
+    },
+    {
+      "path": "pages/login/login",
+      "style": {
+        "navigationBarTitleText": "登录",
+        "navigationBarBackgroundColor": "#667eea",
+        "navigationBarTextStyle": "white"
+      }
     }
   ],
   "globalStyle": {
@@ -45,32 +53,32 @@
       {
         "pagePath": "pages/index/index",
         "text": "打分查询",
-        "iconPath": "static/images/tab_search.png",
-        "selectedIconPath": "static/images/tab_search_active.png"
+        "iconPath": "/static/images/tab_search.png",
+        "selectedIconPath": "/static/images/tab_search_active.png"
       },
       {
         "pagePath": "pages/pool/pool",
         "text": "超短池",
-        "iconPath": "static/images/tab_short.png",
-        "selectedIconPath": "static/images/tab_short_active.png"
+        "iconPath": "/static/images/tab_short.png",
+        "selectedIconPath": "/static/images/tab_short_active.png"
       },
       {
         "pagePath": "pages/strong/strong",
         "text": "强势池",
-        "iconPath": "static/images/tab_strong.png",
-        "selectedIconPath": "static/images/tab_strong_active.png"
+        "iconPath": "/static/images/tab_strong.png",
+        "selectedIconPath": "/static/images/tab_strong_active.png"
       },
       {
         "pagePath": "pages/rank/rank",
         "text": "模拟排名",
-        "iconPath": "static/images/tab_rank.png",
-        "selectedIconPath": "static/images/tab_rank_active.png"
+        "iconPath": "/static/images/tab_rank.png",
+        "selectedIconPath": "/static/images/tab_rank_active.png"
       },
       {
         "pagePath": "pages/mine/mine",
         "text": "个人中心",
-        "iconPath": "static/images/tab_mine.png",
-        "selectedIconPath": "static/images/tab_mine_active.png"
+        "iconPath": "/static/images/tab_mine.png",
+        "selectedIconPath": "/static/images/tab_mine_active.png"
       }
     ]
   }

+ 63 - 6
src/pages/index/index.vue

@@ -23,7 +23,8 @@
               @confirm="onSearch"
               @blur="onInputBlur"
             />
-            <view class="search-button" @click="onSearch">
+            <!-- 搜索按钮(统一处理登录检查) -->
+            <view class="search-button" @click="handleSearchClick">
               <text class="icon-search"></text>
             </view>
           </view>
@@ -42,7 +43,7 @@
             </view>
           </view>
 
-          <text class="search-tip">支持A股代码或名称模糊查询</text>
+          <text class="search-tip">支持A股代码或名称模糊查询{{ isLoggedIn ? '' : '(需登录)' }}</text>
         </view>
 
         <!-- 查询结果 -->
@@ -128,8 +129,12 @@
 </template>
 
 <script setup>
-import { ref } from 'vue'
+import { ref, onMounted } from 'vue'
 import { getSuggestions, searchStocks } from '../../utils/api.js'
+import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
+
+// 引入 onShow 生命周期
+import { onShow } from '@dcloudio/uni-app'
 
 const keyword = ref('')
 const loading = ref(false)
@@ -138,8 +143,57 @@ const errorMsg = ref('')
 const result = ref(null)
 const suggestions = ref([])
 const showDropdown = ref(false)
+const isLoggedIn = ref(false)
 let timer = null
 
+/**
+ * 页面加载时检查登录状态
+ */
+onMounted(() => {
+  isLoggedIn.value = checkLoginStatus()
+  console.log('=== 页面加载 ===')
+  console.log('登录状态:', isLoggedIn.value)
+  console.log('Token:', uni.getStorageSync('user_token'))
+})
+
+/**
+ * 页面显示时检查登录状态(从登录页返回时会触发)
+ */
+onShow(() => {
+  isLoggedIn.value = checkLoginStatus()
+  console.log('=== 页面显示 ===')
+  console.log('登录状态:', isLoggedIn.value)
+})
+
+/**
+ * 处理搜索按钮点击(统一登录校验UI)
+ */
+const handleSearchClick = () => {
+  console.log('=== 点击搜索按钮 ===')
+  console.log('当前登录状态:', isLoggedIn.value)
+  
+  // 检查登录状态
+  if (!isLoggedIn.value) {
+    console.log('未登录,显示登录提示')
+    uni.showModal({
+      title: '提示',
+      content: '查询股票信息需要先登录,是否前往登录?',
+      success: (res) => {
+        if (res.confirm) {
+          uni.navigateTo({
+            url: '/pages/login/login'
+          })
+        }
+      }
+    })
+    return
+  }
+  
+  console.log('已登录,执行搜索')
+  // 已登录,执行搜索
+  onSearch()
+}
+
 const onKeywordChange = (e) => {
   const value = e.detail.value
   keyword.value = value
@@ -162,9 +216,12 @@ const doSearchSuggestions = async (kw) => {
   }
 
   try {
-    // getSuggestions 已经通过 request 函数处理,返回的是 data 部分(数组)
-    const list = await getSuggestions(kw.trim())
-    console.log('模糊查询返回数据:', list)
+    // getSuggestions 返回完整响应对象 {code, message, data}
+    const response = await getSuggestions(kw.trim())
+    console.log('模糊查询返回数据:', response)
+    
+    // 从响应中提取 data 数组
+    const list = response.data || []
     suggestions.value = Array.isArray(list) ? list : []
     showDropdown.value = suggestions.value.length > 0
     console.log('下拉框状态:', { showDropdown: showDropdown.value, suggestionsLength: suggestions.value.length })

+ 462 - 0
src/pages/login/login.vue

@@ -0,0 +1,462 @@
+<template>
+  <view class="login-container">
+    <view class="login-header">
+      <text class="app-name">量化选股大师</text>
+      <text class="welcome-text">欢迎登录</text>
+    </view>
+
+    <view class="login-form">
+      <!-- 微信一键登录 -->
+      <button 
+        class="wx-login-btn" 
+        open-type="getPhoneNumber" 
+        @getphonenumber="handleWxLogin"
+        v-if="canUseWxLogin"
+      >
+        <image class="wx-icon" src="/static/images/wechat.png" mode="aspectFit"></image>
+        <text>微信一键登录</text>
+      </button>
+
+      <view class="divider">
+        <view class="divider-line"></view>
+        <text class="divider-text">或</text>
+        <view class="divider-line"></view>
+      </view>
+
+      <!-- 手机号登录 -->
+      <view class="phone-login">
+        <view class="input-group">
+          <input 
+            class="input-field" 
+            type="number" 
+            v-model="phone" 
+            placeholder="请输入手机号"
+            maxlength="11"
+          />
+        </view>
+
+        <view class="input-group code-group">
+          <input 
+            class="input-field code-input" 
+            type="number" 
+            v-model="code" 
+            placeholder="请输入验证码"
+            maxlength="6"
+          />
+          <button 
+            class="code-btn" 
+            @click="sendCode" 
+            :disabled="countdown > 0"
+          >
+            {{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
+          </button>
+        </view>
+
+        <button class="login-btn" @click="handlePhoneLogin">登录</button>
+      </view>
+
+      <view class="agreement">
+        <checkbox-group @change="handleAgreeChange">
+          <label>
+            <checkbox :checked="agreed" color="#5d55e8" />
+            <text class="agreement-text">我已阅读并同意</text>
+            <text class="agreement-link">《用户协议》</text>
+            <text class="agreement-text">和</text>
+            <text class="agreement-link">《隐私政策》</text>
+          </label>
+        </checkbox-group>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { wxLogin, phoneLogin, sendSmsCode } from '@/utils/api.js'
+import { setToken, setUserInfo } from '@/utils/auth.js'
+
+export default {
+  data() {
+    return {
+      phone: '',
+      code: '',
+      countdown: 0,
+      agreed: false,
+      canUseWxLogin: true, // 是否支持微信登录
+      timer: null
+    }
+  },
+
+  onLoad() {
+    // 检查是否在微信环境
+    // #ifdef MP-WEIXIN
+    this.canUseWxLogin = true
+    // #endif
+    
+    // #ifndef MP-WEIXIN
+    this.canUseWxLogin = false
+    // #endif
+  },
+
+  onUnload() {
+    // 清除定时器
+    if (this.timer) {
+      clearInterval(this.timer)
+    }
+  },
+
+  methods: {
+    /**
+     * 处理微信一键登录
+     * @param {object} e - 事件对象,包含加密的手机号信息
+     */
+    async handleWxLogin(e) {
+      if (!this.agreed) {
+        uni.showToast({
+          title: '请先同意用户协议和隐私政策',
+          icon: 'none'
+        })
+        return
+      }
+
+      if (e.detail.errMsg === 'getPhoneNumber:ok') {
+        try {
+          uni.showLoading({ title: '登录中...' })
+          
+          // 获取微信登录code
+          const loginRes = await uni.login()
+          
+          // 调用后端登录接口
+          const result = await wxLogin(loginRes.code)
+          
+          // 保存token和用户信息
+          setToken(result.data.token)
+          setUserInfo(result.data.userInfo)
+          
+          uni.hideLoading()
+          uni.showToast({
+            title: '登录成功',
+            icon: 'success'
+          })
+          
+          // 延迟跳转,让用户看到成功提示
+          setTimeout(() => {
+            this.navigateBack()
+          }, 1500)
+          
+        } catch (error) {
+          uni.hideLoading()
+          uni.showToast({
+            title: error.message || '登录失败',
+            icon: 'none'
+          })
+        }
+      } else {
+        uni.showToast({
+          title: '获取手机号失败',
+          icon: 'none'
+        })
+      }
+    },
+
+    /**
+     * 发送验证码
+     */
+    async sendCode() {
+      if (!this.phone) {
+        uni.showToast({
+          title: '请输入手机号',
+          icon: 'none'
+        })
+        return
+      }
+
+      if (!/^1[3-9]\d{9}$/.test(this.phone)) {
+        uni.showToast({
+          title: '请输入正确的手机号',
+          icon: 'none'
+        })
+        return
+      }
+
+      if (!this.agreed) {
+        uni.showToast({
+          title: '请先同意用户协议和隐私政策',
+          icon: 'none'
+        })
+        return
+      }
+
+      try {
+        uni.showLoading({ title: '发送中...' })
+        await sendSmsCode(this.phone)
+        uni.hideLoading()
+        
+        uni.showToast({
+          title: '验证码已发送',
+          icon: 'success'
+        })
+        
+        // 开始倒计时
+        this.countdown = 60
+        this.timer = setInterval(() => {
+          this.countdown--
+          if (this.countdown <= 0) {
+            clearInterval(this.timer)
+          }
+        }, 1000)
+        
+      } catch (error) {
+        uni.hideLoading()
+        uni.showToast({
+          title: error.message || '发送失败',
+          icon: 'none'
+        })
+      }
+    },
+
+    /**
+     * 处理手机号登录
+     */
+    async handlePhoneLogin() {
+      if (!this.phone) {
+        uni.showToast({
+          title: '请输入手机号',
+          icon: 'none'
+        })
+        return
+      }
+
+      if (!/^1[3-9]\d{9}$/.test(this.phone)) {
+        uni.showToast({
+          title: '请输入正确的手机号',
+          icon: 'none'
+        })
+        return
+      }
+
+      if (!this.code) {
+        uni.showToast({
+          title: '请输入验证码',
+          icon: 'none'
+        })
+        return
+      }
+
+      if (!this.agreed) {
+        uni.showToast({
+          title: '请先同意用户协议和隐私政策',
+          icon: 'none'
+        })
+        return
+      }
+
+      try {
+        uni.showLoading({ title: '登录中...' })
+        
+        console.log('准备登录,phone:', this.phone, 'code:', this.code)
+        
+        // 调用后端登录接口(注意:phoneLogin的第二个参数是verifyCode)
+        const result = await phoneLogin(this.phone, this.code)
+        
+        console.log('登录成功,result:', result)
+        
+        // 保存token和用户信息
+        setToken(result.data.token)
+        setUserInfo(result.data.userInfo)
+        setUserInfo(result.data.userInfo)
+        
+        uni.hideLoading()
+        uni.showToast({
+          title: '登录成功',
+          icon: 'success'
+        })
+        
+        // 延迟跳转
+        setTimeout(() => {
+          this.navigateBack()
+        }, 1500)
+        
+      } catch (error) {
+        uni.hideLoading()
+        uni.showToast({
+          title: error.message || '登录失败',
+          icon: 'none'
+        })
+      }
+    },
+
+    /**
+     * 处理协议勾选变化
+     */
+    handleAgreeChange(e) {
+      this.agreed = e.detail.value.length > 0
+    },
+
+    /**
+     * 返回上一页或首页
+     */
+    navigateBack() {
+      const pages = getCurrentPages()
+      if (pages.length > 1) {
+        // 有上一页,返回上一页
+        uni.navigateBack()
+        
+        // 执行登录后的回调
+        const app = getApp()
+        if (app.globalData.loginCallback) {
+          app.globalData.loginCallback()
+          app.globalData.loginCallback = null
+        }
+      } else {
+        // 没有上一页,跳转到首页
+        uni.switchTab({
+          url: '/pages/index/index'
+        })
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.login-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  padding: 100rpx 60rpx;
+}
+
+.login-header {
+  text-align: center;
+  margin-bottom: 100rpx;
+}
+
+.app-name {
+  display: block;
+  font-size: 48rpx;
+  font-weight: bold;
+  color: #ffffff;
+  margin-bottom: 20rpx;
+}
+
+.welcome-text {
+  display: block;
+  font-size: 28rpx;
+  color: rgba(255, 255, 255, 0.8);
+}
+
+.login-form {
+  background: #ffffff;
+  border-radius: 20rpx;
+  padding: 60rpx 40rpx;
+  box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.1);
+}
+
+.wx-login-btn {
+  width: 100%;
+  height: 90rpx;
+  background: #07c160;
+  color: #ffffff;
+  border-radius: 45rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 32rpx;
+  border: none;
+  margin-bottom: 40rpx;
+}
+
+.wx-icon {
+  width: 40rpx;
+  height: 40rpx;
+  margin-right: 15rpx;
+}
+
+.divider {
+  display: flex;
+  align-items: center;
+  margin: 40rpx 0;
+}
+
+.divider-line {
+  flex: 1;
+  height: 1rpx;
+  background: #e5e5e5;
+}
+
+.divider-text {
+  padding: 0 20rpx;
+  color: #999999;
+  font-size: 24rpx;
+}
+
+.phone-login {
+  margin-top: 40rpx;
+}
+
+.input-group {
+  margin-bottom: 30rpx;
+}
+
+.input-field {
+  width: 100%;
+  height: 90rpx;
+  background: #f5f6fb;
+  border-radius: 45rpx;
+  padding: 0 30rpx;
+  font-size: 28rpx;
+  box-sizing: border-box;
+}
+
+.code-group {
+  display: flex;
+  align-items: center;
+}
+
+.code-input {
+  flex: 1;
+  margin-right: 20rpx;
+}
+
+.code-btn {
+  width: 200rpx;
+  height: 90rpx;
+  background: #5d55e8;
+  color: #ffffff;
+  border-radius: 45rpx;
+  font-size: 24rpx;
+  border: none;
+  padding: 0;
+  line-height: 90rpx;
+}
+
+.code-btn[disabled] {
+  background: #cccccc;
+}
+
+.login-btn {
+  width: 100%;
+  height: 90rpx;
+  background: #5d55e8;
+  color: #ffffff;
+  border-radius: 45rpx;
+  font-size: 32rpx;
+  border: none;
+  margin-top: 20rpx;
+}
+
+.agreement {
+  margin-top: 40rpx;
+  text-align: center;
+}
+
+.agreement-text {
+  font-size: 24rpx;
+  color: #999999;
+  margin: 0 5rpx;
+}
+
+.agreement-link {
+  font-size: 24rpx;
+  color: #5d55e8;
+}
+</style>

+ 74 - 22
src/pages/mine/mine.vue

@@ -6,12 +6,13 @@
         <!-- Default avatar if not logged in or no avatar -->
         <image 
           class="avatar" 
-          :src="isLoggedIn && userInfo.avatarUrl ? userInfo.avatarUrl : '/static/images/tab_mine_active.png'" 
+          :src="isLoggedIn && userInfo.avatar ? userInfo.avatar : '/static/images/tab_mine_active.png'" 
           mode="aspectFill"
         ></image>
       </view>
       <view class="user-info">
-        <text class="username">{{ isLoggedIn ? (userInfo.nickName || '微信用户') : '游客' }}</text>
+        <text class="username">{{ isLoggedIn ? (userInfo.nickname || '微信用户') : '游客' }}</text>
+        <text class="phone" v-if="isLoggedIn && userInfo.phone">{{ userInfo.phone }}</text>
       </view>
     </view>
 
@@ -56,47 +57,90 @@
 </template>
 
 <script setup>
-import { ref } from 'vue'
+import { ref, onMounted } from 'vue'
+import { isLoggedIn as checkLogin, getUserInfo as getStoredUserInfo, logout, checkLogin as requireLogin } from '@/utils/auth.js'
 
 const isLoggedIn = ref(false)
 const userInfo = ref({
-  nickName: '',
-  avatarUrl: ''
+  nickname: '',
+  avatar: '',
+  phone: ''
 })
 const points = ref(900)
 
+/**
+ * 页面加载时检查登录状态
+ */
+onMounted(() => {
+  loadUserInfo()
+})
+
+/**
+ * 加载用户信息
+ */
+const loadUserInfo = () => {
+  isLoggedIn.value = checkLogin()
+  if (isLoggedIn.value) {
+    const storedInfo = getStoredUserInfo()
+    if (storedInfo) {
+      userInfo.value = storedInfo
+    }
+  }
+}
+
+/**
+ * 切换登录状态
+ */
 const toggleLogin = () => {
   if (isLoggedIn.value) {
-    // Logout
-    isLoggedIn.value = false
-    userInfo.value = { nickName: '', avatarUrl: '' }
-    uni.showToast({ title: '已退出登录', icon: 'none' })
+    // 退出登录
+    uni.showModal({
+      title: '提示',
+      content: '确定要退出登录吗?',
+      success: (res) => {
+        if (res.confirm) {
+          logout()
+          isLoggedIn.value = false
+          userInfo.value = { nickname: '', avatar: '', phone: '' }
+          uni.showToast({ title: '已退出登录', icon: 'none' })
+        }
+      }
+    })
   } else {
-    // Login Simulation
-    // In a real app, use uni.getUserProfile or similar
-    isLoggedIn.value = true
-    userInfo.value = {
-      nickName: '测试用户', 
-      // Using a placeholder or one of the existing images as a mock avatar
-      avatarUrl: '/static/images/tab_mine_active.png' 
-    }
-    uni.showToast({ title: '登录成功', icon: 'success' })
+    // 跳转到登录页
+    uni.navigateTo({
+      url: '/pages/login/login'
+    })
   }
 }
 
+/**
+ * 处理充值按钮点击
+ * 需要登录才能操作
+ */
 const handleRecharge = () => {
-  if (!isLoggedIn.value) {
-    uni.showToast({ title: '请先登录', icon: 'none' })
+  if (!requireLogin(() => {
+    // 登录成功后的回调
+    handleRecharge()
+  })) {
     return
   }
+  
   uni.showToast({ title: '充值功能开发中', icon: 'none' })
 }
 
+/**
+ * 处理积分记录按钮点击
+ * 需要登录才能操作
+ */
 const handleHistory = () => {
-  if (!isLoggedIn.value) {
-    uni.showToast({ title: '请先登录', icon: 'none' })
+  if (!requireLogin(() => {
+    // 登录成功后的回调
+    handleHistory()
+  })) {
     return
   }
+  
   uni.showToast({ title: '记录功能开发中', icon: 'none' })
 }
 </script>
@@ -139,12 +183,20 @@ const handleHistory = () => {
 .user-info {
   margin-left: 30rpx;
   flex: 1;
+  display: flex;
+  flex-direction: column;
 }
 
 .username {
   font-size: 36rpx;
   font-weight: bold;
   color: #222222;
+  margin-bottom: 10rpx;
+}
+
+.phone {
+  font-size: 24rpx;
+  color: #999999;
 }
 
 /* Section Cards */

+ 33 - 2
src/pages/pool/pool.vue

@@ -126,11 +126,20 @@
 <script setup>
 import { ref } from 'vue'
 import { onLoad, onShow } from '@dcloudio/uni-app'
+import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
 
 const isPurchased = ref(false)
 const showModal = ref(false)
 const selectedPlan = ref('daily')
 const selectedDate = ref('2025年11月20日')
+const isLoggedIn = ref(false)
+
+// 检查登录状态
+const checkLogin = () => {
+  isLoggedIn.value = checkLoginStatus()
+  console.log('超短池 - 登录状态:', isLoggedIn.value)
+  return isLoggedIn.value
+}
 
 // 检查购买状态
 const checkPurchaseStatus = () => {
@@ -155,8 +164,28 @@ const checkPurchaseStatus = () => {
   }
 }
 
-// 显示购买弹窗
+// 显示购买弹窗(需要登录)
 const showPurchaseModal = () => {
+  console.log('点击立即解锁')
+  
+  // 检查登录状态
+  if (!checkLogin()) {
+    console.log('未登录,跳转登录页')
+    uni.showModal({
+      title: '提示',
+      content: '解锁超短池需要先登录,是否前往登录?',
+      success: (res) => {
+        if (res.confirm) {
+          uni.navigateTo({
+            url: '/pages/login/login'
+          })
+        }
+      }
+    })
+    return
+  }
+  
+  console.log('已登录,显示购买弹窗')
   showModal.value = true
 }
 
@@ -220,11 +249,13 @@ const onHistorySearch = () => {
 
 // 使用uni-app生命周期钩子
 onLoad(() => {
+  checkLogin()
   checkPurchaseStatus()
 })
 
 onShow(() => {
-  // 每次页面显示时都检查一次购买状态
+  // 每次页面显示时都检查登录状态和购买状态
+  checkLogin()
   checkPurchaseStatus()
 })
 </script>

+ 55 - 4
src/pages/strong/strong.vue

@@ -178,6 +178,7 @@
 <script setup>
 import { ref, computed } from 'vue'
 import { onLoad, onShow } from '@dcloudio/uni-app'
+import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
 
 // 股票列表数据
 const stockList = ref([
@@ -201,6 +202,14 @@ const showSellModalFlag = ref(false)
 const currentStock = ref({})
 const buyQuantity = ref('100')
 const sellQuantity = ref('100')
+const isLoggedIn = ref(false)
+
+// 检查登录状态
+const checkLogin = () => {
+  isLoggedIn.value = checkLoginStatus()
+  console.log('强势池 - 登录状态:', isLoggedIn.value)
+  return isLoggedIn.value
+}
 
 // 计算买入总金额
 const buyTotalAmount = computed(() => {
@@ -216,8 +225,28 @@ const sellTotalAmount = computed(() => {
   return qty * price
 })
 
-// 显示买入弹窗
+// 显示买入弹窗(需要登录)
 const showBuyModal = (stock) => {
+  console.log('点击买入按钮')
+  
+  // 检查登录状态
+  if (!checkLogin()) {
+    console.log('未登录,提示登录')
+    uni.showModal({
+      title: '提示',
+      content: '模拟交易需要先登录,是否前往登录?',
+      success: (res) => {
+        if (res.confirm) {
+          uni.navigateTo({
+            url: '/pages/login/login'
+          })
+        }
+      }
+    })
+    return
+  }
+  
+  console.log('已登录,显示买入弹窗')
   currentStock.value = { ...stock }
   buyQuantity.value = '100'
   showBuyModalFlag.value = true
@@ -228,8 +257,28 @@ const closeBuyModal = () => {
   showBuyModalFlag.value = false
 }
 
-// 显示卖出弹窗
+// 显示卖出弹窗(需要登录)
 const showSellModal = (stock) => {
+  console.log('点击卖出按钮')
+  
+  // 检查登录状态
+  if (!checkLogin()) {
+    console.log('未登录,提示登录')
+    uni.showModal({
+      title: '提示',
+      content: '模拟交易需要先登录,是否前往登录?',
+      success: (res) => {
+        if (res.confirm) {
+          uni.navigateTo({
+            url: '/pages/login/login'
+          })
+        }
+      }
+    })
+    return
+  }
+  
+  console.log('已登录,显示卖出弹窗')
   currentStock.value = { ...stock }
   sellQuantity.value = '100'
   showSellModalFlag.value = true
@@ -363,11 +412,13 @@ const onHistorySearch = () => {
 }
 
 onLoad(() => {
-  // 页面加载
+  // 页面加载时检查登录状态
+  checkLogin()
 })
 
 onShow(() => {
-  // 页面显示
+  // 页面显示时检查登录状态(从登录页返回时会触发)
+  checkLogin()
 })
 </script>
 

+ 135 - 63
src/utils/api.js

@@ -1,19 +1,45 @@
+import { getToken } from './auth.js'
+
 const BASE_URL = 'http://localhost:8080'
 
-// 模糊搜索(联想建议)
-export const getSuggestions = (keyword) => {
+/**
+ * 统一请求封装
+ * 自动添加token到请求头
+ * @param {object} options - 请求配置对象
+ * @returns {Promise} 请求Promise
+ */
+const request = (options) => {
   return new Promise((resolve, reject) => {
+    // 获取token并添加到请求头
+    const token = getToken()
+    const header = options.header || {}
+    if (token) {
+      header['Authorization'] = `Bearer ${token}`
+    }
+    
     uni.request({
-      url: `${BASE_URL}/v1/stock/suggestion`,
-      method: 'GET',
-      data: {
-        keyword: keyword
-      },
+      url: `${BASE_URL}${options.url}`,
+      method: options.method || 'GET',
+      data: options.data || {},
+      header: header,
       success: (res) => {
-        if (res.statusCode === 200 && res.data) {
+        // 统一处理响应
+        if (res.statusCode === 200) {
           resolve(res.data)
+        } else if (res.statusCode === 401) {
+          // token过期或未登录
+          uni.showToast({
+            title: '登录已过期,请重新登录',
+            icon: 'none'
+          })
+          // 清除token并跳转到登录页
+          uni.removeStorageSync('user_token')
+          uni.navigateTo({
+            url: '/pages/login/login'
+          })
+          reject(new Error('未授权'))
         } else {
-          reject(new Error('服务暂不可用'))
+          reject(new Error(res.data.message || '服务暂不可用'))
         }
       },
       fail: (err) => {
@@ -23,68 +49,114 @@ export const getSuggestions = (keyword) => {
   })
 }
 
-// 股票详情查询
+/**
+ * 微信登录接口
+ * @param {string} code - 微信登录code
+ * @returns {Promise} 返回登录结果,包含token和用户信息
+ */
+export const wxLogin = (code) => {
+  return request({
+    url: '/v1/auth/wxLogin',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: { code }
+  })
+}
+
+/**
+ * 手机号登录接口
+ * @param {string} phone - 手机号
+ * @param {string} verifyCode - 验证码
+ * @returns {Promise} 返回登录结果,包含token和用户信息
+ */
+export const phoneLogin = (phone, verifyCode) => {
+  console.log('phoneLogin调用参数:', { phone, verifyCode })
+  return request({
+    url: '/v1/auth/phoneLogin',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: { phone, verifyCode }
+  })
+}
+
+/**
+ * 发送验证码接口
+ * @param {string} phone - 手机号
+ * @returns {Promise} 返回发送结果
+ */
+export const sendSmsCode = (phone) => {
+  return request({
+    url: '/v1/auth/sendCode',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: { phone }
+  })
+}
+
+/**
+ * 获取用户信息接口
+ * @returns {Promise} 返回用户信息
+ */
+export const getUserInfo = () => {
+  return request({
+    url: '/v1/user/info',
+    method: 'GET'
+  })
+}
+
+/**
+ * 模糊搜索(联想建议)
+ * @param {string} keyword - 搜索关键词
+ * @returns {Promise} 返回搜索建议列表
+ */
+export const getSuggestions = (keyword) => {
+  return request({
+    url: '/v1/stock/suggestion',
+    method: 'GET',
+    data: { keyword }
+  })
+}
+
+/**
+ * 股票详情查询
+ * @param {string} keyword - 股票代码或名称
+ * @returns {Promise} 返回股票详情信息
+ */
 export const searchStocks = (keyword) => {
-  return new Promise((resolve, reject) => {
-    uni.request({
-      url: `${BASE_URL}/v1/stock/search`,
-      method: 'POST',
-      header: {
-        'content-type': 'application/json'
-      },
-      data: {
-        keyword: keyword
-      },
-      success: (res) => {
-        if (res.statusCode === 200 && res.data) {
-          resolve(res.data)
-        } else {
-          reject(new Error('服务暂不可用'))
-        }
-      },
-      fail: (err) => {
-        reject(new Error('网络异常'))
-      }
-    })
+  return request({
+    url: '/v1/stock/search',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: { keyword }
   })
 }
 
-// 获取用户模拟资产
+/**
+ * 获取用户模拟资产(需要登录)
+ * @returns {Promise} 返回用户资产信息
+ */
 export const getUserPortfolio = () => {
-  return new Promise((resolve, reject) => {
-    uni.request({
-      url: `${BASE_URL}/v1/user/portfolio`,
-      method: 'GET',
-      success: (res) => {
-        if (res.statusCode === 200 && res.data) {
-          resolve(res.data)
-        } else {
-          reject(new Error('服务暂不可用'))
-        }
-      },
-      fail: (err) => {
-        reject(new Error('网络异常'))
-      }
-    })
+  return request({
+    url: '/v1/user/portfolio',
+    method: 'GET'
   })
 }
 
-// 获取模拟交易排行榜
+/**
+ * 获取模拟交易排行榜
+ * @returns {Promise} 返回排行榜数据
+ */
 export const getLeaderboard = () => {
-  return new Promise((resolve, reject) => {
-    uni.request({
-      url: `${BASE_URL}/v1/rank/leaderboard`,
-      method: 'GET',
-      success: (res) => {
-        if (res.statusCode === 200 && res.data) {
-          resolve(res.data)
-        } else {
-          reject(new Error('服务暂不可用'))
-        }
-      },
-      fail: (err) => {
-        reject(new Error('网络异常'))
-      }
-    })
+  return request({
+    url: '/v1/rank/leaderboard',
+    method: 'GET'
   })
 }

+ 102 - 0
src/utils/auth.js

@@ -0,0 +1,102 @@
+/**
+ * 认证工具类
+ * 用于管理用户登录状态、token存储和登录检查
+ */
+
+const TOKEN_KEY = 'user_token'
+const USER_INFO_KEY = 'user_info'
+
+/**
+ * 保存用户token到本地存储
+ * @param {string} token - 用户登录token
+ */
+export const setToken = (token) => {
+  uni.setStorageSync(TOKEN_KEY, token)
+}
+
+/**
+ * 获取本地存储的token
+ * @returns {string|null} token或null
+ */
+export const getToken = () => {
+  return uni.getStorageSync(TOKEN_KEY) || null
+}
+
+/**
+ * 移除本地存储的token
+ */
+export const removeToken = () => {
+  uni.removeStorageSync(TOKEN_KEY)
+}
+
+/**
+ * 保存用户信息到本地存储
+ * @param {object} userInfo - 用户信息对象
+ */
+export const setUserInfo = (userInfo) => {
+  uni.setStorageSync(USER_INFO_KEY, JSON.stringify(userInfo))
+}
+
+/**
+ * 获取本地存储的用户信息
+ * @returns {object|null} 用户信息对象或null
+ */
+export const getUserInfo = () => {
+  const userInfo = uni.getStorageSync(USER_INFO_KEY)
+  return userInfo ? JSON.parse(userInfo) : null
+}
+
+/**
+ * 移除本地存储的用户信息
+ */
+export const removeUserInfo = () => {
+  uni.removeStorageSync(USER_INFO_KEY)
+}
+
+/**
+ * 检查用户是否已登录
+ * @returns {boolean} 是否已登录
+ */
+export const isLoggedIn = () => {
+  return !!getToken()
+}
+
+/**
+ * 清除所有登录信息(退出登录)
+ */
+export const logout = () => {
+  removeToken()
+  removeUserInfo()
+}
+
+/**
+ * 检查登录状态,未登录则跳转到登录页
+ * @param {function} callback - 登录成功后的回调函数
+ * @returns {boolean} 是否已登录
+ */
+export const checkLogin = (callback) => {
+  if (isLoggedIn()) {
+    return true
+  }
+  
+  // 未登录,显示提示并跳转到登录页
+  uni.showModal({
+    title: '提示',
+    content: '此操作需要登录,是否前往登录?',
+    success: (res) => {
+      if (res.confirm) {
+        uni.navigateTo({
+          url: '/pages/login/login',
+          success: () => {
+            // 登录成功后的回调可以通过事件总线或全局变量传递
+            if (callback) {
+              getApp().globalData.loginCallback = callback
+            }
+          }
+        })
+      }
+    }
+  })
+  
+  return false
+}

Някои файлове не бяха показани, защото твърде много файлове са промени