ranking.uvue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <template>
  2. <uni-navbar-lite :showLeft="true" title="排名详情"></uni-navbar-lite>
  3. <view class="ranking-page">
  4. <!-- 排名列表 -->
  5. <common-list
  6. :dataList="rankingList"
  7. :loading="loading"
  8. :refreshing="refreshing"
  9. :hasMore="false"
  10. @refresh="handleRefresh"
  11. class="list-with-padding"
  12. >
  13. <template #default="{ item, index }">
  14. <view class="ranking-item">
  15. <view class="ranking-info">
  16. <view class="ranking-position">
  17. <view class="position-circle" :class="getPositionClass(index as number)">
  18. <text class="position-text">{{ (item as UTSJSONObject).get('rank').toString() }}</text>
  19. </view>
  20. </view>
  21. <view class="user-info">
  22. <text class="user-name" :style="{ color: (item as UTSJSONObject).get('nickName') == currentUserNickName ? '#ff0000' : '#333333' }">{{ (item as UTSJSONObject).get('nickName') as string }}</text>
  23. <text class="user-dept">{{ (item as UTSJSONObject).get('deptName') as string }}</text>
  24. </view>
  25. </view>
  26. <view class="score-info">
  27. <text class="score-value">{{ formatScore((item as UTSJSONObject).get('finalScore') as number | null) }}</text>
  28. </view>
  29. </view>
  30. </template>
  31. </common-list>
  32. </view>
  33. </template>
  34. <script setup lang="uts">
  35. import { ref } from 'vue'
  36. import { selectHomePageData } from '@/api/index/index'
  37. // 数据状态
  38. const loading = ref<boolean>(false)
  39. const refreshing = ref<boolean>(false)
  40. // 添加防重复刷新的标志
  41. const isRefreshing = ref<boolean>(false)
  42. // 添加刷新时间戳,用于防抖
  43. const lastRefreshTime = ref<number>(0)
  44. // 排名类型 (dept, center, company)
  45. const rankingType = ref<string>('')
  46. const rankingTypeName = ref<string>('')
  47. // 排名列表数据
  48. const rankingList = ref<UTSJSONObject[]>([])
  49. // 当前用户昵称
  50. const currentUserNickName = ref<string>('')
  51. // 获取排名数据
  52. const loadRankingData = async (): Promise<void> => {
  53. try {
  54. loading.value = true
  55. refreshing.value = true
  56. const response = await selectHomePageData()
  57. if (response != null) {
  58. const respObj = response as UTSJSONObject
  59. const data = respObj['data'] as UTSJSONObject
  60. // 保存当前用户昵称
  61. currentUserNickName.value = data.get('nickName') as string
  62. console.log("当前用户昵称:", currentUserNickName.value)
  63. // 根据类型获取相应的排名数据
  64. let rawData: UTSJSONObject[] = []
  65. const type = rankingType.value;
  66. if (type == 'dept' && data.get('scoreDept') != null) {
  67. const deptData = data.get('scoreDept');
  68. if (Array.isArray(deptData)) {
  69. rawData = deptData as UTSJSONObject[];
  70. }
  71. } else if (type == 'center' && data.get('scoreCenter') != null) {
  72. const centerData = data.get('scoreCenter');
  73. if (Array.isArray(centerData)) {
  74. rawData = centerData as UTSJSONObject[];
  75. }
  76. } else if (type == 'company' && data.get('scoreCompany') != null) {
  77. const companyData = data.get('scoreCompany');
  78. if (Array.isArray(companyData)) {
  79. rawData = companyData as UTSJSONObject[];
  80. }
  81. } else {
  82. console.log("没有匹配到任何分支")
  83. }
  84. // 按照分数排序
  85. rawData.sort((a: UTSJSONObject, b: UTSJSONObject): number => {
  86. const scoreA = (a['finalScore'] as number | null) ?? 0
  87. const scoreB = (b['finalScore'] as number | null) ?? 0
  88. return scoreB - scoreA
  89. })
  90. // 处理排名(相同分数排名相同)
  91. const processedData: UTSJSONObject[] = []
  92. rawData.forEach((item: UTSJSONObject, index: number) => {
  93. let rank = index + 1
  94. // 如果当前项分数与前一项相同,则排名也相同
  95. if (index > 0) {
  96. const prevScore = (rawData[index - 1]['finalScore'] as number | null) ?? 0
  97. const currentScore = (item['finalScore'] as number | null) ?? 0
  98. if (currentScore === prevScore) {
  99. // 找到前一个处理后的项的排名
  100. rank = (processedData[processedData.length - 1]['rank'] as number)
  101. }
  102. }
  103. processedData.push({
  104. ...item,
  105. 'rank': rank
  106. })
  107. })
  108. rankingList.value = processedData
  109. }
  110. } catch (error) {
  111. console.error('获取排名数据失败:', error)
  112. // 出错时也需要重置刷新状态
  113. if (refreshing.value) {
  114. refreshing.value = false;
  115. isRefreshing.value = false;
  116. }
  117. } finally {
  118. loading.value = false
  119. refreshing.value = false
  120. // 确保在finally中也重置isRefreshing标志
  121. isRefreshing.value = false
  122. }
  123. }
  124. const formatScore = (score: number | null): string => {
  125. if (score === null) return '0.00'
  126. return score.toFixed(2)
  127. }
  128. // 获取排名位置的样式类
  129. const getPositionClass = (index: number): string => {
  130. if (index == 0) {
  131. return 'first'
  132. } else if (index == 1) {
  133. return 'second'
  134. } else if (index == 2) {
  135. return 'third'
  136. }
  137. return 'other'
  138. }
  139. // 下拉刷新
  140. const handleRefresh = (): void => {
  141. console.log("handleRefresh被触发")
  142. console.log("loading.value===" + loading.value)
  143. console.log("isRefreshing.value===" + isRefreshing.value)
  144. // 防抖处理,避免频繁触发
  145. const now = Date.now();
  146. if (now - lastRefreshTime.value < 1000) {
  147. console.log("刷新操作过于频繁,忽略本次触发");
  148. refreshing.value = false;
  149. return;
  150. }
  151. lastRefreshTime.value = now;
  152. // 添加防重复调用检查
  153. if (loading.value || isRefreshing.value) {
  154. // 如果已经在加载或正在刷新,直接重置刷新状态
  155. refreshing.value = false;
  156. return;
  157. }
  158. console.log("loading.value1===" + loading.value)
  159. console.log("isRefreshing.value1===" + isRefreshing.value)
  160. // 设置刷新标志
  161. isRefreshing.value = true;
  162. loadRankingData(); // 使用默认的下拉刷新行为
  163. // 确保在一定时间后重置刷新标志,防止意外情况
  164. setTimeout(() => {
  165. isRefreshing.value = false
  166. }, 100) // 延迟重置,确保状态完全更新
  167. }
  168. // 页面加载时获取参数
  169. onLoad((options: UTSJSONObject | null) => {
  170. if (options != null && options['type'] != null) {
  171. const typeVal = options['type'] as string
  172. rankingType.value = typeVal
  173. /* switch(rankingType.value) {
  174. case 'dept':
  175. rankingTypeName.value = '部门'
  176. break
  177. case 'center':
  178. rankingTypeName.value = '中心'
  179. break
  180. case 'company':
  181. rankingTypeName.value = '公司'
  182. break
  183. default:
  184. rankingTypeName.value = '排名'
  185. } */
  186. loadRankingData()
  187. }
  188. })
  189. // 组件销毁时清除状态
  190. onUnload(() => {
  191. // 如果组件销毁前还有未完成的刷新操作,确保状态被重置
  192. loading.value = false;
  193. refreshing.value = false;
  194. isRefreshing.value = false;
  195. })
  196. </script>
  197. <style lang="scss">
  198. .ranking-page {
  199. flex: 1;
  200. background-color: #e8f0f9;
  201. }
  202. .ranking-item {
  203. display: flex;
  204. flex-direction: row;
  205. justify-content: space-between;
  206. align-items: center;
  207. padding: 30rpx;
  208. background-color: #ffffff;
  209. margin: 12rpx 30rpx;
  210. border-radius: 16rpx;
  211. }
  212. .ranking-info {
  213. display: flex;
  214. flex-direction: row;
  215. align-items: center;
  216. flex: 1;
  217. }
  218. .ranking-position {
  219. margin-right: 24rpx;
  220. }
  221. .position-circle {
  222. width: 60rpx;
  223. height: 60rpx;
  224. border-radius: 50%;
  225. display: flex;
  226. align-items: center;
  227. justify-content: center;
  228. }
  229. .position-circle.first {
  230. //background-color: #ffd700; /* 金色 */
  231. }
  232. .position-circle.second {
  233. //background-color: #c0c0c0; /* 银色 */
  234. }
  235. .position-circle.third {
  236. //background-color: #cd7f32; /* 铜色 */
  237. }
  238. .position-circle.other {
  239. //background-color: #e0e0e0;
  240. }
  241. .position-text {
  242. font-size: 30rpx;
  243. font-weight: bold;
  244. color: #333333;
  245. }
  246. .user-info {
  247. display: flex;
  248. flex-direction: row;
  249. align-items: center;
  250. }
  251. .user-name {
  252. font-size: 30rpx;
  253. color: #333333;
  254. font-weight: bold;
  255. margin-right: 20rpx;
  256. }
  257. .user-dept {
  258. font-size: 30rpx;
  259. color: #666666;
  260. }
  261. .score-info {
  262. display: flex;
  263. flex-direction: column;
  264. align-items: flex-end;
  265. }
  266. .score-value {
  267. font-size: 30rpx;
  268. font-weight: bold;
  269. color: #165dff;
  270. }
  271. // 添加底部填充的类
  272. .list-with-padding {
  273. padding-bottom: 40rpx;
  274. }
  275. </style>