list.vue 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <script setup lang="ts">
  2. /**
  3. * 成员/班级列表页(views/org/member/list.vue)—— useListPage + ListPage(菜单「成员/班级」)。
  4. * 作用域纪律:institutionId/campusId 只从 useScope().toQuery() 取。
  5. */
  6. import { listMembers, type MemberDTO } from '@/api/org/member';
  7. import ListPage from '@/components/ListPage.vue';
  8. import type { ListColumn } from '@/components/list-column';
  9. import { useListPage } from '@/composables/use-list-page';
  10. import { useScope } from '@/store/scope';
  11. import { formatDateTime, formatStatus } from '@/utils/format';
  12. const scope = useScope();
  13. /** 关键字筛选(页面级状态) */
  14. const keyword = ref('');
  15. /** 人员类型筛选:'all' 哨兵(el-segmented);'staff'/'student'/'parent' 对齐 MemberDTO.personType */
  16. const personTypeSeg = ref<string>('all');
  17. /** 列表状态(useListPage 收口;fetcher 并入作用域) */
  18. const { rows, total, page, pageSize, loading, error, fetchList } = useListPage<MemberDTO>({
  19. fetcher: (p, ps) =>
  20. listMembers({
  21. ...scope.toQuery(),
  22. keyword: keyword.value || undefined,
  23. personType: personTypeSeg.value === 'all' ? undefined : (personTypeSeg.value as 'staff' | 'student' | 'parent'),
  24. page: p,
  25. pageSize: ps,
  26. }),
  27. });
  28. /** 人员类型中文映射 */
  29. const personTypeText: Record<string, string> = { staff: '教职工', student: '学生', parent: '家长' };
  30. /** 列定义 */
  31. const columns: ListColumn<MemberDTO>[] = [
  32. { prop: 'realName', label: '姓名', minWidth: 120 },
  33. { prop: 'personType', label: '类型', minWidth: 90, formatter: (row) => personTypeText[row.personType] ?? row.personType },
  34. { prop: 'phone', label: '手机号', minWidth: 130 },
  35. { prop: 'role', label: '角色', minWidth: 120 },
  36. { prop: 'campusId', label: '校区', minWidth: 80, formatter: (row) => (row.campusId > 0 ? `#${row.campusId}` : '机构级') },
  37. { prop: 'status', label: '状态', minWidth: 90, formatter: (row) => formatStatus(row.status) },
  38. { prop: 'createdAt', label: '创建时间', minWidth: 150, formatter: (row) => formatDateTime(row.createdAt) },
  39. ];
  40. function onSearch(): void {
  41. page.value = 1;
  42. void fetchList();
  43. }
  44. onMounted(() => {
  45. void fetchList();
  46. });
  47. </script>
  48. <template>
  49. <div class="member-list">
  50. <ListPage
  51. v-model:page="page"
  52. v-model:page-size="pageSize"
  53. :rows="rows"
  54. :total="total"
  55. :loading="loading"
  56. :error="error"
  57. :columns="columns"
  58. @search="onSearch"
  59. >
  60. <template #toolbar>
  61. <el-input v-model="keyword" placeholder="搜索姓名" clearable class="toolbar-input" />
  62. <el-segmented
  63. v-model="personTypeSeg"
  64. :options="[{ label: '全部', value: 'all' }, { label: '教职工', value: 'staff' }, { label: '学生', value: 'student' }, { label: '家长', value: 'parent' }]"
  65. />
  66. </template>
  67. </ListPage>
  68. </div>
  69. </template>
  70. <style scoped>
  71. .member-list {
  72. width: 100%;
  73. }
  74. </style>