rank.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. StockListItem();
  7. }
  8. const StockListItem = () => "../../components/StockListItem.js";
  9. const _sfc_main = {
  10. __name: "rank",
  11. setup(__props) {
  12. const isLoggedIn = common_vendor.ref(false);
  13. const myStocks = common_vendor.ref([]);
  14. const viewMode = common_vendor.ref("list");
  15. const swiperIndex = common_vendor.ref(0);
  16. const isLoading = common_vendor.ref(false);
  17. const lastLoadTime = common_vendor.ref(0);
  18. const CACHE_DURATION = 5e3;
  19. const setViewMode = (mode) => {
  20. viewMode.value = mode;
  21. swiperIndex.value = mode === "list" ? 0 : 1;
  22. };
  23. const onSwiperChange = (e) => {
  24. const index = e.detail.current;
  25. swiperIndex.value = index;
  26. viewMode.value = index === 0 ? "list" : "table";
  27. };
  28. const indexData = common_vendor.ref({
  29. stockCode: "000001",
  30. stockName: "上证指数",
  31. currentPrice: null,
  32. priceChange: null,
  33. changePercent: null
  34. });
  35. let refreshTimer = null;
  36. const fetchIndexData = async () => {
  37. try {
  38. const res = await utils_api.getIndexQuote("000001");
  39. if (res.code === 200 && res.data) {
  40. indexData.value = { ...indexData.value, ...res.data };
  41. }
  42. } catch (e) {
  43. console.error("[上证指数] 获取失败:", e.message);
  44. }
  45. };
  46. const formatIndexPrice = (price) => {
  47. if (!price)
  48. return "--";
  49. return parseFloat(price).toFixed(2);
  50. };
  51. const formatPrice = (price) => {
  52. if (!price)
  53. return "--";
  54. return parseFloat(price).toFixed(2);
  55. };
  56. const getIndexChangeClass = (changePercent) => {
  57. if (!changePercent)
  58. return "";
  59. const str = String(changePercent).replace("%", "").replace("+", "");
  60. const value = parseFloat(str);
  61. if (value > 0)
  62. return "index-up";
  63. if (value < 0)
  64. return "index-down";
  65. return "";
  66. };
  67. const getProfitClass = (profitPercent) => {
  68. if (!profitPercent)
  69. return "";
  70. const str = String(profitPercent).replace("%", "").replace("+", "");
  71. const value = parseFloat(str);
  72. if (value > 0)
  73. return "profit-up";
  74. if (value < 0)
  75. return "profit-down";
  76. return "";
  77. };
  78. const getMarketTag = (code) => {
  79. if (code.startsWith("6"))
  80. return "沪";
  81. if (code.startsWith("0"))
  82. return "深";
  83. if (code.startsWith("3"))
  84. return "创";
  85. return "沪";
  86. };
  87. const getMarketClass = (code) => {
  88. if (code.startsWith("6"))
  89. return "market-sh";
  90. if (code.startsWith("0"))
  91. return "market-sz";
  92. if (code.startsWith("3"))
  93. return "market-cy";
  94. return "market-sh";
  95. };
  96. const loadMyStocks = async (forceRefresh = false) => {
  97. console.log("[我的股票] loadMyStocks 开始执行, isLoggedIn=", isLoggedIn.value, "forceRefresh=", forceRefresh);
  98. if (!isLoggedIn.value) {
  99. myStocks.value = [];
  100. stopAutoRefresh();
  101. return;
  102. }
  103. if (isLoading.value) {
  104. console.log("[我的股票] 正在加载中,跳过");
  105. return;
  106. }
  107. const now = Date.now();
  108. if (!forceRefresh && myStocks.value.length > 0 && now - lastLoadTime.value < CACHE_DURATION) {
  109. console.log("[我的股票] 使用缓存数据,只刷新行情");
  110. await fetchIndexData();
  111. await refreshAllQuotes();
  112. startAutoRefresh();
  113. return;
  114. }
  115. try {
  116. isLoading.value = true;
  117. if (myStocks.value.length === 0 || forceRefresh) {
  118. common_vendor.index.showLoading({ title: "加载中..." });
  119. }
  120. console.log("[我的股票] 调用 getUserStocks 接口");
  121. const res = await utils_api.getUserStocks();
  122. console.log("[我的股票] 服务器返回:", JSON.stringify(res));
  123. common_vendor.index.hideLoading();
  124. if (res.code === 200 && res.data) {
  125. myStocks.value = res.data.map((item) => ({
  126. code: item.stockCode,
  127. name: item.stockName,
  128. addPrice: item.addPrice,
  129. addDate: item.addDate,
  130. currentPrice: item.currentPrice,
  131. profitPercent: item.profitPercent,
  132. priceChange: item.priceChange,
  133. changePercent: item.changePercent,
  134. trendData: item.trendData
  135. }));
  136. lastLoadTime.value = Date.now();
  137. console.log("[我的股票] 加载完成, 股票数量:", myStocks.value.length);
  138. } else {
  139. myStocks.value = [];
  140. console.log("[我的股票] 返回数据为空");
  141. }
  142. await fetchIndexData();
  143. if (myStocks.value.length > 0) {
  144. await refreshAllQuotes();
  145. }
  146. startAutoRefresh();
  147. } catch (e) {
  148. common_vendor.index.hideLoading();
  149. console.error("[我的股票] 加载失败:", e);
  150. if (myStocks.value.length === 0) {
  151. myStocks.value = [];
  152. }
  153. startAutoRefresh();
  154. } finally {
  155. isLoading.value = false;
  156. }
  157. };
  158. const refreshAllQuotes = async () => {
  159. if (myStocks.value.length === 0)
  160. return;
  161. try {
  162. const codes = myStocks.value.map((stock) => stock.code).join(",");
  163. const quoteRes = await utils_api.getStockQuotes(codes);
  164. if (quoteRes.code === 200 && quoteRes.data && quoteRes.data.length > 0) {
  165. quoteRes.data.forEach((quoteData) => {
  166. const index = myStocks.value.findIndex((stock) => stock.code === quoteData.stockCode);
  167. if (index !== -1) {
  168. const stock = myStocks.value[index];
  169. stock.priceChange = quoteData.priceChange;
  170. stock.changePercent = quoteData.changePercent;
  171. stock.currentPrice = quoteData.currentPrice;
  172. stock.name = quoteData.stockName || stock.name;
  173. stock.trendData = quoteData.trendData || null;
  174. if (stock.addPrice && quoteData.currentPrice) {
  175. const addPrice = parseFloat(stock.addPrice);
  176. const currentPrice = parseFloat(quoteData.currentPrice);
  177. if (addPrice > 0) {
  178. const profit = ((currentPrice - addPrice) / addPrice * 100).toFixed(2);
  179. stock.profitPercent = profit >= 0 ? `+${profit}%` : `${profit}%`;
  180. }
  181. }
  182. }
  183. });
  184. }
  185. } catch (e) {
  186. console.error("[我的股票] 刷新异常:", e.message);
  187. }
  188. };
  189. const startAutoRefresh = () => {
  190. if (!isLoggedIn.value)
  191. return;
  192. stopAutoRefresh();
  193. const scheduleNextRefresh = () => {
  194. const delay = 3e3 + Math.random() * 1e3;
  195. refreshTimer = setTimeout(async () => {
  196. await fetchIndexData();
  197. if (myStocks.value.length > 0) {
  198. await refreshAllQuotes();
  199. }
  200. scheduleNextRefresh();
  201. }, delay);
  202. };
  203. scheduleNextRefresh();
  204. };
  205. const stopAutoRefresh = () => {
  206. if (refreshTimer) {
  207. clearTimeout(refreshTimer);
  208. refreshTimer = null;
  209. }
  210. };
  211. const goToLogin = () => {
  212. common_vendor.index.navigateTo({ url: "/pages/login/login" });
  213. };
  214. const handleStockClick = async (stockItem, idx) => {
  215. console.log("点击股票:", stockItem.name, idx);
  216. try {
  217. const quoteRes = await utils_api.getStockQuotes(stockItem.code);
  218. if (quoteRes.code === 200 && quoteRes.data && quoteRes.data.length > 0) {
  219. const quoteData = quoteRes.data[0];
  220. const stock = myStocks.value[idx];
  221. if (stock) {
  222. stock.priceChange = quoteData.priceChange;
  223. stock.changePercent = quoteData.changePercent;
  224. stock.currentPrice = quoteData.currentPrice;
  225. stock.name = quoteData.stockName || stock.name;
  226. stock.trendData = quoteData.trendData || null;
  227. if (stock.addPrice && quoteData.currentPrice) {
  228. const addPrice = parseFloat(stock.addPrice);
  229. const currentPrice = parseFloat(quoteData.currentPrice);
  230. if (addPrice > 0) {
  231. const profit = ((currentPrice - addPrice) / addPrice * 100).toFixed(2);
  232. stock.profitPercent = profit >= 0 ? `+${profit}%` : `${profit}%`;
  233. }
  234. }
  235. }
  236. }
  237. } catch (e) {
  238. console.error("刷新股票行情失败:", e);
  239. }
  240. common_vendor.index.showToast({ title: "股票详情功能开发中", icon: "none" });
  241. };
  242. const removeStock = async (idx) => {
  243. const stock = myStocks.value[idx];
  244. common_vendor.index.showModal({
  245. title: "确认删除",
  246. content: `确定要删除 ${stock.name} 吗?`,
  247. confirmText: "删除",
  248. cancelText: "取消",
  249. success: async (res) => {
  250. if (res.confirm) {
  251. try {
  252. await utils_api.deleteUserStock(stock.code);
  253. myStocks.value.splice(idx, 1);
  254. common_vendor.index.showToast({ title: "删除成功", icon: "success" });
  255. if (myStocks.value.length === 0) {
  256. stopAutoRefresh();
  257. }
  258. } catch (e) {
  259. console.error("删除失败:", e);
  260. common_vendor.index.showToast({ title: "删除失败", icon: "none" });
  261. }
  262. }
  263. }
  264. });
  265. };
  266. common_vendor.onLoad(() => {
  267. console.log("[我的股票] onLoad 触发");
  268. isLoggedIn.value = utils_auth.isLoggedIn();
  269. loadMyStocks(true);
  270. });
  271. common_vendor.onShow(() => {
  272. console.log("[我的股票] onShow 触发");
  273. const wasLoggedIn = isLoggedIn.value;
  274. isLoggedIn.value = utils_auth.isLoggedIn();
  275. if (wasLoggedIn !== isLoggedIn.value) {
  276. loadMyStocks(true);
  277. } else {
  278. loadMyStocks(false);
  279. }
  280. common_vendor.index.setNavigationBarTitle({ title: "量化交易大师" });
  281. });
  282. common_vendor.onHide(() => {
  283. stopAutoRefresh();
  284. });
  285. common_vendor.onUnload(() => {
  286. stopAutoRefresh();
  287. });
  288. return (_ctx, _cache) => {
  289. return common_vendor.e({
  290. a: common_vendor.t(formatIndexPrice(indexData.value.currentPrice)),
  291. b: common_vendor.n(getIndexChangeClass(indexData.value.changePercent)),
  292. c: common_vendor.t(indexData.value.priceChange || "--"),
  293. d: common_vendor.n(getIndexChangeClass(indexData.value.changePercent)),
  294. e: common_vendor.t(indexData.value.stockName || "上证指数"),
  295. f: common_vendor.t(indexData.value.changePercent || "--"),
  296. g: common_vendor.n(getIndexChangeClass(indexData.value.changePercent)),
  297. h: viewMode.value === "table" ? 1 : "",
  298. i: viewMode.value === "list" ? 1 : "",
  299. j: common_vendor.o(($event) => setViewMode("list")),
  300. k: viewMode.value === "table" ? 1 : "",
  301. l: common_vendor.o(($event) => setViewMode("table")),
  302. m: myStocks.value.length > 0
  303. }, myStocks.value.length > 0 ? {
  304. n: common_vendor.f(myStocks.value, (stock, index, i0) => {
  305. return {
  306. a: stock.code,
  307. b: common_vendor.o(($event) => removeStock(index), stock.code),
  308. c: common_vendor.o(($event) => handleStockClick(stock, index), stock.code),
  309. d: "2482292d-0-" + i0,
  310. e: common_vendor.p({
  311. stock,
  312. ["show-delete"]: true
  313. })
  314. };
  315. })
  316. } : {}, {
  317. o: myStocks.value.length > 0
  318. }, myStocks.value.length > 0 ? {
  319. p: common_vendor.f(myStocks.value, (stock, index, i0) => {
  320. return {
  321. a: common_vendor.t(stock.name),
  322. b: common_vendor.t(getMarketTag(stock.code)),
  323. c: common_vendor.n(getMarketClass(stock.code)),
  324. d: common_vendor.t(stock.code),
  325. e: common_vendor.t(stock.addDate || "--"),
  326. f: common_vendor.t(formatPrice(stock.addPrice)),
  327. g: common_vendor.t(stock.profitPercent || "--"),
  328. h: common_vendor.n(getProfitClass(stock.profitPercent)),
  329. i: stock.code,
  330. j: common_vendor.o(($event) => handleStockClick(stock, index), stock.code)
  331. };
  332. })
  333. } : {}, {
  334. q: swiperIndex.value,
  335. r: common_vendor.o(onSwiperChange),
  336. s: !isLoggedIn.value ? 1 : "",
  337. t: !isLoggedIn.value
  338. }, !isLoggedIn.value ? {
  339. v: common_vendor.o(goToLogin)
  340. } : {});
  341. };
  342. }
  343. };
  344. const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "D:/program/gupiao-wx/src/pages/rank/rank.vue"]]);
  345. wx.createPage(MiniProgramPage);