List.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <template>
  2. <view class="tab-content">
  3. <!-- 列表 -->
  4. <view v-if="list.length > 0" class="list">
  5. <view v-for="item in list" :key="item.id" class="col-12 m-b30">
  6. <view class="card card-action action-elevate action-border-primary cursor-pointer" :data-tooltip="t('vu.tooltip.t103')" @click="handleItemClick(item)">
  7. <view class="row g-0">
  8. <view class="col-md-3" v-if="item.coverImage">
  9. <view class="card-header border-0 p-0 m-2 position-relative overflow-hidden">
  10. <image v-if="item.coverImage" :src="imgUrl + item.coverImage" class="img-fluid rounded"
  11. mode="widthFix" />
  12. <view v-else class="placeholder-image"></view>
  13. </view>
  14. </view>
  15. <view class="col-md-9 py-3 d-flex flex-column">
  16. <view class="card-body px-3 py-2">
  17. <h5>
  18. <span class="text-2xs text-body p-text"><i class="icon-calendar text-primary"></i> {{
  19. formatDate(item.deliveryTime) }}</span><br>
  20. <text class="text-dark h5">{{ item.title }}</text>
  21. </h5>
  22. <p class="p-text">{{ item.subTitle }}</p>
  23. </view>
  24. </view>
  25. </view>
  26. </view>
  27. </view>
  28. </view>
  29. <!-- 空状态 -->
  30. <view v-else-if="!loading && list.length === 0" class="list-empty-state empty">
  31. <cwg-empty-state />
  32. </view>
  33. <view class="table-loading-mask">
  34. <uni-loading v-if="loading" />
  35. </view>
  36. </view>
  37. </template>
  38. <script setup>
  39. import { ref, watch, computed } from 'vue'
  40. import { useI18n } from 'vue-i18n'
  41. import Config from '@/config/index'
  42. const { locale, t } = useI18n()
  43. const props = defineProps({
  44. fetchData: { type: Function, required: true },
  45. queryParams: { type: Object, default: () => ({}) },
  46. pageSize: { type: Number, default: 100 },
  47. type: { type: Number, default: 0 },
  48. immediate: { type: Boolean, default: true },
  49. })
  50. const imgUrl = computed(() => props.queryParams.tag !== 3 ? Config.Host80 : Config.Host05)
  51. const list = ref([])
  52. const page = ref(1)
  53. const loading = ref(false)
  54. const loadingMore = ref(false)
  55. const finished = ref(false)
  56. const total = ref(0)
  57. const formatDate = (dateStr) => {
  58. if (!dateStr) return ''
  59. return dateStr.slice(0, 10).replace('T', ' ')
  60. }
  61. const currentTitleText = computed(() => {
  62. const map = {
  63. 1: t('News.ViewAnalysis'),
  64. 2: t('News.NewsInformation'),
  65. 3: t('News.Announcement'),
  66. 4: t('News.VideoCommentary'),
  67. 5: t('News.NewsInformation'),
  68. 6: t('News.VideoCommentary'),
  69. 7: t('News.Notice'),
  70. 8: t('News.TradeIdeas'),
  71. 9: t('News.FinancialCalendar'),
  72. 10: t('News.Ebook'),
  73. 11: t('News.VideoCommentary')
  74. }
  75. return map[props.type] || ''
  76. })
  77. const load = async () => {
  78. list.value = []
  79. if (loading.value) return
  80. loading.value = true
  81. finished.value = false
  82. page.value = 1
  83. try {
  84. const res = await props.fetchData({
  85. lang: locale.value == 'vn' ? 'vi' : locale.value,
  86. page: { current: page.value, row: props.queryParams?.pageSize || props.pageSize },
  87. ...props.queryParams
  88. })
  89. if (res.code === 200) {
  90. list.value = res.data || []
  91. total.value = res.page?.rowTotal || 0
  92. finished.value = list.value.length >= total.value
  93. } else {
  94. throw new Error(res.msg || '请求失败')
  95. }
  96. } catch (err) {
  97. console.error('加载失败', err)
  98. uni.showToast({ title: err.message || '加载失败', icon: 'none' })
  99. } finally {
  100. loading.value = false
  101. }
  102. }
  103. const loadMore = async () => {
  104. if (loadingMore.value || finished.value) return
  105. loadingMore.value = true
  106. try {
  107. const nextPage = page.value + 1
  108. const params = {
  109. lang: locale.value,
  110. page: { current: nextPage, row: props.queryParams?.pageSize || props.pageSize },
  111. ...props.queryParams
  112. }
  113. const res = await props.fetchData(params)
  114. if (res.code === 200) {
  115. const newList = res.data || []
  116. if (newList.length > 0) {
  117. list.value.push(...newList)
  118. page.value = nextPage
  119. }
  120. const totalRows = res.page?.rowTotal || total.value
  121. finished.value = list.value.length >= totalRows
  122. } else {
  123. throw new Error(res.msg || '请求失败')
  124. }
  125. } catch (err) {
  126. uni.showToast({ title: err.message || '加载更多失败', icon: 'none' })
  127. } finally {
  128. loadingMore.value = false
  129. }
  130. }
  131. const handleItemClick = (item) => {
  132. uni.navigateTo({ url: `/pages/analytics/detail?type=${props.type}&id=${item.id}` })
  133. }
  134. const lang = computed(() => uni.getLocale())
  135. watch(lang, () => {
  136. load()
  137. }, { immediate: true })
  138. defineExpose({ load, loadMore })
  139. </script>
  140. <style lang="scss" scoped>
  141. @import "@/uni.scss";
  142. .p-text{
  143. font-size: px2rpx(14);
  144. line-height: px2rpx(20);
  145. color: var(--cwg-gray-color)!important;
  146. }
  147. </style>