From 293beb9f5d85d5c9d3b4e33911a9b0148dfdc068 Mon Sep 17 00:00:00 2001 From: Zzz1zz Date: Sun, 6 Jul 2025 00:12:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=BE=E7=BD=AE=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=88=97=E8=A1=A8=E5=8A=9F=E8=83=BD90%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 2 + package.json | 6 +- pnpm-lock.yaml | 22 +- src/api/setting.ts | 132 ++++++- src/components/common/DataTable.md | 236 ++++++++++++ src/components/common/DataTable.vue | 175 +++++++++ src/components/index.ts | 2 + src/composables/README.md | 196 ++++++++++ src/composables/useDataTable.ts | 172 +++++++++ .../composables/useDepartmentList.ts | 1 + src/locale/zh-CN.ts | 26 +- src/views/business/project/index.vue | 130 ++++--- .../components/department-form-modal.vue | 50 ++- src/views/setting/system/index.vue | 23 +- src/views/setting/system/organization.vue | 82 ++-- .../user/components/user-form-drawer.vue | 300 +++++++++++++++ src/views/setting/user/list.vue | 350 ++++++++++++++++-- 17 files changed, 1758 insertions(+), 147 deletions(-) create mode 100644 src/components/common/DataTable.md create mode 100644 src/components/common/DataTable.vue create mode 100644 src/composables/README.md create mode 100644 src/composables/useDataTable.ts rename src/{views/setting => }/composables/useDepartmentList.ts (97%) create mode 100644 src/views/setting/user/components/user-form-drawer.vue diff --git a/.eslintrc.js b/.eslintrc.js index 22937fa..9b3f858 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -67,5 +67,7 @@ module.exports = { 'no-param-reassign': 0, 'prefer-regex-literals': 0, 'import/no-extraneous-dependencies': 0, + // 禁用 prefer-destructuring 规则,因为它对对象解构和数组解构都产生错误警告 + 'prefer-destructuring': 0, }, }; diff --git a/package.json b/package.json index e259a08..fca13be 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ ] }, "dependencies": { - "@arco-design/web-vue": "^2.44.7", + "@arco-design/web-vue": "^2.57.0", "@vueuse/core": "^9.3.0", "arco-design-pro-vue": "^2.7.3", "axios": "^0.24.0", @@ -49,7 +49,6 @@ "vue-router": "^4.0.14" }, "devDependencies": { - "@arco-design/web-vue": "^2.0.0-beta.7", "@arco-plugins/vite-vue": "^1.4.5", "@commitlint/cli": "^17.1.2", "@commitlint/config-conventional": "^17.1.0", @@ -102,8 +101,5 @@ "bin-wrapper": "npm:bin-wrapper-china", "rollup": "^2.56.3", "gifsicle": "5.2.0" - }, - "peerDependencies": { - "@arco-design/web-vue": ">=2.0.0-beta.7" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7231321..2c8e383 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,7 +14,7 @@ importers: .: dependencies: '@arco-design/web-vue': - specifier: ^2.44.7 + specifier: ^2.57.0 version: 2.57.0(vue@3.5.17(typescript@4.9.5)) '@vueuse/core': specifier: ^9.3.0 @@ -115,7 +115,7 @@ importers: version: 8.57.1 eslint-config-airbnb-base: specifier: ^15.0.0 - version: 15.0.0(eslint-plugin-import@2.32.0)(eslint@8.57.1) + version: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1))(eslint@8.57.1) eslint-config-prettier: specifier: ^8.5.0 version: 8.10.0(eslint@8.57.1) @@ -770,41 +770,49 @@ packages: resolution: {integrity: sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.9.2': resolution: {integrity: sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.9.2': resolution: {integrity: sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.9.2': resolution: {integrity: sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.9.2': resolution: {integrity: sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.9.2': resolution: {integrity: sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.9.2': resolution: {integrity: sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.9.2': resolution: {integrity: sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.9.2': resolution: {integrity: sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==} @@ -4753,7 +4761,7 @@ snapshots: '@types/node': 20.5.1 chalk: 4.1.2 cosmiconfig: 8.3.6(typescript@4.9.5) - cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@4.9.5))(ts-node@10.9.2(@types/node@24.0.3)(typescript@4.9.5))(typescript@4.9.5) + cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@4.9.5))(ts-node@10.9.2(@types/node@20.5.1)(typescript@4.9.5))(typescript@4.9.5) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -5839,7 +5847,7 @@ snapshots: core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@4.9.5))(ts-node@10.9.2(@types/node@24.0.3)(typescript@4.9.5))(typescript@4.9.5): + cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@4.9.5))(ts-node@10.9.2(@types/node@20.5.1)(typescript@4.9.5))(typescript@4.9.5): dependencies: '@types/node': 20.5.1 cosmiconfig: 8.3.6(typescript@4.9.5) @@ -6424,7 +6432,7 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0)(eslint@8.57.1): + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1))(eslint@8.57.1): dependencies: confusing-browser-globals: 1.0.11 eslint: 8.57.1 @@ -6460,7 +6468,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -6482,7 +6490,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 diff --git a/src/api/setting.ts b/src/api/setting.ts index efb65df..4f3318b 100644 --- a/src/api/setting.ts +++ b/src/api/setting.ts @@ -13,7 +13,18 @@ export interface DepartmentIdData { departmentId: number | string; } type DepartmentFormRes = HttpResponse; - +// 创建部门 +export function createDepartment( + params: DepartmentFormParams +): Promise { + return axios.post('/department/createDepartment', params); +} +// 更新(编辑)部门 +export function updateDepartment( + params: DepartmentFormParams +): Promise { + return axios.post('/department/updateDepartment', params); +} export interface DepartmentItem { id: number; systemTenantId: number; @@ -41,22 +52,11 @@ export interface DepartmentDetailData { } export type DepartmentDetailRes = HttpResponse; -// 创建部门 -export function createDepartment( - params: DepartmentFormParams -): Promise { - return axios.post('/department/createDepartment', params); -} // 获取部门列表 export function getDepartmentList(): Promise { return axios.get('/department/getDepartmentList'); } -// 更新(编辑)部门 -export function updateDepartment( - params: DepartmentFormParams -): Promise { - return axios.post('/department/updateDepartment', params); -} + export function getDepartmentDetail(id: number): Promise { return axios.get(`/department/getDepartment?id=${id}`); } @@ -68,19 +68,73 @@ export interface UserItem { email: string; // 邮箱 tel: string; // 手机号 status: 1 | 2; // 状态:1 启用,2 禁用 - role_id: number; // 角色id - department_id: number; // 部门id - role_name: string; // 角色名称 - dept_name: string; // 部门名称 + roleId: number; // 角色id + departmentId: number; // 部门id + roleName: string; // 角色名称 + deptName: string; // 部门名称 lastLoginTime: string; // 最后登录时间 } export type UserList = UserItem[]; export type UserListRes = HttpResponse; - +export interface Pagination { + page: number; + page_size: number; + total?: number; + total_pages?: number; +} +export interface AllUserList { + page: Pagination; + user: UserListRes; +} +export type AllUserListRes = HttpResponse; // 后台-用户列表(除主管外所有用户) -export function getAllUserList(params): Promise { +export function getAllUserList(params): Promise { return axios.post('/user/getAllUserList', params); } +// 后台-用户列表(所有用户) +export function getUserList(params): Promise { + return axios.post('/user/getUserList', params); +} +export interface FormUserItem { + username: string; // 用户名 + realname: string; // 姓名 + tel: string | number; // 手机号 + password: string; // 密码 + repassword: string; // 确认密码 + role_id: number | null; // 角色id + department_id: number | null; // 部门id + email: string; // 邮件 + is_manager: 1 | 2; // 是否为主管:1是,2否 +} +// 创建用户 +export function createUser(params: FormUserItem): Promise { + return axios.post('/user/createUser', params); +} +// 更新用户信息 +export function updateUser(params: FormUserItem): Promise { + return axios.post('/user/updateUser', params); +} +interface UserInfo { + user: FormUserItem; +} +export type UserInfoRES = HttpResponse; +// 获取用户信息 +export function getUserInfo(id: number): Promise { + return axios.get(`/user/getUser?id=${id}`); +} +// 删除用户 +export function delUser(id: number): Promise { + return axios.get(`/user/delUser?id=${id}`); +} +export interface DisableUserParam { + id: number; + status: number; +} +// 禁用启用用户 +export function disableUser(params: DisableUserParam): Promise { + return axios.post('/user/disableUser', params); +} + // 部门-向上移动 export function departmentSetUp(id: number): Promise { return axios.get(`/department/setUp?id=${id}`); @@ -93,6 +147,46 @@ export function departmentSetDown(id: number): Promise { export function delDepartment(id: number): Promise { return axios.get(`/department/delDepartment?id=${id}`); } + +export interface RoleItem { + id: number; // 角色id + systemTenantId: number; // 租户名称 + name: string; // 角色名称 + createUserId?: number; // 创建人id + isDelete?: number; // 是否删除 + remark?: string; + dataType: number; // 数据权限类型:1全部数据权限,2自定义数据权限,3仅本人数据 + createdAt: string; + updatedAt: string; +} +// 角色列表 +export interface RoleList { + rolesList: RoleItem[]; +} +export type RoleListRes = HttpResponse; +// 角色列表 +export function getRoleList(): Promise { + return axios.get('/role/getRoleList'); +} + +// 创建角色 +export interface RoleFormParam { + id?: number; // 角色id + name: string; // 姓名 + remark?: string; // 备注 +} +export function createRole(params: RoleFormParam): Promise { + return axios.post('/role/createRole', params); +} +// 更新角色 +export function updateRole(params: RoleFormParam): Promise { + return axios.post('/role/updateRole', params); +} +// 删除角色 +export function delRole(id: number): Promise { + return axios.get(`/role/delRole?id=${id}`); +} + // // 项目信息 // export function getProjectDetail(id: string) { // return axios.get(`/project/getProject?id=${id}`); diff --git a/src/components/common/DataTable.md b/src/components/common/DataTable.md new file mode 100644 index 0000000..56fd908 --- /dev/null +++ b/src/components/common/DataTable.md @@ -0,0 +1,236 @@ +# DataTable 组件使用指南 + +## 概述 + +`DataTable` 是一个基于 Arco Design Vue 的 `a-table` 组件封装,提供了统一的分页配置和事件处理,可以在多个页面中复用。 + +## 特性 + +- 🚀 统一的分页配置 +- 📊 支持自定义列配置 +- 🔄 内置加载状态管理 +- 🎯 支持排序和筛选 +- 📱 响应式设计 +- 🎨 可自定义样式 + +## 基础用法 + +### 1. 使用 DataTable 组件 + +```vue + + + +``` + +### 2. 高级配置 + +```vue + +``` + +## Props 说明 + +| 属性 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| data | TableData[] | [] | 表格数据 | +| columns | TableColumnData[] | [] | 列配置 | +| loading | boolean | false | 加载状态 | +| rowKey | string | 'id' | 行键 | +| pagination | boolean \| PaginationProps | true | 分页配置 | +| bordered | boolean | false | 是否显示边框 | +| size | 'mini' \| 'small' \| 'medium' \| 'large' | 'medium' | 表格大小 | +| stripe | boolean | false | 是否显示斑马纹 | +| tableLayout | 'auto' \| 'fixed' | 'auto' | 表格布局 | +| scroll | object | - | 滚动配置 | +| current | number | 1 | 当前页码 | +| pageSize | number | 10 | 每页条数 | +| total | number | 0 | 总条数 | +| showTotal | boolean | true | 是否显示总数 | +| pageSizeOptions | number[] | [10, 20, 50, 100] | 每页条数选项 | +| showJumper | boolean | true | 是否显示快速跳转 | +| showPageSize | boolean | true | 是否显示每页条数选择器 | + +## Events 说明 + +| 事件名 | 参数 | 说明 | +|--------|------|------| +| page-change | (page: number) | 页码变化 | +| page-size-change | (pageSize: number) | 每页条数变化 | +| selection-change | (selection: TableData[]) | 选择变化 | +| sorter-change | (sorter: any) | 排序变化 | +| filter-change | (filters: any) | 筛选变化 | + +## useDataTable Composable + +`useDataTable` 是一个用于管理表格状态的 composable,提供了以下功能: + +### 返回值 + +| 属性 | 类型 | 说明 | +|------|------|------| +| data | TableData[] | 表格数据 | +| columns | TableColumnData[] | 列配置 | +| loading | boolean | 加载状态 | +| pagination | PaginationProps | 分页配置 | +| current | number | 当前页码 | +| pageSize | number | 每页条数 | +| total | number | 总条数 | +| setData | (data: TableData[]) => void | 设置数据 | +| setColumns | (columns: TableColumnData[]) => void | 设置列配置 | +| setLoading | (loading: boolean) => void | 设置加载状态 | +| setTotal | (total: number) => void | 设置总条数 | +| setCurrent | (current: number) => void | 设置当前页码 | +| setPageSize | (pageSize: number) => void | 设置每页条数 | +| resetPagination | () => void | 重置分页 | + +### 配置选项 + +```typescript +interface UseDataTableOptions { + initialPage?: number; // 初始页码 + initialPageSize?: number; // 初始每页条数 + pageSizeOptions?: number[]; // 每页条数选项 + showTotal?: boolean; // 是否显示总数 + showJumper?: boolean; // 是否显示快速跳转 + showPageSize?: boolean; // 是否显示每页条数选择器 + rowKey?: string; // 行键 + bordered?: boolean; // 是否显示边框 + size?: 'mini' | 'small' | 'medium' | 'large'; // 表格大小 + stripe?: boolean; // 是否显示斑马纹 + tableLayout?: 'auto' | 'fixed'; // 表格布局 +} +``` + +## 最佳实践 + +1. **统一使用 DataTable 组件**:在项目中统一使用 DataTable 组件,避免重复配置分页逻辑。 + +2. **使用 useDataTable composable**:利用 composable 管理表格状态,提高代码复用性。 + +3. **合理配置列插槽**:对于需要自定义渲染的列,使用插槽进行自定义。 + +4. **处理加载状态**:在数据请求时正确设置 loading 状态。 + +5. **分页事件处理**:在分页事件中调用 API 获取对应页的数据。 + +## 注意事项 + +1. 确保在 `main.ts` 中注册了 `DataTable` 组件。 +2. 列配置中的 `slotName` 要与模板中的插槽名称一致。 +3. 数据源需要包含 `rowKey` 指定的字段。 +4. 分页事件处理中应该调用实际的 API 接口。 \ No newline at end of file diff --git a/src/components/common/DataTable.vue b/src/components/common/DataTable.vue new file mode 100644 index 0000000..87e2b0c --- /dev/null +++ b/src/components/common/DataTable.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/src/components/index.ts b/src/components/index.ts index f418972..30c0459 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -11,6 +11,7 @@ import { } from 'echarts/components'; import Chart from './chart/index.vue'; import Breadcrumb from './breadcrumb/index.vue'; +import DataTable from './common/DataTable.vue'; // Manually introduce ECharts modules to reduce packing size @@ -31,5 +32,6 @@ export default { install(Vue: App) { Vue.component('Chart', Chart); Vue.component('Breadcrumb', Breadcrumb); + Vue.component('DataTable', DataTable); }, }; diff --git a/src/composables/README.md b/src/composables/README.md new file mode 100644 index 0000000..3d61804 --- /dev/null +++ b/src/composables/README.md @@ -0,0 +1,196 @@ +# 分页处理 Composable 使用指南 + +## 概述 + +本项目提供了两个用于处理表格分页的 composable: + +1. `usePagination` - 基础分页事件处理 +2. `useTableWithPagination` - 完整的分页和数据获取集成 + +## usePagination + +### 基础用法 + +```typescript +import usePagination from '@/composables/usePagination'; + +const { handlePageChange, handlePageSizeChange } = usePagination({ + onPageChange: () => getUserListData(), + onPageSizeChange: () => getUserListData(), + resetToFirstPage: true, +}); + +// 在分页事件中使用 +const onPageChange = (page: number) => { + handlePageChange(page, setCurrent); +}; + +const onPageSizeChange = (pageSize: number) => { + handlePageSizeChange(pageSize, setPageSize, setCurrent); +}; +``` + +### 配置选项 + +| 选项 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| onPageChange | Function | - | 页码变化时的回调函数 | +| onPageSizeChange | Function | - | 每页条数变化时的回调函数 | +| resetToFirstPage | boolean | true | 切换每页条数时是否重置到第一页 | + +## useTableWithPagination + +### 完整用法 + +```typescript +import useTableWithPagination from '@/composables/useTableWithPagination'; + +const { + data: renderData, + columns: projectColumn, + loading, + total, + current, + pageSize, + setColumns, + onPageChange, + onPageSizeChange, + refresh, + updateParams, +} = useTableWithPagination({ + // 数据获取函数 + fetchData: async (params) => { + const { code, data } = await getUserList({ + dept_id: selectedKeys.value[0], + keyword: searchKeyWord.value, + page: params.page, + page_size: params.pageSize, + }); + + if (code === 200) { + const { page, user } = data; + return { + data: Array.isArray(user) ? user : [], + total: typeof page.total === 'number' ? page.total : 0, + }; + } + + return { data: [], total: 0 }; + }, + initialPage: 1, + initialPageSize: 10, + pageSizeOptions: [10, 20, 50, 100], + autoFetch: false, // 不自动获取,等选中部门后再获取 +}); +``` + +### 配置选项 + +| 选项 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| fetchData | Function | - | 数据获取函数,必须返回 { data, total } | +| initialPage | number | 1 | 初始页码 | +| initialPageSize | number | 10 | 初始每页条数 | +| pageSizeOptions | number[] | [10, 20, 50, 100] | 每页条数选项 | +| extraParams | object | {} | 额外的查询参数 | +| autoFetch | boolean | true | 是否自动获取数据 | +| resetToFirstPage | boolean | true | 切换每页条数时是否重置到第一页 | + +### 返回值 + +| 属性 | 类型 | 说明 | +|------|------|------| +| data | Ref | 表格数据 | +| columns | Ref | 列配置 | +| loading | Ref | 加载状态 | +| total | Ref | 总条数 | +| current | Ref | 当前页码 | +| pageSize | Ref | 每页条数 | +| setColumns | Function | 设置列配置 | +| onPageChange | Function | 页码变化处理 | +| onPageSizeChange | Function | 每页条数变化处理 | +| refresh | Function | 刷新数据 | +| updateParams | Function | 更新查询参数 | + +## 最佳实践 + +### 1. 使用 usePagination(推荐用于简单场景) + +```typescript +// 在现有的 useDataTable 基础上添加分页处理 +const { handlePageChange, handlePageSizeChange } = usePagination({ + onPageChange: () => getUserListData(), + onPageSizeChange: () => getUserListData(), +}); + +const onPageChange = (page: number) => { + handlePageChange(page, setCurrent); +}; + +const onPageSizeChange = (pageSize: number) => { + handlePageSizeChange(pageSize, setPageSize, setCurrent); +}; +``` + +### 2. 使用 useTableWithPagination(推荐用于新页面) + +```typescript +// 完整的分页和数据获取集成 +const { + data: renderData, + columns: projectColumn, + loading, + total, + current, + pageSize, + onPageChange, + onPageSizeChange, + refresh, +} = useTableWithPagination({ + fetchData: async (params) => { + // 你的数据获取逻辑 + return { data: [], total: 0 }; + }, + autoFetch: false, +}); + +// 搜索时刷新数据 +const handleSearch = () => { + refresh(); +}; + +// 删除后刷新数据 +const handleDelete = async (id: number) => { + await deleteItem(id); + refresh(); +}; +``` + +### 3. 处理搜索和筛选 + +```typescript +// 使用 updateParams 更新查询参数 +const handleSearch = () => { + updateParams({ keyword: searchKeyWord.value }); +}; + +// 或者使用 refresh 直接刷新 +const handleSearch = () => { + refresh(); +}; +``` + +## 优势 + +1. **代码复用**:避免在每个页面重复编写分页逻辑 +2. **统一处理**:确保所有页面的分页行为一致 +3. **类型安全**:完整的 TypeScript 类型支持 +4. **易于维护**:分页逻辑集中管理,便于修改和扩展 +5. **自动优化**:自动处理页码重置、加载状态等细节 + +## 注意事项 + +1. `fetchData` 函数必须返回 `{ data, total }` 格式 +2. 使用 `useTableWithPagination` 时,确保 `fetchData` 函数能正确处理错误 +3. 如果需要自定义分页行为,可以使用 `usePagination` 进行更精细的控制 +4. `autoFetch: false` 适用于需要等待用户操作后再获取数据的场景 \ No newline at end of file diff --git a/src/composables/useDataTable.ts b/src/composables/useDataTable.ts new file mode 100644 index 0000000..c4638da --- /dev/null +++ b/src/composables/useDataTable.ts @@ -0,0 +1,172 @@ +import { ref, reactive, computed, Ref, ComputedRef } from 'vue'; +import type { + TableColumnData, + TableData, + PaginationProps, +} from '@arco-design/web-vue'; + +interface UseDataTableOptions { + // 初始页码 + initialPage?: number; + // 初始每页条数 + initialPageSize?: number; + // 每页条数选项 + pageSizeOptions?: number[]; + // 是否显示总数 + showTotal?: boolean; + // 是否显示快速跳转 + showJumper?: boolean; + // 是否显示每页条数选择器 + showPageSize?: boolean; + // 行键 + rowKey?: string; + // 是否显示边框 + bordered?: boolean; + // 表格大小 + size?: 'mini' | 'small' | 'medium' | 'large'; + // 是否显示斑马纹 + stripe?: boolean; + // 表格布局 + tableLayout?: 'auto' | 'fixed'; +} + +interface UseDataTableReturn { + // 表格数据 + data: Ref; + // 列配置 + columns: Ref; + // 加载状态 + loading: Ref; + // 分页配置 + pagination: ComputedRef; + // 当前页码 + current: Ref; + // 每页条数 + pageSize: Ref; + // 总条数 + total: Ref; + // 设置数据 + setData: (data: TableData[]) => void; + // 设置列配置 + setColumns: (columns: TableColumnData[]) => void; + // 设置加载状态 + setLoading: (loading: boolean) => void; + // 设置总条数 + setTotal: (total: number) => void; + // 设置当前页码 + setCurrent: (current: number) => void; + // 设置每页条数 + setPageSize: (pageSize: number) => void; + // 重置分页 + resetPagination: () => void; + // 表格配置 + tableConfig: { + rowKey: string; + bordered: boolean; + size: 'mini' | 'small' | 'medium' | 'large'; + stripe: boolean; + tableLayout: 'auto' | 'fixed'; + }; +} + +export default function useDataTable( + options: UseDataTableOptions = {} +): UseDataTableReturn { + const { + initialPage = 1, + initialPageSize = 10, + pageSizeOptions = [10, 20, 50, 100], + showTotal = true, + showJumper = true, + showPageSize = true, + rowKey = 'id', + bordered = false, + size = 'medium', + stripe = false, + tableLayout = 'auto', + } = options; + + // 响应式数据 + const data = ref([]); + const columns = ref([]); + const loading = ref(false); + const current = ref(initialPage); + const pageSize = ref(initialPageSize); + const total = ref(0); + + // 分页配置 + const pagination = computed(() => ({ + current: current.value, + pageSize: pageSize.value, + total: total.value, + showTotal, + showJumper, + showPageSize, + pageSizeOptions, + })); + + // 表格配置 + const tableConfig = reactive({ + rowKey, + bordered, + size, + stripe, + tableLayout, + }); + + // 设置数据 + const setData = (newData: TableData[]) => { + data.value = newData; + }; + + // 设置列配置 + const setColumns = (newColumns: TableColumnData[]) => { + columns.value = newColumns; + }; + + // 设置加载状态 + const setLoading = (newLoading: boolean) => { + loading.value = newLoading; + }; + + // 设置总条数 + const setTotal = (newTotal: number) => { + total.value = newTotal; + }; + + // 设置当前页码 + const setCurrent = (newCurrent: number) => { + current.value = newCurrent; + }; + + // 设置每页条数 + const setPageSize = (newPageSize: number) => { + pageSize.value = newPageSize; + // 切换每页条数时重置到第一页 + current.value = 1; + }; + + // 重置分页 + const resetPagination = () => { + current.value = initialPage; + pageSize.value = initialPageSize; + }; + + return { + data, + columns, + loading, + pagination, + current, + pageSize, + total, + setData, + setColumns, + setLoading, + setTotal, + setCurrent, + setPageSize, + resetPagination, + tableConfig, + }; +} diff --git a/src/views/setting/composables/useDepartmentList.ts b/src/composables/useDepartmentList.ts similarity index 97% rename from src/views/setting/composables/useDepartmentList.ts rename to src/composables/useDepartmentList.ts index c02d738..5785cc5 100644 --- a/src/views/setting/composables/useDepartmentList.ts +++ b/src/composables/useDepartmentList.ts @@ -4,6 +4,7 @@ import { DepartmentItem, getDepartmentDetail, } from '@/api/setting'; +import useLoading from '@/hooks/loading'; export default function useDepartmentList(autoFetch = true) { const departmentListData = ref([]); diff --git a/src/locale/zh-CN.ts b/src/locale/zh-CN.ts index 496dc3d..f294472 100644 --- a/src/locale/zh-CN.ts +++ b/src/locale/zh-CN.ts @@ -35,7 +35,10 @@ export default { 'menu.finance': '财务', 'menu.property': '物业', 'menu.setting': '设置', - 'search.placeholder': '请输入内容', + 'please.enter': '请输入{placeholder}', + 'please.select': '请选择{placeholder}', + 'please.confirm': '请确认{placeholder}', + 'content': '内容', 'table.filter': '筛选', 'table.export': '导出', 'watch-detail': '查看详情', @@ -61,6 +64,7 @@ export default { 'company-info': '公司信息', 'appearance-setting': '外观设置', 'add-department': '新建部门', + 'edit-department': '编辑部门', 'department-name': '部门名称', 'belongto-department': '所属部门', 'department-head': '部门主管', @@ -73,6 +77,26 @@ export default { 'delete': '删除', 'confirm': '确定', 'cancel': '取消', + 'search.username.name': '搜索用户名、姓名', + 'add-user': '新建用户', + 'edit-user': '编辑用户', + 'username': '用户名', + 'name': '姓名', + 'role': '角色', + 'status': '状态', + 'last-login': '最后登录', + 'operate': '操作', + 'password': '密码', + 'confirm.password': '确认密码', + 'tel': '手机号', + 'email': '邮箱', + 'lastLogin': '最后登录', + 'please.enter.correct': '请输入正确的{field}', + 'enable': '启用', + 'disable': '禁用', + 'edit': '编辑', + 'more': '更多', + 'user': '用户', ...localeSettings, ...localeMessageBox, diff --git a/src/views/business/project/index.vue b/src/views/business/project/index.vue index 6daf797..4358a9f 100644 --- a/src/views/business/project/index.vue +++ b/src/views/business/project/index.vue @@ -4,43 +4,46 @@ - + - + diff --git a/src/views/setting/system/components/department-form-modal.vue b/src/views/setting/system/components/department-form-modal.vue index 6a8d6e4..de00712 100644 --- a/src/views/setting/system/components/department-form-modal.vue +++ b/src/views/setting/system/components/department-form-modal.vue @@ -1,7 +1,7 @@ diff --git a/src/views/setting/user/list.vue b/src/views/setting/user/list.vue index 2aa198f..a83ca6d 100644 --- a/src/views/setting/user/list.vue +++ b/src/views/setting/user/list.vue @@ -3,17 +3,21 @@ - + - +