vehicleList.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <template>
  2. <view class="sub-page">
  3. <TopBar title="车辆列表" show-back />
  4. <view class="px-4 pt-3">
  5. <!-- 筛选 -->
  6. <view class="filter-bar mb-3">
  7. <view
  8. v-for="status in filterStatuses"
  9. :key="status.value"
  10. class="filter-chip"
  11. :class="{ 'filter-chip-active': currentFilter === status.value }"
  12. @click="currentFilter = status.value"
  13. >
  14. {{ status.label }}
  15. </view>
  16. </view>
  17. <view v-if="loading" class="py-10 text-center">
  18. <text class="empty-text">加载中...</text>
  19. </view>
  20. <view v-else-if="filteredVehicles.length === 0">
  21. <EmptyState message="暂无车辆" />
  22. </view>
  23. <view v-else class="gap-y-3">
  24. <view
  25. v-for="vehicle in filteredVehicles"
  26. :key="vehicle.id"
  27. class="vehicle-card"
  28. hover-class="vehicle-card-hover"
  29. :hover-start-time="0"
  30. :hover-stay-time="120"
  31. @click="showVehicleDetail(vehicle)"
  32. >
  33. <view class="flex-1 min-w-0 mr-3">
  34. <text class="plate-number truncate">{{ vehicle.plateNumber }}</text>
  35. <text class="vehicle-type truncate">{{ vehicle.type || '-' }}</text>
  36. </view>
  37. <text class="status-tag flex-shrink-0" :class="getStatusTagClass(vehicle.status)">
  38. {{ getStatusText(vehicle.status) }}
  39. </text>
  40. </view>
  41. </view>
  42. </view>
  43. </view>
  44. </template>
  45. <script setup lang="ts">
  46. import { ref, computed, onMounted } from 'vue'
  47. import { onPullDownRefresh } from '@dcloudio/uni-app'
  48. import TopBar from '../../components/common/TopBar.vue'
  49. import EmptyState from '../../components/common/EmptyState.vue'
  50. import { useVehicleStore } from '../../stores/vehicle'
  51. import type { Vehicle } from '../../types'
  52. const vehicleStore = useVehicleStore()
  53. const loading = ref(false)
  54. const currentFilter = ref('all')
  55. const filterStatuses = [
  56. { value: 'all', label: '全部' },
  57. { value: 'available', label: '可用' },
  58. { value: 'in_use', label: '使用中' },
  59. { value: 'maintenance', label: '维修中' },
  60. ]
  61. const filteredVehicles = computed(() => {
  62. if (currentFilter.value === 'all') return vehicleStore.vehicles
  63. return vehicleStore.vehicles.filter((v) => v.status === currentFilter.value)
  64. })
  65. function getStatusText(status: string) {
  66. const map: Record<string, string> = {
  67. available: '可用',
  68. in_use: '使用中',
  69. maintenance: '维修中',
  70. }
  71. return map[status] || status
  72. }
  73. function getStatusTagClass(status: string) {
  74. const map: Record<string, string> = {
  75. available: 'status-tag-green',
  76. in_use: 'status-tag-blue',
  77. maintenance: 'status-tag-gold',
  78. }
  79. return map[status] || 'status-tag-gray'
  80. }
  81. function showVehicleDetail(vehicle: Vehicle) {
  82. const content = [
  83. `车牌号: ${vehicle.plateNumber}`,
  84. `类型: ${vehicle.type}`,
  85. `尺寸: ${vehicle.size || '-'}`,
  86. `载重: ${vehicle.loadCapacity || '-'}`,
  87. `容积: ${vehicle.capacity || '-'}`,
  88. `驾驶员: ${vehicle.driver || '未分配'}`,
  89. `驾驶员电话: ${vehicle.driverPhone || '-'}`,
  90. `购车日期: ${vehicle.purchaseDate || '-'}`,
  91. `里程: ${vehicle.mileage != null ? `${vehicle.mileage} km` : '-'}`,
  92. `状态: ${getStatusText(vehicle.status)}`,
  93. ].join('\n')
  94. uni.showModal({
  95. title: vehicle.plateNumber,
  96. content,
  97. showCancel: false,
  98. })
  99. }
  100. onMounted(async () => {
  101. loading.value = true
  102. await vehicleStore.fetchVehicles()
  103. loading.value = false
  104. })
  105. onPullDownRefresh(async () => {
  106. await vehicleStore.fetchVehicles()
  107. uni.stopPullDownRefresh()
  108. })
  109. </script>
  110. <style scoped>
  111. .sub-page {
  112. max-width: 430px;
  113. margin: 0 auto;
  114. min-height: 100vh;
  115. background-color: #f6fbf9;
  116. padding-bottom: 24px;
  117. }
  118. .gap-y-3 {
  119. display: flex;
  120. flex-direction: column;
  121. gap: 12px;
  122. }
  123. /* 筛选条:默认白底灰边,激活态绿色 */
  124. .filter-bar {
  125. display: flex;
  126. gap: 8px;
  127. overflow-x: auto;
  128. }
  129. .filter-chip {
  130. padding: 6px 16px;
  131. border-radius: 999px;
  132. font-size: 13px;
  133. color: #6b7280;
  134. background-color: #ffffff;
  135. border: 1px solid #e5e7eb;
  136. flex-shrink: 0;
  137. }
  138. .filter-chip-active {
  139. color: #ffffff;
  140. font-weight: 600;
  141. background: linear-gradient(135deg, #368f6f 0%, #4ba98a 100%);
  142. border-color: transparent;
  143. }
  144. .vehicle-card {
  145. display: flex;
  146. align-items: center;
  147. justify-content: space-between;
  148. padding: 16px;
  149. background-color: rgba(255, 255, 255, 0.85);
  150. backdrop-filter: blur(14px);
  151. -webkit-backdrop-filter: blur(14px);
  152. border: 1px solid rgba(164, 216, 152, 0.35);
  153. border-radius: 16px;
  154. box-shadow: 0 14px 40px -10px rgba(54, 143, 111, 0.22);
  155. }
  156. .plate-number {
  157. display: block;
  158. font-size: 18px;
  159. font-weight: 700;
  160. color: #1f2937;
  161. }
  162. .vehicle-type {
  163. display: block;
  164. margin-top: 4px;
  165. font-size: 12px;
  166. color: #6b7280;
  167. }
  168. .vehicle-card-hover {
  169. background-color: rgba(164, 216, 152, 0.1);
  170. }
  171. .status-tag {
  172. padding: 2px 10px;
  173. border-radius: 999px;
  174. font-size: 11px;
  175. font-weight: 600;
  176. }
  177. .status-tag-green {
  178. color: #368f6f;
  179. background-color: rgba(164, 216, 152, 0.24);
  180. }
  181. .status-tag-blue {
  182. color: #2563eb;
  183. background-color: rgba(37, 99, 235, 0.1);
  184. }
  185. .status-tag-gold {
  186. color: #b7791f;
  187. background-color: rgba(251, 211, 141, 0.25);
  188. }
  189. .status-tag-gray {
  190. color: #6b7280;
  191. background-color: #f3f4f6;
  192. }
  193. .empty-text {
  194. font-size: 13px;
  195. color: #9ca3af;
  196. }
  197. </style>