You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
14 KiB

4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
  1. <template>
  2. <LoginPrompt ref="loginPrompt"></LoginPrompt>
  3. <view class="main">
  4. <!-- 顶部状态栏占位 -->
  5. <view class="top" :style="{ height: iSMT + 'px' }"></view>
  6. <!-- 标题图标部分 -->
  7. <deepExploration_header></deepExploration_header>
  8. <view class="search">
  9. <input v-model="stockName" class="searchInput" type="text" placeholder="请输入股票名称、股票代码"
  10. placeholder-style="color: #A6A6A6; font-size: 28rpx; " />
  11. <image @click="searchStock" class="seachIcon" src="/static/deepExploration-images/search.png"
  12. mode="aspectFill"></image>
  13. </view>
  14. <!-- 四大功能模块 -->
  15. <view class="select">
  16. <view class="selectItem" @click="toMain('主力追踪')">
  17. <image class="img" src="/static/deepExploration-images/icon3.png" mode="aspectFill"></image>
  18. <view class="txt">主力追踪</view>
  19. </view>
  20. <view class="selectItem" @click="toMain('主力雷达')">
  21. <image class="img" src="/static/deepExploration-images/icon2.png" mode="aspectFill"></image>
  22. <view class="txt">主力雷达</view>
  23. </view>
  24. <view class="selectItem" @click="toMain('主力解码')">
  25. <image class="img" src="/static/deepExploration-images/icon1.png" mode="aspectFill"></image>
  26. <view class="txt">主力解码</view>
  27. </view>
  28. <view class="selectItem" @click="toMain('主力资金流')">
  29. <image class="img" src="/static/deepExploration-images/icon4.png" mode="aspectFill"></image>
  30. <view class="txt">主力资金流</view>
  31. </view>
  32. </view>
  33. <!-- 灰色间隔 -->
  34. <view class="gap"></view>
  35. <!-- 选股策略 -->
  36. <view class="stockSelection">
  37. <view class="stockSelection_top">
  38. <view class="txt">
  39. <text>选股策略</text>
  40. </view>
  41. <view class="viewAll" @click="viewAll">
  42. <text>查看全部</text>
  43. </view>
  44. </view>
  45. <view class="stockSelection_content">
  46. <view class="selectionItem" @click="viewAll">
  47. <view class="header">
  48. <view class="left">
  49. <image src="/static/deepExploration-images/plus.png" mode="aspectFill"></image>
  50. <text>抄底卖顶</text>
  51. </view>
  52. <view class="right">
  53. <image src="/static/deepExploration-images/Americle.png" mode="aspectFill"></image>
  54. <text>美股</text>
  55. </view>
  56. </view>
  57. <view class="content">
  58. <view class="contentTitle">
  59. <view class="contentTitle_name">股票名称</view>
  60. <view class="contentTitle_close">最新收盘价</view>
  61. <view class="contentTitle_price">选股价格</view>
  62. </view>
  63. <view class="contentItem">
  64. <view class="row" v-for="(item, index) in stockData" :key="index">
  65. <view class="nameItem">{{ item.tscode }}</view>
  66. <view class="closeItem">{{ item.close }}</view>
  67. <view class="priceItem">{{ item.preClose }}</view>
  68. </view>
  69. </view>
  70. </view>
  71. </view>
  72. </view>
  73. <view class="stockSelection_content">
  74. <view class="selectionItem" @click="viewAll">
  75. <view class="header">
  76. <view class="left">
  77. <image src="/static/deepExploration-images/plus.png" mode="aspectFill"></image>
  78. <text>波段行情</text>
  79. </view>
  80. <view class="right">
  81. <image src="/static/deepExploration-images/Americle.png" mode="aspectFill"></image>
  82. <text>美股</text>
  83. </view>
  84. </view>
  85. <view class="content">
  86. <view class="contentTitle">
  87. <view class="contentTitle_name">股票名称</view>
  88. <view class="contentTitle_close">最新收盘价</view>
  89. <view class="contentTitle_price">选股价格</view>
  90. </view>
  91. <view class="contentItem">
  92. <view class="row" v-for="(item, index) in stockDataByName" :key="index">
  93. <view class="nameItem">{{ item.tscode }}</view>
  94. <view class="closeItem">{{ item.close }}</view>
  95. <view class="priceItem">{{ item.preClose }}</view>
  96. </view>
  97. </view>
  98. </view>
  99. </view>
  100. </view>
  101. </view>
  102. <footerBar class="static-footer" :type="type"></footerBar>
  103. </view>
  104. </template>
  105. <script setup>
  106. import {
  107. ref,
  108. onMounted
  109. } from 'vue'
  110. import footerBar from '@/components/footerBar.vue'
  111. import deepExploration_header from '@/components/deepExploration_header.vue'
  112. import {
  113. stocSelectApi,
  114. stocSelectByNameApi
  115. } from '@/api/deepExploration/deepExploration.js'
  116. import {
  117. useUserStore
  118. } from '@/stores/modules/userInfo.js'
  119. import {
  120. getUserInfo
  121. } from "@/api/member"
  122. const userInfo = ref({})
  123. const type = ref("deepExploration");
  124. const iSMT = ref(0);
  125. //查看全部选股策略
  126. const toMain = (val) => {
  127. if (val == "主力追踪") {
  128. uni.navigateTo({
  129. url: "/pages/deepExploration/MainForceActions?index=1",
  130. });
  131. } else if (val == "主力雷达") {
  132. uni.navigateTo({
  133. url: "/pages/deepExploration/MainForceActions?index=2",
  134. });
  135. } else if (val == "主力解码") {
  136. uni.navigateTo({
  137. url: "/pages/deepExploration/MainForceActions?index=3",
  138. });
  139. } else if (val == "主力资金流") {
  140. uni.navigateTo({
  141. url: "/pages/deepExploration/MainForceActions?index=4",
  142. });
  143. }
  144. };
  145. const stockName = ref("");
  146. //搜索股票
  147. const searchStock = () => {
  148. console.log("搜索参数:", stockName.value);
  149. console.log(userInfo.value.isVisitor);
  150. if (userInfo.value.isVisitor) {
  151. uni.showToast({
  152. title: '请登录',
  153. icon: 'none'
  154. })
  155. return
  156. }
  157. uni.navigateTo({
  158. url: `/pages/deepExploration/MainForceActions?stockName=${stockName.value}`,
  159. });
  160. };
  161. //查看全部选股策略
  162. const viewAll = () => {
  163. uni.navigateTo({
  164. url: '/pages/deepExploration/stockSelectDetail'
  165. })
  166. }
  167. //选股策略数据(接口填充)
  168. const stockData = ref([]);
  169. const stockDataByName = ref([]); // 安徽板块数据(只显示前三条)
  170. // 加载选股策略(接口)
  171. const loadStockSelection = async () => {
  172. try {
  173. const res = await stocSelectApi({
  174. language: 'cn',
  175. size: 3
  176. })
  177. // console.log('选股策略接口响应原始:', typeof res === 'object' ? JSON.stringify(res) : res)
  178. const raw = res?.data
  179. const listCandidates = [
  180. raw?.list,
  181. raw?.data?.list,
  182. raw?.data?.rows,
  183. raw?.rows,
  184. Array.isArray(raw) ? raw : null
  185. ].filter(Array.isArray)
  186. let list = listCandidates.length ? listCandidates[0] : []
  187. // 若是对象包含多个数组(如不同市场),进行扁平化
  188. if ((!Array.isArray(list) || !list.length) && raw && typeof raw === 'object' && !Array.isArray(raw)) {
  189. const arrays = Object.values(raw).filter(Array.isArray)
  190. if (arrays.length) {
  191. list = arrays.flat()
  192. }
  193. }
  194. if (Array.isArray(list) && list.length) {
  195. const mapped = list.map(item => ({
  196. tscode: item.tsCode ?? item.tscode ?? item.code ?? '',
  197. close: item.close ?? item.lastClose ?? '',
  198. preClose: item.preClose ?? item.preclose ?? item.prevClose ?? ''
  199. }))
  200. stockData.value = mapped.slice(0, 3)
  201. console.log('选股策略列表长度:', stockData.value.length, '首项:', stockData.value[0])
  202. } else {
  203. console.warn('选股策略接口返回空列表或结构不匹配', raw)
  204. }
  205. } catch (e) {
  206. console.error('选股策略接口调用失败', e)
  207. uni.showToast({
  208. title: '选股策略加载失败',
  209. icon: 'none'
  210. })
  211. }
  212. }
  213. // 安徽板块:按名称查询,仅前三条,字段映射不变
  214. const loadStockSelectionByName = async () => {
  215. try {
  216. const res = await stocSelectByNameApi({
  217. name: '安徽'
  218. })
  219. const raw = res?.data
  220. const dataObj = raw?.data || raw
  221. let list = []
  222. if (Array.isArray(dataObj)) {
  223. list = dataObj
  224. } else if (dataObj && typeof dataObj === 'object') {
  225. const target = dataObj['安徽']
  226. if (Array.isArray(target)) {
  227. list = target
  228. } else {
  229. const firstArr = Object.values(dataObj).find(v => Array.isArray(v))
  230. if (Array.isArray(firstArr)) list = firstArr
  231. }
  232. }
  233. if (Array.isArray(list) && list.length) {
  234. const mapped = list.map(item => ({
  235. tscode: item.tsCode ?? item.tscode ?? item.code ?? '',
  236. close: item.close ?? item.lastClose ?? '',
  237. preClose: item.preClose ?? item.preclose ?? item.prevClose ?? ''
  238. }))
  239. stockDataByName.value = mapped.slice(0, 3)
  240. console.log('安徽板块列表长度:', stockDataByName.value.length, '首项:', stockDataByName.value[0])
  241. } else {
  242. console.warn('按名称(安徽)接口返回空列表或结构不匹配', raw)
  243. }
  244. } catch (e) {
  245. console.error('按名称(安徽)接口调用失败', e)
  246. uni.showToast({
  247. title: '安徽板块加载失败',
  248. icon: 'none'
  249. })
  250. }
  251. }
  252. onMounted(() => {
  253. // 状态栏高度
  254. iSMT.value = uni.getSystemInfoSync().statusBarHeight;
  255. // 调用接口填充数据
  256. getUserInfo()
  257. loadStockSelection()
  258. loadStockSelectionByName() // 安徽板块填充
  259. userInfo.value = useUserStore().userInfo
  260. console.log('用户信息', userInfo.value);
  261. })
  262. </script>
  263. <style scoped lang="scss">
  264. .main {
  265. width: 100%;
  266. min-height: 100vh; // 保持至少一屏高度
  267. height: auto; // 允许内容超出时自动增高
  268. overflow-y: auto; // 开启纵向滚动
  269. background-color: #fff;
  270. padding-bottom: 120rpx; // 为固定底栏预留空间,避免遮挡
  271. .search {
  272. position: relative;
  273. display: flex;
  274. align-items: center;
  275. background-color: #f3f3f3;
  276. width: calc(100% - 60rpx);
  277. height: 80rpx;
  278. border-radius: 50rpx;
  279. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  280. padding: 0 40rpx;
  281. margin: 15rpx 30rpx 0 30rpx;
  282. .seachIcon {
  283. position: absolute;
  284. right: 50rpx;
  285. width: 32rpx;
  286. height: 32rpx;
  287. }
  288. .searchInput {
  289. color: #111;
  290. width: 100%;
  291. }
  292. }
  293. .select {
  294. display: flex;
  295. padding: 60rpx 10rpx 30rpx 30rpx;
  296. gap: 70rpx;
  297. align-items: center;
  298. justify-content: center;
  299. .selectItem {
  300. .img {
  301. width: 80rpx;
  302. height: 80rpx;
  303. display: block;
  304. margin: 0 auto;
  305. }
  306. .txt {
  307. color: #6a6a6a;
  308. font-family: "PingFang SC";
  309. font-size: 11px;
  310. font-style: normal;
  311. font-weight: 400;
  312. line-height: 14.5px;
  313. margin-top: 13rpx;
  314. white-space: nowrap;
  315. }
  316. }
  317. }
  318. .gap {
  319. width: 100%;
  320. height: 15rpx;
  321. background-color: #f3f3f3;
  322. }
  323. .stockSelection {
  324. width: 100%;
  325. padding: 32rpx 15rpx;
  326. .stockSelection_top {
  327. display: flex;
  328. justify-content: space-between;
  329. .txt {
  330. color: #000000;
  331. font-family: "PingFang SC";
  332. font-size: 38rpx;
  333. font-style: normal;
  334. font-weight: 400;
  335. line-height: 50rpx;
  336. }
  337. .viewAll {
  338. background-color: #000000;
  339. border-radius: 10rpx;
  340. padding: 6rpx 20rpx;
  341. color: #ffffff;
  342. font-family: "PingFang SC";
  343. font-size: 24rpx;
  344. font-style: normal;
  345. font-weight: 100;
  346. line-height: 29rpx;
  347. height: 40rpx;
  348. width: 140rpx;
  349. }
  350. }
  351. .stockSelection_content {
  352. .selectionItem {
  353. background-color: #f3f3f3;
  354. padding: 30rpx 15rpx 17rpx 30rpx;
  355. border-radius: 30rpx;
  356. margin-top: 30rpx;
  357. .header {
  358. display: flex;
  359. justify-content: space-between;
  360. align-items: center;
  361. .left {
  362. display: flex;
  363. justify-content: space-between;
  364. align-items: center;
  365. image {
  366. display: flex;
  367. justify-content: center;
  368. align-items: center;
  369. width: 15rpx;
  370. height: 15rpx;
  371. }
  372. text {
  373. margin-left: 15rpx;
  374. color: #000000;
  375. font-family: "PingFang SC";
  376. font-size: 28rpx;
  377. font-style: normal;
  378. font-weight: 400;
  379. line-height: 18.5px;
  380. }
  381. }
  382. .right {
  383. display: flex;
  384. justify-content: space-between;
  385. align-items: center;
  386. border-radius: 15rpx;
  387. background-color: #ffffff;
  388. padding: 6rpx 20rpx;
  389. image {
  390. display: flex;
  391. justify-content: center;
  392. align-items: center;
  393. width: 40rpx;
  394. height: 26.5rpx;
  395. }
  396. text {
  397. margin-left: 10rpx;
  398. color: #6a6a6a;
  399. font-family: "PingFang SC";
  400. font-size: 18rpx;
  401. font-style: normal;
  402. font-weight: 400;
  403. line-height: 24rpx;
  404. }
  405. }
  406. }
  407. .content {
  408. .contentTitle {
  409. display: flex;
  410. color: #6a6a6a;
  411. font-family: "PingFang SC";
  412. font-size: 11px;
  413. font-style: normal;
  414. font-weight: 400;
  415. line-height: 14.5px;
  416. margin-top: 24rpx;
  417. margin-bottom: 20rpx;
  418. .contentTitle_name {
  419. width: 100rpx;
  420. }
  421. .contentTitle_close {
  422. width: 130rpx;
  423. margin-left: 260rpx;
  424. }
  425. .contentTitle_price {
  426. width: 120rpx;
  427. margin-left: 60rpx;
  428. }
  429. }
  430. .contentItem {
  431. .row {
  432. display: flex;
  433. box-shadow: 0 -2rpx 5rpx rgba(0, 0, 0, 0.05);
  434. padding: 10rpx 0;
  435. margin-bottom: 10rpx;
  436. .nameItem {
  437. width: 260rpx;
  438. white-space: nowrap;
  439. overflow: hidden;
  440. text-overflow: ellipsis;
  441. color: #000000;
  442. font-family: "PingFang SC";
  443. font-size: 13px;
  444. font-style: normal;
  445. font-weight: 400;
  446. line-height: 17.5px;
  447. }
  448. .closeItem {
  449. width: 120rpx;
  450. margin-left: 100rpx;
  451. color: #25ba5d;
  452. font-family: "PingFang SC";
  453. font-size: 13px;
  454. font-style: normal;
  455. font-weight: 400;
  456. line-height: 17.5px;
  457. }
  458. .priceItem {
  459. width: 120rpx;
  460. margin-left: 73rpx;
  461. color: #25ba5d;
  462. font-family: "PingFang SC";
  463. font-size: 13px;
  464. font-style: normal;
  465. font-weight: 400;
  466. line-height: 17.5px;
  467. }
  468. }
  469. }
  470. }
  471. }
  472. }
  473. }
  474. .static-footer {
  475. position: fixed;
  476. bottom: 0;
  477. left: 0;
  478. right: 0;
  479. }
  480. }
  481. * {
  482. box-sizing: border-box;
  483. }
  484. </style>