feat(user): 添加用户列表导出功能

- 新增 exportUsers API 接口用于导出用户数据
- 在用户管理页面添加导出按钮,仅超级管理员可见- 实现 handleExport 方法处理导出逻辑
- 使用 xlsx 库生成 Excel 文件并自动下载
- 添加搜索处理方法 handleSearch 优化查询体验
- 引入 computed 属性获取当前登录用户信息
- 更新搜索框事件处理为 handleSearch 方法- 调整导入/导出按钮显示权限控制逻辑
This commit is contained in:
2025-09-24 17:24:34 +08:00
parent 5516e994d5
commit ad2657a40a
4 changed files with 145 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
VITE_APP_NAME=后台管理(开发环境) VITE_APP_NAME=后台管理(开发环境)
VITE_API_URL=http://127.0.0.1:9200/api #VITE_API_URL=http://127.0.0.1:9200/api
#VITE_SERVER_API_URL=http://127.0.0.1:8000/api #VITE_SERVER_API_URL=http://127.0.0.1:8000/api

View File

@@ -282,3 +282,17 @@ export async function listAdminsByPhoneAll(params?: UserParam){
} }
return Promise.reject(new Error(res.data.message)); return Promise.reject(new Error(res.data.message));
} }
/**
* 导出用户列表
*/
export async function exportUsers(params?: UserParam) {
const res = await request.get<Blob>(
SERVER_API_URL + '/system/user/export',
{
params,
responseType: 'blob'
}
);
return res.data;
}

View File

@@ -165,6 +165,15 @@ const columns = ref<ColumnItem[]>([
key: 'comments', key: 'comments',
align: 'left' align: 'left'
}, },
{
title: '收益基数',
dataIndex: 'rate',
key: 'rate',
align: 'left',
customRender: () => {
return `0.007`;
}
},
{ {
title: '报备人信息', title: '报备人信息',
key: 'applicantInfo', key: 'applicantInfo',

View File

@@ -54,18 +54,21 @@
</template> </template>
<span>批量删除</span> <span>批量删除</span>
</a-button> </a-button>
<template v-if="hasRole('superAdmin')">
<a-button type="dashed" class="ele-btn-icon" @click="openImport"> <a-button type="dashed" class="ele-btn-icon" @click="openImport">
<template #icon> <template #icon>
<upload-outlined/> <upload-outlined/>
</template> </template>
<span>导入</span> <span>导入</span>
</a-button> </a-button>
<a-button type="dashed" @click="handleExport">导出xls</a-button>
</template>
<a-input-search <a-input-search
allow-clear allow-clear
v-model:value="searchText" v-model:value="searchText"
placeholder="请输入关键词" placeholder="请输入关键词"
@search="reload" @search="handleSearch"
@pressEnter="reload" @pressEnter="handleSearch"
/> />
</a-space> </a-space>
</template> </template>
@@ -149,7 +152,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {createVNode, ref, reactive} from 'vue'; import {createVNode, ref,computed, reactive} from 'vue';
import {message, Modal} from 'ant-design-vue/es'; import {message, Modal} from 'ant-design-vue/es';
import { import {
PlusOutlined, PlusOutlined,
@@ -177,16 +180,20 @@ import {
removeUser, removeUser,
removeUsers, removeUsers,
updateUserPassword, updateUserPassword,
updateUser updateUser,
listUsers
} from '@/api/system/user'; } from '@/api/system/user';
import type {User, UserParam} from '@/api/system/user/model'; import type {User, UserParam} from '@/api/system/user/model';
import {toTreeData, uuid} from 'ele-admin-pro'; import {toTreeData, uuid} from 'ele-admin-pro';
import {utils, writeFile} from 'xlsx';
import dayjs from 'dayjs';
import {listRoles} from '@/api/system/role'; import {listRoles} from '@/api/system/role';
import {listOrganizations} from '@/api/system/organization'; import {listOrganizations} from '@/api/system/organization';
import {Organization} from '@/api/system/organization/model'; import {Organization} from '@/api/system/organization/model';
import {hasRole} from '@/utils/permission'; import {hasRole} from '@/utils/permission';
import {getPageTitle} from "@/utils/common"; import {getPageTitle} from "@/utils/common";
import Extra from "./components/Extra.vue"; import Extra from "./components/Extra.vue";
import {useUserStore} from '@/store/modules/user';
// 加载状态 // 加载状态
const loading = ref(true); const loading = ref(true);
@@ -208,7 +215,9 @@ const showInfo = ref(false);
const showImport = ref(false); const showImport = ref(false);
const userType = ref<number>(); const userType = ref<number>();
const searchText = ref(''); const searchText = ref('');
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
// 加载角色 // 加载角色
const roles = ref<any[]>([]); const roles = ref<any[]>([]);
const filters = () => { const filters = () => {
@@ -536,6 +545,107 @@ const updateIsAdmin = (row: User) => {
}); });
}; };
/* 搜索处理 */
const handleSearch = () => {
reload();
};
/* 导出功能 */
const handleExport = async () => {
if (loading.value) {
return;
}
loading.value = true;
message.loading('正在准备导出数据...', 0);
try {
const array: (string | number)[][] = [
[
'用户ID',
'头像',
'手机号码',
'真实姓名',
'昵称',
'性别',
'邮箱',
'可用余额',
'可用积分',
'注册来源',
'注册时间'
]
];
// 构建查询参数
const queryParams = {
keywords: searchText.value
};
// 按搜索结果导出
const list = await listUsers(queryParams);
if (!list || list.length === 0) {
message.warning('没有数据可以导出');
loading.value = false;
return;
}
list.forEach((user: User) => {
array.push([
`${user.userId || ''}`,
`${user.avatar ? '有头像' : '无头像'}`,
`${user.phone || ''}`,
`${user.realName || ''}`,
`${user.nickname || ''}`,
`${user.sex === '1' ? '男' : user.sex === '2' ? '女' : ''}`,
`${user.email || ''}`,
`${user.balance || '0'}`,
`${user.points || '0'}`,
`${user.platform || ''}`,
`${user.createTime || ''}`
]);
});
const sheetName = `导出用户列表${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10}, // 用户ID
{wch: 10}, // 头像
{wch: 15}, // 手机号码
{wch: 15}, // 真实姓名
{wch: 15}, // 昵称
{wch: 8}, // 性别
{wch: 20}, // 邮箱
{wch: 12}, // 可用余额
{wch: 12}, // 可用积分
{wch: 12}, // 注册来源
{wch: 20} // 注册时间
];
message.destroy();
message.loading('正在生成Excel文件...', 0);
setTimeout(() => {
writeFile(workbook, `${sheetName}.xlsx`);
loading.value = false;
message.destroy();
message.success(`成功导出 ${list.length} 条记录`);
}, 1000);
} catch (error: any) {
loading.value = false;
message.destroy();
message.error(error.message || '导出失败,请重试');
}
};
/* 自定义行属性 */ /* 自定义行属性 */
const customRow = (record: User) => { const customRow = (record: User) => {
return { return {