pool.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. "use strict";
  2. const common_vendor = require("../../common/vendor.js");
  3. const utils_auth = require("../../utils/auth.js");
  4. const utils_api = require("../../utils/api.js");
  5. if (!Math) {
  6. (PerformanceCard + HistorySearchCard + PurchaseModal)();
  7. }
  8. const PurchaseModal = () => "../../components/PurchaseModal.js";
  9. const PerformanceCard = () => "../../components/PerformanceCard.js";
  10. const HistorySearchCard = () => "../../components/HistorySearchCard.js";
  11. const _sfc_main = {
  12. __name: "pool",
  13. setup(__props) {
  14. const isPurchased = common_vendor.ref(false);
  15. const showModal = common_vendor.ref(false);
  16. const isLoggedIn = common_vendor.ref(false);
  17. const isPageVisible = common_vendor.ref(false);
  18. const shortPrice = common_vendor.ref(1);
  19. const stockList = common_vendor.ref([]);
  20. let refreshTimer = null;
  21. let subscriptionTimer = null;
  22. const performanceStats = common_vendor.reactive({
  23. successRate: "0%",
  24. avgTrend: "+0%",
  25. totalCount: 0
  26. });
  27. const onDateChange = async ({ startDate, endDate, poolType }) => {
  28. if (!checkLogin()) {
  29. console.log("[超短池] 未登录,跳过统计数据查询");
  30. return;
  31. }
  32. try {
  33. const res = await utils_api.getStockHistoryStats({
  34. startDate,
  35. endDate,
  36. poolType
  37. });
  38. if (res.code === 200 && res.data) {
  39. performanceStats.successRate = res.data.successRate || "0%";
  40. performanceStats.avgTrend = res.data.avgTrend || "+0%";
  41. performanceStats.totalCount = res.data.totalCount || 0;
  42. }
  43. } catch (e) {
  44. console.error("加载统计数据失败:", e);
  45. }
  46. };
  47. const getChangeClass = (changePercent) => {
  48. if (!changePercent || changePercent === "-")
  49. return "";
  50. return changePercent.startsWith("+") ? "text-red" : "text-green";
  51. };
  52. const getRandomInterval = () => 2e3 + Math.random() * 1e3;
  53. const startAutoRefresh = () => {
  54. if (!isPageVisible.value)
  55. return;
  56. stopAutoRefresh();
  57. const scheduleNextRefresh = () => {
  58. if (!isPageVisible.value) {
  59. stopAutoRefresh();
  60. return;
  61. }
  62. refreshTimer = setTimeout(async () => {
  63. if (!isPageVisible.value) {
  64. stopAutoRefresh();
  65. return;
  66. }
  67. await loadStockPool();
  68. scheduleNextRefresh();
  69. }, getRandomInterval());
  70. };
  71. scheduleNextRefresh();
  72. };
  73. const stopAutoRefresh = () => {
  74. if (refreshTimer) {
  75. clearTimeout(refreshTimer);
  76. refreshTimer = null;
  77. }
  78. };
  79. const startSubscriptionRefresh = () => {
  80. stopSubscriptionRefresh();
  81. if (!isPageVisible.value)
  82. return;
  83. subscriptionTimer = setInterval(() => {
  84. if (isPageVisible.value) {
  85. checkPurchaseStatus();
  86. }
  87. }, 3e4);
  88. };
  89. const stopSubscriptionRefresh = () => {
  90. if (subscriptionTimer) {
  91. clearInterval(subscriptionTimer);
  92. subscriptionTimer = null;
  93. }
  94. };
  95. const checkLogin = () => {
  96. isLoggedIn.value = utils_auth.isLoggedIn();
  97. return isLoggedIn.value;
  98. };
  99. const checkPurchaseStatus = async () => {
  100. if (!checkLogin()) {
  101. isPurchased.value = false;
  102. stopAutoRefresh();
  103. return;
  104. }
  105. try {
  106. const res = await utils_api.checkSubscription(1);
  107. if (res.code === 200 && res.data.hasSubscription) {
  108. isPurchased.value = true;
  109. loadAndStartRefresh();
  110. } else {
  111. isPurchased.value = false;
  112. stopAutoRefresh();
  113. }
  114. } catch (e) {
  115. console.error("检查订阅状态失败:", e);
  116. isPurchased.value = false;
  117. stopAutoRefresh();
  118. }
  119. };
  120. const loadStockPool = async () => {
  121. try {
  122. const res = await utils_api.getStockPoolList(1);
  123. if (res.code === 200 && res.data) {
  124. stockList.value = res.data;
  125. }
  126. } catch (e) {
  127. console.error("加载标的池失败:", e);
  128. }
  129. };
  130. const loadAndStartRefresh = async () => {
  131. await loadStockPool();
  132. startAutoRefresh();
  133. };
  134. const showPurchaseModal = async () => {
  135. if (!checkLogin()) {
  136. common_vendor.index.showModal({
  137. title: "登录提示",
  138. content: "此功能需要登录后使用,是否前往登录?",
  139. confirmText: "去登录",
  140. cancelText: "取消",
  141. success: (res) => {
  142. if (res.confirm) {
  143. common_vendor.index.navigateTo({ url: "/pages/login/login" });
  144. }
  145. }
  146. });
  147. return;
  148. }
  149. try {
  150. const res = await utils_api.getPaymentConfig(1);
  151. if (res.code === 200 && res.data) {
  152. shortPrice.value = res.data.price || 1;
  153. }
  154. } catch (e) {
  155. console.error("获取价格配置失败:", e);
  156. }
  157. showModal.value = true;
  158. };
  159. const closePurchaseModal = () => {
  160. showModal.value = false;
  161. };
  162. const pollOrderStatus = async (orderNo, maxRetries = 5, interval = 1e3) => {
  163. for (let i = 0; i < maxRetries; i++) {
  164. try {
  165. const res = await utils_api.queryOrder(orderNo);
  166. if (res.code === 200 && res.data && res.data.orderStatus === 1) {
  167. return true;
  168. }
  169. } catch (e) {
  170. console.log("查询订单状态失败:", e);
  171. }
  172. if (i < maxRetries - 1) {
  173. await new Promise((r) => setTimeout(r, interval));
  174. }
  175. }
  176. return false;
  177. };
  178. const handlePurchase = async () => {
  179. try {
  180. common_vendor.index.showLoading({ title: "正在支付..." });
  181. const res = await utils_api.createOrder({ poolType: 1 });
  182. if (res.code !== 200) {
  183. throw new Error(res.message || "创建订单失败");
  184. }
  185. const orderNo = res.data.orderNo;
  186. common_vendor.index.hideLoading();
  187. await utils_api.wxPay(res.data);
  188. common_vendor.index.showLoading({ title: "确认支付结果..." });
  189. const confirmed = await pollOrderStatus(orderNo);
  190. common_vendor.index.hideLoading();
  191. if (confirmed) {
  192. isPurchased.value = true;
  193. closePurchaseModal();
  194. common_vendor.index.showToast({ title: "支付成功", icon: "success" });
  195. loadAndStartRefresh();
  196. } else {
  197. closePurchaseModal();
  198. common_vendor.index.showToast({ title: "支付处理中,请稍后刷新", icon: "none" });
  199. }
  200. } catch (e) {
  201. common_vendor.index.hideLoading();
  202. common_vendor.index.showToast({ title: e.message || "支付失败", icon: "none" });
  203. }
  204. };
  205. const addToMyStocks = async (stock) => {
  206. if (!checkLogin()) {
  207. common_vendor.index.showModal({
  208. title: "登录提示",
  209. content: "添加自选需要登录,是否前往登录?",
  210. confirmText: "去登录",
  211. cancelText: "取消",
  212. success: (res) => {
  213. if (res.confirm) {
  214. common_vendor.index.navigateTo({ url: "/pages/login/login" });
  215. }
  216. }
  217. });
  218. return;
  219. }
  220. try {
  221. common_vendor.index.showLoading({ title: "添加中..." });
  222. let currentPrice = null;
  223. try {
  224. const quoteRes = await utils_api.getStockQuotes(stock.code);
  225. if (quoteRes.code === 200 && quoteRes.data && quoteRes.data.length > 0) {
  226. currentPrice = quoteRes.data[0].currentPrice;
  227. }
  228. } catch (e) {
  229. console.error("获取行情数据失败:", e);
  230. }
  231. const addRes = await utils_api.addUserStock({
  232. stockCode: stock.code,
  233. stockName: stock.name,
  234. currentPrice,
  235. poolType: 1
  236. // 超短池
  237. });
  238. common_vendor.index.hideLoading();
  239. if (addRes.code === 200 && addRes.data === true) {
  240. common_vendor.index.showToast({ title: "添加成功", icon: "success" });
  241. } else if (addRes.code === 200 && addRes.data === false) {
  242. common_vendor.index.showToast({ title: "样本已存在", icon: "none" });
  243. } else {
  244. common_vendor.index.showToast({ title: addRes.message || "添加失败", icon: "none" });
  245. }
  246. } catch (e) {
  247. common_vendor.index.hideLoading();
  248. console.error("添加样本失败:", e);
  249. common_vendor.index.showToast({ title: "添加失败", icon: "none" });
  250. }
  251. };
  252. common_vendor.onLoad(() => {
  253. console.log("[超短池] onLoad");
  254. isPageVisible.value = true;
  255. checkPurchaseStatus();
  256. startSubscriptionRefresh();
  257. });
  258. common_vendor.onShow(() => {
  259. console.log("[超短池] onShow");
  260. isPageVisible.value = true;
  261. checkPurchaseStatus();
  262. startSubscriptionRefresh();
  263. common_vendor.index.setNavigationBarTitle({ title: "量化交易大师" });
  264. });
  265. common_vendor.onHide(() => {
  266. console.log("[超短池] onHide");
  267. isPageVisible.value = false;
  268. stopAutoRefresh();
  269. stopSubscriptionRefresh();
  270. });
  271. common_vendor.onUnmounted(() => {
  272. isPageVisible.value = false;
  273. stopAutoRefresh();
  274. stopSubscriptionRefresh();
  275. });
  276. return (_ctx, _cache) => {
  277. return common_vendor.e({
  278. a: common_vendor.p({
  279. successRate: performanceStats.successRate,
  280. profitRate: performanceStats.avgTrend,
  281. totalTrades: performanceStats.totalCount
  282. }),
  283. b: !isPurchased.value
  284. }, !isPurchased.value ? {
  285. c: common_vendor.o(showPurchaseModal)
  286. } : {
  287. d: common_vendor.f(stockList.value, (stock, index, i0) => {
  288. return {
  289. a: common_vendor.t(stock.name),
  290. b: common_vendor.t(stock.code),
  291. c: common_vendor.t(stock.currentPrice || "-"),
  292. d: common_vendor.t(stock.changePercent || "-"),
  293. e: common_vendor.n(getChangeClass(stock.changePercent)),
  294. f: common_vendor.o(($event) => addToMyStocks(stock), index),
  295. g: index
  296. };
  297. })
  298. }, {
  299. e: common_vendor.o(onDateChange),
  300. f: common_vendor.p({
  301. poolType: 1,
  302. canSearch: true
  303. }),
  304. g: common_vendor.o(closePurchaseModal),
  305. h: common_vendor.o(handlePurchase),
  306. i: common_vendor.p({
  307. visible: showModal.value,
  308. icon: "💰",
  309. title: "打赏解锁",
  310. description: "支持作者,解锁今日超短池内容",
  311. amountLabel: "打赏金额:",
  312. amount: shortPrice.value
  313. })
  314. });
  315. };
  316. }
  317. };
  318. const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao/gupiao-wx/src/pages/pool/pool.vue"]]);
  319. wx.createPage(MiniProgramPage);