index.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <view class="user-list-page">
  3. <NavBar title="用户列表" bgColor="#fff" color="#000"></NavBar>
  4. <!-- 顶部操作栏 -->
  5. <view class="action-bar">
  6. <view class="search-box">
  7. <uni-icons type="search" size="14" color="#999"></uni-icons>
  8. <input type="text" v-model="searchValue" placeholder="搜索姓名/手机号" class="search-input" />
  9. </view>
  10. <picker :range="shopOptions" range-key="text" @change="onShopChange">
  11. <view class="filter-btn">
  12. <text>{{ shopOptions[filterShop].text }}</text>
  13. <uni-icons type="bottom" size="12" color="#666"></uni-icons>
  14. </view>
  15. </picker>
  16. <button size="mini" class="add-btn" @click="goToAdd">+ 新增</button>
  17. </view>
  18. <!-- 用户列表 -->
  19. <view class="list-container">
  20. <view class="user-card" v-for="user in users" :key="user.id">
  21. <view class="user-header">
  22. <image :src="user.avatar" class="user-avatar" mode="aspectFill"></image>
  23. <view class="user-info-main">
  24. <text class="user-name">{{ user.name }}</text>
  25. <text class="phone-row">{{ user.phone }}</text>
  26. </view>
  27. <view class="user-status">
  28. <switch :checked="user.isActive" color="#ff9800" style="transform: scale(0.6);"
  29. @change="onStatusChange(user)" />
  30. <text class="status-text">{{ user.isActive ? '正常' : '禁用' }}</text>
  31. </view>
  32. </view>
  33. <view class="user-body">
  34. <view class="info-row">
  35. <text class="label">住址:</text>
  36. <text class="value">{{ user.address }}</text>
  37. </view>
  38. <view class="info-grid">
  39. <view class="grid-cell" @click="goToPetList(user)">
  40. <text class="label">关联宠物</text>
  41. <text class="value text-warning">{{ user.petCount }}只</text>
  42. </view>
  43. <view class="grid-cell" @click="goToOrderList(user)">
  44. <text class="label">订单数量</text>
  45. <text class="value">{{ user.orderCount }}单</text>
  46. </view>
  47. </view>
  48. <view class="source-box">
  49. <text class="source-tag">{{ user.source }}</text>
  50. <text class="create-time">创建时间: {{ user.createTime }}</text>
  51. </view>
  52. </view>
  53. <view class="card-actions">
  54. <button size="mini" class="action-btn" @click.stop="goToDetail(user)">详情</button>
  55. <button size="mini" class="action-btn" @click.stop="goToEdit(user)">编辑</button>
  56. </view>
  57. </view>
  58. </view>
  59. </view>
  60. </template>
  61. <script setup>
  62. import { ref } from 'vue'
  63. import NavBar from '@/components/nav-bar/index.vue'
  64. import userMockData from '@/mock/user.json'
  65. const searchValue = ref('')
  66. const filterShop = ref(0)
  67. const shopOptions = [{ text: '全部', value: 0 }, { text: '三里屯店', value: 1 }, { text: '浦东新区店', value: 2 }]
  68. const onShopChange = (e) => { filterShop.value = Number(e.detail.value) }
  69. const users = ref(userMockData)
  70. const goToAdd = () => uni.navigateTo({ url: '/pages/my/user/add/index' })
  71. const goToDetail = (user) => uni.navigateTo({ url: '/pages/my/user/detail/index' })
  72. const goToEdit = (user) => uni.navigateTo({ url: '/pages/my/user/edit/index' })
  73. const goToPetList = (user) => uni.navigateTo({ url: `/pages/my/pet/list/index?userId=${user.id}` })
  74. const goToOrderList = (user) => uni.reLaunch({ url: '/pages/order/list/index' })
  75. const onStatusChange = (user) => {
  76. user.isActive = !user.isActive
  77. uni.showToast({ title: user.isActive ? '已启用' : '已禁用', icon: 'none' })
  78. }
  79. </script>
  80. <style lang="scss" scoped>
  81. .user-list-page {
  82. min-height: 100vh;
  83. background: #f2f2f2;
  84. padding-bottom: 40rpx;
  85. }
  86. .action-bar {
  87. display: flex;
  88. align-items: center;
  89. padding: 20rpx 24rpx;
  90. background: #fff;
  91. gap: 16rpx;
  92. }
  93. .search-box {
  94. flex: 1;
  95. display: flex;
  96. align-items: center;
  97. background: #f5f5f5;
  98. border-radius: 32rpx;
  99. padding: 12rpx 20rpx;
  100. gap: 12rpx;
  101. }
  102. .search-input {
  103. flex: 1;
  104. font-size: 26rpx;
  105. background: transparent;
  106. }
  107. .filter-btn {
  108. display: flex;
  109. align-items: center;
  110. gap: 8rpx;
  111. background: #f5f5f5;
  112. border-radius: 32rpx;
  113. padding: 12rpx 20rpx;
  114. font-size: 24rpx;
  115. color: #666;
  116. }
  117. .add-btn {
  118. font-size: 24rpx;
  119. font-weight: bold;
  120. background: linear-gradient(90deg, #ffd53f, #ff9500);
  121. color: #333;
  122. border: none;
  123. border-radius: 32rpx;
  124. padding: 12rpx 24rpx;
  125. white-space: nowrap;
  126. }
  127. .list-container {
  128. padding: 24rpx;
  129. }
  130. .user-card {
  131. background: #fff;
  132. border-radius: 24rpx;
  133. padding: 28rpx;
  134. margin-bottom: 24rpx;
  135. }
  136. .user-header {
  137. display: flex;
  138. align-items: center;
  139. margin-bottom: 24rpx;
  140. padding-bottom: 24rpx;
  141. border-bottom: 1rpx solid #f9f9f9;
  142. }
  143. .user-avatar {
  144. width: 88rpx;
  145. height: 88rpx;
  146. border-radius: 50%;
  147. background: #f0f0f0;
  148. margin-right: 24rpx;
  149. }
  150. .user-info-main {
  151. flex: 1;
  152. }
  153. .user-name {
  154. display: block;
  155. font-size: 32rpx;
  156. font-weight: bold;
  157. color: #333;
  158. margin-bottom: 8rpx;
  159. }
  160. .phone-row {
  161. display: block;
  162. font-size: 26rpx;
  163. color: #666;
  164. }
  165. .user-status {
  166. display: flex;
  167. flex-direction: column;
  168. align-items: center;
  169. gap: 4rpx;
  170. }
  171. .status-text {
  172. font-size: 20rpx;
  173. color: #ff9800;
  174. }
  175. .user-body {
  176. font-size: 26rpx;
  177. }
  178. .info-row {
  179. display: flex;
  180. margin-bottom: 16rpx;
  181. }
  182. .label {
  183. color: #999;
  184. width: 100rpx;
  185. flex-shrink: 0;
  186. }
  187. .value {
  188. color: #333;
  189. flex: 1;
  190. }
  191. .info-grid {
  192. display: flex;
  193. background: #fdfdfd;
  194. border: 1rpx solid #f2f2f2;
  195. border-radius: 12rpx;
  196. padding: 20rpx;
  197. margin-bottom: 20rpx;
  198. }
  199. .grid-cell {
  200. flex: 1;
  201. display: flex;
  202. flex-direction: column;
  203. align-items: center;
  204. gap: 8rpx;
  205. }
  206. .grid-cell .label {
  207. width: auto;
  208. font-size: 24rpx;
  209. }
  210. .grid-cell .value {
  211. font-size: 30rpx;
  212. font-weight: bold;
  213. }
  214. .text-warning {
  215. color: #ff9800;
  216. }
  217. .source-box {
  218. background: #fff8e1;
  219. padding: 16rpx;
  220. border-radius: 8rpx;
  221. }
  222. .source-tag {
  223. display: block;
  224. color: #ff9800;
  225. font-weight: bold;
  226. font-size: 24rpx;
  227. margin-bottom: 8rpx;
  228. }
  229. .create-time {
  230. font-size: 22rpx;
  231. color: #a1887f;
  232. }
  233. .card-actions {
  234. display: flex;
  235. justify-content: flex-end;
  236. gap: 20rpx;
  237. margin-top: 24rpx;
  238. padding-top: 24rpx;
  239. border-top: 1rpx dashed #eee;
  240. }
  241. .action-btn {
  242. border: 1rpx solid #e0e0e0;
  243. color: #666;
  244. font-size: 24rpx;
  245. background: transparent;
  246. border-radius: 12rpx;
  247. padding: 8rpx 24rpx;
  248. }
  249. </style>