Selaa lähdekoodia

订单记录修改

Zhangbw 3 kuukautta sitten
vanhempi
sitoutus
1e347fda0e

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

@@ -12,6 +12,7 @@ if (!Math) {
   "./pages/mine/mine.js";
   "./pages/profile/edit.js";
   "./pages/transaction/transaction.js";
+  "./pages/order/order.js";
   "./pages/admin/shortPool.js";
 }
 const _sfc_main = {

+ 1 - 0
dist/dev/mp-weixin/app.json

@@ -8,6 +8,7 @@
     "pages/mine/mine",
     "pages/profile/edit",
     "pages/transaction/transaction",
+    "pages/order/order",
     "pages/admin/shortPool"
   ],
   "window": {

+ 1 - 0
dist/dev/mp-weixin/common/vendor.js

@@ -6829,3 +6829,4 @@ exports.ref = ref;
 exports.resolveComponent = resolveComponent;
 exports.sr = sr;
 exports.t = t;
+exports.unref = unref;

+ 1 - 1
dist/dev/mp-weixin/pages/admin/shortPool.js

@@ -94,7 +94,7 @@ const _sfc_main = {
       searchTimer = setTimeout(async () => {
         try {
           const res = await utils_api.getSuggestions(searchKeyword.value.trim());
-          suggestions.value = (res.code === 0 || res.code === 200) && res.data ? res.data : [];
+          suggestions.value = res.code === 200 && res.data ? res.data : [];
         } catch (e) {
           suggestions.value = [];
         } finally {

+ 2 - 2
dist/dev/mp-weixin/pages/index/index.js

@@ -65,7 +65,7 @@ const _sfc_main = {
       try {
         const response = await utils_api.getSuggestions(kw.trim());
         console.log("模糊查询返回数据:", response);
-        const list = response.data || [];
+        const list = response.code === 200 && response.data ? response.data : [];
         suggestions.value = Array.isArray(list) ? list : [];
         showDropdown.value = suggestions.value.length > 0;
         console.log("下拉框状态:", { showDropdown: showDropdown.value, suggestionsLength: suggestions.value.length });
@@ -107,7 +107,7 @@ const _sfc_main = {
       showDropdown.value = false;
       try {
         const res = await utils_api.searchStocks(queryCode);
-        if (res.code === 0 && res.data) {
+        if (res.code === 200 && res.data) {
           result.value = res.data;
         } else {
           errorMsg.value = res.message || "未查询到相关股票数据";

+ 15 - 6
dist/dev/mp-weixin/pages/mine/mine.js

@@ -1,7 +1,7 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
 const utils_auth = require("../../utils/auth.js");
-require("../../utils/api.js");
+const utils_api = require("../../utils/api.js");
 const _sfc_main = {
   __name: "mine",
   setup(__props) {
@@ -70,6 +70,14 @@ const _sfc_main = {
         url: "/pages/transaction/transaction"
       });
     };
+    const handleOrderRecord = () => {
+      if (!utils_auth.checkLogin()) {
+        return;
+      }
+      common_vendor.index.navigateTo({
+        url: "/pages/order/order"
+      });
+    };
     const handleShortPoolManage = () => {
       if (!isAdmin.value) {
         common_vendor.index.showToast({ title: "无权限访问", icon: "none" });
@@ -81,17 +89,18 @@ const _sfc_main = {
     };
     return (_ctx, _cache) => {
       return common_vendor.e({
-        a: isLoggedIn.value && userInfo.value.avatar ? userInfo.value.avatar : "/static/images/head.png",
+        a: isLoggedIn.value && userInfo.value.avatar ? common_vendor.unref(utils_api.getImageUrl)(userInfo.value.avatar) : "/static/images/head.png",
         b: common_vendor.t(isLoggedIn.value ? userInfo.value.nickname || "微信用户" : "登录/注册"),
         c: common_vendor.o(handleUserCardClick),
         d: common_vendor.o(handleSubscriptionRecord),
-        e: isAdmin.value
+        e: common_vendor.o(handleOrderRecord),
+        f: isAdmin.value
       }, isAdmin.value ? {
-        f: common_vendor.o(handleShortPoolManage)
+        g: common_vendor.o(handleShortPoolManage)
       } : {}, {
-        g: isLoggedIn.value
+        h: isLoggedIn.value
       }, isLoggedIn.value ? {
-        h: common_vendor.o(handleLogout)
+        i: common_vendor.o(handleLogout)
       } : {});
     };
   }

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

@@ -1 +1 @@
-<view class="page-mine"><scroll-view class="scroll-view" scroll-y><view class="content-wrapper"><view class="user-info-card" bindtap="{{c}}"><view class="user-header"><image class="user-avatar" src="{{a}}" mode="aspectFill"></image><view class="user-details"><text class="user-name">{{b}}</text></view><view class="arrow-icon">›</view></view></view><view class="menu-list"><view class="menu-item" bindtap="{{d}}"><view class="menu-left"><text class="menu-icon">📝</text><text class="menu-label">订阅记录</text></view><text class="menu-arrow">›</text></view><view wx:if="{{e}}" class="menu-item" bindtap="{{f}}"><view class="menu-left"><text class="menu-icon">⚡</text><text class="menu-label">超短池管理</text></view><text class="menu-arrow">›</text></view></view><view wx:if="{{g}}" class="logout-section"><button class="logout-btn" bindtap="{{h}}"> 退出登录 </button></view><view class="bottom-safe-area"></view></view></scroll-view></view>
+<view class="page-mine"><scroll-view class="scroll-view" scroll-y><view class="content-wrapper"><view class="user-info-card" bindtap="{{c}}"><view class="user-header"><image class="user-avatar" src="{{a}}" mode="aspectFill"></image><view class="user-details"><text class="user-name">{{b}}</text></view><view class="arrow-icon">›</view></view></view><view class="menu-list"><view class="menu-item" bindtap="{{d}}"><view class="menu-left"><text class="menu-icon">📝</text><text class="menu-label">我的订阅</text></view><text class="menu-arrow">›</text></view><view class="menu-item" bindtap="{{e}}"><view class="menu-left"><text class="menu-icon">📋</text><text class="menu-label">我的订单</text></view><text class="menu-arrow">›</text></view><view wx:if="{{f}}" class="menu-item" bindtap="{{g}}"><view class="menu-left"><text class="menu-icon">⚡</text><text class="menu-label">超短池管理</text></view><text class="menu-arrow">›</text></view></view><view wx:if="{{h}}" class="logout-section"><button class="logout-btn" bindtap="{{i}}"> 退出登录 </button></view><view class="bottom-safe-area"></view></view></scroll-view></view>

+ 98 - 0
dist/dev/mp-weixin/pages/order/order.js

@@ -0,0 +1,98 @@
+"use strict";
+const common_vendor = require("../../common/vendor.js");
+const utils_auth = require("../../utils/auth.js");
+const utils_api = require("../../utils/api.js");
+const _sfc_main = {
+  __name: "order",
+  setup(__props) {
+    const orders = common_vendor.ref([]);
+    const loading = common_vendor.ref(false);
+    const isLoggedIn = common_vendor.ref(false);
+    const checkLogin = () => {
+      isLoggedIn.value = utils_auth.isLoggedIn();
+    };
+    const handleBack = () => {
+      const pages = getCurrentPages();
+      pages.length > 1 ? common_vendor.index.navigateBack() : common_vendor.index.switchTab({ url: "/pages/mine/mine" });
+    };
+    const loadOrders = async () => {
+      if (!isLoggedIn.value)
+        return;
+      loading.value = true;
+      try {
+        const res = await utils_api.getUserOrders();
+        if (res.code === 200) {
+          orders.value = res.data || [];
+        }
+      } catch (e) {
+        console.error("加载订单记录失败:", e);
+      } finally {
+        loading.value = false;
+      }
+    };
+    const handlePay = async (order) => {
+      try {
+        common_vendor.index.showLoading({ title: "正在支付..." });
+        const res = await utils_api.createOrder({ planId: order.planId });
+        if (res.code !== 200) {
+          throw new Error(res.message || "创建订单失败");
+        }
+        await utils_api.wxPay(res.data);
+        common_vendor.index.showToast({ title: "支付成功", icon: "success" });
+        setTimeout(() => loadOrders(), 1500);
+      } catch (e) {
+        common_vendor.index.showToast({ title: e.message || "支付失败", icon: "none" });
+      } finally {
+        common_vendor.index.hideLoading();
+      }
+    };
+    const getStatusClass = (status) => {
+      switch (status) {
+        case 0:
+          return "pending";
+        case 1:
+          return "paid";
+        case 2:
+          return "cancelled";
+        default:
+          return "closed";
+      }
+    };
+    common_vendor.onLoad(() => checkLogin());
+    common_vendor.onMounted(() => {
+      if (isLoggedIn.value)
+        loadOrders();
+    });
+    common_vendor.onShow(() => {
+      checkLogin();
+      if (isLoggedIn.value)
+        loadOrders();
+    });
+    return (_ctx, _cache) => {
+      return common_vendor.e({
+        a: common_vendor.o(handleBack),
+        b: loading.value
+      }, loading.value ? {} : orders.value.length === 0 ? {} : {
+        d: common_vendor.f(orders.value, (order, k0, i0) => {
+          return common_vendor.e({
+            a: common_vendor.t(order.orderNo),
+            b: common_vendor.t(order.orderStatusName),
+            c: common_vendor.n(getStatusClass(order.orderStatus)),
+            d: common_vendor.t(order.planName),
+            e: common_vendor.t(order.amount),
+            f: common_vendor.t(order.createTime),
+            g: order.orderStatus === 0
+          }, order.orderStatus === 0 ? {
+            h: common_vendor.o(($event) => handlePay(order), order.orderNo)
+          } : {}, {
+            i: order.orderNo
+          });
+        })
+      }, {
+        c: orders.value.length === 0
+      });
+    };
+  }
+};
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-88bf5328"], ["__file", "D:/program/gupiao-wx/src/pages/order/order.vue"]]);
+wx.createPage(MiniProgramPage);

+ 4 - 0
dist/dev/mp-weixin/pages/order/order.json

@@ -0,0 +1,4 @@
+{
+  "navigationStyle": "custom",
+  "usingComponents": {}
+}

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

@@ -0,0 +1 @@
+<view class="page-container data-v-88bf5328"><view class="custom-navbar data-v-88bf5328"><view class="navbar-back data-v-88bf5328" bindtap="{{a}}"><text class="back-icon data-v-88bf5328">←</text></view><view class="navbar-title data-v-88bf5328"><text class="title-text data-v-88bf5328">我的订单</text></view><view class="navbar-placeholder data-v-88bf5328"></view></view><scroll-view class="scroll-view data-v-88bf5328" scroll-y><view class="content-wrapper data-v-88bf5328"><view class="order-card data-v-88bf5328"><view wx:if="{{b}}" class="loading-state data-v-88bf5328"><text class="data-v-88bf5328">加载中...</text></view><view wx:elif="{{c}}" class="empty-state data-v-88bf5328"><text class="empty-icon data-v-88bf5328">📋</text><text class="empty-text data-v-88bf5328">暂无订单记录</text></view><view wx:else class="order-list data-v-88bf5328"><view wx:for="{{d}}" wx:for-item="order" wx:key="i" class="order-item data-v-88bf5328"><view class="order-header data-v-88bf5328"><text class="order-no data-v-88bf5328">{{order.a}}</text><view class="{{['order-status', 'data-v-88bf5328', order.c]}}"><text class="data-v-88bf5328">{{order.b}}</text></view></view><view class="order-body data-v-88bf5328"><text class="order-plan data-v-88bf5328">{{order.d}}</text><text class="order-amount data-v-88bf5328">¥{{order.e}}</text></view><view class="order-footer data-v-88bf5328"><text class="order-time data-v-88bf5328">{{order.f}}</text><view wx:if="{{order.g}}" class="pay-btn data-v-88bf5328" bindtap="{{order.h}}"><text class="data-v-88bf5328">去支付</text></view></view></view></view></view><view class="bottom-safe-area data-v-88bf5328"></view></view></scroll-view></view>

+ 148 - 0
dist/dev/mp-weixin/pages/order/order.wxss

@@ -0,0 +1,148 @@
+
+.page-container.data-v-88bf5328 {
+  min-height: 100vh;
+  background: #f5f6fb;
+  display: flex;
+  flex-direction: column;
+}
+.custom-navbar.data-v-88bf5328 {
+  background: #ffffff;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 80rpx 32rpx 30rpx;
+  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+  position: relative;
+}
+.navbar-back.data-v-88bf5328 {
+  width: 80rpx;
+  height: 60rpx;
+  display: flex;
+  align-items: center;
+}
+.back-icon.data-v-88bf5328 {
+  font-size: 40rpx;
+  color: #222222;
+  font-weight: bold;
+}
+.navbar-title.data-v-88bf5328 {
+  position: absolute;
+  left: 50%;
+  transform: translateX(-50%);
+}
+.title-text.data-v-88bf5328 {
+  font-size: 36rpx;
+  font-weight: 600;
+  color: #222222;
+}
+.navbar-placeholder.data-v-88bf5328 {
+  width: 80rpx;
+}
+.scroll-view.data-v-88bf5328 {
+  flex: 1;
+  height: 0;
+}
+.content-wrapper.data-v-88bf5328 {
+  padding: 32rpx;
+}
+.order-card.data-v-88bf5328 {
+  background: #ffffff;
+  border-radius: 24rpx;
+  padding: 32rpx;
+  box-shadow: 0 8rpx 24rpx rgba(37, 52, 94, 0.08);
+}
+.loading-state.data-v-88bf5328, .empty-state.data-v-88bf5328 {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 60rpx 40rpx;
+}
+.empty-icon.data-v-88bf5328 {
+  font-size: 80rpx;
+  margin-bottom: 20rpx;
+}
+.empty-text.data-v-88bf5328 {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333333;
+}
+.order-list.data-v-88bf5328 {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+.order-item.data-v-88bf5328 {
+  background: #f9f9fb;
+  border-radius: 12rpx;
+  padding: 24rpx;
+}
+.order-header.data-v-88bf5328 {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16rpx;
+}
+.order-no.data-v-88bf5328 {
+  font-size: 22rpx;
+  color: #999;
+  font-family: monospace;
+}
+.order-status.data-v-88bf5328 {
+  padding: 4rpx 16rpx;
+  border-radius: 20rpx;
+  font-size: 22rpx;
+}
+.order-status.pending.data-v-88bf5328 {
+  background: #fff3e0;
+  color: #ff9800;
+}
+.order-status.paid.data-v-88bf5328 {
+  background: #e8f5e9;
+  color: #4caf50;
+}
+.order-status.cancelled.data-v-88bf5328 {
+  background: #f5f5f5;
+  color: #999;
+}
+.order-status.closed.data-v-88bf5328 {
+  background: #ffebee;
+  color: #f44336;
+}
+.order-body.data-v-88bf5328 {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12rpx;
+}
+.order-plan.data-v-88bf5328 {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+}
+.order-amount.data-v-88bf5328 {
+  font-size: 30rpx;
+  font-weight: 700;
+  color: #f16565;
+}
+.order-footer.data-v-88bf5328 {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.order-time.data-v-88bf5328 {
+  font-size: 22rpx;
+  color: #999;
+}
+.pay-btn.data-v-88bf5328 {
+  padding: 10rpx 28rpx;
+  background: #ff9800;
+  border-radius: 24rpx;
+}
+.pay-btn text.data-v-88bf5328 {
+  font-size: 24rpx;
+  color: #fff;
+  font-weight: 500;
+}
+.bottom-safe-area.data-v-88bf5328 {
+  height: 80rpx;
+}

+ 26 - 26
dist/dev/mp-weixin/pages/pool/pool.js

@@ -53,30 +53,23 @@ const _sfc_main = {
       isLoggedIn.value = utils_auth.isLoggedIn();
       return isLoggedIn.value;
     };
-    const checkPurchaseStatus = () => {
+    const checkPurchaseStatus = async () => {
       if (!checkLogin()) {
         isPurchased.value = false;
         stopAutoRefresh();
         return;
       }
       try {
-        const purchaseInfo = common_vendor.index.getStorageSync("pool_purchase");
-        if (purchaseInfo) {
-          const now = Date.now();
-          if (now < purchaseInfo.expireTime) {
-            isPurchased.value = true;
-            loadAndStartRefresh();
-          } else {
-            common_vendor.index.removeStorageSync("pool_purchase");
-            isPurchased.value = false;
-            stopAutoRefresh();
-          }
+        const res = await utils_api.checkSubscription(1);
+        if (res.code === 200 && res.data.hasSubscription) {
+          isPurchased.value = true;
+          loadAndStartRefresh();
         } else {
           isPurchased.value = false;
           stopAutoRefresh();
         }
       } catch (e) {
-        console.error("检查购买状态失败:", e);
+        console.error("检查订阅状态失败:", e);
         isPurchased.value = false;
         stopAutoRefresh();
       }
@@ -115,19 +108,26 @@ const _sfc_main = {
     const closePurchaseModal = () => {
       showModal.value = false;
     };
-    const handlePurchase = () => {
-      const now = Date.now();
-      const today = /* @__PURE__ */ new Date();
-      today.setHours(23, 59, 59, 999);
-      common_vendor.index.setStorageSync("pool_purchase", {
-        plan: "reward",
-        purchaseTime: now,
-        expireTime: today.getTime()
-      });
-      isPurchased.value = true;
-      closePurchaseModal();
-      common_vendor.index.showToast({ title: "解锁成功", icon: "success" });
-      loadAndStartRefresh();
+    const handlePurchase = async () => {
+      try {
+        common_vendor.index.showLoading({ title: "正在支付..." });
+        const res = await utils_api.createOrder({ poolType: 1 });
+        if (res.code !== 200) {
+          throw new Error(res.message || "创建订单失败");
+        }
+        const payRes = await utils_api.mockPay(res.data.orderNo);
+        if (payRes.code !== 200) {
+          throw new Error(payRes.message || "支付失败");
+        }
+        common_vendor.index.hideLoading();
+        isPurchased.value = true;
+        closePurchaseModal();
+        common_vendor.index.showToast({ title: "支付成功", icon: "success" });
+        loadAndStartRefresh();
+      } catch (e) {
+        common_vendor.index.hideLoading();
+        common_vendor.index.showToast({ title: e.message || "支付失败", icon: "none" });
+      }
     };
     const onHistorySearch = ({ startMonth, endMonth }) => {
       const formatMonth = (monthStr) => {

+ 12 - 8
dist/dev/mp-weixin/pages/profile/edit.js

@@ -24,18 +24,18 @@ const _sfc_main = {
     const loadUserInfo = async () => {
       const userInfo = utils_auth.getUserInfo();
       if (userInfo) {
-        avatarUrl.value = userInfo.avatar || "/static/images/head.png";
+        avatarUrl.value = userInfo.avatar ? utils_api.getImageUrl(userInfo.avatar) : "/static/images/head.png";
         nickname.value = userInfo.nickname || "";
         phone.value = userInfo.phone || "";
-        originalAvatar.value = avatarUrl.value;
+        originalAvatar.value = userInfo.avatar || "";
         originalNickname.value = nickname.value;
       }
       const latestInfo = await utils_auth.refreshUserInfo();
       if (latestInfo) {
-        avatarUrl.value = latestInfo.avatar || "/static/images/head.png";
+        avatarUrl.value = latestInfo.avatar ? utils_api.getImageUrl(latestInfo.avatar) : "/static/images/head.png";
         nickname.value = latestInfo.nickname || "";
         phone.value = latestInfo.phone || "";
-        originalAvatar.value = avatarUrl.value;
+        originalAvatar.value = latestInfo.avatar || "";
         originalNickname.value = nickname.value;
       }
     };
@@ -51,7 +51,7 @@ const _sfc_main = {
       return new Promise((resolve, reject) => {
         const token = common_vendor.index.getStorageSync("user_token");
         common_vendor.index.uploadFile({
-          url: "http://localhost:8081/v1/file/upload",
+          url: `${utils_api.BASE_URL}/v1/file/upload`,
           filePath: tempPath,
           name: "file",
           header: {
@@ -81,15 +81,19 @@ const _sfc_main = {
         common_vendor.index.showToast({ title: "请输入昵称", icon: "none" });
         return;
       }
-      if (avatarUrl.value === originalAvatar.value && nickname.value === originalNickname.value) {
+      let currentAvatarPath = avatarUrl.value;
+      if (currentAvatarPath.startsWith(utils_api.BASE_URL)) {
+        currentAvatarPath = currentAvatarPath.replace(utils_api.BASE_URL, "");
+      }
+      if (currentAvatarPath === originalAvatar.value && nickname.value === originalNickname.value) {
         common_vendor.index.showToast({ title: "没有修改", icon: "none" });
         return;
       }
       saving.value = true;
       common_vendor.index.showLoading({ title: "保存中..." });
       try {
-        let uploadedAvatarUrl = avatarUrl.value;
-        if (avatarUrl.value !== originalAvatar.value && !avatarUrl.value.startsWith("/static/") && !avatarUrl.value.startsWith("http")) {
+        let uploadedAvatarUrl = currentAvatarPath;
+        if (currentAvatarPath !== originalAvatar.value && !avatarUrl.value.startsWith("/static/") && !avatarUrl.value.startsWith("http")) {
           try {
             uploadedAvatarUrl = await uploadAvatar(avatarUrl.value);
           } catch (e) {

+ 26 - 25
dist/dev/mp-weixin/pages/strong/strong.js

@@ -48,30 +48,23 @@ const _sfc_main = {
         refreshTimer = null;
       }
     };
-    const checkPurchaseStatus = () => {
+    const checkPurchaseStatus = async () => {
       if (!utils_auth.isLoggedIn()) {
         isPurchased.value = false;
         stopAutoRefresh();
         return;
       }
       try {
-        const purchaseInfo = common_vendor.index.getStorageSync("strong_pool_purchase");
-        if (purchaseInfo) {
-          const now = Date.now();
-          if (now < purchaseInfo.expireTime) {
-            isPurchased.value = true;
-            loadAndStartRefresh();
-          } else {
-            common_vendor.index.removeStorageSync("strong_pool_purchase");
-            isPurchased.value = false;
-            stopAutoRefresh();
-          }
+        const res = await utils_api.checkSubscription(2);
+        if (res.code === 200 && res.data.hasSubscription) {
+          isPurchased.value = true;
+          loadAndStartRefresh();
         } else {
           isPurchased.value = false;
           stopAutoRefresh();
         }
       } catch (e) {
-        console.error("检查购买状态失败:", e);
+        console.error("检查订阅状态失败:", e);
         isPurchased.value = false;
         stopAutoRefresh();
       }
@@ -110,18 +103,26 @@ const _sfc_main = {
     const closePurchaseModal = () => {
       showModal.value = false;
     };
-    const handlePurchase = () => {
-      const now = Date.now();
-      const expireTime = now + 365 * 24 * 60 * 60 * 1e3;
-      common_vendor.index.setStorageSync("strong_pool_purchase", {
-        plan: "yearly",
-        purchaseTime: now,
-        expireTime
-      });
-      isPurchased.value = true;
-      closePurchaseModal();
-      common_vendor.index.showToast({ title: "解锁成功", icon: "success" });
-      loadAndStartRefresh();
+    const handlePurchase = async () => {
+      try {
+        common_vendor.index.showLoading({ title: "正在支付..." });
+        const res = await utils_api.createOrder({ poolType: 2 });
+        if (res.code !== 200) {
+          throw new Error(res.message || "创建订单失败");
+        }
+        const payRes = await utils_api.mockPay(res.data.orderNo);
+        if (payRes.code !== 200) {
+          throw new Error(payRes.message || "支付失败");
+        }
+        common_vendor.index.hideLoading();
+        isPurchased.value = true;
+        closePurchaseModal();
+        common_vendor.index.showToast({ title: "支付成功", icon: "success" });
+        loadAndStartRefresh();
+      } catch (e) {
+        common_vendor.index.hideLoading();
+        common_vendor.index.showToast({ title: e.message || "支付失败", icon: "none" });
+      }
     };
     const onHistorySearch = ({ startMonth, endMonth }) => {
       const formatMonth = (monthStr) => {

+ 29 - 50
dist/dev/mp-weixin/pages/transaction/transaction.js

@@ -1,11 +1,12 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
 const utils_auth = require("../../utils/auth.js");
-require("../../utils/api.js");
+const utils_api = require("../../utils/api.js");
 const _sfc_main = {
   __name: "transaction",
   setup(__props) {
     const subscriptions = common_vendor.ref([]);
+    const loading = common_vendor.ref(false);
     const isLoggedIn = common_vendor.ref(false);
     const checkLogin = () => {
       isLoggedIn.value = utils_auth.isLoggedIn();
@@ -14,76 +15,54 @@ const _sfc_main = {
       const pages = getCurrentPages();
       pages.length > 1 ? common_vendor.index.navigateBack() : common_vendor.index.switchTab({ url: "/pages/mine/mine" });
     };
-    const formatDateTime = (timestamp) => {
-      const date = new Date(timestamp);
-      const year = date.getFullYear();
-      const month = String(date.getMonth() + 1).padStart(2, "0");
-      const day = String(date.getDate()).padStart(2, "0");
-      const hours = String(date.getHours()).padStart(2, "0");
-      const minutes = String(date.getMinutes()).padStart(2, "0");
-      return `${year}-${month}-${day} ${hours}:${minutes}`;
-    };
-    const loadSubscriptions = () => {
+    const loadSubscriptions = async () => {
+      if (!isLoggedIn.value)
+        return;
+      loading.value = true;
       try {
-        const now = Date.now();
-        const allSubscriptions = [];
-        const poolPurchase = common_vendor.index.getStorageSync("pool_purchase");
-        if (poolPurchase) {
-          allSubscriptions.push({
-            poolType: "pool",
-            poolName: "超短精选池",
-            planName: poolPurchase.plan === "daily" ? "日订阅" : "周套餐",
-            amount: poolPurchase.plan === "daily" ? "18" : "98",
-            purchaseTime: poolPurchase.purchaseTime,
-            expireTime: poolPurchase.expireTime,
-            isActive: now < poolPurchase.expireTime
-          });
-        }
-        const strongPurchase = common_vendor.index.getStorageSync("strong_pool_purchase");
-        if (strongPurchase) {
-          allSubscriptions.push({
-            poolType: "strong",
-            poolName: "强势趋势池",
-            planName: "年订阅",
-            amount: "98",
-            purchaseTime: strongPurchase.purchaseTime,
-            expireTime: strongPurchase.expireTime,
-            isActive: now < strongPurchase.expireTime
-          });
+        const res = await utils_api.getUserSubscriptions();
+        if (res.code === 200) {
+          subscriptions.value = res.data || [];
         }
-        subscriptions.value = allSubscriptions.sort((a, b) => b.purchaseTime - a.purchaseTime);
       } catch (e) {
         console.error("加载订阅记录失败:", e);
-        subscriptions.value = [];
+      } finally {
+        loading.value = false;
       }
     };
     common_vendor.onLoad(() => checkLogin());
-    common_vendor.onMounted(() => loadSubscriptions());
+    common_vendor.onMounted(() => {
+      if (isLoggedIn.value)
+        loadSubscriptions();
+    });
     common_vendor.onShow(() => {
       checkLogin();
-      loadSubscriptions();
+      if (isLoggedIn.value)
+        loadSubscriptions();
     });
     return (_ctx, _cache) => {
       return common_vendor.e({
         a: common_vendor.o(handleBack),
-        b: common_vendor.t(subscriptions.value.length),
-        c: subscriptions.value.length === 0
-      }, subscriptions.value.length === 0 ? {} : {
+        b: loading.value
+      }, loading.value ? {} : subscriptions.value.length === 0 ? {} : {
         d: common_vendor.f(subscriptions.value, (item, index, i0) => {
           return common_vendor.e({
-            a: common_vendor.t(item.poolType === "pool" ? "⚡" : "📈"),
+            a: common_vendor.t(item.poolType === 1 ? "⚡" : "📈"),
             b: common_vendor.t(item.poolName),
             c: common_vendor.t(item.isActive ? "生效中" : "已过期"),
             d: common_vendor.n(item.isActive ? "active" : "expired"),
-            e: common_vendor.t(item.planName),
-            f: common_vendor.t(item.amount),
-            g: common_vendor.t(formatDateTime(item.purchaseTime)),
-            h: common_vendor.t(formatDateTime(item.expireTime)),
-            i: item.isActive
-          }, item.isActive ? {} : {}, {
+            e: common_vendor.t(item.amount),
+            f: common_vendor.t(item.startTime),
+            g: common_vendor.t(item.expireTime),
+            h: item.isActive
+          }, item.isActive ? {
+            i: common_vendor.t(item.remainDays)
+          } : {}, {
             j: index
           });
         })
+      }, {
+        c: subscriptions.value.length === 0
       });
     };
   }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/dev/mp-weixin/pages/transaction/transaction.wxml


+ 19 - 72
dist/dev/mp-weixin/pages/transaction/transaction.wxss

@@ -5,8 +5,6 @@
   display: flex;
   flex-direction: column;
 }
-
-/* 导航栏 */
 .custom-navbar.data-v-a901d1cb {
   background: #ffffff;
   display: flex;
@@ -40,8 +38,6 @@
 .navbar-placeholder.data-v-a901d1cb {
   width: 80rpx;
 }
-
-/* 内容区 */
 .scroll-view.data-v-a901d1cb {
   flex: 1;
   height: 0;
@@ -49,48 +45,36 @@
 .content-wrapper.data-v-a901d1cb {
   padding: 32rpx;
 }
-
-/* 记录卡片 */
-.record-card.data-v-a901d1cb {
+.subscription-card.data-v-a901d1cb {
   background: #ffffff;
   border-radius: 24rpx;
   padding: 32rpx;
   box-shadow: 0 8rpx 24rpx rgba(37, 52, 94, 0.08);
 }
-.card-header.data-v-a901d1cb {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 24rpx;
-  padding-bottom: 24rpx;
-  border-bottom: 1rpx solid #f1f2f6;
-}
-.header-left.data-v-a901d1cb {
+.loading-state.data-v-a901d1cb, .empty-state.data-v-a901d1cb {
   display: flex;
+  flex-direction: column;
   align-items: center;
+  padding: 60rpx 40rpx;
 }
-.header-dot.data-v-a901d1cb {
-  width: 12rpx;
-  height: 12rpx;
-  border-radius: 50%;
-  background: #5B5AEA;
-  margin-right: 12rpx;
+.empty-icon.data-v-a901d1cb {
+  font-size: 80rpx;
+  margin-bottom: 20rpx;
 }
-.header-title.data-v-a901d1cb {
-  font-size: 30rpx;
+.empty-text.data-v-a901d1cb {
+  font-size: 28rpx;
   font-weight: 600;
-  color: #222222;
+  color: #333333;
+  margin-bottom: 8rpx;
 }
-.header-count.data-v-a901d1cb {
-  font-size: 26rpx;
-  color: #9ca2b5;
+.empty-desc.data-v-a901d1cb {
+  font-size: 24rpx;
+  color: #999999;
 }
-
-/* 订阅列表 */
 .subscription-list.data-v-a901d1cb {
   display: flex;
   flex-direction: column;
-  gap: 24rpx;
+  gap: 20rpx;
 }
 .subscription-item.data-v-a901d1cb {
   background: #f9f9fb;
@@ -133,7 +117,7 @@
 .item-body.data-v-a901d1cb {
   display: flex;
   flex-direction: column;
-  gap: 16rpx;
+  gap: 14rpx;
 }
 .info-row.data-v-a901d1cb {
   display: flex;
@@ -152,47 +136,10 @@
 .info-value.price.data-v-a901d1cb {
   color: #f16565;
   font-weight: 700;
-  font-size: 28rpx;
 }
-.item-footer.data-v-a901d1cb {
-  margin-top: 24rpx;
-  padding-top: 20rpx;
-  border-top: 1rpx solid #eee;
-  display: flex;
-  justify-content: flex-end;
-}
-.renew-btn.data-v-a901d1cb {
-  padding: 14rpx 40rpx;
-  background: #5B5AEA;
-  border-radius: 32rpx;
-}
-.renew-btn text.data-v-a901d1cb {
-  font-size: 26rpx;
-  color: #ffffff;
-  font-weight: 500;
-}
-
-/* 空状态 */
-.empty-state.data-v-a901d1cb {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 80rpx 40rpx;
-}
-.empty-icon.data-v-a901d1cb {
-  font-size: 100rpx;
-  margin-bottom: 24rpx;
-}
-.empty-text.data-v-a901d1cb {
-  font-size: 30rpx;
-  font-weight: 600;
-  color: #333333;
-  margin-bottom: 12rpx;
-}
-.empty-desc.data-v-a901d1cb {
-  font-size: 26rpx;
-  color: #999999;
-  text-align: center;
+.info-value.highlight.data-v-a901d1cb {
+  color: #5B5AEA;
+  font-weight: 700;
 }
 .bottom-safe-area.data-v-a901d1cb {
   height: 80rpx;

+ 73 - 0
dist/dev/mp-weixin/utils/api.js

@@ -2,6 +2,14 @@
 const common_vendor = require("../common/vendor.js");
 const DEV_BASE_URL = "http://localhost:8081";
 const BASE_URL = DEV_BASE_URL;
+const getImageUrl = (url) => {
+  if (!url)
+    return "";
+  if (url.startsWith("http") || url.startsWith("/static/")) {
+    return url;
+  }
+  return BASE_URL + url;
+};
 const getToken = () => {
   return common_vendor.index.getStorageSync("user_token") || null;
 };
@@ -168,17 +176,82 @@ const getStockPoolList = (poolType) => {
     data: { poolType }
   });
 };
+const createOrder = (data) => {
+  return request({
+    url: "/v1/order/create",
+    method: "POST",
+    header: {
+      "content-type": "application/json"
+    },
+    data
+  });
+};
+const getUserOrders = () => {
+  return request({
+    url: "/v1/order/list",
+    method: "GET"
+  });
+};
+const getUserSubscriptions = () => {
+  return request({
+    url: "/v1/order/subscriptions",
+    method: "GET"
+  });
+};
+const checkSubscription = (poolType) => {
+  return request({
+    url: "/v1/order/check-subscription",
+    method: "GET",
+    data: { poolType }
+  });
+};
+const wxPay = (payParams) => {
+  return new Promise((resolve, reject) => {
+    common_vendor.index.requestPayment({
+      provider: "wxpay",
+      timeStamp: payParams.timeStamp,
+      nonceStr: payParams.nonceStr,
+      package: payParams.packageValue,
+      signType: payParams.signType,
+      paySign: payParams.paySign,
+      success: (res) => {
+        resolve(res);
+      },
+      fail: (err) => {
+        if (err.errMsg.includes("cancel")) {
+          reject(new Error("用户取消支付"));
+        } else {
+          reject(new Error("支付失败:" + err.errMsg));
+        }
+      }
+    });
+  });
+};
+const mockPay = (orderNo) => {
+  return request({
+    url: `/v1/order/mock-pay?orderNo=${encodeURIComponent(orderNo)}`,
+    method: "POST"
+  });
+};
+exports.BASE_URL = BASE_URL;
 exports.addUserStock = addUserStock;
+exports.checkSubscription = checkSubscription;
+exports.createOrder = createOrder;
 exports.deleteUserStock = deleteUserStock;
+exports.getImageUrl = getImageUrl;
 exports.getIndexQuote = getIndexQuote;
 exports.getStockPoolList = getStockPoolList;
 exports.getStockQuotes = getStockQuotes;
 exports.getSuggestions = getSuggestions;
 exports.getUserInfoApi = getUserInfoApi;
+exports.getUserOrders = getUserOrders;
 exports.getUserStocks = getUserStocks;
+exports.getUserSubscriptions = getUserSubscriptions;
+exports.mockPay = mockPay;
 exports.searchStocks = searchStocks;
 exports.updateUserProfile = updateUserProfile;
 exports.uploadFile = uploadFile;
 exports.wxCompleteUserInfoApi = wxCompleteUserInfoApi;
+exports.wxPay = wxPay;
 exports.wxPhoneLoginApi = wxPhoneLoginApi;
 exports.wxSilentLoginApi = wxSilentLoginApi;

+ 6 - 0
src/pages.json

@@ -48,6 +48,12 @@
         "navigationStyle": "custom"
       }
     },
+    {
+      "path": "pages/order/order",
+      "style": {
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/admin/shortPool",
       "style": {

+ 1 - 1
src/pages/admin/shortPool.vue

@@ -217,7 +217,7 @@ const handleSearchInput = () => {
   searchTimer = setTimeout(async () => {
     try {
       const res = await getSuggestions(searchKeyword.value.trim())
-      suggestions.value = (res.code === 0 || res.code === 200) && res.data ? res.data : []
+      suggestions.value = res.code === 200 && res.data ? res.data : []
     } catch (e) {
       suggestions.value = []
     } finally {

+ 2 - 2
src/pages/index/index.vue

@@ -262,7 +262,7 @@ const doSearchSuggestions = async (kw) => {
     console.log('模糊查询返回数据:', response)
     
     // 从响应中提取 data 数组
-    const list = response.data || []
+    const list = (response.code === 200 && response.data) ? response.data : []
     suggestions.value = Array.isArray(list) ? list : []
     showDropdown.value = suggestions.value.length > 0
     console.log('下拉框状态:', { showDropdown: showDropdown.value, suggestionsLength: suggestions.value.length })
@@ -311,7 +311,7 @@ const doSearch = async (queryCode) => {
 
   try {
     const res = await searchStocks(queryCode)
-    if (res.code === 0 && res.data) {
+    if (res.code === 200 && res.data) {
       result.value = res.data
     } else {
       errorMsg.value = res.message || '未查询到相关股票数据'

+ 27 - 2
src/pages/mine/mine.vue

@@ -7,7 +7,7 @@
           <view class="user-header">
             <image 
               class="user-avatar" 
-              :src="isLoggedIn && userInfo.avatar ? userInfo.avatar : '/static/images/head.png'" 
+              :src="isLoggedIn && userInfo.avatar ? getImageUrl(userInfo.avatar) : '/static/images/head.png'" 
               mode="aspectFill"
             ></image>
             <view class="user-details">
@@ -22,7 +22,15 @@
           <view class="menu-item" @click="handleSubscriptionRecord">
             <view class="menu-left">
               <text class="menu-icon">📝</text>
-              <text class="menu-label">订阅记录</text>
+              <text class="menu-label">我的订阅</text>
+            </view>
+            <text class="menu-arrow">›</text>
+          </view>
+          
+          <view class="menu-item" @click="handleOrderRecord">
+            <view class="menu-left">
+              <text class="menu-icon">📋</text>
+              <text class="menu-label">我的订单</text>
             </view>
             <text class="menu-arrow">›</text>
           </view>
@@ -55,6 +63,7 @@
 import { ref } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLogin, getUserInfo as getStoredUserInfo, refreshUserInfo, logout, checkLogin as requireLogin } from '@/utils/auth.js'
+import { getImageUrl } from '@/utils/api.js'
 
 const isLoggedIn = ref(false)
 const isAdmin = ref(false)
@@ -157,6 +166,22 @@ const handleSubscriptionRecord = () => {
   })
 }
 
+/**
+ * 订单记录
+ */
+const handleOrderRecord = () => {
+  if (!requireLogin(() => {
+    handleOrderRecord()
+  })) {
+    return
+  }
+  
+  // 跳转到订单记录页面
+  uni.navigateTo({
+    url: '/pages/order/order'
+  })
+}
+
 /**
  * 超短池管理(管理员专属)
  */

+ 314 - 0
src/pages/order/order.vue

@@ -0,0 +1,314 @@
+<template>
+  <view class="page-container">
+    <!-- 顶部导航栏 -->
+    <view class="custom-navbar">
+      <view class="navbar-back" @click="handleBack">
+        <text class="back-icon">←</text>
+      </view>
+      <view class="navbar-title">
+        <text class="title-text">我的订单</text>
+      </view>
+      <view class="navbar-placeholder"></view>
+    </view>
+
+    <scroll-view class="scroll-view" scroll-y>
+      <view class="content-wrapper">
+        <view class="order-card">
+          <!-- 加载中 -->
+          <view v-if="loading" class="loading-state">
+            <text>加载中...</text>
+          </view>
+
+          <!-- 空状态 -->
+          <view v-else-if="orders.length === 0" class="empty-state">
+            <text class="empty-icon">📋</text>
+            <text class="empty-text">暂无订单记录</text>
+          </view>
+
+          <!-- 订单列表 -->
+          <view v-else class="order-list">
+            <view 
+              v-for="order in orders" 
+              :key="order.orderNo"
+              class="order-item"
+            >
+              <view class="order-header">
+                <text class="order-no">{{ order.orderNo }}</text>
+                <view class="order-status" :class="getStatusClass(order.orderStatus)">
+                  <text>{{ order.orderStatusName }}</text>
+                </view>
+              </view>
+              <view class="order-body">
+                <text class="order-plan">{{ order.planName }}</text>
+                <text class="order-amount">¥{{ order.amount }}</text>
+              </view>
+              <view class="order-footer">
+                <text class="order-time">{{ order.createTime }}</text>
+                <view v-if="order.orderStatus === 0" class="pay-btn" @click="handlePay(order)">
+                  <text>去支付</text>
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="bottom-safe-area"></view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { onLoad, onShow } from '@dcloudio/uni-app'
+import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
+import { getUserOrders, createOrder, wxPay } from '../../utils/api.js'
+
+const orders = ref([])
+const loading = ref(false)
+const isLoggedIn = ref(false)
+
+const checkLogin = () => {
+  isLoggedIn.value = checkLoginStatus()
+}
+
+const handleBack = () => {
+  const pages = getCurrentPages()
+  pages.length > 1 ? uni.navigateBack() : uni.switchTab({ url: '/pages/mine/mine' })
+}
+
+// 加载订单列表
+const loadOrders = async () => {
+  if (!isLoggedIn.value) return
+  loading.value = true
+  
+  try {
+    const res = await getUserOrders()
+    if (res.code === 200) {
+      orders.value = res.data || []
+    }
+  } catch (e) {
+    console.error('加载订单记录失败:', e)
+  } finally {
+    loading.value = false
+  }
+}
+
+// 去支付
+const handlePay = async (order) => {
+  try {
+    uni.showLoading({ title: '正在支付...' })
+    
+    const res = await createOrder({ planId: order.planId })
+    if (res.code !== 200) {
+      throw new Error(res.message || '创建订单失败')
+    }
+    
+    await wxPay(res.data)
+    uni.showToast({ title: '支付成功', icon: 'success' })
+    setTimeout(() => loadOrders(), 1500)
+  } catch (e) {
+    uni.showToast({ title: e.message || '支付失败', icon: 'none' })
+  } finally {
+    uni.hideLoading()
+  }
+}
+
+// 获取订单状态样式类
+const getStatusClass = (status) => {
+  switch (status) {
+    case 0: return 'pending'
+    case 1: return 'paid'
+    case 2: return 'cancelled'
+    default: return 'closed'
+  }
+}
+
+onLoad(() => checkLogin())
+onMounted(() => {
+  if (isLoggedIn.value) loadOrders()
+})
+onShow(() => {
+  checkLogin()
+  if (isLoggedIn.value) loadOrders()
+})
+</script>
+
+<style scoped>
+.page-container {
+  min-height: 100vh;
+  background: #f5f6fb;
+  display: flex;
+  flex-direction: column;
+}
+
+.custom-navbar {
+  background: #ffffff;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 80rpx 32rpx 30rpx;
+  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+  position: relative;
+}
+
+.navbar-back {
+  width: 80rpx;
+  height: 60rpx;
+  display: flex;
+  align-items: center;
+}
+
+.back-icon {
+  font-size: 40rpx;
+  color: #222222;
+  font-weight: bold;
+}
+
+.navbar-title {
+  position: absolute;
+  left: 50%;
+  transform: translateX(-50%);
+}
+
+.title-text {
+  font-size: 36rpx;
+  font-weight: 600;
+  color: #222222;
+}
+
+.navbar-placeholder {
+  width: 80rpx;
+}
+
+.scroll-view {
+  flex: 1;
+  height: 0;
+}
+
+.content-wrapper {
+  padding: 32rpx;
+}
+
+.order-card {
+  background: #ffffff;
+  border-radius: 24rpx;
+  padding: 32rpx;
+  box-shadow: 0 8rpx 24rpx rgba(37, 52, 94, 0.08);
+}
+
+.loading-state, .empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 60rpx 40rpx;
+}
+
+.empty-icon {
+  font-size: 80rpx;
+  margin-bottom: 20rpx;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333333;
+}
+
+.order-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+
+.order-item {
+  background: #f9f9fb;
+  border-radius: 12rpx;
+  padding: 24rpx;
+}
+
+.order-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16rpx;
+}
+
+.order-no {
+  font-size: 22rpx;
+  color: #999;
+  font-family: monospace;
+}
+
+.order-status {
+  padding: 4rpx 16rpx;
+  border-radius: 20rpx;
+  font-size: 22rpx;
+}
+
+.order-status.pending {
+  background: #fff3e0;
+  color: #ff9800;
+}
+
+.order-status.paid {
+  background: #e8f5e9;
+  color: #4caf50;
+}
+
+.order-status.cancelled {
+  background: #f5f5f5;
+  color: #999;
+}
+
+.order-status.closed {
+  background: #ffebee;
+  color: #f44336;
+}
+
+.order-body {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12rpx;
+}
+
+.order-plan {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+}
+
+.order-amount {
+  font-size: 30rpx;
+  font-weight: 700;
+  color: #f16565;
+}
+
+.order-footer {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.order-time {
+  font-size: 22rpx;
+  color: #999;
+}
+
+.pay-btn {
+  padding: 10rpx 28rpx;
+  background: #ff9800;
+  border-radius: 24rpx;
+}
+
+.pay-btn text {
+  font-size: 24rpx;
+  color: #fff;
+  font-weight: 500;
+}
+
+.bottom-safe-area {
+  height: 80rpx;
+}
+</style>

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

@@ -86,7 +86,7 @@
 import { ref, onUnmounted } from 'vue'
 import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
-import { getStockQuotes, addUserStock, getStockPoolList } from '../../utils/api.js'
+import { getStockQuotes, addUserStock, getStockPoolList, createOrder, mockPay, checkSubscription } from '../../utils/api.js'
 import PurchaseModal from '../../components/PurchaseModal.vue'
 import PerformanceCard from '../../components/PerformanceCard.vue'
 import HistorySearchCard from '../../components/HistorySearchCard.vue'
@@ -146,8 +146,8 @@ const checkLogin = () => {
   return isLoggedIn.value
 }
 
-// 检查购买状态
-const checkPurchaseStatus = () => {
+// 检查购买状态(从后端查询)
+const checkPurchaseStatus = async () => {
   if (!checkLogin()) {
     isPurchased.value = false
     stopAutoRefresh()
@@ -155,24 +155,16 @@ const checkPurchaseStatus = () => {
   }
   
   try {
-    const purchaseInfo = uni.getStorageSync('pool_purchase')
-    if (purchaseInfo) {
-      const now = Date.now()
-      if (now < purchaseInfo.expireTime) {
-        isPurchased.value = true
-        // 已解锁,加载股票池数据并启动自动刷新
-        loadAndStartRefresh()
-      } else {
-        uni.removeStorageSync('pool_purchase')
-        isPurchased.value = false
-        stopAutoRefresh()
-      }
+    const res = await checkSubscription(1)  // 1=超短池
+    if (res.code === 200 && res.data.hasSubscription) {
+      isPurchased.value = true
+      loadAndStartRefresh()
     } else {
       isPurchased.value = false
       stopAutoRefresh()
     }
   } catch (e) {
-    console.error('检查购买状态失败:', e)
+    console.error('检查订阅状态失败:', e)
     isPurchased.value = false
     stopAutoRefresh()
   }
@@ -219,23 +211,35 @@ const closePurchaseModal = () => {
   showModal.value = false
 }
 
-// 处理购买
-const handlePurchase = () => {
-  const now = Date.now()
-  const today = new Date()
-  today.setHours(23, 59, 59, 999)
-
-  uni.setStorageSync('pool_purchase', {
-    plan: 'reward',
-    purchaseTime: now,
-    expireTime: today.getTime()
-  })
-
-  isPurchased.value = true
-  closePurchaseModal()
-  uni.showToast({ title: '解锁成功', icon: 'success' })
-  // 解锁后加载数据并启动自动刷新
-  loadAndStartRefresh()
+// 处理购买(调用后端支付接口)
+const handlePurchase = async () => {
+  try {
+    uni.showLoading({ title: '正在支付...' })
+    
+    // 1. 创建订单
+    const res = await createOrder({ poolType: 1 })  // 1=超短池
+    if (res.code !== 200) {
+      throw new Error(res.message || '创建订单失败')
+    }
+    
+    // 2. 模拟支付成功(测试用,正式环境改用 wxPay)
+    const payRes = await mockPay(res.data.orderNo)
+    if (payRes.code !== 200) {
+      throw new Error(payRes.message || '支付失败')
+    }
+    
+    // 3. 支付成功
+    uni.hideLoading()
+    isPurchased.value = true
+    closePurchaseModal()
+    uni.showToast({ title: '支付成功', icon: 'success' })
+    
+    // 解锁后加载数据并启动自动刷新
+    loadAndStartRefresh()
+  } catch (e) {
+    uni.hideLoading()
+    uni.showToast({ title: e.message || '支付失败', icon: 'none' })
+  }
 }
 
 // 历史查询

+ 16 - 10
src/pages/profile/edit.vue

@@ -62,7 +62,7 @@
 import { ref, onMounted } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
 import { getUserInfo, setUserInfo, refreshUserInfo } from '@/utils/auth.js'
-import { updateUserProfile } from '@/utils/api.js'
+import { updateUserProfile, getImageUrl, BASE_URL } from '@/utils/api.js'
 
 const avatarUrl = ref('/static/images/head.png')
 const nickname = ref('')
@@ -88,20 +88,20 @@ const loadUserInfo = async () => {
   // 先显示本地缓存
   const userInfo = getUserInfo()
   if (userInfo) {
-    avatarUrl.value = userInfo.avatar || '/static/images/head.png'
+    avatarUrl.value = userInfo.avatar ? getImageUrl(userInfo.avatar) : '/static/images/head.png'
     nickname.value = userInfo.nickname || ''
     phone.value = userInfo.phone || ''
-    originalAvatar.value = avatarUrl.value
+    originalAvatar.value = userInfo.avatar || ''
     originalNickname.value = nickname.value
   }
   
   // 刷新最新状态
   const latestInfo = await refreshUserInfo()
   if (latestInfo) {
-    avatarUrl.value = latestInfo.avatar || '/static/images/head.png'
+    avatarUrl.value = latestInfo.avatar ? getImageUrl(latestInfo.avatar) : '/static/images/head.png'
     nickname.value = latestInfo.nickname || ''
     phone.value = latestInfo.phone || ''
-    originalAvatar.value = avatarUrl.value
+    originalAvatar.value = latestInfo.avatar || ''
     originalNickname.value = nickname.value
   }
 }
@@ -119,7 +119,7 @@ const uploadAvatar = async (tempPath) => {
   return new Promise((resolve, reject) => {
     const token = uni.getStorageSync('user_token')
     uni.uploadFile({
-      url: 'http://localhost:8081/v1/file/upload',
+      url: `${BASE_URL}/v1/file/upload`,
       filePath: tempPath,
       name: 'file',
       header: {
@@ -150,7 +150,13 @@ const handleSave = async () => {
     return
   }
 
-  if (avatarUrl.value === originalAvatar.value && nickname.value === originalNickname.value) {
+  // 获取当前头像的原始路径(去掉BASE_URL前缀)
+  let currentAvatarPath = avatarUrl.value
+  if (currentAvatarPath.startsWith(BASE_URL)) {
+    currentAvatarPath = currentAvatarPath.replace(BASE_URL, '')
+  }
+
+  if (currentAvatarPath === originalAvatar.value && nickname.value === originalNickname.value) {
     uni.showToast({ title: '没有修改', icon: 'none' })
     return
   }
@@ -159,10 +165,10 @@ const handleSave = async () => {
   uni.showLoading({ title: '保存中...' })
 
   try {
-    let uploadedAvatarUrl = avatarUrl.value
+    let uploadedAvatarUrl = currentAvatarPath
 
-    // 如果头像是临时文件路径,需要上传到OSS
-    if (avatarUrl.value !== originalAvatar.value && 
+    // 如果头像是临时文件路径,需要上传
+    if (currentAvatarPath !== originalAvatar.value && 
         !avatarUrl.value.startsWith('/static/') && 
         !avatarUrl.value.startsWith('http')) {
       try {

+ 37 - 32
src/pages/strong/strong.vue

@@ -86,7 +86,7 @@
 import { ref, onUnmounted } from 'vue'
 import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
-import { getStockQuotes, addUserStock, getStockPoolList } from '../../utils/api.js'
+import { getStockQuotes, addUserStock, getStockPoolList, createOrder, mockPay, checkSubscription } from '../../utils/api.js'
 import PurchaseModal from '../../components/PurchaseModal.vue'
 import PerformanceCard from '../../components/PerformanceCard.vue'
 import HistorySearchCard from '../../components/HistorySearchCard.vue'
@@ -139,8 +139,8 @@ const stopAutoRefresh = () => {
   }
 }
 
-// 检查购买状态
-const checkPurchaseStatus = () => {
+// 检查购买状态(从后端查询)
+const checkPurchaseStatus = async () => {
   if (!checkLoginStatus()) {
     isPurchased.value = false
     stopAutoRefresh()
@@ -148,24 +148,16 @@ const checkPurchaseStatus = () => {
   }
   
   try {
-    const purchaseInfo = uni.getStorageSync('strong_pool_purchase')
-    if (purchaseInfo) {
-      const now = Date.now()
-      if (now < purchaseInfo.expireTime) {
-        isPurchased.value = true
-        // 已解锁,加载股票池数据并启动自动刷新
-        loadAndStartRefresh()
-      } else {
-        uni.removeStorageSync('strong_pool_purchase')
-        isPurchased.value = false
-        stopAutoRefresh()
-      }
+    const res = await checkSubscription(2)  // 2=强势池
+    if (res.code === 200 && res.data.hasSubscription) {
+      isPurchased.value = true
+      loadAndStartRefresh()
     } else {
       isPurchased.value = false
       stopAutoRefresh()
     }
   } catch (e) {
-    console.error('检查购买状态失败:', e)
+    console.error('检查订阅状态失败:', e)
     isPurchased.value = false
     stopAutoRefresh()
   }
@@ -212,22 +204,35 @@ const closePurchaseModal = () => {
   showModal.value = false
 }
 
-// 处理购买
-const handlePurchase = () => {
-  const now = Date.now()
-  const expireTime = now + 365 * 24 * 60 * 60 * 1000
-
-  uni.setStorageSync('strong_pool_purchase', {
-    plan: 'yearly',
-    purchaseTime: now,
-    expireTime: expireTime
-  })
-
-  isPurchased.value = true
-  closePurchaseModal()
-  uni.showToast({ title: '解锁成功', icon: 'success' })
-  // 解锁后加载数据并启动自动刷新
-  loadAndStartRefresh()
+// 处理购买(调用后端支付接口)
+const handlePurchase = async () => {
+  try {
+    uni.showLoading({ title: '正在支付...' })
+    
+    // 1. 创建订单
+    const res = await createOrder({ poolType: 2 })  // 2=强势池
+    if (res.code !== 200) {
+      throw new Error(res.message || '创建订单失败')
+    }
+    
+    // 2. 模拟支付成功(测试用,正式环境改用 wxPay)
+    const payRes = await mockPay(res.data.orderNo)
+    if (payRes.code !== 200) {
+      throw new Error(payRes.message || '支付失败')
+    }
+    
+    // 3. 支付成功
+    uni.hideLoading()
+    isPurchased.value = true
+    closePurchaseModal()
+    uni.showToast({ title: '支付成功', icon: 'success' })
+    
+    // 解锁后加载数据并启动自动刷新
+    loadAndStartRefresh()
+  } catch (e) {
+    uni.hideLoading()
+    uni.showToast({ title: e.message || '支付失败', icon: 'none' })
+  }
 }
 
 // 历史查询

+ 48 - 140
src/pages/transaction/transaction.vue

@@ -6,25 +6,21 @@
         <text class="back-icon">←</text>
       </view>
       <view class="navbar-title">
-        <text class="title-text">订阅记录</text>
+        <text class="title-text">我的订阅</text>
       </view>
       <view class="navbar-placeholder"></view>
     </view>
 
     <scroll-view class="scroll-view" scroll-y>
       <view class="content-wrapper">
-        <!-- 订阅记录卡片 -->
-        <view class="record-card">
-          <view class="card-header">
-            <view class="header-left">
-              <view class="header-dot"></view>
-              <text class="header-title">我的订阅</text>
-            </view>
-            <text class="header-count">共 {{ subscriptions.length }} 条</text>
+        <view class="subscription-card">
+          <!-- 加载中 -->
+          <view v-if="loading" class="loading-state">
+            <text>加载中...</text>
           </view>
 
           <!-- 空状态 -->
-          <view v-if="subscriptions.length === 0" class="empty-state">
+          <view v-else-if="subscriptions.length === 0" class="empty-state">
             <text class="empty-icon">📋</text>
             <text class="empty-text">暂无订阅记录</text>
             <text class="empty-desc">前往超短池或强势池订阅服务</text>
@@ -39,7 +35,7 @@
             >
               <view class="item-header">
                 <view class="pool-info">
-                  <text class="pool-icon">{{ item.poolType === 'pool' ? '⚡' : '📈' }}</text>
+                  <text class="pool-icon">{{ item.poolType === 1 ? '⚡' : '📈' }}</text>
                   <text class="pool-name">{{ item.poolName }}</text>
                 </view>
                 <view class="status-badge" :class="item.isActive ? 'active' : 'expired'">
@@ -48,27 +44,21 @@
               </view>
               
               <view class="item-body">
-                <view class="info-row">
-                  <text class="info-label">订阅方案</text>
-                  <text class="info-value">{{ item.planName }}</text>
-                </view>
                 <view class="info-row">
                   <text class="info-label">订阅金额</text>
                   <text class="info-value price">¥{{ item.amount }}</text>
                 </view>
                 <view class="info-row">
-                  <text class="info-label">购买时间</text>
-                  <text class="info-value">{{ formatDateTime(item.purchaseTime) }}</text>
+                  <text class="info-label">开始时间</text>
+                  <text class="info-value">{{ item.startTime }}</text>
                 </view>
                 <view class="info-row">
                   <text class="info-label">到期时间</text>
-                  <text class="info-value">{{ formatDateTime(item.expireTime) }}</text>
+                  <text class="info-value">{{ item.expireTime }}</text>
                 </view>
-              </view>
-
-              <view v-if="item.isActive" class="item-footer">
-                <view class="renew-btn">
-                  <text>续费</text>
+                <view v-if="item.isActive" class="info-row">
+                  <text class="info-label">剩余天数</text>
+                  <text class="info-value highlight">{{ item.remainDays }}天</text>
                 </view>
               </view>
             </view>
@@ -85,8 +75,10 @@
 import { ref, onMounted } from 'vue'
 import { onLoad, onShow } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
+import { getUserSubscriptions } from '../../utils/api.js'
 
 const subscriptions = ref([])
+const loading = ref(false)
 const isLoggedIn = ref(false)
 
 const checkLogin = () => {
@@ -98,59 +90,30 @@ const handleBack = () => {
   pages.length > 1 ? uni.navigateBack() : uni.switchTab({ url: '/pages/mine/mine' })
 }
 
-const formatDateTime = (timestamp) => {
-  const date = new Date(timestamp)
-  const year = date.getFullYear()
-  const month = String(date.getMonth() + 1).padStart(2, '0')
-  const day = String(date.getDate()).padStart(2, '0')
-  const hours = String(date.getHours()).padStart(2, '0')
-  const minutes = String(date.getMinutes()).padStart(2, '0')
-  return `${year}-${month}-${day} ${hours}:${minutes}`
-}
-
-const loadSubscriptions = () => {
+// 加载订阅列表
+const loadSubscriptions = async () => {
+  if (!isLoggedIn.value) return
+  loading.value = true
+  
   try {
-    const now = Date.now()
-    const allSubscriptions = []
-    
-    const poolPurchase = uni.getStorageSync('pool_purchase')
-    if (poolPurchase) {
-      allSubscriptions.push({
-        poolType: 'pool',
-        poolName: '超短精选池',
-        planName: poolPurchase.plan === 'daily' ? '日订阅' : '周套餐',
-        amount: poolPurchase.plan === 'daily' ? '18' : '98',
-        purchaseTime: poolPurchase.purchaseTime,
-        expireTime: poolPurchase.expireTime,
-        isActive: now < poolPurchase.expireTime
-      })
-    }
-    
-    const strongPurchase = uni.getStorageSync('strong_pool_purchase')
-    if (strongPurchase) {
-      allSubscriptions.push({
-        poolType: 'strong',
-        poolName: '强势趋势池',
-        planName: '年订阅',
-        amount: '98',
-        purchaseTime: strongPurchase.purchaseTime,
-        expireTime: strongPurchase.expireTime,
-        isActive: now < strongPurchase.expireTime
-      })
+    const res = await getUserSubscriptions()
+    if (res.code === 200) {
+      subscriptions.value = res.data || []
     }
-    
-    subscriptions.value = allSubscriptions.sort((a, b) => b.purchaseTime - a.purchaseTime)
   } catch (e) {
     console.error('加载订阅记录失败:', e)
-    subscriptions.value = []
+  } finally {
+    loading.value = false
   }
 }
 
 onLoad(() => checkLogin())
-onMounted(() => loadSubscriptions())
+onMounted(() => {
+  if (isLoggedIn.value) loadSubscriptions()
+})
 onShow(() => {
   checkLogin()
-  loadSubscriptions()
+  if (isLoggedIn.value) loadSubscriptions()
 })
 </script>
 
@@ -162,7 +125,6 @@ onShow(() => {
   flex-direction: column;
 }
 
-/* 导航栏 */
 .custom-navbar {
   background: #ffffff;
   display: flex;
@@ -202,7 +164,6 @@ onShow(() => {
   width: 80rpx;
 }
 
-/* 内容区 */
 .scroll-view {
   flex: 1;
   height: 0;
@@ -212,52 +173,41 @@ onShow(() => {
   padding: 32rpx;
 }
 
-/* 记录卡片 */
-.record-card {
+.subscription-card {
   background: #ffffff;
   border-radius: 24rpx;
   padding: 32rpx;
   box-shadow: 0 8rpx 24rpx rgba(37, 52, 94, 0.08);
 }
 
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 24rpx;
-  padding-bottom: 24rpx;
-  border-bottom: 1rpx solid #f1f2f6;
-}
-
-.header-left {
+.loading-state, .empty-state {
   display: flex;
+  flex-direction: column;
   align-items: center;
+  padding: 60rpx 40rpx;
 }
 
-.header-dot {
-  width: 12rpx;
-  height: 12rpx;
-  border-radius: 50%;
-  background: #5B5AEA;
-  margin-right: 12rpx;
+.empty-icon {
+  font-size: 80rpx;
+  margin-bottom: 20rpx;
 }
 
-.header-title {
-  font-size: 30rpx;
+.empty-text {
+  font-size: 28rpx;
   font-weight: 600;
-  color: #222222;
+  color: #333333;
+  margin-bottom: 8rpx;
 }
 
-.header-count {
-  font-size: 26rpx;
-  color: #9ca2b5;
+.empty-desc {
+  font-size: 24rpx;
+  color: #999999;
 }
 
-/* 订阅列表 */
 .subscription-list {
   display: flex;
   flex-direction: column;
-  gap: 24rpx;
+  gap: 20rpx;
 }
 
 .subscription-item {
@@ -309,7 +259,7 @@ onShow(() => {
 .item-body {
   display: flex;
   flex-direction: column;
-  gap: 16rpx;
+  gap: 14rpx;
 }
 
 .info-row {
@@ -332,53 +282,11 @@ onShow(() => {
 .info-value.price {
   color: #f16565;
   font-weight: 700;
-  font-size: 28rpx;
 }
 
-.item-footer {
-  margin-top: 24rpx;
-  padding-top: 20rpx;
-  border-top: 1rpx solid #eee;
-  display: flex;
-  justify-content: flex-end;
-}
-
-.renew-btn {
-  padding: 14rpx 40rpx;
-  background: #5B5AEA;
-  border-radius: 32rpx;
-}
-
-.renew-btn text {
-  font-size: 26rpx;
-  color: #ffffff;
-  font-weight: 500;
-}
-
-/* 空状态 */
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 80rpx 40rpx;
-}
-
-.empty-icon {
-  font-size: 100rpx;
-  margin-bottom: 24rpx;
-}
-
-.empty-text {
-  font-size: 30rpx;
-  font-weight: 600;
-  color: #333333;
-  margin-bottom: 12rpx;
-}
-
-.empty-desc {
-  font-size: 26rpx;
-  color: #999999;
-  text-align: center;
+.info-value.highlight {
+  color: #5B5AEA;
+  font-weight: 700;
 }
 
 .bottom-safe-area {

+ 152 - 0
src/utils/api.js

@@ -17,6 +17,25 @@ const PROD_BASE_URL = 'https://your-domain.com'
 // 在微信开发者工具中使用 localhost,真机调试使用局域网IP
 const BASE_URL = DEV_BASE_URL
 
+/**
+ * 获取完整的图片URL
+ * 将相对路径转换为完整的服务器地址
+ * @param {string} url - 图片路径(可能是相对路径或完整URL)
+ * @returns {string} 完整的图片URL
+ */
+export const getImageUrl = (url) => {
+  if (!url) return ''
+  // 已经是完整URL或本地静态资源,直接返回
+  if (url.startsWith('http') || url.startsWith('/static/')) {
+    return url
+  }
+  // 相对路径,拼接BASE_URL
+  return BASE_URL + url
+}
+
+// 导出BASE_URL供其他模块使用
+export { BASE_URL }
+
 /**
  * 获取本地存储的token(避免循环依赖)
  */
@@ -288,3 +307,136 @@ export const getStockPoolList = (poolType) => {
     data: { poolType }
   })
 }
+
+
+// ==================== 订单与支付相关API ====================
+
+/**
+ * 获取套餐列表
+ * @param {number} poolType - 池类型:1-超短池,2-强势池(可选)
+ * @returns {Promise} 返回套餐列表
+ */
+export const getPlanList = (poolType) => {
+  return request({
+    url: '/v1/order/plans',
+    method: 'GET',
+    data: poolType ? { poolType } : {}
+  })
+}
+
+/**
+ * 创建订单(获取支付参数)
+ * @param {object} data - { planId } 或 { planCode }
+ * @returns {Promise} 返回微信支付参数
+ */
+export const createOrder = (data) => {
+  return request({
+    url: '/v1/order/create',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: data
+  })
+}
+
+/**
+ * 查询订单状态
+ * @param {string} orderNo - 订单号
+ * @returns {Promise} 返回订单信息
+ */
+export const queryOrder = (orderNo) => {
+  return request({
+    url: '/v1/order/query',
+    method: 'GET',
+    data: { orderNo }
+  })
+}
+
+/**
+ * 获取用户订单列表
+ * @returns {Promise} 返回订单列表
+ */
+export const getUserOrders = () => {
+  return request({
+    url: '/v1/order/list',
+    method: 'GET'
+  })
+}
+
+/**
+ * 获取用户订阅列表
+ * @returns {Promise} 返回订阅列表
+ */
+export const getUserSubscriptions = () => {
+  return request({
+    url: '/v1/order/subscriptions',
+    method: 'GET'
+  })
+}
+
+/**
+ * 检查用户是否有有效订阅
+ * @param {number} poolType - 池类型:1-超短池,2-强势池
+ * @returns {Promise} 返回 { hasSubscription: boolean, poolType: number }
+ */
+export const checkSubscription = (poolType) => {
+  return request({
+    url: '/v1/order/check-subscription',
+    method: 'GET',
+    data: { poolType }
+  })
+}
+
+/**
+ * 取消订单
+ * @param {string} orderNo - 订单号
+ * @returns {Promise}
+ */
+export const cancelOrder = (orderNo) => {
+  return request({
+    url: '/v1/order/cancel',
+    method: 'POST',
+    data: { orderNo }
+  })
+}
+
+/**
+ * 调起微信支付
+ * @param {object} payParams - 支付参数(从createOrder返回)
+ * @returns {Promise}
+ */
+export const wxPay = (payParams) => {
+  return new Promise((resolve, reject) => {
+    uni.requestPayment({
+      provider: 'wxpay',
+      timeStamp: payParams.timeStamp,
+      nonceStr: payParams.nonceStr,
+      package: payParams.packageValue,
+      signType: payParams.signType,
+      paySign: payParams.paySign,
+      success: (res) => {
+        resolve(res)
+      },
+      fail: (err) => {
+        if (err.errMsg.includes('cancel')) {
+          reject(new Error('用户取消支付'))
+        } else {
+          reject(new Error('支付失败:' + err.errMsg))
+        }
+      }
+    })
+  })
+}
+
+/**
+ * 模拟支付成功(测试用)
+ * @param {string} orderNo - 订单号
+ * @returns {Promise}
+ */
+export const mockPay = (orderNo) => {
+  return request({
+    url: `/v1/order/mock-pay?orderNo=${encodeURIComponent(orderNo)}`,
+    method: 'POST'
+  })
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä