wuheng 1 gadu atpakaļ
vecāks
revīzija
254855e565
33 mainītis faili ar 607 papildinājumiem un 1149 dzēšanām
  1. 7 0
      src/locales/lang/en.ts
  2. 7 0
      src/locales/lang/zh-cn.ts
  3. 59 0
      src/router/modules/archives.ts
  4. 2 2
      src/router/modules/lesson.ts
  5. 10 1
      src/typings/page-route.d.ts
  6. 7 0
      src/typings/system.d.ts
  7. 5 0
      src/views/archives/attendance/index.vue
  8. 5 0
      src/views/archives/other/index.vue
  9. 5 0
      src/views/archives/scores/index.vue
  10. 5 0
      src/views/archives/students/index.vue
  11. 4 0
      src/views/index.ts
  12. 1 1
      src/views/lesson/calendar/index.vue
  13. 3 0
      src/views/lesson/checkin/crud.ts
  14. 3 0
      src/views/lesson/classroom/crud.ts
  15. 3 0
      src/views/lesson/group/crud.ts
  16. 3 0
      src/views/lesson/student/crud.ts
  17. 3 0
      src/views/management/attendance/crud.ts
  18. 60 0
      src/views/management/role/api.ts
  19. 26 76
      src/views/management/role/crud.ts
  20. 9 83
      src/views/management/role/index.vue
  21. 0 108
      src/views/management/role/queryUser.vue
  22. 0 31
      src/views/management/role/userPa.vue
  23. 25 34
      src/views/management/sort/api.ts
  24. 0 95
      src/views/management/sort/components/column-search.vue
  25. 0 201
      src/views/management/sort/components/table-action-add.vue
  26. 130 0
      src/views/management/sort/crud.ts
  27. 9 299
      src/views/management/sort/index.vue
  28. 3 0
      src/views/management/student/crud.ts
  29. 62 0
      src/views/management/user/api.ts
  30. 0 69
      src/views/management/user/components/column-setting.vue
  31. 0 147
      src/views/management/user/components/table-action-modal.vue
  32. 137 0
      src/views/management/user/curd.ts
  33. 14 2
      src/views/management/user/index.vue

+ 7 - 0
src/locales/lang/en.ts

@@ -89,6 +89,13 @@ const locale: LocaleMessages<I18nType.Schema> = {
         student: 'group student',
         checkin: 'student sign in'
       },
+      archives: {
+        _value: 'archives',
+        scores: 'scores',
+        students: 'students base',
+        attendance: 'attendance',
+        other: 'other'
+      },
       about: 'About'
     }
   }

+ 7 - 0
src/locales/lang/zh-cn.ts

@@ -89,6 +89,13 @@ const locale: LocaleMessages<I18nType.Schema> = {
         student: '班级学员',
         checkin: '学员签到'
       },
+      archives: {
+        _value: '档案管理',
+        scores: '学员成绩',
+        students: '基本信息',
+        attendance: '签到信息',
+        other: '其他信息'
+      },
       about: '关于'
     }
   }

+ 59 - 0
src/router/modules/archives.ts

@@ -0,0 +1,59 @@
+const archives: AuthRoute.Route = {
+  name: 'archives',
+  path: '/archives',
+  component: 'basic',
+  children: [
+    {
+      name: 'archives_scores',
+      path: '/archives/scores',
+      component: 'self',
+      meta: {
+        title: '成绩查看',
+        i18nTitle: 'message.routes.archives.scores',
+        requiresAuth: true,
+        icon: 'arcticons:score-sheets'
+      }
+    },
+    {
+      name: 'archives_students',
+      path: '/archives/students',
+      component: 'self',
+      meta: {
+        title: '资料查看',
+        i18nTitle: 'message.routes.archives.students',
+        requiresAuth: true,
+        icon: 'icons8:student'
+      }
+    },
+    {
+      name: 'archives_attendance',
+      path: '/archives/attendance',
+      component: 'self',
+      meta: {
+        title: '考勤查看',
+        i18nTitle: 'message.routes.archives.attendance',
+        requiresAuth: true,
+        icon: 'openmoji:mark'
+      }
+    },
+    {
+      name: 'archives_other',
+      path: '/archives/other',
+      component: 'self',
+      meta: {
+        title: '其他查看',
+        i18nTitle: 'message.routes.archives.other',
+        requiresAuth: true,
+        icon: 'basil:other-1-outline'
+      }
+    }
+  ],
+  meta: {
+    title: '档案管理',
+    i18nTitle: 'message.routes.archives._value',
+    icon: 'vaadin:archives',
+    order: 9
+  }
+};
+
+export default archives;

+ 2 - 2
src/router/modules/lesson.ts

