index.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <template>
  2. <!-- TODO @芋艿:【优化:全局样式】后续要全局样式么 -->
  3. <view class="min-h-screen bg-[#f5f5f5]">
  4. <!-- 顶部导航栏 -->
  5. <wd-navbar
  6. title="用户管理"
  7. left-arrow placeholder safe-area-inset-top fixed
  8. @click-left="handleBack"
  9. />
  10. <!-- 搜索组件 -->
  11. <SearchForm
  12. :search-params="queryParams"
  13. @search="handleQuery"
  14. @reset="handleReset"
  15. />
  16. <!-- 用户列表 -->
  17. <view class="p-24rpx">
  18. <view
  19. v-for="item in list"
  20. :key="item.id"
  21. class="mb-24rpx rounded-12rpx bg-white"
  22. @click="handleDetail(item)"
  23. >
  24. <view class="relative p-24rpx">
  25. <view class="absolute right-24rpx top-24rpx">
  26. <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="item.status" />
  27. </view>
  28. <view class="flex items-center gap-16rpx">
  29. <wd-img
  30. v-if="item.avatar"
  31. :src="item.avatar"
  32. :width="40"
  33. :height="40"
  34. mode="aspectFill"
  35. round
  36. />
  37. <view
  38. v-else
  39. class="h-80rpx w-80rpx flex items-center justify-center rounded-full bg-[#1890ff] text-32rpx text-white"
  40. >
  41. {{ (item.nickname || item.username)?.charAt(0) }}
  42. </view>
  43. <view>
  44. <view class="text-32rpx text-[#333] font-semibold">
  45. {{ item.nickname || item.username }}
  46. </view>
  47. <view class="text-24rpx text-[#999]">
  48. {{ item.deptName || '未分配部门' }}
  49. </view>
  50. </view>
  51. </view>
  52. <view v-if="item.loginDate" class="absolute bottom-24rpx right-24rpx text-22rpx text-[#999]">
  53. 登录时间:{{ formatDate(item.loginDate) }}
  54. </view>
  55. </view>
  56. </view>
  57. <!-- 加载更多 -->
  58. <view v-if="loadMoreState !== 'loading' && list.length === 0" class="py-100rpx text-center">
  59. <wd-status-tip image="content" tip="暂无用户数据" />
  60. </view>
  61. <wd-loadmore
  62. v-if="list.length > 0"
  63. :state="loadMoreState"
  64. @reload="loadMore"
  65. />
  66. </view>
  67. <!-- 新增按钮 -->
  68. <wd-fab
  69. v-if="hasAccessByCodes(['system:user:create'])"
  70. position="right-bottom"
  71. type="primary"
  72. :expandable="false"
  73. @click="handleAdd"
  74. />
  75. </view>
  76. </template>
  77. <script lang="ts" setup>
  78. import type { SearchFormData } from './components/search-form.vue'
  79. import type { User } from '@/api/system/user'
  80. import type { LoadMoreState } from '@/http/types'
  81. import { onReachBottom } from '@dcloudio/uni-app'
  82. import { onMounted, reactive, ref } from 'vue'
  83. import { getUserPage } from '@/api/system/user'
  84. import { useAccess } from '@/hooks/useAccess'
  85. import { navigateBackPlus } from '@/utils'
  86. import { DICT_TYPE } from '@/utils/constants'
  87. import SearchForm from './components/search-form.vue'
  88. import { formatDate } from '@/utils/date';
  89. definePage({
  90. style: {
  91. navigationBarTitleText: '',
  92. navigationStyle: 'custom',
  93. },
  94. })
  95. const { hasAccessByCodes } = useAccess()
  96. const total = ref(0) // 列表的总页数
  97. const list = ref<User[]>([]) // 列表的数据
  98. const loadMoreState = ref<LoadMoreState>('loading') // 加载更多状态
  99. const queryParams = reactive({
  100. pageNo: 1,
  101. pageSize: 10,
  102. username: undefined as string | undefined,
  103. nickname: undefined as string | undefined,
  104. })
  105. /** 返回上一页 */
  106. function handleBack() {
  107. navigateBackPlus()
  108. }
  109. /** 查询用户列表 */
  110. async function getList() {
  111. loadMoreState.value = 'loading'
  112. try {
  113. const data = await getUserPage(queryParams)
  114. list.value = [...list.value, ...data.list]
  115. total.value = data.total
  116. loadMoreState.value = list.value.length >= total.value ? 'finished' : 'loading'
  117. } catch {
  118. queryParams.pageNo = queryParams.pageNo > 1 ? queryParams.pageNo - 1 : 1
  119. loadMoreState.value = 'error'
  120. }
  121. }
  122. /** 搜索按钮操作 */
  123. function handleQuery(data?: SearchFormData) {
  124. queryParams.username = data?.username
  125. queryParams.nickname = data?.nickname
  126. queryParams.pageNo = 1
  127. list.value = [] // 清空列表
  128. getList()
  129. }
  130. /** 重置按钮操作 */
  131. function handleReset() {
  132. handleQuery()
  133. }
  134. /** 加载更多 */
  135. function loadMore() {
  136. if (loadMoreState.value === 'finished') {
  137. return
  138. }
  139. queryParams.pageNo++
  140. getList()
  141. }
  142. /** 新增用户 */
  143. function handleAdd() {
  144. uni.navigateTo({
  145. url: '/pages-system/user/form/index',
  146. })
  147. }
  148. /** 查看详情 */
  149. function handleDetail(item: User) {
  150. uni.navigateTo({
  151. url: `/pages-system/user/detail/index?id=${item.id}`,
  152. })
  153. }
  154. /** 触底加载更多 */
  155. onReachBottom(() => {
  156. loadMore()
  157. })
  158. /** 初始化 */
  159. onMounted(() => {
  160. getList()
  161. })
  162. </script>
  163. <style lang="scss" scoped>
  164. </style>