index.uvue 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. <template>
  2. <uni-navbar-lite :showLeft="false" title="工分" />
  3. <view class="list-page">
  4. <!-- 搜索栏 -->
  5. <view class="search-bar">
  6. <view class="search-box">
  7. <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
  8. <input
  9. class="search-input"
  10. placeholder="搜索工单编码、风机编号"
  11. v-model="searchKeyword"
  12. @confirm="handleSearch"
  13. @blur="handleSearch"
  14. />
  15. <text v-if="searchKeyword.length > 0" class="clear-icon" @click="clearSearch">✕</text>
  16. </view>
  17. </view>
  18. <!-- 工单评分状态筛选 -->
  19. <view class="status-bar">
  20. <view class="status-box">
  21. <text
  22. class="status-txt"
  23. :class="{ 'stauts-sel': statusFilter === '' }"
  24. @click="filterByStatus('')"
  25. >
  26. 全部
  27. </text>
  28. <text
  29. class="status-txt"
  30. :class="{ 'stauts-sel': statusFilter === 'to_self' }"
  31. @click="filterByStatus('to_self')"
  32. >
  33. 待自评
  34. </text>
  35. <text
  36. class="status-txt"
  37. :class="{ 'stauts-sel': statusFilter === 'to_re' }"
  38. @click="filterByStatus('to_re')"
  39. >
  40. 待复评
  41. </text>
  42. <text
  43. class="status-txt"
  44. :class="{ 'stauts-sel': statusFilter === 'appealing' }"
  45. @click="filterByStatus('appealing')"
  46. >
  47. 申诉中
  48. </text>
  49. <!-- <text
  50. class="status-txt"
  51. :class="{ 'stauts-sel': statusFilter === 'to_confirm' }"
  52. @click="filterByStatus('to_confirm')"
  53. >
  54. 待确认
  55. </text>
  56. <text
  57. class="status-txt"
  58. :class="{ 'stauts-sel': statusFilter === 'to_final' }"
  59. @click="filterByStatus('to_final')"
  60. >
  61. 待终评
  62. </text> -->
  63. </view>
  64. </view>
  65. <view class="stats-section">
  66. <!-- 标签切换 -->
  67. <view class="stats-tabs">
  68. <text
  69. class="tab-item"
  70. :class="{ 'tab-active': activeTab === 'ranking' }"
  71. @click="switchTab('ranking')"
  72. >
  73. 个人排名
  74. </text>
  75. <text
  76. class="tab-item"
  77. :class="{ 'tab-active': activeTab === 'score' }"
  78. @click="switchTab('score')"
  79. >
  80. 月度工分
  81. </text>
  82. </view>
  83. <!-- 个人排名内容 -->
  84. <view v-if="activeTab === 'ranking'" class="ranking-content">
  85. <!-- 排名模块 -->
  86. <view v-if="rankingItems.length > 0">
  87. <view class="ranking-item-wrapper" v-for="(item, index) in rankingItems" :key="index">
  88. <view class="ranking-item" @click="goToRankingDetail(getRankingType(index))">
  89. <view class="ranking-circle-wrapper">
  90. <view class="ranking-number" :class="getRankingSizeClass(index)">
  91. <text class="ranking-number-text">{{ item.rank }}/{{ item.total }}</text>
  92. </view>
  93. </view>
  94. <text class="ranking-name">{{ item.name }}</text>
  95. <text class="ranking-label">第{{ item.rank }}名</text>
  96. </view>
  97. </view>
  98. </view>
  99. <view v-else class="no-data">
  100. <text class="no-data-text">无排名数据</text>
  101. </view>
  102. </view>
  103. <!-- 月度工分内容 -->
  104. <view v-if="activeTab === 'score'" class="score-content">
  105. <view class="stats-header">
  106. <text class="stats-title">{{ monthTitle }}月度工分</text>
  107. <view class="month-filters">
  108. <view class="month-tab">
  109. <text
  110. class="month-filter"
  111. :class="{ 'month-filter-sel': selectedMonth === 'prev' }"
  112. @click="changeMonth('prev')"
  113. >
  114. 上月
  115. </text>
  116. </view>
  117. <view class="month-tab">
  118. <text
  119. class="month-filter"
  120. :class="{ 'month-filter-sel': selectedMonth === 'current' }"
  121. @click="changeMonth('current')"
  122. >
  123. 本月
  124. </text>
  125. </view>
  126. <view class="month-tab">
  127. <text
  128. class="month-filter"
  129. :class="{ 'month-filter-sel': selectedMonth === 'custom' }"
  130. @click="showCustomDatePicker"
  131. >
  132. 自定义
  133. </text>
  134. </view>
  135. </view>
  136. </view>
  137. <!-- 统计数据 -->
  138. <view class="stats-content">
  139. <view class="total-score">
  140. <text class="score-value">{{ totalScore }}分</text>
  141. <text class="score-label">{{ monthTitle }}总工分</text>
  142. </view>
  143. <view class="score-breakdown">
  144. <view class="breakdown-item">
  145. <text class="breakdown-label">维保工分</text>
  146. <text class="breakdown-value">{{ maintenanceScore }}分</text>
  147. </view>
  148. <view class="breakdown-item">
  149. <text class="breakdown-label">维修工分</text>
  150. <text class="breakdown-value">{{ repairScore }}分</text>
  151. </view>
  152. <!-- <view v-if="rank !== null && totalRankingUsers !== null" class="breakdown-item">
  153. <text class="breakdown-label">排名</text>
  154. <text class="breakdown-value">{{ rank }}/{{ totalRankingUsers }}</text>
  155. </view> -->
  156. </view>
  157. </view>
  158. </view>
  159. </view>
  160. <!-- 工单评分列表 -->
  161. <common-list
  162. :dataList="orderList"
  163. :loading="loading"
  164. :refreshing="refreshing"
  165. :hasMore="hasMore"
  166. @refresh="handleRefresh"
  167. @loadMore="loadMore"
  168. @itemClick="handleItemClick"
  169. class="list-with-padding"
  170. >
  171. <template #default="{ item, index }">
  172. <view class="list-item">
  173. <view class="item-container">
  174. <view class="item-header">
  175. <text class="item-title">{{ getPropertyValue(item, 'pcsStationName') }}{{ getPropertyValue(item, 'pcsDeviceName') }}{{ getWorkOrderTypeText(getPropertyValue(item, 'orderType')) }}</text>
  176. <!-- <text class="info-value">{{ getScoringStatus(item) }}</text> -->
  177. <text class="status-tag" :class="getStatusClass(item)">{{ getScoringStatus(item) }}</text>
  178. </view>
  179. <view class="info-row">
  180. <view class="info-label">
  181. <text class="text-gray">{{ getWorkOrderTypeInfo(item) }}</text>
  182. </view>
  183. <view class="info-value-row">
  184. <text v-if="getPropertyValue(item, 'myScore') !== ''" class="score-text">{{ formatNumber(parseFloat(getPropertyValue(item, 'myScore'))) }}</text>
  185. <!-- <text class="status-text">{{ getOrderStatusText(getPropertyValue(item, 'scoreStatus')) }}</text> -->
  186. </view>
  187. </view>
  188. <view class="info-row">
  189. <view class="info-label">
  190. <text class="text-gray">工作结束时间: {{ getPropertyValue(item, 'realEndTime') }}</text>
  191. </view>
  192. </view>
  193. </view>
  194. </view>
  195. </template>
  196. </common-list>
  197. <!-- 自定义时间选择弹窗 -->
  198. <l-popup v-model="showDatePickerPopup" position="bottom">
  199. <view class="date-picker-popup">
  200. <view class="popup-header">
  201. <text class="popup-title">选择年月</text>
  202. <view class="popup-actions">
  203. <view class="popup-btn">
  204. <text class="cancel-btn" @click="closeDatePicker">取消</text>
  205. </view>
  206. </view>
  207. </view>
  208. <view class="date-picker-container">
  209. <view class="date-picker-item">
  210. <!-- <text class="date-label">选择年月</text> -->
  211. <view class="date-display" @click="openDatePicker">
  212. {{ customDate != null && customDate != '' ? customDate : '请选择年月' }}
  213. </view>
  214. </view>
  215. </view>
  216. </view>
  217. </l-popup>
  218. <!-- Date Picker -->
  219. <l-popup v-model="showDatePicker" position="bottom" :z-index="1000">
  220. <l-date-time-picker
  221. :mode="3"
  222. format="YYYY-MM"
  223. :modelValue="customDate"
  224. confirm-btn="确定"
  225. cancel-btn="取消"
  226. @confirm="onDateConfirm"
  227. @cancel="showDatePicker = false">
  228. </l-date-time-picker>
  229. </l-popup>
  230. <!-- 底部 TabBar -->
  231. <custom-tabbar :current="3" :hide0="tabbar[0]" :hide1="tabbar[1]" :hide2="tabbar[2]" :hide3="tabbar[3]"/>
  232. </view>
  233. </template>
  234. <script setup lang="uts">
  235. import { ref, computed, onMounted, onBeforeUnmount, reactive } from 'vue'
  236. import { listOrderScores, getOrderScoreStatistics, listMobileOrderScores } from '@/api/score/index'
  237. import { getDictDataByType } from '@/api/dict/index'
  238. import { selectHomePageData } from '@/api/index/index'
  239. import type { SysDictData } from '@/types/dict'
  240. import {checkPermi} from '../../utils/storage'
  241. // 数据状态
  242. const searchKeyword = ref<string>('')
  243. const statusFilter = ref<string>('')
  244. const selectedMonth = ref<string>('current')
  245. const loading = ref<boolean>(false)
  246. const refreshing = ref<boolean>(false)
  247. const hasMore = ref<boolean>(true)
  248. const orderList = ref<any[]>([])
  249. const currentPage = ref<number>(1)
  250. const totalScore = ref<number>(0)
  251. const maintenanceScore = ref<number>(0)
  252. const repairScore = ref<number>(0)
  253. const rank = ref<number | null>(null)
  254. const totalRankingUsers = ref<number | null>(null)
  255. const customDate = ref<string>('')
  256. // 新增:标签切换相关
  257. const activeTab = ref<string>('ranking') // 默认显示个人排名
  258. // 用户数据类型定义
  259. type RankingItem = {
  260. name: string;
  261. rank: number;
  262. total: number;
  263. }
  264. // 新增:个人排名相关数据
  265. const rankingItems = ref<RankingItem[]>([])
  266. // 防止重复请求的标志位
  267. const isSearching = ref<boolean>(false)
  268. // 添加防重复刷新的标志
  269. const isRefreshing = ref<boolean>(false)
  270. // 添加刷新时间戳,用于防抖
  271. const lastRefreshTime = ref<number>(0)
  272. const tabbar = reactive([1,1,1,1])
  273. // 添加防抖定时器
  274. let searchTimer: number | null = null
  275. // 弹窗显示状态
  276. const showDatePickerPopup = ref<boolean>(false)
  277. const showDatePicker = ref<boolean>(false)
  278. // 工单状态字典列表
  279. const statusDictList = ref<SysDictData[]>([])
  280. // 维保类型字典列表
  281. const inspectionTypeDictList = ref<SysDictData[]>([])
  282. // 检修类型字典列表
  283. const maintenanceTypeDictList = ref<SysDictData[]>([])
  284. const dictLoaded = ref<boolean>(false)
  285. // 计算属性
  286. const monthTitle = computed(() => {
  287. switch (selectedMonth.value) {
  288. case 'prev':
  289. return '上月'
  290. case 'current':
  291. return '本月'
  292. case 'custom':
  293. // 显示自定义选择的年月
  294. return (customDate.value != null && customDate.value != '') ? customDate.value : '自定义'
  295. default:
  296. return '本月'
  297. }
  298. })
  299. // 根据索引获取排名圆圈的大小类(复制并修改自开屏页)
  300. const getRankingSizeClass = (index: number): string => {
  301. // size-1 对应最小的圆圈,size-3 对应最大的圆圈
  302. const size = Math.min(index + 1, 3);
  303. return `size-${size}`;
  304. };
  305. // 获取个人排名数据(复制并修改自开屏页)
  306. const fetchRankingData = async (): Promise<void> => {
  307. try {
  308. // 调用后端API获取用户数据
  309. const response = await selectHomePageData();
  310. // 根据响应结果更新rankingItems
  311. if (response != null) {
  312. const respObj = response as UTSJSONObject;
  313. const data = respObj['data'] as UTSJSONObject;
  314. // 初始化默认值
  315. let deptName = '未知部门';
  316. let center = '未知中心';
  317. let companyName = '未知公司';
  318. // 从scoreDept中提取部门、中心和公司名称
  319. if (data.hasOwnProperty('scoreDept') && Array.isArray(data['scoreDept']) && (data['scoreDept'] as any[]).length > 0) {
  320. const scoreDept = data['scoreDept'] as UTSJSONObject[];
  321. const firstItem = scoreDept[0];
  322. if (firstItem.hasOwnProperty('deptName')) {
  323. deptName = firstItem['deptName'] as string;
  324. }
  325. if (firstItem.hasOwnProperty('center')) {
  326. center = firstItem['center'] as string;
  327. }
  328. if (firstItem.hasOwnProperty('companyName')) {
  329. companyName = firstItem['companyName'] as string;
  330. }
  331. }
  332. // 更新排名信息
  333. const items : RankingItem[] = [];
  334. // 部门排名(东山电场)
  335. if (data.hasOwnProperty('deptSort') && data.hasOwnProperty('scoreDept')) {
  336. const deptSort = typeof data['deptSort'] === 'number' ? data['deptSort'] as number : 0;
  337. const scoreDept = Array.isArray(data['scoreDept']) ? data['scoreDept'] as any[] : [];
  338. // 只有当部门排序大于0且scoreDept数组不为空时才添加
  339. if (deptSort > 0 && scoreDept.length > 0) {
  340. items.push({
  341. name: deptName,
  342. rank: deptSort,
  343. total: scoreDept.length
  344. });
  345. }
  346. }
  347. // 中心排名(陆上设备维护中心)
  348. if (data.hasOwnProperty('centerSort') && data.hasOwnProperty('scoreCenter')) {
  349. const centerSort = typeof data['centerSort'] === 'number' ? data['centerSort'] as number : 0;
  350. const scoreCenter = Array.isArray(data['scoreCenter']) ? data['scoreCenter'] as any[] : [];
  351. // 只有当中心排序大于0且scoreCenter数组不为空时才添加
  352. if (centerSort > 0 && scoreCenter.length > 0) {
  353. items.push({
  354. name: center,
  355. rank: centerSort,
  356. total: scoreCenter.length
  357. });
  358. }
  359. }
  360. // 公司排名
  361. if (data.hasOwnProperty('companySort') && data.hasOwnProperty('scoreCompany')) {
  362. const companySort = typeof data['companySort'] === 'number' ? data['companySort'] as number : 0;
  363. const scoreCompany = Array.isArray(data['scoreCompany']) ? data['scoreCompany'] as any[] : [];
  364. // 只有当公司排序大于0且scoreCompany数组不为空时才添加
  365. if (companySort > 0 && scoreCompany.length > 0) {
  366. items.push({
  367. name: companyName,
  368. rank: companySort,
  369. total: scoreCompany.length
  370. });
  371. }
  372. }
  373. // 更新rankingItems
  374. rankingItems.value = items;
  375. }
  376. } catch (error) {
  377. console.error('获取用户排名数据失败:', error);
  378. }
  379. };
  380. // 新增:切换标签
  381. function switchTab(tab: string) {
  382. activeTab.value = tab;
  383. // 当切换到个人排名标签时,获取排名数据
  384. if (tab === 'ranking') {
  385. fetchRankingData();
  386. }
  387. }
  388. // 打开自定义日期选择弹窗
  389. function showCustomDatePicker() {
  390. showDatePickerPopup.value = true
  391. }
  392. function closeDatePicker() {
  393. showDatePicker.value = false
  394. showDatePickerPopup.value = false
  395. }
  396. // 打开日期选择器
  397. function openDatePicker() {
  398. showDatePicker.value = true
  399. }
  400. // 获取工单评分状态字典列表
  401. const loadStatusDictList = async (): Promise<void> => {
  402. try {
  403. const result = await getDictDataByType('gxt_scoring_status')
  404. const resultObj = result as UTSJSONObject
  405. if (resultObj['code'] == 200) {
  406. const data = resultObj['data'] as any[]
  407. const dictData: SysDictData[] = []
  408. if (data != null && data.length > 0) {
  409. for (let i = 0; i < data.length; i++) {
  410. const item = data[i] as UTSJSONObject
  411. // 只提取需要的字段
  412. const dictItem: SysDictData = {
  413. dictValue: item['dictValue'] as string | null,
  414. dictLabel: item['dictLabel'] as string | null,
  415. dictCode: null,
  416. dictSort: null,
  417. dictType: null,
  418. cssClass: null,
  419. listClass: null,
  420. isDefault: null,
  421. status: null,
  422. default: null,
  423. createTime: null,
  424. remark: null
  425. }
  426. dictData.push(dictItem)
  427. }
  428. }
  429. statusDictList.value = dictData
  430. }
  431. } catch (e: any) {
  432. console.error('获取工单评分状态字典失败:', e.message)
  433. }
  434. }
  435. // 获取维保类型字典列表
  436. const loadInspectionTypeDictList = async (): Promise<void> => {
  437. try {
  438. const result = await getDictDataByType('gxt_inspection_type')
  439. const resultObj = result as UTSJSONObject
  440. if (resultObj['code'] == 200) {
  441. const data = resultObj['data'] as any[]
  442. const dictData: SysDictData[] = []
  443. if (data != null && data.length > 0) {
  444. for (let i = 0; i < data.length; i++) {
  445. const item = data[i] as UTSJSONObject
  446. // 只提取需要的字段
  447. const dictItem: SysDictData = {
  448. dictValue: item['dictValue'] as string | null,
  449. dictLabel: item['dictLabel'] as string | null,
  450. dictCode: null,
  451. dictSort: null,
  452. dictType: null,
  453. cssClass: null,
  454. listClass: null,
  455. isDefault: null,
  456. status: null,
  457. default: null,
  458. createTime: null,
  459. remark: null
  460. }
  461. dictData.push(dictItem)
  462. }
  463. }
  464. inspectionTypeDictList.value = dictData
  465. }
  466. } catch (e: any) {
  467. console.error('获取维保类型字典失败:', e.message)
  468. }
  469. }
  470. // 获取检修类型字典列表
  471. const loadMaintenanceTypeDictList = async (): Promise<void> => {
  472. try {
  473. const result = await getDictDataByType('gxt_maintenance_type')
  474. const resultObj = result as UTSJSONObject
  475. if (resultObj['code'] == 200) {
  476. const data = resultObj['data'] as any[]
  477. const dictData: SysDictData[] = []
  478. if (data != null && data.length > 0) {
  479. for (let i = 0; i < data.length; i++) {
  480. const item = data[i] as UTSJSONObject
  481. // 只提取需要的字段
  482. const dictItem: SysDictData = {
  483. dictValue: item['dictValue'] as string | null,
  484. dictLabel: item['dictLabel'] as string | null,
  485. dictCode: null,
  486. dictSort: null,
  487. dictType: null,
  488. cssClass: null,
  489. listClass: null,
  490. isDefault: null,
  491. status: null,
  492. default: null,
  493. createTime: null,
  494. remark: null
  495. }
  496. dictData.push(dictItem)
  497. }
  498. }
  499. maintenanceTypeDictList.value = dictData
  500. }
  501. } catch (e: any) {
  502. console.error('获取检修类型字典失败:', e.message)
  503. }
  504. }
  505. // Helper function to safely extract properties from item
  506. function getPropertyValue(item: any | null, propertyName: string): string {
  507. if (item == null) return ''
  508. const itemObj = item as UTSJSONObject
  509. const value = itemObj[propertyName]
  510. return value != null ? '' + value : ''
  511. }
  512. // 获取工单类型文本
  513. function getWorkOrderTypeText(orderType: string | null): string {
  514. if (orderType != null) {
  515. if (orderType == "1") {
  516. return "维修工单"
  517. } else if (orderType == "2") {
  518. return "维保工单"
  519. }
  520. }
  521. return ""
  522. }
  523. // 获取工单类型相关信息(维保类型或检修类型)
  524. function getWorkOrderTypeInfo(item: any | null): string {
  525. if (item == null) return ''
  526. const orderType = getPropertyValue(item, 'orderType')
  527. // 维保工单显示维保类型
  528. if (orderType == "2") {
  529. const inspectionType = getPropertyValue(item, 'inspectionType')
  530. if (inspectionType != null && inspectionType != '') {
  531. // 如果字典尚未加载,返回原始值
  532. if (inspectionTypeDictList.value.length == 0) {
  533. return inspectionType
  534. }
  535. // 查找字典中对应的标签
  536. const dictItem = inspectionTypeDictList.value.find(dict => dict.dictValue == inspectionType)
  537. return (dictItem != null ? dictItem.dictLabel : inspectionType) as string
  538. }
  539. }
  540. // 维修工单显示检修类型
  541. else if (orderType == "1") {
  542. const maintenanceType = getPropertyValue(item, 'maintenanceType')
  543. if (maintenanceType != null && maintenanceType != '') {
  544. // 如果字典尚未加载,返回原始值
  545. if (maintenanceTypeDictList.value.length == 0) {
  546. return maintenanceType
  547. }
  548. // 查找字典中对应的标签
  549. const dictItem = maintenanceTypeDictList.value.find(dict => dict.dictValue == maintenanceType)
  550. return (dictItem != null ? dictItem.dictLabel : maintenanceType) as string
  551. }
  552. }
  553. return ""
  554. }
  555. // 获取工单状态文本
  556. function getScoringStatus(item: any | null): string | null {
  557. if (item == null) return ''
  558. const rawStatus = getPropertyValue(item, 'scoringStatus')
  559. if (rawStatus == null || rawStatus == '') return ''
  560. // 如果字典尚未加载,返回原始值
  561. if (!dictLoaded.value) {
  562. return rawStatus
  563. }
  564. // 查找字典中对应的标签
  565. const dictItem = statusDictList.value.find(dict => dict.dictValue == rawStatus)
  566. return dictItem != null ? dictItem.dictLabel : rawStatus
  567. }
  568. // 获取工单评分状态文本
  569. function getOrderStatusText(status: string | null): string {
  570. const statusMap: UTSJSONObject = {
  571. '1': '待自评',
  572. '2': '待复评',
  573. '3': '待确认',
  574. '4': '待终评'
  575. }
  576. if (status == null) return ''
  577. const result = statusMap[status]
  578. return result != null ? result as string : '未知状态'
  579. }
  580. // 获取统计数据
  581. function getStatistics() {
  582. // Convert 'current' and 'prev' values to actual date strings
  583. let monthValue = '';
  584. if (selectedMonth.value === 'custom') {
  585. monthValue = customDate.value;
  586. } else if (selectedMonth.value === 'current') {
  587. const now = new Date();
  588. monthValue = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
  589. } else if (selectedMonth.value === 'prev') {
  590. const now = new Date();
  591. const prevMonth = now.getMonth(); // getMonth() returns 0-11
  592. const prevYear = prevMonth === 0 ? now.getFullYear() - 1 : now.getFullYear();
  593. const monthStr = prevMonth === 0 ? '12' : prevMonth.toString().padStart(2, '0');
  594. monthValue = `${prevYear}-${monthStr}`;
  595. }
  596. const params: UTSJSONObject = {
  597. //scoringStatus: statusFilter.value,
  598. month: monthValue
  599. }
  600. getOrderScoreStatistics(params).then((response: any) => {
  601. const resultObj = response as UTSJSONObject
  602. const responseData = resultObj['data'] as UTSJSONObject
  603. if (responseData != null) {
  604. totalScore.value = (responseData['totalScore'] != null) ? parseFloat((responseData['totalScore'] as number).toFixed(2)) : 0
  605. maintenanceScore.value = (responseData['maintenanceScore'] != null) ? parseFloat((responseData['maintenanceScore'] as number).toFixed(2)) : 0
  606. repairScore.value = (responseData['repairScore'] != null) ? parseFloat((responseData['repairScore'] as number).toFixed(2)) : 0
  607. rank.value = (responseData['rank'] != null) ? responseData['rank'] as number : null
  608. totalRankingUsers.value = (responseData['totalRankingUsers'] != null) ? responseData['totalRankingUsers'] as number : null
  609. } else {
  610. totalScore.value = 0
  611. maintenanceScore.value = 0
  612. repairScore.value = 0
  613. rank.value = null
  614. totalRankingUsers.value = null
  615. }
  616. })
  617. }
  618. function onDateConfirm(value: string) {
  619. customDate.value = value
  620. showDatePicker.value = false
  621. showDatePickerPopup.value = false
  622. selectedMonth.value = 'custom'
  623. // 添加防重复调用检查
  624. if (loading.value) {
  625. return;
  626. }
  627. getStatistics()
  628. }
  629. // 方法
  630. function loadData(isRefresh: boolean, disablePullDown = false) {
  631. // 防止重复请求的核心机制 - 参考工单页面的简单有效方式
  632. if (loading.value) {
  633. // 确保刷新状态最终被重置,防止卡死
  634. if (!isRefresh) {
  635. refreshing.value = false;
  636. }
  637. return
  638. }
  639. const shouldRefresh = isRefresh !== false
  640. loading.value = true
  641. if (shouldRefresh && !disablePullDown) {
  642. currentPage.value = 1
  643. refreshing.value = true
  644. } else if (shouldRefresh && disablePullDown) {
  645. // 筛选条件变化时,重置页码但不触发下拉刷新
  646. currentPage.value = 1
  647. // 即使禁用下拉刷新,也要确保刷新状态最终被重置
  648. refreshing.value = false
  649. } else {
  650. // 对于加载更多操作,不需要显示下拉刷新状态
  651. refreshing.value = false;
  652. }
  653. // Convert 'current' and 'prev' values to actual date strings
  654. let monthValue = '';
  655. if (selectedMonth.value === 'custom') {
  656. monthValue = customDate.value;
  657. } else if (selectedMonth.value === 'current') {
  658. const now = new Date();
  659. const m = now.getMonth() + 1
  660. let mStr = m.toString()
  661. if (m < 10) {
  662. mStr = '0' + mStr
  663. }
  664. monthValue = `${now.getFullYear()}-${mStr}`;
  665. } else if (selectedMonth.value === 'prev') {
  666. const now = new Date();
  667. const prevMonth = now.getMonth(); // getMonth() returns 0-11
  668. const prevYear = prevMonth === 0 ? now.getFullYear() - 1 : now.getFullYear();
  669. let monthStr = prevMonth === 0 ? '12' : prevMonth.toString()
  670. if (prevMonth !== 0 && prevMonth < 10) {
  671. monthStr = '0' + monthStr
  672. }
  673. monthValue = `${prevYear}-${monthStr}`;
  674. }
  675. const params: UTSJSONObject = {
  676. pageNum: currentPage.value,
  677. pageSize: 5,
  678. keyword: searchKeyword.value,
  679. scoringStatus: statusFilter.value,
  680. month: monthValue
  681. }
  682. listMobileOrderScores(params).then((response: any) => {
  683. // 提取响应数据
  684. const resultObj = response as UTSJSONObject
  685. console.log('API响应数据:', resultObj)
  686. const responseData = resultObj['rows'] as any[]
  687. const responseTotal = resultObj['total'] as number
  688. if (shouldRefresh) {
  689. orderList.value = Array.isArray(responseData) ? responseData : []
  690. } else {
  691. const currentRows = Array.isArray(responseData) ? responseData : []
  692. orderList.value = [...orderList.value, ...currentRows]
  693. }
  694. hasMore.value = orderList.value.length < responseTotal
  695. }).catch((error) => {
  696. console.error('获取工单列表失败:', error);
  697. // 出错时也需要重置刷新状态
  698. if (shouldRefresh) {
  699. refreshing.value = false;
  700. isRefreshing.value = false;
  701. }
  702. }).finally(() => {
  703. // 无论成功还是失败,都重置所有加载状态
  704. loading.value = false;
  705. // 确保刷新状态最终被重置
  706. if (shouldRefresh) {
  707. refreshing.value = false;
  708. // 使用setTimeout确保状态彻底重置
  709. setTimeout(() => {
  710. isRefreshing.value = false;
  711. }, 50);
  712. }
  713. })
  714. }
  715. function handleSearch() {
  716. // 添加防重复调用检查
  717. if (loading.value) {
  718. return;
  719. }
  720. // 添加防抖和防止重复请求
  721. if (isSearching.value) {
  722. return
  723. }
  724. const timer = searchTimer
  725. if (timer != null) {
  726. clearTimeout(timer)
  727. searchTimer = null
  728. }
  729. searchTimer = setTimeout(() => {
  730. isSearching.value = true
  731. loadData(true, true); // 添加true参数表示这是筛选条件变化触发的加载,应禁用下拉刷新
  732. // 延迟重置标志位,确保请求发送后才允许下一次搜索
  733. setTimeout(() => {
  734. isSearching.value = false
  735. }, 100)
  736. }, 300)
  737. }
  738. function clearSearch() {
  739. // 添加防重复调用检查
  740. if (loading.value) {
  741. return;
  742. }
  743. // 添加防抖和防止重复请求
  744. if (isSearching.value) {
  745. return
  746. }
  747. const timer = searchTimer
  748. if (timer != null) {
  749. clearTimeout(timer)
  750. searchTimer = null
  751. }
  752. searchKeyword.value = ""
  753. searchTimer = setTimeout(() => {
  754. isSearching.value = true
  755. loadData(true, true); // 添加true参数表示这是筛选条件变化触发的加载,应禁用下拉刷新
  756. // 延迟重置标志位,确保请求发送后才允许下一次搜索
  757. setTimeout(() => {
  758. isSearching.value = false
  759. }, 100)
  760. }, 300)
  761. }
  762. function filterByStatus(status: string) {
  763. // 添加防重复调用检查
  764. if (loading.value) {
  765. return;
  766. }
  767. // 添加防止重复请求
  768. if (isSearching.value) {
  769. return
  770. }
  771. const timer = searchTimer
  772. if (timer != null) {
  773. clearTimeout(timer)
  774. searchTimer = null
  775. }
  776. statusFilter.value = status
  777. searchTimer = setTimeout(() => {
  778. isSearching.value = true
  779. loadData(true, true); // 添加true参数表示这是筛选条件变化触发的加载,应禁用下拉刷新
  780. getStatistics()
  781. // 延迟重置标志位,确保请求发送后才允许下一次搜索
  782. setTimeout(() => {
  783. isSearching.value = false
  784. }, 100)
  785. }, 300)
  786. }
  787. function changeMonth(month: string) {
  788. // 添加防重复调用检查
  789. if (loading.value) {
  790. return;
  791. }
  792. // 添加防止重复请求
  793. if (isSearching.value) {
  794. return
  795. }
  796. const timer = searchTimer
  797. if (timer != null) {
  798. clearTimeout(timer)
  799. searchTimer = null
  800. }
  801. selectedMonth.value = month
  802. searchTimer = setTimeout(() => {
  803. getStatistics()
  804. }, 300)
  805. }
  806. function loadMore() {
  807. console.log("loadMore被触发")
  808. if (!hasMore.value || loading.value) return
  809. currentPage.value++
  810. loadData(false)
  811. }
  812. function handleRefresh() {
  813. console.log("handleRefresh被触发")
  814. console.log("loading.value===",loading.value)
  815. console.log("isRefreshing.value===",isRefreshing.value)
  816. // 防抖处理,避免频繁触发
  817. const now = Date.now();
  818. if (now - lastRefreshTime.value < 1000) {
  819. console.log("刷新操作过于频繁,忽略本次触发");
  820. refreshing.value = false;
  821. return;
  822. }
  823. lastRefreshTime.value = now;
  824. // 添加防重复调用检查
  825. if (loading.value || isRefreshing.value) {
  826. // 如果已经在加载或正在刷新,直接重置刷新状态
  827. refreshing.value = false;
  828. return;
  829. }
  830. console.log("loading.value1===",loading.value)
  831. console.log("isRefreshing.value1===",isRefreshing.value)
  832. // 设置刷新标志
  833. isRefreshing.value = true;
  834. loadData(true); // 使用默认的下拉刷新行为
  835. // 确保在一定时间后重置刷新标志,防止意外情况
  836. setTimeout(() => {
  837. isRefreshing.value = false
  838. }, 100) // 延迟重置,确保状态完全更新
  839. }
  840. // 点击列表项
  841. function handleItemClick(item: any | null, index: number): void {
  842. if (item == null) return
  843. const itemObj = item as UTSJSONObject
  844. const id = itemObj.get('id')
  845. const orderType = itemObj.get('orderType')
  846. if (id != null && orderType != null) {
  847. // 转换为字符串
  848. const idStr = '' + id
  849. const orderTypeStr = '' + orderType
  850. uni.navigateTo({
  851. url: `/pages/score/detail/index?id=${idStr}&orderType=${orderTypeStr}`
  852. })
  853. }
  854. }
  855. function formatNumber(value: number | null) {
  856. if (value === null) return '0.0'
  857. return value.toFixed(2)
  858. }
  859. // 获取工单评分状态样式类
  860. function getStatusClass(item: any | null): string {
  861. if (item == null) return ''
  862. const rawStatus = getPropertyValue(item, 'scoringStatus')
  863. if (rawStatus == null || rawStatus == '') return ''
  864. return `status-${rawStatus}`
  865. }
  866. function formatDate(dateString: string) {
  867. if (dateString == '' || dateString == null) return ''
  868. const date = new Date(dateString)
  869. const year = date.getFullYear()
  870. const month = (date.getMonth() + 1).toString().padStart(2, '0')
  871. const day = date.getDate().toString().padStart(2, '0')
  872. const hours = date.getHours().toString().padStart(2, '0')
  873. const minutes = date.getMinutes().toString().padStart(2, '0')
  874. return `${year}-${month}-${day} ${hours}:${minutes}`
  875. }
  876. // 生命周期
  877. //onMounted(() => {
  878. onLoad((options: any) => {
  879. // 检查权限设置tabbar
  880. tabbar[2] = checkPermi(['gxt:app:worktime']) ? 1 : 0
  881. tabbar[3] = checkPermi(['gxt:app:score']) ? 1 : 0
  882. loadStatusDictList()
  883. loadInspectionTypeDictList()
  884. loadMaintenanceTypeDictList()
  885. loadData(false)
  886. getStatistics()
  887. dictLoaded.value = true
  888. // 获取初始排名数据
  889. fetchRankingData();
  890. })
  891. // 组件销毁时清除定时器
  892. onBeforeUnmount(() => {
  893. const timer = searchTimer
  894. if (timer != null) {
  895. clearTimeout(timer)
  896. searchTimer = null
  897. }
  898. // 如果组件销毁前还有未完成的刷新操作,确保状态被重置
  899. loading.value = false;
  900. refreshing.value = false;
  901. isRefreshing.value = false;
  902. })
  903. // 获取排名类型(dept, center, company)
  904. function getRankingType(index: number): string {
  905. // 根据索引返回对应的排名类型
  906. if (index === 0) return 'dept';
  907. if (index === 1) return 'center';
  908. if (index === 2) return 'company';
  909. return 'dept'; // 默认返回部门排名
  910. }
  911. // 跳转到排名详情页面
  912. function goToRankingDetail(type: string) {
  913. console.log("type======",type)
  914. uni.navigateTo({
  915. url: `/pages/score/ranking?type=${type}`
  916. })
  917. }
  918. </script>
  919. <style lang="scss">
  920. .list-page {
  921. flex: 1;
  922. background-color: #e8f0f9;
  923. padding-bottom: 150rpx; // 为底部 TabBar 留出空间
  924. }
  925. /* 搜索栏样式 */
  926. .search-bar {
  927. padding: 20rpx 30rpx;
  928. background-color: #d7eafe;
  929. }
  930. .search-box {
  931. flex-direction: row;
  932. align-items: center;
  933. height: 72rpx;
  934. padding: 0 24rpx;
  935. background-color: #f5f5f5;
  936. border-radius: 36rpx;
  937. .search-icon {
  938. width: 32rpx;
  939. height: 32rpx;
  940. margin-right: 12rpx;
  941. }
  942. .search-input {
  943. flex: 1;
  944. font-size: 28rpx;
  945. color: #333333;
  946. }
  947. .clear-icon {
  948. margin-left: 12rpx;
  949. font-size: 28rpx;
  950. color: #999999;
  951. }
  952. }
  953. /* 工单状态筛选 */
  954. .status-bar {
  955. padding-bottom: 10px;
  956. padding-left: 30rpx;
  957. background-color: #d7eafe;
  958. }
  959. .status-box {
  960. flex-direction: row;
  961. align-items: center;
  962. height: 72rpx;
  963. flex: 1;
  964. .status-txt {
  965. padding: 8px 12px;
  966. text-align: center;
  967. margin-right: 12rpx;
  968. border-radius: 36rpx;
  969. background-color: #fff;
  970. font-size: 28rpx;
  971. }
  972. .stauts-sel {
  973. background-color: #007AFF;
  974. color: #fff;
  975. }
  976. }
  977. /* 工分统计 */
  978. .stats-section {
  979. margin: 15rpx 30rpx;
  980. background-color: #ffffff;
  981. border-radius: 16rpx;
  982. padding: 20rpx;
  983. }
  984. /* 标签切换样式 */
  985. .stats-tabs {
  986. display: flex;
  987. flex-direction: row;
  988. border-bottom: 2rpx solid #f0f0f0;
  989. margin-bottom: 20rpx;
  990. }
  991. .tab-item {
  992. padding: 15rpx 30rpx;
  993. font-size: 30rpx;
  994. color: #666;
  995. position: relative;
  996. }
  997. .tab-active {
  998. color: #165dff;
  999. font-weight: bold;
  1000. }
  1001. .tab-active::after {
  1002. content: '';
  1003. position: absolute;
  1004. bottom: -2rpx;
  1005. left: 0;
  1006. right: 0;
  1007. height: 6rpx;
  1008. background-color: #165dff;
  1009. border-radius: 3rpx;
  1010. }
  1011. .stats-header {
  1012. flex-direction: row;
  1013. justify-content: space-between;
  1014. align-items: center;
  1015. margin-bottom: 20rpx;
  1016. }
  1017. .stats-title {
  1018. font-size: 32rpx;
  1019. font-weight: bold;
  1020. flex: 1;
  1021. }
  1022. .month-filters {
  1023. flex-direction: row;
  1024. justify-content: flex-end;
  1025. display: flex;
  1026. }
  1027. .month-tab {
  1028. white-space: nowrap;
  1029. margin: 0 8rpx;
  1030. border-radius: 24rpx;
  1031. background-color: #f2f3f5;
  1032. }
  1033. .month-filter {
  1034. padding: 6rpx 12rpx;
  1035. font-size: 24rpx;
  1036. color: #666;
  1037. }
  1038. .month-filter-sel {
  1039. background-color: #165dff;
  1040. color: white;
  1041. }
  1042. .stats-content {
  1043. margin-top: 20rpx;
  1044. flex-direction: column;
  1045. }
  1046. .total-score {
  1047. margin-bottom: 30rpx;
  1048. flex-direction: column;
  1049. align-items: center;
  1050. }
  1051. .score-label {
  1052. font-size: 28rpx;
  1053. color: #666;
  1054. display: flex;
  1055. margin-bottom: 8rpx;
  1056. }
  1057. .score-value {
  1058. display: flex;
  1059. font-size: 48rpx;
  1060. font-weight: bold;
  1061. color: #165dff;
  1062. line-height: 1.2;
  1063. }
  1064. .score-breakdown {
  1065. flex-direction: row;
  1066. justify-content: space-between;
  1067. }
  1068. .breakdown-item {
  1069. flex: 1;
  1070. flex-direction: column;
  1071. align-items: center;
  1072. }
  1073. .breakdown-label {
  1074. display: flex;
  1075. font-size: 32rpx;
  1076. font-weight: bold;
  1077. color: #333;
  1078. line-height: 1.4;
  1079. }
  1080. .breakdown-value {
  1081. font-size: 28rpx;
  1082. color: #666;
  1083. display: flex;
  1084. margin-bottom: 8rpx;
  1085. }
  1086. /* 列表项样式 */
  1087. .list-item {
  1088. margin: 12rpx 30rpx;
  1089. background-color: #ffffff;
  1090. border-radius: 16rpx;
  1091. }
  1092. .item-container {
  1093. padding: 30rpx;
  1094. }
  1095. .item-header {
  1096. flex-direction: row;
  1097. align-items: flex-start;
  1098. margin-bottom: 16rpx;
  1099. .item-title {
  1100. font-size: 30rpx;
  1101. color: #333333;
  1102. font-weight: bold;
  1103. flex-wrap: wrap;
  1104. flex: 0 1 70%;
  1105. min-width: 0;
  1106. }
  1107. .info-value {
  1108. font-size: 28rpx;
  1109. color: #999999;
  1110. margin-left: auto;
  1111. flex: 0 0 auto;
  1112. white-space: nowrap;
  1113. }
  1114. }
  1115. .info-row {
  1116. flex-direction: row;
  1117. justify-content: space-between;
  1118. align-items: center;
  1119. .info-label {
  1120. font-size: 26rpx;
  1121. color: #666;
  1122. }
  1123. .info-value-row {
  1124. flex-direction: row;
  1125. align-items: center;
  1126. }
  1127. .score-text {
  1128. font-size: 28rpx;
  1129. color: #ff9900;
  1130. font-weight: bold;
  1131. margin-right: 20rpx;
  1132. }
  1133. .status-text {
  1134. font-size: 24rpx;
  1135. padding: 4rpx 10rpx;
  1136. border-radius: 24rpx;
  1137. background-color: #f2f3f5;
  1138. color: #666;
  1139. }
  1140. }
  1141. .text-gray{
  1142. font-size: 26rpx;
  1143. color: #666;
  1144. }
  1145. /* 日期选择弹窗 */
  1146. .date-picker-popup {
  1147. background-color: white;
  1148. border-top-left-radius: 30rpx;
  1149. border-top-right-radius: 30rpx;
  1150. padding: 40rpx;
  1151. // padding-bottom: 40rpx;
  1152. // margin-bottom: 150rpx; /* 提高弹窗,避免被底部导航栏遮挡 */
  1153. min-height: 750rpx;
  1154. box-sizing: border-box; // 确保padding不撑大容器
  1155. }
  1156. .popup-header {
  1157. display: flex;
  1158. justify-content: space-between;
  1159. align-items: center;
  1160. margin-bottom: 40rpx;
  1161. width: 100%;
  1162. }
  1163. .popup-title {
  1164. font-size: 34rpx;
  1165. font-weight: bold;
  1166. flex: 1;
  1167. white-space: nowrap;
  1168. overflow: hidden;
  1169. text-overflow: ellipsis;
  1170. }
  1171. .popup-actions {
  1172. display: flex;
  1173. flex-direction: row;
  1174. white-space: nowrap;
  1175. flex: 0 0 auto;
  1176. }
  1177. .popup-btn {
  1178. white-space: nowrap;
  1179. flex: 0 0 auto; // 不伸缩,保持原有宽度
  1180. }
  1181. .cancel-btn {
  1182. color: #999;
  1183. padding: 10rpx 20rpx;
  1184. font-size: 30rpx;
  1185. font-weight: bold;
  1186. // 核心修复:强制文字不换行
  1187. white-space: nowrap !important;
  1188. // 可选:固定按钮最小宽度,避免极端情况挤压
  1189. min-width: 120rpx;
  1190. text-align: center;
  1191. }
  1192. .confirm-btn {
  1193. color: #165dff;
  1194. padding: 10rpx 20rpx;
  1195. font-weight: bold;
  1196. }
  1197. .date-picker-container {
  1198. padding: 20rpx 0;
  1199. padding-bottom: env(safe-area-inset-bottom); // 适配安全区域
  1200. }
  1201. .date-picker-item {
  1202. margin-bottom: 40rpx;
  1203. }
  1204. .date-label {
  1205. display: flex;
  1206. margin-bottom: 20rpx;
  1207. font-size: 30rpx;
  1208. color: #333;
  1209. }
  1210. .date-display {
  1211. width: 100%;
  1212. padding: 20rpx;
  1213. border: 2rpx solid #ddd;
  1214. border-radius: 8rpx;
  1215. font-size: 32rpx;
  1216. color: #333;
  1217. }
  1218. // 添加底部填充的类
  1219. .list-with-padding {
  1220. padding-bottom: 40rpx;
  1221. }
  1222. /* 工单评分状态标签样式 */
  1223. .status-tag {
  1224. padding: 8rpx 20rpx;
  1225. border-radius: 20rpx;
  1226. font-size: 24rpx;
  1227. white-space: nowrap;
  1228. margin-left: 70rpx;
  1229. border: 1rpx solid;
  1230. }
  1231. /* 待自评 */
  1232. .status-to_self {
  1233. background-color: #fff7e6;
  1234. color: #fa8c16;
  1235. border-color: #ffd591;
  1236. }
  1237. /* 待复评 */
  1238. .status-to_re {
  1239. background-color: #ebf5ff;
  1240. color: #409eff;
  1241. border-color: #d8ebff;
  1242. }
  1243. /* 待确认 */
  1244. .status-to_confirm {
  1245. background-color: #f0f9eb;
  1246. color: #5cb87a;
  1247. border-color: #c2e7b0;
  1248. }
  1249. /* 待终评 */
  1250. .status-to_final {
  1251. background-color: #ebf5ff;
  1252. color: #409eff;
  1253. border-color: #d8ebff;
  1254. }
  1255. /* 待归档 */
  1256. .status-to_archive {
  1257. background-color: #f0f9eb;
  1258. color: #5cb87a;
  1259. border-color: #c2e7b0;
  1260. }
  1261. /* 已归档 */
  1262. .status-archived {
  1263. background-color: #f0f9eb;
  1264. color: #5cb87a;
  1265. border-color: #c2e7b0;
  1266. }
  1267. /* 申诉中 */
  1268. .status-appealing {
  1269. background-color: #ffcfc2;
  1270. color: #ff0000;
  1271. border-color: #ff7446;
  1272. }
  1273. /* 个人排名模块样式 */
  1274. .ranking-item-wrapper {
  1275. padding: 5rpx 30rpx;
  1276. margin-bottom: 5rpx;
  1277. background: transparent;
  1278. }
  1279. .ranking-item {
  1280. display: flex;
  1281. flex-direction: row;
  1282. align-items: center;
  1283. width: 100%;
  1284. padding: 10rpx 0;
  1285. }
  1286. .module-title {
  1287. color: #ffffff;
  1288. font-size: 36rpx;
  1289. font-weight: bold;
  1290. margin-bottom: 0;
  1291. text-align: center;
  1292. }
  1293. .ranking-item {
  1294. display: flex;
  1295. flex-direction: row;
  1296. align-items: center;
  1297. width: 100%;
  1298. padding-left: 0;
  1299. margin-left: 0;
  1300. }
  1301. .ranking-name {
  1302. color: #000000;
  1303. font-size: 32rpx;
  1304. font-weight: normal;
  1305. flex: 1;
  1306. margin-left: 20rpx;
  1307. }
  1308. .ranking-number {
  1309. background: #38bdf8;
  1310. display: flex;
  1311. align-items: center;
  1312. justify-content: center;
  1313. }
  1314. .ranking-circle-wrapper {
  1315. width: 100rpx;
  1316. display: flex;
  1317. justify-content: center;
  1318. align-items: center;
  1319. }
  1320. .ranking-number.size-1 {
  1321. width: 60rpx;
  1322. height: 60rpx;
  1323. border-radius: 30rpx;
  1324. }
  1325. .ranking-number.size-2 {
  1326. width: 70rpx;
  1327. height: 70rpx;
  1328. border-radius: 35rpx;
  1329. }
  1330. .ranking-number.size-3 {
  1331. width: 80rpx;
  1332. height: 80rpx;
  1333. border-radius: 40rpx;
  1334. }
  1335. .ranking-number-text {
  1336. color: #ffffff;
  1337. font-weight: normal;
  1338. text-align: center;
  1339. font-size: 28rpx;
  1340. }
  1341. .ranking-number.size-1 .ranking-number-text {
  1342. font-size: 18rpx;
  1343. }
  1344. .ranking-number.size-2 .ranking-number-text {
  1345. font-size: 20rpx;
  1346. }
  1347. .ranking-number.size-3 .ranking-number-text {
  1348. font-size: 22rpx;
  1349. }
  1350. .ranking-label {
  1351. color: #ff0000;
  1352. font-size: 32rpx;
  1353. white-space: nowrap;
  1354. margin-left: 20rpx;
  1355. }
  1356. .no-data {
  1357. display: flex;
  1358. justify-content: center;
  1359. align-items: center;
  1360. height: 200rpx;
  1361. }
  1362. .no-data-text {
  1363. font-size: 28rpx;
  1364. color: #999;
  1365. }
  1366. </style>