@@ -1,4 +1,4 @@
-const management: AuthRoute.Route = {
+const lesson: AuthRoute.Route = {
   name: 'lesson',
   path: '/lesson',
   component: 'basic',
@@ -78,4 +78,4 @@ const management: AuthRoute.Route = {
   }
 };
 
-export default management;
+export default lesson;

+ 10 - 1
src/typings/page-route.d.ts

@@ -52,6 +52,11 @@ declare namespace PageRoute {
     | 'function_tab-detail'
     | 'function_tab-multi-detail'
     | 'function_tab'
+    | 'archives'
+    | 'archives_scores'
+    | 'archives_students'
+    | 'archives_other'
+    | 'archives_attendance'
     | 'lesson'
     | 'lesson_calendar'
     | 'lesson_checkin'
@@ -122,11 +127,15 @@ declare namespace PageRoute {
     | 'function_tab'
     | 'lesson_calendar'
     | 'lesson_checkin'
-    | 'management_attendance'
     | 'lesson_classroom'
     | 'lesson_group'
     | 'lesson_schedule'
     | 'lesson_student'
+    | 'archives_scores'
+    | 'archives_students'
+    | 'archives_other'
+    | 'archives_attendance'
+    | 'management_attendance'
     | 'management_auth'
     | 'management_role'
     | 'management_route'

+ 7 - 0
src/typings/system.d.ts

@@ -402,6 +402,13 @@ declare namespace I18nType {
         student: string;
         checkin: string;
       };
+      archives: {
+        _value: string;
+        other: string;
+        attendance: string;
+        students: string;
+        scores: string;
+      };
       about: string;
     };
   }

+ 5 - 0
src/views/archives/attendance/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div></div>
+</template>
+<script setup lang="ts"></script>
+<style scoped></style>

+ 5 - 0
src/views/archives/other/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div></div>
+</template>
+<script setup lang="ts"></script>
+<style scoped></style>

+ 5 - 0
src/views/archives/scores/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div></div>
+</template>
+<script setup lang="ts"></script>
+<style scoped></style>

+ 5 - 0
src/views/archives/students/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div></div>
+</template>
+<script setup lang="ts"></script>
+<style scoped></style>

+ 4 - 0
src/views/index.ts

@@ -38,6 +38,10 @@ export const views: Record<
   lesson_group: () => import('./lesson/group/index.vue'),
   lesson_schedule: () => import('./lesson/schedule/index.vue'),
   lesson_student: () => import('./lesson/student/index.vue'),
+  archives_scores: () => import('./archives/scores/index.vue'),
+  archives_students: () => import('./archives/students/index.vue'),
+  archives_other: () => import('./archives/other/index.vue'),
+  archives_attendance: () => import('./archives/attendance/index.vue'),
   management_attendance: () => import('./management/attendance/index.vue'),
   management_auth: () => import('./management/auth/index.vue'),
   management_role: () => import('./management/role/index.vue'),

+ 1 - 1
src/views/lesson/calendar/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="wh-full bg-white">
-    <n-form :label-width="200" inline>
+    <n-form style="justify-content: center; margin-top: 1rem" :label-width="200" inline>
       <n-form-item class="min-w-34" label="请选择查询学员" path="scheduleParamsOptions.studentId">
         <n-select
           v-model:value="scheduleParamsOptions.studentId"

+ 3 - 0
src/views/lesson/checkin/crud.ts

@@ -5,6 +5,9 @@ import { updateAttendance, getStudentList } from './api';
 function curd({ context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
   return {
     crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
       search: {
         show: false
       },

+ 3 - 0
src/views/lesson/classroom/crud.ts

@@ -13,6 +13,9 @@ const dictOpt = {
 function curd(): CreateCrudOptionsRet {
   return {
     crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
       request: {
         pageRequest: async ({ page, query }) => {
           const { total, data } = await pageRequest(page.offset + 1, page.limit, query);

+ 3 - 0
src/views/lesson/group/crud.ts

@@ -16,6 +16,9 @@ function curd({ context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
   const goToCalendar = context?.goToCalendar;
   return {
     crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
       request: {
         pageRequest: async ({ page, query }) => {
           const { total, data } = await pageRequest(page.offset + 1, page.limit, query);

+ 3 - 0
src/views/lesson/student/crud.ts

@@ -4,6 +4,9 @@ import { queryClassStudent, submitGroupStudentUpdate, submitGroupStudentDelete }
 function curd(): CreateCrudOptionsRet {
   return {
     crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
       pagination: {
         show: false
       },

+ 3 - 0
src/views/management/attendance/crud.ts

@@ -4,6 +4,9 @@ import { queryAttendance, queryClassAll, queryClassRoomList } from './api';
 function curd(): CreateCrudOptionsRet {
   return {
     crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
       request: {
         pageRequest: async ({ page, query }) => {
           const { total, data } = await queryAttendance(page.offset + 1, page.limit, query);

+ 60 - 0
src/views/management/role/api.ts

@@ -0,0 +1,60 @@
+import { request } from '@/service/request';
+
+// 参数接口
+export interface DepartmentParams {
+  id?: number;
+  depname?: string;
+  address?: string;
+  phone?: string;
+  email?: string;
+  manager?: string;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  createUid?: number;
+  disabled?: string;
+}
+
+// 响应接口
+export interface Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+  total: number;
+}
+
+/**
+ * 查询部门
+ * @param {string} pageNum
+ * @param {string} pageSize
+ * @param {object} params EasSysDepartment
+ * @param {number} params.id
+ * @param {string} params.depname 部门名称
+ * @param {string} params.address 部门地址
+ * @param {string} params.phone 部门电话
+ * @param {string} params.email 部门电子邮箱
+ * @param {string} params.manager 部门负责人
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function queryDepartment(
+  pageNum: number,
+  pageSize: number,
+  params: DepartmentParams
+): Promise<Service.RequestResult<DepartmentParams[]>> {
+  return request.post(`/department/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+}
+
+export function addDepartment(params: DepartmentParams): Promise<Service.RequestResult<Res>> {
+  return request.post(`/department/add`, params);
+}
+
+export function editDepartment(params: DepartmentParams): Promise<Service.RequestResult<Res>> {
+  return request.put(`/department/update`, params);
+}
+
+export function delDepartment(id: number): Promise<Service.RequestResult<Res>> {
+  return request.delete(`/department/delete?id=${id}`);
+}

+ 26 - 76
src/views/management/sort/crud.tsx → src/views/management/role/crud.ts

@@ -1,47 +1,33 @@
-import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq } from '@fast-crud/fast-crud';
+import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
 import { dict } from '@fast-crud/fast-crud';
 import dayjs from 'dayjs';
-import * as api from './api';
-import { selectAll_1 } from './api';
+import { queryDepartment, addDepartment, editDepartment, delDepartment } from './api';
 export default function createCrudOptions(): CreateCrudOptionsRet {
-  const pageRequest = async (query: any): Promise<any> => {
-    // 调用你自己的函数从接口获取数据
-    const data = await selectAll_1(query);
-    return {
-      rows: data,
-      total: data.length
-    };
-  };
-  const editRequest = async (ctx: EditReq) => {
-    const { form, row } = ctx;
-    form.id = row.id;
-    return api.UpdateObj(form);
-  };
-  const delRequest = async (ctx: DelReq) => {
-    const { row } = ctx;
-    return api.DelObj(row.id);
-  };
-
-  const addRequest = async (req: AddReq) => {
-    const { form } = req;
-    return api.AddObj(form);
-  };
   return {
     crudOptions: {
       container: {
         is: 'fs-layout-card'
       },
       request: {
-        pageRequest,
-        addRequest,
-        editRequest,
-        delRequest
+        pageRequest: async ({ page, query }) => {
+          const { total, data } = await queryDepartment(page.offset + 1, page.limit, query);
+          return { records: data, total, currentPage: page.offset, pageSize: page.limit };
+        },
+        addRequest: ({ form }) => {
+          return addDepartment(form);
+        },
+        editRequest: ({ form }) => {
+          editDepartment(form);
+        },
+        delRequest: ({ row }) => {
+          return delDepartment(row.id);
+        }
       },
       columns: {
         id: {
           title: 'ID',
           key: 'id',
-          type: 'number',
+          type: 'text',
           search: { show: true },
           column: {
             width: 50,
@@ -51,10 +37,10 @@ export default function createCrudOptions(): CreateCrudOptionsRet {
             show: false
           }
         },
-        name: {
+        depname: {
           title: '部门名称',
           search: { show: true },
-          key: 'name',
+          key: 'depname',
           type: 'text',
           column: {
             width: 150,
@@ -64,40 +50,27 @@ export default function createCrudOptions(): CreateCrudOptionsRet {
             show: true
           }
         },
-        // select: {
-        //   title: '状态',
-        //   search: { show: true },
-        //   type: 'dict-select',
-        // 	align: 'center',
-        //   // dict: dict({
-        //   //   url: '/mock/crud/demo/dict'
-        //   // })
-        // },
-        description: {
+        address: {
           title: '部门地址',
-          key: 'description',
+          key: 'address',
           type: 'text',
           search: { show: true },
           column: {
-            width: 200,
             align: 'center'
           }
         },
         createTime: {
           key: 'createTime',
           title: '创建时间',
-          type: 'text',
+          type: 'datetime',
           column: {
             width: 250,
             align: 'center'
           },
           search: { show: true },
-          // naive 默认仅支持数字类型时间戳作为日期输入与输出
-          // 字符串类型的时间需要转换格式
           valueBuilder(context) {
             const { value, row, key } = context;
             if (value) {
-              // naive 默认仅支持时间戳作为日期输入与输出
               row[key] = dayjs(value).valueOf();
             }
           },
@@ -111,18 +84,15 @@ export default function createCrudOptions(): CreateCrudOptionsRet {
         modifyTime: {
           title: '修改时间',
           key: 'modifyTime',
-          type: 'text',
+          type: 'datetime',
           align: 'center',
           column: {
             width: 250,
             align: 'center'
           },
-          // naive 默认仅支持数字类型时间戳作为日期输入与输出
-          // 字符串类型的时间需要转换格式
           valueBuilder(context) {
             const { value, row, key } = context;
             if (value) {
-              // naive 默认仅支持时间戳作为日期输入与输出
               row[key] = dayjs(value).valueOf();
             }
           },
@@ -146,34 +116,14 @@ export default function createCrudOptions(): CreateCrudOptionsRet {
           key: 'disabled',
           title: '状态',
           type: 'dict-select',
-          column: {
-            width: 100,
-            align: 'center'
-          },
           dict: dict({
-            url: '/mock/crud/demo/dict'
+            data: [
+              { value: 'N', label: '启用' },
+              { value: 'Y', label: '禁用' }
+            ]
           }),
           search: { show: true }
         }
-        // richtext: {
-        //   title: '富文本',
-        //   type: 'editor-wang5',
-        //   column: {
-        //     // cell中不显示
-        //     show: false
-        //   },
-        //   form: {
-        //     col: {
-        //       // 横跨两列
-        //       span: 24
-        //     },
-        //     component: {
-        //       style: {
-        //         height: '300px'
-        //       }
-        //     }
-        //   }
-        // }
       }
     }
   };

+ 9 - 83
src/views/management/role/index.vue

@@ -1,91 +1,17 @@
 <template>
-  <div class="h-full overflow-hidden">
-    <n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
-      <n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
-    </n-card>
+  <div class="h-full bg-white">
+    <fs-crud ref="crudRef" v-bind="crudBinding" />
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue';
-import type { Ref } from 'vue';
-import type { DataTableColumns, PaginationProps } from 'naive-ui';
-import { query } from '~/src/service/api/user';
-import type { QueryParams } from '~/src/service/api/user';
+import { onMounted } from 'vue';
+import { useFs } from '@fast-crud/fast-crud';
+import createCrudOptions from './crud';
 
-const tableData = ref<QueryParams[]>([]);
-const pagination: PaginationProps = ref({
-  page: 1,
-  pageSize: 10,
-  showSizePicker: true,
-  pageSizes: [10, 20, 50]
-  // onChange: (page: number) => {
-  //   // 处理页码变化
-  // },
-  // onUpdatePageSize: (pageSize: number) => {
-  //   // 处理每页显示数量变化
-  // }
-}).value;
+const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
 
-async function getTableData() {
-  const pageNum = pagination.page as number; // 页码
-  const pageSize = pagination.pageSize as number; // 当前所在数
-  const params: QueryParams = {};
-
-  query(pageNum, pageSize, params).then(res => {
-    tableData.value = res.data as [];
-  });
-}
-
-const columns: Ref<DataTableColumns<QueryParams>> = ref([
-  {
-    type: 'selection',
-    align: 'center'
-  },
-  {
-    key: 'name',
-    title: '部门名称',
-    align: 'center'
-  },
-  {
-    key: 'description',
-    title: '部门地址',
-    align: 'center'
-  },
-  {
-    key: 'isActive',
-    title: '是否激活',
-    align: 'center',
-    render: (row: QueryParams) => {
-      return row.isActive ? '是' : '否';
-    }
-  },
-  {
-    key: 'createTime',
-    title: '创建时间',
-    align: 'center'
-  },
-  {
-    key: 'modifyTime',
-    title: '修改时间',
-    align: 'center'
-  },
-  {
-    key: 'createUid',
-    title: '创建用户ID',
-    align: 'center'
-  },
-  {
-    key: 'disabled',
-    title: '状态',
-    align: 'center'
-  }
-]) as Ref<DataTableColumns<QueryParams>>;
-
-function init() {
-  getTableData();
-}
-
-// 初始化
-init();
+onMounted(() => {
+  crudExpose.doRefresh();
+});
 </script>

+ 0 - 108
src/views/management/role/queryUser.vue

@@ -1,108 +0,0 @@
-<template>
-  <div class="h-full overflow-hidden">
-    <n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
-      <n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
-    </n-card>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { ref } from 'vue';
-import type { Ref } from 'vue';
-import type { DataTableColumns, PaginationProps } from 'naive-ui';
-import { query } from '~/src/service/api/user';
-import type { QueryParams } from '~/src/service/api/user';
-
-const tableData = ref<QueryParams[]>([]);
-
-const pagination: PaginationProps = ref({
-  page: 1,
-  pageSize: 10,
-  showSizePicker: true,
-  pageSizes: [10, 20, 50],
-  onChange: () => {
-    // 处理页码变化
-  },
-  onUpdatePageSize: () => {
-    // 处理每页显示数量变化
-  }
-}).value;
-
-async function getTableData() {
-  const pageNum = pagination.page as number;
-  const pageSize = pagination.pageSize as number;
-
-  const params: QueryParams = {
-    name: '',
-    description: '',
-    isActive: '',
-    createTime: '',
-    modifyTime: '',
-    createUid: 0,
-    disabled: ''
-  };
-
-  query(pageNum, pageSize, params).then(res => {
-    tableData.value = res.data as [];
-  });
-}
-
-const columns: Ref<DataTableColumns<QueryParams>> = ref([
-  {
-    type: 'selection',
-    align: 'center'
-  },
-  {
-    key: 'depname',
-    title: '部门名称',
-    align: 'center'
-  },
-  {
-    key: 'address',
-    title: '部门地址',
-    align: 'center'
-  },
-  {
-    key: 'phone',
-    title: '部门电话',
-    align: 'center'
-  },
-  {
-    key: 'email',
-    title: '部门电子邮箱',
-    align: 'center'
-  },
-  {
-    key: 'manager',
-    title: '部门负责人',
-    align: 'center'
-  },
-  {
-    key: 'createTime',
-    title: '创建时间',
-    align: 'center'
-  },
-  {
-    key: 'modifyTime',
-    title: '修改时间',
-    align: 'center'
-  },
-  {
-    key: 'createUid',
-    title: '创建用户ID',
-    align: 'center'
-  },
-  {
-    key: 'disabled',
-    title: '状态',
-    align: 'center'
-  }
-]) as Ref<DataTableColumns<QueryParams>>;
-
-function init() {
-  getTableData();
-}
-
-// 初始化
-init();
-</script>

+ 0 - 31
src/views/management/role/userPa.vue

@@ -1,31 +0,0 @@
-<template>
-  <n-space vertical>
-    <n-pagination v-model:page="pageNum" :page-count="pageSize" />
-  </n-space>
-</template>
-
-<script lang="ts">
-import { reactive, defineComponent, ref } from 'vue';
-import type { QueryParams } from '~/src/service/api/user';
-
-export default defineComponent({
-  setup() {
-    const pageNum = ref(2);
-    const pageSize = ref<any>(10);
-    const queryData = reactive<QueryParams>({});
-    function queryList() {}
-
-    return {
-      // 每页条数, 可自定义 page-size
-      pageNum,
-      // page-count
-      pageSize,
-      queryList,
-      queryData
-      // page: ref(2),
-      // pageSize: ref(20),
-      // queryData:reactive({})
-    };
-  }
-});
-</script>

+ 25 - 34
src/views/management/sort/api.ts

@@ -1,49 +1,40 @@
-import type { UserPageQuery } from '@fast-crud/fast-crud';
 import { request } from '@/service/request';
 
+// 参数接口
+export interface QueryParams {
+  id?: number;
+  name?: string;
+  description?: string;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  createUid?: number;
+  disabled?: string;
+}
+
 // 响应接口
-export interface SelectAll_1Res {
+export interface Res {
   status: boolean;
   msg: string;
   data: Record<string, unknown>;
+  total: number;
 }
 
-const apiPrefix = ``;
-
-export type HeaderGroupRecord = {
-  id: number;
-  [key: string]: any;
-};
-
-function resHandle(res: any) {
-  return res.data;
-}
-export async function selectAll_1(query: UserPageQuery) {
-  const res = await request.get(`/selectAll`, query);
-  return resHandle(res);
-}
-
-export async function AddObj(obj: HeaderGroupRecord) {
-  const res = await request.post(`${apiPrefix}/add`, obj);
-  return resHandle(res);
-}
-
-export async function UpdateObj(obj: HeaderGroupRecord) {
-  const res = await request.post(`${apiPrefix}/update`, obj);
-  return resHandle(res);
+export function search(
+  pageNum: number,
+  pageSize: number,
+  params: QueryParams
+): Promise<Service.RequestResult<QueryParams[]>> {
+  return request.post(`/category/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
 }
 
-export async function DelObj(id: number) {
-  const res = await request.post(`${apiPrefix}/delete`, { id });
-  return resHandle(res);
+export function add(params: QueryParams): Promise<Service.RequestResult<Res>> {
+  return request.post(`/department/add`, params);
 }
 
-export async function GetObj(id: number) {
-  const res = await request.get(`${apiPrefix}/info`, { params: { id } });
-  return resHandle(res);
+export function edit(params: QueryParams): Promise<Service.RequestResult<Res>> {
+  return request.put(`/department/update`, params);
 }
 
-export async function BatchDelete(ids: number[]) {
-  const res = await request.post(`${apiPrefix}/batchDelete`, { ids });
-  return resHandle(res);
+export function del(id: number): Promise<Service.RequestResult<Res>> {
+  return request.delete(`/department/delete?id=${id}`);
 }

+ 0 - 95
src/views/management/sort/components/column-search.vue

@@ -1,95 +0,0 @@
-<template>
-  <n-space>
-    <n-input-group>
-      <n-input-group>
-        <span class="w-80px mr-5px line-height-33.99px">学科名称</span>
-        <n-input :style="{ width: '31%', marginRight: '2%' }" clearable />
-        <span class="w-80px mr-5px line-height-33.99px">学科描述</span>
-        <n-input :style="{ width: '31%', marginRight: '2%' }" clearable />
-        <span class="w-100px line-height-33.99px">创建用户ID</span>
-        <n-input-number :style="{ width: '22%', marginRight: '10%' }" clearable />
-      </n-input-group>
-      <n-input-group>
-        <span class="line-height-33.99px mr-5px w-70px">创建时间</span>
-        <n-date-picker :style="{ width: '13%' }" />
-        <n-time-picker :style="{ width: '14.2%', marginRight: '2%' }" />
-        <span class="line-height-33.99px mr-5px w-70px">修改时间</span>
-        <n-date-picker :style="{ width: '13%' }" />
-        <n-time-picker :style="{ width: '14.2%', marginRight: '2%' }" />
-        <span class="w-50px line-height-33.99px">状态</span>
-        <n-select :options="selectOptions" :style="{ width: '21%', marginRight: '2%' }" clearable />
-      </n-input-group>
-      <span class="w-40px mr-5px line-height-33.99px">页码</span>
-      <n-input-number
-        v-model="pagination.page"
-        :style="{ width: '48%', marginRight: '2%' }"
-        placeholder="请输入页码..."
-        clearable
-        @change="searchCondition"
-      />
-      <span class="w-40px mr-5px line-height-33.99px">条数</span>
-      <n-input-number
-        v-model="pagination.pageSize"
-        :style="{ width: '48%' }"
-        placeholder="请输入数据条数..."
-        clearable
-        @change="searchCondition"
-      />
-      <n-button type="primary" ghost @click="searchCondition()"> 搜索 </n-button>
-    </n-input-group>
-  </n-space>
-</template>
-
-<script lang="ts">
-import { defineComponent, ref } from 'vue';
-import type { PaginationProps } from 'naive-ui';
-import { selectByCondition_1 } from '~/src/service/api/sort';
-import type { SelectByCondition_1Params } from '~/src/service/api/sort';
-
-const searchData = ref<any[]>([]);
-const pagination: PaginationProps = ref({
-  page: 1,
-  pageSize: 10,
-  showSizePicker: true,
-  pageSizes: [10, 20, 50],
-  onChange: (page: number) => {
-    // 处理页码变化
-    pagination.page = page;
-  },
-  onUpdatePageSize: (pageSize: number) => {
-    // 处理每页显示数量变化
-    pagination.pageSize = pageSize;
-    pagination.page = 1;
-  }
-}).value;
-
-function searchCondition() {
-  const pageNum = pagination.page as number;
-  const pageSize = pagination.pageSize as number;
-  const params: SelectByCondition_1Params = {};
-
-  selectByCondition_1(pageNum, pageSize, params).then(res => {
-    searchData.value = res.data as [];
-  });
-}
-
-export default defineComponent({
-  setup() {
-    return {
-      searchCondition,
-      searchData,
-      pagination,
-      selectOptions: ref([
-        {
-          label: 'Y',
-          value: 'Y'
-        },
-        {
-          label: 'N',
-          value: 'N'
-        }
-      ])
-    };
-  }
-});
-</script>

+ 0 - 201
src/views/management/sort/components/table-action-add.vue

@@ -1,201 +0,0 @@
-<template>
-  <n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
-    <n-form ref="formRef" label-placement="left" :label-width="100" :model="formModel" :rules="rules">
-      <n-grid :cols="24" :x-gap="18">
-        <!-- <n-form-item-grid-item :span="12" label="ID" path="id">
-					<n-input-number v-model:value="formModel.id" />
-				</n-form-item-grid-item> -->
-        <n-form-item-grid-item :span="12" label="学科名称" path="name">
-          <n-input v-model:value="formModel.name" clearable />
-        </n-form-item-grid-item>
-        <!-- <n-form-item-grid-item :span="12" label="创建时间" path="createTime">
-					  <n-date-picker v-model:value="timestamp" type="datetime" clearable />
-					<n-input v-model:value="formModel.createTime" />
-				</n-form-item-grid-item> -->
-        <n-form-item-grid-item :span="12" label="学科描述" path="description">
-          <n-input v-model:value="formModel.description" clearable />
-          <!-- <n-radio-group v-model:value="formModel.description">
-						<n-radio v-for="item in genderOptions" :key="item.value" :value="item.value">{{ item.label }}</n-radio>
-					</n-radio-group> -->
-        </n-form-item-grid-item>
-        <!-- <n-form-item-grid-item :span="12" label="修改时间" path="email">
-					<n-date-picker v-show="props.type==='edit'" v-model:value="formModel.modifyTime" type="datetime" clearable />
-				</n-form-item-grid-item> -->
-        <n-form-item-grid-item :span="12" label="创建用户ID" path="createUid">
-          <n-input-number v-model:value="formModel.createUid" />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="状态" path="disabled">
-          <n-select v-model:value="formModel.disabled" :options="userStatusOptions" />
-        </n-form-item-grid-item>
-      </n-grid>
-      <n-space class="w-full pt-16px" :size="24" justify="end">
-        <n-button class="w-72px" @click="closeModal">取消</n-button>
-        <n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
-      </n-space>
-    </n-form>
-  </n-modal>
-</template>
-
-<script setup lang="ts">
-import { ref, computed, reactive, watch } from 'vue';
-import type { FormInst, FormItemRule } from 'naive-ui';
-import dayjs from 'dayjs';
-import { userStatusOptions } from '@/constants';
-import { createRequiredFormRule } from '@/utils';
-import type {
-  AddEasEduCategoryParams,
-  UpdateEasEduCategoryParams,
-  SelectByCondition_1Params
-} from '~/src/service/api/sort';
-import { addEasEduCategory, updateEasEduCategory } from '~/src/service/api/sort';
-
-export interface Props {
-  /** 弹窗可见性 */
-  visible: boolean;
-  /**
-   * 弹窗类型
-   * add: 新增
-   * edit: 编辑
-   */
-  type?: 'add' | 'edit';
-  /** 编辑的表格行数据 */
-  editData?: SelectByCondition_1Params | null;
-}
-
-export type ModalType = NonNullable<Props['type']>;
-
-defineOptions({ name: 'TableActionAdd' });
-
-const props = withDefaults(defineProps<Props>(), {
-  type: 'add',
-  editData: null
-});
-
-interface Emits {
-  (e: 'update:visible', visible: boolean): void;
-  /** 点击协议 */
-  (e: 'searchCondition'): void;
-}
-
-const emit = defineEmits<Emits>();
-
-const modalVisible = computed({
-  get() {
-    return props.visible;
-  },
-  set(visible) {
-    emit('update:visible', visible);
-  }
-});
-
-const closeModal = () => {
-  modalVisible.value = false;
-};
-
-const title = computed(() => {
-  const titles: Record<ModalType, string> = {
-    add: '添加用户',
-    edit: '编辑用户'
-  };
-  return titles[props.type];
-});
-
-const formRef = ref<HTMLElement & FormInst>();
-
-interface FormModel
-  extends Pick<SelectByCondition_1Params, 'id' | 'name' | 'description' | 'createUid' | 'disabled' | 'createTime'> {
-  modifyTime: string;
-}
-
-const formModel = reactive<FormModel>(createDefaultFormModel());
-
-const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
-  id: createRequiredFormRule('请输入用户名'),
-  name: createRequiredFormRule('请输入学科名称'),
-  description: createRequiredFormRule('请进行部门描述'),
-  createTime: createRequiredFormRule('请输入创建时间'),
-  modifyTime: createRequiredFormRule('请输入修改时间'),
-  createUid: createRequiredFormRule('请输入创建用户ID'),
-  disabled: createRequiredFormRule('请选择用户状态')
-};
-// 'createTime' |'modifyTime'|
-function createDefaultFormModel(): FormModel {
-  return {
-    id: null,
-    name: '',
-    description: '',
-    createTime: '',
-    modifyTime: '',
-    createUid: null,
-    disabled: null
-  };
-}
-
-function handleUpdateFormModel(model: SelectByCondition_1Params) {
-  Object.assign(formModel, model);
-}
-
-function handleUpdateFormModelByModalType() {
-  const handlers: Record<ModalType, () => void> = {
-    add: () => {
-      const defaultFormModel = createDefaultFormModel();
-      handleUpdateFormModel(defaultFormModel);
-    },
-    edit: () => {
-      if (props.editData) {
-        handleUpdateFormModel(props.editData);
-      }
-    }
-  };
-
-  handlers[props.type]();
-}
-async function handleSubmit() {
-  await formRef.value?.validate();
-  if (props.type === 'add') {
-    const params: AddEasEduCategoryParams = {
-      // id: formModel.id,
-      name: formModel.name,
-      description: formModel.description,
-      createUid: formModel.createUid,
-      disabled: formModel.disabled
-    };
-    // 调用新增接口
-    addEasEduCategory(params)
-      .then(() => {
-        window.$message?.success('新增成功!');
-        emit('searchCondition');
-        closeModal();
-      })
-      .catch(() => {
-        window.$message?.error('新增失败!');
-      });
-  } else {
-    const currentTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
-    handleUpdateFormModel({ modifyTime: currentTime });
-    const params: UpdateEasEduCategoryParams = {
-      id: props.editData?.id, // 编辑数据的id
-      name: formModel.name,
-      description: formModel.description,
-      createTime: formModel.createTime,
-      modifyTime: formModel.modifyTime,
-      createUid: formModel.createUid,
-      disabled: formModel.disabled
-    };
-    // 调用更新接口
-    updateEasEduCategory(params).then(() => {
-      window.$message?.success('更新成功!');
-      emit('searchCondition');
-      closeModal();
-    });
-  }
-}
-watch(
-  () => props.visible,
-  newValue => {
-    if (newValue) {
-      handleUpdateFormModelByModalType();
-    }
-  }
-);
-</script>

+ 130 - 0
src/views/management/sort/crud.ts

@@ -0,0 +1,130 @@
+import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import { dict } from '@fast-crud/fast-crud';
+import dayjs from 'dayjs';
+import { search, add, edit, del } from './api';
+export default function createCrudOptions(): CreateCrudOptionsRet {
+  return {
+    crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
+      request: {
+        pageRequest: async ({ page, query }) => {
+          const { total, data } = await search(page.offset + 1, page.limit, query);
+          return { records: data, total, currentPage: page.offset, pageSize: page.limit };
+        },
+        addRequest: ({ form }) => {
+          return add(form);
+        },
+        editRequest: ({ form }) => {
+          edit(form);
+        },
+        delRequest: ({ row }) => {
+          return del(row.id);
+        }
+      },
+      columns: {
+        id: {
+          title: 'ID',
+          key: 'id',
+          type: 'text',
+          search: { show: true },
+          column: {
+            width: 50,
+            align: 'center'
+          },
+          form: {
+            show: false
+          }
+        },
+        depname: {
+          title: '部门名称',
+          search: { show: true },
+          key: 'depname',
+          type: 'text',
+          column: {
+            width: 150,
+            align: 'center'
+          },
+          form: {
+            show: true
+          }
+        },
+        address: {
+          title: '部门地址',
+          key: 'address',
+          type: 'text',
+          search: { show: true },
+          column: {
+            align: 'center'
+          }
+        },
+        createTime: {
+          key: 'createTime',
+          title: '创建时间',
+          type: 'datetime',
+          column: {
+            width: 250,
+            align: 'center'
+          },
+          search: { show: true },
+          valueBuilder(context) {
+            const { value, row, key } = context;
+            if (value) {
+              row[key] = dayjs(value).valueOf();
+            }
+          },
+          valueResolve(context) {
+            const { value, form, key } = context;
+            if (value) {
+              form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
+            }
+          }
+        },
+        modifyTime: {
+          title: '修改时间',
+          key: 'modifyTime',
+          type: 'datetime',
+          align: 'center',
+          column: {
+            width: 250,
+            align: 'center'
+          },
+          valueBuilder(context) {
+            const { value, row, key } = context;
+            if (value) {
+              row[key] = dayjs(value).valueOf();
+            }
+          },
+          valueResolve(context) {
+            const { value, form, key } = context;
+            if (value) {
+              form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
+            }
+          }
+        },
+        createUid: {
+          title: '创建用户ID',
+          key: 'createUid',
+          type: 'file-uploader',
+          column: {
+            width: 250,
+            align: 'center'
+          }
+        },
+        disabled: {
+          key: 'disabled',
+          title: '状态',
+          type: 'dict-select',
+          dict: dict({
+            data: [
+              { value: 'N', label: '启用' },
+              { value: 'Y', label: '禁用' }
+            ]
+          }),
+          search: { show: true }
+        }
+      }
+    }
+  };
+}

+ 9 - 299
src/views/management/sort/index.vue

@@ -1,307 +1,17 @@
 <template>
-  <div class="h-full overflow-hidden">
-    <n-scrollbar>
-      <n-card title="课程分类" :bordered="false" class="rounded-16px shadow-sm">
-        <n-space class="pb-14px" justify="space-between">
-          <!-- 加入查询组件 -->
-          <n-space>
-            <n-button type="primary" @click="addTableData">
-              <icon-ic-round-plus class="mr-4px text-20px" />
-              新增
-            </n-button>
-            <n-button type="error" @click="deleteTableData">
-              <icon-ic-round-delete class="mr-4px text-20px" />
-              删除
-            </n-button>
-            <!-- 添加查询按钮 -->
-            <n-button type="primary">
-              <icon-simple-line-icons:magnifier class="mr-4px text-16px" />
-              查询
-            </n-button>
-            <div class="mr-5px"></div>
-            <n-input-group>
-              <n-input-number
-                v-model:value="conditionParams.id"
-                :style="{ width: '98%', marginRight: '2%' }"
-                placeholder="请输入ID..."
-                clearable
-              />
-              <n-input
-                :style="{ width: '98%', marginRight: '2%' }"
-                :value="conditionParams.name"
-                placeholder="请输入学科名称..."
-                clearable
-                @input="event => (conditionParams.name = event)"
-              />
-              <n-input
-                :style="{ width: '98%', marginRight: '2%' }"
-                :value="conditionParams.description"
-                placeholder="请输入学科描述..."
-                clearable
-                @input="event => (conditionParams.description = event)"
-              />
-              <n-input-number
-                v-model:value="conditionParams.createUid"
-                :style="{ width: '120%', marginRight: '2%' }"
-                placeholder="请输入用户ID..."
-                clearable
-              />
-              <n-input
-                v-model:value="conditionParams.createTime"
-                :style="{ width: '98%', marginRight: '2%' }"
-                placeholder="请输入创建时间..."
-                clearable
-                @input="event => (conditionParams.createTime = event)"
-              />
-              <n-input
-                v-model:value="conditionParams.modifyTime"
-                :style="{ width: '98%', marginRight: '2%' }"
-                placeholder="请输入修改时间..."
-                clearable
-                @input="event => (conditionParams.modifyTime = event)"
-              />
-              <n-select
-                v-model:value="conditionParams.disabled"
-                :options="userStatusOptions"
-                :style="{ width: '98%', marginRight: '2%' }"
-                placeholder="输入查询状态..."
-                clearable
-                @input="event => (conditionParams.disabled = event)"
-              />
-              <n-button type="primary" ghost @click="searchCondition()"> 搜索 </n-button>
-            </n-input-group>
-          </n-space>
-          <n-space align="center" :size="18">
-            <n-button size="small" type="primary" @click="searchCondition">
-              <icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
-              刷新表格
-            </n-button>
-          </n-space>
-        </n-space>
-        <n-data-table
-          :columns="columns"
-          :data="tableData"
-          :loading="loading"
-          :row-key="rowKey"
-          @update:checked-row-keys="handleCheck"
-        />
-        <n-pagination
-          v-model:page="pagination.page"
-          v-model:page-size="pagination.pageSize"
-          class="flex-justify-end"
-          :item-count="pagination.itemCount"
-          show-size-picker
-          :page-sizes="[10, 20, 30, 40]"
-          :on-update:page="(page: number)=>{pagination.page = page; searchCondition()}"
-          :on-update:page-size="(pageSize: number)=>{pagination.pageSize = pageSize; searchCondition()}"
-        />
-        <table-action-add
-          v-model:visible="visible"
-          :type="modalType"
-          :pagination="pagination"
-          :edit-data="(editData as SelectByCondition_1Params)"
-          @search-condition="searchCondition"
-        />
-      </n-card>
-    </n-scrollbar>
+  <div class="h-full bg-white">
+    <fs-crud ref="crudRef" v-bind="crudBinding" />
   </div>
-  <!-- <div>
-			<PDFView :pdfUrl="tableData" />
-		</div> -->
 </template>
 
-<script setup lang="tsx">
-import type { Ref } from 'vue';
-import { ref, reactive, watch } from 'vue';
-import { NButton, NSpace, NTag, NPopconfirm } from 'naive-ui';
-import type { DataTableColumns, PaginationProps, DataTableRowKey } from 'naive-ui';
-import { userStatusOptions, userStatusLabels } from '@/constants';
-import { useBoolean, useLoading } from '@/hooks';
-import { deleteById, selectByCondition_1 } from '~/src/service/api/sort';
-import type { SelectByCondition_1Params } from '~/src/service/api/sort';
-import type { ModalType } from './components/table-action-add.vue';
-import TableActionAdd from './components/table-action-add.vue';
-// import PDFView from "../../.././components/View/pdfPreview.vue"
-type RowData = {
-  key: number;
-  id: number;
-  name: string;
-  description: string;
-  createTime: string;
-  modifyTime: string;
-  createUid: number;
-  disabled: string;
-};
-const conditionParams: SelectByCondition_1Params = reactive({
-  id: null,
-  name: null,
-  description: null,
-  createUid: null,
-  createTime: null,
-  modifyTime: null,
-  disabled: null
-});
-
-const { loading, startLoading, endLoading } = useLoading(false);
-const { bool: visible, setTrue: openModal } = useBoolean();
-const tableData = ref<SelectByCondition_1Params[]>([]);
-
-const pagination: PaginationProps = ref({
-  page: 1,
-  pageSize: 10,
-  showSizePicker: true,
-  itemCount: 1
-}).value;
+<script setup lang="ts">
+import { onMounted } from 'vue';
+import { useFs } from '@fast-crud/fast-crud';
+import createCrudOptions from './crud';
 
-async function searchCondition() {
-  startLoading();
-  const res = await selectByCondition_1(pagination.page as number, pagination.pageSize as number, conditionParams);
-  tableData.value = res.data as [];
-  if (pagination.itemCount === 1) {
-    pagination.itemCount = Number(res.total);
-  }
-  endLoading();
-}
+const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
 
-function init() {
-  searchCondition();
-}
-// 在条件改变时立即触发搜索
-watch(conditionParams, () => {
-  if (Object.values(conditionParams).every(value => value === '' || value === null)) {
-    // 如果所有条件参数都为空,则查询所有数据
-    searchCondition();
-  }
-  searchCondition();
+onMounted(() => {
+  crudExpose.doRefresh();
 });
-// 初始化
-init();
-
-const checkedRowKeysRef = ref<DataTableRowKey[]>([]);
-const s = ref<DataTableRowKey[]>([]);
-
-const modalType = ref<ModalType>('add');
-
-function setModalType(type: ModalType) {
-  modalType.value = type;
-}
-// 添加
-function addTableData() {
-  openModal();
-  setModalType('add');
-}
-
-const editData = ref<SelectByCondition_1Params | null>(null);
-function setEditData(data: SelectByCondition_1Params | null) {
-  editData.value = data;
-}
-function handleEditTable(rowId: number) {
-  const findItem = tableData.value.find(item => item.id === rowId);
-  setModalType('edit');
-  openModal();
-  if (findItem) {
-    setEditData(findItem);
-  }
-}
-
-function handleDeleteTable(rowId: number) {
-  const rowIdNumber = Number(rowId);
-  deleteById(rowIdNumber).then(() => {
-    searchCondition(); // 删除完成后刷新表格数据
-  });
-}
-const rowKey = (row: RowData) => row.id;
-function handleCheck(rowKeys: DataTableRowKey[]) {
-  checkedRowKeysRef.value = rowKeys;
-}
-async function deleteTableData() {
-  const ids = checkedRowKeysRef.value.filter(item => !s.value?.includes(item));
-  s.value = checkedRowKeysRef.value;
-  if (ids.length !== 0) {
-    for (const id of ids) {
-      deleteById(id as number);
-    }
-    window.$message?.success('删除成功!');
-    searchCondition(); // 删除完成后刷新表格数据
-  } else {
-    // 没有选择任何行时的操作
-    window.$message?.error('未选择要删除的行');
-  }
-}
-
-const columns: Ref<DataTableColumns<UserManagement.User>> = ref([
-  {
-    type: 'selection',
-    align: 'center'
-  },
-  {
-    key: 'id',
-    title: 'ID',
-    align: 'center'
-  },
-  {
-    key: 'name',
-    title: '学科名称',
-    align: 'center'
-  },
-  {
-    key: 'description',
-    title: '学科描述',
-    align: 'center'
-  },
-  {
-    key: 'createTime',
-    title: '创建时间',
-    align: 'center'
-  },
-  {
-    key: 'modifyTime',
-    title: '修改时间',
-    align: 'center'
-  },
-  {
-    key: 'createUid',
-    title: '创建用户ID',
-    align: 'center'
-  },
-  {
-    key: 'disabled',
-    title: '状态',
-    align: 'center',
-
-    render: row => {
-      if (row.disabled) {
-        const tagTypes: Record<UserManagement.UserStatusKey, NaiveUI.ThemeColor> = {
-          N: 'error',
-          Y: 'success'
-        };
-        return <NTag type={tagTypes[row.disabled]}>{userStatusLabels[row.disabled]}</NTag>;
-      }
-      return '';
-    }
-  },
-  {
-    key: 'actions',
-    title: '操作',
-    align: 'center',
-    render: row => {
-      return (
-        <NSpace justify={'center'}>
-          <NButton size={'small'} onClick={() => handleEditTable(row.id)}>
-            编辑
-          </NButton>
-          <NPopconfirm onPositiveClick={() => handleDeleteTable(row.id)}>
-            {{
-              default: () => '确认删除',
-              trigger: () => <NButton size={'small'}>删除</NButton>
-            }}
-          </NPopconfirm>
-          <NButton size={'small'} onClick={() => handleEditTable(row.id)}>
-            查看
-          </NButton>
-        </NSpace>
-      );
-    }
-  }
-]);
 </script>

+ 3 - 0
src/views/management/student/crud.ts

@@ -5,6 +5,9 @@ import { addRequest, delRequest, editRequest, pageRequest } from './api';
 function curd({ context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
   return {
     crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
       request: {
         pageRequest: async ({ page, query }) => {
           const { total, data } = await pageRequest(page.offset + 1, page.limit, query);

+ 62 - 0
src/views/management/user/api.ts

@@ -0,0 +1,62 @@
+import { request } from '@/service/request';
+
+// 参数接口
+export interface UserParams {
+  id?: number;
+  username?: string;
+  passwd?: string;
+  email?: string;
+  relname?: string;
+  phone?: string;
+  address?: string;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  createUid?: number;
+  disabled?: string;
+}
+
+// 响应接口
+export interface Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+  code: number;
+}
+
+/**
+ * 查询用户信息
+ * @param {string} pageNum
+ * @param {string} pageSize
+ * @param {object} params EasSysUserinfo
+ * @param {number} params.id 用户ID
+ * @param {string} params.username 用户名
+ * @param {string} params.passwd 密码
+ * @param {string} params.email 电子邮件
+ * @param {string} params.relname 姓名
+ * @param {string} params.phone 电话
+ * @param {string} params.address 地址
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function queryUser(
+  pageNum: number,
+  pageSize: number,
+  params: UserParams
+): Promise<Service.RequestResult<UserParams[]>> {
+  return request.post(`/userinfo/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+}
+
+export function addUser(params: UserParams): Promise<Service.RequestResult<Res>> {
+  return request.post(`/userinfo/add`, params);
+}
+
+export function updateUser(params: UserParams): Promise<Service.RequestResult<Res>> {
+  return request.put(`/userinfo/update`, params);
+}
+
+export function deleteUser(id: number): Promise<Service.RequestResult<Res>> {
+  return request.delete(`/userinfo/delete?id=${id}`);
+}

+ 0 - 69
src/views/management/user/components/column-setting.vue

@@ -1,69 +0,0 @@
-<template>
-  <n-popover placement="bottom" trigger="click">
-    <template #trigger>
-      <n-button size="small" type="primary">
-        <icon-ant-design-setting-outlined class="mr-4px text-16px" />
-        表格列设置
-      </n-button>
-    </template>
-    <div class="w-180px">
-      <vue-draggable v-model="list" item-key="key">
-        <template #item="{ element }">
-          <div v-if="element.key" class="flex-y-center h-36px px-12px hover:bg-primary_active">
-            <icon-mdi-drag class="mr-8px text-20px cursor-move" />
-            <n-checkbox v-model:checked="element.checked">
-              {{ element.title }}
-            </n-checkbox>
-          </div>
-        </template>
-      </vue-draggable>
-    </div>
-  </n-popover>
-</template>
-
-<script setup lang="ts">
-import { ref, watch } from 'vue';
-import type { DataTableColumn } from 'naive-ui';
-import VueDraggable from 'vuedraggable';
-
-type Column = DataTableColumn<UserManagement.User>;
-
-interface Props {
-  columns: Column[];
-}
-
-const props = defineProps<Props>();
-
-interface Emits {
-  (e: 'update:columns', columns: Column[]): void;
-}
-
-const emit = defineEmits<Emits>();
-
-type List = Column & { checked?: boolean };
-
-const list = ref(initList());
-
-function initList(): List[] {
-  return props.columns.map(item => ({ ...item, checked: true }));
-}
-
-watch(
-  list,
-  newValue => {
-    const newColumns = newValue.filter(item => item.checked);
-
-    const columns: Column[] = newColumns.map(item => {
-      const column = { ...item };
-      delete column.checked;
-
-      return column;
-    }) as Column[];
-
-    emit('update:columns', columns);
-  },
-  { deep: true }
-);
-</script>
-
-<style scoped></style>

+ 0 - 147
src/views/management/user/components/table-action-modal.vue

@@ -1,147 +0,0 @@
-<template>
-  <n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
-    <n-form ref="formRef" label-placement="left" :label-width="100" :model="formModel" :rules="rules">
-      <n-grid :cols="24" :x-gap="18">
-        <n-form-item-grid-item :span="12" label="学科名称" path="name">
-          <n-input v-model:value="formModel.name" clearable />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="学科描述" path="description">
-          <n-input v-model:value="formModel.description" clearable />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="创建用户ID" path="createUid">
-          <n-input-number v-model:value="formModel.createUid" />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="状态" path="disabled">
-          <n-select v-model:value="formModel.disabled" :options="userStatusOptions" />
-        </n-form-item-grid-item>
-      </n-grid>
-      <n-space class="w-full pt-16px" :size="24" justify="end">
-        <n-button class="w-72px" @click="closeModal">取消</n-button>
-        <n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
-      </n-space>
-    </n-form>
-  </n-modal>
-</template>
-
-<script setup lang="ts">
-import { ref, computed, reactive, watch } from 'vue';
-import type { FormInst, FormItemRule } from 'naive-ui';
-import { userStatusOptions } from '@/constants';
-import { createRequiredFormRule } from '@/utils';
-import type { AddEasEduCategoryParams } from '~/src/service/api/sort';
-import { addEasEduCategory } from '~/src/service/api/sort';
-
-export interface Props {
-  /** 弹窗可见性 */
-  visible: boolean;
-  /**
-   * 弹窗类型
-   * add: 新增
-   * edit: 编辑
-   */
-  type?: 'add' | 'edit';
-  /** 编辑的表格行数据 */
-  editData?: UserManagement.User | null;
-}
-
-export type ModalType = NonNullable<Props['type']>;
-
-defineOptions({ name: 'TableActionAdd' });
-
-const props = withDefaults(defineProps<Props>(), {
-  type: 'add',
-  editData: null
-});
-
-interface Emits {
-  (e: 'update:visible', visible: boolean): void;
-}
-const emit = defineEmits<Emits>();
-
-const modalVisible = computed({
-  get() {
-    return props.visible;
-  },
-  set(visible) {
-    emit('update:visible', visible);
-  }
-});
-const closeModal = () => {
-  modalVisible.value = false;
-};
-
-const title = computed(() => {
-  const titles: Record<ModalType, string> = {
-    add: '添加用户',
-    edit: '编辑用户'
-  };
-  return titles[props.type];
-});
-
-const formRef = ref<HTMLElement & FormInst>();
-
-type FormModel = Pick<UserManagement.User, 'name' | 'description' | 'createUid' | 'disabled'>;
-
-const formModel = reactive<FormModel>(createDefaultFormModel());
-
-const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
-  name: createRequiredFormRule('请输入学科名称'),
-  description: createRequiredFormRule('请进行部门描述'),
-  createUid: createRequiredFormRule('请输入创建用户ID'),
-  disabled: createRequiredFormRule('请选择用户状态')
-};
-function createDefaultFormModel(): FormModel {
-  return {
-    name: '',
-    description: '',
-    createUid: null,
-    disabled: null
-  };
-}
-
-function handleUpdateFormModel(model: Partial<FormModel>) {
-  Object.assign(formModel, model);
-}
-
-function handleUpdateFormModelByModalType() {
-  const handlers: Record<ModalType, () => void> = {
-    add: () => {
-      const defaultFormModel = createDefaultFormModel();
-      handleUpdateFormModel(defaultFormModel);
-    },
-    edit: () => {
-      if (props.editData) {
-        handleUpdateFormModel(props.editData);
-      }
-    }
-  };
-  handlers[props.type]();
-}
-
-async function handleSubmit() {
-  await formRef.value?.validate();
-  const params: AddEasEduCategoryParams = {
-    name: formModel.name,
-    description: formModel.description,
-    createUid: formModel.createUid,
-    disabled: formModel.disabled
-  };
-  addEasEduCategory(params)
-    .then(() => {
-      window.$message?.success('新增成功!');
-      closeModal();
-    })
-    .catch(() => {
-      window.$message?.error('新增失败!');
-    });
-}
-watch(
-  () => props.visible,
-  newValue => {
-    if (newValue) {
-      handleUpdateFormModelByModalType();
-    }
-  }
-);
-</script>
-<style scoped></style>

+ 137 - 0
src/views/management/user/curd.ts

@@ -0,0 +1,137 @@
+import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import { dict } from '@fast-crud/fast-crud';
+import dayjs from 'dayjs';
+import { queryUser, addUser, updateUser, deleteUser } from './api';
+export default function createCrudOptions(): CreateCrudOptionsRet {
+  return {
+    crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
+      request: {
+        pageRequest: async ({ page, query }) => {
+          const { total, data } = await queryUser(page.offset + 1, page.limit, query);
+          return { records: data, total, currentPage: page.offset, pageSize: page.limit };
+        },
+        addRequest: ({ form }) => {
+          return addUser(form);
+        },
+        editRequest: ({ form }) => {
+          updateUser(form);
+        },
+        delRequest: ({ row }) => {
+          return deleteUser(row.id);
+        }
+      },
+      columns: {
+        id: {
+          title: 'ID',
+          key: 'id',
+          type: 'text',
+          search: { show: true },
+          column: {
+            width: 50,
+            align: 'center'
+          },
+          form: {
+            show: false
+          }
+        },
+        username: {
+          title: '用户名',
+          search: { show: true },
+          key: 'username',
+          type: 'text',
+          column: {
+            width: 150,
+            align: 'center'
+          },
+          form: {
+            show: true
+          }
+        },
+        phone: {
+          title: '手机号',
+          key: 'phone',
+          type: 'text',
+          search: { show: true },
+          column: {
+            align: 'center'
+          }
+        },
+        createTime: {
+          key: 'createTime',
+          title: '创建时间',
+          type: 'datetime',
+          column: {
+            width: 250,
+            align: 'center'
+          },
+          search: { show: true },
+          valueBuilder(context) {
+            const { value, row, key } = context;
+            if (value) {
+              row[key] = dayjs(value).valueOf();
+            }
+          },
+          valueResolve(context) {
+            const { value, form, key } = context;
+            if (value) {
+              form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
+            }
+          }
+        },
+        modifyTime: {
+          title: '修改时间',
+          key: 'modifyTime',
+          type: 'datetime',
+          align: 'center',
+          column: {
+            width: 250,
+            align: 'center'
+          },
+          valueBuilder(context) {
+            const { value, row, key } = context;
+            if (value) {
+              row[key] = dayjs(value).valueOf();
+            }
+          },
+          valueResolve(context) {
+            const { value, form, key } = context;
+            if (value) {
+              form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
+            }
+          }
+        },
+        email: {
+          title: '邮箱',
+          key: 'email',
+          type: 'text',
+          column: {
+            align: 'center'
+          }
+        },
+        relname: {
+          title: '姓名',
+          key: 'text',
+          type: 'text',
+          column: {
+            align: 'center'
+          }
+        },
+        disabled: {
+          key: 'disabled',
+          title: '状态',
+          type: 'dict-select',
+          dict: dict({
+            data: [
+              { value: 'N', label: '启用' },
+              { value: 'Y', label: '禁用' }
+            ]
+          }),
+          search: { show: true }
+        }
+      }
+    }
+  };
+}

+ 14 - 2
src/views/management/user/index.vue

@@ -1,7 +1,19 @@
 <template>
-  <div class="h-full overflow-hidden"></div>
+  <div class="h-full overflow-hidden">
+    <fs-crud ref="crudRef" v-bind="crudBinding" />
+  </div>
 </template>
 
-<script setup lang="tsx"></script>
+<script setup lang="ts">
+import { onMounted } from 'vue';
+import { useFs } from '@fast-crud/fast-crud';
+import createCrudOptions from './curd';
+
+const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
+
+onMounted(() => {
+  crudExpose.doRefresh();
+});
+</script>
 
 <style scoped></style>