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.

474 lines
9.7 KiB

  1. <!-- 自选股页面 -->
  2. <template>
  3. <view class="container">
  4. <!-- 自定义导航栏 -->
  5. <view class="custom-navbar">
  6. <view class="navbar-content">
  7. <view class="navbar-left">
  8. <view class="back-btn" @click="goBack">
  9. <image class="back-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/e5c501fd23303533622fadce8dedd6a0.png" mode="aspectFit"></image>
  10. </view>
  11. </view>
  12. <view class="navbar-center">
  13. <text class="navbar-title">我的自选</text>
  14. </view>
  15. <view class="navbar-right">
  16. <image
  17. class="navbar-btn"
  18. src="https://d31zlh4on95l9h.cloudfront.net/images/ba5c8a2eda065274e868bcd9b2d7d914.png"
  19. @click="onFirstButtonClick"
  20. mode="aspectFit"
  21. ></image>
  22. <image
  23. class="navbar-btn"
  24. src="https://d31zlh4on95l9h.cloudfront.net/images/a4ae8952aeae90dac6d2b4c221c65fa9.png"
  25. @click="onSecondButtonClick"
  26. mode="aspectFit"
  27. ></image>
  28. </view>
  29. </view>
  30. </view>
  31. <!-- 页面内容 -->
  32. <view class="page-content">
  33. <!-- 分组标签 -->
  34. <view class="group-tabs" v-if="stockGroups.length > 0">
  35. <scroll-view class="tabs-scroll" scroll-x="true" show-scrollbar="false">
  36. <view class="tabs-container">
  37. <view
  38. v-for="group in stockGroups"
  39. :key="group.id"
  40. :class="['tab-item', { 'active': currentGroupId === group.id }]"
  41. @click="switchGroup(group.id)"
  42. >
  43. <text class="tab-text">{{ group.name }}</text>
  44. </view>
  45. </view>
  46. </scroll-view>
  47. </view>
  48. <!-- 股票列表 -->
  49. <view class="stock-list">
  50. <view v-if="loading" class="loading-container">
  51. <text class="loading-text">加载中...</text>
  52. </view>
  53. <view v-else-if="stockList.length === 0" class="empty-container">
  54. <image
  55. class="empty-image"
  56. src="https://d31zlh4on95l9h.cloudfront.net/images/f5a9bd32c81bc7cca47252b51357c12f.png"
  57. mode="aspectFit"
  58. ></image>
  59. <text class="empty-text">暂无数据~</text>
  60. </view>
  61. <view v-else>
  62. <view
  63. v-for="stock in stockList"
  64. :key="stock.id"
  65. class="stock-item"
  66. >
  67. <view class="stock-info">
  68. <text class="stock-name">{{ stock.name || stock.code }}</text>
  69. <text class="stock-code">{{ stock.code }}</text>
  70. </view>
  71. <view class="stock-price">
  72. <text class="price">{{ stock.price || '--' }}</text>
  73. <text :class="['change', stock.change >= 0 ? 'up' : 'down']">
  74. {{ stock.change >= 0 ? '+' : '-' }}{{ stock.change || '--' }}
  75. </text>
  76. </view>
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </template>
  83. <script>
  84. import { getUserStockGroupList, addUserStockGroup, getUserStockList } from '@/api/home/mySelections.js'
  85. export default {
  86. data() {
  87. return {
  88. // 分组数据
  89. stockGroups: [],
  90. // 当前选中的分组ID
  91. currentGroupId: null,
  92. // 当前分组下的股票列表
  93. stockList: [],
  94. // 加载状态
  95. loading: false
  96. }
  97. },
  98. onLoad() {
  99. this.loadStockGroups()
  100. },
  101. methods: {
  102. // 加载股票分组
  103. async loadStockGroups() {
  104. this.loading = true
  105. try {
  106. getUserStockGroupList(
  107. (response) => {
  108. console.log('获取分组成功:', response)
  109. if (response.code === 200 && response.data) {
  110. // 按ID排序,ID小的排在前面
  111. this.stockGroups = response.data.sort((a, b) => a.id - b.id)
  112. // 如果有分组,默认选中第一个
  113. if (this.stockGroups.length > 0) {
  114. this.currentGroupId = this.stockGroups[0].id
  115. this.loadStocksByGroup(this.currentGroupId)
  116. } else {
  117. // 如果没有分组,创建默认分组
  118. this.createDefaultGroup()
  119. }
  120. }
  121. },
  122. (error) => {
  123. console.error('获取分组失败:', error)
  124. // 如果获取失败,也尝试创建默认分组
  125. this.createDefaultGroup()
  126. }
  127. )
  128. } catch (error) {
  129. console.error('加载分组异常:', error)
  130. } finally {
  131. this.loading = false
  132. }
  133. },
  134. // 创建默认分组
  135. createDefaultGroup() {
  136. addUserStockGroup(
  137. (response) => {
  138. console.log('创建默认分组成功:', response)
  139. // 重新加载分组列表
  140. this.loadStockGroups()
  141. },
  142. (error) => {
  143. console.error('创建默认分组失败:', error)
  144. },
  145. { name: '默认分组' }
  146. )
  147. },
  148. // 根据分组ID加载股票列表
  149. loadStocksByGroup(groupId) {
  150. if (!groupId) return
  151. getUserStockList(
  152. (response) => {
  153. console.log('获取股票列表成功:', response)
  154. if (response.code === 200 && response.data && response.data.records) {
  155. // 股票列表在data.records中,根据groupId过滤
  156. this.stockList = response.data.records.filter(stock => stock.groupId === groupId)
  157. } else {
  158. this.stockList = []
  159. }
  160. },
  161. (error) => {
  162. console.error('获取股票列表失败:', error)
  163. this.stockList = []
  164. },
  165. { groupId: groupId }
  166. )
  167. },
  168. // 切换分组
  169. switchGroup(groupId) {
  170. if (this.currentGroupId === groupId) return
  171. this.currentGroupId = groupId
  172. this.loadStocksByGroup(groupId)
  173. },
  174. // 创建新分组
  175. async createNewGroup(groupName) {
  176. if (!groupName) {
  177. uni.showToast({
  178. title: '分组名称不能为空',
  179. icon: 'none'
  180. })
  181. return
  182. }
  183. uni.showLoading({
  184. title: '创建中...'
  185. })
  186. console.log('开始请求创建分组接口')
  187. try {
  188. const response = await addUserStockGroup(null, null, {
  189. name: groupName
  190. })
  191. console.log('创建分组接口返回:', response)
  192. if (response.code === 200) {
  193. uni.showToast({
  194. title: '创建成功',
  195. icon: 'success'
  196. })
  197. // 重新加载分组列表
  198. await this.loadStockGroups()
  199. // 切换到新创建的分组
  200. if (response.data && response.data.id) {
  201. this.switchGroup(response.data.id)
  202. }
  203. } else {
  204. uni.showToast({
  205. title: response.message || '创建失败',
  206. icon: 'none'
  207. })
  208. }
  209. } catch (error) {
  210. console.error('创建分组失败:', error)
  211. uni.showToast({
  212. title: '创建失败,请重试',
  213. icon: 'none'
  214. })
  215. } finally {
  216. // 确保在所有情况下都隐藏加载状态
  217. uni.hideLoading()
  218. }
  219. },
  220. // 返回上一页
  221. goBack() {
  222. uni.navigateBack()
  223. },
  224. // 第一个按钮点击事件 - 创建分组
  225. onFirstButtonClick() {
  226. uni.showModal({
  227. title: '创建分组',
  228. content: '请输入分组名称',
  229. editable: true,
  230. placeholderText: '请输入分组名称',
  231. success: (res) => {
  232. if (res.confirm && res.content) {
  233. this.createNewGroup(res.content.trim())
  234. }
  235. }
  236. })
  237. },
  238. // 第二个按钮点击事件
  239. onSecondButtonClick() {
  240. console.log('第二个按钮被点击')
  241. // 这里可以添加具体的功能逻辑
  242. }
  243. }
  244. }
  245. </script>
  246. <style>
  247. .container {
  248. width: 100%;
  249. height: 100vh;
  250. background-color: #f5f5f5;
  251. }
  252. /* 自定义导航栏 */
  253. .custom-navbar {
  254. position: fixed;
  255. top: 0;
  256. left: 0;
  257. right: 0;
  258. z-index: 999;
  259. background-color: #ffffff;
  260. border-bottom: 1px solid #e5e5e5;
  261. }
  262. .navbar-content {
  263. display: flex;
  264. align-items: center;
  265. justify-content: space-between;
  266. height: 44px;
  267. padding: 0 15px;
  268. /* 适配状态栏高度 */
  269. padding-top: var(--status-bar-height, 20px);
  270. min-height: calc(44px + var(--status-bar-height, 20px));
  271. }
  272. .navbar-left {
  273. flex: 0 0 auto;
  274. display: flex;
  275. align-items: center;
  276. }
  277. .back-btn {
  278. width: 40px;
  279. height: 40px;
  280. display: flex;
  281. align-items: center;
  282. justify-content: center;
  283. }
  284. .back-icon {
  285. width: 24px;
  286. height: 24px;
  287. }
  288. .navbar-center {
  289. flex: 1;
  290. display: flex;
  291. align-items: center;
  292. justify-content: center;
  293. }
  294. .navbar-title {
  295. font-size: 18px;
  296. font-weight: 500;
  297. color: #333333;
  298. }
  299. .navbar-right {
  300. flex: 0 0 auto;
  301. display: flex;
  302. align-items: center;
  303. gap: 10px;
  304. }
  305. .navbar-btn {
  306. width: 24px;
  307. height: 24px;
  308. }
  309. /* 页面内容 */
  310. .page-content {
  311. padding-top: calc(44px + var(--status-bar-height, 20px) + 20px);
  312. min-height: calc(100vh - 44px - var(--status-bar-height, 20px) - 20px);
  313. }
  314. /* 分组标签样式 */
  315. .group-tabs {
  316. background-color: #ffffff;
  317. padding: 10px 0;
  318. }
  319. .tabs-scroll {
  320. width: 100%;
  321. }
  322. .tabs-container {
  323. display: flex;
  324. padding: 0 15px;
  325. white-space: nowrap;
  326. }
  327. .tab-item {
  328. flex-shrink: 0;
  329. padding: 8px 16px;
  330. margin-right: 10px;
  331. border-radius: 4px;
  332. background-color: #ff3b30;
  333. transition: all 0.3s ease;
  334. }
  335. .tab-item.active {
  336. background-color: #ff3b30;
  337. opacity: 1;
  338. }
  339. .tab-text {
  340. font-size: 14px;
  341. color: #ffffff;
  342. white-space: nowrap;
  343. font-weight: 500;
  344. }
  345. .tab-item.active .tab-text {
  346. color: #ffffff;
  347. font-weight: 500;
  348. }
  349. /* 股票列表样式 */
  350. .stock-list {
  351. flex: 1;
  352. padding: 15px;
  353. }
  354. .loading-container,
  355. .empty-container {
  356. display: flex;
  357. flex-direction: column;
  358. align-items: center;
  359. justify-content: center;
  360. padding: 60px 20px;
  361. }
  362. .loading-text {
  363. font-size: 16px;
  364. color: #666666;
  365. }
  366. .empty-image {
  367. width: 120px;
  368. height: 120px;
  369. margin-bottom: 20px;
  370. }
  371. .empty-text {
  372. font-size: 16px;
  373. color: #999999;
  374. }
  375. .stock-item {
  376. display: flex;
  377. align-items: center;
  378. justify-content: space-between;
  379. padding: 15px 0;
  380. border-bottom: 1px solid #f0f0f0;
  381. background-color: #ffffff;
  382. margin-bottom: 8px;
  383. border-radius: 8px;
  384. padding: 15px;
  385. }
  386. .stock-item:last-child {
  387. margin-bottom: 0;
  388. }
  389. .stock-info {
  390. flex: 1;
  391. display: flex;
  392. flex-direction: column;
  393. }
  394. .stock-name {
  395. font-size: 16px;
  396. font-weight: 500;
  397. color: #333333;
  398. margin-bottom: 4px;
  399. }
  400. .stock-code {
  401. font-size: 12px;
  402. color: #999999;
  403. }
  404. .stock-price {
  405. display: flex;
  406. flex-direction: row;
  407. align-items: center;
  408. gap: 8px;
  409. }
  410. .price {
  411. font-size: 16px;
  412. font-weight: 500;
  413. color: #333333;
  414. }
  415. .change {
  416. font-size: 12px;
  417. font-weight: 500;
  418. }
  419. .change.up {
  420. color: #ff3b30;
  421. }
  422. .change.down {
  423. color: #34c759;
  424. }
  425. </style>